9 #include <sys/reboot.h>
10 #include <sys/types.h>
11 #include <sys/socket.h>
19 #include "control-cmds.h"
20 #include "service-constants.h"
21 #include "dinit-client.h"
22 #include "dinit-util.h"
27 // shutdown: shut down the system
28 // This utility communicates with the dinit daemon via a unix socket (specified in SYSCONTROLSOCKET).
30 static constexpr uint16_t min_cp_version = 1;
31 static constexpr uint16_t max_cp_version = 1;
33 using loop_t = dasynq::event_loop_n;
34 using rearm = dasynq::rearm;
35 using clock_type = dasynq::clock_type;
38 void do_system_shutdown(shutdown_type_t shutdown_type);
39 static void unmount_disks(loop_t &loop, subproc_buffer &sub_buf);
40 static void swap_off(loop_t &loop, subproc_buffer &sub_buf);
42 constexpr static int subproc_bufsize = 4096;
44 constexpr static char output_lost_msg[] = "[Some output has not been shown due to buffer overflow]\n";
46 // A buffer which maintains a series of overflow markers, used for capturing and echoing
48 class subproc_buffer : private cpbuffer<subproc_bufsize>
50 using base = cpbuffer<subproc_bufsize>;
52 int overflow_marker = -1;
53 int last_overflow = -1; // last marker in the series
54 const char *overflow_msg_ptr = nullptr; // current position in overflow message
55 dasynq::event_loop_n &loop;
56 dasynq::event_loop_n::fd_watcher * out_watch;
59 enum class fill_status
66 subproc_buffer(dasynq::event_loop_n &loop_p, int out_fd) : loop(loop_p)
68 using loop_t = dasynq::event_loop_n;
69 using rearm = dasynq::rearm;
71 out_watch = loop_t::fd_watcher::add_watch(loop, out_fd, dasynq::OUT_EVENTS,
72 [&](loop_t &eloop, int fd, int flags) -> rearm {
74 auto fstatus = flush(STDOUT_FILENO);
75 if (fstatus == subproc_buffer::fill_status::ENDFILE) {
85 out_watch->deregister(loop);
88 // Fill buffer by reading from file descriptor. Note caller must set overflow marker
89 // if the buffer becomes full and more data is available.
90 fill_status fill(int fd)
95 return fill_status::FULL;
98 int read = base::fill(fd, rem);
100 if (read == -1 && errno == EAGAIN) {
101 return fill_status::OK;
103 return fill_status::ENDFILE;
106 out_watch->set_enabled(loop, true);
107 return fill_status::OK;
110 // Append a message. If the message will not fit in the buffer, discard it and mark overflow.
111 void append(const char *msg)
113 out_watch->set_enabled(loop, true);
114 int len = strlen(msg);
115 if (subproc_bufsize - get_length() >= len) {
116 base::append(msg, len);
123 // Append the given buffer, which must fit in the remaining space in this buffer.
124 void append(const char *buf, int len)
126 out_watch->set_enabled(loop, true);
127 base::append(buf, len);
132 return base::get_free();
135 // Write buffer contents out to file descriptor. The descriptor is assumed to be non-blocking.
136 // returns ENDFILE if there is no more content to flush (buffer is now empty) or OK otherwise.
137 fill_status flush(int fd)
139 int to_write = get_contiguous_length(get_ptr(0));
141 if (overflow_marker != -1) {
142 if (overflow_marker == 0) {
143 // output (remainder of) overflow message
144 int l = std::strlen(overflow_msg_ptr);
145 int r = write(fd, overflow_msg_ptr, l);
147 // entire message has been written; next marker is in buffer
148 int16_t overflow_marker16;
149 extract(reinterpret_cast<char *>(&overflow_marker16), 0, sizeof(overflow_marker16));
150 overflow_marker = overflow_marker16;
151 consume(sizeof(overflow_marker16));
153 // no more overflow markers?
154 if (overflow_marker == -1) {
157 return get_length() == 0 ? fill_status::ENDFILE : fill_status::OK;
160 overflow_msg_ptr += r;
162 return fill_status::OK;
165 to_write = std::min(to_write, overflow_marker);
168 int r = write(fd, get_ptr(0), to_write);
171 if (overflow_marker != -1) {
172 overflow_marker -= r;
174 if (overflow_marker == 0) {
175 overflow_msg_ptr = output_lost_msg;
180 return get_length() == 0 ? fill_status::ENDFILE : fill_status::OK;
183 // Mark overflow occurred. Call this only when the buffer is full.
184 // The marker is put after the most recent newline in the buffer, if possible, so that whole
185 // lines are retained in the buffer. In some cases marking overflow will not add a new overflow
186 // marker but simply trim the buffer to an existing marker.
189 // Try to find the last newline in the buffer
191 if (last_overflow != -1) {
192 begin = last_overflow + sizeof(int16_t);
194 int end = get_length() - 1 - sizeof(int16_t); // -1, then -2 for storage of marker
197 for (i = end; i >= begin; i--) {
198 if ((*this)[i] == '\n') break;
201 if (last_overflow != -1 && i < begin) {
202 // No new line after existing marker: trim all beyond that marker, don't
203 // create a new marker:
204 trim_to(last_overflow + sizeof(uint16_t));
209 // No newline in the whole buffer... we'll put the overflow marker at the end,
210 // on the assumption that it is better to output a partial line than it is to
211 // discard the entire buffer:
212 last_overflow = get_length() - sizeof(int16_t);
213 overflow_marker = last_overflow;
214 int16_t overflow16 = -1;
215 char * overflow16_ptr = reinterpret_cast<char *>(&overflow16);
216 *get_ptr(last_overflow + 0) = overflow16_ptr[0];
217 *get_ptr(last_overflow + 1) = overflow16_ptr[1];
221 // We found a newline, put the overflow marker just after it:
222 int new_overflow = i + 1;
223 if (last_overflow != -1) {
224 int16_t new_overflow16 = new_overflow;
225 char * new_overflow16_ptr = reinterpret_cast<char *>(&new_overflow16);
226 *get_ptr(last_overflow + 0) = new_overflow16_ptr[0];
227 *get_ptr(last_overflow + 1) = new_overflow16_ptr[1];
229 last_overflow = new_overflow;
230 if (overflow_marker == -1) {
231 overflow_marker = last_overflow;
234 int16_t overflow16 = -1;
235 char * overflow16_ptr = reinterpret_cast<char *>(&overflow16);
236 *get_ptr(last_overflow + 0) = overflow16_ptr[0];
237 *get_ptr(last_overflow + 0) = overflow16_ptr[1];
238 trim_to(last_overflow + sizeof(int16_t));
243 int main(int argc, char **argv)
247 bool show_help = false;
248 bool sys_shutdown = false;
249 bool use_passed_cfd = false;
251 auto shutdown_type = shutdown_type_t::POWEROFF;
253 const char *execname = base_name(argv[0]);
254 if (strcmp(execname, "reboot") == 0) {
255 shutdown_type = shutdown_type_t::REBOOT;
258 for (int i = 1; i < argc; i++) {
259 if (argv[i][0] == '-') {
260 if (strcmp(argv[i], "--help") == 0) {
265 if (strcmp(argv[i], "--system") == 0) {
268 else if (strcmp(argv[i], "-r") == 0) {
269 shutdown_type = shutdown_type_t::REBOOT;
271 else if (strcmp(argv[i], "-h") == 0) {
272 shutdown_type = shutdown_type_t::HALT;
274 else if (strcmp(argv[i], "-p") == 0) {
275 shutdown_type = shutdown_type_t::POWEROFF;
277 else if (strcmp(argv[i], "--use-passed-cfd") == 0) {
278 use_passed_cfd = true;
281 cerr << "Unrecognized command-line parameter: " << argv[i] << endl;
286 // time argument? TODO
292 cout << execname << " : shutdown the system\n"
293 " --help : show this help\n"
295 " -h : halt system\n"
296 " -p : power down (default)\n"
297 " --use-passed-cfd : use the socket file descriptor identified by the DINIT_CS_FD\n"
298 " environment variable to communicate with the init daemon.\n"
299 " --system : perform shutdown immediately, instead of issuing shutdown\n"
300 " command to the init program. Not recommended for use\n"
306 do_system_shutdown(shutdown_type);
310 signal(SIGPIPE, SIG_IGN);
314 if (use_passed_cfd) {
315 char * dinit_cs_fd_env = getenv("DINIT_CS_FD");
316 if (dinit_cs_fd_env != nullptr) {
318 long int cfdnum = strtol(dinit_cs_fd_env, &endptr, 10);
319 if (endptr != dinit_cs_fd_env) {
320 socknum = (int) cfdnum;
321 // Set non-blocking mode:
322 int sock_flags = fcntl(socknum, F_GETFL, 0);
323 fcntl(socknum, F_SETFL, sock_flags & ~O_NONBLOCK);
326 use_passed_cfd = false;
330 use_passed_cfd = false;
334 if (! use_passed_cfd) {
335 socknum = socket(AF_UNIX, SOCK_STREAM, 0);
341 const char *naddr = SYSCONTROLSOCKET;
343 struct sockaddr_un name;
344 name.sun_family = AF_UNIX;
345 strcpy(name.sun_path, naddr);
346 int sunlen = offsetof(struct sockaddr_un, sun_path) + strlen(naddr) + 1; // family, (string), nul
348 int connr = connect(socknum, (struct sockaddr *) &name, sunlen);
358 check_protocol_version(min_cp_version, max_cp_version, rbuffer, socknum);
361 constexpr int bufsize = 2;
364 buf[0] = DINIT_CP_SHUTDOWN;
365 buf[1] = static_cast<char>(shutdown_type);
367 cout << "Issuing shutdown command..." << endl;
369 write_all_x(socknum, buf, bufsize);
373 wait_for_reply(rbuffer, socknum);
375 if (rbuffer[0] != DINIT_RP_ACK) {
376 cerr << "shutdown: control socket protocol error" << endl;
380 catch (cp_old_client_exception &e) {
381 std::cerr << "shutdown: too old (server reports newer protocol version)" << std::endl;
384 catch (cp_old_server_exception &e) {
385 std::cerr << "shutdown: server too old or protocol error" << std::endl;
388 catch (cp_read_exception &e) {
389 cerr << "shutdown: control socket read failure or protocol error" << endl;
392 catch (cp_write_exception &e) {
393 cerr << "shutdown: control socket write error: " << std::strerror(e.errcode) << endl;
404 // Actually shut down the system.
405 void do_system_shutdown(shutdown_type_t shutdown_type)
409 // Mask all signals to prevent death of our parent etc from terminating us
411 sigfillset(&allsigs);
412 sigprocmask(SIG_SETMASK, &allsigs, nullptr);
414 int reboot_type = RB_AUTOBOOT; // reboot
415 #if defined(RB_POWER_OFF)
416 if (shutdown_type == shutdown_type_t::POWEROFF) reboot_type = RB_POWER_OFF;
418 #if defined(RB_HALT_SYSTEM)
419 if (shutdown_type == shutdown_type_t::HALT) reboot_type = RB_HALT_SYSTEM;
420 #elif defined(RB_HALT)
421 if (shutdown_type == shutdown_type_t::HALT) reboot_type = RB_HALT;
424 // Write to console rather than any terminal, since we lose the terminal it seems:
425 close(STDOUT_FILENO);
426 int consfd = open("/dev/console", O_WRONLY);
427 if (consfd != STDOUT_FILENO) {
428 dup2(consfd, STDOUT_FILENO);
432 subproc_buffer sub_buf {loop, STDOUT_FILENO};
434 sub_buf.append("Sending TERM/KILL to all processes...\n");
436 // Send TERM/KILL to all (remaining) processes
439 // 1 second delay (while outputting from sub_buf):
440 bool timeout_reached = false;
441 dasynq::time_val timeout {1, 0};
442 dasynq::time_val interval {0,0};
443 loop_t::timer::add_timer(loop, clock_type::MONOTONIC, true /* relative */,
444 timeout.get_timespec(), interval.get_timespec(),
445 [&](loop_t &eloop, int expiry_count) -> rearm {
447 timeout_reached = true;
448 return rearm::REMOVE;
453 } while (! timeout_reached);
458 sub_buf.append("Turning off swap...\n");
459 swap_off(loop, sub_buf);
460 sub_buf.append("Unmounting disks...\n");
461 unmount_disks(loop, sub_buf);
464 sub_buf.append("Issuing shutdown via kernel...\n");
465 loop.poll(); // give message a chance to get to console
469 // Watcher for subprocess output.
470 class subproc_out_watch : public loop_t::fd_watcher_impl<subproc_out_watch>
472 subproc_buffer &sub_buf;
473 bool in_overflow = false;
475 rearm read_overflow(int fd)
478 int r = read(fd, buf, 128);
479 if (r == 0 || (r == -1 && errno != EAGAIN)) {
480 return rearm::NOOP; // leave disarmed
486 // How much space is available?
487 int fr = sub_buf.get_free();
488 for (int b = r - std::min(r, fr); b < r; b++) {
489 if (buf[b] == '\n') {
490 // Copy the (partial) line into sub_buf and leave overflow mode
491 sub_buf.append(buf + b, r - b);
499 subproc_out_watch(subproc_buffer &sub_buf_p) : sub_buf(sub_buf_p) {}
501 rearm fd_event(loop_t &, int fd, int flags)
503 // if current status is reading overflow, read and discard until newline
505 return read_overflow(fd);
508 auto r = sub_buf.fill(fd);
509 if (r == subproc_buffer::fill_status::FULL) {
510 sub_buf.mark_overflow();
512 return read_overflow(fd);
514 else if (r == subproc_buffer::fill_status::ENDFILE) {
518 return rearm::REARM; // re-enable watcher
522 // Run process, put its output through the subprocess buffer
523 // may throw: std::system_error, std::bad_alloc
524 static void run_process(const char * prog_args[], loop_t &loop, subproc_buffer &sub_buf)
526 class sp_watcher_t : public loop_t::child_proc_watcher_impl<sp_watcher_t>
529 bool terminated = false;
531 rearm status_change(loop_t &, pid_t child, int status)
534 return rearm::REMOVE;
538 sp_watcher_t sp_watcher;
540 // Create output pipe
541 bool have_pipe = true;
543 if (dasynq::pipe2(pipefds, O_NONBLOCK) == -1) {
544 sub_buf.append("Warning: ");
545 sub_buf.append(prog_args[0]);
546 sub_buf.append(": could not create pipe for subprocess output\n");
548 // Note, we proceed and let the sub-process run with our stdout/stderr.
551 subproc_out_watch owatch {sub_buf};
556 owatch.add_watch(loop, pipefds[0], dasynq::IN_EVENTS);
559 // failed to create the watcher for the subprocess output; again, let it run with
561 sub_buf.append("Warning: could not create output watch for subprocess\n");
567 // If we've buffered any messages/output, give them a chance to go out now:
570 pid_t ch_pid = sp_watcher.fork(loop);
573 // Dup output pipe to stdout, stderr
575 dup2(pipefds[1], STDOUT_FILENO);
576 dup2(pipefds[1], STDERR_FILENO);
580 execv(prog_args[0], const_cast<char **>(prog_args));
581 puts("Failed to execute subprocess: ");
582 perror(prog_args[0]);
588 } while (! sp_watcher.terminated);
591 owatch.deregister(loop);
595 static void unmount_disks(loop_t &loop, subproc_buffer &sub_buf)
598 const char * unmount_args[] = { "/bin/umount", "-a", "-r", nullptr };
599 run_process(unmount_args, loop, sub_buf);
601 catch (std::exception &e) {
602 sub_buf.append("Couldn't fork for umount: ");
603 sub_buf.append(e.what());
604 sub_buf.append("\n");
608 static void swap_off(loop_t &loop, subproc_buffer &sub_buf)
611 const char * swapoff_args[] = { "/sbin/swapoff", "-a", nullptr };
612 run_process(swapoff_args, loop, sub_buf);
614 catch (std::exception &e) {
615 sub_buf.append("Couldn't fork for swapoff: ");
616 sub_buf.append(e.what());
617 sub_buf.append("\n");