/[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.11 - (show annotations)
Wed Oct 22 11:48:04 2025 UTC (4 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.10: +106 -6 lines
Content type: text/x-csrc
Add query_user_online_info_by_uid()

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 static int user_info_index_uid_comp(const void *ptr1, const void *ptr2)
97 {
98 const USER_INFO_INDEX_UID *p1 = ptr1;
99 const USER_INFO_INDEX_UID *p2 = ptr2;
100
101 if (p1->uid < p2->uid)
102 {
103 return -1;
104 }
105 else if (p1->uid > p2->uid)
106 {
107 return 1;
108 }
109 else if (p1->id < p2->id)
110 {
111 return -1;
112 }
113 else if (p1->id > p2->id)
114 {
115 return 1;
116 }
117 return 0;
118 }
119
120 int user_list_load(MYSQL *db, USER_LIST *p_list)
121 {
122 MYSQL_RES *rs = NULL;
123 MYSQL_ROW row;
124 char sql[SQL_BUFFER_LEN];
125 int ret = 0;
126 int i;
127 int32_t last_uid = -1;
128
129 if (db == NULL || p_list == NULL)
130 {
131 log_error("NULL pointer error\n");
132 return -1;
133 }
134
135 if (p_list->user_count > 0)
136 {
137 last_uid = p_list->users[p_list->user_count - 1].uid;
138 }
139
140 snprintf(sql, sizeof(sql),
141 "SELECT user_list.UID AS UID, username, nickname, gender, gender_pub, life, exp, "
142 "UNIX_TIMESTAMP(signup_dt), UNIX_TIMESTAMP(last_login_dt), UNIX_TIMESTAMP(birthday) "
143 "FROM user_list INNER JOIN user_pubinfo ON user_list.UID = user_pubinfo.UID "
144 "INNER JOIN user_reginfo ON user_list.UID = user_reginfo.UID "
145 "WHERE enable ORDER BY username");
146
147 if (mysql_query(db, sql) != 0)
148 {
149 log_error("Query user info error: %s\n", mysql_error(db));
150 ret = -1;
151 goto cleanup;
152 }
153
154 if ((rs = mysql_use_result(db)) == NULL)
155 {
156 log_error("Get user info data failed\n");
157 ret = -1;
158 goto cleanup;
159 }
160
161 i = 0;
162 while ((row = mysql_fetch_row(rs)))
163 {
164 // record
165 p_list->users[i].id = i;
166 p_list->users[i].uid = atoi(row[0]);
167 strncpy(p_list->users[i].username, row[1], sizeof(p_list->users[i].username) - 1);
168 p_list->users[i].username[sizeof(p_list->users[i].username) - 1] = '\0';
169 strncpy(p_list->users[i].nickname, row[2], sizeof(p_list->users[i].nickname) - 1);
170 p_list->users[i].nickname[sizeof(p_list->users[i].nickname) - 1] = '\0';
171 p_list->users[i].gender = row[3][0];
172 p_list->users[i].gender_pub = (int8_t)(row[4] == NULL ? 0 : atoi(row[4]));
173 p_list->users[i].life = (row[5] == NULL ? 0 : atoi(row[5]));
174 p_list->users[i].exp = (row[6] == NULL ? 0 : atoi(row[6]));
175 p_list->users[i].signup_dt = (row[7] == NULL ? 0 : atol(row[7]));
176 p_list->users[i].last_login_dt = (row[8] == NULL ? 0 : atol(row[8]));
177 p_list->users[i].birthday = (row[9] == NULL ? 0 : atol(row[9]));
178
179 // index
180 p_list->index_uid[i].uid = p_list->users[i].uid;
181 p_list->index_uid[i].id = i;
182
183 i++;
184 if (i >= BBS_max_user_count)
185 {
186 log_error("Too many users, exceed limit %d\n", BBS_max_user_count);
187 break;
188 }
189 }
190 mysql_free_result(rs);
191 rs = NULL;
192
193 // Sort index
194 if (i != p_list->user_count || p_list->users[i - 1].uid != last_uid) // Count of users changed
195 {
196 qsort(p_list->index_uid, (size_t)i, sizeof(USER_INFO_INDEX_UID), user_info_index_uid_comp);
197 #ifdef _DEBUG
198 log_error("Rebuild index of %d users, last_uid=%d\n", i, p_list->users[i - 1].uid);
199 #endif
200 }
201
202 p_list->user_count = i;
203
204 #ifdef _DEBUG
205 log_error("Loaded %d users\n", p_list->user_count);
206 #endif
207
208 cleanup:
209 mysql_free_result(rs);
210
211 return ret;
212 }
213
214 int user_online_list_load(MYSQL *db, USER_ONLINE_LIST *p_list)
215 {
216 MYSQL_RES *rs = NULL;
217 MYSQL_ROW row;
218 char sql[SQL_BUFFER_LEN];
219 int ret = 0;
220 int i;
221
222 if (db == NULL || p_list == NULL)
223 {
224 log_error("NULL pointer error\n");
225 return -1;
226 }
227
228 snprintf(sql, sizeof(sql),
229 "SELECT SID, UID, ip, current_action, UNIX_TIMESTAMP(login_tm), "
230 "UNIX_TIMESTAMP(last_tm) FROM user_online "
231 "WHERE last_tm >= SUBDATE(NOW(), INTERVAL %d SECOND) AND UID <> 0 "
232 "ORDER BY last_tm DESC",
233 BBS_user_off_line);
234
235 if (mysql_query(db, sql) != 0)
236 {
237 log_error("Query user online error: %s\n", mysql_error(db));
238 ret = -1;
239 goto cleanup;
240 }
241
242 if ((rs = mysql_use_result(db)) == NULL)
243 {
244 log_error("Get user online data failed\n");
245 ret = -1;
246 goto cleanup;
247 }
248
249 i = 0;
250 while ((row = mysql_fetch_row(rs)))
251 {
252 p_list->users[i].id = i;
253 strncpy(p_list->users[i].session_id, row[0], sizeof(p_list->users[i].session_id) - 1);
254 p_list->users[i].session_id[sizeof(p_list->users[i].session_id) - 1] = '\0';
255
256 if ((ret = query_user_info_by_uid(atoi(row[1]), &(p_list->users[i].user_info))) < 0)
257 {
258 log_error("query_user_info(%d) error\n", atoi(row[1]));
259 continue;
260 }
261 else if (ret == 0) // skip Guest
262 {
263 continue;
264 }
265
266 strncpy(p_list->users[i].ip, row[2], sizeof(p_list->users[i].ip) - 1);
267 p_list->users[i].ip[sizeof(p_list->users[i].ip) - 1] = '\0';
268
269 strncpy(p_list->users[i].current_action, row[3], sizeof(p_list->users[i].current_action) - 1);
270 p_list->users[i].current_action[sizeof(p_list->users[i].current_action) - 1] = '\0';
271 p_list->users[i].current_action_title = NULL;
272 if (p_list->users[i].current_action[0] == '\0')
273 {
274 p_list->users[i].current_action_title = "";
275 }
276 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)
277 {
278 log_error("trie_dict_get(p_trie_action_dict, %s) error on session_id=%s\n",
279 p_list->users[i].current_action, p_list->users[i].session_id);
280 continue;
281 }
282
283 p_list->users[i].login_tm = (row[4] == NULL ? 0 : atol(row[4]));
284 p_list->users[i].last_tm = (row[5] == NULL ? 0 : atol(row[5]));
285
286 // index
287 p_list->index_uid[i].uid = p_list->users[i].user_info.uid;
288 p_list->index_uid[i].id = i;
289
290 i++;
291 if (i >= BBS_max_user_online_count)
292 {
293 log_error("Too many online users, exceed limit %d\n", BBS_max_user_online_count);
294 break;
295 }
296 }
297 mysql_free_result(rs);
298 rs = NULL;
299
300 // Sort index
301 if (i > 0)
302 {
303 qsort(p_list->index_uid, (size_t)i, sizeof(USER_INFO_INDEX_UID), user_info_index_uid_comp);
304 #ifdef _DEBUG
305 log_error("Rebuild index of %d online users\n", i);
306 #endif
307 }
308
309 p_list->user_count = i;
310
311 #ifdef _DEBUG
312 log_error("Loaded %d online users\n", p_list->user_count);
313 #endif
314
315 cleanup:
316 mysql_free_result(rs);
317
318 return ret;
319 }
320
321 int user_list_pool_init(void)
322 {
323 int shmid;
324 int semid;
325 int proj_id;
326 key_t key;
327 size_t size;
328 void *p_shm;
329 union semun arg;
330 int i;
331
332 if (p_user_list_pool != NULL || p_trie_action_dict != NULL)
333 {
334 log_error("p_user_list_pool already initialized\n");
335 return -1;
336 }
337
338 p_trie_action_dict = trie_dict_create();
339 if (p_trie_action_dict == NULL)
340 {
341 log_error("trie_dict_create() error\n");
342 return -1;
343 }
344
345 for (i = 0; i < user_action_map_size; i++)
346 {
347 if (trie_dict_set(p_trie_action_dict, user_action_map[i].name, (int64_t)(user_action_map[i].title)) < 0)
348 {
349 log_error("trie_dict_set(p_trie_action_dict, %s) error\n", user_action_map[i].name);
350 }
351 }
352
353 // Allocate shared memory
354 proj_id = (int)(time(NULL) % getpid());
355 key = ftok(VAR_USER_LIST_SHM, proj_id);
356 if (key == -1)
357 {
358 log_error("ftok(%s %d) error (%d)\n", VAR_USER_LIST_SHM, proj_id, errno);
359 return -2;
360 }
361
362 size = sizeof(USER_LIST_POOL);
363 shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
364 if (shmid == -1)
365 {
366 log_error("shmget(size = %d) error (%d)\n", size, errno);
367 return -3;
368 }
369 p_shm = shmat(shmid, NULL, 0);
370 if (p_shm == (void *)-1)
371 {
372 log_error("shmat(shmid=%d) error (%d)\n", shmid, errno);
373 return -3;
374 }
375
376 p_user_list_pool = p_shm;
377 p_user_list_pool->shmid = shmid;
378
379 // Allocate semaphore as user list pool lock
380 size = 2; // r_sem and w_sem
381 semid = semget(key, (int)size, IPC_CREAT | IPC_EXCL | 0600);
382 if (semid == -1)
383 {
384 log_error("semget(user_list_pool_sem, size = %d) error (%d)\n", size, errno);
385 return -3;
386 }
387
388 // Initialize sem value to 0
389 arg.val = 0;
390 for (i = 0; i < size; i++)
391 {
392 if (semctl(semid, i, SETVAL, arg) == -1)
393 {
394 log_error("semctl(user_list_pool_sem, SETVAL) error (%d)\n", errno);
395 return -3;
396 }
397 }
398
399 p_user_list_pool->semid = semid;
400
401 // Set user counts to 0
402 p_user_list_pool->user_list[0].user_count = 0;
403 p_user_list_pool->user_list[1].user_count = 0;
404
405 p_user_list_pool->p_current = &(p_user_list_pool->user_list[0]);
406 p_user_list_pool->p_new = &(p_user_list_pool->user_list[1]);
407
408 p_user_list_pool->p_online_current = &(p_user_list_pool->user_online_list[0]);
409 p_user_list_pool->p_online_new = &(p_user_list_pool->user_online_list[1]);
410
411 return 0;
412 }
413
414 void user_list_pool_cleanup(void)
415 {
416 int shmid;
417
418 if (p_user_list_pool == NULL)
419 {
420 return;
421 }
422
423 shmid = p_user_list_pool->shmid;
424
425 if (semctl(p_user_list_pool->semid, 0, IPC_RMID) == -1)
426 {
427 log_error("semctl(semid = %d, IPC_RMID) error (%d)\n", p_user_list_pool->semid, errno);
428 }
429
430 if (shmdt(p_user_list_pool) == -1)
431 {
432 log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
433 }
434
435 if (shmctl(shmid, IPC_RMID, NULL) == -1)
436 {
437 log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", shmid, errno);
438 }
439
440 p_user_list_pool = NULL;
441
442 if (p_trie_action_dict != NULL)
443 {
444 trie_dict_destroy(p_trie_action_dict);
445
446 p_trie_action_dict = NULL;
447 }
448 }
449
450 int set_user_list_pool_shm_readonly(void)
451 {
452 int shmid;
453 void *p_shm;
454
455 if (p_user_list_pool == NULL)
456 {
457 log_error("p_user_list_pool not initialized\n");
458 return -1;
459 }
460
461 shmid = p_user_list_pool->shmid;
462
463 // Remap shared memory in read-only mode
464 p_shm = shmat(shmid, p_user_list_pool, SHM_RDONLY | SHM_REMAP);
465 if (p_shm == (void *)-1)
466 {
467 log_error("shmat(user_list_pool shmid = %d) error (%d)\n", shmid, errno);
468 return -3;
469 }
470
471 p_user_list_pool = p_shm;
472
473 return 0;
474 }
475
476 int detach_user_list_pool_shm(void)
477 {
478 if (p_user_list_pool != NULL && shmdt(p_user_list_pool) == -1)
479 {
480 log_error("shmdt(user_list_pool) error (%d)\n", errno);
481 return -1;
482 }
483
484 p_user_list_pool = NULL;
485
486 return 0;
487 }
488
489 int user_list_pool_reload(int online_user)
490 {
491 MYSQL *db = NULL;
492 USER_LIST *p_tmp;
493 USER_ONLINE_LIST *p_online_tmp;
494 int ret = 0;
495
496 if (p_user_list_pool == NULL)
497 {
498 log_error("p_user_list_pool not initialized\n");
499 return -1;
500 }
501
502 db = db_open();
503 if (db == NULL)
504 {
505 log_error("db_open() error: %s\n", mysql_error(db));
506 return -1;
507 }
508
509 if (online_user)
510 {
511 if (user_online_list_load(db, p_user_list_pool->p_online_new) < 0)
512 {
513 log_error("user_online_list_load() error\n");
514 ret = -2;
515 goto cleanup;
516 }
517
518 // No online user, no need to swap lists
519 if (p_user_list_pool->p_online_current->user_count == 0 && p_user_list_pool->p_online_new->user_count == 0)
520 {
521 goto cleanup;
522 }
523 }
524 else
525 {
526 if (user_list_load(db, p_user_list_pool->p_new) < 0)
527 {
528 log_error("user_list_load() error\n");
529 ret = -2;
530 goto cleanup;
531 }
532 }
533
534 mysql_close(db);
535 db = NULL;
536
537 if (user_list_rw_lock(p_user_list_pool->semid) < 0)
538 {
539 log_error("user_list_rw_lock() error\n");
540 ret = -3;
541 goto cleanup;
542 }
543
544 if (online_user)
545 {
546 // Swap p_online_current and p_online_new
547 p_online_tmp = p_user_list_pool->p_online_current;
548 p_user_list_pool->p_online_current = p_user_list_pool->p_online_new;
549 p_user_list_pool->p_online_new = p_online_tmp;
550 }
551 else
552 {
553 // Swap p_current and p_new
554 p_tmp = p_user_list_pool->p_current;
555 p_user_list_pool->p_current = p_user_list_pool->p_new;
556 p_user_list_pool->p_new = p_tmp;
557 }
558
559 if (user_list_rw_unlock(p_user_list_pool->semid) < 0)
560 {
561 log_error("user_list_rw_unlock() error\n");
562 ret = -3;
563 goto cleanup;
564 }
565
566 cleanup:
567 mysql_close(db);
568
569 return ret;
570 }
571
572 int user_list_try_rd_lock(int semid, int wait_sec)
573 {
574 struct sembuf sops[2];
575 struct timespec timeout;
576 int ret;
577
578 sops[0].sem_num = 1; // w_sem
579 sops[0].sem_op = 0; // wait until unlocked
580 sops[0].sem_flg = 0;
581
582 sops[1].sem_num = 0; // r_sem
583 sops[1].sem_op = 1; // lock
584 sops[1].sem_flg = SEM_UNDO; // undo on terminate
585
586 timeout.tv_sec = wait_sec;
587 timeout.tv_nsec = 0;
588
589 ret = semtimedop(semid, sops, 2, &timeout);
590 if (ret == -1 && errno != EAGAIN && errno != EINTR)
591 {
592 log_error("semtimedop(lock read) error %d\n", errno);
593 }
594
595 return ret;
596 }
597
598 int user_list_try_rw_lock(int semid, int wait_sec)
599 {
600 struct sembuf sops[3];
601 struct timespec timeout;
602 int ret;
603
604 sops[0].sem_num = 1; // w_sem
605 sops[0].sem_op = 0; // wait until unlocked
606 sops[0].sem_flg = 0;
607
608 sops[1].sem_num = 1; // w_sem
609 sops[1].sem_op = 1; // lock
610 sops[1].sem_flg = SEM_UNDO; // undo on terminate
611
612 sops[2].sem_num = 0; // r_sem
613 sops[2].sem_op = 0; // wait until unlocked
614 sops[2].sem_flg = 0;
615
616 timeout.tv_sec = wait_sec;
617 timeout.tv_nsec = 0;
618
619 ret = semtimedop(semid, sops, 3, &timeout);
620 if (ret == -1 && errno != EAGAIN && errno != EINTR)
621 {
622 log_error("semtimedop(lock write) error %d\n", errno);
623 }
624
625 return ret;
626 }
627
628 int user_list_rd_unlock(int semid)
629 {
630 struct sembuf sops[2];
631 int ret;
632
633 sops[0].sem_num = 0; // r_sem
634 sops[0].sem_op = -1; // unlock
635 sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
636
637 ret = semop(semid, sops, 1);
638 if (ret == -1 && errno != EAGAIN && errno != EINTR)
639 {
640 log_error("semop(unlock read) error %d\n", errno);
641 }
642
643 return ret;
644 }
645
646 int user_list_rw_unlock(int semid)
647 {
648 struct sembuf sops[1];
649 int ret;
650
651 sops[0].sem_num = 1; // w_sem
652 sops[0].sem_op = -1; // unlock
653 sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
654
655 ret = semop(semid, sops, 1);
656 if (ret == -1 && errno != EAGAIN && errno != EINTR)
657 {
658 log_error("semop(unlock write) error %d\n", errno);
659 }
660
661 return ret;
662 }
663
664 int user_list_rd_lock(int semid)
665 {
666 int timer = 0;
667 int ret = -1;
668
669 while (!SYS_server_exit)
670 {
671 ret = user_list_try_rd_lock(semid, USER_LIST_TRY_LOCK_WAIT_TIME);
672 if (ret == 0) // success
673 {
674 break;
675 }
676 else if (errno == EAGAIN || errno == EINTR) // retry
677 {
678 timer++;
679 if (timer % USER_LIST_TRY_LOCK_TIMES == 0)
680 {
681 log_error("user_list_try_rd_lock() tried %d times\n", timer);
682 }
683 }
684 else // failed
685 {
686 log_error("user_list_try_rd_lock() failed\n");
687 break;
688 }
689 }
690
691 return ret;
692 }
693
694 int user_list_rw_lock(int semid)
695 {
696 int timer = 0;
697 int ret = -1;
698
699 while (!SYS_server_exit)
700 {
701 ret = user_list_try_rw_lock(semid, USER_LIST_TRY_LOCK_WAIT_TIME);
702 if (ret == 0) // success
703 {
704 break;
705 }
706 else if (errno == EAGAIN || errno == EINTR) // retry
707 {
708 timer++;
709 if (timer % USER_LIST_TRY_LOCK_TIMES == 0)
710 {
711 log_error("user_list_try_rw_lock() tried %d times\n", timer);
712 }
713 }
714 else // failed
715 {
716 log_error("user_list_try_rw_lock() failed\n");
717 break;
718 }
719 }
720
721 return ret;
722 }
723
724 int query_user_list(int page_id, USER_INFO *p_users, int *p_user_count, int *p_page_count)
725 {
726 int ret = 0;
727
728 if (p_users == NULL || p_user_count == NULL || p_page_count == NULL)
729 {
730 log_error("NULL pointer error\n");
731 return -1;
732 }
733
734 // acquire lock of user list
735 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
736 {
737 log_error("user_list_rd_lock() error\n");
738 return -2;
739 }
740
741 if (p_user_list_pool->p_current->user_count == 0)
742 {
743 // empty list
744 ret = 0;
745 goto cleanup;
746 }
747
748 *p_page_count = p_user_list_pool->p_current->user_count / BBS_user_limit_per_page +
749 (p_user_list_pool->p_current->user_count % BBS_user_limit_per_page == 0 ? 0 : 1);
750
751 if (page_id < 0 || page_id >= *p_page_count)
752 {
753 log_error("Invalid page_id = %d, not in range [0, %d)\n", page_id, *p_page_count);
754 ret = -3;
755 goto cleanup;
756 }
757
758 *p_user_count = MIN(BBS_user_limit_per_page,
759 p_user_list_pool->p_current->user_count -
760 page_id * BBS_user_limit_per_page);
761
762 memcpy(p_users,
763 p_user_list_pool->p_current->users + page_id * BBS_user_limit_per_page,
764 sizeof(USER_INFO) * (size_t)(*p_user_count));
765
766 cleanup:
767 // release lock of user list
768 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
769 {
770 log_error("user_list_rd_unlock() error\n");
771 ret = -1;
772 }
773
774 return ret;
775 }
776
777 int query_user_online_list(int page_id, USER_ONLINE_INFO *p_online_users, int *p_user_count, int *p_page_count)
778 {
779 int ret = 0;
780
781 if (p_online_users == NULL || p_user_count == NULL || p_page_count == NULL)
782 {
783 log_error("NULL pointer error\n");
784 return -1;
785 }
786
787 // acquire lock of user list
788 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
789 {
790 log_error("user_list_rd_lock() error\n");
791 return -2;
792 }
793
794 if (p_user_list_pool->p_online_current->user_count == 0)
795 {
796 // empty list
797 ret = 0;
798 goto cleanup;
799 }
800
801 *p_page_count = p_user_list_pool->p_online_current->user_count / BBS_user_limit_per_page +
802 (p_user_list_pool->p_online_current->user_count % BBS_user_limit_per_page == 0 ? 0 : 1);
803
804 if (page_id < 0 || page_id >= *p_page_count)
805 {
806 log_error("Invalid page_id = %d, not in range [0, %d)\n", page_id, *p_page_count);
807 ret = -3;
808 goto cleanup;
809 }
810
811 *p_user_count = MIN(BBS_user_limit_per_page,
812 p_user_list_pool->p_online_current->user_count -
813 page_id * BBS_user_limit_per_page);
814
815 memcpy(p_online_users,
816 p_user_list_pool->p_online_current->users + page_id * BBS_user_limit_per_page,
817 sizeof(USER_ONLINE_INFO) * (size_t)(*p_user_count));
818
819 cleanup:
820 // release lock of user list
821 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
822 {
823 log_error("user_list_rd_unlock() error\n");
824 ret = -1;
825 }
826
827 return ret;
828 }
829
830 int query_user_info(int32_t id, USER_INFO *p_user)
831 {
832 int ret = 0;
833
834 if (p_user == NULL)
835 {
836 log_error("NULL pointer error\n");
837 return -1;
838 }
839
840 // acquire lock of user list
841 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
842 {
843 log_error("user_list_rd_lock() error\n");
844 return -2;
845 }
846
847 if (id >= 0 && id < p_user_list_pool->p_current->user_count) // Found
848 {
849 *p_user = p_user_list_pool->p_current->users[id];
850 ret = 1;
851 }
852
853 // release lock of user list
854 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
855 {
856 log_error("user_list_rd_unlock() error\n");
857 ret = -1;
858 }
859
860 return ret;
861 }
862
863 int query_user_info_by_uid(int32_t uid, USER_INFO *p_user)
864 {
865 int left;
866 int right;
867 int mid;
868 int32_t id;
869 int ret = 0;
870
871 if (p_user == NULL)
872 {
873 log_error("NULL pointer error\n");
874 return -1;
875 }
876
877 // acquire lock of user list
878 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
879 {
880 log_error("user_list_rd_lock() error\n");
881 return -2;
882 }
883
884 left = 0;
885 right = p_user_list_pool->p_current->user_count - 1;
886
887 while (left < right)
888 {
889 mid = (left + right) / 2;
890 if (uid < p_user_list_pool->p_current->index_uid[mid].uid)
891 {
892 right = mid;
893 }
894 else if (uid > p_user_list_pool->p_current->index_uid[mid].uid)
895 {
896 left = mid + 1;
897 }
898 else // if (uid == p_user_list_pool->p_current->index_uid[mid].uid)
899 {
900 left = mid;
901 break;
902 }
903 }
904
905 if (uid == p_user_list_pool->p_current->index_uid[left].uid) // Found
906 {
907 id = p_user_list_pool->p_current->index_uid[left].id;
908 if ((ret = query_user_info(id, p_user)) <= 0)
909 {
910 log_error("query_user_info(id=%d) error: %d\n", id, ret);
911 }
912 else
913 {
914 ret = 1;
915 }
916 }
917
918 // release lock of user list
919 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
920 {
921 log_error("user_list_rd_unlock() error\n");
922 ret = -1;
923 }
924
925 return ret;
926 }
927
928 int query_user_online_info(int32_t id, USER_ONLINE_INFO *p_user)
929 {
930 int ret = 0;
931
932 if (p_user == NULL)
933 {
934 log_error("NULL pointer error\n");
935 return -1;
936 }
937
938 // acquire lock of user list
939 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
940 {
941 log_error("user_list_rd_lock() error\n");
942 return -2;
943 }
944
945 if (id >= 0 && id < p_user_list_pool->p_online_current->user_count) // Found
946 {
947 *p_user = p_user_list_pool->p_online_current->users[id];
948 ret = 1;
949 }
950
951 // release lock of user list
952 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
953 {
954 log_error("user_list_rd_unlock() error\n");
955 ret = -1;
956 }
957
958 return ret;
959 }
960
961 int query_user_online_info_by_uid(int32_t uid, USER_ONLINE_INFO *p_users, int *p_user_cnt, int start_id)
962 {
963 int left;
964 int right;
965 int mid;
966 int32_t id;
967 int ret = 0;
968 int i;
969
970 if (p_users == NULL || p_user_cnt == NULL)
971 {
972 log_error("NULL pointer error\n");
973 return -1;
974 }
975
976 // acquire lock of user list
977 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
978 {
979 log_error("user_list_rd_lock() error\n");
980 return -2;
981 }
982
983 left = start_id;
984 right = p_user_list_pool->p_online_current->user_count - 1;
985
986 while (left < right)
987 {
988 mid = (left + right) / 2;
989 if (uid < p_user_list_pool->p_online_current->index_uid[mid].uid)
990 {
991 right = mid;
992 }
993 else if (uid > p_user_list_pool->p_online_current->index_uid[mid].uid)
994 {
995 left = mid + 1;
996 }
997 else // if (uid == p_user_list_pool->p_online_current->index_uid[mid].uid)
998 {
999 left = mid;
1000 break;
1001 }
1002 }
1003
1004 if (uid == p_user_list_pool->p_online_current->index_uid[left].uid)
1005 {
1006 right = left;
1007 left = start_id;
1008
1009 while (left < right)
1010 {
1011 mid = (left + right) / 2;
1012 if (uid - 1 < p_user_list_pool->p_online_current->index_uid[mid].uid)
1013 {
1014 right = mid;
1015 }
1016 else // if (uid - 1 >= p_user_list_pool->p_online_current->index_uid[mid].uid)
1017 {
1018 left = mid + 1;
1019 }
1020 }
1021
1022 for (id = left, i = 0;
1023 id < p_user_list_pool->p_online_current->user_count && i < *p_user_cnt &&
1024 uid == p_user_list_pool->p_online_current->index_uid[id].uid;
1025 id++, i++)
1026 {
1027 if ((ret = query_user_online_info(id, &(p_users[i]))) <= 0)
1028 {
1029 log_error("query_user_info(id=%d) error: %d\n", id, ret);
1030 ret = -3;
1031 break;
1032 }
1033 }
1034
1035 if (ret > 0 && i > 0)
1036 {
1037 *p_user_cnt = i;
1038 ret = 1;
1039 }
1040 }
1041
1042 // release lock of user list
1043 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
1044 {
1045 log_error("user_list_rd_unlock() error\n");
1046 ret = -1;
1047 }
1048
1049 return ret;
1050 }

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