static void handle_fd_set(isrv_state_t *state, fd_set *fds, int (*h)(int, void **)) { enum { LONG_CNT = sizeof(fd_set) / sizeof(long) }; int fds_pos; int fd, peer; /* need to know value at _the beginning_ of this routine */ int fd_cnt = FD_COUNT; if (LONG_CNT * sizeof(long) != sizeof(fd_set)) BUG_sizeof_fd_set_is_strange(); fds_pos = 0; while (1) { /* Find next nonzero bit */ while (fds_pos < LONG_CNT) { if (((long*)fds)[fds_pos] == 0) { fds_pos++; continue; } /* Found non-zero word */ fd = fds_pos * sizeof(long)*8; /* word# -> bit# */ while (1) { if (FD_ISSET(fd, fds)) { FD_CLR(fd, fds); goto found_fd; } fd++; } } break; /* all words are zero */ found_fd: if (fd >= fd_cnt) { /* paranoia */ DPRINTF("handle_fd_set: fd > fd_cnt?? (%d > %d)", fd, fd_cnt); break; } DPRINTF("handle_fd_set: fd %d is active", fd); peer = FD2PEER[fd]; if (peer < 0) continue; /* peer is already gone */ if (peer == 0) { handle_accept(state, fd); continue; } DPRINTF("h(fd:%d)", fd); if (h(fd, &PARAM_TBL[peer])) { /* this peer is gone */ remove_peer(state, peer); } else if (TIMEOUT) { TIMEO_TBL[peer] = monotonic_sec(); } } }
void FAST_FUNC bb_progress_init(bb_progress_t *p, const char *curfile) { #if ENABLE_UNICODE_SUPPORT init_unicode(); p->curfile = unicode_conv_to_printable_fixedwidth(/*NULL,*/ curfile, 20); #else p->curfile = curfile; #endif p->start_sec = monotonic_sec(); p->last_update_sec = p->start_sec; p->last_change_sec = p->start_sec; p->last_size = 0; }
/* File already had beg_size bytes. * Then we started downloading. * We downloaded "transferred" bytes so far. * Download is expected to stop when total size (beg_size + transferred) * will be "totalsize" bytes. * If totalsize == 0, then it is unknown. */ void FAST_FUNC bb_progress_update(bb_progress_t *p, uoff_t beg_size, uoff_t transferred, uoff_t totalsize) { uoff_t beg_and_transferred; unsigned since_last_update, elapsed; int barlength; int kiloscale; //transferred = 1234; /* use for stall detection testing */ //totalsize = 0; /* use for unknown size download testing */ elapsed = monotonic_sec(); since_last_update = elapsed - p->last_update_sec; p->last_update_sec = elapsed; if (totalsize != 0 && transferred >= totalsize - beg_size) { /* Last call. Do not skip this update */ transferred = totalsize - beg_size; /* sanitize just in case */ } else if (since_last_update == 0) { /* * Do not update on every call * (we can be called on every network read!) */ return; } kiloscale = 0; /* * Scale sizes down if they are close to overflowing. * This allows calculations like (100 * transferred / totalsize) * without risking overflow: we guarantee 10 highest bits to be 0. * Introduced error is less than 1 / 2^12 ~= 0.025% */ if (ULONG_MAX > 0xffffffff || sizeof(off_t) == 4 || sizeof(off_t) != 8) { /* * 64-bit CPU || small off_t: in either case, * >> is cheap, single-word operation. * ... || strange off_t: also use this code * (it is safe, just suboptimal wrt code size), * because 32/64 optimized one works only for 64-bit off_t. */ if (totalsize >= (1 << 22)) { totalsize >>= 10; beg_size >>= 10; transferred >>= 10; kiloscale = 1; } } else {
static void xid_expire(void) { struct xid_item *item = dhcprelay_xid_list.next; struct xid_item *last = &dhcprelay_xid_list; unsigned current_time = monotonic_sec(); while (item != NULL) { if ((current_time - item->timestamp) > MAX_LIFETIME) { last->next = item->next; free(item); item = last->next; } else { last = item; item = item->next; } } }
static struct xid_item *xid_add(uint32_t xid, struct sockaddr_in *ip, int client) { struct xid_item *item; /* create new xid entry */ item = xmalloc(sizeof(struct xid_item)); /* add xid entry */ item->ip = *ip; item->xid = xid; item->client = client; item->timestamp = monotonic_sec(); item->next = dhcprelay_xid_list.next; dhcprelay_xid_list.next = item; return item; }
static int setup_log(void) { rploglen = strlen(rplog); if (rploglen < 7) { warnx("log must have at least seven characters"); return 0; } if (pipe(logpipe)) { warnx("cannot create pipe for log"); return -1; } coe(logpipe[1]); coe(logpipe[0]); ndelay_on(logpipe[0]); ndelay_on(logpipe[1]); if (dup2(logpipe[1], 2) == -1) { warnx("cannot set filedescriptor for log"); return -1; } pfd[0].fd = logpipe[0]; pfd[0].events = POLLIN; stamplog = monotonic_sec(); return 1; }
/* Driver */ void isrv_run( int listen_fd, int (*new_peer)(isrv_state_t *state, int fd), int (*do_rd)(int fd, void **), int (*do_wr)(int fd, void **), int (*do_timeout)(void **), int timeout, int linger_timeout) { isrv_state_t *state = xzalloc(sizeof(*state)); state->new_peer = new_peer; state->timeout = timeout; /* register "peer" #0 - it will accept new connections */ isrv_register_peer(state, NULL); isrv_register_fd(state, /*peer:*/ 0, listen_fd); isrv_want_rd(state, listen_fd); /* remember flags to make blocking<->nonblocking switch faster */ /* (suppress gcc warning "cast from ptr to int of different size") */ PARAM_TBL[0] = (void*)(ptrdiff_t)(fcntl(listen_fd, F_GETFL)); while (1) { struct timeval tv; fd_set rd; fd_set wr; fd_set *wrp = NULL; int n; tv.tv_sec = timeout; if (PEER_COUNT <= 1) tv.tv_sec = linger_timeout; tv.tv_usec = 0; rd = state->rd; if (WR_COUNT) { wr = state->wr; wrp = ≀ } DPRINTF("run: select(FD_COUNT:%d,timeout:%d)...", FD_COUNT, (int)tv.tv_sec); n = select(FD_COUNT, &rd, wrp, NULL, tv.tv_sec ? &tv : NULL); DPRINTF("run: ...select:%d", n); if (n < 0) { if (errno != EINTR) bb_perror_msg("select"); continue; } if (n == 0 && linger_timeout && PEER_COUNT <= 1) break; if (timeout) { time_t t = monotonic_sec(); if (t != CURTIME) { CURTIME = t; handle_timeout(state, do_timeout); } } if (n > 0) { handle_fd_set(state, &rd, do_rd); if (wrp) handle_fd_set(state, wrp, do_wr); } } DPRINTF("run: bailout"); /* NB: accept socket is not closed. Caller is to decide what to do */ }
/* File already had beg_size bytes. * Then we started downloading. * We downloaded "transferred" bytes so far. * Download is expected to stop when total size (beg_size + transferred) * will be "totalsize" bytes. * If totalsize == 0, then it is unknown. */ void FAST_FUNC bb_progress_update(bb_progress_t *p, uoff_t beg_size, uoff_t transferred, uoff_t totalsize) { char numbuf5[6]; /* 5 + 1 for NUL */ unsigned since_last_update, elapsed; int notty; //transferred = 1234; /* use for stall detection testing */ //totalsize = 0; /* use for unknown size download testing */ elapsed = monotonic_sec(); since_last_update = elapsed - p->last_update_sec; p->last_update_sec = elapsed; if (totalsize != 0 && transferred >= totalsize - beg_size) { /* Last call. Do not skip this update */ transferred = totalsize - beg_size; /* sanitize just in case */ } else if (since_last_update == 0) { /* * Do not update on every call * (we can be called on every network read!) */ return; } /* Before we lose real, unscaled sizes, produce human-readable size string */ smart_ulltoa5(beg_size + transferred, numbuf5, " kMGTPEZY")[0] = '\0'; /* * Scale sizes down if they are close to overflowing. * This allows calculations like (100 * transferred / totalsize) * without risking overflow: we guarantee 10 highest bits to be 0. * Introduced error is less than 1 / 2^12 ~= 0.025% */ while (totalsize >= (1 << 20)) { totalsize >>= 8; beg_size >>= 8; transferred >>= 8; } /* If they were huge, now they are scaled down to [1048575,4096] range. * (N * totalsize) won't overflow 32 bits for N up to 4096. */ #if ULONG_MAX == 0xffffffff /* 32-bit CPU, uoff_t arithmetic is complex on it, cast variables to narrower types */ # define totalsize ((unsigned)totalsize) # define beg_size ((unsigned)beg_size) # define transferred ((unsigned)transferred) #endif notty = !isatty(STDERR_FILENO); if (ENABLE_UNICODE_SUPPORT) fprintf(stderr, "\r%s " + notty, p->curfile); else fprintf(stderr, "\r%-20.20s " + notty, p->curfile); if (totalsize != 0) { int barlength; unsigned beg_and_transferred; /* does not need uoff_t, see scaling code */ unsigned ratio; beg_and_transferred = beg_size + transferred; ratio = 100 * beg_and_transferred / totalsize; /* can't overflow ^^^^^^^^^^^^^^^ */ fprintf(stderr, "%3u%% ", ratio); barlength = get_terminal_width(2) - 48; /* * Must reject barlength <= 0 (terminal too narrow). While at it, * also reject: 1-char bar (useless), 2-char bar (ridiculous). */ if (barlength > 2) { if (barlength > 999) barlength = 999; { /* god bless gcc for variable arrays :) */ char buf[barlength + 1]; unsigned stars = (unsigned)barlength * beg_and_transferred / totalsize; /* can't overflow ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ memset(buf, ' ', barlength); buf[barlength] = '\0'; memset(buf, '*', stars); fprintf(stderr, "|%s| ", buf); } } } fputs(numbuf5, stderr); /* "NNNNk" */ since_last_update = elapsed - p->last_change_sec; if ((unsigned)transferred != p->last_size) { p->last_change_sec = elapsed; p->last_size = (unsigned)transferred; if (since_last_update >= STALLTIME) { /* We "cut out" these seconds from elapsed time * by adjusting start time */ p->start_sec += since_last_update; } since_last_update = 0; /* we are un-stalled now */ } elapsed -= p->start_sec; /* now it's "elapsed since start" */ if (since_last_update >= STALLTIME) { fprintf(stderr, " - stalled -"); } else if (!totalsize || !transferred || (int)elapsed < 0) { fprintf(stderr, " --:--:-- ETA"); } else { unsigned eta, secs, hours; unsigned bytes; bytes = totalsize - beg_size; /* Estimated remaining time = * estimated_sec_to_dl_bytes - elapsed_sec = * bytes / average_bytes_sec_so_far - elapsed = * bytes / (transferred/elapsed) - elapsed = * bytes * elapsed / transferred - elapsed */ eta = (unsigned long)bytes * elapsed / transferred - elapsed; /* if 32bit, can overflow ^^^^^^^^^^, but this would only show bad ETA */ if (eta >= 1000*60*60) eta = 1000*60*60 - 1; #if 0 /* To prevent annoying "back-and-forth" estimation jitter, * if new ETA is larger than the last just by a few seconds, * disregard it, and show last one. The end result is that * ETA usually only decreases, unless download slows down a lot. */ if ((unsigned)(eta - p->last_eta) < 10) eta = p->last_eta; p->last_eta = eta; #endif secs = eta % 3600; hours = eta / 3600; fprintf(stderr, "%3u:%02u:%02u ETA", hours, secs / 60, secs % 60); } if (notty) fputc('\n', stderr); }
int runsvdir_main(int argc, 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; int pid; unsigned deadline; unsigned now; unsigned stampcheck; char ch; int i; argv++; if (!*argv) bb_show_usage(); if (argv[0][0] == '-') { switch (argv[0][1]) { case 'P': set_pgrp = 1; case '-': ++argv; } if (!*argv) bb_show_usage(); } sig_catch(SIGTERM, s_term); sig_catch(SIGHUP, s_hangup); svdir = *argv++; if (argv && *argv) { rplog = *argv; if (setup_log() != 1) { rplog = 0; warnx("log service disabled"); } } curdir = open_read("."); if (curdir == -1) fatal2_cannot("open current directory", ""); coe(curdir); stampcheck = monotonic_sec(); for (;;) { /* collect children */ for (;;) { pid = wait_nohang(&wstat); if (pid <= 0) break; for (i = 0; i < svnum; i++) { if (pid == sv[i].pid) { /* runsv has gone */ sv[i].pid = 0; check = 1; break; } } } now = monotonic_sec(); if ((int)(now - stampcheck) >= 0) { /* wait at least a second */ stampcheck = now + 1; if (stat(svdir, &s) != -1) { if (check || 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; check = 0; //if (now <= mtime) // sleep(1); runsvdir(); while (fchdir(curdir) == -1) { warn2_cannot("change directory, pausing", ""); sleep(5); } } else warn2_cannot("change directory to ", svdir); } } else warn2_cannot("stat ", svdir); } if (rplog) { if ((int)(now - stamplog) >= 0) { write(logpipe[1], ".", 1); stamplog = now + 900; } } pfd[0].revents = 0; sig_block(SIGCHLD); deadline = (check ? 1 : 5); if (rplog) poll(pfd, 1, deadline*1000); else sleep(deadline); sig_unblock(SIGCHLD); if (pfd[0].revents & POLLIN) { while (read(logpipe[0], &ch, 1) > 0) { if (ch) { for (i = 6; i < rploglen; i++) rplog[i-1] = rplog[i]; rplog[rploglen-1] = ch; } } } switch (exitsoon) { case 1: _exit(0); case 2: for (i = 0; i < svnum; i++) if (sv[i].pid) kill(sv[i].pid, SIGTERM); _exit(111); } } /* not reached */ return 0; }
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 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; }