/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include "libilb_impl.h" #include "ilbd.h" #define ILBD_PG_NAME_RULE "rule_" #define ILBD_PG_NAME_SG "sg_" #define ILBD_PG_NAME_HC "hc_" #define ILBD_SVC_FMRI "svc:/network/loadbalancer/ilb" #define ILBD_INST_NAME "default" typedef enum { ILBD_RULE_STATUS, ILBD_RULE_VIP, ILBD_RULE_PROTO, ILBD_RULE_PORT, ILBD_RULE_ALGO, ILBD_RULE_TOPO, ILBD_RULE_NAT_STR, ILBD_RULE_NAT_END, ILBD_RULE_STI_MASK, ILBD_RULE_SGNAME, ILBD_RULE_HCNAME, ILBD_RULE_HCPORT, ILBD_RULE_HCPFLAG, ILBD_RULE_DRAINTIME, ILBD_RULE_NAT_TO, ILBD_RULE_PERS_TO, ILBD_SG_SERVER, ILBD_HC_TEST, ILBD_HC_TIMEOUT, ILBD_HC_INTERVAL, ILBD_HC_DEF_PING, ILBD_HC_COUNT, ILBD_VAR_INVALID } ilbd_var_type_t; typedef struct prop_tbl_entry { ilbd_var_type_t val_type; const char *scf_propname; scf_type_t scf_proptype; } prop_tbl_entry_t; /* * this table contains a map of all SCF properties, including rules, * servergroups and health checks. The place to add new property needs to be * watched carefully. When new properties are added, corresponding *VAR_NUM * needs to be adjusted to reflect the correct index of the table */ prop_tbl_entry_t prop_tbl[] = { /* entried for rule */ {ILBD_RULE_STATUS, "status", SCF_TYPE_BOOLEAN}, /* SCF_TYPE_NET_ADDR_V4 or SCF_TYPE_NET_ADDR_V6 */ {ILBD_RULE_VIP, "vip", SCF_TYPE_INVALID}, {ILBD_RULE_PROTO, "protocol", SCF_TYPE_ASTRING}, {ILBD_RULE_PORT, "port", SCF_TYPE_ASTRING}, {ILBD_RULE_ALGO, "ilb-algo", SCF_TYPE_ASTRING}, {ILBD_RULE_TOPO, "ilb-type", SCF_TYPE_ASTRING}, {ILBD_RULE_NAT_STR, "ilb-nat-start", SCF_TYPE_INVALID}, {ILBD_RULE_NAT_END, "ilb-nat-end", SCF_TYPE_INVALID}, {ILBD_RULE_STI_MASK, "ilb-sti-mask", SCF_TYPE_INVALID}, {ILBD_RULE_SGNAME, "servergroup", SCF_TYPE_ASTRING}, {ILBD_RULE_HCNAME, "healthcheck", SCF_TYPE_ASTRING}, {ILBD_RULE_HCPORT, "hc-port", SCF_TYPE_INTEGER}, {ILBD_RULE_HCPFLAG, "hcp-flag", SCF_TYPE_INTEGER}, {ILBD_RULE_DRAINTIME, "drain-time", SCF_TYPE_INTEGER}, {ILBD_RULE_NAT_TO, "nat-timeout", SCF_TYPE_INTEGER}, {ILBD_RULE_PERS_TO, "pers-timeout", SCF_TYPE_INTEGER}, /* add new rule related prop here */ /* entries for sg */ {ILBD_SG_SERVER, "server", SCF_TYPE_ASTRING}, /* add new sg related prop here */ /* entries for hc */ {ILBD_HC_TEST, "test", SCF_TYPE_ASTRING}, {ILBD_HC_TIMEOUT, "timeout", SCF_TYPE_INTEGER}, {ILBD_HC_INTERVAL, "interval", SCF_TYPE_INTEGER}, {ILBD_HC_DEF_PING, "ping", SCF_TYPE_BOOLEAN}, /* add new hc related prop here */ {ILBD_HC_COUNT, "count", SCF_TYPE_INTEGER} }; #define ILBD_PROP_VAR_NUM (ILBD_HC_COUNT + 1) #define ILBD_RULE_VAR_NUM (ILBD_SG_SERVER) #define ILBD_SG_VAR_NUM (ILBD_HC_TEST - ILBD_SG_SERVER) #define ILBD_HC_VAR_NUM (ILBD_PROP_VAR_NUM - ILBD_HC_TEST) static ilb_status_t ilbd_scf_set_prop(scf_propertygroup_t *, const char *, scf_type_t, scf_value_t *); static ilb_status_t ilbd_scf_retrieve_pg(const char *, scf_propertygroup_t **, boolean_t); static ilb_status_t ilbd_scf_delete_pg(scf_propertygroup_t *); static ilb_status_t ilbd_scf_get_prop_val(scf_propertygroup_t *, const char *, scf_value_t **); #define MIN(a, b) ((a) < (b) ? (a) : (b)) int ilbd_scf_limit(int type) { return (MIN(scf_limit(type), 120)); } /* * Translate libscf error to libilb status */ ilb_status_t ilbd_scf_err_to_ilb_err() { switch (scf_error()) { case SCF_ERROR_NONE: return (ILB_STATUS_OK); case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_HANDLE_DESTROYED: case SCF_ERROR_VERSION_MISMATCH: case SCF_ERROR_NOT_BOUND: case SCF_ERROR_CONSTRAINT_VIOLATED: case SCF_ERROR_NOT_SET: case SCF_ERROR_TYPE_MISMATCH: case SCF_ERROR_INVALID_ARGUMENT: return (ILB_STATUS_EINVAL); case SCF_ERROR_NO_MEMORY: case SCF_ERROR_NO_RESOURCES: return (ILB_STATUS_ENOMEM); case SCF_ERROR_NOT_FOUND: case SCF_ERROR_DELETED: return (ILB_STATUS_ENOENT); case SCF_ERROR_EXISTS: return (ILB_STATUS_EEXIST); case SCF_ERROR_PERMISSION_DENIED: return (ILB_STATUS_PERMIT); case SCF_ERROR_CALLBACK_FAILED: return (ILB_STATUS_CALLBACK); case SCF_ERROR_IN_USE: return (ILB_STATUS_INUSE); default: return (ILB_STATUS_INTERNAL); } } static void ilbd_name_to_scfpgname(ilbd_scf_pg_type_t pg_type, const char *pgname, char *scf_pgname) { switch (pg_type) { case ILBD_SCF_RULE: (void) snprintf(scf_pgname, ILBD_MAX_NAME_LEN, ILBD_PG_NAME_RULE "%s", pgname); return; case ILBD_SCF_SG: (void) snprintf(scf_pgname, ILBD_MAX_NAME_LEN, ILBD_PG_NAME_SG "%s", pgname); return; case ILBD_SCF_HC: (void) snprintf(scf_pgname, ILBD_MAX_NAME_LEN, ILBD_PG_NAME_HC "%s", pgname); return; /* Should not happen. Log it and put ILB service in maintenance. */ default: logerr("ilbd_name_to_scfpgname: invalid pg type %d for pg %s", pg_type, pgname); (void) smf_maintain_instance(ILB_FMRI, SMF_IMMEDIATE); exit(EXIT_FAILURE); return; } } static void ilbd_scf_destroy(scf_handle_t *h, scf_service_t *s, scf_instance_t *inst, scf_propertygroup_t *pg) { if (pg != NULL) scf_pg_destroy(pg); if (inst != NULL) scf_instance_destroy(inst); if (s != NULL) scf_service_destroy(s); if (h != NULL) scf_handle_destroy(h); } static ilb_status_t ilbd_scf_get_inst(scf_handle_t **h, scf_service_t **svc, scf_instance_t **inst) { if ((*h = scf_handle_create(SCF_VERSION)) == NULL) return (ILB_STATUS_INTERNAL); if (scf_handle_bind(*h) != 0) { ilbd_scf_destroy(*h, NULL, NULL, NULL); return (ilbd_scf_err_to_ilb_err()); } if ((*svc = scf_service_create(*h)) == NULL) { ilbd_scf_destroy(*h, NULL, NULL, NULL); return (ilbd_scf_err_to_ilb_err()); } if (scf_handle_decode_fmri(*h, ILBD_SVC_FMRI, NULL, *svc, NULL, NULL, NULL, SCF_DECODE_FMRI_EXACT) != 0) { ilbd_scf_destroy(*h, *svc, NULL, NULL); return (ilbd_scf_err_to_ilb_err()); } if ((*inst = scf_instance_create(*h)) == NULL) { ilbd_scf_destroy(*h, *svc, NULL, NULL); return (ilbd_scf_err_to_ilb_err()); } if (scf_service_get_instance(*svc, ILBD_INST_NAME, *inst) != 0) { ilbd_scf_destroy(*h, *svc, *inst, NULL); return (ilbd_scf_err_to_ilb_err()); } return (ILB_STATUS_OK); } /* * If create is set, create a new prop group, destroy the old one if exists. * If create not set, try to find the prop group with given name. * The created or found entry is returned as *pg. * Caller frees *pg and its handle scf_pg_handle(pg) */ static ilb_status_t ilbd_scf_retrieve_pg(const char *pgname, scf_propertygroup_t **pg, boolean_t create) { scf_instance_t *inst; scf_handle_t *h; scf_service_t *svc; ilb_status_t ret; ret = ilbd_scf_get_inst(&h, &svc, &inst); if (ret != ILB_STATUS_OK) return (ret); *pg = scf_pg_create(h); if (*pg == NULL) return (ILB_STATUS_INTERNAL); if (scf_instance_get_pg(inst, pgname, *pg) != 0) { if (scf_error() != SCF_ERROR_NOT_FOUND || (scf_error() == SCF_ERROR_NOT_FOUND && (!create))) { ilbd_scf_destroy(h, svc, inst, *pg); *pg = NULL; return (ilbd_scf_err_to_ilb_err()); } } else { /* * Found pg, don't want to create, return EEXIST. Note that * h cannot be destroyed here since the caller needs to use it. * The caller gets it by calling scf_pg_handle(). */ if (!create) { ilbd_scf_destroy(NULL, svc, inst, NULL); return (ILB_STATUS_EEXIST); } /* found pg, need to create, destroy the existing one */ else (void) ilbd_scf_delete_pg(*pg); } if (create) { if (scf_instance_add_pg(inst, pgname, SCF_GROUP_APPLICATION, 0, *pg) != 0) { ilbd_scf_destroy(h, svc, inst, *pg); *pg = NULL; return (ilbd_scf_err_to_ilb_err()); } } /* * Note that handle cannot be destroyed here, caller sometimes needs * to use it. It gets the handle by calling scf_pg_handle(). */ ilbd_scf_destroy(NULL, svc, inst, NULL); return (ILB_STATUS_OK); } struct algo_tbl_entry { ilb_algo_t algo_type; const char *algo_str; } algo_tbl[] = { {ILB_ALG_ROUNDROBIN, "ROUNDROBIN"}, {ILB_ALG_HASH_IP, "HASH-IP"}, {ILB_ALG_HASH_IP_SPORT, "HASH-IP-PORT"}, {ILB_ALG_HASH_IP_VIP, "HASH-IP-VIP"} }; #define ILBD_ALGO_TBL_SIZE (sizeof (algo_tbl) / \ sizeof (*algo_tbl)) void ilbd_algo_to_str(ilb_algo_t algo_type, char *valstr) { int i; for (i = 0; i < ILBD_ALGO_TBL_SIZE; i++) { if (algo_type == algo_tbl[i].algo_type) { (void) strlcpy(valstr, algo_tbl[i].algo_str, ILBD_MAX_VALUE_LEN); return; } } logerr("ilbd_algo_to_str: algo not found"); } static void ilbd_scf_str_to_algo(ilb_algo_t *algo_type, char *valstr) { int i; for (i = 0; i < ILBD_ALGO_TBL_SIZE; i++) { if (strcmp(valstr, algo_tbl[i].algo_str) == 0) { *algo_type = algo_tbl[i].algo_type; return; } } logerr("ilbd_scf_str_to_algo: algo not found"); } struct topo_tbl_entry { ilb_topo_t topo_type; const char *topo_str; } topo_tbl[] = { {ILB_TOPO_DSR, "DSR"}, {ILB_TOPO_NAT, "NAT"}, {ILB_TOPO_HALF_NAT, "HALF-NAT"} }; #define ILBD_TOPO_TBL_SIZE (sizeof (topo_tbl) / \ sizeof (*topo_tbl)) void ilbd_topo_to_str(ilb_topo_t topo_type, char *valstr) { int i; for (i = 0; i < ILBD_TOPO_TBL_SIZE; i++) { if (topo_type == topo_tbl[i].topo_type) { (void) strlcpy(valstr, topo_tbl[i].topo_str, ILBD_MAX_VALUE_LEN); return; } } logerr("ilbd_scf_topo_to_str: topo not found"); } static void ilbd_scf_str_to_topo(ilb_topo_t *topo_type, char *valstr) { int i; for (i = 0; i < ILBD_TOPO_TBL_SIZE; i++) { if (strcmp(valstr, topo_tbl[i].topo_str) == 0) { *topo_type = topo_tbl[i].topo_type; return; } } logerr("ilbd_scf_str_to_topo: topo not found"); } static void ilbd_get_svr_field(char *valstr, struct in6_addr *sgs_addr, int32_t *min_port, int32_t *max_port, int32_t *sgs_flags) { char *ipaddr, *ipverstr, *portstr, *flagstr; int ip_ver; ilb_ip_addr_t temp_ip; void *addrptr; char *max_portstr; ipaddr = strtok(valstr, ";"); ipverstr = strtok(NULL, ";"); portstr = strtok(NULL, ";"); flagstr = strtok(NULL, ";"); if (ipaddr == NULL || ipverstr == NULL || portstr == NULL || flagstr == NULL) { logerr("%s: invalid server fields", __func__); (void) smf_maintain_instance(ILB_FMRI, SMF_IMMEDIATE); exit(EXIT_FAILURE); } ip_ver = atoi(ipverstr); addrptr = (ip_ver == AF_INET) ? (void *)&temp_ip.ia_v4 : (void *)&temp_ip.ia_v6; if (inet_pton(ip_ver, ipaddr, addrptr) == 0) { logerr("ilbd_get_svr_field: inet_pton failed"); return; } if (ip_ver == AF_INET) { IN6_INADDR_TO_V4MAPPED(&(temp_ip.ia_v4), sgs_addr); } else { (void) memcpy(sgs_addr, &(temp_ip.ia_v6), sizeof (struct in6_addr)); } *sgs_flags = atoi(flagstr); *min_port = atoi(strtok(portstr, "-")); *min_port = ntohs(*min_port); max_portstr = strtok(NULL, "-"); if (max_portstr != NULL) { *max_port = atoi(max_portstr); *max_port = ntohs(*max_port); } } /* * Convert the info of a server to its SCF string value representation. * Argument value is assumed to be of size ILBD_MAX_VALUE_LEN. */ static void ilbd_srv_scf_val(ilbd_srv_t *srv, char *value) { char ipstr[INET6_ADDRSTRLEN]; int ipver; if (GET_AF(&srv->isv_addr) == AF_INET) { struct in_addr v4_addr; IN6_V4MAPPED_TO_INADDR(&srv->isv_addr, &v4_addr); (void) inet_ntop(AF_INET, &v4_addr, ipstr, sizeof (ipstr)); ipver = AF_INET; } else { (void) inet_ntop(AF_INET6, &srv->isv_addr, ipstr, sizeof (ipstr)); ipver = AF_INET6; } (void) snprintf(value, ILBD_MAX_VALUE_LEN, "%s;%d;%d-%d;%d", ipstr, ipver, ntohs(srv->isv_minport), ntohs(srv->isv_maxport), srv->isv_flags); } /* get the "ip:port:status" str of the #num server in the servergroup */ ilb_status_t ilbd_get_svr_info(ilbd_sg_t *sg, int num, char *valstr, char *svrname) { int i; ilbd_srv_t *tmp_srv = NULL; tmp_srv = list_head(&sg->isg_srvlist); if (tmp_srv == NULL) return (ILB_STATUS_ENOENT); for (i = 0; i < num; i++) tmp_srv = list_next(&sg->isg_srvlist, tmp_srv); assert(tmp_srv != NULL); if (valstr != NULL) ilbd_srv_scf_val(tmp_srv, valstr); if (svrname != NULL) { (void) snprintf(svrname, ILBD_MAX_NAME_LEN, "server%d", tmp_srv->isv_id); } return (ILB_STATUS_OK); } /* convert a struct in6_addr to valstr */ ilb_status_t ilbd_scf_ip_to_str(uint16_t ipversion, struct in6_addr *addr, scf_type_t *scftype, char *valstr) { size_t vallen; ilb_ip_addr_t ipaddr; void *addrptr; vallen = (ipversion == AF_INET) ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN; if (scftype != NULL) *scftype = (ipversion == AF_INET) ? SCF_TYPE_NET_ADDR_V4 : SCF_TYPE_NET_ADDR_V6; IP_COPY_IMPL_2_CLI(addr, &ipaddr); addrptr = (ipversion == AF_INET) ? (void *)&ipaddr.ia_v4 : (void *)&ipaddr.ia_v6; (void) inet_ntop(ipversion, (void *)addrptr, valstr, vallen); return (ILB_STATUS_OK); } /* * This function takes a ilbd internal data struct and translate its value to * scf value. The data struct is passed in within "data". * Upon successful return, the scf val will be stored in "val" and the scf type * will be returned in "scftype" if scftype != NULL, the number of values * translated will be in "numval" * If it failed, no data will be written to SCF */ static ilb_status_t ilbd_data_to_scfval(ilbd_scf_pg_type_t pg_type, ilbd_var_type_t type, scf_handle_t *h, void *data, scf_value_t ***val, scf_type_t *scftype, int *numval) { scf_value_t *v, **varray = NULL; int ret = ILB_STATUS_OK; int i; int scf_val_len = ILBD_MAX_VALUE_LEN; char *valstr = NULL; int valint; uint8_t valbool = 0; ilbd_rule_t *r_ent = NULL; ilbd_sg_t *s_ent = NULL; ilbd_hc_t *h_ent = NULL; switch (pg_type) { case ILBD_SCF_RULE: r_ent = (ilbd_rule_t *)data; break; case ILBD_SCF_SG: s_ent = (ilbd_sg_t *)data; break; case ILBD_SCF_HC: h_ent = (ilbd_hc_t *)data; break; } v = scf_value_create(h); if (v == NULL) return (ILB_STATUS_INTERNAL); if ((valstr = malloc(scf_val_len)) == NULL) return (ILB_STATUS_ENOMEM); switch (type) { case ILBD_RULE_STATUS: valbool = r_ent->irl_flags & ILB_FLAGS_RULE_ENABLED; break; case ILBD_RULE_VIP: ret = ilbd_scf_ip_to_str(r_ent->irl_ipversion, &r_ent->irl_vip, scftype, valstr); if (ret != ILB_STATUS_OK) { free(valstr); scf_value_destroy(v); return (ret); } break; case ILBD_RULE_PROTO: { struct protoent *protoent; protoent = getprotobynumber(r_ent->irl_proto); (void) strlcpy(valstr, protoent->p_name, scf_val_len); break; } case ILBD_RULE_PORT: (void) snprintf(valstr, scf_val_len, "%d-%d", r_ent->irl_minport, r_ent->irl_maxport); break; case ILBD_RULE_ALGO: ilbd_algo_to_str(r_ent->irl_algo, valstr); break; case ILBD_RULE_TOPO: ilbd_topo_to_str(r_ent->irl_topo, valstr); break; case ILBD_RULE_NAT_STR: ret = ilbd_scf_ip_to_str(r_ent->irl_ipversion, &r_ent->irl_nat_src_start, scftype, valstr); if (ret != ILB_STATUS_OK) { free(valstr); scf_value_destroy(v); return (ret); } break; case ILBD_RULE_NAT_END: ret = ilbd_scf_ip_to_str(r_ent->irl_ipversion, &r_ent->irl_nat_src_end, scftype, valstr); if (ret != ILB_STATUS_OK) { free(valstr); scf_value_destroy(v); return (ret); } break; case ILBD_RULE_STI_MASK: ret = ilbd_scf_ip_to_str(r_ent->irl_ipversion, &r_ent->irl_stickymask, scftype, valstr); if (ret != ILB_STATUS_OK) { free(valstr); scf_value_destroy(v); return (ret); } break; case ILBD_RULE_SGNAME: (void) strlcpy(valstr, r_ent->irl_sgname, scf_val_len); break; case ILBD_RULE_HCNAME: if (r_ent->irl_hcname[0] != '\0') (void) strlcpy(valstr, r_ent->irl_hcname, scf_val_len); else bzero(valstr, ILBD_MAX_VALUE_LEN); break; case ILBD_RULE_HCPORT: valint = r_ent->irl_hcport; break; case ILBD_RULE_HCPFLAG: valint = r_ent->irl_hcpflag; break; case ILBD_RULE_DRAINTIME: valint = r_ent->irl_conndrain; break; case ILBD_RULE_NAT_TO: valint = r_ent->irl_nat_timeout; break; case ILBD_RULE_PERS_TO: valint = r_ent->irl_sticky_timeout; break; case ILBD_SG_SERVER: if (s_ent->isg_srvcount == 0) { (void) strlcpy(valstr, "EMPTY_SERVERGROUP", scf_val_len); break; } varray = calloc(sizeof (*varray), s_ent->isg_srvcount); if (varray == NULL) { scf_value_destroy(v); free(valstr); return (ILB_STATUS_ENOMEM); } for (i = 0; i < s_ent->isg_srvcount; i++) { if (v == NULL) { for (i--; i >= 0; i--) scf_value_destroy(varray[i]); free(valstr); return (ILB_STATUS_ENOMEM); } ret = ilbd_get_svr_info(s_ent, i, valstr, NULL); if (ret != ILB_STATUS_OK) { scf_value_destroy(v); for (i--; i >= 0; i--) scf_value_destroy(varray[i]); free(valstr); free(varray); return (ret); } (void) scf_value_set_astring(v, valstr); varray[i] = v; v = scf_value_create(h); } /* the last 'v' we created will go unused, so drop it */ scf_value_destroy(v); *numval = s_ent->isg_srvcount; *val = varray; free(valstr); return (ret); case ILBD_HC_TEST: (void) strlcpy(valstr, h_ent->ihc_test, scf_val_len); break; case ILBD_HC_TIMEOUT: valint = h_ent->ihc_timeout; break; case ILBD_HC_INTERVAL: valint = h_ent->ihc_interval; break; case ILBD_HC_DEF_PING: valbool = h_ent->ihc_def_ping; break; case ILBD_HC_COUNT: valint = h_ent->ihc_count; break; } switch (*scftype) { case SCF_TYPE_BOOLEAN: scf_value_set_boolean(v, valbool); break; case SCF_TYPE_ASTRING: (void) scf_value_set_astring(v, valstr); break; case SCF_TYPE_INTEGER: scf_value_set_integer(v, valint); break; case SCF_TYPE_NET_ADDR_V4: (void) scf_value_set_from_string(v, SCF_TYPE_NET_ADDR_V4, valstr); break; case SCF_TYPE_NET_ADDR_V6: (void) scf_value_set_from_string(v, SCF_TYPE_NET_ADDR_V6, valstr); break; } free(valstr); varray = calloc(1, sizeof (*varray)); if (varray == NULL) { scf_value_destroy(v); return (ILB_STATUS_ENOMEM); } varray[0] = v; *val = varray; *numval = 1; return (ret); } /* * create a scf property group */ ilb_status_t ilbd_create_pg(ilbd_scf_pg_type_t pg_type, void *data) { ilb_status_t ret; char *pgname; scf_propertygroup_t *pg = NULL; scf_value_t **val; scf_handle_t *h; int scf_name_len = ILBD_MAX_NAME_LEN; char *scfpgbuf; /* property group name or group type */ int i, i_st, i_end; switch (pg_type) { case ILBD_SCF_RULE: { ilbd_rule_t *r_ent = (ilbd_rule_t *)data; pgname = r_ent->irl_name; i_st = 0; i_end = ILBD_RULE_VAR_NUM; break; } case ILBD_SCF_SG: { ilbd_sg_t *s_ent = (ilbd_sg_t *)data; pgname = s_ent->isg_name; i_st = ILBD_RULE_VAR_NUM; i_end = ILBD_RULE_VAR_NUM + ILBD_SG_VAR_NUM; break; } case ILBD_SCF_HC: { ilbd_hc_t *h_ent = (ilbd_hc_t *)data; pgname = h_ent->ihc_name; i_st = ILBD_RULE_VAR_NUM + ILBD_SG_VAR_NUM; i_end = ILBD_PROP_VAR_NUM; break; } default: logdebug("ilbd_create_pg: invalid pg type %d for pg %s", pg_type, pgname); return (ILB_STATUS_EINVAL); } if ((scfpgbuf = malloc(scf_name_len)) == NULL) return (ILB_STATUS_ENOMEM); ilbd_name_to_scfpgname(pg_type, pgname, scfpgbuf); ret = ilbd_scf_retrieve_pg(scfpgbuf, &pg, B_TRUE); if (ret != ILB_STATUS_OK) { free(scfpgbuf); return (ret); } h = scf_pg_handle(pg); /* fill in props */ for (i = i_st; i < i_end; i++) { int num, j; scf_type_t scftype = prop_tbl[i].scf_proptype; ret = ilbd_data_to_scfval(pg_type, prop_tbl[i].val_type, h, data, &val, &scftype, &num); if (ret != ILB_STATUS_OK) goto done; for (j = 0; j < num; j++) { if (pg_type == ILBD_SCF_SG) { ret = ilbd_get_svr_info(data, j, NULL, scfpgbuf); if (ret == ILB_STATUS_ENOENT) { (void) strlcpy(scfpgbuf, "EMPTY_SERVER", scf_name_len); } ret = ilbd_scf_set_prop(pg, scfpgbuf, scftype, val[j]); } else { ret = ilbd_scf_set_prop(pg, prop_tbl[i].scf_propname, scftype, val[j]); } scf_value_destroy(val[j]); } free(val); } done: free(scfpgbuf); ilbd_scf_destroy(h, NULL, NULL, pg); return (ret); } /* * destroy a scf property group */ static ilb_status_t ilbd_scf_delete_pg(scf_propertygroup_t *pg) { if (scf_pg_delete(pg) != 0) return (ilbd_scf_err_to_ilb_err()); return (ILB_STATUS_OK); } /* sg can have same name as rule */ ilb_status_t ilbd_destroy_pg(ilbd_scf_pg_type_t pg_t, const char *pgname) { ilb_status_t ret; scf_propertygroup_t *pg; int scf_name_len = ILBD_MAX_NAME_LEN; char *scfname; if ((scfname = malloc(scf_name_len)) == NULL) return (ILB_STATUS_ENOMEM); ilbd_name_to_scfpgname(pg_t, pgname, scfname); ret = ilbd_scf_retrieve_pg(scfname, &pg, B_FALSE); free(scfname); if (ret != ILB_STATUS_EEXIST) return (ret); ret = ilbd_scf_delete_pg(pg); ilbd_scf_destroy(scf_pg_handle(pg), NULL, NULL, pg); return (ret); } /* * Set named property to scf value specified. If property is new, * create it. */ static ilb_status_t ilbd_scf_set_prop(scf_propertygroup_t *pg, const char *propname, scf_type_t proptype, scf_value_t *val) { scf_handle_t *h = NULL; scf_property_t *prop = NULL; scf_value_t *oldval = NULL; scf_transaction_t *tx = NULL; scf_transaction_entry_t *ent = NULL; boolean_t new = B_FALSE; ilb_status_t ret = ILB_STATUS_OK; int commit_ret; h = scf_pg_handle(pg); if (h == NULL || propname == NULL) return (ILB_STATUS_EINVAL); ret = ilbd_scf_get_prop_val(pg, propname, &oldval); if (oldval != NULL) scf_value_destroy(oldval); if (ret == ILB_STATUS_ENOENT) new = B_TRUE; else if (ret != ILB_STATUS_OK) return (ret); if ((prop = scf_property_create(h)) == NULL) return (ilbd_scf_err_to_ilb_err()); if ((tx = scf_transaction_create(h)) == NULL || (ent = scf_entry_create(h)) == NULL) { ret = ilbd_scf_err_to_ilb_err(); logdebug("ilbd_scf_set_prop: create scf transaction failed\n"); goto out; } if (scf_transaction_start(tx, pg) == -1) { ret = ilbd_scf_err_to_ilb_err(); logdebug("ilbd_scf_set_prop: start scf transaction failed\n"); goto out; } if (new) { if (scf_transaction_property_new(tx, ent, propname, proptype) == -1) { ret = ilbd_scf_err_to_ilb_err(); logdebug("ilbd_scf_set_prop: create scf prop failed\n"); goto out; } } else { if (scf_transaction_property_change(tx, ent, propname, proptype) == -1) { ret = ilbd_scf_err_to_ilb_err(); logdebug("ilbd_scf_set_prop: change scf prop failed\n"); goto out; } } if (scf_entry_add_value(ent, val) != 0) { logdebug("ilbd_scf_set_prop: add scf entry failed\n"); ret = ilbd_scf_err_to_ilb_err(); goto out; } commit_ret = scf_transaction_commit(tx); switch (commit_ret) { case 1: ret = ILB_STATUS_OK; /* update pg here, so subsequent property setting succeeds */ (void) scf_pg_update(pg); break; case 0: /* transaction failed due to not having most recent pg */ ret = ILB_STATUS_INUSE; break; default: ret = ilbd_scf_err_to_ilb_err(); break; } out: if (tx != NULL) scf_transaction_destroy(tx); if (ent != NULL) scf_entry_destroy(ent); if (prop != NULL) scf_property_destroy(prop); return (ret); } /* * get a prop's scf val */ static ilb_status_t ilbd_scf_get_prop_val(scf_propertygroup_t *pg, const char *propname, scf_value_t **val) { scf_handle_t *h = NULL; scf_property_t *prop = NULL; scf_value_t *value = NULL; ilb_status_t ret = ILB_STATUS_OK; h = scf_pg_handle(pg); if (h == NULL || propname == NULL) return (ILB_STATUS_EINVAL); if ((prop = scf_property_create(h)) == NULL) return (ilbd_scf_err_to_ilb_err()); if (scf_pg_get_property(pg, propname, prop) != 0) { ret = ilbd_scf_err_to_ilb_err(); goto out; } if ((value = scf_value_create(h)) == NULL) { ret = ilbd_scf_err_to_ilb_err(); goto out; } if (scf_property_get_value(prop, value) != 0) { scf_value_destroy(value); ret = ilbd_scf_err_to_ilb_err(); goto out; } *val = value; out: if (prop != NULL) scf_property_destroy(prop); return (ret); } typedef struct ilbd_data { union { ilb_sg_info_t *sg_info; ilb_hc_info_t *hc_info; ilb_rule_info_t *rule_info; } data; ilbd_scf_pg_type_t pg_type; /* type of data */ #define sg_data data.sg_info #define hc_data data.hc_info #define rule_data data.rule_info } ilbd_data_t; void ilbd_scf_str_to_ip(int ipversion, char *ipstr, struct in6_addr *addr) { ilb_ip_addr_t ipaddr; void *addrptr; addrptr = (ipversion == AF_INET) ? (void *)&ipaddr.ia_v4 : (void *)&ipaddr.ia_v6; (void) inet_pton(ipversion, ipstr, addrptr); if (ipversion == AF_INET) { IN6_INADDR_TO_V4MAPPED(&(ipaddr.ia_v4), addr); } else { (void) memcpy(addr, &(ipaddr.ia_v6), sizeof (struct in6_addr)); } } /* * This function takes a scf value and writes it to the correct field of the * corresponding data struct. */ static ilb_status_t ilbd_scfval_to_data(const char *propname, ilbd_var_type_t ilb_type, scf_value_t *val, ilbd_data_t *ilb_data) { scf_type_t scf_type = scf_value_type(val); ilbd_scf_pg_type_t pg_type = ilb_data->pg_type; int ret = 0; ilb_rule_info_t *r_ent = NULL; ilb_sg_info_t *s_ent = NULL; ilb_hc_info_t *h_ent = NULL; char ipstr[INET6_ADDRSTRLEN]; char *valstr; int64_t valint; uint8_t valbool; int ipversion; switch (pg_type) { case ILBD_SCF_RULE: r_ent = ilb_data->rule_data; break; case ILBD_SCF_HC: h_ent = ilb_data->hc_data; break; case ILBD_SCF_SG: s_ent = ilb_data->sg_data; break; } /* get scf value out */ if ((valstr = malloc(ILBD_MAX_VALUE_LEN)) == NULL) return (ILB_STATUS_ENOMEM); switch (scf_type) { case SCF_TYPE_NET_ADDR_V4: if (scf_value_get_as_string_typed(val, SCF_TYPE_NET_ADDR_V4, ipstr, INET_ADDRSTRLEN) < 0) { free(valstr); return (ILB_STATUS_INTERNAL); } ipversion = AF_INET; break; case SCF_TYPE_NET_ADDR_V6: if (scf_value_get_as_string_typed(val, SCF_TYPE_NET_ADDR_V6, ipstr, INET6_ADDRSTRLEN) < 0) { free(valstr); return (ILB_STATUS_INTERNAL); } ipversion = AF_INET6; break; case SCF_TYPE_BOOLEAN: if (scf_value_get_boolean(val, &valbool) < 0) { free(valstr); return (ILB_STATUS_INTERNAL); } break; case SCF_TYPE_ASTRING: if (scf_value_get_astring(val, valstr, ILBD_MAX_VALUE_LEN) < 0) { free(valstr); return (ILB_STATUS_INTERNAL); } break; case SCF_TYPE_INTEGER: if (scf_value_get_integer(val, &valint) < 0) { free(valstr); return (ILB_STATUS_INTERNAL); } break; default: free(valstr); return (ILB_STATUS_INTERNAL); } ret = ILB_STATUS_OK; switch (ilb_type) { case ILBD_RULE_STATUS: if (valbool) r_ent->rl_flags |= ILB_FLAGS_RULE_ENABLED; break; case ILBD_RULE_VIP: r_ent->rl_ipversion = ipversion; ilbd_scf_str_to_ip(ipversion, ipstr, &r_ent->rl_vip); break; case ILBD_RULE_PROTO: { struct protoent *protoent; protoent = getprotobyname(valstr); r_ent->rl_proto = protoent->p_proto; break; } case ILBD_RULE_PORT: { char *token1, *token2; token1 = strtok(valstr, "-"); token2 = strtok(NULL, "-"); r_ent->rl_minport = atoi(token1); r_ent->rl_maxport = atoi(token2); break; } case ILBD_RULE_ALGO: ilbd_scf_str_to_algo(&(r_ent->rl_algo), valstr); break; case ILBD_RULE_TOPO: ilbd_scf_str_to_topo(&(r_ent->rl_topo), valstr); break; case ILBD_RULE_NAT_STR: ilbd_scf_str_to_ip(ipversion, ipstr, &r_ent->rl_nat_src_start); break; case ILBD_RULE_NAT_END: ilbd_scf_str_to_ip(ipversion, ipstr, &r_ent->rl_nat_src_end); break; case ILBD_RULE_STI_MASK: ilbd_scf_str_to_ip(ipversion, ipstr, &r_ent->rl_stickymask); if (ipversion == AF_INET) { if (!IN6_IS_ADDR_V4MAPPED_ANY(&r_ent->rl_stickymask)) r_ent->rl_flags |= ILB_FLAGS_RULE_STICKY; } else { if (!IN6_IS_ADDR_UNSPECIFIED(&r_ent->rl_stickymask)) r_ent->rl_flags |= ILB_FLAGS_RULE_STICKY; } break; case ILBD_RULE_SGNAME: (void) strlcpy(r_ent->rl_sgname, valstr, sizeof (r_ent->rl_sgname)); break; case ILBD_RULE_HCNAME: (void) strlcpy(r_ent->rl_hcname, valstr, sizeof (r_ent->rl_hcname)); break; case ILBD_RULE_HCPORT: r_ent->rl_hcport = valint; break; case ILBD_RULE_HCPFLAG: r_ent->rl_hcpflag = valint; break; case ILBD_RULE_DRAINTIME: r_ent->rl_conndrain = valint; break; case ILBD_RULE_NAT_TO: r_ent->rl_nat_timeout = valint; break; case ILBD_RULE_PERS_TO: r_ent->rl_sticky_timeout = valint; break; case ILBD_SG_SERVER: { int svr_cnt = s_ent->sg_srvcount; /* found a new server, increase the svr count of this sg */ s_ent->sg_srvcount++; /* * valstr contains information of one server in the servergroup * valstr is in the format of "ip:minport-maxport:enable" */ s_ent = realloc(s_ent, sizeof (ilb_sg_info_t) + s_ent->sg_srvcount * sizeof (ilb_sg_srv_t)); /* sgs_srvID is the sg name, leave it blank */ /* * sgs_id is the digit in propname, propname is in a format of * "server" + the digital serverID. We get the serverID by * reading from the 7th char of propname. */ s_ent->sg_servers[svr_cnt].sgs_id = atoi(&propname[6]); ilbd_get_svr_field(valstr, &s_ent->sg_servers[svr_cnt].sgs_addr, &s_ent->sg_servers[svr_cnt].sgs_minport, &s_ent->sg_servers[svr_cnt].sgs_maxport, &s_ent->sg_servers[svr_cnt].sgs_flags); ilb_data->sg_data = s_ent; break; } case ILBD_HC_TEST: (void) strlcpy(h_ent->hci_test, valstr, sizeof (h_ent->hci_test)); break; case ILBD_HC_TIMEOUT: h_ent->hci_timeout = valint; break; case ILBD_HC_INTERVAL: h_ent->hci_interval = valint; break; case ILBD_HC_DEF_PING: h_ent->hci_def_ping = valbool; break; case ILBD_HC_COUNT: h_ent->hci_count = valint; break; case ILBD_VAR_INVALID: /* * An empty server group is represented by an invalid * SCF property. So when loading a server group, this * case can be hit. But it should happen only for this * single case. So if it happens in another case, move * the service into maintenance mode. */ if (pg_type != ILBD_SCF_SG || scf_type != SCF_TYPE_ASTRING) { logerr("%s: invalid ilb type", __func__); (void) smf_maintain_instance(ILB_FMRI, SMF_IMMEDIATE); } else { logdebug("%s: invalid ilb type", __func__); } break; } free(valstr); return (ret); } static ilbd_var_type_t ilbd_name_to_valtype(const char *prop_name) { int i; for (i = 0; i < ILBD_PROP_VAR_NUM; i++) if (strncmp(prop_name, prop_tbl[i].scf_propname, strlen(prop_tbl[i].scf_propname)) == 0) return (prop_tbl[i].val_type); logdebug("ilbd_name_to_valtype: couldn't find prop %s", prop_name); return (ILBD_VAR_INVALID); } /* callback for pg_walk_prop, arg is ilbd_data_t */ static ilb_status_t ilbd_scf_load_prop(scf_propertygroup_t *pg, const char *prop_name, void *arg) { scf_handle_t *h; scf_value_t *val; ilb_status_t ret; ilbd_data_t *ilb_data = (ilbd_data_t *)arg; ilbd_var_type_t val_type = ilbd_name_to_valtype(prop_name); h = scf_pg_handle(pg); if (h == NULL) return (ILB_STATUS_EINVAL); ret = ilbd_scf_get_prop_val(pg, prop_name, &val); if (ret == ILB_STATUS_ENOENT) return (ILB_STATUS_OK); else if (ret != ILB_STATUS_OK) return (ret); /* * Load value to ilb_data. */ ret = ilbd_scfval_to_data(prop_name, val_type, val, ilb_data); out: if (val != NULL) scf_value_destroy(val); return (ret); } /* * walk properties in one prop group, arg is ilbd_data * cb is ilbd_scf_load_prop() */ static ilb_status_t ilbd_scf_pg_walk_props(scf_propertygroup_t *pg, ilb_status_t (*cb)(scf_propertygroup_t *, const char *, void *), void *arg) { scf_handle_t *h; scf_iter_t *propiter; scf_property_t *prop; int scf_name_len = ILBD_MAX_NAME_LEN; char *prop_name = NULL; ilb_status_t ret = ILB_STATUS_OK; int scf_ret = -1; h = scf_pg_handle(pg); if (h == NULL) return (ILB_STATUS_EINVAL); prop = scf_property_create(h); propiter = scf_iter_create(h); if (prop == NULL || propiter == NULL) goto out; if (scf_iter_pg_properties(propiter, pg) != 0) goto out; if ((prop_name = malloc(scf_name_len)) == NULL) { ret = ILB_STATUS_ENOMEM; goto out; } while ((scf_ret = scf_iter_next_property(propiter, prop)) == 1) { if (scf_property_get_name(prop, prop_name, scf_name_len) < 0) { ret = ilbd_scf_err_to_ilb_err(); goto out; } ret = cb(pg, prop_name, arg); if (ret != ILB_STATUS_OK) break; } out: if (prop_name != NULL) free(prop_name); if (scf_ret == -1) ret = ilbd_scf_err_to_ilb_err(); if (prop != NULL) scf_property_destroy(prop); if (propiter != NULL) scf_iter_destroy(propiter); return (ret); } /* cbs are libd_create_X */ static ilb_status_t ilbd_scf_instance_walk_pg(scf_instance_t *inst, ilbd_scf_pg_type_t pg_type, ilb_status_t (*cb)(void *, int, struct passwd *, ucred_t *), void *arg1, void *arg2) { int scf_ret; ilb_status_t ret; scf_handle_t *h; scf_iter_t *pgiter; scf_propertygroup_t *newpg; int port = *((int *)arg1); int scf_name_len = ILBD_MAX_NAME_LEN; char *pg_name = NULL; if (inst == NULL) return (ILB_STATUS_EINVAL); h = scf_instance_handle(inst); if (h == NULL) return (ILB_STATUS_EINVAL); if ((newpg = scf_pg_create(h)) == NULL) return (ilbd_scf_err_to_ilb_err()); if ((pgiter = scf_iter_create(h)) == NULL) { scf_pg_destroy(newpg); return (ilbd_scf_err_to_ilb_err()); } if ((scf_ret = scf_iter_instance_pgs(pgiter, inst)) < 0) goto out; if ((pg_name = malloc(scf_name_len)) == NULL) { ret = ILB_STATUS_ENOMEM; goto out; } while ((scf_ret = scf_iter_next_pg(pgiter, newpg)) > 0) { ilbd_data_t data; if (scf_pg_get_name(newpg, pg_name, scf_name_len) < 0) { ret = ilbd_scf_err_to_ilb_err(); goto out; } /* * if pg name indicates it's a ilb configuration, walk its prop */ data.pg_type = pg_type; data.hc_data = NULL; data.sg_data = NULL; data.rule_data = NULL; switch (pg_type) { case ILBD_SCF_RULE: if (strncmp(ILBD_PG_NAME_RULE, pg_name, strlen(ILBD_PG_NAME_RULE)) == 0) { data.rule_data = calloc(1, sizeof (ilb_rule_info_t)); if (data.rule_data == NULL) { ret = ILB_STATUS_ENOMEM; goto out; } ret = ilbd_scf_pg_walk_props(newpg, ilbd_scf_load_prop, &data); if (ret != ILB_STATUS_OK) goto out; assert(data.rule_data != NULL); /* set rule name */ (void) strlcpy(data.rule_data->rl_name, &pg_name[strlen(ILBD_PG_NAME_RULE)], sizeof (data.rule_data->rl_name)); ret = cb(data.rule_data, port, arg2, NULL); free(data.rule_data); if (ret != ILB_STATUS_OK) goto out; } break; case ILBD_SCF_SG: if (strncmp(ILBD_PG_NAME_SG, pg_name, strlen(ILBD_PG_NAME_SG)) == 0) { data.sg_data = calloc(1, sizeof (ilb_sg_info_t)); if (data.sg_data == NULL) { ret = ILB_STATUS_ENOMEM; goto out; } ret = ilbd_scf_pg_walk_props(newpg, ilbd_scf_load_prop, &data); if (ret != ILB_STATUS_OK) { free(data.sg_data); goto out; } assert(data.sg_data != NULL); /* set sg name */ (void) strlcpy(data.sg_data->sg_name, &pg_name[strlen(ILBD_PG_NAME_SG)], sizeof (data.sg_data->sg_name)); ret = cb(data.sg_data, port, arg2, NULL); if (ret != ILB_STATUS_OK) { free(data.sg_data); goto out; } /* * create a servergroup is two-step operation. * 1. create an empty servergroup. * 2. add server(s) to the group. * * since we are here from: * main_loop()->ilbd_read_config()-> * ilbd_walk_sg_pgs() * there is no cli to send. So in this * path auditing will skip the * adt_set_from_ucred() check */ if (data.sg_data->sg_srvcount > 0) { ret = ilbd_add_server_to_group( data.sg_data, port, NULL, NULL); if (ret != ILB_STATUS_OK) { free(data.sg_data); goto out; } free(data.sg_data); } } break; case ILBD_SCF_HC: if (strncmp(ILBD_PG_NAME_HC, pg_name, strlen(ILBD_PG_NAME_HC)) == 0) { data.hc_data = calloc(1, sizeof (ilb_hc_info_t)); if (data.hc_data == NULL) { ret = ILB_STATUS_ENOMEM; goto out; } ret = ilbd_scf_pg_walk_props(newpg, ilbd_scf_load_prop, &data); if (ret != ILB_STATUS_OK) goto out; assert(data.hc_data != NULL); /* set hc name */ (void) strlcpy(data.hc_data->hci_name, &pg_name[strlen(ILBD_PG_NAME_HC)], sizeof (data.hc_data->hci_name)); ret = cb(data.hc_data, port, arg2, NULL); free(data.hc_data); if (ret != ILB_STATUS_OK) goto out; } break; } } out: if (pg_name != NULL) free(pg_name); if (scf_ret < 0) ret = ilbd_scf_err_to_ilb_err(); scf_pg_destroy(newpg); scf_iter_destroy(pgiter); return (ret); } typedef ilb_status_t (*ilbd_scf_walker_fn)(void *, int, struct passwd *, ucred_t *); ilb_status_t ilbd_walk_rule_pgs(ilb_status_t (*func)(ilb_rule_info_t *, int, const struct passwd *, ucred_t *), void *arg1, void *arg2) { scf_instance_t *inst; scf_handle_t *h; scf_service_t *svc; ilb_status_t ret; ret = ilbd_scf_get_inst(&h, &svc, &inst); if (ret != ILB_STATUS_OK) return (ret); /* get rule prop group, transfer it to ilb_lrule_info_t */ ret = ilbd_scf_instance_walk_pg(inst, ILBD_SCF_RULE, (ilbd_scf_walker_fn)func, arg1, arg2); ilbd_scf_destroy(h, svc, inst, NULL); return (ret); } ilb_status_t ilbd_walk_sg_pgs(ilb_status_t (*func)(ilb_sg_info_t *, int, const struct passwd *, ucred_t *), void *arg1, void *arg2) { scf_instance_t *inst; scf_handle_t *h; scf_service_t *svc; ilb_status_t ret; ret = ilbd_scf_get_inst(&h, &svc, &inst); if (ret != ILB_STATUS_OK) return (ret); ret = ilbd_scf_instance_walk_pg(inst, ILBD_SCF_SG, (ilbd_scf_walker_fn)func, arg1, arg2); ilbd_scf_destroy(h, svc, inst, NULL); return (ret); } ilb_status_t ilbd_walk_hc_pgs(ilb_status_t (*func)(const ilb_hc_info_t *, int, const struct passwd *, ucred_t *), void *arg1, void *arg2) { scf_instance_t *inst; scf_handle_t *h; scf_service_t *svc; ilb_status_t ret; ret = ilbd_scf_get_inst(&h, &svc, &inst); if (ret != ILB_STATUS_OK) return (ret); ret = ilbd_scf_instance_walk_pg(inst, ILBD_SCF_HC, (ilbd_scf_walker_fn)func, arg1, arg2); ilbd_scf_destroy(h, svc, inst, NULL); return (ret); } ilb_status_t ilbd_change_prop(ilbd_scf_pg_type_t pg_type, const char *pg_name, const char *prop_name, void *new_val) { int ret; scf_propertygroup_t *scfpg = NULL; char *scf_pgname = NULL; scf_type_t scftype; scf_value_t *scfval; scf_handle_t *h; if ((scf_pgname = malloc(ILBD_MAX_NAME_LEN)) == NULL) return (ILB_STATUS_ENOMEM); ilbd_name_to_scfpgname(pg_type, pg_name, scf_pgname); ret = ilbd_scf_retrieve_pg(scf_pgname, &scfpg, B_FALSE); free(scf_pgname); if (ret != ILB_STATUS_EEXIST) return (ret); assert(scfpg != NULL); h = scf_pg_handle(scfpg); if (h == NULL) { ret = ILB_STATUS_EINVAL; goto done; } if ((scfval = scf_value_create(h)) == NULL) { ret = ILB_STATUS_ENOMEM; goto done; } if (pg_type == ILBD_SCF_RULE) { scftype = SCF_TYPE_BOOLEAN; scf_value_set_boolean(scfval, *(boolean_t *)new_val); } else if (pg_type == ILBD_SCF_SG) { scftype = SCF_TYPE_ASTRING; (void) scf_value_set_astring(scfval, (char *)new_val); } ret = ilbd_scf_set_prop(scfpg, prop_name, scftype, scfval); done: if (scf_pg_handle(scfpg) != NULL) scf_handle_destroy(scf_pg_handle(scfpg)); if (scfpg != NULL) scf_pg_destroy(scfpg); if (scfval != NULL) scf_value_destroy(scfval); return (ret); } /* * Update the persistent configuration with a new server, srv, added to a * server group, sg. */ ilb_status_t ilbd_scf_add_srv(ilbd_sg_t *sg, ilbd_srv_t *srv) { scf_propertygroup_t *pg; scf_handle_t *h; scf_value_t *val; ilb_status_t ret; int scf_name_len = ILBD_MAX_NAME_LEN; char *buf = NULL; if ((buf = malloc(scf_name_len)) == NULL) return (ILB_STATUS_ENOMEM); ilbd_name_to_scfpgname(ILBD_SCF_SG, sg->isg_name, buf); ret = ilbd_scf_retrieve_pg(buf, &pg, B_FALSE); /* * The server group does not exist in persistent storage. This * cannot happen. Should probably transition the service to * maintenance since it should be there. */ if (ret != ILB_STATUS_EEXIST) { logerr("ilbd_scf_add_srv: SCF update failed - entering" " maintenance mode"); (void) smf_maintain_instance(ILB_FMRI, SMF_IMMEDIATE); free(buf); return (ILB_STATUS_INTERNAL); } if ((h = scf_pg_handle(pg)) == NULL) { ilbd_scf_destroy(NULL, NULL, NULL, pg); free(buf); return (ilbd_scf_err_to_ilb_err()); } if ((val = scf_value_create(h)) == NULL) { ilbd_scf_destroy(h, NULL, NULL, pg); free(buf); return (ILB_STATUS_ENOMEM); } ilbd_srv_scf_val(srv, buf); (void) scf_value_set_astring(val, buf); (void) snprintf(buf, scf_name_len, "server%d", srv->isv_id); ret = ilbd_scf_set_prop(pg, buf, SCF_TYPE_ASTRING, val); free(buf); ilbd_scf_destroy(h, NULL, NULL, pg); scf_value_destroy(val); return (ret); } /* * Delete a server, srv, of a server group, sg, from the persistent * configuration. */ ilb_status_t ilbd_scf_del_srv(ilbd_sg_t *sg, ilbd_srv_t *srv) { ilb_status_t ret; scf_propertygroup_t *pg; scf_handle_t *h; int scf_name_len = ILBD_MAX_NAME_LEN; char *buf; scf_transaction_t *tx = NULL; scf_transaction_entry_t *entry = NULL; if ((buf = malloc(scf_name_len)) == NULL) return (ILB_STATUS_ENOMEM); ilbd_name_to_scfpgname(ILBD_SCF_SG, sg->isg_name, buf); ret = ilbd_scf_retrieve_pg(buf, &pg, B_FALSE); /* * The server group does not exist in persistent storage. This * cannot happen. THe caller of this function puts service in * maintenance mode. */ if (ret != ILB_STATUS_EEXIST) { free(buf); return (ILB_STATUS_INTERNAL); } ret = ILB_STATUS_OK; if ((h = scf_pg_handle(pg)) == NULL) { logdebug("ilbd_scf_del_srv: scf_pg_handle: %s\n", scf_strerror(scf_error())); ilbd_scf_destroy(NULL, NULL, NULL, pg); free(buf); return (ilbd_scf_err_to_ilb_err()); } if ((tx = scf_transaction_create(h)) == NULL || (entry = scf_entry_create(h)) == NULL) { logdebug("ilbd_scf_del_srv: create scf transaction failed: " "%s\n", scf_strerror(scf_error())); ret = ilbd_scf_err_to_ilb_err(); goto out; } (void) snprintf(buf, scf_name_len, "server%d", srv->isv_id); if (scf_transaction_start(tx, pg) == -1) { logdebug("ilbd_scf_set_prop: start scf transaction failed: " "%s\n", scf_strerror(scf_error())); ret = ilbd_scf_err_to_ilb_err(); goto out; } if (scf_transaction_property_delete(tx, entry, buf) == -1) { logdebug("ilbd_scf_set_prop: delete property failed: %s\n", scf_strerror(scf_error())); ret = ilbd_scf_err_to_ilb_err(); goto out; } if (scf_transaction_commit(tx) != 1) { logdebug("ilbd_scf_set_prop: commit transaction failed: %s\n", scf_strerror(scf_error())); ret = ilbd_scf_err_to_ilb_err(); } out: free(buf); if (entry != NULL) scf_entry_destroy(entry); if (tx != NULL) scf_transaction_destroy(tx); ilbd_scf_destroy(h, NULL, NULL, pg); return (ret); }