xref: /illumos-gate/usr/src/cmd/fs.d/nfs/lib/smfcfg.c (revision 9164a50bf932130cbb5097a16f6986873ce0e6e5)
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