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