xref: /freebsd/usr.bin/systat/devs.c (revision 02e9120893770924227138ba49df1edb3896112a)
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
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
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
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
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
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
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
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
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