/[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.61 - (hide annotations)
Tue Nov 18 15:15:18 2025 UTC (3 months, 4 weeks ago) by sysadm
Branch: MAIN
Changes since 1.60: +8 -22 lines
Content type: text/x-csrc
Skip remap shared memory in read only mode under Cygwin
shmat() again after shmdt() without SHM_REMAP would change the address

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

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