xref: /csrg-svn/sbin/shutdown/shutdown.c (revision 42712)
121179Sdist /*
240224Skarels  * Copyright (c) 1988, 1990 Regents of the University of California.
335528Sbostic  * All rights reserved.
435528Sbostic  *
5*42712Sbostic  * %sccs.include.redist.c%
621179Sdist  */
721179Sdist 
812682Ssam #ifndef lint
921179Sdist char copyright[] =
1035528Sbostic "@(#) Copyright (c) 1988 Regents of the University of California.\n\
1121179Sdist  All rights reserved.\n";
1235528Sbostic #endif /* not lint */
132373Swnj 
1421179Sdist #ifndef lint
15*42712Sbostic static char sccsid[] = "@(#)shutdown.c	5.14 (Berkeley) 06/01/90";
1635528Sbostic #endif /* not lint */
1721179Sdist 
1835528Sbostic #include <sys/param.h>
1935528Sbostic #include <sys/time.h>
2035528Sbostic #include <sys/file.h>
2135528Sbostic #include <sys/resource.h>
2235528Sbostic #include <sys/syslog.h>
232373Swnj #include <signal.h>
2415920Skarels #include <setjmp.h>
2535528Sbostic #include <tzfile.h>
2628802Skarels #include <pwd.h>
2735528Sbostic #include <stdio.h>
2835528Sbostic #include <ctype.h>
2937300Sbostic #include "pathnames.h"
3028024Seric 
3135528Sbostic #ifdef DEBUG
3237300Sbostic #undef _PATH_NOLOGIN
3337300Sbostic #define	_PATH_NOLOGIN	"./nologin"
3437300Sbostic #undef _PATH_FASTBOOT
3537300Sbostic #define	_PATH_FASTBOOT	"./fastboot"
362373Swnj #endif
378841Smckusick 
3835528Sbostic #define	H		*60*60
3935528Sbostic #define	M		*60
4035528Sbostic #define	S		*1
4135528Sbostic #define	NOLOG_TIME	5*60
422373Swnj struct interval {
4335528Sbostic 	int timeleft, timetowait;
4435528Sbostic } tlist[] = {
4535528Sbostic 	10 H,  5 H,	 5 H,  3 H,	 2 H,  1 H,	1 H, 30 M,
4635528Sbostic 	30 M, 10 M,	20 M, 10 M,	10 M,  5 M,	5 M,  3 M,
4735528Sbostic 	 2 M,  1 M,	 1 M, 30 S,	30 S, 30 S,
4835528Sbostic 	 0, 0,
4935528Sbostic }, *tp = tlist;
5035528Sbostic #undef H
5135528Sbostic #undef M
5235528Sbostic #undef S
538841Smckusick 
5435528Sbostic static time_t offset, shuttime;
5535528Sbostic static int dofast, dohalt, doreboot, killflg, mbuflen;
5635528Sbostic static char *nosync, *whom, mbuf[BUFSIZ];
578841Smckusick 
5835528Sbostic main(argc, argv)
592373Swnj 	int argc;
602373Swnj 	char **argv;
612373Swnj {
6235528Sbostic 	extern int optind;
6336433Sbostic 	register char *p, *endp;
6436433Sbostic 	int arglen, ch, len, readstdin;
6528802Skarels 	struct passwd *pw, *getpwuid();
6635528Sbostic 	char *strcat(), *getlogin();
6735528Sbostic 	uid_t geteuid();
682373Swnj 
6935528Sbostic #ifndef DEBUG
7035528Sbostic 	if (geteuid()) {
7135528Sbostic 		fprintf(stderr, "shutdown: NOT super-user\n");
7235528Sbostic 		exit(1);
7335528Sbostic 	}
7435528Sbostic #endif
7536433Sbostic 	nosync = NULL;
7636433Sbostic 	readstdin = 0;
7736433Sbostic 	while ((ch = getopt(argc, argv, "-fhknr")) != EOF)
7840224Skarels 		switch (ch) {
7936433Sbostic 		case '-':
8036433Sbostic 			readstdin = 1;
8136433Sbostic 			break;
8235528Sbostic 		case 'f':
8335528Sbostic 			dofast = 1;
8435528Sbostic 			break;
8535528Sbostic 		case 'h':
8635528Sbostic 			dohalt = 1;
8735528Sbostic 			break;
882373Swnj 		case 'k':
8935528Sbostic 			killflg = 1;
9035528Sbostic 			break;
9125366Sbloom 		case 'n':
9235528Sbostic 			nosync = "-n";
9335528Sbostic 			break;
942373Swnj 		case 'r':
9525408Sbloom 			doreboot = 1;
9635528Sbostic 			break;
9735528Sbostic 		case '?':
982373Swnj 		default:
9935528Sbostic 			usage();
1002373Swnj 		}
10135528Sbostic 	argc -= optind;
10235528Sbostic 	argv += optind;
10335528Sbostic 
10435528Sbostic 	if (argc < 1)
10535528Sbostic 		usage();
10635528Sbostic 
10736433Sbostic 	if (dofast && nosync) {
10835528Sbostic 		fprintf(stderr,
10935528Sbostic 		    "shutdown: incompatible switches -f and -n.\n");
11035528Sbostic 		usage();
1112373Swnj 	}
11235528Sbostic 	if (doreboot && dohalt) {
11335528Sbostic 		fprintf(stderr,
11435528Sbostic 		    "shutdown: incompatible switches -h and -r.\n");
11535528Sbostic 		usage();
1162373Swnj 	}
11735528Sbostic 	getoffset(*argv++);
11836433Sbostic 
11935528Sbostic 	if (*argv) {
12036433Sbostic 		for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) {
12136433Sbostic 			arglen = strlen(*argv);
12236433Sbostic 			if ((len -= arglen) <= 2)
12336433Sbostic 				break;
12436433Sbostic 			if (p != mbuf)
12536433Sbostic 				*p++ = ' ';
12636433Sbostic 			bcopy(*argv, p, arglen);
12736433Sbostic 			p += arglen;
12836433Sbostic 		}
12936433Sbostic 		*p = '\n';
13036433Sbostic 		*++p = '\0';
13125366Sbloom 	}
13236433Sbostic 
13336433Sbostic 	if (readstdin) {
13436433Sbostic 		p = mbuf;
13536433Sbostic 		endp = mbuf + sizeof(mbuf) - 2;
13636433Sbostic 		for (;;) {
13736433Sbostic 			if (!fgets(p, endp - p + 1, stdin))
13836433Sbostic 				break;
13936433Sbostic 			for (; *p &&  p < endp; ++p);
14036433Sbostic 			if (p == endp) {
14136433Sbostic 				*p = '\n';
14236433Sbostic 				*++p = '\0';
14336433Sbostic 				break;
14436433Sbostic 			}
14536433Sbostic 		}
1463648Stoy 	}
14735528Sbostic 	mbuflen = strlen(mbuf);
14835528Sbostic 
14935528Sbostic 	if (offset)
15038990Sbostic 		printf("Shutdown at %.24s.\n", ctime(&shuttime));
15135528Sbostic 	else
15235528Sbostic 		printf("Shutdown NOW!\n");
15335528Sbostic 
15435528Sbostic 	if (!(whom = getlogin()))
15535528Sbostic 		whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
15635528Sbostic 
15735528Sbostic #ifdef DEBUG
15835528Sbostic 	(void)putc('\n', stdout);
15935528Sbostic #else
16035528Sbostic 	(void)setpriority(PRIO_PROCESS, 0, PRIO_MIN);
16135528Sbostic 	{
16235528Sbostic 		int forkpid;
16335528Sbostic 
16435528Sbostic 		forkpid = fork();
16535528Sbostic 		if (forkpid == -1) {
16635528Sbostic 			perror("shutdown: fork");
16735528Sbostic 			exit(1);
16835528Sbostic 		}
16935528Sbostic 		if (forkpid) {
17035528Sbostic 			printf("shutdown: [pid %d]\n", forkpid);
17135528Sbostic 			exit(0);
17235528Sbostic 		}
17328024Seric 	}
1742373Swnj #endif
17535528Sbostic 	openlog("shutdown", LOG_CONS, LOG_AUTH);
17635528Sbostic 	loop();
17735528Sbostic 	/*NOTREACHED*/
17835528Sbostic }
17935528Sbostic 
18035528Sbostic loop()
18135528Sbostic {
18235528Sbostic 	u_int sltime;
18335528Sbostic 	int logged;
18435528Sbostic 
18535528Sbostic 	if (offset <= NOLOG_TIME) {
18635528Sbostic 		logged = 1;
18735528Sbostic 		nolog();
1882373Swnj 	}
18935528Sbostic 	else
19035528Sbostic 		logged = 0;
19135528Sbostic 	tp = tlist;
19235528Sbostic 	if (tp->timeleft < offset)
19335528Sbostic 		(void)sleep((u_int)(offset - tp->timeleft));
19435528Sbostic 	else {
19535528Sbostic 		while (offset < tp->timeleft)
19635528Sbostic 			++tp;
19735528Sbostic 		/*
19835528Sbostic 		 * warn now, if going to sleep more than a fifth of
19935528Sbostic 		 * the next wait time.
20035528Sbostic 		 */
20135528Sbostic 		if (sltime = offset - tp->timeleft) {
20235528Sbostic 			if (sltime > tp->timetowait / 5)
20335528Sbostic 				warn();
20435528Sbostic 			(void)sleep(sltime);
20535528Sbostic 		}
2068841Smckusick 	}
20735528Sbostic 	for (;; ++tp) {
20835528Sbostic 		warn();
20935528Sbostic 		if (!logged && tp->timeleft <= NOLOG_TIME) {
21035528Sbostic 			logged = 1;
21135528Sbostic 			nolog();
2122373Swnj 		}
21335528Sbostic 		(void)sleep((u_int)tp->timetowait);
21435528Sbostic 		if (!tp->timeleft)
21535528Sbostic 			break;
21635528Sbostic 	}
21735528Sbostic 	die_you_gravy_sucking_pig_dog();
21835528Sbostic }
21925366Sbloom 
22035528Sbostic static jmp_buf alarmbuf;
22135528Sbostic 
22235528Sbostic warn()
22335528Sbostic {
22435528Sbostic 	static int first;
22540224Skarels 	static char hostname[MAXHOSTNAMELEN + 1];
22640224Skarels 	char wcmd[MAXPATHLEN + 4];
22735528Sbostic 	FILE *pf;
22835528Sbostic 	char *ctime();
22935528Sbostic 	int timeout();
23035528Sbostic 
23139421Sbostic 	if (!first++)
23240224Skarels 		(void)gethostname(hostname, sizeof(hostname));
23335528Sbostic 
23440224Skarels 	/* undoc -n option to wall suppresses normal wall banner */
23540224Skarels 	(void) sprintf(wcmd, "%s -n", _PATH_WALL);
23640224Skarels 	if (!(pf = popen(wcmd, "w"))) {
23737300Sbostic 		syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL);
23835528Sbostic 		return;
23935528Sbostic 	}
24035528Sbostic 
24140224Skarels 	fprintf(pf, "\007*** %sSystem shutdown message from %s@%s ***\007\n",
24240224Skarels 	    tp->timeleft ? "": "FINAL ", whom, hostname);
24335528Sbostic 
24435528Sbostic 	if (tp->timeleft > 10*60)
24535528Sbostic 		fprintf(pf, "System going down at %5.5s\n\n",
24635528Sbostic 		    ctime(&shuttime) + 11);
24735528Sbostic 	else if (tp->timeleft > 59)
24835528Sbostic 		fprintf(pf, "System going down in %d minute%s\n\n",
24935528Sbostic 		    tp->timeleft / 60, (tp->timeleft > 60) ? "s" : "");
25035528Sbostic 	else if (tp->timeleft)
25135528Sbostic 		fprintf(pf, "System going down in 30 seconds\n\n");
25235528Sbostic 	else
25335528Sbostic 		fprintf(pf, "System going down IMMEDIATELY\n\n");
25435528Sbostic 
25536433Sbostic 	if (mbuflen)
25636433Sbostic 		(void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf);
25735528Sbostic 
25835528Sbostic 	/*
25935528Sbostic 	 * play some games, just in case wall doesn't come back
26035528Sbostic 	 * probably unecessary, given that wall is careful.
26135528Sbostic 	 */
26235528Sbostic 	if (!setjmp(alarmbuf)) {
26340224Skarels 		(void)signal(SIGALRM, timeout);
26435528Sbostic 		(void)alarm((u_int)30);
26535528Sbostic 		(void)pclose(pf);
26635528Sbostic 		(void)alarm((u_int)0);
26740224Skarels 		(void)signal(SIGALRM, SIG_DFL);
26835528Sbostic 	}
2692373Swnj }
2702373Swnj 
27135528Sbostic timeout()
2722373Swnj {
27335528Sbostic 	longjmp(alarmbuf, 1);
27435528Sbostic }
2752373Swnj 
27635528Sbostic die_you_gravy_sucking_pig_dog()
27735528Sbostic {
27835528Sbostic 	syslog(LOG_NOTICE, "%s by %s: %s",
27935528Sbostic 	    doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf);
28035528Sbostic 	(void)sleep(2);
28135528Sbostic 
28235528Sbostic 	printf("\r\nSystem shutdown time has arrived\007\007\r\n");
28335528Sbostic 	if (killflg) {
28435528Sbostic 		printf("\rbut you'll have to do it yourself\r\n");
2852373Swnj 		finish();
2862373Swnj 	}
28735528Sbostic 	if (dofast)
28835528Sbostic 		doitfast();
28935528Sbostic #ifdef DEBUG
29035528Sbostic 	if (doreboot)
29136433Sbostic 		printf("reboot");
29236433Sbostic 	else if (dohalt)
29336433Sbostic 		printf("halt");
29436433Sbostic 	if (nosync)
29536433Sbostic 		printf(" no sync");
29635528Sbostic 	if (dofast)
29736433Sbostic 		printf(" no fsck");
29836433Sbostic 	printf("\nkill -HUP 1\n");
29935528Sbostic #else
30035528Sbostic 	if (doreboot) {
30137300Sbostic 		execle(_PATH_REBOOT, "reboot", "-l", nosync, 0);
30237300Sbostic 		syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT);
30335528Sbostic 		perror("shutdown");
30435528Sbostic 	}
30535528Sbostic 	else if (dohalt) {
30637300Sbostic 		execle(_PATH_HALT, "halt", "-l", nosync, 0);
30737300Sbostic 		syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT);
30835528Sbostic 		perror("shutdown");
30935528Sbostic 	}
31035528Sbostic 	(void)kill(1, SIGTERM);		/* to single user */
31135528Sbostic #endif
3122373Swnj 	finish();
3132373Swnj }
3142373Swnj 
31535528Sbostic #define	ATOI2(p)	(p[0] - '0') * 10 + (p[1] - '0'); p += 2;
31635528Sbostic static int dmsize[] =
31735528Sbostic 	{ -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
31835528Sbostic 
31935528Sbostic getoffset(timearg)
32035528Sbostic 	register char *timearg;
3212373Swnj {
32235528Sbostic 	register struct tm *lt;
32335528Sbostic 	register char *p;
32435528Sbostic 	time_t now, time();
32535528Sbostic 	int year, month, day, hour, min;
3262373Swnj 
32735528Sbostic 	if (!strcasecmp(timearg, "now")) {		/* now */
32835528Sbostic 		offset = 0;
32935528Sbostic 		return;
33035528Sbostic 	}
3313880Swnj 
33235528Sbostic 	(void)time(&now);
33335528Sbostic 	if (*timearg == '+') {				/* +minutes */
33435528Sbostic 		if (!isdigit(*++timearg))
33535528Sbostic 			goto badtime;
33635528Sbostic 		min = atoi(timearg);
33735528Sbostic 		offset = min * 60;
33835528Sbostic 		shuttime = now + offset;
33935528Sbostic 		return;
34035528Sbostic 	}
3418841Smckusick 
34235528Sbostic 	/* handle hh:mm by getting rid of the colon */
34335528Sbostic 	for (p = timearg; *p; ++p)
34435528Sbostic 		if (!isascii(*p) || !isdigit(*p))
34535528Sbostic 			if (*p == ':' && strlen(p) == 3) {
34635528Sbostic 				p[0] = p[1];
34735528Sbostic 				p[1] = p[2];
34835528Sbostic 				p[2] = '\0';
34935528Sbostic 			}
35035528Sbostic 			else
35135528Sbostic 				goto badtime;
35235528Sbostic 
35335528Sbostic 	unsetenv("TZ");					/* OUR timezone */
35435528Sbostic 	lt = localtime(&now);				/* [yymmdd]hhmm */
35535528Sbostic 	year = lt->tm_year;
35635528Sbostic 	month = lt->tm_mon + 1;
35735528Sbostic 	day = lt->tm_mday;
35835528Sbostic 
35935528Sbostic 	switch(strlen(timearg)) {
36035528Sbostic 	case 10:
36135528Sbostic 		year = ATOI2(timearg);
36235528Sbostic 		/* FALLTHROUGH */
36335528Sbostic 	case 8:
36435528Sbostic 		month = ATOI2(timearg);
36535528Sbostic 		/* FALLTHROUGH */
36635528Sbostic 	case 6:
36735528Sbostic 		day = ATOI2(timearg);
36835528Sbostic 		/* FALLTHROUGH */
36935528Sbostic 	case 4:
37035528Sbostic 		hour = ATOI2(timearg);
37135528Sbostic 		min = ATOI2(timearg);
37235528Sbostic 		if (month < 1 || month > 12 || day < 1 || day > 31 ||
37335528Sbostic 		    hour < 0 || hour > 23 || min < 0 || min > 59)
37435528Sbostic 			goto badtime;
37535528Sbostic 		shuttime = 0;
37635528Sbostic 		year += TM_YEAR_BASE;
37735528Sbostic 		if (isleap(year) && month > 2)
37835528Sbostic 			++shuttime;
37935528Sbostic 		for (--year; year >= EPOCH_YEAR; --year)
38035528Sbostic 			shuttime += isleap(year) ?
38137300Sbostic 			    DAYSPERLYEAR : DAYSPERNYEAR;
38235528Sbostic 		while (--month)
38335528Sbostic 			shuttime += dmsize[month];
38435528Sbostic 		shuttime += day - 1;
38537300Sbostic 		shuttime = HOURSPERDAY * shuttime + hour;
38637300Sbostic 		shuttime = MINSPERHOUR * shuttime + min;
38737300Sbostic 		shuttime *= SECSPERMIN;
38835528Sbostic 		shuttime -= lt->tm_gmtoff;
38935528Sbostic 		if ((offset = shuttime - now) >= 0)
39035528Sbostic 			break;
39135528Sbostic 		/* FALLTHROUGH */
39235528Sbostic 	default:
39335528Sbostic badtime:	fprintf(stderr,
39435528Sbostic 		    "shutdown: bad time format, or already past.\n");
39535528Sbostic 		exit(1);
39635528Sbostic 	}
3972373Swnj }
3982373Swnj 
39935528Sbostic #define	FSMSG	"fastboot file for fsck\n"
40025366Sbloom doitfast()
40125366Sbloom {
40235528Sbostic 	int fastfd;
40325366Sbloom 
40437300Sbostic 	if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC,
40537300Sbostic 	    0664)) >= 0) {
40635528Sbostic 		(void)write(fastfd, FSMSG, sizeof(FSMSG) - 1);
40735528Sbostic 		(void)close(fastfd);
40825366Sbloom 	}
40925366Sbloom }
41025366Sbloom 
41135528Sbostic #define	NOMSG	"\n\nNO LOGINS: System going down at "
41235528Sbostic nolog()
4132373Swnj {
41435528Sbostic 	int logfd, finish();
41535528Sbostic 	char *ct, *ctime();
4162373Swnj 
41737300Sbostic 	(void)unlink(_PATH_NOLOGIN);	/* in case linked to another file */
41835528Sbostic 	(void)signal(SIGINT, finish);
41935528Sbostic 	(void)signal(SIGHUP, finish);
42035528Sbostic 	(void)signal(SIGQUIT, finish);
42135528Sbostic 	(void)signal(SIGTERM, finish);
42237300Sbostic 	if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC,
42337300Sbostic 	    0664)) >= 0) {
42435528Sbostic 		(void)write(logfd, NOMSG, sizeof(NOMSG) - 1);
42535528Sbostic 		ct = ctime(&shuttime);
42635528Sbostic 		(void)write(logfd, ct + 11, 5);
42735528Sbostic 		(void)write(logfd, "\n\n", 2);
42835528Sbostic 		(void)write(logfd, mbuf, strlen(mbuf));
42935528Sbostic 		(void)close(logfd);
4302373Swnj 	}
4312373Swnj }
4322373Swnj 
4332373Swnj finish()
4342373Swnj {
43537300Sbostic 	(void)unlink(_PATH_NOLOGIN);
4362373Swnj 	exit(0);
4372373Swnj }
4382373Swnj 
43935528Sbostic usage()
4402373Swnj {
44135528Sbostic 	fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n");
44235528Sbostic 	exit(1);
4432373Swnj }
444