/[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.22 - (show annotations)
Fri Oct 24 02:03:15 2025 UTC (4 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.21: +6 -6 lines
Content type: text/x-csrc
Update user_list
Refine search algorithm

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

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