/[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.27 - (show annotations)
Tue Nov 4 14:30:44 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.26: +1 -1 lines
Content type: text/x-csrc
Call shmctl() on valid shmid only

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

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