/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1990-2011 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * * by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * * * * Information and Software Systems Research * * AT&T Research * * Florham Park NJ * * * * Glenn Fowler * * * ***********************************************************************/ #pragma prototyped /* * Glenn Fowler * AT&T Research * * open a new coshell */ #include "colib.h" #include #include #include #include static const Namval_t options[] = { "cross", CO_CROSS, "debug", CO_DEBUG, "devfd", CO_DEVFD, "ignore", CO_IGNORE, "orphan", CO_ORPHAN, "silent", CO_SILENT, "separate", CO_SEPARATE, "service", CO_SERVICE, 0, 0 }; Costate_t state = { "libcoshell:coshell" }; /* * called when ident sequence hung */ static void hung(int sig) { NoP(sig); close(sffileno(state.current->msgfp)); } /* * close all open coshells */ static void clean(void) { coclose(NiL); } #ifdef SIGCONT /* * pass job control signals to the coshell and self */ static void stop(int sig) { cokill(NiL, NiL, sig); signal(sig, SIG_DFL); sigunblock(sig); kill(getpid(), sig); cokill(NiL, NiL, SIGCONT); signal(sig, stop); } #endif /* * called by stropt() to set options */ static int setopt(void* handle, register const void* p, int n, const char* v) { Coshell_t* co = (Coshell_t*)handle; Coservice_t* cs; char* s; char** a; NoP(v); if (p) { if (n) { co->flags |= ((Namval_t*)p)->value; if (((Namval_t*)p)->value == CO_SERVICE && v && (cs = vmnewof(co->vm, 0, Coservice_t, 1, 2 * strlen(v)))) { a = cs->argv; *a++ = s = cs->path = cs->name = (char*)(cs + 1); while (*s = *v++) if (*s++ == ':') { *(s - 1) = 0; if (*v == '-') { v++; if (*v == '-') v++; } if (strneq(v, "command=", 8)) cs->path = s + 8; else if (strneq(v, "state=", 6)) cs->db = s + 6; else if (strneq(v, "db=", 3)) cs->db = s + 3; else if (a < &cs->argv[elementsof(cs->argv)-2] && *v && *v != ':') { *a++ = s; *s++ = '-'; *s++ = '-'; } } if (cs->db) *a++ = cs->db; *a = 0; cs->next = co->service; co->service = cs; } } else co->mask |= ((Namval_t*)p)->value; } return 0; } Coshell_t* coopen(const char* path, int flags, const char* attributes) { register Coshell_t* co; register char* s; register int i; char* t; int n; Proc_t* proc; Cojob_t* cj; Vmalloc_t* vm; Sfio_t* sp; Sig_handler_t handler; int pio[4]; long ops[5]; char devfd[16]; char evbuf[sizeof(CO_ENV_MSGFD) + 8]; char* av[8]; char* ev[2]; static char* sh[] = { 0, 0, "ksh", "sh", "/bin/sh" }; if (!state.type && (!(s = getenv(CO_ENV_TYPE)) || !(state.type = strdup(s)))) state.type = ""; if ((flags & CO_ANY) && (co = state.coshells)) return co; if (!(vm = vmopen(Vmdcheap, Vmbest, 0)) || !(co = vmnewof(vm, 0, Coshell_t, 1, 0))) { if (vm) vmclose(vm); errormsg(state.lib, ERROR_LIBRARY|2, "out of space"); return 0; } co->vm = vm; co->index = ++state.index; stropt(getenv(CO_ENV_OPTIONS), options, sizeof(*options), setopt, co); if (attributes) stropt(attributes, options, sizeof(*options), setopt, co); co->flags |= ((flags | CO_DEVFD) & ~co->mask); if (co->flags & CO_SEPARATE) { co->flags &= ~CO_SEPARATE; co->mode |= CO_MODE_SEPARATE; } co->flags |= CO_INIT; if (co->mode & CO_MODE_SEPARATE) { flags = 0; proc = 0; } else { for (i = 0; i < elementsof(pio); i++) pio[i] = -1; if (pipe(&pio[0]) < 0 || pipe(&pio[2]) < 0) { errormsg(state.lib, ERROR_LIBRARY|ERROR_SYSTEM|2, "cannot allocate pipes"); goto bad; } if (flags & CO_SHELL) for (i = 0; i < elementsof(pio); i++) if (pio[i] < 10 && (n = fcntl(pio[i], F_DUPFD, 10)) >= 0) { close(pio[i]); pio[i] = n; } co->cmdfd = pio[1]; co->gsmfd = pio[3]; if (!(co->msgfp = sfnew(NiL, NiL, 256, pio[2], SF_READ))) { errormsg(state.lib, ERROR_LIBRARY|ERROR_SYSTEM|2, "cannot allocate message stream"); goto bad; } sfdcslow(co->msgfp); ops[0] = PROC_FD_DUP(pio[0], 0, PROC_FD_PARENT); ops[1] = PROC_FD_CLOSE(pio[1], PROC_FD_CHILD); ops[2] = PROC_FD_CLOSE(pio[2], PROC_FD_CHILD); ops[3] = PROC_FD_CLOSE(pio[3], PROC_FD_PARENT); ops[4] = 0; sfsprintf(devfd, sizeof(devfd), "/dev/fd/%d", pio[0]); flags = !access(devfd, F_OK); } sh[0] = (char*)path; sh[1] = getenv(CO_ENV_SHELL); for (i = 0; i < elementsof(sh); i++) if ((s = sh[i]) && *s && (s = strdup(s))) { if ((n = tokscan(s, NiL, " %v ", av, elementsof(av) - 1)) > 0) { if (t = strrchr(s = av[0], '/')) av[0] = t + 1; if (flags || (co->flags & CO_DEVFD) && strmatch(s, "*ksh*")) av[n++] = devfd; av[n] = 0; sfsprintf(evbuf, sizeof(evbuf), "%s=%d", CO_ENV_MSGFD, co->gsmfd); ev[0] = evbuf; ev[1] = 0; if ((co->mode & CO_MODE_SEPARATE) || (proc = procopen(s, av, ev, ops, (co->flags & (CO_SHELL|CO_ORPHAN)) ? (PROC_ORPHAN|PROC_DAEMON|PROC_IGNORE) : (PROC_DAEMON|PROC_IGNORE)))) { if (!state.sh) state.sh = strdup(s); free(s); if (proc) { co->pid = proc->pid; procfree(proc); } break; } } free(s); } if (i >= elementsof(sh)) { errormsg(state.lib, ERROR_LIBRARY|ERROR_SYSTEM|2, "cannot execute"); goto bad; } if (!(co->mode & CO_MODE_SEPARATE)) { /* * send the shell identification sequence */ if (!(sp = sfstropen())) { errormsg(state.lib, ERROR_LIBRARY|2, "out of buffer space"); goto bad; } sfprintf(sp, "#%05d\n%s='", 0, CO_ENV_ATTRIBUTES); if (t = getenv(CO_ENV_ATTRIBUTES)) { coquote(sp, t, 0); if (attributes) sfprintf(sp, ","); } if (attributes) coquote(sp, attributes, 0); sfprintf(sp, "'\n"); sfprintf(sp, coident, pio[3]); i = sfstrtell(sp); sfstrseek(sp, 0, SEEK_SET); sfprintf(sp, "#%05d\n", i - 7); i = write(co->cmdfd, sfstrbase(sp), i) != i; sfstrclose(sp); if (i) { errormsg(state.lib, ERROR_LIBRARY|ERROR_SYSTEM|2, "cannot write initialization message"); goto nope; } state.current = co; handler = signal(SIGALRM, hung); i = alarm(30); if (!(s = sfgetr(co->msgfp, '\n', 1))) { if (errno == EINTR) errormsg(state.lib, ERROR_LIBRARY|ERROR_SYSTEM|2, "identification message read timeout"); goto nope; } alarm(i); signal(SIGALRM, handler); if (co->flags & CO_DEBUG) errormsg(state.lib, 2, "coshell %d shell path %s identification \"%s\"", co->index, state.sh, s); switch (*s) { case 'o': co->flags |= CO_OSH; /*FALLTHROUGH*/ case 'b': s = cobinit; break; case 'k': co->flags |= CO_KSH; s = cokinit; break; case 'i': /* NOTE: 'i' is obsolete */ case 's': co->flags |= CO_SERVER; co->pid = 0; for (;;) { if (t = strchr(s, ',')) *t = 0; if (streq(s, CO_OPT_ACK)) co->mode |= CO_MODE_ACK; else if (streq(s, CO_OPT_INDIRECT)) co->mode |= CO_MODE_INDIRECT; if (!(s = t)) break; s++; } if (!(co->mode & CO_MODE_INDIRECT)) wait(NiL); break; default: goto nope; } if (s) { if (!(cj = coexec(co, s, 0, NiL, NiL, NiL)) || cowait(co, cj, -1) != cj) { errormsg(state.lib, ERROR_LIBRARY|ERROR_SYSTEM|2, "initialization message exec error"); goto nope; } co->total = 0; co->user = 0; co->sys = 0; } } co->flags &= ~CO_INIT; fcntl(pio[1], F_SETFD, FD_CLOEXEC); fcntl(pio[2], F_SETFD, FD_CLOEXEC); co->next = state.coshells; state.coshells = co; if (!(co->flags & CO_SHELL)) { #ifdef SIGCONT #ifdef SIGTSTP signal(SIGTSTP, stop); #endif #ifdef SIGTTIN signal(SIGTTIN, stop); #endif #ifdef SIGTTOU signal(SIGTTOU, stop); #endif #endif if (!state.init) { state.init = 1; atexit(clean); } } return co; bad: n = errno; if (co->msgfp) { sfclose(co->msgfp); pio[2] = -1; } for (i = 0; i < elementsof(pio); i++) if (pio[i] >= 0) close(pio[i]); coclose(co); errno = n; return 0; nope: i = errno; if (!(s = sh[1]) || (s = (t = strrchr(s, '/')) ? (t + 1) : s) && !strmatch(s, "?(k)sh") && !streq(s, CO_ID)) error(2, "export %s={ksh,sh,%s}", CO_ENV_SHELL, CO_ID); coclose(co); errno = i; return 0; } /* * set coshell attributes */ int coattr(Coshell_t* co, const char* attributes) { return 0; }