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

Diff of /lbbs/src/user_list.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

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


Legend:
Removed lines/characters  
Changed lines/characters
  Added lines/characters

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