1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <sys/types.h>
27#include <sys/stat.h>
28#include <sys/mkdev.h>
29#include <sys/param.h>
30#include <fcntl.h>
31#include <stdarg.h>
32#include <stdlib.h>
33#include <strings.h>
34#include <errno.h>
35#include <stdio.h>
36#include <locale.h>
37#include <unistd.h>
38#include <libgen.h>
39#include <nsctl.h>
40
41#include <sys/unistat/spcs_s.h>
42#include <sys/unistat/spcs_s_u.h>
43#include <sys/unistat/spcs_errors.h>
44
45#include <sys/nsctl/sv.h>
46#include <sys/nsctl/sv_impl.h>
47
48#include <sys/nsctl/cfg.h>
49
50
51static int sv_max_devices;
52
53
54/*
55 * Pathnames.
56 */
57
58static const caddr_t sv_rpath = SV_DEVICE;
59
60/*
61 * Functions.
62 */
63
64static void resume_dev(int, sv_name_t *);
65static void suspend_dev(int, const caddr_t);
66static int read_libcfg(sv_name_t svn[]);
67static void resume_sv();
68static void suspend_sv();
69static void prepare_unload_sv();
70
71
72/*
73 * support for the special cluster tag "local" to be used with -C in a
74 * cluster for local volumes.
75 */
76
77#define	SV_LOCAL_TAG	"local"
78
79static caddr_t program;
80static caddr_t cfg_cluster_tag;
81
82
83static void
84usage(void)
85{
86	(void) fprintf(stderr, gettext("usage:\n"));
87
88	(void) fprintf(stderr, gettext(
89	    "\t%s -h                     help\n"), program);
90
91	(void) fprintf(stderr, gettext(
92	    "\t%s [-C tag] -r            resume all sv devices\n"), program);
93
94	(void) fprintf(stderr, gettext(
95	    "\t%s [-C tag] -s            suspend all sv devices\n"), program);
96
97	(void) fprintf(stderr, gettext(
98	    "\t%s -u                     prepare for sv unload\n"), program);
99}
100
101
102static void
103message(caddr_t prefix, spcs_s_info_t *status, caddr_t string, va_list ap)
104{
105	(void) fprintf(stderr, "%s: %s: ", program, prefix);
106	(void) vfprintf(stderr, string, ap);
107	(void) fprintf(stderr, "\n");
108
109	if (status) {
110		spcs_s_report(*status, stderr);
111		spcs_s_ufree(status);
112	}
113}
114
115
116static void
117error(spcs_s_info_t *status, caddr_t string, ...)
118{
119	va_list ap;
120	va_start(ap, string);
121
122	message(gettext("error"), status, string, ap);
123
124	va_end(ap);
125	exit(1);
126}
127
128
129static void
130warn(spcs_s_info_t *status, caddr_t string, ...)
131{
132	va_list ap;
133	va_start(ap, string);
134
135	message(gettext("warning"), status, string, ap);
136
137	va_end(ap);
138}
139
140
141static void
142sv_get_maxdevs(void)
143{
144	sv_name_t svn[1];
145	sv_list_t svl;
146	int fd;
147
148	if (sv_max_devices > 0)
149		return;
150
151	fd = open(sv_rpath, O_RDONLY);
152	if (fd < 0)
153		error(NULL, gettext("unable to open %s: %s"),
154			sv_rpath, strerror(errno));
155
156	bzero(&svl, sizeof (svl));
157	bzero(&svn[0], sizeof (svn));
158
159	svl.svl_names = &svn[0];
160	svl.svl_error = spcs_s_ucreate();
161
162	if (ioctl(fd, SVIOC_LIST, &svl) < 0)
163		error(&svl.svl_error, gettext("unable to get max devs"));
164
165	spcs_s_ufree(&svl.svl_error);
166	sv_max_devices = svl.svl_maxdevs;
167
168	(void) close(fd);
169}
170
171
172static sv_name_t *
173sv_alloc_svnames(void)
174{
175	sv_name_t *svn = NULL;
176
177	sv_get_maxdevs();
178
179	svn = calloc(sv_max_devices, sizeof (*svn));
180	if (svn == NULL) {
181		error(NULL, "unable to allocate %ld bytes of memory",
182		    sv_max_devices * sizeof (*svn));
183	}
184
185	return (svn);
186}
187
188int
189main(int argc, char *argv[])
190{
191	extern int optind;
192	extern char *optarg;
193	int Cflag, resume, suspend, unload;
194	int opt;
195
196	(void) setlocale(LC_ALL, "");
197	(void) textdomain("svboot");
198
199	program = strdup(basename(argv[0]));
200
201	Cflag = unload = resume = suspend = 0;
202
203	while ((opt = getopt(argc, argv, "C:hrsu")) != EOF) {
204		switch (opt) {
205
206		case 'C':
207			if (Cflag) {
208				warn(NULL,
209				    gettext("-C specified multiple times"));
210				usage();
211				exit(2);
212				/* NOTREACHED */
213			}
214
215			Cflag++;
216			cfg_cluster_tag = optarg;
217			break;
218
219		case 'r':
220			resume++;
221			break;
222
223		case 's':
224			suspend++;
225			break;
226
227		case 'u':
228			unload++;
229			break;
230
231		case 'h':
232			usage();
233			exit(0);
234
235		case '?':	/* FALLTHRU */
236
237		default:
238			usage();
239			exit(2);
240			/* NOTREACHED */
241		}
242	}
243
244
245	/*
246	 * Usage checks
247	 */
248
249	if ((resume + suspend + unload) > 1) {
250		warn(NULL, gettext("-r , -s and -u are mutually exclusive"));
251		usage();
252		exit(2);
253	}
254
255	if (!resume && !suspend && !unload) {
256		warn(NULL, gettext("option required"));
257		usage();
258		exit(2);
259	}
260
261	if (optind != argc) {
262		usage();
263		exit(2);
264	}
265
266
267	/*
268	 * Check for the special (local) cluster tag
269	 */
270
271	if (cfg_cluster_tag != NULL &&
272	    strcmp(cfg_cluster_tag, SV_LOCAL_TAG) == 0)
273		cfg_cluster_tag = "-";
274
275	/*
276	 * Process commands
277	 */
278
279	if (resume)
280		resume_sv();
281	else if (suspend)
282		suspend_sv();
283	else if (unload)
284		prepare_unload_sv();
285
286	return (0);
287}
288
289
290static void
291resume_sv()
292{
293	int index;
294	sv_name_t *svn;
295	int cnt;
296	int fd;
297
298	svn = sv_alloc_svnames();
299
300	index = read_libcfg(svn);
301
302	fd = open(sv_rpath, O_RDONLY);
303	if (fd < 0) {
304		warn(NULL, gettext("unable to open %s: %s"),
305			svn->svn_path, strerror(errno));
306		return;
307	}
308
309	for (cnt = 0; cnt < index; cnt++) {
310
311		/*
312		 * Check for more data.
313		 */
314		if (svn[cnt].svn_path[0] == '\0') {
315			/*
316			 * This was set when reading sv.conf.  After the last
317			 * line svn_path was set to \0, so we are finished.
318			 * We shouldn't get here, but put this in just in
319			 * case.
320			 */
321			break;
322		}
323		resume_dev(fd, &svn[cnt]);
324	}
325	(void) close(fd);
326}
327
328
329static void
330resume_dev(int fd, sv_name_t *svn)
331{
332	struct stat stb;
333	sv_conf_t svc;
334
335	bzero(&svc, sizeof (svc));
336
337	if (stat(svn->svn_path, &stb) != 0) {
338		warn(NULL, gettext("unable to access %s: %s"),
339			svn->svn_path, strerror(errno));
340		return;
341	}
342
343	svc.svc_major = major(stb.st_rdev);
344	svc.svc_minor = minor(stb.st_rdev);
345	(void) strncpy(svc.svc_path, svn->svn_path, sizeof (svc.svc_path));
346
347	svc.svc_flag = svn->svn_mode;
348	svc.svc_error = spcs_s_ucreate();
349
350	if (ioctl(fd, SVIOC_ENABLE, &svc) < 0) {
351		spcs_log("sv", &svc.svc_error,
352		    gettext("%s: unable to resume %s"),
353		    program, svn->svn_path);
354
355		warn(&svc.svc_error, gettext("unable to resume %s"),
356			svn->svn_path);
357		return;
358	}
359
360	spcs_log("sv", NULL, gettext("%s: resume %s"),
361	    program, svn->svn_path);
362
363	spcs_s_ufree(&svc.svc_error);
364}
365
366
367/*
368 * This routine parses the config file and
369 * stores the data in the svn array.  The return value is the number
370 * of entries read from conf_file.  If an error occurs the error()
371 * routine is called (which exits the program).
372 */
373static int
374read_libcfg(sv_name_t svn[])
375{
376	char rdev[CFG_MAX_BUF];
377	char key[CFG_MAX_KEY];
378	struct stat stb;
379	int i;
380	int setnumber;
381	int index = 0;		/* Current location in svn array	*/
382	sv_name_t *cur_svn;	/* Pointer to svn[index]		*/
383	CFGFILE *cfg;
384
385	if ((cfg = cfg_open("")) == NULL) {
386		error(NULL, gettext("Error opening config: %s"),
387		    strerror(errno));
388	}
389
390	cfg_resource(cfg, cfg_cluster_tag);
391	if (!cfg_lock(cfg, CFG_RDLOCK)) {
392		error(NULL, gettext("Error locking config: %s"),
393		    strerror(errno));
394	}
395
396	for (i = 0; /*CSTYLED*/; i++) {
397		setnumber = i + 1;
398
399		bzero(rdev, CFG_MAX_BUF);
400		(void) snprintf(key, sizeof (key), "sv.set%d.vol", setnumber);
401		if (cfg_get_cstring(cfg, key, rdev, sizeof (rdev)) < 0)
402			break;
403
404		/* Check to see if the raw device is present */
405		if (stat(rdev, &stb) != 0) {
406			warn(NULL, gettext("unable to access %s: %s"),
407			    rdev, strerror(errno));
408			continue;
409		}
410
411		if (!S_ISCHR(stb.st_mode)) {
412			warn(NULL, gettext("%s is not a character device"),
413			    rdev);
414			continue;
415		}
416
417		cur_svn = &svn[index];  /* For easier reading below */
418
419		if (strlen(rdev) >= sizeof (cur_svn->svn_path)) {
420			warn(NULL, gettext(
421			    "raw device name (%s) longer than %d characters"),
422			    rdev,
423			    (sizeof (cur_svn->svn_path) - 1));
424			continue;
425		}
426
427		(void) strcpy(cur_svn->svn_path, rdev);
428		cur_svn->svn_mode = (NSC_DEVICE | NSC_CACHE);
429
430		index++;
431	}
432
433	cfg_close(cfg);
434
435	/* Set the last path to NULL */
436	svn[index].svn_path[0] = '\0';
437
438	return (index);
439}
440
441
442static void
443suspend_dev(int fd, const caddr_t path)
444{
445	struct stat stb;
446	sv_conf_t svc;
447
448	if (stat(path, &stb) < 0) {
449		svc.svc_major = (major_t)-1;
450		svc.svc_minor = (minor_t)-1;
451	} else {
452		svc.svc_major = major(stb.st_rdev);
453		svc.svc_minor = minor(stb.st_rdev);
454	}
455
456	(void) strcpy(svc.svc_path, path);
457	svc.svc_error = spcs_s_ucreate();
458
459	if (ioctl(fd, SVIOC_DISABLE, &svc) < 0) {
460		if (errno != SV_EDISABLED) {
461			spcs_log("sv", &svc.svc_error,
462			    gettext("%s: unable to suspend %s"),
463			    program, path);
464
465			warn(&svc.svc_error,
466				gettext("unable to suspend %s"), path);
467			return;
468		}
469	}
470
471	spcs_log("sv", NULL, gettext("%s: suspend %s"), program, path);
472
473	spcs_s_ufree(&svc.svc_error);
474}
475
476
477static void
478suspend_sv(void)
479{
480	sv_name_t *svn, *svn_system;	/* Devices in system */
481	sv_list_t svl_system;
482	int i;
483	int fd;
484
485	svn_system = sv_alloc_svnames();
486
487	svl_system.svl_count = read_libcfg(svn_system);
488
489	if ((fd = open(sv_rpath, O_RDONLY)) < 0) {
490		warn(NULL, gettext("unable to open %s: %s"),
491			sv_rpath, strerror(errno));
492		return;
493	}
494
495	for (i = 0; i < svl_system.svl_count; i++) {
496		if (*svn_system[i].svn_path == '\0')
497			break;
498
499		svn = &svn_system[i];
500		suspend_dev(fd, svn->svn_path);
501	}
502
503	(void) close(fd);
504}
505
506
507/*
508 * Check kernel's sv_ndevices and thread sets,
509 * if empty then change kernel state to allow unload,
510 * and sleep SV_WAIT_UNLAOD (10 seconds).
511 *
512 * Only called in pkgrm time.
513 */
514static void
515prepare_unload_sv(void)
516{
517	int fd;
518	int rc = 0;
519
520	if ((fd = open(sv_rpath, O_RDONLY)) < 0) {
521		warn(NULL, gettext("unable to open %s: %s"),
522			sv_rpath, strerror(errno));
523		return;
524	}
525
526	if (ioctl(fd, SVIOC_UNLOAD, &rc) < 0)
527		error(NULL, gettext("unable to unload"));
528
529	if (rc != 0)
530		error(NULL, gettext("still has active devices or threads"));
531
532	(void) close(fd);
533}
534