Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/coshell/shell.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 shell support
26
*/
27
28
#include "service.h"
29
30
#include <proc.h>
31
32
/*
33
* single quote s into sp
34
*/
35
36
static void
37
quote(register Sfio_t* sp, register char* s)
38
{
39
register int c;
40
41
while (c = *s++)
42
{
43
sfputc(sp, c);
44
if (c == '\'') sfputr(sp, "\\''", -1);
45
}
46
}
47
48
/*
49
* open a new shell connection to sp
50
* error output to fd
51
*/
52
53
int
54
shellopen(register Coshell_t* sp, int fd)
55
{
56
char* sh;
57
Proc_t* proc;
58
Sfio_t* xp;
59
Sfio_t* vp;
60
char* av[4];
61
62
if (!sp)
63
{
64
if (fd >= 0)
65
error(ERROR_OUTPUT|2, fd, "host not found");
66
}
67
else if (sp->fd || sp == &state.wait)
68
return 0;
69
else
70
{
71
update(sp);
72
if (sp->stat.up < 0)
73
{
74
if (fd >= 0)
75
error(ERROR_OUTPUT|2, fd, "%s: host is down", sp->name);
76
return -1;
77
}
78
if (!(xp = sfstropen()) || !(vp = sfstropen()))
79
{
80
if (xp)
81
vp = 0;
82
goto nospace;
83
}
84
sfprintf(xp, sp->shell[0] ? sp->shell : state.sh, sp->type);
85
if (!(sh = sfstruse(xp)))
86
goto nospace;
87
if (sp == state.shell)
88
{
89
sfprintf(vp, "%s=%s %s=%s %s='%s %s' COINIT='%s' %s /dev/fd/4 >/dev/null 2>&1 3<%s 4<&3 5>&- 6>&- 7>&- 8>&- 9>&- &", CO_ENV_HOST, sp->name, CO_ENV_TYPE, sp->type, CO_ENV_SHELL, opt_info.argv[0], state.service, (sp->flags & SETRATING) ? "rating=0;" : "", sh, state.mesg);
90
av[0] = "sh";
91
av[1] = "-c";
92
}
93
else
94
{
95
sfprintf(vp, "%s -c 'trap \"\" HUP; %s=%s %s=%s %s= COINIT='\\''%s%s'\\'' %s /dev/fd/4 >/dev/null 2>&1 3<%s 4<&3 5>&- 6>&- 7>&- 8>&- 9>&- &'", sh, CO_ENV_HOST, sp->name, CO_ENV_TYPE, sp->type, CO_ENV_SHELL, (sp->flags & SETRATING) ? "rating=0;" : "", state.profile ? state.profile : "", sh, state.mesg);
96
av[0] = sh = state.remote;
97
av[1] = sp->name;
98
}
99
if (!(av[2] = sfstruse(vp)))
100
goto nospace;
101
av[3] = 0;
102
message((-2, "%s %s \"%s\"", sh, av[1], av[2]));
103
if (!(proc = procopen(sh, av, NiL, NiL, 0)))
104
{
105
if (fd >= 0)
106
error(ERROR_OUTPUT|2, fd, "%s: cannot exec shell", sh);
107
sfstrclose(xp);
108
sfstrclose(vp);
109
return -1;
110
}
111
message((-1, "%s: open shell pid=%d", sp->name, proc->pid));
112
sp->flags &= ~DEF;
113
sp->fd = -proc->pid;
114
sp->start = cs.time;
115
if (sp->update > cs.time + UPDATE)
116
sp->update = cs.time + UPDATE;
117
if (sp->mode & SHELL_OVERRIDE)
118
{
119
if (!sp->override)
120
state.override++;
121
sp->override = cs.time + (sp->home ? HOME : OVERRIDE);
122
}
123
state.shellwait++;
124
state.open += sp->cpu;
125
procfree(proc);
126
sfstrclose(xp);
127
sfstrclose(vp);
128
return 0;
129
}
130
return -1;
131
nospace:
132
if (fd >= 0)
133
error(ERROR_OUTPUT|2, fd, "out of space");
134
if (xp)
135
sfstrclose(xp);
136
if (vp)
137
sfstrclose(vp);
138
return -1;
139
}
140
141
/*
142
* close shell sp if open
143
* error output to fd
144
*/
145
146
void
147
shellclose(register Coshell_t* sp, int fd)
148
{
149
register Cojob_t* jp;
150
151
if (sp->fd != -1) message((-1, "%s: close shell", sp->name));
152
if (sp->fd < 0)
153
{
154
if (sp->fd != -1)
155
{
156
kill(-sp->fd, SIGKILL);
157
state.shellwait--;
158
}
159
if (sp->idle_override)
160
{
161
sp->idle = sp->idle_override;
162
sp->idle_override = 0;
163
}
164
sp->stat.up = sp->start - cs.time;
165
sp->update = cs.time + FORGET;
166
sp->rank = RANK * 100;
167
state.open -= sp->cpu;
168
if (sp->override)
169
{
170
sp->override = 0;
171
state.override--;
172
sp->update = 0;
173
}
174
for (jp = state.job; sp->running && jp <= state.jobmax; jp++)
175
if (jp->shell == sp && jp->pid)
176
{
177
if (jp->cmd)
178
{
179
sp->running--;
180
sp->total--;
181
jp->shell = 0;
182
shellexec(jp, jp->cmd, jp->fd);
183
}
184
else jobkill(jp, SIGKILL);
185
}
186
if (sp->fd != -1) waitpid(-sp->fd, NiL, 0);
187
sp->fd = 0;
188
}
189
else if (sp->fd > 0)
190
{
191
drop(sp->fd);
192
sp->start = cs.time;
193
}
194
else if (fd >= 0) error(ERROR_OUTPUT|2, fd, "%s: not open", sp->name);
195
}
196
197
/*
198
* send command in the exec message msg to a shell for user fd
199
* if jp!=0 then the msg was popped off jp->sp's delay queue
200
*/
201
202
void
203
shellexec(Cojob_t* jp, char* msg, int fd)
204
{
205
register Coshell_t* sp;
206
Connection_t* con;
207
int n;
208
int id;
209
int flags;
210
char* s;
211
char* cmd;
212
char* act;
213
char* pwd;
214
char* out;
215
char* err;
216
char* att;
217
char* env;
218
char* end;
219
char* red;
220
Coattr_t attr;
221
222
attr.set = attr.global.set = 0;
223
con = state.con;
224
cmd = strdup(msg);
225
if (tokscan(msg, &end, "%s %d %d %s %s %s %s %s %s", NiL, &id, &flags, &pwd, &out, &err, &att, &env, &act) != 9)
226
{
227
error(ERROR_OUTPUT|2, con[fd].info.user.fds[2], "invalid exec message");
228
goto noexec;
229
}
230
if (!jp || !(sp = jp->shell) || sp == &state.wait)
231
{
232
/*
233
* find a shell slot
234
*/
235
236
if (cs.time > state.access) info(SET, NiL);
237
if (sp == &state.wait) state.joblimit--;
238
if (state.running >= (state.perserver + state.jobwait) || con[fd].info.user.running >= state.peruser)
239
{
240
sp = &state.wait;
241
if (!jp) attributes(att, &attr, &con[fd].info.user.attr);
242
}
243
else if (!(sp = search((flags & CO_LOCAL) ? DEF|JOB : JOB, att, &attr, &con[fd].info.user.attr)))
244
{
245
if (s = con[fd].info.user.expr)
246
{
247
if (att) error(ERROR_OUTPUT|2, con[fd].info.user.fds[2], "%s && %s: invalid host", s, att);
248
else error(ERROR_OUTPUT|2, con[fd].info.user.fds[2], "%s: invalid host", s);
249
}
250
else if (att) error(ERROR_OUTPUT|2, con[fd].info.user.fds[2], "%s: invalid host", att);
251
else error(ERROR_OUTPUT|2, con[fd].info.user.fds[2], "all hosts closed");
252
goto noexec;
253
}
254
if (sp->fd <= 0 && shellopen(sp, con[fd].info.user.fds[2]))
255
{
256
error(ERROR_OUTPUT|2, con[fd].info.user.fds[2], "%s: cannot open", sp->name);
257
goto noexec;
258
}
259
if (!jp)
260
{
261
/*
262
* find a free job slot
263
*/
264
265
jp = state.jobnext;
266
for (;;)
267
{
268
if (jp++ >= state.jobmax) jp = state.job;
269
if (!jp->pid) break;
270
if (jp == state.jobnext)
271
{
272
jp = 0;
273
error(ERROR_OUTPUT|2, con[fd].info.user.fds[2], "job table full");
274
goto noexec;
275
}
276
}
277
state.jobnext = jp;
278
if (attr.set & SETLABEL) strcpy(jp->label, attr.label);
279
else *jp->label = 0;
280
jp->rid = id;
281
jp->fd = fd;
282
jp->sig = 0;
283
jp->status = 0;
284
jp->busy = 0;
285
jp->lost = 0;
286
jp->user = 0;
287
jp->sys = 0;
288
if (!state.running++)
289
{
290
state.clock = cs.time;
291
if (state.busy) cswakeup(state.wakeup = UPDATE * 1000L);
292
}
293
con[fd].info.user.running++;
294
con[fd].info.user.total++;
295
state.jobs++;
296
}
297
jp->shell = sp;
298
sp->running++;
299
sp->total++;
300
}
301
jp->flags = flags;
302
jp->start = cs.time;
303
if (!(msg = jp->cmd)) state.jobwait++;
304
jp->cmd = cmd;
305
cmd = msg;
306
if (sp->fd <= 0)
307
{
308
jp->pid = QUEUE;
309
if (sp == &state.wait) state.joblimit++;
310
if (cmd) free(cmd);
311
return;
312
}
313
jp->pid = START;
314
jp->ref = 1;
315
red = (flags & CO_APPEND) ? ">>" : ">";
316
sfprintf(state.string, "{\ntrap 'set %s$?; trap \"\" 0; print -u3 x %d $1 $(times); 3>&-; exit $1' 0 HUP INT QUIT TERM%s\n",
317
(flags & CO_SILENT) ? "" : "+x ",
318
jp - state.job,
319
(flags & CO_IGNORE) ? "" : " ERR");
320
if (!out)
321
{
322
if (con[fd].info.user.pump) sfprintf(state.string, "print '#%05d'\n", 1);
323
else
324
{
325
sfprintf(state.string, "print '#%05d.%05d'\n", jp - state.job, con[fd].info.user.fds[1]);
326
jp->ref++;
327
}
328
}
329
if (!err)
330
{
331
if (!out && con[fd].info.user.fds[1] == con[fd].info.user.fds[2]) err = "&1";
332
else if (con[fd].info.user.pump) sfprintf(state.string, "print -u2 '#%05d'\n", 2);
333
else
334
{
335
sfprintf(state.string, "print -u2 '#%05d.%05d'\n", jp - state.job, con[fd].info.user.fds[2]);
336
jp->ref++;
337
}
338
}
339
sfprintf(state.string, "%s\ntypeset -i%d ___=${!:-$$}\nexport %s=%..*u${___#%d#}\neval '%s", env ? env : "", TEMPBASE, CO_ENV_TEMP, TEMPBASE, sp->addr, TEMPBASE, (flags & CO_SILENT) ? "" : "set -x\n");
340
if (act) quote(state.string, act);
341
sfprintf(state.string, "\n'\n} </dev/null %s", red);
342
343
/*
344
* NOTE: state.pump could be replaced by remote /dev/tty*
345
*/
346
347
if (!out && !(out = con[fd].info.user.pump)) out = state.pump;
348
else if (*out != '/' && *out != '&') sfprintf(state.string, "%s/", pwd);
349
sfprintf(state.string, "%s 2%s", out, red);
350
if (!err && !(err = con[fd].info.user.pump)) err = state.pump;
351
else if (*err != '/' && *err != '&') sfprintf(state.string, "%s/", pwd);
352
sfprintf(state.string, "%s &\nprint -u3 j %d $!\n", err, jp - state.job);
353
n = sfstrtell(state.string);
354
if (!(s = sfstruse(state.string)))
355
{
356
error(ERROR_OUTPUT|2, con[fd].info.user.fds[2], "out of space");
357
goto nojob;
358
}
359
message((-5, "job: %s", s));
360
if (cswrite(sp->fd, s, n) != n)
361
{
362
error(ERROR_OUTPUT|2, con[fd].info.user.fds[2], "%s: lost host connection", sp->name);
363
goto nojob;
364
}
365
if (cmd) free(cmd);
366
n = sfsprintf(state.buf, state.buflen, "a %d\n", id);
367
if (cswrite(con[fd].info.user.fds[0], state.buf, n) != n)
368
goto noexec;
369
return;
370
nojob:
371
if (cmd) free(cmd);
372
jp->status = EXIT_NOEXEC;
373
jobdone(jp);
374
return;
375
noexec:
376
if (cmd) free(cmd);
377
if (jp) jobdone(jp);
378
n = sfsprintf(state.buf, state.buflen, "x %d %d\n", id, EXIT_NOEXEC);
379
if (cswrite(con[fd].info.user.fds[0], state.buf, n) != n)
380
drop(fd);
381
}
382
383
/*
384
* check for queued jobs on shells hung during open
385
* call if state.shellwait>0
386
*/
387
388
void
389
shellcheck(void)
390
{
391
register Coshell_t* sp;
392
393
sp = state.shell;
394
do
395
{
396
if (sp->fd < 0 && cs.time > (sp->start + LOST))
397
{
398
message((-4, "shellcheck: %s: hung %s", sp->name, fmtelapsed(cs.time - sp->start, 1)));
399
shellclose(sp, -1);
400
}
401
} while ((sp = sp->next) != state.shell);
402
}
403
404