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