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