/[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.65 - (hide annotations)
Wed Nov 26 02:04:02 2025 UTC (3 months, 2 weeks ago) by sysadm
Branch: MAIN
Changes since 1.64: +42 -10 lines
Content type: text/x-csrc
Add section_list_set_article_excerption()

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

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