/[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.33 - (show annotations)
Sun Nov 16 00:10:48 2025 UTC (4 months ago) by sysadm
Branch: MAIN
Changes since 1.32: +3 -4 lines
Content type: text/x-csrc
Refine ceiling of integer division

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

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