/* Always sets uid and gid */ int FAST_FUNC get_uidgid(struct bb_uidgid_t *u, const char *ug, int numeric_ok) { struct passwd *pwd; struct group *gr; char *user, *group; unsigned n; user = (char*)ug; group = strchr(ug, ':'); if (group) { int sz = (++group) - ug; user = alloca(sz); /* copies sz-1 bytes, stores terminating '\0' */ safe_strncpy(user, ug, sz); } if (numeric_ok) { n = bb_strtou(user, NULL, 10); if (!errno) { u->uid = n; pwd = getpwuid(n); /* If we have e.g. "500" string without user */ /* with uid 500 in /etc/passwd, we set gid == uid */ u->gid = pwd ? pwd->pw_gid : n; goto skip; } } /* Either it is not numeric, or caller disallows numeric username */ pwd = getpwnam(user); if (!pwd) return 0; u->uid = pwd->pw_uid; u->gid = pwd->pw_gid; skip: if (group) { if (numeric_ok) { n = bb_strtou(group, NULL, 10); if (!errno) { u->gid = n; return 1; } } gr = getgrnam(group); if (!gr) return 0; u->gid = gr->gr_gid; } return 1; }
/* NB: does chdir internally */ static void scan_proc_pids(void) { DIR *d; struct dirent *de; pid_t pid; xchdir("/proc"); d = opendir("/proc"); if (!d) return; while ((de = readdir(d)) != NULL) { pid = (pid_t)bb_strtou(de->d_name, NULL, 10); if (errno) continue; if (chdir(de->d_name) < 0) continue; scan_link("cwd", pid); scan_link("exe", pid); scan_link("root", pid); scan_dir_links("fd", pid); scan_dir_links("lib", pid); scan_dir_links("mmap", pid); scan_pid_maps("maps", pid); xchdir("/proc"); } closedir(d); }
/* NB: does chdir internally */ static pid_list *scan_proc_pids(inode_list *ilist) { DIR *d; struct dirent *de; pid_t pid; pid_list *plist; xchdir("/proc"); d = opendir("/proc"); if (!d) return NULL; plist = NULL; while ((de = readdir(d)) != NULL) { pid = (pid_t)bb_strtou(de->d_name, NULL, 10); if (errno) continue; if (chdir(de->d_name) < 0) continue; plist = scan_link("cwd", pid, ilist, plist); plist = scan_link("exe", pid, ilist, plist); plist = scan_link("root", pid, ilist, plist); plist = scan_dir_links("fd", pid, ilist, plist); plist = scan_dir_links("lib", pid, ilist, plist); plist = scan_dir_links("mmap", pid, ilist, plist); plist = scan_pid_maps("maps", pid, ilist, plist); xchdir("/proc"); } closedir(d); return plist; }
static void do_procinit(void) { DIR *procdir; struct dirent *entry; int pid; if (pidfile) { do_pidfile(); return; } procdir = xopendir("/proc"); pid = 0; while (1) { errno = 0; /* clear any previous error */ entry = readdir(procdir); // TODO: this check is too generic, it's better // to check for exact errno(s) which mean that we got stale entry if (errno) /* Stale entry, process has died after opendir */ continue; if (!entry) /* EOF, no more entries */ break; pid = bb_strtou(entry->d_name, NULL, 10); if (errno) /* NaN */ continue; check(pid); } closedir(procdir); if (!pid) bb_error_msg_and_die("nothing in /proc - not mounted?"); }
int get_signum(const char *name) { int i; i = bb_strtou(name, NULL, 10); if (!errno) return i; if (strncasecmp(name, "SIG", 3) == 0) name += 3; for (i = 0; i < sizeof(signals) / sizeof(signals[0]); i++) if (strcasecmp(name, signals[i]) == 0) return i; #if ENABLE_DESKTOP && (defined(SIGIOT) || defined(SIGIO)) /* These are aliased to other names */ if ((name[0] | 0x20) == 'i' && (name[1] | 0x20) == 'o') { #ifdef SIGIO if (!name[2]) return SIGIO; #endif #ifdef SIGIOT if ((name[2] | 0x20) == 't' && !name[3]) return SIGIOT; #endif } #endif return -1; }
static void get_prefix_1(inet_prefix *dst, char *arg, int family) { char *slash; memset(dst, 0, sizeof(*dst)); if (strcmp(arg, "default") == 0 || strcmp(arg, "all") == 0 || strcmp(arg, "any") == 0 ) { dst->family = family; /*dst->bytelen = 0; - done by memset */ /*dst->bitlen = 0;*/ return; } slash = strchr(arg, '/'); if (slash) *slash = '\0'; if (get_addr_1(dst, arg, family) == 0) { dst->bitlen = (dst->family == AF_INET6) ? 128 : 32; if (slash) { unsigned plen; inet_prefix netmask_pfx; netmask_pfx.family = AF_UNSPEC; plen = bb_strtou(slash + 1, NULL, 0); if ((errno || plen > (unsigned) dst->bitlen) && get_addr_1(&netmask_pfx, slash + 1, family) != 0 ) { goto bad; } if (netmask_pfx.family == AF_INET) { /* fill in prefix length of dotted quad */ uint32_t mask = ntohl(netmask_pfx.data[0]); uint32_t host = ~mask; /* a valid netmask must be 2^n - 1 */ if (host & (host + 1)) goto bad; for (plen = 0; mask; mask <<= 1) ++plen; if (plen > (unsigned) dst->bitlen) goto bad; /* dst->flags |= PREFIXLEN_SPECIFIED; */ } dst->bitlen = plen; } } if (slash) *slash = '/'; return; bad: bb_error_msg_and_die("an %s %s is expected rather than \"%s\"", "inet", "prefix", arg); }
static int get_prefix_1(inet_prefix *dst, char *arg, int family) { int err; unsigned plen; char *slash; memset(dst, 0, sizeof(*dst)); if (strcmp(arg, bb_str_default) == 0 || strcmp(arg, "all") == 0 || strcmp(arg, "any") == 0 ) { dst->family = family; /*dst->bytelen = 0; - done by memset */ /*dst->bitlen = 0;*/ return 0; } slash = strchr(arg, '/'); if (slash) *slash = '\0'; err = get_addr_1(dst, arg, family); if (err == 0) { dst->bitlen = (dst->family == AF_INET6) ? 128 : 32; if (slash) { inet_prefix netmask_pfx; netmask_pfx.family = AF_UNSPEC; plen = bb_strtou(slash + 1, NULL, 0); if ((errno || plen > dst->bitlen) && (get_addr_1(&netmask_pfx, slash + 1, family))) err = -1; else if (netmask_pfx.family == AF_INET) { /* fill in prefix length of dotted quad */ uint32_t mask = ntohl(netmask_pfx.data[0]); uint32_t host = ~mask; /* a valid netmask must be 2^n - 1 */ if (!(host & (host + 1))) { for (plen = 0; mask; mask <<= 1) ++plen; if (plen <= dst->bitlen) { dst->bitlen = plen; /* dst->flags |= PREFIXLEN_SPECIFIED; */ } else err = -1; } else err = -1; } else { /* plain prefix */ dst->bitlen = plen; } } } if (slash) *slash = '/'; return err; }
static int dump_procs(FILE *fp, int look_for_login_process) { struct dirent *entry; DIR *dir = opendir("/proc"); int found_login_process = 0; fputs(G.jiffy_line, fp); while ((entry = readdir(dir)) != NULL) { char name[sizeof("/proc/%u/cmdline") + sizeof(int)*3]; int stat_fd; unsigned pid = bb_strtou(entry->d_name, NULL, 10); if (errno) continue; /* Android's version reads /proc/PID/cmdline and extracts * non-truncated process name. Do we want to do that? */ sprintf(name, "/proc/%u/stat", pid); stat_fd = open(name, O_RDONLY); if (stat_fd >= 0) { char *p; char stat_line[4*1024]; int rd = safe_read(stat_fd, stat_line, sizeof(stat_line)-2); close(stat_fd); if (rd < 0) continue; stat_line[rd] = '\0'; p = strchrnul(stat_line, '\n'); *p++ = '\n'; *p = '\0'; fputs(stat_line, fp); if (!look_for_login_process) continue; p = strchr(stat_line, '('); if (!p) continue; p++; strchrnul(p, ')')[0] = '\0'; /* Is it gdm, kdm or a getty? */ if (((p[0] == 'g' || p[0] == 'k' || p[0] == 'x') && p[1] == 'd' && p[2] == 'm' && p[3] == '\0' ) || strstr(p, "getty") ) { found_login_process = 1; } } } closedir(dir); fputc('\n', fp); return found_login_process; }
/* String to timespec: "NNNN[.NNNNN]" -> struct timespec. * Can be used for other fixed-point needs. * Returns pointer past last converted char, * and returns errno similar to bb_strtoXX functions. */ char* FAST_FUNC bb_str_to_ts(struct timespec *ts, const char *arg) { if (sizeof(ts->tv_sec) <= sizeof(int)) ts->tv_sec = bb_strtou(arg, &arg, 10); else if (sizeof(ts->tv_sec) <= sizeof(long)) ts->tv_sec = bb_strtoul(arg, &arg, 10); else ts->tv_sec = bb_strtoull(arg, &arg, 10); ts->tv_nsec = 0; if (*arg != '.') return arg; /* !EINVAL: number is not ok (alphanumeric ending, overflow etc) */ if (errno != EINVAL) return arg; if (!*++arg) /* "NNN." */ return arg; { /* "NNN.xxx" - parse xxx */ int ndigits; char *p; char buf[10]; /* we never use more than 9 digits */ /* Need to make a copy to avoid false overflow */ safe_strncpy(buf, arg, 10); ts->tv_nsec = bb_strtou(buf, &p, 10); ndigits = p - buf; arg += ndigits; /* normalize to nsec */ while (ndigits < 9) { ndigits++; ts->tv_nsec *= 10; } while (isdigit(*arg)) /* skip possible 10th plus digits */ arg++; } return arg; }
static void rtnl_tab_initialize(const char *file, const char **tab, int size) { char *token[2]; parser_t *parser = config_open2(file, fopen_for_read); while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) { int id = bb_strtou(token[0], NULL, 0); if (id < 0 || id > size) { bb_error_msg("database %s is corrupted at line %d", file, parser->lineno); break; } tab[id] = xstrdup(token[1]); } config_close(parser); }
static void dev_get_major_minor(char *device_name, int *major, int *minor) { char dev[16]; char *dev_path; char *colon; int sz; dev_path = xasprintf("/sys/block/%s/dev", device_name); sz = open_read_close(dev_path, dev, sizeof(dev) - 1); if (sz < 0) goto ret; dev[sz] = '\0'; colon = strchr(dev, ':'); if (!colon) goto ret; *major = bb_strtou(dev, NULL, 10); *minor = bb_strtou(colon + 1, NULL, 10); ret: free(dev_path); return; }
static int tftp_blksize_check(const char *blksize_str, int maxsize) { /* Check if the blksize is valid: * RFC2348 says between 8 and 65464, * but our implementation makes it impossible * to use blksizes smaller than 22 octets. */ unsigned blksize = bb_strtou(blksize_str, NULL, 10); if (errno || (blksize < 24) || (blksize > maxsize) ) { bb_error_msg("bad blocksize '%s'", blksize_str); return -1; } # if ENABLE_TFTP_DEBUG bb_error_msg("using blksize %u", blksize); # endif return blksize; }
int FAST_FUNC ll_proto_a2n(unsigned short *id, char *buf) { unsigned i; const char *name = llproto_names; for (i = 0; i < ARRAY_SIZE(llproto_ids); i++) { if (strcasecmp(name, buf) == 0) { i = llproto_ids[i]; goto good; } name += strlen(name) + 1; } errno = 0; i = bb_strtou(buf, NULL, 0); if (errno || i > 0xffff) return -1; good: *id = htons(i); return 0; }
/* Return port number for a service. * If "port" is a number use it as the port. * If "port" is a name it is looked up in /etc/services, if it isnt found return * default_port */ unsigned FAST_FUNC bb_lookup_port(const char *port, const char *protocol, unsigned default_port) { unsigned port_nr = default_port; if (port) { int old_errno; /* Since this is a lib function, we're not allowed to reset errno to 0. * Doing so could break an app that is deferring checking of errno. */ old_errno = errno; port_nr = bb_strtou(port, NULL, 10); if (errno || port_nr > 65535) { struct servent *tserv = getservbyname(port, protocol); port_nr = default_port; if (tserv) port_nr = ntohs(tserv->s_port); } errno = old_errno; } return (uint16_t)port_nr; }
static NOINLINE int process_timer_stats(void) { char buf[128]; char line[15 + 3 + 128]; int n; FILE *fp; buf[0] = '\0'; n = 0; fp = NULL; if (!G.cant_enable_timer_stats) fp = fopen_for_read("/proc/timer_stats"); if (fp) { // Example file contents: // Timer Stats Version: v0.2 // Sample period: 1.329 s // 76, 0 swapper hrtimer_start_range_ns (tick_sched_timer) // 88, 0 swapper hrtimer_start_range_ns (tick_sched_timer) // 24, 3787 firefox hrtimer_start_range_ns (hrtimer_wakeup) // 46D, 1136 kondemand/1 do_dbs_timer (delayed_work_timer_fn) // ... // 1, 1656 Xorg hrtimer_start_range_ns (hrtimer_wakeup) // 1, 2159 udisks-daemon hrtimer_start_range_ns (hrtimer_wakeup) // 331 total events, 249.059 events/sec while (fgets(buf, sizeof(buf), fp)) { const char *count, *process, *func; char *p; int idx; unsigned cnt; count = skip_whitespace(buf); p = strchr(count, ','); if (!p) continue; *p++ = '\0'; cnt = bb_strtou(count, NULL, 10); if (strcmp(skip_non_whitespace(count), " total events") == 0) { #if ENABLE_FEATURE_POWERTOP_PROCIRQ n = cnt / G.total_cpus; if (n > 0 && n < G.interrupt_0) { sprintf(line, " <interrupt> : %s", "extra timer interrupt"); save_line(line, G.interrupt_0 - n); } #endif break; } if (strchr(count, 'D')) continue; /* deferred */ p = skip_whitespace(p); /* points to pid now */ process = NULL; get_func_name: p = strchr(p, ' '); if (!p) continue; *p++ = '\0'; p = skip_whitespace(p); if (process == NULL) { process = p; goto get_func_name; } func = p; //if (strcmp(process, "swapper") == 0 // && strcmp(func, "hrtimer_start_range_ns (tick_sched_timer)\n") == 0 //) { // process = "[kernel scheduler]"; // func = "Load balancing tick"; //} if (strncmp(func, "tick_nohz_", 10) == 0) continue; if (strncmp(func, "tick_setup_sched_timer", 20) == 0) continue; //if (strcmp(process, "powertop") == 0) // continue; idx = index_in_strings("insmod\0modprobe\0swapper\0", process); if (idx != -1) { process = idx < 2 ? "[kernel module]" : "<kernel core>"; } strchrnul(p, '\n')[0] = '\0'; // 46D\01136\0kondemand/1\0do_dbs_timer (delayed_work_timer_fn) // ^ ^ ^ // count process func //if (strchr(process, '[')) sprintf(line, "%15.15s : %s", process, func); //else // sprintf(line, "%s", process); save_line(line, cnt); } fclose(fp); } return n; }
static void number_process(int first_digit) { unsigned i; int num; int keypress; char num_input[sizeof(int)*4]; /* more than enough */ num_input[0] = first_digit; /* Clear the current line, print a prompt, and then print the digit */ clear_line(); printf(":%c", first_digit); /* Receive input until a letter is given */ i = 1; while (i < sizeof(num_input)-1) { keypress = less_getch(i + 1); if ((unsigned)keypress > 255 || !isdigit(num_input[i])) break; num_input[i] = keypress; bb_putchar(keypress); i++; } num_input[i] = '\0'; num = bb_strtou(num_input, NULL, 10); /* on format error, num == -1 */ if (num < 1 || num > MAXLINES) { buffer_print(); return; } /* We now know the number and the letter entered, so we process them */ switch (keypress) { case KEYCODE_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015': buffer_down(num); break; case KEYCODE_UP: case 'b': case 'w': case 'y': case 'u': buffer_up(num); break; case 'g': case '<': case 'G': case '>': cur_fline = num + max_displayed_line; read_lines(); buffer_line(num - 1); break; case 'p': case '%': num = num * (max_fline / 100); /* + max_fline / 2; */ cur_fline = num + max_displayed_line; read_lines(); buffer_line(num); break; #if ENABLE_FEATURE_LESS_REGEXP case 'n': goto_match(match_pos + num); break; case '/': option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS; regex_process(); break; case '?': option_mask32 |= LESS_STATE_MATCH_BACKWARDS; regex_process(); break; #endif } }
int start_stop_daemon_main(int argc UNUSED_PARAM, char **argv) { unsigned opt; char *signame; char *startas; char *chuid; #if ENABLE_FEATURE_START_STOP_DAEMON_FANCY // char *retry_arg = NULL; // int retries = -1; char *opt_N; #endif INIT_G(); opt = GETOPT32(argv, "^" "KSbqtma:n:s:u:c:x:p:" IF_FEATURE_START_STOP_DAEMON_FANCY("ovN:R:") /* -K or -S is required; they are mutually exclusive */ /* -p is required if -m is given */ /* -xpun (at least one) is required if -K is given */ /* -xa (at least one) is required if -S is given */ /* -q turns off -v */ "\0" "K:S:K--S:S--K:m?p:K?xpun:S?xa" IF_FEATURE_START_STOP_DAEMON_FANCY("q-v"), LONGOPTS &startas, &cmdname, &signame, &userspec, &chuid, &execname, &pidfile IF_FEATURE_START_STOP_DAEMON_FANCY(,&opt_N) /* We accept and ignore -R <param> / --retry <param> */ IF_FEATURE_START_STOP_DAEMON_FANCY(,NULL) ); if (opt & OPT_s) { signal_nr = get_signum(signame); if (signal_nr < 0) bb_show_usage(); } if (!(opt & OPT_a)) startas = execname; if (!execname) /* in case -a is given and -x is not */ execname = startas; if (execname) { G.execname_sizeof = strlen(execname) + 1; G.execname_cmpbuf = xmalloc(G.execname_sizeof + 1); } // IF_FEATURE_START_STOP_DAEMON_FANCY( // if (retry_arg) // retries = xatoi_positive(retry_arg); // ) //argc -= optind; argv += optind; if (userspec) { user_id = bb_strtou(userspec, NULL, 10); if (errno) user_id = xuname2uid(userspec); } /* Both start and stop need to know current processes */ do_procinit(); if (opt & CTX_STOP) { int i = do_stop(); return (opt & OPT_OKNODO) ? 0 : (i <= 0); } if (G.found_procs) { if (!QUIET) printf("%s is already running\n%u\n", execname, (unsigned)G.found_procs->pid); return !(opt & OPT_OKNODO); } #ifdef OLDER_VERSION_OF_X if (execname) xstat(execname, &G.execstat); #endif *--argv = startas; if (opt & OPT_BACKGROUND) { #if BB_MMU bb_daemonize(DAEMON_DEVNULL_STDIO + DAEMON_CLOSE_EXTRA_FDS + DAEMON_DOUBLE_FORK); /* DAEMON_DEVNULL_STDIO is superfluous - * it's always done by bb_daemonize() */ #else /* Daemons usually call bb_daemonize_or_rexec(), but SSD can do * without: SSD is not itself a daemon, it _execs_ a daemon. * The usual NOMMU problem of "child can't run indefinitely, * it must exec" does not bite us: we exec anyway. */ pid_t pid = xvfork(); if (pid != 0) { /* parent */ /* why _exit? the child may have changed the stack, * so "return 0" may do bad things */ _exit(EXIT_SUCCESS); } /* Child */ setsid(); /* detach from controlling tty */ /* Redirect stdio to /dev/null, close extra FDs */ bb_daemon_helper(DAEMON_DEVNULL_STDIO + DAEMON_CLOSE_EXTRA_FDS); #endif } if (opt & OPT_MAKEPID) { /* User wants _us_ to make the pidfile */ write_pidfile(pidfile); } if (opt & OPT_c) { struct bb_uidgid_t ugid; parse_chown_usergroup_or_die(&ugid, chuid); if (ugid.uid != (uid_t) -1L) { struct passwd *pw = xgetpwuid(ugid.uid); if (ugid.gid != (gid_t) -1L) pw->pw_gid = ugid.gid; /* initgroups, setgid, setuid: */ change_identity(pw); } else if (ugid.gid != (gid_t) -1L) { xsetgid(ugid.gid); setgroups(1, &ugid.gid); } } #if ENABLE_FEATURE_START_STOP_DAEMON_FANCY if (opt & OPT_NICELEVEL) { /* Set process priority */ int prio = getpriority(PRIO_PROCESS, 0) + xatoi_range(opt_N, INT_MIN/2, INT_MAX/2); if (setpriority(PRIO_PROCESS, 0, prio) < 0) { bb_perror_msg_and_die("setpriority(%d)", prio); } } #endif execvp(startas, argv); bb_perror_msg_and_die("can't execute '%s'", startas); }
int kill_main(int argc, char **argv) { char *arg; pid_t pid; int signo = SIGTERM, errors = 0, quiet = 0; #if !ENABLE_KILLALL && !ENABLE_KILLALL5 #define killall 0 #define killall5 0 #else /* How to determine who we are? find 3rd char from the end: * kill, killall, killall5 * ^i ^a ^l - it's unique * (checking from the start is complicated by /bin/kill... case) */ const char char3 = argv[0][strlen(argv[0]) - 3]; #define killall (ENABLE_KILLALL && char3 == 'a') #define killall5 (ENABLE_KILLALL5 && char3 == 'l') #endif /* Parse any options */ argc--; arg = *++argv; if (argc < 1 || arg[0] != '-') { goto do_it_now; } /* The -l option, which prints out signal names. * Intended usage in shell: * echo "Died of SIG`kill -l $?`" * We try to mimic what kill from coreutils-6.8 does */ if (arg[1] == 'l' && arg[2] == '\0') { if (argc == 1) { /* Print the whole signal list */ print_signames(); return 0; } /* -l <sig list> */ while ((arg = *++argv)) { if (isdigit(arg[0])) { signo = bb_strtou(arg, NULL, 10); if (errno) { bb_error_msg("unknown signal '%s'", arg); return EXIT_FAILURE; } /* Exitcodes >= 0x80 are to be treated * as "killed by signal (exitcode & 0x7f)" */ puts(get_signame(signo & 0x7f)); /* TODO: 'bad' signal# - coreutils says: * kill: 127: invalid signal * we just print "127" instead */ } else { signo = get_signum(arg); if (signo < 0) { bb_error_msg("unknown signal '%s'", arg); return EXIT_FAILURE; } printf("%d\n", signo); } } /* If they specified -l, we are all done */ return EXIT_SUCCESS; } /* The -q quiet option */ if (killall && arg[1] == 'q' && arg[2] == '\0') { quiet = 1; arg = *++argv; argc--; if (argc < 1) bb_show_usage(); if (arg[0] != '-') goto do_it_now; } /* -SIG */ signo = get_signum(&arg[1]); if (signo < 0) { /* || signo > MAX_SIGNUM ? */ bb_error_msg("bad signal name '%s'", &arg[1]); return EXIT_FAILURE; } arg = *++argv; argc--; do_it_now: pid = getpid(); if (killall5) { pid_t sid; procps_status_t* p = NULL; /* Find out our own session id */ sid = getsid(pid); /* Now stop all processes */ kill(-1, SIGSTOP); /* Now kill all processes except our session */ while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_SID))) { if (p->sid != (unsigned)sid && p->pid != (unsigned)pid && p->pid != 1) kill(p->pid, signo); } /* And let them continue */ kill(-1, SIGCONT); return 0; } /* Pid or name is required for kill/killall */ if (argc < 1) { bb_error_msg("you need to specify whom to kill"); return EXIT_FAILURE; } if (killall) { /* Looks like they want to do a killall. Do that */ while (arg) { pid_t* pidList; pidList = find_pid_by_name(arg); if (*pidList == 0) { errors++; if (!quiet) bb_error_msg("%s: no process killed", arg); } else { pid_t *pl; for (pl = pidList; *pl; pl++) { if (*pl == pid) continue; if (kill(*pl, signo) == 0) continue; errors++; if (!quiet) bb_perror_msg("cannot kill pid %u", (unsigned)*pl); } } free(pidList); arg = *++argv; } return errors; } /* Looks like they want to do a kill. Do that */ while (arg) { /* Support shell 'space' trick */ if (arg[0] == ' ') arg++; pid = bb_strtoi(arg, NULL, 10); if (errno) { bb_error_msg("bad pid '%s'", arg); errors++; } else if (kill(pid, signo) != 0) { bb_perror_msg("cannot kill pid %d", (int)pid); errors++; } arg = *++argv; } return errors; }
static smallint scan_recursive(const char *path) { DIR *d; struct dirent *d_ent; smallint stop_scan; smallint retval; d = opendir(path); if (d == NULL) return 0; G.recursion_depth++; retval = 0; stop_scan = 0; while (!stop_scan && (d_ent = readdir(d)) != NULL) { struct stat statbuf; pid_t pid; char *subpath; subpath = concat_subpath_file(path, d_ent->d_name); if (subpath == NULL) continue; /* . or .. */ switch (G.recursion_depth) { case PROC_DIR: pid = (pid_t)bb_strtou(d_ent->d_name, NULL, 10); if (errno != 0 || pid == G.mypid /* "this PID doesn't use specified FILEs or PORT/PROTO": */ || scan_recursive(subpath) == 0 ) { break; } if (option_mask32 & OPT_KILL) { if (kill(pid, G.killsig) != 0) { bb_perror_msg("kill pid %s", d_ent->d_name); G.kill_failed = 1; } } if (!(option_mask32 & OPT_SILENT)) printf("%s ", d_ent->d_name); retval = 1; break; case PROC_DIR_LINKS: switch ( index_in_substrings( "cwd" "\0" "exe" "\0" "root" "\0" "fd" "\0" "lib" "\0" "mmap" "\0" "maps" "\0", d_ent->d_name ) ) { enum { CWD_LINK, EXE_LINK, ROOT_LINK, FD_DIR_LINKS, LIB_DIR_LINKS, MMAP_DIR_LINKS, MAPS, }; case CWD_LINK: case EXE_LINK: case ROOT_LINK: goto scan_link; case FD_DIR_LINKS: case LIB_DIR_LINKS: case MMAP_DIR_LINKS: // change for ofgwrite retval = scan_recursive(subpath); break; case MAPS: // change for ofgwrite retval = scan_proc_net_or_maps(subpath, 0); default: break; } break; case PROC_SUBDIR_LINKS: scan_link: if (stat(subpath, &statbuf) < 0) break; // change for ofgwrite retval = search_dev_inode(&statbuf); if (retval) { if (strcmp(d_ent->d_name, "exe") == 0) { char* ln = xmalloc_readlink(subpath); if (ln != NULL) { // change for ofgwrite: Don't kill VU+ and GB specific processes if (strcmp(ln, "/oldroot/usr/bin/dvb_server") == 0 || strcmp(ln, "/oldroot/usr/bin/init_client") == 0 || strcmp(ln, "/oldroot/usr/bin/ntfs-3g") == 0 || strcmp(ln, "/oldroot/usr/share/platform/dvb_init") == 0 || strcmp(ln, "/oldroot/usr/bin/nxserver") == 0 || strcmp(ln, "/oldroot/usr/bin/init_driver") == 0 || strcmp(ln, "/oldroot/usr/share/platform/dvb_init.bin") == 0 || strcmp(ln, "/oldroot/usr/share/platform/nxserver") == 0 || strcmp(ln, "/oldroot/usr/bin/showiframe") == 0 || strcmp(ln, "/oldroot/usr/bin/libreader") == 0 || ( strncmp(ln, "/oldroot/lib/modules/", 21) == 0 && strstr(ln, "/extra/hi_play.ko") != NULL ) ) { my_printf("found vu or gb or octagon or ntfs process %s -> don't kill\n", ln); retval = 0; stop_scan=1; } free(ln); } } } default: break; } free(subpath); } closedir(d); G.recursion_depth--; return retval; }
procps_status_t* procps_scan(procps_status_t* sp, int flags) { struct dirent *entry; char buf[PROCPS_BUFSIZE]; char filename[sizeof("/proc//cmdline") + sizeof(int)*3]; char *filename_tail; long tasknice; unsigned pid; int n; struct stat sb; if (!sp) sp = alloc_procps_scan(flags); for (;;) { entry = readdir(sp->dir); if (entry == NULL) { free_procps_scan(sp); return NULL; } pid = bb_strtou(entry->d_name, NULL, 10); if (errno) continue; /* After this point we have to break, not continue * ("continue" would mean that current /proc/NNN * is not a valid process info) */ memset(&sp->vsz, 0, sizeof(*sp) - offsetof(procps_status_t, vsz)); sp->pid = pid; if (!(flags & ~PSSCAN_PID)) break; filename_tail = filename + sprintf(filename, "/proc/%d", pid); if (flags & PSSCAN_UIDGID) { if (stat(filename, &sb)) break; /* Need comment - is this effective or real UID/GID? */ sp->uid = sb.st_uid; sp->gid = sb.st_gid; } if (flags & PSSCAN_STAT) { char *cp; /* see proc(5) for some details on this */ strcpy(filename_tail, "/stat"); n = read_to_buf(filename, buf); if (n < 0) break; cp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */ if (!cp || cp[1] != ' ') break; cp[0] = '\0'; if (sizeof(sp->comm) < 16) BUG_comm_size(); sscanf(buf, "%*s (%15c", sp->comm); n = sscanf(cp+2, "%c %u " /* state, ppid */ "%u %u %*s %*s " /* pgid, sid, tty, tpgid */ "%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */ "%lu %lu " /* utime, stime */ "%*s %*s %*s " /* cutime, cstime, priority */ "%ld " /* nice */ "%*s %*s %*s " /* timeout, it_real_value, start_time */ "%lu ", /* vsize */ sp->state, &sp->ppid, &sp->pgid, &sp->sid, &sp->utime, &sp->stime, &tasknice, &sp->vsz); if (n != 8) break; if (sp->vsz == 0 && sp->state[0] != 'Z') sp->state[1] = 'W'; else sp->state[1] = ' '; if (tasknice < 0) sp->state[2] = '<'; else if (tasknice > 0) sp->state[2] = 'N'; else sp->state[2] = ' '; sp->vsz >>= 10; /* vsize is in bytes and we want kb */ } if (flags & PSSCAN_CMD) { free(sp->cmd); sp->cmd = NULL; strcpy(filename_tail, "/cmdline"); n = read_to_buf(filename, buf); if (n <= 0) break; if (buf[n-1] == '\n') { if (!--n) break; buf[n] = '\0'; } do { n--; if ((unsigned char)(buf[n]) < ' ') buf[n] = ' '; } while (n); sp->cmd = xstrdup(buf); } break; }
int renice_main(int argc, char **argv) { static const char Xetpriority_msg[] ALIGN1 = "%cetpriority"; int retval = EXIT_SUCCESS; int which = PRIO_PROCESS; /* Default 'which' value. */ int use_relative = 0; int adjustment, new_priority; unsigned who; char *arg; /* Yes, they are not #defines in glibc 2.4! #if won't work */ if (PRIO_PROCESS < CHAR_MIN || PRIO_PROCESS > CHAR_MAX) BUG_bad_PRIO_PROCESS(); if (PRIO_PGRP < CHAR_MIN || PRIO_PGRP > CHAR_MAX) BUG_bad_PRIO_PGRP(); if (PRIO_USER < CHAR_MIN || PRIO_USER > CHAR_MAX) BUG_bad_PRIO_USER(); arg = *++argv; /* Check if we are using a relative adjustment. */ if (arg && arg[0] == '-' && arg[1] == 'n') { use_relative = 1; if (!arg[2]) arg = *++argv; else arg += 2; } if (!arg) { /* No args? Then show usage. */ bb_show_usage(); } /* Get the priority adjustment (absolute or relative). */ adjustment = xatoi_range(arg, INT_MIN/2, INT_MAX/2); while ((arg = *++argv) != NULL) { /* Check for a mode switch. */ if (arg[0] == '-' && arg[1]) { static const char opts[] ALIGN1 = { 'p', 'g', 'u', 0, PRIO_PROCESS, PRIO_PGRP, PRIO_USER }; const char *p = strchr(opts, arg[1]); if (p) { which = p[4]; if (!arg[2]) continue; arg += 2; } } /* Process an ID arg. */ if (which == PRIO_USER) { struct passwd *p; p = getpwnam(arg); if (!p) { bb_error_msg("unknown user: %s", arg); goto HAD_ERROR; } who = p->pw_uid; } else { who = bb_strtou(arg, NULL, 10); if (errno) { bb_error_msg("bad value: %s", arg); goto HAD_ERROR; } } /* Get priority to use, and set it. */ if (use_relative) { int old_priority; errno = 0; /* Needed for getpriority error detection. */ old_priority = getpriority(which, who); if (errno) { bb_perror_msg(Xetpriority_msg, 'g'); goto HAD_ERROR; } new_priority = old_priority + adjustment; } else { new_priority = adjustment; } if (setpriority(which, who, new_priority) == 0) { continue; } bb_perror_msg(Xetpriority_msg, 's'); HAD_ERROR: retval = EXIT_FAILURE; } /* No need to check for errors outputing to stderr since, if it * was used, the HAD_ERROR label was reached and retval was set. */ return retval; }
//TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL" //string. hush naturally has it, and ash has setvareq(). //Here we can simply store "VAR=" at buffer start and store read data directly //after "=", then pass buffer to setvar() to consume. const char* FAST_FUNC shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), char **argv, const char *ifs, int read_flags, const char *opt_n, const char *opt_p, const char *opt_t, const char *opt_u ) { unsigned end_ms; /* -t TIMEOUT */ int fd; /* -u FD */ int nchars; /* -n NUM */ char **pp; char *buffer; struct termios tty, old_tty; const char *retval; int bufpos; /* need to be able to hold -1 */ int startword; smallint backslash; pp = argv; while (*pp) { if (!is_well_formed_var_name(*pp, '\0')) { /* Mimic bash message */ bb_error_msg("read: '%s': not a valid identifier", *pp); return (const char *)(uintptr_t)1; } pp++; } nchars = 0; /* if != 0, -n is in effect */ if (opt_n) { nchars = bb_strtou(opt_n, NULL, 10); if (nchars < 0 || errno) return "invalid count"; /* note: "-n 0": off (bash 3.2 does this too) */ } end_ms = 0; if (opt_t) { end_ms = bb_strtou(opt_t, NULL, 10); if (errno || end_ms > UINT_MAX / 2048) return "invalid timeout"; end_ms *= 1000; #if 0 /* even bash has no -t N.NNN support */ ts.tv_sec = bb_strtou(opt_t, &p, 10); ts.tv_usec = 0; /* EINVAL means number is ok, but not terminated by NUL */ if (*p == '.' && errno == EINVAL) { char *p2; if (*++p) { int scale; ts.tv_usec = bb_strtou(p, &p2, 10); if (errno) return "invalid timeout"; scale = p2 - p; /* normalize to usec */ if (scale > 6) return "invalid timeout"; while (scale++ < 6) ts.tv_usec *= 10; } } else if (ts.tv_sec < 0 || errno) { return "invalid timeout"; } if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */ return "invalid timeout"; } #endif /* if 0 */ } fd = STDIN_FILENO; if (opt_u) { fd = bb_strtou(opt_u, NULL, 10); if (fd < 0 || errno) return "invalid file descriptor"; } if (opt_p && isatty(fd)) { fputs(opt_p, stderr); fflush_all(); } if (ifs == NULL) ifs = defifs; if (nchars || (read_flags & BUILTIN_READ_SILENT)) { tcgetattr(fd, &tty); old_tty = tty; if (nchars) { tty.c_lflag &= ~ICANON; tty.c_cc[VMIN] = nchars < 256 ? nchars : 255; } if (read_flags & BUILTIN_READ_SILENT) { tty.c_lflag &= ~(ECHO | ECHOK | ECHONL); } /* This forces execution of "restoring" tcgetattr later */ read_flags |= BUILTIN_READ_SILENT; /* if tcgetattr failed, tcsetattr will fail too. * Ignoring, it's harmless. */ tcsetattr(fd, TCSANOW, &tty); } retval = (const char *)(uintptr_t)0; startword = 1; backslash = 0; if (end_ms) /* NB: end_ms stays nonzero: */ end_ms = ((unsigned)monotonic_ms() + end_ms) | 1; buffer = NULL; bufpos = 0; do { char c; if (end_ms) { int timeout; struct pollfd pfd[1]; pfd[0].fd = fd; pfd[0].events = POLLIN; timeout = end_ms - (unsigned)monotonic_ms(); if (timeout <= 0 /* already late? */ || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */ ) { /* timed out! */ retval = (const char *)(uintptr_t)1; goto ret; } } if ((bufpos & 0xff) == 0) buffer = xrealloc(buffer, bufpos + 0x100); if (nonblock_safe_read(fd, &buffer[bufpos], 1) != 1) { retval = (const char *)(uintptr_t)1; break; } c = buffer[bufpos]; if (c == '\0') continue; if (backslash) { backslash = 0; if (c != '\n') goto put; continue; } if (!(read_flags & BUILTIN_READ_RAW) && c == '\\') { backslash = 1; continue; } if (c == '\n') break; /* $IFS splitting. NOT done if we run "read" * without variable names (bash compat). * Thus, "read" and "read REPLY" are not the same. */ if (argv[0]) { /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */ const char *is_ifs = strchr(ifs, c); if (startword && is_ifs) { if (isspace(c)) continue; /* it is a non-space ifs char */ startword--; if (startword == 1) /* first one? */ continue; /* yes, it is not next word yet */ } startword = 0; if (argv[1] != NULL && is_ifs) { buffer[bufpos] = '\0'; bufpos = 0; setvar(*argv, buffer); argv++; /* can we skip one non-space ifs char? (2: yes) */ startword = isspace(c) ? 2 : 1; continue; } } put: bufpos++; } while (--nchars); if (argv[0]) { /* Remove trailing space $IFS chars */ while (--bufpos >= 0 && isspace(buffer[bufpos]) && strchr(ifs, buffer[bufpos]) != NULL) continue; buffer[bufpos + 1] = '\0'; /* Use the remainder as a value for the next variable */ setvar(*argv, buffer); /* Set the rest to "" */ while (*++argv) setvar(*argv, ""); } else { /* Note: no $IFS removal */ buffer[bufpos] = '\0'; setvar("REPLY", buffer); } ret: free(buffer); if (read_flags & BUILTIN_READ_SILENT) tcsetattr(fd, TCSANOW, &old_tty); return retval; }
/* Run uuidcache_check_device() for every device mentioned * in /proc/partitions */ static void uuidcache_init_partitions(void) { char line[100]; int ma, mi; unsigned long long sz; FILE *procpt; int firstPass; int handleOnFirst; char *chptr; procpt = xfopen("/proc/partitions", "r"); /* # cat /proc/partitions major minor #blocks name 8 0 293036184 sda 8 1 6835626 sda1 8 2 1 sda2 8 5 979933 sda5 8 6 15623181 sda6 8 7 97659103 sda7 8 8 171935631 sda8 */ for (firstPass = 1; firstPass >= 0; firstPass--) { fseek(procpt, 0, SEEK_SET); while (fgets(line, sizeof(line), procpt)) { /* The original version of this code used sscanf, but diet's sscanf is quite limited */ chptr = line; if (*chptr != ' ') continue; chptr = skip_whitespace(chptr); ma = bb_strtou(chptr, &chptr, 0); if (ma < 0) continue; chptr = skip_whitespace(chptr); mi = bb_strtou(chptr, &chptr, 0); if (mi < 0) continue; chptr = skip_whitespace(chptr); sz = bb_strtoull(chptr, &chptr, 0); if ((long long)sz == -1LL) continue; chptr = skip_whitespace(chptr); /* skip extended partitions (heuristic: size 1) */ if (sz == 1) continue; *strchrnul(chptr, '\n') = '\0'; /* now chptr => device name */ dbg("/proc/partitions: maj:%d min:%d sz:%llu name:'%s'", ma, mi, sz, chptr); if (!chptr[0]) continue; /* look only at md devices on first pass */ handleOnFirst = (chptr[0] == 'm' && chptr[1] == 'd'); if (firstPass != handleOnFirst) continue; /* heuristic: partition name ends in a digit */ if (isdigit(chptr[strlen(chptr) - 1])) { uuidcache_check_device(chptr, ma, mi, 0); } } } fclose(procpt); }
int FAST_FUNC shell_builtin_ulimit(char **argv) { unsigned opts; unsigned argc; /* We can't use getopt32: need to handle commands like * ulimit 123 -c2 -l 456 */ /* In case getopt was already called: * reset the libc getopt() function, which keeps internal state. */ #ifdef __GLIBC__ optind = 0; #else /* BSD style */ optind = 1; /* optreset = 1; */ #endif /* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */ argc = 1; while (argv[argc]) argc++; opts = 0; while (1) { struct rlimit limit; const struct limits *l; int opt_char = getopt(argc, argv, ulimit_opt_string); if (opt_char == -1) break; if (opt_char == 'H') { opts |= OPT_hard; continue; } if (opt_char == 'S') { opts |= OPT_soft; continue; } if (opt_char == 'a') { for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) { getrlimit(l->cmd, &limit); printf("-%c: %-30s ", l->option, l->name); printlim(opts, &limit, l); } continue; } if (opt_char == 1) opt_char = 'f'; for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) { if (opt_char == l->option) { char *val_str; getrlimit(l->cmd, &limit); val_str = optarg; if (!val_str && argv[optind] && argv[optind][0] != '-') val_str = argv[optind++]; /* ++ skips NN in "-c NN" case */ if (val_str) { rlim_t val; if (strcmp(val_str, "unlimited") == 0) val = RLIM_INFINITY; else { if (sizeof(val) == sizeof(int)) val = bb_strtou(val_str, NULL, 10); else if (sizeof(val) == sizeof(long)) val = bb_strtoul(val_str, NULL, 10); else val = bb_strtoull(val_str, NULL, 10); if (errno) { bb_error_msg("invalid number '%s'", val_str); return EXIT_FAILURE; } val <<= l->factor_shift; } //bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val); /* from man bash: "If neither -H nor -S * is specified, both the soft and hard * limits are set. */ if (!opts) opts = OPT_hard + OPT_soft; if (opts & OPT_hard) limit.rlim_max = val; if (opts & OPT_soft) limit.rlim_cur = val; //bb_error_msg("setrlimit(%d, %lld, %lld)", l->cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max); if (setrlimit(l->cmd, &limit) < 0) { bb_perror_msg("error setting limit"); return EXIT_FAILURE; } } else { printlim(opts, &limit, l); } break; } } /* for (every possible opt) */ if (l == &limits_tbl[ARRAY_SIZE(limits_tbl)]) { /* bad option. getopt already complained. */ break; } } /* while (there are options) */ return 0; }
int lpd_main(int argc UNUSED_PARAM, char *argv[]) { int spooling = spooling; // for compiler char *s, *queue; char *filenames[2]; // goto spool directory if (*++argv) xchdir(*argv++); // error messages of xfuncs will be sent over network xdup2(STDOUT_FILENO, STDERR_FILENO); // nullify ctrl/data filenames memset(filenames, 0, sizeof(filenames)); // read command s = queue = xmalloc_read_stdin(); // we understand only "receive job" command if (2 != *queue) { unsupported_cmd: printf("Command %02x %s\n", (unsigned char)s[0], "is not supported"); goto err_exit; } // parse command: "2 | QUEUE_NAME | '\n'" queue++; // protect against "/../" attacks // *strchrnul(queue, '\n') = '\0'; - redundant, sane() will do if (!*sane(queue)) return EXIT_FAILURE; // queue is a directory -> chdir to it and enter spooling mode spooling = chdir(queue) + 1; // 0: cannot chdir, 1: done // we don't free(s), we might need "queue" var later while (1) { char *fname; int fd; // int is easier than ssize_t: can use xatoi_u, // and can correctly display error returns (-1) int expected_len, real_len; // signal OK safe_write(STDOUT_FILENO, "", 1); // get subcommand // valid s must be of form: "SUBCMD | LEN | space | FNAME" // N.B. we bail out on any error s = xmalloc_read_stdin(); if (!s) { // (probably) EOF char *p, *q, var[2]; // non-spooling mode or no spool helper specified if (!spooling || !*argv) return EXIT_SUCCESS; // the only non-error exit // spooling mode but we didn't see both ctrlfile & datafile if (spooling != 7) goto err_exit; // reject job // spooling mode and spool helper specified -> exec spool helper // (we exit 127 if helper cannot be executed) var[1] = '\0'; // read and delete ctrlfile q = xmalloc_xopen_read_close(filenames[0], NULL); unlink(filenames[0]); // provide datafile name // we can use leaky setenv since we are about to exec or exit xsetenv("DATAFILE", filenames[1]); // parse control file by "\n" while ((p = strchr(q, '\n')) != NULL && isalpha(*q)) { *p++ = '\0'; // q is a line of <SYM><VALUE>, // we are setting environment string <SYM>=<VALUE>. // Ignoring "l<datafile>", exporting others: if (*q != 'l') { var[0] = *q++; xsetenv(var, q); } q = p; // next line } // helper should not talk over network. // this call reopens stdio fds to "/dev/null" // (no daemonization is done) bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO | DAEMON_ONLY_SANITIZE, NULL); BB_EXECVP(*argv, argv); exit(127); } // validate input. // we understand only "control file" or "data file" cmds if (2 != s[0] && 3 != s[0]) goto unsupported_cmd; if (spooling & (1 << (s[0]-1))) { printf("Duplicated subcommand\n"); goto err_exit; } // get filename *strchrnul(s, '\n') = '\0'; fname = strchr(s, ' '); if (!fname) { // bad_fname: printf("No or bad filename\n"); goto err_exit; } *fname++ = '\0'; // // s[0]==2: ctrlfile, must start with 'c' // // s[0]==3: datafile, must start with 'd' // if (fname[0] != s[0] + ('c'-2)) // goto bad_fname; // get length expected_len = bb_strtou(s + 1, NULL, 10); if (errno || expected_len < 0) { printf("Bad length\n"); goto err_exit; } if (2 == s[0] && expected_len > 16 * 1024) { // SECURITY: // ctrlfile can't be big (we want to read it back later!) printf("File is too big\n"); goto err_exit; } // open the file if (spooling) { // spooling mode: dump both files // job in flight has mode 0200 "only writable" sane(fname); fd = open3_or_warn(fname, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0200); if (fd < 0) goto err_exit; filenames[s[0] - 2] = xstrdup(fname); } else { // non-spooling mode: // 2: control file (ignoring), 3: data file fd = -1; if (3 == s[0]) fd = xopen(queue, O_RDWR | O_APPEND); } // signal OK safe_write(STDOUT_FILENO, "", 1); // copy the file real_len = bb_copyfd_size(STDIN_FILENO, fd, expected_len); if (real_len != expected_len) { printf("Expected %d but got %d bytes\n", expected_len, real_len); goto err_exit; } // get EOF indicator, see whether it is NUL (ok) // (and don't trash s[0]!) if (safe_read(STDIN_FILENO, &s[1], 1) != 1 || s[1] != 0) { // don't send error msg to peer - it obviously // doesn't follow the protocol, so probably // it can't understand us either goto err_exit; } if (spooling) { // chmod completely downloaded file as "readable+writable" fchmod(fd, 0600); // accumulate dump state // N.B. after all files are dumped spooling should be 1+2+4==7 spooling |= (1 << (s[0]-1)); // bit 1: ctrlfile; bit 2: datafile } free(s); close(fd); // NB: can do close(-1). Who cares? // NB: don't do "signal OK" write here, it will be done // at the top of the loop } // while (1) err_exit: // don't keep corrupted files if (spooling) { #define i spooling for (i = 2; --i >= 0; ) if (filenames[i]) unlink(filenames[i]); } return EXIT_FAILURE; }
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]); }
NOINLINE sha_crypt(/*const*/ char *key_data, /*const*/ char *salt_data) { void (*sha_begin)(void *ctx) FAST_FUNC; void (*sha_hash)(void *ctx, const void *buffer, size_t len) FAST_FUNC; void (*sha_end)(void *ctx, void *resbuf) FAST_FUNC; int _32or64; char *result, *resptr; /* btw, sha256 needs [32] and uint32_t only */ struct { unsigned char alt_result[64]; unsigned char temp_result[64]; union { sha256_ctx_t x; sha512_ctx_t y; } ctx; union { sha256_ctx_t x; sha512_ctx_t y; } alt_ctx; } L __attribute__((__aligned__(__alignof__(uint64_t)))); #define alt_result (L.alt_result ) #define temp_result (L.temp_result) #define ctx (L.ctx ) #define alt_ctx (L.alt_ctx ) unsigned salt_len; unsigned key_len; unsigned cnt; unsigned rounds; char *cp; char is_sha512; /* Analyze salt, construct already known part of result */ cnt = strlen(salt_data) + 1 + 43 + 1; is_sha512 = salt_data[1]; if (is_sha512 == '6') cnt += 43; result = resptr = xzalloc(cnt); /* will provide NUL terminator */ *resptr++ = '$'; *resptr++ = is_sha512; *resptr++ = '$'; rounds = ROUNDS_DEFAULT; salt_data += 3; if (strncmp(salt_data, str_rounds, 7) == 0) { /* 7 == strlen("rounds=") */ char *endp; cnt = bb_strtou(salt_data + 7, &endp, 10); if (*endp == '$') { salt_data = endp + 1; rounds = cnt; if (rounds < ROUNDS_MIN) rounds = ROUNDS_MIN; if (rounds > ROUNDS_MAX) rounds = ROUNDS_MAX; /* add "rounds=NNNNN$" to result */ resptr += sprintf(resptr, str_rounds, rounds); } } salt_len = strchrnul(salt_data, '$') - salt_data; if (salt_len > SALT_LEN_MAX) salt_len = SALT_LEN_MAX; /* xstrdup assures suitable alignment; also we will use it as a scratch space later. */ salt_data = xstrndup(salt_data, salt_len); /* add "salt$" to result */ strcpy(resptr, salt_data); resptr += salt_len; *resptr++ = '$'; /* key data doesn't need much processing */ key_len = strlen(key_data); key_data = xstrdup(key_data); /* Which flavor of SHAnnn ops to use? */ sha_begin = (void*)sha256_begin; sha_hash = (void*)sha256_hash; sha_end = (void*)sha256_end; _32or64 = 32; if (is_sha512 == '6') { sha_begin = (void*)sha512_begin; sha_hash = (void*)sha512_hash; sha_end = (void*)sha512_end; _32or64 = 64; } /* Add KEY, SALT. */ sha_begin(&ctx); sha_hash(&ctx, key_data, key_len); sha_hash(&ctx, salt_data, salt_len); /* Compute alternate SHA sum with input KEY, SALT, and KEY. The final result will be added to the first context. */ sha_begin(&alt_ctx); sha_hash(&alt_ctx, key_data, key_len); sha_hash(&alt_ctx, salt_data, salt_len); sha_hash(&alt_ctx, key_data, key_len); sha_end(&alt_ctx, alt_result); /* Add result of this to the other context. */ /* Add for any character in the key one byte of the alternate sum. */ for (cnt = key_len; cnt > _32or64; cnt -= _32or64) sha_hash(&ctx, alt_result, _32or64); sha_hash(&ctx, alt_result, cnt); /* Take the binary representation of the length of the key and for every 1 add the alternate sum, for every 0 the key. */ for (cnt = key_len; cnt != 0; cnt >>= 1) if ((cnt & 1) != 0) sha_hash(&ctx, alt_result, _32or64); else sha_hash(&ctx, key_data, key_len); /* Create intermediate result. */ sha_end(&ctx, alt_result); /* Start computation of P byte sequence. */ /* For every character in the password add the entire password. */ sha_begin(&alt_ctx); for (cnt = 0; cnt < key_len; ++cnt) sha_hash(&alt_ctx, key_data, key_len); sha_end(&alt_ctx, temp_result); /* NB: past this point, raw key_data is not used anymore */ /* Create byte sequence P. */ #define p_bytes key_data /* reuse the buffer as it is of the key_len size */ cp = p_bytes; /* was: ... = alloca(key_len); */ for (cnt = key_len; cnt >= _32or64; cnt -= _32or64) { cp = memcpy(cp, temp_result, _32or64); cp += _32or64; } memcpy(cp, temp_result, cnt); /* Start computation of S byte sequence. */ /* For every character in the password add the entire password. */ sha_begin(&alt_ctx); for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt) sha_hash(&alt_ctx, salt_data, salt_len); sha_end(&alt_ctx, temp_result); /* NB: past this point, raw salt_data is not used anymore */ /* Create byte sequence S. */ #define s_bytes salt_data /* reuse the buffer as it is of the salt_len size */ cp = s_bytes; /* was: ... = alloca(salt_len); */ for (cnt = salt_len; cnt >= _32or64; cnt -= _32or64) { cp = memcpy(cp, temp_result, _32or64); cp += _32or64; } memcpy(cp, temp_result, cnt); /* Repeatedly run the collected hash value through SHA to burn CPU cycles. */ for (cnt = 0; cnt < rounds; ++cnt) { sha_begin(&ctx); /* Add key or last result. */ if ((cnt & 1) != 0) sha_hash(&ctx, p_bytes, key_len); else sha_hash(&ctx, alt_result, _32or64); /* Add salt for numbers not divisible by 3. */ if (cnt % 3 != 0) sha_hash(&ctx, s_bytes, salt_len); /* Add key for numbers not divisible by 7. */ if (cnt % 7 != 0) sha_hash(&ctx, p_bytes, key_len); /* Add key or last result. */ if ((cnt & 1) != 0) sha_hash(&ctx, alt_result, _32or64); else sha_hash(&ctx, p_bytes, key_len); sha_end(&ctx, alt_result); } /* Append encrypted password to result buffer */ //TODO: replace with something like // bb_uuencode(cp, src, length, bb_uuenc_tbl_XXXbase64); #define b64_from_24bit(B2, B1, B0, N) \ do { \ unsigned w = ((B2) << 16) | ((B1) << 8) | (B0); \ resptr = to64(resptr, w, N); \ } while (0) if (is_sha512 == '5') { unsigned i = 0; while (1) { unsigned j = i + 10; unsigned k = i + 20; if (j >= 30) j -= 30; if (k >= 30) k -= 30; b64_from_24bit(alt_result[i], alt_result[j], alt_result[k], 4); if (k == 29) break; i = k + 1; } b64_from_24bit(0, alt_result[31], alt_result[30], 3); /* was: b64_from_24bit(alt_result[0], alt_result[10], alt_result[20], 4); b64_from_24bit(alt_result[21], alt_result[1], alt_result[11], 4); b64_from_24bit(alt_result[12], alt_result[22], alt_result[2], 4); b64_from_24bit(alt_result[3], alt_result[13], alt_result[23], 4); b64_from_24bit(alt_result[24], alt_result[4], alt_result[14], 4); b64_from_24bit(alt_result[15], alt_result[25], alt_result[5], 4); b64_from_24bit(alt_result[6], alt_result[16], alt_result[26], 4); b64_from_24bit(alt_result[27], alt_result[7], alt_result[17], 4); b64_from_24bit(alt_result[18], alt_result[28], alt_result[8], 4); b64_from_24bit(alt_result[9], alt_result[19], alt_result[29], 4); b64_from_24bit(0, alt_result[31], alt_result[30], 3); */ } else { unsigned i = 0; while (1) { unsigned j = i + 21; unsigned k = i + 42; if (j >= 63) j -= 63; if (k >= 63) k -= 63; b64_from_24bit(alt_result[i], alt_result[j], alt_result[k], 4); if (j == 20) break; i = j + 1; } b64_from_24bit(0, 0, alt_result[63], 2); /* was: b64_from_24bit(alt_result[0], alt_result[21], alt_result[42], 4); b64_from_24bit(alt_result[22], alt_result[43], alt_result[1], 4); b64_from_24bit(alt_result[44], alt_result[2], alt_result[23], 4); b64_from_24bit(alt_result[3], alt_result[24], alt_result[45], 4); b64_from_24bit(alt_result[25], alt_result[46], alt_result[4], 4); b64_from_24bit(alt_result[47], alt_result[5], alt_result[26], 4); b64_from_24bit(alt_result[6], alt_result[27], alt_result[48], 4); b64_from_24bit(alt_result[28], alt_result[49], alt_result[7], 4); b64_from_24bit(alt_result[50], alt_result[8], alt_result[29], 4); b64_from_24bit(alt_result[9], alt_result[30], alt_result[51], 4); b64_from_24bit(alt_result[31], alt_result[52], alt_result[10], 4); b64_from_24bit(alt_result[53], alt_result[11], alt_result[32], 4); b64_from_24bit(alt_result[12], alt_result[33], alt_result[54], 4); b64_from_24bit(alt_result[34], alt_result[55], alt_result[13], 4); b64_from_24bit(alt_result[56], alt_result[14], alt_result[35], 4); b64_from_24bit(alt_result[15], alt_result[36], alt_result[57], 4); b64_from_24bit(alt_result[37], alt_result[58], alt_result[16], 4); b64_from_24bit(alt_result[59], alt_result[17], alt_result[38], 4); b64_from_24bit(alt_result[18], alt_result[39], alt_result[60], 4); b64_from_24bit(alt_result[40], alt_result[61], alt_result[19], 4); b64_from_24bit(alt_result[62], alt_result[20], alt_result[41], 4); b64_from_24bit(0, 0, alt_result[63], 2); */ } /* *resptr = '\0'; - xzalloc did it */ #undef b64_from_24bit /* Clear the buffer for the intermediate result so that people attaching to processes or reading core dumps cannot get any information. */ memset(&L, 0, sizeof(L)); /* [alt]_ctx and XXX_result buffers */ memset(key_data, 0, key_len); /* also p_bytes */ memset(salt_data, 0, salt_len); /* also s_bytes */ free(key_data); free(salt_data); #undef p_bytes #undef s_bytes return result; #undef alt_result #undef temp_result #undef ctx #undef alt_ctx }
int kill_main(int argc, char **argv) { char *arg; pid_t pid; int signo = SIGTERM, errors = 0, quiet = 0; #if !ENABLE_KILLALL && !ENABLE_KILLALL5 #define killall 0 #define killall5 0 #else /* How to determine who we are? find 3rd char from the end: * kill, killall, killall5 * ^i ^a ^l - it's unique * (checking from the start is complicated by /bin/kill... case) */ const char char3 = argv[0][strlen(argv[0]) - 3]; #define killall (ENABLE_KILLALL && char3 == 'a') #define killall5 (ENABLE_KILLALL5 && char3 == 'l') #endif /* Parse any options */ argc--; arg = *++argv; if (argc < 1 || arg[0] != '-') { goto do_it_now; } /* The -l option, which prints out signal names. * Intended usage in shell: * echo "Died of SIG`kill -l $?`" * We try to mimic what kill from coreutils-6.8 does */ if (arg[1] == 'l' && arg[2] == '\0') { if (argc == 1) { /* Print the whole signal list */ print_signames(); return 0; } /* -l <sig list> */ while ((arg = *++argv)) { if (isdigit(arg[0])) { signo = bb_strtou(arg, NULL, 10); if (errno) { bb_error_msg("unknown signal '%s'", arg); return EXIT_FAILURE; } /* Exitcodes >= 0x80 are to be treated * as "killed by signal (exitcode & 0x7f)" */ puts(get_signame(signo & 0x7f)); /* TODO: 'bad' signal# - coreutils says: * kill: 127: invalid signal * we just print "127" instead */ } else { signo = get_signum(arg); if (signo < 0) { bb_error_msg("unknown signal '%s'", arg); return EXIT_FAILURE; } printf("%d\n", signo); } } /* If they specified -l, we are all done */ return EXIT_SUCCESS; } /* The -q quiet option */ if (killall && arg[1] == 'q' && arg[2] == '\0') { quiet = 1; arg = *++argv; argc--; if (argc < 1) bb_show_usage(); if (arg[0] != '-') goto do_it_now; } arg++; /* skip '-' */ /* -o PID? (if present, it always is at the end of command line) */ if (killall5 && arg[0] == 'o') goto do_it_now; if (argc > 1 && arg[0] == 's' && arg[1] == '\0') { /* -s SIG? */ argc--; arg = *++argv; } /* else it must be -SIG */ signo = get_signum(arg); if (signo < 0) { /* || signo > MAX_SIGNUM ? */ bb_error_msg("bad signal name '%s'", arg); return EXIT_FAILURE; } arg = *++argv; argc--; do_it_now: pid = getpid(); if (killall5) { pid_t sid; procps_status_t* p = NULL; int ret = 0; /* Find out our session id */ sid = getsid(pid); /* Stop all processes */ if (signo != SIGSTOP && signo != SIGCONT) kill(-1, SIGSTOP); /* Signal all processes except those in our session */ while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_SID)) != NULL) { int i; if (p->sid == (unsigned)sid || p->pid == (unsigned)pid || p->pid == 1 ) { continue; } /* All remaining args must be -o PID options. * Check p->pid against them. */ for (i = 0; i < argc; i++) { pid_t omit; arg = argv[i]; if (arg[0] != '-' || arg[1] != 'o') { bb_error_msg("bad option '%s'", arg); ret = 1; goto resume; } arg += 2; if (!arg[0] && argv[++i]) arg = argv[i]; omit = bb_strtoi(arg, NULL, 10); if (errno) { bb_error_msg("invalid number '%s'", arg); ret = 1; goto resume; } if (p->pid == omit) goto dont_kill; } kill(p->pid, signo); dont_kill: ; } resume: /* And let them continue */ if (signo != SIGSTOP && signo != SIGCONT) kill(-1, SIGCONT); return ret; } /* Pid or name is required for kill/killall */ if (argc < 1) { bb_error_msg("you need to specify whom to kill"); return EXIT_FAILURE; } if (killall) { /* Looks like they want to do a killall. Do that */ while (arg) { pid_t* pidList; pidList = find_pid_by_name(arg); if (*pidList == 0) { errors++; if (!quiet) bb_error_msg("%s: no process killed", arg); } else { pid_t *pl; for (pl = pidList; *pl; pl++) { if (*pl == pid) continue; if (kill(*pl, signo) == 0) continue; errors++; if (!quiet) bb_perror_msg("can't kill pid %d", (int)*pl); } } free(pidList); arg = *++argv; } return errors; } /* Looks like they want to do a kill. Do that */ while (arg) { #if ENABLE_ASH || ENABLE_HUSH /* * We need to support shell's "hack formats" of * " -PRGP_ID" (yes, with a leading space) * and " PID1 PID2 PID3" (with degenerate case "") */ while (*arg != '\0') { char *end; if (*arg == ' ') arg++; pid = bb_strtoi(arg, &end, 10); if (errno && (errno != EINVAL || *end != ' ')) { bb_error_msg("invalid number '%s'", arg); errors++; break; } if (kill(pid, signo) != 0) { bb_perror_msg("can't kill pid %d", (int)pid); errors++; } arg = end; /* can only point to ' ' or '\0' now */ } #else pid = bb_strtoi(arg, NULL, 10); if (errno) { bb_error_msg("invalid number '%s'", arg); errors++; } else if (kill(pid, signo) != 0) { bb_perror_msg("can't kill pid %d", (int)pid); errors++; } #endif arg = *++argv; } return errors; }