1 /*
2 * Copyright (c) 2006-2023, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2023-08-29 zmq810150896 first version
9 * 2024-04-08 TroyMitchell Add all function comments
10 */
11
12 #include <rtthread.h>
13 #include <sys/signalfd.h>
14 #include <dfs_file.h>
15 #include <signal.h>
16 #include <rthw.h>
17 #include <sys/time.h>
18 #include <lwp_signal.h>
19 #include <lwp.h>
20 #include <poll.h>
21
22 #define SIGNALFD_MUTEX_NAME "signalfd"
23 #define SIGINFO_MAX 10
24 #define SIGNALFD_SHART_MAX RT_SIGNALFD_MAX_NUM
25
26 static int is_head_init = 0;
27
28 struct rt_signalfd_ctx
29 {
30 sigset_t sigmask;
31 struct rt_mutex lock;
32 siginfo_t info[SIGINFO_MAX];
33 int sig_num;
34 rt_wqueue_t signalfd_queue;
35 struct rt_lwp *lwp[SIGNALFD_SHART_MAX];
36 };
37
38 static int signalfd_close(struct dfs_file *file);
39 static int signalfd_poll(struct dfs_file *file, struct rt_pollreq *req);
40 #ifndef RT_USING_DFS_V2
41 static ssize_t signalfd_read(struct dfs_file *file, void *buf, size_t count);
42 #else
43 static ssize_t signalfd_read(struct dfs_file *file, void *buf, size_t count, off_t *pos);
44 #endif
45 static int signalfd_add_notify(struct rt_signalfd_ctx *sfd);
46
47 static const struct dfs_file_ops signalfd_fops =
48 {
49 .close = signalfd_close,
50 .poll = signalfd_poll,
51 .read = signalfd_read,
52 };
53
54 /**
55 * @brief Closes the file descriptor associated with a signalfd file.
56 * @param file Pointer to the file structure.
57 * @return Upon successful completion, returns 0; otherwise, returns an error code.
58 */
signalfd_close(struct dfs_file * file)59 static int signalfd_close(struct dfs_file *file)
60 {
61 struct rt_signalfd_ctx *sfd;
62
63 if (file->vnode->ref_count != 1)
64 return 0;
65
66 sfd = file->vnode->data;
67
68 if (sfd)
69 {
70 rt_mutex_detach(&sfd->lock);
71 rt_free(sfd);
72 }
73
74 return 0;
75 }
76
77 /**
78 * @brief Adds a signalfd file descriptor to the poll queue and checks for pending events.
79 * @param file Pointer to the file structure.
80 * @param req Pointer to the poll request structure.
81 * @return The events that are ready on the file descriptor.
82 */
signalfd_poll(struct dfs_file * file,struct rt_pollreq * req)83 static int signalfd_poll(struct dfs_file *file, struct rt_pollreq *req)
84 {
85 struct rt_signalfd_ctx *sfd;
86 int events = 0;
87
88 if (file->vnode)
89 {
90 sfd = file->vnode->data;
91
92 rt_poll_add(&sfd->signalfd_queue, req);
93 signalfd_add_notify(sfd);
94
95 rt_mutex_take(&sfd->lock, RT_WAITING_FOREVER);
96
97 if (sfd->sig_num)
98 events |= POLLIN;
99
100 rt_mutex_release(&sfd->lock);
101 }
102
103 return events;
104 }
105
106 #ifndef RT_USING_DFS_V2
107 /**
108 * @brief Reads signals from a signalfd file descriptor.
109 * @param file Pointer to the file structure.
110 * @param buf Pointer to the buffer to store the signals.
111 * @param count Maximum number of bytes to read.
112 * @return Upon successful completion, returns the number of bytes read; otherwise, returns an error code.
113 */
signalfd_read(struct dfs_file * file,void * buf,size_t count)114 static ssize_t signalfd_read(struct dfs_file *file, void *buf, size_t count)
115 #else
116 /**
117 * @brief Reads signals from a signalfd file descriptor with file offset.
118 * @param file Pointer to the file structure.
119 * @param buf Pointer to the buffer to store the signals.
120 * @param count Maximum number of bytes to read.
121 * @param pos Pointer to the file offset.
122 * @return Upon successful completion, returns the number of bytes read; otherwise, returns an negative error code.
123 */
124 static ssize_t signalfd_read(struct dfs_file *file, void *buf, size_t count, off_t *pos)
125 #endif
126 {
127 struct rt_signalfd_ctx *sfd = RT_NULL;
128 struct signalfd_siginfo *buffer = RT_NULL;
129 int user_buf_num = 0;
130 int sig_num = 0;
131 int i = 0;
132 rt_err_t ret = -1;
133
134 if (sizeof(struct signalfd_siginfo) > count)
135 return -1;
136
137 if (buf == RT_NULL)
138 return -1;
139
140 buffer = (struct signalfd_siginfo *)buf;
141 user_buf_num = count / sizeof(struct signalfd_siginfo);
142
143 if (file->vnode)
144 {
145 sfd = file->vnode->data;
146
147 signalfd_add_notify(sfd);
148 if ((sfd->sig_num == 0) && (file->flags & O_NONBLOCK))
149 {
150 ret = -EAGAIN;
151 }
152 else
153 {
154 if (sfd->sig_num == 0)
155 {
156 rt_wqueue_wait(&sfd->signalfd_queue, 0, RT_WAITING_FOREVER);
157 }
158
159 rt_mutex_take(&sfd->lock, RT_WAITING_FOREVER);
160 for (i = 0; i < sfd->sig_num; i++)
161 {
162 if (i < user_buf_num)
163 {
164 memcpy(&buffer[i], &sfd->info[i], sizeof(struct signalfd_siginfo));
165 sfd->sig_num -= 1;
166 sig_num += 1;
167 }
168 else
169 {
170 break;
171 }
172 }
173
174 for (int j = 0; j < sfd->sig_num; j ++)
175 {
176 memcpy(&sfd->info[j], &sfd->info[i ++], sizeof(struct signalfd_siginfo));
177 }
178
179 rt_mutex_release(&sfd->lock);
180
181 ret = sizeof(struct signalfd_siginfo) * sig_num;
182 }
183 }
184
185 return ret;
186 }
187
188 /**
189 * @brief Callback function for signalfd file descriptor notifications.
190 * @param signalfd_queue Pointer to the signalfd queue.
191 * @param signum Signal number.
192 */
signalfd_callback(rt_wqueue_t * signalfd_queue,int signum)193 static void signalfd_callback(rt_wqueue_t *signalfd_queue, int signum)
194 {
195 struct rt_signalfd_ctx *sfd;
196
197 sfd = rt_container_of(signalfd_queue, struct rt_signalfd_ctx, signalfd_queue);
198
199 if (sfd)
200 {
201 if (sigismember(&sfd->sigmask, signum))
202 {
203 rt_mutex_take(&sfd->lock, RT_WAITING_FOREVER);
204 if (sfd->sig_num < SIGINFO_MAX)
205 {
206 sfd->info[sfd->sig_num].si_signo = signum;
207 sfd->sig_num += 1;
208 }
209 rt_mutex_release(&sfd->lock);
210 rt_wqueue_wakeup(signalfd_queue, (void*)POLLIN);
211 }
212 }
213 }
214
215 /**
216 * @brief Adds a signal file descriptor notification.
217 * @param sfd Pointer to the signalfd context.
218 * @return Upon successful completion, returns 0; otherwise, returns an error code.
219 */
signalfd_add_notify(struct rt_signalfd_ctx * sfd)220 static int signalfd_add_notify(struct rt_signalfd_ctx *sfd)
221 {
222 struct rt_lwp_notify *lwp_notify;
223 rt_err_t ret = -1;
224 rt_slist_t *node;
225 int is_lwp = 0;
226
227 rt_mutex_take(&sfd->lock, RT_WAITING_FOREVER);
228
229 for (int i = 0; i < is_head_init; i++)
230 {
231 if (sfd->lwp[i])
232 {
233 if (sfd->lwp[i] == lwp_self())
234 {
235 is_lwp = 1;
236 }
237 }
238 }
239
240 if (is_lwp == 0)
241 {
242 sfd->lwp[is_head_init] = lwp_self();
243
244 if (is_head_init == 0)
245 {
246 rt_slist_init(&sfd->lwp[is_head_init]->signalfd_notify_head);
247 }
248
249 lwp_notify = (struct rt_lwp_notify *)rt_malloc(sizeof(struct rt_lwp_notify));
250 if (lwp_notify)
251 {
252 lwp_notify->notify = signalfd_callback;
253 lwp_notify->signalfd_queue = &sfd->signalfd_queue;
254 rt_slist_append(&sfd->lwp[is_head_init]->signalfd_notify_head, &(lwp_notify->list_node));
255
256 is_head_init ++;
257 ret = 0;
258 }
259 else
260 {
261 rt_slist_for_each(node, &sfd->lwp[is_head_init]->signalfd_notify_head)
262 {
263 struct rt_lwp_notify *n = rt_slist_entry(node, struct rt_lwp_notify, list_node);
264 rt_slist_remove(&sfd->lwp[is_head_init]->signalfd_notify_head, &n->list_node);
265 rt_free(n);
266 }
267 rt_set_errno(ENOMEM);
268 }
269 }
270
271 rt_mutex_release(&sfd->lock);
272
273 return ret;
274 }
275
276 /**
277 * @brief Creates a new signalfd file descriptor or modifies an existing one.
278 * @param fd File descriptor to modify (-1 to create a new one).
279 * @param mask Signal mask.
280 * @param flags File descriptor flags.
281 * @return Upon successful completion, returns the file descriptor number; otherwise, returns an error code.
282 */
signalfd_do(int fd,const sigset_t * mask,int flags)283 static int signalfd_do(int fd, const sigset_t *mask, int flags)
284 {
285 struct dfs_file *df;
286 struct rt_signalfd_ctx *sfd;
287 rt_err_t ret = 0;
288
289 if (fd == -1)
290 {
291 fd = fd_new();
292 if (fd < 0)
293 return -1;
294
295 ret = fd;
296 df = fd_get(fd);
297
298 if (df)
299 {
300 sfd = (struct rt_signalfd_ctx *)rt_malloc(sizeof(struct rt_signalfd_ctx));
301 if (sfd)
302 {
303 df->vnode = (struct dfs_vnode *)rt_malloc(sizeof(struct dfs_vnode));
304 if (df->vnode)
305 {
306 dfs_vnode_init(df->vnode, FT_REGULAR, &signalfd_fops);
307 df->vnode->data = sfd;
308
309 for (int i = 0; i < is_head_init; i++)
310 {
311 sfd->lwp[i] = RT_NULL;
312 }
313
314 sigemptyset(&sfd->sigmask);
315 memcpy(&sfd->sigmask, mask, sizeof(sigset_t));
316
317 rt_mutex_init(&sfd->lock, SIGNALFD_MUTEX_NAME, RT_IPC_FLAG_FIFO);
318 rt_wqueue_init(&sfd->signalfd_queue);
319
320 if (signalfd_add_notify(sfd) < 0)
321 {
322 is_head_init = 0;
323 fd_release(fd);
324 rt_free(sfd);
325 ret = -1;
326 }
327
328 sfd->sig_num = 0;
329
330 df->flags |= flags;
331
332 #ifdef RT_USING_DFS_V2
333 df->fops = &signalfd_fops;
334 #endif
335 }
336 else
337 {
338 fd_release(fd);
339 rt_free(sfd);
340 ret = -1;
341 }
342 }
343 else
344 {
345 fd_release(fd);
346 ret = -1;
347 }
348 }
349 else
350 {
351 fd_release(fd);
352 }
353 }
354 else
355 {
356 df = fd_get(fd);
357 if (df)
358 {
359 sfd = df->vnode->data;
360 df->flags = flags;
361 sigemptyset(&sfd->sigmask);
362 memcpy(&sfd->sigmask, mask, sizeof(sigset_t));
363 ret = fd;
364 }
365 else
366 {
367 rt_set_errno(EBADF);
368 ret = -1;
369 }
370 }
371
372 return ret;
373 }
374
375 /**
376 * @brief Creates a new signalfd file descriptor or modifies an existing one.
377 * @param fd File descriptor to modify (-1 to create a new one).
378 * @param mask Signal mask.
379 * @param flags File descriptor flags.
380 * @return Upon successful completion, returns the file descriptor number; otherwise, returns an error code.
381 */
signalfd(int fd,const sigset_t * mask,int flags)382 int signalfd(int fd, const sigset_t *mask, int flags)
383 {
384 return signalfd_do(fd, mask, flags);
385 }
386