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.
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.
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]
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2012 Milan Jurik. All rights reserved.
28 * Copyright (c) 2013, Joyent, Inc. All rights reserved.
32 * fork.c - safe forking for svc.startd
34 * fork_configd() and fork_sulogin() are related, special cases that handle the
35 * spawning of specific client processes for svc.startd.
38 #include <sys/contract/process.h>
39 #include <sys/corectl.h>
42 #include <sys/types.h>
48 #include <libcontract.h>
49 #include <libcontract_priv.h>
50 #include <libscf_priv.h>
63 #include "manifest_hash.h"
64 #include "configd_exit.h"
68 static struct utmpx
*utmpp
; /* pointer for getutxent() */
71 startd_fork1(int *forkerr
)
83 if (p
== -1 && forkerr
!= NULL
)
96 * void fork_mount(char *, char *)
97 * Run mount(8) with the given options and mount point. (mount(8) has much
98 * hidden knowledge; it's much less correct to reimplement that logic here to
99 * save a fork(2)/exec(2) invocation.)
102 fork_mount(char *path
, char *opts
)
108 for (pid
= fork1(); pid
== -1; pid
= fork1()) {
109 if (++tries
> MAX_MOUNT_RETRIES
)
116 (void) waitpid(pid
, &status
, 0);
119 * If our mount(8) invocation exited by peculiar means, or with
120 * a non-zero status, our mount likelihood is low.
122 if (!WIFEXITED(status
) ||
123 WEXITSTATUS(status
) != 0)
129 (void) execl("/sbin/mount", "mount", "-o", opts
, path
, NULL
);
135 * pid_t fork_common(...)
136 * Common routine used by fork_sulogin, fork_emi, and fork_configd to
137 * fork a process in a contract with the provided terms. Invokes
138 * fork_sulogin (with its no-fork argument set) on errors.
141 fork_common(const char *name
, const char *svc_fmri
, int retries
, ctid_t
*ctidp
,
142 uint_t inf
, uint_t crit
, uint_t fatal
, uint_t param
, uint64_t cookie
)
149 * Establish process contract terms.
151 if ((ctfd
= open(CTFS_ROOT
"/process/template", O_RDWR
)) == -1) {
152 fork_sulogin(B_TRUE
, "Could not open process contract template "
153 "for %s: %s\n", name
, strerror(errno
));
157 err
= ct_tmpl_set_critical(ctfd
, crit
);
158 err
|= ct_pr_tmpl_set_fatal(ctfd
, fatal
);
159 err
|= ct_tmpl_set_informative(ctfd
, inf
);
160 err
|= ct_pr_tmpl_set_param(ctfd
, param
);
161 err
|= ct_tmpl_set_cookie(ctfd
, cookie
);
162 err
|= ct_pr_tmpl_set_svc_fmri(ctfd
, svc_fmri
);
163 err
|= ct_pr_tmpl_set_svc_aux(ctfd
, name
);
166 fork_sulogin(B_TRUE
, "Could not set %s process contract "
171 if (err
= ct_tmpl_activate(ctfd
)) {
173 fork_sulogin(B_TRUE
, "Could not activate %s process contract "
174 "template: %s\n", name
, strerror(err
));
181 * Attempt to fork "retries" times.
183 for (pid
= fork1(); pid
== -1; pid
= fork1()) {
184 if (++tries
> retries
) {
186 * When we exit the sulogin session, init(8)
187 * will restart svc.startd(8).
190 (void) ct_tmpl_clear(ctfd
);
193 fork_sulogin(B_TRUE
, "Could not fork to start %s: %s\n",
194 name
, strerror(err
));
203 * Clean up, return pid and ctid.
205 if (pid
!= 0 && (errno
= contract_latest(ctidp
)) != 0)
206 uu_die("Could not get new contract id for %s\n", name
);
207 (void) ct_tmpl_clear(ctfd
);
214 * void fork_sulogin(boolean_t, const char *, ...)
215 * When we are invoked with the -s flag from boot (or run into an unfixable
216 * situation), we run a private copy of sulogin. When the sulogin session
217 * is ended, we continue. This is the last fallback action for system
220 * If immediate is true, fork_sulogin() executes sulogin(8) directly, without
223 * Because fork_sulogin() is needed potentially before we daemonize, we leave
224 * it outside the wait_register() framework.
228 fork_sulogin(boolean_t immediate
, const char *format
, ...)
233 (void) printf("Requesting System Maintenance Mode\n");
235 if (!booting_to_single_user
)
236 (void) printf("(See /lib/svc/share/README for more "
239 va_start(args
, format
);
240 (void) vprintf(format
, args
);
247 pid
= fork_common("sulogin", SVC_SULOGIN_FMRI
,
248 MAX_SULOGIN_RETRIES
, &ctid
, CT_PR_EV_HWERR
, 0,
249 CT_PR_EV_HWERR
, CT_PR_PGRPONLY
, SULOGIN_COOKIE
);
252 (void) waitpid(pid
, NULL
, 0);
253 contract_abandon(ctid
);
256 /* close all inherited fds */
259 (void) printf("Directly executing sulogin.\n");
261 * Can't call closefrom() in this MT section
262 * so safely close a minimum set of fds.
264 (void) close(STDIN_FILENO
);
265 (void) close(STDOUT_FILENO
);
266 (void) close(STDERR_FILENO
);
271 /* open the console for sulogin */
272 if ((fd_console
= open("/dev/console", O_RDWR
)) >= 0) {
273 if (fd_console
!= STDIN_FILENO
)
274 while (dup2(fd_console
, STDIN_FILENO
) < 0 &&
277 if (fd_console
!= STDOUT_FILENO
)
278 while (dup2(fd_console
, STDOUT_FILENO
) < 0 &&
281 if (fd_console
!= STDERR_FILENO
)
282 while (dup2(fd_console
, STDERR_FILENO
) < 0 &&
285 if (fd_console
> STDERR_FILENO
)
286 (void) close(fd_console
);
290 while ((utmpp
= getutxent()) != NULL
) {
291 if (strcmp(utmpp
->ut_user
, "LOGIN") != 0) {
292 if (strcmp(utmpp
->ut_line
, "console") == 0) {
293 (void) kill(utmpp
->ut_pid
, 9);
299 (void) execl("/sbin/sulogin", "sulogin", NULL
);
301 uu_warn("Could not exec() sulogin");
306 #define CONFIGD_PATH "/lib/svc/bin/svc.configd"
309 * void fork_configd(int status)
310 * We are interested in exit events (since the parent's exiting means configd
311 * is ready to run and since the child's exiting indicates an error case) and
312 * in empty events. This means we have a unique template for initiating
316 fork_configd(int exitstatus
)
324 * Checking the existatus for the potential failure of the
325 * daemonized svc.configd. If this is not the first time
326 * through, but a call from the svc.configd monitoring thread
327 * after a failure this is the status that is expected. Other
328 * failures are exposed during initialization or are fixed
329 * by a restart (e.g door closings).
331 * If this is on-disk database corruption it will also be
332 * caught by a restart but could be cleared before the restart.
334 * Or this could be internal database corruption due to a
335 * rogue service that needs to be cleared before restart.
337 if (WEXITSTATUS(exitstatus
) == CONFIGD_EXIT_DATABASE_BAD
) {
338 fork_sulogin(B_FALSE
, "svc.configd exited with database "
339 "corrupt error after initialization of the repository\n");
343 log_framework(LOG_DEBUG
, "fork_configd trying to start svc.configd\n");
346 * If we're retrying, we will have an old contract lying around
347 * from the failure. Since we're going to be creating a new
348 * contract shortly, we abandon the old one now.
351 contract_abandon(ctid
);
354 pid
= fork_common("svc.configd", SCF_SERVICE_CONFIGD
,
355 MAX_CONFIGD_RETRIES
, &ctid
, 0, CT_PR_EV_EXIT
, 0,
356 CT_PR_INHERIT
| CT_PR_REGENT
, CONFIGD_COOKIE
);
361 st
->st_configd_pid
= pid
;
363 if (waitpid(pid
, &exitstatus
, 0) == -1) {
364 fork_sulogin(B_FALSE
, "waitpid on svc.configd "
365 "failed: %s\n", strerror(errno
));
366 } else if (WIFEXITED(exitstatus
)) {
370 * Examine exitstatus. This will eventually get more
371 * complicated, as we will want to teach startd how to
372 * invoke configd with alternate repositories, etc.
374 * Note that exec(2) failure results in an exit status
375 * of 1, resulting in the default clause below.
379 * Assign readable strings to cases we don't handle, or
380 * have error outcomes that cannot be eliminated.
382 switch (WEXITSTATUS(exitstatus
)) {
383 case CONFIGD_EXIT_BAD_ARGS
:
384 errstr
= "bad arguments";
387 case CONFIGD_EXIT_DATABASE_BAD
:
388 errstr
= "database corrupt";
391 case CONFIGD_EXIT_DATABASE_LOCKED
:
392 errstr
= "database locked";
394 case CONFIGD_EXIT_INIT_FAILED
:
395 errstr
= "initialization failure";
397 case CONFIGD_EXIT_DOOR_INIT_FAILED
:
398 errstr
= "door initialization failure";
400 case CONFIGD_EXIT_DATABASE_INIT_FAILED
:
401 errstr
= "database initialization failure";
403 case CONFIGD_EXIT_NO_THREADS
:
404 errstr
= "no threads available";
406 case CONFIGD_EXIT_LOST_MAIN_DOOR
:
407 errstr
= "lost door server attachment";
410 errstr
= "execution failure";
413 errstr
= "unknown error";
418 * Remedial actions for various configd failures.
420 switch (WEXITSTATUS(exitstatus
)) {
421 case CONFIGD_EXIT_OKAY
:
424 case CONFIGD_EXIT_DATABASE_LOCKED
:
425 /* attempt remount of / read-write */
426 if (fs_is_read_only("/", NULL
) == 1) {
427 if (fs_remount("/") == -1)
428 fork_sulogin(B_FALSE
,
430 "filesystem failed\n");
437 fork_sulogin(B_FALSE
, "svc.configd exited "
438 "with status %d (%s)\n",
439 WEXITSTATUS(exitstatus
), errstr
);
442 } else if (WIFSIGNALED(exitstatus
)) {
443 char signame
[SIG2STR_MAX
];
445 if (sig2str(WTERMSIG(exitstatus
), signame
))
446 (void) snprintf(signame
, SIG2STR_MAX
,
447 "signum %d", WTERMSIG(exitstatus
));
449 fork_sulogin(B_FALSE
, "svc.configd signalled:"
454 fork_sulogin(B_FALSE
, "svc.configd non-exit "
455 "condition: 0x%x\n", exitstatus
);
461 * Announce that we have a valid svc.configd status.
463 MUTEX_LOCK(&st
->st_configd_live_lock
);
464 st
->st_configd_lives
= 1;
465 err
= pthread_cond_broadcast(&st
->st_configd_live_cv
);
467 MUTEX_UNLOCK(&st
->st_configd_live_lock
);
469 log_framework(LOG_DEBUG
, "fork_configd broadcasts configd is "
475 * Set our per-process core file path to leave core files in
476 * /etc/svc/volatile directory, named after the PID to aid in debugging.
478 (void) snprintf(path
, sizeof (path
),
479 "/etc/svc/volatile/core.configd.%%p");
481 (void) core_set_process_path(path
, strlen(path
) + 1, getpid());
483 log_framework(LOG_DEBUG
, "executing svc.configd\n");
485 (void) execl(CONFIGD_PATH
, CONFIGD_PATH
, NULL
);
488 * Status code is used above to identify configd exec failure.
494 fork_configd_thread(void *vctid
)
497 ctid_t configd_ctid
= (ctid_t
)vctid
;
499 if (configd_ctid
== -1) {
500 log_framework(LOG_DEBUG
,
501 "fork_configd_thread starting svc.configd\n");
505 * configd_ctid is known: we broadcast and continue.
506 * test contract for appropriate state by verifying that
507 * there is one or more processes within it?
509 log_framework(LOG_DEBUG
,
510 "fork_configd_thread accepting svc.configd with CTID %ld\n",
512 MUTEX_LOCK(&st
->st_configd_live_lock
);
513 st
->st_configd_lives
= 1;
514 (void) pthread_cond_broadcast(&st
->st_configd_live_cv
);
515 MUTEX_UNLOCK(&st
->st_configd_live_lock
);
518 fd
= open(CTFS_ROOT
"/process/pbundle", O_RDONLY
);
520 uu_die("process bundle open failed");
523 * Make sure we get all events (including those generated by configd
524 * before this thread was started).
526 err
= ct_event_reset(fd
);
539 if (err
= ct_event_read_critical(fd
, &ev
)) {
540 assert(err
!= EINVAL
&& err
!= EAGAIN
);
541 log_error(LOG_WARNING
,
542 "Error reading next contract event: %s",
547 evid
= ct_event_get_evid(ev
);
548 ctid
= ct_event_get_ctid(ev
);
549 type
= ct_event_get_type(ev
);
552 sfd
= contract_open(ctid
, "process", "status", O_RDONLY
);
558 if (err
= ct_status_read(sfd
, CTD_COMMON
, &status
)) {
559 log_framework(LOG_WARNING
, "Could not get status for "
560 "contract %ld: %s\n", ctid
, strerror(err
));
567 cookie
= ct_status_get_cookie(status
);
569 ct_status_free(status
);
574 * Don't process events from contracts we aren't interested in.
576 if (cookie
!= CONFIGD_COOKIE
) {
581 if (type
== CT_PR_EV_EXIT
) {
584 (void) ct_pr_event_get_pid(ev
, &pid
);
585 (void) ct_pr_event_get_exitstatus(ev
,
588 if (st
->st_configd_pid
!= pid
) {
590 * This is the child exiting, so we
591 * abandon the contract and restart
594 contract_abandon(ctid
);
595 fork_configd(exitstatus
);
599 efd
= contract_open(ctid
, "process", "ctl", O_WRONLY
);
601 (void) ct_ctl_ack(efd
, evid
);
614 fork_rc_script(char rl
, const char *arg
, boolean_t wait
)
618 char path
[20] = "/sbin/rc.", log
[20] = "rc..log", timebuf
[20];
627 tmpl
= open(CTFS_ROOT
"/process/template", O_RDWR
);
629 err
= ct_tmpl_set_critical(tmpl
, 0);
632 err
= ct_tmpl_set_informative(tmpl
, 0);
635 err
= ct_pr_tmpl_set_fatal(tmpl
, 0);
638 err
= ct_tmpl_activate(tmpl
);
644 uu_warn("Could not create contract template for %s.\n", path
);
647 pid
= startd_fork1(NULL
);
650 } else if (pid
!= 0) {
654 err
= waitpid(pid
, &stat
, 0);
655 while (err
!= 0 && errno
== EINTR
)
658 if (!WIFEXITED(stat
)) {
659 log_framework(LOG_INFO
,
660 "%s terminated with waitpid() status %d.\n",
662 } else if (WEXITSTATUS(stat
) != 0) {
663 log_framework(LOG_INFO
,
664 "%s failed with status %d.\n", path
,
679 sz
= strftime(timebuf
, sizeof (timebuf
), "%b %e %T",
680 localtime_r(&now
, <ime
));
683 (void) fprintf(stderr
, "%s Executing %s %s\n", timebuf
, path
, arg
);
686 pathenv
= "PATH=/sbin:/usr/sbin:/usr/bin";
688 pathenv
= "PATH=/usr/sbin:/usr/bin";
690 nenv
= set_smf_env(NULL
, 0, pathenv
, NULL
, NULL
);
692 (void) execle(path
, path
, arg
, 0, nenv
);
698 #define SVCCFG_PATH "/usr/sbin/svccfg"
699 #define EMI_MFST "/lib/svc/manifest/system/early-manifest-import.xml"
700 #define EMI_PATH "/lib/svc/method/manifest-import"
703 * Set Early Manifest Import service's state and log file.
706 emi_set_state(restarter_instance_state_t state
, boolean_t setlog
)
709 instance_data_t idata
;
710 scf_handle_t
*hndl
= NULL
;
711 scf_instance_t
*inst
= NULL
;
715 hndl
= libscf_handle_create_bound(SCF_VERSION
);
719 * In the case that we can't bind to the repository
720 * (which should have been started), we need to allow
721 * the user into maintenance mode to determine what's
724 fork_sulogin(B_FALSE
, "Unable to bind a new repository"
725 " handle: %s\n", scf_strerror(scf_error()));
730 inst
= safe_scf_instance_create(hndl
);
732 if (scf_handle_decode_fmri(hndl
, SCF_INSTANCE_EMI
, NULL
, NULL
,
733 inst
, NULL
, NULL
, SCF_DECODE_FMRI_EXACT
) == -1) {
734 switch (scf_error()) {
735 case SCF_ERROR_NOT_FOUND
:
738 case SCF_ERROR_CONNECTION_BROKEN
:
739 case SCF_ERROR_NOT_BOUND
:
740 libscf_handle_rebind(hndl
);
744 fork_sulogin(B_FALSE
, "Couldn't fetch %s service: "
745 "%s\n", SCF_INSTANCE_EMI
,
746 scf_strerror(scf_error()));
752 (void) libscf_note_method_log(inst
, st
->st_log_prefix
, EMI_LOG
);
753 log_framework(LOG_DEBUG
,
754 "Set logfile property for %s\n", SCF_INSTANCE_EMI
);
757 idata
.i_fmri
= SCF_INSTANCE_EMI
;
758 idata
.i_state
= RESTARTER_STATE_NONE
;
759 idata
.i_next_state
= RESTARTER_STATE_NONE
;
760 switch (r
= _restarter_commit_states(hndl
, &idata
, state
,
761 RESTARTER_STATE_NONE
, NULL
)) {
766 libscf_handle_rebind(hndl
);
774 fork_sulogin(B_FALSE
, "Could not set state of "
775 "%s: %s\n", SCF_INSTANCE_EMI
, strerror(r
));
780 bad_error("_restarter_commit_states", r
);
785 scf_instance_destroy(inst
);
786 scf_handle_destroy(hndl
);
791 * It is possible that the early-manifest-import service is disabled. This
792 * would not be the normal case for Solaris, but it may happen on dedicated
793 * systems. So this function checks the state of the general/enabled
794 * property for Early Manifest Import.
796 * It is also possible that the early-manifest-import service does not yet
797 * have a repository representation when this function runs. This happens
798 * if non-Early Manifest Import system is upgraded to an Early Manifest
799 * Import based system. Thus, the non-existence of general/enabled is not
802 * Returns 1 if Early Manifest Import is disabled and 0 otherwise.
808 int disconnected
= 1;
810 scf_handle_t
*hndl
= NULL
;
811 scf_instance_t
*inst
= NULL
;
812 uchar_t stored_hash
[MHASH_SIZE
];
816 while (hndl
== NULL
) {
817 hndl
= libscf_handle_create_bound(SCF_VERSION
);
821 * In the case that we can't bind to the repository
822 * (which should have been started), we need to
823 * allow the user into maintenance mode to
824 * determine what's failed.
826 fork_sulogin(B_FALSE
, "Unable to bind a new repository "
827 "handle: %s\n", scf_strerror(scf_error()));
831 while (disconnected
) {
832 r
= libscf_fmri_get_instance(hndl
, SCF_INSTANCE_EMI
, &inst
);
836 libscf_handle_rebind(hndl
);
841 * Early Manifest Import service is not in
842 * the repository. Check the manifest file
843 * and service's hash in smf/manifest to
844 * figure out whether Early Manifest Import
845 * service was deleted. If Early Manifest Import
846 * service was deleted, treat that as a disable
847 * and don't run early import.
850 if (access(EMI_MFST
, F_OK
)) {
852 * Manifest isn't found, so service is
858 * If manifest exists and we have the
859 * hash, the service was improperly
860 * deleted, generate a warning and treat
864 if ((pname
= mhash_filename_to_propname(
865 EMI_MFST
, B_TRUE
)) == NULL
) {
867 * Treat failure to get propname
871 uu_warn("Failed to get propname"
875 hashash
= mhash_retrieve_entry(
883 uu_warn("%s service is "
895 bad_error("libscf_fmri_get_instance",
899 r
= libscf_get_basic_instance_data(hndl
, inst
, SCF_INSTANCE_EMI
,
900 &enabled
, NULL
, NULL
);
903 * enabled can be returned as -1, which indicates
904 * that the enabled property was not found. To us
905 * that means that the service was not disabled.
912 libscf_handle_rebind(hndl
);
919 bad_error("libscf_get_basic_instance_data", r
);
927 scf_instance_destroy(inst
);
928 scf_handle_destroy(hndl
);
939 char corepath
[PATH_MAX
];
944 if (emi_is_disabled()) {
945 log_framework(LOG_NOTICE
, "%s is disabled and will "
946 "not be run.\n", SCF_INSTANCE_EMI
);
951 * Early Manifest Import should run only once, at boot. If svc.startd
952 * is some how restarted, Early Manifest Import should not run again.
953 * Use the Early Manifest Import service's state to figure out whether
954 * Early Manifest Import has successfully completed earlier and bail
957 if (svc_state
= smf_get_state(SCF_INSTANCE_EMI
)) {
958 if (strcmp(svc_state
, SCF_STATE_STRING_ONLINE
) == 0) {
966 * Attempt to set Early Manifest Import service's state and log file.
967 * If emi_set_state fails, set log file again in the next call to
970 setemilog
= emi_set_state(RESTARTER_STATE_OFFLINE
, B_TRUE
);
972 /* Don't go further if /usr isn't available */
973 if (access(SVCCFG_PATH
, F_OK
)) {
974 log_framework(LOG_NOTICE
, "Early Manifest Import is not "
975 "supported on systems with a separate /usr filesystem.\n");
980 log_framework(LOG_DEBUG
, "Starting Early Manifest Import\n");
983 * If we're retrying, we will have an old contract lying around
984 * from the failure. Since we're going to be creating a new
985 * contract shortly, we abandon the old one now.
988 contract_abandon(ctid
);
991 pid
= fork_common(SCF_INSTANCE_EMI
, SCF_INSTANCE_EMI
,
992 MAX_EMI_RETRIES
, &ctid
, 0, 0, 0, 0, EMI_COOKIE
);
997 if (waitpid(pid
, &exitstatus
, 0) == -1) {
998 fork_sulogin(B_FALSE
, "waitpid on %s failed: "
999 "%s\n", SCF_INSTANCE_EMI
, strerror(errno
));
1000 } else if (WIFEXITED(exitstatus
)) {
1001 if (WEXITSTATUS(exitstatus
)) {
1002 fork_sulogin(B_FALSE
, "%s exited with status "
1003 "%d \n", SCF_INSTANCE_EMI
,
1004 WEXITSTATUS(exitstatus
));
1007 } else if (WIFSIGNALED(exitstatus
)) {
1008 char signame
[SIG2STR_MAX
];
1010 if (sig2str(WTERMSIG(exitstatus
), signame
))
1011 (void) snprintf(signame
, SIG2STR_MAX
,
1012 "signum %d", WTERMSIG(exitstatus
));
1014 fork_sulogin(B_FALSE
, "%s signalled: %s\n",
1015 SCF_INSTANCE_EMI
, signame
);
1018 fork_sulogin(B_FALSE
, "%s non-exit condition: 0x%x\n",
1019 SCF_INSTANCE_EMI
, exitstatus
);
1023 log_framework(LOG_DEBUG
, "%s completed successfully\n",
1027 * Once Early Manifest Import completed, the Early Manifest
1028 * Import service must have been imported so set log file and
1029 * state properties. Since this information is required for
1030 * late manifest import and common admin operations, failing to
1031 * set these properties should result in su login so admin can
1032 * correct the problem.
1034 (void) emi_set_state(RESTARTER_STATE_ONLINE
,
1035 setemilog
? B_TRUE
: B_FALSE
);
1043 * Set our per-process core file path to leave core files in
1044 * /etc/svc/volatile directory, named after the PID to aid in debugging.
1046 (void) snprintf(corepath
, sizeof (corepath
),
1047 "/etc/svc/volatile/core.emi.%%p");
1048 (void) core_set_process_path(corepath
, strlen(corepath
) + 1, getpid());
1051 * Similar to running legacy services, we need to manually set
1052 * log files here and environment variables.
1056 envp
= startd_zalloc(sizeof (char *) * 3);
1059 sz
= sizeof ("SMF_FMRI=") + strlen(SCF_INSTANCE_EMI
);
1060 *np
= startd_zalloc(sz
);
1061 (void) strlcpy(*np
, "SMF_FMRI=", sz
);
1062 (void) strncat(*np
, SCF_INSTANCE_EMI
, sz
);
1065 emipath
= getenv("PATH");
1066 if (emipath
== NULL
)
1067 emipath
= strdup("/usr/sbin:/usr/bin");
1069 sz
= sizeof ("PATH=") + strlen(emipath
);
1070 *np
= startd_zalloc(sz
);
1071 (void) strlcpy(*np
, "PATH=", sz
);
1072 (void) strncat(*np
, emipath
, sz
);
1074 log_framework(LOG_DEBUG
, "executing Early Manifest Import\n");
1075 (void) execle(EMI_PATH
, EMI_PATH
, NULL
, envp
);
1078 * Status code is used above to identify Early Manifest Import
1084 extern char **environ
;
1087 * A local variation on system(3c) which accepts a timeout argument. This
1088 * allows us to better ensure that the system will actually shut down.
1090 * gracetime specifies an amount of time in seconds which the routine must wait
1091 * after the command exits, to allow for asynchronous effects (like sent
1092 * signals) to take effect. This can be zero.
1095 fork_with_timeout(const char *cmd
, uint_t gracetime
, uint_t timeout
)
1100 posix_spawnattr_t attr
;
1101 posix_spawn_file_actions_t factions
;
1103 sigset_t mask
, savemask
;
1104 uint_t msec_timeout
;
1105 uint_t msec_spent
= 0;
1106 uint_t msec_gracetime
;
1109 msec_timeout
= timeout
* 1000;
1110 msec_gracetime
= gracetime
* 1000;
1113 * See also system(3c) in libc. This is very similar, except
1114 * that we avoid some unneeded complexity.
1116 err
= posix_spawnattr_init(&attr
);
1118 err
= posix_spawnattr_setflags(&attr
,
1119 POSIX_SPAWN_SETSIGMASK
| POSIX_SPAWN_SETSIGDEF
|
1120 POSIX_SPAWN_NOSIGCHLD_NP
| POSIX_SPAWN_WAITPID_NP
|
1121 POSIX_SPAWN_NOEXECERR_NP
);
1124 * We choose to close fd's above 2, a deviation from system.
1127 err
= posix_spawn_file_actions_init(&factions
);
1129 err
= posix_spawn_file_actions_addclosefrom_np(&factions
,
1132 (void) sigemptyset(&mask
);
1133 (void) sigaddset(&mask
, SIGCHLD
);
1134 (void) thr_sigsetmask(SIG_BLOCK
, &mask
, &savemask
);
1136 argv
[0] = "/bin/sh";
1138 argv
[2] = (char *)cmd
;
1142 err
= posix_spawn(&pid
, "/bin/sh", &factions
, &attr
,
1143 (char *const *)argv
, (char *const *)environ
);
1145 (void) posix_spawnattr_destroy(&attr
);
1146 (void) posix_spawn_file_actions_destroy(&factions
);
1149 uu_warn("Failed to spawn %s: %s\n", cmd
, strerror(err
));
1153 w
= waitpid(pid
, &status
, WNOHANG
);
1154 if (w
== -1 && errno
!= EINTR
)
1158 * Command succeeded, so give it gracetime
1159 * seconds for it to have an effect.
1161 if (status
== 0 && msec_gracetime
!= 0)
1162 (void) poll(NULL
, 0, msec_gracetime
);
1166 (void) poll(NULL
, 0, 100);
1169 * If we timed out, kill off the process, then try to
1170 * wait for it-- it's possible that we could accumulate
1171 * a zombie here since we don't allow waitpid to hang,
1172 * but it's better to let that happen and continue to
1175 if (msec_spent
>= msec_timeout
) {
1176 uu_warn("'%s' timed out after %d "
1177 "seconds. Killing.\n", cmd
,
1179 (void) kill(pid
, SIGTERM
);
1180 (void) poll(NULL
, 0, 100);
1181 (void) kill(pid
, SIGKILL
);
1182 (void) poll(NULL
, 0, 100);
1183 (void) waitpid(pid
, &status
, WNOHANG
);
1188 (void) thr_sigsetmask(SIG_BLOCK
, &savemask
, NULL
);