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"
21 // shutdown: shut down the system
22 // This utility communicates with the dinit daemon via a unix socket (/dev/initctl).
24 void do_system_shutdown(ShutdownType shutdown_type);
25 static void unmount_disks();
26 static void swap_off();
27 static void wait_for_reply(CPBuffer<1024> &rbuffer, int fd);
34 ReadCPException(int err) : errcode(err) { }
38 int main(int argc, char **argv)
42 bool show_help = false;
43 bool sys_shutdown = false;
44 bool use_passed_cfd = false;
46 auto shutdown_type = ShutdownType::POWEROFF;
48 for (int i = 1; i < argc; i++) {
49 if (argv[i][0] == '-') {
50 if (strcmp(argv[i], "--help") == 0) {
55 if (strcmp(argv[i], "--system") == 0) {
58 else if (strcmp(argv[i], "-r") == 0) {
59 shutdown_type = ShutdownType::REBOOT;
61 else if (strcmp(argv[i], "-h") == 0) {
62 shutdown_type = ShutdownType::HALT;
64 else if (strcmp(argv[i], "-p") == 0) {
65 shutdown_type = ShutdownType::POWEROFF;
67 else if (strcmp(argv[i], "--use-passed-cfd") == 0) {
68 use_passed_cfd = true;
71 cerr << "Unrecognized command-line parameter: " << argv[i] << endl;
76 // time argument? TODO
82 cout << "dinit-shutdown : shutdown the system" << endl;
83 cout << " --help : show this help" << endl;
84 cout << " -r : reboot" << endl;
85 cout << " -h : halt system" << endl;
86 cout << " -p : power down (default)" << endl;
87 cout << " --use-passed-cfd : use the socket file descriptor identified by the DINIT_CS_FD" << endl;
88 cout << " environment variable to communicate with the init daemon." << endl;
89 cout << " --system : perform shutdown immediately, instead of issuing shutdown" << endl;
90 cout << " command to the init program. Not recommended for use" << endl;
91 cout << " by users." << endl;
96 do_system_shutdown(shutdown_type);
100 signal(SIGPIPE, SIG_IGN);
104 if (use_passed_cfd) {
105 char * dinit_cs_fd_env = getenv("DINIT_CS_FD");
106 if (dinit_cs_fd_env != nullptr) {
108 long int cfdnum = strtol(dinit_cs_fd_env, &endptr, 10);
109 if (endptr != dinit_cs_fd_env) {
110 socknum = (int) cfdnum;
113 use_passed_cfd = false;
117 use_passed_cfd = false;
121 if (! use_passed_cfd) {
122 socknum = socket(AF_UNIX, SOCK_STREAM, 0);
128 const char *naddr = "/dev/dinitctl";
130 struct sockaddr_un name;
131 name.sun_family = AF_UNIX;
132 strcpy(name.sun_path, naddr);
133 int sunlen = offsetof(struct sockaddr_un, sun_path) + strlen(naddr) + 1; // family, (string), nul
135 int connr = connect(socknum, (struct sockaddr *) &name, sunlen);
143 //uint16_t sname_len = strlen(service_name);
145 char * buf = new char[bufsize];
147 buf[0] = DINIT_CP_SHUTDOWN;
148 buf[1] = static_cast<char>(shutdown_type);
150 cout << "Issuing shutdown command..." << endl;
152 // TODO make sure to write the whole buffer
153 int r = write(socknum, buf, bufsize);
160 // r = read(socknum, buf, 1);
162 // cout << "Received acknowledgement. System should now shut down." << endl;
165 CPBuffer<1024> rbuffer;
167 wait_for_reply(rbuffer, socknum);
169 if (rbuffer[0] != DINIT_RP_ACK) {
170 cerr << "shutdown: control socket protocol error" << endl;
174 catch (ReadCPException &exc)
176 cerr << "shutdown: control socket read failure or protocol error" << endl;
187 // Fill a circular buffer from a file descriptor, reading at least _rlength_ bytes.
188 // Throws ReadException if the requested number of bytes cannot be read, with:
189 // errcode = 0 if end of stream (remote end closed)
190 // errcode = errno if another error occurred
191 // Note that EINTR is ignored (i.e. the read will be re-tried).
192 static void fillBufferTo(CPBuffer<1024> *buf, int fd, int rlength)
195 int r = buf->fill_to(fd, rlength);
197 if (errno != EINTR) {
198 throw ReadCPException(errno);
202 throw ReadCPException(0);
211 // Wait for a reply packet, skipping over any information packets
212 // that are received in the meantime.
213 static void wait_for_reply(CPBuffer<1024> &rbuffer, int fd)
215 fillBufferTo(&rbuffer, fd, 1);
217 while (rbuffer[0] >= 100) {
218 // Information packet; discard.
219 fillBufferTo(&rbuffer, fd, 1);
220 int pktlen = (unsigned char) rbuffer[1];
222 rbuffer.consume(1); // Consume one byte so we'll read one byte of the next packet
223 fillBufferTo(&rbuffer, fd, pktlen);
224 rbuffer.consume(pktlen - 1);
228 // Actually shut down the system.
229 void do_system_shutdown(ShutdownType shutdown_type)
233 // Mask all signals to prevent death of our parent etc from terminating us
235 sigfillset(&allsigs);
236 sigprocmask(SIG_SETMASK, &allsigs, nullptr);
239 if (shutdown_type == ShutdownType::REBOOT) reboot_type = RB_AUTOBOOT;
240 else if (shutdown_type == ShutdownType::POWEROFF) reboot_type = RB_POWER_OFF;
241 else reboot_type = RB_HALT_SYSTEM;
243 // Write to console rather than any terminal, since we lose the terminal it seems:
244 close(STDOUT_FILENO);
245 int consfd = open("/dev/console", O_WRONLY);
246 if (consfd != STDOUT_FILENO) {
247 dup2(consfd, STDOUT_FILENO);
250 cout << "Sending TERM/KILL to all processes..." << endl; // DAV
252 // Send TERM/KILL to all (remaining) processes
258 cout << "Turning off swap..." << endl;
260 cout << "Unmounting disks..." << endl;
264 cout << "Issuing shutdown via kernel..." << endl;
268 static void unmount_disks()
270 pid_t chpid = fork();
273 // -a : all filesystems (except proc)
274 // -r : mount readonly if can't unmount
275 execl("/bin/umount", "/bin/umount", "-a", "-r", nullptr);
277 else if (chpid > 0) {
279 waitpid(chpid, &status, 0);
283 static void swap_off()
285 pid_t chpid = fork();
288 execl("/sbin/swapoff", "/sbin/swapoff", "-a", nullptr);
290 else if (chpid > 0) {
292 waitpid(chpid, &status, 0);