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