/[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.9 by sysadm, Wed Oct 22 07:39:02 2025 UTC Revision 1.46 by sysadm, Thu Dec 18 02:56:01 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            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    #ifndef HAVE_SYSTEM_V
57            sem_t sem;
58            uint16_t read_lock_count;
59            uint16_t write_lock_count;
60    #else
61          int semid;          int semid;
62    #endif
63          USER_LIST user_list[2];          USER_LIST user_list[2];
64          USER_LIST *p_current;          int user_list_index_current;
65          USER_LIST *p_new;          int user_list_index_new;
66          USER_ONLINE_LIST user_online_list[2];          USER_ONLINE_LIST user_online_list[2];
67          USER_ONLINE_LIST *p_online_current;          int user_online_list_index_current;
68          USER_ONLINE_LIST *p_online_new;          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;  static TRIE_NODE *p_trie_action_dict = NULL;
77    
# Line 75  const USER_ACTION_MAP user_action_map[] Line 91  const USER_ACTION_MAP user_action_map[]
91                  {"MENU", "菜单选择"},                  {"MENU", "菜单选择"},
92                  {"POST_ARTICLE", "撰写文章"},                  {"POST_ARTICLE", "撰写文章"},
93                  {"REPLY_ARTICLE", "回复文章"},                  {"REPLY_ARTICLE", "回复文章"},
94                    {"TOP10_MENU", "十大热门"},
95                  {"USER_LIST", "查花名册"},                  {"USER_LIST", "查花名册"},
96                  {"USER_ONLINE", "环顾四周"},                  {"USER_ONLINE", "环顾四周"},
97                  {"VIEW_ARTICLE", "阅读文章"},                  {"VIEW_ARTICLE", "阅读文章"},
# Line 83  const USER_ACTION_MAP user_action_map[] Line 100  const USER_ACTION_MAP user_action_map[]
100    
101  const int user_action_map_size = sizeof(user_action_map) / sizeof(USER_ACTION_MAP);  const int user_action_map_size = sizeof(user_action_map) / sizeof(USER_ACTION_MAP);
102    
103  static int user_list_try_rd_lock(int semid, int wait_sec);  static int user_list_try_rd_lock(int wait_sec);
104  static int user_list_try_rw_lock(int semid, int wait_sec);  static int user_list_try_rw_lock(int wait_sec);
105  static int user_list_rd_unlock(int semid);  static int user_list_rd_unlock(void);
106  static int user_list_rw_unlock(int semid);  static int user_list_rw_unlock(void);
107  static int user_list_rd_lock(int semid);  static int user_list_rd_lock(void);
108  static int user_list_rw_lock(int semid);  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);  static int user_list_load(MYSQL *db, USER_LIST *p_list);
114  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);
115    static int user_login_count_load(MYSQL *db);
116    
117  static int user_info_index_uid_comp(const void *ptr1, const void *ptr2)  static int user_info_index_uid_comp(const void *ptr1, const void *ptr2)
118  {  {
# Line 106  static int user_info_index_uid_comp(cons Line 127  static int user_info_index_uid_comp(cons
127          {          {
128                  return 1;                  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;          return 0;
139  }  }
140    
# Line 116  int user_list_load(MYSQL *db, USER_LIST Line 145  int user_list_load(MYSQL *db, USER_LIST
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            int32_t last_uid;
150            size_t intro_buf_offset;
151            size_t intro_len;
152    
153          if (db == NULL || p_list == NULL)          if (db == NULL || p_list == NULL)
154          {          {
# Line 123  int user_list_load(MYSQL *db, USER_LIST Line 156  int user_list_load(MYSQL *db, USER_LIST
156                  return -1;                  return -1;
157          }          }
158    
159            if (p_list->user_count > 0)
160            {
161                    last_uid = p_list->users[p_list->user_count - 1].uid;
162            }
163            else
164            {
165                    last_uid = -1;
166            }
167    
168          snprintf(sql, sizeof(sql),          snprintf(sql, sizeof(sql),
169                           "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, "
170                           "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), "
171                             "UNIX_TIMESTAMP(birthday), `introduction` "
172                           "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 "
173                           "INNER JOIN user_reginfo ON user_list.UID = user_reginfo.UID "                           "INNER JOIN user_reginfo ON user_list.UID = user_reginfo.UID "
174                           "WHERE enable ORDER BY username");                           "WHERE enable ORDER BY username");
# Line 144  int user_list_load(MYSQL *db, USER_LIST Line 187  int user_list_load(MYSQL *db, USER_LIST
187                  goto cleanup;                  goto cleanup;
188          }          }
189    
190            intro_buf_offset = 0;
191          i = 0;          i = 0;
192          while ((row = mysql_fetch_row(rs)))          while ((row = mysql_fetch_row(rs)))
193          {          {
# Line 158  int user_list_load(MYSQL *db, USER_LIST Line 202  int user_list_load(MYSQL *db, USER_LIST
202                  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]));
203                  p_list->users[i].life = (row[5] == NULL ? 0 : atoi(row[5]));                  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]));                  p_list->users[i].exp = (row[6] == NULL ? 0 : atoi(row[6]));
205                  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]));
206                  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]));
207                  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]));
208                    p_list->users[i].last_logout_dt = (row[10] == NULL ? 0 : atol(row[10]));
209                  // index                  p_list->users[i].birthday = (row[11] == NULL ? 0 : atol(row[11]));
210                  p_list->index_uid[i].uid = p_list->users[i].uid;                  intro_len = strlen((row[12] == NULL ? "" : row[12]));
211                  p_list->index_uid[i].id = i;                  if (intro_len >= sizeof(p_list->user_intro_buf) - 1 - intro_buf_offset)
212                    {
213                            log_error("OOM for user introduction: len=%d, i=%d\n", intro_len, i);
214                            break;
215                    }
216                    memcpy(p_list->user_intro_buf + intro_buf_offset,
217                               (row[12] == NULL ? "" : row[12]),
218                               intro_len + 1);
219                    p_list->users[i].intro = p_list->user_intro_buf + intro_buf_offset;
220                    intro_buf_offset += (intro_len + 1);
221    
222                  i++;                  i++;
223                  if (i >= BBS_max_user_count)                  if (i >= BBS_max_user_count)
# Line 176  int user_list_load(MYSQL *db, USER_LIST Line 229  int user_list_load(MYSQL *db, USER_LIST
229          mysql_free_result(rs);          mysql_free_result(rs);
230          rs = NULL;          rs = NULL;
231    
232          p_list->user_count = i;          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          // Sort index                  qsort(p_list->index_uid, (size_t)i, sizeof(USER_INFO_INDEX_UID), user_info_index_uid_comp);
         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\n", 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\n", p_list->user_count);
