aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/toybox.c
blob: cd1071b36917d832cb48d74eb8956aa8e77a6193 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
#include "toybox.h"


char libbuf[4096];


// From lib.c

void tb_verror_msg(char *msg, int err, va_list va)
{
  char *s = ": %s";

//  fprintf(stderr, "%s: ", toys.which->name);
  if (msg) vfprintf(stderr, msg, va);
  else s+=2;
  if (err>0) fprintf(stderr, s, strerror(err));
//  if (err<0 && CFG_TOYBOX_HELP)
//    fprintf(stderr, " (see \"%s --help\")", toys.which->name);
  if (msg || err) putc('\n', stderr);
//  if (!toys.exitval) toys.exitval++;
}

// These functions don't collapse together because of the va_stuff.

void tb_error_msg(char *msg, ...)
{
  va_list va;

  va_start(va, msg);
  tb_verror_msg(msg, 0, va);
  va_end(va);
}

void tb_perror_msg(char *msg, ...)
{
  va_list va;

  va_start(va, msg);
  tb_verror_msg(msg, errno, va);
  va_end(va);
}

// Die with an error message.
void tb_error_exit(char *msg, ...)
{
  va_list va;

  va_start(va, msg);
  tb_verror_msg(msg, 0, va);
  va_end(va);

  xexit();
}

// Die with an error message and strerror(errno)
void tb_perror_exit(char *msg, ...)
{
  // Die silently if our pipeline exited.
  if (errno != EPIPE) {
    va_list va;

    va_start(va, msg);
    tb_verror_msg(msg, errno, va);
    va_end(va);
  }

  xexit();
}

// If you want to explicitly disable the printf() behavior (because you're
// printing user-supplied data, or because android's static checker produces
// false positives for 'char *s = x ? "blah1" : "blah2"; printf(s);' and it's
// -Werror there for policy reasons).
void tb_error_msg_raw(char *msg)
{
  tb_error_msg("%s", msg);
}

void tb_perror_msg_raw(char *msg)
{
  tb_perror_msg("%s", msg);
}

void tb_error_exit_raw(char *msg)
{
  tb_error_exit("%s", msg);
}

void tb_perror_exit_raw(char *msg)
{
  tb_perror_exit("%s", msg);
}

// Sleep for this many thousandths of a second
void msleep(long milliseconds)
{
  struct timespec ts;

  ts.tv_sec = milliseconds/1000;
  ts.tv_nsec = (milliseconds%1000)*1000000;
  nanosleep(&ts, &ts);
}

// Slow, but small.
char *get_line(int fd)
{
  char c, *buf = NULL;
  long len = 0;

  for (;;) {
    if (1>read(fd, &c, 1)) break;
    if (!(len & 63)) buf=xrealloc(buf, len+65);
    if ((buf[len++]=c) == '\n') break;
  }
  if (buf) {
    buf[len]=0;
    if (buf[--len]=='\n') buf[len]=0;
  }

  return buf;
}

// The qsort man page says you can use alphasort, the posix committee
// disagreed, and doubled down: http://austingroupbugs.net/view.php?id=142
// So just do our own. (The const is entirely to humor the stupid compiler.)
int qstrcmp(const void *a, const void *b)
{
  return strcmp(*(char **)a, *(char **)b);
}



// From xwrap.c

// We replaced exit(), _exit(), and atexit() with xexit(), _xexit(), and
// sigatexit(). This gives _xexit() the option to siglongjmp(toys.rebound, 1)
// instead of exiting, lets xexit() report stdout flush failures to stderr
// and change the exit code to indicate error, lets our toys.exit function
// change happen for signal exit paths and lets us remove the functions
// after we've called them.

void _xexit(void)
{
//  if (toys.rebound) siglongjmp(*toys.rebound, 1);

//  _exit(toys.exitval);
  _exit(0);
}

void xexit(void)
{
  // Call toys.xexit functions in reverse order added.
//  while (toys.xexit) {
//    struct arg_list *al = tb_llist_pop(&toys.xexit);

    // typecast xexit->arg to a function pointer, then call it using invalid
    // signal 0 to let signal handlers tell actual signal from regular exit.
//    ((void (*)(int))(al->arg))(0);

//    free(al);
//  }
  xflush(1);
  _xexit();
}

// Die unless we can allocate memory.
void *xmalloc(size_t size)
{
  void *ret = malloc(size);
  if (!ret) tb_error_exit("xmalloc(%ld)", (long)size);

  return ret;
}

// Die unless we can allocate prezeroed memory.
void *xzalloc(size_t size)
{
  void *ret = xmalloc(size);
  memset(ret, 0, size);
  return ret;
}

// Die unless we can change the size of an existing allocation, possibly
// moving it.  (Notice different arguments from libc function.)
void *xrealloc(void *ptr, size_t size)
{
  ptr = realloc(ptr, size);
  if (!ptr) tb_error_exit("xrealloc");

  return ptr;
}

