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