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