/[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.12 by sysadm, Wed Oct 22 14:25:15 2025 UTC
# Line 1  Line 1 
1  /***************************************************************************  /***************************************************************************
2                           user_list.c  -  description                                                   user_list.c  -  description
3                               -------------------                                                           -------------------
4      Copyright            : (C) 2004-2025 by Leaflet          Copyright            : (C) 2004-2025 by Leaflet
5      Email                : leaflet@leafok.com          Email                : leaflet@leafok.com
6   ***************************************************************************/   ***************************************************************************/
7    
8  /***************************************************************************  /***************************************************************************
# Line 17  Line 17 
17  #include "common.h"  #include "common.h"
18  #include "database.h"  #include "database.h"
19  #include "log.h"  #include "log.h"
20    #include "trie_dict.h"
21  #include "user_list.h"  #include "user_list.h"
22  #include <errno.h>  #include <errno.h>
23  #include <stdlib.h>  #include <stdlib.h>
# Line 31  Line 32 
32  #ifdef _SEM_SEMUN_UNDEFINED  #ifdef _SEM_SEMUN_UNDEFINED
33  union semun  union semun
34  {  {
35      int val;               /* Value for SETVAL */          int val;                           /* Value for SETVAL */
36      struct semid_ds *buf;  /* Buffer for IPC_STAT, IPC_SET */          struct semid_ds *buf;  /* Buffer for IPC_STAT, IPC_SET */
37      unsigned short *array; /* Array for GETALL, SETALL */          unsigned short *array; /* Array for GETALL, SETALL */
38      struct seminfo *__buf; /* Buffer for IPC_INFO          struct seminfo *__buf; /* Buffer for IPC_INFO
39                                (Linux-specific) */                                                            (Linux-specific) */
40  };  };
41  #endif // #ifdef _SEM_SEMUN_UNDEFINED  #endif // #ifdef _SEM_SEMUN_UNDEFINED
42    
# Line 44  union semun Line 45  union semun
45    
46  struct user_list_pool_t  struct user_list_pool_t
47  {  {
48      int shmid;          int shmid;
49      int semid;          int semid;
50      USER_LIST user_list[2];          USER_LIST user_list[2];
51      USER_LIST *p_current;          USER_LIST *p_current;
52      USER_LIST *p_new;          USER_LIST *p_new;
53            USER_ONLINE_LIST user_online_list[2];
54            USER_ONLINE_LIST *p_online_current;
55            USER_ONLINE_LIST *p_online_new;
56  };  };
57  typedef struct user_list_pool_t USER_LIST_POOL;  typedef struct user_list_pool_t USER_LIST_POOL;
58    
59  static USER_LIST_POOL *p_user_list_pool = NULL;  static USER_LIST_POOL *p_user_list_pool = NULL;
60    static TRIE_NODE *p_trie_action_dict = NULL;
61    
62    typedef struct user_action_map_t
63    {
64            char name[BBS_current_action_max_len + 1];
65            char title[BBS_current_action_max_len + 1];
66    } USER_ACTION_MAP;
67    
68    const USER_ACTION_MAP user_action_map[] =
69            {
70                    {"ARTICLE_FAVOR", "浏览收藏"},
71                    {"BBS_NET", "站点穿梭"},
72                    {"CHICKEN", "电子小鸡"},
73                    {"EDIT_ARTICLE", "修改文章"},
74                    {"LOGIN", "进入大厅"},
75                    {"MENU", "菜单选择"},
76                    {"POST_ARTICLE", "撰写文章"},
77                    {"REPLY_ARTICLE", "回复文章"},
78                    {"USER_LIST", "查花名册"},
79                    {"USER_ONLINE", "环顾四周"},
80                    {"VIEW_ARTICLE", "阅读文章"},
81                    {"VIEW_FILE", "查看文档"},
82                    {"WWW", "Web浏览"}};
83    
84    const int user_action_map_size = sizeof(user_action_map) / sizeof(USER_ACTION_MAP);
85    
86  static int user_list_try_rd_lock(int semid, int wait_sec);  static int user_list_try_rd_lock(int semid, int wait_sec);
87  static int user_list_try_rw_lock(int semid, int wait_sec);  static int user_list_try_rw_lock(int semid, int wait_sec);
# Line 61  static int user_list_rw_unlock(int semid Line 90  static int user_list_rw_unlock(int semid
90  static int user_list_rd_lock(int semid);  static int user_list_rd_lock(int semid);
91  static int user_list_rw_lock(int semid);  static int user_list_rw_lock(int semid);
92    
93    static int user_list_load(MYSQL *db, USER_LIST *p_list);
94    static int user_online_list_load(MYSQL *db, USER_ONLINE_LIST *p_list);
95    
96    static int user_info_index_uid_comp(const void *ptr1, const void *ptr2)
97    {
98            const USER_INFO_INDEX_UID *p1 = ptr1;
99            const USER_INFO_INDEX_UID *p2 = ptr2;
100    
101            if (p1->uid < p2->uid)
102            {
103                    return -1;
104            }
105            else if (p1->uid > p2->uid)
106            {
107                    return 1;
108            }
109            else if (p1->id < p2->id)
110            {
111                    return -1;
112            }
113            else if (p1->id > p2->id)
114            {
115                    return 1;
116            }
117            return 0;
118    }
119    
120  int user_list_load(MYSQL *db, USER_LIST *p_list)  int user_list_load(MYSQL *db, USER_LIST *p_list)
121  {  {
122      MYSQL_RES *rs = NULL;          USER_INFO_INDEX_UID index_uid[BBS_max_user_count];
123      MYSQL_ROW row;          MYSQL_RES *rs = NULL;
124      char sql[SQL_BUFFER_LEN];          MYSQL_ROW row;
125      int ret = 0;          char sql[SQL_BUFFER_LEN];
126      int i;          int ret = 0;
127            int i;
128      if (db == NULL || p_list == NULL)          int32_t last_uid = -1;
129      {  
130          log_error("NULL pointer error\n");          if (db == NULL || p_list == NULL)
131          return -1;          {
132      }                  log_error("NULL pointer error\n");
133                    return -1;
134      snprintf(sql, sizeof(sql),          }
135               "SELECT user_list.UID AS UID, username, nickname, gender, gender_pub, life, exp, "  
136               "UNIX_TIMESTAMP(signup_dt), UNIX_TIMESTAMP(last_login_dt), UNIX_TIMESTAMP(birthday) "          if (p_list->user_count > 0)
137               "FROM user_list INNER JOIN user_pubinfo ON user_list.UID = user_pubinfo.UID "          {
138               "INNER JOIN user_reginfo ON user_list.UID = user_reginfo.UID "                  last_uid = p_list->users[p_list->user_count - 1].uid;
139               "WHERE enable ORDER BY UID");          }
140    
141      if (mysql_query(db, sql) != 0)          snprintf(sql, sizeof(sql),
142      {                           "SELECT user_list.UID AS UID, username, nickname, gender, gender_pub, life, exp, "
143          log_error("Query user info error: %s\n", mysql_error(db));                           "UNIX_TIMESTAMP(signup_dt), UNIX_TIMESTAMP(last_login_dt), UNIX_TIMESTAMP(birthday) "
144          ret = -1;                           "FROM user_list INNER JOIN user_pubinfo ON user_list.UID = user_pubinfo.UID "
145          goto cleanup;                           "INNER JOIN user_reginfo ON user_list.UID = user_reginfo.UID "
146      }                           "WHERE enable ORDER BY username");
147    
148      if ((rs = mysql_use_result(db)) == NULL)          if (mysql_query(db, sql) != 0)
149      {          {
150          log_error("Get user info data failed\n");                  log_error("Query user info error: %s\n", mysql_error(db));
151          ret = -1;                  ret = -1;
152          goto cleanup;                  goto cleanup;
153      }          }
154    
155      i = 0;          if ((rs = mysql_use_result(db)) == NULL)
156      while ((row = mysql_fetch_row(rs)))          {
157      {                  log_error("Get user info data failed\n");
158          p_list->users[i].uid = atoi(row[0]);                  ret = -1;
159          strncpy(p_list->users[i].username, row[1], sizeof(p_list->users[i].username) - 1);                  goto cleanup;
160          p_list->users[i].username[sizeof(p_list->users[i].username) - 1] = '\0';          }
161          strncpy(p_list->users[i].nickname, row[2], sizeof(p_list->users[i].nickname) - 1);  
162          p_list->users[i].nickname[sizeof(p_list->users[i].nickname) - 1] = '\0';          i = 0;
163          p_list->users[i].gender = row[3][0];          while ((row = mysql_fetch_row(rs)))
164          p_list->users[i].gender_pub = (int8_t)(row[4] == NULL ? 0 : atoi(row[4]));          {
165          p_list->users[i].life = (row[5] == NULL ? 0 : atoi(row[5]));                  // record
166          p_list->users[i].exp = (row[6] == NULL ? 0 : atoi(row[6]));                  p_list->users[i].id = i;
167          p_list->users[i].signup_dt = (row[7] == NULL ? 0 : atol(row[7]));                  p_list->users[i].uid = atoi(row[0]);
168          p_list->users[i].last_login_dt = (row[8] == NULL ? 0 : atol(row[8]));                  strncpy(p_list->users[i].username, row[1], sizeof(p_list->users[i].username) - 1);
169          p_list->users[i].birthday = (row[9] == NULL ? 0 : atol(row[9]));                  p_list->users[i].username[sizeof(p_list->users[i].username) - 1] = '\0';
170                    strncpy(p_list->users[i].nickname, row[2], sizeof(p_list->users[i].nickname) - 1);
171          i++;                  p_list->users[i].nickname[sizeof(p_list->users[i].nickname) - 1] = '\0';
172          if (i >= BBS_max_user_count)                  p_list->users[i].gender = row[3][0];
173          {                  p_list->users[i].gender_pub = (int8_t)(row[4] == NULL ? 0 : atoi(row[4]));
174              log_error("Too many users, exceed limit %d\n", BBS_max_user_count);                  p_list->users[i].life = (row[5] == NULL ? 0 : atoi(row[5]));
175              break;                  p_list->users[i].exp = (row[6] == NULL ? 0 : atoi(row[6]));
176          }                  p_list->users[i].signup_dt = (row[7] == NULL ? 0 : atol(row[7]));
177      }                  p_list->users[i].last_login_dt = (row[8] == NULL ? 0 : atol(row[8]));
178      mysql_free_result(rs);                  p_list->users[i].birthday = (row[9] == NULL ? 0 : atol(row[9]));
179      rs = NULL;  
180                    // index
181                    index_uid[i].uid = p_list->users[i].uid;
182                    index_uid[i].id = i;
183    
184                    i++;
185                    if (i >= BBS_max_user_count)
186                    {
187                            log_error("Too many users, exceed limit %d\n", BBS_max_user_count);
188                            break;
189                    }
190            }
191            mysql_free_result(rs);
192            rs = NULL;
193    
194            // Sort index
195            if (i != p_list->user_count || p_list->users[i - 1].uid != last_uid) // Count of users changed
196            {
197                    qsort(index_uid, (size_t)i, sizeof(USER_INFO_INDEX_UID), user_info_index_uid_comp);
198                    memcpy(p_list->index_uid, index_uid, sizeof(USER_INFO_INDEX_UID) * (size_t)i);
199    
200    #ifdef _DEBUG
201                    log_error("Rebuild index of %d users, last_uid=%d\n", i, p_list->users[i - 1].uid);
202    #endif
203            }
204    
205            p_list->user_count = i;
206    
207    #ifdef _DEBUG
208            log_error("Loaded %d users\n", p_list->user_count);
209    #endif
210    
211    cleanup:
212            mysql_free_result(rs);
213    
214            return ret;
215    }
216    
217    int user_online_list_load(MYSQL *db, USER_ONLINE_LIST *p_list)
218    {
219            MYSQL_RES *rs = NULL;
220            MYSQL_ROW row;
221            char sql[SQL_BUFFER_LEN];
222            int ret = 0;
223            int i;
224    
225            if (db == NULL || p_list == NULL)
226            {
227                    log_error("NULL pointer error\n");
228                    return -1;
229            }
230    
231            snprintf(sql, sizeof(sql),
232                             "SELECT SID, UID, ip, current_action, UNIX_TIMESTAMP(login_tm), "
233                             "UNIX_TIMESTAMP(last_tm) FROM user_online "
234                             "WHERE last_tm >= SUBDATE(NOW(), INTERVAL %d SECOND) AND UID <> 0 "
235                             "ORDER BY last_tm DESC",
236                             BBS_user_off_line);
237    
238            if (mysql_query(db, sql) != 0)
239            {
240                    log_error("Query user online error: %s\n", mysql_error(db));
241                    ret = -1;
242                    goto cleanup;
243            }
244    
245            if ((rs = mysql_use_result(db)) == NULL)
246            {
247                    log_error("Get user online data failed\n");
248                    ret = -1;
249                    goto cleanup;
250            }
251    
252            i = 0;
253            while ((row = mysql_fetch_row(rs)))
254            {
255                    p_list->users[i].id = i;
256                    strncpy(p_list->users[i].session_id, row[0], sizeof(p_list->users[i].session_id) - 1);
257                    p_list->users[i].session_id[sizeof(p_list->users[i].session_id) - 1] = '\0';
258    
259                    if ((ret = query_user_info_by_uid(atoi(row[1]), &(p_list->users[i].user_info))) <= 0)
260                    {
261                            log_error("query_user_info_by_uid(%d) error\n", atoi(row[1]));
262                            continue;
263                    }
264    
265                    strncpy(p_list->users[i].ip, row[2], sizeof(p_list->users[i].ip) - 1);
266                    p_list->users[i].ip[sizeof(p_list->users[i].ip) - 1] = '\0';
267    
268                    strncpy(p_list->users[i].current_action, row[3], sizeof(p_list->users[i].current_action) - 1);
269                    p_list->users[i].current_action[sizeof(p_list->users[i].current_action) - 1] = '\0';
270                    p_list->users[i].current_action_title = NULL;
271                    if (p_list->users[i].current_action[0] == '\0')
272                    {
273                            p_list->users[i].current_action_title = "";
274                    }
275                    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)
276                    {
277                            log_error("trie_dict_get(p_trie_action_dict, %s) error on session_id=%s\n",
278                                              p_list->users[i].current_action, p_list->users[i].session_id);
279                            continue;
280                    }
281    
282                    p_list->users[i].login_tm = (row[4] == NULL ? 0 : atol(row[4]));
283                    p_list->users[i].last_tm = (row[5] == NULL ? 0 : atol(row[5]));
284    
285                    // index
286                    p_list->index_uid[i].uid = p_list->users[i].user_info.uid;
287                    p_list->index_uid[i].id = i;
288    
289                    i++;
290                    if (i >= BBS_max_user_online_count)
291                    {
292                            log_error("Too many online users, exceed limit %d\n", BBS_max_user_online_count);
293                            break;
294                    }
295            }
296            mysql_free_result(rs);
297            rs = NULL;
298    
299            // Sort index
300            if (i > 0)
301            {
302                    qsort(p_list->index_uid, (size_t)i, sizeof(USER_INFO_INDEX_UID), user_info_index_uid_comp);
303    #ifdef _DEBUG
304                    log_error("Rebuild index of %d online users\n", i);
305    #endif
306            }
307    
308      p_list->user_count = i;          p_list->user_count = i;
309    
310  #ifdef _DEBUG  #ifdef _DEBUG
311      log_error("Loaded %d users\n", p_list->user_count);          log_error("Loaded %d online users\n", p_list->user_count);
312  #endif  #endif
313    
314  cleanup:  cleanup:
315      mysql_free_result(rs);          mysql_free_result(rs);
316    
317      return ret;          return ret;
318  }  }
319    
320  int user_list_pool_init(void)  int user_list_pool_init(void)
321  {  {
322      int shmid;          int shmid;
323      int semid;          int semid;
324      int proj_id;          int proj_id;
325      key_t key;          key_t key;
326      size_t size;          size_t size;
327      void *p_shm;          void *p_shm;
328      union semun arg;          union semun arg;
329      int i;          int i;
330    
331      if (p_user_list_pool != NULL)          if (p_user_list_pool != NULL || p_trie_action_dict != NULL)
332      {          {
333          log_error("p_user_list_pool already initialized\n");                  log_error("p_user_list_pool already initialized\n");
334          return -1;                  return -1;
335      }          }
336    
337      // Allocate shared memory          p_trie_action_dict = trie_dict_create();
338      proj_id = (int)(time(NULL) % getpid());          if (p_trie_action_dict == NULL)
339      key = ftok(VAR_USER_LIST_SHM, proj_id);          {
340      if (key == -1)                  log_error("trie_dict_create() error\n");
341      {                  return -1;
342          log_error("ftok(%s %d) error (%d)\n", VAR_USER_LIST_SHM, proj_id, errno);          }
343          return -2;  
344      }          for (i = 0; i < user_action_map_size; i++)
345            {
346      size = sizeof(USER_LIST_POOL);                  if (trie_dict_set(p_trie_action_dict, user_action_map[i].name, (int64_t)(user_action_map[i].title)) < 0)
347      shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);                  {
348      if (shmid == -1)                          log_error("trie_dict_set(p_trie_action_dict, %s) error\n", user_action_map[i].name);
349      {                  }
350          log_error("shmget(size = %d) error (%d)\n", size, errno);          }
351          return -3;  
352      }          // Allocate shared memory
353      p_shm = shmat(shmid, NULL, 0);          proj_id = (int)(time(NULL) % getpid());
354      if (p_shm == (void *)-1)          key = ftok(VAR_USER_LIST_SHM, proj_id);
355      {          if (key == -1)
356          log_error("shmat(shmid=%d) error (%d)\n", shmid, errno);          {
357          return -3;                  log_error("ftok(%s %d) error (%d)\n", VAR_USER_LIST_SHM, proj_id, errno);
358      }                  return -2;
359            }
360      p_user_list_pool = p_shm;  
361      p_user_list_pool->shmid = shmid;          size = sizeof(USER_LIST_POOL);
362            shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
363      // Allocate semaphore as user list pool lock          if (shmid == -1)
364      size = 2; // r_sem and w_sem          {
365      semid = semget(key, (int)size, IPC_CREAT | IPC_EXCL | 0600);                  log_error("shmget(size = %d) error (%d)\n", size, errno);
366      if (semid == -1)                  return -3;
367      {          }
368          log_error("semget(user_list_pool_sem, size = %d) error (%d)\n", size, errno);          p_shm = shmat(shmid, NULL, 0);
369          return -3;          if (p_shm == (void *)-1)
370      }          {
371                    log_error("shmat(shmid=%d) error (%d)\n", shmid, errno);
372      // Initialize sem value to 0                  return -3;
373      arg.val = 0;          }
374      for (i = 0; i < size; i++)  
375      {          p_user_list_pool = p_shm;
376          if (semctl(semid, i, SETVAL, arg) == -1)          p_user_list_pool->shmid = shmid;
377          {  
378              log_error("semctl(user_list_pool_sem, SETVAL) error (%d)\n", errno);          // Allocate semaphore as user list pool lock
379              return -3;          size = 2; // r_sem and w_sem
380          }          semid = semget(key, (int)size, IPC_CREAT | IPC_EXCL | 0600);
381      }          if (semid == -1)
382            {
383      p_user_list_pool->semid = semid;                  log_error("semget(user_list_pool_sem, size = %d) error (%d)\n", size, errno);
384                    return -3;
385      // Set user counts to 0          }
386      p_user_list_pool->user_list[0].user_count = 0;  
387      p_user_list_pool->user_list[1].user_count = 0;          // Initialize sem value to 0
388            arg.val = 0;
389            for (i = 0; i < size; i++)
390            {
391                    if (semctl(semid, i, SETVAL, arg) == -1)
392                    {
393                            log_error("semctl(user_list_pool_sem, SETVAL) error (%d)\n", errno);
394                            return -3;
395                    }
396            }
397    
398            p_user_list_pool->semid = semid;
399    
400            // Set user counts to 0
401            p_user_list_pool->user_list[0].user_count = 0;
402            p_user_list_pool->user_list[1].user_count = 0;
403    
404      p_user_list_pool->p_current = &(p_user_list_pool->user_list[0]);          p_user_list_pool->p_current = &(p_user_list_pool->user_list[0]);
405      p_user_list_pool->p_new = &(p_user_list_pool->user_list[1]);          p_user_list_pool->p_new = &(p_user_list_pool->user_list[1]);
406    
407      return 0;          p_user_list_pool->p_online_current = &(p_user_list_pool->user_online_list[0]);
408            p_user_list_pool->p_online_new = &(p_user_list_pool->user_online_list[1]);
409    
410            return 0;
411  }  }
412    
413  void user_list_pool_cleanup(void)  void user_list_pool_cleanup(void)
414  {  {
415      int shmid;          int shmid;
416    
417      if (p_user_list_pool == NULL)          if (p_user_list_pool == NULL)
418      {          {
419          return;                  return;
420      }          }
421    
422      shmid = p_user_list_pool->shmid;          shmid = p_user_list_pool->shmid;
423    
424      if (semctl(p_user_list_pool->semid, 0, IPC_RMID) == -1)          if (semctl(p_user_list_pool->semid, 0, IPC_RMID) == -1)
425      {          {
426          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);
427      }          }
428    
429      if (shmdt(p_user_list_pool) == -1)          if (shmdt(p_user_list_pool) == -1)
430      {          {
431          log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);                  log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
432      }          }
433    
434      if (shmctl(shmid, IPC_RMID, NULL) == -1)          if (shmctl(shmid, IPC_RMID, NULL) == -1)
435      {          {
436          log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", shmid, errno);                  log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", shmid, errno);
437      }          }
438    
439            p_user_list_pool = NULL;
440    
441            if (p_trie_action_dict != NULL)
442            {
443                    trie_dict_destroy(p_trie_action_dict);
444    
445      p_user_list_pool = NULL;                  p_trie_action_dict = NULL;
446            }
447  }  }
448    
449  int set_user_list_pool_shm_readonly(void)  int set_user_list_pool_shm_readonly(void)
450  {  {
451      int shmid;          int shmid;
452      void *p_shm;          void *p_shm;
453    
454      if (p_user_list_pool == NULL)          if (p_user_list_pool == NULL)
455      {          {
456          log_error("p_user_list_pool not initialized\n");                  log_error("p_user_list_pool not initialized\n");
457          return -1;                  return -1;
458      }          }
459    
460      shmid = p_user_list_pool->shmid;          shmid = p_user_list_pool->shmid;
461    
462      // Remap shared memory in read-only mode          // Remap shared memory in read-only mode
463      p_shm = shmat(shmid, p_user_list_pool, SHM_RDONLY | SHM_REMAP);          p_shm = shmat(shmid, p_user_list_pool, SHM_RDONLY | SHM_REMAP);
464      if (p_shm == (void *)-1)          if (p_shm == (void *)-1)
465      {          {
466          log_error("shmat(user_list_pool shmid = %d) error (%d)\n", shmid, errno);                  log_error("shmat(user_list_pool shmid = %d) error (%d)\n", shmid, errno);
467          return -3;                  return -3;
468      }          }
469    
470      p_user_list_pool = p_shm;          p_user_list_pool = p_shm;
471    
472      return 0;          return 0;
473  }  }
474    
475  int detach_user_list_pool_shm(void)  int detach_user_list_pool_shm(void)
476  {  {
477      if (p_user_list_pool != NULL && shmdt(p_user_list_pool) == -1)          if (p_user_list_pool != NULL && shmdt(p_user_list_pool) == -1)
478      {          {
479          log_error("shmdt(user_list_pool) error (%d)\n", errno);                  log_error("shmdt(user_list_pool) error (%d)\n", errno);
480          return -1;                  return -1;
481      }          }
482    
483      p_user_list_pool = NULL;          p_user_list_pool = NULL;
484    
485      return 0;          return 0;
486  }  }
487    
488  int user_list_pool_reload(void)  int user_list_pool_reload(int online_user)
489  {  {
490      MYSQL *db = NULL;          MYSQL *db = NULL;
491      USER_LIST *p_tmp;          USER_LIST *p_tmp;
492            USER_ONLINE_LIST *p_online_tmp;
493      if (p_user_list_pool == NULL)          int ret = 0;
494      {  
495          log_error("p_user_list_pool not initialized\n");          if (p_user_list_pool == NULL)
496          return -1;          {
497      }                  log_error("p_user_list_pool not initialized\n");
498                    return -1;
499      db = db_open();          }
500      if (db == NULL)  
501      {          db = db_open();
502          log_error("db_open() error: %s\n", mysql_error(db));          if (db == NULL)
503          return -1;          {
504      }                  log_error("db_open() error: %s\n", mysql_error(db));
505                    return -1;
506      if (user_list_load(db, p_user_list_pool->p_new) < 0)          }
507      {  
508          log_error("user_list_rw_lock() error\n");          if (online_user)
509          return -2;          {
510      }                  if (user_online_list_load(db, p_user_list_pool->p_online_new) < 0)
511                    {
512      mysql_close(db);                          log_error("user_online_list_load() error\n");
513                            ret = -2;
514      if (user_list_rw_lock(p_user_list_pool->semid) < 0)                          goto cleanup;
515      {                  }
516          log_error("user_list_rw_lock() error\n");          }
517          return -3;          else
518      }          {
519                    if (user_list_load(db, p_user_list_pool->p_new) < 0)
520      // Swap p_current and p_new                  {
521      p_tmp = p_user_list_pool->p_current;                          log_error("user_list_load() error\n");
522      p_user_list_pool->p_current = p_user_list_pool->p_new;                          ret = -2;
523      p_user_list_pool->p_new = p_tmp;                          goto cleanup;
524                    }
525      if (user_list_rw_unlock(p_user_list_pool->semid) < 0)          }
526      {  
527          log_error("user_list_rw_unlock() error\n");          mysql_close(db);
528          return -3;          db = NULL;
529      }  
530            if (user_list_rw_lock(p_user_list_pool->semid) < 0)
531            {
532                    log_error("user_list_rw_lock() error\n");
533                    ret = -3;
534                    goto cleanup;
535            }
536    
537            if (online_user)
538            {
539                    // Swap p_online_current and p_online_new
540                    p_online_tmp = p_user_list_pool->p_online_current;
541                    p_user_list_pool->p_online_current = p_user_list_pool->p_online_new;
542                    p_user_list_pool->p_online_new = p_online_tmp;
543            }
544            else
545            {
546                    // Swap p_current and p_new
547                    p_tmp = p_user_list_pool->p_current;
548                    p_user_list_pool->p_current = p_user_list_pool->p_new;
549                    p_user_list_pool->p_new = p_tmp;
550            }
551    
552            if (user_list_rw_unlock(p_user_list_pool->semid) < 0)
553            {
554                    log_error("user_list_rw_unlock() error\n");
555                    ret = -3;
556                    goto cleanup;
557            }
558    
559    cleanup:
560            mysql_close(db);
561    
562      return 0;          return ret;
563  }  }
564    
565  int user_list_try_rd_lock(int semid, int wait_sec)  int user_list_try_rd_lock(int semid, int wait_sec)
566  {  {
567      struct sembuf sops[2];          struct sembuf sops[2];
568      struct timespec timeout;          struct timespec timeout;
569      int ret;          int ret;
570    
571      sops[0].sem_num = 1; // w_sem          sops[0].sem_num = 1; // w_sem
572      sops[0].sem_op = 0;  // wait until unlocked          sops[0].sem_op = 0;      // wait until unlocked
573      sops[0].sem_flg = 0;          sops[0].sem_flg = 0;
574    
575      sops[1].sem_num = 0;        // r_sem          sops[1].sem_num = 0;            // r_sem
576      sops[1].sem_op = 1;         // lock          sops[1].sem_op = 1;                     // lock
577      sops[1].sem_flg = SEM_UNDO; // undo on terminate          sops[1].sem_flg = SEM_UNDO; // undo on terminate
578    
579      timeout.tv_sec = wait_sec;          timeout.tv_sec = wait_sec;
580      timeout.tv_nsec = 0;          timeout.tv_nsec = 0;
581    
582      ret = semtimedop(semid, sops, 2, &timeout);          ret = semtimedop(semid, sops, 2, &timeout);
583      if (ret == -1 && errno != EAGAIN && errno != EINTR)          if (ret == -1 && errno != EAGAIN && errno != EINTR)
584      {          {
585          log_error("semtimedop(lock read) error %d\n", errno);                  log_error("semtimedop(lock read) error %d\n", errno);
586      }          }
587    
588      return ret;          return ret;
589  }  }
590    
591  int user_list_try_rw_lock(int semid, int wait_sec)  int user_list_try_rw_lock(int semid, int wait_sec)
592  {  {
593      struct sembuf sops[3];          struct sembuf sops[3];
594      struct timespec timeout;          struct timespec timeout;
595      int ret;          int ret;
596    
597      sops[0].sem_num = 1; // w_sem          sops[0].sem_num = 1; // w_sem
598      sops[0].sem_op = 0;  // wait until unlocked          sops[0].sem_op = 0;      // wait until unlocked
599      sops[0].sem_flg = 0;          sops[0].sem_flg = 0;
600    
601      sops[1].sem_num = 1;        // w_sem          sops[1].sem_num = 1;            // w_sem
602      sops[1].sem_op = 1;         // lock          sops[1].sem_op = 1;                     // lock
603      sops[1].sem_flg = SEM_UNDO; // undo on terminate          sops[1].sem_flg = SEM_UNDO; // undo on terminate
604    
605      sops[2].sem_num = 0; // r_sem          sops[2].sem_num = 0; // r_sem
606      sops[2].sem_op = 0;  // wait until unlocked          sops[2].sem_op = 0;      // wait until unlocked
607      sops[2].sem_flg = 0;          sops[2].sem_flg = 0;
608    
609      timeout.tv_sec = wait_sec;          timeout.tv_sec = wait_sec;
610      timeout.tv_nsec = 0;          timeout.tv_nsec = 0;
611    
612      ret = semtimedop(semid, sops, 3, &timeout);          ret = semtimedop(semid, sops, 3, &timeout);
613      if (ret == -1 && errno != EAGAIN && errno != EINTR)          if (ret == -1 && errno != EAGAIN && errno != EINTR)
614      {          {
615          log_error("semtimedop(lock write) error %d\n", errno);                  log_error("semtimedop(lock write) error %d\n", errno);
616      }          }
617    
618      return ret;          return ret;
619  }  }
620    
621  int user_list_rd_unlock(int semid)  int user_list_rd_unlock(int semid)
622  {  {
623      struct sembuf sops[2];          struct sembuf sops[2];
624      int ret;          int ret;
625    
626      sops[0].sem_num = 0;                     // r_sem          sops[0].sem_num = 0;                                     // r_sem
627      sops[0].sem_op = -1;                     // unlock          sops[0].sem_op = -1;                                     // unlock
628      sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait          sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
629    
630      ret = semop(semid, sops, 1);          ret = semop(semid, sops, 1);
631      if (ret == -1 && errno != EAGAIN && errno != EINTR)          if (ret == -1 && errno != EAGAIN && errno != EINTR)
632      {          {
633          log_error("semop(unlock read) error %d\n", errno);                  log_error("semop(unlock read) error %d\n", errno);
634      }          }
635    
636      return ret;          return ret;
637  }  }
638    
639  int user_list_rw_unlock(int semid)  int user_list_rw_unlock(int semid)
640  {  {
641      struct sembuf sops[1];          struct sembuf sops[1];
642      int ret;          int ret;
643    
644      sops[0].sem_num = 1;                     // w_sem          sops[0].sem_num = 1;                                     // w_sem
645      sops[0].sem_op = -1;                     // unlock          sops[0].sem_op = -1;                                     // unlock
646      sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait          sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
647    
648      ret = semop(semid, sops, 1);          ret = semop(semid, sops, 1);
649      if (ret == -1 && errno != EAGAIN && errno != EINTR)          if (ret == -1 && errno != EAGAIN && errno != EINTR)
650      {          {
651          log_error("semop(unlock write) error %d\n", errno);                  log_error("semop(unlock write) error %d\n", errno);
652      }          }
653    
654      return ret;          return ret;
655  }  }
656    
657  int user_list_rd_lock(int semid)  int user_list_rd_lock(int semid)
658  {  {
659      int timer = 0;          int timer = 0;
660      int ret = -1;          int ret = -1;
661    
662      while (!SYS_server_exit)          while (!SYS_server_exit)
663      {          {
664          ret = user_list_try_rd_lock(semid, USER_LIST_TRY_LOCK_WAIT_TIME);                  ret = user_list_try_rd_lock(semid, USER_LIST_TRY_LOCK_WAIT_TIME);
665          if (ret == 0) // success                  if (ret == 0) // success
666          {                  {
667              break;                          break;
668          }                  }
669          else if (errno == EAGAIN || errno == EINTR) // retry                  else if (errno == EAGAIN || errno == EINTR) // retry
670          {                  {
671              timer++;                          timer++;
672              if (timer % USER_LIST_TRY_LOCK_TIMES == 0)                          if (timer % USER_LIST_TRY_LOCK_TIMES == 0)
673              {                          {
674                  log_error("user_list_try_rd_lock() tried %d times\n", timer);                                  log_error("user_list_try_rd_lock() tried %d times\n", timer);
675              }                          }
676          }                  }
677          else // failed                  else // failed
678          {                  {
679              log_error("user_list_try_rd_lock() failed\n");                          log_error("user_list_try_rd_lock() failed\n");
680              break;                          break;
681          }                  }
682      }          }
683    
684      return ret;          return ret;
685  }  }
686    
687  int user_list_rw_lock(int semid)  int user_list_rw_lock(int semid)
688  {  {
689      int timer = 0;          int timer = 0;
690      int ret = -1;          int ret = -1;
691    
692      while (!SYS_server_exit)          while (!SYS_server_exit)
693      {          {
694          ret = user_list_try_rw_lock(semid, USER_LIST_TRY_LOCK_WAIT_TIME);                  ret = user_list_try_rw_lock(semid, USER_LIST_TRY_LOCK_WAIT_TIME);
695          if (ret == 0) // success                  if (ret == 0) // success
696          {                  {
697              break;                          break;
698          }                  }
699          else if (errno == EAGAIN || errno == EINTR) // retry                  else if (errno == EAGAIN || errno == EINTR) // retry
700          {                  {
701              timer++;                          timer++;
702              if (timer % USER_LIST_TRY_LOCK_TIMES == 0)                          if (timer % USER_LIST_TRY_LOCK_TIMES == 0)
703              {                          {
704                  log_error("user_list_try_rw_lock() tried %d times\n", timer);                                  log_error("user_list_try_rw_lock() tried %d times\n", timer);
705              }                          }
706          }                  }
707          else // failed                  else // failed
708          {                  {
709              log_error("user_list_try_rw_lock() failed\n");                          log_error("user_list_try_rw_lock() failed\n");
710              break;                          break;
711          }                  }
712      }          }
713    
714      return ret;          return ret;
715  }  }
716    
717  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)
718  {  {
719      int ret = 0;          int ret = 0;
720    
721            if (p_users == NULL || p_user_count == NULL || p_page_count == NULL)
722            {
723                    log_error("NULL pointer error\n");
724                    return -1;
725            }
726    
727            *p_user_count = 0;
728            *p_page_count = 0;
729    
730            // acquire lock of user list
731            if (user_list_rd_lock(p_user_list_pool->semid) < 0)
732            {
733                    log_error("user_list_rd_lock() error\n");
734                    return -2;
735            }
736    
737            if (p_user_list_pool->p_current->user_count == 0)
738            {
739                    // empty list
740                    ret = 0;
741                    goto cleanup;
742            }
743    
744            *p_page_count = p_user_list_pool->p_current->user_count / BBS_user_limit_per_page +
745                                            (p_user_list_pool->p_current->user_count % BBS_user_limit_per_page == 0 ? 0 : 1);
746    
747            if (page_id < 0 || page_id >= *p_page_count)
748            {
749                    log_error("Invalid page_id = %d, not in range [0, %d)\n", page_id, *p_page_count);
750                    ret = -3;
751                    goto cleanup;
752            }
753    
754            *p_user_count = MIN(BBS_user_limit_per_page,
755                                                    p_user_list_pool->p_current->user_count -
756                                                            page_id * BBS_user_limit_per_page);
757    
758            memcpy(p_users,
759                       p_user_list_pool->p_current->users + page_id * BBS_user_limit_per_page,
760                       sizeof(USER_INFO) * (size_t)(*p_user_count));
761    
762    cleanup:
763            // release lock of user list
764            if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
765            {
766                    log_error("user_list_rd_unlock() error\n");
767                    ret = -1;
768            }
769    
770            return ret;
771    }
772    
773    int query_user_online_list(int page_id, USER_ONLINE_INFO *p_online_users, int *p_user_count, int *p_page_count)
774    {
775            int ret = 0;
776    
777      if (p_users == NULL || p_user_count == NULL || p_page_count == NULL)          if (p_online_users == NULL || p_user_count == NULL || p_page_count == NULL)
778      {          {
779          log_error("NULL pointer error\n");                  log_error("NULL pointer error\n");
780          return -1;                  return -1;
781      }          }
782    
783      // acquire lock of user list          *p_user_count = 0;
784      if (user_list_rd_lock(p_user_list_pool->semid) < 0)          *p_page_count = 0;
785      {  
786          log_error("user_list_rd_lock() error\n");          // acquire lock of user list
787          return -2;          if (user_list_rd_lock(p_user_list_pool->semid) < 0)
788      }          {
789                    log_error("user_list_rd_lock() error\n");
790      if (p_user_list_pool->p_current->user_count == 0)                  return -2;
791      {          }
792          // empty list  
793          ret = 0;          if (p_user_list_pool->p_online_current->user_count == 0)
794          goto cleanup;          {
795      }                  // empty list
796                    ret = 0;
797      *p_page_count = p_user_list_pool->p_current->user_count / BBS_user_limit_per_page +                  goto cleanup;
798                      (p_user_list_pool->p_current->user_count % BBS_user_limit_per_page == 0 ? 0 : 1);          }
799    
800      if (page_id < 0 || page_id >= *p_page_count)          *p_page_count = p_user_list_pool->p_online_current->user_count / BBS_user_limit_per_page +
801      {                                          (p_user_list_pool->p_online_current->user_count % BBS_user_limit_per_page == 0 ? 0 : 1);
802          log_error("Invalid page_id = %d, not in range [0, %d)\n", page_id, *p_page_count);  
803          ret = -3;          if (page_id < 0 || page_id >= *p_page_count)
804          goto cleanup;          {
805      }                  log_error("Invalid page_id = %d, not in range [0, %d)\n", page_id, *p_page_count);
806                    ret = -3;
807      *p_user_count = MIN(BBS_user_limit_per_page,                  goto cleanup;
808                          p_user_list_pool->p_current->user_count -          }
809                              page_id * BBS_user_limit_per_page);  
810            *p_user_count = MIN(BBS_user_limit_per_page,
811      memcpy(p_users,                                                  p_user_list_pool->p_online_current->user_count -
812             p_user_list_pool->p_current->users + page_id * BBS_user_limit_per_page,                                                          page_id * BBS_user_limit_per_page);
813             sizeof(USER_INFO) * (size_t)(*p_user_count));  
814            memcpy(p_online_users,
815                       p_user_list_pool->p_online_current->users + page_id * BBS_user_limit_per_page,
816                       sizeof(USER_ONLINE_INFO) * (size_t)(*p_user_count));
817    
818  cleanup:  cleanup:
819      // release lock of user list          // release lock of user list
820      if (user_list_rd_unlock(p_user_list_pool->semid) < 0)          if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
821      {          {
822          log_error("user_list_rd_unlock() error\n");                  log_error("user_list_rd_unlock() error\n");
823          ret = -1;                  ret = -1;
824      }          }
825    
826      return ret;          return ret;
827  }  }
828    
829  int query_user_info(int32_t uid, USER_INFO *p_user)  int query_user_info(int32_t id, USER_INFO *p_user)
830  {  {
831      int left;          int ret = 0;
832      int right;  
833      int mid;          if (p_user == NULL)
834      int ret = 0;          {
835                    log_error("NULL pointer error\n");
836      if (p_user == NULL)                  return -1;
837      {          }
838          log_error("NULL pointer error\n");  
839          return -1;          // acquire lock of user list
840      }          if (user_list_rd_lock(p_user_list_pool->semid) < 0)
841            {
842      // acquire lock of user list                  log_error("user_list_rd_lock() error\n");
843      if (user_list_rd_lock(p_user_list_pool->semid) < 0)                  return -2;
844      {          }
845          log_error("user_list_rd_lock() error\n");  
846          return -2;          if (id >= 0 && id < p_user_list_pool->p_current->user_count) // Found
847      }          {
848                    *p_user = p_user_list_pool->p_current->users[id];
849      left = 0;                  ret = 1;
850      right = p_user_list_pool->p_current->user_count - 1;          }
851    
852      while (left < right)          // release lock of user list
853      {          if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
854          mid = (left + right) / 2;          {
855          if (uid < p_user_list_pool->p_current->users[mid].uid)                  log_error("user_list_rd_unlock() error\n");
856          {                  ret = -1;
857              right = mid;          }
858          }  
859          else if (uid > p_user_list_pool->p_current->users[mid].uid)          return ret;
860          {  }
861              left = mid + 1;  
862          }  int query_user_info_by_uid(int32_t uid, USER_INFO *p_user)
863          else // if (uid == p_user_list_pool->p_current->users[mid].uid)  {
864          {          int left;
865              left = mid;          int right;
866              break;          int mid;
867          }          int32_t id;
868      }          int ret = 0;
869    
870      if (uid == p_user_list_pool->p_current->users[left].uid) // Found          if (p_user == NULL)
871      {          {
872          *p_user = p_user_list_pool->p_current->users[left];                  log_error("NULL pointer error\n");
873          ret = 1;                  return -1;
874      }          }
875    
876      // release lock of user list          // acquire lock of user list
877      if (user_list_rd_unlock(p_user_list_pool->semid) < 0)          if (user_list_rd_lock(p_user_list_pool->semid) < 0)
878      {          {
879          log_error("user_list_rd_unlock() error\n");                  log_error("user_list_rd_lock() error\n");
880          ret = -1;                  return -2;
881      }          }
882    
883            if (p_user_list_pool->p_current->index_uid[0].uid != 1)
884            {
885                    log_error("Incorrect index\n");
886            }
887    
888            left = 0;
889            right = p_user_list_pool->p_current->user_count - 1;
890    
891            while (left < right)
892            {
893                    mid = (left + right) / 2;
894                    if (uid < p_user_list_pool->p_current->index_uid[mid].uid)
895                    {
896                            right = mid;
897                    }
898                    else if (uid > p_user_list_pool->p_current->index_uid[mid].uid)
899                    {
900                            left = mid + 1;
901                    }
902                    else // if (uid == p_user_list_pool->p_current->index_uid[mid].uid)
903                    {
904                            left = mid;
905                            break;
906                    }
907            }
908    
909            if (uid == p_user_list_pool->p_current->index_uid[left].uid) // Found
910            {
911                    id = p_user_list_pool->p_current->index_uid[left].id;
912                    *p_user = p_user_list_pool->p_current->users[id];
913                    ret = 1;
914            }
915    
916            // release lock of user list
917            if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
918            {
919                    log_error("user_list_rd_unlock() error\n");
920                    ret = -1;
921            }
922    
923            return ret;
924    }
925    
926    int query_user_online_info(int32_t id, USER_ONLINE_INFO *p_user)
927    {
928            int ret = 0;
929    
930            if (p_user == NULL)
931            {
932                    log_error("NULL pointer error\n");
933                    return -1;
934            }
935    
936            // acquire lock of user list
937            if (user_list_rd_lock(p_user_list_pool->semid) < 0)
938            {
939                    log_error("user_list_rd_lock() error\n");
940                    return -2;
941            }
942    
943            if (id >= 0 && id < p_user_list_pool->p_online_current->user_count) // Found
944            {
945                    *p_user = p_user_list_pool->p_online_current->users[id];
946                    ret = 1;
947            }
948    
949            // release lock of user list
950            if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
951            {
952                    log_error("user_list_rd_unlock() error\n");
953                    ret = -1;
954            }
955    
956            return ret;
957    }
958    
959    int query_user_online_info_by_uid(int32_t uid, USER_ONLINE_INFO *p_users, int *p_user_cnt, int start_id)
960    {
961            int left;
962            int right;
963            int mid;
964            int32_t id;
965            int ret = 0;
966            int i;
967    
968            if (p_users == NULL || p_user_cnt == NULL)
969            {
970                    log_error("NULL pointer error\n");
971                    return -1;
972            }
973    
974            // acquire lock of user list
975            if (user_list_rd_lock(p_user_list_pool->semid) < 0)
976            {
977                    log_error("user_list_rd_lock() error\n");
978                    return -2;
979            }
980    
981            left = start_id;
982            right = p_user_list_pool->p_online_current->user_count - 1;
983    
984            while (left < right)
985            {
986                    mid = (left + right) / 2;
987                    if (uid < p_user_list_pool->p_online_current->index_uid[mid].uid)
988                    {
989                            right = mid;
990                    }
991                    else if (uid > p_user_list_pool->p_online_current->index_uid[mid].uid)
992                    {
993                            left = mid + 1;
994                    }
995                    else // if (uid == p_user_list_pool->p_online_current->index_uid[mid].uid)
996                    {
997                            left = mid;
998                            break;
999                    }
1000            }
1001    
1002            if (uid == p_user_list_pool->p_online_current->index_uid[left].uid)
1003            {
1004                    right = left;
1005                    left = start_id;
1006    
1007                    while (left < right)
1008                    {
1009                            mid = (left + right) / 2;
1010                            if (uid - 1 < p_user_list_pool->p_online_current->index_uid[mid].uid)
1011                            {
1012                                    right = mid;
1013                            }
1014                            else // if (uid - 1 >= p_user_list_pool->p_online_current->index_uid[mid].uid)
1015                            {
1016                                    left = mid + 1;
1017                            }
1018                    }
1019    
1020                    for (id = left, i = 0;
1021                             id < p_user_list_pool->p_online_current->user_count && i < *p_user_cnt &&
1022                             uid == p_user_list_pool->p_online_current->index_uid[id].uid;
1023                             id++, i++)
1024                    {
1025                            p_users[i] = p_user_list_pool->p_online_current->users[id];
1026                            log_error("Debug: i=%d id=%d users[i].id=%d\n", i, id, p_users[i].id);
1027                    }
1028    
1029                    if (i > 0)
1030                    {
1031                            *p_user_cnt = i;
1032                            ret = 1;
1033                    }
1034            }
1035    
1036            // release lock of user list
1037            if (user_list_rd_unlock(p_user_list_pool->semid) < 0)
1038            {
1039                    log_error("user_list_rd_unlock() error\n");
1040                    ret = -1;
1041            }
1042    
1043      return ret;          return ret;
1044  }  }


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

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