/[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.7 - (show annotations)
Wed Oct 22 05:50:13 2025 UTC (4 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.6: +21 -4 lines
Content type: text/x-csrc
Refine handling of Guest

1 /***************************************************************************
2 user_list.c - description
3 -------------------
4 Copyright : (C) 2004-2025 by Leaflet
5 Email : leaflet@leafok.com
6 ***************************************************************************/
7
8 /***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 3 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 #include "common.h"
18 #include "database.h"
19 #include "log.h"
20 #include "trie_dict.h"
21 #include "user_list.h"
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26 #include <sys/ipc.h>
27 #include <sys/mman.h>
28 #include <sys/param.h>
29 #include <sys/sem.h>
30 #include <sys/shm.h>
31
32 #ifdef _SEM_SEMUN_UNDEFINED
33 union semun
34 {
35 int val; /* Value for SETVAL */
36 struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
37 unsigned short *array; /* Array for GETALL, SETALL */
38 struct seminfo *__buf; /* Buffer for IPC_INFO
39 (Linux-specific) */
40 };
41 #endif // #ifdef _SEM_SEMUN_UNDEFINED
42
43 #define USER_LIST_TRY_LOCK_WAIT_TIME 1 // second
44 #define USER_LIST_TRY_LOCK_TIMES 10
45
46 struct user_list_pool_t
47 {
48 int shmid;
49 int semid;
50 USER_LIST user_list[2];
51 USER_LIST *p_current;
52 USER_LIST *p_new;
53 USER_ONLINE_LIST user_online_list[2];
54 USER_ONLINE_LIST *p_online_current;
55 USER_ONLINE_LIST *p_online_new;
56 };
57 typedef struct user_list_pool_t USER_LIST_POOL;
58
59 static USER_LIST_POOL *p_user_list_pool = NULL;
60 static TRIE_NODE *p_trie_action_dict = NULL;
61
62 typedef struct user_action_map_t
63 {
64 char name[BBS_current_action_max_len + 1];
65 char title[BBS_current_action_max_len + 1];
66 } USER_ACTION_MAP;
67
68 const USER_ACTION_MAP user_action_map[] =
69 {
70 {"ARTICLE_FAVOR", "浏览收藏"},
71 {"BBS_NET", "站点穿梭"},
72 {"CHICKEN", "电子小鸡"},
73 {"EDIT_ARTICLE", "修改文章"},
74 {"LOGIN", "进入大厅"},
75 {"MENU", "菜单选择"},
76 {"POST_ARTICLE", "撰写文章"},
77 {"REPLY_ARTICLE", "回复文章"},
78 {"USER_LIST", "查花名册"},
79 {"USER_ONLINE", "环顾四周"},
80 {"VIEW_ARTICLE", "阅读文章"},
81 {"VIEW_FILE", "查看文档"},
82 {"WWW", "Web浏览"}};
83
84 const int user_action_map_size = sizeof(user_action_map) / sizeof(USER_ACTION_MAP);
85
86 static int user_list_try_rd_lock(int semid, int wait_sec);
87 static int user_list_try_rw_lock(int semid, int wait_sec);
88 static int user_list_rd_unlock(int semid);
89 static int user_list_rw_unlock(int semid);
90 static int user_list_rd_lock(int semid);
91 static int user_list_rw_lock(int semid);
92
93 static int user_list_load(MYSQL *db, USER_LIST *p_list);
94 static int user_online_list_load(MYSQL *db, USER_ONLINE_LIST *p_list);
95
96 int user_list_load(MYSQL *db, USER_LIST *p_list)
97 {
98 MYSQL_RES *rs = NULL;
99 MYSQL_ROW row;
100 char sql[SQL_BUFFER_LEN];
101 int ret = 0;
102 int i;
103
104 if (db == NULL || p_list == NULL)
105 {
106 log_error("NULL pointer error\n");
107 return -1;
108 }
109
110 snprintf(sql, sizeof(sql),
111 "SELECT user_list.UID AS UID, username, nickname, gender, gender_pub, life, exp, "
112 "UNIX_TIMESTAMP(signup_dt), UNIX_TIMESTAMP(last_login_dt), UNIX_TIMESTAMP(birthday) "
113 "FROM user_list INNER JOIN user_pubinfo ON user_list.UID = user_pubinfo.UID "
114 "INNER JOIN user_reginfo ON user_list.UID = user_reginfo.UID "
115 "WHERE enable ORDER BY UID");
116
117 if (mysql_query(db, sql) != 0)
118 {
119 log_error("Query user info error: %s\n", mysql_error(db));
120 ret = -1;
121 goto cleanup;
122 }
123
124 if ((rs = mysql_use_result(db)) == NULL)
125 {
126 log_error("Get user info data failed\n");
127 ret = -1;
128 goto cleanup;
129 }
130
131 i = 0;
132 while ((row = mysql_fetch_row(rs)))
133 {
134 p_list->users[i].uid = atoi(row[0]);
135 strncpy(p_list->users[i].username, row[1], sizeof(p_list->users[i].username) - 1);
136 p_list->users[i].username[sizeof(p_list->users[i].username) - 1] = '\0';
137 strncpy(p_list->users[i].nickname, row[2], sizeof(p_list->users[i].nickname) - 1);
138 p_list->users[i].nickname[sizeof(p_list->users[i].nickname) - 1] = '\0';
139 p_list->users[i].gender = row[3][0];
140 p_list->users[i].gender_pub = (int8_t)(row[4] == NULL ? 0 : atoi(row[4]));
141 p_list->users[i].life = (row[5] == NULL ? 0 : atoi(row[5]));
142 p_list->users[i].exp = (row[6] == NULL ? 0 : atoi(row[6]));
143 p_list->users[i].signup_dt = (row[7] == NULL ? 0 : atol(row[7]));
144 p_list->users[i].last_login_dt = (row[8] == NULL ? 0 : atol(row[8]));
145 p_list->users[i].birthday = (row[9] == NULL ? 0 : atol(row[9]));
146
147 i++;
148 if (i >= BBS_max_user_count)
149 {
150 log_error("Too many users, exceed limit %d\n", BBS_max_user_count);
151 break;
152 }
153 }
154 mysql_free_result(rs);
155 rs = NULL;
156
157 p_list->user_count = i;
158
159 #ifdef _DEBUG
160 log_error("Loaded %d users\n", p_list->user_count);
161 #endif
162
163 cleanup:
164 mysql_free_result(rs);
165
166 return ret;
167 }
168
169 int user_online_list_load(MYSQL *db, USER_ONLINE_LIST *p_list)
170 {
171 MYSQL_RES *rs = NULL;
172 MYSQL_ROW row;
173 char sql[SQL_BUFFER_LEN];
174 int ret = 0;
175 int i;
176
177 if (db == NULL || p_list == NULL)
178 {
179 log_error("NULL pointer error\n");
180 return -1;
181 }
182
183 snprintf(sql, sizeof(sql),
184 "SELECT SID, UID, ip, current_action, UNIX_TIMESTAMP(login_tm), "
185 "UNIX_TIMESTAMP(last_tm) FROM user_online "
186 "WHERE last_tm >= SUBDATE(NOW(), INTERVAL %d SECOND) "
187 "ORDER BY last_tm DESC",
188 BBS_user_off_line);
189
190 if (mysql_query(db, sql) != 0)
191 {
192 log_error("Query user online error: %s\n", mysql_error(db));
193 ret = -1;
194 goto cleanup;
195 }
196
197 if ((rs = mysql_use_result(db)) == NULL)
198 {
199 log_error("Get user online data failed\n");
200 ret = -1;
201 goto cleanup;
202 }
203
204 i = 0;
205 while ((row = mysql_fetch_row(rs)))
206 {
207 strncpy(p_list->users[i].session_id, row[0], sizeof(p_list->users[i].session_id) - 1);
208
209 p_list->users[i].session_id[sizeof(p_list->users[i].session_id) - 1] = '\0';
210 if ((ret = query_user_info(atoi(row[1]), &(p_list->users[i].user_info))) < 0)
211 {
212 log_error("query_user_info(%d) error\n", atoi(row[1]));
213 continue;
214 }
215 else if (ret == 0) // Guest
216 {
217 p_list->users[i].user_info.uid = 0;
218 strncpy(p_list->users[i].user_info.username, "guest", sizeof(p_list->users[i].user_info.username) - 1);
219 p_list->users[i].user_info.username[sizeof(p_list->users[i].user_info.username) - 1] = '\0';
220 strncpy(p_list->users[i].user_info.nickname, "Guest", sizeof(p_list->users[i].user_info.nickname) - 1);
221 p_list->users[i].user_info.nickname[sizeof(p_list->users[i].user_info.nickname) - 1] = '\0';
222 p_list->users[i].user_info.gender = 'M';
223 p_list->users[i].user_info.gender_pub = 0;
224 p_list->users[i].user_info.life = 150;
225 p_list->users[i].user_info.exp = 0;
226 p_list->users[i].user_info.signup_dt = 0;
227 p_list->users[i].user_info.last_login_dt = 0;
228 p_list->users[i].user_info.birthday = 0;
229 }
230
231 strncpy(p_list->users[i].ip, row[2], sizeof(p_list->users[i].ip) - 1);
232 p_list->users[i].ip[sizeof(p_list->users[i].ip) - 1] = '\0';
233
234 strncpy(p_list->users[i].current_action, row[3], sizeof(p_list->users[i].current_action) - 1);
235 p_list->users[i].current_action[sizeof(p_list->users[i].current_action) - 1] = '\0';
236 p_list->users[i].current_action_title = NULL;
237 if (p_list->users[i].current_action[0] == '\0')
238 {
239 p_list->users[i].current_action_title = "";
240 }
241 else if (trie_dict_get(p_trie_action_dict, p_list->users[i].current_action, (int64_t *)(&(p_list->users[i].current_action_title))) < 0)
242 {
243 log_error("trie_dict_get(p_trie_action_dict, %s) error on session_id=%s\n",
244 p_list->users[i].current_action, p_list->users[i].session_id);
245 continue;
246 }
247
248 p_list->users[i].login_tm = (row[4] == NULL ? 0 : atol(row[4]));
249 p_list->users[i].last_tm = (row[5] == NULL ? 0 : atol(row[5]));
250
251 i++;
252 if (i >= BBS_max_user_online_count)
253 {
254 log_error("Too many online users, exceed limit %d\n", BBS_max_user_online_count);
255 break;
256 }
257 }
258 mysql_free_result(rs);
259 rs = NULL;
260
261 p_list->user_count = i;
262
263 #ifdef _DEBUG
264 log_error("Loaded %d users\n", p_list->user_count);
265 #endif
266
267 cleanup:
268 mysql_free_result(rs);
269
270 return ret;
271 }
272
273 int user_list_pool_init(void)
274 {
275 int shmid;
276 int semid;
277 int proj_id;
278 key_t key;
279 size_t size;
280 void *p_shm;
281 union semun arg;
282 int i;
283
284 if (p_user_list_pool != NULL || p_trie_action_dict != NULL)
285 {
286 log_error("p_user_list_pool already initialized\n");
287 return -1;
288 }
289
290 p_trie_action_dict = trie_dict_create();
291 if (p_trie_action_dict == NULL)
292 {
293 log_error("trie_dict_create() error\n");
294 return -1;
295 }
296
297 for (i = 0; i < user_action_map_size; i++)
298 {
299 if (trie_dict_set(p_trie_action_dict, user_action_map[i].name, (int64_t)(user_action_map[i].title)) < 0)
300 {
301 log_error("trie_dict_set(p_trie_action_dict, %s) error\n", user_action_map[i].name);
302 }
303 }
304
305 // Allocate shared memory
306 proj_id = (int)(time(NULL) % getpid());
307 key = ftok(VAR_USER_LIST_SHM, proj_id);
308 if (key == -1)
309 {
310 log_error("ftok(%s %d) error (%d)\n", VAR_USER_LIST_SHM, proj_id, errno);
311 return -2;
312 }
313
314 size = sizeof(USER_LIST_POOL);
315 shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
316 if (shmid == -1)
317 {
318 log_error("shmget(size = %d) error (%d)\n", size, errno);
319 return -3;
320 }
321 p_shm = shmat(shmid, NULL, 0);
322 if (p_shm == (void *)-1)
323 {
324 log_error("shmat(shmid=%d) error (%d)\n", shmid, errno);
325 return -3;
326 }
327
328 p_user_list_pool = p_shm;
329 p_user_list_pool->shmid = shmid;
330
331 // Allocate semaphore as user list pool lock
332 size = 2; // r_sem and w_sem
333 semid = semget(key, (int)size, IPC_CREAT | IPC_EXCL | 0600);
334 if (semid == -1)
335 {
336 log_error("semget(user_list_pool_sem, size = %d) error (%d)\n", size, errno);
337 return -3;
338 }
339
340 // Initialize sem value to 0
341 arg.val = 0;
342 for (i = 0; i < size; i++)
343 {
344 if (semctl(semid, i, SETVAL, arg) == -1)
345 {
346 log_error("semctl(user_list_pool_sem, SETVAL) error (%d)\n", errno);
347 return -3;
348 }
349 }
350
351 p_user_list_pool->semid = semid;
352
353 // Set user counts to 0
354 p_user_list_pool->user_list[0].user_count = 0;
355 p_user_list_pool->user_list[1].user_count = 0;
356
357 p_user_list_pool->p_current = &(p_user_list_pool->user_list[0]);
358 p_user_list_pool->p_new = &(p_user_list_pool->user_list[1]);
359
360 p_user_list_pool->p_online_current = &(p_user_list_pool->user_online_list[0]);
361 p_user_list_pool->p_online_new = &(p_user_list_pool->user_online_list[1]);
362
363 return 0;
364 }
365
366 void user_list_pool_cleanup(void)
367 {
368 int shmid;
369
370 if (p_user_list_pool == NULL)
371 {
372 return;
373 }
374
375 shmid = p_user_list_pool->shmid;
376
377 if (semctl(p_user_list_pool->semid, 0, IPC_RMID) == -1)
378 {
379 log_error("semctl(semid = %d, IPC_RMID) error (%d)\n", p_user_list_pool->semid, errno);
380 }
381
382 if (shmdt(p_user_list_pool) == -1)
383 {
384 log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
385 }
386
387 if (shmctl(shmid, IPC_RMID, NULL) == -1)
388 {
389 log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", shmid, errno);
390 }
391
392 p_user_list_pool = NULL;
393
394 if (p_trie_action_dict != NULL)
395 {
396 trie_dict_destroy(p_trie_action_dict);
397
398 p_trie_action_dict = NULL;
399 }
400 }
401
402 int set_user_list_pool_shm_readonly(void)
403 {
404 int shmid;
405 void *p_shm;
406
407 if (p_user_list_pool == NULL)
408 {
409 log_error("p_user_list_pool not initialized\n");
410 return -1;
411 }
412
413 shmid = p_user_list_pool->shmid;
414
415 // Remap shared memory in read-only mode
416 p_shm = shmat(shmid, p_user_list_pool, SHM_RDONLY | SHM_REMAP);
417 if (p_shm == (void *)-1)
418 {
419 log_error("shmat(user_list_pool shmid = %d) error (%d)\n", shmid, errno);
420 return -3;
421 }
422
423 p_user_list_pool = p_shm;
424
425 return 0;
426 }
427
428 int detach_user_list_pool_shm(void)
429 {
430 if (p_user_list_pool != NULL && shmdt(p_user_list_pool) == -1)
431 {
432 log_error("shmdt(user_list_pool) error (%d)\n", errno);
433 return -1;
434 }
435
436 p_user_list_pool = NULL;
437
438 return 0;
439 }
440
441 int user_list_pool_reload(int online_user)
442 {
443 MYSQL *db = NULL;
444 USER_LIST *p_tmp;
445 USER_ONLINE_LIST *p_online_tmp;
446
447 if (p_user_list_pool == NULL)
448 {
449 log_error("p_user_list_pool not initialized\n");
450 return -1;
451 }
452
453 db = db_open();
454 if (db == NULL)
455 {
456 log_error("db_open() error: %s\n", mysql_error(db));
457 return -1;
458 }
459
460 if (online_user)
461 {
462 if (user_online_list_load(db, p_user_list_pool->p_online_new) < 0)
463 {
464 log_error("user_online_list_load() error\n");
465 return -2;
466 }
467 }
468 else
469 {
470 if (user_list_load(db, p_user_list_pool->p_new) < 0)
471 {
472 log_error("user_list_load() error\n");
473 return -2;
474 }
475 }
476
477 mysql_close(db);
478
479 if (user_list_rw_lock(p_user_list_pool->semid) < 0)
480 {
481 log_error("user_list_rw_lock() error\n");
482 return -3;
483 }
484
485 if (online_user)
486 {
487 // Swap p_online_current and p_online_new
488 p_online_tmp = p_user_list_pool->p_online_current;
489 p_user_list_pool->p_online_current = p_user_list_pool->p_online_new;
490 p_user_list_pool->p_online_new = p_online_tmp;
491 }
492 else
493 {
494 // Swap p_current and p_new
495 p_tmp = p_user_list_pool->p_current;
496 p_user_list_pool->p_current = p_user_list_pool->p_new;
497 p_user_list_pool->p_new = p_tmp;
498 }
499
500 if (user_list_rw_unlock(p_user_list_pool->semid) < 0)
501 {
502 log_error("user_list_rw_unlock() error\n");
503 return -3;
504 }
505
506 return 0;
507 }
508
509 int user_list_try_rd_lock(int semid, int wait_sec)
510 {
511 struct sembuf sops[2];
512 struct timespec timeout;
513 int ret;
514
515 sops[0].sem_num = 1; // w_sem
516 sops[0].sem_op = 0; // wait until unlocked
517 sops[0].sem_flg = 0;
518
519 sops[1].sem_num = 0; // r_sem
520 sops[1].sem_op = 1; // lock
521 sops[1].sem_flg = SEM_UNDO; // undo on terminate
522
523 timeout.tv_sec = wait_sec;
524 timeout.tv_nsec = 0;
525
526 ret = semtimedop(semid, sops, 2, &timeout);
527 if (ret == -1 && errno != EAGAIN && errno != EINTR)
528 {
529 log_error("semtimedop(lock read) error %d\n", errno);
530 }
531
532 return ret;
533 }
534
535 int user_list_try_rw_lock(int semid, int wait_sec)
536 {
537 struct sembuf sops[3];
538 struct timespec timeout;
539 int ret;
540
541 sops[0].sem_num = 1; // w_sem
542 sops[0].sem_op = 0; // wait until unlocked
543 sops[0].sem_flg = 0;
544
545 sops[1].sem_num = 1; // w_sem
546 sops[1].sem_op = 1; // lock
547 sops[1].sem_flg = SEM_UNDO; // undo on terminate
548
549 sops[2].sem_num = 0; // r_sem
550 sops[2].sem_op = 0; // wait until unlocked
551 sops[2].sem_flg = 0;
552
553 timeout.tv_sec = wait_sec;
554 timeout.tv_nsec = 0;
555
556 ret = semtimedop(semid, sops, 3, &timeout);
557 if (ret == -1 && errno != EAGAIN && errno != EINTR)
558 {
559 log_error("semtimedop(lock write) error %d\n", errno);
560 }
561
562 return ret;
563 }
564
565 int user_list_rd_unlock(int semid)
566 {
567 struct sembuf sops[2];
568 int ret;
569
570 sops[0].sem_num = 0; // r_sem
571 sops[0].sem_op = -1; // unlock
572 sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
573
574 ret = semop(semid, sops, 1);
575 if (ret == -1 && errno != EAGAIN && errno != EINTR)
576 {
577 log_error("semop(unlock read) error %d\n", errno);
578 }
579
580 return ret;
581 }
582
583 int user_list_rw_unlock(int semid)
584 {
585 struct sembuf sops[1];
586 int ret;
587
588 sops[0].sem_num = 1; // w_sem
589 sops[0].sem_op = -1; // unlock
590 sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
591
592 ret = semop(semid, sops, 1);
593 if (ret == -1 && errno != EAGAIN && errno != EINTR)
594 {
595 log_error("semop(unlock write) error %d\n", errno);
596 }
597
598 return ret;
599 }
600
601 int user_list_rd_lock(int semid)
602 {
603 int timer = 0;
604 int ret = -1;
605
606 while (!SYS_server_exit)
607 {
608 ret = user_list_try_rd_lock(semid, USER_LIST_TRY_LOCK_WAIT_TIME);
609 if (ret == 0) // success
610 {
611 break;
612 }
613 else if (errno == EAGAIN || errno == EINTR) // retry
614 {
615 timer++;
616 if (timer % USER_LIST_TRY_LOCK_TIMES == 0)
617 {
618 log_error("user_list_try_rd_lock() tried %d times\n", timer);
619 }
620 }
621 else // failed
622 {
623 log_error("user_list_try_rd_lock() failed\n");
624 break;
625 }
626 }
627
628 return ret;
629 }
630
631 int user_list_rw_lock(int semid)
632 {
633 int timer = 0;
634 int ret = -1;
635
636 while (!SYS_server_exit)
637 {
638 ret = user_list_try_rw_lock(semid, USER_LIST_TRY_LOCK_WAIT_TIME);
639 if (ret == 0) // success
640 {
641 break;
642 }
643 else if (errno == EAGAIN || errno == EINTR) // retry
644 {
645 timer++;
646 if (timer % USER_LIST_TRY_LOCK_TIMES == 0)
647 {
648 log_error("user_list_try_rw_lock() tried %d times\n", timer);
649 }
650 }
651 else // failed
652 {
653 log_error("user_list_try_rw_lock() failed\n");
654 break;
655 }
656 }
657
658 return ret;
659 }
660
661 int query_user_list(int page_id, USER_INFO *p_users, int *p_user_count, int *p_page_count)
662 {
663 int ret = 0;
664
665 if (p_users == NULL || p_user_count == NULL || p_page_count == NULL)
666 {
667 log_error("NULL pointer error\n");
668 return -1;
669 }
670
671 // acquire lock of user list
672 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
673 {
674 log_error("user_list_rd_lock() error\n");
675 return -2;
676 }
677
678 if (p_user_list_pool->p_current->user_count == 0)
679 {
680 // empty list
681 ret = 0;
682 goto cleanup;
683 }
684
685 *p_page_count = p_user_list_pool->p_current->user_count / BBS_user_limit_per_page +
686 (p_user_list_pool->p_current->user_count % BBS_user_limit_per_page == 0 ? 0 : 1);
687
688 if (page_id < 0 || page_id >= *p_page_count)
689 {
690 log_error("Invalid page_id = %d, not in range [0, %d)\n", page_id, *p_page_count);
691 ret = -3;
692 goto cleanup;
693 }
694
695 *p_user_count = MIN(BBS_user_limit_per_page,
696 p_user_list_pool->p_current->user_count -
697 page_id * BBS_user_limit_per_page);
698
699 memcpy(p_users,
700 p_user_list_pool->p_current->users + page_id * BBS_user_limit_per_page,
701 sizeof(USER_INFO) * (size_t)(*p_user_count));
702
703 cleanup:
704 // release lock of user list
705 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
706 {
707 log_error("user_list_rd_unlock() error\n");
708 ret = -1;
709 }
710
711 return ret;
712 }
713
714 int query_user_online_list(int page_id, USER_ONLINE_INFO *p_online_users, int *p_user_count, int *p_page_count)
715 {
716 int ret = 0;
717
718 if (p_online_users == NULL || p_user_count == NULL || p_page_count == NULL)
719 {
720 log_error("NULL pointer error\n");
721 return -1;
722 }
723
724 // acquire lock of user list
725 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
726 {
727 log_error("user_list_rd_lock() error\n");
728 return -2;
729 }
730
731 if (p_user_list_pool->p_online_current->user_count == 0)
732 {
733 // empty list
734 ret = 0;
735 goto cleanup;
736 }
737
738 *p_page_count = p_user_list_pool->p_online_current->user_count / BBS_user_limit_per_page +
739 (p_user_list_pool->p_online_current->user_count % BBS_user_limit_per_page == 0 ? 0 : 1);
740
741 if (page_id < 0 || page_id >= *p_page_count)
742 {
743 log_error("Invalid page_id = %d, not in range [0, %d)\n", page_id, *p_page_count);
744 ret = -3;
745 goto cleanup;
746 }
747
748 *p_user_count = MIN(BBS_user_limit_per_page,
749 p_user_list_pool->p_online_current->user_count -
750 page_id * BBS_user_limit_per_page);
751
752 memcpy(p_online_users,
753 p_user_list_pool->p_online_current->users + page_id * BBS_user_limit_per_page,
754 sizeof(USER_ONLINE_INFO) * (size_t)(*p_user_count));
755
756 cleanup:
757 // release lock of user list
758 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
759 {
760 log_error("user_list_rd_unlock() error\n");
761 ret = -1;
762 }
763
764 return ret;
765 }
766
767 int query_user_info(int32_t uid, USER_INFO *p_user)
768 {
769 int left;
770 int right;
771 int mid;
772 int ret = 0;
773
774 if (p_user == NULL)
775 {
776 log_error("NULL pointer error\n");
777 return -1;
778 }
779
780 // acquire lock of user list
781 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
782 {
783 log_error("user_list_rd_lock() error\n");
784 return -2;
785 }
786
787 left = 0;
788 right = p_user_list_pool->p_current->user_count - 1;
789
790 while (left < right)
791 {
792 mid = (left + right) / 2;
793 if (uid < p_user_list_pool->p_current->users[mid].uid)
794 {
795 right = mid;
796 }
797 else if (uid > p_user_list_pool->p_current->users[mid].uid)
798 {
799 left = mid + 1;
800 }
801 else // if (uid == p_user_list_pool->p_current->users[mid].uid)
802 {
803 left = mid;
804 break;
805 }
806 }
807
808 if (uid == p_user_list_pool->p_current->users[left].uid) // Found
809 {
810 *p_user = p_user_list_pool->p_current->users[left];
811 ret = 1;
812 }
813
814 // release lock of user list
815 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
816 {
817 log_error("user_list_rd_unlock() error\n");
818 ret = -1;
819 }
820
821 return ret;
822 }

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