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