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