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