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