diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2012-06-24 22:28:35 +0000 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2012-06-24 22:28:35 +0000 |
commit | 3950ffe2a485479f6561c27364d3d7df5a21d124 (patch) | |
tree | 468c6e14449d1b1e279222ec32f676b0311917d2 /src/lib/libcoshell/coopen.c | |
download | ksh-upstream.tar.gz |
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/lib/libcoshell/coopen.c')
-rw-r--r-- | src/lib/libcoshell/coopen.c | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/src/lib/libcoshell/coopen.c b/src/lib/libcoshell/coopen.c new file mode 100644 index 0000000..07dcb87 --- /dev/null +++ b/src/lib/libcoshell/coopen.c @@ -0,0 +1,411 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1990-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * open a new coshell + */ + +#include "colib.h" + +#include <namval.h> +#include <proc.h> +#include <sfdisc.h> +#include <tok.h> + +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, CO_ENV_MSGFD, pio[3], CO_ENV_MSGFD, CO_ENV_MSGFD, CO_ENV_MSGFD); + 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; +} |