dinitctl.cc 46 KB

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