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


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

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