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