main.cpp.tmpl 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. #include "h.h"
  2. #define DEFAULT_WORKER_PROCESSES 4
  3. #define MAXNUM_WORKER_PROCESSES 1024
  4. static proc_ctx_t s_worker_processes[MAXNUM_WORKER_PROCESSES];
  5. typedef struct conf_ctx_s {
  6. IniParser* parser;
  7. int loglevel;
  8. int worker_processes;
  9. int port;
  10. } conf_ctx_t;
  11. conf_ctx_t g_conf_ctx;
  12. inline void conf_ctx_init(conf_ctx_t* ctx) {
  13. ctx->parser = new IniParser;
  14. ctx->loglevel = LOG_LEVEL_DEBUG;
  15. ctx->worker_processes = 0;
  16. ctx->port = 0;
  17. }
  18. static void print_version();
  19. static void print_help();
  20. static int parse_confile(const char* confile);
  21. static int signal_init();
  22. static void signal_cleanup();
  23. static void handle_signal();
  24. static void master_proc(void* userdata);
  25. static void worker_proc(void* userdata);
  26. // short options
  27. static const char options[] = "hvc:ts:dp:";
  28. // long options
  29. static const option_t long_options[] = {
  30. {'h', "help", NO_ARGUMENT},
  31. {'v', "version", NO_ARGUMENT},
  32. {'c', "confile", REQUIRED_ARGUMENT},
  33. {'t', "test", NO_ARGUMENT},
  34. {'s', "signal", REQUIRED_ARGUMENT},
  35. {'d', "daemon", NO_ARGUMENT},
  36. {'p', "port", REQUIRED_ARGUMENT}
  37. };
  38. static const char detail_options[] = R"(
  39. -h|--help Print this information
  40. -v|--version Print version
  41. -c|--confile <confile> Set configure file, default etc/{program}.conf
  42. -t|--test Test Configure file and exit
  43. -s|--signal <signal> Send <signal> to process,
  44. <signal>=[start,stop,restart,status,reload]
  45. -d|--daemon Daemonize
  46. -p|--port <port> Set listen port
  47. )";
  48. void print_version() {
  49. printf("%s version %s\n", g_main_ctx.program_name, get_compile_version());
  50. }
  51. void print_help() {
  52. printf("Usage: %s [%s]\n", g_main_ctx.program_name, options);
  53. printf("Options:\n%s\n", detail_options);
  54. }
  55. int parse_confile(const char* confile) {
  56. conf_ctx_init(&g_conf_ctx);
  57. int ret = g_conf_ctx.parser->LoadFromFile(confile);
  58. if (ret != 0) {
  59. printf("Load confile [%s] failed: %d\n", confile, ret);
  60. exit(-40);
  61. }
  62. // logfile
  63. string str = g_conf_ctx.parser->GetValue("logfile");
  64. if (!str.empty()) {
  65. hlog_set_file(str.c_str());
  66. }
  67. // loglevel
  68. const char* szLoglevel = g_conf_ctx.parser->GetValue("loglevel").c_str();
  69. if (stricmp(szLoglevel, "VERBOSE") == 0) {
  70. g_conf_ctx.loglevel = LOG_LEVEL_VERBOSE;
  71. } else if (stricmp(szLoglevel, "DEBUG") == 0) {
  72. g_conf_ctx.loglevel = LOG_LEVEL_DEBUG;
  73. } else if (stricmp(szLoglevel, "INFO") == 0) {
  74. g_conf_ctx.loglevel = LOG_LEVEL_INFO;
  75. } else if (stricmp(szLoglevel, "WARN") == 0) {
  76. g_conf_ctx.loglevel = LOG_LEVEL_WARN;
  77. } else if (stricmp(szLoglevel, "ERROR") == 0) {
  78. g_conf_ctx.loglevel = LOG_LEVEL_ERROR;
  79. } else if (stricmp(szLoglevel, "FATAL") == 0) {
  80. g_conf_ctx.loglevel = LOG_LEVEL_FATAL;
  81. } else if (stricmp(szLoglevel, "SILENT") == 0) {
  82. g_conf_ctx.loglevel = LOG_LEVEL_SILENT;
  83. } else {
  84. g_conf_ctx.loglevel = LOG_LEVEL_VERBOSE;
  85. }
  86. hlog_set_level(g_conf_ctx.loglevel);
  87. // log_remain_days
  88. str = g_conf_ctx.parser->GetValue("log_remain_days");
  89. if (!str.empty()) {
  90. hlog_set_remain_days(atoi(str.c_str()));
  91. }
  92. // worker_processes
  93. int worker_processes = 0;
  94. worker_processes = atoi(g_conf_ctx.parser->GetValue("worker_processes").c_str());
  95. if (worker_processes <= 0 || worker_processes > MAXNUM_WORKER_PROCESSES) {
  96. worker_processes = get_ncpu();
  97. hlogd("worker_processes=ncpu=%d", worker_processes);
  98. }
  99. if (worker_processes <= 0 || worker_processes > MAXNUM_WORKER_PROCESSES) {
  100. worker_processes = DEFAULT_WORKER_PROCESSES;
  101. }
  102. g_conf_ctx.worker_processes = worker_processes;
  103. // port
  104. int port = 0;
  105. const char* szPort = get_arg("p");
  106. if (szPort) {
  107. port = atoi(szPort);
  108. }
  109. if (port == 0) {
  110. port = atoi(g_conf_ctx.parser->GetValue("port").c_str());
  111. }
  112. if (port == 0) {
  113. printf("Please config listen port!\n");
  114. exit(-10);
  115. }
  116. g_conf_ctx.port = port;
  117. return 0;
  118. }
  119. #ifdef OS_UNIX
  120. // unix use signal
  121. // we use SIGTERM to quit process, SIGUSR1 to reload confile
  122. #define SIGNAL_TERMINATE SIGTERM
  123. #define SIGNAL_RELOAD SIGUSR1
  124. #include <sys/wait.h>
  125. void signal_handler(int signo) {
  126. hlogi("pid=%d recv signo=%d", getpid(), signo);
  127. switch (signo) {
  128. case SIGINT:
  129. case SIGNAL_TERMINATE:
  130. hlogi("killall processes");
  131. signal(SIGCHLD, SIG_IGN);
  132. for (int i = 0; i < MAXNUM_WORKER_PROCESSES; ++i) {
  133. if (s_worker_processes[i].pid <= 0) break;
  134. kill(s_worker_processes[i].pid, SIGKILL);
  135. s_worker_processes[i].pid = -1;
  136. }
  137. exit(0);
  138. break;
  139. case SIGNAL_RELOAD:
  140. hlogi("reload confile [%s]", g_main_ctx.confile);
  141. parse_confile(g_main_ctx.confile);
  142. break;
  143. case SIGCHLD:
  144. {
  145. pid_t pid = 0;
  146. int status = 0;
  147. while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
  148. hlogw("proc stop/waiting, pid=%d status=%d", pid, status);
  149. for (int i = 0; i < MAXNUM_WORKER_PROCESSES; ++i) {
  150. if (s_worker_processes[i].pid == pid) {
  151. s_worker_processes[i].pid = -1;
  152. create_proc(&s_worker_processes[i]);
  153. break;
  154. }
  155. }
  156. }
  157. }
  158. break;
  159. default:
  160. break;
  161. }
  162. }
  163. int signal_init() {
  164. signal(SIGINT, signal_handler);
  165. signal(SIGCHLD, signal_handler);
  166. signal(SIGNAL_TERMINATE, signal_handler);
  167. signal(SIGNAL_RELOAD, signal_handler);
  168. atexit(signal_cleanup);
  169. return 0;
  170. }
  171. void signal_cleanup() {
  172. }
  173. #elif defined(OS_WIN)
  174. // win32 use Event
  175. static HANDLE s_hEventTerm = NULL;
  176. static HANDLE s_hEventReload = NULL;
  177. #include <mmsystem.h>
  178. #ifdef _MSC_VER
  179. #pragma comment(lib, "winmm.lib")
  180. #endif
  181. void WINAPI on_timer(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) {
  182. DWORD ret = WaitForSingleObject(s_hEventTerm, 0);
  183. if (ret == WAIT_OBJECT_0) {
  184. timeKillEvent(uTimerID);
  185. hlogi("pid=%d recv event [TERM]", getpid());
  186. exit(0);
  187. }
  188. ret = WaitForSingleObject(s_hEventReload, 0);
  189. if (ret == WAIT_OBJECT_0) {
  190. hlogi("pid=%d recv event [RELOAD]", getpid());
  191. parse_confile(g_main_ctx.confile);
  192. }
  193. }
  194. int signal_init() {
  195. char eventname[MAX_PATH] = {0};
  196. snprintf(eventname, sizeof(eventname), "%s_term_event", g_main_ctx.program_name);
  197. s_hEventTerm = CreateEvent(NULL, FALSE, FALSE, eventname);
  198. //s_hEventTerm = OpenEvent(EVENT_ALL_ACCESS, FALSE, eventname);
  199. snprintf(eventname, sizeof(eventname), "%s_reload_event", g_main_ctx.program_name);
  200. s_hEventReload = CreateEvent(NULL, FALSE, FALSE, eventname);
  201. timeSetEvent(1000, 1000, on_timer, 0, TIME_PERIODIC);
  202. atexit(signal_cleanup);
  203. return 0;
  204. }
  205. void signal_cleanup() {
  206. CloseHandle(s_hEventTerm);
  207. s_hEventTerm = NULL;
  208. CloseHandle(s_hEventReload);
  209. s_hEventReload = NULL;
  210. }
  211. #endif
  212. void handle_signal() {
  213. const char* signal = get_arg("s");
  214. if (signal) {
  215. if (strcmp(signal, "start") == 0) {
  216. if (g_main_ctx.oldpid > 0) {
  217. printf("%s is already running, pid=%d\n", g_main_ctx.program_name, g_main_ctx.oldpid);
  218. exit(0);
  219. }
  220. } else if (strcmp(signal, "stop") == 0) {
  221. if (g_main_ctx.oldpid > 0) {
  222. #ifdef OS_UNIX
  223. kill(g_main_ctx.oldpid, SIGNAL_TERMINATE);
  224. #else
  225. SetEvent(s_hEventTerm);
  226. #endif
  227. printf("%s stop/waiting\n", g_main_ctx.program_name);
  228. } else {
  229. printf("%s is already stopped\n", g_main_ctx.program_name);
  230. }
  231. exit(0);
  232. } else if (strcmp(signal, "restart") == 0) {
  233. if (g_main_ctx.oldpid > 0) {
  234. #ifdef OS_UNIX
  235. kill(g_main_ctx.oldpid, SIGNAL_TERMINATE);
  236. #else
  237. SetEvent(s_hEventTerm);
  238. #endif
  239. printf("%s stop/waiting\n", g_main_ctx.program_name);
  240. msleep(1000);
  241. }
  242. } else if (strcmp(signal, "status") == 0) {
  243. if (g_main_ctx.oldpid > 0) {
  244. printf("%s start/running, pid=%d\n", g_main_ctx.program_name, g_main_ctx.oldpid);
  245. } else {
  246. printf("%s stop/waiting\n", g_main_ctx.program_name);
  247. }
  248. exit(0);
  249. } else if (strcmp(signal, "reload") == 0) {
  250. if (g_main_ctx.oldpid > 0) {
  251. printf("reload confile [%s]\n", g_main_ctx.confile);
  252. #ifdef __unix__
  253. kill(g_main_ctx.oldpid, SIGNAL_RELOAD);
  254. #else
  255. SetEvent(s_hEventReload);
  256. #endif
  257. }
  258. sleep(1);
  259. exit(0);
  260. } else {
  261. printf("Invalid signal: '%s'\n", signal);
  262. exit(0);
  263. }
  264. printf("%s start/running\n", g_main_ctx.program_name);
  265. }
  266. }
  267. int main(int argc, char** argv) {
  268. // g_main_ctx
  269. main_ctx_init(argc, argv);
  270. if (argc == 1) {
  271. print_help();
  272. exit(10);
  273. }
  274. //int ret = parse_opt(argc, argv, options);
  275. int ret = parse_opt_long(argc, argv, long_options, ARRAY_SIZE(long_options));
  276. if (ret != 0) {
  277. print_help();
  278. exit(ret);
  279. }
  280. /*
  281. printf("---------------arg------------------------------\n");
  282. printf("%s\n", g_main_ctx.cmdline);
  283. for (auto& pair : g_main_ctx.arg_kv) {
  284. printf("%s=%s\n", pair.first.c_str(), pair.second.c_str());
  285. }
  286. for (auto& item : g_main_ctx.arg_list) {
  287. printf("%s\n", item.c_str());
  288. }
  289. printf("================================================\n");
  290. */
  291. /*
  292. printf("---------------env------------------------------\n");
  293. for (auto& pair : g_main_ctx.env_kv) {
  294. printf("%s=%s\n", pair.first.c_str(), pair.second.c_str());
  295. }
  296. printf("================================================\n");
  297. */
  298. // help
  299. if (get_arg("h")) {
  300. print_help();
  301. exit(0);
  302. }
  303. // version
  304. if (get_arg("v")) {
  305. print_version();
  306. exit(0);
  307. }
  308. // logfile
  309. hlog_set_file(g_main_ctx.logfile);
  310. hlogi("%s version: %s", g_main_ctx.program_name, get_compile_version());
  311. // confile
  312. const char* confile = get_arg("c");
  313. if (confile) {
  314. strncpy(g_main_ctx.confile, confile, sizeof(g_main_ctx.confile));
  315. }
  316. // g_conf_ctx
  317. parse_confile(g_main_ctx.confile);
  318. // test
  319. if (get_arg("t")) {
  320. printf("Test confile [%s] OK!\n", g_main_ctx.confile);
  321. exit(0);
  322. }
  323. // signal
  324. signal_init();
  325. handle_signal();
  326. #ifdef OS_UNIX
  327. // daemon
  328. if (get_arg("d")) {
  329. // nochdir, noclose
  330. int ret = daemon(1, 1);
  331. if (ret != 0) {
  332. printf("daemon error: %d\n", ret);
  333. exit(-10);
  334. }
  335. // parent process exit after daemon, so pid changed.
  336. g_main_ctx.pid = getpid();
  337. }
  338. // proctitle
  339. char proctitle[256] = {0};
  340. snprintf(proctitle, sizeof(proctitle), "%s: master process", g_main_ctx.program_name);
  341. setproctitle(proctitle);
  342. #endif
  343. // pidfile
  344. create_pidfile();
  345. hlogi("%s start/running, pid=%d", g_main_ctx.program_name, g_main_ctx.pid);
  346. // master-worker proc
  347. memset(s_worker_processes, 0, sizeof(s_worker_processes));
  348. for (int i = 0; i < g_conf_ctx.worker_processes; ++i) {
  349. proc_ctx_t* ctx = &s_worker_processes[i];
  350. snprintf(ctx->proctitle, sizeof(ctx->proctitle), "%s: worker process", g_main_ctx.program_name);
  351. ctx->proc = worker_proc;
  352. ctx->userdata = NULL;
  353. create_proc(ctx);
  354. }
  355. master_proc(NULL);
  356. return 0;
  357. }
  358. void master_proc(void* userdata) {
  359. while(1) msleep(1000);
  360. }
  361. void worker_proc(void* userdata) {
  362. while(1) msleep(1000);
  363. }