void tgt_node_replace(tgt_node_t *parent, tgt_node_t *child, match_type_t m) { tgt_node_t *s; tgt_node_t *c; if ((parent == NULL) || (child == NULL)) return; for (s = parent->x_child; s; s = s->x_sibling) { /* * See if the new child node matches one of the children * in the parent. */ if ((strcmp(s->x_name, child->x_name) == 0) && ((m == MatchName) || (strcmp(s->x_value, child->x_value) == 0))) { /* * We have a match. Now save the values of the new * child in this current node. */ free(s->x_name); free(s->x_value); s->x_name = strdup(child->x_name); s->x_value = strdup(child->x_value); if (s->x_child) { tgt_node_free(s->x_child); s->x_child = NULL; } for (c = child->x_child; c; c = c->x_sibling) (void) tgt_node_add(s, tgt_node_dup(c)); break; } } if (s == NULL) { /* * Never found the child so add it */ (void) tgt_node_add(parent, tgt_node_dup(child)); } }
tgt_node_t * tgt_node_dup(tgt_node_t *n) { tgt_node_t *d = node_alloc(); tgt_node_t *c; if (d == NULL) return (NULL); if (node_name(d, (xmlChar *)n->x_name) == False) return (NULL); if (n->x_value && (node_value(d, (xmlChar *)n->x_value, True) == False)) return (NULL); for (c = n->x_child; c; c = c->x_sibling) (void) tgt_node_add(d, tgt_node_dup(c)); for (c = n->x_attr; c; c = c->x_sibling) (void) tgt_node_add_attr(d, tgt_node_dup(c)); return (d); }
/* * this function tries to convert configuration in files into scf * it loads xml conf into node tree then dump them to scf with * mgmt_config_save2scf() * this function has 3 return values: * CONVERT_OK: successfully converted * CONVERT_INIT_NEW: configuration files dont exist, created a new scf entry * CONVERT_FAIL: some error occurred in conversion and no scf entry created. * In this case, user have to check files manually and try * conversion again. */ convert_ret_t mgmt_convert_conf() { targ_scf_t *h = NULL; xmlTextReaderPtr r; convert_ret_t ret = CONVERT_FAIL; int xml_fd = -1; int n; tgt_node_t *node = NULL; tgt_node_t *next = NULL; char path[MAXPATHLEN]; char *target = NULL; h = mgmt_handle_init(); if (h == NULL) return (CONVERT_FAIL); /* check main config in pgroup iscsitgt */ if (scf_service_get_pg(h->t_service, "iscsitgt", h->t_pg) == 0) { ret = CONVERT_OK; goto done; } /* check the conf files */ if (access(config_file, R_OK) != 0) { /* * if there is no configuration file, initialize * an empty scf entry */ if (mgmt_transaction_start(h, "iscsitgt", "basic") == True) { ret = CONVERT_INIT_NEW; node = tgt_node_alloc(XML_ELEMENT_VERS, String, "1.0"); new_property(h, node); tgt_node_free(node); /* "daemonize" is set to true by default */ node = tgt_node_alloc(XML_ELEMENT_DBGDAEMON, String, "true"); new_property(h, node); tgt_node_free(node); node = NULL; node = tgt_node_alloc(ISCSI_MODIFY_AUTHNAME, String, ISCSI_AUTH_MODIFY); new_property(h, node); tgt_node_free(node); node = tgt_node_alloc(ISCSI_VALUE_AUTHNAME, String, ISCSI_AUTH_VALUE); new_property(h, node); tgt_node_free(node); mgmt_transaction_end(h); } else { syslog(LOG_ERR, "Creating empty entry failed"); ret = CONVERT_FAIL; goto done; } if (mgmt_transaction_start(h, "passwords", "application") == True) { node = tgt_node_alloc(ISCSI_READ_AUTHNAME, String, ISCSI_AUTH_READ); new_property(h, node); tgt_node_free(node); node = tgt_node_alloc(ISCSI_MODIFY_AUTHNAME, String, ISCSI_AUTH_MODIFY); new_property(h, node); tgt_node_free(node); node = tgt_node_alloc(ISCSI_VALUE_AUTHNAME, String, ISCSI_AUTH_VALUE); new_property(h, node); tgt_node_free(node); mgmt_transaction_end(h); } else { syslog(LOG_ERR, "Creating empty entry failed"); ret = CONVERT_FAIL; } goto done; } if ((xml_fd = open(config_file, O_RDONLY)) >= 0) r = (xmlTextReaderPtr)xmlReaderForFd(xml_fd, NULL, NULL, 0); if (r != NULL) { n = xmlTextReaderRead(r); while (n == 1) { if (tgt_node_process(r, &node) == False) { break; } n = xmlTextReaderRead(r); } if (n < 0) { syslog(LOG_ERR, "Parsing main config failed"); ret = CONVERT_FAIL; goto done; } main_config = node; (void) tgt_find_value_str(node, XML_ELEMENT_BASEDIR, &target_basedir); if (target_basedir == NULL) target_basedir = strdup(DEFAULT_TARGET_BASEDIR); /* Now convert targets' config if possible */ if (xml_fd != -1) (void) close(xml_fd); xmlTextReaderClose(r); xmlFreeTextReader(r); xmlCleanupParser(); r = NULL; xml_fd = -1; node = NULL; (void) snprintf(path, MAXPATHLEN, "%s/%s", target_basedir, "config.xml"); if ((xml_fd = open(path, O_RDONLY)) >= 0) r = (xmlTextReaderPtr)xmlReaderForFd(xml_fd, NULL, NULL, 0); if (r != NULL) { n = xmlTextReaderRead(r); while (n == 1) { if (tgt_node_process(r, &node) == False) { break; } n = xmlTextReaderRead(r); } if (n < 0) { syslog(LOG_ERR, "Parsing target conf failed"); ret = CONVERT_FAIL; goto done; } /* now combine main_config and node */ if (node) { next = NULL; while ((next = tgt_node_next(node, XML_ELEMENT_TARG, next)) != NULL) { tgt_node_add(main_config, tgt_node_dup(next)); } tgt_node_free(node); } if (mgmt_config_save2scf() != True) { syslog(LOG_ERR, "Converting config failed"); if (xml_fd != -1) (void) close(xml_fd); xmlTextReaderClose(r); xmlFreeTextReader(r); xmlCleanupParser(); ret = CONVERT_FAIL; goto done; } /* Copy files into backup dir */ (void) snprintf(path, sizeof (path), "%s/backup", target_basedir); if ((mkdir(path, 0755) == -1) && (errno != EEXIST)) { syslog(LOG_ERR, "Creating backup dir failed"); ret = CONVERT_FAIL; goto done; } backup(config_file, NULL); (void) snprintf(path, MAXPATHLEN, "%s/%s", target_basedir, "config.xml"); backup(path, NULL); while ((next = tgt_node_next(main_config, XML_ELEMENT_TARG, next)) != NULL) { if (tgt_find_value_str(next, XML_ELEMENT_INAME, &target) == False) { continue; } (void) snprintf(path, MAXPATHLEN, "%s/%s", target_basedir, target); if (mgmt_convert_param(path, next) != True) { ret = CONVERT_FAIL; goto done; } free(target); } ret = CONVERT_OK; syslog(LOG_NOTICE, "Conversion succeeded"); xmlTextReaderClose(r); xmlFreeTextReader(r); xmlCleanupParser(); } else { syslog(LOG_ERR, "Reading targets config failed"); ret = CONVERT_FAIL; goto done; } } else { syslog(LOG_ERR, "Reading main config failed"); ret = CONVERT_FAIL; goto done; } done: if (xml_fd != -1) (void) close(xml_fd); mgmt_handle_fini(h); return (ret); }
/* * mgmt_get_main_config() loads main configuration * from scf into a node tree. * Main configuration includes: admin/target/tpgt/initiator info. * admin info is stored in "iscsitgt" property group * target info is stored in "target_<name>" property group * initiator info is stored in "initiator_<name>" property group * tpgt info is stored in "tpgt_<number>" property group */ Boolean_t mgmt_get_main_config(tgt_node_t **node) { targ_scf_t *h = NULL; scf_property_t *prop = NULL; scf_value_t *value = NULL; scf_iter_t *iter = NULL; scf_iter_t *iter_v = NULL; scf_iter_t *iter_pv = NULL; char pname[32]; char valuebuf[MAXPATHLEN]; char passcode[32]; unsigned int outlen; tgt_node_t *n; tgt_node_t *pn; tgt_node_t *vn; Boolean_t status = False; h = mgmt_handle_init(); if (h == NULL) return (status); prop = scf_property_create(h->t_handle); value = scf_value_create(h->t_handle); iter = scf_iter_create(h->t_handle); (void) pthread_mutex_lock(&scf_conf_mutex); /* Basic Information is stored in iscsitgt pg */ if (scf_service_get_pg(h->t_service, "iscsitgt", h->t_pg) == -1) { goto error; } *node = NULL; *node = tgt_node_alloc("main_config", String, NULL); if (*node == NULL) goto error; if (scf_iter_pg_properties(iter, h->t_pg) == -1) { goto error; } while (scf_iter_next_property(iter, prop) > 0) { scf_property_get_value(prop, value); scf_value_get_as_string(value, valuebuf, MAXPATHLEN); scf_property_get_name(prop, pname, sizeof (pname)); /* avoid load auth to incore data */ if (strcmp(pname, ISCSI_READ_AUTHNAME) == 0 || strcmp(pname, ISCSI_MODIFY_AUTHNAME) == 0 || strcmp(pname, ISCSI_VALUE_AUTHNAME) == 0) continue; n = tgt_node_alloc(pname, String, valuebuf); if (n == NULL) goto error; /* put version info into root node's attr */ if (strcmp(pname, XML_ELEMENT_VERS) == 0) { tgt_node_add_attr(*node, n); } else { /* add other basic info into root node */ tgt_node_add(*node, n); } } /* * targets/initiators/tpgt information is * stored as type "configuration" in scf * each target's param is stored as type "parameter" */ if (scf_iter_service_pgs_typed(iter, h->t_service, "configuration") == -1) { goto error; } while (scf_iter_next_pg(iter, h->t_pg) > 0) { char *iname; scf_pg_get_name(h->t_pg, pname, sizeof (pname)); iname = strchr(pname, '_'); if (iname == NULL) { /* the pg found here is not a tgt/initiator/tpgt */ continue; } *iname = '\0'; iname++; /* * now pname is "target" or "initiator" or "tpgt" * meanwhile iname is the actual name of the item */ n = tgt_node_alloc(pname, String, iname); if (n == NULL) goto error; iter_v = scf_iter_create(h->t_handle); if (scf_iter_pg_properties(iter_v, h->t_pg) == -1) { goto error; } while (scf_iter_next_property(iter_v, prop) > 0) { /* there may be many values in one property */ char *vname; scf_property_get_name(prop, pname, sizeof (pname)); /* avoid load auth to incore data */ if (strcmp(pname, ISCSI_READ_AUTHNAME) == 0 || strcmp(pname, ISCSI_MODIFY_AUTHNAME) == 0 || strcmp(pname, ISCSI_VALUE_AUTHNAME) == 0) continue; vname = strstr(pname, "-list"); if (vname == NULL) { scf_property_get_value(prop, value); scf_value_get_as_string(value, valuebuf, MAXPATHLEN); pn = tgt_node_alloc(pname, String, valuebuf); if (pn == NULL) goto error; tgt_node_add(n, pn); } else { pn = tgt_node_alloc(pname, String, NULL); if (pn == NULL) goto error; tgt_node_add(n, pn); *vname = '\0'; iter_pv = scf_iter_create(h->t_handle); scf_iter_property_values(iter_pv, prop); while (scf_iter_next_value(iter_pv, value) > 0) { scf_value_get_as_string(value, valuebuf, MAXPATHLEN); vn = tgt_node_alloc(pname, String, valuebuf); if (vn == NULL) goto error; tgt_node_add(pn, vn); } scf_iter_destroy(iter_pv); iter_pv = NULL; } } tgt_node_add(*node, n); scf_iter_destroy(iter_v); iter_v = NULL; } /* chap-secrets are stored in "passwords" pgroup as "application" */ if (scf_service_get_pg(h->t_service, "passwords", h->t_pg) == 0) { if (scf_iter_pg_properties(iter, h->t_pg) == -1) { goto error; } while (scf_iter_next_property(iter, prop) > 0) { scf_property_get_value(prop, value); scf_value_get_as_string(value, valuebuf, MAXPATHLEN); scf_property_get_name(prop, pname, sizeof (pname)); /* avoid load auth to incore data */ if (strcmp(pname, ISCSI_READ_AUTHNAME) == 0 || strcmp(pname, ISCSI_MODIFY_AUTHNAME) == 0 || strcmp(pname, ISCSI_VALUE_AUTHNAME) == 0) continue; /* max length of decoded passwd is 16B */ sasl_decode64(valuebuf, strlen(valuebuf), passcode, sizeof (passcode), &outlen); if (strcmp(pname, "radius") == 0) { pn = tgt_node_alloc(XML_ELEMENT_RAD_SECRET, String, passcode); tgt_node_add(*node, pn); } else if (strcmp(pname, "main") == 0) { pn = tgt_node_alloc(XML_ELEMENT_CHAPSECRET, String, passcode); tgt_node_add(*node, pn); } else { /* find corresponding initiator */ n = NULL; while (n = tgt_node_next_child(*node, XML_ELEMENT_INIT, n)) { if (strcmp(pname + 2, n->x_value) != 0) continue; pn = tgt_node_alloc( XML_ELEMENT_CHAPSECRET, String, passcode); tgt_node_add(n, pn); } } } } status = True; error: if ((status != True) && (*node != NULL)) tgt_node_free(*node); (void) pthread_mutex_unlock(&scf_conf_mutex); if (iter_pv != NULL) scf_iter_destroy(iter_pv); if (iter_v != NULL) scf_iter_destroy(iter_v); scf_iter_destroy(iter); scf_value_destroy(value); scf_property_destroy(prop); mgmt_handle_fini(h); return (status); }
/* * mgmt_get_param() get parameter of a specific LUN from scf * Args: * node - the node which parameters will be stored in mem * target_name - the local target name * lun - the LUN number * See also : mgmt_param_save2scf() */ Boolean_t mgmt_get_param(tgt_node_t **node, char *target_name, int lun) { targ_scf_t *h = NULL; scf_property_t *prop = NULL; scf_value_t *value = NULL; scf_iter_t *iter = NULL; char pname[64]; char pgname[64]; char valuebuf[MAXPATHLEN]; tgt_node_t *n; Boolean_t status = False; h = mgmt_handle_init(); if (h == NULL) return (status); prop = scf_property_create(h->t_handle); value = scf_value_create(h->t_handle); iter = scf_iter_create(h->t_handle); snprintf(pgname, sizeof (pgname), "param_%s_%d", target_name, lun); (void) pthread_mutex_lock(&scf_param_mutex); if (scf_service_get_pg(h->t_service, pgname, h->t_pg) == -1) { goto error; } *node = tgt_node_alloc(XML_ELEMENT_PARAMS, String, NULL); if (*node == NULL) goto error; if (scf_iter_pg_properties(iter, h->t_pg) == -1) { goto error; } while (scf_iter_next_property(iter, prop) > 0) { scf_property_get_value(prop, value); scf_value_get_as_string(value, valuebuf, MAXPATHLEN); scf_property_get_name(prop, pname, sizeof (pname)); /* avoid load auth to incore data */ if (strcmp(pname, ISCSI_READ_AUTHNAME) == 0 || strcmp(pname, ISCSI_MODIFY_AUTHNAME) == 0 || strcmp(pname, ISCSI_VALUE_AUTHNAME) == 0) continue; n = tgt_node_alloc(pname, String, valuebuf); if (n == NULL) goto error; /* put version info into root node's attr */ if (strcmp(pname, XML_ELEMENT_VERS) == 0) { tgt_node_add_attr(*node, n); } else { /* add other basic info into root node */ tgt_node_add(*node, n); } } status = True; error: (void) pthread_mutex_unlock(&scf_param_mutex); scf_iter_destroy(iter); scf_value_destroy(value); scf_property_destroy(prop); mgmt_handle_fini(h); return (status); }
/* * modify_zfs -- test for the existence of a certain dataset being shared * * Called when someone uses the iscsitgt_is_shared() function from libiscsitgt. * All that */ static char * modify_zfs(tgt_node_t *x, ucred_t *cred) { char *msg = NULL; char *dataset = NULL; char *prop; char *m; tgt_node_t *n = NULL; tgt_node_t *t = NULL; tgt_node_t *list = NULL; tgt_node_t *c1, *c2; Boolean_t change_made = False; uint64_t size; int status; int val; char *tru = "true"; (void) pthread_rwlock_wrlock(&targ_config_mutex); if (tgt_find_value_str(x, XML_ELEMENT_NAME, &dataset) == False) { xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_NAME); goto error; } /* * Validate request */ if (tgt_find_value_str(x, XML_ELEMENT_VALIDATE, &tru)) { (void) pthread_rwlock_unlock(&targ_config_mutex); if (tru) free(tru); free(dataset); return (validate_zfs_iscsitgt(x)); } /* * Check for existance of ZFS shareiscsi properties */ status = get_zfs_shareiscsi(dataset, &n, &size, cred); if ((status != ERR_SUCCESS) && (status != ERR_NULL_XML_MESSAGE)) { xml_rtn_msg(&msg, ERR_TARG_NOT_FOUND); goto error; } while ((t = tgt_node_next_child(targets_config, XML_ELEMENT_TARG, t)) != NULL) { if (strcmp(t->x_value, dataset) == 0) break; } if (t == NULL) { xml_rtn_msg(&msg, ERR_TARG_NOT_FOUND); goto error; } if (tgt_find_value_str(x, XML_ELEMENT_TPGT, &prop) == True) { if (prop == NULL) { xml_rtn_msg(&msg, ERR_SYNTAX_EMPTY_TPGT); goto error; } /* * Validate that the Target Portal Group Tag is reasonable. */ val = strtoll(prop, &m, 0); if ((val < TPGT_MIN) || (val > TPGT_MAX) || ((m != NULL) && (*m != '\0'))) { xml_rtn_msg(&msg, ERR_INVALID_TPGT); goto error; } if ((c1 = tgt_node_alloc(XML_ELEMENT_TPGT, String, prop)) == NULL) { xml_rtn_msg(&msg, ERR_NO_MEM); goto error; } /* * Due to the fact that the targets_config differs from the * ZVOL properties stored in zfs_shareiscsi, two lists need to * be updated */ c2 = tgt_node_dup(c1); if ((list = tgt_node_next(t, XML_ELEMENT_TPGTLIST, NULL)) != NULL) { /* * tgt_node_replace will duplicate the child node * tgt_node_add which is used below just links it * into the tree. */ tgt_node_replace(list, c1, MatchBoth); tgt_node_free(c1); } else { list = tgt_node_alloc(XML_ELEMENT_TPGTLIST, String, ""); if (list == NULL) { xml_rtn_msg(&msg, ERR_NO_MEM); goto error; } tgt_node_add(list, c1); tgt_node_add(t, list); } if ((list = tgt_node_next(n, XML_ELEMENT_TPGTLIST, NULL)) != NULL) { /* * tgt_node_replace will duplicate the child node * tgt_node_add which is used below just links it * into the tree. */ tgt_node_replace(list, c2, MatchBoth); tgt_node_free(c2); } else { list = tgt_node_alloc(XML_ELEMENT_TPGTLIST, String, ""); if (list == NULL) { xml_rtn_msg(&msg, ERR_NO_MEM); goto error; } tgt_node_add(list, c2); tgt_node_add(n, list); } change_made = True; } if (tgt_find_value_str(x, XML_ELEMENT_ACL, &prop) == True) { if (prop == NULL) { xml_rtn_msg(&msg, ERR_SYNTAX_EMPTY_ACL); goto error; } c1 = tgt_node_alloc(XML_ELEMENT_INIT, String, prop); if (c1 == NULL) { xml_rtn_msg(&msg, ERR_NO_MEM); goto error; } /* * Due to the fact that the targets_config differs from the * ZVOL properties stored in zfs_shareiscsi, two lists need to * be updated */ c2 = tgt_node_dup(c1); if ((list = tgt_node_next(t, XML_ELEMENT_ACLLIST, NULL)) != NULL) { /* * tgt_node_replace will duplicate the child node * tgt_node_add which is used below just links it * into the tree. */ tgt_node_replace(list, c1, MatchBoth); tgt_node_free(c1); } else { list = tgt_node_alloc(XML_ELEMENT_ACLLIST, String, ""); if (list == NULL) { xml_rtn_msg(&msg, ERR_NO_MEM); goto error; } tgt_node_add(list, c1); tgt_node_add(t, list); } if ((list = tgt_node_next(n, XML_ELEMENT_ACLLIST, NULL)) != NULL) { /* * tgt_node_replace will duplicate the child node * tgt_node_add which is used below just links it * into the tree. */ tgt_node_replace(list, c2, MatchBoth); tgt_node_free(c2); } else { list = tgt_node_alloc(XML_ELEMENT_ACLLIST, String, ""); if (list == NULL) { xml_rtn_msg(&msg, ERR_NO_MEM); goto error; } tgt_node_add(list, c2); tgt_node_add(n, list); } change_made = True; } if (change_made == True) { status = put_zfs_shareiscsi(dataset, n); if (status != ERR_SUCCESS) { xml_rtn_msg(&msg, status); goto error; } else { xml_rtn_msg(&msg, ERR_SUCCESS); } } else { xml_rtn_msg(&msg, ERR_SUCCESS); } error: if (n) tgt_node_free(n); if (dataset) free(dataset); (void) pthread_rwlock_unlock(&targ_config_mutex); return (msg); }
/* * []---- * | modify_tpgt -- add an IP-address to a target portal group * []---- */ static char * modify_tpgt(tgt_node_t *x) { struct addrinfo *res = NULL; char *msg = NULL; char *name = NULL; char *ip_str = NULL; tgt_node_t *tnode = NULL; tgt_node_t *list = NULL; (void) pthread_rwlock_wrlock(&targ_config_mutex); if (tgt_find_value_str(x, XML_ELEMENT_NAME, &name) == False) { xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_NAME); goto error; } if (tgt_find_value_str(x, XML_ELEMENT_IPADDR, &ip_str) == False) { xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_IPADDR); goto error; } if ((getaddrinfo(ip_str, NULL, NULL, &res) != 0) || (res == NULL)) { xml_rtn_msg(&msg, ERR_INVALID_IP); goto error; } while ((tnode = tgt_node_next_child(main_config, XML_ELEMENT_TPGT, tnode)) != NULL) { if (strcmp(tnode->x_value, name) == 0) break; } if (tnode == NULL) { xml_rtn_msg(&msg, ERR_TPGT_NOT_FOUND); goto error; } if ((list = tgt_node_next(tnode, XML_ELEMENT_IPADDRLIST, NULL)) == NULL) { list = tgt_node_alloc(XML_ELEMENT_IPADDRLIST, String, ""); if (list == NULL) { xml_rtn_msg(&msg, ERR_NO_MEM); goto error; } tgt_node_add(tnode, list); } if (modify_element(XML_ELEMENT_IPADDR, ip_str, list, MatchBoth) == False) { xml_rtn_msg(&msg, ERR_NO_MEM); goto error; } if (mgmt_config_save2scf() == True) { xml_rtn_msg(&msg, ERR_SUCCESS); } else { /* tpgt change should be updated to smf */ xml_rtn_msg(&msg, ERR_INTERNAL_ERROR); } /* * Re-register all targets, currently there's no method to * update TPGT for individual target */ if (isns_enabled() == True) { (void) isns_reg_all(); } error: if (name) free(name); if (ip_str) free(ip_str); if (res) freeaddrinfo(res); (void) pthread_rwlock_unlock(&targ_config_mutex); return (msg); }
/* * []---- * | modify_target -- updates one or more properties for a target * []---- */ static char * modify_target(tgt_node_t *x, ucred_t *cred) { char *msg = NULL; char *name = NULL; char iscsi_path[MAXPATHLEN]; char targ_name[64]; char *iscsi = NULL; char *prop = NULL; char path[MAXPATHLEN]; char *m; char buf[512]; /* one sector size block */ tgt_node_t *t = NULL; tgt_node_t *list = NULL; tgt_node_t *c = NULL; tgt_node_t *node = NULL; tgt_node_t *tpgt = NULL; Boolean_t change_made = False; int lun = 0; int fd; uint64_t val, new_lu_size, cur_lu_size; struct stat st; uint32_t isns_mods = 0; (void) pthread_rwlock_wrlock(&targ_config_mutex); if (tgt_find_value_str(x, XML_ELEMENT_NAME, &name) == False) { xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_NAME); goto error; } while ((t = tgt_node_next_child(targets_config, XML_ELEMENT_TARG, t)) != NULL) { if (strcmp(t->x_value, name) == 0) { break; } } if (t == NULL) { free(name); xml_rtn_msg(&msg, ERR_TARG_NOT_FOUND); goto error; } if (tgt_find_attr_str(t, XML_ELEMENT_INCORE, &m) == True) { if (strcmp(m, "true") == 0) { free(m); free(name); (void) pthread_rwlock_unlock(&targ_config_mutex); return (modify_zfs(x, cred)); } free(m); } /* * Under base dir, file 'target name' is a symbolic link * to the real directory 'IQN name' which stores params and back * storage. Therefore we can easily get IQN name from target * name by read the symbolic link content. */ (void) snprintf(path, sizeof (path), "%s/%s", target_basedir, name); bzero(iscsi_path, sizeof (iscsi_path)); (void) readlink(path, iscsi_path, sizeof (iscsi_path)); iscsi = basename(iscsi_path); /* ---- Finished with these so go ahead and release the memory ---- */ (void) strncpy(targ_name, name, sizeof (targ_name)); free(name); /* * Grow the LU. We currently do not support shrinking the LU and * that is only because it's unknown if any applications could support * that type of data loss. To support shrinking all that would be * needed is to remove the new/old size check and perform a truncation. * The actually truncation request should be shipped off to the T10 * layer so that the LU thread can remap the smaller size without * anyone accessing the data. */ if (tgt_find_value_str(x, XML_ELEMENT_SIZE, &prop) == True) { if (prop == NULL) { xml_rtn_msg(&msg, ERR_SYNTAX_EMPTY_TPGT); goto error; } if (strtoll_multiplier(prop, &new_lu_size) == False) { free(prop); xml_rtn_msg(&msg, ERR_INVALID_SIZE); goto error; } free(prop); if ((new_lu_size % 512LL) != 0) { xml_rtn_msg(&msg, ERR_SIZE_MOD_BLOCK); goto error; } new_lu_size /= 512LL; /* ---- default to LUN 0 ---- */ (void) tgt_find_value_int(x, XML_ELEMENT_LUN, &lun); /* ---- read in current parameters ---- */ if (mgmt_get_param(&node, targ_name, lun) == False) { xml_rtn_msg(&msg, ERR_OPEN_PARAM_FILE_FAILED); goto error; } /* ---- validate that we're indeed growing the LU ---- */ if (tgt_find_value_str(node, XML_ELEMENT_SIZE, &prop) == False) { xml_rtn_msg(&msg, ERR_INIT_XML_READER_FAILED); goto error; } if (strtoll_multiplier(prop, &cur_lu_size) == False) { free(prop); xml_rtn_msg(&msg, ERR_INVALID_SIZE); goto error; } free(prop); if (new_lu_size < cur_lu_size) { xml_rtn_msg(&msg, ERR_CANT_SHRINK_LU); goto error; } /* ---- check that this LU is of type 'disk' or 'tape' ---- */ if (tgt_find_value_str(node, XML_ELEMENT_DTYPE, &prop) == False) { xml_rtn_msg(&msg, ERR_INIT_XML_READER_FAILED); goto error; } if ((strcmp(prop, TGT_TYPE_DISK) != 0) && (strcmp(prop, TGT_TYPE_TAPE) != 0)) { xml_rtn_msg(&msg, ERR_RESIZE_WRONG_DTYPE); free(prop); goto error; } free(prop); /* ---- validate the backing store is a regular file ---- */ (void) snprintf(path, sizeof (path), "%s/%s/%s%d", target_basedir, iscsi, LUNBASE, lun); if (stat(path, &st) == -1) { xml_rtn_msg(&msg, ERR_STAT_BACKING_FAILED); goto error; } if ((st.st_mode & S_IFMT) != S_IFREG) { xml_rtn_msg(&msg, ERR_DISK_BACKING_MUST_BE_REGULAR_FILE); goto error; } /* ---- update the parameter node with new size ---- */ if ((c = tgt_node_alloc(XML_ELEMENT_SIZE, Uint64, &new_lu_size)) == NULL) { xml_rtn_msg(&msg, ERR_NO_MEM); goto error; } tgt_node_replace(node, c, MatchName); tgt_node_free(c); /* ---- now update params file ---- */ (void) mgmt_param_save2scf(node, targ_name, lun); /* ---- grow lu backing store ---- */ (void) snprintf(path, sizeof (path), "%s/%s/%s%d", target_basedir, iscsi, LUNBASE, lun); if ((fd = open(path, O_RDWR|O_CREAT|O_LARGEFILE, 0600)) < 0) { xml_rtn_msg(&msg, ERR_LUN_NOT_FOUND); goto error; } (void) lseek(fd, (new_lu_size * 512LL) - 512LL, 0); bzero(buf, sizeof (buf)); if (write(fd, buf, sizeof (buf)) != sizeof (buf)) { xml_rtn_msg(&msg, ERR_LUN_NOT_GROWN); (void) close(fd); goto error; } (void) close(fd); /* ---- send updates to current initiators via ASC/ASCQ ---- */ iscsi_capacity_change(iscsi, lun); prop = NULL; tgt_node_free(node); node = NULL; change_made = True; } if (tgt_find_value_str(x, XML_ELEMENT_TPGT, &prop) == True) { if (prop == NULL) { xml_rtn_msg(&msg, ERR_SYNTAX_EMPTY_TPGT); goto error; } /* * Validate that the Target Portal Group Tag is reasonable. */ val = strtoll(prop, &m, 0); if ((val < TPGT_MIN) || (val > TPGT_MAX) || ((m != NULL) && (*m != '\0'))) { xml_rtn_msg(&msg, ERR_INVALID_TPGT); free(prop); goto error; } /* update isns only if TPGT contains ip_addr */ tpgt = NULL; while ((tpgt = tgt_node_next_child(main_config, XML_ELEMENT_TPGT, tpgt)) != NULL) { if (strcmp(prop, tpgt->x_value) != 0) continue; if (tgt_node_next(tpgt, XML_ELEMENT_IPADDR, NULL) != NULL) { isns_mods |= ISNS_MOD_TPGT; break; } else { xml_rtn_msg(&msg, ERR_TPGT_NO_IPADDR); free(prop); goto error; } } if ((c = tgt_node_alloc(XML_ELEMENT_TPGT, String, prop)) == NULL) { free(prop); xml_rtn_msg(&msg, ERR_NO_MEM); goto error; } if ((list = tgt_node_next(t, XML_ELEMENT_TPGTLIST, NULL)) != NULL) { tgt_node_replace(list, c, MatchBoth); /* * tgt_node_replace will duplicate the child node * tgt_node_add which is used below just links it * into the tree. */ tgt_node_free(c); } else { list = tgt_node_alloc(XML_ELEMENT_TPGTLIST, String, ""); if (list == NULL) { free(prop); xml_rtn_msg(&msg, ERR_NO_MEM); goto error; } tgt_node_add(list, c); tgt_node_add(t, list); } free(prop); prop = NULL; change_made = True; } if (tgt_find_value_str(x, XML_ELEMENT_ACL, &prop) == True) { if (prop == NULL) { xml_rtn_msg(&msg, ERR_SYNTAX_EMPTY_ACL); goto error; } c = tgt_node_alloc(XML_ELEMENT_INIT, String, prop); if (c == NULL) { xml_rtn_msg(&msg, ERR_NO_MEM); free(prop); goto error; } if ((list = tgt_node_next(t, XML_ELEMENT_ACLLIST, NULL)) != NULL) { tgt_node_replace(list, c, MatchBoth); /* ---- See above usage ---- */ tgt_node_free(c); } else { list = tgt_node_alloc(XML_ELEMENT_ACLLIST, String, ""); if (list == NULL) { xml_rtn_msg(&msg, ERR_NO_MEM); free(prop); goto error; } tgt_node_add(list, c); tgt_node_add(t, list); } free(prop); prop = NULL; change_made = True; } if (tgt_find_value_str(x, XML_ELEMENT_ALIAS, &prop) == True) { if (prop == NULL) { xml_rtn_msg(&msg, ERR_SYNTAX_EMPTY_ALIAS); goto error; } if (modify_element(XML_ELEMENT_ALIAS, prop, t, MatchName) == False) { xml_rtn_msg(&msg, ERR_NO_MEM); free(prop); goto error; } free(prop); prop = NULL; isns_mods |= ISNS_MOD_ALIAS; change_made = True; } if (tgt_find_value_str(x, XML_ELEMENT_MAXRECV, &prop) == True) { if (prop == NULL) { xml_rtn_msg(&msg, ERR_SYNTAX_EMPTY_MAXRECV); goto error; } if ((strtoll_multiplier(prop, &val) == False) || (val < MAXRCVDATA_MIN) || (val > MAXRCVDATA_MAX)) { free(prop); xml_rtn_msg(&msg, ERR_INVALID_MAXRECV); goto error; } free(prop); if ((prop = malloc(32)) == NULL) { xml_rtn_msg(&msg, ERR_NO_MEM); goto error; } (void) snprintf(prop, 32, "%d", val); if (modify_element(XML_ELEMENT_MAXRECV, prop, t, MatchName) == False) { free(prop); xml_rtn_msg(&msg, ERR_NO_MEM); goto error; } free(prop); prop = NULL; change_made = True; } if (change_made == True) { if (mgmt_config_save2scf() == False) { xml_rtn_msg(&msg, ERR_UPDATE_TARGCFG_FAILED); goto error; } if (isns_enabled() == True) { if (isns_dev_update(t->x_value, isns_mods) != 0) { xml_rtn_msg(&msg, ERR_ISNS_ERROR); goto error; } } xml_rtn_msg(&msg, ERR_SUCCESS); } else { xml_rtn_msg(&msg, ERR_SYNTAX_MISSING_OPERAND); } error: (void) pthread_rwlock_unlock(&targ_config_mutex); if (node) tgt_node_free(node); return (msg); }
/* * mgmt_get_param() get parameter of a specific LUN from scf * Args: * node - the node which parameters will be stored in mem * target_name - the local target name * lun - the LUN number * See also : mgmt_param_save2scf() */ Boolean_t mgmt_get_param(tgt_node_t **node, char *target_name, int lun) { targ_scf_t *h = NULL; scf_property_t *prop = NULL; scf_value_t *value = NULL; scf_iter_t *iter = NULL; char *pname = NULL; char *expgname = NULL; char *pgname = NULL; char *valuebuf = NULL; ssize_t max_name_len; ssize_t expg_max_name_len; ssize_t max_value_len; tgt_node_t *n; Boolean_t status = False; /* Set NULL as default output value */ *node = NULL; h = mgmt_handle_init(); if (h == NULL) return (status); prop = scf_property_create(h->t_handle); value = scf_value_create(h->t_handle); iter = scf_iter_create(h->t_handle); if ((alloc_scf_name(&max_name_len, (void *)&pname) == NULL) || (alloc_scf_name(&max_name_len, (void *)&pgname) == NULL) || (alloc_scf_value(&max_value_len, (void *)&valuebuf) == NULL)) { goto error; } /* * Allocate memory for an "expanded" (or "decoded") Property Group * name. */ expg_max_name_len = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) * PG_FACTOR + 1; if ((expgname = malloc(expg_max_name_len)) == NULL) { goto error; } (void) snprintf(pgname, max_name_len, "param_%s_%d", target_name, lun); pgname_encode(pgname, expgname, max_name_len); (void) pthread_mutex_lock(&scf_param_mutex); if (scf_service_get_pg(h->t_service, expgname, h->t_pg) == -1) { goto error; } *node = tgt_node_alloc(XML_ELEMENT_PARAMS, String, NULL); if (*node == NULL) goto error; if (scf_iter_pg_properties(iter, h->t_pg) == -1) { goto error; } while (scf_iter_next_property(iter, prop) > 0) { (void) scf_property_get_value(prop, value); (void) scf_value_get_as_string(value, valuebuf, max_value_len); (void) scf_property_get_name(prop, pname, max_name_len); /* avoid load auth to incore data */ if (strcmp(pname, ISCSI_READ_AUTHNAME) == 0 || strcmp(pname, ISCSI_MODIFY_AUTHNAME) == 0 || strcmp(pname, ISCSI_VALUE_AUTHNAME) == 0) continue; n = tgt_node_alloc(pname, String, valuebuf); if (n == NULL) goto error; /* put version info into root node's attr */ if (strcmp(pname, XML_ELEMENT_VERS) == 0) { tgt_node_add_attr(*node, n); } else { /* add other basic info into root node */ tgt_node_add(*node, n); } } status = True; error: (void) pthread_mutex_unlock(&scf_param_mutex); free(valuebuf); free(expgname); free(pgname); free(pname); scf_iter_destroy(iter); scf_value_destroy(value); scf_property_destroy(prop); mgmt_handle_fini(h); return (status); }
/* * Convert legacy (XML) configuration files into an equivalent SCF * representation. * * Read the XML from disk, translate the XML into a tree of nodes of * type tgt_node_t, and write the in-memory tree to SCF's persistent * data-store using mgmt_config_save2scf(). * * Return Values: * CONVERT_OK: successfully converted * CONVERT_INIT_NEW: configuration files don't exist; created an SCF entry * CONVERT_FAIL: some conversion error occurred; no SCF entry created. * In this case, user has to manually check files and try * conversion again. */ convert_ret_t mgmt_convert_conf() { targ_scf_t *h = NULL; xmlTextReaderPtr r; convert_ret_t ret = CONVERT_FAIL; int xml_fd = -1; int n; tgt_node_t *node = NULL; tgt_node_t *next = NULL; char path[MAXPATHLEN]; char *target = NULL; h = mgmt_handle_init(); if (h == NULL) return (CONVERT_FAIL); /* * Check if the "iscsitgt" PropertyGroup has already been added * to the "iscsitgt" SMF Service. If so, then we have already * converted the legacy configuration files (and there is no work * to do). */ if (scf_service_get_pg(h->t_service, "iscsitgt", h->t_pg) == 0) { ret = CONVERT_OK; goto done; } if (access(config_file, R_OK) != 0) { /* * then the Main Config file is not present; initialize * SCF Properties to default values. */ if (mgmt_transaction_start(h, "iscsitgt", "basic") == True) { ret = CONVERT_INIT_NEW; node = tgt_node_alloc(XML_ELEMENT_VERS, String, "1.0"); new_property(h, node); tgt_node_free(node); /* "daemonize" is set to true by default */ node = tgt_node_alloc(XML_ELEMENT_DBGDAEMON, String, "true"); new_property(h, node); tgt_node_free(node); node = NULL; node = tgt_node_alloc(ISCSI_MODIFY_AUTHNAME, String, ISCSI_AUTH_MODIFY); new_property(h, node); tgt_node_free(node); node = tgt_node_alloc(ISCSI_VALUE_AUTHNAME, String, ISCSI_AUTH_VALUE); new_property(h, node); tgt_node_free(node); (void) mgmt_transaction_end(h); } else { syslog(LOG_ERR, "Creating empty entry failed"); ret = CONVERT_FAIL; goto done; } if (mgmt_transaction_start(h, "passwords", "application") == True) { node = tgt_node_alloc(ISCSI_READ_AUTHNAME, String, ISCSI_AUTH_READ); new_property(h, node); tgt_node_free(node); node = tgt_node_alloc(ISCSI_MODIFY_AUTHNAME, String, ISCSI_AUTH_MODIFY); new_property(h, node); tgt_node_free(node); node = tgt_node_alloc(ISCSI_VALUE_AUTHNAME, String, ISCSI_AUTH_VALUE); new_property(h, node); tgt_node_free(node); (void) mgmt_transaction_end(h); } else { syslog(LOG_ERR, "Creating empty entry failed"); ret = CONVERT_FAIL; } goto done; } if ((xml_fd = open(config_file, O_RDONLY)) >= 0) r = (xmlTextReaderPtr)xmlReaderForFd(xml_fd, NULL, NULL, 0); if (r != NULL) { int is_target_config; n = xmlTextReaderRead(r); while (n == 1) { if (tgt_node_process(r, &node) == False) { break; } n = xmlTextReaderRead(r); } if (n < 0) { syslog(LOG_ERR, "Parsing main config failed"); ret = CONVERT_FAIL; goto done; } main_config = node; /* * Initialize the Base Directory (global) variable by * using the value specified in the XML_ELEMENT_BASEDIR * XML tag. If a tag is not specified, use a default. */ (void) tgt_find_value_str(node, XML_ELEMENT_BASEDIR, &target_basedir); if (target_basedir == NULL) target_basedir = strdup(DEFAULT_TARGET_BASEDIR); if (xml_fd != -1) { (void) close(xml_fd); xml_fd = -1; } (void) xmlTextReaderClose(r); xmlFreeTextReader(r); xmlCleanupParser(); /* * If a Target Config file is present, read and translate * its XML representation into a tree of tgt_node_t. * Merge that tree with the tree of tgt_node_t rooted at * 'main_config'. The merged tree will then be archived * using an SCF representation. */ (void) snprintf(path, MAXPATHLEN, "%s/%s", target_basedir, "config.xml"); if ((xml_fd = open(path, O_RDONLY)) >= 0) { is_target_config = 1; r = (xmlTextReaderPtr)xmlReaderForFd(xml_fd, NULL, NULL, 0); } else { is_target_config = 0; r = NULL; } if (r != NULL) { /* then the Target Config file is available. */ node = NULL; /* * Create a tree of tgt_node_t rooted at 'node' by * processing each XML Tag in the file. */ n = xmlTextReaderRead(r); while (n == 1) { if (tgt_node_process(r, &node) == False) { break; } n = xmlTextReaderRead(r); } if (n < 0) { syslog(LOG_ERR, "Parsing target conf failed"); ret = CONVERT_FAIL; goto done; } /* * Merge the tree at 'node' into the tree rooted at * 'main_config'. */ if (node != NULL) { next = NULL; while ((next = tgt_node_next(node, XML_ELEMENT_TARG, next)) != NULL) { tgt_node_add(main_config, tgt_node_dup(next)); } tgt_node_free(node); } } /* * Iterate over the in-memory tree rooted at 'main_config' * and write a representation of the appropriate nodes to * SCF's persistent data-store. */ if (mgmt_config_save2scf() != True) { syslog(LOG_ERR, "Converting config failed"); if (xml_fd != -1) { (void) close(xml_fd); xml_fd = -1; } (void) xmlTextReaderClose(r); xmlFreeTextReader(r); xmlCleanupParser(); ret = CONVERT_FAIL; goto done; } /* * Move the configuration files into a well-known backup * directory. This allows a user to restore their * configuration, if they choose. */ (void) snprintf(path, sizeof (path), "%s/backup", target_basedir); if ((mkdir(path, 0755) == -1) && (errno != EEXIST)) { syslog(LOG_ERR, "Creating backup dir failed"); ret = CONVERT_FAIL; goto done; } /* Save the Main Config file. */ backup(config_file, NULL); /* Save the Target Config file, if it was present. */ if (is_target_config != 0) { (void) snprintf(path, MAXPATHLEN, "%s/%s", target_basedir, "config.xml"); backup(path, NULL); } /* * For each tgt_node_t node in 'main_config' whose value is * an iSCSI Name as defined in the RFC (3720) standard (eg, * "iqn.1986..."), read its XML-encoded attributes from a * flat-file and write an equivalent representation to SCF's * data-store. */ while ((next = tgt_node_next(main_config, XML_ELEMENT_TARG, next)) != NULL) { if (tgt_find_value_str(next, XML_ELEMENT_INAME, &target) == False) { continue; } (void) snprintf(path, MAXPATHLEN, "%s/%s", target_basedir, target); if (mgmt_convert_param(path, next) != True) { ret = CONVERT_FAIL; goto done; } free(target); } ret = CONVERT_OK; syslog(LOG_NOTICE, "Conversion succeeded"); (void) xmlTextReaderClose(r); xmlFreeTextReader(r); xmlCleanupParser(); } else { syslog(LOG_ERR, "Reading main config failed"); ret = CONVERT_FAIL; goto done; } done: if (xml_fd != -1) (void) close(xml_fd); mgmt_handle_fini(h); return (ret); }