/[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.23 - (show annotations)
Sat Nov 1 10:35:42 2025 UTC (4 months, 2 weeks ago) by sysadm
Branch: MAIN
Changes since 1.22: +77 -7 lines
Content type: text/x-csrc
Get online user/guest count from user_list instead of database

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 "user_stat.h"
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27 #include <sys/ipc.h>
28 #include <sys/mman.h>
29 #include <sys/param.h>
30 #include <sys/sem.h>
31 #include <sys/shm.h>
32
33 #ifdef _SEM_SEMUN_UNDEFINED
34 union semun
35 {
36 int val; /* Value for SETVAL */
37 struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
38 unsigned short *array; /* Array for GETALL, SETALL */
39 struct seminfo *__buf; /* Buffer for IPC_INFO
40 (Linux-specific) */
41 };
42 #endif // #ifdef _SEM_SEMUN_UNDEFINED
43
44 #define USER_LIST_TRY_LOCK_WAIT_TIME 1 // second
45 #define USER_LIST_TRY_LOCK_TIMES 10
46
47 struct user_list_pool_t
48 {
49 int shmid;
50 int semid;
51 USER_LIST user_list[2];
52 USER_LIST *p_current;
53 USER_LIST *p_new;
54 USER_ONLINE_LIST user_online_list[2];
55 USER_ONLINE_LIST *p_online_current;
56 USER_ONLINE_LIST *p_online_new;
57 USER_STAT_MAP user_stat_map;
58 };
59 typedef struct user_list_pool_t USER_LIST_POOL;
60
61 static USER_LIST_POOL *p_user_list_pool = NULL;
62 static TRIE_NODE *p_trie_action_dict = NULL;
63
64 typedef struct user_action_map_t
65 {
66 char name[BBS_current_action_max_len + 1];
67 char title[BBS_current_action_max_len + 1];
68 } USER_ACTION_MAP;
69
70 const USER_ACTION_MAP user_action_map[] =
71 {
72 {"ARTICLE_FAVOR", "浏览收藏"},
73 {"BBS_NET", "站点穿梭"},
74 {"CHICKEN", "电子小鸡"},
75 {"EDIT_ARTICLE", "修改文章"},
76 {"LOGIN", "进入大厅"},
77 {"MENU", "菜单选择"},
78 {"POST_ARTICLE", "撰写文章"},
79 {"REPLY_ARTICLE", "回复文章"},
80 {"USER_LIST", "查花名册"},
81 {"USER_ONLINE", "环顾四周"},
82 {"VIEW_ARTICLE", "阅读文章"},
83 {"VIEW_FILE", "查看文档"},
84 {"WWW", "Web浏览"}};
85
86 const int user_action_map_size = sizeof(user_action_map) / sizeof(USER_ACTION_MAP);
87
88 static int user_list_try_rd_lock(int semid, int wait_sec);
89 static int user_list_try_rw_lock(int semid, int wait_sec);
90 static int user_list_rd_unlock(int semid);
91 static int user_list_rw_unlock(int semid);
92 static int user_list_rd_lock(int semid);
93 static int user_list_rw_lock(int semid);
94
95 static int user_list_load(MYSQL *db, USER_LIST *p_list);
96 static int user_online_list_load(MYSQL *db, USER_ONLINE_LIST *p_list);
97
98 static int user_info_index_uid_comp(const void *ptr1, const void *ptr2)
99 {
100 const USER_INFO_INDEX_UID *p1 = ptr1;
101 const USER_INFO_INDEX_UID *p2 = ptr2;
102
103 if (p1->uid < p2->uid)
104 {
105 return -1;
106 }
107 else if (p1->uid > p2->uid)
108 {
109 return 1;
110 }
111 else if (p1->id < p2->id)
112 {
113 return -1;
114 }
115 else if (p1->id > p2->id)
116 {
117 return 1;
118 }
119 return 0;
120 }
121
122 int user_list_load(MYSQL *db, USER_LIST *p_list)
123 {
124 MYSQL_RES *rs = NULL;
125 MYSQL_ROW row;
126 char sql[SQL_BUFFER_LEN];
127 int ret = 0;
128 int i;
129 int j;
130 int32_t last_uid;
131 size_t intro_buf_offset;
132 size_t intro_len;
133
134 if (db == NULL || p_list == NULL)
135 {
136 log_error("NULL pointer error\n");
137 return -1;
138 }
139
140 if (p_list->user_count > 0)
141 {
142 last_uid = p_list->users[p_list->user_count - 1].uid;
143 }
144 else
145 {
146 last_uid = -1;
147 }
148
149 snprintf(sql, sizeof(sql),
150 "SELECT user_list.UID AS UID, username, nickname, gender, gender_pub, life, exp, visit_count, "
151 "UNIX_TIMESTAMP(signup_dt), UNIX_TIMESTAMP(last_login_dt), UNIX_TIMESTAMP(last_logout_dt), "
152 "UNIX_TIMESTAMP(birthday), `introduction` "
153 "FROM user_list INNER JOIN user_pubinfo ON user_list.UID = user_pubinfo.UID "
154 "INNER JOIN user_reginfo ON user_list.UID = user_reginfo.UID "
155 "WHERE enable ORDER BY username");
156
157 if (mysql_query(db, sql) != 0)
158 {
159 log_error("Query user info error: %s\n", mysql_error(db));
160 ret = -1;
161 goto cleanup;
162 }
163
164 if ((rs = mysql_use_result(db)) == NULL)
165 {
166 log_error("Get user info data failed\n");
167 ret = -1;
168 goto cleanup;
169 }
170
171 intro_buf_offset = 0;
172 i = 0;
173 while ((row = mysql_fetch_row(rs)))
174 {
175 // record
176 p_list->users[i].id = i;
177 p_list->users[i].uid = atoi(row[0]);
178 strncpy(p_list->users[i].username, row[1], sizeof(p_list->users[i].username) - 1);
179 p_list->users[i].username[sizeof(p_list->users[i].username) - 1] = '\0';
180 strncpy(p_list->users[i].nickname, row[2], sizeof(p_list->users[i].nickname) - 1);
181 p_list->users[i].nickname[sizeof(p_list->users[i].nickname) - 1] = '\0';
182 p_list->users[i].gender = row[3][0];
183 p_list->users[i].gender_pub = (int8_t)(row[4] == NULL ? 0 : atoi(row[4]));
184 p_list->users[i].life = (row[5] == NULL ? 0 : atoi(row[5]));
185 p_list->users[i].exp = (row[6] == NULL ? 0 : atoi(row[6]));
186 p_list->users[i].visit_count = (row[7] == NULL ? 0 : atoi(row[7]));
187 p_list->users[i].signup_dt = (row[8] == NULL ? 0 : atol(row[8]));
188 p_list->users[i].last_login_dt = (row[9] == NULL ? 0 : atol(row[9]));
189 p_list->users[i].last_logout_dt = (row[10] == NULL ? 0 : atol(row[10]));
190 p_list->users[i].birthday = (row[10] == NULL ? 0 : atol(row[11]));
191 intro_len = strlen((row[12] == NULL ? "" : row[12]));
192 if (intro_len >= sizeof(p_list->user_intro_buf) - 1 - intro_buf_offset)
193 {
194 log_error("OOM for user introduction: len=%d, i=%d\n", intro_len, i);
195 break;
196 }
197 memcpy(p_list->user_intro_buf + intro_buf_offset,
198 (row[12] == NULL ? "" : row[12]),
199 intro_len + 1);
200 p_list->users[i].intro = p_list->user_intro_buf + intro_buf_offset;
201 intro_buf_offset += (intro_len + 1);
202
203 i++;
204 if (i >= BBS_max_user_count)
205 {
206 log_error("Too many users, exceed limit %d\n", BBS_max_user_count);
207 break;
208 }
209 }
210 mysql_free_result(rs);
211 rs = NULL;
212
213 if (i != p_list->user_count || p_list->users[i - 1].uid != last_uid) // Count of users changed
214 {
215 // Rebuild index
216 for (j = 0; j < i; j++)
217 {
218 p_list->index_uid[j].uid = p_list->users[j].uid;
219 p_list->index_uid[j].id = j;
220 }
221
222 qsort(p_list->index_uid, (size_t)i, sizeof(USER_INFO_INDEX_UID), user_info_index_uid_comp);
223
224 #ifdef _DEBUG
225 log_error("Rebuild index of %d users, last_uid=%d\n", i, p_list->users[i - 1].uid);
226 #endif
227 }
228
229 p_list->user_count = i;
230
231 #ifdef _DEBUG
232 log_error("Loaded %d users\n", p_list->user_count);
233 #endif
234
235 cleanup:
236 mysql_free_result(rs);
237
238 return ret;
239 }
240
241 int user_online_list_load(MYSQL *db, USER_ONLINE_LIST *p_list)
242 {
243 MYSQL_RES *rs = NULL;
244 MYSQL_ROW row;
245 char sql[SQL_BUFFER_LEN];
246 int ret = 0;
247 int i;
248 int j;
249 int user_cnt;
250 int guest_cnt;
251
252 if (db == NULL || p_list == NULL)
253 {
254 log_error("NULL pointer error\n");
255 return -1;
256 }
257
258 snprintf(sql, sizeof(sql),
259 "SELECT SID, UID, ip, current_action, UNIX_TIMESTAMP(login_tm), "
260 "UNIX_TIMESTAMP(last_tm) FROM user_online "
261 "WHERE last_tm >= SUBDATE(NOW(), INTERVAL %d SECOND) "
262 "ORDER BY last_tm DESC",
263 BBS_user_off_line);
264
265 if (mysql_query(db, sql) != 0)
266 {
267 log_error("Query user online error: %s\n", mysql_error(db));
268 ret = -1;
269 goto cleanup;
270 }
271
272 if ((rs = mysql_use_result(db)) == NULL)
273 {
274 log_error("Get user online data failed\n");
275 ret = -1;
276 goto cleanup;
277 }
278
279 i = 0;
280 user_cnt = 0;
281 guest_cnt = 0;
282 while ((row = mysql_fetch_row(rs)))
283 {
284 if (atoi(row[1]) == 0) // guest
285 {
286 guest_cnt++;
287 continue;
288 }
289 else
290 {
291 user_cnt++;
292 }
293
294 p_list->users[i].id = i;
295 strncpy(p_list->users[i].session_id, row[0], sizeof(p_list->users[i].session_id) - 1);
296 p_list->users[i].session_id[sizeof(p_list->users[i].session_id) - 1] = '\0';
297
298 if ((ret = query_user_info_by_uid(atoi(row[1]), &(p_list->users[i].user_info))) <= 0)
299 {
300 log_error("query_user_info_by_uid(%d) error\n", atoi(row[1]));
301 continue;
302 }
303
304 strncpy(p_list->users[i].ip, row[2], sizeof(p_list->users[i].ip) - 1);
305 p_list->users[i].ip[sizeof(p_list->users[i].ip) - 1] = '\0';
306
307 strncpy(p_list->users[i].current_action, row[3], sizeof(p_list->users[i].current_action) - 1);
308 p_list->users[i].current_action[sizeof(p_list->users[i].current_action) - 1] = '\0';
309 p_list->users[i].current_action_title = NULL;
310 if (p_list->users[i].current_action[0] == '\0')
311 {
312 p_list->users[i].current_action_title = "";
313 }
314 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)
315 {
316 log_error("trie_dict_get(p_trie_action_dict, %s) error on session_id=%s\n",
317 p_list->users[i].current_action, p_list->users[i].session_id);
318 continue;
319 }
320
321 p_list->users[i].login_tm = (row[4] == NULL ? 0 : atol(row[4]));
322 p_list->users[i].last_tm = (row[5] == NULL ? 0 : atol(row[5]));
323
324 i++;
325 if (i >= BBS_max_user_online_count)
326 {
327 log_error("Too many online users, exceed limit %d\n", BBS_max_user_online_count);
328 break;
329 }
330 }
331 mysql_free_result(rs);
332 rs = NULL;
333
334 if (user_cnt > 0)
335 {
336 // Rebuild index
337 for (j = 0; j < user_cnt; j++)
338 {
339 p_list->index_uid[j].uid = p_list->users[j].user_info.uid;
340 p_list->index_uid[j].id = j;
341 }
342
343 qsort(p_list->index_uid, (size_t)user_cnt, sizeof(USER_INFO_INDEX_UID), user_info_index_uid_comp);
344
345 #ifdef _DEBUG
346 log_error("Rebuild index of %d online users\n", user_cnt);
347 #endif
348 }
349
350 p_list->user_count = user_cnt;
351 p_list->guest_count = guest_cnt;
352
353 #ifdef _DEBUG
354 log_error("Loaded %d online users and %d guest users\n", p_list->user_count, p_list->guest_count);
355 #endif
356
357 cleanup:
358 mysql_free_result(rs);
359
360 return ret;
361 }
362
363 int user_list_pool_init(const char *filename)
364 {
365 int shmid;
366 int semid;
367 int proj_id;
368 key_t key;
369 size_t size;
370 void *p_shm;
371 union semun arg;
372 int i;
373
374 if (p_user_list_pool != NULL || p_trie_action_dict != NULL)
375 {
376 log_error("p_user_list_pool already initialized\n");
377 return -1;
378 }
379
380 p_trie_action_dict = trie_dict_create();
381 if (p_trie_action_dict == NULL)
382 {
383 log_error("trie_dict_create() error\n");
384 return -1;
385 }
386
387 for (i = 0; i < user_action_map_size; i++)
388 {
389 if (trie_dict_set(p_trie_action_dict, user_action_map[i].name, (int64_t)(user_action_map[i].title)) < 0)
390 {
391 log_error("trie_dict_set(p_trie_action_dict, %s) error\n", user_action_map[i].name);
392 }
393 }
394
395 // Allocate shared memory
396 proj_id = (int)(time(NULL) % getpid());
397 key = ftok(filename, proj_id);
398 if (key == -1)
399 {
400 log_error("ftok(%s %d) error (%d)\n", filename, proj_id, errno);
401 return -2;
402 }
403
404 size = sizeof(USER_LIST_POOL);
405 shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
406 if (shmid == -1)
407 {
408 log_error("shmget(size = %d) error (%d)\n", size, errno);
409 return -3;
410 }
411 p_shm = shmat(shmid, NULL, 0);
412 if (p_shm == (void *)-1)
413 {
414 log_error("shmat(shmid=%d) error (%d)\n", shmid, errno);
415 return -3;
416 }
417
418 p_user_list_pool = p_shm;
419 p_user_list_pool->shmid = shmid;
420
421 // Allocate semaphore as user list pool lock
422 size = 2; // r_sem and w_sem
423 semid = semget(key, (int)size, IPC_CREAT | IPC_EXCL | 0600);
424 if (semid == -1)
425 {
426 log_error("semget(user_list_pool_sem, size = %d) error (%d)\n", size, errno);
427 return -3;
428 }
429
430 // Initialize sem value to 0
431 arg.val = 0;
432 for (i = 0; i < size; i++)
433 {
434 if (semctl(semid, i, SETVAL, arg) == -1)
435 {
436 log_error("semctl(user_list_pool_sem, SETVAL) error (%d)\n", errno);
437 return -3;
438 }
439 }
440
441 p_user_list_pool->semid = semid;
442
443 // Set user counts to 0
444 p_user_list_pool->user_list[0].user_count = 0;
445 p_user_list_pool->user_list[1].user_count = 0;
446
447 p_user_list_pool->p_current = &(p_user_list_pool->user_list[0]);
448 p_user_list_pool->p_new = &(p_user_list_pool->user_list[1]);
449
450 p_user_list_pool->p_online_current = &(p_user_list_pool->user_online_list[0]);
451 p_user_list_pool->p_online_new = &(p_user_list_pool->user_online_list[1]);
452
453 user_stat_map_init(&(p_user_list_pool->user_stat_map));
454
455 return 0;
456 }
457
458 void user_list_pool_cleanup(void)
459 {
460 int shmid;
461
462 if (p_user_list_pool == NULL)
463 {
464 return;
465 }
466
467 shmid = p_user_list_pool->shmid;
468
469 if (semctl(p_user_list_pool->semid, 0, IPC_RMID) == -1)
470 {
471 log_error("semctl(semid = %d, IPC_RMID) error (%d)\n", p_user_list_pool->semid, errno);
472 }
473
474 if (shmdt(p_user_list_pool) == -1)
475 {
476 log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
477 }
478
479 if (shmctl(shmid, IPC_RMID, NULL) == -1)
480 {
481 log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", shmid, errno);
482 }
483
484 p_user_list_pool = NULL;
485
486 if (p_trie_action_dict != NULL)
487 {
488 trie_dict_destroy(p_trie_action_dict);
489
490 p_trie_action_dict = NULL;
491 }
492 }
493
494 int set_user_list_pool_shm_readonly(void)
495 {
496 int shmid;
497 void *p_shm;
498
499 if (p_user_list_pool == NULL)
500 {
501 log_error("p_user_list_pool not initialized\n");
502 return -1;
503 }
504
505 shmid = p_user_list_pool->shmid;
506
507 // Remap shared memory in read-only mode
508 p_shm = shmat(shmid, p_user_list_pool, SHM_RDONLY | SHM_REMAP);
509 if (p_shm == (void *)-1)
510 {
511 log_error("shmat(user_list_pool shmid = %d) error (%d)\n", shmid, errno);
512 return -3;
513 }
514
515 p_user_list_pool = p_shm;
516
517 return 0;
518 }
519
520 int detach_user_list_pool_shm(void)
521 {
522 if (p_user_list_pool != NULL && shmdt(p_user_list_pool) == -1)
523 {
524 log_error("shmdt(user_list_pool) error (%d)\n", errno);
525 return -1;
526 }
527
528 p_user_list_pool = NULL;
529
530 return 0;
531 }
532
533 int user_list_pool_reload(int online_user)
534 {
535 MYSQL *db = NULL;
536 USER_LIST *p_tmp;
537 USER_ONLINE_LIST *p_online_tmp;
538 int ret = 0;
539
540 if (p_user_list_pool == NULL)
541 {
542 log_error("p_user_list_pool not initialized\n");
543 return -1;
544 }
545
546 db = db_open();
547 if (db == NULL)
548 {
549 log_error("db_open() error: %s\n", mysql_error(db));
550 return -1;
551 }
552
553 if (online_user)
554 {
555 if (user_online_list_load(db, p_user_list_pool->p_online_new) < 0)
556 {
557 log_error("user_online_list_load() error\n");
558 ret = -2;
559 goto cleanup;
560 }
561 }
562 else
563 {
564 if (user_list_load(db, p_user_list_pool->p_new) < 0)
565 {
566 log_error("user_list_load() error\n");
567 ret = -2;
568 goto cleanup;
569 }
570 }
571
572 mysql_close(db);
573 db = NULL;
574
575 if (user_list_rw_lock(p_user_list_pool->semid) < 0)
576 {
577 log_error("user_list_rw_lock() error\n");
578 ret = -3;
579 goto cleanup;
580 }
581
582 if (online_user)
583 {
584 // Swap p_online_current and p_online_new
585 p_online_tmp = p_user_list_pool->p_online_current;
586 p_user_list_pool->p_online_current = p_user_list_pool->p_online_new;
587 p_user_list_pool->p_online_new = p_online_tmp;
588 }
589 else
590 {
591 // Swap p_current and p_new
592 p_tmp = p_user_list_pool->p_current;
593 p_user_list_pool->p_current = p_user_list_pool->p_new;
594 p_user_list_pool->p_new = p_tmp;
595 }
596
597 if (user_list_rw_unlock(p_user_list_pool->semid) < 0)
598 {
599 log_error("user_list_rw_unlock() error\n");
600 ret = -3;
601 goto cleanup;
602 }
603
604 cleanup:
605 mysql_close(db);
606
607 return ret;
608 }
609
610 int user_list_try_rd_lock(int semid, int wait_sec)
611 {
612 struct sembuf sops[2];
613 struct timespec timeout;
614 int ret;
615
616 sops[0].sem_num = 1; // w_sem
617 sops[0].sem_op = 0; // wait until unlocked
618 sops[0].sem_flg = 0;
619
620 sops[1].sem_num = 0; // r_sem
621 sops[1].sem_op = 1; // lock
622 sops[1].sem_flg = SEM_UNDO; // undo on terminate
623
624 timeout.tv_sec = wait_sec;
625 timeout.tv_nsec = 0;
626
627 ret = semtimedop(semid, sops, 2, &timeout);
628 if (ret == -1 && errno != EAGAIN && errno != EINTR)
629 {
630 log_error("semtimedop(lock read) error %d\n", errno);
631 }
632
633 return ret;
634 }
635
636 int user_list_try_rw_lock(int semid, int wait_sec)
637 {
638 struct sembuf sops[3];
639 struct timespec timeout;
640 int ret;
641
642 sops[0].sem_num = 1; // w_sem
643 sops[0].sem_op = 0; // wait until unlocked
644 sops[0].sem_flg = 0;
645
646 sops[1].sem_num = 1; // w_sem
647 sops[1].sem_op = 1; // lock
648 sops[1].sem_flg = SEM_UNDO; // undo on terminate
649
650 sops[2].sem_num = 0; // r_sem
651 sops[2].sem_op = 0; // wait until unlocked
652 sops[2].sem_flg = 0;
653
654 timeout.tv_sec = wait_sec;
655 timeout.tv_nsec = 0;
656
657 ret = semtimedop(semid, sops, 3, &timeout);
658 if (ret == -1 && errno != EAGAIN && errno != EINTR)
659 {
660 log_error("semtimedop(lock write) error %d\n", errno);
661 }
662
663 return ret;
664 }
665
666 int user_list_rd_unlock(int semid)
667 {
668 struct sembuf sops[2];
669 int ret;
670
671 sops[0].sem_num = 0; // r_sem
672 sops[0].sem_op = -1; // unlock
673 sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
674
675 ret = semop(semid, sops, 1);
676 if (ret == -1 && errno != EAGAIN && errno != EINTR)
677 {
678 log_error("semop(unlock read) error %d\n", errno);
679 }
680
681 return ret;
682 }
683
684 int user_list_rw_unlock(int semid)
685 {
686 struct sembuf sops[1];
687 int ret;
688
689 sops[0].sem_num = 1; // w_sem
690 sops[0].sem_op = -1; // unlock
691 sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
692
693 ret = semop(semid, sops, 1);
694 if (ret == -1 && errno != EAGAIN && errno != EINTR)
695 {
696 log_error("semop(unlock write) error %d\n", errno);
697 }
698
699 return ret;
700 }
701
702 int user_list_rd_lock(int semid)
703 {
704 int timer = 0;
705 int ret = -1;
706
707 while (!SYS_server_exit)
708 {
709 ret = user_list_try_rd_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_rd_lock() tried %d times\n", timer);
720 }
721 }
722 else // failed
723 {
724 log_error("user_list_try_rd_lock() failed\n");
725 break;
726 }
727 }
728
729 return ret;
730 }
731
732 int user_list_rw_lock(int semid)
733 {
734 int timer = 0;
735 int ret = -1;
736
737 while (!SYS_server_exit)
738 {
739 ret = user_list_try_rw_lock(semid, USER_LIST_TRY_LOCK_WAIT_TIME);
740 if (ret == 0) // success
741 {
742 break;
743 }
744 else if (errno == EAGAIN || errno == EINTR) // retry
745 {
746 timer++;
747 if (timer % USER_LIST_TRY_LOCK_TIMES == 0)
748 {
749 log_error("user_list_try_rw_lock() tried %d times\n", timer);
750 }
751 }
752 else // failed
753 {
754 log_error("user_list_try_rw_lock() failed\n");
755 break;
756 }
757 }
758
759 return ret;
760 }
761
762 int query_user_list(int page_id, USER_INFO *p_users, int *p_user_count, int *p_page_count)
763 {
764 int ret = 0;
765
766 if (p_users == NULL || p_user_count == NULL || p_page_count == NULL)
767 {
768 log_error("NULL pointer error\n");
769 return -1;
770 }
771
772 *p_user_count = 0;
773 *p_page_count = 0;
774
775 // acquire lock of user list
776 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
777 {
778 log_error("user_list_rd_lock() error\n");
779 return -2;
780 }
781
782 if (p_user_list_pool->p_current->user_count == 0)
783 {
784 // empty list
785 ret = 0;
786 goto cleanup;
787 }
788
789 *p_page_count = p_user_list_pool->p_current->user_count / BBS_user_limit_per_page +
790 (p_user_list_pool->p_current->user_count % BBS_user_limit_per_page == 0 ? 0 : 1);
791
792 if (page_id < 0 || page_id >= *p_page_count)
793 {
794 log_error("Invalid page_id = %d, not in range [0, %d)\n", page_id, *p_page_count);
795 ret = -3;
796 goto cleanup;
797 }
798
799 *p_user_count = MIN(BBS_user_limit_per_page,
800 p_user_list_pool->p_current->user_count -
801 page_id * BBS_user_limit_per_page);
802
803 memcpy(p_users,
804 p_user_list_pool->p_current->users + page_id * BBS_user_limit_per_page,
805 sizeof(USER_INFO) * (size_t)(*p_user_count));
806
807 cleanup:
808 // release lock of user list
809 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
810 {
811 log_error("user_list_rd_unlock() error\n");
812 ret = -1;
813 }
814
815 return ret;
816 }
817
818 int query_user_online_list(int page_id, USER_ONLINE_INFO *p_online_users, int *p_user_count, int *p_page_count)
819 {
820 int ret = 0;
821
822 if (p_online_users == NULL || p_user_count == NULL || p_page_count == NULL)
823 {
824 log_error("NULL pointer error\n");
825 return -1;
826 }
827
828 *p_user_count = 0;
829 *p_page_count = 0;
830
831 // acquire lock of user list
832 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
833 {
834 log_error("user_list_rd_lock() error\n");
835 return -2;
836 }
837
838 if (p_user_list_pool->p_online_current->user_count == 0)
839 {
840 // empty list
841 ret = 0;
842 goto cleanup;
843 }
844
845 *p_page_count = p_user_list_pool->p_online_current->user_count / BBS_user_limit_per_page +
846 (p_user_list_pool->p_online_current->user_count % BBS_user_limit_per_page == 0 ? 0 : 1);
847
848 if (page_id < 0 || page_id >= *p_page_count)
849 {
850 log_error("Invalid page_id = %d, not in range [0, %d)\n", page_id, *p_page_count);
851 ret = -3;
852 goto cleanup;
853 }
854
855 *p_user_count = MIN(BBS_user_limit_per_page,
856 p_user_list_pool->p_online_current->user_count -
857 page_id * BBS_user_limit_per_page);
858
859 memcpy(p_online_users,
860 p_user_list_pool->p_online_current->users + page_id * BBS_user_limit_per_page,
861 sizeof(USER_ONLINE_INFO) * (size_t)(*p_user_count));
862
863 cleanup:
864 // release lock of user list
865 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
866 {
867 log_error("user_list_rd_unlock() error\n");
868 ret = -1;
869 }
870
871 return ret;
872 }
873
874 int get_user_list_count(int *p_user_cnt)
875 {
876 if (p_user_cnt == NULL)
877 {
878 log_error("NULL pointer error\n");
879 return -1;
880 }
881
882 // acquire lock of user list
883 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
884 {
885 log_error("user_list_rd_lock() error\n");
886 return -2;
887 }
888
889 *p_user_cnt = p_user_list_pool->p_current->user_count;
890
891 // release lock of user list
892 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
893 {
894 log_error("user_list_rd_unlock() error\n");
895 return -2;
896 }
897
898 return 0;
899 }
900
901 int get_user_online_list_count(int *p_user_cnt, int *p_guest_cnt)
902 {
903 if (p_user_cnt == NULL || p_guest_cnt == NULL)
904 {
905 log_error("NULL pointer error\n");
906 return -1;
907 }
908
909 // acquire lock of user list
910 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
911 {
912 log_error("user_list_rd_lock() error\n");
913 return -2;
914 }
915
916 *p_user_cnt = p_user_list_pool->p_online_current->user_count;
917 *p_guest_cnt = p_user_list_pool->p_online_current->guest_count;
918
919 // release lock of user list
920 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
921 {
922 log_error("user_list_rd_unlock() error\n");
923 return -2;
924 }
925
926 return 0;
927 }
928
929 int query_user_info(int32_t id, USER_INFO *p_user)
930 {
931 int ret = 0;
932
933 if (p_user == NULL)
934 {
935 log_error("NULL pointer error\n");
936 return -1;
937 }
938
939 // acquire lock of user list
940 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
941 {
942 log_error("user_list_rd_lock() error\n");
943 return -2;
944 }
945
946 if (id >= 0 && id < p_user_list_pool->p_current->user_count) // Found
947 {
948 *p_user = p_user_list_pool->p_current->users[id];
949 ret = 1;
950 }
951
952 // release lock of user list
953 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
954 {
955 log_error("user_list_rd_unlock() error\n");
956 ret = -1;
957 }
958
959 return ret;
960 }
961
962 int query_user_info_by_uid(int32_t uid, USER_INFO *p_user)
963 {
964 int left;
965 int right;
966 int mid;
967 int32_t id;
968 int ret = 0;
969
970 if (p_user == 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 = 0;
984 right = p_user_list_pool->p_current->user_count - 1;
985
986 while (left < right)
987 {
988 mid = (left + right) / 2;
989 if (uid < p_user_list_pool->p_current->index_uid[mid].uid)
990 {
991 right = mid - 1;
992 }
993 else if (uid > p_user_list_pool->p_current->index_uid[mid].uid)
994 {
995 left = mid + 1;
996 }
997 else // if (uid == p_user_list_pool->p_current->index_uid[mid].uid)
998 {
999 left = mid;
1000 break;
1001 }
1002 }
1003
1004 if (uid == p_user_list_pool->p_current->index_uid[left].uid) // Found
1005 {
1006 id = p_user_list_pool->p_current->index_uid[left].id;
1007 *p_user = p_user_list_pool->p_current->users[id];
1008 ret = 1;
1009 }
1010
1011 // release lock of user list
1012 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
1013 {
1014 log_error("user_list_rd_unlock() error\n");
1015 ret = -1;
1016 }
1017
1018 return ret;
1019 }
1020
1021 int query_user_online_info(int32_t id, USER_ONLINE_INFO *p_user)
1022 {
1023 int ret = 0;
1024
1025 if (p_user == NULL)
1026 {
1027 log_error("NULL pointer error\n");
1028 return -1;
1029 }
1030
1031 // acquire lock of user list
1032 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
1033 {
1034 log_error("user_list_rd_lock() error\n");
1035 return -2;
1036 }
1037
1038 if (id >= 0 && id < p_user_list_pool->p_online_current->user_count) // Found
1039 {
1040 *p_user = p_user_list_pool->p_online_current->users[id];
1041 ret = 1;
1042 }
1043
1044 // release lock of user list
1045 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
1046 {
1047 log_error("user_list_rd_unlock() error\n");
1048 ret = -1;
1049 }
1050
1051 return ret;
1052 }
1053
1054 int query_user_online_info_by_uid(int32_t uid, USER_ONLINE_INFO *p_users, int *p_user_cnt, int start_id)
1055 {
1056 int left;
1057 int right;
1058 int mid;
1059 int32_t id;
1060 int ret = 0;
1061 int i;
1062 int user_cnt;
1063
1064 if (p_users == NULL || p_user_cnt == NULL)
1065 {
1066 log_error("NULL pointer error\n");
1067 return -1;
1068 }
1069
1070 user_cnt = *p_user_cnt;
1071 *p_user_cnt = 0;
1072
1073 // acquire lock of user list
1074 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
1075 {
1076 log_error("user_list_rd_lock() error\n");
1077 return -2;
1078 }
1079
1080 left = start_id;
1081 right = p_user_list_pool->p_online_current->user_count - 1;
1082
1083 while (left < right)
1084 {
1085 mid = (left + right) / 2;
1086 if (uid < p_user_list_pool->p_online_current->index_uid[mid].uid)
1087 {
1088 right = mid - 1;
1089 }
1090 else if (uid > p_user_list_pool->p_online_current->index_uid[mid].uid)
1091 {
1092 left = mid + 1;
1093 }
1094 else // if (uid == p_user_list_pool->p_online_current->index_uid[mid].uid)
1095 {
1096 left = mid;
1097 break;
1098 }
1099 }
1100
1101 if (uid == p_user_list_pool->p_online_current->index_uid[left].uid)
1102 {
1103 right = left;
1104 left = start_id;
1105
1106 while (left < right)
1107 {
1108 mid = (left + right) / 2;
1109 if (uid - 1 < p_user_list_pool->p_online_current->index_uid[mid].uid)
1110 {
1111 right = mid;
1112 }
1113 else // if (uid - 1 >= p_user_list_pool->p_online_current->index_uid[mid].uid)
1114 {
1115 left = mid + 1;
1116 }
1117 }
1118
1119 for (i = 0;
1120 left < p_user_list_pool->p_online_current->user_count && i < user_cnt &&
1121 uid == p_user_list_pool->p_online_current->index_uid[left].uid;
1122 left++, i++)
1123 {
1124 id = p_user_list_pool->p_online_current->index_uid[left].id;
1125 p_users[i] = p_user_list_pool->p_online_current->users[id];
1126 }
1127
1128 if (i > 0)
1129 {
1130 *p_user_cnt = i;
1131 ret = 1;
1132 }
1133 }
1134
1135 // release lock of user list
1136 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
1137 {
1138 log_error("user_list_rd_unlock() error\n");
1139 ret = -1;
1140 }
1141
1142 return ret;
1143 }
1144
1145 int get_user_id_list(int32_t *p_uid_list, int *p_user_cnt, int start_uid)
1146 {
1147 int left;
1148 int right;
1149 int mid;
1150 int ret = 0;
1151 int i;
1152
1153 if (p_uid_list == NULL || p_user_cnt == NULL)
1154 {
1155 log_error("NULL pointer error\n");
1156 return -1;
1157 }
1158
1159 // acquire lock of user list
1160 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
1161 {
1162 log_error("user_list_rd_lock() error\n");
1163 return -2;
1164 }
1165
1166 left = 0;
1167 right = p_user_list_pool->p_current->user_count - 1;
1168
1169 while (left < right)
1170 {
1171 mid = (left + right) / 2;
1172 if (start_uid < p_user_list_pool->p_current->index_uid[mid].uid)
1173 {
1174 right = mid - 1;
1175 }
1176 else if (start_uid > p_user_list_pool->p_current->index_uid[mid].uid)
1177 {
1178 left = mid + 1;
1179 }
1180 else // if (start_uid == p_user_list_pool->p_current->index_uid[mid].uid)
1181 {
1182 left = mid;
1183 break;
1184 }
1185 }
1186
1187 for (i = 0; i < *p_user_cnt && left + i < p_user_list_pool->p_current->user_count; i++)
1188 {
1189 p_uid_list[i] = p_user_list_pool->p_current->index_uid[left + i].uid;
1190 }
1191 *p_user_cnt = i;
1192
1193 // release lock of user list
1194 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
1195 {
1196 log_error("user_list_rd_unlock() error\n");
1197 ret = -1;
1198 }
1199
1200 return ret;
1201 }
1202
1203 int user_stat_update(void)
1204 {
1205 return user_stat_map_update(&(p_user_list_pool->user_stat_map));
1206 }
1207
1208 int user_article_cnt_inc(int32_t uid, int n)
1209 {
1210 return user_stat_article_cnt_inc(&(p_user_list_pool->user_stat_map), uid, n);
1211 }
1212
1213 int get_user_article_cnt(int32_t uid)
1214 {
1215 const USER_STAT *p_stat;
1216 int ret;
1217
1218 ret = user_stat_get(&(p_user_list_pool->user_stat_map), uid, &p_stat);
1219 if (ret < 0)
1220 {
1221 log_error("user_stat_get(uid=%d) error: %d\n", uid, ret);
1222 return -1;
1223 }
1224 else if (ret == 0) // user not found
1225 {
1226 return -1;
1227 }
1228
1229 return p_stat->article_count;
1230 }

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