249    
250  cleanup:  cleanup:
251          mysql_free_result(rs);          mysql_free_result(rs);
# Line 191  cleanup: Line 253  cleanup:
253          return ret;          return ret;
254  }  }
255    
256  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)
257  {  {
258          MYSQL_RES *rs = NULL;          MYSQL_RES *rs = NULL;
259          MYSQL_ROW row;          MYSQL_ROW row;
260          char sql[SQL_BUFFER_LEN];          char sql[SQL_BUFFER_LEN];
261          int ret = 0;          int ret = 0;
262          int i;          int i;
263            int j;
264            int user_cnt;
265            int guest_cnt;
266    
267          if (db == NULL || p_list == NULL)          if (db == NULL || p_online_list == NULL)
268          {          {
269                  log_error("NULL pointer error\n");                  log_error("NULL pointer error\n");
270                  return -1;                  return -1;
# Line 208  int user_online_list_load(MYSQL *db, USE Line 273  int user_online_list_load(MYSQL *db, USE
273          snprintf(sql, sizeof(sql),          snprintf(sql, sizeof(sql),
274                           "SELECT SID, UID, ip, current_action, UNIX_TIMESTAMP(login_tm), "                           "SELECT SID, UID, ip, current_action, UNIX_TIMESTAMP(login_tm), "
275                           "UNIX_TIMESTAMP(last_tm) FROM user_online "                           "UNIX_TIMESTAMP(last_tm) FROM user_online "
276                           "WHERE last_tm >= SUBDATE(NOW(), INTERVAL %d SECOND) AND UID <> 0 "                           "WHERE last_tm >= SUBDATE(NOW(), INTERVAL %d SECOND) "
277                           "ORDER BY last_tm DESC",                           "ORDER BY last_tm DESC",
278                           BBS_user_off_line);                           BBS_user_off_line);
279    
# Line 227  int user_online_list_load(MYSQL *db, USE Line 292  int user_online_list_load(MYSQL *db, USE
292          }          }
293    
294          i = 0;          i = 0;
295            user_cnt = 0;
296            guest_cnt = 0;
297          while ((row = mysql_fetch_row(rs)))          while ((row = mysql_fetch_row(rs)))
298          {          {
299                  p_list->users[i].id = i;                  if (atoi(row[1]) == 0) // guest
                 strncpy(p_list->users[i].session_id, row[0], sizeof(p_list->users[i].session_id) - 1);  
                 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_list->users[i].user_info))) < 0)  
