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 <stdio.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <sys/stat.h>
31 #include <sys/mkdev.h>
32 #include <strings.h>
33 #include <stdarg.h>
34 #include <stdlib.h>
35 #include <locale.h>
36 #include <errno.h>
37
38 #include <sys/nsctl/cfg.h>
39
40 #include <sys/unistat/spcs_s.h>
41 #include <sys/unistat/spcs_s_u.h>
42 #include <sys/unistat/spcs_errors.h>
43 #include <sys/unistat/spcs_s_impl.h>
44
45 #include <sys/nsctl/sv.h>
46 #include <sys/nsctl/nsc_hash.h>
47
48 #define DEV_EXPAND 32
49
50 #define DO_DISABLE 0
51 #define DO_ENABLE 1
52
53 /*
54 * Utility functions for iiadm and rdcadm/sndradm.
55 */
56
57 typedef struct hash_data_s {
58 union {
59 char *users;
60 char *mode;
61 } u;
62 char *path;
63 char *node;
64 int setno;
65 } hash_data_t;
66
67 typedef struct {
68 dev_t rdev;
69 mode_t mode;
70 char *path;
71 } device_t;
72
73 static hash_data_t *make_svol_data(char *, char *, char *, int);
74 static hash_data_t *make_dsvol_data(char *, char *, char *, int);
75 static void delete_svol_data(void *);
76 static void delete_dsvol_data(void *);
77 static int sv_action(char *, CFGFILE *, char *, int);
78
79 static int add_dev_entry(const char *);
80 static int compare(const void *, const void *);
81 static char *find_devid(const char *);
82 static void free_dev_entries();
83 static void rebuild_devhash();
84
85 static hash_node_t **dsvol;
86 static int dsvol_loaded = 0;
87
88 static hash_node_t **svol;
89 static int svol_loaded = 0;
90
91 static hash_node_t **shadowvol;
92
93 static hash_node_t **devhash;
94 static device_t *devlist;
95 static int devcount = 0;
96 static int devalloc = 0;
97
98 /*
99 * cfg_add_user
100 *
101 * Description:
102 * Adds the calling tool as a user of the volume.
103 *
104 * Inputs:
105 * char *path: The pathname of the volume to be enabled.
106 * char *cnode: The device group name, or NULL if -C local or not cluster
107 * CFGFILE *cfg: A pointer to the current config file, or NULL if this
108 * function is to open/write/commit/close the change itself.
109 *
110 * Return values:
111 * CFG_USER_FIRST: Indicates that this is the first user of this
112 * particular volume.
113 * CFG_USER_OK: Indicates that the volume has already been entered into
114 * the config file.
115 * CFG_USER_ERR: Indicates that some failure has occurred and no changes
116 * to the config file have been made.
117 * CFG_USER_REPEAT: Indicates that this user has already registered for
118 * the volume.
119 */
120 int
cfg_add_user(CFGFILE * cfg,char * path,char * cnode,char * user)121 cfg_add_user(CFGFILE* cfg, char *path, char *cnode, char *user)
122 {
123 int self_open, self_loaded, change_made;
124 char *ctag, search_key[ CFG_MAX_KEY ], buf[ CFG_MAX_BUF ];
125 int retval, rc;
126 hash_data_t *data;
127
128 self_open = (cfg == NULL);
129 self_loaded = 0;
130 change_made = 0;
131
132 if (self_open) {
133 cfg = cfg_open(NULL);
134 if (cfg == NULL) {
135 return (CFG_USER_ERR);
136 }
137
138 if (!cfg_lock(cfg, CFG_WRLOCK)) {
139 /* oops */
140 cfg_close(cfg);
141 return (CFG_USER_ERR);
142 }
143 }
144
145 /* Check cnode */
146 ctag = cfg_get_resource(cfg);
147 if (cnode) {
148 if (ctag) {
149 if (strcmp(cnode, ctag))
150 return (CFG_USER_ERR);
151 } else
152 cfg_resource(cfg, cnode);
153 } else
154 cnode = ctag;
155
156 if (!dsvol_loaded) {
157 if (cfg_load_dsvols(cfg) < 0) {
158 if (self_open) {
159 cfg_close(cfg);
160 }
161 return (CFG_USER_ERR);
162 }
163 self_loaded = 1;
164 }
165
166 /* find the volume */
167 (void) snprintf(search_key, CFG_MAX_KEY, "%s:%s", path, cnode);
168 data = nsc_lookup(dsvol, search_key);
169
170 if (!data) {
171 /* whoops, not found. Add as new user */
172 cfg_rewind(cfg, CFG_SEC_CONF);
173 (void) snprintf(buf, CFG_MAX_BUF, "%s %s %s", path, cnode,
174 user);
175 rc = cfg_put_cstring(cfg, "dsvol", buf, strlen(buf));
176 if (rc < 0) {
177 if (self_loaded) {
178 cfg_unload_dsvols();
179 }
180 if (self_open) {
181 cfg_close(cfg);
182 }
183 return (CFG_USER_ERR);
184 }
185 /* reload hash, if we need to */
186 if (!self_loaded) {
187 cfg_unload_dsvols();
188 if (cfg_load_dsvols(cfg) < 0) {
189 if (self_open) {
190 cfg_close(cfg);
191 }
192 return (CFG_USER_ERR);
193 }
194 }
195 retval = CFG_USER_FIRST;
196 change_made = 1;
197 } else {
198 /* Check to ensure we're not already listed */
199 char *p = strdup(data->u.users);
200 char *q = strtok(p, ",");
201 while (q && (strcmp(q, user) != 0)) {
202 q = strtok(0, ",");
203 }
204 free(p); /* not using data; only testing 'q' ptr */
205
206 if (!q) {
207 /* not listed as a user */
208 cfg_rewind(cfg, CFG_SEC_CONF);
209 (void) snprintf(buf, CFG_MAX_BUF, "%s %s %s,%s",
210 data->path, data->node, data->u.users, user);
211 (void) snprintf(search_key, CFG_MAX_KEY, "dsvol.set%d",
212 data->setno);
213 if (cfg_put_cstring(cfg, search_key, buf,
214 strlen(buf)) < 0) {
215 if (self_loaded) {
216 cfg_unload_dsvols();
217 }
218 if (self_open) {
219 cfg_close(cfg);
220 }
221 return (CFG_USER_ERR);
222 }
223
224 /*
225 * Since we deleted an entry from the config
226 * file, we don't know what all the new
227 * set numbers are. We need to reload
228 * everything
229 */
230 if (!self_loaded) {
231 cfg_unload_dsvols();
232 if (cfg_load_dsvols(cfg) < 0) {
233 if (self_open) {
234 cfg_close(cfg);
235 }
236 return (CFG_USER_ERR);
237 }
238 }
239 change_made = 1;
240 retval = CFG_USER_OK;
241 } else {
242 retval = CFG_USER_REPEAT;
243 }
244 }
245
246 if (self_loaded) {
247 cfg_unload_dsvols();
248 }
249
250 if (self_open) {
251 if (change_made)
252 (void) cfg_commit(cfg);
253 cfg_close(cfg);
254 }
255
256 return (retval);
257 }
258
259 /*
260 * cfg_rem_user
261 *
262 * Description:
263 * Removes a user from the config file.
264 *
265 * Inputs:
266 * char *path: The pathname of the volume to be enabled.
267 * char *cnode: The device group name, or NULL if -C local or not cluster
268 * char *user: The subsystem that is adding this tag (sv, ii, sndr)
269 * CFGFILE *cfg: A pointer to the current config file, or NULL if this
270 * function is to open/write/commit/close the change itself.
271 * Return values:
272 * CFG_USER_ERR: An error occurred during the processing of this
273 * directive.
274 * CFG_USER_OK: User successfully removed; volume in use by other(s).
275 * CFG_USER_LAST: User successfuly removed; no other users registered
276 * CFG_USER_GONE: The volume is no longer listed in the dsvol section,
277 * indicating some sort of application-level error.
278 *
279 */
280 int
cfg_rem_user(CFGFILE * cfg,char * path,char * cnode,char * user)281 cfg_rem_user(CFGFILE *cfg, char *path, char *cnode, char *user)
282 {
283 int self_open, self_loaded, change_made;
284 char *ctag, search_key[ CFG_MAX_KEY ], buf[ CFG_MAX_BUF ];
285 char cfg_key[ CFG_MAX_KEY ];
286 hash_data_t *data;
287 int retval;
288 int force_remove;
289
290 self_open = (cfg == NULL);
291 self_loaded = 0;
292 change_made = 0;
293 force_remove = (strcmp(user, "sv") == 0);
294
295 if ('-' == *user) {
296 ++user;
297 }
298
299 /* Check cnode */
300 ctag = cfg_get_resource(cfg);
301 if (cnode) {
302 if (ctag) {
303 if (strcmp(cnode, ctag))
304 return (CFG_USER_ERR);
305 } else
306 cfg_resource(cfg, cnode);
307 } else
308 cnode = ctag;
309
310 if (self_open) {
311 cfg = cfg_open(NULL);
312 if (cfg == NULL) {
313 return (CFG_USER_ERR);
314 }
315
316 if (!cfg_lock(cfg, CFG_WRLOCK)) {
317 /* oops */
318 cfg_close(cfg);
319 return (CFG_USER_ERR);
320 }
321 }
322
323
324 change_made = 0;
325 if (!dsvol_loaded) {
326 if (cfg_load_dsvols(cfg) < 0) {
327 if (self_open) {
328 cfg_close(cfg);
329 }
330 return (CFG_USER_ERR);
331 }
332 self_loaded = 1;
333 }
334
335 /* find the volume */
336 (void) snprintf(search_key, CFG_MAX_KEY, "%s:%s", path, cnode);
337 data = nsc_lookup(dsvol, search_key);
338
339 if (!data) {
340 /* yipes */
341 retval = CFG_USER_GONE;
342 } else if (force_remove) {
343 retval = CFG_USER_LAST;
344 cfg_rewind(cfg, CFG_SEC_CONF);
345 (void) snprintf(cfg_key, CFG_MAX_KEY, "dsvol.set%d",
346 data->setno);
347 if (cfg_put_cstring(cfg, cfg_key, NULL, 0) < 0) {
348 if (self_loaded) {
349 cfg_unload_dsvols();
350 }
351 if (self_open) {
352 cfg_close(cfg);
353 }
354 return (CFG_USER_ERR);
355 }
356 if (!self_loaded) {
357 cfg_unload_dsvols();
358 if (cfg_load_dsvols(cfg) < 0) {
359 if (self_open) {
360 cfg_close(cfg);
361 }
362 return (CFG_USER_ERR);
363 }
364 }
365 } else {
366 char *p = strdup(data->u.users);
367 char *q = strtok(p, ",");
368 int appended = 0;
369
370 (void) snprintf(buf, CFG_MAX_BUF, "%s %s ", data->path,
371 data->node);
372 while (q && (strcmp(q, user) != 0)) {
373 if (appended) {
374 strcat(buf, ",");
375 strcat(buf, q);
376 } else {
377 strcat(buf, q);
378 appended = 1;
379 }
380 q = strtok(0, ",");
381 }
382
383 if (!q) {
384 /* uh-oh */
385 retval = CFG_USER_GONE;
386 } else {
387 /* old user skipped; add in remaining users */
388 while (q = strtok(0, ", ")) {
389 if (appended) {
390 strcat(buf, ",");
391 strcat(buf, q);
392 } else {
393 strcat(buf, q);
394 appended = 1;
395 }
396 }
397
398 if (appended) {
399 retval = CFG_USER_OK;
400 cfg_rewind(cfg, CFG_SEC_CONF);
401 (void) snprintf(cfg_key, CFG_MAX_KEY,
402 "dsvol.set%d", data->setno);
403 if (cfg_put_cstring(cfg, cfg_key, buf,
404 strlen(buf)) < 0) {
405 if (self_loaded) {
406 cfg_unload_dsvols();
407 }
408 if (self_open) {
409 cfg_close(cfg);
410 }
411 return (CFG_USER_ERR);
412 }
413 if (!self_loaded) {
414 cfg_unload_dsvols();
415 if (cfg_load_dsvols(cfg) < 0) {
416 if (self_open) {
417 cfg_close(cfg);
418 }
419 return (CFG_USER_ERR);
420 }
421 }
422 } else {
423 retval = CFG_USER_LAST;
424 cfg_rewind(cfg, CFG_SEC_CONF);
425 (void) snprintf(cfg_key, CFG_MAX_KEY,
426 "dsvol.set%d", data->setno);
427 if (cfg_put_cstring(cfg, cfg_key, NULL,
428 0) < 0) {
429 if (self_loaded) {
430 cfg_unload_dsvols();
431 }
432 if (self_open) {
433 cfg_close(cfg);
434 }
435 return (CFG_USER_ERR);
436 }
437 /*
438 * Since we deleted an entry from the config
439 * file, we don't know what all the new
440 * set numbers are. We need to reload
441 * everything
442 */
443 if (!self_loaded) {
444 cfg_unload_dsvols();
445 if (cfg_load_dsvols(cfg) < 0) {
446 if (self_open) {
447 cfg_close(cfg);
448 }
449 return (CFG_USER_ERR);
450 }
451 }
452 }
453 change_made = 1;
454 }
455 }
456
457 if (self_loaded) {
458 cfg_unload_dsvols();
459 }
460
461 if (self_open) {
462 if (change_made)
463 (void) cfg_commit(cfg);
464 cfg_close(cfg);
465 }
466
467 return (retval);
468 }
469
470 /*
471 * Enable a volume under SV control (or add this char *user to the list
472 * of users of that volume).
473 *
474 * Parameters:
475 * cfg - The config file to use.
476 * path - The pathname of the volume
477 * ctag - The cluster tag for this volume (if any)
478 * user - The user (sv, ii, sndr) of the volume.
479 */
480 int
cfg_vol_enable(CFGFILE * cfg,char * path,char * ctag,char * user)481 cfg_vol_enable(CFGFILE *cfg, char *path, char *ctag, char *user)
482 {
483 int rc;
484 int retval;
485
486 if (!ctag || *ctag == '\0') {
487 ctag = "-";
488 }
489
490 retval = -1;
491 rc = cfg_add_user(cfg, path, ctag, user);
492 switch (rc) {
493 case CFG_USER_ERR:
494 spcs_log("dsvol", NULL,
495 gettext("unable to set up dsvol section of config for %s"),
496 path);
497 break;
498 case CFG_USER_OK:
499 retval = 0;
500 break;
501 case CFG_USER_FIRST:
502 /* enable sv! */
503 retval = sv_action(path, cfg, ctag, DO_ENABLE);
504 if (retval < 0) {
505 (void) cfg_rem_user(cfg, path, ctag, user);
506 }
507 break;
508 default:
509 spcs_log("dsvol", NULL,
510 gettext("unexpected return from cfg_add_user(%d)"), rc);
511 break;
512 }
513
514 return (retval);
515 }
516
517 /*
518 * Disable a volume from SV control (or remove this char *user from the list
519 * of users of that volume).
520 *
521 * Parameters:
522 * cfg - The config file to use.
523 * path - The pathname of the volume
524 * ctag - The cluster tag for this volume (if any)
525 * user - The user (sv, ii, sndr) of the volume.
526 */
527 int
cfg_vol_disable(CFGFILE * cfg,char * path,char * ctag,char * user)528 cfg_vol_disable(CFGFILE *cfg, char *path, char *ctag, char *user)
529 {
530 int rc;
531 int retval;
532
533 if (!ctag || *ctag == '\0') {
534 ctag = "-";
535 }
536
537 retval = -1;
538 rc = cfg_rem_user(cfg, path, ctag, user);
539 switch (rc) {
540 case CFG_USER_ERR:
541 spcs_log("dsvol", NULL,
542 gettext("unable to set up dsvol section of config for %s"),
543 path);
544 break;
545 case CFG_USER_OK:
546 retval = 0;
547 break;
548 case CFG_USER_GONE:
549 spcs_log("dsvol", NULL,
550 gettext("%s tried to remove non-existent tag for %s"),
551 user, path);
552 break;
553 case CFG_USER_LAST:
554 /* diable sv! */
555 retval = sv_action(path, cfg, ctag, DO_DISABLE);
556 break;
557 default:
558 spcs_log("dsvol", NULL,
559 gettext("unexpected return from cfg_rem_user(%d)"), rc);
560 break;
561 }
562
563 return (retval);
564 }
565
566 /*
567 * cfg_load_dsvols
568 *
569 * Description:
570 * Loads the dsvol section of the config file into a giant hash, to
571 * make searching faster. The important bit to remember is to not
572 * release the write lock between calling cfg_load_dsvols() and the
573 * cfg_*_user() functions.
574 *
575 * Assumptions:
576 * 1/ cfg file is open
577 * 2/ cfg file has been write-locked
578 * 3/ user of this routine may already be using hcreate/hsearch
579 *
580 * Return value:
581 * -1 if error, or total number of sets found
582 */
583 int
cfg_load_dsvols(CFGFILE * cfg)584 cfg_load_dsvols(CFGFILE *cfg)
585 {
586 int set, rc, entries;
587 char search_key[ CFG_MAX_KEY ];
588 char *buf;
589 char **entry, *path, *cnode, *users;
590 hash_data_t *data;
591 int devs_added = 0;
592 int offset = 0;
593 char *ctag = cfg_get_resource(cfg);
594 if (!ctag || *ctag == '\0') {
595 ctag = "-";
596 }
597
598 dsvol = nsc_create_hash();
599 if (!dsvol) {
600 return (-1);
601 }
602
603 rc = 0;
604 cfg_rewind(cfg, CFG_SEC_CONF);
605 entries = cfg_get_section(cfg, &entry, "dsvol");
606 for (set = 1; set <= entries; set++) {
607 buf = entry[set - 1];
608
609 /* split up the line */
610 if (!(path = strtok(buf, " "))) {
611 /* oops, now what? */
612 free(buf);
613 break;
614 }
615 if (!(cnode = strtok(0, " "))) {
616 free(buf);
617 break;
618 }
619 if (ctag && (strcmp(cnode, ctag) != 0)) {
620 ++offset;
621 free(buf);
622 continue;
623 }
624
625 if (!(users = strtok(0, " "))) {
626 free(buf);
627 break;
628 }
629
630 data = make_dsvol_data(path, cnode, users, set - offset);
631 if (!data) {
632 free(buf);
633 break;
634 }
635 (void) snprintf(search_key, CFG_MAX_KEY, "%s:%s", path, cnode);
636 rc = nsc_insert_node(dsvol, data, search_key);
637 if (rc < 0) {
638 free(buf);
639 break;
640 }
641
642 /* we also need to keep track of node information */
643 rc = add_dev_entry(path);
644 if (rc < 0) {
645 free(buf);
646 break;
647 } else if (rc)
648 ++devs_added;
649
650 free(buf);
651 rc = 0;
652 }
653
654 while (set < entries)
655 free(entry[set++]);
656 if (entries)
657 free(entry);
658
659 if (devs_added) {
660 qsort(devlist, devcount, sizeof (device_t), compare);
661 rebuild_devhash();
662 }
663
664 dsvol_loaded = 1;
665 return (rc < 0? rc : entries);
666 }
667
668 /*
669 * cfg_unload_dsvols
670 *
671 * Description:
672 * Free all memory allocated with cfg_load_dsvols.
673 */
674 void
cfg_unload_dsvols()675 cfg_unload_dsvols()
676 {
677 if (dsvol) {
678 nsc_remove_all(dsvol, delete_dsvol_data);
679 dsvol = 0;
680 dsvol_loaded = 0;
681 }
682 }
683
684 /*
685 * cfg_load_svols
686 *
687 * Description:
688 * Loads the sv section of the config file into a giant hash, to make
689 * searching faster. The important bit to remember is to not release
690 * the write lock between calling cfg_load_svols() and the cfg_*_user()
691 * functions.
692 *
693 * Assumptions:
694 * 1/ cfg file is open
695 * 2/ cfg file has been write-locked
696 * 3/ user of this routine may already be using builtin hcreate/hsearch
697 */
698 int
cfg_load_svols(CFGFILE * cfg)699 cfg_load_svols(CFGFILE *cfg)
700 {
701 int set, entries, offset = 0;
702 char *buf, **entry;
703 char *path, *mode, *cnode;
704 hash_data_t *data;
705 char *ctag = cfg_get_resource(cfg);
706 if (!ctag || *ctag == '\0') {
707 ctag = "-";
708 }
709
710 svol = nsc_create_hash();
711 if (!svol) {
712 return (-1);
713 }
714
715 cfg_rewind(cfg, CFG_SEC_CONF);
716 entries = cfg_get_section(cfg, &entry, "sv");
717 for (set = 1; set <= entries; set++) {
718 buf = entry[set - 1];
719
720 /* split up the line */
721 if (!(path = strtok(buf, " "))) {
722 free(buf);
723 break;
724 }
725 if (!(mode = strtok(0, " "))) {
726 free(buf);
727 break;
728 }
729 if (!(cnode = strtok(0, " "))) {
730 cnode = "";
731 }
732
733 if (ctag && (strcmp(cnode, ctag) != 0)) {
734 ++offset;
735 free(buf);
736 continue;
737 }
738
739 data = make_svol_data(path, mode, cnode, set - offset);
740 if (!data) {
741 free(buf);
742 break;
743 }
744 if (nsc_insert_node(svol, data, path) < 0) {
745 free(buf);
746 break;
747 }
748 free(buf);
749 }
750 while (set < entries)
751 free(entry[set++]);
752 if (entries)
753 free(entry);
754
755 svol_loaded = 1;
756 return (0);
757 }
758
759 /*
760 * cfg_unload_svols
761 *
762 * Description:
763 * Frees all memory allocated with cfg_load_dsvols
764 */
765 void
cfg_unload_svols()766 cfg_unload_svols()
767 {
768 if (svol) {
769 nsc_remove_all(svol, delete_svol_data);
770 svol = 0;
771 svol_loaded = 0;
772 }
773 }
774
775 /*
776 * cfg_get_canonical_name
777 *
778 * Description:
779 * Find out whether a device is already known by another name in
780 * the config file.
781 *
782 * Parameters:
783 * cfg - The config file to use
784 * path - The pathname of the device
785 * result - (output) The name it is otherwise known as. This parameter
786 * must be freed by the caller.
787 *
788 * Return values:
789 * -1: error
790 * 0: name is as expected, or is not known
791 * 1: Name is known by different name (stored in 'result')
792 */
793 int
cfg_get_canonical_name(CFGFILE * cfg,const char * path,char ** result)794 cfg_get_canonical_name(CFGFILE *cfg, const char *path, char **result)
795 {
796 int self_loaded;
797 char *alt_path;
798 int retval;
799
800 if (devlist) {
801 self_loaded = 0;
802 } else {
803 if (cfg_load_shadows(cfg) < 0) {
804 return (-1);
805 }
806 self_loaded = 1;
807 }
808
809 /* see if it exists under a different name */
810 alt_path = find_devid(path);
811 if (!alt_path || strcmp(path, alt_path) == 0) {
812 *result = NULL;
813 retval = 0;
814 } else {
815 /* a-ha */
816 *result = strdup(alt_path);
817 retval = 1;
818 }
819
820 if (self_loaded) {
821 free_dev_entries();
822 }
823
824 return (retval);
825 }
826
827 /*
828 * cfg_load_shadows
829 *
830 * Description:
831 * Load in shadow and bitmap volumes from the II section of the
832 * config file. SNDR's volumes are handled already by cfg_load_dsvols.
833 * Not all shadow volumes are listed under dsvol: they can be exported.
834 *
835 * Parameters:
836 * cfg - The config file to use
837 *
838 * Return values:
839 * -1: error
840 * 0: success
841 */
842 int
cfg_load_shadows(CFGFILE * cfg)843 cfg_load_shadows(CFGFILE *cfg)
844 {
845 int set, self_loaded, rc, entries;
846 char *buf, **entry, *ptr;
847 int devs_added = 0;
848
849 if (dsvol_loaded) {
850 self_loaded = 0;
851 } else {
852 if (cfg_load_dsvols(cfg) < 0) {
853 return (-1);
854 }
855 self_loaded = 1;
856 }
857
858 shadowvol = nsc_create_hash();
859 if (!shadowvol) {
860 return (-1);
861 }
862
863 rc = 0;
864 cfg_rewind(cfg, CFG_SEC_CONF);
865 entries = cfg_get_section(cfg, &entry, "ii");
866 for (set = 1; set <= entries; set++) {
867 buf = entry[set - 1];
868
869 /* skip the master vol */
870 ptr = strtok(buf, " ");
871
872 /* shadow is next */
873 ptr = strtok(NULL, " ");
874
875 rc = add_dev_entry(ptr);
876 if (rc < 0) {
877 free(buf);
878 break;
879 } else if (rc)
880 ++devs_added;
881
882 /* and next is bitmap */
883 ptr = strtok(NULL, " ");
884
885 rc = add_dev_entry(ptr);
886 if (rc < 0) {
887 free(buf);
888 break;
889 } else if (rc)
890 ++devs_added;
891 rc = 0;
892 free(buf);
893 }
894 while (set < entries)
895 free(entry[set++]);
896 if (entries)
897 free(entry);
898
899 if (self_loaded) {
900 cfg_unload_dsvols();
901 }
902
903 if (devs_added) {
904 /* sort it, in preparation for lookups */
905 qsort(devlist, devcount, sizeof (device_t), compare);
906 rebuild_devhash();
907 }
908
909 return (rc);
910 }
911
912 void
cfg_unload_shadows()913 cfg_unload_shadows()
914 {
915 /* do nothing */
916 }
917
918 /* ---------------------------------------------------------------------- */
919
920 static hash_data_t *
make_dsvol_data(char * path,char * cnode,char * users,int set)921 make_dsvol_data(char *path, char *cnode, char *users, int set)
922 {
923 hash_data_t *data;
924
925 data = (hash_data_t *)malloc(sizeof (hash_data_t));
926 if (!data) {
927 return (0);
928 }
929
930 data->u.users = strdup(users);
931 data->path = strdup(path);
932 data->node = strdup(cnode);
933 data->setno = set;
934
935 return (data);
936 }
937
938 static void
delete_dsvol_data(void * data)939 delete_dsvol_data(void *data)
940 {
941 hash_data_t *p = (hash_data_t *)data;
942
943 free(p->u.users);
944 free(p->path);
945 free(p->node);
946 free(p);
947 }
948
949 static hash_data_t *
make_svol_data(char * path,char * mode,char * cnode,int set)950 make_svol_data(char *path, char *mode, char *cnode, int set)
951 {
952 hash_data_t *data;
953
954 data = (hash_data_t *)malloc(sizeof (hash_data_t));
955 if (!data) {
956 return (0);
957 }
958
959 data->u.mode = strdup(mode);
960 data->path = strdup(path);
961 data->node = strdup(cnode);
962 data->setno = set;
963
964 return (data);
965 }
966
967
968 static void
delete_svol_data(void * data)969 delete_svol_data(void *data)
970 {
971 hash_data_t *p = (hash_data_t *)data;
972
973 free(p->u.mode);
974 free(p->path);
975 free(p->node);
976 free(p);
977 }
978
979 static int
sv_action(char * path,CFGFILE * caller_cfg,char * ctag,int enable)980 sv_action(char *path, CFGFILE *caller_cfg, char *ctag, int enable)
981 {
982 struct stat stb;
983 sv_conf_t svc;
984 int fd = -1;
985 int cfg_changed = 0;
986 CFGFILE *cfg;
987 int print_log = 0;
988 int err = 0, rc;
989 int sv_ioctl, spcs_err, self_loaded;
990 char *log_str1, *log_str2;
991 char key[ CFG_MAX_KEY ];
992 char buf[ CFG_MAX_BUF ];
993 hash_data_t *node;
994 device_t *statinfo = 0;
995
996 if (caller_cfg == NULL) {
997 cfg = cfg_open(NULL);
998 if (cfg == NULL)
999 return (-1);
1000
1001 if (ctag)
1002 cfg_resource(cfg, ctag);
1003 } else
1004 cfg = caller_cfg;
1005
1006
1007 self_loaded = 0;
1008 sv_ioctl = (enable? SVIOC_ENABLE : SVIOC_DISABLE);
1009 log_str1 = (enable? gettext("enabled %s") : gettext("disabled %s"));
1010 log_str2 = (enable? gettext("unable to enable %s") :
1011 gettext("unable to disable %s"));
1012 spcs_err = (enable? SV_EENABLED : SV_EDISABLED);
1013 bzero(&svc, sizeof (svc));
1014
1015 if (devhash)
1016 statinfo = nsc_lookup(devhash, path);
1017
1018 if (statinfo) {
1019 if (!S_ISCHR(statinfo->mode))
1020 goto error;
1021 svc.svc_major = major(statinfo->rdev);
1022 svc.svc_minor = minor(statinfo->rdev);
1023 } else {
1024 if (stat(path, &stb) != 0)
1025 goto error;
1026
1027 if (!S_ISCHR(stb.st_mode))
1028 goto error;
1029 svc.svc_major = major(stb.st_rdev);
1030 svc.svc_minor = minor(stb.st_rdev);
1031 }
1032
1033 strncpy(svc.svc_path, path, sizeof (svc.svc_path));
1034
1035 fd = open(SV_DEVICE, O_RDONLY);
1036 if (fd < 0)
1037 goto error;
1038
1039 svc.svc_flag = (NSC_DEVICE | NSC_CACHE);
1040 svc.svc_error = spcs_s_ucreate();
1041
1042 do {
1043 rc = ioctl(fd, sv_ioctl, &svc);
1044 } while (rc < 0 && errno == EINTR);
1045
1046 if (rc < 0) {
1047 if (errno != spcs_err) {
1048 spcs_log("sv", &svc.svc_error, log_str2, svc.svc_path);
1049 if (enable)
1050 goto error;
1051 else
1052 err = errno;
1053 } else
1054 err = spcs_err;
1055 }
1056
1057 spcs_log("sv", NULL, log_str1, svc.svc_path);
1058
1059 /* SV enable succeeded */
1060 if (caller_cfg == NULL) /* was not previously locked */
1061 if (!cfg_lock(cfg, CFG_WRLOCK))
1062 goto error;
1063
1064 if (err != spcs_err) { /* already enabled, already in config */
1065 if (enable) {
1066 cfg_rewind(cfg, CFG_SEC_CONF);
1067 (void) snprintf(buf, CFG_MAX_BUF, "%s - %s", path,
1068 ctag? ctag : "-");
1069 if (cfg_put_cstring(cfg, "sv", buf, CFG_MAX_BUF) < 0) {
1070 /* SV config not updated, so SV disable again */
1071 (void) ioctl(fd, SVIOC_DISABLE, &svc);
1072 print_log++;
1073 } else
1074 cfg_changed = 1;
1075 } else {
1076 /* pull it out of the config */
1077 if (!svol_loaded) {
1078 if (cfg_load_svols(cfg) < 0) {
1079 if (NULL == caller_cfg) {
1080 cfg_close(cfg);
1081 }
1082 return (-1);
1083 }
1084 self_loaded = 1;
1085 }
1086 node = nsc_lookup(svol, svc.svc_path);
1087 if (node) {
1088 cfg_rewind(cfg, CFG_SEC_CONF);
1089 (void) snprintf(key, CFG_MAX_KEY, "sv.set%d",
1090 node->setno);
1091 if (cfg_put_cstring(cfg, key, NULL, NULL) < 0) {
1092 spcs_log("sv", NULL,
1093 gettext("failed to remove %s from "
1094 "sv config"), svc.svc_path);
1095 }
1096 /*
1097 * Since we deleted an entry from the config
1098 * file, we don't know what all the new
1099 * set numbers are. We need to reload
1100 * everything
1101 */
1102 if (!self_loaded) {
1103 cfg_unload_svols();
1104 if (cfg_load_svols(cfg) < 0) {
1105 if (NULL == caller_cfg) {
1106 cfg_close(cfg);
1107 }
1108 return (-1);
1109 }
1110 }
1111 cfg_changed = 1;
1112 }
1113 if (self_loaded) {
1114 cfg_unload_svols();
1115 self_loaded = 0;
1116 }
1117 }
1118 }
1119
1120 #ifdef lint
1121 (void) printf("extra line to shut lint up %s\n", module_names[0]);
1122 #endif
1123
1124 error:
1125 if (fd >= 0)
1126 (void) close(fd);
1127
1128 if (cfg == NULL)
1129 return (-1);
1130
1131 if (cfg_changed)
1132 if (caller_cfg == NULL) /* we opened config */
1133 (void) cfg_commit(cfg);
1134
1135 if (caller_cfg == NULL)
1136 cfg_close(cfg);
1137 if ((cfg_changed) || (err == spcs_err))
1138 return (1);
1139 if (print_log)
1140 spcs_log("sv", NULL,
1141 gettext("unable to add to configuration, disabled %s"),
1142 svc.svc_path);
1143 spcs_s_ufree(&svc.svc_error);
1144
1145 return (-1);
1146 }
1147
1148 /*
1149 * add_dev_entry
1150 *
1151 * Add an entry into the devlist and the devhash for future lookups.
1152 *
1153 * Return values:
1154 * -1 An error occurred.
1155 * 0 Entry added
1156 * 1 Entry already exists.
1157 */
1158 static int
add_dev_entry(const char * path)1159 add_dev_entry(const char *path)
1160 {
1161 struct stat buf;
1162 device_t *newmem;
1163 hash_data_t *data;
1164
1165 if (!devhash) {
1166 devhash = nsc_create_hash();
1167 if (!devhash) {
1168 return (-1);
1169 }
1170 } else {
1171 data = nsc_lookup(devhash, path);
1172 if (data) {
1173 return (1);
1174 }
1175 }
1176
1177 if (stat(path, &buf) < 0) {
1178 /* ignore error, we are most likely deleting entry anyway */
1179 buf.st_rdev = 0;
1180 }
1181
1182 if (devcount >= devalloc) {
1183 /* make some room */
1184 devalloc += DEV_EXPAND;
1185 newmem = (device_t *)realloc(devlist, devalloc *
1186 sizeof (device_t));
1187 if (!newmem) {
1188 free_dev_entries();
1189 return (-1);
1190 } else {
1191 devlist = newmem;
1192 }
1193 }
1194
1195 devlist[ devcount ].path = strdup(path);
1196 devlist[ devcount ].rdev = buf.st_rdev;
1197 devlist[ devcount ].mode = buf.st_mode;
1198
1199 if (nsc_insert_node(devhash, &devlist[devcount], path) < 0) {
1200 return (-1);
1201 }
1202
1203 ++devcount;
1204 return (0);
1205 }
1206
1207 static void
rebuild_devhash()1208 rebuild_devhash()
1209 {
1210 int i;
1211
1212 if (!devhash)
1213 nsc_remove_all(devhash, 0);
1214
1215 devhash = nsc_create_hash();
1216 if (!devhash)
1217 return;
1218
1219 for (i = 0; i < devcount; i++) {
1220 nsc_insert_node(devhash, &devlist[i], devlist[i].path);
1221 }
1222 }
1223
1224 static int
compare(const void * va,const void * vb)1225 compare(const void *va, const void *vb)
1226 {
1227 device_t *a = (device_t *)va;
1228 device_t *b = (device_t *)vb;
1229
1230 return (b->rdev - a->rdev);
1231 }
1232
1233 static char *
find_devid(const char * path)1234 find_devid(const char *path)
1235 {
1236 device_t key;
1237 device_t *result;
1238 struct stat buf;
1239
1240 if (!devlist || !devhash)
1241 return (NULL);
1242
1243 /* See if we already know the device id by this name */
1244 result = (device_t *)nsc_lookup(devhash, path);
1245 if (result) {
1246 return (NULL);
1247 }
1248
1249 /* try to find it by another name */
1250 if (stat(path, &buf) < 0)
1251 return (NULL);
1252
1253 key.rdev = buf.st_rdev;
1254
1255 /* it's storted, so we use the binary-chop method to find it */
1256 result = bsearch(&key, devlist, devcount, sizeof (device_t), compare);
1257
1258 if (result) {
1259 return (result->path);
1260 }
1261
1262 return (NULL);
1263 }
1264
1265 static void
free_dev_entries()1266 free_dev_entries()
1267 {
1268 int i;
1269 device_t *p;
1270
1271 if (!devlist) {
1272 return;
1273 }
1274 for (i = 0, p = devlist; i < devcount; i++, p++) {
1275 free(p->path);
1276 }
1277 free(devlist);
1278 devlist = NULL;
1279 devcount = 0;
1280 devalloc = 0;
1281
1282 if (devhash) {
1283 nsc_remove_all(devhash, 0);
1284 devhash = NULL;
1285 }
1286 }
1287