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


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

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