]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/systat/devs.c
Remove spurious newline
[FreeBSD/FreeBSD.git] / usr.bin / systat / devs.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1998 Kenneth D. Merry.
5  *               2015 Yoshihiro Ota
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 /*-
32  * Copyright (c) 1980, 1992, 1993
33  *      The Regents of the University of California.  All rights reserved.
34  *
35  * Redistribution and use in source and binary forms, with or without
36  * modification, are permitted provided that the following conditions
37  * are met:
38  * 1. Redistributions of source code must retain the above copyright
39  *    notice, this list of conditions and the following disclaimer.
40  * 2. Redistributions in binary form must reproduce the above copyright
41  *    notice, this list of conditions and the following disclaimer in the
42  *    documentation and/or other materials provided with the distribution.
43  * 3. Neither the name of the University nor the names of its contributors
44  *    may be used to endorse or promote products derived from this software
45  *    without specific prior written permission.
46  *
47  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
48  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
51  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57  * SUCH DAMAGE.
58  */
59
60 #include <sys/cdefs.h>
61
62 __FBSDID("$FreeBSD$");
63
64 #ifdef lint
65 static const char sccsid[] = "@(#)disks.c       8.1 (Berkeley) 6/6/93";
66 #endif
67
68 #include <sys/types.h>
69 #include <sys/devicestat.h>
70 #include <sys/resource.h>
71
72 #include <ctype.h>
73 #include <err.h>
74 #include <stdlib.h>
75 #include <string.h>
76
77 #include "systat.h"
78 #include "extern.h"
79 #include "devs.h"
80
81 typedef enum {
82         DS_MATCHTYPE_NONE,
83         DS_MATCHTYPE_SPEC,
84         DS_MATCHTYPE_PATTERN
85 } last_match_type;
86
87 struct statinfo cur_dev, last_dev, run_dev;
88
89 last_match_type last_type;
90 struct device_selection *dev_select;
91 long generation;
92 int num_devices, num_selected;
93 int num_selections;
94 long select_generation;
95 struct devstat_match *matches = NULL;
96 int num_matches = 0;
97 char **specified_devices;
98 int num_devices_specified = 0;
99
100 static int dsmatchselect(const char *args, devstat_select_mode select_mode,
101                          int maxshowdevs, struct statinfo *s1);
102 static int dsselect(const char *args, devstat_select_mode select_mode,
103                     int maxshowdevs, struct statinfo *s1);
104
105 int
106 dsinit(int maxshowdevs)
107 {
108         /*
109          * Make sure that the userland devstat version matches the kernel
110          * devstat version.  If not, exit and print a message informing
111          * the user of his mistake.
112          */
113         if (devstat_checkversion(NULL) < 0)
114                 errx(1, "%s", devstat_errbuf);
115
116         if( cur_dev.dinfo ) // init was alreay ran
117                 return(1);
118
119         if ((num_devices = devstat_getnumdevs(NULL)) < 0) {
120                 warnx("%s", devstat_errbuf);
121                 return(0);
122         }
123
124         cur_dev.dinfo = calloc(1, sizeof(struct devinfo));
125         last_dev.dinfo = calloc(1, sizeof(struct devinfo));
126         run_dev.dinfo = calloc(1, sizeof(struct devinfo));
127
128         generation = 0;
129         num_devices = 0;
130         num_selected = 0;
131         num_selections = 0;
132         select_generation = 0;
133         last_type = DS_MATCHTYPE_NONE;
134
135         if (devstat_getdevs(NULL, &cur_dev) == -1)
136                 errx(1, "%s", devstat_errbuf);
137
138         num_devices = cur_dev.dinfo->numdevs;
139         generation = cur_dev.dinfo->generation;
140
141         dev_select = NULL;
142
143         /*
144          * At this point, selectdevs will almost surely indicate that the
145          * device list has changed, so we don't look for return values of 0
146          * or 1.  If we get back -1, though, there is an error.
147          */
148         if (devstat_selectdevs(&dev_select, &num_selected, &num_selections,
149             &select_generation, generation, cur_dev.dinfo->devices, num_devices,
150             NULL, 0, NULL, 0, DS_SELECT_ADD, maxshowdevs, 0) == -1)
151                 errx(1, "%d %s", __LINE__, devstat_errbuf);
152
153         return(1);
154 }
155
156
157 void
158 dsgetinfo(struct statinfo* dev)
159 {
160         switch (devstat_getdevs(NULL, dev)) {
161         case -1:
162                 errx(1, "%s", devstat_errbuf);
163                 break;
164         case 1:
165                 num_devices = dev->dinfo->numdevs;
166                 generation = dev->dinfo->generation;
167                 cmdkre("refresh", NULL);
168                 break;
169         default:
170                 break;
171         }
172 }
173
174 int
175 dscmd(const char *cmd, const char *args, int maxshowdevs, struct statinfo *s1)
176 {
177         int retval;
178
179         if (prefix(cmd, "display") || prefix(cmd, "add"))
180                 return(dsselect(args, DS_SELECT_ADDONLY, maxshowdevs, s1));
181         if (prefix(cmd, "ignore") || prefix(cmd, "delete"))
182                 return(dsselect(args, DS_SELECT_REMOVE, maxshowdevs, s1));
183         if (prefix(cmd, "show") || prefix(cmd, "only"))
184                 return(dsselect(args, DS_SELECT_ONLY, maxshowdevs, s1));
185         if (prefix(cmd, "type") || prefix(cmd, "match"))
186                 return(dsmatchselect(args, DS_SELECT_ONLY, maxshowdevs, s1));
187         if (prefix(cmd, "refresh")) {
188                 retval = devstat_selectdevs(&dev_select, &num_selected,
189                     &num_selections, &select_generation, generation,
190                     s1->dinfo->devices, num_devices,
191                     (last_type ==DS_MATCHTYPE_PATTERN) ?  matches : NULL,
192                     (last_type ==DS_MATCHTYPE_PATTERN) ?  num_matches : 0,
193                     (last_type == DS_MATCHTYPE_SPEC) ?specified_devices : NULL,
194                     (last_type == DS_MATCHTYPE_SPEC) ?num_devices_specified : 0,
195                     (last_type == DS_MATCHTYPE_NONE) ?  DS_SELECT_ADD :
196                     DS_SELECT_ADDONLY, maxshowdevs, 0);
197                 if (retval == -1) {
198                         warnx("%s", devstat_errbuf);
199                         return(0);
200                 } else if (retval == 1)
201                         return(2);
202         }
203         if (prefix(cmd, "drives")) {
204                 int i;
205                 move(CMDLINE, 0);
206                 clrtoeol();
207                 for (i = 0; i < num_devices; i++) {
208                         printw("%s%d ", s1->dinfo->devices[i].device_name,
209                                s1->dinfo->devices[i].unit_number);
210                 }
211                 return(1);
212         }
213         return(0);
214 }
215
216 static int
217 dsmatchselect(const char *args, devstat_select_mode select_mode, int maxshowdevs,
218               struct statinfo *s1)
219 {
220         char **tempstr, *tmpstr, *tmpstr1;
221         char *tstr[100];
222         int num_args = 0;
223         int i;
224         int retval = 0;
225
226         if (!args) {
227                 warnx("dsmatchselect: no arguments");
228                 return(1);
229         }
230
231         /*
232          * Break the (pipe delimited) input string out into separate
233          * strings.
234          */
235         tmpstr = tmpstr1 = strdup(args);
236         for (tempstr = tstr, num_args  = 0;
237              (*tempstr = strsep(&tmpstr1, "|")) != NULL && (num_args < 100);
238              num_args++)
239                 if (**tempstr != '\0')
240                         if (++tempstr >= &tstr[100])
241                                 break;
242         free(tmpstr);
243
244         if (num_args > 99) {
245                 warnx("dsmatchselect: too many match arguments");
246                 return(0);
247         }
248
249         /*
250          * If we've gone through the matching code before, clean out
251          * previously used memory.
252          */
253         if (num_matches > 0) {
254                 free(matches);
255                 matches = NULL;
256                 num_matches = 0;
257         }
258
259         for (i = 0; i < num_args; i++) {
260                 if (devstat_buildmatch(tstr[i], &matches, &num_matches) != 0) {
261                         warnx("%s", devstat_errbuf);
262                         return(0);
263                 }
264         }
265         if (num_args > 0) {
266
267                 last_type = DS_MATCHTYPE_PATTERN;
268
269                 retval = devstat_selectdevs(&dev_select, &num_selected,
270                     &num_selections, &select_generation, generation,
271                     s1->dinfo->devices, num_devices, matches, num_matches,
272                     NULL, 0, select_mode, maxshowdevs, 0);
273                 if (retval == -1)
274                         err(1, "device selection error");
275                 else if (retval == 1)
276                         return(2);
277         }
278         return(1);
279 }
280
281 static int
282 dsselect(const char *args, devstat_select_mode select_mode, int maxshowdevs,
283          struct statinfo *s1)
284 {
285         char *cp, *tmpstr, *tmpstr1, *buffer;
286         int i;
287         int retval = 0;
288
289         if (!args) {
290                 warnx("dsselect: no argument");
291                 return(1);
292         }
293
294         /*
295          * If we've gone through this code before, free previously
296          * allocated resources.
297          */
298         if (num_devices_specified > 0) {
299                 for (i = 0; i < num_devices_specified; i++)
300                         free(specified_devices[i]);
301                 free(specified_devices);
302                 specified_devices = NULL;
303                 num_devices_specified = 0;
304         }
305
306         /* do an initial malloc */
307         specified_devices = (char **)malloc(sizeof(char *));
308
309         tmpstr = tmpstr1 = strdup(args);
310         cp = strchr(tmpstr1, '\n');
311         if (cp)
312                 *cp = '\0';
313         for (;;) {
314                 for (cp = tmpstr1; *cp && isspace(*cp); cp++)
315                         ;
316                 tmpstr1 = cp;
317                 for (; *cp && !isspace(*cp); cp++)
318                         ;
319                 if (*cp)
320                         *cp++ = '\0';
321                 if (cp - tmpstr1 == 0)
322                         break;
323                 for (i = 0; i < num_devices; i++) {
324                         asprintf(&buffer, "%s%d", dev_select[i].device_name,
325                                 dev_select[i].unit_number);
326                         if (strcmp(buffer, tmpstr1) == 0) {
327
328                                 num_devices_specified++;
329
330                                 specified_devices =(char **)realloc(
331                                                 specified_devices,
332                                                 sizeof(char *) *
333                                                 num_devices_specified);
334                                 specified_devices[num_devices_specified -1]=
335                                         strdup(tmpstr1);
336                                 free(buffer);
337
338                                 break;
339                         }
340                         else
341                                 free(buffer);
342                 }
343                 if (i >= num_devices)
344                         error("%s: unknown drive", args);
345                 tmpstr1 = cp;
346         }
347         free(tmpstr);
348
349         if (num_devices_specified > 0) {
350                 last_type = DS_MATCHTYPE_SPEC;
351
352                 retval = devstat_selectdevs(&dev_select, &num_selected,
353                     &num_selections, &select_generation, generation,
354                     s1->dinfo->devices, num_devices, NULL, 0,
355                     specified_devices, num_devices_specified,
356                     select_mode, maxshowdevs, 0);
357                 if (retval == -1)
358                         err(1, "%s", devstat_errbuf);
359                 else if (retval == 1)
360                         return(2);
361         }
362         return(1);
363 }
364
365
366 void
367 dslabel(int maxdrives, int diskcol, int diskrow)
368 {
369         int i, j;
370
371         mvprintw(diskrow, diskcol, "Disks");
372         mvprintw(diskrow + 1, diskcol, "KB/t");
373         mvprintw(diskrow + 2, diskcol, "tps");
374         mvprintw(diskrow + 3, diskcol, "MB/s");
375         mvprintw(diskrow + 4, diskcol, "%%busy");
376         /*
377          * For now, we don't support a fourth disk statistic.  So there's
378          * no point in providing a label for it.  If someone can think of a
379          * fourth useful disk statistic, there is room to add it.
380          */
381         /* mvprintw(diskrow + 4, diskcol, " msps"); */
382         j = 0;
383         for (i = 0; i < num_devices && j < maxdrives; i++)
384                 if (dev_select[i].selected) {
385                         char tmpstr[80];
386                         sprintf(tmpstr, "%s%d", dev_select[i].device_name,
387                                 dev_select[i].unit_number);
388                         mvprintw(diskrow, diskcol + 5 + 6 * j,
389                                 " %5.5s", tmpstr);
390                         j++;
391                 }
392 }
393
394 static void
395 dsshow2(int diskcol, int diskrow, int dn, int lc, struct statinfo *now, struct statinfo *then)
396 {
397         long double transfers_per_second;
398         long double kb_per_transfer, mb_per_second;
399         long double elapsed_time, device_busy;
400         int di;
401
402         di = dev_select[dn].position;
403
404         if (then != NULL) {
405                 /* Calculate relative to previous sample */
406                 elapsed_time = now->snap_time - then->snap_time;
407         } else {
408                 /* Calculate relative to device creation */
409                 elapsed_time = now->snap_time - devstat_compute_etime(
410                     &now->dinfo->devices[di].creation_time, NULL);
411         }
412
413         if (devstat_compute_statistics(&now->dinfo->devices[di], then ?
414             &then->dinfo->devices[di] : NULL, elapsed_time,
415             DSM_KB_PER_TRANSFER, &kb_per_transfer,
416             DSM_TRANSFERS_PER_SECOND, &transfers_per_second,
417             DSM_MB_PER_SECOND, &mb_per_second,
418             DSM_BUSY_PCT, &device_busy,
419             DSM_NONE) != 0)
420                 errx(1, "%s", devstat_errbuf);
421
422         lc = diskcol + lc * 6;
423         putlongdouble(kb_per_transfer, diskrow + 1, lc, 5, 2, 0);
424         putlongdouble(transfers_per_second, diskrow + 2, lc, 5, 0, 0);
425         putlongdouble(mb_per_second, diskrow + 3, lc, 5, 2, 0);
426         putlongdouble(device_busy, diskrow + 4, lc, 5, 0, 0);
427 }
428
429 static void
430 dsshow3(int diskcol, int diskrow, int dn, int lc, struct statinfo *now, struct statinfo *then)
431 {
432         dsshow2(diskcol, diskrow, dn, lc, now, then);
433 }
434
435 void
436 dsshow(int maxdrives, int diskcol, int diskrow, struct statinfo *now, struct statinfo *then)
437 {
438         int i, lc;
439
440         for (i = 0, lc = 0; i < num_devices && lc < maxdrives; i++)
441                 if (dev_select[i].selected)
442                         dsshow3(diskcol, diskrow, i, ++lc, now, then);
443 }