xref: /original-bsd/sbin/shutdown/shutdown.c (revision ad93c43e)
1 /*
2  * Copyright (c) 1983,1986 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 char copyright[] =
9 "@(#) Copyright (c) 1983,1986 Regents of the University of California.\n\
10  All rights reserved.\n";
11 #endif not lint
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)shutdown.c	5.7 (Berkeley) 12/26/87";
15 #endif not lint
16 
17 #include <stdio.h>
18 #include <ctype.h>
19 #include <signal.h>
20 #include <setjmp.h>
21 #include <utmp.h>
22 #include <pwd.h>
23 #include <sys/time.h>
24 #include <sys/resource.h>
25 #include <sys/param.h>
26 #include <sys/syslog.h>
27 
28 /*
29  *	/etc/shutdown when [messages]
30  *
31  *	allow super users to tell users and remind users
32  *	of iminent shutdown of unix
33  *	and shut it down automatically
34  *	and even reboot or halt the machine if they desire
35  */
36 
37 #define	REBOOT	"/etc/reboot"
38 #define	HALT	"/etc/halt"
39 #define MAXINTS 20
40 #define	HOURS	*3600
41 #define MINUTES	*60
42 #define SECONDS
43 #define NLOG		600		/* no of bytes possible for message */
44 #define	NOLOGTIME	5 MINUTES
45 #define IGNOREUSER	"sleeper"
46 
47 char	hostname[MAXHOSTNAMELEN];
48 
49 int	timeout();
50 time_t	getsdt();
51 void	finish();
52 
53 extern	char *ctime();
54 extern	struct tm *localtime();
55 extern	long time();
56 
57 extern	char *strcpy();
58 extern	char *strncat();
59 extern	off_t lseek();
60 
61 struct	utmp utmp;
62 int	sint;
63 int	stogo;
64 char	tpath[] =	"/dev/";
65 int	nlflag = 1;		/* nolog yet to be done */
66 int	killflg = 1;
67 int	doreboot = 0;
68 int	halt = 0;
69 int     fast = 0;
70 char    *nosync = NULL;
71 char    nosyncflag[] = "-n";
72 char	term[sizeof tpath + sizeof utmp.ut_line];
73 char	tbuf[BUFSIZ];
74 char	nolog1[] = "\n\nNO LOGINS: System going down at %5.5s\n\n";
75 char	nolog2[NLOG+1];
76 #ifdef	DEBUG
77 char	nologin[] = "nologin";
78 char    fastboot[] = "fastboot";
79 #else
80 char	nologin[] = "/etc/nologin";
81 char	fastboot[] = "/fastboot";
82 #endif
83 time_t	nowtime;
84 jmp_buf	alarmbuf;
85 
86 struct interval {
87 	int stogo;
88 	int sint;
89 } interval[] = {
90 	4 HOURS,	1 HOURS,
91 	2 HOURS,	30 MINUTES,
92 	1 HOURS,	15 MINUTES,
93 	30 MINUTES,	10 MINUTES,
94 	15 MINUTES,	5 MINUTES,
95 	10 MINUTES,	5 MINUTES,
96 	5 MINUTES,	3 MINUTES,
97 	2 MINUTES,	1 MINUTES,
98 	1 MINUTES,	30 SECONDS,
99 	0 SECONDS,	0 SECONDS
100 };
101 
102 char *shutter, *getlogin();
103 
104 main(argc,argv)
105 	int argc;
106 	char **argv;
107 {
108 	register i, ufd;
109 	register char *f;
110 	char *ts;
111 	time_t sdt;
112 	int h, m;
113 	int first;
114 	FILE *termf;
115 	struct passwd *pw, *getpwuid();
116 	extern char *strcat();
117 	extern uid_t geteuid();
118 
119 	shutter = getlogin();
120 	if (shutter == 0 && (pw = getpwuid(getuid())))
121 		shutter = pw->pw_name;
122 	if (shutter == 0)
123 		shutter = "???";
124 	(void) gethostname(hostname, sizeof (hostname));
125 	openlog("shutdown", 0, LOG_AUTH);
126 	argc--, argv++;
127 	while (argc > 0 && (f = argv[0], *f++ == '-')) {
128 		while (i = *f++) switch (i) {
129 		case 'k':
130 			killflg = 0;
131 			continue;
132 		case 'n':
133 			nosync = nosyncflag;
134 			continue;
135 		case 'f':
136 			fast = 1;
137 			continue;
138 		case 'r':
139 			doreboot = 1;
140 			continue;
141 		case 'h':
142 			halt = 1;
143 			continue;
144 		default:
145 			fprintf(stderr, "shutdown: '%c' - unknown flag\n", i);
146 			exit(1);
147 		}
148 		argc--, argv++;
149 	}
150 	if (argc < 1) {
151 	        /* argv[0] is not available after the argument handling. */
152 		printf("Usage: shutdown [ -krhfn ] shutdowntime [ message ]\n");
153 		finish();
154 	}
155 	if (fast && (nosync == nosyncflag)) {
156 	        printf ("shutdown: Incompatible switches 'fast' & 'nosync'\n");
157 		finish();
158 	}
159 	if (geteuid()) {
160 		fprintf(stderr, "NOT super-user\n");
161 		finish();
162 	}
163 	nowtime = time((long *)0);
164 	sdt = getsdt(argv[0]);
165 	argc--, argv++;
166 	nolog2[0] = '\0';
167 	while (argc-- > 0) {
168 		(void) strcat(nolog2, " ");
169 		(void) strcat(nolog2, *argv++);
170 	}
171 	m = ((stogo = sdt - nowtime) + 30)/60;
172 	h = m/60;
173 	m %= 60;
174 	ts = ctime(&sdt);
175 	printf("Shutdown at %5.5s (in ", ts+11);
176 	if (h > 0)
177 		printf("%d hour%s ", h, h != 1 ? "s" : "");
178 	printf("%d minute%s) ", m, m != 1 ? "s" : "");
179 #ifndef DEBUG
180 	(void) signal(SIGHUP, SIG_IGN);
181 	(void) signal(SIGQUIT, SIG_IGN);
182 	(void) signal(SIGINT, SIG_IGN);
183 #endif
184 	(void) signal(SIGTTOU, SIG_IGN);
185 	(void) signal(SIGTERM, finish);
186 	(void) signal(SIGALRM, timeout);
187 	(void) setpriority(PRIO_PROCESS, 0, PRIO_MIN);
188 	(void) fflush(stdout);
189 #ifndef DEBUG
190 	if (i = fork()) {
191 		printf("[pid %d]\n", i);
192 		exit(0);
193 	}
194 #else
195 	(void) putc('\n', stdout);
196 #endif
197 	sint = 1 HOURS;
198 	f = "";
199 	ufd = open("/etc/utmp",0);
200 	if (ufd < 0) {
201 		perror("shutdown: /etc/utmp");
202 		exit(1);
203 	}
204 	first = 1;
205 	for (;;) {
206 		for (i = 0; stogo <= interval[i].stogo && interval[i].sint; i++)
207 			sint = interval[i].sint;
208 		if (stogo > 0 && (stogo-sint) < interval[i].stogo)
209 			sint = stogo - interval[i].stogo;
210 		if (stogo <= NOLOGTIME && nlflag) {
211 			nlflag = 0;
212 			nolog(sdt);
213 		}
214 		if (sint >= stogo || sint == 0)
215 			f = "FINAL ";
216 		nowtime = time((long *)0);
217 		(void) lseek(ufd, 0L, 0);
218 		while (read(ufd,(char *)&utmp,sizeof utmp)==sizeof utmp)
219 		if (utmp.ut_name[0] &&
220 		    strncmp(utmp.ut_name, IGNOREUSER, sizeof(utmp.ut_name))) {
221 			if (setjmp(alarmbuf))
222 				continue;
223 			(void) strcpy(term, tpath);
224 			(void) strncat(term, utmp.ut_line, sizeof utmp.ut_line);
225 			(void) alarm(3);
226 #ifdef DEBUG
227 			if ((termf = stdout) != NULL)
228 #else
229 			if ((termf = fopen(term, "w")) != NULL)
230 #endif
231 			{
232 				(void) alarm(0);
233 				setbuf(termf, tbuf);
234 				fprintf(termf, "\n\r\n");
235 				warn(termf, sdt, nowtime, f);
236 				if (first || sdt - nowtime > 1 MINUTES) {
237 					if (*nolog2)
238 						fprintf(termf, "\t...%s", nolog2);
239 				}
240 				(void) fputc('\r', termf);
241 				(void) fputc('\n', termf);
242 				(void) alarm(5);
243 #ifdef DEBUG
244 				(void) fflush(termf);
245 #else
246 				(void) fclose(termf);
247 #endif
248 				(void) alarm(0);
249 			}
250 		}
251 		if (stogo <= 0) {
252 			printf("\n\007\007System shutdown time has arrived\007\007\n");
253 			syslog(LOG_CRIT, "%s by %s: %s",
254 			    doreboot ? "reboot" : halt ? "halt" : "shutdown",
255 			    shutter, nolog2);
256 			sleep(2);
257 			(void) unlink(nologin);
258 			if (!killflg) {
259 				printf("but you'll have to do it yourself\n");
260 				finish();
261 			}
262 			if (fast)
263 				doitfast();
264 #ifndef DEBUG
265 			if (doreboot)
266 				execle(REBOOT, "reboot", "-l", nosync, 0, 0);
267 			if (halt)
268 				execle(HALT, "halt", "-l", nosync, 0, 0);
269 			(void) kill(1, SIGTERM);	/* to single user */
270 #else
271 			if (doreboot)
272 				printf("REBOOT");
273 			if (halt)
274 				printf(" HALT");
275 			if (fast)
276 				printf(" -l %s (without fsck's)\n", nosync);
277 			else
278 				printf(" -l %s\n", nosync);
279 			else
280 				printf("kill -HUP 1\n");
281 
282 #endif
283 			finish();
284 		}
285 		stogo = sdt - time((long *) 0);
286 		if (stogo > 0 && sint > 0)
287 			sleep((unsigned)(sint<stogo ? sint : stogo));
288 		stogo -= sint;
289 		first = 0;
290 	}
291 }
292 
293 time_t
294 getsdt(s)
295 	register char *s;
296 {
297 	time_t t, t1, tim;
298 	register char c;
299 	struct tm *lt;
300 
301 	if (strcmp(s, "now") == 0)
302 		return(nowtime);
303 	if (*s == '+') {
304 		++s;
305 		t = 0;
306 		for (;;) {
307 			c = *s++;
308 			if (!isdigit(c))
309 				break;
310 			t = t * 10 + c - '0';
311 		}
312 		if (t <= 0)
313 			t = 5;
314 		t *= 60;
315 		tim = time((long *) 0) + t;
316 		return(tim);
317 	}
318 	t = 0;
319 	while (strlen(s) > 2 && isdigit(*s))
320 		t = t * 10 + *s++ - '0';
321 	if (*s == ':')
322 		s++;
323 	if (t > 23)
324 		goto badform;
325 	tim = t*60;
326 	t = 0;
327 	while (isdigit(*s))
328 		t = t * 10 + *s++ - '0';
329 	if (t > 59)
330 		goto badform;
331 	tim += t;
332 	tim *= 60;
333 	t1 = time((long *) 0);
334 	lt = localtime(&t1);
335 	t = lt->tm_sec + lt->tm_min*60 + lt->tm_hour*3600;
336 	if (tim < t || tim >= (24*3600)) {
337 		/* before now or after midnight */
338 		printf("That must be tomorrow\nCan't you wait till then?\n");
339 		finish();
340 	}
341 	return (t1 + tim - t);
342 badform:
343 	printf("Bad time format\n");
344 	finish();
345 	/*NOTREACHED*/
346 }
347 
348 warn(term, sdt, now, type)
349 	FILE *term;
350 	time_t sdt, now;
351 	char *type;
352 {
353 	char *ts;
354 	register delay = sdt - now;
355 
356 	if (delay > 8)
357 		while (delay % 5)
358 			delay++;
359 
360 	fprintf(term,
361 	    "\007\007\t*** %sSystem shutdown message from %s@%s ***\r\n\n",
362 		    type, shutter, hostname);
363 
364 	ts = ctime(&sdt);
365 	if (delay > 10 MINUTES)
366 		fprintf(term, "System going down at %5.5s\r\n", ts+11);
367 	else if (delay > 95 SECONDS) {
368 		fprintf(term, "System going down in %d minute%s\r\n",
369 		    (delay+30)/60, (delay+30)/60 != 1 ? "s" : "");
370 	} else if (delay > 0) {
371 		fprintf(term, "System going down in %d second%s\r\n",
372 		    delay, delay != 1 ? "s" : "");
373 	} else
374 		fprintf(term, "System going down IMMEDIATELY\r\n");
375 }
376 
377 doitfast()
378 {
379 	FILE *fastd;
380 
381 	if ((fastd = fopen(fastboot, "w")) != NULL) {
382 		putc('\n', fastd);
383 		(void) fclose(fastd);
384 	}
385 }
386 
387 nolog(sdt)
388 	time_t sdt;
389 {
390 	FILE *nologf;
391 
392 	(void) unlink(nologin);			/* in case linked to std file */
393 	if ((nologf = fopen(nologin, "w")) != NULL) {
394 		fprintf(nologf, nolog1, (ctime(&sdt)) + 11);
395 		if (*nolog2)
396 			fprintf(nologf, "\t%s\n", nolog2 + 1);
397 		(void) fclose(nologf);
398 	}
399 }
400 
401 void
402 finish()
403 {
404 	(void) signal(SIGTERM, SIG_IGN);
405 	(void) unlink(nologin);
406 	exit(0);
407 }
408 
409 timeout()
410 {
411 	longjmp(alarmbuf, 1);
412 }
413