/[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.12 - (show annotations)
Wed Oct 22 14:25:15 2025 UTC (4 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.11: +24 -30 lines
Content type: text/x-csrc
Fix bug

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

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