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