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.
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.
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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <sys/types.h>
31 #include <sys/mkdev.h>
38 #include <sys/nsctl/cfg.h>
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>
45 #include <sys/nsctl/sv.h>
46 #include <sys/nsctl/nsc_hash.h>
54 * Utility functions for iiadm and rdcadm/sndradm.
57 typedef struct hash_data_s
{
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);
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();
85 static hash_node_t
**dsvol
;
86 static int dsvol_loaded
= 0;
88 static hash_node_t
**svol
;
89 static int svol_loaded
= 0;
91 static hash_node_t
**shadowvol
;
93 static hash_node_t
**devhash
;
94 static device_t
*devlist
;
95 static int devcount
= 0;
96 static int devalloc
= 0;
102 * Adds the calling tool as a user of the volume.
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.
111 * CFG_USER_FIRST: Indicates that this is the first user of this
113 * CFG_USER_OK: Indicates that the volume has already been entered into
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
121 cfg_add_user(CFGFILE
* cfg
, char *path
, char *cnode
, char *user
)
123 int self_open
, self_loaded
, change_made
;
124 char *ctag
, search_key
[ CFG_MAX_KEY
], buf
[ CFG_MAX_BUF
];
128 self_open
= (cfg
== NULL
);
133 cfg
= cfg_open(NULL
);
135 return (CFG_USER_ERR
);
138 if (!cfg_lock(cfg
, CFG_WRLOCK
)) {
141 return (CFG_USER_ERR
);
146 ctag
= cfg_get_resource(cfg
);
149 if (strcmp(cnode
, ctag
))
150 return (CFG_USER_ERR
);
152 cfg_resource(cfg
, cnode
);
157 if (cfg_load_dsvols(cfg
) < 0) {
161 return (CFG_USER_ERR
);
166 /* find the volume */
167 (void) snprintf(search_key
, CFG_MAX_KEY
, "%s:%s", path
, cnode
);
168 data
= nsc_lookup(dsvol
, search_key
);
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
,
175 rc
= cfg_put_cstring(cfg
, "dsvol", buf
, strlen(buf
));
183 return (CFG_USER_ERR
);
185 /* reload hash, if we need to */
188 if (cfg_load_dsvols(cfg
) < 0) {
192 return (CFG_USER_ERR
);
195 retval
= CFG_USER_FIRST
;
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)) {
204 free(p
); /* not using data; only testing 'q' ptr */
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",
213 if (cfg_put_cstring(cfg
, search_key
, buf
,
221 return (CFG_USER_ERR
);
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
232 if (cfg_load_dsvols(cfg
) < 0) {
236 return (CFG_USER_ERR
);
240 retval
= CFG_USER_OK
;
242 retval
= CFG_USER_REPEAT
;
252 (void) cfg_commit(cfg
);
263 * Removes a user from the config file.
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.
272 * CFG_USER_ERR: An error occurred during the processing of this
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.
281 cfg_rem_user(CFGFILE
*cfg
, char *path
, char *cnode
, char *user
)
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
];
290 self_open
= (cfg
== NULL
);
293 force_remove
= (strcmp(user
, "sv") == 0);
300 ctag
= cfg_get_resource(cfg
);
303 if (strcmp(cnode
, ctag
))
304 return (CFG_USER_ERR
);
306 cfg_resource(cfg
, cnode
);
311 cfg
= cfg_open(NULL
);
313 return (CFG_USER_ERR
);
316 if (!cfg_lock(cfg
, CFG_WRLOCK
)) {
319 return (CFG_USER_ERR
);
326 if (cfg_load_dsvols(cfg
) < 0) {
330 return (CFG_USER_ERR
);
335 /* find the volume */
336 (void) snprintf(search_key
, CFG_MAX_KEY
, "%s:%s", path
, cnode
);
337 data
= nsc_lookup(dsvol
, search_key
);
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",
347 if (cfg_put_cstring(cfg
, cfg_key
, NULL
, 0) < 0) {
354 return (CFG_USER_ERR
);
358 if (cfg_load_dsvols(cfg
) < 0) {
362 return (CFG_USER_ERR
);
366 char *p
= strdup(data
->u
.users
);
367 char *q
= strtok(p
, ",");
370 (void) snprintf(buf
, CFG_MAX_BUF
, "%s %s ", data
->path
,
372 while (q
&& (strcmp(q
, user
) != 0)) {
385 retval
= CFG_USER_GONE
;
387 /* old user skipped; add in remaining users */
388 while (q
= strtok(0, ", ")) {
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
,
411 return (CFG_USER_ERR
);
415 if (cfg_load_dsvols(cfg
) < 0) {
419 return (CFG_USER_ERR
);
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
,
435 return (CFG_USER_ERR
);
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
445 if (cfg_load_dsvols(cfg
) < 0) {
449 return (CFG_USER_ERR
);
463 (void) cfg_commit(cfg
);
471 * Enable a volume under SV control (or add this char *user to the list
472 * of users of that volume).
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.
481 cfg_vol_enable(CFGFILE
*cfg
, char *path
, char *ctag
, char *user
)
486 if (!ctag
|| *ctag
== '\0') {
491 rc
= cfg_add_user(cfg
, path
, ctag
, user
);
494 spcs_log("dsvol", NULL
,
495 gettext("unable to set up dsvol section of config for %s"),
503 retval
= sv_action(path
, cfg
, ctag
, DO_ENABLE
);
505 (void) cfg_rem_user(cfg
, path
, ctag
, user
);
509 spcs_log("dsvol", NULL
,
510 gettext("unexpected return from cfg_add_user(%d)"), rc
);
518 * Disable a volume from SV control (or remove this char *user from the list
519 * of users of that volume).
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.
528 cfg_vol_disable(CFGFILE
*cfg
, char *path
, char *ctag
, char *user
)
533 if (!ctag
|| *ctag
== '\0') {
538 rc
= cfg_rem_user(cfg
, path
, ctag
, user
);
541 spcs_log("dsvol", NULL
,
542 gettext("unable to set up dsvol section of config for %s"),
549 spcs_log("dsvol", NULL
,
550 gettext("%s tried to remove non-existent tag for %s"),
555 retval
= sv_action(path
, cfg
, ctag
, DO_DISABLE
);
558 spcs_log("dsvol", NULL
,
559 gettext("unexpected return from cfg_rem_user(%d)"), rc
);
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.
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
581 * -1 if error, or total number of sets found
584 cfg_load_dsvols(CFGFILE
*cfg
)
586 int set
, rc
, entries
;
587 char search_key
[ CFG_MAX_KEY
];
589 char **entry
, *path
, *cnode
, *users
;
593 char *ctag
= cfg_get_resource(cfg
);
594 if (!ctag
|| *ctag
== '\0') {
598 dsvol
= nsc_create_hash();
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];
609 /* split up the line */
610 if (!(path
= strtok(buf
, " "))) {
611 /* oops, now what? */
615 if (!(cnode
= strtok(0, " "))) {
619 if (ctag
&& (strcmp(cnode
, ctag
) != 0)) {
625 if (!(users
= strtok(0, " "))) {
630 data
= make_dsvol_data(path
, cnode
, users
, set
- offset
);
635 (void) snprintf(search_key
, CFG_MAX_KEY
, "%s:%s", path
, cnode
);
636 rc
= nsc_insert_node(dsvol
, data
, search_key
);
642 /* we also need to keep track of node information */
643 rc
= add_dev_entry(path
);
654 while (set
< entries
)
660 qsort(devlist
, devcount
, sizeof (device_t
), compare
);
665 return (rc
< 0? rc
: entries
);
672 * Free all memory allocated with cfg_load_dsvols.
678 nsc_remove_all(dsvol
, delete_dsvol_data
);
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()
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
699 cfg_load_svols(CFGFILE
*cfg
)
701 int set
, entries
, offset
= 0;
703 char *path
, *mode
, *cnode
;
705 char *ctag
= cfg_get_resource(cfg
);
706 if (!ctag
|| *ctag
== '\0') {
710 svol
= nsc_create_hash();
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];
720 /* split up the line */
721 if (!(path
= strtok(buf
, " "))) {
725 if (!(mode
= strtok(0, " "))) {
729 if (!(cnode
= strtok(0, " "))) {
733 if (ctag
&& (strcmp(cnode
, ctag
) != 0)) {
739 data
= make_svol_data(path
, mode
, cnode
, set
- offset
);
744 if (nsc_insert_node(svol
, data
, path
) < 0) {
750 while (set
< entries
)
763 * Frees all memory allocated with cfg_load_dsvols
769 nsc_remove_all(svol
, delete_svol_data
);
776 * cfg_get_canonical_name
779 * Find out whether a device is already known by another name in
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.
790 * 0: name is as expected, or is not known
791 * 1: Name is known by different name (stored in 'result')
794 cfg_get_canonical_name(CFGFILE
*cfg
, const char *path
, char **result
)
803 if (cfg_load_shadows(cfg
) < 0) {
809 /* see if it exists under a different name */
810 alt_path
= find_devid(path
);
811 if (!alt_path
|| strcmp(path
, alt_path
) == 0) {
816 *result
= strdup(alt_path
);
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.
836 * cfg - The config file to use
843 cfg_load_shadows(CFGFILE
*cfg
)
845 int set
, self_loaded
, rc
, entries
;
846 char *buf
, **entry
, *ptr
;
852 if (cfg_load_dsvols(cfg
) < 0) {
858 shadowvol
= nsc_create_hash();
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];
869 /* skip the master vol */
870 ptr
= strtok(buf
, " ");
873 ptr
= strtok(NULL
, " ");
875 rc
= add_dev_entry(ptr
);
882 /* and next is bitmap */
883 ptr
= strtok(NULL
, " ");
885 rc
= add_dev_entry(ptr
);
894 while (set
< entries
)
904 /* sort it, in preparation for lookups */
905 qsort(devlist
, devcount
, sizeof (device_t
), compare
);
918 /* ---------------------------------------------------------------------- */
921 make_dsvol_data(char *path
, char *cnode
, char *users
, int set
)
925 data
= (hash_data_t
*)malloc(sizeof (hash_data_t
));
930 data
->u
.users
= strdup(users
);
931 data
->path
= strdup(path
);
932 data
->node
= strdup(cnode
);
939 delete_dsvol_data(void *data
)
941 hash_data_t
*p
= (hash_data_t
*)data
;
950 make_svol_data(char *path
, char *mode
, char *cnode
, int set
)
954 data
= (hash_data_t
*)malloc(sizeof (hash_data_t
));
959 data
->u
.mode
= strdup(mode
);
960 data
->path
= strdup(path
);
961 data
->node
= strdup(cnode
);
969 delete_svol_data(void *data
)
971 hash_data_t
*p
= (hash_data_t
*)data
;
980 sv_action(char *path
, CFGFILE
*caller_cfg
, char *ctag
, int enable
)
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
];
994 device_t
*statinfo
= 0;
996 if (caller_cfg
== NULL
) {
997 cfg
= cfg_open(NULL
);
1002 cfg_resource(cfg
, ctag
);
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
));
1016 statinfo
= nsc_lookup(devhash
, path
);
1019 if (!S_ISCHR(statinfo
->mode
))
1021 svc
.svc_major
= major(statinfo
->rdev
);
1022 svc
.svc_minor
= minor(statinfo
->rdev
);
1024 if (stat(path
, &stb
) != 0)
1027 if (!S_ISCHR(stb
.st_mode
))
1029 svc
.svc_major
= major(stb
.st_rdev
);
1030 svc
.svc_minor
= minor(stb
.st_rdev
);
1033 strncpy(svc
.svc_path
, path
, sizeof (svc
.svc_path
));
1035 fd
= open(SV_DEVICE
, O_RDONLY
);
1039 svc
.svc_flag
= (NSC_DEVICE
| NSC_CACHE
);
1040 svc
.svc_error
= spcs_s_ucreate();
1043 rc
= ioctl(fd
, sv_ioctl
, &svc
);
1044 } while (rc
< 0 && errno
== EINTR
);
1047 if (errno
!= spcs_err
) {
1048 spcs_log("sv", &svc
.svc_error
, log_str2
, svc
.svc_path
);
1057 spcs_log("sv", NULL
, log_str1
, svc
.svc_path
);
1059 /* SV enable succeeded */
1060 if (caller_cfg
== NULL
) /* was not previously locked */
1061 if (!cfg_lock(cfg
, CFG_WRLOCK
))
1064 if (err
!= spcs_err
) { /* already enabled, already in config */
1066 cfg_rewind(cfg
, CFG_SEC_CONF
);
1067 (void) snprintf(buf
, CFG_MAX_BUF
, "%s - %s", path
,
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
);
1076 /* pull it out of the config */
1078 if (cfg_load_svols(cfg
) < 0) {
1079 if (NULL
== caller_cfg
) {
1086 node
= nsc_lookup(svol
, svc
.svc_path
);
1088 cfg_rewind(cfg
, CFG_SEC_CONF
);
1089 (void) snprintf(key
, CFG_MAX_KEY
, "sv.set%d",
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
);
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
1104 if (cfg_load_svols(cfg
) < 0) {
1105 if (NULL
== caller_cfg
) {
1121 (void) printf("extra line to shut lint up %s\n", module_names
[0]);
1132 if (caller_cfg
== NULL
) /* we opened config */
1133 (void) cfg_commit(cfg
);
1135 if (caller_cfg
== NULL
)
1137 if ((cfg_changed
) || (err
== spcs_err
))
1140 spcs_log("sv", NULL
,
1141 gettext("unable to add to configuration, disabled %s"),
1143 spcs_s_ufree(&svc
.svc_error
);
1151 * Add an entry into the devlist and the devhash for future lookups.
1154 * -1 An error occurred.
1156 * 1 Entry already exists.
1159 add_dev_entry(const char *path
)
1166 devhash
= nsc_create_hash();
1171 data
= nsc_lookup(devhash
, path
);
1177 if (stat(path
, &buf
) < 0) {
1178 /* ignore error, we are most likely deleting entry anyway */
1182 if (devcount
>= devalloc
) {
1183 /* make some room */
1184 devalloc
+= DEV_EXPAND
;
1185 newmem
= (device_t
*)realloc(devlist
, devalloc
*
1195 devlist
[ devcount
].path
= strdup(path
);
1196 devlist
[ devcount
].rdev
= buf
.st_rdev
;
1197 devlist
[ devcount
].mode
= buf
.st_mode
;
1199 if (nsc_insert_node(devhash
, &devlist
[devcount
], path
) < 0) {
1213 nsc_remove_all(devhash
, 0);
1215 devhash
= nsc_create_hash();
1219 for (i
= 0; i
< devcount
; i
++) {
1220 nsc_insert_node(devhash
, &devlist
[i
], devlist
[i
].path
);
1225 compare(const void *va
, const void *vb
)
1227 device_t
*a
= (device_t
*)va
;
1228 device_t
*b
= (device_t
*)vb
;
1230 return (b
->rdev
- a
->rdev
);
1234 find_devid(const char *path
)
1240 if (!devlist
|| !devhash
)
1243 /* See if we already know the device id by this name */
1244 result
= (device_t
*)nsc_lookup(devhash
, path
);
1249 /* try to find it by another name */
1250 if (stat(path
, &buf
) < 0)
1253 key
.rdev
= buf
.st_rdev
;
1255 /* it's storted, so we use the binary-chop method to find it */
1256 result
= bsearch(&key
, devlist
, devcount
, sizeof (device_t
), compare
);
1259 return (result
->path
);
1274 for (i
= 0, p
= devlist
; i
< devcount
; i
++, p
++) {
1283 nsc_remove_all(devhash
, 0);