xref: /csrg-svn/sbin/shutdown/shutdown.c (revision 35528)
121179Sdist /*
2*35528Sbostic  * Copyright (c) 1988 Regents of the University of California.
3*35528Sbostic  * All rights reserved.
4*35528Sbostic  *
5*35528Sbostic  * Redistribution and use in source and binary forms are permitted
6*35528Sbostic  * provided that the above copyright notice and this paragraph are
7*35528Sbostic  * duplicated in all such forms and that any documentation,
8*35528Sbostic  * advertising materials, and other materials related to such
9*35528Sbostic  * distribution and use acknowledge that the software was developed
10*35528Sbostic  * by the University of California, Berkeley.  The name of the
11*35528Sbostic  * University may not be used to endorse or promote products derived
12*35528Sbostic  * from this software without specific prior written permission.
13*35528Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14*35528Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*35528Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1621179Sdist  */
1721179Sdist 
1812682Ssam #ifndef lint
1921179Sdist char copyright[] =
20*35528Sbostic "@(#) Copyright (c) 1988 Regents of the University of California.\n\
2121179Sdist  All rights reserved.\n";
22*35528Sbostic #endif /* not lint */
232373Swnj 
2421179Sdist #ifndef lint
25*35528Sbostic static char sccsid[] = "@(#)shutdown.c	5.8 (Berkeley) 09/18/88";
26*35528Sbostic #endif /* not lint */
2721179Sdist 
28*35528Sbostic #include <sys/param.h>
29*35528Sbostic #include <sys/time.h>
30*35528Sbostic #include <sys/file.h>
31*35528Sbostic #include <sys/resource.h>
32*35528Sbostic #include <sys/syslog.h>
332373Swnj #include <signal.h>
3415920Skarels #include <setjmp.h>
35*35528Sbostic #include <tzfile.h>
3628802Skarels #include <pwd.h>
37*35528Sbostic #include <stdio.h>
38*35528Sbostic #include <ctype.h>
3928024Seric 
40*35528Sbostic #define	REBOOT		"/etc/reboot"
41*35528Sbostic #define	HALT		"/etc/halt"
4228024Seric 
43*35528Sbostic #ifdef DEBUG
44*35528Sbostic #define	NOLOGIN		"./nologin"
45*35528Sbostic #define	FASTBOOT	"./fastboot"
462373Swnj #else
47*35528Sbostic #define	NOLOGIN		"/etc/nologin"
48*35528Sbostic #define	FASTBOOT	"/fastboot"
492373Swnj #endif
508841Smckusick 
51*35528Sbostic #define	H		*60*60
52*35528Sbostic #define	M		*60
53*35528Sbostic #define	S		*1
54*35528Sbostic #define	NOLOG_TIME	5*60
552373Swnj struct interval {
56*35528Sbostic 	int timeleft, timetowait;
57*35528Sbostic } tlist[] = {
58*35528Sbostic 	10 H,  5 H,	 5 H,  3 H,	 2 H,  1 H,	1 H, 30 M,
59*35528Sbostic 	30 M, 10 M,	20 M, 10 M,	10 M,  5 M,	5 M,  3 M,
60*35528Sbostic 	 2 M,  1 M,	 1 M, 30 S,	30 S, 30 S,
61*35528Sbostic 	 0, 0,
62*35528Sbostic }, *tp = tlist;
63*35528Sbostic #undef H
64*35528Sbostic #undef M
65*35528Sbostic #undef S
668841Smckusick 
67*35528Sbostic static time_t offset, shuttime;
68*35528Sbostic static int dofast, dohalt, doreboot, killflg, mbuflen;
69*35528Sbostic static char *nosync, *whom, mbuf[BUFSIZ];
708841Smckusick 
71*35528Sbostic main(argc, argv)
722373Swnj 	int argc;
732373Swnj 	char **argv;
742373Swnj {
75*35528Sbostic 	extern int optind;
76*35528Sbostic 	register char *p;
77*35528Sbostic 	int arglen, ch, len;
7828802Skarels 	struct passwd *pw, *getpwuid();
79*35528Sbostic 	char *strcat(), *getlogin();
80*35528Sbostic 	uid_t geteuid();
812373Swnj 
82*35528Sbostic #ifndef DEBUG
83*35528Sbostic 	if (geteuid()) {
84*35528Sbostic 		fprintf(stderr, "shutdown: NOT super-user\n");
85*35528Sbostic 		exit(1);
86*35528Sbostic 	}
87*35528Sbostic #endif
88*35528Sbostic 	nosync = "";
89*35528Sbostic 	while ((ch = getopt(argc, argv, "fhknr")) != EOF)
90*35528Sbostic 		switch((char)ch) {
91*35528Sbostic 		case 'f':
92*35528Sbostic 			dofast = 1;
93*35528Sbostic 			break;
94*35528Sbostic 		case 'h':
95*35528Sbostic 			dohalt = 1;
96*35528Sbostic 			break;
972373Swnj 		case 'k':
98*35528Sbostic 			killflg = 1;
99*35528Sbostic 			break;
10025366Sbloom 		case 'n':
101*35528Sbostic 			nosync = "-n";
102*35528Sbostic 			break;
1032373Swnj 		case 'r':
10425408Sbloom 			doreboot = 1;
105*35528Sbostic 			break;
106*35528Sbostic 		case '?':
1072373Swnj 		default:
108*35528Sbostic 			usage();
1092373Swnj 		}
110*35528Sbostic 	argc -= optind;
111*35528Sbostic 	argv += optind;
112*35528Sbostic 
113*35528Sbostic 	if (argc < 1)
114*35528Sbostic 		usage();
115*35528Sbostic 
116*35528Sbostic 	if (dofast && *nosync) {
117*35528Sbostic 		fprintf(stderr,
118*35528Sbostic 		    "shutdown: incompatible switches -f and -n.\n");
119*35528Sbostic 		usage();
1202373Swnj 	}
121*35528Sbostic 	if (doreboot && dohalt) {
122*35528Sbostic 		fprintf(stderr,
123*35528Sbostic 		    "shutdown: incompatible switches -h and -r.\n");
124*35528Sbostic 		usage();
1252373Swnj 	}
126*35528Sbostic 	getoffset(*argv++);
127*35528Sbostic 	if (*argv) {
128*35528Sbostic 		do {
129*35528Sbostic 			(void)strcat(mbuf, *argv);
130*35528Sbostic 			(void)strcat(mbuf, " ");
131*35528Sbostic 		} while(*++argv);
132*35528Sbostic 		(void)strcat(mbuf, "\n");
13325366Sbloom 	}
134*35528Sbostic 	else for (len = sizeof(mbuf), p = mbuf; fgets(p, len, stdin);) {
135*35528Sbostic 		arglen = strlen(p);
136*35528Sbostic 		if (!(len -= arglen))
137*35528Sbostic 			break;
138*35528Sbostic 		p += arglen;
1393648Stoy 	}
140*35528Sbostic 	mbuflen = strlen(mbuf);
141*35528Sbostic 
142*35528Sbostic 	if (offset)
143*35528Sbostic 		printf("Shutdown at %.24s.", ctime(&shuttime));
144*35528Sbostic 	else
145*35528Sbostic 		printf("Shutdown NOW!\n");
146*35528Sbostic 
147*35528Sbostic 	if (!(whom = getlogin()))
148*35528Sbostic 		whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
149*35528Sbostic 
150*35528Sbostic #ifdef DEBUG
151*35528Sbostic 	(void)putc('\n', stdout);
152*35528Sbostic #else
153*35528Sbostic 	(void)setpriority(PRIO_PROCESS, 0, PRIO_MIN);
154*35528Sbostic 	{
155*35528Sbostic 		int forkpid;
156*35528Sbostic 
157*35528Sbostic 		forkpid = fork();
158*35528Sbostic 		if (forkpid == -1) {
159*35528Sbostic 			perror("shutdown: fork");
160*35528Sbostic 			exit(1);
161*35528Sbostic 		}
162*35528Sbostic 		if (forkpid) {
163*35528Sbostic 			printf("shutdown: [pid %d]\n", forkpid);
164*35528Sbostic 			exit(0);
165*35528Sbostic 		}
16628024Seric 	}
1672373Swnj #endif
168*35528Sbostic 	openlog("shutdown", LOG_CONS, LOG_AUTH);
169*35528Sbostic 	loop();
170*35528Sbostic 	/*NOTREACHED*/
171*35528Sbostic }
172*35528Sbostic 
173*35528Sbostic #define	WALL_CMD	"/bin/wall"
174*35528Sbostic 
175*35528Sbostic loop()
176*35528Sbostic {
177*35528Sbostic 	u_int sltime;
178*35528Sbostic 	int logged;
179*35528Sbostic 
180*35528Sbostic 	if (offset <= NOLOG_TIME) {
181*35528Sbostic 		logged = 1;
182*35528Sbostic 		nolog();
1832373Swnj 	}
184*35528Sbostic 	else
185*35528Sbostic 		logged = 0;
186*35528Sbostic 	tp = tlist;
187*35528Sbostic 	if (tp->timeleft < offset)
188*35528Sbostic 		(void)sleep((u_int)(offset - tp->timeleft));
189*35528Sbostic 	else {
190*35528Sbostic 		while (offset < tp->timeleft)
191*35528Sbostic 			++tp;
192*35528Sbostic 		/*
193*35528Sbostic 		 * warn now, if going to sleep more than a fifth of
194*35528Sbostic 		 * the next wait time.
195*35528Sbostic 		 */
196*35528Sbostic 		if (sltime = offset - tp->timeleft) {
197*35528Sbostic 			if (sltime > tp->timetowait / 5)
198*35528Sbostic 				warn();
199*35528Sbostic 			(void)sleep(sltime);
200*35528Sbostic 		}
2018841Smckusick 	}
202*35528Sbostic 	for (;; ++tp) {
203*35528Sbostic 		warn();
204*35528Sbostic 		if (!logged && tp->timeleft <= NOLOG_TIME) {
205*35528Sbostic 			logged = 1;
206*35528Sbostic 			nolog();
2072373Swnj 		}
208*35528Sbostic 		(void)sleep((u_int)tp->timetowait);
209*35528Sbostic 		if (!tp->timeleft)
210*35528Sbostic 			break;
211*35528Sbostic 	}
212*35528Sbostic 	die_you_gravy_sucking_pig_dog();
213*35528Sbostic }
21425366Sbloom 
215*35528Sbostic static jmp_buf alarmbuf;
216*35528Sbostic 
217*35528Sbostic warn()
218*35528Sbostic {
219*35528Sbostic 	static int first;
220*35528Sbostic 	static char hostname[MAXHOSTNAMELEN];
221*35528Sbostic 	FILE *pf;
222*35528Sbostic 	char *ctime();
223*35528Sbostic 	int timeout();
224*35528Sbostic 
225*35528Sbostic 	if (!first++) {
226*35528Sbostic 		(void)signal(SIGALRM, timeout);
227*35528Sbostic 		(void)gethostname(hostname, sizeof(hostname));
2282373Swnj 	}
229*35528Sbostic 
230*35528Sbostic 	if (!(pf = popen(WALL_CMD, "w"))) {
231*35528Sbostic 		syslog(LOG_ERR, "shutdown: can't find %s: %m", WALL_CMD);
232*35528Sbostic 		return;
233*35528Sbostic 	}
234*35528Sbostic 
235*35528Sbostic 	fprintf(pf, "*** %sSystem shutdown message ***\n",
236*35528Sbostic 	    tp->timeleft ? "": "FINAL ");
237*35528Sbostic 
238*35528Sbostic 	if (tp->timeleft > 10*60)
239*35528Sbostic 		fprintf(pf, "System going down at %5.5s\n\n",
240*35528Sbostic 		    ctime(&shuttime) + 11);
241*35528Sbostic 	else if (tp->timeleft > 59)
242*35528Sbostic 		fprintf(pf, "System going down in %d minute%s\n\n",
243*35528Sbostic 		    tp->timeleft / 60, (tp->timeleft > 60) ? "s" : "");
244*35528Sbostic 	else if (tp->timeleft)
245*35528Sbostic 		fprintf(pf, "System going down in 30 seconds\n\n");
246*35528Sbostic 	else
247*35528Sbostic 		fprintf(pf, "System going down IMMEDIATELY\n\n");
248*35528Sbostic 
249*35528Sbostic 	(void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf);
250*35528Sbostic 
251*35528Sbostic 	/*
252*35528Sbostic 	 * play some games, just in case wall doesn't come back
253*35528Sbostic 	 * probably unecessary, given that wall is careful.
254*35528Sbostic 	 */
255*35528Sbostic 	if (!setjmp(alarmbuf)) {
256*35528Sbostic 		(void)alarm((u_int)30);
257*35528Sbostic 		(void)pclose(pf);
258*35528Sbostic 		(void)alarm((u_int)0);
259*35528Sbostic 	}
2602373Swnj }
2612373Swnj 
262*35528Sbostic timeout()
2632373Swnj {
264*35528Sbostic 	longjmp(alarmbuf, 1);
265*35528Sbostic }
2662373Swnj 
267*35528Sbostic die_you_gravy_sucking_pig_dog()
268*35528Sbostic {
269*35528Sbostic 	syslog(LOG_NOTICE, "%s by %s: %s",
270*35528Sbostic 	    doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf);
271*35528Sbostic 	(void)sleep(2);
272*35528Sbostic 
273*35528Sbostic 	printf("\r\nSystem shutdown time has arrived\007\007\r\n");
274*35528Sbostic 	if (killflg) {
275*35528Sbostic 		printf("\rbut you'll have to do it yourself\r\n");
2762373Swnj 		finish();
2772373Swnj 	}
278*35528Sbostic 	if (dofast)
279*35528Sbostic 		doitfast();
280*35528Sbostic #ifdef DEBUG
281*35528Sbostic 	if (doreboot)
282*35528Sbostic 		printf("REBOOT");
283*35528Sbostic 	if (dohalt)
284*35528Sbostic 		printf(" HALT");
285*35528Sbostic 	if (dofast)
286*35528Sbostic 		printf(" -l %s (without fsck's)\n", nosync);
287*35528Sbostic 	else
288*35528Sbostic 		printf(" -l %s\n", nosync);
289*35528Sbostic 	printf("kill -HUP 1\n");
290*35528Sbostic #else
291*35528Sbostic 	if (doreboot) {
292*35528Sbostic 		execle(REBOOT, "reboot", "-l", nosync, 0, 0);
293*35528Sbostic 		syslog(LOG_ERR, "shutdown: can't exec %s: %m.", REBOOT);
294*35528Sbostic 		perror("shutdown");
295*35528Sbostic 	}
296*35528Sbostic 	else if (dohalt) {
297*35528Sbostic 		execle(HALT, "halt", "-l", nosync, 0, 0);
298*35528Sbostic 		syslog(LOG_ERR, "shutdown: can't exec %s: %m.", HALT);
299*35528Sbostic 		perror("shutdown");
300*35528Sbostic 	}
301*35528Sbostic 	(void)kill(1, SIGTERM);		/* to single user */
302*35528Sbostic #endif
3032373Swnj 	finish();
3042373Swnj }
3052373Swnj 
306*35528Sbostic #define	ATOI2(p)	(p[0] - '0') * 10 + (p[1] - '0'); p += 2;
307*35528Sbostic static int dmsize[] =
308*35528Sbostic 	{ -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
309*35528Sbostic 
310*35528Sbostic getoffset(timearg)
311*35528Sbostic 	register char *timearg;
3122373Swnj {
313*35528Sbostic 	register struct tm *lt;
314*35528Sbostic 	register char *p;
315*35528Sbostic 	time_t now, time();
316*35528Sbostic 	int year, month, day, hour, min;
3172373Swnj 
318*35528Sbostic 	if (!strcasecmp(timearg, "now")) {		/* now */
319*35528Sbostic 		offset = 0;
320*35528Sbostic 		return;
321*35528Sbostic 	}
3223880Swnj 
323*35528Sbostic 	(void)time(&now);
324*35528Sbostic 	if (*timearg == '+') {				/* +minutes */
325*35528Sbostic 		if (!isdigit(*++timearg))
326*35528Sbostic 			goto badtime;
327*35528Sbostic 		min = atoi(timearg);
328*35528Sbostic 		offset = min * 60;
329*35528Sbostic 		shuttime = now + offset;
330*35528Sbostic 		return;
331*35528Sbostic 	}
3328841Smckusick 
333*35528Sbostic 	/* handle hh:mm by getting rid of the colon */
334*35528Sbostic 	for (p = timearg; *p; ++p)
335*35528Sbostic 		if (!isascii(*p) || !isdigit(*p))
336*35528Sbostic 			if (*p == ':' && strlen(p) == 3) {
337*35528Sbostic 				p[0] = p[1];
338*35528Sbostic 				p[1] = p[2];
339*35528Sbostic 				p[2] = '\0';
340*35528Sbostic 			}
341*35528Sbostic 			else
342*35528Sbostic 				goto badtime;
343*35528Sbostic 
344*35528Sbostic 	unsetenv("TZ");					/* OUR timezone */
345*35528Sbostic 	lt = localtime(&now);				/* [yymmdd]hhmm */
346*35528Sbostic 	year = lt->tm_year;
347*35528Sbostic 	month = lt->tm_mon + 1;
348*35528Sbostic 	day = lt->tm_mday;
349*35528Sbostic 
350*35528Sbostic 	switch(strlen(timearg)) {
351*35528Sbostic 	case 10:
352*35528Sbostic 		year = ATOI2(timearg);
353*35528Sbostic 		/* FALLTHROUGH */
354*35528Sbostic 	case 8:
355*35528Sbostic 		month = ATOI2(timearg);
356*35528Sbostic 		/* FALLTHROUGH */
357*35528Sbostic 	case 6:
358*35528Sbostic 		day = ATOI2(timearg);
359*35528Sbostic 		/* FALLTHROUGH */
360*35528Sbostic 	case 4:
361*35528Sbostic 		hour = ATOI2(timearg);
362*35528Sbostic 		min = ATOI2(timearg);
363*35528Sbostic 		if (month < 1 || month > 12 || day < 1 || day > 31 ||
364*35528Sbostic 		    hour < 0 || hour > 23 || min < 0 || min > 59)
365*35528Sbostic 			goto badtime;
366*35528Sbostic 		shuttime = 0;
367*35528Sbostic 		year += TM_YEAR_BASE;
368*35528Sbostic 		if (isleap(year) && month > 2)
369*35528Sbostic 			++shuttime;
370*35528Sbostic 		for (--year; year >= EPOCH_YEAR; --year)
371*35528Sbostic 			shuttime += isleap(year) ?
372*35528Sbostic 			    DAYS_PER_LYEAR : DAYS_PER_NYEAR;
373*35528Sbostic 		while (--month)
374*35528Sbostic 			shuttime += dmsize[month];
375*35528Sbostic 		shuttime += day - 1;
376*35528Sbostic 		shuttime = HOURS_PER_DAY * shuttime + hour;
377*35528Sbostic 		shuttime = MINS_PER_HOUR * shuttime + min;
378*35528Sbostic 		shuttime *= SECS_PER_MIN;
379*35528Sbostic 		shuttime -= lt->tm_gmtoff;
380*35528Sbostic 		if ((offset = shuttime - now) >= 0)
381*35528Sbostic 			break;
382*35528Sbostic 		/* FALLTHROUGH */
383*35528Sbostic 	default:
384*35528Sbostic badtime:	fprintf(stderr,
385*35528Sbostic 		    "shutdown: bad time format, or already past.\n");
386*35528Sbostic 		exit(1);
387*35528Sbostic 	}
3882373Swnj }
3892373Swnj 
390*35528Sbostic #define	FSMSG	"fastboot file for fsck\n"
39125366Sbloom doitfast()
39225366Sbloom {
393*35528Sbostic 	int fastfd;
39425366Sbloom 
395*35528Sbostic 	if ((fastfd = open(FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC, 0664)) >= 0) {
396*35528Sbostic 		(void)write(fastfd, FSMSG, sizeof(FSMSG) - 1);
397*35528Sbostic 		(void)close(fastfd);
39825366Sbloom 	}
39925366Sbloom }
40025366Sbloom 
401*35528Sbostic #define	NOMSG	"\n\nNO LOGINS: System going down at "
402*35528Sbostic nolog()
4032373Swnj {
404*35528Sbostic 	int logfd, finish();
405*35528Sbostic 	char *ct, *ctime();
4062373Swnj 
407*35528Sbostic 	(void)unlink(NOLOGIN);		/* in case linked to another file */
408*35528Sbostic 	(void)signal(SIGINT, finish);
409*35528Sbostic 	(void)signal(SIGHUP, finish);
410*35528Sbostic 	(void)signal(SIGQUIT, finish);
411*35528Sbostic 	(void)signal(SIGTERM, finish);
412*35528Sbostic 	if ((logfd = open(NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 0664)) >= 0) {
413*35528Sbostic 		(void)write(logfd, NOMSG, sizeof(NOMSG) - 1);
414*35528Sbostic 		ct = ctime(&shuttime);
415*35528Sbostic 		(void)write(logfd, ct + 11, 5);
416*35528Sbostic 		(void)write(logfd, "\n\n", 2);
417*35528Sbostic 		(void)write(logfd, mbuf, strlen(mbuf));
418*35528Sbostic 		(void)close(logfd);
4192373Swnj 	}
4202373Swnj }
4212373Swnj 
4222373Swnj finish()
4232373Swnj {
424*35528Sbostic 	(void)unlink(NOLOGIN);
4252373Swnj 	exit(0);
4262373Swnj }
4272373Swnj 
428*35528Sbostic usage()
4292373Swnj {
430*35528Sbostic 	fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n");
431*35528Sbostic 	exit(1);
4322373Swnj }
433