shutdown.c 13 KB

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