dinitctl.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. #include <cstdio>
  2. #include <cstddef>
  3. #include <cstring>
  4. #include <string>
  5. #include <iostream>
  6. #include <system_error>
  7. #include <memory>
  8. #include <sys/types.h>
  9. #include <sys/socket.h>
  10. #include <sys/un.h>
  11. #include <unistd.h>
  12. #include <signal.h>
  13. #include <pwd.h>
  14. #include "control-cmds.h"
  15. #include "service-constants.h"
  16. #include "cpbuffer.h"
  17. #include "dinit-client.h"
  18. // dinitctl: utility to control the Dinit daemon, including starting and stopping of services.
  19. // This utility communicates with the dinit daemon via a unix stream socket (/dev/initctl, or $HOME/.dinitctl).
  20. static constexpr uint16_t min_cp_version = 1;
  21. static constexpr uint16_t max_cp_version = 1;
  22. enum class command_t;
  23. static int issue_load_service(int socknum, const char *service_name, bool find_only = false);
  24. static int check_load_reply(int socknum, cpbuffer_t &, handle_t *handle_p, service_state_t *state_p);
  25. static int start_stop_service(int socknum, cpbuffer_t &, const char *service_name, command_t command,
  26. bool do_pin, bool wait_for_service, bool verbose);
  27. static int unpin_service(int socknum, cpbuffer_t &, const char *service_name, bool verbose);
  28. static int unload_service(int socknum, cpbuffer_t &, const char *service_name);
  29. static int list_services(int socknum, cpbuffer_t &);
  30. static int shutdown_dinit(int soclknum, cpbuffer_t &);
  31. static const char * describeState(bool stopped)
  32. {
  33. return stopped ? "stopped" : "started";
  34. }
  35. static const char * describeVerb(bool stop)
  36. {
  37. return stop ? "stop" : "start";
  38. }
  39. enum class command_t {
  40. NONE,
  41. START_SERVICE,
  42. WAKE_SERVICE,
  43. STOP_SERVICE,
  44. RELEASE_SERVICE,
  45. UNPIN_SERVICE,
  46. UNLOAD_SERVICE,
  47. LIST_SERVICES,
  48. SHUTDOWN
  49. };
  50. // Entry point.
  51. int main(int argc, char **argv)
  52. {
  53. using namespace std;
  54. bool show_help = argc < 2;
  55. char *service_name = nullptr;
  56. std::string control_socket_str;
  57. const char * control_socket_path = nullptr;
  58. bool verbose = true;
  59. bool sys_dinit = false; // communicate with system daemon
  60. bool wait_for_service = true;
  61. bool do_pin = false;
  62. command_t command = command_t::NONE;
  63. for (int i = 1; i < argc; i++) {
  64. if (argv[i][0] == '-') {
  65. if (strcmp(argv[i], "--help") == 0) {
  66. show_help = true;
  67. break;
  68. }
  69. else if (strcmp(argv[i], "--no-wait") == 0) {
  70. wait_for_service = false;
  71. }
  72. else if (strcmp(argv[i], "--quiet") == 0) {
  73. verbose = false;
  74. }
  75. else if (strcmp(argv[i], "--system") == 0 || strcmp(argv[i], "-s") == 0) {
  76. sys_dinit = true;
  77. }
  78. else if (strcmp(argv[i], "--pin") == 0) {
  79. do_pin = true;
  80. }
  81. else {
  82. return 1;
  83. }
  84. }
  85. else if (command == command_t::NONE) {
  86. if (strcmp(argv[i], "start") == 0) {
  87. command = command_t::START_SERVICE;
  88. }
  89. else if (strcmp(argv[i], "wake") == 0) {
  90. command = command_t::WAKE_SERVICE;
  91. }
  92. else if (strcmp(argv[i], "stop") == 0) {
  93. command = command_t::STOP_SERVICE;
  94. }
  95. else if (strcmp(argv[i], "release") == 0) {
  96. command = command_t::RELEASE_SERVICE;
  97. }
  98. else if (strcmp(argv[i], "unpin") == 0) {
  99. command = command_t::UNPIN_SERVICE;
  100. }
  101. else if (strcmp(argv[i], "unload") == 0) {
  102. command = command_t::UNLOAD_SERVICE;
  103. }
  104. else if (strcmp(argv[i], "list") == 0) {
  105. command = command_t::LIST_SERVICES;
  106. }
  107. else if (strcmp(argv[i], "shutdown") == 0) {
  108. command = command_t::SHUTDOWN;
  109. }
  110. else {
  111. show_help = true;
  112. break;
  113. }
  114. }
  115. else {
  116. // service name
  117. if (service_name != nullptr) {
  118. show_help = true;
  119. break;
  120. }
  121. service_name = argv[i];
  122. // TODO support multiple services
  123. }
  124. }
  125. bool no_service_cmd = (command == command_t::LIST_SERVICES || command == command_t::SHUTDOWN);
  126. if (service_name != nullptr && no_service_cmd) {
  127. show_help = true;
  128. }
  129. if ((service_name == nullptr && ! no_service_cmd) || command == command_t::NONE) {
  130. show_help = true;
  131. }
  132. if (show_help) {
  133. cout << "dinitctl: control Dinit services" << endl;
  134. cout << "\nUsage:" << endl;
  135. cout << " dinitctl [options] start [options] <service-name> : start and activate service" << endl;
  136. cout << " dinitctl [options] stop [options] <service-name> : stop service and cancel explicit activation" << endl;
  137. cout << " dinitctl [options] wake [options] <service-name> : start but do not mark activated" << endl;
  138. cout << " dinitctl [options] release [options] <service-name> : release activation, stop if no dependents" << endl;
  139. cout << " dinitctl [options] unpin <service-name> : un-pin the service (after a previous pin)" << endl;
  140. cout << " dinitctl unload <service-name> : unload the service" << endl;
  141. cout << " dinitctl list : list loaded services" << endl;
  142. cout << " dinitctl shutdown : stop all services and terminate dinit" << endl;
  143. cout << "\nNote: An activated service continues running when its dependents stop." << endl;
  144. cout << "\nGeneral options:" << endl;
  145. cout << " -s, --system : control system daemon instead of user daemon" << endl;
  146. cout << " --quiet : suppress output (except errors)" << endl;
  147. cout << "\nCommand options:" << endl;
  148. cout << " --help : show this help" << endl;
  149. cout << " --no-wait : don't wait for service startup/shutdown to complete" << endl;
  150. cout << " --pin : pin the service in the requested (started/stopped) state" << endl;
  151. return 1;
  152. }
  153. signal(SIGPIPE, SIG_IGN);
  154. control_socket_path = "/dev/dinitctl";
  155. // Locate control socket
  156. if (! sys_dinit) {
  157. char * userhome = getenv("HOME");
  158. if (userhome == nullptr) {
  159. struct passwd * pwuid_p = getpwuid(getuid());
  160. if (pwuid_p != nullptr) {
  161. userhome = pwuid_p->pw_dir;
  162. }
  163. }
  164. if (userhome != nullptr) {
  165. control_socket_str = userhome;
  166. control_socket_str += "/.dinitctl";
  167. control_socket_path = control_socket_str.c_str();
  168. }
  169. else {
  170. cerr << "Cannot locate user home directory (set HOME or check /etc/passwd file)" << endl;
  171. return 1;
  172. }
  173. }
  174. int socknum = socket(AF_UNIX, SOCK_STREAM, 0);
  175. if (socknum == -1) {
  176. perror("dinitctl: socket");
  177. return 1;
  178. }
  179. struct sockaddr_un * name;
  180. uint sockaddr_size = offsetof(struct sockaddr_un, sun_path) + strlen(control_socket_path) + 1;
  181. name = (struct sockaddr_un *) malloc(sockaddr_size);
  182. if (name == nullptr) {
  183. cerr << "dinitctl: Out of memory" << endl;
  184. return 1;
  185. }
  186. name->sun_family = AF_UNIX;
  187. strcpy(name->sun_path, control_socket_path);
  188. int connr = connect(socknum, (struct sockaddr *) name, sockaddr_size);
  189. if (connr == -1) {
  190. perror("dinitctl: connect");
  191. return 1;
  192. }
  193. try {
  194. // Start by querying protocol version:
  195. cpbuffer_t rbuffer;
  196. check_protocol_version(min_cp_version, max_cp_version, rbuffer, socknum);
  197. if (command == command_t::UNPIN_SERVICE) {
  198. return unpin_service(socknum, rbuffer, service_name, verbose);
  199. }
  200. else if (command == command_t::UNLOAD_SERVICE) {
  201. return unload_service(socknum, rbuffer, service_name);
  202. }
  203. else if (command == command_t::LIST_SERVICES) {
  204. return list_services(socknum, rbuffer);
  205. }
  206. else if (command == command_t::SHUTDOWN) {
  207. return shutdown_dinit(socknum, rbuffer);
  208. }
  209. else {
  210. return start_stop_service(socknum, rbuffer, service_name, command, do_pin,
  211. wait_for_service, verbose);
  212. }
  213. }
  214. catch (cp_old_client_exception &e) {
  215. std::cerr << "dinitctl: too old (server reports newer protocol version)" << std::endl;
  216. return 1;
  217. }
  218. catch (cp_old_server_exception &e) {
  219. std::cerr << "dinitctl: server too old or protocol error" << std::endl;
  220. return 1;
  221. }
  222. catch (cp_read_exception &e) {
  223. cerr << "dinitctl: control socket read failure or protocol error" << endl;
  224. return 1;
  225. }
  226. catch (cp_write_exception &e) {
  227. cerr << "dinitctl: control socket write error: " << std::strerror(e.errcode) << endl;
  228. return 1;
  229. }
  230. }
  231. // Start/stop a service
  232. static int start_stop_service(int socknum, cpbuffer_t &rbuffer, const char *service_name,
  233. command_t command, bool do_pin, bool wait_for_service, bool verbose)
  234. {
  235. using namespace std;
  236. bool do_stop = (command == command_t::STOP_SERVICE || command == command_t::RELEASE_SERVICE);
  237. if (issue_load_service(socknum, service_name)) {
  238. return 1;
  239. }
  240. // Now we expect a reply:
  241. wait_for_reply(rbuffer, socknum);
  242. service_state_t state;
  243. //service_state_t target_state;
  244. handle_t handle;
  245. if (check_load_reply(socknum, rbuffer, &handle, &state) != 0) {
  246. return 0;
  247. }
  248. service_state_t wanted_state = do_stop ? service_state_t::STOPPED : service_state_t::STARTED;
  249. int pcommand = 0;
  250. switch (command) {
  251. case command_t::STOP_SERVICE:
  252. pcommand = DINIT_CP_STOPSERVICE;
  253. break;
  254. case command_t::RELEASE_SERVICE:
  255. pcommand = DINIT_CP_RELEASESERVICE;
  256. break;
  257. case command_t::START_SERVICE:
  258. pcommand = DINIT_CP_STARTSERVICE;
  259. break;
  260. case command_t::WAKE_SERVICE:
  261. pcommand = DINIT_CP_WAKESERVICE;
  262. break;
  263. default: ;
  264. }
  265. // Need to issue STOPSERVICE/STARTSERVICE
  266. // We'll do this regardless of the current service state / target state, since issuing
  267. // start/stop also sets or clears the "explicitly started" flag on the service.
  268. {
  269. char buf[2 + sizeof(handle)];
  270. buf[0] = pcommand;
  271. buf[1] = do_pin ? 1 : 0;
  272. memcpy(buf + 2, &handle, sizeof(handle));
  273. write_all_x(socknum, buf, 2 + sizeof(handle));
  274. wait_for_reply(rbuffer, socknum);
  275. if (rbuffer[0] == DINIT_RP_ALREADYSS) {
  276. bool already = (state == wanted_state);
  277. if (verbose) {
  278. cout << "Service " << (already ? "(already) " : "") << describeState(do_stop) << "." << endl;
  279. }
  280. return 0; // success!
  281. }
  282. if (rbuffer[0] != DINIT_RP_ACK) {
  283. cerr << "dinitctl: Protocol error." << endl;
  284. return 1;
  285. }
  286. rbuffer.consume(1);
  287. }
  288. if (! wait_for_service) {
  289. if (verbose) {
  290. cout << "Issued " << describeVerb(do_stop) << " command successfully." << endl;
  291. }
  292. return 0;
  293. }
  294. service_event_t completionEvent;
  295. service_event_t cancelledEvent;
  296. if (do_stop) {
  297. completionEvent = service_event_t::STOPPED;
  298. cancelledEvent = service_event_t::STOPCANCELLED;
  299. }
  300. else {
  301. completionEvent = service_event_t::STARTED;
  302. cancelledEvent = service_event_t::STARTCANCELLED;
  303. }
  304. // Wait until service started:
  305. int r = rbuffer.fill_to(socknum, 2);
  306. while (r > 0) {
  307. if (rbuffer[0] >= 100) {
  308. int pktlen = (unsigned char) rbuffer[1];
  309. fill_buffer_to(rbuffer, socknum, pktlen);
  310. if (rbuffer[0] == DINIT_IP_SERVICEEVENT) {
  311. handle_t ev_handle;
  312. rbuffer.extract((char *) &ev_handle, 2, sizeof(ev_handle));
  313. service_event_t event = static_cast<service_event_t>(rbuffer[2 + sizeof(ev_handle)]);
  314. if (ev_handle == handle) {
  315. if (event == completionEvent) {
  316. if (verbose) {
  317. cout << "Service " << describeState(do_stop) << "." << endl;
  318. }
  319. return 0;
  320. }
  321. else if (event == cancelledEvent) {
  322. if (verbose) {
  323. cout << "Service " << describeVerb(do_stop) << " cancelled." << endl;
  324. }
  325. return 1;
  326. }
  327. else if (! do_stop && event == service_event_t::FAILEDSTART) {
  328. if (verbose) {
  329. cout << "Service failed to start." << endl;
  330. }
  331. return 1;
  332. }
  333. }
  334. }
  335. rbuffer.consume(pktlen);
  336. r = rbuffer.fill_to(socknum, 2);
  337. }
  338. else {
  339. // Not an information packet?
  340. cerr << "dinitctl: protocol error" << endl;
  341. return 1;
  342. }
  343. }
  344. if (r == -1) {
  345. perror("dinitctl: read");
  346. }
  347. else {
  348. cerr << "protocol error (connection closed by server)" << endl;
  349. }
  350. return 1;
  351. }
  352. // Issue a "load service" command (DINIT_CP_LOADSERVICE), without waiting for
  353. // a response. Returns 1 on failure (with error logged), 0 on success.
  354. static int issue_load_service(int socknum, const char *service_name, bool find_only)
  355. {
  356. // Build buffer;
  357. uint16_t sname_len = strlen(service_name);
  358. int bufsize = 3 + sname_len;
  359. std::unique_ptr<char[]> ubuf(new char[bufsize]);
  360. auto buf = ubuf.get();
  361. buf[0] = find_only ? DINIT_CP_FINDSERVICE : DINIT_CP_LOADSERVICE;
  362. memcpy(buf + 1, &sname_len, 2);
  363. memcpy(buf + 3, service_name, sname_len);
  364. write_all_x(socknum, buf, bufsize);
  365. return 0;
  366. }
  367. // Check that a "load service" reply was received, and that the requested service was found.
  368. static int check_load_reply(int socknum, cpbuffer_t &rbuffer, handle_t *handle_p, service_state_t *state_p)
  369. {
  370. using namespace std;
  371. if (rbuffer[0] == DINIT_RP_SERVICERECORD) {
  372. fill_buffer_to(rbuffer, socknum, 2 + sizeof(*handle_p));
  373. rbuffer.extract((char *) handle_p, 2, sizeof(*handle_p));
  374. if (state_p) *state_p = static_cast<service_state_t>(rbuffer[1]);
  375. //target_state = static_cast<service_state_t>(rbuffer[2 + sizeof(handle)]);
  376. rbuffer.consume(3 + sizeof(*handle_p));
  377. return 0;
  378. }
  379. else if (rbuffer[0] == DINIT_RP_NOSERVICE) {
  380. cerr << "dinitctl: failed to find/load service." << endl;
  381. return 1;
  382. }
  383. else {
  384. cerr << "dinitctl: protocol error." << endl;
  385. return 1;
  386. }
  387. }
  388. static int unpin_service(int socknum, cpbuffer_t &rbuffer, const char *service_name, bool verbose)
  389. {
  390. using namespace std;
  391. // Build buffer;
  392. if (issue_load_service(socknum, service_name) == 1) {
  393. return 1;
  394. }
  395. // Now we expect a reply:
  396. wait_for_reply(rbuffer, socknum);
  397. handle_t handle;
  398. if (check_load_reply(socknum, rbuffer, &handle, nullptr) != 0) {
  399. return 1;
  400. }
  401. // Issue UNPIN command.
  402. {
  403. char buf[1 + sizeof(handle)];
  404. buf[0] = DINIT_CP_UNPINSERVICE;
  405. memcpy(buf + 1, &handle, sizeof(handle));
  406. write_all_x(socknum, buf, 2 + sizeof(handle));
  407. wait_for_reply(rbuffer, socknum);
  408. if (rbuffer[0] != DINIT_RP_ACK) {
  409. cerr << "dinitctl: protocol error." << endl;
  410. return 1;
  411. }
  412. rbuffer.consume(1);
  413. }
  414. if (verbose) {
  415. cout << "Service unpinned." << endl;
  416. }
  417. return 0;
  418. }
  419. static int unload_service(int socknum, cpbuffer_t &rbuffer, const char *service_name)
  420. {
  421. using namespace std;
  422. // Build buffer;
  423. if (issue_load_service(socknum, service_name, true) == 1) {
  424. return 1;
  425. }
  426. // Now we expect a reply:
  427. wait_for_reply(rbuffer, socknum);
  428. handle_t handle;
  429. if (check_load_reply(socknum, rbuffer, &handle, nullptr) != 0) {
  430. return 1;
  431. }
  432. // Issue UNLOAD command.
  433. {
  434. char buf[1 + sizeof(handle)];
  435. buf[0] = DINIT_CP_UNLOADSERVICE;
  436. memcpy(buf + 1, &handle, sizeof(handle));
  437. write_all_x(socknum, buf, 2 + sizeof(handle));
  438. wait_for_reply(rbuffer, socknum);
  439. if (rbuffer[0] == DINIT_RP_NAK) {
  440. cerr << "dinitctl: Could not unload service; service not stopped, or is a dependency of "
  441. "other service." << endl;
  442. return 1;
  443. }
  444. if (rbuffer[0] != DINIT_RP_ACK) {
  445. cerr << "dinitctl: Protocol error." << endl;
  446. return 1;
  447. }
  448. rbuffer.consume(1);
  449. }
  450. cout << "Service unloaded." << endl;
  451. return 0;
  452. }
  453. static int list_services(int socknum, cpbuffer_t &rbuffer)
  454. {
  455. using namespace std;
  456. char cmdbuf[] = { (char)DINIT_CP_LISTSERVICES };
  457. write_all_x(socknum, cmdbuf, 1);
  458. wait_for_reply(rbuffer, socknum);
  459. while (rbuffer[0] == DINIT_RP_SVCINFO) {
  460. int hdrsize = 8 + std::max(sizeof(int), sizeof(pid_t));
  461. fill_buffer_to(rbuffer, socknum, hdrsize);
  462. int nameLen = rbuffer[1];
  463. service_state_t current = static_cast<service_state_t>(rbuffer[2]);
  464. service_state_t target = static_cast<service_state_t>(rbuffer[3]);
  465. int console_flags = rbuffer[4];
  466. bool has_console = (console_flags & 2) != 0;
  467. bool waiting_console = (console_flags & 1) != 0;
  468. // stopped_reason_t stop_reason = static_cast<stopped_reason_t>(rbuffer[5]);
  469. pid_t service_pid;
  470. int exit_status;
  471. if (current != service_state_t::STOPPED) {
  472. rbuffer.extract((char *)&service_pid, 8, sizeof(service_pid));
  473. }
  474. else {
  475. rbuffer.extract((char *)&exit_status, 8, sizeof(exit_status));
  476. }
  477. fill_buffer_to(rbuffer, socknum, nameLen + hdrsize);
  478. char *name_ptr = rbuffer.get_ptr(hdrsize);
  479. int clength = std::min(rbuffer.get_contiguous_length(name_ptr), nameLen);
  480. string name = string(name_ptr, clength);
  481. name.append(rbuffer.get_buf_base(), nameLen - clength);
  482. cout << "[";
  483. cout << (target == service_state_t::STARTED ? "{" : " ");
  484. cout << (current == service_state_t::STARTED ? "+" : " ");
  485. cout << (target == service_state_t::STARTED ? "}" : " ");
  486. if (current == service_state_t::STARTING) {
  487. cout << "<<";
  488. }
  489. else if (current == service_state_t::STOPPING) {
  490. cout << ">>";
  491. }
  492. else {
  493. cout << " ";
  494. }
  495. cout << (target == service_state_t::STOPPED ? "{" : " ");
  496. cout << (current == service_state_t::STOPPED ? "-" : " ");
  497. cout << (target == service_state_t::STOPPED ? "}" : " ");
  498. cout << "] " << name;
  499. if (current != service_state_t::STOPPED && service_pid != -1) {
  500. cout << " (pid: " << service_pid << ")";
  501. }
  502. if (has_console) {
  503. cout << " (has console)";
  504. }
  505. else if (waiting_console) {
  506. cout << " (waiting for console)";
  507. }
  508. cout << endl;
  509. rbuffer.consume(hdrsize + nameLen);
  510. wait_for_reply(rbuffer, socknum);
  511. }
  512. if (rbuffer[0] != DINIT_RP_LISTDONE) {
  513. cerr << "dinitctl: Control socket protocol error" << endl;
  514. return 1;
  515. }
  516. return 0;
  517. }
  518. static int shutdown_dinit(int socknum, cpbuffer_t &rbuffer)
  519. {
  520. // TODO support no-wait option.
  521. using namespace std;
  522. // Build buffer;
  523. constexpr int bufsize = 2;
  524. char buf[bufsize];
  525. buf[0] = DINIT_CP_SHUTDOWN;
  526. buf[1] = static_cast<char>(shutdown_type_t::HALT);
  527. write_all_x(socknum, buf, bufsize);
  528. wait_for_reply(rbuffer, socknum);
  529. if (rbuffer[0] != DINIT_RP_ACK) {
  530. cerr << "dinitctl: Control socket protocol error" << endl;
  531. return 1;
  532. }
  533. // Now wait for rollback complete:
  534. try {
  535. while (true) {
  536. wait_for_info(rbuffer, socknum);
  537. if (rbuffer[0] == DINIT_ROLLBACK_COMPLETED) {
  538. break;
  539. }
  540. }
  541. }
  542. catch (cp_read_exception &exc) {
  543. // Dinit can terminate before replying: let's assume that happened.
  544. // TODO: better check, possibly ensure that dinit actually sends rollback complete before
  545. // termination.
  546. }
  547. return 0;
  548. }