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

Diff of /lbbs/src/section_list_loader.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 1.3 by sysadm, Tue May 27 00:54:59 2025 UTC Revision 1.8 by sysadm, Wed May 28 07:30:23 2025 UTC
# Line 17  Line 17 
17  #include "section_list_loader.h"  #include "section_list_loader.h"
18  #include "log.h"  #include "log.h"
19  #include "database.h"  #include "database.h"
20    #include "menu.h"
21  #include <stdio.h>  #include <stdio.h>
22  #include <string.h>  #include <string.h>
23  #include <errno.h>  #include <errno.h>
24    #include <signal.h>
25  #include <stdlib.h>  #include <stdlib.h>
26  #include <strings.h>  #include <strings.h>
27    #include <unistd.h>
28    
29    #define _POSIX_C_SOURCE 200809L
30    #include <string.h>
31    
32    #define SECTION_LIST_LOAD_INTERVAL 10 // second
33    
34    int section_list_loader_pid;
35    int last_article_op_log_mid;
36    
37  int load_section_config_from_db(void)  int load_section_config_from_db(void)
38  {  {
# Line 30  int load_section_config_from_db(void) Line 41  int load_section_config_from_db(void)
41          MYSQL_ROW row, row2;          MYSQL_ROW row, row2;
42          char sql[SQL_BUFFER_LEN];          char sql[SQL_BUFFER_LEN];
43          int32_t sid;          int32_t sid;
44          char master_name[BBS_username_max_len + 1];          char master_list[(BBS_username_max_len + 1) * 3 + 1];
45          SECTION_LIST *p_section;          SECTION_LIST *p_section;
46          int ret;          int ret;
47    
# Line 68  int load_section_config_from_db(void) Line 79  int load_section_config_from_db(void)
79                                   "SELECT username FROM section_master "                                   "SELECT username FROM section_master "
80                                   "INNER JOIN user_list ON section_master.UID = user_list.UID "                                   "INNER JOIN user_list ON section_master.UID = user_list.UID "
81                                   "WHERE SID = %d AND section_master.enable AND (NOW() BETWEEN begin_dt AND end_dt) "                                   "WHERE SID = %d AND section_master.enable AND (NOW() BETWEEN begin_dt AND end_dt) "
82                                   "ORDER BY major DESC LIMIT 1",                                   "ORDER BY major DESC, begin_dt ASC LIMIT 3",
83                                   sid);                                   sid);
84    
85                  if (mysql_query(db, sql) != 0)                  if (mysql_query(db, sql) != 0)
# Line 83  int load_section_config_from_db(void) Line 94  int load_section_config_from_db(void)
94                          ret = -3;                          ret = -3;
95                          break;                          break;
96                  }                  }
97                  if ((row2 = mysql_fetch_row(rs2)))  
98                  {                  master_list[0] = '\0';
99                          strncpy(master_name, row2[0], sizeof(master_name) - 1);                  while ((row2 = mysql_fetch_row(rs2)))
                         master_name[sizeof(master_name) - 1] = '\0';  
                 }  
                 else  
