/[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.5 - (hide annotations)
Tue Oct 21 13:00:55 2025 UTC (4 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.4: +25 -18 lines
Content type: text/x-csrc
Set user_list_[rd|rw]_(un)lock() as static
These functions are for internal use only

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

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