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

Annotation of /lbbs/src/section_list.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.54 - (hide annotations)
Wed Nov 5 03:01:14 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.53: +8 -8 lines
Content type: text/x-csrc
Use enum / const int instead of macro define constant integers

1 sysadm 1.51 /* SPDX-License-Identifier: GPL-3.0-or-later */
2     /*
3     * section_list
4     * - data models and basic operations of section and article
5     *
6 sysadm 1.53 * Copyright (C) 2004-2025 Leaflet <leaflet@leafok.com>
7 sysadm 1.51 */
8 sysadm 1.1
9 sysadm 1.34 #include "log.h"
10 sysadm 1.1 #include "section_list.h"
11     #include "trie_dict.h"
12 sysadm 1.46 #include "user_list.h"
13 sysadm 1.34 #include <errno.h>
14     #include <signal.h>
15 sysadm 1.1 #include <stdio.h>
16 sysadm 1.34 #include <stdlib.h>
17 sysadm 1.1 #include <string.h>
18     #include <unistd.h>
19 sysadm 1.34 #include <sys/ipc.h>
20 sysadm 1.7 #include <sys/param.h>
21 sysadm 1.16 #include <sys/sem.h>
22 sysadm 1.1 #include <sys/shm.h>
23    
24 sysadm 1.17 #ifdef _SEM_SEMUN_UNDEFINED
25     union semun
26     {
27     int val; /* Value for SETVAL */
28     struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
29     unsigned short *array; /* Array for GETALL, SETALL */
30     struct seminfo *__buf; /* Buffer for IPC_INFO
31     (Linux-specific) */
32     };
33     #endif // #ifdef _SEM_SEMUN_UNDEFINED
34    
35 sysadm 1.23 #define SECTION_TRY_LOCK_WAIT_TIME 1 // second
36     #define SECTION_TRY_LOCK_TIMES 10
37    
38 sysadm 1.35 #define ARTICLE_BLOCK_PER_SHM 1000 // sizeof(ARTICLE_BLOCK) * ARTICLE_BLOCK_PER_SHM is the size of each shm segment to allocate
39 sysadm 1.33 #define ARTICLE_BLOCK_SHM_COUNT_LIMIT 80 // limited by length (8-bit) of proj_id in ftok(path, proj_id)
40 sysadm 1.7 #define ARTICLE_BLOCK_PER_POOL (ARTICLE_BLOCK_PER_SHM * ARTICLE_BLOCK_SHM_COUNT_LIMIT)
41    
42 sysadm 1.31 #define CALCULATE_PAGE_THRESHOLD 100 // Adjust to tune performance of moving topic between sections
43 sysadm 1.12
44 sysadm 1.14 #define SID_STR_LEN 5 // 32-bit + NULL
45    
46 sysadm 1.7 struct article_block_t
47     {
48 sysadm 1.54 ARTICLE articles[BBS_article_count_per_block];
49 sysadm 1.22 int article_count;
50 sysadm 1.7 struct article_block_t *p_next_block;
51     };
52     typedef struct article_block_t ARTICLE_BLOCK;
53 sysadm 1.1
54 sysadm 1.22 struct article_block_pool_t
55 sysadm 1.1 {
56     int shmid;
57 sysadm 1.22 struct
58     {
59     int shmid;
60     void *p_shm;
61     } shm_pool[ARTICLE_BLOCK_SHM_COUNT_LIMIT];
62 sysadm 1.7 int shm_count;
63     ARTICLE_BLOCK *p_block_free_list;
64     ARTICLE_BLOCK *p_block[ARTICLE_BLOCK_PER_POOL];
65 sysadm 1.22 int block_count;
66 sysadm 1.7 };
67     typedef struct article_block_pool_t ARTICLE_BLOCK_POOL;
68    
69     static ARTICLE_BLOCK_POOL *p_article_block_pool = NULL;
70 sysadm 1.1
71 sysadm 1.38 SECTION_LIST_POOL *p_section_list_pool = NULL;
72 sysadm 1.7
73     int article_block_init(const char *filename, int block_count)
74 sysadm 1.1 {
75     int shmid;
76     int proj_id;
77     key_t key;
78     size_t size;
79     void *p_shm;
80     int i;
81 sysadm 1.7 int block_count_in_shm;
82     ARTICLE_BLOCK *p_block_in_shm;
83     ARTICLE_BLOCK **pp_block_next;
84    
85     if (p_article_block_pool != NULL)
86 sysadm 1.1 {
87 sysadm 1.7 log_error("article_block_pool already initialized\n");
88 sysadm 1.1 return -1;
89     }
90    
91 sysadm 1.27 if (block_count <= 0 || block_count > ARTICLE_BLOCK_PER_POOL)
92 sysadm 1.1 {
93 sysadm 1.7 log_error("article_block_count exceed limit %d\n", ARTICLE_BLOCK_PER_POOL);
94 sysadm 1.1 return -2;
95     }
96    
97 sysadm 1.18 // Allocate shared memory
98     proj_id = ARTICLE_BLOCK_SHM_COUNT_LIMIT; // keep different from proj_id used to create block shm
99     key = ftok(filename, proj_id);
100     if (key == -1)
101     {
102     log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
103     return -3;
104     }
105    
106     size = sizeof(ARTICLE_BLOCK_POOL);
107     shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
108     if (shmid == -1)
109     {
110     log_error("shmget(article_block_pool_shm, size = %d) error (%d)\n", size, errno);
111     return -3;
112     }
113     p_shm = shmat(shmid, NULL, 0);
114     if (p_shm == (void *)-1)
115 sysadm 1.1 {
116 sysadm 1.18 log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
117     return -3;
118 sysadm 1.1 }
119    
120 sysadm 1.18 p_article_block_pool = p_shm;
121 sysadm 1.22 p_article_block_pool->shmid = shmid;
122 sysadm 1.18
123 sysadm 1.7 p_article_block_pool->shm_count = 0;
124     pp_block_next = &(p_article_block_pool->p_block_free_list);
125 sysadm 1.1
126 sysadm 1.7 while (block_count > 0)
127 sysadm 1.1 {
128 sysadm 1.7 block_count_in_shm = MIN(block_count, ARTICLE_BLOCK_PER_SHM);
129     block_count -= block_count_in_shm;
130 sysadm 1.1
131 sysadm 1.18 proj_id = p_article_block_pool->shm_count;
132 sysadm 1.1 key = ftok(filename, proj_id);
133     if (key == -1)
134     {
135     log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
136 sysadm 1.7 article_block_cleanup();
137 sysadm 1.1 return -3;
138     }
139    
140 sysadm 1.19 size = sizeof(ARTICLE_BLOCK) * (size_t)block_count_in_shm;
141 sysadm 1.1 shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
142     if (shmid == -1)
143     {
144 sysadm 1.7 log_error("shmget(shm_index = %d, size = %d) error (%d)\n", p_article_block_pool->shm_count, size, errno);
145     article_block_cleanup();
146 sysadm 1.1 return -3;
147     }
148     p_shm = shmat(shmid, NULL, 0);
149     if (p_shm == (void *)-1)
150     {
151     log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
152 sysadm 1.7 article_block_cleanup();
153 sysadm 1.1 return -3;
154     }
155    
156 sysadm 1.7 (p_article_block_pool->shm_pool + p_article_block_pool->shm_count)->shmid = shmid;
157     (p_article_block_pool->shm_pool + p_article_block_pool->shm_count)->p_shm = p_shm;
158     p_article_block_pool->shm_count++;
159 sysadm 1.1
160 sysadm 1.7 p_block_in_shm = p_shm;
161     *pp_block_next = p_block_in_shm;
162 sysadm 1.1
163 sysadm 1.7 for (i = 0; i < block_count_in_shm; i++)
164 sysadm 1.1 {
165 sysadm 1.7 if (i < block_count_in_shm - 1)
166 sysadm 1.1 {
167 sysadm 1.7 (p_block_in_shm + i)->p_next_block = (p_block_in_shm + i + 1);
168 sysadm 1.1 }
169     else
170     {
171 sysadm 1.7 (p_block_in_shm + i)->p_next_block = NULL;
172     pp_block_next = &((p_block_in_shm + i)->p_next_block);
173 sysadm 1.1 }
174     }
175     }
176    
177 sysadm 1.7 p_article_block_pool->block_count = 0;
178    
179 sysadm 1.1 return 0;
180     }
181    
182 sysadm 1.7 void article_block_cleanup(void)
183     {
184 sysadm 1.22 int shmid;
185    
186 sysadm 1.18 if (p_article_block_pool == NULL)
187     {
188     return;
189     }
190    
191     for (int i = 0; i < p_article_block_pool->shm_count; i++)
192 sysadm 1.7 {
193 sysadm 1.18 if (shmdt((p_article_block_pool->shm_pool + i)->p_shm) == -1)
194 sysadm 1.7 {
195 sysadm 1.18 log_error("shmdt(shmid = %d) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);
196     }
197 sysadm 1.7
198 sysadm 1.18 if (shmctl((p_article_block_pool->shm_pool + i)->shmid, IPC_RMID, NULL) == -1)
199     {
200     log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);
201 sysadm 1.7 }
202 sysadm 1.18 }
203    
204 sysadm 1.22 shmid = p_article_block_pool->shmid;
205    
206 sysadm 1.18 if (shmdt(p_article_block_pool) == -1)
207     {
208 sysadm 1.22 log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
209 sysadm 1.18 }
210 sysadm 1.7
211 sysadm 1.52 if (shmid != 0 && shmctl(shmid, IPC_RMID, NULL) == -1)
212 sysadm 1.18 {
213 sysadm 1.22 log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", shmid, errno);
214 sysadm 1.7 }
215 sysadm 1.18
216     p_article_block_pool = NULL;
217 sysadm 1.7 }
218    
219 sysadm 1.26 int set_article_block_shm_readonly(void)
220     {
221     int shmid;
222     void *p_shm;
223     int i;
224    
225     if (p_article_block_pool == NULL)
226     {
227     log_error("article_block_pool not initialized\n");
228     return -1;
229     }
230    
231     for (i = 0; i < p_article_block_pool->shm_count; i++)
232     {
233     shmid = (p_article_block_pool->shm_pool + i)->shmid;
234    
235     // Remap shared memory in read-only mode
236     p_shm = shmat(shmid, (p_article_block_pool->shm_pool + i)->p_shm, SHM_RDONLY | SHM_REMAP);
237     if (p_shm == (void *)-1)
238     {
239     log_error("shmat(article_block_pool shmid = %d) error (%d)\n", shmid, errno);
240     return -2;
241     }
242     }
243    
244     return 0;
245     }
246    
247     int detach_article_block_shm(void)
248     {
249     int shmid;
250    
251     if (p_article_block_pool == NULL)
252     {
253     return -1;
254     }
255    
256     for (int i = 0; i < p_article_block_pool->shm_count; i++)
257     {
258     if ((p_article_block_pool->shm_pool + i)->p_shm != NULL && shmdt((p_article_block_pool->shm_pool + i)->p_shm) == -1)
259     {
260     log_error("shmdt(shmid = %d) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);
261     return -2;
262     }
263     }
264    
265     shmid = p_article_block_pool->shmid;
266    
267     if (shmdt(p_article_block_pool) == -1)
268     {
269     log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
270     return -3;
271     }
272    
273     p_article_block_pool = NULL;
274    
275     return 0;
276     }
277    
278 sysadm 1.7 inline static ARTICLE_BLOCK *pop_free_article_block(void)
279     {
280     ARTICLE_BLOCK *p_block = NULL;
281    
282     if (p_article_block_pool->p_block_free_list != NULL)
283     {
284     p_block = p_article_block_pool->p_block_free_list;
285     p_article_block_pool->p_block_free_list = p_block->p_next_block;
286     p_block->p_next_block = NULL;
287     p_block->article_count = 0;
288     }
289    
290     return p_block;
291     }
292    
293     inline static void push_free_article_block(ARTICLE_BLOCK *p_block)
294 sysadm 1.1 {
295 sysadm 1.7 p_block->p_next_block = p_article_block_pool->p_block_free_list;
296     p_article_block_pool->p_block_free_list = p_block;
297     }
298    
299     int article_block_reset(void)
300     {
301     ARTICLE_BLOCK *p_block;
302    
303     if (p_article_block_pool == NULL)
304     {
305     log_error("article_block_pool not initialized\n");
306     return -1;
307     }
308 sysadm 1.1
309 sysadm 1.7 while (p_article_block_pool->block_count > 0)
310 sysadm 1.1 {
311 sysadm 1.7 p_article_block_pool->block_count--;
312     p_block = p_article_block_pool->p_block[p_article_block_pool->block_count];
313     push_free_article_block(p_block);
314 sysadm 1.1 }
315    
316 sysadm 1.7 return 0;
317     }
318    
319     ARTICLE *article_block_find_by_aid(int32_t aid)
320     {
321     ARTICLE_BLOCK *p_block;
322     int left;
323     int right;
324     int mid;
325    
326     if (p_article_block_pool == NULL)
327 sysadm 1.1 {
328 sysadm 1.7 log_error("article_block_pool not initialized\n");
329     return NULL;
330 sysadm 1.1 }
331    
332 sysadm 1.7 if (p_article_block_pool->block_count == 0) // empty
333 sysadm 1.1 {
334 sysadm 1.7 return NULL;
335 sysadm 1.1 }
336    
337 sysadm 1.7 left = 0;
338 sysadm 1.47 right = p_article_block_pool->block_count - 1;
339 sysadm 1.7
340 sysadm 1.47 // aid in the range [ head aid of blocks[left], tail aid of blocks[right] ]
341     while (left < right)
342 sysadm 1.1 {
343 sysadm 1.7 // get block offset no less than mid value of left and right block offsets
344 sysadm 1.47 mid = (left + right) / 2 + (left + right) % 2;
345 sysadm 1.1
346 sysadm 1.7 if (aid < p_article_block_pool->p_block[mid]->articles[0].aid)
347     {
348 sysadm 1.47 right = mid - 1;
349 sysadm 1.7 }
350 sysadm 1.47 else // if (aid >= p_article_block_pool->p_block[mid]->articles[0].aid)
351 sysadm 1.7 {
352     left = mid;
353 sysadm 1.1 }
354 sysadm 1.7 }
355    
356     p_block = p_article_block_pool->p_block[left];
357    
358     left = 0;
359     right = p_block->article_count - 1;
360    
361     // aid in the range [ aid of articles[left], aid of articles[right] ]
362     while (left < right)
363     {
364     mid = (left + right) / 2;
365 sysadm 1.1
366 sysadm 1.7 if (aid <= p_block->articles[mid].aid)
367     {
368     right = mid;
369     }
370     else
371     {
372     left = mid + 1;
373     }
374 sysadm 1.1 }
375 sysadm 1.7
376 sysadm 1.28 if (aid != p_block->articles[left].aid) // not found
377     {
378     return NULL;
379     }
380    
381     return (p_block->articles + left); // found
382 sysadm 1.1 }
383    
384 sysadm 1.7 ARTICLE *article_block_find_by_index(int index)
385 sysadm 1.1 {
386 sysadm 1.7 ARTICLE_BLOCK *p_block;
387    
388     if (p_article_block_pool == NULL)
389     {
390     log_error("article_block_pool not initialized\n");
391     return NULL;
392     }
393    
394 sysadm 1.54 if (index < 0 || index / BBS_article_count_per_block >= p_article_block_pool->block_count)
395 sysadm 1.7 {
396 sysadm 1.14 log_error("article_block_find_by_index(%d) is out of boundary of block [0, %d)\n", index, p_article_block_pool->block_count);
397 sysadm 1.7 return NULL;
398     }
399    
400 sysadm 1.54 p_block = p_article_block_pool->p_block[index / BBS_article_count_per_block];
401 sysadm 1.1
402 sysadm 1.54 if (index % BBS_article_count_per_block >= p_block->article_count)
403 sysadm 1.1 {
404 sysadm 1.14 log_error("article_block_find_by_index(%d) is out of boundary of article [0, %d)\n", index, p_block->article_count);
405 sysadm 1.7 return NULL;
406 sysadm 1.1 }
407    
408 sysadm 1.54 return (p_block->articles + (index % BBS_article_count_per_block));
409 sysadm 1.1 }
410    
411 sysadm 1.19 extern int section_list_init(const char *filename)
412 sysadm 1.1 {
413 sysadm 1.16 int semid;
414 sysadm 1.13 int shmid;
415     int proj_id;
416     key_t key;
417     size_t size;
418     void *p_shm;
419 sysadm 1.17 union semun arg;
420     int i;
421 sysadm 1.13
422 sysadm 1.22 if (p_section_list_pool != NULL)
423 sysadm 1.13 {
424 sysadm 1.19 section_list_cleanup();
425 sysadm 1.13 }
426 sysadm 1.7
427 sysadm 1.13 proj_id = (int)(time(NULL) % getpid());
428     key = ftok(filename, proj_id);
429     if (key == -1)
430     {
431     log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
432     return -3;
433     }
434 sysadm 1.1
435 sysadm 1.22 // Allocate shared memory
436     size = sizeof(SECTION_LIST_POOL);
437     shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
438     if (shmid == -1)
439     {
440     log_error("shmget(section_list_pool_shm, size = %d) error (%d)\n", size, errno);
441     return -3;
442     }
443     p_shm = shmat(shmid, NULL, 0);
444     if (p_shm == (void *)-1)
445     {
446     log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
447     return -3;
448     }
449    
450     p_section_list_pool = p_shm;
451     p_section_list_pool->shmid = shmid;
452     p_section_list_pool->section_count = 0;
453    
454     // Allocate semaphore as section locks
455 sysadm 1.16 size = 2 * (BBS_max_section + 1); // r_sem and w_sem per section, the last pair for all sections
456     semid = semget(key, (int)size, IPC_CREAT | IPC_EXCL | 0600);
457     if (semid == -1)
458     {
459 sysadm 1.17 log_error("semget(section_list_pool_sem, size = %d) error (%d)\n", size, errno);
460 sysadm 1.16 return -3;
461     }
462    
463 sysadm 1.17 // Initialize sem value to 0
464     arg.val = 0;
465     for (i = 0; i < size; i++)
466     {
467     if (semctl(semid, i, SETVAL, arg) == -1)
468     {
469     log_error("semctl(section_list_pool_sem, SETVAL) error (%d)\n", errno);
470     return -3;
471     }
472     }
473    
474 sysadm 1.22 p_section_list_pool->semid = semid;
475 sysadm 1.16
476 sysadm 1.22 p_section_list_pool->p_trie_dict_section_by_name = trie_dict_create();
477     if (p_section_list_pool->p_trie_dict_section_by_name == NULL)
478 sysadm 1.14 {
479     log_error("trie_dict_create() OOM\n", BBS_max_section);
480     return -2;
481     }
482    
483 sysadm 1.22 p_section_list_pool->p_trie_dict_section_by_sid = trie_dict_create();
484     if (p_section_list_pool->p_trie_dict_section_by_sid == NULL)
485 sysadm 1.1 {
486 sysadm 1.13 log_error("trie_dict_create() OOM\n", BBS_max_section);
487     return -2;
488     }
489    
490     return 0;
491     }
492    
493 sysadm 1.26 void section_list_cleanup(void)
494     {
495     int shmid;
496    
497     if (p_section_list_pool == NULL)
498     {
499     return;
500     }
501    
502     if (p_section_list_pool->p_trie_dict_section_by_name != NULL)
503     {
504     trie_dict_destroy(p_section_list_pool->p_trie_dict_section_by_name);
505     p_section_list_pool->p_trie_dict_section_by_name = NULL;
506     }
507    
508     if (p_section_list_pool->p_trie_dict_section_by_sid != NULL)
509     {
510     trie_dict_destroy(p_section_list_pool->p_trie_dict_section_by_sid);
511     p_section_list_pool->p_trie_dict_section_by_sid = NULL;
512     }
513    
514     shmid = p_section_list_pool->shmid;
515    
516     if (semctl(p_section_list_pool->semid, 0, IPC_RMID) == -1)
517     {
518     log_error("semctl(semid = %d, IPC_RMID) error (%d)\n", p_section_list_pool->semid, errno);
519     }
520    
521     if (shmdt(p_section_list_pool) == -1)
522     {
523     log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
524     }
525    
526 sysadm 1.52 if (shmid != 0 && shmctl(shmid, IPC_RMID, NULL) == -1)
527 sysadm 1.26 {
528     log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", shmid, errno);
529     }
530    
531     p_section_list_pool = NULL;
532     }
533    
534     int set_section_list_shm_readonly(void)
535     {
536     int shmid;
537     void *p_shm;
538    
539     if (p_section_list_pool == NULL)
540     {
541     log_error("p_section_list_pool not initialized\n");
542     return -1;
543     }
544    
545     shmid = p_section_list_pool->shmid;
546    
547     // Remap shared memory in read-only mode
548     p_shm = shmat(shmid, p_section_list_pool, SHM_RDONLY | SHM_REMAP);
549     if (p_shm == (void *)-1)
550     {
551     log_error("shmat(section_list_pool shmid = %d) error (%d)\n", shmid, errno);
552     return -3;
553     }
554    
555     p_section_list_pool = p_shm;
556    
557     return 0;
558     }
559    
560     int detach_section_list_shm(void)
561     {
562     if (p_section_list_pool != NULL && shmdt(p_section_list_pool) == -1)
563     {
564     log_error("shmdt(section_list_pool) error (%d)\n", errno);
565     return -1;
566     }
567    
568     p_section_list_pool = NULL;
569    
570     return 0;
571     }
572    
573 sysadm 1.14 inline static void sid_to_str(int32_t sid, char *p_sid_str)
574     {
575     uint32_t u_sid;
576     int i;
577    
578     u_sid = (uint32_t)sid;
579     for (i = 0; i < SID_STR_LEN - 1; i++)
580     {
581     p_sid_str[i] = (char)(u_sid % 255 + 1);
582     u_sid /= 255;
583     }
584     p_sid_str[i] = '\0';
585     }
586    
587 sysadm 1.30 SECTION_LIST *section_list_create(int32_t sid, const char *sname, const char *stitle, const char *master_list)
588 sysadm 1.13 {
589     SECTION_LIST *p_section;
590 sysadm 1.14 char sid_str[SID_STR_LEN];
591 sysadm 1.13
592 sysadm 1.22 if (p_section_list_pool == NULL)
593 sysadm 1.13 {
594     log_error("session_list_pool not initialized\n");
595     return NULL;
596 sysadm 1.1 }
597    
598 sysadm 1.22 if (p_section_list_pool->section_count >= BBS_max_section)
599 sysadm 1.1 {
600 sysadm 1.22 log_error("section_count reach limit %d >= %d\n", p_section_list_pool->section_count, BBS_max_section);
601 sysadm 1.1 return NULL;
602     }
603    
604 sysadm 1.14 sid_to_str(sid, sid_str);
605    
606 sysadm 1.22 p_section = p_section_list_pool->sections + p_section_list_pool->section_count;
607 sysadm 1.1
608 sysadm 1.11 p_section->sid = sid;
609 sysadm 1.36 p_section->ex_menu_tm = 0;
610 sysadm 1.11
611 sysadm 1.29 strncpy(p_section->sname, sname, sizeof(p_section->sname) - 1);
612     p_section->sname[sizeof(p_section->sname) - 1] = '\0';
613 sysadm 1.1
614 sysadm 1.29 strncpy(p_section->stitle, stitle, sizeof(p_section->stitle) - 1);
615     p_section->stitle[sizeof(p_section->stitle) - 1] = '\0';
616 sysadm 1.1
617 sysadm 1.30 strncpy(p_section->master_list, master_list, sizeof(p_section->master_list) - 1);
618 sysadm 1.29 p_section->master_list[sizeof(p_section->master_list) - 1] = '\0';
619 sysadm 1.1
620 sysadm 1.22 if (trie_dict_set(p_section_list_pool->p_trie_dict_section_by_name, sname, p_section_list_pool->section_count) != 1)
621 sysadm 1.14 {
622 sysadm 1.22 log_error("trie_dict_set(section, %s, %d) error\n", sname, p_section_list_pool->section_count);
623 sysadm 1.14 return NULL;
624     }
625    
626 sysadm 1.22 if (trie_dict_set(p_section_list_pool->p_trie_dict_section_by_sid, sid_str, p_section_list_pool->section_count) != 1)
627 sysadm 1.1 {
628 sysadm 1.22 log_error("trie_dict_set(section, %d, %d) error\n", sid, p_section_list_pool->section_count);
629 sysadm 1.1 return NULL;
630     }
631    
632 sysadm 1.7 section_list_reset_articles(p_section);
633    
634 sysadm 1.22 p_section_list_pool->section_count++;
635 sysadm 1.1
636     return p_section;
637     }
638    
639 sysadm 1.43 int section_list_update(SECTION_LIST *p_section, const char *sname, const char *stitle, const char *master_list)
640     {
641 sysadm 1.44 int64_t index;
642    
643 sysadm 1.43 if (p_section == NULL || sname == NULL || stitle == NULL || master_list == NULL)
644     {
645     log_error("NULL pointer error\n");
646     return -1;
647     }
648    
649 sysadm 1.44 index = get_section_index(p_section);
650    
651 sysadm 1.43 strncpy(p_section->sname, sname, sizeof(p_section->sname) - 1);
652     p_section->sname[sizeof(p_section->sname) - 1] = '\0';
653    
654     strncpy(p_section->stitle, stitle, sizeof(p_section->stitle) - 1);
655     p_section->stitle[sizeof(p_section->stitle) - 1] = '\0';
656    
657     strncpy(p_section->master_list, master_list, sizeof(p_section->master_list) - 1);
658     p_section->master_list[sizeof(p_section->master_list) - 1] = '\0';
659    
660 sysadm 1.44 if (trie_dict_set(p_section_list_pool->p_trie_dict_section_by_name, sname, index) < 0)
661 sysadm 1.43 {
662 sysadm 1.44 log_error("trie_dict_set(section, %s, %d) error\n", sname, index);
663 sysadm 1.43 return -2;
664     }
665    
666     return 0;
667     }
668    
669 sysadm 1.7 void section_list_reset_articles(SECTION_LIST *p_section)
670 sysadm 1.1 {
671 sysadm 1.7 p_section->article_count = 0;
672 sysadm 1.8 p_section->topic_count = 0;
673     p_section->visible_article_count = 0;
674     p_section->visible_topic_count = 0;
675 sysadm 1.7 p_section->p_article_head = NULL;
676     p_section->p_article_tail = NULL;
677 sysadm 1.1
678 sysadm 1.7 p_section->page_count = 0;
679 sysadm 1.8 p_section->last_page_visible_article_count = 0;
680 sysadm 1.35
681     p_section->ontop_article_count = 0;
682 sysadm 1.7 }
683 sysadm 1.1
684 sysadm 1.41 SECTION_LIST *section_list_find_by_name(const char *sname)
685 sysadm 1.1 {
686     int64_t index;
687 sysadm 1.24 int ret;
688 sysadm 1.1
689 sysadm 1.22 if (p_section_list_pool == NULL)
690 sysadm 1.14 {
691     log_error("section_list not initialized\n");
692     return NULL;
693     }
694    
695 sysadm 1.24 ret = trie_dict_get(p_section_list_pool->p_trie_dict_section_by_name, sname, &index);
696     if (ret < 0)
697 sysadm 1.14 {
698     log_error("trie_dict_get(section, %s) error\n", sname);
699     return NULL;
700     }
701 sysadm 1.24 else if (ret == 0)
702     {
703     return NULL;
704     }
705 sysadm 1.14
706 sysadm 1.22 return (p_section_list_pool->sections + index);
707 sysadm 1.14 }
708    
709 sysadm 1.41 SECTION_LIST *section_list_find_by_sid(int32_t sid)
710 sysadm 1.14 {
711     int64_t index;
712 sysadm 1.24 int ret;
713 sysadm 1.14 char sid_str[SID_STR_LEN];
714    
715 sysadm 1.22 if (p_section_list_pool == NULL)
716 sysadm 1.1 {
717 sysadm 1.7 log_error("section_list not initialized\n");
718 sysadm 1.1 return NULL;
719     }
720    
721 sysadm 1.14 sid_to_str(sid, sid_str);
722    
723 sysadm 1.24 ret = trie_dict_get(p_section_list_pool->p_trie_dict_section_by_sid, sid_str, &index);
724     if (ret < 0)
725 sysadm 1.1 {
726 sysadm 1.14 log_error("trie_dict_get(section, %d) error\n", sid);
727 sysadm 1.1 return NULL;
728     }
729 sysadm 1.24 else if (ret == 0)
730     {
731     return NULL;
732     }
733 sysadm 1.1
734 sysadm 1.22 return (p_section_list_pool->sections + index);
735 sysadm 1.1 }
736    
737 sysadm 1.7 int section_list_append_article(SECTION_LIST *p_section, const ARTICLE *p_article_src)
738 sysadm 1.1 {
739     ARTICLE_BLOCK *p_block;
740     int32_t last_aid = 0;
741 sysadm 1.6 ARTICLE *p_article;
742 sysadm 1.1 ARTICLE *p_topic_head;
743     ARTICLE *p_topic_tail;
744    
745 sysadm 1.6 if (p_section == NULL || p_article_src == NULL)
746 sysadm 1.1 {
747 sysadm 1.42 log_error("NULL pointer error\n");
748 sysadm 1.1 return -1;
749     }
750    
751 sysadm 1.7 if (p_article_block_pool == NULL)
752 sysadm 1.1 {
753 sysadm 1.7 log_error("article_block_pool not initialized\n");
754 sysadm 1.1 return -1;
755     }
756    
757 sysadm 1.14 if (p_section->sid != p_article_src->sid)
758     {
759     log_error("section_list_append_article() error: section sid %d != article sid %d\n", p_section->sid, p_article_src->sid);
760     return -2;
761     }
762    
763 sysadm 1.9 if (p_section->article_count >= BBS_article_limit_per_section)
764     {
765     log_error("section_list_append_article() error: article_count reach limit in section %d\n", p_section->sid);
766     return -2;
767     }
768    
769 sysadm 1.7 if (p_article_block_pool->block_count == 0 ||
770 sysadm 1.54 p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->article_count >= BBS_article_count_per_block)
771 sysadm 1.1 {
772     if ((p_block = pop_free_article_block()) == NULL)
773     {
774     log_error("pop_free_article_block() error\n");
775     return -2;
776     }
777    
778 sysadm 1.7 if (p_article_block_pool->block_count > 0)
779 sysadm 1.1 {
780 sysadm 1.54 last_aid = p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->articles[BBS_article_count_per_block - 1].aid;
781 sysadm 1.1 }
782 sysadm 1.7
783     p_article_block_pool->p_block[p_article_block_pool->block_count] = p_block;
784     p_article_block_pool->block_count++;
785 sysadm 1.1 }
786     else
787     {
788 sysadm 1.7 p_block = p_article_block_pool->p_block[p_article_block_pool->block_count - 1];
789     last_aid = p_block->articles[p_block->article_count - 1].aid;
790 sysadm 1.1 }
791    
792     // AID of articles should be strictly ascending
793 sysadm 1.6 if (p_article_src->aid <= last_aid)
794 sysadm 1.1 {
795 sysadm 1.14 log_error("section_list_append_article(aid=%d) error: last_aid=%d\n", p_article_src->aid, last_aid);
796 sysadm 1.1 return -3;
797     }
798    
799 sysadm 1.7 p_article = (p_block->articles + p_block->article_count);
800     p_block->article_count++;
801     p_section->article_count++;
802    
803     // Copy article data
804     *p_article = *p_article_src;
805 sysadm 1.1
806 sysadm 1.8 if (p_article->visible)
807     {
808     p_section->visible_article_count++;
809     }
810    
811 sysadm 1.7 // Link appended article as tail node of topic bi-directional list
812     if (p_article->tid != 0)
813 sysadm 1.1 {
814 sysadm 1.7 p_topic_head = article_block_find_by_aid(p_article->tid);
815 sysadm 1.1 if (p_topic_head == NULL)
816     {
817 sysadm 1.7 log_error("search head of topic (aid=%d) error\n", p_article->tid);
818 sysadm 1.1 return -4;
819     }
820    
821 sysadm 1.6 p_topic_tail = p_topic_head->p_topic_prior;
822 sysadm 1.1 if (p_topic_tail == NULL)
823     {
824 sysadm 1.7 log_error("tail of topic (aid=%d) is NULL\n", p_article->tid);
825 sysadm 1.1 return -4;
826     }
827     }
828     else
829     {
830 sysadm 1.8 p_section->topic_count++;
831    
832     if (p_article->visible)
833     {
834     p_section->visible_topic_count++;
835     }
836    
837 sysadm 1.7 p_topic_head = p_article;
838     p_topic_tail = p_article;
839 sysadm 1.1 }
840    
841 sysadm 1.7 p_article->p_topic_prior = p_topic_tail;
842     p_article->p_topic_next = p_topic_head;
843     p_topic_head->p_topic_prior = p_article;
844     p_topic_tail->p_topic_next = p_article;
845 sysadm 1.1
846 sysadm 1.6 // Link appended article as tail node of article bi-directional list
847     if (p_section->p_article_head == NULL)
848     {
849     p_section->p_article_head = p_article;
850     p_section->p_article_tail = p_article;
851     }
852     p_article->p_prior = p_section->p_article_tail;
853     p_article->p_next = p_section->p_article_head;
854     p_section->p_article_head->p_prior = p_article;
855     p_section->p_article_tail->p_next = p_article;
856     p_section->p_article_tail = p_article;
857    
858 sysadm 1.7 // Update page
859 sysadm 1.50 if ((p_article->visible && p_section->last_page_visible_article_count == BBS_article_limit_per_page) ||
860     p_section->page_count == 0)
861 sysadm 1.1 {
862 sysadm 1.7 p_section->p_page_first_article[p_section->page_count] = p_article;
863     p_section->page_count++;
864 sysadm 1.8 p_section->last_page_visible_article_count = 0;
865 sysadm 1.1 }
866 sysadm 1.9
867     if (p_article->visible)
868     {
869     p_section->last_page_visible_article_count++;
870     }
871 sysadm 1.1
872 sysadm 1.35 if (p_article->ontop && section_list_update_article_ontop(p_section, p_article) < 0)
873     {
874     log_error("section_list_update_article_ontop(sid=%d, aid=%d) error\n",
875     p_section->sid, p_article->aid);
876     return -5;
877     }
878    
879 sysadm 1.7 return 0;
880 sysadm 1.2 }
881    
882 sysadm 1.7 int section_list_set_article_visible(SECTION_LIST *p_section, int32_t aid, int8_t visible)
883 sysadm 1.1 {
884     ARTICLE *p_article;
885 sysadm 1.8 ARTICLE *p_reply;
886     int affected_count = 0;
887 sysadm 1.1
888 sysadm 1.2 if (p_section == NULL)
889     {
890 sysadm 1.35 log_error("NULL pointer error\n");
891     return -1;
892 sysadm 1.2 }
893    
894 sysadm 1.7 p_article = article_block_find_by_aid(aid);
895 sysadm 1.1 if (p_article == NULL)
896     {
897     return -1; // Not found
898     }
899    
900 sysadm 1.14 if (p_section->sid != p_article->sid)
901     {
902 sysadm 1.35 log_error("Inconsistent section sid %d != article sid %d\n", p_section->sid, p_article->sid);
903 sysadm 1.14 return -2;
904     }
905    
906 sysadm 1.7 if (p_article->visible == visible)
907 sysadm 1.1 {
908 sysadm 1.7 return 0; // Already set
909 sysadm 1.1 }
910    
911 sysadm 1.8 if (visible == 0) // 1 -> 0
912     {
913     p_section->visible_article_count--;
914    
915 sysadm 1.46 if (user_article_cnt_inc(p_article->uid, -1) < 0)
916     {
917     log_error("user_article_cnt_inc(uid=%d, -1) error\n", p_article->uid);
918     }
919    
920 sysadm 1.8 if (p_article->tid == 0)
921     {
922     p_section->visible_topic_count--;
923    
924     // Set related visible replies to invisible
925     for (p_reply = p_article->p_topic_next; p_reply->tid != 0; p_reply = p_reply->p_topic_next)
926     {
927     if (p_reply->tid != aid)
928     {
929     log_error("Inconsistent tid = %d found in reply %d of topic %d\n", p_reply->tid, p_reply->aid, aid);
930     continue;
931     }
932    
933     if (p_reply->visible == 1)
934     {
935     p_reply->visible = 0;
936     p_section->visible_article_count--;
937     affected_count++;
938 sysadm 1.46
939     if (user_article_cnt_inc(p_reply->uid, -1) < 0)
940     {
941     log_error("user_article_cnt_inc(uid=%d, -1) error\n", p_reply->uid);
942     }
943 sysadm 1.8 }
944     }
945     }
946     }
947     else // 0 -> 1
948     {
949     p_section->visible_article_count++;
950    
951     if (p_article->tid == 0)
952     {
953     p_section->visible_topic_count++;
954     }
955 sysadm 1.46
956     if (user_article_cnt_inc(p_article->uid, 1) < 0)
957     {
958     log_error("user_article_cnt_inc(uid=%d, 1) error\n", p_article->uid);
959     }
960 sysadm 1.8 }
961    
962 sysadm 1.7 p_article->visible = visible;
963 sysadm 1.8 affected_count++;
964    
965     return affected_count;
966     }
967    
968 sysadm 1.35 int section_list_update_article_ontop(SECTION_LIST *p_section, ARTICLE *p_article)
969     {
970     int i;
971    
972     if (p_section == NULL || p_article == NULL)
973     {
974     log_error("NULL pointer error\n");
975     return -1;
976     }
977    
978     if (p_section->sid != p_article->sid)
979     {
980     log_error("Inconsistent section sid %d != article sid %d\n", p_section->sid, p_article->sid);
981     return -2;
982     }
983    
984     if (p_article->ontop)
985     {
986     for (i = 0; i < p_section->ontop_article_count; i++)
987     {
988     if (p_section->p_ontop_articles[i]->aid == p_article->aid)
989     {
990     log_error("Inconsistent state found: article %d already ontop in section %d\n", p_article->aid, p_section->sid);
991     return 0;
992     }
993     else if (p_section->p_ontop_articles[i]->aid > p_article->aid)
994     {
995     break;
996     }
997     }
998    
999     // Remove the oldest one if the array of ontop articles is full
1000     if (p_section->ontop_article_count >= BBS_ontop_article_limit_per_section)
1001     {
1002     if (i == 0) // p_article is the oldest one
1003     {
1004     return 0;
1005     }
1006     memmove((void *)(p_section->p_ontop_articles),
1007     (void *)(p_section->p_ontop_articles + 1),
1008     sizeof(ARTICLE *) * (size_t)(i - 1));
1009     p_section->ontop_article_count--;
1010     i--;
1011     }
1012     else
1013     {
1014     memmove((void *)(p_section->p_ontop_articles + i + 1),
1015     (void *)(p_section->p_ontop_articles + i),
1016     sizeof(ARTICLE *) * (size_t)(p_section->ontop_article_count - i));
1017     }
1018    
1019     p_section->p_ontop_articles[i] = p_article;
1020     p_section->ontop_article_count++;
1021    
1022     // TODO: debug
1023     }
1024     else // ontop == 0
1025     {
1026     for (i = 0; i < p_section->ontop_article_count; i++)
1027     {
1028     if (p_section->p_ontop_articles[i]->aid == p_article->aid)
1029     {
1030     break;
1031     }
1032     }
1033     if (i == p_section->ontop_article_count) // not found
1034     {
1035     log_error("Inconsistent state found: article %d not ontop in section %d\n", p_article->aid, p_section->sid);
1036     return 0;
1037     }
1038    
1039     memmove((void *)(p_section->p_ontop_articles + i),
1040     (void *)(p_section->p_ontop_articles + i + 1),
1041     sizeof(ARTICLE *) * (size_t)(p_section->ontop_article_count - i - 1));
1042     p_section->ontop_article_count--;
1043     }
1044    
1045     return 0;
1046     }
1047    
1048     int section_list_page_count_with_ontop(SECTION_LIST *p_section)
1049     {
1050     int page_count;
1051    
1052     if (p_section == NULL)
1053     {
1054     log_error("NULL pointer error\n");
1055     return -1;
1056     }
1057    
1058 sysadm 1.50 page_count = p_section->page_count - 1 +
1059 sysadm 1.35 (p_section->last_page_visible_article_count + p_section->ontop_article_count) / BBS_article_limit_per_page +
1060 sysadm 1.50 ((p_section->last_page_visible_article_count + p_section->ontop_article_count) % BBS_article_limit_per_page ? 1 : 0);
1061    
1062     if (page_count < 0)
1063     {
1064     page_count = 0;
1065     }
1066 sysadm 1.35
1067     return page_count;
1068     }
1069    
1070     int section_list_page_article_count_with_ontop(SECTION_LIST *p_section, int32_t page_id)
1071     {
1072     if (p_section == NULL)
1073     {
1074     log_error("NULL pointer error\n");
1075     return -1;
1076     }
1077    
1078     if (page_id < p_section->page_count - 1)
1079     {
1080     return BBS_article_limit_per_page;
1081     }
1082     else // if (page_id >= p_section->page_count - 1)
1083     {
1084 sysadm 1.49 return MIN(MAX(0,
1085     (p_section->last_page_visible_article_count + p_section->ontop_article_count -
1086     BBS_article_limit_per_page * (page_id - p_section->page_count + 1))),
1087     BBS_article_limit_per_page);
1088 sysadm 1.35 }
1089     }
1090    
1091 sysadm 1.11 ARTICLE *section_list_find_article_with_offset(SECTION_LIST *p_section, int32_t aid, int32_t *p_page, int32_t *p_offset, ARTICLE **pp_next)
1092 sysadm 1.8 {
1093     ARTICLE *p_article;
1094     int left;
1095     int right;
1096     int mid;
1097    
1098     *p_page = -1;
1099     *p_offset = -1;
1100 sysadm 1.11 *pp_next = NULL;
1101 sysadm 1.8
1102     if (p_section == NULL)
1103     {
1104 sysadm 1.42 log_error("NULL pointer error\n");
1105 sysadm 1.8 return NULL;
1106     }
1107 sysadm 1.7
1108 sysadm 1.8 if (p_section->article_count == 0) // empty
1109     {
1110     *p_page = 0;
1111     *p_offset = 0;
1112     return NULL;
1113     }
1114    
1115     left = 0;
1116 sysadm 1.47 right = p_section->page_count - 1;
1117 sysadm 1.8
1118 sysadm 1.47 // aid in the range [ head aid of pages[left], tail aid of pages[right] ]
1119     while (left < right)
1120 sysadm 1.8 {
1121     // get page id no less than mid value of left page id and right page id
1122 sysadm 1.47 mid = (left + right) / 2 + (left + right) % 2;
1123 sysadm 1.8
1124     if (aid < p_section->p_page_first_article[mid]->aid)
1125     {
1126 sysadm 1.47 right = mid - 1;
1127 sysadm 1.8 }
1128 sysadm 1.47 else // if (aid < p_section->p_page_first_article[mid]->aid)
1129 sysadm 1.8 {
1130     left = mid;
1131     }
1132     }
1133    
1134     *p_page = left;
1135    
1136     p_article = p_section->p_page_first_article[*p_page];
1137    
1138     // p_section->p_page_first_article[*p_page]->aid <= aid < p_section->p_page_first_article[*p_page + 1]->aid
1139 sysadm 1.11 right = (*p_page == MAX(0, p_section->page_count - 1) ? INT32_MAX : p_section->p_page_first_article[*p_page + 1]->aid);
1140 sysadm 1.8
1141     // left will be the offset of article found or offset to insert
1142     left = 0;
1143    
1144 sysadm 1.10 while (aid > p_article->aid)
1145 sysadm 1.8 {
1146     p_article = p_article->p_next;
1147     left++;
1148    
1149 sysadm 1.11 if (aid == p_article->aid)
1150     {
1151     *pp_next = p_article->p_next;
1152     break;
1153     }
1154    
1155 sysadm 1.8 // over last article in the page
1156     if (p_article == p_section->p_article_head || p_article->aid >= right)
1157     {
1158 sysadm 1.11 *pp_next = (p_article == p_section->p_article_head ? p_section->p_article_head : p_section->p_page_first_article[*p_page + 1]);
1159     *p_offset = left;
1160     return NULL; // not found
1161 sysadm 1.8 }
1162     }
1163    
1164 sysadm 1.11 if (aid < p_article->aid)
1165     {
1166     *pp_next = p_article;
1167     p_article = NULL; // not found
1168     }
1169     else // aid == p_article->aid
1170 sysadm 1.10 {
1171 sysadm 1.11 *pp_next = p_article->p_next;
1172 sysadm 1.10 }
1173    
1174 sysadm 1.8 *p_offset = left;
1175    
1176     return p_article;
1177     }
1178    
1179     int section_list_calculate_page(SECTION_LIST *p_section, int32_t start_aid)
1180     {
1181 sysadm 1.9 ARTICLE *p_article;
1182 sysadm 1.11 ARTICLE *p_next;
1183 sysadm 1.9 int32_t page;
1184     int32_t offset;
1185     int visible_article_count;
1186     int page_head_set;
1187 sysadm 1.8
1188     if (p_section == NULL)
1189     {
1190 sysadm 1.42 log_error("NULL pointer error\n");
1191 sysadm 1.8 return -1;
1192     }
1193    
1194 sysadm 1.11 if (p_section->article_count == 0) // empty
1195     {
1196     p_section->page_count = 0;
1197     p_section->last_page_visible_article_count = 0;
1198    
1199     return 0;
1200     }
1201    
1202     if (start_aid > 0)
1203 sysadm 1.9 {
1204 sysadm 1.14 p_article = article_block_find_by_aid(start_aid);
1205     if (p_article == NULL)
1206     {
1207     return -1; // Not found
1208     }
1209    
1210     if (p_section->sid != p_article->sid)
1211     {
1212     log_error("section_list_calculate_page() error: section sid %d != start article sid %d\n", p_section->sid, p_article->sid);
1213     return -2;
1214     }
1215    
1216 sysadm 1.11 p_article = section_list_find_article_with_offset(p_section, start_aid, &page, &offset, &p_next);
1217     if (p_article == NULL)
1218 sysadm 1.9 {
1219 sysadm 1.11 if (page < 0)
1220     {
1221     return -1;
1222     }
1223     log_error("section_list_calculate_page() aid = %d not found in section sid = %d\n",
1224     start_aid, p_section->sid);
1225     return -2;
1226 sysadm 1.9 }
1227    
1228 sysadm 1.11 if (offset > 0)
1229     {
1230     p_article = p_section->p_page_first_article[page];
1231     }
1232 sysadm 1.9 }
1233 sysadm 1.11 else
1234 sysadm 1.9 {
1235 sysadm 1.11 p_article = p_section->p_article_head;
1236     page = 0;
1237     offset = 0;
1238 sysadm 1.9 }
1239    
1240     visible_article_count = 0;
1241     page_head_set = 0;
1242    
1243     do
1244     {
1245     if (!page_head_set && visible_article_count == 0)
1246     {
1247     p_section->p_page_first_article[page] = p_article;
1248     page_head_set = 1;
1249     }
1250    
1251     if (p_article->visible)
1252     {
1253     visible_article_count++;
1254     }
1255    
1256     p_article = p_article->p_next;
1257    
1258     // skip remaining invisible articles
1259     while (p_article->visible == 0 && p_article != p_section->p_article_head)
1260     {
1261     p_article = p_article->p_next;
1262     }
1263    
1264     if (visible_article_count >= BBS_article_limit_per_page && p_article != p_section->p_article_head)
1265     {
1266     page++;
1267     visible_article_count = 0;
1268     page_head_set = 0;
1269    
1270     if (page >= BBS_article_limit_per_section / BBS_article_limit_per_page && p_article != p_section->p_article_head)
1271     {
1272     log_error("Count of page exceed limit in section %d\n", p_section->sid);
1273     break;
1274     }
1275     }
1276     } while (p_article != p_section->p_article_head);
1277    
1278     p_section->page_count = page + (visible_article_count > 0 ? 1 : 0);
1279 sysadm 1.50 p_section->last_page_visible_article_count = (visible_article_count > 0
1280     ? visible_article_count
1281     : (page > 0 ? BBS_article_limit_per_page : 0));
1282 sysadm 1.9
1283 sysadm 1.8 return 0;
1284 sysadm 1.1 }
1285 sysadm 1.11
1286 sysadm 1.25 int32_t article_block_last_aid(void)
1287     {
1288     ARTICLE_BLOCK *p_block = p_article_block_pool->p_block[p_article_block_pool->block_count - 1];
1289     int32_t last_aid = p_block->articles[p_block->article_count - 1].aid;
1290    
1291     return last_aid;
1292     }
1293    
1294 sysadm 1.27 int article_block_article_count(void)
1295     {
1296     int ret;
1297    
1298     if (p_article_block_pool == NULL || p_article_block_pool->block_count <= 0)
1299     {
1300     return -1;
1301     }
1302    
1303 sysadm 1.54 ret = (p_article_block_pool->block_count - 1) * BBS_article_count_per_block +
1304 sysadm 1.27 p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->article_count;
1305    
1306     return ret;
1307     }
1308    
1309 sysadm 1.14 int article_count_of_topic(int32_t aid)
1310 sysadm 1.11 {
1311     ARTICLE *p_article;
1312     int article_count;
1313    
1314     p_article = article_block_find_by_aid(aid);
1315     if (p_article == NULL)
1316     {
1317     return 0; // Not found
1318     }
1319    
1320     article_count = 0;
1321    
1322     do
1323     {
1324 sysadm 1.15 if (p_article->tid != 0 && p_article->tid != aid)
1325     {
1326     log_error("article_count_of_topic(%d) error: article %d not linked to the topic\n", aid, p_article->aid);
1327     break;
1328     }
1329    
1330 sysadm 1.11 article_count++;
1331     p_article = p_article->p_topic_next;
1332     } while (p_article->aid != aid);
1333    
1334     return article_count;
1335     }
1336    
1337     int section_list_move_topic(SECTION_LIST *p_section_src, SECTION_LIST *p_section_dest, int32_t aid)
1338     {
1339     ARTICLE *p_article;
1340     ARTICLE *p_next;
1341     int32_t page;
1342     int32_t offset;
1343     int32_t move_article_count;
1344     int32_t dest_article_count_old;
1345     int32_t last_unaffected_aid_src;
1346 sysadm 1.12 int32_t first_inserted_aid_dest;
1347     int move_counter;
1348 sysadm 1.11
1349 sysadm 1.28 if (p_section_src == NULL || p_section_dest == NULL)
1350 sysadm 1.11 {
1351 sysadm 1.42 log_error("NULL pointer error\n");
1352 sysadm 1.11 return -1;
1353     }
1354    
1355 sysadm 1.28 if (p_section_src->sid == p_section_dest->sid)
1356     {
1357     log_error("section_list_move_topic() src and dest section are the same\n");
1358     return -1;
1359     }
1360    
1361 sysadm 1.14 if ((p_article = article_block_find_by_aid(aid)) == NULL)
1362 sysadm 1.11 {
1363 sysadm 1.28 log_error("article_block_find_by_aid(aid = %d) error: article not found\n", aid);
1364 sysadm 1.14 return -2;
1365     }
1366    
1367     if (p_section_src->sid != p_article->sid)
1368     {
1369     log_error("section_list_move_topic() error: src section sid %d != article %d sid %d\n",
1370     p_section_src->sid, p_article->aid, p_article->sid);
1371 sysadm 1.11 return -2;
1372     }
1373    
1374     if (p_article->tid != 0)
1375     {
1376     log_error("section_list_move_topic(aid = %d) error: article is not head of topic, tid = %d\n", aid, p_article->tid);
1377     return -2;
1378     }
1379    
1380     last_unaffected_aid_src = (p_article == p_section_src->p_article_head ? 0 : p_article->p_prior->aid);
1381    
1382 sysadm 1.14 move_article_count = article_count_of_topic(aid);
1383 sysadm 1.11 if (move_article_count <= 0)
1384     {
1385 sysadm 1.22 log_error("article_count_of_topic(aid = %d) <= 0\n", aid);
1386 sysadm 1.11 return -2;
1387     }
1388    
1389     if (p_section_dest->article_count + move_article_count > BBS_article_limit_per_section)
1390     {
1391     log_error("section_list_move_topic() error: article_count %d reach limit in section %d\n",
1392     p_section_dest->article_count + move_article_count, p_section_dest->sid);
1393     return -3;
1394     }
1395    
1396     dest_article_count_old = p_section_dest->article_count;
1397 sysadm 1.12 move_counter = 0;
1398     first_inserted_aid_dest = p_article->aid;
1399 sysadm 1.11
1400     do
1401     {
1402 sysadm 1.14 if (p_section_src->sid != p_article->sid)
1403 sysadm 1.11 {
1404 sysadm 1.28 log_error("section_list_move_topic() warning: src section sid %d != article %d sid %d\n",
1405 sysadm 1.14 p_section_src->sid, p_article->aid, p_article->sid);
1406 sysadm 1.28 p_article = p_article->p_topic_next;
1407     continue;
1408 sysadm 1.11 }
1409    
1410     // Remove from bi-directional article list of src section
1411     if (p_section_src->p_article_head == p_article)
1412     {
1413     p_section_src->p_article_head = p_article->p_next;
1414     }
1415     if (p_section_src->p_article_tail == p_article)
1416     {
1417     p_section_src->p_article_tail = p_article->p_prior;
1418     }
1419     if (p_section_src->p_article_head == p_article) // || p_section_src->p_article_tail == p_article
1420     {
1421     p_section_src->p_article_head = NULL;
1422     p_section_src->p_article_tail = NULL;
1423     }
1424    
1425     p_article->p_prior->p_next = p_article->p_next;
1426     p_article->p_next->p_prior = p_article->p_prior;
1427    
1428 sysadm 1.14 // Update sid of article
1429     p_article->sid = p_section_dest->sid;
1430    
1431     if (section_list_find_article_with_offset(p_section_dest, p_article->aid, &page, &offset, &p_next) != NULL)
1432     {
1433 sysadm 1.28 log_error("section_list_move_topic() warning: article %d already in section %d\n", p_article->aid, p_section_dest->sid);
1434     p_article = p_article->p_topic_next;
1435     continue;
1436 sysadm 1.14 }
1437    
1438 sysadm 1.11 // Insert into bi-directional article list of dest section
1439     if (p_next == NULL) // empty section
1440     {
1441     p_section_dest->p_article_head = p_article;
1442     p_section_dest->p_article_tail = p_article;
1443     p_article->p_prior = p_article;
1444     p_article->p_next = p_article;
1445     }
1446     else
1447     {
1448     if (p_section_dest->p_article_head == p_next)
1449     {
1450     if (p_article->aid < p_next->aid)
1451     {
1452     p_section_dest->p_article_head = p_article;
1453     }
1454     else // p_article->aid > p_next->aid
1455     {
1456     p_section_dest->p_article_tail = p_article;
1457     }
1458     }
1459    
1460     p_article->p_prior = p_next->p_prior;
1461     p_article->p_next = p_next;
1462     p_next->p_prior->p_next = p_article;
1463     p_next->p_prior = p_article;
1464     }
1465    
1466     // Update article / topic counter of src / desc section
1467     p_section_src->article_count--;
1468     p_section_dest->article_count++;
1469     if (p_article->tid == 0)
1470     {
1471     p_section_src->topic_count--;
1472     p_section_dest->topic_count++;
1473     }
1474    
1475     // Update visible article / topic counter of src / desc section
1476     if (p_article->visible)
1477     {
1478     p_section_src->visible_article_count--;
1479     p_section_dest->visible_article_count++;
1480     if (p_article->tid == 0)
1481     {
1482     p_section_src->visible_topic_count--;
1483     p_section_dest->visible_topic_count++;
1484     }
1485     }
1486    
1487     // Update page for empty dest section
1488     if (p_section_dest->article_count == 1)
1489     {
1490     p_section_dest->p_page_first_article[0] = p_article;
1491     p_section_dest->page_count = 1;
1492     p_section_dest->last_page_visible_article_count = (p_article->visible ? 1 : 0);
1493     }
1494    
1495     p_article = p_article->p_topic_next;
1496 sysadm 1.12
1497     move_counter++;
1498     if (move_counter % CALCULATE_PAGE_THRESHOLD == 0)
1499     {
1500     // Re-calculate pages of desc section
1501     if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1502     {
1503 sysadm 1.14 log_error("section_list_calculate_page(dest section = %d, aid = %d) error\n",
1504 sysadm 1.12 p_section_dest->sid, first_inserted_aid_dest);
1505     }
1506    
1507     first_inserted_aid_dest = p_article->aid;
1508     }
1509 sysadm 1.11 } while (p_article->aid != aid);
1510    
1511     if (p_section_dest->article_count - dest_article_count_old != move_article_count)
1512     {
1513 sysadm 1.28 log_error("section_list_move_topic() warning: count of moved articles %d != %d\n",
1514 sysadm 1.11 p_section_dest->article_count - dest_article_count_old, move_article_count);
1515     }
1516    
1517 sysadm 1.12 // Re-calculate pages of src section
1518 sysadm 1.11 if (section_list_calculate_page(p_section_src, last_unaffected_aid_src) < 0)
1519     {
1520 sysadm 1.14 log_error("section_list_calculate_page(src section = %d, aid = %d) error at aid = %d\n",
1521 sysadm 1.12 p_section_src->sid, last_unaffected_aid_src, aid);
1522 sysadm 1.11 }
1523 sysadm 1.12
1524     if (move_counter % CALCULATE_PAGE_THRESHOLD != 0)
1525 sysadm 1.11 {
1526 sysadm 1.12 // Re-calculate pages of desc section
1527     if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1528     {
1529 sysadm 1.14 log_error("section_list_calculate_page(dest section = %d, aid = %d) error\n",
1530 sysadm 1.12 p_section_dest->sid, first_inserted_aid_dest);
1531     }
1532 sysadm 1.11 }
1533    
1534     return move_article_count;
1535     }
1536 sysadm 1.16
1537     int get_section_index(SECTION_LIST *p_section)
1538     {
1539     int index;
1540    
1541     if (p_section_list_pool == NULL)
1542     {
1543     log_error("get_section_index() error: uninitialized\n");
1544     return -1;
1545     }
1546    
1547     if (p_section == NULL)
1548     {
1549     index = BBS_max_section;
1550     }
1551     else
1552     {
1553 sysadm 1.22 index = (int)(p_section - p_section_list_pool->sections);
1554 sysadm 1.16 if (index < 0 || index >= BBS_max_section)
1555     {
1556     log_error("get_section_index(%d) error: index out of range\n", index);
1557     return -2;
1558     }
1559     }
1560    
1561     return index;
1562     }
1563    
1564 sysadm 1.48 int get_section_info(SECTION_LIST *p_section, char *sname, char *stitle, char *master_list)
1565     {
1566     if (p_section == NULL)
1567     {
1568     log_error("NULL pointer error\n");
1569     return -1;
1570     }
1571    
1572     if (section_list_rd_lock(p_section) < 0)
1573     {
1574     log_error("section_list_rd_lock(sid=%d) error\n", p_section->sid);
1575     return -2;
1576     }
1577    
1578     if (sname != NULL)
1579     {
1580     memcpy(sname, p_section->sname, sizeof(p_section->sname));
1581     }
1582     if (stitle != NULL)
1583     {
1584     memcpy(stitle, p_section->stitle, sizeof(p_section->stitle));
1585     }
1586     if (master_list != NULL)
1587     {
1588     memcpy(master_list, p_section->master_list, sizeof(p_section->master_list));
1589     }
1590    
1591     // release lock of section
1592     if (section_list_rd_unlock(p_section) < 0)
1593     {
1594     log_error("section_list_rd_unlock(sid=%d) error\n", p_section->sid);
1595     return -2;
1596     }
1597    
1598     return 0;
1599     }
1600    
1601 sysadm 1.16 int section_list_try_rd_lock(SECTION_LIST *p_section, int wait_sec)
1602     {
1603     int index;
1604 sysadm 1.17 struct sembuf sops[4];
1605 sysadm 1.16 struct timespec timeout;
1606     int ret;
1607    
1608     index = get_section_index(p_section);
1609     if (index < 0)
1610     {
1611     return -2;
1612     }
1613    
1614 sysadm 1.17 sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1615     sops[0].sem_op = 0; // wait until unlocked
1616 sysadm 1.16 sops[0].sem_flg = 0;
1617    
1618 sysadm 1.17 sops[1].sem_num = (unsigned short)(index * 2); // r_sem of section index
1619     sops[1].sem_op = 1; // lock
1620     sops[1].sem_flg = SEM_UNDO; // undo on terminate
1621    
1622     // Read lock on any specific section will also acquire single read lock on "all section"
1623     // so that write lock on all section only need to acquire single write on on "all section"
1624     // rather than to acquire multiple write locks on all the available sections.
1625 sysadm 1.21 if (index != BBS_max_section)
1626 sysadm 1.17 {
1627     sops[2].sem_num = BBS_max_section * 2 + 1; // w_sem of all section
1628     sops[2].sem_op = 0; // wait until unlocked
1629     sops[2].sem_flg = 0;
1630    
1631     sops[3].sem_num = BBS_max_section * 2; // r_sem of all section
1632     sops[3].sem_op = 1; // lock
1633     sops[3].sem_flg = SEM_UNDO; // undo on terminate
1634     }
1635 sysadm 1.16
1636     timeout.tv_sec = wait_sec;
1637     timeout.tv_nsec = 0;
1638    
1639 sysadm 1.22 ret = semtimedop(p_section_list_pool->semid, sops, (index == BBS_max_section ? 2 : 4), &timeout);
1640 sysadm 1.17 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1641     {
1642     log_error("semtimedop(index = %d, lock read) error %d\n", index, errno);
1643     }
1644 sysadm 1.16
1645     return ret;
1646     }
1647    
1648     int section_list_try_rw_lock(SECTION_LIST *p_section, int wait_sec)
1649     {
1650     int index;
1651     struct sembuf sops[3];
1652     struct timespec timeout;
1653     int ret;
1654    
1655     index = get_section_index(p_section);
1656     if (index < 0)
1657     {
1658     return -2;
1659     }
1660    
1661 sysadm 1.17 sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1662     sops[0].sem_op = 0; // wait until unlocked
1663 sysadm 1.16 sops[0].sem_flg = 0;
1664    
1665 sysadm 1.17 sops[1].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1666     sops[1].sem_op = 1; // lock
1667     sops[1].sem_flg = SEM_UNDO; // undo on terminate
1668    
1669     sops[2].sem_num = (unsigned short)(index * 2); // r_sem of section index
1670     sops[2].sem_op = 0; // wait until unlocked
1671     sops[2].sem_flg = 0;
1672 sysadm 1.16
1673     timeout.tv_sec = wait_sec;
1674     timeout.tv_nsec = 0;
1675    
1676 sysadm 1.22 ret = semtimedop(p_section_list_pool->semid, sops, 3, &timeout);
1677 sysadm 1.17 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1678     {
1679     log_error("semtimedop(index = %d, lock write) error %d\n", index, errno);
1680     }
1681 sysadm 1.16
1682     return ret;
1683     }
1684    
1685     int section_list_rd_unlock(SECTION_LIST *p_section)
1686     {
1687     int index;
1688 sysadm 1.17 struct sembuf sops[2];
1689 sysadm 1.16 int ret;
1690    
1691     index = get_section_index(p_section);
1692     if (index < 0)
1693     {
1694     return -2;
1695     }
1696    
1697 sysadm 1.17 sops[0].sem_num = (unsigned short)(index * 2); // r_sem of section index
1698     sops[0].sem_op = -1; // unlock
1699     sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
1700 sysadm 1.16
1701 sysadm 1.21 if (index != BBS_max_section)
1702 sysadm 1.17 {
1703     sops[1].sem_num = BBS_max_section * 2; // r_sem of all section
1704     sops[1].sem_op = -1; // unlock
1705     sops[1].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
1706     }
1707    
1708 sysadm 1.22 ret = semop(p_section_list_pool->semid, sops, (index == BBS_max_section ? 1 : 2));
1709 sysadm 1.17 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1710     {
1711     log_error("semop(index = %d, unlock read) error %d\n", index, errno);
1712     }
1713 sysadm 1.16
1714     return ret;
1715     }
1716    
1717     int section_list_rw_unlock(SECTION_LIST *p_section)
1718     {
1719     int index;
1720     struct sembuf sops[1];
1721     int ret;
1722    
1723     index = get_section_index(p_section);
1724     if (index < 0)
1725     {
1726     return -2;
1727     }
1728    
1729 sysadm 1.17 sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1730     sops[0].sem_op = -1; // unlock
1731     sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
1732 sysadm 1.16
1733 sysadm 1.22 ret = semop(p_section_list_pool->semid, sops, 1);
1734 sysadm 1.17 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1735     {
1736     log_error("semop(index = %d, unlock write) error %d\n", index, errno);
1737     }
1738 sysadm 1.16
1739     return ret;
1740     }
1741 sysadm 1.23
1742     int section_list_rd_lock(SECTION_LIST *p_section)
1743     {
1744     int timer = 0;
1745     int sid = (p_section == NULL ? 0 : p_section->sid);
1746     int ret = -1;
1747    
1748     while (!SYS_server_exit)
1749     {
1750     ret = section_list_try_rd_lock(p_section, SECTION_TRY_LOCK_WAIT_TIME);
1751     if (ret == 0) // success
1752     {
1753     break;
1754     }
1755     else if (errno == EAGAIN || errno == EINTR) // retry
1756     {
1757     timer++;
1758     if (timer % SECTION_TRY_LOCK_TIMES == 0)
1759     {
1760 sysadm 1.45 log_error("section_list_try_rd_lock() tried %d times on section %d\n", timer, sid);
1761 sysadm 1.23 }
1762     }
1763     else // failed
1764     {
1765 sysadm 1.37 log_error("section_list_try_rd_lock() failed on section %d\n", sid);
1766 sysadm 1.23 break;
1767     }
1768     }
1769    
1770     return ret;
1771     }
1772    
1773     int section_list_rw_lock(SECTION_LIST *p_section)
1774     {
1775     int timer = 0;
1776     int sid = (p_section == NULL ? 0 : p_section->sid);
1777     int ret = -1;
1778    
1779     while (!SYS_server_exit)
1780     {
1781     ret = section_list_try_rw_lock(p_section, SECTION_TRY_LOCK_WAIT_TIME);
1782     if (ret == 0) // success
1783     {
1784     break;
1785     }
1786     else if (errno == EAGAIN || errno == EINTR) // retry
1787     {
1788     timer++;
1789     if (timer % SECTION_TRY_LOCK_TIMES == 0)
1790     {
1791 sysadm 1.45 log_error("section_list_try_rw_lock() tried %d times on section %d\n", timer, sid);
1792 sysadm 1.23 }
1793     }
1794     else // failed
1795     {
1796 sysadm 1.37 log_error("section_list_try_rw_lock() failed on section %d\n", sid);
1797 sysadm 1.23 break;
1798     }
1799     }
1800    
1801     return ret;
1802     }

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