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
57typedef 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
67typedef struct {
68	dev_t	rdev;
69	mode_t  mode;
70	char	*path;
71} device_t;
72
73static hash_data_t *make_svol_data(char *, char *, char *, int);
74static hash_data_t *make_dsvol_data(char *, char *, char *, int);
75static void delete_svol_data(void *);
76static void delete_dsvol_data(void *);
77static int sv_action(char *, CFGFILE *, char *, int);
78
79static int add_dev_entry(const char *);
80static int compare(const void *, const void *);
81static char *find_devid(const char *);
82static void free_dev_entries();
83static void rebuild_devhash();
84
85static hash_node_t **dsvol;
86static int dsvol_loaded = 0;
87
88static hash_node_t **svol;
89static int svol_loaded = 0;
90
91static hash_node_t **shadowvol;
92
93static hash_node_t **devhash;
94static device_t *devlist;
95static int devcount = 0;
96static 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 */
120int
121cfg_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 */
280int
281cfg_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 */
480int
481cfg_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 */
527int
528cfg_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 */
583int
584cfg_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 */
674void
675cfg_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 */
698int
699cfg_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 */
765void
766cfg_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 */
793int
794cfg_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 */
842int
843cfg_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
912void
913cfg_unload_shadows()
914{
915	/* do nothing */
916}
917
918/* ---------------------------------------------------------------------- */
919
920static hash_data_t *
921make_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
938static void
939delete_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
949static hash_data_t *
950make_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
968static void
969delete_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
979static int
980sv_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
1124error:
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 */
1158static int
1159add_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
1207static void
1208rebuild_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
1224static int
1225compare(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
1233static char *
1234find_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
1265static void
1266free_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