static int catv(unsigned opts, char **argv) { int retval = EXIT_SUCCESS; int fd; BUILD_BUG_ON(CATV_OPT_e != VISIBLE_ENDLINE); BUILD_BUG_ON(CATV_OPT_t != VISIBLE_SHOW_TABS); #if 0 /* These consts match, we can just pass "opts" to visible() */ if (opts & CATV_OPT_e) flags |= VISIBLE_ENDLINE; if (opts & CATV_OPT_t) flags |= VISIBLE_SHOW_TABS; #endif /* Read from stdin if there's nothing else to do. */ if (!argv[0]) *--argv = (char*)"-"; #define read_buf bb_common_bufsiz1 setup_common_bufsiz(); do { fd = open_or_warn_stdin(*argv); if (fd < 0) { retval = EXIT_FAILURE; continue; } for (;;) { int i, res; res = read(fd, read_buf, COMMON_BUFSIZE); if (res < 0) retval = EXIT_FAILURE; if (res <= 0) break; for (i = 0; i < res; i++) { unsigned char c = read_buf[i]; char buf[sizeof("M-^c")]; visible(c, buf, opts); fputs(buf, stdout); } } if (ENABLE_FEATURE_CLEAN_UP && fd) close(fd); } while (*++argv); fflush_stdout_and_exit(retval); }
static const char *human_time(time_t t) { /* Old static char *str; str = ctime(&t); str[strlen(str)-1] = '\0'; return str; */ /* coreutils 6.3 compat: */ /*static char buf[sizeof("YYYY-MM-DD HH:MM:SS.000000000")] ALIGN1;*/ #define buf bb_common_bufsiz1 setup_common_bufsiz(); strcpy(strftime_YYYYMMDDHHMMSS(buf, COMMON_BUFSIZE, &t), ".000000000"); return buf; #undef buf }
int split_main(int argc UNUSED_PARAM, char **argv) { unsigned suffix_len = 2; char *pfx; char *count_p; const char *sfx; off_t cnt = 1000; off_t remaining = 0; unsigned opt; ssize_t bytes_read, to_write; char *src; setup_common_bufsiz(); opt_complementary = "?2:a+"; /* max 2 args; -a N */ opt = getopt32(argv, "l:b:a:", &count_p, &count_p, &suffix_len); if (opt & SPLIT_OPT_l) cnt = XATOOFF(count_p); if (opt & SPLIT_OPT_b) // FIXME: also needs XATOOFF cnt = xatoull_sfx(count_p, IF_FEATURE_SPLIT_FANCY(split_suffixes) IF_NOT_FEATURE_SPLIT_FANCY(km_suffixes) ); sfx = "x"; argv += optind; if (argv[0]) { int fd; if (argv[1]) sfx = argv[1]; fd = xopen_stdin(argv[0]); xmove_fd(fd, STDIN_FILENO); } else { argv[0] = (char *) bb_msg_standard_input; } if (NAME_MAX < strlen(sfx) + suffix_len) bb_error_msg_and_die("suffix too long"); { char *char_p = xzalloc(suffix_len + 1); memset(char_p, 'a', suffix_len); pfx = xasprintf("%s%s", sfx, char_p); if (ENABLE_FEATURE_CLEAN_UP) free(char_p); } while (1) { bytes_read = safe_read(STDIN_FILENO, read_buffer, READ_BUFFER_SIZE); if (!bytes_read) break; if (bytes_read < 0) bb_simple_perror_msg_and_die(argv[0]); src = read_buffer; do { if (!remaining) { if (!pfx) bb_error_msg_and_die("suffixes exhausted"); xmove_fd(xopen(pfx, O_WRONLY | O_CREAT | O_TRUNC), 1); pfx = next_file(pfx, suffix_len); remaining = cnt; } if (opt & SPLIT_OPT_b) { /* split by bytes */ to_write = (bytes_read < remaining) ? bytes_read : remaining; remaining -= to_write; } else { /* split by lines */ /* can be sped up by using _memrchr_ * and writing many lines at once... */ char *end = memchr(src, '\n', bytes_read); if (end) { --remaining; to_write = end - src + 1; } else { to_write = bytes_read; } } xwrite(STDOUT_FILENO, src, to_write); bytes_read -= to_write; src += to_write; } while (bytes_read); } return EXIT_SUCCESS; }
int inotifyd_main(int argc, char **argv) { int n; unsigned mask; struct pollfd pfd; char **watches; // names of files being watched const char *args[5]; // sanity check: agent and at least one watch must be given if (!argv[1] || !argv[2]) bb_show_usage(); argv++; // inotify_add_watch will number watched files // starting from 1, thus watches[0] is unimportant, // and 1st file name is watches[1]. watches = argv; args[0] = *argv; args[4] = NULL; argc -= 2; // number of files we watch // open inotify pfd.fd = inotify_init(); if (pfd.fd < 0) bb_perror_msg_and_die("no kernel support"); // setup watches while (*++argv) { char *path = *argv; char *masks = strchr(path, ':'); mask = 0x0fff; // assuming we want all non-kernel events // if mask is specified -> if (masks) { *masks = '\0'; // split path and mask // convert mask names to mask bitset mask = 0; while (*++masks) { const char *found; found = memchr(mask_names, *masks, MASK_BITS); if (found) mask |= (1 << (found - mask_names)); } } // add watch n = inotify_add_watch(pfd.fd, path, mask); if (n < 0) bb_perror_msg_and_die("add watch (%s) failed", path); //bb_error_msg("added %d [%s]:%4X", n, path, mask); } // setup signals bb_signals(BB_FATAL_SIGS, record_signo); // do watch pfd.events = POLLIN; while (1) { int len; void *buf; struct inotify_event *ie; again: if (bb_got_signal) break; n = poll(&pfd, 1, -1); // Signal interrupted us? if (n < 0 && errno == EINTR) goto again; // Under Linux, above if() is not necessary. // Non-fatal signals, e.g. SIGCHLD, when set to SIG_DFL, // are not interrupting poll(). // Thus we can just break if n <= 0 (see below), // because EINTR will happen only on SIGTERM et al. // But this might be not true under other Unixes, // and is generally way too subtle to depend on. if (n <= 0) // strange error? break; // read out all pending events // (NB: len must be int, not ssize_t or long!) #define eventbuf bb_common_bufsiz1 setup_common_bufsiz(); xioctl(pfd.fd, FIONREAD, &len); ie = buf = (len <= COMMON_BUFSIZE) ? eventbuf : xmalloc(len); len = full_read(pfd.fd, buf, len); // process events. N.B. events may vary in length while (len > 0) { int i; // cache relevant events mask unsigned m = ie->mask & ((1 << MASK_BITS) - 1); if (m) { char events[MASK_BITS + 1]; char *s = events; for (i = 0; i < MASK_BITS; ++i, m >>= 1) { if ((m & 1) && (mask_names[i] != '\0')) *s++ = mask_names[i]; } *s = '\0'; if (LONE_CHAR(args[0], '-')) { /* "inotifyd - FILE": built-in echo */ printf(ie->len ? "%s\t%s\t%s\n" : "%s\t%s\n", events, watches[ie->wd], ie->name); fflush(stdout); } else { // bb_error_msg("exec %s %08X\t%s\t%s\t%s", args[0], // ie->mask, events, watches[ie->wd], ie->len ? ie->name : ""); args[1] = events; args[2] = watches[ie->wd]; args[3] = ie->len ? ie->name : NULL; spawn_and_wait((char **)args); } // we are done if all files got final x event if (ie->mask & 0x8000) { if (--argc <= 0) goto done; inotify_rm_watch(pfd.fd, ie->wd); } } // next event i = sizeof(struct inotify_event) + ie->len; len -= i; ie = (void*)((char*)ie + i); } if (eventbuf != buf) free(buf); } // while (1) done: return bb_got_signal; }
int logger_main(int argc UNUSED_PARAM, char **argv) { char *str_p, *str_t; int opt; int i = 0; int fd = -1; setup_common_bufsiz(); /* Fill out the name string early (may be overwritten later) */ str_t = uid2uname_utoa(geteuid()); /* Parse any options */ opt = getopt32(argv, "p:st:c", &str_p, &str_t); if (opt & 0x2) /* -s */ i |= LOG_PERROR; //if (opt & 0x4) /* -t */ openlog(str_t, i, 0); if (opt & 0x8) { /* -c */ fd = device_open(DEV_CONSOLE, O_WRONLY | O_NOCTTY | O_NONBLOCK); if (fd < 0) bb_error_msg("can't open console"); } i = LOG_USER | LOG_WARNING; if (opt & 0x1) /* -p */ i = pencode(str_p); argv += optind; if (!argv[0]) { while (fgets(strbuf, COMMON_BUFSIZE, stdin)) { if (strbuf[0] && NOT_LONE_CHAR(strbuf, '\n') ) { /* Neither "" nor "\n" */ syslog(i, "%s", strbuf); if (fd >= 0) { fdprintf(fd, "%s: %s%s", str_t, strbuf, strchr(strbuf, '\n') ? "" : "\n"); } } } } else { char *message = NULL; int len = 0; int pos = 0; do { len += strlen(*argv) + 1; message = xrealloc(message, len + 1); sprintf(message + pos, " %s", *argv), pos = len; } while (*++argv); syslog(i, "%s", message + 1); /* skip leading " " */ if (fd >= 0 && len) { fdprintf(fd, "%s:%s%s", str_t, message, message[len - 1] == '\n' ? "" : "\n"); } } closelog(); if (fd >= 0) close(fd); return EXIT_SUCCESS; }
int klogd_main(int argc UNUSED_PARAM, char **argv) { int i = 0; char *opt_c; int opt; int used; setup_common_bufsiz(); opt = getopt32(argv, "c:n", &opt_c); if (opt & OPT_LEVEL) { /* Valid levels are between 1 and 8 */ i = xatou_range(opt_c, 1, 8); } if (!(opt & OPT_FOREGROUND)) { bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv); } logmode = LOGMODE_SYSLOG; /* klogd_open() before openlog(), since it might use fixed fd 3, * and openlog() also may use the same fd 3 if we swap them: */ klogd_open(); openlog("kernel", 0, LOG_KERN); /* * glibc problem: for some reason, glibc changes LOG_KERN to LOG_USER * above. The logic behind this is that standard * http://pubs.opengroup.org/onlinepubs/9699919799/functions/syslog.html * says the following about openlog and syslog: * "LOG_USER * Messages generated by arbitrary processes. * This is the default facility identifier if none is specified." * * I believe glibc misinterpreted this text as "if openlog's * third parameter is 0 (=LOG_KERN), treat it as LOG_USER". * Whereas it was meant to say "if *syslog* is called with facility * 0 in its 1st parameter without prior call to openlog, then perform * implicit openlog(LOG_USER)". * * As a result of this, eh, feature, standard klogd was forced * to open-code its own openlog and syslog implementation (!). * * Note that prohibiting openlog(LOG_KERN) on libc level does not * add any security: any process can open a socket to "/dev/log" * and write a string "<0>Voila, a LOG_KERN + LOG_EMERG message" * * Google code search tells me there is no widespread use of * openlog("foo", 0, 0), thus fixing glibc won't break userspace. * * The bug against glibc was filed: * bugzilla.redhat.com/show_bug.cgi?id=547000 */ if (i) klogd_setloglevel(i); signal(SIGHUP, SIG_IGN); /* We want klogd_read to not be restarted, thus _norestart: */ bb_signals_recursive_norestart(BB_FATAL_SIGS, record_signo); syslog(LOG_NOTICE, "klogd started: %s", bb_banner); write_pidfile(CONFIG_PID_FILE_PATH "/klogd.pid"); used = 0; while (!bb_got_signal) { int n; int priority; char *start; /* "2 -- Read from the log." */ start = log_buffer + used; n = klogd_read(start, KLOGD_LOGBUF_SIZE-1 - used); if (n < 0) { if (errno == EINTR) continue; bb_perror_msg(READ_ERROR); break; } start[n] = '\0'; /* Process each newline-terminated line in the buffer */ start = log_buffer; while (1) { char *newline = strchrnul(start, '\n'); if (*newline == '\0') { /* This line is incomplete */ /* move it to the front of the buffer */ overlapping_strcpy(log_buffer, start); used = newline - start; if (used < KLOGD_LOGBUF_SIZE-1) { /* buffer isn't full */ break; } /* buffer is full, log it anyway */ used = 0; newline = NULL; } else { *newline++ = '\0'; } /* Extract the priority */ priority = LOG_INFO; if (*start == '<') { start++; if (*start) priority = strtoul(start, &start, 10); if (*start == '>') start++; } /* Log (only non-empty lines) */ if (*start) syslog(priority, "%s", start); if (!newline) break; start = newline; } } klogd_close(); syslog(LOG_NOTICE, "klogd: exiting"); remove_pidfile(CONFIG_PID_FILE_PATH "/klogd.pid"); if (bb_got_signal) kill_myself_with_sig(bb_got_signal); return EXIT_FAILURE; }
int chat_main(int argc UNUSED_PARAM, char **argv) { int record_fd = -1; bool echo = 0; // collection of device replies which cause unconditional termination llist_t *aborts = NULL; // inactivity period int timeout = DEFAULT_CHAT_TIMEOUT; // maximum length of abort string #if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN size_t max_abort_len = 0; #else #define max_abort_len MAX_ABORT_LEN #endif #if ENABLE_FEATURE_CHAT_TTY_HIFI struct termios tio0, tio; #endif // directive names enum { DIR_HANGUP = 0, DIR_ABORT, #if ENABLE_FEATURE_CHAT_CLR_ABORT DIR_CLR_ABORT, #endif DIR_TIMEOUT, DIR_ECHO, DIR_SAY, DIR_RECORD, }; // make x* functions fail with correct exitcode xfunc_error_retval = ERR_IO; // trap vanilla signals to prevent process from being killed suddenly bb_signals(0 + (1 << SIGHUP) + (1 << SIGINT) + (1 << SIGTERM) + (1 << SIGPIPE) , signal_handler); #if ENABLE_FEATURE_CHAT_TTY_HIFI //TODO: use set_termios_to_raw() tcgetattr(STDIN_FILENO, &tio); tio0 = tio; cfmakeraw(&tio); tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio); #endif #if ENABLE_FEATURE_CHAT_SWALLOW_OPTS getopt32(argv, "vVsSE"); argv += optind; #else argv++; // goto first arg #endif // handle chat expect-send pairs while (*argv) { // directive given? process it int key = index_in_strings( "HANGUP\0" "ABORT\0" #if ENABLE_FEATURE_CHAT_CLR_ABORT "CLR_ABORT\0" #endif "TIMEOUT\0" "ECHO\0" "SAY\0" "RECORD\0" , *argv ); if (key >= 0) { bool onoff; // cache directive value char *arg = *++argv; if (!arg) { #if ENABLE_FEATURE_CHAT_TTY_HIFI tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0); #endif bb_show_usage(); } // OFF -> 0, anything else -> 1 onoff = (0 != strcmp("OFF", arg)); // process directive if (DIR_HANGUP == key) { // turn SIGHUP on/off signal(SIGHUP, onoff ? signal_handler : SIG_IGN); } else if (DIR_ABORT == key) { // append the string to abort conditions #if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN size_t len = strlen(arg); if (len > max_abort_len) max_abort_len = len; #endif llist_add_to_end(&aborts, arg); #if ENABLE_FEATURE_CHAT_CLR_ABORT } else if (DIR_CLR_ABORT == key) { llist_t *l; // remove the string from abort conditions // N.B. gotta refresh maximum length too... # if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN max_abort_len = 0; # endif for (l = aborts; l; l = l->link) { # if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN size_t len = strlen(l->data); # endif if (strcmp(arg, l->data) == 0) { llist_unlink(&aborts, l); continue; } # if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN if (len > max_abort_len) max_abort_len = len; # endif } #endif } else if (DIR_TIMEOUT == key) { // set new timeout // -1 means OFF timeout = atoi(arg) * 1000; // 0 means default // >0 means value in msecs if (!timeout) timeout = DEFAULT_CHAT_TIMEOUT; } else if (DIR_ECHO == key) { // turn echo on/off // N.B. echo means dumping device input/output to stderr echo = onoff; } else if (DIR_RECORD == key) { // turn record on/off // N.B. record means dumping device input to a file // close previous record_fd if (record_fd > 0) close(record_fd); // N.B. do we have to die here on open error? record_fd = (onoff) ? xopen(arg, O_WRONLY|O_CREAT|O_TRUNC) : -1; } else if (DIR_SAY == key) { // just print argument verbatim // TODO: should we use full_write() to avoid unistd/stdio conflict? bb_error_msg("%s", arg); } // next, please! argv++; // ordinary expect-send pair! } else { //----------------------- // do expect //----------------------- int expect_len; size_t buf_len = 0; size_t max_len = max_abort_len; struct pollfd pfd; #if ENABLE_FEATURE_CHAT_NOFAIL int nofail = 0; #endif char *expect = *argv++; // sanity check: shall we really expect something? if (!expect) goto expect_done; #if ENABLE_FEATURE_CHAT_NOFAIL // if expect starts with - if ('-' == *expect) { // swallow - expect++; // and enter nofail mode nofail++; } #endif #ifdef ___TEST___BUF___ // test behaviour with a small buffer # undef COMMON_BUFSIZE # define COMMON_BUFSIZE 6 #endif // expand escape sequences in expect expect_len = unescape(expect, &expect_len /*dummy*/); if (expect_len > max_len) max_len = expect_len; // sanity check: // we should expect more than nothing but not more than input buffer // TODO: later we'll get rid of fixed-size buffer if (!expect_len) goto expect_done; if (max_len >= COMMON_BUFSIZE) { exitcode = ERR_MEM; goto expect_done; } // get reply pfd.fd = STDIN_FILENO; pfd.events = POLLIN; while (!exitcode && poll(&pfd, 1, timeout) > 0 && (pfd.revents & POLLIN) ) { llist_t *l; ssize_t delta; #define buf bb_common_bufsiz1 setup_common_bufsiz(); // read next char from device if (safe_read(STDIN_FILENO, buf+buf_len, 1) > 0) { // dump device input if RECORD fname if (record_fd > 0) { full_write(record_fd, buf+buf_len, 1); } // dump device input if ECHO ON if (echo) { // if (buf[buf_len] < ' ') { // full_write(STDERR_FILENO, "^", 1); // buf[buf_len] += '@'; // } full_write(STDERR_FILENO, buf+buf_len, 1); } buf_len++; // move input frame if we've reached higher bound if (buf_len > COMMON_BUFSIZE) { memmove(buf, buf+buf_len-max_len, max_len); buf_len = max_len; } } // N.B. rule of thumb: values being looked for can // be found only at the end of input buffer // this allows to get rid of strstr() and memmem() // TODO: make expect and abort strings processed uniformly // abort condition is met? -> bail out for (l = aborts, exitcode = ERR_ABORT; l; l = l->link, ++exitcode) { size_t len = strlen(l->data); delta = buf_len-len; if (delta >= 0 && !memcmp(buf+delta, l->data, len)) goto expect_done; } exitcode = ERR_OK; // expected reply received? -> goto next command delta = buf_len - expect_len; if (delta >= 0 && !memcmp(buf+delta, expect, expect_len)) goto expect_done; #undef buf } /* while (have data) */ // device timed out or unexpected reply received exitcode = ERR_TIMEOUT; expect_done: #if ENABLE_FEATURE_CHAT_NOFAIL // on success and when in nofail mode // we should skip following subsend-subexpect pairs if (nofail) { if (!exitcode) { // find last send before non-dashed expect while (*argv && argv[1] && '-' == argv[1][0]) argv += 2; // skip the pair // N.B. do we really need this?! if (!*argv++ || !*argv++) break; } // nofail mode also clears all but IO errors (or signals) if (ERR_IO != exitcode) exitcode = ERR_OK; } #endif // bail out unless we expected successfully if (exitcode) break; //----------------------- // do send //----------------------- if (*argv) { #if ENABLE_FEATURE_CHAT_IMPLICIT_CR int nocr = 0; // inhibit terminating command with \r #endif char *loaded = NULL; // loaded command size_t len; char *buf = *argv++; // if command starts with @ // load "real" command from file named after @ if ('@' == *buf) { // skip the @ and any following white-space trim(++buf); buf = loaded = xmalloc_xopen_read_close(buf, NULL); } // expand escape sequences in command len = unescape(buf, &nocr); // send command alarm(timeout); pfd.fd = STDOUT_FILENO; pfd.events = POLLOUT; while (len && !exitcode && poll(&pfd, 1, -1) > 0 && (pfd.revents & POLLOUT) ) { #if ENABLE_FEATURE_CHAT_SEND_ESCAPES // "\\d" means 1 sec delay, "\\p" means 0.01 sec delay // "\\K" means send BREAK char c = *buf; if ('\\' == c) { c = *++buf; if ('d' == c) { sleep(1); len--; continue; } if ('p' == c) { usleep(10000); len--; continue; } if ('K' == c) { tcsendbreak(STDOUT_FILENO, 0); len--; continue; } buf--; } if (safe_write(STDOUT_FILENO, buf, 1) != 1) break; len--; buf++; #else len -= full_write(STDOUT_FILENO, buf, len); #endif } /* while (can write) */ alarm(0); // report I/O error if there still exists at least one non-sent char if (len) exitcode = ERR_IO; // free loaded command (if any) if (loaded) free(loaded); #if ENABLE_FEATURE_CHAT_IMPLICIT_CR // or terminate command with \r (if not inhibited) else if (!nocr) xwrite(STDOUT_FILENO, "\r", 1); #endif // bail out unless we sent command successfully if (exitcode) break; } /* if (*argv) */ } } /* while (*argv) */ #if ENABLE_FEATURE_CHAT_TTY_HIFI tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0); #endif return exitcode; }