// Die unless we can allocate a copy of this many bytes of string.
char *xstrndup(char *s, size_t n)
{
  char *ret = strndup(s, n);

  if (!ret) tb_error_exit("xstrndup");

  return ret;
}

// Die unless we can allocate a copy of this string.
char *xstrdup(char *s)
{
  return xstrndup(s, strlen(s));
}

void *xmemdup(void *s, long len)
{
  void *ret = xmalloc(len);
  memcpy(ret, s, len);

  return ret;
}

// Die unless we can allocate enough space to sprintf() into.
char *xmprintf(char *format, ...)
{
  va_list va, va2;
  int len;
  char *ret;

  va_start(va, format);
  va_copy(va2, va);

  // How long is it?
  len = vsnprintf(0, 0, format, va);
  len++;
  va_end(va);

  // Allocate and do the sprintf()
  ret = xmalloc(len);
  vsnprintf(ret, len, format, va2);
  va_end(va2);

  return ret;
}

// if !flush just check for error on stdout without flushing 
void xflush(int flush)
{
  if ((flush && fflush(0)) || ferror(stdout))
;//    if (!toys.exitval) tb_perror_msg("write");
}

// Die unless we can open/create a file, returning file descriptor.
// The meaning of O_CLOEXEC is reversed (it defaults on, pass it to disable)
// and WARN_ONLY tells us not to exit.
int xcreate_stdio(char *path, int flags, int mode)
{
  int fd = open(path, (flags^O_CLOEXEC)&~WARN_ONLY, mode);

  if (fd == -1) ((mode&WARN_ONLY) ? tb_perror_msg_raw : tb_perror_exit_raw)(path);
  return fd;
}

// Die unless we can open a file, returning file descriptor.
int xopen_stdio(char *path, int flags)
{
  return xcreate_stdio(path, flags, 0);
}

void xclose(int fd)
{
  if (close(fd)) tb_perror_exit("xclose");
}

int xdup(int fd)
{
  if (fd != -1) {
    fd = dup(fd);
    if (fd == -1) tb_perror_exit("xdup");
  }
  return fd;
}

// Move file descriptor above stdin/stdout/stderr, using /dev/null to consume
// old one. (We should never be called with stdin/stdout/stderr closed, but...)
int notstdio(int fd)
{
  if (fd<0) return fd;

  while (fd<3) {
    int fd2 = xdup(fd);

    close(fd);
    xopen_stdio("/dev/null", O_RDWR);
    fd = fd2;
  }

  return fd;
}

// Open a file descriptor NOT in stdin/stdout/stderr
int xopen(char *path, int flags)
{
  return notstdio(xopen_stdio(path, flags));
}

// Open read only, treating "-" as a synonym for stdin, defaulting to warn only
int openro(char *path, int flags)
{
  if (!strcmp(path, "-")) return 0;

  return xopen(path, flags^WARN_ONLY);
}

// Open read only, treating "-" as a synonym for stdin.
int xopenro(char *path)
{
  return openro(path, O_RDONLY|WARN_ONLY);
}

// Compile a regular expression into a regex_t
void xregcomp(regex_t *preg, char *regex, int cflags)
{
  int rc;

  // BSD regex implementations don't support the empty regex (which isn't
  // allowed in the POSIX grammar), but glibc does. Fake it for BSD.
  if (!*regex) {
    regex = "()";
    cflags |= REG_EXTENDED;
  }

  if ((rc = regcomp(preg, regex, cflags))) {
    regerror(rc, preg, libbuf, sizeof(libbuf));
    tb_error_exit("bad regex: %s", libbuf);
  }
}



// From dirtree.c

int tb_isdotdot(char *name)
{
  if (name[0]=='.' && (!name[1] || (name[1]=='.' && !name[2]))) return 1;

  return 0;
}

// Create a tb_dirtree node from a path, with stat and symlink info.
// (This doesn't open directory filehandles yet so as not to exhaust the
// filehandle space on large trees, tb_dirtree_handle_callback() does that.)

struct tb_dirtree *tb_dirtree_add_node(struct tb_dirtree *parent, char *name, int flags)
{
  struct tb_dirtree *dt = 0;
  struct stat st;
  int len = 0, linklen = 0, statless = 0;

  if (name) {
    // open code fd = because haven't got node to call tb_dirtree_parentfd() on yet
    int fd = parent ? parent->dirfd : AT_FDCWD,
      sym = AT_SYMLINK_NOFOLLOW*!(flags&TB_DIRTREE_SYMFOLLOW);

    // stat dangling symlinks
    if (fstatat(fd, name, &st, sym)) {
      // If we got ENOENT without NOFOLLOW, try again with NOFOLLOW.
      if (errno!=ENOENT || sym || fstatat(fd, name, &st, AT_SYMLINK_NOFOLLOW)) {
        if (flags&TB_DIRTREE_STATLESS) statless++;
        else goto error;
      }
    }
    if (!statless && S_ISLNK(st.st_mode)) {
      if (0>(linklen = readlinkat(fd, name, libbuf, 4095))) goto error;
      libbuf[linklen++]=0;
    }
    len = strlen(name);
  }

