diff options
Diffstat (limited to 'usr/src/cmd')
-rw-r--r-- | usr/src/cmd/boot/scripts/boot-archive-update.xml | 8 | ||||
-rw-r--r-- | usr/src/cmd/cmd-inet/etc/init.d/pppd | 15 | ||||
-rw-r--r-- | usr/src/cmd/initpkg/rc0.sh | 11 | ||||
-rw-r--r-- | usr/src/cmd/initpkg/shutdown.sh | 10 | ||||
-rw-r--r-- | usr/src/cmd/initpkg/umountall.sh | 10 | ||||
-rw-r--r-- | usr/src/cmd/svc/shell/smf_include.sh | 33 | ||||
-rw-r--r-- | usr/src/cmd/svc/startd/fork.c | 111 | ||||
-rw-r--r-- | usr/src/cmd/svc/startd/graph.c | 157 | ||||
-rw-r--r-- | usr/src/cmd/svc/startd/method.c | 16 | ||||
-rw-r--r-- | usr/src/cmd/svc/startd/startd.h | 1 |
10 files changed, 286 insertions, 86 deletions
diff --git a/usr/src/cmd/boot/scripts/boot-archive-update.xml b/usr/src/cmd/boot/scripts/boot-archive-update.xml index 670a704d94..af20f2b748 100644 --- a/usr/src/cmd/boot/scripts/boot-archive-update.xml +++ b/usr/src/cmd/boot/scripts/boot-archive-update.xml @@ -1,7 +1,7 @@ <?xml version="1.0"?> <!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> <!-- - Copyright 2006 Sun Microsystems, Inc. All rights reserved. + Copyright 2009 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. CDDL HEADER START @@ -23,8 +23,6 @@ CDDL HEADER END - ident "%Z%%M% %I% %E% SMI" - NOTE: This service manifest is not editable; its contents will be overwritten by package or patch operations, including operating system upgrade. Make customizations in a different @@ -60,6 +58,10 @@ when there are many glommed kernels to wad up, a longer timeout is needed. However if it doesn't finish eventually something up, and an error message to trigger further investigation is appropriate. + + Please note that a similar timeout exists in startd, which again + runs bootadm at the end of shutdown. These timeouts should be + adjusted in sync with each other. --> <exec_method type='method' diff --git a/usr/src/cmd/cmd-inet/etc/init.d/pppd b/usr/src/cmd/cmd-inet/etc/init.d/pppd index cdb57e62f5..e820e6b15e 100644 --- a/usr/src/cmd/cmd-inet/etc/init.d/pppd +++ b/usr/src/cmd/cmd-inet/etc/init.d/pppd @@ -3,9 +3,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -21,10 +20,9 @@ # CDDL HEADER END # # -# Copyright (c) 2000-2001 by Sun Microsystems, Inc. -# All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" PATH=/sbin:/usr/bin:/usr/sbin; export PATH PPPDIR=/etc/ppp; export PPPDIR @@ -58,9 +56,8 @@ case "$1" in ;; 'stop') - /usr/bin/pkill -x pppd - sleep 1 - /usr/bin/pkill -x pppoed + /usr/bin/pkill -z `/sbin/zonename` -x pppd && sleep 1 + /usr/bin/pkill -z `/sbin/zonename` -x pppoed # Use ifconfig to make the interfaces down just in case if [ -f $PPPDIR/ifconfig ]; then diff --git a/usr/src/cmd/initpkg/rc0.sh b/usr/src/cmd/initpkg/rc0.sh index 741be2290d..8b1018ad57 100644 --- a/usr/src/cmd/initpkg/rc0.sh +++ b/usr/src/cmd/initpkg/rc0.sh @@ -3,9 +3,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -25,10 +24,9 @@ # All rights reserved. # # -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" # "Run Commands" for init states 0, 5 and 6. @@ -86,6 +84,3 @@ fi [ -f /etc/.dynamic_routing ] && /usr/bin/rm -f /etc/.dynamic_routing trap "" 15 - -[ -x /usr/lib/acct/closewtmp ] && /usr/lib/acct/closewtmp -/sbin/sync; /sbin/sync; /sbin/sync diff --git a/usr/src/cmd/initpkg/shutdown.sh b/usr/src/cmd/initpkg/shutdown.sh index 187e8d679d..e65224c632 100644 --- a/usr/src/cmd/initpkg/shutdown.sh +++ b/usr/src/cmd/initpkg/shutdown.sh @@ -3,9 +3,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ # # CDDL HEADER END # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T @@ -28,8 +27,6 @@ -#ident "%Z%%M% %I% %E% SMI" - # Sequence performed to change the init state of a machine. Only allows # transitions to states 0,1,5,6,s,S (i.e.: down or administrative states). @@ -139,7 +136,6 @@ echo NODENAME=`uname -n` -/sbin/sync cd / trap "rm $nologin >/dev/null 2>&1 ;exit 1" 1 2 15 diff --git a/usr/src/cmd/initpkg/umountall.sh b/usr/src/cmd/initpkg/umountall.sh index 25790c2804..e589feebb9 100644 --- a/usr/src/cmd/initpkg/umountall.sh +++ b/usr/src/cmd/initpkg/umountall.sh @@ -20,7 +20,7 @@ # CDDL HEADER END # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T @@ -169,14 +169,6 @@ fi ZONENAME=`zonename` -# Check and if needed sync the boot archive before unmounting everything. -# -if [ -z "${RFLAG}${NFLAG}${HFLAG}${FSType}" -a "$ZONENAME" = "global" \ - -a -x /sbin/bootadm ] ; then - /sbin/bootadm -a update_all -fi - - # # If we are in deferred activation patching, and the caller is # svc.startd, then exit without unmounting any of the remaining diff --git a/usr/src/cmd/svc/shell/smf_include.sh b/usr/src/cmd/svc/shell/smf_include.sh index 28aa22d701..0253a61ec5 100644 --- a/usr/src/cmd/svc/shell/smf_include.sh +++ b/usr/src/cmd/svc/shell/smf_include.sh @@ -20,7 +20,7 @@ # CDDL HEADER END # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # @@ -160,6 +160,11 @@ smf_netstrategy () { # every second until the contract is empty. This will catch # races between fork(2) and pkill(1). # +# Note that time in this routine is tracked (after being input +# via TIMEOUT) in 10ths of a second. This is because we want +# to sleep for short periods of time, and expr(1) is too dumb +# to do non-integer math. +# # Returns 1 if the contract is invalid. # Returns 2 if WAIT is "1", TIMEOUT is > 0, and TIMEOUT expires. # Returns 0 on success. @@ -171,6 +176,9 @@ smf_kill_contract() { [ -z "$time_to_wait" ] && time_to_wait=0 + # convert to 10ths. + time_to_wait=`expr $time_to_wait * 10` + # Verify contract id is valid using pgrep /usr/bin/pgrep -c $1 > /dev/null 2>&1 ret=$? @@ -195,17 +203,26 @@ smf_kill_contract() { # If contract does not empty, keep killing the contract to catch # any child processes missed because they were forking - /usr/bin/sleep 5 /usr/bin/pgrep -c $1 > /dev/null 2>&1 while [ $? -eq 0 ] ; do - - time_waited=`/usr/bin/expr $time_waited + 5` - - # Return if TIMEOUT was passed, and it has expired + # Return 2 if TIMEOUT was passed, and it has expired [ "$time_to_wait" -gt 0 -a $time_waited -ge $time_to_wait ] && \ return 2 - /usr/bin/pkill -$2 -c $1 - /usr/bin/sleep 5 + + # + # At five second intervals, issue the kill again. Note that + # the sleep time constant (in tenths) must be a factor of 50 + # for the remainder trick to work. i.e. sleeping 2 tenths is + # fine, but 27 tenths is not. + # + remainder=`/usr/bin/expr $time_waited % 50` + if [ $time_waited -gt 0 -a $remainder -eq 0 ]; then + /usr/bin/pkill -$2 -c $1 + fi + + # Wait two tenths, and go again. + /usr/bin/sleep 0.2 + time_waited=`/usr/bin/expr $time_waited + 2` /usr/bin/pgrep -c $1 > /dev/null 2>&1 done diff --git a/usr/src/cmd/svc/startd/fork.c b/usr/src/cmd/svc/startd/fork.c index 5acbe3f322..f53fce964b 100644 --- a/usr/src/cmd/svc/startd/fork.c +++ b/usr/src/cmd/svc/startd/fork.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -44,6 +44,7 @@ #include <libcontract_priv.h> #include <libscf_priv.h> #include <limits.h> +#include <poll.h> #include <port.h> #include <signal.h> #include <stdarg.h> @@ -52,6 +53,7 @@ #include <string.h> #include <unistd.h> #include <utmpx.h> +#include <spawn.h> #include "configd_exit.h" #include "protocol.h" @@ -679,3 +681,110 @@ fork_rc_script(char rl, const char *arg, boolean_t wait) perror("exec"); exit(0); } + +extern char **environ; + +/* + * A local variation on system(3c) which accepts a timeout argument. This + * allows us to better ensure that the system will actually shut down. + * + * gracetime specifies an amount of time in seconds which the routine must wait + * after the command exits, to allow for asynchronous effects (like sent + * signals) to take effect. This can be zero. + */ +void +fork_with_timeout(const char *cmd, uint_t gracetime, uint_t timeout) +{ + int err = 0; + pid_t pid; + char *argv[4]; + posix_spawnattr_t attr; + posix_spawn_file_actions_t factions; + + sigset_t mask, savemask; + uint_t msec_timeout; + uint_t msec_spent = 0; + uint_t msec_gracetime; + int status; + + msec_timeout = timeout * 1000; + msec_gracetime = gracetime * 1000; + + /* + * See also system(3c) in libc. This is very similar, except + * that we avoid some unneeded complexity. + */ + err = posix_spawnattr_init(&attr); + if (err == 0) + err = posix_spawnattr_setflags(&attr, + POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF | + POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP | + POSIX_SPAWN_NOEXECERR_NP); + + /* + * We choose to close fd's above 2, a deviation from system. + */ + if (err == 0) + err = posix_spawn_file_actions_init(&factions); + if (err == 0) + err = posix_spawn_file_actions_addclosefrom_np(&factions, + STDERR_FILENO + 1); + + (void) sigemptyset(&mask); + (void) sigaddset(&mask, SIGCHLD); + (void) thr_sigsetmask(SIG_BLOCK, &mask, &savemask); + + argv[0] = "/bin/sh"; + argv[1] = "-c"; + argv[2] = (char *)cmd; + argv[3] = NULL; + + if (err == 0) + err = posix_spawn(&pid, "/bin/sh", &factions, &attr, + (char *const *)argv, (char *const *)environ); + + (void) posix_spawnattr_destroy(&attr); + (void) posix_spawn_file_actions_destroy(&factions); + + if (err) { + uu_warn("Failed to spawn %s: %s\n", cmd, strerror(err)); + } else { + for (;;) { + int w; + w = waitpid(pid, &status, WNOHANG); + if (w == -1 && errno != EINTR) + break; + if (w > 0) { + /* + * Command succeeded, so give it gracetime + * seconds for it to have an effect. + */ + if (status == 0 && msec_gracetime != 0) + (void) poll(NULL, 0, msec_gracetime); + break; + } + + (void) poll(NULL, 0, 100); + msec_spent += 100; + /* + * If we timed out, kill off the process, then try to + * wait for it-- it's possible that we could accumulate + * a zombie here since we don't allow waitpid to hang, + * but it's better to let that happen and continue to + * make progress. + */ + if (msec_spent >= msec_timeout) { + uu_warn("'%s' timed out after %d " + "seconds. Killing.\n", cmd, + timeout); + (void) kill(pid, SIGTERM); + (void) poll(NULL, 0, 100); + (void) kill(pid, SIGKILL); + (void) poll(NULL, 0, 100); + (void) waitpid(pid, &status, WNOHANG); + break; + } + } + } + (void) thr_sigsetmask(SIG_BLOCK, &savemask, NULL); +} diff --git a/usr/src/cmd/svc/startd/graph.c b/usr/src/cmd/svc/startd/graph.c index fe17f6c4fc..455b14ab0c 100644 --- a/usr/src/cmd/svc/startd/graph.c +++ b/usr/src/cmd/svc/startd/graph.c @@ -178,6 +178,11 @@ static boolean_t go_single_user_mode = B_FALSE; static boolean_t go_to_level1 = B_FALSE; /* + * Tracks when we started halting. + */ +static time_t halting_time = 0; + +/* * This tracks the legacy runlevel to ensure we signal init and manage * utmpx entries correctly. */ @@ -3441,11 +3446,44 @@ out: return (0); } + +static void +kill_user_procs(void) +{ + (void) fputs("svc.startd: Killing user processes.\n", stdout); + + /* + * Despite its name, killall's role is to get select user processes-- + * basically those representing terminal-based logins-- to die. Victims + * are located by killall in the utmp database. Since these are most + * often shell based logins, and many shells mask SIGTERM (but are + * responsive to SIGHUP) we first HUP and then shortly thereafter + * kill -9. + */ + (void) fork_with_timeout("/usr/sbin/killall HUP", 1, 5); + (void) fork_with_timeout("/usr/sbin/killall KILL", 1, 5); + + /* + * Note the selection of user id's 0, 1 and 15, subsequently + * inverted by -v. 15 is reserved for dladmd. Yes, this is a + * kludge-- a better policy is needed. + * + * Note that fork_with_timeout will only wait out the 1 second + * "grace time" if pkill actually returns 0. So if there are + * no matches, this will run to completion much more quickly. + */ + (void) fork_with_timeout("/usr/bin/pkill -TERM -v -u 0,1,15", 1, 5); + (void) fork_with_timeout("/usr/bin/pkill -KILL -v -u 0,1,15", 1, 5); +} + static void do_uadmin(void) { - int fd, left; + int fd; struct statvfs vfs; + time_t now; + struct tm nowtm; + char down_buf[256], time_buf[256]; const char * const resetting = "/etc/svc/volatile/resetting"; @@ -3458,30 +3496,87 @@ do_uadmin(void) /* Kill dhcpagent if we're not using nfs for root */ if ((statvfs("/", &vfs) == 0) && (strncmp(vfs.f_basetype, "nfs", sizeof ("nfs") - 1) != 0)) - (void) system("/usr/bin/pkill -x -u 0 dhcpagent"); + fork_with_timeout("/usr/bin/pkill -x -u 0 dhcpagent", 0, 5); - (void) system("/usr/sbin/killall"); - left = 5; - while (left > 0) - left = sleep(left); + /* + * Call sync(2) now, before we kill off user processes. This takes + * advantage of the several seconds of pause we have before the + * killalls are done. Time we can make good use of to get pages + * moving out to disk. + * + * Inside non-global zones, we don't bother, and it's better not to + * anyway, since sync(2) can have system-wide impact. + */ + if (getzoneid() == 0) + sync(); - (void) system("/usr/sbin/killall 9"); - left = 10; - while (left > 0) - left = sleep(left); + kill_user_procs(); + + /* + * Note that this must come after the killing of user procs, since + * killall relies on utmpx, and this command affects the contents of + * said file. + */ + if (access("/usr/lib/acct/closewtmp", X_OK) == 0) + fork_with_timeout("/usr/lib/acct/closewtmp", 0, 5); - sync(); - sync(); - sync(); + /* + * For patches which may be installed as the system is shutting + * down, we need to ensure, one more time, that the boot archive + * really is up to date. + */ + if (getzoneid() == 0 && access("/usr/sbin/bootadm", X_OK) == 0) + fork_with_timeout("/usr/sbin/bootadm -ea update_all", 0, 3600); - (void) system("/sbin/umountall -l"); - (void) system("/sbin/umount /tmp >/dev/null 2>&1"); - (void) system("/sbin/umount /var/adm >/dev/null 2>&1"); - (void) system("/sbin/umount /var/run >/dev/null 2>&1"); - (void) system("/sbin/umount /var >/dev/null 2>&1"); - (void) system("/sbin/umount /usr >/dev/null 2>&1"); + fork_with_timeout("/sbin/umountall -l", 0, 5); + fork_with_timeout("/sbin/umount /tmp /var/adm /var/run /var " + ">/dev/null 2>&1", 0, 5); + + /* + * Try to get to consistency for whatever UFS filesystems are left. + * This is pretty expensive, so we save it for the end in the hopes of + * minimizing what it must do. The other option would be to start in + * parallel with the killall's, but lockfs tends to throw out much more + * than is needed, and so subsequent commands (like umountall) take a + * long time to get going again. + * + * Inside of zones, we don't bother, since we're not about to terminate + * the whole OS instance. + * + * On systems using only ZFS, this call to lockfs -fa is a no-op. + */ + if (getzoneid() == 0) { + if (access("/usr/sbin/lockfs", X_OK) == 0) + fork_with_timeout("/usr/sbin/lockfs -fa", 0, 30); - uu_warn("The system is down.\n"); + sync(); /* once more, with feeling */ + } + + fork_with_timeout("/sbin/umount /usr >/dev/null 2>&1", 0, 5); + + /* + * Construct and emit the last words from userland: + * "<timestamp> The system is down. Shutdown took <N> seconds." + * + * Normally we'd use syslog, but with /var and other things + * potentially gone, try to minimize the external dependencies. + */ + now = time(NULL); + (void) localtime_r(&now, &nowtm); + + if (strftime(down_buf, sizeof (down_buf), + "%b %e %T The system is down.", &nowtm) == 0) { + (void) strlcpy(down_buf, "The system is down.", + sizeof (down_buf)); + } + + if (halting_time != 0 && halting_time <= now) { + (void) snprintf(time_buf, sizeof (time_buf), + " Shutdown took %lu seconds.", now - halting_time); + } else { + time_buf[0] = '\0'; + } + (void) printf("%s%s\n", down_buf, time_buf); (void) uadmin(A_SHUTDOWN, halting, NULL); uu_warn("uadmin() failed"); @@ -3681,23 +3776,8 @@ single_user_thread(void *unused) MUTEX_LOCK(&single_user_thread_lock); single_user_thread_count++; - if (!booting_to_single_user) { - /* - * From rcS.sh: Look for ttymon, in.telnetd, in.rlogind and - * processes in their process groups so they can be terminated. - */ - (void) fputs("svc.startd: Killing user processes: ", stdout); - (void) system("/usr/sbin/killall"); - (void) system("/usr/sbin/killall 9"); - (void) system("/usr/bin/pkill -TERM -v -u 0,1"); - - left = 5; - while (left > 0) - left = sleep(left); - - (void) system("/usr/bin/pkill -KILL -v -u 0,1"); - (void) puts("done."); - } + if (!booting_to_single_user) + kill_user_procs(); if (go_single_user_mode || booting_to_single_user) { msg = "SINGLE USER MODE\n"; @@ -4972,16 +5052,19 @@ dgraph_set_runlevel(scf_propertygroup_t *pg, scf_property_t *prop) break; case '0': + halting_time = time(NULL); fork_rc_script(rl, stop, B_TRUE); halting = AD_HALT; goto uadmin; case '5': + halting_time = time(NULL); fork_rc_script(rl, stop, B_TRUE); halting = AD_POWEROFF; goto uadmin; case '6': + halting_time = time(NULL); fork_rc_script(rl, stop, B_TRUE); halting = AD_BOOT; goto uadmin; diff --git a/usr/src/cmd/svc/startd/method.c b/usr/src/cmd/svc/startd/method.c index 3b83fdc418..ed538c3c3a 100644 --- a/usr/src/cmd/svc/startd/method.c +++ b/usr/src/cmd/svc/startd/method.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * method.c - method execution functions * @@ -955,13 +953,23 @@ assured_kill: */ if (type == METHOD_STOP && (!instance_is_transient_style(inst)) && !(contract_is_empty(inst->ri_i.i_primary_ctid))) { + int times = 0; if (timeout != METHOD_TIMEOUT_INFINITE) timeout_insert(inst, inst->ri_i.i_primary_ctid, timeout); for (;;) { - (void) poll(NULL, 0, 100); + /* + * Check frequently at first, then back off. This + * keeps startd from idling while shutting down. + */ + if (times < 20) { + (void) poll(NULL, 0, 5); + times++; + } else { + (void) poll(NULL, 0, 100); + } if (contract_is_empty(inst->ri_i.i_primary_ctid)) break; } diff --git a/usr/src/cmd/svc/startd/startd.h b/usr/src/cmd/svc/startd/startd.h index 8cc61dd830..3bf401723a 100644 --- a/usr/src/cmd/svc/startd/startd.h +++ b/usr/src/cmd/svc/startd/startd.h @@ -588,6 +588,7 @@ void fork_rc_script(char, const char *, boolean_t); void *fork_configd_thread(void *); pid_t startd_fork1(int *); +void fork_with_timeout(const char *, uint_t, uint_t); /* graph.c */ void graph_init(void); |