static int addgroup_wrapper(struct passwd *p, const char *group_name) { char *argv[6]; argv[0] = (char*)"addgroup"; if (group_name) { /* Add user to existing group */ argv[1] = (char*)"--"; argv[2] = p->pw_name; argv[3] = (char*)group_name; argv[4] = NULL; } else { /* Add user to his own group with the first free gid * found in passwd_study. */ #if ENABLE_FEATURE_ADDGROUP_LONG_OPTIONS || !ENABLE_ADDGROUP /* We try to use --gid, not -g, because "standard" addgroup * has no short option -g, it has only long --gid. */ argv[1] = (char*)"--gid"; #else /* Breaks if system in fact does NOT use busybox addgroup */ argv[1] = (char*)"-g"; #endif argv[2] = utoa(p->pw_gid); argv[3] = (char*)"--"; argv[4] = p->pw_name; argv[5] = NULL; } return spawn_and_wait(argv); }
/* This function has special algorithm. Don't use fork and include to main! */ static int xargs_exec(char **args) { int status; status = spawn_and_wait(args); if (status < 0) { bb_perror_msg("%s", args[0]); return errno == ENOENT ? 127 : 126; } if (status == 255) { bb_error_msg("%s: exited with status 255; aborting", args[0]); return 124; } /* Huh? I think we won't see this, ever. We don't wait with WUNTRACED! if (WIFSTOPPED(status)) { bb_error_msg("%s: stopped by signal %d", args[0], WSTOPSIG(status)); return 125; } */ if (status >= 1000) { bb_error_msg("%s: terminated by signal %d", args[0], status - 1000); return 125; } if (status) return 123; return 0; }
/** * Run a script. * argv[0]:intf argv[1]:script_name argv[2]:junk argv[3]:NULL */ static int run(char *argv[3], const char *param, struct in_addr *ip) { int status; char *addr = addr; /* for gcc */ const char *fmt = "%s %s %s" + 3; argv[2] = (char*)param; VDBG("%s run %s %s\n", argv[0], argv[1], argv[2]); if (ip) { addr = inet_ntoa(*ip); xsetenv("ip", addr); fmt -= 3; } if (verbose) bb_info_msg(fmt, argv[2], argv[0], addr); status = spawn_and_wait(argv + 1); if (status < 0) { bb_perror_msg("%s %s %s" + 3, argv[2], argv[0]); return -errno; } if (status != 0) bb_error_msg("script %s %s failed, exitcode=%d", argv[1], argv[2], status & 0xff); return status; }
static int run_script(const char *action) { char *env_PREVIOUS, *env_CURRENT; char *argv[5]; int r; bb_error_msg("executing '%s %s %s'", G.script_name, G.iface, action); argv[0] = (char*) G.script_name; argv[1] = (char*) G.iface; argv[2] = (char*) action; argv[3] = (char*) G.extra_arg; argv[4] = NULL; env_PREVIOUS = xasprintf("%s=%s", IFPLUGD_ENV_PREVIOUS, strstatus(G.iface_prev_status)); putenv(env_PREVIOUS); env_CURRENT = xasprintf("%s=%s", IFPLUGD_ENV_CURRENT, strstatus(G.iface_last_status)); putenv(env_CURRENT); /* r < 0 - can't exec, 0 <= r < 0x180 - exited, >=0x180 - killed by sig (r-0x180) */ r = spawn_and_wait(argv); unsetenv(IFPLUGD_ENV_PREVIOUS); unsetenv(IFPLUGD_ENV_CURRENT); free(env_PREVIOUS); free(env_CURRENT); bb_error_msg("exit code: %d", r & 0xff); return (option_mask32 & FLAG_IGNORE_RETVAL) ? 0 : r; }
int main(int argc, char **argv) { int naptime = 0; assert(argc > 2); naptime = atoi(argv[1]); /* assert(naptime > 0 && naptime < 1800); */ alarm(naptime); return spawn_and_wait(argc+2, argv+2); }
void FAST_FUNC write_leases(void) { int fd; unsigned i; leasetime_t curr; int64_t written_at; fd = open_or_warn(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC); if (fd < 0) return; curr = written_at = time(NULL); written_at = SWAP_BE64(written_at); full_write(fd, &written_at, sizeof(written_at)); for (i = 0; i < server_config.max_leases; i++) { leasetime_t tmp_time; if (g_leases[i].lease_nip == 0) continue; /* Screw with the time in the struct, for easier writing */ tmp_time = g_leases[i].expires; g_leases[i].expires -= curr; if ((signed_leasetime_t) g_leases[i].expires < 0) g_leases[i].expires = 0; g_leases[i].expires = htonl(g_leases[i].expires); /* No error check. If the file gets truncated, * we lose some leases on restart. Oh well. */ full_write(fd, &g_leases[i], sizeof(g_leases[i])); /* Then restore it when done */ g_leases[i].expires = tmp_time; } close(fd); if (server_config.notify_file) { char *argv[3]; argv[0] = server_config.notify_file; argv[1] = server_config.lease_file; argv[2] = NULL; spawn_and_wait(argv); } }
static void run_login_script(struct passwd *pw, char *full_tty) { char *t_argv[2]; t_argv[0] = getenv("LOGIN_PRE_SUID_SCRIPT"); if (t_argv[0]) { t_argv[1] = NULL; xsetenv("LOGIN_TTY", full_tty); xsetenv("LOGIN_USER", pw->pw_name); xsetenv("LOGIN_UID", utoa(pw->pw_uid)); xsetenv("LOGIN_GID", utoa(pw->pw_gid)); xsetenv("LOGIN_SHELL", pw->pw_shell); spawn_and_wait(t_argv); /* NOMMU-friendly */ unsetenv("LOGIN_TTY"); unsetenv("LOGIN_USER"); unsetenv("LOGIN_UID"); unsetenv("LOGIN_GID"); unsetenv("LOGIN_SHELL"); } }
static int xargs_exec(void) { int status; status = spawn_and_wait(G.args); if (status < 0) { bb_simple_perror_msg(G.args[0]); return errno == ENOENT ? 127 : 126; } if (status == 255) { bb_error_msg("%s: exited with status 255; aborting", G.args[0]); return 124; } if (status >= 0x180) { bb_error_msg("'%s' terminated by signal %d", G.args[0], status - 0x180); return 125; } if (status) return 123; return 0; }
int ginit_main(int argc UNUSED_PARAM, char **argv) { FILE *mntlist; bool ismnted_dev, ismnted_sys, ismnted_usr; struct mntent *mntent; /* int fd = open("/dev/console", O_RDWR); if (fd >= 0) { dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); } */ /* If given an argv[] with an applet name, run it instead. * Makes recovering simple by doing: init=/ginit bb */ if (argv[1] && argv[1][0] != '/') { eprintf("running user requested applet %s\n", argv[1]); return spawn_and_wait(argv+1); } #define saw(argv...) \ ({ \ static const char *args[] = { argv, NULL }; \ /* These casts are fine -- see process_args for mem setup */ \ process_args((void *)args); \ spawn_and_wait((void *)args); \ }) /* First setup basic /dev */ if (saw("mountpoint", "-q", "/dev") != 0) { /* Try /etc/fstab */ if (saw("mount", "-n", "/dev")) /* Then devtmpfs */ if (saw("mount", "-n", "-t", "devtmpfs", "devtmpfs", "/dev")) /* Finally normal tmpfs */ saw("mount", "-n", "-t", "tmpfs", "dev", "/dev"); } else { eprintf("%s appears to be mounted; skipping its setup\n", "/dev"); } /* If /dev is empty (e.g. tmpfs), run mdev to seed things */ if (access("/dev/console", F_OK) != 0) { if (saw("mountpoint", "-q", "/sys") != 0) { if (saw("mount", "-n", "/sys")) saw("mount", "-n", "-t", "sysfs", "sysfs", "/sys"); } else { eprintf("%s appears to be mounted; skipping its setup\n", "/sys"); } /* Mount /proc as mdev will fork+exec /proc/self/exe */ if (saw("mountpoint", "-q", "/proc") != 0) { /* Try /etc/fstab */ if (saw("mount", "-n", "/proc")) saw("mount", "-n", "-t", "proc", "proc", "/proc"); } saw("mdev", "-s"); } /* Then seed the stuff we care about */ saw("mkdir", "-p", "/dev/pts", "/dev/shm"); /* Then mount /usr */ if (saw("mountpoint", "-q", "/usr") != 0) { saw("mount", "-n", "/usr", "-o", "ro"); } else { eprintf("%s appears to be mounted; skipping its setup\n", "/usr"); } /* Now that we're all done, exec the real init */ if (!argv[1]) { argv[0] = (void *)"/sbin/init"; argv[1] = NULL; } else ++argv; process_args(argv); return execv(argv[0], argv); }
int inotifyd_main(int argc, char **argv) { int n; unsigned mask; struct pollfd pfd; char **watches; // names of files being watched const char *args[5]; // sanity check: agent and at least one watch must be given if (!argv[1] || !argv[2]) bb_show_usage(); argv++; // inotify_add_watch will number watched files // starting from 1, thus watches[0] is unimportant, // and 1st file name is watches[1]. watches = argv; args[0] = *argv; args[4] = NULL; argc -= 2; // number of files we watch // open inotify pfd.fd = inotify_init(); if (pfd.fd < 0) bb_perror_msg_and_die("no kernel support"); // setup watches while (*++argv) { char *path = *argv; char *masks = strchr(path, ':'); mask = 0x0fff; // assuming we want all non-kernel events // if mask is specified -> if (masks) { *masks = '\0'; // split path and mask // convert mask names to mask bitset mask = 0; while (*++masks) { const char *found; found = memchr(mask_names, *masks, MASK_BITS); if (found) mask |= (1 << (found - mask_names)); } } // add watch n = inotify_add_watch(pfd.fd, path, mask); if (n < 0) bb_perror_msg_and_die("add watch (%s) failed", path); //bb_error_msg("added %d [%s]:%4X", n, path, mask); } // setup signals bb_signals(BB_FATAL_SIGS, record_signo); // do watch pfd.events = POLLIN; while (1) { int len; void *buf; struct inotify_event *ie; again: if (bb_got_signal) break; n = poll(&pfd, 1, -1); // Signal interrupted us? if (n < 0 && errno == EINTR) goto again; // Under Linux, above if() is not necessary. // Non-fatal signals, e.g. SIGCHLD, when set to SIG_DFL, // are not interrupting poll(). // Thus we can just break if n <= 0 (see below), // because EINTR will happen only on SIGTERM et al. // But this might be not true under other Unixes, // and is generally way too subtle to depend on. if (n <= 0) // strange error? break; // read out all pending events // (NB: len must be int, not ssize_t or long!) #define eventbuf bb_common_bufsiz1 setup_common_bufsiz(); xioctl(pfd.fd, FIONREAD, &len); ie = buf = (len <= COMMON_BUFSIZE) ? eventbuf : xmalloc(len); len = full_read(pfd.fd, buf, len); // process events. N.B. events may vary in length while (len > 0) { int i; // cache relevant events mask unsigned m = ie->mask & ((1 << MASK_BITS) - 1); if (m) { char events[MASK_BITS + 1]; char *s = events; for (i = 0; i < MASK_BITS; ++i, m >>= 1) { if ((m & 1) && (mask_names[i] != '\0')) *s++ = mask_names[i]; } *s = '\0'; if (LONE_CHAR(args[0], '-')) { /* "inotifyd - FILE": built-in echo */ printf(ie->len ? "%s\t%s\t%s\n" : "%s\t%s\n", events, watches[ie->wd], ie->name); fflush(stdout); } else { // bb_error_msg("exec %s %08X\t%s\t%s\t%s", args[0], // ie->mask, events, watches[ie->wd], ie->len ? ie->name : ""); args[1] = events; args[2] = watches[ie->wd]; args[3] = ie->len ? ie->name : NULL; spawn_and_wait((char **)args); } // we are done if all files got final x event if (ie->mask & 0x8000) { if (--argc <= 0) goto done; inotify_rm_watch(pfd.fd, ie->wd); } } // next event i = sizeof(struct inotify_event) + ie->len; len -= i; ie = (void*)((char*)ie + i); } if (eventbuf != buf) free(buf); } // while (1) done: return bb_got_signal; }
int uevent_main(int argc UNUSED_PARAM, char **argv) { struct sockaddr_nl sa; int fd; argv++; // Subscribe for UEVENT kernel messages sa.nl_family = AF_NETLINK; sa.nl_pad = 0; sa.nl_pid = getpid(); sa.nl_groups = 1 << 0; fd = xsocket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); xbind(fd, (struct sockaddr *) &sa, sizeof(sa)); close_on_exec_on(fd); // Without a sufficiently big RCVBUF, a ton of simultaneous events // can trigger ENOBUFS on read, which is unrecoverable. // Reproducer: // uevent mdev & // find /sys -name uevent -exec sh -c 'echo add >"{}"' ';' // // SO_RCVBUFFORCE (root only) can go above net.core.rmem_max sysctl setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &RCVBUF, sizeof(RCVBUF)); setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &RCVBUF, sizeof(RCVBUF)); if (0) { int z; socklen_t zl = sizeof(z); getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &z, &zl); bb_error_msg("SO_RCVBUF:%d", z); } for (;;) { char *netbuf; char *s, *end; ssize_t len; int idx; // In many cases, a system sits for *days* waiting // for a new uevent notification to come in. // We use a fresh mmap so that buffer is not allocated // until kernel actually starts filling it. netbuf = mmap(NULL, BUFFER_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, /* ignored: */ -1, 0); if (netbuf == MAP_FAILED) bb_perror_msg_and_die("mmap"); // Here we block, possibly for a very long time len = safe_read(fd, netbuf, BUFFER_SIZE - 1); if (len < 0) bb_perror_msg_and_die("read"); end = netbuf + len; *end = '\0'; // Each netlink message starts with "ACTION@/path" // (which we currently ignore), // followed by environment variables. if (!argv[0]) putchar('\n'); idx = 0; s = netbuf; while (s < end) { if (!argv[0]) puts(s); if (strchr(s, '=') && idx < MAX_ENV) env[idx++] = s; s += strlen(s) + 1; } env[idx] = NULL; idx = 0; while (env[idx]) putenv(env[idx++]); if (argv[0]) spawn_and_wait(argv); idx = 0; while (env[idx]) bb_unsetenv(env[idx++]); munmap(netbuf, BUFFER_SIZE); } return 0; // not reached }
int install_main(int argc, char **argv) { struct stat statbuf; mode_t mode; uid_t uid; gid_t gid; char *arg, *last; const char *gid_str; const char *uid_str; const char *mode_str; int copy_flags = FILEUTILS_DEREFERENCE | FILEUTILS_FORCE; int opts; int min_args = 1; int ret = EXIT_SUCCESS; int isdir = 0; #if ENABLE_SELINUX security_context_t scontext; bool use_default_selinux_context = 1; #endif enum { OPT_c = 1 << 0, OPT_v = 1 << 1, OPT_b = 1 << 2, OPT_MKDIR_LEADING = 1 << 3, OPT_DIRECTORY = 1 << 4, OPT_PRESERVE_TIME = 1 << 5, OPT_STRIP = 1 << 6, OPT_GROUP = 1 << 7, OPT_MODE = 1 << 8, OPT_OWNER = 1 << 9, #if ENABLE_SELINUX OPT_SET_SECURITY_CONTEXT = 1 << 10, OPT_PRESERVE_SECURITY_CONTEXT = 1 << 11, #endif }; #if ENABLE_FEATURE_INSTALL_LONG_OPTIONS applet_long_options = install_longopts; #endif opt_complementary = "s--d:d--s" IF_FEATURE_INSTALL_LONG_OPTIONS(IF_SELINUX(":Z--\xff:\xff--Z")); /* -c exists for backwards compatibility, it's needed */ /* -v is ignored ("print name of each created directory") */ /* -b is ignored ("make a backup of each existing destination file") */ opts = getopt32(argv, "cvb" "Ddpsg:m:o:" IF_SELINUX("Z:"), &gid_str, &mode_str, &uid_str IF_SELINUX(, &scontext)); argc -= optind; argv += optind; #if ENABLE_SELINUX if (opts & (OPT_PRESERVE_SECURITY_CONTEXT|OPT_SET_SECURITY_CONTEXT)) { selinux_or_die(); use_default_selinux_context = 0; if (opts & OPT_PRESERVE_SECURITY_CONTEXT) { copy_flags |= FILEUTILS_PRESERVE_SECURITY_CONTEXT; } if (opts & OPT_SET_SECURITY_CONTEXT) { setfscreatecon_or_die(scontext); copy_flags |= FILEUTILS_SET_SECURITY_CONTEXT; } } #endif /* preserve access and modification time, this is GNU behaviour, * BSD only preserves modification time */ if (opts & OPT_PRESERVE_TIME) { copy_flags |= FILEUTILS_PRESERVE_STATUS; } mode = 0755; /* GNU coreutils 6.10 compat */ if (opts & OPT_MODE) bb_parse_mode(mode_str, &mode); uid = (opts & OPT_OWNER) ? get_ug_id(uid_str, xuname2uid) : getuid(); gid = (opts & OPT_GROUP) ? get_ug_id(gid_str, xgroup2gid) : getgid(); last = argv[argc - 1]; if (!(opts & OPT_DIRECTORY)) { argv[argc - 1] = NULL; min_args++; /* coreutils install resolves link in this case, don't use lstat */ isdir = stat(last, &statbuf) < 0 ? 0 : S_ISDIR(statbuf.st_mode); } if (argc < min_args) bb_show_usage(); while ((arg = *argv++) != NULL) { char *dest = last; if (opts & OPT_DIRECTORY) { dest = arg; /* GNU coreutils 6.9 does not set uid:gid * on intermediate created directories * (only on last one) */ if (bb_make_directory(dest, 0755, FILEUTILS_RECUR)) { ret = EXIT_FAILURE; goto next; } } else { if (opts & OPT_MKDIR_LEADING) { char *ddir = xstrdup(dest); bb_make_directory(dirname(ddir), 0755, FILEUTILS_RECUR); /* errors are not checked. copy_file * will fail if dir is not created. */ free(ddir); } if (isdir) dest = concat_path_file(last, bb_basename(arg)); if (copy_file(arg, dest, copy_flags) != 0) { /* copy is not made */ ret = EXIT_FAILURE; goto next; } if (opts & OPT_STRIP) { char *args[4]; args[0] = (char*)"strip"; args[1] = (char*)"-p"; /* -p --preserve-dates */ args[2] = dest; args[3] = NULL; if (spawn_and_wait(args)) { bb_perror_msg("strip"); ret = EXIT_FAILURE; } } } /* Set the file mode (always, not only with -m). * GNU coreutils 6.10 is not affected by umask. */ if (chmod(dest, mode) == -1) { bb_perror_msg("can't change %s of %s", "permissions", dest); ret = EXIT_FAILURE; } #if ENABLE_SELINUX if (use_default_selinux_context) setdefaultfilecon(dest); #endif /* Set the user and group id */ if ((opts & (OPT_OWNER|OPT_GROUP)) && lchown(dest, uid, gid) == -1 ) { bb_perror_msg("can't change %s of %s", "ownership", dest); ret = EXIT_FAILURE; } next: if (ENABLE_FEATURE_CLEAN_UP && isdir) free(dest); } return ret; }
int flock_main(int argc UNUSED_PARAM, char **argv) { int mode, opt, fd; enum { OPT_s = (1 << 0), OPT_x = (1 << 1), OPT_n = (1 << 2), OPT_u = (1 << 3), OPT_c = (1 << 4), }; #if ENABLE_LONG_OPTS static const char getopt_longopts[] ALIGN1 = "shared\0" No_argument "s" "exclusive\0" No_argument "x" "unlock\0" No_argument "u" "nonblock\0" No_argument "n" ; applet_long_options = getopt_longopts; #endif opt_complementary = "-1"; opt = getopt32(argv, "+sxnu"); argv += optind; if (argv[1]) { fd = open(argv[0], O_RDONLY|O_NOCTTY|O_CREAT, 0666); if (fd < 0 && errno == EISDIR) fd = open(argv[0], O_RDONLY|O_NOCTTY); if (fd < 0) bb_perror_msg_and_die("can't open '%s'", argv[0]); //TODO? close_on_exec_on(fd); } else { fd = xatoi_positive(argv[0]); } argv++; /* If it is "flock FILE -c PROG", then -c isn't caught by getopt32: * we use "+" in order to support "flock -opt FILE PROG -with-opts", * we need to remove -c by hand. */ if (argv[0] && argv[0][0] == '-' && ( (argv[0][1] == 'c' && !argv[0][2]) || (ENABLE_LONG_OPTS && strcmp(argv[0] + 1, "-command") == 0) ) ) { argv++; if (argv[1]) bb_error_msg_and_die("-c takes only one argument"); opt |= OPT_c; } if (OPT_s == LOCK_SH && OPT_x == LOCK_EX && OPT_n == LOCK_NB && OPT_u == LOCK_UN) { /* With suitably matched constants, mode setting is much simpler */ mode = opt & (LOCK_SH + LOCK_EX + LOCK_NB + LOCK_UN); if (!(mode & ~LOCK_NB)) mode |= LOCK_EX; } else { if (opt & OPT_u) mode = LOCK_UN; else if (opt & OPT_s) mode = LOCK_SH; else mode = LOCK_EX; if (opt & OPT_n) mode |= LOCK_NB; } if (flock(fd, mode) != 0) { if (errno == EWOULDBLOCK) return EXIT_FAILURE; bb_perror_nomsg_and_die(); } if (argv[0]) { int rc; if (opt & OPT_c) { /* -c 'PROG ARGS' means "run sh -c 'PROG ARGS'" */ argv -= 2; argv[0] = (char*)get_shell_name(); argv[1] = (char*)"-c"; /* argv[2] = "PROG ARGS"; */ /* argv[3] = NULL; */ } rc = spawn_and_wait(argv); if (rc < 0) bb_simple_perror_msg(argv[0]); return rc; } return EXIT_SUCCESS; }