Move source files int src directory
[oweals/dinit.git] / src / shutdown.cc
1 // #include <netinet/in.h>
2 #include <cstddef>
3 #include <cstdio>
4 #include <csignal>
5 #include <unistd.h>
6 #include <cstring>
7 #include <string>
8 #include <iostream>
9
10 #include <sys/reboot.h>
11 #include <sys/types.h>
12 #include <sys/socket.h>
13 #include <sys/un.h>
14 #include <sys/wait.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17
18 #include "control-cmds.h"
19 #include "service-constants.h"
20
21 // shutdown:  shut down the system
22 // This utility communicates with the dinit daemon via a unix socket (/dev/initctl).
23
24 void do_system_shutdown(ShutdownType shutdown_type);
25 static void unmount_disks();
26 static void swap_off();
27
28 int main(int argc, char **argv)
29 {
30     using namespace std;
31     
32     bool show_help = false;
33     bool sys_shutdown = false;
34     
35     auto shutdown_type = ShutdownType::POWEROFF;
36         
37     for (int i = 1; i < argc; i++) {
38         if (argv[i][0] == '-') {
39             if (strcmp(argv[i], "--help") == 0) {
40                 show_help = true;
41                 break;
42             }
43             
44             if (strcmp(argv[i], "--system") == 0) {
45                 sys_shutdown = true;
46             }
47             else if (strcmp(argv[i], "-r") == 0) {
48                 shutdown_type = ShutdownType::REBOOT;
49             }
50             else if (strcmp(argv[i], "-h") == 0) {
51                 shutdown_type = ShutdownType::HALT;
52             }
53             else if (strcmp(argv[i], "-p") == 0) {
54                 shutdown_type = ShutdownType::POWEROFF;
55             }
56             else {
57                 cerr << "Unrecognized command-line parameter: " << argv[i] << endl;
58                 return 1;
59             }
60         }
61         else {
62             // time argument? TODO
63             show_help = true;
64         }
65     }
66
67     if (show_help) {
68         cout << "dinit-shutdown :   shutdown the system" << endl;
69         cout << "  --help           : show this help" << endl;
70         cout << "  -r               : reboot" << endl;
71         cout << "  -h               : halt system" << endl;
72         cout << "  -p               : power down (default)" << endl;
73         cout << "  --system         : perform shutdown immediately, instead of issuing shutdown" << endl;
74         cout << "                     command to the init program. Not recommended for use" << endl;
75         cout << "                     by users." << endl;
76         return 1;
77     }
78     
79     if (sys_shutdown) {
80         do_system_shutdown(shutdown_type);
81         return 0;
82     }
83     
84     int socknum = socket(AF_UNIX, SOCK_STREAM, 0);
85     if (socknum == -1) {
86         perror("socket");
87         return 1;
88     }
89
90     const char *naddr = "/dev/dinitctl";
91     
92     struct sockaddr_un name;
93     name.sun_family = AF_UNIX;
94     strcpy(name.sun_path, naddr);
95     int sunlen = offsetof(struct sockaddr_un, sun_path) + strlen(naddr) + 1; // family, (string), nul
96     
97     int connr = connect(socknum, (struct sockaddr *) &name, sunlen);
98     if (connr == -1) {
99         perror("connect");
100         return 1;
101     }
102     
103     // Build buffer;
104     //uint16_t sname_len = strlen(service_name);
105     int bufsize = 2;
106     char * buf = new char[bufsize];
107     
108     buf[0] = DINIT_CP_SHUTDOWN;
109     buf[1] = static_cast<char>(shutdown_type);
110     
111     cout << "Issuing shutdown command..." << endl; // DAV
112     
113     // TODO make sure to write the whole buffer
114     int r = write(socknum, buf, bufsize);
115     if (r == -1) {
116         perror("write");
117     }
118     
119     // Wait for ACK/NACK
120     r = read(socknum, buf, 1);
121     // TODO: check result
122     
123     return 0;
124 }
125
126 void do_system_shutdown(ShutdownType shutdown_type)
127 {
128     using namespace std;
129     
130     int reboot_type = 0;
131     if (shutdown_type == ShutdownType::REBOOT) reboot_type = RB_AUTOBOOT;
132     else if (shutdown_type == ShutdownType::POWEROFF) reboot_type = RB_POWER_OFF;
133     else reboot_type = RB_HALT_SYSTEM;
134     
135     // Write to console rather than any terminal, since we lose the terminal it seems:
136     close(STDOUT_FILENO);
137     int consfd = open("/dev/console", O_WRONLY);
138     if (consfd != STDOUT_FILENO) {
139         dup2(consfd, STDOUT_FILENO);
140     }
141     
142     cout << "Sending TERM/KILL to all processes..." << endl; // DAV
143     
144     // Send TERM/KILL to all (remaining) processes
145     kill(-1, SIGTERM);
146     sleep(1);
147     kill(-1, SIGKILL);
148     
149     // cout << "Sending QUIT to init..." << endl; // DAV
150     
151     // Tell init to exec reboot:
152     // TODO what if it's not PID=1? probably should have dinit pass us its PID
153     // kill(1, SIGQUIT);
154     
155     // TODO can we wait somehow for above to work?
156     // maybe have a pipe/socket and we read from our end...
157     
158     // TODO: close all ancillary file descriptors.
159     
160     // perform shutdown
161     cout << "Turning off swap..." << endl;
162     swap_off();
163     cout << "Unmounting disks..." << endl;
164     unmount_disks();
165     sync();
166     
167     cout << "Issuing shutdown via kernel..." << endl;
168     reboot(reboot_type);
169 }
170
171 static void unmount_disks()
172 {
173     pid_t chpid = fork();
174     if (chpid == 0) {
175         // umount -a -r
176         //  -a : all filesystems (except proc)
177         //  -r : mount readonly if can't unmount
178         execl("/bin/umount", "/bin/umount", "-a", "-r", nullptr);
179     }
180     else if (chpid > 0) {
181         int status;
182         waitpid(chpid, &status, 0);
183     }
184 }
185
186 static void swap_off()
187 {
188     pid_t chpid = fork();
189     if (chpid == 0) {
190         // swapoff -a
191         execl("/sbin/swapoff", "/sbin/swapoff", "-a", nullptr);
192     }
193     else if (chpid > 0) {
194         int status;
195         waitpid(chpid, &status, 0);
196     }
197 }