dinitctl.cc 44 KB

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