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

Annotation of /lbbs/src/section_list_loader.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.5 - (hide 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 sysadm 1.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 sysadm 1.4 #include "menu.h"
21 sysadm 1.1 #include <stdio.h>
22     #include <string.h>
23     #include <errno.h>
24 sysadm 1.4 #include <signal.h>
25 sysadm 1.1 #include <stdlib.h>
26 sysadm 1.3 #include <strings.h>
27 sysadm 1.4 #include <unistd.h>
28    
29     #define SECTION_LIST_LOAD_INTERVAL 10 // second
30    
31 sysadm 1.5 int section_list_loader_pid;
32     int last_article_op_log_mid;
33 sysadm 1.1
34 sysadm 1.2 int load_section_config_from_db(void)
35 sysadm 1.1 {
36 sysadm 1.2 MYSQL *db;
37 sysadm 1.1 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 sysadm 1.2 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 sysadm 1.1 snprintf(sql, sizeof(sql),
53 sysadm 1.2 "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 sysadm 1.1 "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 sysadm 1.2 return -3;
62 sysadm 1.1 }
63     if ((rs = mysql_store_result(db)) == NULL)
64     {
65     log_error("Get section_list data failed\n");
66 sysadm 1.2 return -3;
67 sysadm 1.1 }
68 sysadm 1.2
69     ret = 0;
70 sysadm 1.1 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 sysadm 1.2 ret = -3;
86     break;
87 sysadm 1.1 }
88     if ((rs2 = mysql_store_result(db)) == NULL)
89     {
90     log_error("Get section_master data failed\n");
91 sysadm 1.2 ret = -3;
92     break;
93 sysadm 1.1 }
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 sysadm 1.2 log_error("section_list_create() error: load new section sid = %d sname = %s\n", sid, row[1]);
113     ret = -4;
114 sysadm 1.1 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 sysadm 1.2 mysql_close(db);
156    
157     return ret;
158 sysadm 1.1 }
159 sysadm 1.3
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 sysadm 1.5 ARTICLE *p_topic;
168 sysadm 1.3 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 sysadm 1.5 "SELECT AID, TID, SID, CID, UID, visible, excerption, ontop, `lock`, "
182     "transship, username, nickname, title, UNIX_TIMESTAMP(sub_dt) AS sub_dt "
183 sysadm 1.3 "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 sysadm 1.5 article.transship = (int8_t)atoi(row[i++]);
224 sysadm 1.3
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 sysadm 1.4 ret = ERR_UNKNOWN_SECTION; // Unknown section found
248 sysadm 1.3 break;
249     }
250    
251 sysadm 1.5 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 sysadm 1.3 // acquire lock of current section if different from last one
264     if (!global_lock && article.sid != last_sid)
265     {
266 sysadm 1.4 if ((ret = section_list_rw_lock(p_section)) < 0)
267 sysadm 1.3 {
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 sysadm 1.4
311 sysadm 1.5 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 sysadm 1.4 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 sysadm 1.5 int last_mid;
551 sysadm 1.4 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 sysadm 1.5
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 sysadm 1.4 {
631 sysadm 1.5 log_error("apply_article_op_log_from_db() error\n");
632 sysadm 1.4
633 sysadm 1.5 if (ret == ERR_UNKNOWN_SECTION)
634 sysadm 1.4 {
635 sysadm 1.5 SYS_section_list_reload = 1; // Force reload section_list
636 sysadm 1.4 }
637 sysadm 1.5 }
638 sysadm 1.4
639 sysadm 1.5 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 sysadm 1.4 }
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