1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <unistd.h>
30#include <errno.h>
31#include <inttypes.h>
32#include <locale.h>
33
34#include <kstat.h>
35
36#include "dsstat.h"
37#include "multi_stats.h"
38
39/* Globals */
40int mode = 0;
41int interval = 1;
42int iterations = 1;
43int zflag = 0;
44int linesout = 0;
45
46short hflags = HEADERS_EXL;
47short dflags = 0;
48short rflags = 0;
49vslist_t *vs_top = NULL;
50
51void
52errout(char *msg)
53{
54
55	(void) fprintf(stderr, msg);
56}
57
58void
59usage()
60{
61	errout(gettext(
62	    "\ndsstat [-m <mode>[,<mode>]] [-f | -F] [-z] [-s <sets>] "
63	    "[-r <flags>] \\\n[-d <flags>] [<interval> [<count>]]\n\n"));
64}
65
66void
67help()
68{
69	usage();
70
71	errout(gettext("\t"
72	    "-d <flags> Specifies the statistics to be displayed\n\n"));
73	errout(gettext("\t"
74	    "   For 'cache' mode\n"));
75	errout(gettext("\t"
76	    "      Valid <flags> are 'rwfsdc', default <flags> are 'sf'\n"));
77	errout(gettext("\t"
78	    "      r=read, w=write, f=flags, s=summary,\n"));
79	errout(gettext("\t"
80	    "   only available for cache mode, need to combine with '-m'\n"));
81	errout(gettext("\t"
82	    "      d=destaged, c=write cancellations\n\n"));
83	errout(gettext("\t"
84	    "   For 'ii' mode;\n"));
85	errout(gettext("\t"
86	    "      Valid <flags> are 'rwtfps', default <flags> are 'sf'\n"));
87	errout(gettext("\t"
88	    "      r=read, w=write, t=timing, f=flags, p=percentages,\n"));
89	errout(gettext("\t"
90	    "      s=summary\n\n"));
91	errout(gettext("\t"
92	    "   For 'sndr' mode;\n"));
93	errout(gettext("\t"
94	    "      Valid <flags> are'rwtfpsq', default <flags> are 'spf'\n"));
95	errout(gettext("\t"
96	    "      r=read, w=write, t=timing, f=flags, p=percentages,\n"));
97	errout(gettext("\t"
98	    "      s=summary\n"));
99	errout(gettext("\t"
100	    "   only available for sndr mode, need to combine with '-m'\n"));
101	errout(gettext("\t"
102	    "      q=queue\n\n"));
103	errout(gettext("\t"
104	    "-f  prints field headers once for each iteration\n\n"));
105	errout(gettext("\t"
106	    "-F  prints field headers once, at the start of reporting\n\n"));
107	errout(gettext("\t"
108	    "-h  prints detailed usage message\n\n"));
109	errout(gettext("\t"
110	    "-m <mode>[,<mode>] where mode is, 'cache', 'ii', or 'sndr'\n\n"));
111	errout(gettext("\t"
112	    "   Multiple modes may be specified as a comma separated list,\n"));
113	errout(gettext("\t"
114	    "   or multiple -m switches may be used.\n\n"));
115	errout(gettext("\t"
116	    "-r <flags> specifies components to be reported\n\n"));
117	errout(gettext("\t"
118	    "   For 'cache' mode, this option is not used.\n\n"));
119	errout(gettext("\t"
120	    "   For 'ii' mode;\n"));
121	errout(gettext("\t"
122	    "      Valid <flags> are 'msbo', default <flags> are 'msbo'\n"));
123	errout(gettext("\t"
124	    "      m=master, s=shadow, b=bitmap, o=overflow\n\n"));
125	errout(gettext("\t"
126	    "   For 'sndr' mode;\n"));
127	errout(gettext("\t"
128	    "      Valid <flags> are 'nb', default <flags> are 'nb'\n"));
129	errout(gettext("\t"
130	    "      n=network, b=bitmap\n\n"));
131	errout(gettext("\t"
132	    "-s <sets> outputs specified sets\n"));
133	errout(gettext("\t"
134	    "    Where <sets> is a comma delimited list of set names\n\n"));
135	errout(gettext("\t"
136	    "-z  suppress reports with zero value (no activity)\n\n"));
137	errout(gettext("\t"
138	    "<interval> is the number of seconds between reports\n\n"));
139	errout(gettext("\t"
140	    "<count> is the number of reports to be generated\n\n"));
141}
142
143void
144fail(int err, char *msg)
145{
146	errout(gettext("\ndsstat: "));
147	errout(msg);
148
149	usage();
150
151	errout(gettext("For detailed usage run \"dsstat -h\"\n"));
152
153	exit(err);
154}
155
156int
157set_mode(char *user_modes)
158{
159	char *m;
160	int local_mode = 0;
161
162	for (m = strtok(user_modes, ","); m != NULL; m = strtok(NULL, ",")) {
163		if (local_mode != 0) {
164			local_mode |= MULTI;
165		}
166
167		if (strncasecmp("sndr", m, strlen(m)) == 0) {
168			local_mode |= SNDR;
169			continue;
170		}
171
172		if (strncasecmp("ii", m, strlen(m)) == 0) {
173			local_mode |= IIMG;
174			continue;
175		}
176
177		if (strncasecmp("cache", m, strlen(m)) == 0) {
178			local_mode |= SDBC;
179			continue;
180		}
181
182		fail(DSSTAT_EINVAL, gettext("Invalid mode specified"));
183	}
184
185	return (local_mode);
186}
187
188short
189set_dflags(char *flags)
190{
191	int index;
192	short user_dflags = 0;
193
194	for (index = 0; index < strlen(flags); index++) {
195		switch (flags[index]) {
196			case 'r':
197				user_dflags |= READ;
198				break;
199			case 'w':
200				user_dflags |= WRITE;
201				break;
202			case 't':
203				user_dflags |= TIMING;
204				break;
205			case 'f':
206				user_dflags |= FLAGS;
207				break;
208			case 'p':
209				user_dflags |= PCTS;
210				break;
211			case 's':
212				user_dflags |= SUMMARY;
213				break;
214			case 'd':
215				user_dflags |= DESTAGED;
216				break;
217			case 'c':
218				user_dflags |= WRCANCEL;
219				break;
220			case 'h':
221				user_dflags |= RATIO;
222				break;
223			case 'q':
224				user_dflags |= ASYNC_QUEUE;
225				break;
226			default:
227				fail(DSSTAT_EINVAL,
228				    gettext("Invalid display-flags set\n"));
229		}
230	}
231
232	return (user_dflags);
233}
234
235short
236set_rflags(char *flags)
237{
238	int index;
239	short user_rflags = 0;
240
241	for (index = 0; index < strlen(flags); index++) {
242		switch (flags[index]) {
243			case 'm':
244				user_rflags |= IIMG_MST;
245				break;
246			case 's':
247				user_rflags |= IIMG_SHD;
248				break;
249			case 'b':
250				user_rflags |= IIMG_BMP;
251				user_rflags |= SNDR_BMP;
252				break;
253			case 'o':
254				user_rflags |= IIMG_OVR;
255				break;
256			case 'n':
257				user_rflags |= SNDR_NET;
258				break;
259			default:
260				fail(DSSTAT_EINVAL,
261				    gettext("Invalid report-flags set\n"));
262		}
263	}
264
265	return (user_rflags);
266}
267
268void
269set_vol_list(char *list)
270{
271	vslist_t *pre;
272	vslist_t *newvol;
273	vslist_t *vslist;
274	char *volume;
275
276	for (volume = strtok(list, ","); volume != NULL;
277	    volume = strtok(NULL, ",")) {
278		int dup = 0;
279		char *vh = NULL;
280		char *vn = NULL;
281
282		/* get user-specified set information */
283		if ((vn = strchr(volume, ':')) == NULL) {
284			vn = volume;
285		} else {
286			*vn = '\0';
287			vn++;
288			vh = volume;
289		}
290
291		/* check for duplicates */
292		dup = 0;
293
294		for (vslist = vs_top; vslist != NULL; vslist = vslist->next) {
295			if (vslist->volhost && vh) {
296				if (strcmp(vslist->volhost, vh) == 0 &&
297				    strcmp(vslist->volname, vn) == 0)
298					dup = 1;
299			} else {
300				if (strcmp(vslist->volname, vn) == 0)
301					dup = 1;
302			}
303
304			pre = vslist;
305		}
306
307		if (dup)
308			continue;
309
310		/* initialize new vslist record */
311		newvol = (vslist_t *)calloc(1, sizeof (vslist_t));
312
313		newvol->volname = (char *)calloc((strlen(vn) + 1),
314		    sizeof (char));
315		(void) strcpy(newvol->volname, vn);
316
317		if (vh == NULL)
318			goto save;
319
320		newvol->volhost = (char *)calloc((strlen(vh) + 1),
321		    sizeof (char));
322		(void) strcpy(newvol->volhost, vh);
323
324save:
325		/* save record */
326		if (vs_top == NULL) {
327			vslist = vs_top = newvol;
328			vslist->next = NULL;
329			continue;
330		}
331
332		if (vslist == NULL) {
333			vslist = pre->next = newvol;
334			vslist->next = NULL;
335			continue;
336		}
337	}
338}
339
340int
341main(int argc, char **argv)
342{
343	extern char *optarg;
344	extern int optind;
345
346	int c;
347	int error;
348	short user_dflags = 0;
349	short user_rflags = 0;
350
351	/* Parse command line */
352	while ((c = getopt(argc, argv, "d:fFhm:r:s:z")) != EOF) {
353		switch (c) {
354			case 'd':	/* what to display */
355				user_dflags = set_dflags(optarg);
356				break;
357			case 'f':
358				hflags = HEADERS_ATT;
359				break;
360			case 'F':
361				hflags = HEADERS_BOR;
362				break;
363			case 'h':		/* usage */
364				help();
365				exit(0);
366				break;
367			case 'm':		/* Mode */
368				mode |= set_mode(optarg);
369				break;
370			case 'r':		/* what to report on */
371				user_rflags = set_rflags(optarg);
372				break;
373			case 's':
374				set_vol_list(optarg);
375				break;
376			case 'z':
377				zflag = 1;
378				break;
379
380			default:
381				fail(DSSTAT_EINVAL,
382				    "Invalid argument specified\n");
383		}
384	}
385
386	/* Parse additional arguments */
387	if (optind < argc) {
388		if ((interval = atoi(argv[optind])) <= 0) {
389			fail(DSSTAT_EINVAL,
390			    gettext("Invalid interval specified.\n"));
391		} else {
392			iterations = -1;
393		}
394
395		optind++;
396
397		if (optind < argc) {
398			if ((iterations = atoi(argv[optind])) <= 0) {
399				fail(DSSTAT_EINVAL,
400				    gettext("Invalid count specified.\n"));
401			}
402		}
403
404		optind++;
405	}
406
407	if (optind < argc) {
408		fail(DSSTAT_EINVAL,
409		    gettext("Too many parameters specified.\n"));
410	}
411
412	if (mode == 0)
413		mode |= MULTI | IIMG | SNDR | SDBC;
414
415	/* Select statistics to gather */
416	if (mode & SNDR) {
417		if (! (mode & MULTI)) {
418			if (user_rflags & IIMG_BMP)
419				user_rflags ^= IIMG_BMP;
420
421			if ((user_dflags | SNDR_DIS_MASK) != SNDR_DIS_MASK) {
422				fail(DSSTAT_EINVAL, gettext("Invalid "
423				    "display-flags for RemoteMirror\n"));
424			}
425
426			if ((user_rflags | SNDR_REP_MASK) != SNDR_REP_MASK) {
427				fail(DSSTAT_EINVAL,
428				    gettext("Invalid report-flags for "
429				    "Remote Mirror\n"));
430			}
431		}
432
433		if ((mode & MULTI) && (user_dflags & ASYNC_QUEUE)) {
434			fail(DSSTAT_EINVAL, gettext("Remote Mirror async. queue"
435			    "statistics can not be displayed with mutiple "
436			    "modes."));
437		}
438
439		if (user_dflags)
440			dflags = user_dflags;
441		else
442			dflags |= (SUMMARY | PCTS | FLAGS | RATIO);
443
444		if (user_rflags)
445			rflags = user_rflags;
446		else
447			rflags |= (SNDR_NET | SNDR_BMP);
448	}
449
450	if (mode & IIMG) {
451		if (! (mode & MULTI)) {
452			if (user_rflags & SNDR_BMP)
453				user_rflags ^= SNDR_BMP;
454
455			if ((user_dflags | IIMG_DIS_MASK) != IIMG_DIS_MASK) {
456				fail(DSSTAT_EINVAL,
457				    gettext("Invalid display-flags for "
458				    "Point-in-Time Copy\n"));
459			}
460
461			if ((user_rflags | IIMG_REP_MASK) != IIMG_REP_MASK) {
462				fail(DSSTAT_EINVAL,
463				    gettext("Invalid report-flags for "
464				    "Point-in-Time Copy\n"));
465			}
466		}
467
468		if (user_dflags)
469			dflags = user_dflags;
470		else
471			dflags |= (SUMMARY | PCTS | FLAGS | RATIO);
472
473		if (user_rflags)
474			rflags = user_rflags;
475		else
476			rflags |= (IIMG_MST | IIMG_SHD | IIMG_BMP | IIMG_OVR);
477	}
478
479	if (mode & SDBC) {
480		if (! (mode & MULTI)) {
481			if ((user_dflags | CACHE_DIS_MASK) != CACHE_DIS_MASK) {
482				fail(DSSTAT_EINVAL, gettext("Invalid "
483				    "display-flags for CACHE\n"));
484			}
485
486			if ((user_rflags | CACHE_REP_MASK) != CACHE_REP_MASK) {
487				fail(DSSTAT_EINVAL, gettext("Invalid "
488				    "report-flags for CACHE\n"));
489			}
490		} else {
491		    if ((user_dflags & DESTAGED) || (user_dflags & WRCANCEL)) {
492			if (user_dflags & DESTAGED)
493			    fail(DSSTAT_EINVAL, gettext("Cache, destaged "
494				"statistics can not be displayed with mutiple "
495				"modes."));
496			else
497			    fail(DSSTAT_EINVAL, gettext("Cache, write "
498				"cancellations "
499				"statistics can not be displayed with mutiple "
500				"modes."));
501		    }
502		}
503
504		if (user_dflags)
505			dflags = user_dflags;
506		else
507			if (mode & MULTI)
508				dflags |= (SUMMARY);
509			else
510				dflags |= (SUMMARY | FLAGS);
511
512		if (user_rflags)
513			rflags = user_rflags;
514		else
515			rflags |= user_rflags;
516	}
517
518	error = do_stats();
519
520	if (error == EAGAIN) {
521		fail(DSSTAT_NOSTAT, gettext("No statistics available for the "
522		    "specified mode(s).\n"));
523	}
524
525	if (error == EINVAL) {
526		fail(DSSTAT_EINVAL,
527		    gettext("Invalid kstat format detected.\n"));
528	}
529
530	if (error == ENOMEM) {
531		fail(DSSTAT_ENOMEM,
532		    gettext("Unable to open kstat device for reading.\n"));
533	}
534
535	if (error == -1) {
536		if (execv("/usr/sbin/dsstat", argv) != 0) {
537			fail(DSSTAT_EMAP, gettext("Kstat is invalid.\n"));
538		}
539	}
540
541	if (error) {
542		fail(DSSTAT_EUNKNWN, gettext("An unknown error occured.\n"));
543	}
544
545	return (0);
546}
547