/[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.4 - (hide annotations)
Tue Oct 21 12:35:03 2025 UTC (4 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.3: +57 -0 lines
Content type: text/x-csrc
Add query_user_info()

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 sysadm 1.2 #include <sys/param.h>
28 sysadm 1.1 #include <sys/sem.h>
29     #include <sys/shm.h>
30    
31     #ifdef _SEM_SEMUN_UNDEFINED
32     union semun
33     {
34     int val; /* Value for SETVAL */
35     struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
36     unsigned short *array; /* Array for GETALL, SETALL */
37     struct seminfo *__buf; /* Buffer for IPC_INFO
38     (Linux-specific) */
39     };
40     #endif // #ifdef _SEM_SEMUN_UNDEFINED
41    
42     #define USER_LIST_TRY_LOCK_WAIT_TIME 1 // second
43     #define USER_LIST_TRY_LOCK_TIMES 10
44    
45     struct user_list_pool_t
46     {
47     int shmid;
48     int semid;
49     USER_LIST user_list[2];
50     USER_LIST *p_current;
51     USER_LIST *p_new;
52     };
53     typedef struct user_list_pool_t USER_LIST_POOL;
54    
55     static USER_LIST_POOL *p_user_list_pool = NULL;
56    
57     int user_list_load(MYSQL *db, USER_LIST *p_list)
58     {
59     MYSQL_RES *rs = NULL;
60     MYSQL_ROW row;
61     char sql[SQL_BUFFER_LEN];
62     int ret = 0;
63     int i;
64    
65     if (db == NULL || p_list == NULL)
66     {
67     log_error("NULL pointer error\n");
68     return -1;
69     }
70    
71     snprintf(sql, sizeof(sql),
72     "SELECT user_list.UID AS UID, username, nickname, gender, gender_pub, life, exp, "
73     "UNIX_TIMESTAMP(signup_dt), UNIX_TIMESTAMP(last_login_dt), UNIX_TIMESTAMP(birthday) "
74     "FROM user_list INNER JOIN user_pubinfo ON user_list.UID = user_pubinfo.UID "
75     "INNER JOIN user_reginfo ON user_list.UID = user_reginfo.UID "
76     "WHERE enable ORDER BY UID");
77    
78     if (mysql_query(db, sql) != 0)
79     {
80     log_error("Query user info error: %s\n", mysql_error(db));
81     ret = -1;
82     goto cleanup;
83     }
84    
85     if ((rs = mysql_use_result(db)) == NULL)
86     {
87     log_error("Get user info data failed\n");
88     ret = -1;
89     goto cleanup;
90     }
91    
92     i = 0;
93     while ((row = mysql_fetch_row(rs)))
94     {
95     p_list->users[i].uid = atoi(row[0]);
96     strncpy(p_list->users[i].username, row[1], sizeof(p_list->users[i].username) - 1);
97     p_list->users[i].username[sizeof(p_list->users[i].username) - 1] = '\0';
98     strncpy(p_list->users[i].nickname, row[2], sizeof(p_list->users[i].nickname) - 1);
99     p_list->users[i].nickname[sizeof(p_list->users[i].nickname) - 1] = '\0';
100     p_list->users[i].gender = row[3][0];
101     p_list->users[i].gender_pub = (int8_t)(row[4] == NULL ? 0 : atoi(row[4]));
102     p_list->users[i].life = (row[5] == NULL ? 0 : atoi(row[5]));
103     p_list->users[i].exp = (row[6] == NULL ? 0 : atoi(row[6]));
104     p_list->users[i].signup_dt = (row[7] == NULL ? 0 : atol(row[7]));
105     p_list->users[i].last_login_dt = (row[8] == NULL ? 0 : atol(row[8]));
106     p_list->users[i].birthday = (row[9] == NULL ? 0 : atol(row[9]));
107    
108     i++;
109 sysadm 1.2 if (i >= BBS_max_user_count)
110     {
111     log_error("Too many users, exceed limit %d\n", BBS_max_user_count);
112     break;
113     }
114 sysadm 1.1 }
115     mysql_free_result(rs);
116     rs = NULL;
117    
118     p_list->user_count = i;
119    
120     #ifdef _DEBUG
121     log_error("Loaded %d users\n", p_list->user_count);
122     #endif
123    
124     cleanup:
125     mysql_free_result(rs);
126    
127     return ret;
128     }
129    
130     int user_list_pool_init(void)
131     {
132     int shmid;
133     int semid;
134     int proj_id;
135     key_t key;
136     size_t size;
137     void *p_shm;
138     union semun arg;
139     int i;
140    
141     if (p_user_list_pool != NULL)
142     {
143     log_error("p_user_list_pool already initialized\n");
144     return -1;
145     }
146    
147     // Allocate shared memory
148     proj_id = (int)(time(NULL) % getpid());
149     key = ftok(VAR_USER_LIST_SHM, proj_id);
150     if (key == -1)
151     {
152     log_error("ftok(%s %d) error (%d)\n", VAR_USER_LIST_SHM, proj_id, errno);
153     return -2;
154     }
155    
156     size = sizeof(USER_LIST_POOL);
157     shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
158     if (shmid == -1)
159     {
160     log_error("shmget(size = %d) error (%d)\n", size, errno);
161     return -3;
162     }
163     p_shm = shmat(shmid, NULL, 0);
164     if (p_shm == (void *)-1)
165     {
166     log_error("shmat(shmid=%d) error (%d)\n", shmid, errno);
167     return -3;
168     }
169    
170     p_user_list_pool = p_shm;
171     p_user_list_pool->shmid = shmid;
172    
173     // Allocate semaphore as user list pool lock
174     size = 2; // r_sem and w_sem
175     semid = semget(key, (int)size, IPC_CREAT | IPC_EXCL | 0600);
176     if (semid == -1)
177     {
178     log_error("semget(user_list_pool_sem, size = %d) error (%d)\n", size, errno);
179     return -3;
180     }
181    
182     // Initialize sem value to 0
183     arg.val = 0;
184     for (i = 0; i < size; i++)
185     {
186     if (semctl(semid, i, SETVAL, arg) == -1)
187     {
188     log_error("semctl(user_list_pool_sem, SETVAL) error (%d)\n", errno);
189     return -3;
190     }
191     }
192    
193     p_user_list_pool->semid = semid;
194    
195     // Set user counts to 0
196     p_user_list_pool->user_list[0].user_count = 0;
197     p_user_list_pool->user_list[1].user_count = 0;
198    
199     p_user_list_pool->p_current = &(p_user_list_pool->user_list[0]);
200     p_user_list_pool->p_new = &(p_user_list_pool->user_list[1]);
201    
202     return 0;
203     }
204    
205     void user_list_pool_cleanup(void)
206     {
207     int shmid;
208    
209     if (p_user_list_pool == NULL)
210     {
211     return;
212     }
213    
214     shmid = p_user_list_pool->shmid;
215    
216     if (semctl(p_user_list_pool->semid, 0, IPC_RMID) == -1)
217     {
218     log_error("semctl(semid = %d, IPC_RMID) error (%d)\n", p_user_list_pool->semid, errno);
219     }
220    
221     if (shmdt(p_user_list_pool) == -1)
222     {
223     log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
224     }
225    
226     if (shmctl(shmid, IPC_RMID, NULL) == -1)
227     {
228     log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", shmid, errno);
229     }
230    
231     p_user_list_pool = NULL;
232     }
233    
234     int set_user_list_pool_shm_readonly(void)
235     {
236     int shmid;
237     void *p_shm;
238    
239     if (p_user_list_pool == NULL)
240     {
241     log_error("p_user_list_pool not initialized\n");
242     return -1;
243     }
244    
245     shmid = p_user_list_pool->shmid;
246    
247     // Remap shared memory in read-only mode
248     p_shm = shmat(shmid, p_user_list_pool, SHM_RDONLY | SHM_REMAP);
249     if (p_shm == (void *)-1)
250     {
251     log_error("shmat(user_list_pool shmid = %d) error (%d)\n", shmid, errno);
252     return -3;
253     }
254    
255     p_user_list_pool = p_shm;
256    
257     return 0;
258     }
259    
260     int detach_user_list_pool_shm(void)
261     {
262     if (p_user_list_pool != NULL && shmdt(p_user_list_pool) == -1)
263     {
264     log_error("shmdt(user_list_pool) error (%d)\n", errno);
265     return -1;
266     }
267    
268     p_user_list_pool = NULL;
269    
270     return 0;
271     }
272    
273     int user_list_pool_reload(void)
274     {
275     MYSQL *db = NULL;
276     USER_LIST *p_tmp;
277    
278     if (p_user_list_pool == NULL)
279     {
280     log_error("p_user_list_pool not initialized\n");
281     return -1;
282     }
283    
284     db = db_open();
285     if (db == NULL)
286     {
287     log_error("db_open() error: %s\n", mysql_error(db));
288     return -1;
289     }
290    
291 sysadm 1.3 if (user_list_load(db, p_user_list_pool->p_new) < 0)
292 sysadm 1.1 {
293     log_error("user_list_rw_lock() error\n");
294     return -2;
295     }
296    
297 sysadm 1.3 mysql_close(db);
298    
299     if (user_list_rw_lock() < 0)
300 sysadm 1.1 {
301     log_error("user_list_rw_lock() error\n");
302 sysadm 1.3 return -3;
303 sysadm 1.1 }
304    
305     // Swap p_current and p_new
306     p_tmp = p_user_list_pool->p_current;
307     p_user_list_pool->p_current = p_user_list_pool->p_new;
308     p_user_list_pool->p_new = p_tmp;
309    
310     if (user_list_rw_unlock() < 0)
311     {
312     log_error("user_list_rw_unlock() error\n");
313 sysadm 1.3 return -3;
314 sysadm 1.1 }
315    
316 sysadm 1.3 return 0;
317 sysadm 1.1 }
318    
319     int user_list_try_rd_lock(int wait_sec)
320     {
321     struct sembuf sops[2];
322     struct timespec timeout;
323     int ret;
324    
325     sops[0].sem_num = 1; // w_sem
326     sops[0].sem_op = 0; // wait until unlocked
327     sops[0].sem_flg = 0;
328    
329     sops[1].sem_num = 0; // r_sem
330     sops[1].sem_op = 1; // lock
331     sops[1].sem_flg = SEM_UNDO; // undo on terminate
332    
333     timeout.tv_sec = wait_sec;
334     timeout.tv_nsec = 0;
335    
336     ret = semtimedop(p_user_list_pool->semid, sops, 2, &timeout);
337     if (ret == -1 && errno != EAGAIN && errno != EINTR)
338     {
339     log_error("semtimedop(lock read) error %d\n", errno);
340     }
341    
342     return ret;
343     }
344    
345     int user_list_try_rw_lock(int wait_sec)
346     {
347     struct sembuf sops[3];
348     struct timespec timeout;
349     int ret;
350    
351     sops[0].sem_num = 1; // w_sem
352     sops[0].sem_op = 0; // wait until unlocked
353     sops[0].sem_flg = 0;
354    
355     sops[1].sem_num = 1; // w_sem
356     sops[1].sem_op = 1; // lock
357     sops[1].sem_flg = SEM_UNDO; // undo on terminate
358    
359     sops[2].sem_num = 0; // r_sem
360     sops[2].sem_op = 0; // wait until unlocked
361     sops[2].sem_flg = 0;
362    
363     timeout.tv_sec = wait_sec;
364     timeout.tv_nsec = 0;
365    
366     ret = semtimedop(p_user_list_pool->semid, sops, 3, &timeout);
367     if (ret == -1 && errno != EAGAIN && errno != EINTR)
368     {
369     log_error("semtimedop(lock write) error %d\n", errno);
370     }
371    
372     return ret;
373     }
374    
375     int user_list_rd_unlock(void)
376     {
377     struct sembuf sops[2];
378     int ret;
379    
380     sops[0].sem_num = 0; // r_sem
381     sops[0].sem_op = -1; // unlock
382     sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
383    
384     ret = semop(p_user_list_pool->semid, sops, 1);
385     if (ret == -1 && errno != EAGAIN && errno != EINTR)
386     {
387     log_error("semop(unlock read) error %d\n", errno);
388     }
389    
390     return ret;
391     }
392    
393     int user_list_rw_unlock(void)
394     {
395     struct sembuf sops[1];
396     int ret;
397    
398     sops[0].sem_num = 1; // w_sem
399     sops[0].sem_op = -1; // unlock
400     sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
401    
402     ret = semop(p_user_list_pool->semid, sops, 1);
403     if (ret == -1 && errno != EAGAIN && errno != EINTR)
404     {
405     log_error("semop(unlock write) error %d\n", errno);
406     }
407    
408     return ret;
409     }
410    
411     int user_list_rd_lock(void)
412     {
413     int timer = 0;
414     int ret = -1;
415    
416     while (!SYS_server_exit)
417     {
418     ret = user_list_try_rd_lock(USER_LIST_TRY_LOCK_WAIT_TIME);
419     if (ret == 0) // success
420     {
421     break;
422     }
423     else if (errno == EAGAIN || errno == EINTR) // retry
424     {
425     timer++;
426     if (timer % USER_LIST_TRY_LOCK_TIMES == 0)
427     {
428     log_error("user_list_try_rd_lock() tried %d times\n", timer);
429     }
430     }
431     else // failed
432     {
433     log_error("user_list_try_rd_lock() failed\n");
434     break;
435     }
436     }
437    
438     return ret;
439     }
440    
441     int user_list_rw_lock(void)
442     {
443     int timer = 0;
444     int ret = -1;
445    
446     while (!SYS_server_exit)
447     {
448     ret = user_list_try_rw_lock(USER_LIST_TRY_LOCK_WAIT_TIME);
449     if (ret == 0) // success
450     {
451     break;
452     }
453     else if (errno == EAGAIN || errno == EINTR) // retry
454     {
455     timer++;
456     if (timer % USER_LIST_TRY_LOCK_TIMES == 0)
457     {
458     log_error("user_list_try_rw_lock() tried %d times\n", timer);
459     }
460     }
461     else // failed
462     {
463     log_error("user_list_try_rw_lock() failed\n");
464     break;
465     }
466     }
467    
468     return ret;
469     }
470 sysadm 1.2
471     int query_user_list(int page_id, USER_INFO *p_users, int *p_user_count, int *p_page_count)
472     {
473     int ret = 0;
474    
475     if (p_users == NULL || p_user_count == NULL || p_page_count == NULL)
476     {
477     log_error("NULL pointer error\n");
478     return -1;
479     }
480    
481     // acquire lock of user list
482     if (user_list_rd_lock() < 0)
483     {
484     log_error("user_list_rd_lock() error\n");
485     return -2;
486     }
487    
488     if (p_user_list_pool->p_current->user_count == 0)
489     {
490     // empty list
491     ret = 0;
492     goto cleanup;
493     }
494    
495     *p_page_count = p_user_list_pool->p_current->user_count / BBS_user_limit_per_page +
496     (p_user_list_pool->p_current->user_count % BBS_user_limit_per_page == 0 ? 0 : 1);
497    
498     if (page_id < 0 || page_id >= *p_page_count)
499     {
500     log_error("Invalid page_id = %d, not in range [0, %d)\n", page_id, *p_page_count);
501     ret = -3;
502     goto cleanup;
503     }
504    
505     *p_user_count = MIN(BBS_user_limit_per_page,
506     p_user_list_pool->p_current->user_count -
507     page_id * BBS_user_limit_per_page);
508    
509     memcpy(p_users,
510     p_user_list_pool->p_current->users + page_id * BBS_user_limit_per_page,
511     sizeof(USER_INFO) * (size_t)(*p_user_count));
512    
513     cleanup:
514     // release lock of user list
515     if (user_list_rd_unlock() < 0)
516     {
517     log_error("user_list_rd_unlock() error\n");
518     ret = -1;
519     }
520    
521     return ret;
522     }
523 sysadm 1.4
524     int query_user_info(int32_t uid, USER_INFO *p_user)
525     {
526     int left;
527     int right;
528     int mid;
529     int ret = 0;
530    
531     if (p_user == NULL)
532     {
533     log_error("NULL pointer error\n");
534     return -1;
535     }
536    
537     // acquire lock of user list
538     if (user_list_rd_lock() < 0)
539     {
540     log_error("user_list_rd_lock() error\n");
541     return -2;
542     }
543    
544     left = 0;
545     right = p_user_list_pool->p_current->user_count - 1;
546    
547     while (left < right)
548     {
549     mid = (left + right) / 2;
550     if (uid < p_user_list_pool->p_current->users[mid].uid)
551     {
552     right = mid;
553     }
554     else if (uid > p_user_list_pool->p_current->users[mid].uid)
555     {
556     left = mid + 1;
557     }
558     else // if (uid == p_user_list_pool->p_current->users[mid].uid)
559     {
560     left = mid;
561     break;
562     }
563     }
564    
565     if (uid == p_user_list_pool->p_current->users[left].uid) // Found
566     {
567     *p_user = p_user_list_pool->p_current->users[left];
568     ret = 1;
569     }
570    
571     // release lock of user list
572     if (user_list_rd_unlock() < 0)
573     {
574     log_error("user_list_rd_unlock() error\n");
575     ret = -1;
576     }
577    
578     return ret;
579     }

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