#pragma prototyped
#include <ast.h>
#if _lib_spawnveg
NoN(spawnveg)
#else
#if _lib_posix_spawn > 1
#include <spawn.h>
#include <error.h>
#include <wait.h>
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;
}
#else
#if _lib_spawn_mode
#include <process.h>
#ifndef P_NOWAIT
#define P_NOWAIT _P_NOWAIT
#endif
#ifndef P_DETACH
#define P_DETACH _P_DETACH
#endif
pid_t
spawnveg(const char* path, char* const argv[], char* const envv[], pid_t pgid)
{
return spawnve(pgid ? P_DETACH : P_NOWAIT, path, argv, envv ? envv : environ);
}
#else
#if _lib_spawn && _hdr_spawn && _mem_pgroup_inheritance
#include <spawn.h>
pid_t
spawnveg(const char* path, char* const argv[], char* const envv[], pid_t pgid)
{
struct inheritance inherit;
inherit.flags = 0;
if (pgid)
{
inherit.flags |= SPAWN_SETGROUP;
inherit.pgroup = (pgid > 1) ? pgid : SPAWN_NEWPGROUP;
}
return spawn(path, 0, (int*)0, &inherit, (const char**)argv, (const char**)envv);
}
#else
#include <error.h>
#include <wait.h>
#include <sig.h>
#include <ast_vfork.h>
#ifndef ENOSYS
#define ENOSYS EINVAL
#endif
#if _lib_spawnve && _hdr_process
#include <process.h>
#if defined(P_NOWAIT) || defined(_P_NOWAIT)
#undef _lib_spawnve
#endif
#endif
#if !_lib_vfork
#undef _real_vfork
#endif
pid_t
spawnveg(const char* path, char* const argv[], char* const envv[], pid_t pgid)
{
#if _lib_fork || _lib_vfork
int n;
int m;
pid_t pid;
pid_t rid;
#if _real_vfork
volatile int exec_errno;
volatile int* volatile exec_errno_ptr;
#else
int err[2];
#endif
#endif
#if 0
if (access(path, X_OK))
return -1;
#endif
if (!envv)
envv = environ;
#if _lib_spawnve
#if _lib_fork || _lib_vfork
if (!pgid)
#endif
return spawnve(path, argv, envv);
#endif
#if _lib_fork || _lib_vfork
n = errno;
#if _real_vfork
exec_errno = 0;
exec_errno_ptr = &exec_errno;
#else
if (pipe(err) < 0)
err[0] = -1;
else
{
fcntl(err[0], F_SETFD, FD_CLOEXEC);
fcntl(err[1], F_SETFD, FD_CLOEXEC);
}
#endif
sigcritical(1);
#if _lib_vfork
pid = vfork();
#else
pid = fork();
#endif
sigcritical(0);
if (pid == -1)
n = errno;
else if (!pid)
{
if (pgid < 0)
setsid();
else if (pgid > 0)
{
if (pgid == 1)
pgid = 0;
if (setpgid(0, pgid) < 0 && pgid && errno == EPERM)
setpgid(0, 0);
}
execve(path, argv, envv);
#if _real_vfork
*exec_errno_ptr = errno;
#else
if (err[0] != -1)
{
n = errno;
write(err[1], &n, sizeof(n));
}
#endif
_exit(errno == ENOENT ? EXIT_NOTFOUND : EXIT_NOEXEC);
}
rid = pid;
#if _real_vfork
if (pid != -1 && (m = *exec_errno_ptr))
{
while (waitpid(pid, NiL, 0) == -1 && errno == EINTR);
rid = pid = -1;
n = m;
}
#else
if (err[0] != -1)
{
close(err[1]);
if (pid != -1 && read(err[0], &m, sizeof(m)) == sizeof(m) && m)
{
while (waitpid(pid, NiL, 0) == -1 && errno == EINTR);
rid = pid = -1;
n = m;
}
close(err[0]);
}
#endif
if (pid != -1 && pgid > 0)
{
if (pgid == 1)
pgid = pid;
if (setpgid(pid, pgid) < 0 && pid != pgid && errno == EPERM)
setpgid(pid, pid);
}
errno = n;
return rid;
#else
errno = ENOSYS;
return -1;
#endif
}
#endif
#endif
#endif
#endif