shutdown.c revision 26737
11556Srgrimes/*
21556Srgrimes * Copyright (c) 1988, 1990, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * Redistribution and use in source and binary forms, with or without
61556Srgrimes * modification, are permitted provided that the following conditions
71556Srgrimes * are met:
81556Srgrimes * 1. Redistributions of source code must retain the above copyright
91556Srgrimes *    notice, this list of conditions and the following disclaimer.
101556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111556Srgrimes *    notice, this list of conditions and the following disclaimer in the
121556Srgrimes *    documentation and/or other materials provided with the distribution.
131556Srgrimes * 3. All advertising materials mentioning features or use of this software
141556Srgrimes *    must display the following acknowledgement:
151556Srgrimes *	This product includes software developed by the University of
161556Srgrimes *	California, Berkeley and its contributors.
171556Srgrimes * 4. Neither the name of the University nor the names of its contributors
181556Srgrimes *    may be used to endorse or promote products derived from this software
191556Srgrimes *    without specific prior written permission.
201556Srgrimes *
211556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311556Srgrimes * SUCH DAMAGE.
321556Srgrimes *
331556Srgrimes *	$Id$
3436150Scharnier */
3536150Scharnier
3636150Scharnier#ifndef lint
371556Srgrimesstatic char copyright[] =
3899110Sobrien"@(#) Copyright (c) 1988, 1990, 1993\n\
3999110Sobrien	The Regents of the University of California.  All rights reserved.\n";
401556Srgrimes#endif /* not lint */
4117987Speter
4217987Speter#ifndef lint
4317987Speterstatic char sccsid[] = "@(#)shutdown.c	8.2 (Berkeley) 2/16/94";
4417987Speter#endif /* not lint */
4517987Speter
4617987Speter#include <sys/param.h>
4717987Speter#include <sys/time.h>
481556Srgrimes#include <sys/resource.h>
491556Srgrimes#include <sys/syslog.h>
501556Srgrimes
511556Srgrimes#include <ctype.h>
521556Srgrimes#include <fcntl.h>
5317987Speter#include <pwd.h>
541556Srgrimes#include <setjmp.h>
551556Srgrimes#include <signal.h>
561556Srgrimes#include <stdio.h>
571556Srgrimes#include <stdlib.h>
581556Srgrimes#include <string.h>
591556Srgrimes#include <unistd.h>
601556Srgrimes#include <err.h>
611556Srgrimes
621556Srgrimes#include "pathnames.h"
63100588Stjr
641556Srgrimes#ifdef DEBUG
651556Srgrimes#undef _PATH_NOLOGIN
661556Srgrimes#define	_PATH_NOLOGIN	"./nologin"
671556Srgrimes#endif
681556Srgrimes
691556Srgrimes#define	H		*60*60
701556Srgrimes#define	M		*60
711556Srgrimes#define	S		*1
7212043Speter#define	NOLOG_TIME	5*60
731556Srgrimesstruct interval {
741556Srgrimes	int timeleft, timetowait;
751556Srgrimes} tlist[] = {
761556Srgrimes	10 H,  5 H,	 5 H,  3 H,	 2 H,  1 H,	1 H, 30 M,
771556Srgrimes	30 M, 10 M,	20 M, 10 M,	10 M,  5 M,	5 M,  3 M,
781556Srgrimes	 2 M,  1 M,	 1 M, 30 S,	30 S, 30 S,
791556Srgrimes	 0, 0,
801556Srgrimes};
811556Srgrimes#undef H
821556Srgrimes#undef M
831556Srgrimes#undef S
841556Srgrimes
851556Srgrimesstatic time_t offset, shuttime;
8620425Sstevestatic int dohalt, doreboot, killflg, mbuflen;
8720425Sstevestatic char *nosync, *whom, mbuf[BUFSIZ];
881556Srgrimes
891556Srgrimesvoid badtime __P((void));
901556Srgrimesvoid die_you_gravy_sucking_pig_dog __P((void));
911556Srgrimesvoid finish __P((int));
921556Srgrimesvoid getoffset __P((char *));
931556Srgrimesvoid loop __P((void));
941556Srgrimesvoid nolog __P((void));
951556Srgrimesvoid timeout __P((int));
961556Srgrimesvoid timewarn __P((int));
9712043Spetervoid usage __P((void));
981556Srgrimes
991556Srgrimesint
1001556Srgrimesmain(argc, argv)
101117261Sdds	int argc;
1021556Srgrimes	char *argv[];
1031556Srgrimes{
1041556Srgrimes	extern int optind;
1051556Srgrimes	register char *p, *endp;
1061556Srgrimes	struct passwd *pw;
10790111Simp	int arglen, ch, len, readstdin;
10890111Simp
1091556Srgrimes#ifndef DEBUG
1101556Srgrimes	if (geteuid())
1111556Srgrimes		errx(1, "NOT super-user");
1121556Srgrimes#endif
1131556Srgrimes	nosync = NULL;
1141556Srgrimes	readstdin = 0;
1151556Srgrimes	while ((ch = getopt(argc, argv, "-hknr")) != -1)
1161556Srgrimes		switch (ch) {
1171556Srgrimes		case '-':
1181556Srgrimes			readstdin = 1;
1191556Srgrimes			break;
1201556Srgrimes		case 'h':
121194406Sjilles			dohalt = 1;
1221556Srgrimes			break;
12312043Speter		case 'k':
1241556Srgrimes			killflg = 1;
1251556Srgrimes			break;
1261556Srgrimes		case 'n':
1271556Srgrimes			nosync = "-n";
1281556Srgrimes			break;
1291556Srgrimes		case 'r':
1301556Srgrimes			doreboot = 1;
1311556Srgrimes			break;
1321556Srgrimes		case '?':
1331556Srgrimes		default:
1341556Srgrimes			usage();
1351556Srgrimes		}
1361556Srgrimes	argc -= optind;
13790111Simp	argv += optind;
13817987Speter
13925225Ssteve	if (argc < 1)
1401556Srgrimes		usage();
1411556Srgrimes
1421556Srgrimes	if (doreboot && dohalt) {
1431556Srgrimes		warnx("incompatible switches -h and -r.");
1441556Srgrimes		usage();
1451556Srgrimes	}
1461556Srgrimes	getoffset(*argv++);
1471556Srgrimes
1481556Srgrimes	if (*argv) {
1491556Srgrimes		for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) {
1501556Srgrimes			arglen = strlen(*argv);
1511556Srgrimes			if ((len -= arglen) <= 2)
1521556Srgrimes				break;
1531556Srgrimes			if (p != mbuf)
1541556Srgrimes				*p++ = ' ';
1551556Srgrimes			bcopy(*argv, p, arglen);
1561556Srgrimes			p += arglen;
1571556Srgrimes		}
1581556Srgrimes		*p = '\n';
1591556Srgrimes		*++p = '\0';
1601556Srgrimes	}
1611556Srgrimes
1621556Srgrimes	if (readstdin) {
1631556Srgrimes		p = mbuf;
1641556Srgrimes		endp = mbuf + sizeof(mbuf) - 2;
1651556Srgrimes		for (;;) {
16690111Simp			if (!fgets(p, endp - p + 1, stdin))
16720425Ssteve				break;
1681556Srgrimes			for (; *p &&  p < endp; ++p);
1691556Srgrimes			if (p == endp) {
1701556Srgrimes				*p = '\n';
17120425Ssteve				*++p = '\0';
17212043Speter				break;
17390111Simp			}
17412043Speter		}
17512043Speter	}
17620425Ssteve	mbuflen = strlen(mbuf);
1771556Srgrimes
178100588Stjr	if (offset)
179100588Stjr		(void)printf("Shutdown at %.24s.\n", ctime(&shuttime));
180100588Stjr	else
181100588Stjr		(void)printf("Shutdown NOW!\n");
182100588Stjr
183100588Stjr	if (!(whom = getlogin()))
18412043Speter		whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
18525225Ssteve
18612043Speter#ifdef DEBUG
187158143Sstefanf	(void)putc('\n', stdout);
188158143Sstefanf#else
18912043Speter	(void)setpriority(PRIO_PROCESS, 0, PRIO_MIN);
19012043Speter	{
191158143Sstefanf		int forkpid;
192158143Sstefanf
19312043Speter		forkpid = fork();
19412043Speter		if (forkpid == -1)
195158143Sstefanf			err(1, "fork");
196158143Sstefanf		if (forkpid)
197158143Sstefanf			errx(0, "[pid %d]", forkpid);
198158143Sstefanf	}
199158143Sstefanf#endif
200158143Sstefanf	openlog("shutdown", LOG_CONS, LOG_AUTH);
201158143Sstefanf	loop();
202158143Sstefanf	/* NOTREACHED */
203158143Sstefanf}
20412043Speter
20525225Sstevevoid
20625225Ssteveloop()
20712043Speter{
20812043Speter	struct interval *tp;
20912043Speter	u_int sltime;
21012043Speter	int logged;
21112043Speter
21212043Speter	if (offset <= NOLOG_TIME) {
21312043Speter		logged = 1;
21412043Speter		nolog();
21512043Speter	}
21612043Speter	else
21712043Speter		logged = 0;
218199629Sjilles	tp = tlist;
21912043Speter	if (tp->timeleft < offset)
22012043Speter		(void)sleep((u_int)(offset - tp->timeleft));
22112043Speter	else {
22212043Speter		while (offset < tp->timeleft)
22312043Speter			++tp;
22420425Ssteve		/*
22512043Speter		 * Warn now, if going to sleep more than a fifth of
22612043Speter		 * the next wait time.
22712043Speter		 */
22812043Speter		if (sltime = offset - tp->timeleft) {
2291556Srgrimes			if (sltime > tp->timetowait / 5)
2301556Srgrimes				timewarn(offset);
2311556Srgrimes			(void)sleep(sltime);
2321556Srgrimes		}
2331556Srgrimes	}
2341556Srgrimes	for (;; ++tp) {
23520425Ssteve		timewarn(tp->timeleft);
23620425Ssteve		if (!logged && tp->timeleft <= NOLOG_TIME) {
2371556Srgrimes			logged = 1;
2381556Srgrimes			nolog();
2391556Srgrimes		}
24090111Simp		(void)sleep((u_int)tp->timetowait);
24120425Ssteve		if (!tp->timeleft)
24212043Speter			break;
24312043Speter	}
24412043Speter	die_you_gravy_sucking_pig_dog();
24512043Speter}
2461556Srgrimes
2471556Srgrimesstatic jmp_buf alarmbuf;
2481556Srgrimes
2491556Srgrimesvoid
2501556Srgrimestimewarn(timeleft)
2511556Srgrimes	int timeleft;
2521556Srgrimes{
2531556Srgrimes	static int first;
2541556Srgrimes	static char hostname[MAXHOSTNAMELEN + 1];
2551556Srgrimes	FILE *pf;
2561556Srgrimes	char wcmd[MAXPATHLEN + 4];
25712043Speter
25812043Speter	if (!first++)
25925225Ssteve		(void)gethostname(hostname, sizeof(hostname));
26012043Speter
26112043Speter	/* undoc -n option to wall suppresses normal wall banner */
2621556Srgrimes	(void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL);
2631556Srgrimes	if (!(pf = popen(wcmd, "w"))) {
2641556Srgrimes		syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL);
26512043Speter		return;
26612043Speter	}
2671556Srgrimes
2681556Srgrimes	(void)fprintf(pf,
26912043Speter	    "\007*** %sSystem shutdown message from %s@%s ***\007\n",
27012043Speter	    timeleft ? "": "FINAL ", whom, hostname);
27112043Speter
27212043Speter	if (timeleft > 10*60)
27312043Speter		(void)fprintf(pf, "System going down at %5.5s\n\n",
27412043Speter		    ctime(&shuttime) + 11);
27512043Speter	else if (timeleft > 59)
27612043Speter		(void)fprintf(pf, "System going down in %d minute%s\n\n",
2771556Srgrimes		    timeleft / 60, (timeleft > 60) ? "s" : "");
27812043Speter	else if (timeleft)
27912043Speter		(void)fprintf(pf, "System going down in 30 seconds\n\n");
28012043Speter	else
28112043Speter		(void)fprintf(pf, "System going down IMMEDIATELY\n\n");
28212043Speter
28312043Speter	if (mbuflen)
28412043Speter		(void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf);
2851556Srgrimes
28612043Speter	/*
2871556Srgrimes	 * play some games, just in case wall doesn't come back
28812043Speter	 * probably unecessary, given that wall is careful.
28912043Speter	 */
29012043Speter	if (!setjmp(alarmbuf)) {
29112043Speter		(void)signal(SIGALRM, timeout);
29212043Speter		(void)alarm((u_int)30);
29312043Speter		(void)pclose(pf);
29412043Speter		(void)alarm((u_int)0);
29512043Speter		(void)signal(SIGALRM, SIG_DFL);
29612043Speter	}
29712043Speter}
2981556Srgrimes
29912043Spetervoid
30012043Spetertimeout(signo)
3011556Srgrimes	int signo;
3021556Srgrimes{
30318018Speter	longjmp(alarmbuf, 1);
3041556Srgrimes}
30584261Sobrien
3061556Srgrimesvoid
30784261Sobriendie_you_gravy_sucking_pig_dog()
30884261Sobrien{
3091556Srgrimes
3101556Srgrimes	syslog(LOG_NOTICE, "%s by %s: %s",
31118018Speter	    doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf);
31212043Speter	(void)sleep(2);
3131556Srgrimes
31412043Speter	(void)printf("\r\nSystem shutdown time has arrived\007\007\r\n");
3151556Srgrimes	if (killflg) {
3161556Srgrimes		(void)printf("\rbut you'll have to do it yourself\r\n");
31712043Speter		exit(0);
31812043Speter	}
31912043Speter#ifdef DEBUG
3201556Srgrimes	if (doreboot)
3211556Srgrimes		(void)printf("reboot");
3221556Srgrimes	else if (dohalt)
3231556Srgrimes		(void)printf("halt");
324194128Sjilles	if (nosync)
325194128Sjilles		(void)printf(" no sync");
326194128Sjilles	(void)printf("\nkill -HUP 1\n");
327194128Sjilles#else
328194128Sjilles	if (doreboot) {
329194128Sjilles		execle(_PATH_REBOOT, "reboot", "-l", nosync, 0);
330194128Sjilles		syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT);
331194128Sjilles		perror("shutdown");
332194128Sjilles	}
333194128Sjilles	else if (dohalt) {
334194128Sjilles		execle(_PATH_HALT, "halt", "-l", nosync, 0);
335194128Sjilles		syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT);
336194128Sjilles		perror("shutdown");
337194128Sjilles	}
338194128Sjilles	(void)kill(1, SIGTERM);		/* to single user */
339194128Sjilles#endif
340194128Sjilles	finish(0);
3411556Srgrimes}
3421556Srgrimes
3431556Srgrimes#define	ATOI2(p)	(p[0] - '0') * 10 + (p[1] - '0'); p += 2;
3441556Srgrimes
3451556Srgrimesvoid
34690111Simpgetoffset(timearg)
34790111Simp	register char *timearg;
3481556Srgrimes{
3491556Srgrimes	register struct tm *lt;
3501556Srgrimes	register char *p;
3511556Srgrimes	time_t now;
3521556Srgrimes
3531556Srgrimes	if (!strcasecmp(timearg, "now")) {		/* now */
3541556Srgrimes		offset = 0;
3551556Srgrimes		return;
3561556Srgrimes	}
35790111Simp
35890111Simp	(void)time(&now);
3591556Srgrimes	if (*timearg == '+') {				/* +minutes */
3601556Srgrimes		if (!isdigit(*++timearg))
3611556Srgrimes			badtime();
362199629Sjilles		offset = atoi(timearg) * 60;
3631556Srgrimes		shuttime = now + offset;
3641556Srgrimes		return;
3651556Srgrimes	}
3661556Srgrimes
3671556Srgrimes	/* handle hh:mm by getting rid of the colon */
3681556Srgrimes	for (p = timearg; *p; ++p)
3691556Srgrimes		if (!isascii(*p) || !isdigit(*p))
3701556Srgrimes			if (*p == ':' && strlen(p) == 3) {
37112043Speter				p[0] = p[1];
3721556Srgrimes				p[1] = p[2];
3731556Srgrimes				p[2] = '\0';
3741556Srgrimes			}
3751556Srgrimes			else
3761556Srgrimes				badtime();
3771556Srgrimes
3781556Srgrimes	unsetenv("TZ");					/* OUR timezone */
3791556Srgrimes	lt = localtime(&now);				/* current time val */
38017987Speter
38190111Simp	switch(strlen(timearg)) {
3821556Srgrimes	case 10:
3831556Srgrimes		lt->tm_year = ATOI2(timearg);
3841556Srgrimes		/* FALLTHROUGH */
3851556Srgrimes	case 8:
3861556Srgrimes		lt->tm_mon = ATOI2(timearg);
3871556Srgrimes		if (--lt->tm_mon < 0 || lt->tm_mon > 11)
38812043Speter			badtime();
389199629Sjilles		/* FALLTHROUGH */
3901556Srgrimes	case 6:
3911556Srgrimes		lt->tm_mday = ATOI2(timearg);
3921556Srgrimes		if (lt->tm_mday < 1 || lt->tm_mday > 31)
3931556Srgrimes			badtime();
3941556Srgrimes		/* FALLTHROUGH */
3951556Srgrimes	case 4:
3961556Srgrimes		lt->tm_hour = ATOI2(timearg);
3971556Srgrimes		if (lt->tm_hour < 0 || lt->tm_hour > 23)
3981556Srgrimes			badtime();
3991556Srgrimes		lt->tm_min = ATOI2(timearg);
4001556Srgrimes		if (lt->tm_min < 0 || lt->tm_min > 59)
4011556Srgrimes			badtime();
4021556Srgrimes		lt->tm_sec = 0;
4031556Srgrimes		if ((shuttime = mktime(lt)) == -1)
40490111Simp			badtime();
40517987Speter		if ((offset = shuttime - now) < 0)
4061556Srgrimes			errx(1, "that time is already past.");
4071556Srgrimes		break;
4081556Srgrimes	default:
4091556Srgrimes		badtime();
4101556Srgrimes	}
41153891Scracauer}
4121556Srgrimes
413124780Sdes#define	NOMSG	"\n\nNO LOGINS: System going down at "
4141556Srgrimesvoid
4151556Srgrimesnolog()
4161556Srgrimes{
4171556Srgrimes	int logfd;
4181556Srgrimes	char *ct;
4191556Srgrimes
4201556Srgrimes	(void)unlink(_PATH_NOLOGIN);	/* in case linked to another file */
4211556Srgrimes	(void)signal(SIGINT, finish);
4221556Srgrimes	(void)signal(SIGHUP, finish);
4231556Srgrimes	(void)signal(SIGQUIT, finish);
4241556Srgrimes	(void)signal(SIGTERM, finish);
4251556Srgrimes	if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC,
4261556Srgrimes	    0664)) >= 0) {
4271556Srgrimes		(void)write(logfd, NOMSG, sizeof(NOMSG) - 1);
4281556Srgrimes		ct = ctime(&shuttime);
4291556Srgrimes		(void)write(logfd, ct + 11, 5);
43090111Simp		(void)write(logfd, "\n\n", 2);
43117987Speter		(void)write(logfd, mbuf, strlen(mbuf));
43225225Ssteve		(void)close(logfd);
4331556Srgrimes	}
4341556Srgrimes}
4351556Srgrimes
4361556Srgrimesvoid
4371556Srgrimesfinish(signo)
4381556Srgrimes	int signo;
4391556Srgrimes{
4401556Srgrimes	(void)unlink(_PATH_NOLOGIN);
4411556Srgrimes	exit(0);
44212043Speter}
4431556Srgrimes
4441556Srgrimesvoid
4451556Srgrimesbadtime()
4461556Srgrimes{
4471556Srgrimes	errx(1, "bad time format.");
4481556Srgrimes}
4491556Srgrimes
4501556Srgrimesvoid
4511556Srgrimesusage()
45290111Simp{
45390111Simp	fprintf(stderr, "usage: shutdown [-hknr] shutdowntime [ message ]\n");
4541556Srgrimes	exit(1);
4551556Srgrimes}
4561556Srgrimes