]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - usr.sbin/pstat/pstat.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / usr.sbin / pstat / pstat.c
1 /*-
2  * Copyright (c) 1980, 1991, 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 2002 Networks Associates Technologies, Inc.
5  * All rights reserved.
6  *
7  * Portions of this software were developed for the FreeBSD Project by
8  * ThinkSec AS and NAI Labs, the Security Research Division of Network
9  * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
10  * ("CBOSS"), as part of the DARPA CHATS research program.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #if 0
38 #ifndef lint
39 static const char copyright[] =
40 "@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
41         The Regents of the University of California.  All rights reserved.\n";
42 #endif /* not lint */
43
44 #ifndef lint
45 static char sccsid[] = "@(#)pstat.c     8.16 (Berkeley) 5/9/95";
46 #endif /* not lint */
47 #endif
48 #include <sys/cdefs.h>
49 __FBSDID("$FreeBSD$");
50
51 #include <sys/param.h>
52 #include <sys/time.h>
53 #include <sys/file.h>
54 #include <sys/stat.h>
55 #include <sys/stdint.h>
56 #include <sys/ioctl.h>
57 #include <sys/ioctl_compat.h>   /* XXX NTTYDISC is too well hidden */
58 #include <sys/tty.h>
59 #include <sys/blist.h>
60
61 #include <sys/sysctl.h>
62 #include <vm/vm_param.h>
63
64 #include <err.h>
65 #include <errno.h>
66 #include <fcntl.h>
67 #include <kvm.h>
68 #include <libutil.h>
69 #include <limits.h>
70 #include <nlist.h>
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <unistd.h>
75
76 enum {
77         NL_CONSTTY,
78         NL_MAXFILES,
79         NL_NFILES,
80         NL_TTY_LIST
81 };
82
83 static struct nlist nl[] = {
84         { .n_name = "_constty" },
85         { .n_name = "_maxfiles" },
86         { .n_name = "_openfiles" },
87         { .n_name = "_tty_list" },
88         { .n_name = "" }
89 };
90
91 static int      humanflag;
92 static int      usenumflag;
93 static int      totalflag;
94 static int      swapflag;
95 static char     *nlistf;
96 static char     *memf;
97 static kvm_t    *kd;
98
99 static char     *usagestr;
100
101 static void     filemode(void);
102 static int      getfiles(char **, size_t *);
103 static void     swapmode(void);
104 static void     ttymode(void);
105 static void     ttyprt(struct xtty *);
106 static void     usage(void);
107
108 int
109 main(int argc, char *argv[])
110 {
111         int ch, i, quit, ret;
112         int fileflag, ttyflag;
113         char buf[_POSIX2_LINE_MAX],*opts;
114
115         fileflag = swapflag = ttyflag = 0;
116
117         /* We will behave like good old swapinfo if thus invoked */
118         opts = strrchr(argv[0], '/');
119         if (opts)
120                 opts++;
121         else
122                 opts = argv[0];
123         if (!strcmp(opts, "swapinfo")) {
124                 swapflag = 1;
125                 opts = "ghkmM:N:";
126                 usagestr = "swapinfo [-ghkm] [-M core [-N system]]";
127         } else {
128                 opts = "TM:N:fghkmnst";
129                 usagestr = "pstat [-Tfghkmnst] [-M core [-N system]]";
130         }
131
132         while ((ch = getopt(argc, argv, opts)) != -1)
133                 switch (ch) {
134                 case 'f':
135                         fileflag = 1;
136                         break;
137                 case 'g':
138                         setenv("BLOCKSIZE", "1G", 1);
139                         break;
140                 case 'h':
141                         humanflag = 1;
142                         break;
143                 case 'k':
144                         setenv("BLOCKSIZE", "1K", 1);
145                         break;
146                 case 'm':
147                         setenv("BLOCKSIZE", "1M", 1);
148                         break;
149                 case 'M':
150                         memf = optarg;
151                         break;
152                 case 'N':
153                         nlistf = optarg;
154                         break;
155                 case 'n':
156                         usenumflag = 1;
157                         break;
158                 case 's':
159                         ++swapflag;
160                         break;
161                 case 'T':
162                         totalflag = 1;
163                         break;
164                 case 't':
165                         ttyflag = 1;
166                         break;
167                 default:
168                         usage();
169                 }
170         argc -= optind;
171         argv += optind;
172
173         if (memf != NULL) {
174                 kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf);
175                 if (kd == NULL)
176                         errx(1, "kvm_openfiles: %s", buf);
177                 if ((ret = kvm_nlist(kd, nl)) != 0) {
178                         if (ret == -1)
179                                 errx(1, "kvm_nlist: %s", kvm_geterr(kd));
180                         quit = 0;
181                         for (i = 0; nl[i].n_name[0] != '\0'; ++i)
182                                 if (nl[i].n_value == 0) {
183                                         quit = 1;
184                                         warnx("undefined symbol: %s",
185                                             nl[i].n_name);
186                                 }
187                         if (quit)
188                                 exit(1);
189                 }
190         }
191         if (!(fileflag | ttyflag | swapflag | totalflag))
192                 usage();
193         if (fileflag || totalflag)
194                 filemode();
195         if (ttyflag)
196                 ttymode();
197         if (swapflag || totalflag)
198                 swapmode();
199         exit (0);
200 }
201
202 static void
203 usage(void)
204 {
205         fprintf(stderr, "usage: %s\n", usagestr);
206         exit (1);
207 }
208
209 static const char fhdr32[] =
210   "   LOC   TYPE   FLG  CNT MSG   DATA        OFFSET\n";
211 /* c0000000 ------ RWAI 123 123 c0000000 1000000000000000 */
212
213 static const char fhdr64[] =
214   "       LOC       TYPE   FLG  CNT MSG       DATA            OFFSET\n";
215 /* c000000000000000 ------ RWAI 123 123 c000000000000000 1000000000000000 */
216
217 static const char hdr[] =
218 "  LINE RAW CAN OUT IHIWT ILOWT OHWT LWT     COL STATE  SESS      PGID DISC\n";
219
220 static void
221 ttymode_kvm(void)
222 {
223         TAILQ_HEAD(, tty) tl;
224         struct tty *tp, tty;
225         struct xtty xt;
226
227         (void)printf("%s", hdr);
228         bzero(&xt, sizeof xt);
229         xt.xt_size = sizeof xt;
230         if (kvm_read(kd, nl[NL_TTY_LIST].n_value, &tl, sizeof tl) != sizeof tl)
231                 errx(1, "kvm_read(): %s", kvm_geterr(kd));
232         tp = TAILQ_FIRST(&tl);
233         while (tp != NULL) {
234                 if (kvm_read(kd, (u_long)tp, &tty, sizeof tty) != sizeof tty)
235                         errx(1, "kvm_read(): %s", kvm_geterr(kd));
236                 xt.xt_rawcc = tty.t_rawq.c_cc;
237                 xt.xt_cancc = tty.t_canq.c_cc;
238                 xt.xt_outcc = tty.t_outq.c_cc;
239 #define XT_COPY(field) xt.xt_##field = tty.t_##field
240                 XT_COPY(line);
241                 XT_COPY(state);
242                 XT_COPY(column);
243                 XT_COPY(ihiwat);
244                 XT_COPY(ilowat);
245                 XT_COPY(ohiwat);
246                 XT_COPY(olowat);
247 #undef XT_COPY
248                 ttyprt(&xt);
249                 tp = TAILQ_NEXT(tp, t_list);
250         }
251 }
252
253 static void
254 ttymode_sysctl(void)
255 {
256         struct xtty *xt, *end;
257         void *xttys;
258         size_t len;
259
260         (void)printf("%s", hdr);
261         if ((xttys = malloc(len = sizeof *xt)) == NULL)
262                 err(1, "malloc()");
263         while (sysctlbyname("kern.ttys", xttys, &len, 0, 0) == -1) {
264                 if (errno != ENOMEM)
265                         err(1, "sysctlbyname()");
266                 len *= 2;
267                 if ((xttys = realloc(xttys, len)) == NULL)
268                         err(1, "realloc()");
269         }
270         if (len > 0) {
271                 end = (struct xtty *)((char *)xttys + len);
272                 for (xt = xttys; xt < end; xt++)
273                         ttyprt(xt);
274         }
275 }
276
277 static void
278 ttymode(void)
279 {
280
281         if (kd != NULL)
282                 ttymode_kvm();
283         else
284                 ttymode_sysctl();
285 }
286
287 static struct {
288         int flag;
289         char val;
290 } ttystates[] = {
291 #ifdef TS_WOPEN
292         { TS_WOPEN,     'W'},
293 #endif
294         { TS_ISOPEN,    'O'},
295         { TS_CARR_ON,   'C'},
296 #ifdef TS_CONNECTED
297         { TS_CONNECTED, 'c'},
298 #endif
299         { TS_TIMEOUT,   'T'},
300         { TS_FLUSH,     'F'},
301         { TS_BUSY,      'B'},
302 #ifdef TS_ASLEEP
303         { TS_ASLEEP,    'A'},
304 #endif
305 #ifdef TS_SO_OLOWAT
306         { TS_SO_OLOWAT, 'A'},
307 #endif
308 #ifdef TS_SO_OCOMPLETE
309         { TS_SO_OCOMPLETE, 'a'},
310 #endif
311         { TS_XCLUDE,    'X'},
312         { TS_TTSTOP,    'S'},
313 #ifdef TS_CAR_OFLOW
314         { TS_CAR_OFLOW, 'm'},
315 #endif
316 #ifdef TS_CTS_OFLOW
317         { TS_CTS_OFLOW, 'o'},
318 #endif
319 #ifdef TS_DSR_OFLOW
320         { TS_DSR_OFLOW, 'd'},
321 #endif
322         { TS_TBLOCK,    'K'},
323         { TS_ASYNC,     'Y'},
324         { TS_BKSL,      'D'},
325         { TS_ERASE,     'E'},
326         { TS_LNCH,      'L'},
327         { TS_TYPEN,     'P'},
328         { TS_CNTTB,     'N'},
329 #ifdef TS_CAN_BYPASS_L_RINT
330         { TS_CAN_BYPASS_L_RINT, 'l'},
331 #endif
332 #ifdef TS_SNOOP
333         { TS_SNOOP,     's'},
334 #endif
335 #ifdef TS_ZOMBIE
336         { TS_ZOMBIE,    'Z'},
337 #endif
338         { 0,           '\0'},
339 };
340
341 static void
342 ttyprt(struct xtty *xt)
343 {
344         int i, j;
345         char *name, state[20];
346
347         if (xt->xt_size != sizeof *xt)
348                 errx(1, "struct xtty size mismatch");
349         if (usenumflag || xt->xt_dev == 0 ||
350            (name = devname(xt->xt_dev, S_IFCHR)) == NULL)
351                 printf("   %2d,%-2d", major(xt->xt_dev), minor(xt->xt_dev));
352         else
353                 (void)printf("%7s ", name);
354         (void)printf("%2ld %3ld ", xt->xt_rawcc, xt->xt_cancc);
355         (void)printf("%3ld %5d %5d %4d %3d %7d ", xt->xt_outcc,
356                 xt->xt_ihiwat, xt->xt_ilowat, xt->xt_ohiwat, xt->xt_olowat,
357                 xt->xt_column);
358         for (i = j = 0; ttystates[i].flag; i++)
359                 if (xt->xt_state & ttystates[i].flag)
360                         state[j++] = ttystates[i].val;
361         if (j == 0)
362                 state[j++] = '-';
363         state[j] = '\0';
364         (void)printf("%-6s %8d", state, xt->xt_sid);
365         (void)printf("%6d ", xt->xt_pgid);
366         switch (xt->xt_line) {
367         case TTYDISC:
368                 (void)printf("term\n");
369                 break;
370         case NTTYDISC:
371                 (void)printf("ntty\n");
372                 break;
373         case SLIPDISC:
374                 (void)printf("slip\n");
375                 break;
376         case PPPDISC:
377                 (void)printf("ppp\n");
378                 break;
379         default:
380                 (void)printf("%d\n", xt->xt_line);
381                 break;
382         }
383 }
384
385 static void
386 filemode(void)
387 {
388         struct xfile *fp;
389         char *buf, flagbuf[16], *fbp;
390         int maxf, openf;
391         size_t len;
392         static char *dtypes[] = { "???", "inode", "socket", "pipe",
393             "fifo", "kqueue", "crypto" };
394         int i;
395         int wid;
396
397         if (kd != NULL) {
398                 if (kvm_read(kd, nl[NL_MAXFILES].n_value,
399                         &maxf, sizeof maxf) != sizeof maxf ||
400                     kvm_read(kd, nl[NL_NFILES].n_value,
401                         &openf, sizeof openf) != sizeof openf)
402                         errx(1, "kvm_read(): %s", kvm_geterr(kd));
403         } else {
404                 len = sizeof(int);
405                 if (sysctlbyname("kern.maxfiles", &maxf, &len, 0, 0) == -1 ||
406                     sysctlbyname("kern.openfiles", &openf, &len, 0, 0) == -1)
407                         err(1, "sysctlbyname()");
408         }
409
410         if (totalflag) {
411                 (void)printf("%3d/%3d files\n", openf, maxf);
412                 return;
413         }
414         if (getfiles(&buf, &len) == -1)
415                 return;
416         openf = len / sizeof *fp;
417
418         (void)printf("%d/%d open files\n", openf, maxf);
419         printf(sizeof(uintptr_t) == 4 ? fhdr32 : fhdr64);
420         wid = (int)sizeof(uintptr_t) * 2;
421         for (fp = (struct xfile *)buf, i = 0; i < openf; ++fp, ++i) {
422                 if ((size_t)fp->xf_type >= sizeof(dtypes) / sizeof(dtypes[0]))
423                         continue;
424                 (void)printf("%*jx", wid, (uintmax_t)(uintptr_t)fp->xf_file);
425                 (void)printf(" %-6.6s", dtypes[fp->xf_type]);
426                 fbp = flagbuf;
427                 if (fp->xf_flag & FREAD)
428                         *fbp++ = 'R';
429                 if (fp->xf_flag & FWRITE)
430                         *fbp++ = 'W';
431                 if (fp->xf_flag & FAPPEND)
432                         *fbp++ = 'A';
433                 if (fp->xf_flag & FASYNC)
434                         *fbp++ = 'I';
435                 *fbp = '\0';
436                 (void)printf(" %4s %3d", flagbuf, fp->xf_count);
437                 (void)printf(" %3d", fp->xf_msgcount);
438                 (void)printf(" %*jx", wid, (uintmax_t)(uintptr_t)fp->xf_data);
439                 (void)printf(" %*jx\n", (int)sizeof(fp->xf_offset) * 2,
440                     (uintmax_t)fp->xf_offset);
441         }
442         free(buf);
443 }
444
445 static int
446 getfiles(char **abuf, size_t *alen)
447 {
448         size_t len;
449         int mib[2];
450         char *buf;
451
452         /*
453          * XXX
454          * Add emulation of KINFO_FILE here.
455          */
456         if (kd != NULL)
457                 errx(1, "files on dead kernel, not implemented");
458
459         mib[0] = CTL_KERN;
460         mib[1] = KERN_FILE;
461         if (sysctl(mib, 2, NULL, &len, NULL, 0) == -1) {
462                 warn("sysctl: KERN_FILE");
463                 return (-1);
464         }
465         if ((buf = malloc(len)) == NULL)
466                 errx(1, "malloc");
467         if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) {
468                 warn("sysctl: KERN_FILE");
469                 return (-1);
470         }
471         *abuf = buf;
472         *alen = len;
473         return (0);
474 }
475
476 /*
477  * swapmode is based on a program called swapinfo written
478  * by Kevin Lahey <kml@rokkaku.atl.ga.us>.
479  */
480
481 #define CONVERT(v)      ((int64_t)(v) * pagesize / blocksize)
482 static struct kvm_swap swtot;
483 static int nswdev;
484
485 static void
486 print_swap_header(void)
487 {
488         int hlen;
489         long blocksize;
490         const char *header;
491
492         header = getbsize(&hlen, &blocksize);
493         if (totalflag == 0)
494                 (void)printf("%-15s %*s %8s %8s %8s\n",
495                     "Device", hlen, header,
496                     "Used", "Avail", "Capacity");
497 }
498
499 static void
500 print_swap_line(const char *devname, intmax_t nblks, intmax_t bused,
501     intmax_t bavail, float bpercent)
502 {
503         char usedbuf[5];
504         char availbuf[5];
505         int hlen, pagesize;
506         long blocksize;
507
508         pagesize = getpagesize();
509         getbsize(&hlen, &blocksize);
510
511         printf("%-15s %*jd ", devname, hlen, CONVERT(nblks));
512         if (humanflag) {
513                 humanize_number(usedbuf, sizeof(usedbuf),
514                     CONVERT(blocksize * bused), "",
515                     HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
516                 humanize_number(availbuf, sizeof(availbuf),
517                     CONVERT(blocksize * bavail), "",
518                     HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
519                 printf("%8s %8s %5.0f%%\n", usedbuf, availbuf, bpercent);
520         } else {
521                 printf("%8jd %8jd %5.0f%%\n", (intmax_t)CONVERT(bused),
522                     (intmax_t)CONVERT(bavail), bpercent);
523         }
524 }
525
526 static void
527 print_swap(struct kvm_swap *ksw)
528 {
529
530         swtot.ksw_total += ksw->ksw_total;
531         swtot.ksw_used += ksw->ksw_used;
532         ++nswdev;
533         if (totalflag == 0)
534                 print_swap_line(ksw->ksw_devname, ksw->ksw_total,
535                     ksw->ksw_used, ksw->ksw_total - ksw->ksw_used,
536                     (ksw->ksw_used * 100.0) / ksw->ksw_total);
537 }
538
539 static void
540 print_swap_total(void)
541 {
542         int hlen, pagesize;
543         long blocksize;
544
545         pagesize = getpagesize();
546         getbsize(&hlen, &blocksize);
547         if (totalflag) {
548                 blocksize = 1024 * 1024;
549                 (void)printf("%jdM/%jdM swap space\n",
550                     CONVERT(swtot.ksw_used), CONVERT(swtot.ksw_total));
551         } else if (nswdev > 1) {
552                 print_swap_line("Total", swtot.ksw_total, swtot.ksw_used,
553                     swtot.ksw_total - swtot.ksw_used,
554                     (swtot.ksw_used * 100.0) / swtot.ksw_total);
555         }
556 }
557
558 static void
559 swapmode_kvm(void)
560 {
561         struct kvm_swap kswap[16];
562         int i, n;
563
564         n = kvm_getswapinfo(kd, kswap, sizeof kswap / sizeof kswap[0],
565             SWIF_DEV_PREFIX);
566
567         print_swap_header();
568         for (i = 0; i < n; ++i)
569                 print_swap(&kswap[i]);
570         print_swap_total();
571 }
572
573 static void
574 swapmode_sysctl(void)
575 {
576         struct kvm_swap ksw;
577         struct xswdev xsw;
578         size_t mibsize, size;
579         int mib[16], n;
580
581         print_swap_header();
582         mibsize = sizeof mib / sizeof mib[0];
583         if (sysctlnametomib("vm.swap_info", mib, &mibsize) == -1)
584                 err(1, "sysctlnametomib()");
585         for (n = 0; ; ++n) {
586                 mib[mibsize] = n;
587                 size = sizeof xsw;
588                 if (sysctl(mib, mibsize + 1, &xsw, &size, NULL, 0) == -1)
589                         break;
590                 if (xsw.xsw_version != XSWDEV_VERSION)
591                         errx(1, "xswdev version mismatch");
592                 if (xsw.xsw_dev == NODEV)
593                         snprintf(ksw.ksw_devname, sizeof ksw.ksw_devname,
594                             "<NFSfile>");
595                 else
596                         snprintf(ksw.ksw_devname, sizeof ksw.ksw_devname,
597                             "/dev/%s", devname(xsw.xsw_dev, S_IFCHR));
598                 ksw.ksw_used = xsw.xsw_used;
599                 ksw.ksw_total = xsw.xsw_nblks;
600                 ksw.ksw_flags = xsw.xsw_flags;
601                 print_swap(&ksw);
602         }
603         if (errno != ENOENT)
604                 err(1, "sysctl()");
605         print_swap_total();
606 }
607
608 static void
609 swapmode(void)
610 {
611         if (kd != NULL)
612                 swapmode_kvm();
613         else
614                 swapmode_sysctl();
615 }