/***********************************************************************
* *
* 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 *
* (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 Bell Laboratories
*
* remote coshell server main
* except for history this program would
* have been called shmux (with a TeX x)
*/
#include "service.h"
#include <namval.h>
static const char usage[] =
"[-?\n@(#)$Id: coshell (AT&T Research) 2007-10-23 $\n]"
"[+NAME?coshell - network shell coprocess server]"
"[+DESCRIPTION?\bcoshell\b is a local network shell coprocess server for "
"programs using \bcoshell\b(3). There is one \bcoshell\b server per "
"user. This server runs as a daemon on the user's home host, and only "
"processes running on the home host have access to the server. The "
"server controls a background \bksh\b(1) shell process, initiated by "
"\brsh\b(1) or \bssh\b(1), on each of the connected hosts. The "
"environment of the local host shell is inherited from the server "
"whereas the environment of remote shells is initialized by \b.profile\b "
"and \b$ENV\b. The shells run with the \bksh\b \b--bgnice\b and "
"\b--monitor\b options.]"
"[+?Job requests are accepted from user processes on the local host and "
"are executed on the connected hosts. \bstdout\b, \bstderr\b, \bFPATH\b, "
"\bNPROC\b (see ENVIRONMENT), \bPWD\b, \bPATH\b, \bVPATH\b, \bvpath\b, "
"\bumask\b and the environment variables listed in \bCOEXPORT\b (see "
"ENVIRONMENT) are set to match requesting user values. \bstdin\b is set "
"jobs. Job scheduling is based on load and idle time information "
"generated by the \bss\b(1) system status daemon. This information is "
"updated every 60 seconds on average.]"
"[+?The server is started by running \acoshell +\a. The command exits "
"after a child server process is forked in the background. The optional "
"\ainfo\a arguments name files containing local network host information "
"which may be generated from two shell scripts \bgenlocal\b and "
"\bgenshare\b under the subdirectory bin of the installation root "
"directory. If no files are specified then the default \alocal\a is "
"used. The local network is comprised of hosts sharing the same file "
"name space.]"
"[+?Attributes used by \bcoshell\b can be categorized into two types, "
"global and host-specific. The global attributes control \bcoshell\b and "
"are not associated with any particular host. Attribute value pairs, not "
"including readonly ones, may be specified in the local network host "
"information files, in \bCOATTRIBUTES \b (see ENVIRONMENT) or may be "
"in \bCOATTRIBUTES\b. Attribute names must match "
"[a-zA-Z_]][a-zA-Z_0-9]]*. In the following description on these "
"attributes, \ahost\a may be an actual host name or a comma separated "
"list of attribute value pairs specified in \bCOATTRIBUTES\b.]"
"\n"
"\n+ | - | -\acommand\a [ arg ... ]\n"
"\n"
"[+SEE ALSO?\bnmake\b(1), \bksh\b(1), \bss\b(1)]"
;
#ifndef PATHCHECK
#endif
{
"3d", "ksh", "sh"
};
/*
* initialize the server state
*/
static void*
{
register int n;
register char* s;
for (n = 0; n < 10; n++) TOSS;
n = NGROUPS_MAX;
n = 0;
/*
* initialze the shell table
*/
state.profile = strdup("{ . ./.profile; eval test -f \\$ENV \\&\\& . \\$ENV; } >/dev/null 2>&1 </dev/null");
/*
* load the local net configuration
*/
/*
* load the access controls if any
*/
/*
* set the job limits
*/
/*
* bias the local host so it can generate more work
*/
{
}
/*
* get the shell path
*/
{
for (n = 0;;)
{
break;
if (++n >= elementsof(coshell))
}
}
/*
* parameterize the shell path name on state.home->type
*/
/*
* initialize the remote shell path
*/
/*
* set up the mesg and pump connect streams
*/
/*
* open the local shell
*/
return((void*)&state);
}
/*
* accept a new user connection
* no id checks since we can trust the connect stream path access
*/
static int
{
{
}
return(0);
}
#ifdef O_NONBLOCK
static int
{
int flags;
return flags;
}
#else
#define nonblocking(f)
#endif
/*
* service a read event
*/
static int
{
register int n;
register int i;
char* s;
char* t;
char* x;
char* e;
int n1;
int n2;
{
case ANON:
case SHELL:
#ifdef O_NONBLOCK
#endif
#ifdef O_NONBLOCK
#endif
if (n <= 0)
{
#ifdef O_NONBLOCK
else
#endif
break;
}
s[n - 1] = 0;
/*
* parse the message(s)
*/
do
{
if (x = strchr(s, '\n')) *x++ = 0;
while (isspace(*s)) s++;
if (!(i = *s++)) continue;
while (isspace(*s)) s++;
while (*s && !isspace(*s)) s++;
while (isspace(*s)) s++;
/*
* now interpret the message
*/
switch (i)
{
case 'j':
/*
* <s> is the job pid
*/
if (!sp) break;
{
}
break;
case 'n':
/*
* <s> is the name of the anonymous shell and its pid
*/
{
{
/*
* nuke the zombie that kicked this shell
*/
while (*s && !isspace(*s)) s++;
while (isspace(*s)) s++;
}
else
{
sp = 0;
}
}
break;
case 'r':
/*
* <s> is the shell rating
*/
if (!sp) break;
break;
case 'x':
/*
* <s> is the job exit code and user,sys times
*/
if (!sp) break;
for (;;)
{
if (t <= s) break;
if (t <= s) break;
}
{
{
}
}
else
{
nuke:
/*
* nuke the zombies
*/
}
break;
}
} while (s = x);
break;
case DEST:
if (csread(fd, s = state.buf, 13, CS_EXACT) != 13 || s[0] != '#' || (jp = state.job + (int)strtol(s + 1, NiL, 10)) > state.jobmax || fstat(state.con[fd].info.pass.fd = (int)strtol(s + 7, NiL, 10), &st))
else
{
}
break;
case IDENT:
else
{
if (i == 3)
{
}
else
{
}
{
}
break;
}
while (--i >= 0)
break;
case INIT:
if (csread(fd, s = cmd, 7, CS_EXACT) != 7 || s[0] != '#' || (i = (int)strtol(s + 1, NiL, 10)) && (i < 0 || i > state.buflen || csread(fd, state.buf, i, CS_EXACT) != i))
{
break;
}
x = 0;
if (i)
{
s[i] = 0;
while (e = strchr(s, '\n'))
{
*e++ = 0;
if (strneq(s, CO_ENV_ATTRIBUTES, sizeof(CO_ENV_ATTRIBUTES) - 1) && s[sizeof(CO_ENV_ATTRIBUTES) - 1] == '=')
{
x = s + sizeof(CO_ENV_ATTRIBUTES);
if (*x == '\'')
{
i = 1;
s = t = ++x;
while (*s = *t++)
{
if (*s == '\'') i = !i;
else if (i || *s != '\\') s++;
else if (!(*s++ = *t++)) break;
}
}
}
{
if (streq(s, CO_OPT_COMMAND))
else if (streq(s, CO_OPT_DUP))
else if (strneq(s, CO_OPT_HOME, sizeof(CO_OPT_HOME) - 1) && s[sizeof(CO_OPT_HOME) - 1] == '=' && (sp = search(GET, s + sizeof(CO_OPT_HOME), NiL, NiL)))
else if (strneq(s, CO_OPT_INDIRECT, sizeof(CO_OPT_INDIRECT) - 1) && s[sizeof(CO_OPT_INDIRECT) - 1] == '=')
}
s = e;
}
}
{
break;
}
{
{
break;
}
else
{
}
if (state.con[fd].info.user.flags & USER_DUP) state.con[fd].info.user.fds[2] = state.con[fd].info.user.fds[1];
{
break;
}
}
else if (!fstat(state.con[fd].info.user.fds[1], &st) && !fstat(state.con[fd].info.user.fds[2], &ts) && st.st_ino == ts.st_ino && st.st_dev == ts.st_dev)
{
}
{
{
break;
}
}
break;
case MESG:
{
i = fds[0];
{
#ifdef O_NONBLOCK
#endif
csfd(i, CS_POLL_READ);
}
else close(i);
}
break;
case PASS:
else
{
{
{
}
}
{
}
}
break;
case PUMP:
{
i = fds[0];
csfd(i, CS_POLL_READ);
}
break;
case SCHED:
/*HERE*/
break;
case USER:
{
break;
}
{
}
{
break;
}
n = error_info.errors;
{
case 'e':
case 'E':
n = error_info.errors;
break;
case 'k':
case 'K':
error_info.errors++;
else
{
{
if (n1) break;
}
n = error_info.errors;
}
break;
case 's':
case 'S':
error_info.errors++;
break;
default:
break;
}
if (isupper(i))
{
}
break;
}
return(0);
}
/*
* wake up to check for hung shells and jobs on busy hosts
*/
static int
{
shellcheck();
return(0);
}
/*
* indirect coshell initialization
*/
static void*
{
int* pass;
return((void*)pass);
}
/*
* indirect coshell data pump
*/
static int
{
register int n;
register int pd;
{
if (!n) return(0);
}
return(0);
drop:
{
*pass = 0;
}
return(-1);
}
/*
* coshell main
*/
int
{
register int n;
register int fd;
register int i;
int pfd;
int d;
char* s;
char* t;
exit(1);
/*
* optget() for self documentation
* "options" passed to the server as commands
*/
{
{
case ':':
break;
case '?':
break;
}
break;
}
if (error_info.errors)
/*
* check for alternate connect stream
*/
{
}
/*
* check for alternate stdin
*/
{
argv++;
{
close(0);
if (dup(i))
}
}
/*
* COMMAND interactive or argv command processor to server (-*)
* SERVER the server (+*)
*/
{
n = SERVER;
argv--;
}
if (n != SERVER)
{
{
}
{
for (i = 0; i < elementsof(fds); i++)
fds[i] = i;
if (n == COMMAND)
i--;
}
if ((state.indirect.con || !cssend(fd, fds, i)) && csread(fd, s, 7, CS_EXACT) == 7 && s[0] == '#' && !(errno = (int)strtol(s + 1, NiL, 10))) do
{
{
}
s += 7;
if ((t = getenv(CO_ENV_ATTRIBUTES)) && *t) s += sfsprintf(s, state.buflen - (s - state.buf), ",%s", t);
{
{
d = 1;
}
else d = 2;
}
if (n == COMMAND)
{
{
for (; d > 0 && csrecv(pfd, NiL, fds, 1) == 1 && csread(fds[0], s, 7, CS_EXACT) == 7 && s[0] == '#'; d--)
else break;
if (d) break;
}
}
close(0);
} while (0);
}
/*
* we are the server
*/
/*NOTREACHED*/
return 1;
}