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
75typedef enum {
76	DS_MATCHTYPE_NONE,
77	DS_MATCHTYPE_SPEC,
78	DS_MATCHTYPE_PATTERN
79} last_match_type;
80
81struct statinfo cur_dev, last_dev, run_dev;
82
83static last_match_type last_type;
84struct device_selection *dev_select;
85long generation;
86int num_devices, num_selected;
87int num_selections;
88long select_generation;
89static struct devstat_match *matches = NULL;
90static int num_matches = 0;
91static char **specified_devices;
92static int num_devices_specified = 0;
93
94static int dsmatchselect(const char *args, devstat_select_mode select_mode,
95			 int maxshowdevs, struct statinfo *s1);
96static int dsselect(const char *args, devstat_select_mode select_mode,
97		    int maxshowdevs, struct statinfo *s1);
98
99int
100dsinit(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
151void
152dsgetinfo(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
168int
169dscmd(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
210static int
211dsmatchselect(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
275static int
276dsselect(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
360void
361dslabel(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
388static void
389dsshow2(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
423void
424dsshow(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