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