xref: /csrg-svn/sbin/shutdown/shutdown.c (revision 25408)
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*25408Sbloom static char sccsid[] = "@(#)shutdown.c	5.3 (Berkeley) 11/06/85";
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>
252373Swnj /*
262373Swnj  *	/etc/shutdown when [messages]
272373Swnj  *
282373Swnj  *	allow super users to tell users and remind users
292373Swnj  *	of iminent shutdown of unix
302373Swnj  *	and shut it down automatically
312373Swnj  *	and even reboot or halt the machine if they desire
322373Swnj  */
332818Swnj #ifdef DEBUG
342818Swnj #define LOGFILE "shutdown.log"
352818Swnj #else
362818Swnj #define LOGFILE "/usr/adm/shutdownlog"
372818Swnj #endif
382373Swnj #define	REBOOT	"/etc/reboot"
392373Swnj #define	HALT	"/etc/halt"
402373Swnj #define MAXINTS 20
412373Swnj #define	HOURS	*3600
422373Swnj #define MINUTES	*60
432373Swnj #define SECONDS
443880Swnj #define NLOG		20		/* no of args possible for message */
452373Swnj #define	NOLOGTIME	5 MINUTES
463885Sroot #define IGNOREUSER	"sleeper"
472373Swnj 
486203Sroot char	hostname[32];
496203Sroot 
5015920Skarels int	timeout();
512373Swnj time_t	getsdt();
522373Swnj 
532373Swnj extern	char *ctime();
542373Swnj extern	struct tm *localtime();
55*25408Sbloom extern	long time();
562373Swnj 
57*25408Sbloom extern	char *strcpy();
58*25408Sbloom extern	char *strncat();
59*25408Sbloom extern	off_t lseek();
60*25408Sbloom 
612373Swnj struct	utmp utmp;
622373Swnj int	sint;
632373Swnj int	stogo;
642373Swnj char	tpath[] =	"/dev/";
652373Swnj int	nlflag = 1;		/* nolog yet to be done */
662373Swnj int	killflg = 1;
67*25408Sbloom int	doreboot = 0;
682373Swnj int	halt = 0;
6925366Sbloom int     fast = 0;
7025366Sbloom char    *nosync = NULL;
7125366Sbloom char    nosyncflag[] = "-n";
722373Swnj char	term[sizeof tpath + sizeof utmp.ut_line];
732373Swnj char	tbuf[BUFSIZ];
742373Swnj char	nolog1[] = "\n\nNO LOGINS: System going down at %5.5s\n\n";
752373Swnj char	*nolog2[NLOG+1];
762373Swnj #ifdef	DEBUG
772373Swnj char	nologin[] = "nologin";
7825366Sbloom char    fastboot[] = "fastboot";
792373Swnj #else
802373Swnj char	nologin[] = "/etc/nologin";
8125366Sbloom char	fastboot[] = "/fastboot";
822373Swnj #endif
838841Smckusick time_t	nowtime;
8415920Skarels jmp_buf	alarmbuf;
858841Smckusick 
862373Swnj struct interval {
872373Swnj 	int stogo;
882373Swnj 	int sint;
892373Swnj } interval[] = {
902373Swnj 	4 HOURS,	1 HOURS,
912373Swnj 	2 HOURS,	30 MINUTES,
922373Swnj 	1 HOURS,	15 MINUTES,
932373Swnj 	30 MINUTES,	10 MINUTES,
942373Swnj 	15 MINUTES,	5 MINUTES,
952818Swnj 	10 MINUTES,	5 MINUTES,
962818Swnj 	5 MINUTES,	3 MINUTES,
978841Smckusick 	2 MINUTES,	1 MINUTES,
988841Smckusick 	1 MINUTES,	30 SECONDS,
992373Swnj 	0 SECONDS,	0 SECONDS
1002373Swnj };
1018841Smckusick 
1022818Swnj char *shutter, *getlogin();
1038841Smckusick 
1042373Swnj main(argc,argv)
1052373Swnj 	int argc;
1062373Swnj 	char **argv;
1072373Swnj {
1082373Swnj 	register i, ufd;
1092373Swnj 	register char **mess, *f;
1102373Swnj 	char *ts;
1118841Smckusick 	time_t sdt;
1122373Swnj 	int h, m;
1138841Smckusick 	int first;
1142373Swnj 	FILE *termf;
1152373Swnj 
1162818Swnj 	shutter = getlogin();
117*25408Sbloom 	(void) gethostname(hostname, sizeof (hostname));
1182373Swnj 	argc--, argv++;
1192373Swnj 	while (argc > 0 && (f = argv[0], *f++ == '-')) {
1202373Swnj 		while (i = *f++) switch (i) {
1212373Swnj 		case 'k':
1222373Swnj 			killflg = 0;
1232373Swnj 			continue;
12425366Sbloom 		case 'n':
12525366Sbloom 			nosync = nosyncflag;
12625366Sbloom 			continue;
12725366Sbloom 		case 'f':
12825366Sbloom 			fast = 1;
12925366Sbloom 			continue;
1302373Swnj 		case 'r':
131*25408Sbloom 			doreboot = 1;
1322373Swnj 			continue;
1332373Swnj 		case 'h':
1342373Swnj 			halt = 1;
1352373Swnj 			continue;
1362373Swnj 		default:
1372373Swnj 			fprintf(stderr, "shutdown: '%c' - unknown flag\n", i);
1382373Swnj 			exit(1);
1392373Swnj 		}
1402373Swnj 		argc--, argv++;
1412373Swnj 	}
1422373Swnj 	if (argc < 1) {
14325366Sbloom 	        /* argv[0] is not available after the argument handling. */
14425366Sbloom 		printf("Usage: shutdown [ -krhfn ] shutdowntime [ message ]\n");
1452373Swnj 		finish();
1462373Swnj 	}
14725366Sbloom 	if (fast && (nosync == nosyncflag)) {
14825366Sbloom 	        printf ("shutdown: Incompatible switches 'fast' & 'nosync'\n");
14925366Sbloom 		finish();
15025366Sbloom 	}
1513720Sroot 	if (geteuid()) {
1523720Sroot 		fprintf(stderr, "NOT super-user\n");
1533648Stoy 		finish();
1543648Stoy 	}
155*25408Sbloom 	nowtime = time((long *)0);
1562373Swnj 	sdt = getsdt(argv[0]);
1572373Swnj 	argc--, argv++;
1582373Swnj 	i = 0;
1592373Swnj 	while (argc-- > 0)
1602373Swnj 		if (i < NLOG)
1612373Swnj 			nolog2[i++] = *argv++;
1622373Swnj 	nolog2[i] = NULL;
1632373Swnj 	m = ((stogo = sdt - nowtime) + 30)/60;
1642373Swnj 	h = m/60;
1652373Swnj 	m %= 60;
1662373Swnj 	ts = ctime(&sdt);
1672818Swnj 	printf("Shutdown at %5.5s (in ", ts+11);
1682373Swnj 	if (h > 0)
1692373Swnj 		printf("%d hour%s ", h, h != 1 ? "s" : "");
1702818Swnj 	printf("%d minute%s) ", m, m != 1 ? "s" : "");
1712373Swnj #ifndef DEBUG
172*25408Sbloom 	(void) signal(SIGHUP, SIG_IGN);
173*25408Sbloom 	(void) signal(SIGQUIT, SIG_IGN);
174*25408Sbloom 	(void) signal(SIGINT, SIG_IGN);
1752373Swnj #endif
176*25408Sbloom 	(void) signal(SIGTTOU, SIG_IGN);
177*25408Sbloom 	(void) signal(SIGTERM, finish);
178*25408Sbloom 	(void) signal(SIGALRM, timeout);
179*25408Sbloom 	(void) setpriority(PRIO_PROCESS, 0, PRIO_MIN);
180*25408Sbloom 	(void) fflush(stdout);
1812818Swnj #ifndef DEBUG
1822373Swnj 	if (i = fork()) {
1832818Swnj 		printf("[pid %d]\n", i);
1842373Swnj 		exit(0);
1852373Swnj 	}
1868841Smckusick #else
187*25408Sbloom 	(void) putc('\n', stdout);
1882818Swnj #endif
1892373Swnj 	sint = 1 HOURS;
1902373Swnj 	f = "";
1918841Smckusick 	ufd = open("/etc/utmp",0);
1928841Smckusick 	if (ufd < 0) {
1938841Smckusick 		perror("shutdown: /etc/utmp");
1948841Smckusick 		exit(1);
1958841Smckusick 	}
1968841Smckusick 	first = 1;
1972373Swnj 	for (;;) {
1982373Swnj 		for (i = 0; stogo <= interval[i].stogo && interval[i].sint; i++)
1992373Swnj 			sint = interval[i].sint;
2008841Smckusick 		if (stogo > 0 && (stogo-sint) < interval[i].stogo)
2018841Smckusick 			sint = stogo - interval[i].stogo;
2022373Swnj 		if (stogo <= NOLOGTIME && nlflag) {
2032373Swnj 			nlflag = 0;
2042373Swnj 			nolog(sdt);
2052373Swnj 		}
2062373Swnj 		if (sint >= stogo || sint == 0)
2072373Swnj 			f = "FINAL ";
208*25408Sbloom 		nowtime = time((long *)0);
209*25408Sbloom 		(void) lseek(ufd, 0L, 0);
210*25408Sbloom 		while (read(ufd,(char *)&utmp,sizeof utmp)==sizeof utmp)
2113885Sroot 		if (utmp.ut_name[0] &&
2123885Sroot 		    strncmp(utmp.ut_name, IGNOREUSER, sizeof(utmp.ut_name))) {
21315920Skarels 			if (setjmp(alarmbuf))
21415920Skarels 				continue;
215*25408Sbloom 			(void) strcpy(term, tpath);
216*25408Sbloom 			(void) strncat(term, utmp.ut_line, sizeof utmp.ut_line);
217*25408Sbloom 			(void) alarm(3);
2182373Swnj #ifdef DEBUG
2192818Swnj 			if ((termf = stdout) != NULL)
2202373Swnj #else
2212373Swnj 			if ((termf = fopen(term, "w")) != NULL)
2222373Swnj #endif
2232373Swnj 			{
224*25408Sbloom 				(void) alarm(0);
2252373Swnj 				setbuf(termf, tbuf);
2268841Smckusick 				fprintf(termf, "\n\r\n");
2278841Smckusick 				warn(termf, sdt, nowtime, f);
2288841Smckusick 				if (first || sdt - nowtime > 1 MINUTES) {
2298841Smckusick 					if (*nolog2)
2308841Smckusick 						fprintf(termf, "\t...");
2312373Swnj 					for (mess = nolog2; *mess; mess++)
2328841Smckusick 						fprintf(termf, " %s", *mess);
2338841Smckusick 				}
234*25408Sbloom 				(void) fputc('\r', termf);
235*25408Sbloom 				(void) fputc('\n', termf);
236*25408Sbloom 				(void) alarm(5);
2372818Swnj #ifdef DEBUG
238*25408Sbloom 				(void) fflush(termf);
2392818Swnj #else
240*25408Sbloom 				(void) fclose(termf);
2412818Swnj #endif
242*25408Sbloom 				(void) alarm(0);
2432373Swnj 			}
2442373Swnj 		}
2458841Smckusick 		if (stogo <= 0) {
2462373Swnj 	printf("\n\007\007System shutdown time has arrived\007\007\n");
2472818Swnj 			log_entry(sdt);
248*25408Sbloom 			(void) unlink(nologin);
2492373Swnj 			if (!killflg) {
2502373Swnj 				printf("but you'll have to do it yourself\n");
2512373Swnj 				finish();
2522373Swnj 			}
25325366Sbloom 			if (fast)
25425366Sbloom 				doitfast();
2552373Swnj #ifndef DEBUG
256*25408Sbloom 			(void) kill(-1, SIGTERM);	/* terminate everyone */
2578841Smckusick 			sleep(5);		/* & wait while they die */
258*25408Sbloom 			if (doreboot)
25925366Sbloom 				execle(REBOOT, "reboot", nosync, 0, 0);
2602373Swnj 			if (halt)
26125366Sbloom 				execle(HALT, "halt", nosync, 0, 0);
262*25408Sbloom 			(void) kill(1, SIGTERM);	/* sync */
263*25408Sbloom 			(void) kill(1, SIGTERM);	/* sync */
2642373Swnj 			sleep(20);
2652373Swnj #else
2662373Swnj 			printf("EXTERMINATE EXTERMINATE\n");
267*25408Sbloom 			if (doreboot)
26825366Sbloom 				printf("REBOOT");
26925366Sbloom 			if (halt)
27025366Sbloom 				printf(" HALT");
27125366Sbloom 			if (fast)
27225366Sbloom 				printf(" %s (without fsck's)\n", nosync);
27325366Sbloom 			else
27425366Sbloom 				printf(" %s\n", nosync);
27525366Sbloom 
2762373Swnj #endif
2772373Swnj 			finish();
2782373Swnj 		}
279*25408Sbloom 		stogo = sdt - time((long *) 0);
2808841Smckusick 		if (stogo > 0 && sint > 0)
281*25408Sbloom 			sleep((unsigned)(sint<stogo ? sint : stogo));
2822373Swnj 		stogo -= sint;
2838841Smckusick 		first = 0;
2842373Swnj 	}
2852373Swnj }
2862373Swnj 
2872373Swnj time_t
2882373Swnj getsdt(s)
2898841Smckusick 	register char *s;
2902373Swnj {
2912373Swnj 	time_t t, t1, tim;
2922373Swnj 	register char c;
2932373Swnj 	struct tm *lt;
2942373Swnj 
2958841Smckusick 	if (strcmp(s, "now") == 0)
2968841Smckusick 		return(nowtime);
2972373Swnj 	if (*s == '+') {
2982373Swnj 		++s;
2992373Swnj 		t = 0;
3002373Swnj 		for (;;) {
3012373Swnj 			c = *s++;
3022373Swnj 			if (!isdigit(c))
3032373Swnj 				break;
3042373Swnj 			t = t * 10 + c - '0';
3052373Swnj 		}
3062373Swnj 		if (t <= 0)
3072373Swnj 			t = 5;
3082373Swnj 		t *= 60;
309*25408Sbloom 		tim = time((long *) 0) + t;
3102373Swnj 		return(tim);
3112373Swnj 	}
3122373Swnj 	t = 0;
3132373Swnj 	while (strlen(s) > 2 && isdigit(*s))
3142373Swnj 		t = t * 10 + *s++ - '0';
3152373Swnj 	if (*s == ':')
3162373Swnj 		s++;
3172373Swnj 	if (t > 23)
3182373Swnj 		goto badform;
3192373Swnj 	tim = t*60;
3202373Swnj 	t = 0;
3212373Swnj 	while (isdigit(*s))
3222373Swnj 		t = t * 10 + *s++ - '0';
3232373Swnj 	if (t > 59)
3242373Swnj 		goto badform;
3252373Swnj 	tim += t;
3262373Swnj 	tim *= 60;
327*25408Sbloom 	t1 = time((long *) 0);
3282373Swnj 	lt = localtime(&t1);
3292373Swnj 	t = lt->tm_sec + lt->tm_min*60 + lt->tm_hour*3600;
3302373Swnj 	if (tim < t || tim >= (24*3600)) {
3312373Swnj 		/* before now or after midnight */
3322373Swnj 		printf("That must be tomorrow\nCan't you wait till then?\n");
3332373Swnj 		finish();
3342373Swnj 	}
3358841Smckusick 	return (t1 + tim - t);
3362373Swnj badform:
3372373Swnj 	printf("Bad time format\n");
3382373Swnj 	finish();
339*25408Sbloom 	/*NOTREACHED*/
3402373Swnj }
3412373Swnj 
3428841Smckusick warn(term, sdt, now, type)
3432373Swnj 	FILE *term;
3448841Smckusick 	time_t sdt, now;
3458841Smckusick 	char *type;
3462373Swnj {
3472373Swnj 	char *ts;
3488841Smckusick 	register delay = sdt - now;
3492373Swnj 
3503880Swnj 	if (delay > 8)
3513880Swnj 		while (delay % 5)
3523880Swnj 			delay++;
3533880Swnj 
3543880Swnj 	if (shutter)
3553880Swnj 		fprintf(term,
35613178Sroot 	    "\007\007\t*** %sSystem shutdown message from %s@%s ***\r\n\n",
35713178Sroot 		    type, shutter, hostname);
3583880Swnj 	else
3593880Swnj 		fprintf(term,
3608841Smckusick 		    "\007\007\t*** %sSystem shutdown message (%s) ***\r\n\n",
3618841Smckusick 		    type, hostname);
3628841Smckusick 
3632373Swnj 	ts = ctime(&sdt);
3648841Smckusick 	if (delay > 10 MINUTES)
3658841Smckusick 		fprintf(term, "System going down at %5.5s\r\n", ts+11);
3668841Smckusick 	else if (delay > 95 SECONDS) {
3678841Smckusick 		fprintf(term, "System going down in %d minute%s\r\n",
3688841Smckusick 		    (delay+30)/60, (delay+30)/60 != 1 ? "s" : "");
3698841Smckusick 	} else if (delay > 0) {
3708841Smckusick 		fprintf(term, "System going down in %d second%s\r\n",
3718841Smckusick 		    delay, delay != 1 ? "s" : "");
3722373Swnj 	} else
3738841Smckusick 		fprintf(term, "System going down IMMEDIATELY\r\n");
3742373Swnj }
3752373Swnj 
37625366Sbloom doitfast()
37725366Sbloom {
37825366Sbloom 	FILE *fastd;
37925366Sbloom 
38025366Sbloom 	if ((fastd = fopen(fastboot, "w")) != NULL) {
38125366Sbloom 		putc('\n', fastd);
382*25408Sbloom 		(void) fclose(fastd);
38325366Sbloom 	}
38425366Sbloom }
38525366Sbloom 
3862373Swnj nolog(sdt)
3878841Smckusick 	time_t sdt;
3882373Swnj {
3892373Swnj 	FILE *nologf;
3902373Swnj 	register char **mess;
3912373Swnj 
392*25408Sbloom 	(void) unlink(nologin);			/* in case linked to std file */
3932373Swnj 	if ((nologf = fopen(nologin, "w")) != NULL) {
3942373Swnj 		fprintf(nologf, nolog1, (ctime(&sdt)) + 11);
395*25408Sbloom 		(void) putc('\t', nologf);
3962373Swnj 		for (mess = nolog2; *mess; mess++)
3973880Swnj 			fprintf(nologf, " %s", *mess);
398*25408Sbloom 		(void) putc('\n', nologf);
399*25408Sbloom 		(void) fclose(nologf);
4002373Swnj 	}
4012373Swnj }
4022373Swnj 
4032373Swnj finish()
4042373Swnj {
405*25408Sbloom 	(void) signal(SIGTERM, SIG_IGN);
406*25408Sbloom 	(void) unlink(nologin);
4072373Swnj 	exit(0);
4082373Swnj }
4092373Swnj 
41015920Skarels timeout()
4112373Swnj {
41215920Skarels 	longjmp(alarmbuf, 1);
4132373Swnj }
4142818Swnj 
4152818Swnj /*
4162818Swnj  * make an entry in the shutdown log
4172818Swnj  */
4182818Swnj 
4193484Sroot char *days[] = {
4203484Sroot 	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
4213484Sroot };
4223484Sroot 
4233484Sroot char *months[] = {
4243484Sroot 	"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
4253484Sroot 	"Oct", "Nov", "Dec"
4263484Sroot };
4273484Sroot 
4283484Sroot log_entry(now)
4298841Smckusick 	time_t now;
4302818Swnj {
4313484Sroot 	register FILE *fp;
4323484Sroot 	register char **mess;
4333484Sroot 	struct tm *tm, *localtime();
4342818Swnj 
4353484Sroot 	tm = localtime(&now);
4363484Sroot 	fp = fopen(LOGFILE, "a");
4378841Smckusick 	if (fp == NULL) {
4388841Smckusick 		printf("Shutdown: log entry failed\n");
4393578Sroot 		return;
4408841Smckusick 	}
441*25408Sbloom 	(void) fseek(fp, 0L, 2);
4423484Sroot 	fprintf(fp, "%02d:%02d  %s %s %2d, %4d.  Shutdown:", tm->tm_hour,
4433484Sroot 		tm->tm_min, days[tm->tm_wday], months[tm->tm_mon],
4443484Sroot 		tm->tm_mday, tm->tm_year + 1900);
4452818Swnj 	for (mess = nolog2; *mess; mess++)
4463484Sroot 		fprintf(fp, " %s", *mess);
4473880Swnj 	if (shutter)
4488841Smckusick 		fprintf(fp, " (by %s!%s)", hostname, shutter);
449*25408Sbloom 	(void) fputc('\n', fp);
450*25408Sbloom 	(void) fclose(fp);
4512818Swnj }
452