/[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.49 - (hide annotations)
Mon Nov 3 08:48:56 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.48: +4 -2 lines
Content type: text/x-csrc
Fix bug

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.11 if ((p_article->visible && p_section->last_page_visible_article_count % BBS_article_limit_per_page == 0) ||
868     p_section->article_count == 1)
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.39 page_count = p_section->page_count - (p_section->last_page_visible_article_count > 0 ? 1 : 0) +
1067 sysadm 1.35 (p_section->last_page_visible_article_count + p_section->ontop_article_count) / BBS_article_limit_per_page +
1068     ((p_section->last_page_visible_article_count + p_section->ontop_article_count) % BBS_article_limit_per_page == 0 ? 0 : 1);
1069    
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     p_section->last_page_visible_article_count = visible_article_count;
1283    
1284 sysadm 1.8 return 0;
1285 sysadm 1.1 }
1286 sysadm 1.11
1287 sysadm 1.25 int32_t article_block_last_aid(void)
1288     {
1289     ARTICLE_BLOCK *p_block = p_article_block_pool->p_block[p_article_block_pool->block_count - 1];
1290     int32_t last_aid = p_block->articles[p_block->article_count - 1].aid;
1291    
1292     return last_aid;
1293     }
1294    
1295 sysadm 1.27 int article_block_article_count(void)
1296     {
1297     int ret;
1298    
1299     if (p_article_block_pool == NULL || p_article_block_pool->block_count <= 0)
1300     {
1301     return -1;
1302     }
1303    
1304     ret = (p_article_block_pool->block_count - 1) * ARTICLE_PER_BLOCK +
1305     p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->article_count;
1306    
1307     return ret;
1308     }
1309    
1310 sysadm 1.14 int article_count_of_topic(int32_t aid)
1311 sysadm 1.11 {
1312     ARTICLE *p_article;
1313     int article_count;
1314    
1315     p_article = article_block_find_by_aid(aid);
1316     if (p_article == NULL)
1317     {
1318     return 0; // Not found
1319     }
1320    
1321     article_count = 0;
1322    
1323     do
1324     {
1325 sysadm 1.15 if (p_article->tid != 0 && p_article->tid != aid)
1326     {
1327     log_error("article_count_of_topic(%d) error: article %d not linked to the topic\n", aid, p_article->aid);
1328     break;
1329     }
1330    
1331 sysadm 1.11 article_count++;
1332     p_article = p_article->p_topic_next;
1333     } while (p_article->aid != aid);
1334    
1335     return article_count;
1336     }
1337    
1338     int section_list_move_topic(SECTION_LIST *p_section_src, SECTION_LIST *p_section_dest, int32_t aid)
1339     {
1340     ARTICLE *p_article;
1341     ARTICLE *p_next;
1342     int32_t page;
1343     int32_t offset;
1344     int32_t move_article_count;
1345     int32_t dest_article_count_old;
1346     int32_t last_unaffected_aid_src;
1347 sysadm 1.12 int32_t first_inserted_aid_dest;
1348     int move_counter;
1349 sysadm 1.11
1350 sysadm 1.28 if (p_section_src == NULL || p_section_dest == NULL)
1351 sysadm 1.11 {
1352 sysadm 1.42 log_error("NULL pointer error\n");
1353 sysadm 1.11 return -1;
1354     }
1355    
1356 sysadm 1.28 if (p_section_src->sid == p_section_dest->sid)
1357     {
1358     log_error("section_list_move_topic() src and dest section are the same\n");
1359     return -1;
1360     }
1361    
1362 sysadm 1.14 if ((p_article = article_block_find_by_aid(aid)) == NULL)
1363 sysadm 1.11 {
1364 sysadm 1.28 log_error("article_block_find_by_aid(aid = %d) error: article not found\n", aid);
1365 sysadm 1.14 return -2;
1366     }
1367    
1368     if (p_section_src->sid != p_article->sid)
1369     {
1370     log_error("section_list_move_topic() error: src section sid %d != article %d sid %d\n",
1371     p_section_src->sid, p_article->aid, p_article->sid);
1372 sysadm 1.11 return -2;
1373     }
1374    
1375     if (p_article->tid != 0)
1376     {
1377     log_error("section_list_move_topic(aid = %d) error: article is not head of topic, tid = %d\n", aid, p_article->tid);
1378     return -2;
1379     }
1380    
1381     last_unaffected_aid_src = (p_article == p_section_src->p_article_head ? 0 : p_article->p_prior->aid);
1382    
1383 sysadm 1.14 move_article_count = article_count_of_topic(aid);
1384 sysadm 1.11 if (move_article_count <= 0)
1385     {
1386 sysadm 1.22 log_error("article_count_of_topic(aid = %d) <= 0\n", aid);
1387 sysadm 1.11 return -2;
1388     }
1389    
1390     if (p_section_dest->article_count + move_article_count > BBS_article_limit_per_section)
1391     {
1392     log_error("section_list_move_topic() error: article_count %d reach limit in section %d\n",
1393     p_section_dest->article_count + move_article_count, p_section_dest->sid);
1394     return -3;
1395     }
1396    
1397     dest_article_count_old = p_section_dest->article_count;
1398 sysadm 1.12 move_counter = 0;
1399     first_inserted_aid_dest = p_article->aid;
1400 sysadm 1.11
1401     do
1402     {
1403 sysadm 1.14 if (p_section_src->sid != p_article->sid)
1404 sysadm 1.11 {
1405 sysadm 1.28 log_error("section_list_move_topic() warning: src section sid %d != article %d sid %d\n",
1406 sysadm 1.14 p_section_src->sid, p_article->aid, p_article->sid);
1407 sysadm 1.28 p_article = p_article->p_topic_next;
1408     continue;
1409 sysadm 1.11 }
1410    
1411     // Remove from bi-directional article list of src section
1412     if (p_section_src->p_article_head == p_article)
1413     {
1414     p_section_src->p_article_head = p_article->p_next;
1415     }
1416     if (p_section_src->p_article_tail == p_article)
1417     {
1418     p_section_src->p_article_tail = p_article->p_prior;
1419     }
1420     if (p_section_src->p_article_head == p_article) // || p_section_src->p_article_tail == p_article
1421     {
1422     p_section_src->p_article_head = NULL;
1423     p_section_src->p_article_tail = NULL;
1424     }
1425    
1426     p_article->p_prior->p_next = p_article->p_next;
1427     p_article->p_next->p_prior = p_article->p_prior;
1428    
1429 sysadm 1.14 // Update sid of article
1430     p_article->sid = p_section_dest->sid;
1431    
1432     if (section_list_find_article_with_offset(p_section_dest, p_article->aid, &page, &offset, &p_next) != NULL)
1433     {
1434 sysadm 1.28 log_error("section_list_move_topic() warning: article %d already in section %d\n", p_article->aid, p_section_dest->sid);
1435     p_article = p_article->p_topic_next;
1436     continue;
1437 sysadm 1.14 }
1438    
1439 sysadm 1.11 // Insert into bi-directional article list of dest section
1440     if (p_next == NULL) // empty section
1441     {
1442     p_section_dest->p_article_head = p_article;
1443     p_section_dest->p_article_tail = p_article;
1444     p_article->p_prior = p_article;
1445     p_article->p_next = p_article;
1446     }
1447     else
1448     {
1449     if (p_section_dest->p_article_head == p_next)
1450     {
1451     if (p_article->aid < p_next->aid)
1452     {
1453     p_section_dest->p_article_head = p_article;
1454     }
1455     else // p_article->aid > p_next->aid
1456     {
1457     p_section_dest->p_article_tail = p_article;
1458     }
1459     }
1460    
1461     p_article->p_prior = p_next->p_prior;
1462     p_article->p_next = p_next;
1463     p_next->p_prior->p_next = p_article;
1464     p_next->p_prior = p_article;
1465     }
1466    
1467     // Update article / topic counter of src / desc section
1468     p_section_src->article_count--;
1469     p_section_dest->article_count++;
1470     if (p_article->tid == 0)
1471     {
1472     p_section_src->topic_count--;
1473     p_section_dest->topic_count++;
1474     }
1475    
1476     // Update visible article / topic counter of src / desc section
1477     if (p_article->visible)
1478     {
1479     p_section_src->visible_article_count--;
1480     p_section_dest->visible_article_count++;
1481     if (p_article->tid == 0)
1482     {
1483     p_section_src->visible_topic_count--;
1484     p_section_dest->visible_topic_count++;
1485     }
1486     }
1487    
1488     // Update page for empty dest section
1489     if (p_section_dest->article_count == 1)
1490     {
1491     p_section_dest->p_page_first_article[0] = p_article;
1492     p_section_dest->page_count = 1;
1493     p_section_dest->last_page_visible_article_count = (p_article->visible ? 1 : 0);
1494     }
1495    
1496     p_article = p_article->p_topic_next;
1497 sysadm 1.12
1498     move_counter++;
1499     if (move_counter % CALCULATE_PAGE_THRESHOLD == 0)
1500     {
1501     // Re-calculate pages of desc section
1502     if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1503     {
1504 sysadm 1.14 log_error("section_list_calculate_page(dest section = %d, aid = %d) error\n",
1505 sysadm 1.12 p_section_dest->sid, first_inserted_aid_dest);
1506     }
1507    
1508     first_inserted_aid_dest = p_article->aid;
1509     }
1510 sysadm 1.11 } while (p_article->aid != aid);
1511    
1512     if (p_section_dest->article_count - dest_article_count_old != move_article_count)
1513     {
1514 sysadm 1.28 log_error("section_list_move_topic() warning: count of moved articles %d != %d\n",
1515 sysadm 1.11 p_section_dest->article_count - dest_article_count_old, move_article_count);
1516     }
1517    
1518 sysadm 1.12 // Re-calculate pages of src section
1519 sysadm 1.11 if (section_list_calculate_page(p_section_src, last_unaffected_aid_src) < 0)
1520     {
1521 sysadm 1.14 log_error("section_list_calculate_page(src section = %d, aid = %d) error at aid = %d\n",
1522 sysadm 1.12 p_section_src->sid, last_unaffected_aid_src, aid);
1523 sysadm 1.11 }
1524 sysadm 1.12
1525     if (move_counter % CALCULATE_PAGE_THRESHOLD != 0)
1526 sysadm 1.11 {
1527 sysadm 1.12 // Re-calculate pages of desc section
1528     if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1529     {
1530 sysadm 1.14 log_error("section_list_calculate_page(dest section = %d, aid = %d) error\n",
1531 sysadm 1.12 p_section_dest->sid, first_inserted_aid_dest);
1532     }
1533 sysadm 1.11 }
1534    
1535     return move_article_count;
1536     }
1537 sysadm 1.16
1538     int get_section_index(SECTION_LIST *p_section)
1539     {
1540     int index;
1541    
1542     if (p_section_list_pool == NULL)
1543     {
1544     log_error("get_section_index() error: uninitialized\n");
1545     return -1;
1546     }
1547    
1548     if (p_section == NULL)
1549     {
1550     index = BBS_max_section;
1551     }
1552     else
1553     {
1554 sysadm 1.22 index = (int)(p_section - p_section_list_pool->sections);
1555 sysadm 1.16 if (index < 0 || index >= BBS_max_section)
1556     {
1557     log_error("get_section_index(%d) error: index out of range\n", index);
1558     return -2;
1559     }
1560     }
1561    
1562     return index;
1563     }
1564    
1565 sysadm 1.48 int get_section_info(SECTION_LIST *p_section, char *sname, char *stitle, char *master_list)
1566     {
1567     if (p_section == NULL)
1568     {
1569     log_error("NULL pointer error\n");
1570     return -1;
1571     }
1572    
1573     if (section_list_rd_lock(p_section) < 0)
1574     {
1575     log_error("section_list_rd_lock(sid=%d) error\n", p_section->sid);
1576     return -2;
1577     }
1578    
1579     if (sname != NULL)
1580     {
1581     memcpy(sname, p_section->sname, sizeof(p_section->sname));
1582     }
1583     if (stitle != NULL)
1584     {
1585     memcpy(stitle, p_section->stitle, sizeof(p_section->stitle));
1586     }
1587     if (master_list != NULL)
1588     {
1589     memcpy(master_list, p_section->master_list, sizeof(p_section->master_list));
1590     }
1591    
1592     // release lock of section
1593     if (section_list_rd_unlock(p_section) < 0)
1594     {
1595     log_error("section_list_rd_unlock(sid=%d) error\n", p_section->sid);
1596     return -2;
1597     }
1598    
1599     return 0;
1600     }
1601    
1602 sysadm 1.16 int section_list_try_rd_lock(SECTION_LIST *p_section, int wait_sec)
1603     {
1604     int index;
1605 sysadm 1.17 struct sembuf sops[4];
1606 sysadm 1.16 struct timespec timeout;
1607     int ret;
1608    
1609     index = get_section_index(p_section);
1610     if (index < 0)
1611     {
1612     return -2;
1613     }
1614    
1615 sysadm 1.17 sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1616     sops[0].sem_op = 0; // wait until unlocked
1617 sysadm 1.16 sops[0].sem_flg = 0;
1618    
1619 sysadm 1.17 sops[1].sem_num = (unsigned short)(index * 2); // r_sem of section index
1620     sops[1].sem_op = 1; // lock
1621     sops[1].sem_flg = SEM_UNDO; // undo on terminate
1622    
1623     // Read lock on any specific section will also acquire single read lock on "all section"
1624     // so that write lock on all section only need to acquire single write on on "all section"
1625     // rather than to acquire multiple write locks on all the available sections.
1626 sysadm 1.21 if (index != BBS_max_section)
1627 sysadm 1.17 {
1628     sops[2].sem_num = BBS_max_section * 2 + 1; // w_sem of all section
1629     sops[2].sem_op = 0; // wait until unlocked
1630     sops[2].sem_flg = 0;
1631    
1632     sops[3].sem_num = BBS_max_section * 2; // r_sem of all section
1633     sops[3].sem_op = 1; // lock
1634     sops[3].sem_flg = SEM_UNDO; // undo on terminate
1635     }
1636 sysadm 1.16
1637     timeout.tv_sec = wait_sec;
1638     timeout.tv_nsec = 0;
1639    
1640 sysadm 1.22 ret = semtimedop(p_section_list_pool->semid, sops, (index == BBS_max_section ? 2 : 4), &timeout);
1641 sysadm 1.17 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1642     {
1643     log_error("semtimedop(index = %d, lock read) error %d\n", index, errno);
1644     }
1645 sysadm 1.16
1646     return ret;
1647     }
1648    
1649     int section_list_try_rw_lock(SECTION_LIST *p_section, int wait_sec)
1650     {
1651     int index;
1652     struct sembuf sops[3];
1653     struct timespec timeout;
1654     int ret;
1655    
1656     index = get_section_index(p_section);
1657     if (index < 0)
1658     {
1659     return -2;
1660     }
1661    
1662 sysadm 1.17 sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1663     sops[0].sem_op = 0; // wait until unlocked
1664 sysadm 1.16 sops[0].sem_flg = 0;
1665    
1666 sysadm 1.17 sops[1].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1667     sops[1].sem_op = 1; // lock
1668     sops[1].sem_flg = SEM_UNDO; // undo on terminate
1669    
1670     sops[2].sem_num = (unsigned short)(index * 2); // r_sem of section index
1671     sops[2].sem_op = 0; // wait until unlocked
1672     sops[2].sem_flg = 0;
1673 sysadm 1.16
1674     timeout.tv_sec = wait_sec;
1675     timeout.tv_nsec = 0;
1676    
1677 sysadm 1.22 ret = semtimedop(p_section_list_pool->semid, sops, 3, &timeout);
1678 sysadm 1.17 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1679     {
1680     log_error("semtimedop(index = %d, lock write) error %d\n", index, errno);
1681     }
1682 sysadm 1.16
1683     return ret;
1684     }
1685    
1686     int section_list_rd_unlock(SECTION_LIST *p_section)
1687     {
1688     int index;
1689 sysadm 1.17 struct sembuf sops[2];
1690 sysadm 1.16 int ret;
1691    
1692     index = get_section_index(p_section);
1693     if (index < 0)
1694     {
1695     return -2;
1696     }
1697    
1698 sysadm 1.17 sops[0].sem_num = (unsigned short)(index * 2); // r_sem of section index
1699     sops[0].sem_op = -1; // unlock
1700     sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
1701 sysadm 1.16
1702 sysadm 1.21 if (index != BBS_max_section)
1703 sysadm 1.17 {
1704     sops[1].sem_num = BBS_max_section * 2; // r_sem of all section
1705     sops[1].sem_op = -1; // unlock
1706     sops[1].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
1707     }
1708    
1709 sysadm 1.22 ret = semop(p_section_list_pool->semid, sops, (index == BBS_max_section ? 1 : 2));
1710 sysadm 1.17 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1711     {
1712     log_error("semop(index = %d, unlock read) error %d\n", index, errno);
1713     }
1714 sysadm 1.16
1715     return ret;
1716     }
1717    
1718     int section_list_rw_unlock(SECTION_LIST *p_section)
1719     {
1720     int index;
1721     struct sembuf sops[1];
1722     int ret;
1723    
1724     index = get_section_index(p_section);
1725     if (index < 0)
1726     {
1727     return -2;
1728     }
1729    
1730 sysadm 1.17 sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1731     sops[0].sem_op = -1; // unlock
1732     sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
1733 sysadm 1.16
1734 sysadm 1.22 ret = semop(p_section_list_pool->semid, sops, 1);
1735 sysadm 1.17 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1736     {
1737     log_error("semop(index = %d, unlock write) error %d\n", index, errno);
1738     }
1739 sysadm 1.16
1740     return ret;
1741     }
1742 sysadm 1.23
1743     int section_list_rd_lock(SECTION_LIST *p_section)
1744     {
1745     int timer = 0;
1746     int sid = (p_section == NULL ? 0 : p_section->sid);
1747     int ret = -1;
1748    
1749     while (!SYS_server_exit)
1750     {
1751     ret = section_list_try_rd_lock(p_section, SECTION_TRY_LOCK_WAIT_TIME);
1752     if (ret == 0) // success
1753     {
1754     break;
1755     }
1756     else if (errno == EAGAIN || errno == EINTR) // retry
1757     {
1758     timer++;
1759     if (timer % SECTION_TRY_LOCK_TIMES == 0)
1760     {
1761 sysadm 1.45 log_error("section_list_try_rd_lock() tried %d times on section %d\n", timer, sid);
1762 sysadm 1.23 }
1763     }
1764     else // failed
1765     {
1766 sysadm 1.37 log_error("section_list_try_rd_lock() failed on section %d\n", sid);
1767 sysadm 1.23 break;
1768     }
1769     }
1770    
1771     return ret;
1772     }
1773    
1774     int section_list_rw_lock(SECTION_LIST *p_section)
1775     {
1776     int timer = 0;
1777     int sid = (p_section == NULL ? 0 : p_section->sid);
1778     int ret = -1;
1779    
1780     while (!SYS_server_exit)
1781     {
1782     ret = section_list_try_rw_lock(p_section, SECTION_TRY_LOCK_WAIT_TIME);
1783     if (ret == 0) // success
1784     {
1785     break;
1786     }
1787     else if (errno == EAGAIN || errno == EINTR) // retry
1788     {
1789     timer++;
1790     if (timer % SECTION_TRY_LOCK_TIMES == 0)
1791     {
1792 sysadm 1.45 log_error("section_list_try_rw_lock() tried %d times on section %d\n", timer, sid);
1793 sysadm 1.23 }
1794     }
1795     else // failed
1796     {
1797 sysadm 1.37 log_error("section_list_try_rw_lock() failed on section %d\n", sid);
1798 sysadm 1.23 break;
1799     }
1800     }
1801    
1802     return ret;
1803     }

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