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
65static 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
81typedef enum {
82	DS_MATCHTYPE_NONE,
83	DS_MATCHTYPE_SPEC,
84	DS_MATCHTYPE_PATTERN
85} last_match_type;
86
87struct statinfo cur_dev, last_dev, run_dev;
88
89last_match_type last_type;
90struct device_selection *dev_select;
91long generation;
92int num_devices, num_selected;
93int num_selections;
94long select_generation;
95struct devstat_match *matches = NULL;
96int num_matches = 0;
97char **specified_devices;
98int num_devices_specified = 0;
99
100static int dsmatchselect(const char *args, devstat_select_mode select_mode,
101			 int maxshowdevs, struct statinfo *s1);
102static int dsselect(const char *args, devstat_select_mode select_mode,
103		    int maxshowdevs, struct statinfo *s1);
104
105int
106dsinit(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
157void
158dsgetinfo(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
174int
175dscmd(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
216static int
217dsmatchselect(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
281static int
282dsselect(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
366void
367dslabel(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
394static void
395dsshow2(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
429static void
430dsshow3(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
435void
436dsshow(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}
444