/* Run command CMD and return statistics on it. Put the statistics in *RESP. */ static void run_command(char *const *cmd, resource_t *resp) { pid_t pid; /* Pid of child. */ void (*interrupt_signal)(int); void (*quit_signal)(int); resp->elapsed_ms = monotonic_ms(); pid = vfork(); /* Run CMD as child process. */ if (pid < 0) bb_perror_msg_and_die("fork"); if (pid == 0) { /* If child. */ /* Don't cast execvp arguments; that causes errors on some systems, versus merely warnings if the cast is left off. */ BB_EXECVP(cmd[0], cmd); xfunc_error_retval = (errno == ENOENT ? 127 : 126); bb_error_msg_and_die("can't run %s", cmd[0]); } /* Have signals kill the child but not self (if possible). */ //TODO: just block all sigs? and reenable them in the very end in main? interrupt_signal = signal(SIGINT, SIG_IGN); quit_signal = signal(SIGQUIT, SIG_IGN); resuse_end(pid, resp); /* Re-enable signals. */ signal(SIGINT, interrupt_signal); signal(SIGQUIT, quit_signal); }
/* * Check expiration for each timer. If a timer is expired, * call the expire function for the timer and update the timer. * Return the next interval for select() call. */ struct timeval *dhcp6_timer_check(void) { unsigned long long now = monotonic_ms(); struct dhcp6_timer *tm, *tm_next; client6_config.tm_sentinel = ULLONG_MAX; for (tm = LIST_FIRST(&client6_config.timer_head); tm; tm = tm_next) { tm_next = LIST_NEXT(tm, link); if ((long long)(now - tm->t) >= 0) { if ((*tm->expire)(tm->expire_data) == NULL) continue; /* timer has been freed */ } if (tm->t < client6_config.tm_sentinel) client6_config.tm_sentinel = tm->t; } if (ULLONG_MAX == client6_config.tm_sentinel) { /* no need to timeout */ return NULL; } else if (client6_config.tm_sentinel < now) { /* this may occur when the interval is too small */ client6_config.tm_check.tv_sec = client6_config.tm_check.tv_usec = 0; } else { client6_config.tm_check.tv_sec = (client6_config.tm_sentinel - now) / 1000ULL; client6_config.tm_check.tv_usec = ((client6_config.tm_sentinel - now) % 1000ULL) * 1000ULL; } return (&client6_config.tm_check); }
void dhcp6_timer_set(unsigned long long t, struct dhcp6_timer *timer) { timer->t = monotonic_ms() + t; /* update the next expiration time */ if (timer->t < client6_config.tm_sentinel) client6_config.tm_sentinel = timer->t; }
unsigned long long dhcp6_timer_rest(struct dhcp6_timer *timer) { unsigned long long now = monotonic_ms(); if (timer->t - now <= 0) { log2("a timer must be expired, but not yet"); return 0; } else { return (timer->t - now); } }
/* pid_t is short on BSDI, so don't try to promote it. */ static void resuse_end(pid_t pid, resource_t *resp) { pid_t caught; /* Ignore signals, but don't ignore the children. When wait3 returns the child process, set the time the command finished. */ while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) { if (caught == -1 && errno != EINTR) { bb_perror_msg("wait"); return; } } resp->elapsed_ms = monotonic_ms() - resp->elapsed_ms; }
/* Run command CMD and return statistics on it. Put the statistics in *RESP. */ static void run_command(char *const *cmd, resource_t *resp) { pid_t pid; void (*interrupt_signal)(int); void (*quit_signal)(int); resp->elapsed_ms = monotonic_ms(); pid = xvfork(); if (pid == 0) { /* Child */ BB_EXECVP_or_die((char**)cmd); } /* Have signals kill the child but not self (if possible). */ //TODO: just block all sigs? and reenable them in the very end in main? interrupt_signal = signal(SIGINT, SIG_IGN); quit_signal = signal(SIGQUIT, SIG_IGN); resuse_end(pid, resp); /* Re-enable signals. */ signal(SIGINT, interrupt_signal); signal(SIGQUIT, quit_signal); }
//TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL" //string. hush naturally has it, and ash has setvareq(). //Here we can simply store "VAR=" at buffer start and store read data directly //after "=", then pass buffer to setvar() to consume. const char* FAST_FUNC shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), char **argv, const char *ifs, int read_flags, const char *opt_n, const char *opt_p, const char *opt_t, const char *opt_u ) { unsigned end_ms; /* -t TIMEOUT */ int fd; /* -u FD */ int nchars; /* -n NUM */ char **pp; char *buffer; struct termios tty, old_tty; const char *retval; int bufpos; /* need to be able to hold -1 */ int startword; smallint backslash; pp = argv; while (*pp) { if (!is_well_formed_var_name(*pp, '\0')) { /* Mimic bash message */ bb_error_msg("read: '%s': not a valid identifier", *pp); return (const char *)(uintptr_t)1; } pp++; } nchars = 0; /* if != 0, -n is in effect */ if (opt_n) { nchars = bb_strtou(opt_n, NULL, 10); if (nchars < 0 || errno) return "invalid count"; /* note: "-n 0": off (bash 3.2 does this too) */ } end_ms = 0; if (opt_t) { end_ms = bb_strtou(opt_t, NULL, 10); if (errno || end_ms > UINT_MAX / 2048) return "invalid timeout"; end_ms *= 1000; #if 0 /* even bash has no -t N.NNN support */ ts.tv_sec = bb_strtou(opt_t, &p, 10); ts.tv_usec = 0; /* EINVAL means number is ok, but not terminated by NUL */ if (*p == '.' && errno == EINVAL) { char *p2; if (*++p) { int scale; ts.tv_usec = bb_strtou(p, &p2, 10); if (errno) return "invalid timeout"; scale = p2 - p; /* normalize to usec */ if (scale > 6) return "invalid timeout"; while (scale++ < 6) ts.tv_usec *= 10; } } else if (ts.tv_sec < 0 || errno) { return "invalid timeout"; } if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */ return "invalid timeout"; } #endif /* if 0 */ } fd = STDIN_FILENO; if (opt_u) { fd = bb_strtou(opt_u, NULL, 10); if (fd < 0 || errno) return "invalid file descriptor"; } if (opt_p && isatty(fd)) { fputs(opt_p, stderr); fflush_all(); } if (ifs == NULL) ifs = defifs; if (nchars || (read_flags & BUILTIN_READ_SILENT)) { tcgetattr(fd, &tty); old_tty = tty; if (nchars) { tty.c_lflag &= ~ICANON; tty.c_cc[VMIN] = nchars < 256 ? nchars : 255; } if (read_flags & BUILTIN_READ_SILENT) { tty.c_lflag &= ~(ECHO | ECHOK | ECHONL); } /* This forces execution of "restoring" tcgetattr later */ read_flags |= BUILTIN_READ_SILENT; /* if tcgetattr failed, tcsetattr will fail too. * Ignoring, it's harmless. */ tcsetattr(fd, TCSANOW, &tty); } retval = (const char *)(uintptr_t)0; startword = 1; backslash = 0; if (end_ms) /* NB: end_ms stays nonzero: */ end_ms = ((unsigned)monotonic_ms() + end_ms) | 1; buffer = NULL; bufpos = 0; do { char c; if (end_ms) { int timeout; struct pollfd pfd[1]; pfd[0].fd = fd; pfd[0].events = POLLIN; timeout = end_ms - (unsigned)monotonic_ms(); if (timeout <= 0 /* already late? */ || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */ ) { /* timed out! */ retval = (const char *)(uintptr_t)1; goto ret; } } if ((bufpos & 0xff) == 0) buffer = xrealloc(buffer, bufpos + 0x100); if (nonblock_safe_read(fd, &buffer[bufpos], 1) != 1) { retval = (const char *)(uintptr_t)1; break; } c = buffer[bufpos]; if (c == '\0') continue; if (backslash) { backslash = 0; if (c != '\n') goto put; continue; } if (!(read_flags & BUILTIN_READ_RAW) && c == '\\') { backslash = 1; continue; } if (c == '\n') break; /* $IFS splitting. NOT done if we run "read" * without variable names (bash compat). * Thus, "read" and "read REPLY" are not the same. */ if (argv[0]) { /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */ const char *is_ifs = strchr(ifs, c); if (startword && is_ifs) { if (isspace(c)) continue; /* it is a non-space ifs char */ startword--; if (startword == 1) /* first one? */ continue; /* yes, it is not next word yet */ } startword = 0; if (argv[1] != NULL && is_ifs) { buffer[bufpos] = '\0'; bufpos = 0; setvar(*argv, buffer); argv++; /* can we skip one non-space ifs char? (2: yes) */ startword = isspace(c) ? 2 : 1; continue; } } put: bufpos++; } while (--nchars); if (argv[0]) { /* Remove trailing space $IFS chars */ while (--bufpos >= 0 && isspace(buffer[bufpos]) && strchr(ifs, buffer[bufpos]) != NULL) continue; buffer[bufpos + 1] = '\0'; /* Use the remainder as a value for the next variable */ setvar(*argv, buffer); /* Set the rest to "" */ while (*++argv) setvar(*argv, ""); } else { /* Note: no $IFS removal */ buffer[bufpos] = '\0'; setvar("REPLY", buffer); } ret: free(buffer); if (read_flags & BUILTIN_READ_SILENT) tcsetattr(fd, TCSANOW, &old_tty); return retval; }
/* Returns 1 if no reply received */ int FAST_FUNC arpping(uint32_t test_nip, const uint8_t *safe_mac, uint32_t from_ip, uint8_t *from_mac, const char *interface, unsigned timeo) { int timeout_ms; struct pollfd pfd[1]; #define s (pfd[0].fd) /* socket */ int rv = 1; /* "no reply received" yet */ struct sockaddr addr; /* for interface name */ struct arpMsg arp; if (!timeo) return 1; s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)); if (s == -1) { bb_perror_msg(bb_msg_can_not_create_raw_socket); return -1; } if (setsockopt_broadcast(s) == -1) { bb_perror_msg("can't enable bcast on raw socket"); goto ret; } /* send arp request */ memset(&arp, 0, sizeof(arp)); memset(arp.h_dest, 0xff, 6); /* MAC DA */ memcpy(arp.h_source, from_mac, 6); /* MAC SA */ arp.h_proto = htons(ETH_P_ARP); /* protocol type (Ethernet) */ arp.htype = htons(ARPHRD_ETHER); /* hardware type */ arp.ptype = htons(ETH_P_IP); /* protocol type (ARP message) */ arp.hlen = 6; /* hardware address length */ arp.plen = 4; /* protocol address length */ arp.operation = htons(ARPOP_REQUEST); /* ARP op code */ memcpy(arp.sHaddr, from_mac, 6); /* source hardware address */ memcpy(arp.sInaddr, &from_ip, sizeof(from_ip)); /* source IP address */ /* tHaddr is zero-filled */ /* target hardware address */ memcpy(arp.tInaddr, &test_nip, sizeof(test_nip));/* target IP address */ memset(&addr, 0, sizeof(addr)); safe_strncpy(addr.sa_data, interface, sizeof(addr.sa_data)); if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0) { // TODO: error message? caller didn't expect us to fail, // just returning 1 "no reply received" misleads it. goto ret; } /* wait for arp reply, and check it */ timeout_ms = (int)timeo; do { typedef uint32_t aliased_uint32_t FIX_ALIASING; int r; unsigned prevTime = monotonic_ms(); pfd[0].events = POLLIN; r = safe_poll(pfd, 1, timeout_ms); if (r < 0) break; if (r) { r = safe_read(s, &arp, sizeof(arp)); if (r < 0) break; //log3("sHaddr %02x:%02x:%02x:%02x:%02x:%02x", // arp.sHaddr[0], arp.sHaddr[1], arp.sHaddr[2], // arp.sHaddr[3], arp.sHaddr[4], arp.sHaddr[5]); if (r >= ARP_MSG_SIZE && arp.operation == htons(ARPOP_REPLY) /* don't check it: Linux doesn't return proper tHaddr (fixed in 2.6.24?) */ /* && memcmp(arp.tHaddr, from_mac, 6) == 0 */ && *(aliased_uint32_t*)arp.sInaddr == test_nip ) { /* if ARP source MAC matches safe_mac * (which is client's MAC), then it's not a conflict * (client simply already has this IP and replies to ARPs!) */ if (!safe_mac || memcmp(safe_mac, arp.sHaddr, 6) != 0) rv = 0; //else log2("sHaddr == safe_mac"); break; } } timeout_ms -= (unsigned)monotonic_ms() - prevTime + 1; /* We used to check "timeout_ms > 0", but * this is more under/overflow-resistant * (people did see overflows here when system time jumps): */ } while ((unsigned)timeout_ms <= timeo); ret: close(s); log1("%srp reply received for this address", rv ? "no a" : "A"); return rv; }