/[LeafOK_CVS]/lbbs/src/user_list.c
ViewVC logotype

Annotation of /lbbs/src/user_list.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.23 - (hide annotations)
Sat Nov 1 10:35:42 2025 UTC (4 months, 2 weeks ago) by sysadm
Branch: MAIN
Changes since 1.22: +77 -7 lines
Content type: text/x-csrc
Get online user/guest count from user_list instead of database

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

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