dinitctl.cc 62 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829
  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 service_status(int socknum, cpbuffer_t &rbuffer, const char *service_name, command_t command, bool verbose);
  41. static int shutdown_dinit(int soclknum, cpbuffer_t &, bool verbose);
  42. static int add_remove_dependency(int socknum, cpbuffer_t &rbuffer, bool add, const char *service_from,
  43. const char *service_to, dependency_type dep_type, bool verbose);
  44. static int enable_disable_service(int socknum, cpbuffer_t &rbuffer, const char *from, const char *to,
  45. bool enable, bool verbose);
  46. static int do_setenv(int socknum, cpbuffer_t &rbuffer, std::vector<const char *> &env_names);
  47. static int trigger_service(int socknum, cpbuffer_t &rbuffer, const char *service_name, bool trigger_value);
  48. static int cat_service_log(int socknum, cpbuffer_t &rbuffer, const char *service_name);
  49. static const char * describeState(bool stopped)
  50. {
  51. return stopped ? "stopped" : "started";
  52. }
  53. static const char * describeVerb(bool stop)
  54. {
  55. return stop ? "stop" : "start";
  56. }
  57. enum class command_t {
  58. NONE,
  59. START_SERVICE,
  60. WAKE_SERVICE,
  61. STOP_SERVICE,
  62. RESTART_SERVICE,
  63. RELEASE_SERVICE,
  64. UNPIN_SERVICE,
  65. UNLOAD_SERVICE,
  66. RELOAD_SERVICE,
  67. LIST_SERVICES,
  68. SERVICE_STATUS,
  69. SHUTDOWN,
  70. ADD_DEPENDENCY,
  71. RM_DEPENDENCY,
  72. ENABLE_SERVICE,
  73. DISABLE_SERVICE,
  74. SETENV,
  75. SET_TRIGGER,
  76. UNSET_TRIGGER,
  77. CAT_LOG,
  78. IS_ACTIVE,
  79. IS_FAILED,
  80. };
  81. class dinit_protocol_error
  82. {
  83. // no body
  84. };
  85. // Entry point.
  86. int dinitctl_main(int argc, char **argv)
  87. {
  88. using namespace std;
  89. bool show_help = argc < 2;
  90. const char *service_name = nullptr;
  91. const char *to_service_name = nullptr;
  92. dependency_type dep_type = dependency_type::AFTER; // avoid maybe-uninitialised warning
  93. bool dep_type_set = false;
  94. std::string control_socket_str;
  95. const char * control_socket_path = nullptr;
  96. bool verbose = true;
  97. bool user_dinit = (getuid() != 0); // communicate with user daemon
  98. bool wait_for_service = true;
  99. bool do_pin = false;
  100. bool do_force = false;
  101. bool ignore_unstarted = false;
  102. bool use_passed_cfd = false;
  103. command_t command = command_t::NONE;
  104. std::vector<const char *> cmd_args;
  105. for (int i = 1; i < argc; i++) {
  106. if (argv[i][0] == '-') {
  107. if (strcmp(argv[i], "--help") == 0) {
  108. show_help = true;
  109. break;
  110. }
  111. else if (strcmp(argv[i], "--version") == 0) {
  112. cout << "Dinit version " << DINIT_VERSION << ".\n";
  113. return 0;
  114. }
  115. else if (strcmp(argv[i], "--no-wait") == 0) {
  116. wait_for_service = false;
  117. }
  118. else if (strcmp(argv[i], "--ignore-unstarted") == 0) {
  119. ignore_unstarted = true;
  120. }
  121. else if (strcmp(argv[i], "--quiet") == 0) {
  122. verbose = false;
  123. }
  124. else if (strcmp(argv[i], "--system") == 0 || strcmp(argv[i], "-s") == 0) {
  125. user_dinit = false;
  126. }
  127. else if (strcmp(argv[i], "--user") == 0 || strcmp(argv[i], "-u") == 0) {
  128. user_dinit = true;
  129. }
  130. else if (strcmp(argv[i], "--pin") == 0) {
  131. do_pin = true;
  132. }
  133. else if (strcmp(argv[i], "--socket-path") == 0 || strcmp(argv[i], "-p") == 0) {
  134. ++i;
  135. if (i == argc) {
  136. cerr << "dinitctl: --socket-path/-p should be followed by socket path" << std::endl;
  137. return 1;
  138. }
  139. control_socket_str = argv[i];
  140. }
  141. else if (strcmp(argv[i], "--use-passed-cfd") == 0) {
  142. use_passed_cfd = true;
  143. }
  144. else if ((command == command_t::ENABLE_SERVICE || command == command_t::DISABLE_SERVICE)
  145. && strcmp(argv[i], "--from") == 0) {
  146. ++i;
  147. if (i == argc) {
  148. cerr << "dinitctl: --from should be followed by a service name" << std::endl;
  149. return 1;
  150. }
  151. service_name = argv[i];
  152. }
  153. else if ((command == command_t::STOP_SERVICE || command == command_t::RESTART_SERVICE)
  154. && (strcmp(argv[i], "--force") == 0 || strcmp(argv[i], "-f") == 0)) {
  155. do_force = true;
  156. }
  157. else {
  158. cerr << "dinitctl: unrecognized/invalid option: " << argv[i] << " (use --help for help)\n";
  159. return 1;
  160. }
  161. }
  162. else if (command == command_t::NONE) {
  163. if (strcmp(argv[i], "start") == 0) {
  164. command = command_t::START_SERVICE;
  165. }
  166. else if (strcmp(argv[i], "wake") == 0) {
  167. command = command_t::WAKE_SERVICE;
  168. }
  169. else if (strcmp(argv[i], "stop") == 0) {
  170. command = command_t::STOP_SERVICE;
  171. }
  172. else if (strcmp(argv[i], "restart") == 0) {
  173. command = command_t::RESTART_SERVICE;
  174. }
  175. else if (strcmp(argv[i], "release") == 0) {
  176. command = command_t::RELEASE_SERVICE;
  177. }
  178. else if (strcmp(argv[i], "unpin") == 0) {
  179. command = command_t::UNPIN_SERVICE;
  180. }
  181. else if (strcmp(argv[i], "unload") == 0) {
  182. command = command_t::UNLOAD_SERVICE;
  183. }
  184. else if (strcmp(argv[i], "reload") == 0) {
  185. command = command_t::RELOAD_SERVICE;
  186. }
  187. else if (strcmp(argv[i], "list") == 0) {
  188. command = command_t::LIST_SERVICES;
  189. }
  190. else if (strcmp(argv[i], "status") == 0) {
  191. command = command_t::SERVICE_STATUS;
  192. }
  193. else if (strcmp(argv[i], "is-active") == 0) {
  194. command = command_t::IS_ACTIVE;
  195. }
  196. else if (strcmp(argv[i], "is-failed") == 0) {
  197. command = command_t::IS_FAILED;
  198. }
  199. else if (strcmp(argv[i], "shutdown") == 0) {
  200. command = command_t::SHUTDOWN;
  201. }
  202. else if (strcmp(argv[i], "add-dep") == 0) {
  203. command = command_t::ADD_DEPENDENCY;
  204. }
  205. else if (strcmp(argv[i], "rm-dep") == 0) {
  206. command = command_t::RM_DEPENDENCY;
  207. }
  208. else if (strcmp(argv[i], "enable") == 0) {
  209. command = command_t::ENABLE_SERVICE;
  210. }
  211. else if (strcmp(argv[i], "disable") == 0) {
  212. command = command_t::DISABLE_SERVICE;
  213. }
  214. else if (strcmp(argv[i], "setenv") == 0) {
  215. command = command_t::SETENV;
  216. }
  217. else if (strcmp(argv[i], "trigger") == 0) {
  218. command = command_t::SET_TRIGGER;
  219. }
  220. else if (strcmp(argv[i], "untrigger") == 0) {
  221. command = command_t::UNSET_TRIGGER;
  222. }
  223. else if (strcmp(argv[i], "catlog") == 0) {
  224. command = command_t::CAT_LOG;
  225. }
  226. else {
  227. cerr << "dinitctl: unrecognized command: " << argv[i] << " (use --help for help)\n";
  228. return 1;
  229. }
  230. }
  231. else {
  232. // service name / other non-option
  233. if (command == command_t::ADD_DEPENDENCY || command == command_t::RM_DEPENDENCY) {
  234. if (! dep_type_set) {
  235. if (strcmp(argv[i], "regular") == 0) {
  236. dep_type = dependency_type::REGULAR;
  237. }
  238. else if (strcmp(argv[i], "milestone") == 0) {
  239. dep_type = dependency_type::MILESTONE;
  240. }
  241. else if (strcmp(argv[i], "waits-for") == 0) {
  242. dep_type = dependency_type::WAITS_FOR;
  243. }
  244. else {
  245. show_help = true;
  246. break;
  247. }
  248. dep_type_set = true;
  249. }
  250. else if (service_name == nullptr) {
  251. service_name = argv[i];
  252. }
  253. else if (to_service_name == nullptr) {
  254. to_service_name = argv[i];
  255. }
  256. else {
  257. show_help = true;
  258. break;
  259. }
  260. }
  261. else if (command == command_t::ENABLE_SERVICE || command == command_t::DISABLE_SERVICE) {
  262. if (to_service_name != nullptr) {
  263. show_help = true;
  264. break;
  265. }
  266. to_service_name = argv[i];
  267. }
  268. else {
  269. cmd_args.push_back(argv[i]);
  270. }
  271. }
  272. }
  273. // Additional argument checks for various commands:
  274. if (command == command_t::NONE) {
  275. show_help = true;
  276. }
  277. else if (command == command_t::ENABLE_SERVICE || command == command_t::DISABLE_SERVICE) {
  278. show_help |= (to_service_name == nullptr);
  279. }
  280. else if (command == command_t::SETENV) {
  281. // Handle SETENV specially, since it needs arguments but they are not service names
  282. if (cmd_args.empty()) {
  283. show_help = true;
  284. }
  285. }
  286. else {
  287. bool no_service_cmd = (command == command_t::LIST_SERVICES || command == command_t::SHUTDOWN);
  288. if (no_service_cmd) {
  289. if (!cmd_args.empty()) {
  290. show_help = true;
  291. }
  292. }
  293. else {
  294. if (command == command_t::ADD_DEPENDENCY || command == command_t::RM_DEPENDENCY) {
  295. if (! dep_type_set || service_name == nullptr || to_service_name == nullptr) {
  296. show_help = true;
  297. }
  298. }
  299. else if (cmd_args.empty()) {
  300. show_help = true;
  301. }
  302. else {
  303. // No command can currently accept more than one service argument:
  304. if (cmd_args.size() > 1) {
  305. show_help = true;
  306. }
  307. service_name = cmd_args.front();
  308. }
  309. }
  310. }
  311. if (show_help) {
  312. cout << "dinitctl: control Dinit services\n"
  313. "\n"
  314. "Usage:\n"
  315. " dinitctl [options] status <service-name>\n"
  316. " dinitctl [options] is-active <service-name>\n"
  317. " dinitctl [options] is-failed <service-name>\n"
  318. " dinitctl [options] start [options] <service-name>\n"
  319. " dinitctl [options] stop [options] <service-name>\n"
  320. " dinitctl [options] restart [options] <service-name>\n"
  321. " dinitctl [options] wake [options] <service-name>\n"
  322. " dinitctl [options] release [options] <service-name>\n"
  323. " dinitctl [options] unpin <service-name>\n"
  324. " dinitctl [options] unload <service-name>\n"
  325. " dinitctl [options] reload <service-name>\n"
  326. " dinitctl [options] list\n"
  327. " dinitctl [options] shutdown\n"
  328. " dinitctl [options] add-dep <type> <from-service> <to-service>\n"
  329. " dinitctl [options] rm-dep <type> <from-service> <to-service>\n"
  330. " dinitctl [options] enable [--from <from-service>] <to-service>\n"
  331. " dinitctl [options] disable [--from <from-service>] <to-service>\n"
  332. " dinitctl [options] trigger <service-name>\n"
  333. " dinitctl [options] untrigger <service-name>\n"
  334. " dinitctl [options] setenv [name[=value] ...]\n"
  335. " dinitctl [options] catlog <service-name>\n"
  336. "\n"
  337. "Note: An activated service continues running when its dependents stop.\n"
  338. "\n"
  339. "General options:\n"
  340. " --help : show this help\n"
  341. " -s, --system : control system daemon (default if run as root)\n"
  342. " -u, --user : control user daemon\n"
  343. " --quiet : suppress output (except errors)\n"
  344. " --socket-path <path>, -p <path>\n"
  345. " : specify socket for communication with daemon\n"
  346. " --use-passed-cfd : use the socket file descriptor identified by the DINIT_CS_FD\n"
  347. " environment variable to communicate with the dinit daemon.\n"
  348. "\n"
  349. "Command options:\n"
  350. " --no-wait : don't wait for service startup/shutdown to complete\n"
  351. " --pin : pin the service in the requested state\n"
  352. " --force : force stop even if dependents will be affected\n";
  353. return 1;
  354. }
  355. // Begin the real work: connect to dinit
  356. signal(SIGPIPE, SIG_IGN);
  357. int socknum = -1;
  358. if (use_passed_cfd) {
  359. socknum = get_passed_cfd();
  360. if (socknum == -1) {
  361. use_passed_cfd = false;
  362. }
  363. }
  364. if (!use_passed_cfd) {
  365. // Locate control socket
  366. if (!control_socket_str.empty()) {
  367. control_socket_path = control_socket_str.c_str();
  368. }
  369. else {
  370. control_socket_path = get_default_socket_path(control_socket_str, user_dinit);
  371. if (control_socket_path == nullptr) {
  372. cerr << "dinitctl: cannot locate user home directory (set XDG_RUNTIME_DIR, HOME, check /etc/passwd file, or "
  373. "specify socket path via -p)" << endl;
  374. return 1;
  375. }
  376. }
  377. }
  378. try {
  379. if (!use_passed_cfd) {
  380. socknum = connect_to_daemon(control_socket_path);
  381. }
  382. // Start by querying protocol version:
  383. cpbuffer_t rbuffer;
  384. uint16_t daemon_protocol_ver = check_protocol_version(min_cp_version, max_cp_version, rbuffer, socknum);
  385. if (command == command_t::UNPIN_SERVICE) {
  386. return unpin_service(socknum, rbuffer, service_name, verbose);
  387. }
  388. else if (command == command_t::UNLOAD_SERVICE) {
  389. return unload_service(socknum, rbuffer, service_name, verbose);
  390. }
  391. else if (command == command_t::RELOAD_SERVICE) {
  392. return reload_service(socknum, rbuffer, service_name, verbose);
  393. }
  394. else if (command == command_t::LIST_SERVICES) {
  395. return list_services(socknum, rbuffer);
  396. }
  397. else if (command == command_t::SERVICE_STATUS || command == command_t::IS_ACTIVE || command == command_t::IS_FAILED) {
  398. return service_status(socknum, rbuffer, service_name, command, verbose);
  399. }
  400. else if (command == command_t::SHUTDOWN) {
  401. return shutdown_dinit(socknum, rbuffer, verbose);
  402. }
  403. else if (command == command_t::ADD_DEPENDENCY || command == command_t::RM_DEPENDENCY) {
  404. return add_remove_dependency(socknum, rbuffer, command == command_t::ADD_DEPENDENCY,
  405. service_name, to_service_name, dep_type, verbose);
  406. }
  407. else if (command == command_t::ENABLE_SERVICE || command == command_t::DISABLE_SERVICE) {
  408. // If only one service specified, assume that we enable for 'boot' service:
  409. if (service_name == nullptr) {
  410. service_name = "boot";
  411. }
  412. return enable_disable_service(socknum, rbuffer, service_name, to_service_name,
  413. command == command_t::ENABLE_SERVICE, verbose);
  414. }
  415. else if (command == command_t::SETENV) {
  416. return do_setenv(socknum, rbuffer, cmd_args);
  417. }
  418. else if (command == command_t::SET_TRIGGER || command == command_t::UNSET_TRIGGER) {
  419. if (daemon_protocol_ver < 2) {
  420. throw cp_old_server_exception();
  421. }
  422. return trigger_service(socknum, rbuffer, service_name, (command == command_t::SET_TRIGGER));
  423. }
  424. else if (command == command_t::CAT_LOG) {
  425. if (daemon_protocol_ver < 2) {
  426. throw cp_old_server_exception();
  427. }
  428. return cat_service_log(socknum, rbuffer, service_name);
  429. }
  430. else {
  431. return start_stop_service(socknum, rbuffer, service_name, command, do_pin, do_force,
  432. wait_for_service, ignore_unstarted, verbose);
  433. }
  434. }
  435. catch (cp_old_client_exception &e) {
  436. std::cerr << "dinitctl: too old (server reports newer protocol version)" << std::endl;
  437. }
  438. catch (cp_old_server_exception &e) {
  439. std::cerr << "dinitctl: server too old or protocol error" << std::endl;
  440. }
  441. catch (cp_read_exception &e) {
  442. cerr << "dinitctl: control socket read failure or protocol error" << endl;
  443. }
  444. catch (cp_write_exception &e) {
  445. cerr << "dinitctl: control socket write error: " << std::strerror(e.errcode) << endl;
  446. }
  447. catch (dinit_protocol_error &e) {
  448. cerr << "dinitctl: protocol error" << endl;
  449. }
  450. catch (general_error &ge) {
  451. std::cerr << "dinit-client";
  452. if (ge.get_action() != nullptr) {
  453. std::cerr << ": " << ge.get_action();
  454. std::string &arg = ge.get_arg();
  455. if (!arg.empty()) {
  456. std::cerr << " " << arg;
  457. }
  458. }
  459. if (ge.get_err() != 0) {
  460. std::cerr << ": " << strerror(ge.get_err());
  461. }
  462. std::cerr << '\n';
  463. }
  464. return 1;
  465. }
  466. int main(int argc, char **argv)
  467. {
  468. try {
  469. return dinitctl_main(argc, argv);
  470. }
  471. catch (std::bad_alloc &e) {
  472. std::cerr << "dinitctl: out of memory\n";
  473. }
  474. return 1;
  475. }
  476. // Size of service status info (in various packets)
  477. constexpr static unsigned STATUS_BUFFER_SIZE = 6 + ((sizeof(pid_t) > sizeof(int)) ? sizeof(pid_t) : sizeof(int));
  478. // Extract/read a string of specified length from the buffer/socket. The string is consumed
  479. // from the buffer.
  480. static std::string read_string(int socknum, cpbuffer_t &rbuffer, uint32_t length)
  481. {
  482. int rb_len = rbuffer.get_length();
  483. if (uint32_t(rb_len) >= length) {
  484. std::string r = rbuffer.extract_string(0, length);
  485. rbuffer.consume(length);
  486. return r;
  487. }
  488. std::string r = rbuffer.extract_string(0, rb_len);
  489. uint32_t rlen = length - rb_len;
  490. uint32_t clen;
  491. do {
  492. rbuffer.reset();
  493. fill_some(rbuffer, socknum);
  494. char *bptr = rbuffer.get_ptr(0);
  495. clen = rbuffer.get_length();
  496. clen = std::min(clen, rlen);
  497. r.append(bptr, clen);
  498. rlen -= clen;
  499. } while (rlen > 0);
  500. rbuffer.consume(clen);
  501. return r;
  502. }
  503. // Load a service: issue load command, wait for reply. Return true on success, display error message
  504. // and return false on failure.
  505. // socknum - the socket fd to communicate via
  506. // rbuffer - the buffer for communication
  507. // name - the name of the service to load
  508. // handle - where to store the handle of the loaded service
  509. // state - where to store the state of the loaded service (may be null).
  510. // write_error - whether to write an error message if the service can't be loaded
  511. static bool load_service(int socknum, cpbuffer_t &rbuffer, const char *name, handle_t *handle,
  512. service_state_t *state, bool write_error=true)
  513. {
  514. // Load 'to' service:
  515. if (issue_load_service(socknum, name)) {
  516. return false;
  517. }
  518. wait_for_reply(rbuffer, socknum);
  519. if (check_load_reply(socknum, rbuffer, handle, state, write_error) != 0) {
  520. return false;
  521. }
  522. return true;
  523. }
  524. // Get the service name for a given handle, by querying the daemon.
  525. static std::string get_service_name(int socknum, cpbuffer_t &rbuffer, handle_t handle)
  526. {
  527. auto m = membuf()
  528. .append((char) DINIT_CP_QUERYSERVICENAME)
  529. .append((char) 0)
  530. .append(handle);
  531. write_all_x(socknum, m);
  532. wait_for_reply(rbuffer, socknum);
  533. if (rbuffer[0] != DINIT_RP_SERVICENAME) {
  534. throw cp_read_exception{0};
  535. }
  536. // 1 byte reserved
  537. // uint16_t size
  538. fill_buffer_to(rbuffer, socknum, 2 + sizeof(uint16_t));
  539. uint16_t namesize;
  540. rbuffer.extract(&namesize, 2, sizeof(uint16_t));
  541. rbuffer.consume(2 + sizeof(uint16_t));
  542. std::string name;
  543. do {
  544. if (rbuffer.get_length() == 0) {
  545. fill_some(rbuffer, socknum);
  546. }
  547. size_t to_extract = std::min(size_t(rbuffer.get_length()), namesize - name.length());
  548. size_t contiguous_len = rbuffer.get_contiguous_length(rbuffer.get_ptr(0));
  549. if (contiguous_len <= to_extract) {
  550. name.append(rbuffer.get_ptr(0), contiguous_len);
  551. rbuffer.consume(contiguous_len);
  552. name.append(rbuffer.get_ptr(0), to_extract - contiguous_len);
  553. rbuffer.consume(to_extract - contiguous_len);
  554. }
  555. else {
  556. name.append(rbuffer.get_ptr(0), to_extract);
  557. rbuffer.consume(to_extract);
  558. break;
  559. }
  560. } while (name.length() < namesize);
  561. return name;
  562. }
  563. static void print_termination_details(int exit_status)
  564. {
  565. using namespace std;
  566. if (WIFSIGNALED(exit_status)) {
  567. cout << "signalled - signal ";
  568. cout << WTERMSIG(exit_status);
  569. }
  570. else if (WIFEXITED(exit_status)) {
  571. cout << "exited - status ";
  572. cout << WEXITSTATUS(exit_status);
  573. }
  574. else {
  575. cout << "unknown reason";
  576. }
  577. }
  578. // Wait for a service to reached stopped (do_stop == true) or started (do_stop == false) state.
  579. // Returns 0 if the service started/stopped, 1 if start/stop was cancelled or failed.
  580. static int wait_service_state(int socknum, cpbuffer_t &rbuffer, handle_t handle,
  581. const std::string &service_name, bool do_stop, bool verbose)
  582. {
  583. using std::cout;
  584. using std::cerr;
  585. using std::endl;
  586. service_event_t completionEvent;
  587. service_event_t cancelledEvent;
  588. if (do_stop) {
  589. completionEvent = service_event_t::STOPPED;
  590. cancelledEvent = service_event_t::STOPCANCELLED;
  591. }
  592. else {
  593. completionEvent = service_event_t::STARTED;
  594. cancelledEvent = service_event_t::STARTCANCELLED;
  595. }
  596. // Wait until service started:
  597. int r = rbuffer.fill_to(socknum, 2);
  598. while (r > 0) {
  599. if (rbuffer[0] >= 100) {
  600. unsigned pktlen = (unsigned char) rbuffer[1];
  601. fill_buffer_to(rbuffer, socknum, pktlen);
  602. if (rbuffer[0] == DINIT_IP_SERVICEEVENT) {
  603. // earlier versions do not include status info, the size in that case is base_pkt_size:
  604. constexpr unsigned base_pkt_size = 2 + sizeof(handle_t) + 1;
  605. if (pktlen < base_pkt_size) {
  606. throw dinit_protocol_error();
  607. }
  608. handle_t ev_handle;
  609. rbuffer.extract((char *) &ev_handle, 2, sizeof(ev_handle));
  610. service_event_t event = static_cast<service_event_t>(rbuffer[2 + sizeof(ev_handle)]);
  611. if (ev_handle == handle) {
  612. if (event == completionEvent) {
  613. if (verbose) {
  614. cout << "Service '" << service_name << "' " << describeState(do_stop) << ".\n";
  615. }
  616. return 0;
  617. }
  618. else if (event == cancelledEvent) {
  619. if (verbose) {
  620. cout << "Service '" << service_name << "' " << describeVerb(do_stop) << " cancelled.\n";
  621. }
  622. return 1;
  623. }
  624. else if (!do_stop && event == service_event_t::FAILEDSTART) {
  625. if (verbose) {
  626. cout << "Service '" << service_name << "' failed to start.\n";
  627. if (pktlen >= base_pkt_size + STATUS_BUFFER_SIZE) {
  628. stopped_reason_t stop_reason =
  629. static_cast<stopped_reason_t>(rbuffer[base_pkt_size + 3]);
  630. int exit_status;
  631. rbuffer.extract((char *)&exit_status, base_pkt_size + 6, sizeof(exit_status));
  632. switch (stop_reason) {
  633. case stopped_reason_t::DEPFAILED:
  634. cout << "Reason: a dependency of the service failed to start. Check dinit log.\n";
  635. break;
  636. case stopped_reason_t::TIMEDOUT:
  637. cout << "Reason: start timed out.\n";
  638. break;
  639. case stopped_reason_t::EXECFAILED:
  640. cout << "Reason: execution of service process failed:\n";
  641. uint16_t launch_stage;
  642. rbuffer.extract((char *)&launch_stage, base_pkt_size + 4, sizeof(uint16_t));
  643. cout << " Stage: " << exec_stage_descriptions[launch_stage] << "\n";
  644. cout << " Error: " << strerror(exit_status) << "\n";
  645. break;
  646. case stopped_reason_t::FAILED:
  647. cout << "Reason: service process terminated before ready: ";
  648. print_termination_details(exit_status);
  649. cout << "\n";
  650. break;
  651. default:
  652. cout << "Reason unknown/unrecognised. Check dinit log.\n";
  653. }
  654. }
  655. }
  656. return 1;
  657. }
  658. }
  659. }
  660. rbuffer.consume(pktlen);
  661. r = rbuffer.fill_to(socknum, 2);
  662. }
  663. else {
  664. // Not an information packet?
  665. throw dinit_protocol_error();
  666. }
  667. }
  668. if (r == -1) {
  669. perror("dinitctl: read");
  670. }
  671. else {
  672. throw dinit_protocol_error();
  673. }
  674. return 1;
  675. }
  676. // Start/stop a service
  677. static int start_stop_service(int socknum, cpbuffer_t &rbuffer, const char *service_name,
  678. command_t command, bool do_pin, bool do_force, bool wait_for_service, bool ignore_unstarted,
  679. bool verbose)
  680. {
  681. using namespace std;
  682. bool do_stop = (command == command_t::STOP_SERVICE || command == command_t::RELEASE_SERVICE);
  683. service_state_t state;
  684. handle_t handle;
  685. if (command != command_t::RESTART_SERVICE && command != command_t::STOP_SERVICE
  686. && command != command_t::RELEASE_SERVICE) {
  687. ignore_unstarted = false;
  688. }
  689. if (!load_service(socknum, rbuffer, service_name, &handle, &state, !ignore_unstarted)) {
  690. return ignore_unstarted ? 0 : 1;
  691. }
  692. service_state_t wanted_state = do_stop ? service_state_t::STOPPED : service_state_t::STARTED;
  693. int pcommand = 0;
  694. switch (command) {
  695. case command_t::STOP_SERVICE:
  696. case command_t::RESTART_SERVICE: // stop, and then start
  697. pcommand = DINIT_CP_STOPSERVICE;
  698. break;
  699. case command_t::RELEASE_SERVICE:
  700. pcommand = DINIT_CP_RELEASESERVICE;
  701. break;
  702. case command_t::START_SERVICE:
  703. pcommand = DINIT_CP_STARTSERVICE;
  704. break;
  705. case command_t::WAKE_SERVICE:
  706. pcommand = DINIT_CP_WAKESERVICE;
  707. break;
  708. default: ;
  709. }
  710. // Need to issue STOPSERVICE/STARTSERVICE
  711. // We'll do this regardless of the current service state / target state, since issuing
  712. // start/stop also sets or clears the "explicitly started" flag on the service.
  713. {
  714. char flags = (do_pin ? 1 : 0) | ((pcommand == DINIT_CP_STOPSERVICE && !do_force) ? 2 : 0);
  715. if (command == command_t::RESTART_SERVICE) {
  716. flags |= 4;
  717. }
  718. auto m = membuf()
  719. .append((char) pcommand)
  720. .append(flags)
  721. .append(handle);
  722. write_all_x(socknum, m);
  723. wait_for_reply(rbuffer, socknum);
  724. auto reply_pkt_h = rbuffer[0];
  725. rbuffer.consume(1); // consume header
  726. if (reply_pkt_h == DINIT_RP_ALREADYSS) {
  727. bool already = (state == wanted_state);
  728. if (verbose) {
  729. cout << "Service " << (already ? "(already) " : "")
  730. << describeState(do_stop) << "." << endl;
  731. }
  732. return 0; // success!
  733. }
  734. if (reply_pkt_h == DINIT_RP_PINNEDSTARTED) {
  735. cerr << "dinitctl: cannot stop service '" << service_name << "' as it is pinned started\n";
  736. return 1;
  737. }
  738. if (reply_pkt_h == DINIT_RP_PINNEDSTOPPED) {
  739. cerr << "dinitctl: cannot start service '" << service_name << "' as it is pinned stopped\n";
  740. return 1;
  741. }
  742. if (reply_pkt_h == DINIT_RP_DEPENDENTS && pcommand == DINIT_CP_STOPSERVICE) {
  743. cerr << "dinitctl: cannot stop service '" << service_name << "' due to the following dependents:\n";
  744. if (command != command_t::RESTART_SERVICE) {
  745. cerr << "(only direct dependents are listed. Exercise caution before using '--force' !!)\n";
  746. }
  747. // size_t number, N * handle_t handles
  748. size_t number;
  749. rbuffer.fill_to(socknum, sizeof(number));
  750. rbuffer.extract(&number, 0, sizeof(number));
  751. rbuffer.consume(sizeof(number));
  752. std::vector<handle_t> handles;
  753. handles.reserve(number);
  754. for (size_t i = 0; i < number; i++) {
  755. handle_t handle;
  756. rbuffer.fill_to(socknum, sizeof(handle_t));
  757. rbuffer.extract(&handle, 0, sizeof(handle));
  758. handles.push_back(handle);
  759. rbuffer.consume(sizeof(handle));
  760. }
  761. // Print the directly affected dependents:
  762. cerr << " ";
  763. for (handle_t handle : handles) {
  764. cerr << " " << get_service_name(socknum, rbuffer, handle);
  765. }
  766. cerr << "\n";
  767. return 1;
  768. }
  769. if (reply_pkt_h == DINIT_RP_NAK && command == command_t::RESTART_SERVICE) {
  770. if (ignore_unstarted) {
  771. if (verbose) {
  772. cout << "Service '" << service_name << "' is not currently started.\n";
  773. }
  774. return 0;
  775. }
  776. cerr << "dinitctl: cannot restart service; service not started.\n";
  777. return 1;
  778. }
  779. if (reply_pkt_h == DINIT_RP_NAK && command == command_t::WAKE_SERVICE) {
  780. cerr << "dinitctl: service has no active dependents, cannot wake.\n";
  781. return 1;
  782. }
  783. if (reply_pkt_h == DINIT_RP_SHUTTINGDOWN) {
  784. cerr << "dinitctl: cannot start/restart/wake service, shutdown is in progress.\n";
  785. return 1;
  786. }
  787. if (reply_pkt_h != DINIT_RP_ACK && reply_pkt_h != DINIT_RP_ALREADYSS) {
  788. cerr << "dinitctl: protocol error." << endl;
  789. return 1;
  790. }
  791. }
  792. if (! wait_for_service) {
  793. if (verbose) {
  794. cout << "Issued " << describeVerb(do_stop) << " command successfully for service '"
  795. << service_name << "'." << endl;
  796. }
  797. return 0;
  798. }
  799. return wait_service_state(socknum, rbuffer, handle, service_name, do_stop, verbose);
  800. }
  801. // Issue a "load service" command (DINIT_CP_LOADSERVICE), without waiting for
  802. // a response. Returns 1 on failure (with error logged), 0 on success.
  803. static int issue_load_service(int socknum, const char *service_name, bool find_only)
  804. {
  805. // Build buffer;
  806. uint16_t sname_len = strlen(service_name);
  807. int bufsize = 3 + sname_len;
  808. std::unique_ptr<char[]> ubuf(new char[bufsize]);
  809. auto buf = ubuf.get();
  810. buf[0] = find_only ? DINIT_CP_FINDSERVICE : DINIT_CP_LOADSERVICE;
  811. memcpy(buf + 1, &sname_len, 2);
  812. memcpy(buf + 3, service_name, sname_len);
  813. write_all_x(socknum, buf, bufsize);
  814. return 0;
  815. }
  816. // Check that a "load service" reply was received, and that the requested service was found.
  817. // state_p may be null.
  818. static int check_load_reply(int socknum, cpbuffer_t &rbuffer, handle_t *handle_p, service_state_t *state_p, bool write_error)
  819. {
  820. using namespace std;
  821. if (rbuffer[0] == DINIT_RP_SERVICERECORD) {
  822. fill_buffer_to(rbuffer, socknum, 2 + sizeof(*handle_p));
  823. rbuffer.extract((char *) handle_p, 2, sizeof(*handle_p));
  824. if (state_p) *state_p = static_cast<service_state_t>(rbuffer[1]);
  825. //target_state = static_cast<service_state_t>(rbuffer[2 + sizeof(handle)]);
  826. rbuffer.consume(3 + sizeof(*handle_p));
  827. return 0;
  828. }
  829. else if (rbuffer[0] == DINIT_RP_NOSERVICE) {
  830. if (write_error) {
  831. cerr << "dinitctl: failed to find service description.\n";
  832. cerr << "dinitctl: check service description file exists / service name spelling.\n";
  833. }
  834. return 1;
  835. }
  836. else if (rbuffer[0] == DINIT_RP_SERVICE_DESC_ERR) {
  837. if (write_error) {
  838. cerr << "dinitctl: error in service description.\n";
  839. cerr << "dinitctl: try 'dinitcheck <service-name>' or check log for more information.\n";
  840. }
  841. return 1;
  842. }
  843. else if (rbuffer[0] == DINIT_RP_SERVICE_LOAD_ERR) {
  844. if (write_error) {
  845. cerr << "dinitctl: error loading service (or dependency of service).\n";
  846. cerr << "dinitctl: try 'dinitcheck <service-name>' or check log for more information.\n";
  847. }
  848. return 1;
  849. }
  850. else {
  851. throw dinit_protocol_error();
  852. }
  853. }
  854. static int unpin_service(int socknum, cpbuffer_t &rbuffer, const char *service_name, bool verbose)
  855. {
  856. using namespace std;
  857. handle_t handle;
  858. // Build buffer;
  859. if (! load_service(socknum, rbuffer, service_name, &handle, nullptr)) {
  860. return 1;
  861. }
  862. // Issue UNPIN command.
  863. {
  864. auto m = membuf()
  865. .append<char>(DINIT_CP_UNPINSERVICE)
  866. .append(handle);
  867. write_all_x(socknum, m);
  868. wait_for_reply(rbuffer, socknum);
  869. if (rbuffer[0] != DINIT_RP_ACK) {
  870. cerr << "dinitctl: protocol error." << endl;
  871. return 1;
  872. }
  873. rbuffer.consume(1);
  874. }
  875. if (verbose) {
  876. cout << "Service '" << service_name << "' unpinned." << endl;
  877. }
  878. return 0;
  879. }
  880. static int unload_service(int socknum, cpbuffer_t &rbuffer, const char *service_name, bool verbose)
  881. {
  882. using namespace std;
  883. if (issue_load_service(socknum, service_name, true) == 1) {
  884. return 1;
  885. }
  886. wait_for_reply(rbuffer, socknum);
  887. handle_t handle;
  888. if (rbuffer[0] == DINIT_RP_NOSERVICE) {
  889. cerr << "dinitctl: service not loaded." << endl;
  890. return 1;
  891. }
  892. if (check_load_reply(socknum, rbuffer, &handle, nullptr) != 0) {
  893. return 1;
  894. }
  895. // Issue UNLOAD command.
  896. {
  897. auto m = membuf()
  898. .append<char>(DINIT_CP_UNLOADSERVICE)
  899. .append(handle);
  900. write_all_x(socknum, m);
  901. wait_for_reply(rbuffer, socknum);
  902. if (rbuffer[0] == DINIT_RP_NAK) {
  903. cerr << "dinitctl: could not unload service; service not stopped, or is a dependency of "
  904. "other service." << endl;
  905. return 1;
  906. }
  907. if (rbuffer[0] != DINIT_RP_ACK) {
  908. cerr << "dinitctl: protocol error." << endl;
  909. return 1;
  910. }
  911. rbuffer.consume(1);
  912. }
  913. if (verbose) {
  914. cout << "Service '" << service_name << "' unloaded." << endl;
  915. }
  916. return 0;
  917. }
  918. static int reload_service(int socknum, cpbuffer_t &rbuffer, const char *service_name, bool verbose)
  919. {
  920. using namespace std;
  921. if (issue_load_service(socknum, service_name, true) == 1) {
  922. return 1;
  923. }
  924. wait_for_reply(rbuffer, socknum);
  925. handle_t handle;
  926. if (rbuffer[0] == DINIT_RP_NOSERVICE) {
  927. cerr << "dinitctl: service not loaded." << endl;
  928. return 1;
  929. }
  930. if (check_load_reply(socknum, rbuffer, &handle, nullptr) != 0) {
  931. return 1;
  932. }
  933. // Issue RELOAD command.
  934. {
  935. auto m = membuf()
  936. .append<char>(DINIT_CP_RELOADSERVICE)
  937. .append(handle);
  938. write_all_x(socknum, m);
  939. wait_for_reply(rbuffer, socknum);
  940. if (rbuffer[0] == DINIT_RP_NAK) {
  941. cerr << "dinitctl: could not reload service; service in wrong state, incompatible change, "
  942. "or bad service description." << endl;
  943. return 1;
  944. }
  945. if (rbuffer[0] != DINIT_RP_ACK) {
  946. cerr << "dinitctl: protocol error." << endl;
  947. return 1;
  948. }
  949. rbuffer.consume(1);
  950. }
  951. if (verbose) {
  952. cout << "Service '" << service_name << "' reloaded." << endl;
  953. }
  954. return 0;
  955. }
  956. static int list_services(int socknum, cpbuffer_t &rbuffer)
  957. {
  958. using namespace std;
  959. char cmdbuf[] = { (char)DINIT_CP_LISTSERVICES };
  960. write_all_x(socknum, cmdbuf, 1);
  961. wait_for_reply(rbuffer, socknum);
  962. while (rbuffer[0] == DINIT_RP_SVCINFO) {
  963. int hdrsize = 8 + std::max(sizeof(int), sizeof(pid_t));
  964. fill_buffer_to(rbuffer, socknum, hdrsize);
  965. unsigned name_len = (unsigned char)rbuffer[1];
  966. service_state_t current = static_cast<service_state_t>(rbuffer[2]);
  967. service_state_t target = static_cast<service_state_t>(rbuffer[3]);
  968. int console_flags = rbuffer[4];
  969. bool has_console = (console_flags & 2) != 0;
  970. bool waiting_console = (console_flags & 1) != 0;
  971. bool was_skipped = (console_flags & 4) != 0;
  972. bool marked_active = (console_flags & 8) != 0;
  973. bool has_pid = (console_flags & 16) != 0;
  974. stopped_reason_t stop_reason = static_cast<stopped_reason_t>(rbuffer[5]);
  975. pid_t service_pid;
  976. int exit_status;
  977. if (has_pid) {
  978. rbuffer.extract((char *)&service_pid, 8, sizeof(service_pid));
  979. }
  980. else {
  981. rbuffer.extract((char *)&exit_status, 8, sizeof(exit_status));
  982. }
  983. fill_buffer_to(rbuffer, socknum, name_len + hdrsize);
  984. char *name_ptr = rbuffer.get_ptr(hdrsize);
  985. unsigned clength = std::min(rbuffer.get_contiguous_length(name_ptr), name_len);
  986. string name = string(name_ptr, clength);
  987. name.append(rbuffer.get_buf_base(), name_len - clength);
  988. cout << "[";
  989. // [ ] if marked active; otherwise, { } if target state is STARTED
  990. // + if started, 's' if skipped, space otherwise
  991. char lbracket = target == service_state_t::STARTED ? '{' : ' ';
  992. char rbracket = target == service_state_t::STARTED ? '}' : ' ';
  993. cout << (marked_active ? '[' : lbracket);
  994. if (current == service_state_t::STARTED) {
  995. cout << (was_skipped ? 's' : '+');
  996. }
  997. else {
  998. cout << ' ';
  999. }
  1000. cout << (marked_active ? ']' : rbracket);
  1001. if (current == service_state_t::STARTING) {
  1002. cout << "<<";
  1003. }
  1004. else if (current == service_state_t::STOPPING) {
  1005. cout << ">>";
  1006. }
  1007. else {
  1008. cout << " ";
  1009. }
  1010. cout << (target == service_state_t::STOPPED ? '{' : ' ');
  1011. if (current == service_state_t::STOPPED) {
  1012. bool did_fail = false;
  1013. if (stop_reason == stopped_reason_t::TERMINATED) {
  1014. if (!WIFEXITED(exit_status) || WEXITSTATUS(exit_status) != 0) {
  1015. did_fail = true;
  1016. }
  1017. }
  1018. else did_fail = (stop_reason != stopped_reason_t::NORMAL);
  1019. cout << (did_fail ? 'X' : '-');
  1020. }
  1021. else {
  1022. cout << ' ';
  1023. }
  1024. cout << (target == service_state_t::STOPPED ? '}' : ' ');
  1025. cout << "] " << name;
  1026. if (current != service_state_t::STOPPED && has_pid) {
  1027. cout << " (pid: " << service_pid << ")";
  1028. }
  1029. if (current == service_state_t::STOPPED && stop_reason == stopped_reason_t::TERMINATED) {
  1030. if (WIFEXITED(exit_status)) {
  1031. cout << " (exit status: " << WEXITSTATUS(exit_status) << ")";
  1032. }
  1033. else if (WIFSIGNALED(exit_status)) {
  1034. cout << " (signal: " << WTERMSIG(exit_status) << ")";
  1035. }
  1036. }
  1037. if (has_console) {
  1038. cout << " (has console)";
  1039. }
  1040. else if (waiting_console) {
  1041. cout << " (waiting for console)";
  1042. }
  1043. cout << endl;
  1044. rbuffer.consume(hdrsize + name_len);
  1045. wait_for_reply(rbuffer, socknum);
  1046. }
  1047. if (rbuffer[0] != DINIT_RP_LISTDONE) {
  1048. cerr << "dinitctl: control socket protocol error" << endl;
  1049. return 1;
  1050. }
  1051. return 0;
  1052. }
  1053. static int service_status(int socknum, cpbuffer_t &rbuffer, const char *service_name, command_t command, bool verbose)
  1054. {
  1055. using namespace std;
  1056. bool is_status = command == command_t::SERVICE_STATUS;
  1057. if (issue_load_service(socknum, service_name, true) == 1) {
  1058. return 1;
  1059. }
  1060. wait_for_reply(rbuffer, socknum);
  1061. handle_t handle;
  1062. if (rbuffer[0] == DINIT_RP_NOSERVICE) {
  1063. if (is_status) {
  1064. cerr << "dinitctl: service not loaded." << endl;
  1065. }
  1066. return 1;
  1067. }
  1068. if (check_load_reply(socknum, rbuffer, &handle, nullptr, is_status) != 0) {
  1069. return 1;
  1070. }
  1071. // Issue STATUS request
  1072. {
  1073. auto m = membuf()
  1074. .append<char>(DINIT_CP_SERVICESTATUS)
  1075. .append(handle);
  1076. write_all_x(socknum, m);
  1077. wait_for_reply(rbuffer, socknum);
  1078. if (rbuffer[0] != DINIT_RP_SERVICESTATUS) {
  1079. cerr << "dinitctl: protocol error." << endl;
  1080. return 1;
  1081. }
  1082. rbuffer.consume(1);
  1083. fill_buffer_to(rbuffer, socknum, STATUS_BUFFER_SIZE + 1 /* reserved */);
  1084. rbuffer.consume(1);
  1085. service_state_t current = static_cast<service_state_t>(rbuffer[0]);
  1086. service_state_t target = static_cast<service_state_t>(rbuffer[1]);
  1087. int console_flags = rbuffer[2];
  1088. bool has_console = (console_flags & 2) != 0;
  1089. bool waiting_console = (console_flags & 1) != 0;
  1090. bool was_skipped = (console_flags & 4) != 0;
  1091. bool marked_active = (console_flags & 8) != 0;
  1092. bool has_pid = (console_flags & 16) != 0;
  1093. stopped_reason_t stop_reason = static_cast<stopped_reason_t>(rbuffer[3]);
  1094. pid_t service_pid = -1;
  1095. int exit_status = 0;
  1096. if (has_pid) {
  1097. rbuffer.extract((char *)&service_pid, 6, sizeof(service_pid));
  1098. }
  1099. else {
  1100. rbuffer.extract((char *)&exit_status, 6, sizeof(exit_status));
  1101. }
  1102. switch (command) {
  1103. case command_t::IS_ACTIVE:
  1104. case command_t::IS_FAILED:
  1105. if (verbose) {
  1106. switch (current) {
  1107. case service_state_t::STOPPED:
  1108. cout << "STOPPED" << endl;
  1109. break;
  1110. case service_state_t::STARTING:
  1111. cout << "STARTING" << endl;
  1112. break;
  1113. case service_state_t::STARTED:
  1114. cout << "STARTED" << endl;
  1115. break;
  1116. case service_state_t::STOPPING:
  1117. cout << "STOPPING" << endl;
  1118. }
  1119. }
  1120. /* return 0 (success) for started */
  1121. if (command == command_t::IS_ACTIVE) {
  1122. return current != service_state_t::STARTED;
  1123. }
  1124. /* return 0 (success) for specific stopped reasons */
  1125. if (current == service_state_t::STOPPED) {
  1126. switch (stop_reason) {
  1127. case stopped_reason_t::DEPFAILED:
  1128. case stopped_reason_t::FAILED:
  1129. case stopped_reason_t::EXECFAILED:
  1130. case stopped_reason_t::TIMEDOUT:
  1131. return 0;
  1132. default:
  1133. break;
  1134. }
  1135. }
  1136. return 1;
  1137. default:
  1138. /* status */
  1139. break;
  1140. }
  1141. cout << "Service: " << service_name << "\n"
  1142. " State: ";
  1143. switch (current) {
  1144. case service_state_t::STOPPED:
  1145. cout << "STOPPED";
  1146. switch (stop_reason) {
  1147. case stopped_reason_t::DEPRESTART:
  1148. cout << " (dependency restarted)";
  1149. break;
  1150. case stopped_reason_t::DEPFAILED:
  1151. cout << " (dependency failed/terminated)";
  1152. break;
  1153. case stopped_reason_t::FAILED:
  1154. cout << " (failed to start";
  1155. if (exit_status != 0) {
  1156. cout << "; ";
  1157. print_termination_details(exit_status);
  1158. }
  1159. cout << ")";
  1160. break;
  1161. case stopped_reason_t::EXECFAILED:
  1162. uint16_t launch_stage;
  1163. rbuffer.extract((char *)&launch_stage, 4, 2);
  1164. cout << " (could not be launched)\n";
  1165. cout << " Stage: " << exec_stage_descriptions[launch_stage] << "\n";
  1166. cout << " Error: " << strerror(exit_status);
  1167. break;
  1168. case stopped_reason_t::TERMINATED:
  1169. cout << " (terminated";
  1170. if (exit_status != 0) {
  1171. cout << "; ";
  1172. print_termination_details(exit_status);
  1173. }
  1174. cout << ")";
  1175. break;
  1176. case stopped_reason_t::TIMEDOUT:
  1177. cout << " (start timed out)";
  1178. break;
  1179. case stopped_reason_t::NORMAL:
  1180. break;
  1181. }
  1182. break;
  1183. case service_state_t::STARTING:
  1184. cout << "STARTING";
  1185. if (target == service_state_t::STOPPED) {
  1186. cout << " (target state: STOPPED)";
  1187. }
  1188. break;
  1189. case service_state_t::STARTED:
  1190. cout << "STARTED";
  1191. if (was_skipped) {
  1192. cout << " (startup skipped)";
  1193. }
  1194. break;
  1195. case service_state_t::STOPPING:
  1196. cout << "STOPPING";
  1197. if (target == service_state_t::STARTED) {
  1198. cout << " (target state: STARTED)";
  1199. }
  1200. if (exit_status != 0) {
  1201. cout << "(terminated ;";
  1202. print_termination_details(exit_status);
  1203. cout << ")";
  1204. }
  1205. }
  1206. if (has_console) {
  1207. cout << " (holding console)";
  1208. }
  1209. if (waiting_console) {
  1210. cout << " (waiting for console)";
  1211. }
  1212. cout << "\n";
  1213. if (target == service_state_t::STARTED) {
  1214. cout << " Activation: ";
  1215. if (marked_active) {
  1216. cout << "explicitly started\n";
  1217. }
  1218. else {
  1219. cout << "start due to dependent(s)\n";
  1220. }
  1221. }
  1222. if (service_pid != -1) {
  1223. cout << " Process ID: " << service_pid << "\n";
  1224. }
  1225. }
  1226. return 0;
  1227. }
  1228. static int add_remove_dependency(int socknum, cpbuffer_t &rbuffer, bool add,
  1229. const char *service_from, const char *service_to, dependency_type dep_type, bool verbose)
  1230. {
  1231. using namespace std;
  1232. handle_t from_handle;
  1233. handle_t to_handle;
  1234. if (! load_service(socknum, rbuffer, service_from, &from_handle, nullptr)
  1235. || ! load_service(socknum, rbuffer, service_to, &to_handle, nullptr)) {
  1236. return 1;
  1237. }
  1238. if (from_handle == to_handle) {
  1239. cerr << "dinitctl: can not add/remove a dependency from a service to itself" << endl;
  1240. return 1;
  1241. }
  1242. auto m = membuf()
  1243. .append<char>(add ? (char)DINIT_CP_ADD_DEP : (char)DINIT_CP_REM_DEP)
  1244. .append(static_cast<char>(dep_type))
  1245. .append(from_handle)
  1246. .append(to_handle);
  1247. write_all_x(socknum, m);
  1248. wait_for_reply(rbuffer, socknum);
  1249. // check reply
  1250. if (rbuffer[0] == DINIT_RP_NAK) {
  1251. if (add) {
  1252. cerr << "dinitctl: could not add dependency: circular dependency or wrong state" << endl;
  1253. }
  1254. else {
  1255. cerr << "dinitctl: no such dependency to remove" << endl;
  1256. }
  1257. return 1;
  1258. }
  1259. if (rbuffer[0] != DINIT_RP_ACK) {
  1260. cerr << "dinitctl: control socket protocol error" << endl;
  1261. return 1;
  1262. }
  1263. if (verbose) {
  1264. std::cout << "Service '" << service_from << "': dependency '" << service_to << "' " << (add ? "added" : "removed") << endl;
  1265. }
  1266. return 0;
  1267. }
  1268. static int shutdown_dinit(int socknum, cpbuffer_t &rbuffer, bool verbose)
  1269. {
  1270. // TODO support no-wait option.
  1271. using namespace std;
  1272. auto m = membuf()
  1273. .append<char>(DINIT_CP_SHUTDOWN)
  1274. .append(static_cast<char>(shutdown_type_t::HALT));
  1275. write_all_x(socknum, m);
  1276. wait_for_reply(rbuffer, socknum);
  1277. if (rbuffer[0] != DINIT_RP_ACK) {
  1278. cerr << "dinitctl: control socket protocol error" << endl;
  1279. return 1;
  1280. }
  1281. if (verbose) {
  1282. std::cout << "Shutting down dinit..." << std::endl;
  1283. }
  1284. // Now wait for rollback complete, by waiting for the connection to close:
  1285. try {
  1286. while (true) {
  1287. wait_for_info(rbuffer, socknum);
  1288. rbuffer.consume(rbuffer[1]);
  1289. }
  1290. }
  1291. catch (cp_read_exception &exc) {
  1292. // Assume that the connection closed.
  1293. }
  1294. if (verbose) {
  1295. std::cout << "Connection closed." << std::endl;
  1296. }
  1297. return 0;
  1298. }
  1299. // exception for cancelling a service operation
  1300. class service_op_cancel { };
  1301. static int enable_disable_service(int socknum, cpbuffer_t &rbuffer, const char *from, const char *to,
  1302. bool enable, bool verbose)
  1303. {
  1304. using namespace std;
  1305. service_state_t from_state = service_state_t::STARTED;
  1306. handle_t from_handle;
  1307. handle_t to_handle;
  1308. if (!load_service(socknum, rbuffer, from, &from_handle, &from_state)
  1309. || !load_service(socknum, rbuffer, to, &to_handle, nullptr)) {
  1310. return 1;
  1311. }
  1312. // Get service load path
  1313. char buf[1] = { DINIT_CP_QUERY_LOAD_MECH };
  1314. write_all_x(socknum, buf, 1);
  1315. wait_for_reply(rbuffer, socknum);
  1316. if (rbuffer[0] != DINIT_RP_LOADER_MECH) {
  1317. cerr << "dinitctl: control socket protocol error" << endl;
  1318. return 1;
  1319. }
  1320. // Packet type, load mechanism type, packet size:
  1321. fill_buffer_to(rbuffer, socknum, 2 + sizeof(uint32_t));
  1322. if (rbuffer[1] != SSET_TYPE_DIRLOAD) {
  1323. cerr << "dinitctl: unknown configuration, unable to load service descriptions" << endl;
  1324. return 1;
  1325. }
  1326. vector<string> paths;
  1327. uint32_t pktsize;
  1328. rbuffer.extract(&pktsize, 2, sizeof(uint32_t));
  1329. fill_buffer_to(rbuffer, socknum, 2 + sizeof(uint32_t) * 3); // path entries, cwd length
  1330. uint32_t path_entries; // number of service directories
  1331. rbuffer.extract(&path_entries, 2 + sizeof(uint32_t), sizeof(uint32_t));
  1332. uint32_t cwd_len;
  1333. rbuffer.extract(&cwd_len, 2 + sizeof(uint32_t) * 2, sizeof(uint32_t));
  1334. rbuffer.consume(2 + sizeof(uint32_t) * 3);
  1335. pktsize -= 2 + sizeof(uint32_t) * 3;
  1336. // Read current working directory of daemon:
  1337. std::string dinit_cwd = read_string(socknum, rbuffer, cwd_len);
  1338. // dinit daemon base directory against which service paths are resolved is in dinit_cwd
  1339. for (uint32_t i = 0; i < path_entries; ++i) {
  1340. uint32_t plen;
  1341. fill_buffer_to(rbuffer, socknum, sizeof(uint32_t));
  1342. rbuffer.extract(&plen, 0, sizeof(uint32_t));
  1343. rbuffer.consume(sizeof(uint32_t));
  1344. paths.push_back(read_string(socknum, rbuffer, plen));
  1345. }
  1346. // all service directories are now in the 'paths' vector
  1347. // Load/read service description for 'from' service:
  1348. ifstream service_file;
  1349. string service_file_path;
  1350. for (std::string path : paths) {
  1351. string test_path = combine_paths(combine_paths(dinit_cwd, path.c_str()), from);
  1352. service_file.open(test_path.c_str(), ios::in);
  1353. if (service_file) {
  1354. service_file_path = test_path;
  1355. break;
  1356. }
  1357. }
  1358. if (! service_file) {
  1359. cerr << "dinitctl: could not locate service file for service '" << from << "'" << endl;
  1360. return 1;
  1361. }
  1362. // We now need to read the service file, identify the waits-for.d directory (bail out if more than one),
  1363. // make sure the service is not listed as a dependency individually.
  1364. string waits_for_d;
  1365. try {
  1366. process_service_file(from, service_file, [&](string &line, unsigned line_num, string &setting,
  1367. dinit_load::string_iterator i, dinit_load::string_iterator end) -> void {
  1368. if (setting == "waits-for" || setting == "depends-on" || setting == "depends-ms") {
  1369. string dname = dinit_load::read_setting_value(line_num, i, end);
  1370. if (dname == to) {
  1371. // There is already a dependency
  1372. cerr << "dinitctl: there is a fixed dependency to service '" << to
  1373. << "' in the service description of '" << from << "'." << endl;
  1374. throw service_op_cancel();
  1375. }
  1376. }
  1377. else if (setting == "waits-for.d") {
  1378. string dname = dinit_load::read_setting_value(line_num, i, end);
  1379. if (! waits_for_d.empty()) {
  1380. cerr << "dinitctl: service '" << from << "' has multiple waits-for.d directories "
  1381. << "specified in service description" << endl;
  1382. throw service_op_cancel();
  1383. }
  1384. waits_for_d = std::move(dname);
  1385. }
  1386. });
  1387. }
  1388. catch (const service_op_cancel &cexc) {
  1389. return 1;
  1390. }
  1391. // If the from service has no waits-for.d specified, we can't continue
  1392. if (waits_for_d.empty()) {
  1393. cerr << "dinitctl: service '" << from << "' has no waits-for.d directory specified" << endl;
  1394. return 1;
  1395. }
  1396. // The waits-for.d path is relative to the service file path, combine:
  1397. string waits_for_d_full = combine_paths(parent_path(service_file_path), waits_for_d.c_str());
  1398. // check if dependency already exists
  1399. string dep_link_path = combine_paths(waits_for_d_full, to);
  1400. struct stat stat_buf;
  1401. if (lstat(dep_link_path.c_str(), &stat_buf) == -1) {
  1402. if (errno != ENOENT) {
  1403. cerr << "dinitctl: checking for existing dependency link: " << dep_link_path << ": "
  1404. << strerror(errno) << endl;
  1405. return 1;
  1406. }
  1407. }
  1408. else {
  1409. // dependency already exists
  1410. if (enable) {
  1411. cerr << "dinitctl: service already enabled." << endl;
  1412. return 1;
  1413. }
  1414. }
  1415. // warn if 'from' service is not started
  1416. if (enable && from_state != service_state_t::STARTED) {
  1417. cerr << "dinitctl: warning: enabling dependency for non-started service" << endl;
  1418. }
  1419. // add/remove dependency
  1420. constexpr int enable_pktsize = 2 + sizeof(handle_t) * 2;
  1421. char cmdbuf[enable_pktsize] = { char(enable ? DINIT_CP_ENABLESERVICE : DINIT_CP_REM_DEP),
  1422. char(dependency_type::WAITS_FOR)};
  1423. memcpy(cmdbuf + 2, &from_handle, sizeof(from_handle));
  1424. memcpy(cmdbuf + 2 + sizeof(from_handle), &to_handle, sizeof(to_handle));
  1425. write_all_x(socknum, cmdbuf, enable_pktsize);
  1426. wait_for_reply(rbuffer, socknum);
  1427. // check reply
  1428. if (rbuffer[0] == DINIT_RP_NAK) {
  1429. if (enable) {
  1430. cerr << "dinitctl: could not enable service: possible circular dependency" << endl;
  1431. }
  1432. else {
  1433. cerr << "dinitctl: service not currently enabled" << endl;
  1434. }
  1435. return 1;
  1436. }
  1437. if (rbuffer[0] != DINIT_RP_ACK) {
  1438. cerr << "dinitctl: control socket protocol error" << endl;
  1439. return 1;
  1440. }
  1441. rbuffer.consume(1);
  1442. // create link
  1443. if (enable) {
  1444. if (symlink((string("../") + to).c_str(), dep_link_path.c_str()) == -1) {
  1445. cerr << "dinitctl: could not create symlink at " << dep_link_path << ": " << strerror(errno)
  1446. << "\n" "dinitctl: note: service was enabled for now; persistent enable failed."
  1447. << endl;
  1448. return 1;
  1449. }
  1450. }
  1451. else {
  1452. if (unlink(dep_link_path.c_str()) == -1) {
  1453. cerr << "dinitctl: could not unlink dependency entry " << dep_link_path << ": "
  1454. << strerror(errno) << "\n"
  1455. "dinitctl: note: service was disabled for now; persistent disable failed." << endl;
  1456. return 1;
  1457. }
  1458. }
  1459. // Check status of the service now
  1460. auto m = membuf()
  1461. .append<char>(DINIT_CP_SERVICESTATUS)
  1462. .append(to_handle);
  1463. write_all_x(socknum, m);
  1464. wait_for_reply(rbuffer, socknum);
  1465. if (rbuffer[0] != DINIT_RP_SERVICESTATUS) {
  1466. cerr << "dinitctl: protocol error." << endl;
  1467. return 1;
  1468. }
  1469. rbuffer.consume(1);
  1470. int statussize = 6 + std::max(sizeof(pid_t), sizeof(int));;
  1471. fill_buffer_to(rbuffer, socknum, statussize + 1 /* reserved */);
  1472. rbuffer.consume(1);
  1473. service_state_t current = static_cast<service_state_t>(rbuffer[0]);
  1474. service_state_t target = static_cast<service_state_t>(rbuffer[1]);
  1475. rbuffer.consume(statussize);
  1476. if (verbose) {
  1477. cout << "Service '" << to << "' has been " << (enable ? "enabled" : "disabled") << "." << endl;
  1478. }
  1479. if (enable) {
  1480. if (current != service_state_t::STARTED) {
  1481. wait_service_state(socknum, rbuffer, to_handle, to, false /* start */, verbose);
  1482. }
  1483. }
  1484. else {
  1485. if (target != service_state_t::STOPPED) {
  1486. std::cerr << "dinitctl: note: disabled service may have other dependents\n";
  1487. }
  1488. }
  1489. return 0;
  1490. }
  1491. static int do_setenv(int socknum, cpbuffer_t &rbuffer, std::vector<const char *> &env_names)
  1492. {
  1493. using namespace std;
  1494. string buf;
  1495. for (const char *envp : env_names) {
  1496. buf.clear();
  1497. buf.reserve(6);
  1498. // protocol message and size space
  1499. buf.push_back(DINIT_CP_SETENV);
  1500. buf.append(2, 0);
  1501. const unsigned hdr_len = 3;
  1502. // either full var or name
  1503. auto elen = strlen(envp);
  1504. buf.append(envp, elen);
  1505. // if '=' not found, get value from environment
  1506. if (!memchr(envp, '=', elen)) {
  1507. buf.push_back('=');
  1508. auto *envv = getenv(envp);
  1509. if (envv) {
  1510. buf.append(envv);
  1511. }
  1512. }
  1513. uint16_t bufs = buf.size() - hdr_len;
  1514. // sanitize length early on
  1515. if (buf.size() > cpbuffer_t::get_size()) {
  1516. auto eq = buf.find('=', hdr_len);
  1517. auto name = buf.substr(hdr_len, eq - hdr_len);
  1518. cerr << "dinitctl: environment variable '" << name << "' too long." << endl;
  1519. return 1;
  1520. }
  1521. // set size in protocol message
  1522. memcpy(&buf[1], &bufs, 2);
  1523. // send
  1524. write_all_x(socknum, buf.data(), buf.size());
  1525. wait_for_reply(rbuffer, socknum);
  1526. if (rbuffer[0] == DINIT_RP_BADREQ) {
  1527. cerr << "dinitctl: failed to export environment." << endl;
  1528. return 1;
  1529. } else if (rbuffer[0] != DINIT_RP_ACK) {
  1530. throw dinit_protocol_error();
  1531. }
  1532. rbuffer.consume(1);
  1533. }
  1534. return 0;
  1535. }
  1536. static int trigger_service(int socknum, cpbuffer_t &rbuffer, const char *service_name, bool trigger_value)
  1537. {
  1538. using namespace std;
  1539. handle_t handle;
  1540. if (!load_service(socknum, rbuffer, service_name, &handle, nullptr, true)) {
  1541. return 1;
  1542. }
  1543. // Issue SET_TRIGGER command.
  1544. {
  1545. auto m = membuf()
  1546. .append<char>(DINIT_CP_SETTRIGGER)
  1547. .append(handle)
  1548. .append<char>(trigger_value);
  1549. write_all_x(socknum, m);
  1550. wait_for_reply(rbuffer, socknum);
  1551. if (rbuffer[0] == DINIT_RP_NAK) {
  1552. cerr << "dinitctl: cannot trigger a service that is not of 'triggered' type.\n";
  1553. return 1;
  1554. }
  1555. if (rbuffer[0] != DINIT_RP_ACK) {
  1556. cerr << "dinitctl: protocol error.\n";
  1557. return 1;
  1558. }
  1559. rbuffer.consume(1);
  1560. }
  1561. return 0;
  1562. }
  1563. static int cat_service_log(int socknum, cpbuffer_t &rbuffer, const char *service_name)
  1564. {
  1565. using namespace std;
  1566. handle_t handle;
  1567. if (!load_service(socknum, rbuffer, service_name, &handle, nullptr, true)) {
  1568. return 1;
  1569. }
  1570. // Issue CATLOG
  1571. auto m = membuf()
  1572. .append<char>(DINIT_CP_CATLOG)
  1573. .append<char>(0)
  1574. .append(handle);
  1575. write_all_x(socknum, m);
  1576. wait_for_reply(rbuffer, socknum);
  1577. if (rbuffer[0] == DINIT_RP_NAK) {
  1578. cerr << "dinitctl: cannot cat log for service not configured to buffer output.\n";
  1579. return 1;
  1580. }
  1581. if (rbuffer[0] != DINIT_RP_SERVICE_LOG) {
  1582. cerr << "dinitctl: protocol error.\n";
  1583. return 1;
  1584. }
  1585. fill_buffer_to(rbuffer, socknum, 1 + sizeof(unsigned));
  1586. unsigned bufsize;
  1587. rbuffer.extract(&bufsize, 1, sizeof(unsigned));
  1588. rbuffer.consume(1 + sizeof(unsigned));
  1589. // output the log
  1590. if (bufsize > 0) {
  1591. cout << flush;
  1592. bool trailing_nl = false;
  1593. char output_buf[rbuffer.get_size()];
  1594. while (bufsize > 0) {
  1595. unsigned l = rbuffer.get_length();
  1596. if (l == 0) {
  1597. fill_buffer_to(rbuffer, socknum, 1);
  1598. }
  1599. l = std::min(rbuffer.get_length(), bufsize);
  1600. rbuffer.extract(output_buf, 0, l);
  1601. write(STDOUT_FILENO, output_buf, l);
  1602. rbuffer.consume(l);
  1603. bufsize -= l;
  1604. trailing_nl = (output_buf[l - 1] == '\n');
  1605. }
  1606. if (!trailing_nl) {
  1607. cout << "\n(last line is truncated or incomplete)\n";
  1608. }
  1609. }
  1610. return 0;
  1611. }