int fakeidentd_main(int argc, char **argv) { enum { OPT_foreground = 0x1, OPT_inetd = 0x2, OPT_inetdwait = 0x4, OPT_fiw = 0x7, OPT_bindaddr = 0x8, }; const char *bind_address = NULL; unsigned opt; int fd; opt = getopt32(argv, "fiwb:", &bind_address); strcpy(bogouser, "nobody"); if (argv[optind]) strncpy(bogouser, argv[optind], sizeof(bogouser)); /* Daemonize if no -f and no -i and no -w */ if (!(opt & OPT_fiw)); bb_daemonize_or_rexec(0, argv); /* Where to log in inetd modes? "Classic" inetd * probably has its stderr /dev/null'ed (we need log to syslog?), * but daemontools-like utilities usually expect that children * log to stderr. I like daemontools more. Go their way. * (Or maybe we need yet another option "log to syslog") */ if (!(opt & OPT_fiw) /* || (opt & OPT_syslog) */) { openlog(applet_name, 0, LOG_DAEMON); logmode = LOGMODE_SYSLOG; } if (opt & OPT_inetd) { inetd_mode(); return 0; } /* Ignore closed connections when writing */ signal(SIGPIPE, SIG_IGN); fd = 0; if (!(opt & OPT_inetdwait)) { fd = create_and_bind_stream_or_die(bind_address, bb_lookup_port("identd", "tcp", 113)); xlisten(fd, 5); } isrv_run(fd, new_peer, do_rd, /*do_wr:*/ NULL, do_timeout, TIMEOUT, (opt & OPT_inetdwait) ? TIMEOUT : 0); return 0; }
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! */ }
int openvt_main(int argc, char **argv) { char vtname[sizeof(VC_FORMAT) + 2]; if (argc < 3) bb_show_usage(); /* check for illegal vt number: < 1 or > 63 */ sprintf(vtname, VC_FORMAT, (int)xatou_range(argv[1], 1, 63)); bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); /* grab new one */ close(0); xopen(vtname, O_RDWR); xdup2(0, STDOUT_FILENO); xdup2(0, STDERR_FILENO); argv += 2; BB_EXECVP(argv[0], argv); _exit(1); }
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"); }
void FAST_FUNC bb_sanitize_stdio(void) { bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE, NULL); }
int klogd_main(int argc UNUSED_PARAM, char **argv) { int i = 0; char *opt_c; int opt; int used; unsigned int cnt; 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); used = 0; cnt = 0; while (!bb_got_signal) { int n; int priority; char *start; char *eor; /* "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'; eor = &start[n]; /* 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) { /* kernel never generates multi-digit prios */ priority = (*start - '0'); start++; } if (*start == '>') start++; } /* Log (only non-empty lines) */ if (*start) { syslog(priority, "%s", start); /* give syslog time to catch up */ ++cnt; if ((cnt & 0x07) == 0 && (cnt < 300 || (eor - start) > 200)) usleep(50 * 1000); } if (!newline) break; start = newline; } } klogd_close(); syslog(LOG_NOTICE, "klogd: exiting"); if (bb_got_signal) kill_myself_with_sig(bb_got_signal); return EXIT_FAILURE; }
int klogd_main(int argc, 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 */ signal(SIGINT, klogd_signal); signal(SIGKILL, klogd_signal); signal(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 crond_main(int argc ATTRIBUTE_UNUSED, char **argv) { unsigned opt; INIT_G(); /* "-b after -f is ignored", and so on for every pair a-b */ opt_complementary = "f-b:b-f:S-L:L-S" USE_DEBUG_CROND_OPTION(":d-l") ":l+:d+"; /* -l and -d have numeric param */ opt = getopt32(argv, "l:L:fbSc:" USE_DEBUG_CROND_OPTION("d:"), &LogLevel, &LogFile, &CDir USE_DEBUG_CROND_OPTION(,&LogLevel)); /* both -d N and -l N set the same variable: LogLevel */ if (!(opt & OPT_f)) { /* close stdin, stdout, stderr. * close unused descriptors - don't need them. */ bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); } if (!DebugOpt && LogFile == NULL) { /* logging to syslog */ openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON); logmode = LOGMODE_SYSLOG; } xchdir(CDir); //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */ setenv("SHELL", DEFAULT_SHELL, 1); /* once, for all future children */ crondlog(LVL9 "crond (busybox "BB_VER") started, log level %d", LogLevel); SynchronizeDir(); /* main loop - synchronize to 1 second after the minute, minimum sleep * of 1 second. */ { time_t t1 = time(NULL); time_t t2; long dt; int rescan = 60; int sleep_time = 60; write_pidfile("/var/run/crond.pid"); for (;;) { sleep((sleep_time + 1) - (time(NULL) % sleep_time)); t2 = time(NULL); dt = (long)t2 - (long)t1; /* * The file 'cron.update' is checked to determine new cron * jobs. The directory is rescanned once an hour to deal * with any screwups. * * check for disparity. Disparities over an hour either way * result in resynchronization. A reverse-indexed disparity * less then an hour causes us to effectively sleep until we * match the original time (i.e. no re-execution of jobs that * have just been run). A forward-indexed disparity less then * an hour causes intermediate jobs to be run, but only once * in the worst case. * * when running jobs, the inequality used is greater but not * equal to t1, and less then or equal to t2. */ if (--rescan == 0) { rescan = 60; SynchronizeDir(); } CheckUpdates(); if (DebugOpt) crondlog(LVL5 "wakeup dt=%ld", dt); if (dt < -60 * 60 || dt > 60 * 60) { crondlog(WARN9 "time disparity of %d minutes detected", dt / 60); } else if (dt > 0) { TestJobs(t1, t2); RunJobs(); sleep(5); if (CheckJobs() > 0) { sleep_time = 10; } else { sleep_time = 60; } } t1 = t2; } } return 0; /* not reached */ }
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; }
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 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 int verbose; } 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 ) #define verbose (L.verbose ) 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:v", &r_opt, &l_opt, &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)); t += getpid(); 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 bb_info_msg("start, interface %s", argv_intf); } // 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 return EXIT_FAILURE; } 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) return EXIT_SUCCESS; // 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); } return EXIT_FAILURE; } 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 lpd_main(int argc UNUSED_PARAM, char *argv[]) { int spooling = spooling; // for compiler char *s, *queue; char *filenames[2]; // goto spool directory if (*++argv) xchdir(*argv++); // error messages of xfuncs will be sent over network xdup2(STDOUT_FILENO, STDERR_FILENO); // nullify ctrl/data filenames memset(filenames, 0, sizeof(filenames)); // read command s = queue = xmalloc_read_stdin(); // we understand only "receive job" command if (2 != *queue) { unsupported_cmd: printf("Command %02x %s\n", (unsigned char)s[0], "is not supported"); goto err_exit; } // parse command: "2 | QUEUE_NAME | '\n'" queue++; // protect against "/../" attacks // *strchrnul(queue, '\n') = '\0'; - redundant, sane() will do if (!*sane(queue)) return EXIT_FAILURE; // queue is a directory -> chdir to it and enter spooling mode spooling = chdir(queue) + 1; // 0: cannot chdir, 1: done // we don't free(s), we might need "queue" var later while (1) { char *fname; int fd; // int is easier than ssize_t: can use xatoi_u, // and can correctly display error returns (-1) int expected_len, real_len; // signal OK safe_write(STDOUT_FILENO, "", 1); // get subcommand // valid s must be of form: "SUBCMD | LEN | space | FNAME" // N.B. we bail out on any error s = xmalloc_read_stdin(); if (!s) { // (probably) EOF char *p, *q, var[2]; // non-spooling mode or no spool helper specified if (!spooling || !*argv) return EXIT_SUCCESS; // the only non-error exit // spooling mode but we didn't see both ctrlfile & datafile if (spooling != 7) goto err_exit; // reject job // spooling mode and spool helper specified -> exec spool helper // (we exit 127 if helper cannot be executed) var[1] = '\0'; // read and delete ctrlfile q = xmalloc_xopen_read_close(filenames[0], NULL); unlink(filenames[0]); // provide datafile name // we can use leaky setenv since we are about to exec or exit xsetenv("DATAFILE", filenames[1]); // parse control file by "\n" while ((p = strchr(q, '\n')) != NULL && isalpha(*q)) { *p++ = '\0'; // q is a line of <SYM><VALUE>, // we are setting environment string <SYM>=<VALUE>. // Ignoring "l<datafile>", exporting others: if (*q != 'l') { var[0] = *q++; xsetenv(var, q); } q = p; // next line } // helper should not talk over network. // this call reopens stdio fds to "/dev/null" // (no daemonization is done) bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO | DAEMON_ONLY_SANITIZE, NULL); BB_EXECVP(*argv, argv); exit(127); } // validate input. // we understand only "control file" or "data file" cmds if (2 != s[0] && 3 != s[0]) goto unsupported_cmd; if (spooling & (1 << (s[0]-1))) { printf("Duplicated subcommand\n"); goto err_exit; } // get filename *strchrnul(s, '\n') = '\0'; fname = strchr(s, ' '); if (!fname) { // bad_fname: printf("No or bad filename\n"); goto err_exit; } *fname++ = '\0'; // // s[0]==2: ctrlfile, must start with 'c' // // s[0]==3: datafile, must start with 'd' // if (fname[0] != s[0] + ('c'-2)) // goto bad_fname; // get length expected_len = bb_strtou(s + 1, NULL, 10); if (errno || expected_len < 0) { printf("Bad length\n"); goto err_exit; } if (2 == s[0] && expected_len > 16 * 1024) { // SECURITY: // ctrlfile can't be big (we want to read it back later!) printf("File is too big\n"); goto err_exit; } // open the file if (spooling) { // spooling mode: dump both files // job in flight has mode 0200 "only writable" sane(fname); fd = open3_or_warn(fname, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0200); if (fd < 0) goto err_exit; filenames[s[0] - 2] = xstrdup(fname); } else { // non-spooling mode: // 2: control file (ignoring), 3: data file fd = -1; if (3 == s[0]) fd = xopen(queue, O_RDWR | O_APPEND); } // signal OK safe_write(STDOUT_FILENO, "", 1); // copy the file real_len = bb_copyfd_size(STDIN_FILENO, fd, expected_len); if (real_len != expected_len) { printf("Expected %d but got %d bytes\n", expected_len, real_len); goto err_exit; } // get EOF indicator, see whether it is NUL (ok) // (and don't trash s[0]!) if (safe_read(STDIN_FILENO, &s[1], 1) != 1 || s[1] != 0) { // don't send error msg to peer - it obviously // doesn't follow the protocol, so probably // it can't understand us either goto err_exit; } if (spooling) { // chmod completely downloaded file as "readable+writable" fchmod(fd, 0600); // accumulate dump state // N.B. after all files are dumped spooling should be 1+2+4==7 spooling |= (1 << (s[0]-1)); // bit 1: ctrlfile; bit 2: datafile } free(s); close(fd); // NB: can do close(-1). Who cares? // NB: don't do "signal OK" write here, it will be done // at the top of the loop } // while (1) err_exit: // don't keep corrupted files if (spooling) { #define i spooling for (i = 2; --i >= 0; ) if (filenames[i]) unlink(filenames[i]); } return EXIT_FAILURE; }
int udhcpd_main(int argc, char **argv) { fd_set rfds; struct timeval tv; int server_socket = -1, bytes, retval, max_sock; struct dhcpMessage packet; uint8_t *state, *server_id, *requested; uint32_t server_id_align, requested_align, static_lease_ip; unsigned timeout_end; unsigned num_ips; unsigned opt; struct option_set *option; struct dhcpOfferedAddr *lease, static_lease; opt = getopt32(argv, "fS"); argv += optind; if (!(opt & 1)) { /* no -f */ bb_daemonize_or_rexec(0, argv); logmode &= ~LOGMODE_STDIO; } if (opt & 2) { /* -S */ openlog(applet_name, LOG_PID, LOG_LOCAL0); logmode |= LOGMODE_SYSLOG; } /* Would rather not do read_config before daemonization - * otherwise NOMMU machines will parse config twice */ read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE); /* Make sure fd 0,1,2 are open */ bb_sanitize_stdio(); /* Equivalent of doing a fflush after every \n */ setlinebuf(stdout); /* Create pidfile */ write_pidfile(server_config.pidfile); /* if (!..) bb_perror_msg("cannot create pidfile %s", pidfile); */ bb_info_msg("%s (v"BB_VER") started", applet_name); option = find_option(server_config.options, DHCP_LEASE_TIME); server_config.lease = LEASE_TIME; if (option) { memcpy(&server_config.lease, option->data + 2, 4); server_config.lease = ntohl(server_config.lease); } /* Sanity check */ num_ips = server_config.end_ip - server_config.start_ip + 1; if (server_config.max_leases > num_ips) { bb_error_msg("max_leases=%u is too big, setting to %u", (unsigned)server_config.max_leases, num_ips); server_config.max_leases = num_ips; } leases = xzalloc(server_config.max_leases * sizeof(*leases)); read_leases(server_config.lease_file); if (read_interface(server_config.interface, &server_config.ifindex, &server_config.server, server_config.arp)) { retval = 1; goto ret; } /* Setup the signal pipe */ udhcp_sp_setup(); timeout_end = monotonic_sec() + server_config.auto_time; while (1) { /* loop until universe collapses */ if (server_socket < 0) { server_socket = listen_socket(/*INADDR_ANY,*/ SERVER_PORT, server_config.interface); } max_sock = udhcp_sp_fd_set(&rfds, server_socket); if (server_config.auto_time) { tv.tv_sec = timeout_end - monotonic_sec(); tv.tv_usec = 0; } retval = 0; if (!server_config.auto_time || tv.tv_sec > 0) { retval = select(max_sock + 1, &rfds, NULL, NULL, server_config.auto_time ? &tv : NULL); } if (retval == 0) { write_leases(); timeout_end = monotonic_sec() + server_config.auto_time; continue; } if (retval < 0 && errno != EINTR) { DEBUG("error on select"); continue; } switch (udhcp_sp_read(&rfds)) { case SIGUSR1: bb_info_msg("Received a SIGUSR1"); write_leases(); /* why not just reset the timeout, eh */ timeout_end = monotonic_sec() + server_config.auto_time; continue; case SIGTERM: bb_info_msg("Received a SIGTERM"); goto ret0; case 0: break; /* no signal */ default: continue; /* signal or error (probably EINTR) */ } bytes = udhcp_recv_packet(&packet, server_socket); /* this waits for a packet - idle */ if (bytes < 0) { if (bytes == -1 && errno != EINTR) { DEBUG("error on read, %s, reopening socket", strerror(errno)); close(server_socket); server_socket = -1; } continue; } state = get_option(&packet, DHCP_MESSAGE_TYPE); if (state == NULL) { bb_error_msg("cannot get option from packet, ignoring"); continue; } /* Look for a static lease */ static_lease_ip = getIpByMac(server_config.static_leases, &packet.chaddr); if (static_lease_ip) { bb_info_msg("Found static lease: %x", static_lease_ip); memcpy(&static_lease.chaddr, &packet.chaddr, 16); static_lease.yiaddr = static_lease_ip; static_lease.expires = 0; lease = &static_lease; } else { lease = find_lease_by_chaddr(packet.chaddr); } switch (state[0]) { case DHCPDISCOVER: DEBUG("Received DISCOVER"); if (sendOffer(&packet) < 0) { bb_error_msg("send OFFER failed"); } /* circle test Stat. add by wangpu begin */ g_offercount ++; printf("Circle test: [DHCPS] send OFFER packet num:[%d]\n",g_offercount); memset(g_acCmd,0,sizeof(g_acCmd)); memset(g_interface,0,sizeof(g_interface)); sprintf(g_interface,"%s",server_config.interface); sprintf(g_acCmd,"echo %d > /var/circle/%s",g_offercount,g_interface); system(g_acCmd); /* circle test Stat. add by wangpu end */ break; case DHCPREQUEST: DEBUG("received REQUEST"); #if 0 /* ?¡¤??2a¨º? add by wangpu begin */ g_offercount ++; printf("Circle test: [DHCPS] send OFFER packet num:[%d]\n",g_offercount); memset(g_acCmd,0,sizeof(g_acCmd)); memset(g_interface,0,sizeof(g_interface)); sprintf(g_interface,"%s",server_config.interface); sprintf(g_acCmd,"echo %d > /var/circle/%s",g_offercount,g_interface); system(g_acCmd); /* ?¡¤??2a¨º? add by wangpu begin */ #endif requested = get_option(&packet, DHCP_REQUESTED_IP); server_id = get_option(&packet, DHCP_SERVER_ID); if (requested) memcpy(&requested_align, requested, 4); if (server_id) memcpy(&server_id_align, server_id, 4); if (lease) { if (server_id) { /* SELECTING State */ DEBUG("server_id = %08x", ntohl(server_id_align)); if (server_id_align == server_config.server && requested && requested_align == lease->yiaddr ) { sendACK(&packet, lease->yiaddr); } } else if (requested) { /* INIT-REBOOT State */ if (lease->yiaddr == requested_align) sendACK(&packet, lease->yiaddr); else sendNAK(&packet); } else if (lease->yiaddr == packet.ciaddr) { /* RENEWING or REBINDING State */ sendACK(&packet, lease->yiaddr); } else { /* don't know what to do!!!! */ sendNAK(&packet); } /* what to do if we have no record of the client */ } else if (server_id) { /* SELECTING State */ } else if (requested) { /* INIT-REBOOT State */ lease = find_lease_by_yiaddr(requested_align); if (lease) { if (lease_expired(lease)) { /* probably best if we drop this lease */ memset(lease->chaddr, 0, 16); /* make some contention for this address */ } else sendNAK(&packet); } else { uint32_t r = ntohl(requested_align); if (r < server_config.start_ip || r > server_config.end_ip ) { sendNAK(&packet); } /* else remain silent */ } } else { /* RENEWING or REBINDING State */ } break; case DHCPDECLINE: DEBUG("Received DECLINE"); if (lease) { memset(lease->chaddr, 0, 16); lease->expires = time(0) + server_config.decline_time; } break; case DHCPRELEASE: DEBUG("Received RELEASE"); if (lease) lease->expires = time(0); break; case DHCPINFORM: DEBUG("Received INFORM"); send_inform(&packet); break; default: bb_info_msg("Unsupported DHCP message (%02x) - ignoring", state[0]); } } ret0: retval = 0; ret: /*if (server_config.pidfile) - server_config.pidfile is never NULL */ remove_pidfile(server_config.pidfile); return retval; }
int openvt_main(int argc UNUSED_PARAM, char **argv) { char vtname[sizeof(VC_FORMAT) + sizeof(int)*3]; struct vt_stat vtstat; char *str_c; int vtno; int flags; enum { OPT_c = (1 << 0), OPT_w = (1 << 1), OPT_s = (1 << 2), OPT_l = (1 << 3), OPT_f = (1 << 4), OPT_v = (1 << 5), }; /* "+" - stop on first non-option */ flags = getopt32(argv, "+c:wslfv", &str_c); argv += optind; if (flags & OPT_c) { /* Check for illegal vt number: < 1 or > 63 */ vtno = xatou_range(str_c, 1, 63); } else { vtno = find_free_vtno(); } /* Grab new VT */ sprintf(vtname, VC_FORMAT, vtno); /* (Try to) clean up stray open fds above fd 2 */ bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS | DAEMON_ONLY_SANITIZE, NULL); close(STDIN_FILENO); /*setsid(); - BAD IDEA: after we exit, child is SIGHUPed... */ xopen(vtname, O_RDWR); xioctl(STDIN_FILENO, VT_GETSTATE, &vtstat); if (flags & OPT_s) { console_make_active(STDIN_FILENO, vtno); } if (!argv[0]) { argv--; argv[0] = getenv("SHELL"); if (!argv[0]) argv[0] = (char *) DEFAULT_SHELL; /*argv[1] = NULL; - already is */ } xdup2(STDIN_FILENO, STDOUT_FILENO); xdup2(STDIN_FILENO, STDERR_FILENO); #ifdef BLOAT { /* Handle -l (login shell) option */ const char *prog = argv[0]; if (flags & OPT_l) argv[0] = xasprintf("-%s", argv[0]); } #endif vfork_child(argv); if (flags & OPT_w) { /* We have only one child, wait for it */ safe_waitpid(-1, NULL, 0); /* loops on EINTR */ if (flags & OPT_s) { console_make_active(STDIN_FILENO, vtstat.v_active); // Compat: even with -c N (try to) disallocate: // # /usr/app/kbd-1.12/bin/openvt -f -c 9 -ws sleep 5 // openvt: could not deallocate console 9 xioctl(STDIN_FILENO, VT_DISALLOCATE, (void*)(ptrdiff_t)vtno); } } return EXIT_SUCCESS; }
int timeout_main(int argc UNUSED_PARAM, char **argv) { int signo; int status; int parent = 0; int timeout = 10; pid_t pid; #if !BB_MMU char *sv1, *sv2; #endif const char *opt_s = "TERM"; /* -p option is not documented, it is needed to support NOMMU. */ /* -t SECONDS; -p PARENT_PID */ opt_complementary = "t+" USE_FOR_NOMMU(":p+"); /* '+': stop at first non-option */ getopt32(argv, "+s:t:" USE_FOR_NOMMU("p:"), &opt_s, &timeout, &parent); /*argv += optind; - no, wait for bb_daemonize_or_rexec! */ signo = get_signum(opt_s); if (signo < 0) bb_error_msg_and_die("unknown signal '%s'", opt_s); /* We want to create a grandchild which will watch * and kill the grandparent. Other methods: * making parent watch child disrupts parent<->child link * (example: "tcpsvd 0.0.0.0 1234 timeout service_prog" - * it's better if service_prog is a child of tcpsvd!), * making child watch parent results in programs having * unexpected children. */ if (parent) /* we were re-execed, already grandchild */ goto grandchild; if (!argv[optind]) /* no PROG? */ bb_show_usage(); #if !BB_MMU sv1 = argv[optind]; sv2 = argv[optind + 1]; #endif pid = xvfork(); if (pid == 0) { /* Child: spawn grandchild and exit */ parent = getppid(); #if !BB_MMU argv[optind] = xasprintf("-p%u", parent); argv[optind + 1] = NULL; #endif /* NB: exits with nonzero on error: */ bb_daemonize_or_rexec(0, argv); /* Here we are grandchild. Sleep, then kill grandparent */ grandchild: /* Just sleep(HUGE_NUM); kill(parent) may kill wrong process! */ while (1) { sleep(1); if (--timeout <= 0) break; if (kill(parent, 0)) { /* process is gone */ return EXIT_SUCCESS; } } kill(parent, signo); return EXIT_SUCCESS; } /* Parent */ wait(&status); /* wait for child to die */ /* Did intermediate [v]fork or exec fail? */ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) return EXIT_FAILURE; /* Ok, exec a program as requested */ argv += optind; #if !BB_MMU argv[0] = sv1; argv[1] = sv2; #endif BB_EXECVP_or_die(argv); }
int crond_main(int argc UNUSED_PARAM, char **argv) { time_t t2; unsigned rescan; unsigned sleep_time; unsigned opts; INIT_G(); /* "-b after -f is ignored", and so on for every pair a-b */ opt_complementary = "f-b:b-f:S-L:L-S" IF_FEATURE_CROND_D(":d-l") /* -l and -d have numeric param */ ":l+" IF_FEATURE_CROND_D(":d+"); opts = getopt32(argv, "l:L:fbSc:" IF_FEATURE_CROND_D("d:"), &G.log_level, &G.log_filename, &G.crontab_dir_name IF_FEATURE_CROND_D(,&G.log_level)); /* both -d N and -l N set the same variable: G.log_level */ if (!(opts & OPT_f)) { /* close stdin, stdout, stderr. * close unused descriptors - don't need them. */ bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); } if (!(opts & OPT_d) && G.log_filename == NULL) { /* logging to syslog */ openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON); logmode = LOGMODE_SYSLOG; } //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */ reopen_logfile_to_stderr(); xchdir(G.crontab_dir_name); log8("crond (busybox "BB_VER") started, log level %d", G.log_level); rescan_crontab_dir(); write_pidfile(CONFIG_PID_FILE_PATH "/crond.pid"); /* Main loop */ t2 = time(NULL); rescan = 60; sleep_time = 60; for (;;) { struct stat sbuf; time_t t1; long dt; /* Synchronize to 1 minute, minimum 1 second */ t1 = t2; sleep(sleep_time - (time(NULL) % sleep_time)); t2 = time(NULL); dt = (long)t2 - (long)t1; reopen_logfile_to_stderr(); /* * The file 'cron.update' is checked to determine new cron * jobs. The directory is rescanned once an hour to deal * with any screwups. * * Check for time jump. Disparities over an hour either way * result in resynchronization. A negative disparity * less than an hour causes us to effectively sleep until we * match the original time (i.e. no re-execution of jobs that * have just been run). A positive disparity less than * an hour causes intermediate jobs to be run, but only once * in the worst case. * * When running jobs, the inequality used is greater but not * equal to t1, and less then or equal to t2. */ if (stat(G.crontab_dir_name, &sbuf) != 0) sbuf.st_mtime = 0; /* force update (once) if dir was deleted */ if (G.crontab_dir_mtime != sbuf.st_mtime) { G.crontab_dir_mtime = sbuf.st_mtime; rescan = 1; } if (--rescan == 0) { rescan = 60; rescan_crontab_dir(); } process_cron_update_file(); log5("wakeup dt=%ld", dt); if (dt < -60 * 60 || dt > 60 * 60) { bb_error_msg("time disparity of %ld minutes detected", dt / 60); /* and we do not run any jobs in this case */ } else if (dt > 0) { /* Usual case: time advances forward, as expected */ flag_starting_jobs(t1, t2); start_jobs(); sleep_time = 60; if (check_completions() > 0) { /* some jobs are still running */ sleep_time = 10; } } /* else: time jumped back, do not run any jobs */ } /* for (;;) */ return 0; /* not reached */ }
int start_stop_daemon_main(int argc UNUSED_PARAM, char **argv) { unsigned opt; char *signame; char *startas; char *chuid; #ifdef OLDER_VERSION_OF_X struct stat execstat; #endif #if ENABLE_FEATURE_START_STOP_DAEMON_FANCY // char *retry_arg = NULL; // int retries = -1; char *opt_N; #endif INIT_G(); #if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS applet_long_options = start_stop_daemon_longopts; #endif /* -K or -S is required; they are mutually exclusive */ /* -p is required if -m is given */ /* -xpun (at least one) is required if -K is given */ /* -xa (at least one) is required if -S is given */ /* -q turns off -v */ opt_complementary = "K:S:K--S:S--K:m?p:K?xpun:S?xa" IF_FEATURE_START_STOP_DAEMON_FANCY("q-v"); opt = getopt32(argv, "KSbqtma:n:s:u:c:x:p:" IF_FEATURE_START_STOP_DAEMON_FANCY("ovN:R:"), &startas, &cmdname, &signame, &userspec, &chuid, &execname, &pidfile IF_FEATURE_START_STOP_DAEMON_FANCY(,&opt_N) /* We accept and ignore -R <param> / --retry <param> */ IF_FEATURE_START_STOP_DAEMON_FANCY(,NULL) ); if (opt & OPT_s) { signal_nr = get_signum(signame); if (signal_nr < 0) bb_show_usage(); } if (!(opt & OPT_a)) startas = execname; if (!execname) /* in case -a is given and -x is not */ execname = startas; if (execname) { G.execname_sizeof = strlen(execname) + 1; G.execname_cmpbuf = xmalloc(G.execname_sizeof + 1); } // IF_FEATURE_START_STOP_DAEMON_FANCY( // if (retry_arg) // retries = xatoi_positive(retry_arg); // ) //argc -= optind; argv += optind; if (userspec) { user_id = bb_strtou(userspec, NULL, 10); if (errno) user_id = xuname2uid(userspec); } /* Both start and stop need to know current processes */ do_procinit(); if (opt & CTX_STOP) { int i = do_stop(); return (opt & OPT_OKNODO) ? 0 : (i <= 0); } if (G.found_procs) { if (!QUIET) printf("%s is already running\n%u\n", execname, (unsigned)G.found_procs->pid); return !(opt & OPT_OKNODO); } #ifdef OLDER_VERSION_OF_X if (execname) xstat(execname, &execstat); #endif *--argv = startas; if (opt & OPT_BACKGROUND) { #if BB_MMU bb_daemonize(DAEMON_DEVNULL_STDIO + DAEMON_CLOSE_EXTRA_FDS + DAEMON_DOUBLE_FORK); /* DAEMON_DEVNULL_STDIO is superfluous - * it's always done by bb_daemonize() */ #else pid_t pid = xvfork(); if (pid != 0) { /* parent */ /* why _exit? the child may have changed the stack, * so "return 0" may do bad things */ _exit(EXIT_SUCCESS); } /* Child */ setsid(); /* detach from controlling tty */ /* Redirect stdio to /dev/null, close extra FDs. * We do not actually daemonize because of DAEMON_ONLY_SANITIZE */ bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO + DAEMON_CLOSE_EXTRA_FDS + DAEMON_ONLY_SANITIZE, NULL /* argv, unused */ ); #endif } if (opt & OPT_MAKEPID) { /* User wants _us_ to make the pidfile */ write_pidfile(pidfile); } if (opt & OPT_c) { struct bb_uidgid_t ugid; parse_chown_usergroup_or_die(&ugid, chuid); if (ugid.uid != (uid_t) -1L) { struct passwd *pw = xgetpwuid(ugid.uid); if (ugid.gid != (gid_t) -1L) pw->pw_gid = ugid.gid; /* initgroups, setgid, setuid: */ change_identity(pw); } else if (ugid.gid != (gid_t) -1L) { xsetgid(ugid.gid); setgroups(1, &ugid.gid); } } #if ENABLE_FEATURE_START_STOP_DAEMON_FANCY if (opt & OPT_NICELEVEL) { /* Set process priority */ int prio = getpriority(PRIO_PROCESS, 0) + xatoi_range(opt_N, INT_MIN/2, INT_MAX/2); if (setpriority(PRIO_PROCESS, 0, prio) < 0) { bb_perror_msg_and_die("setpriority(%d)", prio); } } #endif execvp(startas, argv); bb_perror_msg_and_die("can't execute '%s'", startas); }