xref: /csrg-svn/sbin/shutdown/shutdown.c (revision 38990)
121179Sdist /*
235528Sbostic  * Copyright (c) 1988 Regents of the University of California.
335528Sbostic  * All rights reserved.
435528Sbostic  *
535528Sbostic  * Redistribution and use in source and binary forms are permitted
635528Sbostic  * provided that the above copyright notice and this paragraph are
735528Sbostic  * duplicated in all such forms and that any documentation,
835528Sbostic  * advertising materials, and other materials related to such
935528Sbostic  * distribution and use acknowledge that the software was developed
1035528Sbostic  * by the University of California, Berkeley.  The name of the
1135528Sbostic  * University may not be used to endorse or promote products derived
1235528Sbostic  * from this software without specific prior written permission.
1335528Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1435528Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1535528Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1621179Sdist  */
1721179Sdist 
1812682Ssam #ifndef lint
1921179Sdist char copyright[] =
2035528Sbostic "@(#) Copyright (c) 1988 Regents of the University of California.\n\
2121179Sdist  All rights reserved.\n";
2235528Sbostic #endif /* not lint */
232373Swnj 
2421179Sdist #ifndef lint
25*38990Sbostic static char sccsid[] = "@(#)shutdown.c	5.11 (Berkeley) 09/05/89";
2635528Sbostic #endif /* not lint */
2721179Sdist 
2835528Sbostic #include <sys/param.h>
2935528Sbostic #include <sys/time.h>
3035528Sbostic #include <sys/file.h>
3135528Sbostic #include <sys/resource.h>
3235528Sbostic #include <sys/syslog.h>
332373Swnj #include <signal.h>
3415920Skarels #include <setjmp.h>
3535528Sbostic #include <tzfile.h>
3628802Skarels #include <pwd.h>
3735528Sbostic #include <stdio.h>
3835528Sbostic #include <ctype.h>
3937300Sbostic #include "pathnames.h"
4028024Seric 
4135528Sbostic #ifdef DEBUG
4237300Sbostic #undef _PATH_NOLOGIN
4337300Sbostic #define	_PATH_NOLOGIN	"./nologin"
4437300Sbostic #undef _PATH_FASTBOOT
4537300Sbostic #define	_PATH_FASTBOOT	"./fastboot"
462373Swnj #endif
478841Smckusick 
4835528Sbostic #define	H		*60*60
4935528Sbostic #define	M		*60
5035528Sbostic #define	S		*1
5135528Sbostic #define	NOLOG_TIME	5*60
522373Swnj struct interval {
5335528Sbostic 	int timeleft, timetowait;
5435528Sbostic } tlist[] = {
5535528Sbostic 	10 H,  5 H,	 5 H,  3 H,	 2 H,  1 H,	1 H, 30 M,
5635528Sbostic 	30 M, 10 M,	20 M, 10 M,	10 M,  5 M,	5 M,  3 M,
5735528Sbostic 	 2 M,  1 M,	 1 M, 30 S,	30 S, 30 S,
5835528Sbostic 	 0, 0,
5935528Sbostic }, *tp = tlist;
6035528Sbostic #undef H
6135528Sbostic #undef M
6235528Sbostic #undef S
638841Smckusick 
6435528Sbostic static time_t offset, shuttime;
6535528Sbostic static int dofast, dohalt, doreboot, killflg, mbuflen;
6635528Sbostic static char *nosync, *whom, mbuf[BUFSIZ];
678841Smckusick 
6835528Sbostic main(argc, argv)
692373Swnj 	int argc;
702373Swnj 	char **argv;
712373Swnj {
7235528Sbostic 	extern int optind;
7336433Sbostic 	register char *p, *endp;
7436433Sbostic 	int arglen, ch, len, readstdin;
7528802Skarels 	struct passwd *pw, *getpwuid();
7635528Sbostic 	char *strcat(), *getlogin();
7735528Sbostic 	uid_t geteuid();
782373Swnj 
7935528Sbostic #ifndef DEBUG
8035528Sbostic 	if (geteuid()) {
8135528Sbostic 		fprintf(stderr, "shutdown: NOT super-user\n");
8235528Sbostic 		exit(1);
8335528Sbostic 	}
8435528Sbostic #endif
8536433Sbostic 	nosync = NULL;
8636433Sbostic 	readstdin = 0;
8736433Sbostic 	while ((ch = getopt(argc, argv, "-fhknr")) != EOF)
8835528Sbostic 		switch((char)ch) {
8936433Sbostic 		case '-':
9036433Sbostic 			readstdin = 1;
9136433Sbostic 			break;
9235528Sbostic 		case 'f':
9335528Sbostic 			dofast = 1;
9435528Sbostic 			break;
9535528Sbostic 		case 'h':
9635528Sbostic 			dohalt = 1;
9735528Sbostic 			break;
982373Swnj 		case 'k':
9935528Sbostic 			killflg = 1;
10035528Sbostic 			break;
10125366Sbloom 		case 'n':
10235528Sbostic 			nosync = "-n";
10335528Sbostic 			break;
1042373Swnj 		case 'r':
10525408Sbloom 			doreboot = 1;
10635528Sbostic 			break;
10735528Sbostic 		case '?':
1082373Swnj 		default:
10935528Sbostic 			usage();
1102373Swnj 		}
11135528Sbostic 	argc -= optind;
11235528Sbostic 	argv += optind;
11335528Sbostic 
11435528Sbostic 	if (argc < 1)
11535528Sbostic 		usage();
11635528Sbostic 
11736433Sbostic 	if (dofast && nosync) {
11835528Sbostic 		fprintf(stderr,
11935528Sbostic 		    "shutdown: incompatible switches -f and -n.\n");
12035528Sbostic 		usage();
1212373Swnj 	}
12235528Sbostic 	if (doreboot && dohalt) {
12335528Sbostic 		fprintf(stderr,
12435528Sbostic 		    "shutdown: incompatible switches -h and -r.\n");
12535528Sbostic 		usage();
1262373Swnj 	}
12735528Sbostic 	getoffset(*argv++);
12836433Sbostic 
12935528Sbostic 	if (*argv) {
13036433Sbostic 		for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) {
13136433Sbostic 			arglen = strlen(*argv);
13236433Sbostic 			if ((len -= arglen) <= 2)
13336433Sbostic 				break;
13436433Sbostic 			if (p != mbuf)
13536433Sbostic 				*p++ = ' ';
13636433Sbostic 			bcopy(*argv, p, arglen);
13736433Sbostic 			p += arglen;
13836433Sbostic 		}
13936433Sbostic 		*p = '\n';
14036433Sbostic 		*++p = '\0';
14125366Sbloom 	}
14236433Sbostic 
14336433Sbostic 	if (readstdin) {
14436433Sbostic 		p = mbuf;
14536433Sbostic 		endp = mbuf + sizeof(mbuf) - 2;
14636433Sbostic 		for (;;) {
14736433Sbostic 			if (!fgets(p, endp - p + 1, stdin))
14836433Sbostic 				break;
14936433Sbostic 			for (; *p &&  p < endp; ++p);
15036433Sbostic 			if (p == endp) {
15136433Sbostic 				*p = '\n';
15236433Sbostic 				*++p = '\0';
15336433Sbostic 				break;
15436433Sbostic 			}
15536433Sbostic 		}
1563648Stoy 	}
15735528Sbostic 	mbuflen = strlen(mbuf);
15835528Sbostic 
15935528Sbostic 	if (offset)
160*38990Sbostic 		printf("Shutdown at %.24s.\n", ctime(&shuttime));
16135528Sbostic 	else
16235528Sbostic 		printf("Shutdown NOW!\n");
16335528Sbostic 
16435528Sbostic 	if (!(whom = getlogin()))
16535528Sbostic 		whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
16635528Sbostic 
16735528Sbostic #ifdef DEBUG
16835528Sbostic 	(void)putc('\n', stdout);
16935528Sbostic #else
17035528Sbostic 	(void)setpriority(PRIO_PROCESS, 0, PRIO_MIN);
17135528Sbostic 	{
17235528Sbostic 		int forkpid;
17335528Sbostic 
17435528Sbostic 		forkpid = fork();
17535528Sbostic 		if (forkpid == -1) {
17635528Sbostic 			perror("shutdown: fork");
17735528Sbostic 			exit(1);
17835528Sbostic 		}
17935528Sbostic 		if (forkpid) {
18035528Sbostic 			printf("shutdown: [pid %d]\n", forkpid);
18135528Sbostic 			exit(0);
18235528Sbostic 		}
18328024Seric 	}
1842373Swnj #endif
18535528Sbostic 	openlog("shutdown", LOG_CONS, LOG_AUTH);
18635528Sbostic 	loop();
18735528Sbostic 	/*NOTREACHED*/
18835528Sbostic }
18935528Sbostic 
19035528Sbostic loop()
19135528Sbostic {
19235528Sbostic 	u_int sltime;
19335528Sbostic 	int logged;
19435528Sbostic 
19535528Sbostic 	if (offset <= NOLOG_TIME) {
19635528Sbostic 		logged = 1;
19735528Sbostic 		nolog();
1982373Swnj 	}
19935528Sbostic 	else
20035528Sbostic 		logged = 0;
20135528Sbostic 	tp = tlist;
20235528Sbostic 	if (tp->timeleft < offset)
20335528Sbostic 		(void)sleep((u_int)(offset - tp->timeleft));
20435528Sbostic 	else {
20535528Sbostic 		while (offset < tp->timeleft)
20635528Sbostic 			++tp;
20735528Sbostic 		/*
20835528Sbostic 		 * warn now, if going to sleep more than a fifth of
20935528Sbostic 		 * the next wait time.
21035528Sbostic 		 */
21135528Sbostic 		if (sltime = offset - tp->timeleft) {
21235528Sbostic 			if (sltime > tp->timetowait / 5)
21335528Sbostic 				warn();
21435528Sbostic 			(void)sleep(sltime);
21535528Sbostic 		}
2168841Smckusick 	}
21735528Sbostic 	for (;; ++tp) {
21835528Sbostic 		warn();
21935528Sbostic 		if (!logged && tp->timeleft <= NOLOG_TIME) {
22035528Sbostic 			logged = 1;
22135528Sbostic 			nolog();
2222373Swnj 		}
22335528Sbostic 		(void)sleep((u_int)tp->timetowait);
22435528Sbostic 		if (!tp->timeleft)
22535528Sbostic 			break;
22635528Sbostic 	}
22735528Sbostic 	die_you_gravy_sucking_pig_dog();
22835528Sbostic }
22925366Sbloom 
23035528Sbostic static jmp_buf alarmbuf;
23135528Sbostic 
23235528Sbostic warn()
23335528Sbostic {
23435528Sbostic 	static int first;
23535528Sbostic 	static char hostname[MAXHOSTNAMELEN];
23635528Sbostic 	FILE *pf;
23735528Sbostic 	char *ctime();
23835528Sbostic 	int timeout();
23935528Sbostic 
24035528Sbostic 	if (!first++) {
24135528Sbostic 		(void)signal(SIGALRM, timeout);
24235528Sbostic 		(void)gethostname(hostname, sizeof(hostname));
2432373Swnj 	}
24435528Sbostic 
24537300Sbostic 	if (!(pf = popen(_PATH_WALL, "w"))) {
24637300Sbostic 		syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL);
24735528Sbostic 		return;
24835528Sbostic 	}
24935528Sbostic 
25035528Sbostic 	fprintf(pf, "*** %sSystem shutdown message ***\n",
25135528Sbostic 	    tp->timeleft ? "": "FINAL ");
25235528Sbostic 
25335528Sbostic 	if (tp->timeleft > 10*60)
25435528Sbostic 		fprintf(pf, "System going down at %5.5s\n\n",
25535528Sbostic 		    ctime(&shuttime) + 11);
25635528Sbostic 	else if (tp->timeleft > 59)
25735528Sbostic 		fprintf(pf, "System going down in %d minute%s\n\n",
25835528Sbostic 		    tp->timeleft / 60, (tp->timeleft > 60) ? "s" : "");
25935528Sbostic 	else if (tp->timeleft)
26035528Sbostic 		fprintf(pf, "System going down in 30 seconds\n\n");
26135528Sbostic 	else
26235528Sbostic 		fprintf(pf, "System going down IMMEDIATELY\n\n");
26335528Sbostic 
26436433Sbostic 	if (mbuflen)
26536433Sbostic 		(void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf);
26635528Sbostic 
26735528Sbostic 	/*
26835528Sbostic 	 * play some games, just in case wall doesn't come back
26935528Sbostic 	 * probably unecessary, given that wall is careful.
27035528Sbostic 	 */
27135528Sbostic 	if (!setjmp(alarmbuf)) {
27235528Sbostic 		(void)alarm((u_int)30);
27335528Sbostic 		(void)pclose(pf);
27435528Sbostic 		(void)alarm((u_int)0);
27535528Sbostic 	}
2762373Swnj }
2772373Swnj 
27835528Sbostic timeout()
2792373Swnj {
28035528Sbostic 	longjmp(alarmbuf, 1);
28135528Sbostic }
2822373Swnj 
28335528Sbostic die_you_gravy_sucking_pig_dog()
28435528Sbostic {
28535528Sbostic 	syslog(LOG_NOTICE, "%s by %s: %s",
28635528Sbostic 	    doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf);
28735528Sbostic 	(void)sleep(2);
28835528Sbostic 
28935528Sbostic 	printf("\r\nSystem shutdown time has arrived\007\007\r\n");
29035528Sbostic 	if (killflg) {
29135528Sbostic 		printf("\rbut you'll have to do it yourself\r\n");
2922373Swnj 		finish();
2932373Swnj 	}
29435528Sbostic 	if (dofast)
29535528Sbostic 		doitfast();
29635528Sbostic #ifdef DEBUG
29735528Sbostic 	if (doreboot)
29836433Sbostic 		printf("reboot");
29936433Sbostic 	else if (dohalt)
30036433Sbostic 		printf("halt");
30136433Sbostic 	if (nosync)
30236433Sbostic 		printf(" no sync");
30335528Sbostic 	if (dofast)
30436433Sbostic 		printf(" no fsck");
30536433Sbostic 	printf("\nkill -HUP 1\n");
30635528Sbostic #else
30735528Sbostic 	if (doreboot) {
30837300Sbostic 		execle(_PATH_REBOOT, "reboot", "-l", nosync, 0);
30937300Sbostic 		syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT);
31035528Sbostic 		perror("shutdown");
31135528Sbostic 	}
31235528Sbostic 	else if (dohalt) {
31337300Sbostic 		execle(_PATH_HALT, "halt", "-l", nosync, 0);
31437300Sbostic 		syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT);
31535528Sbostic 		perror("shutdown");
31635528Sbostic 	}
31735528Sbostic 	(void)kill(1, SIGTERM);		/* to single user */
31835528Sbostic #endif
3192373Swnj 	finish();
3202373Swnj }
3212373Swnj 
32235528Sbostic #define	ATOI2(p)	(p[0] - '0') * 10 + (p[1] - '0'); p += 2;
32335528Sbostic static int dmsize[] =
32435528Sbostic 	{ -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
32535528Sbostic 
32635528Sbostic getoffset(timearg)
32735528Sbostic 	register char *timearg;
3282373Swnj {
32935528Sbostic 	register struct tm *lt;
33035528Sbostic 	register char *p;
33135528Sbostic 	time_t now, time();
33235528Sbostic 	int year, month, day, hour, min;
3332373Swnj 
33435528Sbostic 	if (!strcasecmp(timearg, "now")) {		/* now */
33535528Sbostic 		offset = 0;
33635528Sbostic 		return;
33735528Sbostic 	}
3383880Swnj 
33935528Sbostic 	(void)time(&now);
34035528Sbostic 	if (*timearg == '+') {				/* +minutes */
34135528Sbostic 		if (!isdigit(*++timearg))
34235528Sbostic 			goto badtime;
34335528Sbostic 		min = atoi(timearg);
34435528Sbostic 		offset = min * 60;
34535528Sbostic 		shuttime = now + offset;
34635528Sbostic 		return;
34735528Sbostic 	}
3488841Smckusick 
34935528Sbostic 	/* handle hh:mm by getting rid of the colon */
35035528Sbostic 	for (p = timearg; *p; ++p)
35135528Sbostic 		if (!isascii(*p) || !isdigit(*p))
35235528Sbostic 			if (*p == ':' && strlen(p) == 3) {
35335528Sbostic 				p[0] = p[1];
35435528Sbostic 				p[1] = p[2];
35535528Sbostic 				p[2] = '\0';
35635528Sbostic 			}
35735528Sbostic 			else
35835528Sbostic 				goto badtime;
35935528Sbostic 
36035528Sbostic 	unsetenv("TZ");					/* OUR timezone */
36135528Sbostic 	lt = localtime(&now);				/* [yymmdd]hhmm */
36235528Sbostic 	year = lt->tm_year;
36335528Sbostic 	month = lt->tm_mon + 1;
36435528Sbostic 	day = lt->tm_mday;
36535528Sbostic 
36635528Sbostic 	switch(strlen(timearg)) {
36735528Sbostic 	case 10:
36835528Sbostic 		year = ATOI2(timearg);
36935528Sbostic 		/* FALLTHROUGH */
37035528Sbostic 	case 8:
37135528Sbostic 		month = ATOI2(timearg);
37235528Sbostic 		/* FALLTHROUGH */
37335528Sbostic 	case 6:
37435528Sbostic 		day = ATOI2(timearg);
37535528Sbostic 		/* FALLTHROUGH */
37635528Sbostic 	case 4:
37735528Sbostic 		hour = ATOI2(timearg);
37835528Sbostic 		min = ATOI2(timearg);
37935528Sbostic 		if (month < 1 || month > 12 || day < 1 || day > 31 ||
38035528Sbostic 		    hour < 0 || hour > 23 || min < 0 || min > 59)
38135528Sbostic 			goto badtime;
38235528Sbostic 		shuttime = 0;
38335528Sbostic 		year += TM_YEAR_BASE;
38435528Sbostic 		if (isleap(year) && month > 2)
38535528Sbostic 			++shuttime;
38635528Sbostic 		for (--year; year >= EPOCH_YEAR; --year)
38735528Sbostic 			shuttime += isleap(year) ?
38837300Sbostic 			    DAYSPERLYEAR : DAYSPERNYEAR;
38935528Sbostic 		while (--month)
39035528Sbostic 			shuttime += dmsize[month];
39135528Sbostic 		shuttime += day - 1;
39237300Sbostic 		shuttime = HOURSPERDAY * shuttime + hour;
39337300Sbostic 		shuttime = MINSPERHOUR * shuttime + min;
39437300Sbostic 		shuttime *= SECSPERMIN;
39535528Sbostic 		shuttime -= lt->tm_gmtoff;
39635528Sbostic 		if ((offset = shuttime - now) >= 0)
39735528Sbostic 			break;
39835528Sbostic 		/* FALLTHROUGH */
39935528Sbostic 	default:
40035528Sbostic badtime:	fprintf(stderr,
40135528Sbostic 		    "shutdown: bad time format, or already past.\n");
40235528Sbostic 		exit(1);
40335528Sbostic 	}
4042373Swnj }
4052373Swnj 
40635528Sbostic #define	FSMSG	"fastboot file for fsck\n"
40725366Sbloom doitfast()
40825366Sbloom {
40935528Sbostic 	int fastfd;
41025366Sbloom 
41137300Sbostic 	if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC,
41237300Sbostic 	    0664)) >= 0) {
41335528Sbostic 		(void)write(fastfd, FSMSG, sizeof(FSMSG) - 1);
41435528Sbostic 		(void)close(fastfd);
41525366Sbloom 	}
41625366Sbloom }
41725366Sbloom 
41835528Sbostic #define	NOMSG	"\n\nNO LOGINS: System going down at "
41935528Sbostic nolog()
4202373Swnj {
42135528Sbostic 	int logfd, finish();
42235528Sbostic 	char *ct, *ctime();
4232373Swnj 
42437300Sbostic 	(void)unlink(_PATH_NOLOGIN);	/* in case linked to another file */
42535528Sbostic 	(void)signal(SIGINT, finish);
42635528Sbostic 	(void)signal(SIGHUP, finish);
42735528Sbostic 	(void)signal(SIGQUIT, finish);
42835528Sbostic 	(void)signal(SIGTERM, finish);
42937300Sbostic 	if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC,
43037300Sbostic 	    0664)) >= 0) {
43135528Sbostic 		(void)write(logfd, NOMSG, sizeof(NOMSG) - 1);
43235528Sbostic 		ct = ctime(&shuttime);
43335528Sbostic 		(void)write(logfd, ct + 11, 5);
43435528Sbostic 		(void)write(logfd, "\n\n", 2);
43535528Sbostic 		(void)write(logfd, mbuf, strlen(mbuf));
43635528Sbostic 		(void)close(logfd);
4372373Swnj 	}
4382373Swnj }
4392373Swnj 
4402373Swnj finish()
4412373Swnj {
44237300Sbostic 	(void)unlink(_PATH_NOLOGIN);
4432373Swnj 	exit(0);
4442373Swnj }
4452373Swnj 
44635528Sbostic usage()
4472373Swnj {
44835528Sbostic 	fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n");
44935528Sbostic 	exit(1);
4502373Swnj }
451