1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187#define FOR_ps
188#include "toys.h"
189
190GLOBALS(
191 union {
192 struct {
193 struct arg_list *G, *g, *U, *u, *t, *s, *p, *O, *o, *P, *k;
194 } ps;
195 struct {
196 long n, m, d, s;
197 struct arg_list *u, *p, *o, *k, *O;
198 } top;
199 struct {
200 char *L;
201 struct arg_list *G, *g, *P, *s, *t, *U, *u;
202 char *d;
203
204 void *regexes, *snapshot;
205 int signal;
206 pid_t self, match;
207 } pgrep;
208 };
209
210 struct ptr_len gg, GG, pp, PP, ss, tt, uu, UU;
211 struct dirtree *threadparent;
212 unsigned width, height;
213 dev_t tty;
214 void *fields, *kfields;
215 long long ticks, bits, time;
216 int kcount, forcek, sortpos;
217 int (*match_process)(long long *slot);
218 void (*show_process)(void *tb);
219)
220
221
222
223struct ofields {
224 struct ofields *next, *prev;
225 short which, len, reverse;
226 char *title;
227};
228
229
230
231
232
233
234
235
236
237
238
239enum {
240 SLOT_pid, SLOT_ppid,
241 SLOT_pgrp, SLOT_sid,
242 SLOT_ttynr, SLOT_ttypgrp,
243 SLOT_flags, SLOT_minflt,
244 SLOT_cminflt, SLOT_majflt,
245 SLOT_cmajflt, SLOT_utime,
246 SLOT_stime, SLOT_cutime,
247 SLOT_cstime, SLOT_priority,
248 SLOT_nice, SLOT_numthreads,
249 SLOT_vmlck, SLOT_starttime,
250 SLOT_vsize, SLOT_rss,
251 SLOT_rsslim, SLOT_startcode,
252 SLOT_endcode, SLOT_startstack,
253 SLOT_esp, SLOT_eip,
254 SLOT_iobytes, SLOT_diobytes,
255 SLOT_utime2, SLOT_uid,
256 SLOT_ruid, SLOT_gid,
257 SLOT_rgid, SLOT_exitsig,
258 SLOT_taskcpu, SLOT_rtprio,
259 SLOT_policy, SLOT_blkioticks,
260 SLOT_gtime, SLOT_cgtime,
261 SLOT_startbss, SLOT_endbss,
262
263 SLOT_upticks, SLOT_argv0len,
264 SLOT_uptime, SLOT_totalram,
265 SLOT_vsz, SLOT_shr,
266 SLOT_pcy, SLOT_rchar,
267 SLOT_wchar, SLOT_rbytes,
268 SLOT_wbytes, SLOT_swap,
269 SLOT_bits, SLOT_tid,
270 SLOT_tcount,
271
272 SLOT_count
273};
274
275
276
277
278
279
280
281struct procpid {
282 long long slot[SLOT_count];
283 unsigned short offset[6];
284 char state;
285 char str[];
286};
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311#define XX 64
312
313
314struct typography {
315 char *name, *help;
316 signed char width, slot;
317} static const typos[] = TAGGED_ARRAY(PS,
318
319 {"PID", "Process ID", 5, SLOT_pid},
320 {"PPID", "Parent Process ID", 5, SLOT_ppid},
321 {"PRI", "Priority (dynamic 0 to 139)", 3, SLOT_priority},
322 {"NI", "Niceness (static 19 to -20)", 3, SLOT_nice},
323 {"ADDR", "Instruction pointer", 4+sizeof(long), SLOT_eip},
324 {"SZ", "4k pages to swap out", 5, SLOT_vsize},
325 {"RSS", "Resident Set Size (DRAM pages)", 6, SLOT_rss},
326 {"PGID", "Process Group ID", 5, SLOT_pgrp},
327 {"VSZ", "Virtual memory size (1k units)", 7, SLOT_vsize},
328 {"MAJFL", "Major page faults", 6, SLOT_majflt},
329 {"MINFL", "Minor page faults", 6, SLOT_minflt},
330 {"PR", "Prio Reversed (dyn 39-0, RT)", 2, SLOT_priority},
331 {"PSR", "Processor last executed on", 3, SLOT_taskcpu},
332 {"RTPRIO", "Realtime priority", 6, SLOT_rtprio},
333 {"SCH", "Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)",
334 3, SLOT_policy},
335 {"CPU", "Which processor running on", 3, SLOT_taskcpu},
336 {"TID", "Thread ID", 5, SLOT_tid},
337 {"TCNT", "Thread count", 4, SLOT_tcount},
338 {"BIT", "32 or 64", 3, SLOT_bits},
339
340
341 {"TTY", "Controlling terminal", -8, -2},
342 {"WCHAN", "Wait location in kernel", -6, -3},
343 {"LABEL", "Security label", -30, -4},
344 {"COMM", "EXE filename (/proc/PID/exe)", -27, -5},
345 {"NAME", "Process name (PID's argv[0])", -27, -7},
346 {"COMMAND", "EXE path (/proc/PID/exe)", -27, -5},
347 {"CMDLINE", "Command line (argv[])", -27, -6},
348 {"ARGS", "CMDLINE minus initial path", -27, -6},
349 {"CMD", "Thread name (/proc/TID/stat:2)", -15, -1},
350
351
352 {"UID", "User id", 5, SLOT_uid},
353 {"USER", "User name", -12, XX|SLOT_uid},
354 {"RUID", "Real (before suid) user ID", 4, SLOT_ruid},
355 {"RUSER", "Real (before suid) user name", -8, XX|SLOT_ruid},
356 {"GID", "Group ID", 8, SLOT_gid},
357 {"GROUP", "Group name", -8, XX|SLOT_gid},
358 {"RGID", "Real (before sgid) Group ID", 4, SLOT_rgid},
359 {"RGROUP", "Real (before sgid) group name", -8, XX|SLOT_rgid},
360
361
362 {"TIME", "CPU time consumed", 8, SLOT_utime},
363 {"ELAPSED", "Elapsed time since PID start", 11, SLOT_starttime},
364 {"TIME+", "CPU time (high precision)", 9, SLOT_utime},
365
366
367 {"C", "Total %CPU used since start", 1, SLOT_utime2},
368 {"%VSZ", "VSZ as % of physical memory", 5, SLOT_vsize},
369 {"%MEM", "RSS as % of physical memory", 5, SLOT_rss},
370 {"%CPU", "Percentage of CPU time used", 4, SLOT_utime2},
371
372
373 {"VIRT", "Virtual memory size", 4, SLOT_vsz},
374 {"RES", "Short RSS", 4, SLOT_rss},
375 {"SHR", "Shared memory", 4, SLOT_shr},
376 {"READ", "Data read", 6, SLOT_rchar},
377 {"WRITE", "Data written", 6, SLOT_wchar},
378 {"IO", "Data I/O", 6, SLOT_iobytes},
379 {"DREAD", "Data read from disk", 6, SLOT_rbytes},
380 {"DWRITE", "Data written to disk", 6, SLOT_wbytes},
381 {"SWAP", "Swap I/O", 6, SLOT_swap},
382 {"DIO", "Disk I/O", 6, SLOT_diobytes},
383
384
385 {"STIME", "Start time (ISO 8601)", 5, SLOT_starttime},
386 {"F", "Flags 1=FORKNOEXEC 4=SUPERPRIV", 1, XX|SLOT_flags},
387 {"S", "Process state:\n"
388 "\t R (running) S (sleeping) D (device I/O) T (stopped) t (traced)\n"
389 "\t Z (zombie) X (deader) x (dead) K (wakekill) W (waking)",
390 -1, XX},
391 {"STAT", "Process state (S) plus:\n"
392 "\t < high priority N low priority L locked memory\n"
393 "\t s session leader + foreground l multithreaded",
394 -5, XX},
395 {"PCY", "Android scheduling policy", 3, XX|SLOT_pcy},
396);
397
398
399static void help_fields(int len, int multi)
400{
401 int i, j, k, left = 0;
402 struct typography *t;
403
404
405 for (j = len; j--; ) {
406 k = -1;
407
408 for (i=0; i<j; i++) {
409 if (strcmp(typos[toybuf[i]].name, typos[toybuf[i+1]].name)>0) {
410 k = toybuf[i];
411 toybuf[i] = toybuf[i+1];
412 toybuf[i+1] = k;
413 }
414 }
415 if (k == -1) break;
416 }
417
418
419 for (i = j = 0; i<len; i++, j++) {
420 t = (void *)(typos+toybuf[i]);
421 if (strlen(t->help)>30) {
422 if (multi) printf(" %-8s%s\n", t->name, t->help);
423 else j--;
424 } else if (!multi) {
425 left = !(j&1);
426 printf(" %-8s%*s%c"+2*!left, t->name, -30*left, t->help, 10+22*left);
427 }
428 }
429 if (!multi && left) xputc('\n');
430}
431
432
433static void help_help(void)
434{
435 int i, jump = PS_CMD+1-PS_COMM;
436
437
438
439
440
441
442
443 printf("Command line field types:\n\n");
444 for (i = 0; i<jump; i++) toybuf[i] = PS_COMM+i;
445 help_fields(jump, 0);
446
447
448
449 printf("\nProcess attribute field types:\n\n");
450 for (i = 0; i<ARRAY_LEN(typos)-jump; i++) toybuf[i] = i+(i>=PS_COMM)*jump;
451 help_fields(ARRAY_LEN(typos)-jump, 1);
452 help_fields(ARRAY_LEN(typos)-jump, 0);
453
454 xexit();
455}
456
457
458static int shared_match_process(long long *slot)
459{
460 struct ptr_len match[] = {
461 {&TT.gg, SLOT_gid}, {&TT.GG, SLOT_rgid}, {&TT.pp, SLOT_pid},
462 {&TT.PP, SLOT_ppid}, {&TT.ss, SLOT_sid}, {&TT.tt, SLOT_ttynr},
463 {&TT.uu, SLOT_uid}, {&TT.UU, SLOT_ruid}
464 };
465 int i, j;
466 long *ll = 0;
467
468
469 for (i = 0; i < ARRAY_LEN(match); i++) {
470 struct ptr_len *mm = match[i].ptr;
471
472 if (mm->len) {
473 ll = mm->ptr;
474 for (j = 0; j<mm->len; j++) if (ll[j] == slot[match[i].len]) return 1;
475 }
476 }
477
478 return ll ? 0 : -1;
479}
480
481
482static int ps_match_process(long long *slot)
483{
484 int i = shared_match_process(slot);
485
486 if (i>0) return 1;
487
488 if (!i) return 0;
489
490
491 if ((FLAG(a)||FLAG(d)) && slot[SLOT_sid]==*slot) return 0;
492 if (FLAG(a) && !slot[SLOT_ttynr]) return 0;
493 if (!(FLAG(a)||FLAG(d)||FLAG(A)||FLAG(e)) && TT.tty!=slot[SLOT_ttynr])
494 return 0;
495
496 return 1;
497}
498
499
500static char *string_field(struct procpid *tb, struct ofields *field)
501{
502 char *buf = toybuf+sizeof(toybuf)-260, *out = buf, *s;
503 int which = field->which, sl = typos[which].slot;
504 long long *slot = tb->slot, ll = (sl >= 0) ? slot[sl&(XX-1)] : 0;
505
506
507 if (which <= PS_BIT) {
508 char *fmt = "%lld";
509
510 if (which==PS_PRI) ll = 39-ll;
511 if (which==PS_ADDR) fmt = "%llx";
512 else if (which==PS_SZ) ll >>= 12;
513 else if (which==PS_RSS) ll <<= 2;
514 else if (which==PS_VSZ) ll >>= 10;
515 else if (which==PS_PR && ll<-9) fmt="RT";
516 else if ((which==PS_RTPRIO || which==PS_BIT) && ll == 0) fmt="-";
517 sprintf(out, fmt, ll);
518
519
520 } else if (sl < 0) {
521 out = tb->str;
522 sl *= -1;
523
524 if (--sl) out += tb->offset[--sl];
525 if (which==PS_ARGS || which==PS_COMM) {
526 int i;
527
528 s = out;
529 for (i = 0; (which==PS_ARGS) ? i < slot[SLOT_argv0len] : out[i]; i++)
530 if (out[i] == '/') s = out+i+1;
531 out = s;
532 }
533 if (which>=PS_COMM && !*out) sprintf(out = buf, "[%s]", tb->str);
534
535
536 } else if (which <= PS_RGROUP) {
537 sprintf(out, "%lld", ll);
538 if (sl&XX) {
539 if (which > PS_RUSER) {
540 struct group *gr = bufgetgrgid(ll);
541
542 if (gr) out = gr->gr_name;
543 } else {
544 struct passwd *pw = bufgetpwuid(ll);
545
546 if (pw) out = pw->pw_name;
547 }
548 }
549
550
551 } else if (which <= PS_TIME_) {
552 int unit = 60, pad = 2, j = TT.ticks;
553 time_t seconds;
554
555 if (which!=PS_TIME_) unit *= 60*24;
556 else pad = 0;
557
558 if (which==PS_ELAPSED) ll = (slot[SLOT_uptime]*j)-slot[SLOT_starttime];
559 seconds = ll/j;
560
561
562
563 for (s = 0, j = 2*(which==PS_TIME_); j<4; j++) {
564 if (!s && (seconds>unit || j == 1+(which!=PS_TIME))) s = out;
565 if (s) {
566 s += sprintf(s, j ? "%0*ld": "%*ld", pad, (long)(seconds/unit));
567 pad = 2;
568 if ((*s = "-::"[j])) s++;
569 }
570 seconds %= unit;
571 unit /= j ? 60 : 24;
572 }
573 if (which==PS_TIME_ && s-out<8)
574 sprintf(s, ".%02lld", (100*(ll%TT.ticks))/TT.ticks);
575
576
577 } else if (which <= PS__CPU) {
578 ll = slot[sl&(XX-1)]*1000;
579 if (which==PS__VSZ || which==PS__MEM)
580 ll /= slot[SLOT_totalram]/((which==PS__VSZ) ? 1024 : 4096);
581 else if (slot[SLOT_upticks]) ll /= slot[SLOT_upticks];
582 sl = ll;
583 if (which==PS_C) sl += 5;
584 sprintf(out, "%d", sl/10);
585 if (which!=PS_C && sl<1000) sprintf(out+strlen(out), ".%d", sl%10);
586
587
588 } else if (which <= PS_DIO) {
589 ll = slot[typos[which].slot];
590 if (which <= PS_SHR) ll *= sysconf(_SC_PAGESIZE);
591 if (TT.forcek) sprintf(out, "%lldk", ll/1024);
592 else human_readable(out, ll, 0);
593
594
595
596 } else if (which==PS_F) sprintf(out, "%llo", (slot[SLOT_flags]>>6)&5);
597 else if (which==PS_S || which==PS_STAT) {
598 s = out;
599 *s++ = tb->state;
600 if (which==PS_STAT) {
601
602 if (slot[SLOT_nice]<0) *s++ = '<';
603 else if (slot[SLOT_nice]>0) *s++ = 'N';
604 if (slot[SLOT_sid]==*slot) *s++ = 's';
605 if (slot[SLOT_vmlck]) *s++ = 'L';
606 if (slot[SLOT_ttypgrp]==*slot) *s++ = '+';
607 }
608 *s = 0;
609 } else if (which==PS_STIME) {
610 time_t t = time(0)-slot[SLOT_uptime]+slot[SLOT_starttime]/TT.ticks;
611
612
613
614
615
616 strftime(out, 260, "%F %T", localtime(&t));
617 out = out+strlen(out)-3-abs(field->len);
618 if (out<buf) out = buf;
619
620 } else if (which==PS_PCY) sprintf(out, "%.2s", get_sched_policy_name(ll));
621 else if (CFG_TOYBOX_DEBUG) error_exit("bad which %d", which);
622
623 return out;
624}
625
626
627static void show_ps(void *p)
628{
629 struct procpid *tb = p;
630 struct ofields *field;
631 int pad, len, width = TT.width, abslen, sign, olen, extra = 0;
632
633
634 for (field = TT.fields; field; field = field->next) {
635 char *out = string_field(tb, field);
636
637
638
639
640 if (width<2) break;
641 if (field != TT.fields) {
642 putchar(' ');
643 width--;
644 }
645
646
647
648 abslen = abs(field->len);
649 sign = field->len<0 ? -1 : 1;
650 olen = (TT.tty) ? utf8len(out) : strlen(out);
651 if ((field->which<=PS_BIT || FLAG(w)) && olen>abslen) {
652
653 extra += olen-abslen;
654 abslen = olen;
655 } else if (extra && olen<abslen) {
656 int unused = abslen-olen;
657
658
659 if (unused>extra) unused = extra;
660 abslen -= unused;
661 extra -= unused;
662 }
663 if (abslen>width) abslen = width;
664 len = pad = abslen;
665 pad *= sign;
666
667
668 if (!field->next && sign<0) {
669 pad = -1;
670 len = width;
671 }
672
673
674 if (olen>len && len>1 && sign<0) {
675 width--;
676 len--;
677 if (field->next) pad++;
678 abslen = 0;
679 }
680
681 if (TT.tty) width -= draw_trim(out, pad, len);
682 else width -= printf("%*.*s", pad, len, out);
683 if (!abslen) putchar('+');
684 if (!width) break;
685 }
686 putchar(TT.time ? '\r' : '\n');
687}
688
689
690
691
692static int get_ps(struct dirtree *new)
693{
694 struct {
695 char *name;
696 long long bits;
697 } fetch[] = {
698
699 {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
700 {"exe", _PS_COMMAND|_PS_COMM}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME},
701 {"", _PS_NAME}
702 };
703 struct procpid *tb = (void *)toybuf;
704 long long *slot = tb->slot;
705 char *name, *s, *buf = tb->str, *end = 0;
706 struct sysinfo si;
707 int i, j, fd;
708 off_t len;
709
710
711 if (!new->parent)
712 return DIRTREE_RECURSE|DIRTREE_SHUTUP|DIRTREE_PROC
713 |(DIRTREE_SAVE*(TT.threadparent||!TT.show_process));
714
715
716 memset(slot, 0, sizeof(tb->slot));
717 slot[SLOT_tid] = *slot = atol(new->name);
718 if (TT.threadparent && TT.threadparent->extra) {
719 *slot = *(((struct procpid *)TT.threadparent->extra)->slot);
720
721 if (*slot == slot[SLOT_tid]) return 0;
722 }
723 fd = dirtree_parentfd(new);
724
725
726 len = 2048;
727 sprintf(buf, "%lld/stat", slot[SLOT_tid]);
728 if (!readfileat(fd, buf, buf, &len)) return 0;
729
730
731
732
733
734
735 if (!(name = strchr(buf, '('))) return 0;
736 for (s = ++name; *s; s++) if (*s == ')') end = s;
737 if (!end || end-name>255) return 0;
738 if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
739
740
741
742
743 for (j = SLOT_ppid; j<SLOT_count; j++)
744 if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
745
746
747
748 for (i = 0; i<end-name; i++)
749 if ((tb->str[i] = name[i]) < ' ')
750 if (!TT.tty) tb->str[i] = '?';
751 buf = tb->str+i;
752 *buf++ = 0;
753 len = sizeof(toybuf)-(buf-toybuf);
754
755
756
757
758
759
760 slot[SLOT_uid] = new->st.st_uid;
761 slot[SLOT_gid] = new->st.st_gid;
762
763
764 slot[SLOT_utime] += slot[SLOT_stime];
765 slot[SLOT_utime2] = slot[SLOT_utime];
766
767
768 if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP
769 |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len)
770 {
771 off_t temp = len;
772
773 sprintf(buf, "%lld/status", slot[SLOT_tid]);
774 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
775 s = strafter(buf, "\nUid:");
776 slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid;
777 s = strafter(buf, "\nGid:");
778 slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid;
779 if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s);
780 if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s);
781 }
782
783
784 if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) {
785 off_t temp = len;
786
787 sprintf(buf, "%lld/io", slot[SLOT_tid]);
788 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
789 if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s);
790 if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s);
791 if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s);
792 if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s);
793 slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap];
794 slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap];
795 }
796
797
798 if (TT.match_process && !TT.match_process(slot)) return 0;
799
800
801
802 sysinfo(&si);
803 slot[SLOT_uptime] = si.uptime;
804 slot[SLOT_totalram] = si.totalram;
805 slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime];
806
807
808 if (TT.bits&(_PS_VIRT|_PS_SHR)) {
809 off_t temp = len;
810
811 sprintf(buf, "%lld/statm", slot[SLOT_tid]);
812 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
813
814
815 slot[SLOT_vsz] = slot[SLOT_shr] = 0;
816 sscanf(buf, "%lld %*d %lld", &slot[SLOT_vsz], &slot[SLOT_shr]);
817 }
818
819
820 if (TT.bits&_PS_BIT) {
821 off_t temp = 6;
822
823 sprintf(buf, "%lld/exe", slot[SLOT_tid]);
824 if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) {
825 if (buf[4] == 1) slot[SLOT_bits] = 32;
826 else if (buf[4] == 2) slot[SLOT_bits] = 64;
827 }
828 }
829
830
831 if (TT.bits&_PS_PCY)
832 get_sched_policy(slot[SLOT_tid], (void *)&slot[SLOT_pcy]);
833
834
835
836
837
838
839
840
841
842
843
844
845
846 slot[SLOT_argv0len] = 0;
847 for (j = 0; j<ARRAY_LEN(fetch); j++) {
848 tb->offset[j] = buf-(tb->str);
849 if (!(TT.bits&fetch[j].bits)) {
850 *buf++ = 0;
851 continue;
852 }
853
854
855
856
857
858
859
860 len = sizeof(toybuf)-256*(ARRAY_LEN(fetch)-j)-(buf-toybuf)-260;
861 sprintf(buf, "%lld/%s", slot[SLOT_tid], fetch[j].name);
862
863
864
865 if (j==3 || j==5) {
866 struct procpid *ptb = 0;
867 int k;
868
869
870 if (TT.threadparent && TT.threadparent->extra)
871 ptb = (void *)TT.threadparent->extra;
872
873 if (j==3 && !ptb) len = readlinkat0(fd, buf, buf, len);
874 else {
875 if (j==3) i = strlen(s = ptb->str+ptb->offset[3]);
876 else {
877 if (!ptb || slot[SLOT_argv0len]) ptb = tb;
878 i = ptb->slot[SLOT_argv0len];
879 s = ptb->str+ptb->offset[4];
880 while (-1!=(k = stridx(s, '/')) && k<i) {
881 s += k+1;
882 i -= k+1;
883 }
884 }
885 if (i<len) len = i;
886 memcpy(buf, s, len);
887 buf[len] = 0;
888 }
889
890
891 } else if (!j) {
892 int rdev = slot[SLOT_ttynr];
893 struct stat st;
894
895
896 strcpy(buf, "?");
897 if (rdev) {
898
899 for (i = 0; i<3; i++) {
900 sprintf(buf, "%lld/fd/%i", slot[SLOT_tid], i);
901 if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode)
902 && st.st_rdev == rdev && (len = readlinkat0(fd, buf, buf, len)))
903 break;
904 }
905
906
907 if (i == 3) {
908 FILE *fp = fopen("/proc/tty/drivers", "r");
909 int tty_major = 0, maj = dev_major(rdev), min = dev_minor(rdev);
910
911 if (fp) {
912 while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) {
913
914 if (tty_major == maj) {
915 len = strlen(buf);
916 len += sprintf(buf+len, "%d", min);
917 if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev)
918 break;
919 }
920 tty_major = 0;
921 }
922 fclose(fp);
923 }
924
925
926 if (!tty_major) len = sprintf(buf, "%d:%d", maj, min);
927 }
928
929 s = buf;
930 if (strstart(&s, "/dev/")) memmove(buf, s, len -= 4);
931 }
932
933
934 } else {
935 int temp = 0;
936
937
938 if (readfileat(fd, buf, buf, &len) && len>0) {
939
940
941 while (len)
942 if (!buf[len-1] || isspace(buf[len-1])) buf[--len] = 0;
943 else break;
944
945
946
947 for (i=0; i<len-1; i++) {
948 char c = buf[i];
949
950 if (!c) {
951 if (!temp) temp = i;
952 c = ' ';
953 } else if (!TT.tty && c<' ') c = '?';
954 buf[i] = c;
955 }
956 } else *buf = len = 0;
957
958
959
960 slot[SLOT_argv0len] = temp ? temp : len;
961 }
962
963
964 buf += len+1;
965 }
966
967
968 TT.kcount++;
969 if (TT.show_process && !TT.threadparent) {
970 TT.show_process(tb);
971
972 return 0;
973 }
974
975
976 s = xmalloc(buf-toybuf);
977 new->extra = (long)s;
978 memcpy(s, toybuf, buf-toybuf);
979
980 return DIRTREE_SAVE;
981}
982
983
984static int get_threads(struct dirtree *new)
985{
986 struct dirtree *dt;
987 struct procpid *tb;
988 unsigned pid, kcount;
989
990 if (!new->parent) return get_ps(new);
991 pid = atol(new->name);
992
993 TT.threadparent = new;
994 if (!get_ps(new)) {
995 TT.threadparent = 0;
996
997 return 0;
998 }
999
1000
1001
1002 kcount = TT.kcount;
1003 sprintf(toybuf, "/proc/%u/task", pid);
1004 new->child = dirtree_flagread(toybuf, DIRTREE_SHUTUP|DIRTREE_PROC, get_ps);
1005 if (new->child == DIRTREE_ABORTVAL) new->child = 0;
1006 TT.threadparent = 0;
1007 kcount = TT.kcount-kcount+1;
1008 tb = (void *)new->extra;
1009 tb->slot[SLOT_tcount] = kcount;
1010
1011
1012 if (new->child) for (dt = new->child->child; dt; dt = dt->next) {
1013 tb = (void *)dt->extra;
1014 tb->slot[SLOT_pid] = pid;
1015 tb->slot[SLOT_tcount] = kcount;
1016 }
1017
1018
1019 if (!TT.show_process) return DIRTREE_SAVE;
1020 TT.show_process((void *)new->extra);
1021 if ((dt = new->child)) {
1022 new->child = 0;
1023 while (dt->child) {
1024 new = dt->child->next;
1025 TT.show_process((void *)dt->child->extra);
1026 free(dt->child);
1027 dt->child = new;
1028 }
1029 free(dt);
1030 }
1031
1032 return 0;
1033}
1034
1035
1036static char *parse_ko(void *data, char *type, int length)
1037{
1038 struct ofields *field;
1039 char *width, *title, *end, *s;
1040 int i, j, k;
1041
1042
1043 if (length==4 && !strncasecmp(type, "HELP", length)) xexit();
1044
1045
1046
1047
1048 if ((end = strchr(type, '=')) && length>(end-type)) {
1049 title = end+1;
1050 length -= (end-type)+1;
1051 } else {
1052 end = type+length;
1053 title = 0;
1054 }
1055
1056
1057 if ((width = strchr(type, ':')) && width<end) {
1058 if (!title) length = width-type;
1059 } else width = 0;
1060
1061
1062
1063 field = xzalloc(sizeof(struct ofields)+(length+1)*!!title);
1064 if (title) {
1065 memcpy(field->title = (char *)(field+1), title, length);
1066 field->title[field->len = length] = 0;
1067 }
1068
1069 if (width) {
1070 field->len = strtol(++width, &title, 10);
1071 if (!isdigit(*width) || title != end) return title;
1072 end = --width;
1073 }
1074
1075
1076 field->reverse = 1;
1077 if (*type == '-') field->reverse = -1;
1078 else if (*type != '+') type--;
1079 type++;
1080 for (i = 0; i<ARRAY_LEN(typos); i++) {
1081 field->which = i;
1082 for (j = 0; j<2; j++) {
1083 if (!j) s = typos[i].name;
1084
1085 else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU,
1086 PS_VSZ, PS_USER, 0}, i))) continue;
1087 else
1088 s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k];
1089
1090 if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break;
1091 }
1092 if (j!=2) break;
1093 }
1094 if (i==ARRAY_LEN(typos)) return type;
1095 if (!field->title) field->title = typos[field->which].name;
1096 if (!field->len) field->len = typos[field->which].width;
1097 else if (typos[field->which].width<0) field->len *= -1;
1098 dlist_add_nomalloc(data, (void *)field);
1099
1100 return 0;
1101}
1102
1103
1104
1105static long long get_headers(struct ofields *field, char *buf, int blen)
1106{
1107 long long bits = 0;
1108 int len = 0;
1109
1110 for (; field; field = field->next) {
1111 len += snprintf(buf+len, blen-len, " %*s"+!bits, field->len,
1112 field->title);
1113 bits |= 1LL<<field->which;
1114 }
1115
1116 return bits;
1117}
1118
1119
1120static char *parse_rest(void *data, char *str, int len)
1121{
1122 struct ptr_len *pl = (struct ptr_len *)data;
1123 long *ll = pl->ptr;
1124 char *end;
1125 int num = 0;
1126
1127
1128 if (!(15&pl->len))
1129 ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16));
1130
1131
1132 if (isdigit(*str)) {
1133 ll[pl->len] = xstrtol(str, &end, 10);
1134 if (end==(len+str)) num++;
1135
1136 if (pl==&TT.ss && ll[pl->len]==0) ll[pl->len] = getsid(0);
1137 }
1138
1139 if (pl==&TT.pp || pl==&TT.ss) {
1140 if (num && ll[pl->len]>0) {
1141 pl->len++;
1142
1143 return 0;
1144 }
1145 } else if (pl==&TT.tt) {
1146
1147 if (!num) {
1148 if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5;
1149 if (strstart(&str, "pts/")) {
1150 len -= 4;
1151 num++;
1152 } else if (strstart(&str, "tty")) len -= 3;
1153 }
1154 if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) {
1155 struct stat st;
1156
1157 end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty");
1158 memcpy(end, str, len);
1159 end[len] = 0;
1160 xstat(toybuf, &st);
1161 ll[pl->len++] = st.st_rdev;
1162
1163 return 0;
1164 }
1165 } else if (len<255) {
1166 char name[256];
1167
1168 if (num) {
1169 pl->len++;
1170
1171 return 0;
1172 }
1173
1174 memcpy(name, str, len);
1175 name[len] = 0;
1176 if (pl==&TT.gg || pl==&TT.GG) {
1177 struct group *gr = getgrnam(name);
1178 if (gr) {
1179 ll[pl->len++] = gr->gr_gid;
1180
1181 return 0;
1182 }
1183 } else if (pl==&TT.uu || pl==&TT.UU) {
1184 struct passwd *pw = getpwnam(name);
1185 if (pw) {
1186 ll[pl->len++] = pw->pw_uid;
1187
1188 return 0;
1189 }
1190 }
1191 }
1192
1193
1194 return str;
1195}
1196
1197
1198static int ksort(void *aa, void *bb)
1199{
1200 struct ofields *field;
1201 struct procpid *ta = *(struct procpid **)aa, *tb = *(struct procpid **)bb;
1202 int ret = 0, slot;
1203
1204 for (field = TT.kfields; field && !ret; field = field->next) {
1205 slot = typos[field->which].slot;
1206
1207
1208 if (!(slot&XX)) {
1209 if (ta->slot[slot]<tb->slot[slot]) ret = -1;
1210 if (ta->slot[slot]>tb->slot[slot]) ret = 1;
1211 }
1212
1213
1214 if (!ret) {
1215 memccpy(toybuf, string_field(ta, field), 0, 2048);
1216 toybuf[2048] = 0;
1217 ret = strcmp(toybuf, string_field(tb, field));
1218 }
1219 ret *= field->reverse;
1220 }
1221
1222 return ret;
1223}
1224
1225
1226
1227static struct procpid **collate_leaves(struct procpid **tb, struct dirtree *dt)
1228{
1229 while (dt) {
1230 struct dirtree *next = dt->next;
1231
1232 if (dt->extra) *(tb++) = (void *)dt->extra;
1233 if (dt->child) tb = collate_leaves(tb, dt->child);
1234 free(dt);
1235 dt = next;
1236 }
1237
1238 return tb;
1239}
1240
1241
1242
1243static struct procpid **collate(int count, struct dirtree *dt)
1244{
1245 struct procpid **tbsort = xmalloc(count*sizeof(struct procpid *));
1246
1247 collate_leaves(tbsort, dt);
1248
1249 return tbsort;
1250}
1251
1252
1253static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
1254{
1255 struct arg_list def;
1256 int x;
1257
1258 memset(&def, 0, sizeof(struct arg_list));
1259 def.arg = s;
1260 WOULD_EXIT(x, comma_args(arg ? arg : &def, fields, err, parse_ko));
1261 if (x) help_help();
1262}
1263
1264void ps_main(void)
1265{
1266 char **arg;
1267 struct dirtree *dt;
1268 char *not_o;
1269 int i;
1270
1271 TT.ticks = sysconf(_SC_CLK_TCK);
1272
1273 if (-1 != (i = tty_fd())) {
1274 struct stat st;
1275
1276 if (!fstat(i, &st)) TT.tty = st.st_rdev;
1277 }
1278
1279
1280 TT.width = 80;
1281 if (!isatty(1) || !terminal_size(&TT.width, 0)) toys.optflags |= FLAG_w;
1282 if (FLAG(w)) TT.width = 99999;
1283
1284
1285 comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest);
1286 comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest);
1287 comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest);
1288 comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest);
1289 comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest);
1290 comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest);
1291 comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest);
1292 comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest);
1293 comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
1294 dlist_terminate(TT.kfields);
1295
1296
1297 for (arg = toys.optargs; *arg; arg++)
1298 if (parse_rest(&TT.pp, *arg, strlen(*arg))) error_exit("bad %s", *arg);
1299
1300
1301 not_o = "%sTTY,TIME,CMD";
1302 if (FLAG(f))
1303 sprintf(not_o = toybuf+128,
1304 "USER:12=UID,%%sPPID,%s,STIME,TTY,TIME,ARGS=CMD", FLAG(T) ? "TCNT" :"C");
1305 else if (FLAG(l))
1306 not_o = "F,S,UID,%sPPID,C,PRI,NI,BIT,SZ,WCHAN,TTY,TIME,CMD";
1307 else if (CFG_TOYBOX_ON_ANDROID)
1308 sprintf(not_o = toybuf+128,
1309 "USER,%%sPPID,VSIZE,RSS,WCHAN:10,ADDR:10,S,%s",
1310 FLAG(T) ? "CMD" : "NAME");
1311 sprintf(toybuf, not_o, FLAG(T) ? "PID,TID," : "PID,");
1312
1313
1314 if (FLAG(Z)) default_ko("LABEL", &TT.fields, 0, 0);
1315 default_ko(toybuf, &TT.fields, "bad -o", TT.ps.o);
1316
1317 if (TT.ps.O) {
1318 if (TT.fields) TT.fields = ((struct ofields *)TT.fields)->prev;
1319 comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
1320 if (TT.fields) TT.fields = ((struct ofields *)TT.fields)->next;
1321 }
1322 dlist_terminate(TT.fields);
1323
1324
1325 if (FLAG(f)||FLAG(n)) {
1326 struct ofields *field;
1327
1328 for (field = TT.fields; field; field = field->next) {
1329 if (FLAG(n) && field->which>=PS_UID
1330 && field->which<=PS_RGROUP && (typos[field->which].slot&XX))
1331 field->which--;
1332 }
1333 }
1334
1335
1336
1337 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1338 if (!FLAG(M)) printf("%.*s\n", TT.width, toybuf);
1339 if (!(FLAG(k)||FLAG(M))) TT.show_process = show_ps;
1340 TT.match_process = ps_match_process;
1341 dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC,
1342 (FLAG(T) || (TT.bits&(_PS_TID|_PS_TCNT)))
1343 ? get_threads : get_ps);
1344
1345 if ((dt != DIRTREE_ABORTVAL) && (FLAG(k)||FLAG(M))) {
1346 struct procpid **tbsort = collate(TT.kcount, dt);
1347
1348 if (FLAG(M)) {
1349 for (i = 0; i<TT.kcount; i++) {
1350 struct ofields *field;
1351
1352 for (field = TT.fields; field; field = field->next) {
1353 int len = strlen(string_field(tbsort[i], field));
1354
1355 if (abs(field->len)<len) field->len = (field->len<0) ? -len : len;
1356 }
1357 }
1358
1359
1360 get_headers(TT.fields, toybuf, sizeof(toybuf));
1361 printf("%.*s\n", TT.width, toybuf);
1362 }
1363
1364 if (FLAG(k)) qsort(tbsort, TT.kcount, sizeof(void *), (void *)ksort);
1365 for (i = 0; i<TT.kcount; i++) {
1366 show_ps(tbsort[i]);
1367 free(tbsort[i]);
1368 }
1369 if (CFG_TOYBOX_FREE) free(tbsort);
1370 }
1371
1372 if (CFG_TOYBOX_FREE) {
1373 free(TT.gg.ptr);
1374 free(TT.GG.ptr);
1375 free(TT.pp.ptr);
1376 free(TT.PP.ptr);
1377 free(TT.ss.ptr);
1378 free(TT.tt.ptr);
1379 free(TT.uu.ptr);
1380 free(TT.UU.ptr);
1381 llist_traverse(TT.fields, free);
1382 }
1383}
1384
1385#define CLEANUP_ps
1386#define FOR_top
1387#include "generated/flags.h"
1388
1389
1390static void setsort(int pos)
1391{
1392 struct ofields *field, *field2;
1393 int i = 0;
1394
1395 if (pos<0) pos = 0;
1396
1397 for (field = TT.fields; field; field = field->next) {
1398 if ((TT.sortpos = i++)<pos && field->next) continue;
1399 field2 = TT.kfields;
1400 field2->which = field->which;
1401 field2->len = field->len;
1402 break;
1403 }
1404}
1405
1406
1407
1408
1409static int merge_deltas(long long *oslot, long long *nslot, int milis)
1410{
1411 char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar,
1412 SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
1413 int i;
1414
1415 for (i = 0; i<ARRAY_LEN(deltas); i++)
1416 oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
1417 oslot[SLOT_upticks] = (milis*TT.ticks)/1000;
1418
1419 return 1;
1420}
1421
1422static int header_line(int line, int rev)
1423{
1424 if (!line) return 0;
1425
1426 if (FLAG(b)) rev = 0;
1427
1428 printf("%s%*.*s%s%s\n", rev ? "\033[7m" : "", -TT.width*!!FLAG(b), TT.width,
1429 toybuf, rev ? "\033[0m" : "", FLAG(b) ? "" : "\r");
1430
1431 return line-1;
1432}
1433
1434static void top_common(
1435 int (*filter)(long long *oslot, long long *nslot, int milis))
1436{
1437 long long timeout = 0, now, stats[16];
1438 struct proclist {
1439 struct procpid **tb;
1440 int count;
1441 long long whence;
1442 } plist[2], *plold, *plnew, old, new, mix;
1443 char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle",
1444 "iow", "irq", "sirq", "host"};
1445
1446 unsigned tock = 0;
1447 int i, lines, topoff = 0, done = 0;
1448 char stdout_buf[BUFSIZ];
1449
1450
1451 if (!FLAG(b)) setbuf(stdout, stdout_buf);
1452
1453 toys.signal = SIGWINCH;
1454 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1455 *scratch = 0;
1456 memset(plist, 0, sizeof(plist));
1457 memset(stats, 0, sizeof(stats));
1458 do {
1459 struct dirtree *dt;
1460 int recalc = 1;
1461
1462 plold = plist+(tock++&1);
1463 plnew = plist+(tock&1);
1464 plnew->whence = millitime();
1465 dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC,
1466 (FLAG(H) || (TT.bits&(_PS_TID|_PS_TCNT))) ? get_threads : get_ps);
1467 if (dt == DIRTREE_ABORTVAL) error_exit("no /proc");
1468 plnew->tb = collate(plnew->count = TT.kcount, dt);
1469 TT.kcount = 0;
1470
1471 if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
1472 long long *st = stats+8*(tock&1);
1473
1474
1475 sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
1476 st, st+1, st+2, st+3, st+4, st+5, st+6, st+7);
1477 }
1478
1479
1480 if (!plold->tb) {
1481 msleep(250);
1482 continue;
1483 }
1484
1485
1486 old = *plold;
1487 new = *plnew;
1488 mix.tb = xmalloc((old.count+new.count)*sizeof(struct procpid));
1489 mix.count = 0;
1490
1491 while (old.count || new.count) {
1492 struct procpid *otb = old.count ? *old.tb : 0,
1493 *ntb = new.count ? *new.tb : 0;
1494
1495
1496 if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
1497 old.tb++;
1498 old.count--;
1499
1500 continue;
1501 }
1502
1503
1504 if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
1505 else {
1506
1507 if (filter(otb->slot, ntb->slot, new.whence-old.whence)) {
1508 mix.tb[mix.count] = otb;
1509 mix.count++;
1510 }
1511 old.tb++;
1512 old.count--;
1513 }
1514 new.tb++;
1515 new.count--;
1516 }
1517
1518
1519 for (;;) {
1520 char was, is;
1521
1522 if (recalc) {
1523 qsort(mix.tb, mix.count, sizeof(struct procpid *), (void *)ksort);
1524 if (!FLAG(b)) {
1525 printf("\033[H\033[J");
1526 if (toys.signal) {
1527 toys.signal = 0;
1528 terminal_probesize(&TT.width, &TT.height);
1529 }
1530 }
1531 if (TT.top.m) TT.height = TT.top.m+5;
1532 lines = TT.height;
1533 }
1534 if (recalc && !FLAG(q)) {
1535
1536 if (*toys.which->name == 't') {
1537 struct ofields field;
1538 long long ll, up = 0;
1539 long run[6];
1540 int j;
1541
1542
1543 field.which = PS_S;
1544 memset(run, 0, sizeof(run));
1545 for (i = 0; i<mix.count; i++)
1546 run[1+stridx("RSTZ", *string_field(mix.tb[i], &field))]++;
1547 sprintf(toybuf,
1548 "Tasks: %d total,%4ld running,%4ld sleeping,%4ld stopped,"
1549 "%4ld zombie", mix.count, run[1], run[2], run[3], run[4]);
1550 lines = header_line(lines, 0);
1551
1552 if (readfile("/proc/meminfo", toybuf, sizeof(toybuf))) {
1553 for (i=0; i<6; i++) {
1554 pos = strafter(toybuf, (char *[]){"MemTotal:","\nMemFree:",
1555 "\nBuffers:","\nCached:","\nSwapTotal:","\nSwapFree:"}[i]);
1556 run[i] = pos ? atol(pos) : 0;
1557 }
1558 sprintf(toybuf,
1559 "Mem:%10ldk total,%9ldk used,%9ldk free,%9ldk buffers",
1560 run[0], run[0]-run[1], run[1], run[2]);
1561 lines = header_line(lines, 0);
1562 sprintf(toybuf,
1563 "Swap:%9ldk total,%9ldk used,%9ldk free,%9ldk cached",
1564 run[4], run[4]-run[5], run[5], run[3]);
1565 lines = header_line(lines, 0);
1566 }
1567
1568 pos = toybuf;
1569 i = sysconf(_SC_NPROCESSORS_CONF);
1570 pos += sprintf(pos, "%d%%cpu", i*100);
1571 j = 4+(i>10);
1572
1573
1574
1575 if (mix.count) up = mix.tb[0]->slot[SLOT_upticks];
1576 if (!up) up = 1;
1577 now = up*i;
1578 ll = stats[3] = stats[11] = 0;
1579 for (i = 0; i<8; i++) ll += stats[i]-stats[i+8];
1580 stats[3] = now - llabs(ll);
1581
1582 for (i = 0; i<8; i++) {
1583 ll = (llabs(stats[i]-stats[i+8])*1000)/up;
1584 pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]);
1585 }
1586 lines = header_line(lines, 0);
1587 } else {
1588 struct ofields *field;
1589 struct procpid tb;
1590
1591 memset(&tb, 0, sizeof(struct procpid));
1592 pos = stpcpy(toybuf, "Totals:");
1593 for (field = TT.fields; field; field = field->next) {
1594 long long ll, bits = 0;
1595 int slot = typos[field->which].slot&(XX-1);
1596
1597 if (field->which<PS_C || field->which>PS_DIO) continue;
1598 ll = 1LL<<field->which;
1599 if (bits&ll) continue;
1600 bits |= ll;
1601 for (i=0; i<mix.count; i++)
1602 tb.slot[slot] += mix.tb[i]->slot[slot];
1603 pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
1604 " %s: %*s,", typos[field->which].name,
1605 field->len, string_field(&tb, field));
1606 }
1607 *--pos = 0;
1608 lines = header_line(lines, 0);
1609 }
1610
1611 get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
1612 for (i = 0, is = ' '; *pos; pos++) {
1613 was = is;
1614 is = *pos;
1615 if (isspace(was) && !isspace(is) && i++==TT.sortpos && pos!=toybuf)
1616 pos[-1] = '[';
1617 if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
1618 }
1619 *pos = 0;
1620 lines = header_line(lines, 1);
1621 }
1622 if (!recalc && !FLAG(b))
1623 printf("\033[%dH\033[J", 1+TT.height-lines);
1624 recalc = 1;
1625
1626 for (i = 0; i<lines && i+topoff<mix.count; i++) {
1627
1628 int bold = !FLAG(b) && mix.tb[i+topoff]->state == 'R';
1629
1630 if (!FLAG(b) && i) putchar('\n');
1631 if (bold) printf("\033[1m");
1632 show_ps(mix.tb[i+topoff]);
1633 if (bold) printf("\033[m");
1634 }
1635
1636 if (TT.top.n && !--TT.top.n) {
1637 done++;
1638 break;
1639 }
1640
1641 now = millitime();
1642 if (timeout<=now) timeout = new.whence+TT.top.d;
1643 if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
1644
1645
1646 if (FLAG(b)) {
1647 msleep(timeout-now);
1648
1649 xputs("\n\n");
1650 continue;
1651 } else fflush(stdout);
1652
1653 i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
1654 if (i==-1 || i==3 || toupper(i)=='Q') {
1655 done++;
1656 break;
1657 }
1658 if (i==-2) break;
1659 if (i==-3) continue;
1660
1661
1662 if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
1663 else if (i==' ') {
1664 timeout = 0;
1665 break;
1666 } else if (toupper(i)=='R')
1667 ((struct ofields *)TT.kfields)->reverse *= -1;
1668 else {
1669 i -= 256;
1670 if (i == KEY_LEFT) setsort(TT.sortpos-1);
1671 else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
1672
1673 else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) {
1674 recalc = 0;
1675
1676 if (i == KEY_UP) topoff--;
1677 else if (i == KEY_DOWN) topoff++;
1678 else if (i == KEY_PGDN) topoff += lines;
1679 else if (i == KEY_PGUP) topoff -= lines;
1680 if (topoff<0) topoff = 0;
1681 if (topoff>mix.count) topoff = mix.count;
1682 }
1683 }
1684 continue;
1685 }
1686
1687 free(mix.tb);
1688 for (i=0; i<plold->count; i++) free(plold->tb[i]);
1689 free(plold->tb);
1690 } while (!done);
1691
1692 if (!FLAG(b)) tty_reset();
1693}
1694
1695static void top_setup(char *defo, char *defk)
1696{
1697 TT.ticks = sysconf(_SC_CLK_TCK);
1698 TT.tty = tty_fd() != -1;
1699
1700
1701 if (FLAG(b)) TT.width = TT.height = 99999;
1702 else {
1703
1704
1705 TT.time = millitime();
1706 start_redraw(&TT.width, &TT.height);
1707 }
1708
1709 comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
1710 comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
1711 TT.match_process = shared_match_process;
1712
1713 default_ko(defo, &TT.fields, "bad -o", TT.top.o);
1714 dlist_terminate(TT.fields);
1715
1716
1717 default_ko("-S", &TT.kfields, 0, 0);
1718 default_ko(defk, &TT.kfields, "bad -k", TT.top.k);
1719 dlist_terminate(TT.kfields);
1720 setsort(TT.top.s-1);
1721}
1722
1723void top_main(void)
1724{
1725 sprintf(toybuf, "PID,USER,%s%%CPU,%%MEM,TIME+,%s",
1726 TT.top.O ? "" : "PR,NI,VIRT,RES,SHR,S,",
1727 FLAG(H) ? "CMD:15=THREAD,NAME=PROCESS" : "ARGS");
1728 if (!TT.top.s) TT.top.s = TT.top.O ? 3 : 9;
1729 top_setup(toybuf, "-%CPU,-ETIME,-PID");
1730 if (TT.top.O) {
1731 struct ofields *field = TT.fields;
1732
1733 field = field->next->next;
1734 comma_args(TT.top.O, &field, "bad -O", parse_ko);
1735 }
1736
1737 top_common(merge_deltas);
1738}
1739
1740#define CLEANUP_top
1741#define FOR_iotop
1742#include "generated/flags.h"
1743
1744
1745static int iotop_filter(long long *oslot, long long *nslot, int milis)
1746{
1747
1748 if (!FLAG(a)) merge_deltas(oslot, nslot, milis);
1749 else oslot[SLOT_upticks] = ((millitime()-TT.time)*TT.ticks)/1000;
1750
1751 return !FLAG(O)||oslot[SLOT_iobytes+!FLAG(A)];
1752}
1753
1754void iotop_main(void)
1755{
1756 char *s1 = 0, *s2 = 0, *d = "D"+!!FLAG(A);
1757
1758 if (FLAG(K)) TT.forcek++;
1759
1760 top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d),
1761 s2 = xmprintf("-%sIO,-ETIME,-PID",d));
1762 free(s1);
1763 free(s2);
1764 top_common(iotop_filter);
1765}
1766
1767
1768
1769
1770#define FORCE_FLAGS
1771#define CLEANUP_iotop
1772#define FOR_pgrep
1773#include "generated/flags.h"
1774
1775struct regex_list {
1776 struct regex_list *next;
1777 regex_t reg;
1778};
1779
1780static void do_pgk(struct procpid *tb)
1781{
1782 if (TT.pgrep.signal) {
1783 if (kill(*tb->slot, TT.pgrep.signal)) {
1784 char *s = num_to_sig(TT.pgrep.signal);
1785
1786 if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
1787 perror_msg("%s->%lld", s, *tb->slot);
1788 }
1789 }
1790 if (!FLAG(c) && (!TT.pgrep.signal || TT.tty)) {
1791 printf("%lld", *tb->slot);
1792 if (FLAG(l))
1793 printf(" %s", tb->str+tb->offset[4]*!!FLAG(f));
1794
1795 printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
1796 }
1797}
1798
1799static void match_pgrep(void *p)
1800{
1801 struct procpid *tb = p;
1802 regmatch_t match;
1803 struct regex_list *reg;
1804 char *name = tb->str+tb->offset[4]*!!FLAG(f);
1805
1806
1807 if (TT.pgrep.self == *tb->slot) return;
1808
1809 if (TT.pgrep.regexes) {
1810 for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
1811 if (regexec(®->reg, name, 1, &match, 0)) continue;
1812 if (FLAG(x))
1813 if (match.rm_so || match.rm_eo!=strlen(name)) continue;
1814 break;
1815 }
1816 if (!FLAG(v) == !reg) return;
1817 }
1818
1819
1820 toys.exitval = 0;
1821
1822
1823 TT.sortpos++;
1824 if (FLAG(n)||FLAG(o)) {
1825 long long ll = tb->slot[SLOT_starttime];
1826
1827 if (FLAG(o)) ll *= -1;
1828 if (TT.time && TT.time>ll) return;
1829 TT.time = ll;
1830 free(TT.pgrep.snapshot);
1831 TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf);
1832 } else do_pgk(tb);
1833}
1834
1835static int pgrep_match_process(long long *slot)
1836{
1837 return !FLAG(v) == !!shared_match_process(slot);
1838}
1839
1840void pgrep_main(void)
1841{
1842 char **arg;
1843 struct regex_list *reg;
1844
1845 TT.pgrep.self = getpid();
1846
1847
1848 if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
1849 error_exit("bad -L '%s'", TT.pgrep.L);
1850
1851 comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest);
1852 comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest);
1853 comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest);
1854 comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest);
1855 comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest);
1856 comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest);
1857 comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest);
1858
1859 if ((toys.optflags&(FLAG_x|FLAG_f)) ||
1860 !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u)))
1861 if (!toys.optc) help_exit("No PATTERN");
1862
1863 if (FLAG(f)) TT.bits |= _PS_CMDLINE;
1864 for (arg = toys.optargs; *arg; arg++) {
1865 reg = xmalloc(sizeof(struct regex_list));
1866 xregcomp(®->reg, *arg, REG_EXTENDED);
1867 reg->next = TT.pgrep.regexes;
1868 TT.pgrep.regexes = reg;
1869 }
1870 TT.match_process = pgrep_match_process;
1871 TT.show_process = match_pgrep;
1872
1873
1874 toys.exitval = 1;
1875
1876 dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC, get_ps);
1877 if (FLAG(c)) printf("%d\n", TT.sortpos);
1878 if (TT.pgrep.snapshot) {
1879 do_pgk(TT.pgrep.snapshot);
1880 if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot);
1881 }
1882 if (TT.pgrep.d) xputc('\n');
1883}
1884
1885#define CLEANUP_pgrep
1886#define FOR_pkill
1887#include "generated/flags.h"
1888
1889void pkill_main(void)
1890{
1891 char **args = toys.optargs;
1892
1893 if (!FLAG(l) && *args && **args=='-') TT.pgrep.L = *(args++)+1;
1894 if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM;
1895 if (FLAG(V)) TT.tty = 1;
1896 pgrep_main();
1897}
1898