/[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.48 - (show annotations)
Sat Jan 3 10:27:14 2026 UTC (2 months, 1 week ago) by sysadm
Branch: MAIN
CVS Tags: HEAD
Changes since 1.47: +1 -1 lines
Content type: text/x-csrc
Update copyright info

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

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