void impl::muxer::mux(volatile const bool& terminate) { atf::utils::auto_array< struct pollfd > poll_fds(new struct pollfd[m_nfds]); for (size_t i = 0; i < m_nfds; i++) { poll_fds[i].fd = m_fds[i]; poll_fds[i].events = POLLIN; } size_t nactive = m_nfds; while (nactive > 0 && !terminate) { int ret; while (!terminate && (ret = safe_poll(poll_fds.get(), 2, 250)) == 0) {} for (size_t i = 0; !terminate && i < m_nfds; i++) { if (poll_fds[i].events == 0) continue; if (poll_fds[i].revents & POLLHUP) { // Any data still available at this point will be processed by // a call to the flush method. poll_fds[i].events = 0; INV(nactive >= 1); nactive--; } else if (poll_fds[i].revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { (void)read_one(i, poll_fds[i].fd, m_buffers[i], true); } } } }
void main() { char buf[1024]; puts("before open \n"); FILE *p=popen("top -b", "r"); struct pollfd pfd[1]; if(NULL != p) { int i =0; puts("normal open\n"); pfd[0].fd = fileno(p); pfd[0].events = POLLIN; int ret =safe_poll(pfd, 1, 5000); if( ret > 0) { puts("could read \n"); while(!feof(p)) { printf("read %d time\n",i); fread(buf, sizeof(buf), 1, p); puts(buf); i++; } } else if( ret == 0) { puts("timeout"); } } else { printf("rpwt\n"); } }
static ssize_t getch_nowait(char* input, int sz) { ssize_t rd; struct pollfd pfd[2]; pfd[0].fd = STDIN_FILENO; pfd[0].events = POLLIN; pfd[1].fd = kbd_fd; pfd[1].events = POLLIN; again: tcsetattr(kbd_fd, TCSANOW, &term_less); /* NB: select/poll returns whenever read will not block. Therefore: * if eof is reached, select/poll will return immediately * because read will immediately return 0 bytes. * Even if select/poll says that input is available, read CAN block * (switch fd into O_NONBLOCK'ed mode to avoid it) */ rd = 1; if (max_fline <= cur_fline + max_displayed_line && eof_error > 0 /* did NOT reach eof yet */ ) { /* We are interested in stdin */ rd = 0; } /* position cursor if line input is done */ if (less_gets_pos >= 0) move_cursor(max_displayed_line + 2, less_gets_pos + 1); fflush(stdout); safe_poll(pfd + rd, 2 - rd, -1); input[0] = '\0'; rd = safe_read(kbd_fd, input, sz); /* NB: kbd_fd is in O_NONBLOCK mode */ if (rd < 0 && errno == EAGAIN) { /* No keyboard input -> we have input on stdin! */ read_lines(); buffer_fill_and_print(); goto again; } set_tty_cooked(); return rd; }
static int wait_for_reply(len_and_sockaddr *from_lsa, struct sockaddr *to, unsigned *timestamp_us, int *left_ms) { struct pollfd pfd[1]; int read_len = 0; pfd[0].fd = rcvsock; pfd[0].events = POLLIN; if (*left_ms >= 0 && safe_poll(pfd, 1, *left_ms) > 0) { unsigned t; read_len = recv_from_to(rcvsock, recv_pkt, sizeof(recv_pkt), /*flags:*/ MSG_DONTWAIT, &from_lsa->u.sa, to, from_lsa->len); t = monotonic_us(); *left_ms -= (t - *timestamp_us) / 1000; *timestamp_us = t; } return read_len; }
//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; }
static int getch_nowait(void) { int rd; struct pollfd pfd[2]; pfd[0].fd = STDIN_FILENO; pfd[0].events = POLLIN; pfd[1].fd = kbd_fd; pfd[1].events = POLLIN; again: tcsetattr(kbd_fd, TCSANOW, &term_less); /* NB: select/poll returns whenever read will not block. Therefore: * if eof is reached, select/poll will return immediately * because read will immediately return 0 bytes. * Even if select/poll says that input is available, read CAN block * (switch fd into O_NONBLOCK'ed mode to avoid it) */ rd = 1; /* Are we interested in stdin? */ //TODO: reuse code for determining this if (!(option_mask32 & FLAG_S) ? !(max_fline > cur_fline + max_displayed_line) : !(max_fline >= cur_fline && max_lineno > LINENO(flines[cur_fline]) + max_displayed_line) ) { if (eof_error > 0) /* did NOT reach eof yet */ rd = 0; /* yes, we are interested in stdin */ } /* Position cursor if line input is done */ if (less_gets_pos >= 0) move_cursor(max_displayed_line + 2, less_gets_pos + 1); fflush_all(); if (kbd_input[0] == 0) { /* if nothing is buffered */ #if ENABLE_FEATURE_LESS_WINCH while (1) { int r; /* NB: SIGWINCH interrupts poll() */ r = poll(pfd + rd, 2 - rd, -1); if (/*r < 0 && errno == EINTR &&*/ winch_counter) return '\\'; /* anything which has no defined function */ if (r) break; } #else safe_poll(pfd + rd, 2 - rd, -1); #endif } /* We have kbd_fd in O_NONBLOCK mode, read inside read_key() * would not block even if there is no input available */ rd = read_key(kbd_fd, kbd_input, /*timeout off:*/ -2); if (rd == -1) { if (errno == EAGAIN) { /* No keyboard input available. Since poll() did return, * we should have input on stdin */ read_lines(); buffer_fill_and_print(); goto again; } /* EOF/error (ssh session got killed etc) */ less_exit(0); } set_tty_cooked(); return rd; }
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 }
static void NOINLINE retrieve_file_data(FILE *dfp) { #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT # if ENABLE_FEATURE_WGET_TIMEOUT unsigned second_cnt; # endif struct pollfd polldata; polldata.fd = fileno(dfp); polldata.events = POLLIN | POLLPRI; #endif progress_meter(PROGRESS_START); if (G.chunked) goto get_clen; /* Loops only if chunked */ while (1) { #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT /* Must use nonblocking I/O, otherwise fread will loop * and *block* until it reads full buffer, * which messes up progress bar and/or timeout logic. * Because of nonblocking I/O, we need to dance * very carefully around EAGAIN. See explanation at * clearerr() call. */ ndelay_on(polldata.fd); #endif while (1) { int n; unsigned rdsz; rdsz = sizeof(G.wget_buf); if (G.got_clen) { if (G.content_len < (off_t)sizeof(G.wget_buf)) { if ((int)G.content_len <= 0) break; rdsz = (unsigned)G.content_len; } } #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT # if ENABLE_FEATURE_WGET_TIMEOUT second_cnt = G.timeout_seconds; # endif while (1) { if (safe_poll(&polldata, 1, 1000) != 0) break; /* error, EOF, or data is available */ # if ENABLE_FEATURE_WGET_TIMEOUT if (second_cnt != 0 && --second_cnt == 0) { progress_meter(PROGRESS_END); bb_error_msg_and_die("download timed out"); } # endif /* Needed for "stalled" indicator */ progress_meter(PROGRESS_BUMP); } /* fread internally uses read loop, which in our case * is usually exited when we get EAGAIN. * In this case, libc sets error marker on the stream. * Need to clear it before next fread to avoid possible * rare false positive ferror below. Rare because usually * fread gets more than zero bytes, and we don't fall * into if (n <= 0) ... */ clearerr(dfp); errno = 0; #endif n = fread(G.wget_buf, 1, rdsz, dfp); /* man fread: * If error occurs, or EOF is reached, the return value * is a short item count (or zero). * fread does not distinguish between EOF and error. */ if (n <= 0) { #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT if (errno == EAGAIN) /* poll lied, there is no data? */ continue; /* yes */ #endif if (ferror(dfp)) bb_perror_msg_and_die(bb_msg_read_error); break; /* EOF, not error */ } xwrite(G.output_fd, G.wget_buf, n); #if ENABLE_FEATURE_WGET_STATUSBAR G.transferred += n; progress_meter(PROGRESS_BUMP); #endif if (G.got_clen) { G.content_len -= n; if (G.content_len == 0) break; } } #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT clearerr(dfp); ndelay_off(polldata.fd); /* else fgets can get very unhappy */ #endif if (!G.chunked) break; fgets_and_trim(dfp); /* Eat empty line */ get_clen: fgets_and_trim(dfp); G.content_len = STRTOOFF(G.wget_buf, NULL, 16); /* FIXME: error check? */ if (G.content_len == 0) break; /* all done! */ G.got_clen = 1; } /* If -c failed, we restart from the beginning, * but we do not truncate file then, we do it only now, at the end. * This lets user to ^C if his 99% complete 10 GB file download * failed to restart *without* losing the almost complete file. */ { off_t pos = lseek(G.output_fd, 0, SEEK_CUR); if (pos != (off_t)-1) ftruncate(G.output_fd, pos); } /* Draw full bar and free its resources */ G.chunked = 0; /* makes it show 100% even for chunked download */ G.got_clen = 1; /* makes it show 100% even for download of (formerly) unknown size */ progress_meter(PROGRESS_END); }
int arpping(uint32_t test_ip, uint32_t from_ip, uint8_t *from_mac, const char *interface) { 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; 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("cannot 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-fiiled */ /* target hardware address */ memcpy(arp.tInaddr, &test_ip, sizeof(test_ip)); /* 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 = 2000; do { int r; unsigned prevTime = monotonic_us(); pfd[0].events = POLLIN; r = safe_poll(pfd, 1, timeout_ms); if (r < 0) break; if (r) { r = read(s, &arp, sizeof(arp)); if (r < 0) break; 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 */ && *((uint32_t *) arp.sInaddr) == test_ip ) { rv = 0; break; } } timeout_ms -= (monotonic_us() - prevTime) / 1000; } while (timeout_ms > 0); ret: close(s); DEBUG("%srp reply received for this address", rv ? "No a" : "A"); return rv; }
int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout) { struct pollfd pfd; const char *seq; int n; /* Known escape sequences for cursor and function keys. * See "Xterm Control Sequences" * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html */ static const char esccmds[] ALIGN1 = { 'O','A' |0x80,KEYCODE_UP , 'O','B' |0x80,KEYCODE_DOWN , 'O','C' |0x80,KEYCODE_RIGHT , 'O','D' |0x80,KEYCODE_LEFT , 'O','H' |0x80,KEYCODE_HOME , 'O','F' |0x80,KEYCODE_END , #if 0 'O','P' |0x80,KEYCODE_FUN1 , /* [ESC] ESC O [2] P - [Alt-][Shift-]F1 */ /* ESC [ O 1 ; 2 P - Shift-F1 */ /* ESC [ O 1 ; 3 P - Alt-F1 */ /* ESC [ O 1 ; 4 P - Alt-Shift-F1 */ /* ESC [ O 1 ; 5 P - Ctrl-F1 */ /* ESC [ O 1 ; 6 P - Ctrl-Shift-F1 */ 'O','Q' |0x80,KEYCODE_FUN2 , 'O','R' |0x80,KEYCODE_FUN3 , 'O','S' |0x80,KEYCODE_FUN4 , #endif '[','A' |0x80,KEYCODE_UP , '[','B' |0x80,KEYCODE_DOWN , '[','C' |0x80,KEYCODE_RIGHT , '[','D' |0x80,KEYCODE_LEFT , /* ESC [ 1 ; 2 x, where x = A/B/C/D: Shift-<arrow> */ /* ESC [ 1 ; 3 x, where x = A/B/C/D: Alt-<arrow> - implemented below */ /* ESC [ 1 ; 4 x, where x = A/B/C/D: Alt-Shift-<arrow> */ /* ESC [ 1 ; 5 x, where x = A/B/C/D: Ctrl-<arrow> - implemented below */ /* ESC [ 1 ; 6 x, where x = A/B/C/D: Ctrl-Shift-<arrow> */ /* ESC [ 1 ; 7 x, where x = A/B/C/D: Ctrl-Alt-<arrow> */ /* ESC [ 1 ; 8 x, where x = A/B/C/D: Ctrl-Alt-Shift-<arrow> */ '[','H' |0x80,KEYCODE_HOME , /* xterm */ '[','F' |0x80,KEYCODE_END , /* xterm */ /* [ESC] ESC [ [2] H - [Alt-][Shift-]Home (End similarly?) */ /* '[','Z' |0x80,KEYCODE_SHIFT_TAB, */ '[','1','~' |0x80,KEYCODE_HOME , /* vt100? linux vt? or what? */ '[','2','~' |0x80,KEYCODE_INSERT , /* ESC [ 2 ; 3 ~ - Alt-Insert */ '[','3','~' |0x80,KEYCODE_DELETE , /* [ESC] ESC [ 3 [;2] ~ - [Alt-][Shift-]Delete */ /* ESC [ 3 ; 3 ~ - Alt-Delete */ /* ESC [ 3 ; 5 ~ - Ctrl-Delete */ '[','4','~' |0x80,KEYCODE_END , /* vt100? linux vt? or what? */ '[','5','~' |0x80,KEYCODE_PAGEUP , /* ESC [ 5 ; 3 ~ - Alt-PgUp */ /* ESC [ 5 ; 5 ~ - Ctrl-PgUp */ /* ESC [ 5 ; 7 ~ - Ctrl-Alt-PgUp */ '[','6','~' |0x80,KEYCODE_PAGEDOWN, '[','7','~' |0x80,KEYCODE_HOME , /* vt100? linux vt? or what? */ '[','8','~' |0x80,KEYCODE_END , /* vt100? linux vt? or what? */ #if 0 '[','1','1','~'|0x80,KEYCODE_FUN1 , /* old xterm, deprecated by ESC O P */ '[','1','2','~'|0x80,KEYCODE_FUN2 , /* old xterm... */ '[','1','3','~'|0x80,KEYCODE_FUN3 , /* old xterm... */ '[','1','4','~'|0x80,KEYCODE_FUN4 , /* old xterm... */ '[','1','5','~'|0x80,KEYCODE_FUN5 , /* [ESC] ESC [ 1 5 [;2] ~ - [Alt-][Shift-]F5 */ '[','1','7','~'|0x80,KEYCODE_FUN6 , '[','1','8','~'|0x80,KEYCODE_FUN7 , '[','1','9','~'|0x80,KEYCODE_FUN8 , '[','2','0','~'|0x80,KEYCODE_FUN9 , '[','2','1','~'|0x80,KEYCODE_FUN10 , '[','2','3','~'|0x80,KEYCODE_FUN11 , '[','2','4','~'|0x80,KEYCODE_FUN12 , /* ESC [ 2 4 ; 2 ~ - Shift-F12 */ /* ESC [ 2 4 ; 3 ~ - Alt-F12 */ /* ESC [ 2 4 ; 4 ~ - Alt-Shift-F12 */ /* ESC [ 2 4 ; 5 ~ - Ctrl-F12 */ /* ESC [ 2 4 ; 6 ~ - Ctrl-Shift-F12 */ #endif /* '[','1',';','5','A' |0x80,KEYCODE_CTRL_UP , - unugsed */ /* '[','1',';','5','B' |0x80,KEYCODE_CTRL_DOWN , - unugsed */ '[','1',';','5','C' |0x80,KEYCODE_CTRL_RIGHT, '[','1',';','5','D' |0x80,KEYCODE_CTRL_LEFT , /* '[','1',';','3','A' |0x80,KEYCODE_ALT_UP , - unugsed */ /* '[','1',';','3','B' |0x80,KEYCODE_ALT_DOWN , - unugsed */ '[','1',';','3','C' |0x80,KEYCODE_ALT_RIGHT, '[','1',';','3','D' |0x80,KEYCODE_ALT_LEFT , /* '[','3',';','3','~' |0x80,KEYCODE_ALT_DELETE, - unugsed */ 0 }; pfd.fd = fd; pfd.events = POLLIN; buffer++; /* saved chars counter is in buffer[-1] now */ start_over: errno = 0; n = (unsigned char)buffer[-1]; if (n == 0) { /* If no data, wait for input. * If requested, wait TIMEOUT ms. TIMEOUT = -1 is useful * if fd can be in non-blocking mode. */ if (timeout >= -1) { if (safe_poll(&pfd, 1, timeout) == 0) { /* Timed out */ errno = EAGAIN; return -1; } } /* It is tempting to read more than one byte here, * but it breaks pasting. Example: at shell prompt, * user presses "c","a","t" and then pastes "\nline\n". * When we were reading 3 bytes here, we were eating * "li" too, and cat was getting wrong input. */ n = safe_read(fd, buffer, 1); if (n <= 0) return -1; } { unsigned char c = buffer[0]; n--; if (n) memmove(buffer, buffer + 1, n); /* Only ESC starts ESC sequences */ if (c != 27) { buffer[-1] = n; return c; } } /* Loop through known ESC sequences */ seq = esccmds; while (*seq != '\0') { /* n - position in sequence we did not read yet */ int i = 0; /* position in sequence to compare */ /* Loop through chars in this sequence */ while (1) { /* So far escape sequence matched up to [i-1] */ if (n <= i) { /* Need more chars, read another one if it wouldn't block. * Note that escape sequences come in as a unit, * so if we block for long it's not really an escape sequence. * Timeout is needed to reconnect escape sequences * split up by transmission over a serial console. */ if (safe_poll(&pfd, 1, 50) == 0) { /* No more data! * Array is sorted from shortest to longest, * we can't match anything later in array - * anything later is longer than this seq. * Break out of both loops. */ goto got_all; } errno = 0; if (safe_read(fd, buffer + n, 1) <= 0) { /* If EAGAIN, then fd is O_NONBLOCK and poll lied: * in fact, there is no data. */ if (errno != EAGAIN) { /* otherwise: it's EOF/error */ buffer[-1] = 0; return -1; } goto got_all; } n++; } if (buffer[i] != (seq[i] & 0x7f)) { /* This seq doesn't match, go to next */ seq += i; /* Forward to last char */ while (!(*seq & 0x80)) seq++; /* Skip it and the keycode which follows */ seq += 2; break; } if (seq[i] & 0x80) { /* Entire seq matched */ n = 0; /* n -= i; memmove(...); * would be more correct, * but we never read ahead that much, * and n == i here. */ buffer[-1] = 0; return (signed char)seq[i+1]; } i++; } } /* We did not find matching sequence. * We possibly read and stored more input in buffer[] by now. * n = bytes read. Try to read more until we time out. */ while (n < KEYCODE_BUFFER_SIZE-1) { /* 1 for count byte at buffer[-1] */ if (safe_poll(&pfd, 1, 50) == 0) { /* No more data! */ break; } errno = 0; if (safe_read(fd, buffer + n, 1) <= 0) { /* If EAGAIN, then fd is O_NONBLOCK and poll lied: * in fact, there is no data. */ if (errno != EAGAIN) { /* otherwise: it's EOF/error */ buffer[-1] = 0; return -1; } break; } n++; /* Try to decipher "ESC [ NNN ; NNN R" sequence */ if ((ENABLE_FEATURE_EDITING_ASK_TERMINAL || ENABLE_FEATURE_VI_ASK_TERMINAL || ENABLE_FEATURE_LESS_ASK_TERMINAL ) && n >= 5 && buffer[0] == '[' && buffer[n-1] == 'R' && isdigit(buffer[1]) ) { char *end; unsigned long row, col; row = strtoul(buffer + 1, &end, 10); if (*end != ';' || !isdigit(end[1])) continue; col = strtoul(end + 1, &end, 10); if (*end != 'R') continue; if (row < 1 || col < 1 || (row | col) > 0x7fff) continue; buffer[-1] = 0; /* Pack into "1 <row15bits> <col16bits>" 32-bit sequence */ col |= (((-1 << 15) | row) << 16); /* Return it in high-order word */ return ((int64_t) col << 32) | (uint32_t)KEYCODE_CURSOR_POS; } } got_all: if (n <= 1) { /* Alt-x is usually returned as ESC x. * Report ESC, x is remembered for the next call. */ buffer[-1] = n; return 27; } /* We were doing "buffer[-1] = n; return c;" here, but this results * in unknown key sequences being interpreted as ESC + garbage. * This was not useful. Pretend there was no key pressed, * go and wait for a new keypress: */ buffer[-1] = 0; goto start_over; }
/* 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; }
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 microcom_main(int argc, char **argv) { struct pollfd pfd[2]; #define sfd (pfd[1].fd) char *device_lock_file = NULL; const char *s; const char *opt_s = "9600"; unsigned speed; int len; int exitcode = 1; struct termios tio0, tiosfd, tio; getopt32(argv, "s:", &opt_s); argc -= optind; argv += optind; if (!argv[0]) bb_show_usage(); speed = xatou(opt_s); // try to create lock file in /var/lock s = bb_basename(argv[0]); if (!s[0]) { errno = ENODEV; bb_perror_msg_and_die("can't lock device"); } device_lock_file = xasprintf("/var/lock/LCK..%s", s); sfd = open(device_lock_file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0644); if (sfd < 0) { if (ENABLE_FEATURE_CLEAN_UP) free(device_lock_file); device_lock_file = NULL; if (errno == EEXIST) bb_perror_msg_and_die("can't lock device"); // We don't abort on other errors: /var/lock can be // non-writable or non-existent } else { // %4d to make mgetty happy. It treats 4-bytes lock files as binary, // not text, PID. Making 5+ char file. Brrr... s = xasprintf("%4d\n", getpid()); write(sfd, s, strlen(s)); if (ENABLE_FEATURE_CLEAN_UP) free((char*)s); close(sfd); } // open device sfd = open(argv[0], O_RDWR); if (sfd < 0) { bb_perror_msg("can't open device"); goto unlock_and_exit; } // put stdin to "raw mode", handle one character at a time tcgetattr(STDIN_FILENO, &tio0); tio = tio0; tio.c_lflag &= ~(ICANON|ECHO); tio.c_iflag &= ~(IXON|ICRNL); tio.c_oflag &= ~(ONLCR); tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; if (tcsetattr(STDIN_FILENO, TCSANOW, &tio)) { bb_perror_msg("can't tcsetattr for %s", "stdin"); goto unlock_and_exit; } /* same thing for modem (plus: set baud rate) - TODO: make CLI option */ tcgetattr(sfd, &tiosfd); tio = tiosfd; tio.c_lflag &= ~(ICANON|ECHO); tio.c_iflag &= ~(IXON|ICRNL); tio.c_oflag &= ~(ONLCR); tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; cfsetispeed(&tio, tty_value_to_baud(speed)); cfsetospeed(&tio, tty_value_to_baud(speed)); if (tcsetattr(sfd, TCSANOW, &tio)) { bb_perror_msg("can't tcsetattr for %s", "device"); goto unlock_and_exit; } // disable SIGINT signal(SIGINT, SIG_IGN); // drain stdin tcflush(STDIN_FILENO, TCIFLUSH); printf("connected to '%s' (%d bps), exit with ctrl-X...\r\n", argv[0], speed); // main loop: check with poll(), then read/write bytes across pfd[0].fd = STDIN_FILENO; pfd[0].events = POLLIN; /*pfd[1].fd = sfd;*/ pfd[1].events = POLLIN; while (1) { int i; safe_poll(pfd, 2, -1); for (i = 0; i < 2; ++i) { if (pfd[i].revents & POLLIN) { len = read(pfd[i].fd, bb_common_bufsiz1, COMMON_BUFSIZE); if (len > 0) { if (!i && 24 == bb_common_bufsiz1[0]) goto done; // ^X exits write(pfd[1-i].fd, bb_common_bufsiz1, len); } } } } done: tcsetattr(sfd, TCSANOW, &tiosfd); tcsetattr(STDIN_FILENO, TCSANOW, &tio0); tcflush(STDIN_FILENO, TCIFLUSH); if (ENABLE_FEATURE_CLEAN_UP) close(sfd); exitcode = 0; unlock_and_exit: // delete lock file if (device_lock_file) { unlink(device_lock_file); if (ENABLE_FEATURE_CLEAN_UP) free(device_lock_file); } return exitcode; }
int microcom_main(int argc UNUSED_PARAM, char **argv) { int sfd; int nfd; struct pollfd pfd[2]; struct termios tio0, tiosfd, tio; char *device_lock_file; enum { OPT_X = 1 << 0, // do not respect Ctrl-X, Ctrl-@ OPT_s = 1 << 1, // baudrate OPT_d = 1 << 2, // wait for device response, ms OPT_t = 1 << 3, // timeout, ms }; speed_t speed = 9600; int delay = -1; int timeout = -1; unsigned opts; // fetch options opt_complementary = "=1:s+:d+:t+"; // exactly one arg, numeric options opts = getopt32(argv, "Xs:d:t:", &speed, &delay, &timeout); // argc -= optind; argv += optind; // try to create lock file in /var/lock device_lock_file = (char *)bb_basename(argv[0]); device_lock_file = xasprintf("/var/lock/LCK..%s", device_lock_file); sfd = open(device_lock_file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0644); if (sfd < 0) { // device already locked -> bail out if (errno == EEXIST) bb_perror_msg_and_die("can't create %s", device_lock_file); // can't create lock -> don't care if (ENABLE_FEATURE_CLEAN_UP) free(device_lock_file); device_lock_file = NULL; } else { // %4d to make concurrent mgetty (if any) happy. // Mgetty treats 4-bytes lock files as binary, // not text, PID. Making 5+ char file. Brrr... fdprintf(sfd, "%4d\n", getpid()); close(sfd); } // setup signals bb_signals(0 + (1 << SIGHUP) + (1 << SIGINT) + (1 << SIGTERM) + (1 << SIGPIPE) , signal_handler); // error exit code if we fail to open the device signalled = 1; // open device sfd = open_or_warn(argv[0], O_RDWR | O_NOCTTY | O_NONBLOCK); if (sfd < 0) goto done; fcntl(sfd, F_SETFL, 0); // put device to "raw mode" xget1(sfd, &tio, &tiosfd); // set device speed cfsetspeed(&tio, tty_value_to_baud(speed)); if (xset1(sfd, &tio, argv[0])) goto done; // put stdin to "raw mode" (if stdin is a TTY), // handle one character at a time if (isatty(STDIN_FILENO)) { xget1(STDIN_FILENO, &tio, &tio0); if (xset1(STDIN_FILENO, &tio, "stdin")) goto done; } // main loop: check with poll(), then read/write bytes across pfd[0].fd = sfd; pfd[0].events = POLLIN; pfd[1].fd = STDIN_FILENO; pfd[1].events = POLLIN; signalled = 0; nfd = 2; while (!signalled && safe_poll(pfd, nfd, timeout) > 0) { if (nfd > 1 && pfd[1].revents) { char c; // read from stdin -> write to device if (safe_read(STDIN_FILENO, &c, 1) < 1) { // don't poll stdin anymore if we got EOF/error nfd--; goto skip_write; } // do we need special processing? if (!(opts & OPT_X)) { // ^@ sends Break if (VINTR == c) { tcsendbreak(sfd, 0); goto skip_write; } // ^X exits if (24 == c) break; } write(sfd, &c, 1); if (delay >= 0) safe_poll(pfd, 1, delay); skip_write: ; } if (pfd[0].revents) { #define iobuf bb_common_bufsiz1 ssize_t len; // read from device -> write to stdout len = safe_read(sfd, iobuf, sizeof(iobuf)); if (len > 0) full_write(STDOUT_FILENO, iobuf, len); else { // EOF/error -> bail out signalled = SIGHUP; break; } } } // restore device mode tcsetattr(sfd, TCSAFLUSH, &tiosfd); if (isatty(STDIN_FILENO)) tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0); done: if (device_lock_file) unlink(device_lock_file); return signalled; }
static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd) { char buf[512]; #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT # if ENABLE_FEATURE_WGET_TIMEOUT unsigned second_cnt; # endif struct pollfd polldata; polldata.fd = fileno(dfp); polldata.events = POLLIN | POLLPRI; ndelay_on(polldata.fd); #endif progress_meter(PROGRESS_START); if (G.chunked) goto get_clen; /* Loops only if chunked */ while (1) { while (1) { int n; unsigned rdsz; rdsz = sizeof(buf); if (G.got_clen) { if (G.content_len < (off_t)sizeof(buf)) { if ((int)G.content_len <= 0) break; rdsz = (unsigned)G.content_len; } } #if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT # if ENABLE_FEATURE_WGET_TIMEOUT second_cnt = G.timeout_seconds; # endif while (1) { if (safe_poll(&polldata, 1, 1000) != 0) break; /* error, EOF, or data is available */ # if ENABLE_FEATURE_WGET_TIMEOUT if (second_cnt != 0 && --second_cnt == 0) { progress_meter(PROGRESS_END); bb_perror_msg_and_die("download timed out"); } # endif /* Needed for "stalled" indicator */ progress_meter(PROGRESS_BUMP); } #endif n = safe_fread(buf, rdsz, dfp); if (n <= 0) { if (ferror(dfp)) { /* perror will not work: ferror doesn't set errno */ bb_error_msg_and_die(bb_msg_read_error); } break; } xwrite(output_fd, buf, n); #if ENABLE_FEATURE_WGET_STATUSBAR G.transferred += n; progress_meter(PROGRESS_BUMP); #endif if (G.got_clen) G.content_len -= n; } if (!G.chunked) break; safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */ get_clen: safe_fgets(buf, sizeof(buf), dfp); G.content_len = STRTOOFF(buf, NULL, 16); /* FIXME: error check? */ if (G.content_len == 0) break; /* all done! */ G.got_clen = 1; } progress_meter(PROGRESS_END); }
int32_t FAST_FUNC read_key(int fd, char *buffer, int timeout) { struct pollfd pfd; int n; int count; unsigned char c; pfd.fd = fd; pfd.events = POLLIN; buffer++; /* saved chars counter is in buffer[-1] now */ errno = 0; n = (unsigned char)buffer[-1]; if (n == 0) { /* If no data, wait for input. * If requested, wait TIMEOUT ms. TIMEOUT = -1 is useful * if fd can be in non-blocking mode. */ /* On GNO, skip the poll if timeout == -1. * It's pointless, since non-blocking mode isn't implemented, * and it seems to hang in certain cases (e.g. where another * process was interrupted while reading from the terminal). * This may be due to bugs in the underlying select() implementation. */ #ifndef __GNO__ if (timeout >= -1) #else if (timeout >= 0) #endif { if (safe_poll(&pfd, 1, timeout) == 0) { /* Timed out */ errno = EAGAIN; return -1; } } /* It is tempting to read more than one byte here, * but it breaks pasting. Example: at shell prompt, * user presses "c","a","t" and then pastes "\nline\n". * When we were reading 3 bytes here, we were eating * "li" too, and cat was getting wrong input. */ n = safe_read(fd, buffer, 1); if (n <= 0) return -1; } /* ASCII printable chars presumably don't start escape sequences */ c = buffer[0]; if (c >= ' ' && c <= '~') { goto return_c; } /* Loop through known escape sequences */ for (count = 0; count < n_escape_seqs; count++) { struct escape_seq *escseq = &escape_seqs[count]; /* n - position in sequence we did not read yet */ int i = 0; /* position in sequence to compare */ while (1) { /* So far escape sequence matched up to [i-1] */ if (escseq->seq[i] == 0) { /* Entire seq matched */ n = 0; /* n -= i; memmove(...); * would be more correct, * but we never read ahead that much, * and n == i here. */ buffer[-1] = 0; return escseq->keycode; } if (n <= i) { /* Need more chars, read another one if it wouldn't block. * Note that escape sequences come in as a unit, * so if we block for long it's not really an escape sequence. * Timeout is needed to reconnect escape sequences * split up by transmission over a serial console. */ if (safe_poll(&pfd, 1, 50) == 0) { /* No more data! * Can't match this sequence. Keep looping in case * a later one is shorter. */ goto next_seq; } errno = 0; if (safe_read(fd, buffer + n, 1) <= 0) { /* If EAGAIN, then fd is O_NONBLOCK and poll lied: * in fact, there is no data. */ if (errno != EAGAIN) { /* otherwise: it's EOF/error */ buffer[-1] = 0; return -1; } goto got_all; } n++; } if (buffer[i] != (escseq->seq[i] & 0x7f)) { /* This seq doesn't match, go to next */ goto next_seq; } i++; } next_seq: ; } /* We did not find matching sequence. * We possibly read and stored more input in buffer[] by now. * n = bytes read. */ got_all: if (n <= 0) { buffer[-1] = 0; return -1; } /* Returning miscellaneous characters may result in unknown * escape sequences being interpreted as ESC + garbage, but * it's difficult to know what to filter out, so we'll * just return everything for now. */ c = buffer[0]; return_c: n--; if (n) memmove(buffer, buffer + 1, n); buffer[-1] = n; return c; }
static int retrieve_file_data(struct globals *state, FILE *dfp, int (*progress)(void *data, int current, int total), int (*output_func)(void *data, char *bytes, int len), void *data) { char buf[4*1024]; /* made bigger to speed up local xfers */ unsigned second_cnt; struct pollfd polldata; polldata.fd = fileno(dfp); polldata.events = POLLIN | POLLPRI; progress(data, 0, state->total_len); if (state->chunked) goto get_clen; /* Loops only if chunked */ while (1) { ndelay_on(polldata.fd); while (1) { int n; unsigned rdsz; rdsz = sizeof(buf); if (state->got_clen) { if (state->content_len < (off_t)sizeof(buf)) { if ((int)state->content_len <= 0) break; rdsz = (unsigned)state->content_len; } } second_cnt = state->timeout_seconds; while (1) { if (safe_poll(&polldata, 1, 1000) != 0) break; /* error, EOF, or data is available */ if (second_cnt != 0 && --second_cnt == 0) { progress(data, -1, state->total_len); ERROR("download timed out"); return -1; } /* Needed for "stalled" indicator */ progress(data, state->transferred, state->total_len); } /* fread internally uses read loop, which in our case * is usually exited when we get EAGAIN. * In this case, libc sets error marker on the stream. * Need to clear it before next fread to avoid possible * rare false positive ferror below. Rare because usually * fread gets more than zero bytes, and we don't fall * into if (n <= 0) ... */ clearerr(dfp); errno = 0; n = safe_fread(buf, rdsz, dfp); /* man fread: * If error occurs, or EOF is reached, the return value * is a short item count (or zero). * fread does not distinguish between EOF and error. */ if (n <= 0) { if (errno == EAGAIN) /* poll lied, there is no data? */ continue; /* yes */ if (ferror(dfp)) ERROR("Could not read file"); break; /* EOF, not error */ } output_func(data, buf, n); state->transferred += n; progress(data, state->transferred, state->total_len); if (state->got_clen) { state->content_len -= n; if (state->content_len == 0) break; } } ndelay_off(polldata.fd); if (!state->chunked) break; safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */ get_clen: safe_fgets(buf, sizeof(buf), dfp); state->content_len = strtol(buf, NULL, 16); /* FIXME: error check? */ if (state->content_len == 0) break; /* all done! */ state->got_clen = 1; } progress(data, state->transferred, state->total_len); return 0; }
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; }