Home | History | Annotate | Download | only in lib
      1 /* xwrap.c - wrappers around existing library functions.
      2  *
      3  * Functions with the x prefix are wrappers that either succeed or kill the
      4  * program with an error message, but never return failure. They usually have
      5  * the same arguments and return value as the function they wrap.
      6  *
      7  * Copyright 2006 Rob Landley <rob (at) landley.net>
      8  */
      9 
     10 #include "toys.h"
     11 
     12 // strcpy and strncat with size checking. Size is the total space in "dest",
     13 // including null terminator. Exit if there's not enough space for the string
     14 // (including space for the null terminator), because silently truncating is
     15 // still broken behavior. (And leaving the string unterminated is INSANE.)
     16 void xstrncpy(char *dest, char *src, size_t size)
     17 {
     18   if (strlen(src)+1 > size) error_exit("'%s' > %ld bytes", src, (long)size);
     19   strcpy(dest, src);
     20 }
     21 
     22 void xstrncat(char *dest, char *src, size_t size)
     23 {
     24   long len = strlen(dest);
     25 
     26   if (len+strlen(src)+1 > size)
     27     error_exit("'%s%s' > %ld bytes", dest, src, (long)size);
     28   strcpy(dest+len, src);
     29 }
     30 
     31 // We replaced exit(), _exit(), and atexit() with xexit(), _xexit(), and
     32 // sigatexit(). This gives _xexit() the option to siglongjmp(toys.rebound, 1)
     33 // instead of exiting, lets xexit() report stdout flush failures to stderr
     34 // and change the exit code to indicate error, lets our toys.exit function
     35 // change happen for signal exit paths and lets us remove the functions
     36 // after we've called them.
     37 
     38 void _xexit(void)
     39 {
     40   if (toys.rebound) siglongjmp(*toys.rebound, 1);
     41 
     42   _exit(toys.exitval);
     43 }
     44 
     45 void xexit(void)
     46 {
     47   // Call toys.xexit functions in reverse order added.
     48   while (toys.xexit) {
     49     // This is typecasting xexit->arg to a function pointer,then calling it.
     50     // Using the invalid signal number 0 lets the signal handlers distinguish
     51     // an actual signal from a regular exit.
     52     ((void (*)(int))(toys.xexit->arg))(0);
     53 
     54     free(llist_pop(&toys.xexit));
     55   }
     56   if (fflush(NULL) || ferror(stdout))
     57     if (!toys.exitval) perror_msg("write");
     58   _xexit();
     59 }
     60 
     61 void *xmmap(void *addr, size_t length, int prot, int flags, int fd, off_t off)
     62 {
     63   void *ret = mmap(addr, length, prot, flags, fd, off);
     64   if (ret == MAP_FAILED) perror_exit("mmap");
     65   return ret;
     66 }
     67 
     68 // Die unless we can allocate memory.
     69 void *xmalloc(size_t size)
     70 {
     71   void *ret = malloc(size);
     72   if (!ret) error_exit("xmalloc(%ld)", (long)size);
     73 
     74   return ret;
     75 }
     76 
     77 // Die unless we can allocate prezeroed memory.
     78 void *xzalloc(size_t size)
     79 {
     80   void *ret = xmalloc(size);
     81   memset(ret, 0, size);
     82   return ret;
     83 }
     84 
     85 // Die unless we can change the size of an existing allocation, possibly
     86 // moving it.  (Notice different arguments from libc function.)
     87 void *xrealloc(void *ptr, size_t size)
     88 {
     89   ptr = realloc(ptr, size);
     90   if (!ptr) error_exit("xrealloc");
     91 
     92   return ptr;
     93 }
     94 
     95 // Die unless we can allocate a copy of this many bytes of string.
     96 char *xstrndup(char *s, size_t n)
     97 {
     98   char *ret = strndup(s, ++n);
     99 
    100   if (!ret) error_exit("xstrndup");
    101   ret[--n] = 0;
    102 
    103   return ret;
    104 }
    105 
    106 // Die unless we can allocate a copy of this string.
    107 char *xstrdup(char *s)
    108 {
    109   return xstrndup(s, strlen(s));
    110 }
    111 
    112 void *xmemdup(void *s, long len)
    113 {
    114   void *ret = xmalloc(len);
    115   memcpy(ret, s, len);
    116 
    117   return ret;
    118 }
    119 
    120 // Die unless we can allocate enough space to sprintf() into.
    121 char *xmprintf(char *format, ...)
    122 {
    123   va_list va, va2;
    124   int len;
    125   char *ret;
    126 
    127   va_start(va, format);
    128   va_copy(va2, va);
    129 
    130   // How long is it?
    131   len = vsnprintf(0, 0, format, va);
    132   len++;
    133   va_end(va);
    134 
    135   // Allocate and do the sprintf()
    136   ret = xmalloc(len);
    137   vsnprintf(ret, len, format, va2);
    138   va_end(va2);
    139 
    140   return ret;
    141 }
    142 
    143 void xprintf(char *format, ...)
    144 {
    145   va_list va;
    146   va_start(va, format);
    147 
    148   vprintf(format, va);
    149   va_end(va);
    150   if (fflush(stdout) || ferror(stdout)) perror_exit("write");
    151 }
    152 
    153 void xputs(char *s)
    154 {
    155   if (EOF == puts(s) || fflush(stdout) || ferror(stdout)) perror_exit("write");
    156 }
    157 
    158 void xputc(char c)
    159 {
    160   if (EOF == fputc(c, stdout) || fflush(stdout) || ferror(stdout))
    161     perror_exit("write");
    162 }
    163 
    164 void xflush(void)
    165 {
    166   if (fflush(stdout) || ferror(stdout)) perror_exit("write");;
    167 }
    168 
    169 // This is called through the XVFORK macro because parent/child of vfork
    170 // share a stack, so child returning from a function would stomp the return
    171 // address parent would need. Solution: make vfork() an argument so processes
    172 // diverge before function gets called.
    173 pid_t __attribute__((returns_twice)) xvforkwrap(pid_t pid)
    174 {
    175   if (pid == -1) perror_exit("vfork");
    176 
    177   // Signal to xexec() and friends that we vforked so can't recurse
    178   toys.stacktop = 0;
    179 
    180   return pid;
    181 }
    182 
    183 // Die unless we can exec argv[] (or run builtin command).  Note that anything
    184 // with a path isn't a builtin, so /bin/sh won't match the builtin sh.
    185 void xexec(char **argv)
    186 {
    187   // Only recurse to builtin when we have multiplexer and !vfork context.
    188   if (CFG_TOYBOX && !CFG_TOYBOX_NORECURSE && toys.stacktop) toy_exec(argv);
    189   execvp(argv[0], argv);
    190 
    191   perror_msg("exec %s", argv[0]);
    192   toys.exitval = 127;
    193   if (!CFG_TOYBOX_FORK) _exit(toys.exitval);
    194   xexit();
    195 }
    196 
    197 // Spawn child process, capturing stdin/stdout.
    198 // argv[]: command to exec. If null, child re-runs original program with
    199 //         toys.stacktop zeroed.
    200 // pipes[2]: stdin, stdout of new process, only allocated if zero on way in,
    201 //           pass NULL to skip pipe allocation entirely.
    202 // return: pid of child process
    203 pid_t xpopen_both(char **argv, int *pipes)
    204 {
    205   int cestnepasun[4], pid;
    206 
    207   // Make the pipes? Note this won't set either pipe to 0 because if fds are
    208   // allocated in order and if fd0 was free it would go to cestnepasun[0]
    209   if (pipes) {
    210     for (pid = 0; pid < 2; pid++) {
    211       if (pipes[pid] != 0) continue;
    212       if (pipe(cestnepasun+(2*pid))) perror_exit("pipe");
    213       pipes[pid] = cestnepasun[pid+1];
    214     }
    215   }
    216 
    217   // Child process.
    218   if (!(pid = CFG_TOYBOX_FORK ? xfork() : XVFORK())) {
    219     // Dance of the stdin/stdout redirection.
    220     if (pipes) {
    221       // if we had no stdin/out, pipe handles could overlap, so test for it
    222       // and free up potentially overlapping pipe handles before reuse
    223       if (pipes[1] != -1) close(cestnepasun[2]);
    224       if (pipes[0] != -1) {
    225         close(cestnepasun[1]);
    226         if (cestnepasun[0]) {
    227           dup2(cestnepasun[0], 0);
    228           close(cestnepasun[0]);
    229         }
    230       }
    231       if (pipes[1] != -1) {
    232         dup2(cestnepasun[3], 1);
    233         dup2(cestnepasun[3], 2);
    234         if (cestnepasun[3] > 2 || !cestnepasun[3]) close(cestnepasun[3]);
    235       }
    236     }
    237     if (argv) xexec(argv);
    238 
    239     // In fork() case, force recursion because we know it's us.
    240     if (CFG_TOYBOX_FORK) {
    241       toy_init(toys.which, toys.argv);
    242       toys.stacktop = 0;
    243       toys.which->toy_main();
    244       xexit();
    245     // In vfork() case, exec /proc/self/exe with high bit of first letter set
    246     // to tell main() we reentered.
    247     } else {
    248       char *s = "/proc/self/exe";
    249 
    250       // We did a nommu-friendly vfork but must exec to continue.
    251       // setting high bit of argv[0][0] to let new process know
    252       **toys.argv |= 0x80;
    253       execv(s, toys.argv);
    254       perror_msg_raw(s);
    255 
    256       _exit(127);
    257     }
    258   }
    259 
    260   // Parent process
    261   if (!CFG_TOYBOX_FORK) **toys.argv &= 0x7f;
    262   if (pipes) {
    263     if (pipes[0] != -1) close(cestnepasun[0]);
    264     if (pipes[1] != -1) close(cestnepasun[3]);
    265   }
    266 
    267   return pid;
    268 }
    269 
    270 // Wait for child process to exit, then return adjusted exit code.
    271 int xwaitpid(pid_t pid)
    272 {
    273   int status;
    274 
    275   while (-1 == waitpid(pid, &status, 0) && errno == EINTR);
    276 
    277   return WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status)+127;
    278 }
    279 
    280 int xpclose_both(pid_t pid, int *pipes)
    281 {
    282   if (pipes) {
    283     close(pipes[0]);
    284     close(pipes[1]);
    285   }
    286 
    287   return xwaitpid(pid);
    288 }
    289 
    290 // Wrapper to xpopen with a pipe for just one of stdin/stdout
    291 pid_t xpopen(char **argv, int *pipe, int isstdout)
    292 {
    293   int pipes[2], pid;
    294 
    295   pipes[!isstdout] = -1;
    296   pipes[!!isstdout] = 0;
    297   pid = xpopen_both(argv, pipes);
    298   *pipe = pid ? pipes[!!isstdout] : -1;
    299 
    300   return pid;
    301 }
    302 
    303 int xpclose(pid_t pid, int pipe)
    304 {
    305   close(pipe);
    306 
    307   return xpclose_both(pid, 0);
    308 }
    309 
    310 // Call xpopen and wait for it to finish, keeping existing stdin/stdout.
    311 int xrun(char **argv)
    312 {
    313   return xpclose_both(xpopen_both(argv, 0), 0);
    314 }
    315 
    316 void xaccess(char *path, int flags)
    317 {
    318   if (access(path, flags)) perror_exit("Can't access '%s'", path);
    319 }
    320 
    321 // Die unless we can delete a file.  (File must exist to be deleted.)
    322 void xunlink(char *path)
    323 {
    324   if (unlink(path)) perror_exit("unlink '%s'", path);
    325 }
    326 
    327 // Die unless we can open/create a file, returning file descriptor.
    328 // The meaning of O_CLOEXEC is reversed (it defaults on, pass it to disable)
    329 // and WARN_ONLY tells us not to exit.
    330 int xcreate_stdio(char *path, int flags, int mode)
    331 {
    332   int fd = open(path, (flags^O_CLOEXEC)&~WARN_ONLY, mode);
    333 
    334   if (fd == -1) ((mode&WARN_ONLY) ? perror_msg_raw : perror_exit_raw)(path);
    335   return fd;
    336 }
    337 
    338 // Die unless we can open a file, returning file descriptor.
    339 int xopen_stdio(char *path, int flags)
    340 {
    341   return xcreate_stdio(path, flags, 0);
    342 }
    343 
    344 void xpipe(int *pp)
    345 {
    346   if (pipe(pp)) perror_exit("xpipe");
    347 }
    348 
    349 void xclose(int fd)
    350 {
    351   if (close(fd)) perror_exit("xclose");
    352 }
    353 
    354 int xdup(int fd)
    355 {
    356   if (fd != -1) {
    357     fd = dup(fd);
    358     if (fd == -1) perror_exit("xdup");
    359   }
    360   return fd;
    361 }
    362 
    363 // Move file descriptor above stdin/stdout/stderr, using /dev/null to consume
    364 // old one. (We should never be called with stdin/stdout/stderr closed, but...)
    365 int notstdio(int fd)
    366 {
    367   if (fd<0) return fd;
    368 
    369   while (fd<3) {
    370     int fd2 = xdup(fd);
    371 
    372     close(fd);
    373     xopen_stdio("/dev/null", O_RDWR);
    374     fd = fd2;
    375   }
    376 
    377   return fd;
    378 }
    379 
    380 // Create a file but don't return stdin/stdout/stderr
    381 int xcreate(char *path, int flags, int mode)
    382 {
    383   return notstdio(xcreate_stdio(path, flags, mode));
    384 }
    385 
    386 // Open a file descriptor NOT in stdin/stdout/stderr
    387 int xopen(char *path, int flags)
    388 {
    389   return notstdio(xopen_stdio(path, flags));
    390 }
    391 
    392 // Open read only, treating "-" as a synonym for stdin, defaulting to warn only
    393 int openro(char *path, int flags)
    394 {
    395   if (!strcmp(path, "-")) return 0;
    396 
    397   return xopen(path, flags^WARN_ONLY);
    398 }
    399 
    400 // Open read only, treating "-" as a synonym for stdin.
    401 int xopenro(char *path)
    402 {
    403   return openro(path, O_RDONLY|WARN_ONLY);
    404 }
    405 
    406 FILE *xfdopen(int fd, char *mode)
    407 {
    408   FILE *f = fdopen(fd, mode);
    409 
    410   if (!f) perror_exit("xfdopen");
    411 
    412   return f;
    413 }
    414 
    415 // Die unless we can open/create a file, returning FILE *.
    416 FILE *xfopen(char *path, char *mode)
    417 {
    418   FILE *f = fopen(path, mode);
    419   if (!f) perror_exit("No file %s", path);
    420   return f;
    421 }
    422 
    423 // Die if there's an error other than EOF.
    424 size_t xread(int fd, void *buf, size_t len)
    425 {
    426   ssize_t ret = read(fd, buf, len);
    427   if (ret < 0) perror_exit("xread");
    428 
    429   return ret;
    430 }
    431 
    432 void xreadall(int fd, void *buf, size_t len)
    433 {
    434   if (len != readall(fd, buf, len)) perror_exit("xreadall");
    435 }
    436 
    437 // There's no xwriteall(), just xwrite().  When we read, there may or may not
    438 // be more data waiting.  When we write, there is data and it had better go
    439 // somewhere.
    440 
    441 void xwrite(int fd, void *buf, size_t len)
    442 {
    443   if (len != writeall(fd, buf, len)) perror_exit("xwrite");
    444 }
    445 
    446 // Die if lseek fails, probably due to being called on a pipe.
    447 
    448 off_t xlseek(int fd, off_t offset, int whence)
    449 {
    450   offset = lseek(fd, offset, whence);
    451   if (offset<0) perror_exit("lseek");
    452 
    453   return offset;
    454 }
    455 
    456 char *xgetcwd(void)
    457 {
    458   char *buf = getcwd(NULL, 0);
    459   if (!buf) perror_exit("xgetcwd");
    460 
    461   return buf;
    462 }
    463 
    464 void xstat(char *path, struct stat *st)
    465 {
    466   if(stat(path, st)) perror_exit("Can't stat %s", path);
    467 }
    468 
    469 // Cannonicalize path, even to file with one or more missing components at end.
    470 // if exact, require last path component to exist
    471 char *xabspath(char *path, int exact)
    472 {
    473   struct string_list *todo, *done = 0;
    474   int try = 9999, dirfd = open("/", 0);;
    475   char *ret;
    476 
    477   // If this isn't an absolute path, start with cwd.
    478   if (*path != '/') {
    479     char *temp = xgetcwd();
    480 
    481     splitpath(path, splitpath(temp, &todo));
    482     free(temp);
    483   } else splitpath(path, &todo);
    484 
    485   // Iterate through path components
    486   while (todo) {
    487     struct string_list *new = llist_pop(&todo), **tail;
    488     ssize_t len;
    489 
    490     if (!try--) {
    491       errno = ELOOP;
    492       goto error;
    493     }
    494 
    495     // Removable path componenents.
    496     if (!strcmp(new->str, ".") || !strcmp(new->str, "..")) {
    497       int x = new->str[1];
    498 
    499       free(new);
    500       if (x) {
    501         if (done) free(llist_pop(&done));
    502         len = 0;
    503       } else continue;
    504 
    505     // Is this a symlink?
    506     } else len = readlinkat(dirfd, new->str, libbuf, sizeof(libbuf));
    507 
    508     if (len>4095) goto error;
    509     if (len<1) {
    510       int fd;
    511       char *s = "..";
    512 
    513       // For .. just move dirfd
    514       if (len) {
    515         // Not a symlink: add to linked list, move dirfd, fail if error
    516         if ((exact || todo) && errno != EINVAL) goto error;
    517         new->next = done;
    518         done = new;
    519         if (errno == EINVAL && !todo) break;
    520         s = new->str;
    521       }
    522       fd = openat(dirfd, s, 0);
    523       if (fd == -1 && (exact || todo || errno != ENOENT)) goto error;
    524       close(dirfd);
    525       dirfd = fd;
    526       continue;
    527     }
    528 
    529     // If this symlink is to an absolute path, discard existing resolved path
    530     libbuf[len] = 0;
    531     if (*libbuf == '/') {
    532       llist_traverse(done, free);
    533       done=0;
    534       close(dirfd);
    535       dirfd = open("/", 0);
    536     }
    537     free(new);
    538 
    539     // prepend components of new path. Note symlink to "/" will leave new NULL
    540     tail = splitpath(libbuf, &new);
    541 
    542     // symlink to "/" will return null and leave tail alone
    543     if (new) {
    544       *tail = todo;
    545       todo = new;
    546     }
    547   }
    548   close(dirfd);
    549 
    550   // At this point done has the path, in reverse order. Reverse list while
    551   // calculating buffer length.
    552 
    553   try = 2;
    554   while (done) {
    555     struct string_list *temp = llist_pop(&done);;
    556 
    557     if (todo) try++;
    558     try += strlen(temp->str);
    559     temp->next = todo;
    560     todo = temp;
    561   }
    562 
    563   // Assemble return buffer
    564 
    565   ret = xmalloc(try);
    566   *ret = '/';
    567   ret [try = 1] = 0;
    568   while (todo) {
    569     if (try>1) ret[try++] = '/';
    570     try = stpcpy(ret+try, todo->str) - ret;
    571     free(llist_pop(&todo));
    572   }
    573 
    574   return ret;
    575 
    576 error:
    577   close(dirfd);
    578   llist_traverse(todo, free);
    579   llist_traverse(done, free);
    580 
    581   return NULL;
    582 }
    583 
    584 void xchdir(char *path)
    585 {
    586   if (chdir(path)) error_exit("chdir '%s'", path);
    587 }
    588 
    589 void xchroot(char *path)
    590 {
    591   if (chroot(path)) error_exit("chroot '%s'", path);
    592   xchdir("/");
    593 }
    594 
    595 struct passwd *xgetpwuid(uid_t uid)
    596 {
    597   struct passwd *pwd = getpwuid(uid);
    598   if (!pwd) error_exit("bad uid %ld", (long)uid);
    599   return pwd;
    600 }
    601 
    602 struct group *xgetgrgid(gid_t gid)
    603 {
    604   struct group *group = getgrgid(gid);
    605 
    606   if (!group) perror_exit("gid %ld", (long)gid);
    607   return group;
    608 }
    609 
    610 unsigned xgetuid(char *name)
    611 {
    612   struct passwd *up = getpwnam(name);
    613   char *s = 0;
    614   long uid;
    615 
    616   if (up) return up->pw_uid;
    617 
    618   uid = estrtol(name, &s, 10);
    619   if (!errno && s && !*s && uid>=0 && uid<=UINT_MAX) return uid;
    620 
    621   error_exit("bad user '%s'", name);
    622 }
    623 
    624 unsigned xgetgid(char *name)
    625 {
    626   struct group *gr = getgrnam(name);
    627   char *s = 0;
    628   long gid;
    629 
    630   if (gr) return gr->gr_gid;
    631 
    632   gid = estrtol(name, &s, 10);
    633   if (!errno && s && !*s && gid>=0 && gid<=UINT_MAX) return gid;
    634 
    635   error_exit("bad group '%s'", name);
    636 }
    637 
    638 struct passwd *xgetpwnam(char *name)
    639 {
    640   struct passwd *up = getpwnam(name);
    641 
    642   if (!up) perror_exit("user '%s'", name);
    643   return up;
    644 }
    645 
    646 struct group *xgetgrnam(char *name)
    647 {
    648   struct group *gr = getgrnam(name);
    649 
    650   if (!gr) perror_exit("group '%s'", name);
    651   return gr;
    652 }
    653 
    654 // setuid() can fail (for example, too many processes belonging to that user),
    655 // which opens a security hole if the process continues as the original user.
    656 
    657 void xsetuser(struct passwd *pwd)
    658 {
    659   if (initgroups(pwd->pw_name, pwd->pw_gid) || setgid(pwd->pw_uid)
    660       || setuid(pwd->pw_uid)) perror_exit("xsetuser '%s'", pwd->pw_name);
    661 }
    662 
    663 // This can return null (meaning file not found).  It just won't return null
    664 // for memory allocation reasons.
    665 char *xreadlink(char *name)
    666 {
    667   int len, size = 0;
    668   char *buf = 0;
    669 
    670   // Grow by 64 byte chunks until it's big enough.
    671   for(;;) {
    672     size +=64;
    673     buf = xrealloc(buf, size);
    674     len = readlink(name, buf, size);
    675 
    676     if (len<0) {
    677       free(buf);
    678       return 0;
    679     }
    680     if (len<size) {
    681       buf[len]=0;
    682       return buf;
    683     }
    684   }
    685 }
    686 
    687 char *xreadfile(char *name, char *buf, off_t len)
    688 {
    689   if (!(buf = readfile(name, buf, len))) perror_exit("Bad '%s'", name);
    690 
    691   return buf;
    692 }
    693 
    694 // The data argument to ioctl() is actually long, but it's usually used as
    695 // a pointer. If you need to feed in a number, do (void *)(long) typecast.
    696 int xioctl(int fd, int request, void *data)
    697 {
    698   int rc;
    699 
    700   errno = 0;
    701   rc = ioctl(fd, request, data);
    702   if (rc == -1 && errno) perror_exit("ioctl %x", request);
    703 
    704   return rc;
    705 }
    706 
    707 // Open a /var/run/NAME.pid file, dying if we can't write it or if it currently
    708 // exists and is this executable.
    709 void xpidfile(char *name)
    710 {
    711   char pidfile[256], spid[32];
    712   int i, fd;
    713   pid_t pid;
    714 
    715   sprintf(pidfile, "/var/run/%s.pid", name);
    716   // Try three times to open the sucker.
    717   for (i=0; i<3; i++) {
    718     fd = open(pidfile, O_CREAT|O_EXCL|O_WRONLY, 0644);
    719     if (fd != -1) break;
    720 
    721     // If it already existed, read it.  Loop for race condition.
    722     fd = open(pidfile, O_RDONLY);
    723     if (fd == -1) continue;
    724 
    725     // Is the old program still there?
    726     spid[xread(fd, spid, sizeof(spid)-1)] = 0;
    727     close(fd);
    728     pid = atoi(spid);
    729     if (pid < 1 || (kill(pid, 0) && errno == ESRCH)) unlink(pidfile);
    730 
    731     // An else with more sanity checking might be nice here.
    732   }
    733 
    734   if (i == 3) error_exit("xpidfile %s", name);
    735 
    736   xwrite(fd, spid, sprintf(spid, "%ld\n", (long)getpid()));
    737   close(fd);
    738 }
    739 
    740 // Copy the rest of in to out and close both files.
    741 
    742 long long xsendfile(int in, int out)
    743 {
    744   long long total = 0;
    745   long len;
    746 
    747   if (in<0) return 0;
    748   for (;;) {
    749     len = xread(in, libbuf, sizeof(libbuf));
    750     if (len<1) break;
    751     xwrite(out, libbuf, len);
    752     total += len;
    753   }
    754 
    755   return total;
    756 }
    757 
    758 double xstrtod(char *s)
    759 {
    760   char *end;
    761   double d;
    762 
    763   errno = 0;
    764   d = strtod(s, &end);
    765   if (!errno && *end) errno = E2BIG;
    766   if (errno) perror_exit("strtod %s", s);
    767 
    768   return d;
    769 }
    770 
    771 // parse fractional seconds with optional s/m/h/d suffix
    772 long xparsetime(char *arg, long units, long *fraction)
    773 {
    774   double d;
    775   long l;
    776 
    777   if (CFG_TOYBOX_FLOAT) d = strtod(arg, &arg);
    778   else l = strtoul(arg, &arg, 10);
    779 
    780   // Parse suffix
    781   if (*arg) {
    782     int ismhd[]={1,60,3600,86400}, i = stridx("smhd", *arg);
    783 
    784     if (i == -1) error_exit("Unknown suffix '%c'", *arg);
    785     if (CFG_TOYBOX_FLOAT) d *= ismhd[i];
    786     else l *= ismhd[i];
    787   }
    788 
    789   if (CFG_TOYBOX_FLOAT) {
    790     l = (long)d;
    791     if (fraction) *fraction = units*(d-l);
    792   } else if (fraction) *fraction = 0;
    793 
    794   return l;
    795 }
    796 
    797 // Compile a regular expression into a regex_t
    798 void xregcomp(regex_t *preg, char *regex, int cflags)
    799 {
    800   int rc = regcomp(preg, regex, cflags);
    801 
    802   if (rc) {
    803     regerror(rc, preg, libbuf, sizeof(libbuf));
    804     error_exit("xregcomp: %s", libbuf);
    805   }
    806 }
    807 
    808 char *xtzset(char *new)
    809 {
    810   char *old = getenv("TZ");
    811 
    812   if (old) old = xstrdup(old);
    813   if (new ? setenv("TZ", new, 1) : unsetenv("TZ")) perror_exit("setenv");
    814   tzset();
    815 
    816   return old;
    817 }
    818 
    819 // Set a signal handler
    820 void xsignal(int signal, void *handler)
    821 {
    822   struct sigaction *sa = (void *)libbuf;
    823 
    824   memset(sa, 0, sizeof(struct sigaction));
    825   sa->sa_handler = handler;
    826 
    827   if (sigaction(signal, sa, 0)) perror_exit("xsignal %d", signal);
    828 }
    829