/[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.32 - (show annotations)
Tue Nov 11 00:28:05 2025 UTC (4 months ago) by sysadm
Branch: MAIN
Changes since 1.31: +4 -0 lines
Content type: text/x-csrc
Use config.h

1 /* SPDX-License-Identifier: GPL-3.0-or-later */
2 /*
3 * user_list
4 * - data model and basic operations of (online) user list
5 *
6 * Copyright (C) 2004-2025 Leaflet <leaflet@leafok.com>
7 */
8
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12
13 #include "common.h"
14 #include "database.h"
15 #include "log.h"
16 #include "trie_dict.h"
17 #include "user_list.h"
18 #include "user_stat.h"
19 #include <errno.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <time.h>
23 #include <sys/ipc.h>
24 #include <sys/mman.h>
25 #include <sys/param.h>
26 #include <sys/sem.h>
27 #include <sys/shm.h>
28
29 #ifdef _SEM_SEMUN_UNDEFINED
30 union semun
31 {
32 int val; /* Value for SETVAL */
33 struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
34 unsigned short *array; /* Array for GETALL, SETALL */
35 struct seminfo *__buf; /* Buffer for IPC_INFO
36 (Linux-specific) */
37 };
38 #endif // #ifdef _SEM_SEMUN_UNDEFINED
39
40 enum _user_list_constant_t
41 {
42 USER_LIST_TRY_LOCK_WAIT_TIME = 1, // second
43 USER_LIST_TRY_LOCK_TIMES = 10,
44 };
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 USER_STAT_MAP user_stat_map;
57 int user_login_count;
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_online_list);
97 static int user_login_count_load(MYSQL *db);
98
99 static int user_info_index_uid_comp(const void *ptr1, const void *ptr2)
100 {
101 const USER_INFO_INDEX_UID *p1 = ptr1;
102 const USER_INFO_INDEX_UID *p2 = ptr2;
103
104 if (p1->uid < p2->uid)
105 {
106 return -1;
107 }
108 else if (p1->uid > p2->uid)
109 {
110 return 1;
111 }
112 else if (p1->id < p2->id)
113 {
114 return -1;
115 }
116 else if (p1->id > p2->id)
117 {
118 return 1;
119 }
120 return 0;
121 }
122
123 int user_list_load(MYSQL *db, USER_LIST *p_list)
124 {
125 MYSQL_RES *rs = NULL;
126 MYSQL_ROW row;
127 char sql[SQL_BUFFER_LEN];
128 int ret = 0;
129 int i;
130 int j;
131 int32_t last_uid;
132 size_t intro_buf_offset;
133 size_t intro_len;
134
135 if (db == NULL || p_list == NULL)
136 {
137 log_error("NULL pointer error\n");
138 return -1;
139 }
140
141 if (p_list->user_count > 0)
142 {
143 last_uid = p_list->users[p_list->user_count - 1].uid;
144 }
145 else
146 {
147 last_uid = -1;
148 }
149
150 snprintf(sql, sizeof(sql),
151 "SELECT user_list.UID AS UID, username, nickname, gender, gender_pub, life, exp, visit_count, "
152 "UNIX_TIMESTAMP(signup_dt), UNIX_TIMESTAMP(last_login_dt), UNIX_TIMESTAMP(last_logout_dt), "
153 "UNIX_TIMESTAMP(birthday), `introduction` "
154 "FROM user_list INNER JOIN user_pubinfo ON user_list.UID = user_pubinfo.UID "
155 "INNER JOIN user_reginfo ON user_list.UID = user_reginfo.UID "
156 "WHERE enable ORDER BY username");
157
158 if (mysql_query(db, sql) != 0)
159 {
160 log_error("Query user info error: %s\n", mysql_error(db));
161 ret = -1;
162 goto cleanup;
163 }
164
165 if ((rs = mysql_use_result(db)) == NULL)
166 {
167 log_error("Get user info data failed\n");
168 ret = -1;
169 goto cleanup;
170 }
171
172 intro_buf_offset = 0;
173 i = 0;
174 while ((row = mysql_fetch_row(rs)))
175 {
176 // record
177 p_list->users[i].id = i;
178 p_list->users[i].uid = atoi(row[0]);
179 strncpy(p_list->users[i].username, row[1], sizeof(p_list->users[i].username) - 1);
180 p_list->users[i].username[sizeof(p_list->users[i].username) - 1] = '\0';
181 strncpy(p_list->users[i].nickname, row[2], sizeof(p_list->users[i].nickname) - 1);
182 p_list->users[i].nickname[sizeof(p_list->users[i].nickname) - 1] = '\0';
183 p_list->users[i].gender = row[3][0];
184 p_list->users[i].gender_pub = (int8_t)(row[4] == NULL ? 0 : atoi(row[4]));
185 p_list->users[i].life = (row[5] == NULL ? 0 : atoi(row[5]));
186 p_list->users[i].exp = (row[6] == NULL ? 0 : atoi(row[6]));
187 p_list->users[i].visit_count = (row[7] == NULL ? 0 : atoi(row[7]));
188 p_list->users[i].signup_dt = (row[8] == NULL ? 0 : atol(row[8]));
189 p_list->users[i].last_login_dt = (row[9] == NULL ? 0 : atol(row[9]));
190 p_list->users[i].last_logout_dt = (row[10] == NULL ? 0 : atol(row[10]));
191 p_list->users[i].birthday = (row[11] == NULL ? 0 : atol(row[11]));
192 intro_len = strlen((row[12] == NULL ? "" : row[12]));
193 if (intro_len >= sizeof(p_list->user_intro_buf) - 1 - intro_buf_offset)
194 {
195 log_error("OOM for user introduction: len=%d, i=%d\n", intro_len, i);
196 break;
197 }
198 memcpy(p_list->user_intro_buf + intro_buf_offset,
199 (row[12] == NULL ? "" : row[12]),
200 intro_len + 1);
201 p_list->users[i].intro = p_list->user_intro_buf + intro_buf_offset;
202 intro_buf_offset += (intro_len + 1);
203
204 i++;
205 if (i >= BBS_max_user_count)
206 {
207 log_error("Too many users, exceed limit %d\n", BBS_max_user_count);
208 break;
209 }
210 }
211 mysql_free_result(rs);
212 rs = NULL;
213
214 if (i != p_list->user_count || p_list->users[i - 1].uid != last_uid) // Count of users changed
215 {
216 // Rebuild index
217 for (j = 0; j < i; j++)
218 {
219 p_list->index_uid[j].uid = p_list->users[j].uid;
220 p_list->index_uid[j].id = j;
221 }
222
223 qsort(p_list->index_uid, (size_t)i, sizeof(USER_INFO_INDEX_UID), user_info_index_uid_comp);
224
225 #ifdef _DEBUG
226 log_error("Rebuild index of %d users, last_uid=%d\n", i, p_list->users[i - 1].uid);
227 #endif
228 }
229
230 p_list->user_count = i;
231
232 #ifdef _DEBUG
233 log_error("Loaded %d users\n", p_list->user_count);
234 #endif
235
236 cleanup:
237 mysql_free_result(rs);
238
239 return ret;
240 }
241
242 int user_online_list_load(MYSQL *db, USER_ONLINE_LIST *p_online_list)
243 {
244 MYSQL_RES *rs = NULL;
245 MYSQL_ROW row;
246 char sql[SQL_BUFFER_LEN];
247 int ret = 0;
248 int i;
249 int j;
250 int user_cnt;
251 int guest_cnt;
252
253 if (db == NULL || p_online_list == NULL)
254 {
255 log_error("NULL pointer error\n");
256 return -1;
257 }
258
259 snprintf(sql, sizeof(sql),
260 "SELECT SID, UID, ip, current_action, UNIX_TIMESTAMP(login_tm), "
261 "UNIX_TIMESTAMP(last_tm) FROM user_online "
262 "WHERE last_tm >= SUBDATE(NOW(), INTERVAL %d SECOND) "
263 "ORDER BY last_tm DESC",
264 BBS_user_off_line);
265
266 if (mysql_query(db, sql) != 0)
267 {
268 log_error("Query user online error: %s\n", mysql_error(db));
269 ret = -1;
270 goto cleanup;
271 }
272
273 if ((rs = mysql_use_result(db)) == NULL)
274 {
275 log_error("Get user online data failed\n");
276 ret = -1;
277 goto cleanup;
278 }
279
280 i = 0;
281 user_cnt = 0;
282 guest_cnt = 0;
283 while ((row = mysql_fetch_row(rs)))
284 {
285 if (atoi(row[1]) == 0) // guest
286 {
287 guest_cnt++;
288 continue;
289 }
290 else
291 {
292 user_cnt++;
293 }
294
295 p_online_list->users[i].id = i;
296 strncpy(p_online_list->users[i].session_id, row[0], sizeof(p_online_list->users[i].session_id) - 1);
297 p_online_list->users[i].session_id[sizeof(p_online_list->users[i].session_id) - 1] = '\0';
298
299 if ((ret = query_user_info_by_uid(atoi(row[1]), &(p_online_list->users[i].user_info), NULL, 0)) <= 0)
300 {
301 log_error("query_user_info_by_uid(%d) error\n", atoi(row[1]));
302 continue;
303 }
304
305 strncpy(p_online_list->users[i].ip, row[2], sizeof(p_online_list->users[i].ip) - 1);
306 p_online_list->users[i].ip[sizeof(p_online_list->users[i].ip) - 1] = '\0';
307
308 strncpy(p_online_list->users[i].current_action, row[3], sizeof(p_online_list->users[i].current_action) - 1);
309 p_online_list->users[i].current_action[sizeof(p_online_list->users[i].current_action) - 1] = '\0';
310 p_online_list->users[i].current_action_title = NULL;
311 if (p_online_list->users[i].current_action[0] == '\0')
312 {
313 p_online_list->users[i].current_action_title = "";
314 }
315 else if (trie_dict_get(p_trie_action_dict, p_online_list->users[i].current_action, (int64_t *)(&(p_online_list->users[i].current_action_title))) < 0)
316 {
317 log_error("trie_dict_get(p_trie_action_dict, %s) error on session_id=%s\n",
318 p_online_list->users[i].current_action, p_online_list->users[i].session_id);
319 continue;
320 }
321
322 p_online_list->users[i].login_tm = (row[4] == NULL ? 0 : atol(row[4]));
323 p_online_list->users[i].last_tm = (row[5] == NULL ? 0 : atol(row[5]));
324
325 i++;
326 if (i >= BBS_max_user_online_count)
327 {
328 log_error("Too many online users, exceed limit %d\n", BBS_max_user_online_count);
329 break;
330 }
331 }
332 mysql_free_result(rs);
333 rs = NULL;
334
335 if (user_cnt > 0)
336 {
337 // Rebuild index
338 for (j = 0; j < user_cnt; j++)
339 {
340 p_online_list->index_uid[j].uid = p_online_list->users[j].user_info.uid;
341 p_online_list->index_uid[j].id = j;
342 }
343
344 qsort(p_online_list->index_uid, (size_t)user_cnt, sizeof(USER_INFO_INDEX_UID), user_info_index_uid_comp);
345 }
346
347 p_online_list->user_count = user_cnt;
348 p_online_list->guest_count = guest_cnt;
349
350 cleanup:
351 mysql_free_result(rs);
352
353 return ret;
354 }
355
356 int user_login_count_load(MYSQL *db)
357 {
358 MYSQL_RES *rs = NULL;
359 MYSQL_ROW row;
360 char sql[SQL_BUFFER_LEN];
361
362 if (db == NULL)
363 {
364 log_error("NULL pointer error\n");
365 return -1;
366 }
367
368 snprintf(sql, sizeof(sql),
369 "SELECT ID FROM user_login_log ORDER BY ID DESC LIMIT 1");
370 if (mysql_query(db, sql) != 0)
371 {
372 log_error("Query user_login_log error: %s\n", mysql_error(db));
373 return -2;
374 }
375 if ((rs = mysql_store_result(db)) == NULL)
376 {
377 log_error("Get user_login_log data failed\n");
378 return -2;
379 }
380 if ((row = mysql_fetch_row(rs)))
381 {
382 p_user_list_pool->user_login_count = atoi(row[0]);
383 }
384 mysql_free_result(rs);
385
386 return 0;
387 }
388
389 int user_list_pool_init(const char *filename)
390 {
391 int shmid;
392 int semid;
393 int proj_id;
394 key_t key;
395 size_t size;
396 void *p_shm;
397 union semun arg;
398 int i;
399
400 if (p_user_list_pool != NULL || p_trie_action_dict != NULL)
401 {
402 log_error("p_user_list_pool already initialized\n");
403 return -1;
404 }
405
406 p_trie_action_dict = trie_dict_create();
407 if (p_trie_action_dict == NULL)
408 {
409 log_error("trie_dict_create() error\n");
410 return -1;
411 }
412
413 for (i = 0; i < user_action_map_size; i++)
414 {
415 if (trie_dict_set(p_trie_action_dict, user_action_map[i].name, (int64_t)(user_action_map[i].title)) < 0)
416 {
417 log_error("trie_dict_set(p_trie_action_dict, %s) error\n", user_action_map[i].name);
418 }
419 }
420
421 // Allocate shared memory
422 proj_id = (int)(time(NULL) % getpid());
423 key = ftok(filename, proj_id);
424 if (key == -1)
425 {
426 log_error("ftok(%s %d) error (%d)\n", filename, proj_id, errno);
427 return -2;
428 }
429
430 size = sizeof(USER_LIST_POOL);
431 shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
432 if (shmid == -1)
433 {
434 log_error("shmget(size = %d) error (%d)\n", size, errno);
435 return -3;
436 }
437 p_shm = shmat(shmid, NULL, 0);
438 if (p_shm == (void *)-1)
439 {
440 log_error("shmat(shmid=%d) error (%d)\n", shmid, errno);
441 return -3;
442 }
443
444 p_user_list_pool = p_shm;
445 p_user_list_pool->shmid = shmid;
446
447 // Allocate semaphore as user list pool lock
448 size = 2; // r_sem and w_sem
449 semid = semget(key, (int)size, IPC_CREAT | IPC_EXCL | 0600);
450 if (semid == -1)
451 {
452 log_error("semget(user_list_pool_sem, size = %d) error (%d)\n", size, errno);
453 return -3;
454 }
455
456 // Initialize sem value to 0
457 arg.val = 0;
458 for (i = 0; i < size; i++)
459 {
460 if (semctl(semid, i, SETVAL, arg) == -1)
461 {
462 log_error("semctl(user_list_pool_sem, SETVAL) error (%d)\n", errno);
463 return -3;
464 }
465 }
466
467 p_user_list_pool->semid = semid;
468
469 // Set user counts to 0
470 p_user_list_pool->user_list[0].user_count = 0;
471 p_user_list_pool->user_list[1].user_count = 0;
472
473 p_user_list_pool->p_current = &(p_user_list_pool->user_list[0]);
474 p_user_list_pool->p_new = &(p_user_list_pool->user_list[1]);
475
476 p_user_list_pool->p_online_current = &(p_user_list_pool->user_online_list[0]);
477 p_user_list_pool->p_online_new = &(p_user_list_pool->user_online_list[1]);
478
479 user_stat_map_init(&(p_user_list_pool->user_stat_map));
480
481 return 0;
482 }
483
484 void user_list_pool_cleanup(void)
485 {
486 int shmid;
487
488 if (p_user_list_pool == NULL)
489 {
490 return;
491 }
492
493 shmid = p_user_list_pool->shmid;
494
495 if (semctl(p_user_list_pool->semid, 0, IPC_RMID) == -1)
496 {
497 log_error("semctl(semid = %d, IPC_RMID) error (%d)\n", p_user_list_pool->semid, errno);
498 }
499
500 if (shmdt(p_user_list_pool) == -1)
501 {
502 log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
503 }
504
505 if (shmid != 0 && shmctl(shmid, IPC_RMID, NULL) == -1)
506 {
507 log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", shmid, errno);
508 }
509
510 p_user_list_pool = NULL;
511
512 if (p_trie_action_dict != NULL)
513 {
514 trie_dict_destroy(p_trie_action_dict);
515
516 p_trie_action_dict = NULL;
517 }
518 }
519
520 int set_user_list_pool_shm_readonly(void)
521 {
522 int shmid;
523 void *p_shm;
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 shmid = p_user_list_pool->shmid;
532
533 // Remap shared memory in read-only mode
534 p_shm = shmat(shmid, p_user_list_pool, SHM_RDONLY | SHM_REMAP);
535 if (p_shm == (void *)-1)
536 {
537 log_error("shmat(user_list_pool shmid = %d) error (%d)\n", shmid, errno);
538 return -3;
539 }
540
541 p_user_list_pool = p_shm;
542
543 return 0;
544 }
545
546 int detach_user_list_pool_shm(void)
547 {
548 if (p_user_list_pool != NULL && shmdt(p_user_list_pool) == -1)
549 {
550 log_error("shmdt(user_list_pool) error (%d)\n", errno);
551 return -1;
552 }
553
554 p_user_list_pool = NULL;
555
556 return 0;
557 }
558
559 int user_list_pool_reload(int online_user)
560 {
561 MYSQL *db = NULL;
562 USER_LIST *p_tmp;
563 USER_ONLINE_LIST *p_online_tmp;
564 int ret = 0;
565
566 if (p_user_list_pool == NULL)
567 {
568 log_error("p_user_list_pool not initialized\n");
569 return -1;
570 }
571
572 db = db_open();
573 if (db == NULL)
574 {
575 log_error("db_open() error: %s\n", mysql_error(db));
576 return -1;
577 }
578
579 if (online_user)
580 {
581 if (user_online_list_load(db, p_user_list_pool->p_online_new) < 0)
582 {
583 log_error("user_online_list_load() error\n");
584 ret = -2;
585 goto cleanup;
586 }
587
588 if (user_login_count_load(db) < 0)
589 {
590 log_error("user_login_count_load() error\n");
591 ret = -2;
592 goto cleanup;
593 }
594 }
595 else
596 {
597 if (user_list_load(db, p_user_list_pool->p_new) < 0)
598 {
599 log_error("user_list_load() error\n");
600 ret = -2;
601 goto cleanup;
602 }
603 }
604
605 mysql_close(db);
606 db = NULL;
607
608 if (user_list_rw_lock(p_user_list_pool->semid) < 0)
609 {
610 log_error("user_list_rw_lock() error\n");
611 ret = -3;
612 goto cleanup;
613 }
614
615 if (online_user)
616 {
617 // Swap p_online_current and p_online_new
618 p_online_tmp = p_user_list_pool->p_online_current;
619 p_user_list_pool->p_online_current = p_user_list_pool->p_online_new;
620 p_user_list_pool->p_online_new = p_online_tmp;
621 }
622 else
623 {
624 // Swap p_current and p_new
625 p_tmp = p_user_list_pool->p_current;
626 p_user_list_pool->p_current = p_user_list_pool->p_new;
627 p_user_list_pool->p_new = p_tmp;
628 }
629
630 if (user_list_rw_unlock(p_user_list_pool->semid) < 0)
631 {
632 log_error("user_list_rw_unlock() error\n");
633 ret = -3;
634 goto cleanup;
635 }
636
637 cleanup:
638 mysql_close(db);
639
640 return ret;
641 }
642
643 int user_list_try_rd_lock(int semid, int wait_sec)
644 {
645 struct sembuf sops[2];
646 struct timespec timeout;
647 int ret;
648
649 sops[0].sem_num = 1; // w_sem
650 sops[0].sem_op = 0; // wait until unlocked
651 sops[0].sem_flg = 0;
652
653 sops[1].sem_num = 0; // r_sem
654 sops[1].sem_op = 1; // lock
655 sops[1].sem_flg = SEM_UNDO; // undo on terminate
656
657 timeout.tv_sec = wait_sec;
658 timeout.tv_nsec = 0;
659
660 ret = semtimedop(semid, sops, 2, &timeout);
661 if (ret == -1 && errno != EAGAIN && errno != EINTR)
662 {
663 log_error("semtimedop(lock read) error %d\n", errno);
664 }
665
666 return ret;
667 }
668
669 int user_list_try_rw_lock(int semid, int wait_sec)
670 {
671 struct sembuf sops[3];
672 struct timespec timeout;
673 int ret;
674
675 sops[0].sem_num = 1; // w_sem
676 sops[0].sem_op = 0; // wait until unlocked
677 sops[0].sem_flg = 0;
678
679 sops[1].sem_num = 1; // w_sem
680 sops[1].sem_op = 1; // lock
681 sops[1].sem_flg = SEM_UNDO; // undo on terminate
682
683 sops[2].sem_num = 0; // r_sem
684 sops[2].sem_op = 0; // wait until unlocked
685 sops[2].sem_flg = 0;
686
687 timeout.tv_sec = wait_sec;
688 timeout.tv_nsec = 0;
689
690 ret = semtimedop(semid, sops, 3, &timeout);
691 if (ret == -1 && errno != EAGAIN && errno != EINTR)
692 {
693 log_error("semtimedop(lock write) error %d\n", errno);
694 }
695
696 return ret;
697 }
698
699 int user_list_rd_unlock(int semid)
700 {
701 struct sembuf sops[2];
702 int ret;
703
704 sops[0].sem_num = 0; // r_sem
705 sops[0].sem_op = -1; // unlock
706 sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
707
708 ret = semop(semid, sops, 1);
709 if (ret == -1 && errno != EAGAIN && errno != EINTR)
710 {
711 log_error("semop(unlock read) error %d\n", errno);
712 }
713
714 return ret;
715 }
716
717 int user_list_rw_unlock(int semid)
718 {
719 struct sembuf sops[1];
720 int ret;
721
722 sops[0].sem_num = 1; // w_sem
723 sops[0].sem_op = -1; // unlock
724 sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
725
726 ret = semop(semid, sops, 1);
727 if (ret == -1 && errno != EAGAIN && errno != EINTR)
728 {
729 log_error("semop(unlock write) error %d\n", errno);
730 }
731
732 return ret;
733 }
734
735 int user_list_rd_lock(int semid)
736 {
737 int timer = 0;
738 int ret = -1;
739
740 while (!SYS_server_exit)
741 {
742 ret = user_list_try_rd_lock(semid, USER_LIST_TRY_LOCK_WAIT_TIME);
743 if (ret == 0) // success
744 {
745 break;
746 }
747 else if (errno == EAGAIN || errno == EINTR) // retry
748 {
749 timer++;
750 if (timer % USER_LIST_TRY_LOCK_TIMES == 0)
751 {
752 log_error("user_list_try_rd_lock() tried %d times\n", timer);
753 }
754 }
755 else // failed
756 {
757 log_error("user_list_try_rd_lock() failed\n");
758 break;
759 }
760 }
761
762 return ret;
763 }
764
765 int user_list_rw_lock(int semid)
766 {
767 int timer = 0;
768 int ret = -1;
769
770 while (!SYS_server_exit)
771 {
772 ret = user_list_try_rw_lock(semid, USER_LIST_TRY_LOCK_WAIT_TIME);
773 if (ret == 0) // success
774 {
775 break;
776 }
777 else if (errno == EAGAIN || errno == EINTR) // retry
778 {
779 timer++;
780 if (timer % USER_LIST_TRY_LOCK_TIMES == 0)
781 {
782 log_error("user_list_try_rw_lock() tried %d times\n", timer);
783 }
784 }
785 else // failed
786 {
787 log_error("user_list_try_rw_lock() failed\n");
788 break;
789 }
790 }
791
792 return ret;
793 }
794
795 int query_user_list(int page_id, USER_INFO *p_users, int *p_user_count, int *p_page_count)
796 {
797 int ret = 0;
798
799 if (p_users == NULL || p_user_count == NULL || p_page_count == NULL)
800 {
801 log_error("NULL pointer error\n");
802 return -1;
803 }
804
805 *p_user_count = 0;
806 *p_page_count = 0;
807
808 // acquire lock of user list
809 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
810 {
811 log_error("user_list_rd_lock() error\n");
812 return -2;
813 }
814
815 if (p_user_list_pool->p_current->user_count == 0)
816 {
817 // empty list
818 ret = 0;
819 goto cleanup;
820 }
821
822 *p_page_count = p_user_list_pool->p_current->user_count / BBS_user_limit_per_page +
823 (p_user_list_pool->p_current->user_count % BBS_user_limit_per_page == 0 ? 0 : 1);
824
825 if (page_id < 0 || page_id >= *p_page_count)
826 {
827 log_error("Invalid page_id = %d, not in range [0, %d)\n", page_id, *p_page_count);
828 ret = -3;
829 goto cleanup;
830 }
831
832 *p_user_count = MIN(BBS_user_limit_per_page,
833 p_user_list_pool->p_current->user_count -
834 page_id * BBS_user_limit_per_page);
835
836 memcpy(p_users,
837 p_user_list_pool->p_current->users + page_id * BBS_user_limit_per_page,
838 sizeof(USER_INFO) * (size_t)(*p_user_count));
839
840 cleanup:
841 // release lock of user list
842 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
843 {
844 log_error("user_list_rd_unlock() error\n");
845 ret = -1;
846 }
847
848 return ret;
849 }
850
851 int query_user_online_list(int page_id, USER_ONLINE_INFO *p_online_users, int *p_user_count, int *p_page_count)
852 {
853 int ret = 0;
854
855 if (p_online_users == NULL || p_user_count == NULL || p_page_count == NULL)
856 {
857 log_error("NULL pointer error\n");
858 return -1;
859 }
860
861 *p_user_count = 0;
862 *p_page_count = 0;
863
864 // acquire lock of user list
865 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
866 {
867 log_error("user_list_rd_lock() error\n");
868 return -2;
869 }
870
871 if (p_user_list_pool->p_online_current->user_count == 0)
872 {
873 // empty list
874 ret = 0;
875 goto cleanup;
876 }
877
878 *p_page_count = p_user_list_pool->p_online_current->user_count / BBS_user_limit_per_page +
879 (p_user_list_pool->p_online_current->user_count % BBS_user_limit_per_page == 0 ? 0 : 1);
880
881 if (page_id < 0 || page_id >= *p_page_count)
882 {
883 log_error("Invalid page_id = %d, not in range [0, %d)\n", page_id, *p_page_count);
884 ret = -3;
885 goto cleanup;
886 }
887
888 *p_user_count = MIN(BBS_user_limit_per_page,
889 p_user_list_pool->p_online_current->user_count -
890 page_id * BBS_user_limit_per_page);
891
892 memcpy(p_online_users,
893 p_user_list_pool->p_online_current->users + page_id * BBS_user_limit_per_page,
894 sizeof(USER_ONLINE_INFO) * (size_t)(*p_user_count));
895
896 cleanup:
897 // release lock of user list
898 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
899 {
900 log_error("user_list_rd_unlock() error\n");
901 ret = -1;
902 }
903
904 return ret;
905 }
906
907 int get_user_list_count(int *p_user_cnt)
908 {
909 if (p_user_cnt == NULL)
910 {
911 log_error("NULL pointer error\n");
912 return -1;
913 }
914
915 // acquire lock of user list
916 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
917 {
918 log_error("user_list_rd_lock() error\n");
919 return -2;
920 }
921
922 *p_user_cnt = p_user_list_pool->p_current->user_count;
923
924 // release lock of user list
925 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
926 {
927 log_error("user_list_rd_unlock() error\n");
928 return -2;
929 }
930
931 return 0;
932 }
933
934 int get_user_online_list_count(int *p_user_cnt, int *p_guest_cnt)
935 {
936 if (p_user_cnt == NULL || p_guest_cnt == NULL)
937 {
938 log_error("NULL pointer error\n");
939 return -1;
940 }
941
942 // acquire lock of user list
943 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
944 {
945 log_error("user_list_rd_lock() error\n");
946 return -2;
947 }
948
949 *p_user_cnt = p_user_list_pool->p_online_current->user_count;
950 *p_guest_cnt = p_user_list_pool->p_online_current->guest_count;
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 return -2;
957 }
958
959 return 0;
960 }
961
962 int get_user_login_count(int *p_login_cnt)
963 {
964 if (p_login_cnt == NULL)
965 {
966 log_error("NULL pointer error\n");
967 return -1;
968 }
969
970 *p_login_cnt = p_user_list_pool->user_login_count;
971
972 return 0;
973 }
974
975 int query_user_info(int32_t id, USER_INFO *p_user)
976 {
977 int ret = 0;
978
979 if (p_user == NULL)
980 {
981 log_error("NULL pointer error\n");
982 return -1;
983 }
984
985 // acquire lock of user list
986 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
987 {
988 log_error("user_list_rd_lock() error\n");
989 return -2;
990 }
991
992 if (id >= 0 && id < p_user_list_pool->p_current->user_count) // Found
993 {
994 *p_user = p_user_list_pool->p_current->users[id];
995 ret = 1;
996 }
997
998 // release lock of user list
999 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
1000 {
1001 log_error("user_list_rd_unlock() error\n");
1002 ret = -1;
1003 }
1004
1005 return ret;
1006 }
1007
1008 int query_user_info_by_uid(int32_t uid, USER_INFO *p_user, char *p_intro_buf, size_t intro_buf_len)
1009 {
1010 int left;
1011 int right;
1012 int mid;
1013 int32_t id;
1014 int ret = 0;
1015
1016 if (p_user == NULL)
1017 {
1018 log_error("NULL pointer error\n");
1019 return -1;
1020 }
1021
1022 // acquire lock of user list
1023 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
1024 {
1025 log_error("user_list_rd_lock() error\n");
1026 return -2;
1027 }
1028
1029 left = 0;
1030 right = p_user_list_pool->p_current->user_count - 1;
1031
1032 while (left < right)
1033 {
1034 mid = (left + right) / 2;
1035 if (uid < p_user_list_pool->p_current->index_uid[mid].uid)
1036 {
1037 right = mid - 1;
1038 }
1039 else if (uid > p_user_list_pool->p_current->index_uid[mid].uid)
1040 {
1041 left = mid + 1;
1042 }
1043 else // if (uid == p_user_list_pool->p_current->index_uid[mid].uid)
1044 {
1045 left = mid;
1046 break;
1047 }
1048 }
1049
1050 if (uid == p_user_list_pool->p_current->index_uid[left].uid) // Found
1051 {
1052 id = p_user_list_pool->p_current->index_uid[left].id;
1053 *p_user = p_user_list_pool->p_current->users[id];
1054 ret = 1;
1055
1056 if (p_intro_buf != NULL)
1057 {
1058 strncpy(p_intro_buf, p_user_list_pool->p_current->users[id].intro, intro_buf_len - 1);
1059 p_intro_buf[intro_buf_len - 1] = '\0';
1060 p_user->intro = p_intro_buf;
1061 }
1062 }
1063
1064 // release lock of user list
1065 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
1066 {
1067 log_error("user_list_rd_unlock() error\n");
1068 ret = -1;
1069 }
1070
1071 return ret;
1072 }
1073
1074 int query_user_info_by_username(const char *username_prefix, int max_user_cnt,
1075 int32_t uid_list[], char username_list[][BBS_username_max_len + 1])
1076 {
1077 int left;
1078 int right;
1079 int mid;
1080 int left_save;
1081 int ret = 0;
1082 size_t prefix_len;
1083 int comp;
1084 int i;
1085
1086 if (username_prefix == NULL || uid_list == NULL || username_list == NULL)
1087 {
1088 log_error("NULL pointer error\n");
1089 return -1;
1090 }
1091
1092 prefix_len = strlen(username_prefix);
1093
1094 // acquire lock of user list
1095 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
1096 {
1097 log_error("user_list_rd_lock() error\n");
1098 return -2;
1099 }
1100
1101 left = 0;
1102 right = p_user_list_pool->p_current->user_count - 1;
1103
1104 while (left < right)
1105 {
1106 mid = (left + right) / 2;
1107 comp = strncasecmp(username_prefix, p_user_list_pool->p_current->users[mid].username, prefix_len);
1108 if (comp < 0)
1109 {
1110 right = mid - 1;
1111 }
1112 else if (comp > 0)
1113 {
1114 left = mid + 1;
1115 }
1116 else // if (comp == 0)
1117 {
1118 left = mid;
1119 break;
1120 }
1121 }
1122
1123 if (strncasecmp(username_prefix, p_user_list_pool->p_current->users[left].username, prefix_len) == 0) // Found
1124 {
1125 #ifdef _DEBUG
1126 log_error("Debug: match found, pos=%d\n", left);
1127 #endif
1128
1129 left_save = left;
1130 right = left;
1131 left = 0;
1132
1133 while (left < right)
1134 {
1135 mid = (left + right) / 2;
1136 comp = strncasecmp(username_prefix, p_user_list_pool->p_current->users[mid].username, prefix_len);
1137 if (comp > 0)
1138 {
1139 left = mid + 1;
1140 }
1141 else if (comp == 0)
1142 {
1143 right = mid;
1144 }
1145 else // if (comp < 0)
1146 {
1147 log_error("Bug: left=%d right=%d mid=%d");
1148 ret = -2;
1149 goto cleanup;
1150 }
1151 }
1152
1153 #ifdef _DEBUG
1154 log_error("Debug: first match found, pos=%d\n", right);
1155 #endif
1156
1157 left = left_save;
1158 left_save = right;
1159 right = p_user_list_pool->p_current->user_count - 1;
1160
1161 while (left < right)
1162 {
1163 mid = (left + right) / 2 + (left + right) % 2;
1164 comp = strncasecmp(username_prefix, p_user_list_pool->p_current->users[mid].username, prefix_len);
1165 if (comp < 0)
1166 {
1167 right = mid - 1;
1168 }
1169 else if (comp == 0)
1170 {
1171 left = mid;
1172 }
1173 else // if (comp > 0)
1174 {
1175 log_error("Bug: left=%d right=%d mid=%d");
1176 ret = -2;
1177 goto cleanup;
1178 }
1179 }
1180
1181 #ifdef _DEBUG
1182 log_error("Debug: last match found, pos=%d\n", left);
1183 #endif
1184
1185 right = left;
1186 left = left_save;
1187
1188 for (i = 0; i < max_user_cnt && left + i <= right; i++)
1189 {
1190 uid_list[i] = p_user_list_pool->p_current->users[left + i].uid;
1191 strncpy(username_list[i], p_user_list_pool->p_current->users[left + i].username, sizeof(username_list[i]) - 1);
1192 username_list[i][sizeof(username_list[i]) - 1] = '\0';
1193 }
1194 ret = i;
1195 }
1196
1197 cleanup:
1198 // release lock of user list
1199 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
1200 {
1201 log_error("user_list_rd_unlock() error\n");
1202 ret = -1;
1203 }
1204
1205 return ret;
1206 }
1207
1208 int query_user_online_info(int32_t id, USER_ONLINE_INFO *p_user)
1209 {
1210 int ret = 0;
1211
1212 if (p_user == NULL)
1213 {
1214 log_error("NULL pointer error\n");
1215 return -1;
1216 }
1217
1218 // acquire lock of user list
1219 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
1220 {
1221 log_error("user_list_rd_lock() error\n");
1222 return -2;
1223 }
1224
1225 if (id >= 0 && id < p_user_list_pool->p_online_current->user_count) // Found
1226 {
1227 *p_user = p_user_list_pool->p_online_current->users[id];
1228 ret = 1;
1229 }
1230
1231 // release lock of user list
1232 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
1233 {
1234 log_error("user_list_rd_unlock() error\n");
1235 ret = -1;
1236 }
1237
1238 return ret;
1239 }
1240
1241 int query_user_online_info_by_uid(int32_t uid, USER_ONLINE_INFO *p_users, int *p_user_cnt, int start_id)
1242 {
1243 int left;
1244 int right;
1245 int mid;
1246 int32_t id;
1247 int ret = 0;
1248 int i;
1249 int user_cnt;
1250
1251 if (p_users == NULL || p_user_cnt == NULL)
1252 {
1253 log_error("NULL pointer error\n");
1254 return -1;
1255 }
1256
1257 user_cnt = *p_user_cnt;
1258 *p_user_cnt = 0;
1259
1260 // acquire lock of user list
1261 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
1262 {
1263 log_error("user_list_rd_lock() error\n");
1264 return -2;
1265 }
1266
1267 left = start_id;
1268 right = p_user_list_pool->p_online_current->user_count - 1;
1269
1270 while (left < right)
1271 {
1272 mid = (left + right) / 2;
1273 if (uid < p_user_list_pool->p_online_current->index_uid[mid].uid)
1274 {
1275 right = mid - 1;
1276 }
1277 else if (uid > p_user_list_pool->p_online_current->index_uid[mid].uid)
1278 {
1279 left = mid + 1;
1280 }
1281 else // if (uid == p_user_list_pool->p_online_current->index_uid[mid].uid)
1282 {
1283 left = mid;
1284 break;
1285 }
1286 }
1287
1288 if (uid == p_user_list_pool->p_online_current->index_uid[left].uid)
1289 {
1290 right = left;
1291 left = start_id;
1292
1293 while (left < right)
1294 {
1295 mid = (left + right) / 2;
1296 if (uid - 1 < p_user_list_pool->p_online_current->index_uid[mid].uid)
1297 {
1298 right = mid;
1299 }
1300 else // if (uid - 1 >= p_user_list_pool->p_online_current->index_uid[mid].uid)
1301 {
1302 left = mid + 1;
1303 }
1304 }
1305
1306 for (i = 0;
1307 left < p_user_list_pool->p_online_current->user_count && i < user_cnt &&
1308 uid == p_user_list_pool->p_online_current->index_uid[left].uid;
1309 left++, i++)
1310 {
1311 id = p_user_list_pool->p_online_current->index_uid[left].id;
1312 p_users[i] = p_user_list_pool->p_online_current->users[id];
1313 }
1314
1315 if (i > 0)
1316 {
1317 *p_user_cnt = i;
1318 ret = 1;
1319 }
1320 }
1321
1322 // release lock of user list
1323 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
1324 {
1325 log_error("user_list_rd_unlock() error\n");
1326 ret = -1;
1327 }
1328
1329 return ret;
1330 }
1331
1332 int get_user_id_list(int32_t *p_uid_list, int *p_user_cnt, int start_uid)
1333 {
1334 int left;
1335 int right;
1336 int mid;
1337 int ret = 0;
1338 int i;
1339
1340 if (p_uid_list == NULL || p_user_cnt == NULL)
1341 {
1342 log_error("NULL pointer error\n");
1343 return -1;
1344 }
1345
1346 // acquire lock of user list
1347 if (user_list_rd_lock(p_user_list_pool->semid) < 0)
1348 {
1349 log_error("user_list_rd_lock() error\n");
1350 return -2;
1351 }
1352
1353 left = 0;
1354 right = p_user_list_pool->p_current->user_count - 1;
1355
1356 while (left < right)
1357 {
1358 mid = (left + right) / 2;
1359 if (start_uid < p_user_list_pool->p_current->index_uid[mid].uid)
1360 {
1361 right = mid - 1;
1362 }
1363 else if (start_uid > p_user_list_pool->p_current->index_uid[mid].uid)
1364 {
1365 left = mid + 1;
1366 }
1367 else // if (start_uid == p_user_list_pool->p_current->index_uid[mid].uid)
1368 {
1369 left = mid;
1370 break;
1371 }
1372 }
1373
1374 for (i = 0; i < *p_user_cnt && left + i < p_user_list_pool->p_current->user_count; i++)
1375 {
1376 p_uid_list[i] = p_user_list_pool->p_current->index_uid[left + i].uid;
1377 }
1378 *p_user_cnt = i;
1379
1380 // release lock of user list
1381 if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
1382 {
1383 log_error("user_list_rd_unlock() error\n");
1384 ret = -1;
1385 }
1386
1387 return ret;
1388 }
1389
1390 int user_stat_update(void)
1391 {
1392 return user_stat_map_update(&(p_user_list_pool->user_stat_map));
1393 }
1394
1395 int user_article_cnt_inc(int32_t uid, int n)
1396 {
1397 return user_stat_article_cnt_inc(&(p_user_list_pool->user_stat_map), uid, n);
1398 }
1399
1400 int get_user_article_cnt(int32_t uid)
1401 {
1402 const USER_STAT *p_stat;
1403 int ret;
1404
1405 ret = user_stat_get(&(p_user_list_pool->user_stat_map), uid, &p_stat);
1406 if (ret < 0)
1407 {
1408 log_error("user_stat_get(uid=%d) error: %d\n", uid, ret);
1409 return -1;
1410 }
1411 else if (ret == 0) // user not found
1412 {
1413 return -1;
1414 }
1415
1416 return p_stat->article_count;
1417 }

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