+ int c, runlevel;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ if (utmp_get_runlevel(&runlevel, NULL) >= 0)
+ if (runlevel == '0' || runlevel == '6')
+ arg_immediate = true;
+
+ while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0) {
+ switch (c) {
+
+ case ARG_HELP:
+ halt_help();
+ return 0;
+
+ case ARG_HALT:
+ arg_action = ACTION_HALT;
+ break;
+
+ case 'p':
+ arg_action = ACTION_POWEROFF;
+ break;
+
+ case ARG_REBOOT:
+ arg_action = ACTION_REBOOT;
+ break;
+
+ case 'f':
+ arg_immediate = true;
+ break;
+
+ case 'w':
+ arg_dry = true;
+ break;
+
+ case 'd':
+ arg_no_wtmp = true;
+ break;
+
+ case 'n':
+ arg_no_sync = true;
+ break;
+
+ case ARG_NO_WALL:
+ arg_no_wall = true;
+ break;
+
+ case 'i':
+ case 'h':
+ /* Compatibility nops */
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ if (optind < argc) {
+ log_error("Too many arguments.");
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+static int shutdown_parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_HELP = 0x100,
+ ARG_NO_WALL
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, ARG_HELP },
+ { "halt", no_argument, NULL, 'H' },
+ { "poweroff", no_argument, NULL, 'P' },
+ { "reboot", no_argument, NULL, 'r' },
+ { "no-wall", no_argument, NULL, ARG_NO_WALL },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "HPrhkt:a", options, NULL)) >= 0) {
+ switch (c) {
+
+ case ARG_HELP:
+ shutdown_help();
+ return 0;
+
+ case 'H':
+ arg_action = ACTION_HALT;
+ break;
+
+ case 'P':
+ arg_action = ACTION_POWEROFF;
+ break;
+
+ case 'r':
+ arg_action = ACTION_REBOOT;
+ break;
+
+ case 'h':
+ if (arg_action != ACTION_HALT)
+ arg_action = ACTION_POWEROFF;
+ break;
+
+ case 'k':
+ arg_dry = true;
+ break;
+
+ case ARG_NO_WALL:
+ arg_no_wall = true;
+ break;
+
+ case 't':
+ case 'a':
+ /* Compatibility nops */
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ if (argc > optind && !streq(argv[optind], "now"))
+ log_warning("First argument '%s' isn't 'now'. Ignoring.", argv[optind]);
+
+ /* We ignore the time argument */
+ if (argc > optind + 1)
+ arg_wall = argv + optind + 1;
+
+ optind = argc;
+
+ return 1;
+}
+
+static int telinit_parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_HELP = 0x100,
+ ARG_NO_WALL
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, ARG_HELP },
+ { "no-wall", no_argument, NULL, ARG_NO_WALL },
+ { NULL, 0, NULL, 0 }
+ };
+
+ static const struct {
+ char from;
+ enum action to;
+ } table[] = {
+ { '0', ACTION_POWEROFF },
+ { '6', ACTION_REBOOT },
+ { '1', ACTION_RESCUE },
+ { '2', ACTION_RUNLEVEL2 },
+ { '3', ACTION_RUNLEVEL3 },
+ { '4', ACTION_RUNLEVEL4 },
+ { '5', ACTION_RUNLEVEL5 },
+ { 's', ACTION_RESCUE },
+ { 'S', ACTION_RESCUE },
+ { 'q', ACTION_RELOAD },
+ { 'Q', ACTION_RELOAD },
+ { 'u', ACTION_REEXEC },
+ { 'U', ACTION_REEXEC }
+ };
+
+ unsigned i;
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
+ switch (c) {
+
+ case ARG_HELP:
+ telinit_help();
+ return 0;
+
+ case ARG_NO_WALL:
+ arg_no_wall = true;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ if (optind >= argc) {
+ log_error("Argument missing.");
+ return -EINVAL;
+ }
+
+ if (optind + 1 < argc) {
+ log_error("Too many arguments.");
+ return -EINVAL;
+ }
+
+ if (strlen(argv[optind]) != 1) {
+ log_error("Expected single character argument.");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ELEMENTSOF(table); i++)
+ if (table[i].from == argv[optind][0])
+ break;
+
+ if (i >= ELEMENTSOF(table)) {
+ log_error("Unknown command %s.", argv[optind]);
+ return -EINVAL;
+ }
+
+ arg_action = table[i].to;
+
+ optind ++;
+
+ return 1;
+}
+
+static int runlevel_parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_HELP = 0x100,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, ARG_HELP },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
+ switch (c) {
+
+ case ARG_HELP:
+ runlevel_help();
+ return 0;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ if (optind < argc) {
+ log_error("Too many arguments.");
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+ assert(argc >= 0);
+ assert(argv);
+
+ if (program_invocation_short_name) {
+
+ if (strstr(program_invocation_short_name, "halt")) {
+ arg_action = ACTION_HALT;
+ return halt_parse_argv(argc, argv);
+ } else if (strstr(program_invocation_short_name, "poweroff")) {
+ arg_action = ACTION_POWEROFF;
+ return halt_parse_argv(argc, argv);
+ } else if (strstr(program_invocation_short_name, "reboot")) {
+ arg_action = ACTION_REBOOT;
+ return halt_parse_argv(argc, argv);
+ } else if (strstr(program_invocation_short_name, "shutdown")) {
+ arg_action = ACTION_POWEROFF;
+ return shutdown_parse_argv(argc, argv);
+ } else if (strstr(program_invocation_short_name, "init")) {
+ arg_action = ACTION_INVALID;
+ return telinit_parse_argv(argc, argv);
+ } else if (strstr(program_invocation_short_name, "runlevel")) {
+ arg_action = ACTION_RUNLEVEL;
+ return runlevel_parse_argv(argc, argv);
+ }
+ }
+
+ arg_action = ACTION_SYSTEMCTL;
+ return systemctl_parse_argv(argc, argv);
+}
+
+static int action_to_runlevel(void) {
+
+ static const char table[_ACTION_MAX] = {
+ [ACTION_HALT] = '0',
+ [ACTION_POWEROFF] = '0',
+ [ACTION_REBOOT] = '6',
+ [ACTION_RUNLEVEL2] = '2',
+ [ACTION_RUNLEVEL3] = '3',
+ [ACTION_RUNLEVEL4] = '4',
+ [ACTION_RUNLEVEL5] = '5',
+ [ACTION_RESCUE] = '1'
+ };
+
+ assert(arg_action < _ACTION_MAX);
+
+ return table[arg_action];
+}
+
+static int talk_upstart(void) {
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusError error;
+ int previous, rl, r;
+ char
+ env1_buf[] = "RUNLEVEL=X",
+ env2_buf[] = "PREVLEVEL=X";
+ char *env1 = env1_buf, *env2 = env2_buf;
+ const char *emit = "runlevel";
+ dbus_bool_t b_false = FALSE;
+ DBusMessageIter iter, sub;
+ DBusConnection *bus;