xref: /csrg-svn/sbin/shutdown/shutdown.c (revision 15920)
112682Ssam #ifndef lint
2*15920Skarels static	char *sccsid = "@(#)shutdown.c	4.20 (Berkeley) 84/02/02";
312682Ssam #endif
42373Swnj 
52373Swnj #include <stdio.h>
62373Swnj #include <ctype.h>
72373Swnj #include <signal.h>
8*15920Skarels #include <setjmp.h>
92373Swnj #include <utmp.h>
1012897Ssam #include <sys/time.h>
1112897Ssam #include <sys/resource.h>
122373Swnj #include <sys/types.h>
132373Swnj /*
142373Swnj  *	/etc/shutdown when [messages]
152373Swnj  *
162373Swnj  *	allow super users to tell users and remind users
172373Swnj  *	of iminent shutdown of unix
182373Swnj  *	and shut it down automatically
192373Swnj  *	and even reboot or halt the machine if they desire
202373Swnj  */
212818Swnj #ifdef DEBUG
222818Swnj #define LOGFILE "shutdown.log"
232818Swnj #else
242818Swnj #define LOGFILE "/usr/adm/shutdownlog"
252818Swnj #endif
262373Swnj #define	REBOOT	"/etc/reboot"
272373Swnj #define	HALT	"/etc/halt"
282373Swnj #define MAXINTS 20
292373Swnj #define	HOURS	*3600
302373Swnj #define MINUTES	*60
312373Swnj #define SECONDS
323880Swnj #define NLOG		20		/* no of args possible for message */
332373Swnj #define	NOLOGTIME	5 MINUTES
343885Sroot #define IGNOREUSER	"sleeper"
352373Swnj 
366203Sroot char	hostname[32];
376203Sroot 
38*15920Skarels int	timeout();
392373Swnj time_t	getsdt();
402373Swnj 
412373Swnj extern	char *ctime();
422373Swnj extern	struct tm *localtime();
432373Swnj 
442373Swnj struct	utmp utmp;
452373Swnj int	sint;
462373Swnj int	stogo;
472373Swnj char	tpath[] =	"/dev/";
482373Swnj int	nlflag = 1;		/* nolog yet to be done */
492373Swnj int	killflg = 1;
502373Swnj int	reboot = 0;
512373Swnj int	halt = 0;
522373Swnj char	term[sizeof tpath + sizeof utmp.ut_line];
532373Swnj char	tbuf[BUFSIZ];
542373Swnj char	nolog1[] = "\n\nNO LOGINS: System going down at %5.5s\n\n";
552373Swnj char	*nolog2[NLOG+1];
562373Swnj #ifdef	DEBUG
572373Swnj char	nologin[] = "nologin";
582373Swnj #else
592373Swnj char	nologin[] = "/etc/nologin";
602373Swnj #endif
618841Smckusick time_t	nowtime;
62*15920Skarels jmp_buf	alarmbuf;
638841Smckusick 
642373Swnj struct interval {
652373Swnj 	int stogo;
662373Swnj 	int sint;
672373Swnj } interval[] = {
682373Swnj 	4 HOURS,	1 HOURS,
692373Swnj 	2 HOURS,	30 MINUTES,
702373Swnj 	1 HOURS,	15 MINUTES,
712373Swnj 	30 MINUTES,	10 MINUTES,
722373Swnj 	15 MINUTES,	5 MINUTES,
732818Swnj 	10 MINUTES,	5 MINUTES,
742818Swnj 	5 MINUTES,	3 MINUTES,
758841Smckusick 	2 MINUTES,	1 MINUTES,
768841Smckusick 	1 MINUTES,	30 SECONDS,
772373Swnj 	0 SECONDS,	0 SECONDS
782373Swnj };
798841Smckusick 
802818Swnj char *shutter, *getlogin();
818841Smckusick 
822373Swnj main(argc,argv)
832373Swnj 	int argc;
842373Swnj 	char **argv;
852373Swnj {
862373Swnj 	register i, ufd;
872373Swnj 	register char **mess, *f;
882373Swnj 	char *ts;
898841Smckusick 	time_t sdt;
902373Swnj 	int h, m;
918841Smckusick 	int first;
922373Swnj 	FILE *termf;
932373Swnj 
942818Swnj 	shutter = getlogin();
956203Sroot 	gethostname(hostname, sizeof (hostname));
962373Swnj 	argc--, argv++;
972373Swnj 	while (argc > 0 && (f = argv[0], *f++ == '-')) {
982373Swnj 		while (i = *f++) switch (i) {
992373Swnj 		case 'k':
1002373Swnj 			killflg = 0;
1012373Swnj 			continue;
1022373Swnj 		case 'r':
1032373Swnj 			reboot = 1;
1042373Swnj 			continue;
1052373Swnj 		case 'h':
1062373Swnj 			halt = 1;
1072373Swnj 			continue;
1082373Swnj 		default:
1092373Swnj 			fprintf(stderr, "shutdown: '%c' - unknown flag\n", i);
1102373Swnj 			exit(1);
1112373Swnj 		}
1122373Swnj 		argc--, argv++;
1132373Swnj 	}
1142373Swnj 	if (argc < 1) {
1153880Swnj 		printf("Usage: %s [ -krh ] shutdowntime [ message ]\n",
1162373Swnj 		    argv[0]);
1172373Swnj 		finish();
1182373Swnj 	}
1193720Sroot 	if (geteuid()) {
1203720Sroot 		fprintf(stderr, "NOT super-user\n");
1213648Stoy 		finish();
1223648Stoy 	}
1238841Smckusick 	nowtime = time((time_t *)0);
1242373Swnj 	sdt = getsdt(argv[0]);
1252373Swnj 	argc--, argv++;
1262373Swnj 	i = 0;
1272373Swnj 	while (argc-- > 0)
1282373Swnj 		if (i < NLOG)
1292373Swnj 			nolog2[i++] = *argv++;
1302373Swnj 	nolog2[i] = NULL;
1312373Swnj 	m = ((stogo = sdt - nowtime) + 30)/60;
1322373Swnj 	h = m/60;
1332373Swnj 	m %= 60;
1342373Swnj 	ts = ctime(&sdt);
1352818Swnj 	printf("Shutdown at %5.5s (in ", ts+11);
1362373Swnj 	if (h > 0)
1372373Swnj 		printf("%d hour%s ", h, h != 1 ? "s" : "");
1382818Swnj 	printf("%d minute%s) ", m, m != 1 ? "s" : "");
1392373Swnj #ifndef DEBUG
1402373Swnj 	signal(SIGHUP, SIG_IGN);
1412373Swnj 	signal(SIGQUIT, SIG_IGN);
1422373Swnj 	signal(SIGINT, SIG_IGN);
1432373Swnj #endif
14412129Sedward 	signal(SIGTTOU, SIG_IGN);
1452373Swnj 	signal(SIGTERM, finish);
146*15920Skarels 	signal(SIGALRM, timeout);
14712897Ssam 	setpriority(PRIO_PROCESS, 0, PRIO_MIN);
1483880Swnj 	fflush(stdout);
1492818Swnj #ifndef DEBUG
1502373Swnj 	if (i = fork()) {
1512818Swnj 		printf("[pid %d]\n", i);
1522373Swnj 		exit(0);
1532373Swnj 	}
1548841Smckusick #else
1558841Smckusick 	putc('\n', stdout);
1562818Swnj #endif
1572373Swnj 	sint = 1 HOURS;
1582373Swnj 	f = "";
1598841Smckusick 	ufd = open("/etc/utmp",0);
1608841Smckusick 	if (ufd < 0) {
1618841Smckusick 		perror("shutdown: /etc/utmp");
1628841Smckusick 		exit(1);
1638841Smckusick 	}
1648841Smckusick 	first = 1;
1652373Swnj 	for (;;) {
1662373Swnj 		for (i = 0; stogo <= interval[i].stogo && interval[i].sint; i++)
1672373Swnj 			sint = interval[i].sint;
1688841Smckusick 		if (stogo > 0 && (stogo-sint) < interval[i].stogo)
1698841Smckusick 			sint = stogo - interval[i].stogo;
1702373Swnj 		if (stogo <= NOLOGTIME && nlflag) {
1712373Swnj 			nlflag = 0;
1722373Swnj 			nolog(sdt);
1732373Swnj 		}
1742373Swnj 		if (sint >= stogo || sint == 0)
1752373Swnj 			f = "FINAL ";
1768841Smckusick 		nowtime = time((time_t *) 0);
1778841Smckusick 		lseek(ufd, 0L, 0);
1782373Swnj 		while (read(ufd,&utmp,sizeof utmp)==sizeof utmp)
1793885Sroot 		if (utmp.ut_name[0] &&
1803885Sroot 		    strncmp(utmp.ut_name, IGNOREUSER, sizeof(utmp.ut_name))) {
181*15920Skarels 			if (setjmp(alarmbuf))
182*15920Skarels 				continue;
1832373Swnj 			strcpy(term, tpath);
1842373Swnj 			strncat(term, utmp.ut_line, sizeof utmp.ut_line);
1852373Swnj 			alarm(3);
1862373Swnj #ifdef DEBUG
1872818Swnj 			if ((termf = stdout) != NULL)
1882373Swnj #else
1892373Swnj 			if ((termf = fopen(term, "w")) != NULL)
1902373Swnj #endif
1912373Swnj 			{
1922373Swnj 				alarm(0);
1932373Swnj 				setbuf(termf, tbuf);
1948841Smckusick 				fprintf(termf, "\n\r\n");
1958841Smckusick 				warn(termf, sdt, nowtime, f);
1968841Smckusick 				if (first || sdt - nowtime > 1 MINUTES) {
1978841Smckusick 					if (*nolog2)
1988841Smckusick 						fprintf(termf, "\t...");
1992373Swnj 					for (mess = nolog2; *mess; mess++)
2008841Smckusick 						fprintf(termf, " %s", *mess);
2018841Smckusick 				}
2028841Smckusick 				fputc('\r', termf);
2032373Swnj 				fputc('\n', termf);
2042818Swnj 				alarm(5);
2052818Swnj #ifdef DEBUG
2062818Swnj 				fflush(termf);
2072818Swnj #else
2082373Swnj 				fclose(termf);
2092818Swnj #endif
2102373Swnj 				alarm(0);
2112373Swnj 			}
2122373Swnj 		}
2138841Smckusick 		if (stogo <= 0) {
2142373Swnj 	printf("\n\007\007System shutdown time has arrived\007\007\n");
2152818Swnj 			log_entry(sdt);
2162373Swnj 			unlink(nologin);
2172373Swnj 			if (!killflg) {
2182373Swnj 				printf("but you'll have to do it yourself\n");
2192373Swnj 				finish();
2202373Swnj 			}
2212373Swnj #ifndef DEBUG
2228841Smckusick 			kill(-1, SIGTERM);	/* terminate everyone */
2238841Smckusick 			sleep(5);		/* & wait while they die */
2242373Swnj 			if (reboot)
2252373Swnj 				execle(REBOOT, "reboot", 0, 0);
2262373Swnj 			if (halt)
2272373Swnj 				execle(HALT, "halt", 0, 0);
2282373Swnj 			kill(1, SIGTERM);	/* sync */
2292373Swnj 			kill(1, SIGTERM);	/* sync */
2302373Swnj 			sleep(20);
2312373Swnj #else
2322373Swnj 			printf("EXTERMINATE EXTERMINATE\n");
2332373Swnj #endif
2342373Swnj 			finish();
2352373Swnj 		}
2368841Smckusick 		stogo = sdt - time((time_t *) 0);
2378841Smckusick 		if (stogo > 0 && sint > 0)
2382373Swnj 			sleep(sint<stogo ? sint : stogo);
2392373Swnj 		stogo -= sint;
2408841Smckusick 		first = 0;
2412373Swnj 	}
2422373Swnj }
2432373Swnj 
2442373Swnj time_t
2452373Swnj getsdt(s)
2468841Smckusick 	register char *s;
2472373Swnj {
2482373Swnj 	time_t t, t1, tim;
2492373Swnj 	register char c;
2502373Swnj 	struct tm *lt;
2512373Swnj 
2528841Smckusick 	if (strcmp(s, "now") == 0)
2538841Smckusick 		return(nowtime);
2542373Swnj 	if (*s == '+') {
2552373Swnj 		++s;
2562373Swnj 		t = 0;
2572373Swnj 		for (;;) {
2582373Swnj 			c = *s++;
2592373Swnj 			if (!isdigit(c))
2602373Swnj 				break;
2612373Swnj 			t = t * 10 + c - '0';
2622373Swnj 		}
2632373Swnj 		if (t <= 0)
2642373Swnj 			t = 5;
2652373Swnj 		t *= 60;
2668841Smckusick 		tim = time((time_t *) 0) + t;
2672373Swnj 		return(tim);
2682373Swnj 	}
2692373Swnj 	t = 0;
2702373Swnj 	while (strlen(s) > 2 && isdigit(*s))
2712373Swnj 		t = t * 10 + *s++ - '0';
2722373Swnj 	if (*s == ':')
2732373Swnj 		s++;
2742373Swnj 	if (t > 23)
2752373Swnj 		goto badform;
2762373Swnj 	tim = t*60;
2772373Swnj 	t = 0;
2782373Swnj 	while (isdigit(*s))
2792373Swnj 		t = t * 10 + *s++ - '0';
2802373Swnj 	if (t > 59)
2812373Swnj 		goto badform;
2822373Swnj 	tim += t;
2832373Swnj 	tim *= 60;
2848841Smckusick 	t1 = time((time_t *) 0);
2852373Swnj 	lt = localtime(&t1);
2862373Swnj 	t = lt->tm_sec + lt->tm_min*60 + lt->tm_hour*3600;
2872373Swnj 	if (tim < t || tim >= (24*3600)) {
2882373Swnj 		/* before now or after midnight */
2892373Swnj 		printf("That must be tomorrow\nCan't you wait till then?\n");
2902373Swnj 		finish();
2912373Swnj 	}
2928841Smckusick 	return (t1 + tim - t);
2932373Swnj badform:
2942373Swnj 	printf("Bad time format\n");
2952373Swnj 	finish();
2962373Swnj }
2972373Swnj 
2988841Smckusick warn(term, sdt, now, type)
2992373Swnj 	FILE *term;
3008841Smckusick 	time_t sdt, now;
3018841Smckusick 	char *type;
3022373Swnj {
3032373Swnj 	char *ts;
3048841Smckusick 	register delay = sdt - now;
3052373Swnj 
3063880Swnj 	if (delay > 8)
3073880Swnj 		while (delay % 5)
3083880Swnj 			delay++;
3093880Swnj 
3103880Swnj 	if (shutter)
3113880Swnj 		fprintf(term,
31213178Sroot 	    "\007\007\t*** %sSystem shutdown message from %s@%s ***\r\n\n",
31313178Sroot 		    type, shutter, hostname);
3143880Swnj 	else
3153880Swnj 		fprintf(term,
3168841Smckusick 		    "\007\007\t*** %sSystem shutdown message (%s) ***\r\n\n",
3178841Smckusick 		    type, hostname);
3188841Smckusick 
3192373Swnj 	ts = ctime(&sdt);
3208841Smckusick 	if (delay > 10 MINUTES)
3218841Smckusick 		fprintf(term, "System going down at %5.5s\r\n", ts+11);
3228841Smckusick 	else if (delay > 95 SECONDS) {
3238841Smckusick 		fprintf(term, "System going down in %d minute%s\r\n",
3248841Smckusick 		    (delay+30)/60, (delay+30)/60 != 1 ? "s" : "");
3258841Smckusick 	} else if (delay > 0) {
3268841Smckusick 		fprintf(term, "System going down in %d second%s\r\n",
3278841Smckusick 		    delay, delay != 1 ? "s" : "");
3282373Swnj 	} else
3298841Smckusick 		fprintf(term, "System going down IMMEDIATELY\r\n");
3302373Swnj }
3312373Swnj 
3322373Swnj nolog(sdt)
3338841Smckusick 	time_t sdt;
3342373Swnj {
3352373Swnj 	FILE *nologf;
3362373Swnj 	register char **mess;
3372373Swnj 
3388841Smckusick 	unlink(nologin);			/* in case linked to std file */
3392373Swnj 	if ((nologf = fopen(nologin, "w")) != NULL) {
3402373Swnj 		fprintf(nologf, nolog1, (ctime(&sdt)) + 11);
3413880Swnj 		putc('\t', nologf);
3422373Swnj 		for (mess = nolog2; *mess; mess++)
3433880Swnj 			fprintf(nologf, " %s", *mess);
3443880Swnj 		putc('\n', nologf);
3452373Swnj 		fclose(nologf);
3462373Swnj 	}
3472373Swnj }
3482373Swnj 
3492373Swnj finish()
3502373Swnj {
3512373Swnj 	signal(SIGTERM, SIG_IGN);
3522373Swnj 	unlink(nologin);
3532373Swnj 	exit(0);
3542373Swnj }
3552373Swnj 
356*15920Skarels timeout()
3572373Swnj {
358*15920Skarels 	longjmp(alarmbuf, 1);
3592373Swnj }
3602818Swnj 
3612818Swnj /*
3622818Swnj  * make an entry in the shutdown log
3632818Swnj  */
3642818Swnj 
3653484Sroot char *days[] = {
3663484Sroot 	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
3673484Sroot };
3683484Sroot 
3693484Sroot char *months[] = {
3703484Sroot 	"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
3713484Sroot 	"Oct", "Nov", "Dec"
3723484Sroot };
3733484Sroot 
3743484Sroot log_entry(now)
3758841Smckusick 	time_t now;
3762818Swnj {
3773484Sroot 	register FILE *fp;
3783484Sroot 	register char **mess;
3793484Sroot 	struct tm *tm, *localtime();
3802818Swnj 
3813484Sroot 	tm = localtime(&now);
3823484Sroot 	fp = fopen(LOGFILE, "a");
3838841Smckusick 	if (fp == NULL) {
3848841Smckusick 		printf("Shutdown: log entry failed\n");
3853578Sroot 		return;
3868841Smckusick 	}
3873484Sroot 	fseek(fp, 0L, 2);
3883484Sroot 	fprintf(fp, "%02d:%02d  %s %s %2d, %4d.  Shutdown:", tm->tm_hour,
3893484Sroot 		tm->tm_min, days[tm->tm_wday], months[tm->tm_mon],
3903484Sroot 		tm->tm_mday, tm->tm_year + 1900);
3912818Swnj 	for (mess = nolog2; *mess; mess++)
3923484Sroot 		fprintf(fp, " %s", *mess);
3933880Swnj 	if (shutter)
3948841Smckusick 		fprintf(fp, " (by %s!%s)", hostname, shutter);
3953484Sroot 	fputc('\n', fp);
3963484Sroot 	fclose(fp);
3972818Swnj }
398