Improve some dinitctl error messages
[oweals/dinit.git] / src / dinitctl.cc
1 #include <cstdio>
2 #include <cstddef>
3 #include <cstring>
4 #include <string>
5 #include <iostream>
6 #include <fstream>
7 #include <system_error>
8 #include <memory>
9 #include <algorithm>
10
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <sys/wait.h>
14 #include <sys/socket.h>
15 #include <sys/un.h>
16 #include <unistd.h>
17 #include <signal.h>
18 #include <pwd.h>
19
20 #include "control-cmds.h"
21 #include "service-constants.h"
22 #include "cpbuffer.h"
23 #include "dinit-client.h"
24 #include "load-service.h"
25 #include "dinit-util.h"
26 #include "mconfig.h"
27
28 // dinitctl:  utility to control the Dinit daemon, including starting and stopping of services.
29
30 // This utility communicates with the dinit daemon via a unix stream socket (as specified in
31 // SYSCONTROLSOCKET, or $HOME/.dinitctl).
32
33 static constexpr uint16_t min_cp_version = 1;
34 static constexpr uint16_t max_cp_version = 1;
35
36 enum class command_t;
37
38 static int issue_load_service(int socknum, const char *service_name, bool find_only = false);
39 static int check_load_reply(int socknum, cpbuffer_t &, handle_t *handle_p, service_state_t *state_p);
40 static int start_stop_service(int socknum, cpbuffer_t &, const char *service_name, command_t command,
41         bool do_pin, bool do_force, bool wait_for_service, bool verbose);
42 static int unpin_service(int socknum, cpbuffer_t &, const char *service_name, bool verbose);
43 static int unload_service(int socknum, cpbuffer_t &, const char *service_name);
44 static int reload_service(int socknum, cpbuffer_t &, const char *service_name);
45 static int list_services(int socknum, cpbuffer_t &);
46 static int shutdown_dinit(int soclknum, cpbuffer_t &);
47 static int add_remove_dependency(int socknum, cpbuffer_t &rbuffer, bool add, const char *service_from,
48         const char *service_to, dependency_type dep_type);
49 static int enable_disable_service(int socknum, cpbuffer_t &rbuffer, const char *from, const char *to,
50         bool enable);
51
52 static const char * describeState(bool stopped)
53 {
54     return stopped ? "stopped" : "started";
55 }
56
57 static const char * describeVerb(bool stop)
58 {
59     return stop ? "stop" : "start";
60 }
61
62 enum class command_t {
63     NONE,
64     START_SERVICE,
65     WAKE_SERVICE,
66     STOP_SERVICE,
67     RESTART_SERVICE,
68     RELEASE_SERVICE,
69     UNPIN_SERVICE,
70     UNLOAD_SERVICE,
71     RELOAD_SERVICE,
72     LIST_SERVICES,
73     SHUTDOWN,
74     ADD_DEPENDENCY,
75     RM_DEPENDENCY,
76     ENABLE_SERVICE,
77     DISABLE_SERVICE
78 };
79
80
81 // Entry point.
82 int main(int argc, char **argv)
83 {
84     using namespace std;
85     
86     bool show_help = argc < 2;
87     const char *service_name = nullptr;
88     const char *to_service_name = nullptr;
89     dependency_type dep_type;
90     bool dep_type_set = false;
91     
92     std::string control_socket_str;
93     const char * control_socket_path = nullptr;
94     
95     bool verbose = true;
96     bool user_dinit = (getuid() != 0);  // communicate with user daemon
97     bool wait_for_service = true;
98     bool do_pin = false;
99     bool do_force = false;
100     
101     command_t command = command_t::NONE;
102         
103     for (int i = 1; i < argc; i++) {
104         if (argv[i][0] == '-') {
105             if (strcmp(argv[i], "--help") == 0) {
106                 show_help = true;
107                 break;
108             }
109             else if (strcmp(argv[i], "--no-wait") == 0) {
110                 wait_for_service = false;
111             }
112             else if (strcmp(argv[i], "--quiet") == 0) {
113                 verbose = false;
114             }
115             else if (strcmp(argv[i], "--system") == 0 || strcmp(argv[i], "-s") == 0) {
116                 user_dinit = false;
117             }
118             else if (strcmp(argv[i], "--user") == 0 || strcmp(argv[i], "-u") == 0) {
119                 user_dinit = true;
120             }
121             else if (strcmp(argv[i], "--pin") == 0) {
122                 do_pin = true;
123             }
124             else if (strcmp(argv[i], "--socket-path") == 0 || strcmp(argv[i], "-p") == 0) {
125                 ++i;
126                 if (i == argc) {
127                     cerr << "dinitctl: --socket-path/-p should be followed by socket path" << std::endl;
128                     return 1;
129                 }
130                 control_socket_str = argv[i];
131             }
132             else if ((command == command_t::ENABLE_SERVICE || command == command_t::DISABLE_SERVICE)
133                     && strcmp(argv[i], "--from") == 0) {
134                 ++i;
135                 if (i == argc) {
136                     cerr << "dinitctl: --from should be followed by a service name" << std::endl;
137                     return 1;
138                 }
139                 service_name = argv[i];
140             }
141             else if ((command == command_t::STOP_SERVICE || command == command_t::RESTART_SERVICE)
142                     && (strcmp(argv[i], "--force") == 0 || strcmp(argv[i], "-f") == 0)) {
143                 do_force = true;
144             }
145             else {
146                 cerr << "dinitctl: unrecognized/invalid option: " << argv[i] << " (use --help for help)\n";
147                 return 1;
148             }
149         }
150         else if (command == command_t::NONE) {
151             if (strcmp(argv[i], "start") == 0) {
152                 command = command_t::START_SERVICE; 
153             }
154             else if (strcmp(argv[i], "wake") == 0) {
155                 command = command_t::WAKE_SERVICE;
156             }
157             else if (strcmp(argv[i], "stop") == 0) {
158                 command = command_t::STOP_SERVICE;
159             }
160             else if (strcmp(argv[i], "restart") == 0) {
161                 command = command_t::RESTART_SERVICE;
162             }
163             else if (strcmp(argv[i], "release") == 0) {
164                 command = command_t::RELEASE_SERVICE;
165             }
166             else if (strcmp(argv[i], "unpin") == 0) {
167                 command = command_t::UNPIN_SERVICE;
168             }
169             else if (strcmp(argv[i], "unload") == 0) {
170                 command = command_t::UNLOAD_SERVICE;
171             }
172             else if (strcmp(argv[i], "reload") == 0) {
173                 command = command_t::RELOAD_SERVICE;
174             }
175             else if (strcmp(argv[i], "list") == 0) {
176                 command = command_t::LIST_SERVICES;
177             }
178             else if (strcmp(argv[i], "shutdown") == 0) {
179                 command = command_t::SHUTDOWN;
180             }
181             else if (strcmp(argv[i], "add-dep") == 0) {
182                 command = command_t::ADD_DEPENDENCY;
183             }
184             else if (strcmp(argv[i], "rm-dep") == 0) {
185                 command = command_t::RM_DEPENDENCY;
186             }
187             else if (strcmp(argv[i], "enable") == 0) {
188                 command = command_t::ENABLE_SERVICE;
189             }
190             else if (strcmp(argv[i], "disable") == 0) {
191                 command = command_t::DISABLE_SERVICE;
192             }
193             else {
194                 cerr << "dinitctl: unrecognized command: " << argv[i] << " (use --help for help)\n";
195                 return 1;
196             }
197         }
198         else {
199             // service name / other non-option
200             if (command == command_t::ADD_DEPENDENCY || command == command_t::RM_DEPENDENCY) {
201                 if (! dep_type_set) {
202                     if (strcmp(argv[i], "regular") == 0) {
203                         dep_type = dependency_type::REGULAR;
204                     }
205                     else if (strcmp(argv[i], "milestone") == 0) {
206                         dep_type = dependency_type::MILESTONE;
207                     }
208                     else if (strcmp(argv[i], "waits-for") == 0) {
209                         dep_type = dependency_type::WAITS_FOR;
210                     }
211                     else {
212                         show_help = true;
213                         break;
214                     }
215                     dep_type_set = true;
216                 }
217                 else if (service_name == nullptr) {
218                     service_name = argv[i];
219                 }
220                 else if (to_service_name == nullptr) {
221                     to_service_name = argv[i];
222                 }
223                 else {
224                     show_help = true;
225                     break;
226                 }
227             }
228             else if (command == command_t::ENABLE_SERVICE || command == command_t::DISABLE_SERVICE) {
229                 if (to_service_name != nullptr) {
230                     show_help = true;
231                     break;
232                 }
233                 to_service_name = argv[i];
234             }
235             else {
236                 if (service_name != nullptr) {
237                     show_help = true;
238                     break;
239                 }
240                 service_name = argv[i];
241                 // TODO support multiple services
242             }
243         }
244     }
245     
246     bool no_service_cmd = (command == command_t::LIST_SERVICES || command == command_t::SHUTDOWN);
247
248     if (command == command_t::ENABLE_SERVICE || command == command_t::DISABLE_SERVICE) {
249         show_help |= (to_service_name == nullptr);
250     }
251     else if ((service_name == nullptr && ! no_service_cmd) || command == command_t::NONE) {
252         show_help = true;
253     }
254
255     if (service_name != nullptr && no_service_cmd) {
256         show_help = true;
257     }
258
259     if ((command == command_t::ADD_DEPENDENCY || command == command_t::RM_DEPENDENCY)
260             && (! dep_type_set || service_name == nullptr || to_service_name == nullptr)) {
261         show_help = true;
262     }
263
264     if (show_help) {
265         cout << "dinitctl:   control Dinit services\n"
266           "\n"
267           "Usage:\n"
268           "    dinitctl [options] start [options] <service-name>\n"
269           "    dinitctl [options] stop [options] <service-name>\n"
270           "    dinitctl [options] restart [options] <service-name>\n"
271           "    dinitctl [options] wake [options] <service-name>\n"
272           "    dinitctl [options] release [options] <service-name>\n"
273           "    dinitctl [options] unpin <service-name>\n"
274           "    dinitctl [options] unload <service-name>\n"
275           "    dinitctl [options] reload <service-name>\n"
276           "    dinitctl [options] list\n"
277           "    dinitctl [options] shutdown\n"
278           "    dinitctl [options] add-dep <type> <from-service> <to-service>\n"
279           "    dinitctl [options] rm-dep <type> <from-service> <to-service>\n"
280           "    dinitctl [options] enable [--from <from-service>] <to-service>\n"
281           "    dinitctl [options] disable [--from <from-service>] <to-service>\n"
282           "\n"
283           "Note: An activated service continues running when its dependents stop.\n"
284           "\n"
285           "General options:\n"
286           "  --help           : show this help\n"
287           "  -s, --system     : control system daemon (default if run as root)\n"
288           "  -u, --user       : control user daemon\n"
289           "  --quiet          : suppress output (except errors)\n"
290           "  --socket-path <path>, -p <path>\n"
291           "                   : specify socket for communication with daemon\n"
292           "\n"
293           "Command options:\n"
294           "  --no-wait        : don't wait for service startup/shutdown to complete\n"
295           "  --pin            : pin the service in the requested state\n"
296           "  --force          : force stop even if dependents will be affected\n";
297         return 1;
298     }
299     
300     signal(SIGPIPE, SIG_IGN);
301     
302     // Locate control socket
303     if (! control_socket_str.empty()) {
304         control_socket_path = control_socket_str.c_str();
305     }
306     else {
307         control_socket_path = SYSCONTROLSOCKET; // default to system
308         if (user_dinit) {
309             char * userhome = getenv("HOME");
310             if (userhome == nullptr) {
311                 struct passwd * pwuid_p = getpwuid(getuid());
312                 if (pwuid_p != nullptr) {
313                     userhome = pwuid_p->pw_dir;
314                 }
315             }
316
317             if (userhome != nullptr) {
318                 control_socket_str = userhome;
319                 control_socket_str += "/.dinitctl";
320                 control_socket_path = control_socket_str.c_str();
321             }
322             else {
323                 cerr << "dinitctl: Cannot locate user home directory (set HOME, check /etc/passwd file, or "
324                         "specify socket path via -p)" << endl;
325                 return 1;
326             }
327         }
328     }
329     
330     int socknum = socket(AF_UNIX, SOCK_STREAM, 0);
331     if (socknum == -1) {
332         perror("dinitctl: error opening socket");
333         return 1;
334     }
335
336     struct sockaddr_un * name;
337     uint sockaddr_size = offsetof(struct sockaddr_un, sun_path) + strlen(control_socket_path) + 1;
338     name = (struct sockaddr_un *) malloc(sockaddr_size);
339     if (name == nullptr) {
340         cerr << "dinitctl: Out of memory" << endl;
341         return 1;
342     }
343     
344     name->sun_family = AF_UNIX;
345     strcpy(name->sun_path, control_socket_path);
346     
347     int connr = connect(socknum, (struct sockaddr *) name, sockaddr_size);
348     if (connr == -1) {
349         perror((std::string("dinitctl: connecting to socket ") + control_socket_path).c_str());
350         return 1;
351     }
352     
353     try {
354         // Start by querying protocol version:
355         cpbuffer_t rbuffer;
356         check_protocol_version(min_cp_version, max_cp_version, rbuffer, socknum);
357
358         if (command == command_t::UNPIN_SERVICE) {
359             return unpin_service(socknum, rbuffer, service_name, verbose);
360         }
361         else if (command == command_t::UNLOAD_SERVICE) {
362             return unload_service(socknum, rbuffer, service_name);
363         }
364         else if (command == command_t::RELOAD_SERVICE) {
365             return reload_service(socknum, rbuffer, service_name);
366         }
367         else if (command == command_t::LIST_SERVICES) {
368             return list_services(socknum, rbuffer);
369         }
370         else if (command == command_t::SHUTDOWN) {
371             return shutdown_dinit(socknum, rbuffer);
372         }
373         else if (command == command_t::ADD_DEPENDENCY || command == command_t::RM_DEPENDENCY) {
374             return add_remove_dependency(socknum, rbuffer, command == command_t::ADD_DEPENDENCY,
375                     service_name, to_service_name, dep_type);
376         }
377         else if (command == command_t::ENABLE_SERVICE || command == command_t::DISABLE_SERVICE) {
378             // If only one service specified, assume that we enable for 'boot' service:
379             if (service_name == nullptr) {
380                 service_name = "boot";
381             }
382             return enable_disable_service(socknum, rbuffer, service_name, to_service_name,
383                     command == command_t::ENABLE_SERVICE);
384         }
385         else {
386             return start_stop_service(socknum, rbuffer, service_name, command, do_pin, do_force,
387                     wait_for_service, verbose);
388         }
389     }
390     catch (cp_old_client_exception &e) {
391         std::cerr << "dinitctl: too old (server reports newer protocol version)" << std::endl;
392         return 1;
393     }
394     catch (cp_old_server_exception &e) {
395         std::cerr << "dinitctl: server too old or protocol error" << std::endl;
396         return 1;
397     }
398     catch (cp_read_exception &e) {
399         cerr << "dinitctl: control socket read failure or protocol error" << endl;
400         return 1;
401     }
402     catch (cp_write_exception &e) {
403         cerr << "dinitctl: control socket write error: " << std::strerror(e.errcode) << endl;
404         return 1;
405     }
406 }
407
408 // Extract/read a string of specified length from the buffer/socket. The string is consumed
409 // from the buffer.
410 static std::string read_string(int socknum, cpbuffer_t &rbuffer, uint32_t length)
411 {
412     int rb_len = rbuffer.get_length();
413     if (uint32_t(rb_len) >= length) {
414         std::string r = rbuffer.extract_string(0, length);
415         rbuffer.consume(length);
416         return r;
417     }
418
419     std::string r = rbuffer.extract_string(0, rb_len);
420     uint32_t rlen = length - rb_len;
421     uint32_t clen;
422     do {
423         rbuffer.reset();
424         rbuffer.fill(socknum);
425         char *bptr = rbuffer.get_ptr(0);
426         clen = rbuffer.get_length();
427         clen = std::min(clen, rlen);
428         r.append(bptr, clen);
429         rlen -= clen;
430     } while (rlen > 0);
431
432     rbuffer.consume(clen);
433
434     return r;
435 }
436
437 // Load a service: issue load command, wait for reply. Return true on success, display error message
438 // and return false on failure.
439 //      socknum  - the socket fd to communicate via
440 //      rbuffer  - the buffer for communication
441 //      name     - the name of the service to load
442 //      handle   - where to store the handle of the loaded service
443 //      state    - where to store the state of the loaded service (may be null).
444 static bool load_service(int socknum, cpbuffer_t &rbuffer, const char *name, handle_t *handle,
445         service_state_t *state)
446 {
447     // Load 'to' service:
448     if (issue_load_service(socknum, name)) {
449         return false;
450     }
451
452     wait_for_reply(rbuffer, socknum);
453
454     if (check_load_reply(socknum, rbuffer, handle, state) != 0) {
455         return false;
456     }
457
458     return true;
459 }
460
461 // Get the service name for a given handle, by querying the daemon.
462 static std::string get_service_name(int socknum, cpbuffer_t &rbuffer, handle_t handle)
463 {
464     auto m = membuf()
465             .append((char) DINIT_CP_QUERYSERVICENAME)
466             .append((char) 0)
467             .append(handle);
468     write_all_x(socknum, m);
469
470     wait_for_reply(rbuffer, socknum);
471
472     if (rbuffer[0] != DINIT_RP_SERVICENAME) {
473         throw cp_read_exception{0};
474     }
475
476     // 1 byte reserved
477     // uint16_t size
478     fill_buffer_to(rbuffer, socknum, 2 + sizeof(uint16_t));
479     uint16_t namesize;
480     rbuffer.extract(&namesize, 2, sizeof(uint16_t));
481     rbuffer.consume(2 + sizeof(uint16_t));
482
483     std::string name;
484
485     do {
486         if (rbuffer.get_length() == 0) {
487             rbuffer.fill(socknum);
488         }
489
490         size_t to_extract = std::min(size_t(rbuffer.get_length()), namesize - name.length());
491         size_t contiguous_len = rbuffer.get_contiguous_length(rbuffer.get_ptr(0));
492         if (contiguous_len <= to_extract) {
493             name.append(rbuffer.get_ptr(0), contiguous_len);
494             rbuffer.consume(contiguous_len);
495             name.append(rbuffer.get_ptr(0), to_extract - contiguous_len);
496             rbuffer.consume(to_extract - contiguous_len);
497         }
498         else {
499             name.append(rbuffer.get_ptr(0), to_extract);
500             rbuffer.consume(to_extract);
501             break;
502         }
503
504     } while (name.length() < namesize);
505
506     return name;
507 }
508
509 // Start/stop a service
510 static int start_stop_service(int socknum, cpbuffer_t &rbuffer, const char *service_name,
511         command_t command, bool do_pin, bool do_force, bool wait_for_service, bool verbose)
512 {
513     using namespace std;
514
515     bool do_stop = (command == command_t::STOP_SERVICE || command == command_t::RELEASE_SERVICE);
516
517     service_state_t state;
518     handle_t handle;
519     
520     if (! load_service(socknum, rbuffer, service_name, &handle, &state)) {
521         return 1;
522     }
523
524     service_state_t wanted_state = do_stop ? service_state_t::STOPPED : service_state_t::STARTED;
525     int pcommand = 0;
526     switch (command) {
527         case command_t::STOP_SERVICE:
528         case command_t::RESTART_SERVICE:  // stop, and then start
529             pcommand = DINIT_CP_STOPSERVICE;
530             break;
531         case command_t::RELEASE_SERVICE:
532             pcommand = DINIT_CP_RELEASESERVICE;
533             break;
534         case command_t::START_SERVICE:
535             pcommand = DINIT_CP_STARTSERVICE;
536             break;
537         case command_t::WAKE_SERVICE:
538             pcommand = DINIT_CP_WAKESERVICE;
539             break;
540         default: ;
541     }
542
543     // Need to issue STOPSERVICE/STARTSERVICE
544     // We'll do this regardless of the current service state / target state, since issuing
545     // start/stop also sets or clears the "explicitly started" flag on the service.
546     {
547         char flags = (do_pin ? 1 : 0) | ((pcommand == DINIT_CP_STOPSERVICE && !do_force) ? 2 : 0);
548         if (command == command_t::RESTART_SERVICE) {
549             flags |= 4;
550         }
551
552         auto m = membuf()
553                 .append((char) pcommand)
554                 .append(flags)
555                 .append(handle);
556         write_all_x(socknum, m);
557
558         wait_for_reply(rbuffer, socknum);
559         auto reply_pkt_h = rbuffer[0];
560         rbuffer.consume(1); // consume header
561         if (reply_pkt_h == DINIT_RP_ALREADYSS) {
562             bool already = (state == wanted_state);
563             if (verbose) {
564                 cout << "Service " << (already ? "(already) " : "")
565                         << describeState(do_stop) << "." << endl;
566             }
567             return 0; // success!
568         }
569         if (reply_pkt_h == DINIT_RP_DEPENDENTS && pcommand == DINIT_CP_STOPSERVICE) {
570             cerr << "dinitctl: cannot stop service due to the following dependents:\n";
571             if (command != command_t::RESTART_SERVICE) {
572                 cerr << "(Only direct dependents are listed. Exercise caution before using '--force' !!)\n";
573             }
574             // size_t number, N * handle_t handles
575             size_t number;
576             rbuffer.fill_to(socknum, sizeof(number));
577             rbuffer.extract(&number, 0, sizeof(number));
578             rbuffer.consume(sizeof(number));
579             std::vector<handle_t> handles;
580             handles.reserve(number);
581             for (size_t i = 0; i < number; i++) {
582                 handle_t handle;
583                 rbuffer.fill_to(socknum, sizeof(handle_t));
584                 rbuffer.extract(&handle, 0, sizeof(handle));
585                 handles.push_back(handle);
586                 rbuffer.consume(sizeof(handle));
587             }
588             // Print the directly affected dependents:
589             cerr << " ";
590             for (handle_t handle : handles) {
591                 cerr << " " << get_service_name(socknum, rbuffer, handle);
592             }
593             cerr << "\n";
594             return 1;
595         }
596         if (reply_pkt_h == DINIT_RP_NAK && command == command_t::RESTART_SERVICE) {
597             cerr << "dinitctl: cannot restart service; service not started.\n";
598             return 1;
599         }
600         if (reply_pkt_h == DINIT_RP_NAK && command == command_t::START_SERVICE) {
601             cerr << "dinitctl: cannot start service (during shut down).\n";
602             return 1;
603         }
604         if (reply_pkt_h == DINIT_RP_NAK && command == command_t::WAKE_SERVICE) {
605             cerr << "dinitctl: service has no active dependents (or system is shutting down), cannot wake.\n";
606             return 1;
607         }
608         if (reply_pkt_h != DINIT_RP_ACK && reply_pkt_h != DINIT_RP_ALREADYSS) {
609             cerr << "dinitctl: protocol error." << endl;
610             return 1;
611         }
612     }
613
614     if (! wait_for_service) {
615         if (verbose) {
616             cout << "Issued " << describeVerb(do_stop) << " command successfully." << endl;
617         }
618         return 0;
619     }
620
621     service_event_t completionEvent;
622     service_event_t cancelledEvent;
623
624     if (do_stop) {
625         completionEvent = service_event_t::STOPPED;
626         cancelledEvent = service_event_t::STOPCANCELLED;
627     }
628     else {
629         completionEvent = service_event_t::STARTED;
630         cancelledEvent = service_event_t::STARTCANCELLED;
631     }
632
633     // Wait until service started:
634     int r = rbuffer.fill_to(socknum, 2);
635     while (r > 0) {
636         if (rbuffer[0] >= 100) {
637             int pktlen = (unsigned char) rbuffer[1];
638             fill_buffer_to(rbuffer, socknum, pktlen);
639
640             if (rbuffer[0] == DINIT_IP_SERVICEEVENT) {
641                 handle_t ev_handle;
642                 rbuffer.extract((char *) &ev_handle, 2, sizeof(ev_handle));
643                 service_event_t event = static_cast<service_event_t>(rbuffer[2 + sizeof(ev_handle)]);
644                 if (ev_handle == handle) {
645                     if (event == completionEvent) {
646                         if (verbose) {
647                             cout << "Service " << describeState(do_stop) << "." << endl;
648                         }
649                         return 0;
650                     }
651                     else if (event == cancelledEvent) {
652                         if (verbose) {
653                             cout << "Service " << describeVerb(do_stop) << " cancelled." << endl;
654                         }
655                         return 1;
656                     }
657                     else if (! do_stop && event == service_event_t::FAILEDSTART) {
658                         if (verbose) {
659                             cout << "Service failed to start." << endl;
660                         }
661                         return 1;
662                     }
663                 }
664             }
665
666             rbuffer.consume(pktlen);
667             r = rbuffer.fill_to(socknum, 2);
668         }
669         else {
670             // Not an information packet?
671             cerr << "dinitctl: protocol error" << endl;
672             return 1;
673         }
674     }
675
676     if (r == -1) {
677         perror("dinitctl: read");
678     }
679     else {
680         cerr << "protocol error (connection closed by server)" << endl;
681     }
682     return 1;
683 }
684
685 // Issue a "load service" command (DINIT_CP_LOADSERVICE), without waiting for
686 // a response. Returns 1 on failure (with error logged), 0 on success.
687 static int issue_load_service(int socknum, const char *service_name, bool find_only)
688 {
689     // Build buffer;
690     uint16_t sname_len = strlen(service_name);
691     int bufsize = 3 + sname_len;
692     
693     std::unique_ptr<char[]> ubuf(new char[bufsize]);
694     auto buf = ubuf.get();
695
696     buf[0] = find_only ? DINIT_CP_FINDSERVICE : DINIT_CP_LOADSERVICE;
697     memcpy(buf + 1, &sname_len, 2);
698     memcpy(buf + 3, service_name, sname_len);
699
700     write_all_x(socknum, buf, bufsize);
701     
702     return 0;
703 }
704
705 // Check that a "load service" reply was received, and that the requested service was found.
706 //   state_p may be null.
707 static int check_load_reply(int socknum, cpbuffer_t &rbuffer, handle_t *handle_p, service_state_t *state_p)
708 {
709     using namespace std;
710     
711     if (rbuffer[0] == DINIT_RP_SERVICERECORD) {
712         fill_buffer_to(rbuffer, socknum, 2 + sizeof(*handle_p));
713         rbuffer.extract((char *) handle_p, 2, sizeof(*handle_p));
714         if (state_p) *state_p = static_cast<service_state_t>(rbuffer[1]);
715         //target_state = static_cast<service_state_t>(rbuffer[2 + sizeof(handle)]);
716         rbuffer.consume(3 + sizeof(*handle_p));
717         return 0;
718     }
719     else if (rbuffer[0] == DINIT_RP_NOSERVICE) {
720         cerr << "dinitctl: failed to find/load service." << endl;
721         return 1;
722     }
723     else {
724         cerr << "dinitctl: protocol error." << endl;
725         return 1;
726     }
727 }
728
729 static int unpin_service(int socknum, cpbuffer_t &rbuffer, const char *service_name, bool verbose)
730 {
731     using namespace std;
732
733     handle_t handle;
734     
735     // Build buffer;
736     if (! load_service(socknum, rbuffer, service_name, &handle, nullptr)) {
737         return 1;
738     }
739     
740     // Issue UNPIN command.
741     {
742         auto m = membuf()
743                 .append<char>(DINIT_CP_UNPINSERVICE)
744                 .append(handle);
745         write_all_x(socknum, m);
746         
747         wait_for_reply(rbuffer, socknum);
748         if (rbuffer[0] != DINIT_RP_ACK) {
749             cerr << "dinitctl: protocol error." << endl;
750             return 1;
751         }
752         rbuffer.consume(1);
753     }
754
755     if (verbose) {
756         cout << "Service unpinned." << endl;
757     }
758     return 0;
759 }
760
761 static int unload_service(int socknum, cpbuffer_t &rbuffer, const char *service_name)
762 {
763     using namespace std;
764
765     if (issue_load_service(socknum, service_name, true) == 1) {
766         return 1;
767     }
768
769     wait_for_reply(rbuffer, socknum);
770
771     handle_t handle;
772
773     if (rbuffer[0] == DINIT_RP_NOSERVICE) {
774         cerr << "dinitctl: service not loaded." << endl;
775         return 1;
776     }
777
778     if (check_load_reply(socknum, rbuffer, &handle, nullptr) != 0) {
779         return 1;
780     }
781
782     // Issue UNLOAD command.
783     {
784         auto m = membuf()
785                 .append<char>(DINIT_CP_UNLOADSERVICE)
786                 .append(handle);
787         write_all_x(socknum, m);
788
789         wait_for_reply(rbuffer, socknum);
790         if (rbuffer[0] == DINIT_RP_NAK) {
791             cerr << "dinitctl: Could not unload service; service not stopped, or is a dependency of "
792                     "other service." << endl;
793             return 1;
794         }
795         if (rbuffer[0] != DINIT_RP_ACK) {
796             cerr << "dinitctl: Protocol error." << endl;
797             return 1;
798         }
799         rbuffer.consume(1);
800     }
801
802     cout << "Service unloaded." << endl;
803     return 0;
804 }
805
806 static int reload_service(int socknum, cpbuffer_t &rbuffer, const char *service_name)
807 {
808     using namespace std;
809
810     if (issue_load_service(socknum, service_name, true) == 1) {
811         return 1;
812     }
813
814     wait_for_reply(rbuffer, socknum);
815
816     handle_t handle;
817
818     if (rbuffer[0] == DINIT_RP_NOSERVICE) {
819         cerr << "dinitctl: service not loaded." << endl;
820         return 1;
821     }
822
823     if (check_load_reply(socknum, rbuffer, &handle, nullptr) != 0) {
824         return 1;
825     }
826
827     // Issue RELOAD command.
828     {
829         auto m = membuf()
830                 .append<char>(DINIT_CP_RELOADSERVICE)
831                 .append(handle);
832         write_all_x(socknum, m);
833
834         wait_for_reply(rbuffer, socknum);
835         if (rbuffer[0] == DINIT_RP_NAK) {
836             cerr << "dinitctl: Could not reload service; service in wrong state, incompatible change, "
837                     "or bad service description." << endl;
838             return 1;
839         }
840         if (rbuffer[0] != DINIT_RP_ACK) {
841             cerr << "dinitctl: Protocol error." << endl;
842             return 1;
843         }
844         rbuffer.consume(1);
845     }
846
847     cout << "Service reloaded." << endl;
848     return 0;
849 }
850
851 static int list_services(int socknum, cpbuffer_t &rbuffer)
852 {
853     using namespace std;
854     
855     char cmdbuf[] = { (char)DINIT_CP_LISTSERVICES };
856     write_all_x(socknum, cmdbuf, 1);
857
858     wait_for_reply(rbuffer, socknum);
859     while (rbuffer[0] == DINIT_RP_SVCINFO) {
860         int hdrsize = 8 + std::max(sizeof(int), sizeof(pid_t));
861         fill_buffer_to(rbuffer, socknum, hdrsize);
862         int nameLen = rbuffer[1];
863         service_state_t current = static_cast<service_state_t>(rbuffer[2]);
864         service_state_t target = static_cast<service_state_t>(rbuffer[3]);
865
866         int console_flags = rbuffer[4];
867         bool has_console = (console_flags & 2) != 0;
868         bool waiting_console = (console_flags & 1) != 0;
869         bool was_skipped = (console_flags & 4) != 0;
870
871         stopped_reason_t stop_reason = static_cast<stopped_reason_t>(rbuffer[5]);
872
873         pid_t service_pid;
874         int exit_status;
875         if (current != service_state_t::STOPPED) {
876             rbuffer.extract((char *)&service_pid, 8, sizeof(service_pid));
877         }
878         else {
879                 rbuffer.extract((char *)&exit_status, 8, sizeof(exit_status));
880         }
881
882         fill_buffer_to(rbuffer, socknum, nameLen + hdrsize);
883
884         char *name_ptr = rbuffer.get_ptr(hdrsize);
885         int clength = std::min(rbuffer.get_contiguous_length(name_ptr), nameLen);
886
887         string name = string(name_ptr, clength);
888         name.append(rbuffer.get_buf_base(), nameLen - clength);
889
890         cout << "[";
891
892         cout << (target  == service_state_t::STARTED ? "{" : " ");
893         if (current == service_state_t::STARTED) {
894             cout << (was_skipped ? "s" : "+");
895         }
896         else {
897             cout << " ";
898         }
899         cout << (target  == service_state_t::STARTED ? "}" : " ");
900         
901         if (current == service_state_t::STARTING) {
902             cout << "<<";
903         }
904         else if (current == service_state_t::STOPPING) {
905             cout << ">>";
906         }
907         else {
908             cout << "  ";
909         }
910         
911         cout << (target  == service_state_t::STOPPED ? "{" : " ");
912         if (current == service_state_t::STOPPED) {
913             bool did_fail = false;
914             if (stop_reason == stopped_reason_t::TERMINATED) {
915                 if (!WIFEXITED(exit_status) || WEXITSTATUS(exit_status) != 0) {
916                     did_fail = true;
917                 }
918             }
919             else did_fail = (stop_reason != stopped_reason_t::NORMAL);
920
921             cout << (did_fail ? "X" : "-");
922         }
923         else {
924                 cout << " ";
925         }
926         cout << (target == service_state_t::STOPPED ? "}" : " ");
927
928         cout << "] " << name;
929
930         if (current != service_state_t::STOPPED && service_pid != -1) {
931                 cout << " (pid: " << service_pid << ")";
932         }
933         
934         if (current == service_state_t::STOPPED && stop_reason == stopped_reason_t::TERMINATED) {
935             if (WIFEXITED(exit_status)) {
936                 cout << " (exit status: " << WEXITSTATUS(exit_status) << ")";
937             }
938             else if (WIFSIGNALED(exit_status)) {
939                 cout << " (signal: " << WTERMSIG(exit_status) << ")";
940             }
941         }
942
943         if (has_console) {
944                 cout << " (has console)";
945         }
946         else if (waiting_console) {
947                 cout << " (waiting for console)";
948         }
949
950         cout << endl;
951
952         rbuffer.consume(hdrsize + nameLen);
953         wait_for_reply(rbuffer, socknum);
954     }
955
956     if (rbuffer[0] != DINIT_RP_LISTDONE) {
957         cerr << "dinitctl: Control socket protocol error" << endl;
958         return 1;
959     }
960
961     return 0;
962 }
963
964 static int add_remove_dependency(int socknum, cpbuffer_t &rbuffer, bool add,
965         const char *service_from, const char *service_to, dependency_type dep_type)
966 {
967     using namespace std;
968
969     handle_t from_handle;
970     handle_t to_handle;
971
972     if (! load_service(socknum, rbuffer, service_from, &from_handle, nullptr)
973             || ! load_service(socknum, rbuffer, service_to, &to_handle, nullptr)) {
974         return 1;
975     }
976
977     auto m = membuf()
978             .append<char>(add ? (char)DINIT_CP_ADD_DEP : (char)DINIT_CP_REM_DEP)
979             .append(dep_type)
980             .append(from_handle)
981             .append(to_handle);
982     write_all_x(socknum, m);
983
984     wait_for_reply(rbuffer, socknum);
985
986     // check reply
987     if (rbuffer[0] == DINIT_RP_NAK) {
988         cerr << "dinitctl: Could not add dependency: circular dependency or wrong state" << endl;
989         return 1;
990     }
991     if (rbuffer[0] != DINIT_RP_ACK) {
992         cerr << "dinitctl: Control socket protocol error" << endl;
993         return 1;
994     }
995
996     return 0;
997 }
998
999 static int shutdown_dinit(int socknum, cpbuffer_t &rbuffer)
1000 {
1001     // TODO support no-wait option.
1002     using namespace std;
1003
1004     auto m = membuf()
1005             .append<char>(DINIT_CP_SHUTDOWN)
1006             .append(static_cast<char>(shutdown_type_t::HALT));
1007     write_all_x(socknum, m);
1008
1009     wait_for_reply(rbuffer, socknum);
1010
1011     if (rbuffer[0] != DINIT_RP_ACK) {
1012         cerr << "dinitctl: Control socket protocol error" << endl;
1013         return 1;
1014     }
1015
1016     // Now wait for rollback complete, by waiting for the connection to close:
1017     try {
1018         while (true) {
1019             wait_for_info(rbuffer, socknum);
1020             rbuffer.consume(rbuffer[1]);
1021         }
1022     }
1023     catch (cp_read_exception &exc) {
1024         // Assume that the connection closed.
1025     }
1026
1027     return 0;
1028 }
1029
1030 // exception for cancelling a service operation
1031 class service_op_cancel { };
1032
1033 static int enable_disable_service(int socknum, cpbuffer_t &rbuffer, const char *from, const char *to,
1034         bool enable)
1035 {
1036     using namespace std;
1037
1038     service_state_t from_state = service_state_t::STARTED;
1039     handle_t from_handle;
1040
1041     handle_t to_handle;
1042
1043     if (! load_service(socknum, rbuffer, from, &from_handle, &from_state)
1044             || ! load_service(socknum, rbuffer, to, &to_handle, nullptr)) {
1045         return 1;
1046     }
1047
1048     // Get service load path
1049     char buf[1] = { DINIT_CP_QUERY_LOAD_MECH };
1050     write_all_x(socknum, buf, 1);
1051
1052     wait_for_reply(rbuffer, socknum);
1053
1054     if (rbuffer[0] != DINIT_RP_LOADER_MECH) {
1055         cerr << "dinitctl: Control socket protocol error" << endl;
1056         return 1;
1057     }
1058
1059     // Packet type, load mechanism type, packet size:
1060     fill_buffer_to(rbuffer, socknum, 2 + sizeof(uint32_t));
1061
1062     if (rbuffer[1] != SSET_TYPE_DIRLOAD) {
1063         cerr << "dinitctl: unknown configuration, unable to load service descriptions" << endl;
1064         return 1;
1065     }
1066
1067     vector<string> paths;
1068
1069     uint32_t pktsize;
1070     rbuffer.extract(&pktsize, 2, sizeof(uint32_t));
1071
1072     fill_buffer_to(rbuffer, socknum, 2 + sizeof(uint32_t) * 3); // path entries, cwd length
1073
1074     uint32_t path_entries;  // number of service directories
1075     rbuffer.extract(&path_entries, 2 + sizeof(uint32_t), sizeof(uint32_t));
1076
1077     uint32_t cwd_len;
1078     rbuffer.extract(&cwd_len, 2 + sizeof(uint32_t) * 2, sizeof(uint32_t));
1079     rbuffer.consume(2 + sizeof(uint32_t) * 3);
1080     pktsize -= 2 + sizeof(uint32_t) * 3;
1081
1082     // Read current working directory of daemon:
1083     std::string dinit_cwd = read_string(socknum, rbuffer, cwd_len);
1084
1085     // dinit daemon base directory against which service paths are resolved is in dinit_cwd
1086
1087     for (int i = 0; i < (int)path_entries; i++) {
1088         uint32_t plen;
1089         fill_buffer_to(rbuffer, socknum, sizeof(uint32_t));
1090         rbuffer.extract(&plen, 0, sizeof(uint32_t));
1091         rbuffer.consume(sizeof(uint32_t));
1092         paths.push_back(read_string(socknum, rbuffer, plen));
1093     }
1094
1095     // all service directories are now in the 'paths' vector
1096     // Load/read service description for 'from' service:
1097
1098     ifstream service_file;
1099     string service_file_path;
1100
1101     for (std::string path : paths) {
1102         string test_path = combine_paths(combine_paths(dinit_cwd, path.c_str()), from);
1103
1104         service_file.open(test_path.c_str(), ios::in);
1105         if (service_file) {
1106             service_file_path = test_path;
1107             break;
1108         }
1109     }
1110
1111     if (! service_file) {
1112         cerr << "dinitctl: could not locate service file for service '" << from << "'" << endl;
1113         return 1;
1114     }
1115
1116     // We now need to read the service file, identify the waits-for.d directory (bail out if more than one),
1117     // make sure the service is not listed as a dependency individually.
1118
1119     string waits_for_d;
1120
1121     try {
1122         process_service_file(from, service_file, [&](string &line, string &setting,
1123                 dinit_load::string_iterator i, dinit_load::string_iterator end) -> void {
1124             if (setting == "waits-for" || setting == "depends-on" || setting == "depends-ms") {
1125                 string dname = dinit_load::read_setting_value(i, end);
1126                 if (dname == to) {
1127                     // There is already a dependency
1128                     cerr << "dinitctl: there is a fixed dependency to service '" << to
1129                             << "' in the service description of '" << from << "'." << endl;
1130                     throw service_op_cancel();
1131                 }
1132             }
1133             else if (setting == "waits-for.d") {
1134                 string dname = dinit_load::read_setting_value(i, end);
1135                 if (! waits_for_d.empty()) {
1136                     cerr << "dinitctl: service '" << from << "' has multiple waits-for.d directories "
1137                             << "specified in service description" << endl;
1138                     throw service_op_cancel();
1139                 }
1140                 waits_for_d = std::move(dname);
1141             }
1142         });
1143     }
1144     catch (const service_op_cancel &cexc) {
1145         return 1;
1146     }
1147
1148     // If the from service has no waits-for.d specified, we can't continue
1149     if (waits_for_d.empty()) {
1150         cerr << "dinitctl: service '" << from << "' has no waits-for.d directory specified" << endl;
1151         return 1;
1152     }
1153
1154     // The waits-for.d path is relative to the service file path, combine:
1155     string waits_for_d_full = combine_paths(parent_path(service_file_path), waits_for_d.c_str());
1156
1157     // check if dependency already exists
1158     string dep_link_path = combine_paths(waits_for_d_full, to);
1159     struct stat stat_buf;
1160     if (lstat(dep_link_path.c_str(), &stat_buf) == -1) {
1161         if (errno != ENOENT) {
1162             cerr << "dinitctl: checking for existing dependency link: " << dep_link_path << ": "
1163                     << strerror(errno) << endl;
1164             return 1;
1165         }
1166     }
1167     else {
1168         // dependency already exists
1169         if (enable) {
1170             cerr << "dinitctl: service already enabled." << endl;
1171             return 1;
1172         }
1173     }
1174
1175     // warn if 'from' service is not started
1176     if (enable && from_state != service_state_t::STARTED) {
1177         cerr << "dinitctl: warning: enabling dependency for non-started service" << endl;
1178     }
1179
1180     // add/remove dependency
1181     constexpr int enable_pktsize = 2 + sizeof(handle_t) * 2;
1182     char cmdbuf[enable_pktsize] = { char(enable ? DINIT_CP_ENABLESERVICE : DINIT_CP_REM_DEP),
1183             char(dependency_type::WAITS_FOR)};
1184     memcpy(cmdbuf + 2, &from_handle, sizeof(from_handle));
1185     memcpy(cmdbuf + 2 + sizeof(from_handle), &to_handle, sizeof(to_handle));
1186     write_all_x(socknum, cmdbuf, enable_pktsize);
1187
1188     wait_for_reply(rbuffer, socknum);
1189
1190     // check reply
1191     if (enable && rbuffer[0] == DINIT_RP_NAK) {
1192         cerr << "dinitctl: Could not enable service: possible circular dependency" << endl;
1193         return 1;
1194     }
1195     if (rbuffer[0] != DINIT_RP_ACK) {
1196         cerr << "dinitctl: Control socket protocol error" << endl;
1197         return 1;
1198     }
1199
1200     // create link
1201     if (enable) {
1202         if (symlink((string("../") + to).c_str(), dep_link_path.c_str()) == -1) {
1203             cerr << "dinitctl: Could not create symlink at " << dep_link_path << ": " << strerror(errno)
1204                     << "\n" "dinitctl: Note: service was activated, but will not be enabled on restart."
1205                     << endl;
1206             return 1;
1207         }
1208     }
1209     else {
1210         if (unlink(dep_link_path.c_str()) == -1) {
1211             cerr << "dinitctl: Could not unlink dependency entry " << dep_link_path << ": "
1212                     << strerror(errno) << "\n"
1213                     "dinitctl: Note: service was disabled, but will be re-enabled on restart." << endl;
1214             return 1;
1215         }
1216     }
1217
1218     return 0;
1219 }