/[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.15 - (show annotations)
Thu Oct 23 00:48:34 2025 UTC (4 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.14: +25 -16 lines
Content type: text/x-csrc
Optimize index rebuild

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

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