smemcap: keep outputting by ignoring transient processes
[oweals/busybox.git] / procps / smemcap.c
1 /*
2  smemcap - a tool for meaningful memory reporting
3
4  Copyright 2008-2009 Matt Mackall <mpm@selenic.com>
5
6  This software may be used and distributed according to the terms of
7  the GNU General Public License version 2 or later, incorporated
8  herein by reference.
9 */
10 //config:config SMEMCAP
11 //config:       bool "smemcap (2.5 kb)"
12 //config:       default y
13 //config:       help
14 //config:       smemcap is a tool for capturing process data for smem,
15 //config:       a memory usage statistic tool.
16
17 //applet:IF_SMEMCAP(APPLET(smemcap, BB_DIR_USR_BIN, BB_SUID_DROP))
18
19 //kbuild:lib-$(CONFIG_SMEMCAP) += smemcap.o
20
21 #include "libbb.h"
22 #include "bb_archive.h"
23
24 struct fileblock {
25         struct fileblock *next;
26         char data[TAR_BLOCK_SIZE];
27 };
28
29 static void writeheader(const char *path, struct stat *sb, int type)
30 {
31         struct tar_header_t header;
32         int i, sum;
33
34         memset(&header, 0, TAR_BLOCK_SIZE);
35         strcpy(header.name, path);
36         sprintf(header.mode, "%o", sb->st_mode & 0777);
37         /* careful to not overflow fields! */
38         sprintf(header.uid, "%o", sb->st_uid & 07777777);
39         sprintf(header.gid, "%o", sb->st_gid & 07777777);
40         sprintf(header.size, "%o", (unsigned)sb->st_size);
41         sprintf(header.mtime, "%llo", sb->st_mtime & 077777777777LL);
42         header.typeflag = type;
43         strcpy(header.magic, "ustar  "); /* like GNU tar */
44
45         /* Calculate and store the checksum (the sum of all of the bytes of
46          * the header). The checksum field must be filled with blanks for the
47          * calculation. The checksum field is formatted differently from the
48          * other fields: it has 6 digits, a NUL, then a space -- rather than
49          * digits, followed by a NUL like the other fields... */
50         header.chksum[7] = ' ';
51         sum = ' ' * 7;
52         for (i = 0; i < TAR_BLOCK_SIZE; i++)
53                 sum += ((unsigned char*)&header)[i];
54         sprintf(header.chksum, "%06o", sum);
55
56         xwrite(STDOUT_FILENO, &header, TAR_BLOCK_SIZE);
57 }
58
59 static void archivefile(const char *path)
60 {
61         struct fileblock *start, *cur;
62         struct fileblock **prev = &start;
63         int fd, r;
64         unsigned size = 0;
65         struct stat s;
66
67         /* buffer the file */
68         fd = xopen(path, O_RDONLY);
69         if (fd == -1) {
70                 /* skip vanished processes between dir listing and traversal */
71                 return;
72         }
73         do {
74                 cur = xzalloc(sizeof(*cur));
75                 *prev = cur;
76                 prev = &cur->next;
77                 r = full_read(fd, cur->data, TAR_BLOCK_SIZE);
78                 if (r > 0)
79                         size += r;
80         } while (r == TAR_BLOCK_SIZE);
81
82         /* write archive header */
83         fstat(fd, &s);
84         close(fd);
85         s.st_size = size;
86         writeheader(path, &s, '0');
87
88         /* dump file contents */
89         for (cur = start; (int)size > 0; size -= TAR_BLOCK_SIZE) {
90                 xwrite(STDOUT_FILENO, cur->data, TAR_BLOCK_SIZE);
91                 start = cur;
92                 cur = cur->next;
93                 free(start);
94         }
95 }
96
97 static void archivejoin(const char *sub, const char *name)
98 {
99         char path[sizeof(long long)*3 + sizeof("/cmdline")];
100         sprintf(path, "%s/%s", sub, name);
101         archivefile(path);
102 }
103
104 //usage:#define smemcap_trivial_usage ">SMEMDATA.TAR"
105 //usage:#define smemcap_full_usage "\n\n"
106 //usage:       "Collect memory usage data in /proc and write it to stdout"
107
108 int smemcap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
109 int smemcap_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
110 {
111         DIR *d;
112         struct dirent *de;
113
114         xchdir("/proc");
115         d = xopendir(".");
116
117         archivefile("meminfo");
118         archivefile("version");
119         while ((de = readdir(d)) != NULL) {
120                 if (isdigit(de->d_name[0])) {
121                         struct stat s;
122                         memset(&s, 0, sizeof(s));
123                         s.st_mode = 0555;
124                         writeheader(de->d_name, &s, '5');
125                         archivejoin(de->d_name, "smaps");
126                         archivejoin(de->d_name, "cmdline");
127                         archivejoin(de->d_name, "stat");
128                 }
129         }
130
131         if (ENABLE_FEATURE_CLEAN_UP)
132                 closedir(d);
133
134         return EXIT_SUCCESS;
135 }