xref: /csrg-svn/sbin/shutdown/shutdown.c (revision 28024)
121179Sdist /*
221179Sdist  * Copyright (c) 1983 Regents of the University of California.
321179Sdist  * All rights reserved.  The Berkeley software License Agreement
421179Sdist  * specifies the terms and conditions for redistribution.
521179Sdist  */
621179Sdist 
712682Ssam #ifndef lint
821179Sdist char copyright[] =
921179Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\
1021179Sdist  All rights reserved.\n";
1121179Sdist #endif not lint
122373Swnj 
1321179Sdist #ifndef lint
14*28024Seric static char sccsid[] = "@(#)shutdown.c	5.4 (Berkeley) 05/12/86";
1521179Sdist #endif not lint
1621179Sdist 
172373Swnj #include <stdio.h>
182373Swnj #include <ctype.h>
192373Swnj #include <signal.h>
2015920Skarels #include <setjmp.h>
212373Swnj #include <utmp.h>
2212897Ssam #include <sys/time.h>
2312897Ssam #include <sys/resource.h>
242373Swnj #include <sys/types.h>
25*28024Seric #include <sys/syslog.h>
26*28024Seric 
272373Swnj /*
282373Swnj  *	/etc/shutdown when [messages]
292373Swnj  *
302373Swnj  *	allow super users to tell users and remind users
312373Swnj  *	of iminent shutdown of unix
322373Swnj  *	and shut it down automatically
332373Swnj  *	and even reboot or halt the machine if they desire
342373Swnj  */
35*28024Seric 
362373Swnj #define	REBOOT	"/etc/reboot"
372373Swnj #define	HALT	"/etc/halt"
382373Swnj #define MAXINTS 20
392373Swnj #define	HOURS	*3600
402373Swnj #define MINUTES	*60
412373Swnj #define SECONDS
42*28024Seric #define NLOG		600		/* no of bytes possible for message */
432373Swnj #define	NOLOGTIME	5 MINUTES
443885Sroot #define IGNOREUSER	"sleeper"
452373Swnj 
466203Sroot char	hostname[32];
476203Sroot 
4815920Skarels int	timeout();
492373Swnj time_t	getsdt();
502373Swnj 
512373Swnj extern	char *ctime();
522373Swnj extern	struct tm *localtime();
5325408Sbloom extern	long time();
542373Swnj 
5525408Sbloom extern	char *strcpy();
5625408Sbloom extern	char *strncat();
5725408Sbloom extern	off_t lseek();
5825408Sbloom 
592373Swnj struct	utmp utmp;
602373Swnj int	sint;
612373Swnj int	stogo;
622373Swnj char	tpath[] =	"/dev/";
632373Swnj int	nlflag = 1;		/* nolog yet to be done */
642373Swnj int	killflg = 1;
6525408Sbloom int	doreboot = 0;
662373Swnj int	halt = 0;
6725366Sbloom int     fast = 0;
6825366Sbloom char    *nosync = NULL;
6925366Sbloom char    nosyncflag[] = "-n";
702373Swnj char	term[sizeof tpath + sizeof utmp.ut_line];
712373Swnj char	tbuf[BUFSIZ];
722373Swnj char	nolog1[] = "\n\nNO LOGINS: System going down at %5.5s\n\n";
73*28024Seric char	nolog2[NLOG+1];
742373Swnj #ifdef	DEBUG
752373Swnj char	nologin[] = "nologin";
7625366Sbloom char    fastboot[] = "fastboot";
772373Swnj #else
782373Swnj char	nologin[] = "/etc/nologin";
7925366Sbloom char	fastboot[] = "/fastboot";
802373Swnj #endif
818841Smckusick time_t	nowtime;
8215920Skarels jmp_buf	alarmbuf;
838841Smckusick 
842373Swnj struct interval {
852373Swnj 	int stogo;
862373Swnj 	int sint;
872373Swnj } interval[] = {
882373Swnj 	4 HOURS,	1 HOURS,
892373Swnj 	2 HOURS,	30 MINUTES,
902373Swnj 	1 HOURS,	15 MINUTES,
912373Swnj 	30 MINUTES,	10 MINUTES,
922373Swnj 	15 MINUTES,	5 MINUTES,
932818Swnj 	10 MINUTES,	5 MINUTES,
942818Swnj 	5 MINUTES,	3 MINUTES,
958841Smckusick 	2 MINUTES,	1 MINUTES,
968841Smckusick 	1 MINUTES,	30 SECONDS,
972373Swnj 	0 SECONDS,	0 SECONDS
982373Swnj };
998841Smckusick 
1002818Swnj char *shutter, *getlogin();
1018841Smckusick 
1022373Swnj main(argc,argv)
1032373Swnj 	int argc;
1042373Swnj 	char **argv;
1052373Swnj {
1062373Swnj 	register i, ufd;
107*28024Seric 	register char *f;
1082373Swnj 	char *ts;
1098841Smckusick 	time_t sdt;
1102373Swnj 	int h, m;
1118841Smckusick 	int first;
1122373Swnj 	FILE *termf;
1132373Swnj 
1142818Swnj 	shutter = getlogin();
115*28024Seric 	if (shutter == 0)
116*28024Seric 		shutter = "???";
11725408Sbloom 	(void) gethostname(hostname, sizeof (hostname));
118*28024Seric 	openlog("shutdown", 0, LOG_AUTH);
1192373Swnj 	argc--, argv++;
1202373Swnj 	while (argc > 0 && (f = argv[0], *f++ == '-')) {
1212373Swnj 		while (i = *f++) switch (i) {
1222373Swnj 		case 'k':
1232373Swnj 			killflg = 0;
1242373Swnj 			continue;
12525366Sbloom 		case 'n':
12625366Sbloom 			nosync = nosyncflag;
12725366Sbloom 			continue;
12825366Sbloom 		case 'f':
12925366Sbloom 			fast = 1;
13025366Sbloom 			continue;
1312373Swnj 		case 'r':
13225408Sbloom 			doreboot = 1;
1332373Swnj 			continue;
1342373Swnj 		case 'h':
1352373Swnj 			halt = 1;
1362373Swnj 			continue;
1372373Swnj 		default:
1382373Swnj 			fprintf(stderr, "shutdown: '%c' - unknown flag\n", i);
1392373Swnj 			exit(1);
1402373Swnj 		}
1412373Swnj 		argc--, argv++;
1422373Swnj 	}
1432373Swnj 	if (argc < 1) {
14425366Sbloom 	        /* argv[0] is not available after the argument handling. */
14525366Sbloom 		printf("Usage: shutdown [ -krhfn ] shutdowntime [ message ]\n");
1462373Swnj 		finish();
1472373Swnj 	}
14825366Sbloom 	if (fast && (nosync == nosyncflag)) {
14925366Sbloom 	        printf ("shutdown: Incompatible switches 'fast' & 'nosync'\n");
15025366Sbloom 		finish();
15125366Sbloom 	}
1523720Sroot 	if (geteuid()) {
1533720Sroot 		fprintf(stderr, "NOT super-user\n");
1543648Stoy 		finish();
1553648Stoy 	}
15625408Sbloom 	nowtime = time((long *)0);
1572373Swnj 	sdt = getsdt(argv[0]);
1582373Swnj 	argc--, argv++;
159*28024Seric 	nolog2[0] = '\0';
160*28024Seric 	while (argc-- > 0) {
161*28024Seric 		strcat(nolog2, " ");
162*28024Seric 		strcat(nolog2, *argv++);
163*28024Seric 	}
1642373Swnj 	m = ((stogo = sdt - nowtime) + 30)/60;
1652373Swnj 	h = m/60;
1662373Swnj 	m %= 60;
1672373Swnj 	ts = ctime(&sdt);
1682818Swnj 	printf("Shutdown at %5.5s (in ", ts+11);
1692373Swnj 	if (h > 0)
1702373Swnj 		printf("%d hour%s ", h, h != 1 ? "s" : "");
1712818Swnj 	printf("%d minute%s) ", m, m != 1 ? "s" : "");
1722373Swnj #ifndef DEBUG
17325408Sbloom 	(void) signal(SIGHUP, SIG_IGN);
17425408Sbloom 	(void) signal(SIGQUIT, SIG_IGN);
17525408Sbloom 	(void) signal(SIGINT, SIG_IGN);
1762373Swnj #endif
17725408Sbloom 	(void) signal(SIGTTOU, SIG_IGN);
17825408Sbloom 	(void) signal(SIGTERM, finish);
17925408Sbloom 	(void) signal(SIGALRM, timeout);
18025408Sbloom 	(void) setpriority(PRIO_PROCESS, 0, PRIO_MIN);
18125408Sbloom 	(void) fflush(stdout);
1822818Swnj #ifndef DEBUG
1832373Swnj 	if (i = fork()) {
1842818Swnj 		printf("[pid %d]\n", i);
1852373Swnj 		exit(0);
1862373Swnj 	}
1878841Smckusick #else
18825408Sbloom 	(void) putc('\n', stdout);
1892818Swnj #endif
1902373Swnj 	sint = 1 HOURS;
1912373Swnj 	f = "";
1928841Smckusick 	ufd = open("/etc/utmp",0);
1938841Smckusick 	if (ufd < 0) {
1948841Smckusick 		perror("shutdown: /etc/utmp");
1958841Smckusick 		exit(1);
1968841Smckusick 	}
1978841Smckusick 	first = 1;
1982373Swnj 	for (;;) {
1992373Swnj 		for (i = 0; stogo <= interval[i].stogo && interval[i].sint; i++)
2002373Swnj 			sint = interval[i].sint;
2018841Smckusick 		if (stogo > 0 && (stogo-sint) < interval[i].stogo)
2028841Smckusick 			sint = stogo - interval[i].stogo;
2032373Swnj 		if (stogo <= NOLOGTIME && nlflag) {
2042373Swnj 			nlflag = 0;
2052373Swnj 			nolog(sdt);
2062373Swnj 		}
2072373Swnj 		if (sint >= stogo || sint == 0)
2082373Swnj 			f = "FINAL ";
20925408Sbloom 		nowtime = time((long *)0);
21025408Sbloom 		(void) lseek(ufd, 0L, 0);
21125408Sbloom 		while (read(ufd,(char *)&utmp,sizeof utmp)==sizeof utmp)
2123885Sroot 		if (utmp.ut_name[0] &&
2133885Sroot 		    strncmp(utmp.ut_name, IGNOREUSER, sizeof(utmp.ut_name))) {
21415920Skarels 			if (setjmp(alarmbuf))
21515920Skarels 				continue;
21625408Sbloom 			(void) strcpy(term, tpath);
21725408Sbloom 			(void) strncat(term, utmp.ut_line, sizeof utmp.ut_line);
21825408Sbloom 			(void) alarm(3);
2192373Swnj #ifdef DEBUG
2202818Swnj 			if ((termf = stdout) != NULL)
2212373Swnj #else
2222373Swnj 			if ((termf = fopen(term, "w")) != NULL)
2232373Swnj #endif
2242373Swnj 			{
22525408Sbloom 				(void) alarm(0);
2262373Swnj 				setbuf(termf, tbuf);
2278841Smckusick 				fprintf(termf, "\n\r\n");
2288841Smckusick 				warn(termf, sdt, nowtime, f);
2298841Smckusick 				if (first || sdt - nowtime > 1 MINUTES) {
2308841Smckusick 					if (*nolog2)
231*28024Seric 						fprintf(termf, "\t...%s", nolog2);
2328841Smckusick 				}
23325408Sbloom 				(void) fputc('\r', termf);
23425408Sbloom 				(void) fputc('\n', termf);
23525408Sbloom 				(void) alarm(5);
2362818Swnj #ifdef DEBUG
23725408Sbloom 				(void) fflush(termf);
2382818Swnj #else
23925408Sbloom 				(void) fclose(termf);
2402818Swnj #endif
24125408Sbloom 				(void) alarm(0);
2422373Swnj 			}
2432373Swnj 		}
2448841Smckusick 		if (stogo <= 0) {
245*28024Seric 			printf("\n\007\007System shutdown time has arrived\007\007\n");
246*28024Seric 			syslog(LOG_CRIT, "%s!%s: %s", hostname, shutter, nolog2);
24725408Sbloom 			(void) unlink(nologin);
2482373Swnj 			if (!killflg) {
2492373Swnj 				printf("but you'll have to do it yourself\n");
2502373Swnj 				finish();
2512373Swnj 			}
25225366Sbloom 			if (fast)
25325366Sbloom 				doitfast();
2542373Swnj #ifndef DEBUG
25525408Sbloom 			(void) kill(-1, SIGTERM);	/* terminate everyone */
2568841Smckusick 			sleep(5);		/* & wait while they die */
25725408Sbloom 			if (doreboot)
25825366Sbloom 				execle(REBOOT, "reboot", nosync, 0, 0);
2592373Swnj 			if (halt)
26025366Sbloom 				execle(HALT, "halt", nosync, 0, 0);
26125408Sbloom 			(void) kill(1, SIGTERM);	/* sync */
26225408Sbloom 			(void) kill(1, SIGTERM);	/* sync */
2632373Swnj 			sleep(20);
2642373Swnj #else
2652373Swnj 			printf("EXTERMINATE EXTERMINATE\n");
26625408Sbloom 			if (doreboot)
26725366Sbloom 				printf("REBOOT");
26825366Sbloom 			if (halt)
26925366Sbloom 				printf(" HALT");
27025366Sbloom 			if (fast)
27125366Sbloom 				printf(" %s (without fsck's)\n", nosync);
27225366Sbloom 			else
27325366Sbloom 				printf(" %s\n", nosync);
27425366Sbloom 
2752373Swnj #endif
2762373Swnj 			finish();
2772373Swnj 		}
27825408Sbloom 		stogo = sdt - time((long *) 0);
2798841Smckusick 		if (stogo > 0 && sint > 0)
28025408Sbloom 			sleep((unsigned)(sint<stogo ? sint : stogo));
2812373Swnj 		stogo -= sint;
2828841Smckusick 		first = 0;
2832373Swnj 	}
2842373Swnj }
2852373Swnj 
2862373Swnj time_t
2872373Swnj getsdt(s)
2888841Smckusick 	register char *s;
2892373Swnj {
2902373Swnj 	time_t t, t1, tim;
2912373Swnj 	register char c;
2922373Swnj 	struct tm *lt;
2932373Swnj 
2948841Smckusick 	if (strcmp(s, "now") == 0)
2958841Smckusick 		return(nowtime);
2962373Swnj 	if (*s == '+') {
2972373Swnj 		++s;
2982373Swnj 		t = 0;
2992373Swnj 		for (;;) {
3002373Swnj 			c = *s++;
3012373Swnj 			if (!isdigit(c))
3022373Swnj 				break;
3032373Swnj 			t = t * 10 + c - '0';
3042373Swnj 		}
3052373Swnj 		if (t <= 0)
3062373Swnj 			t = 5;
3072373Swnj 		t *= 60;
30825408Sbloom 		tim = time((long *) 0) + t;
3092373Swnj 		return(tim);
3102373Swnj 	}
3112373Swnj 	t = 0;
3122373Swnj 	while (strlen(s) > 2 && isdigit(*s))
3132373Swnj 		t = t * 10 + *s++ - '0';
3142373Swnj 	if (*s == ':')
3152373Swnj 		s++;
3162373Swnj 	if (t > 23)
3172373Swnj 		goto badform;
3182373Swnj 	tim = t*60;
3192373Swnj 	t = 0;
3202373Swnj 	while (isdigit(*s))
3212373Swnj 		t = t * 10 + *s++ - '0';
3222373Swnj 	if (t > 59)
3232373Swnj 		goto badform;
3242373Swnj 	tim += t;
3252373Swnj 	tim *= 60;
32625408Sbloom 	t1 = time((long *) 0);
3272373Swnj 	lt = localtime(&t1);
3282373Swnj 	t = lt->tm_sec + lt->tm_min*60 + lt->tm_hour*3600;
3292373Swnj 	if (tim < t || tim >= (24*3600)) {
3302373Swnj 		/* before now or after midnight */
3312373Swnj 		printf("That must be tomorrow\nCan't you wait till then?\n");
3322373Swnj 		finish();
3332373Swnj 	}
3348841Smckusick 	return (t1 + tim - t);
3352373Swnj badform:
3362373Swnj 	printf("Bad time format\n");
3372373Swnj 	finish();
33825408Sbloom 	/*NOTREACHED*/
3392373Swnj }
3402373Swnj 
3418841Smckusick warn(term, sdt, now, type)
3422373Swnj 	FILE *term;
3438841Smckusick 	time_t sdt, now;
3448841Smckusick 	char *type;
3452373Swnj {
3462373Swnj 	char *ts;
3478841Smckusick 	register delay = sdt - now;
3482373Swnj 
3493880Swnj 	if (delay > 8)
3503880Swnj 		while (delay % 5)
3513880Swnj 			delay++;
3523880Swnj 
353*28024Seric 	fprintf(term,
35413178Sroot 	    "\007\007\t*** %sSystem shutdown message from %s@%s ***\r\n\n",
35513178Sroot 		    type, shutter, hostname);
3568841Smckusick 
3572373Swnj 	ts = ctime(&sdt);
3588841Smckusick 	if (delay > 10 MINUTES)
3598841Smckusick 		fprintf(term, "System going down at %5.5s\r\n", ts+11);
3608841Smckusick 	else if (delay > 95 SECONDS) {
3618841Smckusick 		fprintf(term, "System going down in %d minute%s\r\n",
3628841Smckusick 		    (delay+30)/60, (delay+30)/60 != 1 ? "s" : "");
3638841Smckusick 	} else if (delay > 0) {
3648841Smckusick 		fprintf(term, "System going down in %d second%s\r\n",
3658841Smckusick 		    delay, delay != 1 ? "s" : "");
3662373Swnj 	} else
3678841Smckusick 		fprintf(term, "System going down IMMEDIATELY\r\n");
3682373Swnj }
3692373Swnj 
37025366Sbloom doitfast()
37125366Sbloom {
37225366Sbloom 	FILE *fastd;
37325366Sbloom 
37425366Sbloom 	if ((fastd = fopen(fastboot, "w")) != NULL) {
37525366Sbloom 		putc('\n', fastd);
37625408Sbloom 		(void) fclose(fastd);
37725366Sbloom 	}
37825366Sbloom }
37925366Sbloom 
3802373Swnj nolog(sdt)
3818841Smckusick 	time_t sdt;
3822373Swnj {
3832373Swnj 	FILE *nologf;
3842373Swnj 
38525408Sbloom 	(void) unlink(nologin);			/* in case linked to std file */
3862373Swnj 	if ((nologf = fopen(nologin, "w")) != NULL) {
3872373Swnj 		fprintf(nologf, nolog1, (ctime(&sdt)) + 11);
388*28024Seric 		if (*nolog2)
389*28024Seric 			fprintf(nologf, "\t%s\n", nolog2 + 1);
39025408Sbloom 		(void) fclose(nologf);
3912373Swnj 	}
3922373Swnj }
3932373Swnj 
3942373Swnj finish()
3952373Swnj {
39625408Sbloom 	(void) signal(SIGTERM, SIG_IGN);
39725408Sbloom 	(void) unlink(nologin);
3982373Swnj 	exit(0);
3992373Swnj }
4002373Swnj 
40115920Skarels timeout()
4022373Swnj {
40315920Skarels 	longjmp(alarmbuf, 1);
4042373Swnj }
405