Fix log flushing and clean up output in boot failure handling.
[oweals/dinit.git] / src / shutdown.cc
1 #include <cstddef>
2 #include <cstdio>
3 #include <csignal>
4 #include <cstring>
5 #include <string>
6 #include <iostream>
7 #include <exception>
8
9 #include <sys/reboot.h>
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <sys/un.h>
13 #include <sys/wait.h>
14 #include <sys/stat.h>
15 #include <unistd.h>
16 #include <fcntl.h>
17
18 #include "cpbuffer.h"
19 #include "control-cmds.h"
20 #include "service-constants.h"
21 #include "dinit-client.h"
22 #include "dinit-util.h"
23 #include "mconfig.h"
24
25 #include "dasynq.h"
26
27 // shutdown:  shut down the system
28 // This utility communicates with the dinit daemon via a unix socket (specified in SYSCONTROLSOCKET).
29
30 static constexpr uint16_t min_cp_version = 1;
31 static constexpr uint16_t max_cp_version = 1;
32
33 using loop_t = dasynq::event_loop_n;
34 using rearm = dasynq::rearm;
35 using clock_type = dasynq::clock_type;
36 class subproc_buffer;
37
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);
41
42 constexpr static int subproc_bufsize = 4096;
43
44 constexpr static char output_lost_msg[] = "[Some output has not been shown due to buffer overflow]\n";
45
46 // A buffer which maintains a series of overflow markers, used for capturing and echoing
47 // subprocess output.
48 class subproc_buffer : private cpbuffer<subproc_bufsize>
49 {
50     using base = cpbuffer<subproc_bufsize>;
51
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;
57
58     public:
59     enum class fill_status
60     {
61         OK,
62         ENDFILE,
63         FULL
64     };
65
66     subproc_buffer(dasynq::event_loop_n &loop_p, int out_fd) : loop(loop_p)
67     {
68         using loop_t = dasynq::event_loop_n;
69         using rearm = dasynq::rearm;
70
71         out_watch = loop_t::fd_watcher::add_watch(loop, out_fd, dasynq::OUT_EVENTS,
72                 [&](loop_t &eloop, int fd, int flags) -> rearm {
73
74             auto fstatus = flush(STDOUT_FILENO);
75             if (fstatus == subproc_buffer::fill_status::ENDFILE) {
76                 return rearm::DISARM;
77             }
78
79             return rearm::REARM;
80         });
81     }
82
83     ~subproc_buffer()
84     {
85         out_watch->deregister(loop);
86     }
87
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)
91     {
92         int rem = get_free();
93
94         if (rem == 0) {
95             return fill_status::FULL;
96         }
97
98         int read = base::fill(fd, rem);
99         if (read <= 0) {
100             if (read == -1 && errno == EAGAIN) {
101                 return fill_status::OK;
102             }
103             return fill_status::ENDFILE;
104         }
105
106         out_watch->set_enabled(loop, true);
107         return fill_status::OK;
108     }
109
110     // Append a message. If the message will not fit in the buffer, discard it and mark overflow.
111     void append(const char *msg)
112     {
113         out_watch->set_enabled(loop, true);
114         int len = strlen(msg);
115         if (subproc_bufsize - get_length() >= len) {
116             base::append(msg, len);
117         }
118         else {
119             mark_overflow();
120         }
121     }
122
123     // Append the given buffer, which must fit in the remaining space in this buffer.
124     void append(const char *buf, int len)
125     {
126         out_watch->set_enabled(loop, true);
127         base::append(buf, len);
128     }
129
130     int get_free()
131     {
132         return base::get_free();
133     }
134
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)
138     {
139         int to_write = get_contiguous_length(get_ptr(0));
140
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);
146                 if (r == 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));
152
153                     // no more overflow markers?
154                     if (overflow_marker == -1) {
155                         last_overflow = -1;
156                     }
157                     return get_length() == 0 ? fill_status::ENDFILE : fill_status::OK;
158                 }
159                 if (r > 0) {
160                     overflow_msg_ptr += r;
161                 }
162                 return fill_status::OK;
163             }
164
165             to_write = std::min(to_write, overflow_marker);
166         }
167
168         int r = write(fd, get_ptr(0), to_write);
169         if (r > 0) {
170             consume(r);
171             if (overflow_marker != -1) {
172                 overflow_marker -= r;
173                 last_overflow -= r;
174                 if (overflow_marker == 0) {
175                     overflow_msg_ptr = output_lost_msg;
176                 }
177             }
178         }
179
180         return get_length() == 0 ? fill_status::ENDFILE : fill_status::OK;
181     }
182
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.
187     void mark_overflow()
188     {
189         // Try to find the last newline in the buffer
190         int begin = 0;
191         if (last_overflow != -1) {
192             begin = last_overflow + sizeof(int16_t);
193         }
194         int end = get_length() - 1 - sizeof(int16_t); // -1, then -2 for storage of marker
195
196         int i;
197         for (i = end; i >= begin; i--) {
198             if ((*this)[i] == '\n') break;
199         }
200
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));
205             return;
206         }
207
208         if (i < begin) {
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];
218             return;
219         }
220
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];
228         }
229         last_overflow = new_overflow;
230         if (overflow_marker == -1) {
231             overflow_marker = last_overflow;
232         }
233
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));
239     }
240 };
241
242
243 int main(int argc, char **argv)
244 {
245     using namespace std;
246     
247     bool show_help = false;
248     bool sys_shutdown = false;
249     bool use_passed_cfd = false;
250     
251     auto shutdown_type = shutdown_type_t::POWEROFF;
252
253     const char *execname = base_name(argv[0]);
254     if (strcmp(execname, "reboot") == 0) {
255         shutdown_type = shutdown_type_t::REBOOT;
256     }
257         
258     for (int i = 1; i < argc; i++) {
259         if (argv[i][0] == '-') {
260             if (strcmp(argv[i], "--help") == 0) {
261                 show_help = true;
262                 break;
263             }
264             
265             if (strcmp(argv[i], "--system") == 0) {
266                 sys_shutdown = true;
267             }
268             else if (strcmp(argv[i], "-r") == 0) {
269                 shutdown_type = shutdown_type_t::REBOOT;
270             }
271             else if (strcmp(argv[i], "-h") == 0) {
272                 shutdown_type = shutdown_type_t::HALT;
273             }
274             else if (strcmp(argv[i], "-p") == 0) {
275                 shutdown_type = shutdown_type_t::POWEROFF;
276             }
277             else if (strcmp(argv[i], "--use-passed-cfd") == 0) {
278                 use_passed_cfd = true;
279             }
280             else {
281                 cerr << "Unrecognized command-line parameter: " << argv[i] << endl;
282                 return 1;
283             }
284         }
285         else {
286             // time argument? TODO
287             show_help = true;
288         }
289     }
290
291     if (show_help) {
292         cout << execname << " :   shutdown the system\n"
293                 "  --help           : show this help\n"
294                 "  -r               : reboot\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"
301                 "                     by users.\n";
302         return 1;
303     }
304     
305     if (sys_shutdown) {
306         do_system_shutdown(shutdown_type);
307         return 0;
308     }
309
310     signal(SIGPIPE, SIG_IGN);
311     
312     int socknum = 0;
313     
314     if (use_passed_cfd) {
315         char * dinit_cs_fd_env = getenv("DINIT_CS_FD");
316         if (dinit_cs_fd_env != nullptr) {
317             char * endptr;
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);
324             }
325             else {
326                 use_passed_cfd = false;
327             }
328         }
329         else {
330             use_passed_cfd = false;
331         }
332     }
333     
334     if (! use_passed_cfd) {
335         socknum = socket(AF_UNIX, SOCK_STREAM, 0);
336         if (socknum == -1) {
337             perror("socket");
338             return 1;
339         }
340         
341         const char *naddr = SYSCONTROLSOCKET;
342         
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
347         
348         int connr = connect(socknum, (struct sockaddr *) &name, sunlen);
349         if (connr == -1) {
350             perror("connect");
351             return 1;
352         }
353     }
354
355     try {
356         cpbuffer_t rbuffer;
357     
358         check_protocol_version(min_cp_version, max_cp_version, rbuffer, socknum);
359
360         // Build buffer;
361         constexpr int bufsize = 2;
362         char buf[bufsize];
363
364         buf[0] = DINIT_CP_SHUTDOWN;
365         buf[1] = static_cast<char>(shutdown_type);
366
367         cout << "Issuing shutdown command..." << endl;
368
369         write_all_x(socknum, buf, bufsize);
370
371         // Wait for ACK/NACK
372     
373         wait_for_reply(rbuffer, socknum);
374         
375         if (rbuffer[0] != DINIT_RP_ACK) {
376             cerr << "shutdown: control socket protocol error" << endl;
377             return 1;
378         }
379     }
380     catch (cp_old_client_exception &e) {
381         std::cerr << "shutdown: too old (server reports newer protocol version)" << std::endl;
382         return 1;
383     }
384     catch (cp_old_server_exception &e) {
385         std::cerr << "shutdown: server too old or protocol error" << std::endl;
386         return 1;
387     }
388     catch (cp_read_exception &e) {
389         cerr << "shutdown: control socket read failure or protocol error" << endl;
390         return 1;
391     }
392     catch (cp_write_exception &e) {
393         cerr << "shutdown: control socket write error: " << std::strerror(e.errcode) << endl;
394         return 1;
395     }
396     
397     while (true) {
398         pause();
399     }
400     
401     return 0;
402 }
403
404 // Actually shut down the system.
405 void do_system_shutdown(shutdown_type_t shutdown_type)
406 {
407     using namespace std;
408     
409     // Mask all signals to prevent death of our parent etc from terminating us
410     sigset_t allsigs;
411     sigfillset(&allsigs);
412     sigprocmask(SIG_SETMASK, &allsigs, nullptr);
413     
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;
417 #endif
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;
422 #endif
423     
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);
429     }
430     
431     loop_t loop;
432     subproc_buffer sub_buf {loop, STDOUT_FILENO};
433
434     sub_buf.append("Sending TERM/KILL to all processes...\n");
435     
436     // Send TERM/KILL to all (remaining) processes
437     kill(-1, SIGTERM);
438
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 {
446
447         timeout_reached = true;
448         return rearm::REMOVE;
449     });
450
451     do {
452       loop.run();
453     } while (! timeout_reached);
454
455     kill(-1, SIGKILL);
456     
457     // perform shutdown
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);
462     sync();
463     
464     sub_buf.append("Issuing shutdown via kernel...\n");
465     loop.poll();  // give message a chance to get to console
466     reboot(reboot_type);
467 }
468
469 // Watcher for subprocess output.
470 class subproc_out_watch : public loop_t::fd_watcher_impl<subproc_out_watch>
471 {
472     subproc_buffer &sub_buf;
473     bool in_overflow = false;
474
475     rearm read_overflow(int fd)
476     {
477         char buf[128];
478         int r = read(fd, buf, 128);
479         if (r == 0 || (r == -1 && errno != EAGAIN)) {
480             return rearm::NOOP; // leave disarmed
481         }
482         if (r == -1) {
483             return rearm::REARM;
484         }
485
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);
492                 in_overflow = false;
493             }
494         }
495         return rearm::REARM;
496     }
497
498     public:
499     subproc_out_watch(subproc_buffer &sub_buf_p) : sub_buf(sub_buf_p) {}
500
501     rearm fd_event(loop_t &, int fd, int flags)
502     {
503         // if current status is reading overflow, read and discard until newline
504         if (in_overflow) {
505             return read_overflow(fd);
506         }
507
508         auto r = sub_buf.fill(fd);
509         if (r == subproc_buffer::fill_status::FULL) {
510             sub_buf.mark_overflow();
511             in_overflow = true;
512             return read_overflow(fd);
513         }
514         else if (r == subproc_buffer::fill_status::ENDFILE) {
515             return rearm::NOOP;
516         }
517
518         return rearm::REARM;  // re-enable watcher
519     }
520 };
521
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)
525 {
526     class sp_watcher_t : public loop_t::child_proc_watcher_impl<sp_watcher_t>
527     {
528         public:
529         bool terminated = false;
530
531         rearm status_change(loop_t &, pid_t child, int status)
532         {
533             terminated = true;
534             return rearm::REMOVE;
535         }
536     };
537
538     sp_watcher_t sp_watcher;
539
540     // Create output pipe
541     bool have_pipe = true;
542     int pipefds[2];
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");
547         have_pipe = false;
548         // Note, we proceed and let the sub-process run with our stdout/stderr.
549     }
550
551     subproc_out_watch owatch {sub_buf};
552
553     if (have_pipe) {
554         close(pipefds[1]);
555         try {
556             owatch.add_watch(loop, pipefds[0], dasynq::IN_EVENTS);
557         }
558         catch (...) {
559             // failed to create the watcher for the subprocess output; again, let it run with
560             // our stdout/stderr
561             sub_buf.append("Warning: could not create output watch for subprocess\n");
562             close(pipefds[0]);
563             have_pipe = false;
564         }
565     }
566
567     // If we've buffered any messages/output, give them a chance to go out now:
568     loop.poll();
569
570     pid_t ch_pid = sp_watcher.fork(loop);
571     if (ch_pid == 0) {
572         // child
573         // Dup output pipe to stdout, stderr
574         if (have_pipe) {
575             dup2(pipefds[1], STDOUT_FILENO);
576             dup2(pipefds[1], STDERR_FILENO);
577             close(pipefds[0]);
578             close(pipefds[1]);
579         }
580         execv(prog_args[0], const_cast<char **>(prog_args));
581         puts("Failed to execute subprocess: ");
582         perror(prog_args[0]);
583         _exit(1);
584     }
585
586     do {
587         loop.run();
588     } while (! sp_watcher.terminated);
589
590     if (have_pipe) {
591         owatch.deregister(loop);
592     }
593 }
594
595 static void unmount_disks(loop_t &loop, subproc_buffer &sub_buf)
596 {
597     try {
598         const char * unmount_args[] = { "/bin/umount", "-a", "-r", nullptr };
599         run_process(unmount_args, loop, sub_buf);
600     }
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");
605     }
606 }
607
608 static void swap_off(loop_t &loop, subproc_buffer &sub_buf)
609 {
610     try {
611         const char * swapoff_args[] = { "/sbin/swapoff", "-a", nullptr };
612         run_process(swapoff_args, loop, sub_buf);
613     }
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");
618     }
619 }