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