/[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.62 - (hide annotations)
Thu Nov 20 03:38:49 2025 UTC (3 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.61: +145 -121 lines
Content type: text/x-csrc
Use POSIX shared object instead of SysV shared segment in section_list

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

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