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


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

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