Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/coshell/command.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 command interface support
26
*/
27
28
#include "service.h"
29
30
#define NOARG (((int)1)<<(CHAR_BIT*sizeof(int)-2))
31
32
static struct
33
{
34
int* pass;
35
int fd;
36
} svc;
37
38
/*
39
* send command message to fd and read ack
40
* 0 returned after the last command
41
*/
42
43
static int
44
cmdsend(int fd, int c, register char* s, register int n, int* code)
45
{
46
int i;
47
int m;
48
int x;
49
CSPOLL fds[5];
50
char cmd[1024];
51
52
if (cswrite(fd, s, n) != n)
53
error(ERROR_SYSTEM|3, "message write error");
54
if (c == 'Q') return(0);
55
s = cmd;
56
if (((n = read(fd, s, sizeof(cmd) - 1)) == 4 || n > 6) && s[0] == 'a' && isdigit(s[2]))
57
{
58
if (n == 4) n = read(fd, s, sizeof(cmd) - 1);
59
else
60
{
61
n -= 4;
62
s += 4;
63
}
64
}
65
if (n < 6 || !isdigit(*(s += 4)))
66
{
67
if (code) *code = EXIT_NOEXEC;
68
return(0);
69
}
70
if (code && (n = (int)strtol(s, NiL, 10))) *code = n;
71
if (state.indirect.con)
72
{
73
n = 0;
74
fds[n].fd = state.indirect.con;
75
fds[n++].events = CS_POLL_READ|CS_POLL_CONNECT;
76
if (fds[n].fd = state.indirect.err)
77
fds[n++].events = CS_POLL_READ;
78
fds[n].fd = state.indirect.out;
79
fds[n++].events = CS_POLL_READ;
80
i = n;
81
while (cspoll(fds, n, 0) > 0)
82
for (m = 0; m < n; m++)
83
if (fds[m].status & CS_POLL_CONNECT)
84
{
85
if (n < elementsof(fds) && csrecv(fds[m].fd, NiL, &x, 1) == 1)
86
{
87
svc.pass[x] = 0;
88
fds[n].fd = x;
89
fds[n].status = 0;
90
fds[n++].events = CS_POLL_READ;
91
}
92
}
93
else if (fds[m].status & CS_POLL_READ)
94
{
95
if (x = svc.pass[fds[m].fd])
96
{
97
if ((c = read(fds[m].fd, cmd, sizeof(cmd))) > 0)
98
cswrite(x, cmd, c);
99
else
100
{
101
close(fds[m].fd);
102
fds[m].events = 0;
103
}
104
}
105
else if (csread(fds[m].fd, cmd, 7, CS_EXACT) == 7 && cmd[0] == '#')
106
svc.pass[fds[m].fd] = (int)strtol(cmd + 1, NiL, 10);
107
else
108
{
109
close(fds[m].fd);
110
fds[m].events = 0;
111
}
112
}
113
while (--i > n)
114
if (fds[i].events)
115
close(fds[i].fd);
116
}
117
return(1);
118
}
119
120
#ifdef SIGCONT
121
122
/*
123
* send a job control kill message to fd
124
*/
125
126
static void
127
cmdkill(int fd, int sig)
128
{
129
int n;
130
char buf[32];
131
132
n = sfsprintf(buf, sizeof(buf), "#%05d\nk 0 %d\n", 0, sig);
133
sfsprintf(buf, 7, "#%05d\n", n - 7);
134
cmdsend(fd, 'Q', buf, n, NiL);
135
}
136
137
/*
138
* pass job control signals to the server and self
139
*/
140
141
static void
142
cmdstop(int sig)
143
{
144
cmdkill(svc.fd, sig);
145
signal(sig, SIG_DFL);
146
kill(getpid(), sig);
147
cmdkill(svc.fd, SIGCONT);
148
signal(sig, cmdstop);
149
}
150
151
#endif
152
153
/*
154
* command loop on server stream fd
155
* if ap!=0 then it is an argv of commands
156
* otherwise the commands are read from stdin
157
* exit code returned
158
*/
159
160
int
161
command(int fd, char** ap)
162
{
163
register char* s;
164
register int n;
165
int c;
166
int m;
167
int helped = 0;
168
int code = 0;
169
int prompt;
170
char* t;
171
char* arg;
172
char* att;
173
174
#ifdef SIGCONT
175
svc.fd = fd;
176
#ifdef SIGTSTP
177
signal(SIGTSTP, cmdstop);
178
#endif
179
#ifdef SIGTTIN
180
signal(SIGTTIN, cmdstop);
181
#endif
182
#ifdef SIGTTOU
183
signal(SIGTTOU, cmdstop);
184
#endif
185
#endif
186
if (state.indirect.con)
187
{
188
if (!(svc.pass = newof(0, int, (int)strtol(astconf("OPEN_MAX", NiL, NiL), NiL, 0), 0)))
189
error(3, "out of space [pass]");
190
svc.pass[state.indirect.err] = 2;
191
svc.pass[state.indirect.out] = 1;
192
}
193
prompt = !ap && isatty(0);
194
for (;;)
195
{
196
if (!ap)
197
{
198
if (prompt) error(ERROR_PROMPT, "%s> ", CO_ID);
199
if (!(s = sfgetr(sfstdin, '\n', 1))) break;
200
while (isspace(*s)) s++;
201
if (c = *s) s++;
202
}
203
else if (!(s = *ap++)) break;
204
else if (*s == '-' && s[1])
205
{
206
c = *++s;
207
s++;
208
}
209
if (c == 'q') break;
210
while (isspace(*s)) s++;
211
switch (c)
212
{
213
case 0:
214
case '#':
215
continue;
216
case 'a':
217
case 'c':
218
case 'l':
219
case 'o':
220
if (!*s && ap && *ap) s = *ap++;
221
sfprintf(state.string, "#%05d\nS %c - 0 ", 0, c);
222
if (!s || !*s) sfprintf(state.string, "%s\n", (char*)0);
223
else sfprintf(state.string, "(%d:%s)\n", strlen(s), s);
224
break;
225
case 'd':
226
case 'f':
227
case 'g':
228
case 'j':
229
case 'k':
230
case 's':
231
case 't':
232
case 'u':
233
case 'v':
234
case 'Q':
235
if (!*s && ap && *ap && **ap != '-') s = *ap++;
236
while (isspace(*s)) s++;
237
m = isalpha(*s) ? *s++ : '-';
238
while (isspace(*s)) s++;
239
if (isdigit(*s))
240
{
241
n = strtol(s, &t, 0);
242
s = t;
243
}
244
else n = *s ? *s : NOARG;
245
sfprintf(state.string, "#%05d\nS %c %c %d ", 0, c, m, n);
246
if (!s || !*s) sfprintf(state.string, "%s\n", (char*)0);
247
else sfprintf(state.string, "(%d:%s)\n", strlen(s), s);
248
break;
249
case 'r':
250
case 'R':
251
if (!*s && ap && *ap) s = *ap++;
252
m = 0;
253
att = s;
254
for (;;)
255
{
256
if (!(n = *s++))
257
{
258
s--;
259
break;
260
}
261
if (n == '\\')
262
{
263
if (*s) s++;
264
}
265
else if (!m)
266
{
267
if (n == '\'' || n == '"') m = n;
268
else if (isspace(*s))
269
{
270
*s++ = 0;
271
while (isspace(*s)) s++;
272
break;
273
}
274
}
275
else if (n == m) m = 0;
276
}
277
sfprintf(state.string, "#%05d\ne %d %d %s %s %s",
278
0,
279
1,
280
c == 'r' ? CO_SILENT : 0,
281
NiL,
282
NiL,
283
NiL);
284
if (!att[0] || att[0] == '-' && !att[1]) sfprintf(state.string, " %s", NiL);
285
else sfprintf(state.string, " (%d:%s)", strlen(att), att);
286
arg = coinit(CO_SERVER);
287
sfprintf(state.string, " (%d:%s)", strlen(arg), arg);
288
t = state.buf;
289
if (!*(arg = s) && (!ap || !*ap)) arg = "hostname 2>/dev/null || uname -n";
290
t += sfsprintf(t, state.buf + state.buflen - t, "%s", arg);
291
if (ap)
292
{
293
while (s = *ap++)
294
t += sfsprintf(t, state.buf + state.buflen - t, " %s", s);
295
ap--;
296
}
297
sfprintf(state.string, " (%d:%s)\n", t - state.buf, state.buf);
298
break;
299
default:
300
if (ap) error(3, "%c: unknown command -- use h for help", c);
301
error(2, "%c: unknown command", c);
302
if (helped) continue;
303
/*FALLTHROUGH*/
304
case 'h':
305
case '?':
306
helped++;
307
error(0, "\
308
\n\
309
a host ... change host shell attributes\n\
310
c host ... close host shell\n\
311
d [level] set debug trace stderr [and level]\n\
312
f [fd] internal fd status [drop CON fd]\n\
313
g global state\n\
314
h help\n\
315
j job status\n\
316
k [ckst] job terminate [continue|kill|stop|term] job\n\
317
l host list matching host names\n\
318
o host ... open host shell\n\
319
q quit\n\
320
r host [cmd] run cmd [hostname] on host\n\
321
s [aelopst] shell status [attr|every|long|open|pid|sched|temp]\n\
322
t shell and user connection totals\n\
323
u user connection status\n\
324
v server version\n\
325
Q kill server and quit\n\
326
");
327
continue;
328
}
329
n = sfstrtell(state.string);
330
sfstrseek(state.string, 0, SEEK_SET);
331
sfprintf(state.string, "#%05d\n", n - 7);
332
if (!cmdsend(fd, c, sfstrseek(state.string, 0, SEEK_SET), n, &code)) break;
333
}
334
return(code);
335
}
336
337
/*
338
* return file mode given fd
339
* if must!=0 then 0 returned if fstat fails
340
*/
341
342
static char*
343
ffmtmode(int fd, int must)
344
{
345
char* p;
346
struct stat st;
347
348
if (fstat(fd, &st)) return(must ? (char*)0 : (char*)"**ERROR** ");
349
p = fmtmode(st.st_mode ? st.st_mode : (S_IRUSR|S_IWUSR), 0);
350
if (!st.st_mode)
351
#ifdef S_ISSOCK
352
*p = 's';
353
#else
354
*p = 'p';
355
#endif
356
return(p);
357
}
358
359
/*
360
* do CO_server op for user fd
361
*/
362
363
void
364
server(int fd, int op, int sub, int arg, char* dat)
365
{
366
register char* s;
367
register int n;
368
register Connection_t* con;
369
int u;
370
int hdr;
371
char* t;
372
char* v;
373
Cojob_t* jp;
374
Coshell_t* sp;
375
Coshell_t** sv;
376
Coattr_t attr;
377
Coshell_t tot;
378
char num[4][12];
379
380
con = state.con;
381
hdr = 0;
382
switch (op)
383
{
384
case 0:
385
break;
386
case 'a':
387
if (dat)
388
{
389
t = tokopen(dat, 1);
390
while (s = tokread(t))
391
if (!search(SET, s, NiL, NiL))
392
error(ERROR_OUTPUT|2, con[fd].info.user.fds[2], "%s: invalid host name", s);
393
tokclose(t);
394
}
395
break;
396
case 'c':
397
if (dat)
398
{
399
t = tokopen(dat, 1);
400
while (s = tokread(t))
401
{
402
if (!(sp = search(GET, s, NiL, NiL))) error(ERROR_OUTPUT|2, con[fd].info.user.fds[2], "%s: not found", s);
403
else if (!sp->fd) error(ERROR_OUTPUT|2, con[fd].info.user.fds[2], "%s: host not open", s);
404
else shellclose(sp, con[fd].info.user.fds[2]);
405
}
406
tokclose(t);
407
}
408
break;
409
case 'd':
410
if (arg == 't')
411
{
412
cs.flags |= CS_TEST;
413
break;
414
}
415
else if (arg == 'T')
416
{
417
cs.flags &= ~CS_TEST;
418
break;
419
}
420
arg = (arg == NOARG) ? error_info.trace : -arg;
421
message((error_info.trace, "level=%d output=%s", -arg, dat ? dat : cspath(state.indirect.con ? 2 : con[fd].info.user.fds[2], 0)));
422
error_info.trace = arg;
423
if (dat)
424
{
425
if ((n = open(dat, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) >= 0)
426
{
427
close(2);
428
dup(n);
429
close(n);
430
}
431
}
432
else if (!state.indirect.con)
433
{
434
close(2);
435
dup(con[fd].info.user.fds[2]);
436
}
437
break;
438
case 'f':
439
if (arg == NOARG)
440
{
441
sfprintf(state.string, "CON TYPE INFO\n");
442
for (n = 0; n <= state.fdtotal; n++)
443
switch (con[n].type)
444
{
445
case ANON:
446
sfprintf(state.string, "%3d anon\n", n);
447
break;
448
case DEST:
449
sfprintf(state.string, "%3d dest\n", n);
450
break;
451
case IDENT:
452
sfprintf(state.string, "%3d ident\n", n);
453
break;
454
case MESG:
455
sfprintf(state.string, "%3d mesg %s %s\n", n, ffmtmode(n, 0), state.mesg);
456
break;
457
case PASS:
458
if (jp = con[n].info.pass.job) sfprintf(state.string, "%3d pass %-3d %s %d%s\n", n, con[n].info.pass.fd, jp->shell->name, jp->pid, con[n].info.pass.serialize ? " serialize" : "");
459
else sfprintf(state.string, "%3d pass %-3d zombie\n", n, con[n].info.pass.fd);
460
break;
461
case POLL:
462
sfprintf(state.string, "%3d poll %s %s\n", n, ffmtmode(n, 0), state.service);
463
break;
464
case PUMP:
465
sfprintf(state.string, "%3d pump %s %s\n", n, ffmtmode(n, 0), state.pump);
466
break;
467
case SHELL:
468
sfprintf(state.string, "%3d shell %s\n", n, con[n].info.shell->name);
469
break;
470
case USER:
471
if (con[n].info.user.fds[0] == n) sfprintf(state.string, "%3d query %-3d %-3d %s\n", n, con[n].info.user.fds[1], con[n].info.user.fds[2], (sp = con[n].info.user.home) ? sp->name : "");
472
else sfprintf(state.string, "%3d user %-3d %-3d %-3d %s\n", n, con[n].info.user.fds[0], con[n].info.user.fds[1], con[n].info.user.fds[2], (sp = con[n].info.user.home) ? sp->name : "");
473
break;
474
case UCMD:
475
t = "ucmd";
476
goto path;
477
case UERR:
478
t = "uerr";
479
goto path;
480
case UOUT:
481
t = "uout";
482
goto path;
483
case 0:
484
t = "open";
485
path:
486
if (v = ffmtmode(n, 1)) sfprintf(state.string, "%3d %s %s %s\n", n, t, v, cspath(n, 0));
487
else if (con[n].type) sfprintf(state.string, "%3d %s error\n", n, t);
488
break;
489
}
490
}
491
else if (arg >= state.fdtotal || !con[arg].type) error(ERROR_OUTPUT|2, con[fd].info.user.fds[2], "cannot drop CON %d", arg);
492
else drop(arg);
493
break;
494
case 'g':
495
/*
496
* string values
497
*/
498
499
sfprintf(state.string, "\n");
500
sfprintf(state.string, " version %s\n", state.version);
501
sfprintf(state.string, " mesg %s\n", state.mesg);
502
if (state.identify)
503
sfprintf(state.string, " identify %s\n", fmtesc(state.identify));
504
if (state.migrate)
505
sfprintf(state.string, " migrate %s\n", state.migrate);
506
if (state.profile && *state.profile)
507
sfprintf(state.string, " profile %s\n", state.profile);
508
sfprintf(state.string, " pump %s\n", state.pump);
509
sfprintf(state.string, " remote %s\n", state.remote);
510
sfprintf(state.string, " service %s\n", state.service);
511
sfprintf(state.string, " shell %s\n", state.sh);
512
513
/*
514
* readonly values
515
*/
516
517
sfprintf(state.string, "\n");
518
519
sfprintf(state.string, " access %-7s", state.access < cs.time ? "EXPIRED" : fmtelapsed(state.access - cs.time, 1));
520
sfprintf(state.string, " joblimit %-6d ", state.joblimit);
521
sfprintf(state.string, " pid %-6d ", getpid());
522
sfprintf(state.string, " sys %-6s\n", fmtelapsed(state.sys, CO_QUANT));
523
524
525
sfprintf(state.string, " clock %-6s ", fmtelapsed(cs.time - state.clock, 1));
526
sfprintf(state.string, " jobs %-6d ", state.jobs);
527
sfprintf(state.string, " real %-6s ", fmtelapsed(state.real, 1));
528
sfprintf(state.string, " user %-6s\n", fmtelapsed(state.user, CO_QUANT));
529
530
sfprintf(state.string, " cmds %-6d ", state.cmds);
531
sfprintf(state.string, " jobwait %-6d ", state.jobwait);
532
sfprintf(state.string, " running %-6d ", state.running);
533
sfprintf(state.string, " users %-6d\n", state.users);
534
535
sfprintf(state.string, " connect %-6d ", state.connect);
536
sfprintf(state.string, " open %-6d ", state.open);
537
sfprintf(state.string, " shells %-6d ", state.shells);
538
sfprintf(state.string, " wakeup %-6s\n", fmtelapsed(state.wakeup, 1000));
539
540
sfprintf(state.string, " fdtotal %-6d ", state.fdtotal);
541
sfprintf(state.string, " override %-6d ", state.override);
542
sfprintf(state.string, " shellwait %-6d\n", state.shellwait);
543
544
/*
545
* global values
546
*/
547
548
sfprintf(state.string, "\n");
549
550
sfprintf(state.string, " busy %-6s ", fmtelapsed(state.busy, 1));
551
sfprintf(state.string, " grace %-6s ", fmtelapsed(state.grace, 1));
552
sfprintf(state.string, " percpu %-6d ", state.percpu);
553
sfprintf(state.string, " peruser %-6d\n", state.peruser);
554
555
sfprintf(state.string, " debug %-6d ", -error_info.trace);
556
sfprintf(state.string, " maxidle %-6s ", fmtelapsed(state.maxidle, 1));
557
sfprintf(state.string, " perhost %-6d ", state.perhost);
558
sfprintf(state.string, " pool %-6d\n", state.pool);
559
560
sfprintf(state.string, " disable %-6s ", fmtelapsed(state.disable, 1));
561
sfprintf(state.string, " maxload %-6s ", fmtfloat(state.maxload));
562
sfprintf(state.string, " perserver %-6d\n", state.perserver);
563
564
/*
565
* done
566
*/
567
568
sfprintf(state.string, "\n");
569
break;
570
case 'j':
571
for (jp = state.job; jp <= state.jobmax; jp++)
572
if (jp->pid)
573
{
574
switch (jp->pid)
575
{
576
case QUEUE:
577
t = "QUEUE";
578
break;
579
case START:
580
t = "START";
581
break;
582
case WARP:
583
t = "WARP";
584
break;
585
default:
586
sfsprintf(t = num[0], sizeof(num[0]), "%d", jp->pid);
587
break;
588
}
589
if (!hdr)
590
{
591
hdr = 1;
592
sfprintf(state.string, "JOB USR RID PID TIME HOST LABEL\n");
593
}
594
v = con[jp->fd].info.user.attr.label;
595
sfprintf(state.string, "%3d%4d%4d%6s%7s%c%-16s %s%s%s", jp - state.job, jp->fd, jp->rid, t, fmtelapsed(cs.time - jp->start, 1), jp->ref <= 0 ? '*' : jp->lost ? (jp->lost > cs.time ? '?' : '!') : ' ', jp->shell->name, v, *v ? " " : "", jp->label);
596
if (jp->sig) sfprintf(state.string, " [%s]", fmtsignal(jp->sig));
597
sfputc(state.string, '\n');
598
}
599
break;
600
case 'k':
601
if (arg == NOARG)
602
error(ERROR_OUTPUT|2, con[fd].info.user.fds[2], "job id required");
603
else if ((jp = state.job + arg) > state.jobmax || !jp->pid)
604
error(ERROR_OUTPUT|2, con[fd].info.user.fds[2], "%d: invalid job id", arg);
605
else switch (sub)
606
{
607
#ifdef SIGCONT
608
case 'c':
609
jobkill(jp, SIGCONT);
610
break;
611
#endif
612
case 'k':
613
jobkill(jp, SIGKILL);
614
break;
615
#ifdef SIGSTOP
616
case 's':
617
jobkill(jp, SIGSTOP);
618
break;
619
#endif
620
case 't':
621
case '-':
622
jobkill(jp, SIGTERM);
623
break;
624
default:
625
error(ERROR_OUTPUT|2, con[fd].info.user.fds[2], "%c: invalid signal id", sub);
626
break;
627
}
628
break;
629
case 'l':
630
sv = state.shellv;
631
sp = state.shell;
632
if (dat && *dat) attributes(dat, &attr, NiL);
633
else attr = con[fd].info.user.attr;
634
do
635
{
636
if (match(sp, &attr, 0))
637
*sv++ = sp;
638
} while ((sp = sp->next) != state.shell);
639
if (n = sv - state.shellv)
640
{
641
*sv = 0;
642
strsort((char**)(sv = state.shellv), n, byrank);
643
if (dat && (attr.global.set & SETPOOL) && attr.global.pool > 0 && attr.global.pool < n) sv[attr.global.pool] = 0;
644
while (sp = *sv++)
645
sfprintf(state.string, "%s\n", sp->name);
646
}
647
break;
648
case 'o':
649
if (dat)
650
{
651
t = tokopen(dat, 1);
652
while (s = tokread(t))
653
{
654
if (!(sp = search(NEW, s, NiL, NiL))) error(ERROR_OUTPUT|2, con[fd].info.user.fds[2], "%s: invalid host name", s);
655
else if (sp->fd) error(ERROR_OUTPUT|2, con[fd].info.user.fds[2], "%s: host already open", s);
656
else shellopen(sp, con[fd].info.user.fds[2]);
657
}
658
tokclose(t);
659
}
660
break;
661
case 's':
662
sv = state.shellv;
663
sp = state.shell;
664
do
665
{
666
if (sub == 'a' || sub == 'e' || sp->fd || (sub == 'l' || sub == 's') && sp->temp && !(sp->flags & IGN)) *sv++ = sp;
667
} while ((sp = sp->next) != state.shell);
668
if (n = sv - state.shellv)
669
{
670
*sv = 0;
671
strsort((char**)(sv = state.shellv), n, sub == 'a' || sub == 'e' || sub == 'p' ? byname : sub == 't' ? bytemp : byrank);
672
switch (sub)
673
{
674
case 'a':
675
sfprintf(state.string, "HOST CONF\n%-12.12s busy=%s grace=%s name=%s percpu=%d perserver=%d peruser=%d pool=%d\n", CS_HOST_LOCAL, fmtelapsed(state.busy, 1), fmtelapsed(state.grace, 1), state.shell->name, state.percpu, state.perserver, state.peruser, state.pool);
676
break;
677
case 'o':
678
case 's':
679
case 't':
680
sfprintf(state.string, "CON OPEN USERS UP CONNECT UPDATE OVERRIDE IDLE TEMP RANK HOST\n");
681
break;
682
case 'p':
683
sfprintf(state.string, "PID HOST\n%-6d %s\n", getpid(), cs.cs);
684
break;
685
default:
686
sfprintf(state.string, "CON JOBS TOTAL USER SYS IDLE CPU LOAD RATING BIAS TYPE HOST\n");
687
break;
688
}
689
while (sp = *sv++)
690
{
691
switch (sub)
692
{
693
case 'a':
694
sfprintf(state.string, "%-12.12s rating=%s", sp->name, fmtfloat(sp->rating));
695
if (sp->flags & IGN)
696
sfprintf(state.string, " ignore=1");
697
if (sp->bias != BIAS)
698
sfprintf(state.string, " bias=%s", fmtfloat(sp->bias));
699
if (sp->cpu != 1)
700
sfprintf(state.string, " cpu=%d", sp->cpu);
701
if (sp->scale != sp->cpu)
702
sfprintf(state.string, " scale=%d", sp->scale);
703
if (sp->idle)
704
sfprintf(state.string, " idle=%s", fmtelapsed(sp->idle, 1));
705
if (sp->type[0] != '*' || sp->type[1])
706
sfprintf(state.string, " type=%s", sp->type);
707
if (sp->access)
708
sfprintf(state.string, " access=%s", sp->access);
709
if (sp->bypass)
710
sfprintf(state.string, " bypass=%s", sp->bypass);
711
if (sp->shell[0])
712
sfprintf(state.string, " shell=%s", sp->shell);
713
sfprintf(state.string, " %s\n", sp->misc);
714
break;
715
case 'p':
716
if (sp->fd > 0) sfprintf(state.string, "%-6d %s\n", sp->pid, sp->name);
717
break;
718
default:
719
if (sp->fd < 0) t = "+";
720
else if (sp->fd > 0) sfsprintf(t = num[0], sizeof(num[0]), "%d", sp->fd);
721
else t = (sp->temp && !(sp->flags & IGN)) ? "-" : "@";
722
if (sub == 'o' || sub == 's' || sub == 't') sfprintf(state.string, "%3s%c%4d%6d%7s%8s%7s%7s/%d%7s%7s%9s %s\n"
723
, t
724
, sp->fd > 0 && state.con[sp->fd].error ? '?' : ' '
725
, sp->open
726
, sp->stat.users
727
, sp->stat.up < 0 ? "DOWN" : fmtelapsed(sp->stat.up, 1)
728
, sp->start ? fmtelapsed(cs.time - sp->start, 1) : ""
729
, fmtelapsed(sp->update > cs.time ? sp->update - cs.time : 0, 1)
730
, fmtelapsed(sp->override > cs.time ? sp->override - cs.time : 0, 1)
731
, sp->home
732
, fmtelapsed(sp->idle, 1)
733
, fmtfloat(sp->temp >> (CHAR_BIT * sizeof(sp->temp) / 2))
734
, fmtfloat(sp->rank)
735
, sp->name
736
);
737
else sfprintf(state.string, "%3s %4d%6d%7s%7s%7s%c%3d%6s%7s%6s %-8.8s %s\n"
738
, t
739
, sp->running
740
, sp->total
741
, fmtelapsed(sp->user, CO_QUANT)
742
, fmtelapsed(sp->sys, CO_QUANT)
743
, fmtelapsed(sp->stat.up < 0 ? -sp->stat.up : sp->stat.idle, 1)
744
, sp->stat.up < 0 ? '-' : (sp->mode & SHELL_DISABLE) ? '$' : (sp->mode & SHELL_DENIED) ? '!' : sp->idle && sp->stat.idle < sp->idle ? (sp->home ? '~' : '*') : ' '
745
, sp->cpu
746
, fmtfloat(sp->stat.load / sp->scale)
747
, fmtfloat(sp->rating)
748
, fmtfloat(sp->bias)
749
, sp->type
750
, sp->name
751
);
752
break;
753
}
754
}
755
}
756
break;
757
case 't':
758
memzero(&tot, sizeof(tot));
759
sfprintf(state.string, "SHELLS USERS JOBS CMDS UP REAL USER SYS CPU LOAD RATING\n");
760
tot.running = state.running;
761
sp = state.shell;
762
do
763
{
764
if (sp->fd)
765
{
766
tot.fd++;
767
tot.cpu += sp->cpu;
768
tot.stat.load += sp->cpu * sp->stat.load / sp->scale;
769
tot.rating += sp->cpu * sp->rating;
770
}
771
} while ((sp = sp->next) != state.shell);
772
if (tot.cpu)
773
{
774
tot.stat.load /= tot.cpu;
775
tot.rating /= tot.cpu;
776
}
777
for (n = u = 0; n <= state.fdtotal; n++)
778
if (con[n].type == USER) u++;
779
if (state.running)
780
{
781
state.real += cs.time - state.clock;
782
state.clock = cs.time;
783
}
784
sfsprintf(num[0], sizeof(num[0]), "%d/%d", tot.fd, state.shells);
785
sfsprintf(num[1], sizeof(num[1]), "%d/%d", u, state.users);
786
sfsprintf(num[2], sizeof(num[2]), "%d/%d", tot.running, state.jobs);
787
sfsprintf(num[3], sizeof(num[3]), "%d/%d+%d", tot.cpu, state.pool, state.override);
788
sfprintf(state.string, "%6s%7s%8s%6d%7s%7s%7s%7s%8s%6s%7s\n", num[0], num[1], num[2], state.cmds, fmtelapsed(cs.time - state.start, 1), fmtelapsed(state.real, 1), fmtelapsed(state.user, CO_QUANT), fmtelapsed(state.sys, CO_QUANT), num[3], fmtfloat(tot.stat.load), fmtfloat(tot.rating));
789
break;
790
case 'u':
791
for (n = 0; n <= state.fdtotal; n++)
792
if (con[n].type == USER)
793
{
794
if (!hdr)
795
{
796
hdr = 1;
797
sfprintf(state.string, "CON PID JOBS TOTAL TTY LABEL\n");
798
}
799
sfprintf(state.string, "%3d%6d%5d%6d %-28s %s\n", n, con[n].info.user.pid, con[n].info.user.running, con[n].info.user.total, cspath(con[n].info.user.fds[2], 0), con[n].info.user.attr.label);
800
}
801
break;
802
case 'v':
803
sfprintf(state.string, "%s\n", state.version);
804
break;
805
case 'Q':
806
exit(arg);
807
default:
808
error(ERROR_OUTPUT|2, con[fd].info.user.fds[2], "%c: unknown server op", op);
809
break;
810
}
811
if ((n = sfstrtell(state.string)) > 0)
812
cswrite(con[fd].info.user.fds[1], sfstrseek(state.string, 0, SEEK_SET), n);
813
}
814
815