/[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.29 - (show annotations)
Wed Nov 5 04:19:21 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.28: +5 -2 lines
Content type: text/x-csrc
Use enum / const int instead of macro define constant integers
Use const char * instead of macro define for constant strings

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

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