]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
2d21ac55 | 2 | * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved. |
5d5c5d0d | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
1c79356b | 5 | * |
2d21ac55 A |
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. | |
8f6c56a5 | 14 | * |
2d21ac55 A |
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 | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
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. | |
8f6c56a5 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
1c79356b A |
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> | |
91447636 | 39 | #include <sys/proc_internal.h> |
1c79356b | 40 | #include <sys/user.h> |
1c79356b A |
41 | #include <sys/reboot.h> |
42 | #include <sys/conf.h> | |
91447636 A |
43 | #include <sys/vnode_internal.h> |
44 | #include <sys/file_internal.h> | |
1c79356b A |
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> | |
9bccf70c | 53 | #include <sys/quota.h> |
1c79356b A |
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> | |
91447636 | 63 | #include <mach/host_priv.h> |
1c79356b | 64 | #include <mach/host_reboot.h> |
91447636 | 65 | |
e5568f75 | 66 | #include <bsm/audit_kernel.h> |
1c79356b | 67 | |
2d21ac55 A |
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 | ||
1c79356b | 80 | int waittime = -1; |
2d21ac55 A |
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); | |
1c79356b A |
104 | |
105 | void | |
2d21ac55 | 106 | boot(int paniced, int howto, char *command) |
1c79356b | 107 | { |
1c79356b A |
108 | struct proc *p = current_proc(); /* XXX */ |
109 | int hostboot_option=0; | |
110 | int funnel_state; | |
1c79356b A |
111 | |
112 | funnel_state = thread_funnel_set(kernel_flock, TRUE); | |
113 | ||
2d21ac55 A |
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 | ||
0b4e3aa0 | 122 | md_prepare_for_shutdown(paniced, howto, command); |
1c79356b A |
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 | ||
2d21ac55 A |
138 | #if AUDIT |
139 | audit_shutdown(); | |
140 | #endif | |
55e303ae | 141 | |
1c79356b A |
142 | sync(p, (void *)NULL, (int *)NULL); |
143 | ||
91447636 | 144 | /* |
2d21ac55 A |
145 | * Now that all processes have been terminated and system is |
146 | * sync'ed up, suspend init | |
91447636 | 147 | */ |
1c79356b | 148 | |
2d21ac55 A |
149 | if (initproc && p != initproc) |
150 | task_suspend(initproc->task); | |
1c79356b A |
151 | |
152 | /* | |
153 | * Unmount filesystems | |
154 | */ | |
91447636 | 155 | vfs_unmountall(); |
1c79356b A |
156 | |
157 | /* Wait for the buffer cache to clean remaining dirty buffers */ | |
91447636 | 158 | for (iter = 0; iter < 100; iter++) { |
1c79356b A |
159 | nbusy = count_busy_buffers(); |
160 | if (nbusy == 0) | |
161 | break; | |
162 | printf("%d ", nbusy); | |
2d21ac55 | 163 | delay_for_interval( 1 * nbusy, 1000 * 1000); |
1c79356b A |
164 | } |
165 | if (nbusy) | |
166 | printf("giving up\n"); | |
167 | else | |
168 | printf("done\n"); | |
169 | } | |
170 | ||
2d21ac55 | 171 | #if NETWORKING |
1c79356b A |
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(); | |
2d21ac55 | 178 | #endif /* NETWORKING */ |
1c79356b A |
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 | ||
0c530ab8 A |
187 | if (howto & RB_UPSDELAY) { |
188 | hostboot_option = HOST_REBOOT_UPSDELAY; | |
189 | } | |
190 | ||
1c79356b A |
191 | host_reboot(host_priv_self(), hostboot_option); |
192 | ||
193 | thread_funnel_set(kernel_flock, FALSE); | |
194 | } | |
195 | ||
2d21ac55 A |
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 | ||
1c79356b A |
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 | |
2d21ac55 | 317 | proc_shutdown(void) |
1c79356b A |
318 | { |
319 | struct proc *p, *self; | |
2d21ac55 | 320 | int i, TERM_catch; |
91447636 | 321 | int delayterm = 0; |
2d21ac55 A |
322 | struct sd_filterargs sfargs; |
323 | struct sd_iterargs sdargs; | |
1c79356b A |
324 | |
325 | /* | |
326 | * Kill as many procs as we can. (Except ourself...) | |
327 | */ | |
0b4e3aa0 | 328 | self = (struct proc *)current_proc(); |
1c79356b A |
329 | |
330 | /* | |
91447636 A |
331 | * Signal the init with SIGTERM so that he does not launch |
332 | * new processes | |
1c79356b | 333 | */ |
2d21ac55 | 334 | p = proc_find(1); |
91447636 A |
335 | if (p && p != self) { |
336 | psignal(p, SIGTERM); | |
337 | } | |
2d21ac55 | 338 | proc_rele(p); |
1c79356b | 339 | |
1c79356b A |
340 | printf("Killing all processes "); |
341 | ||
2d21ac55 | 342 | sigterm_loop: |
1c79356b A |
343 | /* |
344 | * send SIGTERM to those procs interested in catching one | |
345 | */ | |
2d21ac55 A |
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); | |
91447636 | 353 | |
1c79356b A |
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 | */ | |
2d21ac55 | 365 | delay_for_interval(100, 1000 * 1000); |
1c79356b A |
366 | TERM_catch = 0; |
367 | ||
2d21ac55 A |
368 | |
369 | proc_list_lock(); | |
370 | ||
91447636 A |
371 | for (p = allproc.lh_first; p; p = p->p_list.le_next) { |
372 | if (p->p_shutdownstate == 1) { | |
373 | TERM_catch++; | |
374 | } | |
1c79356b | 375 | } |
2d21ac55 A |
376 | |
377 | proc_list_unlock(); | |
378 | ||
1c79356b A |
379 | if (TERM_catch == 0) |
380 | break; | |
381 | } | |
55e303ae | 382 | if (TERM_catch) { |
91447636 | 383 | /* |
55e303ae A |
384 | * log the names of the unresponsive tasks |
385 | */ | |
91447636 | 386 | |
2d21ac55 A |
387 | |
388 | proc_list_lock(); | |
389 | ||
55e303ae | 390 | for (p = allproc.lh_first; p; p = p->p_list.le_next) { |
91447636 | 391 | if (p->p_shutdownstate == 1) { |
55e303ae | 392 | printf("%s[%d]: didn't act on SIGTERM\n", p->p_comm, p->p_pid); |
91447636 | 393 | } |
55e303ae | 394 | } |
2d21ac55 A |
395 | |
396 | proc_list_unlock(); | |
397 | ||
398 | delay_for_interval(1000 * 5, 1000 * 1000); | |
55e303ae | 399 | } |
1c79356b A |
400 | |
401 | /* | |
402 | * send a SIGKILL to all the procs still hanging around | |
403 | */ | |
2d21ac55 A |
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); | |
91447636 | 411 | |
1c79356b A |
412 | /* |
413 | * wait for up to 60 seconds to allow these procs to exit normally | |
2d21ac55 A |
414 | * |
415 | * History: The delay interval was changed from 100 to 200 | |
416 | * for NFS requests in particular. | |
1c79356b A |
417 | */ |
418 | for (i = 0; i < 300; i++) { | |
2d21ac55 A |
419 | delay_for_interval(200, 1000 * 1000); |
420 | ||
421 | ||
422 | proc_list_lock(); | |
1c79356b A |
423 | |
424 | for (p = allproc.lh_first; p; p = p->p_list.le_next) { | |
91447636 | 425 | if (p->p_shutdownstate == 2) |
1c79356b A |
426 | break; |
427 | } | |
2d21ac55 A |
428 | |
429 | proc_list_unlock(); | |
430 | ||
1c79356b A |
431 | if (!p) |
432 | break; | |
433 | } | |
434 | ||
435 | /* | |
436 | * if we still have procs that haven't exited, then brute force 'em | |
437 | */ | |
2d21ac55 A |
438 | sfargs.delayterm = delayterm; |
439 | sfargs.shutdownstate = 3; | |
440 | sdargs.signo = 0; | |
441 | sdargs.setsdstate = 3; | |
4a249263 | 442 | |
2d21ac55 A |
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"); | |
91447636 A |
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; | |
1c79356b | 451 | } |
2d21ac55 A |
452 | /* drop the ref on initproc */ |
453 | proc_rele(initproc); | |
1c79356b A |
454 | printf("continuing\n"); |
455 | } | |
456 | ||
2d21ac55 A |
457 | /* |
458 | * Check whether the system has begun its shutdown sequence. | |
459 | */ | |
460 | int | |
461 | in_shutdown(void) | |
462 | { | |
463 | return shutting_down; | |
464 | } |