Implement service unloading.
[oweals/dinit.git] / src / dinitctl.cc
1 #include <cstdio>
2 #include <cstddef>
3 #include <cstring>
4 #include <string>
5 #include <iostream>
6 #include <system_error>
7 #include <memory>
8
9 #include <sys/types.h>
10 #include <sys/socket.h>
11 #include <sys/un.h>
12 #include <unistd.h>
13 #include <signal.h>
14 #include <pwd.h>
15
16 #include "control-cmds.h"
17 #include "service-constants.h"
18 #include "cpbuffer.h"
19
20 // dinitctl:  utility to control the Dinit daemon, including starting and stopping of services.
21
22 // This utility communicates with the dinit daemon via a unix stream socket (/dev/initctl, or $HOME/.dinitctl).
23
24 using handle_t = uint32_t;
25
26
27 class read_cp_exception
28 {
29     public:
30     int errcode;
31     read_cp_exception(int err) : errcode(err) { }
32 };
33
34 enum class command_t;
35
36 static int issue_load_service(int socknum, const char *service_name, bool find_only = false);
37 static int check_load_reply(int socknum, cpbuffer<1024> &rbuffer, handle_t *handle_p, service_state_t *state_p);
38 static int start_stop_service(int socknum, const char *service_name, command_t command, bool do_pin, bool wait_for_service, bool verbose);
39 static int unpin_service(int socknum, const char *service_name, bool verbose);
40 static int unload_service(int socknum, const char *service_name);
41 static int list_services(int socknum);
42 static int shutdown_dinit(int soclknum);
43
44
45 // Fill a circular buffer from a file descriptor, until it contains at least _rlength_ bytes.
46 // Throws read_cp_exception if the requested number of bytes cannot be read, with:
47 //     errcode = 0   if end of stream (remote end closed)
48 //     errcode = errno   if another error occurred
49 // Note that EINTR is ignored (i.e. the read will be re-tried).
50 static void fillBufferTo(cpbuffer<1024> *buf, int fd, int rlength)
51 {
52     do {
53         int r = buf->fill_to(fd, rlength);
54         if (r == -1) {
55             if (errno != EINTR) {
56                 throw read_cp_exception(errno);
57             }
58         }
59         else if (r == 0) {
60             throw read_cp_exception(0);
61         }
62         else {
63             return;
64         }
65     }
66     while (true);
67 }
68
69 static const char * describeState(bool stopped)
70 {
71     return stopped ? "stopped" : "started";
72 }
73
74 static const char * describeVerb(bool stop)
75 {
76     return stop ? "stop" : "start";
77 }
78
79 // Wait for a reply packet, skipping over any information packets that are received in the meantime.
80 static void wait_for_reply(cpbuffer<1024> &rbuffer, int fd)
81 {
82     fillBufferTo(&rbuffer, fd, 1);
83     
84     while (rbuffer[0] >= 100) {
85         // Information packet; discard.
86         fillBufferTo(&rbuffer, fd, 2);
87         int pktlen = (unsigned char) rbuffer[1];
88         
89         rbuffer.consume(1);  // Consume one byte so we'll read one byte of the next packet
90         fillBufferTo(&rbuffer, fd, pktlen);
91         rbuffer.consume(pktlen - 1);
92     }
93 }
94
95 // Wait for an info packet. If any other reply packet comes, throw a read_cp_exception.
96 static void wait_for_info(cpbuffer<1024> &rbuffer, int fd)
97 {
98     fillBufferTo(&rbuffer, fd, 2);
99
100     if (rbuffer[0] < 100) {
101         throw read_cp_exception(0);
102     }
103
104     int pktlen = (unsigned char) rbuffer[1];
105     fillBufferTo(&rbuffer, fd, pktlen);
106 }
107
108 // Write *all* the requested buffer and re-try if necessary until
109 // the buffer is written or an unrecoverable error occurs.
110 static int write_all(int fd, const void *buf, size_t count)
111 {
112     const char *cbuf = static_cast<const char *>(buf);
113     int w = 0;
114     while (count > 0) {
115         int r = write(fd, cbuf, count);
116         if (r == -1) {
117             if (errno == EINTR) continue;
118             return r;
119         }
120         w += r;
121         cbuf += r;
122         count -= r;
123     }
124     return w;
125 }
126
127
128 enum class command_t {
129     NONE,
130     START_SERVICE,
131     WAKE_SERVICE,
132     STOP_SERVICE,
133     RELEASE_SERVICE,
134     UNPIN_SERVICE,
135     UNLOAD_SERVICE,
136     LIST_SERVICES,
137     SHUTDOWN
138 };
139
140 // Entry point.
141 int main(int argc, char **argv)
142 {
143     using namespace std;
144     
145     bool show_help = argc < 2;
146     char *service_name = nullptr;
147     
148     std::string control_socket_str;
149     const char * control_socket_path = nullptr;
150     
151     bool verbose = true;
152     bool sys_dinit = false;  // communicate with system daemon
153     bool wait_for_service = true;
154     bool do_pin = false;
155     
156     command_t command = command_t::NONE;
157         
158     for (int i = 1; i < argc; i++) {
159         if (argv[i][0] == '-') {
160             if (strcmp(argv[i], "--help") == 0) {
161                 show_help = true;
162                 break;
163             }
164             else if (strcmp(argv[i], "--no-wait") == 0) {
165                 wait_for_service = false;
166             }
167             else if (strcmp(argv[i], "--quiet") == 0) {
168                 verbose = false;
169             }
170             else if (strcmp(argv[i], "--system") == 0 || strcmp(argv[i], "-s") == 0) {
171                 sys_dinit = true;
172             }
173             else if (strcmp(argv[i], "--pin") == 0) {
174                 do_pin = true;
175             }
176             else {
177                 return 1;
178             }
179         }
180         else if (command == command_t::NONE) {
181             if (strcmp(argv[i], "start") == 0) {
182                 command = command_t::START_SERVICE; 
183             }
184             else if (strcmp(argv[i], "wake") == 0) {
185                 command = command_t::WAKE_SERVICE;
186             }
187             else if (strcmp(argv[i], "stop") == 0) {
188                 command = command_t::STOP_SERVICE;
189             }
190             else if (strcmp(argv[i], "release") == 0) {
191                 command = command_t::RELEASE_SERVICE;
192             }
193             else if (strcmp(argv[i], "unpin") == 0) {
194                 command = command_t::UNPIN_SERVICE;
195             }
196             else if (strcmp(argv[i], "unload") == 0) {
197                 command = command_t::UNLOAD_SERVICE;
198             }
199             else if (strcmp(argv[i], "list") == 0) {
200                 command = command_t::LIST_SERVICES;
201             }
202             else if (strcmp(argv[i], "shutdown") == 0) {
203                 command = command_t::SHUTDOWN;
204             }
205             else {
206                 show_help = true;
207                 break;
208             }
209         }
210         else {
211             // service name
212             if (service_name != nullptr) {
213                 show_help = true;
214                 break;
215             }
216             service_name = argv[i];
217             // TODO support multiple services
218         }
219     }
220     
221     bool no_service_cmd = (command == command_t::LIST_SERVICES || command == command_t::SHUTDOWN);
222
223     if (service_name != nullptr && no_service_cmd) {
224         show_help = true;
225     }
226     
227     if ((service_name == nullptr && ! no_service_cmd) || command == command_t::NONE) {
228         show_help = true;
229     }
230
231     if (show_help) {
232         cout << "dinitctl:   control Dinit services" << endl;
233         
234         cout << "\nUsage:" << endl;
235         cout << "    dinitctl [options] start [options] <service-name> : start and activate service" << endl;
236         cout << "    dinitctl [options] stop [options] <service-name>  : stop service and cancel explicit activation" << endl;
237         cout << "    dinitctl [options] wake [options] <service-name>  : start but do not mark activated" << endl;
238         cout << "    dinitctl [options] release [options] <service-name> : release activation, stop if no dependents" << endl;
239         cout << "    dinitctl [options] unpin <service-name>           : un-pin the service (after a previous pin)" << endl;
240         cout << "    dinitctl unload <service-name>                    : unload the service" << endl;
241         cout << "    dinitctl list                                     : list loaded services" << endl;
242         cout << "    dinitctl shutdown                                 : stop all services and terminate dinit" << endl;
243         
244         cout << "\nNote: An activated service continues running when its dependents stop." << endl;
245         
246         cout << "\nGeneral options:" << endl;
247         cout << "  -s, --system     : control system daemon instead of user daemon" << endl;
248         cout << "  --quiet          : suppress output (except errors)" << endl;
249         
250         cout << "\nCommand options:" << endl;
251         cout << "  --help           : show this help" << endl;
252         cout << "  --no-wait        : don't wait for service startup/shutdown to complete" << endl;
253         cout << "  --pin            : pin the service in the requested (started/stopped) state" << endl;
254         return 1;
255     }
256     
257     signal(SIGPIPE, SIG_IGN);
258     
259     control_socket_path = "/dev/dinitctl";
260     
261     // Locate control socket
262     if (! sys_dinit) {
263         char * userhome = getenv("HOME");
264         if (userhome == nullptr) {
265             struct passwd * pwuid_p = getpwuid(getuid());
266             if (pwuid_p != nullptr) {
267                 userhome = pwuid_p->pw_dir;
268             }
269         }
270         
271         if (userhome != nullptr) {
272             control_socket_str = userhome;
273             control_socket_str += "/.dinitctl";
274             control_socket_path = control_socket_str.c_str();
275         }
276         else {
277             cerr << "Cannot locate user home directory (set HOME or check /etc/passwd file)" << endl;
278             return 1;
279         }
280     }
281     
282     int socknum = socket(AF_UNIX, SOCK_STREAM, 0);
283     if (socknum == -1) {
284         perror("dinitctl: socket");
285         return 1;
286     }
287
288     struct sockaddr_un * name;
289     uint sockaddr_size = offsetof(struct sockaddr_un, sun_path) + strlen(control_socket_path) + 1;
290     name = (struct sockaddr_un *) malloc(sockaddr_size);
291     if (name == nullptr) {
292         cerr << "dinitctl: Out of memory" << endl;
293         return 1;
294     }
295     
296     name->sun_family = AF_UNIX;
297     strcpy(name->sun_path, control_socket_path);
298     
299     int connr = connect(socknum, (struct sockaddr *) name, sockaddr_size);
300     if (connr == -1) {
301         perror("dinitctl: connect");
302         return 1;
303     }
304     
305     // TODO should start by querying protocol version
306     
307     if (command == command_t::UNPIN_SERVICE) {
308         return unpin_service(socknum, service_name, verbose);
309     }
310     else if (command == command_t::UNLOAD_SERVICE) {
311         return unload_service(socknum, service_name);
312     }
313     else if (command == command_t::LIST_SERVICES) {
314         return list_services(socknum);
315     }
316     else if (command == command_t::SHUTDOWN) {
317         return shutdown_dinit(socknum);
318     }
319     else {
320         return start_stop_service(socknum, service_name, command, do_pin, wait_for_service, verbose);
321     }
322 }
323
324 // Start/stop a service
325 static int start_stop_service(int socknum, const char *service_name, command_t command, bool do_pin, bool wait_for_service, bool verbose)
326 {
327     using namespace std;
328
329     bool do_stop = (command == command_t::STOP_SERVICE || command == command_t::RELEASE_SERVICE);
330     
331     if (issue_load_service(socknum, service_name)) {
332         return 1;
333     }
334
335     // Now we expect a reply:
336     
337     try {
338         cpbuffer<1024> rbuffer;
339         wait_for_reply(rbuffer, socknum);
340         
341         service_state_t state;
342         //service_state_t target_state;
343         handle_t handle;
344         
345         if (check_load_reply(socknum, rbuffer, &handle, &state) != 0) {
346             return 0;
347         }
348                 
349         service_state_t wanted_state = do_stop ? service_state_t::STOPPED : service_state_t::STARTED;
350         int pcommand = 0;
351         switch (command) {
352         case command_t::STOP_SERVICE:
353             pcommand = DINIT_CP_STOPSERVICE;
354             break;
355         case command_t::RELEASE_SERVICE:
356             pcommand = DINIT_CP_RELEASESERVICE;
357             break;
358         case command_t::START_SERVICE:
359             pcommand = DINIT_CP_STARTSERVICE;
360             break;
361         case command_t::WAKE_SERVICE:
362             pcommand = DINIT_CP_WAKESERVICE;
363             break;
364         default: ;
365         }
366         
367         // Need to issue STOPSERVICE/STARTSERVICE
368         // We'll do this regardless of the current service state / target state, since issuing
369         // start/stop also sets or clears the "explicitly started" flag on the service.
370         {
371             int r;
372             
373             {
374                 auto buf = new char[2 + sizeof(handle)];
375                 unique_ptr<char[]> ubuf(buf);
376                 
377                 buf[0] = pcommand;
378                 buf[1] = do_pin ? 1 : 0;
379                 memcpy(buf + 2, &handle, sizeof(handle));
380                 r = write_all(socknum, buf, 2 + sizeof(handle));
381             }
382             
383             if (r == -1) {
384                 perror("dinitctl: write");
385                 return 1;
386             }
387             
388             wait_for_reply(rbuffer, socknum);
389             if (rbuffer[0] == DINIT_RP_ALREADYSS) {
390                 bool already = (state == wanted_state);
391                 if (verbose) {
392                     cout << "Service " << (already ? "(already) " : "") << describeState(do_stop) << "." << endl;
393                 }
394                 return 0; // success!
395             }
396             if (rbuffer[0] != DINIT_RP_ACK) {
397                 cerr << "dinitctl: Protocol error." << endl;
398                 return 1;
399             }
400             rbuffer.consume(1);
401         }
402         
403         if (! wait_for_service) {
404             if (verbose) {
405                 cout << "Issued " << describeVerb(do_stop) << " command successfully." << endl;
406             }
407             return 0;
408         }
409         
410         service_event_t completionEvent;
411         service_event_t cancelledEvent;
412         
413         if (do_stop) {
414             completionEvent = service_event_t::STOPPED;
415             cancelledEvent = service_event_t::STOPCANCELLED;
416         }
417         else {
418             completionEvent = service_event_t::STARTED;
419             cancelledEvent = service_event_t::STARTCANCELLED;
420         }
421         
422         // Wait until service started:
423         int r = rbuffer.fill_to(socknum, 2);
424         while (r > 0) {
425             if (rbuffer[0] >= 100) {
426                 int pktlen = (unsigned char) rbuffer[1];
427                 fillBufferTo(&rbuffer, socknum, pktlen);
428                 
429                 if (rbuffer[0] == DINIT_IP_SERVICEEVENT) {
430                     handle_t ev_handle;
431                     rbuffer.extract((char *) &ev_handle, 2, sizeof(ev_handle));
432                     service_event_t event = static_cast<service_event_t>(rbuffer[2 + sizeof(ev_handle)]);
433                     if (ev_handle == handle) {
434                         if (event == completionEvent) {
435                             if (verbose) {
436                                 cout << "Service " << describeState(do_stop) << "." << endl;
437                             }
438                             return 0;
439                         }
440                         else if (event == cancelledEvent) {
441                             if (verbose) {
442                                 cout << "Service " << describeVerb(do_stop) << " cancelled." << endl;
443                             }
444                             return 1;
445                         }
446                         else if (! do_stop && event == service_event_t::FAILEDSTART) {
447                             if (verbose) {
448                                 cout << "Service failed to start." << endl;
449                             }
450                             return 1;
451                         }
452                     }
453                 }
454                 
455                 rbuffer.consume(pktlen);
456                 r = rbuffer.fill_to(socknum, 2);
457             }
458             else {
459                 // Not an information packet?
460                 cerr << "dinitctl: protocol error" << endl;
461                 return 1;
462             }
463         }
464         
465         if (r == -1) {
466             perror("dinitctl: read");
467         }
468         else {
469             cerr << "protocol error (connection closed by server)" << endl;
470         }
471         return 1;
472     }
473     catch (read_cp_exception &exc) {
474         cerr << "dinitctl: control socket read failure or protocol error" << endl;
475         return 1;
476     }
477     catch (std::bad_alloc &exc) {
478         cerr << "dinitctl: out of memory" << endl;
479         return 1;
480     }
481     
482     return 0;
483 }
484
485 // Issue a "load service" command (DINIT_CP_LOADSERVICE), without waiting for
486 // a response. Returns 1 on failure (with error logged), 0 on success.
487 static int issue_load_service(int socknum, const char *service_name, bool find_only)
488 {
489     // Build buffer;
490     uint16_t sname_len = strlen(service_name);
491     int bufsize = 3 + sname_len;
492     int r;
493     
494     try {
495         std::unique_ptr<char[]> ubuf(new char[bufsize]);
496         auto buf = ubuf.get();
497         
498         buf[0] = find_only ? DINIT_CP_FINDSERVICE : DINIT_CP_LOADSERVICE;
499         memcpy(buf + 1, &sname_len, 2);
500         memcpy(buf + 3, service_name, sname_len);
501         
502         r = write_all(socknum, buf, bufsize);
503     }
504     catch (std::bad_alloc &badalloc) {
505         std::cerr << "dinitctl: " << badalloc.what() << std::endl;
506         return 1;
507     }
508     
509     if (r == -1) {
510         perror("dinitctl: write");
511         return 1;
512     }
513     
514     return 0;
515 }
516
517 // Check that a "load service" reply was received, and that the requested service was found.
518 static int check_load_reply(int socknum, cpbuffer<1024> &rbuffer, handle_t *handle_p, service_state_t *state_p)
519 {
520     using namespace std;
521     
522     if (rbuffer[0] == DINIT_RP_SERVICERECORD) {
523         fillBufferTo(&rbuffer, socknum, 2 + sizeof(*handle_p));
524         rbuffer.extract((char *) handle_p, 2, sizeof(*handle_p));
525         if (state_p) *state_p = static_cast<service_state_t>(rbuffer[1]);
526         //target_state = static_cast<service_state_t>(rbuffer[2 + sizeof(handle)]);
527         rbuffer.consume(3 + sizeof(*handle_p));
528         return 0;
529     }
530     else if (rbuffer[0] == DINIT_RP_NOSERVICE) {
531         cerr << "dinitctl: Failed to find/load service." << endl;
532         return 1;
533     }
534     else {
535         cerr << "dinitctl: Protocol error." << endl;
536         return 1;
537     }
538 }
539
540 static int unpin_service(int socknum, const char *service_name, bool verbose)
541 {
542     using namespace std;
543     
544     // Build buffer;
545     if (issue_load_service(socknum, service_name) == 1) {
546         return 1;
547     }
548
549     // Now we expect a reply:
550     
551     try {
552         cpbuffer<1024> rbuffer;
553         wait_for_reply(rbuffer, socknum);
554         
555         handle_t handle;
556         
557         if (check_load_reply(socknum, rbuffer, &handle, nullptr) != 0) {
558             return 1;
559         }
560         
561         // Issue UNPIN command.
562         {
563             int r;
564             
565             {
566                 char *buf = new char[1 + sizeof(handle)];
567                 unique_ptr<char[]> ubuf(buf);
568                 buf[0] = DINIT_CP_UNPINSERVICE;
569                 memcpy(buf + 1, &handle, sizeof(handle));
570                 r = write_all(socknum, buf, 2 + sizeof(handle));
571             }
572             
573             if (r == -1) {
574                 perror("dinitctl: write");
575                 return 1;
576             }
577             
578             wait_for_reply(rbuffer, socknum);
579             if (rbuffer[0] != DINIT_RP_ACK) {
580                 cerr << "dinitctl: Protocol error." << endl;
581                 return 1;
582             }
583             rbuffer.consume(1);
584         }
585     }
586     catch (read_cp_exception &exc) {
587         cerr << "dinitctl: Control socket read failure or protocol error" << endl;
588         return 1;
589     }
590     catch (std::bad_alloc &exc) {
591         cerr << "dinitctl: Out of memory" << endl;
592         return 1;
593     }
594     
595     if (verbose) {
596         cout << "Service unpinned." << endl;
597     }
598     return 0;
599 }
600
601 static int unload_service(int socknum, const char *service_name)
602 {
603     using namespace std;
604
605     // Build buffer;
606     if (issue_load_service(socknum, service_name, true) == 1) {
607         return 1;
608     }
609
610     // Now we expect a reply:
611
612     try {
613         cpbuffer<1024> rbuffer;
614         wait_for_reply(rbuffer, socknum);
615
616         handle_t handle;
617
618         if (check_load_reply(socknum, rbuffer, &handle, nullptr) != 0) {
619             return 1;
620         }
621
622         // Issue UNLOAD command.
623         {
624             int r;
625
626             {
627                 char *buf = new char[1 + sizeof(handle)];
628                 unique_ptr<char[]> ubuf(buf);
629                 buf[0] = DINIT_CP_UNLOADSERVICE;
630                 memcpy(buf + 1, &handle, sizeof(handle));
631                 r = write_all(socknum, buf, 2 + sizeof(handle));
632             }
633
634             if (r == -1) {
635                 perror("dinitctl: write");
636                 return 1;
637             }
638
639             wait_for_reply(rbuffer, socknum);
640             if (rbuffer[0] == DINIT_RP_NAK) {
641                 cerr << "dinitctl: Could not unload service; service not stopped, or is a dependency of "
642                         "other service." << endl;
643                 return 1;
644             }
645             if (rbuffer[0] != DINIT_RP_ACK) {
646                 cerr << "dinitctl: Protocol error." << endl;
647                 return 1;
648             }
649             rbuffer.consume(1);
650         }
651     }
652     catch (read_cp_exception &exc) {
653         cerr << "dinitctl: Control socket read failure or protocol error" << endl;
654         return 1;
655     }
656     catch (std::bad_alloc &exc) {
657         cerr << "dinitctl: Out of memory" << endl;
658         return 1;
659     }
660
661     cout << "Service unloaded." << endl;
662     return 0;
663 }
664
665 static int list_services(int socknum)
666 {
667     using namespace std;
668     
669     try {
670         char cmdbuf[] = { (char)DINIT_CP_LISTSERVICES };
671         int r = write_all(socknum, cmdbuf, 1);
672         
673         if (r == -1) {
674             perror("dinitctl: write");
675             return 1;
676         }
677         
678         cpbuffer<1024> rbuffer;
679         wait_for_reply(rbuffer, socknum);
680         while (rbuffer[0] == DINIT_RP_SVCINFO) {
681             fillBufferTo(&rbuffer, socknum, 8);
682             int nameLen = rbuffer[1];
683             service_state_t current = static_cast<service_state_t>(rbuffer[2]);
684             service_state_t target = static_cast<service_state_t>(rbuffer[3]);
685             
686             fillBufferTo(&rbuffer, socknum, nameLen + 8);
687             
688             char *name_ptr = rbuffer.get_ptr(8);
689             int clength = std::min(rbuffer.get_contiguous_length(name_ptr), nameLen);
690             
691             string name = string(name_ptr, clength);
692             name.append(rbuffer.get_buf_base(), nameLen - clength);
693             
694             cout << "[";
695             
696             cout << (target  == service_state_t::STARTED ? "{" : " ");
697             cout << (current == service_state_t::STARTED ? "+" : " ");
698             cout << (target  == service_state_t::STARTED ? "}" : " ");
699             
700             if (current == service_state_t::STARTING) {
701                 cout << "<<";
702             }
703             else if (current == service_state_t::STOPPING) {
704                 cout << ">>";
705             }
706             else {
707                 cout << "  ";
708             }
709             
710             cout << (target  == service_state_t::STOPPED ? "{" : " ");
711             cout << (current == service_state_t::STOPPED ? "-" : " ");
712             cout << (target  == service_state_t::STOPPED ? "}" : " ");
713             
714             cout << "] " << name << endl;
715             
716             rbuffer.consume(8 + nameLen);
717             wait_for_reply(rbuffer, socknum);
718         }
719         
720         if (rbuffer[0] != DINIT_RP_LISTDONE) {
721             cerr << "dinitctl: Control socket protocol error" << endl;
722             return 1;
723         }
724     }
725     catch (read_cp_exception &exc) {
726         cerr << "dinitctl: Control socket read failure or protocol error" << endl;
727         return 1;
728     }
729     catch (std::bad_alloc &exc) {
730         cerr << "dinitctl: Out of memory" << endl;
731         return 1;
732     }
733     
734     return 0;
735 }
736
737 static int shutdown_dinit(int socknum)
738 {
739     // TODO support no-wait option.
740     using namespace std;
741
742     // Build buffer;
743     constexpr int bufsize = 2;
744     char buf[bufsize];
745
746     buf[0] = DINIT_CP_SHUTDOWN;
747     buf[1] = static_cast<char>(shutdown_type_t::HALT);
748
749     // TODO make sure to write the whole buffer
750     int r = write(socknum, buf, bufsize);
751     if (r == -1) {
752         perror("write");
753         return 1;
754     }
755
756     // Wait for ACK/NACK
757     // r = read(socknum, buf, 1);
758     //if (r > 0) {
759     //    cout << "Received acknowledgement. System should now shut down." << endl;
760     //}
761
762     cpbuffer<1024> rbuffer;
763     try {
764         wait_for_reply(rbuffer, socknum);
765
766         if (rbuffer[0] != DINIT_RP_ACK) {
767             cerr << "dinitctl: Control socket protocol error" << endl;
768             return 1;
769         }
770     }
771     catch (read_cp_exception &exc) {
772         cerr << "dinitctl: Control socket read failure or protocol error" << endl;
773         return 1;
774     }
775
776     // Now wait for rollback complete:
777     try {
778         while (true) {
779             wait_for_info(rbuffer, socknum);
780             if (rbuffer[0] == DINIT_ROLLBACK_COMPLETED) {
781                 break;
782             }
783         }
784     }
785     catch (read_cp_exception &exc) {
786         // Dinit can terminate before replying: let's assume that happened.
787         // TODO: better check, possibly ensure that dinit actually sends rollback complete before
788         // termination.
789     }
790
791     return 0;
792 }