diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/args.c | 12 | ||||
-rw-r--r-- | lib/dirtree.c | 41 | ||||
-rw-r--r-- | lib/getmountlist.c | 10 | ||||
-rw-r--r-- | lib/help.c | 2 | ||||
-rw-r--r-- | lib/interestingtimes.c | 163 | ||||
-rw-r--r-- | lib/lib.c | 128 | ||||
-rw-r--r-- | lib/lib.h | 55 | ||||
-rw-r--r-- | lib/lsm.h | 115 | ||||
-rw-r--r-- | lib/net.c | 5 | ||||
-rw-r--r-- | lib/password.c | 43 | ||||
-rw-r--r-- | lib/pending.h | 2 | ||||
-rw-r--r-- | lib/portability.c | 39 | ||||
-rw-r--r-- | lib/portability.h | 89 | ||||
-rw-r--r-- | lib/xwrap.c | 106 |
14 files changed, 615 insertions, 195 deletions
@@ -3,6 +3,12 @@ * Copyright 2006 Rob Landley <rob@landley.net> */ +// NOTE: If option parsing segfaults, switch on TOYBOX_DEBUG in menuconfig. + +// Enabling TOYBOX_DEBUG in .config adds syntax checks to option string parsing +// which aren't needed in the final code (your option string is hardwired and +// should be correct when you ship), but are useful for development. + #include "toys.h" // Design goals: @@ -39,10 +45,6 @@ * this[1]="fruit" (argument to -b) */ -// Enabling TOYBOX_DEBUG in .config adds syntax checks to option string parsing -// which aren't needed in the final code (your option string is hardwired and -// should be correct when you ship), but are useful for development. - // What you can put in a get_opt string: // Any otherwise unused character (all letters, unprefixed numbers) specify // an option that sets a flag. The bit value is the same as the binary digit @@ -82,7 +84,6 @@ // + Synonyms (switch on all) [+abc] means -ab=-abc, -c=-abc // ! More than one in group is error [!abc] means -ab calls error_exit() // primarily useful if you can switch things back off again. -// // Notes from getopt man page // - and -- cannot be arguments. @@ -327,6 +328,7 @@ void parse_optflaglist(struct getoptflagstate *gof) for (new = gof->opts; new; new = new->next) { unsigned u = 1<<idx++; + if (new->c == 1) new->c = 0; new->dex[1] = u; if (new->flags & 1) gof->requires |= u; if (new->type) { diff --git a/lib/dirtree.c b/lib/dirtree.c index 60ce56b..1e89816 100644 --- a/lib/dirtree.c +++ b/lib/dirtree.c @@ -24,22 +24,21 @@ int dirtree_notdotdot(struct dirtree *catch) // (This doesn't open directory filehandles yet so as not to exhaust the // filehandle space on large trees, dirtree_handle_callback() does that.) -struct dirtree *dirtree_add_node(struct dirtree *parent, char *name, - int symfollow) +struct dirtree *dirtree_add_node(struct dirtree *parent, char *name, int flags) { struct dirtree *dt = NULL; struct stat st; - char buf[4096]; int len = 0, linklen = 0; if (name) { // open code this because haven't got node to call dirtree_parentfd() on yet int fd = parent ? parent->data : AT_FDCWD; - if (fstatat(fd, name, &st, symfollow ? 0 : AT_SYMLINK_NOFOLLOW)) goto error; + if (fstatat(fd, name, &st, AT_SYMLINK_NOFOLLOW*!(flags&DIRTREE_SYMFOLLOW))) + goto error; if (S_ISLNK(st.st_mode)) { - if (0>(linklen = readlinkat(fd, name, buf, 4095))) goto error; - buf[linklen++]=0; + if (0>(linklen = readlinkat(fd, name, libbuf, 4095))) goto error; + libbuf[linklen++]=0; } len = strlen(name); } @@ -50,7 +49,7 @@ struct dirtree *dirtree_add_node(struct dirtree *parent, char *name, strcpy(dt->name, name); if (linklen) { - dt->symlink = memcpy(len+(char *)dt, buf, linklen); + dt->symlink = memcpy(len+(char *)dt, libbuf, linklen); dt->data = --linklen; } } @@ -58,7 +57,7 @@ struct dirtree *dirtree_add_node(struct dirtree *parent, char *name, return dt; error: - if (notdotdot(name)) { + if (!(flags&DIRTREE_SHUTUP) && notdotdot(name)) { char *path = parent ? dirtree_path(parent, 0) : ""; perror_msg("%s%s%s", path, parent ? "/" : "", name); @@ -111,13 +110,13 @@ int dirtree_parentfd(struct dirtree *node) struct dirtree *dirtree_handle_callback(struct dirtree *new, int (*callback)(struct dirtree *node)) { - int flags, dir = S_ISDIR(new->st.st_mode); + int flags; + if (!new) return 0; if (!callback) callback = dirtree_notdotdot; - flags = callback(new); - if (dir) { + if (S_ISDIR(new->st.st_mode)) { if (flags & (DIRTREE_RECURSE|DIRTREE_COMEAGAIN)) { new->data = openat(dirtree_parentfd(new), new->name, O_CLOEXEC); flags = dirtree_recurse(new, callback, flags); @@ -144,9 +143,11 @@ int dirtree_recurse(struct dirtree *node, DIR *dir; if (node->data == -1 || !(dir = fdopendir(node->data))) { - char *path = dirtree_path(node, 0); - perror_msg("No %s", path); - free(path); + if (!(flags & DIRTREE_SHUTUP)) { + char *path = dirtree_path(node, 0); + perror_msg("No %s", path); + free(path); + } close(node->data); return flags; @@ -157,8 +158,7 @@ int dirtree_recurse(struct dirtree *node, // The extra parentheses are to shut the stupid compiler up. while ((entry = readdir(dir))) { - if (!(new = dirtree_add_node(node, entry->d_name, flags&DIRTREE_SYMFOLLOW))) - continue; + if (!(new = dirtree_add_node(node, entry->d_name, flags))) continue; new = dirtree_handle_callback(new, callback); if (new == DIRTREE_ABORTVAL) break; if (new) { @@ -179,14 +179,19 @@ int dirtree_recurse(struct dirtree *node, return flags; } +// Create dirtree root +struct dirtree *dirtree_start(char *name, int symfollow) +{ + return dirtree_add_node(0, name, DIRTREE_SYMFOLLOW*!!symfollow); +} + // Create dirtree from path, using callback to filter nodes. // If callback == NULL allocate a tree of struct dirtree nodes and return // pointer to root node. -// symfollow is just for the top of tree, callback return code controls children struct dirtree *dirtree_read(char *path, int (*callback)(struct dirtree *node)) { - struct dirtree *root = dirtree_add_node(0, path, 0); + struct dirtree *root = dirtree_start(path, 0); return root ? dirtree_handle_callback(root, callback) : DIRTREE_ABORTVAL; } diff --git a/lib/getmountlist.c b/lib/getmountlist.c index 5f4bc63..4fec41b 100644 --- a/lib/getmountlist.c +++ b/lib/getmountlist.c @@ -41,7 +41,7 @@ char *comma_iterate(char **list, int *len) return start; } -static void deslash(char *s) +static void octal_deslash(char *s) { char *o = s; @@ -78,7 +78,7 @@ int comma_scan(char *optlist, char *opt, int clean) no = 2*(*s == 'n' && s[1] == 'o'); if (optlen == len-no && !strncmp(opt, s+no, optlen)) { got = !no; - if (clean) memmove(s, optlist, strlen(optlist)+1); + if (clean && optlist) memmove(s, optlist, strlen(optlist)+1); } } @@ -90,7 +90,7 @@ int comma_scanall(char *optlist, char *scanlist) { int i = 1; - for (;;) { + while (scanlist && *scanlist) { char *opt = comma_iterate(&scanlist, &i), *s = xstrndup(opt, i); i = comma_scan(optlist, s, 0); @@ -165,8 +165,8 @@ struct mtab_list *xgetmountlist(char *path) mt->opts = stpcpy(mt->device, me->mnt_fsname)+1; strcpy(mt->opts, me->mnt_opts); - deslash(mt->dir); - deslash(mt->device); + octal_deslash(mt->dir); + octal_deslash(mt->device); } endmntent(fp); @@ -10,7 +10,7 @@ void show_help(void) {;} #undef NEWTOY #undef OLDTOY #define NEWTOY(name,opt,flags) help_##name "\0" -#define OLDTOY(name,oldname,opts,flags) "\xff" #oldname "\0" +#define OLDTOY(name,oldname,flags) "\xff" #oldname "\0" static char *help_data = #include "generated/newtoys.h" ; diff --git a/lib/interestingtimes.c b/lib/interestingtimes.c new file mode 100644 index 0000000..8f8b35c --- /dev/null +++ b/lib/interestingtimes.c @@ -0,0 +1,163 @@ +/* interestingtimes.c - cursor control + * + * Copyright 2015 Rob Landley <rob@landley.net> + */ + +#include "toys.h" + +int xgettty(void) +{ + int i, j; + + for (i = 0; i<3; i++) if (isatty(j = (i+1)%3)) return j; + + return xopen("/dev/tty", O_RDWR); +} + +// Quick and dirty query size of terminal, doesn't do ANSI probe fallback. +// set x=80 y=25 before calling to provide defaults. Returns 0 if couldn't +// determine size. + +int terminal_size(unsigned *xx, unsigned *yy) +{ + struct winsize ws; + unsigned i, x = 0, y = 0; + char *s; + + // stdin, stdout, stderr + for (i=0; i<3; i++) { + memset(&ws, 0, sizeof(ws)); + if (!ioctl(i, TIOCGWINSZ, &ws)) { + if (ws.ws_col) x = ws.ws_col; + if (ws.ws_row) y = ws.ws_row; + + break; + } + } + s = getenv("COLUMNS"); + if (s) sscanf(s, "%u", &x); + s = getenv("LINES"); + if (s) sscanf(s, "%u", &y); + + // Never return 0 for either value, leave it at default instead. + if (xx && x) *xx = x; + if (yy && y) *yy = y; + + return x || y; +} + +// Reset terminal to known state, saving copy of old state if old != NULL. +int set_terminal(int fd, int raw, struct termios *old) +{ + struct termios termio; + + // Fetch local copy of old terminfo, and copy struct contents to *old if set + if (!tcgetattr(fd, &termio) && old) *old = termio; + + // the following are the bits set for an xterm. Linux text mode TTYs by + // default add two additional bits that only matter for serial processing + // (turn serial line break into an interrupt, and XON/XOFF flow control) + + // Any key unblocks output, swap CR and NL on input + termio.c_iflag = IXANY|ICRNL|INLCR; + if (toys.which->flags & TOYFLAG_LOCALE) termio.c_iflag |= IUTF8; + + // Output appends CR to NL, does magic undocumented postprocessing + termio.c_oflag = ONLCR|OPOST; + + // Leave serial port speed alone + // termio.c_cflag = C_READ|CS8|EXTB; + + // Generate signals, input entire line at once, echo output + // erase, line kill, escape control characters with ^ + // erase line char at a time + // "extended" behavior: ctrl-V quotes next char, ctrl-R reprints unread chars, + // ctrl-W erases word + termio.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE|IEXTEN; + + if (raw) cfmakeraw(&termio); + + return tcsetattr(fd, TCSANOW, &termio); +} + +// Scan stdin for a keypress, parsing known escape sequences +// Returns: 0-255=literal, -1=EOF, -2=NONE, 256-...=index into seq +// scratch space is necessary because last char of !seq could start new seq +// Zero out first byte of scratch before first call to scan_key +// block=0 allows fetching multiple characters before updating display +int scan_key(char *scratch, int block) +{ + // up down right left pgup pgdn home end ins + char *seqs[] = {"\033[A", "\033[B", "\033[C", "\033[D", "\033[5~", "\033[6~", + "\033OH", "\033OF", "\033[2~", 0}; + struct pollfd pfd; + int maybe, i, j; + char *test; + + for (;;) { + pfd.fd = 0; + pfd.events = POLLIN; + pfd.revents = 0; + + // check sequences + maybe = 0; + if (*scratch) { + for (i = maybe = 0; (test = seqs[i]); i++) { + for (j = 0; j<*scratch; j++) if (scratch[j+1] != test[j]) break; + if (j == *scratch) { + maybe = 1; + if (!test[j]) { + // We recognized current sequence: consume and return + *scratch = 0; + return 256+i; + } + } + } + // If current data can't be a known sequence, return next raw char + if (!maybe) break; + } + + // Need more data to decide + + // 30 miliseconds is about the gap between characters at 300 baud + if (maybe || !block) if (!xpoll(&pfd, 1, 30*maybe)) break; + + if (1 != read(0, scratch+1+*scratch, 1)) return -1; + ++*scratch; + } + + // Was not a sequence + if (!*scratch) return -2; + i = scratch[1]; + if (--*scratch) memmove(scratch+1, scratch+2, *scratch); + + return i; +} + +void tty_esc(char *s) +{ + printf("\033[%s", s); +} + +void tty_jump(int x, int y) +{ + char s[32]; + + sprintf(s, "%d;%dH", y+1, x+1); + tty_esc(s); +} + +void tty_reset(void) +{ + set_terminal(1, 0, 0); + tty_esc("?25h"); + tty_esc("0m"); + tty_jump(0, 999); + tty_esc("K"); +} + +void tty_sigreset(int i) +{ + tty_reset(); + _exit(128+i); +} @@ -155,7 +155,7 @@ int mkpathat(int atfd, char *dir, mode_t lastmode, int flags) if (!(flags&2) || errno != EEXIST) return 1; } else if (flags&4) fprintf(stderr, "%s: created directory '%s'\n", toys.which->name, dir); - + if (!(*s = save)) break; } @@ -177,7 +177,7 @@ struct string_list **splitpath(char *path, struct string_list **list) if (len > 0) { *list = xmalloc(sizeof(struct string_list) + len + 1); (*list)->next = 0; - strncpy((*list)->str, new, len); + memcpy((*list)->str, new, len); (*list)->str[len] = 0; list = &(*list)->next; } @@ -210,7 +210,8 @@ struct string_list *find_in_path(char *path, char *filename) if (!len) sprintf(rnext->str, "%s/%s", cwd, filename); else { char *res = rnext->str; - strncpy(res, path, len); + + memcpy(res, path, len); res += len; *(res++) = '/'; strcpy(res, filename); @@ -232,13 +233,30 @@ struct string_list *find_in_path(char *path, char *filename) return rlist; } +long estrtol(char *str, char **end, int base) +{ + errno = 0; + + return strtol(str, end, base); +} + +long xstrtol(char *str, char **end, int base) +{ + long l = estrtol(str, end, base); + + if (errno) perror_exit("%s", str); + + return l; +} + // atol() with the kilo/mega/giga/tera/peta/exa extensions. // (zetta and yotta don't fit in 64 bits.) long atolx(char *numstr) { char *c, *suffixes="cbkmgtpe", *end; - long val = strtol(numstr, &c, 0); + long val; + val = xstrtol(numstr, &c, 0); if (*c) { if (c != numstr && (end = strchr(suffixes, tolower(*c)))) { int shift = end-suffixes-2; @@ -262,16 +280,6 @@ long atolx_range(char *numstr, long low, long high) return val; } -int numlen(long l) -{ - int len = 0; - while (l) { - l /= 10; - len++; - } - return len; -} - int stridx(char *haystack, char needle) { char *off; @@ -344,14 +352,12 @@ off_t fdlength(int fd) // Read contents of file as a single nul-terminated string. // malloc new one if buf=len=0 -char *readfile(char *name, char *ibuf, off_t len) +char *readfileat(int dirfd, char *name, char *ibuf, off_t len) { int fd; char *buf; - fd = open(name, O_RDONLY); - if (fd == -1) return 0; - + if (-1 == (fd = openat(dirfd, name, O_RDONLY))) return 0; if (len<1) { len = fdlength(fd); // proc files don't report a length, so try 1 page minimum. @@ -370,6 +376,11 @@ char *readfile(char *name, char *ibuf, off_t len) return buf; } +char *readfile(char *name, char *ibuf, off_t len) +{ + return readfileat(AT_FDCWD, name, ibuf, len); +} + // Sleep for this many thousandths of a second void msleep(long miliseconds) { @@ -511,8 +522,7 @@ int copy_tempfile(int fdin, char *name, char **tempname) struct stat statbuf; int fd; - *tempname = xstrndup(name, strlen(name)+6); - strcat(*tempname,"XXXXXX"); + *tempname = xmprintf("%s%s", name, "XXXXXX"); if(-1 == (fd = mkstemp(*tempname))) error_exit("no temp file"); if (!tempfile2zap) sigatexit(tempfile_handler); tempfile2zap = *tempname; @@ -570,36 +580,19 @@ void crc_init(unsigned int *crc_table, int little_endian) } } -// Quick and dirty query size of terminal, doesn't do ANSI probe fallback. -// set x=80 y=25 before calling to provide defaults. Returns 0 if couldn't -// determine size. +// Init base64 table -int terminal_size(unsigned *xx, unsigned *yy) +void base64_init(char *p) { - struct winsize ws; - unsigned i, x = 0, y = 0; - char *s; - - // stdin, stdout, stderr - for (i=0; i<3; i++) { - memset(&ws, 0, sizeof(ws)); - if (!ioctl(i, TIOCGWINSZ, &ws)) { - if (ws.ws_col) x = ws.ws_col; - if (ws.ws_row) y = ws.ws_row; + int i; - break; - } + for (i = 'A'; i != ':'; i++) { + if (i == 'Z'+1) i = 'a'; + if (i == 'z'+1) i = '0'; + *(p++) = i; } - s = getenv("COLUMNS"); - if (s) sscanf(s, "%u", &x); - s = getenv("ROWS"); - if (s) sscanf(s, "%u", &y); - - // Never return 0 for either value, leave it at default instead. - if (xx && x) *xx = x; - if (yy && y) *yy = y; - - return x || y; + *(p++) = '+'; + *(p++) = '/'; } int yesno(char *prompt, int def) @@ -670,8 +663,9 @@ int sig_to_num(char *pidstr) if (pidstr) { char *s; - i = strtol(pidstr, &s, 10); - if (!*s) return i; + + i = estrtol(pidstr, &s, 10); + if (!errno && !*s) return i; if (!strncasecmp(pidstr, "sig", 3)) pidstr+=3; } @@ -700,8 +694,8 @@ mode_t string_to_mode(char *modestr, mode_t mode) // Handle octal mode if (isdigit(*str)) { - mode = strtol(str, &s, 8); - if (*s || (mode & ~(07777))) goto barf; + mode = estrtol(str, &s, 8); + if (errno || *s || (mode & ~(07777))) goto barf; return mode | extrabits; } @@ -804,6 +798,14 @@ void mode_to_string(mode_t mode, char *buf) *buf = c; } +char *basename_r(char *name) +{ + char *s = strrchr(name, '/'); + + if (s) return s+1; + return name; +} + // Execute a callback for each PID that matches a process name from a list. void names_to_pid(char **names, int (*callback)(pid_t pid, char *name)) { @@ -822,7 +824,7 @@ void names_to_pid(char **names, int (*callback)(pid_t pid, char *name)) for (curname = names; *curname; curname++) if (**curname == '/' ? !strcmp(cmd, *curname) - : !strcmp(basename(cmd), basename(*curname))) + : !strcmp(basename_r(cmd), basename_r(*curname))) if (callback(u, *curname)) break; if (*curname) break; } @@ -835,8 +837,8 @@ int human_readable(char *buf, unsigned long long num) { int end, len; - len = sprintf(buf, "%lld", num); - end = ((len-1)%3)+1; + len = sprintf(buf, "%lld", num)-1; + end = (len%3)+1; len /= 3; if (len && end == 1) { @@ -851,3 +853,23 @@ int human_readable(char *buf, unsigned long long num) return end; } + +// 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); +} + +int xpoll(struct pollfd *fds, int nfds, int timeout) +{ + int i; + + for (;;) { + if (0>(i = poll(fds, nfds, timeout))) { + if (errno != EINTR && errno != ENOMEM) perror_exit("xpoll"); + else if (timeout>0) timeout--; + } else return i; + } +} @@ -50,6 +50,8 @@ void get_optflags(void); #define DIRTREE_COMEAGAIN 4 // Follow symlinks to directories #define DIRTREE_SYMFOLLOW 8 +// Don't warn about failure to stat +#define DIRTREE_SHUTUP 16 // Don't look at any more files in this directory. #define DIRTREE_ABORT 256 @@ -65,7 +67,8 @@ struct dirtree { char name[]; }; -struct dirtree *dirtree_add_node(struct dirtree *p, char *name, int symfollow); +struct dirtree *dirtree_start(char *name, int symfollow); +struct dirtree *dirtree_add_node(struct dirtree *p, char *name, int flags); char *dirtree_path(struct dirtree *node, int *plen); int dirtree_notdotdot(struct dirtree *catch); int dirtree_parentfd(struct dirtree *node); @@ -81,19 +84,18 @@ void show_help(void); // xwrap.c void xstrncpy(char *dest, char *src, size_t size); +void xstrncat(char *dest, char *src, size_t size); void xexit(void) noreturn; void *xmalloc(size_t size); void *xzalloc(size_t size); void *xrealloc(void *ptr, size_t size); char *xstrndup(char *s, size_t n); char *xstrdup(char *s); -char *xmprintf(char *format, ...); -void xprintf(char *format, ...); +char *xmprintf(char *format, ...) printf_format; +void xprintf(char *format, ...) printf_format; void xputs(char *s); void xputc(char c); void xflush(void); -pid_t xfork(void); -void xexec_optargs(int skip); void xexec(char **argv); pid_t xpopen_both(char **argv, int *pipes); int xpclose_both(pid_t pid, int *pipes); @@ -124,23 +126,28 @@ struct passwd *xgetpwuid(uid_t uid); struct group *xgetgrgid(gid_t gid); struct passwd *xgetpwnam(char *name); struct group *xgetgrnam(char *name); +struct passwd *xgetpwnamid(char *user); +struct group *xgetgrnamid(char *group); void xsetuser(struct passwd *pwd); char *xreadlink(char *name); long xparsetime(char *arg, long units, long *fraction); void xpidfile(char *name); void xregcomp(regex_t *preg, char *rexec, int cflags); +char *xtzset(char *new); +void xsignal(int signal, void *handler); // lib.c void verror_msg(char *msg, int err, va_list va); -void error_msg(char *msg, ...); -void perror_msg(char *msg, ...); -void error_exit(char *msg, ...) noreturn; -void perror_exit(char *msg, ...) noreturn; +void error_msg(char *msg, ...) printf_format; +void perror_msg(char *msg, ...) printf_format; +void error_exit(char *msg, ...) printf_format noreturn; +void perror_exit(char *msg, ...) printf_format noreturn; ssize_t readall(int fd, void *buf, size_t len); ssize_t writeall(int fd, void *buf, size_t len); off_t lskip(int fd, off_t offset); int mkpathat(int atfd, char *dir, mode_t lastmode, int flags); struct string_list **splitpath(char *path, struct string_list **list); +char *readfileat(int dirfd, char *name, char *buf, off_t len); char *readfile(char *name, char *buf, off_t len); void msleep(long miliseconds); int64_t peek_le(void *ptr, unsigned size); @@ -148,9 +155,10 @@ int64_t peek_be(void *ptr, unsigned size); int64_t peek(void *ptr, unsigned size); void poke(void *ptr, uint64_t val, int size); struct string_list *find_in_path(char *path, char *filename); +long estrtol(char *str, char **end, int base); +long xstrtol(char *str, char **end, int base); long atolx(char *c); long atolx_range(char *numstr, long low, long high); -int numlen(long l); int stridx(char *haystack, char needle); int unescape(char c); int strstart(char **a, char *b); @@ -166,12 +174,36 @@ int copy_tempfile(int fdin, char *name, char **tempname); void delete_tempfile(int fdin, int fdout, char **tempname); void replace_tempfile(int fdin, int fdout, char **tempname); void crc_init(unsigned int *crc_table, int little_endian); -int terminal_size(unsigned *x, unsigned *y); +void base64_init(char *p); int yesno(char *prompt, int def); int human_readable(char *buf, unsigned long long num); +int qstrcmp(const void *a, const void *b); +int xpoll(struct pollfd *fds, int nfds, int timeout); + +// interestingtimes.c +int xgettty(void); +int terminal_size(unsigned *xx, unsigned *yy); +int set_terminal(int fd, int raw, struct termios *old); +int scan_key(char *scratch, int block); +void tty_esc(char *s); +void tty_jump(int x, int y); +void tty_reset(void); +void tty_sigreset(int i); + +// Results from scan_key() +#define KEY_UP 256 +#define KEY_DOWN 257 +#define KEY_RIGHT 258 +#define KEY_LEFT 259 +#define KEY_PGUP 260 +#define KEY_PGDN 261 +#define KEY_HOME 262 +#define KEY_END 263 +#define KEY_INSERT 264 // net.c int xsocket(int domain, int type, int protocol); +void xsetsockopt(int fd, int level, int opt, void *val, socklen_t len); // password.c int get_salt(char *salt, char * algo); @@ -203,6 +235,7 @@ char *num_to_sig(int sig); mode_t string_to_mode(char *mode_str, mode_t base); void mode_to_string(mode_t mode, char *buf); +char *basename_r(char *name); void names_to_pid(char **names, int (*callback)(pid_t pid, char *name)); // Functions in need of further review/cleanup diff --git a/lib/lsm.h b/lib/lsm.h new file mode 100644 index 0000000..d7e7de9 --- /dev/null +++ b/lib/lsm.h @@ -0,0 +1,115 @@ +/* lsm.h - header file for lib directory + * + * Copyright 2015 Rob Landley <rob@landley.net> + */ + +#if CFG_TOYBOX_SELINUX +#include <selinux/selinux.h> +#else +#define is_selinux_enabled() 0 +#define setfscreatecon(...) (-1) +#define getcon(...) (-1) +#define getfilecon(...) (-1) +#define lgetfilecon(...) (-1) +#define fgetfilecon(...) (-1) +#define setfilecon(...) (-1) +#define lsetfilecon(...) (-1) +#define fsetfilecon(...) (-1) +#endif + +#if CFG_TOYBOX_SMACK +#include <sys/smack.h> +#include <sys/xattr.h> +#include <linux/xattr.h> +#else +#define XATTR_NAME_SMACK 0 +//ssize_t fgetxattr (int fd, char *name, void *value, size_t size); +#define smack_smackfs_path(...) (-1) +#define smack_new_label_from_self(...) (-1) +#define smack_new_label_from_path(...) (-1) +#define smack_new_label_from_file(...) (-1) +#define smack_set_label_for_self(...) (-1) +#define smack_set_label_for_path(...) (-1) +#define smack_set_label_for_file(...) (-1) +#endif + +// This turns into "return 0" when no LSM and lets code optimize out. +static inline int lsm_enabled(void) +{ + if (CFG_TOYBOX_SMACK) return !!smack_smackfs_path(); + else return is_selinux_enabled() == 1; +} + +static inline char *lsm_name(void) +{ + if (CFG_TOYBOX_SMACK) return "Smack"; + if (CFG_TOYBOX_SELINUX) return "SELinux"; + + return "LSM"; +} + +// Fetch this process's lsm context +static inline char *lsm_context(void) +{ + int ok = 0; + char *result; + + if (CFG_TOYBOX_SMACK) ok = smack_new_label_from_self(&result) > 0; + else ok = getcon(&result) == 0; + + return ok ? result : strdup("?"); +} + +// Set default label to apply to newly created stuff (NULL to clear it) +static inline int lsm_set_create(char *context) +{ + if (CFG_TOYBOX_SMACK) return smack_set_label_for_self(context); + else return setfscreatecon(context); +} + +// Label a file, following symlinks +static inline int lsm_set_context(char *filename, char *context) +{ + if (CFG_TOYBOX_SMACK) + return smack_set_label_for_path(filename, XATTR_NAME_SMACK, 1, context); + else return setfilecon(filename, context); +} + +// Label a file, don't follow symlinks +static inline int lsm_lset_context(char *filename, char *context) +{ + if (CFG_TOYBOX_SMACK) + return smack_set_label_for_path(filename, XATTR_NAME_SMACK, 0, context); + else return lsetfilecon(filename, context); +} + +// Label a file by filehandle +static inline int lsm_fset_context(int file, char *context) +{ + if (CFG_TOYBOX_SMACK) + return smack_set_label_for_file(file, XATTR_NAME_SMACK, context); + else return fsetfilecon(file, context); +} + +// returns -1 in case of error or else the length of the context */ +// context can be NULL to get the length only */ +static inline int lsm_get_context(char *filename, char **context) +{ + if (CFG_TOYBOX_SMACK) + return smack_new_label_from_path(filename, XATTR_NAME_SMACK, 1, context); + else return getfilecon(filename, context); +} + +static inline int lsm_lget_context(char *filename, char **context) +{ + if (CFG_TOYBOX_SMACK) + return smack_new_label_from_path(filename, XATTR_NAME_SMACK, 0, context); + else return lgetfilecon(filename, context); +} + +static inline int lsm_fget_context(int file, char **context) +{ + if (CFG_TOYBOX_SMACK) + return smack_new_label_from_file(file, XATTR_NAME_SMACK, context); + return fgetfilecon(file, context); +} @@ -7,3 +7,8 @@ int xsocket(int domain, int type, int protocol) if (fd < 0) perror_exit("socket %x %x", type, protocol); return fd; } + +void xsetsockopt(int fd, int level, int opt, void *val, socklen_t len) +{ + if (-1 == setsockopt(fd, level, opt, val, len)) perror_exit("setsockopt"); +} diff --git a/lib/password.c b/lib/password.c index 985bd57..bf13c44 100644 --- a/lib/password.c +++ b/lib/password.c @@ -1,6 +1,8 @@ /* password.c - password read/update helper functions. * * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com> + * + * TODO: cleanup */ #include "toys.h" @@ -8,7 +10,7 @@ // generate appropriate random salt string for given encryption algorithm. int get_salt(char *salt, char *algo) -{ +{ struct { char *type, id, len; } al[] = {{"des", 0, 2}, {"md5", 1, 8}, {"sha256", 5, 16}, {"sha512", 6, 16}}; @@ -46,39 +48,6 @@ int get_salt(char *salt, char *algo) return -1; } -// Reset terminal to known state, returning old state if old != NULL. -int set_terminal(int fd, int raw, struct termios *old) -{ - struct termios termio; - - if (!tcgetattr(fd, &termio) && old) *old = termio; - - // the following are the bits set for an xterm. Linux text mode TTYs by - // default add two additional bits that only matter for serial processing - // (turn serial line break into an interrupt, and XON/XOFF flow control) - - // Any key unblocks output, swap CR and NL on input - termio.c_iflag = IXANY|ICRNL|INLCR; - if (toys.which->flags & TOYFLAG_LOCALE) termio.c_iflag |= IUTF8; - - // Output appends CR to NL, does magic undocumented postprocessing - termio.c_oflag = ONLCR|OPOST; - - // Leave serial port speed alone - // termio.c_cflag = C_READ|CS8|EXTB; - - // Generate signals, input entire line at once, echo output - // erase, line kill, escape control characters with ^ - // erase line char at a time - // "extended" behavior: ctrl-V quotes next char, ctrl-R reprints unread chars, - // ctrl-W erases word - termio.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE|IEXTEN; - - if (raw) cfmakeraw(&termio); - - return tcsetattr(fd, TCSANOW, &termio); -} - // Prompt with mesg, read password into buf, return 0 for success 1 for fail int read_password(char *buf, int buflen, char *mesg) { @@ -128,9 +97,9 @@ static char *get_nextcolon(char *line, int cnt) } /*update_password is used by multiple utilities to update /etc/passwd, - * /etc/shadow, /etc/group and /etc/gshadow files, + * /etc/shadow, /etc/group and /etc/gshadow files, * which are used as user, group databeses - * entry can be + * entry can be * 1. encrypted password, when updating user password. * 2. complete entry for user details, when creating new user * 3. group members comma',' separated list, when adding user to group @@ -197,7 +166,7 @@ int update_password(char *filename, char* username, char* entry) current_ptr = get_nextcolon(current_ptr, 1); fprintf(newfp, "%s\n",current_ptr); } else fprintf(newfp, "%s\n",current_ptr); - } else if (!strcmp(toys.which->name, "groupadd") || + } else if (!strcmp(toys.which->name, "groupadd") || !strcmp(toys.which->name, "addgroup") || !strcmp(toys.which->name, "delgroup") || !strcmp(toys.which->name, "groupdel")){ diff --git a/lib/pending.h b/lib/pending.h index c67d81c..ffbd025 100644 --- a/lib/pending.h +++ b/lib/pending.h @@ -4,3 +4,5 @@ #define MAX_SALT_LEN 20 //3 for id, 16 for key, 1 for '\0' int read_password(char * buff, int buflen, char* mesg); int update_password(char *filename, char* username, char* encrypted); + +// TODO this goes away when lib/password.c cleaned up diff --git a/lib/portability.c b/lib/portability.c index 29608bc..6441076 100644 --- a/lib/portability.c +++ b/lib/portability.c @@ -9,7 +9,31 @@ #include <asm/unistd.h> #endif -#if defined(__APPLE__) || defined(__ANDROID__) +// We can't fork() on nommu systems, and vfork() requires an exec() or exit() +// before resuming the parent (because they share a heap until then). And no, +// we can't implement our own clone() call that does the equivalent of fork() +// because nommu heaps use physical addresses so if we copy the heap all our +// pointers are wrong. (You need an mmu in order to map two heaps to the same +// address range without interfering with each other.) In the absence of +// a portable way to tell malloc() to start a new heap without freeing the old +// one, you pretty much need the exec().) + +// So we exec ourselves (via /proc/self/exe, if anybody knows a way to +// re-exec self without depending on the filesystem, I'm all ears), +// and use the arguments to signal reentry. + +#if CFG_TOYBOX_FORK +pid_t xfork(void) +{ + pid_t pid = fork(); + + if (pid < 0) perror_exit("fork"); + + return pid; +} +#endif + +#if defined(__APPLE__) ssize_t getdelim(char **linep, size_t *np, int delim, FILE *stream) { int ch; @@ -37,7 +61,7 @@ ssize_t getdelim(char **linep, size_t *np, int delim, FILE *stream) new_line = realloc(*linep, new_len); if (!new_line) return -1; *np = new_len; - *linep = new_line; + line = *linep = new_line; } line[i] = ch; @@ -51,7 +75,7 @@ ssize_t getdelim(char **linep, size_t *np, int delim, FILE *stream) new_line = realloc(*linep, new_len); if (!new_line) return -1; *np = new_len; - *linep = new_line; + line = *linep = new_line; } line[i + 1] = '\0'; @@ -62,16 +86,7 @@ ssize_t getline(char **linep, size_t *np, FILE *stream) { return getdelim(linep, np, '\n', stream); } -#endif - -#if defined(__ANDROID__) -int sethostname(const char *name, size_t len) -{ - return syscall(__NR_sethostname, name, len); -} -#endif -#if defined(__APPLE__) extern char **environ; int clearenv(void) diff --git a/lib/portability.h b/lib/portability.h index 0dace96..ff22fa5 100644 --- a/lib/portability.h +++ b/lib/portability.h @@ -4,15 +4,6 @@ // in specific compiler, library, or OS versions, localize all that here // and in portability.c -// The tendency of gcc to produce stupid warnings continues with -// warn_unused_result, which warns about things like ignoring the return code -// of nice(2) (which is completely useless since -1 is a legitimate return -// value on success and even the man page tells you to use errno instead). - -// This makes it stop. - -#undef _FORTIFY_SOURCE - // For musl #define _ALL_SOURCE @@ -20,8 +11,14 @@ #ifdef __GNUC__ #define noreturn __attribute__((noreturn)) +#if CFG_TOYBOX_DEBUG +#define printf_format __attribute__((format(printf, 1, 2))) +#else +#define printf_format +#endif #else #define noreturn +#define printf_format #endif // Always use long file support. @@ -31,6 +28,9 @@ #include <features.h> +// Types various replacement prototypes need +#include <sys/types.h> + // Various constants old build environments might not have even if kernel does #ifndef AT_FDCWD @@ -69,6 +69,19 @@ int wcwidth(wchar_t wc); #include <time.h> char *strptime(const char *buf, const char *format, struct tm *tm); +// They didn't like posix basename so they defined another function with the +// same name and if you include libgen.h it #defines basename to something +// else (where they implemented the real basename), and that define breaks +// the table entry for the basename command. They didn't make a new function +// with a different name for their new behavior because gnu. +// +// Solution: don't use their broken header, provide an inline to redirect the +// correct name to the broken name. + +char *dirname(char *path); +char *__xpg_basename(char *path); +static inline char *basename(char *path) { return __xpg_basename(path); } + // uClibc pretends to be glibc and copied a lot of its bugs, but has a few more #if defined(__UCLIBC__) #include <unistd.h> @@ -81,13 +94,28 @@ pid_t getsid(pid_t pid); // any flag newer than MS_MOVE, which was added in 2001 (linux 2.5.0.5), // eleven years earlier. +#include <sys/mount.h> +#ifndef MS_MOVE #define MS_MOVE (1<<13) +#endif +#ifndef MS_REC #define MS_REC (1<<14) +#endif +#ifndef MS_SILENT #define MS_SILENT (1<<15) +#endif +#ifndef MS_UNBINDABLE #define MS_UNBINDABLE (1<<17) +#endif +#ifndef MS_PRIVATE #define MS_PRIVATE (1<<18) +#endif +#ifndef MS_SLAVE #define MS_SLAVE (1<<19) +#endif +#ifndef MS_SHARED #define MS_SHARED (1<<20) +#endif // When building under obsolete glibc (Ubuntu 8.04-ish), hold its hand a bit. #elif __GLIBC__ == 2 && __GLIBC_MINOR__ < 10 @@ -122,14 +150,21 @@ int utimensat(int fd, const char *path, const struct timespec times[2], int flag #ifndef MNT_DETACH #define MNT_DETACH 2 #endif -#endif +#endif // Old glibc +#endif // glibc in general + +#if !defined(__GLIBC__) && !defined(__BIONIC__) +// POSIX basename. +#include <libgen.h> #endif -#ifdef __MUSL__ -#include <unistd.h> -// Without this "rm -r dir" fails with "is directory". -#define faccessat(A, B, C, D) faccessat(A, B, C, 0) +// glibc was handled above; for 32-bit bionic we need to avoid a collision +// with toybox's basename_r so we can't include <libgen.h> even though that +// would give us a POSIX basename(3). +#if defined(__BIONIC__) +char *basename(char *path); +char *dirname(char *path); #endif // Work out how to do endianness @@ -173,7 +208,7 @@ int clearenv(void); #define SWAP_LE64(x) (x) #endif -#if defined(__APPLE__) || defined(__ANDROID__) \ +#if defined(__APPLE__) \ || (defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 10) ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream); ssize_t getline(char **lineptr, size_t *n, FILE *stream); @@ -184,9 +219,6 @@ ssize_t getline(char **lineptr, size_t *n, FILE *stream); #include <sys/swap.h> // Android is missing some headers and functions -#if defined(__ANDROID__) -int sethostname(const char *name, size_t len); -#endif // "generated/config.h" is included first #if CFG_TOYBOX_SHADOW #include <shadow.h> @@ -194,12 +226,6 @@ int sethostname(const char *name, size_t len); #if CFG_TOYBOX_UTMPX #include <utmpx.h> #endif -#if CFG_TOYBOX_PTY -#include <pty.h> -#else -pid_t forkpty(int *amaster, char *name, void *termp, void *winp); -#endif - // Some systems don't define O_NOFOLLOW, and it varies by architecture, so... #include <fcntl.h> @@ -207,10 +233,18 @@ pid_t forkpty(int *amaster, char *name, void *termp, void *winp); #define O_NOFOLLOW 0 #endif +#ifndef O_NOATIME +#define O_NOATIME 01000000 +#endif + #ifndef O_CLOEXEC #define O_CLOEXEC 02000000 #endif +#ifndef O_PATH +#define O_PATH 010000000 +#endif + #if defined(__SIZEOF_DOUBLE__) && defined(__SIZEOF_LONG__) \ && __SIZEOF_DOUBLE__ <= __SIZEOF_LONG__ typedef double FLOAT; @@ -218,3 +252,10 @@ typedef double FLOAT; typedef float FLOAT; #endif +#ifndef __uClinux__ +pid_t xfork(void); +#endif + +//#define strncpy(...) @@strncpyisbadmmkay@@ +//#define strncat(...) @@strncatisbadmmkay@@ + diff --git a/lib/xwrap.c b/lib/xwrap.c index 6216d91..54f2cbb 100644 --- a/lib/xwrap.c +++ b/lib/xwrap.c @@ -9,15 +9,29 @@ #include "toys.h" -// Strcpy with size checking: exit if there's not enough space for the string. +// strcpy and strncat with size checking. Size is the total space in "dest", +// including null terminator. Exit if there's not enough space for the string +// (including space for the null terminator), because silently truncating is +// still broken behavior. (And leaving the string unterminated is INSANE.) void xstrncpy(char *dest, char *src, size_t size) { if (strlen(src)+1 > size) error_exit("'%s' > %ld bytes", src, (long)size); strcpy(dest, src); } +void xstrncat(char *dest, char *src, size_t size) +{ + long len = strlen(src); + + if (len+strlen(dest)+1 > size) + error_exit("'%s%s' > %ld bytes", dest, src, (long)size); + strcpy(dest+len, src); +} + void xexit(void) { + if (fflush(NULL) || ferror(stdout)) + if (!toys.exitval) perror_msg("write"); if (toys.rebound) longjmp(*toys.rebound, 1); else exit(toys.exitval); } @@ -52,9 +66,10 @@ void *xrealloc(void *ptr, size_t size) // Die unless we can allocate a copy of this many bytes of string. char *xstrndup(char *s, size_t n) { - char *ret = xmalloc(++n); - strncpy(ret, s, n); - ret[--n]=0; + char *ret = strndup(s, ++n); + + if (!ret) error_exit("xstrndup"); + ret[--n] = 0; return ret; } @@ -114,26 +129,6 @@ void xflush(void) if (fflush(stdout) || ferror(stdout)) perror_exit("write");; } -pid_t xfork(void) -{ - pid_t pid = fork(); - - if (pid < 0) perror_exit("fork"); - - return pid; -} - -// Call xexec with a chunk of optargs, starting at skip. (You can't just -// call xexec() directly because toy_init() frees optargs.) -void xexec_optargs(int skip) -{ - char **s = toys.optargs; - - toys.optargs = 0; - xexec(s+skip); -} - - // Die unless we can exec argv[] (or run builtin command). Note that anything // with a path isn't a builtin, so /bin/sh won't match the builtin sh. void xexec(char **argv) @@ -343,7 +338,7 @@ void xstat(char *path, struct stat *st) // Cannonicalize path, even to file with one or more missing components at end. // if exact, require last path component to exist -char *xabspath(char *path, int exact) +char *xabspath(char *path, int exact) { struct string_list *todo, *done = 0; int try = 9999, dirfd = open("/", 0);; @@ -482,6 +477,38 @@ struct group *xgetgrgid(gid_t gid) return group; } +struct passwd *xgetpwnamid(char *user) +{ + struct passwd *up = getpwnam(user); + uid_t uid; + + if (!up) { + char *s = 0; + + uid = estrtol(user, &s, 10); + if (!errno && s && !*s) up = getpwuid(uid); + } + if (!up) perror_exit("user '%s'", user); + + return up; +} + +struct group *xgetgrnamid(char *group) +{ + struct group *gr = getgrnam(group); + gid_t gid; + + if (!gr) { + char *s = 0; + + gid = estrtol(group, &s, 10); + if (!errno && s && !*s) gr = getgrgid(gid); + } + if (!gr) perror_exit("group '%s'", group); + + return gr; +} + struct passwd *xgetpwnam(char *name) { struct passwd *up = getpwnam(name); @@ -587,13 +614,12 @@ void xpidfile(char *name) void xsendfile(int in, int out) { long len; - char buf[4096]; if (in<0) return; for (;;) { - len = xread(in, buf, 4096); + len = xread(in, libbuf, sizeof(libbuf)); if (len<1) break; - xwrite(out, buf, len); + xwrite(out, libbuf, len); } } @@ -605,7 +631,7 @@ long xparsetime(char *arg, long units, long *fraction) if (CFG_TOYBOX_FLOAT) d = strtod(arg, &arg); else l = strtoul(arg, &arg, 10); - + // Parse suffix if (*arg) { int ismhd[]={1,60,3600,86400}, i = stridx("smhd", *arg); @@ -633,3 +659,25 @@ void xregcomp(regex_t *preg, char *regex, int cflags) error_exit("xregcomp: %s", libbuf); } } + +char *xtzset(char *new) +{ + char *tz = getenv("TZ"); + + if (tz) tz = xstrdup(tz); + if (setenv("TZ", new, 1)) perror_exit("setenv"); + tzset(); + + return tz; +} + +// Set a signal handler +void xsignal(int signal, void *handler) +{ + struct sigaction *sa = (void *)libbuf; + + memset(sa, 0, sizeof(struct sigaction)); + sa->sa_handler = handler; + + if (sigaction(signal, sa, 0)) perror_exit("xsignal %d", signal); +} |