#include "lint.h"
#include "thr_uberdata.h"
#include <sys/sysmacros.h>
#include <limits.h>
#include <spawn.h>
#include <stdbool.h>
#include <sys/spawn_impl.h>
#include <paths.h>
extern const char **_environ;
extern int libc__xpg4;
extern pid_t __spawn(const char *, const void *, uint32_t, const void *,
uint32_t);
static inline bool
spawn_addlen(size_t *lenp, size_t sz)
{
if (sz > ARG_MAX - *lenp)
return (false);
*lenp += sz;
return (true);
}
static bool
spawn_marshal_argenv(spawn_args_t *sargs, char *const *argp, char *const *envp,
size_t *lenp)
{
struct vec {
char *const *vec;
uint32_t *off;
uint32_t *cnt;
} vecs[] = {
{ .vec = argp },
{ .vec = envp }
};
if (sargs != NULL) {
vecs[0].off = &sargs->sa_arg_off;
vecs[0].cnt = &sargs->sa_arg_cnt;
vecs[1].off = &sargs->sa_env_off;
vecs[1].cnt = &sargs->sa_env_cnt;
}
for (size_t i = 0; i < ARRAY_SIZE(vecs); i++) {
struct vec *vec = &vecs[i];
uint32_t cnt = 0;
char *const *p;
if (vec->off != NULL)
*vec->off = (uint32_t)*lenp;
for (p = vec->vec; p != NULL && *p != NULL; p++) {
size_t sz = strlen(*p) + 1;
if (sargs != NULL) {
if (*lenp + sz > sargs->sa_datalen)
return (false);
(void) memcpy(&sargs->sa_data[*lenp], *p, sz);
}
if (!spawn_addlen(lenp, sz))
return (false);
cnt++;
}
if (vec->cnt != NULL)
*vec->cnt = cnt;
}
return (true);
}
static bool
spawn_marshal_param(spawn_param_t *sparam, const spawn_attr_t *sa,
const kspawn_sched_t *ksched, const file_attr_t *fa, const char *shell,
const char *pathstr, size_t *lenp)
{
if (sa != NULL) {
size_t sz = sizeof (*sa);
if (sparam != NULL) {
if (*lenp + sz > sparam->sp_datalen)
return (false);
sparam->sp_attr_off = (uint32_t)*lenp;
sparam->sp_attr_len = (uint32_t)sz;
(void) memcpy(&sparam->sp_data[*lenp], sa, sz);
}
if (!spawn_addlen(lenp, sz))
return (false);
}
if (ksched != NULL) {
size_t sz = sizeof (*ksched);
if (sparam != NULL) {
if (*lenp + sz > sparam->sp_datalen)
return (false);
sparam->sp_sched_off = (uint32_t)*lenp;
sparam->sp_sched_len = (uint32_t)sz;
(void) memcpy(&sparam->sp_data[*lenp], ksched, sz);
}
if (!spawn_addlen(lenp, sz))
return (false);
}
if (fa != NULL) {
const file_attr_t *froot = fa;
uint32_t cnt = 0;
if (sparam != NULL)
sparam->sp_fattr_off = (uint32_t)*lenp;
do {
size_t sz = P2ROUNDUP(
sizeof (kfile_attr_t) + fa->fa_pathsize,
sizeof (uint32_t));
if (sparam != NULL) {
kfile_attr_t *ka;
if (*lenp + sz > sparam->sp_datalen)
return (false);
ka = (kfile_attr_t *)&sparam->sp_data[*lenp];
ka->kfa_len = sz;
ka->kfa_type = fa->fa_type;
ka->kfa_pathsize = fa->fa_pathsize;
switch (fa->fa_type) {
case FA_OPEN:
ka->kfa_filedes = fa->fa_filedes;
ka->kfa_oflag = fa->fa_oflag;
ka->kfa_mode = fa->fa_mode;
case FA_CHDIR:
(void) memcpy(ka->kfa_path,
fa->fa_path, fa->fa_pathsize);
break;
case FA_DUP2:
ka->kfa_newfiledes = fa->fa_newfiledes;
case FA_CLOSE:
case FA_CLOSEFROM:
case FA_FCHDIR:
ka->kfa_filedes = fa->fa_filedes;
break;
}
}
if (!spawn_addlen(lenp, sz))
return (false);
cnt++;
} while ((fa = fa->fa_next) != froot);
if (sparam != NULL)
sparam->sp_fattr_cnt = cnt;
}
if (shell != NULL) {
size_t sz = strlen(shell) + 1;
if (sparam != NULL) {
if (*lenp + sz > sparam->sp_datalen)
return (false);
sparam->sp_shell_off = (uint32_t)*lenp;
sparam->sp_shell_len = (uint32_t)sz;
(void) memcpy(&sparam->sp_data[*lenp], shell, sz);
}
if (!spawn_addlen(lenp, sz))
return (false);
}
if (pathstr != NULL) {
size_t sz = strlen(pathstr) + 1;
if (sparam != NULL) {
if (*lenp + sz > sparam->sp_datalen)
return (false);
sparam->sp_path_off = (uint32_t)*lenp;
sparam->sp_path_len = (uint32_t)sz;
(void) memcpy(&sparam->sp_data[*lenp], pathstr, sz);
}
if (!spawn_addlen(lenp, sz))
return (false);
}
return (true);
}
static int
posix_spawn_common(pid_t *pidp, const char *path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp, char *const *argp, char *const *envp,
const char *shell, const char *pathstr)
{
spawn_attr_t *sa = (attrp != NULL) ? attrp->__spawn_attrp : NULL;
file_attr_t *fa = (file_actions != NULL) ? file_actions->__file_attrp :
NULL;
const kspawn_sched_t *ksched = NULL;
kspawn_sched_t ks;
spawn_args_t *sargs = NULL;
spawn_param_t *sparam = NULL;
pid_t pid;
size_t datalen, len;
int ret = 0;
if (attrp != NULL && sa == NULL)
return (EINVAL);
if (sa != NULL && (sa->sa_psflags &
(POSIX_SPAWN_SETSCHEDULER | POSIX_SPAWN_SETSCHEDPARAM)) != 0) {
ret = spawn_sched_resolve(sa->sa_psflags, sa->sa_schedpolicy,
sa->sa_priority, &ks);
if (ret != 0)
return (ret);
ksched = &ks;
}
datalen = 0;
if (!spawn_marshal_param(NULL, sa, ksched, fa, shell, pathstr,
&datalen)) {
ret = E2BIG;
goto out;
}
if (datalen > 0) {
len = sizeof (*sparam) + datalen;
sparam = lmalloc(len);
if (sparam == NULL) {
ret = errno;
goto out;
}
sparam->sp_size = len;
sparam->sp_datalen = datalen;
datalen = 0;
if (!spawn_marshal_param(sparam, sa, ksched, fa, shell,
pathstr, &datalen) || datalen != sparam->sp_datalen) {
ret = EINVAL;
goto out;
}
}
datalen = 0;
if (!spawn_marshal_argenv(NULL, argp, envp, &datalen)) {
ret = E2BIG;
goto out;
}
len = sizeof (*sargs) + datalen;
sargs = lmalloc(len);
if (sargs == NULL) {
ret = errno;
goto out;
}
sargs->sa_size = len;
sargs->sa_datalen = datalen;
datalen = 0;
if (!spawn_marshal_argenv(sargs, argp, envp, &datalen) ||
datalen != sargs->sa_datalen) {
ret = EINVAL;
goto out;
}
pid = __spawn(path, sparam, sparam != NULL ? sparam->sp_size : 0,
sargs, sargs->sa_size);
if (pid == -1) {
ret = errno;
goto out;
}
if (pidp != NULL)
*pidp = pid;
out:
if (sparam != NULL)
lfree(sparam, sparam->sp_size);
if (sargs != NULL)
lfree(sargs, sargs->sa_size);
return (ret);
}
int
posix_spawn(pid_t *pidp, const char *path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp, char *const *argp, char *const *envp)
{
return (posix_spawn_common(pidp, path, file_actions, attrp,
argp, envp, NULL, NULL));
}
int
posix_spawnp(pid_t *pidp, const char *file,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp, char *const *argp, char *const *envp)
{
const char *pathstr =
(strchr(file, '/') == NULL) ? getenv("PATH") : "";
if (*file == '\0')
return (EACCES);
if (pathstr == NULL) {
if (geteuid() == 0 || getuid() == 0) {
if (!libc__xpg4) {
pathstr = "/usr/sbin:/usr/ccs/bin:/usr/bin";
} else {
pathstr = "/usr/xpg4/bin:/usr/ccs/bin:"
"/usr/bin:/opt/SUNWspro/bin:/usr/sbin";
}
} else {
if (!libc__xpg4) {
pathstr = "/usr/ccs/bin:/usr/bin:";
} else {
pathstr = "/usr/xpg4/bin:/usr/ccs/bin:"
"/usr/bin:/opt/SUNWspro/bin:";
}
}
}
return (posix_spawn_common(pidp, file, file_actions, attrp,
argp, envp, _PATH_BSHELL, pathstr));
}
int
posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions)
{
file_actions->__file_attrp = NULL;
return (0);
}
int
posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions)
{
file_attr_t *froot = file_actions->__file_attrp;
file_attr_t *fap;
file_attr_t *next;
if ((fap = froot) != NULL) {
do {
next = fap->fa_next;
if (fap->fa_type == FA_OPEN || fap->fa_type == FA_CHDIR)
lfree(fap->fa_path, fap->fa_pathsize);
lfree(fap, sizeof (*fap));
} while ((fap = next) != froot);
}
file_actions->__file_attrp = NULL;
return (0);
}
static void
add_file_attr(posix_spawn_file_actions_t *file_actions, file_attr_t *fap)
{
file_attr_t *froot = file_actions->__file_attrp;
if (froot == NULL) {
fap->fa_next = fap->fa_prev = fap;
file_actions->__file_attrp = froot = fap;
} else {
fap->fa_next = froot;
fap->fa_prev = froot->fa_prev;
froot->fa_prev->fa_next = fap;
froot->fa_prev = fap;
}
}
int
posix_spawn_file_actions_addopen(
posix_spawn_file_actions_t *restrict file_actions,
int filedes, const char *restrict path, int oflag, mode_t mode)
{
file_attr_t *fap;
if (filedes < 0)
return (EBADF);
if ((fap = lmalloc(sizeof (*fap))) == NULL)
return (ENOMEM);
fap->fa_pathsize = strlen(path) + 1;
if ((fap->fa_path = lmalloc(fap->fa_pathsize)) == NULL) {
lfree(fap, sizeof (*fap));
return (ENOMEM);
}
(void) memcpy(fap->fa_path, path, fap->fa_pathsize);
fap->fa_type = FA_OPEN;
fap->fa_oflag = oflag;
fap->fa_mode = mode;
fap->fa_filedes = filedes;
add_file_attr(file_actions, fap);
return (0);
}
int
posix_spawn_file_actions_addclose(
posix_spawn_file_actions_t *restrict file_actions, int filedes)
{
file_attr_t *fap;
if (filedes < 0)
return (EBADF);
if ((fap = lmalloc(sizeof (*fap))) == NULL)
return (ENOMEM);
fap->fa_type = FA_CLOSE;
fap->fa_filedes = filedes;
add_file_attr(file_actions, fap);
return (0);
}
int
posix_spawn_file_actions_adddup2(
posix_spawn_file_actions_t *restrict file_actions,
int filedes, int newfiledes)
{
file_attr_t *fap;
if (filedes < 0 || newfiledes < 0)
return (EBADF);
if ((fap = lmalloc(sizeof (*fap))) == NULL)
return (ENOMEM);
fap->fa_type = FA_DUP2;
fap->fa_filedes = filedes;
fap->fa_newfiledes = newfiledes;
add_file_attr(file_actions, fap);
return (0);
}
int
posix_spawn_file_actions_addclosefrom_np(
posix_spawn_file_actions_t *restrict file_actions, int lowfiledes)
{
file_attr_t *fap;
if (lowfiledes < 0)
return (EBADF);
if ((fap = lmalloc(sizeof (*fap))) == NULL)
return (ENOMEM);
fap->fa_type = FA_CLOSEFROM;
fap->fa_filedes = lowfiledes;
add_file_attr(file_actions, fap);
return (0);
}
int
posix_spawn_file_actions_addchdir(
posix_spawn_file_actions_t *restrict file_actions,
const char *restrict path)
{
file_attr_t *fap;
if ((fap = lmalloc(sizeof (*fap))) == NULL)
return (ENOMEM);
fap->fa_pathsize = strlen(path) + 1;
if ((fap->fa_path = lmalloc(fap->fa_pathsize)) == NULL) {
lfree(fap, sizeof (*fap));
return (ENOMEM);
}
(void) memcpy(fap->fa_path, path, fap->fa_pathsize);
fap->fa_type = FA_CHDIR;
add_file_attr(file_actions, fap);
return (0);
}
int
posix_spawn_file_actions_addchdir_np(
posix_spawn_file_actions_t *restrict file_actions,
const char *restrict path)
{
return (posix_spawn_file_actions_addchdir(file_actions, path));
}
int
posix_spawn_file_actions_addfchdir(
posix_spawn_file_actions_t *restrict file_actions, int fd)
{
file_attr_t *fap;
if (fd < 0)
return (EBADF);
if ((fap = lmalloc(sizeof (*fap))) == NULL)
return (ENOMEM);
fap->fa_type = FA_FCHDIR;
fap->fa_filedes = fd;
add_file_attr(file_actions, fap);
return (0);
}
int
posix_spawn_file_actions_addfchdir_np(
posix_spawn_file_actions_t *restrict file_actions, int fd)
{
return (posix_spawn_file_actions_addfchdir(file_actions, fd));
}
int
posix_spawnattr_init(posix_spawnattr_t *attr)
{
if ((attr->__spawn_attrp = lmalloc(sizeof (posix_spawnattr_t))) == NULL)
return (ENOMEM);
return (0);
}
int
posix_spawnattr_destroy(posix_spawnattr_t *attr)
{
spawn_attr_t *sap = attr->__spawn_attrp;
if (sap == NULL)
return (EINVAL);
lfree(sap, sizeof (*sap));
attr->__spawn_attrp = NULL;
return (0);
}
int
posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags)
{
spawn_attr_t *sap = attr->__spawn_attrp;
if (sap == NULL ||
(flags & ~ALL_POSIX_SPAWN_FLAGS))
return (EINVAL);
sap->sa_psflags = flags;
return (0);
}
int
posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags)
{
spawn_attr_t *sap = attr->__spawn_attrp;
if (sap == NULL)
return (EINVAL);
*flags = sap->sa_psflags;
return (0);
}
int
posix_spawnattr_setpgroup(posix_spawnattr_t *attr, pid_t pgroup)
{
spawn_attr_t *sap = attr->__spawn_attrp;
if (sap == NULL)
return (EINVAL);
sap->sa_pgroup = pgroup;
return (0);
}
int
posix_spawnattr_getpgroup(const posix_spawnattr_t *attr, pid_t *pgroup)
{
spawn_attr_t *sap = attr->__spawn_attrp;
if (sap == NULL)
return (EINVAL);
*pgroup = sap->sa_pgroup;
return (0);
}
int
posix_spawnattr_setschedparam(posix_spawnattr_t *attr,
const struct sched_param *schedparam)
{
spawn_attr_t *sap = attr->__spawn_attrp;
if (sap == NULL)
return (EINVAL);
sap->sa_priority = schedparam->sched_priority;
return (0);
}
int
posix_spawnattr_getschedparam(const posix_spawnattr_t *attr,
struct sched_param *schedparam)
{
spawn_attr_t *sap = attr->__spawn_attrp;
if (sap == NULL)
return (EINVAL);
schedparam->sched_priority = sap->sa_priority;
return (0);
}
int
posix_spawnattr_setschedpolicy(posix_spawnattr_t *attr, int schedpolicy)
{
spawn_attr_t *sap = attr->__spawn_attrp;
if (sap == NULL || schedpolicy == SCHED_SYS)
return (EINVAL);
if (get_info_by_policy(schedpolicy) == NULL)
return (errno);
sap->sa_schedpolicy = schedpolicy;
return (0);
}
int
posix_spawnattr_getschedpolicy(const posix_spawnattr_t *attr, int *schedpolicy)
{
spawn_attr_t *sap = attr->__spawn_attrp;
if (sap == NULL)
return (EINVAL);
*schedpolicy = sap->sa_schedpolicy;
return (0);
}
int
posix_spawnattr_setsigdefault(posix_spawnattr_t *attr,
const sigset_t *sigdefault)
{
spawn_attr_t *sap = attr->__spawn_attrp;
if (sap == NULL)
return (EINVAL);
sap->sa_sigdefault = *sigdefault;
return (0);
}
int
posix_spawnattr_getsigdefault(const posix_spawnattr_t *attr,
sigset_t *sigdefault)
{
spawn_attr_t *sap = attr->__spawn_attrp;
if (sap == NULL)
return (EINVAL);
*sigdefault = sap->sa_sigdefault;
return (0);
}
int
posix_spawnattr_setsigignore_np(posix_spawnattr_t *attr,
const sigset_t *sigignore)
{
spawn_attr_t *sap = attr->__spawn_attrp;
if (sap == NULL)
return (EINVAL);
sap->sa_sigignore = *sigignore;
return (0);
}
int
posix_spawnattr_getsigignore_np(const posix_spawnattr_t *attr,
sigset_t *sigignore)
{
spawn_attr_t *sap = attr->__spawn_attrp;
if (sap == NULL)
return (EINVAL);
*sigignore = sap->sa_sigignore;
return (0);
}
int
posix_spawnattr_setsigmask(posix_spawnattr_t *attr,
const sigset_t *sigmask)
{
spawn_attr_t *sap = attr->__spawn_attrp;
if (sap == NULL)
return (EINVAL);
sap->sa_sigmask = *sigmask;
return (0);
}
int
posix_spawnattr_getsigmask(const posix_spawnattr_t *attr,
sigset_t *sigmask)
{
spawn_attr_t *sap = attr->__spawn_attrp;
if (sap == NULL)
return (EINVAL);
*sigmask = sap->sa_sigmask;
return (0);
}
int
posix_spawn_pipe_np(pid_t *pidp, int *fdp,
const char *cmd, boolean_t write,
posix_spawn_file_actions_t *fact, posix_spawnattr_t *attr)
{
int p[2];
int myside, yourside, stdio;
const char *shpath = _PATH_BSHELL;
const char *argvec[4] = { "sh", "-c", cmd, NULL };
int error;
if (pipe(p) < 0)
return (errno);
if (access(shpath, X_OK))
shpath = "";
if (write) {
myside = p[1];
yourside = p[0];
stdio = STDIN_FILENO;
} else {
myside = p[0];
yourside = p[1];
stdio = STDOUT_FILENO;
}
error = posix_spawn_file_actions_addclose(fact, myside);
if (yourside != stdio) {
if (error == 0) {
error = posix_spawn_file_actions_adddup2(fact,
yourside, stdio);
}
if (error == 0) {
error = posix_spawn_file_actions_addclose(fact,
yourside);
}
}
if (error)
return (error);
error = posix_spawn(pidp, shpath, fact, attr,
(char *const *)argvec, (char *const *)_environ);
(void) close(yourside);
if (error) {
(void) close(myside);
return (error);
}
*fdp = myside;
return (0);
}