/[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.59 - (hide annotations)
Mon Nov 17 12:16:48 2025 UTC (3 months, 4 weeks ago) by sysadm
Branch: MAIN
Changes since 1.58: +18 -0 lines
Content type: text/x-csrc
Add alternative implementation of shmat(..., SHM_REMAP) under MSYS2
Fix error reported by gcc under MSYS2

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

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