300                  {                  {
301                          log_error("query_user_info(%d) error\n", atoi(row[1]));                          guest_cnt++;
302                          continue;                          continue;
303                  }                  }
304                  else if (ret == 0) // skip Guest                  else
305                  {                  {
306                            user_cnt++;
307                    }
308    
309                    p_online_list->users[i].id = i;
310                    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    
313                    if ((ret = query_user_info_by_uid(atoi(row[1]), &(p_online_list->users[i].user_info), NULL, 0)) <= 0)
314                    {
315                            log_error("query_user_info_by_uid(%d) error\n", atoi(row[1]));
316                          continue;                          continue;
317                  }                  }
318    
319                  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);
320                  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';
321    
322                  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);
323                  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';
324                  p_list->users[i].current_action_title = NULL;                  p_online_list->users[i].current_action_title = NULL;
325                  if (p_list->users[i].current_action[0] == '\0')                  if (p_online_list->users[i].current_action[0] == '\0')
326                  {                  {
327                          p_list->users[i].current_action_title = "";                          p_online_list->users[i].current_action_title = "";
328                  }                  }
329                  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)
330                  {                  {
331                          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",
332                                            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);
333                          continue;                          continue;
334                  }                  }
335    
336                  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]));
337                  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]));
338    
339                  i++;                  i++;
340                  if (i >= BBS_max_user_online_count)                  if (i >= BBS_max_user_online_count)
# Line 273  int user_online_list_load(MYSQL *db, USE Line 346  int user_online_list_load(MYSQL *db, USE
346          mysql_free_result(rs);          mysql_free_result(rs);
347          rs = NULL;          rs = NULL;
348    
349          p_list->user_count = i;          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  #ifdef _DEBUG                  qsort(p_online_list->index_uid, (size_t)user_cnt, sizeof(USER_INFO_INDEX_UID), user_info_index_uid_comp);
359          log_error("Loaded %d users\n", p_list->user_count);          }
360  #endif  
361            p_online_list->user_count = user_cnt;
362            p_online_list->guest_count = guest_cnt;
363    
364  cleanup:  cleanup:
365          mysql_free_result(rs);          mysql_free_result(rs);
# Line 285  cleanup: Line 367  cleanup:
367          return ret;          return ret;
368  }  }
369    
370  int user_list_pool_init(void)  int user_login_count_load(MYSQL *db)
371  {  {
372          int shmid;          MYSQL_RES *rs = NULL;
373          int semid;          MYSQL_ROW row;
374          int proj_id;          char sql[SQL_BUFFER_LEN];
375          key_t key;  
376            if (db == NULL)
377            {
378                    log_error("NULL pointer error\n");
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\n", 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\n");
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;
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;          size_t size;
408          void *p_shm;          void *p_shm;
409    #ifdef HAVE_SYSTEM_V
410            int proj_id;
411            key_t key;
412            int semid;
413          union semun arg;          union semun arg;
414    #endif
415          int i;          int i;
416    
417          if (p_user_list_pool != NULL || p_trie_action_dict != NULL)          if (p_user_list_pool != NULL || p_trie_action_dict != NULL)
# Line 318  int user_list_pool_init(void) Line 436  int user_list_pool_init(void)
436          }          }
437    
438          // Allocate shared memory          // Allocate shared memory
439          proj_id = (int)(time(NULL) % getpid());          size = sizeof(USER_LIST_POOL);
440          key = ftok(VAR_USER_LIST_SHM, proj_id);  
441          if (key == -1)          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("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);
448                  return -2;                  return -2;
449          }          }
450    
451          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)  
452          {          {
453                  log_error("shmget(size = %d) error (%d)\n", size, errno);                  log_error("shm_open(%s) error (%d)\n", user_list_shm_name, errno);
454                  return -3;                  return -2;
455          }          }
456          p_shm = shmat(shmid, NULL, 0);          if (ftruncate(fd, (off_t)size) == -1)
         if (p_shm == (void *)-1)  
