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
51 static int sv_max_devices;
52
53
54 /*
55 * Pathnames.
56 */
57
58 static const caddr_t sv_rpath = SV_DEVICE;
59
60 /*
61 * Functions.
62 */
63
64 static void resume_dev(int, sv_name_t *);
65 static void suspend_dev(int, const caddr_t);
66 static int read_libcfg(sv_name_t svn[]);
67 static void resume_sv();
68 static void suspend_sv();
69 static 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
79 static caddr_t program;
80 static caddr_t cfg_cluster_tag;
81
82
83 static void
usage(void)84 usage(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
102 static void
message(caddr_t prefix,spcs_s_info_t * status,caddr_t string,va_list ap)103 message(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
116 static void
error(spcs_s_info_t * status,caddr_t string,...)117 error(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
129 static void
warn(spcs_s_info_t * status,caddr_t string,...)130 warn(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
141 static void
sv_get_maxdevs(void)142 sv_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
172 static sv_name_t *
sv_alloc_svnames(void)173 sv_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
188 int
main(int argc,char * argv[])189 main(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
290 static void
resume_sv()291 resume_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
329 static void
resume_dev(int fd,sv_name_t * svn)330 resume_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 */
373 static int
read_libcfg(sv_name_t svn[])374 read_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
442 static void
suspend_dev(int fd,const caddr_t path)443 suspend_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
477 static void
suspend_sv(void)478 suspend_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 */
514 static void
prepare_unload_sv(void)515 prepare_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