xref: /titanic_51/usr/src/cmd/avs/sdbc/scmadm.c (revision 6a72db4a7fa12c3e0d1c1cf91a07390739fa0fbf)
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 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Utility for cache configuration
29  */
30 #include <unistd.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <strings.h>
34 #include <locale.h>
35 #include <langinfo.h>
36 #include <libintl.h>
37 #include <time.h>
38 #include <sys/nsctl/sd_bcache.h>
39 #include <sys/wait.h>
40 #include <errno.h>
41 #include <signal.h>
42 #include <sys/types.h>
43 #include <fcntl.h>
44 #include <stropts.h>
45 #include <ctype.h>
46 #include <libgen.h>
47 
48 #include <sys/nsctl/sdbc_ioctl.h>
49 #include <sys/unistat/spcs_s.h>
50 #include <sys/unistat/spcs_s_u.h>
51 #include <sys/unistat/spcs_errors.h>
52 #include <nsctl.h>
53 
54 #include <sys/nsctl/cfg.h>
55 #define	STATS_PATH	"/usr/bin/sd_stats"
56 
57 #define	_SD_FNAME	/* bring in function names from sd_trace.h */
58 #include <sys/nsctl/sd_trace.h>
59 #include <sys/syslog.h>
60 
61 /*
62  * Since we no longer support nvram cards, the hints wrthru and nowrthru no
63  * longer serve any purpose, and the system will always be in wrthru mode.
64  * WRTHRU_HINTS, if defined still allows the setting and reporting of write
65  * hints.  This is defined by default on DEBUG builds.
66  */
67 #ifdef DEBUG
68 #define	WRTHRU_HINTS
69 #endif
70 
71 static int sdbc_max_devices = 0;
72 
73 static char alert_file[200]  = "/dev/console";
74 
75 /* Variables used to set up paramater block passed to kernel */
76 static _sd_cache_param_t	user_level_conf;
77 static int			myid;
78 
79 static int		nodes_configured = 0;
80 static int		minidsp = 0; /* Is it a sp10 */
81 static int		forced_wrthru = -1; /* 0 clear, 1 set,-1 as is */
82 static int		no_forced_wrthru = -1;
83 static short		node_defined[MAX_SD_NODES];
84 static short		nodes_conf[MAX_SD_NODES];
85 
86 #define	USAGELEN	1024
87 char stats_usage[USAGELEN+128];
88 char scmadmUsage[USAGELEN];
89 
90 static caddr_t progname;
91 
92 
93 /*
94  * Functions exported for fwcadm.
95  */
96 void enable_sdbc(void);
97 void disable_sdbc(void);
98 void sdbc_set_maxdev();
99 
100 static void buildusage(char *);
101 
102 void print_all_options(void);
103 void get_cd_all(void);
104 int toggle_flush(void);
105 static void sd_gather_alert_dumps();
106 static int get_cd(char *);
107 static int get_hint(char *, int *, int *);
108 static void check_and_set_mirrors(int, int);
109 static void print_hint(const uint_t, const int);
110 static char *get_device_name(char *arg);
111 static void get_version();
112 
113 extern struct tm *localtime_r(const time_t *, struct tm *);
114 
115 #define	PRINT_CACHE_SZ_ERR(sz) {\
116 	(void) fprintf(stderr, gettext("\n%s: desired cache size (%d) "\
117 	    "set to system max (%d)\n"), \
118 	    progname, (sz), MAX_CACHE_SIZE); \
119 	spcs_log("sdbc", NULL, \
120 		gettext("desired cache size (%d) "\
121 		    "set to system max (%d)\n"), \
122 		(sz), MAX_CACHE_SIZE); \
123 }
124 
125 void
126 sdbc_report_error(spcs_s_info_t *ustatus)
127 {
128 	if (*ustatus != NULL) {
129 		spcs_s_report(*ustatus, stderr);
130 		spcs_s_ufree(ustatus);
131 	} else
132 		(void) fprintf(stderr, "%s\n", strerror(errno));
133 }
134 
135 
136 /*
137  * Return the per-cd hints for a cd.
138  *
139  * Since the global (no)wrthru and NSC_NOCACHE hints take precedence
140  * over the per-cd hints, get them as well and OR the whole lot
141  * together.
142  */
143 static int
144 get_cd_hint(const int cd)
145 {
146 	spcs_s_info_t ustats;
147 	int nodehint, cdhint;
148 
149 	nodehint = SDBC_IOCTL(SDBC_GET_NODE_HINT, 0, 0, 0, 0, 0, &ustats);
150 	if (nodehint == SPCS_S_ERROR) {
151 		(void) fprintf(stderr,
152 		    gettext("%s: get system options failed\n"), progname);
153 		sdbc_report_error(&ustats);
154 		exit(1);
155 	}
156 
157 	cdhint = SDBC_IOCTL(SDBC_GET_CD_HINT, cd, 0, 0, 0, 0, &ustats);
158 	if (cdhint == SPCS_S_ERROR) {
159 		(void) fprintf(stderr,
160 		    gettext("%s: get cd(%d) hint failed\n"), progname, cd);
161 		sdbc_report_error(&ustats);
162 		exit(1);
163 	}
164 
165 #ifdef WRTHRU_HINTS
166 	nodehint &= (NSC_FORCED_WRTHRU | NSC_NO_FORCED_WRTHRU | NSC_NOCACHE);
167 #else
168 	nodehint &= (NSC_NOCACHE);
169 #endif
170 	if (nodehint) {
171 		/* set the top bit to mark it as a system override */
172 		nodehint |= 0x80000000;
173 	}
174 
175 	return (cdhint | nodehint);
176 }
177 
178 
179 
180 /*
181  * Check for a config.
182  *
183  * If no suitable config can be found, install the default config.
184  *
185  * Calling state:
186  *	libcfg locked (mode describes type of lock)
187  */
188 static void
189 convert_config(CFGFILE *cfg, CFGLOCK mode)
190 {
191 	char buf[CFG_MAX_BUF];
192 	char *default_cfg = "128 64";
193 
194 retry:
195 	if (cfg_get_cstring(cfg, "scm.set1", buf, sizeof (buf)) >= 0) {
196 		/* config exists, return */
197 		return;
198 	}
199 
200 	cfg_rewind(cfg, CFG_SEC_CONF);
201 
202 #ifdef DEBUG
203 	(void) printf(gettext("%s: installing default config entry '%s'\n"),
204 	    progname, default_cfg);
205 #endif
206 	if (mode != CFG_WRLOCK) {
207 		cfg_unlock(cfg);
208 		if (!cfg_lock(cfg, CFG_WRLOCK)) {
209 			(void) fprintf(stderr,
210 			    gettext("%s: unable to lock configuration: %s\n"),
211 			    progname, cfg_error(NULL));
212 			exit(1);
213 		}
214 		mode = CFG_WRLOCK;
215 #ifdef DEBUG
216 		(void) printf(gettext("%s: upgraded lock, retrying\n"),
217 		    progname);
218 #endif
219 		goto retry;
220 	}
221 
222 	if (cfg_put_cstring(cfg, "scm", default_cfg, strlen(default_cfg)) < 0) {
223 		(void) fprintf(stderr,
224 		    gettext("%s: unable to write configuration: %s\n"),
225 		    progname, cfg_error(NULL));
226 		exit(1);
227 	}
228 
229 	if (!cfg_commit(cfg)) {
230 		(void) fprintf(stderr,
231 		    gettext("%s: unable to write to configuration: %s\n"),
232 		    progname, cfg_error(NULL));
233 	}
234 
235 	if (mode != CFG_WRLOCK) {
236 		if (!cfg_lock(cfg, mode)) {
237 			(void) fprintf(stderr,
238 			    gettext("%s: unable to relock configuration: %s\n"),
239 			    progname, cfg_error(NULL));
240 			exit(1);
241 		}
242 	}
243 
244 	cfg_rewind(cfg, CFG_SEC_CONF);
245 }
246 
247 
248 static int
249 iscluster(void)
250 {
251 	int rc;
252 
253 	rc = cfg_iscluster();
254 	if (rc == 0) {
255 		return (FALSE);
256 	} else if (rc > 0) {
257 		return (TRUE);
258 	} else {
259 		(void) fprintf(stderr,
260 		    gettext("%s: unable to ascertain environment\n"), progname);
261 		exit(1);
262 	}
263 
264 	/* NOTREACHED */
265 }
266 
267 
268 static void
269 restore_hints()
270 {
271 	CFGFILE *cfg;
272 	char key[CFG_MAX_KEY], buf[CFG_MAX_BUF];
273 	int setnumber;
274 	spcs_s_info_t ustatus;
275 	int cd;
276 
277 	if ((cfg = cfg_open(NULL)) == NULL) {
278 		(void) fprintf(stderr,
279 		    gettext("%s: unable to access configuration: %s\n"),
280 		    progname, cfg_error(NULL));
281 		exit(1);
282 	}
283 	if (!cfg_lock(cfg, CFG_RDLOCK)) {
284 		(void) fprintf(stderr,
285 		    gettext("%s: unable to lock configuration: %s\n"),
286 		    progname, cfg_error(NULL));
287 		exit(1);
288 	}
289 
290 	for (setnumber = 1; /*CONSTCOND*/ TRUE; setnumber++) {
291 		(void) snprintf(key, sizeof (key), "cache_hint.set%d.device",
292 		    setnumber);
293 		if (cfg_get_cstring(cfg,  key,  buf, sizeof (buf)) < 0) {
294 			/* error or not found */
295 			break;
296 		}
297 
298 		if (strcmp(buf, "system") == 0) {
299 			cd = -1;
300 		} else {
301 			cd = get_cd(buf);
302 			if (cd < 0)
303 				continue;
304 		}
305 
306 		(void) snprintf(key, sizeof (key), "cache_hint.set%d.wrthru",
307 		    setnumber);
308 		if (cfg_get_cstring(cfg,  key,  buf, sizeof (buf)) < 0)
309 			continue;
310 
311 		if (atoi(buf) == 1) {
312 			if (cd == -1) {
313 				/* Node hint */
314 				if (SDBC_IOCTL(SDBC_SET_NODE_HINT, NSC_WRTHRU,
315 				    1, 0, 0, 0, &ustatus) == SPCS_S_ERROR) {
316 					(void) fprintf(stderr,
317 					    gettext("%s: set system "
318 					    "option failed\n"),
319 					    progname);
320 					sdbc_report_error(&ustatus);
321 					exit(1);
322 				}
323 			} else if (SDBC_IOCTL(SDBC_SET_CD_HINT, cd,
324 			    NSC_WRTHRU, 1, 0, 0, &ustatus) == SPCS_S_ERROR) {
325 				(void) fprintf(stderr,
326 				    gettext("%s: set option failed\n"),
327 				    progname);
328 				sdbc_report_error(&ustatus);
329 				exit(1);
330 			}
331 		}
332 
333 		(void) snprintf(key, sizeof (key), "cache_hint.set%d.nordcache",
334 		    setnumber);
335 		if (cfg_get_cstring(cfg,  key,  buf, sizeof (buf)) < 0)
336 			continue;
337 
338 		if (atoi(buf) == 1) {
339 			if (cd == -1) {
340 				/* Node hint */
341 				if (SDBC_IOCTL(SDBC_SET_NODE_HINT, NSC_NOCACHE,
342 				    1, 0, 0, 0, &ustatus) == SPCS_S_ERROR) {
343 					(void) fprintf(stderr,
344 					    gettext("%s: set system "
345 					    "option failed\n"),
346 					    progname);
347 					sdbc_report_error(&ustatus);
348 					exit(1);
349 				}
350 			} else if (SDBC_IOCTL(SDBC_SET_CD_HINT, cd, NSC_NOCACHE,
351 			    1, 0, 0, &ustatus) == SPCS_S_ERROR) {
352 				(void) fprintf(stderr,
353 				    gettext("%s: set option failed\n"),
354 				    progname);
355 				sdbc_report_error(&ustatus);
356 				exit(1);
357 			}
358 		}
359 	}
360 
361 	cfg_close(cfg);
362 }
363 
364 void
365 sdbc_set_maxdev()
366 {
367 	spcs_s_info_t ustats;
368 
369 	if (SDBC_IOCTL(SDBC_MAXFILES, &sdbc_max_devices,
370 	    0, 0, 0, 0, &ustats) == SPCS_S_ERROR) {
371 		(void) fprintf(stderr, gettext("%s: get maxfiles failed\n"),
372 		    progname);
373 		sdbc_report_error(&ustats);
374 		exit(1);
375 	}
376 }
377 
378 static void
379 bitmapfs_print(void)
380 {
381 	CFGFILE *cfg;
382 	char key[CFG_MAX_KEY], buf[CFG_MAX_BUF];
383 	int setnumber;
384 
385 	cfg = cfg_open(NULL);
386 	if (cfg == NULL) {
387 		(void) fprintf(stderr,
388 		    gettext("%s: unable to access configuration: %s\n"),
389 		    progname, cfg_error(NULL));
390 		exit(1);
391 	}
392 
393 	if (!cfg_lock(cfg, CFG_RDLOCK)) {
394 		(void) fprintf(stderr,
395 		    gettext("%s: unable to lock configuration: %s\n"),
396 		    progname, cfg_error(NULL));
397 		exit(1);
398 	}
399 
400 	for (setnumber = 1; /*CSTYLED*/; setnumber++) {
401 		(void) snprintf(key, sizeof (key),
402 		    "bitmaps.set%d.bitmap", setnumber);
403 		buf[0] = 0;
404 
405 		if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) {
406 			if (errno == ESRCH) {
407 				/* end of list */
408 				break;
409 			}
410 
411 			(void) fprintf(stderr,
412 			    gettext("%s: error reading configuration: %s\n"),
413 			    progname, cfg_error(NULL));
414 			exit(1);
415 		}
416 
417 		(void) printf("%s\n", buf);
418 	}
419 
420 	cfg_close(cfg);
421 }
422 
423 
424 static void
425 bitmapfs_delete(char *bitmapfs)
426 {
427 	CFGFILE *cfg;
428 	char key[CFG_MAX_KEY], buf[CFG_MAX_BUF];
429 	int setnumber;
430 	int commit = 0;
431 
432 	cfg = cfg_open(NULL);
433 	if (cfg == NULL) {
434 		(void) fprintf(stderr,
435 		    gettext("%s: unable to access configuration: %s\n"),
436 		    progname, cfg_error(NULL));
437 		exit(1);
438 	}
439 
440 	if (!cfg_lock(cfg, CFG_WRLOCK)) {
441 		(void) fprintf(stderr,
442 		    gettext("%s: unable to lock configuration: %s\n"),
443 		    progname, cfg_error(NULL));
444 		exit(1);
445 	}
446 
447 	for (setnumber = 1; /*CSTYLED*/; setnumber++) {
448 		(void) snprintf(key, sizeof (key),
449 		    "bitmaps.set%d.bitmap", setnumber);
450 		buf[0] = 0;
451 
452 		if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) {
453 			if (errno == ESRCH) {
454 				/* end of list */
455 				(void) fprintf(stderr,
456 				    gettext("%s: %s not found "
457 				    "in configuration\n"),
458 				    progname, bitmapfs);
459 				break;
460 			}
461 
462 			(void) fprintf(stderr,
463 			    gettext("%s: error reading configuration: %s\n"),
464 			    progname, cfg_error(NULL));
465 			exit(1);
466 		}
467 
468 		if (strcmp(bitmapfs, buf) == 0) {
469 			(void) snprintf(key, sizeof (key),
470 			    "bitmaps.set%d", setnumber);
471 
472 			if (cfg_put_cstring(cfg, key, (char *)NULL, 0) < 0) {
473 				(void) fprintf(stderr,
474 				    gettext("%s: unable to delete %s "
475 				    "from configuration: %s\n"),
476 				    progname, bitmapfs, cfg_error(NULL));
477 			} else
478 				commit++;
479 
480 			break;
481 		}
482 	}
483 
484 	if (commit) {
485 		if (!cfg_commit(cfg)) {
486 			(void) fprintf(stderr,
487 			    gettext("%s: unable to write "
488 			    "to configuration: %s\n"),
489 			    progname, cfg_error(NULL));
490 		}
491 		commit = 0;
492 	}
493 
494 	cfg_close(cfg);
495 }
496 
497 
498 /*
499  * User visible configuration.
500  */
501 
502 static const struct {
503 	const char *tag;	/* libcfg tag */
504 	const char *name;	/* user presented name */
505 	const char *help;	/* explanation string */
506 } sdbc_cfg_options[] = {
507 	{ "thread", "nthreads", "number of threads" },
508 	{ "size", "cache_size", "total cache size" },
509 #ifdef DEBUG
510 	{ "write_cache", "write_cache_size", "write cache size" },
511 	{ "fill_pattern", "fill_pattern", "debug fill pattern" },
512 	{ "reserved1", "reserved1", "unavailable, do not use" },
513 	{ "iobuf", "niobuf", "number of io buffers" },
514 	{ "tdemons", "ntdeamons", "number of sd_test daemons" },
515 	{ "forced_wrthru", "forced_wrthru", "override wrthru detection" },
516 	{ "no_forced_wrthru", "no_forced_wrthru", "override wrthru"},
517 #endif
518 	{ NULL }
519 };
520 
521 
522 static int
523 configure_sdbc(int argc, char *argv[], int optind)
524 {
525 	CFGFILE *cfg;
526 	char key[CFG_MAX_KEY], buf[CFG_MAX_BUF];
527 	char *cp, option[CFG_MAX_BUF], value[CFG_MAX_BUF];
528 	const int opt_width = 20;
529 	int error, found, commit;
530 	int i;
531 
532 	error = commit = 0;
533 
534 	cfg = cfg_open(NULL);
535 	if (cfg == NULL) {
536 		(void) fprintf(stderr, "%s: unable to open configuration: %s",
537 		    progname, cfg_error(NULL));
538 		return (1);
539 	}
540 
541 	if (argc == optind) {
542 		/* display current user visible config */
543 
544 		if (!cfg_lock(cfg, CFG_RDLOCK)) {
545 			(void) fprintf(stderr,
546 			    gettext("%s: unable to lock configuration: %s\n"),
547 			    progname, cfg_error(NULL));
548 			error = 1;
549 			goto out;
550 		}
551 
552 		convert_config(cfg, CFG_RDLOCK);
553 
554 		for (i = 0; sdbc_cfg_options[i].tag != NULL; i++) {
555 			(void) snprintf(key, sizeof (key),
556 			    "scm.set1.%s", sdbc_cfg_options[i].tag);
557 			if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) {
558 				if (errno == ESRCH) {
559 					/* not found */
560 					(void) strcpy(buf, "");
561 				} else {
562 					(void) fprintf(stderr,
563 					    gettext("%s: error reading "
564 					    "configuration: %s\n"),
565 					    progname, cfg_error(NULL));
566 					error = 1;
567 					goto out;
568 				}
569 			}
570 
571 			(void) printf("%-*s: %-*s /* %s */\n",
572 			    opt_width, sdbc_cfg_options[i].name,
573 			    opt_width, buf, sdbc_cfg_options[i].help);
574 		}
575 	} else {
576 		if (!cfg_lock(cfg, CFG_WRLOCK)) {
577 			(void) fprintf(stderr,
578 			    gettext("%s: unable to lock configuration: %s\n"),
579 			    progname, cfg_error(NULL));
580 			error = 1;
581 			goto out;
582 		}
583 
584 		convert_config(cfg, CFG_WRLOCK);
585 
586 		for (/*CSTYLED*/; optind < argc; optind++) {
587 			(void) strncpy(option, argv[optind], sizeof (option));
588 			option[sizeof (option) - 1] = '\0';	/* terminate */
589 
590 			cp = strchr(option, '=');
591 			if (cp != NULL) {
592 				*cp = '\0';	/* terminate option */
593 				cp++;
594 				(void) strncpy(value, cp, sizeof (value));
595 				value[sizeof (value) - 1] = '\0';
596 
597 				if (*value == '\0')
598 					(void) strncpy(value, "-",
599 					    sizeof (value));
600 			}
601 
602 			found = 0;
603 			for (i = 0; sdbc_cfg_options[i].tag != NULL; i++) {
604 				if (strcmp(option,
605 				    sdbc_cfg_options[i].name) == 0) {
606 					found = 1;
607 					break;
608 				}
609 			}
610 
611 			if (!found) {
612 				(void) fprintf(stderr,
613 				    gettext("%s: unknown configuration "
614 				    "parameter: %s\n"), progname, option);
615 				continue;
616 			}
617 
618 			(void) snprintf(key, sizeof (key),
619 			    "scm.set1.%s", sdbc_cfg_options[i].tag);
620 			if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) {
621 				(void) fprintf(stderr,
622 				    gettext("%s: error reading "
623 				    "configuration: %s\n"),
624 				    progname, cfg_error(NULL));
625 				error = 1;
626 				goto out;
627 			}
628 
629 			if (*buf == '\0')
630 				(void) strncpy(buf, "<default>", sizeof (buf));
631 
632 			if (cp != NULL) {
633 				char *tmp;
634 				long val;
635 				/* set to new value */
636 
637 				if (strcmp(value, "-")) { /* default ? */
638 
639 					val = strtol(value, &tmp, 0);
640 					if (strcmp(value, tmp) == 0) {
641 						(void) fprintf(stderr,
642 						    gettext(
643 						    "%s: bad value (%s) "
644 						    "for option %s\n"),
645 						    progname, value, option);
646 						error = 1;
647 						goto out;
648 					}
649 
650 					/* make sure cache size is valid */
651 					if (strcmp(key, "scm.set1.size") == 0) {
652 						if (val > MAX_CACHE_SIZE) {
653 							PRINT_CACHE_SZ_ERR(val);
654 
655 							/*
656 							 * Overwrite the
657 							 * cache size with
658 							 * the maximum cache
659 							 * size.
660 							 */
661 							(void) snprintf(value,
662 							    sizeof (value),
663 							    "%ld",
664 							    (long)
665 							    MAX_CACHE_SIZE);
666 						}
667 					}
668 				}
669 
670 				if (cfg_put_cstring(cfg, key, value,
671 				    strlen(value)) < 0) {
672 					(void) fprintf(stderr,
673 					    gettext("\n%s: error writing "
674 					    "configuration: %s\n"),
675 					    progname, cfg_error(NULL));
676 					error = 1;
677 					goto out;
678 				}
679 
680 				(void) snprintf(buf, sizeof (buf),
681 				    "%s = %s", buf,
682 				    (strcmp(value, "-") == 0) ?
683 				    "<default>" : value);
684 
685 				commit = 1;
686 			}
687 
688 			(void) printf("%-*s: %-*s /* %s */\n",
689 			    opt_width, sdbc_cfg_options[i].name,
690 			    opt_width, buf, sdbc_cfg_options[i].help);
691 		} /* end command line args */
692 	}
693 
694 out:
695 	if (commit) {
696 		if (!cfg_commit(cfg)) {
697 			(void) fprintf(stderr,
698 			    gettext("%s: unable to write "
699 			    "to configuration: %s\n"),
700 			    progname, cfg_error(NULL));
701 		}
702 		commit = 0;
703 
704 		(void) printf("\n%s\n",
705 		    gettext("Changed configuration parameters "
706 		    "will take effect when the cache is restarted"));
707 	}
708 
709 	cfg_close(cfg);
710 	return (error);
711 }
712 
713 
714 static char *
715 cd_to_device(int cd)
716 {
717 	static _sd_stats_t *cs_cur = NULL;
718 	spcs_s_info_t ustatus;
719 
720 	if (cs_cur == NULL) {
721 		cs_cur = malloc(sizeof (_sd_stats_t) +
722 		    (sdbc_max_devices - 1) * sizeof (_sd_shared_t));
723 
724 		if (cs_cur == NULL) {
725 			(void) fprintf(stderr, gettext("%s malloc: %s\n"),
726 			    progname, strerror(errno));
727 			exit(1);
728 		}
729 	}
730 
731 	if (SDBC_IOCTL(SDBC_STATS, cs_cur, 0, 0, 0, 0,
732 	    &ustatus) == SPCS_S_ERROR) {
733 		(void) fprintf(stderr,
734 		    gettext("%s: stats ioctl failed\n"), progname);
735 		sdbc_report_error(&ustatus);
736 		exit(1);
737 	}
738 	if (cs_cur->st_cachesize == 0 || cd >= cs_cur->st_count)
739 		return ("");
740 
741 	return (cs_cur->st_shared[cd].sh_filename);
742 }
743 
744 /*
745  * takes either either a string containing the cd or the device name, and
746  * returns the device name.
747  */
748 static char *
749 get_device_name(char *arg)
750 {
751 	long cd = 0;
752 	char *device;
753 
754 	/* if the arg has a leading '/', assume it's a valid device name */
755 	if (!arg || *arg == '/') {
756 		return (arg);
757 	}
758 
759 	/* treat the "all" keyword as a valid device name */
760 	if (strcmp(arg, "all") == 0) {
761 		return (arg);
762 	}
763 
764 	/*
765 	 * Next, assume it's a cd, and try to convert it to an integer, and
766 	 * subsequently convert that cd to its corresponding device name.
767 	 *
768 	 * Since strtol returns 0 on failure, we need to make a special case
769 	 * for a cd of "0", which is valid.
770 	 */
771 	if (((cd = strtol(arg, (char **)NULL, 10)) > 0) ||
772 	    strcmp(arg, "0") == 0) {
773 		device = cd_to_device((int)cd);
774 
775 		/* cd_to_device returns NULL or "" on failure--check both */
776 		if (device && (strcmp(device, ""))) {
777 			/* it seems to be a valid device name */
778 			return (device);
779 		}
780 	}
781 
782 	return (NULL);
783 }
784 
785 static void
786 remove_hint(char *device)
787 {
788 	CFGFILE *cfg;
789 	char key[CFG_MAX_KEY], buf[CFG_MAX_BUF];
790 	int setnumber;
791 	int rc;
792 
793 	if ((cfg = cfg_open(NULL)) == NULL) {
794 		(void) fprintf(stderr,
795 		    gettext("%s: unable to access configuration: %s\n"),
796 		    progname, cfg_error(NULL));
797 		exit(1);
798 	}
799 	if (!cfg_lock(cfg, CFG_WRLOCK)) {
800 		(void) fprintf(stderr,
801 		    gettext("%s: unable to lock configuration: %s\n"),
802 		    progname, cfg_error(NULL));
803 		exit(1);
804 	}
805 
806 	for (setnumber = 1; /*CONSTCOND*/ TRUE; setnumber++) {
807 		(void) snprintf(key, sizeof (key), "cache_hint.set%d.device",
808 		    setnumber);
809 		if (cfg_get_cstring(cfg,  key,  buf, sizeof (buf)) < 0) {
810 			/* error or not found */
811 			break;
812 		}
813 
814 		if (strcmp(device, buf) != 0)
815 			continue;
816 
817 		/* remove config file entry */
818 		(void) snprintf(key, sizeof (key),
819 		    "cache_hint.set%d", setnumber);
820 		rc = cfg_put_cstring(cfg, key, NULL, 0);
821 		if (rc < 0)
822 			(void) fprintf(stderr,
823 			    gettext("%s: unable to update configuration "
824 			    "storage: %s"),
825 			    progname, cfg_error(NULL));
826 		else if (!cfg_commit(cfg))
827 			(void) fprintf(stderr,
828 			    gettext("%s: unable to update configuration "
829 			    "storage: %s"),
830 			    progname, cfg_error(NULL));
831 		else
832 			(void) fprintf(stderr,
833 			    gettext("%s: persistent hint for %s"
834 			    " removed from configuration\n"),
835 			    progname, device);
836 		break;
837 	}
838 	cfg_close(cfg);
839 }
840 
841 
842 static void
843 save_hint(int cd, int hint, int flag)
844 {
845 	char device[NSC_MAXPATH];
846 	CFGFILE *cfg;
847 	char key[CFG_MAX_KEY], buf[CFG_MAX_BUF];
848 	int setnumber;
849 	int found;
850 	int rc;
851 
852 	if (hint != NSC_WRTHRU && hint != NSC_NOCACHE)
853 		return;
854 
855 	if (flag != 0 && flag != 1)
856 		return;
857 
858 	if ((cfg = cfg_open(NULL)) == NULL) {
859 		(void) fprintf(stderr,
860 		    gettext("%s: unable to access configuration: %s\n"),
861 		    progname, cfg_error(NULL));
862 		exit(1);
863 	}
864 	if (!cfg_lock(cfg, CFG_WRLOCK)) {
865 		(void) fprintf(stderr,
866 		    gettext("%s: unable to lock configuration: %s\n"),
867 		    progname, cfg_error(NULL));
868 		exit(1);
869 	}
870 
871 	if (cd == -1)
872 		(void) strcpy(device, "system");
873 	else
874 		(void) strncpy(device, cd_to_device(cd), NSC_MAXPATH);
875 
876 	found = 0;
877 	for (setnumber = 1; /*CONSTCOND*/ TRUE; setnumber++) {
878 		(void) snprintf(key, sizeof (key), "cache_hint.set%d.device",
879 		    setnumber);
880 		if (cfg_get_cstring(cfg,  key,  buf, sizeof (buf)) < 0) {
881 			/* error or not found */
882 			break;
883 		}
884 
885 		if (strcmp(device, buf) == 0) {
886 			found = 1;
887 			break;
888 		}
889 	}
890 
891 	if (found) {
892 		if (hint == NSC_WRTHRU)
893 			(void) snprintf(key, sizeof (key),
894 			    "cache_hint.set%d.wrthru", setnumber);
895 		else /* NSC_NOCACHE */
896 			(void) snprintf(key, sizeof (key),
897 			    "cache_hint.set%d.nordcache", setnumber);
898 		if (flag == 0)
899 			rc = cfg_put_cstring(cfg, key, "0", 1);
900 		else
901 			rc = cfg_put_cstring(cfg, key, "1", 1);
902 	} else {
903 		(void) strncpy(buf, device, CFG_MAX_BUF);
904 		if (flag == 0)
905 			(void) strncat(buf, " 0 0", CFG_MAX_BUF);
906 		else if (hint == NSC_WRTHRU)
907 			(void) strncat(buf, " 1 0", CFG_MAX_BUF);
908 		else /* NSC_NOCACHE */
909 			(void) strncat(buf, " 0 1", CFG_MAX_BUF);
910 		rc = cfg_put_cstring(cfg, "cache_hint", buf, sizeof (buf));
911 	}
912 
913 	if (rc < 0)
914 		(void) fprintf(stderr,
915 		    gettext("%s: unable to update configuration storage: %s"),
916 		    progname, cfg_error(NULL));
917 	else if (!cfg_commit(cfg))
918 		(void) fprintf(stderr,
919 		    gettext("%s: unable to update configuration storage: %s"),
920 		    progname, cfg_error(NULL));
921 	cfg_close(cfg);
922 }
923 
924 #ifdef lint
925 int
926 scmadm_lintmain(int argc, char *argv[])
927 #else
928 int
929 main(int argc, char *argv[])
930 #endif
931 {
932 	int o = 0;
933 	int c;
934 	int errflg = 0;
935 	int hflag = 0;
936 	int qflag = 1;
937 	extern int optind;
938 	extern char *optarg;
939 	int cd;
940 	int hint;
941 	int flag;
942 	int optflag = 0;
943 	spcs_s_info_t ustats;
944 	int Dopt, Lopt;
945 	int Oopt = 0;
946 	char *bitmapfs = NULL;
947 	const char *exclusive = gettext(
948 	    "-d, -e, -m, -o, -C, -D, -L, and -v "
949 	    "are mutually exclusive\n");
950 
951 	(void) setlocale(LC_ALL, "");
952 	(void) textdomain("scm");
953 
954 	progname = strdup(basename(argv[0]));
955 
956 	sdbc_set_maxdev();
957 
958 	buildusage(progname);
959 
960 	Dopt = Lopt = 0;
961 
962 	while ((c = getopt(argc, argv,
963 #ifdef DEBUG
964 	    "gi:t:S"
965 #endif
966 	    "CD:LOa:devqhm:o:")) != EOF) {
967 
968 		switch (c) {
969 
970 		case 'D':
971 			if (optflag) {
972 				(void) fprintf(stderr, exclusive);
973 				goto usage;
974 			}
975 
976 			Dopt++;
977 			optflag++;
978 			bitmapfs = optarg;
979 			break;
980 
981 		case 'L':
982 			if (optflag) {
983 				(void) fprintf(stderr, exclusive);
984 				goto usage;
985 			}
986 
987 			Lopt++;
988 			optflag++;
989 			break;
990 
991 #ifdef DEBUG
992 		case 'S':
993 			if (optflag) {
994 				(void) fprintf(stderr, exclusive);
995 				goto usage;
996 			}
997 
998 			if (putenv(stats_usage) != 0) {
999 				(void) fprintf(stderr,
1000 				    gettext("%s: unable to putenv()\n"),
1001 				    progname);
1002 				exit(1);
1003 			}
1004 
1005 			argv[1] = "scmadm";
1006 			if (execv(STATS_PATH, &argv[1]) == -1) {
1007 				(void) fprintf(stderr,
1008 				    gettext("%s: failed to execute " STATS_PATH
1009 					"\n"), progname);
1010 				(void) fprintf(stderr,
1011 				    gettext("Please be sure to copy sd_stats"
1012 					" from src/cmd/ns/sdbc in a development"
1013 					" workspace\n"));
1014 			}
1015 			exit(0);
1016 			break;
1017 #endif
1018 		case 'a':
1019 			(void) strcpy(alert_file, optarg);
1020 			break;
1021 		case 'q':
1022 			qflag++;
1023 			break;
1024 		case 'O': /* restore hints */
1025 			Oopt++;
1026 			break;
1027 		case 'C': /* configure */
1028 		case 'e': /* enable */
1029 		case 'd': /* disable */
1030 		case 'v': /* get version */
1031 		case 'o': /* get/set options */
1032 		case 'm': /* get cd map */
1033 #ifdef DEBUG
1034 		case 't': /* trace */
1035 		case 'i': /* inject_ioerr */
1036 		case 'c': /* clear_ioerr */
1037 		case 'g': /* toggle_flush */
1038 #endif
1039 			if (optflag) {
1040 				(void) fprintf(stderr,
1041 #ifdef DEBUG
1042 				    "%s%s", gettext("-t, -i, -c, -g, "),
1043 #endif
1044 				    exclusive);
1045 
1046 				errflg++;
1047 			}
1048 			optflag++;
1049 			o = c;
1050 			break;
1051 		case 'h':
1052 			hflag = 1;
1053 			break;
1054 		case '?':
1055 		default:
1056 			errflg++;
1057 			break;
1058 		}
1059 		if (errflg || hflag)
1060 			goto usage;
1061 	}
1062 
1063 	if (Oopt) {
1064 		/* Set hints saved in persistent configuration */
1065 		restore_hints();
1066 		exit(0);
1067 	}
1068 	if (Dopt || Lopt) {
1069 		/* bitmapfs control */
1070 
1071 		if (iscluster()) {
1072 			(void) fprintf(stderr,
1073 			    gettext("%s: bitmap filesystems are not "
1074 			    "allowed in a cluster\n"), progname);
1075 			goto usage;
1076 		}
1077 
1078 		if ((Dopt + Lopt) > 1) {
1079 			(void) fprintf(stderr, gettext("-D and -L are"
1080 			    "mutually exclusive\n"));
1081 			goto usage;
1082 		}
1083 
1084 		if (Lopt)
1085 			bitmapfs_print();
1086 		else /* if (Dopt) */
1087 			bitmapfs_delete(bitmapfs);
1088 
1089 		exit(0);
1090 	}
1091 
1092 	if (!o) {
1093 		if (argc > 1)
1094 			goto usage;
1095 		(void) printf(gettext("%s: Printing all cd's and options:\n"),
1096 		    progname);
1097 		print_all_options();
1098 	}
1099 
1100 	/* Configure */
1101 	if (o == 'C') {
1102 		exit(configure_sdbc(argc, argv, optind));
1103 	}
1104 	/* enable */
1105 	if (o == 'e') {
1106 		enable_sdbc();
1107 		if (qflag == 0)
1108 			sd_gather_alert_dumps();
1109 		exit(0);
1110 	}
1111 	/* disable */
1112 	if (o == 'd') {
1113 		disable_sdbc();
1114 		exit(0);
1115 	}
1116 	/* get version */
1117 	if (o == 'v') {
1118 		get_version();
1119 		exit(0);
1120 	}
1121 	/* node_hint or cd_hint */
1122 	if (o == 'o') {
1123 		if (!(strcoll(optarg, "system"))) {  /* node_hint */
1124 			if ((optind - 1) == (argc - 1)) {  /* get */
1125 				if ((hint = SDBC_IOCTL(SDBC_GET_NODE_HINT, 0, 0,
1126 				    0, 0, 0, &ustats)) == SPCS_S_ERROR) {
1127 					(void) fprintf(stderr,
1128 					    gettext("%s: get system "
1129 					    "options failed\n"),
1130 					    progname);
1131 					sdbc_report_error(&ustats);
1132 					exit(1);
1133 				}
1134 #ifdef WRTHRU_HINTS
1135 				(void) printf(gettext("System Status: "));
1136 				print_hint(hint, 1);
1137 #endif
1138 				(void) printf(gettext("System Options: "));
1139 				print_hint(hint, 0);
1140 				exit(0);
1141 			} else {  /* set, clear */
1142 				if (get_hint(argv[optind], &hint, &flag) == -1)
1143 					goto usage;
1144 				if (hint == -1) {
1145 					/* remove hint from config */
1146 					remove_hint("system");
1147 					exit(0);
1148 				}
1149 
1150 				if (SDBC_IOCTL(SDBC_SET_NODE_HINT, hint, flag,
1151 				    0, 0, 0, &ustats) == SPCS_S_ERROR) {
1152 					(void) fprintf(stderr,
1153 					    gettext("%s: set system "
1154 					    "option failed\n"),
1155 					    progname);
1156 					sdbc_report_error(&ustats);
1157 					exit(1);
1158 				}
1159 				save_hint(-1, hint, flag);
1160 				(void) printf(gettext("%s: System option %s"
1161 				    " now set.\n"), progname, argv[optind]);
1162 				exit(0);
1163 			}
1164 		} else {  /* cd_hint */
1165 			cd = get_cd(optarg);
1166 			if ((optind - 1) == (argc - 1)) {  /* get */
1167 				if (cd < 0) {
1168 					(void) fprintf(stderr,
1169 					    gettext("%s: device %s not "
1170 					    "found\n"),
1171 					    progname, optarg);
1172 					exit(1);
1173 				}
1174 				hint = get_cd_hint(cd);
1175 				(void) printf(gettext("%s: cd(%d) Current "
1176 				    "options are: "), progname, cd);
1177 				print_hint(hint, 0);
1178 				exit(0);
1179 			} else { /* set, clear */
1180 				if (get_hint(argv[optind], &hint, &flag) == -1)
1181 					goto usage;
1182 				if (hint == -1) {
1183 					/* remove hint from config */
1184 					if (cd < 0)
1185 						remove_hint(optarg);
1186 					else
1187 						remove_hint(cd_to_device(cd));
1188 					exit(0);
1189 				}
1190 				if (cd < 0) {
1191 					(void) fprintf(stderr,
1192 					    gettext("%s: device %s not "
1193 					    "found\n"),
1194 					    progname, optarg);
1195 					exit(1);
1196 				}
1197 
1198 				if (SDBC_IOCTL(SDBC_SET_CD_HINT, cd, hint,
1199 				    flag, 0, 0, &ustats) == SPCS_S_ERROR) {
1200 					(void) fprintf(stderr,
1201 					    gettext("%s: set option "
1202 					    "failed\n"), progname);
1203 					sdbc_report_error(&ustats);
1204 					exit(1);
1205 				}
1206 				save_hint(cd, hint, flag);
1207 				(void) printf(gettext("%s: cd %d option %s now"
1208 				    " set.\n"), progname, cd, argv[optind]);
1209 				exit(0);
1210 			}
1211 		}
1212 	}
1213 
1214 	if (o == 'm') {   /* "get_cd" = map */
1215 		char *dev_name;
1216 
1217 		if (!(strcoll(optarg, "all"))) /* all */
1218 			(void) get_cd_all();
1219 		else {
1220 			cd = get_cd(optarg);
1221 			if (cd < 0) {
1222 				(void) fprintf(stderr,
1223 				    gettext("%s: device or cd %s not found\n"),
1224 				    progname, optarg);
1225 				exit(1);
1226 			}
1227 
1228 			if ((dev_name = get_device_name(optarg)) == NULL) {
1229 				(void) fprintf(stderr, gettext(
1230 				    "%s: device for cd %d not found\n"),
1231 				    progname, cd);
1232 				exit(1);
1233 			}
1234 
1235 			(void) printf(gettext("%s: diskname %s; cd %d\n"),
1236 			    progname, dev_name, cd);
1237 			exit(0);
1238 		}
1239 	}
1240 
1241 #ifdef DEBUG
1242 	if (o == 't') { /* "trace" */
1243 		int flag, value;
1244 		_sdtr_table_t tt;
1245 		if ((optind+1) != (argc-1))
1246 			goto usage;
1247 		cd = get_cd(argv[optind]);
1248 		if (cd < 0) {
1249 			(void) fprintf(stderr,
1250 			    gettext("%s: device or cd %s not found\n"),
1251 			    progname, argv[optind]);
1252 			exit(1);
1253 		}
1254 
1255 		value = strtol(argv[optind+1], 0, 0);
1256 		if (!(strcoll(optarg, gettext("size")))) {
1257 			flag = SD_SET_SIZE;
1258 			tt.tt_max = value;
1259 		} else if (!(strcoll(optarg, gettext("mask")))) {
1260 			flag = SD_SET_MASK;
1261 			tt.tt_mask = value;
1262 		} else if (!(strcoll(optarg, gettext("lbolt")))) {
1263 			flag = SD_SET_LBOLT;
1264 			tt.tt_lbolt = value;
1265 		} else if (!(strcoll(optarg, gettext("good")))) {
1266 			flag = SD_SET_GOOD;
1267 			tt.tt_good = value;
1268 		} else	goto usage;
1269 
1270 		if (SDBC_IOCTL(SDBC_ADUMP, (long)cd, &tt, NULL, 0L,
1271 		    (long)flag, &ustats) == SPCS_S_ERROR) {
1272 			(void) fprintf(stderr,
1273 			    gettext("%s: trace %s failed\n"),
1274 			    progname, optarg);
1275 			sdbc_report_error(&ustats);
1276 			exit(1);
1277 		}
1278 		(void) printf(gettext("%s: trace %s processed\n"),
1279 		    progname, optarg);
1280 		if (cd != -1)
1281 			(void) printf(gettext(" cd %d; size %d; mask 0x%04x; "
1282 			    "lbolt %d; good %d;\n"),
1283 			    cd, tt.tt_max, tt.tt_mask,
1284 			    tt.tt_lbolt, tt.tt_good);
1285 		exit(0);
1286 	}
1287 
1288 	if (o == 'i') { /* "inject_ioerr" */
1289 		int ioj_err = EIO;
1290 		int cd;
1291 		int ioj_cnt = 0;
1292 
1293 		/* a cd of "-1" represents all devices */
1294 		if (strcmp(optarg, "-1") == 0) {
1295 			cd = -1;
1296 		} else if ((cd = get_cd(optarg)) < 0) {
1297 			(void) fprintf(stderr,
1298 			    gettext("%s: device or cd %s not found\n"),
1299 			    progname, optarg);
1300 			exit(1);
1301 		}
1302 		if (argc == 4)
1303 			ioj_err = strtol(argv[optind], 0, 0);
1304 		if (argc == 5)
1305 			ioj_cnt = strtol(argv[optind+1], 0, 0);
1306 
1307 		if (SDBC_IOCTL(SDBC_INJ_IOERR, cd, ioj_err, ioj_cnt, 0, 0,
1308 		    &ustats) == SPCS_S_ERROR)  {
1309 			(void) fprintf(stderr,
1310 			    gettext("%s: i/o error injection for cd %s "
1311 			    "failed\n"), progname, optarg);
1312 			sdbc_report_error(&ustats);
1313 			exit(1);
1314 		}
1315 		(void) printf(gettext("%s: i/o error injection cd %d errno %d "
1316 		    "processed\n"), progname, cd, ioj_err);
1317 		exit(0);
1318 	}
1319 
1320 	if (o == 'c') { /* "clear_ioerr" */
1321 		int cd;
1322 
1323 		/* a cd of "-1" represents all devices */
1324 		if (strcmp(optarg, "-1") == 0) {
1325 			cd = -1;
1326 		} else if ((cd = get_cd(optarg)) < 0) {
1327 			(void) fprintf(stderr,
1328 			    gettext("%s: device or cd %s not found\n"),
1329 			    progname, optarg);
1330 			exit(1);
1331 		}
1332 
1333 		if (SDBC_IOCTL(SDBC_CLR_IOERR, cd, 0, 0, 0, 0, &ustats)
1334 		    == SPCS_S_ERROR) {
1335 			(void) fprintf(stderr,
1336 			    gettext("%s: i/o error clear %s failed\n"),
1337 			    progname, optarg);
1338 			sdbc_report_error(&ustats);
1339 			exit(1);
1340 		}
1341 		(void) printf(gettext("%s: i/o error clear for cd %d "
1342 		    "processed\n"), progname, cd);
1343 		exit(0);
1344 	}
1345 
1346 	if (o == 'g') { /* "toggle_flush" */
1347 		flag = toggle_flush();
1348 		(void) printf(gettext("%s: sdbc cache flush now %s\n"),
1349 		    progname, flag ? "on" : "off");
1350 		exit(0);
1351 	}
1352 #endif /* DEBUG */
1353 
1354 	return (0);
1355 usage:
1356 	(void) fprintf(stderr, "%s\n", scmadmUsage);
1357 	if (hflag) {
1358 		return (0);
1359 	}
1360 	return (1);
1361 }
1362 
1363 
1364 #define	addusage(f__)	\
1365 	(void) strncat(scmadmUsage, f__, sizeof (scmadmUsage));
1366 
1367 #define	addusage1(f__, a__)	\
1368 	(void) snprintf(fmt, sizeof (fmt), "%s%s", scmadmUsage, f__);	\
1369 	(void) snprintf(scmadmUsage, sizeof (scmadmUsage), fmt, a__);
1370 
1371 #define	addusage2(f__, a__, b__)	\
1372 	(void) snprintf(fmt, sizeof (fmt), "%s%s", scmadmUsage, f__);	\
1373 	(void) snprintf(scmadmUsage, sizeof (scmadmUsage), fmt, a__, b__);
1374 
1375 static void
1376 buildusage(char *p)
1377 {
1378 	char fmt[USAGELEN];
1379 #ifdef WRTHRU_HINTS
1380 	char *hints_str = "[nordcache|rdcache|wrthru|nowrthru|forget]\n";
1381 #else
1382 	char *hints_str = "[nordcache|rdcache|forget]\n";
1383 #endif
1384 
1385 	bzero(scmadmUsage, sizeof (scmadmUsage));
1386 	bzero(fmt, sizeof (fmt));
1387 
1388 	addusage(gettext("Usage :\n"));
1389 	addusage1(gettext("\t%s\n"), p);
1390 	addusage1(gettext("\t%s -h\n"), p);
1391 	addusage1(gettext("\t%s -e\n"), p);
1392 	addusage1(gettext("\t%s -d\n"), p);
1393 	addusage1(gettext("\t%s -v\n"), p);
1394 	addusage1(gettext("\t%s {-L | -D bitmapfs}\n"), p);
1395 	addusage1(gettext("\t%s -C [parameter[=[value]] ...]\n"), p);
1396 	addusage2(gettext("\t%s -o system %s"), p, hints_str);
1397 	addusage2(gettext("\t%s -o <cd> %s"), p, hints_str);
1398 	addusage2(gettext("\t%s -o <diskname> %s"), p, hints_str);
1399 	addusage1(gettext("\t%s -m {<cd>|<diskname>|all}\n"), p);
1400 #ifdef DEBUG
1401 	addusage1(gettext(
1402 	    "\t%s -S [-Mz] [-d delay_time] [-l logfile] [-r range]\n"), p);
1403 	addusage1(gettext(
1404 	    "\t%s -t {size|mask|lbolt|good} <cd|diskname> <value>\n"), p);
1405 	addusage1(gettext("\t%s -g\n"), p);
1406 	addusage1(gettext(
1407 	    "\t%s -i {cd|diskname|-1 for all} [errno [countdown]]\n"), p);
1408 	addusage1(gettext("\t%s -c {cd|diskname|-1 for all}\n"), p);
1409 	addusage(gettext("\nt = trace\tg = toggle_flush\ti = inject ioerr\n"
1410 	    "c = clear ioerr\tS = stats\n"));
1411 #endif /* DEBUG */
1412 	addusage(gettext(
1413 	    "e = enable\td = disable\tv=version\to = get/ set options\n"));
1414 	addusage(gettext(
1415 	    "m = get cd map\n"));
1416 	addusage1(gettext(
1417 	    "note: cd is a cache descriptor integer in the range [0-%d]\n"),
1418 	    sdbc_max_devices - 1);
1419 	addusage(gettext(
1420 	    "      bitmapfs is a block device or filesystem mount point\n"));
1421 
1422 #ifdef DEBUG
1423 	(void) snprintf(stats_usage, sizeof (stats_usage),
1424 	    "SD_STATS_USAGE=%s", scmadmUsage);
1425 #endif
1426 }
1427 
1428 static int
1429 get_hint(char *str,  int *hint, int *flag)
1430 {
1431 #ifdef WRTHRU_HINTS
1432 	if (!(strcoll(str, gettext("wrthru")))) {
1433 		*hint = NSC_WRTHRU;
1434 		*flag = 1;
1435 		return (0);
1436 	} else if (!(strcoll(str, gettext("nowrthru")))) {
1437 		*hint =  NSC_WRTHRU;
1438 		*flag = 0;
1439 		return (0);
1440 	} else
1441 #endif
1442 	if (!(strcoll(str, gettext("nordcache")))) {
1443 		*hint = NSC_NOCACHE;
1444 		*flag = 1;
1445 		return (0);
1446 	} else if (!(strcoll(str, gettext("rdcache")))) {
1447 		*hint = NSC_NOCACHE;
1448 		*flag = 0;
1449 		return (0);
1450 	} else if (!(strcoll(str, gettext("forget")))) {
1451 		*hint = -1;
1452 		*flag = 0;
1453 		return (0);
1454 	}
1455 	return (-1);
1456 }
1457 
1458 /*ARGSUSED*/
1459 void
1460 print_hint(const uint_t type, const int status)
1461 {
1462 #ifdef WRTHRU_HINTS
1463 	if (status) {
1464 		if (type & NSC_FORCED_WRTHRU) {
1465 			(void) printf(gettext("Fast Writes Overridden\n"));
1466 		} else {
1467 			/* if (type & NSC_NO_FORCED_WRTHRU) */
1468 			(void) printf(gettext("default\n"));
1469 		}
1470 	} else {
1471 		(void) printf("%swrthru, %srdcache",
1472 		    (type & (NSC_FORCED_WRTHRU|NSC_WRTHRU)) ? "" : "no",
1473 		    (type & NSC_NOCACHE) ? "no" : "");
1474 #else
1475 	{
1476 		(void) printf("%srdcache", (type & NSC_NOCACHE) ? "no" : "");
1477 #endif
1478 
1479 		if (type & 0x80000000)
1480 			(void) printf(" (overridden by system)");
1481 
1482 		(void) printf("\n");
1483 	}
1484 }
1485 
1486 /*
1487  * Read the configuration via libcfg
1488  */
1489 
1490 int
1491 get_cache_config()
1492 {
1493 	int i;
1494 	int sysid;
1495 	CFGFILE *cfg;
1496 	char buf[CFG_MAX_BUF];
1497 	char key[CFG_MAX_KEY];
1498 
1499 
1500 	if ((cfg = cfg_open(NULL)) == NULL) {
1501 		(void) fprintf(stderr,
1502 		    gettext("Cannot open configuration file\n"));
1503 		exit(1);
1504 	}
1505 
1506 	if (!cfg_lock(cfg, CFG_RDLOCK)) {
1507 		(void) fprintf(stderr,
1508 		    gettext("Cannot lock configuration file\n"));
1509 		exit(1);
1510 	}
1511 
1512 	convert_config(cfg, CFG_RDLOCK);
1513 	(void) memset((char *)&user_level_conf, 0, sizeof (_sd_cache_param_t));
1514 
1515 	/* Get the system ID */
1516 	if (nsc_getsystemid(&sysid) < 0) {
1517 		(void) fprintf(stderr,
1518 		    gettext("%s Unable to obtain subsystem ID: %s\n"),
1519 		    progname, strerror(errno));
1520 		exit(1);
1521 	}
1522 	myid = sysid;
1523 
1524 	user_level_conf.blk_size = 8192;	/* DEFAULT */
1525 	user_level_conf.procs = 16;	/* DEFAULT */
1526 	user_level_conf.reserved1 = RESERVED1_DEFAULTS;
1527 
1528 	bzero(buf, CFG_MAX_BUF);
1529 	(void) snprintf(key, sizeof (key), "scm.set1.thread");
1530 	if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) {
1531 		user_level_conf.threads = atoi(buf);
1532 	} else
1533 		user_level_conf.threads = 128;	/* DEFAULT */
1534 
1535 	(void) snprintf(key, sizeof (key), "scm.set1.tdemons");
1536 	if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) {
1537 		user_level_conf.test_demons = atoi(buf);
1538 	}
1539 
1540 	(void) snprintf(key, sizeof (key), "scm.set1.write_cache");
1541 	if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) {
1542 		user_level_conf.write_cache = atoi(buf);
1543 	}
1544 
1545 	(void) snprintf(key, sizeof (key), "scm.set1.size");
1546 	if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) {
1547 		/*
1548 		 * We need to run strtol for backwards compatibility in 3.2.
1549 		 * A workaround for this bug was put in 3.2 which allowed
1550 		 * customers to set the cache size up to 1024 if it was
1551 		 * specified in hexadecimal. Decimal still had the limit
1552 		 * of 128.  This change treats them both identically.
1553 		 */
1554 		user_level_conf.cache_mem[0] = (int)strtol(buf, NULL, 0);
1555 		if (user_level_conf.cache_mem[0] > MAX_CACHE_SIZE) {
1556 			(void) fprintf(stderr, gettext(
1557 			    "The cache size of %ld is larger than "
1558 			    "the system maximum of %ld.\nUse \"scmadm -C "
1559 			    "cache_size=<size>\" to set the size to a proper "
1560 			    "value.\n"),
1561 			    user_level_conf.cache_mem[0], MAX_CACHE_SIZE);
1562 			user_level_conf.cache_mem[0] = MAX_CACHE_SIZE;
1563 		}
1564 	}
1565 
1566 	(void) snprintf(key, sizeof (key), "scm.set1.iobuf");
1567 	if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) {
1568 		user_level_conf.iobuf = atoi(buf);
1569 	}
1570 
1571 	(void) snprintf(key, sizeof (key), "scm.set1.fill_pattern");
1572 	if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) {
1573 		user_level_conf.fill_pattern = atoi(buf);
1574 		user_level_conf.gen_pattern = 1;
1575 	}
1576 
1577 	(void) snprintf(key, sizeof (key), "scm.set1.no_forced_wrthru");
1578 	if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) {
1579 		no_forced_wrthru = atoi(buf);
1580 	}
1581 
1582 	(void) snprintf(key, sizeof (key), "scm.set1.forced_wrthru");
1583 	if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) {
1584 		forced_wrthru = atoi(buf);
1585 	}
1586 
1587 	(void) snprintf(key, sizeof (key), "scm.set1.reserved1");
1588 	if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) {
1589 		user_level_conf.reserved1 = atoi(buf);
1590 	}
1591 
1592 	cfg_close(cfg);
1593 
1594 	/*
1595 	 * use the default minidsp configuration if no
1596 	 * node/mirror/remote-mirror/cluster line is in the sd.cf file
1597 	 */
1598 	if (nodes_configured == 0)
1599 		check_and_set_mirrors(myid, _SD_NO_HOST);
1600 
1601 
1602 	/* Check if our sysid was defined */
1603 	if (!node_defined[myid]) {
1604 		(void) fprintf(stderr,
1605 		    gettext("This node(%d) is not defined in config.\n"), myid);
1606 		exit(1);
1607 	}
1608 
1609 	/*
1610 	 * Save off number of nodes so we can calculate the point-to-point
1611 	 * segements.  Code in kernel currently supports MAX_SD_NODES
1612 	 */
1613 	if ((user_level_conf.num_nodes = nodes_configured) >
1614 	    MAX_SD_NODES) {
1615 		(void) fprintf(stderr,
1616 		    gettext("Cache can support only %d nodes(%d).\n"),
1617 		    MAX_SD_NODES, nodes_configured);
1618 		exit(1);
1619 	}
1620 
1621 	if ((nodes_configured % 2) && !minidsp) {
1622 		if (nodes_configured == 1)
1623 			(void) fprintf(stderr,
1624 			    gettext("Only one node configured, "
1625 			    "mirror node must be %d\n"), _SD_NO_HOST);
1626 		else
1627 			(void) fprintf(stderr,
1628 			    gettext("Cannot configure odd number of nodes.\n"));
1629 		exit(1);
1630 	}
1631 
1632 
1633 	/* Pass List of Nodes Configured to Cache */
1634 	for (i = 0; i < nodes_configured; i++)
1635 		user_level_conf.nodes_conf[i] = nodes_conf[i];
1636 
1637 	/* Place magic number in user_level_conf.  Kernel will test for it */
1638 	user_level_conf.magic = _SD_MAGIC;
1639 	(void) sleep(1);
1640 	return (0);
1641 }
1642 
1643 _sdtr_t hdr;
1644 
1645 /* function name string */
1646 char *
1647 _sd_fname(int f)
1648 {
1649 	int fn = f & ST_FUNC;
1650 	static char c[8];
1651 	char *s;
1652 
1653 	if (f & ST_BCACHE)
1654 		s = _bcache_fname[fn];
1655 	else if (f & ST_BSUB)
1656 		s = _bsub_fname[fn];
1657 	else if (f & ST_IO)
1658 		s = _io_fname[fn];
1659 	else if (f & ST_STATS)
1660 		s = _stats_fname[fn];
1661 	else if (f & ST_CCIO)
1662 		s = _ccio_fname[fn];
1663 	else if (f & ST_FT)
1664 		s = _ft_fname[fn];
1665 	else if (f & ST_INFO)
1666 		s = _info_fname[fn];
1667 	if (!s)
1668 		(void) sprintf(s = c, "0x%04x", f & 0xffff);
1669 	return (s);
1670 }
1671 
1672 int alerts = 0;
1673 
1674 /*
1675  * Background daemon to wait for alert (on any device)
1676  * Writes the traces to "sd_alert.CD.NUM",
1677  * and writes an information message to the alert_file.
1678  */
1679 
1680 void
1681 sd_gather_alert_dumps()
1682 {
1683 	_sdtr_table_t tt;
1684 	_sdtr_t *buf;
1685 	int cd, count, size, flag;
1686 	char filename[64];
1687 	int fd;
1688 	time_t tloc;
1689 	struct tm tm_storage;
1690 	struct tm *tm_ptr;
1691 	char timebuf[80];
1692 	spcs_s_info_t ustats;
1693 
1694 	/* fork and detach daemon */
1695 	if (fork())
1696 		exit(0);
1697 	(void) close(0);
1698 	fd = open(alert_file, O_WRONLY|O_APPEND|O_CREAT, 0644);
1699 	if (fd == -1)
1700 		fd = open("/dev/console", O_WRONLY);
1701 	if (fd != -1) {
1702 		(void) dup2(fd, 1);
1703 		(void) dup2(fd, 2);
1704 		(void) close(fd);
1705 	}
1706 	(void) setsid();
1707 
1708 	size = 10000;
1709 	if (size < user_level_conf.trace_size)
1710 		size = user_level_conf.trace_size;
1711 
1712 	buf = (_sdtr_t *)malloc(size * sizeof (_sdtr_t));
1713 	if (!buf) {
1714 		(void) fprintf(stderr, gettext("%s malloc: %s\n"),
1715 		    progname, strerror(errno));
1716 		exit(1);
1717 	}
1718 	tloc = time(NULL);
1719 	tm_ptr = (struct tm *)localtime_r(&tloc, &tm_storage);
1720 
1721 loop:
1722 	cd = SDT_ANY_CD;		/* any device */
1723 	flag = SD_ALERT_WAIT;	/* block for alert */
1724 	if ((count = SDBC_IOCTL(SDBC_ADUMP, cd, &tt, buf, size,
1725 	    flag, &ustats)) == SPCS_S_ERROR) {
1726 		(void) fprintf(stderr, gettext("%s: sd_adump\n"), progname);
1727 		sdbc_report_error(&ustats);
1728 		if (errno == EIDRM) {
1729 			(void) strftime(timebuf, 80, "%x %X", tm_ptr);
1730 			(void) fprintf(stderr,
1731 			    gettext("%s: cache deconfigured at %s\n"),
1732 			    progname, timebuf);
1733 			exit(0);
1734 		}
1735 		if (errno == ENOSYS)
1736 			exit(0);
1737 		exit(errno);
1738 	}
1739 	if (count == 0)
1740 		goto loop;
1741 	cd = tt.tt_cd;
1742 	(void) sprintf(filename, "%s.%d.%d", "sd_alert", cd, alerts++);
1743 	if ((fd = open(filename, O_CREAT | O_RDWR, 0444)) == -1) {
1744 		(void) fprintf(stderr, gettext("%s: open: %s\n"),
1745 		    progname, strerror(errno));
1746 		exit(errno);
1747 	}
1748 	/*
1749 	 * write header to identify device, write entries
1750 	 */
1751 	hdr.t_func = SDF_CD;
1752 	hdr.t_len = count;
1753 	hdr.t_ret = tt.tt_cd;
1754 	if (write(fd, &hdr, sizeof (_sdtr_t)) == -1) {
1755 		(void) fprintf(stderr, gettext("%s: write: %s\n"),
1756 		    progname, strerror(errno));
1757 		exit(errno);
1758 	}
1759 
1760 	if (write(fd, buf, sizeof (_sdtr_t)*count) == -1) {
1761 		(void) fprintf(stderr, gettext("%s: write: %s\n"),
1762 		    progname, strerror(errno));
1763 		exit(errno);
1764 	}
1765 	(void) close(fd);
1766 
1767 	(void) strftime(timebuf, 80, "%x %X", tm_ptr);
1768 	(void) printf("sd alert trace dump %s at %s\n", filename, timebuf);
1769 	goto loop;
1770 }
1771 
1772 
1773 
1774 /*
1775  * print list of configured cd's, diskname, options and global options
1776  */
1777 void
1778 print_all_options()
1779 {
1780 	static _sd_stats_t *cs_cur;
1781 	spcs_s_info_t ustats;
1782 	int cd;
1783 	int hint;
1784 	char *s1 = "device name";
1785 	char *s2 = "option";
1786 	char fn[19];
1787 	int len;
1788 
1789 	/* No corresponding free because this function exits */
1790 	cs_cur = malloc(sizeof (_sd_stats_t) +
1791 	    (sdbc_max_devices - 1) * sizeof (_sd_shared_t));
1792 	if (cs_cur == NULL) {
1793 		(void) fprintf(stderr, gettext("%s malloc: %s\n"),
1794 		    progname, strerror(errno));
1795 		exit(1);
1796 	}
1797 
1798 	/* node hints */
1799 	if ((hint = SDBC_IOCTL(SDBC_GET_NODE_HINT, 0, 0, 0, 0, 0,
1800 	    &ustats)) == SPCS_S_ERROR) {
1801 		(void) fprintf(stderr,
1802 		    gettext("%s: get system option failed\n"),
1803 		    progname);
1804 		sdbc_report_error(&ustats);
1805 		exit(1);
1806 	}
1807 #ifdef WRTHRU_HINTS
1808 	(void) printf(gettext("System Status: "));
1809 	print_hint(hint, 1);
1810 #endif
1811 	(void) printf(gettext("System Options: "));
1812 	print_hint(hint, 0);
1813 
1814 	/* get cds */
1815 	if (SDBC_IOCTL(SDBC_STATS, cs_cur, 0, 0, 0, 0, &ustats)
1816 	    == SPCS_S_ERROR) {
1817 		(void) fprintf(stderr,
1818 		    gettext("%s: get_cd failed in print_all options\n"),
1819 		    progname);
1820 		sdbc_report_error(&ustats);
1821 		exit(1);
1822 	}
1823 	if (cs_cur->st_cachesize == 0)
1824 		(void) printf(gettext("Cache is disabled\n"));
1825 	else if (cs_cur->st_count == 0)
1826 		(void) printf(gettext("No devices are configured\n"));
1827 	else {
1828 		(void) printf(
1829 		    gettext("\nConfigured cd's, disknames and options: \n"));
1830 		(void) printf(gettext("cd\t%-28s\t%-20s\n"), s1, s2);
1831 		for (cd = 0; cd < cs_cur->st_count; cd++) {
1832 			if (cs_cur->st_shared[cd].sh_alloc) {
1833 				hint = get_cd_hint(cd);
1834 				if ((len =
1835 				    strlen(cs_cur->st_shared[cd].sh_filename))
1836 				    > 23) {
1837 					(void) strcpy(fn, "...");
1838 					(void) strcat(fn,
1839 					    cs_cur->st_shared[cd].sh_filename +
1840 					    len - 20);
1841 				} else {
1842 					(void) strcpy(fn,
1843 					    cs_cur->st_shared[cd].sh_filename);
1844 				}
1845 
1846 				(void) printf(gettext("%d\t%-28.*s\t"), cd,
1847 				    NSC_MAXPATH, fn);
1848 
1849 				print_hint(hint, 0);
1850 			}
1851 		}
1852 	}
1853 	exit(0);
1854 }
1855 
1856 
1857 /*
1858  * cache device -- lookup names and cache descriptors of all configured devices
1859  */
1860 void
1861 get_cd_all()
1862 {
1863 	static _sd_stats_t *cs_cur;
1864 	spcs_s_info_t ustats;
1865 	int cd;
1866 	char fn[19];
1867 	int len;
1868 
1869 	/* No corresponding free because this function exits */
1870 	cs_cur = malloc(sizeof (_sd_stats_t) +
1871 	    (sdbc_max_devices - 1) * sizeof (_sd_shared_t));
1872 	if (cs_cur == NULL) {
1873 		(void) fprintf(stderr, gettext("%s malloc: %s\n"),
1874 		    progname, strerror(errno));
1875 		exit(1);
1876 	}
1877 
1878 	if (SDBC_IOCTL(SDBC_STATS, cs_cur, 0, 0, 0, 0, &ustats)
1879 	    == SPCS_S_ERROR) {
1880 		(void) fprintf(stderr, gettext("%s: get_cd_all"),
1881 		    progname);
1882 		sdbc_report_error(&ustats);
1883 		exit(1);
1884 	}
1885 	if (cs_cur->st_cachesize == 0)
1886 		(void) printf(gettext("Cache is disabled\n"));
1887 	else if (cs_cur->st_count == 0)
1888 		(void) printf(gettext("No devices are configured\n"));
1889 	else {
1890 		(void) printf(gettext("\tcd\tdevice name\n"));
1891 		for (cd = 0; cd < cs_cur->st_count; cd++) {
1892 			if (cs_cur->st_shared[cd].sh_alloc) {
1893 				if ((len = strlen(
1894 				    cs_cur->st_shared[cd].sh_filename)) > 15) {
1895 					(void) strcpy(fn, "...");
1896 					(void) strcat(fn,
1897 					    cs_cur->st_shared[cd].sh_filename +
1898 					    len - 12);
1899 				} else {
1900 					(void) strcpy(fn,
1901 					    cs_cur->st_shared[cd].sh_filename);
1902 				}
1903 				(void) printf(gettext("\t%d\t%s\n"),
1904 				    cd, fn);
1905 			}
1906 		}
1907 	}
1908 	exit(0);
1909 }
1910 
1911 /*
1912  * cache device -- specified by number or lookup name
1913  */
1914 static int
1915 get_cd(char *s)
1916 {
1917 	static _sd_stats_t *cs_cur = NULL;
1918 	spcs_s_info_t ustats;
1919 	int cd, arg_cd = -1;
1920 
1921 	if (cs_cur == NULL) {
1922 		/*
1923 		 * No corresponding free because the memory is reused
1924 		 * every time the function is called.
1925 		 */
1926 		cs_cur = malloc(sizeof (_sd_stats_t) +
1927 		    (sdbc_max_devices - 1) * sizeof (_sd_shared_t));
1928 		if (cs_cur == NULL) {
1929 			(void) fprintf(stderr, gettext("%s malloc: %s\n"),
1930 			    progname, strerror(errno));
1931 			exit(1);
1932 		}
1933 	}
1934 
1935 	if (SDBC_IOCTL(SDBC_STATS, cs_cur, 0, 0, 0, 0, &ustats)
1936 	    == SPCS_S_ERROR) {
1937 		(void) fprintf(stderr, gettext("%s: get_cd\n"), progname);
1938 		sdbc_report_error(&ustats);
1939 		exit(1);
1940 	}
1941 	if (cs_cur->st_cachesize == 0) {
1942 		(void) printf(gettext("Cache is disabled\n"));
1943 		exit(0);
1944 	}
1945 
1946 	if (*s != '/') {
1947 		/*
1948 		 * Since strtol returns 0 on failure, we need to make a
1949 		 * special case for a cd of "0", which is valid.
1950 		 *
1951 		 * This case also deals with the difference between
1952 		 * scmadm -o system and scmadm -o 0
1953 		 */
1954 		if (((int)strtol(s, (char **)NULL, 10) == 0) &&
1955 		    strcmp(s, "0"))
1956 			return (-1);
1957 
1958 		/*
1959 		 * Only return failure at this point, in order to allow
1960 		 * checking arg_cd against st_count later on.
1961 		 */
1962 		if ((arg_cd = strtol(s, 0, 0)) < 0) {
1963 			return (arg_cd);
1964 		}
1965 	}
1966 
1967 	/* make sure the cd passed as an argument is alloc'd and < st_count */
1968 	if (arg_cd >= 0) {
1969 		return (((arg_cd < cs_cur->st_count) &&
1970 		    (cs_cur->st_shared[arg_cd].sh_alloc)) ? arg_cd : -1);
1971 	}
1972 
1973 	for (cd = 0; cd < cs_cur->st_count; cd++) {
1974 		if (cs_cur->st_shared[cd].sh_alloc &&
1975 		    strcmp(s, cs_cur->st_shared[cd].sh_filename) == 0)
1976 			return (cd);
1977 	}
1978 	return (-1);
1979 }
1980 
1981 void
1982 check_and_set_mirrors(int node, int mirror)
1983 {
1984 
1985 	if (minidsp) {
1986 		(void) fprintf(stderr,
1987 		    gettext("%s: minidsp defined. "
1988 		    "Cannot define other nodes.\n"),
1989 		    progname);
1990 		exit(1);
1991 	}
1992 
1993 	if (mirror == _SD_NO_HOST) {
1994 		minidsp++;
1995 	} else if ((!(node % 2) && !(node == mirror - 1)) ||
1996 	    (((node % 2) && !(node == mirror + 1)))) {
1997 		(void) fprintf(stderr,
1998 		    gettext("%s: Node and Mirror identification values "
1999 		    "must be consecutive\n"
2000 		    "starting at an even number (Node = %d Mirror = %d)\n"),
2001 		    progname, node, mirror);
2002 		exit(1);
2003 	}
2004 
2005 	node_defined[node]++;
2006 
2007 	nodes_conf[nodes_configured] = node;
2008 	nodes_configured++;
2009 
2010 	if (node == myid) {
2011 		user_level_conf.mirror_host  = mirror;
2012 	}
2013 }
2014 
2015 char *mem_string =
2016 	"%-8s Structures use approx. %8d bytes (%5d pages) of memory\n";
2017 
2018 void
2019 enable_sdbc()
2020 {
2021 	spcs_s_info_t ustats;
2022 
2023 	if (get_cache_config()) {
2024 		(void) fprintf(stderr,
2025 		    gettext("%s: unable to read configuration file\n"),
2026 		    progname);
2027 		exit(1);
2028 	}
2029 
2030 	if (SDBC_IOCTL(SDBC_ENABLE, &user_level_conf, 0, 0, 0, 0,
2031 	    &ustats) == SPCS_S_ERROR) {
2032 		(void) fprintf(stderr, gettext("%s: cache enable failed\n"),
2033 		    progname);
2034 		spcs_log("scm", &ustats, gettext("%s cache enable failed"),
2035 		    progname);
2036 		sdbc_report_error(&ustats);
2037 		exit(1);
2038 	}
2039 	spcs_log("scm", NULL, gettext("%s cache enable succeeded"),
2040 	    progname);
2041 #ifdef DEBUG
2042 	(void) printf(gettext("%s: cache has been configured\n"), progname);
2043 #endif
2044 #ifdef WRTHRU_HINTS
2045 	if (iscluster()) {
2046 		/* Must writethru on a cluster, even if nvram configured */
2047 		forced_wrthru = 1;
2048 	}
2049 
2050 	if (minidsp && forced_wrthru != -1) {
2051 		/* Have minidsp with forced_wrthru hint. Set / Clear hint */
2052 		if (SDBC_IOCTL(SDBC_SET_NODE_HINT, NSC_FORCED_WRTHRU,
2053 		    forced_wrthru, 0, 0, 0, &ustats) == SPCS_S_ERROR) {
2054 			(void) fprintf(stderr,
2055 			    gettext("%s: set/clear forced_wrthru failed\n"),
2056 			    progname);
2057 			sdbc_report_error(&ustats);
2058 		} else if (forced_wrthru) {
2059 			(void) printf(gettext("%s: Node option forced_wrthru "
2060 			    "now set.\n"), progname);
2061 		} else {
2062 			(void) printf(gettext("%s: Node option forced_wrthru "
2063 			    "now cleared.\n"), progname);
2064 		}
2065 	}
2066 	if (no_forced_wrthru != -1) {
2067 		if (SDBC_IOCTL(SDBC_SET_NODE_HINT, NSC_NO_FORCED_WRTHRU,
2068 		    no_forced_wrthru, 0, 0, 0, &ustats) == SPCS_S_ERROR) {
2069 			(void) fprintf(stderr,
2070 			    gettext("%s: set/clear no_forced_wrthru "
2071 			    "failed\n"), progname);
2072 			sdbc_report_error(&ustats);
2073 		} else if (no_forced_wrthru) {
2074 			(void) printf(gettext("%s: Node option no_forced_wrthru"
2075 			    " now set.\n"), progname);
2076 		} else {
2077 			(void) printf(gettext("%s: Node option no_forced_wrthru"
2078 			    " now cleared.\n"), progname);
2079 		}
2080 	}
2081 #endif
2082 
2083 	/* do scmadm -O to cater for manual cache disable then enable */
2084 	restore_hints();
2085 }
2086 
2087 void
2088 disable_sdbc()
2089 {
2090 	spcs_s_info_t ustats;
2091 
2092 	if (SDBC_IOCTL(SDBC_DISABLE, 0, 0, 0, 0, 0, &ustats) != SPCS_S_OK) {
2093 		/*
2094 		 * If it wasn't already enabled, don't appear to fail
2095 		 * or users of this program might think the cache is
2096 		 * configured, when it actually isn't.
2097 		 */
2098 		if (errno != SDBC_EDISABLE) {
2099 			spcs_log("scm", &ustats,
2100 			    gettext("%s cache disable failed"), progname);
2101 			sdbc_report_error(&ustats);
2102 			exit(1);
2103 		}
2104 	}
2105 #ifdef DEBUG
2106 	(void) printf(gettext("%s: cache has been deconfigured\n"), progname);
2107 #endif
2108 	spcs_log("scm", NULL, gettext("%s cache disable succeeded"),
2109 	    progname);
2110 }
2111 
2112 static void
2113 get_version()
2114 {
2115 	cache_version_t version;
2116 	spcs_s_info_t ustats;
2117 
2118 	if (SDBC_IOCTL(SDBC_VERSION, &version, 0, 0, 0, 0, &ustats) ==
2119 	    SPCS_S_ERROR) {
2120 		(void) fprintf(stderr,
2121 		    gettext("%s: get cache version failed\n"), progname);
2122 		sdbc_report_error(&ustats);
2123 		exit(1);
2124 	}
2125 #ifdef DEBUG
2126 	(void) printf(gettext("Cache version %d.%d.%d.%d\n"),
2127 	    version.major, version.minor, version.micro, version.baseline);
2128 #else
2129 	if (version.micro) {
2130 		(void) printf(gettext("Cache version %d.%d.%d\n"),
2131 		    version.major, version.minor, version.micro);
2132 	} else {
2133 		(void) printf(gettext("Cache version %d.%d\n"),
2134 		    version.major, version.minor);
2135 	}
2136 #endif
2137 }
2138 
2139 #ifdef DEBUG
2140 int
2141 toggle_flush(void)
2142 {
2143 	int rc;
2144 	spcs_s_info_t ustats;
2145 
2146 	if ((rc = SDBC_IOCTL(SDBC_TOGGLE_FLUSH, 0, 0, 0,
2147 	    0, 0, &ustats)) == SPCS_S_ERROR) {
2148 		(void) fprintf(stderr,
2149 		    gettext("%s: toggle sdbc cache flush failed\n"),
2150 		    progname);
2151 		sdbc_report_error(&ustats);
2152 		exit(1);
2153 	}
2154 	return (rc);
2155 }
2156 #endif
2157