Scott James Remnant | 4715848 | 2006-08-27 22:37:10 +0100 | [diff] [blame] | 1 | /* upstart |
| 2 | * |
Scott James Remnant | bdcf3be | 2010-02-04 00:33:40 -0800 | [diff] [blame] | 3 | * Copyright © 2010 Canonical Ltd. |
Scott James Remnant | 7d5b2ea | 2009-05-22 15:20:12 +0200 | [diff] [blame] | 4 | * Author: Scott James Remnant <scott@netsplit.com>. |
Scott James Remnant | 4715848 | 2006-08-27 22:37:10 +0100 | [diff] [blame] | 5 | * |
Scott James Remnant | 0c0c5a5 | 2009-06-23 10:29:35 +0100 | [diff] [blame] | 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License version 2, as |
Scott James Remnant | 7512302 | 2009-05-22 13:27:56 +0200 | [diff] [blame] | 8 | * published by the Free Software Foundation. |
Scott James Remnant | 4715848 | 2006-08-27 22:37:10 +0100 | [diff] [blame] | 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU General Public License for more details. |
| 14 | * |
Scott James Remnant | 0c0c5a5 | 2009-06-23 10:29:35 +0100 | [diff] [blame] | 15 | * You should have received a copy of the GNU General Public License along |
| 16 | * with this program; if not, write to the Free Software Foundation, Inc., |
| 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
Scott James Remnant | 4715848 | 2006-08-27 22:37:10 +0100 | [diff] [blame] | 18 | */ |
| 19 | |
| 20 | #ifdef HAVE_CONFIG_H |
| 21 | # include <config.h> |
| 22 | #endif /* HAVE_CONFIG_H */ |
| 23 | |
| 24 | |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 25 | #include <dbus/dbus.h> |
| 26 | |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 27 | #include <sys/types.h> |
| 28 | #include <sys/stat.h> |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 29 | #include <sys/param.h> |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 30 | |
| 31 | #include <pwd.h> |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 32 | #include <time.h> |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 33 | #include <utmpx.h> |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 34 | #include <fcntl.h> |
| 35 | #include <errno.h> |
| 36 | #include <stdio.h> |
| 37 | #include <limits.h> |
| 38 | #include <signal.h> |
| 39 | #include <stdlib.h> |
| 40 | #include <string.h> |
| 41 | #include <unistd.h> |
| 42 | |
Scott James Remnant | 4715848 | 2006-08-27 22:37:10 +0100 | [diff] [blame] | 43 | #include <nih/macros.h> |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 44 | #include <nih/alloc.h> |
| 45 | #include <nih/timer.h> |
| 46 | #include <nih/string.h> |
| 47 | #include <nih/signal.h> |
Scott James Remnant | da10027 | 2007-01-06 20:24:18 +0000 | [diff] [blame] | 48 | #include <nih/io.h> |
Scott James Remnant | 4715848 | 2006-08-27 22:37:10 +0100 | [diff] [blame] | 49 | #include <nih/main.h> |
| 50 | #include <nih/option.h> |
| 51 | #include <nih/logging.h> |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 52 | #include <nih/error.h> |
| 53 | |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 54 | #include <nih-dbus/dbus_error.h> |
| 55 | #include <nih-dbus/errors.h> |
| 56 | |
| 57 | #include "sysv.h" |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 58 | |
| 59 | |
| 60 | /** |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 61 | * ETC_NOLOGIN: |
| 62 | * |
| 63 | * File we write to to prevent logins. |
| 64 | **/ |
| 65 | #define ETC_NOLOGIN "/etc/nologin" |
| 66 | |
| 67 | /** |
| 68 | * DEV: |
| 69 | * |
| 70 | * Directory containing tty device nodes. |
| 71 | **/ |
| 72 | #define DEV "/dev" |
| 73 | |
Scott James Remnant | 7e6bdb3 | 2006-09-14 10:57:03 +0100 | [diff] [blame] | 74 | /** |
| 75 | * DEV_INITCTL: |
| 76 | * |
| 77 | * System V init control socket. |
| 78 | **/ |
| 79 | #ifndef DEV_INITCTL |
| 80 | #define DEV_INITCTL "/dev/initctl" |
| 81 | #endif |
| 82 | |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 83 | |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 84 | /* Prototypes for option functions */ |
| 85 | static int runlevel_option (NihOption *option, const char *arg); |
| 86 | |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 87 | /* Prototypes for static functions */ |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 88 | static void shutdown_now (void) |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 89 | __attribute__ ((noreturn)); |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 90 | static void cancel_callback (void *data, NihSignal *signal) |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 91 | __attribute__ ((noreturn)); |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 92 | static void timer_callback (const char *message); |
| 93 | static char *warning_message (const char *message) |
| 94 | __attribute__ ((warn_unused_result)); |
| 95 | static void wall (const char *message); |
Scott James Remnant | b828007 | 2006-09-07 03:19:00 +0100 | [diff] [blame] | 96 | static void sysvinit_shutdown (void); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 97 | |
| 98 | |
| 99 | /** |
Scott James Remnant | e8d263c | 2007-02-08 03:20:30 +0000 | [diff] [blame] | 100 | * runlevel: |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 101 | * |
Scott James Remnant | e8d263c | 2007-02-08 03:20:30 +0000 | [diff] [blame] | 102 | * Runlevel to switch to. |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 103 | **/ |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 104 | static int runlevel = 0; |
Scott James Remnant | e8d263c | 2007-02-08 03:20:30 +0000 | [diff] [blame] | 105 | |
| 106 | /** |
| 107 | * init_halt: |
| 108 | * |
| 109 | * Value of init_halt environment variable for event. |
| 110 | **/ |
| 111 | static const char *init_halt = NULL; |
| 112 | |
| 113 | /** |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 114 | * cancel: |
| 115 | * |
Scott James Remnant | 3b73464 | 2006-10-11 17:22:33 +0100 | [diff] [blame] | 116 | * TRUE if we should cancel an already running shutdown. |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 117 | **/ |
| 118 | static int cancel = FALSE; |
| 119 | |
| 120 | /** |
| 121 | * warn_only: |
| 122 | * |
Scott James Remnant | 3b73464 | 2006-10-11 17:22:33 +0100 | [diff] [blame] | 123 | * TRUE if we should only send the warning, and not perform the actual |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 124 | * shutdown. |
| 125 | **/ |
| 126 | static int warn_only = FALSE; |
| 127 | |
| 128 | /** |
| 129 | * when: |
| 130 | * |
| 131 | * Time to shutdown, parsed from the old -g argument. |
| 132 | **/ |
| 133 | static char *when = NULL; |
| 134 | |
| 135 | /** |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 136 | * delay: |
| 137 | * |
| 138 | * How long until we shutdown. |
| 139 | **/ |
| 140 | static int delay = 0; |
Scott James Remnant | 4715848 | 2006-08-27 22:37:10 +0100 | [diff] [blame] | 141 | |
| 142 | |
| 143 | /** |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 144 | * runlevel_option: |
| 145 | * @option: option found in arguments, |
| 146 | * @arg: always NULL. |
| 147 | * |
| 148 | * This function is called whenever one of the -r, -h, -H or -P options |
| 149 | * is found in the argument list. It changes the runlevel to that implied |
| 150 | * by the option. |
| 151 | **/ |
| 152 | static int |
| 153 | runlevel_option (NihOption *option, |
| 154 | const char *arg) |
| 155 | { |
| 156 | int *value; |
| 157 | |
| 158 | nih_assert (option != NULL); |
| 159 | nih_assert (option->value != NULL); |
| 160 | nih_assert (arg == NULL); |
| 161 | |
| 162 | value = (int *)option->value; |
| 163 | |
| 164 | switch (option->option) { |
| 165 | case 'r': |
| 166 | *value = '6'; |
| 167 | init_halt = NULL; |
| 168 | break; |
| 169 | case 'h': |
| 170 | *value = '0'; |
| 171 | init_halt = NULL; |
| 172 | break; |
| 173 | case 'H': |
| 174 | *value = '0'; |
| 175 | init_halt = "HALT"; |
| 176 | break; |
| 177 | case 'P': |
| 178 | *value = '0'; |
| 179 | init_halt = "POWEROFF"; |
| 180 | break; |
| 181 | } |
| 182 | |
| 183 | return 0; |
| 184 | } |
| 185 | |
| 186 | |
| 187 | /** |
Scott James Remnant | 4715848 | 2006-08-27 22:37:10 +0100 | [diff] [blame] | 188 | * options: |
| 189 | * |
| 190 | * Command-line options accepted for all arguments. |
| 191 | **/ |
| 192 | static NihOption options[] = { |
Scott James Remnant | eff01f1 | 2006-10-13 13:32:12 +0100 | [diff] [blame] | 193 | { 'r', NULL, N_("reboot after shutdown"), |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 194 | NULL, NULL, &runlevel, runlevel_option }, |
Scott James Remnant | eff01f1 | 2006-10-13 13:32:12 +0100 | [diff] [blame] | 195 | { 'h', NULL, N_("halt or power off after shutdown"), |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 196 | NULL, NULL, &runlevel, runlevel_option }, |
Scott James Remnant | eff01f1 | 2006-10-13 13:32:12 +0100 | [diff] [blame] | 197 | { 'H', NULL, N_("halt after shutdown (implies -h)"), |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 198 | NULL, NULL, &runlevel, runlevel_option }, |
Scott James Remnant | eff01f1 | 2006-10-13 13:32:12 +0100 | [diff] [blame] | 199 | { 'P', NULL, N_("power off after shutdown (implies -h)"), |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 200 | NULL, NULL, &runlevel, runlevel_option }, |
Scott James Remnant | eff01f1 | 2006-10-13 13:32:12 +0100 | [diff] [blame] | 201 | { 'c', NULL, N_("cancel a running shutdown"), |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 202 | NULL, NULL, &cancel, NULL }, |
Scott James Remnant | eff01f1 | 2006-10-13 13:32:12 +0100 | [diff] [blame] | 203 | { 'k', NULL, N_("only send warnings, don't shutdown"), |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 204 | NULL, NULL, &warn_only, NULL }, |
| 205 | |
| 206 | /* Compatibility option for specifying time */ |
| 207 | { 'g', NULL, NULL, NULL, "TIME", &when, NULL }, |
| 208 | |
| 209 | /* Compatibility options, all ignored */ |
| 210 | { 'a', NULL, NULL, NULL, NULL, NULL, NULL }, |
| 211 | { 'n', NULL, NULL, NULL, NULL, NULL, NULL }, |
| 212 | { 'f', NULL, NULL, NULL, NULL, NULL, NULL }, |
| 213 | { 'F', NULL, NULL, NULL, NULL, NULL, NULL }, |
| 214 | { 'i', NULL, NULL, NULL, "LEVEL", NULL, NULL }, |
| 215 | { 't', NULL, NULL, NULL, "SECS", NULL, NULL }, |
| 216 | { 'y', NULL, NULL, NULL, NULL, NULL, NULL }, |
| 217 | |
Scott James Remnant | 4715848 | 2006-08-27 22:37:10 +0100 | [diff] [blame] | 218 | NIH_OPTION_LAST |
| 219 | }; |
| 220 | |
| 221 | |
| 222 | int |
| 223 | main (int argc, |
| 224 | char *argv[]) |
| 225 | { |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 226 | char ** args; |
| 227 | nih_local char *message = NULL; |
| 228 | size_t messagelen; |
| 229 | nih_local char *msg = NULL; |
| 230 | int arg; |
| 231 | pid_t pid = 0; |
Scott James Remnant | 4715848 | 2006-08-27 22:37:10 +0100 | [diff] [blame] | 232 | |
| 233 | nih_main_init (argv[0]); |
Scott James Remnant | 1ee0f48 | 2006-10-11 17:40:41 +0100 | [diff] [blame] | 234 | |
| 235 | nih_option_set_usage (_("TIME [MESSAGE]")); |
Scott James Remnant | eff01f1 | 2006-10-13 13:32:12 +0100 | [diff] [blame] | 236 | nih_option_set_synopsis (_("Bring the system down.")); |
| 237 | nih_option_set_help ( |
| 238 | _("TIME may have different formats, the most common is simply " |
| 239 | "the word 'now' which will bring the system down " |
| 240 | "immediately. Other valid formats are +m, where m is the " |
| 241 | "number of minutes to wait until shutting down and hh:mm " |
| 242 | "which specifies the time on the 24hr clock.\n" |
| 243 | "\n" |
| 244 | "Logged in users are warned by a message sent to their " |
| 245 | "terminal, you may include an optional MESSAGE included " |
| 246 | "with this. Messages can be sent without actually " |
| 247 | "bringing the system down by using the -k option.\n" |
| 248 | "\n" |
| 249 | "If TIME is given, the command will remain in the " |
| 250 | "foreground until the shutdown occurs. It can be cancelled " |
| 251 | "by Control-C, or by another user using the -c option.\n" |
| 252 | "\n" |
| 253 | "The system is brought down into maintenance (single-user) " |
| 254 | "mode by default, you can change this with either the -r or " |
| 255 | "-h option which specify a reboot or system halt " |
| 256 | "respectively. The -h option can be further modified with " |
| 257 | "-H or -P to specify whether to halt the system, or to " |
| 258 | "power it off afterwards. The default is left up to the " |
| 259 | "shutdown scripts.")); |
Scott James Remnant | 4715848 | 2006-08-27 22:37:10 +0100 | [diff] [blame] | 260 | |
| 261 | args = nih_option_parser (NULL, argc, argv, options, FALSE); |
| 262 | if (! args) |
| 263 | exit (1); |
| 264 | |
Scott James Remnant | e8d263c | 2007-02-08 03:20:30 +0000 | [diff] [blame] | 265 | /* If the runlevel wasn't given explicitly, set it to 1 so we go |
| 266 | * down into single-user mode. |
| 267 | */ |
| 268 | if (! runlevel) { |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 269 | runlevel = '1'; |
Scott James Remnant | e8d263c | 2007-02-08 03:20:30 +0000 | [diff] [blame] | 270 | init_halt = NULL; |
Scott James Remnant | e8d263c | 2007-02-08 03:20:30 +0000 | [diff] [blame] | 271 | } |
Scott James Remnant | 4715848 | 2006-08-27 22:37:10 +0100 | [diff] [blame] | 272 | |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 273 | |
| 274 | /* When may be specified with -g, or must be first argument */ |
| 275 | if (! (cancel || when || args[0])) { |
| 276 | fprintf (stderr, _("%s: time expected\n"), program_name); |
| 277 | nih_main_suggest_help (); |
| 278 | exit (1); |
| 279 | } else if (! (cancel || when)) { |
Scott James Remnant | c5d3e1b | 2009-02-20 22:22:48 +0000 | [diff] [blame] | 280 | when = NIH_MUST (nih_strdup (NULL, args[0])); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 281 | arg = 1; |
| 282 | } else { |
| 283 | arg = 0; |
| 284 | } |
| 285 | |
| 286 | /* Parse the time argument */ |
| 287 | if (when) { |
| 288 | if (! strcmp (when, "now")) { |
| 289 | /* "now" means, err, now */ |
| 290 | delay = 0; |
| 291 | } else if (strchr (when, ':')) { |
| 292 | /* Clock time */ |
| 293 | long hours, mins; |
| 294 | char *endptr; |
| 295 | struct tm *tm; |
| 296 | time_t now; |
| 297 | |
| 298 | hours = strtoul (when, &endptr, 10); |
| 299 | if ((*endptr != ':') || (hours < 0) || (hours > 23)) { |
| 300 | fprintf (stderr, _("%s: illegal hour value\n"), |
| 301 | program_name); |
| 302 | nih_main_suggest_help (); |
| 303 | exit (1); |
| 304 | } |
| 305 | |
| 306 | mins = strtoul (endptr + 1, &endptr, 10); |
| 307 | if (*endptr || (mins < 0) || (mins > 59)) { |
| 308 | fprintf (stderr, |
| 309 | _("%s: illegal minute value\n"), |
| 310 | program_name); |
| 311 | nih_main_suggest_help (); |
| 312 | exit (1); |
| 313 | } |
| 314 | |
| 315 | /* Subtract the current time to get the delay. |
| 316 | * Add a whole day if we go negative */ |
| 317 | now = time (NULL); |
| 318 | tm = localtime (&now); |
| 319 | delay = (((hours * 60) + mins) |
| 320 | - ((tm->tm_hour * 60) + tm->tm_min)); |
| 321 | if (delay < 0) |
| 322 | delay += 1440; |
| 323 | } else { |
| 324 | /* Delay in minutes */ |
| 325 | char *endptr; |
| 326 | |
| 327 | delay = strtoul (when, &endptr, 10); |
| 328 | if (*endptr || (delay < 0)) { |
| 329 | fprintf (stderr, _("%s: illegal time value\n"), |
| 330 | program_name); |
| 331 | nih_main_suggest_help (); |
| 332 | exit (1); |
| 333 | } |
| 334 | } |
| 335 | nih_free (when); |
| 336 | } |
| 337 | |
| 338 | |
| 339 | /* The rest of the arguments are a message. |
| 340 | * Really this should be just the next argument, but that's not |
| 341 | * how this has been traditionally done *sigh* |
| 342 | */ |
Scott James Remnant | c5d3e1b | 2009-02-20 22:22:48 +0000 | [diff] [blame] | 343 | message = NIH_MUST (nih_strdup (NULL, "")); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 344 | messagelen = 0; |
| 345 | for (; args[arg]; arg++) { |
Scott James Remnant | 7d59ed2 | 2009-02-20 22:28:03 +0000 | [diff] [blame] | 346 | message = NIH_MUST (nih_realloc ( |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 347 | message, NULL, |
| 348 | messagelen + strlen(args[arg]) + 4)); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 349 | |
| 350 | strcat (message, args[arg]); |
| 351 | strcat (message, " "); |
| 352 | messagelen += strlen (args[arg]) + 1; |
| 353 | } |
| 354 | |
| 355 | /* Terminate with \r\n */ |
| 356 | if (messagelen) |
| 357 | strcat (message, "\r\n"); |
| 358 | |
| 359 | |
| 360 | /* Check we're root, or setuid root */ |
| 361 | setuid (geteuid ()); |
| 362 | if (getuid ()) { |
Scott James Remnant | 31421b7 | 2007-03-08 23:53:05 +0000 | [diff] [blame] | 363 | nih_fatal (_("Need to be root")); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 364 | exit (1); |
| 365 | } |
| 366 | |
Scott James Remnant | f9aec8c | 2007-06-12 12:18:15 +0100 | [diff] [blame] | 367 | /* Look for an existing pid file and deal with the existing |
| 368 | * process if there is one. |
| 369 | */ |
| 370 | pid = nih_main_read_pidfile (); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 371 | if (pid > 0) { |
| 372 | if (cancel) { |
| 373 | if (kill (pid, SIGINT) < 0) { |
| 374 | nih_error (_("Shutdown is not running")); |
| 375 | exit (1); |
| 376 | } |
| 377 | |
| 378 | if (messagelen) |
| 379 | wall (message); |
| 380 | |
| 381 | exit (0); |
| 382 | } else if (kill (pid, 0) == 0) { |
| 383 | nih_error (_("Another shutdown is already running")); |
| 384 | exit (1); |
| 385 | } |
| 386 | } else if (cancel) { |
| 387 | nih_error (_("Cannot find pid of running shutdown")); |
| 388 | exit (1); |
| 389 | } |
| 390 | |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 391 | /* Send an initial message */ |
Scott James Remnant | c5d3e1b | 2009-02-20 22:22:48 +0000 | [diff] [blame] | 392 | msg = NIH_MUST (warning_message (message)); |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 393 | wall (msg); |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 394 | |
Scott James Remnant | edcae30 | 2006-09-04 07:06:23 +0100 | [diff] [blame] | 395 | if (warn_only) |
| 396 | exit (0); |
| 397 | |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 398 | |
| 399 | /* Give us a sane environment */ |
Scott James Remnant | b175de8 | 2009-07-09 12:50:19 +0100 | [diff] [blame] | 400 | if (chdir ("/") < 0) |
| 401 | nih_warn ("%s: %s", _("Unable to change directory"), |
| 402 | strerror (errno)); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 403 | umask (022); |
| 404 | |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 405 | /* Shutdown now? */ |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 406 | if (! delay) |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 407 | shutdown_now (); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 408 | |
| 409 | /* Save our pid so we can be interrupted later */ |
Scott James Remnant | f9aec8c | 2007-06-12 12:18:15 +0100 | [diff] [blame] | 410 | if (nih_main_write_pidfile (getpid ()) < 0) { |
| 411 | NihError *err; |
| 412 | |
| 413 | err = nih_error_get (); |
| 414 | nih_warn ("%s: %s: %s", nih_main_get_pidfile(), |
| 415 | _("Unable to write pid file"), err->message); |
| 416 | nih_free (err); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 417 | } |
| 418 | |
| 419 | |
| 420 | /* Ignore a whole bunch of signals */ |
| 421 | nih_signal_set_ignore (SIGCHLD); |
| 422 | nih_signal_set_ignore (SIGHUP); |
| 423 | nih_signal_set_ignore (SIGTSTP); |
| 424 | nih_signal_set_ignore (SIGTTIN); |
| 425 | nih_signal_set_ignore (SIGTTOU); |
| 426 | |
| 427 | /* Catch the usual quit signals */ |
| 428 | nih_signal_set_handler (SIGINT, nih_signal_handler); |
Scott James Remnant | 3691157 | 2007-02-01 18:18:27 +0000 | [diff] [blame] | 429 | NIH_MUST (nih_signal_add_handler (NULL, SIGINT, |
| 430 | cancel_callback, NULL)); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 431 | nih_signal_set_handler (SIGQUIT, nih_signal_handler); |
Scott James Remnant | 3691157 | 2007-02-01 18:18:27 +0000 | [diff] [blame] | 432 | NIH_MUST (nih_signal_add_handler (NULL, SIGQUIT, |
| 433 | cancel_callback, NULL)); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 434 | nih_signal_set_handler (SIGTERM, nih_signal_handler); |
Scott James Remnant | 3691157 | 2007-02-01 18:18:27 +0000 | [diff] [blame] | 435 | NIH_MUST (nih_signal_add_handler (NULL, SIGTERM, |
| 436 | cancel_callback, NULL)); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 437 | |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 438 | /* Call a timer every minute until we shutdown */ |
Scott James Remnant | 3691157 | 2007-02-01 18:18:27 +0000 | [diff] [blame] | 439 | NIH_MUST (nih_timer_add_periodic (NULL, 60, |
| 440 | (NihTimerCb)timer_callback, |
| 441 | message)); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 442 | |
| 443 | /* Hang around */ |
| 444 | nih_main_loop (); |
| 445 | |
| 446 | return 0; |
| 447 | } |
| 448 | |
| 449 | |
| 450 | /** |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 451 | * shutdown_now: |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 452 | * |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 453 | * Send a signal to init to shut down the machine. |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 454 | * |
| 455 | * This does not return. |
| 456 | **/ |
| 457 | static void |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 458 | shutdown_now (void) |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 459 | { |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 460 | nih_local char **extra_env = NULL; |
| 461 | NihDBusError * dbus_err; |
Petr Lautrbach | 18ff78d | 2010-12-20 09:46:30 +0000 | [diff] [blame] | 462 | int exit_val = 0; |
Scott James Remnant | e8d263c | 2007-02-08 03:20:30 +0000 | [diff] [blame] | 463 | |
| 464 | if (init_halt) { |
| 465 | char *e; |
| 466 | |
Scott James Remnant | c5d3e1b | 2009-02-20 22:22:48 +0000 | [diff] [blame] | 467 | e = NIH_MUST (nih_sprintf (NULL, "INIT_HALT=%s", init_halt)); |
Scott James Remnant | e8d263c | 2007-02-08 03:20:30 +0000 | [diff] [blame] | 468 | |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 469 | extra_env = NIH_MUST (nih_str_array_new (NULL)); |
| 470 | NIH_MUST (nih_str_array_addp (&extra_env, NULL, NULL, e)); |
Scott James Remnant | e8d263c | 2007-02-08 03:20:30 +0000 | [diff] [blame] | 471 | } |
| 472 | |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 473 | if (sysv_change_runlevel (runlevel, extra_env, NULL, NULL) < 0) { |
| 474 | dbus_err = (NihDBusError *)nih_error_get (); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 475 | |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 476 | if ((dbus_err->number != NIH_DBUS_ERROR) |
| 477 | || strcmp (dbus_err->name, DBUS_ERROR_NO_SERVER)) { |
| 478 | nih_fatal ("%s", dbus_err->message); |
Scott James Remnant | 6d61066 | 2010-02-03 20:00:11 -0800 | [diff] [blame] | 479 | nih_free (dbus_err); |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 480 | exit (1); |
| 481 | } |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 482 | |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 483 | nih_free (dbus_err); |
Scott James Remnant | b828007 | 2006-09-07 03:19:00 +0100 | [diff] [blame] | 484 | |
| 485 | /* Connection Refused means that init isn't running, this |
| 486 | * might mean we've just upgraded to upstart and haven't |
| 487 | * yet rebooted ... so try /dev/initctl |
| 488 | */ |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 489 | sysvinit_shutdown (); |
Petr Lautrbach | 18ff78d | 2010-12-20 09:46:30 +0000 | [diff] [blame] | 490 | nih_fatal ("Unable to shutdown system"); |
| 491 | exit_val = 1; |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 492 | } |
| 493 | |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 494 | unlink (ETC_NOLOGIN); |
Scott James Remnant | f9aec8c | 2007-06-12 12:18:15 +0100 | [diff] [blame] | 495 | nih_main_unlink_pidfile (); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 496 | |
Petr Lautrbach | 18ff78d | 2010-12-20 09:46:30 +0000 | [diff] [blame] | 497 | exit (exit_val); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 498 | } |
| 499 | |
| 500 | /** |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 501 | * cancel_callback: |
| 502 | * @data: not used, |
| 503 | * @signal: signal caught. |
| 504 | * |
| 505 | * This callback is run whenever one of the "cancel running shutdown" |
| 506 | * signals is sent to us. |
| 507 | * |
| 508 | * This does not return. |
| 509 | **/ |
| 510 | static void |
| 511 | cancel_callback (void *data, |
| 512 | NihSignal *signal) |
| 513 | { |
| 514 | nih_error (_("Shutdown cancelled")); |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 515 | unlink (ETC_NOLOGIN); |
Scott James Remnant | f9aec8c | 2007-06-12 12:18:15 +0100 | [diff] [blame] | 516 | nih_main_unlink_pidfile (); |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 517 | exit (0); |
| 518 | } |
| 519 | |
| 520 | /** |
| 521 | * timer_callback: |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 522 | * @message: message to display. |
| 523 | * |
| 524 | * This callback is run every minute until we are ready to shutdown, it |
| 525 | * ensures regular warnings are sent to logged in users and handles |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 526 | * preventing new logins. Once time is up, it handles shutting down. |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 527 | * |
| 528 | * This will modify delay each time it is called. |
| 529 | **/ |
| 530 | static void |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 531 | timer_callback (const char *message) |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 532 | { |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 533 | nih_local char *msg = NULL; |
| 534 | int warn = FALSE; |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 535 | |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 536 | delay--; |
Scott James Remnant | c5d3e1b | 2009-02-20 22:22:48 +0000 | [diff] [blame] | 537 | msg = NIH_MUST (warning_message (message)); |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 538 | |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 539 | |
| 540 | /* Write /etc/nologin with less than 5 minutes remaining */ |
| 541 | if (delay <= 5) { |
| 542 | FILE *nologin; |
| 543 | |
| 544 | nologin = fopen (ETC_NOLOGIN, "w"); |
| 545 | if (nologin) { |
| 546 | fputs (msg, nologin); |
| 547 | fclose (nologin); |
| 548 | } |
| 549 | } |
| 550 | |
| 551 | /* Only warn at particular intervals */ |
| 552 | if (delay < 10) { |
| 553 | warn = TRUE; |
| 554 | } else if (delay < 60) { |
| 555 | warn = (delay % 15 ? FALSE : TRUE); |
| 556 | } else if (delay < 180) { |
| 557 | warn = (delay % 30 ? FALSE : TRUE); |
| 558 | } else { |
| 559 | warn = (delay % 60 ? FALSE : TRUE); |
| 560 | } |
| 561 | |
| 562 | if (warn) |
| 563 | wall (msg); |
| 564 | |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 565 | /* Shutdown the machine at zero */ |
| 566 | if (! delay) |
| 567 | shutdown_now (); |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 568 | } |
| 569 | |
Scott James Remnant | 0b358ab | 2006-08-31 02:07:32 +0100 | [diff] [blame] | 570 | |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 571 | /** |
| 572 | * warning_message: |
| 573 | * @message: user message. |
| 574 | * |
| 575 | * Prefixes the message with details about how long until the shutdown |
| 576 | * completes. |
| 577 | * |
| 578 | * Returns: newly allocated string. |
| 579 | **/ |
| 580 | static char * |
| 581 | warning_message (const char *message) |
| 582 | { |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 583 | nih_local char *banner = NULL; |
| 584 | char * msg; |
Scott James Remnant | e371cab | 2006-09-18 16:16:00 +0100 | [diff] [blame] | 585 | |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 586 | nih_assert (message != NULL); |
| 587 | |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 588 | if ((runlevel == '0') |
| 589 | && init_halt && (! strcmp (init_halt, "POWEROFF"))) { |
| 590 | if (delay) { |
| 591 | banner = nih_sprintf ( |
| 592 | NULL, _n("The system is going down for " |
| 593 | "power off in %d minute!", |
| 594 | "The system is going down for " |
| 595 | "power off in %d minutes!", |
| 596 | delay), delay); |
| 597 | } else { |
| 598 | banner = nih_strdup ( |
| 599 | NULL, _("The system is going down for " |
| 600 | "power off NOW!")); |
| 601 | } |
| 602 | } else if (runlevel == '0') { |
| 603 | if (delay) { |
| 604 | banner = nih_sprintf ( |
| 605 | NULL, _n("The system is going down for " |
| 606 | "halt in %d minute!", |
| 607 | "The system is going down for " |
| 608 | "halt in %d minutes!", |
| 609 | delay), delay); |
| 610 | } else { |
| 611 | banner = nih_strdup ( |
| 612 | NULL, _("The system is going down for " |
| 613 | "halt NOW!")); |
| 614 | } |
| 615 | } else if (runlevel == '1') { |
| 616 | if (delay) { |
| 617 | banner = nih_sprintf ( |
| 618 | NULL, _n("The system is going down for " |
| 619 | "maintenance in %d minute!", |
| 620 | "The system is going down for " |
| 621 | "maintenance in %d minutes!", |
| 622 | delay), delay); |
| 623 | } else { |
| 624 | banner = nih_strdup ( |
| 625 | NULL, _("The system is going down for " |
| 626 | "maintenance NOW!")); |
| 627 | } |
| 628 | } else if (runlevel == '6') { |
| 629 | if (delay) { |
| 630 | banner = nih_sprintf ( |
| 631 | NULL, _n("The system is going down for " |
| 632 | "reboot in %d minute!", |
| 633 | "The system is going down for " |
| 634 | "reboot in %d minutes!", |
| 635 | delay), delay); |
| 636 | } else { |
| 637 | banner = nih_strdup ( |
| 638 | NULL, _("The system is going down for " |
| 639 | "reboot NOW!")); |
| 640 | } |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 641 | } |
| 642 | |
Scott James Remnant | e371cab | 2006-09-18 16:16:00 +0100 | [diff] [blame] | 643 | if (! banner) |
| 644 | return NULL; |
| 645 | |
| 646 | msg = nih_sprintf (NULL, "\r%s\r\n%s", banner, message); |
Scott James Remnant | e371cab | 2006-09-18 16:16:00 +0100 | [diff] [blame] | 647 | |
| 648 | return msg; |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 649 | } |
| 650 | |
| 651 | /** |
| 652 | * alarm_handler: |
| 653 | * @signum: signal called. |
| 654 | * |
| 655 | * Empty function used to cause the ALRM signal to break a syscall. |
| 656 | **/ |
| 657 | static void |
| 658 | alarm_handler (int signum) |
| 659 | { |
| 660 | } |
| 661 | |
| 662 | /** |
| 663 | * wall: |
| 664 | * @message: message to send. |
| 665 | * |
| 666 | * Send a message to all logged in users; based largely on the code from |
| 667 | * bsdutils. This is done in a child process to stop anything blocking. |
| 668 | **/ |
| 669 | static void |
| 670 | wall (const char *message) |
| 671 | { |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 672 | struct sigaction act; |
| 673 | struct utmpx * ent; |
| 674 | pid_t pid; |
| 675 | time_t now; |
| 676 | struct tm * tm; |
| 677 | char * user; |
| 678 | char * tty; |
| 679 | char hostname[MAXHOSTNAMELEN]; |
| 680 | char * banner1; |
| 681 | char * banner2; |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 682 | |
| 683 | pid = fork (); |
| 684 | if (pid < 0) { |
| 685 | nih_warn (_("Unable to fork child-process to warn users: %s"), |
| 686 | strerror (errno)); |
| 687 | return; |
| 688 | } else if (pid > 0) { |
| 689 | return; |
| 690 | } |
| 691 | |
| 692 | /* Break syscalls with SIGALRM */ |
| 693 | act.sa_handler = alarm_handler; |
| 694 | act.sa_flags = 0; |
| 695 | sigemptyset (&act.sa_mask); |
| 696 | sigaction (SIGALRM, &act, NULL); |
| 697 | |
| 698 | |
| 699 | /* Get username for banner */ |
| 700 | user = getlogin (); |
| 701 | if (! user) { |
| 702 | struct passwd *pw; |
| 703 | |
| 704 | pw = getpwuid (getuid ()); |
| 705 | if (pw) |
| 706 | user = pw->pw_name; |
| 707 | } |
| 708 | if (! user) { |
| 709 | if (getuid ()) { |
Scott James Remnant | c5d3e1b | 2009-02-20 22:22:48 +0000 | [diff] [blame] | 710 | user = NIH_MUST (nih_sprintf (NULL, "uid %d", |
Scott James Remnant | 0b358ab | 2006-08-31 02:07:32 +0100 | [diff] [blame] | 711 | getuid ())); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 712 | } else { |
| 713 | user = "root"; |
| 714 | } |
| 715 | } |
| 716 | |
Scott James Remnant | d4cdaca | 2006-08-31 00:47:11 +0100 | [diff] [blame] | 717 | /* Get hostname for banner */ |
| 718 | gethostname (hostname, sizeof (hostname)); |
| 719 | |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 720 | /* Get terminal for banner */ |
| 721 | tty = ttyname (0); |
| 722 | if (! tty) |
| 723 | tty = "unknown"; |
| 724 | |
| 725 | /* Get time */ |
| 726 | now = time (NULL); |
| 727 | tm = localtime (&now); |
| 728 | |
| 729 | /* Construct banner */ |
Scott James Remnant | 136dc6f | 2006-12-14 12:36:43 +0000 | [diff] [blame] | 730 | banner1 = nih_sprintf (NULL, _("Broadcast message from %s@%s"), |
| 731 | user, hostname); |
| 732 | banner2 = nih_sprintf (NULL, _("(%s) at %d:%02d ..."), |
| 733 | tty, tm->tm_hour, tm->tm_min); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 734 | |
| 735 | |
| 736 | /* Iterate entries in the utmp file */ |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 737 | setutxent (); |
| 738 | while ((ent = getutxent ()) != NULL) { |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 739 | char dev[PATH_MAX + 1]; |
| 740 | int fd; |
| 741 | |
| 742 | /* Ignore entries without a name, or not a user process */ |
| 743 | if ((ent->ut_type != USER_PROCESS) |
| 744 | || (! strlen (ent->ut_user))) |
| 745 | continue; |
| 746 | |
| 747 | /* Construct the device path */ |
| 748 | if (strncmp (ent->ut_line, DEV "/", 5)) { |
| 749 | snprintf (dev, sizeof (dev), |
| 750 | "%s/%s", DEV, ent->ut_line); |
| 751 | } else { |
| 752 | snprintf (dev, sizeof (dev), "%s", ent->ut_line); |
| 753 | } |
| 754 | |
| 755 | alarm (2); |
| 756 | fd = open (dev, O_WRONLY | O_NDELAY | O_NOCTTY); |
| 757 | if ((fd >= 0) && isatty (fd)) { |
| 758 | FILE *term; |
| 759 | |
| 760 | term = fdopen (fd, "w"); |
| 761 | if (term) { |
Scott James Remnant | 136dc6f | 2006-12-14 12:36:43 +0000 | [diff] [blame] | 762 | fprintf (term, "\007\r\n%s\r\n\t%s\r\n\r\n", |
| 763 | banner1, banner2); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 764 | fputs (message, term); |
| 765 | fflush (term); |
| 766 | fclose (term); |
| 767 | } |
| 768 | } |
| 769 | alarm (0); |
| 770 | } |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 771 | endutxent (); |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 772 | |
Scott James Remnant | 136dc6f | 2006-12-14 12:36:43 +0000 | [diff] [blame] | 773 | nih_free (banner1); |
| 774 | nih_free (banner2); |
| 775 | |
Scott James Remnant | 4a9245f | 2006-08-30 19:13:00 +0100 | [diff] [blame] | 776 | exit (0); |
Scott James Remnant | 4715848 | 2006-08-27 22:37:10 +0100 | [diff] [blame] | 777 | } |
Scott James Remnant | b828007 | 2006-09-07 03:19:00 +0100 | [diff] [blame] | 778 | |
| 779 | |
| 780 | /** |
| 781 | * struct request: |
| 782 | * |
| 783 | * This is the structure passed across /dev/initctl. |
| 784 | **/ |
| 785 | struct request { |
| 786 | int magic; |
| 787 | int cmd; |
| 788 | int runlevel; |
| 789 | int sleeptime; |
| 790 | char data[368]; |
| 791 | }; |
| 792 | |
| 793 | /** |
| 794 | * sysvinit_shutdown: |
| 795 | * |
| 796 | * Attempt to shutdown a running sysvinit /sbin/init using its /dev/initctl |
| 797 | * socket. |
| 798 | **/ |
| 799 | static void |
| 800 | sysvinit_shutdown (void) |
| 801 | { |
| 802 | struct sigaction act; |
| 803 | struct request request; |
| 804 | int fd; |
| 805 | |
| 806 | /* Fill in the magic values */ |
| 807 | memset (&request, 0, sizeof (request)); |
| 808 | request.magic = 0x03091969; |
| 809 | request.sleeptime = 5; |
| 810 | request.cmd = 1; |
| 811 | |
| 812 | /* Select a runlevel based on the event name */ |
Scott James Remnant | f24e55d | 2009-07-08 19:58:37 +0100 | [diff] [blame] | 813 | request.runlevel = runlevel; |
Scott James Remnant | b828007 | 2006-09-07 03:19:00 +0100 | [diff] [blame] | 814 | |
| 815 | |
| 816 | /* Break syscalls with SIGALRM */ |
| 817 | act.sa_handler = alarm_handler; |
| 818 | act.sa_flags = 0; |
| 819 | sigemptyset (&act.sa_mask); |
| 820 | sigaction (SIGALRM, &act, NULL); |
| 821 | |
| 822 | /* Try and open /dev/initctl */ |
| 823 | alarm (3); |
Scott James Remnant | 7e6bdb3 | 2006-09-14 10:57:03 +0100 | [diff] [blame] | 824 | fd = open (DEV_INITCTL, O_WRONLY | O_NDELAY | O_NOCTTY); |
Scott James Remnant | b828007 | 2006-09-07 03:19:00 +0100 | [diff] [blame] | 825 | if (fd >= 0) { |
| 826 | if (write (fd, &request, sizeof (request)) == sizeof (request)) |
| 827 | exit (0); |
| 828 | |
| 829 | close (fd); |
| 830 | } |
| 831 | |
| 832 | alarm (0); |
| 833 | } |