/[LeafOK_CVS]/lbbs/src/user_list.c
ViewVC logotype

Annotation of /lbbs/src/user_list.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1 - (hide annotations)
Tue Oct 21 06:24:51 2025 UTC (4 months, 3 weeks ago) by sysadm
Branch: MAIN
Content type: text/x-csrc
Add user_list with data loader

1 sysadm 1.1 /***************************************************************************
2     user_list.c - description
3     -------------------
4     Copyright : (C) 2004-2025 by Leaflet
5     Email : leaflet@leafok.com
6     ***************************************************************************/
7    
8     /***************************************************************************
9     * *
10     * This program is free software; you can redistribute it and/or modify *
11     * it under the terms of the GNU General Public License as published by *
12     * the Free Software Foundation; either version 3 of the License, or *
13     * (at your option) any later version. *
14     * *
15     ***************************************************************************/
16    
17     #include "common.h"
18     #include "database.h"
19     #include "log.h"
20     #include "user_list.h"
21     #include <errno.h>
22     #include <stdlib.h>
23     #include <string.h>
24     #include <time.h>
25     #include <sys/ipc.h>
26     #include <sys/mman.h>
27     #include <sys/sem.h>
28     #include <sys/shm.h>
29    
30     #ifdef _SEM_SEMUN_UNDEFINED
31     union semun
32     {
33     int val; /* Value for SETVAL */
34     struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
35     unsigned short *array; /* Array for GETALL, SETALL */
36     struct seminfo *__buf; /* Buffer for IPC_INFO
37     (Linux-specific) */
38     };
39     #endif // #ifdef _SEM_SEMUN_UNDEFINED
40    
41     #define USER_LIST_TRY_LOCK_WAIT_TIME 1 // second
42     #define USER_LIST_TRY_LOCK_TIMES 10
43    
44     struct user_list_pool_t
45     {
46     int shmid;
47     int semid;
48     USER_LIST user_list[2];
49     USER_LIST *p_current;
50     USER_LIST *p_new;
51     };
52     typedef struct user_list_pool_t USER_LIST_POOL;
53    
54     static USER_LIST_POOL *p_user_list_pool = NULL;
55    
56     int user_list_load(MYSQL *db, USER_LIST *p_list)
57     {
58     MYSQL_RES *rs = NULL;
59     MYSQL_ROW row;
60     char sql[SQL_BUFFER_LEN];
61     int ret = 0;
62     int i;
63    
64     if (db == NULL || p_list == NULL)
65     {
66     log_error("NULL pointer error\n");
67     return -1;
68     }
69    
70     snprintf(sql, sizeof(sql),
71     "SELECT user_list.UID AS UID, username, nickname, gender, gender_pub, life, exp, "
72     "UNIX_TIMESTAMP(signup_dt), UNIX_TIMESTAMP(last_login_dt), UNIX_TIMESTAMP(birthday) "
73     "FROM user_list INNER JOIN user_pubinfo ON user_list.UID = user_pubinfo.UID "
74     "INNER JOIN user_reginfo ON user_list.UID = user_reginfo.UID "
75     "WHERE enable ORDER BY UID");
76    
77     if (mysql_query(db, sql) != 0)
78     {
79     log_error("Query user info error: %s\n", mysql_error(db));
80     ret = -1;
81     goto cleanup;
82     }
83    
84     if ((rs = mysql_use_result(db)) == NULL)
85     {
86     log_error("Get user info data failed\n");
87     ret = -1;
88     goto cleanup;
89     }
90    
91     i = 0;
92     while ((row = mysql_fetch_row(rs)))
93     {
94     p_list->users[i].uid = atoi(row[0]);
95     strncpy(p_list->users[i].username, row[1], sizeof(p_list->users[i].username) - 1);
96     p_list->users[i].username[sizeof(p_list->users[i].username) - 1] = '\0';
97     strncpy(p_list->users[i].nickname, row[2], sizeof(p_list->users[i].nickname) - 1);
98     p_list->users[i].nickname[sizeof(p_list->users[i].nickname) - 1] = '\0';
99     p_list->users[i].gender = row[3][0];
100     p_list->users[i].gender_pub = (int8_t)(row[4] == NULL ? 0 : atoi(row[4]));
101     p_list->users[i].life = (row[5] == NULL ? 0 : atoi(row[5]));
102     p_list->users[i].exp = (row[6] == NULL ? 0 : atoi(row[6]));
103     p_list->users[i].signup_dt = (row[7] == NULL ? 0 : atol(row[7]));
104     p_list->users[i].last_login_dt = (row[8] == NULL ? 0 : atol(row[8]));
105     p_list->users[i].birthday = (row[9] == NULL ? 0 : atol(row[9]));
106    
107     i++;
108     }
109     mysql_free_result(rs);
110     rs = NULL;
111    
112     p_list->user_count = i;
113    
114     #ifdef _DEBUG
115     log_error("Loaded %d users\n", p_list->user_count);
116     #endif
117    
118     cleanup:
119     mysql_free_result(rs);
120    
121     return ret;
122     }
123    
124     int user_list_pool_init(void)
125     {
126     int shmid;
127     int semid;
128     int proj_id;
129     key_t key;
130     size_t size;
131     void *p_shm;
132     union semun arg;
133     int i;
134    
135     if (p_user_list_pool != NULL)
136     {
137     log_error("p_user_list_pool already initialized\n");
138     return -1;
139     }
140    
141     // Allocate shared memory
142     proj_id = (int)(time(NULL) % getpid());
143     key = ftok(VAR_USER_LIST_SHM, proj_id);
144     if (key == -1)
145     {
146     log_error("ftok(%s %d) error (%d)\n", VAR_USER_LIST_SHM, proj_id, errno);
147     return -2;
148     }
149    
150     size = sizeof(USER_LIST_POOL);
151     shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
152     if (shmid == -1)
153     {
154     log_error("shmget(size = %d) error (%d)\n", size, errno);
155     return -3;
156     }
157     p_shm = shmat(shmid, NULL, 0);
158     if (p_shm == (void *)-1)
159     {
160     log_error("shmat(shmid=%d) error (%d)\n", shmid, errno);
161     return -3;
162     }
163    
164     p_user_list_pool = p_shm;
165     p_user_list_pool->shmid = shmid;
166    
167     // Allocate semaphore as user list pool lock
168     size = 2; // r_sem and w_sem
169     semid = semget(key, (int)size, IPC_CREAT | IPC_EXCL | 0600);
170     if (semid == -1)
171     {
172     log_error("semget(user_list_pool_sem, size = %d) error (%d)\n", size, errno);
173     return -3;
174     }
175    
176     // Initialize sem value to 0
177     arg.val = 0;
178     for (i = 0; i < size; i++)
179     {
180     if (semctl(semid, i, SETVAL, arg) == -1)
181     {
182     log_error("semctl(user_list_pool_sem, SETVAL) error (%d)\n", errno);
183     return -3;
184     }
185     }
186    
187     p_user_list_pool->semid = semid;
188    
189     // Set user counts to 0
190     p_user_list_pool->user_list[0].user_count = 0;
191     p_user_list_pool->user_list[1].user_count = 0;
192    
193     p_user_list_pool->p_current = &(p_user_list_pool->user_list[0]);
194     p_user_list_pool->p_new = &(p_user_list_pool->user_list[1]);
195    
196     return 0;
197     }
198    
199     void user_list_pool_cleanup(void)
200     {
201     int shmid;
202    
203     if (p_user_list_pool == NULL)
204     {
205     return;
206     }
207    
208     shmid = p_user_list_pool->shmid;
209    
210     if (semctl(p_user_list_pool->semid, 0, IPC_RMID) == -1)
211     {
212     log_error("semctl(semid = %d, IPC_RMID) error (%d)\n", p_user_list_pool->semid, errno);
213     }
214    
215     if (shmdt(p_user_list_pool) == -1)
216     {
217     log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
218     }
219    
220     if (shmctl(shmid, IPC_RMID, NULL) == -1)
221     {
222     log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", shmid, errno);
223     }
224    
225     p_user_list_pool = NULL;
226     }
227    
228     int set_user_list_pool_shm_readonly(void)
229     {
230     int shmid;
231     void *p_shm;
232    
233     if (p_user_list_pool == NULL)
234     {
235     log_error("p_user_list_pool not initialized\n");
236     return -1;
237     }
238    
239     shmid = p_user_list_pool->shmid;
240    
241     // Remap shared memory in read-only mode
242     p_shm = shmat(shmid, p_user_list_pool, SHM_RDONLY | SHM_REMAP);
243     if (p_shm == (void *)-1)
244     {
245     log_error("shmat(user_list_pool shmid = %d) error (%d)\n", shmid, errno);
246     return -3;
247     }
248    
249     p_user_list_pool = p_shm;
250    
251     return 0;
252     }
253    
254     int detach_user_list_pool_shm(void)
255     {
256     if (p_user_list_pool != NULL && shmdt(p_user_list_pool) == -1)
257     {
258     log_error("shmdt(user_list_pool) error (%d)\n", errno);
259     return -1;
260     }
261    
262     p_user_list_pool = NULL;
263    
264     return 0;
265     }
266    
267     int user_list_pool_reload(void)
268     {
269     MYSQL *db = NULL;
270     USER_LIST *p_tmp;
271     int ret = 0;
272    
273     if (p_user_list_pool == NULL)
274     {
275     log_error("p_user_list_pool not initialized\n");
276     return -1;
277     }
278    
279     db = db_open();
280     if (db == NULL)
281     {
282     log_error("db_open() error: %s\n", mysql_error(db));
283     return -1;
284     }
285    
286     if (user_list_rw_lock() < 0)
287     {
288     log_error("user_list_rw_lock() error\n");
289     return -2;
290     }
291    
292     if (user_list_load(db, p_user_list_pool->p_new) < 0)
293     {
294     log_error("user_list_rw_lock() error\n");
295     ret = -3;
296     goto cleanup;
297     }
298    
299     // Swap p_current and p_new
300     p_tmp = p_user_list_pool->p_current;
301     p_user_list_pool->p_current = p_user_list_pool->p_new;
302     p_user_list_pool->p_new = p_tmp;
303    
304     cleanup:
305     if (user_list_rw_unlock() < 0)
306     {
307     log_error("user_list_rw_unlock() error\n");
308     ret = -2;
309     }
310    
311     mysql_close(db);
312    
313     return ret;
314     }
315    
316     int user_list_try_rd_lock(int wait_sec)
317     {
318     struct sembuf sops[2];
319     struct timespec timeout;
320     int ret;
321    
322     sops[0].sem_num = 1; // w_sem
323     sops[0].sem_op = 0; // wait until unlocked
324     sops[0].sem_flg = 0;
325    
326     sops[1].sem_num = 0; // r_sem
327     sops[1].sem_op = 1; // lock
328     sops[1].sem_flg = SEM_UNDO; // undo on terminate
329    
330     timeout.tv_sec = wait_sec;
331     timeout.tv_nsec = 0;
332    
333     ret = semtimedop(p_user_list_pool->semid, sops, 2, &timeout);
334     if (ret == -1 && errno != EAGAIN && errno != EINTR)
335     {
336     log_error("semtimedop(lock read) error %d\n", errno);
337     }
338    
339     return ret;
340     }
341    
342     int user_list_try_rw_lock(int wait_sec)
343     {
344     struct sembuf sops[3];
345     struct timespec timeout;
346     int ret;
347    
348     sops[0].sem_num = 1; // w_sem
349     sops[0].sem_op = 0; // wait until unlocked
350     sops[0].sem_flg = 0;
351    
352     sops[1].sem_num = 1; // w_sem
353     sops[1].sem_op = 1; // lock
354     sops[1].sem_flg = SEM_UNDO; // undo on terminate
355    
356     sops[2].sem_num = 0; // r_sem
357     sops[2].sem_op = 0; // wait until unlocked
358     sops[2].sem_flg = 0;
359    
360     timeout.tv_sec = wait_sec;
361     timeout.tv_nsec = 0;
362    
363     ret = semtimedop(p_user_list_pool->semid, sops, 3, &timeout);
364     if (ret == -1 && errno != EAGAIN && errno != EINTR)
365     {
366     log_error("semtimedop(lock write) error %d\n", errno);
367     }
368    
369     return ret;
370     }
371    
372     int user_list_rd_unlock(void)
373     {
374     struct sembuf sops[2];
375     int ret;
376    
377     sops[0].sem_num = 0; // r_sem
378     sops[0].sem_op = -1; // unlock
379     sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
380    
381     ret = semop(p_user_list_pool->semid, sops, 1);
382     if (ret == -1 && errno != EAGAIN && errno != EINTR)
383     {
384     log_error("semop(unlock read) error %d\n", errno);
385     }
386    
387     return ret;
388     }
389    
390     int user_list_rw_unlock(void)
391     {
392     struct sembuf sops[1];
393     int ret;
394    
395     sops[0].sem_num = 1; // w_sem
396     sops[0].sem_op = -1; // unlock
397     sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
398    
399     ret = semop(p_user_list_pool->semid, sops, 1);
400     if (ret == -1 && errno != EAGAIN && errno != EINTR)
401     {
402     log_error("semop(unlock write) error %d\n", errno);
403     }
404    
405     return ret;
406     }
407    
408     int user_list_rd_lock(void)
409     {
410     int timer = 0;
411     int ret = -1;
412    
413     while (!SYS_server_exit)
414     {
415     ret = user_list_try_rd_lock(USER_LIST_TRY_LOCK_WAIT_TIME);
416     if (ret == 0) // success
417     {
418     break;
419     }
420     else if (errno == EAGAIN || errno == EINTR) // retry
421     {
422     timer++;
423     if (timer % USER_LIST_TRY_LOCK_TIMES == 0)
424     {
425     log_error("user_list_try_rd_lock() tried %d times\n", timer);
426     }
427     }
428     else // failed
429     {
430     log_error("user_list_try_rd_lock() failed\n");
431     break;
432     }
433     }
434    
435     return ret;
436     }
437    
438     int user_list_rw_lock(void)
439     {
440     int timer = 0;
441     int ret = -1;
442    
443     while (!SYS_server_exit)
444     {
445     ret = user_list_try_rw_lock(USER_LIST_TRY_LOCK_WAIT_TIME);
446     if (ret == 0) // success
447     {
448     break;
449     }
450     else if (errno == EAGAIN || errno == EINTR) // retry
451     {
452     timer++;
453     if (timer % USER_LIST_TRY_LOCK_TIMES == 0)
454     {
455     log_error("user_list_try_rw_lock() tried %d times\n", timer);
456     }
457     }
458     else // failed
459     {
460     log_error("user_list_try_rw_lock() failed\n");
461     break;
462     }
463     }
464    
465     return ret;
466     }

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