]> git.saurik.com Git - apple/xnu.git/blame_incremental - bsd/kern/kern_shutdown.c
xnu-792.13.8.tar.gz
[apple/xnu.git] / bsd / kern / kern_shutdown.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30/*
31 * File: bsd/kern/kern_shutdown.c
32 *
33 * Copyright (C) 1989, NeXT, Inc.
34 *
35 */
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/kernel.h>
40#include <sys/vm.h>
41#include <sys/proc_internal.h>
42#include <sys/user.h>
43#include <sys/reboot.h>
44#include <sys/conf.h>
45#include <sys/vnode_internal.h>
46#include <sys/file_internal.h>
47#include <sys/clist.h>
48#include <sys/callout.h>
49#include <sys/mbuf.h>
50#include <sys/msgbuf.h>
51#include <sys/ioctl.h>
52#include <sys/signal.h>
53#include <sys/tty.h>
54#include <kern/task.h>
55#include <sys/quota.h>
56#include <ufs/ufs/inode.h>
57#if NCPUS > 1
58#include <kern/processor.h>
59#include <kern/thread.h>
60#include <sys/lock.h>
61#endif /* NCPUS > 1 */
62#include <vm/vm_kern.h>
63#include <mach/vm_param.h>
64#include <sys/filedesc.h>
65#include <mach/host_priv.h>
66#include <mach/host_reboot.h>
67
68#include <bsm/audit_kernel.h>
69
70int waittime = -1;
71static void proc_shutdown();
72
73void
74boot(paniced, howto, command)
75 int paniced, howto;
76 char *command;
77{
78 register int i;
79 int s;
80 struct proc *p = current_proc(); /* XXX */
81 int hostboot_option=0;
82 int funnel_state;
83 struct proc *launchd_proc;
84
85 extern void md_prepare_for_shutdown(int paniced, int howto, char * command);
86
87 funnel_state = thread_funnel_set(kernel_flock, TRUE);
88
89 md_prepare_for_shutdown(paniced, howto, command);
90
91 if ((howto&RB_NOSYNC)==0 && waittime < 0) {
92 int iter, nbusy;
93
94 waittime = 0;
95
96 printf("syncing disks... ");
97
98 /*
99 * Release vnodes held by texts before sync.
100 */
101
102 /* handle live procs (deallocate their root and current directories). */
103 proc_shutdown();
104
105 audit_shutdown();
106
107 sync(p, (void *)NULL, (int *)NULL);
108
109 /*
110 * Now that all processes have been termianted and system is sync'ed up,
111 * suspend launchd
112 */
113
114 launchd_proc = pfind(1);
115 if (launchd_proc && p != launchd_proc) {
116 task_suspend(launchd_proc->task);
117 }
118
119 /*
120 * Unmount filesystems
121 */
122 vfs_unmountall();
123
124 /* Wait for the buffer cache to clean remaining dirty buffers */
125 for (iter = 0; iter < 100; iter++) {
126 nbusy = count_busy_buffers();
127 if (nbusy == 0)
128 break;
129 printf("%d ", nbusy);
130 IOSleep( 1 * nbusy );
131 }
132 if (nbusy)
133 printf("giving up\n");
134 else
135 printf("done\n");
136 }
137
138 /*
139 * Can't just use an splnet() here to disable the network
140 * because that will lock out softints which the disk
141 * drivers depend on to finish DMAs.
142 */
143 if_down_all();
144
145 if (howto & RB_POWERDOWN)
146 hostboot_option = HOST_REBOOT_HALT;
147 if (howto & RB_HALT)
148 hostboot_option = HOST_REBOOT_HALT;
149 if (paniced == RB_PANIC)
150 hostboot_option = HOST_REBOOT_HALT;
151
152 if (howto & RB_UPSDELAY) {
153 hostboot_option = HOST_REBOOT_UPSDELAY;
154 }
155
156 /*
157 * if we're going to power down due to a halt,
158 * give the disks a chance to finish getting
159 * the track cache flushed to the media...
160 * unfortunately, some of our earlier drives
161 * don't properly hold off on returning
162 * from the track flush command (issued by
163 * the unmounts) until it's actully fully
164 * committed.
165 */
166 if (hostboot_option == HOST_REBOOT_HALT)
167 IOSleep( 1 * 1000 );
168
169 host_reboot(host_priv_self(), hostboot_option);
170
171 thread_funnel_set(kernel_flock, FALSE);
172}
173
174/*
175 * proc_shutdown()
176 *
177 * Shutdown down proc system (release references to current and root
178 * dirs for each process).
179 *
180 * POSIX modifications:
181 *
182 * For POSIX fcntl() file locking call vno_lockrelease() on
183 * the file to release all of its record locks, if any.
184 */
185
186static void
187proc_shutdown()
188{
189 struct proc *p, *self;
190 struct vnode **cdirp, **rdirp, *vp;
191 int restart, i, TERM_catch;
192 int delayterm = 0;
193
194 /*
195 * Kill as many procs as we can. (Except ourself...)
196 */
197 self = (struct proc *)current_proc();
198
199 /*
200 * Signal the init with SIGTERM so that he does not launch
201 * new processes
202 */
203 p = pfind(1);
204 if (p && p != self) {
205 psignal(p, SIGTERM);
206 }
207
208 printf("Killing all processes ");
209
210 /*
211 * send SIGTERM to those procs interested in catching one
212 */
213sigterm_loop:
214 for (p = allproc.lh_first; p; p = p->p_list.le_next) {
215 if (((p->p_flag&P_SYSTEM) == 0) && (p->p_pptr->p_pid != 0) && (p != self) && (p->p_stat != SZOMB) && (p->p_shutdownstate == 0)) {
216
217 if ((delayterm == 0) && ((p->p_lflag& P_LDELAYTERM) == P_LDELAYTERM)) {
218 continue;
219 }
220 if (p->p_sigcatch & sigmask(SIGTERM)) {
221 p->p_shutdownstate = 1;
222 if (proc_refinternal(p, 1) == p) {
223 psignal(p, SIGTERM);
224 proc_dropinternal(p, 1);
225 }
226 goto sigterm_loop;
227 }
228 }
229 }
230 /*
231 * now wait for up to 30 seconds to allow those procs catching SIGTERM
232 * to digest it
233 * as soon as these procs have exited, we'll continue on to the next step
234 */
235 for (i = 0; i < 300; i++) {
236 /*
237 * sleep for a tenth of a second
238 * and then check to see if the tasks that were sent a
239 * SIGTERM have exited
240 */
241 IOSleep(100);
242 TERM_catch = 0;
243
244 for (p = allproc.lh_first; p; p = p->p_list.le_next) {
245 if (p->p_shutdownstate == 1) {
246 TERM_catch++;
247 }
248 }
249 if (TERM_catch == 0)
250 break;
251 }
252 if (TERM_catch) {
253 /*
254 * log the names of the unresponsive tasks
255 */
256
257 for (p = allproc.lh_first; p; p = p->p_list.le_next) {
258 if (p->p_shutdownstate == 1) {
259 printf("%s[%d]: didn't act on SIGTERM\n", p->p_comm, p->p_pid);
260 }
261 }
262 IOSleep(1000 * 5);
263 }
264
265 /*
266 * send a SIGKILL to all the procs still hanging around
267 */
268sigkill_loop:
269 for (p = allproc.lh_first; p; p = p->p_list.le_next) {
270 if (((p->p_flag&P_SYSTEM) == 0) && (p->p_pptr->p_pid != 0) && (p != self) && (p->p_stat != SZOMB) && (p->p_shutdownstate != 2)) {
271
272 if ((delayterm == 0) && ((p->p_lflag& P_LDELAYTERM) == P_LDELAYTERM)) {
273 continue;
274 }
275 if (proc_refinternal(p, 1) == p) {
276 psignal(p, SIGKILL);
277 proc_dropinternal(p, 1);
278 }
279 p->p_shutdownstate = 2;
280 goto sigkill_loop;
281 }
282 }
283 /*
284 * wait for up to 60 seconds to allow these procs to exit normally
285 */
286 for (i = 0; i < 300; i++) {
287 IOSleep(200); /* double the time from 100 to 200 for NFS requests in particular */
288
289 for (p = allproc.lh_first; p; p = p->p_list.le_next) {
290 if (p->p_shutdownstate == 2)
291 break;
292 }
293 if (!p)
294 break;
295 }
296
297 /*
298 * if we still have procs that haven't exited, then brute force 'em
299 */
300 p = allproc.lh_first;
301 while (p) {
302 if ((p->p_shutdownstate == 3) || (p->p_flag&P_SYSTEM) || (!delayterm && ((p->p_lflag& P_LDELAYTERM)))
303 || (p->p_pptr->p_pid == 0) || (p == self)) {
304 p = p->p_list.le_next;
305 }
306 else {
307 p->p_shutdownstate = 3;
308 /*
309 * NOTE: following code ignores sig_lock and plays
310 * with exit_thread correctly. This is OK unless we
311 * are a multiprocessor, in which case I do not
312 * understand the sig_lock. This needs to be fixed.
313 * XXX
314 */
315 if (p->exit_thread) { /* someone already doing it */
316 /* give him a chance */
317 thread_block(THREAD_CONTINUE_NULL);
318 } else {
319 p->exit_thread = current_thread();
320 printf(".");
321 if (proc_refinternal(p, 1) == p) {
322 exit1(p, 1, (int *)NULL);
323 proc_dropinternal(p, 1);
324 }
325 }
326 p = allproc.lh_first;
327 }
328 }
329 printf("\n");
330
331
332 /* Now start the termination of processes that are marked for delayed termn */
333 if (delayterm == 0) {
334 delayterm = 1;
335 goto sigterm_loop;
336 }
337 printf("continuing\n");
338}
339