loadtests.cc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. #include <string>
  2. #include <iostream>
  3. #include <sstream>
  4. #include <cassert>
  5. #include <cstdlib>
  6. #include <cstring>
  7. #include "service.h"
  8. #include "proc-service.h"
  9. #include "load-service.h"
  10. std::string test_service_dir;
  11. environment tenv;
  12. environment::env_map tenvmap;
  13. void init_test_service_dir()
  14. {
  15. test_service_dir = "./test-services";
  16. tenvmap = tenv.build(main_env);
  17. }
  18. void test_basic()
  19. {
  20. dirload_service_set sset(test_service_dir.c_str());
  21. auto t1 = sset.load_service("t1");
  22. assert(t1->get_name() == "t1");
  23. }
  24. void test_env_subst()
  25. {
  26. dirload_service_set sset(test_service_dir.c_str());
  27. bp_sys::setenv("ONEVAR", "a", true);
  28. bp_sys::setenv("TWOVAR", "hellohello", true);
  29. bp_sys::setenv("THREEVAR", "", true);
  30. // leave FOURVAR undefined
  31. auto t2 = static_cast<base_process_service *>(sset.load_service("t2"));
  32. auto exec_parts = t2->get_exec_arg_parts();
  33. assert(strcmp("echo", exec_parts[0]) == 0);
  34. assert(strcmp("a", exec_parts[1]) == 0); // $ONEVAR
  35. assert(strcmp("a", exec_parts[2]) == 0); // ${ONEVAR}
  36. assert(strcmp("b", exec_parts[3]) == 0); // ${ONEVAR+b}
  37. assert(strcmp("b", exec_parts[4]) == 0); // ${ONEVAR:+b}
  38. assert(strcmp("hellohello", exec_parts[5]) == 0); // $TWOVAR
  39. assert(strcmp("hellohello", exec_parts[6]) == 0); // ${TWOVAR}
  40. assert(strcmp("hellohello", exec_parts[7]) == 0); // ${TWOVAR-world}
  41. assert(strcmp("hellohello", exec_parts[8]) == 0); // ${TWOVAR:-world}
  42. assert(strcmp("", exec_parts[9]) == 0); // $THREEVAR
  43. assert(strcmp("", exec_parts[10]) == 0); // ${THREEVAR}
  44. assert(strcmp("empty", exec_parts[11]) == 0); // ${THREEVAR+empty}
  45. assert(strcmp("", exec_parts[12]) == 0); // ${THREEVAR:+empty}
  46. assert(strcmp("", exec_parts[13]) == 0); // ${THREEVAR-empty}
  47. assert(strcmp("empty", exec_parts[14]) == 0); // ${THREEVAR:-empty}
  48. assert(strcmp("", exec_parts[15]) == 0); // $FOURVAR
  49. assert(strcmp("", exec_parts[16]) == 0); // ${FOURVAR}
  50. assert(strcmp("", exec_parts[17]) == 0); // ${FOURVAR+empty2}
  51. assert(strcmp("", exec_parts[18]) == 0); // ${FOURVAR:+empty2}
  52. assert(strcmp("empty2", exec_parts[19]) == 0); // ${FOURVAR-empty2}
  53. assert(strcmp("empty2", exec_parts[20]) == 0); // ${FOURVAR:-empty2}
  54. }
  55. void test_env_subst2()
  56. {
  57. auto resolve_env_var = [](const std::string &name, environment::env_map const &) {
  58. if (name == "ONE_VAR") return "a";
  59. if (name == "TWOVAR") return "hellohello";
  60. return "";
  61. };
  62. std::string line = "test x$ONE_VAR-${ONE_VAR}~ y$${TWOVAR}$TWOVAR$$ONE_VAR";
  63. std::list<std::pair<unsigned,unsigned>> offsets;
  64. std::string::iterator li = line.begin();
  65. std::string::iterator le = line.end();
  66. std::string file_name = "dummy";
  67. file_pos_ref fpr { file_name, 1 };
  68. dinit_load::read_setting_value(fpr, li, le, &offsets);
  69. dinit_load::value_var_subst("command", line, offsets, resolve_env_var, &tenvmap, nullptr);
  70. assert(line == "test xa-a~ y${TWOVAR}hellohello$ONE_VAR");
  71. assert(offsets.size() == 3);
  72. assert((*std::next(offsets.begin(), 0) == std::pair<unsigned,unsigned>{0, 4}));
  73. assert((*std::next(offsets.begin(), 1) == std::pair<unsigned,unsigned>{5, 10}));
  74. assert((*std::next(offsets.begin(), 2) == std::pair<unsigned,unsigned>{11, 39}));
  75. }
  76. void test_env_subst3()
  77. {
  78. auto resolve_env_var = [](const std::string &name, environment::env_map const &) {
  79. if (name == "EMPTY") return "";
  80. if (name == "WS") return " ";
  81. if (name == "PADDED") return " p ";
  82. return "";
  83. };
  84. std::string file_name = "dummy";
  85. file_pos_ref fpr { file_name, 1 };
  86. std::string line = "test $/EMPTY foo";
  87. std::list<std::pair<unsigned,unsigned>> offsets;
  88. std::string::iterator li = line.begin();
  89. std::string::iterator le = line.end();
  90. dinit_load::read_setting_value(fpr, li, le, &offsets);
  91. dinit_load::value_var_subst("command", line, offsets, resolve_env_var, &tenvmap, nullptr);
  92. auto check_arg = [&](unsigned idx, const char *val)
  93. {
  94. auto &offs = *std::next(offsets.begin(), idx);
  95. assert(line.substr(offs.first, offs.second - offs.first) == val);
  96. };
  97. assert(line == "test foo");
  98. check_arg(1, "foo");
  99. line = "test $EMPTY foo";
  100. li = line.begin(); le = line.end(); offsets.clear();
  101. dinit_load::read_setting_value(fpr, li, le, &offsets);
  102. dinit_load::value_var_subst("command", line, offsets, resolve_env_var, &tenvmap, nullptr);
  103. assert(line == "test foo");
  104. check_arg(1, "");
  105. check_arg(2, "foo");
  106. // adjacent collapsing
  107. line = "test $/EMPTY$/EMPTY$/EMPTY foo";
  108. li = line.begin(); le = line.end(); offsets.clear();
  109. dinit_load::read_setting_value(fpr, li, le, &offsets);
  110. dinit_load::value_var_subst("command", line, offsets, resolve_env_var, &tenvmap, nullptr);
  111. assert(line == "test foo");
  112. check_arg(1, "foo");
  113. // middle empty is non-collapsing:
  114. line = "test $/EMPTY$EMPTY$/EMPTY foo";
  115. li = line.begin(); le = line.end(); offsets.clear();
  116. dinit_load::read_setting_value(fpr, li, le, &offsets);
  117. dinit_load::value_var_subst("command", line, offsets, resolve_env_var, &tenvmap, nullptr);
  118. assert(line == "test foo");
  119. check_arg(1, "");
  120. check_arg(2, "foo");
  121. // empty doesn't wordsplit:
  122. line = "test abc$/{EMPTY}def";
  123. li = line.begin(); le = line.end(); offsets.clear();
  124. dinit_load::read_setting_value(fpr, li, le, &offsets);
  125. dinit_load::value_var_subst("command", line, offsets, resolve_env_var, &tenvmap, nullptr);
  126. assert(line == "test abcdef");
  127. check_arg(1, "abcdef");
  128. // whitespace does wordsplit:
  129. line = "test abc$/{WS}def";
  130. li = line.begin(); le = line.end(); offsets.clear();
  131. dinit_load::read_setting_value(fpr, li, le, &offsets);
  132. dinit_load::value_var_subst("command", line, offsets, resolve_env_var, &tenvmap, nullptr);
  133. assert(line == "test abc def");
  134. check_arg(1, "abc");
  135. check_arg(2, "def");
  136. // internal words handled correctly:
  137. line = "test abc$/{PADDED}def";
  138. li = line.begin(); le = line.end(); offsets.clear();
  139. dinit_load::read_setting_value(fpr, li, le, &offsets);
  140. dinit_load::value_var_subst("command", line, offsets, resolve_env_var, &tenvmap, nullptr);
  141. assert(line == "test abc p def");
  142. check_arg(1, "abc");
  143. check_arg(2, "p");
  144. check_arg(3, "def");
  145. }
  146. void test_nonexistent()
  147. {
  148. bool got_service_not_found = false;
  149. dirload_service_set sset(test_service_dir.c_str());
  150. try {
  151. sset.load_service("does-not-exist");
  152. }
  153. catch (service_not_found &) {
  154. got_service_not_found = true;
  155. }
  156. assert(got_service_not_found);
  157. }
  158. // test_prelim_dep: A preliminary (unresolved) service dependency
  159. class test_prelim_dep
  160. {
  161. public:
  162. std::string name;
  163. dependency_type dep_type;
  164. test_prelim_dep(const std::string &name_p, dependency_type dep_type_p)
  165. : name(name_p), dep_type(dep_type_p) { }
  166. test_prelim_dep(std::string &&name_p, dependency_type dep_type_p)
  167. : name(std::move(name_p)), dep_type(dep_type_p) { }
  168. };
  169. void test_settings()
  170. {
  171. using string = std::string;
  172. using string_iterator = std::string::iterator;
  173. using prelim_dep = test_prelim_dep;
  174. dinit_load::service_settings_wrapper<prelim_dep> settings;
  175. std::stringstream ss;
  176. ss << "type = process\n"
  177. "command = /something/test\n"
  178. "depends-on = abc\n"
  179. "rlimit-nofile = 50:100\n"
  180. "rlimit-core = 60:\n"
  181. "rlimit-data = -:-";
  182. file_input_stack input_stack;
  183. input_stack.add_source(ss.str(), "dummy");
  184. try {
  185. process_service_file("test-service", input_stack,
  186. [&](string &line, file_pos_ref input_pos, string &setting,
  187. dinit_load::setting_op_t op, string_iterator &i,
  188. string_iterator &end) -> void {
  189. auto process_dep_dir_n = [&](std::list<prelim_dep> &deplist, const std::string &waitsford,
  190. dependency_type dep_type) -> void {
  191. //process_dep_dir(name.c_str(), service_filename, deplist, waitsford, dep_type);
  192. };
  193. auto load_service_n = [&](const string &dep_name) -> const string & {
  194. return dep_name;
  195. };
  196. try {
  197. process_service_line(settings, "test-service", nullptr, line, input_pos, setting,
  198. op, i, end, load_service_n, process_dep_dir_n);
  199. }
  200. catch (service_description_exc &exc) {
  201. //report_service_description_exc(exc);
  202. }
  203. });
  204. }
  205. catch (std::system_error &sys_err)
  206. {
  207. //report_error(sys_err, name);
  208. throw service_description_exc("", "error while reading service description.", "unknown");
  209. }
  210. assert(settings.service_type == service_type_t::PROCESS);
  211. assert(settings.command == "/something/test");
  212. assert(settings.rlimits.size() == 3);
  213. assert(settings.rlimits[0].resource_id == RLIMIT_NOFILE);
  214. assert(settings.rlimits[0].soft_set && settings.rlimits[0].hard_set);
  215. assert(settings.rlimits[0].limits.rlim_cur == 50);
  216. assert(settings.rlimits[0].limits.rlim_max == 100);
  217. assert(settings.rlimits[1].resource_id == RLIMIT_CORE);
  218. assert(settings.rlimits[1].soft_set && !settings.rlimits[1].hard_set);
  219. assert(settings.rlimits[1].limits.rlim_cur == 60);
  220. assert(settings.rlimits[2].resource_id == RLIMIT_DATA);
  221. assert(settings.rlimits[2].soft_set && settings.rlimits[2].hard_set);
  222. assert(settings.rlimits[2].limits.rlim_cur == RLIM_INFINITY);
  223. assert(settings.rlimits[2].limits.rlim_max == RLIM_INFINITY);
  224. assert(settings.depends.size() == 1);
  225. assert(settings.depends.front().dep_type == dependency_type::REGULAR);
  226. assert(settings.depends.front().name == "abc");
  227. }
  228. void test_path_env_subst()
  229. {
  230. using string = std::string;
  231. using string_iterator = std::string::iterator;
  232. using prelim_dep = test_prelim_dep;
  233. dinit_load::service_settings_wrapper<prelim_dep> settings;
  234. std::stringstream ss;
  235. ss << "type = process\n"
  236. "command = /something/test\n"
  237. "logfile = /some/$1/$username/${1}/dir\n";
  238. file_input_stack input_stack;
  239. input_stack.add_source(ss.str(), "dummy");
  240. try {
  241. process_service_file("test-service", input_stack,
  242. [&](string &line, file_pos_ref input_pos, string &setting,
  243. dinit_load::setting_op_t op, string_iterator &i, string_iterator &end) -> void {
  244. auto process_dep_dir_n = [&](std::list<prelim_dep> &deplist, const std::string &waitsford,
  245. dependency_type dep_type) -> void {
  246. //process_dep_dir(name.c_str(), service_filename, deplist, waitsford, dep_type);
  247. };
  248. auto load_service_n = [&](const string &dep_name) -> const string & {
  249. return dep_name;
  250. };
  251. try {
  252. process_service_line(settings, "test-service", nullptr, line, input_pos, setting,
  253. op, i, end, load_service_n, process_dep_dir_n);
  254. }
  255. catch (service_description_exc &exc) {
  256. //report_service_description_exc(exc);
  257. }
  258. });
  259. }
  260. catch (std::system_error &sys_err)
  261. {
  262. throw service_description_exc("", "error while reading service description.", "unknown");
  263. }
  264. auto report_error = [](const char *msg) {};
  265. auto resolve_var = [](const std::string &name, environment::env_map const &) -> const char * {
  266. if (name == "username") return "testsuccess";
  267. return nullptr;
  268. };
  269. settings.finalise(report_error, tenvmap, "foo", report_error /* lint */, resolve_var);
  270. assert(settings.service_type == service_type_t::PROCESS);
  271. assert(settings.command == "/something/test");
  272. assert(settings.logfile == "/some/foo/testsuccess/foo/dir");
  273. }
  274. void test_newline()
  275. {
  276. dirload_service_set sset(test_service_dir.c_str());
  277. bp_sys::setenv("arg", "t3", true);
  278. auto t3 = static_cast<base_process_service *>(sset.load_service("t3"));
  279. auto exec_parts = t3->get_exec_arg_parts();
  280. assert(t3->get_type() == service_type_t::PROCESS);
  281. assert(strcmp(exec_parts[0], "command1") == 0);
  282. assert(strcmp(exec_parts[1], "t3") == 0);
  283. assert(strcmp(exec_parts[2], "arg1") == 0);
  284. assert(strcmp(exec_parts[3], "command2") == 0);
  285. assert(strcmp(exec_parts[4], "t3") == 0);
  286. assert(strcmp(exec_parts[5], "arg2") == 0);
  287. assert(strcmp(exec_parts[6], "command3") == 0);
  288. assert(strcmp(exec_parts[7], "t3") == 0);
  289. assert(strcmp(exec_parts[8], "arg3") == 0);
  290. }
  291. void test_newline_err()
  292. {
  293. using string = std::string;
  294. using string_iterator = std::string::iterator;
  295. using sstream = std::stringstream;
  296. using prelim_dep = test_prelim_dep;
  297. unsigned errcount = 0;
  298. auto test_inner = [&](std::stringstream &ss)
  299. {
  300. dinit_load::service_settings_wrapper<prelim_dep> settings;
  301. file_input_stack input_stack;
  302. input_stack.add_source(ss.str(), "dummy");
  303. process_service_file("test-service", input_stack,
  304. [&](string &line, file_pos_ref input_pos, string &setting,
  305. dinit_load::setting_op_t op, string_iterator &i, string_iterator &end) -> void {
  306. auto process_dep_dir_n = [&](std::list<prelim_dep> &deplist, const std::string &waitsford,
  307. dependency_type dep_type) -> void {
  308. //process_dep_dir(name.c_str(), service_filename, deplist, waitsford, dep_type);
  309. };
  310. auto load_service_n = [&](const string &dep_name) -> const string & {
  311. return dep_name;
  312. };
  313. process_service_line(settings, "test-service", nullptr, line, input_pos, setting,
  314. op, i, end, load_service_n, process_dep_dir_n);
  315. });
  316. };
  317. try {
  318. sstream ss;
  319. ss << "type = process\n"
  320. "command = /something/test\\\n"
  321. " # comment with leading space\\\n"
  322. "# comment without leading space";
  323. test_inner(ss);
  324. }
  325. catch (service_description_exc &exc)
  326. {
  327. if (exc.input_pos.get_line_num() == 4) ++errcount;
  328. }
  329. try {
  330. sstream ss;
  331. ss << "type = process\n"
  332. "command = EOF\\";
  333. test_inner(ss);
  334. }
  335. catch (service_description_exc &exc)
  336. {
  337. if (exc.input_pos.get_line_num() == 2) ++errcount;
  338. }
  339. assert(errcount == 2);
  340. }
  341. void test_newline2()
  342. {
  343. using string = std::string;
  344. using string_iterator = std::string::iterator;
  345. using prelim_dep = test_prelim_dep;
  346. dinit_load::service_settings_wrapper<prelim_dep> settings;
  347. std::stringstream ss;
  348. ss << "type = process\n"
  349. "command = /something/test\\\\\n" // even number of backslashes
  350. "stop-command = /something/stop\\\\\\\n" // odd number (3) backslashes
  351. " next line\n";
  352. file_input_stack input_stack;
  353. input_stack.add_source(ss.str(), "dummy");
  354. try {
  355. process_service_file("test-service", input_stack,
  356. [&](string &line, file_pos_ref input_pos, string &setting,
  357. dinit_load::setting_op_t op, string_iterator &i,
  358. string_iterator &end) -> void {
  359. auto process_dep_dir_n = [&](std::list<prelim_dep> &deplist, const std::string &waitsford,
  360. dependency_type dep_type) -> void {
  361. //process_dep_dir(name.c_str(), service_filename, deplist, waitsford, dep_type);
  362. };
  363. auto load_service_n = [&](const string &dep_name) -> const string & {
  364. return dep_name;
  365. };
  366. try {
  367. process_service_line(settings, "test-service", nullptr, line, input_pos,
  368. setting, op, i, end, load_service_n, process_dep_dir_n);
  369. }
  370. catch (service_description_exc &exc) {
  371. //report_service_description_exc(exc);
  372. }
  373. });
  374. }
  375. catch (std::system_error &sys_err)
  376. {
  377. //report_error(sys_err, name);
  378. throw service_description_exc("", "error while reading service description.", "unknown");
  379. }
  380. assert(settings.command == "/something/test\\");
  381. assert(settings.stop_command == "/something/stop\\ next line");
  382. }
  383. void test_comments()
  384. {
  385. std::string file_name = "dummy";
  386. file_pos_ref input_pos { file_name, 1 };
  387. std::string line = "one two three # comment";
  388. std::list<std::pair<unsigned,unsigned>> offsets;
  389. std::string::iterator li = line.begin();
  390. std::string::iterator le = line.end();
  391. std::string val = dinit_load::read_setting_value(input_pos, li, le, &offsets);
  392. assert(val == "one two three");
  393. assert(offsets.size() == 3);
  394. auto i = offsets.begin();
  395. assert(i->first == 0);
  396. assert(i->second == 3);
  397. ++i;
  398. assert(i->first == 4);
  399. assert(i->second == 7);
  400. ++i;
  401. assert(i->first == 8);
  402. assert(i->second == 13);
  403. }
  404. void test_plusassign()
  405. {
  406. std::string file_name = "dummy";
  407. file_pos_ref input_pos { file_name, 1 };
  408. std::string setting_val;
  409. std::list<std::pair<unsigned,unsigned>> part_positions;
  410. std::string primary = "echo one";
  411. std::string::iterator i = primary.begin();
  412. std::string::iterator e = primary.end();
  413. dinit_load::read_setting_value(setting_val, dinit_load::setting_op_t::ASSIGN, input_pos, i, e, &part_positions);
  414. assert(setting_val == "echo one");
  415. assert(part_positions.size() == 2);
  416. std::string altered = "echo two";
  417. i = altered.begin();
  418. e = altered.end();
  419. dinit_load::read_setting_value(setting_val, dinit_load::setting_op_t::ASSIGN, input_pos, i, e, &part_positions);
  420. assert(setting_val == "echo two");
  421. assert(part_positions.size() == 2);
  422. std::string addendum = "three";
  423. i = addendum.begin();
  424. e = addendum.end();
  425. dinit_load::read_setting_value(setting_val, dinit_load::setting_op_t::PLUSASSIGN, input_pos, i, e, &part_positions);
  426. assert(setting_val == "echo two three");
  427. assert(part_positions.size() == 3);
  428. auto ppi = part_positions.begin();
  429. ++ppi; ++ppi;
  430. assert(ppi->first == 9);
  431. assert(ppi->second == 14);
  432. std::string s2 = "echo \"space \"";
  433. i = s2.begin();
  434. e = s2.end();
  435. dinit_load::read_setting_value(setting_val, dinit_load::setting_op_t::ASSIGN, input_pos, i, e, &part_positions);
  436. assert(setting_val == "echo space ");
  437. i = addendum.begin();
  438. e = addendum.end();
  439. dinit_load::read_setting_value(setting_val, dinit_load::setting_op_t::PLUSASSIGN, input_pos, i, e, &part_positions);
  440. assert(setting_val == "echo space three");
  441. assert(part_positions.size() == 3);
  442. ppi = part_positions.begin();
  443. ++ppi;
  444. assert(ppi->first == 5);
  445. assert(ppi->second == 11);
  446. ++ppi;
  447. assert(ppi->first == 12);
  448. assert(ppi->second == 17);
  449. }
  450. #define RUN_TEST(name, spacing) \
  451. std::cout << #name "..." spacing << std::flush; \
  452. name(); \
  453. std::cout << "PASSED" << std::endl;
  454. int main(int argc, char **argv)
  455. {
  456. init_test_service_dir();
  457. RUN_TEST(test_basic, " ");
  458. RUN_TEST(test_env_subst, " ");
  459. RUN_TEST(test_env_subst2, " ");
  460. RUN_TEST(test_env_subst3, " ");
  461. RUN_TEST(test_nonexistent, " ");
  462. RUN_TEST(test_settings, " ");
  463. RUN_TEST(test_path_env_subst, " ");
  464. RUN_TEST(test_newline, " ");
  465. RUN_TEST(test_newline_err, " ");
  466. RUN_TEST(test_newline2, " ");
  467. RUN_TEST(test_comments, " ");
  468. RUN_TEST(test_plusassign, " ");
  469. bp_sys::clearenv();
  470. return 0;
  471. }