Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/coshell/main.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1990-2011 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* Glenn Fowler <[email protected]> *
18
* *
19
***********************************************************************/
20
#pragma prototyped
21
/*
22
* Glenn Fowler
23
* AT&T Bell Laboratories
24
*
25
* remote coshell server main
26
* except for history this program would
27
* have been called shmux (with a TeX x)
28
*/
29
30
#include "service.h"
31
32
#include <namval.h>
33
34
static const char usage[] =
35
"[-?\n@(#)$Id: coshell (AT&T Research) 2007-10-23 $\n]"
36
USAGE_LICENSE
37
"[+NAME?coshell - network shell coprocess server]"
38
"[+DESCRIPTION?\bcoshell\b is a local network shell coprocess server for "
39
"programs using \bcoshell\b(3). There is one \bcoshell\b server per "
40
"user. This server runs as a daemon on the user's home host, and only "
41
"processes running on the home host have access to the server. The "
42
"server controls a background \bksh\b(1) shell process, initiated by "
43
"\brsh\b(1) or \bssh\b(1), on each of the connected hosts. The "
44
"environment of the local host shell is inherited from the server "
45
"whereas the environment of remote shells is initialized by \b.profile\b "
46
"and \b$ENV\b. The shells run with the \bksh\b \b--bgnice\b and "
47
"\b--monitor\b options.]"
48
"[+?Job requests are accepted from user processes on the local host and "
49
"are executed on the connected hosts. \bstdout\b, \bstderr\b, \bFPATH\b, "
50
"\bNPROC\b (see ENVIRONMENT), \bPWD\b, \bPATH\b, \bVPATH\b, \bvpath\b, "
51
"\bumask\b and the environment variables listed in \bCOEXPORT\b (see "
52
"ENVIRONMENT) are set to match requesting user values. \bstdin\b is set "
53
"to \v/dev/null\v; \bcoshell\b does not directly support interactive "
54
"jobs. Job scheduling is based on load and idle time information "
55
"generated by the \bss\b(1) system status daemon. This information is "
56
"updated every 60 seconds on average.]"
57
"[+?The server is started by running \acoshell +\a. The command exits "
58
"after a child server process is forked in the background. The optional "
59
"\ainfo\a arguments name files containing local network host information "
60
"which may be generated from two shell scripts \bgenlocal\b and "
61
"\bgenshare\b under the subdirectory bin of the installation root "
62
"directory. If no files are specified then the default \alocal\a is "
63
"used. The local network is comprised of hosts sharing the same file "
64
"name space.]"
65
"[+?Attributes used by \bcoshell\b can be categorized into two types, "
66
"global and host-specific. The global attributes control \bcoshell\b and "
67
"are not associated with any particular host. Attribute value pairs, not "
68
"including readonly ones, may be specified in the local network host "
69
"information files, in \bCOATTRIBUTES \b (see ENVIRONMENT) or may be "
70
"set/added using \acoshell -a\a, and may be referenced in an expression "
71
"in \bCOATTRIBUTES\b. Attribute names must match "
72
"[a-zA-Z_]][a-zA-Z_0-9]]*. In the following description on these "
73
"attributes, \ahost\a may be an actual host name or a comma separated "
74
"list of attribute value pairs specified in \bCOATTRIBUTES\b.]"
75
76
"\n"
77
"\n+ | - | -\acommand\a [ arg ... ]\n"
78
"\n"
79
80
"[+SEE ALSO?\bnmake\b(1), \bksh\b(1), \bss\b(1)]"
81
;
82
83
#ifndef PATHCHECK
84
#define PATHCHECK CO_ID
85
#endif
86
87
#define CO_OPT_COMMAND "command"
88
#define CO_OPT_DUP "dup"
89
#define CO_OPT_HOME "home"
90
91
static char* coshell[] = /* shell to run on other side */
92
{
93
"3d", "ksh", "sh"
94
};
95
96
State_t state; /* program state */
97
98
/*
99
* initialize the server state
100
*/
101
102
static void*
103
init(void* handle, int fdmax)
104
{
105
register int n;
106
register char* s;
107
108
NoP(handle);
109
message((-1, "init pid=%d", getpid()));
110
state.clock = state.start = state.toss = cs.time;
111
for (n = 0; n < 10; n++) TOSS;
112
state.fdtotal = fdmax;
113
if (!(state.con = newof(0, Connection_t, state.fdtotal, 0)))
114
error(3, "out of space [con]");
115
state.con[0].type = POLL;
116
if ((n = getgroups(0, NiL)) <= 0)
117
n = NGROUPS_MAX;
118
if (!(state.gids = newof(0, gid_t, n + 2, 0)))
119
error(3, "out of space [gids]");
120
if ((n = getgroups(n, state.gids + 1)) < 0)
121
n = 0;
122
state.gids[n + 1] = -1;
123
state.gids[0] = getegid();
124
state.uid = geteuid();
125
n = state.fdtotal / 2;
126
if (!(state.job = state.jobnext = newof(0, Cojob_t, n, 0)))
127
error(3, "out of space [job]");
128
state.jobmax = state.jobnext += n - 1;
129
130
/*
131
* initialze the shell table
132
*/
133
134
state.busy = BUSY;
135
state.grace = GRACE;
136
state.maxidle = INT_MAX;
137
state.pool = ((s = getenv(CO_ENV_PROC)) && *s) ? (int)strtol(s, NiL, 0) : POOL;
138
state.profile = strdup("{ . ./.profile; eval test -f \\$ENV \\&\\& . \\$ENV; } >/dev/null 2>&1 </dev/null");
139
if (!(state.home = search(DEF|NEW, csname(0), NiL, NiL)))
140
error(3, "cannot get local host address");
141
state.shell = state.shellnext = state.home;
142
message((-1, "local name is %s", state.home->name));
143
144
/*
145
* load the local net configuration
146
*/
147
148
do info(DEF|NEW, *state.argv); while (*state.argv && *++state.argv);
149
150
/*
151
* load the access controls if any
152
*/
153
154
info(SET, NiL);
155
156
/*
157
* set the job limits
158
*/
159
160
if (state.perserver <= 0 || state.perserver > n)
161
state.perserver = n;
162
if (state.peruser <= 0 && (state.peruser = (3 * (state.pool - 1)) / 2) <= 0)
163
state.peruser = 1;
164
if (state.percpu <= 0 && (state.percpu = state.peruser / 4) <= 2)
165
state.percpu = 2;
166
167
/*
168
* bias the local host so it can generate more work
169
*/
170
171
if (!state.indirect.con && state.home->idle)
172
{
173
state.home->idle = 0;
174
if (!(state.home->flags & SETBIAS)) state.home->bias *= 4;
175
}
176
177
/*
178
* get the shell path
179
*/
180
181
if (!(s = state.sh))
182
{
183
s = state.buf;
184
for (n = 0;;)
185
{
186
if (pathpath(coshell[n], NiL, PATH_ABSOLUTE|PATH_REGULAR|PATH_EXECUTE, s, state.buflen))
187
break;
188
if (++n >= elementsof(coshell))
189
error(3, "shell not found");
190
}
191
}
192
193
/*
194
* parameterize the shell path name on state.home->type
195
*/
196
197
pathrepl(s, 0, state.home->type, "%s");
198
if (!(state.sh = strdup(s)))
199
error(3, "out of space [shell path]");
200
message((-1, "parameterized shell path is %s", state.sh));
201
202
/*
203
* initialize the remote shell path
204
*/
205
206
if (!(state.remote = strdup(CS_REMOTE_SHELL)))
207
error(3, "out of space [remote shell path]");
208
message((-1, "remote shell path is %s", state.remote));
209
210
/*
211
* set up the mesg and pump connect streams
212
*/
213
214
state.mesg = stream(MESG, "mesg");
215
state.pump = stream(PUMP, "pump");
216
217
/*
218
* open the local shell
219
*/
220
221
shellopen(state.home, 2);
222
return((void*)&state);
223
}
224
225
/*
226
* accept a new user connection
227
* no id checks since we can trust the connect stream path access
228
*/
229
230
static int
231
user(void* handle, int fd, Cs_id_t* id, int clone, char** argv)
232
{
233
NoP(handle);
234
NoP(id);
235
NoP(clone);
236
NoP(argv);
237
if (state.indirect.con)
238
{
239
state.con[fd].type = INIT;
240
state.con[fd].info.user.fds[0] = fd;
241
state.con[fd].info.user.fds[1] = -1;
242
state.con[fd].info.user.fds[2] = -1;
243
if (cswrite(fd, "#00000\n", 7) != 7) return(-1);
244
}
245
else state.con[fd].type = IDENT;
246
state.con[fd].info.user.flags = USER_IDENT;
247
state.con[fd].info.user.home = 0;
248
state.con[fd].info.user.pump = 0;
249
state.con[fd].info.user.expr = 0;
250
return(0);
251
}
252
253
#ifdef O_NONBLOCK
254
255
static int
256
nonblocking(int fd)
257
{
258
int flags;
259
260
if ((flags = fcntl(fd, F_GETFL)) < 0)
261
return flags;
262
return fcntl(fd, F_SETFL, flags|O_NONBLOCK);
263
}
264
265
#else
266
267
#define nonblocking(f)
268
269
#endif
270
271
/*
272
* service a read event
273
*/
274
275
static int
276
service(void* handle, register int fd)
277
{
278
register int n;
279
register int i;
280
char* s;
281
char* t;
282
char* x;
283
char* e;
284
int n1;
285
int n2;
286
Coshell_t* sp;
287
Cojob_t* jp;
288
Cs_id_t id;
289
struct stat st;
290
struct stat ts;
291
int fds[5];
292
char cmd[256];
293
294
NoP(handle);
295
switch (state.con[fd].type)
296
{
297
case ANON:
298
case SHELL:
299
#ifdef O_NONBLOCK
300
if (state.con[fd].flags >= 0 && fcntl(fd, F_SETFL, state.con[fd].flags|O_NONBLOCK) < 0)
301
state.con[fd].flags = -1;
302
#endif
303
n = csread(fd, s = state.buf, state.buflen, CS_LINE);
304
#ifdef O_NONBLOCK
305
if (state.con[fd].flags >= 0 && fcntl(fd, F_SETFL, state.con[fd].flags) < 0)
306
state.con[fd].flags = -1;
307
#endif
308
if (n <= 0)
309
{
310
#ifdef O_NONBLOCK
311
if (n < 0 && errno == EAGAIN)
312
state.con[fd].error++;
313
else
314
#endif
315
drop(fd);
316
break;
317
}
318
s[n - 1] = 0;
319
320
/*
321
* parse the message(s)
322
*/
323
324
sp = state.con[fd].info.shell;
325
do
326
{
327
if (x = strchr(s, '\n')) *x++ = 0;
328
while (isspace(*s)) s++;
329
if (!(i = *s++)) continue;
330
message((-3, "%s-message: %s", sp ? sp->name : "", s - 1));
331
while (isspace(*s)) s++;
332
if ((jp = state.job + (int)strtol(s, NiL, 0)) > state.jobmax) continue;
333
while (*s && !isspace(*s)) s++;
334
while (isspace(*s)) s++;
335
336
/*
337
* now interpret the message
338
*/
339
340
switch (i)
341
{
342
case 'j':
343
/*
344
* <s> is the job pid
345
*/
346
347
if (!sp) break;
348
i = jp->pid;
349
jp->pid = (int)strtol(s, NiL, 0);
350
if (i == WARP) goto nuke;
351
if (jp->cmd)
352
{
353
free(jp->cmd);
354
jp->cmd = 0;
355
state.jobwait--;
356
}
357
break;
358
case 'n':
359
/*
360
* <s> is the name of the anonymous shell and its pid
361
*/
362
363
if (!sp && (sp = search(GET, s, NiL, NiL)))
364
{
365
if (sp->fd < 0)
366
{
367
/*
368
* nuke the zombie that kicked this shell
369
*/
370
371
if (sp->fd != -1) waitpid(-sp->fd, NiL, 0);
372
state.con[fd].type = SHELL;
373
state.con[fd].info.shell = sp;
374
while (*s && !isspace(*s)) s++;
375
while (isspace(*s)) s++;
376
sp->pid = (int)strtol(s, NiL, 0);
377
sp->fd = fd;
378
sp->open++;
379
state.shellwait--;
380
state.shells++;
381
}
382
else
383
{
384
sp = 0;
385
drop(fd);
386
}
387
}
388
break;
389
case 'r':
390
/*
391
* <s> is the shell rating
392
*/
393
394
if (!sp) break;
395
sfsprintf(cmd, sizeof(cmd), "%s,%s", s, sp->name);
396
search(DEF, cmd, NiL, NiL);
397
break;
398
case 'x':
399
/*
400
* <s> is the job exit code and user,sys times
401
*/
402
403
if (!sp) break;
404
jp->status = strtol(s, &t, 10);
405
jp->sig = 0;
406
for (;;)
407
{
408
if (t <= s) break;
409
for (s = t; isalpha(*s) || isspace(*s); s++);
410
jp->user += strelapsed(s, &t, CO_QUANT);
411
if (t <= s) break;
412
for (s = t; isalpha(*s) || isspace(*s); s++);
413
jp->sys += strelapsed(s, &t, CO_QUANT);
414
}
415
if (jp->pid == START)
416
{
417
if (jp->cmd)
418
{
419
free(jp->cmd);
420
jp->cmd = 0;
421
state.jobwait--;
422
}
423
jp->pid = WARP;
424
}
425
else
426
{
427
nuke:
428
/*
429
* nuke the zombies
430
*/
431
432
sfsprintf(cmd, sizeof(cmd), "wait %d\n", jp->pid);
433
cswrite(fd, cmd, strlen(cmd));
434
if (--jp->ref <= 0) jobdone(jp);
435
}
436
break;
437
}
438
} while (s = x);
439
if (sp && sp->running)
440
jobcheck(sp);
441
break;
442
case DEST:
443
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))
444
drop(fd);
445
else
446
{
447
state.con[fd].type = PASS;
448
state.con[fd].info.pass.job = jp->pid ? jp : 0;
449
state.con[fd].info.pass.serialize = (jp->flags & CO_SERIALIZE) ? sfstropen() : (Sfio_t*)0;
450
}
451
break;
452
case IDENT:
453
if ((i = csrecv(fd, &id, fds, elementsof(fds))) < 0) /* error */;
454
else if (i < 3) errno = EBADF;
455
else if (cswrite(fd, "#00000\n", 7) != 7) /* error */;
456
else
457
{
458
if (i == 3)
459
{
460
close(fds[0]);
461
fds[0] = n = fd;
462
state.con[n].info.user.flags = 0;
463
}
464
else
465
{
466
drop(fd);
467
csfd(n = fds[0], CS_POLL_READ);
468
fds[0] = fds[3];
469
state.con[n].info.user.flags = USER_IDENT;
470
state.con[n].info.user.home = 0;
471
state.con[n].info.user.pump = 0;
472
}
473
state.con[fds[0]].type = UCMD;
474
state.con[fds[1]].type = UOUT;
475
state.con[fds[2]].type = UERR;
476
state.con[n].type = INIT;
477
state.con[n].info.user.pid = id.pid;
478
for (i = 0; i < elementsof(state.con[n].info.user.fds); i++)
479
{
480
fcntl(fds[i], F_SETFD, FD_CLOEXEC);
481
state.con[n].info.user.fds[i] = fds[i];
482
}
483
break;
484
}
485
sfsprintf(state.buf, state.buflen, "#%05d\n", errno);
486
cswrite(fd, state.buf, 7);
487
drop(fd);
488
while (--i >= 0)
489
close(fds[i]);
490
break;
491
case INIT:
492
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))
493
{
494
drop(fd);
495
break;
496
}
497
state.con[fd].info.user.attr.label[0] = 0;
498
x = 0;
499
if (i)
500
{
501
s = state.buf;
502
s[i] = 0;
503
while (e = strchr(s, '\n'))
504
{
505
*e++ = 0;
506
if (strneq(s, CO_ENV_ATTRIBUTES, sizeof(CO_ENV_ATTRIBUTES) - 1) && s[sizeof(CO_ENV_ATTRIBUTES) - 1] == '=')
507
{
508
x = s + sizeof(CO_ENV_ATTRIBUTES);
509
if (*x == '\'')
510
{
511
i = 1;
512
s = t = ++x;
513
while (*s = *t++)
514
{
515
if (*s == '\'') i = !i;
516
else if (i || *s != '\\') s++;
517
else if (!(*s++ = *t++)) break;
518
}
519
}
520
}
521
else if (state.indirect.con)
522
{
523
if (streq(s, CO_OPT_COMMAND))
524
state.con[fd].info.user.flags &= ~USER_IDENT;
525
else if (streq(s, CO_OPT_DUP))
526
state.con[fd].info.user.flags |= USER_DUP;
527
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)))
528
(state.con[fd].info.user.home = sp)->home++;
529
else if (strneq(s, CO_OPT_INDIRECT, sizeof(CO_OPT_INDIRECT) - 1) && s[sizeof(CO_OPT_INDIRECT) - 1] == '=')
530
state.con[fd].info.user.pump = strdup(s + sizeof(CO_OPT_INDIRECT));
531
}
532
s = e;
533
}
534
}
535
if (state.indirect.con && (state.con[fd].info.user.flags & (USER_IDENT|USER_INIT)) == USER_IDENT)
536
{
537
state.con[fd].info.user.flags |= USER_INIT;
538
break;
539
}
540
if (x) state.con[fd].info.user.expr = strdup(x);
541
attributes(x, &state.con[fd].info.user.attr, NiL);
542
state.con[fd].info.user.attr.set &= ~SETLABEL;
543
if (state.indirect.con)
544
{
545
if (!state.con[fd].info.user.pump ||
546
(state.con[fd].info.user.fds[1] = csopen(state.con[fd].info.user.pump, 0)) < 0 ||
547
cswrite(state.con[fd].info.user.fds[1], "#00001\n", 7) != 7)
548
{
549
drop(fd);
550
break;
551
}
552
else
553
{
554
nonblocking(state.con[fd].info.user.fds[1]);
555
state.con[state.con[fd].info.user.fds[1]].type = UOUT;
556
}
557
if (state.con[fd].info.user.flags & USER_DUP) state.con[fd].info.user.fds[2] = state.con[fd].info.user.fds[1];
558
else if ((state.con[fd].info.user.fds[2] = csopen(state.con[fd].info.user.pump, 0)) < 0 ||
559
cswrite(state.con[fd].info.user.fds[2], "#00002\n", 7) != 7)
560
{
561
drop(fd);
562
break;
563
}
564
else state.con[state.con[fd].info.user.fds[2]].type = UERR;
565
}
566
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)
567
{
568
drop(state.con[fd].info.user.fds[2]);
569
state.con[fd].info.user.fds[2] = state.con[fd].info.user.fds[1];
570
}
571
if (state.con[fd].info.user.flags & USER_IDENT)
572
{
573
s = state.buf;
574
s += sfsprintf(s, state.buflen - (s - state.buf), "%s,%s", CO_OPT_SERVER, CO_OPT_ACK);
575
if (state.indirect.con) s += sfsprintf(s, state.buflen - (s - state.buf), ",%s", CO_OPT_INDIRECT);
576
s += sfsprintf(s, state.buflen - (s - state.buf), "\n");
577
i = s - state.buf;
578
if (cswrite(state.con[fd].info.user.fds[0], state.buf, i) != i)
579
{
580
drop(fd);
581
break;
582
}
583
}
584
state.con[fd].info.user.running = 0;
585
state.con[fd].info.user.total = 0;
586
state.con[fd].type = USER;
587
state.users++;
588
break;
589
case MESG:
590
if (csrecv(fd, &id, fds, 1) == 1)
591
{
592
i = fds[0];
593
n = strlen(corinit);
594
if (cswrite(i, corinit, n) == n)
595
{
596
state.con[i].type = ANON;
597
state.con[i].info.shell = 0;
598
#ifdef O_NONBLOCK
599
state.con[i].flags = fcntl(fd, F_GETFL, 0);
600
#endif
601
csfd(i, CS_POLL_READ);
602
}
603
else close(i);
604
}
605
break;
606
case PASS:
607
if ((i = read(fd, state.buf, state.buflen)) <= 0)
608
drop(fd);
609
else
610
{
611
if (state.identify)
612
{
613
n = state.con[fd].info.pass.fd;
614
jp = state.con[fd].info.pass.job;
615
if (state.con[n].info.ident.shell != jp->shell || state.con[n].info.ident.pid != jp->pid)
616
{
617
state.con[n].info.ident.shell = jp->shell;
618
state.con[n].info.ident.pid = jp->pid;
619
sfprintf(state.string, "%s", jp->shell->name);
620
if (*(s = state.con[jp->fd].info.user.attr.label))
621
sfprintf(state.string, " %s", s);
622
if (*(s = state.con[fd].info.pass.job->label))
623
sfprintf(state.string, " %s", s);
624
else sfprintf(state.string, " %d", jp->pid);
625
if (!(s = sfstruse(state.string)))
626
error(3, "out of space");
627
n = sfsprintf(cmd, sizeof(cmd) - 1, state.identify, s);
628
cswrite(state.con[fd].info.pass.fd, cmd, n);
629
}
630
}
631
if (state.con[fd].info.pass.serialize)
632
{
633
if (sfwrite(state.con[fd].info.pass.serialize, state.buf, i) != i)
634
drop(fd);
635
}
636
else if (cswrite(state.con[fd].info.pass.fd, state.buf, i) != i)
637
drop(fd);
638
}
639
break;
640
case PUMP:
641
if (csrecv(fd, &id, fds, 1) == 1)
642
{
643
i = fds[0];
644
state.con[i].type = DEST;
645
csfd(i, CS_POLL_READ);
646
}
647
break;
648
case SCHED:
649
if ((i = read(fd, state.buf, state.buflen)) <= 0)
650
drop(fd);
651
/*HERE*/
652
break;
653
case USER:
654
if (csread(fd, cmd, 7, CS_EXACT) != 7 || cmd[0] != '#' || (n = (int)strtol(cmd + 1, NiL, 10)) <= 0)
655
{
656
drop(fd);
657
break;
658
}
659
if (n > state.buflen)
660
{
661
state.buflen = roundof(n, CHUNK);
662
if (!(state.buf = newof(state.buf, char, state.buflen, 0)))
663
error(3, "out of space [buf]");
664
}
665
if (csread(fd, state.buf, n, CS_EXACT) != n)
666
{
667
drop(fd);
668
break;
669
}
670
state.buf[n - 1] = 0;
671
state.cmds++;
672
n = error_info.errors;
673
if (!(state.home = state.con[fd].info.user.home))
674
state.home = state.shell;
675
switch (i = *state.buf)
676
{
677
case 'e':
678
case 'E':
679
shellexec(NiL, state.buf, fd);
680
n = error_info.errors;
681
break;
682
case 'k':
683
case 'K':
684
if (tokscan(state.buf, NiL, "%s %d %d ", NiL, &n1, &n2) != 3)
685
error_info.errors++;
686
else
687
{
688
message((-1, "kill state.con=%d rid=%d sig=%d", fd, n1, n2));
689
for (jp = state.job; jp <= state.jobmax; jp++)
690
if (jp->fd == fd && jp->pid && (!n1 || jp->rid == n1))
691
{
692
jobkill(jp, n2);
693
if (n1) break;
694
}
695
n = error_info.errors;
696
}
697
break;
698
case 's':
699
case 'S':
700
if (tokscan(state.buf, NiL, "%s %s %s %d %s", NiL, &s, &t, &n1, &x) != 5)
701
error_info.errors++;
702
else server(fd, *s, *t, n1, x);
703
break;
704
default:
705
error(ERROR_OUTPUT|2, state.con[fd].info.user.fds[2], "%c: unknown op", i);
706
break;
707
}
708
state.home = state.shell;
709
if (isupper(i))
710
{
711
n = sfsprintf(state.buf, state.buflen, "a 1 %d\n", error_info.errors != n);
712
cswrite(state.con[fd].info.user.fds[0], state.buf, n);
713
}
714
break;
715
}
716
return(0);
717
}
718
719
/*
720
* wake up to check for hung shells and jobs on busy hosts
721
*/
722
723
static int
724
wakeup(void* handle)
725
{
726
NoP(handle);
727
shellcheck();
728
jobcheck(NiL);
729
return(0);
730
}
731
732
/*
733
* indirect coshell initialization
734
*/
735
736
static void*
737
indirect(void* handle, int fdmax)
738
{
739
int* pass;
740
741
NoP(handle);
742
if (!(pass = newof(0, int, fdmax, 0)))
743
error(3, "out of space [pass]");
744
csfd(state.indirect.con, CS_POLL_READ);
745
pass[state.indirect.con] = state.indirect.msg;
746
csfd(state.indirect.cmd, CS_POLL_READ);
747
pass[state.indirect.cmd] = state.indirect.con;
748
return((void*)pass);
749
}
750
751
/*
752
* indirect coshell data pump
753
*/
754
755
static int
756
pump(void* handle, register int fd)
757
{
758
register int n;
759
register int pd;
760
register int* pass = (int*)handle + fd;
761
register char* s = state.buf;
762
763
if ((n = read(fd, s, state.buflen)) <= 0) goto drop;
764
if (!(pd = *pass))
765
{
766
if ((n -= 7) < 0 || s[0] != '#' || (pd = (int)strtol(s + 1, NiL, 10)) < 1 || pd > 2) goto drop;
767
*pass = pd == 1 ? state.indirect.out : state.indirect.err;
768
if (!n) return(0);
769
}
770
if (cswrite(pd, s, n) != n) goto drop;
771
return(0);
772
drop:
773
if (fd == state.indirect.cmd) exit(0);
774
if (fd = *pass)
775
{
776
close(fd);
777
*pass = 0;
778
}
779
return(-1);
780
}
781
782
/*
783
* coshell main
784
*/
785
786
int
787
main(int argc, char** argv)
788
{
789
register int n;
790
register int fd;
791
register int i;
792
int pfd;
793
int d;
794
char* s;
795
char* t;
796
int fds[4];
797
798
NoP(argc);
799
setlocale(LC_ALL, "");
800
opt_info.argv = argv;
801
error_info.id = CO_ID;
802
if (pathcheck(PATHCHECK, error_info.id, &state.check))
803
exit(1);
804
state.version = strdup(fmtident(usage));
805
error(-1, "%s", state.version);
806
if (!(state.string = sfstropen()))
807
error(3, "out of space [string]");
808
state.buflen = 8 * CHUNK;
809
if (!(state.buf = newof(0, char, state.buflen, 0)))
810
error(3, "out of space [buf]");
811
812
/*
813
* optget() for self documentation
814
* "options" passed to the server as commands
815
*/
816
817
while ((s = argv[opt_info.index+1]) && s[0] == '-' && (s[1] == '-' || s[1] == '?'))
818
{
819
switch (optget(argv, usage))
820
{
821
case ':':
822
error(2, "%s", opt_info.arg);
823
break;
824
case '?':
825
error(ERROR_USAGE|4, "%s", opt_info.arg);
826
break;
827
}
828
break;
829
}
830
argv += opt_info.index;
831
if (error_info.errors)
832
error(ERROR_USAGE|4, "%s", optusage(NiL));
833
834
/*
835
* check for alternate connect stream
836
*/
837
838
if (*argv && (s = *++argv) && strmatch(s, "/dev/(fdp|tcp)/*")) argv++;
839
else if (!(t = getenv(CO_ENV_SHELL)) || tokscan(t, NiL, " %s %s ", NiL, &s) != 2)
840
{
841
if ((fd = csopen(t = "/dev/fdp", 0)) >= 0) close(fd);
842
else t = "/dev/tcp";
843
sfsprintf(s = state.buf, state.buflen, "%s/local/%s/user", t, CO_ID);
844
}
845
if (!strmatch(s, "/dev/fdp/*")) state.indirect.con = 1;
846
error(-1, "connect stream is %s", s);
847
if (!(state.service = strdup(s)))
848
error(3, "out of space [service]");
849
850
/*
851
* check for alternate stdin
852
*/
853
854
if ((s = *argv) && strneq(s, "/dev/fd/", 8))
855
{
856
argv++;
857
if (i = (int)strtol(s + 8, NiL, 0))
858
{
859
close(0);
860
if (dup(i))
861
error(ERROR_SYSTEM|3, "%s: cannot open", s);
862
}
863
}
864
865
/*
866
* COMMAND interactive or argv command processor to server (-*)
867
* DEFER pass fds to the server and exit (no args or /dev/fd/n)
868
* SERVER the server (+*)
869
*/
870
871
if ((s = strrchr(*opt_info.argv, '.')) && streq(s + 1, CS_SVC_SUFFIX))
872
{
873
n = SERVER;
874
argv--;
875
}
876
else if (!(s = *argv)) n = DEFER;
877
else if (s[0] == '+' && !s[1]) n = SERVER;
878
else if (s[0] == '-' && s[1] != '?') n = COMMAND;
879
else error(ERROR_USAGE|4, "[connect-stream] [-hjqQs[aelpst]] [-r host [cmd]] | + [info]");
880
if (n != SERVER)
881
{
882
if ((fd = csopen(state.service, CS_OPEN_TEST)) < 0)
883
{
884
if (errno == ENOENT) error(3, "%s: server not running", state.service);
885
else error(ERROR_SYSTEM|3, "%s: cannot open connect stream", state.service);
886
}
887
if (!state.indirect.con)
888
{
889
for (i = 0; i < elementsof(fds); i++)
890
fds[i] = i;
891
if (n == COMMAND)
892
i--;
893
else if (!(s = getenv(CO_ENV_MSGFD)) || (state.indirect.msg = (int)strtol(s, &t, 0)) <= 0 || *t)
894
state.indirect.msg = i - 1;
895
fds[i - 1] = state.indirect.msg;
896
}
897
errno = EINVAL;
898
s = state.buf;
899
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
900
{
901
if (state.indirect.con)
902
{
903
if ((pfd = csopen(t = "/dev/tcp/local/normal/slave", CS_OPEN_CREATE)) < 0)
904
error(ERROR_SYSTEM|3, "%s: cannot create pump connect stream", t);
905
}
906
else if (n != COMMAND) exit(0);
907
s += 7;
908
s += sfsprintf(s, state.buflen - (s - state.buf), "%s=label=%s", CO_ENV_ATTRIBUTES, CO_ID);
909
if ((t = getenv(CO_ENV_ATTRIBUTES)) && *t) s += sfsprintf(s, state.buflen - (s - state.buf), ",%s", t);
910
if (state.indirect.con)
911
{
912
struct stat st;
913
struct stat ts;
914
915
s += sfsprintf(s, state.buflen - (s - state.buf), "\n%s=%s", CO_OPT_INDIRECT, cspath(pfd, 0));
916
s += sfsprintf(s, state.buflen - (s - state.buf), "\n%s=%s", CO_OPT_HOME, csname(0));
917
if (!fstat(1, &st) && !fstat(2, &ts) && st.st_ino == ts.st_ino && st.st_dev == ts.st_dev)
918
{
919
s += sfsprintf(s, state.buflen - (s - state.buf), "\n%s", CO_OPT_DUP);
920
d = 1;
921
}
922
else d = 2;
923
if (n == COMMAND) s += sfsprintf(s, state.buflen - (s - state.buf), "\n%s", CO_OPT_COMMAND);
924
}
925
s += sfsprintf(s, state.buflen - (s - state.buf), "\n");
926
i = s - state.buf;
927
s = state.buf;
928
sfsprintf(s, 7, "#%05d\n", i - 7);
929
if (cswrite(fd, s, i) != i) break;
930
if (n == COMMAND)
931
{
932
if (state.indirect.con)
933
{
934
for (; d > 0 && csrecv(pfd, NiL, fds, 1) == 1 && csread(fds[0], s, 7, CS_EXACT) == 7 && s[0] == '#'; d--)
935
if ((n = (int)strtol(s + 1, NiL, 10)) == 1) state.indirect.out = fds[0];
936
else if (n == 2) state.indirect.err = fds[0];
937
else break;
938
if (d) break;
939
state.indirect.con = pfd;
940
}
941
exit(command(fd, (*argv)[1] ? argv : (char**)0));
942
}
943
if ((state.indirect.cmd = dup(0)) < 0) break;
944
close(0);
945
if (dup(pfd)) break;
946
close(pfd);
947
csfd(state.indirect.cmd, 0);
948
csfd(state.indirect.con = fd, 0);
949
csfd(state.indirect.out = 1, 0);
950
csfd(state.indirect.err = 2, 0);
951
csfd(state.indirect.msg, 0);
952
csserve(NiL, NiL, indirect, NiL, NiL, pump, NiL, NiL);
953
} while (0);
954
error(ERROR_SYSTEM|3, "%s: cannot connect to server", state.service);
955
}
956
state.argv = argv + 1;
957
958
/*
959
* we are the server
960
*/
961
962
csserve(NiL, state.service, init, NiL, user, service, NiL, wakeup);
963
/*NOTREACHED*/
964
return 1;
965
}
966
967