shutdown.c revision 1559
1275970Scy/* 2275970Scy * Copyright (c) 1988, 1990, 1993 3275970Scy * The Regents of the University of California. All rights reserved. 4275970Scy * 5275970Scy * Redistribution and use in source and binary forms, with or without 6275970Scy * modification, are permitted provided that the following conditions 7275970Scy * are met: 8275970Scy * 1. Redistributions of source code must retain the above copyright 9275970Scy * notice, this list of conditions and the following disclaimer. 10275970Scy * 2. Redistributions in binary form must reproduce the above copyright 11275970Scy * notice, this list of conditions and the following disclaimer in the 12275970Scy * documentation and/or other materials provided with the distribution. 13275970Scy * 3. All advertising materials mentioning features or use of this software 14275970Scy * must display the following acknowledgement: 15275970Scy * This product includes software developed by the University of 16275970Scy * California, Berkeley and its contributors. 17275970Scy * 4. Neither the name of the University nor the names of its contributors 18275970Scy * may be used to endorse or promote products derived from this software 19275970Scy * without specific prior written permission. 20275970Scy * 21275970Scy * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22275970Scy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23275970Scy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24275970Scy * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25275970Scy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26275970Scy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27275970Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28275970Scy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29275970Scy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30275970Scy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31275970Scy * SUCH DAMAGE. 32275970Scy */ 33275970Scy 34275970Scy#ifndef lint 35275970Scystatic char copyright[] = 36275970Scy"@(#) Copyright (c) 1988, 1990, 1993\n\ 37275970Scy The Regents of the University of California. All rights reserved.\n"; 38275970Scy#endif /* not lint */ 39275970Scy 40275970Scy#ifndef lint 41275970Scystatic char sccsid[] = "@(#)shutdown.c 8.2 (Berkeley) 2/16/94"; 42275970Scy#endif /* not lint */ 43275970Scy 44275970Scy#include <sys/param.h> 45275970Scy#include <sys/time.h> 46275970Scy#include <sys/resource.h> 47275970Scy#include <sys/syslog.h> 48275970Scy 49275970Scy#include <ctype.h> 50275970Scy#include <fcntl.h> 51275970Scy#include <pwd.h> 52275970Scy#include <setjmp.h> 53275970Scy#include <signal.h> 54275970Scy#include <stdio.h> 55275970Scy#include <stdlib.h> 56275970Scy#include <string.h> 57275970Scy#include <tzfile.h> 58275970Scy#include <unistd.h> 59275970Scy 60275970Scy#include "pathnames.h" 61275970Scy 62275970Scy#ifdef DEBUG 63275970Scy#undef _PATH_NOLOGIN 64275970Scy#define _PATH_NOLOGIN "./nologin" 65275970Scy#undef _PATH_FASTBOOT 66275970Scy#define _PATH_FASTBOOT "./fastboot" 67275970Scy#endif 68275970Scy 69275970Scy#define H *60*60 70275970Scy#define M *60 71275970Scy#define S *1 72275970Scy#define NOLOG_TIME 5*60 73275970Scystruct interval { 74275970Scy int timeleft, timetowait; 75275970Scy} tlist[] = { 76275970Scy 10 H, 5 H, 5 H, 3 H, 2 H, 1 H, 1 H, 30 M, 77275970Scy 30 M, 10 M, 20 M, 10 M, 10 M, 5 M, 5 M, 3 M, 78275970Scy 2 M, 1 M, 1 M, 30 S, 30 S, 30 S, 79275970Scy 0, 0, 80275970Scy}; 81275970Scy#undef H 82275970Scy#undef M 83275970Scy#undef S 84275970Scy 85275970Scystatic time_t offset, shuttime; 86275970Scystatic int dofast, dohalt, doreboot, killflg, mbuflen; 87275970Scystatic char *nosync, *whom, mbuf[BUFSIZ]; 88275970Scy 89275970Scyvoid badtime __P((void)); 90275970Scyvoid die_you_gravy_sucking_pig_dog __P((void)); 91275970Scyvoid doitfast __P((void)); 92275970Scyvoid finish __P((int)); 93275970Scyvoid getoffset __P((char *)); 94275970Scyvoid loop __P((void)); 95275970Scyvoid nolog __P((void)); 96275970Scyvoid timeout __P((int)); 97275970Scyvoid timewarn __P((int)); 98275970Scyvoid usage __P((void)); 99275970Scy 100275970Scyint 101275970Scymain(argc, argv) 102275970Scy int argc; 103275970Scy char *argv[]; 104275970Scy{ 105275970Scy extern int optind; 106275970Scy register char *p, *endp; 107275970Scy struct passwd *pw; 108275970Scy int arglen, ch, len, readstdin; 109275970Scy 110275970Scy#ifndef DEBUG 111275970Scy if (geteuid()) { 112275970Scy (void)fprintf(stderr, "shutdown: NOT super-user\n"); 113275970Scy exit(1); 114275970Scy } 115275970Scy#endif 116275970Scy nosync = NULL; 117275970Scy readstdin = 0; 118275970Scy while ((ch = getopt(argc, argv, "-fhknr")) != EOF) 119275970Scy switch (ch) { 120275970Scy case '-': 121275970Scy readstdin = 1; 122275970Scy break; 123275970Scy case 'f': 124275970Scy dofast = 1; 125275970Scy break; 126275970Scy case 'h': 127275970Scy dohalt = 1; 128275970Scy break; 129275970Scy case 'k': 130275970Scy killflg = 1; 131275970Scy break; 132275970Scy case 'n': 133275970Scy nosync = "-n"; 134275970Scy break; 135275970Scy case 'r': 136275970Scy doreboot = 1; 137275970Scy break; 138275970Scy case '?': 139275970Scy default: 140275970Scy usage(); 141275970Scy } 142275970Scy argc -= optind; 143275970Scy argv += optind; 144275970Scy 145275970Scy if (argc < 1) 146275970Scy usage(); 147275970Scy 148275970Scy if (dofast && nosync) { 149275970Scy (void)fprintf(stderr, 150275970Scy "shutdown: incompatible switches -f and -n.\n"); 151275970Scy usage(); 152275970Scy } 153275970Scy if (doreboot && dohalt) { 154275970Scy (void)fprintf(stderr, 155275970Scy "shutdown: incompatible switches -h and -r.\n"); 156275970Scy usage(); 157275970Scy } 158275970Scy getoffset(*argv++); 159275970Scy 160275970Scy if (*argv) { 161275970Scy for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { 162275970Scy arglen = strlen(*argv); 163275970Scy if ((len -= arglen) <= 2) 164275970Scy break; 165275970Scy if (p != mbuf) 166275970Scy *p++ = ' '; 167275970Scy bcopy(*argv, p, arglen); 168275970Scy p += arglen; 169275970Scy } 170275970Scy *p = '\n'; 171275970Scy *++p = '\0'; 172275970Scy } 173275970Scy 174275970Scy if (readstdin) { 175275970Scy p = mbuf; 176275970Scy endp = mbuf + sizeof(mbuf) - 2; 177275970Scy for (;;) { 178275970Scy if (!fgets(p, endp - p + 1, stdin)) 179275970Scy break; 180275970Scy for (; *p && p < endp; ++p); 181275970Scy if (p == endp) { 182275970Scy *p = '\n'; 183275970Scy *++p = '\0'; 184275970Scy break; 185275970Scy } 186275970Scy } 187275970Scy } 188275970Scy mbuflen = strlen(mbuf); 189275970Scy 190275970Scy if (offset) 191275970Scy (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); 192275970Scy else 193275970Scy (void)printf("Shutdown NOW!\n"); 194275970Scy 195275970Scy if (!(whom = getlogin())) 196275970Scy whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 197275970Scy 198275970Scy#ifdef DEBUG 199275970Scy (void)putc('\n', stdout); 200275970Scy#else 201275970Scy (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); 202275970Scy { 203275970Scy int forkpid; 204275970Scy 205275970Scy forkpid = fork(); 206275970Scy if (forkpid == -1) { 207275970Scy perror("shutdown: fork"); 208275970Scy exit(1); 209275970Scy } 210275970Scy if (forkpid) { 211275970Scy (void)printf("shutdown: [pid %d]\n", forkpid); 212275970Scy exit(0); 213275970Scy } 214275970Scy } 215275970Scy#endif 216275970Scy openlog("shutdown", LOG_CONS, LOG_AUTH); 217275970Scy loop(); 218275970Scy /* NOTREACHED */ 219275970Scy} 220275970Scy 221275970Scyvoid 222275970Scyloop() 223275970Scy{ 224275970Scy struct interval *tp; 225275970Scy u_int sltime; 226275970Scy int logged; 227275970Scy 228275970Scy if (offset <= NOLOG_TIME) { 229275970Scy logged = 1; 230275970Scy nolog(); 231275970Scy } 232275970Scy else 233275970Scy logged = 0; 234275970Scy tp = tlist; 235275970Scy if (tp->timeleft < offset) 236275970Scy (void)sleep((u_int)(offset - tp->timeleft)); 237275970Scy else { 238275970Scy while (offset < tp->timeleft) 239275970Scy ++tp; 240275970Scy /* 241275970Scy * Warn now, if going to sleep more than a fifth of 242275970Scy * the next wait time. 243275970Scy */ 244275970Scy if (sltime = offset - tp->timeleft) { 245275970Scy if (sltime > tp->timetowait / 5) 246275970Scy timewarn(offset); 247275970Scy (void)sleep(sltime); 248275970Scy } 249275970Scy } 250275970Scy for (;; ++tp) { 251275970Scy timewarn(tp->timeleft); 252289997Sglebius if (!logged && tp->timeleft <= NOLOG_TIME) { 253275970Scy logged = 1; 254289997Sglebius nolog(); 255289997Sglebius } 256289997Sglebius (void)sleep((u_int)tp->timetowait); 257289997Sglebius if (!tp->timeleft) 258275970Scy break; 259275970Scy } 260275970Scy die_you_gravy_sucking_pig_dog(); 261275970Scy} 262275970Scy 263275970Scystatic jmp_buf alarmbuf; 264275970Scy 265275970Scyvoid 266275970Scytimewarn(timeleft) 267275970Scy int timeleft; 268275970Scy{ 269275970Scy static int first; 270275970Scy static char hostname[MAXHOSTNAMELEN + 1]; 271275970Scy FILE *pf; 272275970Scy char wcmd[MAXPATHLEN + 4]; 273275970Scy 274275970Scy if (!first++) 275275970Scy (void)gethostname(hostname, sizeof(hostname)); 276275970Scy 277275970Scy /* undoc -n option to wall suppresses normal wall banner */ 278275970Scy (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); 279275970Scy if (!(pf = popen(wcmd, "w"))) { 280275970Scy syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); 281275970Scy return; 282275970Scy } 283275970Scy 284275970Scy (void)fprintf(pf, 285275970Scy "\007*** %sSystem shutdown message from %s@%s ***\007\n", 286275970Scy timeleft ? "": "FINAL ", whom, hostname); 287275970Scy 288275970Scy if (timeleft > 10*60) 289275970Scy (void)fprintf(pf, "System going down at %5.5s\n\n", 290275970Scy ctime(&shuttime) + 11); 291275970Scy else if (timeleft > 59) 292275970Scy (void)fprintf(pf, "System going down in %d minute%s\n\n", 293275970Scy timeleft / 60, (timeleft > 60) ? "s" : ""); 294275970Scy else if (timeleft) 295275970Scy (void)fprintf(pf, "System going down in 30 seconds\n\n"); 296275970Scy else 297275970Scy (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); 298275970Scy 299275970Scy if (mbuflen) 300275970Scy (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); 301275970Scy 302275970Scy /* 303275970Scy * play some games, just in case wall doesn't come back 304275970Scy * probably unecessary, given that wall is careful. 305275970Scy */ 306275970Scy if (!setjmp(alarmbuf)) { 307275970Scy (void)signal(SIGALRM, timeout); 308275970Scy (void)alarm((u_int)30); 309275970Scy (void)pclose(pf); 310275970Scy (void)alarm((u_int)0); 311275970Scy (void)signal(SIGALRM, SIG_DFL); 312275970Scy } 313275970Scy} 314275970Scy 315275970Scyvoid 316275970Scytimeout(signo) 317275970Scy int signo; 318275970Scy{ 319275970Scy longjmp(alarmbuf, 1); 320275970Scy} 321275970Scy 322275970Scyvoid 323275970Scydie_you_gravy_sucking_pig_dog() 324275970Scy{ 325275970Scy 326275970Scy syslog(LOG_NOTICE, "%s by %s: %s", 327275970Scy doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf); 328275970Scy (void)sleep(2); 329275970Scy 330275970Scy (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); 331275970Scy if (killflg) { 332275970Scy (void)printf("\rbut you'll have to do it yourself\r\n"); 333275970Scy finish(0); 334275970Scy } 335275970Scy if (dofast) 336275970Scy doitfast(); 337275970Scy#ifdef DEBUG 338275970Scy if (doreboot) 339275970Scy (void)printf("reboot"); 340275970Scy else if (dohalt) 341275970Scy (void)printf("halt"); 342275970Scy if (nosync) 343275970Scy (void)printf(" no sync"); 344275970Scy if (dofast) 345275970Scy (void)printf(" no fsck"); 346275970Scy (void)printf("\nkill -HUP 1\n"); 347275970Scy#else 348275970Scy if (doreboot) { 349275970Scy execle(_PATH_REBOOT, "reboot", "-l", nosync, 0); 350275970Scy syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); 351275970Scy perror("shutdown"); 352275970Scy } 353275970Scy else if (dohalt) { 354275970Scy execle(_PATH_HALT, "halt", "-l", nosync, 0); 355275970Scy syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); 356275970Scy perror("shutdown"); 357275970Scy } 358275970Scy (void)kill(1, SIGTERM); /* to single user */ 359275970Scy#endif 360275970Scy finish(0); 361275970Scy} 362275970Scy 363275970Scy#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; 364275970Scy 365275970Scyvoid 366275970Scygetoffset(timearg) 367275970Scy register char *timearg; 368275970Scy{ 369275970Scy register struct tm *lt; 370275970Scy register char *p; 371275970Scy time_t now; 372275970Scy 373275970Scy if (!strcasecmp(timearg, "now")) { /* now */ 374275970Scy offset = 0; 375275970Scy return; 376275970Scy } 377275970Scy 378275970Scy (void)time(&now); 379275970Scy if (*timearg == '+') { /* +minutes */ 380275970Scy if (!isdigit(*++timearg)) 381275970Scy badtime(); 382275970Scy offset = atoi(timearg) * 60; 383275970Scy shuttime = now + offset; 384275970Scy return; 385275970Scy } 386275970Scy 387275970Scy /* handle hh:mm by getting rid of the colon */ 388275970Scy for (p = timearg; *p; ++p) 389275970Scy if (!isascii(*p) || !isdigit(*p)) 390275970Scy if (*p == ':' && strlen(p) == 3) { 391275970Scy p[0] = p[1]; 392275970Scy p[1] = p[2]; 393275970Scy p[2] = '\0'; 394275970Scy } 395275970Scy else 396275970Scy badtime(); 397275970Scy 398275970Scy unsetenv("TZ"); /* OUR timezone */ 399275970Scy lt = localtime(&now); /* current time val */ 400275970Scy 401275970Scy switch(strlen(timearg)) { 402275970Scy case 10: 403275970Scy lt->tm_year = ATOI2(timearg); 404275970Scy /* FALLTHROUGH */ 405275970Scy case 8: 406275970Scy lt->tm_mon = ATOI2(timearg); 407275970Scy if (--lt->tm_mon < 0 || lt->tm_mon > 11) 408275970Scy badtime(); 409275970Scy /* FALLTHROUGH */ 410275970Scy case 6: 411275970Scy lt->tm_mday = ATOI2(timearg); 412275970Scy if (lt->tm_mday < 1 || lt->tm_mday > 31) 413275970Scy badtime(); 414275970Scy /* FALLTHROUGH */ 415275970Scy case 4: 416275970Scy lt->tm_hour = ATOI2(timearg); 417275970Scy if (lt->tm_hour < 0 || lt->tm_hour > 23) 418275970Scy badtime(); 419275970Scy lt->tm_min = ATOI2(timearg); 420275970Scy if (lt->tm_min < 0 || lt->tm_min > 59) 421275970Scy badtime(); 422275970Scy lt->tm_sec = 0; 423289997Sglebius if ((shuttime = mktime(lt)) == -1) 424275970Scy badtime(); 425275970Scy if ((offset = shuttime - now) < 0) { 426275970Scy (void)fprintf(stderr, 427275970Scy "shutdown: that time is already past.\n"); 428275970Scy exit(1); 429275970Scy } 430275970Scy break; 431275970Scy default: 432275970Scy badtime(); 433275970Scy } 434275970Scy} 435275970Scy 436275970Scy#define FSMSG "fastboot file for fsck\n" 437275970Scyvoid 438275970Scydoitfast() 439275970Scy{ 440275970Scy int fastfd; 441275970Scy 442275970Scy if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC, 443275970Scy 0664)) >= 0) { 444275970Scy (void)write(fastfd, FSMSG, sizeof(FSMSG) - 1); 445275970Scy (void)close(fastfd); 446275970Scy } 447275970Scy} 448275970Scy 449275970Scy#define NOMSG "\n\nNO LOGINS: System going down at " 450275970Scyvoid 451275970Scynolog() 452275970Scy{ 453275970Scy int logfd; 454275970Scy char *ct; 455275970Scy 456275970Scy (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ 457275970Scy (void)signal(SIGINT, finish); 458275970Scy (void)signal(SIGHUP, finish); 459275970Scy (void)signal(SIGQUIT, finish); 460275970Scy (void)signal(SIGTERM, finish); 461275970Scy if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 462275970Scy 0664)) >= 0) { 463275970Scy (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); 464275970Scy ct = ctime(&shuttime); 465275970Scy (void)write(logfd, ct + 11, 5); 466275970Scy (void)write(logfd, "\n\n", 2); 467275970Scy (void)write(logfd, mbuf, strlen(mbuf)); 468275970Scy (void)close(logfd); 469275970Scy } 470275970Scy} 471275970Scy 472275970Scyvoid 473275970Scyfinish(signo) 474275970Scy int signo; 475275970Scy{ 476275970Scy (void)unlink(_PATH_NOLOGIN); 477275970Scy exit(0); 478275970Scy} 479275970Scy 480275970Scyvoid 481275970Scybadtime() 482275970Scy{ 483275970Scy (void)fprintf(stderr, "shutdown: bad time format.\n"); 484275970Scy exit(1); 485275970Scy} 486275970Scy 487275970Scyvoid 488275970Scyusage() 489275970Scy{ 490275970Scy fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n"); 491275970Scy exit(1); 492275970Scy} 493275970Scy