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