| 1 | /* |
| 2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. |
| 3 | * |
| 4 | * @APPLE_LICENSE_HEADER_START@ |
| 5 | * |
| 6 | * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. |
| 7 | * |
| 8 | * This file contains Original Code and/or Modifications of Original Code |
| 9 | * as defined in and that are subject to the Apple Public Source License |
| 10 | * Version 2.0 (the 'License'). You may not use this file except in |
| 11 | * compliance with the License. Please obtain a copy of the License at |
| 12 | * http://www.opensource.apple.com/apsl/ and read it before using this |
| 13 | * file. |
| 14 | * |
| 15 | * The Original Code and all software distributed under the License are |
| 16 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
| 17 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
| 18 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
| 19 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
| 20 | * Please see the License for the specific language governing rights and |
| 21 | * limitations under the License. |
| 22 | * |
| 23 | * @APPLE_LICENSE_HEADER_END@ |
| 24 | */ |
| 25 | /* |
| 26 | * File: bsd/kern/kern_shutdown.c |
| 27 | * |
| 28 | * Copyright (C) 1989, NeXT, Inc. |
| 29 | * |
| 30 | */ |
| 31 | |
| 32 | #include <sys/param.h> |
| 33 | #include <sys/systm.h> |
| 34 | #include <sys/kernel.h> |
| 35 | #include <sys/vm.h> |
| 36 | #include <sys/proc.h> |
| 37 | #include <sys/user.h> |
| 38 | #include <sys/buf.h> |
| 39 | #include <sys/reboot.h> |
| 40 | #include <sys/conf.h> |
| 41 | #include <sys/vnode.h> |
| 42 | #include <sys/file.h> |
| 43 | #include <sys/clist.h> |
| 44 | #include <sys/callout.h> |
| 45 | #include <sys/mbuf.h> |
| 46 | #include <sys/msgbuf.h> |
| 47 | #include <sys/ioctl.h> |
| 48 | #include <sys/signal.h> |
| 49 | #include <sys/tty.h> |
| 50 | #include <kern/task.h> |
| 51 | #include <sys/quota.h> |
| 52 | #include <ufs/ufs/inode.h> |
| 53 | #if NCPUS > 1 |
| 54 | #include <kern/processor.h> |
| 55 | #include <kern/thread.h> |
| 56 | #include <sys/lock.h> |
| 57 | #endif /* NCPUS > 1 */ |
| 58 | #include <vm/vm_kern.h> |
| 59 | #include <mach/vm_param.h> |
| 60 | #include <sys/filedesc.h> |
| 61 | #include <mach/host_reboot.h> |
| 62 | #include <sys/kern_audit.h> |
| 63 | |
| 64 | int waittime = -1; |
| 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 | |
| 77 | static void proc_shutdown(); |
| 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 | /* Release vnodes from the VM object cache */ |
| 103 | ubc_unmountall(); |
| 104 | |
| 105 | IOSleep( 1 * 1000 ); |
| 106 | |
| 107 | /* |
| 108 | * Unmount filesystems |
| 109 | */ |
| 110 | if (panicstr == 0) |
| 111 | vfs_unmountall(); |
| 112 | |
| 113 | /* Wait for the buffer cache to clean remaining dirty buffers */ |
| 114 | for (iter = 0; iter < 20; iter++) { |
| 115 | nbusy = count_busy_buffers(); |
| 116 | if (nbusy == 0) |
| 117 | break; |
| 118 | printf("%d ", nbusy); |
| 119 | IOSleep( 4 * nbusy ); |
| 120 | } |
| 121 | if (nbusy) |
| 122 | printf("giving up\n"); |
| 123 | else |
| 124 | printf("done\n"); |
| 125 | } |
| 126 | |
| 127 | /* |
| 128 | * Can't just use an splnet() here to disable the network |
| 129 | * because that will lock out softints which the disk |
| 130 | * drivers depend on to finish DMAs. |
| 131 | */ |
| 132 | if_down_all(); |
| 133 | |
| 134 | if (howto & RB_POWERDOWN) |
| 135 | hostboot_option = HOST_REBOOT_HALT; |
| 136 | if (howto & RB_HALT) |
| 137 | hostboot_option = HOST_REBOOT_HALT; |
| 138 | if (paniced == RB_PANIC) |
| 139 | hostboot_option = HOST_REBOOT_HALT; |
| 140 | |
| 141 | if (hostboot_option == HOST_REBOOT_HALT) |
| 142 | IOSleep( 1 * 1000 ); |
| 143 | |
| 144 | host_reboot(host_priv_self(), hostboot_option); |
| 145 | |
| 146 | thread_funnel_set(kernel_flock, FALSE); |
| 147 | } |
| 148 | |
| 149 | /* |
| 150 | * proc_shutdown() |
| 151 | * |
| 152 | * Shutdown down proc system (release references to current and root |
| 153 | * dirs for each process). |
| 154 | * |
| 155 | * POSIX modifications: |
| 156 | * |
| 157 | * For POSIX fcntl() file locking call vno_lockrelease() on |
| 158 | * the file to release all of its record locks, if any. |
| 159 | */ |
| 160 | |
| 161 | static void |
| 162 | proc_shutdown() |
| 163 | { |
| 164 | struct proc *p, *self; |
| 165 | struct vnode **cdirp, **rdirp, *vp; |
| 166 | int restart, i, TERM_catch; |
| 167 | |
| 168 | /* |
| 169 | * Kill as many procs as we can. (Except ourself...) |
| 170 | */ |
| 171 | self = (struct proc *)current_proc(); |
| 172 | |
| 173 | /* |
| 174 | * Suspend /etc/init |
| 175 | */ |
| 176 | p = pfind(1); |
| 177 | if (p && p != self) |
| 178 | task_suspend(p->task); /* stop init */ |
| 179 | |
| 180 | printf("Killing all processes "); |
| 181 | |
| 182 | /* |
| 183 | * send SIGTERM to those procs interested in catching one |
| 184 | */ |
| 185 | for (p = allproc.lh_first; p; p = p->p_list.le_next) { |
| 186 | if (((p->p_flag&P_SYSTEM) == 0) && (p->p_pptr->p_pid != 0) && (p != self)) { |
| 187 | if (p->p_sigcatch & sigmask(SIGTERM)) |
| 188 | psignal(p, SIGTERM); |
| 189 | } |
| 190 | } |
| 191 | /* |
| 192 | * now wait for up to 30 seconds to allow those procs catching SIGTERM |
| 193 | * to digest it |
| 194 | * as soon as these procs have exited, we'll continue on to the next step |
| 195 | */ |
| 196 | for (i = 0; i < 300; i++) { |
| 197 | /* |
| 198 | * sleep for a tenth of a second |
| 199 | * and then check to see if the tasks that were sent a |
| 200 | * SIGTERM have exited |
| 201 | */ |
| 202 | IOSleep(100); |
| 203 | TERM_catch = 0; |
| 204 | |
| 205 | for (p = allproc.lh_first; p; p = p->p_list.le_next) { |
| 206 | if (((p->p_flag&P_SYSTEM) == 0) && (p->p_pptr->p_pid != 0) && (p != self)) { |
| 207 | if (p->p_sigcatch & sigmask(SIGTERM)) |
| 208 | TERM_catch++; |
| 209 | } |
| 210 | } |
| 211 | if (TERM_catch == 0) |
| 212 | break; |
| 213 | } |
| 214 | if (TERM_catch) { |
| 215 | /* |
| 216 | * log the names of the unresponsive tasks |
| 217 | */ |
| 218 | |
| 219 | for (p = allproc.lh_first; p; p = p->p_list.le_next) { |
| 220 | if (((p->p_flag&P_SYSTEM) == 0) && (p->p_pptr->p_pid != 0) && (p != self)) { |
| 221 | if (p->p_sigcatch & sigmask(SIGTERM)) |
| 222 | printf("%s[%d]: didn't act on SIGTERM\n", p->p_comm, p->p_pid); |
| 223 | } |
| 224 | } |
| 225 | IOSleep(1000 * 5); |
| 226 | } |
| 227 | |
| 228 | /* |
| 229 | * send a SIGKILL to all the procs still hanging around |
| 230 | */ |
| 231 | for (p = allproc.lh_first; p; p = p->p_list.le_next) { |
| 232 | if (((p->p_flag&P_SYSTEM) == 0) && (p->p_pptr->p_pid != 0) && (p != self)) |
| 233 | psignal(p, SIGKILL); |
| 234 | } |
| 235 | /* |
| 236 | * wait for up to 60 seconds to allow these procs to exit normally |
| 237 | */ |
| 238 | for (i = 0; i < 300; i++) { |
| 239 | IOSleep(200); /* double the time from 100 to 200 for NFS requests in particular */ |
| 240 | |
| 241 | for (p = allproc.lh_first; p; p = p->p_list.le_next) { |
| 242 | if (((p->p_flag&P_SYSTEM) == 0) && (p->p_pptr->p_pid != 0) && (p != self)) |
| 243 | break; |
| 244 | } |
| 245 | if (!p) |
| 246 | break; |
| 247 | } |
| 248 | |
| 249 | /* |
| 250 | * if we still have procs that haven't exited, then brute force 'em |
| 251 | */ |
| 252 | p = allproc.lh_first; |
| 253 | while (p) { |
| 254 | if ((p->p_flag&P_SYSTEM) || (p->p_pptr->p_pid == 0) || (p == self)) { |
| 255 | p = p->p_list.le_next; |
| 256 | } |
| 257 | else { |
| 258 | /* |
| 259 | * NOTE: following code ignores sig_lock and plays |
| 260 | * with exit_thread correctly. This is OK unless we |
| 261 | * are a multiprocessor, in which case I do not |
| 262 | * understand the sig_lock. This needs to be fixed. |
| 263 | * XXX |
| 264 | */ |
| 265 | if (p->exit_thread) { /* someone already doing it */ |
| 266 | /* give him a chance */ |
| 267 | thread_block(THREAD_CONTINUE_NULL); |
| 268 | } |
| 269 | else { |
| 270 | p->exit_thread = current_act(); |
| 271 | printf("."); |
| 272 | exit1(p, 1, (int *)NULL); |
| 273 | } |
| 274 | p = allproc.lh_first; |
| 275 | } |
| 276 | } |
| 277 | printf("\n"); |
| 278 | /* |
| 279 | * Forcibly free resources of what's left. |
| 280 | */ |
| 281 | p = allproc.lh_first; |
| 282 | while (p) { |
| 283 | /* |
| 284 | * Close open files and release open-file table. |
| 285 | * This may block! |
| 286 | */ |
| 287 | #ifdef notyet |
| 288 | /* panics on reboot due to "zfree: non-allocated memory in collectable zone" message */ |
| 289 | fdfree(p); |
| 290 | #endif /* notyet */ |
| 291 | p = p->p_list.le_next; |
| 292 | } |
| 293 | /* Wait for the reaper thread to run, and clean up what we have done |
| 294 | * before we proceed with the hardcore shutdown. This reduces the race |
| 295 | * between kill_tasks and the reaper thread. |
| 296 | */ |
| 297 | /* thread_wakeup(&reaper_queue); */ |
| 298 | /* IOSleep( 1 * 1000); */ |
| 299 | printf("continuing\n"); |
| 300 | } |
| 301 | |