/[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.55 - (hide annotations)
Wed Nov 5 04:19:21 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.54: +10 -7 lines
Content type: text/x-csrc
Use enum / const int instead of macro define constant integers
Use const char * instead of macro define for constant strings

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

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