]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2000-2006 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 | #include <kern/sched_prim.h> /* for thread_block() */ | |
69 | #include <kern/host.h> /* for host_priv_self() */ | |
70 | #include <net/if_var.h> /* for if_down_all() */ | |
71 | #include <sys/buf_internal.h> /* for count_busy_buffers() */ | |
72 | #include <sys/mount_internal.h> /* for vfs_unmountall() */ | |
73 | #include <mach/task.h> /* for task_suspend() */ | |
74 | #include <sys/sysproto.h> /* abused for sync() */ | |
75 | #include <kern/clock.h> /* for delay_for_interval() */ | |
76 | ||
77 | /* XXX should be in a header file somewhere, but isn't */ | |
78 | extern void md_prepare_for_shutdown(int, int, char *); | |
79 | ||
80 | int waittime = -1; | |
81 | static int shutting_down = 0; | |
82 | ||
83 | static void proc_shutdown(void); | |
84 | int in_shutdown(void); | |
85 | ||
86 | extern void IOSystemShutdownNotification(void); | |
87 | ||
88 | struct sd_filterargs{ | |
89 | int delayterm; | |
90 | int shutdownstate; | |
91 | }; | |
92 | ||
93 | ||
94 | struct sd_iterargs { | |
95 | int signo; /* the signal to be posted */ | |
96 | int setsdstate; /* shutdown state to be set */ | |
97 | }; | |
98 | ||
99 | static int sd_filt1(proc_t, void *); | |
100 | static int sd_filt2(proc_t, void *); | |
101 | static int sd_callback1(proc_t p, void * arg); | |
102 | static int sd_callback2(proc_t p, void * arg); | |
103 | static int sd_callback3(proc_t p, void * arg); | |
104 | ||
105 | void | |
106 | boot(int paniced, int howto, char *command) | |
107 | { | |
108 | struct proc *p = current_proc(); /* XXX */ | |
109 | int hostboot_option=0; | |
110 | int funnel_state; | |
111 | ||
112 | funnel_state = thread_funnel_set(kernel_flock, TRUE); | |
113 | ||
114 | /* | |
115 | * Temporary hack to notify the power management root domain | |
116 | * that the system will shut down. | |
117 | */ | |
118 | IOSystemShutdownNotification(); | |
119 | ||
120 | shutting_down = 1; | |
121 | ||
122 | md_prepare_for_shutdown(paniced, howto, command); | |
123 | ||
124 | if ((howto&RB_NOSYNC)==0 && waittime < 0) { | |
125 | int iter, nbusy; | |
126 | ||
127 | waittime = 0; | |
128 | ||
129 | printf("syncing disks... "); | |
130 | ||
131 | /* | |
132 | * Release vnodes held by texts before sync. | |
133 | */ | |
134 | ||
135 | /* handle live procs (deallocate their root and current directories). */ | |
136 | proc_shutdown(); | |
137 | ||
138 | #if AUDIT | |
139 | audit_shutdown(); | |
140 | #endif | |
141 | ||
142 | sync(p, (void *)NULL, (int *)NULL); | |
143 | ||
144 | /* | |
145 | * Now that all processes have been terminated and system is | |
146 | * sync'ed up, suspend init | |
147 | */ | |
148 | ||
149 | if (initproc && p != initproc) | |
150 | task_suspend(initproc->task); | |
151 | ||
152 | /* | |
153 | * Unmount filesystems | |
154 | */ | |
155 | vfs_unmountall(); | |
156 | ||
157 | /* Wait for the buffer cache to clean remaining dirty buffers */ | |
158 | for (iter = 0; iter < 100; iter++) { | |
159 | nbusy = count_busy_buffers(); | |
160 | if (nbusy == 0) | |
161 | break; | |
162 | printf("%d ", nbusy); | |
163 | delay_for_interval( 1 * nbusy, 1000 * 1000); | |
164 | } | |
165 | if (nbusy) | |
166 | printf("giving up\n"); | |
167 | else | |
168 | printf("done\n"); | |
169 | } | |
170 | ||
171 | #if NETWORKING | |
172 | /* | |
173 | * Can't just use an splnet() here to disable the network | |
174 | * because that will lock out softints which the disk | |
175 | * drivers depend on to finish DMAs. | |
176 | */ | |
177 | if_down_all(); | |
178 | #endif /* NETWORKING */ | |
179 | ||
180 | if (howto & RB_POWERDOWN) | |
181 | hostboot_option = HOST_REBOOT_HALT; | |
182 | if (howto & RB_HALT) | |
183 | hostboot_option = HOST_REBOOT_HALT; | |
184 | if (paniced == RB_PANIC) | |
185 | hostboot_option = HOST_REBOOT_HALT; | |
186 | ||
187 | if (howto & RB_UPSDELAY) { | |
188 | hostboot_option = HOST_REBOOT_UPSDELAY; | |
189 | } | |
190 | ||
191 | host_reboot(host_priv_self(), hostboot_option); | |
192 | ||
193 | thread_funnel_set(kernel_flock, FALSE); | |
194 | } | |
195 | ||
196 | static int | |
197 | sd_filt1(proc_t p, void * args) | |
198 | { | |
199 | proc_t self = current_proc(); | |
200 | struct sd_filterargs * sf = (struct sd_filterargs *)args; | |
201 | int delayterm = sf-> delayterm; | |
202 | int shutdownstate = sf->shutdownstate; | |
203 | ||
204 | if (((p->p_flag&P_SYSTEM) != 0) || (p->p_ppid == 0) | |
205 | ||(p == self) || (p->p_stat == SZOMB) | |
206 | || (p->p_shutdownstate != shutdownstate) | |
207 | ||((delayterm == 0) && ((p->p_lflag& P_LDELAYTERM) == P_LDELAYTERM)) | |
208 | || ((p->p_sigcatch & sigmask(SIGTERM))== 0)) { | |
209 | return(0); | |
210 | } | |
211 | else | |
212 | return(1); | |
213 | } | |
214 | ||
215 | ||
216 | static int | |
217 | sd_callback1(proc_t p, void * args) | |
218 | { | |
219 | struct sd_iterargs * sd = (struct sd_iterargs *)args; | |
220 | int signo = sd->signo; | |
221 | int setsdstate = sd->setsdstate; | |
222 | ||
223 | proc_lock(p); | |
224 | p->p_shutdownstate = setsdstate; | |
225 | if (p->p_stat != SZOMB) { | |
226 | proc_unlock(p); | |
227 | psignal(p, signo); | |
228 | } else | |
229 | proc_unlock(p); | |
230 | return(PROC_RETURNED); | |
231 | ||
232 | } | |
233 | ||
234 | static int | |
235 | sd_filt2(proc_t p, void * args) | |
236 | { | |
237 | proc_t self = current_proc(); | |
238 | struct sd_filterargs * sf = (struct sd_filterargs *)args; | |
239 | int delayterm = sf-> delayterm; | |
240 | int shutdownstate = sf->shutdownstate; | |
241 | ||
242 | if (((p->p_flag&P_SYSTEM) != 0) || (p->p_ppid == 0) | |
243 | ||(p == self) || (p->p_stat == SZOMB) | |
244 | || (p->p_shutdownstate == shutdownstate) | |
245 | ||((delayterm == 0) && ((p->p_lflag& P_LDELAYTERM) == P_LDELAYTERM))) { | |
246 | return(0); | |
247 | } | |
248 | else | |
249 | return(1); | |
250 | } | |
251 | ||
252 | static int | |
253 | sd_callback2(proc_t p, void * args) | |
254 | { | |
255 | struct sd_iterargs * sd = (struct sd_iterargs *)args; | |
256 | int signo = sd->signo; | |
257 | int setsdstate = sd->setsdstate; | |
258 | ||
259 | proc_lock(p); | |
260 | p->p_shutdownstate = setsdstate; | |
261 | if (p->p_stat != SZOMB) { | |
262 | proc_unlock(p); | |
263 | psignal(p, signo); | |
264 | } else | |
265 | proc_unlock(p); | |
266 | ||
267 | return(PROC_RETURNED); | |
268 | ||
269 | } | |
270 | ||
271 | static int | |
272 | sd_callback3(proc_t p, void * args) | |
273 | { | |
274 | struct sd_iterargs * sd = (struct sd_iterargs *)args; | |
275 | int setsdstate = sd->setsdstate; | |
276 | ||
277 | proc_lock(p); | |
278 | p->p_shutdownstate = setsdstate; | |
279 | if (p->p_stat != SZOMB) { | |
280 | /* | |
281 | * NOTE: following code ignores sig_lock and plays | |
282 | * with exit_thread correctly. This is OK unless we | |
283 | * are a multiprocessor, in which case I do not | |
284 | * understand the sig_lock. This needs to be fixed. | |
285 | * XXX | |
286 | */ | |
287 | if (p->exit_thread) { /* someone already doing it */ | |
288 | proc_unlock(p); | |
289 | /* give him a chance */ | |
290 | thread_block(THREAD_CONTINUE_NULL); | |
291 | } else { | |
292 | p->exit_thread = current_thread(); | |
293 | printf("."); | |
294 | proc_unlock(p); | |
295 | exit1(p, 1, (int *)NULL); | |
296 | } | |
297 | } else | |
298 | proc_unlock(p); | |
299 | ||
300 | return(PROC_RETURNED); | |
301 | } | |
302 | ||
303 | ||
304 | /* | |
305 | * proc_shutdown() | |
306 | * | |
307 | * Shutdown down proc system (release references to current and root | |
308 | * dirs for each process). | |
309 | * | |
310 | * POSIX modifications: | |
311 | * | |
312 | * For POSIX fcntl() file locking call vno_lockrelease() on | |
313 | * the file to release all of its record locks, if any. | |
314 | */ | |
315 | ||
316 | static void | |
317 | proc_shutdown(void) | |
318 | { | |
319 | struct proc *p, *self; | |
320 | int i, TERM_catch; | |
321 | int delayterm = 0; | |
322 | struct sd_filterargs sfargs; | |
323 | struct sd_iterargs sdargs; | |
324 | ||
325 | /* | |
326 | * Kill as many procs as we can. (Except ourself...) | |
327 | */ | |
328 | self = (struct proc *)current_proc(); | |
329 | ||
330 | /* | |
331 | * Signal the init with SIGTERM so that he does not launch | |
332 | * new processes | |
333 | */ | |
334 | p = proc_find(1); | |
335 | if (p && p != self) { | |
336 | psignal(p, SIGTERM); | |
337 | } | |
338 | proc_rele(p); | |
339 | ||
340 | printf("Killing all processes "); | |
341 | ||
342 | sigterm_loop: | |
343 | /* | |
344 | * send SIGTERM to those procs interested in catching one | |
345 | */ | |
346 | sfargs.delayterm = delayterm; | |
347 | sfargs.shutdownstate = 0; | |
348 | sdargs.signo = SIGTERM; | |
349 | sdargs.setsdstate = 1; | |
350 | ||
351 | /* post a SIGTERM to all that catch SIGTERM and not marked for delay */ | |
352 | proc_rebootscan(sd_callback1, (void *)&sdargs, sd_filt1, (void *)&sfargs); | |
353 | ||
354 | /* | |
355 | * now wait for up to 30 seconds to allow those procs catching SIGTERM | |
356 | * to digest it | |
357 | * as soon as these procs have exited, we'll continue on to the next step | |
358 | */ | |
359 | for (i = 0; i < 300; i++) { | |
360 | /* | |
361 | * sleep for a tenth of a second | |
362 | * and then check to see if the tasks that were sent a | |
363 | * SIGTERM have exited | |
364 | */ | |
365 | delay_for_interval(100, 1000 * 1000); | |
366 | TERM_catch = 0; | |
367 | ||
368 | ||
369 | proc_list_lock(); | |
370 | ||
371 | for (p = allproc.lh_first; p; p = p->p_list.le_next) { | |
372 | if (p->p_shutdownstate == 1) { | |
373 | TERM_catch++; | |
374 | } | |
375 | } | |
376 | ||
377 | proc_list_unlock(); | |
378 | ||
379 | if (TERM_catch == 0) | |
380 | break; | |
381 | } | |
382 | if (TERM_catch) { | |
383 | /* | |
384 | * log the names of the unresponsive tasks | |
385 | */ | |
386 | ||
387 | ||
388 | proc_list_lock(); | |
389 | ||
390 | for (p = allproc.lh_first; p; p = p->p_list.le_next) { | |
391 | if (p->p_shutdownstate == 1) { | |
392 | printf("%s[%d]: didn't act on SIGTERM\n", p->p_comm, p->p_pid); | |
393 | } | |
394 | } | |
395 | ||
396 | proc_list_unlock(); | |
397 | ||
398 | delay_for_interval(1000 * 5, 1000 * 1000); | |
399 | } | |
400 | ||
401 | /* | |
402 | * send a SIGKILL to all the procs still hanging around | |
403 | */ | |
404 | sfargs.delayterm = delayterm; | |
405 | sfargs.shutdownstate = 2; | |
406 | sdargs.signo = SIGKILL; | |
407 | sdargs.setsdstate = 2; | |
408 | ||
409 | /* post a SIGTERM to all that catch SIGTERM and not marked for delay */ | |
410 | proc_rebootscan(sd_callback2, (void *)&sdargs, sd_filt2, (void *)&sfargs); | |
411 | ||
412 | /* | |
413 | * wait for up to 60 seconds to allow these procs to exit normally | |
414 | * | |
415 | * History: The delay interval was changed from 100 to 200 | |
416 | * for NFS requests in particular. | |
417 | */ | |
418 | for (i = 0; i < 300; i++) { | |
419 | delay_for_interval(200, 1000 * 1000); | |
420 | ||
421 | ||
422 | proc_list_lock(); | |
423 | ||
424 | for (p = allproc.lh_first; p; p = p->p_list.le_next) { | |
425 | if (p->p_shutdownstate == 2) | |
426 | break; | |
427 | } | |
428 | ||
429 | proc_list_unlock(); | |
430 | ||
431 | if (!p) | |
432 | break; | |
433 | } | |
434 | ||
435 | /* | |
436 | * if we still have procs that haven't exited, then brute force 'em | |
437 | */ | |
438 | sfargs.delayterm = delayterm; | |
439 | sfargs.shutdownstate = 3; | |
440 | sdargs.signo = 0; | |
441 | sdargs.setsdstate = 3; | |
442 | ||
443 | /* post a SIGTERM to all that catch SIGTERM and not marked for delay */ | |
444 | proc_rebootscan(sd_callback3, (void *)&sdargs, sd_filt2, (void *)&sfargs); | |
445 | printf("\n"); | |
446 | ||
447 | /* Now start the termination of processes that are marked for delayed termn */ | |
448 | if (delayterm == 0) { | |
449 | delayterm = 1; | |
450 | goto sigterm_loop; | |
451 | } | |
452 | /* drop the ref on initproc */ | |
453 | proc_rele(initproc); | |
454 | printf("continuing\n"); | |
455 | } | |
456 | ||
457 | /* | |
458 | * Check whether the system has begun its shutdown sequence. | |
459 | */ | |
460 | int | |
461 | in_shutdown(void) | |
462 | { | |
463 | return shutting_down; | |
464 | } |