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


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

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