Files
QNX/services/system/proc/proc_loader.c
2025-08-20 19:02:58 +08:00

942 lines
25 KiB
C

/*
* $QNXLicenseC:
* Copyright 2007, QNX Software Systems. All Rights Reserved.
*
* You must obtain a written license from and pay applicable license fees to QNX
* Software Systems before you may reproduce, modify or distribute this software,
* or any work that includes all or part of this software. Free development
* licenses are available for evaluation and non-commercial purposes. For more
* information visit http://licensing.qnx.com or email licensing@qnx.com.
*
* This file may contain contributions from others. Please review this entire
* file for other proprietary rights or license notices, as well as the QNX
* Development Suite License Guide at http://licensing.qnx.com/license-guide/
* for other information.
* $
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <share.h>
#include <sys/dcmd_all.h>
#include <sys/stat.h>
#include "externs.h"
#include <termios.h>
static void set_signal(int signo, void (*func)(int)) {
struct sigaction act;
act.sa_sigaction = act.sa_handler = func;
act.sa_mask.__bits[0] = act.sa_mask.__bits[1] = 0;
act.sa_flags = 0;
(void)SignalAction(0, 0, signo, &act, 0);
}
void loader_exit(resmgr_context_t *ctp, union proc_msg_union *msg, PROCESS *prp) {
struct loader_context *lcp;
int r;
if((lcp = prp->lcp)) {
if(lcp->rcvid != -1) {
switch(prp->siginfo.si_signo) {
case SIGCHLD:
r = prp->siginfo.si_status;
break;
case SIGBUS:
r = (lcp->fault_errno != EOK) ? lcp->fault_errno : ENOMEM;
break;
default:
r = EINTR;
break;
}
MsgError(lcp->rcvid, r);
}
procmgr_context_free(lcp);
prp->lcp = 0;
}
}
/*
In local case, this function gets the fdlist (need to be duped) either by reading
the fd list area in *lcp when a fd array is passed in by spawn or by walking the
fd list of its parents when inheritant case. Then, ConnectServerInfo is called
to get the fd infos and dup messages are sent to the servers to get them duped.
When any fds are connected to other network nodes, a message is sent to proc to query.
In remote case, at least the first 10 fds' info are passed in by the remote
node and stored in *lcp. The function sends dup messages to servers based
on those info. If the fd list is longer than stored (which is very rare),
a message is sent to the remote node's proc to make queries.
*/
static int loader_fd(struct loader_context *lcp) {
struct _server_info info;
unsigned flags;
int *pfd, fd2, fd, nfds, i_nfds;
enum {
_FD_SPAWN_LIST,
_FD_FORK,
_FD_SPAWN_NOCLOEXC,
_FD_SPAWN_REMOTE,
_FD_SPAWN_QUERY
} type;
io_dup_t msgdup;
struct _proc_spawn_fd_info *pfdinfo;
struct _proc_spawn_fd_info temp_buff[32];
int coid, size;
type = _FD_FORK;
coid = SYSMGR_COID;
size = 0;
/* following three lines is used to kill warnings */
flags = 0;
pfdinfo = NULL;
pfd = NULL;
if((lcp->state & LC_STATE_MASK) != LC_FORK) {
nfds = i_nfds = lcp->msg.spawn.i.nfds;
if(ND_NODE_CMP(lcp->pnode, ND_LOCAL_NODE) == 0) {
type = _FD_SPAWN_NOCLOEXC;
if(nfds != 0) {
type = _FD_SPAWN_LIST;
pfd = (int*)((char *)&lcp->msg.spawn.i + sizeof lcp->msg.spawn.i);
}
} else {
proc_spawn_remote_t *p;
p = (proc_spawn_remote_t*) ((char*)&lcp->msg.spawn.i + lcp->remote_off);
if(lcp->state & LC_CROSS_ENDIAN) {
/*
* Put here by _PROC_SPAWN_REMOTE, skipped by lcp->remote_off?
* ENDIAN_SWAP32(&remote.nd);
* ENDIAN_SWAP32(&remote.pid);
* ENDIAN_SWAP32(&remote.chid);
* ENDIAN_SWAP32(&remote.size);
*/
ENDIAN_SWAP32(&p->key);
ENDIAN_SWAP32(&p->umask);
ENDIAN_SWAP16(&p->nfds);
ENDIAN_SWAP16(&p->root_len);
ENDIAN_SWAP16(&p->cwd_len);
ENDIAN_SWAP16(&p->flags);
}
info.flags = 0;
type = _FD_SPAWN_REMOTE;
nfds = p->nfds;
flags = p->flags;
pfdinfo = (struct _proc_spawn_fd_info*) (((uintptr_t)p + sizeof(*p) + p->root_len + p->cwd_len + sizeof(_Int32t) - 1) & ~(sizeof(_Int32t)-1));
}
} else {
nfds = i_nfds = 0;
}
fd = -1;
errno = EMFILE;
msgdup.i.type = _IO_DUP;
msgdup.i.combine_len = sizeof msgdup.i;
msgdup.i.info.pid = lcp->ppid;
msgdup.i.info.tid = 0;
msgdup.i.info.nd = ND_LOCAL_NODE;
while(1) {
switch(type) {
case _FD_SPAWN_LIST:
if(++fd >= nfds) {
return EOK;
}
if((fd2 = *pfd++) == SPAWN_FDCLOSED) {
continue;
}
if((ConnectServerInfo(lcp->ppid, fd2, &info) != fd2) || (info.flags & _NTO_COF_DEAD)) {
return EBADF;
}
info.flags = 0;
if(ND_NODE_CMP(info.nd, ND_LOCAL_NODE) != 0) {
type = _FD_SPAWN_QUERY;
nfds = 0;
fd--;
continue;
}
break;
case _FD_FORK:
case _FD_SPAWN_NOCLOEXC:
if(ConnectServerInfo(lcp->ppid, ++fd, &info) == -1 || ((fd = info.coid) & _NTO_SIDE_CHANNEL)) {
return EOK;
}
if(info.flags & _NTO_COF_DEAD) {
continue;
}
if((info.flags & _NTO_COF_CLOEXEC) && (type != _FD_FORK)) {
continue;
}
if(ND_NODE_CMP(info.nd, ND_LOCAL_NODE) != 0) {
type = _FD_SPAWN_QUERY;
fd--;
continue;
}
break;
case _FD_SPAWN_QUERY:
case _FD_SPAWN_REMOTE:
if(nfds-- <= 0) {
if(flags & _PROC_SPAWN_REMOTE_FLAGS_FDALLIN) {
/* finish */
if(coid != SYSMGR_COID) {
ConnectDetach(coid);
}
return EOK;
}
/* read additional fd info */
{
proc_spawn_fd_t spawnfd;
fd++;
if(type == _FD_SPAWN_REMOTE) {
proc_spawn_remote_t *p;
p = (proc_spawn_remote_t*) ((char*)&lcp->msg.spawn.i + lcp->remote_off);
if(coid == SYSMGR_COID) {
/* make a connection to remote proc */
if((coid = ConnectAttach(lcp->pnode, PROCMGR_PID, PROCMGR_CHID, _NTO_SIDE_CHANNEL, 0)) == -1) {
return errno;
}
}
pfdinfo = (struct _proc_spawn_fd_info*) ((char*)p + sizeof(*p));
nfds = (SPAWN_REMOTE_REMOTEBUF_SIZE - sizeof(*p))/sizeof(*pfdinfo);
} else {
size = sizeof(lcp->msg.spawn.i) + i_nfds * sizeof(_Int32t);
pfdinfo = (struct _proc_spawn_fd_info*) ((char*)&lcp->msg.spawn.i + size);
size = sizeof(lcp->msg) - size;
if(size < sizeof(temp_buff)) {
size = sizeof(temp_buff);
pfdinfo = temp_buff;
}
nfds = size/(int)sizeof(*pfdinfo);
}
SETIOV(lcp->iov + 0, &spawnfd.i, sizeof spawnfd.i);
SETIOV(lcp->iov + 2, &spawnfd.o, sizeof spawnfd.o);
SETIOV(lcp->iov + 3, pfdinfo, nfds * sizeof(*pfdinfo));
spawnfd.i.type = _PROC_SPAWN;
spawnfd.i.subtype = _PROC_SPAWN_FD;
spawnfd.i.base = fd;
spawnfd.i.ppid = lcp->ppid;
spawnfd.i.flags = 0;
if(i_nfds) {
spawnfd.i.nfds = min(nfds, i_nfds - fd);
SETIOV(lcp->iov + 1, (char *)&lcp->msg.spawn.i + sizeof lcp->msg.spawn.i + fd * sizeof(_Int32t), spawnfd.i.nfds * sizeof(_Int32t));
spawnfd.i.flags |= _PROC_SPAWN_FD_LIST;
} else {
spawnfd.i.nfds = nfds;
SETIOV(lcp->iov + 1, lcp->iov[3].iov_base, 0);
if((lcp->state & LC_STATE_MASK) != LC_FORK) {
spawnfd.i.flags |= _PROC_SPAWN_FD_NOCLOEXC;
}
}
if(MsgSendv(coid, lcp->iov + 0, 2, lcp->iov + 2, 2) == -1) {
if(coid != SYSMGR_COID) {
ConnectDetach(coid);
}
return errno;
}
nfds = spawnfd.o.nfds;
if(nfds-- == 0) {
/* finish */
if(coid != SYSMGR_COID) {
ConnectDetach(coid);
}
return EOK;
}
if(i_nfds) {
if((pfdinfo + nfds)->fd + 1 >= i_nfds) {
/* set finish flag */
flags |= _PROC_SPAWN_REMOTE_FLAGS_FDALLIN;
}
} else {
if(!(spawnfd.o.flags & _PROC_SPAWN_FDREPLY_MORE)) {
/* set finish flag */
flags |= _PROC_SPAWN_REMOTE_FLAGS_FDALLIN;
}
}
}
}
if(lcp->state & LC_CROSS_ENDIAN) {
ENDIAN_SWAP32(&pfdinfo->fd);
ENDIAN_SWAP32(&pfdinfo->nd);
ENDIAN_SWAP32(&pfdinfo->pid);
ENDIAN_SWAP32(&pfdinfo->chid);
ENDIAN_SWAP32(&pfdinfo->scoid);
ENDIAN_SWAP32(&pfdinfo->coid);
ENDIAN_SWAP32(&pfdinfo->srcnd);
}
fd = pfdinfo->fd;
info.nd = pfdinfo->nd;
info.pid = pfdinfo->pid;
info.chid = pfdinfo->chid;
info.scoid = pfdinfo->scoid;
info.coid = pfdinfo->coid;
msgdup.i.info.nd = pfdinfo->srcnd;
pfdinfo ++;
}
if(ConnectAttach(info.nd, info.pid, info.chid, fd, info.flags) != fd) {
if(coid != SYSMGR_COID) {
ConnectDetach(coid);
}
return errno;
}
if(!(info.chid & _NTO_GLOBAL_CHANNEL)) {
msgdup.i.info.scoid = info.scoid;
msgdup.i.info.coid = info.coid;
if(MsgSend(fd, &msgdup.i, sizeof msgdup.i, 0, 0) == -1) {
if(coid != SYSMGR_COID) {
ConnectDetach(coid);
}
return errno;
}
}
}
}
void *loader_fork(void *parm) {
struct loader_context *lcp = parm;
PROCESS *prp;
int fd, status = 0;
int sig;
THREAD *thp = lcp->process->valid_thp;
sigset_t mask;
if(!thp) crash();
lcp->tid = pthread_self();
SignalProcmask(0, 0, SIG_SETMASK, &lcp->mask, 0);
// Block all signals to avoid deadlock
thp->sig_blocked.__bits[0] = thp->sig_blocked.__bits[1] = ~0;
if((fd = ConnectAttach(0, PROCMGR_PID, PROCMGR_CHID, PROCMGR_COID, 0)) == -1) {
ThreadDestroy(-1, -1, (void *)errno);
} else if(fd != PROCMGR_COID) {
ThreadDestroy(-1, -1, (void *)EL3HLT);
}
// Lookup the parent
prp = proc_lookup_pid(lcp->ppid);
if(proc_rlock_adp(prp) == -1) {
ThreadDestroy(-1, -1, (void *)EL2HLT);
}
if(lcp->flags & _FORK_ASPACE) {
if((proc_wlock_adp(lcp->process) == -1) || (status = memmgr.dup(prp, lcp->process))) {
if(lcp->process->memory) {
proc_unlock_adp(lcp->process);
}
proc_unlock_adp(prp);
ThreadDestroy(-1, -1, (void *)status);
}
proc_unlock_adp(lcp->process);
} else {
lcp->start.stackaddr = (uintptr_t)lcp->msg.fork.i.frame;
if(lcp->process->memory) {
proc_wlock_adp(sysmgr_prp);
while(ProcessShutdown(0, 0) == -1 && errno == EAGAIN) {
/* nothing to do */
}
proc_unlock_adp(sysmgr_prp);
}
lcp->process->flags |= _NTO_PF_VFORKED;
lcp->process->memory = prp->memory;
ProcessBind(lcp->process->pid);
}
proc_unlock_adp(prp);
if(prp->debug_name) {
lcp->process->debug_name = strdup(prp->debug_name);
}
lcp->process->initial_esp = prp->initial_esp;
lcp->process->base_addr = prp->base_addr;
lcp->process->pls = prp->pls;
lcp->process->umask = prp->umask;
// Copy the signal handlers...
mask = lcp->mask;
for(sig = 1; sig < NSIG; sig++) {
struct sigaction act;
if(SignalAction(lcp->ppid, 0, sig, 0, &act) != -1) {
(void)SignalAction(lcp->process->pid, prp->sigstub, sig, &act, 0);
sigaddset(&mask, sig);
}
}
// Only enable signals that don't have a handler
SignalProcmask(0, 0, SIG_SETMASK, &mask, 0);
if(!(lcp->flags & _FORK_NOFDS)) {
if((status = loader_fd(lcp)) != 0) {
ThreadDestroy(-1, -1, (void *)status);
}
}
lcp->msg.spawn_done.i.type = _PROC_SPAWN;
lcp->msg.spawn_done.i.subtype = _PROC_SPAWN_DONE;
SETIOV(lcp->iov + 0, &lcp->msg.spawn_done.i, sizeof lcp->msg.spawn_done.i);
MsgSendv(PROCMGR_COID, lcp->iov, 1, 0, 0);
ThreadDestroy(-1, -1, (void *)errno);
return 0;
}
static int is_exec(const char *path) {
struct stat st;
if(stat(path, &st) == -1) {
return -1;
} else if(!S_ISREG(st.st_mode) && !S_ISNAM(st.st_mode)) {
errno = EACCES;
return -1;
} else if(access(path, X_OK) == -1) {
return -1;
}
return 0;
}
static int searchpath(const char *name, const char *path, char *buffer, int bufsize) {
char *b;
int n, trailing;
if (name == NULL || *name == '\0' || path == NULL || *path == '\0') {
errno = ENOENT;
}
else {
#ifndef NDEBUG
/* assert proper args */
if ((buffer == NULL) || (bufsize < strlen(name))) crash();
#endif
bufsize -= strlen(name);
do {
for (n = bufsize, *(b = buffer) = 0; *path && *path != ':'; n--, path++) {
if (n > 0) {
*b++ = *path;
}
}
if (n > 0) {
if (*buffer && b[-1] != '/') {
*b++ = '/';
}
STRLCPY(b, name, bufsize - (b - buffer));
if (is_exec(buffer) != -1) {
return 0;
}
}
else {
errno = ENAMETOOLONG;
break;
}
trailing = (*path != ':') ? 0 : !*++path;
} while (*path || trailing);
}
*buffer = 0;
return -1;
}
static void loader_args(struct loader_context *lcp, const char *p, const char *exefile,
char *interp_name, char *interp_arg) {
char *aux_exefile;
const char **arg, *orig_exefile;
char *strings;
int naux;
auxv_t *auxv;
int exelen;
int arg_bytes;
int ninterp = 0, interplen = 0,
interp_name_len = 0, interp_arg_len = 0;
int skip_arg0 = 0;
p += strlen(p) + 1;
orig_exefile = NULL;
if (interp_name) {
orig_exefile = exefile;
exefile = interp_name;
interplen = interp_name_len = strlen(interp_name) + 1;
interplen += strlen(orig_exefile) + 1;
ninterp = 2;
}
if (interp_arg) {
interp_arg_len = strlen(interp_arg) + 1;
interplen += interp_arg_len;
ninterp++;
}
exelen = strlen(exefile) + 1;
arg_bytes = lcp->msg.spawn.i.nbytes;
arg_bytes += interplen;
#ifdef STACK_GROWS_UP
#error STACK_GROWS_UP not supported
#endif
// strings is also top of stack
aux_exefile = (char *)lcp->start.esp - exelen;
strcpy(aux_exefile, exefile);
strings = ((char *)lcp->start.esp - arg_bytes) - exelen;
if (interp_name) {
strcpy(strings, interp_name);
if (interp_arg)
strcpy(strings+interp_name_len, interp_arg);
strcpy(strings+interp_name_len+interp_arg_len, orig_exefile);
}
// can only modify "p" and "nbytes"
if(lcp->msgsize - (p - lcp->msg.filler) > lcp->msg.spawn.i.nbytes) {
memcpy(strings + interplen, p, lcp->msg.spawn.i.nbytes);
} else {
proc_spawn_args_t args;
args.i.type = _PROC_SPAWN;
args.i.subtype = _PROC_SPAWN_ARGS;
args.i.nbytes = lcp->msg.spawn.i.nbytes;
args.i.offset = (uintptr_t)p - (uintptr_t)&lcp->msg;
args.i.zero = 0;
if(MsgSend(PROCMGR_COID, &args.i, sizeof args.i,
strings + interplen, args.i.nbytes) == -1) {
ThreadDestroy(-1, -1, (void *)errno);
}
}
// count the size of the aux vector
for(naux = 1, auxv = lcp->start.aux; auxv->a_type != AT_NULL; auxv++, naux++) {
/* nothing to do */
}
auxv[1] = auxv[0];
auxv->a_type = AT_EXEFILE;
auxv->a_un.a_ptr = aux_exefile;
naux++;
// find start of args, must include argc and 2 terminating null pointers
arg = (const char **)((((uintptr_t)strings & ~(sizeof *arg - 1)) -
(ninterp + lcp->msg.spawn.i.nargv + lcp->msg.spawn.i.narge +
naux * (sizeof *lcp->start.aux / sizeof *arg) + 3) * sizeof *arg) &
~(STACK_ALIGNMENT - 1));
lcp->start.esp = (uintptr_t)arg;
if((*(unsigned *)arg = lcp->msg.spawn.i.nargv) == 0) {
ThreadDestroy(-1, -1, (void *)EINVAL);
}
/* account for the interpreter and its arg if they are present */
if (ninterp) {
*(unsigned *)arg += (ninterp - 1); /* -1 is because we skip argv[0] */
}
arg++;
for(p = strings;;) {
*arg++ = p;
if(lcp->msg.spawn.i.nargv) {
if (ninterp) {
ninterp--;
if (ninterp == 0) {
/* we have to skip argv[0] because it isn't the
* correct argv[0]. the correct argv[0] (i.e.
* with a full path) is stored in orig_exefile
* and is accounted for by "ninterp".
*/
skip_arg0 = 1;
lcp->msg.spawn.i.nargv--;
}
} else {
lcp->msg.spawn.i.nargv--;
}
if (lcp->msg.spawn.i.nargv == 0) {
*arg++ = 0;
}
}
if(lcp->msg.spawn.i.nargv == 0) {
if(lcp->msg.spawn.i.narge == 0) {
*arg++ = 0;
break;
} else {
lcp->msg.spawn.i.narge--;
}
}
redo:
while(*p++ != '\0') {
if(p > aux_exefile) {
ThreadDestroy(-1, -1, (void *)EINVAL);
}
}
if((char *)arg > strings) {
ThreadDestroy(-1, -1, (void *)EINVAL);
}
if (skip_arg0) { /* this makes us skip over two strings */
skip_arg0 = 0;
goto redo;
}
}
memcpy(arg, lcp->start.aux, sizeof *lcp->start.aux * naux);
}
int
try_hash_bang(int fd, char **pname, char **arg, char *buf, int buflen)
{
int numread;
char *ptr = buf;
*pname = NULL; /* initialize these two */
*arg = NULL;
numread = proc_read(fd, buf, buflen, 0);
if (numread <= 3) { /* need at least #! characters */
return ENOEXEC;
}
if (ptr[0] != '#' || ptr[1] != '!') {
return ENOEXEC;
}
ptr += 2;
/* skip whitespace after the #! */
while ((*ptr == ' ' || *ptr == '\t') && ptr < (buf + numread)) {
ptr++;
}
/*
* If this condition is true it means the first line was just "#!\n".
* The spec says this is not valid. We could be nice and interpret
* it to be the same as "#!/bin/sh" but we won't do that for now.
*/
if (ptr >= (buf+numread) || *ptr == '\n') {
return ENOEXEC;
}
/* interpreter name starts here */
*pname = ptr;
/* find the end of the interpeter name */
while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n' && ptr < (buf+numread)) {
ptr++;
}
if (ptr >= (buf+numread)) { /* hmmm, interpreter name was too long... */
return ENOEXEC;
}
if (*ptr == '\n') { /* we're at the end of the line */
*ptr = '\0';
return EOK;
} else {
*ptr++ = '\0'; /* null terminate but continue processing */
}
/* skip whitespace after the #! */
while ((*ptr == ' ' || *ptr == '\t') && ptr < (buf+numread)) {
ptr++;
}
if (ptr >= (buf+numread) || *ptr == '\n') { /* there was nothing else */
return EOK;
}
*arg = ptr;
/* find the end of the interpeter name */
while (*ptr != '\n' && ptr < (buf+numread)) {
ptr++;
}
if (ptr >= (buf+numread)) {
ptr--;
}
if (*ptr == '\n' || *ptr == ' ' || *ptr == '\t') {
ptr--;
while(*ptr == ' ' || *ptr == '\t') {
ptr--;
}
ptr++;
}
*ptr = '\0'; /* null terminate the arg */
return EOK;
}
static int
open_exe(const char *name, struct stat *statp) {
int fd, flags;
/*
* Try to open the file using the real uid/gid, which will be actual
* uid & gid of the new process. If that fails, try the open with
* the effective uid/guid, which will be root. The first will work
* 99.9% of the time, the latter is needed for files that have execute
* but not read permission. This whole song and dance is to handle
* NFS root squashing.
*/
fd = sopen(name, O_RDONLY | O_REALIDS, SH_DENYWR);
if((fd == -1) && (errno == EACCES)) {
fd = sopen(name, O_RDONLY, SH_DENYWR);
}
if((fd == -1) || (fstat(fd, statp) == -1)) {
if(errno == EBUSY) {
errno = ETXTBSY;
}
ThreadDestroy(-1, -1, (void *)errno);
}
/*
* Honour the MOUNT_NOSUID qualifier by knocking down these bits
* (affects local/internal processing only). If filesystem does
* not support this probe, assume that MOUNT_NOSUID is disabled.
*/
if((statp->st_mode & (S_ISUID | S_ISGID))
&& _devctl(fd, DCMD_ALL_GETMOUNTFLAGS, &flags, sizeof(flags), NULL) == EOK
&& (flags & _MOUNT_NOSUID)) {
statp->st_mode &= ~(S_ISUID | S_ISGID);
}
return fd;
}
void *loader_load(void *parm) {
struct loader_context *lcp = parm;
const char *p;
int fd;
int status;
uid_t uid;
struct stat statl;
char buffer[_POSIX_PATH_MAX + 1];
void *base;
size_t size;
char *interp_name=NULL, *interp_arg=NULL;
char linebuf[128]; /* for the '#!' test */
int needs_secure;
unsigned prealloc;
lcp->tid = pthread_self();
SignalProcmask(0, 0, SIG_SETMASK, &lcp->mask, 0);
switch(lcp->msg.spawn.i.parms.flags & SPAWN_ALIGN_MASK) {
case SPAWN_ALIGN_DEFAULT:
status = 0;
break;
case SPAWN_ALIGN_FAULT:
status = 1;
break;
case SPAWN_ALIGN_NOFAULT:
status = -1;
break;
default:
ThreadDestroy(-1, -1, (void *)EINVAL);
}
(void)ThreadCtl(_NTO_TCTL_ALIGN_FAULT, &status);
if(lcp->msg.spawn.i.parms.flags & SPAWN_SETSIGDEF) {
int sig;
for(sig = _SIGMIN; sig <= _SIGMAX; sig++) {
if(sigismember(&lcp->msg.spawn.i.parms.sigdefault, sig)) {
set_signal(sig, SIG_DFL);
}
}
}
if(lcp->msg.spawn.i.parms.flags & SPAWN_SETSIGIGN) {
int sig;
for(sig = _SIGMIN; sig <= _SIGMAX; sig++) {
if(sigismember(&lcp->msg.spawn.i.parms.sigignore, sig)) {
set_signal(sig, SIG_IGN);
}
}
}
if((fd = ConnectAttach(0, SYSMGR_PID, SYSMGR_CHID, PROCMGR_COID, 0)) == -1) {
ThreadDestroy(-1, -1, (void *)errno);
} else if(fd != PROCMGR_COID) {
ThreadDestroy(-1, -1, (void *)EL3HLT);
}
if(lcp->msg.spawn.i.parms.flags & SPAWN_SETSID) {
if(setsid() == -1) {
ThreadDestroy(-1, -1, (void *)errno);
}
}
if(lcp->msg.spawn.i.parms.flags & SPAWN_EXPLICIT_CPU) {
unsigned runmask = lcp->msg.spawn.i.parms.runmask;
// Verify specified runmask is valid
if((runmask & LEGAL_CPU_BITMASK) == 0) {
ThreadDestroy(-1, -1, (void *)EINVAL);
}
}
if(lcp->msg.spawn.i.parms.flags & SPAWN_SETGROUP) {
if(setpgid(getpid(), lcp->msg.spawn.i.parms.pgroup) == -1) {
ThreadDestroy(-1, -1, (void *)errno);
}
}
// proc_spawn set saved_user_id to 0, so this will work...
uid = getuid();
(void)setreuid(geteuid(), 0);
(void)setregid(getegid(), getgid());
/* set root and cwd */
if(ND_NODE_CMP(lcp->pnode, ND_LOCAL_NODE) != 0) {
proc_spawn_remote_t *premote;
char *p0, *p1;
premote = (proc_spawn_remote_t *)((char*)&lcp->msg.spawn.i + lcp->remote_off);
p0 = (char*)premote + sizeof(*premote);
if(netmgr_strtond(p0, &p1) == 0) {
if(strlen(p1) == 0) {
*p0 = '/';
*(p0+1) = 0;
} else {
p0 = p1 - 1;
}
}
(void)chroot(p0);
(void)chdir((char*)premote + sizeof(*premote) + premote->root_len);
}
p = (const char *)&lcp->msg.spawn.i + (sizeof lcp->msg.spawn.i + lcp->msg.spawn.i.nfds * sizeof(long));
if(lcp->msg.spawn.i.parms.flags & SPAWN_SEARCH_PATH) {
if(searchpath(p + lcp->msg.spawn.i.searchlen, p, buffer, sizeof buffer) == -1) {
ThreadDestroy(-1, -1, (void *)errno);
}
p += lcp->msg.spawn.i.searchlen;
} else {
if(is_exec(p) == -1) {
ThreadDestroy(-1, -1, (void *)errno);
} else if (memccpy(buffer, p, '\0', sizeof(buffer)) == NULL) {
ThreadDestroy(-1, -1, (void *)ENAMETOOLONG);
}
}
fd = open_exe(buffer, &statl);
lcp->start.aux[0].a_type = AT_NULL;
lcp->start.aux[0].a_un.a_val = 0;
needs_secure = 0;
if((statl.st_mode & S_ISUID) && (statl.st_uid != getuid())) {
needs_secure = 1;
}
if((statl.st_mode & S_ISGID) && (statl.st_gid != getgid())) {
needs_secure = 1;
}
if(needs_secure) {
lcp->start.aux[0].a_type = AT_LIBPATH;
lcp->start.aux[1].a_type = AT_NULL;
lcp->start.aux[1].a_un.a_val = 0;
}
if((status = elf_load(fd, buffer, &lcp->start, &statl, &lcp->msg.spawn.i.parms)) == -1) {
/*
* fd is not an ELF executable - see if we should exec interpreter
*/
status = try_hash_bang(fd, &interp_name, &interp_arg, linebuf, sizeof(linebuf));
if(status == ENOEXEC && lcp->msg.spawn.i.parms.flags & SPAWN_CHECK_SCRIPT) {
/*
* POSIX 1003.1-2001 requires exec[vl]p to execute a shell as
* the interpreter for ENOEXEC errors.
*/
strcpy(linebuf, shell_path);
interp_name = linebuf;
interp_arg = 0;
status = EOK;
}
close(fd);
if (status == EOK) {
if(is_exec(interp_name) == -1) {
ThreadDestroy(-1, -1, (void *)errno);
}
fd = open_exe(interp_name, &statl);
status = elf_load(fd, interp_name, &lcp->start, &statl, &lcp->msg.spawn.i.parms);
}
if (status == -1) {
status = ENOEXEC;
}
}
if(status != EOK) {
close(fd);
ThreadDestroy(-1, -1, (void *)status);
}
close(fd);
if(lcp->msg.spawn.i.parms.flags & SPAWN_SETSTACKMAX) {
lcp->start.stacksize = lcp->msg.spawn.i.parms.stack_max;
}
if(lcp->start.stacksize < lcp->start.stackalloc) {
lcp->start.stacksize = lcp->start.stackalloc;
}
if(lcp->flags & PROC_LF_LAZYSTACK) {
prealloc = 0;
} else {
prealloc = lcp->start.stackalloc;
}
if(_mmap2((void *)lcp->start.stackaddr, lcp->start.stacksize + guardpagesize,
PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON|MAP_STACK|MAP_LAZY,
NOFD, guardpagesize, __PAGESIZE, prealloc, &base, &size) == MAP_FAILED) {
ThreadDestroy(-1, -1, (void *)ENOMEM);
}
if((lcp->start.esp = (uintptr_t)ThreadTLS(0, (uintptr_t)base, size, guardpagesize, (uintptr_t)base + size)) == (uintptr_t)-1) {
ThreadDestroy(-1, -1, (void *)(errno == EFAULT ? ENOMEM : errno));
}
loader_args(lcp, p, buffer, interp_name, interp_arg);
if(!(lcp->flags & SPAWN_EXEC)) {
status = loader_fd(lcp);
if(status != EOK) {
ThreadDestroy(-1, -1, (void *)status);
}
}
(void)setregid(getegid(), (statl.st_mode & S_ISGID) ? statl.st_gid : getgid());
(void)setreuid(uid, (statl.st_mode & S_ISUID) ? statl.st_uid : getuid());
if(!(lcp->flags & SPAWN_EXEC)) {
if((lcp->msg.spawn.i.parms.flags & SPAWN_TCSETPGROUP) && isatty(STDIN_FILENO) > 0) {
if(lcp->msg.spawn.i.parms.flags & SPAWN_SETSID) {
(void)tcsetsid(STDIN_FILENO, getpgrp());
}
(void)tcsetpgrp(STDIN_FILENO, getpgrp());
}
}
lcp->msg.spawn_done.i.type = _PROC_SPAWN;
lcp->msg.spawn_done.i.subtype = (lcp->flags & SPAWN_EXEC) ? _PROC_SPAWN_EXEC : _PROC_SPAWN_DONE;
SETIOV(lcp->iov + 0, &lcp->msg.spawn_done.i, sizeof lcp->msg.spawn_done.i);
MsgSendv(PROCMGR_COID, lcp->iov, 1, 0, 0);
ThreadDestroy(-1, -1, (void *)errno);
return 0;
}
__SRCVERSION("proc_loader.c $Rev: 153052 $");