1
0

hmain.cpp.tmpl 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  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. const char* szPort = get_arg("p");
  293. if (szPort) {
  294. port = atoi(szPort);
  295. }
  296. if (port == 0) {
  297. port = atoi(g_conf_ctx.parser->GetValue("port").c_str());
  298. }
  299. if (port == 0) {
  300. printf("Please config listen port!\n");
  301. exit(-10);
  302. }
  303. g_conf_ctx.port = port;
  304. return 0;
  305. }
  306. int master_process_cycle() {
  307. while (1) msleep(1);
  308. return 0;
  309. }
  310. int worker_process_cycle(void* ctx) {
  311. while (1) msleep(1);
  312. return 0;
  313. }
  314. #ifdef __unix__
  315. // unix use signal
  316. // unix use multi-processes
  317. // we use SIGTERM to quit process
  318. #define SIGNAL_TERMINATE SIGTERM
  319. #include <sys/wait.h>
  320. static pid_t s_worker_processes[MAXNUM_WORKER_PROCESSES];
  321. int create_worker_processes(int worker_processes) {
  322. for (int i = 0; i < worker_processes; ++i) {
  323. pid_t pid = fork();
  324. if (pid < 0) {
  325. hloge("fork error: %d", errno);
  326. return errno;
  327. }
  328. if (pid == 0) {
  329. hlogi("worker process start/running, pid=%d", getpid());
  330. char proctitle[256] = {0};
  331. snprintf(proctitle, sizeof(proctitle), "%s: worker process", g_main_ctx.program_name);
  332. setproctitle(proctitle);
  333. long port = g_conf_ctx.port + i + 1;
  334. worker_process_cycle((void*)port);
  335. exit(0);
  336. }
  337. for (int i = 0; i < MAXNUM_WORKER_PROCESSES; ++i) {
  338. if (s_worker_processes[i] <= 0) {
  339. s_worker_processes[i] = pid;
  340. break;
  341. }
  342. }
  343. }
  344. return 0;
  345. }
  346. void master_process_signal_handler(int signo) {
  347. hlogi("pid=%d recv signo=%d", getpid(), signo);
  348. switch (signo) {
  349. case SIGINT:
  350. case SIGNAL_TERMINATE:
  351. hlogi("killall worker processes");
  352. signal(SIGCHLD, SIG_IGN);
  353. for (int i = 0; i < MAXNUM_WORKER_PROCESSES; ++i) {
  354. if (s_worker_processes[i] <= 0) break;
  355. kill(s_worker_processes[i], SIGKILL);
  356. s_worker_processes[i] = -1;
  357. }
  358. exit(0);
  359. break;
  360. case SIGCHLD:
  361. {
  362. pid_t pid = 0;
  363. int status = 0;
  364. while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
  365. hlogw("worker process stop/waiting, pid=%d status=%d", pid, status);
  366. for (int i = 0; i < MAXNUM_WORKER_PROCESSES; ++i) {
  367. if (s_worker_processes[i] == pid) {
  368. s_worker_processes[i] = -1;
  369. break;
  370. }
  371. }
  372. create_worker_processes(1);
  373. }
  374. }
  375. break;
  376. default:
  377. break;
  378. }
  379. }
  380. int master_process_init() {
  381. char proctitle[256] = {0};
  382. snprintf(proctitle, sizeof(proctitle), "%s: master process", g_main_ctx.program_name);
  383. setproctitle(proctitle);
  384. signal(SIGINT, master_process_signal_handler);
  385. signal(SIGCHLD, master_process_signal_handler);
  386. signal(SIGNAL_TERMINATE, master_process_signal_handler);
  387. for (int i = 0; i < MAXNUM_WORKER_PROCESSES; ++i) {
  388. s_worker_processes[i] = -1;
  389. }
  390. atexit(master_process_exit);
  391. return 0;
  392. }
  393. void master_process_exit() {
  394. }
  395. #elif defined(_WIN32)
  396. // win32 use Event
  397. // win32 use multi-threads
  398. static HANDLE s_hEventTerm = NULL;
  399. #include <mmsystem.h>
  400. #ifdef _MSC_VER
  401. #pragma comment(lib, "winmm.lib")
  402. #endif
  403. void WINAPI on_timer(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) {
  404. DWORD ret = WaitForSingleObject(s_hEventTerm, 0);
  405. if (ret == WAIT_OBJECT_0) {
  406. timeKillEvent(uTimerID);
  407. hlogi("pid=%d recv event [TERM]", getpid());
  408. exit(0);
  409. }
  410. }
  411. int master_process_init() {
  412. char eventname[MAX_PATH] = {0};
  413. snprintf(eventname, sizeof(eventname), "%s_term_event", g_main_ctx.program_name);
  414. s_hEventTerm = CreateEvent(NULL, FALSE, FALSE, eventname);
  415. //s_hEventTerm = OpenEvent(EVENT_ALL_ACCESS, FALSE, eventname);
  416. timeSetEvent(1000, 1000, on_timer, 0, TIME_PERIODIC);
  417. atexit(master_process_exit);
  418. return 0;
  419. }
  420. void master_process_exit() {
  421. CloseHandle(s_hEventTerm);
  422. s_hEventTerm = NULL;
  423. }
  424. #include <process.h>
  425. void thread_proc(void* ctx) {
  426. hlogi("worker thread start/running, tid=%d", gettid());
  427. worker_process_cycle(ctx);
  428. }
  429. int create_worker_processes(int worker_processes) {
  430. for (int i = 0; i < worker_processes; ++i) {
  431. long port = g_conf_ctx.port + i + 1;
  432. _beginthread(thread_proc, 0, (void*)port);
  433. }
  434. return 0;
  435. }
  436. #endif
  437. void handle_signal() {
  438. const char* signal = get_arg("s");
  439. if (signal) {
  440. if (strcmp(signal, "start") == 0) {
  441. if (g_main_ctx.oldpid > 0) {
  442. printf("%s is already running, pid=%d\n", g_main_ctx.program_name, g_main_ctx.oldpid);
  443. exit(0);
  444. }
  445. } else if (strcmp(signal, "stop") == 0) {
  446. if (g_main_ctx.oldpid > 0) {
  447. #ifdef __unix__
  448. kill(g_main_ctx.oldpid, SIGNAL_TERMINATE);
  449. #else
  450. SetEvent(s_hEventTerm);
  451. #endif
  452. printf("%s stop/waiting\n", g_main_ctx.program_name);
  453. } else {
  454. printf("%s is already stopped\n", g_main_ctx.program_name);
  455. }
  456. exit(0);
  457. } else if (strcmp(signal, "restart") == 0) {
  458. if (g_main_ctx.oldpid > 0) {
  459. #ifdef __unix__
  460. kill(g_main_ctx.oldpid, SIGNAL_TERMINATE);
  461. #else
  462. SetEvent(s_hEventTerm);
  463. #endif
  464. printf("%s stop/waiting\n", g_main_ctx.program_name);
  465. msleep(1000);
  466. }
  467. } else if (strcmp(signal, "status") == 0) {
  468. if (g_main_ctx.oldpid > 0) {
  469. printf("%s start/running, pid=%d\n", g_main_ctx.program_name, g_main_ctx.oldpid);
  470. } else {
  471. printf("%s stop/waiting\n", g_main_ctx.program_name);
  472. }
  473. exit(0);
  474. } else {
  475. printf("Invalid signal: '%s'\n", signal);
  476. exit(0);
  477. }
  478. printf("%s start/running\n", g_main_ctx.program_name);
  479. }
  480. }
  481. int main(int argc, char** argv) {
  482. // g_main_ctx
  483. main_ctx_init(argc, argv);
  484. parse_cmdline(argc, argv);
  485. // help
  486. if (get_arg("h")) {
  487. print_help();
  488. exit(0);
  489. }
  490. // version
  491. if (get_arg("v")) {
  492. print_version();
  493. exit(0);
  494. }
  495. // logfile
  496. hlog_set_file(g_main_ctx.logfile);
  497. hlogi("%s version: %s", g_main_ctx.program_name, get_compile_version());
  498. // confile
  499. const char* confile = get_arg("c");
  500. if (confile) {
  501. strncpy(g_main_ctx.confile, confile, sizeof(g_main_ctx.confile));
  502. }
  503. // g_conf_ctx
  504. parse_confile(g_main_ctx.confile);
  505. // test
  506. if (get_arg("t")) {
  507. printf("Test confile [%s] OK!\n", g_main_ctx.confile);
  508. exit(0);
  509. }
  510. master_process_init();
  511. // signal
  512. handle_signal();
  513. #ifdef __unix__
  514. // daemon
  515. if (get_arg("d")) {
  516. // nochdir, noclose
  517. int ret = daemon(1, 1);
  518. if (ret != 0) {
  519. printf("daemon error: %d\n", ret);
  520. exit(-10);
  521. }
  522. // parent process exit after daemon, so pid changed.
  523. g_main_ctx.pid = getpid();
  524. }
  525. #endif
  526. // pidfile
  527. create_pidfile();
  528. hlogi("%s start/running, pid=%d", g_main_ctx.program_name, g_main_ctx.pid);
  529. // cycle
  530. create_worker_processes(g_conf_ctx.worker_processes);
  531. master_process_cycle();
  532. return 0;
  533. }