/* inlining + vfork -> bigger code */ static NOINLINE pid_t runsv(const char *name) { pid_t pid; /* If we got signaled, stop spawning children at once! */ if (bb_got_signal) return 0; pid = vfork(); if (pid == -1) { warn2_cannot("vfork", ""); return 0; } if (pid == 0) { /* child */ if (option_mask32 & 1) /* -P option? */ setsid(); /* man execv: * "Signals set to be caught by the calling process image * shall be set to the default action in the new process image." * Therefore, we do not need this: */ #if 0 bb_signals(0 | (1 << SIGHUP) | (1 << SIGTERM) , SIG_DFL); #endif execlp("runsv", "runsv", name, (char *) NULL); fatal2_cannot("start runsv ", name); } return pid; }
void FAST_FUNC launch_helper(const char **argv) { // setup vanilla unidirectional pipes interchange int i; int pipes[4]; xpipe(pipes); xpipe(pipes + 2); // NB: handler must be installed before vfork bb_signals(0 + (1 << SIGCHLD) + (1 << SIGALRM) , signal_handler); G.helper_pid = xvfork(); i = (!G.helper_pid) * 2; // for parent:0, for child:2 close(pipes[i + 1]); // 1 or 3 - closing one write end close(pipes[2 - i]); // 2 or 0 - closing one read end xmove_fd(pipes[i], STDIN_FILENO); // 0 or 2 - using other read end xmove_fd(pipes[3 - i], STDOUT_FILENO); // 3 or 1 - other write end if (!G.helper_pid) { // child: try to execute connection helper // NB: SIGCHLD & SIGALRM revert to SIG_DFL on exec BB_EXECVP_or_die((char**)argv); } // parent // check whether child is alive //redundant:signal_handler(SIGCHLD); // child seems OK -> parent goes on atexit(kill_helper); }
int less_main(int argc, char **argv) { int keypress; INIT_G(); /* TODO: -x: do not interpret backspace, -xx: tab also */ /* -xxx: newline also */ /* -w N: assume width N (-xxx -w 32: hex viewer of sorts) */ getopt32(argv, "EMmN~"); argc -= optind; argv += optind; num_files = argc; files = argv; /* Another popular pager, most, detects when stdout * is not a tty and turns into cat. This makes sense. */ if (!isatty(STDOUT_FILENO)) return bb_cat(argv); kbd_fd = open(CURRENT_TTY, O_RDONLY); if (kbd_fd < 0) return bb_cat(argv); ndelay_on(kbd_fd); if (!num_files) { if (isatty(STDIN_FILENO)) { /* Just "less"? No args and no redirection? */ bb_error_msg("missing filename"); bb_show_usage(); } } else filename = xstrdup(files[0]); get_terminal_width_height(kbd_fd, &width, &max_displayed_line); /* 20: two tabstops + 4 */ if (width < 20 || max_displayed_line < 3) return bb_cat(argv); max_displayed_line -= 2; buffer = xmalloc((max_displayed_line+1) * sizeof(char *)); if (option_mask32 & FLAG_TILDE) empty_line_marker = ""; tcgetattr(kbd_fd, &term_orig); term_less = term_orig; term_less.c_lflag &= ~(ICANON | ECHO); term_less.c_iflag &= ~(IXON | ICRNL); /*term_less.c_oflag &= ~ONLCR;*/ term_less.c_cc[VMIN] = 1; term_less.c_cc[VTIME] = 0; /* We want to restore term_orig on exit */ bb_signals(BB_FATAL_SIGS, sig_catcher); reinitialize(); while (1) { keypress = less_getch(-1); /* -1: do not position cursor */ keypress_process(keypress); } }
/* Call this before doing anything else. Sets up the socket pair * and installs the signal handler */ void FAST_FUNC udhcp_sp_setup(void) { /* was socketpair, but it needs AF_UNIX in kernel */ xpiped_pair(signal_pipe); close_on_exec_on(signal_pipe.rd); close_on_exec_on(signal_pipe.wr); ndelay_on(signal_pipe.wr); bb_signals(0 + (1 << SIGUSR1) + (1 << SIGUSR2) + (1 << SIGTERM) , signal_handler); }
static void startservice(struct svdir *s) { int p; char *run[2]; if (s->state == S_FINISH) run[0] = (char*)"./finish"; else { run[0] = (char*)"./run"; custom(s, 'u'); } run[1] = NULL; if (s->pid != 0) stopservice(s); /* should never happen */ while ((p = vfork()) == -1) { warn_cannot("vfork, sleeping"); sleep(5); } if (p == 0) { /* child */ if (haslog) { /* NB: bug alert! right order is close, then dup2 */ if (s->islog) { xchdir("./log"); close(logpipe.wr); xdup2(logpipe.rd, 0); } else { close(logpipe.rd); xdup2(logpipe.wr, 1); } } bb_signals(0 + (1 << SIGCHLD) + (1 << SIGTERM) , SIG_DFL); sig_unblock(SIGCHLD); sig_unblock(SIGTERM); execvp(*run, run); fatal2_cannot(s->islog ? "start log/" : "start ", *run); } /* parent */ if (s->state != S_FINISH) { gettimeofday_ns(&s->start); s->state = S_RUN; } s->pid = p; pidchanged = 1; s->ctrl = C_NOOP; update_status(s); }
int watchdog_main(int argc, char **argv) { unsigned opts; unsigned timer_duration = 30000; /* Userspace timer duration, in milliseconds */ char *t_arg; opt_complementary = "=1"; /* must have 1 argument */ opts = getopt32(argv, "Ft:", &t_arg); if (opts & OPT_TIMER) { static const struct suffix_mult suffixes[] = { { "ms", 1 }, { "", 1000 }, { } }; timer_duration = xatou_sfx(t_arg, suffixes); } if (!(opts & OPT_FOREGROUND)) { bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv); } bb_signals(BB_FATAL_SIGS, watchdog_shutdown); /* Use known fd # - avoid needing global 'int fd' */ xmove_fd(xopen(argv[argc - 1], O_WRONLY), 3); // TODO? // if (!(opts & OPT_TIMER)) { // if (ioctl(fd, WDIOC_GETTIMEOUT, &timer_duration) == 0) // timer_duration *= 500; // else // timer_duration = 30000; // } while (1) { /* * Make sure we clear the counter before sleeping, as the counter value * is undefined at this point -- PFM */ write(3, "", 1); /* write zero byte */ usleep(timer_duration * 1000L); } return EXIT_SUCCESS; /* - not reached, but gcc 4.2.1 is too dumb! */ }
void FAST_FUNC launch_helper(const char **argv) { // setup vanilla unidirectional pipes interchange int i; int pipes[4]; xpipe(pipes); xpipe(pipes + 2); // NB: handler must be installed before vfork bb_signals(0 + (1 << SIGCHLD) + (1 << SIGALRM) , signal_handler); G.helper_pid = xvfork(); i = (!G.helper_pid) * 2; // for parent:0, for child:2 close(pipes[i + 1]); // 1 or 3 - closing one write end close(pipes[2 - i]); // 2 or 0 - closing one read end xmove_fd(pipes[i], STDIN_FILENO); // 0 or 2 - using other read end xmove_fd(pipes[3 - i], STDOUT_FILENO); // 3 or 1 - using other write end // End result: // parent stdout [3] -> child stdin [2] // child stdout [1] -> parent stdin [0] if (!G.helper_pid) { // child // if parent dies, get SIGTERM prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0); // try to execute connection helper // NB: SIGCHLD & SIGALRM revert to SIG_DFL on exec BB_EXECVP_or_die((char**)argv); } // parent goes on }
int klogd_main(int argc UNUSED_PARAM, char **argv) { int i = 0; char *opt_c; int opt; int used = 0; 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); } openlog("kernel", 0, LOG_KERN); bb_signals(BB_FATAL_SIGS, klogd_signal); signal(SIGHUP, SIG_IGN); /* "Open the log. Currently a NOP" */ klogctl(1, NULL, 0); /* "printk() prints a message on the console only if it has a loglevel * less than console_loglevel". Here we set console_loglevel = i. */ if (i) klogctl(8, NULL, i); syslog(LOG_NOTICE, "klogd started: %s", bb_banner); while (1) { int n; int priority; char *start; /* "2 -- Read from the log." */ start = log_buffer + used; n = klogctl(2, start, KLOGD_LOGBUF_SIZE-1 - used); if (n < 0) { if (errno == EINTR) continue; syslog(LOG_ERR, "klogd: error %d in klogctl(2): %m", errno); break; } start[n] = '\0'; /* klogctl buffer parsing modelled after code in dmesg.c */ /* 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... */ if (start != log_buffer) { /* move it to the front of the buffer */ overlapping_strcpy(log_buffer, start); used = newline - start; /* don't log it yet */ break; } /* ...but if buffer is full, log it anyway */ used = 0; newline = NULL; } else { *newline++ = '\0'; } /* Extract the priority */ priority = LOG_INFO; if (*start == '<') { start++; if (*start) { /* kernel never generates multi-digit prios */ priority = (*start - '0'); start++; } if (*start == '>') start++; } /* Log (only non-empty lines) */ if (*start) syslog(priority, "%s", start); if (!newline) break; start = newline; } } return EXIT_FAILURE; }
int watchdog_main(int argc, char **argv) { static const struct suffix_mult suffixes[] = { { "ms", 1 }, { "", 1000 }, { "", 0 } }; unsigned opts; unsigned stimer_duration; /* how often to restart */ unsigned htimer_duration = 60000; /* reboots after N ms if not restarted */ char *st_arg; char *ht_arg; opt_complementary = "=1"; /* must have exactly 1 argument */ opts = getopt32(argv, "Ft:T:", &st_arg, &ht_arg); /* We need to daemonize *before* opening the watchdog as many drivers * will only allow one process at a time to do so. Since daemonizing * is not perfect (child may run before parent finishes exiting), we * can't rely on parent exiting before us (let alone *cleanly* releasing * the watchdog fd -- something else that may not even be allowed). */ if (!(opts & OPT_FOREGROUND)) bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv); if (opts & OPT_HTIMER) htimer_duration = xatou_sfx(ht_arg, suffixes); stimer_duration = htimer_duration / 2; if (opts & OPT_STIMER) stimer_duration = xatou_sfx(st_arg, suffixes); bb_signals(BB_FATAL_SIGS, watchdog_shutdown); /* Use known fd # - avoid needing global 'int fd' */ xmove_fd(xopen(argv[argc - 1], O_WRONLY), 3); /* WDIOC_SETTIMEOUT takes seconds, not milliseconds */ htimer_duration = htimer_duration / 1000; #ifndef WDIOC_SETTIMEOUT # error WDIOC_SETTIMEOUT is not defined, cannot compile watchdog applet #else # if defined WDIOC_SETOPTIONS && defined WDIOS_ENABLECARD { static const int enable = WDIOS_ENABLECARD; ioctl_or_warn(3, WDIOC_SETOPTIONS, (void*) &enable); } # endif ioctl_or_warn(3, WDIOC_SETTIMEOUT, &htimer_duration); #endif #if 0 ioctl_or_warn(3, WDIOC_GETTIMEOUT, &htimer_duration); printf("watchdog: SW timer is %dms, HW timer is %ds\n", stimer_duration, htimer_duration * 1000); #endif while (1) { /* * Make sure we clear the counter before sleeping, * as the counter value is undefined at this point -- PFM */ write(3, "", 1); /* write zero byte */ usleep(stimer_duration * 1000L); } return EXIT_SUCCESS; /* - not reached, but gcc 4.2.1 is too dumb! */ }
int ifplugd_main(int argc UNUSED_PARAM, char **argv) { int iface_status; int delay_time; const char *iface_status_str; struct pollfd netlink_pollfd[1]; unsigned opts; const char *api_mode_found; #if ENABLE_FEATURE_PIDFILE char *pidfile_name; pid_t pid_from_pidfile; #endif INIT_G(); opt_complementary = "t+:u+:d+"; opts = getopt32(argv, OPTION_STR, &G.iface, &G.script_name, &G.poll_time, &G.delay_up, &G.delay_down, &G.api_mode, &G.extra_arg); G.poll_time *= 1000; applet_name = xasprintf("ifplugd(%s)", G.iface); #if ENABLE_FEATURE_PIDFILE pidfile_name = xasprintf(CONFIG_PID_FILE_PATH "/ifplugd.%s.pid", G.iface); pid_from_pidfile = read_pid(pidfile_name); if (opts & FLAG_KILL) { if (pid_from_pidfile > 0) /* Upstream tool use SIGINT for -k */ kill(pid_from_pidfile, SIGINT); return EXIT_SUCCESS; } if (pid_from_pidfile > 0 && kill(pid_from_pidfile, 0) == 0) bb_error_msg_and_die("daemon already running"); #endif api_mode_found = strchr(api_modes, G.api_mode[0]); if (!api_mode_found) bb_error_msg_and_die("unknown API mode '%s'", G.api_mode); G.api_method_num = api_mode_found - api_modes; if (!(opts & FLAG_NO_DAEMON)) bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv); xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), ioctl_fd); if (opts & FLAG_MONITOR) { struct sockaddr_nl addr; int fd = xsocket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; addr.nl_groups = RTMGRP_LINK; addr.nl_pid = getpid(); xbind(fd, (struct sockaddr*)&addr, sizeof(addr)); xmove_fd(fd, netlink_fd); } write_pidfile(pidfile_name); /* this can't be moved before socket creation */ if (!(opts & FLAG_NO_SYSLOG)) { openlog(applet_name, 0, LOG_DAEMON); logmode |= LOGMODE_SYSLOG; } bb_signals(0 | (1 << SIGINT ) | (1 << SIGTERM) | (1 << SIGQUIT) | (1 << SIGHUP ) /* why we ignore it? */ /* | (1 << SIGCHLD) - run_script does not use it anymore */ , record_signo); bb_error_msg("started: %s", bb_banner); if (opts & FLAG_MONITOR) { struct ifreq ifrequest; set_ifreq_to_ifname(&ifrequest); G.iface_exists = (network_ioctl(SIOCGIFINDEX, &ifrequest, NULL) == 0); } if (G.iface_exists) maybe_up_new_iface(); iface_status = detect_link(); if (iface_status == IFSTATUS_ERR) goto exiting; iface_status_str = strstatus(iface_status); if (opts & FLAG_MONITOR) { bb_error_msg("interface %s", G.iface_exists ? "exists" : "doesn't exist, waiting"); } /* else we assume it always exists, but don't mislead user * by potentially lying that it really exists */ if (G.iface_exists) { bb_error_msg("link is %s", iface_status_str); } if ((!(opts & FLAG_NO_STARTUP) && iface_status == IFSTATUS_UP ) || (opts & FLAG_INITIAL_DOWN) ) { if (run_script(iface_status_str) != 0) goto exiting; } /* Main loop */ netlink_pollfd[0].fd = netlink_fd; netlink_pollfd[0].events = POLLIN; delay_time = 0; while (1) { int iface_status_old; int iface_exists_old; switch (bb_got_signal) { case SIGINT: case SIGTERM: bb_got_signal = 0; goto cleanup; case SIGQUIT: bb_got_signal = 0; goto exiting; default: bb_got_signal = 0; break; } if (poll(netlink_pollfd, (opts & FLAG_MONITOR) ? 1 : 0, G.poll_time ) < 0 ) { if (errno == EINTR) continue; bb_perror_msg("poll"); goto exiting; } iface_status_old = iface_status; iface_exists_old = G.iface_exists; if ((opts & FLAG_MONITOR) && (netlink_pollfd[0].revents & POLLIN) ) { G.iface_exists = check_existence_through_netlink(); if (G.iface_exists < 0) /* error */ goto exiting; if (iface_exists_old != G.iface_exists) { bb_error_msg("interface %sappeared", G.iface_exists ? "" : "dis"); if (G.iface_exists) maybe_up_new_iface(); } } /* note: if !G.iface_exists, returns DOWN */ iface_status = detect_link(); if (iface_status == IFSTATUS_ERR) { if (!(opts & FLAG_MONITOR)) goto exiting; iface_status = IFSTATUS_DOWN; } iface_status_str = strstatus(iface_status); if (iface_status_old != iface_status) { bb_error_msg("link is %s", iface_status_str); if (delay_time) { /* link restored its old status before * we run script. don't run the script: */ delay_time = 0; } else { delay_time = monotonic_sec(); if (iface_status == IFSTATUS_UP) delay_time += G.delay_up; if (iface_status == IFSTATUS_DOWN) delay_time += G.delay_down; if (delay_time == 0) delay_time++; } } if (delay_time && (int)(monotonic_sec() - delay_time) >= 0) { delay_time = 0; if (run_script(iface_status_str) != 0) goto exiting; } } /* while (1) */ cleanup: if (!(opts & FLAG_NO_SHUTDOWN) && (iface_status == IFSTATUS_UP || (iface_status == IFSTATUS_DOWN && delay_time) ) ) { setenv(IFPLUGD_ENV_PREVIOUS, strstatus(iface_status), 1); setenv(IFPLUGD_ENV_CURRENT, strstatus(-1), 1); run_script("down\0up"); /* reusing string */ } exiting: remove_pidfile(pidfile_name); bb_error_msg_and_die("exiting"); }
int runsvdir_main(int argc UNUSED_PARAM, char **argv) { struct stat s; dev_t last_dev = last_dev; /* for gcc */ ino_t last_ino = last_ino; /* for gcc */ time_t last_mtime = 0; int wstat; int curdir; pid_t pid; unsigned deadline; unsigned now; unsigned stampcheck; int i; int need_rescan = 1; char *opt_s_argv[3]; INIT_G(); opt_complementary = "-1"; opt_s_argv[0] = NULL; opt_s_argv[2] = NULL; getopt32(argv, "Ps:", &opt_s_argv[0]); argv += optind; bb_signals(0 | (1 << SIGTERM) | (1 << SIGHUP) /* For busybox's init, SIGTERM == reboot, * SIGUSR1 == halt * SIGUSR2 == poweroff * so we need to intercept SIGUSRn too. * Note that we do not implement actual reboot * (killall(TERM) + umount, etc), we just pause * respawing and avoid exiting (-> making kernel oops). * The user is responsible for the rest. */ | (getpid() == 1 ? ((1 << SIGUSR1) | (1 << SIGUSR2)) : 0) , record_signo); svdir = *argv++; #if ENABLE_FEATURE_RUNSVDIR_LOG /* setup log */ if (*argv) { rplog = *argv; rploglen = strlen(rplog); if (rploglen < 7) { warnx("log must have at least seven characters"); } else if (piped_pair(logpipe)) { warnx("can't create pipe for log"); } else { close_on_exec_on(logpipe.rd); close_on_exec_on(logpipe.wr); ndelay_on(logpipe.rd); ndelay_on(logpipe.wr); if (dup2(logpipe.wr, 2) == -1) { warnx("can't set filedescriptor for log"); } else { pfd[0].fd = logpipe.rd; pfd[0].events = POLLIN; stamplog = monotonic_sec(); goto run; } } rplog = NULL; warnx("log service disabled"); } run: #endif curdir = open(".", O_RDONLY|O_NDELAY); if (curdir == -1) fatal2_cannot("open current directory", ""); close_on_exec_on(curdir); stampcheck = monotonic_sec(); for (;;) { /* collect children */ for (;;) { pid = wait_any_nohang(&wstat); if (pid <= 0) break; for (i = 0; i < svnum; i++) { if (pid == sv[i].pid) { /* runsv has died */ sv[i].pid = 0; need_rescan = 1; } } } now = monotonic_sec(); if ((int)(now - stampcheck) >= 0) { /* wait at least a second */ stampcheck = now + 1; if (stat(svdir, &s) != -1) { if (need_rescan || s.st_mtime != last_mtime || s.st_ino != last_ino || s.st_dev != last_dev ) { /* svdir modified */ if (chdir(svdir) != -1) { last_mtime = s.st_mtime; last_dev = s.st_dev; last_ino = s.st_ino; /* if the svdir changed this very second, wait until the * next second, because we won't be able to detect more * changes within this second */ while (time(NULL) == last_mtime) usleep(100000); need_rescan = do_rescan(); while (fchdir(curdir) == -1) { warn2_cannot("change directory, pausing", ""); sleep(5); } } else { warn2_cannot("change directory to ", svdir); } } } else { warn2_cannot("stat ", svdir); } } #if ENABLE_FEATURE_RUNSVDIR_LOG if (rplog) { if ((int)(now - stamplog) >= 0) { write(logpipe.wr, ".", 1); stamplog = now + 900; } } pfd[0].revents = 0; #endif deadline = (need_rescan ? 1 : 5); sig_block(SIGCHLD); #if ENABLE_FEATURE_RUNSVDIR_LOG if (rplog) poll(pfd, 1, deadline*1000); else #endif sleep(deadline); sig_unblock(SIGCHLD); #if ENABLE_FEATURE_RUNSVDIR_LOG if (pfd[0].revents & POLLIN) { char ch; while (read(logpipe.rd, &ch, 1) > 0) { if (ch < ' ') ch = ' '; for (i = 6; i < rploglen; i++) rplog[i-1] = rplog[i]; rplog[rploglen-1] = ch; } } #endif if (!bb_got_signal) continue; /* -s SCRIPT: useful if we are init. * In this case typically script never returns, * it halts/powers off/reboots the system. */ if (opt_s_argv[0]) { /* Single parameter: signal# */ opt_s_argv[1] = utoa(bb_got_signal); pid = spawn(opt_s_argv); if (pid > 0) { /* Remembering to wait for _any_ children, * not just pid */ while (wait(NULL) != pid) continue; } } if (bb_got_signal == SIGHUP) { for (i = 0; i < svnum; i++) if (sv[i].pid) kill(sv[i].pid, SIGTERM); } /* SIGHUP or SIGTERM (or SIGUSRn if we are init) */ /* Exit unless we are init */ if (getpid() != 1) return (SIGHUP == bb_got_signal) ? 111 : EXIT_SUCCESS; /* init continues to monitor services forever */ bb_got_signal = 0; } /* for (;;) */ }
int inotifyd_main(int argc UNUSED_PARAM, char **argv) { int n; unsigned mask = IN_ALL_EVENTS; // assume we want all events struct pollfd pfd; char **watched = ++argv; // watched name list const char *args[] = { *argv, NULL, NULL, NULL, NULL }; // sanity check: agent and at least one watch must be given if (!argv[1]) bb_show_usage(); // open inotify pfd.fd = inotify_init(); if (pfd.fd < 0) bb_perror_msg_and_die("no kernel support"); // setup watched while (*++argv) { char *path = *argv; char *masks = strchr(path, ':'); // if mask is specified -> if (masks) { *masks = '\0'; // split path and mask // convert mask names to mask bitset mask = 0; while (*++masks) { int i = strchr(mask_names, *masks) - mask_names; if (i >= 0) { mask |= (1 << i); } } } // 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) { ssize_t 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 xioctl(pfd.fd, FIONREAD, &len); #define eventbuf bb_common_bufsiz1 ie = buf = (len <= sizeof(eventbuf)) ? eventbuf : xmalloc(len); len = full_read(pfd.fd, buf, len); // process events. N.B. events may vary in length while (len > 0) { int i; char events[sizeof(mask_names)]; char *s = events; unsigned m = ie->mask; for (i = 0; i < sizeof(mask_names)-1; ++i, m >>= 1) { if (m & 1) *s++ = mask_names[i]; } *s = '\0'; //bb_error_msg("exec %s %08X\t%s\t%s\t%s", agent, // ie->mask, events, watched[ie->wd], ie->len ? ie->name : ""); args[1] = events; args[2] = watched[ie->wd]; args[3] = ie->len ? ie->name : NULL; wait4pid(xspawn((char **)args)); // next event i = sizeof(struct inotify_event) + ie->len; len -= i; ie = (void*)((char*)ie + i); } if (eventbuf != buf) free(buf); } return EXIT_SUCCESS; }
int more_main(int argc UNUSED_PARAM, char **argv) { int c = c; /* for compiler */ int lines; int input = 0; int spaces = 0; int please_display_more_prompt; struct stat st; FILE *file; FILE *cin; int len; unsigned terminal_width; unsigned terminal_height; INIT_G(); argv++; /* Another popular pager, most, detects when stdout * is not a tty and turns into cat. This makes sense. */ if (!isatty(STDOUT_FILENO)) return bb_cat(argv); cin = fopen_for_read(CURRENT_TTY); if (!cin) return bb_cat(argv); if (ENABLE_FEATURE_USE_TERMIOS) { cin_fileno = fileno(cin); getTermSettings(cin_fileno, &initial_settings); new_settings = initial_settings; new_settings.c_lflag &= ~(ICANON | ECHO); new_settings.c_cc[VMIN] = 1; new_settings.c_cc[VTIME] = 0; setTermSettings(cin_fileno, &new_settings); bb_signals(0 + (1 << SIGINT) + (1 << SIGQUIT) + (1 << SIGTERM) , gotsig); } do { file = stdin; if (*argv) { file = fopen_or_warn(*argv, "r"); if (!file) continue; } st.st_size = 0; fstat(fileno(file), &st); please_display_more_prompt = 0; /* never returns w, h <= 1 */ get_terminal_width_height(fileno(cin), &terminal_width, &terminal_height); terminal_height -= 1; len = 0; lines = 0; while (spaces || (c = getc(file)) != EOF) { int wrap; if (spaces) spaces--; loop_top: if (input != 'r' && please_display_more_prompt) { len = printf("--More-- "); if (st.st_size != 0) { uoff_t d = (uoff_t)st.st_size / 100; if (d == 0) d = 1; len += printf("(%u%% of %"OFF_FMT"u bytes)", (int) ((uoff_t)ftello(file) / d), st.st_size); } fflush_all(); /* * We've just displayed the "--More--" prompt, so now we need * to get input from the user. */ for (;;) { input = getc(cin); input = tolower(input); if (!ENABLE_FEATURE_USE_TERMIOS) printf("\033[A"); /* cursor up */ /* Erase the last message */ printf("\r%*s\r", len, ""); /* Due to various multibyte escape * sequences, it's not ok to accept * any input as a command to scroll * the screen. We only allow known * commands, else we show help msg. */ if (input == ' ' || input == '\n' || input == 'q' || input == 'r') break; len = printf("(Enter:next line Space:next page Q:quit R:show the rest)"); } len = 0; lines = 0; please_display_more_prompt = 0; if (input == 'q') goto end; /* The user may have resized the terminal. * Re-read the dimensions. */ if (ENABLE_FEATURE_USE_TERMIOS) { get_terminal_width_height(cin_fileno, &terminal_width, &terminal_height); terminal_height -= 1; } } /* Crudely convert tabs into spaces, which are * a bajillion times easier to deal with. */ if (c == '\t') { spaces = ((unsigned)~len) % CONVERTED_TAB_SIZE; c = ' '; } /* * There are two input streams to worry about here: * * c : the character we are reading from the file being "mored" * input: a character received from the keyboard * * If we hit a newline in the _file_ stream, we want to test and * see if any characters have been hit in the _input_ stream. This * allows the user to quit while in the middle of a file. */ wrap = (++len > terminal_width); if (c == '\n' || wrap) { /* Then outputting this character * will move us to a new line. */ if (++lines >= terminal_height || input == '\n') please_display_more_prompt = 1; len = 0; } if (c != '\n' && wrap) { /* Then outputting this will also put a character on * the beginning of that new line. Thus we first want to * display the prompt (if any), so we skip the putchar() * and go back to the top of the loop, without reading * a new character. */ putchar('\n'); goto loop_top; } /* My small mind cannot fathom backspaces and UTF-8 */ putchar(c); die_if_ferror_stdout(); /* if tty was destroyed (closed xterm, etc) */ } fclose(file); fflush_all(); } while (*argv && *++argv); end: setTermSettings(cin_fileno, &initial_settings); return 0; }
int klogd_main(int argc ATTRIBUTE_UNUSED, char **argv) { int i = i; /* silence gcc */ char *start; /* do normal option parsing */ getopt32(argv, "c:n", &start); if (option_mask32 & OPT_LEVEL) { /* Valid levels are between 1 and 8 */ i = xatoul_range(start, 1, 8); } if (!(option_mask32 & OPT_FOREGROUND)) { bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv); } openlog("kernel", 0, LOG_KERN); /* Set up sig handlers */ bb_signals(0 + (1 << SIGINT) + (1 << SIGTERM) , klogd_signal); signal(SIGHUP, SIG_IGN); /* "Open the log. Currently a NOP." */ klogctl(1, NULL, 0); /* Set level of kernel console messaging. */ if (option_mask32 & OPT_LEVEL) klogctl(8, NULL, i); syslog(LOG_NOTICE, "klogd started: %s", bb_banner); /* Note: this code does not detect incomplete messages * (messages not ending with '\n' or just when kernel * generates too many messages for us to keep up) * and will split them in two separate lines */ while (1) { int n; int priority; n = klogctl(2, log_buffer, KLOGD_LOGBUF_SIZE - 1); if (n < 0) { if (errno == EINTR) continue; syslog(LOG_ERR, "klogd: error from klogctl(2): %d - %m", errno); break; } log_buffer[n] = '\n'; i = 0; while (i < n) { priority = LOG_INFO; start = &log_buffer[i]; if (log_buffer[i] == '<') { i++; // kernel never ganerates multi-digit prios //priority = 0; //while (log_buffer[i] >= '0' && log_buffer[i] <= '9') { // priority = priority * 10 + (log_buffer[i] - '0'); // i++; //} if (isdigit(log_buffer[i])) { priority = (log_buffer[i] - '0'); i++; } if (log_buffer[i] == '>') i++; start = &log_buffer[i]; } while (log_buffer[i] != '\n') i++; log_buffer[i] = '\0'; syslog(priority, "%s", start); i++; } } return EXIT_FAILURE; }
int slattach_main(int argc ATTRIBUTE_UNUSED, char **argv) { /* Line discipline code table */ static const char proto_names[] ALIGN1 = "slip\0" /* 0 */ "cslip\0" /* 1 */ "slip6\0" /* 2 */ "cslip6\0" /* 3 */ "adaptive\0" /* 8 */ ; int i, encap, opt; struct termios state; const char *proto = "cslip"; const char *extcmd; /* Command to execute after hangup */ const char *baud_str; int baud_code = -1; /* Line baud rate (system code) */ enum { OPT_p_proto = 1 << 0, OPT_s_baud = 1 << 1, OPT_c_extcmd = 1 << 2, OPT_e_quit = 1 << 3, OPT_h_watch = 1 << 4, OPT_m_nonraw = 1 << 5, OPT_L_local = 1 << 6, OPT_F_noflow = 1 << 7 }; INIT_G(); /* Parse command line options */ opt = getopt32(argv, "p:s:c:ehmLF", &proto, &baud_str, &extcmd); /*argc -= optind;*/ argv += optind; if (!*argv) bb_show_usage(); encap = index_in_strings(proto_names, proto); if (encap < 0) invarg(proto, "protocol"); if (encap > 3) encap = 8; /* We want to know if the baud rate is valid before we start touching the ttys */ if (opt & OPT_s_baud) { baud_code = tty_value_to_baud(xatoi(baud_str)); if (baud_code < 0) invarg(baud_str, "baud rate"); } /* Trap signals in order to restore tty states upon exit */ if (!(opt & OPT_e_quit)) { bb_signals(0 + (1 << SIGHUP) + (1 << SIGINT) + (1 << SIGQUIT) + (1 << SIGTERM) , sig_handler); } /* Open tty */ handle = open(*argv, O_RDWR | O_NDELAY); if (handle < 0) { char *buf = concat_path_file("/dev", *argv); handle = xopen(buf, O_RDWR | O_NDELAY); /* maybe if (ENABLE_FEATURE_CLEAN_UP) ?? */ free(buf); } /* Save current tty state */ save_state(); /* Configure tty */ memcpy(&state, &saved_state, sizeof(state)); if (!(opt & OPT_m_nonraw)) { /* raw not suppressed */ memset(&state.c_cc, 0, sizeof(state.c_cc)); state.c_cc[VMIN] = 1; state.c_iflag = IGNBRK | IGNPAR; state.c_oflag = 0; state.c_lflag = 0; state.c_cflag = CS8 | HUPCL | CREAD | ((opt & OPT_L_local) ? CLOCAL : 0) | ((opt & OPT_F_noflow) ? 0 : CRTSCTS); } if (opt & OPT_s_baud) { cfsetispeed(&state, baud_code); cfsetospeed(&state, baud_code); } set_state(&state, encap); /* Exit now if option -e was passed */ if (opt & OPT_e_quit) return 0; /* If we're not requested to watch, just keep descriptor open * until we are killed */ if (!(opt & OPT_h_watch)) while (1) sleep(24*60*60); /* Watch line for hangup */ while (1) { if (ioctl(handle, TIOCMGET, &i) < 0 || !(i & TIOCM_CAR)) goto no_carrier; sleep(15); } no_carrier: /* Execute command on hangup */ if (opt & OPT_c_extcmd) system(extcmd); /* Restore states and exit */ restore_state_and_exit(EXIT_SUCCESS); }
int tcpudpsvd_main(int argc ATTRIBUTE_UNUSED, char **argv) { char *str_C, *str_t; char *user; struct hcc *hccp; const char *instructs; char *msg_per_host = NULL; unsigned len_per_host = len_per_host; /* gcc */ #ifndef SSLSVD struct bb_uidgid_t ugid; #endif bool tcp; uint16_t local_port; char *preset_local_hostname = NULL; char *remote_hostname = remote_hostname; /* for compiler */ char *remote_addr = remote_addr; /* for compiler */ len_and_sockaddr *lsa; len_and_sockaddr local, remote; socklen_t sa_len; int pid; int sock; int conn; unsigned backlog = 20; INIT_G(); tcp = (applet_name[0] == 't'); /* 3+ args, -i at most once, -p implies -h, -v is counter, -b N, -c N */ opt_complementary = "-3:i--i:ph:vv:b+:c+"; #ifdef SSLSVD getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:", &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname, &backlog, &str_t, &ssluser, &root, &cert, &key, &verbose ); #else /* "+": stop on first non-option */ getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:v", &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname, &backlog, &str_t, &verbose ); #endif if (option_mask32 & OPT_C) { /* -C n[:message] */ max_per_host = bb_strtou(str_C, &str_C, 10); if (str_C[0]) { if (str_C[0] != ':') bb_show_usage(); msg_per_host = str_C + 1; len_per_host = strlen(msg_per_host); } } if (max_per_host > cmax) max_per_host = cmax; if (option_mask32 & OPT_u) { if (!get_uidgid(&ugid, user, 1)) bb_error_msg_and_die("unknown user/group: %s", user); } #ifdef SSLSVD if (option_mask32 & OPT_U) ssluser = optarg; if (option_mask32 & OPT_slash) root = optarg; if (option_mask32 & OPT_Z) cert = optarg; if (option_mask32 & OPT_K) key = optarg; #endif argv += optind; if (!argv[0][0] || LONE_CHAR(argv[0], '0')) argv[0] = (char*)"0.0.0.0"; /* Per-IP flood protection is not thought-out for UDP */ if (!tcp) max_per_host = 0; bb_sanitize_stdio(); /* fd# 0,1,2 must be opened */ #ifdef SSLSVD sslser = user; client = 0; if ((getuid() == 0) && !(option_mask32 & OPT_u)) { xfunc_exitcode = 100; bb_error_msg_and_die("-U ssluser must be set when running as root"); } if (option_mask32 & OPT_u) if (!uidgid_get(&sslugid, ssluser, 1)) { if (errno) { bb_perror_msg_and_die("fatal: cannot get user/group: %s", ssluser); } bb_error_msg_and_die("unknown user/group '%s'", ssluser); } if (!cert) cert = "./cert.pem"; if (!key) key = cert; if (matrixSslOpen() < 0) fatal("cannot initialize ssl"); if (matrixSslReadKeys(&keys, cert, key, 0, ca) < 0) { if (client) fatal("cannot read cert, key, or ca file"); fatal("cannot read cert or key file"); } if (matrixSslNewSession(&ssl, keys, 0, SSL_FLAGS_SERVER) < 0) fatal("cannot create ssl session"); #endif sig_block(SIGCHLD); signal(SIGCHLD, sig_child_handler); bb_signals(BB_FATAL_SIGS, sig_term_handler); signal(SIGPIPE, SIG_IGN); if (max_per_host) ipsvd_perhost_init(cmax); local_port = bb_lookup_port(argv[1], tcp ? "tcp" : "udp", 0); lsa = xhost2sockaddr(argv[0], local_port); argv += 2; sock = xsocket(lsa->u.sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0); setsockopt_reuseaddr(sock); sa_len = lsa->len; /* I presume sockaddr len stays the same */ xbind(sock, &lsa->u.sa, sa_len); if (tcp) xlisten(sock, backlog); else /* udp: needed for recv_from_to to work: */ socket_want_pktinfo(sock); /* ndelay_off(sock); - it is the default I think? */ #ifndef SSLSVD if (option_mask32 & OPT_u) { /* drop permissions */ xsetgid(ugid.gid); xsetuid(ugid.uid); } #endif if (verbose) { char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa); bb_error_msg("listening on %s, starting", addr); free(addr); #ifndef SSLSVD if (option_mask32 & OPT_u) printf(", uid %u, gid %u", (unsigned)ugid.uid, (unsigned)ugid.gid); #endif } /* Main accept() loop */ again: hccp = NULL; while (cnum >= cmax) wait_for_any_sig(); /* expecting SIGCHLD */ /* Accept a connection to fd #0 */ again1: close(0); again2: sig_unblock(SIGCHLD); local.len = remote.len = sa_len; if (tcp) { conn = accept(sock, &remote.u.sa, &remote.len); } else { /* In case recv_from_to won't be able to recover local addr. * Also sets port - recv_from_to is unable to do it. */ local = *lsa; conn = recv_from_to(sock, NULL, 0, MSG_PEEK, &remote.u.sa, &local.u.sa, sa_len); } sig_block(SIGCHLD); if (conn < 0) { if (errno != EINTR) bb_perror_msg(tcp ? "accept" : "recv"); goto again2; } xmove_fd(tcp ? conn : sock, 0); if (max_per_host) { /* Drop connection immediately if cur_per_host > max_per_host * (minimizing load under SYN flood) */ remote_addr = xmalloc_sockaddr2dotted_noport(&remote.u.sa); cur_per_host = ipsvd_perhost_add(remote_addr, max_per_host, &hccp); if (cur_per_host > max_per_host) { /* ipsvd_perhost_add detected that max is exceeded * (and did not store ip in connection table) */ free(remote_addr); if (msg_per_host) { /* don't block or test for errors */ send(0, msg_per_host, len_per_host, MSG_DONTWAIT); } goto again1; } /* NB: remote_addr is not leaked, it is stored in conn table */ } if (!tcp) { /* Voodoo magic: making udp sockets each receive its own * packets is not trivial, and I still not sure * I do it 100% right. * 1) we have to do it before fork() * 2) order is important - is it right now? */ /* Open new non-connected UDP socket for further clients... */ sock = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0); setsockopt_reuseaddr(sock); /* Make plain write/send work for old socket by supplying default * destination address. This also restricts incoming packets * to ones coming from this remote IP. */ xconnect(0, &remote.u.sa, sa_len); /* hole? at this point we have no wildcard udp socket... * can this cause clients to get "port unreachable" icmp? * Yup, time window is very small, but it exists (is it?) */ /* ..."open new socket", continued */ xbind(sock, &lsa->u.sa, sa_len); socket_want_pktinfo(sock); /* Doesn't work: * we cannot replace fd #0 - we will lose pending packet * which is already buffered for us! And we cannot use fd #1 * instead - it will "intercept" all following packets, but child * does not expect data coming *from fd #1*! */ #if 0 /* Make it so that local addr is fixed to localp->u.sa * and we don't accidentally accept packets to other local IPs. */ /* NB: we possibly bind to the _very_ same_ address & port as the one * already bound in parent! This seems to work in Linux. * (otherwise we can move socket to fd #0 only if bind succeeds) */ close(0); set_nport(localp, htons(local_port)); xmove_fd(xsocket(localp->u.sa.sa_family, SOCK_DGRAM, 0), 0); setsockopt_reuseaddr(0); /* crucial */ xbind(0, &localp->u.sa, localp->len); #endif } pid = vfork(); if (pid == -1) { bb_perror_msg("vfork"); goto again; } if (pid != 0) { /* Parent */ cnum++; if (verbose) connection_status(); if (hccp) hccp->pid = pid; /* clean up changes done by vforked child */ undo_xsetenv(); goto again; } /* Child: prepare env, log, and exec prog */ /* Closing tcp listening socket */ if (tcp) close(sock); { /* vfork alert! every xmalloc in this block should be freed! */ char *local_hostname = local_hostname; /* for compiler */ char *local_addr = NULL; char *free_me0 = NULL; char *free_me1 = NULL; char *free_me2 = NULL; if (verbose || !(option_mask32 & OPT_E)) { if (!max_per_host) /* remote_addr is not yet known */ free_me0 = remote_addr = xmalloc_sockaddr2dotted(&remote.u.sa); if (option_mask32 & OPT_h) { free_me1 = remote_hostname = xmalloc_sockaddr2host_noport(&remote.u.sa); if (!remote_hostname) { bb_error_msg("cannot look up hostname for %s", remote_addr); remote_hostname = remote_addr; } } /* Find out local IP peer connected to. * Errors ignored (I'm not paranoid enough to imagine kernel * which doesn't know local IP). */ if (tcp) getsockname(0, &local.u.sa, &local.len); /* else: for UDP it is done earlier by parent */ local_addr = xmalloc_sockaddr2dotted(&local.u.sa); if (option_mask32 & OPT_h) { local_hostname = preset_local_hostname; if (!local_hostname) { free_me2 = local_hostname = xmalloc_sockaddr2host_noport(&local.u.sa); if (!local_hostname) bb_error_msg_and_die("cannot look up hostname for %s", local_addr); } /* else: local_hostname is not NULL, but is NOT malloced! */ } } if (verbose) { pid = getpid(); if (max_per_host) { bb_error_msg("concurrency %s %u/%u", remote_addr, cur_per_host, max_per_host); } bb_error_msg((option_mask32 & OPT_h) ? "start %u %s-%s (%s-%s)" : "start %u %s-%s", pid, local_addr, remote_addr, local_hostname, remote_hostname); } if (!(option_mask32 & OPT_E)) { /* setup ucspi env */ const char *proto = tcp ? "TCP" : "UDP"; /* Extract "original" destination addr:port * from Linux firewall. Useful when you redirect * an outbond connection to local handler, and it needs * to know where it originally tried to connect */ if (tcp && getsockopt(0, SOL_IP, SO_ORIGINAL_DST, &local.u.sa, &local.len) == 0) { char *addr = xmalloc_sockaddr2dotted(&local.u.sa); xsetenv_plain("TCPORIGDSTADDR", addr); free(addr); } xsetenv_plain("PROTO", proto); xsetenv_proto(proto, "LOCALADDR", local_addr); xsetenv_proto(proto, "REMOTEADDR", remote_addr); if (option_mask32 & OPT_h) { xsetenv_proto(proto, "LOCALHOST", local_hostname); xsetenv_proto(proto, "REMOTEHOST", remote_hostname); } //compat? xsetenv_proto(proto, "REMOTEINFO", ""); /* additional */ if (cur_per_host > 0) /* can not be true for udp */ xsetenv_plain("TCPCONCURRENCY", utoa(cur_per_host)); } free(local_addr); free(free_me0); free(free_me1); free(free_me2); } xdup2(0, 1); signal(SIGTERM, SIG_DFL); signal(SIGPIPE, SIG_DFL); signal(SIGCHLD, SIG_DFL); sig_unblock(SIGCHLD); #ifdef SSLSVD strcpy(id, utoa(pid)); ssl_io(0, argv); #else BB_EXECVP(argv[0], argv); #endif bb_perror_msg_and_die("exec '%s'", argv[0]); }
int vlock_main(int argc UNUSED_PARAM, char **argv) { #ifdef __linux__ struct vt_mode vtm; struct vt_mode ovtm; #endif struct termios term; struct termios oterm; struct passwd *pw; pw = xgetpwuid(getuid()); opt_complementary = "=0"; /* no params! */ getopt32(argv, "a"); /* Ignore some signals so that we don't get killed by them */ bb_signals(0 + (1 << SIGTSTP) + (1 << SIGTTIN) + (1 << SIGTTOU) + (1 << SIGHUP ) + (1 << SIGCHLD) /* paranoia :) */ + (1 << SIGQUIT) + (1 << SIGINT ) , SIG_IGN); #ifdef __linux__ /* We will use SIGUSRx for console switch control: */ /* 1: set handlers */ signal_SA_RESTART_empty_mask(SIGUSR1, release_vt); signal_SA_RESTART_empty_mask(SIGUSR2, acquire_vt); /* 2: unmask them */ sig_unblock(SIGUSR1); sig_unblock(SIGUSR2); #endif /* Revert stdin/out to our controlling tty * (or die if we have none) */ xmove_fd(xopen(CURRENT_TTY, O_RDWR), STDIN_FILENO); xdup2(STDIN_FILENO, STDOUT_FILENO); #ifdef __linux__ xioctl(STDIN_FILENO, VT_GETMODE, &vtm); ovtm = vtm; /* "console switches are controlled by us, not kernel!" */ vtm.mode = VT_PROCESS; vtm.relsig = SIGUSR1; vtm.acqsig = SIGUSR2; ioctl(STDIN_FILENO, VT_SETMODE, &vtm); #endif //TODO: use set_termios_to_raw() tcgetattr(STDIN_FILENO, &oterm); term = oterm; term.c_iflag |= IGNBRK; /* ignore serial break (why? VTs don't have breaks, right?) */ term.c_iflag &= ~BRKINT; /* redundant? "dont translate break to SIGINT" */ term.c_lflag &= ~(ISIG | ECHO | ECHOCTL); /* ignore ^C ^Z, echo off */ tcsetattr_stdin_TCSANOW(&term); while (1) { printf("Virtual console%s locked by %s.\n", /* "s" if -a, else "": */ "s" + !option_mask32, pw->pw_name ); if (ask_and_check_password(pw) > 0) { break; } bb_do_delay(LOGIN_FAIL_DELAY); puts("Incorrect password"); } #ifdef __linux__ ioctl(STDIN_FILENO, VT_SETMODE, &ovtm); #endif tcsetattr_stdin_TCSANOW(&oterm); fflush_stdout_and_exit(EXIT_SUCCESS); }
int vlock_main(int argc ATTRIBUTE_UNUSED, char **argv) { struct vt_mode vtm; struct termios term; struct termios oterm; struct vt_mode ovtm; uid_t uid; struct passwd *pw; uid = getuid(); pw = getpwuid(uid); if (pw == NULL) bb_error_msg_and_die("unknown uid %d", (int)uid); opt_complementary = "=0"; /* no params! */ getopt32(argv, "a"); /* Ignore some signals so that we don't get killed by them */ bb_signals(0 + (1 << SIGTSTP) + (1 << SIGTTIN) + (1 << SIGTTOU) + (1 << SIGHUP ) + (1 << SIGCHLD) /* paranoia :) */ + (1 << SIGQUIT) + (1 << SIGINT ) , SIG_IGN); /* We will use SIGUSRx for console switch control: */ /* 1: set handlers */ signal_SA_RESTART_empty_mask(SIGUSR1, release_vt); signal_SA_RESTART_empty_mask(SIGUSR2, acquire_vt); /* 2: unmask them */ sig_unblock(SIGUSR1); sig_unblock(SIGUSR2); /* Revert stdin/out to our controlling tty * (or die if we have none) */ xmove_fd(xopen(CURRENT_TTY, O_RDWR), STDIN_FILENO); xdup2(STDIN_FILENO, STDOUT_FILENO); xioctl(STDIN_FILENO, VT_GETMODE, &vtm); ovtm = vtm; /* "console switches are controlled by us, not kernel!" */ vtm.mode = VT_PROCESS; vtm.relsig = SIGUSR1; vtm.acqsig = SIGUSR2; ioctl(STDIN_FILENO, VT_SETMODE, &vtm); tcgetattr(STDIN_FILENO, &oterm); term = oterm; term.c_iflag &= ~BRKINT; term.c_iflag |= IGNBRK; term.c_lflag &= ~ISIG; term.c_lflag &= ~(ECHO | ECHOCTL); tcsetattr(STDIN_FILENO, TCSANOW, &term); do { printf("Virtual console%s locked by %s.\n", option_mask32 /*o_lock_all*/ ? "s" : "", pw->pw_name); if (correct_password(pw)) { break; } bb_do_delay(FAIL_DELAY); puts("Password incorrect"); } while (1); ioctl(STDIN_FILENO, VT_SETMODE, &ovtm); tcsetattr(STDIN_FILENO, TCSANOW, &oterm); fflush_stdout_and_exit(EXIT_SUCCESS); }
int microcom_main(int argc UNUSED_PARAM, char **argv) { int sfd; int nfd; struct pollfd pfd[2]; struct termios tio0, tiosfd, tio; char *device_lock_file; enum { OPT_X = 1 << 0, // do not respect Ctrl-X, Ctrl-@ OPT_s = 1 << 1, // baudrate OPT_d = 1 << 2, // wait for device response, ms OPT_t = 1 << 3, // timeout, ms }; speed_t speed = 9600; int delay = -1; int timeout = -1; unsigned opts; // fetch options opt_complementary = "=1:s+:d+:t+"; // exactly one arg, numeric options opts = getopt32(argv, "Xs:d:t:", &speed, &delay, &timeout); // argc -= optind; argv += optind; // try to create lock file in /var/lock device_lock_file = (char *)bb_basename(argv[0]); device_lock_file = xasprintf("/var/lock/LCK..%s", device_lock_file); sfd = open(device_lock_file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0644); if (sfd < 0) { // device already locked -> bail out if (errno == EEXIST) bb_perror_msg_and_die("can't create %s", device_lock_file); // can't create lock -> don't care if (ENABLE_FEATURE_CLEAN_UP) free(device_lock_file); device_lock_file = NULL; } else { // %4d to make concurrent mgetty (if any) happy. // Mgetty treats 4-bytes lock files as binary, // not text, PID. Making 5+ char file. Brrr... fdprintf(sfd, "%4d\n", getpid()); close(sfd); } // setup signals bb_signals(0 + (1 << SIGHUP) + (1 << SIGINT) + (1 << SIGTERM) + (1 << SIGPIPE) , signal_handler); // error exit code if we fail to open the device signalled = 1; // open device sfd = open_or_warn(argv[0], O_RDWR | O_NOCTTY | O_NONBLOCK); if (sfd < 0) goto done; fcntl(sfd, F_SETFL, 0); // put device to "raw mode" xget1(sfd, &tio, &tiosfd); // set device speed cfsetspeed(&tio, tty_value_to_baud(speed)); if (xset1(sfd, &tio, argv[0])) goto done; // put stdin to "raw mode" (if stdin is a TTY), // handle one character at a time if (isatty(STDIN_FILENO)) { xget1(STDIN_FILENO, &tio, &tio0); if (xset1(STDIN_FILENO, &tio, "stdin")) goto done; } // main loop: check with poll(), then read/write bytes across pfd[0].fd = sfd; pfd[0].events = POLLIN; pfd[1].fd = STDIN_FILENO; pfd[1].events = POLLIN; signalled = 0; nfd = 2; while (!signalled && safe_poll(pfd, nfd, timeout) > 0) { if (nfd > 1 && pfd[1].revents) { char c; // read from stdin -> write to device if (safe_read(STDIN_FILENO, &c, 1) < 1) { // don't poll stdin anymore if we got EOF/error nfd--; goto skip_write; } // do we need special processing? if (!(opts & OPT_X)) { // ^@ sends Break if (VINTR == c) { tcsendbreak(sfd, 0); goto skip_write; } // ^X exits if (24 == c) break; } write(sfd, &c, 1); if (delay >= 0) safe_poll(pfd, 1, delay); skip_write: ; } if (pfd[0].revents) { #define iobuf bb_common_bufsiz1 ssize_t len; // read from device -> write to stdout len = safe_read(sfd, iobuf, sizeof(iobuf)); if (len > 0) full_write(STDOUT_FILENO, iobuf, len); else { // EOF/error -> bail out signalled = SIGHUP; break; } } } // restore device mode tcsetattr(sfd, TCSAFLUSH, &tiosfd); if (isatty(STDIN_FILENO)) tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0); done: if (device_lock_file) unlink(device_lock_file); return signalled; }
int passwd_main(int argc UNUSED_PARAM, char **argv) { enum { OPT_algo = (1 << 0), /* -a - password algorithm */ OPT_lock = (1 << 1), /* -l - lock account */ OPT_unlock = (1 << 2), /* -u - unlock account */ OPT_delete = (1 << 3), /* -d - delete password */ OPT_lud = OPT_lock | OPT_unlock | OPT_delete, }; unsigned opt; int rc; const char *opt_a = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO; const char *filename; char *myname; char *name; char *newp; struct passwd *pw; uid_t myuid; struct rlimit rlimit_fsize; char c; #if ENABLE_FEATURE_SHADOWPASSWDS /* Using _r function to avoid pulling in static buffers */ struct spwd spw; char buffer[256]; #endif logmode = LOGMODE_BOTH; openlog(applet_name, 0, LOG_AUTH); opt = getopt32(argv, "a:lud", &opt_a); //argc -= optind; argv += optind; myuid = getuid(); /* -l, -u, -d require root priv and username argument */ if ((opt & OPT_lud) && (myuid != 0 || !argv[0])) bb_show_usage(); /* Will complain and die if username not found */ myname = xstrdup(xuid2uname(myuid)); name = argv[0] ? argv[0] : myname; pw = xgetpwnam(name); if (myuid != 0 && pw->pw_uid != myuid) { /* LOGMODE_BOTH */ bb_error_msg_and_die("%s can't change password for %s", myname, name); } #if ENABLE_FEATURE_SHADOWPASSWDS { /* getspnam_r may return 0 yet set result to NULL. * At least glibc 2.4 does this. Be extra paranoid here. */ struct spwd *result = NULL; errno = 0; if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result) != 0 || !result /* no error, but no record found either */ || strcmp(result->sp_namp, pw->pw_name) != 0 /* paranoia */ ) { if (errno != ENOENT) { /* LOGMODE_BOTH */ bb_perror_msg("no record of %s in %s, using %s", name, bb_path_shadow_file, bb_path_passwd_file); } /* else: /etc/shadow does not exist, * apparently we are on a shadow-less system, * no surprise there */ } else { pw->pw_passwd = result->sp_pwdp; } } #endif /* Decide what the new password will be */ newp = NULL; c = pw->pw_passwd[0] - '!'; if (!(opt & OPT_lud)) { if (myuid != 0 && !c) { /* passwd starts with '!' */ /* LOGMODE_BOTH */ bb_error_msg_and_die("can't change " "locked password for %s", name); } printf("Changing password for %s\n", name); newp = new_password(pw, myuid, opt_a); if (!newp) { logmode = LOGMODE_STDIO; bb_error_msg_and_die("password for %s is unchanged", name); } } else if (opt & OPT_lock) { if (!c) goto skip; /* passwd starts with '!' */ newp = xasprintf("!%s", pw->pw_passwd); } else if (opt & OPT_unlock) { if (c) goto skip; /* not '!' */ /* pw->pw_passwd points to static storage, * strdup'ing to avoid nasty surprizes */ newp = xstrdup(&pw->pw_passwd[1]); } else if (opt & OPT_delete) { newp = (char*)""; } rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000; setrlimit(RLIMIT_FSIZE, &rlimit_fsize); bb_signals(0 + (1 << SIGHUP) + (1 << SIGINT) + (1 << SIGQUIT) , SIG_IGN); umask(077); xsetuid(0); #if ENABLE_FEATURE_SHADOWPASSWDS filename = bb_path_shadow_file; rc = update_passwd(bb_path_shadow_file, name, newp, NULL); if (rc > 0) /* password in /etc/shadow was updated */ newp = (char*) "x"; if (rc >= 0) /* 0 = /etc/shadow missing (not an error), >0 = passwd changed in /etc/shadow */ #endif { filename = bb_path_passwd_file; rc = update_passwd(bb_path_passwd_file, name, newp, NULL); } /* LOGMODE_BOTH */ if (rc < 0) bb_error_msg_and_die("can't update password file %s", filename); bb_error_msg("password for %s changed by %s", name, myname); /*if (ENABLE_FEATURE_CLEAN_UP) free(newp); - can't, it may be non-malloced */ skip: if (!newp) { bb_error_msg_and_die("password for %s is already %slocked", name, (opt & OPT_unlock) ? "un" : ""); } if (ENABLE_FEATURE_CLEAN_UP) free(myname); return 0; }
int zcip_main(int argc UNUSED_PARAM, char **argv) { int state; char *r_opt; unsigned opts; // ugly trick, but I want these zeroed in one go struct { const struct in_addr null_ip; const struct ether_addr null_addr; struct in_addr ip; struct ifreq ifr; int timeout_ms; /* must be signed */ unsigned conflicts; unsigned nprobes; unsigned nclaims; int ready; } L; #define null_ip (L.null_ip ) #define null_addr (L.null_addr ) #define ip (L.ip ) #define ifr (L.ifr ) #define timeout_ms (L.timeout_ms) #define conflicts (L.conflicts ) #define nprobes (L.nprobes ) #define nclaims (L.nclaims ) #define ready (L.ready ) memset(&L, 0, sizeof(L)); INIT_G(); #define FOREGROUND (opts & 1) #define QUIT (opts & 2) // parse commandline: prog [options] ifname script // exactly 2 args; -v accumulates and implies -f opt_complementary = "=2:vv:vf"; opts = getopt32(argv, "fqr:p:v", &r_opt, &pidfile, &verbose); #if !BB_MMU // on NOMMU reexec early (or else we will rerun things twice) if (!FOREGROUND) bb_daemonize_or_rexec(0 /*was: DAEMON_CHDIR_ROOT*/, argv); #endif // open an ARP socket // (need to do it before openlog to prevent openlog from taking // fd 3 (sock_fd==3)) xmove_fd(xsocket(AF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)), sock_fd); if (!FOREGROUND) { // do it before all bb_xx_msg calls openlog(applet_name, 0, LOG_DAEMON); logmode |= LOGMODE_SYSLOG; } if (opts & 4) { // -r n.n.n.n if (inet_aton(r_opt, &ip) == 0 || (ntohl(ip.s_addr) & IN_CLASSB_NET) != LINKLOCAL_ADDR ) { bb_error_msg_and_die("invalid link address"); } } argv += optind - 1; /* Now: argv[0]:junk argv[1]:intf argv[2]:script argv[3]:NULL */ /* We need to make space for script argument: */ argv[0] = argv[1]; argv[1] = argv[2]; /* Now: argv[0]:intf argv[1]:script argv[2]:junk argv[3]:NULL */ #define argv_intf (argv[0]) xsetenv("interface", argv_intf); // initialize the interface (modprobe, ifup, etc) if (run(argv, "init", NULL)) return EXIT_FAILURE; // initialize saddr // saddr is: { u16 sa_family; u8 sa_data[14]; } //memset(&saddr, 0, sizeof(saddr)); //TODO: are we leaving sa_family == 0 (AF_UNSPEC)?! safe_strncpy(saddr.sa_data, argv_intf, sizeof(saddr.sa_data)); // bind to the interface's ARP socket xbind(sock_fd, &saddr, sizeof(saddr)); // get the interface's ethernet address //memset(&ifr, 0, sizeof(ifr)); strncpy_IFNAMSIZ(ifr.ifr_name, argv_intf); xioctl(sock_fd, SIOCGIFHWADDR, &ifr); memcpy(ð_addr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN); // start with some stable ip address, either a function of // the hardware address or else the last address we used. // we are taking low-order four bytes, as top-order ones // aren't random enough. // NOTE: the sequence of addresses we try changes only // depending on when we detect conflicts. { uint32_t t; move_from_unaligned32(t, ((char *)ð_addr + 2)); srand(t); } if (ip.s_addr == 0) ip.s_addr = pick(); // FIXME cases to handle: // - zcip already running! // - link already has local address... just defend/update // daemonize now; don't delay system startup if (!FOREGROUND) { #if BB_MMU bb_daemonize(0 /*was: DAEMON_CHDIR_ROOT*/); #endif if (verbose) bb_info_msg("start, interface %s", argv_intf); } write_pidfile(pidfile); bb_signals(BB_FATAL_SIGS, cleanup); // run the dynamic address negotiation protocol, // restarting after address conflicts: // - start with some address we want to try // - short random delay // - arp probes to see if another host uses it // - arp announcements that we're claiming it // - use it // - defend it, within limits // exit if: // - address is successfully obtained and -q was given: // run "<script> config", then exit with exitcode 0 // - poll error (when does this happen?) // - read error (when does this happen?) // - sendto error (in arp()) (when does this happen?) // - revents & POLLERR (link down). run "<script> deconfig" first state = PROBE; while (1) { struct pollfd fds[1]; unsigned deadline_us; struct arp_packet p; int source_ip_conflict; int target_ip_conflict; fds[0].fd = sock_fd; fds[0].events = POLLIN; fds[0].revents = 0; // poll, being ready to adjust current timeout if (!timeout_ms) { timeout_ms = random_delay_ms(PROBE_WAIT); // FIXME setsockopt(sock_fd, SO_ATTACH_FILTER, ...) to // make the kernel filter out all packets except // ones we'd care about. } // set deadline_us to the point in time when we timeout deadline_us = MONOTONIC_US() + timeout_ms * 1000; VDBG("...wait %d %s nprobes=%u, nclaims=%u\n", timeout_ms, argv_intf, nprobes, nclaims); switch (safe_poll(fds, 1, timeout_ms)) { default: //bb_perror_msg("poll"); - done in safe_poll cleanup(EXIT_FAILURE); // timeout case 0: VDBG("state = %d\n", state); switch (state) { case PROBE: // timeouts in the PROBE state mean no conflicting ARP packets // have been received, so we can progress through the states if (nprobes < PROBE_NUM) { nprobes++; VDBG("probe/%u %s@%s\n", nprobes, argv_intf, inet_ntoa(ip)); arp(/* ARPOP_REQUEST, */ /* ð_addr, */ null_ip, &null_addr, ip); timeout_ms = PROBE_MIN * 1000; timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN); } else { // Switch to announce state. state = ANNOUNCE; nclaims = 0; VDBG("announce/%u %s@%s\n", nclaims, argv_intf, inet_ntoa(ip)); arp(/* ARPOP_REQUEST, */ /* ð_addr, */ ip, ð_addr, ip); timeout_ms = ANNOUNCE_INTERVAL * 1000; } break; case RATE_LIMIT_PROBE: // timeouts in the RATE_LIMIT_PROBE state mean no conflicting ARP packets // have been received, so we can move immediately to the announce state state = ANNOUNCE; nclaims = 0; VDBG("announce/%u %s@%s\n", nclaims, argv_intf, inet_ntoa(ip)); arp(/* ARPOP_REQUEST, */ /* ð_addr, */ ip, ð_addr, ip); timeout_ms = ANNOUNCE_INTERVAL * 1000; break; case ANNOUNCE: // timeouts in the ANNOUNCE state mean no conflicting ARP packets // have been received, so we can progress through the states if (nclaims < ANNOUNCE_NUM) { nclaims++; VDBG("announce/%u %s@%s\n", nclaims, argv_intf, inet_ntoa(ip)); arp(/* ARPOP_REQUEST, */ /* ð_addr, */ ip, ð_addr, ip); timeout_ms = ANNOUNCE_INTERVAL * 1000; } else { // Switch to monitor state. state = MONITOR; // link is ok to use earlier // FIXME update filters run(argv, "config", &ip); ready = 1; conflicts = 0; timeout_ms = -1; // Never timeout in the monitor state. // NOTE: all other exit paths // should deconfig ... if (QUIT) cleanup(EXIT_SUCCESS); } break; case DEFEND: // We won! No ARP replies, so just go back to monitor. state = MONITOR; timeout_ms = -1; conflicts = 0; break; default: // Invalid, should never happen. Restart the whole protocol. state = PROBE; ip.s_addr = pick(); timeout_ms = 0; nprobes = 0; nclaims = 0; break; } // switch (state) break; // case 0 (timeout) // packets arriving, or link went down case 1: // We need to adjust the timeout in case we didn't receive // a conflicting packet. if (timeout_ms > 0) { unsigned diff = deadline_us - MONOTONIC_US(); if ((int)(diff) < 0) { // Current time is greater than the expected timeout time. // Should never happen. VDBG("missed an expected timeout\n"); timeout_ms = 0; } else { VDBG("adjusting timeout\n"); timeout_ms = (diff / 1000) | 1; /* never 0 */ } } if ((fds[0].revents & POLLIN) == 0) { if (fds[0].revents & POLLERR) { // FIXME: links routinely go down; // this shouldn't necessarily exit. bb_error_msg("iface %s is down", argv_intf); if (ready) { run(argv, "deconfig", &ip); } cleanup(EXIT_FAILURE); } continue; } // read ARP packet if (safe_read(sock_fd, &p, sizeof(p)) < 0) { bb_perror_msg(bb_msg_read_error); cleanup(EXIT_FAILURE); } if (p.eth.ether_type != htons(ETHERTYPE_ARP)) continue; #ifdef DEBUG { struct ether_addr *sha = (struct ether_addr *) p.arp.arp_sha; struct ether_addr *tha = (struct ether_addr *) p.arp.arp_tha; struct in_addr *spa = (struct in_addr *) p.arp.arp_spa; struct in_addr *tpa = (struct in_addr *) p.arp.arp_tpa; VDBG("%s recv arp type=%d, op=%d,\n", argv_intf, ntohs(p.eth.ether_type), ntohs(p.arp.arp_op)); VDBG("\tsource=%s %s\n", ether_ntoa(sha), inet_ntoa(*spa)); VDBG("\ttarget=%s %s\n", ether_ntoa(tha), inet_ntoa(*tpa)); } #endif if (p.arp.arp_op != htons(ARPOP_REQUEST) && p.arp.arp_op != htons(ARPOP_REPLY)) continue; source_ip_conflict = 0; target_ip_conflict = 0; if (memcmp(p.arp.arp_spa, &ip.s_addr, sizeof(struct in_addr)) == 0 && memcmp(&p.arp.arp_sha, ð_addr, ETH_ALEN) != 0 ) { source_ip_conflict = 1; } if (p.arp.arp_op == htons(ARPOP_REQUEST) && memcmp(p.arp.arp_tpa, &ip.s_addr, sizeof(struct in_addr)) == 0 && memcmp(&p.arp.arp_tha, ð_addr, ETH_ALEN) != 0 ) { target_ip_conflict = 1; } VDBG("state = %d, source ip conflict = %d, target ip conflict = %d\n", state, source_ip_conflict, target_ip_conflict); switch (state) { case PROBE: case ANNOUNCE: // When probing or announcing, check for source IP conflicts // and other hosts doing ARP probes (target IP conflicts). if (source_ip_conflict || target_ip_conflict) { conflicts++; if (conflicts >= MAX_CONFLICTS) { VDBG("%s ratelimit\n", argv_intf); timeout_ms = RATE_LIMIT_INTERVAL * 1000; state = RATE_LIMIT_PROBE; } // restart the whole protocol ip.s_addr = pick(); timeout_ms = 0; nprobes = 0; nclaims = 0; } break; case MONITOR: // If a conflict, we try to defend with a single ARP probe. if (source_ip_conflict) { VDBG("monitor conflict -- defending\n"); state = DEFEND; timeout_ms = DEFEND_INTERVAL * 1000; arp(/* ARPOP_REQUEST, */ /* ð_addr, */ ip, ð_addr, ip); } break; case DEFEND: // Well, we tried. Start over (on conflict). if (source_ip_conflict) { state = PROBE; VDBG("defend conflict -- starting over\n"); ready = 0; run(argv, "deconfig", &ip); // restart the whole protocol ip.s_addr = pick(); timeout_ms = 0; nprobes = 0; nclaims = 0; } break; default: // Invalid, should never happen. Restart the whole protocol. VDBG("invalid state -- starting over\n"); state = PROBE; ip.s_addr = pick(); timeout_ms = 0; nprobes = 0; nclaims = 0; break; } // switch state break; // case 1 (packets arriving) } // switch poll } // while (1) #undef argv_intf }
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 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) { // cache directive value char *arg = *++argv; // OFF -> 0, anything else -> 1 bool 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) { // 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 (llist_t *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)) { 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) ) { #define buf bb_common_bufsiz1 llist_t *l; ssize_t delta; // 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 > 0) { // 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; }
int less_main(int argc, char **argv) { int keypress; INIT_G(); /* TODO: -x: do not interpret backspace, -xx: tab also */ /* -xxx: newline also */ /* -w N: assume width N (-xxx -w 32: hex viewer of sorts) */ getopt32(argv, "EMmN~I" IF_FEATURE_LESS_DASHCMD("S")); argc -= optind; argv += optind; num_files = argc; files = argv; /* Another popular pager, most, detects when stdout * is not a tty and turns into cat. This makes sense. */ if (!isatty(STDOUT_FILENO)) return bb_cat(argv); if (!num_files) { if (isatty(STDIN_FILENO)) { /* Just "less"? No args and no redirection? */ bb_error_msg("missing filename"); bb_show_usage(); } } else { filename = xstrdup(files[0]); } if (option_mask32 & FLAG_TILDE) empty_line_marker = ""; kbd_fd = open(CURRENT_TTY, O_RDONLY); if (kbd_fd < 0) return bb_cat(argv); ndelay_on(kbd_fd); tcgetattr(kbd_fd, &term_orig); term_less = term_orig; term_less.c_lflag &= ~(ICANON | ECHO); term_less.c_iflag &= ~(IXON | ICRNL); /*term_less.c_oflag &= ~ONLCR;*/ term_less.c_cc[VMIN] = 1; term_less.c_cc[VTIME] = 0; get_terminal_width_height(kbd_fd, &width, &max_displayed_line); /* 20: two tabstops + 4 */ if (width < 20 || max_displayed_line < 3) return bb_cat(argv); max_displayed_line -= 2; /* We want to restore term_orig on exit */ bb_signals(BB_FATAL_SIGS, sig_catcher); #if ENABLE_FEATURE_LESS_WINCH signal(SIGWINCH, sigwinch_handler); #endif buffer = xmalloc((max_displayed_line+1) * sizeof(char *)); reinitialize(); while (1) { #if ENABLE_FEATURE_LESS_WINCH while (WINCH_COUNTER) { again: winch_counter--; get_terminal_width_height(kbd_fd, &width, &max_displayed_line); /* 20: two tabstops + 4 */ if (width < 20) width = 20; if (max_displayed_line < 3) max_displayed_line = 3; max_displayed_line -= 2; free(buffer); buffer = xmalloc((max_displayed_line+1) * sizeof(char *)); /* Avoid re-wrap and/or redraw if we already know * we need to do it again. These ops are expensive */ if (WINCH_COUNTER) goto again; re_wrap(); if (WINCH_COUNTER) goto again; buffer_fill_and_print(); /* This took some time. Loop back and check, * were there another SIGWINCH? */ } #endif keypress = less_getch(-1); /* -1: do not position cursor */ keypress_process(keypress); } }
int zcip_main(int argc UNUSED_PARAM, char **argv) { char *r_opt; const char *l_opt = "169.254.0.0"; int state; int nsent; unsigned opts; // Ugly trick, but I want these zeroed in one go struct { const struct ether_addr null_ethaddr; struct ifreq ifr; uint32_t chosen_nip; int conflicts; int timeout_ms; // must be signed } L; #define null_ethaddr (L.null_ethaddr) #define ifr (L.ifr ) #define chosen_nip (L.chosen_nip ) #define conflicts (L.conflicts ) #define timeout_ms (L.timeout_ms ) memset(&L, 0, sizeof(L)); INIT_G(); #define FOREGROUND (opts & 1) #define QUIT (opts & 2) // Parse commandline: prog [options] ifname script // exactly 2 args; -v accumulates and implies -f opt_complementary = "=2:vv:vf"; opts = getopt32(argv, "fqr:l:p:v", &r_opt, &l_opt, &G.pidfile, &G.verbose); #if !BB_MMU // on NOMMU reexec early (or else we will rerun things twice) if (!FOREGROUND) bb_daemonize_or_rexec(0 /*was: DAEMON_CHDIR_ROOT*/, argv); #endif // Open an ARP socket // (need to do it before openlog to prevent openlog from taking // fd 3 (sock_fd==3)) xmove_fd(xsocket(AF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)), sock_fd); if (!FOREGROUND) { // do it before all bb_xx_msg calls openlog(applet_name, 0, LOG_DAEMON); logmode |= LOGMODE_SYSLOG; } bb_logenv_override(); { // -l n.n.n.n struct in_addr net; if (inet_aton(l_opt, &net) == 0 || (net.s_addr & htonl(IN_CLASSB_NET)) != net.s_addr ) { bb_error_msg_and_die("invalid network address"); } G.localnet_ip = ntohl(net.s_addr); } if (opts & 4) { // -r n.n.n.n struct in_addr ip; if (inet_aton(r_opt, &ip) == 0 || (ntohl(ip.s_addr) & IN_CLASSB_NET) != G.localnet_ip ) { bb_error_msg_and_die("invalid link address"); } chosen_nip = ip.s_addr; } argv += optind - 1; /* Now: argv[0]:junk argv[1]:intf argv[2]:script argv[3]:NULL */ /* We need to make space for script argument: */ argv[0] = argv[1]; argv[1] = argv[2]; /* Now: argv[0]:intf argv[1]:script argv[2]:junk argv[3]:NULL */ #define argv_intf (argv[0]) xsetenv("interface", argv_intf); // Initialize the interface (modprobe, ifup, etc) if (run(argv, "init", 0)) return EXIT_FAILURE; // Initialize G.iface_sockaddr // G.iface_sockaddr is: { u16 sa_family; u8 sa_data[14]; } //memset(&G.iface_sockaddr, 0, sizeof(G.iface_sockaddr)); //TODO: are we leaving sa_family == 0 (AF_UNSPEC)?! safe_strncpy(G.iface_sockaddr.sa_data, argv_intf, sizeof(G.iface_sockaddr.sa_data)); // Bind to the interface's ARP socket xbind(sock_fd, &G.iface_sockaddr, sizeof(G.iface_sockaddr)); // Get the interface's ethernet address //memset(&ifr, 0, sizeof(ifr)); strncpy_IFNAMSIZ(ifr.ifr_name, argv_intf); xioctl(sock_fd, SIOCGIFHWADDR, &ifr); memcpy(&G.our_ethaddr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN); // Start with some stable ip address, either a function of // the hardware address or else the last address we used. // we are taking low-order four bytes, as top-order ones // aren't random enough. // NOTE: the sequence of addresses we try changes only // depending on when we detect conflicts. { uint32_t t; move_from_unaligned32(t, ((char *)&G.our_ethaddr + 2)); srand(t); } // FIXME cases to handle: // - zcip already running! // - link already has local address... just defend/update // Daemonize now; don't delay system startup if (!FOREGROUND) { #if BB_MMU bb_daemonize(0 /*was: DAEMON_CHDIR_ROOT*/); #endif if (G.verbose) bb_info_msg("start, interface %s", argv_intf); } write_pidfile(G.pidfile); die_func = cleanup; bb_signals(BB_FATAL_SIGS, term_handler); // Run the dynamic address negotiation protocol, // restarting after address conflicts: // - start with some address we want to try // - short random delay // - arp probes to see if another host uses it // 00:04:e2:64:23:c2 > ff:ff:ff:ff:ff:ff arp who-has 169.254.194.171 tell 0.0.0.0 // - arp announcements that we're claiming it // 00:04:e2:64:23:c2 > ff:ff:ff:ff:ff:ff arp who-has 169.254.194.171 (00:04:e2:64:23:c2) tell 169.254.194.171 // - use it // - defend it, within limits // exit if: // - address is successfully obtained and -q was given: // run "<script> config", then exit with exitcode 0 // - poll error (when does this happen?) // - read error (when does this happen?) // - sendto error (in send_arp_request()) (when does this happen?) // - revents & POLLERR (link down). run "<script> deconfig" first if (chosen_nip == 0) { new_nip_and_PROBE: chosen_nip = pick_nip(); } nsent = 0; state = PROBE; while (1) { struct pollfd fds[1]; unsigned deadline_us; struct arp_packet p; int ip_conflict; int n; fds[0].fd = sock_fd; fds[0].events = POLLIN; fds[0].revents = 0; // Poll, being ready to adjust current timeout if (!timeout_ms) { timeout_ms = random_delay_ms(PROBE_WAIT); // FIXME setsockopt(sock_fd, SO_ATTACH_FILTER, ...) to // make the kernel filter out all packets except // ones we'd care about. } // Set deadline_us to the point in time when we timeout deadline_us = MONOTONIC_US() + timeout_ms * 1000; VDBG("...wait %d %s nsent=%u\n", timeout_ms, argv_intf, nsent); n = safe_poll(fds, 1, timeout_ms); if (n < 0) { //bb_perror_msg("poll"); - done in safe_poll xfunc_die(); } if (n == 0) { // timed out? VDBG("state:%d\n", state); switch (state) { case PROBE: // No conflicting ARP packets were seen: // we can progress through the states if (nsent < PROBE_NUM) { nsent++; VDBG("probe/%u %s@%s\n", nsent, argv_intf, nip_to_a(chosen_nip)); timeout_ms = PROBE_MIN * 1000; timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN); send_arp_request(0, &null_ethaddr, chosen_nip); continue; } // Switch to announce state nsent = 0; state = ANNOUNCE; goto send_announce; case ANNOUNCE: // No conflicting ARP packets were seen: // we can progress through the states if (nsent < ANNOUNCE_NUM) { send_announce: nsent++; VDBG("announce/%u %s@%s\n", nsent, argv_intf, nip_to_a(chosen_nip)); timeout_ms = ANNOUNCE_INTERVAL * 1000; send_arp_request(chosen_nip, &G.our_ethaddr, chosen_nip); continue; } // Switch to monitor state // FIXME update filters run(argv, "config", chosen_nip); // NOTE: all other exit paths should deconfig... if (QUIT) { xfunc_error_retval = EXIT_SUCCESS; xfunc_die(); } // fall through: switch to MONITOR default: // case DEFEND: // case MONITOR: (shouldn't happen, MONITOR timeout is infinite) // Defend period ended with no ARP replies - we won timeout_ms = -1; // never timeout in monitor state state = MONITOR; continue; } } // Packet arrived, or link went down. // We need to adjust the timeout in case we didn't receive // a conflicting packet. if (timeout_ms > 0) { unsigned diff = deadline_us - MONOTONIC_US(); if ((int)(diff) < 0) { // Current time is greater than the expected timeout time. diff = 0; } VDBG("adjusting timeout\n"); timeout_ms = (diff / 1000) | 1; // never 0 } if ((fds[0].revents & POLLIN) == 0) { if (fds[0].revents & POLLERR) { // FIXME: links routinely go down; // this shouldn't necessarily exit. bb_error_msg("iface %s is down", argv_intf); if (state >= MONITOR) { // Only if we are in MONITOR or DEFEND run(argv, "deconfig", chosen_nip); } xfunc_die(); } continue; } // Read ARP packet if (safe_read(sock_fd, &p, sizeof(p)) < 0) { bb_perror_msg_and_die(bb_msg_read_error); } if (p.eth.ether_type != htons(ETHERTYPE_ARP)) continue; if (p.arp.arp_op != htons(ARPOP_REQUEST) && p.arp.arp_op != htons(ARPOP_REPLY) ) { continue; } #ifdef DEBUG { struct ether_addr *sha = (struct ether_addr *) p.arp.arp_sha; struct ether_addr *tha = (struct ether_addr *) p.arp.arp_tha; struct in_addr *spa = (struct in_addr *) p.arp.arp_spa; struct in_addr *tpa = (struct in_addr *) p.arp.arp_tpa; VDBG("source=%s %s\n", ether_ntoa(sha), inet_ntoa(*spa)); VDBG("target=%s %s\n", ether_ntoa(tha), inet_ntoa(*tpa)); } #endif ip_conflict = 0; if (memcmp(&p.arp.arp_sha, &G.our_ethaddr, ETH_ALEN) != 0) { if (memcmp(p.arp.arp_spa, &chosen_nip, 4) == 0) { // A probe or reply with source_ip == chosen ip ip_conflict = 1; } if (p.arp.arp_op == htons(ARPOP_REQUEST) && memcmp(p.arp.arp_spa, &const_int_0, 4) == 0 && memcmp(p.arp.arp_tpa, &chosen_nip, 4) == 0 ) { // A probe with source_ip == 0.0.0.0, target_ip == chosen ip: // another host trying to claim this ip! ip_conflict |= 2; } } VDBG("state:%d ip_conflict:%d\n", state, ip_conflict); if (!ip_conflict) continue; // Either src or target IP conflict exists if (state <= ANNOUNCE) { // PROBE or ANNOUNCE conflicts++; timeout_ms = PROBE_MIN * 1000 + CONFLICT_MULTIPLIER * random_delay_ms(conflicts); goto new_nip_and_PROBE; } // MONITOR or DEFEND: only src IP conflict is a problem if (ip_conflict & 1) { if (state == MONITOR) { // Src IP conflict, defend with a single ARP probe VDBG("monitor conflict - defending\n"); timeout_ms = DEFEND_INTERVAL * 1000; state = DEFEND; send_arp_request(chosen_nip, &G.our_ethaddr, chosen_nip); continue; } // state == DEFEND // Another src IP conflict, start over VDBG("defend conflict - starting over\n"); run(argv, "deconfig", chosen_nip); conflicts = 0; timeout_ms = 0; goto new_nip_and_PROBE; } // Note: if we only have a target IP conflict here (ip_conflict & 2), // IOW: if we just saw this sort of ARP packet: // aa:bb:cc:dd:ee:ff > xx:xx:xx:xx:xx:xx arp who-has <chosen_nip> tell 0.0.0.0 // we expect _kernel_ to respond to that, because <chosen_nip> // is (expected to be) configured on this iface. } // while (1) #undef argv_intf }
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 acpid_main(int argc UNUSED_PARAM, char **argv) { int nfd; int opts; struct pollfd *pfd; const char *opt_dir = "/etc/acpi"; const char *opt_input = "/dev/input/event"; const char *opt_logfile = "/var/log/acpid.log"; const char *opt_action = "/etc/acpid.conf"; const char *opt_map = "/etc/acpi.map"; #if ENABLE_FEATURE_PIDFILE const char *opt_pidfile = "/var/run/acpid.pid"; #endif INIT_G(); opt_complementary = "df:e--e"; opts = getopt32(argv, "c:de:fl:a:M:" IF_FEATURE_PIDFILE("p:") IF_FEATURE_ACPID_COMPAT("g:m:s:S:v"), &opt_dir, &opt_input, &opt_logfile, &opt_action, &opt_map IF_FEATURE_PIDFILE(, &opt_pidfile) IF_FEATURE_ACPID_COMPAT(, NULL, NULL, NULL, NULL) ); if (!(opts & OPT_f)) { /* No -f "Foreground", we go to background */ bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); } if (!(opts & OPT_d)) { /* No -d "Debug", we log to log file. * This includes any output from children. */ xmove_fd(xopen(opt_logfile, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO); xdup2(STDOUT_FILENO, STDERR_FILENO); /* Also, acpid's messages (but not children) will go to syslog too */ openlog(applet_name, LOG_PID, LOG_DAEMON); logmode = LOGMODE_SYSLOG | LOGMODE_STDIO; } /* else: -d "Debug", log is not redirected */ parse_conf_file(opt_action); parse_map_file(opt_map); xchdir(opt_dir); bb_signals((1 << SIGCHLD), SIG_IGN); bb_signals(BB_FATAL_SIGS, record_signo); pfd = NULL; nfd = 0; while (1) { int fd; char *dev_event; dev_event = xasprintf((opts & OPT_e) ? "%s" : "%s%u", opt_input, nfd); fd = open(dev_event, O_RDONLY | O_NONBLOCK); if (fd < 0) { if (nfd == 0) bb_simple_perror_msg_and_die(dev_event); break; } free(dev_event); pfd = xrealloc_vector(pfd, 1, nfd); pfd[nfd].fd = fd; pfd[nfd].events = POLLIN; nfd++; } write_pidfile(opt_pidfile); while (safe_poll(pfd, nfd, -1) > 0) { int i; for (i = 0; i < nfd; i++) { const char *event; if (!(pfd[i].revents & POLLIN)) { if (pfd[i].revents == 0) continue; /* this fd has nothing */ /* Likely POLLERR, POLLHUP, POLLNVAL. * Do not listen on this fd anymore. */ close(pfd[i].fd); nfd--; for (; i < nfd; i++) pfd[i].fd = pfd[i + 1].fd; break; /* do poll() again */ } event = NULL; if (option_mask32 & OPT_e) { char *buf; int len; buf = xmalloc_reads(pfd[i].fd, NULL); /* buf = "button/power PWRB 00000080 00000000" */ len = strlen(buf) - 9; if (len >= 0) buf[len] = '\0'; event = find_action(NULL, buf); free(buf); } else { struct input_event ev; if (sizeof(ev) != full_read(pfd[i].fd, &ev, sizeof(ev))) continue; if (ev.value != 1 && ev.value != 0) continue; event = find_action(&ev, NULL); } if (!event) continue; // spawn event handler process_event(event); } } if (ENABLE_FEATURE_CLEAN_UP) { while (nfd--) close(pfd[nfd].fd); free(pfd); } remove_pidfile(opt_pidfile); return EXIT_SUCCESS; }