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