6 #include <system_error>
9 #include <sys/socket.h>
14 #include "control-cmds.h"
15 #include "service-constants.h"
18 // dinitctl: utility to control the Dinit daemon, including starting and stopping of services.
20 // This utility communicates with the dinit daemon via a unix socket (/dev/initctl).
22 using handle_t = uint32_t;
29 ReadCPException(int err) : errcode(err) { }
32 static void fillBufferTo(CPBuffer *buf, int fd, int rlength)
34 int r = buf->fillTo(fd, rlength);
36 throw ReadCPException(errno);
39 throw ReadCPException(0);
43 static const char * describeState(bool stopped)
45 return stopped ? "stopped" : "started";
48 static const char * describeVerb(bool stop)
50 return stop ? "stop" : "start";
53 int main(int argc, char **argv)
58 bool show_help = argc < 2;
59 char *service_name = nullptr;
61 std::string control_socket_str;
62 const char * control_socket_path = nullptr;
65 bool sys_dinit = false; // communicate with system daemon
66 bool wait_for_service = true;
70 constexpr int START_SERVICE = 1;
71 constexpr int STOP_SERVICE = 2;
73 for (int i = 1; i < argc; i++) {
74 if (argv[i][0] == '-') {
75 if (strcmp(argv[i], "--help") == 0) {
79 else if (strcmp(argv[i], "--no-wait") == 0) {
80 wait_for_service = false;
82 else if (strcmp(argv[i], "--quiet") == 0) {
85 else if (strcmp(argv[i], "--system") == 0 || strcmp(argv[i], "-s") == 0) {
89 cerr << "Unrecognized command-line parameter: " << argv[i] << endl;
93 else if (command == 0) {
94 if (strcmp(argv[i], "start")) {
95 command = START_SERVICE;
97 else if (strcmp(argv[i], "stop")) {
98 command = STOP_SERVICE;
107 service_name = argv[i];
108 // TODO support multiple services (or at least give error if multiple
109 // services supplied)
113 if (service_name == nullptr || command = 0) {
118 cout << "dinit-start: start a dinit service" << endl;
119 cout << " --help : show this help" << endl;
120 cout << " --no-wait : don't wait for service startup/shutdown to complete" << endl;
121 cout << " --quiet : suppress output (except errors)" << endl;
122 cout << " -s, --system : control system daemon instead of user daemon" << endl;
123 cout << " <service-name> : start the named service" << endl;
127 do_stop = (command == STOP_SERVICE);
129 control_socket_path = "/dev/dinitctl";
132 char * userhome = getenv("HOME");
133 if (userhome == nullptr) {
134 struct passwd * pwuid_p = getpwuid(getuid());
135 if (pwuid_p != nullptr) {
136 userhome = pwuid_p->pw_dir;
140 if (userhome != nullptr) {
141 control_socket_str = userhome;
142 control_socket_str += "/.dinitctl";
143 control_socket_path = control_socket_str.c_str();
146 cerr << "Cannot locate user home directory (set HOME or check /etc/passwd file)" << endl;
151 int socknum = socket(AF_UNIX, SOCK_STREAM, 0);
157 struct sockaddr_un * name;
158 uint sockaddr_size = offsetof(struct sockaddr_un, sun_path) + strlen(control_socket_path) + 1;
159 name = (struct sockaddr_un *) malloc(sockaddr_size);
160 if (name == nullptr) {
161 cerr << "dinit-start: out of memory" << endl;
165 name->sun_family = AF_UNIX;
166 strcpy(name->sun_path, control_socket_path);
168 int connr = connect(socknum, (struct sockaddr *) name, sockaddr_size);
174 // TODO should start by querying protocol version
177 uint16_t sname_len = strlen(service_name);
178 int bufsize = 3 + sname_len;
179 char * buf = new char[bufsize];
181 buf[0] = DINIT_CP_LOADSERVICE;
182 memcpy(buf + 1, &sname_len, 2);
183 memcpy(buf + 3, service_name, sname_len);
185 int r = write(socknum, buf, bufsize);
186 // TODO make sure we write it all
193 // Now we expect a reply:
194 // NOTE: should skip over information packets.
198 fillBufferTo(&rbuffer, socknum, 1);
201 ServiceState target_state;
204 if (rbuffer[0] == DINIT_RP_SERVICERECORD) {
205 fillBufferTo(&rbuffer, socknum, 2 + sizeof(handle));
206 rbuffer.extract((char *) &handle, 2, sizeof(handle));
207 state = static_cast<ServiceState>(rbuffer[1]);
208 target_state = static_cast<ServiceState>(rbuffer[2 + sizeof(handle)]);
209 rbuffer.consume(3 + sizeof(handle));
211 else if (rbuffer[0] == DINIT_RP_NOSERVICE) {
212 cerr << "Failed to find/load service." << endl;
216 cerr << "Protocol error." << endl;
220 ServiceState wanted_state = do_stop ? ServiceState::STOPPED : ServiceState::STARTED;
221 int command = do_stop ? DINIT_CP_STOPSERVICE : DINIT_CP_STARTSERVICE;
223 // Need to issue STOPSERVICE/STARTSERVICE
224 if (target_state != wanted_state) {
225 buf = new char[2 + sizeof(handle)];
227 buf[1] = 0; // don't pin
228 memcpy(buf + 2, &handle, sizeof(handle));
229 r = write(socknum, buf, 2 + sizeof(handle));
233 if (state == wanted_state) {
235 cout << "Service already " << describeState(do_stop) << "." << endl;
237 return 0; // success!
240 if (! wait_for_service) {
244 ServiceEvent completionEvent;
245 ServiceEvent cancelledEvent;
248 completionEvent = ServiceEvent::STOPPED;
249 cancelledEvent = ServiceEvent::STOPCANCELLED;
252 completionEvent = ServiceEvent::STARTED;
253 cancelledEvent = ServiceEvent::STARTCANCELLED;
256 // Wait until service started:
257 r = rbuffer.fillTo(socknum, 2);
259 if (rbuffer[0] >= 100) {
260 int pktlen = (unsigned char) rbuffer[1];
261 fillBufferTo(&rbuffer, socknum, pktlen);
263 if (rbuffer[0] == DINIT_IP_SERVICEEVENT) {
265 rbuffer.extract((char *) &ev_handle, 2, sizeof(ev_handle));
266 ServiceEvent event = static_cast<ServiceEvent>(rbuffer[2 + sizeof(ev_handle)]);
267 if (ev_handle == handle) {
268 if (event == completionEvent) {
270 cout << "Service " << describeState(do_stop) << "." << endl;
274 else if (event == cancelledEvent) {
276 cout << "Service " << describeVerb(do_stop) << " cancelled." << endl;
284 // Not an information packet?
285 cerr << "protocol error" << endl;
294 cerr << "protocol error (connection closed by server)" << endl;
298 catch (ReadCPException &exc) {
299 cerr << "control socket read failure or protocol error" << endl;
302 catch (std::bad_alloc &exc) {
303 cerr << "out of memory" << endl;