10f66f451Sopenharmony_ci/* xwrap.c - wrappers around existing library functions. 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Functions with the x prefix are wrappers that either succeed or kill the 40f66f451Sopenharmony_ci * program with an error message, but never return failure. They usually have 50f66f451Sopenharmony_ci * the same arguments and return value as the function they wrap. 60f66f451Sopenharmony_ci * 70f66f451Sopenharmony_ci * Copyright 2006 Rob Landley <rob@landley.net> 80f66f451Sopenharmony_ci */ 90f66f451Sopenharmony_ci 100f66f451Sopenharmony_ci#include "toys.h" 110f66f451Sopenharmony_ci 120f66f451Sopenharmony_ci// strcpy and strncat with size checking. Size is the total space in "dest", 130f66f451Sopenharmony_ci// including null terminator. Exit if there's not enough space for the string 140f66f451Sopenharmony_ci// (including space for the null terminator), because silently truncating is 150f66f451Sopenharmony_ci// still broken behavior. (And leaving the string unterminated is INSANE.) 160f66f451Sopenharmony_civoid xstrncpy(char *dest, char *src, size_t size) 170f66f451Sopenharmony_ci{ 180f66f451Sopenharmony_ci if (strlen(src)+1 > size) error_exit("'%s' > %ld bytes", src, (long)size); 190f66f451Sopenharmony_ci strcpy(dest, src); 200f66f451Sopenharmony_ci} 210f66f451Sopenharmony_ci 220f66f451Sopenharmony_civoid xstrncat(char *dest, char *src, size_t size) 230f66f451Sopenharmony_ci{ 240f66f451Sopenharmony_ci long len = strlen(dest); 250f66f451Sopenharmony_ci 260f66f451Sopenharmony_ci if (len+strlen(src)+1 > size) 270f66f451Sopenharmony_ci error_exit("'%s%s' > %ld bytes", dest, src, (long)size); 280f66f451Sopenharmony_ci strcpy(dest+len, src); 290f66f451Sopenharmony_ci} 300f66f451Sopenharmony_ci 310f66f451Sopenharmony_ci// We replaced exit(), _exit(), and atexit() with xexit(), _xexit(), and 320f66f451Sopenharmony_ci// sigatexit(). This gives _xexit() the option to siglongjmp(toys.rebound, 1) 330f66f451Sopenharmony_ci// instead of exiting, lets xexit() report stdout flush failures to stderr 340f66f451Sopenharmony_ci// and change the exit code to indicate error, lets our toys.exit function 350f66f451Sopenharmony_ci// change happen for signal exit paths and lets us remove the functions 360f66f451Sopenharmony_ci// after we've called them. 370f66f451Sopenharmony_ci 380f66f451Sopenharmony_civoid _xexit(void) 390f66f451Sopenharmony_ci{ 400f66f451Sopenharmony_ci if (toys.rebound) siglongjmp(*toys.rebound, 1); 410f66f451Sopenharmony_ci 420f66f451Sopenharmony_ci _exit(toys.exitval); 430f66f451Sopenharmony_ci} 440f66f451Sopenharmony_ci 450f66f451Sopenharmony_civoid xexit(void) 460f66f451Sopenharmony_ci{ 470f66f451Sopenharmony_ci // Call toys.xexit functions in reverse order added. 480f66f451Sopenharmony_ci while (toys.xexit) { 490f66f451Sopenharmony_ci struct arg_list *al = llist_pop(&toys.xexit); 500f66f451Sopenharmony_ci 510f66f451Sopenharmony_ci // typecast xexit->arg to a function pointer, then call it using invalid 520f66f451Sopenharmony_ci // signal 0 to let signal handlers tell actual signal from regular exit. 530f66f451Sopenharmony_ci ((void (*)(int))(al->arg))(0); 540f66f451Sopenharmony_ci 550f66f451Sopenharmony_ci free(al); 560f66f451Sopenharmony_ci } 570f66f451Sopenharmony_ci xflush(1); 580f66f451Sopenharmony_ci _xexit(); 590f66f451Sopenharmony_ci} 600f66f451Sopenharmony_ci 610f66f451Sopenharmony_civoid *xmmap(void *addr, size_t length, int prot, int flags, int fd, off_t off) 620f66f451Sopenharmony_ci{ 630f66f451Sopenharmony_ci void *ret = mmap(addr, length, prot, flags, fd, off); 640f66f451Sopenharmony_ci if (ret == MAP_FAILED) perror_exit("mmap"); 650f66f451Sopenharmony_ci return ret; 660f66f451Sopenharmony_ci} 670f66f451Sopenharmony_ci 680f66f451Sopenharmony_ci// Die unless we can allocate memory. 690f66f451Sopenharmony_civoid *xmalloc(size_t size) 700f66f451Sopenharmony_ci{ 710f66f451Sopenharmony_ci void *ret = malloc(size); 720f66f451Sopenharmony_ci if (!ret) error_exit("xmalloc(%ld)", (long)size); 730f66f451Sopenharmony_ci 740f66f451Sopenharmony_ci return ret; 750f66f451Sopenharmony_ci} 760f66f451Sopenharmony_ci 770f66f451Sopenharmony_ci// Die unless we can allocate prezeroed memory. 780f66f451Sopenharmony_civoid *xzalloc(size_t size) 790f66f451Sopenharmony_ci{ 800f66f451Sopenharmony_ci void *ret = xmalloc(size); 810f66f451Sopenharmony_ci memset(ret, 0, size); 820f66f451Sopenharmony_ci return ret; 830f66f451Sopenharmony_ci} 840f66f451Sopenharmony_ci 850f66f451Sopenharmony_ci// Die unless we can change the size of an existing allocation, possibly 860f66f451Sopenharmony_ci// moving it. (Notice different arguments from libc function.) 870f66f451Sopenharmony_civoid *xrealloc(void *ptr, size_t size) 880f66f451Sopenharmony_ci{ 890f66f451Sopenharmony_ci ptr = realloc(ptr, size); 900f66f451Sopenharmony_ci if (!ptr) error_exit("xrealloc"); 910f66f451Sopenharmony_ci 920f66f451Sopenharmony_ci return ptr; 930f66f451Sopenharmony_ci} 940f66f451Sopenharmony_ci 950f66f451Sopenharmony_ci// Die unless we can allocate a copy of this many bytes of string. 960f66f451Sopenharmony_cichar *xstrndup(char *s, size_t n) 970f66f451Sopenharmony_ci{ 980f66f451Sopenharmony_ci char *ret = strndup(s, n); 990f66f451Sopenharmony_ci 1000f66f451Sopenharmony_ci if (!ret) error_exit("xstrndup"); 1010f66f451Sopenharmony_ci 1020f66f451Sopenharmony_ci return ret; 1030f66f451Sopenharmony_ci} 1040f66f451Sopenharmony_ci 1050f66f451Sopenharmony_ci// Die unless we can allocate a copy of this string. 1060f66f451Sopenharmony_cichar *xstrdup(char *s) 1070f66f451Sopenharmony_ci{ 1080f66f451Sopenharmony_ci return xstrndup(s, strlen(s)); 1090f66f451Sopenharmony_ci} 1100f66f451Sopenharmony_ci 1110f66f451Sopenharmony_civoid *xmemdup(void *s, long len) 1120f66f451Sopenharmony_ci{ 1130f66f451Sopenharmony_ci void *ret = xmalloc(len); 1140f66f451Sopenharmony_ci memcpy(ret, s, len); 1150f66f451Sopenharmony_ci 1160f66f451Sopenharmony_ci return ret; 1170f66f451Sopenharmony_ci} 1180f66f451Sopenharmony_ci 1190f66f451Sopenharmony_ci// Die unless we can allocate enough space to sprintf() into. 1200f66f451Sopenharmony_cichar *xmprintf(char *format, ...) 1210f66f451Sopenharmony_ci{ 1220f66f451Sopenharmony_ci va_list va, va2; 1230f66f451Sopenharmony_ci int len; 1240f66f451Sopenharmony_ci char *ret; 1250f66f451Sopenharmony_ci 1260f66f451Sopenharmony_ci va_start(va, format); 1270f66f451Sopenharmony_ci va_copy(va2, va); 1280f66f451Sopenharmony_ci 1290f66f451Sopenharmony_ci // How long is it? 1300f66f451Sopenharmony_ci len = vsnprintf(0, 0, format, va); 1310f66f451Sopenharmony_ci len++; 1320f66f451Sopenharmony_ci va_end(va); 1330f66f451Sopenharmony_ci 1340f66f451Sopenharmony_ci // Allocate and do the sprintf() 1350f66f451Sopenharmony_ci ret = xmalloc(len); 1360f66f451Sopenharmony_ci vsnprintf(ret, len, format, va2); 1370f66f451Sopenharmony_ci va_end(va2); 1380f66f451Sopenharmony_ci 1390f66f451Sopenharmony_ci return ret; 1400f66f451Sopenharmony_ci} 1410f66f451Sopenharmony_ci 1420f66f451Sopenharmony_ci// if !flush just check for error on stdout without flushing 1430f66f451Sopenharmony_civoid xflush(int flush) 1440f66f451Sopenharmony_ci{ 1450f66f451Sopenharmony_ci if ((flush && fflush(0)) || ferror(stdout)) 1460f66f451Sopenharmony_ci if (!toys.exitval) perror_msg("write"); 1470f66f451Sopenharmony_ci} 1480f66f451Sopenharmony_ci 1490f66f451Sopenharmony_civoid xprintf(char *format, ...) 1500f66f451Sopenharmony_ci{ 1510f66f451Sopenharmony_ci va_list va; 1520f66f451Sopenharmony_ci va_start(va, format); 1530f66f451Sopenharmony_ci 1540f66f451Sopenharmony_ci vprintf(format, va); 1550f66f451Sopenharmony_ci va_end(va); 1560f66f451Sopenharmony_ci xflush(0); 1570f66f451Sopenharmony_ci} 1580f66f451Sopenharmony_ci 1590f66f451Sopenharmony_ci// Put string with length (does not append newline) 1600f66f451Sopenharmony_civoid xputsl(char *s, int len) 1610f66f451Sopenharmony_ci{ 1620f66f451Sopenharmony_ci int out; 1630f66f451Sopenharmony_ci 1640f66f451Sopenharmony_ci while (len != (out = fwrite(s, 1, len, stdout))) { 1650f66f451Sopenharmony_ci if (out<1) perror_exit("write"); 1660f66f451Sopenharmony_ci len -= out; 1670f66f451Sopenharmony_ci s += out; 1680f66f451Sopenharmony_ci } 1690f66f451Sopenharmony_ci xflush(0); 1700f66f451Sopenharmony_ci} 1710f66f451Sopenharmony_ci 1720f66f451Sopenharmony_ci// xputs with no newline 1730f66f451Sopenharmony_civoid xputsn(char *s) 1740f66f451Sopenharmony_ci{ 1750f66f451Sopenharmony_ci xputsl(s, strlen(s)); 1760f66f451Sopenharmony_ci} 1770f66f451Sopenharmony_ci 1780f66f451Sopenharmony_ci// Write string to stdout with newline, flushing and checking for errors 1790f66f451Sopenharmony_civoid xputs(char *s) 1800f66f451Sopenharmony_ci{ 1810f66f451Sopenharmony_ci puts(s); 1820f66f451Sopenharmony_ci xflush(0); 1830f66f451Sopenharmony_ci} 1840f66f451Sopenharmony_ci 1850f66f451Sopenharmony_civoid xputc(char c) 1860f66f451Sopenharmony_ci{ 1870f66f451Sopenharmony_ci if (EOF == fputc(c, stdout)) perror_exit("write"); 1880f66f451Sopenharmony_ci xflush(0); 1890f66f451Sopenharmony_ci} 1900f66f451Sopenharmony_ci 1910f66f451Sopenharmony_ci// This is called through the XVFORK macro because parent/child of vfork 1920f66f451Sopenharmony_ci// share a stack, so child returning from a function would stomp the return 1930f66f451Sopenharmony_ci// address parent would need. Solution: make vfork() an argument so processes 1940f66f451Sopenharmony_ci// diverge before function gets called. 1950f66f451Sopenharmony_cipid_t __attribute__((returns_twice)) xvforkwrap(pid_t pid) 1960f66f451Sopenharmony_ci{ 1970f66f451Sopenharmony_ci if (pid == -1) perror_exit("vfork"); 1980f66f451Sopenharmony_ci 1990f66f451Sopenharmony_ci // Signal to xexec() and friends that we vforked so can't recurse 2000f66f451Sopenharmony_ci if (!pid) toys.stacktop = 0; 2010f66f451Sopenharmony_ci 2020f66f451Sopenharmony_ci return pid; 2030f66f451Sopenharmony_ci} 2040f66f451Sopenharmony_ci 2050f66f451Sopenharmony_ci// Die unless we can exec argv[] (or run builtin command). Note that anything 2060f66f451Sopenharmony_ci// with a path isn't a builtin, so /bin/sh won't match the builtin sh. 2070f66f451Sopenharmony_civoid xexec(char **argv) 2080f66f451Sopenharmony_ci{ 2090f66f451Sopenharmony_ci // Only recurse to builtin when we have multiplexer and !vfork context. 2100f66f451Sopenharmony_ci if (CFG_TOYBOX && !CFG_TOYBOX_NORECURSE && toys.stacktop && **argv != '/') 2110f66f451Sopenharmony_ci toy_exec(argv); 2120f66f451Sopenharmony_ci execvp(argv[0], argv); 2130f66f451Sopenharmony_ci 2140f66f451Sopenharmony_ci toys.exitval = 126+(errno == ENOENT); 2150f66f451Sopenharmony_ci perror_msg("exec %s", argv[0]); 2160f66f451Sopenharmony_ci if (!toys.stacktop) _exit(toys.exitval); 2170f66f451Sopenharmony_ci xexit(); 2180f66f451Sopenharmony_ci} 2190f66f451Sopenharmony_ci 2200f66f451Sopenharmony_ci// Spawn child process, capturing stdin/stdout. 2210f66f451Sopenharmony_ci// argv[]: command to exec. If null, child re-runs original program with 2220f66f451Sopenharmony_ci// toys.stacktop zeroed. 2230f66f451Sopenharmony_ci// pipes[2]: Filehandle to move to stdin/stdout of new process. 2240f66f451Sopenharmony_ci// If -1, replace with pipe handle connected to stdin/stdout. 2250f66f451Sopenharmony_ci// NULL treated as {0, 1}, I.E. leave stdin/stdout as is 2260f66f451Sopenharmony_ci// return: pid of child process 2270f66f451Sopenharmony_cipid_t xpopen_both(char **argv, int *pipes) 2280f66f451Sopenharmony_ci{ 2290f66f451Sopenharmony_ci int cestnepasun[4], pid; 2300f66f451Sopenharmony_ci 2310f66f451Sopenharmony_ci // Make the pipes? 2320f66f451Sopenharmony_ci memset(cestnepasun, 0, sizeof(cestnepasun)); 2330f66f451Sopenharmony_ci if (pipes) for (pid = 0; pid < 2; pid++) { 2340f66f451Sopenharmony_ci if (pipes[pid] != -1) continue; 2350f66f451Sopenharmony_ci if (pipe(cestnepasun+(2*pid))) perror_exit("pipe"); 2360f66f451Sopenharmony_ci } 2370f66f451Sopenharmony_ci 2380f66f451Sopenharmony_ci if (!(pid = CFG_TOYBOX_FORK ? xfork() : XVFORK())) { 2390f66f451Sopenharmony_ci // Child process: Dance of the stdin/stdout redirection. 2400f66f451Sopenharmony_ci // cestnepasun[1]->cestnepasun[0] and cestnepasun[3]->cestnepasun[2] 2410f66f451Sopenharmony_ci if (pipes) { 2420f66f451Sopenharmony_ci // if we had no stdin/out, pipe handles could overlap, so test for it 2430f66f451Sopenharmony_ci // and free up potentially overlapping pipe handles before reuse 2440f66f451Sopenharmony_ci 2450f66f451Sopenharmony_ci // in child, close read end of output pipe, use write end as new stdout 2460f66f451Sopenharmony_ci if (cestnepasun[2]) { 2470f66f451Sopenharmony_ci close(cestnepasun[2]); 2480f66f451Sopenharmony_ci pipes[1] = cestnepasun[3]; 2490f66f451Sopenharmony_ci } 2500f66f451Sopenharmony_ci 2510f66f451Sopenharmony_ci // in child, close write end of input pipe, use read end as new stdin 2520f66f451Sopenharmony_ci if (cestnepasun[1]) { 2530f66f451Sopenharmony_ci close(cestnepasun[1]); 2540f66f451Sopenharmony_ci pipes[0] = cestnepasun[0]; 2550f66f451Sopenharmony_ci } 2560f66f451Sopenharmony_ci 2570f66f451Sopenharmony_ci // If swapping stdin/stdout, dup a filehandle that gets closed before use 2580f66f451Sopenharmony_ci if (!pipes[1]) pipes[1] = dup(0); 2590f66f451Sopenharmony_ci 2600f66f451Sopenharmony_ci // Are we redirecting stdin? 2610f66f451Sopenharmony_ci if (pipes[0]) { 2620f66f451Sopenharmony_ci dup2(pipes[0], 0); 2630f66f451Sopenharmony_ci close(pipes[0]); 2640f66f451Sopenharmony_ci } 2650f66f451Sopenharmony_ci 2660f66f451Sopenharmony_ci // Are we redirecting stdout? 2670f66f451Sopenharmony_ci if (pipes[1] != 1) { 2680f66f451Sopenharmony_ci dup2(pipes[1], 1); 2690f66f451Sopenharmony_ci close(pipes[1]); 2700f66f451Sopenharmony_ci } 2710f66f451Sopenharmony_ci } 2720f66f451Sopenharmony_ci if (argv) xexec(argv); 2730f66f451Sopenharmony_ci 2740f66f451Sopenharmony_ci // In fork() case, force recursion because we know it's us. 2750f66f451Sopenharmony_ci if (CFG_TOYBOX_FORK) { 2760f66f451Sopenharmony_ci toy_init(toys.which, toys.argv); 2770f66f451Sopenharmony_ci toys.stacktop = 0; 2780f66f451Sopenharmony_ci toys.which->toy_main(); 2790f66f451Sopenharmony_ci xexit(); 2800f66f451Sopenharmony_ci // In vfork() case, exec /proc/self/exe with high bit of first letter set 2810f66f451Sopenharmony_ci // to tell main() we reentered. 2820f66f451Sopenharmony_ci } else { 2830f66f451Sopenharmony_ci char *s = "/proc/self/exe"; 2840f66f451Sopenharmony_ci 2850f66f451Sopenharmony_ci // We did a nommu-friendly vfork but must exec to continue. 2860f66f451Sopenharmony_ci // setting high bit of argv[0][0] to let new process know 2870f66f451Sopenharmony_ci **toys.argv |= 0x80; 2880f66f451Sopenharmony_ci execv(s, toys.argv); 2890f66f451Sopenharmony_ci perror_msg_raw(s); 2900f66f451Sopenharmony_ci 2910f66f451Sopenharmony_ci _exit(127); 2920f66f451Sopenharmony_ci } 2930f66f451Sopenharmony_ci } 2940f66f451Sopenharmony_ci 2950f66f451Sopenharmony_ci // Parent process: vfork had a shared environment, clean up. 2960f66f451Sopenharmony_ci if (!CFG_TOYBOX_FORK) **toys.argv &= 0x7f; 2970f66f451Sopenharmony_ci 2980f66f451Sopenharmony_ci if (pipes) { 2990f66f451Sopenharmony_ci if (cestnepasun[1]) { 3000f66f451Sopenharmony_ci pipes[0] = cestnepasun[1]; 3010f66f451Sopenharmony_ci close(cestnepasun[0]); 3020f66f451Sopenharmony_ci } 3030f66f451Sopenharmony_ci if (cestnepasun[2]) { 3040f66f451Sopenharmony_ci pipes[1] = cestnepasun[2]; 3050f66f451Sopenharmony_ci close(cestnepasun[3]); 3060f66f451Sopenharmony_ci } 3070f66f451Sopenharmony_ci } 3080f66f451Sopenharmony_ci 3090f66f451Sopenharmony_ci return pid; 3100f66f451Sopenharmony_ci} 3110f66f451Sopenharmony_ci 3120f66f451Sopenharmony_ci// Wait for child process to exit, then return adjusted exit code. 3130f66f451Sopenharmony_ciint xwaitpid(pid_t pid) 3140f66f451Sopenharmony_ci{ 3150f66f451Sopenharmony_ci int status; 3160f66f451Sopenharmony_ci 3170f66f451Sopenharmony_ci while (-1 == waitpid(pid, &status, 0) && errno == EINTR); 3180f66f451Sopenharmony_ci 3190f66f451Sopenharmony_ci return WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status)+128; 3200f66f451Sopenharmony_ci} 3210f66f451Sopenharmony_ci 3220f66f451Sopenharmony_ciint xpclose_both(pid_t pid, int *pipes) 3230f66f451Sopenharmony_ci{ 3240f66f451Sopenharmony_ci if (pipes) { 3250f66f451Sopenharmony_ci close(pipes[0]); 3260f66f451Sopenharmony_ci close(pipes[1]); 3270f66f451Sopenharmony_ci } 3280f66f451Sopenharmony_ci 3290f66f451Sopenharmony_ci return xwaitpid(pid); 3300f66f451Sopenharmony_ci} 3310f66f451Sopenharmony_ci 3320f66f451Sopenharmony_ci// Wrapper to xpopen with a pipe for just one of stdin/stdout 3330f66f451Sopenharmony_cipid_t xpopen(char **argv, int *pipe, int isstdout) 3340f66f451Sopenharmony_ci{ 3350f66f451Sopenharmony_ci int pipes[2], pid; 3360f66f451Sopenharmony_ci 3370f66f451Sopenharmony_ci pipes[0] = isstdout ? 0 : -1; 3380f66f451Sopenharmony_ci pipes[1] = isstdout ? -1 : 1; 3390f66f451Sopenharmony_ci pid = xpopen_both(argv, pipes); 3400f66f451Sopenharmony_ci *pipe = pid ? pipes[!!isstdout] : -1; 3410f66f451Sopenharmony_ci 3420f66f451Sopenharmony_ci return pid; 3430f66f451Sopenharmony_ci} 3440f66f451Sopenharmony_ci 3450f66f451Sopenharmony_ciint xpclose(pid_t pid, int pipe) 3460f66f451Sopenharmony_ci{ 3470f66f451Sopenharmony_ci close(pipe); 3480f66f451Sopenharmony_ci 3490f66f451Sopenharmony_ci return xpclose_both(pid, 0); 3500f66f451Sopenharmony_ci} 3510f66f451Sopenharmony_ci 3520f66f451Sopenharmony_ci// Call xpopen and wait for it to finish, keeping existing stdin/stdout. 3530f66f451Sopenharmony_ciint xrun(char **argv) 3540f66f451Sopenharmony_ci{ 3550f66f451Sopenharmony_ci return xpclose_both(xpopen_both(argv, 0), 0); 3560f66f451Sopenharmony_ci} 3570f66f451Sopenharmony_ci 3580f66f451Sopenharmony_civoid xaccess(char *path, int flags) 3590f66f451Sopenharmony_ci{ 3600f66f451Sopenharmony_ci if (access(path, flags)) perror_exit("Can't access '%s'", path); 3610f66f451Sopenharmony_ci} 3620f66f451Sopenharmony_ci 3630f66f451Sopenharmony_ci// Die unless we can delete a file. (File must exist to be deleted.) 3640f66f451Sopenharmony_civoid xunlink(char *path) 3650f66f451Sopenharmony_ci{ 3660f66f451Sopenharmony_ci if (unlink(path)) perror_exit("unlink '%s'", path); 3670f66f451Sopenharmony_ci} 3680f66f451Sopenharmony_ci 3690f66f451Sopenharmony_ci// Die unless we can open/create a file, returning file descriptor. 3700f66f451Sopenharmony_ci// The meaning of O_CLOEXEC is reversed (it defaults on, pass it to disable) 3710f66f451Sopenharmony_ci// and WARN_ONLY tells us not to exit. 3720f66f451Sopenharmony_ciint xcreate_stdio(char *path, int flags, int mode) 3730f66f451Sopenharmony_ci{ 3740f66f451Sopenharmony_ci int fd = open(path, (flags^O_CLOEXEC)&~WARN_ONLY, mode); 3750f66f451Sopenharmony_ci 3760f66f451Sopenharmony_ci if (fd == -1) ((mode&WARN_ONLY) ? perror_msg_raw : perror_exit_raw)(path); 3770f66f451Sopenharmony_ci return fd; 3780f66f451Sopenharmony_ci} 3790f66f451Sopenharmony_ci 3800f66f451Sopenharmony_ci// Die unless we can open a file, returning file descriptor. 3810f66f451Sopenharmony_ciint xopen_stdio(char *path, int flags) 3820f66f451Sopenharmony_ci{ 3830f66f451Sopenharmony_ci return xcreate_stdio(path, flags, 0); 3840f66f451Sopenharmony_ci} 3850f66f451Sopenharmony_ci 3860f66f451Sopenharmony_civoid xpipe(int *pp) 3870f66f451Sopenharmony_ci{ 3880f66f451Sopenharmony_ci if (pipe(pp)) perror_exit("xpipe"); 3890f66f451Sopenharmony_ci} 3900f66f451Sopenharmony_ci 3910f66f451Sopenharmony_civoid xclose(int fd) 3920f66f451Sopenharmony_ci{ 3930f66f451Sopenharmony_ci if (fd != -1 && close(fd)) perror_exit("xclose"); 3940f66f451Sopenharmony_ci} 3950f66f451Sopenharmony_ci 3960f66f451Sopenharmony_ciint xdup(int fd) 3970f66f451Sopenharmony_ci{ 3980f66f451Sopenharmony_ci if (fd != -1) { 3990f66f451Sopenharmony_ci fd = dup(fd); 4000f66f451Sopenharmony_ci if (fd == -1) perror_exit("xdup"); 4010f66f451Sopenharmony_ci } 4020f66f451Sopenharmony_ci return fd; 4030f66f451Sopenharmony_ci} 4040f66f451Sopenharmony_ci 4050f66f451Sopenharmony_ci// Move file descriptor above stdin/stdout/stderr, using /dev/null to consume 4060f66f451Sopenharmony_ci// old one. (We should never be called with stdin/stdout/stderr closed, but...) 4070f66f451Sopenharmony_ciint notstdio(int fd) 4080f66f451Sopenharmony_ci{ 4090f66f451Sopenharmony_ci if (fd<0) return fd; 4100f66f451Sopenharmony_ci 4110f66f451Sopenharmony_ci while (fd<3) { 4120f66f451Sopenharmony_ci int fd2 = xdup(fd); 4130f66f451Sopenharmony_ci 4140f66f451Sopenharmony_ci close(fd); 4150f66f451Sopenharmony_ci xopen_stdio("/dev/null", O_RDWR); 4160f66f451Sopenharmony_ci fd = fd2; 4170f66f451Sopenharmony_ci } 4180f66f451Sopenharmony_ci 4190f66f451Sopenharmony_ci return fd; 4200f66f451Sopenharmony_ci} 4210f66f451Sopenharmony_ci 4220f66f451Sopenharmony_civoid xrename(char *from, char *to) 4230f66f451Sopenharmony_ci{ 4240f66f451Sopenharmony_ci if (rename(from, to)) perror_exit("rename %s -> %s", from, to); 4250f66f451Sopenharmony_ci} 4260f66f451Sopenharmony_ci 4270f66f451Sopenharmony_ciint xtempfile(char *name, char **tempname) 4280f66f451Sopenharmony_ci{ 4290f66f451Sopenharmony_ci int fd; 4300f66f451Sopenharmony_ci 4310f66f451Sopenharmony_ci *tempname = xmprintf("%s%s", name, "XXXXXX"); 4320f66f451Sopenharmony_ci if(-1 == (fd = mkstemp(*tempname))) error_exit("no temp file"); 4330f66f451Sopenharmony_ci 4340f66f451Sopenharmony_ci return fd; 4350f66f451Sopenharmony_ci} 4360f66f451Sopenharmony_ci 4370f66f451Sopenharmony_ci// Create a file but don't return stdin/stdout/stderr 4380f66f451Sopenharmony_ciint xcreate(char *path, int flags, int mode) 4390f66f451Sopenharmony_ci{ 4400f66f451Sopenharmony_ci return notstdio(xcreate_stdio(path, flags, mode)); 4410f66f451Sopenharmony_ci} 4420f66f451Sopenharmony_ci 4430f66f451Sopenharmony_ci// Open a file descriptor NOT in stdin/stdout/stderr 4440f66f451Sopenharmony_ciint xopen(char *path, int flags) 4450f66f451Sopenharmony_ci{ 4460f66f451Sopenharmony_ci return notstdio(xopen_stdio(path, flags)); 4470f66f451Sopenharmony_ci} 4480f66f451Sopenharmony_ci 4490f66f451Sopenharmony_ci// Open read only, treating "-" as a synonym for stdin, defaulting to warn only 4500f66f451Sopenharmony_ciint openro(char *path, int flags) 4510f66f451Sopenharmony_ci{ 4520f66f451Sopenharmony_ci if (!strcmp(path, "-")) return 0; 4530f66f451Sopenharmony_ci 4540f66f451Sopenharmony_ci return xopen(path, flags^WARN_ONLY); 4550f66f451Sopenharmony_ci} 4560f66f451Sopenharmony_ci 4570f66f451Sopenharmony_ci// Open read only, treating "-" as a synonym for stdin. 4580f66f451Sopenharmony_ciint xopenro(char *path) 4590f66f451Sopenharmony_ci{ 4600f66f451Sopenharmony_ci return openro(path, O_RDONLY|WARN_ONLY); 4610f66f451Sopenharmony_ci} 4620f66f451Sopenharmony_ci 4630f66f451Sopenharmony_ciFILE *xfdopen(int fd, char *mode) 4640f66f451Sopenharmony_ci{ 4650f66f451Sopenharmony_ci FILE *f = fdopen(fd, mode); 4660f66f451Sopenharmony_ci 4670f66f451Sopenharmony_ci if (!f) perror_exit("xfdopen"); 4680f66f451Sopenharmony_ci 4690f66f451Sopenharmony_ci return f; 4700f66f451Sopenharmony_ci} 4710f66f451Sopenharmony_ci 4720f66f451Sopenharmony_ci// Die unless we can open/create a file, returning FILE *. 4730f66f451Sopenharmony_ciFILE *xfopen(char *path, char *mode) 4740f66f451Sopenharmony_ci{ 4750f66f451Sopenharmony_ci FILE *f = fopen(path, mode); 4760f66f451Sopenharmony_ci if (!f) perror_exit("No file %s", path); 4770f66f451Sopenharmony_ci return f; 4780f66f451Sopenharmony_ci} 4790f66f451Sopenharmony_ci 4800f66f451Sopenharmony_ci// Die if there's an error other than EOF. 4810f66f451Sopenharmony_cisize_t xread(int fd, void *buf, size_t len) 4820f66f451Sopenharmony_ci{ 4830f66f451Sopenharmony_ci ssize_t ret = read(fd, buf, len); 4840f66f451Sopenharmony_ci if (ret < 0) perror_exit("xread"); 4850f66f451Sopenharmony_ci 4860f66f451Sopenharmony_ci return ret; 4870f66f451Sopenharmony_ci} 4880f66f451Sopenharmony_ci 4890f66f451Sopenharmony_civoid xreadall(int fd, void *buf, size_t len) 4900f66f451Sopenharmony_ci{ 4910f66f451Sopenharmony_ci if (len != readall(fd, buf, len)) perror_exit("xreadall"); 4920f66f451Sopenharmony_ci} 4930f66f451Sopenharmony_ci 4940f66f451Sopenharmony_ci// There's no xwriteall(), just xwrite(). When we read, there may or may not 4950f66f451Sopenharmony_ci// be more data waiting. When we write, there is data and it had better go 4960f66f451Sopenharmony_ci// somewhere. 4970f66f451Sopenharmony_ci 4980f66f451Sopenharmony_civoid xwrite(int fd, void *buf, size_t len) 4990f66f451Sopenharmony_ci{ 5000f66f451Sopenharmony_ci if (len != writeall(fd, buf, len)) perror_exit("xwrite"); 5010f66f451Sopenharmony_ci} 5020f66f451Sopenharmony_ci 5030f66f451Sopenharmony_ci// Die if lseek fails, probably due to being called on a pipe. 5040f66f451Sopenharmony_ci 5050f66f451Sopenharmony_cioff_t xlseek(int fd, off_t offset, int whence) 5060f66f451Sopenharmony_ci{ 5070f66f451Sopenharmony_ci offset = lseek(fd, offset, whence); 5080f66f451Sopenharmony_ci if (offset<0) perror_exit("lseek"); 5090f66f451Sopenharmony_ci 5100f66f451Sopenharmony_ci return offset; 5110f66f451Sopenharmony_ci} 5120f66f451Sopenharmony_ci 5130f66f451Sopenharmony_cichar *xgetcwd(void) 5140f66f451Sopenharmony_ci{ 5150f66f451Sopenharmony_ci char *buf = getcwd(NULL, 0); 5160f66f451Sopenharmony_ci if (!buf) perror_exit("xgetcwd"); 5170f66f451Sopenharmony_ci 5180f66f451Sopenharmony_ci return buf; 5190f66f451Sopenharmony_ci} 5200f66f451Sopenharmony_ci 5210f66f451Sopenharmony_civoid xstat(char *path, struct stat *st) 5220f66f451Sopenharmony_ci{ 5230f66f451Sopenharmony_ci if(stat(path, st)) perror_exit("Can't stat %s", path); 5240f66f451Sopenharmony_ci} 5250f66f451Sopenharmony_ci 5260f66f451Sopenharmony_ci// Canonicalize path, even to file with one or more missing components at end. 5270f66f451Sopenharmony_ci// Returns allocated string for pathname or NULL if doesn't exist 5280f66f451Sopenharmony_ci// exact = 1 file must exist, 0 dir must exist, -1 show theoretical location 5290f66f451Sopenharmony_cichar *xabspath(char *path, int exact) 5300f66f451Sopenharmony_ci{ 5310f66f451Sopenharmony_ci struct string_list *todo, *done = 0; 5320f66f451Sopenharmony_ci int try = 9999, dirfd = open("/", O_PATH), missing = 0; 5330f66f451Sopenharmony_ci char *ret; 5340f66f451Sopenharmony_ci 5350f66f451Sopenharmony_ci // If this isn't an absolute path, start with cwd. 5360f66f451Sopenharmony_ci if (*path != '/') { 5370f66f451Sopenharmony_ci char *temp = xgetcwd(); 5380f66f451Sopenharmony_ci 5390f66f451Sopenharmony_ci splitpath(path, splitpath(temp, &todo)); 5400f66f451Sopenharmony_ci free(temp); 5410f66f451Sopenharmony_ci } else splitpath(path, &todo); 5420f66f451Sopenharmony_ci 5430f66f451Sopenharmony_ci // Iterate through path components in todo, prepend processed ones to done. 5440f66f451Sopenharmony_ci while (todo) { 5450f66f451Sopenharmony_ci struct string_list *new = llist_pop(&todo), **tail; 5460f66f451Sopenharmony_ci ssize_t len; 5470f66f451Sopenharmony_ci 5480f66f451Sopenharmony_ci // Eventually break out of endless loops 5490f66f451Sopenharmony_ci if (!try--) { 5500f66f451Sopenharmony_ci errno = ELOOP; 5510f66f451Sopenharmony_ci goto error; 5520f66f451Sopenharmony_ci } 5530f66f451Sopenharmony_ci 5540f66f451Sopenharmony_ci // Removable path componenents. 5550f66f451Sopenharmony_ci if (!strcmp(new->str, ".") || !strcmp(new->str, "..")) { 5560f66f451Sopenharmony_ci int x = new->str[1]; 5570f66f451Sopenharmony_ci 5580f66f451Sopenharmony_ci free(new); 5590f66f451Sopenharmony_ci if (!x) continue; 5600f66f451Sopenharmony_ci if (done) free(llist_pop(&done)); 5610f66f451Sopenharmony_ci len = 0; 5620f66f451Sopenharmony_ci 5630f66f451Sopenharmony_ci if (missing) missing--; 5640f66f451Sopenharmony_ci else { 5650f66f451Sopenharmony_ci if (-1 == (x = openat(dirfd, "..", O_PATH))) goto error; 5660f66f451Sopenharmony_ci close(dirfd); 5670f66f451Sopenharmony_ci dirfd = x; 5680f66f451Sopenharmony_ci } 5690f66f451Sopenharmony_ci continue; 5700f66f451Sopenharmony_ci } 5710f66f451Sopenharmony_ci 5720f66f451Sopenharmony_ci // Is this a symlink? 5730f66f451Sopenharmony_ci len = readlinkat(dirfd, new->str, libbuf, sizeof(libbuf)); 5740f66f451Sopenharmony_ci if (len>4095) goto error; 5750f66f451Sopenharmony_ci 5760f66f451Sopenharmony_ci // Not a symlink: add to linked list, move dirfd, fail if error 5770f66f451Sopenharmony_ci if (len<1) { 5780f66f451Sopenharmony_ci int fd; 5790f66f451Sopenharmony_ci 5800f66f451Sopenharmony_ci new->next = done; 5810f66f451Sopenharmony_ci done = new; 5820f66f451Sopenharmony_ci if (errno == EINVAL && !todo) break; 5830f66f451Sopenharmony_ci if (errno == ENOENT && exact<0) { 5840f66f451Sopenharmony_ci missing++; 5850f66f451Sopenharmony_ci continue; 5860f66f451Sopenharmony_ci } 5870f66f451Sopenharmony_ci if (errno != EINVAL && (exact || todo)) goto error; 5880f66f451Sopenharmony_ci 5890f66f451Sopenharmony_ci fd = openat(dirfd, new->str, O_PATH); 5900f66f451Sopenharmony_ci if (fd == -1 && (exact || todo || errno != ENOENT)) goto error; 5910f66f451Sopenharmony_ci close(dirfd); 5920f66f451Sopenharmony_ci dirfd = fd; 5930f66f451Sopenharmony_ci continue; 5940f66f451Sopenharmony_ci } 5950f66f451Sopenharmony_ci 5960f66f451Sopenharmony_ci // If this symlink is to an absolute path, discard existing resolved path 5970f66f451Sopenharmony_ci libbuf[len] = 0; 5980f66f451Sopenharmony_ci if (*libbuf == '/') { 5990f66f451Sopenharmony_ci llist_traverse(done, free); 6000f66f451Sopenharmony_ci done=0; 6010f66f451Sopenharmony_ci close(dirfd); 6020f66f451Sopenharmony_ci dirfd = open("/", O_PATH); 6030f66f451Sopenharmony_ci } 6040f66f451Sopenharmony_ci free(new); 6050f66f451Sopenharmony_ci 6060f66f451Sopenharmony_ci // prepend components of new path. Note symlink to "/" will leave new NULL 6070f66f451Sopenharmony_ci tail = splitpath(libbuf, &new); 6080f66f451Sopenharmony_ci 6090f66f451Sopenharmony_ci // symlink to "/" will return null and leave tail alone 6100f66f451Sopenharmony_ci if (new) { 6110f66f451Sopenharmony_ci *tail = todo; 6120f66f451Sopenharmony_ci todo = new; 6130f66f451Sopenharmony_ci } 6140f66f451Sopenharmony_ci } 6150f66f451Sopenharmony_ci close(dirfd); 6160f66f451Sopenharmony_ci 6170f66f451Sopenharmony_ci // At this point done has the path, in reverse order. Reverse list while 6180f66f451Sopenharmony_ci // calculating buffer length. 6190f66f451Sopenharmony_ci 6200f66f451Sopenharmony_ci try = 2; 6210f66f451Sopenharmony_ci while (done) { 6220f66f451Sopenharmony_ci struct string_list *temp = llist_pop(&done); 6230f66f451Sopenharmony_ci 6240f66f451Sopenharmony_ci if (todo) try++; 6250f66f451Sopenharmony_ci try += strlen(temp->str); 6260f66f451Sopenharmony_ci temp->next = todo; 6270f66f451Sopenharmony_ci todo = temp; 6280f66f451Sopenharmony_ci } 6290f66f451Sopenharmony_ci 6300f66f451Sopenharmony_ci // Assemble return buffer 6310f66f451Sopenharmony_ci 6320f66f451Sopenharmony_ci ret = xmalloc(try); 6330f66f451Sopenharmony_ci *ret = '/'; 6340f66f451Sopenharmony_ci ret [try = 1] = 0; 6350f66f451Sopenharmony_ci while (todo) { 6360f66f451Sopenharmony_ci if (try>1) ret[try++] = '/'; 6370f66f451Sopenharmony_ci try = stpcpy(ret+try, todo->str) - ret; 6380f66f451Sopenharmony_ci free(llist_pop(&todo)); 6390f66f451Sopenharmony_ci } 6400f66f451Sopenharmony_ci 6410f66f451Sopenharmony_ci return ret; 6420f66f451Sopenharmony_ci 6430f66f451Sopenharmony_cierror: 6440f66f451Sopenharmony_ci close(dirfd); 6450f66f451Sopenharmony_ci llist_traverse(todo, free); 6460f66f451Sopenharmony_ci llist_traverse(done, free); 6470f66f451Sopenharmony_ci 6480f66f451Sopenharmony_ci return 0; 6490f66f451Sopenharmony_ci} 6500f66f451Sopenharmony_ci 6510f66f451Sopenharmony_civoid xchdir(char *path) 6520f66f451Sopenharmony_ci{ 6530f66f451Sopenharmony_ci if (chdir(path)) perror_exit("chdir '%s'", path); 6540f66f451Sopenharmony_ci} 6550f66f451Sopenharmony_ci 6560f66f451Sopenharmony_civoid xchroot(char *path) 6570f66f451Sopenharmony_ci{ 6580f66f451Sopenharmony_ci if (chroot(path)) error_exit("chroot '%s'", path); 6590f66f451Sopenharmony_ci xchdir("/"); 6600f66f451Sopenharmony_ci} 6610f66f451Sopenharmony_ci 6620f66f451Sopenharmony_cistruct passwd *xgetpwuid(uid_t uid) 6630f66f451Sopenharmony_ci{ 6640f66f451Sopenharmony_ci struct passwd *pwd = getpwuid(uid); 6650f66f451Sopenharmony_ci if (!pwd) error_exit("bad uid %ld", (long)uid); 6660f66f451Sopenharmony_ci return pwd; 6670f66f451Sopenharmony_ci} 6680f66f451Sopenharmony_ci 6690f66f451Sopenharmony_cistruct group *xgetgrgid(gid_t gid) 6700f66f451Sopenharmony_ci{ 6710f66f451Sopenharmony_ci struct group *group = getgrgid(gid); 6720f66f451Sopenharmony_ci 6730f66f451Sopenharmony_ci if (!group) perror_exit("bad gid %ld", (long)gid); 6740f66f451Sopenharmony_ci return group; 6750f66f451Sopenharmony_ci} 6760f66f451Sopenharmony_ci 6770f66f451Sopenharmony_ciunsigned xgetuid(char *name) 6780f66f451Sopenharmony_ci{ 6790f66f451Sopenharmony_ci struct passwd *up = getpwnam(name); 6800f66f451Sopenharmony_ci char *s = 0; 6810f66f451Sopenharmony_ci long uid; 6820f66f451Sopenharmony_ci 6830f66f451Sopenharmony_ci if (up) return up->pw_uid; 6840f66f451Sopenharmony_ci 6850f66f451Sopenharmony_ci uid = estrtol(name, &s, 10); 6860f66f451Sopenharmony_ci if (!errno && s && !*s && uid>=0 && uid<=UINT_MAX) return uid; 6870f66f451Sopenharmony_ci 6880f66f451Sopenharmony_ci error_exit("bad user '%s'", name); 6890f66f451Sopenharmony_ci} 6900f66f451Sopenharmony_ci 6910f66f451Sopenharmony_ciunsigned xgetgid(char *name) 6920f66f451Sopenharmony_ci{ 6930f66f451Sopenharmony_ci struct group *gr = getgrnam(name); 6940f66f451Sopenharmony_ci char *s = 0; 6950f66f451Sopenharmony_ci long gid; 6960f66f451Sopenharmony_ci 6970f66f451Sopenharmony_ci if (gr) return gr->gr_gid; 6980f66f451Sopenharmony_ci 6990f66f451Sopenharmony_ci gid = estrtol(name, &s, 10); 7000f66f451Sopenharmony_ci if (!errno && s && !*s && gid>=0 && gid<=UINT_MAX) return gid; 7010f66f451Sopenharmony_ci 7020f66f451Sopenharmony_ci error_exit("bad group '%s'", name); 7030f66f451Sopenharmony_ci} 7040f66f451Sopenharmony_ci 7050f66f451Sopenharmony_cistruct passwd *xgetpwnam(char *name) 7060f66f451Sopenharmony_ci{ 7070f66f451Sopenharmony_ci struct passwd *up = getpwnam(name); 7080f66f451Sopenharmony_ci 7090f66f451Sopenharmony_ci if (!up) perror_exit("bad user '%s'", name); 7100f66f451Sopenharmony_ci return up; 7110f66f451Sopenharmony_ci} 7120f66f451Sopenharmony_ci 7130f66f451Sopenharmony_cistruct group *xgetgrnam(char *name) 7140f66f451Sopenharmony_ci{ 7150f66f451Sopenharmony_ci struct group *gr = getgrnam(name); 7160f66f451Sopenharmony_ci 7170f66f451Sopenharmony_ci if (!gr) perror_exit("bad group '%s'", name); 7180f66f451Sopenharmony_ci return gr; 7190f66f451Sopenharmony_ci} 7200f66f451Sopenharmony_ci 7210f66f451Sopenharmony_ci// setuid() can fail (for example, too many processes belonging to that user), 7220f66f451Sopenharmony_ci// which opens a security hole if the process continues as the original user. 7230f66f451Sopenharmony_ci 7240f66f451Sopenharmony_civoid xsetuser(struct passwd *pwd) 7250f66f451Sopenharmony_ci{ 7260f66f451Sopenharmony_ci if (initgroups(pwd->pw_name, pwd->pw_gid) || setgid(pwd->pw_uid) 7270f66f451Sopenharmony_ci || setuid(pwd->pw_uid)) perror_exit("xsetuser '%s'", pwd->pw_name); 7280f66f451Sopenharmony_ci} 7290f66f451Sopenharmony_ci 7300f66f451Sopenharmony_ci// This can return null (meaning file not found). It just won't return null 7310f66f451Sopenharmony_ci// for memory allocation reasons. 7320f66f451Sopenharmony_cichar *xreadlinkat(int dir, char *name) 7330f66f451Sopenharmony_ci{ 7340f66f451Sopenharmony_ci int len, size = 0; 7350f66f451Sopenharmony_ci char *buf = 0; 7360f66f451Sopenharmony_ci 7370f66f451Sopenharmony_ci // Grow by 64 byte chunks until it's big enough. 7380f66f451Sopenharmony_ci for(;;) { 7390f66f451Sopenharmony_ci size +=64; 7400f66f451Sopenharmony_ci buf = xrealloc(buf, size); 7410f66f451Sopenharmony_ci len = readlinkat(dir, name, buf, size); 7420f66f451Sopenharmony_ci 7430f66f451Sopenharmony_ci if (len<0) { 7440f66f451Sopenharmony_ci free(buf); 7450f66f451Sopenharmony_ci return 0; 7460f66f451Sopenharmony_ci } 7470f66f451Sopenharmony_ci if (len<size) { 7480f66f451Sopenharmony_ci buf[len]=0; 7490f66f451Sopenharmony_ci return buf; 7500f66f451Sopenharmony_ci } 7510f66f451Sopenharmony_ci } 7520f66f451Sopenharmony_ci} 7530f66f451Sopenharmony_ci 7540f66f451Sopenharmony_cichar *xreadlink(char *name) 7550f66f451Sopenharmony_ci{ 7560f66f451Sopenharmony_ci return xreadlinkat(AT_FDCWD, name); 7570f66f451Sopenharmony_ci} 7580f66f451Sopenharmony_ci 7590f66f451Sopenharmony_ci 7600f66f451Sopenharmony_cichar *xreadfile(char *name, char *buf, off_t len) 7610f66f451Sopenharmony_ci{ 7620f66f451Sopenharmony_ci if (!(buf = readfile(name, buf, len))) perror_exit("Bad '%s'", name); 7630f66f451Sopenharmony_ci 7640f66f451Sopenharmony_ci return buf; 7650f66f451Sopenharmony_ci} 7660f66f451Sopenharmony_ci 7670f66f451Sopenharmony_ci// The data argument to ioctl() is actually long, but it's usually used as 7680f66f451Sopenharmony_ci// a pointer. If you need to feed in a number, do (void *)(long) typecast. 7690f66f451Sopenharmony_ciint xioctl(int fd, int request, void *data) 7700f66f451Sopenharmony_ci{ 7710f66f451Sopenharmony_ci int rc; 7720f66f451Sopenharmony_ci 7730f66f451Sopenharmony_ci errno = 0; 7740f66f451Sopenharmony_ci rc = ioctl(fd, request, data); 7750f66f451Sopenharmony_ci if (rc == -1 && errno) perror_exit("ioctl %x", request); 7760f66f451Sopenharmony_ci 7770f66f451Sopenharmony_ci return rc; 7780f66f451Sopenharmony_ci} 7790f66f451Sopenharmony_ci 7800f66f451Sopenharmony_ci// Open a /var/run/NAME.pid file, dying if we can't write it or if it currently 7810f66f451Sopenharmony_ci// exists and is this executable. 7820f66f451Sopenharmony_civoid xpidfile(char *name) 7830f66f451Sopenharmony_ci{ 7840f66f451Sopenharmony_ci char pidfile[256], spid[32]; 7850f66f451Sopenharmony_ci int i, fd; 7860f66f451Sopenharmony_ci pid_t pid; 7870f66f451Sopenharmony_ci 7880f66f451Sopenharmony_ci sprintf(pidfile, "/var/run/%s.pid", name); 7890f66f451Sopenharmony_ci // Try three times to open the sucker. 7900f66f451Sopenharmony_ci for (i=0; i<3; i++) { 7910f66f451Sopenharmony_ci fd = open(pidfile, O_CREAT|O_EXCL|O_WRONLY, 0644); 7920f66f451Sopenharmony_ci if (fd != -1) break; 7930f66f451Sopenharmony_ci 7940f66f451Sopenharmony_ci // If it already existed, read it. Loop for race condition. 7950f66f451Sopenharmony_ci fd = open(pidfile, O_RDONLY); 7960f66f451Sopenharmony_ci if (fd == -1) continue; 7970f66f451Sopenharmony_ci 7980f66f451Sopenharmony_ci // Is the old program still there? 7990f66f451Sopenharmony_ci spid[xread(fd, spid, sizeof(spid)-1)] = 0; 8000f66f451Sopenharmony_ci close(fd); 8010f66f451Sopenharmony_ci pid = atoi(spid); 8020f66f451Sopenharmony_ci if (pid < 1 || (kill(pid, 0) && errno == ESRCH)) unlink(pidfile); 8030f66f451Sopenharmony_ci 8040f66f451Sopenharmony_ci // An else with more sanity checking might be nice here. 8050f66f451Sopenharmony_ci } 8060f66f451Sopenharmony_ci 8070f66f451Sopenharmony_ci if (i == 3) error_exit("xpidfile %s", name); 8080f66f451Sopenharmony_ci 8090f66f451Sopenharmony_ci xwrite(fd, spid, sprintf(spid, "%ld\n", (long)getpid())); 8100f66f451Sopenharmony_ci close(fd); 8110f66f451Sopenharmony_ci} 8120f66f451Sopenharmony_ci 8130f66f451Sopenharmony_ci// error_exit if we couldn't copy all bytes 8140f66f451Sopenharmony_cilong long xsendfile_len(int in, int out, long long bytes) 8150f66f451Sopenharmony_ci{ 8160f66f451Sopenharmony_ci long long len = sendfile_len(in, out, bytes, 0); 8170f66f451Sopenharmony_ci 8180f66f451Sopenharmony_ci if (bytes != -1 && bytes != len) { 8190f66f451Sopenharmony_ci if (out == 1 && len<0) xexit(); 8200f66f451Sopenharmony_ci error_exit("short %s", (len<0) ? "write" : "read"); 8210f66f451Sopenharmony_ci } 8220f66f451Sopenharmony_ci 8230f66f451Sopenharmony_ci return len; 8240f66f451Sopenharmony_ci} 8250f66f451Sopenharmony_ci 8260f66f451Sopenharmony_ci// warn and pad with zeroes if we couldn't copy all bytes 8270f66f451Sopenharmony_civoid xsendfile_pad(int in, int out, long long len) 8280f66f451Sopenharmony_ci{ 8290f66f451Sopenharmony_ci len -= xsendfile_len(in, out, len); 8300f66f451Sopenharmony_ci if (len) { 8310f66f451Sopenharmony_ci perror_msg("short read"); 8320f66f451Sopenharmony_ci memset(libbuf, 0, sizeof(libbuf)); 8330f66f451Sopenharmony_ci while (len) { 8340f66f451Sopenharmony_ci int i = len>sizeof(libbuf) ? sizeof(libbuf) : len; 8350f66f451Sopenharmony_ci 8360f66f451Sopenharmony_ci xwrite(out, libbuf, i); 8370f66f451Sopenharmony_ci len -= i; 8380f66f451Sopenharmony_ci } 8390f66f451Sopenharmony_ci } 8400f66f451Sopenharmony_ci} 8410f66f451Sopenharmony_ci 8420f66f451Sopenharmony_ci// copy all of in to out 8430f66f451Sopenharmony_cilong long xsendfile(int in, int out) 8440f66f451Sopenharmony_ci{ 8450f66f451Sopenharmony_ci return xsendfile_len(in, out, -1); 8460f66f451Sopenharmony_ci} 8470f66f451Sopenharmony_ci 8480f66f451Sopenharmony_cidouble xstrtod(char *s) 8490f66f451Sopenharmony_ci{ 8500f66f451Sopenharmony_ci char *end; 8510f66f451Sopenharmony_ci double d; 8520f66f451Sopenharmony_ci 8530f66f451Sopenharmony_ci errno = 0; 8540f66f451Sopenharmony_ci d = strtod(s, &end); 8550f66f451Sopenharmony_ci if (!errno && *end) errno = E2BIG; 8560f66f451Sopenharmony_ci if (errno) perror_exit("strtod %s", s); 8570f66f451Sopenharmony_ci 8580f66f451Sopenharmony_ci return d; 8590f66f451Sopenharmony_ci} 8600f66f451Sopenharmony_ci 8610f66f451Sopenharmony_ci// parse fractional seconds with optional s/m/h/d suffix 8620f66f451Sopenharmony_cilong xparsetime(char *arg, long zeroes, long *fraction) 8630f66f451Sopenharmony_ci{ 8640f66f451Sopenharmony_ci long l, fr = 0, mask = 1; 8650f66f451Sopenharmony_ci char *end; 8660f66f451Sopenharmony_ci 8670f66f451Sopenharmony_ci if (*arg != '.' && !isdigit(*arg)) error_exit("Not a number '%s'", arg); 8680f66f451Sopenharmony_ci l = strtoul(arg, &end, 10); 8690f66f451Sopenharmony_ci if (*end == '.') { 8700f66f451Sopenharmony_ci end++; 8710f66f451Sopenharmony_ci while (zeroes--) { 8720f66f451Sopenharmony_ci fr *= 10; 8730f66f451Sopenharmony_ci mask *= 10; 8740f66f451Sopenharmony_ci if (isdigit(*end)) fr += *end++-'0'; 8750f66f451Sopenharmony_ci } 8760f66f451Sopenharmony_ci while (isdigit(*end)) end++; 8770f66f451Sopenharmony_ci } 8780f66f451Sopenharmony_ci 8790f66f451Sopenharmony_ci // Parse suffix 8800f66f451Sopenharmony_ci if (*end) { 8810f66f451Sopenharmony_ci int ismhd[]={1,60,3600,86400}, i = stridx("smhd", *end); 8820f66f451Sopenharmony_ci 8830f66f451Sopenharmony_ci if (i == -1 || *(end+1)) error_exit("Unknown suffix '%s'", end); 8840f66f451Sopenharmony_ci l *= ismhd[i]; 8850f66f451Sopenharmony_ci fr *= ismhd[i]; 8860f66f451Sopenharmony_ci l += fr/mask; 8870f66f451Sopenharmony_ci fr %= mask; 8880f66f451Sopenharmony_ci } 8890f66f451Sopenharmony_ci if (fraction) *fraction = fr; 8900f66f451Sopenharmony_ci 8910f66f451Sopenharmony_ci return l; 8920f66f451Sopenharmony_ci} 8930f66f451Sopenharmony_ci 8940f66f451Sopenharmony_cilong long xparsemillitime(char *arg) 8950f66f451Sopenharmony_ci{ 8960f66f451Sopenharmony_ci long l, ll; 8970f66f451Sopenharmony_ci 8980f66f451Sopenharmony_ci l = xparsetime(arg, 3, &ll); 8990f66f451Sopenharmony_ci 9000f66f451Sopenharmony_ci return (l*1000LL)+ll; 9010f66f451Sopenharmony_ci} 9020f66f451Sopenharmony_ci 9030f66f451Sopenharmony_civoid xparsetimespec(char *arg, struct timespec *ts) 9040f66f451Sopenharmony_ci{ 9050f66f451Sopenharmony_ci ts->tv_sec = xparsetime(arg, 9, &ts->tv_nsec); 9060f66f451Sopenharmony_ci} 9070f66f451Sopenharmony_ci 9080f66f451Sopenharmony_ci 9090f66f451Sopenharmony_ci// Compile a regular expression into a regex_t 9100f66f451Sopenharmony_civoid xregcomp(regex_t *preg, char *regex, int cflags) 9110f66f451Sopenharmony_ci{ 9120f66f451Sopenharmony_ci int rc; 9130f66f451Sopenharmony_ci 9140f66f451Sopenharmony_ci // BSD regex implementations don't support the empty regex (which isn't 9150f66f451Sopenharmony_ci // allowed in the POSIX grammar), but glibc does. Fake it for BSD. 9160f66f451Sopenharmony_ci if (!*regex) { 9170f66f451Sopenharmony_ci regex = "()"; 9180f66f451Sopenharmony_ci cflags |= REG_EXTENDED; 9190f66f451Sopenharmony_ci } 9200f66f451Sopenharmony_ci 9210f66f451Sopenharmony_ci if ((rc = regcomp(preg, regex, cflags))) { 9220f66f451Sopenharmony_ci regerror(rc, preg, libbuf, sizeof(libbuf)); 9230f66f451Sopenharmony_ci error_exit("bad regex: %s", libbuf); 9240f66f451Sopenharmony_ci } 9250f66f451Sopenharmony_ci} 9260f66f451Sopenharmony_ci 9270f66f451Sopenharmony_cichar *xtzset(char *new) 9280f66f451Sopenharmony_ci{ 9290f66f451Sopenharmony_ci char *old = getenv("TZ"); 9300f66f451Sopenharmony_ci 9310f66f451Sopenharmony_ci if (old) old = xstrdup(old); 9320f66f451Sopenharmony_ci if (new ? setenv("TZ", new, 1) : unsetenv("TZ")) perror_exit("setenv"); 9330f66f451Sopenharmony_ci tzset(); 9340f66f451Sopenharmony_ci 9350f66f451Sopenharmony_ci return old; 9360f66f451Sopenharmony_ci} 9370f66f451Sopenharmony_ci 9380f66f451Sopenharmony_ci// Set a signal handler 9390f66f451Sopenharmony_civoid xsignal_flags(int signal, void *handler, int flags) 9400f66f451Sopenharmony_ci{ 9410f66f451Sopenharmony_ci struct sigaction *sa = (void *)libbuf; 9420f66f451Sopenharmony_ci 9430f66f451Sopenharmony_ci memset(sa, 0, sizeof(struct sigaction)); 9440f66f451Sopenharmony_ci sa->sa_handler = handler; 9450f66f451Sopenharmony_ci sa->sa_flags = flags; 9460f66f451Sopenharmony_ci 9470f66f451Sopenharmony_ci if (sigaction(signal, sa, 0)) perror_exit("xsignal %d", signal); 9480f66f451Sopenharmony_ci} 9490f66f451Sopenharmony_ci 9500f66f451Sopenharmony_civoid xsignal(int signal, void *handler) 9510f66f451Sopenharmony_ci{ 9520f66f451Sopenharmony_ci xsignal_flags(signal, handler, 0); 9530f66f451Sopenharmony_ci} 9540f66f451Sopenharmony_ci 9550f66f451Sopenharmony_ci 9560f66f451Sopenharmony_citime_t xvali_date(struct tm *tm, char *str) 9570f66f451Sopenharmony_ci{ 9580f66f451Sopenharmony_ci time_t t; 9590f66f451Sopenharmony_ci 9600f66f451Sopenharmony_ci if (tm && (unsigned)tm->tm_sec<=60 && (unsigned)tm->tm_min<=59 9610f66f451Sopenharmony_ci && (unsigned)tm->tm_hour<=23 && tm->tm_mday && (unsigned)tm->tm_mday<=31 9620f66f451Sopenharmony_ci && (unsigned)tm->tm_mon<=11 && (t = mktime(tm)) != -1) return t; 9630f66f451Sopenharmony_ci 9640f66f451Sopenharmony_ci error_exit("bad date %s", str); 9650f66f451Sopenharmony_ci} 9660f66f451Sopenharmony_ci 9670f66f451Sopenharmony_ci// Parse date string (relative to current *t). Sets time_t and nanoseconds. 9680f66f451Sopenharmony_civoid xparsedate(char *str, time_t *t, unsigned *nano, int endian) 9690f66f451Sopenharmony_ci{ 9700f66f451Sopenharmony_ci struct tm tm; 9710f66f451Sopenharmony_ci time_t now = *t; 9720f66f451Sopenharmony_ci int len = 0, i = 0; 9730f66f451Sopenharmony_ci // Formats with seconds come first. Posix can't agree on whether 12 digits 9740f66f451Sopenharmony_ci // has year before (touch -t) or year after (date), so support both. 9750f66f451Sopenharmony_ci char *s = str, *p, *oldtz = 0, *formats[] = {"%Y-%m-%d %T", "%Y-%m-%dT%T", 9760f66f451Sopenharmony_ci "%H:%M:%S", "%Y-%m-%d %H:%M", "%Y-%m-%d", "%H:%M", "%m%d%H%M", 9770f66f451Sopenharmony_ci endian ? "%m%d%H%M%y" : "%y%m%d%H%M", 9780f66f451Sopenharmony_ci endian ? "%m%d%H%M%C%y" : "%C%y%m%d%H%M"}; 9790f66f451Sopenharmony_ci 9800f66f451Sopenharmony_ci *nano = 0; 9810f66f451Sopenharmony_ci 9820f66f451Sopenharmony_ci // Parse @UNIXTIME[.FRACTION] 9830f66f451Sopenharmony_ci if (*str == '@') { 9840f66f451Sopenharmony_ci long long ll; 9850f66f451Sopenharmony_ci 9860f66f451Sopenharmony_ci // Collect seconds and nanoseconds. 9870f66f451Sopenharmony_ci // &ll is not just t because we can't guarantee time_t is 64 bit (yet). 9880f66f451Sopenharmony_ci sscanf(s, "@%lld%n", &ll, &len); 9890f66f451Sopenharmony_ci if (s[len]=='.') { 9900f66f451Sopenharmony_ci s += len+1; 9910f66f451Sopenharmony_ci for (len = 0; len<9; len++) { 9920f66f451Sopenharmony_ci *nano *= 10; 9930f66f451Sopenharmony_ci if (isdigit(*s)) *nano += *s++-'0'; 9940f66f451Sopenharmony_ci } 9950f66f451Sopenharmony_ci } 9960f66f451Sopenharmony_ci *t = ll; 9970f66f451Sopenharmony_ci if (!s[len]) return; 9980f66f451Sopenharmony_ci xvali_date(0, str); 9990f66f451Sopenharmony_ci } 10000f66f451Sopenharmony_ci 10010f66f451Sopenharmony_ci // Trailing Z means UTC timezone, don't expect libc to know this. 10020f66f451Sopenharmony_ci // (Trimming it off here means it won't show up in error messages.) 10030f66f451Sopenharmony_ci if ((i = strlen(str)) && toupper(str[i-1])=='Z') { 10040f66f451Sopenharmony_ci str[--i] = 0; 10050f66f451Sopenharmony_ci oldtz = getenv("TZ"); 10060f66f451Sopenharmony_ci if (oldtz) oldtz = xstrdup(oldtz); 10070f66f451Sopenharmony_ci setenv("TZ", "UTC0", 1); 10080f66f451Sopenharmony_ci } 10090f66f451Sopenharmony_ci 10100f66f451Sopenharmony_ci // Try each format 10110f66f451Sopenharmony_ci for (i = 0; i<ARRAY_LEN(formats); i++) { 10120f66f451Sopenharmony_ci localtime_r(&now, &tm); 10130f66f451Sopenharmony_ci tm.tm_hour = tm.tm_min = tm.tm_sec = 0; 10140f66f451Sopenharmony_ci tm.tm_isdst = -endian; 10150f66f451Sopenharmony_ci 10160f66f451Sopenharmony_ci if ((p = strptime(s, formats[i], &tm))) { 10170f66f451Sopenharmony_ci if (*p == '.') { 10180f66f451Sopenharmony_ci p++; 10190f66f451Sopenharmony_ci // If format didn't already specify seconds, grab seconds 10200f66f451Sopenharmony_ci if (i>2) { 10210f66f451Sopenharmony_ci len = 0; 10220f66f451Sopenharmony_ci sscanf(p, "%2u%n", &tm.tm_sec, &len); 10230f66f451Sopenharmony_ci p += len; 10240f66f451Sopenharmony_ci } 10250f66f451Sopenharmony_ci // nanoseconds 10260f66f451Sopenharmony_ci for (len = 0; len<9; len++) { 10270f66f451Sopenharmony_ci *nano *= 10; 10280f66f451Sopenharmony_ci if (isdigit(*p)) *nano += *p++-'0'; 10290f66f451Sopenharmony_ci } 10300f66f451Sopenharmony_ci } 10310f66f451Sopenharmony_ci 10320f66f451Sopenharmony_ci if (!*p) break; 10330f66f451Sopenharmony_ci } 10340f66f451Sopenharmony_ci } 10350f66f451Sopenharmony_ci 10360f66f451Sopenharmony_ci // Sanity check field ranges 10370f66f451Sopenharmony_ci *t = xvali_date((i!=ARRAY_LEN(formats)) ? &tm : 0, str); 10380f66f451Sopenharmony_ci 10390f66f451Sopenharmony_ci if (oldtz) setenv("TZ", oldtz, 1); 10400f66f451Sopenharmony_ci free(oldtz); 10410f66f451Sopenharmony_ci} 10420f66f451Sopenharmony_ci 10430f66f451Sopenharmony_cichar *xgetline(FILE *fp, int *len) 10440f66f451Sopenharmony_ci{ 10450f66f451Sopenharmony_ci char *new = 0; 10460f66f451Sopenharmony_ci size_t linelen = 0; 10470f66f451Sopenharmony_ci 10480f66f451Sopenharmony_ci errno = 0; 10490f66f451Sopenharmony_ci if (1>(linelen = getline(&new, &linelen, fp))) { 10500f66f451Sopenharmony_ci if (errno) perror_msg("getline"); 10510f66f451Sopenharmony_ci new = 0; 10520f66f451Sopenharmony_ci } else if (new[linelen-1] == '\n') new[--linelen] = 0; 10530f66f451Sopenharmony_ci if (len) *len = linelen; 10540f66f451Sopenharmony_ci 10550f66f451Sopenharmony_ci return new; 10560f66f451Sopenharmony_ci} 1057