int spawn_and_wait(char **argv) { int rc; #if ENABLE_FEATURE_PREFER_APPLETS const struct bb_applet *a = find_applet_by_name(argv[0]); if (a && (a->nofork #if BB_MMU || a->noexec /* NOEXEC trick needs fork() */ #endif )) { #if BB_MMU if (a->nofork) #endif { return run_nofork_applet(a, argv); } #if BB_MMU /* MMU only */ /* a->noexec is true */ rc = fork(); if (rc) /* parent or error */ return wait4pid(rc); /* child */ xfunc_error_retval = EXIT_FAILURE; current_applet = a; run_current_applet_and_exit(argv); #endif } #endif /* FEATURE_PREFER_APPLETS */ rc = spawn(argv); return wait4pid(rc); }
int FAST_FUNC spawn_and_wait(char **argv) { int rc; #if ENABLE_FEATURE_PREFER_APPLETS int a = find_applet_by_name(argv[0]); if (a >= 0 && (APPLET_IS_NOFORK(a) # if BB_MMU || APPLET_IS_NOEXEC(a) /* NOEXEC trick needs fork() */ # endif )) { # if BB_MMU if (APPLET_IS_NOFORK(a)) # endif { return run_nofork_applet(a, argv); } # if BB_MMU /* MMU only */ /* a->noexec is true */ rc = fork(); if (rc) /* parent or error */ return wait4pid(rc); /* child */ xfunc_error_retval = EXIT_FAILURE; run_applet_no_and_exit(a, argv); # endif } #endif /* FEATURE_PREFER_APPLETS */ rc = spawn(argv); return wait4pid(rc); }
static void edit_file(const struct passwd *pas, const char *file) { const char *ptr; pid_t pid; pid = xvfork(); if (pid) { /* parent */ wait4pid(pid); return; } /* CHILD - change user and run editor */ /* initgroups, setgid, setuid */ change_identity(pas); setup_environment(pas->pw_shell, SETUP_ENV_CHANGEENV | SETUP_ENV_TO_TMP, pas); ptr = getenv("VISUAL"); if (!ptr) { ptr = getenv("EDITOR"); if (!ptr) ptr = "vi"; } BB_EXECLP(ptr, ptr, file, NULL); bb_perror_msg_and_die("can't execute '%s'", ptr); }
/** * 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; } bb_info_msg(fmt, argv[2], argv[0], addr); status = wait4pid(spawn(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); return status; }
static int open_as_user(const struct passwd *pas, const char *file) { pid_t pid; char c; pid = xvfork(); if (pid) { /* PARENT */ if (wait4pid(pid) == 0) { /* exitcode 0: child says it can read */ return open(file, O_RDONLY); } return -1; } /* CHILD */ /* initgroups, setgid, setuid */ change_identity(pas); /* We just try to read one byte. If it works, file is readable * under this user. We signal that by exiting with 0. */ _exit(safe_read(xopen(file, O_RDONLY), &c, 1) < 0); }
static void edit_file(const struct passwd *pas, const char *file) { const char *ptr; int pid = vfork(); if (pid < 0) /* failure */ bb_perror_msg_and_die("vfork"); if (pid) { /* parent */ wait4pid(pid); return; } /* CHILD - change user and run editor */ change_user(pas); ptr = getenv("VISUAL"); if (!ptr) { ptr = getenv("EDITOR"); if (!ptr) ptr = "vi"; } BB_EXECLP(ptr, ptr, file, NULL); bb_perror_msg_and_die("exec %s", ptr); }
/* Call a script with a par file and env vars */ void FAST_FUNC udhcp_run_script(struct dhcp_packet *packet, const char *name) { char **envp, **curr; char *argv[3]; if (client_config.script == NULL) return; envp = fill_envp(packet); /* call script */ log1("Executing %s", client_config.script); argv[0] = (char*) client_config.script; argv[1] = (char*) name; argv[2] = NULL; wait4pid(spawn(argv)); for (curr = envp; *curr; curr++) { log2(" %s", *curr); bb_unsetenv(*curr); free(*curr); } free(envp); }
static int parse(const char *boundary, char **argv) { char *line, *s, *p; const char *type; int boundary_len = strlen(boundary); const char *delims = " ;\"\t\r\n"; const char *uniq; int ntokens; const char *tokens[32]; // 32 is enough // prepare unique string pattern uniq = xasprintf("%%llu.%u.%s", (unsigned)getpid(), safe_gethostname()); //bb_info_msg("PARSE[%s]", terminator); while ((line = xmalloc_fgets_str(stdin, "\r\n\r\n")) != NULL) { // seek to start of MIME section // N.B. to avoid false positives let us seek to the _last_ occurance p = NULL; s = line; while ((s=strcasestr(s, "Content-Type:")) != NULL) p = s++; if (!p) goto next; //bb_info_msg("L[%s]", p); // split to tokens // TODO: strip of comments which are of form: (comment-text) ntokens = 0; tokens[ntokens] = NULL; for (s = strtok(p, delims); s; s = strtok(NULL, delims)) { tokens[ntokens] = s; if (ntokens < ARRAY_SIZE(tokens) - 1) ntokens++; //bb_info_msg("L[%d][%s]", ntokens, s); } tokens[ntokens] = NULL; //bb_info_msg("N[%d]", ntokens); // analyse tokens type = find_token(tokens, "Content-Type:", "text/plain"); //bb_info_msg("T[%s]", type); if (0 == strncasecmp(type, "multipart/", 10)) { if (0 == strcasecmp(type+10, "mixed")) { parse(xfind_token(tokens, "boundary="), argv); } else bb_error_msg_and_die("no support of content type '%s'", type); } else { pid_t pid = pid; int rc; FILE *fp; // fetch charset const char *charset = find_token(tokens, "charset=", CONFIG_FEATURE_MIME_CHARSET); // fetch encoding const char *encoding = find_token(tokens, "Content-Transfer-Encoding:", "7bit"); // compose target filename char *filename = (char *)find_token(tokens, "filename=", NULL); if (!filename) filename = xasprintf(uniq, monotonic_us()); else filename = bb_get_last_path_component_strip(xstrdup(filename)); // start external helper, if any if (opts & OPT_X) { int fd[2]; xpipe(fd); pid = vfork(); if (0 == pid) { // child reads from fd[0] xdup2(fd[0], STDIN_FILENO); close(fd[0]); close(fd[1]); xsetenv("CONTENT_TYPE", type); xsetenv("CHARSET", charset); xsetenv("ENCODING", encoding); xsetenv("FILENAME", filename); BB_EXECVP(*argv, argv); _exit(EXIT_FAILURE); } // parent dumps to fd[1] close(fd[0]); fp = fdopen(fd[1], "w"); signal(SIGPIPE, SIG_IGN); // ignore EPIPE // or create a file for dump } else { char *fname = xasprintf("%s%s", *argv, filename); fp = xfopen_for_write(fname); free(fname); } // housekeeping free(filename); // dump to fp if (0 == strcasecmp(encoding, "base64")) { decode_base64(stdin, fp); } else if (0 != strcasecmp(encoding, "7bit") && 0 != strcasecmp(encoding, "8bit")) { // quoted-printable, binary, user-defined are unsupported so far bb_error_msg_and_die("no support of encoding '%s'", encoding); } else { // N.B. we have written redundant \n. so truncate the file // The following weird 2-tacts reading technique is due to // we have to not write extra \n at the end of the file // In case of -x option we could truncate the resulting file as // fseek(fp, -1, SEEK_END); // if (ftruncate(fileno(fp), ftell(fp))) // bb_perror_msg("ftruncate"); // But in case of -X we have to be much more careful. There is // no means to truncate what we already have sent to the helper. p = xmalloc_fgets_str(stdin, "\r\n"); while (p) { if ((s = xmalloc_fgets_str(stdin, "\r\n")) == NULL) break; if ('-' == s[0] && '-' == s[1] && 0 == strncmp(s+2, boundary, boundary_len)) break; fputs(p, fp); p = s; } /* while ((s = xmalloc_fgetline_str(stdin, "\r\n")) != NULL) { if ('-' == s[0] && '-' == s[1] && 0 == strncmp(s+2, boundary, boundary_len)) break; fprintf(fp, "%s\n", s); } // N.B. we have written redundant \n. so truncate the file fseek(fp, -1, SEEK_END); if (ftruncate(fileno(fp), ftell(fp))) bb_perror_msg("ftruncate"); */ } fclose(fp); // finalize helper if (opts & OPT_X) { signal(SIGPIPE, SIG_DFL); // exit if helper exited >0 rc = wait4pid(pid); if (rc) return rc+20; } // check multipart finalized if (s && '-' == s[2+boundary_len] && '-' == s[2+boundary_len+1]) { free(line); break; } } next: free(line); } //bb_info_msg("ENDPARSE[%s]", boundary); return EXIT_SUCCESS; }
int do_aspawn(SV* really, SV **mark, SV **sp) { char **a, *tmps; struct inheritance inherit; pid_t pid; int status, fd, nFd, fdMap[3]; SV *sv, **p_sv; STRLEN n_a; status = FAIL; if (sp > mark) { Newx(PL_Argv, sp - mark + 1, char*); a = PL_Argv; while (++mark <= sp) { if (*mark) *a++ = SvPVx(*mark, n_a); else *a++ = ""; } inherit.flags = SPAWN_SETGROUP; inherit.pgroup = SPAWN_NEWPGROUP; fdMap[STDIN_FILENO] = Perl_stdin_fd; fdMap[STDOUT_FILENO] = Perl_stdout_fd; fdMap[STDERR_FILENO] = STDERR_FILENO; nFd = 3; *a = NULL; /*-----------------------------------------------------*/ /* Will execvp() use PATH? */ /*-----------------------------------------------------*/ if (*PL_Argv[0] != '/') TAINT_ENV(); if (really && *(tmps = SvPV(really, n_a))) pid = spawnp(tmps, nFd, fdMap, &inherit, (const char **) PL_Argv, (const char **) environ); else pid = spawnp(PL_Argv[0], nFd, fdMap, &inherit, (const char **) PL_Argv, (const char **) environ); if (pid < 0) { status = FAIL; if (ckWARN(WARN_EXEC)) warner(WARN_EXEC,"Can't exec \"%s\": %s", PL_Argv[0], Strerror(errno)); } else { /*------------------------------------------------*/ /* If the file descriptors have been remapped then*/ /* we've been called following a my_popen request */ /* therefore we don't want to wait for spawnned */ /* program to complete. We need to set the fdpid */ /* value to the value of the spawnned process' pid*/ /*------------------------------------------------*/ fd = 0; if (Perl_stdin_fd != STDIN_FILENO) fd = Perl_stdin_fd; else if (Perl_stdout_fd != STDOUT_FILENO) fd = Perl_stdout_fd; if (fd != 0) { /*---------------------------------------------*/ /* Get the fd of the other end of the pipe, */ /* use this to reference the fdpid which will */ /* be used by my_pclose */ /*---------------------------------------------*/ close(fd); MUTEX_LOCK(&PL_fdpid_mutex); p_sv = av_fetch(PL_fdpid,fd,TRUE); fd = (int) SvIVX(*p_sv); SvREFCNT_dec(*p_sv); *p_sv = &PL_sv_undef; sv = *av_fetch(PL_fdpid,fd,TRUE); MUTEX_UNLOCK(&PL_fdpid_mutex); (void) SvUPGRADE(sv, SVt_IV); SvIVX(sv) = pid; status = 0; } else wait4pid(pid, &status, 0); } do_execfree(); }
static int mod_process ( struct mod_list_t *list, int do_insert ) { int rc = 0; char **argv = NULL; struct mod_opt_t *opts; int argc_malloc; /* never used when CONFIG_FEATURE_CLEAN_UP not defined */ int argc; while ( list ) { argc = 0; if( ENABLE_FEATURE_CLEAN_UP ) argc_malloc = 0; /* If CONFIG_FEATURE_CLEAN_UP is not defined, then we leak memory * each time we allocate memory for argv. * But it is (quite) small amounts of memory that leak each * time a module is loaded, and it is reclaimed when modprobe * exits anyway (even when standalone shell?). * This could become a problem when loading a module with LOTS of * dependencies, with LOTS of options for each dependencies, with * very little memory on the target... But in that case, the module * would not load because there is no more memory, so there's no * problem. */ /* enough for minimal insmod (5 args + NULL) or rmmod (3 args + NULL) */ argv = (char**) malloc( 6 * sizeof( char* ) ); if ( do_insert ) { if (already_loaded (list->m_name) != 1) { argv[argc++] = "insmod"; if (ENABLE_FEATURE_2_4_MODULES) { if (do_syslog) argv[argc++] = "-s"; if (autoclean) argv[argc++] = "-k"; if (quiet) argv[argc++] = "-q"; else if(verbose) /* verbose and quiet are mutually exclusive */ argv[argc++] = "-v"; } argv[argc++] = list-> m_path; if( ENABLE_FEATURE_CLEAN_UP ) argc_malloc = argc; opts = list-> m_options; while( opts ) { /* Add one more option */ argc++; argv = (char**) xrealloc( argv, ( argc + 1 ) * sizeof( char* ) ); argv[argc-1] = opts-> m_opt_val; opts = opts-> m_next; } } } else { /* modutils uses short name for removal */ if (already_loaded (list->m_name) != 0) { argv[argc++] = "rmmod"; if (do_syslog) argv[argc++] = "-s"; argv[argc++] = list->m_name; if( ENABLE_FEATURE_CLEAN_UP ) argc_malloc = argc; } } argv[argc] = NULL; if (argc) { if (verbose) { printf("%s module %s\n", do_insert?"Loading":"Unloading", list-> m_name ); } if (!show_only) { int rc2 = wait4pid(bb_spawn(argv)); if (do_insert) { rc = rc2; /* only last module matters */ } else if (!rc2) { rc = 0; /* success if remove any mod */ } } if( ENABLE_FEATURE_CLEAN_UP ) /* the last value in the array has index == argc, but * it is the terminating NULL, so we must not free it. */ while( argc_malloc < argc ) { free( argv[argc_malloc++] ); } } if( ENABLE_FEATURE_CLEAN_UP ) { free( argv ); argv = NULL; } list = do_insert ? list-> m_prev : list-> m_next; } return (show_only) ? 0 : rc; }
int inotifyd_main(int argc UNUSED_PARAM, char **argv) { int n; unsigned mask = IN_ALL_EVENTS; // assume we want all events struct pollfd pfd; char **watched = ++argv; // watched name list const char *args[] = { *argv, NULL, NULL, NULL, NULL }; // sanity check: agent and at least one watch must be given if (!argv[1]) bb_show_usage(); // open inotify pfd.fd = inotify_init(); if (pfd.fd < 0) bb_perror_msg_and_die("no kernel support"); // setup watched while (*++argv) { char *path = *argv; char *masks = strchr(path, ':'); // if mask is specified -> if (masks) { *masks = '\0'; // split path and mask // convert mask names to mask bitset mask = 0; while (*++masks) { int i = strchr(mask_names, *masks) - mask_names; if (i >= 0) { mask |= (1 << i); } } } // 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) { ssize_t 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 xioctl(pfd.fd, FIONREAD, &len); #define eventbuf bb_common_bufsiz1 ie = buf = (len <= sizeof(eventbuf)) ? eventbuf : xmalloc(len); len = full_read(pfd.fd, buf, len); // process events. N.B. events may vary in length while (len > 0) { int i; char events[sizeof(mask_names)]; char *s = events; unsigned m = ie->mask; for (i = 0; i < sizeof(mask_names)-1; ++i, m >>= 1) { if (m & 1) *s++ = mask_names[i]; } *s = '\0'; //bb_error_msg("exec %s %08X\t%s\t%s\t%s", agent, // ie->mask, events, watched[ie->wd], ie->len ? ie->name : ""); args[1] = events; args[2] = watched[ie->wd]; args[3] = ie->len ? ie->name : NULL; wait4pid(xspawn((char **)args)); // next event i = sizeof(struct inotify_event) + ie->len; len -= i; ie = (void*)((char*)ie + i); } if (eventbuf != buf) free(buf); } return EXIT_SUCCESS; }
int run_parts_main(int argc, char **argv) { const char *umask_p = "22"; llist_t *arg_list = NULL; unsigned n; int ret; #if ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS applet_long_options = runparts_longopts; #endif /* We require exactly one argument: the directory name */ opt_complementary = "=1:a::"; getopt32(argv, "a:u:t"USE_FEATURE_RUN_PARTS_FANCY("l"), &arg_list, &umask_p); umask(xstrtou_range(umask_p, 8, 0, 07777)); n = 1; while (arg_list && n < NUM_CMD) { cmd[n] = arg_list->data; arg_list = arg_list->link; n++; } /* cmd[n] = NULL; - is already zeroed out */ /* run-parts has to sort executables by name before running them */ recursive_action(argv[optind], ACTION_RECURSE|ACTION_FOLLOWLINKS, act, /* file action */ act, /* dir action */ NULL, /* user data */ 1 /* depth */ ); if (!names) return 0; qsort(names, cur, sizeof(char *), bb_alphasort); n = 0; while (1) { char *name = *names++; if (!name) break; if (option_mask32 & (RUN_PARTS_OPT_t | RUN_PARTS_OPT_l)) { puts(name); continue; } cmd[0] = name; ret = wait4pid(spawn(cmd)); if (ret == 0) continue; n = 1; if (ret < 0) bb_perror_msg("failed to exec %s", name); else /* ret > 0 */ bb_error_msg("%s exited with return code %d", name, ret); } return n; }
static int run_script(const char *action) { char *argv[5]; int r; bb_error_msg("executing '%s %s %s'", G.script_name, G.iface, action); #if 1 argv[0] = (char*) G.script_name; argv[1] = (char*) G.iface; argv[2] = (char*) action; argv[3] = (char*) G.extra_arg; argv[4] = NULL; /* r < 0 - can't exec, 0 <= r < 1000 - exited, >1000 - killed by sig (r-1000) */ r = wait4pid(spawn(argv)); bb_error_msg("exit code: %d", r); return (option_mask32 & FLAG_IGNORE_RETVAL) ? 0 : r; #else /* insanity */ struct fd_pair pipe_pair; char buf[256]; int i = 0; xpiped_pair(pipe_pair); pid = vfork(); if (pid < 0) { bb_perror_msg("fork"); return -1; } /* child */ if (pid == 0) { xmove_fd(pipe_pair.wr, 1); xdup2(1, 2); if (pipe_pair.rd > 2) close(pipe_pair.rd); // umask(0022); // Set up a sane umask execlp(G.script_name, G.script_name, G.iface, action, G.extra_arg, NULL); _exit(EXIT_FAILURE); } /* parent */ close(pipe_pair.wr); while (1) { if (bb_got_signal && bb_got_signal != SIGCHLD) { bb_error_msg("killing child"); kill(pid, SIGTERM); bb_got_signal = 0; break; } r = read(pipe_pair.rd, &buf[i], 1); if (buf[i] == '\n' || i == sizeof(buf)-2 || r != 1) { if (r == 1 && buf[i] != '\n') i++; buf[i] = '\0'; if (i > 0) bb_error_msg("client: %s", buf); i = 0; } else { i++; } if (r != 1) break; } close(pipe_pair.rd); wait(&r); if (!WIFEXITED(r) || WEXITSTATUS(r) != 0) { bb_error_msg("program execution failed, return value is %i", WEXITSTATUS(r)); return option_mask32 & FLAG_IGNORE_RETVAL ? 0 : WEXITSTATUS(r); } bb_error_msg("program executed successfully"); return 0; #endif }