xref: /csrg-svn/sbin/shutdown/shutdown.c (revision 3720)
1*3720Sroot /*	@(#)shutdown.c	4.6 (Berkeley/Melbourne) 81/05/10	*/
22373Swnj 
32373Swnj #include <stdio.h>
42373Swnj #include <ctype.h>
52373Swnj #include <signal.h>
62373Swnj #include <utmp.h>
72373Swnj #include <time.h>
82373Swnj #include <sys/types.h>
92373Swnj /*
102373Swnj  *	/etc/shutdown when [messages]
112373Swnj  *
122373Swnj  *	allow super users to tell users and remind users
132373Swnj  *	of iminent shutdown of unix
142373Swnj  *	and shut it down automatically
152373Swnj  *	and even reboot or halt the machine if they desire
162373Swnj  *
172373Swnj  *		Ian Johnstone, Sydney, 1977
182373Swnj  *		Robert Elz, Melbourne, 1978
192373Swnj  *		Peter Lamb, Melbourne, 1980
202373Swnj  *		William Joy, Berkeley, 1981
212818Swnj  *		Michael Toy, Berkeley, 1981
222373Swnj  */
232818Swnj #ifdef DEBUG
242818Swnj #define LOGFILE "shutdown.log"
252818Swnj #else
262818Swnj #define LOGFILE "/usr/adm/shutdownlog"
272818Swnj #endif
282373Swnj #define	REBOOT	"/etc/reboot"
292373Swnj #define	HALT	"/etc/halt"
302373Swnj #define MAXINTS 20
312373Swnj #define	HOURS	*3600
322373Swnj #define MINUTES	*60
332373Swnj #define SECONDS
342373Swnj #define NLOG		20		/* no of lines possible for message */
352373Swnj #define	NOLOGTIME	5 MINUTES
362373Swnj 
372373Swnj int	do_nothing();
382373Swnj time_t	getsdt();
392373Swnj 
402373Swnj extern	char *ctime();
412373Swnj extern	struct tm *localtime();
422373Swnj 
432373Swnj struct	utmp utmp;
442373Swnj int	sint;
452373Swnj int	stogo;
462373Swnj char	tpath[] =	"/dev/";
472373Swnj int	nlflag = 1;		/* nolog yet to be done */
482373Swnj int	killflg = 1;
492373Swnj int	reboot = 0;
502373Swnj int	halt = 0;
512373Swnj char	term[sizeof tpath + sizeof utmp.ut_line];
522373Swnj char	tbuf[BUFSIZ];
532373Swnj char	nolog1[] = "\n\nNO LOGINS: System going down at %5.5s\n\n";
542373Swnj char	*nolog2[NLOG+1];
552373Swnj #ifdef	DEBUG
562373Swnj char	nologin[] = "nologin";
572373Swnj #else
582373Swnj char	nologin[] = "/etc/nologin";
592373Swnj #endif
602373Swnj int slots;
612373Swnj struct interval {
622373Swnj 	int stogo;
632373Swnj 	int sint;
642373Swnj } interval[] = {
652373Swnj 	4 HOURS,	1 HOURS,
662373Swnj 	2 HOURS,	30 MINUTES,
672373Swnj 	1 HOURS,	15 MINUTES,
682373Swnj 	30 MINUTES,	10 MINUTES,
692373Swnj 	15 MINUTES,	5 MINUTES,
702818Swnj 	10 MINUTES,	5 MINUTES,
712818Swnj 	5 MINUTES,	3 MINUTES,
722373Swnj 	2 MINUTES,	30 SECONDS,
732373Swnj 	0 SECONDS,	0 SECONDS
742373Swnj };
752818Swnj char *shutter, *getlogin();
762373Swnj main(argc,argv)
772373Swnj 	int argc;
782373Swnj 	char **argv;
792373Swnj {
802373Swnj 	register i, ufd;
812373Swnj 	register char **mess, *f;
822373Swnj 	char *ts;
832373Swnj 	long sdt;
842373Swnj 	int h, m;
852373Swnj 	long nowtime;
862373Swnj 	FILE *termf;
872373Swnj 
882818Swnj 	shutter = getlogin();
892373Swnj 	argc--, argv++;
902373Swnj 	while (argc > 0 && (f = argv[0], *f++ == '-')) {
912373Swnj 		while (i = *f++) switch (i) {
922373Swnj 		case 'k':
932373Swnj 			killflg = 0;
942373Swnj 			continue;
952373Swnj 		case 'r':
962373Swnj 			reboot = 1;
972373Swnj 			continue;
982373Swnj 		case 'h':
992373Swnj 			halt = 1;
1002373Swnj 			continue;
1012373Swnj 		default:
1022373Swnj 			fprintf(stderr, "shutdown: '%c' - unknown flag\n", i);
1032373Swnj 			exit(1);
1042373Swnj 		}
1052373Swnj 		argc--, argv++;
1062373Swnj 	}
1072373Swnj 	if (argc < 1) {
108*3720Sroot 		printf("Usage: %s [ -krd ] shutdowntime [ message ]\n",
1092373Swnj 		    argv[0]);
1102373Swnj 		finish();
1112373Swnj 	}
112*3720Sroot 	if (geteuid()) {
113*3720Sroot 		fprintf(stderr, "NOT super-user\n");
1143648Stoy 		finish();
1153648Stoy 	}
1162373Swnj 	sdt = getsdt(argv[0]);
1172373Swnj 	argc--, argv++;
1182373Swnj 	i = 0;
1192373Swnj 	while (argc-- > 0)
1202373Swnj 		if (i < NLOG)
1212373Swnj 			nolog2[i++] = *argv++;
1222373Swnj 	nolog2[i] = NULL;
1232373Swnj 	nowtime = time((long *)0);
1242373Swnj 	m = ((stogo = sdt - nowtime) + 30)/60;
1252373Swnj 	h = m/60;
1262373Swnj 	m %= 60;
1272373Swnj 	ts = ctime(&sdt);
1282818Swnj 	printf("Shutdown at %5.5s (in ", ts+11);
1292373Swnj 	if (h > 0)
1302373Swnj 		printf("%d hour%s ", h, h != 1 ? "s" : "");
1312818Swnj 	printf("%d minute%s) ", m, m != 1 ? "s" : "");
1322373Swnj #ifndef DEBUG
1332373Swnj 	signal(SIGHUP, SIG_IGN);
1342373Swnj 	signal(SIGQUIT, SIG_IGN);
1352373Swnj 	signal(SIGINT, SIG_IGN);
1362373Swnj #endif
1372373Swnj 	signal(SIGTERM, finish);
1382373Swnj 	signal(SIGALRM, do_nothing);
1392373Swnj 	nice(-20);
1402818Swnj #ifndef DEBUG
1412373Swnj 	if (i = fork()) {
1422818Swnj 		printf("[pid %d]\n", i);
1432373Swnj 		exit(0);
1442373Swnj 	}
1452818Swnj #endif
1462373Swnj 	sint = 1 HOURS;
1472373Swnj 	f = "";
1482373Swnj 	for (;;) {
1492373Swnj 		for (i = 0; stogo <= interval[i].stogo && interval[i].sint; i++)
1502373Swnj 			sint = interval[i].sint;
1512373Swnj 		if (stogo <= NOLOGTIME && nlflag) {
1522373Swnj 			nlflag = 0;
1532373Swnj 			nolog(sdt);
1542373Swnj 		}
1552373Swnj 		if (sint >= stogo || sint == 0)
1562373Swnj 			f = "FINAL ";
1572373Swnj 		ufd = open("/etc/utmp",0);
1582373Swnj 		nowtime = time((long *) 0);
1592373Swnj 		while (read(ufd,&utmp,sizeof utmp)==sizeof utmp)
1602373Swnj 		if (utmp.ut_name[0]) {
1612373Swnj 			strcpy(term, tpath);
1622373Swnj 			strncat(term, utmp.ut_line, sizeof utmp.ut_line);
1632373Swnj 			alarm(3);
1642373Swnj #ifdef DEBUG
1652818Swnj 			if ((termf = stdout) != NULL)
1662373Swnj #else
1672373Swnj 			if ((termf = fopen(term, "w")) != NULL)
1682373Swnj #endif
1692373Swnj 			{
1702373Swnj 				alarm(0);
1712373Swnj 				setbuf(termf, tbuf);
1722373Swnj 				fprintf(termf, "\n\n");
1732373Swnj 				warn(termf, sdt, nowtime);
1742373Swnj 				if (sdt - nowtime > 1 MINUTES)
1752373Swnj 					for (mess = nolog2; *mess; mess++)
1762373Swnj 						fprintf(termf, "%s ", *mess);
1772373Swnj 				fputc('\n', termf);
1782818Swnj 				alarm(5);
1792818Swnj #ifdef DEBUG
1802818Swnj 				fflush(termf);
1812818Swnj #else
1822373Swnj 				fclose(termf);
1832818Swnj #endif
1842373Swnj 				alarm(0);
1852373Swnj 			}
1862373Swnj 		}
1872373Swnj 		if (stogo < 0) {
1882373Swnj 	printf("\n\007\007System shutdown time has arrived\007\007\n");
1892818Swnj 			log_entry(sdt);
1902373Swnj 			unlink(nologin);
1912373Swnj 			if (!killflg) {
1922373Swnj 				printf("but you'll have to do it yourself\n");
1932373Swnj 				finish();
1942373Swnj 			}
1952373Swnj #ifndef DEBUG
1962373Swnj 			if (reboot)
1972373Swnj 				execle(REBOOT, "reboot", 0, 0);
1982373Swnj 			if (halt)
1992373Swnj 				execle(HALT, "halt", 0, 0);
2002373Swnj 			kill(1, SIGTERM);	/* sync */
2012373Swnj 			kill(1, SIGTERM);	/* sync */
2022373Swnj 			sleep(20);
2032373Swnj #else
2042373Swnj 			printf("EXTERMINATE EXTERMINATE\n");
2052373Swnj #endif
2062373Swnj 			finish();
2072373Swnj 		}
2082373Swnj 		stogo = sdt - time((long *) 0);
2092373Swnj 		if (stogo > 0)
2102373Swnj 			sleep(sint<stogo ? sint : stogo);
2112373Swnj 		stogo -= sint;
2122373Swnj 	}
2132373Swnj }
2142373Swnj 
2152373Swnj time_t
2162373Swnj getsdt(s)
2172373Swnj register char *s;
2182373Swnj {
2192373Swnj 	time_t t, t1, tim;
2202373Swnj 	register char c;
2212373Swnj 	struct tm *lt;
2222373Swnj 
2232373Swnj 	if (*s == '+') {
2242373Swnj 		++s;
2252373Swnj 		t = 0;
2262373Swnj 		for (;;) {
2272373Swnj 			c = *s++;
2282373Swnj 			if (!isdigit(c))
2292373Swnj 				break;
2302373Swnj 			t = t * 10 + c - '0';
2312373Swnj 		}
2322373Swnj 		if (t <= 0)
2332373Swnj 			t = 5;
2342373Swnj 		t *= 60;
2352373Swnj 		tim = time((long *) 0) + t;
2362373Swnj 		return(tim);
2372373Swnj 	}
2382373Swnj 	t = 0;
2392373Swnj 	while (strlen(s) > 2 && isdigit(*s))
2402373Swnj 		t = t * 10 + *s++ - '0';
2412373Swnj 	if (*s == ':')
2422373Swnj 		s++;
2432373Swnj 	if (t > 23)
2442373Swnj 		goto badform;
2452373Swnj 	tim = t*60;
2462373Swnj 	t = 0;
2472373Swnj 	while (isdigit(*s))
2482373Swnj 		t = t * 10 + *s++ - '0';
2492373Swnj 	if (t > 59)
2502373Swnj 		goto badform;
2512373Swnj 	tim += t;
2522373Swnj 	tim *= 60;
2532373Swnj 	t1 = time((long *) 0);
2542373Swnj 	lt = localtime(&t1);
2552373Swnj 	t = lt->tm_sec + lt->tm_min*60 + lt->tm_hour*3600;
2562373Swnj 	if (tim < t || tim >= (24*3600)) {
2572373Swnj 		/* before now or after midnight */
2582373Swnj 		printf("That must be tomorrow\nCan't you wait till then?\n");
2592373Swnj 		finish();
2602373Swnj 	}
2612373Swnj 	return (t1 + tim -t);
2622373Swnj badform:
2632373Swnj 	printf("Bad time format\n");
2642373Swnj 	finish();
2652373Swnj }
2662373Swnj 
2672373Swnj warn(term, sdt, nowtime)
2682373Swnj 	FILE *term;
2692373Swnj 	long sdt, nowtime;
2702373Swnj {
2712373Swnj 	char *ts;
2722373Swnj 
2732818Swnj 	fprintf(term, "\007\007*** System shutdown message from %s ***\n", shutter);
2742373Swnj 	ts = ctime(&sdt);
2752373Swnj 	if (sdt - nowtime > 10 MINUTES)
2762373Swnj 		fprintf(term, "System going down at %5.5s\n", ts+11);
2772373Swnj 	else if ( sdt - nowtime > 60 SECONDS ) {
2782373Swnj 		fprintf(term, "System going down in %d minute%s\n",
2792373Swnj 		(sdt-nowtime+30)/60, (sdt-nowtime+30)/60 != 1 ? "s" : "");
2802373Swnj 	} else if ( sdt - nowtime > 0 ) {
2812818Swnj 		fprintf(term, "System going down in %d second%s\n",
2822373Swnj 		sdt-nowtime, sdt-nowtime != 1 ? "s" : "");
2832373Swnj 	} else
2842373Swnj 		fprintf(term, "System going down IMMEDIATELY\n");
2852373Swnj }
2862373Swnj 
2872373Swnj nolog(sdt)
2882373Swnj 	long sdt;
2892373Swnj {
2902373Swnj 	FILE *nologf;
2912373Swnj 	register char **mess;
2922373Swnj 
2932373Swnj 	if ((nologf = fopen(nologin, "w")) != NULL) {
2942373Swnj 		fprintf(nologf, nolog1, (ctime(&sdt)) + 11);
2952373Swnj 		for (mess = nolog2; *mess; mess++)
2962373Swnj 			fprintf(nologf, "\t%s\n", *mess);
2972373Swnj 		fclose(nologf);
2982373Swnj 	}
2992373Swnj }
3002373Swnj 
3012373Swnj finish()
3022373Swnj {
3032373Swnj 	signal(SIGTERM, SIG_IGN);
3042373Swnj 	unlink(nologin);
3052373Swnj 	exit(0);
3062373Swnj }
3072373Swnj 
3082373Swnj do_nothing()
3092373Swnj {
3102373Swnj 
3112373Swnj 	signal(SIGALRM, do_nothing);
3122373Swnj }
3132818Swnj 
3142818Swnj /*
3152818Swnj  * make an entry in the shutdown log
3162818Swnj  */
3172818Swnj 
3183484Sroot char *days[] = {
3193484Sroot 	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
3203484Sroot };
3213484Sroot 
3223484Sroot char *months[] = {
3233484Sroot 	"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
3243484Sroot 	"Oct", "Nov", "Dec"
3253484Sroot };
3263484Sroot 
3273484Sroot log_entry(now)
3283484Sroot time_t now;
3292818Swnj {
3303484Sroot 	register FILE *fp;
3313484Sroot 	register char **mess;
3323484Sroot 	struct tm *tm, *localtime();
3332818Swnj 
3343484Sroot 	tm = localtime(&now);
3353484Sroot 	fp = fopen(LOGFILE, "a");
3363578Sroot 	if (fp==0)
3373578Sroot 		return;
3383484Sroot 	fseek(fp, 0L, 2);
3393484Sroot 	fprintf(fp, "%02d:%02d  %s %s %2d, %4d.  Shutdown:", tm->tm_hour,
3403484Sroot 		tm->tm_min, days[tm->tm_wday], months[tm->tm_mon],
3413484Sroot 		tm->tm_mday, tm->tm_year + 1900);
3422818Swnj 	for (mess = nolog2; *mess; mess++)
3433484Sroot 		fprintf(fp, " %s", *mess);
3443484Sroot 	fputc('\n', fp);
3453484Sroot 	fclose(fp);
3462818Swnj }
347