/[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.45 - (show annotations)
Wed Dec 3 05:31:11 2025 UTC (3 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.44: +1 -0 lines
Content type: text/x-csrc
Refresh user online status during list item selection.

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

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