Fix missing #include (needed for compiling with GCC 10)
[oweals/dinit.git] / src / shutdown.cc
index c9bb8b50425020df03766566f43bba58d0ccb1b7..c82af94d7f2ea87f2d3215ae4ef8f572954921a1 100644 (file)
@@ -1,10 +1,10 @@
 #include <cstddef>
 #include <cstdio>
 #include <csignal>
-#include <unistd.h>
 #include <cstring>
 #include <string>
 #include <iostream>
+#include <exception>
 
 #include <sys/reboot.h>
 #include <sys/types.h>
 #include <sys/un.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
+#include <unistd.h>
 #include <fcntl.h>
 
 #include "cpbuffer.h"
 #include "control-cmds.h"
 #include "service-constants.h"
 #include "dinit-client.h"
+#include "dinit-util.h"
+#include "mconfig.h"
 
 #include "dasynq.h"
 
 // shutdown:  shut down the system
-// This utility communicates with the dinit daemon via a unix socket (/dev/initctl).
+// This utility communicates with the dinit daemon via a unix socket (specified in SYSCONTROLSOCKET).
+
+static constexpr uint16_t min_cp_version = 1;
+static constexpr uint16_t max_cp_version = 1;
 
 using loop_t = dasynq::event_loop_n;
 using rearm = dasynq::rearm;
@@ -243,6 +249,11 @@ int main(int argc, char **argv)
     bool use_passed_cfd = false;
     
     auto shutdown_type = shutdown_type_t::POWEROFF;
+
+    const char *execname = base_name(argv[0]);
+    if (strcmp(execname, "reboot") == 0) {
+        shutdown_type = shutdown_type_t::REBOOT;
+    }
         
     for (int i = 1; i < argc; i++) {
         if (argv[i][0] == '-') {
@@ -278,16 +289,16 @@ int main(int argc, char **argv)
     }
 
     if (show_help) {
-        cout << "dinit-shutdown :   shutdown the system" << endl;
-        cout << "  --help           : show this help" << endl;
-        cout << "  -r               : reboot" << endl;
-        cout << "  -h               : halt system" << endl;
-        cout << "  -p               : power down (default)" << endl;
-        cout << "  --use-passed-cfd : use the socket file descriptor identified by the DINIT_CS_FD" << endl;
-        cout << "                     environment variable to communicate with the init daemon." << endl;
-        cout << "  --system         : perform shutdown immediately, instead of issuing shutdown" << endl;
-        cout << "                     command to the init program. Not recommended for use" << endl;
-        cout << "                     by users." << endl;
+        cout << execname << " :   shutdown the system\n"
+                "  --help           : show this help\n"
+                "  -r               : reboot\n"
+                "  -h               : halt system\n"
+                "  -p               : power down (default)\n"
+                "  --use-passed-cfd : use the socket file descriptor identified by the DINIT_CS_FD\n"
+                "                     environment variable to communicate with the init daemon.\n"
+                "  --system         : perform shutdown immediately, instead of issuing shutdown\n"
+                "                     command to the init program. Not recommended for use\n"
+                "                     by users.\n";
         return 1;
     }
     
@@ -327,7 +338,7 @@ int main(int argc, char **argv)
             return 1;
         }
         
-        const char *naddr = "/dev/dinitctl";
+        const char *naddr = SYSCONTROLSOCKET;
         
         struct sockaddr_un name;
         name.sun_family = AF_UNIX;
@@ -344,7 +355,7 @@ int main(int argc, char **argv)
     try {
         cpbuffer_t rbuffer;
     
-        check_protocol_version(0, 0, rbuffer, socknum);
+        check_protocol_version(min_cp_version, max_cp_version, rbuffer, socknum);
 
         // Build buffer;
         constexpr int bufsize = 2;
@@ -527,46 +538,82 @@ static void run_process(const char * prog_args[], loop_t &loop, subproc_buffer &
     sp_watcher_t sp_watcher;
 
     // Create output pipe
+    bool have_pipe = true;
     int pipefds[2];
     if (dasynq::pipe2(pipefds, O_NONBLOCK) == -1) {
-        // TODO
-        std::cout << "*** pipe2 failed ***" << std::endl;
+        sub_buf.append("Warning: ");
+        sub_buf.append(prog_args[0]);
+        sub_buf.append(": could not create pipe for subprocess output\n");
+        have_pipe = false;
+        // Note, we proceed and let the sub-process run with our stdout/stderr.
     }
 
+    subproc_out_watch owatch {sub_buf};
+
+    if (have_pipe) {
+        close(pipefds[1]);
+        try {
+            owatch.add_watch(loop, pipefds[0], dasynq::IN_EVENTS);
+        }
+        catch (...) {
+            // failed to create the watcher for the subprocess output; again, let it run with
+            // our stdout/stderr
+            sub_buf.append("Warning: could not create output watch for subprocess\n");
+            close(pipefds[0]);
+            have_pipe = false;
+        }
+    }
+
+    // If we've buffered any messages/output, give them a chance to go out now:
+    loop.poll();
+
     pid_t ch_pid = sp_watcher.fork(loop);
     if (ch_pid == 0) {
         // child
         // Dup output pipe to stdout, stderr
-        dup2(pipefds[1], STDOUT_FILENO);
-        dup2(pipefds[1], STDERR_FILENO);
-        close(pipefds[0]);
-        close(pipefds[1]);
+        if (have_pipe) {
+            dup2(pipefds[1], STDOUT_FILENO);
+            dup2(pipefds[1], STDERR_FILENO);
+            close(pipefds[0]);
+            close(pipefds[1]);
+        }
         execv(prog_args[0], const_cast<char **>(prog_args));
-        puts("Failed to execute subprocess:\n");
+        puts("Failed to execute subprocess: ");
         perror(prog_args[0]);
         _exit(1);
     }
 
-    close(pipefds[1]);
-
-    subproc_out_watch owatch {sub_buf};
-    owatch.add_watch(loop, pipefds[0], dasynq::IN_EVENTS);
-
     do {
         loop.run();
     } while (! sp_watcher.terminated);
 
-    owatch.deregister(loop);
+    if (have_pipe) {
+        owatch.deregister(loop);
+    }
 }
 
 static void unmount_disks(loop_t &loop, subproc_buffer &sub_buf)
 {
-    const char * unmount_args[] = { "/bin/umount", "-a", "-r", nullptr };
-    run_process(unmount_args, loop, sub_buf);
+    try {
+        const char * unmount_args[] = { "/bin/umount", "-a", "-r", nullptr };
+        run_process(unmount_args, loop, sub_buf);
+    }
+    catch (std::exception &e) {
+        sub_buf.append("Couldn't fork for umount: ");
+        sub_buf.append(e.what());
+        sub_buf.append("\n");
+    }
 }
 
 static void swap_off(loop_t &loop, subproc_buffer &sub_buf)
 {
-    const char * swapoff_args[] = { "/sbin/swapoff", "-a", nullptr };
-    run_process(swapoff_args, loop, sub_buf);
+    try {
+        const char * swapoff_args[] = { "/sbin/swapoff", "-a", nullptr };
+        run_process(swapoff_args, loop, sub_buf);
+    }
+    catch (std::exception &e) {
+        sub_buf.append("Couldn't fork for swapoff: ");
+        sub_buf.append(e.what());
+        sub_buf.append("\n");
+    }
 }