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) { /* * parent and child are in a race here */ 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 }
int csopen(register Cs_t* state, const char* apath, int op) { register char* path = (char*)apath; register char* b; register char* s; register int n; int fd; char* t; char* u; char* type; char* endtype; char* host; char* endhost; char* serv; char* endserv; char* qual; char* endqual; char* opath; char* user = 0; char* group = 0; char* trust = 0; char* arg = 0; int nfd = -1; int uid = -1; int gid = -1; int sid = -1; int auth = 1; int mode; unsigned long addr; unsigned long port = 0; struct stat st; char buf[PATH_MAX]; char tmp[PATH_MAX]; if (!path) { errno = EFAULT; return -1; } csprotect(&cs); if (op < 0) op = CS_OPEN_TEST; messagef((state->id, NiL, -8, "open(%s,%o) call", path, op)); /* * blast out the parts */ opath = path; if (pathgetlink(path, buf, sizeof(buf)) <= 0) { if (strlen(path) >= sizeof(buf)) return -1; strcpy(buf, path); } else if ((state->flags & CS_ADDR_LOCAL) && (s = strrchr(buf, '/'))) { /* * dynamic ip assignment can change the addr * underfoot in some implementations so we * double check the local ip here */ strcpy(tmp, buf); if (tokscan(tmp, NiL, "/dev/%s/%s/%s", &type, NiL, &serv) == 3) sfsprintf(buf, sizeof(buf), "/dev/%s/%s/%s", type, csntoa(state, 0), serv); } path = buf; pathcanon(path, 0, 0); errno = ENOENT; strcpy(state->path, path); b = path; if ((*b++ != '/') || !(s = strchr(b, '/'))) return -1; *s++ = 0; if (!streq(b, "dev")) return -1; if (b = strchr(s, '/')) *b++ = 0; if (streq(s, "fdp")) { #if !( CS_LIB_SOCKET_UN || CS_LIB_STREAM || CS_LIB_V10 ) if (access(CS_PROC_FD_TST, F_OK)) { errno = ENODEV; messagef((state->id, NiL, -1, "open: %s: %s: not supported", state->path, s)); return -1; } #endif } else if (!streq(s, "tcp") && !streq(s, "udp")) { messagef((state->id, NiL, -1, "open: %s: %s: invalid type", state->path, s)); return -1; } #if !( CS_LIB_SOCKET || CS_LIB_STREAM || CS_LIB_V10 ) else { errno = ENODEV; messagef((state->id, NiL, -1, "open: %s: %s: not supported", state->path, s)); return -1; } #endif type = s; qual = state->qual; if (!b) host = serv = 0; else { host = b; if (!(s = strchr(b, '/'))) serv = 0; else { *s++ = 0; serv = s; /* * grab the next fd to preserve open semantics */ for (n = 0; n < 10; n++) if ((nfd = dup(n)) >= 0) break; /* * get qual, perm and arg */ mode = S_IRWXU|S_IRWXG|S_IRWXO; if (b = strchr(s, '/')) { *b++ = 0; do { if (*b == '#') { arg = b + 1; break; } if (u = strchr(b, '/')) *u++ = 0; if (s = strchr(b, '=')) *s++ = 0; for (n = 0, t = b; *t; n = HASHKEYPART(n, *t++)); switch (n) { case HASHKEY5('g','r','o','u','p'): group = s ? s : ""; break; case HASHKEY5('l','o','c','a','l'): op |= CS_OPEN_LOCAL; break; case HASHKEY3('n','o','w'): op |= CS_OPEN_NOW; break; case HASHKEY5('o','t','h','e','r'): auth = 0; break; case HASHKEY6('r','e','m','o','t','e'): op |= CS_OPEN_REMOTE; break; case HASHKEY5('s','h','a','r','e'): op |= CS_OPEN_SHARE; break; case HASHKEY5('s','l','a','v','e'): op |= CS_OPEN_SLAVE; break; case HASHKEY4('t','e','s','t'): op |= CS_OPEN_TEST; break; case HASHKEY5('t','r','u','s','t'): op |= CS_OPEN_TRUST; trust = s; break; case HASHKEY4('u','s','e','r'): user = s ? s : ""; break; default: qual += sfsprintf(qual, sizeof(state->qual) - (qual - state->qual) - 1, "%s%s", qual == state->qual ? "" : "-", b); if (s) *(s - 1) = '='; break; } } while (b = u); } } } if (*type != 't') auth = 0; strncpy(state->type, type, sizeof(state->type) - 1); qual = (qual == state->qual) ? (char*)0 : state->qual; messagef((state->id, NiL, -8, "open: type=%s host=%s serv=%s qual=%s", type, host, serv, qual)); if (host) { /* * validate host */ if (!(state->addr = addr = csaddr(state, host))) { if (serv && !(op & CS_OPEN_CREATE) && *type == 't' && (port = csport(state, type, serv)) >= CS_PORT_MIN && port <= CS_PORT_MAX) { /* * attempt proxy connection */ if (nfd >= 0) { close(nfd); nfd = -1; } if ((fd = state->proxy.addr ? csbind(state, type, state->proxy.addr, state->proxy.port, 0L) : reopen(state, csvar(state, CS_VAR_PROXY, 0))) >= 0) { state->proxy.addr = state->addr; state->proxy.port = state->port; n = sfsprintf(tmp, sizeof(tmp), "\n%s!%s!%d\n\n%s\n%s\n0\n-1\n-1\n", type, host, port, csname(state, 0), error_info.id ? error_info.id : state->id); if (cswrite(state, fd, tmp, n) == n && (n = csread(state, fd, tmp, sizeof(tmp), CS_LINE)) >= 2) { if (tmp[0] == '0' && tmp[1] == '\n') return fd; if (error_info.trace <= -4 && n > 2) { s = tmp; s[n - 1] = 0; while (*s && *s++ != '\n'); messagef((state->id, NiL, -4, "%s error message `%s'", csvar(state, CS_VAR_PROXY, 0), s)); } } close(fd); } } #ifdef EADDRNOTAVAIL errno = EADDRNOTAVAIL; #else errno = ENOENT; #endif goto bad; } if (op & CS_OPEN_LOCAL) { state->flags |= CS_ADDR_LOCAL; state->flags &= ~CS_ADDR_REMOTE; } if (op & CS_OPEN_NOW) state->flags |= CS_ADDR_NOW; if ((op & (CS_OPEN_AGENT|CS_OPEN_REMOTE)) == CS_OPEN_REMOTE) { state->flags |= CS_ADDR_REMOTE; state->flags &= ~CS_ADDR_LOCAL; } if (op & CS_OPEN_SHARE) state->flags |= CS_ADDR_SHARE; if (op & CS_OPEN_SLAVE) state->flags |= CS_DAEMON_SLAVE; if (op & CS_OPEN_TEST) state->flags |= CS_ADDR_TEST; if (op & CS_OPEN_TRUST) state->flags |= CS_ADDR_TRUST; if ((state->flags & CS_ADDR_REMOTE) && (!serv || !strneq(serv, CS_SVC_INET, sizeof(CS_SVC_INET) - 1) && (strtol(serv, &t, 0), *t))) return agent(state, state->host, state->user, state->path); if (s = user) { n = geteuid(); if (*s) { if ((uid = struid(s)) < 0) { uid = strtol(s, &t, 0); if (*t) { errno = EACCES; goto bad; } } if (n && uid != n) { errno = EACCES; goto bad; } } else uid = n; mode &= ~(S_IRWXG|S_IRWXO); } if (s = group) { n = getegid(); if (*s) { if ((gid = strgid(s)) < 0) { gid = strtol(s, &t, 0); if (*t) { errno = EACCES; goto bad; } } if (geteuid() && gid != n) { gid_t* groups; int g; if ((g = getgroups(0, NiL)) <= 0) g = getconf("NGROUPS_MAX"); if (groups = newof(0, gid_t, g, 0)) { for (n = getgroups(g, groups); n >= 0; n--) if (gid == groups[n]) break; free(groups); } else n = -1; if (n < 0) { errno = EACCES; goto bad; } } } else gid = n; mode &= ~S_IRWXO; } if (s = trust) { if (!*s) sid = geteuid(); else if ((sid = struid(s)) < 0) { sid = strtol(s, &t, 0); if (*t) { errno = EACCES; goto bad; } } } if (state->flags & CS_ADDR_SHARE) host = CS_HOST_SHARE; else { host = state->host; if (!(state->flags & CS_ADDR_LOCAL)) { if (*type == 'f') { errno = ENODEV; goto bad; } if (op & CS_OPEN_CREATE) { errno = EROFS; goto bad; } } if (serv && !qual && *type != 'f' && (port = csport(state, type, serv)) != CS_PORT_INVALID) { if (op & CS_OPEN_CREATE) addr = 0; else if (port == CS_PORT_RESERVED || port == CS_PORT_NORMAL) goto bad; if (nfd >= 0) { close(nfd); nfd = -1; } state->control = 0; if ((fd = csbind(state, type, addr, port, 0L)) >= 0) { if (mode != (S_IRWXU|S_IRWXG|S_IRWXO) && csauth(state, fd, NiL, NiL)) { close(fd); return -1; } return fd; } } } } /* * get the mount dir prefix */ if (opath == (b = path = state->mount)) { #ifdef ELOOP errno = ELOOP; #else errno = EINVAL; #endif goto bad; } if (*type == 'f') { if (host && !(state->flags & CS_ADDR_LOCAL)) { errno = ENODEV; goto bad; } b += sfsprintf(b, sizeof(state->mount) - (b - path), "%s", csvar(state, CS_VAR_LOCAL, 0)); if ((op & CS_OPEN_CREATE) && eaccess(path, X_OK) && (mkdir(path, S_IRWXU|S_IRWXG|S_IRWXO) || chmod(path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO))) goto bad; } else { if (op & CS_OPEN_TRUST) { if (!pathaccess(csvar(state, CS_VAR_TRUST, 1), csvar(state, CS_VAR_SHARE, 1), NiL, PATH_EXECUTE, b, sizeof(state->mount) - (b - state->mount))) goto bad; } else if (!pathpath(csvar(state, CS_VAR_SHARE, 0), "", PATH_EXECUTE, b, sizeof(state->mount) - (b - state->mount))) goto bad; b += strlen(b); } /* * add the type */ b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%s", type); if (!host) { *(state->control = b + 1) = 0; if (nfd >= 0) close(nfd); if ((fd = open(path, O_RDONLY)) < 0) { mkmount(state, S_IRWXU|S_IRWXG|S_IRWXO, -1, -1, NiL, NiL, NiL); fd = open(path, O_RDONLY); } if (fd < 0) messagef((state->id, NiL, -1, "open: %s: %s: open error", state->path, path)); return fd; } endtype = b; /* * add the host */ if (strlen(host) <= CS_MNT_MAX) b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%s", host); else { s = csntoa(state, addr); if (strlen(s) <= CS_MNT_MAX) b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%s", s); else { unsigned char* a = (unsigned char*)&addr; b += sfsprintf(b, sizeof(state->mount) - (b - path), "/0x%X.%X.%X.%X", a[0], a[1], a[2], a[3]); } } messagef((state->id, NiL, -8, "%s:%d host=`%s' path=`%s'", __FILE__, __LINE__, host, path)); if (!serv) { *(state->control = b + 1) = 0; if (nfd >= 0) close(nfd); if ((fd = open(path, O_RDONLY)) < 0) messagef((state->id, NiL, -1, "open: %s: %s: open error", state->path, path)); return fd; } endhost = b; /* * add the service */ sfsprintf(b, sizeof(state->mount) - (b - path), "%s/%s/%s/%s%s", CS_SVC_DIR, type, serv, serv, CS_SVC_SUFFIX); if (!pathpath(b, "", PATH_ABSOLUTE|PATH_EXECUTE, tmp, sizeof(tmp)) || stat(tmp, &st)) op |= CS_OPEN_TEST; else { *strrchr(tmp, '/') = 0; if (!(op & CS_OPEN_TRUST)) sid = st.st_uid; if (!st.st_size) op |= CS_OPEN_TEST; } b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%s", serv); endserv = b; /* * add the qualifier and perm */ if (sid >= 0) b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%d-", sid); else b += sfsprintf(b, sizeof(state->mount) - (b - path), "/-"); if (uid >= 0) b += sfsprintf(b, sizeof(state->mount) - (b - path), "%d-", uid); else if (gid >= 0) b += sfsprintf(b, sizeof(state->mount) - (b - path), "-%d", gid); else b += sfsprintf(b, sizeof(state->mount) - (b - path), "-"); #if limit_qualifier_length endqual = endserv + CS_MNT_MAX + 1; #else endqual = state->mount + sizeof(state->mount) - 1; #endif if (qual) { if (b < endqual) *b++ = '-'; while (b < endqual && *qual) *b++ = *qual++; } if (*type == 't' && !auth) { if (b >= endqual) b--; *b++ = CS_MNT_OTHER; } /* * add in the connect stream control */ *b++ = '/'; *b = CS_MNT_STREAM; strcpy(b + 1, CS_MNT_TAIL); messagef((state->id, NiL, -8, "%s:%d %s", __FILE__, __LINE__, state->mount)); state->control = b; /* * create the mount subdirs if necessary */ if ((op & CS_OPEN_CREATE) && mkmount(state, mode, uid, gid, endserv, endhost, endtype)) goto bad; mode &= S_IRWXU|S_IRWXG|S_IRWXO; if (nfd >= 0) { close(nfd); nfd = -1; } if (op & CS_OPEN_MOUNT) { messagef((state->id, NiL, -1, "open(%s,%o) = %d, mount = %s", state->path, op, state->mount)); return 0; } if (*type == 'f') { /* * {fdp} */ if ((fd = doattach(state, path, op, mode, user, opath, tmp, serv, b)) < 0) return -1; } else { /* * {tcp,udp} */ messagef((state->id, NiL, -8, "%s:%d %s", __FILE__, __LINE__, state->mount)); if ((fd = reopen(state, path)) < 0) { /* * check for old single char cs mount */ *(state->control + 1) = 0; if ((fd = reopen(state, path)) < 0) messagef((state->id, NiL, -1, "open: %s: %s: reopen error", state->path, path)); *(state->control + 1) = CS_MNT_TAIL[0]; } if (op & CS_OPEN_CREATE) { if (fd >= 0) { close(fd); errno = EEXIST; return -1; } if (errno != ENOENT && errno != ENOTDIR) return -1; sigcritical(1); *state->control = CS_MNT_LOCK; if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0)) < 0) { if (stat(path, &st)) { messagef((state->id, NiL, -1, "open: %s: %s: creat error", state->path, path)); goto unblock; } if ((CSTIME() - (unsigned long)st.st_ctime) < 2 * 60) { errno = EEXIST; messagef((state->id, NiL, -1, "open: %s: %s: another server won the race", state->path, path)); goto unblock; } if (remove(path)) { messagef((state->id, NiL, -1, "open: %s: %s: remove error", state->path, path)); goto unblock; } if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0)) < 0) { messagef((state->id, NiL, -1, "open: %s: %s: creat error", state->path, path)); goto unblock; } } close(fd); if (!port && (n = strtol(serv, &t, 0)) && t > serv && !*t) port = n; else if (geteuid()) port = CS_NORMAL; else port = CS_RESERVED; if ((fd = csbind(state, type, 0L, port, 0L)) >= 0) { *state->control = CS_MNT_STREAM; remove(path); if (pathsetlink(cspath(state, fd, 0), path)) { messagef((state->id, NiL, -1, "open: %s: %s: link error", cspath(state, fd, 0), path)); close(fd); fd = -1; } } unblock: *state->control = CS_MNT_LOCK; remove(path); sigcritical(0); *state->control = CS_MNT_STREAM; if (fd < 0) return -1; } else if (fd < 0 && ((op & CS_OPEN_TEST) || initiate(state, user, opath, tmp, serv) || (fd = reopen(state, path)) < 0)) { messagef((state->id, NiL, -1, "open: %s: %s: reopen/initiate error", state->path, path)); return -1; } else if (!(op & CS_OPEN_AGENT)) { *state->control = CS_MNT_AUTH; n = csauth(state, fd, path, arg); *state->control = CS_MNT_STREAM; if (n) { close(fd); messagef((state->id, NiL, -1, "open: %s: %s: authentication error", state->path, path)); return -1; } } } /* * fd is open at this point * make sure its not a bogus mount */ if (mode != (S_IRWXU|S_IRWXG|S_IRWXO)) { *state->control = 0; n = stat(path, &st); *state->control = CS_MNT_STREAM; if (n) { messagef((state->id, NiL, -1, "open: %s: %s: stat error", state->path, path)); close(fd); return -1; } if (uid >= 0 && st.st_uid != uid || gid >= 0 && st.st_gid != gid) { close(fd); errno = EPERM; messagef((state->id, NiL, -1, "open: %s: %s: uid/gid error", state->path, path)); return -1; } } return fd; bad: if (nfd >= 0) close(nfd); return -1; }
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 }
int start_command(char* cmd, int critical, int infd, int outfd, char* a0, char* a1, char* a2) { int pid; int savein; int saveout; char** args; char** p; struct argvec vec; if (!a0 && a1) args = (char**)a1; else { initargs(&vec); getargs(&vec, cmd); if (a0) { addarg(&vec, a0); if (a1) { addarg(&vec, a1); if (a2) addarg(&vec, a2); } } endargs(&vec); cmd = vec.argv[0]; args = vec.argv; } if (infd > READ) { if ((savein = dup(READ)) < 0) { note(SYSTEM, "%s: Cannot save standard input", cmd); return -1; } fcntl(savein, F_SETFD, 1); fcntl(infd, F_SETFD, 1); close(READ); if (dup(infd) != READ) { note(SYSTEM, "%s: Cannot redirect standard input", cmd); dup(savein); close(savein); return -1; } } else infd = -1; if (outfd > WRITE) { if ((saveout = dup(WRITE)) < 0) { note(SYSTEM, "%s: Cannot save standard output", cmd); return -1; } fcntl(saveout, F_SETFD, 1); fcntl(outfd, F_SETFD, 1); close(WRITE); if (dup(outfd) != WRITE) { note(SYSTEM, "%s: Cannot redirect standard input", cmd); dup(savein); close(savein); dup(saveout); close(saveout); return -1; } } else outfd = -1; if (state.var.debug) { note(DEBUG|PROMPT, "spawn:"); for (p = args; *p; p++) printf(" \"%s\"", *p); printf("\n"); } if (critical) sigcritical(critical); if ((pid = spawnvp(cmd, args)) == -1) note(SYSTEM, "%s", cmd); if (critical) sigcritical(0); if (infd > READ) { close(READ); if (dup(savein) != READ) { note(SYSTEM, "%s: Cannot restore standard input", cmd); return -1; } close(savein); fcntl(READ, F_SETFD, 0); } if (outfd > WRITE) { close(WRITE); if (dup(saveout) != WRITE) { note(SYSTEM, "%s: Cannot restore standard output", cmd); return -1; } close(saveout); fcntl(WRITE, F_SETFD, 0); } return pid; }