/[LeafOK_CVS]/lbbs/src/section_list.c
ViewVC logotype

Contents of /lbbs/src/section_list.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.56 - (show annotations)
Tue Nov 11 00:28:05 2025 UTC (4 months ago) by sysadm
Branch: MAIN
Changes since 1.55: +4 -0 lines
Content type: text/x-csrc
Use config.h

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

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