  // Allocate/populate return structure
  dt = xmalloc((len = sizeof(struct tb_dirtree)+len+1)+linklen);
  memset(dt, 0, statless ? offsetof(struct tb_dirtree, again)
    : offsetof(struct tb_dirtree, st));
  dt->parent = parent;
  dt->again = statless ? 2 : 0;
  if (!statless) memcpy(&dt->st, &st, sizeof(struct stat));
  strcpy(dt->name, name ? name : "");
  if (linklen) dt->symlink = memcpy(len+(char *)dt, libbuf, linklen);

  return dt;

error:
  if (!(flags&TB_DIRTREE_SHUTUP) && !tb_isdotdot(name)) {
    char *path = parent ? tb_dirtree_path(parent, 0) : "";

    tb_perror_msg("%s%s%s", path, parent ? "/" : "", name);
    if (parent) free(path);
  }
  if (parent) parent->symlink = (char *)1;
  free(dt);
  return 0;
}

// Return path to this node, assembled recursively.

// Initial call can pass in NULL to plen, or point to an int initialized to 0
// to return the length of the path, or a value greater than 0 to allocate
// extra space if you want to append your own text to the string.

char *tb_dirtree_path(struct tb_dirtree *node, int *plen)
{
  char *path;
  int len;

  if (!node) {
    path = xmalloc(*plen);
    *plen = 0;
    return path;
  }

  len = (plen ? *plen : 0)+strlen(node->name)+1;
  path = tb_dirtree_path(node->parent, &len);
  if (len && path[len-1] != '/') path[len++]='/';
  len = stpcpy(path+len, node->name) - path;
  if (plen) *plen = len;

  return path;
}

int tb_dirtree_parentfd(struct tb_dirtree *node)
{
  return node->parent ? node->parent->dirfd : AT_FDCWD;
}

// Handle callback for a node in the tree. Returns saved node(s) if
// callback returns TB_DIRTREE_SAVE, otherwise frees consumed nodes and
// returns NULL. If !callback return top node unchanged.
// If !new return TB_DIRTREE_ABORTVAL

struct tb_dirtree *tb_dirtree_handle_callback(struct tb_dirtree *new,
          int (*callback)(struct tb_dirtree *node))
{
  int flags;

  if (!new) return TB_DIRTREE_ABORTVAL;
  if (!callback) return new;
  flags = callback(new);

  if (S_ISDIR(new->st.st_mode) && (flags & (TB_DIRTREE_RECURSE|TB_DIRTREE_COMEAGAIN)))
    flags = tb_dirtree_recurse(new, callback,
      openat(tb_dirtree_parentfd(new), new->name, O_CLOEXEC), flags);

  // If this had children, it was callback's job to free them already.
  if (!(flags & TB_DIRTREE_SAVE)) {
    free(new);
    new = 0;
  }

  return (flags & TB_DIRTREE_ABORT)==TB_DIRTREE_ABORT ? TB_DIRTREE_ABORTVAL : new;
}

// Recursively read/process children of directory node, filtering through
// callback(). Uses and closes supplied ->dirfd.

int tb_dirtree_recurse(struct tb_dirtree *node,
          int (*callback)(struct tb_dirtree *node), int dirfd, int flags)
{
  struct tb_dirtree *new, **ddt = &(node->child);
  struct dirent *entry;
  DIR *dir;

  node->dirfd = dirfd;
  if (node->dirfd == -1 || !(dir = fdopendir(node->dirfd))) {
    if (!(flags & TB_DIRTREE_SHUTUP)) {
      char *path = tb_dirtree_path(node, 0);
      tb_perror_msg_raw(path);
      free(path);
    }
    close(node->dirfd);

    return flags;
  }

  // according to the fddir() man page, the filehandle in the DIR * can still
  // be externally used by things that don't lseek() it.

  // The extra parentheses are to shut the stupid compiler up.
  while ((entry = readdir(dir))) {
    if ((flags&TB_DIRTREE_PROC) && !isdigit(*entry->d_name)) continue;
    if (!(new = tb_dirtree_add_node(node, entry->d_name, flags))) continue;
    if (!new->st.st_blksize && !new->st.st_mode)
      new->st.st_mode = entry->d_type<<12;
    new = tb_dirtree_handle_callback(new, callback);
    if (new == TB_DIRTREE_ABORTVAL) break;
    if (new) {
      *ddt = new;
      ddt = &((*ddt)->next);
    }
  }

  if (flags & TB_DIRTREE_COMEAGAIN) {
    node->again |= 1;
    flags = callback(node);
  }

  // This closes filehandle as well, so note it
  closedir(dir);
  node->dirfd = -1;

  return flags;
}

void tb_dirtree_free(struct tb_dirtree *new)
{
  struct tb_dirtree *dt;

  if ((dt = new)) {
    new = 0;
    while (dt->child) {
      new = dt->child->next;
      free(dt->child);
      dt->child = new;
    }
    free(dt);
  }
}