457          {          {
458                  log_error("shmat(shmid=%d) error (%d)\n", shmid, errno);                  log_error("ftruncate(size=%d) error (%d)\n", size, errno);
459                  return -3;                  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)\n", errno);
467                    close(fd);
468                    return -2;
469            }
470    
471            if (close(fd) < 0)
472            {
473                    log_error("close(fd) error (%d)\n", errno);
474                    return -1;
475          }          }
476    
477          p_user_list_pool = p_shm;          p_user_list_pool = p_shm;
478          p_user_list_pool->shmid = shmid;          p_user_list_pool->shm_size = size;
479    
480          // Allocate semaphore as user list pool lock          // 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)\n", 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)\n", filename, proj_id, errno);
496                    return -2;
497            }
498    
499          size = 2; // r_sem and w_sem          size = 2; // r_sem and w_sem
500          semid = semget(key, (int)size, IPC_CREAT | IPC_EXCL | 0600);          semid = semget(key, (int)size, IPC_CREAT | IPC_EXCL | 0600);
501          if (semid == -1)          if (semid == -1)
# Line 364  int user_list_pool_init(void) Line 516  int user_list_pool_init(void)
516          }          }
517    
518          p_user_list_pool->semid = semid;          p_user_list_pool->semid = semid;
519    #endif
520    
521          // Set user counts to 0          // Set user counts to 0
522          p_user_list_pool->user_list[0].user_count = 0;          p_user_list_pool->user_list[0].user_count = 0;
523          p_user_list_pool->user_list[1].user_count = 0;          p_user_list_pool->user_list[1].user_count = 0;
524    
525          p_user_list_pool->p_current = &(p_user_list_pool->user_list[0]);          p_user_list_pool->user_list_index_current = 0;
526          p_user_list_pool->p_new = &(p_user_list_pool->user_list[1]);          p_user_list_pool->user_list_index_new = 1;
527    
528          p_user_list_pool->p_online_current = &(p_user_list_pool->user_online_list[0]);          p_user_list_pool->user_online_list_index_current = 0;
529          p_user_list_pool->p_online_new = &(p_user_list_pool->user_online_list[1]);          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;          return 0;
534  }  }
535    
536  void user_list_pool_cleanup(void)  void user_list_pool_cleanup(void)
537  {  {
         int shmid;  
   
538          if (p_user_list_pool == NULL)          if (p_user_list_pool == NULL)
539          {          {
540                  return;                  return;
541          }          }
542    
543          shmid = p_user_list_pool->shmid;  #ifdef HAVE_SYSTEM_V
   
544          if (semctl(p_user_list_pool->semid, 0, IPC_RMID) == -1)          if (semctl(p_user_list_pool->semid, 0, IPC_RMID) == -1)
545          {          {
546                  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);
547          }          }
548    #else
549          if (shmdt(p_user_list_pool) == -1)          if (sem_destroy(&(p_user_list_pool->sem)) == -1)
550          {          {
551                  log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);                  log_error("sem_destroy() error (%d)\n", errno);
552          }          }
553    #endif
554    
555            detach_user_list_pool_shm();
556    
557          if (shmctl(shmid, IPC_RMID, NULL) == -1)          if (shm_unlink(user_list_shm_name) == -1 && errno != ENOENT)
558          {          {
559                  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);
560          }          }
561    
562          p_user_list_pool = NULL;          user_list_shm_name[0] = '\0';
563    
564          if (p_trie_action_dict != NULL)          if (p_trie_action_dict != NULL)
565          {          {
# Line 416  void user_list_pool_cleanup(void) Line 571  void user_list_pool_cleanup(void)
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)
         void *p_shm;  
   
         if (p_user_list_pool == NULL)  
575          {          {
576                  log_error("p_user_list_pool not initialized\n");                  log_error("mprotect() error (%d)\n", errno);
577                  return -1;                  return -1;
578          }          }
579    
         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;  
   
580          return 0;          return 0;
581  }  }
582    
583  int detach_user_list_pool_shm(void)  int detach_user_list_pool_shm(void)
584  {  {
585          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)
586          {          {
587                  log_error("shmdt(user_list_pool) error (%d)\n", errno);                  log_error("munmap() error (%d)\n", errno);
588                  return -1;                  return -1;
589          }          }
590    
# Line 456  int detach_user_list_pool_shm(void) Line 596  int detach_user_list_pool_shm(void)
596  int user_list_pool_reload(int online_user)  int user_list_pool_reload(int online_user)
597  {  {
598          MYSQL *db = NULL;          MYSQL *db = NULL;
599          USER_LIST *p_tmp;          int tmp;
600          USER_ONLINE_LIST *p_online_tmp;          int ret = 0;
601    
602          if (p_user_list_pool == NULL)          if (p_user_list_pool == NULL)
603          {          {
# Line 474  int user_list_pool_reload(int online_use Line 614  int user_list_pool_reload(int online_use
614    
615          if (online_user)          if (online_user)
616          {          {
617                  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)
618                  {                  {
619                          log_error("user_online_list_load() error\n");                          log_error("user_online_list_load() error\n");
620                          return -2;                          ret = -2;
621                            goto cleanup;
622                    }
623    
624                    if (user_login_count_load(db) < 0)
625                    {
626                            log_error("user_login_count_load() error\n");
627                            ret = -2;
628                            goto cleanup;
629                  }                  }
630          }          }
631          else          else
632          {          {
633                  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)
634                  {                  {
635                          log_error("user_list_load() error\n");                          log_error("user_list_load() error\n");
636                          return -2;                          ret = -2;
637                            goto cleanup;
638                  }                  }
639          }          }
640    
641          mysql_close(db);          mysql_close(db);
642            db = NULL;
643    
644          if (user_list_rw_lock(p_user_list_pool->semid) < 0)          if (user_list_rw_lock() < 0)
645          {          {
646                  log_error("user_list_rw_lock() error\n");                  log_error("user_list_rw_lock() error\n");
647                  return -3;                  ret = -3;
648                    goto cleanup;
649          }          }
650    
651          if (online_user)          if (online_user)
652          {          {
653                  // Swap p_online_current and p_online_new                  // Swap p_online_current and p_online_new
654                  p_online_tmp = p_user_list_pool->p_online_current;                  tmp = p_user_list_pool->user_online_list_index_current;
655                  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;
656                  p_user_list_pool->p_online_new = p_online_tmp;                  p_user_list_pool->user_online_list_index_new = tmp;
657          }          }
658          else          else
659          {          {
660                  // Swap p_current and p_new                  // Swap index_current and index_new
661                  p_tmp = p_user_list_pool->p_current;                  tmp = p_user_list_pool->user_list_index_current;
662                  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;
663                  p_user_list_pool->p_new = p_tmp;                  p_user_list_pool->user_list_index_new = tmp;
664          }          }
665    
666          if (user_list_rw_unlock(p_user_list_pool->semid) < 0)          if (user_list_rw_unlock() < 0)
667          {          {
668                  log_error("user_list_rw_unlock() error\n");                  log_error("user_list_rw_unlock() error\n");
669                  return -3;                  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 semid, int wait_sec)  int user_list_try_rd_lock(int wait_sec)
680  {  {
681    #ifdef HAVE_SYSTEM_V
682          struct sembuf sops[2];          struct sembuf sops[2];
683    #endif
684          struct timespec timeout;          struct timespec timeout;
685          int ret;          int ret = 0;
686    
687            if (p_user_list_pool == NULL)
688            {
689                    log_error("p_user_list_pool not initialized\n");
690                    return -1;
691            }
692    
693            timeout.tv_sec = wait_sec;
694            timeout.tv_nsec = 0;
695    
696    #ifdef HAVE_SYSTEM_V
697          sops[0].sem_num = 1; // w_sem          sops[0].sem_num = 1; // w_sem
698          sops[0].sem_op = 0;      // wait until unlocked          sops[0].sem_op = 0;      // wait until unlocked
699          sops[0].sem_flg = 0;          sops[0].sem_flg = 0;
# Line 535  int user_list_try_rd_lock(int semid, int Line 702  int user_list_try_rd_lock(int semid, int
702          sops[1].sem_op = 1;                     // lock          sops[1].sem_op = 1;                     // lock
703          sops[1].sem_flg = SEM_UNDO; // undo on terminate          sops[1].sem_flg = SEM_UNDO; // undo on terminate
704    
705          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);  
706          if (ret == -1 && errno != EAGAIN && errno != EINTR)          if (ret == -1 && errno != EAGAIN && errno != EINTR)
707          {          {
708                  log_error("semtimedop(lock read) error %d\n", errno);                  log_error("semop(lock read) error %d\n", 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\n", 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\n", errno);
733                    return -1;
734            }
735    #endif
736    
737          return ret;          return ret;
738  }  }
739    
740  int user_list_try_rw_lock(int semid, int wait_sec)  int user_list_try_rw_lock(int wait_sec)
741  {  {
742    #ifdef HAVE_SYSTEM_V
743          struct sembuf sops[3];          struct sembuf sops[3];
744    #endif
745          struct timespec timeout;          struct timespec timeout;
746          int ret;          int ret = 0;
747    
748            if (p_user_list_pool == NULL)
749            {
750                    log_error("p_user_list_pool not initialized\n");
751                    return -1;
752            }
753    
754            timeout.tv_sec = wait_sec;
755            timeout.tv_nsec = 0;
756    
757    #ifdef HAVE_SYSTEM_V
758          sops[0].sem_num = 1; // w_sem          sops[0].sem_num = 1; // w_sem
759          sops[0].sem_op = 0;      // wait until unlocked          sops[0].sem_op = 0;      // wait until unlocked
760          sops[0].sem_flg = 0;          sops[0].sem_flg = 0;
# Line 565  int user_list_try_rw_lock(int semid, int Line 767  int user_list_try_rw_lock(int semid, int
767          sops[2].sem_op = 0;      // wait until unlocked          sops[2].sem_op = 0;      // wait until unlocked
768          sops[2].sem_flg = 0;          sops[2].sem_flg = 0;
769    
770          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);  
771          if (ret == -1 && errno != EAGAIN && errno != EINTR)          if (ret == -1 && errno != EAGAIN && errno != EINTR)
772          {          {
773                  log_error("semtimedop(lock write) error %d\n", errno);                  log_error("semop(lock write) error %d\n", 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\n", 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\n", errno);
798                    return -1;
799            }
800    #endif
801    
802          return ret;          return ret;
803  }  }
804    
805  int user_list_rd_unlock(int semid)  int user_list_rd_unlock(void)
806  {  {
807    #ifdef HAVE_SYSTEM_V
808          struct sembuf sops[2];          struct sembuf sops[2];
809          int ret;  #endif
810            int ret = 0;
811    
812            if (p_user_list_pool == NULL)
813            {
814                    log_error("p_user_list_pool not initialized\n");
815                    return -1;
816            }
817    
818    #ifdef HAVE_SYSTEM_V
819          sops[0].sem_num = 0;                                     // r_sem          sops[0].sem_num = 0;                                     // r_sem
820          sops[0].sem_op = -1;                                     // unlock          sops[0].sem_op = -1;                                     // unlock
821          sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait          sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
822    
823          ret = semop(semid, sops, 1);          ret = semop(p_user_list_pool->semid, sops, 1);
824          if (ret == -1 && errno != EAGAIN && errno != EINTR)          if (ret == -1 && errno != EAGAIN && errno != EINTR)
825          {          {
826                  log_error("semop(unlock read) error %d\n", errno);                  log_error("semop(unlock read) error %d\n", 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\n", 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\n");
845            }
846    
847            if (sem_post(&(p_user_list_pool->sem)) == -1)
848            {
849                    log_error("sem_post() error %d\n", errno);
850                    return -1;
851            }
852    #endif
853    
854          return ret;          return ret;
855  }  }
856    
857  int user_list_rw_unlock(int semid)  int user_list_rw_unlock(void)
858  {  {
859    #ifdef HAVE_SYSTEM_V
860          struct sembuf sops[1];          struct sembuf sops[1];
861          int ret;  #endif
862            int ret = 0;
863    
864            if (p_user_list_pool == NULL)
865            {
866                    log_error("p_user_list_pool not initialized\n");
867                    return -1;
868            }
869    
870    #ifdef HAVE_SYSTEM_V
871          sops[0].sem_num = 1;                                     // w_sem          sops[0].sem_num = 1;                                     // w_sem
872          sops[0].sem_op = -1;                                     // unlock          sops[0].sem_op = -1;                                     // unlock
873          sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait          sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
874    
875          ret = semop(semid, sops, 1);          ret = semop(p_user_list_pool->semid, sops, 1);
876          if (ret == -1 && errno != EAGAIN && errno != EINTR)          if (ret == -1 && errno != EAGAIN && errno != EINTR)
877          {          {
878                  log_error("semop(unlock write) error %d\n", errno);                  log_error("semop(unlock write) error %d\n", 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\n", 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\n");
897            }
898    
899            if (sem_post(&(p_user_list_pool->sem)) == -1)
900            {
901                    log_error("sem_post() error %d\n", errno);
902                    return -1;
903            }
904    #endif
905    
906          return ret;          return ret;
907  }  }
908    
909  int user_list_rd_lock(int semid)  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    
915            if (p_user_list_pool == NULL)
916            {
917                    log_error("p_user_list_pool not initialized\n");
918                    return -1;
919            }
920    
921          while (!SYS_server_exit)          while (!SYS_server_exit)
922          {          {
923                  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);
924                  if (ret == 0) // success                  if (ret == 0) // success
925                  {                  {
926                          break;                          break;
# Line 631  int user_list_rd_lock(int semid) Line 931  int user_list_rd_lock(int semid)
931                          if (timer % USER_LIST_TRY_LOCK_TIMES == 0)                          if (timer % USER_LIST_TRY_LOCK_TIMES == 0)
932                          {                          {
933                                  log_error("user_list_try_rd_lock() tried %d times\n", timer);                                  log_error("user_list_try_rd_lock() tried %d times\n", 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\n", 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\n");
941    #endif
942                                            break;
943                                    }
944                          }                          }
945                            usleep(100 * 1000); // 0.1 second
946                  }                  }
947                  else // failed                  else // failed
948                  {                  {
# Line 643  int user_list_rd_lock(int semid) Line 954  int user_list_rd_lock(int semid)
954          return ret;          return ret;
955  }  }
956    
957  int user_list_rw_lock(int semid)  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\n");
966                    return -1;
967            }
968    
969          while (!SYS_server_exit)          while (!SYS_server_exit)
970          {          {
971                  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);
972                  if (ret == 0) // success                  if (ret == 0) // success
973                  {                  {
974                          break;                          break;
# Line 661  int user_list_rw_lock(int semid) Line 979  int user_list_rw_lock(int semid)
979                          if (timer % USER_LIST_TRY_LOCK_TIMES == 0)                          if (timer % USER_LIST_TRY_LOCK_TIMES == 0)
980                          {                          {
981                                  log_error("user_list_try_rw_lock() tried %d times\n", timer);                                  log_error("user_list_try_rw_lock() tried %d times\n", 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\n", 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\n");
989    #endif
990                                            break;
991                                    }
992                          }                          }
993                            usleep(100 * 1000); // 0.1 second
994                  }                  }
995                  else // failed                  else // failed
996                  {                  {
# Line 673  int user_list_rw_lock(int semid) Line 1002  int user_list_rw_lock(int semid)
1002          return ret;          return ret;
1003  }  }
1004    
1005    #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\n");
1011                    return -1;
1012            }
1013    
1014            if (sem_destroy(&(p_user_list_pool->sem)) == -1)
1015            {
1016                    log_error("sem_destroy() error (%d)\n", 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)\n", 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;
# Line 683  int query_user_list(int page_id, USER_IN Line 1038  int query_user_list(int page_id, USER_IN
1038                  return -1;                  return -1;
1039          }          }
1040    
1041            *p_user_count = 0;
1042            *p_page_count = 0;
1043    
1044          // acquire lock of user list          // acquire lock of user list
1045          if (user_list_rd_lock(p_user_list_pool->semid) < 0)          if (user_list_rd_lock() < 0)
1046          {          {
1047                  log_error("user_list_rd_lock() error\n");                  log_error("user_list_rd_lock() error\n");
1048                  return -2;                  return -2;
1049          }          }
1050    
1051          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)
1052          {          {
1053                  // empty list                  // empty list
1054                  ret = 0;                  ret = 0;
1055                  goto cleanup;                  goto cleanup;
1056          }          }
1057    
1058          *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) /
1059                                          (p_user_list_pool->p_current->user_count % BBS_user_limit_per_page == 0 ? 0 : 1);                                          BBS_user_limit_per_page;
1060    
1061          if (page_id < 0 || page_id >= *p_page_count)          if (page_id < 0 || page_id >= *p_page_count)
1062          {          {
# Line 708  int query_user_list(int page_id, USER_IN Line 1066  int query_user_list(int page_id, USER_IN
1066          }          }
1067    
1068          *p_user_count = MIN(BBS_user_limit_per_page,          *p_user_count = MIN(BBS_user_limit_per_page,
1069                                                  p_user_list_pool->p_current->user_count -                                                  p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count -
1070                                                          page_id * BBS_user_limit_per_page);                                                          page_id * BBS_user_limit_per_page);
1071    
1072          memcpy(p_users,          memcpy(p_users,
1073                     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,
1074                     sizeof(USER_INFO) * (size_t)(*p_user_count));                     sizeof(USER_INFO) * (size_t)(*p_user_count));
1075    
1076  cleanup:  cleanup:
1077          // release lock of user list          // release lock of user list
1078          if (user_list_rd_unlock(p_user_list_pool->semid) < 0)          if (user_list_rd_unlock() < 0)
1079          {          {
1080                  log_error("user_list_rd_unlock() error\n");                  log_error("user_list_rd_unlock() error\n");
1081                  ret = -1;                  ret = -1;
# Line 736  int query_user_online_list(int page_id, Line 1094  int query_user_online_list(int page_id,
1094                  return -1;                  return -1;
1095          }          }
1096    
1097            *p_user_count = 0;
1098            *p_page_count = 0;
1099    
1100          // acquire lock of user list          // acquire lock of user list
1101          if (user_list_rd_lock(p_user_list_pool->semid) < 0)          if (user_list_rd_lock() < 0)
1102          {          {
1103                  log_error("user_list_rd_lock() error\n");                  log_error("user_list_rd_lock() error\n");
1104                  return -2;                  return -2;
1105          }          }
1106    
1107          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)
1108          {          {
1109                  // empty list                  // empty list
1110                  ret = 0;                  ret = 0;
1111                  goto cleanup;                  goto cleanup;
1112          }          }
1113    
1114          *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);  
1115    
1116          if (page_id < 0 || page_id >= *p_page_count)          if (page_id < 0 || page_id >= *p_page_count)
1117          {          {
# Line 761  int query_user_online_list(int page_id, Line 1121  int query_user_online_list(int page_id,
1121          }          }
1122    
1123          *p_user_count = MIN(BBS_user_limit_per_page,          *p_user_count = MIN(BBS_user_limit_per_page,
1124                                                  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 -
1125                                                          page_id * BBS_user_limit_per_page);                                                          page_id * BBS_user_limit_per_page);
1126    
1127          memcpy(p_online_users,          memcpy(p_online_users,
1128                     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,
1129                     sizeof(USER_ONLINE_INFO) * (size_t)(*p_user_count));                     sizeof(USER_ONLINE_INFO) * (size_t)(*p_user_count));
1130    
1131  cleanup:  cleanup:
1132          // release lock of user list          // release lock of user list
1133          if (user_list_rd_unlock(p_user_list_pool->semid) < 0)          if (user_list_rd_unlock() < 0)
1134          {          {
1135                  log_error("user_list_rd_unlock() error\n");                  log_error("user_list_rd_unlock() error\n");
1136                  ret = -1;                  ret = -1;
# Line 779  cleanup: Line 1139  cleanup:
1139          return ret;          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\n");
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\n");
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\n");
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\n");
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\n");
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\n");
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\n");
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)  int query_user_info(int32_t id, USER_INFO *p_user)
1211  {  {
1212          int ret = 0;          int ret = 0;
# Line 790  int query_user_info(int32_t id, USER_INF Line 1218  int query_user_info(int32_t id, USER_INF
1218          }          }
1219    
1220          // acquire lock of user list          // acquire lock of user list
1221          if (user_list_rd_lock(p_user_list_pool->semid) < 0)          if (user_list_rd_lock() < 0)
1222          {          {
1223                  log_error("user_list_rd_lock() error\n");                  log_error("user_list_rd_lock() error\n");
1224                  return -2;                  return -2;
1225          }          }
1226    
1227          if (id >= 0 && id < p_user_list_pool->p_current->user_count) // Found          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->p_current->users[id];                  *p_user = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users[id];
1230                  ret = 1;                  ret = 1;
1231          }          }
1232    
1233          // release lock of user list          // release lock of user list
1234          if (user_list_rd_unlock(p_user_list_pool->semid) < 0)          if (user_list_rd_unlock() < 0)
1235          {          {
1236                  log_error("user_list_rd_unlock() error\n");                  log_error("user_list_rd_unlock() error\n");
1237                  ret = -1;                  ret = -1;
# Line 812  int query_user_info(int32_t id, USER_INF Line 1240  int query_user_info(int32_t id, USER_INF
1240          return ret;          return ret;
1241  }  }
1242    
1243  int query_user_info_by_uid(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)
1244  {  {
1245          int left;          int left;
1246          int right;          int right;
# Line 827  int query_user_info_by_uid(int32_t uid, Line 1255  int query_user_info_by_uid(int32_t uid,
1255          }          }
1256    
1257          // acquire lock of user list          // acquire lock of user list
1258          if (user_list_rd_lock(p_user_list_pool->semid) < 0)          if (user_list_rd_lock() < 0)
1259          {          {
1260                  log_error("user_list_rd_lock() error\n");                  log_error("user_list_rd_lock() error\n");
1261                  return -2;                  return -2;
1262          }          }
1263    
1264          left = 0;          left = 0;
1265          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;
1266    
1267          while (left < right)          while (left < right)
1268          {          {
1269                  mid = (left + right) / 2;                  mid = (left + right) / 2;
1270                  if (uid < p_user_list_pool->p_current->index_uid[mid].uid)                  if (uid < p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[mid].uid)
1271                  {                  {
1272                          right = mid;                          right = mid - 1;
1273                  }                  }
1274                  else if (uid > p_user_list_pool->p_current->index_uid[mid].uid)                  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;                          left = mid + 1;
1277                  }                  }
1278                  else // if (uid == p_user_list_pool->p_current->index_uid[mid].uid)                  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;                          left = mid;
1281                          break;                          break;
1282                  }                  }
1283          }          }
1284    
1285          if (uid == p_user_list_pool->p_current->index_uid[left].uid) // Found          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->p_current->index_uid[left].id;                  id = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[left].id;
1288                  if ((ret = query_user_info(id, p_user)) <= 0)                  *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                          log_error("query_user_info(id=%d) error: %d\n", id, ret);                          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                  else          }
1298    
1299            // release lock of user list
1300            if (user_list_rd_unlock() < 0)
1301            {
1302                    log_error("user_list_rd_unlock() error\n");
1303                    ret = -1;
1304            }
1305    
1306            return ret;
1307    }
1308    
1309    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            int left;
1313            int right;
1314            int mid;
1315            int left_save;
1316            int ret = 0;
1317            size_t prefix_len;
1318            int comp;
1319            int i;
1320    
1321            if (username_prefix == NULL || uid_list == NULL || username_list == NULL)
1322            {
1323                    log_error("NULL pointer error\n");
1324                    return -1;
1325            }
1326    
1327            prefix_len = strlen(username_prefix);
1328    
1329            // acquire lock of user list
1330            if (user_list_rd_lock() < 0)
1331            {
1332                    log_error("user_list_rd_lock() error\n");
1333                    return -2;
1334            }
1335    
1336            left = 0;
1337            right = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count - 1;
1338    
1339            while (left < right)
1340            {
1341                    mid = (left + right) / 2;
1342                    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                          ret = 1;                          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\n", 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\n", 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\n", 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:
1427          // release lock of user list          // release lock of user list
1428          if (user_list_rd_unlock(p_user_list_pool->semid) < 0)          if (user_list_rd_unlock() < 0)
1429          {          {
1430                  log_error("user_list_rd_unlock() error\n");                  log_error("user_list_rd_unlock() error\n");
1431                  ret = -1;                  ret = -1;
# Line 888  int query_user_online_info(int32_t id, U Line 1445  int query_user_online_info(int32_t id, U
1445          }          }
1446    
1447          // acquire lock of user list          // acquire lock of user list
1448          if (user_list_rd_lock(p_user_list_pool->semid) < 0)          if (user_list_rd_lock() < 0)
1449          {          {
1450                  log_error("user_list_rd_lock() error\n");                  log_error("user_list_rd_lock() error\n");
1451                  return -2;                  return -2;
1452          }          }
1453    
1454          if (id >= 0 && id < p_user_list_pool->p_online_current->user_count) // Found          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->p_online_current->users[id];                  *p_user = p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].users[id];
1457                  ret = 1;                  ret = 1;
1458          }          }
1459    
1460          // release lock of user list          // release lock of user list
1461          if (user_list_rd_unlock(p_user_list_pool->semid) < 0)          if (user_list_rd_unlock() < 0)
1462          {          {
1463                  log_error("user_list_rd_unlock() error\n");                  log_error("user_list_rd_unlock() error\n");
1464                  ret = -1;                  ret = -1;
# Line 909  int query_user_online_info(int32_t id, U Line 1466  int query_user_online_info(int32_t id, U
1466    
1467          return ret;          return ret;
1468  }  }
1469    
1470    int query_user_online_info_by_uid(int32_t uid, USER_ONLINE_INFO *p_users, int *p_user_cnt, int start_id)
1471    {
1472            int left;
1473            int right;
1474            int mid;
1475            int32_t id;
1476            int ret = 0;
1477            int i;
1478            int user_cnt;
1479    
1480            if (p_users == NULL || p_user_cnt == NULL)
1481            {
1482                    log_error("NULL pointer error\n");
1483                    return -1;
1484            }
1485    
1486            user_cnt = *p_user_cnt;
1487            *p_user_cnt = 0;
1488    
1489            // acquire lock of user list
1490            if (user_list_rd_lock() < 0)
1491            {
1492                    log_error("user_list_rd_lock() error\n");
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\n");
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\n");
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\n");
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\n");
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\n", uid, ret);
1638                    return -1;
1639            }
1640            else if (ret == 0) // user not found
1641            {
1642                    return -1;
1643            }
1644    
1645            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