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

Contents of /lbbs/src/user_list.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.42 - (show annotations)
Thu Nov 20 09:02:46 2025 UTC (3 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.41: +162 -26 lines
Content type: text/x-csrc
Add alternative POSIX semaphore based rd/rw (un)lock in user_list

1 /* SPDX-License-Identifier: GPL-3.0-or-later */
2 /*
3 * user_list
4 * - data model and basic operations of (online) user list
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 "common.h"
14 #include "database.h"
15 #include "log.h"
16 #include "trie_dict.h"
17 #include "user_list.h"
18 #include "user_stat.h"
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <time.h>
24 #include <sys/mman.h>
25 #include <sys/param.h>
26 #include <sys/stat.h>
27
28 #ifdef HAVE_SYSTEM_V
29 #include <sys/sem.h>
30
31 #ifdef _SEM_SEMUN_UNDEFINED
32 union semun
33 {
34 int val; /* Value for SETVAL */
35 struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
36 unsigned short *array; /* Array for GETALL, SETALL */
37 struct seminfo *__buf; /* Buffer for IPC_INFO
38 (Linux-specific) */
39 };
40 #endif // #ifdef _SEM_SEMUN_UNDEFINED
41
42 #else
43 #include <semaphore.h>
44 #endif
45
46 enum _user_list_constant_t
47 {
48 USER_LIST_TRY_LOCK_WAIT_TIME = 1, // second
49 USER_LIST_TRY_LOCK_TIMES = 10,
50 };
51
52 struct user_list_pool_t
53 {
54 size_t shm_size;
55 #ifndef HAVE_SYSTEM_V
56 sem_t sem;
57 int read_lock_count;
58 int write_lock_count;
59 #else
60 int semid;
61 #endif
62 USER_LIST user_list[2];
63 int user_list_index_current;
64 int user_list_index_new;
65 USER_ONLINE_LIST user_online_list[2];
66 int user_online_list_index_current;
67 int user_online_list_index_new;
68 USER_STAT_MAP user_stat_map;
69 int user_login_count;
70 };
71 typedef struct user_list_pool_t USER_LIST_POOL;
72
73 static char user_list_shm_name[FILE_NAME_LEN];
74 static USER_LIST_POOL *p_user_list_pool = NULL;
75 static TRIE_NODE *p_trie_action_dict = NULL;
76
77 typedef struct user_action_map_t
78 {
79 char name[BBS_current_action_max_len + 1];
80 char title[BBS_current_action_max_len + 1];
81 } USER_ACTION_MAP;
82
83 const USER_ACTION_MAP user_action_map[] =
84 {
85 {"ARTICLE_FAVOR", "浏览收藏"},
86 {"BBS_NET", "站点穿梭"},
87 {"CHICKEN", "电子小鸡"},
88 {"EDIT_ARTICLE", "修改文章"},
89 {"LOGIN", "进入大厅"},
90 {"MENU", "菜单选择"},
91 {"POST_ARTICLE", "撰写文章"},
92 {"REPLY_ARTICLE", "回复文章"},
93 {"USER_LIST", "查花名册"},
94 {"USER_ONLINE", "环顾四周"},
95 {"VIEW_ARTICLE", "阅读文章"},
96 {"VIEW_FILE", "查看文档"},
97 {"WWW", "Web浏览"}};
98
99 const int user_action_map_size = sizeof(user_action_map) / sizeof(USER_ACTION_MAP);
100
101 static int user_list_try_rd_lock(int wait_sec);
102 static int user_list_try_rw_lock(int wait_sec);
103 static int user_list_rd_unlock(void);
104 static int user_list_rw_unlock(void);
105 static int user_list_rd_lock(void);
106 static int user_list_rw_lock(void);
107
108 static int user_list_load(MYSQL *db, USER_LIST *p_list);
109 static int user_online_list_load(MYSQL *db, USER_ONLINE_LIST *p_online_list);
110 static int user_login_count_load(MYSQL *db);
111
112 static int user_info_index_uid_comp(const void *ptr1, const void *ptr2)
113 {
114 const USER_INFO_INDEX_UID *p1 = ptr1;
115 const USER_INFO_INDEX_UID *p2 = ptr2;
116
117 if (p1->uid < p2->uid)
118 {
119 return -1;
120 }
121 else if (p1->uid > p2->uid)
122 {
123 return 1;
124 }
125 else if (p1->id < p2->id)
126 {
127 return -1;
128 }
129 else if (p1->id > p2->id)
130 {
131 return 1;
132 }
133 return 0;
134 }
135
136 int user_list_load(MYSQL *db, USER_LIST *p_list)
137 {
138 MYSQL_RES *rs = NULL;
139 MYSQL_ROW row;
140 char sql[SQL_BUFFER_LEN];
141 int ret = 0;
142 int i;
143 int j;
144 int32_t last_uid;
145 size_t intro_buf_offset;
146 size_t intro_len;
147
148 if (db == NULL || p_list == NULL)
149 {
150 log_error("NULL pointer error\n");
151 return -1;
152 }
153
154 if (p_list->user_count > 0)
155 {
156 last_uid = p_list->users[p_list->user_count - 1].uid;
157 }
158 else
159 {
160 last_uid = -1;
161 }
162
163 snprintf(sql, sizeof(sql),
164 "SELECT user_list.UID AS UID, username, nickname, gender, gender_pub, life, exp, visit_count, "
165 "UNIX_TIMESTAMP(signup_dt), UNIX_TIMESTAMP(last_login_dt), UNIX_TIMESTAMP(last_logout_dt), "
166 "UNIX_TIMESTAMP(birthday), `introduction` "
167 "FROM user_list INNER JOIN user_pubinfo ON user_list.UID = user_pubinfo.UID "
168 "INNER JOIN user_reginfo ON user_list.UID = user_reginfo.UID "
169 "WHERE enable ORDER BY username");
170
171 if (mysql_query(db, sql) != 0)
172 {
173 log_error("Query user info error: %s\n", mysql_error(db));
174 ret = -1;
175 goto cleanup;
176 }
177
178 if ((rs = mysql_use_result(db)) == NULL)
179 {
180 log_error("Get user info data failed\n");
181 ret = -1;
182 goto cleanup;
183 }
184
185 intro_buf_offset = 0;
186 i = 0;
187 while ((row = mysql_fetch_row(rs)))
188 {
189 // record
190 p_list->users[i].id = i;
191 p_list->users[i].uid = atoi(row[0]);
192 strncpy(p_list->users[i].username, row[1], sizeof(p_list->users[i].username) - 1);
193 p_list->users[i].username[sizeof(p_list->users[i].username) - 1] = '\0';
194 strncpy(p_list->users[i].nickname, row[2], sizeof(p_list->users[i].nickname) - 1);
195 p_list->users[i].nickname[sizeof(p_list->users[i].nickname) - 1] = '\0';
196 p_list->users[i].gender = row[3][0];
197 p_list->users[i].gender_pub = (int8_t)(row[4] == NULL ? 0 : atoi(row[4]));
198 p_list->users[i].life = (row[5] == NULL ? 0 : atoi(row[5]));
199 p_list->users[i].exp = (row[6] == NULL ? 0 : atoi(row[6]));
200 p_list->users[i].visit_count = (row[7] == NULL ? 0 : atoi(row[7]));
201 p_list->users[i].signup_dt = (row[8] == NULL ? 0 : atol(row[8]));
202 p_list->users[i].last_login_dt = (row[9] == NULL ? 0 : atol(row[9]));
203 p_list->users[i].last_logout_dt = (row[10] == NULL ? 0 : atol(row[10]));
204 p_list->users[i].birthday = (row[11] == NULL ? 0 : atol(row[11]));
205 intro_len = strlen((row[12] == NULL ? "" : row[12]));
206 if (intro_len >= sizeof(p_list->user_intro_buf) - 1 - intro_buf_offset)
207 {
208 log_error("OOM for user introduction: len=%d, i=%d\n", intro_len, i);
209 break;
210 }
211 memcpy(p_list->user_intro_buf + intro_buf_offset,
212 (row[12] == NULL ? "" : row[12]),
213 intro_len + 1);
214 p_list->users[i].intro = p_list->user_intro_buf + intro_buf_offset;
215 intro_buf_offset += (intro_len + 1);
216
217 i++;
218 if (i >= BBS_max_user_count)
219 {
220 log_error("Too many users, exceed limit %d\n", BBS_max_user_count);
221 break;
222 }
223 }
224 mysql_free_result(rs);
225 rs = NULL;
226
227 if (i != p_list->user_count || p_list->users[i - 1].uid != last_uid) // Count of users changed
228 {
229 // Rebuild index
230 for (j = 0; j < i; j++)
231 {
232 p_list->index_uid[j].uid = p_list->users[j].uid;
233 p_list->index_uid[j].id = j;
234 }
235
236 qsort(p_list->index_uid, (size_t)i, sizeof(USER_INFO_INDEX_UID), user_info_index_uid_comp);
237
238 #ifdef _DEBUG
239 log_error("Rebuild index of %d users, last_uid=%d\n", i, p_list->users[i - 1].uid);
240 #endif
241 }
242
243 p_list->user_count = i;
244
245 #ifdef _DEBUG
246 log_error("Loaded %d users\n", p_list->user_count);
247 #endif
248
249 cleanup:
250 mysql_free_result(rs);
251
252 return ret;
253 }
254
255 int user_online_list_load(MYSQL *db, USER_ONLINE_LIST *p_online_list)
256 {
257 MYSQL_RES *rs = NULL;
258 MYSQL_ROW row;
259 char sql[SQL_BUFFER_LEN];
260 int ret = 0;
261 int i;
262 int j;
263 int user_cnt;
264 int guest_cnt;
265
266 if (db == NULL || p_online_list == NULL)
267 {
268 log_error("NULL pointer error\n");
269 return -1;
270 }
271
272 snprintf(sql, sizeof(sql),
273 "SELECT SID, UID, ip, current_action, UNIX_TIMESTAMP(login_tm), "
274 "UNIX_TIMESTAMP(last_tm) FROM user_online "
275 "WHERE last_tm >= SUBDATE(NOW(), INTERVAL %d SECOND) "
276 "ORDER BY last_tm DESC",
277 BBS_user_off_line);
278
279 if (mysql_query(db, sql) != 0)
280 {
281 log_error("Query user online error: %s\n", mysql_error(db));
282 ret = -1;
283 goto cleanup;
284 }
285
286 if ((rs = mysql_use_result(db)) == NULL)
287 {
288 log_error("Get user online data failed\n");
289 ret = -1;
290 goto cleanup;
291 }
292
293 i = 0;
294 user_cnt = 0;
295 guest_cnt = 0;
296 while ((row = mysql_fetch_row(rs)))
297 {
298 if (atoi(row[1]) == 0) // guest
299 {
300 guest_cnt++;
301 continue;
302 }
303 else
304 {
305 user_cnt++;
306 }
307
308 p_online_list->users[i].id = i;
309 strncpy(p_online_list->users[i].session_id, row[0], sizeof(p_online_list->users[i].session_id) - 1);
310 p_online_list->users[i].session_id[sizeof(p_online_list->users[i].session_id) - 1] = '\0';
311
312 if ((ret = query_user_info_by_uid(atoi(row[1]), &(p_online_list->users[i].user_info), NULL, 0)) <= 0)
313 {
314 log_error("query_user_info_by_uid(%d) error\n", atoi(row[1]));
315 continue;
316 }
317
318 strncpy(p_online_list->users[i].ip, row[2], sizeof(p_online_list->users[i].ip) - 1);
319 p_online_list->users[i].ip[sizeof(p_online_list->users[i].ip) - 1] = '\0';
320
321 strncpy(p_online_list->users[i].current_action, row[3], sizeof(p_online_list->users[i].current_action) - 1);
322 p_online_list->users[i].current_action[sizeof(p_online_list->users[i].current_action) - 1] = '\0';
323 p_online_list->users[i].current_action_title = NULL;
324 if (p_online_list->users[i].current_action[0] == '\0')
325 {
326 p_online_list->users[i].current_action_title = "";
327 }
328 else if (trie_dict_get(p_trie_action_dict, p_online_list->users[i].current_action, (int64_t *)(&(p_online_list->users[i].current_action_title))) < 0)
329 {
330 log_error("trie_dict_get(p_trie_action_dict, %s) error on session_id=%s\n",
331 p_online_list->users[i].current_action, p_online_list->users[i].session_id);
332 continue;
333 }
334
335 p_online_list->users[i].login_tm = (row[4] == NULL ? 0 : atol(row[4]));
336 p_online_list->users[i].last_tm = (row[5] == NULL ? 0 : atol(row[5]));
337
338 i++;
339 if (i >= BBS_max_user_online_count)
340 {
341 log_error("Too many online users, exceed limit %d\n", BBS_max_user_online_count);
342 break;
343 }
344 }
345 mysql_free_result(rs);
346 rs = NULL;
347
348 if (user_cnt > 0)
349 {
350 // Rebuild index
351 for (j = 0; j < user_cnt; j++)
352 {
353 p_online_list->index_uid[j].uid = p_online_list->users[j].user_info.uid;
354 p_online_list->index_uid[j].id = j;
355 }
356
357 qsort(p_online_list->index_uid, (size_t)user_cnt, sizeof(USER_INFO_INDEX_UID), user_info_index_uid_comp);
358 }
359
360 p_online_list->user_count = user_cnt;
361 p_online_list->guest_count = guest_cnt;
362
363 cleanup:
364 mysql_free_result(rs);
365
366 return ret;
367 }
368
369 int user_login_count_load(MYSQL *db)
370 {
371 MYSQL_RES *rs = NULL;
372 MYSQL_ROW row;
373 char sql[SQL_BUFFER_LEN];
374
375 if (db == NULL)
376 {
377 log_error("NULL pointer error\n");
378 return -1;
379 }
380
381 snprintf(sql, sizeof(sql),
382 "SELECT ID FROM user_login_log ORDER BY ID DESC LIMIT 1");
383 if (mysql_query(db, sql) != 0)
384 {
385 log_error("Query user_login_log error: %s\n", mysql_error(db));
386 return -2;
387 }
388 if ((rs = mysql_store_result(db)) == NULL)
389 {
390 log_error("Get user_login_log data failed\n");
391 return -2;
392 }
393 if ((row = mysql_fetch_row(rs)))
394 {
395 p_user_list_pool->user_login_count = atoi(row[0]);
396 }
397 mysql_free_result(rs);
398
399 return 0;
400 }
401
402 int user_list_pool_init(const char *filename)
403 {
404 char filepath[FILE_PATH_LEN];
405 int fd;
406 size_t size;
407 void *p_shm;
408 #ifdef HAVE_SYSTEM_V
409 int proj_id;
410 key_t key;
411 int semid;
412 union semun arg;
413 #endif
414 int i;
415
416 if (p_user_list_pool != NULL || p_trie_action_dict != NULL)
417 {
418 log_error("p_user_list_pool already initialized\n");
419 return -1;
420 }
421
422 p_trie_action_dict = trie_dict_create();
423 if (p_trie_action_dict == NULL)
424 {
425 log_error("trie_dict_create() error\n");
426 return -1;
427 }
428
429 for (i = 0; i < user_action_map_size; i++)
430 {
431 if (trie_dict_set(p_trie_action_dict, user_action_map[i].name, (int64_t)(user_action_map[i].title)) < 0)
432 {
433 log_error("trie_dict_set(p_trie_action_dict, %s) error\n", user_action_map[i].name);
434 }
435 }
436
437 // Allocate shared memory
438 size = sizeof(USER_LIST_POOL);
439
440 strncpy(filepath, filename, sizeof(filepath) - 1);
441 filepath[sizeof(filepath) - 1] = '\0';
442 snprintf(user_list_shm_name, sizeof(user_list_shm_name), "/USER_LIST_SHM_%s", basename(filepath));
443
444 if (shm_unlink(user_list_shm_name) == -1 && errno != ENOENT)
445 {
446 log_error("shm_unlink(%s) error (%d)\n", user_list_shm_name, errno);
447 return -2;
448 }
449
450 if ((fd = shm_open(user_list_shm_name, O_CREAT | O_EXCL | O_RDWR, 0600)) == -1)
451 {
452 log_error("shm_open(%s) error (%d)\n", user_list_shm_name, errno);
453 return -2;
454 }
455 if (ftruncate(fd, (off_t)size) == -1)
456 {
457 log_error("ftruncate(size=%d) error (%d)\n", size, errno);
458 close(fd);
459 return -2;
460 }
461
462 p_shm = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0L);
463 if (p_shm == MAP_FAILED)
464 {
465 log_error("mmap() error (%d)\n", errno);
466 close(fd);
467 return -2;
468 }
469
470 if (close(fd) < 0)
471 {
472 log_error("close(fd) error (%d)\n", errno);
473 return -1;
474 }
475
476 p_user_list_pool = p_shm;
477 p_user_list_pool->shm_size = size;
478
479 // Allocate semaphore as user list pool lock
480 #ifndef HAVE_SYSTEM_V
481 if (sem_init(&(p_user_list_pool->sem), 1, 1) == -1)
482 {
483 log_error("sem_init() error (%d)\n", errno);
484 return -3;
485 }
486
487 p_user_list_pool->read_lock_count = 0;
488 p_user_list_pool->write_lock_count = 0;
489 #else
490 proj_id = (int)(time(NULL) % getpid());
491 key = ftok(filename, proj_id);
492 if (key == -1)
493 {
494 log_error("ftok(%s %d) error (%d)\n", filename, proj_id, errno);
495 return -2;
496 }
497
498 size = 2; // r_sem and w_sem
499 semid = semget(key, (int)size, IPC_CREAT | IPC_EXCL | 0600);
500 if (semid == -1)
501 {
502 log_error("semget(user_list_pool_sem, size = %d) error (%d)\n", size, errno);
503 return -3;
504 }
505
506 // Initialize sem value to 0
507 arg.val = 0;
508 for (i = 0; i < size; i++)
509 {
510 if (semctl(semid, i, SETVAL, arg) == -1)
511 {
512 log_error("semctl(user_list_pool_sem, SETVAL) error (%d)\n", errno);
513 return -3;
514 }
515 }
516
517 p_user_list_pool->semid = semid;
518 #endif
519
520 // Set user counts to 0
521 p_user_list_pool->user_list[0].user_count = 0;
522 p_user_list_pool->user_list[1].user_count = 0;
523
524 p_user_list_pool->user_list_index_current = 0;
525 p_user_list_pool->user_list_index_new = 1;
526
527 p_user_list_pool->user_online_list_index_current = 0;
528 p_user_list_pool->user_online_list_index_new = 1;
529
530 user_stat_map_init(&(p_user_list_pool->user_stat_map));
531
532 return 0;
533 }
534
535 void user_list_pool_cleanup(void)
536 {
537 if (p_user_list_pool == NULL)
538 {
539 return;
540 }
541
542 #ifdef HAVE_SYSTEM_V
543 if (semctl(p_user_list_pool->semid, 0, IPC_RMID) == -1)
544 {
545 log_error("semctl(semid = %d, IPC_RMID) error (%d)\n", p_user_list_pool->semid, errno);
546 }
547 #else
548 if (sem_destroy(&(p_user_list_pool->sem)) == -1)
549 {
550 log_error("sem_destroy() error (%d)\n", errno);
551 }
552 #endif
553
554 detach_user_list_pool_shm();
555
556 if (shm_unlink(user_list_shm_name) == -1 && errno != ENOENT)
557 {
558 log_error("shm_unlink(%s) error (%d)\n", user_list_shm_name, errno);
559 }
560
561 user_list_shm_name[0] = '\0';
562
563 if (p_trie_action_dict != NULL)
564 {
565 trie_dict_destroy(p_trie_action_dict);
566
567 p_trie_action_dict = NULL;
568 }
569 }
570
571 int set_user_list_pool_shm_readonly(void)
572 {
573 if (p_user_list_pool != NULL && mprotect(p_user_list_pool, p_user_list_pool->shm_size, PROT_READ) < 0)
574 {
575 log_error("mprotect() error (%d)\n", errno);
576 return -1;
577 }
578
579 return 0;
580 }
581
582 int detach_user_list_pool_shm(void)
583 {
584 if (p_user_list_pool != NULL && munmap(p_user_list_pool, p_user_list_pool->shm_size) < 0)
585 {
586 log_error("munmap() error (%d)\n", errno);
587 return -1;
588 }
589
590 p_user_list_pool = NULL;
591
592 return 0;
593 }
594
595 int user_list_pool_reload(int online_user)
596 {
597 MYSQL *db = NULL;
598 int tmp;
599 int ret = 0;
600
601 if (p_user_list_pool == NULL)
602 {
603 log_error("p_user_list_pool not initialized\n");
604 return -1;
605 }
606
607 db = db_open();
608 if (db == NULL)
609 {
610 log_error("db_open() error: %s\n", mysql_error(db));
611 return -1;
612 }
613
614 if (online_user)
615 {
616 if (user_online_list_load(db, &(p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_new])) < 0)
617 {
618 log_error("user_online_list_load() error\n");
619 ret = -2;
620 goto cleanup;
621 }
622
623 if (user_login_count_load(db) < 0)
624 {
625 log_error("user_login_count_load() error\n");
626 ret = -2;
627 goto cleanup;
628 }
629 }
630 else
631 {
632 if (user_list_load(db, &(p_user_list_pool->user_list[p_user_list_pool->user_list_index_new])) < 0)
633 {
634 log_error("user_list_load() error\n");
635 ret = -2;
636 goto cleanup;
637 }
638 }
639
640 mysql_close(db);
641 db = NULL;
642
643 if (user_list_rw_lock() < 0)
644 {
645 log_error("user_list_rw_lock() error\n");
646 ret = -3;
647 goto cleanup;
648 }
649
650 if (online_user)
651 {
652 // Swap p_online_current and p_online_new
653 tmp = p_user_list_pool->user_online_list_index_current;
654 p_user_list_pool->user_online_list_index_current = p_user_list_pool->user_online_list_index_new;
655 p_user_list_pool->user_online_list_index_new = tmp;
656 }
657 else
658 {
659 // Swap index_current and index_new
660 tmp = p_user_list_pool->user_list_index_current;
661 p_user_list_pool->user_list_index_current = p_user_list_pool->user_list_index_new;
662 p_user_list_pool->user_list_index_new = tmp;
663 }
664
665 if (user_list_rw_unlock() < 0)
666 {
667 log_error("user_list_rw_unlock() error\n");
668 ret = -3;
669 goto cleanup;
670 }
671
672 cleanup:
673 mysql_close(db);
674
675 return ret;
676 }
677
678 int user_list_try_rd_lock(int wait_sec)
679 {
680 #ifdef HAVE_SYSTEM_V
681 struct sembuf sops[2];
682 #endif
683 struct timespec timeout;
684 int ret = 0;
685
686 if (p_user_list_pool == NULL)
687 {
688 log_error("p_user_list_pool not initialized\n");
689 return -1;
690 }
691
692 timeout.tv_sec = wait_sec;
693 timeout.tv_nsec = 0;
694
695 #ifdef HAVE_SYSTEM_V
696 sops[0].sem_num = 1; // w_sem
697 sops[0].sem_op = 0; // wait until unlocked
698 sops[0].sem_flg = 0;
699
700 sops[1].sem_num = 0; // r_sem
701 sops[1].sem_op = 1; // lock
702 sops[1].sem_flg = SEM_UNDO; // undo on terminate
703
704 ret = semtimedop(p_user_list_pool->semid, sops, 2, &timeout);
705 if (ret == -1 && errno != EAGAIN && errno != EINTR)
706 {
707 log_error("semop(lock read) error %d\n", errno);
708 }
709 #else
710 if (sem_timedwait(&(p_user_list_pool->sem), &timeout) == -1)
711 {
712 if (errno != ETIMEDOUT && errno != EAGAIN && errno != EINTR)
713 {
714 log_error("sem_timedwait() error %d\n", errno);
715 }
716 return -1;
717 }
718
719 if (p_user_list_pool->write_lock_count == 0)
720 {
721 p_user_list_pool->read_lock_count++;
722 }
723 else
724 {
725 errno = EAGAIN;
726 ret = -1;
727 }
728
729 if (sem_post(&(p_user_list_pool->sem)) == -1)
730 {
731 log_error("sem_post() error %d\n", errno);
732 return -1;
733 }
734 #endif
735
736 return ret;
737 }
738
739 int user_list_try_rw_lock(int wait_sec)
740 {
741 #ifdef HAVE_SYSTEM_V
742 struct sembuf sops[3];
743 #endif
744 struct timespec timeout;
745 int ret = 0;
746
747 if (p_user_list_pool == NULL)
748 {
749 log_error("p_user_list_pool not initialized\n");
750 return -1;
751 }
752
753 timeout.tv_sec = wait_sec;
754 timeout.tv_nsec = 0;
755
756 #ifdef HAVE_SYSTEM_V
757 sops[0].sem_num = 1; // w_sem
758 sops[0].sem_op = 0; // wait until unlocked
759 sops[0].sem_flg = 0;
760
761 sops[1].sem_num = 1; // w_sem
762 sops[1].sem_op = 1; // lock
763 sops[1].sem_flg = SEM_UNDO; // undo on terminate
764
765 sops[2].sem_num = 0; // r_sem
766 sops[2].sem_op = 0; // wait until unlocked
767 sops[2].sem_flg = 0;
768
769 ret = semtimedop(p_user_list_pool->semid, sops, 3, &timeout);
770 if (ret == -1 && errno != EAGAIN && errno != EINTR)
771 {
772 log_error("semop(lock write) error %d\n", errno);
773 }
774 #else
775 if (sem_timedwait(&(p_user_list_pool->sem), &timeout) == -1)
776 {
777 if (errno != ETIMEDOUT && errno != EAGAIN && errno != EINTR)
778 {
779 log_error("sem_timedwait() error %d\n", errno);
780 }
781 return -1;
782 }
783
784 if (p_user_list_pool->read_lock_count == 0 && p_user_list_pool->write_lock_count == 0)
785 {
786 p_user_list_pool->write_lock_count++;
787 }
788 else
789 {
790 errno = EAGAIN;
791 ret = -1;
792 }
793
794 if (sem_post(&(p_user_list_pool->sem)) == -1)
795 {
796 log_error("sem_post() error %d\n", errno);
797 return -1;
798 }
799 #endif
800
801 return ret;
802 }
803
804 int user_list_rd_unlock(void)
805 {
806 #ifdef HAVE_SYSTEM_V
807 struct sembuf sops[2];
808 #endif
809 int ret = 0;
810
811 if (p_user_list_pool == NULL)
812 {
813 log_error("p_user_list_pool not initialized\n");
814 return -1;
815 }
816
817 #ifdef HAVE_SYSTEM_V
818 sops[0].sem_num = 0; // r_sem
819 sops[0].sem_op = -1; // unlock
820 sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
821
822 ret = semop(p_user_list_pool->semid, sops, 1);
823 if (ret == -1 && errno != EAGAIN && errno != EINTR)
824 {
825 log_error("semop(unlock read) error %d\n", errno);
826 }
827 #else
828 if (sem_wait(&(p_user_list_pool->sem)) == -1)
829 {
830 if (errno != ETIMEDOUT && errno != EAGAIN && errno != EINTR)
831 {
832 log_error("sem_timedwait() error %d\n", errno);
833 }
834 return -1;
835 }
836
837 if (p_user_list_pool->read_lock_count > 0)
838 {
839 p_user_list_pool->read_lock_count--;
840 }
841 else
842 {
843 log_error("read_lock_count already 0\n");
844 }
845
846 if (sem_post(&(p_user_list_pool->sem)) == -1)
847 {
848 log_error("sem_post() error %d\n", errno);
849 return -1;
850 }
851 #endif
852
853 return ret;
854 }
855
856 int user_list_rw_unlock(void)
857 {
858 #ifdef HAVE_SYSTEM_V
859 struct sembuf sops[1];
860 #endif
861 int ret = 0;
862
863 if (p_user_list_pool == NULL)
864 {
865 log_error("p_user_list_pool not initialized\n");
866 return -1;
867 }
868
869 #ifdef HAVE_SYSTEM_V
870 sops[0].sem_num = 1; // w_sem
871 sops[0].sem_op = -1; // unlock
872 sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
873
874 ret = semop(p_user_list_pool->semid, sops, 1);
875 if (ret == -1 && errno != EAGAIN && errno != EINTR)
876 {
877 log_error("semop(unlock write) error %d\n", errno);
878 }
879 #else
880 if (sem_wait(&(p_user_list_pool->sem)) == -1)
881 {
882 if (errno != ETIMEDOUT && errno != EAGAIN && errno != EINTR)
883 {
884 log_error("sem_timedwait() error %d\n", errno);
885 }
886 return -1;
887 }
888
889 if (p_user_list_pool->write_lock_count > 0)
890 {
891 p_user_list_pool->write_lock_count--;
892 }
893 else
894 {
895 log_error("write_lock_count already 0\n");
896 }
897
898 if (sem_post(&(p_user_list_pool->sem)) == -1)
899 {
900 log_error("sem_post() error %d\n", errno);
901 return -1;
902 }
903 #endif
904
905 return ret;
906 }
907
908 int user_list_rd_lock(void)
909 {
910 int timer = 0;
911 int ret = -1;
912
913 if (p_user_list_pool == NULL)
914 {
915 log_error("p_user_list_pool not initialized\n");
916 return -1;
917 }
918
919 while (!SYS_server_exit)
920 {
921 ret = user_list_try_rd_lock(USER_LIST_TRY_LOCK_WAIT_TIME);
922 if (ret == 0) // success
923 {
924 break;
925 }
926 else if (errno == EAGAIN || errno == EINTR) // retry
927 {
928 timer++;
929 if (timer % USER_LIST_TRY_LOCK_TIMES == 0)
930 {
931 log_error("user_list_try_rd_lock() tried %d times\n", timer);
932 }
933 usleep(100 * 1000); // 0.1 second
934 }
935 else // failed
936 {
937 log_error("user_list_try_rd_lock() failed\n");
938 break;
939 }
940 }
941
942 return ret;
943 }
944
945 int user_list_rw_lock(void)
946 {
947 int timer = 0;
948 int ret = -1;
949
950 if (p_user_list_pool == NULL)
951 {
952 log_error("p_user_list_pool not initialized\n");
953 return -1;
954 }
955
956 while (!SYS_server_exit)
957 {
958 ret = user_list_try_rw_lock(USER_LIST_TRY_LOCK_WAIT_TIME);
959 if (ret == 0) // success
960 {
961 break;
962 }
963 else if (errno == EAGAIN || errno == EINTR) // retry
964 {
965 timer++;
966 if (timer % USER_LIST_TRY_LOCK_TIMES == 0)
967 {
968 log_error("user_list_try_rw_lock() tried %d times\n", timer);
969 }
970 usleep(100 * 1000); // 0.1 second
971 }
972 else // failed
973 {
974 log_error("user_list_try_rw_lock() failed\n");
975 break;
976 }
977 }
978
979 return ret;
980 }
981
982 int query_user_list(int page_id, USER_INFO *p_users, int *p_user_count, int *p_page_count)
983 {
984 int ret = 0;
985
986 if (p_users == NULL || p_user_count == NULL || p_page_count == NULL)
987 {
988 log_error("NULL pointer error\n");
989 return -1;
990 }
991
992 *p_user_count = 0;
993 *p_page_count = 0;
994
995 // acquire lock of user list
996 if (user_list_rd_lock() < 0)
997 {
998 log_error("user_list_rd_lock() error\n");
999 return -2;
1000 }
1001
1002 if (p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count == 0)
1003 {
1004 // empty list
1005 ret = 0;
1006 goto cleanup;
1007 }
1008
1009 *p_page_count = (p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count + BBS_user_limit_per_page - 1) /
1010 BBS_user_limit_per_page;
1011
1012 if (page_id < 0 || page_id >= *p_page_count)
1013 {
1014 log_error("Invalid page_id = %d, not in range [0, %d)\n", page_id, *p_page_count);
1015 ret = -3;
1016 goto cleanup;
1017 }
1018
1019 *p_user_count = MIN(BBS_user_limit_per_page,
1020 p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count -
1021 page_id * BBS_user_limit_per_page);
1022
1023 memcpy(p_users,
1024 p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users + page_id * BBS_user_limit_per_page,
1025 sizeof(USER_INFO) * (size_t)(*p_user_count));
1026
1027 cleanup:
1028 // release lock of user list
1029 if (user_list_rd_unlock() < 0)
1030 {
1031 log_error("user_list_rd_unlock() error\n");
1032 ret = -1;
1033 }
1034
1035 return ret;
1036 }
1037
1038 int query_user_online_list(int page_id, USER_ONLINE_INFO *p_online_users, int *p_user_count, int *p_page_count)
1039 {
1040 int ret = 0;
1041
1042 if (p_online_users == NULL || p_user_count == NULL || p_page_count == NULL)
1043 {
1044 log_error("NULL pointer error\n");
1045 return -1;
1046 }
1047
1048 *p_user_count = 0;
1049 *p_page_count = 0;
1050
1051 // acquire lock of user list
1052 if (user_list_rd_lock() < 0)
1053 {
1054 log_error("user_list_rd_lock() error\n");
1055 return -2;
1056 }
1057
1058 if (p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].user_count == 0)
1059 {
1060 // empty list
1061 ret = 0;
1062 goto cleanup;
1063 }
1064
1065 *p_page_count = (p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].user_count + BBS_user_limit_per_page - 1) / BBS_user_limit_per_page;
1066
1067 if (page_id < 0 || page_id >= *p_page_count)
1068 {
1069 log_error("Invalid page_id = %d, not in range [0, %d)\n", page_id, *p_page_count);
1070 ret = -3;
1071 goto cleanup;
1072 }
1073
1074 *p_user_count = MIN(BBS_user_limit_per_page,
1075 p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].user_count -
1076 page_id * BBS_user_limit_per_page);
1077
1078 memcpy(p_online_users,
1079 p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].users + page_id * BBS_user_limit_per_page,
1080 sizeof(USER_ONLINE_INFO) * (size_t)(*p_user_count));
1081
1082 cleanup:
1083 // release lock of user list
1084 if (user_list_rd_unlock() < 0)
1085 {
1086 log_error("user_list_rd_unlock() error\n");
1087 ret = -1;
1088 }
1089
1090 return ret;
1091 }
1092
1093 int get_user_list_count(int *p_user_cnt)
1094 {
1095 if (p_user_cnt == NULL)
1096 {
1097 log_error("NULL pointer error\n");
1098 return -1;
1099 }
1100
1101 // acquire lock of user list
1102 if (user_list_rd_lock() < 0)
1103 {
1104 log_error("user_list_rd_lock() error\n");
1105 return -2;
1106 }
1107
1108 *p_user_cnt = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count;
1109
1110 // release lock of user list
1111 if (user_list_rd_unlock() < 0)
1112 {
1113 log_error("user_list_rd_unlock() error\n");
1114 return -2;
1115 }
1116
1117 return 0;
1118 }
1119
1120 int get_user_online_list_count(int *p_user_cnt, int *p_guest_cnt)
1121 {
1122 if (p_user_cnt == NULL || p_guest_cnt == NULL)
1123 {
1124 log_error("NULL pointer error\n");
1125 return -1;
1126 }
1127
1128 // acquire lock of user list
1129 if (user_list_rd_lock() < 0)
1130 {
1131 log_error("user_list_rd_lock() error\n");
1132 return -2;
1133 }
1134
1135 *p_user_cnt = p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].user_count;
1136 *p_guest_cnt = p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].guest_count;
1137
1138 // release lock of user list
1139 if (user_list_rd_unlock() < 0)
1140 {
1141 log_error("user_list_rd_unlock() error\n");
1142 return -2;
1143 }
1144
1145 return 0;
1146 }
1147
1148 int get_user_login_count(int *p_login_cnt)
1149 {
1150 if (p_login_cnt == NULL)
1151 {
1152 log_error("NULL pointer error\n");
1153 return -1;
1154 }
1155
1156 *p_login_cnt = p_user_list_pool->user_login_count;
1157
1158 return 0;
1159 }
1160
1161 int query_user_info(int32_t id, USER_INFO *p_user)
1162 {
1163 int ret = 0;
1164
1165 if (p_user == NULL)
1166 {
1167 log_error("NULL pointer error\n");
1168 return -1;
1169 }
1170
1171 // acquire lock of user list
1172 if (user_list_rd_lock() < 0)
1173 {
1174 log_error("user_list_rd_lock() error\n");
1175 return -2;
1176 }
1177
1178 if (id >= 0 && id < p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count) // Found
1179 {
1180 *p_user = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users[id];
1181 ret = 1;
1182 }
1183
1184 // release lock of user list
1185 if (user_list_rd_unlock() < 0)
1186 {
1187 log_error("user_list_rd_unlock() error\n");
1188 ret = -1;
1189 }
1190
1191 return ret;
1192 }
1193
1194 int query_user_info_by_uid(int32_t uid, USER_INFO *p_user, char *p_intro_buf, size_t intro_buf_len)
1195 {
1196 int left;
1197 int right;
1198 int mid;
1199 int32_t id;
1200 int ret = 0;
1201
1202 if (p_user == NULL)
1203 {
1204 log_error("NULL pointer error\n");
1205 return -1;
1206 }
1207
1208 // acquire lock of user list
1209 if (user_list_rd_lock() < 0)
1210 {
1211 log_error("user_list_rd_lock() error\n");
1212 return -2;
1213 }
1214
1215 left = 0;
1216 right = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count - 1;
1217
1218 while (left < right)
1219 {
1220 mid = (left + right) / 2;
1221 if (uid < p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[mid].uid)
1222 {
1223 right = mid - 1;
1224 }
1225 else if (uid > p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[mid].uid)
1226 {
1227 left = mid + 1;
1228 }
1229 else // if (uid == p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[mid].uid)
1230 {
1231 left = mid;
1232 break;
1233 }
1234 }
1235
1236 if (uid == p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[left].uid) // Found
1237 {
1238 id = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[left].id;
1239 *p_user = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users[id];
1240 ret = 1;
1241
1242 if (p_intro_buf != NULL)
1243 {
1244 strncpy(p_intro_buf, p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users[id].intro, intro_buf_len - 1);
1245 p_intro_buf[intro_buf_len - 1] = '\0';
1246 p_user->intro = p_intro_buf;
1247 }
1248 }
1249
1250 // release lock of user list
1251 if (user_list_rd_unlock() < 0)
1252 {
1253 log_error("user_list_rd_unlock() error\n");
1254 ret = -1;
1255 }
1256
1257 return ret;
1258 }
1259
1260 int query_user_info_by_username(const char *username_prefix, int max_user_cnt,
1261 int32_t uid_list[], char username_list[][BBS_username_max_len + 1])
1262 {
1263 int left;
1264 int right;
1265 int mid;
1266 int left_save;
1267 int ret = 0;
1268 size_t prefix_len;
1269 int comp;
1270 int i;
1271
1272 if (username_prefix == NULL || uid_list == NULL || username_list == NULL)
1273 {
1274 log_error("NULL pointer error\n");
1275 return -1;
1276 }
1277
1278 prefix_len = strlen(username_prefix);
1279
1280 // acquire lock of user list
1281 if (user_list_rd_lock() < 0)
1282 {
1283 log_error("user_list_rd_lock() error\n");
1284 return -2;
1285 }
1286
1287 left = 0;
1288 right = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count - 1;
1289
1290 while (left < right)
1291 {
1292 mid = (left + right) / 2;
1293 comp = strncasecmp(username_prefix, p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users[mid].username, prefix_len);
1294 if (comp < 0)
1295 {
1296 right = mid - 1;
1297 }
1298 else if (comp > 0)
1299 {
1300 left = mid + 1;
1301 }
1302 else // if (comp == 0)
1303 {
1304 left = mid;
1305 break;
1306 }
1307 }
1308
1309 if (strncasecmp(username_prefix, p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users[left].username, prefix_len) == 0) // Found
1310 {
1311 #ifdef _DEBUG
1312 log_error("Debug: match found, pos=%d\n", left);
1313 #endif
1314
1315 left_save = left;
1316 right = left;
1317 left = 0;
1318
1319 while (left < right)
1320 {
1321 mid = (left + right) / 2;
1322 comp = strncasecmp(username_prefix, p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users[mid].username, prefix_len);
1323 if (comp > 0)
1324 {
1325 left = mid + 1;
1326 }
1327 else if (comp == 0)
1328 {
1329 right = mid;
1330 }
1331 else // if (comp < 0)
1332 {
1333 log_error("Bug: left=%d right=%d mid=%d");
1334 ret = -2;
1335 goto cleanup;
1336 }
1337 }
1338
1339 #ifdef _DEBUG
1340 log_error("Debug: first match found, pos=%d\n", right);
1341 #endif
1342
1343 left = left_save;
1344 left_save = right;
1345 right = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count - 1;
1346
1347 while (left < right)
1348 {
1349 mid = (left + right) / 2 + (left + right) % 2;
1350 comp = strncasecmp(username_prefix, p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users[mid].username, prefix_len);
1351 if (comp < 0)
1352 {
1353 right = mid - 1;
1354 }
1355 else if (comp == 0)
1356 {
1357 left = mid;
1358 }
1359 else // if (comp > 0)
1360 {
1361 log_error("Bug: left=%d right=%d mid=%d");
1362 ret = -2;
1363 goto cleanup;
1364 }
1365 }
1366
1367 #ifdef _DEBUG
1368 log_error("Debug: last match found, pos=%d\n", left);
1369 #endif
1370
1371 right = left;
1372 left = left_save;
1373
1374 for (i = 0; i < max_user_cnt && left + i <= right; i++)
1375 {
1376 uid_list[i] = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users[left + i].uid;
1377 strncpy(username_list[i], p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users[left + i].username, sizeof(username_list[i]) - 1);
1378 username_list[i][sizeof(username_list[i]) - 1] = '\0';
1379 }
1380 ret = i;
1381 }
1382
1383 cleanup:
1384 // release lock of user list
1385 if (user_list_rd_unlock() < 0)
1386 {
1387 log_error("user_list_rd_unlock() error\n");
1388 ret = -1;
1389 }
1390
1391 return ret;
1392 }
1393
1394 int query_user_online_info(int32_t id, USER_ONLINE_INFO *p_user)
1395 {
1396 int ret = 0;
1397
1398 if (p_user == NULL)
1399 {
1400 log_error("NULL pointer error\n");
1401 return -1;
1402 }
1403
1404 // acquire lock of user list
1405 if (user_list_rd_lock() < 0)
1406 {
1407 log_error("user_list_rd_lock() error\n");
1408 return -2;
1409 }
1410
1411 if (id >= 0 && id < p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].user_count) // Found
1412 {
1413 *p_user = p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].users[id];
1414 ret = 1;
1415 }
1416
1417 // release lock of user list
1418 if (user_list_rd_unlock() < 0)
1419 {
1420 log_error("user_list_rd_unlock() error\n");
1421 ret = -1;
1422 }
1423
1424 return ret;
1425 }
1426
1427 int query_user_online_info_by_uid(int32_t uid, USER_ONLINE_INFO *p_users, int *p_user_cnt, int start_id)
1428 {
1429 int left;
1430 int right;
1431 int mid;
1432 int32_t id;
1433 int ret = 0;
1434 int i;
1435 int user_cnt;
1436
1437 if (p_users == NULL || p_user_cnt == NULL)
1438 {
1439 log_error("NULL pointer error\n");
1440 return -1;
1441 }
1442
1443 user_cnt = *p_user_cnt;
1444 *p_user_cnt = 0;
1445
1446 // acquire lock of user list
1447 if (user_list_rd_lock() < 0)
1448 {
1449 log_error("user_list_rd_lock() error\n");
1450 return -2;
1451 }
1452
1453 left = start_id;
1454 right = p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].user_count - 1;
1455
1456 while (left < right)
1457 {
1458 mid = (left + right) / 2;
1459 if (uid < p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].index_uid[mid].uid)
1460 {
1461 right = mid - 1;
1462 }
1463 else if (uid > p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].index_uid[mid].uid)
1464 {
1465 left = mid + 1;
1466 }
1467 else // if (uid == p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].index_uid[mid].uid)
1468 {
1469 left = mid;
1470 break;
1471 }
1472 }
1473
1474 if (uid == p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].index_uid[left].uid)
1475 {
1476 right = left;
1477 left = start_id;
1478
1479 while (left < right)
1480 {
1481 mid = (left + right) / 2;
1482 if (uid - 1 < p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].index_uid[mid].uid)
1483 {
1484 right = mid;
1485 }
1486 else // if (uid - 1 >= p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].index_uid[mid].uid)
1487 {
1488 left = mid + 1;
1489 }
1490 }
1491
1492 for (i = 0;
1493 left < p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].user_count && i < user_cnt &&
1494 uid == p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].index_uid[left].uid;
1495 left++, i++)
1496 {
1497 id = p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].index_uid[left].id;
1498 p_users[i] = p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].users[id];
1499 }
1500
1501 if (i > 0)
1502 {
1503 *p_user_cnt = i;
1504 ret = 1;
1505 }
1506 }
1507
1508 // release lock of user list
1509 if (user_list_rd_unlock() < 0)
1510 {
1511 log_error("user_list_rd_unlock() error\n");
1512 ret = -1;
1513 }
1514
1515 return ret;
1516 }
1517
1518 int get_user_id_list(int32_t *p_uid_list, int *p_user_cnt, int start_uid)
1519 {
1520 int left;
1521 int right;
1522 int mid;
1523 int ret = 0;
1524 int i;
1525
1526 if (p_uid_list == NULL || p_user_cnt == NULL)
1527 {
1528 log_error("NULL pointer error\n");
1529 return -1;
1530 }
1531
1532 // acquire lock of user list
1533 if (user_list_rd_lock() < 0)
1534 {
1535 log_error("user_list_rd_lock() error\n");
1536 return -2;
1537 }
1538
1539 left = 0;
1540 right = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count - 1;
1541
1542 while (left < right)
1543 {
1544 mid = (left + right) / 2;
1545 if (start_uid < p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[mid].uid)
1546 {
1547 right = mid - 1;
1548 }
1549 else if (start_uid > p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[mid].uid)
1550 {
1551 left = mid + 1;
1552 }
1553 else // if (start_uid == p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[mid].uid)
1554 {
1555 left = mid;
1556 break;
1557 }
1558 }
1559
1560 for (i = 0; i < *p_user_cnt && left + i < p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count; i++)
1561 {
1562 p_uid_list[i] = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[left + i].uid;
1563 }
1564 *p_user_cnt = i;
1565
1566 // release lock of user list
1567 if (user_list_rd_unlock() < 0)
1568 {
1569 log_error("user_list_rd_unlock() error\n");
1570 ret = -1;
1571 }
1572
1573 return ret;
1574 }
1575
1576 int user_stat_update(void)
1577 {
1578 return user_stat_map_update(&(p_user_list_pool->user_stat_map));
1579 }
1580
1581 int user_article_cnt_inc(int32_t uid, int n)
1582 {
1583 return user_stat_article_cnt_inc(&(p_user_list_pool->user_stat_map), uid, n);
1584 }
1585
1586 int get_user_article_cnt(int32_t uid)
1587 {
1588 const USER_STAT *p_stat;
1589 int ret;
1590
1591 ret = user_stat_get(&(p_user_list_pool->user_stat_map), uid, &p_stat);
1592 if (ret < 0)
1593 {
1594 log_error("user_stat_get(uid=%d) error: %d\n", uid, ret);
1595 return -1;
1596 }
1597 else if (ret == 0) // user not found
1598 {
1599 return -1;
1600 }
1601
1602 return p_stat->article_count;
1603 }

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