hmain.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. #include <signal.h> // for signal,kill...
  2. #include "h.h"
  3. #include "hsysinfo.h"
  4. #include "hmain.h"
  5. main_ctx_t g_main_ctx;
  6. typedef struct conf_ctx_s {
  7. IniParser* parser;
  8. int loglevel;
  9. int worker_processes;
  10. int port;
  11. } conf_ctx_t;
  12. conf_ctx_t g_conf_ctx;
  13. #define DEFAULT_WORKER_PROCESSES 4
  14. #define MAXNUM_WORKER_PROCESSES 1024
  15. inline void conf_ctx_init(conf_ctx_t* ctx) {
  16. ctx->parser = new IniParser;
  17. ctx->loglevel = LOG_LEVEL_DEBUG;
  18. ctx->worker_processes = 0;
  19. ctx->port = 0;
  20. }
  21. static int create_pidfile();
  22. static void delete_pidfile();
  23. static pid_t getpid_from_pidfile();
  24. static int parse_cmdline(int argc, char** argv);
  25. static void print_version();
  26. static void print_help();
  27. static void handle_signal();
  28. static int parse_confile(const char* confile);
  29. static int master_process_init();
  30. static void master_process_exit();
  31. static int master_process_cycle();
  32. static int create_worker_processes(int worker_processes);
  33. static int worker_process_cycle(void* ctx);
  34. int create_pidfile() {
  35. FILE* fp = fopen(g_main_ctx.pidfile, "w");
  36. if (fp == NULL) {
  37. printf("fopen [%s] error: %d\n", g_main_ctx.pidfile, errno);
  38. return -10;
  39. }
  40. char pid[16] = {0};
  41. snprintf(pid, sizeof(pid), "%d\n", g_main_ctx.pid);
  42. fwrite(pid, 1, strlen(pid), fp);
  43. fclose(fp); atexit(delete_pidfile);
  44. hlogi("create_pidfile [%s] pid=%d", g_main_ctx.pidfile, g_main_ctx.pid);
  45. return 0;
  46. }
  47. void delete_pidfile() {
  48. remove(g_main_ctx.pidfile);
  49. hlogi("delete_pidfile [%s]", g_main_ctx.pidfile);
  50. }
  51. pid_t getpid_from_pidfile() {
  52. FILE* fp = fopen(g_main_ctx.pidfile, "r");
  53. if (fp == NULL) {
  54. //printf("fopen [%s] error: %d\n", g_conf_ctx.pidfile, errno);
  55. return -1;
  56. }
  57. char pid[64];
  58. int readbytes = fread(pid, 1, sizeof(pid), fp);
  59. fclose(fp);
  60. if (readbytes <= 0) {
  61. printf("fread [%s] bytes=%d\n", g_main_ctx.pidfile, readbytes);
  62. return -1;
  63. }
  64. return atoi(pid);
  65. }
  66. int main_ctx_init(int argc, char** argv) {
  67. g_main_ctx.pid = getpid();
  68. char* cwd = getcwd(g_main_ctx.run_path, sizeof(g_main_ctx.run_path));
  69. if (cwd == NULL) {
  70. printf("getcwd error\n");
  71. }
  72. //printf("run_path=%s\n", g_main_ctx.run_path);
  73. const char* b = argv[0];
  74. const char* e = b;
  75. while (*e) ++e;
  76. --e;
  77. while (e >= b) {
  78. if (*e == '/' || *e == '\\') {
  79. break;
  80. }
  81. --e;
  82. }
  83. strncpy(g_main_ctx.program_name, e+1, sizeof(g_main_ctx.program_name));
  84. #ifdef _WIN32
  85. if (strcmp(g_main_ctx.program_name+strlen(g_main_ctx.program_name)-4, ".exe") == 0) {
  86. *(g_main_ctx.program_name+strlen(g_main_ctx.program_name)-4) = '\0';
  87. }
  88. #endif
  89. //printf("program_name=%s\n", g_main_ctx.program_name);
  90. // save arg
  91. int i = 0;
  92. g_main_ctx.os_argv = argv;
  93. g_main_ctx.argc = 0;
  94. g_main_ctx.arg_len = 0;
  95. for (i = 0; argv[i]; ++i) {
  96. g_main_ctx.arg_len += strlen(argv[i]) + 1;
  97. }
  98. g_main_ctx.argc = i;
  99. char* argp = (char*)malloc(g_main_ctx.arg_len);
  100. memset(argp, 0, g_main_ctx.arg_len);
  101. g_main_ctx.save_argv = (char**)malloc((g_main_ctx.argc+1) * sizeof(char*));
  102. for (i = 0; argv[i]; ++i) {
  103. g_main_ctx.save_argv[i] = argp;
  104. strcpy(g_main_ctx.save_argv[i], argv[i]);
  105. argp += strlen(argv[i]) + 1;
  106. }
  107. g_main_ctx.save_argv[g_main_ctx.argc] = NULL;
  108. // save env
  109. g_main_ctx.os_envp = environ;
  110. g_main_ctx.envc = 0;
  111. g_main_ctx.env_len = 0;
  112. for (i = 0; environ[i]; ++i) {
  113. g_main_ctx.env_len += strlen(environ[i]) + 1;
  114. }
  115. g_main_ctx.envc = i;
  116. char* envp = (char*)malloc(g_main_ctx.env_len);
  117. memset(envp, 0, g_main_ctx.env_len);
  118. g_main_ctx.save_envp = (char**)malloc((g_main_ctx.envc+1) * sizeof(char*));
  119. for (i = 0; environ[i]; ++i) {
  120. g_main_ctx.save_envp[i] = envp;
  121. strcpy(g_main_ctx.save_envp[i], environ[i]);
  122. envp += strlen(environ[i]) + 1;
  123. }
  124. g_main_ctx.save_envp[g_main_ctx.envc] = NULL;
  125. // parse env
  126. for (i = 0; environ[i]; ++i) {
  127. char* b = environ[i];
  128. char* delim = strchr(b, '=');
  129. if (delim == NULL) {
  130. continue;
  131. }
  132. g_main_ctx.env_kv[std::string(b, delim-b)] = std::string(delim+1);
  133. }
  134. /*
  135. // print argv and envp
  136. printf("---------------arg------------------------------\n");
  137. for (auto& pair : g_main_ctx.arg_kv) {
  138. printf("%s=%s\n", pair.first.c_str(), pair.second.c_str());
  139. }
  140. printf("---------------env------------------------------\n");
  141. for (auto& pair : g_main_ctx.env_kv) {
  142. printf("%s=%s\n", pair.first.c_str(), pair.second.c_str());
  143. }
  144. printf("PWD=%s\n", get_env("PWD"));
  145. printf("USER=%s\n", get_env("USER"));
  146. printf("HOME=%s\n", get_env("HOME"));
  147. printf("LANG=%s\n", get_env("LANG"));
  148. printf("TERM=%s\n", get_env("TERM"));
  149. printf("SHELL=%s\n", get_env("SHELL"));
  150. printf("================================================\n");
  151. */
  152. char logpath[MAX_PATH] = {0};
  153. snprintf(logpath, sizeof(logpath), "%s/logs", g_main_ctx.run_path);
  154. MKDIR(logpath);
  155. snprintf(g_main_ctx.confile, sizeof(g_main_ctx.confile), "%s/etc/%s.conf", g_main_ctx.run_path, g_main_ctx.program_name);
  156. snprintf(g_main_ctx.pidfile, sizeof(g_main_ctx.pidfile), "%s/logs/%s.pid", g_main_ctx.run_path, g_main_ctx.program_name);
  157. snprintf(g_main_ctx.logfile, sizeof(g_main_ctx.confile), "%s/logs/%s.log", g_main_ctx.run_path, g_main_ctx.program_name);
  158. g_main_ctx.oldpid = getpid_from_pidfile();
  159. #ifdef __unix__
  160. if (kill(g_main_ctx.oldpid, 0) == -1 && errno == ESRCH) {
  161. g_main_ctx.oldpid = -1;
  162. }
  163. #else
  164. #endif
  165. return 0;
  166. }
  167. const char* get_arg(const char* key) {
  168. auto iter = g_main_ctx.arg_kv.find(key);
  169. if (iter == g_main_ctx.arg_kv.end()) {
  170. return NULL;
  171. }
  172. return iter->second.c_str();
  173. }
  174. const char* get_env(const char* key) {
  175. auto iter = g_main_ctx.env_kv.find(key);
  176. if (iter == g_main_ctx.env_kv.end()) {
  177. return NULL;
  178. }
  179. return iter->second.c_str();
  180. }
  181. #ifdef __unix__
  182. /*
  183. * memory layout
  184. * argv[0]\0argv[1]\0argv[n]\0env[0]\0env[1]\0env[n]\0
  185. */
  186. void setproctitle(const char* title) {
  187. //printf("proctitle=%s\n", title);
  188. memset(g_main_ctx.os_argv[0], 0, g_main_ctx.arg_len + g_main_ctx.env_len);
  189. strncpy(g_main_ctx.os_argv[0], title, g_main_ctx.arg_len + g_main_ctx.env_len);
  190. }
  191. #endif
  192. // unix short style
  193. static char options[] = "hvc:ts:dp:";
  194. static char detail_options[] = "\
  195. -h : print help\n\
  196. -v : print version\n\
  197. -c confile : set configure file, default etc/${program}.conf\n\
  198. -t : test configure file and exit\n\
  199. -s signal : send signal to process\n\
  200. signal=[start, stop, restart, status]\n\
  201. -d : daemon\n\
  202. -p port : set listen port\n\
  203. ";
  204. void print_version() {
  205. printf("%s version %s\n", g_main_ctx.program_name, get_compile_version());
  206. }
  207. void print_help() {
  208. printf("Usage: %s [%s]\n", g_main_ctx.program_name, options);
  209. printf("Options:\n%s\n", detail_options);
  210. }
  211. #define INVALID_OPTION -1
  212. #define FLAG_OPTION 1
  213. #define PARMA_OPTION 2
  214. int get_option(char opt) {
  215. char* p = options;
  216. while (*p && *p != opt) ++p;
  217. if (*p == '\0') return INVALID_OPTION;
  218. if (*(p+1) == ':') return PARMA_OPTION;
  219. return FLAG_OPTION;
  220. }
  221. int parse_cmdline(int argc, char** argv) {
  222. int i = 1;
  223. while (argv[i]) {
  224. char* p = argv[i];
  225. if (*p != '-') {
  226. printf("Invalid argv[%d]: %s\n", i, argv[i]);
  227. exit(-10);
  228. }
  229. while (*++p) {
  230. switch (get_option(*p)) {
  231. case INVALID_OPTION:
  232. printf("Invalid option: '%c'\n", *p);
  233. exit(-20);
  234. case FLAG_OPTION:
  235. g_main_ctx.arg_kv[std::string(p, 1)] = "true";
  236. break;
  237. case PARMA_OPTION:
  238. if (*(p+1) != '\0') {
  239. g_main_ctx.arg_kv[std::string(p, 1)] = p+1;
  240. ++i;
  241. goto next_option;
  242. } else if (argv[i+1] != NULL) {
  243. g_main_ctx.arg_kv[std::string(p, 1)] = argv[i+1];
  244. i += 2;
  245. goto next_option;
  246. } else {
  247. printf("Option '%c' requires param\n", *p);
  248. exit(-30);
  249. }
  250. }
  251. }
  252. ++i;
  253. next_option:
  254. continue;
  255. }
  256. return 0;
  257. }
  258. int parse_confile(const char* confile) {
  259. conf_ctx_init(&g_conf_ctx);
  260. int ret = g_conf_ctx.parser->LoadFromFile(confile);
  261. if (ret != 0) {
  262. printf("Load confile [%s] failed: %d\n", confile, ret);
  263. exit(-40);
  264. }
  265. // loglevel
  266. const char* szLoglevel = g_conf_ctx.parser->GetValue("loglevel").c_str();
  267. if (stricmp(szLoglevel, "DEBUG") == 0) {
  268. g_conf_ctx.loglevel = LOG_LEVEL_DEBUG;
  269. } else if (stricmp(szLoglevel, "INFO") == 0) {
  270. g_conf_ctx.loglevel = LOG_LEVEL_INFO;
  271. } else if (stricmp(szLoglevel, "WARN") == 0) {
  272. g_conf_ctx.loglevel = LOG_LEVEL_WARN;
  273. } else if (stricmp(szLoglevel, "ERROR") == 0) {
  274. g_conf_ctx.loglevel = LOG_LEVEL_ERROR;
  275. } else {
  276. g_conf_ctx.loglevel = LOG_LEVEL_DEBUG;
  277. }
  278. hlog_set_level(g_conf_ctx.loglevel);
  279. // worker_processes
  280. int worker_processes = 0;
  281. worker_processes = atoi(g_conf_ctx.parser->GetValue("worker_processes").c_str());
  282. if (worker_processes <= 0 || worker_processes > MAXNUM_WORKER_PROCESSES) {
  283. worker_processes = get_ncpu();
  284. hlogd("worker_processes=ncpu=%d", worker_processes);
  285. }
  286. if (worker_processes <= 0 || worker_processes > MAXNUM_WORKER_PROCESSES) {
  287. worker_processes = DEFAULT_WORKER_PROCESSES;
  288. }
  289. g_conf_ctx.worker_processes = worker_processes;
  290. // port
  291. int port = 0;
  292. port = atoi(g_conf_ctx.parser->GetValue("port").c_str());
  293. if (port == 0) {
  294. port = atoi(get_arg("p"));
  295. }
  296. if (port == 0) {
  297. printf("Please config listen port!\n");
  298. exit(-10);
  299. }
  300. g_conf_ctx.port = port;
  301. return 0;
  302. }
  303. int master_process_cycle() {
  304. while (1) msleep(1);
  305. return 0;
  306. }
  307. int worker_process_cycle(void* ctx) {
  308. while (1) msleep(1);
  309. return 0;
  310. }
  311. #ifdef __unix__
  312. // unix use signal
  313. // unix use multi-processes
  314. // we use SIGTERM to quit process
  315. #define SIGNAL_TERMINATE SIGTERM
  316. #include <sys/wait.h>
  317. static pid_t s_worker_processes[MAXNUM_WORKER_PROCESSES];
  318. int create_worker_processes(int worker_processes) {
  319. for (int i = 0; i < worker_processes; ++i) {
  320. pid_t pid = fork();
  321. if (pid < 0) {
  322. hloge("fork error: %d", errno);
  323. return errno;
  324. }
  325. if (pid == 0) {
  326. hlogi("worker process start/running, pid=%d", getpid());
  327. char proctitle[256] = {0};
  328. snprintf(proctitle, sizeof(proctitle), "%s: worker process", g_main_ctx.program_name);
  329. setproctitle(proctitle);
  330. long port = g_conf_ctx.port + i + 1;
  331. worker_process_cycle((void*)port);
  332. exit(0);
  333. }
  334. for (int i = 0; i < MAXNUM_WORKER_PROCESSES; ++i) {
  335. if (s_worker_processes[i] <= 0) {
  336. s_worker_processes[i] = pid;
  337. break;
  338. }
  339. }
  340. }
  341. return 0;
  342. }
  343. void master_process_signal_handler(int signo) {
  344. hlogi("pid=%d recv signo=%d", getpid(), signo);
  345. switch (signo) {
  346. case SIGINT:
  347. case SIGNAL_TERMINATE:
  348. hlogi("killall worker processes");
  349. signal(SIGCHLD, SIG_IGN);
  350. for (int i = 0; i < MAXNUM_WORKER_PROCESSES; ++i) {
  351. if (s_worker_processes[i] <= 0) break;
  352. kill(s_worker_processes[i], SIGKILL);
  353. s_worker_processes[i] = -1;
  354. }
  355. exit(0);
  356. break;
  357. case SIGCHLD:
  358. {
  359. pid_t pid = 0;
  360. int status = 0;
  361. while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
  362. hlogw("worker process stop/waiting, pid=%d status=%d", pid, status);
  363. for (int i = 0; i < MAXNUM_WORKER_PROCESSES; ++i) {
  364. if (s_worker_processes[i] == pid) {
  365. s_worker_processes[i] = -1;
  366. break;
  367. }
  368. }
  369. create_worker_processes(1);
  370. }
  371. }
  372. break;
  373. default:
  374. break;
  375. }
  376. }
  377. int master_process_init() {
  378. char proctitle[256] = {0};
  379. snprintf(proctitle, sizeof(proctitle), "%s: master process", g_main_ctx.program_name);
  380. setproctitle(proctitle);
  381. signal(SIGINT, master_process_signal_handler);
  382. signal(SIGCHLD, master_process_signal_handler);
  383. signal(SIGNAL_TERMINATE, master_process_signal_handler);
  384. for (int i = 0; i < MAXNUM_WORKER_PROCESSES; ++i) {
  385. s_worker_processes[i] = -1;
  386. }
  387. atexit(master_process_exit);
  388. return 0;
  389. }
  390. void master_process_exit() {
  391. }
  392. #elif defined(_WIN32)
  393. // win32 use Event
  394. // win32 use multi-threads
  395. static HANDLE s_hEventTerm = NULL;
  396. #include <mmsystem.h>
  397. #ifdef _MSC_VER
  398. #pragma comment(lib, "winmm.lib")
  399. #endif
  400. void WINAPI on_timer(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) {
  401. DWORD ret = WaitForSingleObject(s_hEventTerm, 0);
  402. if (ret == WAIT_OBJECT_0) {
  403. timeKillEvent(uTimerID);
  404. hlogi("pid=%d recv event [TERM]", getpid());
  405. exit(0);
  406. }
  407. }
  408. int master_process_init() {
  409. char eventname[MAX_PATH] = {0};
  410. snprintf(eventname, sizeof(eventname), "%s_term_event", g_main_ctx.program_name);
  411. s_hEventTerm = CreateEvent(NULL, FALSE, FALSE, eventname);
  412. //s_hEventTerm = OpenEvent(EVENT_ALL_ACCESS, FALSE, eventname);
  413. timeSetEvent(1000, 1000, on_timer, 0, TIME_PERIODIC);
  414. atexit(master_process_exit);
  415. return 0;
  416. }
  417. void master_process_exit() {
  418. CloseHandle(s_hEventTerm);
  419. s_hEventTerm = NULL;
  420. }
  421. #include <process.h>
  422. void thread_proc(void* ctx) {
  423. hlogi("worker thread start/running, tid=%d", gettid());
  424. worker_process_cycle(ctx);
  425. }
  426. int create_worker_processes(int worker_processes) {
  427. for (int i = 0; i < worker_processes; ++i) {
  428. long port = g_conf_ctx.port + i + 1;
  429. _beginthread(thread_proc, 0, (void*)port);
  430. }
  431. return 0;
  432. }
  433. #endif
  434. void handle_signal() {
  435. const char* signal = get_arg("s");
  436. if (signal) {
  437. if (strcmp(signal, "start") == 0) {
  438. if (g_main_ctx.oldpid > 0) {
  439. printf("%s is already running, pid=%d\n", g_main_ctx.program_name, g_main_ctx.oldpid);
  440. exit(0);
  441. }
  442. } else if (strcmp(signal, "stop") == 0) {
  443. if (g_main_ctx.oldpid > 0) {
  444. #ifdef __unix__
  445. kill(g_main_ctx.oldpid, SIGNAL_TERMINATE);
  446. #else
  447. SetEvent(s_hEventTerm);
  448. #endif
  449. printf("%s stop/waiting\n", g_main_ctx.program_name);
  450. } else {
  451. printf("%s is already stopped\n", g_main_ctx.program_name);
  452. }
  453. exit(0);
  454. } else if (strcmp(signal, "restart") == 0) {
  455. if (g_main_ctx.oldpid > 0) {
  456. #ifdef __unix__
  457. kill(g_main_ctx.oldpid, SIGNAL_TERMINATE);
  458. #else
  459. SetEvent(s_hEventTerm);
  460. #endif
  461. printf("%s stop/waiting\n", g_main_ctx.program_name);
  462. msleep(1000);
  463. }
  464. } else if (strcmp(signal, "status") == 0) {
  465. if (g_main_ctx.oldpid > 0) {
  466. printf("%s start/running, pid=%d\n", g_main_ctx.program_name, g_main_ctx.oldpid);
  467. } else {
  468. printf("%s stop/waiting\n", g_main_ctx.program_name);
  469. }
  470. exit(0);
  471. } else {
  472. printf("Invalid signal: '%s'\n", signal);
  473. exit(0);
  474. }
  475. printf("%s start/running\n", g_main_ctx.program_name);
  476. }
  477. }
  478. int main(int argc, char** argv) {
  479. // g_main_ctx
  480. main_ctx_init(argc, argv);
  481. parse_cmdline(argc, argv);
  482. // help
  483. if (get_arg("h")) {
  484. print_help();
  485. exit(0);
  486. }
  487. // version
  488. if (get_arg("v")) {
  489. print_version();
  490. exit(0);
  491. }
  492. // logfile
  493. hlog_set_file(g_main_ctx.logfile);
  494. hlogi("%s version: %s", g_main_ctx.program_name, get_compile_version());
  495. // confile
  496. const char* confile = get_arg("c");
  497. if (confile) {
  498. strncpy(g_main_ctx.confile, confile, sizeof(g_main_ctx.confile));
  499. }
  500. // g_conf_ctx
  501. parse_confile(g_main_ctx.confile);
  502. // test
  503. if (get_arg("t")) {
  504. printf("Test confile [%s] OK!\n", g_main_ctx.confile);
  505. exit(0);
  506. }
  507. master_process_init();
  508. // signal
  509. handle_signal();
  510. #ifdef __unix__
  511. // daemon
  512. if (get_arg("d")) {
  513. // nochdir, noclose
  514. int ret = daemon(1, 1);
  515. if (ret != 0) {
  516. printf("daemon error: %d\n", ret);
  517. exit(-10);
  518. }
  519. // parent process exit after daemon, so pid changed.
  520. g_main_ctx.pid = getpid();
  521. }
  522. #endif
  523. // pidfile
  524. create_pidfile();
  525. hlogi("%s start/running, pid=%d", g_main_ctx.program_name, g_main_ctx.pid);
  526. // cycle
  527. create_worker_processes(g_conf_ctx.worker_processes);
  528. master_process_cycle();
  529. return 0;
  530. }