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