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