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

Contents of /lbbs/src/user_list.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1 - (show 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 /***************************************************************************
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