/[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.68 - (hide annotations)
Wed Jan 7 14:39:16 2026 UTC (2 months, 1 week ago) by sysadm
Branch: MAIN
CVS Tags: HEAD
Changes since 1.67: +18 -9 lines
Content type: text/x-csrc
Merge pull request [#88](https://github.com/leafok/lbbs/issues/88) from 0xfullex/fix/section-list-overflow
fix(section_list): fix binary search overflow and linked list boundary

Refine code based on suggestion provided in PR [#87](https://github.com/leafok/lbbs/issues/87)

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

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