insure stdout is unbuffered
[unix-history] / usr / src / sbin / shutdown / shutdown.c
CommitLineData
7b602762 1/* @(#)shutdown.c 4.5 (Berkeley/Melbourne) 81/05/04 */
954bb1fe
BJ
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
8fa8eeab 21 * Michael Toy, Berkeley, 1981
954bb1fe 22 */
8fa8eeab
BJ
23#ifdef DEBUG
24#define LOGFILE "shutdown.log"
25#else
26#define LOGFILE "/usr/adm/shutdownlog"
27#endif
954bb1fe
BJ
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
37int do_nothing();
38time_t getsdt();
39
40extern char *ctime();
41extern struct tm *localtime();
42
43struct utmp utmp;
44int sint;
45int stogo;
46char tpath[] = "/dev/";
47int nlflag = 1; /* nolog yet to be done */
48int killflg = 1;
49int reboot = 0;
7b602762 50int noroot = 0;
954bb1fe
BJ
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
61int slots;
62struct interval {
63 int stogo;
64 int sint;
65} interval[] = {
66 4 HOURS, 1 HOURS,
67 2 HOURS, 30 MINUTES,
68 1 HOURS, 15 MINUTES,
69 30 MINUTES, 10 MINUTES,
70 15 MINUTES, 5 MINUTES,
8fa8eeab
BJ
71 10 MINUTES, 5 MINUTES,
72 5 MINUTES, 3 MINUTES,
954bb1fe
BJ
73 2 MINUTES, 30 SECONDS,
74 0 SECONDS, 0 SECONDS
75};
8fa8eeab 76char *shutter, *getlogin();
954bb1fe
BJ
77main(argc,argv)
78 int argc;
79 char **argv;
80{
81 register i, ufd;
82 register char **mess, *f;
83 char *ts;
84 long sdt;
85 int h, m;
86 long nowtime;
87 FILE *termf;
88
8fa8eeab 89 shutter = getlogin();
954bb1fe
BJ
90 argc--, argv++;
91 while (argc > 0 && (f = argv[0], *f++ == '-')) {
92 while (i = *f++) switch (i) {
93 case 'k':
94 killflg = 0;
95 continue;
96 case 'r':
97 reboot = 1;
98 continue;
99 case 'h':
100 halt = 1;
101 continue;
7b602762
MT
102 case 'y':
103 noroot = 1;
104 continue;
954bb1fe
BJ
105 default:
106 fprintf(stderr, "shutdown: '%c' - unknown flag\n", i);
107 exit(1);
108 }
109 argc--, argv++;
110 }
111 if (argc < 1) {
7b602762 112 printf("Usage: %s [-krdy] shutdowntime [nologmessage]\n",
954bb1fe
BJ
113 argv[0]);
114 finish();
115 }
7b602762
MT
116 if (noroot == 0 && geteuid() != 0)
117 {
118 printf("Must specify -y flag to run as normal user.\n");
119 finish();
120 }
954bb1fe
BJ
121 sdt = getsdt(argv[0]);
122 argc--, argv++;
123 i = 0;
124 while (argc-- > 0)
125 if (i < NLOG)
126 nolog2[i++] = *argv++;
127 nolog2[i] = NULL;
128 nowtime = time((long *)0);
129 m = ((stogo = sdt - nowtime) + 30)/60;
130 h = m/60;
131 m %= 60;
132 ts = ctime(&sdt);
8fa8eeab 133 printf("Shutdown at %5.5s (in ", ts+11);
954bb1fe
BJ
134 if (h > 0)
135 printf("%d hour%s ", h, h != 1 ? "s" : "");
8fa8eeab 136 printf("%d minute%s) ", m, m != 1 ? "s" : "");
954bb1fe
BJ
137#ifndef DEBUG
138 signal(SIGHUP, SIG_IGN);
139 signal(SIGQUIT, SIG_IGN);
140 signal(SIGINT, SIG_IGN);
141#endif
142 signal(SIGTERM, finish);
143 signal(SIGALRM, do_nothing);
144 nice(-20);
8fa8eeab 145#ifndef DEBUG
954bb1fe 146 if (i = fork()) {
8fa8eeab 147 printf("[pid %d]\n", i);
954bb1fe
BJ
148 exit(0);
149 }
8fa8eeab 150#endif
954bb1fe
BJ
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 strcpy(term, tpath);
167 strncat(term, utmp.ut_line, sizeof utmp.ut_line);
168 alarm(3);
169#ifdef DEBUG
8fa8eeab 170 if ((termf = stdout) != NULL)
954bb1fe
BJ
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);
8fa8eeab
BJ
183 alarm(5);
184#ifdef DEBUG
185 fflush(termf);
186#else
954bb1fe 187 fclose(termf);
8fa8eeab 188#endif
954bb1fe
BJ
189 alarm(0);
190 }
191 }
192 if (stogo < 0) {
193 printf("\n\007\007System shutdown time has arrived\007\007\n");
8fa8eeab 194 log_entry(sdt);
954bb1fe
BJ
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
220time_t
221getsdt(s)
222register 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);
267badform:
268 printf("Bad time format\n");
269 finish();
270}
271
272warn(term, sdt, nowtime)
273 FILE *term;
274 long sdt, nowtime;
275{
276 char *ts;
277
8fa8eeab 278 fprintf(term, "\007\007*** System shutdown message from %s ***\n", shutter);
954bb1fe
BJ
279 ts = ctime(&sdt);
280 if (sdt - nowtime > 10 MINUTES)
281 fprintf(term, "System going down at %5.5s\n", ts+11);
282 else if ( sdt - nowtime > 60 SECONDS ) {
283 fprintf(term, "System going down in %d minute%s\n",
284 (sdt-nowtime+30)/60, (sdt-nowtime+30)/60 != 1 ? "s" : "");
285 } else if ( sdt - nowtime > 0 ) {
8fa8eeab 286 fprintf(term, "System going down in %d second%s\n",
954bb1fe
BJ
287 sdt-nowtime, sdt-nowtime != 1 ? "s" : "");
288 } else
289 fprintf(term, "System going down IMMEDIATELY\n");
290}
291
292nolog(sdt)
293 long sdt;
294{
295 FILE *nologf;
296 register char **mess;
297
298 if ((nologf = fopen(nologin, "w")) != NULL) {
299 fprintf(nologf, nolog1, (ctime(&sdt)) + 11);
300 for (mess = nolog2; *mess; mess++)
301 fprintf(nologf, "\t%s\n", *mess);
302 fclose(nologf);
303 }
304}
305
306finish()
307{
308 signal(SIGTERM, SIG_IGN);
309 unlink(nologin);
310 exit(0);
311}
312
313do_nothing()
314{
315
316 signal(SIGALRM, do_nothing);
317}
8fa8eeab
BJ
318
319/*
320 * make an entry in the shutdown log
321 */
322
f8969a9a
BJ
323char *days[] = {
324 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
325};
326
327char *months[] = {
328 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
329 "Oct", "Nov", "Dec"
330};
331
332log_entry(now)
333time_t now;
8fa8eeab 334{
f8969a9a
BJ
335 register FILE *fp;
336 register char **mess;
337 struct tm *tm, *localtime();
8fa8eeab 338
f8969a9a
BJ
339 tm = localtime(&now);
340 fp = fopen(LOGFILE, "a");
3f51f330
BJ
341 if (fp==0)
342 return;
f8969a9a
BJ
343 fseek(fp, 0L, 2);
344 fprintf(fp, "%02d:%02d %s %s %2d, %4d. Shutdown:", tm->tm_hour,
345 tm->tm_min, days[tm->tm_wday], months[tm->tm_mon],
346 tm->tm_mday, tm->tm_year + 1900);
8fa8eeab 347 for (mess = nolog2; *mess; mess++)
f8969a9a
BJ
348 fprintf(fp, " %s", *mess);
349 fputc('\n', fp);
350 fclose(fp);
8fa8eeab 351}