/[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.6 - (hide annotations)
Tue May 27 09:33:11 2025 UTC (9 months, 2 weeks ago) by sysadm
Branch: MAIN
Changes since 1.5: +14 -1 lines
Content type: text/x-csrc
Refine error handling

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 sysadm 1.6 // acquire lock of dest section
492     if ((ret = section_list_rw_lock(p_section_dest)) < 0)
493     {
494     log_error("section_list_rw_lock(sid = %d) error\n", p_section_dest);
495     break;
496     }
497 sysadm 1.5 // Move topic
498     if ((ret = section_list_move_topic(p_section, p_section_dest, p_article->aid)) < 0)
499     {
500 sysadm 1.6 log_error("section_list_move_topic(src_sid=%d, dest_sid=%d, aid=%d) error (%d), retry in the next loop\n",
501     p_section->sid, p_section_dest->sid, p_article->aid, ret);
502     }
503     // release lock of dest section
504     if (section_list_rw_unlock(p_section_dest) < 0)
505     {
506     log_error("section_list_rw_unlock(sid = %d) error\n", p_section_dest);
507     ret = -1;
508 sysadm 1.5 }
509     }
510     break;
511     case 'E': // Set article as excerption
512     p_article->excerption = 1;
513     break;
514     case 'O': // Unset article as excerption
515     p_article->excerption = 0;
516     break;
517     case 'F': // Set article on top
518     p_article->ontop = 1;
519     break;
520     case 'V': // Unset article on top
521     p_article->ontop = 0;
522     break;
523     case 'Z': // Set article as trnasship
524     p_article->transship = 1;
525     break;
526     default:
527     // log_error("Operation type=%s unknown, mid=%s\n", row[2], row[0]);
528     break;
529     }
530    
531     if (ret < 0)
532     {
533     break;
534     }
535    
536     // Update MID with last successfully proceeded article_op_log
537     last_article_op_log_mid = atoi(row[0]);
538     }
539    
540     // release lock of last section
541     if (last_sid != 0)
542     {
543     if ((ret = section_list_rw_unlock(p_section)) < 0)
544     {
545     log_error("section_list_rw_unlock(sid = %d) error\n", p_section->sid);
546     }
547     }
548    
549     mysql_free_result(rs);
550    
551     mysql_close(db);
552    
553     return ret;
554     }
555    
556 sysadm 1.4 int section_list_loader_launch(void)
557     {
558     int pid;
559     int ret;
560     int32_t last_aid;
561     int article_count;
562     int load_count;
563 sysadm 1.5 int last_mid;
564 sysadm 1.4 int i;
565    
566     if (section_list_loader_pid != 0)
567     {
568     log_error("section_list_loader already running, pid = %d\n", section_list_loader_pid);
569     return -2;
570     }
571    
572     pid = fork();
573    
574     if (pid > 0) // Parent process
575     {
576     SYS_child_process_count++;
577     section_list_loader_pid = pid;
578     log_std("Section list loader process (%d) start\n", pid);
579     return 0;
580     }
581     else if (pid < 0) // Error
582     {
583     log_error("fork() error (%d)\n", errno);
584     return -1;
585     }
586    
587     // Child process
588     SYS_child_process_count = 0;
589    
590     // Detach menu in shared memory
591     detach_menu_shm(p_bbs_menu);
592     free(p_bbs_menu);
593     p_bbs_menu = NULL;
594    
595     // Do section data loader periodically
596     while (!SYS_server_exit)
597     {
598     if (SYS_section_list_reload)
599     {
600     SYS_section_list_reload = 0;
601    
602     // Load section config
603     if (load_section_config_from_db() < 0)
604     {
605     log_error("load_section_config_from_db() error\n");
606     }
607     else
608     {
609     log_error("Reload section config successfully\n");
610     }
611     }
612    
613     // Load section articles
614     last_aid = article_block_last_aid();
615     article_count = article_block_article_count();
616    
617     if ((ret = append_articles_from_db(last_aid + 1, 0)) < 0)
618     {
619     log_error("append_articles_from_db(%d, 0) error\n", last_aid + 1);
620    
621     if (ret == ERR_UNKNOWN_SECTION)
622     {
623     SYS_section_list_reload = 1; // Force reload section_list
624     }
625     }
626 sysadm 1.5
627     load_count = article_block_article_count() - article_count;
628    
629     if (load_count > 0)
630     {
631     log_std("Incrementally load %d articles, last_aid = %d\n", load_count, article_block_last_aid());
632     }
633    
634     if (SYS_section_list_reload)
635     {
636     continue;
637     }
638    
639     // Load article_op log
640     last_mid = last_article_op_log_mid;
641    
642     if ((ret = apply_article_op_log_from_db()) < 0)
643 sysadm 1.4 {
644 sysadm 1.5 log_error("apply_article_op_log_from_db() error\n");
645 sysadm 1.4
646 sysadm 1.5 if (ret == ERR_UNKNOWN_SECTION)
647 sysadm 1.4 {
648 sysadm 1.5 SYS_section_list_reload = 1; // Force reload section_list
649 sysadm 1.4 }
650 sysadm 1.5 }
651 sysadm 1.4
652 sysadm 1.5 if (last_article_op_log_mid > last_mid)
653     {
654     log_std("Proceeded %d article logs, last_mid = %d\n", last_article_op_log_mid - last_mid, last_article_op_log_mid);
655     }
656    
657     if (SYS_section_list_reload)
658     {
659     continue;
660     }
661    
662     for (i = 0; i < SECTION_LIST_LOAD_INTERVAL && !SYS_server_exit && !SYS_section_list_reload; i++)
663     {
664     sleep(1);
665 sysadm 1.4 }
666     }
667    
668     // Child process exit
669    
670     // Detach data pools shm
671     detach_section_list_shm();
672     detach_article_block_shm();
673     detach_trie_dict_shm();
674    
675     log_std("Section list loader process exit normally\n");
676     log_end();
677    
678     section_list_loader_pid = 0;
679    
680     _exit(0);
681    
682     return 0;
683     }
684    
685     int section_list_loader_reload(void)
686     {
687     if (section_list_loader_pid == 0)
688     {
689     log_error("section_list_loader not running\n");
690     return -2;
691     }
692    
693     if (kill(section_list_loader_pid, SIGHUP) < 0)
694     {
695     log_error("Send SIGTERM signal failed (%d)\n", errno);
696     return -1;
697     }
698    
699     return 0;
700     }

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