aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Andersen <andersen@codepoet.org>2002-09-30 20:52:10 +0000
committerEric Andersen <andersen@codepoet.org>2002-09-30 20:52:10 +0000
commit08a72209c9b5cb74d851ef5c3db8c16eee6f657d (patch)
treebaa06980ecaf8a8cedb27887f6da8b6d47e9f8c6
parent02e6ba91e887bd11146a57185b223582f56f3f09 (diff)
downloadbusybox-w32-08a72209c9b5cb74d851ef5c3db8c16eee6f657d.tar.gz
busybox-w32-08a72209c9b5cb74d851ef5c3db8c16eee6f657d.tar.bz2
busybox-w32-08a72209c9b5cb74d851ef5c3db8c16eee6f657d.zip
last_patch58 from vodz:
Ok. I generate patch for include to busybox-devel my work with top (original author give me maintaining) and telnetd (my support and unofficial maintaining) applets. Docs changes also: added awk, netstat, time applets to list ;)
-rw-r--r--AUTHORS3
-rw-r--r--docs/busybox_footer.pod5
-rw-r--r--docs/busybox_header.pod28
-rw-r--r--include/applets.h3
-rw-r--r--include/usage.h8
-rw-r--r--networking/Makefile.in1
-rw-r--r--networking/config.in1
-rw-r--r--networking/telnetd.c538
-rw-r--r--procps/top.c875
9 files changed, 1153 insertions, 309 deletions
diff --git a/AUTHORS b/AUTHORS
index 47d450248..854ee12d8 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -55,7 +55,8 @@ Glenn McGrath <bug1@optushome.com.au>
55 ar, dpkg, dpkg-deb 55 ar, dpkg, dpkg-deb
56 56
57Vladimir Oleynik <dzo@simtreas.ru> 57Vladimir Oleynik <dzo@simtreas.ru>
58 cmdedit; ports: ash, stty, traceroute; locale, various fixes 58 cmdedit; ports: ash, stty, traceroute, telnetd, top;
59 locale, various fixes
59 and irreconcilable critic of everything not perfect. 60 and irreconcilable critic of everything not perfect.
60 61
61Bruce Perens <bruce@pixar.com> 62Bruce Perens <bruce@pixar.com>
diff --git a/docs/busybox_footer.pod b/docs/busybox_footer.pod
index 7c02003fc..b3de7e536 100644
--- a/docs/busybox_footer.pod
+++ b/docs/busybox_footer.pod
@@ -113,7 +113,8 @@ Glenn McGrath <bug1@netconnect.com.au>
113 113
114Vladimir Oleynik <dzo@simtreas.ru> 114Vladimir Oleynik <dzo@simtreas.ru>
115 115
116 cmdedit; ports: ash, stty, traceroute; locale, various fixes 116 cmdedit; ports: ash, stty, traceroute, telnetd, top;
117 locale, various fixes
117 and irreconcilable critic of everything not perfect. 118 and irreconcilable critic of everything not perfect.
118 119
119=for html <br> 120=for html <br>
@@ -166,4 +167,4 @@ Enrique Zanardi <ezanardi@ull.es>
166 167
167=cut 168=cut
168 169
169# $Id: busybox_footer.pod,v 1.5 2001/12/18 14:06:01 andersen Exp $ 170# $Id: busybox_footer.pod,v 1.6 2002/09/30 20:52:08 andersen Exp $
diff --git a/docs/busybox_header.pod b/docs/busybox_header.pod
index 0ea7dbfe7..517be81a3 100644
--- a/docs/busybox_header.pod
+++ b/docs/busybox_header.pod
@@ -55,20 +55,20 @@ terse runtime description of their behavior.
55 55
56Currently defined functions include: 56Currently defined functions include:
57 57
58 addgroup, adduser, adjtimex, ar, basename, busybox, cat, chgrp, chmod, 58 addgroup, adduser, adjtimex, ar, awk, basename, busybox, cat, chgrp,
59chown, chroot, chvt, clear, cmp, cp, cpio, cut, date, dc, dd, deallocvt, 59chmod, chown, chroot, chvt, clear, cmp, cp, cpio, cut, date, dc, dd,
60deluser, df, dirname, dmesg, dos2unix, dpkg, dpkg-deb, du, dumpkmap, dutmp, 60deallocvt, deluser, df, dirname, dmesg, dos2unix, dpkg, dpkg-deb, du,
61echo, expr, false, fbset, fdflush, find, free, freeramdisk, fsck.minix, 61dumpkmap, dutmp, echo, expr, false, fbset, fdflush, find, free,
62getopt, getty, grep, gunzip, gzip, halt, head, hostid, hostname, id, 62freeramdisk, fsck.minix, getopt, getty, grep, gunzip, gzip, halt, head,
63ifconfig, init, insmod, kill, killall, klogd, length, ln, loadacm, 63hostid, hostname, id, ifconfig, init, insmod, kill, killall, klogd, length,
64loadfont, loadkmap, logger, logname, ls, lsmod, makedevs, md5sum, mkdir, 64ln, loadacm, loadfont, loadkmap, logger, logname, ls, lsmod, makedevs,
65mkfifo, mkfs.minix, mknod, mkswap, mktemp, more, mount, mt, mv, nc, 65md5sum, mkdir, mkfifo, mkfs.minix, mknod, mkswap, mktemp, more, mount, mt,
66nslookup, ping, pivot_root, poweroff, printf, ps, pwd, rdate, readlink, 66mv, nc, netstat, nslookup, ping, pivot_root, poweroff, printf, ps, pwd,
67reboot, renice, reset, rm, rmdir, rmmod, route, rpm2cpio, sed, setkeycodes, 67rdate, readlink, reboot, renice, reset, rm, rmdir, rmmod, route, rpm2cpio,
68sh, sleep, sort, stty, swapoff, swapon, sync, syslogd, tail, tar, tee, 68sed, setkeycodes, sh, sleep, sort, stty, swapoff, swapon, sync, syslogd,
69telnet, test, tftp, touch, tr, true, tty, umount, uname, uniq, unix2dos, 69tail, tar, tee, telnet, telnetd, test, tftp, time, top, touch, tr, true,
70update, uptime, usleep, uudecode, uuencode, watchdog, wc, wget, which, 70tty, umount, uname, uniq, unix2dos, update, uptime, usleep, uudecode,
71whoami, xargs, yes, zcat, [ 71uuencode, watchdog, wc, wget, which, whoami, xargs, yes, zcat, [
72 72
73=over 4 73=over 4
74 74
diff --git a/include/applets.h b/include/applets.h
index 9508c3a4b..60928aef2 100644
--- a/include/applets.h
+++ b/include/applets.h
@@ -464,6 +464,9 @@
464#ifdef CONFIG_TELNET 464#ifdef CONFIG_TELNET
465 APPLET(telnet, telnet_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) 465 APPLET(telnet, telnet_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
466#endif 466#endif
467#ifdef CONFIG_TELNETD
468 APPLET(telnetd, telnetd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
469#endif
467#ifdef CONFIG_TEST 470#ifdef CONFIG_TEST
468 APPLET(test, test_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) 471 APPLET(test, test_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
469#endif 472#endif
diff --git a/include/usage.h b/include/usage.h
index 33a81c467..1e0fe6805 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -1801,6 +1801,14 @@
1801 "Telnet is used to establish interactive communication with another\n"\ 1801 "Telnet is used to establish interactive communication with another\n"\
1802 "computer over a network using the TELNET protocol." 1802 "computer over a network using the TELNET protocol."
1803 1803
1804#define telnetd_trivial_usage \
1805 "[OPTION]"
1806#define telnetd_full_usage \
1807 "Telnetd listens for incoming TELNET connections on PORT.\n"\
1808 "Options:\n" \
1809 "\t-p PORT\tlisten for connections on PORT (default 23)\n"\
1810 "\t-l LOGIN\texec LOGIN on connect (default /bin/sh)"
1811
1804#define test_trivial_usage \ 1812#define test_trivial_usage \
1805 "EXPRESSION\n or [ EXPRESSION ]" 1813 "EXPRESSION\n or [ EXPRESSION ]"
1806#define test_full_usage \ 1814#define test_full_usage \
diff --git a/networking/Makefile.in b/networking/Makefile.in
index 425b5d3d9..510c8119d 100644
--- a/networking/Makefile.in
+++ b/networking/Makefile.in
@@ -32,6 +32,7 @@ NETWORKING-$(CONFIG_PING) += ping.o
32NETWORKING-$(CONFIG_PING6) += ping6.o 32NETWORKING-$(CONFIG_PING6) += ping6.o
33NETWORKING-$(CONFIG_ROUTE) += route.o 33NETWORKING-$(CONFIG_ROUTE) += route.o
34NETWORKING-$(CONFIG_TELNET) += telnet.o 34NETWORKING-$(CONFIG_TELNET) += telnet.o
35NETWORKING-$(CONFIG_TELNETD) += telnetd.o
35NETWORKING-$(CONFIG_TFTP) += tftp.o 36NETWORKING-$(CONFIG_TFTP) += tftp.o
36NETWORKING-$(CONFIG_TRACEROUTE) += traceroute.o 37NETWORKING-$(CONFIG_TRACEROUTE) += traceroute.o
37NETWORKING-$(CONFIG_UDHCPC) += udhcpc.o 38NETWORKING-$(CONFIG_UDHCPC) += udhcpc.o
diff --git a/networking/config.in b/networking/config.in
index bc11d83a7..4438a8c6c 100644
--- a/networking/config.in
+++ b/networking/config.in
@@ -34,6 +34,7 @@ bool 'telnet' CONFIG_TELNET
34if [ "$CONFIG_TELNET" = "y" ]; then 34if [ "$CONFIG_TELNET" = "y" ]; then
35 bool ' Pass TERM type to remote host' CONFIG_FEATURE_TELNET_TTYPE 35 bool ' Pass TERM type to remote host' CONFIG_FEATURE_TELNET_TTYPE
36fi 36fi
37bool 'telnetd' CONFIG_TELNETD
37bool 'tftp' CONFIG_TFTP 38bool 'tftp' CONFIG_TFTP
38if [ "$CONFIG_TFTP" = "y" ]; then 39if [ "$CONFIG_TFTP" = "y" ]; then
39 bool ' Enable "get" command' CONFIG_FEATURE_TFTP_GET 40 bool ' Enable "get" command' CONFIG_FEATURE_TFTP_GET
diff --git a/networking/telnetd.c b/networking/telnetd.c
new file mode 100644
index 000000000..edc018a2a
--- /dev/null
+++ b/networking/telnetd.c
@@ -0,0 +1,538 @@
1/* $Id: telnetd.c,v 1.1 2002/09/30 20:52:04 andersen Exp $
2 *
3 * Simple telnet server
4 * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
5 *
6 * This file is distributed under the Gnu Public License (GPL),
7 * please see the file LICENSE for further information.
8 *
9 * ---------------------------------------------------------------------------
10 * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
11 ****************************************************************************
12 *
13 * The telnetd manpage says it all:
14 *
15 * Telnetd operates by allocating a pseudo-terminal device (see pty(4)) for
16 * a client, then creating a login process which has the slave side of the
17 * pseudo-terminal as stdin, stdout, and stderr. Telnetd manipulates the
18 * master side of the pseudo-terminal, implementing the telnet protocol and
19 * passing characters between the remote client and the login process.
20 *
21 * Vladimir Oleynik <dzo@simtreas.ru> 2001
22 * Set process group corrections, initial busybox port
23 */
24
25/*#define DEBUG 1 */
26
27#include <sys/time.h>
28#include <sys/socket.h>
29#include <sys/wait.h>
30#include <string.h>
31#include <stdlib.h>
32#include <unistd.h>
33#include <errno.h>
34#include <netinet/in.h>
35#include <fcntl.h>
36#include <stdio.h>
37#include <signal.h>
38#include <termios.h>
39#ifdef DEBUG
40#define TELCMDS
41#define TELOPTS
42#endif
43#include <arpa/telnet.h>
44#include <ctype.h>
45#include <sys/syslog.h>
46
47#include "busybox.h"
48
49#define BUFSIZE 4000
50
51static const char *loginpath = "/bin/sh";
52
53/* shell name and arguments */
54
55static const char *argv_init[] = {NULL, NULL};
56
57/* structure that describes a session */
58
59struct tsession {
60 struct tsession *next;
61 int sockfd, ptyfd;
62 int shell_pid;
63 /* two circular buffers */
64 char *buf1, *buf2;
65 int rdidx1, wridx1, size1;
66 int rdidx2, wridx2, size2;
67};
68
69/*
70
71 This is how the buffers are used. The arrows indicate the movement
72 of data.
73
74 +-------+ wridx1++ +------+ rdidx1++ +----------+
75 | | <-------------- | buf1 | <-------------- | |
76 | | size1-- +------+ size1++ | |
77 | pty | | socket |
78 | | rdidx2++ +------+ wridx2++ | |
79 | | --------------> | buf2 | --------------> | |
80 +-------+ size2++ +------+ size2-- +----------+
81
82 Each session has got two buffers.
83
84*/
85
86static int maxfd;
87
88static struct tsession *sessions;
89
90
91/*
92
93 Remove all IAC's from the buffer pointed to by bf (recieved IACs are ignored
94 and must be removed so as to not be interpreted by the terminal). Make an
95 uninterrupted string of characters fit for the terminal. Do this by packing
96 all characters meant for the terminal sequentially towards the end of bf.
97
98 Return a pointer to the beginning of the characters meant for the terminal.
99 and make *num_totty the number of characters that should be sent to
100 the terminal.
101
102 Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
103 past (bf + len) then that IAC will be left unprocessed and *processed will be
104 less than len.
105
106 FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
107 what is the escape character? We aren't handling that situation here.
108
109 */
110static char *
111remove_iacs(struct tsession *ts, int *pnum_totty) {
112 unsigned char *ptr0 = ts->buf1 + ts->wridx1;
113 unsigned char *ptr = ptr0;
114 unsigned char *totty = ptr;
115 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
116 int processed;
117 int num_totty;
118
119 while (ptr < end) {
120 if (*ptr != IAC) {
121 *totty++ = *ptr++;
122 }
123 else {
124 if ((ptr+2) < end) {
125 /* the entire IAC is contained in the buffer
126 we were asked to process. */
127#ifdef DEBUG
128 fprintf(stderr, "Ignoring IAC %s,%s\n",
129 *ptr, TELCMD(*(ptr+1)), TELOPT(*(ptr+2)));
130#endif
131 ptr += 3;
132 } else {
133 /* only the beginning of the IAC is in the
134 buffer we were asked to process, we can't
135 process this char. */
136 break;
137 }
138 }
139 }
140
141 processed = ptr - ptr0;
142 num_totty = totty - ptr0;
143 /* the difference between processed and num_to tty
144 is all the iacs we removed from the stream.
145 Adjust buf1 accordingly. */
146 ts->wridx1 += processed - num_totty;
147 ts->size1 -= processed - num_totty;
148 *pnum_totty = num_totty;
149 /* move the chars meant for the terminal towards the end of the
150 buffer. */
151 return memmove(ptr - num_totty, ptr0, num_totty);
152}
153
154
155static int
156getpty(char *line)
157{
158 int p;
159#ifdef HAVE_DEVPTS_FS
160 p = open("/dev/ptmx", 2);
161 if (p > 0) {
162 grantpt(p);
163 unlockpt(p);
164 strcpy(line, ptsname(p));
165 return(p);
166 }
167#else
168 struct stat stb;
169 int i;
170 int j;
171
172 strcpy(line, "/dev/ptyXX");
173
174 for (i = 0; i < 16; i++) {
175 line[8] = "pqrstuvwxyzabcde"[i];
176 line[9] = '0';
177 if (stat(line, &stb) < 0) {
178 continue;
179 }
180 for (j = 0; j < 16; j++) {
181 line[9] = j < 10 ? j + '0' : j - 10 + 'a';
182 if ((p = open(line, O_RDWR | O_NOCTTY)) >= 0) {
183 line[5] = 't';
184 return p;
185 }
186 }
187 }
188#endif /* HAVE_DEVPTS_FS */
189 return -1;
190}
191
192
193static void
194send_iac(struct tsession *ts, unsigned char command, int option)
195{
196 /* We rely on that there is space in the buffer for now. */
197 char *b = ts->buf2 + ts->rdidx2;
198 *b++ = IAC;
199 *b++ = command;
200 *b++ = option;
201 ts->rdidx2 += 3;
202 ts->size2 += 3;
203}
204
205
206static struct tsession *
207make_new_session(int sockfd)
208{
209 struct termios termbuf;
210 int pty, pid;
211 char tty_name[32];
212 struct tsession *ts = malloc(sizeof(struct tsession) + BUFSIZE * 2);
213
214 ts->buf1 = (char *)(&ts[1]);
215 ts->buf2 = ts->buf1 + BUFSIZE;
216
217 ts->sockfd = sockfd;
218
219 ts->rdidx1 = ts->wridx1 = ts->size1 = 0;
220 ts->rdidx2 = ts->wridx2 = ts->size2 = 0;
221
222 /* Got a new connection, set up a tty and spawn a shell. */
223
224 pty = getpty(tty_name);
225
226 if (pty < 0) {
227 syslog_msg(LOG_USER, LOG_ERR, "All network ports in use!");
228 return 0;
229 }
230
231 if (pty > maxfd)
232 maxfd = pty;
233
234 ts->ptyfd = pty;
235
236 /* Make the telnet client understand we will echo characters so it
237 * should not do it locally. We don't tell the client to run linemode,
238 * because we want to handle line editing and tab completion and other
239 * stuff that requires char-by-char support.
240 */
241
242 send_iac(ts, DO, TELOPT_ECHO);
243 send_iac(ts, DO, TELOPT_LFLOW);
244 send_iac(ts, WILL, TELOPT_ECHO);
245 send_iac(ts, WILL, TELOPT_SGA);
246
247
248 if ((pid = fork()) < 0) {
249 syslog_msg(LOG_USER, LOG_ERR, "Can`t forking");
250 }
251 if (pid == 0) {
252 /* In child, open the child's side of the tty. */
253 int i;
254
255 for(i = 0; i <= maxfd; i++)
256 close(i);
257 /* make new process group */
258 setsid();
259
260 if (open(tty_name, O_RDWR /*| O_NOCTTY*/) < 0) {
261 syslog_msg(LOG_USER, LOG_ERR, "Could not open tty");
262 exit(1);
263 }
264 dup(0);
265 dup(0);
266
267 tcsetpgrp(0, getpid());
268
269 /* The pseudo-terminal allocated to the client is configured to operate in
270 * cooked mode, and with XTABS CRMOD enabled (see tty(4)).
271 */
272
273 tcgetattr(0, &termbuf);
274 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
275 termbuf.c_oflag |= ONLCR|XTABS;
276 termbuf.c_iflag |= ICRNL;
277 termbuf.c_iflag &= ~IXOFF;
278 /*termbuf.c_lflag &= ~ICANON;*/
279 tcsetattr(0, TCSANOW, &termbuf);
280
281 /* exec shell, with correct argv and env */
282 execv(loginpath, (char *const *)argv_init);
283
284 /* NOT REACHED */
285 syslog_msg(LOG_USER, LOG_ERR, "execv error");
286 exit(1);
287 }
288
289 ts->shell_pid = pid;
290
291 return ts;
292}
293
294static void
295free_session(struct tsession *ts)
296{
297 struct tsession *t = sessions;
298
299 /* Unlink this telnet session from the session list. */
300 if(t == ts)
301 sessions = ts->next;
302 else {
303 while(t->next != ts)
304 t = t->next;
305 t->next = ts->next;
306 }
307
308 kill(ts->shell_pid, SIGKILL);
309
310 wait4(ts->shell_pid, NULL, 0, NULL);
311
312 close(ts->ptyfd);
313 close(ts->sockfd);
314
315 if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
316 maxfd--;
317 if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
318 maxfd--;
319
320 free(ts);
321}
322
323int
324telnetd_main(int argc, char **argv)
325{
326 struct sockaddr_in sa;
327 int master_fd;
328 fd_set rdfdset, wrfdset;
329 int selret;
330 int on = 1;
331 int portnbr = 23;
332 int c;
333
334 /* check if user supplied a port number */
335
336 for (;;) {
337 c = getopt( argc, argv, "p:l:");
338 if (c == EOF) break;
339 switch (c) {
340 case 'p':
341 portnbr = atoi(optarg);
342 break;
343 case 'l':
344 loginpath = strdup (optarg);
345 break;
346 default:
347 show_usage();
348 }
349 }
350
351 if (access(loginpath, X_OK) < 0) {
352 error_msg_and_die ("'%s' unavailable.", loginpath);
353 }
354
355 argv_init[0] = loginpath;
356 sessions = 0;
357
358 /* Grab a TCP socket. */
359
360 master_fd = socket(AF_INET, SOCK_STREAM, 0);
361 if (master_fd < 0) {
362 perror_msg_and_die("socket");
363 }
364 (void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
365
366 /* Set it to listen to specified port. */
367
368 memset((void *)&sa, 0, sizeof(sa));
369 sa.sin_family = AF_INET;
370 sa.sin_port = htons(portnbr);
371
372 if (bind(master_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
373 perror_msg_and_die("bind");
374 }
375
376 if (listen(master_fd, 1) < 0) {
377 perror_msg_and_die("listen");
378 }
379
380 if (daemon(0, 0) < 0)
381 perror_msg_and_die("daemon");
382
383
384 maxfd = master_fd;
385
386 do {
387 struct tsession *ts;
388
389 FD_ZERO(&rdfdset);
390 FD_ZERO(&wrfdset);
391
392 /* select on the master socket, all telnet sockets and their
393 * ptys if there is room in their respective session buffers.
394 */
395
396 FD_SET(master_fd, &rdfdset);
397
398 ts = sessions;
399 while (ts) {
400 /* buf1 is used from socket to pty
401 * buf2 is used from pty to socket
402 */
403 if (ts->size1 > 0) {
404 FD_SET(ts->ptyfd, &wrfdset); /* can write to pty */
405 }
406 if (ts->size1 < BUFSIZE) {
407 FD_SET(ts->sockfd, &rdfdset); /* can read from socket */
408 }
409 if (ts->size2 > 0) {
410 FD_SET(ts->sockfd, &wrfdset); /* can write to socket */
411 }
412 if (ts->size2 < BUFSIZE) {
413 FD_SET(ts->ptyfd, &rdfdset); /* can read from pty */
414 }
415 ts = ts->next;
416 }
417
418 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
419
420 if (!selret)
421 break;
422
423 /* First check for and accept new sessions. */
424 if (FD_ISSET(master_fd, &rdfdset)) {
425 int fd, salen;
426
427 salen = sizeof(sa);
428 if ((fd = accept(master_fd, (struct sockaddr *)&sa,
429 &salen)) < 0) {
430 continue;
431 } else {
432 /* Create a new session and link it into
433 our active list. */
434 struct tsession *new_ts = make_new_session(fd);
435 if (new_ts) {
436 new_ts->next = sessions;
437 sessions = new_ts;
438 if (fd > maxfd)
439 maxfd = fd;
440 } else {
441 close(fd);
442 }
443 }
444 }
445
446 /* Then check for data tunneling. */
447
448 ts = sessions;
449 while (ts) { /* For all sessions... */
450 int maxlen, w, r;
451 struct tsession *next = ts->next; /* in case we free ts. */
452
453 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
454 int num_totty;
455 char *ptr;
456 /* Write to pty from buffer 1. */
457
458 ptr = remove_iacs(ts, &num_totty);
459
460 w = write(ts->ptyfd, ptr, num_totty);
461 if (w < 0) {
462 free_session(ts);
463 ts = next;
464 continue;
465 }
466 ts->wridx1 += w;
467 ts->size1 -= w;
468 if (ts->wridx1 == BUFSIZE)
469 ts->wridx1 = 0;
470 }
471
472 if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset)) {
473 /* Write to socket from buffer 2. */
474 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
475 w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen);
476 if (w < 0) {
477 free_session(ts);
478 ts = next;
479 continue;
480 }
481 ts->wridx2 += w;
482 ts->size2 -= w;
483 if (ts->wridx2 == BUFSIZE)
484 ts->wridx2 = 0;
485 }
486
487 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset)) {
488 /* Read from socket to buffer 1. */
489 maxlen = MIN(BUFSIZE - ts->rdidx1,
490 BUFSIZE - ts->size1);
491 r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen);
492 if (!r || (r < 0 && errno != EINTR)) {
493 free_session(ts);
494 ts = next;
495 continue;
496 }
497 if(!*(ts->buf1 + ts->rdidx1 + r - 1)) {
498 r--;
499 if(!r)
500 continue;
501 }
502 ts->rdidx1 += r;
503 ts->size1 += r;
504 if (ts->rdidx1 == BUFSIZE)
505 ts->rdidx1 = 0;
506 }
507
508 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
509 /* Read from pty to buffer 2. */
510 maxlen = MIN(BUFSIZE - ts->rdidx2,
511 BUFSIZE - ts->size2);
512 r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
513 if (!r || (r < 0 && errno != EINTR)) {
514 free_session(ts);
515 ts = next;
516 continue;
517 }
518 ts->rdidx2 += r;
519 ts->size2 += r;
520 if (ts->rdidx2 == BUFSIZE)
521 ts->rdidx2 = 0;
522 }
523
524 if (ts->size1 == 0) {
525 ts->rdidx1 = 0;
526 ts->wridx1 = 0;
527 }
528 if (ts->size2 == 0) {
529 ts->rdidx2 = 0;
530 ts->wridx2 = 0;
531 }
532 ts = next;
533 }
534
535 } while (1);
536
537 return 0;
538}
diff --git a/procps/top.c b/procps/top.c
index f6bbb23cd..06ae77119 100644
--- a/procps/top.c
+++ b/procps/top.c
@@ -1,60 +1,330 @@
1/* 1/*
2 * A tiny 'top' utility. 2 * A tiny 'top' utility.
3 * 3 *
4 * This is written specifically for the linux /proc/<PID>/status 4 * This is written specifically for the linux /proc/<PID>/stat(m)
5 * file format, but it checks that the file actually conforms to the 5 * files format.
6 * format that this utility expects. 6
7 7 * This reads the PIDs of all processes and their status and shows
8 * This reads the PIDs of all processes at startup and then shows the 8 * the status of processes (first ones that fit to screen) at given
9 * status of those processes at given intervals. User can give 9 * intervals.
10 * maximum number of processes to show. If a process exits, it's PID
11 * is shown as 'EXIT'. If new processes are started while this works,
12 * it doesn't add them to the list of shown processes.
13 * 10 *
14 * NOTES: 11 * NOTES:
15 * - At startup this changes to /proc, all the reads are then 12 * - At startup this changes to /proc, all the reads are then
16 * relative to that. 13 * relative to that.
17 * - Includes code from the scandir() manual page.
18 *
19 * TODO:
20 * - ppid, uid etc could be read only once when program starts
21 * and rest of the information could be gotten from the
22 * /proc/<PID>/statm file.
23 * - Add process CPU and memory usage *percentages*.
24 * 14 *
25 * (C) Eero Tamminen <oak at welho dot com> 15 * (C) Eero Tamminen <oak at welho dot com>
16 *
17 * Rewroted by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru>
18 */
19
20/* Original code Copyrights */
21/*
22 * Copyright (c) 1992 Branko Lankester
23 * Copyright (c) 1992 Roger Binns
24 * Copyright (C) 1994-1996 Charles L. Blake.
25 * Copyright (C) 1992-1998 Michael K. Johnson
26 * May be distributed under the conditions of the
27 * GNU Library General Public License
26 */ 28 */
29
30#include <sys/types.h>
27#include <stdio.h> 31#include <stdio.h>
28#include <stdlib.h> 32#include <stdlib.h>
29#include <unistd.h> 33#include <unistd.h>
30#include <dirent.h> 34#include <dirent.h>
31#include <string.h> 35#include <string.h>
32#include <sys/ioctl.h> 36#include <sys/ioctl.h>
37#include <sys/stat.h>
38/* get page info */
39#include <asm/page.h>
33#include "busybox.h" 40#include "busybox.h"
34 41
42//#define FEATURE_CPU_USAGE_PERCENTAGE /* + 2k */
43
44#ifdef FEATURE_CPU_USAGE_PERCENTAGE
45#include <time.h>
46#include <sys/time.h>
47#include <fcntl.h>
48#include <netinet/in.h> /* htons */
49#endif
50
35 51
36/* process information taken from /proc,
37 * The code takes into account how long the fields below are,
38 * starting from copying the file from 'status' file to displaying it!
39 */
40typedef struct { 52typedef struct {
41 char uid[6]; /* User ID */ 53 int pid;
42 char pid[6]; /* Pid */ 54 char user[9];
43 char ppid[6]; /* Parent Pid */ 55 char state[4];
44 char name[12]; /* Name */ 56 unsigned long rss;
45 char cmd[20]; /* command line[read/show size] */ 57 int ppid;
46 char state[2]; /* State: S, W... */ 58#ifdef FEATURE_CPU_USAGE_PERCENTAGE
47 char size[9]; /* VmSize */ 59 unsigned pcpu;
48 char lck[9]; /* VmLck */ 60 unsigned long stime, utime;
49 char rss[9]; /* VmRSS */ 61#endif
50 char data[9]; /* VmData */ 62 char *cmd;
51 char stk[9]; /* VmStk */ 63
52 char exe[9]; /* VmExe */ 64 /* basename of executable file in call to exec(2),
53 char lib[9]; /* VmLib */ 65 size from kernel headers */
66 char short_cmd[16];
54} status_t; 67} status_t;
55 68
69typedef int (*cmp_t)(status_t *P, status_t *Q);
70
71static status_t *top; /* Hehe */
72static int ntop;
73
74
75static int pid_sort (status_t *P, status_t *Q)
76{
77 int p = P->pid;
78 int q = Q->pid;
79
80 if( p < q ) return -1;
81 if( p > q ) return 1;
82 return 0;
83}
84
85static int mem_sort (status_t *P, status_t *Q)
86{
87 long p = P->rss;
88 long q = Q->rss;
89
90 if( p > q ) return -1;
91 if( p < q ) return 1;
92 return 0;
93}
94
95#ifdef FEATURE_CPU_USAGE_PERCENTAGE
96
97#define sort_depth 3
98static cmp_t sort_function[sort_depth];
99
100static int pcpu_sort (status_t *P, status_t *Q)
101{
102 int p = P->pcpu;
103 int q = Q->pcpu;
104
105 if( p > q ) return -1;
106 if( p < q ) return 1;
107 return 0;
108}
109
110static int time_sort (status_t *P, status_t *Q)
111{
112 long p = P->stime;
113 long q = Q->stime;
114
115 p += P->utime;
116 q += Q->utime;
117 if( p > q ) return -1;
118 if( p < q ) return 1;
119 return 0;
120}
121
122int mult_lvl_cmp(void* a, void* b) {
123 int i, cmp_val;
124
125 for(i = 0; i < sort_depth; i++) {
126 cmp_val = (*sort_function[i])(a, b);
127 if (cmp_val != 0)
128 return cmp_val;
129 }
130 return 0;
131}
132
133/* This structure stores some critical information from one frame to
134 the next. mostly used for sorting. Added cumulative and resident fields. */
135struct save_hist {
136 int ticks;
137 int pid;
138 int utime;
139 int stime;
140};
141
142/*
143 * Calculates percent cpu usage for each task.
144 */
145
146static struct save_hist *save_history;
147
148static unsigned long Hertz;
149
150/***********************************************************************
151 * Some values in /proc are expressed in units of 1/HZ seconds, where HZ
152 * is the kernel clock tick rate. One of these units is called a jiffy.
153 * The HZ value used in the kernel may vary according to hacker desire.
154 * According to Linus Torvalds, this is not true. He considers the values
155 * in /proc as being in architecture-dependant units that have no relation
156 * to the kernel clock tick rate. Examination of the kernel source code
157 * reveals that opinion as wishful thinking.
158 *
159 * In any case, we need the HZ constant as used in /proc. (the real HZ value
160 * may differ, but we don't care) There are several ways we could get HZ:
161 *
162 * 1. Include the kernel header file. If it changes, recompile this library.
163 * 2. Use the sysconf() function. When HZ changes, recompile the C library!
164 * 3. Ask the kernel. This is obviously correct...
165 *
166 * Linus Torvalds won't let us ask the kernel, because he thinks we should
167 * not know the HZ value. Oh well, we don't have to listen to him.
168 * Someone smuggled out the HZ value. :-)
169 *
170 * This code should work fine, even if Linus fixes the kernel to match his
171 * stated behavior. The code only fails in case of a partial conversion.
172 *
173 */
174
175#define FILE_TO_BUF(filename, fd) do{ \
176 if (fd == -1 && (fd = open(filename, O_RDONLY)) == -1) { \
177 perror_msg_and_die("/proc not be mounted?"); \
178 } \
179 lseek(fd, 0L, SEEK_SET); \
180 if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) { \
181 perror_msg_and_die("%s", filename); \
182 } \
183 buf[local_n] = '\0'; \
184}while(0)
185
186#define FILE_TO_BUF2(filename, fd) do{ \
187 lseek(fd, 0L, SEEK_SET); \
188 if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) { \
189 perror_msg_and_die("%s", filename); \
190 } \
191 buf[local_n] = '\0'; \
192}while(0)
193
194static void init_Hertz_value(void) {
195 unsigned long user_j, nice_j, sys_j, other_j; /* jiffies (clock ticks) */
196 double up_1, up_2, seconds;
197 unsigned long jiffies, h;
198 char buf[80];
199 int uptime_fd = -1;
200 int stat_fd = -1;
201
202 long smp_num_cpus = sysconf(_SC_NPROCESSORS_CONF);
203
204 if(smp_num_cpus<1) smp_num_cpus=1;
205 do {
206 int local_n;
207
208 FILE_TO_BUF("uptime", uptime_fd);
209 up_1 = strtod(buf, 0);
210 FILE_TO_BUF("stat", stat_fd);
211 sscanf(buf, "cpu %lu %lu %lu %lu", &user_j, &nice_j, &sys_j, &other_j);
212 FILE_TO_BUF2("uptime", uptime_fd);
213 up_2 = strtod(buf, 0);
214 } while((long)( (up_2-up_1)*1000.0/up_1 )); /* want under 0.1% error */
215
216 close(uptime_fd);
217 close(stat_fd);
218
219 jiffies = user_j + nice_j + sys_j + other_j;
220 seconds = (up_1 + up_2) / 2;
221 h = (unsigned long)( (double)jiffies/seconds/smp_num_cpus );
222 /* actual values used by 2.4 kernels: 32 64 100 128 1000 1024 1200 */
223 switch(h){
224 case 30 ... 34 : Hertz = 32; break; /* ia64 emulator */
225 case 48 ... 52 : Hertz = 50; break;
226 case 58 ... 62 : Hertz = 60; break;
227 case 63 ... 65 : Hertz = 64; break; /* StrongARM /Shark */
228 case 95 ... 105 : Hertz = 100; break; /* normal Linux */
229 case 124 ... 132 : Hertz = 128; break; /* MIPS, ARM */
230 case 195 ... 204 : Hertz = 200; break; /* normal << 1 */
231 case 253 ... 260 : Hertz = 256; break;
232 case 295 ... 304 : Hertz = 300; break; /* 3 cpus */
233 case 393 ... 408 : Hertz = 400; break; /* normal << 2 */
234 case 495 ... 504 : Hertz = 500; break; /* 5 cpus */
235 case 595 ... 604 : Hertz = 600; break; /* 6 cpus */
236 case 695 ... 704 : Hertz = 700; break; /* 7 cpus */
237 case 790 ... 808 : Hertz = 800; break; /* normal << 3 */
238 case 895 ... 904 : Hertz = 900; break; /* 9 cpus */
239 case 990 ... 1010 : Hertz = 1000; break; /* ARM */
240 case 1015 ... 1035 : Hertz = 1024; break; /* Alpha, ia64 */
241 case 1095 ... 1104 : Hertz = 1100; break; /* 11 cpus */
242 case 1180 ... 1220 : Hertz = 1200; break; /* Alpha */
243 default:
244 /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */
245 Hertz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL;
246 }
247}
248
249static void do_stats(void)
250{
251 struct timeval t;
252 static struct timeval oldtime;
253 struct timezone timez;
254 float elapsed_time;
255
256 status_t *cur;
257 int total_time, i, n;
258 static int prev_count;
259 int systime, usrtime, pid;
260
261 struct save_hist *New_save_hist;
262
263 /*
264 * Finds the current time (in microseconds) and calculates the time
265 * elapsed since the last update.
266 */
267 gettimeofday(&t, &timez);
268 elapsed_time = (t.tv_sec - oldtime.tv_sec)
269 + (float) (t.tv_usec - oldtime.tv_usec) / 1000000.0;
270 oldtime.tv_sec = t.tv_sec;
271 oldtime.tv_usec = t.tv_usec;
272
273 New_save_hist = alloca(sizeof(struct save_hist)*ntop);
274 /*
275 * Make a pass through the data to get stats.
276 */
277 for(n = 0; n < ntop; n++) {
278 cur = top + n;
279
280 /*
281 * Calculate time in cur process. Time is sum of user time
282 * (usrtime) plus system time (systime).
283 */
284 systime = cur->stime;
285 usrtime = cur->utime;
286 pid = cur->pid;
287 total_time = systime + usrtime;
288 New_save_hist[n].ticks = total_time;
289 New_save_hist[n].pid = pid;
290 New_save_hist[n].stime = systime;
291 New_save_hist[n].utime = usrtime;
292
293 /* find matching entry from previous pass */
294 for (i = 0; i < prev_count; i++) {
295 if (save_history[i].pid == pid) {
296 total_time -= save_history[i].ticks;
297 systime -= save_history[i].stime;
298 usrtime -= save_history[i].utime;
299 break;
300 }
301 }
302
303 /*
304 * Calculate percent cpu time for cur task.
305 */
306 i = (total_time * 10 * 100/Hertz) / elapsed_time;
307 if (i > 999)
308 i = 999;
309 cur->pcpu = i;
310
311 }
312
313 /*
314 * Save cur frame's information.
315 */
316 free(save_history);
317 save_history = memcpy(xmalloc(sizeof(struct save_hist)*n), New_save_hist,
318 sizeof(struct save_hist)*n);
319 prev_count = n;
320 qsort(top, n, sizeof(status_t), (void*)mult_lvl_cmp);
321}
322#else
323static cmp_t sort_function;
324#endif /* FEATURE_CPU_USAGE_PERCENTAGE */
325
56/* display generic info (meminfo / loadavg) */ 326/* display generic info (meminfo / loadavg) */
57static void display_generic(void) 327static unsigned long display_generic(void)
58{ 328{
59 FILE *fp; 329 FILE *fp;
60 char buf[80]; 330 char buf[80];
@@ -62,303 +332,245 @@ static void display_generic(void)
62 unsigned long total, used, mfree, shared, buffers, cached; 332 unsigned long total, used, mfree, shared, buffers, cached;
63 333
64 /* read memory info */ 334 /* read memory info */
65 fp = fopen("meminfo", "r"); 335 fp = xfopen("meminfo", "r");
66 if (!fp) {
67 perror("fopen('meminfo')");
68 return;
69 }
70 fgets(buf, sizeof(buf), fp); /* skip first line */ 336 fgets(buf, sizeof(buf), fp); /* skip first line */
71 337
72 if (fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu", 338 if (fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu",
73 &total, &used, &mfree, &shared, &buffers, &cached) != 6) { 339 &total, &used, &mfree, &shared, &buffers, &cached) != 6) {
74 fprintf(stderr, "Error: failed to read 'meminfo'"); 340 error_msg_and_die("failed to read '%s'", "meminfo");
75 fclose(fp);
76 } 341 }
77 fclose(fp); 342 fclose(fp);
78 343
79 /* read load average */ 344 /* read load average */
80 fp = fopen("loadavg", "r"); 345 fp = xfopen("loadavg", "r");
81 if (!fp) {
82 perror("fopen('loadavg')");
83 return;
84 }
85 if (fscanf(fp, "%f %f %f", &avg1, &avg2, &avg3) != 3) { 346 if (fscanf(fp, "%f %f %f", &avg1, &avg2, &avg3) != 3) {
86 fprintf(stderr, "Error: failed to read 'loadavg'"); 347 error_msg_and_die("failed to read '%s'", "loadavg");
87 fclose(fp);
88 return;
89 } 348 }
90 fclose(fp); 349 fclose(fp);
91 350
92 /* convert to kilobytes */ 351 /* convert to kilobytes */
93 if (total) total /= 1024; 352 used /= 1024;
94 if (used) used /= 1024; 353 mfree /= 1024;
95 if (mfree) mfree /= 1024; 354 shared /= 1024;
96 if (shared) shared /= 1024; 355 buffers /= 1024;
97 if (buffers) buffers /= 1024; 356 cached /= 1024;
98 if (cached) cached /= 1024;
99 357
100 /* output memory info and load average */ 358 /* output memory info and load average */
101 printf("Mem: %ldK, %ldK used, %ldK free, %ldK shrd, %ldK buff, %ldK cached\n", 359 /* clear screen & go to top */
102 total, used, mfree, shared, buffers, cached); 360 printf("\e[H\e[J" "Mem: "
103 printf("Load average: %.2f, %.2f, %.2f (State: S=sleeping R=running, W=waiting)\n", 361 "%ldK used, %ldK free, %ldK shrd, %ldK buff, %ldK cached\n",
362 used, mfree, shared, buffers, cached);
363 printf("Load average: %.2f, %.2f, %.2f "
364 "(State: S=sleeping R=running, W=waiting)\n",
104 avg1, avg2, avg3); 365 avg1, avg2, avg3);
366 return total / 1024;
105} 367}
106 368
107 369
108/* display process statuses */ 370/* display process statuses */
109static void display_status(int count, const status_t *s) 371static void display_status(int count, int col)
110{ 372{
111 const char *fmt, *cmd; 373 status_t *s = top;
112 374 char rss_str_buf[8];
113 /* clear screen & go to top */ 375 unsigned long total_memory = display_generic();
114 printf("\e[2J\e[1;1H");
115
116 display_generic();
117 376
377#ifdef FEATURE_CPU_USAGE_PERCENTAGE
118 /* what info of the processes is shown */ 378 /* what info of the processes is shown */
119 printf("\n%*s %*s %*s %*s %*s %*s %-*s\n", 379 printf("\n\e[7m PID USER STATUS RSS PPID %%CPU %%MEM COMMAND\e[0m\n");
120 sizeof(s->pid)-1, "Pid:", 380#else
121 sizeof(s->state)-1, "", 381 printf("\n\e[7m PID USER STATUS RSS PPID %%MEM COMMAND\e[0m\n");
122 sizeof(s->ppid)-1, "PPid:", 382#endif
123 sizeof(s->uid)-1, "UID:",
124 sizeof(s->size)-1, "WmSize:",
125 sizeof(s->rss)-1, "WmRSS:",
126 sizeof(s->cmd)-1, "command line:");
127 383
128 while (count--) { 384 while (count--) {
129 if (s->cmd[0]) { 385 char *namecmd = s->cmd;
130 /* normal process, has command line */ 386 int pmem;
131 cmd = s->cmd;
132 fmt = "%*s %*s %*s %*s %*s %*s %s\n";
133 } else {
134 /* no command line, show only process name */
135 cmd = s->name;
136 fmt = "%*s %*s %*s %*s %*s %*s [%s]\n";
137 }
138 printf(fmt,
139 sizeof(s->pid)-1, s->pid,
140 sizeof(s->state)-1, s->state,
141 sizeof(s->ppid)-1, s->ppid,
142 sizeof(s->uid)-1, s->uid,
143 sizeof(s->size)-1, s->size,
144 sizeof(s->rss)-1, s->rss,
145 cmd);
146 s++;
147 }
148}
149 387
150 388 pmem = 1000.0 * s->rss / total_memory;
151/* checks if given 'buf' for process starts with 'id' + ':' + TAB 389 if (pmem > 999) pmem = 999;
152 * and stores rest of the buf to 'store' with max size 'size'
153 */
154static int process_status(const char *buf, const char *id, char *store, size_t size)
155{
156 int len, i;
157 390
158 /* check status field name */ 391 if(s->rss > 10*1024)
159 len = strlen(id); 392 sprintf(rss_str_buf, "%6ldM", s->rss/1024);
160 if (strncmp(buf, id, len) != 0) {
161 if(store)
162 error_msg_and_die("ERROR status: line doesn't start with '%s' in:\n%s\n", id, buf);
163 else 393 else
164 return 0; 394 sprintf(rss_str_buf, "%7ld", s->rss);
165 } 395#ifdef FEATURE_CPU_USAGE_PERCENTAGE
166 if (!store) { 396 printf("%5d %-8s %s %s %5d %2d.%d %2u.%u ",
167 /* ignoring this field */ 397#else
168 return 1; 398 printf("%5d %-8s %s %s %5d %2u.%u ",
169 } 399#endif
170 buf += len; 400 s->pid, s->user, s->state, rss_str_buf, s->ppid,
171 401#ifdef FEATURE_CPU_USAGE_PERCENTAGE
172 /* check status field format */ 402 s->pcpu/10, s->pcpu%10,
173 if ((*buf++ != ':') || (*buf++ != '\t')) { 403#endif
174 error_msg_and_die("ERROR status: field '%s' not followed with ':' + TAB in:\n%s\n", id, buf); 404 pmem/10, pmem%10);
175 } 405 if(namecmd != 0 && namecmd[0] != 0) {
176 406 if(strlen(namecmd) > col)
177 /* skip whitespace in Wm* fields */ 407 namecmd[col] = 0;
178 if (id[0] == 'V' && id[1] == 'm') { 408 printf("%s\n", namecmd);
179 i = 3;
180 while (i--) {
181 if (*buf == ' ') {
182 buf++;
183 } else { 409 } else {
184 error_msg_and_die("ERROR status: can't skip whitespace for " 410 namecmd = s->short_cmd;
185 "'%s' field in:\n%s\n", id, buf); 411 if(strlen(namecmd) > (col-2))
412 namecmd[col-2] = 0;
413 printf("[%s]\n", namecmd);
186 } 414 }
415 s++;
187 } 416 }
188 }
189
190 /* copy at max (size-1) chars and force '\0' to the end */
191 while (--size) {
192 if (*buf < ' ') {
193 break;
194 }
195 *store++ = *buf++;
196 }
197 *store = '\0';
198 return 1;
199} 417}
200 418
201/* read process statuses */ 419/* returns true for file names which are PID dirs
202static void read_status(int num, status_t *s) 420 * (i.e. start with number)
421 */
422static int filter_pids(const struct dirent *dir)
203{ 423{
424 char *name = dir->d_name;
425 int n;
204 char status[20]; 426 char status[20];
205 char buf[80]; 427 char buf[1024];
206 FILE *fp; 428 FILE *fp;
429 status_t curstatus;
430 int pid;
431 long tasknice;
432 struct stat sb;
433
434 if (!(*name >= '0' && *name <= '9'))
435 return 0;
436 if(stat(name, &sb))
437 return 0;
438
439 memset(&curstatus, 0, sizeof(status_t));
440 pid = atoi(name);
441 curstatus.pid = pid;
207 442
208 while (num--) { 443 my_getpwuid(curstatus.user, sb.st_uid);
209 sprintf(status, "%s/status", s->pid); 444
210 445 sprintf(status, "%d/stat", pid);
211 /* read the command line from 'cmdline' in PID dir */ 446 if((fp = fopen(status, "r")) == NULL)
212 fp = fopen(status, "r"); 447 return 0;
213 if (!fp) { 448 name = fgets(buf, sizeof(buf), fp);
214 strncpy(s->pid, "EXIT", sizeof(s->pid));
215 s->pid[sizeof(s->pid)-1] = '\0';
216 fclose(fp); 449 fclose(fp);
217 continue; 450 if(name == NULL)
218 } 451 return 0;
452 name = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
453 if(name == 0 || name[1] != ' ')
454 return 0;
455 *name = 0;
456 sscanf(buf, "%*s (%15c", curstatus.short_cmd);
457 n = sscanf(name+2,
458 "%c %d "
459 "%*s %*s %*s %*s " /* pgrp, session, tty, tpgid */
460 "%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */
461#ifdef FEATURE_CPU_USAGE_PERCENTAGE
462 "%lu %lu "
463#else
464 "%*s %*s "
465#endif
466 "%*s %*s %*s " /* cutime, cstime, priority */
467 "%ld "
468 "%*s %*s %*s " /* timeout, it_real_value, start_time */
469 "%*s " /* vsize */
470 "%ld",
471 curstatus.state, &curstatus.ppid,
472#ifdef FEATURE_CPU_USAGE_PERCENTAGE
473 &curstatus.utime, &curstatus.stime,
474#endif
475 &tasknice,
476 &curstatus.rss);
477#ifdef FEATURE_CPU_USAGE_PERCENTAGE
478 if(n != 6)
479#else
480 if(n != 4)
481#endif
482 return 0;
483
484 if (curstatus.rss == 0 && curstatus.state[0] != 'Z')
485 curstatus.state[1] = 'W';
486 else
487 curstatus.state[1] = ' ';
488 if (tasknice < 0)
489 curstatus.state[2] = '<';
490 else if (tasknice > 0)
491 curstatus.state[2] = 'N';
492 else
493 curstatus.state[2] = ' ';
494
495 curstatus.rss <<= (PAGE_SHIFT - 10); /* 2**10 = 1kb */
219 496
220 /* get and process the information */ 497 sprintf(status, "%d/cmdline", pid);
221 fgets(buf, sizeof(buf), fp); 498 if((fp = fopen(status, "r")) == NULL)
222 process_status(buf, "Name", s->name, sizeof(s->name)); 499 return 0;
223 fgets(buf, sizeof(buf), fp); 500 if(fgets(buf, sizeof(buf), fp) != NULL) {
224 process_status(buf, "State", s->state, sizeof(s->state)); 501 name = strchr(buf, '\n');
225 fgets(buf, sizeof(buf), fp); 502 if(name != NULL)
226 if(process_status(buf, "Tgid", NULL, 0)) 503 *name = 0;
227 fgets(buf, sizeof(buf), fp); 504 if(buf[0])
228 process_status(buf, "Pid", NULL, 0); 505 curstatus.cmd = strdup(buf); /* if NULL it work true also */
229 fgets(buf, sizeof(buf), fp);
230 process_status(buf, "PPid", s->ppid, sizeof(s->ppid));
231 fgets(buf, sizeof(buf), fp);
232 if(process_status(buf, "TracerPid", NULL, 0))
233 fgets(buf, sizeof(buf), fp);
234 process_status(buf, "Uid", s->uid, sizeof(s->uid));
235 fgets(buf, sizeof(buf), fp);
236 process_status(buf, "Gid", NULL, 0);
237 fgets(buf, sizeof(buf), fp);
238 if(process_status(buf, "FDSize", NULL, 0))
239 fgets(buf, sizeof(buf), fp);
240 process_status(buf, "Groups", NULL, 0);
241 fgets(buf, sizeof(buf), fp);
242 /* only user space processes have command line
243 * and memory statistics
244 */
245 if (s->cmd[0]) {
246 process_status(buf, "VmSize", s->size, sizeof(s->size));
247 fgets(buf, sizeof(buf), fp);
248 process_status(buf, "VmLck", s->lck, sizeof(s->lck));
249 fgets(buf, sizeof(buf), fp);
250 process_status(buf, "VmRSS", s->rss, sizeof(s->rss));
251 fgets(buf, sizeof(buf), fp);
252 process_status(buf, "VmData", s->data, sizeof(s->data));
253 fgets(buf, sizeof(buf), fp);
254 process_status(buf, "VmStk", s->stk, sizeof(s->stk));
255 fgets(buf, sizeof(buf), fp);
256 process_status(buf, "VmExe", s->exe, sizeof(s->exe));
257 fgets(buf, sizeof(buf), fp);
258 process_status(buf, "VmLib", s->lib, sizeof(s->lib));
259 } 506 }
260 fclose(fp); 507 fclose(fp);
261 508
262 /* next process */ 509 n = ntop;
263 s++; 510 top = xrealloc(top, (++ntop)*sizeof(status_t));
264 } 511 memcpy(top + n, &curstatus, sizeof(status_t));
512 return 1;
265} 513}
266 514
267 515
268/* allocs statuslist and reads process command lines, frees namelist, 516static struct dirent **namelist;
269 * returns filled statuslist or NULL in case of error.
270 */
271static status_t *read_info(int num, struct dirent **namelist)
272{
273 status_t *statuslist, *s;
274 char cmdline[20];
275 FILE *fp;
276 int idx;
277 517
278 /* allocate & zero status for each of the processes */ 518static void clearmems(void) {
279 statuslist = calloc(num, sizeof(status_t)); 519 int i;
280 if (!statuslist) { 520
281 return NULL; 521 for(i = 0; i < ntop; i++) {
282 } 522 free(top[i].cmd);
283 523 free(namelist[i]);
284 /* go through the processes */
285 for (idx = 0; idx < num; idx++) {
286
287 /* copy PID string to status struct and free name */
288 s = &(statuslist[idx]);
289 if (strlen(namelist[idx]->d_name) > sizeof(s->pid)-1) {
290 fprintf(stderr, "PID '%s' too long\n", namelist[idx]->d_name);
291 return NULL;
292 }
293 strncpy(s->pid, namelist[idx]->d_name, sizeof(s->pid));
294 s->pid[sizeof(s->pid)-1] = '\0';
295 free(namelist[idx]);
296
297 /* read the command line from 'cmdline' in PID dir */
298 sprintf(cmdline, "%s/cmdline", s->pid);
299 fp = fopen(cmdline, "r");
300 if (!fp) {
301 fclose(fp);
302 perror("fopen('cmdline')");
303 return NULL;
304 }
305 fgets(statuslist[idx].cmd, sizeof(statuslist[idx].cmd), fp);
306 fclose(fp);
307 } 524 }
525 free(top);
308 free(namelist); 526 free(namelist);
309 return statuslist; 527 top = 0;
528 namelist = 0;
529 ntop = 0;
310} 530}
311 531
532#if defined CONFIG_FEATURE_USE_TERMIOS
533#include <termios.h>
534#include <sys/time.h>
535#include <signal.h>
312 536
313/* returns true for file names which are PID dirs
314 * (i.e. start with number)
315 */
316static int filter_pids(const struct dirent *dir)
317{
318 status_t dummy;
319 char *name = dir->d_name;
320
321 if (*name >= '0' && *name <= '9') {
322 if (strlen(name) > sizeof(dummy.pid)-1) {
323 fprintf(stderr, "PID name '%s' too long\n", name);
324 return 0;
325 }
326 return 1;
327 }
328 return 0;
329}
330 537
538static struct termios initial_settings;
331 539
332/* compares two directory entry names as numeric strings 540static void reset_term(void)
333 */
334static int num_sort(const void *a, const void *b)
335{ 541{
336 int ia = atoi((*(struct dirent **)a)->d_name); 542 tcsetattr(0, TCSANOW, (void *) &initial_settings);
337 int ib = atoi((*(struct dirent **)b)->d_name); 543#ifdef CONFIG_FEATURE_CLEAN_UP
544 clearmems();
545#ifdef FEATURE_CPU_USAGE_PERCENTAGE
546 free(save_history);
547#endif
548#endif /* CONFIG_FEATURE_CLEAN_UP */
549}
338 550
339 if (ia == ib) { 551static void sig_catcher (int sig)
340 return 0; 552{
341 } 553 reset_term();
342 /* NOTE: by switching the check, you change the process sort order */
343 if (ia < ib) {
344 return -1;
345 } else {
346 return 1;
347 }
348} 554}
555#endif /* CONFIG_FEATURE_USE_TERMIOS */
556
349 557
350int top_main(int argc, char **argv) 558int top_main(int argc, char **argv)
351{ 559{
352 status_t *statuslist; 560 int opt, interval, lines, col;
353 struct dirent **namelist; 561#if defined CONFIG_FEATURE_USE_TERMIOS
354 int opt, num, interval, lines; 562 struct termios new_settings;
355#if defined CONFIG_FEATURE_AUTOWIDTH && defined CONFIG_FEATURE_USE_TERMIOS 563 struct timeval tv;
564 fd_set readfds;
565 unsigned char c;
566 struct sigaction sa;
567#if defined CONFIG_FEATURE_AUTOWIDTH
356 struct winsize win = { 0, 0, 0, 0 }; 568 struct winsize win = { 0, 0, 0, 0 };
357#endif 569#endif
570#endif /* CONFIG_FEATURE_USE_TERMIOS */
571
358 /* Default update rate is 5 seconds */ 572 /* Default update rate is 5 seconds */
359 interval = 5; 573 interval = 5;
360 /* Default to 25 lines - 5 lines for status */
361 lines = 25 - 5;
362 574
363 /* do normal option parsing */ 575 /* do normal option parsing */
364 while ((opt = getopt(argc, argv, "d:")) > 0) { 576 while ((opt = getopt(argc, argv, "d:")) > 0) {
@@ -371,42 +583,121 @@ int top_main(int argc, char **argv)
371 } 583 }
372 } 584 }
373 585
374#if defined CONFIG_FEATURE_AUTOWIDTH && defined CONFIG_FEATURE_USE_TERMIOS 586 /* Default to 25 lines - 5 lines for status */
375 ioctl(fileno(stdout), TIOCGWINSZ, &win); 587 lines = 25 - 5;
376 if (win.ws_row > 4) 588 /* Default CMD format size */
377 lines = win.ws_row - 5; 589#ifdef FEATURE_CPU_USAGE_PERCENTAGE
590 col = 35 - 6;
591#else
592 col = 35;
378#endif 593#endif
379
380 /* change to proc */ 594 /* change to proc */
381 if (chdir("/proc") < 0) { 595 if (chdir("/proc") < 0) {
382 perror_msg_and_die("chdir('/proc')"); 596 perror_msg_and_die("chdir('/proc')");
383 } 597 }
384 598#if defined CONFIG_FEATURE_USE_TERMIOS
385 /* read process IDs for all the processes from the procfs */ 599 tcgetattr(0, (void *) &initial_settings);
386 num = scandir(".", &namelist, filter_pids, num_sort); 600 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
387 if (num < 0) { 601 new_settings.c_lflag &= ~(ISIG | ICANON); /* unbuffered input */
602 /* Turn off echoing */
603 new_settings.c_lflag &= ~(ECHO | ECHONL);
604
605 signal (SIGTERM, sig_catcher);
606 sigaction (SIGTERM, (struct sigaction *) 0, &sa);
607 sa.sa_flags |= SA_RESTART;
608 sa.sa_flags &= ~SA_INTERRUPT;
609 sigaction (SIGTERM, &sa, (struct sigaction *) 0);
610 sigaction (SIGINT, &sa, (struct sigaction *) 0);
611 tcsetattr(0, TCSANOW, (void *) &new_settings);
612 atexit(reset_term);
613#if defined CONFIG_FEATURE_AUTOWIDTH
614 ioctl(0, TIOCGWINSZ, &win);
615 if (win.ws_row > 4) {
616 lines = win.ws_row - 5;
617#ifdef FEATURE_CPU_USAGE_PERCENTAGE
618 col = win.ws_col - 80 + 35 - 6;
619#else
620 col = win.ws_col - 80 + 35;
621#endif
622 }
623#endif
624#endif /* CONFIG_FEATURE_USE_TERMIOS */
625#ifdef FEATURE_CPU_USAGE_PERCENTAGE
626 sort_function[0] = pcpu_sort;
627 sort_function[1] = mem_sort;
628 sort_function[2] = time_sort;
629#else
630 sort_function = mem_sort;
631#endif
632 while (1) {
633 /* read process IDs & status for all the processes */
634 if (scandir(".", &namelist, filter_pids, 0) < 0) {
388 perror_msg_and_die("scandir('/proc')"); 635 perror_msg_and_die("scandir('/proc')");
389 } 636 }
390 if (lines > num) { 637#ifdef FEATURE_CPU_USAGE_PERCENTAGE
391 lines = num; 638 if(!Hertz) {
639 init_Hertz_value();
640 do_stats();
641 sleep(1);
642 clearmems();
643 continue;
392 } 644 }
393 645 do_stats();
394 /* read command line for each of the processes */ 646#else
395 statuslist = read_info(num, namelist); 647 qsort(top, ntop, sizeof(status_t), (void*)sort_function);
396 if (!statuslist) { 648#endif
649 opt = lines;
650 if (opt > ntop) {
651 opt = ntop;
652 }
653 /* show status for each of the processes */
654 display_status(opt, col);
655#if defined CONFIG_FEATURE_USE_TERMIOS
656 tv.tv_sec = interval;
657 tv.tv_usec = 0;
658 FD_ZERO (&readfds);
659 FD_SET (0, &readfds);
660 select (1, &readfds, NULL, NULL, &tv);
661 if (FD_ISSET (0, &readfds)) {
662 if (read (0, &c, 1) <= 0) { /* signal */
397 return EXIT_FAILURE; 663 return EXIT_FAILURE;
398 } 664 }
399 665 if(c == 'q' || c == initial_settings.c_cc[VINTR])
400 while (1) { 666 return EXIT_SUCCESS;
401 /* read status for each of the processes */ 667 if(c == 'M') {
402 read_status(num, statuslist); 668#ifdef FEATURE_CPU_USAGE_PERCENTAGE
403 669 sort_function[0] = mem_sort;
404 /* display status */ 670 sort_function[1] = pcpu_sort;
405 display_status(lines, statuslist); 671 sort_function[2] = time_sort;
406 672#else
673 sort_function = mem_sort;
674#endif
675 }
676#ifdef FEATURE_CPU_USAGE_PERCENTAGE
677 if(c == 'P') {
678 sort_function[0] = pcpu_sort;
679 sort_function[1] = mem_sort;
680 sort_function[2] = time_sort;
681 }
682 if(c == 'T') {
683 sort_function[0] = time_sort;
684 sort_function[1] = mem_sort;
685 sort_function[2] = pcpu_sort;
686 }
687#endif
688 if(c == 'N') {
689#ifdef FEATURE_CPU_USAGE_PERCENTAGE
690 sort_function[0] = pid_sort;
691#else
692 sort_function = pid_sort;
693#endif
694 }
695 }
696#else
407 sleep(interval); 697 sleep(interval);
698#endif /* CONFIG_FEATURE_USE_TERMIOS */
699 clearmems();
408 } 700 }
409 701
410 free(statuslist);
411 return EXIT_SUCCESS; 702 return EXIT_SUCCESS;
412} 703}