6 #include <system_error>
9 #include <sys/socket.h>
14 #include "control-cmds.h"
15 #include "service-constants.h"
18 // dinit-start: utility to start a dinit service
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);
44 int main(int argc, char **argv)
48 bool show_help = argc < 2;
49 char *service_name = nullptr;
51 std::string control_socket_str;
52 const char * control_socket_path = nullptr;
55 bool sys_dinit = false; // communicate with system daemon
56 bool wait_for_service = true;
58 for (int i = 1; i < argc; i++) {
59 if (argv[i][0] == '-') {
60 if (strcmp(argv[i], "--help") == 0) {
64 else if (strcmp(argv[i], "--no-wait") == 0) {
65 wait_for_service = false;
67 else if (strcmp(argv[i], "--quiet") == 0) {
70 else if (strcmp(argv[i], "--system") == 0 || strcmp(argv[i], "-s") == 0) {
74 cerr << "Unrecognized command-line parameter: " << argv[i] << endl;
80 service_name = argv[i];
81 // TODO support multiple services (or at least give error if multiple
87 cout << "dinit-start: start a dinit service" << endl;
88 cout << " --help : show this help" << endl;
89 cout << " --no-wait : don't wait for service startup/shutdown to complete" << endl;
90 cout << " --quiet : suppress output (except errors)" << endl;
91 cout << " -s, --system : control system daemon instead of user daemon" << endl;
92 cout << " <service-name> : start the named service" << endl;
97 control_socket_path = "/dev/dinitctl";
100 char * userhome = getenv("HOME");
101 if (userhome == nullptr) {
102 struct passwd * pwuid_p = getpwuid(getuid());
103 if (pwuid_p != nullptr) {
104 userhome = pwuid_p->pw_dir;
108 if (userhome != nullptr) {
109 control_socket_str = userhome;
110 control_socket_str += "/.dinitctl";
111 control_socket_path = control_socket_str.c_str();
114 cerr << "Cannot locate user home directory (set HOME or check /etc/passwd file)" << endl;
119 int socknum = socket(AF_UNIX, SOCK_STREAM, 0);
125 struct sockaddr_un * name;
126 uint sockaddr_size = offsetof(struct sockaddr_un, sun_path) + strlen(control_socket_path) + 1;
127 name = (struct sockaddr_un *) malloc(sockaddr_size);
128 if (name == nullptr) {
129 cerr << "dinit-start: out of memory" << endl;
133 name->sun_family = AF_UNIX;
134 strcpy(name->sun_path, control_socket_path);
136 int connr = connect(socknum, (struct sockaddr *) name, sockaddr_size);
142 // TODO should start by querying protocol version
145 uint16_t sname_len = strlen(service_name);
146 int bufsize = 3 + sname_len;
147 char * buf = new char[bufsize];
149 buf[0] = DINIT_CP_LOADSERVICE;
150 memcpy(buf + 1, &sname_len, 2);
151 memcpy(buf + 3, service_name, sname_len);
153 int r = write(socknum, buf, bufsize);
154 // TODO make sure we write it all
161 // Now we expect a reply:
162 // NOTE: should skip over information packets.
166 fillBufferTo(&rbuffer, socknum, 1);
169 ServiceState target_state;
172 if (rbuffer[0] == DINIT_RP_SERVICERECORD) {
173 fillBufferTo(&rbuffer, socknum, 2 + sizeof(handle));
174 rbuffer.extract((char *) &handle, 2, sizeof(handle));
175 state = static_cast<ServiceState>(rbuffer[1]);
176 target_state = static_cast<ServiceState>(rbuffer[2 + sizeof(handle)]);
177 rbuffer.consume(3 + sizeof(handle));
179 else if (rbuffer[0] == DINIT_RP_NOSERVICE) {
180 cerr << "Failed to find/load service." << endl;
184 cerr << "Protocol error." << endl;
188 // Need to issue STARTSERVICE:
189 if (target_state != ServiceState::STARTED) {
190 buf = new char[2 + sizeof(handle)];
191 buf[0] = DINIT_CP_STARTSERVICE;
192 buf[1] = 0; // don't pin
193 memcpy(buf + 2, &handle, sizeof(handle));
194 r = write(socknum, buf, 2 + sizeof(handle));
198 if (state == ServiceState::STARTED) {
200 cout << "Service already started." << endl;
202 return 0; // success!
205 if (! wait_for_service) {
209 // Wait until service started:
210 r = rbuffer.fillTo(socknum, 2);
212 if (rbuffer[0] >= 100) {
213 int pktlen = (unsigned char) rbuffer[1];
214 fillBufferTo(&rbuffer, socknum, pktlen);
216 if (rbuffer[0] == DINIT_IP_SERVICEEVENT) {
218 rbuffer.extract((char *) &ev_handle, 2, sizeof(ev_handle));
219 ServiceEvent event = static_cast<ServiceEvent>(rbuffer[2 + sizeof(ev_handle)]);
220 if (ev_handle == handle && event == ServiceEvent::STARTED) {
222 cout << "Service started." << endl;
229 // Not an information packet?
230 cerr << "protocol error" << endl;
239 cerr << "protocol error (connection closed by server)" << endl;
243 catch (ReadCPException &exc) {
244 cerr << "control socket read failure or protocol error" << endl;
247 catch (std::bad_alloc &exc) {
248 cerr << "out of memory" << endl;