int acpid_main(int argc UNUSED_PARAM, char **argv) { int nfd; int opts; struct pollfd *pfd; const char *opt_dir = "/etc/acpi"; const char *opt_input = "/dev/input/event"; const char *opt_logfile = "/var/log/acpid.log"; const char *opt_action = "/etc/acpid.conf"; const char *opt_map = "/etc/acpi.map"; #if ENABLE_FEATURE_PIDFILE const char *opt_pidfile = "/var/run/acpid.pid"; #endif INIT_G(); opt_complementary = "df:e--e"; opts = getopt32(argv, "c:de:fl:a:M:" IF_FEATURE_PIDFILE("p:") IF_FEATURE_ACPID_COMPAT("g:m:s:S:v"), &opt_dir, &opt_input, &opt_logfile, &opt_action, &opt_map IF_FEATURE_PIDFILE(, &opt_pidfile) IF_FEATURE_ACPID_COMPAT(, NULL, NULL, NULL, NULL) ); if (!(opts & OPT_f)) { /* No -f "Foreground", we go to background */ bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); } if (!(opts & OPT_d)) { /* No -d "Debug", we log to log file. * This includes any output from children. */ xmove_fd(xopen(opt_logfile, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO); xdup2(STDOUT_FILENO, STDERR_FILENO); /* Also, acpid's messages (but not children) will go to syslog too */ openlog(applet_name, LOG_PID, LOG_DAEMON); logmode = LOGMODE_SYSLOG | LOGMODE_STDIO; } /* else: -d "Debug", log is not redirected */ parse_conf_file(opt_action); parse_map_file(opt_map); xchdir(opt_dir); bb_signals((1 << SIGCHLD), SIG_IGN); bb_signals(BB_FATAL_SIGS, record_signo); pfd = NULL; nfd = 0; while (1) { int fd; char *dev_event; dev_event = xasprintf((opts & OPT_e) ? "%s" : "%s%u", opt_input, nfd); fd = open(dev_event, O_RDONLY | O_NONBLOCK); if (fd < 0) { if (nfd == 0) bb_simple_perror_msg_and_die(dev_event); break; } free(dev_event); pfd = xrealloc_vector(pfd, 1, nfd); pfd[nfd].fd = fd; pfd[nfd].events = POLLIN; nfd++; } write_pidfile(opt_pidfile); while (safe_poll(pfd, nfd, -1) > 0) { int i; for (i = 0; i < nfd; i++) { const char *event; if (!(pfd[i].revents & POLLIN)) { if (pfd[i].revents == 0) continue; /* this fd has nothing */ /* Likely POLLERR, POLLHUP, POLLNVAL. * Do not listen on this fd anymore. */ close(pfd[i].fd); nfd--; for (; i < nfd; i++) pfd[i].fd = pfd[i + 1].fd; break; /* do poll() again */ } event = NULL; if (option_mask32 & OPT_e) { char *buf; int len; buf = xmalloc_reads(pfd[i].fd, NULL); /* buf = "button/power PWRB 00000080 00000000" */ len = strlen(buf) - 9; if (len >= 0) buf[len] = '\0'; event = find_action(NULL, buf); free(buf); } else { struct input_event ev; if (sizeof(ev) != full_read(pfd[i].fd, &ev, sizeof(ev))) continue; if (ev.value != 1 && ev.value != 0) continue; event = find_action(&ev, NULL); } if (!event) continue; // spawn event handler process_event(event); } } if (ENABLE_FEATURE_CLEAN_UP) { while (nfd--) close(pfd[nfd].fd); free(pfd); } remove_pidfile(opt_pidfile); return EXIT_SUCCESS; }
int 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 makemime_main(int argc UNUSED_PARAM, char **argv) { llist_t *opt_headers = NULL, *l; const char *opt_output; #define boundary opt_output enum { OPT_c = 1 << 0, // Content-Type: OPT_e = 1 << 1, // Content-Transfer-Encoding. Ignored. Assumed base64 OPT_o = 1 << 2, // output to OPT_C = 1 << 3, // charset OPT_N = 1 << 4, // COMPAT OPT_a = 1 << 5, // additional headers OPT_m = 1 << 6, // COMPAT OPT_j = 1 << 7, // COMPAT }; INIT_G(); // parse options opt_complementary = "a::"; opts = getopt32(argv, "c:e:o:C:N:a:m:j:", &G.content_type, NULL, &opt_output, &G.opt_charset, NULL, &opt_headers, NULL, NULL ); //argc -= optind; argv += optind; // respect -o output if (opts & OPT_o) freopen(opt_output, "w", stdout); // no files given on command line? -> use stdin if (!*argv) *--argv = (char *)"-"; // put additional headers for (l = opt_headers; l; l = l->link) puts(l->data); // make a random string -- it will delimit message parts srand(monotonic_us()); boundary = xasprintf("%d-%d-%d", rand(), rand(), rand()); // put multipart header printf( "Mime-Version: 1.0\n" "Content-Type: multipart/mixed; boundary=\"%s\"\n" , boundary ); // put attachments while (*argv) { printf( "\n--%s\n" "Content-Type: %s; charset=%s\n" "Content-Disposition: inline; filename=\"%s\"\n" "Content-Transfer-Encoding: base64\n" , boundary , G.content_type , G.opt_charset , bb_get_last_path_component_strip(*argv) ); encode_base64(*argv++, (const char *)stdin, ""); } // put multipart footer printf("\n--%s--\n" "\n", boundary); return EXIT_SUCCESS; #undef boundary }
int zcip_main(int argc UNUSED_PARAM, char **argv) { int state; char *r_opt; const char *l_opt = "169.254.0.0"; 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; int verbose; } 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 ) #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 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"); } } 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)); t += getpid(); srand(t); } if (ip.s_addr == 0) ip.s_addr = pick_nip(); // 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 // - 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 return 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)); timeout_ms = PROBE_MIN * 1000; timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN); arp(/* ARPOP_REQUEST, */ /* ð_addr, */ null_ip, &null_addr, ip); } else { // Switch to announce state. state = ANNOUNCE; nclaims = 0; VDBG("announce/%u %s@%s\n", nclaims, argv_intf, inet_ntoa(ip)); timeout_ms = ANNOUNCE_INTERVAL * 1000; arp(/* ARPOP_REQUEST, */ /* ð_addr, */ ip, ð_addr, ip); } 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)); timeout_ms = ANNOUNCE_INTERVAL * 1000; arp(/* ARPOP_REQUEST, */ /* ð_addr, */ ip, ð_addr, ip); 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)); timeout_ms = ANNOUNCE_INTERVAL * 1000; arp(/* ARPOP_REQUEST, */ /* ð_addr, */ ip, ð_addr, ip); } 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) return 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_nip(); 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); } 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; #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_sha, ð_addr, ETH_ALEN) != 0) { if (memcmp(p.arp.arp_spa, &ip.s_addr, sizeof(struct in_addr)) == 0) { /* A probe or reply with source_ip == chosen ip */ source_ip_conflict = 1; } if (p.arp.arp_op == htons(ARPOP_REQUEST) && memcmp(p.arp.arp_spa, &null_ip, sizeof(struct in_addr)) == 0 && memcmp(p.arp.arp_tpa, &ip.s_addr, sizeof(struct in_addr)) == 0 ) { /* A probe with source_ip == 0.0.0.0, target_ip == chosen ip: * another host trying to claim this ip! */ 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_nip(); 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_nip(); 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_nip(); timeout_ms = 0; nprobes = 0; nclaims = 0; break; } // switch state break; // case 1 (packets arriving) } // switch poll } // while (1) #undef argv_intf }
int slattach_main(int argc ATTRIBUTE_UNUSED, char **argv) { /* Line discipline code table */ static const char proto_names[] ALIGN1 = "slip\0" /* 0 */ "cslip\0" /* 1 */ "slip6\0" /* 2 */ "cslip6\0" /* 3 */ "adaptive\0" /* 8 */ ; int i, encap, opt; struct termios state; const char *proto = "cslip"; const char *extcmd; /* Command to execute after hangup */ const char *baud_str; int baud_code = -1; /* Line baud rate (system code) */ enum { OPT_p_proto = 1 << 0, OPT_s_baud = 1 << 1, OPT_c_extcmd = 1 << 2, OPT_e_quit = 1 << 3, OPT_h_watch = 1 << 4, OPT_m_nonraw = 1 << 5, OPT_L_local = 1 << 6, OPT_F_noflow = 1 << 7 }; INIT_G(); /* Parse command line options */ opt = getopt32(argv, "p:s:c:ehmLF", &proto, &baud_str, &extcmd); /*argc -= optind;*/ argv += optind; if (!*argv) bb_show_usage(); encap = index_in_strings(proto_names, proto); if (encap < 0) invarg(proto, "protocol"); if (encap > 3) encap = 8; /* We want to know if the baud rate is valid before we start touching the ttys */ if (opt & OPT_s_baud) { baud_code = tty_value_to_baud(xatoi(baud_str)); if (baud_code < 0) invarg(baud_str, "baud rate"); } /* Trap signals in order to restore tty states upon exit */ if (!(opt & OPT_e_quit)) { bb_signals(0 + (1 << SIGHUP) + (1 << SIGINT) + (1 << SIGQUIT) + (1 << SIGTERM) , sig_handler); } /* Open tty */ handle = open(*argv, O_RDWR | O_NDELAY); if (handle < 0) { char *buf = concat_path_file("/dev", *argv); handle = xopen(buf, O_RDWR | O_NDELAY); /* maybe if (ENABLE_FEATURE_CLEAN_UP) ?? */ free(buf); } /* Save current tty state */ save_state(); /* Configure tty */ memcpy(&state, &saved_state, sizeof(state)); if (!(opt & OPT_m_nonraw)) { /* raw not suppressed */ memset(&state.c_cc, 0, sizeof(state.c_cc)); state.c_cc[VMIN] = 1; state.c_iflag = IGNBRK | IGNPAR; state.c_oflag = 0; state.c_lflag = 0; state.c_cflag = CS8 | HUPCL | CREAD | ((opt & OPT_L_local) ? CLOCAL : 0) | ((opt & OPT_F_noflow) ? 0 : CRTSCTS); } if (opt & OPT_s_baud) { cfsetispeed(&state, baud_code); cfsetospeed(&state, baud_code); } set_state(&state, encap); /* Exit now if option -e was passed */ if (opt & OPT_e_quit) return 0; /* If we're not requested to watch, just keep descriptor open * until we are killed */ if (!(opt & OPT_h_watch)) while (1) sleep(24*60*60); /* Watch line for hangup */ while (1) { if (ioctl(handle, TIOCMGET, &i) < 0 || !(i & TIOCM_CAR)) goto no_carrier; sleep(15); } no_carrier: /* Execute command on hangup */ if (opt & OPT_c_extcmd) system(extcmd); /* Restore states and exit */ restore_state_and_exit(EXIT_SUCCESS); }
int telnet_main(int argc, char **argv) { char *host; int port; int len; #ifdef USE_POLL struct pollfd ufds[2]; #else fd_set readfds; int maxfd; #endif INIT_G(); #if ENABLE_FEATURE_AUTOWIDTH get_terminal_width_height(0, &G.win_width, &G.win_height); #endif #if ENABLE_FEATURE_TELNET_TTYPE G.ttype = getenv("TERM"); #endif if (tcgetattr(0, &G.termios_def) >= 0) { G.do_termios = 1; G.termios_raw = G.termios_def; cfmakeraw(&G.termios_raw); } if (argc < 2) bb_show_usage(); #if ENABLE_FEATURE_TELNET_AUTOLOGIN if (1 & getopt32(argv, "al:", &G.autologin)) G.autologin = getenv("USER"); argv += optind; #else argv++; #endif if (!*argv) bb_show_usage(); host = *argv++; port = bb_lookup_port(*argv ? *argv++ : "telnet", "tcp", 23); if (*argv) /* extra params?? */ bb_show_usage(); G.netfd = create_and_connect_stream_or_die(host, port); setsockopt(G.netfd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1)); signal(SIGINT, fgotsig); #ifdef USE_POLL ufds[0].fd = 0; ufds[1].fd = G.netfd; ufds[0].events = ufds[1].events = POLLIN; #else FD_ZERO(&readfds); FD_SET(STDIN_FILENO, &readfds); FD_SET(G.netfd, &readfds); maxfd = G.netfd + 1; #endif while (1) { #ifndef USE_POLL fd_set rfds = readfds; switch (select(maxfd, &rfds, NULL, NULL, NULL)) #else switch (poll(ufds, 2, -1)) #endif { case 0: /* timeout */ case -1: /* error, ignore and/or log something, bay go to loop */ if (G.gotsig) conescape(); else sleep(1); break; default: #ifdef USE_POLL if (ufds[0].revents) /* well, should check POLLIN, but ... */ #else if (FD_ISSET(STDIN_FILENO, &rfds)) #endif { len = read(STDIN_FILENO, G.buf, DATABUFSIZE); if (len <= 0) doexit(EXIT_SUCCESS); TRACE(0, ("Read con: %d\n", len)); handlenetoutput(len); } #ifdef USE_POLL if (ufds[1].revents) /* well, should check POLLIN, but ... */ #else if (FD_ISSET(G.netfd, &rfds)) #endif { len = read(G.netfd, G.buf, DATABUFSIZE); if (len <= 0) { write_str(1, "Connection closed by foreign host\r\n"); doexit(EXIT_FAILURE); } TRACE(0, ("Read netfd (%d): %d\n", G.netfd, len)); handlenetinput(len); } } } }
int sed_main(int argc UNUSED_PARAM, char **argv) { unsigned opt; llist_t *opt_e, *opt_f; char *opt_i; #if ENABLE_LONG_OPTS static const char sed_longopts[] ALIGN1 = /* name has_arg short */ "in-place\0" Optional_argument "i" "regexp-extended\0" No_argument "r" "quiet\0" No_argument "n" "silent\0" No_argument "n" "expression\0" Required_argument "e" "file\0" Required_argument "f"; #endif INIT_G(); /* destroy command strings on exit */ if (ENABLE_FEATURE_CLEAN_UP) atexit(sed_free_and_close_stuff); /* Lie to autoconf when it starts asking stupid questions. */ if (argv[1] && strcmp(argv[1], "--version") == 0) { puts("This is not GNU sed version 4.0"); return 0; } /* do normal option parsing */ opt_e = opt_f = NULL; opt_i = NULL; /* -i must be first, to match OPT_in_place definition */ /* -E is a synonym of -r: * GNU sed 4.2.1 mentions it in neither --help * nor manpage, but does recognize it. */ opt = getopt32long(argv, "^" "i::rEne:*f:*" "\0" "nn"/*count -n*/, sed_longopts, &opt_i, &opt_e, &opt_f, &G.be_quiet); /* counter for -n */ //argc -= optind; argv += optind; if (opt & OPT_in_place) { // -i die_func = cleanup_outname; } if (opt & (2|4)) G.regex_type |= REG_EXTENDED; // -r or -E //if (opt & 8) // G.be_quiet++; // -n (implemented with a counter instead) while (opt_e) { // -e add_cmd_block(llist_pop(&opt_e)); } while (opt_f) { // -f char *line; FILE *cmdfile; cmdfile = xfopen_stdin(llist_pop(&opt_f)); while ((line = xmalloc_fgetline(cmdfile)) != NULL) { add_cmd(line); free(line); } fclose_if_not_stdin(cmdfile); } /* if we didn't get a pattern from -e or -f, use argv[0] */ if (!(opt & 0x30)) { if (!*argv) bb_show_usage(); add_cmd_block(*argv++); } /* Flush any unfinished commands. */ add_cmd(""); /* By default, we write to stdout */ G.nonstdout = stdout; /* argv[0..(argc-1)] should be names of file to process. If no * files were specified or '-' was specified, take input from stdin. * Otherwise, we process all the files specified. */ G.input_file_list = argv; if (!argv[0]) { if (opt & OPT_in_place) bb_error_msg_and_die(bb_msg_requires_arg, "-i"); argv[0] = (char*)bb_msg_standard_input; /* G.last_input_file = 0; - already is */ } else { goto start; for (; *argv; argv++) { struct stat statbuf; int nonstdoutfd; sed_cmd_t *sed_cmd; G.last_input_file++; start: if (!(opt & OPT_in_place)) { if (LONE_DASH(*argv)) { *argv = (char*)bb_msg_standard_input; process_files(); } continue; } /* -i: process each FILE separately: */ if (stat(*argv, &statbuf) != 0) { bb_simple_perror_msg(*argv); G.exitcode = EXIT_FAILURE; G.current_input_file++; continue; } G.outname = xasprintf("%sXXXXXX", *argv); nonstdoutfd = xmkstemp(G.outname); G.nonstdout = xfdopen_for_write(nonstdoutfd); /* Set permissions/owner of output file */ /* chmod'ing AFTER chown would preserve suid/sgid bits, * but GNU sed 4.2.1 does not preserve them either */ fchmod(nonstdoutfd, statbuf.st_mode); fchown(nonstdoutfd, statbuf.st_uid, statbuf.st_gid); process_files(); fclose(G.nonstdout); G.nonstdout = stdout; if (opt_i) { char *backupname = xasprintf("%s%s", *argv, opt_i); xrename(*argv, backupname); free(backupname); } /* else unlink(*argv); - rename below does this */ xrename(G.outname, *argv); //TODO: rollback backup on error? free(G.outname); G.outname = NULL; /* Fix disabled range matches and mangled ",+N" ranges */ for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) { sed_cmd->beg_line = sed_cmd->beg_line_orig; sed_cmd->end_line = sed_cmd->end_line_orig; } } /* Here, to handle "sed 'cmds' nonexistent_file" case we did: * if (G.current_input_file[G.current_input_file] == NULL) * return G.exitcode; * but it's not needed since process_files() works correctly * in this case too. */ } process_files(); return G.exitcode; }
int sed_main(int argc ATTRIBUTE_UNUSED, char **argv) { enum { OPT_in_place = 1 << 0, }; unsigned opt; llist_t *opt_e, *opt_f; int status = EXIT_SUCCESS; INIT_G(); /* destroy command strings on exit */ if (ENABLE_FEATURE_CLEAN_UP) atexit(sed_free_and_close_stuff); /* Lie to autoconf when it starts asking stupid questions. */ if (argv[1] && !strcmp(argv[1], "--version")) { puts("This is not GNU sed version 4.0"); return 0; } /* do normal option parsing */ opt_e = opt_f = NULL; opt_complementary = "e::f::" /* can occur multiple times */ "nn"; /* count -n */ opt = getopt32(argv, "irne:f:", &opt_e, &opt_f, &G.be_quiet); /* counter for -n */ //argc -= optind; argv += optind; if (opt & OPT_in_place) { // -i atexit(cleanup_outname); } if (opt & 0x2) G.regex_type |= REG_EXTENDED; // -r //if (opt & 0x4) G.be_quiet++; // -n while (opt_e) { // -e add_cmd_block(llist_pop(&opt_e)); } while (opt_f) { // -f char *line; FILE *cmdfile; cmdfile = xfopen(llist_pop(&opt_f), "r"); while ((line = xmalloc_fgetline(cmdfile)) != NULL) { add_cmd(line); free(line); } fclose(cmdfile); } /* if we didn't get a pattern from -e or -f, use argv[0] */ if (!(opt & 0x18)) { if (!*argv) bb_show_usage(); add_cmd_block(*argv++); } /* Flush any unfinished commands. */ add_cmd(""); /* By default, we write to stdout */ G.nonstdout = stdout; /* argv[0..(argc-1)] should be names of file to process. If no * files were specified or '-' was specified, take input from stdin. * Otherwise, we process all the files specified. */ if (argv[0] == NULL) { if (opt & OPT_in_place) bb_error_msg_and_die(bb_msg_requires_arg, "-i"); add_input_file(stdin); process_files(); } else { int i; FILE *file; for (i = 0; argv[i]; i++) { struct stat statbuf; int nonstdoutfd; if (LONE_DASH(argv[i]) && !(opt & OPT_in_place)) { add_input_file(stdin); process_files(); continue; } file = fopen_or_warn(argv[i], "r"); if (!file) { status = EXIT_FAILURE; continue; } if (!(opt & OPT_in_place)) { add_input_file(file); continue; } G.outname = xasprintf("%sXXXXXX", argv[i]); nonstdoutfd = mkstemp(G.outname); if (-1 == nonstdoutfd) bb_perror_msg_and_die("cannot create temp file %s", G.outname); G.nonstdout = fdopen(nonstdoutfd, "w"); /* Set permissions of output file */ fstat(fileno(file), &statbuf); fchmod(nonstdoutfd, statbuf.st_mode); add_input_file(file); process_files(); fclose(G.nonstdout); G.nonstdout = stdout; /* unlink(argv[i]); */ xrename(G.outname, argv[i]); free(G.outname); G.outname = NULL; } if (G.input_file_count > G.current_input_file) process_files(); } return status; }
int sendmail_main(int argc UNUSED_PARAM, char **argv) { char *opt_connect = opt_connect; char *opt_from; char *s; llist_t *list = NULL; char *domain = sane_address(safe_getdomainname()); int code; enum { //--- standard options OPT_t = 1 << 0, // read message for recipients, append them to those on cmdline OPT_f = 1 << 1, // sender address OPT_o = 1 << 2, // various options. -oi IMPLIED! others are IGNORED! //--- BB specific options OPT_w = 1 << 3, // network timeout OPT_H = 1 << 4, // use external connection helper OPT_S = 1 << 5, // specify connection string OPT_a = 1 << 6, // authentication tokens }; // init global variables INIT_G(); // save initial stdin since body is piped! xdup2(STDIN_FILENO, 3); G.fp0 = fdopen(3, "r"); // parse options // -f is required. -H and -S are mutually exclusive opt_complementary = "f:w+:H--S:S--H:a::"; // N.B. since -H and -S are mutually exclusive they do not interfere in opt_connect // -a is for ssmtp (http://downloads.openwrt.org/people/nico/man/man8/ssmtp.8.html) compatibility, // it is still under development. opts = getopt32(argv, "tf:o:w:H:S:a::", &opt_from, NULL, &timeout, &opt_connect, &opt_connect, &list); //argc -= optind; argv += optind; // process -a[upm]<token> options if ((opts & OPT_a) && !list) bb_show_usage(); while (list) { char *a = (char *) llist_pop(&list); if ('u' == a[0]) G.user = xstrdup(a+1); if ('p' == a[0]) G.pass = xstrdup(a+1); // N.B. we support only AUTH LOGIN so far //if ('m' == a[0]) // G.method = xstrdup(a+1); } // N.B. list == NULL here //bb_info_msg("OPT[%x] AU[%s], AP[%s], AM[%s], ARGV[%s]", opts, au, ap, am, *argv); // connect to server // connection helper ordered? -> if (opts & OPT_H) { const char *args[] = { "sh", "-c", opt_connect, NULL }; // plug it in launch_helper(args); // vanilla connection } else { int fd; // host[:port] not explicitly specified? -> use $SMTPHOST // no $SMTPHOST ? -> use localhost if (!(opts & OPT_S)) { opt_connect = getenv("SMTPHOST"); if (!opt_connect) opt_connect = (char *)"127.0.0.1"; } // do connect fd = create_and_connect_stream_or_die(opt_connect, 25); // and make ourselves a simple IO filter xmove_fd(fd, STDIN_FILENO); xdup2(STDIN_FILENO, STDOUT_FILENO); } // N.B. from now we know nothing about network :) // wait for initial server OK // N.B. if we used openssl the initial 220 answer is already swallowed during openssl TLS init procedure // so we need to kick the server to see whether we are ok code = smtp_check("NOOP", -1); // 220 on plain connection, 250 on openssl-helped TLS session if (220 == code) smtp_check(NULL, 250); // reread the code to stay in sync else if (250 != code) bb_error_msg_and_die("INIT failed"); // we should start with modern EHLO if (250 != smtp_checkp("EHLO %s", domain, -1)) { smtp_checkp("HELO %s", domain, 250); } if (ENABLE_FEATURE_CLEAN_UP) free(domain); // perform authentication if (opts & OPT_a) { smtp_check("AUTH LOGIN", 334); // we must read credentials unless they are given via -a[up] options if (!G.user || !G.pass) get_cred_or_die(4); encode_base64(NULL, G.user, NULL); smtp_check("", 334); encode_base64(NULL, G.pass, NULL); smtp_check("", 235); } // set sender // N.B. we have here a very loosely defined algotythm // since sendmail historically offers no means to specify secrets on cmdline. // 1) server can require no authentication -> // we must just provide a (possibly fake) reply address. // 2) server can require AUTH -> // we must provide valid username and password along with a (possibly fake) reply address. // For the sake of security username and password are to be read either from console or from a secured file. // Since reading from console may defeat usability, the solution is either to read from a predefined // file descriptor (e.g. 4), or again from a secured file. // got no sender address? -> use system username as a resort // N.B. we marked -f as required option! //if (!G.user) { // // N.B. IMHO getenv("USER") can be way easily spoofed! // G.user = xuid2uname(getuid()); // opt_from = xasprintf("%s@%s", G.user, domain); //} //if (ENABLE_FEATURE_CLEAN_UP) // free(domain); smtp_checkp("MAIL FROM:<%s>", opt_from, 250); // process message // read recipients from message and add them to those given on cmdline. // this means we scan stdin for To:, Cc:, Bcc: lines until an empty line // and then use the rest of stdin as message body code = 0; // set "analyze headers" mode while ((s = xmalloc_fgetline(G.fp0)) != NULL) { // put message lines doubling leading dots if (code) { // escape leading dots // N.B. this feature is implied even if no -i (-oi) switch given // N.B. we need to escape the leading dot regardless of // whether it is single or not character on the line if ('.' == s[0] /*&& '\0' == s[1] */) printf("."); // dump read line printf("%s\r\n", s); free(s); continue; } // analyze headers // To: or Cc: headers add recipients if (0 == strncasecmp("To: ", s, 4) || 0 == strncasecmp("Bcc: " + 1, s, 4)) { rcptto(sane_address(s+4)); // goto addh; llist_add_to_end(&list, s); // Bcc: header adds blind copy (hidden) recipient } else if (0 == strncasecmp("Bcc: ", s, 5)) { rcptto(sane_address(s+5)); free(s); // N.B. Bcc: vanishes from headers! // other headers go verbatim } else if (s[0]) { // addh: llist_add_to_end(&list, s); // the empty line stops analyzing headers } else { free(s); // put recipients specified on cmdline while (*argv) { s = sane_address(*argv); rcptto(s); llist_add_to_end(&list, xasprintf("To: %s", s)); argv++; } // enter "put message" mode smtp_check("DATA", 354); // dump the headers while (list) { printf("%s\r\n", (char *) llist_pop(&list)); } printf("%s\r\n" + 2); // quirk for format string to be reused // stop analyzing headers code++; } } // finalize the message smtp_check(".", 250); // ... and say goodbye smtp_check("QUIT", 221); // cleanup if (ENABLE_FEATURE_CLEAN_UP) fclose(G.fp0); return EXIT_SUCCESS; }
int less_main(int argc, char **argv) { int keypress; INIT_G(); /* TODO: -x: do not interpret backspace, -xx: tab also */ /* -xxx: newline also */ /* -w N: assume width N (-xxx -w 32: hex viewer of sorts) */ getopt32(argv, "EMmN~I" IF_FEATURE_LESS_DASHCMD("S")); argc -= optind; argv += optind; num_files = argc; files = argv; /* Another popular pager, most, detects when stdout * is not a tty and turns into cat. This makes sense. */ if (!isatty(STDOUT_FILENO)) return bb_cat(argv); if (!num_files) { if (isatty(STDIN_FILENO)) { /* Just "less"? No args and no redirection? */ bb_error_msg("missing filename"); bb_show_usage(); } } else { filename = xstrdup(files[0]); } if (option_mask32 & FLAG_TILDE) empty_line_marker = ""; kbd_fd = open(CURRENT_TTY, O_RDONLY); if (kbd_fd < 0) return bb_cat(argv); ndelay_on(kbd_fd); tcgetattr(kbd_fd, &term_orig); term_less = term_orig; term_less.c_lflag &= ~(ICANON | ECHO); term_less.c_iflag &= ~(IXON | ICRNL); /*term_less.c_oflag &= ~ONLCR;*/ term_less.c_cc[VMIN] = 1; term_less.c_cc[VTIME] = 0; get_terminal_width_height(kbd_fd, &width, &max_displayed_line); /* 20: two tabstops + 4 */ if (width < 20 || max_displayed_line < 3) return bb_cat(argv); max_displayed_line -= 2; /* We want to restore term_orig on exit */ bb_signals(BB_FATAL_SIGS, sig_catcher); #if ENABLE_FEATURE_LESS_WINCH signal(SIGWINCH, sigwinch_handler); #endif buffer = xmalloc((max_displayed_line+1) * sizeof(char *)); reinitialize(); while (1) { #if ENABLE_FEATURE_LESS_WINCH while (WINCH_COUNTER) { again: winch_counter--; get_terminal_width_height(kbd_fd, &width, &max_displayed_line); /* 20: two tabstops + 4 */ if (width < 20) width = 20; if (max_displayed_line < 3) max_displayed_line = 3; max_displayed_line -= 2; free(buffer); buffer = xmalloc((max_displayed_line+1) * sizeof(char *)); /* Avoid re-wrap and/or redraw if we already know * we need to do it again. These ops are expensive */ if (WINCH_COUNTER) goto again; re_wrap(); if (WINCH_COUNTER) goto again; buffer_fill_and_print(); /* This took some time. Loop back and check, * were there another SIGWINCH? */ } #endif keypress = less_getch(-1); /* -1: do not position cursor */ keypress_process(keypress); } }
int main(int argc,char** argv) { char _path[260]; char _root[260]; FiEvent evnt; INIT_G(); G.logFileSize = _M(100); G.logFile.fd = STDOUT_FILENO; FiGetCurDir(sizeof(_path),_path); strcpy(_root, _path); std::string str(_path); dirname(_root); append_slash(_root); G.exe = _path; G.root = _root; str+="FiUpdateMgr.log"; freopen(str.c_str(),"a",stdout); #if 1 if (0 == chdir(G.exe)) { ut_dbg("change cur dir success\n"); } else { ut_err("change cur dir fail\n"); } #endif #ifndef WIN32 FiEnableCoreDumps(); pthread_t tid[3]; pthread_attr_t attr[3]; struct stat st; umparams.type = SERVER;//default type umparams.master == false; FiUpdateAssistant::getinstance()->set(&evnt); if (init_param(argc, argv) < 0) { print_usage(stderr, 1); return -1; } if (stat("../download", &st) == -1) { ut_err("stat error num :%d\n", errno); } else { if (!S_ISDIR(st.st_mode)) { system("rm ../download -rf"); } } system("mkdir -p ../download"); if (pthread_attr_init(&attr[0]) < 0) { ut_err("set attr fail\n"); } if (pthread_attr_init(&attr[1]) < 0) { ut_err("set attr fail\n"); } if (pthread_attr_init(&attr[2]) < 0) { ut_err("set attr fail\n"); } pthread_attr_setdetachstate(&attr[0], PTHREAD_CREATE_DETACHED); pthread_attr_setdetachstate(&attr[1], PTHREAD_CREATE_DETACHED); pthread_attr_setdetachstate(&attr[2], PTHREAD_CREATE_DETACHED); if (sendHello()<0) { ut_err("send hello fail\n"); } pthread_create(&tid[0], &attr[0], selfUpdate, NULL); if (umparams.master == false) { pthread_create(&tid[1], &attr[1], recvHelloHandler, NULL); } else { pthread_create(&tid[2], &attr[2], heartHandler, NULL); } #endif UpdateSrv = new FiRpcSrv<FiUpdateMgrImpl>(RPC_PORT, RPC_SERVER_NAME, (char*)(NULL)); UpdateSrv->run(); #if 0 evnt.wait(); #else do { #ifdef WIN32 Sleep(1000); #else sleep(1); #endif }while(true); #endif return 0; }
int tcpudpsvd_main(int argc ATTRIBUTE_UNUSED, char **argv) { char *str_C, *str_t; char *user; struct hcc *hccp; const char *instructs; char *msg_per_host = NULL; unsigned len_per_host = len_per_host; /* gcc */ #ifndef SSLSVD struct bb_uidgid_t ugid; #endif bool tcp; uint16_t local_port; char *preset_local_hostname = NULL; char *remote_hostname = remote_hostname; /* for compiler */ char *remote_addr = remote_addr; /* for compiler */ len_and_sockaddr *lsa; len_and_sockaddr local, remote; socklen_t sa_len; int pid; int sock; int conn; unsigned backlog = 20; INIT_G(); tcp = (applet_name[0] == 't'); /* 3+ args, -i at most once, -p implies -h, -v is counter, -b N, -c N */ opt_complementary = "-3:i--i:ph:vv:b+:c+"; #ifdef SSLSVD getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:", &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname, &backlog, &str_t, &ssluser, &root, &cert, &key, &verbose ); #else /* "+": stop on first non-option */ getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:v", &cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname, &backlog, &str_t, &verbose ); #endif if (option_mask32 & OPT_C) { /* -C n[:message] */ max_per_host = bb_strtou(str_C, &str_C, 10); if (str_C[0]) { if (str_C[0] != ':') bb_show_usage(); msg_per_host = str_C + 1; len_per_host = strlen(msg_per_host); } } if (max_per_host > cmax) max_per_host = cmax; if (option_mask32 & OPT_u) { if (!get_uidgid(&ugid, user, 1)) bb_error_msg_and_die("unknown user/group: %s", user); } #ifdef SSLSVD if (option_mask32 & OPT_U) ssluser = optarg; if (option_mask32 & OPT_slash) root = optarg; if (option_mask32 & OPT_Z) cert = optarg; if (option_mask32 & OPT_K) key = optarg; #endif argv += optind; if (!argv[0][0] || LONE_CHAR(argv[0], '0')) argv[0] = (char*)"0.0.0.0"; /* Per-IP flood protection is not thought-out for UDP */ if (!tcp) max_per_host = 0; bb_sanitize_stdio(); /* fd# 0,1,2 must be opened */ #ifdef SSLSVD sslser = user; client = 0; if ((getuid() == 0) && !(option_mask32 & OPT_u)) { xfunc_exitcode = 100; bb_error_msg_and_die("-U ssluser must be set when running as root"); } if (option_mask32 & OPT_u) if (!uidgid_get(&sslugid, ssluser, 1)) { if (errno) { bb_perror_msg_and_die("fatal: cannot get user/group: %s", ssluser); } bb_error_msg_and_die("unknown user/group '%s'", ssluser); } if (!cert) cert = "./cert.pem"; if (!key) key = cert; if (matrixSslOpen() < 0) fatal("cannot initialize ssl"); if (matrixSslReadKeys(&keys, cert, key, 0, ca) < 0) { if (client) fatal("cannot read cert, key, or ca file"); fatal("cannot read cert or key file"); } if (matrixSslNewSession(&ssl, keys, 0, SSL_FLAGS_SERVER) < 0) fatal("cannot create ssl session"); #endif sig_block(SIGCHLD); signal(SIGCHLD, sig_child_handler); bb_signals(BB_FATAL_SIGS, sig_term_handler); signal(SIGPIPE, SIG_IGN); if (max_per_host) ipsvd_perhost_init(cmax); local_port = bb_lookup_port(argv[1], tcp ? "tcp" : "udp", 0); lsa = xhost2sockaddr(argv[0], local_port); argv += 2; sock = xsocket(lsa->u.sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0); setsockopt_reuseaddr(sock); sa_len = lsa->len; /* I presume sockaddr len stays the same */ xbind(sock, &lsa->u.sa, sa_len); if (tcp) xlisten(sock, backlog); else /* udp: needed for recv_from_to to work: */ socket_want_pktinfo(sock); /* ndelay_off(sock); - it is the default I think? */ #ifndef SSLSVD if (option_mask32 & OPT_u) { /* drop permissions */ xsetgid(ugid.gid); xsetuid(ugid.uid); } #endif if (verbose) { char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa); bb_error_msg("listening on %s, starting", addr); free(addr); #ifndef SSLSVD if (option_mask32 & OPT_u) printf(", uid %u, gid %u", (unsigned)ugid.uid, (unsigned)ugid.gid); #endif } /* Main accept() loop */ again: hccp = NULL; while (cnum >= cmax) wait_for_any_sig(); /* expecting SIGCHLD */ /* Accept a connection to fd #0 */ again1: close(0); again2: sig_unblock(SIGCHLD); local.len = remote.len = sa_len; if (tcp) { conn = accept(sock, &remote.u.sa, &remote.len); } else { /* In case recv_from_to won't be able to recover local addr. * Also sets port - recv_from_to is unable to do it. */ local = *lsa; conn = recv_from_to(sock, NULL, 0, MSG_PEEK, &remote.u.sa, &local.u.sa, sa_len); } sig_block(SIGCHLD); if (conn < 0) { if (errno != EINTR) bb_perror_msg(tcp ? "accept" : "recv"); goto again2; } xmove_fd(tcp ? conn : sock, 0); if (max_per_host) { /* Drop connection immediately if cur_per_host > max_per_host * (minimizing load under SYN flood) */ remote_addr = xmalloc_sockaddr2dotted_noport(&remote.u.sa); cur_per_host = ipsvd_perhost_add(remote_addr, max_per_host, &hccp); if (cur_per_host > max_per_host) { /* ipsvd_perhost_add detected that max is exceeded * (and did not store ip in connection table) */ free(remote_addr); if (msg_per_host) { /* don't block or test for errors */ send(0, msg_per_host, len_per_host, MSG_DONTWAIT); } goto again1; } /* NB: remote_addr is not leaked, it is stored in conn table */ } if (!tcp) { /* Voodoo magic: making udp sockets each receive its own * packets is not trivial, and I still not sure * I do it 100% right. * 1) we have to do it before fork() * 2) order is important - is it right now? */ /* Open new non-connected UDP socket for further clients... */ sock = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0); setsockopt_reuseaddr(sock); /* Make plain write/send work for old socket by supplying default * destination address. This also restricts incoming packets * to ones coming from this remote IP. */ xconnect(0, &remote.u.sa, sa_len); /* hole? at this point we have no wildcard udp socket... * can this cause clients to get "port unreachable" icmp? * Yup, time window is very small, but it exists (is it?) */ /* ..."open new socket", continued */ xbind(sock, &lsa->u.sa, sa_len); socket_want_pktinfo(sock); /* Doesn't work: * we cannot replace fd #0 - we will lose pending packet * which is already buffered for us! And we cannot use fd #1 * instead - it will "intercept" all following packets, but child * does not expect data coming *from fd #1*! */ #if 0 /* Make it so that local addr is fixed to localp->u.sa * and we don't accidentally accept packets to other local IPs. */ /* NB: we possibly bind to the _very_ same_ address & port as the one * already bound in parent! This seems to work in Linux. * (otherwise we can move socket to fd #0 only if bind succeeds) */ close(0); set_nport(localp, htons(local_port)); xmove_fd(xsocket(localp->u.sa.sa_family, SOCK_DGRAM, 0), 0); setsockopt_reuseaddr(0); /* crucial */ xbind(0, &localp->u.sa, localp->len); #endif } pid = vfork(); if (pid == -1) { bb_perror_msg("vfork"); goto again; } if (pid != 0) { /* Parent */ cnum++; if (verbose) connection_status(); if (hccp) hccp->pid = pid; /* clean up changes done by vforked child */ undo_xsetenv(); goto again; } /* Child: prepare env, log, and exec prog */ /* Closing tcp listening socket */ if (tcp) close(sock); { /* vfork alert! every xmalloc in this block should be freed! */ char *local_hostname = local_hostname; /* for compiler */ char *local_addr = NULL; char *free_me0 = NULL; char *free_me1 = NULL; char *free_me2 = NULL; if (verbose || !(option_mask32 & OPT_E)) { if (!max_per_host) /* remote_addr is not yet known */ free_me0 = remote_addr = xmalloc_sockaddr2dotted(&remote.u.sa); if (option_mask32 & OPT_h) { free_me1 = remote_hostname = xmalloc_sockaddr2host_noport(&remote.u.sa); if (!remote_hostname) { bb_error_msg("cannot look up hostname for %s", remote_addr); remote_hostname = remote_addr; } } /* Find out local IP peer connected to. * Errors ignored (I'm not paranoid enough to imagine kernel * which doesn't know local IP). */ if (tcp) getsockname(0, &local.u.sa, &local.len); /* else: for UDP it is done earlier by parent */ local_addr = xmalloc_sockaddr2dotted(&local.u.sa); if (option_mask32 & OPT_h) { local_hostname = preset_local_hostname; if (!local_hostname) { free_me2 = local_hostname = xmalloc_sockaddr2host_noport(&local.u.sa); if (!local_hostname) bb_error_msg_and_die("cannot look up hostname for %s", local_addr); } /* else: local_hostname is not NULL, but is NOT malloced! */ } } if (verbose) { pid = getpid(); if (max_per_host) { bb_error_msg("concurrency %s %u/%u", remote_addr, cur_per_host, max_per_host); } bb_error_msg((option_mask32 & OPT_h) ? "start %u %s-%s (%s-%s)" : "start %u %s-%s", pid, local_addr, remote_addr, local_hostname, remote_hostname); } if (!(option_mask32 & OPT_E)) { /* setup ucspi env */ const char *proto = tcp ? "TCP" : "UDP"; /* Extract "original" destination addr:port * from Linux firewall. Useful when you redirect * an outbond connection to local handler, and it needs * to know where it originally tried to connect */ if (tcp && getsockopt(0, SOL_IP, SO_ORIGINAL_DST, &local.u.sa, &local.len) == 0) { char *addr = xmalloc_sockaddr2dotted(&local.u.sa); xsetenv_plain("TCPORIGDSTADDR", addr); free(addr); } xsetenv_plain("PROTO", proto); xsetenv_proto(proto, "LOCALADDR", local_addr); xsetenv_proto(proto, "REMOTEADDR", remote_addr); if (option_mask32 & OPT_h) { xsetenv_proto(proto, "LOCALHOST", local_hostname); xsetenv_proto(proto, "REMOTEHOST", remote_hostname); } //compat? xsetenv_proto(proto, "REMOTEINFO", ""); /* additional */ if (cur_per_host > 0) /* can not be true for udp */ xsetenv_plain("TCPCONCURRENCY", utoa(cur_per_host)); } free(local_addr); free(free_me0); free(free_me1); free(free_me2); } xdup2(0, 1); signal(SIGTERM, SIG_DFL); signal(SIGPIPE, SIG_DFL); signal(SIGCHLD, SIG_DFL); sig_unblock(SIGCHLD); #ifdef SSLSVD strcpy(id, utoa(pid)); ssl_io(0, argv); #else BB_EXECVP(argv[0], argv); #endif bb_perror_msg_and_die("exec '%s'", argv[0]); }
int telnet_main(int argc UNUSED_PARAM, char **argv) { char *host; int port; int len; struct pollfd ufds[2]; INIT_G(); #if ENABLE_FEATURE_AUTOWIDTH get_terminal_width_height(0, &G.win_width, &G.win_height); #endif #if ENABLE_FEATURE_TELNET_TTYPE G.ttype = getenv("TERM"); #endif if (tcgetattr(0, &G.termios_def) >= 0) { G.do_termios = 1; G.termios_raw = G.termios_def; cfmakeraw(&G.termios_raw); } #if ENABLE_FEATURE_TELNET_AUTOLOGIN if (1 & getopt32(argv, "al:", &G.autologin)) G.autologin = getenv("USER"); argv += optind; #else argv++; #endif if (!*argv) bb_show_usage(); host = *argv++; port = bb_lookup_port(*argv ? *argv++ : "telnet", "tcp", 23); if (*argv) /* extra params?? */ bb_show_usage(); xmove_fd(create_and_connect_stream_or_die(host, port), netfd); setsockopt_keepalive(netfd); signal(SIGINT, record_signo); ufds[0].fd = STDIN_FILENO; ufds[0].events = POLLIN; ufds[1].fd = netfd; ufds[1].events = POLLIN; while (1) { if (poll(ufds, 2, -1) < 0) { /* error, ignore and/or log something, bay go to loop */ if (bb_got_signal) con_escape(); else sleep(1); continue; } // FIXME: reads can block. Need full bidirectional buffering. if (ufds[0].revents) { len = safe_read(STDIN_FILENO, G.buf, DATABUFSIZE); if (len <= 0) doexit(EXIT_SUCCESS); TRACE(0, ("Read con: %d\n", len)); handle_net_output(len); } if (ufds[1].revents) { len = safe_read(netfd, G.buf, DATABUFSIZE); if (len <= 0) { full_write1_str("Connection closed by foreign host\r\n"); doexit(EXIT_FAILURE); } TRACE(0, ("Read netfd (%d): %d\n", netfd, len)); handle_net_input(len); } } /* while (1) */ }
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; #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(_PATH_VARRUN"ifplugd.%s.pid", G.iface); pid_from_pidfile = read_pid(pidfile_name); if (opts & FLAG_KILL) { if (pid_from_pidfile > 0) kill(pid_from_pidfile, SIGQUIT); return EXIT_SUCCESS; } if (pid_from_pidfile > 0 && kill(pid_from_pidfile, 0) == 0) bb_error_msg_and_die("daemon already running"); #endif switch (G.api_mode[0]) { case API_AUTO: G.detect_link_func = detect_link_auto; break; case API_ETHTOOL: G.detect_link_func = detect_link_ethtool; break; case API_MII: G.detect_link_func = detect_link_mii; break; case API_PRIVATE: G.detect_link_func = detect_link_priv; break; case API_WLAN: G.detect_link_func = detect_link_wlan; break; case API_IFF: G.detect_link_func = detect_link_iff; break; default: bb_error_msg_and_die("unknown API mode '%s'", G.api_mode); } 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) { xmove_fd(netlink_open(), 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 sed_main(int argc UNUSED_PARAM, char **argv) { unsigned opt; llist_t *opt_e, *opt_f; char *opt_i; #if ENABLE_LONG_OPTS static const char sed_longopts[] ALIGN1 = /* name has_arg short */ "in-place\0" Optional_argument "i" "regexp-extended\0" No_argument "r" "quiet\0" No_argument "n" "silent\0" No_argument "n" "expression\0" Required_argument "e" "file\0" Required_argument "f"; #endif int status = EXIT_SUCCESS; INIT_G(); /* destroy command strings on exit */ if (ENABLE_FEATURE_CLEAN_UP) atexit(sed_free_and_close_stuff); /* Lie to autoconf when it starts asking stupid questions. */ if (argv[1] && strcmp(argv[1], "--version") == 0) { puts("This is not GNU sed version 4.0"); return 0; } /* do normal option parsing */ opt_e = opt_f = NULL; opt_i = NULL; opt_complementary = "e::f::" /* can occur multiple times */ "nn"; /* count -n */ IF_LONG_OPTS(applet_long_options = sed_longopts); /* -i must be first, to match OPT_in_place definition */ opt = getopt32(argv, "i::rne:f:", &opt_i, &opt_e, &opt_f, &G.be_quiet); /* counter for -n */ //argc -= optind; argv += optind; if (opt & OPT_in_place) { // -i atexit(cleanup_outname); } if (opt & 0x2) G.regex_type |= REG_EXTENDED; // -r //if (opt & 0x4) G.be_quiet++; // -n while (opt_e) { // -e add_cmd_block(llist_pop(&opt_e)); } while (opt_f) { // -f char *line; FILE *cmdfile; cmdfile = xfopen_for_read(llist_pop(&opt_f)); while ((line = xmalloc_fgetline(cmdfile)) != NULL) { add_cmd(line); free(line); } fclose(cmdfile); } /* if we didn't get a pattern from -e or -f, use argv[0] */ if (!(opt & 0x18)) { if (!*argv) bb_show_usage(); add_cmd_block(*argv++); } /* Flush any unfinished commands. */ add_cmd(""); /* By default, we write to stdout */ G.nonstdout = stdout; /* argv[0..(argc-1)] should be names of file to process. If no * files were specified or '-' was specified, take input from stdin. * Otherwise, we process all the files specified. */ if (argv[0] == NULL) { if (opt & OPT_in_place) bb_error_msg_and_die(bb_msg_requires_arg, "-i"); add_input_file(stdin); } else { int i; for (i = 0; argv[i]; i++) { struct stat statbuf; int nonstdoutfd; FILE *file; sed_cmd_t *sed_cmd; if (LONE_DASH(argv[i]) && !(opt & OPT_in_place)) { add_input_file(stdin); process_files(); continue; } file = fopen_or_warn(argv[i], "r"); if (!file) { status = EXIT_FAILURE; continue; } add_input_file(file); if (!(opt & OPT_in_place)) { continue; } /* -i: process each FILE separately: */ G.outname = xasprintf("%sXXXXXX", argv[i]); nonstdoutfd = xmkstemp(G.outname); G.nonstdout = xfdopen_for_write(nonstdoutfd); /* Set permissions/owner of output file */ fstat(fileno(file), &statbuf); /* chmod'ing AFTER chown would preserve suid/sgid bits, * but GNU sed 4.2.1 does not preserve them either */ fchmod(nonstdoutfd, statbuf.st_mode); fchown(nonstdoutfd, statbuf.st_uid, statbuf.st_gid); process_files(); fclose(G.nonstdout); G.nonstdout = stdout; if (opt_i) { char *backupname = xasprintf("%s%s", argv[i], opt_i); xrename(argv[i], backupname); free(backupname); } /* else unlink(argv[i]); - rename below does this */ xrename(G.outname, argv[i]); //TODO: rollback backup on error? free(G.outname); G.outname = NULL; /* Re-enable disabled range matches */ for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) { sed_cmd->beg_line = sed_cmd->beg_line_orig; } } /* Here, to handle "sed 'cmds' nonexistent_file" case we did: * if (G.current_input_file >= G.input_file_count) * return status; * but it's not needed since process_files() works correctly * in this case too. */ } process_files(); return status; }
int dd_main(int argc UNUSED_PARAM, char **argv) { enum { /* Must be in the same order as OP_conv_XXX! */ /* (see "flags |= (1 << what)" below) */ FLAG_NOTRUNC = 1 << 0, FLAG_SYNC = 1 << 1, FLAG_NOERROR = 1 << 2, FLAG_FSYNC = 1 << 3, /* end of conv flags */ FLAG_TWOBUFS = 1 << 4, FLAG_COUNT = 1 << 5, }; static const char keywords[] ALIGN1 = "bs\0""count\0""seek\0""skip\0""if\0""of\0" #if ENABLE_FEATURE_DD_IBS_OBS "ibs\0""obs\0""conv\0" #endif ; #if ENABLE_FEATURE_DD_IBS_OBS static const char conv_words[] ALIGN1 = "notrunc\0""sync\0""noerror\0""fsync\0"; #endif enum { OP_bs = 0, OP_count, OP_seek, OP_skip, OP_if, OP_of, #if ENABLE_FEATURE_DD_IBS_OBS OP_ibs, OP_obs, OP_conv, /* Must be in the same order as FLAG_XXX! */ OP_conv_notrunc = 0, OP_conv_sync, OP_conv_noerror, OP_conv_fsync, /* Unimplemented conv=XXX: */ //nocreat do not create the output file //excl fail if the output file already exists //fdatasync physically write output file data before finishing //swab swap every pair of input bytes //lcase change upper case to lower case //ucase change lower case to upper case //block pad newline-terminated records with spaces to cbs-size //unblock replace trailing spaces in cbs-size records with newline //ascii from EBCDIC to ASCII //ebcdic from ASCII to EBCDIC //ibm from ASCII to alternate EBCDIC #endif }; int exitcode = EXIT_FAILURE; size_t ibs = 512, obs = 512; ssize_t n, w; char *ibuf, *obuf; /* And these are all zeroed at once! */ struct { int flags; size_t oc; off_t count; off_t seek, skip; const char *infile, *outfile; } Z; #define flags (Z.flags ) #define oc (Z.oc ) #define count (Z.count ) #define seek (Z.seek ) #define skip (Z.skip ) #define infile (Z.infile ) #define outfile (Z.outfile) memset(&Z, 0, sizeof(Z)); INIT_G(); //fflush(NULL); - is this needed because of NOEXEC? #if ENABLE_FEATURE_DD_SIGNAL_HANDLING signal_SA_RESTART_empty_mask(SIGUSR1, dd_output_status); #endif for (n = 1; argv[n]; n++) { int what; char *val; char *arg = argv[n]; #if ENABLE_DESKTOP /* "dd --". NB: coreutils 6.9 will complain if they see * more than one of them. We wouldn't. */ if (arg[0] == '-' && arg[1] == '-' && arg[2] == '\0') continue; #endif val = strchr(arg, '='); if (val == NULL) bb_show_usage(); *val = '\0'; what = index_in_strings(keywords, arg); if (what < 0) bb_show_usage(); /* *val = '='; - to preserve ps listing? */ val++; #if ENABLE_FEATURE_DD_IBS_OBS if (what == OP_ibs) { /* Must fit into positive ssize_t */ ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes); /*continue;*/ } if (what == OP_obs) { obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes); /*continue;*/ } if (what == OP_conv) { while (1) { /* find ',', replace them with NUL so we can use val for * index_in_strings() without copying. * We rely on val being non-null, else strchr would fault. */ arg = strchr(val, ','); if (arg) *arg = '\0'; what = index_in_strings(conv_words, val); if (what < 0) bb_error_msg_and_die(bb_msg_invalid_arg, val, "conv"); flags |= (1 << what); if (!arg) /* no ',' left, so this was the last specifier */ break; /* *arg = ','; - to preserve ps listing? */ val = arg + 1; /* skip this keyword and ',' */ } continue; /* we trashed 'what', can't fall through */ } #endif if (what == OP_bs) { ibs = obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes); /*continue;*/ } /* These can be large: */ if (what == OP_count) { flags |= FLAG_COUNT; count = XATOU_SFX(val, dd_suffixes); /*continue;*/ } if (what == OP_seek) { seek = XATOU_SFX(val, dd_suffixes); /*continue;*/ } if (what == OP_skip) { skip = XATOU_SFX(val, dd_suffixes); /*continue;*/ } if (what == OP_if) { infile = val; /*continue;*/ } if (what == OP_of) { outfile = val; /*continue;*/ } } /* end of "for (argv[n])" */ //XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever ibuf = obuf = xmalloc(ibs); if (ibs != obs) { flags |= FLAG_TWOBUFS; obuf = xmalloc(obs); } if (infile != NULL) xmove_fd(xopen(infile, O_RDONLY), ifd); else { infile = bb_msg_standard_input; } if (outfile != NULL) { int oflag = O_WRONLY | O_CREAT; if (!seek && !(flags & FLAG_NOTRUNC)) oflag |= O_TRUNC; xmove_fd(xopen(outfile, oflag), ofd); if (seek && !(flags & FLAG_NOTRUNC)) { if (ftruncate(ofd, seek * obs) < 0) { struct stat st; if (fstat(ofd, &st) < 0 || S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) goto die_outfile; } } } else { outfile = bb_msg_standard_output; } if (skip) { if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) { while (skip-- > 0) { n = safe_read(ifd, ibuf, ibs); if (n < 0) goto die_infile; if (n == 0) break; } } } if (seek) { if (lseek(ofd, seek * obs, SEEK_CUR) < 0) goto die_outfile; } while (!(flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) { if (flags & FLAG_NOERROR) /* Pre-zero the buffer if conv=noerror */ memset(ibuf, 0, ibs); n = safe_read(ifd, ibuf, ibs); if (n == 0) break; if (n < 0) { if (!(flags & FLAG_NOERROR)) goto die_infile; n = ibs; bb_simple_perror_msg(infile); } if ((size_t)n == ibs) G.in_full++; else { G.in_part++; if (flags & FLAG_SYNC) { memset(ibuf + n, '\0', ibs - n); n = ibs; } } if (flags & FLAG_TWOBUFS) { char *tmp = ibuf; while (n) { size_t d = obs - oc; if (d > (size_t)n) d = n; memcpy(obuf + oc, tmp, d); n -= d; tmp += d; oc += d; if (oc == obs) { if (write_and_stats(obuf, obs, obs, outfile)) goto out_status; oc = 0; } } } else if (write_and_stats(ibuf, n, obs, outfile)) goto out_status; if (flags & FLAG_FSYNC) { if (fsync(ofd) < 0) goto die_outfile; } } if (ENABLE_FEATURE_DD_IBS_OBS && oc) { w = full_write_or_warn(obuf, oc, outfile); if (w < 0) goto out_status; if (w > 0) G.out_part++; } if (close(ifd) < 0) { die_infile: bb_simple_perror_msg_and_die(infile); } if (close(ofd) < 0) { die_outfile: bb_simple_perror_msg_and_die(outfile); } exitcode = EXIT_SUCCESS; out_status: dd_output_status(0); return exitcode; }
int popmaildir_main(int argc UNUSED_PARAM, char **argv) { char *buf; unsigned nmsg; char *hostname; pid_t pid; const char *retr; #if ENABLE_FEATURE_POPMAILDIR_DELIVERY const char *delivery; #endif unsigned opt_nlines = 0; enum { OPT_b = 1 << 0, // -b binary mode. Ignored OPT_d = 1 << 1, // -d,-dd,-ddd debug. Ignored OPT_m = 1 << 2, // -m show ugsed memory. Ignored OPT_V = 1 << 3, // -V version. Ignored OPT_c = 1 << 4, // -c use tcpclient. Ignored OPT_a = 1 << 5, // -a use APOP protocol OPT_s = 1 << 6, // -s skip authorization OPT_T = 1 << 7, // -T get messages with TOP instead with RETR OPT_k = 1 << 8, // -k keep retrieved messages on the server OPT_t = 1 << 9, // -t90 set timeout to 90 sec OPT_R = 1 << 10, // -R20000 remove old messages on the server >= 20000 bytes (requires -k). Ignored OPT_Z = 1 << 11, // -Z11-23 remove messages from 11 to 23 (dangerous). Ignored OPT_L = 1 << 12, // -L50000 not retrieve new messages >= 50000 bytes. Ignored OPT_H = 1 << 13, // -H30 type first 30 lines of a message; (-L12000 -H30). Ignored OPT_M = 1 << 14, // -M\"program arg1 arg2 ...\"; deliver by program. Treated like -F OPT_F = 1 << 15, // -F\"program arg1 arg2 ...\"; filter by program. Treated like -M }; // init global variables INIT_G(); // parse options opt_complementary = "-1:dd:t+:R+:L+:H+"; opts = getopt32(argv, "bdmVcasTkt:" "R:Z:L:H:" IF_FEATURE_POPMAILDIR_DELIVERY("M:F:"), &timeout, NULL, NULL, NULL, &opt_nlines IF_FEATURE_POPMAILDIR_DELIVERY(, &delivery, &delivery) // we treat -M and -F the same ); //argc -= optind; argv += optind; // get auth info if (!(opts & OPT_s)) get_cred_or_die(STDIN_FILENO); // goto maildir xchdir(*argv++); // launch connect helper, if any if (*argv) launch_helper((const char **)argv); // get server greeting pop3_checkr(NULL, NULL, &buf); // authenticate (if no -s given) if (!(opts & OPT_s)) { // server supports APOP and we want it? if ('<' == buf[0] && (opts & OPT_a)) { union { // save a bit of stack md5_ctx_t ctx; char hex[16 * 2 + 1]; } md5; uint32_t res[16 / 4]; char *s = strchr(buf, '>'); if (s) s[1] = '\0'; // get md5 sum of "<stamp>password" string md5_begin(&md5.ctx); md5_hash(&md5.ctx, buf, strlen(buf)); md5_hash(&md5.ctx, G.pass, strlen(G.pass)); md5_end(&md5.ctx, res); *bin2hex(md5.hex, (char*)res, 16) = '\0'; // APOP s = xasprintf("%s %s", G.user, md5.hex); pop3_check("APOP %s", s); free(s); free(buf); // server ignores APOP -> use simple text authentication } else { // USER pop3_check("USER %s", G.user); // PASS pop3_check("PASS %s", G.pass); } } // get mailbox statistics pop3_checkr("STAT", NULL, &buf); // prepare message filename suffix hostname = safe_gethostname(); pid = getpid(); // get messages counter // NOTE: we don't use xatou(buf) since buf is "nmsg nbytes" // we only need nmsg and atoi is just exactly what we need // if atoi fails to convert buf into number it returns 0 // in this case the following loop simply will not be executed nmsg = atoi(buf); free(buf); // loop through messages retr = (opts & OPT_T) ? xasprintf("TOP %%u %u", opt_nlines) : "RETR %u"; for (; nmsg; nmsg--) { char *filename; char *target; char *answer; FILE *fp; #if ENABLE_FEATURE_POPMAILDIR_DELIVERY int rc; #endif // generate unique filename filename = xasprintf("tmp/%llu.%u.%s", monotonic_us(), (unsigned)pid, hostname); // retrieve message in ./tmp/ unless filter is specified pop3_check(retr, (const char *)(ptrdiff_t)nmsg); #if ENABLE_FEATURE_POPMAILDIR_DELIVERY // delivery helper ordered? -> setup pipe if (opts & (OPT_F|OPT_M)) { // helper will have $FILENAME set to filename xsetenv("FILENAME", filename); fp = popen(delivery, "w"); unsetenv("FILENAME"); if (!fp) { bb_perror_msg("delivery helper"); break; } } else #endif // create and open file filename fp = xfopen_for_write(filename); // copy stdin to fp (either filename or delivery helper) while ((answer = xmalloc_fgets_str(stdin, "\r\n")) != NULL) { char *s = answer; if ('.' == answer[0]) { if ('.' == answer[1]) s++; else if ('\r' == answer[1] && '\n' == answer[2] && '\0' == answer[3]) break; } //*strchrnul(s, '\r') = '\n'; fputs(s, fp); free(answer); } #if ENABLE_FEATURE_POPMAILDIR_DELIVERY // analyse delivery status if (opts & (OPT_F|OPT_M)) { rc = pclose(fp); if (99 == rc) // 99 means bail out break; // if (rc) // !0 means skip to the next message goto skip; // // 0 means continue } else { // close filename fclose(fp); } #endif // delete message from server if (!(opts & OPT_k)) pop3_check("DELE %u", (const char*)(ptrdiff_t)nmsg); // atomically move message to ./new/ target = xstrdup(filename); strncpy(target, "new", 3); // ... or just stop receiving on failure if (rename_or_warn(filename, target)) break; free(target); #if ENABLE_FEATURE_POPMAILDIR_DELIVERY skip: #endif free(filename); } // Bye pop3_check("QUIT", NULL); if (ENABLE_FEATURE_CLEAN_UP) { free(G.user); free(G.pass); } return EXIT_SUCCESS; }
int man_main(int argc UNUSED_PARAM, char **argv) { parser_t *parser; char *sec_list; char *cur_path, *cur_sect; char **man_path_list; int count_mp; int cur_mp; int opt, not_found; char *token[2]; INIT_G(); opt_complementary = "-1"; /* at least one argument */ opt = getopt32(argv, "+aw"); argv += optind; sec_list = xstrdup("0p:1:1p:2:3:3p:4:5:6:7:8:9"); count_mp = 0; man_path_list = add_MANPATH(NULL, &count_mp, getenv("MANDATORY_MANPATH"+10) /* "MANPATH" */ ); if (!man_path_list) { /* default, may be overridden by /etc/man.conf */ man_path_list = xzalloc(2 * sizeof(man_path_list[0])); man_path_list[0] = (char*)"/usr/man"; /* count_mp stays 0. * Thus, man.conf will overwrite man_path_list[0] * if a path is defined there. */ } /* Parse man.conf[ig] or man_db.conf */ /* man version 1.6f uses man.config */ /* man-db implementation of man uses man_db.conf */ parser = config_open2("/etc/man.config", fopen_for_read); if (!parser) parser = config_open2("/etc/man.conf", fopen_for_read); if (!parser) parser = config_open2("/etc/man_db.conf", fopen_for_read); while (config_read(parser, token, 2, 0, "# \t", PARSE_NORMAL)) { if (!token[1]) continue; if (strcmp("DEFINE", token[0]) == 0) { G.col = if_redefined(G.tbl , "col", token[1]); G.tbl = if_redefined(G.tbl , "tbl", token[1]); G.nroff = if_redefined(G.nroff, "nroff", token[1]); G.pager = if_redefined(G.pager, "pager", token[1]); } else if (strcmp("MANDATORY_MANPATH"+10, token[0]) == 0 /* "MANPATH"? */ || strcmp("MANDATORY_MANPATH", token[0]) == 0 ) { man_path_list = add_MANPATH(man_path_list, &count_mp, token[1]); } if (strcmp("MANSECT", token[0]) == 0) { free(sec_list); sec_list = xstrdup(token[1]); } } config_close(parser); { /* environment overrides setting from man.config */ char *env_pager = getenv("MANPAGER"); if (!env_pager) env_pager = getenv("PAGER"); if (env_pager) G.pager = env_pager; } if (!isatty(STDOUT_FILENO)) { putenv((char*)"GROFF_NO_SGR=1"); G.pager = xasprintf("%s -b -p -x", G.col); } not_found = 0; do { /* for each argv[] */ int found = 0; cur_mp = 0; if (strchr(*argv, '/')) { found = show_manpage(*argv, /*man:*/ 1, 0); goto check_found; } while ((cur_path = man_path_list[cur_mp++]) != NULL) { /* for each MANPATH */ cur_sect = sec_list; do { /* for each section */ char *next_sect = strchrnul(cur_sect, ':'); int sect_len = next_sect - cur_sect; char *man_filename; int cat0man1 = 0; /* Search for cat, then man page */ while (cat0man1 < 2) { int found_here; man_filename = xasprintf("%s/%s%.*s/%s.%.*s", cur_path, "cat\0man" + (cat0man1 * 4), sect_len, cur_sect, *argv, sect_len, cur_sect); found_here = show_manpage(man_filename, cat0man1, 0); found |= found_here; cat0man1 += found_here + 1; free(man_filename); } if (found && !(opt & OPT_a)) goto next_arg; cur_sect = next_sect; while (*cur_sect == ':') cur_sect++; } while (*cur_sect); } check_found: if (!found) { bb_error_msg("no manual entry for '%s'", *argv); not_found = 1; } next_arg: argv++; } while (*argv); return not_found; }
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 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(); 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; 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; /* do not clear bb_got_signal if already 0, this can lose signals */ case 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; } if ((opts & FLAG_MONITOR) && (netlink_pollfd[0].revents & POLLIN) ) { int iface_exists_old; iface_exists_old = G.iface_exists; 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_old = iface_status; 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 ran 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 0 /* if you are back in 1970... */ if (delay_time == 0) { sleep(1); delay_time = 1; } #endif } } if (delay_time && (int)(monotonic_sec() - delay_time) >= 0) { if (run_script(iface_status_str) != 0) goto exiting; delay_time = 0; } } /* 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 crond_main(int argc ATTRIBUTE_UNUSED, char **argv) { unsigned opt; INIT_G(); /* "-b after -f is ignored", and so on for every pair a-b */ opt_complementary = "f-b:b-f:S-L:L-S" USE_DEBUG_CROND_OPTION(":d-l") ":l+:d+"; /* -l and -d have numeric param */ opt = getopt32(argv, "l:L:fbSc:" USE_DEBUG_CROND_OPTION("d:"), &LogLevel, &LogFile, &CDir USE_DEBUG_CROND_OPTION(,&LogLevel)); /* both -d N and -l N set the same variable: LogLevel */ if (!(opt & OPT_f)) { /* close stdin, stdout, stderr. * close unused descriptors - don't need them. */ bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); } if (!DebugOpt && LogFile == NULL) { /* logging to syslog */ openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON); logmode = LOGMODE_SYSLOG; } xchdir(CDir); //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */ setenv("SHELL", DEFAULT_SHELL, 1); /* once, for all future children */ crondlog(LVL9 "crond (busybox "BB_VER") started, log level %d", LogLevel); SynchronizeDir(); /* main loop - synchronize to 1 second after the minute, minimum sleep * of 1 second. */ { time_t t1 = time(NULL); time_t t2; long dt; int rescan = 60; int sleep_time = 60; write_pidfile("/var/run/crond.pid"); for (;;) { sleep((sleep_time + 1) - (time(NULL) % sleep_time)); t2 = time(NULL); dt = (long)t2 - (long)t1; /* * The file 'cron.update' is checked to determine new cron * jobs. The directory is rescanned once an hour to deal * with any screwups. * * check for disparity. Disparities over an hour either way * result in resynchronization. A reverse-indexed disparity * less then an hour causes us to effectively sleep until we * match the original time (i.e. no re-execution of jobs that * have just been run). A forward-indexed disparity less then * an hour causes intermediate jobs to be run, but only once * in the worst case. * * when running jobs, the inequality used is greater but not * equal to t1, and less then or equal to t2. */ if (--rescan == 0) { rescan = 60; SynchronizeDir(); } CheckUpdates(); if (DebugOpt) crondlog(LVL5 "wakeup dt=%ld", dt); if (dt < -60 * 60 || dt > 60 * 60) { crondlog(WARN9 "time disparity of %d minutes detected", dt / 60); } else if (dt > 0) { TestJobs(t1, t2); RunJobs(); sleep(5); if (CheckJobs() > 0) { sleep_time = 10; } else { sleep_time = 60; } } t1 = t2; } } return 0; /* not reached */ }
int runsv_main(int argc UNUSED_PARAM, char **argv) { struct stat s; int fd; int r; char buf[256]; INIT_G(); dir = single_argv(argv); xpiped_pair(selfpipe); close_on_exec_on(selfpipe.rd); close_on_exec_on(selfpipe.wr); ndelay_on(selfpipe.rd); ndelay_on(selfpipe.wr); sig_block(SIGCHLD); bb_signals_recursive_norestart(1 << SIGCHLD, s_child); sig_block(SIGTERM); bb_signals_recursive_norestart(1 << SIGTERM, s_term); xchdir(dir); /* bss: svd[0].pid = 0; */ if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */ if (C_NOOP) svd[0].ctrl = C_NOOP; if (W_UP) svd[0].sd_want = W_UP; /* bss: svd[0].islog = 0; */ /* bss: svd[1].pid = 0; */ gettimeofday_ns(&svd[0].start); if (stat("down", &s) != -1) svd[0].sd_want = W_DOWN; if (stat("log", &s) == -1) { if (errno != ENOENT) warn_cannot("stat ./log"); } else { if (!S_ISDIR(s.st_mode)) { errno = 0; warn_cannot("stat log/down: log is not a directory"); } else { haslog = 1; svd[1].state = S_DOWN; svd[1].ctrl = C_NOOP; svd[1].sd_want = W_UP; svd[1].islog = 1; gettimeofday_ns(&svd[1].start); if (stat("log/down", &s) != -1) svd[1].sd_want = W_DOWN; xpiped_pair(logpipe); close_on_exec_on(logpipe.rd); close_on_exec_on(logpipe.wr); } } if (mkdir("supervise", 0700) == -1) { r = readlink("supervise", buf, sizeof(buf)); if (r != -1) { if (r == sizeof(buf)) fatal2x_cannot("readlink ./supervise", ": name too long"); buf[r] = 0; mkdir(buf, 0700); } else { if ((errno != ENOENT) && (errno != EINVAL)) fatal_cannot("readlink ./supervise"); } } svd[0].fdlock = xopen3("log/supervise/lock"+4, O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600); if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1) fatal_cannot("lock supervise/lock"); close_on_exec_on(svd[0].fdlock); if (haslog) { if (mkdir("log/supervise", 0700) == -1) { r = readlink("log/supervise", buf, 256); if (r != -1) { if (r == 256) fatal2x_cannot("readlink ./log/supervise", ": name too long"); buf[r] = 0; fd = xopen(".", O_RDONLY|O_NDELAY); xchdir("./log"); mkdir(buf, 0700); if (fchdir(fd) == -1) fatal_cannot("change back to service directory"); close(fd); } else { if ((errno != ENOENT) && (errno != EINVAL)) fatal_cannot("readlink ./log/supervise"); } } svd[1].fdlock = xopen3("log/supervise/lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600); if (flock(svd[1].fdlock, LOCK_EX) == -1) fatal_cannot("lock log/supervise/lock"); close_on_exec_on(svd[1].fdlock); } mkfifo("log/supervise/control"+4, 0600); svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY); close_on_exec_on(svd[0].fdcontrol); svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY); close_on_exec_on(svd[0].fdcontrolwrite); update_status(&svd[0]); if (haslog) { mkfifo("log/supervise/control", 0600); svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY); close_on_exec_on(svd[1].fdcontrol); svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY); close_on_exec_on(svd[1].fdcontrolwrite); update_status(&svd[1]); } mkfifo("log/supervise/ok"+4, 0600); fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY); close_on_exec_on(fd); if (haslog) { mkfifo("log/supervise/ok", 0600); fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY); close_on_exec_on(fd); } for (;;) { struct pollfd x[3]; unsigned deadline; char ch; if (haslog) if (!svd[1].pid && svd[1].sd_want == W_UP) startservice(&svd[1]); if (!svd[0].pid) if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH) startservice(&svd[0]); x[0].fd = selfpipe.rd; x[0].events = POLLIN; x[1].fd = svd[0].fdcontrol; x[1].events = POLLIN; /* x[2] is used only if haslog == 1 */ x[2].fd = svd[1].fdcontrol; x[2].events = POLLIN; sig_unblock(SIGTERM); sig_unblock(SIGCHLD); poll(x, 2 + haslog, 3600*1000); sig_block(SIGTERM); sig_block(SIGCHLD); while (read(selfpipe.rd, &ch, 1) == 1) continue; for (;;) { pid_t child; int wstat; child = wait_any_nohang(&wstat); if (!child) break; if ((child == -1) && (errno != EINTR)) break; if (child == svd[0].pid) { svd[0].wstat = wstat; svd[0].pid = 0; pidchanged = 1; svd[0].ctrl &= ~C_TERM; if (svd[0].state != S_FINISH) { fd = open("finish", O_RDONLY|O_NDELAY); if (fd != -1) { close(fd); svd[0].state = S_FINISH; update_status(&svd[0]); continue; } } svd[0].state = S_DOWN; deadline = svd[0].start.tv_sec + 1; gettimeofday_ns(&svd[0].start); update_status(&svd[0]); if (LESS(svd[0].start.tv_sec, deadline)) sleep(1); } if (haslog) { if (child == svd[1].pid) { svd[0].wstat = wstat; svd[1].pid = 0; pidchanged = 1; svd[1].state = S_DOWN; svd[1].ctrl &= ~C_TERM; deadline = svd[1].start.tv_sec + 1; gettimeofday_ns(&svd[1].start); update_status(&svd[1]); if (LESS(svd[1].start.tv_sec, deadline)) sleep(1); } } } /* for (;;) */ if (read(svd[0].fdcontrol, &ch, 1) == 1) ctrl(&svd[0], ch); if (haslog) if (read(svd[1].fdcontrol, &ch, 1) == 1) ctrl(&svd[1], ch); if (sigterm) { ctrl(&svd[0], 'x'); sigterm = 0; } if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) { if (svd[1].pid == 0) _exit(EXIT_SUCCESS); if (svd[1].sd_want != W_EXIT) { svd[1].sd_want = W_EXIT; /* stopservice(&svd[1]); */ update_status(&svd[1]); close(logpipe.wr); close(logpipe.rd); } } } /* for (;;) */ /* not reached */ return 0; }
int showkey_main(int argc UNUSED_PARAM, char **argv) { enum { OPT_a = (1<<0), // display the decimal/octal/hex values of the keys OPT_k = (1<<1), // display only the interpreted keycodes (default) OPT_s = (1<<2), // display only the raw scan-codes }; INIT_G(); // FIXME: aks are all mutually exclusive getopt32(argv, "aks"); // prepare for raw mode xget1(&tio, &tio0); // put stdin in raw mode xset1(&tio); #define press_keys "Press any keys, program terminates %s:\r\n\n" if (option_mask32 & OPT_a) { // just read stdin char by char unsigned char c; printf(press_keys, "on EOF (ctrl-D)"); // read and show byte values while (1 == read(STDIN_FILENO, &c, 1)) { printf("%3u 0%03o 0x%02x\r\n", c, c, c); if (04 /*CTRL-D*/ == c) break; } } else { // we assume a PC keyboard xioctl(STDIN_FILENO, KDGKBMODE, &kbmode); printf("Keyboard mode was %s.\r\n\n", kbmode == K_RAW ? "RAW" : (kbmode == K_XLATE ? "XLATE" : (kbmode == K_MEDIUMRAW ? "MEDIUMRAW" : (kbmode == K_UNICODE ? "UNICODE" : "UNKNOWN"))) ); // set raw keyboard mode xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)((option_mask32 & OPT_k) ? K_MEDIUMRAW : K_RAW)); // we should exit on any signal; signals should interrupt read bb_signals_recursive_norestart(BB_FATAL_SIGS, record_signo); // inform user that program ends after time of inactivity printf(press_keys, "10s after last keypress"); // read and show scancodes while (!bb_got_signal) { char buf[18]; int i, n; // setup 10s watchdog alarm(10); // read scancodes n = read(STDIN_FILENO, buf, sizeof(buf)); i = 0; while (i < n) { if (option_mask32 & OPT_s) { // show raw scancodes printf("0x%02x ", buf[i++]); } else { // show interpreted scancodes (default) char c = buf[i]; int kc; if (i+2 < n && (c & 0x7f) == 0 && (buf[i+1] & 0x80) != 0 && (buf[i+2] & 0x80) != 0 ) { kc = ((buf[i+1] & 0x7f) << 7) | (buf[i+2] & 0x7f); i += 3; } else { kc = (c & 0x7f); i++; } printf("keycode %3u %s", kc, (c & 0x80) ? "release" : "press"); } } puts("\r"); } // restore keyboard mode xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)kbmode); } // restore console settings xset1(&tio0); return EXIT_SUCCESS; }
int arping_main(int argc UNUSED_PARAM, char **argv) { const char *device = "eth0"; char *source = NULL; char *target; unsigned char *packet; char *err_str; INIT_G(); sock_fd = xsocket(AF_PACKET, SOCK_DGRAM, 0); // Drop suid root privileges // Need to remove SUID_NEVER from applets.h for this to work //xsetuid(getuid()); { unsigned opt; char *str_timeout; /* Dad also sets quit_on_reply. * Advert also sets unsolicited. */ opt_complementary = "=1:Df:AU:c+"; opt = getopt32(argv, "DUAqfbc:w:I:s:", &count, &str_timeout, &device, &source); if (opt & 0x80) /* -w: timeout */ timeout_us = xatou_range(str_timeout, 0, INT_MAX/2000000) * 1000000 + 500000; //if (opt & 0x200) /* -s: source */ option_mask32 &= 0x3f; /* set respective flags */ } target = argv[optind]; err_str = xasprintf("interface %s %%s", device); xfunc_error_retval = 2; { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy_IFNAMSIZ(ifr.ifr_name, device); /* We use ifr.ifr_name in error msg so that problem * with truncated name will be visible */ ioctl_or_perror_and_die(sock_fd, SIOCGIFINDEX, &ifr, err_str, "not found"); me.sll_ifindex = ifr.ifr_ifindex; xioctl(sock_fd, SIOCGIFFLAGS, (char *) &ifr); if (!(ifr.ifr_flags & IFF_UP)) { bb_error_msg_and_die(err_str, "is down"); } if (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) { bb_error_msg(err_str, "is not ARPable"); return (option_mask32 & DAD ? 0 : 2); } } /* if (!inet_aton(target, &dst)) - not needed */ { len_and_sockaddr *lsa; lsa = xhost_and_af2sockaddr(target, 0, AF_INET); dst = lsa->u.sin.sin_addr; if (ENABLE_FEATURE_CLEAN_UP) free(lsa); } if (source && !inet_aton(source, &src)) { bb_error_msg_and_die("invalid source address %s", source); } if ((option_mask32 & (DAD|UNSOLICITED)) == UNSOLICITED && src.s_addr == 0) src = dst; if (!(option_mask32 & DAD) || src.s_addr) { struct sockaddr_in saddr; int probe_fd = xsocket(AF_INET, SOCK_DGRAM, 0); setsockopt_bindtodevice(probe_fd, device); memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; if (src.s_addr) { /* Check that this is indeed our IP */ saddr.sin_addr = src; xbind(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr)); } else { /* !(option_mask32 & DAD) case */ /* Find IP address on this iface */ socklen_t alen = sizeof(saddr); saddr.sin_port = htons(1025); saddr.sin_addr = dst; if (setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, &const_int_1, sizeof(const_int_1)) == -1) bb_perror_msg("setsockopt(SO_DONTROUTE)"); xconnect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr)); getsockname(probe_fd, (struct sockaddr *) &saddr, &alen); //never happens: //if (getsockname(probe_fd, (struct sockaddr *) &saddr, &alen) == -1) // bb_perror_msg_and_die("getsockname"); if (saddr.sin_family != AF_INET) bb_error_msg_and_die("no IP address configured"); src = saddr.sin_addr; } close(probe_fd); } me.sll_family = AF_PACKET; //me.sll_ifindex = ifindex; - done before me.sll_protocol = htons(ETH_P_ARP); xbind(sock_fd, (struct sockaddr *) &me, sizeof(me)); { socklen_t alen = sizeof(me); getsockname(sock_fd, (struct sockaddr *) &me, &alen); //never happens: //if (getsockname(sock_fd, (struct sockaddr *) &me, &alen) == -1) // bb_perror_msg_and_die("getsockname"); } if (me.sll_halen == 0) { bb_error_msg(err_str, "is not ARPable (no ll address)"); return (option_mask32 & DAD ? 0 : 2); } he = me; memset(he.sll_addr, -1, he.sll_halen); if (!(option_mask32 & QUIET)) { /* inet_ntoa uses static storage, can't use in same printf */ printf("ARPING to %s", inet_ntoa(dst)); printf(" from %s via %s\n", inet_ntoa(src), device); } signal_SA_RESTART_empty_mask(SIGINT, (void (*)(int))finish); signal_SA_RESTART_empty_mask(SIGALRM, (void (*)(int))catcher); catcher(); packet = xmalloc(4096); while (1) { sigset_t sset, osset; struct sockaddr_ll from; socklen_t alen = sizeof(from); int cc; cc = recvfrom(sock_fd, packet, 4096, 0, (struct sockaddr *) &from, &alen); if (cc < 0) { bb_perror_msg("recvfrom"); continue; } sigemptyset(&sset); sigaddset(&sset, SIGALRM); sigaddset(&sset, SIGINT); sigprocmask(SIG_BLOCK, &sset, &osset); recv_pack(packet, cc, &from); sigprocmask(SIG_SETMASK, &osset, NULL); } }
int more_main(int argc UNUSED_PARAM, char **argv) { int c = c; /* for compiler */ int lines; int input = 0; int spaces = 0; int please_display_more_prompt; struct stat st; FILE *file; FILE *cin; int len; unsigned terminal_width; unsigned terminal_height; INIT_G(); argv++; /* Another popular pager, most, detects when stdout * is not a tty and turns into cat. This makes sense. */ if (!isatty(STDOUT_FILENO)) return bb_cat(argv); cin = fopen_for_read(CURRENT_TTY); if (!cin) return bb_cat(argv); if (ENABLE_FEATURE_USE_TERMIOS) { cin_fileno = fileno(cin); getTermSettings(cin_fileno, &initial_settings); new_settings = initial_settings; new_settings.c_lflag &= ~(ICANON | ECHO); new_settings.c_cc[VMIN] = 1; new_settings.c_cc[VTIME] = 0; setTermSettings(cin_fileno, &new_settings); bb_signals(0 + (1 << SIGINT) + (1 << SIGQUIT) + (1 << SIGTERM) , gotsig); } do { file = stdin; if (*argv) { file = fopen_or_warn(*argv, "r"); if (!file) continue; } st.st_size = 0; fstat(fileno(file), &st); please_display_more_prompt = 0; /* never returns w, h <= 1 */ get_terminal_width_height(fileno(cin), &terminal_width, &terminal_height); terminal_height -= 1; len = 0; lines = 0; while (spaces || (c = getc(file)) != EOF) { int wrap; if (spaces) spaces--; loop_top: if (input != 'r' && please_display_more_prompt) { len = printf("--More-- "); if (st.st_size != 0) { uoff_t d = (uoff_t)st.st_size / 100; if (d == 0) d = 1; len += printf("(%u%% of %"OFF_FMT"u bytes)", (int) ((uoff_t)ftello(file) / d), st.st_size); } fflush_all(); /* * We've just displayed the "--More--" prompt, so now we need * to get input from the user. */ for (;;) { input = getc(cin); input = tolower(input); if (!ENABLE_FEATURE_USE_TERMIOS) printf("\033[A"); /* cursor up */ /* Erase the last message */ printf("\r%*s\r", len, ""); /* Due to various multibyte escape * sequences, it's not ok to accept * any input as a command to scroll * the screen. We only allow known * commands, else we show help msg. */ if (input == ' ' || input == '\n' || input == 'q' || input == 'r') break; len = printf("(Enter:next line Space:next page Q:quit R:show the rest)"); } len = 0; lines = 0; please_display_more_prompt = 0; if (input == 'q') goto end; /* The user may have resized the terminal. * Re-read the dimensions. */ if (ENABLE_FEATURE_USE_TERMIOS) { get_terminal_width_height(cin_fileno, &terminal_width, &terminal_height); terminal_height -= 1; } } /* Crudely convert tabs into spaces, which are * a bajillion times easier to deal with. */ if (c == '\t') { spaces = ((unsigned)~len) % CONVERTED_TAB_SIZE; c = ' '; } /* * There are two input streams to worry about here: * * c : the character we are reading from the file being "mored" * input: a character received from the keyboard * * If we hit a newline in the _file_ stream, we want to test and * see if any characters have been hit in the _input_ stream. This * allows the user to quit while in the middle of a file. */ wrap = (++len > terminal_width); if (c == '\n' || wrap) { /* Then outputting this character * will move us to a new line. */ if (++lines >= terminal_height || input == '\n') please_display_more_prompt = 1; len = 0; } if (c != '\n' && wrap) { /* Then outputting this will also put a character on * the beginning of that new line. Thus we first want to * display the prompt (if any), so we skip the putchar() * and go back to the top of the loop, without reading * a new character. */ putchar('\n'); goto loop_top; } /* My small mind cannot fathom backspaces and UTF-8 */ putchar(c); die_if_ferror_stdout(); /* if tty was destroyed (closed xterm, etc) */ } fclose(file); fflush_all(); } while (*argv && *++argv); end: setTermSettings(cin_fileno, &initial_settings); return 0; }
int rpm_main(int argc, char **argv) { int opt, func = 0; INIT_G(); G.pagesize = getpagesize(); while ((opt = getopt(argc, argv, "iqpldc")) != -1) { switch (opt) { case 'i': /* First arg: Install mode, with q: Information */ if (!func) func = rpm_install; else func |= rpm_query_info; break; case 'q': /* First arg: Query mode */ if (func) bb_show_usage(); func = rpm_query; break; case 'p': /* Query a package (IOW: .rpm file, we are not querying RPMDB) */ func |= rpm_query_package; break; case 'l': /* List files in a package */ func |= rpm_query_list; break; case 'd': /* List doc files in a package (implies -l) */ func |= rpm_query_list; func |= rpm_query_list_doc; break; case 'c': /* List config files in a package (implies -l) */ func |= rpm_query_list; func |= rpm_query_list_config; break; default: bb_show_usage(); } } argv += optind; //argc -= optind; if (!argv[0]) { bb_show_usage(); } for (;;) { int rpm_fd; const char *source_rpm; rpm_fd = rpm_gettags(*argv); print_all_tags(); source_rpm = rpm_getstr0(TAG_SOURCERPM); if (func & rpm_install) { /* -i (and not -qi) */ /* Backup any config files */ loop_through_files(TAG_BASENAMES, fileaction_dobackup); /* Extact the archive */ extract_cpio(rpm_fd, source_rpm); /* Set the correct file uid/gid's */ loop_through_files(TAG_BASENAMES, fileaction_setowngrp); } else if ((func & (rpm_query|rpm_query_package)) == (rpm_query|rpm_query_package)) { /* -qp */ if (!(func & (rpm_query_info|rpm_query_list))) { /* If just a straight query, just give package name */ printf("%s-%s-%s\n", rpm_getstr0(TAG_NAME), rpm_getstr0(TAG_VERSION), rpm_getstr0(TAG_RELEASE)); } if (func & rpm_query_info) { /* Do the nice printout */ time_t bdate_time; struct tm *bdate_ptm; char bdatestring[50]; const char *p; printf("%-12s: %s\n", "Name" , rpm_getstr0(TAG_NAME)); /* TODO compat: add "Epoch" here */ printf("%-12s: %s\n", "Version" , rpm_getstr0(TAG_VERSION)); printf("%-12s: %s\n", "Release" , rpm_getstr0(TAG_RELEASE)); /* add "Architecture" */ /* printf("%-12s: %s\n", "Install Date", "(not installed)"); - we don't know */ printf("%-12s: %s\n", "Group" , rpm_getstr0(TAG_GROUP)); printf("%-12s: %d\n", "Size" , rpm_getint(TAG_SIZE, 0)); printf("%-12s: %s\n", "License" , rpm_getstr0(TAG_LICENSE)); /* add "Signature" */ printf("%-12s: %s\n", "Source RPM" , source_rpm ? source_rpm : "(none)"); bdate_time = rpm_getint(TAG_BUILDTIME, 0); bdate_ptm = localtime(&bdate_time); strftime(bdatestring, 50, "%a %d %b %Y %T %Z", bdate_ptm); printf("%-12s: %s\n", "Build Date" , bdatestring); printf("%-12s: %s\n", "Build Host" , rpm_getstr0(TAG_BUILDHOST)); p = rpm_getstr0(TAG_PREFIXS); printf("%-12s: %s\n", "Relocations" , p ? p : "(not relocatable)"); /* add "Packager" */ p = rpm_getstr0(TAG_VENDOR); if (p) /* rpm 4.13.0.1 does not show "(none)" for Vendor: */ printf("%-12s: %s\n", "Vendor" , p); p = rpm_getstr0(TAG_URL); if (p) /* rpm 4.13.0.1 does not show "(none)"/"(null)" for URL: */ printf("%-12s: %s\n", "URL" , p); printf("%-12s: %s\n", "Summary" , rpm_getstr0(TAG_SUMMARY)); printf("Description :\n%s\n", rpm_getstr0(TAG_DESCRIPTION)); } if (func & rpm_query_list) { int count, it, flags; count = rpm_getcount(TAG_BASENAMES); for (it = 0; it < count; it++) { flags = rpm_getint(TAG_FILEFLAGS, it); switch (func & (rpm_query_list_doc|rpm_query_list_config)) { case rpm_query_list_doc: if (!(flags & RPMFILE_DOC)) continue; break; case rpm_query_list_config: if (!(flags & RPMFILE_CONFIG)) continue; break; case rpm_query_list_doc|rpm_query_list_config: if (!(flags & (RPMFILE_CONFIG|RPMFILE_DOC))) continue; break; } printf("%s%s\n", rpm_getstr(TAG_DIRNAMES, rpm_getint(TAG_DIRINDEXES, it)), rpm_getstr(TAG_BASENAMES, it)); } } } else { /* Unsupported (help text shows what we support) */ bb_show_usage(); } if (!*++argv) break; munmap(G.map, G.mapsize); free(G.mytags); close(rpm_fd); } return 0; }
int wget_main(int argc UNUSED_PARAM, char **argv) { char buf[512]; struct host_info server, target; len_and_sockaddr *lsa; unsigned opt; int redir_limit; char *proxy = NULL; char *dir_prefix = NULL; #if ENABLE_FEATURE_WGET_LONG_OPTIONS char *post_data; char *extra_headers = NULL; llist_t *headers_llist = NULL; #endif FILE *sfp; /* socket to web/ftp server */ FILE *dfp; /* socket to ftp server (data) */ char *fname_out; /* where to direct output (-O) */ int output_fd = -1; bool use_proxy; /* Use proxies if env vars are set */ const char *proxy_flag = "on"; /* Use proxies if env vars are set */ const char *user_agent = "Wget";/* "User-Agent" header field */ static const char keywords[] ALIGN1 = "content-length\0""transfer-encoding\0""chunked\0""location\0"; enum { KEY_content_length = 1, KEY_transfer_encoding, KEY_chunked, KEY_location }; #if ENABLE_FEATURE_WGET_LONG_OPTIONS static const char wget_longopts[] ALIGN1 = /* name, has_arg, val */ "continue\0" No_argument "c" "spider\0" No_argument "s" "quiet\0" No_argument "q" "output-document\0" Required_argument "O" "directory-prefix\0" Required_argument "P" "proxy\0" Required_argument "Y" "user-agent\0" Required_argument "U" /* Ignored: */ // "tries\0" Required_argument "t" // "timeout\0" Required_argument "T" /* Ignored (we always use PASV): */ "passive-ftp\0" No_argument "\xff" "header\0" Required_argument "\xfe" "post-data\0" Required_argument "\xfd" ; #endif INIT_G(); #if ENABLE_FEATURE_WGET_LONG_OPTIONS applet_long_options = wget_longopts; #endif /* server.allocated = target.allocated = NULL; */ opt_complementary = "-1" IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::"); opt = getopt32(argv, "csqO:P:Y:U:" /*ignored:*/ "t:T:", &fname_out, &dir_prefix, &proxy_flag, &user_agent, NULL, /* -t RETRIES */ NULL /* -T NETWORK_READ_TIMEOUT */ IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist) IF_FEATURE_WGET_LONG_OPTIONS(, &post_data) ); #if ENABLE_FEATURE_WGET_LONG_OPTIONS if (headers_llist) { int size = 1; char *cp; llist_t *ll = headers_llist; while (ll) { size += strlen(ll->data) + 2; ll = ll->link; } extra_headers = cp = xmalloc(size); while (headers_llist) { cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist)); } } #endif /* TODO: compat issue: should handle "wget URL1 URL2..." */ target.user = NULL; parse_url(argv[optind], &target); /* Use the proxy if necessary */ use_proxy = (strcmp(proxy_flag, "off") != 0); if (use_proxy) { proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy"); if (proxy && proxy[0]) { parse_url(proxy, &server); } else { use_proxy = 0; } } if (!use_proxy) { server.port = target.port; if (ENABLE_FEATURE_IPV6) { server.host = xstrdup(target.host); } else { server.host = target.host; } } if (ENABLE_FEATURE_IPV6) strip_ipv6_scope_id(target.host); /* Guess an output filename, if there was no -O FILE */ if (!(opt & WGET_OPT_OUTNAME)) { fname_out = bb_get_last_path_component_nostrip(target.path); /* handle "wget http://kernel.org//" */ if (fname_out[0] == '/' || !fname_out[0]) fname_out = (char*)"index.html"; /* -P DIR is considered only if there was no -O FILE */ if (dir_prefix) fname_out = concat_path_file(dir_prefix, fname_out); } else { if (LONE_DASH(fname_out)) { /* -O - */ output_fd = 1; opt &= ~WGET_OPT_CONTINUE; } } #if ENABLE_FEATURE_WGET_STATUSBAR G.curfile = bb_get_last_path_component_nostrip(fname_out); #endif /* Impossible? if ((opt & WGET_OPT_CONTINUE) && !fname_out) bb_error_msg_and_die("can't specify continue (-c) without a filename (-O)"); */ /* Determine where to start transfer */ if (opt & WGET_OPT_CONTINUE) { output_fd = open(fname_out, O_WRONLY); if (output_fd >= 0) { G.beg_range = xlseek(output_fd, 0, SEEK_END); } /* File doesn't exist. We do not create file here yet. * We are not sure it exists on remove side */ } redir_limit = 5; resolve_lsa: lsa = xhost2sockaddr(server.host, server.port); if (!(opt & WGET_OPT_QUIET)) { char *s = xmalloc_sockaddr2dotted(&lsa->u.sa); fprintf(stderr, "Connecting to %s (%s)\n", server.host, s); free(s); } establish_session: if (use_proxy || !target.is_ftp) { /* * HTTP session */ char *str; int status; /* Open socket to http server */ sfp = open_socket(lsa); /* Send HTTP request */ if (use_proxy) { fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n", target.is_ftp ? "f" : "ht", target.host, target.path); } else { if (opt & WGET_OPT_POST_DATA) fprintf(sfp, "POST /%s HTTP/1.1\r\n", target.path); else fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path); } fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n", target.host, user_agent); #if ENABLE_FEATURE_WGET_AUTHENTICATION if (target.user) { fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6, base64enc_512(buf, target.user)); } if (use_proxy && server.user) { fprintf(sfp, "Proxy-Authorization: Basic %s\r\n", base64enc_512(buf, server.user)); } #endif if (G.beg_range) fprintf(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range); #if ENABLE_FEATURE_WGET_LONG_OPTIONS if (extra_headers) fputs(extra_headers, sfp); if (opt & WGET_OPT_POST_DATA) { char *estr = URL_escape(post_data); fprintf(sfp, "Content-Type: application/x-www-form-urlencoded\r\n"); fprintf(sfp, "Content-Length: %u\r\n" "\r\n" "%s", (int) strlen(estr), estr); /*fprintf(sfp, "Connection: Keep-Alive\r\n\r\n");*/ /*fprintf(sfp, "%s\r\n", estr);*/ free(estr); } else #endif { /* If "Connection:" is needed, document why */ fprintf(sfp, /* "Connection: close\r\n" */ "\r\n"); } /* * Retrieve HTTP response line and check for "200" status code. */ read_response: if (fgets(buf, sizeof(buf), sfp) == NULL) bb_error_msg_and_die("no response from server"); str = buf; str = skip_non_whitespace(str); str = skip_whitespace(str); // FIXME: no error check // xatou wouldn't work: "200 OK" status = atoi(str); switch (status) { case 0: case 100: while (gethdr(buf, sizeof(buf), sfp /*, &n*/) != NULL) /* eat all remaining headers */; goto read_response; case 200: /* Response 204 doesn't say "null file", it says "metadata has changed but data didn't": "10.2.5 204 No Content The server has fulfilled the request but does not need to return an entity-body, and might want to return updated metainformation. The response MAY include new or updated metainformation in the form of entity-headers, which if present SHOULD be associated with the requested variant. If the client is a user agent, it SHOULD NOT change its document view from that which caused the request to be sent. This response is primarily intended to allow input for actions to take place without causing a change to the user agent's active document view, although any new or updated metainformation SHOULD be applied to the document currently in the user agent's active view. The 204 response MUST NOT include a message-body, and thus is always terminated by the first empty line after the header fields." However, in real world it was observed that some web servers (e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero. */ case 204: break; case 300: /* redirection */ case 301: case 302: case 303: break; case 206: if (G.beg_range) break; /* fall through */ default: bb_error_msg_and_die("server returned error: %s", sanitize_string(buf)); } /* * Retrieve HTTP headers. */ while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) { /* gethdr converted "FOO:" string to lowercase */ smalluint key; /* strip trailing whitespace */ char *s = strchrnul(str, '\0') - 1; while (s >= str && (*s == ' ' || *s == '\t')) { *s = '\0'; s--; } key = index_in_strings(keywords, buf) + 1; if (key == KEY_content_length) { G.content_len = BB_STRTOOFF(str, NULL, 10); if (G.content_len < 0 || errno) { bb_error_msg_and_die("content-length %s is garbage", sanitize_string(str)); } G.got_clen = 1; continue; } if (key == KEY_transfer_encoding) { if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked) bb_error_msg_and_die("transfer encoding '%s' is not supported", sanitize_string(str)); G.chunked = G.got_clen = 1; } if (key == KEY_location && status >= 300) { if (--redir_limit == 0) bb_error_msg_and_die("too many redirections"); fclose(sfp); G.got_clen = 0; G.chunked = 0; if (str[0] == '/') /* free(target.allocated); */ target.path = /* target.allocated = */ xstrdup(str+1); /* lsa stays the same: it's on the same server */ else { parse_url(str, &target); if (!use_proxy) { server.host = target.host; /* strip_ipv6_scope_id(target.host); - no! */ /* we assume remote never gives us IPv6 addr with scope id */ server.port = target.port; free(lsa); goto resolve_lsa; } /* else: lsa stays the same: we use proxy */ } goto establish_session; } } // if (status >= 300) // bb_error_msg_and_die("bad redirection (no Location: header from server)"); /* For HTTP, data is pumped over the same connection */ dfp = sfp; } else { /* * FTP session */ sfp = prepare_ftp_session(&dfp, &target, lsa); } if (opt & WGET_OPT_SPIDER) { if (ENABLE_FEATURE_CLEAN_UP) fclose(sfp); return EXIT_SUCCESS; } if (output_fd < 0) { int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL; /* compat with wget: -O FILE can overwrite */ if (opt & WGET_OPT_OUTNAME) o_flags = O_WRONLY | O_CREAT | O_TRUNC; output_fd = xopen(fname_out, o_flags); } retrieve_file_data(dfp, output_fd); xclose(output_fd); if (dfp != sfp) { /* It's ftp. Close it properly */ fclose(dfp); if (ftpcmd(NULL, NULL, sfp, buf) != 226) bb_error_msg_and_die("ftp error: %s", sanitize_string(buf+4)); /* ftpcmd("QUIT", NULL, sfp, buf); - why bother? */ } return EXIT_SUCCESS; }
int stty_main(int argc UNUSED_PARAM, char **argv) { struct termios mode; void (*output_func)(const struct termios *, int); const char *file_name = NULL; int display_all = 0; int stty_state; int k; INIT_G(); stty_state = STTY_noargs; output_func = do_display; /* First pass: only parse/verify command line params */ k = 0; while (argv[++k]) { const struct mode_info *mp; const struct control_info *cp; const char *arg = argv[k]; const char *argnext = argv[k+1]; int param; if (arg[0] == '-') { int i; mp = find_mode(arg+1); if (mp) { if (!(mp->flags & REV)) goto invalid_argument; stty_state &= ~STTY_noargs; continue; } /* It is an option - parse it */ i = 0; while (arg[++i]) { switch (arg[i]) { case 'a': stty_state |= STTY_verbose_output; output_func = do_display; display_all = 1; break; case 'g': stty_state |= STTY_recoverable_output; output_func = display_recoverable; break; case 'F': if (file_name) bb_error_msg_and_die("only one device may be specified"); file_name = &arg[i+1]; /* "-Fdevice" ? */ if (!file_name[0]) { /* nope, "-F device" */ int p = k+1; /* argv[p] is argnext */ file_name = argnext; if (!file_name) bb_error_msg_and_die(bb_msg_requires_arg, "-F"); /* remove -F param from arg[vc] */ while (argv[p]) { argv[p] = argv[p+1]; ++p; } } goto end_option; default: goto invalid_argument; } } end_option: continue; } mp = find_mode(arg); if (mp) { stty_state &= ~STTY_noargs; continue; } cp = find_control(arg); if (cp) { if (!argnext) bb_error_msg_and_die(bb_msg_requires_arg, arg); /* called for the side effect of xfunc death only */ set_control_char_or_die(cp, argnext, &mode); stty_state &= ~STTY_noargs; ++k; continue; } param = find_param(arg); if (param & param_need_arg) { if (!argnext) bb_error_msg_and_die(bb_msg_requires_arg, arg); ++k; } switch (param) { #ifdef HAVE_C_LINE case param_line: # ifndef TIOCGWINSZ xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes); break; # endif /* else fall-through */ #endif #ifdef TIOCGWINSZ case param_rows: case param_cols: case param_columns: xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes); break; case param_size: #endif case param_speed: break; case param_ispeed: /* called for the side effect of xfunc death only */ set_speed_or_die(input_speed, argnext, &mode); break; case param_ospeed: /* called for the side effect of xfunc death only */ set_speed_or_die(output_speed, argnext, &mode); break; default: if (recover_mode(arg, &mode) == 1) break; if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break; invalid_argument: bb_error_msg_and_die("invalid argument '%s'", arg); } stty_state &= ~STTY_noargs; } /* Specifying both -a and -g is an error */ if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) == (STTY_verbose_output | STTY_recoverable_output)) bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive"); /* Specifying -a or -g with non-options is an error */ if (!(stty_state & STTY_noargs) && (stty_state & (STTY_verbose_output | STTY_recoverable_output)) ) { bb_error_msg_and_die("modes may not be set when specifying an output style"); } /* Now it is safe to start doing things */ if (file_name) { G.device_name = file_name; xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO); ndelay_off(STDIN_FILENO); } /* Initialize to all zeroes so there is no risk memcmp will report a spurious difference in an uninitialized portion of the structure */ memset(&mode, 0, sizeof(mode)); if (tcgetattr(STDIN_FILENO, &mode)) perror_on_device_and_die("%s"); if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) { get_terminal_width_height(STDOUT_FILENO, &G.max_col, NULL); output_func(&mode, display_all); return EXIT_SUCCESS; } /* Second pass: perform actions */ k = 0; while (argv[++k]) { const struct mode_info *mp; const struct control_info *cp; const char *arg = argv[k]; const char *argnext = argv[k+1]; int param; if (arg[0] == '-') { mp = find_mode(arg+1); if (mp) { set_mode(mp, 1 /* reversed */, &mode); stty_state |= STTY_require_set_attr; } /* It is an option - already parsed. Skip it */ continue; } mp = find_mode(arg); if (mp) { set_mode(mp, 0 /* non-reversed */, &mode); stty_state |= STTY_require_set_attr; continue; } cp = find_control(arg); if (cp) { ++k; set_control_char_or_die(cp, argnext, &mode); stty_state |= STTY_require_set_attr; continue; } param = find_param(arg); if (param & param_need_arg) { ++k; } switch (param) { #ifdef HAVE_C_LINE case param_line: mode.c_line = xatoul_sfx(argnext, stty_suffixes); stty_state |= STTY_require_set_attr; break; #endif #ifdef TIOCGWINSZ case param_cols: case param_columns: set_window_size(-1, xatoul_sfx(argnext, stty_suffixes)); break; case param_size: display_window_size(0); break; case param_rows: set_window_size(xatoul_sfx(argnext, stty_suffixes), -1); break; #endif case param_speed: display_speed(&mode, 0); break; case param_ispeed: set_speed_or_die(input_speed, argnext, &mode); stty_state |= (STTY_require_set_attr | STTY_speed_was_set); break; case param_ospeed: set_speed_or_die(output_speed, argnext, &mode); stty_state |= (STTY_require_set_attr | STTY_speed_was_set); break; default: if (recover_mode(arg, &mode) == 1) stty_state |= STTY_require_set_attr; else { /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */ set_speed_or_die(both_speeds, arg, &mode); stty_state |= (STTY_require_set_attr | STTY_speed_was_set); } /* else - impossible (caught in the first pass): bb_error_msg_and_die("invalid argument '%s'", arg); */ } } if (stty_state & STTY_require_set_attr) { struct termios new_mode; if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode)) perror_on_device_and_die("%s"); /* POSIX (according to Zlotnick's book) tcsetattr returns zero if it performs *any* of the requested operations. This means it can report 'success' when it has actually failed to perform some proper subset of the requested operations. To detect this partial failure, get the current terminal attributes and compare them to the requested ones */ /* Initialize to all zeroes so there is no risk memcmp will report a spurious difference in an uninitialized portion of the structure */ memset(&new_mode, 0, sizeof(new_mode)); if (tcgetattr(STDIN_FILENO, &new_mode)) perror_on_device_and_die("%s"); if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) { #ifdef CIBAUD /* SunOS 4.1.3 (at least) has the problem that after this sequence, tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2); sometimes (m1 != m2). The only difference is in the four bits of the c_cflag field corresponding to the baud rate. To save Sun users a little confusion, don't report an error if this happens. But suppress the error only if we haven't tried to set the baud rate explicitly -- otherwise we'd never give an error for a true failure to set the baud rate */ new_mode.c_cflag &= (~CIBAUD); if ((stty_state & STTY_speed_was_set) || memcmp(&mode, &new_mode, sizeof(mode)) != 0) #endif perror_on_device_and_die("%s: cannot perform all requested operations"); } } return EXIT_SUCCESS; }
int sendmail_main(int argc UNUSED_PARAM, char **argv) { char *opt_connect = opt_connect; char *opt_from = NULL; char *s; llist_t *list = NULL; char *host = sane_address(safe_gethostname()); unsigned nheaders = 0; int code; enum { HDR_OTHER = 0, HDR_TOCC, HDR_BCC, } last_hdr = 0; int check_hdr; int has_to = 0; enum { //--- standard options OPT_t = 1 << 0, // read message for recipients, append them to those on cmdline OPT_f = 1 << 1, // sender address OPT_o = 1 << 2, // various options. -oi IMPLIED! others are IGNORED! OPT_i = 1 << 3, // IMPLIED! //--- BB specific options OPT_w = 1 << 4, // network timeout OPT_H = 1 << 5, // use external connection helper OPT_S = 1 << 6, // specify connection string OPT_a = 1 << 7, // authentication tokens OPT_v = 1 << 8, // verbosity }; // init global variables INIT_G(); // save initial stdin since body is piped! xdup2(STDIN_FILENO, 3); G.fp0 = xfdopen_for_read(3); // parse options // -v is a counter, -H and -S are mutually exclusive, -a is a list opt_complementary = "vv:w+:H--S:S--H:a::"; // N.B. since -H and -S are mutually exclusive they do not interfere in opt_connect // -a is for ssmtp (http://downloads.openwrt.org/people/nico/man/man8/ssmtp.8.html) compatibility, // it is still under development. opts = getopt32(argv, "tf:o:iw:H:S:a::v", &opt_from, NULL, &timeout, &opt_connect, &opt_connect, &list, &verbose); //argc -= optind; argv += optind; // process -a[upm]<token> options if ((opts & OPT_a) && !list) bb_show_usage(); while (list) { char *a = (char *) llist_pop(&list); if ('u' == a[0]) G.user = xstrdup(a+1); if ('p' == a[0]) G.pass = xstrdup(a+1); // N.B. we support only AUTH LOGIN so far //if ('m' == a[0]) // G.method = xstrdup(a+1); } // N.B. list == NULL here //bb_info_msg("OPT[%x] AU[%s], AP[%s], AM[%s], ARGV[%s]", opts, au, ap, am, *argv); // connect to server // connection helper ordered? -> if (opts & OPT_H) { const char *args[] = { "sh", "-c", opt_connect, NULL }; // plug it in launch_helper(args); // Now: // our stdout will go to helper's stdin, // helper's stdout will be available on our stdin. // Wait for initial server message. // If helper (such as openssl) invokes STARTTLS, the initial 220 // is swallowed by helper (and not repeated after TLS is initiated). // We will send NOOP cmd to server and check the response. // We should get 220+250 on plain connection, 250 on STARTTLSed session. // // The problem here is some servers delay initial 220 message, // and consider client to be a spammer if it starts sending cmds // before 220 reached it. The code below is unsafe in this regard: // in non-STARTTLSed case, we potentially send NOOP before 220 // is sent by server. // Ideas? (--delay SECS opt? --assume-starttls-helper opt?) code = smtp_check("NOOP", -1); if (code == 220) // we got 220 - this is not STARTTLSed connection, // eat 250 response to our NOOP smtp_check(NULL, 250); else if (code != 250) bb_error_msg_and_die("SMTP init failed"); } else { // vanilla connection int fd; // host[:port] not explicitly specified? -> use $SMTPHOST // no $SMTPHOST? -> use localhost if (!(opts & OPT_S)) { opt_connect = getenv("SMTPHOST"); if (!opt_connect) opt_connect = (char *)"127.0.0.1"; } // do connect fd = create_and_connect_stream_or_die(opt_connect, 25); // and make ourselves a simple IO filter xmove_fd(fd, STDIN_FILENO); xdup2(STDIN_FILENO, STDOUT_FILENO); // Wait for initial server 220 message smtp_check(NULL, 220); } // we should start with modern EHLO if (250 != smtp_checkp("EHLO %s", host, -1)) smtp_checkp("HELO %s", host, 250); // perform authentication if (opts & OPT_a) { smtp_check("AUTH LOGIN", 334); // we must read credentials unless they are given via -a[up] options if (!G.user || !G.pass) get_cred_or_die(4); encode_base64(NULL, G.user, NULL); smtp_check("", 334); encode_base64(NULL, G.pass, NULL); smtp_check("", 235); } // set sender // N.B. we have here a very loosely defined algorythm // since sendmail historically offers no means to specify secrets on cmdline. // 1) server can require no authentication -> // we must just provide a (possibly fake) reply address. // 2) server can require AUTH -> // we must provide valid username and password along with a (possibly fake) reply address. // For the sake of security username and password are to be read either from console or from a secured file. // Since reading from console may defeat usability, the solution is either to read from a predefined // file descriptor (e.g. 4), or again from a secured file. // got no sender address? use auth name, then UID username as a last resort if (!opt_from) { opt_from = xasprintf("%s@%s", G.user ? G.user : xuid2uname(getuid()), xgethostbyname(host)->h_name); } free(host); smtp_checkp("MAIL FROM:<%s>", opt_from, 250); // process message // read recipients from message and add them to those given on cmdline. // this means we scan stdin for To:, Cc:, Bcc: lines until an empty line // and then use the rest of stdin as message body code = 0; // set "analyze headers" mode while ((s = xmalloc_fgetline(G.fp0)) != NULL) { dump: // put message lines doubling leading dots if (code) { // escape leading dots // N.B. this feature is implied even if no -i (-oi) switch given // N.B. we need to escape the leading dot regardless of // whether it is single or not character on the line if ('.' == s[0] /*&& '\0' == s[1] */) bb_putchar('.'); // dump read line send_r_n(s); free(s); continue; } // analyze headers // To: or Cc: headers add recipients check_hdr = (0 == strncasecmp("To:", s, 3)); has_to |= check_hdr; if (opts & OPT_t) { if (check_hdr || 0 == strncasecmp("Bcc:" + 1, s, 3)) { rcptto_list(s+3); last_hdr = HDR_TOCC; goto addheader; } // Bcc: header adds blind copy (hidden) recipient if (0 == strncasecmp("Bcc:", s, 4)) { rcptto_list(s+4); free(s); last_hdr = HDR_BCC; continue; // N.B. Bcc: vanishes from headers! } } check_hdr = (list && isspace(s[0])); if (strchr(s, ':') || check_hdr) { // other headers go verbatim // N.B. RFC2822 2.2.3 "Long Header Fields" allows for headers to occupy several lines. // Continuation is denoted by prefixing additional lines with whitespace(s). // Thanks (stefan.seyfried at googlemail.com) for pointing this out. if (check_hdr && last_hdr != HDR_OTHER) { rcptto_list(s+1); if (last_hdr == HDR_BCC) continue; // N.B. Bcc: vanishes from headers! } else { last_hdr = HDR_OTHER; } addheader: // N.B. we allow MAX_HEADERS generic headers at most to prevent attacks if (MAX_HEADERS && ++nheaders >= MAX_HEADERS) goto bail; llist_add_to_end(&list, s); } else { // a line without ":" (an empty line too, by definition) doesn't look like a valid header // so stop "analyze headers" mode reenter: // put recipients specified on cmdline check_hdr = 1; while (*argv) { char *t = sane_address(*argv); rcptto(t); //if (MAX_HEADERS && ++nheaders >= MAX_HEADERS) // goto bail; if (!has_to) { const char *hdr; if (check_hdr && argv[1]) hdr = "To: %s,"; else if (check_hdr) hdr = "To: %s"; else if (argv[1]) hdr = "To: %s," + 3; else hdr = "To: %s" + 3; llist_add_to_end(&list, xasprintf(hdr, t)); check_hdr = 0; } argv++; } // enter "put message" mode // N.B. DATA fails iff no recipients were accepted (or even provided) // in this case just bail out gracefully if (354 != smtp_check("DATA", -1)) goto bail; // dump the headers while (list) { send_r_n((char *) llist_pop(&list)); } // stop analyzing headers code++; // N.B. !s means: we read nothing, and nothing to be read in the future. // just dump empty line and break the loop if (!s) { send_r_n(""); break; } // go dump message body // N.B. "s" already contains the first non-header line, so pretend we read it from input goto dump; } } // odd case: we didn't stop "analyze headers" mode -> message body is empty. Reenter the loop // N.B. after reenter code will be > 0 if (!code) goto reenter; // finalize the message smtp_check(".", 250); bail: // ... and say goodbye smtp_check("QUIT", 221); // cleanup if (ENABLE_FEATURE_CLEAN_UP) fclose(G.fp0); return EXIT_SUCCESS; }
int more_main(int argc, char **argv) { int c, lines, input = 0; int please_display_more_prompt = 0; struct stat st; FILE *file; FILE *cin; int len, page_height; int terminal_width; int terminal_height; INIT_G(); argv++; /* Another popular pager, most, detects when stdout * is not a tty and turns into cat. This makes sense. */ if (!isatty(STDOUT_FILENO)) return bb_cat(argv); cin = fopen(CURRENT_TTY, "r"); if (!cin) return bb_cat(argv); #if ENABLE_FEATURE_USE_TERMIOS cin_fileno = fileno(cin); getTermSettings(cin_fileno, &initial_settings); new_settings = initial_settings; new_settings.c_lflag &= ~ICANON; new_settings.c_lflag &= ~ECHO; new_settings.c_cc[VMIN] = 1; new_settings.c_cc[VTIME] = 0; setTermSettings(cin_fileno, &new_settings); signal(SIGINT, gotsig); signal(SIGQUIT, gotsig); signal(SIGTERM, gotsig); #endif please_display_more_prompt = 2; do { file = stdin; if (*argv) { file = fopen_or_warn(*argv, "r"); if (!file) continue; } st.st_size = 0; fstat(fileno(file), &st); please_display_more_prompt &= ~1; /* never returns w, h <= 1 */ get_terminal_width_height(fileno(cin), &terminal_width, &terminal_height); terminal_width -= 1; terminal_height -= 1; len = 0; lines = 0; page_height = terminal_height; while ((c = getc(file)) != EOF) { if ((please_display_more_prompt & 3) == 3) { len = printf("--More-- "); if (/*file != stdin &&*/ st.st_size > 0) { len += printf("(%d%% of %"OFF_FMT"d bytes)", (int) (ftello(file)*100 / st.st_size), st.st_size); } fflush(stdout); /* * We've just displayed the "--More--" prompt, so now we need * to get input from the user. */ input = getc(cin); #if !ENABLE_FEATURE_USE_TERMIOS printf("\033[A"); /* up cursor */ #endif /* Erase the "More" message */ printf("\r%*s\r", len, ""); len = 0; lines = 0; /* Bottom line on page will become top line * after one page forward. Thus -1: */ page_height = terminal_height - 1; please_display_more_prompt &= ~1; if (input == 'q') goto end; } /* * There are two input streams to worry about here: * * c : the character we are reading from the file being "mored" * input: a character received from the keyboard * * If we hit a newline in the _file_ stream, we want to test and * see if any characters have been hit in the _input_ stream. This * allows the user to quit while in the middle of a file. */ if (c == '\n') { /* increment by just one line if we are at * the end of this line */ if (input == '\n') please_display_more_prompt |= 1; /* Adjust the terminal height for any overlap, so that * no lines get lost off the top. */ if (len >= terminal_width) { int quot, rem; quot = len / terminal_width; rem = len - (quot * terminal_width); page_height -= (quot - 1); if (rem) page_height--; } if (++lines >= page_height) { please_display_more_prompt |= 1; } len = 0; } /* * If we just read a newline from the file being 'mored' and any * key other than a return is hit, scroll by one page */ putc(c, stdout); /* My small mind cannot fathom tabs, backspaces, * and UTF-8 */ len++; } fclose(file); fflush(stdout); } while (*argv && *++argv); end: setTermSettings(cin_fileno, &initial_settings); return 0; }