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
61
62 #include <sys/types.h>
63 #include <sys/devicestat.h>
64 #include <sys/resource.h>
65
66 #include <ctype.h>
67 #include <err.h>
68 #include <stdlib.h>
69 #include <string.h>
70
71 #include "systat.h"
72 #include "extern.h"
73 #include "devs.h"
74
75 typedef enum {
76 DS_MATCHTYPE_NONE,
77 DS_MATCHTYPE_SPEC,
78 DS_MATCHTYPE_PATTERN
79 } last_match_type;
80
81 struct statinfo cur_dev, last_dev, run_dev;
82
83 static last_match_type last_type;
84 struct device_selection *dev_select;
85 long generation;
86 int num_devices, num_selected;
87 int num_selections;
88 long select_generation;
89 static struct devstat_match *matches = NULL;
90 static int num_matches = 0;
91 static char **specified_devices;
92 static int num_devices_specified = 0;
93
94 static int dsmatchselect(const char *args, devstat_select_mode select_mode,
95 int maxshowdevs, struct statinfo *s1);
96 static int dsselect(const char *args, devstat_select_mode select_mode,
97 int maxshowdevs, struct statinfo *s1);
98
99 int
dsinit(int maxshowdevs)100 dsinit(int maxshowdevs)
101 {
102 /*
103 * Make sure that the userland devstat version matches the kernel
104 * devstat version. If not, exit and print a message informing
105 * the user of his mistake.
106 */
107 if (devstat_checkversion(NULL) < 0)
108 errx(1, "%s", devstat_errbuf);
109
110 if( cur_dev.dinfo ) // init was alreay ran
111 return(1);
112
113 if ((num_devices = devstat_getnumdevs(NULL)) < 0) {
114 warnx("%s", devstat_errbuf);
115 return(0);
116 }
117
118 cur_dev.dinfo = calloc(1, sizeof(struct devinfo));
119 last_dev.dinfo = calloc(1, sizeof(struct devinfo));
120 run_dev.dinfo = calloc(1, sizeof(struct devinfo));
121
122 generation = 0;
123 num_devices = 0;
124 num_selected = 0;
125 num_selections = 0;
126 select_generation = 0;
127 last_type = DS_MATCHTYPE_NONE;
128
129 if (devstat_getdevs(NULL, &cur_dev) == -1)
130 errx(1, "%s", devstat_errbuf);
131
132 num_devices = cur_dev.dinfo->numdevs;
133 generation = cur_dev.dinfo->generation;
134
135 dev_select = NULL;
136
137 /*
138 * At this point, selectdevs will almost surely indicate that the
139 * device list has changed, so we don't look for return values of 0
140 * or 1. If we get back -1, though, there is an error.
141 */
142 if (devstat_selectdevs(&dev_select, &num_selected, &num_selections,
143 &select_generation, generation, cur_dev.dinfo->devices, num_devices,
144 NULL, 0, NULL, 0, DS_SELECT_ADD, maxshowdevs, 0) == -1)
145 errx(1, "%d %s", __LINE__, devstat_errbuf);
146
147 return(1);
148 }
149
150
151 void
dsgetinfo(struct statinfo * dev)152 dsgetinfo(struct statinfo* dev)
153 {
154 switch (devstat_getdevs(NULL, dev)) {
155 case -1:
156 errx(1, "%s", devstat_errbuf);
157 break;
158 case 1:
159 num_devices = dev->dinfo->numdevs;
160 generation = dev->dinfo->generation;
161 cmdkre("refresh", NULL);
162 break;
163 default:
164 break;
165 }
166 }
167
168 int
dscmd(const char * cmd,const char * args,int maxshowdevs,struct statinfo * s1)169 dscmd(const char *cmd, const char *args, int maxshowdevs, struct statinfo *s1)
170 {
171 int retval;
172
173 if (prefix(cmd, "display") || prefix(cmd, "add"))
174 return(dsselect(args, DS_SELECT_ADDONLY, maxshowdevs, s1));
175 if (prefix(cmd, "ignore") || prefix(cmd, "delete"))
176 return(dsselect(args, DS_SELECT_REMOVE, maxshowdevs, s1));
177 if (prefix(cmd, "show") || prefix(cmd, "only"))
178 return(dsselect(args, DS_SELECT_ONLY, maxshowdevs, s1));
179 if (prefix(cmd, "type") || prefix(cmd, "match"))
180 return(dsmatchselect(args, DS_SELECT_ONLY, maxshowdevs, s1));
181 if (prefix(cmd, "refresh")) {
182 retval = devstat_selectdevs(&dev_select, &num_selected,
183 &num_selections, &select_generation, generation,
184 s1->dinfo->devices, num_devices,
185 (last_type ==DS_MATCHTYPE_PATTERN) ? matches : NULL,
186 (last_type ==DS_MATCHTYPE_PATTERN) ? num_matches : 0,
187 (last_type == DS_MATCHTYPE_SPEC) ?specified_devices : NULL,
188 (last_type == DS_MATCHTYPE_SPEC) ?num_devices_specified : 0,
189 (last_type == DS_MATCHTYPE_NONE) ? DS_SELECT_ADD :
190 DS_SELECT_ADDONLY, maxshowdevs, 0);
191 if (retval == -1) {
192 warnx("%s", devstat_errbuf);
193 return(0);
194 } else if (retval == 1)
195 return(2);
196 }
197 if (prefix(cmd, "drives")) {
198 int i;
199 move(CMDLINE, 0);
200 clrtoeol();
201 for (i = 0; i < num_devices; i++) {
202 printw("%s%d ", s1->dinfo->devices[i].device_name,
203 s1->dinfo->devices[i].unit_number);
204 }
205 return(1);
206 }
207 return(0);
208 }
209
210 static int
dsmatchselect(const char * args,devstat_select_mode select_mode,int maxshowdevs,struct statinfo * s1)211 dsmatchselect(const char *args, devstat_select_mode select_mode, int maxshowdevs,
212 struct statinfo *s1)
213 {
214 char **tempstr, *tmpstr, *tmpstr1;
215 char *tstr[100];
216 int num_args = 0;
217 int i;
218 int retval = 0;
219
220 if (!args) {
221 warnx("dsmatchselect: no arguments");
222 return(1);
223 }
224
225 /*
226 * Break the (pipe delimited) input string out into separate
227 * strings.
228 */
229 tmpstr = tmpstr1 = strdup(args);
230 for (tempstr = tstr, num_args = 0;
231 (*tempstr = strsep(&tmpstr1, "|")) != NULL && (num_args < 100);
232 num_args++)
233 if (**tempstr != '\0')
234 if (++tempstr >= &tstr[100])
235 break;
236 free(tmpstr);
237
238 if (num_args > 99) {
239 warnx("dsmatchselect: too many match arguments");
240 return(0);
241 }
242
243 /*
244 * If we've gone through the matching code before, clean out
245 * previously used memory.
246 */
247 if (num_matches > 0) {
248 free(matches);
249 matches = NULL;
250 num_matches = 0;
251 }
252
253 for (i = 0; i < num_args; i++) {
254 if (devstat_buildmatch(tstr[i], &matches, &num_matches) != 0) {
255 warnx("%s", devstat_errbuf);
256 return(0);
257 }
258 }
259 if (num_args > 0) {
260
261 last_type = DS_MATCHTYPE_PATTERN;
262
263 retval = devstat_selectdevs(&dev_select, &num_selected,
264 &num_selections, &select_generation, generation,
265 s1->dinfo->devices, num_devices, matches, num_matches,
266 NULL, 0, select_mode, maxshowdevs, 0);
267 if (retval == -1)
268 err(1, "device selection error");
269 else if (retval == 1)
270 return(2);
271 }
272 return(1);
273 }
274
275 static int
dsselect(const char * args,devstat_select_mode select_mode,int maxshowdevs,struct statinfo * s1)276 dsselect(const char *args, devstat_select_mode select_mode, int maxshowdevs,
277 struct statinfo *s1)
278 {
279 char *cp, *tmpstr, *tmpstr1, *buffer;
280 int i;
281 int retval = 0;
282
283 if (!args) {
284 warnx("dsselect: no argument");
285 return(1);
286 }
287
288 /*
289 * If we've gone through this code before, free previously
290 * allocated resources.
291 */
292 if (num_devices_specified > 0) {
293 for (i = 0; i < num_devices_specified; i++)
294 free(specified_devices[i]);
295 free(specified_devices);
296 specified_devices = NULL;
297 num_devices_specified = 0;
298 }
299
300 /* do an initial malloc */
301 specified_devices = (char **)malloc(sizeof(char *));
302
303 tmpstr = tmpstr1 = strdup(args);
304 cp = strchr(tmpstr1, '\n');
305 if (cp)
306 *cp = '\0';
307 for (;;) {
308 for (cp = tmpstr1; *cp && isspace(*cp); cp++)
309 ;
310 tmpstr1 = cp;
311 for (; *cp && !isspace(*cp); cp++)
312 ;
313 if (*cp)
314 *cp++ = '\0';
315 if (cp - tmpstr1 == 0)
316 break;
317 for (i = 0; i < num_devices; i++) {
318 asprintf(&buffer, "%s%d", dev_select[i].device_name,
319 dev_select[i].unit_number);
320 if (strcmp(buffer, tmpstr1) == 0) {
321
322 num_devices_specified++;
323
324 specified_devices =(char **)realloc(
325 specified_devices,
326 sizeof(char *) *
327 num_devices_specified);
328 specified_devices[num_devices_specified -1]=
329 strdup(tmpstr1);
330 free(buffer);
331
332 break;
333 }
334 else
335 free(buffer);
336 }
337 if (i >= num_devices)
338 error("%s: unknown drive", args);
339 tmpstr1 = cp;
340 }
341 free(tmpstr);
342
343 if (num_devices_specified > 0) {
344 last_type = DS_MATCHTYPE_SPEC;
345
346 retval = devstat_selectdevs(&dev_select, &num_selected,
347 &num_selections, &select_generation, generation,
348 s1->dinfo->devices, num_devices, NULL, 0,
349 specified_devices, num_devices_specified,
350 select_mode, maxshowdevs, 0);
351 if (retval == -1)
352 err(1, "%s", devstat_errbuf);
353 else if (retval == 1)
354 return(2);
355 }
356 return(1);
357 }
358
359
360 void
dslabel(int maxdrives,int diskcol,int diskrow)361 dslabel(int maxdrives, int diskcol, int diskrow)
362 {
363 int i, j;
364
365 mvprintw(diskrow, diskcol, "Disks");
366 mvprintw(diskrow + 1, diskcol, "KB/t");
367 mvprintw(diskrow + 2, diskcol, "tps");
368 mvprintw(diskrow + 3, diskcol, "MB/s");
369 mvprintw(diskrow + 4, diskcol, "%%busy");
370 /*
371 * For now, we don't support a fourth disk statistic. So there's
372 * no point in providing a label for it. If someone can think of a
373 * fourth useful disk statistic, there is room to add it.
374 */
375 /* mvprintw(diskrow + 4, diskcol, " msps"); */
376 j = 0;
377 for (i = 0; i < num_devices && j < maxdrives; i++)
378 if (dev_select[i].selected) {
379 char tmpstr[80];
380 sprintf(tmpstr, "%s%d", dev_select[i].device_name,
381 dev_select[i].unit_number);
382 mvprintw(diskrow, diskcol + 5 + 6 * j,
383 " %5.5s", tmpstr);
384 j++;
385 }
386 }
387
388 static void
dsshow2(int diskcol,int diskrow,int dn,int lc,struct statinfo * now,struct statinfo * then)389 dsshow2(int diskcol, int diskrow, int dn, int lc, struct statinfo *now, struct statinfo *then)
390 {
391 long double transfers_per_second;
392 long double kb_per_transfer, mb_per_second;
393 long double elapsed_time, device_busy;
394 int di;
395
396 di = dev_select[dn].position;
397
398 if (then != NULL) {
399 /* Calculate relative to previous sample */
400 elapsed_time = now->snap_time - then->snap_time;
401 } else {
402 /* Calculate relative to device creation */
403 elapsed_time = now->snap_time - devstat_compute_etime(
404 &now->dinfo->devices[di].creation_time, NULL);
405 }
406
407 if (devstat_compute_statistics(&now->dinfo->devices[di], then ?
408 &then->dinfo->devices[di] : NULL, elapsed_time,
409 DSM_KB_PER_TRANSFER, &kb_per_transfer,
410 DSM_TRANSFERS_PER_SECOND, &transfers_per_second,
411 DSM_MB_PER_SECOND, &mb_per_second,
412 DSM_BUSY_PCT, &device_busy,
413 DSM_NONE) != 0)
414 errx(1, "%s", devstat_errbuf);
415
416 lc = diskcol + lc * 6;
417 putlongdouble(kb_per_transfer, diskrow + 1, lc, 5, 2, 0);
418 putlongdouble(transfers_per_second, diskrow + 2, lc, 5, 0, 0);
419 putlongdouble(mb_per_second, diskrow + 3, lc, 5, 2, 0);
420 putlongdouble(device_busy, diskrow + 4, lc, 5, 0, 0);
421 }
422
423 void
dsshow(int maxdrives,int diskcol,int diskrow,struct statinfo * now,struct statinfo * then)424 dsshow(int maxdrives, int diskcol, int diskrow, struct statinfo *now, struct statinfo *then)
425 {
426 int i, lc;
427
428 for (i = 0, lc = 0; i < num_devices && lc < maxdrives; i++)
429 if (dev_select[i].selected)
430 dsshow2(diskcol, diskrow, i, ++lc, now, then);
431 }
432