Merge branch 'vendor/GDB'
[dragonfly.git] / sbin / shutdown / shutdown.c
1 /*
2  * Copyright (c) 1988, 1990, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#) Copyright (c) 1988, 1990, 1993 The Regents of the University of California.  All rights reserved.
30  * @(#)shutdown.c       8.4 (Berkeley) 4/28/95
31  * $FreeBSD: src/sbin/shutdown/shutdown.c,v 1.21.2.1 2001/07/30 10:38:08 dd Exp $
32  * $DragonFly: src/sbin/shutdown/shutdown.c,v 1.6 2005/01/02 01:22:49 cpressey Exp $
33  */
34
35 #include <sys/param.h>
36 #include <sys/time.h>
37 #include <sys/resource.h>
38 #include <sys/syslog.h>
39
40 #include <ctype.h>
41 #include <err.h>
42 #include <fcntl.h>
43 #include <pwd.h>
44 #include <setjmp.h>
45 #include <signal.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50
51 #include "pathnames.h"
52
53 #ifdef DEBUG
54 #undef _PATH_NOLOGIN
55 #define _PATH_NOLOGIN   "./nologin"
56 #endif
57
58 #define H               *60*60
59 #define M               *60
60 #define S               *1
61 #define NOLOG_TIME      5*60
62 struct interval {
63         int timeleft, timetowait;
64 } tlist[] = {
65         { 10 H,  5 H },
66         {  5 H,  3 H },
67         {  2 H,  1 H },
68         {  1 H, 30 M },
69         { 30 M, 10 M },
70         { 20 M, 10 M },
71         { 10 M,  5 M },
72         {  5 M,  3 M },
73         {  2 M,  1 M },
74         {  1 M, 30 S },
75         { 30 S, 30 S },
76         {  0  ,  0   }
77 };
78 #undef H
79 #undef M
80 #undef S
81
82 static time_t offset, shuttime;
83 static int dohalt, dopower, doreboot, killflg, mbuflen, oflag;
84 static char mbuf[BUFSIZ];
85 static const char *nosync, *whom;
86
87 void badtime(void);
88 void die_you_gravy_sucking_pig_dog(void);
89 void finish(int);
90 void getoffset(char *);
91 void loop(void);
92 void nolog(void);
93 void timeout(int);
94 void timewarn(int);
95 void usage(const char *);
96
97 int
98 main(int argc, char **argv)
99 {
100         char *p, *endp;
101         struct passwd *pw;
102         int arglen, ch, len, readstdin;
103
104 #ifndef DEBUG
105         if (geteuid())
106                 errx(1, "NOT super-user");
107 #endif
108         nosync = NULL;
109         readstdin = 0;
110         while ((ch = getopt(argc, argv, "-hknopr")) != -1)
111                 switch (ch) {
112                 case '-':
113                         readstdin = 1;
114                         break;
115                 case 'h':
116                         dohalt = 1;
117                         break;
118                 case 'k':
119                         killflg = 1;
120                         break;
121                 case 'n':
122                         nosync = "-n";
123                         break;
124                 case 'o':
125                         oflag = 1;
126                         break;
127                 case 'p':
128                         dopower = 1;
129                         break;
130                 case 'r':
131                         doreboot = 1;
132                         break;
133                 case '?':
134                 default:
135                         usage(NULL);
136                 }
137         argc -= optind;
138         argv += optind;
139
140         if (argc < 1)
141                 usage(NULL);
142
143         if (killflg + doreboot + dohalt + dopower > 1)
144                 usage("incompatible switches -h, -k, -p and -r");
145
146         if (oflag && !(dohalt || dopower || doreboot))
147                 usage("-o requires -h, -p or -r");
148
149         if (nosync != NULL && !oflag)
150                 usage("-n requires -o");
151
152         getoffset(*argv++);
153
154         if (*argv) {
155                 for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) {
156                         arglen = strlen(*argv);
157                         if ((len -= arglen) <= 2)
158                                 break;
159                         if (p != mbuf)
160                                 *p++ = ' ';
161                         memmove(p, *argv, arglen);
162                         p += arglen;
163                 }
164                 *p = '\n';
165                 *++p = '\0';
166         }
167
168         if (readstdin) {
169                 p = mbuf;
170                 endp = mbuf + sizeof(mbuf) - 2;
171                 for (;;) {
172                         if (!fgets(p, endp - p + 1, stdin))
173                                 break;
174                         for (; *p &&  p < endp; ++p);
175                         if (p == endp) {
176                                 *p = '\n';
177                                 *++p = '\0';
178                                 break;
179                         }
180                 }
181         }
182         mbuflen = strlen(mbuf);
183
184         if (offset)
185                 printf("Shutdown at %.24s.\n", ctime(&shuttime));
186         else
187                 printf("Shutdown NOW!\n");
188
189         if (!(whom = getlogin()))
190                 whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
191
192 #ifdef DEBUG
193         putc('\n', stdout);
194 #else
195         setpriority(PRIO_PROCESS, 0, PRIO_MIN);
196         {
197                 int forkpid;
198
199                 forkpid = fork();
200                 if (forkpid == -1)
201                         err(1, "fork");
202                 if (forkpid)
203                         errx(0, "[pid %d]", forkpid);
204         }
205         setsid();
206 #endif
207         openlog("shutdown", LOG_CONS, LOG_AUTH);
208         loop();
209         return(0);
210 }
211
212 void
213 loop(void)
214 {
215         struct interval *tp;
216         u_int sltime;
217         int logged;
218
219         if (offset <= NOLOG_TIME) {
220                 logged = 1;
221                 nolog();
222         }
223         else
224                 logged = 0;
225         tp = tlist;
226         if (tp->timeleft < offset)
227                 sleep((u_int)(offset - tp->timeleft));
228         else {
229                 while (tp->timeleft && offset < tp->timeleft)
230                         ++tp;
231                 /*
232                  * Warn now, if going to sleep more than a fifth of
233                  * the next wait time.
234                  */
235                 if ((sltime = offset - tp->timeleft)) {
236                         if (sltime > (u_int)(tp->timetowait / 5))
237                                 timewarn(offset);
238                         sleep(sltime);
239                 }
240         }
241         for (;; ++tp) {
242                 timewarn(tp->timeleft);
243                 if (!logged && tp->timeleft <= NOLOG_TIME) {
244                         logged = 1;
245                         nolog();
246                 }
247                 sleep((u_int)tp->timetowait);
248                 if (!tp->timeleft)
249                         break;
250         }
251         die_you_gravy_sucking_pig_dog();
252 }
253
254 static jmp_buf alarmbuf;
255
256 static const char *restricted_environ[] = {
257         "PATH=" _PATH_STDPATH,
258         NULL
259 };
260
261 extern const char **environ;
262
263 void
264 timewarn(int timeleft)
265 {
266         static int first;
267         static char hostname[MAXHOSTNAMELEN + 1];
268         FILE *pf;
269         char wcmd[MAXPATHLEN + 4];
270
271         if (!first++)
272                 gethostname(hostname, sizeof(hostname));
273
274         /* undoc -n option to wall suppresses normal wall banner */
275         snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL);
276         environ = restricted_environ;
277         if (!(pf = popen(wcmd, "w"))) {
278                 syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL);
279                 return;
280         }
281
282         fprintf(pf,
283             "\007*** %sSystem shutdown message from %s@%s ***\007\n",
284             timeleft ? "": "FINAL ", whom, hostname);
285
286         if (timeleft > 10*60)
287                 fprintf(pf, "System going down at %5.5s\n\n",
288                     ctime(&shuttime) + 11);
289         else if (timeleft > 59)
290                 fprintf(pf, "System going down in %d minute%s\n\n",
291                     timeleft / 60, (timeleft > 60) ? "s" : "");
292         else if (timeleft)
293                 fprintf(pf, "System going down in 30 seconds\n\n");
294         else
295                 fprintf(pf, "System going down IMMEDIATELY\n\n");
296
297         if (mbuflen)
298                 fwrite(mbuf, sizeof(*mbuf), mbuflen, pf);
299
300         /*
301          * play some games, just in case wall doesn't come back
302          * probably unnecessary, given that wall is careful.
303          */
304         if (!setjmp(alarmbuf)) {
305                 signal(SIGALRM, timeout);
306                 alarm((u_int)30);
307                 pclose(pf);
308                 alarm((u_int)0);
309                 signal(SIGALRM, SIG_DFL);
310         }
311 }
312
313 void
314 timeout(int signo __unused)
315 {
316         longjmp(alarmbuf, 1);
317 }
318
319 void
320 die_you_gravy_sucking_pig_dog(void)
321 {
322         char *empty_environ[] = { NULL };
323
324         syslog(LOG_NOTICE, "%s by %s: %s",
325             doreboot ? "reboot" : dohalt ? "halt" : dopower ? "power-down" : 
326             "shutdown", whom, mbuf);
327         sleep(2);
328
329         printf("\r\nSystem shutdown time has arrived\007\007\r\n");
330         if (killflg) {
331                 printf("\rbut you'll have to do it yourself\r\n");
332                 exit(0);
333         }
334 #ifdef DEBUG
335         if (doreboot)
336                 printf("reboot");
337         else if (dohalt)
338                 printf("halt");
339         else if (dopower)
340                 printf("power-down");
341         if (nosync != NULL)
342                 printf(" no sync");
343         printf("\nkill -HUP 1\n");
344 #else
345         if (!oflag) {
346                 kill(1, doreboot ? SIGINT :     /* reboot */
347                         dohalt ? SIGUSR1 :      /* halt */
348                         dopower ? SIGUSR2 :     /* power-down */
349                         SIGTERM);               /* single-user */
350         } else {
351                 if (doreboot) {
352                         execle(_PATH_REBOOT, "reboot", "-l", nosync, 
353                                 NULL, empty_environ);
354                         syslog(LOG_ERR, "shutdown: can't exec %s: %m.",
355                                 _PATH_REBOOT);
356                         warn(_PATH_REBOOT);
357                 }
358                 else if (dohalt) {
359                         execle(_PATH_HALT, "halt", "-l", nosync,
360                                 NULL, empty_environ);
361                         syslog(LOG_ERR, "shutdown: can't exec %s: %m.",
362                                 _PATH_HALT);
363                         warn(_PATH_HALT);
364                 }
365                 else if (dopower) {
366                         execle(_PATH_HALT, "halt", "-l", "-p", nosync,
367                                 NULL, empty_environ);
368                         syslog(LOG_ERR, "shutdown: can't exec %s: %m.",
369                                 _PATH_HALT);
370                         warn(_PATH_HALT);
371                 }
372                 kill(1, SIGTERM);               /* to single-user */
373         }
374 #endif
375         finish(0);
376 }
377
378 #define ATOI2(p)        (p[0] - '0') * 10 + (p[1] - '0'); p += 2;
379
380 void
381 getoffset(char *timearg)
382 {
383         struct tm *lt;
384         char *p;
385         time_t now;
386         int this_year;
387
388         time(&now);
389
390         if (!strcasecmp(timearg, "now")) {              /* now */
391                 offset = 0;
392                 shuttime = now;
393                 return;
394         }
395
396         if (*timearg == '+') {                          /* +minutes */
397                 if (!isdigit(*++timearg))
398                         badtime();
399                 if ((offset = atoi(timearg) * 60) < 0)
400                         badtime();
401                 shuttime = now + offset;
402                 return;
403         }
404
405         /* handle hh:mm by getting rid of the colon */
406         for (p = timearg; *p; ++p)
407                 if (!isascii(*p) || !isdigit(*p)) {
408                         if (*p == ':' && strlen(p) == 3) {
409                                 p[0] = p[1];
410                                 p[1] = p[2];
411                                 p[2] = '\0';
412                         }
413                         else
414                                 badtime();
415                 }
416
417         unsetenv("TZ");                                 /* OUR timezone */
418         lt = localtime(&now);                           /* current time val */
419
420         switch(strlen(timearg)) {
421         case 10:
422                 this_year = lt->tm_year;
423                 lt->tm_year = ATOI2(timearg);
424                 /*
425                  * check if the specified year is in the next century.
426                  * allow for one year of user error as many people will
427                  * enter n - 1 at the start of year n.
428                  */
429                 if (lt->tm_year < (this_year % 100) - 1)
430                         lt->tm_year += 100;
431                 /* adjust for the year 2000 and beyond */
432                 lt->tm_year += (this_year - (this_year % 100));
433                 /* FALLTHROUGH */
434         case 8:
435                 lt->tm_mon = ATOI2(timearg);
436                 if (--lt->tm_mon < 0 || lt->tm_mon > 11)
437                         badtime();
438                 /* FALLTHROUGH */
439         case 6:
440                 lt->tm_mday = ATOI2(timearg);
441                 if (lt->tm_mday < 1 || lt->tm_mday > 31)
442                         badtime();
443                 /* FALLTHROUGH */
444         case 4:
445                 lt->tm_hour = ATOI2(timearg);
446                 if (lt->tm_hour < 0 || lt->tm_hour > 23)
447                         badtime();
448                 lt->tm_min = ATOI2(timearg);
449                 if (lt->tm_min < 0 || lt->tm_min > 59)
450                         badtime();
451                 lt->tm_sec = 0;
452                 if ((shuttime = mktime(lt)) == -1)
453                         badtime();
454                 if ((offset = shuttime - now) < 0)
455                         errx(1, "that time is already past.");
456                 break;
457         default:
458                 badtime();
459         }
460 }
461
462 #define NOMSG   "\n\nNO LOGINS: System going down at "
463 void
464 nolog(void)
465 {
466         int logfd;
467         char *ct;
468
469         unlink(_PATH_NOLOGIN);  /* in case linked to another file */
470         signal(SIGINT, finish);
471         signal(SIGHUP, finish);
472         signal(SIGQUIT, finish);
473         signal(SIGTERM, finish);
474         if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC,
475             0664)) >= 0) {
476                 write(logfd, NOMSG, sizeof(NOMSG) - 1);
477                 ct = ctime(&shuttime);
478                 write(logfd, ct + 11, 5);
479                 write(logfd, "\n\n", 2);
480                 write(logfd, mbuf, strlen(mbuf));
481                 close(logfd);
482         }
483 }
484
485 void
486 finish(int signo __unused)
487 {
488         if (!killflg)
489                 unlink(_PATH_NOLOGIN);
490         exit(0);
491 }
492
493 void
494 badtime(void)
495 {
496         errx(1, "bad time format");
497 }
498
499 void
500 usage(const char *cp)
501 {
502         if (cp != NULL)
503                 warnx("%s", cp);
504         fprintf(stderr,
505             "usage: shutdown [-] [-h | -p | -r | -k] [-o [-n]]"
506             " time [warning-message ...]\n");
507         exit(1);
508 }