/[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.50 - (hide annotations)
Mon Nov 3 15:05:02 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.49: +12 -5 lines
Content type: text/x-csrc
Fix bugs of calculate / update page_count / article_count

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

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