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