xref: /illumos-gate/usr/src/cmd/coreadm/coreadm.c (revision 51396a8ee7fb52fe0ab33bfe7b4f495ad431904a)
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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  * Copyright (c) 2018, Joyent, Inc.
25  */
26 
27 #include <stdio.h>
28 #include <fcntl.h>
29 #include <ctype.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <limits.h>
35 #include <libintl.h>
36 #include <locale.h>
37 #include <sys/stat.h>
38 #include <sys/corectl.h>
39 #include <libproc.h>
40 #include <libscf.h>
41 #include <libscf_priv.h>
42 #include <assert.h>
43 
44 #define	E_SUCCESS	0		/* Exit status for success */
45 #define	E_ERROR		1		/* Exit status for error */
46 #define	E_USAGE		2		/* Exit status for usage error */
47 
48 static	const	char	PATH_CONFIG[] = "/etc/coreadm.conf";
49 static	const	char	PATH_CONFIG_OLD[] = "/etc/coreadm.conf.old";
50 
51 #define	COREADM_INST_NAME	"system/coreadm:default"
52 #define	COREADM_INST_FMRI	\
53     SCF_FMRI_SVC_PREFIX SCF_FMRI_SERVICE_PREFIX COREADM_INST_NAME
54 
55 #define	CONFIG_PARAMS		"config_params"
56 #define	GLOBAL_ENABLED		"global_enabled"
57 #define	PROCESS_ENABLED		"process_enabled"
58 #define	GLOBAL_SETID_ENABLED	"global_setid_enabled"
59 #define	PROCESS_SETID_ENABLED	"process_setid_enabled"
60 #define	GLOBAL_LOG_ENABLED	"global_log_enabled"
61 #define	GLOBAL_PATTERN		"global_pattern"
62 #define	GLOBAL_CONTENT		"global_content"
63 #define	INIT_PATTERN		"init_pattern"
64 #define	INIT_CONTENT		"init_content"
65 
66 static	char		*command;
67 static	uint64_t	options;
68 static	int		alloptions;
69 static	char		*glob_pattern;
70 static	char		gpattern[PATH_MAX];
71 static	core_content_t	glob_content = CC_CONTENT_INVALID;
72 static	char		*init_pattern;
73 static	char		ipattern[PATH_MAX];
74 static	core_content_t	init_content = CC_CONTENT_INVALID;
75 static	char		*proc_pattern;
76 static	size_t		proc_size;
77 static	core_content_t	proc_content = CC_CONTENT_INVALID;
78 
79 static	int		report_settings(void);
80 static	int		do_processes(int, char **);
81 static	int		do_modify(boolean_t);
82 static	int		do_update(void);
83 static	int		do_legacy(void);
84 
85 static scf_propvec_t prop_gpattern = { GLOBAL_PATTERN, NULL, SCF_TYPE_ASTRING };
86 static scf_propvec_t prop_gcontent = { GLOBAL_CONTENT, NULL, SCF_TYPE_ASTRING };
87 static scf_propvec_t prop_ipattern = { INIT_PATTERN, NULL, SCF_TYPE_ASTRING };
88 static scf_propvec_t prop_icontent = { INIT_CONTENT, NULL, SCF_TYPE_ASTRING };
89 static scf_propvec_t prop_option[] = {
90     { GLOBAL_ENABLED, NULL, SCF_TYPE_BOOLEAN, NULL, CC_GLOBAL_PATH },
91     { PROCESS_ENABLED, NULL, SCF_TYPE_BOOLEAN, NULL, CC_PROCESS_PATH },
92     { GLOBAL_SETID_ENABLED, NULL, SCF_TYPE_BOOLEAN, NULL, CC_GLOBAL_SETID },
93     { PROCESS_SETID_ENABLED, NULL, SCF_TYPE_BOOLEAN, NULL, CC_PROCESS_SETID },
94     { GLOBAL_LOG_ENABLED, NULL, SCF_TYPE_BOOLEAN, NULL, CC_GLOBAL_LOG },
95     { NULL }
96 };
97 #define	MAX_PROPS	(4 + (sizeof (prop_option) / sizeof (scf_propvec_t)))
98 
99 static void
100 usage(void)
101 {
102 	(void) fprintf(stderr, gettext(
103 "usage:\n"));
104 	(void) fprintf(stderr, gettext(
105 "    %s [ -g pattern ] [ -i pattern ] [ -G content ] [ -I content ]\n"),
106 	    command);
107 	(void) fprintf(stderr, gettext(
108 "            [ -e {global | process | global-setid | proc-setid | log} ]\n"));
109 	(void) fprintf(stderr, gettext(
110 "            [ -d {global | process | global-setid | proc-setid | log} ]\n"));
111 	(void) fprintf(stderr, gettext(
112 "    %s [ -p pattern ] [ -P content ] [ pid ... ]\n"), command);
113 	exit(E_USAGE);
114 }
115 
116 static int
117 perm(void)
118 {
119 	(void) fprintf(stderr, gettext("%s: insufficient privileges to "
120 	    "exercise the -[GIgied] options\n"), command);
121 	return (E_USAGE);
122 }
123 
124 static int
125 parse_content(char *arg, core_content_t *content)
126 {
127 	if (proc_str2content(arg, content) == 0)
128 		return (0);
129 	(void) fprintf(stderr, gettext("%s: invalid content string '%s'\n"),
130 	    command, arg);
131 	return (1);
132 }
133 
134 int
135 main(int argc, char **argv)
136 {
137 	int flag;
138 	int opt;
139 	int modify;
140 	int update = 0;
141 	int legacy_update = 0;
142 	int error = 0;
143 	int npids;
144 	char **pidlist;
145 
146 	char curpid[11];
147 	char *curpid_ptr = &curpid[0];
148 
149 	(void) setlocale(LC_ALL, "");
150 	(void) textdomain(TEXT_DOMAIN);
151 
152 	/* command name (e.g., "coreadm") */
153 	if ((command = strrchr(argv[0], '/')) != NULL)
154 		command++;
155 	else
156 		command = argv[0];
157 
158 	while ((opt = getopt(argc, argv, "g:G:i:I:p:P:e:d:uU?")) != EOF) {
159 		switch (opt) {
160 		case 'g':
161 			glob_pattern = optarg;
162 			break;
163 		case 'i':
164 			init_pattern = optarg;
165 			break;
166 		case 'p':
167 			proc_pattern = optarg;
168 			proc_size = strlen(proc_pattern) + 1;
169 			break;
170 		case 'G':
171 			error |= parse_content(optarg, &glob_content);
172 			break;
173 		case 'I':
174 			error |= parse_content(optarg, &init_content);
175 			break;
176 		case 'P':
177 			error |= parse_content(optarg, &proc_content);
178 			break;
179 		case 'e':
180 		case 'd':
181 			if (strcmp(optarg, "global") == 0)
182 				flag = CC_GLOBAL_PATH;
183 			else if (strcmp(optarg, "process") == 0)
184 				flag = CC_PROCESS_PATH;
185 			else if (strcmp(optarg, "global-setid") == 0)
186 				flag = CC_GLOBAL_SETID;
187 			else if (strcmp(optarg, "proc-setid") == 0)
188 				flag = CC_PROCESS_SETID;
189 			else if (strcmp(optarg, "log") == 0)
190 				flag = CC_GLOBAL_LOG;
191 			else {
192 				flag = 0;
193 				error = 1;
194 			}
195 			if (opt == 'e')
196 				options |= flag;
197 			else
198 				options &= ~flag;
199 			alloptions |= flag;
200 			break;
201 		case 'U':
202 			update = 1;
203 			break;
204 		case 'u':
205 			legacy_update = 1;
206 			break;
207 		case '?':
208 		default:
209 			error = 1;
210 			break;
211 		}
212 	}
213 
214 	npids = argc - optind;
215 	pidlist = argv + optind;
216 
217 	if (error)
218 		usage();
219 
220 	/*
221 	 * If 'modify' is true, we must modify the system settings
222 	 * and update the configuration file with the new parameters.
223 	 */
224 	modify = glob_pattern != NULL || glob_content != CC_CONTENT_INVALID ||
225 	    init_pattern != NULL || init_content != CC_CONTENT_INVALID ||
226 	    alloptions != 0;
227 
228 	if ((update || legacy_update) && (modify || proc_pattern != NULL ||
229 	    proc_content != CC_CONTENT_INVALID || npids != 0)) {
230 		(void) fprintf(stderr,
231 		    gettext("%s: the -u option must stand alone\n"), command);
232 		usage();
233 	}
234 	if (modify &&
235 	    (proc_pattern != NULL || proc_content != CC_CONTENT_INVALID)) {
236 		(void) fprintf(stderr, gettext(
237 		    "%s: -[GIgied] and -[Pp] options are mutually exclusive\n"),
238 		    command);
239 		usage();
240 	}
241 	if (modify && npids != 0) {
242 		(void) fprintf(stderr, gettext(
243 		    "%s: -[GIgied] options cannot have a process-id list\n"),
244 		    command);
245 		usage();
246 	}
247 	if ((proc_pattern != NULL || proc_content != CC_CONTENT_INVALID) &&
248 	    npids == 0) {
249 		(void) sprintf(curpid, "%u", (uint_t)getppid());
250 		npids = 1;
251 		pidlist = &curpid_ptr;
252 	}
253 
254 	if (legacy_update)
255 		return (do_legacy());
256 	if (update)
257 		return (do_update());
258 	if (modify)
259 		return (do_modify(B_FALSE));
260 	if (npids != 0)
261 		return (do_processes(npids, pidlist));
262 
263 	return (report_settings());
264 }
265 
266 static int
267 report_settings(void)
268 {
269 	char content_str[PRCONTENTBUFSZ];
270 
271 	if ((options = core_get_options()) == -1) {
272 		perror("core_get_options()");
273 		return (E_ERROR);
274 	}
275 	if (core_get_global_path(gpattern, sizeof (gpattern)) != 0) {
276 		perror("core_get_global_path()");
277 		return (E_ERROR);
278 	}
279 	if (core_get_default_path(ipattern, sizeof (ipattern)) != 0) {
280 		perror("core_get_default_path()");
281 		return (E_ERROR);
282 	}
283 	if (core_get_global_content(&glob_content) != 0) {
284 		perror("core_get_global_content()");
285 		return (E_ERROR);
286 	}
287 	if (core_get_default_content(&init_content) != 0) {
288 		perror("core_get_default_content()");
289 		return (E_ERROR);
290 	}
291 
292 	(void) printf(gettext("     global core file pattern: %s\n"),
293 	    gpattern);
294 	(void) proc_content2str(glob_content, content_str,
295 	    sizeof (content_str));
296 	(void) printf(gettext("     global core file content: %s\n"),
297 	    content_str);
298 	(void) printf(gettext("       init core file pattern: %s\n"),
299 	    ipattern);
300 	(void) proc_content2str(init_content, content_str,
301 	    sizeof (content_str));
302 	(void) printf(gettext("       init core file content: %s\n"),
303 	    content_str);
304 	(void) printf(gettext("            global core dumps: %s\n"),
305 	    (options & CC_GLOBAL_PATH)? "enabled" : "disabled");
306 	(void) printf(gettext("       per-process core dumps: %s\n"),
307 	    (options & CC_PROCESS_PATH)? "enabled" : "disabled");
308 	(void) printf(gettext("      global setid core dumps: %s\n"),
309 	    (options & CC_GLOBAL_SETID)? "enabled" : "disabled");
310 	(void) printf(gettext(" per-process setid core dumps: %s\n"),
311 	    (options & CC_PROCESS_SETID)? "enabled" : "disabled");
312 	(void) printf(gettext("     global core dump logging: %s\n"),
313 	    (options & CC_GLOBAL_LOG)? "enabled" : "disabled");
314 	return (E_SUCCESS);
315 }
316 
317 static int
318 do_processes(int npids, char **pidlist)
319 {
320 	char process_path[PATH_MAX];
321 	core_content_t content;
322 	pid_t pid;
323 	char *next;
324 	int rc = E_SUCCESS;
325 	char content_str[PRCONTENTBUFSZ];
326 
327 	if (proc_pattern == NULL && proc_content == CC_CONTENT_INVALID) {
328 		while (npids-- > 0) {
329 			pid = strtol(*pidlist, &next, 10);
330 			if (*next != '\0' || !isdigit(**pidlist)) {
331 				(void) fprintf(stderr,
332 				    gettext("%s: invalid process-id\n"),
333 				    *pidlist);
334 				rc = E_USAGE;
335 			} else if (core_get_process_path(process_path,
336 			    sizeof (process_path), pid) != 0 ||
337 			    core_get_process_content(&content, pid) != 0) {
338 				perror(*pidlist);
339 				rc = E_USAGE;
340 			} else {
341 				(void) proc_content2str(content, content_str,
342 				    sizeof (content_str));
343 				(void) printf(gettext("%s:\t%s\t%s\n"),
344 				    *pidlist, process_path, content_str);
345 			}
346 			pidlist++;
347 		}
348 	} else {
349 		while (npids-- > 0) {
350 			pid = strtol(*pidlist, &next, 10);
351 			if (*next != '\0') {
352 				(void) fprintf(stderr,
353 				    gettext("%s: invalid process-id\n"),
354 				    *pidlist);
355 				rc = E_USAGE;
356 			} else {
357 				if (proc_pattern != NULL &&
358 				    core_set_process_path(proc_pattern,
359 				    proc_size, pid) != 0) {
360 					perror(*pidlist);
361 					rc = E_USAGE;
362 				}
363 
364 				if (proc_content != CC_CONTENT_INVALID &&
365 				    core_set_process_content(
366 				    &proc_content, pid) != 0) {
367 					perror(*pidlist);
368 					rc = E_USAGE;
369 				}
370 			}
371 			pidlist++;
372 		}
373 	}
374 
375 	return (rc);
376 }
377 
378 static void
379 addprop(scf_propvec_t *props, int size, int count, scf_propvec_t *pv, void *ptr)
380 {
381 	assert(count + 1 < size);
382 	props[count] = *pv;
383 	props[count].pv_ptr = ptr;
384 }
385 
386 static boolean_t
387 is_online(const char *fmri)
388 {
389 	char *state = smf_get_state(fmri);
390 	boolean_t result = state != NULL &&
391 	    strcmp(state, SCF_STATE_STRING_ONLINE) == 0;
392 
393 	free(state);
394 	return (result);
395 }
396 
397 /*
398  * The user has specified the -g, -G, -i, -I, -d, or -e options to
399  * modify the given configuration parameter. Perform the modification
400  * in the smf repository and then perform a smf_refresh_instance which
401  * will cause a coreadm -u to occur which will transfer ALL coreadm
402  * configuration information from the repository to the kernel.
403  */
404 static int
405 do_modify(boolean_t method)
406 {
407 	char gcontentstr[PRCONTENTBUFSZ];
408 	char icontentstr[PRCONTENTBUFSZ];
409 	scf_propvec_t *prop;
410 	scf_propvec_t properties[MAX_PROPS + 1];
411 	int count = 0;
412 
413 	if (!method && !is_online(COREADM_INST_FMRI)) {
414 		(void) fprintf(stderr,
415 		    gettext("%s: coreadm service not online\n"), command);
416 		return (E_ERROR);
417 	}
418 
419 	if (glob_pattern != NULL)
420 		addprop(properties, MAX_PROPS, count++, &prop_gpattern,
421 		    glob_pattern);
422 
423 	if (glob_content != CC_CONTENT_INVALID) {
424 		(void) proc_content2str(glob_content, gcontentstr,
425 		    sizeof (gcontentstr));
426 		addprop(properties, MAX_PROPS, count++, &prop_gcontent,
427 		    gcontentstr);
428 	}
429 
430 	if (init_pattern != NULL)
431 		addprop(properties, MAX_PROPS, count++, &prop_ipattern,
432 		    init_pattern);
433 
434 	if (init_content != CC_CONTENT_INVALID) {
435 		(void) proc_content2str(init_content, icontentstr,
436 		    sizeof (icontentstr));
437 		addprop(properties, MAX_PROPS, count++, &prop_icontent,
438 		    icontentstr);
439 	}
440 
441 	for (prop = prop_option; prop->pv_prop != NULL; prop++)
442 		if ((alloptions & prop->pv_aux) != 0)
443 			addprop(properties, MAX_PROPS, count++, prop, &options);
444 
445 	properties[count].pv_prop = NULL;
446 
447 	prop = NULL;
448 	if (scf_write_propvec(COREADM_INST_FMRI, CONFIG_PARAMS, properties,
449 	    &prop) == SCF_FAILED) {
450 		if (prop != NULL) {
451 			(void) fprintf(stderr, gettext(
452 			    "%s: Unable to write property '%s': %s"), command,
453 			    prop->pv_prop, scf_strerror(scf_error()));
454 		} else {
455 			(void) fprintf(stderr, gettext(
456 			    "%s: Unable to write configuration: %s\n"),
457 			    command, scf_strerror(scf_error()));
458 		}
459 		return (E_ERROR);
460 	}
461 
462 	if (smf_refresh_instance(COREADM_INST_FMRI) != 0) {
463 		(void) fprintf(stderr,
464 		    gettext("%s: Unable to refresh %s: %s\n"
465 		    "Configuration stored but not made active.\n"),
466 		    command, COREADM_INST_FMRI, scf_strerror(scf_error()));
467 		return (E_ERROR);
468 	}
469 
470 	return (E_SUCCESS);
471 }
472 
473 static const char *
474 write_kernel(void)
475 {
476 	if (core_set_global_path(glob_pattern, strlen(glob_pattern) + 1) != 0)
477 		return ("core_set_global_path()");
478 
479 	if (core_set_global_content(&glob_content) != 0)
480 		return ("core_set_global_content()");
481 
482 	if (core_set_default_path(init_pattern, strlen(init_pattern) + 1) != 0)
483 		return ("core_set_default_path()");
484 
485 	if (core_set_default_content(&init_content) != 0)
486 		return ("core_set_init_content()");
487 
488 	if (core_set_options((int)options) != 0)
489 		return ("core_set_options()");
490 
491 	return (NULL);
492 }
493 
494 /*
495  * BUFSIZE must be large enough to contain the longest path plus some more.
496  */
497 #define	BUFSIZE	(PATH_MAX + 80)
498 
499 static int
500 yes(char *name, char *value, int line)
501 {
502 	if (strcmp(value, "yes") == 0)
503 		return (1);
504 	if (strcmp(value, "no") == 0)
505 		return (0);
506 	(void) fprintf(stderr, gettext(
507 	    "\"%s\", line %d: warning: value must be yes or no: %s=%s\n"),
508 	    PATH_CONFIG, line, name, value);
509 	return (0);
510 }
511 
512 static int
513 read_legacy(void)
514 {
515 	FILE *fp;
516 	int line;
517 	char buf[BUFSIZE];
518 	char name[BUFSIZE], value[BUFSIZE];
519 	int n, len;
520 
521 	/* defaults */
522 	alloptions = CC_OPTIONS;
523 	options = CC_PROCESS_PATH;
524 	gpattern[0] = '\0';
525 	(void) strcpy(ipattern, "core");
526 	glob_content = init_content = CC_CONTENT_DEFAULT;
527 
528 	glob_pattern = gpattern;
529 	init_pattern = ipattern;
530 
531 	if ((fp = fopen(PATH_CONFIG, "r")) == NULL)
532 		return (0);
533 
534 	for (line = 1; fgets(buf, sizeof (buf), fp) != NULL; line++) {
535 		/*
536 		 * Skip comment lines and empty lines.
537 		 */
538 		if (buf[0] == '#' || buf[0] == '\n')
539 			continue;
540 		/*
541 		 * Look for "name=value", with optional whitespace on either
542 		 * side, terminated by a newline, and consuming the whole line.
543 		 */
544 		/* LINTED - unbounded string specifier */
545 		n = sscanf(buf, " %[^=]=%s \n%n", name, value, &len);
546 		if (n >= 1 && name[0] != '\0' &&
547 		    (n == 1 || len == strlen(buf))) {
548 			if (n == 1)
549 				value[0] = '\0';
550 			if (strcmp(name, "COREADM_GLOB_PATTERN") == 0) {
551 				(void) strlcpy(gpattern, value,
552 				    sizeof (gpattern));
553 				continue;
554 			}
555 			if (strcmp(name, "COREADM_GLOB_CONTENT") == 0) {
556 				(void) proc_str2content(value, &glob_content);
557 				continue;
558 			}
559 			if (strcmp(name, "COREADM_INIT_PATTERN") == 0) {
560 				(void) strlcpy(ipattern, value,
561 				    sizeof (ipattern));
562 				continue;
563 			}
564 			if (strcmp(name, "COREADM_INIT_CONTENT") == 0) {
565 				(void) proc_str2content(value, &init_content);
566 				continue;
567 			}
568 			if (strcmp(name, "COREADM_GLOB_ENABLED") == 0) {
569 				if (yes(name, value, line))
570 					options |= CC_GLOBAL_PATH;
571 				continue;
572 			}
573 			if (strcmp(name, "COREADM_PROC_ENABLED") == 0) {
574 				if (yes(name, value, line))
575 					options |= CC_PROCESS_PATH;
576 				else
577 					options &= ~CC_PROCESS_PATH;
578 				continue;
579 			}
580 			if (strcmp(name, "COREADM_GLOB_SETID_ENABLED") == 0) {
581 				if (yes(name, value, line))
582 					options |= CC_GLOBAL_SETID;
583 				continue;
584 			}
585 			if (strcmp(name, "COREADM_PROC_SETID_ENABLED") == 0) {
586 				if (yes(name, value, line))
587 					options |= CC_PROCESS_SETID;
588 				continue;
589 			}
590 			if (strcmp(name, "COREADM_GLOB_LOG_ENABLED") == 0) {
591 				if (yes(name, value, line))
592 					options |= CC_GLOBAL_LOG;
593 				continue;
594 			}
595 			(void) fprintf(stderr, gettext(
596 			    "\"%s\", line %d: warning: invalid token: %s\n"),
597 			    PATH_CONFIG, line, name);
598 		} else {
599 			(void) fprintf(stderr,
600 			    gettext("\"%s\", line %d: syntax error\n"),
601 			    PATH_CONFIG, line);
602 		}
603 	}
604 	(void) fclose(fp);
605 
606 	return (1);
607 }
608 
609 /*
610  * Loads and applies the coreadm configuration stored in the default
611  * coreadm instance.  As this option is (only) used from within an SMF
612  * service method, this function must return an SMF_EXIT_* exit status
613  * to its caller.
614  */
615 static int
616 do_update(void)
617 {
618 	char		*gcstr, *icstr;
619 	scf_propvec_t	properties[MAX_PROPS + 1];
620 	scf_propvec_t	*prop;
621 	int		count = 0;
622 	const char	*errstr;
623 
624 	if (read_legacy()) {
625 		if ((errstr = write_kernel()) != NULL)
626 			goto error;
627 
628 		if (do_modify(B_TRUE) != 0 ||
629 		    rename(PATH_CONFIG, PATH_CONFIG_OLD) != 0) {
630 			(void) fprintf(stderr, gettext(
631 			    "%s: failed to import legacy configuration.\n"),
632 			    command);
633 			return (SMF_EXIT_ERR_FATAL);
634 		}
635 		return (SMF_EXIT_OK);
636 	}
637 
638 	addprop(properties, MAX_PROPS, count++, &prop_gpattern, &glob_pattern);
639 	addprop(properties, MAX_PROPS, count++, &prop_gcontent, &gcstr);
640 	addprop(properties, MAX_PROPS, count++, &prop_ipattern, &init_pattern);
641 	addprop(properties, MAX_PROPS, count++, &prop_icontent, &icstr);
642 	for (prop = prop_option; prop->pv_prop != NULL; prop++)
643 		addprop(properties, MAX_PROPS, count++, prop, &options);
644 	properties[count].pv_prop = NULL;
645 
646 	alloptions = CC_OPTIONS;
647 	if (scf_read_propvec(COREADM_INST_FMRI, CONFIG_PARAMS, B_TRUE,
648 	    properties, &prop) == SCF_FAILED) {
649 		if (prop != NULL) {
650 			(void) fprintf(stderr, gettext(
651 			    "%s: configuration property '%s' not found.\n"),
652 			    command, prop->pv_prop);
653 		} else {
654 			(void) fprintf(stderr, gettext(
655 			    "%s: unable to read configuration: %s\n"),
656 			    command, scf_strerror(scf_error()));
657 		}
658 		return (SMF_EXIT_ERR_FATAL);
659 	}
660 
661 	(void) proc_str2content(gcstr, &glob_content);
662 	(void) proc_str2content(icstr, &init_content);
663 
664 	errstr = write_kernel();
665 	scf_clean_propvec(properties);
666 	if (errstr == NULL)
667 		return (SMF_EXIT_OK);
668 
669 error:
670 	if (errno == EPERM) {
671 		(void) perm();
672 		return (SMF_EXIT_ERR_PERM);
673 	}
674 	perror(errstr);
675 	return (SMF_EXIT_ERR_FATAL);
676 }
677 
678 static int do_legacy()
679 {
680 	const char *errstr;
681 
682 	if (read_legacy() && (errstr = write_kernel()) != NULL) {
683 		if (errno == EPERM)
684 			return (perm());
685 		perror(errstr);
686 		return (E_ERROR);
687 	}
688 
689 	return (E_SUCCESS);
690 }
691