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

Contents of /lbbs/src/section_list_loader.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.5 - (show annotations)
Tue May 27 07:21:43 2025 UTC (9 months, 2 weeks ago) by sysadm
Branch: MAIN
Changes since 1.4: +285 -11 lines
Content type: text/x-csrc
Add set_last_article_op_log_from_db() and set_last_article_op_log_from_db()

1 /***************************************************************************
2 section_list_loader.c - description
3 -------------------
4 Copyright : (C) 2004-2025 by Leaflet
5 Email : leaflet@leafok.com
6 ***************************************************************************/
7
8 /***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 3 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 #include "section_list_loader.h"
18 #include "log.h"
19 #include "database.h"
20 #include "menu.h"
21 #include <stdio.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <signal.h>
25 #include <stdlib.h>
26 #include <strings.h>
27 #include <unistd.h>
28
29 #define SECTION_LIST_LOAD_INTERVAL 10 // second
30
31 int section_list_loader_pid;
32 int last_article_op_log_mid;
33
34 int load_section_config_from_db(void)
35 {
36 MYSQL *db;
37 MYSQL_RES *rs, *rs2;
38 MYSQL_ROW row, row2;
39 char sql[SQL_BUFFER_LEN];
40 int32_t sid;
41 char master_name[BBS_username_max_len + 1];
42 SECTION_LIST *p_section;
43 int ret;
44
45 db = db_open();
46 if (db == NULL)
47 {
48 log_error("db_open() error: %s\n", mysql_error(db));
49 return -2;
50 }
51
52 snprintf(sql, sizeof(sql),
53 "SELECT section_config.SID, sname, section_config.title, section_config.CID, "
54 "read_user_level, write_user_level, section_config.enable * section_class.enable AS enable "
55 "FROM section_config INNER JOIN section_class ON section_config.CID = section_class.CID "
56 "ORDER BY section_config.SID");
57
58 if (mysql_query(db, sql) != 0)
59 {
60 log_error("Query section_list error: %s\n", mysql_error(db));
61 return -3;
62 }
63 if ((rs = mysql_store_result(db)) == NULL)
64 {
65 log_error("Get section_list data failed\n");
66 return -3;
67 }
68
69 ret = 0;
70 while ((row = mysql_fetch_row(rs)))
71 {
72 sid = atoi(row[0]);
73
74 // Query section master
75 snprintf(sql, sizeof(sql),
76 "SELECT username FROM section_master "
77 "INNER JOIN user_list ON section_master.UID = user_list.UID "
78 "WHERE SID = %d AND section_master.enable AND (NOW() BETWEEN begin_dt AND end_dt) "
79 "ORDER BY major DESC LIMIT 1",
80 sid);
81
82 if (mysql_query(db, sql) != 0)
83 {
84 log_error("Query section_master error: %s\n", mysql_error(db));
85 ret = -3;
86 break;
87 }
88 if ((rs2 = mysql_store_result(db)) == NULL)
89 {
90 log_error("Get section_master data failed\n");
91 ret = -3;
92 break;
93 }
94 if ((row2 = mysql_fetch_row(rs2)))
95 {
96 strncpy(master_name, row2[0], sizeof(master_name) - 1);
97 master_name[sizeof(master_name) - 1] = '\0';
98 }
99 else
100 {
101 master_name[0] = '\0';
102 }
103 mysql_free_result(rs2);
104
105 p_section = section_list_find_by_sid(sid);
106
107 if (p_section == NULL)
108 {
109 p_section = section_list_create(sid, row[1], row[2], "");
110 if (p_section == NULL)
111 {
112 log_error("section_list_create() error: load new section sid = %d sname = %s\n", sid, row[1]);
113 ret = -4;
114 break;
115 }
116
117 // acquire rw lock
118 ret = section_list_rw_lock(p_section);
119 if (ret < 0)
120 {
121 break;
122 }
123 }
124 else
125 {
126 // acquire rw lock
127 ret = section_list_rw_lock(p_section);
128 if (ret < 0)
129 {
130 break;
131 }
132
133 strncpy(p_section->sname, row[1], sizeof(p_section->sname) - 1);
134 p_section->sname[sizeof(p_section->sname) - 1] = '\0';
135 strncpy(p_section->stitle, row[1], sizeof(p_section->stitle) - 1);
136 p_section->stitle[sizeof(p_section->stitle) - 1] = '\0';
137 strncpy(p_section->master_name, master_name, sizeof(p_section->master_name) - 1);
138 p_section->master_name[sizeof(p_section->master_name) - 1] = '\0';
139 }
140
141 p_section->class_id = atoi(row[3]);
142 p_section->read_user_level = atoi(row[4]);
143 p_section->write_user_level = atoi(row[5]);
144 p_section->enable = (int8_t)atoi(row[6]);
145
146 // release rw lock
147 ret = section_list_rw_unlock(p_section);
148 if (ret < 0)
149 {
150 break;
151 }
152 }
153 mysql_free_result(rs);
154
155 mysql_close(db);
156
157 return ret;
158 }
159
160 int append_articles_from_db(int32_t start_aid, int global_lock)
161 {
162 MYSQL *db;
163 MYSQL_RES *rs;
164 MYSQL_ROW row;
165 char sql[SQL_BUFFER_LEN];
166 ARTICLE article;
167 ARTICLE *p_topic;
168 SECTION_LIST *p_section = NULL;
169 int32_t last_sid = 0;
170 int ret = 0;
171 int i;
172
173 db = db_open();
174 if (db == NULL)
175 {
176 log_error("db_open() error: %s\n", mysql_error(db));
177 return -2;
178 }
179
180 snprintf(sql, sizeof(sql),
181 "SELECT AID, TID, SID, CID, UID, visible, excerption, ontop, `lock`, "
182 "transship, username, nickname, title, UNIX_TIMESTAMP(sub_dt) AS sub_dt "
183 "FROM bbs WHERE AID >= %d ORDER BY AID",
184 start_aid);
185
186 if (mysql_query(db, sql) != 0)
187 {
188 log_error("Query article list error: %s\n", mysql_error(db));
189 return -3;
190 }
191 if ((rs = mysql_use_result(db)) == NULL)
192 {
193 log_error("Get article list data failed\n");
194 return -3;
195 }
196
197 // acquire global lock
198 if (global_lock)
199 {
200 if ((ret = section_list_rw_lock(NULL)) < 0)
201 {
202 log_error("section_list_rw_lock(sid = 0) error\n");
203 goto cleanup;
204 }
205 }
206
207 while ((row = mysql_fetch_row(rs)))
208 {
209 bzero(&article, sizeof(ARTICLE));
210
211 // copy data of article
212 i = 0;
213
214 article.aid = atoi(row[i++]);
215 article.tid = atoi(row[i++]);
216 article.sid = atoi(row[i++]);
217 article.cid = atoi(row[i++]);
218 article.uid = atoi(row[i++]);
219 article.visible = (int8_t)atoi(row[i++]);
220 article.excerption = (int8_t)atoi(row[i++]);
221 article.ontop = (int8_t)atoi(row[i++]);
222 article.lock = (int8_t)atoi(row[i++]);
223 article.transship = (int8_t)atoi(row[i++]);
224
225 strncpy(article.username, row[i++], sizeof(article.username) - 1);
226 article.username[sizeof(article.username) - 1] = '\0';
227 strncpy(article.nickname, row[i++], sizeof(article.nickname) - 1);
228 article.nickname[sizeof(article.nickname) - 1] = '\0';
229 strncpy(article.title, row[i++], sizeof(article.title) - 1);
230 article.title[sizeof(article.title) - 1] = '\0';
231
232 article.sub_dt = atol(row[i++]);
233
234 // release lock of last section if different from current one
235 if (!global_lock && article.sid != last_sid && last_sid != 0)
236 {
237 if ((ret = section_list_rw_unlock(p_section)) < 0)
238 {
239 log_error("section_list_rw_unlock(sid = %d) error\n", p_section->sid);
240 break;
241 }
242 }
243
244 if ((p_section = section_list_find_by_sid(article.sid)) == NULL)
245 {
246 log_error("section_list_find_by_sid(%d) error: unknown section, try reloading section config\n", article.sid);
247 ret = ERR_UNKNOWN_SECTION; // Unknown section found
248 break;
249 }
250
251 if (article.visible != 0 && article.tid != 0)
252 {
253 // Check if topic article is visible
254 p_topic = article_block_find_by_aid(article.tid);
255 if (p_topic == NULL || p_topic->visible == 0)
256 {
257 // log_error("Set article (aid = %d) as invisible due to invisible or non-existing topic head\n", article.aid);
258 article.tid = 0;
259 article.visible = 0;
260 }
261 }
262
263 // acquire lock of current section if different from last one
264 if (!global_lock && article.sid != last_sid)
265 {
266 if ((ret = section_list_rw_lock(p_section)) < 0)
267 {
268 log_error("section_list_rw_lock(sid = 0) error\n");
269 break;
270 }
271 }
272
273 // append article to section list
274 last_sid = article.sid;
275
276 if (section_list_append_article(p_section, &article) < 0)
277 {
278 log_error("section_list_append_article(sid = %d, aid = %d) error\n",
279 p_section->sid, article.aid);
280 ret = -3;
281 break;
282 }
283 }
284
285 // release lock of last section
286 if (!global_lock && last_sid != 0)
287 {
288 if ((ret = section_list_rw_unlock(p_section)) < 0)
289 {
290 log_error("section_list_rw_unlock(sid = %d) error\n", p_section->sid);
291 }
292 }
293
294 // release global lock
295 if (global_lock)
296 {
297 if ((ret = section_list_rw_unlock(NULL)) < 0)
298 {
299 log_error("section_list_rw_unlock(sid = 0) error\n");
300 }
301 }
302
303 cleanup:
304 mysql_free_result(rs);
305
306 mysql_close(db);
307
308 return ret;
309 }
310
311 int set_last_article_op_log_from_db(void)
312 {
313 MYSQL *db;
314 MYSQL_RES *rs;
315 MYSQL_ROW row;
316 char sql[SQL_BUFFER_LEN];
317
318 db = db_open();
319 if (db == NULL)
320 {
321 log_error("db_open() error: %s\n", mysql_error(db));
322 return -1;
323 }
324
325 snprintf(sql, sizeof(sql),
326 "SELECT MID FROM bbs_article_op ORDER BY MID DESC LIMIT 1");
327
328 if (mysql_query(db, sql) != 0)
329 {
330 log_error("Query article op error: %s\n", mysql_error(db));
331 return -2;
332 }
333 if ((rs = mysql_store_result(db)) == NULL)
334 {
335 log_error("Get article op data failed\n");
336 return -2;
337 }
338
339 if ((row = mysql_fetch_row(rs)))
340 {
341 last_article_op_log_mid = atoi(row[0]);
342 }
343
344 mysql_free_result(rs);
345
346 mysql_close(db);
347
348 return last_article_op_log_mid;
349 }
350
351 int apply_article_op_log_from_db(void)
352 {
353 MYSQL *db;
354 MYSQL_RES *rs, *rs2;
355 MYSQL_ROW row, row2;
356 char sql[SQL_BUFFER_LEN];
357 ARTICLE *p_article;
358 SECTION_LIST *p_section = NULL;
359 SECTION_LIST *p_section_dest;
360 int32_t last_sid = 0;
361 int32_t sid_dest;
362 int ret = 0;
363
364 db = db_open();
365 if (db == NULL)
366 {
367 log_error("db_open() error: %s\n", mysql_error(db));
368 return -3;
369 }
370
371 snprintf(sql, sizeof(sql),
372 "SELECT MID, AID, type FROM bbs_article_op "
373 "WHERE MID > %d AND type NOT IN ('A', 'M') ORDER BY MID",
374 last_article_op_log_mid);
375
376 if (mysql_query(db, sql) != 0)
377 {
378 log_error("Query article log error: %s\n", mysql_error(db));
379 return -3;
380 }
381 if ((rs = mysql_store_result(db)) == NULL)
382 {
383 log_error("Get article log data failed\n");
384 return -3;
385 }
386
387 while ((row = mysql_fetch_row(rs)))
388 {
389 p_article = article_block_find_by_aid(atoi(row[1]));
390 if (p_article == NULL) // related article has not been appended yet
391 {
392 ret = -2;
393 break;
394 }
395
396 // release lock of last section if different from current one
397 if (p_article->sid != last_sid && last_sid != 0)
398 {
399 if ((ret = section_list_rw_unlock(p_section)) < 0)
400 {
401 log_error("section_list_rw_unlock(sid = %d) error\n", p_section->sid);
402 break;
403 }
404 }
405
406 if ((p_section = section_list_find_by_sid(p_article->sid)) == NULL)
407 {
408 log_error("section_list_find_by_sid(%d) error: unknown section, try reloading section config\n", p_article->sid);
409 ret = ERR_UNKNOWN_SECTION; // Unknown section found
410 break;
411 }
412
413 // acquire lock of current section if different from last one
414 if (p_article->sid != last_sid)
415 {
416 if ((ret = section_list_rw_lock(p_section)) < 0)
417 {
418 log_error("section_list_rw_lock(sid = 0) error\n");
419 break;
420 }
421 }
422
423 last_sid = p_article->sid;
424
425 switch (row[2][0])
426 {
427 case 'A': // Add article
428 log_error("Operation type=A should not be found\n");
429 break;
430 case 'D': // Delete article
431 case 'X': // Delete article by Admin
432 p_article->visible = 0;
433 if (p_article->tid == 0)
434 {
435 // Set articles in the topic to be invisible
436 do
437 {
438 p_article = p_article->p_topic_next;
439 p_article->visible = 0;
440 } while (p_article->tid != 0);
441 }
442 break;
443 case 'S': // Restore article
444 p_article->visible = 1;
445 break;
446 case 'L': // Lock article
447 p_article->lock = 1;
448 break;
449 case 'U': // Unlock article
450 p_article->lock = 0;
451 break;
452 case 'M': // Modify article
453 log_error("Operation type=M should not be found\n");
454 break;
455 case 'T': // Move article
456 snprintf(sql, sizeof(sql),
457 "SELECT SID FROM bbs WHERE AID = %d",
458 p_article->aid);
459
460 if (mysql_query(db, sql) != 0)
461 {
462 log_error("Query article error: %s\n", mysql_error(db));
463 ret = -3;
464 break;
465 }
466 if ((rs2 = mysql_store_result(db)) == NULL)
467 {
468 log_error("Get article data failed\n");
469 ret = -3;
470 break;
471 }
472 if ((row2 = mysql_fetch_row(rs2)))
473 {
474 sid_dest = atoi(row2[0]);
475 }
476 else
477 {
478 sid_dest = 0;
479 ret = -4;
480 }
481 mysql_free_result(rs2);
482
483 if (sid_dest > 0 && sid_dest != p_article->sid)
484 {
485 p_section_dest = section_list_find_by_sid(sid_dest);
486 if (p_section_dest == NULL)
487 {
488 ret = ERR_UNKNOWN_SECTION;
489 break;
490 }
491 // Move topic
492 if ((ret = section_list_move_topic(p_section, p_section_dest, p_article->aid)) < 0)
493 {
494 break;
495 }
496 }
497 break;
498 case 'E': // Set article as excerption
499 p_article->excerption = 1;
500 break;
501 case 'O': // Unset article as excerption
502 p_article->excerption = 0;
503 break;
504 case 'F': // Set article on top
505 p_article->ontop = 1;
506 break;
507 case 'V': // Unset article on top
508 p_article->ontop = 0;
509 break;
510 case 'Z': // Set article as trnasship
511 p_article->transship = 1;
512 break;
513 default:
514 // log_error("Operation type=%s unknown, mid=%s\n", row[2], row[0]);
515 break;
516 }
517
518 if (ret < 0)
519 {
520 break;
521 }
522
523 // Update MID with last successfully proceeded article_op_log
524 last_article_op_log_mid = atoi(row[0]);
525 }
526
527 // release lock of last section
528 if (last_sid != 0)
529 {
530 if ((ret = section_list_rw_unlock(p_section)) < 0)
531 {
532 log_error("section_list_rw_unlock(sid = %d) error\n", p_section->sid);
533 }
534 }
535
536 mysql_free_result(rs);
537
538 mysql_close(db);
539
540 return ret;
541 }
542
543 int section_list_loader_launch(void)
544 {
545 int pid;
546 int ret;
547 int32_t last_aid;
548 int article_count;
549 int load_count;
550 int last_mid;
551 int i;
552
553 if (section_list_loader_pid != 0)
554 {
555 log_error("section_list_loader already running, pid = %d\n", section_list_loader_pid);
556 return -2;
557 }
558
559 pid = fork();
560
561 if (pid > 0) // Parent process
562 {
563 SYS_child_process_count++;
564 section_list_loader_pid = pid;
565 log_std("Section list loader process (%d) start\n", pid);
566 return 0;
567 }
568 else if (pid < 0) // Error
569 {
570 log_error("fork() error (%d)\n", errno);
571 return -1;
572 }
573
574 // Child process
575 SYS_child_process_count = 0;
576
577 // Detach menu in shared memory
578 detach_menu_shm(p_bbs_menu);
579 free(p_bbs_menu);
580 p_bbs_menu = NULL;
581
582 // Do section data loader periodically
583 while (!SYS_server_exit)
584 {
585 if (SYS_section_list_reload)
586 {
587 SYS_section_list_reload = 0;
588
589 // Load section config
590 if (load_section_config_from_db() < 0)
591 {
592 log_error("load_section_config_from_db() error\n");
593 }
594 else
595 {
596 log_error("Reload section config successfully\n");
597 }
598 }
599
600 // Load section articles
601 last_aid = article_block_last_aid();
602 article_count = article_block_article_count();
603
604 if ((ret = append_articles_from_db(last_aid + 1, 0)) < 0)
605 {
606 log_error("append_articles_from_db(%d, 0) error\n", last_aid + 1);
607
608 if (ret == ERR_UNKNOWN_SECTION)
609 {
610 SYS_section_list_reload = 1; // Force reload section_list
611 }
612 }
613
614 load_count = article_block_article_count() - article_count;
615
616 if (load_count > 0)
617 {
618 log_std("Incrementally load %d articles, last_aid = %d\n", load_count, article_block_last_aid());
619 }
620
621 if (SYS_section_list_reload)
622 {
623 continue;
624 }
625
626 // Load article_op log
627 last_mid = last_article_op_log_mid;
628
629 if ((ret = apply_article_op_log_from_db()) < 0)
630 {
631 log_error("apply_article_op_log_from_db() error\n");
632
633 if (ret == ERR_UNKNOWN_SECTION)
634 {
635 SYS_section_list_reload = 1; // Force reload section_list
636 }
637 }
638
639 if (last_article_op_log_mid > last_mid)
640 {
641 log_std("Proceeded %d article logs, last_mid = %d\n", last_article_op_log_mid - last_mid, last_article_op_log_mid);
642 }
643
644 if (SYS_section_list_reload)
645 {
646 continue;
647 }
648
649 for (i = 0; i < SECTION_LIST_LOAD_INTERVAL && !SYS_server_exit && !SYS_section_list_reload; i++)
650 {
651 sleep(1);
652 }
653 }
654
655 // Child process exit
656
657 // Detach data pools shm
658 detach_section_list_shm();
659 detach_article_block_shm();
660 detach_trie_dict_shm();
661
662 log_std("Section list loader process exit normally\n");
663 log_end();
664
665 section_list_loader_pid = 0;
666
667 _exit(0);
668
669 return 0;
670 }
671
672 int section_list_loader_reload(void)
673 {
674 if (section_list_loader_pid == 0)
675 {
676 log_error("section_list_loader not running\n");
677 return -2;
678 }
679
680 if (kill(section_list_loader_pid, SIGHUP) < 0)
681 {
682 log_error("Send SIGTERM signal failed (%d)\n", errno);
683 return -1;
684 }
685
686 return 0;
687 }

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