/[LeafOK_CVS]/lbbs/src/main.c
ViewVC logotype

Contents of /lbbs/src/main.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.94 - (show annotations)
Thu Jan 8 14:22:57 2026 UTC (2 months, 1 week ago) by sysadm
Branch: MAIN
CVS Tags: HEAD
Changes since 1.93: +40 -25 lines
Content type: text/x-csrc
Refine handling of cmdline arguments

1 /* SPDX-License-Identifier: GPL-3.0-or-later */
2 /*
3 * main
4 * - entry of server program
5 *
6 * Copyright (C) 2004-2026 Leaflet <leaflet@leafok.com>
7 */
8
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12
13 #include "bbs.h"
14 #include "bwf.h"
15 #include "common.h"
16 #include "file_loader.h"
17 #include "init.h"
18 #include "io.h"
19 #include "lml.h"
20 #include "log.h"
21 #include "menu.h"
22 #include "net_server.h"
23 #include "section_list_loader.h"
24 #include "user_list.h"
25 #include <errno.h>
26 #include <libgen.h>
27 #include <locale.h>
28 #include <signal.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <sys/wait.h>
36
37 typedef void (*arg_option_handler_t)(void);
38
39 struct arg_option_t
40 {
41 const char *short_arg;
42 const char *long_arg;
43 const char *description;
44 arg_option_handler_t handler;
45 };
46 typedef struct arg_option_t ARG_OPTION;
47
48 static void arg_foreground(void);
49 static void arg_help(void);
50 static void arg_version(void);
51 static void arg_display_log(void);
52 static void arg_display_error_log(void);
53 static void arg_compile_info(void);
54 static void arg_error(void);
55
56 static const ARG_OPTION arg_options[] = {
57 {"-f", "--foreground", "Run in foreground", arg_foreground},
58 {"-h", "--help", "Display help message", arg_help},
59 {"-v", "--version", "Display version information", arg_version},
60 {NULL, "--display-log", "Display standard log information", arg_display_log},
61 {NULL, "--display-error-log", "Display error log information", arg_display_error_log},
62 {"-C", "--compile-config", "Display compile configuration", arg_compile_info}};
63
64 static const int arg_options_count = sizeof(arg_options) / sizeof(ARG_OPTION);
65
66 static int daemon_service = 1;
67 static int std_log_redir = 0;
68 static int error_log_redir = 0;
69
70 static void arg_foreground(void)
71 {
72 daemon_service = 0;
73 }
74
75 static void arg_usage(void)
76 {
77 fprintf(stderr, "Usage: bbsd [-fhvC] [...]\n\n");
78
79 for (int i = 0; i < arg_options_count; i++)
80 {
81 fprintf(stderr, "%s%*s%s%*s%s\n",
82 arg_options[i].short_arg ? arg_options[i].short_arg : "",
83 8 - (arg_options[i].short_arg ? (int)strlen(arg_options[i].short_arg) : 0), "",
84 arg_options[i].long_arg ? arg_options[i].long_arg : "",
85 24 - (arg_options[i].long_arg ? (int)strlen(arg_options[i].long_arg) : 0), "",
86 arg_options[i].description ? arg_options[i].description : "");
87 }
88
89 fprintf(stderr, "\n If meet any bug, please report to <leaflet@leafok.com>\n\n");
90 }
91
92 static void arg_help(void)
93 {
94 arg_usage();
95
96 exit(0);
97 }
98
99 static void arg_version(void)
100 {
101 fprintf(stderr, "%s\n", APP_INFO);
102 fprintf(stderr, "%s\n", COPYRIGHT_INFO);
103
104 exit(0);
105 }
106
107 static void arg_display_log(void)
108 {
109 std_log_redir = 1;
110 }
111
112 static void arg_display_error_log(void)
113 {
114 error_log_redir = 1;
115 }
116
117 static void arg_compile_info(void)
118 {
119 fprintf(stderr, "%s\n"
120 "--enable-shared\t\t[%s]\n"
121 "--enable-systemd\t[%s]\n"
122 "--with-debug\t\t[%s]\n"
123 "--with-epoll\t\t[%s]\n"
124 "--with-mariadb\t\t[%s]\n"
125 "--with-sysv\t\t[%s]\n",
126 APP_INFO,
127 #ifdef _ENABLE_SHARED
128 "yes",
129 #else
130 "no",
131 #endif
132 #ifdef HAVE_SYSTEMD_SD_DAEMON_H
133 "yes",
134 #else
135 "no",
136 #endif
137 #ifdef _DEBUG
138 "yes",
139 #else
140 "no",
141 #endif
142 #ifdef HAVE_SYS_EPOLL_H
143 "yes",
144 #else
145 "no",
146 #endif
147 #ifdef HAVE_MARIADB_CLIENT
148 "yes",
149 #else
150 "no",
151 #endif
152 #ifdef HAVE_SYSTEM_V
153 "yes"
154 #else
155 "no"
156 #endif
157 );
158
159 exit(0);
160 }
161
162 static void arg_error(void)
163 {
164 fprintf(stderr, "Invalid arguments\n");
165 arg_usage();
166
167 exit(1);
168 }
169
170 int main(int argc, char *argv[])
171 {
172 char file_path_temp[FILE_PATH_LEN];
173 FILE *fp;
174 int ret;
175 int last_aid;
176 struct sigaction act = {0};
177 int i;
178 int j;
179 struct stat file_stat;
180
181 // Parse args
182 for (i = 1; i < argc; i++)
183 {
184 for (j = 0; j < arg_options_count; j++)
185 {
186 if (arg_options[j].short_arg && strcmp(argv[i], arg_options[j].short_arg) == 0)
187 {
188 arg_options[j].handler();
189 break;
190 }
191 else if (arg_options[j].long_arg && strcmp(argv[i], arg_options[j].long_arg) == 0)
192 {
193 arg_options[j].handler();
194 break;
195 }
196 }
197 if (j == arg_options_count)
198 {
199 arg_error();
200 return -1;
201 }
202 }
203
204 // Initialize daemon
205 if (daemon_service)
206 {
207 init_daemon();
208 }
209
210 // Change current dir
211 strncpy(file_path_temp, argv[0], sizeof(file_path_temp) - 1);
212 file_path_temp[sizeof(file_path_temp) - 1] = '\0';
213
214 if (chdir(dirname(file_path_temp)) < 0)
215 {
216 fprintf(stderr, "chdir(%s) error: %d\n", dirname(file_path_temp), errno);
217 return -1;
218 }
219 if (chdir("..") < 0)
220 {
221 fprintf(stderr, "chdir(..) error: %d\n", errno);
222 return -1;
223 }
224
225 // Apply the specified locale
226 if (setlocale(LC_ALL, "en_US.UTF-8") == NULL)
227 {
228 fprintf(stderr, "setlocale(LC_ALL, en_US.UTF-8) error\n");
229 return -1;
230 }
231
232 fprintf(stderr, "%s\n", APP_INFO);
233
234 // Initialize log
235 if (log_begin(LOG_FILE_INFO, LOG_FILE_ERROR) < 0)
236 {
237 return -1;
238 }
239
240 if ((!daemon_service) && std_log_redir)
241 {
242 log_common_redir(STDERR_FILENO);
243 }
244 if ((!daemon_service) && error_log_redir)
245 {
246 log_error_redir(STDERR_FILENO);
247 }
248
249 log_common("Starting %s", APP_INFO);
250
251 // Load configuration
252 if (load_conf(CONF_BBSD) < 0)
253 {
254 return -2;
255 }
256
257 // Load BWF config
258 if (bwf_load(CONF_BWF) < 0)
259 {
260 return -2;
261 }
262
263 // Get EULA modification tm
264 if (stat(DATA_EULA, &file_stat) == -1)
265 {
266 log_error("stat(%s) error", DATA_EULA, errno);
267 goto cleanup;
268 }
269 BBS_eula_tm = file_stat.st_mtim.tv_sec;
270
271 // Check article cache dir
272 ret = mkdir(VAR_ARTICLE_CACHE_DIR, 0750);
273 if (ret == -1 && errno != EEXIST)
274 {
275 log_error("mkdir(%s) error (%d)", VAR_ARTICLE_CACHE_DIR, errno);
276 goto cleanup;
277 }
278
279 // Check section aid location dir
280 ret = mkdir(VAR_SECTION_AID_LOC_DIR, 0750);
281 if (ret == -1 && errno != EEXIST)
282 {
283 log_error("mkdir(%s) error (%d)", VAR_SECTION_AID_LOC_DIR, errno);
284 goto cleanup;
285 }
286
287 // Initialize data pools
288 if ((fp = fopen(VAR_ARTICLE_BLOCK_SHM, "w")) == NULL)
289 {
290 log_error("fopen(%s) error", VAR_ARTICLE_BLOCK_SHM);
291 goto cleanup;
292 }
293 fclose(fp);
294 if ((fp = fopen(VAR_SECTION_LIST_SHM, "w")) == NULL)
295 {
296 log_error("fopen(%s) error", VAR_SECTION_LIST_SHM);
297 goto cleanup;
298 }
299 fclose(fp);
300 if ((fp = fopen(VAR_TRIE_DICT_SHM, "w")) == NULL)
301 {
302 log_error("fopen(%s) error", VAR_TRIE_DICT_SHM);
303 goto cleanup;
304 }
305 fclose(fp);
306 if ((fp = fopen(VAR_USER_LIST_SHM, "w")) == NULL)
307 {
308 log_error("fopen(%s) error", VAR_USER_LIST_SHM);
309 goto cleanup;
310 }
311 fclose(fp);
312
313 if (trie_dict_init(VAR_TRIE_DICT_SHM, TRIE_NODE_PER_POOL) < 0)
314 {
315 log_error("trie_dict_init(%s, %d) error", VAR_TRIE_DICT_SHM, TRIE_NODE_PER_POOL);
316 goto cleanup;
317 }
318 if (article_block_init(VAR_ARTICLE_BLOCK_SHM, BBS_article_limit_per_section * BBS_max_section / BBS_article_count_per_block) < 0)
319 {
320 log_error("article_block_init(%s, %d) error", VAR_ARTICLE_BLOCK_SHM, BBS_article_limit_per_section * BBS_max_section / BBS_article_count_per_block);
321 goto cleanup;
322 }
323 if (section_list_init(VAR_SECTION_LIST_SHM) < 0)
324 {
325 log_error("section_list_pool_init(%s) error", VAR_SECTION_LIST_SHM);
326 goto cleanup;
327 }
328
329 // Init LML module
330 if (lml_init() < 0)
331 {
332 log_error("lml_init() error");
333 goto cleanup;
334 }
335
336 // Load BBS cmd
337 if (load_cmd() < 0)
338 {
339 log_error("load_cmd() error");
340 goto cleanup;
341 }
342
343 // Load menus
344 if (load_menu(&bbs_menu, CONF_MENU) < 0)
345 {
346 log_error("load_menu(bbs_menu) error");
347 goto cleanup;
348 }
349 if (load_menu(&top10_menu, CONF_TOP10_MENU) < 0)
350 {
351 log_error("load_menu(top10_menu) error");
352 goto cleanup;
353 }
354 top10_menu.allow_exit = 1;
355
356 // Load data files
357 for (int i = 0; i < data_files_load_startup_count; i++)
358 {
359 if (load_file(data_files_load_startup[i]) < 0)
360 {
361 log_error("load_file(%s) error", data_files_load_startup[i]);
362 }
363 }
364
365 // Load user_list and online_user_list
366 if (user_list_pool_init(VAR_USER_LIST_SHM) < 0)
367 {
368 log_error("user_list_pool_init(%s) error", VAR_USER_LIST_SHM);
369 goto cleanup;
370 }
371 if (user_list_pool_reload(0) < 0)
372 {
373 log_error("user_list_pool_reload(all_user) error");
374 goto cleanup;
375 }
376 if (user_list_pool_reload(1) < 0)
377 {
378 log_error("user_list_pool_reload(online_user) error");
379 goto cleanup;
380 }
381
382 // Load section config and gen_ex
383 if (load_section_config_from_db(1) < 0)
384 {
385 log_error("load_section_config_from_db(0) error");
386 goto cleanup;
387 }
388
389 set_last_article_op_log_from_db();
390 last_aid = 0;
391
392 // Load section articles
393 do
394 {
395 if ((ret = append_articles_from_db(last_aid + 1, 1, LOAD_ARTICLE_COUNT_LIMIT)) < 0)
396 {
397 log_error("append_articles_from_db(0, 1, %d) error", LOAD_ARTICLE_COUNT_LIMIT);
398 goto cleanup;
399 }
400
401 last_aid = article_block_last_aid();
402 } while (ret == LOAD_ARTICLE_COUNT_LIMIT);
403
404 log_common("Initially load %d articles, last_aid = %d", article_block_article_count(), article_block_last_aid());
405
406 if ((ret = user_stat_update()) < 0)
407 {
408 log_error("user_stat_update() error");
409 goto cleanup;
410 }
411
412 // Set signal handler
413 act.sa_handler = sig_hup_handler;
414 if (sigaction(SIGHUP, &act, NULL) == -1)
415 {
416 log_error("set signal action of SIGHUP error: %d", errno);
417 goto cleanup;
418 }
419 act.sa_handler = sig_chld_handler;
420 if (sigaction(SIGCHLD, &act, NULL) == -1)
421 {
422 log_error("set signal action of SIGCHLD error: %d", errno);
423 goto cleanup;
424 }
425 act.sa_handler = sig_term_handler;
426 if (sigaction(SIGTERM, &act, NULL) == -1)
427 {
428 log_error("set signal action of SIGTERM error: %d", errno);
429 goto cleanup;
430 }
431 act.sa_handler = SIG_IGN;
432 if (sigaction(SIGPIPE, &act, NULL) == -1)
433 {
434 log_error("set signal action of SIGPIPE error: %d", errno);
435 goto cleanup;
436 }
437 act.sa_handler = SIG_IGN;
438 if (sigaction(SIGUSR1, &act, NULL) == -1)
439 {
440 log_error("set signal action of SIGUSR1 error: %d", errno);
441 goto cleanup;
442 }
443
444 // Launch section_list_loader process
445 if (section_list_loader_launch() < 0)
446 {
447 log_error("section_list_loader_launch() error");
448 goto cleanup;
449 }
450
451 // Initialize socket server
452 net_server(BBS_address, BBS_port);
453
454 cleanup:
455 // Cleanup loader process if still running
456 if (SYS_child_process_count > 0)
457 {
458 SYS_child_exit = 0;
459
460 if (kill(section_list_loader_pid, SIGTERM) < 0)
461 {
462 log_error("Send SIGTERM signal failed (%d)", errno);
463 }
464
465 for (i = 0; SYS_child_exit == 0 && i < 5; i++)
466 {
467 sleep(1); // second
468 }
469
470 if ((ret = waitpid(section_list_loader_pid, NULL, WNOHANG)) < 0)
471 {
472 log_error("waitpid(%d) error (%d)", section_list_loader_pid, errno);
473 }
474 else if (ret == 0)
475 {
476 log_common("Force kill section_list_loader process (%d)", section_list_loader_pid);
477 if (kill(section_list_loader_pid, SIGKILL) < 0)
478 {
479 log_error("Send SIGKILL signal failed (%d)", errno);
480 }
481 }
482 }
483
484 // Cleanup loaded data files
485 for (int i = 0; i < data_files_load_startup_count; i++)
486 {
487 if (unload_file(data_files_load_startup[i]) < 0)
488 {
489 log_error("unload_file(%s) error", data_files_load_startup[i]);
490 }
491 }
492
493 // Cleanup menu
494 unload_menu(&bbs_menu);
495 unload_menu(&top10_menu);
496
497 // Cleanup LML module
498 lml_cleanup();
499
500 // Cleanup data pools
501 section_list_cleanup();
502 article_block_cleanup();
503 trie_dict_cleanup();
504 user_list_pool_cleanup();
505
506 if (unlink(VAR_ARTICLE_BLOCK_SHM) < 0)
507 {
508 log_error("unlink(%s) error", VAR_ARTICLE_BLOCK_SHM);
509 }
510 if (unlink(VAR_SECTION_LIST_SHM) < 0)
511 {
512 log_error("unlink(%s) error", VAR_SECTION_LIST_SHM);
513 }
514 if (unlink(VAR_TRIE_DICT_SHM) < 0)
515 {
516 log_error("unlink(%s) error", VAR_TRIE_DICT_SHM);
517 }
518 if (unlink(VAR_USER_LIST_SHM) < 0)
519 {
520 log_error("unlink(%s) error", VAR_SECTION_LIST_SHM);
521 }
522
523 log_common("Main process exit normally");
524
525 log_end();
526
527 return 0;
528 }

webmaster@leafok.com
ViewVC Help
Powered by ViewVC 1.3.0-beta1