Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/ss/ssd.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 Research
24
*
25
* system status daemon
26
* setuid to the owner of CS_STAT_DIR
27
* setgid to kmem for better performance
28
*/
29
30
static const char id[] = "\n@(#)$Id: ssd (AT&T Research) 2007-11-19 $\0\n";
31
32
#include <ast.h>
33
#include <cs.h>
34
#include <ctype.h>
35
#include <error.h>
36
#include <proc.h>
37
#include <sig.h>
38
#include <times.h>
39
#include <dirent.h>
40
#include <ast_param.h>
41
42
#define since(t) ((now>((unsigned long)t))?(now-((unsigned long)t)):0L)
43
44
/*
45
* define enough of rwhod info to get by
46
*/
47
48
#define WHOVERS 1 /* expected whod.wd_vers value */
49
#define WHOTYPE 1 /* expected whod.wd_type value */
50
51
#define WHODIR "/usr/spool/rwho"
52
#define WHOPRE "whod."
53
#define WHOSUF ""
54
55
struct whod
56
{
57
char wd_vers; /* protocol version # */
58
char wd_type; /* packet type, see below */
59
char wd_pad[2]; /* spare */
60
long wd_sendtime; /* sender time stamp */
61
long wd_recvtime; /* receiver time stamp */
62
char wd_hostname[32]; /* hosts name */
63
long wd_loadav[3]; /* load average as in uptime(1) */
64
long wd_boottime; /* system boot time */
65
};
66
67
#ifndef S_IXUSR
68
#define S_IXUSR 0100
69
#endif
70
#ifndef S_IXGRP
71
#define S_IXGRP 0010
72
#endif
73
#ifndef S_IXOTH
74
#define S_IXOTH 0001
75
#endif
76
77
#include "FEATURE/cmd"
78
79
#if _hdr_nlist && _lib_nlist && _mem_n_name_nlist && _mem_n_type_nlist && _mem_n_value_nlist
80
#define NAMELIST 1
81
#include <nlist.h>
82
#endif
83
84
#if _sys_dk
85
#include <sys/dk.h>
86
#endif
87
88
#if !defined(CPUFOUND) || !defined(NCPU)
89
#undef CP_TIME
90
#endif
91
92
#ifndef CPUSTATES
93
#define CPUSTATES 4
94
#endif
95
#ifndef CP_USER
96
#define CP_USER 0
97
#endif
98
#ifndef CP_NICE
99
#define CP_NICE 1
100
#endif
101
#ifndef CP_SYS
102
#define CP_SYS 2
103
#endif
104
105
#if _hdr_utmpx
106
107
#include <utmpx.h>
108
109
#define utmp utmpx
110
#define ut_name ut_user
111
112
#if !defined(UTMP_FILE) && defined(UTMPX_FILE)
113
#define UTMP_FILE UTMPX_FILE
114
#endif
115
#if !defined(UTMP_PATH) && defined(UTMPX_PATH)
116
#define UTMP_PATH UTMPX_PATH
117
#endif
118
#if !defined(UTMP_PATHNAME) && defined(UTMPX_PATHNAME)
119
#define UTMP_PATHNAME UTMPX_PATHNAME
120
#endif
121
#if !defined(_PATH_UTMP) && defined(_PATH_UTMPX)
122
#define _PATH_UTMP _PATH_UTMPX
123
#endif
124
125
#else
126
127
#include <utmp.h>
128
129
#endif
130
131
static char* usrfiles[] =
132
{
133
#ifdef UTMP_FILE
134
UTMP_FILE,
135
#endif
136
#ifdef UTMP_FILENAME
137
UTMP_FILENAME,
138
#endif
139
#ifdef UTMP_PATH
140
UTMP_PATH,
141
#endif
142
#ifdef UTMP_PATHNAME
143
UTMP_PATHNAME,
144
#endif
145
#ifdef _PATH_UTMP
146
_PATH_UTMP,
147
#endif
148
#if _hdr_utmpx
149
"/etc/utmpx",
150
"/var/adm/utmpx",
151
"/var/run/utmpx",
152
#endif
153
"/etc/utmp",
154
"/var/adm/utmp",
155
"/var/run/utmp"
156
};
157
158
static char* usrfile;
159
160
static char* devfiles[] =
161
{
162
"/dev/mouse",
163
"/dev/kbd",
164
"/dev/keybd",
165
"/dev/keyboard"
166
};
167
168
#ifdef FSCALE
169
static unsigned long avenrun;
170
#else
171
static double avenrun;
172
#define FSCALE 1
173
#endif
174
static unsigned long boottime;
175
static unsigned long cp_time[CPUSTATES];
176
#ifdef CP_TIME
177
static int maxcpu;
178
static struct percpu percpu[NCPU];
179
#endif
180
181
#if NAMELIST
182
183
struct symbol
184
{
185
char* name;
186
char* addr;
187
int size;
188
int once;
189
};
190
191
/*
192
* and here we have the status symbols
193
*/
194
195
static struct symbol symbols[] =
196
{
197
"_avenrun", (char*)&avenrun, sizeof(avenrun), 0,
198
"_boottime", (char*)&boottime, sizeof(boottime), 1,
199
#ifdef CP_TIME
200
"_maxcpu", (char*)&maxcpu, sizeof(maxcpu), 0,
201
"_percpu", (char*)&percpu[0], sizeof(percpu), 0,
202
#else
203
"_cp_time", (char*)&cp_time[0], sizeof(cp_time), 0,
204
#endif
205
};
206
207
static struct nlist names[elementsof(symbols) + 1];
208
209
static char* memfile = "/dev/kmem";
210
static char* sysfiles[] = { "unix", "vmunix" };
211
212
#endif
213
214
static long cp_diff[elementsof(cp_time)];
215
static unsigned long cp_prev[elementsof(cp_time)];
216
217
static struct utmp usrs[256];
218
static struct whod who;
219
static char* whofile;
220
221
static Proc_t* remote;
222
223
static void
224
finish(int status)
225
{
226
if (remote)
227
{
228
kill(remote->pid, SIGKILL);
229
procclose(remote);
230
}
231
exit(status);
232
}
233
234
/*
235
* update data status checking for competing daemon
236
* exit on error or other daemon
237
*/
238
239
static void
240
update(char* data, unsigned long now, int delay, CSSTAT* ss)
241
{
242
unsigned long tm;
243
struct stat st;
244
245
static unsigned long idle;
246
static unsigned long next;
247
static unsigned long down;
248
static char flush[] = ".flush.";
249
250
if (ss->up < 0)
251
{
252
tm = CSTIME();
253
if (down) ss->up -= (long)(tm - down);
254
down = tm;
255
}
256
if (now)
257
{
258
if (now > next || ss->idle < idle)
259
{
260
next = now + delay;
261
now = 0;
262
}
263
if (delay > 10) delay = 10;
264
}
265
if (!now && csnote(data, ss) || stat(data, &st)) finish(1);
266
idle = ss->idle;
267
if (delay)
268
{
269
sleep(delay);
270
if (remote && remove(flush)) close(open(flush, O_WRONLY|O_CREAT|O_TRUNC, 0));
271
tm = st.st_ctime;
272
if (stat(data, &st)) finish(1);
273
if (tm != (unsigned long)st.st_ctime) finish(0);
274
}
275
}
276
277
int
278
main(int argc, char** argv)
279
{
280
register int n;
281
register int i;
282
register long v;
283
char* s;
284
char* e;
285
char* data;
286
int uf;
287
int wf;
288
int idlecmd;
289
int usercount;
290
unsigned long t;
291
unsigned long toss;
292
unsigned long usertime;
293
unsigned long now;
294
unsigned long then;
295
Proc_t* proc;
296
CSSTAT ss;
297
struct stat st;
298
char cmd[PATH_MAX];
299
char buf[PATH_MAX];
300
char tmp[PATH_MAX / 4];
301
char* av[4];
302
char* iv[3];
303
#if NAMELIST
304
DIR* root;
305
struct dirent* entry;
306
int kf;
307
#endif
308
309
NoP(argc);
310
error_info.id = CS_STAT_DAEMON;
311
if (!pathpath(error_info.id, argv[0], PATH_ABSOLUTE|PATH_REGULAR|PATH_EXECUTE, cmd, sizeof(cmd)))
312
error(ERROR_SYSTEM|3, "cannot locate daemon executable");
313
if (!pathpath(CS_STAT_DIR, argv[0], PATH_EXECUTE, buf, sizeof(buf)))
314
error(3, "%s: cannot locate data directory", CS_STAT_DIR);
315
if (stat(buf, &st))
316
error(ERROR_SYSTEM|3, "%s: stat error", buf);
317
if (st.st_uid != geteuid())
318
error(3, "%s: effective uid mismatch", buf);
319
if (chdir(buf))
320
error(ERROR_SYSTEM|3, "%s: chdir error", buf);
321
data = csname(0);
322
if (argv[1] && strcmp(argv[1], data))
323
{
324
/*
325
* start remote status daemon
326
*/
327
328
data = argv[1];
329
if (!csaddr(data))
330
error(3, "%s: unknown host", data);
331
if (!stat(data, &st) && (long)(CSTIME() - (unsigned long)st.st_ctime) < CS_STAT_DOWN)
332
exit(0);
333
sfsprintf(buf, sizeof(buf), "./%s", data);
334
csstat(buf, &ss);
335
if (s = csattr(CS_HOST_LOCAL, "type"))
336
{
337
strcpy(tmp, s);
338
if (s = csattr(data, "type"))
339
pathrepl(cmd, sizeof(cmd), tmp, s);
340
}
341
342
/*
343
* loop until remote status daemon starts
344
* check for competing startup daemon
345
*/
346
347
if (csdaemon(0))
348
exit(1);
349
umask(S_IRWXU|S_IRWXG|S_IRWXO);
350
av[0] = CS_REMOTE_SHELL;
351
av[1] = data;
352
av[2] = cmd;
353
av[3] = 0;
354
for (;;)
355
{
356
update(data, 0, 0, &ss);
357
if (!(remote = procopen(av[0], av, NiL, NiL, PROC_UID|PROC_GID)))
358
break;
359
while (!kill(remote->pid, 0))
360
update(data, 0, CS_STAT_FREQ + (CS_STAT_DOWN - CS_STAT_FREQ) / 2, &ss);
361
procclose(remote);
362
remote = 0;
363
if (ss.up > 0)
364
ss.up = -ss.up;
365
}
366
for (;;) update(data, 0, CS_STAT_FREQ + (CS_STAT_DOWN - CS_STAT_FREQ) / 2, &ss);
367
}
368
remove(data);
369
if ((n = open(data, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0)) < 0)
370
error(ERROR_SYSTEM|3, "%s: cannot update", data);
371
for (i = 0; i < elementsof(usrfiles); i++)
372
if ((uf = open(usrfile = usrfiles[i], O_RDONLY)) >= 0) break;
373
if (uf < 0)
374
error(ERROR_SYSTEM|3, "%s: cannot read", usrfiles[0]);
375
376
/*
377
* final initialization
378
*/
379
380
if (csdaemon((1<<2)|(1<<n)|(1<<uf)))
381
error(ERROR_SYSTEM|3, "cannot dive into background");
382
umask(S_IRWXU|S_IRWXG|S_IRWXO);
383
close(2);
384
dup(n);
385
close(n);
386
error_info.id = data;
387
av[0] = "uptime";
388
av[1] = 0;
389
toss = getpid();
390
for (s = data; *s; s++)
391
CSTOSS(toss, *s);
392
usertime = 0;
393
#if NAMELIST
394
for (n = 0; n < elementsof(symbols); n++)
395
names[n].n_name = symbols[n].name;
396
if ((kf = open(memfile, O_RDONLY)) >= 0)
397
{
398
if (chdir("/"))
399
error(ERROR_SYSTEM|3, "/: chdir error");
400
s = 0;
401
for (i = 0; i < elementsof(sysfiles); i++)
402
if (!access(sysfiles[i], F_OK))
403
{
404
s = sysfiles[i];
405
break;
406
}
407
if (!s)
408
{
409
if (!(root = opendir(".")))
410
error(ERROR_SYSTEM|3, "/: cannot read");
411
while (entry = readdir(root))
412
{
413
if ((i = strlen(entry->d_name) - 2) > 0 && entry->d_name[i] == 'i' && entry->d_name[i + 1] == 'x' && !stat(entry->d_name, &st) && (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
414
{
415
s = entry->d_name;
416
break;
417
}
418
}
419
closedir(root);
420
}
421
nlist(s, names);
422
for (n = 0; n < elementsof(symbols); n++)
423
if (!names[n].n_type)
424
{
425
error(1, "%s: %s not in nlist", s, names[n].n_name);
426
close(kf);
427
kf = -1;
428
}
429
if (chdir(buf))
430
error(ERROR_SYSTEM|3, "%s: chdir error", buf);
431
}
432
if (kf < 0)
433
#endif
434
{
435
sfsprintf(buf, sizeof(buf), "%s/%s%s%s", WHODIR, WHOPRE, data, WHOSUF);
436
if ((wf = open(buf, O_RDONLY)) >= 0)
437
{
438
if (read(wf, &who, sizeof(who)) != sizeof(who) || who.wd_vers != WHOVERS || who.wd_type != WHOTYPE)
439
{
440
error(1, "%s: rwhod protocol mismatch", buf);
441
close(wf);
442
wf = -1;
443
}
444
else whofile = strdup(buf);
445
}
446
}
447
strcpy(cmd + strlen(cmd), ".idle");
448
if (eaccess(cmd, X_OK)) idlecmd = 0;
449
else
450
{
451
idlecmd = 1;
452
iv[0] = cmd;
453
iv[1] = data;
454
iv[2] = 0;
455
}
456
457
/*
458
* the daemon loop
459
*/
460
461
ss.idle = 4 * 60 * 60;
462
now = CSTIME();
463
for (;;)
464
{
465
then = now;
466
now = CSTIME();
467
468
/*
469
* update logged in user stats
470
*/
471
472
if (fstat(uf, &st))
473
error(ERROR_SYSTEM|3, "%s: stat error", usrfile);
474
if (usertime != (unsigned long)st.st_mtime)
475
{
476
usertime = st.st_mtime;
477
if (lseek(uf, 0L, 0))
478
error(ERROR_SYSTEM|3, "%s: seek error", usrfile);
479
if ((n = read(uf, usrs, sizeof(usrs))) < 0)
480
error(ERROR_SYSTEM|3, "%s: read error", usrfile);
481
usercount = n / sizeof(struct utmp);
482
}
483
484
/*
485
* count the interesting users
486
* find the min user idle time
487
*/
488
489
if (idlecmd)
490
{
491
/*
492
* check idle command
493
*/
494
495
if (!(proc = procopen(iv[0], iv, NiL, NiL, PROC_READ|PROC_UID|PROC_GID)))
496
idlecmd = 0;
497
else
498
{
499
idlecmd = 1;
500
n = read(proc->rfd, buf, sizeof(buf));
501
if (procclose(proc) || n < 0)
502
idlecmd = 0;
503
else
504
{
505
if (n > 0)
506
n--;
507
buf[n] = 0;
508
if (isdigit(buf[0]))
509
ss.idle = strtol(buf, NiL, 10);
510
else if (streq(buf, "busy"))
511
ss.idle = 0;
512
else if (streq(buf, "free"))
513
ss.idle = ~0;
514
else if (streq(buf, "idle"))
515
{
516
n = since(then);
517
if ((ss.idle + n) < ss.idle) ss.idle = ~0;
518
else ss.idle += n;
519
}
520
else idlecmd = -1;
521
}
522
}
523
}
524
if (idlecmd <= 0)
525
ss.idle = ~0;
526
ss.users = 0;
527
for (i = 0; i < usercount; i++)
528
if (usrs[i].ut_name[0] && usrs[i].ut_line[0])
529
{
530
sfsprintf(buf, sizeof(buf), "/dev/%s", usrs[i].ut_line);
531
if (stat(buf, &st)) usrs[i].ut_name[0] = 0;
532
else
533
{
534
v = since(st.st_atime);
535
if (v < CS_STAT_IGNORE)
536
ss.users++;
537
if (idlecmd <= 0 && v < ss.idle)
538
ss.idle = v;
539
}
540
}
541
if (idlecmd <= 0 || !ss.users)
542
{
543
/*
544
* check devices for min idle time
545
*/
546
547
for (i = 0; i < elementsof(devfiles); i++)
548
if (devfiles[i])
549
{
550
if (stat(devfiles[i], &st)) devfiles[i] = 0;
551
else
552
{
553
v = since(st.st_atime);
554
if (!ss.users && v < CS_STAT_IGNORE)
555
ss.users++;
556
if (idlecmd <= 0 && v < ss.idle)
557
ss.idle = v;
558
}
559
}
560
}
561
562
/*
563
* get the hard stuff
564
*/
565
566
#if NAMELIST
567
if (kf >= 0)
568
{
569
/*
570
* update memfile symbol values
571
*/
572
573
for (n = 0; n < elementsof(symbols); n++)
574
if (symbols[n].once >= 0)
575
{
576
if (lseek(kf, (long)names[n].n_value, 0) != (long)names[n].n_value)
577
error(ERROR_SYSTEM|3, "%s: %s seek error", memfile, names[n].n_name);
578
if (read(kf, symbols[n].addr, symbols[n].size) != symbols[n].size)
579
error(ERROR_SYSTEM|3, "%s: %s read error", memfile, names[n].n_name);
580
if (symbols[n].once) symbols[n].once = -1;
581
}
582
#ifdef CP_TIME
583
for (i = 0; i < CPUSTATES; i++)
584
cp_time[i] = 0;
585
for (n = 0; n <= maxcpu; n++)
586
if (CPUFOUND(n))
587
for (i = 0; i < CPUSTATES; i++)
588
cp_time[i] += CP_TIME(n)[i];
589
#endif
590
ss.load = (avenrun * 100) / FSCALE;
591
}
592
else
593
#endif
594
if (wf >= 0)
595
{
596
if (lseek(wf, 0L, 0))
597
error(ERROR_SYSTEM|3, "%s: seek error", whofile);
598
read(wf, &who, sizeof(who));
599
ss.load = who.wd_loadav[0];
600
boottime = who.wd_boottime;
601
for (i = 0; i < elementsof(cp_time); i++)
602
cp_time[i] = 100;
603
}
604
else if (!(proc = procopen(av[0], av, NiL, NiL, PROC_READ|PROC_UID|PROC_GID)))
605
error(ERROR_SYSTEM|3, "%s: exec error", av[0]);
606
else
607
{
608
/*
609
* defer to process with memfile access
610
*/
611
612
n = read(proc->rfd, buf, sizeof(buf) - 1);
613
if (procclose(proc) || n <= 0)
614
error(3, "%s: invalid", av[0]);
615
buf[n] = 0;
616
if (!(s = strrchr(buf, ':')))
617
error(3, "%s: invalid output", av[0]);
618
ss.load = strton(s + 1, NiL, NiL, 100);
619
n = 0;
620
if ((s = strchr(buf, 'u')) && *++s == 'p')
621
{
622
n = strtol(s + 1, &e, 10) * 60 * 60;
623
s = e;
624
while (isspace(*s)) s++;
625
if (*s == 'd')
626
{
627
n *= 24;
628
while (*s && !isdigit(*s)) s++;
629
n += strtol(s, &e, 10) * 60 * 60;
630
s = e;
631
}
632
if (*s == ':') n += strtol(s + 1, NiL, 10) * 60;
633
}
634
boottime = since(n);
635
for (i = 0; i < elementsof(cp_time); i++)
636
cp_time[i] = 0;
637
}
638
639
/*
640
* finalize the new stat info
641
*/
642
643
t = 0;
644
for (i = 0; i < elementsof(cp_time); i++)
645
{
646
if ((cp_diff[i] = cp_time[i] - cp_prev[i]) < 0) cp_diff[i] = -cp_diff[i];
647
t += cp_diff[i];
648
cp_prev[i] = cp_time[i];
649
}
650
if (!t) t = 1;
651
ss.pctsys = (cp_diff[CP_SYS] * 100) / t;
652
ss.pctusr = ((cp_diff[CP_USER] + cp_diff[CP_NICE]) * 100) / t;
653
ss.up = since(boottime);
654
update(data, now, (4 * CS_STAT_FREQ + 2 * (CSTOSS(toss, 0) % (CS_STAT_FREQ + 1))) / 5, &ss);
655
}
656
}
657
658