int uniq_main(int argc ATTRIBUTE_UNUSED, char **argv) { FILE *in, *out; unsigned long dups, skip_fields, skip_chars, i; const char *s0, *e0, *s1, *e1, *input_filename; unsigned opt; enum { OPT_c = 0x1, OPT_d = 0x2, OPT_u = 0x4, OPT_f = 0x8, OPT_s = 0x10, }; skip_fields = skip_chars = 0; opt = getopt32(argv, "cduf:s:", &s0, &s1); if (opt & OPT_f) skip_fields = xatoul(s0); if (opt & OPT_s) skip_chars = xatoul(s1); argv += optind; input_filename = *argv; in = xgetoptfile_uniq_s(argv, 0); if (*argv) { ++argv; } out = xgetoptfile_uniq_s(argv, 2); if (*argv && argv[1]) { bb_show_usage(); } s1 = e1 = NULL; /* prime the pump */ do { s0 = s1; e0 = e1; dups = 0; /* gnu uniq ignores newlines */ while ((s1 = xmalloc_getline(in)) != NULL) { e1 = s1; for (i = skip_fields; i; i--) { e1 = skip_whitespace(e1); e1 = skip_non_whitespace(e1); } for (i = skip_chars; *e1 && i; i--) { ++e1; } if (!s0 || strcmp(e0, e1)) { break; } ++dups; /* Note: Testing for overflow seems excessive. */ } if (s0) { if (!(opt & (OPT_d << !!dups))) { /* (if dups, opt & OPT_e) */ fprintf(out, "\0%d " + (opt & 1), dups + 1); fprintf(out, "%s\n", s0); } free((void *)s0); } } while (s1); die_if_ferror(in, input_filename); fflush_stdout_and_exit(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_read("."); 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 (now <= mtime) // sleep(1); 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 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 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; }
//usage:#define tty_example_usage //usage: "$ tty\n" //usage: "/dev/tty2\n" #include "libbb.h" int tty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int tty_main(int argc UNUSED_PARAM, char **argv) { const char *s; IF_INCLUDE_SUSv2(int silent;) /* Note: No longer relevant in SUSv3. */ int retval; xfunc_error_retval = 2; /* SUSv3 requires > 1 for error. */ IF_INCLUDE_SUSv2(silent = getopt32(argv, "s");) IF_INCLUDE_SUSv2(argv += optind;) IF_NOT_INCLUDE_SUSv2(argv += 1;) /* gnu tty outputs a warning that it is ignoring all args. */ bb_warn_ignoring_args(argv[0]); retval = EXIT_SUCCESS; s = xmalloc_ttyname(STDIN_FILENO); if (s == NULL) { /* According to SUSv3, ttyname can fail with EBADF or ENOTTY. * We know the file descriptor is good, so failure means not a tty. */ s = "not a tty"; retval = EXIT_FAILURE; }
int switch_root_main(int argc UNUSED_PARAM, char **argv) { char *newroot, *console = NULL; struct stat st; struct statfs stfs; dev_t rootdev; // Parse args (-c console) opt_complementary = "-2"; // minimum 2 params getopt32(argv, "+c:", &console); // '+': stop at first non-option argv += optind; newroot = *argv++; // Change to new root directory and verify it's a different fs xchdir(newroot); xstat("/", &st); rootdev = st.st_dev; xstat(".", &st); if (st.st_dev == rootdev || getpid() != 1) { // Show usage, it says new root must be a mountpoint // and we must be PID 1 bb_show_usage(); } // Additional sanity checks: we're about to rm -rf /, so be REALLY SURE // we mean it. I could make this a CONFIG option, but I would get email // from all the people who WILL destroy their filesystems. if (stat("/init", &st) != 0 || !S_ISREG(st.st_mode)) { bb_error_msg_and_die("/init is not a regular file"); } statfs("/", &stfs); // this never fails if ((unsigned)stfs.f_type != RAMFS_MAGIC && (unsigned)stfs.f_type != TMPFS_MAGIC ) { bb_error_msg_and_die("root filesystem is not ramfs/tmpfs"); } // Zap everything out of rootdev delete_contents("/", rootdev); // Overmount / with newdir and chroot into it if (mount(".", "/", NULL, MS_MOVE, NULL)) { // For example, fails when newroot is not a mountpoint bb_perror_msg_and_die("error moving root"); } xchroot("."); // The chdir is needed to recalculate "." and ".." links /*xchdir("/"); - done in xchroot */ // If a new console specified, redirect stdin/stdout/stderr to it if (console) { close(0); xopen(console, O_RDWR); xdup2(0, 1); xdup2(0, 2); } // Exec real init execv(argv[0], argv); bb_perror_msg_and_die("can't execute '%s'", argv[0]); }
int dpkg_deb_main(int argc UNUSED_PARAM, char **argv) { archive_handle_t *ar_archive; archive_handle_t *tar_archive; llist_t *control_tar_llist = NULL; unsigned opt; const char *extract_dir; /* Setup the tar archive handle */ tar_archive = init_handle(); /* Setup an ar archive handle that refers to the gzip sub archive */ ar_archive = init_handle(); ar_archive->dpkg__sub_archive = tar_archive; ar_archive->filter = filter_accept_list_reassign; llist_add_to(&ar_archive->accept, (char*)"data.tar"); llist_add_to(&control_tar_llist, (char*)"control.tar"); #if ENABLE_FEATURE_SEAMLESS_GZ llist_add_to(&ar_archive->accept, (char*)"data.tar.gz"); llist_add_to(&control_tar_llist, (char*)"control.tar.gz"); #endif #if ENABLE_FEATURE_SEAMLESS_BZ2 llist_add_to(&ar_archive->accept, (char*)"data.tar.bz2"); llist_add_to(&control_tar_llist, (char*)"control.tar.bz2"); #endif #if ENABLE_FEATURE_SEAMLESS_LZMA llist_add_to(&ar_archive->accept, (char*)"data.tar.lzma"); llist_add_to(&control_tar_llist, (char*)"control.tar.lzma"); #endif #if ENABLE_FEATURE_SEAMLESS_XZ llist_add_to(&ar_archive->accept, (char*)"data.tar.xz"); llist_add_to(&control_tar_llist, (char*)"control.tar.xz"); #endif /* Must have 1 or 2 args */ opt_complementary = "-1:?2:c--efXx:e--cfXx:f--ceXx:X--cefx:x--cefX"; opt = getopt32(argv, "cefXx"); argv += optind; //argc -= optind; extract_dir = argv[1]; if (opt & DPKG_DEB_OPT_CONTENTS) { // -c tar_archive->action_header = header_verbose_list; if (extract_dir) bb_show_usage(); } if (opt & DPKG_DEB_OPT_FIELD) { // -f /* Print the entire control file */ //TODO: standard tool accepts an optional list of fields to print ar_archive->accept = control_tar_llist; llist_add_to(&(tar_archive->accept), (char*)"./control"); tar_archive->filter = filter_accept_list; tar_archive->action_data = data_extract_to_stdout; if (extract_dir) bb_show_usage(); } if (opt & DPKG_DEB_OPT_CONTROL) { // -e ar_archive->accept = control_tar_llist; tar_archive->action_data = data_extract_all; if (!extract_dir) extract_dir = "./DEBIAN"; } if (opt & (DPKG_DEB_OPT_EXTRACT_VERBOSE | DPKG_DEB_OPT_EXTRACT)) { // -Xx if (opt & DPKG_DEB_OPT_EXTRACT_VERBOSE) tar_archive->action_header = header_list; tar_archive->action_data = data_extract_all; if (!extract_dir) bb_show_usage(); } /* Standard tool supports "-" */ tar_archive->src_fd = ar_archive->src_fd = xopen_stdin(argv[0]); if (extract_dir) { mkdir(extract_dir, 0777); /* bb_make_directory(extract_dir, 0777, 0) */ xchdir(extract_dir); } /* Do it */ unpack_ar_archive(ar_archive); /* Cleanup */ if (ENABLE_FEATURE_CLEAN_UP) close(ar_archive->src_fd); return EXIT_SUCCESS; }
int pidof_main(int argc UNUSED_PARAM, char **argv) { unsigned first = 1; unsigned opt; #if ENABLE_FEATURE_PIDOF_OMIT llist_t *omits = NULL; /* list of pids to omit */ opt_complementary = "o::"; #endif /* do unconditional option parsing */ opt = getopt32(argv, "" IF_FEATURE_PIDOF_SINGLE ("s") IF_FEATURE_PIDOF_OMIT("o:", &omits)); #if ENABLE_FEATURE_PIDOF_OMIT /* fill omit list. */ { llist_t *omits_p = omits; while (1) { omits_p = llist_find_str(omits_p, "%PPID"); if (!omits_p) break; /* are we asked to exclude the parent's process ID? */ omits_p->data = utoa((unsigned)getppid()); } } #endif /* Looks like everything is set to go. */ argv += optind; while (*argv) { pid_t *pidList; pid_t *pl; /* reverse the pidlist like GNU pidof does. */ pidList = pidlist_reverse(find_pid_by_name(*argv)); for (pl = pidList; *pl; pl++) { #if ENABLE_FEATURE_PIDOF_OMIT if (opt & OPT_OMIT) { llist_t *omits_p = omits; while (omits_p) { if (xatoul(omits_p->data) == (unsigned long)(*pl)) { goto omitting; } omits_p = omits_p->link; } } #endif printf(" %u" + first, (unsigned)*pl); first = 0; if (ENABLE_FEATURE_PIDOF_SINGLE && (opt & OPT_SINGLE)) break; #if ENABLE_FEATURE_PIDOF_OMIT omitting: ; #endif } free(pidList); argv++; } if (!first) bb_putchar('\n'); #if ENABLE_FEATURE_PIDOF_OMIT if (ENABLE_FEATURE_CLEAN_UP) llist_free(omits, NULL); #endif return first; /* 1 (failure) - no processes found */ }
int losetup_main(int argc, char **argv) { char dev[] = LOOP_NAME"0"; unsigned opt; char *opt_o; char *s; unsigned long long offset = 0; /* max 2 args, all opts are mutually exclusive */ opt_complementary = "?2:d--of:o--df:f-do"; opt = getopt32(argv, "do:f", &opt_o); argc -= optind; argv += optind; if (opt == 0x2) // -o offset = xatoull(opt_o); if (opt == 0x4 && argc) // -f does not take any argument bb_show_usage(); if (opt == 0x1) { // -d /* detach takes exactly one argument */ if (argc != 1) bb_show_usage(); if (del_loop(argv[0])) bb_simple_perror_msg_and_die(argv[0]); return EXIT_SUCCESS; } if (argc == 2) { /* -o or no option */ if (set_loop(&argv[0], argv[1], offset) < 0) bb_simple_perror_msg_and_die(argv[0]); return EXIT_SUCCESS; } if (argc == 1) { /* -o or no option */ s = query_loop(argv[0]); if (!s) bb_simple_perror_msg_and_die(argv[0]); printf("%s: %s\n", argv[0], s); if (ENABLE_FEATURE_CLEAN_UP) free(s); return EXIT_SUCCESS; } /* -o, -f or no option */ while (1) { s = query_loop(dev); if (!s) { if (opt == 0x4) { puts(dev); return EXIT_SUCCESS; } } else { if (opt != 0x4) printf("%s: %s\n", dev, s); if (ENABLE_FEATURE_CLEAN_UP) free(s); } if (++dev[sizeof(dev) - 2] > '9') break; } return EXIT_SUCCESS; }
int sort_main(int argc, char **argv) { FILE *fp, *outfile = stdout; char *line, **lines = NULL; char *str_ignored, *str_o, *str_t; llist_t *lst_k = NULL; int i, flag; int linecount = 0; xfunc_error_retval = 2; /* Parse command line options */ /* -o and -t can be given at most once */ opt_complementary = "o--o:t--t:" /* -t, -o: maximum one of each */ "k::"; /* -k takes list */ getopt32(argv, OPT_STR, &str_ignored, &str_ignored, &str_o, &lst_k, &str_t); #if ENABLE_FEATURE_SORT_BIG if (option_mask32 & FLAG_o) outfile = xfopen(str_o, "w"); if (option_mask32 & FLAG_t) { if (!str_t[0] || str_t[1]) bb_error_msg_and_die("bad -t parameter"); key_separator = str_t[0]; } /* parse sort key */ while (lst_k) { enum { FLAG_allowed_for_k = FLAG_n | /* Numeric sort */ FLAG_g | /* Sort using strtod() */ FLAG_M | /* Sort date */ FLAG_b | /* Ignore leading blanks */ FLAG_r | /* Reverse */ FLAG_d | /* Ignore !(isalnum()|isspace()) */ FLAG_f | /* Force uppercase */ FLAG_i | /* Ignore !isprint() */ 0 }; struct sort_key *key = add_key(); char *str_k = lst_k->data; const char *temp2; i = 0; /* i==0 before comma, 1 after (-k3,6) */ while (*str_k) { /* Start of range */ /* Cannot use bb_strtou - suffix can be a letter */ key->range[2*i] = str2u(&str_k); if (*str_k == '.') { str_k++; key->range[2*i+1] = str2u(&str_k); } while (*str_k) { if (*str_k == ',' && !i++) { str_k++; break; } /* no else needed: fall through to syntax error because comma isn't in OPT_STR */ temp2 = strchr(OPT_STR, *str_k); if (!temp2) bb_error_msg_and_die("unknown key option"); flag = 1 << (temp2 - OPT_STR); if (flag & ~FLAG_allowed_for_k) bb_error_msg_and_die("unknown sort type"); /* b after ',' means strip _trailing_ space */ if (i && flag == FLAG_b) flag = FLAG_bb; key->flags |= flag; str_k++; } } /* leaking lst_k... */ lst_k = lst_k->link; } #endif /* global b strips leading and trailing spaces */ if (option_mask32 & FLAG_b) option_mask32 |= FLAG_bb; /* Open input files and read data */ for (i = argv[optind] ? optind : optind-1; argv[i]; i++) { fp = stdin; if (i >= optind && NOT_LONE_DASH(argv[i])) fp = xfopen(argv[i], "r"); for (;;) { line = GET_LINE(fp); if (!line) break; if (!(linecount & 63)) lines = xrealloc(lines, sizeof(char *) * (linecount + 64)); lines[linecount++] = line; } fclose(fp); } #if ENABLE_FEATURE_SORT_BIG /* if no key, perform alphabetic sort */ if (!key_list) add_key()->range[0] = 1; /* handle -c */ if (option_mask32 & FLAG_c) { int j = (option_mask32 & FLAG_u) ? -1 : 0; for (i = 1; i < linecount; i++) if (compare_keys(&lines[i-1], &lines[i]) > j) { fprintf(stderr, "Check line %d\n", i); return 1; } return 0; } #endif /* Perform the actual sort */ qsort(lines, linecount, sizeof(char *), compare_keys); /* handle -u */ if (option_mask32 & FLAG_u) { flag = 0; /* coreutils 6.3 drop lines for which only key is the same */ /* -- disabling last-resort compare... */ option_mask32 |= FLAG_s; for (i = 1; i < linecount; i++) { if (!compare_keys(&lines[flag], &lines[i])) free(lines[i]); else lines[++flag] = lines[i]; } if (linecount) linecount = flag+1; } /* Print it */ for (i = 0; i < linecount; i++) fprintf(outfile, "%s\n", lines[i]); fflush_stdout_and_exit(EXIT_SUCCESS); }
int cut_main(int argc, char **argv) { char *sopt, *ltok; opt_complementary = "b--bcf:c--bcf:f--bcf"; getopt32(argc, argv, optstring, &sopt, &sopt, &sopt, <ok); // argc -= optind; argv += optind; if (!(option_mask32 & (CUT_OPT_BYTE_FLGS | CUT_OPT_CHAR_FLGS | CUT_OPT_FIELDS_FLGS))) bb_error_msg_and_die("expected a list of bytes, characters, or fields"); if (option_mask32 & BB_GETOPT_ERROR) bb_error_msg_and_die("only one type of list may be specified"); if (option_mask32 & CUT_OPT_DELIM_FLGS) { if (strlen(ltok) > 1) { bb_error_msg_and_die("the delimiter must be a single character"); } delim = ltok[0]; } /* non-field (char or byte) cutting has some special handling */ if (!(option_mask32 & CUT_OPT_FIELDS_FLGS)) { if (option_mask32 & CUT_OPT_SUPPRESS_FLGS) { bb_error_msg_and_die ("suppressing non-delimited lines makes sense%s", _op_on_field); } if (delim != '\t') { bb_error_msg_and_die ("a delimiter may be specified%s", _op_on_field); } } /* * parse list and put values into startpos and endpos. * valid list formats: N, N-, N-M, -M * more than one list can be separated by commas */ { char *ntok; int s = 0, e = 0; /* take apart the lists, one by one (they are separated with commas */ while ((ltok = strsep(&sopt, ",")) != NULL) { /* it's actually legal to pass an empty list */ if (strlen(ltok) == 0) continue; /* get the start pos */ ntok = strsep(<ok, "-"); if (ntok == NULL) { bb_error_msg ("internal error: ntok is null for start pos!?\n"); } else if (strlen(ntok) == 0) { s = BOL; } else { s = xatoi_u(ntok); /* account for the fact that arrays are zero based, while * the user expects the first char on the line to be char #1 */ if (s != 0) s--; } /* get the end pos */ ntok = strsep(<ok, "-"); if (ntok == NULL) { e = NON_RANGE; } else if (strlen(ntok) == 0) { e = EOL; } else { e = xatoi_u(ntok); /* if the user specified and end position of 0, that means "til the * end of the line */ if (e == 0) e = EOL; e--; /* again, arrays are zero based, lines are 1 based */ if (e == s) e = NON_RANGE; } /* if there's something left to tokenize, the user passed * an invalid list */ if (ltok) bb_error_msg_and_die("invalid byte or field list"); /* add the new list */ cut_lists = xrealloc(cut_lists, sizeof(struct cut_list) * (++nlists)); cut_lists[nlists-1].startpos = s; cut_lists[nlists-1].endpos = e; } /* make sure we got some cut positions out of all that */ if (nlists == 0) bb_error_msg_and_die("missing list of positions"); /* now that the lists are parsed, we need to sort them to make life * easier on us when it comes time to print the chars / fields / lines */ qsort(cut_lists, nlists, sizeof(struct cut_list), cmpfunc); } /* 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 || LONE_DASH(argv[0])) { cut_file(stdin); } else { FILE *file; do { file = fopen_or_warn(argv[0], "r"); if (file) { cut_file(file); fclose(file); } } while (*++argv); } if (ENABLE_FEATURE_CLEAN_UP) free(cut_lists); return EXIT_SUCCESS; }
int cat_main(int argc UNUSED_PARAM, char **argv) { getopt32(argv, "u"); argv += optind; return bb_cat(argv); }
int md5_sha1_sum_main(int argc, char **argv) { int return_value = EXIT_SUCCESS; uint8_t *hash_value; unsigned flags; hash_algo_t hash_algo = ENABLE_MD5SUM ? (ENABLE_SHA1SUM ? (**argv=='m' ? HASH_MD5 : HASH_SHA1) : HASH_MD5) : HASH_SHA1; if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK) flags = getopt32(argc, argv, "scw"); else optind = 1; if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && !(flags & FLAG_CHECK)) { if (flags & FLAG_SILENT) { bb_error_msg_and_die ("-%c is meaningful only when verifying checksums", 's'); } else if (flags & FLAG_WARN) { bb_error_msg_and_die ("-%c is meaningful only when verifying checksums", 'w'); } } if (argc == optind) { argv[argc++] = "-"; } if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && (flags & FLAG_CHECK)) { FILE *pre_computed_stream; int count_total = 0; int count_failed = 0; char *file_ptr = argv[optind]; char *line; if (optind + 1 != argc) { bb_error_msg_and_die ("only one argument may be specified when using -c"); } pre_computed_stream = stdin; if (NOT_LONE_DASH(file_ptr)) { pre_computed_stream = xfopen(file_ptr, "r"); } while ((line = xmalloc_getline(pre_computed_stream)) != NULL) { char *filename_ptr; count_total++; filename_ptr = strstr(line, " "); /* handle format for binary checksums */ if (filename_ptr == NULL) { filename_ptr = strstr(line, " *"); } if (filename_ptr == NULL) { if (flags & FLAG_WARN) { bb_error_msg("invalid format"); } count_failed++; return_value = EXIT_FAILURE; free(line); continue; } *filename_ptr = '\0'; filename_ptr += 2; hash_value = hash_file(filename_ptr, hash_algo); if (hash_value && (strcmp((char*)hash_value, line) == 0)) { if (!(flags & FLAG_SILENT)) printf("%s: OK\n", filename_ptr); } else { if (!(flags & FLAG_SILENT)) printf("%s: FAILED\n", filename_ptr); count_failed++; return_value = EXIT_FAILURE; } /* possible free(NULL) */ free(hash_value); free(line); } if (count_failed && !(flags & FLAG_SILENT)) { bb_error_msg("WARNING: %d of %d computed checksums did NOT match", count_failed, count_total); } /* if (fclose_if_not_stdin(pre_computed_stream) == EOF) { bb_perror_msg_and_die("cannot close file %s", file_ptr); } */ } else { while (optind < argc) { char *file_ptr = argv[optind++]; hash_value = hash_file(file_ptr, hash_algo); if (hash_value == NULL) { return_value = EXIT_FAILURE; } else { printf("%s %s\n", hash_value, file_ptr); free(hash_value); } } } return return_value; }
int vlock_main(int argc, char **argv) { sigset_t sig; struct sigaction sa; struct vt_mode vtm; struct termios term; uid_t uid = getuid(); pw = getpwuid(uid); if (pw == NULL) bb_error_msg_and_die("unknown uid %d", uid); if (argc > 2) { bb_show_usage(); } o_lock_all = getopt32(argc, argv, "a"); vfd = xopen(CURRENT_TTY, O_RDWR); if (ioctl(vfd, VT_GETMODE, &vtm) < 0) { bb_perror_msg_and_die("VT_GETMODE"); } /* mask a bunch of signals */ sigprocmask(SIG_SETMASK, NULL, &sig); sigdelset(&sig, SIGUSR1); sigdelset(&sig, SIGUSR2); sigaddset(&sig, SIGTSTP); sigaddset(&sig, SIGTTIN); sigaddset(&sig, SIGTTOU); sigaddset(&sig, SIGHUP); sigaddset(&sig, SIGCHLD); sigaddset(&sig, SIGQUIT); sigaddset(&sig, SIGINT); sigemptyset(&(sa.sa_mask)); sa.sa_flags = SA_RESTART; sa.sa_handler = release_vt; sigaction(SIGUSR1, &sa, NULL); sa.sa_handler = acquire_vt; sigaction(SIGUSR2, &sa, NULL); /* need to handle some signals so that we don't get killed by them */ sa.sa_handler = SIG_IGN; sigaction(SIGHUP, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGTSTP, &sa, NULL); ovtm = vtm; vtm.mode = VT_PROCESS; vtm.relsig = SIGUSR1; vtm.acqsig = SIGUSR2; ioctl(vfd, VT_SETMODE, &vtm); tcgetattr(STDIN_FILENO, &oterm); term = oterm; term.c_iflag &= ~BRKINT; term.c_iflag |= IGNBRK; term.c_lflag &= ~ISIG; term.c_lflag &= ~(ECHO | ECHOCTL); tcsetattr(STDIN_FILENO, TCSANOW, &term); do { printf("Virtual Console%s locked by %s.\n", (o_lock_all) ? "s" : "", pw->pw_name); if (correct_password(pw)) { break; } bb_do_delay(FAIL_DELAY); puts("Password incorrect"); } while (1); restore_terminal(); fflush_stdout_and_exit(0); }
int fuser_main(int argc UNUSED_PARAM, char **argv) { pid_list *plist; pid_t mypid; char **pp; struct stat st; unsigned port; int opt; int exitcode; int killsig; /* fuser [OPTIONS] FILE or PORT/PROTO Find processes which use FILEs or PORTs -m Find processes which use same fs as FILEs -4 Search only IPv4 space -6 Search only IPv6 space -s Don't display PIDs -k Kill found processes -SIGNAL Signal to send (default: KILL) */ /* Handle -SIGNAL. Oh my... */ killsig = SIGKILL; /* yes, the default is not SIGTERM */ pp = argv; while (*++pp) { char *arg = *pp; if (arg[0] != '-') continue; if (arg[1] == '-' && arg[2] == '\0') /* "--" */ break; if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0') continue; /* it's "-4" or "-6" */ opt = get_signum(&arg[1]); if (opt < 0) continue; /* "-SIGNAL" option found. Remove it and bail out */ killsig = opt; do { pp[0] = arg = pp[1]; pp++; } while (arg); break; } opt_complementary = "-1"; /* at least one param */ opt = getopt32(argv, OPTION_STRING); argv += optind; pp = argv; while (*pp) { /* parse net arg */ char path[20], tproto[5]; if (sscanf(*pp, "%u/%4s", &port, tproto) != 2) goto file; sprintf(path, "/proc/net/%s", tproto); if (access(path, R_OK) != 0) { /* PORT/PROTO */ scan_proc_net(path, port); } else { /* FILE */ file: xstat(*pp, &st); add_inode(&st); } pp++; } scan_proc_pids(); /* changes dir to "/proc" */ mypid = getpid(); plist = G.pid_list_head; while (1) { if (!plist) return EXIT_FAILURE; if (plist->pid != mypid) break; plist = plist->next; } exitcode = EXIT_SUCCESS; do { if (plist->pid != mypid) { if (opt & OPT_KILL) { if (kill(plist->pid, killsig) != 0) { bb_perror_msg("kill pid %u", (unsigned)plist->pid); exitcode = EXIT_FAILURE; } } if (!(opt & OPT_SILENT)) { printf("%u ", (unsigned)plist->pid); } } plist = plist->next; } while (plist); if (!(opt & (OPT_SILENT))) { bb_putchar('\n'); } return exitcode; }
int udhcpd_main(int argc, char **argv) { fd_set rfds; struct timeval tv; int server_socket = -1, bytes, retval, max_sock; struct dhcpMessage packet; uint8_t *state, *server_id, *requested; uint32_t server_id_align, requested_align, static_lease_ip; unsigned timeout_end; unsigned num_ips; unsigned opt; struct option_set *option; struct dhcpOfferedAddr *lease, static_lease; opt = getopt32(argv, "fS"); argv += optind; if (!(opt & 1)) { /* no -f */ bb_daemonize_or_rexec(0, argv); logmode &= ~LOGMODE_STDIO; } if (opt & 2) { /* -S */ openlog(applet_name, LOG_PID, LOG_LOCAL0); logmode |= LOGMODE_SYSLOG; } /* Would rather not do read_config before daemonization - * otherwise NOMMU machines will parse config twice */ read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE); /* Make sure fd 0,1,2 are open */ bb_sanitize_stdio(); /* Equivalent of doing a fflush after every \n */ setlinebuf(stdout); /* Create pidfile */ write_pidfile(server_config.pidfile); /* if (!..) bb_perror_msg("cannot create pidfile %s", pidfile); */ bb_info_msg("%s (v"BB_VER") started", applet_name); option = find_option(server_config.options, DHCP_LEASE_TIME); server_config.lease = LEASE_TIME; if (option) { memcpy(&server_config.lease, option->data + 2, 4); server_config.lease = ntohl(server_config.lease); } /* Sanity check */ num_ips = server_config.end_ip - server_config.start_ip + 1; if (server_config.max_leases > num_ips) { bb_error_msg("max_leases=%u is too big, setting to %u", (unsigned)server_config.max_leases, num_ips); server_config.max_leases = num_ips; } leases = xzalloc(server_config.max_leases * sizeof(*leases)); read_leases(server_config.lease_file); if (read_interface(server_config.interface, &server_config.ifindex, &server_config.server, server_config.arp)) { retval = 1; goto ret; } /* Setup the signal pipe */ udhcp_sp_setup(); timeout_end = monotonic_sec() + server_config.auto_time; while (1) { /* loop until universe collapses */ if (server_socket < 0) { server_socket = listen_socket(/*INADDR_ANY,*/ SERVER_PORT, server_config.interface); } max_sock = udhcp_sp_fd_set(&rfds, server_socket); if (server_config.auto_time) { tv.tv_sec = timeout_end - monotonic_sec(); tv.tv_usec = 0; } retval = 0; if (!server_config.auto_time || tv.tv_sec > 0) { retval = select(max_sock + 1, &rfds, NULL, NULL, server_config.auto_time ? &tv : NULL); } if (retval == 0) { write_leases(); timeout_end = monotonic_sec() + server_config.auto_time; continue; } if (retval < 0 && errno != EINTR) { DEBUG("error on select"); continue; } switch (udhcp_sp_read(&rfds)) { case SIGUSR1: bb_info_msg("Received a SIGUSR1"); write_leases(); /* why not just reset the timeout, eh */ timeout_end = monotonic_sec() + server_config.auto_time; continue; case SIGTERM: bb_info_msg("Received a SIGTERM"); goto ret0; case 0: break; /* no signal */ default: continue; /* signal or error (probably EINTR) */ } bytes = udhcp_recv_packet(&packet, server_socket); /* this waits for a packet - idle */ if (bytes < 0) { if (bytes == -1 && errno != EINTR) { DEBUG("error on read, %s, reopening socket", strerror(errno)); close(server_socket); server_socket = -1; } continue; } state = get_option(&packet, DHCP_MESSAGE_TYPE); if (state == NULL) { bb_error_msg("cannot get option from packet, ignoring"); continue; } /* Look for a static lease */ static_lease_ip = getIpByMac(server_config.static_leases, &packet.chaddr); if (static_lease_ip) { bb_info_msg("Found static lease: %x", static_lease_ip); memcpy(&static_lease.chaddr, &packet.chaddr, 16); static_lease.yiaddr = static_lease_ip; static_lease.expires = 0; lease = &static_lease; } else { lease = find_lease_by_chaddr(packet.chaddr); } switch (state[0]) { case DHCPDISCOVER: DEBUG("Received DISCOVER"); if (sendOffer(&packet) < 0) { bb_error_msg("send OFFER failed"); } /* circle test Stat. add by wangpu begin */ g_offercount ++; printf("Circle test: [DHCPS] send OFFER packet num:[%d]\n",g_offercount); memset(g_acCmd,0,sizeof(g_acCmd)); memset(g_interface,0,sizeof(g_interface)); sprintf(g_interface,"%s",server_config.interface); sprintf(g_acCmd,"echo %d > /var/circle/%s",g_offercount,g_interface); system(g_acCmd); /* circle test Stat. add by wangpu end */ break; case DHCPREQUEST: DEBUG("received REQUEST"); #if 0 /* ?¡¤??2a¨º? add by wangpu begin */ g_offercount ++; printf("Circle test: [DHCPS] send OFFER packet num:[%d]\n",g_offercount); memset(g_acCmd,0,sizeof(g_acCmd)); memset(g_interface,0,sizeof(g_interface)); sprintf(g_interface,"%s",server_config.interface); sprintf(g_acCmd,"echo %d > /var/circle/%s",g_offercount,g_interface); system(g_acCmd); /* ?¡¤??2a¨º? add by wangpu begin */ #endif requested = get_option(&packet, DHCP_REQUESTED_IP); server_id = get_option(&packet, DHCP_SERVER_ID); if (requested) memcpy(&requested_align, requested, 4); if (server_id) memcpy(&server_id_align, server_id, 4); if (lease) { if (server_id) { /* SELECTING State */ DEBUG("server_id = %08x", ntohl(server_id_align)); if (server_id_align == server_config.server && requested && requested_align == lease->yiaddr ) { sendACK(&packet, lease->yiaddr); } } else if (requested) { /* INIT-REBOOT State */ if (lease->yiaddr == requested_align) sendACK(&packet, lease->yiaddr); else sendNAK(&packet); } else if (lease->yiaddr == packet.ciaddr) { /* RENEWING or REBINDING State */ sendACK(&packet, lease->yiaddr); } else { /* don't know what to do!!!! */ sendNAK(&packet); } /* what to do if we have no record of the client */ } else if (server_id) { /* SELECTING State */ } else if (requested) { /* INIT-REBOOT State */ lease = find_lease_by_yiaddr(requested_align); if (lease) { if (lease_expired(lease)) { /* probably best if we drop this lease */ memset(lease->chaddr, 0, 16); /* make some contention for this address */ } else sendNAK(&packet); } else { uint32_t r = ntohl(requested_align); if (r < server_config.start_ip || r > server_config.end_ip ) { sendNAK(&packet); } /* else remain silent */ } } else { /* RENEWING or REBINDING State */ } break; case DHCPDECLINE: DEBUG("Received DECLINE"); if (lease) { memset(lease->chaddr, 0, 16); lease->expires = time(0) + server_config.decline_time; } break; case DHCPRELEASE: DEBUG("Received RELEASE"); if (lease) lease->expires = time(0); break; case DHCPINFORM: DEBUG("Received INFORM"); send_inform(&packet); break; default: bb_info_msg("Unsupported DHCP message (%02x) - ignoring", state[0]); } } ret0: retval = 0; ret: /*if (server_config.pidfile) - server_config.pidfile is never NULL */ remove_pidfile(server_config.pidfile); return retval; }
int patch_main(int argc UNUSED_PARAM, char **argv) { int opts; int reverse, state = 0; char *oldname = NULL, *newname = NULL; char *opt_p, *opt_i; long oldlen = oldlen; /* for compiler */ long newlen = newlen; /* for compiler */ INIT_TT(); opts = getopt32(argv, FLAG_STR, &opt_p, &opt_i); argv += optind; reverse = opts & FLAG_REVERSE; TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative! TT.filein = TT.fileout = -1; if (opts & FLAG_INPUT) { xmove_fd(xopen_stdin(opt_i), STDIN_FILENO); } else { if (argv[0] && argv[1]) { xmove_fd(xopen_stdin(argv[1]), STDIN_FILENO); } } if (argv[0]) { oldname = xstrdup(argv[0]); newname = xstrdup(argv[0]); } // Loop through the lines in the patch for(;;) { char *patchline; patchline = xmalloc_fgetline(stdin); if (!patchline) break; // Other versions of patch accept damaged patches, // so we need to also. if (!*patchline) { free(patchline); patchline = xstrdup(" "); } // Are we assembling a hunk? if (state >= 2) { if (*patchline==' ' || *patchline=='+' || *patchline=='-') { dlist_add(&TT.current_hunk, patchline); if (*patchline != '+') oldlen--; if (*patchline != '-') newlen--; // Context line? if (*patchline==' ' && state==2) TT.context++; else state=3; // If we've consumed all expected hunk lines, apply the hunk. if (!oldlen && !newlen) state = apply_one_hunk(); continue; } fail_hunk(); state = 0; continue; } // Open a new file? if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) { char *s, **name = reverse ? &newname : &oldname; int i; if (*patchline == '+') { name = reverse ? &oldname : &newname; state = 1; } finish_oldfile(); if (!argv[0]) { free(*name); // Trim date from end of filename (if any). We don't care. for (s = patchline+4; *s && *s!='\t'; s++) if (*s=='\\' && s[1]) s++; i = atoi(s); if (i>1900 && i<=1970) *name = xstrdup("/dev/null"); else { *s = 0; *name = xstrdup(patchline+4); } } // We defer actually opening the file because svn produces broken // patches that don't signal they want to create a new file the // way the patch man page says, so you have to read the first hunk // and _guess_. // Start a new hunk? Usually @@ -oldline,oldlen +newline,newlen @@ // but a missing ,value means the value is 1. } else if (state == 1 && !strncmp("@@ -", patchline, 4)) { int i; char *s = patchline+4; // Read oldline[,oldlen] +newline[,newlen] TT.oldlen = oldlen = TT.newlen = newlen = 1; TT.oldline = strtol(s, &s, 10); if (*s == ',') TT.oldlen = oldlen = strtol(s+1, &s, 10); TT.newline = strtol(s+2, &s, 10); if (*s == ',') TT.newlen = newlen = strtol(s+1, &s, 10); if (oldlen < 1 && newlen < 1) bb_error_msg_and_die("Really? %s", patchline); TT.context = 0; state = 2; // If this is the first hunk, open the file. if (TT.filein == -1) { int oldsum, newsum, empty = 0; char *name; oldsum = TT.oldline + oldlen; newsum = TT.newline + newlen; name = reverse ? oldname : newname; // We're deleting oldname if new file is /dev/null (before -p) // or if new hunk is empty (zero context) after patching if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum)) { name = reverse ? newname : oldname; empty++; } // handle -p path truncation. for (i=0, s = name; *s;) { if ((option_mask32 & FLAG_PATHLEN) && TT.prefix == i) break; if (*(s++)=='/') { name = s; i++; } } if (empty) { // File is empty after the patches have been applied state = 0; if (option_mask32 & FLAG_RMEMPTY) { // If flag -E or --remove-empty-files is set printf("removing %s\n", name); xunlink(name); } else { printf("patching file %s\n", name); xclose(xopen(name, O_WRONLY | O_TRUNC)); } // If we've got a file to open, do so. } else if (!(option_mask32 & FLAG_PATHLEN) || i <= TT.prefix) { struct stat statbuf; // If the old file was null, we're creating a new one. if (!strcmp(oldname, "/dev/null") || !oldsum) { printf("creating %s\n", name); s = strrchr(name, '/'); if (s) { *s = 0; bb_make_directory(name, -1, FILEUTILS_RECUR); *s = '/'; } TT.filein = xopen(name, O_CREAT|O_EXCL|O_RDWR); } else { printf("patching file %s\n", name); TT.filein = xopen(name, O_RDONLY); } TT.tempname = xasprintf("%sXXXXXX", name); TT.fileout = xmkstemp(TT.tempname); // Set permissions of output file fstat(TT.filein, &statbuf); fchmod(TT.fileout, statbuf.st_mode); TT.linenum = 0; TT.hunknum = 0; } } TT.hunknum++; continue; } // If we didn't continue above, discard this line. free(patchline); } finish_oldfile(); if (ENABLE_FEATURE_CLEAN_UP) { free(oldname); free(newname); } return TT.exitval; }
int microcom_main(int argc UNUSED_PARAM, char **argv) { int sfd; int nfd; struct pollfd pfd[2]; struct termios tio0, tiosfd, tio; char *device_lock_file; enum { OPT_X = 1 << 0, // do not respect Ctrl-X, Ctrl-@ OPT_s = 1 << 1, // baudrate OPT_d = 1 << 2, // wait for device response, ms OPT_t = 1 << 3, // timeout, ms }; speed_t speed = 9600; int delay = -1; int timeout = -1; unsigned opts; // fetch options opt_complementary = "=1:s+:d+:t+"; // exactly one arg, numeric options opts = getopt32(argv, "Xs:d:t:", &speed, &delay, &timeout); // argc -= optind; argv += optind; // try to create lock file in /var/lock device_lock_file = (char *)bb_basename(argv[0]); device_lock_file = xasprintf("/var/lock/LCK..%s", device_lock_file); sfd = open(device_lock_file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0644); if (sfd < 0) { // device already locked -> bail out if (errno == EEXIST) bb_perror_msg_and_die("can't create %s", device_lock_file); // can't create lock -> don't care if (ENABLE_FEATURE_CLEAN_UP) free(device_lock_file); device_lock_file = NULL; } else { // %4d to make concurrent mgetty (if any) happy. // Mgetty treats 4-bytes lock files as binary, // not text, PID. Making 5+ char file. Brrr... fdprintf(sfd, "%4d\n", getpid()); close(sfd); } // setup signals bb_signals(0 + (1 << SIGHUP) + (1 << SIGINT) + (1 << SIGTERM) + (1 << SIGPIPE) , signal_handler); // error exit code if we fail to open the device signalled = 1; // open device sfd = open_or_warn(argv[0], O_RDWR | O_NOCTTY | O_NONBLOCK); if (sfd < 0) goto done; fcntl(sfd, F_SETFL, 0); // put device to "raw mode" xget1(sfd, &tio, &tiosfd); // set device speed cfsetspeed(&tio, tty_value_to_baud(speed)); if (xset1(sfd, &tio, argv[0])) goto done; // put stdin to "raw mode" (if stdin is a TTY), // handle one character at a time if (isatty(STDIN_FILENO)) { xget1(STDIN_FILENO, &tio, &tio0); if (xset1(STDIN_FILENO, &tio, "stdin")) goto done; } // main loop: check with poll(), then read/write bytes across pfd[0].fd = sfd; pfd[0].events = POLLIN; pfd[1].fd = STDIN_FILENO; pfd[1].events = POLLIN; signalled = 0; nfd = 2; while (!signalled && safe_poll(pfd, nfd, timeout) > 0) { if (nfd > 1 && pfd[1].revents) { char c; // read from stdin -> write to device if (safe_read(STDIN_FILENO, &c, 1) < 1) { // don't poll stdin anymore if we got EOF/error nfd--; goto skip_write; } // do we need special processing? if (!(opts & OPT_X)) { // ^@ sends Break if (VINTR == c) { tcsendbreak(sfd, 0); goto skip_write; } // ^X exits if (24 == c) break; } write(sfd, &c, 1); if (delay >= 0) safe_poll(pfd, 1, delay); skip_write: ; } if (pfd[0].revents) { #define iobuf bb_common_bufsiz1 ssize_t len; // read from device -> write to stdout len = safe_read(sfd, iobuf, sizeof(iobuf)); if (len > 0) full_write(STDOUT_FILENO, iobuf, len); else { // EOF/error -> bail out signalled = SIGHUP; break; } } } // restore device mode tcsetattr(sfd, TCSAFLUSH, &tiosfd); if (isatty(STDIN_FILENO)) tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0); done: if (device_lock_file) unlink(device_lock_file); return signalled; }
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 sulogin_main(int argc, char **argv) { char *cp; int timeout = 0; char *timeout_arg; const char *const *p; struct passwd *pwd; const char *shell; #if ENABLE_FEATURE_SHADOWPASSWDS /* Using _r function to avoid pulling in static buffers */ char buffer[256]; struct spwd spw; struct spwd *result; #endif logmode = LOGMODE_BOTH; openlog(applet_name, 0, LOG_AUTH); if (getopt32(argv, "t:", &timeout_arg)) { timeout = xatoi_u(timeout_arg); } if (argv[optind]) { close(0); close(1); dup(xopen(argv[optind], O_RDWR)); close(2); dup(0); } if (!isatty(0) || !isatty(1) || !isatty(2)) { logmode = LOGMODE_SYSLOG; bb_error_msg_and_die("not a tty"); } /* Clear out anything dangerous from the environment */ for (p = forbid; *p; p++) unsetenv(*p); signal(SIGALRM, catchalarm); pwd = getpwuid(0); if (!pwd) { goto auth_error; } #if ENABLE_FEATURE_SHADOWPASSWDS if (getspnam_r(pwd->pw_name, &spw, buffer, sizeof(buffer), &result)) { goto auth_error; } pwd->pw_passwd = spw.sp_pwdp; #endif while (1) { /* cp points to a static buffer that is zeroed every time */ cp = bb_askpass(timeout, "Give root password for system maintenance\n" "(or type Control-D for normal startup):"); if (!cp || !*cp) { bb_info_msg("Normal startup"); return 0; } if (strcmp(pw_encrypt(cp, pwd->pw_passwd), pwd->pw_passwd) == 0) { break; } bb_do_delay(FAIL_DELAY); bb_error_msg("login incorrect"); } memset(cp, 0, strlen(cp)); signal(SIGALRM, SIG_DFL); bb_info_msg("System Maintenance Mode"); USE_SELINUX(renew_current_security_context()); shell = getenv("SUSHELL"); if (!shell) shell = getenv("sushell"); if (!shell) { shell = "/bin/sh"; if (pwd->pw_shell[0]) shell = pwd->pw_shell; } run_shell(shell, 1, 0, 0); /* never returns */ auth_error: bb_error_msg_and_die("no password entry for 'root'"); }
int id_main(int argc UNUSED_PARAM, char **argv) { uid_t ruid; gid_t rgid; uid_t euid; gid_t egid; unsigned opt; int i; int status = EXIT_SUCCESS; const char *prefix; const char *username; #if ENABLE_SELINUX security_context_t scontext = NULL; #endif /* Don't allow -n -r -nr -ug -rug -nug -rnug -uZ -gZ -GZ*/ /* Don't allow more than one username */ opt_complementary = "?1:u--g:g--u:G--u:u--G:g--G:G--g:r?ugG:n?ugG" USE_SELINUX(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G"); opt = getopt32(argv, "rnugG" USE_SELINUX("Z")); username = argv[optind]; if (username) { struct passwd *p = xgetpwnam(username); euid = ruid = p->pw_uid; egid = rgid = p->pw_gid; } else { egid = getegid(); rgid = getgid(); euid = geteuid(); ruid = getuid(); } /* JUST_ALL_GROUPS ignores -r PRINT_REAL flag even if man page for */ /* id says: print the real ID instead of the effective ID, with -ugG */ /* in fact in this case egid is always printed if egid != rgid */ if (!opt || (opt & JUST_ALL_GROUPS)) { gid_t *groups; int n; if (!opt) { /* Default Mode */ status |= print_user(ruid, "uid="); status |= print_group(rgid, " gid="); if (euid != ruid) status |= print_user(euid, " euid="); if (egid != rgid) status |= print_group(egid, " egid="); } else { /* JUST_ALL_GROUPS */ status |= print_group(rgid, NULL); if (egid != rgid) status |= print_group(egid, " "); } /* We are supplying largish buffer, trying * to not run get_groups() twice. That might be slow * ("user database in remote SQL server" case) */ groups = xmalloc(64 * sizeof(gid_t)); n = 64; if (get_groups(username, rgid, groups, &n) < 0) { /* Need bigger buffer after all */ groups = xrealloc(groups, n * sizeof(gid_t)); get_groups(username, rgid, groups, &n); } if (n > 0) { /* Print the list */ prefix = " groups="; for (i = 0; i < n; i++) { if (opt && (groups[i] == rgid || groups[i] == egid)) continue; status |= print_group(groups[i], opt ? " " : prefix); prefix = ","; } } else if (n < 0) { /* error in get_groups() */ if (!ENABLE_DESKTOP) bb_error_msg_and_die("cannot get groups"); else return EXIT_FAILURE; } if (ENABLE_FEATURE_CLEAN_UP) free(groups); #if ENABLE_SELINUX if (is_selinux_enabled()) { if (getcon(&scontext) == 0) printf(" context=%s", scontext); } #endif } else if (opt & PRINT_REAL) { euid = ruid; egid = rgid; } if (opt & JUST_USER) status |= print_user(euid, NULL); else if (opt & JUST_GROUP) status |= print_group(egid, NULL); #if ENABLE_SELINUX else if (opt & JUST_CONTEXT) { selinux_or_die(); if (username || getcon(&scontext)) { bb_error_msg_and_die("can't get process context%s", username ? " for a different user" : ""); } fputs(scontext, stdout); } /* freecon(NULL) seems to be harmless */ if (ENABLE_FEATURE_CLEAN_UP) freecon(scontext); #endif bb_putchar('\n'); fflush_stdout_and_exit(status); }
int makedevs_main(int argc UNUSED_PARAM, char **argv) { parser_t *parser; char *line = (char *)"-"; int ret = EXIT_SUCCESS; opt_complementary = "=1"; /* exactly one param */ getopt32(argv, "d:", &line); argv += optind; xchdir(*argv); /* ensure root dir exists */ umask(0); printf("rootdir=%s\ntable=", *argv); if (NOT_LONE_DASH(line)) { printf("'%s'\n", line); } else { puts("<stdin>"); } parser = config_open(line); while (config_read(parser, &line, 1, 1, "# \t", PARSE_NORMAL)) { int linenum; char type; unsigned mode = 0755; unsigned major = 0; unsigned minor = 0; unsigned count = 0; unsigned increment = 0; unsigned start = 0; char name[41]; char user[41]; char group[41]; char *full_name = name; uid_t uid; gid_t gid; linenum = parser->lineno; if ((2 > sscanf(line, "%40s %c %o %40s %40s %u %u %u %u %u", name, &type, &mode, user, group, &major, &minor, &start, &increment, &count)) || ((unsigned)(major | minor | start | count | increment) > 255) ) { bb_error_msg("invalid line %d: '%s'", linenum, line); ret = EXIT_FAILURE; continue; } gid = (*group) ? get_ug_id(group, xgroup2gid) : getgid(); uid = (*user) ? get_ug_id(user, xuname2uid) : getuid(); /* We are already in the right root dir, * so make absolute paths relative */ if ('/' == *full_name) full_name++; if (type == 'd') { bb_make_directory(full_name, mode | S_IFDIR, FILEUTILS_RECUR); if (chown(full_name, uid, gid) == -1) { chown_fail: bb_perror_msg("line %d: can't chown %s", linenum, full_name); ret = EXIT_FAILURE; continue; } if (chmod(full_name, mode) < 0) { chmod_fail: bb_perror_msg("line %d: can't chmod %s", linenum, full_name); ret = EXIT_FAILURE; continue; } } else if (type == 'f') { struct stat st; if ((stat(full_name, &st) < 0 || !S_ISREG(st.st_mode))) { bb_perror_msg("line %d: regular file '%s' does not exist", linenum, full_name); ret = EXIT_FAILURE; continue; } if (chown(full_name, uid, gid) < 0) goto chown_fail; if (chmod(full_name, mode) < 0) goto chmod_fail; } else { dev_t rdev; unsigned i; char *full_name_inc; if (type == 'p') { mode |= S_IFIFO; } else if (type == 'c') { mode |= S_IFCHR; } else if (type == 'b') { mode |= S_IFBLK; } else { bb_error_msg("line %d: unsupported file type %c", linenum, type); ret = EXIT_FAILURE; continue; } full_name_inc = xmalloc(strlen(full_name) + sizeof(int)*3 + 2); if (count) count--; for (i = start; i <= start + count; i++) { sprintf(full_name_inc, count ? "%s%u" : "%s", full_name, i); rdev = makedev(major, minor + (i - start) * increment); if (mknod(full_name_inc, mode, rdev) != 0 && errno != EEXIST ) { bb_perror_msg("line %d: can't create node %s", linenum, full_name_inc); ret = EXIT_FAILURE; } else if (chown(full_name_inc, uid, gid) < 0) { bb_perror_msg("line %d: can't chown %s", linenum, full_name_inc); ret = EXIT_FAILURE; } else if (chmod(full_name_inc, mode) < 0) { bb_perror_msg("line %d: can't chmod %s", linenum, full_name_inc); ret = EXIT_FAILURE; } } free(full_name_inc); } } if (ENABLE_FEATURE_CLEAN_UP) config_close(parser); return ret; }
int adjtimex_main(int argc UNUSED_PARAM, char **argv) { enum { OPT_quiet = 0x1 }; unsigned opt; char *opt_o, *opt_f, *opt_p, *opt_t; struct timex txc; int i, ret; const char *descript; opt_complementary = "=0"; /* no valid non-option parameters */ opt = getopt32(argv, "qo:f:p:t:", &opt_o, &opt_f, &opt_p, &opt_t); txc.modes = 0; //if (opt & 0x1) // -q if (opt & 0x2) { // -o txc.offset = xatol(opt_o); txc.modes |= ADJ_OFFSET_SINGLESHOT; } if (opt & 0x4) { // -f txc.freq = xatol(opt_f); txc.modes |= ADJ_FREQUENCY; } if (opt & 0x8) { // -p txc.constant = xatol(opt_p); txc.modes |= ADJ_TIMECONST; } if (opt & 0x10) { // -t txc.tick = xatol(opt_t); txc.modes |= ADJ_TICK; } ret = adjtimex(&txc); if (ret < 0) { bb_perror_nomsg_and_die(); } if (!(opt & OPT_quiet)) { int sep; const char *name; printf( " mode: %d\n" "-o offset: %ld\n" "-f frequency: %ld\n" " maxerror: %ld\n" " esterror: %ld\n" " status: %d (", txc.modes, txc.offset, txc.freq, txc.maxerror, txc.esterror, txc.status); /* representative output of next code fragment: "PLL | PPSTIME" */ name = statlist_name; sep = 0; for (i = 0; statlist_bit[i]; i++) { if (txc.status & statlist_bit[i]) { if (sep) fputs(" | ", stdout); fputs(name, stdout); sep = 1; } name += strlen(name) + 1; } descript = "error"; if (ret <= 5) descript = nth_string(ret_code_descript, ret); printf(")\n" "-p timeconstant: %ld\n" " precision: %ld\n" " tolerance: %ld\n" "-t tick: %ld\n" " time.tv_sec: %ld\n" " time.tv_usec: %ld\n" " return value: %d (%s)\n", txc.constant, txc.precision, txc.tolerance, txc.tick, (long)txc.time.tv_sec, (long)txc.time.tv_usec, ret, descript); } return 0; }
int split_main(int argc, char **argv) { unsigned suffix_len = 2; char *pfx; char *count_p; const char *sfx; off_t cnt = 1000; off_t remaining = 0; unsigned opt; ssize_t bytes_read, to_write; char *src; opt_complementary = "?2"; opt = getopt32(argc, argv, "l:b:a:", &count_p, &count_p, &sfx); if (opt & SPLIT_OPT_l) cnt = xatoul(count_p); if (opt & SPLIT_OPT_b) cnt = xatoul_sfx(count_p, split_suffices); if (opt & SPLIT_OPT_a) suffix_len = xatou(sfx); sfx = "x"; argv += optind; if (argv[0]) { if (argv[1]) sfx = argv[1]; xmove_fd(xopen(argv[0], O_RDONLY), 0); } else { argv[0] = (char *) bb_msg_standard_input; } if (NAME_MAX < strlen(sfx) + suffix_len) bb_error_msg_and_die("suffix too long"); { char *char_p = xzalloc(suffix_len + 1); memset(char_p, 'a', suffix_len); pfx = xasprintf("%s%s", sfx, char_p); if (ENABLE_FEATURE_CLEAN_UP) free(char_p); } while (1) { bytes_read = safe_read(0, read_buffer, READ_BUFFER_SIZE); if (!bytes_read) break; if (bytes_read < 0) bb_perror_msg_and_die("%s", argv[0]); src = read_buffer; do { if (!remaining) { if (!pfx) bb_error_msg_and_die("suffixes exhausted"); xmove_fd(xopen(pfx, O_WRONLY | O_CREAT | O_TRUNC), 1); pfx = next_file(pfx, suffix_len); remaining = cnt; } if (opt & SPLIT_OPT_b) { /* split by bytes */ to_write = (bytes_read < remaining) ? bytes_read : remaining; remaining -= to_write; } else { /* split by lines */ /* can be sped up by using _memrchr_ * and writing many lines at once... */ char *end = memchr(src, '\n', bytes_read); if (end) { --remaining; to_write = end - src + 1; } else { to_write = bytes_read; } } xwrite(1, src, to_write); bytes_read -= to_write; src += to_write; } while (bytes_read); } return 0; }
int ubi_tools_main(int argc UNUSED_PARAM, char **argv) { static const struct suffix_mult size_suffixes[] = { { "KiB", 1024 }, { "MiB", 1024*1024 }, { "GiB", 1024*1024*1024 }, { "", 0 } }; unsigned opts; char *ubi_ctrl; int fd; int mtd_num; int dev_num = UBI_DEV_NUM_AUTO; int vol_id = UBI_VOL_NUM_AUTO; char *vol_name; unsigned long long size_bytes = size_bytes; /* for compiler */ char *size_bytes_str; int alignment = 1; char *type; union { struct ubi_attach_req attach_req; struct ubi_mkvol_req mkvol_req; struct ubi_rsvol_req rsvol_req; } req_structs; #define attach_req req_structs.attach_req #define mkvol_req req_structs.mkvol_req #define rsvol_req req_structs.rsvol_req char path[sizeof("/sys/class/ubi/ubi%d_%d/usable_eb_size") + 2 * sizeof(int)*3 + /*just in case:*/ 16]; #define path_sys_class_ubi_ubi (path + sizeof("/sys/class/ubi/ubi")-1) strcpy(path, "/sys/class/ubi/ubi"); memset(&req_structs, 0, sizeof(req_structs)); if (do_mkvol) { opt_complementary = "-1:d+:n+:a+"; opts = getopt32(argv, "md:n:N:s:a:t:", &dev_num, &vol_id, &vol_name, &size_bytes_str, &alignment, &type ); } else { opt_complementary = "-1:m+:d+:n+:a+"; opts = getopt32(argv, "m:d:n:N:s:a:t:", &mtd_num, &dev_num, &vol_id, &vol_name, &size_bytes_str, &alignment, &type ); } #define OPTION_m (1 << 0) #define OPTION_d (1 << 1) #define OPTION_n (1 << 2) #define OPTION_N (1 << 3) #define OPTION_s (1 << 4) #define OPTION_a (1 << 5) #define OPTION_t (1 << 6) if (opts & OPTION_s) size_bytes = xatoull_sfx(size_bytes_str, size_suffixes); argv += optind; ubi_ctrl = *argv++; fd = xopen(ubi_ctrl, O_RDWR); //xfstat(fd, &st, ubi_ctrl); //if (!S_ISCHR(st.st_mode)) // bb_error_msg_and_die("%s: not a char device", ubi_ctrl); //usage:#define ubiattach_trivial_usage //usage: "-m MTD_NUM [-d UBI_NUM] UBI_CTRL_DEV" //usage:#define ubiattach_full_usage "\n\n" //usage: "Attach MTD device to UBI\n" //usage: "\n -m MTD_NUM MTD device number to attach" //usage: "\n -d UBI_NUM UBI device number to assign" if (do_attach) { if (!(opts & OPTION_m)) bb_error_msg_and_die("%s device not specified", "MTD"); attach_req.mtd_num = mtd_num; attach_req.ubi_num = dev_num; xioctl(fd, UBI_IOCATT, &attach_req); } else //usage:#define ubidetach_trivial_usage //usage: "-d UBI_NUM UBI_CTRL_DEV" //usage:#define ubidetach_full_usage "\n\n" //usage: "Detach MTD device from UBI\n" //usage: "\n -d UBI_NUM UBI device number" if (do_detach) { if (!(opts & OPTION_d)) bb_error_msg_and_die("%s device not specified", "UBI"); /* FIXME? kernel expects int32_t* here: */ xioctl(fd, UBI_IOCDET, &dev_num); } else //usage:#define ubimkvol_trivial_usage //usage: "UBI_DEVICE -N NAME [-s SIZE | -m]" //usage:#define ubimkvol_full_usage "\n\n" //usage: "Create UBI volume\n" //usage: "\n -a ALIGNMENT Volume alignment (default 1)" //usage: "\n -m Set volume size to maximum available" //usage: "\n -n VOLID Volume ID. If not specified," //usage: "\n assigned automatically" //usage: "\n -N NAME Volume name" //usage: "\n -s SIZE Size in bytes" //usage: "\n -t TYPE Volume type (static|dynamic)" if (do_mkvol) { if (opts & OPTION_m) { unsigned leb_avail; unsigned leb_size; unsigned num; char *p; if (sscanf(ubi_ctrl, "/dev/ubi%u", &num) != 1) bb_error_msg_and_die("wrong format of UBI device name"); p = path_sys_class_ubi_ubi + sprintf(path_sys_class_ubi_ubi, "%u/", num); strcpy(p, "avail_eraseblocks"); leb_avail = get_num_from_file(path, UINT_MAX, "Can't get available eraseblocks from '%s'"); strcpy(p, "eraseblock_size"); leb_size = get_num_from_file(path, MAX_SANE_ERASEBLOCK, "Can't get eraseblock size from '%s'"); size_bytes = leb_avail * (unsigned long long)leb_size; //if (size_bytes <= 0) // bb_error_msg_and_die("%s invalid maximum size calculated", "UBI"); } else if (!(opts & OPTION_s)) bb_error_msg_and_die("size not specified"); if (!(opts & OPTION_N)) bb_error_msg_and_die("name not specified"); mkvol_req.vol_id = vol_id; mkvol_req.vol_type = UBI_DYNAMIC_VOLUME; if ((opts & OPTION_t) && type[0] == 's') mkvol_req.vol_type = UBI_STATIC_VOLUME; mkvol_req.alignment = alignment; mkvol_req.bytes = size_bytes; /* signed int64_t */ strncpy(mkvol_req.name, vol_name, UBI_MAX_VOLUME_NAME); mkvol_req.name_len = strlen(vol_name); if (mkvol_req.name_len > UBI_MAX_VOLUME_NAME) bb_error_msg_and_die("volume name too long: '%s'", vol_name); xioctl(fd, UBI_IOCMKVOL, &mkvol_req); } else //usage:#define ubirmvol_trivial_usage //usage: "UBI_DEVICE -n VOLID" //usage:#define ubirmvol_full_usage "\n\n" //usage: "Remove UBI volume\n" //usage: "\n -n VOLID Volume ID" if (do_rmvol) { if (!(opts & OPTION_n)) bb_error_msg_and_die("volume id not specified"); /* FIXME? kernel expects int32_t* here: */ xioctl(fd, UBI_IOCRMVOL, &vol_id); } else //usage:#define ubirsvol_trivial_usage //usage: "UBI_DEVICE -n VOLID -s SIZE" //usage:#define ubirsvol_full_usage "\n\n" //usage: "Resize UBI volume\n" //usage: "\n -n VOLID Volume ID" //usage: "\n -s SIZE Size in bytes" if (do_rsvol) { if (!(opts & OPTION_s)) bb_error_msg_and_die("size not specified"); if (!(opts & OPTION_n)) bb_error_msg_and_die("volume id not specified"); rsvol_req.bytes = size_bytes; /* signed int64_t */ rsvol_req.vol_id = vol_id; xioctl(fd, UBI_IOCRSVOL, &rsvol_req); } else //usage:#define ubiupdatevol_trivial_usage //usage: "UBI_DEVICE [-t | [-s SIZE] IMG_FILE]" //usage:#define ubiupdatevol_full_usage "\n\n" //usage: "Update UBI volume\n" //usage: "\n -t Truncate to zero size" //usage: "\n -s SIZE Size in bytes to resize to" if (do_update) { int64_t bytes64; if (opts & OPTION_t) { /* truncate the volume by starting an update for size 0 */ bytes64 = 0; /* this ioctl expects int64_t* parameter */ xioctl(fd, UBI_IOCVOLUP, &bytes64); } else { struct stat st; unsigned ubinum, volnum; unsigned leb_size; ssize_t len; char *input_data; /* Assume that device is in normal format. */ /* Removes need for scanning sysfs tree as full libubi does. */ if (sscanf(ubi_ctrl, "/dev/ubi%u_%u", &ubinum, &volnum) != 2) bb_error_msg_and_die("wrong format of UBI device name"); sprintf(path_sys_class_ubi_ubi, "%u_%u/usable_eb_size", ubinum, volnum); leb_size = get_num_from_file(path, MAX_SANE_ERASEBLOCK, "Can't get usable eraseblock size from '%s'"); if (!(opts & OPTION_s)) { if (!*argv) bb_show_usage(); xstat(*argv, &st); size_bytes = st.st_size; xmove_fd(xopen(*argv, O_RDONLY), STDIN_FILENO); } bytes64 = size_bytes; /* this ioctl expects signed int64_t* parameter */ xioctl(fd, UBI_IOCVOLUP, &bytes64); input_data = xmalloc(leb_size); while ((len = full_read(STDIN_FILENO, input_data, leb_size)) > 0) { xwrite(fd, input_data, len); } if (len < 0) bb_perror_msg_and_die("UBI volume update failed"); } } if (ENABLE_FEATURE_CLEAN_UP) close(fd); return EXIT_SUCCESS; }
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; opt_complementary = "nn"; /* count -n */ IF_LONG_OPTS(applet_long_options = sed_longopts); /* -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 = getopt32(argv, "i::rEne:*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 & (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; }
const char *filename1, *filename2 = "-"; IF_DESKTOP(off_t skip1 = 0, skip2 = 0;) off_t char_pos = 0; int line_pos = 1; /* Hopefully won't overflow... */ const char *fmt; int c1, c2; unsigned opt; int retval = 0; xfunc_error_retval = 2; /* 1 is returned if files are different. */ opt_complementary = "-1" IF_DESKTOP(":?4") IF_NOT_DESKTOP(":?2") ":l--s:s--l"; opt = getopt32(argv, opt_chars); argv += optind; filename1 = *argv; fp1 = xfopen_stdin(filename1); if (*++argv) { filename2 = *argv; #if ENABLE_DESKTOP if (*++argv) { skip1 = XATOOFF(*argv); if (*++argv) { skip2 = XATOOFF(*argv); } } #endif
int dmesg_main(int argc UNUSED_PARAM, char **argv) { int len, level; char *buf; unsigned opts; enum { OPT_c = 1 << 0, OPT_s = 1 << 1, OPT_n = 1 << 2 }; opt_complementary = "s+:n+"; /* numeric */ opts = getopt32(argv, "cs:n:", &len, &level); if (opts & OPT_n) { if (klogctl(8, NULL, (long) level)) bb_perror_msg_and_die("klogctl"); return EXIT_SUCCESS; } if (!(opts & OPT_s)) len = klogctl(10, NULL, 0); /* read ring buffer size */ if (len < 16*1024) len = 16*1024; if (len > 16*1024*1024) len = 16*1024*1024; buf = xmalloc(len); len = klogctl(3 + (opts & OPT_c), buf, len); /* read ring buffer */ if (len < 0) bb_perror_msg_and_die("klogctl"); if (len == 0) return EXIT_SUCCESS; if (ENABLE_FEATURE_DMESG_PRETTY) { int last = '\n'; int in = 0; /* Skip <#> at the start of lines */ while (1) { if (last == '\n' && buf[in] == '<') { in += 3; if (in >= len) break; } last = buf[in]; putchar(last); in++; if (in >= len) break; } /* Make sure we end with a newline */ if (last != '\n') bb_putchar('\n'); } else { full_write(STDOUT_FILENO, buf, len); if (buf[len-1] != '\n') bb_putchar('\n'); } if (ENABLE_FEATURE_CLEAN_UP) free(buf); return EXIT_SUCCESS; }
int zcip_main(int argc, char **argv) { int state = PROBE; struct ether_addr eth_addr; char *r_opt; unsigned opts; // ugly trick, but I want these zeroed in one go struct { const struct in_addr null_ip; const struct ether_addr null_addr; struct in_addr ip; struct ifreq ifr; char *script_av[3]; 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 script_av (L.script_av ) #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)); #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:v", &r_opt, &verbose); #if !BB_MMU // on NOMMU reexec early (or else we will rerun things twice) if (!FOREGROUND) bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv); #endif // open an ARP socket // (need to do it before openlog to prevent openlog from taking // fd 3 (sock_fd==3)) xmove_fd(xsocket(AF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)), sock_fd); if (!FOREGROUND) { // do it before all bb_xx_msg calls openlog(applet_name, 0, LOG_DAEMON); logmode |= LOGMODE_SYSLOG; } if (opts & 4) { // -r n.n.n.n if (inet_aton(r_opt, &ip) == 0 || (ntohl(ip.s_addr) & IN_CLASSB_NET) != LINKLOCAL_ADDR ) { bb_error_msg_and_die("invalid link address"); } } argc -= optind; argv += optind; intf = argv[0]; script_av[0] = argv[1]; setenv("interface", intf, 1); // initialize the interface (modprobe, ifup, etc) script_av[1] = (char*)"init"; if (run(script_av, 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, 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(ifr.ifr_name, intf, sizeof(ifr.ifr_name)); 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; memcpy(&t, (char*)ð_addr + 2, 4); srand(t); } if (ip.s_addr == 0) pick(&ip); // 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(DAEMON_CHDIR_ROOT); #endif bb_info_msg("start, interface %s", 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 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, 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, intf, inet_ntoa(ip)); arp(ARPOP_REQUEST, ð_addr, null_ip, &null_addr, ip); timeout_ms = PROBE_MIN * 1000; timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN); } else { // Switch to announce state. state = ANNOUNCE; nclaims = 0; VDBG("announce/%u %s@%s\n", nclaims, intf, inet_ntoa(ip)); arp(ARPOP_REQUEST, ð_addr, ip, ð_addr, ip); timeout_ms = ANNOUNCE_INTERVAL * 1000; } break; case RATE_LIMIT_PROBE: // timeouts in the RATE_LIMIT_PROBE state mean no conflicting ARP packets // have been received, so we can move immediately to the announce state state = ANNOUNCE; nclaims = 0; VDBG("announce/%u %s@%s\n", nclaims, intf, inet_ntoa(ip)); arp(ARPOP_REQUEST, ð_addr, ip, ð_addr, ip); timeout_ms = ANNOUNCE_INTERVAL * 1000; break; case ANNOUNCE: // timeouts in the ANNOUNCE state mean no conflicting ARP packets // have been received, so we can progress through the states if (nclaims < ANNOUNCE_NUM) { nclaims++; VDBG("announce/%u %s@%s\n", nclaims, intf, inet_ntoa(ip)); arp(ARPOP_REQUEST, ð_addr, ip, ð_addr, ip); timeout_ms = ANNOUNCE_INTERVAL * 1000; } else { // Switch to monitor state. state = MONITOR; // link is ok to use earlier // FIXME update filters script_av[1] = (char*)"config"; run(script_av, &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; pick(&ip); 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", intf); if (ready) { script_av[1] = (char*)"deconfig"; run(script_av, &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", intf, ntohs(p.eth.ether_type), ntohs(p.arp.arp_op)); VDBG("\tsource=%s %s\n", ether_ntoa(sha), inet_ntoa(*spa)); VDBG("\ttarget=%s %s\n", ether_ntoa(tha), inet_ntoa(*tpa)); } #endif if (p.arp.arp_op != htons(ARPOP_REQUEST) && p.arp.arp_op != htons(ARPOP_REPLY)) continue; source_ip_conflict = 0; target_ip_conflict = 0; if (memcmp(p.arp.arp_spa, &ip.s_addr, sizeof(struct in_addr)) == 0 && memcmp(&p.arp.arp_sha, ð_addr, ETH_ALEN) != 0 ) { source_ip_conflict = 1; } if (p.arp.arp_op == htons(ARPOP_REQUEST) && memcmp(p.arp.arp_tpa, &ip.s_addr, sizeof(struct in_addr)) == 0 && memcmp(&p.arp.arp_tha, ð_addr, ETH_ALEN) != 0 ) { target_ip_conflict = 1; } VDBG("state = %d, source ip conflict = %d, target ip conflict = %d\n", state, source_ip_conflict, target_ip_conflict); switch (state) { case PROBE: case ANNOUNCE: // When probing or announcing, check for source IP conflicts // and other hosts doing ARP probes (target IP conflicts). if (source_ip_conflict || target_ip_conflict) { conflicts++; if (conflicts >= MAX_CONFLICTS) { VDBG("%s ratelimit\n", intf); timeout_ms = RATE_LIMIT_INTERVAL * 1000; state = RATE_LIMIT_PROBE; } // restart the whole protocol pick(&ip); 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; script_av[1] = (char*)"deconfig"; run(script_av, &ip); // restart the whole protocol pick(&ip); 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; pick(&ip); timeout_ms = 0; nprobes = 0; nclaims = 0; break; } // switch state break; // case 1 (packets arriving) } // switch poll } // while (1) }
int crontab_main(int argc UNUSED_PARAM, char **argv) { const struct passwd *pas; const char *crontab_dir = CRONTABS; char *tmp_fname; char *new_fname; char *user_name; /* -u USER */ int fd; int src_fd; int opt_ler; /* file [opts] Replace crontab from file * - [opts] Replace crontab from stdin * -u user User * -c dir Crontab directory * -l List crontab for user * -e Edit crontab for user * -r Delete crontab for user * bbox also supports -d == -r, but most other crontab * implementations do not. Deprecated. */ enum { OPT_u = (1 << 0), OPT_c = (1 << 1), OPT_l = (1 << 2), OPT_e = (1 << 3), OPT_r = (1 << 4), OPT_ler = OPT_l + OPT_e + OPT_r, }; opt_complementary = "?1:dr"; /* max one argument; -d implies -r */ opt_ler = getopt32(argv, "u:c:lerd", &user_name, &crontab_dir); argv += optind; if (sanitize_env_if_suid()) { /* Clears dangerous stuff, sets PATH */ /* Run by non-root */ if (opt_ler & (OPT_u|OPT_c)) bb_error_msg_and_die(bb_msg_you_must_be_root); } if (opt_ler & OPT_u) { pas = xgetpwnam(user_name); } else { pas = xgetpwuid(getuid()); } #define user_name DONT_USE_ME_BEYOND_THIS_POINT /* From now on, keep only -l, -e, -r bits */ opt_ler &= OPT_ler; if ((opt_ler - 1) & opt_ler) /* more than one bit set? */ bb_show_usage(); /* Read replacement file under user's UID/GID/group vector */ src_fd = STDIN_FILENO; if (!opt_ler) { /* Replace? */ if (!argv[0]) bb_show_usage(); if (NOT_LONE_DASH(argv[0])) { src_fd = open_as_user(pas, argv[0]); if (src_fd < 0) bb_error_msg_and_die("user %s cannot read %s", pas->pw_name, argv[0]); } } /* cd to our crontab directory */ xchdir(crontab_dir); tmp_fname = NULL; /* Handle requested operation */ switch (opt_ler) { default: /* case OPT_r: Delete */ unlink(pas->pw_name); break; case OPT_l: /* List */ { char *args[2] = { pas->pw_name, NULL }; return bb_cat(args); /* list exits, * the rest go play with cron update file */ } case OPT_e: /* Edit */ tmp_fname = xasprintf("%s.%u", crontab_dir, (unsigned)getpid()); /* No O_EXCL: we don't want to be stuck if earlier crontabs * were killed, leaving stale temp file behind */ src_fd = xopen3(tmp_fname, O_RDWR|O_CREAT|O_TRUNC, 0600); fchown(src_fd, pas->pw_uid, pas->pw_gid); fd = open(pas->pw_name, O_RDONLY); if (fd >= 0) { bb_copyfd_eof(fd, src_fd); close(fd); xlseek(src_fd, 0, SEEK_SET); } close_on_exec_on(src_fd); /* don't want editor to see this fd */ edit_file(pas, tmp_fname); /* fall through */ case 0: /* Replace (no -l, -e, or -r were given) */ new_fname = xasprintf("%s.new", pas->pw_name); fd = open(new_fname, O_WRONLY|O_CREAT|O_TRUNC|O_APPEND, 0600); if (fd >= 0) { bb_copyfd_eof(src_fd, fd); close(fd); xrename(new_fname, pas->pw_name); } else { bb_error_msg("can't create %s/%s", crontab_dir, new_fname); } if (tmp_fname) unlink(tmp_fname); /*free(tmp_fname);*/ /*free(new_fname);*/ } /* switch */ /* Bump notification file. Handle window where crond picks file up * before we can write our entry out. */ while ((fd = open(CRONUPDATE, O_WRONLY|O_CREAT|O_APPEND, 0600)) >= 0) { struct stat st; fdprintf(fd, "%s\n", pas->pw_name); if (fstat(fd, &st) != 0 || st.st_nlink != 0) { /*close(fd);*/ break; } /* st.st_nlink == 0: * file was deleted, maybe crond missed our notification */ close(fd); /* loop */ } if (fd < 0) { bb_error_msg("can't append to %s/%s", crontab_dir, CRONUPDATE); } return 0; }