9 #include <sys/reboot.h>
10 #include <sys/types.h>
11 #include <sys/socket.h>
18 #include "control-cmds.h"
19 #include "service-constants.h"
20 #include "dinit-client.h"
22 // shutdown: shut down the system
23 // This utility communicates with the dinit daemon via a unix socket (/dev/initctl).
25 void do_system_shutdown(shutdown_type_t shutdown_type);
26 static void unmount_disks();
27 static void swap_off();
30 int main(int argc, char **argv)
34 bool show_help = false;
35 bool sys_shutdown = false;
36 bool use_passed_cfd = false;
38 auto shutdown_type = shutdown_type_t::POWEROFF;
40 for (int i = 1; i < argc; i++) {
41 if (argv[i][0] == '-') {
42 if (strcmp(argv[i], "--help") == 0) {
47 if (strcmp(argv[i], "--system") == 0) {
50 else if (strcmp(argv[i], "-r") == 0) {
51 shutdown_type = shutdown_type_t::REBOOT;
53 else if (strcmp(argv[i], "-h") == 0) {
54 shutdown_type = shutdown_type_t::HALT;
56 else if (strcmp(argv[i], "-p") == 0) {
57 shutdown_type = shutdown_type_t::POWEROFF;
59 else if (strcmp(argv[i], "--use-passed-cfd") == 0) {
60 use_passed_cfd = true;
63 cerr << "Unrecognized command-line parameter: " << argv[i] << endl;
68 // time argument? TODO
74 cout << "dinit-shutdown : shutdown the system" << endl;
75 cout << " --help : show this help" << endl;
76 cout << " -r : reboot" << endl;
77 cout << " -h : halt system" << endl;
78 cout << " -p : power down (default)" << endl;
79 cout << " --use-passed-cfd : use the socket file descriptor identified by the DINIT_CS_FD" << endl;
80 cout << " environment variable to communicate with the init daemon." << endl;
81 cout << " --system : perform shutdown immediately, instead of issuing shutdown" << endl;
82 cout << " command to the init program. Not recommended for use" << endl;
83 cout << " by users." << endl;
88 do_system_shutdown(shutdown_type);
92 signal(SIGPIPE, SIG_IGN);
97 char * dinit_cs_fd_env = getenv("DINIT_CS_FD");
98 if (dinit_cs_fd_env != nullptr) {
100 long int cfdnum = strtol(dinit_cs_fd_env, &endptr, 10);
101 if (endptr != dinit_cs_fd_env) {
102 socknum = (int) cfdnum;
105 use_passed_cfd = false;
109 use_passed_cfd = false;
113 if (! use_passed_cfd) {
114 socknum = socket(AF_UNIX, SOCK_STREAM, 0);
120 const char *naddr = "/dev/dinitctl";
122 struct sockaddr_un name;
123 name.sun_family = AF_UNIX;
124 strcpy(name.sun_path, naddr);
125 int sunlen = offsetof(struct sockaddr_un, sun_path) + strlen(naddr) + 1; // family, (string), nul
127 int connr = connect(socknum, (struct sockaddr *) &name, sunlen);
135 constexpr int bufsize = 2;
138 buf[0] = DINIT_CP_SHUTDOWN;
139 buf[1] = static_cast<char>(shutdown_type);
141 cout << "Issuing shutdown command..." << endl;
143 int r = write_all(socknum, buf, bufsize);
150 // r = read(socknum, buf, 1);
152 // cout << "Received acknowledgement. System should now shut down." << endl;
155 cpbuffer<1024> rbuffer;
157 wait_for_reply(rbuffer, socknum);
159 if (rbuffer[0] != DINIT_RP_ACK) {
160 cerr << "shutdown: control socket protocol error" << endl;
164 catch (cp_read_exception &exc)
166 cerr << "shutdown: control socket read failure or protocol error" << endl;
177 // Actually shut down the system.
178 void do_system_shutdown(shutdown_type_t shutdown_type)
182 // Mask all signals to prevent death of our parent etc from terminating us
184 sigfillset(&allsigs);
185 sigprocmask(SIG_SETMASK, &allsigs, nullptr);
188 if (shutdown_type == shutdown_type_t::REBOOT) reboot_type = RB_AUTOBOOT;
189 else if (shutdown_type == shutdown_type_t::POWEROFF) reboot_type = RB_POWER_OFF;
190 else reboot_type = RB_HALT_SYSTEM;
192 // Write to console rather than any terminal, since we lose the terminal it seems:
193 close(STDOUT_FILENO);
194 int consfd = open("/dev/console", O_WRONLY);
195 if (consfd != STDOUT_FILENO) {
196 dup2(consfd, STDOUT_FILENO);
199 cout << "Sending TERM/KILL to all processes..." << endl;
201 // Send TERM/KILL to all (remaining) processes
207 cout << "Turning off swap..." << endl;
209 cout << "Unmounting disks..." << endl;
213 cout << "Issuing shutdown via kernel..." << endl;
217 static void unmount_disks()
219 pid_t chpid = fork();
222 // -a : all filesystems (except proc)
223 // -r : mount readonly if can't unmount
224 execl("/bin/umount", "/bin/umount", "-a", "-r", nullptr);
226 else if (chpid > 0) {
228 waitpid(chpid, &status, 0);
232 static void swap_off()
234 pid_t chpid = fork();
237 execl("/sbin/swapoff", "/sbin/swapoff", "-a", nullptr);
239 else if (chpid > 0) {
241 waitpid(chpid, &status, 0);