1 |
commit: 3f918161aafa61c1c2005709fda0b9bec4c412d8 |
2 |
Author: William Hubbs <w.d.hubbs <AT> gmail <DOT> com> |
3 |
AuthorDate: Fri Oct 5 19:10:59 2018 +0000 |
4 |
Commit: William Hubbs <williamh <AT> gentoo <DOT> org> |
5 |
CommitDate: Thu Oct 18 22:56:36 2018 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/openrc.git/commit/?id=3f918161 |
7 |
|
8 |
openrc-shutdown: Add scheduled shutdown and the ability to cancel a shutdown |
9 |
|
10 |
You can now schedule a shutdown for a certain time or a cpecific number |
11 |
of minutes into the future. |
12 |
|
13 |
When a shutdown is running, you can now cancel it with ^c from the |
14 |
keyboard or by running "openrc-shutdown -c" from another shell. |
15 |
|
16 |
man/openrc-shutdown.8 | 3 + |
17 |
src/rc/Makefile | 4 +- |
18 |
src/rc/broadcast.c | 209 +++++++++++++++++++++++++++++++++++++++++++++++ |
19 |
src/rc/broadcast.h | 16 ++++ |
20 |
src/rc/openrc-shutdown.c | 179 ++++++++++++++++++++++++++++++++++++++-- |
21 |
5 files changed, 402 insertions(+), 9 deletions(-) |
22 |
|
23 |
diff --git a/man/openrc-shutdown.8 b/man/openrc-shutdown.8 |
24 |
index 5db21334..b09e8c20 100644 |
25 |
--- a/man/openrc-shutdown.8 |
26 |
+++ b/man/openrc-shutdown.8 |
27 |
@@ -16,6 +16,7 @@ |
28 |
.Nd bring the system down |
29 |
.Sh SYNOPSIS |
30 |
.Nm |
31 |
+.Op Fl c , -cancel |
32 |
.Op Fl d , -no-write |
33 |
.Op Fl D , -dry-run |
34 |
.Op Fl H , -halt |
35 |
@@ -32,6 +33,8 @@ is the utility that communicates with |
36 |
to bring down the system or instruct openrc-init to re-execute itself. |
37 |
It supports the following options: |
38 |
.Bl -tag -width "poweroff" |
39 |
+.It Fl c , -cancel |
40 |
+Cancel a pending shutdown. |
41 |
.It Fl d , -no-write |
42 |
Do not write the wtmp boot record. |
43 |
.It Fl D , -dry-run |
44 |
|
45 |
diff --git a/src/rc/Makefile b/src/rc/Makefile |
46 |
index b09c5058..9ba240fa 100644 |
47 |
--- a/src/rc/Makefile |
48 |
+++ b/src/rc/Makefile |
49 |
@@ -14,7 +14,7 @@ SRCS+= rc-selinux.c |
50 |
endif |
51 |
|
52 |
ifeq (${OS},Linux) |
53 |
-SRCS+= kill_all.c openrc-init.c openrc-shutdown.c rc-wtmp.c |
54 |
+SRCS+= kill_all.c openrc-init.c openrc-shutdown.c broadcast.c rc-wtmp.c |
55 |
endif |
56 |
|
57 |
CLEANFILES= version.h rc-selinux.o |
58 |
@@ -134,7 +134,7 @@ mountinfo: mountinfo.o _usage.o rc-misc.o |
59 |
openrc rc: rc.o rc-logger.o rc-misc.o rc-plugin.o _usage.o |
60 |
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} |
61 |
|
62 |
-openrc-shutdown: openrc-shutdown.o _usage.o rc-wtmp.o |
63 |
+openrc-shutdown: openrc-shutdown.o rc-misc.o _usage.o broadcast.o rc-wtmp.o |
64 |
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} |
65 |
|
66 |
openrc-run runscript: openrc-run.o _usage.o rc-misc.o rc-plugin.o |
67 |
|
68 |
diff --git a/src/rc/broadcast.c b/src/rc/broadcast.c |
69 |
new file mode 100644 |
70 |
index 00000000..dbc861d1 |
71 |
--- /dev/null |
72 |
+++ b/src/rc/broadcast.c |
73 |
@@ -0,0 +1,209 @@ |
74 |
+/* |
75 |
+ * broadcast.c |
76 |
+ * broadcast a message to every logged in user |
77 |
+ */ |
78 |
+ |
79 |
+/* |
80 |
+ * Copyright 2018 Sony Interactive Entertainment Inc. |
81 |
+ * |
82 |
+ * This file is part of OpenRC. It is subject to the license terms in |
83 |
+ * the LICENSE file found in the top-level directory of this |
84 |
+ * distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE |
85 |
+ * This file may not be copied, modified, propagated, or distributed |
86 |
+ * except according to the terms contained in the LICENSE file. |
87 |
+ */ |
88 |
+#include <ctype.h> |
89 |
+#include <sys/types.h> |
90 |
+#include <sys/stat.h> |
91 |
+#include <sys/sysmacros.h> |
92 |
+#include <limits.h> |
93 |
+#include <stdlib.h> |
94 |
+#include <string.h> |
95 |
+#include <time.h> |
96 |
+#include <unistd.h> |
97 |
+#include <stdio.h> |
98 |
+#include <utmpx.h> |
99 |
+#include <pwd.h> |
100 |
+#include <fcntl.h> |
101 |
+#include <signal.h> |
102 |
+#include <setjmp.h> |
103 |
+#include <paths.h> |
104 |
+#include <sys/utsname.h> |
105 |
+ |
106 |
+#include "broadcast.h" |
107 |
+#include "helpers.h" |
108 |
+ |
109 |
+#ifndef _PATH_DEV |
110 |
+# define _PATH_DEV "/dev/" |
111 |
+#endif |
112 |
+ |
113 |
+#ifndef UT_LINESIZE |
114 |
+#define UT_LINESIZE __UT_LINESIZE |
115 |
+#endif |
116 |
+ |
117 |
+static sigjmp_buf jbuf; |
118 |
+ |
119 |
+/* |
120 |
+ * Alarm handler |
121 |
+ */ |
122 |
+/*ARGSUSED*/ |
123 |
+# ifdef __GNUC__ |
124 |
+static void handler(int arg __attribute__((unused))) |
125 |
+# else |
126 |
+static void handler(int arg) |
127 |
+# endif |
128 |
+{ |
129 |
+ siglongjmp(jbuf, 1); |
130 |
+} |
131 |
+ |
132 |
+static void getuidtty(char **userp, char **ttyp) |
133 |
+{ |
134 |
+ struct passwd *pwd; |
135 |
+ uid_t uid; |
136 |
+ char *tty; |
137 |
+ static char uidbuf[32]; |
138 |
+ static char ttynm[UT_LINESIZE + 4]; |
139 |
+ |
140 |
+ uid = getuid(); |
141 |
+ if ((pwd = getpwuid(uid)) != NULL) { |
142 |
+ uidbuf[0] = 0; |
143 |
+ strncat(uidbuf, pwd->pw_name, sizeof(uidbuf) - 1); |
144 |
+ } else { |
145 |
+ if (uid) |
146 |
+ sprintf(uidbuf, "uid %d", (int) uid); |
147 |
+ else |
148 |
+ sprintf(uidbuf, "root"); |
149 |
+ } |
150 |
+ |
151 |
+ if ((tty = ttyname(0)) != NULL) { |
152 |
+ const size_t plen = strlen(_PATH_DEV); |
153 |
+ if (strncmp(tty, _PATH_DEV, plen) == 0) { |
154 |
+ tty += plen; |
155 |
+ if (tty[0] == '/') |
156 |
+ tty++; |
157 |
+ } |
158 |
+ snprintf(ttynm, sizeof(ttynm), "(%.*s) ", |
159 |
+ UT_LINESIZE, tty); |
160 |
+ } else |
161 |
+ ttynm[0] = 0; |
162 |
+ |
163 |
+ *userp = uidbuf; |
164 |
+ *ttyp = ttynm; |
165 |
+} |
166 |
+ |
167 |
+/* |
168 |
+ * Check whether the given filename looks like a tty device. |
169 |
+ */ |
170 |
+static int file_isatty(const char *fname) |
171 |
+{ |
172 |
+ struct stat st; |
173 |
+ int major; |
174 |
+ |
175 |
+ if (stat(fname, &st) < 0) |
176 |
+ return 0; |
177 |
+ |
178 |
+ if (st.st_nlink != 1 || !S_ISCHR(st.st_mode)) |
179 |
+ return 0; |
180 |
+ |
181 |
+ /* |
182 |
+ * It would be an impossible task to list all major/minors |
183 |
+ * of tty devices here, so we just exclude the obvious |
184 |
+ * majors of which just opening has side-effects: |
185 |
+ * printers and tapes. |
186 |
+ */ |
187 |
+ major = major(st.st_dev); |
188 |
+ if (major == 1 || major == 2 || major == 6 || major == 9 || |
189 |
+ major == 12 || major == 16 || major == 21 || major == 27 || |
190 |
+ major == 37 || major == 96 || major == 97 || major == 206 || |
191 |
+ major == 230) |
192 |
+ return 0; |
193 |
+ return 1; |
194 |
+} |
195 |
+ |
196 |
+/* |
197 |
+ * broadcast function. |
198 |
+ */ |
199 |
+void broadcast(char *text) |
200 |
+{ |
201 |
+ char *tty; |
202 |
+ char *user; |
203 |
+ struct utsname name; |
204 |
+ time_t t; |
205 |
+ char *date; |
206 |
+ char *p; |
207 |
+ char *line = NULL; |
208 |
+ struct sigaction sa; |
209 |
+ int fd; |
210 |
+ FILE *tp; |
211 |
+ int flags; |
212 |
+ char *term = NULL; |
213 |
+ struct utmpx *utmp; |
214 |
+ |
215 |
+ getuidtty(&user, &tty); |
216 |
+ |
217 |
+ /* |
218 |
+ * Get and report current hostname, to make it easier to find out |
219 |
+ * which machine is being shut down. |
220 |
+ */ |
221 |
+ uname(&name); |
222 |
+ |
223 |
+ /* Get the time */ |
224 |
+ time(&t); |
225 |
+ date = ctime(&t); |
226 |
+ p = strchr(date, '\n'); |
227 |
+ if (p) |
228 |
+ *p = 0; |
229 |
+ |
230 |
+ xasprintf(&line, "\007\r\nBroadcast message from %s@%s %s(%s):\r\n\r\n", |
231 |
+ user, name.nodename, tty, date); |
232 |
+ |
233 |
+ /* |
234 |
+ * Fork to avoid hanging in a write() |
235 |
+ */ |
236 |
+ if (fork() != 0) |
237 |
+ return; |
238 |
+ |
239 |
+ memset(&sa, 0, sizeof(sa)); |
240 |
+ sa.sa_handler = handler; |
241 |
+ sigemptyset(&sa.sa_mask); |
242 |
+ sigaction(SIGALRM, &sa, NULL); |
243 |
+ |
244 |
+ setutxent(); |
245 |
+ |
246 |
+ while ((utmp = getutxent()) != NULL) { |
247 |
+ if (utmp->ut_type != USER_PROCESS || utmp->ut_user[0] == 0) |
248 |
+ continue; |
249 |
+ if (strncmp(utmp->ut_line, _PATH_DEV, strlen(_PATH_DEV)) == 0) |
250 |
+ xasprintf(&term, "%s", utmp->ut_line); |
251 |
+ else |
252 |
+ xasprintf(&term, "%s%s", _PATH_DEV, utmp->ut_line); |
253 |
+ if (strstr(term, "/../")) { |
254 |
+ free(term); |
255 |
+ continue; |
256 |
+ } |
257 |
+ |
258 |
+ /* |
259 |
+ * Open it non-delay |
260 |
+ */ |
261 |
+ if (sigsetjmp(jbuf, 1) == 0) { |
262 |
+ alarm(2); |
263 |
+ flags = O_WRONLY|O_NDELAY|O_NOCTTY; |
264 |
+ if (file_isatty(term) && (fd = open(term, flags)) >= 0) { |
265 |
+ if (isatty(fd) && (tp = fdopen(fd, "w")) != NULL) { |
266 |
+ fputs(line, tp); |
267 |
+ fputs(text, tp); |
268 |
+ fflush(tp); |
269 |
+ } |
270 |
+ } |
271 |
+ } |
272 |
+ alarm(0); |
273 |
+ if (fd >= 0) |
274 |
+ close(fd); |
275 |
+ if (tp != NULL) |
276 |
+ fclose(tp); |
277 |
+ free(term); |
278 |
+ } |
279 |
+ endutxent(); |
280 |
+ free(line); |
281 |
+ exit(0); |
282 |
+} |
283 |
|
284 |
diff --git a/src/rc/broadcast.h b/src/rc/broadcast.h |
285 |
new file mode 100644 |
286 |
index 00000000..5f948272 |
287 |
--- /dev/null |
288 |
+++ b/src/rc/broadcast.h |
289 |
@@ -0,0 +1,16 @@ |
290 |
+/* |
291 |
+ * Copyright 2018 Sony Interactive Entertainment Inc. |
292 |
+ * |
293 |
+ * This file is part of OpenRC. It is subject to the license terms in |
294 |
+ * the LICENSE file found in the top-level directory of this |
295 |
+ * distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE |
296 |
+ * This file may not be copied, modified, propagated, or distributed |
297 |
+ * except according to the terms contained in the LICENSE file. |
298 |
+ */ |
299 |
+ |
300 |
+#ifndef BROADCAST_H |
301 |
+#define BROADCAST_H |
302 |
+ |
303 |
+void broadcast(char *text); |
304 |
+ |
305 |
+#endif |
306 |
|
307 |
diff --git a/src/rc/openrc-shutdown.c b/src/rc/openrc-shutdown.c |
308 |
index b17a63d8..ab2e7469 100644 |
309 |
--- a/src/rc/openrc-shutdown.c |
310 |
+++ b/src/rc/openrc-shutdown.c |
311 |
@@ -25,20 +25,24 @@ |
312 |
#include <stdio.h> |
313 |
#include <stdlib.h> |
314 |
#include <string.h> |
315 |
+#include <syslog.h> |
316 |
#include <unistd.h> |
317 |
#include <sys/types.h> |
318 |
#include <sys/utsname.h> |
319 |
|
320 |
+#include "broadcast.h" |
321 |
#include "einfo.h" |
322 |
#include "rc.h" |
323 |
#include "helpers.h" |
324 |
+#include "rc-misc.h" |
325 |
#include "_usage.h" |
326 |
#include "rc-wtmp.h" |
327 |
|
328 |
const char *applet = NULL; |
329 |
const char *extraopts = NULL; |
330 |
-const char *getoptstring = "dDHKpRrsw" getoptstring_COMMON; |
331 |
+const char *getoptstring = "cdDfFHKpRrsw" getoptstring_COMMON; |
332 |
const struct option longopts[] = { |
333 |
+ { "cancel", no_argument, NULL, 'c'}, |
334 |
{ "no-write", no_argument, NULL, 'd'}, |
335 |
{ "dry-run", no_argument, NULL, 'D'}, |
336 |
{ "halt", no_argument, NULL, 'H'}, |
337 |
@@ -51,6 +55,7 @@ const struct option longopts[] = { |
338 |
longopts_COMMON |
339 |
}; |
340 |
const char * const longopts_help[] = { |
341 |
+ "cancel a pending shutdown", |
342 |
"do not write wtmp record", |
343 |
"print actions instead of executing them", |
344 |
"halt the system", |
345 |
@@ -64,8 +69,12 @@ const char * const longopts_help[] = { |
346 |
}; |
347 |
const char *usagestring = NULL; |
348 |
const char *exclusive = "Select one of " |
349 |
-"--halt, --kexec, --poweroff, --reexec, --reboot, --single or --write-only"; |
350 |
+ "--cancel, --halt, --kexec, --poweroff, --reexec, --reboot, --single or \n" |
351 |
+ "--write-only"; |
352 |
+const char *nologin_file = RC_SYSCONFDIR"/nologin"; |
353 |
+const char *shutdown_pid = "/run/openrc-shutdown.pid"; |
354 |
|
355 |
+static bool do_cancel = false; |
356 |
static bool do_dryrun = false; |
357 |
static bool do_halt = false; |
358 |
static bool do_kexec = false; |
359 |
@@ -76,6 +85,40 @@ static bool do_single = false; |
360 |
static bool do_wtmp = true; |
361 |
static bool do_wtmp_only = false; |
362 |
|
363 |
+static void cancel_shutdown(void) |
364 |
+{ |
365 |
+ pid_t pid; |
366 |
+ |
367 |
+ pid = get_pid(applet, shutdown_pid); |
368 |
+ if (pid <= 0) |
369 |
+ eerrorx("%s: Unable to cancel shutdown", applet); |
370 |
+ |
371 |
+ if (kill(pid, SIGTERM) != -1) |
372 |
+ einfo("%s: shutdown canceled", applet); |
373 |
+ else |
374 |
+ eerrorx("%s: Unable to cancel shutdown", applet); |
375 |
+} |
376 |
+ |
377 |
+/* |
378 |
+ * Create the nologin file. |
379 |
+ */ |
380 |
+static void create_nologin(int mins) |
381 |
+{ |
382 |
+ FILE *fp; |
383 |
+ time_t t; |
384 |
+ |
385 |
+ time(&t); |
386 |
+ t += 60 * mins; |
387 |
+ |
388 |
+ if ((fp = fopen(nologin_file, "w")) != NULL) { |
389 |
+ fprintf(fp, "\rThe system is going down on %s\r\n", ctime(&t)); |
390 |
+ fclose(fp); |
391 |
+ } |
392 |
+} |
393 |
+ |
394 |
+/* |
395 |
+ * Send a command to our init |
396 |
+ */ |
397 |
static void send_cmd(const char *cmd) |
398 |
{ |
399 |
FILE *fifo; |
400 |
@@ -99,16 +142,59 @@ static void send_cmd(const char *cmd) |
401 |
fclose(fifo); |
402 |
} |
403 |
|
404 |
+/* |
405 |
+ * sleep without being interrupted. |
406 |
+ * The idea for this code came from sysvinit. |
407 |
+ */ |
408 |
+static void sleep_no_interrupt(int seconds) |
409 |
+{ |
410 |
+ struct timespec duration; |
411 |
+ struct timespec remaining; |
412 |
+ |
413 |
+ duration.tv_sec = seconds; |
414 |
+ duration.tv_nsec = 0; |
415 |
+ |
416 |
+ while(nanosleep(&duration, &remaining) < 0 && errno == EINTR) |
417 |
+ duration = remaining; |
418 |
+} |
419 |
+ |
420 |
+static void stop_shutdown(int sig) |
421 |
+{ |
422 |
+ /* use the sig parameter so the compiler will not complain */ |
423 |
+ if (sig == SIGINT) |
424 |
+ ; |
425 |
+ unlink(nologin_file); |
426 |
+ unlink(shutdown_pid); |
427 |
+einfo("Shutdown canceled"); |
428 |
+exit(0); |
429 |
+} |
430 |
+ |
431 |
int main(int argc, char **argv) |
432 |
{ |
433 |
+ char *ch = NULL; |
434 |
int opt; |
435 |
int cmd_count = 0; |
436 |
+ int hour = 0; |
437 |
+ int min = 0; |
438 |
+ int shutdown_delay = 0; |
439 |
+ struct sigaction sa; |
440 |
+ struct tm *lt; |
441 |
+ time_t tv; |
442 |
+ bool need_warning = false; |
443 |
+ char *msg = NULL; |
444 |
+ char *state = NULL; |
445 |
+ char *time_arg = NULL; |
446 |
+ FILE *fp; |
447 |
|
448 |
applet = basename_c(argv[0]); |
449 |
while ((opt = getopt_long(argc, argv, getoptstring, |
450 |
longopts, (int *) 0)) != -1) |
451 |
{ |
452 |
switch (opt) { |
453 |
+ case 'c': |
454 |
+ do_cancel = true; |
455 |
+ cmd_count++; |
456 |
+ break; |
457 |
case 'd': |
458 |
do_wtmp = false; |
459 |
break; |
460 |
@@ -117,14 +203,17 @@ int main(int argc, char **argv) |
461 |
break; |
462 |
case 'H': |
463 |
do_halt = true; |
464 |
+ xasprintf(&state, "%s", "halt"); |
465 |
cmd_count++; |
466 |
break; |
467 |
case 'K': |
468 |
do_kexec = true; |
469 |
+ xasprintf(&state, "%s", "reboot"); |
470 |
cmd_count++; |
471 |
break; |
472 |
case 'p': |
473 |
do_poweroff = true; |
474 |
+ xasprintf(&state, "%s", "power off"); |
475 |
cmd_count++; |
476 |
break; |
477 |
case 'R': |
478 |
@@ -133,10 +222,12 @@ int main(int argc, char **argv) |
479 |
break; |
480 |
case 'r': |
481 |
do_reboot = true; |
482 |
+ xasprintf(&state, "%s", "reboot"); |
483 |
cmd_count++; |
484 |
break; |
485 |
case 's': |
486 |
do_single = true; |
487 |
+ xasprintf(&state, "%s", "go down for maintenance"); |
488 |
cmd_count++; |
489 |
break; |
490 |
case 'w': |
491 |
@@ -146,12 +237,88 @@ int main(int argc, char **argv) |
492 |
case_RC_COMMON_GETOPT |
493 |
} |
494 |
} |
495 |
- if (geteuid() != 0 && ! do_dryrun) |
496 |
+ if (geteuid() != 0) |
497 |
eerrorx("%s: you must be root\n", applet); |
498 |
if (cmd_count != 1) { |
499 |
eerror("%s: %s\n", applet, exclusive); |
500 |
usage(EXIT_FAILURE); |
501 |
} |
502 |
+ |
503 |
+ if (do_cancel) { |
504 |
+ cancel_shutdown(); |
505 |
+ exit(EXIT_SUCCESS); |
506 |
+ } else if (do_reexec) { |
507 |
+ send_cmd("reexec"); |
508 |
+ exit(EXIT_SUCCESS); |
509 |
+ } |
510 |
+ |
511 |
+ if (optind >= argc) { |
512 |
+ eerror("%s: No shutdown time specified", applet); |
513 |
+ usage(EXIT_FAILURE); |
514 |
+ } |
515 |
+ time_arg = argv[optind]; |
516 |
+ if (*time_arg == '+') |
517 |
+ time_arg++; |
518 |
+ if (strcasecmp(time_arg, "now") == 0) |
519 |
+ strcpy(time_arg, "0"); |
520 |
+ for (ch=time_arg; *ch; ch++) |
521 |
+ if ((*ch < '0' || *ch > '9') && *ch != ':') { |
522 |
+ eerror("%s: invalid time %s", applet, time_arg); |
523 |
+ usage(EXIT_FAILURE); |
524 |
+ } |
525 |
+ if (strchr(time_arg, ':')) { |
526 |
+ if ((sscanf(time_arg, "%2d:%2d", &hour, &min) != 2) || |
527 |
+ (hour > 23) || (min > 59)) { |
528 |
+ eerror("%s: invalid time %s", applet, time_arg); |
529 |
+ usage(EXIT_FAILURE); |
530 |
+ } |
531 |
+ time(&tv); |
532 |
+ lt = localtime(&tv); |
533 |
+ shutdown_delay = (hour * 60 + min) - (lt->tm_hour * 60 + lt->tm_min); |
534 |
+ if (shutdown_delay < 0) |
535 |
+ shutdown_delay += 1440; |
536 |
+ } else { |
537 |
+ shutdown_delay = atoi(time_arg); |
538 |
+ } |
539 |
+ |
540 |
+ fp = fopen(shutdown_pid, "w"); |
541 |
+ if (!fp) |
542 |
+ eerrorx("%s: fopen `%s': %s", applet, shutdown_pid, strerror(errno)); |
543 |
+ fprintf(fp, "%d\n", getpid()); |
544 |
+ fclose(fp); |
545 |
+ |
546 |
+ openlog(applet, LOG_PID, LOG_DAEMON); |
547 |
+ memset(&sa, 0, sizeof(sa)); |
548 |
+ sa.sa_handler = stop_shutdown; |
549 |
+ sigemptyset(&sa.sa_mask); |
550 |
+ sigaction(SIGINT, &sa, NULL); |
551 |
+ sigaction(SIGTERM, &sa, NULL); |
552 |
+ while (shutdown_delay > 0) { |
553 |
+ need_warning = false; |
554 |
+ if (shutdown_delay > 180) |
555 |
+ need_warning = (shutdown_delay % 60 == 0); |
556 |
+ else if (shutdown_delay > 60) |
557 |
+ need_warning = (shutdown_delay % 30 == 0); |
558 |
+ else if (shutdown_delay > 10) |
559 |
+ need_warning = (shutdown_delay % 15 == 0); |
560 |
+ else |
561 |
+ need_warning = true; |
562 |
+ if (shutdown_delay <= 5) |
563 |
+ create_nologin(shutdown_delay); |
564 |
+ if (need_warning) { |
565 |
+ xasprintf(&msg, "\rThe system will %s in %d minutes\r\n", |
566 |
+ state, shutdown_delay); |
567 |
+ broadcast(msg); |
568 |
+ free(msg); |
569 |
+ } |
570 |
+ sleep_no_interrupt(60); |
571 |
+ shutdown_delay--; |
572 |
+ } |
573 |
+ xasprintf(&msg, "\rThe system will %s now\r\n", state); |
574 |
+ broadcast(msg); |
575 |
+ syslog(LOG_NOTICE, "The system will %s now", state); |
576 |
+ unlink(nologin_file); |
577 |
+ unlink(shutdown_pid); |
578 |
if (do_halt) |
579 |
send_cmd("halt"); |
580 |
else if (do_kexec) |
581 |
@@ -160,11 +327,9 @@ int main(int argc, char **argv) |
582 |
send_cmd("poweroff"); |
583 |
else if (do_reboot) |
584 |
send_cmd("reboot"); |
585 |
- else if (do_reexec) |
586 |
- send_cmd("reexec"); |
587 |
- else if (do_wtmp_only) |
588 |
- log_wtmp("shutdown", "~~", 0, RUN_LVL, "~~"); |
589 |
else if (do_single) |
590 |
send_cmd("single"); |
591 |
+ else if (do_wtmp_only) |
592 |
+ log_wtmp("shutdown", "~~", 0, RUN_LVL, "~~"); |
593 |
return 0; |
594 |
} |