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