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
71static int sdbc_max_devices = 0;
72
73static char alert_file[200]  = "/dev/console";
74
75/* Variables used to set up paramater block passed to kernel */
76static _sd_cache_param_t	user_level_conf;
77static int			myid;
78
79static int		nodes_configured = 0;
80static int		minidsp = 0; /* Is it a sp10 */
81static int		forced_wrthru = -1; /* 0 clear, 1 set,-1 as is */
82static int		no_forced_wrthru = -1;
83static short		node_defined[MAX_SD_NODES];
84static short		nodes_conf[MAX_SD_NODES];
85
86#define	USAGELEN	1024
87char stats_usage[USAGELEN+128];
88char scmadmUsage[USAGELEN];
89
90static caddr_t progname;
91
92
93/*
94 * Functions exported for fwcadm.
95 */
96void enable_sdbc(void);
97void disable_sdbc(void);
98void sdbc_set_maxdev();
99
100static void buildusage(char *);
101
102void print_all_options(void);
103void get_cd_all(void);
104int toggle_flush(void);
105static void sd_gather_alert_dumps();
106static int get_cd(char *);
107static int get_hint(char *, int *, int *);
108static void check_and_set_mirrors(int, int);
109static void print_hint(const uint_t, const int);
110static char *get_device_name(char *arg);
111static void get_version();
112
113extern 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
125void
126sdbc_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 */
143static int
144get_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 */
188static void
189convert_config(CFGFILE *cfg, CFGLOCK mode)
190{
191	char buf[CFG_MAX_BUF];
192	char *default_cfg = "128 64";
193
194retry:
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
248static int
249iscluster(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
268static void
269restore_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
364void
365sdbc_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
378static void
379bitmapfs_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
424static void
425bitmapfs_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
502static 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
522static int
523configure_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
694out:
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
714static char *
715cd_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 */
748static char *
749get_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
785static void
786remove_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
842static void
843save_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
925int
926scmadm_lintmain(int argc, char *argv[])
927#else
928int
929main(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);
1355usage:
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
1375static void
1376buildusage(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
1428static int
1429get_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*/
1459void
1460print_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
1490int
1491get_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 */
1646char *
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
1672int 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
1680void
1681sd_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
1721loop:
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 */
1777void
1778print_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 */
1860void
1861get_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 */
1914static int
1915get_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
1981void
1982check_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
2015char *mem_string =
2016	"%-8s Structures use approx. %8d bytes (%5d pages) of memory\n";
2017
2018void
2019enable_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
2087void
2088disable_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
2112static void
2113get_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
2140int
2141toggle_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