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

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