100                  {                  {
101                          master_name[0] = '\0';                          strncat(master_list, row2[0], sizeof(master_list) - 1 - strnlen(master_list, sizeof(master_list)));
102                            strncat(master_list, " ", sizeof(master_list) - 1 - strnlen(master_list, sizeof(master_list)));
103                  }                  }
104                  mysql_free_result(rs2);                  mysql_free_result(rs2);
105    
# Line 98  int load_section_config_from_db(void) Line 107  int load_section_config_from_db(void)
107    
108                  if (p_section == NULL)                  if (p_section == NULL)
109                  {                  {
110                          p_section = section_list_create(sid, row[1], row[2], "");                          p_section = section_list_create(sid, row[1], row[2], master_list);
111                          if (p_section == NULL)                          if (p_section == NULL)
112                          {                          {
113                                  log_error("section_list_create() error: load new section sid = %d sname = %s\n", sid, row[1]);                                  log_error("section_list_create() error: load new section sid = %d sname = %s\n", sid, row[1]);
# Line 126  int load_section_config_from_db(void) Line 135  int load_section_config_from_db(void)
135                          p_section->sname[sizeof(p_section->sname) - 1] = '\0';                          p_section->sname[sizeof(p_section->sname) - 1] = '\0';
136                          strncpy(p_section->stitle, row[1], sizeof(p_section->stitle) - 1);                          strncpy(p_section->stitle, row[1], sizeof(p_section->stitle) - 1);
137                          p_section->stitle[sizeof(p_section->stitle) - 1] = '\0';                          p_section->stitle[sizeof(p_section->stitle) - 1] = '\0';
138                          strncpy(p_section->master_name, master_name, sizeof(p_section->master_name) - 1);                          strncpy(p_section->master_list, master_list, sizeof(p_section->master_list) - 1);
139                          p_section->master_name[sizeof(p_section->master_name) - 1] = '\0';                          p_section->master_list[sizeof(p_section->master_list) - 1] = '\0';
140                  }                  }
141    
142                  p_section->class_id = atoi(row[3]);                  p_section->class_id = atoi(row[3]);
# Line 156  int append_articles_from_db(int32_t star Line 165  int append_articles_from_db(int32_t star
165          MYSQL_ROW row;          MYSQL_ROW row;
166          char sql[SQL_BUFFER_LEN];          char sql[SQL_BUFFER_LEN];
167          ARTICLE article;          ARTICLE article;
168            ARTICLE *p_topic;
169          SECTION_LIST *p_section = NULL;          SECTION_LIST *p_section = NULL;
170          int32_t last_sid = 0;          int32_t last_sid = 0;
171          int ret = 0;          int ret = 0;
# Line 169  int append_articles_from_db(int32_t star Line 179  int append_articles_from_db(int32_t star
179          }          }
180    
181          snprintf(sql, sizeof(sql),          snprintf(sql, sizeof(sql),
182                           "SELECT AID, TID, SID, CID, UID, visible, excerption, "                           "SELECT AID, TID, SID, CID, UID, visible, excerption, ontop, `lock`, "
183                           "ontop, `lock`, username, nickname, title, UNIX_TIMESTAMP(sub_dt) AS sub_dt "                           "transship, username, nickname, title, UNIX_TIMESTAMP(sub_dt) AS sub_dt "
184                           "FROM bbs WHERE AID >= %d ORDER BY AID",                           "FROM bbs WHERE AID >= %d ORDER BY AID",
185                           start_aid);                           start_aid);
186    
# Line 211  int append_articles_from_db(int32_t star Line 221  int append_articles_from_db(int32_t star
221                  article.excerption = (int8_t)atoi(row[i++]);                  article.excerption = (int8_t)atoi(row[i++]);
222                  article.ontop = (int8_t)atoi(row[i++]);                  article.ontop = (int8_t)atoi(row[i++]);
223                  article.lock = (int8_t)atoi(row[i++]);                  article.lock = (int8_t)atoi(row[i++]);
224                    article.transship = (int8_t)atoi(row[i++]);
225    
226                  strncpy(article.username, row[i++], sizeof(article.username) - 1);                  strncpy(article.username, row[i++], sizeof(article.username) - 1);
227                  article.username[sizeof(article.username) - 1] = '\0';                  article.username[sizeof(article.username) - 1] = '\0';
# Line 234  int append_articles_from_db(int32_t star Line 245  int append_articles_from_db(int32_t star
245                  if ((p_section = section_list_find_by_sid(article.sid)) == NULL)                  if ((p_section = section_list_find_by_sid(article.sid)) == NULL)
246                  {                  {
247                          log_error("section_list_find_by_sid(%d) error: unknown section, try reloading section config\n", article.sid);                          log_error("section_list_find_by_sid(%d) error: unknown section, try reloading section config\n", article.sid);
248                          ret = -4; // known section found                          ret = ERR_UNKNOWN_SECTION; // Unknown section found
249                          break;                          break;
250                  }                  }
251    
252                    if (article.visible != 0 && article.tid != 0)
253                    {
254                            // Check if topic article is visible
255                            p_topic = article_block_find_by_aid(article.tid);
256                            if (p_topic == NULL || p_topic->visible == 0)
257                            {
258                                    // log_error("Set article (aid = %d) as invisible due to invisible or non-existing topic head\n", article.aid);
259                                    article.tid = 0;
260                                    article.visible = 0;
261                            }
262                    }
263    
264                  // acquire lock of current section if different from last one                  // acquire lock of current section if different from last one
265                  if (!global_lock && article.sid != last_sid)                  if (!global_lock && article.sid != last_sid)
266                  {                  {
267                          if ((ret = section_list_rw_lock(NULL)) < 0)                          if ((ret = section_list_rw_lock(p_section)) < 0)
268                          {                          {
269                                  log_error("section_list_rw_lock(sid = 0) error\n");                                  log_error("section_list_rw_lock(sid = 0) error\n");
270                                  break;                                  break;
# Line 285  cleanup: Line 308  cleanup:
308    
309          return ret;          return ret;
310  }  }
311    
312    int set_last_article_op_log_from_db(void)
313    {
314            MYSQL *db;
315            MYSQL_RES *rs;
316            MYSQL_ROW row;
317            char sql[SQL_BUFFER_LEN];
318    
319            db = db_open();
320            if (db == NULL)
321            {
322                    log_error("db_open() error: %s\n", mysql_error(db));
323                    return -1;
324            }
325    
326            snprintf(sql, sizeof(sql),
327                             "SELECT MID FROM bbs_article_op ORDER BY MID DESC LIMIT 1");
328    
329            if (mysql_query(db, sql) != 0)
330            {
331                    log_error("Query article op error: %s\n", mysql_error(db));
332                    return -2;
333            }
334            if ((rs = mysql_store_result(db)) == NULL)
335            {
336                    log_error("Get article op data failed\n");
337                    return -2;
338            }
339    
340            if ((row = mysql_fetch_row(rs)))
341            {
342                    last_article_op_log_mid = atoi(row[0]);
343            }
344    
345            mysql_free_result(rs);
346    
347            mysql_close(db);
348    
349            return last_article_op_log_mid;
350    }
351    
352    int apply_article_op_log_from_db(void)
353    {
354            MYSQL *db;
355            MYSQL_RES *rs, *rs2;
356            MYSQL_ROW row, row2;
357            char sql[SQL_BUFFER_LEN];
358            ARTICLE *p_article;
359            SECTION_LIST *p_section = NULL;
360            SECTION_LIST *p_section_dest;
361            int32_t last_sid = 0;
362            int32_t sid_dest;
363            int ret = 0;
364    
365            db = db_open();
366            if (db == NULL)
367            {
368                    log_error("db_open() error: %s\n", mysql_error(db));
369                    return -3;
370            }
371    
372            snprintf(sql, sizeof(sql),
373                             "SELECT MID, AID, type FROM bbs_article_op "
374                             "WHERE MID > %d AND type NOT IN ('A', 'M') ORDER BY MID",
375                             last_article_op_log_mid);
376    
377            if (mysql_query(db, sql) != 0)
378            {
379                    log_error("Query article log error: %s\n", mysql_error(db));
380                    return -3;
381            }
382            if ((rs = mysql_store_result(db)) == NULL)
383            {
384                    log_error("Get article log data failed\n");
385                    return -3;
386            }
387    
388            while ((row = mysql_fetch_row(rs)))
389            {
390                    p_article = article_block_find_by_aid(atoi(row[1]));
391                    if (p_article == NULL) // related article has not been appended yet
392                    {
393                            ret = -2;
394                            break;
395                    }
396    
397                    // release lock of last section if different from current one
398                    if (p_article->sid != last_sid && last_sid != 0)
399                    {
400                            if ((ret = section_list_rw_unlock(p_section)) < 0)
401                            {
402                                    log_error("section_list_rw_unlock(sid = %d) error\n", p_section->sid);
403                                    break;
404                            }
405                    }
406    
407                    if ((p_section = section_list_find_by_sid(p_article->sid)) == NULL)
408                    {
409                            log_error("section_list_find_by_sid(%d) error: unknown section, try reloading section config\n", p_article->sid);
410                            ret = ERR_UNKNOWN_SECTION; // Unknown section found
411                            break;
412                    }
413    
414                    // acquire lock of current section if different from last one
415                    if (p_article->sid != last_sid)
416                    {
417                            if ((ret = section_list_rw_lock(p_section)) < 0)
418                            {
419                                    log_error("section_list_rw_lock(sid = 0) error\n");
420                                    break;
421                            }
422                    }
423    
424                    last_sid = p_article->sid;
425    
426                    switch (row[2][0])
427                    {
428                    case 'A': // Add article
429                            log_error("Operation type=A should not be found\n");
430                            break;
431                    case 'D': // Delete article
432                    case 'X': // Delete article by Admin
433                            p_article->visible = 0;
434                            if (p_article->tid == 0)
435                            {
436                                    // Set articles in the topic to be invisible
437                                    do
438                                    {
439                                            p_article = p_article->p_topic_next;
440                                            p_article->visible = 0;
441                                    } while (p_article->tid != 0);
442                            }
443                            break;
444                    case 'S': // Restore article
445                            p_article->visible = 1;
446                            break;
447                    case 'L': // Lock article
448                            p_article->lock = 1;
449                            break;
450                    case 'U': // Unlock article
451                            p_article->lock = 0;
452                            break;
453                    case 'M': // Modify article
454                            log_error("Operation type=M should not be found\n");
455                            break;
456                    case 'T': // Move article
457                            snprintf(sql, sizeof(sql),
458                                             "SELECT SID FROM bbs WHERE AID = %d",
459                                             p_article->aid);
460    
461                            if (mysql_query(db, sql) != 0)
462                            {
463                                    log_error("Query article error: %s\n", mysql_error(db));
464                                    ret = -3;
465                                    break;
466                            }
467                            if ((rs2 = mysql_store_result(db)) == NULL)
468                            {
469                                    log_error("Get article data failed\n");
470                                    ret = -3;
471                                    break;
472                            }
473                            if ((row2 = mysql_fetch_row(rs2)))
474                            {
475                                    sid_dest = atoi(row2[0]);
476                            }
477                            else
478                            {
479                                    sid_dest = 0;
480                                    ret = -4;
481                            }
482                            mysql_free_result(rs2);
483    
484                            if (sid_dest > 0 && sid_dest != p_article->sid)
485                            {
486                                    p_section_dest = section_list_find_by_sid(sid_dest);
487                                    if (p_section_dest == NULL)
488                                    {
489                                            ret = ERR_UNKNOWN_SECTION;
490                                            break;
491                                    }
492                                    // acquire lock of dest section
493                                    if ((ret = section_list_rw_lock(p_section_dest)) < 0)
494                                    {
495                                            log_error("section_list_rw_lock(sid = %d) error\n", p_section_dest);
496                                            break;
497                                    }
498                                    // Move topic
499                                    if ((ret = section_list_move_topic(p_section, p_section_dest, p_article->aid)) < 0)
500                                    {
501                                            log_error("section_list_move_topic(src_sid=%d, dest_sid=%d, aid=%d) error (%d), retry in the next loop\n",
502                                                              p_section->sid, p_section_dest->sid, p_article->aid, ret);
503                                    }
504                                    // release lock of dest section
505                                    if (section_list_rw_unlock(p_section_dest) < 0)
506                                    {
507                                            log_error("section_list_rw_unlock(sid = %d) error\n", p_section_dest);
508                                            ret = -1;
509                                    }
510                            }
511                            break;
512                    case 'E': // Set article as excerption
513                            p_article->excerption = 1;
514                            break;
515                    case 'O': // Unset article as excerption
516                            p_article->excerption = 0;
517                            break;
518                    case 'F': // Set article on top
519                            p_article->ontop = 1;
520                            break;
521                    case 'V': // Unset article on top
522                            p_article->ontop = 0;
523                            break;
524                    case 'Z': // Set article as trnasship
525                            p_article->transship = 1;
526                            break;
527                    default:
528                            // log_error("Operation type=%s unknown, mid=%s\n", row[2], row[0]);
529                            break;
530                    }
531    
532                    if (ret < 0)
533                    {
534                            break;
535                    }
536    
537                    // Update MID with last successfully proceeded article_op_log
538                    last_article_op_log_mid = atoi(row[0]);
539            }
540    
541            // release lock of last section
542            if (last_sid != 0)
543            {
544                    if ((ret = section_list_rw_unlock(p_section)) < 0)
545                    {
546                            log_error("section_list_rw_unlock(sid = %d) error\n", p_section->sid);
547                    }
548            }
549    
550            mysql_free_result(rs);
551    
552            mysql_close(db);
553    
554            return ret;
555    }
556    
557    int section_list_loader_launch(void)
558    {
559            int pid;
560            int ret;
561            int32_t last_aid;
562            int article_count;
563            int load_count;
564            int last_mid;
565            int i;
566    
567            if (section_list_loader_pid != 0)
568            {
569                    log_error("section_list_loader already running, pid = %d\n", section_list_loader_pid);
570                    return -2;
571            }
572    
573            pid = fork();
574    
575            if (pid > 0) // Parent process
576            {
577                    SYS_child_process_count++;
578                    section_list_loader_pid = pid;
579                    log_std("Section list loader process (%d) start\n", pid);
580                    return 0;
581            }
582            else if (pid < 0) // Error
583            {
584                    log_error("fork() error (%d)\n", errno);
585                    return -1;
586            }
587    
588            // Child process
589            SYS_child_process_count = 0;
590    
591            // Detach menu in shared memory
592            detach_menu_shm(p_bbs_menu);
593            free(p_bbs_menu);
594            p_bbs_menu = NULL;
595    
596            // Do section data loader periodically
597            while (!SYS_server_exit)
598            {
599                    if (SYS_section_list_reload)
600                    {
601                            SYS_section_list_reload = 0;
602    
603                            // Load section config
604                            if (load_section_config_from_db() < 0)
605                            {
606                                    log_error("load_section_config_from_db() error\n");
607                            }
608                            else
609                            {
610                                    log_error("Reload section config successfully\n");
611                            }
612                    }
613    
614                    // Load section articles
615                    last_aid = article_block_last_aid();
616                    article_count = article_block_article_count();
617    
618                    if ((ret = append_articles_from_db(last_aid + 1, 0)) < 0)
619                    {
620                            log_error("append_articles_from_db(%d, 0) error\n", last_aid + 1);
621    
622                            if (ret == ERR_UNKNOWN_SECTION)
623                            {
624                                    SYS_section_list_reload = 1; // Force reload section_list
625                            }
626                    }
627    
628                    load_count = article_block_article_count() - article_count;
629    
630                    if (load_count > 0)
631                    {
632                            log_std("Incrementally load %d articles, last_aid = %d\n", load_count, article_block_last_aid());
633                    }
634    
635                    if (SYS_section_list_reload)
636                    {
637                            continue;
638                    }
639    
640                    // Load article_op log
641                    last_mid = last_article_op_log_mid;
642    
643                    if ((ret = apply_article_op_log_from_db()) < 0)
644                    {
645                            log_error("apply_article_op_log_from_db() error\n");
646    
647                            if (ret == ERR_UNKNOWN_SECTION)
648                            {
649                                    SYS_section_list_reload = 1; // Force reload section_list
650                            }
651                    }
652    
653                    if (last_article_op_log_mid > last_mid)
654                    {
655                            log_std("Proceeded %d article logs, last_mid = %d\n", last_article_op_log_mid - last_mid, last_article_op_log_mid);
656                    }
657    
658                    if (SYS_section_list_reload)
659                    {
660                            continue;
661                    }
662    
663                    for (i = 0; i < SECTION_LIST_LOAD_INTERVAL && !SYS_server_exit && !SYS_section_list_reload; i++)
664                    {
665                            sleep(1);
666                    }
667            }
668    
669            // Child process exit
670    
671            // Detach data pools shm
672            detach_section_list_shm();
673            detach_article_block_shm();
674            detach_trie_dict_shm();
675    
676            log_std("Section list loader process exit normally\n");
677            log_end();
678    
679            section_list_loader_pid = 0;
680    
681            _exit(0);
682    
683            return 0;
684    }
685    
686    int section_list_loader_reload(void)
687    {
688            if (section_list_loader_pid == 0)
689            {
690                    log_error("section_list_loader not running\n");
691                    return -2;
692            }
693    
694            if (kill(section_list_loader_pid, SIGHUP) < 0)
695            {
696                    log_error("Send SIGTERM signal failed (%d)\n", errno);
697                    return -1;
698            }
699    
700            return 0;
701    }
702    
703    int query_section_articles(SECTION_LIST *p_section, int32_t page_id, ARTICLE *p_articles[], int32_t *p_article_count)
704    {
705            ARTICLE *p_article;
706            ARTICLE *p_next_page_first_article;
707            int ret = 0;
708    
709            if (p_section == NULL || p_articles == NULL || p_article_count == NULL)
710            {
711                    log_error("query_section_articles() NULL pointer error\n");
712                    return -1;
713            }
714    
715            // acquire lock of section
716            if ((ret = section_list_rd_lock(p_section)) < 0)
717            {
718                    log_error("section_list_rd_lock(sid = %d) error\n", p_section->sid);
719                    return -2;
720            }
721    
722            if (page_id < 0 || page_id >= p_section->page_count)
723            {
724                    log_error("Invalid page_id=%d, not in range [0, %d)\n", page_id, p_section->page_count);
725                    ret = -3;
726            }
727            else
728            {
729                    ret = page_id;
730                    p_article = p_section->p_page_first_article[page_id];
731                    p_next_page_first_article =
732                            (page_id == p_section->page_count - 1 ? p_section->p_article_head : p_section->p_page_first_article[page_id + 1]);
733                    *p_article_count = 0;
734    
735                    do
736                    {
737                            if (p_article->visible)
738                            {
739                                    p_articles[*p_article_count] = p_article;
740                                    (*p_article_count)++;
741                            }
742                            p_article = p_article->p_next;
743                    } while (p_article != p_next_page_first_article && (*p_article_count) <= BBS_article_limit_per_page);
744    
745                    if (*p_article_count != (page_id < p_section->page_count - 1 ? BBS_article_limit_per_page : p_section->last_page_visible_article_count))
746                    {
747                            log_error("Inconsistent visible article count %d detected in section %d page %d\n", *p_article_count, p_section->sid, page_id);
748                    }
749            }
750    
751            // release lock of section
752            if (section_list_rd_unlock(p_section) < 0)
753            {
754                    log_error("section_list_rd_unlock(sid = %d) error\n", p_section->sid);
755                    ret = -2;
756            }
757    
758            return ret;
759    }


Legend:
Removed lines/characters  
Changed lines/characters
  Added lines/characters

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