/[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.31 - (show annotations)
Sun Nov 9 06:44:02 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.30: +1 -1 lines
Content type: text/x-csrc
Fix bug

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

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