/[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.14 - (show annotations)
Wed Oct 22 16:12:50 2025 UTC (4 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.13: +21 -6 lines
Content type: text/x-csrc
Add data fields of user

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

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