Bump to procps-ng 3.3.17
[platform/upstream/procps-ng.git] / vmstat.c
1 /*
2  * old: "Copyright 1994 by Henry Ware <al172@yfn.ysu.edu>. Copyleft same year."
3  * most code copyright 2002 Albert Cahalan
4  *
5  * 27/05/2003 (Fabian Frederick) : Add unit conversion + interface
6  *                                 Export proc/stat access to libproc
7  *                                 Adapt vmstat helpfile
8  * 31/05/2003 (Fabian) : Add diskstat support (/libproc)
9  * June 2003 (Fabian)  : -S <x> -s & -s -S <x> patch
10  * June 2003 (Fabian)  : Adding diskstat against 3.1.9, slabinfo
11  *                       patching 'header' in disk & slab
12  * July 2003 (Fabian)  : Adding disk partition output
13  *                       Adding disk table
14  *                       Syncing help / usage
15  *
16  * This library is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU Lesser General Public
18  * License as published by the Free Software Foundation; either
19  * version 2.1 of the License, or (at your option) any later version.
20  *
21  * This library is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24  * Lesser General Public License for more details.
25  *
26  * You should have received a copy of the GNU Lesser General Public
27  * License along with this library; if not, write to the Free Software
28  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
29  */
30
31 #include <assert.h>
32 #include <ctype.h>
33 #include <dirent.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <getopt.h>
37 #include <limits.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/ioctl.h>
42 #include <sys/types.h>
43 #include <termios.h>
44 #include <unistd.h>
45 #include <time.h>
46
47 #include "c.h"
48 #include "fileutils.h"
49 #include "nls.h"
50 #include "strutils.h"
51 #include "proc/sysinfo.h"
52 #include "proc/version.h"
53
54 #define UNIT_B        1
55 #define UNIT_k        1000
56 #define UNIT_K        1024
57 #define UNIT_m        1000000
58 #define UNIT_M        1048576
59
60 static unsigned long dataUnit = UNIT_K;
61 static char szDataUnit[3] = "K";
62
63 #define VMSTAT        0
64 #define DISKSTAT      0x00000001
65 #define VMSUMSTAT     0x00000002
66 #define SLABSTAT      0x00000004
67 #define PARTITIONSTAT 0x00000008
68 #define DISKSUMSTAT   0x00000010
69
70 static int statMode = VMSTAT;
71
72 /* "-a" means "show active/inactive" */
73 static int a_option;
74
75 /* "-w" means "wide output" */
76 static int w_option;
77
78 /* "-t" means "show timestamp" */
79 static int t_option;
80
81 static unsigned sleep_time = 1;
82 static int infinite_updates = 0;
83 static unsigned long num_updates;
84 /* window height */
85 static unsigned int height;
86 static unsigned int moreheaders = TRUE;
87
88 static void __attribute__ ((__noreturn__))
89     usage(FILE * out)
90 {
91         fputs(USAGE_HEADER, out);
92         fprintf(out,
93               _(" %s [options] [delay [count]]\n"),
94                 program_invocation_short_name);
95         fputs(USAGE_OPTIONS, out);
96         fputs(_(" -a, --active           active/inactive memory\n"), out);
97         fputs(_(" -f, --forks            number of forks since boot\n"), out);
98         fputs(_(" -m, --slabs            slabinfo\n"), out);
99         fputs(_(" -n, --one-header       do not redisplay header\n"), out);
100         fputs(_(" -s, --stats            event counter statistics\n"), out);
101         fputs(_(" -d, --disk             disk statistics\n"), out);
102         fputs(_(" -D, --disk-sum         summarize disk statistics\n"), out);
103         fputs(_(" -p, --partition <dev>  partition specific statistics\n"), out);
104         fputs(_(" -S, --unit <char>      define display unit\n"), out);
105         fputs(_(" -w, --wide             wide output\n"), out);
106         fputs(_(" -t, --timestamp        show timestamp\n"), out);
107         fputs(USAGE_SEPARATOR, out);
108         fputs(USAGE_HELP, out);
109         fputs(USAGE_VERSION, out);
110         fprintf(out, USAGE_MAN_TAIL("vmstat(8)"));
111
112         exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
113 }
114
115 #if 0
116 /* produce: "  6  ", "123  ", "123k ", etc. */
117 static int format_1024(unsigned long long val64, char *restrict dst)
118 {
119         unsigned oldval;
120         const char suffix[] = " kmgtpe";
121         unsigned level = 0;
122         unsigned val32;
123
124         if (val64 < 1000) {
125                 /* special case to avoid "6.0  " when plain "  6  " would do */
126                 val32 = val64;
127                 return sprintf(dst, "%3u  ", val32);
128         }
129
130         while (val64 > 0xffffffffull) {
131                 level++;
132                 val64 /= 1024;
133         }
134
135         val32 = val64;
136
137         while (val32 > 999) {
138                 level++;
139                 oldval = val32;
140                 val32 /= 1024;
141         }
142
143         if (val32 < 10) {
144                 unsigned fract = (oldval % 1024) * 10 / 1024;
145                 return sprintf(dst, "%u.%u%c ", val32, fract, suffix[level]);
146         }
147         return sprintf(dst, "%3u%c ", val32, suffix[level]);
148 }
149
150 /* produce: "  6  ", "123  ", "123k ", etc. */
151 static int format_1000(unsigned long long val64, char *restrict dst)
152 {
153         unsigned oldval;
154         const char suffix[] = " kmgtpe";
155         unsigned level = 0;
156         unsigned val32;
157
158         if (val64 < 1000) {
159                 /* special case to avoid "6.0  " when plain "  6  " would do */
160                 val32 = val64;
161                 return sprintf(dst, "%3u  ", val32);
162         }
163
164         while (val64 > 0xffffffffull) {
165                 level++;
166                 val64 /= 1000;
167         }
168
169         val32 = val64;
170
171         while (val32 > 999) {
172                 level++;
173                 oldval = val32;
174                 val32 /= 1000;
175         }
176
177         if (val32 < 10) {
178                 unsigned fract = (oldval % 1000) / 100;
179                 return sprintf(dst, "%u.%u%c ", val32, fract, suffix[level]);
180         }
181         return sprintf(dst, "%3u%c ", val32, suffix[level]);
182 }
183 #endif
184
185 static void new_header(void)
186 {
187         struct tm *tm_ptr;
188         time_t the_time;
189         char timebuf[32];
190
191         /* Translation Hint: Translating folloging header & fields
192          * that follow (marked with max x chars) might not work,
193          * unless manual page is translated as well.  */
194         const char *header =
195             _("procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----");
196         const char *wide_header =
197             _("--procs-- -----------------------memory---------------------- ---swap-- -----io---- -system-- --------cpu--------");
198         const char *timestamp_header = _(" -----timestamp-----");
199
200         const char format[] =
201             "%2s %2s %6s %6s %6s %6s %4s %4s %5s %5s %4s %4s %2s %2s %2s %2s %2s";
202         const char wide_format[] =
203             "%4s %4s %12s %12s %12s %12s %4s %4s %5s %5s %4s %4s %3s %3s %3s %3s %3s";
204
205
206         printf("%s", w_option ? wide_header : header);
207
208         if (t_option) {
209                 printf("%s", timestamp_header);
210         }
211
212         printf("\n");
213
214         printf(
215             w_option ? wide_format : format,
216             /* Translation Hint: max 2 chars */
217              _("r"),
218             /* Translation Hint: max 2 chars */
219              _("b"),
220             /* Translation Hint: max 6 chars */
221              _("swpd"),
222             /* Translation Hint: max 6 chars */
223              _("free"),
224             /* Translation Hint: max 6 chars */
225              a_option ? _("inact") :
226             /* Translation Hint: max 6 chars */
227                         _("buff"),
228             /* Translation Hint: max 6 chars */
229              a_option ? _("active") :
230             /* Translation Hint: max 6 chars */
231                         _("cache"),
232             /* Translation Hint: max 4 chars */
233              _("si"),
234             /* Translation Hint: max 4 chars */
235              _("so"),
236             /* Translation Hint: max 5 chars */
237              _("bi"),
238             /* Translation Hint: max 5 chars */
239              _("bo"),
240             /* Translation Hint: max 4 chars */
241              _("in"),
242             /* Translation Hint: max 4 chars */
243              _("cs"),
244             /* Translation Hint: max 2 chars */
245              _("us"),
246             /* Translation Hint: max 2 chars */
247              _("sy"),
248             /* Translation Hint: max 2 chars */
249              _("id"),
250             /* Translation Hint: max 2 chars */
251              _("wa"),
252             /* Translation Hint: max 2 chars */
253              _("st"));
254
255         if (t_option) {
256                 (void) time( &the_time );
257                 tm_ptr = localtime( &the_time );
258                 if (tm_ptr && strftime(timebuf, sizeof(timebuf), "%Z", tm_ptr)) {
259                         const size_t len = strlen(timestamp_header);
260                         if (len >= 1 && len - 1 < sizeof(timebuf)) {
261                                 timebuf[len - 1] = '\0';
262                         }
263                 } else {
264                         timebuf[0] = '\0';
265                 }
266                 printf(" %*s", (int)(strlen(timestamp_header) - 1), timebuf);
267         }
268
269         printf("\n");
270 }
271
272 static unsigned long unitConvert(unsigned long size)
273 {
274         float cvSize;
275         cvSize = (float)size / dataUnit * ((statMode == SLABSTAT) ? 1 : 1024);
276         return ((unsigned long)cvSize);
277 }
278
279 static void new_format(void)
280 {
281         const char format[] =
282             "%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u %2u";
283         const char wide_format[] =
284             "%4u %4u %12lu %12lu %12lu %12lu %4u %4u %5u %5u %4u %4u %3u %3u %3u %3u %3u";
285
286         unsigned int tog = 0;   /* toggle switch for cleaner code */
287         unsigned int i;
288         unsigned int hz = Hertz;
289         unsigned int running, blocked, dummy_1, dummy_2;
290         jiff cpu_use[2], cpu_nic[2], cpu_sys[2], cpu_idl[2], cpu_iow[2],
291             cpu_xxx[2], cpu_yyy[2], cpu_zzz[2];
292         jiff duse, dsys, didl, diow, dstl, Div, divo2;
293         unsigned long pgpgin[2], pgpgout[2], pswpin[2], pswpout[2];
294         unsigned int intr[2], ctxt[2];
295         unsigned int sleep_half;
296         unsigned long kb_per_page = sysconf(_SC_PAGESIZE) / 1024ul;
297         int debt = 0;           /* handle idle ticks running backwards */
298         struct tm *tm_ptr;
299         time_t the_time;
300         char timebuf[32];
301
302         sleep_half = (sleep_time / 2);
303         new_header();
304         meminfo();
305
306         getstat(cpu_use, cpu_nic, cpu_sys, cpu_idl, cpu_iow, cpu_xxx, cpu_yyy,
307                 cpu_zzz, pgpgin, pgpgout, pswpin, pswpout, intr, ctxt, &running,
308                 &blocked, &dummy_1, &dummy_2);
309
310         if (t_option) {
311                 (void) time( &the_time );
312                 tm_ptr = localtime( &the_time );
313                 if (tm_ptr && strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm_ptr)) {
314                         ;
315                 } else {
316                         timebuf[0] = '\0';
317                 }
318         }
319
320         duse = *cpu_use + *cpu_nic;
321         dsys = *cpu_sys + *cpu_xxx + *cpu_yyy;
322         didl = *cpu_idl;
323         diow = *cpu_iow;
324         dstl = *cpu_zzz;
325         Div = duse + dsys + didl + diow + dstl;
326         if (!Div) Div = 1, didl = 1;
327         divo2 = Div / 2UL;
328         printf(w_option ? wide_format : format,
329                running, blocked,
330                unitConvert(kb_swap_used), unitConvert(kb_main_free),
331                unitConvert(a_option?kb_inactive:kb_main_buffers),
332                unitConvert(a_option?kb_active:kb_main_cached),
333                (unsigned)( (unitConvert(*pswpin  * kb_per_page) * hz + divo2) / Div ),
334                (unsigned)( (unitConvert(*pswpout * kb_per_page) * hz + divo2) / Div ),
335                (unsigned)( (*pgpgin                * hz + divo2) / Div ),
336                (unsigned)( (*pgpgout               * hz + divo2) / Div ),
337                (unsigned)( (*intr                  * hz + divo2) / Div ),
338                (unsigned)( (*ctxt                  * hz + divo2) / Div ),
339                (unsigned)( (100*duse                    + divo2) / Div ),
340                (unsigned)( (100*dsys                    + divo2) / Div ),
341                (unsigned)( (100*didl                    + divo2) / Div ),
342                (unsigned)( (100*diow                    + divo2) / Div ),
343                (unsigned)( (100*dstl                    + divo2) / Div )
344         );
345
346         if (t_option) {
347                 printf(" %s", timebuf);
348         }
349
350         printf("\n");
351
352         /* main loop */
353         for (i = 1; infinite_updates || i < num_updates; i++) {
354                 sleep(sleep_time);
355                 if (moreheaders && ((i % height) == 0))
356                         new_header();
357                 tog = !tog;
358
359                 meminfo();
360
361                 getstat(cpu_use + tog, cpu_nic + tog, cpu_sys + tog,
362                         cpu_idl + tog, cpu_iow + tog, cpu_xxx + tog,
363                         cpu_yyy + tog, cpu_zzz + tog, pgpgin + tog,
364                         pgpgout + tog, pswpin + tog, pswpout + tog, intr + tog,
365                         ctxt + tog, &running, &blocked, &dummy_1, &dummy_2);
366
367                 if (t_option) {
368                         (void) time( &the_time );
369                         tm_ptr = localtime( &the_time );
370                         if (tm_ptr && strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm_ptr)) {
371                                 ;
372                         } else {
373                                 timebuf[0] = '\0';
374                         }
375                 }
376
377                 duse =
378                     cpu_use[tog] - cpu_use[!tog] + cpu_nic[tog] - cpu_nic[!tog];
379                 dsys =
380                     cpu_sys[tog] - cpu_sys[!tog] + cpu_xxx[tog] -
381                     cpu_xxx[!tog] + cpu_yyy[tog] - cpu_yyy[!tog];
382                 didl = cpu_idl[tog] - cpu_idl[!tog];
383                 diow = cpu_iow[tog] - cpu_iow[!tog];
384                 dstl = cpu_zzz[tog] - cpu_zzz[!tog];
385
386                 /* idle can run backwards for a moment -- kernel "feature" */
387                 if (debt) {
388                         didl = (int)didl + debt;
389                         debt = 0;
390                 }
391                 if ((int)didl < 0) {
392                         debt = (int)didl;
393                         didl = 0;
394                 }
395
396                 Div = duse + dsys + didl + diow + dstl;
397                 if (!Div) Div = 1, didl = 1;
398                 divo2 = Div / 2UL;
399                 printf(w_option ? wide_format : format,
400                        running,
401                        blocked,
402                        unitConvert(kb_swap_used),unitConvert(kb_main_free),
403                        unitConvert(a_option?kb_inactive:kb_main_buffers),
404                        unitConvert(a_option?kb_active:kb_main_cached),
405                        /*si */
406                        (unsigned)( ( unitConvert((pswpin [tog] - pswpin [!tog])*kb_per_page)+sleep_half )/sleep_time ),
407                        /* so */
408                        (unsigned)( ( unitConvert((pswpout[tog] - pswpout[!tog])*kb_per_page)+sleep_half )/sleep_time ),
409                        /* bi */
410                        (unsigned)( (  pgpgin [tog] - pgpgin [!tog]             +sleep_half )/sleep_time ),
411                        /* bo */
412                        (unsigned)( (  pgpgout[tog] - pgpgout[!tog]             +sleep_half )/sleep_time ),
413                        /* in */
414                        (unsigned)( (  intr   [tog] - intr   [!tog]             +sleep_half )/sleep_time ),
415                        /* cs */
416                        (unsigned)( (  ctxt   [tog] - ctxt   [!tog]             +sleep_half )/sleep_time ),
417                        /* us */
418                        (unsigned)( (100*duse+divo2)/Div ),
419                        /* sy */
420                        (unsigned)( (100*dsys+divo2)/Div ),
421                        /* id */
422                        (unsigned)( (100*didl+divo2)/Div ),
423                        /* wa */
424                        (unsigned)( (100*diow+divo2)/Div ),
425                        /* st */
426                        (unsigned)( (100*dstl+divo2)/Div )
427                 );
428
429                 if (t_option) {
430                         printf(" %s", timebuf);
431                 }
432
433                 printf("\n");
434         }
435 }
436
437 static void diskpartition_header(const char *partition_name)
438 {
439         printf("%-10s %10s  %16s  %10s  %16s\n",
440                partition_name,
441        /* Translation Hint: Translating folloging disk partition
442         * header fields that follow (marked with max x chars) might
443         * not work, unless manual page is translated as well. */
444                /* Translation Hint: max 10 chars. */
445                _("reads"),
446                /* Translation Hint: max 16 chars. */
447                _("read sectors"),
448                /* Translation Hint: max 10 chars. */
449                _("writes"),
450                /* Translation Hint: max 16 chars */
451                _("requested writes"));
452 }
453
454 static int diskpartition_format(const char *partition_name)
455 {
456         FILE *fDiskstat;
457         struct disk_stat *disks;
458         struct partition_stat *partitions, *current_partition = NULL;
459         unsigned long ndisks, j, k, npartitions;
460         const char format[] = "%21u  %16llu  %10u  %16llu\n";
461
462         fDiskstat = fopen("/proc/diskstats", "rb");
463         if (!fDiskstat)
464                 xerrx(EXIT_FAILURE,
465                      _("your kernel does not support diskstat. (2.5.70 or above required)"));
466
467         fclose(fDiskstat);
468         ndisks = getdiskstat(&disks, &partitions);
469         npartitions = getpartitions_num(disks, ndisks);
470         for (k = 0; k < npartitions; k++) {
471                 if (!strcmp(partition_name, partitions[k].partition_name)) {
472                         current_partition = &(partitions[k]);
473                 }
474         }
475         if (!current_partition) {
476                 free(disks);
477                 free(partitions);
478                 return -1;
479         }
480         diskpartition_header(partition_name);
481         printf(format,
482                current_partition->reads, current_partition->reads_sectors,
483                current_partition->writes, current_partition->requested_writes);
484         fflush(stdout);
485         free(disks);
486         free(partitions);
487         for (j = 1; infinite_updates || j < num_updates; j++) {
488                 if (moreheaders && ((j % height) == 0))
489                         diskpartition_header(partition_name);
490                 sleep(sleep_time);
491                 ndisks = getdiskstat(&disks, &partitions);
492                 npartitions = getpartitions_num(disks, ndisks);
493                 current_partition = NULL;
494                 for (k = 0; k < npartitions; k++) {
495                         if (!strcmp
496                             (partition_name, partitions[k].partition_name)) {
497                                 current_partition = &(partitions[k]);
498                         }
499                 }
500                 if (!current_partition) {
501                         free(disks);
502                         free(partitions);
503                         return -1;
504                 }
505                 printf(format,
506                        current_partition->reads,
507                        current_partition->reads_sectors,
508                        current_partition->writes,
509                        current_partition->requested_writes);
510                 fflush(stdout);
511                 free(disks);
512                 free(partitions);
513         }
514         return 0;
515 }
516
517 static void diskheader(void)
518 {
519         struct tm *tm_ptr;
520         time_t the_time;
521         char timebuf[32];
522
523         /* Translation Hint: Translating folloging header & fields
524          * that follow (marked with max x chars) might not work,
525          * unless manual page is translated as well.  */
526         const char *header =
527             _("disk- ------------reads------------ ------------writes----------- -----IO------");
528         const char *wide_header =
529             _("disk- -------------------reads------------------- -------------------writes------------------ ------IO-------");
530         const char *timestamp_header = _(" -----timestamp-----");
531
532         const char format[] =
533             "%5s %6s %6s %7s %7s %6s %6s %7s %7s %6s %6s";
534         const char wide_format[] =
535             "%5s %9s %9s %11s %11s %9s %9s %11s %11s %7s %7s";
536
537         printf("%s", w_option ? wide_header : header);
538
539         if (t_option) {
540                 printf("%s", timestamp_header);
541         }
542
543         printf("\n");
544
545         printf(w_option ? wide_format : format,
546                " ",
547                /* Translation Hint: max 6 chars */
548                _("total"),
549                /* Translation Hint: max 6 chars */
550                _("merged"),
551                /* Translation Hint: max 7 chars */
552                _("sectors"),
553                /* Translation Hint: max 7 chars */
554                _("ms"),
555                /* Translation Hint: max 6 chars */
556                _("total"),
557                /* Translation Hint: max 6 chars */
558                _("merged"),
559                /* Translation Hint: max 7 chars */
560                _("sectors"),
561                /* Translation Hint: max 7 chars */
562                _("ms"),
563                /* Translation Hint: max 6 chars */
564                _("cur"),
565                /* Translation Hint: max 6 chars */
566                _("sec"));
567
568         if (t_option) {
569                 (void) time( &the_time );
570                 tm_ptr = localtime( &the_time );
571                 if (tm_ptr && strftime(timebuf, sizeof(timebuf), "%Z", tm_ptr)) {
572                         const size_t len = strlen(timestamp_header);
573                         if (len >= 1 && len - 1 < sizeof(timebuf)) {
574                                 timebuf[len - 1] = '\0';
575                         }
576                 } else {
577                         timebuf[0] = '\0';
578                 }
579                 printf(" %*s", (int)(strlen(timestamp_header) - 1), timebuf);
580         }
581
582         printf("\n");
583 }
584
585 static void diskformat(void)
586 {
587         const char format[] =
588             "%-5s %6u %6u %7llu %7u %6u %6u %7llu %7u %6u %6u";
589         const char wide_format[] =
590             "%-5s %9u %9u %11llu %11u %9u %9u %11llu %11u %7u %7u";
591
592         FILE *fDiskstat;
593         struct disk_stat *disks;
594         struct partition_stat *partitions;
595         unsigned long ndisks, i, j, k;
596         struct tm *tm_ptr;
597         time_t the_time;
598         char timebuf[32];
599
600
601         if ((fDiskstat = fopen("/proc/diskstats", "rb"))) {
602                 fclose(fDiskstat);
603                 ndisks = getdiskstat(&disks, &partitions);
604
605                 if (t_option) {
606                         (void) time( &the_time );
607                         tm_ptr = localtime( &the_time );
608                         if (tm_ptr && strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm_ptr)) {
609                                 ;
610                         } else {
611                                 timebuf[0] = '\0';
612                         }
613                 }
614
615                 if (!moreheaders)
616                         diskheader();
617                 for (k = 0; k < ndisks; k++) {
618                         if (moreheaders && ((k % height) == 0))
619                                 diskheader();
620                         printf(w_option ? wide_format : format,
621                                disks[k].disk_name,
622                                disks[k].reads,
623                                disks[k].merged_reads,
624                                disks[k].reads_sectors,
625                                disks[k].milli_reading,
626                                disks[k].writes,
627                                disks[k].merged_writes,
628                                disks[k].written_sectors,
629                                disks[k].milli_writing,
630                                disks[k].inprogress_IO ? disks[k].inprogress_IO / 1000 : 0,
631                                disks[k].milli_spent_IO ? disks[k].
632                                milli_spent_IO / 1000 : 0);
633
634                         if (t_option) {
635                                 printf(" %s", timebuf);
636                         }
637
638                         printf("\n");
639                         fflush(stdout);
640                 }
641                 free(disks);
642                 free(partitions);
643
644                 for (j = 1; infinite_updates || j < num_updates; j++) {
645                         sleep(sleep_time);
646                         ndisks = getdiskstat(&disks, &partitions);
647
648                         if (t_option) {
649                                 (void) time( &the_time );
650                                 tm_ptr = localtime( &the_time );
651                                 if (tm_ptr && strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm_ptr)) {
652                                         ;
653                                 } else {
654                                         timebuf[0] = '\0';
655                                 }
656                         }
657
658                         for (i = 0; i < ndisks; i++, k++) {
659                                 if (moreheaders && ((k % height) == 0))
660                                         diskheader();
661                                 printf(w_option ? wide_format : format,
662                                        disks[i].disk_name,
663                                        disks[i].reads,
664                                        disks[i].merged_reads,
665                                        disks[i].reads_sectors,
666                                        disks[i].milli_reading,
667                                        disks[i].writes,
668                                        disks[i].merged_writes,
669                                        disks[i].written_sectors,
670                                        disks[i].milli_writing,
671                                        disks[i].inprogress_IO ? disks[i].inprogress_IO / 1000 : 0,
672                                        disks[i].milli_spent_IO ? disks[i].
673                                        milli_spent_IO / 1000 : 0);
674
675                                 if (t_option) {
676                                         printf(" %s", timebuf);
677                                 }
678
679                                 printf("\n");
680                                 fflush(stdout);
681                         }
682                         free(disks);
683                         free(partitions);
684                 }
685         } else
686                 xerrx(EXIT_FAILURE,
687                      _("your kernel does not support diskstat (2.5.70 or above required)"));
688 }
689
690 static void slabheader(void)
691 {
692         printf("%-24s %6s %6s %6s %6s\n",
693         /* Translation Hint: Translating folloging slab fields that
694          * follow (marked with max x chars) might not work, unless
695          * manual page is translated as well.  */
696                /* Translation Hint: max 24 chars */
697                _("Cache"),
698                /* Translation Hint: max 6 chars */
699                _("Num"),
700                /* Translation Hint: max 6 chars */
701                _("Total"),
702                /* Translation Hint: max 6 chars */
703                _("Size"),
704                /* Translation Hint: max 6 chars */
705                _("Pages"));
706 }
707
708 static void slabformat(void)
709 {
710         FILE *fSlab;
711         struct slab_cache *slabs;
712         unsigned long nSlab, i, j, k;
713         const char format[] = "%-24s %6u %6u %6u %6u\n";
714
715         fSlab = fopen("/proc/slabinfo", "rb");
716         if (!fSlab) {
717                 xwarnx(_("your kernel does not support slabinfo or your permissions are insufficient"));
718                 return;
719         }
720
721         if (!moreheaders)
722                 slabheader();
723         nSlab = getslabinfo(&slabs);
724         for (k = 0; k < nSlab; k++) {
725                 if (moreheaders && ((k % height) == 0))
726                         slabheader();
727                 printf(format,
728                        slabs[k].name,
729                        slabs[k].active_objs,
730                        slabs[k].num_objs,
731                        slabs[k].objsize, slabs[k].objperslab);
732         }
733         free(slabs);
734         for (j = 1, k = 1; infinite_updates || j < num_updates; j++) {
735                 sleep(sleep_time);
736                 nSlab = getslabinfo(&slabs);
737                 for (i = 0; i < nSlab; i++, k++) {
738                         if (moreheaders && ((k % height) == 0))
739                                 slabheader();
740                         printf(format,
741                                slabs[i].name,
742                                slabs[i].active_objs,
743                                slabs[i].num_objs,
744                                slabs[i].objsize, slabs[i].objperslab);
745                 }
746                 free(slabs);
747         }
748         fclose(fSlab);
749 }
750
751 static void disksum_format(void)
752 {
753
754         FILE *fDiskstat;
755         struct disk_stat *disks;
756         struct partition_stat *partitions;
757         int ndisks, i;
758         unsigned long reads, merged_reads, read_sectors, milli_reading, writes,
759             merged_writes, written_sectors, milli_writing, inprogress_IO,
760             milli_spent_IO, weighted_milli_spent_IO;
761
762         reads = merged_reads = read_sectors = milli_reading = writes =
763             merged_writes = written_sectors = milli_writing = inprogress_IO =
764             milli_spent_IO = weighted_milli_spent_IO = 0;
765
766         if ((fDiskstat = fopen("/proc/diskstats", "rb"))) {
767                 fclose(fDiskstat);
768                 ndisks = getdiskstat(&disks, &partitions);
769                 printf(_("%13d disks \n"), ndisks);
770                 printf(_("%13d partitions \n"),
771                        getpartitions_num(disks, ndisks));
772
773                 for (i = 0; i < ndisks; i++) {
774                         reads           += disks[i].reads;
775                         merged_reads    += disks[i].merged_reads;
776                         read_sectors    += disks[i].reads_sectors;
777                         milli_reading   += disks[i].milli_reading;
778                         writes          += disks[i].writes;
779                         merged_writes   += disks[i].merged_writes;
780                         written_sectors += disks[i].written_sectors;
781                         milli_writing   += disks[i].milli_writing;
782                         inprogress_IO   += disks[i].inprogress_IO ? disks[i].inprogress_IO / 1000 : 0;
783                         milli_spent_IO  += disks[i].milli_spent_IO ? disks[i].milli_spent_IO / 1000 : 0;
784                 }
785
786                 printf(_("%13lu total reads\n"), reads);
787                 printf(_("%13lu merged reads\n"), merged_reads);
788                 printf(_("%13lu read sectors\n"), read_sectors);
789                 printf(_("%13lu milli reading\n"), milli_reading);
790                 printf(_("%13lu writes\n"), writes);
791                 printf(_("%13lu merged writes\n"), merged_writes);
792                 printf(_("%13lu written sectors\n"), written_sectors);
793                 printf(_("%13lu milli writing\n"), milli_writing);
794                 printf(_("%13lu inprogress IO\n"), inprogress_IO);
795                 printf(_("%13lu milli spent IO\n"), milli_spent_IO);
796
797                 free(disks);
798                 free(partitions);
799         }
800 }
801
802 static void sum_format(void)
803 {
804         unsigned int running, blocked, btime, processes;
805         jiff cpu_use, cpu_nic, cpu_sys, cpu_idl, cpu_iow, cpu_xxx, cpu_yyy, cpu_zzz;
806         unsigned long pgpgin, pgpgout, pswpin, pswpout;
807         unsigned int intr, ctxt;
808
809         meminfo();
810
811         getstat(&cpu_use, &cpu_nic, &cpu_sys, &cpu_idl,
812                 &cpu_iow, &cpu_xxx, &cpu_yyy, &cpu_zzz,
813                 &pgpgin, &pgpgout, &pswpin, &pswpout,
814                 &intr, &ctxt, &running, &blocked, &btime, &processes);
815
816         printf(_("%13lu %s total memory\n"), unitConvert(kb_main_total), szDataUnit);
817         printf(_("%13lu %s used memory\n"), unitConvert(kb_main_used), szDataUnit);
818         printf(_("%13lu %s active memory\n"), unitConvert(kb_active), szDataUnit);
819         printf(_("%13lu %s inactive memory\n"), unitConvert(kb_inactive), szDataUnit);
820         printf(_("%13lu %s free memory\n"), unitConvert(kb_main_free), szDataUnit);
821         printf(_("%13lu %s buffer memory\n"), unitConvert(kb_main_buffers), szDataUnit);
822         printf(_("%13lu %s swap cache\n"), unitConvert(kb_main_cached), szDataUnit);
823         printf(_("%13lu %s total swap\n"), unitConvert(kb_swap_total), szDataUnit);
824         printf(_("%13lu %s used swap\n"), unitConvert(kb_swap_used), szDataUnit);
825         printf(_("%13lu %s free swap\n"), unitConvert(kb_swap_free), szDataUnit);
826         printf(_("%13lld non-nice user cpu ticks\n"), cpu_use);
827         printf(_("%13lld nice user cpu ticks\n"), cpu_nic);
828         printf(_("%13lld system cpu ticks\n"), cpu_sys);
829         printf(_("%13lld idle cpu ticks\n"), cpu_idl);
830         printf(_("%13lld IO-wait cpu ticks\n"), cpu_iow);
831         printf(_("%13lld IRQ cpu ticks\n"), cpu_xxx);
832         printf(_("%13lld softirq cpu ticks\n"), cpu_yyy);
833         printf(_("%13lld stolen cpu ticks\n"), cpu_zzz);
834         printf(_("%13lu pages paged in\n"), pgpgin);
835         printf(_("%13lu pages paged out\n"), pgpgout);
836         printf(_("%13lu pages swapped in\n"), pswpin);
837         printf(_("%13lu pages swapped out\n"), pswpout);
838         printf(_("%13u interrupts\n"), intr);
839         printf(_("%13u CPU context switches\n"), ctxt);
840         printf(_("%13u boot time\n"), btime);
841         printf(_("%13u forks\n"), processes);
842 }
843
844 static void fork_format(void)
845 {
846         unsigned int running, blocked, btime, processes;
847         jiff cpu_use, cpu_nic, cpu_sys, cpu_idl, cpu_iow, cpu_xxx, cpu_yyy, cpu_zzz;
848         unsigned long pgpgin, pgpgout, pswpin, pswpout;
849         unsigned int intr, ctxt;
850
851         getstat(&cpu_use, &cpu_nic, &cpu_sys, &cpu_idl,
852                 &cpu_iow, &cpu_xxx, &cpu_yyy, &cpu_zzz,
853                 &pgpgin, &pgpgout, &pswpin, &pswpout,
854                 &intr, &ctxt, &running, &blocked, &btime, &processes);
855
856         printf(_("%13u forks\n"), processes);
857 }
858
859 static int winhi(void)
860 {
861         struct winsize win;
862         int rows = 24;
863
864         if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 && 0 < win.ws_row)
865                 rows = win.ws_row;
866
867         return rows;
868 }
869
870 int main(int argc, char *argv[])
871 {
872         char *partition = NULL;
873         int c;
874         long tmp;
875
876         static const struct option longopts[] = {
877                 {"active", no_argument, NULL, 'a'},
878                 {"forks", no_argument, NULL, 'f'},
879                 {"slabs", no_argument, NULL, 'm'},
880                 {"one-header", no_argument, NULL, 'n'},
881                 {"stats", no_argument, NULL, 's'},
882                 {"disk", no_argument, NULL, 'd'},
883                 {"disk-sum", no_argument, NULL, 'D'},
884                 {"partition", required_argument, NULL, 'p'},
885                 {"unit", required_argument, NULL, 'S'},
886                 {"wide", no_argument, NULL, 'w'},
887                 {"timestamp", no_argument, NULL, 't'},
888                 {"help", no_argument, NULL, 'h'},
889                 {"version", no_argument, NULL, 'V'},
890                 {NULL, 0, NULL, 0}
891         };
892
893 #ifdef HAVE_PROGRAM_INVOCATION_NAME
894         program_invocation_name = program_invocation_short_name;
895 #endif
896         setlocale (LC_ALL, "");
897         bindtextdomain(PACKAGE, LOCALEDIR);
898         textdomain(PACKAGE);
899         atexit(close_stdout);
900
901         while ((c =
902                 getopt_long(argc, argv, "afmnsdDp:S:wthV", longopts,
903                             NULL)) != -1)
904                 switch (c) {
905                 case 'V':
906                         printf(PROCPS_NG_VERSION);
907                         return EXIT_SUCCESS;
908                 case 'h':
909                         usage(stdout);
910                 case 'd':
911                         statMode |= DISKSTAT;
912                         break;
913                 case 'a':
914                         /* active/inactive mode */
915                         a_option = 1;
916                         break;
917                 case 'f':
918                         /* FIXME: check for conflicting args */
919                         fork_format();
920                         exit(0);
921                 case 'm':
922                         statMode |= SLABSTAT;
923                         break;
924                 case 'D':
925                         statMode |= DISKSUMSTAT;
926                         break;
927                 case 'n':
928                         /* print only one header */
929                         moreheaders = FALSE;
930                         break;
931                 case 'p':
932                         statMode |= PARTITIONSTAT;
933                         partition = optarg;
934                         if (strncmp(partition, "/dev/", 5) == 0)
935                                 partition += 5;
936                         break;
937                 case 'S':
938                         switch (optarg[0]) {
939                         case 'b':
940                         case 'B':
941                                 dataUnit = UNIT_B;
942                                 break;
943                         case 'k':
944                                 dataUnit = UNIT_k;
945                                 break;
946                         case 'K':
947                                 dataUnit = UNIT_K;
948                                 break;
949                         case 'm':
950                                 dataUnit = UNIT_m;
951                                 break;
952                         case 'M':
953                                 dataUnit = UNIT_M;
954                                 break;
955                         default:
956                                 xerrx(EXIT_FAILURE,
957                                      /* Translation Hint: do not change argument characters */
958                                      _("-S requires k, K, m or M (default is KiB)"));
959                         }
960                         szDataUnit[0] = optarg[0];
961                         break;
962                 case 's':
963                         statMode |= VMSUMSTAT;
964                         break;
965                 case 'w':
966                         w_option = 1;
967                         break;
968                 case 't':
969                         t_option = 1;
970                         break;
971                 default:
972                         /* no other aguments defined yet. */
973                         usage(stderr);
974                 }
975
976         if (optind < argc) {
977                 tmp = strtol_or_err(argv[optind++], _("failed to parse argument"));
978                 if (tmp < 1)
979                         xerrx(EXIT_FAILURE, _("delay must be positive integer"));
980                 else if (UINT_MAX < tmp)
981                         xerrx(EXIT_FAILURE, _("too large delay value"));
982                 sleep_time = tmp;
983                 infinite_updates = 1;
984         }
985         if (optind < argc) {
986                 num_updates = strtol_or_err(argv[optind++], _("failed to parse argument"));
987                 infinite_updates = 0;
988         }
989         if (optind < argc)
990                 usage(stderr);
991
992         if (moreheaders) {
993                 int wheight = winhi() - 3;
994                 height = ((wheight > 0) ? wheight : 22);
995         }
996         setlinebuf(stdout);
997         switch (statMode) {
998         case (VMSTAT):
999                 new_format();
1000                 break;
1001         case (VMSUMSTAT):
1002                 sum_format();
1003                 break;
1004         case (DISKSTAT):
1005                 diskformat();
1006                 break;
1007         case (PARTITIONSTAT):
1008                 if (diskpartition_format(partition) == -1)
1009                         printf(_("partition was not found\n"));
1010                 break;
1011         case (SLABSTAT):
1012                 slabformat();
1013                 break;
1014         case (DISKSUMSTAT):
1015                 disksum_format();
1016                 break;
1017         default:
1018                 usage(stderr);
1019                 break;
1020         }
1021         return 0;
1022 }