1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2015 Nexenta Systems, Inc. All rights reserved. 24 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 25 * Copyright 2023 Oxide Computer Company 26 */ 27 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <syslog.h> 31 #include <stdarg.h> 32 #include <nfs/nfs.h> 33 #include <rpcsvc/daemon_utils.h> 34 #include <sys/sysmacros.h> 35 #include "smfcfg.h" 36 37 /* 38 * NFS version strings translation table to numeric form. 39 */ 40 static struct str_val { 41 const char *str; 42 uint32_t val; 43 } nfs_versions[] = { 44 { "2", NFS_VERS_2 }, 45 { "3", NFS_VERS_3 }, 46 { "4", NFS_VERS_4 }, 47 { "4.0", NFS_VERS_4 }, 48 { "4.1", NFS_VERS_4_1 }, 49 { "4.2", NFS_VERS_4_2 } 50 }; 51 52 /* 53 * Translate NFS version string to numeric form. 54 * Returns NFS_VERS_... value or zero for invalid version string. 55 */ 56 uint32_t 57 nfs_convert_version_str(const char *version) 58 { 59 uint32_t v = 0; 60 61 for (size_t i = 0; i < ARRAY_SIZE(nfs_versions); i++) { 62 if (strcmp(version, nfs_versions[i].str) == 0) { 63 v = nfs_versions[i].val; 64 break; 65 } 66 } 67 68 return (v); 69 } 70 71 fs_smfhandle_t * 72 fs_smf_init(const char *fmri, const char *instance) 73 { 74 fs_smfhandle_t *handle = NULL; 75 char *svcname, srv[MAXPATHLEN]; 76 77 /* 78 * svc name is of the form svc://network/fs/server:instance1 79 * FMRI portion is /network/fs/server 80 */ 81 (void) snprintf(srv, MAXPATHLEN, "%s", fmri + strlen("svc:/")); 82 svcname = strrchr(srv, ':'); 83 if (svcname != NULL) 84 *svcname = '\0'; 85 svcname = srv; 86 87 handle = calloc(1, sizeof (fs_smfhandle_t)); 88 if (handle != NULL) { 89 handle->fs_handle = scf_handle_create(SCF_VERSION); 90 if (handle->fs_handle == NULL) 91 goto out; 92 if (scf_handle_bind(handle->fs_handle) != 0) 93 goto out; 94 handle->fs_service = 95 scf_service_create(handle->fs_handle); 96 handle->fs_scope = 97 scf_scope_create(handle->fs_handle); 98 if (scf_handle_get_local_scope(handle->fs_handle, 99 handle->fs_scope) != 0) 100 goto out; 101 if (scf_scope_get_service(handle->fs_scope, 102 svcname, handle->fs_service) != SCF_SUCCESS) { 103 goto out; 104 } 105 handle->fs_pg = 106 scf_pg_create(handle->fs_handle); 107 handle->fs_instance = 108 scf_instance_create(handle->fs_handle); 109 handle->fs_property = 110 scf_property_create(handle->fs_handle); 111 handle->fs_value = 112 scf_value_create(handle->fs_handle); 113 } else { 114 fprintf(stderr, 115 gettext("Cannot access SMF repository: %s\n"), fmri); 116 } 117 return (handle); 118 119 out: 120 fs_smf_fini(handle); 121 if (scf_error() != SCF_ERROR_NOT_FOUND) { 122 fprintf(stderr, 123 gettext("SMF Initialization problem(%s): %s\n"), 124 fmri, scf_strerror(scf_error())); 125 } 126 return (NULL); 127 } 128 129 void 130 fs_smf_fini(fs_smfhandle_t *handle) 131 { 132 if (handle != NULL) { 133 scf_scope_destroy(handle->fs_scope); 134 scf_instance_destroy(handle->fs_instance); 135 scf_service_destroy(handle->fs_service); 136 scf_pg_destroy(handle->fs_pg); 137 scf_property_destroy(handle->fs_property); 138 scf_value_destroy(handle->fs_value); 139 if (handle->fs_handle != NULL) { 140 (void) scf_handle_unbind(handle->fs_handle); 141 scf_handle_destroy(handle->fs_handle); 142 } 143 free(handle); 144 } 145 } 146 147 int 148 fs_smf_set_prop(smf_fstype_t fstype, char *prop_name, char *valbuf, 149 char *instance, scf_type_t sctype, char *fmri) 150 { 151 fs_smfhandle_t *phandle = NULL; 152 scf_handle_t *handle; 153 scf_propertygroup_t *pg; 154 scf_property_t *prop; 155 scf_transaction_t *tran = NULL; 156 scf_transaction_entry_t *entry = NULL; 157 scf_instance_t *inst; 158 scf_value_t *val; 159 int valint; 160 int ret = 0; 161 char *p = NULL; 162 char *svcname, srv[MAXPATHLEN]; 163 const char *pgname; 164 165 /* 166 * The SVC names we are using currently are already 167 * appended by default. Fix this for instances project. 168 */ 169 (void) snprintf(srv, MAXPATHLEN, "%s", fmri); 170 p = strstr(fmri, ":default"); 171 if (p == NULL) { 172 (void) strcat(srv, ":"); 173 if (instance == NULL) 174 instance = "default"; 175 if (strlen(srv) + strlen(instance) > MAXPATHLEN) 176 goto out; 177 (void) strncat(srv, instance, strlen(instance)); 178 } 179 svcname = srv; 180 phandle = fs_smf_init(fmri, instance); 181 if (phandle == NULL) { 182 return (SMF_SYSTEM_ERR); 183 } 184 handle = phandle->fs_handle; 185 pg = phandle->fs_pg; 186 prop = phandle->fs_property; 187 inst = phandle->fs_instance; 188 val = phandle->fs_value; 189 tran = scf_transaction_create(handle); 190 entry = scf_entry_create(handle); 191 192 if (handle == NULL || pg == NULL || prop == NULL || 193 val == NULL|| tran == NULL || entry == NULL || inst == NULL) { 194 ret = SMF_SYSTEM_ERR; 195 goto out; 196 } 197 198 if (scf_handle_decode_fmri(handle, svcname, phandle->fs_scope, 199 phandle->fs_service, inst, NULL, NULL, 0) != 0) { 200 ret = scf_error(); 201 goto out; 202 } 203 if (fstype == AUTOFS_SMF) 204 pgname = AUTOFS_PROPS_PGNAME; 205 else 206 pgname = NFS_PROPS_PGNAME; 207 208 if (scf_instance_get_pg(inst, pgname, 209 pg) != -1) { 210 uint8_t vint; 211 if (scf_transaction_start(tran, pg) == -1) { 212 ret = scf_error(); 213 goto out; 214 } 215 switch (sctype) { 216 case SCF_TYPE_INTEGER: 217 errno = 0; 218 valint = strtoul(valbuf, NULL, 0); 219 if (errno != 0) { 220 ret = SMF_SYSTEM_ERR; 221 goto out; 222 } 223 if (scf_transaction_property_change(tran, 224 entry, prop_name, SCF_TYPE_INTEGER) == 0) { 225 scf_value_set_integer(val, valint); 226 if (scf_entry_add_value(entry, val) < 0) { 227 ret = scf_error(); 228 goto out; 229 } 230 } 231 break; 232 case SCF_TYPE_ASTRING: 233 if (scf_transaction_property_change(tran, entry, 234 prop_name, SCF_TYPE_ASTRING) == 0) { 235 if (scf_value_set_astring(val, 236 valbuf) == 0) { 237 if (scf_entry_add_value(entry, 238 val) != 0) { 239 ret = scf_error(); 240 goto out; 241 } 242 } else 243 ret = SMF_SYSTEM_ERR; 244 } else 245 ret = SMF_SYSTEM_ERR; 246 break; 247 case SCF_TYPE_BOOLEAN: 248 if (strcmp(valbuf, "1") == 0) { 249 vint = 1; 250 } else if (strcmp(valbuf, "0") == 0) { 251 vint = 0; 252 } else { 253 ret = SMF_SYSTEM_ERR; 254 break; 255 } 256 if (scf_transaction_property_change(tran, entry, 257 prop_name, SCF_TYPE_BOOLEAN) == 0) { 258 scf_value_set_boolean(val, (uint8_t)vint); 259 if (scf_entry_add_value(entry, val) != 0) { 260 ret = scf_error(); 261 goto out; 262 } 263 } else { 264 ret = SMF_SYSTEM_ERR; 265 } 266 break; 267 default: 268 break; 269 } 270 if (ret != SMF_SYSTEM_ERR) 271 (void) scf_transaction_commit(tran); 272 } 273 out: 274 if (tran != NULL) 275 scf_transaction_destroy(tran); 276 if (entry != NULL) 277 scf_entry_destroy(entry); 278 fs_smf_fini(phandle); 279 return (ret); 280 } 281 282 int 283 fs_smf_get_prop(smf_fstype_t fstype, char *prop_name, char *cbuf, 284 char *instance, scf_type_t sctype, char *fmri, int *bufsz) 285 { 286 fs_smfhandle_t *phandle = NULL; 287 scf_handle_t *handle; 288 scf_propertygroup_t *pg; 289 scf_property_t *prop; 290 scf_value_t *val; 291 scf_instance_t *inst; 292 int ret = 0, len = 0, length; 293 int64_t valint = 0; 294 char srv[MAXPATHLEN], *p, *svcname; 295 const char *pgname; 296 uint8_t bval; 297 298 /* 299 * The SVC names we are using currently are already 300 * appended by default. Fix this for instances project. 301 */ 302 (void) snprintf(srv, MAXPATHLEN, "%s", fmri); 303 p = strstr(fmri, ":default"); 304 if (p == NULL) { 305 (void) strcat(srv, ":"); 306 if (instance == NULL) 307 instance = "default"; 308 if (strlen(srv) + strlen(instance) > MAXPATHLEN) 309 goto out; 310 (void) strncat(srv, instance, strlen(instance)); 311 } 312 svcname = srv; 313 phandle = fs_smf_init(fmri, instance); 314 if (phandle == NULL) 315 return (SMF_SYSTEM_ERR); 316 handle = phandle->fs_handle; 317 pg = phandle->fs_pg; 318 inst = phandle->fs_instance; 319 prop = phandle->fs_property; 320 val = phandle->fs_value; 321 322 if (handle == NULL || pg == NULL || prop == NULL || val == NULL || 323 inst == NULL) { 324 return (SMF_SYSTEM_ERR); 325 } 326 327 328 if (scf_handle_decode_fmri(handle, svcname, phandle->fs_scope, 329 phandle->fs_service, inst, NULL, NULL, 0) != 0) { 330 ret = scf_error(); 331 goto out; 332 } 333 334 if (fstype == AUTOFS_SMF) 335 pgname = AUTOFS_PROPS_PGNAME; 336 else 337 pgname = NFS_PROPS_PGNAME; 338 339 if (scf_instance_get_pg(inst, pgname, pg) != -1) { 340 if (scf_pg_get_property(pg, prop_name, 341 prop) != SCF_SUCCESS) { 342 ret = scf_error(); 343 goto out; 344 } 345 if (scf_property_get_value(prop, val) != SCF_SUCCESS) { 346 ret = scf_error(); 347 goto out; 348 } 349 switch (sctype) { 350 case SCF_TYPE_ASTRING: 351 len = scf_value_get_astring(val, cbuf, *bufsz); 352 if (len < 0 || len > *bufsz) { 353 ret = scf_error(); 354 goto out; 355 } 356 ret = 0; 357 *bufsz = len; 358 break; 359 case SCF_TYPE_INTEGER: 360 if (scf_value_get_integer(val, &valint) != 0) { 361 ret = scf_error(); 362 goto out; 363 } 364 length = snprintf(cbuf, *bufsz, "%lld", valint); 365 if (length < 0 || length > *bufsz) { 366 ret = SA_BAD_VALUE; 367 goto out; 368 } 369 ret = 0; 370 break; 371 case SCF_TYPE_BOOLEAN: 372 if (scf_value_get_boolean(val, &bval) != 0) { 373 ret = scf_error(); 374 goto out; 375 } 376 if (bval == 1) { 377 length = snprintf(cbuf, *bufsz, "%s", "true"); 378 } else { 379 length = snprintf(cbuf, *bufsz, "%s", "false"); 380 } 381 if (length < 0 || length > *bufsz) { 382 ret = SA_BAD_VALUE; 383 goto out; 384 } 385 break; 386 default: 387 break; 388 } 389 } else { 390 ret = scf_error(); 391 } 392 if ((ret != 0) && scf_error() != SCF_ERROR_NONE) 393 fprintf(stdout, gettext("%s\n"), scf_strerror(ret)); 394 out: 395 fs_smf_fini(phandle); 396 return (ret); 397 } 398 399 400 int 401 nfs_smf_get_prop(char *prop_name, char *propbuf, char *instance, 402 scf_type_t sctype, char *svc_name, int *bufsz) 403 { 404 return (fs_smf_get_prop(NFS_SMF, prop_name, propbuf, 405 instance, sctype, svc_name, bufsz)); 406 } 407 408 /* Get an integer (base 10) property */ 409 int 410 nfs_smf_get_iprop(char *prop_name, int *rvp, char *instance, 411 scf_type_t sctype, char *svc_name) 412 { 413 char propbuf[32]; 414 int bufsz, rc, val; 415 416 bufsz = sizeof (propbuf); 417 rc = fs_smf_get_prop(NFS_SMF, prop_name, propbuf, 418 instance, sctype, svc_name, &bufsz); 419 if (rc != SA_OK) 420 return (rc); 421 errno = 0; 422 val = strtol(propbuf, NULL, 10); 423 if (errno != 0) 424 return (SA_BAD_VALUE); 425 *rvp = val; 426 return (SA_OK); 427 } 428 429 int 430 nfs_smf_set_prop(char *prop_name, char *value, char *instance, 431 scf_type_t type, char *svc_name) 432 { 433 return (fs_smf_set_prop(NFS_SMF, prop_name, value, instance, 434 type, svc_name)); 435 } 436 437 int 438 autofs_smf_set_prop(char *prop_name, char *value, char *instance, 439 scf_type_t type, char *svc_name) 440 { 441 return (fs_smf_set_prop(AUTOFS_SMF, prop_name, value, instance, 442 type, svc_name)); 443 } 444 445 int 446 autofs_smf_get_prop(char *prop_name, char *propbuf, char *instance, 447 scf_type_t sctype, char *svc_name, int *bufsz) 448 { 449 return (fs_smf_get_prop(AUTOFS_SMF, prop_name, propbuf, 450 instance, sctype, svc_name, bufsz)); 451 } 452 453 boolean_t 454 string_to_boolean(const char *str) 455 { 456 if (strcasecmp(str, "true") == 0 || atoi(str) == 1 || 457 strcasecmp(str, "on") == 0 || strcasecmp(str, "yes") == 0) { 458 return (B_TRUE); 459 } else 460 return (B_FALSE); 461 } 462 463 /* 464 * upgrade server_versmin and server_versmax from int to string. 465 * This is needed to allow to specify version as major.minor. 466 */ 467 static void 468 nfs_upgrade_server_vers(const char *fmri) 469 { 470 fs_smfhandle_t *phandle; 471 scf_handle_t *handle; 472 scf_propertygroup_t *pg; 473 scf_instance_t *inst; 474 scf_value_t *vmin = NULL, *vmax = NULL; 475 scf_transaction_t *tran = NULL; 476 scf_transaction_entry_t *emin = NULL, *emax = NULL; 477 char versmax[32]; 478 char versmin[32]; 479 int bufsz; 480 481 /* 482 * Read old integer values, stop in case of error - apparently 483 * the upgrade is already done. 484 */ 485 bufsz = sizeof (versmax); 486 if (nfs_smf_get_prop("server_versmax", versmax, DEFAULT_INSTANCE, 487 SCF_TYPE_INTEGER, (char *)fmri, &bufsz) != SA_OK) { 488 return; 489 } 490 bufsz = sizeof (versmin); 491 if (nfs_smf_get_prop("server_versmin", versmin, DEFAULT_INSTANCE, 492 SCF_TYPE_INTEGER, (char *)fmri, &bufsz) != SA_OK) { 493 return; 494 } 495 496 /* Write back as SCF_TYPE_ASTRING */ 497 phandle = fs_smf_init(fmri, NULL); 498 if (phandle == NULL) 499 return; 500 501 handle = phandle->fs_handle; 502 if (handle == NULL) 503 goto done; 504 pg = phandle->fs_pg; 505 inst = phandle->fs_instance; 506 tran = scf_transaction_create(handle); 507 vmin = scf_value_create(handle); 508 vmax = scf_value_create(handle); 509 emin = scf_entry_create(handle); 510 emax = scf_entry_create(handle); 511 512 if (pg == NULL || inst == NULL || tran == NULL || 513 emin == NULL || emax == NULL || vmin == NULL || vmax == NULL) { 514 goto done; 515 } 516 517 if (scf_handle_decode_fmri(handle, (char *)fmri, 518 phandle->fs_scope, phandle->fs_service, inst, NULL, NULL, 0) != 0) { 519 goto done; 520 } 521 522 if (scf_instance_get_pg(inst, NFS_PROPS_PGNAME, pg) == -1) 523 goto done; 524 525 if (scf_pg_update(pg) == -1) 526 goto done; 527 528 if (scf_transaction_start(tran, pg) == -1) 529 goto done; 530 531 if (scf_transaction_property_change_type(tran, emax, 532 "server_versmax", SCF_TYPE_ASTRING) != 0) { 533 goto done; 534 } 535 if (scf_value_set_astring(vmax, versmax) == 0) { 536 if (scf_entry_add_value(emax, vmax) != 0) 537 goto done; 538 } else { 539 goto done; 540 } 541 542 if (scf_transaction_property_change_type(tran, emin, 543 "server_versmin", SCF_TYPE_ASTRING) != 0) { 544 goto done; 545 } 546 if (scf_value_set_astring(vmin, versmin) == 0) { 547 if (scf_entry_add_value(emin, vmin) != 0) 548 goto done; 549 } else { 550 goto done; 551 } 552 553 (void) scf_transaction_commit(tran); 554 done: 555 if (tran != NULL) 556 scf_transaction_destroy(tran); 557 if (emin != NULL) 558 scf_entry_destroy(emin); 559 if (emax != NULL) 560 scf_entry_destroy(emax); 561 if (vmin != NULL) 562 scf_value_destroy(vmin); 563 if (vmax != NULL) 564 scf_value_destroy(vmax); 565 fs_smf_fini(phandle); 566 } 567 568 void 569 nfs_config_upgrade(const char *svc_name) 570 { 571 if (strcmp(svc_name, NFSD) == 0) { 572 nfs_upgrade_server_vers(svc_name); 573 } 574 } 575