/* * 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 2015 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2023 Oxide Computer Company */ #include #include #include #include #include #include #include #include "smfcfg.h" /* * NFS version strings translation table to numeric form. */ static struct str_val { const char *str; uint32_t val; } nfs_versions[] = { { "2", NFS_VERS_2 }, { "3", NFS_VERS_3 }, { "4", NFS_VERS_4 }, { "4.0", NFS_VERS_4 }, { "4.1", NFS_VERS_4_1 }, { "4.2", NFS_VERS_4_2 } }; /* * Translate NFS version string to numeric form. * Returns NFS_VERS_... value or zero for invalid version string. */ uint32_t nfs_convert_version_str(const char *version) { uint32_t v = 0; for (size_t i = 0; i < ARRAY_SIZE(nfs_versions); i++) { if (strcmp(version, nfs_versions[i].str) == 0) { v = nfs_versions[i].val; break; } } return (v); } fs_smfhandle_t * fs_smf_init(const char *fmri, const char *instance) { fs_smfhandle_t *handle = NULL; char *svcname, srv[MAXPATHLEN]; /* * svc name is of the form svc://network/fs/server:instance1 * FMRI portion is /network/fs/server */ (void) snprintf(srv, MAXPATHLEN, "%s", fmri + strlen("svc:/")); svcname = strrchr(srv, ':'); if (svcname != NULL) *svcname = '\0'; svcname = srv; handle = calloc(1, sizeof (fs_smfhandle_t)); if (handle != NULL) { handle->fs_handle = scf_handle_create(SCF_VERSION); if (handle->fs_handle == NULL) goto out; if (scf_handle_bind(handle->fs_handle) != 0) goto out; handle->fs_service = scf_service_create(handle->fs_handle); handle->fs_scope = scf_scope_create(handle->fs_handle); if (scf_handle_get_local_scope(handle->fs_handle, handle->fs_scope) != 0) goto out; if (scf_scope_get_service(handle->fs_scope, svcname, handle->fs_service) != SCF_SUCCESS) { goto out; } handle->fs_pg = scf_pg_create(handle->fs_handle); handle->fs_instance = scf_instance_create(handle->fs_handle); handle->fs_property = scf_property_create(handle->fs_handle); handle->fs_value = scf_value_create(handle->fs_handle); } else { fprintf(stderr, gettext("Cannot access SMF repository: %s\n"), fmri); } return (handle); out: fs_smf_fini(handle); if (scf_error() != SCF_ERROR_NOT_FOUND) { fprintf(stderr, gettext("SMF Initialization problem(%s): %s\n"), fmri, scf_strerror(scf_error())); } return (NULL); } void fs_smf_fini(fs_smfhandle_t *handle) { if (handle != NULL) { scf_scope_destroy(handle->fs_scope); scf_instance_destroy(handle->fs_instance); scf_service_destroy(handle->fs_service); scf_pg_destroy(handle->fs_pg); scf_property_destroy(handle->fs_property); scf_value_destroy(handle->fs_value); if (handle->fs_handle != NULL) { (void) scf_handle_unbind(handle->fs_handle); scf_handle_destroy(handle->fs_handle); } free(handle); } } int fs_smf_set_prop(smf_fstype_t fstype, char *prop_name, char *valbuf, char *instance, scf_type_t sctype, char *fmri) { fs_smfhandle_t *phandle = NULL; scf_handle_t *handle; scf_propertygroup_t *pg; scf_property_t *prop; scf_transaction_t *tran = NULL; scf_transaction_entry_t *entry = NULL; scf_instance_t *inst; scf_value_t *val; int valint; int ret = 0; char *p = NULL; char *svcname, srv[MAXPATHLEN]; const char *pgname; /* * The SVC names we are using currently are already * appended by default. Fix this for instances project. */ (void) snprintf(srv, MAXPATHLEN, "%s", fmri); p = strstr(fmri, ":default"); if (p == NULL) { (void) strcat(srv, ":"); if (instance == NULL) instance = "default"; if (strlen(srv) + strlen(instance) > MAXPATHLEN) goto out; (void) strncat(srv, instance, strlen(instance)); } svcname = srv; phandle = fs_smf_init(fmri, instance); if (phandle == NULL) { return (SMF_SYSTEM_ERR); } handle = phandle->fs_handle; pg = phandle->fs_pg; prop = phandle->fs_property; inst = phandle->fs_instance; val = phandle->fs_value; tran = scf_transaction_create(handle); entry = scf_entry_create(handle); if (handle == NULL || pg == NULL || prop == NULL || val == NULL|| tran == NULL || entry == NULL || inst == NULL) { ret = SMF_SYSTEM_ERR; goto out; } if (scf_handle_decode_fmri(handle, svcname, phandle->fs_scope, phandle->fs_service, inst, NULL, NULL, 0) != 0) { ret = scf_error(); goto out; } if (fstype == AUTOFS_SMF) pgname = AUTOFS_PROPS_PGNAME; else pgname = NFS_PROPS_PGNAME; if (scf_instance_get_pg(inst, pgname, pg) != -1) { uint8_t vint; if (scf_transaction_start(tran, pg) == -1) { ret = scf_error(); goto out; } switch (sctype) { case SCF_TYPE_INTEGER: errno = 0; valint = strtoul(valbuf, NULL, 0); if (errno != 0) { ret = SMF_SYSTEM_ERR; goto out; } if (scf_transaction_property_change(tran, entry, prop_name, SCF_TYPE_INTEGER) == 0) { scf_value_set_integer(val, valint); if (scf_entry_add_value(entry, val) < 0) { ret = scf_error(); goto out; } } break; case SCF_TYPE_ASTRING: if (scf_transaction_property_change(tran, entry, prop_name, SCF_TYPE_ASTRING) == 0) { if (scf_value_set_astring(val, valbuf) == 0) { if (scf_entry_add_value(entry, val) != 0) { ret = scf_error(); goto out; } } else ret = SMF_SYSTEM_ERR; } else ret = SMF_SYSTEM_ERR; break; case SCF_TYPE_BOOLEAN: if (strcmp(valbuf, "1") == 0) { vint = 1; } else if (strcmp(valbuf, "0") == 0) { vint = 0; } else { ret = SMF_SYSTEM_ERR; break; } if (scf_transaction_property_change(tran, entry, prop_name, SCF_TYPE_BOOLEAN) == 0) { scf_value_set_boolean(val, (uint8_t)vint); if (scf_entry_add_value(entry, val) != 0) { ret = scf_error(); goto out; } } else { ret = SMF_SYSTEM_ERR; } break; default: break; } if (ret != SMF_SYSTEM_ERR) (void) scf_transaction_commit(tran); } out: if (tran != NULL) scf_transaction_destroy(tran); if (entry != NULL) scf_entry_destroy(entry); fs_smf_fini(phandle); return (ret); } int fs_smf_get_prop(smf_fstype_t fstype, char *prop_name, char *cbuf, char *instance, scf_type_t sctype, char *fmri, int *bufsz) { fs_smfhandle_t *phandle = NULL; scf_handle_t *handle; scf_propertygroup_t *pg; scf_property_t *prop; scf_value_t *val; scf_instance_t *inst; int ret = 0, len = 0, length; int64_t valint = 0; char srv[MAXPATHLEN], *p, *svcname; const char *pgname; uint8_t bval; /* * The SVC names we are using currently are already * appended by default. Fix this for instances project. */ (void) snprintf(srv, MAXPATHLEN, "%s", fmri); p = strstr(fmri, ":default"); if (p == NULL) { (void) strcat(srv, ":"); if (instance == NULL) instance = "default"; if (strlen(srv) + strlen(instance) > MAXPATHLEN) goto out; (void) strncat(srv, instance, strlen(instance)); } svcname = srv; phandle = fs_smf_init(fmri, instance); if (phandle == NULL) return (SMF_SYSTEM_ERR); handle = phandle->fs_handle; pg = phandle->fs_pg; inst = phandle->fs_instance; prop = phandle->fs_property; val = phandle->fs_value; if (handle == NULL || pg == NULL || prop == NULL || val == NULL || inst == NULL) { return (SMF_SYSTEM_ERR); } if (scf_handle_decode_fmri(handle, svcname, phandle->fs_scope, phandle->fs_service, inst, NULL, NULL, 0) != 0) { ret = scf_error(); goto out; } if (fstype == AUTOFS_SMF) pgname = AUTOFS_PROPS_PGNAME; else pgname = NFS_PROPS_PGNAME; if (scf_instance_get_pg(inst, pgname, pg) != -1) { if (scf_pg_get_property(pg, prop_name, prop) != SCF_SUCCESS) { ret = scf_error(); goto out; } if (scf_property_get_value(prop, val) != SCF_SUCCESS) { ret = scf_error(); goto out; } switch (sctype) { case SCF_TYPE_ASTRING: len = scf_value_get_astring(val, cbuf, *bufsz); if (len < 0 || len > *bufsz) { ret = scf_error(); goto out; } ret = 0; *bufsz = len; break; case SCF_TYPE_INTEGER: if (scf_value_get_integer(val, &valint) != 0) { ret = scf_error(); goto out; } length = snprintf(cbuf, *bufsz, "%lld", valint); if (length < 0 || length > *bufsz) { ret = SA_BAD_VALUE; goto out; } ret = 0; break; case SCF_TYPE_BOOLEAN: if (scf_value_get_boolean(val, &bval) != 0) { ret = scf_error(); goto out; } if (bval == 1) { length = snprintf(cbuf, *bufsz, "%s", "true"); } else { length = snprintf(cbuf, *bufsz, "%s", "false"); } if (length < 0 || length > *bufsz) { ret = SA_BAD_VALUE; goto out; } break; default: break; } } else { ret = scf_error(); } if ((ret != 0) && scf_error() != SCF_ERROR_NONE) fprintf(stdout, gettext("%s\n"), scf_strerror(ret)); out: fs_smf_fini(phandle); return (ret); } int nfs_smf_get_prop(char *prop_name, char *propbuf, char *instance, scf_type_t sctype, char *svc_name, int *bufsz) { return (fs_smf_get_prop(NFS_SMF, prop_name, propbuf, instance, sctype, svc_name, bufsz)); } /* Get an integer (base 10) property */ int nfs_smf_get_iprop(char *prop_name, int *rvp, char *instance, scf_type_t sctype, char *svc_name) { char propbuf[32]; int bufsz, rc, val; bufsz = sizeof (propbuf); rc = fs_smf_get_prop(NFS_SMF, prop_name, propbuf, instance, sctype, svc_name, &bufsz); if (rc != SA_OK) return (rc); errno = 0; val = strtol(propbuf, NULL, 10); if (errno != 0) return (SA_BAD_VALUE); *rvp = val; return (SA_OK); } int nfs_smf_set_prop(char *prop_name, char *value, char *instance, scf_type_t type, char *svc_name) { return (fs_smf_set_prop(NFS_SMF, prop_name, value, instance, type, svc_name)); } int autofs_smf_set_prop(char *prop_name, char *value, char *instance, scf_type_t type, char *svc_name) { return (fs_smf_set_prop(AUTOFS_SMF, prop_name, value, instance, type, svc_name)); } int autofs_smf_get_prop(char *prop_name, char *propbuf, char *instance, scf_type_t sctype, char *svc_name, int *bufsz) { return (fs_smf_get_prop(AUTOFS_SMF, prop_name, propbuf, instance, sctype, svc_name, bufsz)); } boolean_t string_to_boolean(const char *str) { if (strcasecmp(str, "true") == 0 || atoi(str) == 1 || strcasecmp(str, "on") == 0 || strcasecmp(str, "yes") == 0) { return (B_TRUE); } else return (B_FALSE); } /* * upgrade server_versmin and server_versmax from int to string. * This is needed to allow to specify version as major.minor. */ static void nfs_upgrade_server_vers(const char *fmri) { fs_smfhandle_t *phandle; scf_handle_t *handle; scf_propertygroup_t *pg; scf_instance_t *inst; scf_value_t *vmin = NULL, *vmax = NULL; scf_transaction_t *tran = NULL; scf_transaction_entry_t *emin = NULL, *emax = NULL; char versmax[32]; char versmin[32]; int bufsz; /* * Read old integer values, stop in case of error - apparently * the upgrade is already done. */ bufsz = sizeof (versmax); if (nfs_smf_get_prop("server_versmax", versmax, DEFAULT_INSTANCE, SCF_TYPE_INTEGER, (char *)fmri, &bufsz) != SA_OK) { return; } bufsz = sizeof (versmin); if (nfs_smf_get_prop("server_versmin", versmin, DEFAULT_INSTANCE, SCF_TYPE_INTEGER, (char *)fmri, &bufsz) != SA_OK) { return; } /* Write back as SCF_TYPE_ASTRING */ phandle = fs_smf_init(fmri, NULL); if (phandle == NULL) return; handle = phandle->fs_handle; if (handle == NULL) goto done; pg = phandle->fs_pg; inst = phandle->fs_instance; tran = scf_transaction_create(handle); vmin = scf_value_create(handle); vmax = scf_value_create(handle); emin = scf_entry_create(handle); emax = scf_entry_create(handle); if (pg == NULL || inst == NULL || tran == NULL || emin == NULL || emax == NULL || vmin == NULL || vmax == NULL) { goto done; } if (scf_handle_decode_fmri(handle, (char *)fmri, phandle->fs_scope, phandle->fs_service, inst, NULL, NULL, 0) != 0) { goto done; } if (scf_instance_get_pg(inst, NFS_PROPS_PGNAME, pg) == -1) goto done; if (scf_pg_update(pg) == -1) goto done; if (scf_transaction_start(tran, pg) == -1) goto done; if (scf_transaction_property_change_type(tran, emax, "server_versmax", SCF_TYPE_ASTRING) != 0) { goto done; } if (scf_value_set_astring(vmax, versmax) == 0) { if (scf_entry_add_value(emax, vmax) != 0) goto done; } else { goto done; } if (scf_transaction_property_change_type(tran, emin, "server_versmin", SCF_TYPE_ASTRING) != 0) { goto done; } if (scf_value_set_astring(vmin, versmin) == 0) { if (scf_entry_add_value(emin, vmin) != 0) goto done; } else { goto done; } (void) scf_transaction_commit(tran); done: if (tran != NULL) scf_transaction_destroy(tran); if (emin != NULL) scf_entry_destroy(emin); if (emax != NULL) scf_entry_destroy(emax); if (vmin != NULL) scf_value_destroy(vmin); if (vmax != NULL) scf_value_destroy(vmax); fs_smf_fini(phandle); } void nfs_config_upgrade(const char *svc_name) { if (strcmp(svc_name, NFSD) == 0) { nfs_upgrade_server_vers(svc_name); } }