/[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.25 - (show annotations)
Sun Nov 2 14:38:53 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.24: +165 -24 lines
Content type: text/x-csrc
Add user search in user list display
Fix bug: memory of user introduction pointed by USER_INFO.intro might be overwritten during user_list_pool_reload(),
         use external buffer passed into query_user_info_by_uid() to prevent this issue.

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

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