pid_t VG_REPLACE_FUNCTION_ZU(libastZdsoZd1, spawnveg)(const char *command, char **argv, char **envp, pid_t pgid) { int err = 0; pid_t pid; posix_spawnattr_t attr; int attr_init_done = 0; err = posix_spawnattr_init(&attr); if (err != 0) goto out; attr_init_done = 1; err = posix_spawnattr_init(&attr); if (err != 0) goto out; if (pgid != 0) { if (pgid <= 1) pgid = 0; err = posix_spawnattr_setpgroup(&attr, pgid); if (err != 0) goto out; err = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETPGROUP); if (err != 0) goto out; } err = posix_spawn(&pid, command, NULL, &attr, argv, envp ? envp : environ); out: if (attr_init_done) posix_spawnattr_destroy(&attr); if (err != 0) { errno = err; return -1; } return pid; }
TInt CTestSendsignal::TestNegativeKill2 ( ) { int ret1 = KErrGeneral, pid, pid1, Signum, ret, status; char **argv=(char**)malloc(3*sizeof(char*)); ret = GetIntFromConfig(ConfigSection(), _L("Signum"), Signum); if(ret == 0) { ERR_PRINTF1(_L("Failed to read the signal number")) ; goto close; } argv[0]=(char*)malloc(34*sizeof(char)); argv[1]= 0; strcpy(argv[0], "z:\\sys\\bin\\receivesignal.exe"); ret = posix_spawn(&pid, "z:\\sys\\bin\\receivesignal.exe", NULL, NULL, argv, (char**)NULL); if(ret != 0) { ERR_PRINTF2(_L("Error in posix spawn and errno is set to %d"),errno); goto close; } INFO_PRINTF2( _L("the value of pid returned by posix_spawn is %d"), pid); ret = kill(pid,Signum); INFO_PRINTF1(_L("Negative test on Kill()")); if((ret != -1) || (errno != EINVAL)) { ERR_PRINTF1(_L("Failed to set the return value")); goto close; } pid1 = waitpid(pid, &status, WUNTRACED); if (pid1 != pid) { ERR_PRINTF1(_L("waitpid failed...")); goto close; } INFO_PRINTF1(_L("Both the return and expected value of kill() are same")); ret1 = KErrNone; close: free((void*)argv[0]); free((void*)argv); return ret1; }
GF_EXPORT GF_Err gf_move_file(const char *fileName, const char *newFileName) { #if defined(_WIN32_WCE) TCHAR swzName[MAX_PATH]; TCHAR swzNewName[MAX_PATH]; CE_CharToWide((char*)fileName, swzName); CE_CharToWide((char*)newFileName, swzNewName); return (MoveFile(swzName, swzNewName) == 0 ) ? GF_IO_ERR : GF_OK; #elif defined(WIN32) /* success if != 0 */ return (MoveFile(fileName, newFileName) == 0 ) ? GF_IO_ERR : GF_OK; #else GF_Err e = GF_IO_ERR; char cmd[1024], *arg1, *arg2; if (!fileName || !newFileName) return GF_IO_ERR; arg1 = gf_sanetize_single_quoted_string(fileName); arg2 = gf_sanetize_single_quoted_string(newFileName); if (snprintf(cmd, sizeof cmd, "mv %s %s", arg1, arg2) >= sizeof cmd) goto error; #if defined(GPAC_IPHONE) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 80000) { pid_t pid; char *argv[3]; argv[0] = "mv"; argv[1] = cmd; argv[2] = NULL; posix_spawn(&pid, argv[0], NULL, NULL, argv, environ); waitpid(pid, NULL, 0); } #else e = (system(cmd) == 0) ? GF_OK : GF_IO_ERR; #endif error: gf_free(arg1); gf_free(arg2); return e; #endif }
TInt CTestSendsignal::TestNegativeSigqueue1 ( ) { int ret1 = KErrGeneral, pid, pid1, ret, status; union sigval sival; char **argv=(char**)malloc(3*sizeof(char*)); argv[0]=(char*)malloc(34*sizeof(char)); argv[1]=(char*)malloc(3*sizeof(char)); argv[2]= 0; strcpy(argv[0], "z:\\sys\\bin\\receivesignal.exe"); sprintf(argv[1], "%d", 0); ret = posix_spawn(&pid, "z:\\sys\\bin\\receivesignal.exe", NULL, NULL, argv, (char**)NULL); if(ret != 0) { ERR_PRINTF2(_L("Error in posix spawn and errno is set to %d"),errno); goto close; } INFO_PRINTF2( _L("the value of pid returned by posix_spawn is %d"), pid); sival.sival_int = 0; ret = sigqueue(pid,0,sival); INFO_PRINTF1(_L("Negative test on sigqueue()")); if(ret != 0) { ERR_PRINTF1(_L("Failed to set the return value")); goto close; } pid1 = waitpid(pid, &status, WUNTRACED); if (pid1 != pid) { ERR_PRINTF1(_L("waitpid failed...")); goto close; } INFO_PRINTF1(_L("Kill() returned 0 as the process exists")); ret1 = KErrNone; close: free((void*)argv[0]); free((void*)argv[1]); free((void*)argv); return ret1; }
pid_t rb_spawn_process(const char *path, const char **argv) { pid_t pid; const void *arghack = argv; char **myenviron; posix_spawnattr_t spattr; posix_spawnattr_init(&spattr); #ifdef POSIX_SPAWN_USEVFORK posix_spawnattr_setflags(&spattr, POSIX_SPAWN_USEVFORK); #endif #ifdef __APPLE__ myenviron = *_NSGetEnviron(); /* apple needs to go f**k themselves for this */ #else myenviron = environ; #endif if(posix_spawn(&pid, path, NULL, &spattr, arghack, myenviron)) { return -1; } return pid; }
pid_t spawnveg(const char* path, char* const argv[], char* const envv[], pid_t pgid) { int err; pid_t pid; posix_spawnattr_t attr; if (err = posix_spawnattr_init(&attr)) goto nope; if (pgid) { if (pgid <= 1) pgid = 0; if (err = posix_spawnattr_setpgroup(&attr, pgid)) goto bad; if (err = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETPGROUP)) goto bad; } if (err = posix_spawn(&pid, path, NiL, &attr, argv, envv ? envv : environ)) goto bad; posix_spawnattr_destroy(&attr); #if _lib_posix_spawn < 2 if (waitpid(pid, &err, WNOHANG|WNOWAIT) == pid && EXIT_STATUS(err) == 127) { while (waitpid(pid, NiL, 0) == -1 && errno == EINTR); if (!access(path, X_OK)) errno = ENOEXEC; pid = -1; } #endif return pid; bad: posix_spawnattr_destroy(&attr); nope: errno = err; return -1; }
BatteryOutput TestRunner::executeBinary(Logger * logger, const std::string & objectInfo, const std::string & binaryPath, const std::string & arguments, const std::string & input) { int stdin_pipe[2]; int stdout_pipe[2]; int stderr_pipe[2]; pid_t pid = 0; posix_spawn_file_actions_t actions; if(pipe(stdin_pipe) || pipe(stdout_pipe) || pipe(stderr_pipe)) { logger->warn(objectInfo + ": pipe creation failed. Test won't be executed."); { /* Remove thread from list of threads without child, notify and end */ std::lock_guard<std::mutex> l (withoutChild_mux); --withoutChild; } withoutChild_cv.notify_one(); return {}; } /* Pipes will be mapped to I/O after process start */ /* Unused ends are closed */ posix_spawn_file_actions_init(&actions); /* Standard input */ posix_spawn_file_actions_addclose(&actions , stdin_pipe[1]); posix_spawn_file_actions_adddup2(&actions , stdin_pipe[0] , 0); posix_spawn_file_actions_addclose(&actions , stdin_pipe[0]); /* Standard output */ posix_spawn_file_actions_addclose(&actions , stdout_pipe[0]); posix_spawn_file_actions_adddup2(&actions , stdout_pipe[1] , 1); posix_spawn_file_actions_addclose(&actions , stdout_pipe[1]); /* Standard error output */ posix_spawn_file_actions_addclose(&actions , stderr_pipe[0]); posix_spawn_file_actions_adddup2(&actions , stderr_pipe[1] , 2); posix_spawn_file_actions_addclose(&actions , stderr_pipe[1]); int argc = 0; char ** args = buildArgv(arguments , &argc); /* Creating locks but not locking them yet */ std::unique_lock<std::mutex> finishedPid_lock(finishedPid_mux , std::defer_lock); std::unique_lock<std::mutex> withoutChild_lock(withoutChild_mux , std::defer_lock); std::unique_lock<std::mutex> waitingForChild_lock(waitingForChild_mux , std::defer_lock); std::unique_lock<std::mutex> childReceived_lock(childReceived_mux , std::defer_lock); std::unique_lock<std::mutex> threadsReady_lock(threadState_mux , std::defer_lock); /* Need both locks, if they cannot be acquired * main thread is waiting for process to end */ std::lock(finishedPid_lock , waitingForChild_lock); /* Starting child process of this thread */ logger->info(objectInfo + ": spawning child process with arguments " + arguments); int status = posix_spawn(&pid , binaryPath.c_str() , &actions , NULL , args , NULL); if(status == 0) { /* Process was started without problems, proceed */ logger->info(objectInfo + ": child process has pid " + Utils::itostr(pid)); /* Closing pipes */ close(stdin_pipe[0]); close(stdout_pipe[1]); close(stderr_pipe[1]); if(!input.empty()) write(stdin_pipe[1] , input.c_str() , input.length()); /* Start thread for output reading. Thread will be mostly in blocked wait. * Also will be essentially over as soon as the process finishes. * With this, pipes won't be filled and won't block underlying process. */ BatteryOutput output; std::thread reader(readOutput , std::ref(output), stdout_pipe , stderr_pipe); /* Incrementing number of threads waiting. * Only if this variable is same as withoutChild * main thread can start reaping child processes. */ ++waitingForChild; waitingForChild_lock.unlock(); waitingForChild_cv.notify_one(); /* Waiting for PID. PID is annouced by main thread and thread * will work with his associated process only * (because the thread owns the pipes to this process) */ auto now = std::chrono::system_clock::now(); /* Don't know why, but if I use TIMEOUT constant directly in argument it won't compile... */ int vArgumenteToJebeChybu = PROCESS_TIMEOUT_SECONDS; if(!finishedPid_cv.wait_until(finishedPid_lock , now + std::chrono::seconds(vArgumenteToJebeChybu) , [&] { return finishedPid == pid; })) { /* If thread goes into this branch, its process timeouted. * Send kill signal to it, main thread will then reap it. * That's why this go into wait once more (usually it will instantly return) */ logger->warn(objectInfo + ": child process with pid " + Utils::itostr(pid) + " timeouted." " Process will be killed now."); kill(pid , SIGKILL); finishedPid_cv.wait(finishedPid_lock , [&] { return finishedPid == pid; }); } /* Obtaining all possible mutexes, decrementing variables that * track waiting and active threads, setting childReceived to true * so main thread will be notified */ std::lock(withoutChild_lock , waitingForChild_lock , childReceived_lock); logger->info(objectInfo + ": child process with pid " + Utils::itostr(pid) + " finished"); childReceived = true; --waitingForChild; --withoutChild; /* Unlock mutexes, this thread no longer needs them */ finishedPid_lock.unlock(); waitingForChild_lock.unlock(); withoutChild_lock.unlock(); childReceived_lock.unlock(); /* Notify threadCreator that number of active threads changed. * He will spawn new threads if there is a need to. */ withoutChild_cv.notify_one(); /* This thread need to wait for notification from threadMaker * that it is ok to continue and after that, main thread can * be notified that child process was received and is being * processed.*/ threadsReady_lock.lock(); threadState_cv.wait(threadsReady_lock , []{ return (threadState == THREAD_STATE_DONE) || (threadState == THREAD_STATE_READY); }); if(threadState == THREAD_STATE_READY) { /* Flip threadState so next thread must wait too. */ threadState = THREAD_STATE_PENDING; } threadsReady_lock.unlock(); /* Notify main thread that it can continue in reaping processes */ childReceived_cv.notify_one(); /* This thread now completed all communication with other threads. * DO YOUR WORK SLAVE!!! */ reader.join(); return output; } else { /* Some nasty error happened at execution. Report and end thread */ logger->warn(objectInfo + ": can't execute child process. " "This never happened during development."); { /* Remove thread from list of threads without child, notify and end */ std::lock_guard<std::mutex> l (withoutChild_mux); --withoutChild; } withoutChild_cv.notify_one(); return {}; } /* All locks go out of scope, so everything will be unlocked here */ }
static int handle_connection() { char line[LINE_MAX]; /* Prepare per connection plugin env vars */ setenvvars_munin(); printf("# munin node at %s\n", host); while (fflush(stdout), fgets(line, LINE_MAX, stdin) != NULL) { char* cmd; char* arg; cmd = strtok(line, " \t\n\r"); if(cmd == NULL) arg = NULL; else arg = strtok(NULL, " \t\n\r"); if (!cmd || strlen(cmd) == 0) { printf("# empty cmd\n"); } else if (strcmp(cmd, "version") == 0) { printf("munin c node version: %s\n", VERSION); } else if (strcmp(cmd, "nodes") == 0) { printf("%s\n", host); printf(".\n"); } else if (strcmp(cmd, "quit") == 0) { return(0); } else if (strcmp(cmd, "list") == 0) { DIR* dirp = opendir(plugin_dir); if (dirp == NULL) { printf("# Cannot open plugin dir\n"); return(0); } { struct dirent* dp; while ((dp = readdir(dirp)) != NULL) { char cmdline[LINE_MAX]; char* plugin_filename = dp->d_name;; if (plugin_filename[0] == '.') { /* No dotted plugin */ continue; } snprintf(cmdline, LINE_MAX, "%s/%s", plugin_dir, plugin_filename); if (access(cmdline, X_OK) == 0) { if(extension_stripping) { /* Strip after the last . */ char *last_dot_idx = strrchr(plugin_filename, '.'); if (last_dot_idx != NULL) { *last_dot_idx = '\0'; } } printf("%s ", plugin_filename); } } closedir(dirp); } putchar('\n'); } else if ( strcmp(cmd, "config") == 0 || strcmp(cmd, "fetch") == 0 ) { char cmdline[LINE_MAX]; char *argv[2] = { 0, }; pid_t pid; if(arg == NULL) { printf("# no plugin given\n"); continue; } if(arg[0] == '.' || strchr(arg, '/') != NULL) { printf("# invalid plugin character\n"); continue; } if (! extension_stripping || find_plugin_with_basename(cmdline, plugin_dir, arg) == 0) { /* extension_stripping failed, using the plain method */ snprintf(cmdline, LINE_MAX, "%s/%s", plugin_dir, arg); } if (access(cmdline, X_OK) == -1) { printf("# unknown plugin: %s\n", arg); continue; } /* Now is the time to set environnement */ setenvvars_conf(arg); argv[0] = arg; /* Using posix_spawnp() here instead of fork() since we will * do a little more than a mere exec --> setenvvars_conf() */ if (0 == posix_spawn(&pid, cmdline, NULL, /* const posix_spawn_file_actions_t *file_actions, */ NULL, /* const posix_spawnattr_t *restrict attrp, */ argv, environ)) { /* Wait for completion */ waitpid(pid, NULL, 0); } else { printf("# fork failed\n"); continue; } printf(".\n"); } else if (strcmp(cmd, "cap") == 0) { printf("cap "); if ('\0' != *spoolfetch_dir) { printf("spool "); } printf("\n"); } else if (strcmp(cmd, "spoolfetch") == 0) { printf("# not implem yet cmd: %s\n", cmd); } else { printf("# Unknown cmd: %s. Try cap, list, nodes, config, fetch, version or quit\n", cmd); } } return 0; }
int /* O - Process ID or 0 */ cupsdStartProcess( const char *command, /* I - Full path to command */ char *argv[], /* I - Command-line arguments */ char *envp[], /* I - Environment */ int infd, /* I - Standard input file descriptor */ int outfd, /* I - Standard output file descriptor */ int errfd, /* I - Standard error file descriptor */ int backfd, /* I - Backchannel file descriptor */ int sidefd, /* I - Sidechannel file descriptor */ int root, /* I - Run as root? */ void *profile, /* I - Security profile to use */ cupsd_job_t *job, /* I - Job associated with process */ int *pid) /* O - Process ID */ { int i; /* Looping var */ const char *exec_path = command; /* Command to be exec'd */ char *real_argv[110], /* Real command-line arguments */ cups_exec[1024], /* Path to "cups-exec" program */ user_str[16], /* User string */ group_str[16], /* Group string */ nice_str[16]; /* FilterNice string */ uid_t user; /* Command UID */ cupsd_proc_t *proc; /* New process record */ #if USE_POSIX_SPAWN posix_spawn_file_actions_t actions; /* Spawn file actions */ posix_spawnattr_t attrs; /* Spawn attributes */ sigset_t defsignals; /* Default signals */ #elif defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) struct sigaction action; /* POSIX signal handler */ #endif /* USE_POSIX_SPAWN */ #if defined(__APPLE__) char processPath[1024], /* CFProcessPath environment variable */ linkpath[1024]; /* Link path for symlinks... */ int linkbytes; /* Bytes for link path */ #endif /* __APPLE__ */ *pid = 0; /* * Figure out the UID for the child process... */ if (RunUser) user = RunUser; else if (root) user = 0; else user = User; /* * Check the permissions of the command we are running... */ if (_cupsFileCheck(command, _CUPS_FILE_CHECK_PROGRAM, !RunUser, cupsdLogFCMessage, job ? job->printer : NULL)) return (0); #if defined(__APPLE__) if (envp) { /* * Add special voodoo magic for OS X - this allows OS X programs to access * their bundle resources properly... */ if ((linkbytes = readlink(command, linkpath, sizeof(linkpath) - 1)) > 0) { /* * Yes, this is a symlink to the actual program, nul-terminate and * use it... */ linkpath[linkbytes] = '\0'; if (linkpath[0] == '/') snprintf(processPath, sizeof(processPath), "CFProcessPath=%s", linkpath); else snprintf(processPath, sizeof(processPath), "CFProcessPath=%s/%s", dirname((char *)command), linkpath); } else snprintf(processPath, sizeof(processPath), "CFProcessPath=%s", command); envp[0] = processPath; /* Replace <CFProcessPath> string */ } #endif /* __APPLE__ */ /* * Use helper program when we have a sandbox profile... */ #if !USE_POSIX_SPAWN if (profile) #endif /* !USE_POSIX_SPAWN */ { snprintf(cups_exec, sizeof(cups_exec), "%s/daemon/cups-exec", ServerBin); snprintf(user_str, sizeof(user_str), "%d", user); snprintf(group_str, sizeof(group_str), "%d", Group); snprintf(nice_str, sizeof(nice_str), "%d", FilterNice); real_argv[0] = cups_exec; real_argv[1] = (char *)"-g"; real_argv[2] = group_str; real_argv[3] = (char *)"-n"; real_argv[4] = nice_str; real_argv[5] = (char *)"-u"; real_argv[6] = user_str; real_argv[7] = profile ? profile : "none"; real_argv[8] = (char *)command; for (i = 0; i < (int)(sizeof(real_argv) / sizeof(real_argv[0]) - 10) && argv[i]; i ++) real_argv[i + 9] = argv[i]; real_argv[i + 9] = NULL; argv = real_argv; exec_path = cups_exec; } if (LogLevel == CUPSD_LOG_DEBUG2) { cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Preparing to start \"%s\", arguments:", command); for (i = 0; argv[i]; i ++) cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: argv[%d] = \"%s\"", i, argv[i]); } #if USE_POSIX_SPAWN /* * Setup attributes and file actions for the spawn... */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Setting spawn attributes."); sigemptyset(&defsignals); sigaddset(&defsignals, SIGTERM); sigaddset(&defsignals, SIGCHLD); sigaddset(&defsignals, SIGPIPE); posix_spawnattr_init(&attrs); posix_spawnattr_setflags(&attrs, POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGDEF); posix_spawnattr_setpgroup(&attrs, 0); posix_spawnattr_setsigdefault(&attrs, &defsignals); cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Setting file actions."); posix_spawn_file_actions_init(&actions); if (infd != 0) { if (infd < 0) posix_spawn_file_actions_addopen(&actions, 0, "/dev/null", O_RDONLY, 0); else posix_spawn_file_actions_adddup2(&actions, infd, 0); } if (outfd != 1) { if (outfd < 0) posix_spawn_file_actions_addopen(&actions, 1, "/dev/null", O_WRONLY, 0); else posix_spawn_file_actions_adddup2(&actions, outfd, 1); } if (errfd != 2) { if (errfd < 0) posix_spawn_file_actions_addopen(&actions, 2, "/dev/null", O_WRONLY, 0); else posix_spawn_file_actions_adddup2(&actions, errfd, 2); } if (backfd != 3 && backfd >= 0) posix_spawn_file_actions_adddup2(&actions, backfd, 3); if (sidefd != 4 && sidefd >= 0) posix_spawn_file_actions_adddup2(&actions, sidefd, 4); cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Calling posix_spawn."); if (posix_spawn(pid, exec_path, &actions, &attrs, argv, envp ? envp : environ)) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork %s - %s.", command, strerror(errno)); *pid = 0; } else cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: pid=%d", (int)*pid); posix_spawn_file_actions_destroy(&actions); posix_spawnattr_destroy(&attrs); #else /* * Block signals before forking... */ cupsdHoldSignals(); if ((*pid = fork()) == 0) { /* * Child process goes here; update stderr as needed... */ if (errfd != 2) { if (errfd < 0) errfd = open("/dev/null", O_WRONLY); if (errfd != 2) { dup2(errfd, 2); close(errfd); } } /* * Put this process in its own process group so that we can kill any child * processes it creates. */ # ifdef HAVE_SETPGID if (!RunUser && setpgid(0, 0)) exit(errno + 100); # else if (!RunUser && setpgrp()) exit(errno + 100); # endif /* HAVE_SETPGID */ /* * Update the remaining file descriptors as needed... */ if (infd != 0) { if (infd < 0) infd = open("/dev/null", O_RDONLY); if (infd != 0) { dup2(infd, 0); close(infd); } } if (outfd != 1) { if (outfd < 0) outfd = open("/dev/null", O_WRONLY); if (outfd != 1) { dup2(outfd, 1); close(outfd); } } if (backfd != 3 && backfd >= 0) { dup2(backfd, 3); close(backfd); fcntl(3, F_SETFL, O_NDELAY); } if (sidefd != 4 && sidefd >= 0) { dup2(sidefd, 4); close(sidefd); fcntl(4, F_SETFL, O_NDELAY); } /* * Change the priority of the process based on the FilterNice setting. * (this is not done for root processes...) */ if (!root) nice(FilterNice); /* * Reset group membership to just the main one we belong to. */ if (!RunUser && setgid(Group)) exit(errno + 100); if (!RunUser && setgroups(1, &Group)) exit(errno + 100); /* * Change user to something "safe"... */ if (!RunUser && user && setuid(user)) exit(errno + 100); /* * Change umask to restrict permissions on created files... */ umask(077); /* * Unblock signals before doing the exec... */ # ifdef HAVE_SIGSET sigset(SIGTERM, SIG_DFL); sigset(SIGCHLD, SIG_DFL); sigset(SIGPIPE, SIG_DFL); # elif defined(HAVE_SIGACTION) memset(&action, 0, sizeof(action)); sigemptyset(&action.sa_mask); action.sa_handler = SIG_DFL; sigaction(SIGTERM, &action, NULL); sigaction(SIGCHLD, &action, NULL); sigaction(SIGPIPE, &action, NULL); # else signal(SIGTERM, SIG_DFL); signal(SIGCHLD, SIG_DFL); signal(SIGPIPE, SIG_DFL); # endif /* HAVE_SIGSET */ cupsdReleaseSignals(); /* * Execute the command; if for some reason this doesn't work, log an error * exit with a non-zero value... */ if (envp) execve(exec_path, argv, envp); else execv(exec_path, argv); exit(errno + 100); } else if (*pid < 0) { /* * Error - couldn't fork a new process! */ cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork %s - %s.", command, strerror(errno)); *pid = 0; } cupsdReleaseSignals(); #endif /* USE_POSIX_SPAWN */ if (*pid) { if (!process_array) process_array = cupsArrayNew((cups_array_func_t)compare_procs, NULL); if (process_array) { if ((proc = calloc(1, sizeof(cupsd_proc_t) + strlen(command))) != NULL) { proc->pid = *pid; proc->job_id = job ? job->id : 0; _cups_strcpy(proc->name, command); cupsArrayAdd(process_array, proc); } } } cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess(command=\"%s\", argv=%p, envp=%p, " "infd=%d, outfd=%d, errfd=%d, backfd=%d, sidefd=%d, root=%d, " "profile=%p, job=%p(%d), pid=%p) = %d", command, argv, envp, infd, outfd, errfd, backfd, sidefd, root, profile, job, job ? job->id : 0, pid, *pid); return (*pid); }
int main(int argc, char* argv[]) { int i; int nandReadOnly=0; struct stat st; printf("Starting ramdisk tool\n"); printf("Compiled " __DATE__ " " __TIME__ "\n"); printf("Revision " HGVERSION "\n"); CFMutableDictionaryRef matching; io_service_t service = 0; matching = IOServiceMatching("IOWatchDogTimer"); if (matching == NULL) { printf("unable to create matching dictionary for class IOWatchDogTimer\n"); } service = IOServiceGetMatchingService(kIOMasterPortDefault, matching); if (service == 0) { printf("unable to create matching dictionary for class IOWatchDogTimer\n"); } uint32_t zero = 0; CFNumberRef n = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero); IORegistryEntrySetCFProperties(service, n); IOObjectRelease(service); CFMutableDictionaryRef deviceInfos = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); get_device_infos(deviceInfos); init_tcp(); sysctlbyname("kern.bootargs", bootargs, &bootargs_len, NULL, 0); if (strstr(bootargs, "nand-readonly") || strstr(bootargs, "nand-disable")) { printf("NAND read only mode, data partition wont be mounted\n"); nandReadOnly = 1; } else { printf("Waiting for data partition\n"); for(i=0; i < 10; i++) { if(!stat("/dev/disk0s2s1", &st)) { system("/sbin/fsck_hfs /dev/disk0s2s1"); break; } if(!stat("/dev/disk0s1s2", &st)) { system("/sbin/fsck_hfs /dev/disk0s1s2"); break; } if(!stat("/dev/disk0s2", &st)) { system("/sbin/fsck_hfs /dev/disk0s2"); break; } sleep(5); } } init_usb(CFDictionaryGetValue(deviceInfos, CFSTR("udid"))); printf("USB init done\n"); system("mount /"); //make ramdisk writable chmod("/var/root/.ssh/authorized_keys", 0600); chown("/var/root/.ssh/authorized_keys", 0, 0); chown("/var/root/.ssh", 0, 0); chown("/var/root/", 0, 0); printf(" ####### ## ##\n"); printf("## ## ## ## \n"); printf("## ## ## ## \n"); printf("## ## ##### \n"); printf("## ## ## ## \n"); printf("## ## ## ## \n"); printf(" ####### ## ##\n"); printf("iphone-dataprotection ramdisk\n"); printf("revision: " HGVERSION " " __DATE__ " " __TIME__ "\n"); if(!stat(execve_params[0], &st)) { printf("Running %s\n", execve_params[0]); if((i = posix_spawn(NULL, execve_params[0], NULL, NULL, execve_params, execve_env))) printf("posix_spawn(%s) returned %d\n", execve_params[0], i); } else { printf("%s is missing\n", execve_params[0]); } /*if (nandReadOnly) {*/ if(!stat(ioflash[0], &st)) { printf("Running %s\n", ioflash[0]); if((i = posix_spawn(NULL, ioflash[0], NULL, NULL, ioflash, execve_env))) printf("posix_spawn(%s) returned %d\n", execve_params[0], i); } /*}*/ CFMutableDictionaryRef handlers = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL); CFDictionaryAddValue(handlers, CFSTR("DeviceInfo"), device_info); CFDictionaryAddValue(handlers, CFSTR("GetSystemKeyBag"), load_system_keybag); CFDictionaryAddValue(handlers, CFSTR("BruteforceSystemKeyBag"), bruteforce_system_keybag); CFDictionaryAddValue(handlers, CFSTR("KeyBagGetPasscodeKey"), keybag_get_passcode_key); CFDictionaryAddValue(handlers, CFSTR("GetEscrowRecord"), get_escrow_record); CFDictionaryAddValue(handlers, CFSTR("DownloadFile"), download_file); CFDictionaryAddValue(handlers, CFSTR("AES"), remote_aes); CFDictionaryAddValue(handlers, CFSTR("Reboot"), reboot__); serve_plist_rpc(1999, handlers); return 0; }
pid_t spawn (boost::asio::io_service& io_service, const child_options& opt, std::map<stream_id, boost::shared_ptr<stream_descriptor> >& streams, boost::system::error_code& ec) { typedef std::map<stream_id, child_options::stream_behaviour> beh_t; posix_spawn_file_actions_t file_actions; posix_spawn_file_actions_init(&file_actions); streams.clear(); std::vector<int> close_later; const beh_t& behaviours = opt.get_stream_behaviours(); for (beh_t::const_iterator iter = behaviours.begin(); iter != behaviours.end(); ++iter) { switch (iter->second) { case child_options::close: { posix_spawn_file_actions_addclose(&file_actions, iter->first.native()); break; } case child_options::inherit: { posix_spawn_file_actions_addinherit_np(&file_actions, iter->first.native()); break; } case child_options::pipe_read: { posix::pipe p; close_later.push_back(p.read_end()); posix_spawn_file_actions_adddup2(&file_actions, p.steal_read_end(), iter->first.native()); streams.insert( std::make_pair( iter->first, boost::shared_ptr<stream_descriptor>( new stream_descriptor(io_service, p.steal_write_end())) ) ); break; } case child_options::pipe_write: { posix::pipe p; close_later.push_back(p.write_end()); posix_spawn_file_actions_adddup2(&file_actions, p.steal_write_end(), iter->first.native()); streams.insert( std::make_pair( iter->first, boost::shared_ptr<stream_descriptor>( new stream_descriptor(io_service, p.steal_read_end())) ) ); break; } } } zero_terminated_list_of_strings argv; argv.add(opt.get_command()); argv.add(opt.get_args()); pid_t child_pid = -1; const char* _path = opt.get_command().c_str(); char* const* _argv = argv.data(); int result = posix_spawn(&child_pid, _path, &file_actions, NULL, _argv, NULL); posix_spawn_file_actions_destroy(&file_actions); std::for_each(close_later.begin(), close_later.end(), ::close); ec.assign(result, boost::system::generic_category()); return child_pid; }
int run_all ( char *args[] ) { pthread_t thr; int *status, rv; char *progname; #if 0 /* run all programs */ char info[INFO_SIZE]; char *sysinfo_args[] = { "sysinfo", "programs", NULL }; printf ( "\n*** Running all programs, one by one ***\n\n" ); syscall ( SYSINFO, &info, INFO_SIZE, sysinfo_args ); printf ( "%s\n", info ); progname = strstr ( info, ":\n" ); #else /* run selected programs */ char progs_to_start[] = { "hello timer args uthreads threads semaphores " "monitors messages signals rr edf" }; progname = progs_to_start; #endif /* progname has program names to be started */ progname = strtok ( progname, ":\n " ); while ( progname ) { printf ( "Starting program: %s\n" "---------------------------------------\n", progname); rv = posix_spawn ( &thr, progname, NULL, NULL, NULL, NULL ); if ( !rv ) { rv = pthread_join ( thr, (void **) &status ); if ( rv && get_errno () != ESRCH ) { printf ( "\npthread_join error!\n\n" ); break; } #if 0 /* most functions do not call pthread_exit directly so * the following test might be "false negative" */ else if ( status && *status ) { printf ( "\nProgram %s exited with error!\n\n", progname ); break; } #endif else { printf ("\nProgram %s exited successfully!\n\n", progname ); } } else { printf ( "\nProgram: %s not started!\n", progname ); break; } progname = strtok ( NULL, " " ); } #if ( TURN_OFF == 1 ) printf ( "Powering off\n\n" ); syscall ( POWER_OFF, NULL, 0, NULL ); #endif return 0; }
int main(int argc, char *argv[]) { posix_spawnattr_t attrs; kern_return_t kr; mach_port_t task = mach_task_self(); mach_msg_type_number_t maskCount = 1; exception_mask_t mask; exception_handler_t handler; exception_behavior_t behavior; thread_state_flavor_t flavor; pthread_t exception_thread; uint64_t exc_id; unsigned int exc_fd; char *test_prog_name = "./guarded_test"; char *child_args[MAX_ARGV]; char test_id[MAX_TEST_ID_LEN]; int i, err; int child_status; int test_status = 0; /* Allocate and initialize new exception port */ if ((kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &exc_port)) != KERN_SUCCESS) { fprintf(stderr, "mach_port_allocate: %#x\n", kr); exit(1); } if ((kr = mach_port_insert_right(task, exc_port, exc_port, MACH_MSG_TYPE_MAKE_SEND)) != KERN_SUCCESS) { fprintf(stderr, "mach_port_allocate: %#x\n", kr); exit(1); } /* Get Current exception ports */ if ((kr = task_get_exception_ports(task, EXC_MASK_GUARD, &mask, &maskCount, &handler, &behavior, &flavor)) != KERN_SUCCESS) { fprintf(stderr,"task_get_exception_ports: %#x\n", kr); exit(1); } /* Create exception serving thread */ if ((err = pthread_create(&exception_thread, NULL, server_thread, 0)) != 0) { fprintf(stderr, "pthread_create server_thread: %s\n", strerror(err)); exit(1); } pthread_detach(exception_thread); /* Initialize posix_spawn attributes */ posix_spawnattr_init(&attrs); if ((err = posix_spawnattr_setflags(&attrs, POSIX_SPAWN_SETEXEC)) != 0) { fprintf(stderr, "posix_spawnattr_setflags: %s\n", strerror(err)); exit(1); } /* Run Tests */ for(i=0; i<NUMTESTS; i++) { exception_code = 0; /* Set Exception Ports for Current Task */ if ((kr = task_set_exception_ports(task, EXC_MASK_GUARD, exc_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, flavor)) != KERN_SUCCESS) { fprintf(stderr, "task_set_exception_ports: %#x\n", kr); exit(1); } child_args[0] = test_prog_name; sprintf(&test_id[0], "%d", i); child_args[1] = &test_id[0]; child_args[2] = NULL; /* Fork and exec child */ if (fork() == 0) { if ((err = posix_spawn(NULL, child_args[0], NULL, &attrs, &child_args[0], environ)) != 0) { fprintf(stderr, "posix_spawn: %s\n", strerror(err)); exit(1); } } /* Restore exception ports for parent */ if ((kr = task_set_exception_ports(task, EXC_MASK_GUARD, handler, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, flavor)) != KERN_SUCCESS) { fprintf(stderr, "task_set_exception_ports: %#x\n", kr); exit(1); } /* Wait for child and check for exception */ if (-1 == wait4(-1, &child_status, 0, NULL)) { exit(1); } exc_fd = (unsigned int) (exception_code & (uint64_t)EXC_GUARD_FD_MASK); exc_id = exception_code & ~((uint64_t)EXC_GUARD_FD_MASK); printf("EXC_GUARD Received: "); (exception_code != 0)?printf("Yes (Code 0x%llx)\n", exception_code):printf("No\n"); printf("Test Result: "); if((WIFEXITED(child_status) && WEXITSTATUS(child_status)) || (exc_id != test_exception_code[i]) || (test_fd[i] && exc_fd != (unsigned int)TEST_FD)) { test_status = 1; printf("FAILED\n"); } else { printf("PASSED\n"); } printf("-------------------\n"); } exit(test_status); }
static void spawn_and_signal(int signal) { /* do not buffer output to stdout */ setvbuf(stdout, NULL, _IONBF, 0); int ret; posix_spawnattr_t attr; ret = posix_spawnattr_init(&attr); T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "posix_spawnattr_init"); ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED); T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "posix_spawnattr_setflags"); char * const prog = "/usr/bin/true"; char * const argv_child[] = { prog, NULL }; pid_t child_pid; extern char **environ; ret = posix_spawn(&child_pid, prog, NULL, &attr, argv_child, environ); T_ASSERT_POSIX_SUCCESS(ret, "posix_spawn"); printf("parent: spawned child with pid %d\n", child_pid); ret = posix_spawnattr_destroy(&attr); T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "posix_spawnattr_destroy"); int status = 0; int waitpid_result = waitpid(child_pid, &status, WUNTRACED|WNOHANG); T_ASSERT_POSIX_SUCCESS(waitpid_result, "waitpid"); T_ASSERT_EQ(waitpid_result, child_pid, "waitpid should return child we spawned"); T_ASSERT_EQ(WIFEXITED(status), 0, "before SIGCONT: must not have exited"); T_ASSERT_EQ(WIFSTOPPED(status), 1, "before SIGCONT: must be stopped"); printf("parent: continuing child process\n"); ret = kill(child_pid, signal); T_ASSERT_POSIX_SUCCESS(ret, "kill(signal)"); printf("parent: waiting for child process\n"); status = 0; waitpid_result = waitpid(child_pid, &status, 0); T_ASSERT_POSIX_SUCCESS(waitpid_result, "waitpid"); T_ASSERT_EQ(waitpid_result, child_pid, "waitpid should return child we spawned"); if (signal == SIGKILL) { T_ASSERT_EQ(WIFSIGNALED(status), 1, "child should have exited due to signal"); T_ASSERT_EQ(WTERMSIG(status), SIGKILL, "child should have exited due to SIGKILL"); } else { T_ASSERT_EQ(WIFEXITED(status), 1, "child should have exited normally"); T_ASSERT_EQ(WEXITSTATUS(status), EX_OK, "child should have exited with success"); } printf("wait returned with pid %d, status %d\n", ret, status); }
static int reexec(cpu_type_t cputype, const char *guardenv) { posix_spawnattr_t attr; int ret, envcount; size_t copied = 0; char **argv, **oldenvp, **newenvp; char execpath[MAXPATHLEN+1]; uint32_t execsize; char guardstr[32]; argv = *_NSGetArgv(); oldenvp = *_NSGetEnviron(); for (envcount = 0; oldenvp[envcount]; envcount++); // if there are 4 elements and a NULL, envcount will be 4 newenvp = calloc(envcount+2, sizeof(newenvp[0])); for (envcount = 0; oldenvp[envcount]; envcount++) { newenvp[envcount] = oldenvp[envcount]; } snprintf(guardstr, sizeof(guardstr), "%s=1", guardenv); newenvp[envcount++] = guardstr; newenvp[envcount] = NULL; execsize = (uint32_t)sizeof(execpath); ret = _NSGetExecutablePath(execpath, &execsize); if (ret != 0) { return -1; } ret = posix_spawnattr_init(&attr); if (ret != 0) { return -1; } ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC); if (ret != 0) { return -1; } ret = posix_spawnattr_setbinpref_np(&attr, 1, &cputype, &copied); if (ret != 0 || copied != 1) { return -1; } #if 0 fprintf(stderr, "reexec: %s (arch=%d)\n", execpath, cputype); for (envcount=0; newenvp[envcount]; envcount++) { fprintf(stderr, "env[%d] = %s\n", envcount, newenvp[envcount]); } for (envcount=0; argv[envcount]; envcount++) { fprintf(stderr, "argv[%d] = %s\n", envcount, argv[envcount]); } #endif ret = posix_spawn(NULL, execpath, NULL, &attr, argv, newenvp); if (ret != 0) { errno = ret; return -1; } /* should not be reached */ return 0; }
int spawn_main(int argc, char *argv[]) { posix_spawn_file_actions_t file_actions; posix_spawnattr_t attr; FAR const char *filepath; pid_t pid; int ret; /* Initialize the memory monitor */ mm_initmonitor(); /* Initialize the ELF binary loader */ message("Initializing the ELF binary loader\n"); ret = elf_initialize(); if (ret < 0) { err("ERROR: Initialization of the ELF loader failed: %d\n", ret); exit(1); } mm_update(&g_mmstep, "after elf_initialize"); /* Create a ROM disk for the ROMFS filesystem */ message("Registering romdisk at /dev/ram%d\n", CONFIG_EXAMPLES_ELF_DEVMINOR); ret = romdisk_register(CONFIG_EXAMPLES_ELF_DEVMINOR, (FAR uint8_t *)romfs_img, NSECTORS(romfs_img_len), SECTORSIZE); if (ret < 0) { err("ERROR: romdisk_register failed: %d\n", ret); elf_uninitialize(); exit(1); } mm_update(&g_mmstep, "after romdisk_register"); /* Mount the file system */ message("Mounting ROMFS filesystem at target=%s with source=%s\n", MOUNTPT, CONFIG_EXAMPLES_ELF_DEVPATH); ret = mount(CONFIG_EXAMPLES_ELF_DEVPATH, MOUNTPT, "romfs", MS_RDONLY, NULL); if (ret < 0) { err("ERROR: mount(%s,%s,romfs) failed: %s\n", CONFIG_EXAMPLES_ELF_DEVPATH, MOUNTPT, errno); elf_uninitialize(); } mm_update(&g_mmstep, "after mount"); /* Does the system support the PATH variable? Has the PATH variable * already been set? If YES and NO, then set the PATH variable to * the ROMFS mountpoint. */ #if defined(CONFIG_BINFMT_EXEPATH) && !defined(CONFIG_PATH_INITIAL) (void)setenv("PATH", MOUNTPT, 1); #endif /* Make sure that we are using our symbol take */ exec_setsymtab(exports, nexports); /************************************************************************* * Case 1: Simple program with arguments *************************************************************************/ /* Output a seperated so that we can clearly discriminate the output of * this program from the others. */ testheader(g_hello); /* Initialize the attributes file actions structure */ ret = posix_spawn_file_actions_init(&file_actions); if (ret != 0) { err("ERROR: posix_spawn_file_actions_init failed: %d\n", ret); } posix_spawn_file_actions_dump(&file_actions); ret = posix_spawnattr_init(&attr); if (ret != 0) { err("ERROR: posix_spawnattr_init failed: %d\n", ret); } posix_spawnattr_dump(&attr); mm_update(&g_mmstep, "after file_action/attr init"); /* If the binary loader does not support the PATH variable, then * create the full path to the executable program. Otherwise, * use the relative path so that the binary loader will have to * search the PATH variable to find the executable. */ #ifdef CONFIG_BINFMT_EXEPATH filepath = g_hello; #else snprintf(fullpath, 128, "%s/%s", MOUNTPT, g_hello); filepath = fullpath; #endif /* Execute the program */ mm_update(&g_mmstep, "before posix_spawn"); ret = posix_spawn(&pid, filepath, &file_actions, &attr, NULL, (FAR char * const*)&g_argv); if (ret != 0) { err("ERROR: posix_spawn failed: %d\n", ret); } sleep(4); mm_update(&g_mmstep, "after posix_spawn"); /* Free attibutes and file actions */ ret = posix_spawn_file_actions_destroy(&file_actions); if (ret != 0) { err("ERROR: posix_spawn_file_actions_destroy failed: %d\n", ret); } posix_spawn_file_actions_dump(&file_actions); ret = posix_spawnattr_destroy(&attr); if (ret != 0) { err("ERROR: posix_spawnattr_destroy failed: %d\n", ret); } posix_spawnattr_dump(&attr); mm_update(&g_mmstep, "after file_action/attr destruction"); /************************************************************************* * Case 2: Simple program with redirection of stdin to a file input *************************************************************************/ /* Output a seperated so that we can clearly discriminate the output of * this program from the others. */ testheader(g_redirect); /* Initialize the attributes file actions structure */ ret = posix_spawn_file_actions_init(&file_actions); if (ret != 0) { err("ERROR: posix_spawn_file_actions_init failed: %d\n", ret); } posix_spawn_file_actions_dump(&file_actions); ret = posix_spawnattr_init(&attr); if (ret != 0) { err("ERROR: posix_spawnattr_init failed: %d\n", ret); } posix_spawnattr_dump(&attr); mm_update(&g_mmstep, "after file_action/attr init"); /* Set up to close stdin (0) and open testdata.txt as the program input */ ret = posix_spawn_file_actions_addclose(&file_actions, 0); if (ret != 0) { err("ERROR: posix_spawn_file_actions_addclose failed: %d\n", ret); } posix_spawn_file_actions_dump(&file_actions); snprintf(fullpath, 128, "%s/%s", MOUNTPT, g_data); ret = posix_spawn_file_actions_addopen(&file_actions, 0, fullpath, O_RDONLY, 0644); if (ret != 0) { err("ERROR: posix_spawn_file_actions_addopen failed: %d\n", ret); } posix_spawn_file_actions_dump(&file_actions); mm_update(&g_mmstep, "after adding file_actions"); /* If the binary loader does not support the PATH variable, then * create the full path to the executable program. Otherwise, * use the relative path so that the binary loader will have to * search the PATH variable to find the executable. */ #ifdef CONFIG_BINFMT_EXEPATH filepath = g_redirect; #else snprintf(fullpath, 128, "%s/%s", MOUNTPT, g_redirect); filepath = fullpath; #endif /* Execute the program */ mm_update(&g_mmstep, "before posix_spawn"); ret = posix_spawn(&pid, filepath, &file_actions, &attr, NULL, NULL); if (ret != 0) { err("ERROR: posix_spawn failed: %d\n", ret); } sleep(2); mm_update(&g_mmstep, "after posix_spawn"); /* Free attibutes and file actions */ ret = posix_spawn_file_actions_destroy(&file_actions); if (ret != 0) { err("ERROR: posix_spawn_file_actions_destroy failed: %d\n", ret); } posix_spawn_file_actions_dump(&file_actions); ret = posix_spawnattr_destroy(&attr); if (ret != 0) { err("ERROR: posix_spawnattr_destroy failed: %d\n", ret); } posix_spawnattr_dump(&attr); mm_update(&g_mmstep, "after file_action/attr destruction"); /* Clean-up */ elf_uninitialize(); mm_update(&g_mmstep, "End-of-Test"); return 0; }
// ---- C.spawn ---- int VMPI_spawn(int *pid, const char *path, char *const argv[], char *const envp[]) { return posix_spawn( (pid_t *)pid, path, NULL, NULL, argv, envp ); }
pid_t spawnvex(const char *path, char *const argv[], char *const envv[], Spawnvex_t *vex) { int i; int op; unsigned int flags = 0; pid_t pid; int arg; int err; int fd; posix_spawnattr_t ax; posix_spawn_file_actions_t fx; Spawnvex_t *xev = NULL; #if _lib_spawn_mode || _lib_spawn && _mem_pgroup_inheritance pid_t pgid; int arg; int j; int k; int m; int ic; int jc; Spawnvex_t *xev; #if !_lib_spawn_mode int *map; int a; struct inheritance inherit; #endif #endif if (vex && vex->debug >= 0) { error(ERROR_OUTPUT, vex->debug, "spawnvex exe %4d %8d %p %4d \"%s\"", __LINE__, getpid(), vex, vex->cur, path); } #if _lib_spawn_mode || _lib_spawn && _mem_pgroup_inheritance if (!envv) envv = environ; pid = -1; m = 0; if (vex) { vex->noexec = -1; vex->pgrp = -1; flags = vex->flags; if (!(xev = spawnvex_open(0))) goto bad; j = -1; for (i = 0; i < vex->cur;) { op = vex->op[i++].number; arg = vex->op[i++].number; if (op & 1) i += 2; op /= 2; if (op >= 0) { if (m < op) m = op + 1; if (m < arg) m = arg + 1; } else if (op == SPAWN_cwd) j = arg; } if (j >= 0) { if ((i = open(".", O_RDONLY)) < 0) goto bad; if ((i = save(i, &ic, m)) < 0) goto bad; if (spawnvex_add(xev, SPAWN_cwd, i, 0, 0) < 0 || restore(xev, i, -1, 0) < 0) { close(i); goto bad; } if (fchdir(j) < 0) goto bad; if ((i = save(j, &jc, m)) < 0) goto bad; if (restore(xev, i, j, jc) < 0) { close(i); goto bad; } } } else { flags = 0; xev = NULL; } #if _lib_spawn_mode if (vex) for (i = 0; i < vex->cur;) { op = vex->op[i++].number; arg = vex->op[i++].number; if (op & 1) i += 2; switch (op /= 2) { case SPAWN_frame: vex->frame = (unsigned int)arg; break; case SPAWN_pgrp: vex->pgrp = (pid_t)arg; break; default: if (op >= 0) { if (arg < 0) { if ((i = save(op, &ic, m)) < 0) { if (i < -1) goto bad; } else if (restore(xev, i, op, ic) < 0) { close(i); goto bad; } else close(op); } else if (arg == op) { if (spawnvex_add(xev, SPAWN_cloexec, arg, 0, 0) < 0) goto bad; if (fcntl(arg, F_SETFD, 0) < 0) goto bad; } else if ((j = save(arg, &jc, m)) < -1) goto bad; else { close(arg); if (fcntl(op, F_DUPFD, arg) >= 0) { if ((i = save(op, &ic, m)) >= 0) { if (restore(xev, i, op, ic) >= 0) { close(op); if (j < 0 || restore(xev, j, arg, jc) >= 0) continue; } close(i); } } if (j >= 0) { fcntl(j, F_DUPFD, arg); close(j); } goto bad; } } break; } } pid = spawnve(vex && vex->pgrp >= 0 ? P_DETACH : P_NOWAIT, path, argv, envv); #else inherit.flags = 0; map = 0; if (vex) { if (m) { map = calloc(1, m * sizeof(int)); if (!map) goto bad; for (i = 0; i < m; i++) map[i] = i; } for (i = 0; i < vex->cur;) { op = vex->op[i++].number; a = i; arg = vex->op[i++].number; if (op & 1) i += 2; switch (op /= 2) { case SPAWN_noop: break; case SPAWN_noexec: break; case SPAWN_frame: vex->frame = (unsigned int)arg; break; case SPAWN_pgrp: inherit.flags |= SPAWN_SETGROUP; inherit.pgroup = arg ? arg : SPAWN_NEWPGROUP; break; case SPAWN_sigdef: inherit.flags |= SPAWN_SETSIGDEF; sigemptyset(&inherit.sigdefault); for (j = 1; j < 8 * sizeof(vex->op[a].number); j++) if (vex->op[a].number & (1 << j)) sigaddset(&inherit.sigdefault, j); break; case SPAWN_sigmask: inherit.flags |= SPAWN_SETSIGMASK; sigemptyset(&inherit.sigmask); for (j = 1; j < 8 * sizeof(vex->op[a].number); j++) if (vex->op[a].number & (1 << j)) sigaddset(&inherit.sigmask, j); break; default: if (op < 0) { errno = EINVAL; goto bad; } else if (arg < 0) map[op] = SPAWN_FDCLOSED; else map[op] = arg; break; } } } pid = spawn(path, m, map, &inherit, (const char **)argv, (const char **)envv); #endif if (pid >= 0 && vex) VEXINIT(vex); bad: if (xev) { spawnvex_apply(xev, 0, SPAWN_FLUSH | SPAWN_NOCALL); spawnvex_close(xev); } #if !_lib_spawn_mode if (map) free(map); #endif return pid; #else #if _lib_spawnve if (!vex || !vex->cur && !vex->flags) return spawnve(path, argv, envv); #endif if (vex && ((vex->set & (0 #if !_lib_posix_spawnattr_setfchdir | VEXFLAG(SPAWN_cwd) #endif #if !_lib_posix_spawnattr_setsid | VEXFLAG(SPAWN_sid) #endif #if !_lib_posix_spawnattr_setumask | VEXFLAG(SPAWN_umask) #endif )) #if !_lib_posix_spawn || !(vex->flags & SPAWN_EXEC) #endif )) { int n; int m; Spawnvex_noexec_t nx; int msg[2]; if (!envv) envv = environ; n = errno; if (pipe(msg) < 0) { msg[0] = msg[1] = -1; } else { (void)fcntl(msg[0], F_SETFD, FD_CLOEXEC); (void)fcntl(msg[1], F_SETFD, FD_CLOEXEC); } if (!(flags & SPAWN_FOREGROUND)) sigcritical(SIG_REG_EXEC | SIG_REG_PROC); pid = fork(); if (pid == -1) { n = errno; } else if (!pid) { if (!(flags & SPAWN_FOREGROUND)) sigcritical(SIG_REG_POP); if (vex && (n = spawnvex_apply(vex, 0, SPAWN_FRAME | SPAWN_NOCALL))) { errno = n; } else { if (vex && vex->debug >= 0) { error(ERROR_OUTPUT, vex->debug, "spawnvex exe %4d %8d %p %4d \"%s\"", __LINE__, getpid(), vex, vex->cur, path); } execve(path, argv, envv); if (vex && vex->debug >= 0) { error(ERROR_OUTPUT, vex->debug, "spawnvex exe %4d %8d %p %4d \"%s\" FAILED", __LINE__, getpid(), vex, vex->cur, path); } if (vex && (i = vex->noexec) >= 0) { nx.vex = vex; nx.handle = vex->op[i + 3].handle; nx.path = path; nx.argv = argv; nx.envv = envv; #if _use_spawn_exec /* * setting SPAWN_EXEC here means that it is more efficient to * exec(interpreter) on script than to fork() initialize and * read script -- highly subjective, based on some ksh * implementaions, and probably won't be set unless its a * noticable win */ nx.flags |= SPAWN_EXEC; #endif nx.msgfd = msg[1]; errno = (*vex->op[i + 2].callback)(&nx, SPAWN_noexec, errno); } } if (msg[1] != -1) { m = errno; write(msg[1], &m, sizeof(m)); } _exit(errno == ENOENT ? EXIT_NOTFOUND : EXIT_NOEXEC); } if (msg[0] != -1) { close(msg[1]); if (pid != -1) { m = 0; while (read(msg[0], &m, sizeof(m)) == -1) { if (errno != EINTR) { m = errno; break; } } if (m) { while (waitpid(pid, &n, 0) && errno == EINTR) { ; } pid = -1; n = m; } } close(msg[0]); } if (!(flags & SPAWN_FOREGROUND)) sigcritical(SIG_REG_POP); if (pid != -1 && vex) VEXINIT(vex); errno = n; return pid; } if (vex) { err = posix_spawnattr_init(&ax); if (err) goto nope; err = posix_spawn_file_actions_init(&fx); if (err) { posix_spawnattr_destroy(&ax); goto nope; } for (i = 0; i < vex->cur;) { op = vex->op[i++].number; arg = vex->op[i++].number; if (op & 1) i += 2; switch (op /= 2) { case SPAWN_noop: break; case SPAWN_noexec: break; case SPAWN_frame: break; #if _lib_posix_spawnattr_setfchdir case SPAWN_cwd: err = posix_spawnattr_setfchdir(&ax, arg); if (err) goto bad; break; #endif case SPAWN_pgrp: err = posix_spawnattr_setpgroup(&ax, arg); if (err) goto bad; err = posix_spawnattr_setflags(&ax, POSIX_SPAWN_SETPGROUP); if (err) goto bad; break; case SPAWN_resetids: err = posix_spawnattr_setflags(&ax, POSIX_SPAWN_RESETIDS); if (err) goto bad; break; #if _lib_posix_spawnattr_setsid case SPAWN_sid: err = posix_spawnattr_setsid(&ax, arg); if (err) goto bad; break; #endif case SPAWN_sigdef: break; case SPAWN_sigmask: break; #if _lib_posix_spawnattr_setumask case SPAWN_umask: if (err = posix_spawnattr_setumask(&ax, arg)) goto bad; break; #endif default: if (op < 0) { err = EINVAL; goto bad; } else if (arg < 0) { err = posix_spawn_file_actions_addclose(&fx, op); if (err) goto bad; } else if (arg == op) { #ifdef F_DUPFD_CLOEXEC if ((fd = fcntl(op, F_DUPFD_CLOEXEC, 0)) < 0) #else if ((fd = fcntl(op, F_DUPFD, 0)) < 0 || fcntl(fd, F_SETFD, FD_CLOEXEC) < 0 && (close(fd), 1)) #endif { err = errno; goto bad; } if (!xev && !(xev = spawnvex_open(0))) goto bad; spawnvex_add(xev, fd, -1, 0, 0); err = posix_spawn_file_actions_adddup2(&fx, fd, op); if (err) goto bad; } else { err = posix_spawn_file_actions_adddup2(&fx, op, arg); if (err) goto bad; } break; } } // Ensure stdin, stdout, stderr are open in the child process. // See https://github.com/att/ast/issues/1117. for (int fd = 0; fd < 3; ++fd) { errno = 0; if (fcntl(fd, F_GETFD, NULL) == -1 || errno == EBADF) { err = posix_spawn_file_actions_addopen(&fx, fd, "/dev/null", O_RDWR, 0); if (err) goto bad; } } err = posix_spawn(&pid, path, &fx, &ax, argv, envv ? envv : environ); if (err) goto bad; posix_spawnattr_destroy(&ax); posix_spawn_file_actions_destroy(&fx); if (xev) { spawnvex_apply(xev, 0, SPAWN_NOCALL); spawnvex_close(xev); } if (vex->flags & SPAWN_CLEANUP) spawnvex_apply(vex, 0, SPAWN_FRAME | SPAWN_CLEANUP); VEXINIT(vex); } else { err = posix_spawn(&pid, path, NULL, NULL, argv, envv ? envv : environ); if (err) goto nope; } if (vex && vex->debug >= 0) { error(ERROR_OUTPUT, vex->debug, "spawnvex exe %4d %8d %p %4d \"%s\" %8d posix_spawn", __LINE__, getpid(), vex, vex->cur, path, pid); } return pid; bad: posix_spawnattr_destroy(&ax); posix_spawn_file_actions_destroy(&fx); if (xev) { spawnvex_apply(xev, 0, SPAWN_NOCALL); spawnvex_close(xev); } nope: errno = err; if (vex && vex->debug >= 0) { error(ERROR_OUTPUT, vex->debug, "spawnvex exe %4d %8d %p %4d \"%s\" %8d posix_spawn FAILED", __LINE__, getpid(), vex, vex->cur, path, -1); } return -1; #endif }
R_API int r_sandbox_system (const char *x, int n) { if (enabled) { eprintf ("sandbox: system call disabled\n"); return -1; } #if LIBC_HAVE_FORK #if LIBC_HAVE_SYSTEM if (n) { #if APPLE_SDK_IPHONEOS #include <dlfcn.h> int (*__system)(const char *cmd) = dlsym (NULL, "system"); if (__system) { return __system (x); } return -1; #else return system (x); #endif } return execl ("/bin/sh", "sh", "-c", x, (const char*)NULL); #else #include <spawn.h> if (n && !strchr (x, '|')) { char **argv, *cmd = strdup (x); int rc, pid, argc; char *isbg = strchr (cmd, '&'); // XXX this is hacky if (isbg) { *isbg = 0; } argv = r_str_argv (cmd, &argc); if (argv) { char *argv0 = r_file_path (argv[0]); if (!argv0) { eprintf ("Cannot find '%s'\n", argv[0]); return -1; } pid = 0; posix_spawn (&pid, argv0, NULL, NULL, argv, NULL); if (isbg) { // XXX. wait for children rc = 0; } else { rc = waitpid (pid, NULL, 0); } r_str_argv_free (argv); free (argv0); return rc; } eprintf ("Error parsing command arguments\n"); return -1; } int child = fork(); if (child == -1) return -1; if (child) { return waitpid (child, NULL, 0); } execl ("/bin/sh", "sh", "-c", x, (const char*)NULL); exit (1); #endif #endif return -1; }
int runDaemonRun(void) { // Register for all the notifications int fd; int quitToken = 0; if (notify_register_file_descriptor(quitNotification, &fd, 0, &quitToken) != NOTIFY_STATUS_OK) { return 0x10; } int startToken = 0; if (notify_register_file_descriptor(startNotification, &fd, NOTIFY_REUSE, &startToken) != NOTIFY_STATUS_OK) { return 0x11; } int stopToken = 0; if (notify_register_file_descriptor(stopNotification, &fd, NOTIFY_REUSE, &stopToken) != NOTIFY_STATUS_OK) { return 0x12; } int debugToken = 0; if (notify_register_file_descriptor(debugNotification, &fd, NOTIFY_REUSE, &debugToken) != NOTIFY_STATUS_OK) { return 0x14; } print_message(0, "Started as %d(%d):%d(%d)\n", getuid(), geteuid(), getgid(), getegid()); fd_set readfds; FD_ZERO(&readfds); FD_SET(fd, &readfds); pid_t ngircdPid = 0; int ps; char debug = 1; char shouldContinue = 1; while (shouldContinue) { int status = select(fd + 1, &readfds, NULL, NULL, NULL); if (status <= 0 || !FD_ISSET(fd, &readfds)) { continue; } int t; status = read(fd, &t, sizeof(int)); if (status < 0) { continue; } t = ntohl(t); // notify_register_file_descriptor docs: "The value is sent in network byte order." // Value in file descriptor matches token for quit notification if (t == quitToken) { if (debug) print_message(0, "Quitting\n"); shouldContinue = 0; } // Value in file descriptor matches token for start notification if (t == startToken) { if (debug) print_message(0, "Starting\n"); // Start ngircd as non-daemon, using custom config file const char *args[] = {"ngircd", "-n", "-f", "/Library/Application Support/IRCyslog/ircyslog.conf", NULL}; if (!ngircdPid) { ps = posix_spawn(&ngircdPid, "/usr/sbin/ngircd", NULL, NULL, (char *const *)args, NULL); if (!ps) { if (debug) print_message(0, "Started ngircd with pid:%d\n", ngircdPid); } } // Start the IRC bot // ... } // Value in file descriptor matches token for stop notification // or the quit notification was posted if (t == stopToken || !shouldContinue) { if (debug) print_message(0, "stopping"); // Stop the IRC bot // ... if (!ps) { // If the server is running, kill it if (debug) print_message(0, "Calling kill with (%d, %d)\n", ngircdPid, SIGTERM); int k = kill(ngircdPid, SIGTERM); if (debug) print_message(0, "kill'd with exit status %d\n", k); // Wait for the server to finish and purge from the process list int st; if (debug) print_message(0, "Calling waitpid with (%d,%d)\n", ngircdPid, 0); int w = waitpid(ngircdPid, &st, 0); if (debug) print_message(0, "waitpid'd with exit status %d, stat_loc %d\n", w, st); } } // Value in file descriptor matches token for debug notification if (t == debugToken) { debug = !debug; print_message(0, "debug is %s\n", debug ? "ON" : "OFF"); } } // Cancel notification watching print_message(0, "Cancelling notifications\n"); notify_cancel(quitToken); notify_cancel(startToken); notify_cancel(stopToken); notify_cancel(debugToken); return 0; }
static PRProcess * ForkAndExec( const char *path, char *const *argv, char *const *envp, const PRProcessAttr *attr) { PRProcess *process; int nEnv, idx; char *const *childEnvp; char **newEnvp = NULL; int flags; #ifdef VMS char VMScurdir[FILENAME_MAX+1] = { '\0' } ; #endif process = PR_NEW(PRProcess); if (!process) { PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); return NULL; } childEnvp = envp; if (attr && attr->fdInheritBuffer) { PRBool found = PR_FALSE; if (NULL == childEnvp) { #ifdef DARWIN childEnvp = *(_NSGetEnviron()); #else childEnvp = environ; #endif } for (nEnv = 0; childEnvp[nEnv]; nEnv++) { } newEnvp = (char **) PR_MALLOC((nEnv + 2) * sizeof(char *)); if (NULL == newEnvp) { PR_DELETE(process); PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); return NULL; } for (idx = 0; idx < nEnv; idx++) { newEnvp[idx] = childEnvp[idx]; if (!found && !strncmp(newEnvp[idx], "NSPR_INHERIT_FDS=", 17)) { newEnvp[idx] = attr->fdInheritBuffer; found = PR_TRUE; } } if (!found) { newEnvp[idx++] = attr->fdInheritBuffer; } newEnvp[idx] = NULL; childEnvp = newEnvp; } #ifdef VMS /* ** Since vfork/exec is implemented VERY differently on OpenVMS, we have to ** handle the setting up of the standard streams very differently. And since ** none of this code can ever execute in the context of the child, we have ** to perform the chdir in the parent so the child is born into the correct ** directory (and then switch the parent back again). */ { int decc$set_child_standard_streams(int,int,int); int n, fd_stdin=0, fd_stdout=1, fd_stderr=2; /* Set up any standard streams we are given, assuming defaults */ if (attr) { if (attr->stdinFd) fd_stdin = attr->stdinFd->secret->md.osfd; if (attr->stdoutFd) fd_stdout = attr->stdoutFd->secret->md.osfd; if (attr->stderrFd) fd_stderr = attr->stderrFd->secret->md.osfd; } /* ** Put a lock around anything that isn't going to be thread-safe. */ PR_Lock(_pr_vms_fork_lock); /* ** Prepare the child's streams. We always do this in case a previous fork ** has left the stream assignments in some non-standard way. */ n = decc$set_child_standard_streams(fd_stdin,fd_stdout,fd_stderr); if (n == -1) { PR_SetError(PR_BAD_DESCRIPTOR_ERROR, errno); PR_DELETE(process); if (newEnvp) { PR_DELETE(newEnvp); } PR_Unlock(_pr_vms_fork_lock); return NULL; } /* Switch directory if we have to */ if (attr) { if (attr->currentDirectory) { if ( (getcwd(VMScurdir,sizeof(VMScurdir)) == NULL) || (chdir(attr->currentDirectory) < 0) ) { PR_SetError(PR_DIRECTORY_OPEN_ERROR, errno); PR_DELETE(process); if (newEnvp) { PR_DELETE(newEnvp); } PR_Unlock(_pr_vms_fork_lock); return NULL; } } } } #endif /* VMS */ #ifdef AIX process->md.pid = (*pr_wp.forkptr)(); #elif defined(NTO) || defined(SYMBIAN) /* * fork() & exec() does not work in a multithreaded process. * Use spawn() instead. */ { int fd_map[3] = { 0, 1, 2 }; if (attr) { if (attr->stdinFd && attr->stdinFd->secret->md.osfd != 0) { fd_map[0] = dup(attr->stdinFd->secret->md.osfd); flags = fcntl(fd_map[0], F_GETFL, 0); if (flags & O_NONBLOCK) fcntl(fd_map[0], F_SETFL, flags & ~O_NONBLOCK); } if (attr->stdoutFd && attr->stdoutFd->secret->md.osfd != 1) { fd_map[1] = dup(attr->stdoutFd->secret->md.osfd); flags = fcntl(fd_map[1], F_GETFL, 0); if (flags & O_NONBLOCK) fcntl(fd_map[1], F_SETFL, flags & ~O_NONBLOCK); } if (attr->stderrFd && attr->stderrFd->secret->md.osfd != 2) { fd_map[2] = dup(attr->stderrFd->secret->md.osfd); flags = fcntl(fd_map[2], F_GETFL, 0); if (flags & O_NONBLOCK) fcntl(fd_map[2], F_SETFL, flags & ~O_NONBLOCK); } PR_ASSERT(attr->currentDirectory == NULL); /* not implemented */ } #ifdef SYMBIAN /* In Symbian OS, we use posix_spawn instead of fork() and exec() */ posix_spawn(&(process->md.pid), path, NULL, NULL, argv, childEnvp); #else process->md.pid = spawn(path, 3, fd_map, NULL, argv, childEnvp); #endif if (fd_map[0] != 0) close(fd_map[0]); if (fd_map[1] != 1) close(fd_map[1]); if (fd_map[2] != 2) close(fd_map[2]); } #else process->md.pid = fork(); #endif if ((pid_t) -1 == process->md.pid) { PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, errno); PR_DELETE(process); if (newEnvp) { PR_DELETE(newEnvp); } return NULL; } else if (0 == process->md.pid) { /* the child process */ /* * If the child process needs to exit, it must call _exit(). * Do not call exit(), because exit() will flush and close * the standard I/O file descriptors, and hence corrupt * the parent process's standard I/O data structures. */ #if !defined(NTO) && !defined(SYMBIAN) #ifdef VMS /* OpenVMS has already handled all this above */ #else if (attr) { /* the osfd's to redirect stdin, stdout, and stderr to */ int in_osfd = -1, out_osfd = -1, err_osfd = -1; if (attr->stdinFd && attr->stdinFd->secret->md.osfd != 0) { in_osfd = attr->stdinFd->secret->md.osfd; if (dup2(in_osfd, 0) != 0) { _exit(1); /* failed */ } flags = fcntl(0, F_GETFL, 0); if (flags & O_NONBLOCK) { fcntl(0, F_SETFL, flags & ~O_NONBLOCK); } } if (attr->stdoutFd && attr->stdoutFd->secret->md.osfd != 1) { out_osfd = attr->stdoutFd->secret->md.osfd; if (dup2(out_osfd, 1) != 1) { _exit(1); /* failed */ } flags = fcntl(1, F_GETFL, 0); if (flags & O_NONBLOCK) { fcntl(1, F_SETFL, flags & ~O_NONBLOCK); } } if (attr->stderrFd && attr->stderrFd->secret->md.osfd != 2) { err_osfd = attr->stderrFd->secret->md.osfd; if (dup2(err_osfd, 2) != 2) { _exit(1); /* failed */ } flags = fcntl(2, F_GETFL, 0); if (flags & O_NONBLOCK) { fcntl(2, F_SETFL, flags & ~O_NONBLOCK); } } if (in_osfd != -1) { close(in_osfd); } if (out_osfd != -1 && out_osfd != in_osfd) { close(out_osfd); } if (err_osfd != -1 && err_osfd != in_osfd && err_osfd != out_osfd) { close(err_osfd); } if (attr->currentDirectory) { if (chdir(attr->currentDirectory) < 0) { _exit(1); /* failed */ } } } #endif /* !VMS */ if (childEnvp) { (void)execve(path, argv, childEnvp); } else { /* Inherit the environment of the parent. */ (void)execv(path, argv); } /* Whoops! It returned. That's a bad sign. */ #ifdef VMS /* ** On OpenVMS we are still in the context of the parent, and so we ** can (and should!) perform normal error handling. */ PR_SetError(PR_UNKNOWN_ERROR, errno); PR_DELETE(process); if (newEnvp) { PR_DELETE(newEnvp); } if (VMScurdir[0] != '\0') chdir(VMScurdir); PR_Unlock(_pr_vms_fork_lock); return NULL; #else _exit(1); #endif /* VMS */ #endif /* !NTO */ } if (newEnvp) { PR_DELETE(newEnvp); } #ifdef VMS /* If we switched directories, then remember to switch back */ if (VMScurdir[0] != '\0') { chdir(VMScurdir); /* can't do much if it fails */ } PR_Unlock(_pr_vms_fork_lock); #endif /* VMS */ #if defined(_PR_NATIVE_THREADS) PR_Lock(pr_wp.ml); if (0 == pr_wp.numProcs++) { PR_NotifyCondVar(pr_wp.cv); } PR_Unlock(pr_wp.ml); #endif return process; }
N_NIMCALL(pid_t, startprocessauxspawn_145001)(Tstartprocessdata144948* data) { pid_t result; posix_spawnattr_t attr; posix_spawn_file_actions_t fops; sigset_t mask; int res; pid_t pid; int LOC98; int LOC99; { result = 0; memset((void*)(&attr), 0, sizeof(attr)); memset((void*)(&fops), 0, sizeof(fops)); { int LOC3; NI32 LOC6; LOC3 = 0; LOC3 = posix_spawn_file_actions_init((&fops)); if (!!((LOC3 == ((NI32) 0)))) goto LA4; LOC6 = 0; LOC6 = oslasterror_117033(); raiseoserror_117009(LOC6); } LA4: ; { int LOC9; NI32 LOC12; LOC9 = 0; LOC9 = posix_spawnattr_init((&attr)); if (!!((LOC9 == ((NI32) 0)))) goto LA10; LOC12 = 0; LOC12 = oslasterror_117033(); raiseoserror_117009(LOC12); } LA10: ; memset((void*)(&mask), 0, sizeof(mask)); { int LOC15; NI32 LOC18; LOC15 = 0; LOC15 = sigemptyset((&mask)); if (!!((LOC15 == ((NI32) 0)))) goto LA16; LOC18 = 0; LOC18 = oslasterror_117033(); raiseoserror_117009(LOC18); } LA16: ; { int LOC21; NI32 LOC24; LOC21 = 0; LOC21 = posix_spawnattr_setsigmask((&attr), (&mask)); if (!!((LOC21 == ((NI32) 0)))) goto LA22; LOC24 = 0; LOC24 = oslasterror_117033(); raiseoserror_117009(LOC24); } LA22: ; { int LOC27; NI32 LOC30; LOC27 = 0; LOC27 = posix_spawnattr_setpgroup((&attr), ((pid_t) 0)); if (!!((LOC27 == ((NI32) 0)))) goto LA28; LOC30 = 0; LOC30 = oslasterror_117033(); raiseoserror_117009(LOC30); } LA28: ; { int LOC33; NI32 LOC36; LOC33 = 0; LOC33 = posix_spawnattr_setflags((&attr), (NI32)((NI32)(((int) 0) | POSIX_SPAWN_SETSIGMASK) | POSIX_SPAWN_SETPGROUP)); if (!!((LOC33 == ((NI32) 0)))) goto LA34; LOC36 = 0; LOC36 = oslasterror_117033(); raiseoserror_117009(LOC36); } LA34: ; { if (!!((*data).optionpoparentstreams)) goto LA39; { int LOC43; NI32 LOC46; LOC43 = 0; LOC43 = posix_spawn_file_actions_addclose((&fops), (*data).pstdin[(((NI) 1))- 0]); if (!!((LOC43 == ((NI32) 0)))) goto LA44; LOC46 = 0; LOC46 = oslasterror_117033(); raiseoserror_117009(LOC46); } LA44: ; { int LOC49; NI32 LOC52; LOC49 = 0; LOC49 = posix_spawn_file_actions_adddup2((&fops), (*data).pstdin[(((NI) 0))- 0], ((int) 0)); if (!!((LOC49 == ((NI32) 0)))) goto LA50; LOC52 = 0; LOC52 = oslasterror_117033(); raiseoserror_117009(LOC52); } LA50: ; { int LOC55; NI32 LOC58; LOC55 = 0; LOC55 = posix_spawn_file_actions_addclose((&fops), (*data).pstdout[(((NI) 0))- 0]); if (!!((LOC55 == ((NI32) 0)))) goto LA56; LOC58 = 0; LOC58 = oslasterror_117033(); raiseoserror_117009(LOC58); } LA56: ; { int LOC61; NI32 LOC64; LOC61 = 0; LOC61 = posix_spawn_file_actions_adddup2((&fops), (*data).pstdout[(((NI) 1))- 0], ((int) 1)); if (!!((LOC61 == ((NI32) 0)))) goto LA62; LOC64 = 0; LOC64 = oslasterror_117033(); raiseoserror_117009(LOC64); } LA62: ; { int LOC67; NI32 LOC70; LOC67 = 0; LOC67 = posix_spawn_file_actions_addclose((&fops), (*data).pstderr[(((NI) 0))- 0]); if (!!((LOC67 == ((NI32) 0)))) goto LA68; LOC70 = 0; LOC70 = oslasterror_117033(); raiseoserror_117009(LOC70); } LA68: ; { if (!(*data).optionpostderrtostdout) goto LA73; { int LOC77; NI32 LOC80; LOC77 = 0; LOC77 = posix_spawn_file_actions_adddup2((&fops), (*data).pstdout[(((NI) 1))- 0], ((int) 2)); if (!!((LOC77 == ((NI32) 0)))) goto LA78; LOC80 = 0; LOC80 = oslasterror_117033(); raiseoserror_117009(LOC80); } LA78: ; } goto LA71; LA73: ; { { int LOC84; NI32 LOC87; LOC84 = 0; LOC84 = posix_spawn_file_actions_adddup2((&fops), (*data).pstderr[(((NI) 1))- 0], ((int) 2)); if (!!((LOC84 == ((NI32) 0)))) goto LA85; LOC87 = 0; LOC87 = oslasterror_117033(); raiseoserror_117009(LOC87); } LA85: ; } LA71: ; } LA39: ; res = 0; { NimStringDesc* LOC92; if (!(((NI) 0) < ((*data).workingdir ? strlen((*data).workingdir) : 0))) goto LA90; LOC92 = 0; LOC92 = cstrToNimstr((*data).workingdir); setcurrentdir_119207(LOC92); } LA90: ; pid = 0; { if (!(*data).optionpousepath) goto LA95; res = posix_spawnp((&pid), (*data).syscommand, (&fops), (&attr), (*data).sysargs, (*data).sysenv); } goto LA93; LA95: ; { res = posix_spawn((&pid), (*data).syscommand, (&fops), (&attr), (*data).sysargs, (*data).sysenv); } LA93: ; LOC98 = 0; LOC98 = posix_spawn_file_actions_destroy((&fops)); LOC99 = 0; LOC99 = posix_spawnattr_destroy((&attr)); { NI32 LOC104; if (!!((res == ((NI32) 0)))) goto LA102; LOC104 = 0; LOC104 = oslasterror_117033(); raiseoserror_117009(LOC104); } LA102: ; result = pid; goto BeforeRet; }BeforeRet: ; return result; }
int shell ( char *args[] ) { char cmd[MAXCMDLEN + 1]; int i, key, rv; timespec_t t __attribute__ ((unused)); int argnum; char *argval[MAXARGS + 1]; pthread_t thr; struct pollfd fds = { 0 /* stdin */, POLLRDNORM, 0 }; //printf ( "\x1b[37m" ); /* test escape sequence: white text */ printf ( "\n*** Simple shell interpreter ***\n\n" ); //printf ( "\x1b[32m" ); /* test escape sequence: green text */ help (); t.tv_sec = 0; t.tv_nsec = 100000000; /* 100 ms */ while (1) { new_cmd: printf ( "\n> " ); i = 0; memset ( cmd, 0, MAXCMDLEN ); /* get command - get chars until new line is received */ while ( i < MAXCMDLEN ) { if ( !poll ( &fds, 1, 0 ) ) /* is anything pressed? */ { nanosleep ( &t, NULL ); continue; } key = getchar (); if ( !key ) /* not ascii? */ continue; if ( key == '\n' || key == '\r') { if ( i > 0 ) break; else goto new_cmd; } switch ( key ) { case '\b': if ( i > 0 ) { cmd[--i] = 0; printf ( "%c", key ); } break; default: printf ( "%c", key ); cmd[i++] = (char) key; break; } } printf ( "\n" ); /* parse command line */ argnum = 0; for(i = 0; i < MAXCMDLEN && cmd[i]!=0 && argnum < MAXARGS; i++) { if ( cmd[i] == ' ' || cmd[i] == '\t') continue; argval[argnum++] = &cmd[i]; while ( cmd[i] && cmd[i] != ' ' && cmd[i] != '\t' && i < MAXCMDLEN ) i++; cmd[i] = 0; } argval[argnum] = NULL; /* match command to shell command */ for ( i = 0; sh_cmd[i].func != NULL; i++ ) { if ( strcmp ( argval[0], sh_cmd[i].name ) == 0 ) { if ( sh_cmd[i].func ( argval ) ) printf ( "\nProgram returned error!\n" ); goto new_cmd; } } /* not shell command; start given program by calling kernel */ rv = posix_spawn ( &thr, argval[0], NULL, NULL, argval, NULL ); if ( !rv ) { if ( argnum < 2 || argval[argnum-1][0] != '&' ) pthread_join ( thr, NULL ); goto new_cmd; } if ( strcmp ( argval[0], "quit" ) == 0 || strcmp ( argval[0], "exit" ) == 0 ) break; /* not program kernel or shell knows about it - report error! */ printf ( "Invalid command or program already started!" ); } printf ( "Exiting from shell\n" ); return 0; }
/* * non-zero return is success */ EXPORT BOOL CreateProcessA(LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ) { // problem here in OS X because we are not suspending the target so it will happily run ! :-) // we need to use ptrace to start the new target in a suspended state, which happens in Win32 with // the DEBUG_PROCESS/DEBUG_ONLY_THIS_PROCESS flags to CreateProcessA // fG! - 04/10/2010 #if DEBUG printf("[DEBUG] Creating process %s %s\n", lpApplicationName, lpCommandLine); #endif posix_spawnattr_t attr; int retval = 0; size_t copied = 0; short flags = 0; cpu_type_t cpu = 0; #if defined (__arm__) cpu = CPU_TYPE_ARM; // ooops... distinction is done at subtype which // posix_spawn doesn't support // lldb solution seems to not set this - must test #else // default target is 32bits cpu = CPU_TYPE_I386; if (dwCreationFlags & TARGET_IS_64BITS) cpu = CPU_TYPE_X86_64; #endif retval = posix_spawnattr_init (&attr); // set process flags // the new process will start in a suspended state and permissions reset to real uid/gid flags = POSIX_SPAWN_RESETIDS | POSIX_SPAWN_START_SUSPENDED; // disable ASLR, default is YES from PyDbg // Snow Leopard will just ignore this flag if (dwCreationFlags & _POSIX_SPAWN_DISABLE_ASLR) flags |= _POSIX_SPAWN_DISABLE_ASLR; retval = posix_spawnattr_setflags(&attr, flags); // reset signals, ripped from LLDB :-] sigset_t no_signals; sigset_t all_signals; sigemptyset (&no_signals); sigfillset (&all_signals); posix_spawnattr_setsigmask(&attr, &no_signals); posix_spawnattr_setsigdefault(&attr, &all_signals); // set the target cpu to be used, due to fat binaries retval = posix_spawnattr_setbinpref_np(&attr, 1, &cpu, &copied); char *spawnedEnv[] = { NULL }; int cmd_line_len = strlen(lpCommandLine); if (cmd_line_len >= ARG_MAX) { fprintf(stderr, "[ERROR] arg list too long\n"); exit(1); } if (cmd_line_len) { // parse command line; int i = 0; char *p = strchr(lpCommandLine, ' '); char *q = lpCommandLine; char **argv = malloc(sizeof(char*) * 256); while (p && i < 253) { *p = '\0'; argv[i++] = q; q = p + 1; p = strchr(q, ' '); } errno = 0; argv[i] = q; argv[i+1] = NULL; #if DEBUG printf("[DEBUG] Spawning %s %s %s\n", argv[0], argv[1], argv[2]); #endif fflush(stdout); retval = posix_spawn(&target_pid, argv[0], NULL, &attr, argv, spawnedEnv); if(retval) { fprintf(stderr, "[ERROR] Could not spawn debuggee: %s\n", strerror(retval)); exit(1); } free(argv); } else { fflush(stdout); // execute with no arguments char *argv[] = {lpApplicationName, NULL}; #if DEBUG printf("[DEBUG] Spawning %s...\n", lpApplicationName); #endif retval = posix_spawnp(&target_pid, argv[0], NULL, &attr, argv, spawnedEnv); if (retval) { fprintf(stderr, "[ERROR] Could not spawn debuggee: %s\n", strerror(retval)); exit(1); } } // parent // initialize the mach port into the debugee retval = DebugActiveProcess(target_pid); // failed to attach if (retval == 0) { kill(target_pid, SIGCONT); // leave no zombies behind! kill(target_pid, SIGKILL); return 0; } // suspend all threads suspend_all_threads(target_pid); // and now we can continue the process, threads are still suspended! kill(target_pid, SIGCONT); fflush(stdout); lpProcessInformation->hProcess = target_pid; lpProcessInformation->dwProcessId = target_pid; return 1; }
int main(int argc, char *argv[]) { posix_spawnattr_t attrs; uint64_t percent, interval; int i, err, ret = 0; kern_return_t kr; mach_port_t task = mach_task_self(); mach_port_t child_task; char **child_args; pthread_t exception_thread; pthread_t timer_thread; pthread_t wait_thread; mach_msg_type_number_t maskCount = 1; exception_mask_t mask; exception_handler_t handler; exception_behavior_t behavior; thread_state_flavor_t flavor; pid_t child_pid; int test_case_id = -1; if (argc > 1) test_case_id = atoi(argv[1]); /* Initialize mutex and condition variable */ if ((err = pthread_mutex_init(&lock, NULL)) != 0) { fprintf(stderr,"pthread_mutex_init: %s\n", strerror(err)); exit(1); } if ((err = pthread_cond_init(&cv, NULL)) != 0) { fprintf(stderr, "pthread_cond_init: %s\n", strerror(err)); exit(1); } /* Allocate and initialize new exception port */ if ((kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &exc_port)) != KERN_SUCCESS) { fprintf(stderr, "mach_port_allocate: %s\n", mach_error_string(kr)); exit(1); } if ((kr = mach_port_insert_right(task, exc_port, exc_port, MACH_MSG_TYPE_MAKE_SEND)) != KERN_SUCCESS) { fprintf(stderr, "mach_port_allocate: %s\n", mach_error_string(kr)); exit(1); } /* Get Current exception ports */ if ((kr = task_get_exception_ports(task, EXC_MASK_RESOURCE, &mask, &maskCount, &handler, &behavior, &flavor)) != KERN_SUCCESS) { fprintf(stderr,"task_get_exception_ports: %s\n", mach_error_string(kr)); exit(1); } /* Create exception serving thread */ if ((err = pthread_create(&exception_thread, NULL, server_thread, 0)) != 0) { fprintf(stderr, "pthread_create server_thread: %s\n", strerror(err)); exit(1); } fprintf(stderr, "---------------System Configuration------------------------------------------\n"); fprintf(stderr, "System Kernel Version: "); system("uname -a"); fprintf(stderr, "System SDK Version: "); system("sw_vers"); for (i = 0; i < NUMTESTS; i++) { indiv_results[i] = -1; } /* Run Tests */ for(i=0; i<NUMTESTS; i++) { int j; if (test_case_id != -1 && test_case_id != i) continue; fprintf(stderr, "---------------Test [%d] Configuration------------------------------------------\n", i); fprintf(stderr, "Test Case ID: %d\n", i); fprintf(stderr, "Description: %s\n", test_description[i]); switch(i) { case 0: child_args = test_argv_0; break; case 1: child_args = test_argv_1; break; case 2: child_args = test_argv_2; break; case 3: child_args = test_argv_3; break; case 4: child_args = test_argv_4; break; case 5: child_args = test_argv_5; break; case 6: child_args = test_argv_6; break; default: fprintf(stderr, "no test argv found\n"); exit(1); } /* Test cases which do not need to run for certain platforms */ if (child_args == NULL) { fprintf(stderr, "Test case unimplemented for current platform.\n"); fprintf(stderr, "[PASSED]\n"); fprintf(stderr, "-------------------------------------------------------------------------------\n"); continue; } fprintf(stderr, "Helper args: "); for (j = 0; child_args[j] != NULL; j++) { fprintf(stderr, "%s ", child_args[j]); } fprintf(stderr, "\n"); /* Print Test Case Configuration */ fprintf(stderr, "Test Case expects EXC_RESOURCE?: %s\n", test_exception_code[i] ? "Yes":"No"); if (test_exception_code[i]) fprintf(stderr, "Expected EXC_RESOURCE code: 0x%llx\n", test_exception_code[i]); if (timeout_secs[i]) fprintf(stderr, "Timeout for Test Program: %d secs\n", timeout_secs[i]); if (exc_expected_at[i]) fprintf(stderr, "Exception Expected After: %d secs\n", exc_expected_at[i]); /* Initialize posix_spawn attributes */ posix_spawnattr_init(&attrs); if ((err = posix_spawnattr_setflags(&attrs, POSIX_SPAWN_SETEXEC)) != 0) { fprintf(stderr, "posix_spawnattr_setflags: %s\n", strerror(err)); exit(1); } /* Use high values so the system defaults take effect (spawn attrs are capped) */ percent = 100; interval = 10000; /* Enable CPU Monitor */ if ((err = posix_spawnattr_setcpumonitor(&attrs, percent, interval)) != 0) { fprintf(stderr, "posix_spawnattr_setcpumonitor: %s\n", strerror(err)); exit(1); } exception_code = 0; time_for_exc = -1; /* Set Exception Ports for Current Task */ if ((kr = task_set_exception_ports(task, EXC_MASK_RESOURCE, exc_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, flavor)) != KERN_SUCCESS) { fprintf(stderr, "task_set_exception_ports: %#x\n", kr); exit(1); } /* * Note the time at start of test. */ start_time = time(NULL); fprintf(stderr, "---------------Test [%d] Runtime------------------------------------------------\n", i); /* Fork and exec child */ if ((child_pid = fork()) == 0) { if ((err = posix_spawn(NULL, child_args[0], NULL, &attrs, &child_args[0], environ)) != 0) { fprintf(stderr, "posix_spawn: %s\n", strerror(err)); exit(1); } } /* Restore exception ports for parent */ if ((kr = task_set_exception_ports(task, EXC_MASK_RESOURCE, handler, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, flavor)) != KERN_SUCCESS) { fprintf(stderr, "task_set_exception_ports: %#x\n", kr); exit(1); } /* Create Timer Thread if timeout specified */ if (timeout_secs[i]) { if ((err = pthread_create(&timer_thread, NULL, timeout_thread, (void *)timeout_secs[i])) != 0) { fprintf(stderr, "pthread_create timeout_thread: %s\n", strerror(err)); test_status = 1; goto cleanup; } } /* Create waiting for child thread */ if ((err = pthread_create(&wait_thread, NULL, wait4_child_thread, NULL)) != 0) { fprintf(stderr, "pthread_create wait4_child_thread: %s\n", strerror(err)); test_status = 1; goto cleanup; } pthread_mutex_lock(&lock); pthread_cond_wait(&cv, &lock); pthread_mutex_unlock(&lock); kill(child_pid, SIGKILL); pthread_join(timer_thread, NULL); pthread_join(wait_thread, NULL); int test_case_status = 0; indiv_results[i] = 0; fprintf(stderr, "---------------Test [%d] Results------------------------------------------------\n", i); if (exception_code) fprintf(stderr, "EXC_RESOURCE Received with Code: 0x%llx\n", exception_code); else fprintf(stderr, "No EXC_RESOURCE Received!\n"); if (time_for_exc > 0) fprintf(stderr, "EXC_RESOURCE Received after %d secs\n", time_for_exc); if (!!exception_code != !!test_exception_code[i]) { test_status = 1; test_case_status = 1; indiv_results[i] = 1; } if (exception_code) { /* Validate test success by checking code and expected time */ if ((exception_code & test_exception_code[i]) != test_exception_code[i]) { fprintf(stderr, "Test Failure Reason: EXC_RESOURCE code did not match expected exception code!\n"); fprintf(stderr, "Expected: 0x%llx Found: 0x%llx\n", test_exception_code[i], exception_code); test_status = 1; test_case_status = 1; indiv_results[i] = 1; } if(exc_expected_at[i] && (time_for_exc < (exc_expected_at[i] - 10) || time_for_exc > (exc_expected_at[i] + 10))) { fprintf(stderr, "Test Failure Reason: Test case did not receive EXC_RESOURCE within expected time!\n"); test_status = 1; test_case_status = 1; indiv_results[i] = 1; } } if(test_case_status) fprintf(stderr, "[FAILED]\n"); else fprintf(stderr, "[PASSED]\n"); fprintf(stderr, "-------------------------------------------------------------------------------\n"); } if (test_case_id == -1) { fprintf(stderr, "--------------- Results Summary -----------------------------------------------\n"); for (i = 0; i < NUMTESTS; i++) { fprintf(stderr, "%2d: %s\n", i, (indiv_results[i] < 0) ? "N/A" : (indiv_results[i] == 0) ? "PASSED" : "FAILED"); } } cleanup: kill(child_pid, SIGKILL); exit(test_status); }
FProcHandle FLinuxPlatformProcess::CreateProc(const TCHAR* URL, const TCHAR* Parms, bool bLaunchDetached, bool bLaunchHidden, bool bLaunchReallyHidden, uint32* OutProcessID, int32 PriorityModifier, const TCHAR* OptionalWorkingDirectory, void* PipeWrite) { // @TODO bLaunchHidden bLaunchReallyHidden are not handled // We need an absolute path to executable FString ProcessPath = URL; if (*URL != TEXT('/')) { ProcessPath = FPaths::ConvertRelativePathToFull(ProcessPath); } if (!FPaths::FileExists(ProcessPath)) { return FProcHandle(); } FString Commandline = ProcessPath; Commandline += TEXT(" "); Commandline += Parms; UE_LOG(LogHAL, Verbose, TEXT("FLinuxPlatformProcess::CreateProc: '%s'"), *Commandline); TArray<FString> ArgvArray; int Argc = Commandline.ParseIntoArray(ArgvArray, TEXT(" "), true); char* Argv[PlatformProcessLimits::MaxArgvParameters + 1] = { NULL }; // last argument is NULL, hence +1 struct CleanupArgvOnExit { int Argc; char** Argv; // relying on it being long enough to hold Argc elements CleanupArgvOnExit( int InArgc, char *InArgv[] ) : Argc(InArgc) , Argv(InArgv) {} ~CleanupArgvOnExit() { for (int Idx = 0; Idx < Argc; ++Idx) { FMemory::Free(Argv[Idx]); } } } CleanupGuard(Argc, Argv); // make sure we do not lose arguments with spaces in them due to Commandline.ParseIntoArray breaking them apart above // @todo this code might need to be optimized somehow and integrated with main argument parser below it TArray<FString> NewArgvArray; if (Argc > 0) { if (Argc > PlatformProcessLimits::MaxArgvParameters) { UE_LOG(LogHAL, Warning, TEXT("FLinuxPlatformProcess::CreateProc: too many (%d) commandline arguments passed, will only pass %d"), Argc, PlatformProcessLimits::MaxArgvParameters); Argc = PlatformProcessLimits::MaxArgvParameters; } FString MultiPartArg; for (int32 Index = 0; Index < Argc; Index++) { if (MultiPartArg.IsEmpty()) { if ((ArgvArray[Index].StartsWith(TEXT("\"")) && !ArgvArray[Index].EndsWith(TEXT("\""))) // check for a starting quote but no ending quote, excludes quoted single arguments || (ArgvArray[Index].Contains(TEXT("=\"")) && !ArgvArray[Index].EndsWith(TEXT("\""))) // check for quote after =, but no ending quote, this gets arguments of the type -blah="string string string" || ArgvArray[Index].EndsWith(TEXT("=\""))) // check for ending quote after =, this gets arguments of the type -blah=" string string string " { MultiPartArg = ArgvArray[Index]; } else { if (ArgvArray[Index].Contains(TEXT("=\""))) { FString SingleArg = ArgvArray[Index]; SingleArg = SingleArg.Replace(TEXT("=\""), TEXT("=")); NewArgvArray.Add(SingleArg.TrimQuotes(NULL)); } else { NewArgvArray.Add(ArgvArray[Index].TrimQuotes(NULL)); } } } else { MultiPartArg += TEXT(" "); MultiPartArg += ArgvArray[Index]; if (ArgvArray[Index].EndsWith(TEXT("\""))) { if (MultiPartArg.StartsWith(TEXT("\""))) { NewArgvArray.Add(MultiPartArg.TrimQuotes(NULL)); } else { NewArgvArray.Add(MultiPartArg); } MultiPartArg.Empty(); } } } } // update Argc with the new argument count Argc = NewArgvArray.Num(); if (Argc > 0) // almost always, unless there's no program name { if (Argc > PlatformProcessLimits::MaxArgvParameters) { UE_LOG(LogHAL, Warning, TEXT("FLinuxPlatformProcess::CreateProc: too many (%d) commandline arguments passed, will only pass %d"), Argc, PlatformProcessLimits::MaxArgvParameters); Argc = PlatformProcessLimits::MaxArgvParameters; } for (int Idx = 0; Idx < Argc; ++Idx) { auto AnsiBuffer = StringCast<ANSICHAR>(*NewArgvArray[Idx]); const char* Ansi = AnsiBuffer.Get(); size_t AnsiSize = FCStringAnsi::Strlen(Ansi) + 1; check(AnsiSize); Argv[Idx] = reinterpret_cast< char* >( FMemory::Malloc(AnsiSize) ); check(Argv[Idx]); FCStringAnsi::Strncpy(Argv[Idx], Ansi, AnsiSize); } // last Argv should be NULL check(Argc <= PlatformProcessLimits::MaxArgvParameters + 1); Argv[Argc] = NULL; } extern char ** environ; // provided by libc pid_t ChildPid = -1; posix_spawn_file_actions_t FileActions; posix_spawn_file_actions_init(&FileActions); if (PipeWrite) { const FPipeHandle* PipeWriteHandle = reinterpret_cast< const FPipeHandle* >(PipeWrite); posix_spawn_file_actions_adddup2(&FileActions, PipeWriteHandle->GetHandle(), STDOUT_FILENO); } int PosixSpawnErrNo = posix_spawn(&ChildPid, TCHAR_TO_ANSI(*ProcessPath), &FileActions, nullptr, Argv, environ); posix_spawn_file_actions_destroy(&FileActions); if (PosixSpawnErrNo != 0) { UE_LOG(LogHAL, Fatal, TEXT("FLinuxPlatformProcess::CreateProc: posix_spawn() failed (%d, %s)"), PosixSpawnErrNo, ANSI_TO_TCHAR(strerror(PosixSpawnErrNo))); return FProcHandle(); // produce knowingly invalid handle if for some reason Fatal log (above) returns } // renice the child (subject to race condition). // Why this instead of posix_spawn_setschedparam()? // Because posix_spawnattr priority is unusable under Linux due to min/max priority range being [0;0] for the default scheduler if (PriorityModifier != 0) { errno = 0; int TheirCurrentPrio = getpriority(PRIO_PROCESS, ChildPid); if (errno != 0) { int ErrNo = errno; UE_LOG(LogHAL, Warning, TEXT("FLinuxPlatformProcess::CreateProc: could not get child's priority, errno=%d (%s)"), ErrNo, ANSI_TO_TCHAR(strerror(ErrNo)) ); // proceed anyway... TheirCurrentPrio = 0; } rlimit PrioLimits; int MaxPrio = 0; if (getrlimit(RLIMIT_NICE, &PrioLimits) == -1) { int ErrNo = errno; UE_LOG(LogHAL, Warning, TEXT("FLinuxPlatformProcess::CreateProc: could not get priority limits (RLIMIT_NICE), errno=%d (%s)"), ErrNo, ANSI_TO_TCHAR(strerror(ErrNo)) ); // proceed anyway... } else { MaxPrio = PrioLimits.rlim_cur; } int NewPrio = TheirCurrentPrio; if (PriorityModifier > 0) { // decrease the nice value - will perhaps fail, it's up to the user to run with proper permissions NewPrio -= 10; } else { NewPrio += 10; } // cap to [RLIMIT_NICE, 19] NewPrio = FMath::Min(19, NewPrio); NewPrio = FMath::Max(MaxPrio, NewPrio); // MaxPrio is actually the _lowest_ numerically priority if (setpriority(PRIO_PROCESS, ChildPid, NewPrio) == -1) { int ErrNo = errno; UE_LOG(LogHAL, Warning, TEXT("FLinuxPlatformProcess::CreateProc: could not change child's priority (nice value) from %d to %d, errno=%d (%s)"), TheirCurrentPrio, NewPrio, ErrNo, ANSI_TO_TCHAR(strerror(ErrNo)) ); } else { UE_LOG(LogHAL, Log, TEXT("Changed child's priority (nice value) to %d (change from %d)"), NewPrio, TheirCurrentPrio); } } else { UE_LOG(LogHAL, Log, TEXT("FLinuxPlatformProcess::CreateProc: spawned child %d"), ChildPid); } if (OutProcessID) { *OutProcessID = ChildPid; } // [RCL] 2015-03-11 @FIXME: is bLaunchDetached usable when determining whether we're in 'fire and forget' mode? This doesn't exactly match what bLaunchDetached is used for. return FProcHandle(new FProcState(ChildPid, bLaunchDetached)); }
RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags, PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser, const char *pszPassword, PRTPROCESS phProcess) { int rc; /* * Input validation */ AssertPtrReturn(pszExec, VERR_INVALID_POINTER); AssertReturn(*pszExec, VERR_INVALID_PARAMETER); AssertReturn(!(fFlags & ~RTPROC_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER); AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER); AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER); AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER); AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER); AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER); AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER); AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER); #if defined(RT_OS_OS2) if (fFlags & RTPROC_FLAGS_DETACHED) return VERR_PROC_DETACH_NOT_SUPPORTED; #endif /* * Get the file descriptors for the handles we've been passed. */ PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr }; int aStdFds[3] = { -1, -1, -1 }; for (int i = 0; i < 3; i++) { if (paHandles[i]) { AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER); switch (paHandles[i]->enmType) { case RTHANDLETYPE_FILE: aStdFds[i] = paHandles[i]->u.hFile != NIL_RTFILE ? (int)RTFileToNative(paHandles[i]->u.hFile) : -2 /* close it */; break; case RTHANDLETYPE_PIPE: aStdFds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE ? (int)RTPipeToNative(paHandles[i]->u.hPipe) : -2 /* close it */; break; case RTHANDLETYPE_SOCKET: aStdFds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET ? (int)RTSocketToNative(paHandles[i]->u.hSocket) : -2 /* close it */; break; default: AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER); } /** @todo check the close-on-execness of these handles? */ } } for (int i = 0; i < 3; i++) if (aStdFds[i] == i) aStdFds[i] = -1; for (int i = 0; i < 3; i++) AssertMsgReturn(aStdFds[i] < 0 || aStdFds[i] > i, ("%i := %i not possible because we're lazy\n", i, aStdFds[i]), VERR_NOT_SUPPORTED); /* * Resolve the user id if specified. */ uid_t uid = ~(uid_t)0; gid_t gid = ~(gid_t)0; if (pszAsUser) { rc = rtCheckCredentials(pszAsUser, pszPassword, &gid, &uid); if (RT_FAILURE(rc)) return rc; } /* * Create the child environment if either RTPROC_FLAGS_PROFILE or * RTPROC_FLAGS_ENV_CHANGE_RECORD are in effect. */ RTENV hEnvToUse = hEnv; if ( (fFlags & (RTPROC_FLAGS_ENV_CHANGE_RECORD | RTPROC_FLAGS_PROFILE)) && ( (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) || hEnv == RTENV_DEFAULT) ) { if (fFlags & RTPROC_FLAGS_PROFILE) rc = rtProcPosixCreateProfileEnv(&hEnvToUse, pszAsUser); else rc = RTEnvClone(&hEnvToUse, RTENV_DEFAULT); if (RT_SUCCESS(rc)) { if ((fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) && hEnv != RTENV_DEFAULT) rc = RTEnvApplyChanges(hEnvToUse, hEnv); if (RT_FAILURE(rc)) RTEnvDestroy(hEnvToUse); } if (RT_FAILURE(rc)) return rc; } /* * Check for execute access to the file. */ char szRealExec[RTPATH_MAX]; if (access(pszExec, X_OK)) { rc = errno; if ( !(fFlags & RTPROC_FLAGS_SEARCH_PATH) || rc != ENOENT || RTPathHavePath(pszExec) ) rc = RTErrConvertFromErrno(rc); else { /* search */ char *pszPath = RTEnvDupEx(hEnvToUse, "PATH"); rc = RTPathTraverseList(pszPath, ':', rtPathFindExec, (void *)pszExec, &szRealExec[0]); RTStrFree(pszPath); if (RT_SUCCESS(rc)) pszExec = szRealExec; else rc = rc == VERR_END_OF_STRING ? VERR_FILE_NOT_FOUND : rc; } if (RT_FAILURE(rc)) return rtProcPosixCreateReturn(rc, hEnvToUse, hEnv); } pid_t pid = -1; const char * const *papszEnv = RTEnvGetExecEnvP(hEnvToUse); AssertPtrReturn(papszEnv, rtProcPosixCreateReturn(VERR_INVALID_HANDLE, hEnvToUse, hEnv)); /* * Take care of detaching the process. * * HACK ALERT! Put the process into a new process group with pgid = pid * to make sure it differs from that of the parent process to ensure that * the IPRT waitpid call doesn't race anyone (read XPCOM) doing group wide * waits. setsid() includes the setpgid() functionality. * 2010-10-11 XPCOM no longer waits for anything, but it cannot hurt. */ #ifndef RT_OS_OS2 if (fFlags & RTPROC_FLAGS_DETACHED) { # ifdef RT_OS_SOLARIS int templateFd = -1; if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) { templateFd = rtSolarisContractPreFork(); if (templateFd == -1) return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv); } # endif /* RT_OS_SOLARIS */ pid = fork(); if (!pid) { # ifdef RT_OS_SOLARIS if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) rtSolarisContractPostForkChild(templateFd); # endif setsid(); /* see comment above */ pid = -1; /* Child falls through to the actual spawn code below. */ } else { # ifdef RT_OS_SOLARIS if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) rtSolarisContractPostForkParent(templateFd, pid); # endif if (pid > 0) { /* Must wait for the temporary process to avoid a zombie. */ int status = 0; pid_t pidChild = 0; /* Restart if we get interrupted. */ do { pidChild = waitpid(pid, &status, 0); } while ( pidChild == -1 && errno == EINTR); /* Assume that something wasn't found. No detailed info. */ if (status) return rtProcPosixCreateReturn(VERR_PROCESS_NOT_FOUND, hEnvToUse, hEnv); if (phProcess) *phProcess = 0; return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv); } return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv); } } #endif /* * Spawn the child. * * Any spawn code MUST not execute any atexit functions if it is for a * detached process. It would lead to running the atexit functions which * make only sense for the parent. libORBit e.g. gets confused by multiple * execution. Remember, there was only a fork() so far, and until exec() * is successfully run there is nothing which would prevent doing anything * silly with the (duplicated) file descriptors. */ #ifdef HAVE_POSIX_SPAWN /** @todo OS/2: implement DETACHED (BACKGROUND stuff), see VbglR3Daemonize. */ if ( uid == ~(uid_t)0 && gid == ~(gid_t)0) { /* Spawn attributes. */ posix_spawnattr_t Attr; rc = posix_spawnattr_init(&Attr); if (!rc) { /* Indicate that process group and signal mask are to be changed, and that the child should use default signal actions. */ rc = posix_spawnattr_setflags(&Attr, POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF); Assert(rc == 0); /* The child starts in its own process group. */ if (!rc) { rc = posix_spawnattr_setpgroup(&Attr, 0 /* pg == child pid */); Assert(rc == 0); } /* Unmask all signals. */ if (!rc) { sigset_t SigMask; sigemptyset(&SigMask); rc = posix_spawnattr_setsigmask(&Attr, &SigMask); Assert(rc == 0); } /* File changes. */ posix_spawn_file_actions_t FileActions; posix_spawn_file_actions_t *pFileActions = NULL; if ((aStdFds[0] != -1 || aStdFds[1] != -1 || aStdFds[2] != -1) && !rc) { rc = posix_spawn_file_actions_init(&FileActions); if (!rc) { pFileActions = &FileActions; for (int i = 0; i < 3; i++) { int fd = aStdFds[i]; if (fd == -2) rc = posix_spawn_file_actions_addclose(&FileActions, i); else if (fd >= 0 && fd != i) { rc = posix_spawn_file_actions_adddup2(&FileActions, fd, i); if (!rc) { for (int j = i + 1; j < 3; j++) if (aStdFds[j] == fd) { fd = -1; break; } if (fd >= 0) rc = posix_spawn_file_actions_addclose(&FileActions, fd); } } if (rc) break; } } } if (!rc) rc = posix_spawn(&pid, pszExec, pFileActions, &Attr, (char * const *)papszArgs, (char * const *)papszEnv); /* cleanup */ int rc2 = posix_spawnattr_destroy(&Attr); Assert(rc2 == 0); NOREF(rc2); if (pFileActions) { rc2 = posix_spawn_file_actions_destroy(pFileActions); Assert(rc2 == 0); } /* return on success.*/ if (!rc) { /* For a detached process this happens in the temp process, so * it's not worth doing anything as this process must exit. */ if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(0); if (phProcess) *phProcess = pid; return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv); } } /* For a detached process this happens in the temp process, so * it's not worth doing anything as this process must exit. */ if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(124); } else #endif { #ifdef RT_OS_SOLARIS int templateFd = -1; if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) { templateFd = rtSolarisContractPreFork(); if (templateFd == -1) return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv); } #endif /* RT_OS_SOLARIS */ pid = fork(); if (!pid) { #ifdef RT_OS_SOLARIS if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) rtSolarisContractPostForkChild(templateFd); #endif /* RT_OS_SOLARIS */ if (!(fFlags & RTPROC_FLAGS_DETACHED)) setpgid(0, 0); /* see comment above */ /* * Change group and user if requested. */ #if 1 /** @todo This needs more work, see suplib/hardening. */ if (pszAsUser) { int ret = initgroups(pszAsUser, gid); if (ret) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(126); else exit(126); } } if (gid != ~(gid_t)0) { if (setgid(gid)) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(126); else exit(126); } } if (uid != ~(uid_t)0) { if (setuid(uid)) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(126); else exit(126); } } #endif /* * Some final profile environment tweaks, if running as user. */ if ( (fFlags & RTPROC_FLAGS_PROFILE) && pszAsUser && ( (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) || hEnv == RTENV_DEFAULT) ) { rc = rtProcPosixAdjustProfileEnvFromChild(hEnvToUse, fFlags, hEnv); papszEnv = RTEnvGetExecEnvP(hEnvToUse); if (RT_FAILURE(rc) || !papszEnv) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(126); else exit(126); } } /* * Unset the signal mask. */ sigset_t SigMask; sigemptyset(&SigMask); rc = sigprocmask(SIG_SETMASK, &SigMask, NULL); Assert(rc == 0); /* * Apply changes to the standard file descriptor and stuff. */ for (int i = 0; i < 3; i++) { int fd = aStdFds[i]; if (fd == -2) close(i); else if (fd >= 0) { int rc2 = dup2(fd, i); if (rc2 != i) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(125); else exit(125); } for (int j = i + 1; j < 3; j++) if (aStdFds[j] == fd) { fd = -1; break; } if (fd >= 0) close(fd); } } /* * Finally, execute the requested program. */ rc = execve(pszExec, (char * const *)papszArgs, (char * const *)papszEnv); if (errno == ENOEXEC) { /* This can happen when trying to start a shell script without the magic #!/bin/sh */ RTAssertMsg2Weak("Cannot execute this binary format!\n"); } else RTAssertMsg2Weak("execve returns %d errno=%d\n", rc, errno); RTAssertReleasePanic(); if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(127); else exit(127); } #ifdef RT_OS_SOLARIS if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) rtSolarisContractPostForkParent(templateFd, pid); #endif /* RT_OS_SOLARIS */ if (pid > 0) { /* For a detached process this happens in the temp process, so * it's not worth doing anything as this process must exit. */ if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(0); if (phProcess) *phProcess = pid; return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv); } /* For a detached process this happens in the temp process, so * it's not worth doing anything as this process must exit. */ if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(124); return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv); } return rtProcPosixCreateReturn(VERR_NOT_IMPLEMENTED, hEnvToUse, hEnv); }
static int do_test (void) { int fd[2]; if (pipe (fd) != 0) { puts ("pipe failed"); exit (1); } /* Not interested in knowing when the pipe is closed. */ if (sigignore (SIGPIPE) != 0) { puts ("sigignore failed"); exit (1); } posix_spawn_file_actions_t a; if (posix_spawn_file_actions_init (&a) != 0) { puts ("spawn_file_actions_init failed"); exit (1); } if (posix_spawn_file_actions_adddup2 (&a, fd[1], STDOUT_FILENO) != 0) { puts ("spawn_file_actions_adddup2 failed"); exit (1); } if (posix_spawn_file_actions_addclose (&a, fd[0]) != 0) { puts ("spawn_file_actions_addclose"); exit (1); } pthread_t th; if (pthread_create (&th, NULL, tf, (void *) pthread_self ()) != 0) { puts ("create failed"); exit (1); } pid_t pid; char *argv[] = { (char *) _PATH_BSHELL, (char *) "-c", (char *) "echo $$", NULL }; if (posix_spawn (&pid, _PATH_BSHELL, &a, NULL, argv, NULL) != 0) { puts ("spawn failed"); exit (1); } close (fd[1]); char buf[200]; ssize_t n; bool seen_pid = false; while (TEMP_FAILURE_RETRY ((n = read (fd[0], buf, sizeof (buf)))) > 0) { /* We only expect to read the PID. */ char *endp; long int rpid = strtol (buf, &endp, 10); if (*endp != '\n') { printf ("didn't parse whole line: \"%s\"\n", buf); exit (1); } if (endp == buf) { puts ("read empty line"); exit (1); } if (rpid != pid) { printf ("found \"%s\", expected PID %ld\n", buf, (long int) pid); exit (1); } if (seen_pid) { puts ("found more than one PID line"); exit (1); } seen_pid = true; } close (fd[0]); int status; int err = waitpid (pid, &status, 0); if (err != pid) { puts ("waitpid failed"); exit (1); } if (!seen_pid) { puts ("didn't get PID"); exit (1); } puts ("read correct PID"); return 0; }
static PRProcess * ForkAndExec( const char *path, char *const *argv, char *const *envp, const PRProcessAttr *attr) { PRProcess *process; int nEnv, idx; char *const *childEnvp; char **newEnvp = NULL; int flags; process = PR_NEW(PRProcess); if (!process) { PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); return NULL; } childEnvp = envp; if (attr && attr->fdInheritBuffer) { PRBool found = PR_FALSE; if (NULL == childEnvp) { #ifdef DARWIN childEnvp = *(_NSGetEnviron()); #else childEnvp = environ; #endif } for (nEnv = 0; childEnvp[nEnv]; nEnv++) { } newEnvp = (char **) PR_MALLOC((nEnv + 2) * sizeof(char *)); if (NULL == newEnvp) { PR_DELETE(process); PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); return NULL; } for (idx = 0; idx < nEnv; idx++) { newEnvp[idx] = childEnvp[idx]; if (!found && !strncmp(newEnvp[idx], "NSPR_INHERIT_FDS=", 17)) { newEnvp[idx] = attr->fdInheritBuffer; found = PR_TRUE; } } if (!found) { newEnvp[idx++] = attr->fdInheritBuffer; } newEnvp[idx] = NULL; childEnvp = newEnvp; } #ifdef AIX process->md.pid = (*pr_wp.forkptr)(); #elif defined(NTO) || defined(SYMBIAN) /* * fork() & exec() does not work in a multithreaded process. * Use spawn() instead. */ { int fd_map[3] = { 0, 1, 2 }; if (attr) { if (attr->stdinFd && attr->stdinFd->secret->md.osfd != 0) { fd_map[0] = dup(attr->stdinFd->secret->md.osfd); flags = fcntl(fd_map[0], F_GETFL, 0); if (flags & O_NONBLOCK) fcntl(fd_map[0], F_SETFL, flags & ~O_NONBLOCK); } if (attr->stdoutFd && attr->stdoutFd->secret->md.osfd != 1) { fd_map[1] = dup(attr->stdoutFd->secret->md.osfd); flags = fcntl(fd_map[1], F_GETFL, 0); if (flags & O_NONBLOCK) fcntl(fd_map[1], F_SETFL, flags & ~O_NONBLOCK); } if (attr->stderrFd && attr->stderrFd->secret->md.osfd != 2) { fd_map[2] = dup(attr->stderrFd->secret->md.osfd); flags = fcntl(fd_map[2], F_GETFL, 0); if (flags & O_NONBLOCK) fcntl(fd_map[2], F_SETFL, flags & ~O_NONBLOCK); } PR_ASSERT(attr->currentDirectory == NULL); /* not implemented */ } #ifdef SYMBIAN /* In Symbian OS, we use posix_spawn instead of fork() and exec() */ posix_spawn(&(process->md.pid), path, NULL, NULL, argv, childEnvp); #else process->md.pid = spawn(path, 3, fd_map, NULL, argv, childEnvp); #endif if (fd_map[0] != 0) close(fd_map[0]); if (fd_map[1] != 1) close(fd_map[1]); if (fd_map[2] != 2) close(fd_map[2]); } #else process->md.pid = fork(); #endif if ((pid_t) -1 == process->md.pid) { PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, errno); PR_DELETE(process); if (newEnvp) { PR_DELETE(newEnvp); } return NULL; } else if (0 == process->md.pid) { /* the child process */ /* * If the child process needs to exit, it must call _exit(). * Do not call exit(), because exit() will flush and close * the standard I/O file descriptors, and hence corrupt * the parent process's standard I/O data structures. */ #if !defined(NTO) && !defined(SYMBIAN) if (attr) { /* the osfd's to redirect stdin, stdout, and stderr to */ int in_osfd = -1, out_osfd = -1, err_osfd = -1; if (attr->stdinFd && attr->stdinFd->secret->md.osfd != 0) { in_osfd = attr->stdinFd->secret->md.osfd; if (dup2(in_osfd, 0) != 0) { _exit(1); /* failed */ } flags = fcntl(0, F_GETFL, 0); if (flags & O_NONBLOCK) { fcntl(0, F_SETFL, flags & ~O_NONBLOCK); } } if (attr->stdoutFd && attr->stdoutFd->secret->md.osfd != 1) { out_osfd = attr->stdoutFd->secret->md.osfd; if (dup2(out_osfd, 1) != 1) { _exit(1); /* failed */ } flags = fcntl(1, F_GETFL, 0); if (flags & O_NONBLOCK) { fcntl(1, F_SETFL, flags & ~O_NONBLOCK); } } if (attr->stderrFd && attr->stderrFd->secret->md.osfd != 2) { err_osfd = attr->stderrFd->secret->md.osfd; if (dup2(err_osfd, 2) != 2) { _exit(1); /* failed */ } flags = fcntl(2, F_GETFL, 0); if (flags & O_NONBLOCK) { fcntl(2, F_SETFL, flags & ~O_NONBLOCK); } } if (in_osfd != -1) { close(in_osfd); } if (out_osfd != -1 && out_osfd != in_osfd) { close(out_osfd); } if (err_osfd != -1 && err_osfd != in_osfd && err_osfd != out_osfd) { close(err_osfd); } if (attr->currentDirectory) { if (chdir(attr->currentDirectory) < 0) { _exit(1); /* failed */ } } } if (childEnvp) { (void)execve(path, argv, childEnvp); } else { /* Inherit the environment of the parent. */ (void)execv(path, argv); } /* Whoops! It returned. That's a bad sign. */ _exit(1); #endif /* !NTO */ } if (newEnvp) { PR_DELETE(newEnvp); } #if defined(_PR_NATIVE_THREADS) PR_Lock(pr_wp.ml); if (0 == pr_wp.numProcs++) { PR_NotifyCondVar(pr_wp.cv); } PR_Unlock(pr_wp.ml); #endif return process; }
/// Executes an external command. /// \return true on success, false if there is an exec error. static bool exec_external_command(job_t *j, process_t *p, const io_chain_t &proc_io_chain) { assert(p->type == EXTERNAL && "Process is not external"); // Get argv and envv before we fork. null_terminated_array_t<char> argv_array; convert_wide_array_to_narrow(p->get_argv_array(), &argv_array); // Ensure that stdin is blocking before we hand it off (see issue #176). It's a // little strange that we only do this with stdin and not with stdout or stderr. // However in practice, setting or clearing O_NONBLOCK on stdin also sets it for the // other two fds, presumably because they refer to the same underlying file // (/dev/tty?). make_fd_blocking(STDIN_FILENO); const char *const *argv = argv_array.get(); const char *const *envv = env_export_arr(); std::string actual_cmd_str = wcs2string(p->actual_cmd); const char *actual_cmd = actual_cmd_str.c_str(); const wchar_t *file = reader_current_filename(); #if FISH_USE_POSIX_SPAWN // Prefer to use posix_spawn, since it's faster on some systems like OS X. bool use_posix_spawn = g_use_posix_spawn && can_use_posix_spawn_for_job(j, p); if (use_posix_spawn) { g_fork_count++; // spawn counts as a fork+exec // Create posix spawn attributes and actions. pid_t pid = 0; posix_spawnattr_t attr = posix_spawnattr_t(); posix_spawn_file_actions_t actions = posix_spawn_file_actions_t(); bool made_it = fork_actions_make_spawn_properties(&attr, &actions, j, p, proc_io_chain); if (made_it) { // We successfully made the attributes and actions; actually call // posix_spawn. int spawn_ret = posix_spawn(&pid, actual_cmd, &actions, &attr, const_cast<char *const *>(argv), const_cast<char *const *>(envv)); // This usleep can be used to test for various race conditions // (https://github.com/fish-shell/fish-shell/issues/360). // usleep(10000); if (spawn_ret != 0) { safe_report_exec_error(spawn_ret, actual_cmd, argv, envv); // Make sure our pid isn't set. pid = 0; } // Clean up our actions. posix_spawn_file_actions_destroy(&actions); posix_spawnattr_destroy(&attr); } // A 0 pid means we failed to posix_spawn. Since we have no pid, we'll never get // told when it's exited, so we have to mark the process as failed. debug(4, L"Fork #%d, pid %d: spawn external command '%s' from '%ls'", g_fork_count, pid, actual_cmd, file ? file : L"<no file>"); if (pid == 0) { job_mark_process_as_failed(j, p); return false; } // these are all things do_fork() takes care of normally (for forked processes): p->pid = pid; on_process_created(j, p->pid); // We explicitly don't call set_child_group() for spawned processes because that // a) isn't necessary, and b) causes issues like fish-shell/fish-shell#4715 #if defined(__GLIBC__) // Unfortunately, using posix_spawn() is not the panacea it would appear to be, // glibc has a penchant for using fork() instead of vfork() when posix_spawn() is // called, meaning that atomicity is not guaranteed and we can get here before the // child group has been set. See discussion here: // https://github.com/Microsoft/WSL/issues/2997 And confirmation that this persists // past glibc 2.24+ here: https://github.com/fish-shell/fish-shell/issues/4715 if (j->get_flag(job_flag_t::JOB_CONTROL) && getpgid(p->pid) != j->pgid) { set_child_group(j, p->pid); } #else // In do_fork, the pid of the child process is used as the group leader if j->pgid // invalid, posix_spawn assigned the new group a pgid equal to its own id if // j->pgid was invalid, so this is what we do instead of calling set_child_group if (j->pgid == INVALID_PID) { j->pgid = pid; } #endif maybe_assign_terminal(j); } else #endif { if (!fork_child_for_process(j, p, proc_io_chain, false, "external command", [&] { safe_launch_process(p, actual_cmd, argv, envv); })) { return false; } } return true; }