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
nfs_convert_version_str(const char * version)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 *
fs_smf_init(const char * fmri,const char * instance)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
fs_smf_fini(fs_smfhandle_t * handle)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
fs_smf_set_prop(smf_fstype_t fstype,char * prop_name,char * valbuf,char * instance,scf_type_t sctype,char * fmri)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
fs_smf_get_prop(smf_fstype_t fstype,char * prop_name,char * cbuf,char * instance,scf_type_t sctype,char * fmri,int * bufsz)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
nfs_smf_get_prop(char * prop_name,char * propbuf,char * instance,scf_type_t sctype,char * svc_name,int * bufsz)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
nfs_smf_get_iprop(char * prop_name,int * rvp,char * instance,scf_type_t sctype,char * svc_name)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
nfs_smf_set_prop(char * prop_name,char * value,char * instance,scf_type_t type,char * svc_name)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
autofs_smf_set_prop(char * prop_name,char * value,char * instance,scf_type_t type,char * svc_name)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
autofs_smf_get_prop(char * prop_name,char * propbuf,char * instance,scf_type_t sctype,char * svc_name,int * bufsz)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
string_to_boolean(const char * str)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
nfs_upgrade_server_vers(const char * fmri)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
nfs_config_upgrade(const char * svc_name)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