1/* 2 * Copyright (c) 1980, 1986, 1993 3 * The Regents of the University of California. All rights reserved. 4 * Portions copyright (c) 2007 Apple Inc. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of the University nor the names of its contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31#ifndef lint 32static const char copyright[] = 33"@(#) Copyright (c) 1980, 1986, 1993\n\ 34 The Regents of the University of California. All rights reserved.\n"; 35#endif /* not lint */ 36 37#ifndef lint 38#if 0 39static char sccsid[] = "@(#)reboot.c 8.1 (Berkeley) 6/5/93"; 40#endif 41static const char rcsid[] = 42 "$FreeBSD: src/sbin/reboot/reboot.c,v 1.17 2002/10/06 16:24:36 thomas Exp $"; 43#endif /* not lint */ 44 45#include <sys/reboot.h> 46#include <sys/types.h> 47#include <sys/sysctl.h> 48#include <signal.h> 49#include <err.h> 50#include <errno.h> 51#include <fcntl.h> 52#include <util.h> 53#include <pwd.h> 54#include <syslog.h> 55#include <stdio.h> 56#include <stdlib.h> 57#include <string.h> 58#include <unistd.h> 59 60#ifdef __APPLE__ 61#include <TargetConditionals.h> 62#if !TARGET_OS_EMBEDDED 63#include "kextmanager.h" 64#include <IOKit/kext/kextmanager_types.h> 65#endif 66#include <mach/mach_port.h> // allocate 67#include <mach/mach.h> // task_self, etc 68#include <servers/bootstrap.h> // bootstrap 69#include <bootstrap_priv.h> 70#include <reboot2.h> 71#include <utmpx.h> 72#include <sys/time.h> 73#endif 74 75void usage(void); 76u_int get_pageins(void); 77#if defined(__APPLE__) && !TARGET_OS_EMBEDDED 78int reserve_reboot(void); 79#endif 80 81int dohalt; 82 83int 84main(int argc, char *argv[]) 85{ 86 struct passwd *pw; 87 int ch, howto, kflag, lflag, nflag, qflag, uflag; 88 char *p; 89 const char *user; 90#ifndef __APPLE__ 91 int i, fd, pflag, sverrno; 92 u_int pageins; 93 char *kernel; 94#endif 95 96 if (strstr((p = rindex(*argv, '/')) ? p + 1 : *argv, "halt")) { 97 dohalt = 1; 98 howto = RB_HALT; 99 } else 100 howto = 0; 101 kflag = lflag = nflag = qflag = 0; 102#ifndef __APPLE__ 103 while ((ch = getopt(argc, argv, "dk:lnpq")) != -1) 104#else 105 while ((ch = getopt(argc, argv, "lnqu")) != -1) 106#endif 107 switch(ch) { 108#ifndef __APPLE__ 109 case 'd': 110 howto |= RB_DUMP; 111 break; 112 case 'k': 113 kflag = 1; 114 kernel = optarg; 115 break; 116#endif 117 case 'l': 118 lflag = 1; 119 break; 120 case 'n': 121 nflag = 1; 122 howto |= RB_NOSYNC; 123 break; 124/* -p is irrelevant on OS X. It does that anyway. */ 125#ifndef __APPLE__ 126 case 'p': 127 pflag = 1; 128 howto |= RB_POWEROFF; 129 break; 130#endif 131 case 'u': 132 uflag = 1; 133 howto |= RB_UPSDELAY; 134 break; 135 case 'q': 136 qflag = 1; 137 howto |= RB_QUICK; 138 break; 139 case '?': 140 default: 141 usage(); 142 } 143 argc -= optind; 144 argv += optind; 145 146#ifndef __APPLE__ 147 if ((howto & (RB_DUMP | RB_HALT)) == (RB_DUMP | RB_HALT)) 148 errx(1, "cannot dump (-d) when halting; must reboot instead"); 149#endif 150 if (geteuid()) { 151 errno = EPERM; 152 err(1, NULL); 153 } 154 155#if defined(__APPLE__) && !TARGET_OS_EMBEDDED 156 if (!qflag && !lflag) { // shutdown(8) has already checked w/kextd 157 if ((errno = reserve_reboot())) 158 err(1, "couldn't lock for reboot"); 159 } 160#endif 161 162 if (qflag) { 163 reboot(howto); 164 err(1, NULL); 165 } 166 167#ifndef __APPLE__ 168 if (kflag) { 169 fd = open("/boot/nextboot.conf", O_WRONLY | O_CREAT, 0444); 170 if (fd > -1) { 171 (void)write(fd, "nextboot_enable=\"YES\"\n", 22); 172 (void)write(fd, "kernel=\"", 8L); 173 (void)write(fd, kernel, strlen(kernel)); 174 (void)write(fd, "\"\n", 2); 175 close(fd); 176 } 177 } 178#endif 179 180 /* Log the reboot. */ 181 if (!lflag) { 182 if ((user = getlogin()) == NULL) 183 user = (pw = getpwuid(getuid())) ? 184 pw->pw_name : "???"; 185 if (dohalt) { 186 openlog("halt", 0, LOG_AUTH | LOG_CONS); 187 syslog(LOG_CRIT, "halted by %s%s", user, 188 (howto & RB_UPSDELAY) ? " with UPS delay":""); 189 } else { 190 openlog("reboot", 0, LOG_AUTH | LOG_CONS); 191 syslog(LOG_CRIT, "rebooted by %s", user); 192 } 193 } 194#if defined(__APPLE__) 195 { 196 struct utmpx utx; 197 bzero(&utx, sizeof(utx)); 198 utx.ut_type = SHUTDOWN_TIME; 199 gettimeofday(&utx.ut_tv, NULL); 200 pututxline(&utx); 201 202 int newvalue = 1; 203 sysctlbyname("kern.willshutdown", NULL, NULL, &newvalue, sizeof(newvalue)); 204 } 205#else 206 logwtmp("~", "shutdown", ""); 207#endif 208 209 /* 210 * Do a sync early on, so disks start transfers while we're off 211 * killing processes. Don't worry about writes done before the 212 * processes die, the reboot system call syncs the disks. 213 */ 214 if (!nflag) 215 sync(); 216 217#ifndef __APPLE__ 218 /* Just stop init -- if we fail, we'll restart it. */ 219 if (kill(1, SIGTSTP) == -1) 220 err(1, "SIGTSTP init"); 221#endif 222 223 /* Ignore the SIGHUP we get when our parent shell dies. */ 224 (void)signal(SIGHUP, SIG_IGN); 225 226#ifndef __APPLE__ 227 /* Send a SIGTERM first, a chance to save the buffers. */ 228 if (kill(-1, SIGTERM) == -1) 229 err(1, "SIGTERM processes"); 230 231 /* 232 * After the processes receive the signal, start the rest of the 233 * buffers on their way. Wait 5 seconds between the SIGTERM and 234 * the SIGKILL to give everybody a chance. If there is a lot of 235 * paging activity then wait longer, up to a maximum of approx 236 * 60 seconds. 237 */ 238 sleep(2); 239 for (i = 0; i < 20; i++) { 240 pageins = get_pageins(); 241 if (!nflag) 242 sync(); 243 sleep(3); 244 if (get_pageins() == pageins) 245 break; 246 } 247 248 for (i = 1;; ++i) { 249 if (kill(-1, SIGKILL) == -1) { 250 if (errno == ESRCH) 251 break; 252 goto restart; 253 } 254 if (i > 5) { 255 (void)fprintf(stderr, 256 "WARNING: some process(es) wouldn't die\n"); 257 break; 258 } 259 (void)sleep(2 * i); 260 } 261#endif 262 263#ifdef __APPLE__ 264 // launchd(8) handles reboot. This call returns NULL on success. 265 exit(reboot2(howto) == NULL ? EXIT_SUCCESS : EXIT_FAILURE); 266#else /* __APPLE__ */ 267 reboot(howto); 268 /* FALLTHROUGH */ 269 270restart: 271 sverrno = errno; 272 errx(1, "%s%s", kill(1, SIGHUP) == -1 ? "(can't restart init): " : "", 273 strerror(sverrno)); 274 /* NOTREACHED */ 275#endif /* __APPLE__ */ 276} 277 278void 279usage() 280{ 281#ifndef __APPLE__ 282 (void)fprintf(stderr, "usage: %s [-dnpq] [-k kernel]\n", 283#else 284 (void)fprintf(stderr, "usage: %s [-lnq]\n", 285#endif 286 dohalt ? "halt" : "reboot"); 287 exit(1); 288} 289 290u_int 291get_pageins() 292{ 293 u_int pageins; 294 size_t len; 295 296 len = sizeof(pageins); 297 if (sysctlbyname("vm.stats.vm.v_swappgsin", &pageins, &len, NULL, 0) 298 != 0) { 299 warnx("v_swappgsin"); 300 return (0); 301 } 302 return pageins; 303} 304 305#if defined(__APPLE__) && !TARGET_OS_EMBEDDED 306// XX this routine is also in shutdown.tproj; it would be nice to share 307 308#define WAITFORLOCK 1 309/* 310 * contact kextd to lock for reboot 311 */ 312int 313reserve_reboot() 314{ 315 int rval = ELAST + 1; 316 kern_return_t macherr = KERN_FAILURE; 317 mach_port_t kxport, tport = MACH_PORT_NULL, myport = MACH_PORT_NULL; 318 int busyStatus = ELAST + 1; 319 mountpoint_t busyVol; 320 321 macherr = bootstrap_look_up2(bootstrap_port, KEXTD_SERVER_NAME, &kxport, 0, BOOTSTRAP_PRIVILEGED_SERVER); 322 if (macherr) goto finish; 323 324 // allocate a port to pass to kextd (in case we die) 325 tport = mach_task_self(); 326 if (tport == MACH_PORT_NULL) goto finish; 327 macherr = mach_port_allocate(tport, MACH_PORT_RIGHT_RECEIVE, &myport); 328 if (macherr) goto finish; 329 330 // try to lock for reboot 331 macherr = kextmanager_lock_reboot(kxport, myport, !WAITFORLOCK, busyVol, 332 &busyStatus); 333 if (macherr) goto finish; 334 335 if (busyStatus == EBUSY) { 336 warnx("%s is busy updating; waiting for lock", busyVol); 337 macherr = kextmanager_lock_reboot(kxport, myport, WAITFORLOCK, 338 busyVol, &busyStatus); 339 if (macherr) goto finish; 340 } 341 342 if (busyStatus == EALREADY) { 343 // reboot already in progress 344 rval = 0; 345 } else { 346 rval = busyStatus; 347 } 348 349finish: 350 // in general, we want to err on the side of allowing the reboot 351 if (macherr) { 352 if (macherr != BOOTSTRAP_UNKNOWN_SERVICE) 353 warnx("WARNING: couldn't lock kext manager for reboot: %s", 354 mach_error_string(macherr)); 355 rval = 0; 356 } 357 // unless we got the lock, clean up our port 358 if (busyStatus != 0 && myport != MACH_PORT_NULL) 359 mach_port_mod_refs(tport, myport, MACH_PORT_RIGHT_RECEIVE, -1); 360 361 return rval; 362} 363#endif 364