TEST(spawn, posix_spawnattr_setpgroup_posix_spawnattr_getpgroup) { posix_spawnattr_t sa; ASSERT_EQ(0, posix_spawnattr_init(&sa)); ASSERT_EQ(0, posix_spawnattr_setpgroup(&sa, 123)); pid_t g; ASSERT_EQ(0, posix_spawnattr_getpgroup(&sa, &g)); ASSERT_EQ(123, g); ASSERT_EQ(0, posix_spawnattr_destroy(&sa)); }
static int l_posix_spawnattr_setpgroup(lua_State *L) { int r; posix_spawnattr_t *attr = luaL_checkudata(L, 1, "posix_spawnattr_t"); pid_t pgroup = luaL_checkinteger(L, 2); if (0 != (r = posix_spawnattr_setpgroup(attr, pgroup))) { lua_pushnil(L); lua_pushstring(L, strerror(r)); lua_pushinteger(L, r); return 3; } lua_pushboolean(L, 1); return 1; }
TEST(spawn, posix_spawn_POSIX_SPAWN_SETPGROUP_set) { pid_t parent_pgrp = getpgrp(); posix_spawnattr_t sa; ASSERT_EQ(0, posix_spawnattr_init(&sa)); ASSERT_EQ(0, posix_spawnattr_setpgroup(&sa, 0)); ASSERT_EQ(0, posix_spawnattr_setflags(&sa, POSIX_SPAWN_SETPGROUP)); ProcStat ps = {}; GetChildStat(&sa, &ps); ASSERT_NE(parent_pgrp, ps.pgrp); // Setting pgid 0 means "the same as the caller's pid". ASSERT_EQ(ps.pid, ps.pgrp); ASSERT_EQ(0, posix_spawnattr_destroy(&sa)); }
int main() { int ret_err = 0; pid_t pid_child; char buf_err[64]; char *argv_child[] = { "forkexec_child", NULL }; printf("Parent[%d]: Start\n", getpid()); posix_spawnattr_t posix_attr; if ((ret_err = posix_spawnattr_init(&posix_attr)) != 0) { strerror_r(ret_err, buf_err, sizeof(buf_err)); fprintf(stderr, "Fail: attr_init: %s\n", buf_err); exit(EXIT_FAILURE); } short posix_flags = POSIX_SPAWN_SETPGROUP; if ((ret_err = posix_spawnattr_setflags(&posix_attr, posix_flags)) != 0) { strerror_r(ret_err, buf_err, sizeof(buf_err)); fprintf(stderr, "Fail: attr_setflags: %s\n", buf_err); exit(EXIT_FAILURE); } // set pgid pid_t pid_pgid = 0; /* child process shall become process group reader */ if ((ret_err = posix_spawnattr_setpgroup(&posix_attr, pid_pgid)) != 0) { strerror_r(ret_err, buf_err, sizeof(buf_err)); fprintf(stderr, "Fail: attr_setpgroup: %s\n", buf_err); exit(EXIT_FAILURE); } ret_err = posix_spawn( &pid_child, argv_child[0], NULL, &posix_attr, /* attribute */ argv_child, NULL); if ((ret_err = posix_spawnattr_destroy(&posix_attr)) != 0) { strerror_r(ret_err, buf_err, sizeof(buf_err)); fprintf(stderr, "Fail: attr_destroy: %s\n", buf_err); exit(EXIT_FAILURE); } printf("Parent[%d]: Wait for child(%d)\n", getpid(), (int)pid_child); (void)wait(NULL); /* wait for child */ printf("Parent[%d]: Exit\n", getpid()); return 0; }
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; }
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; }
bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, posix_spawn_file_actions_t *actions, job_t *j, process_t *p, const io_chain_t &io_chain) { /* Initialize the output */ if (posix_spawnattr_init(attr) != 0) { return false; } if (posix_spawn_file_actions_init(actions) != 0) { posix_spawnattr_destroy(attr); return false; } bool should_set_parent_group_id = false; int desired_parent_group_id = 0; if (job_get_flag(j, JOB_CONTROL)) { should_set_parent_group_id = true; // PCA: I'm quite fuzzy on process groups, // but I believe that the default value of 0 // means that the process becomes its own // group leader, which is what set_child_group did // in this case. So we want this to be 0 if j->pgid is 0. desired_parent_group_id = j->pgid; } /* Set the handling for job control signals back to the default. */ bool reset_signal_handlers = true; /* Remove all signal blocks */ bool reset_sigmask = true; /* Set our flags */ short flags = 0; if (reset_signal_handlers) flags |= POSIX_SPAWN_SETSIGDEF; if (reset_sigmask) flags |= POSIX_SPAWN_SETSIGMASK; if (should_set_parent_group_id) flags |= POSIX_SPAWN_SETPGROUP; int err = 0; if (! err) err = posix_spawnattr_setflags(attr, flags); if (! err && should_set_parent_group_id) err = posix_spawnattr_setpgroup(attr, desired_parent_group_id); /* Everybody gets default handlers */ if (! err && reset_signal_handlers) { sigset_t sigdefault; get_signals_with_handlers(&sigdefault); err = posix_spawnattr_setsigdefault(attr, &sigdefault); } /* No signals blocked */ sigset_t sigmask; sigemptyset(&sigmask); if (! err && reset_sigmask) err = posix_spawnattr_setsigmask(attr, &sigmask); for (size_t idx = 0; idx < io_chain.size(); idx++) { const shared_ptr<const io_data_t> io = io_chain.at(idx); if (io->io_mode == IO_FD) { CAST_INIT(const io_fd_t *, io_fd, io.get()); if (io->fd == io_fd->old_fd) continue; } switch (io->io_mode) { case IO_CLOSE: { if (! err) err = posix_spawn_file_actions_addclose(actions, io->fd); break; } case IO_FILE: { CAST_INIT(const io_file_t *, io_file, io.get()); if (! err) err = posix_spawn_file_actions_addopen(actions, io->fd, io_file->filename_cstr, io_file->flags /* mode */, OPEN_MASK); break; } case IO_FD: { CAST_INIT(const io_fd_t *, io_fd, io.get()); if (! err) err = posix_spawn_file_actions_adddup2(actions, io_fd->old_fd /* from */, io->fd /* to */); break; } case IO_BUFFER: case IO_PIPE: { CAST_INIT(const io_pipe_t *, io_pipe, io.get()); unsigned int write_pipe_idx = (io_pipe->is_input ? 0 : 1); int from_fd = io_pipe->pipe_fd[write_pipe_idx]; int to_fd = io->fd; if (! err) err = posix_spawn_file_actions_adddup2(actions, from_fd, to_fd); if (write_pipe_idx > 0) { if (! err) err = posix_spawn_file_actions_addclose(actions, io_pipe->pipe_fd[0]); if (! err) err = posix_spawn_file_actions_addclose(actions, io_pipe->pipe_fd[1]); } else { if (! err) err = posix_spawn_file_actions_addclose(actions, io_pipe->pipe_fd[0]); } break; } } }
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); }
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(void) { posix_spawnattr_setpgroup(0, 0); return 0; }
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 }
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; }
// Exits with status 0 if posix_spawn works, non-zero otherwise int main(int argc, char **argv) { #if __CYGWIN__ // Not only does it not work it causes the meson configure step to // take tens or minutes to complete and results in a huge cascade // of child processes. printf("Cygwin doesn't have a working posix_spawn()\n"); exit(30); #else // __CYGWIN__ char *s; pid_t pid; posix_spawnattr_t attr; int n; int status; char *cmd[3]; char tmp[1024]; if (argc > 1) { exit(signal(SIGHUP, SIG_DFL) != SIG_IGN); } signal(SIGHUP, SIG_IGN); if (posix_spawnattr_init(&attr)) { printf("posix_spawnattr_init() FAILED\n"); exit(2); } if (posix_spawnattr_setpgroup(&attr, 0)) { printf("posix_spawnattr_setpgroup() FAILED\n"); exit(3); } if (posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETPGROUP)) { printf("posix_spawnattr_setflags() FAILED\n"); exit(4); } /* first try an a.out and verify that SIGHUP is ignored */ cmd[0] = argv[0]; cmd[1] = "test"; cmd[2] = 0; if (posix_spawn(&pid, cmd[0], 0, &attr, cmd, 0)) { printf("posix_spawn() FAILED\n"); exit(5); } status = 1; if (wait(&status) < 0) { printf("wait() FAILED\n"); exit(6); } if (status != 0) { printf("SIGHUP ignored in parent not ignored in child\n"); exit(7); } /* must return exec-type errors or its useless to us *unless* there is no [v]fork() */ n = strlen(cmd[0]); if (n >= (sizeof(tmp) - 3)) { printf("test executable path too long\n"); exit(8); } strcpy(tmp, cmd[0]); tmp[n] = '.'; tmp[n + 1] = 's'; tmp[n + 2] = 'h'; tmp[n + 3] = 0; if ((n = open(tmp, O_CREAT | O_WRONLY, S_IRWXU | S_IRWXG | S_IRWXO)) < 0 || chmod(tmp, S_IRWXU | S_IRWXG | S_IRWXO) < 0 || write(n, "exit 99\n", 8) != 8 || close(n) < 0) { printf("test script create FAILED\n"); exit(9); } cmd[0] = tmp; pid = -1; if (posix_spawn(&pid, cmd[0], 0, &attr, cmd, 0)) { printf("ENOEXEC produces posix_spawn() error (BEST)\n"); exit(0); } else if (pid == -1) { printf("ENOEXEC returns pid == -1\n"); exit(10); } else if (wait(&status) != pid) { printf("ENOEXEC produces no child process\n"); exit(11); } else if (!WIFEXITED(status)) { printf("ENOEXEC produces signal exit\n"); exit(12); } else { status = WEXITSTATUS(status); if (status == 127) { printf("ENOEXEC produces exit status 127 (GOOD)\n"); exit(1); } else if (status == 99) { printf("ENOEXEC invokes sh\n"); exit(13); } else if (status == 0) { printf("ENOEXEC reports no error\n"); exit(14); } else { printf("ENOEXEC produces non-zero exit status\n"); exit(15); } } exit(20); #endif // __CYGWIN__ }