1 /*
2  * Copyright (C) 2020-2021 Alibaba Group Holding Limited
3  */
4 
5 #include <stdbool.h>
6 #include <stdio.h>
7 #include <limits.h>
8 #include <string.h>
9 #include <errno.h>
10 #include <poll.h>
11 #include "aos/kernel.h"
12 #include "aos/vfs.h"
13 #include "aos/list.h"
14 #include "sys/socket.h"
15 #include <select.h>
16 
17 
18 #define WRITE_VAL 1
19 
20 #ifndef CONFIG_AOS_LWIP
21 #define CONFIG_NO_TCPIP
22 #endif
23 
24 typedef struct poll_node {
25     struct pollfd pfd;
26     dlist_t next;
27 } poll_node_t;
28 
29 #ifdef CONFIG_NO_TCPIP
30 
31 struct poll_arg {
32     aos_sem_t sem;
33 };
34 
vfs_poll_notify(void * fd,void * arg)35 static void vfs_poll_notify(void *fd, void *arg)
36 {
37     struct poll_arg *parg = arg;
38     aos_sem_signal(&parg->sem);
39 }
40 
41 
init_parg(struct poll_arg * parg)42 static int init_parg(struct poll_arg *parg)
43 {
44     aos_sem_new(&parg->sem,  0);
45     return 0;
46 }
47 
wait_io(int maxfd,fd_set * readset,fd_set * writeset,fd_set * exceptset,struct poll_arg * parg,struct timeval * timeout)48 static int wait_io(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset,
49                    struct poll_arg *parg, struct timeval *timeout)
50 {
51     uint64_t ms = timeout ? (timeout->tv_sec * 1000 + timeout->tv_usec / 1000) : AOS_WAIT_FOREVER;
52     aos_sem_wait(&parg->sem, ms);
53     return 0;
54 }
55 
deinit_parg(struct poll_arg * parg)56 static void deinit_parg(struct poll_arg *parg)
57 {
58     aos_sem_free(&parg->sem);
59 }
60 #else
61 #include <sys/socket.h>
62 
63 extern int lwip_write(int s, const void *dataptr, size_t size);
64 extern int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
65                        struct timeval *timeout);
66 extern int lwip_eventfd(unsigned int initval, int flags);
67 extern int lwip_close(int s);
68 
69 struct poll_arg {
70     int efd;
71 };
72 
vfs_poll_notify(void * fd,void * arg)73 static void vfs_poll_notify(void *fd, void *arg)
74 {
75     struct poll_arg *parg = arg;
76     uint64_t val = WRITE_VAL;
77     net_write(parg->efd, &val, sizeof val);
78 }
79 
init_parg(struct poll_arg * parg)80 static int init_parg(struct poll_arg *parg)
81 {
82     int efd;
83     efd = net_eventfd(0, 0);
84     if (efd < 0) {
85         errno = EINVAL;
86         return -1;
87     }
88 
89     parg->efd = efd;
90 
91     return 0;
92 }
93 
wait_io(int maxfd,fd_set * readset,fd_set * writeset,fd_set * exceptset,struct poll_arg * parg,struct timeval * timeout)94 static int wait_io(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset,
95                    struct poll_arg *parg, struct timeval *timeout)
96 {
97     int ret = 0;
98     fd_set *real_rset = readset;
99     fd_set bk_rset;
100     if (readset == NULL) {
101         real_rset = &bk_rset;
102         memset(real_rset, 0, sizeof(fd_set));
103     }
104     FD_SET(parg->efd, real_rset);
105     maxfd = parg->efd > maxfd - 1 ? (parg->efd + 1) : maxfd;
106 
107     ret = net_select(maxfd, real_rset, writeset, exceptset, timeout);
108     if (ret > 0) {
109         if (FD_ISSET(parg->efd, real_rset)) {
110             /*mask eventfd event to user*/
111             ret--;
112         }
113     }
114 
115     FD_CLR(parg->efd, real_rset);
116     return ret;
117 }
118 
deinit_parg(struct poll_arg * parg)119 static void deinit_parg(struct poll_arg *parg)
120 {
121     net_close(parg->efd);
122 }
123 #endif
pre_select(int fd,dlist_t * list,struct poll_arg * parg,fd_set * readset,fd_set * writeset,fd_set * exceptset)124 static int pre_select(int fd, dlist_t *list, struct poll_arg *parg, fd_set *readset, fd_set *writeset,
125                       fd_set *exceptset)
126 {
127     poll_node_t *node;
128     struct pollfd *pfd = NULL;
129 
130     if (readset != NULL && FD_ISSET(fd, readset)) {
131         if (fd < aos_vfs_fd_offset_get()) {
132             return 0;
133         }
134         node = aos_malloc(sizeof(poll_node_t));
135         if (node == NULL) {
136             errno = ENOMEM;
137             return -1;
138         }
139         memset(node, 0, sizeof(poll_node_t));
140 
141         pfd = &node->pfd;
142         pfd->fd = fd;
143         pfd->events = POLLIN;
144         dlist_add(&node->next, list);
145         /* delete from socket select events */
146         FD_CLR(fd, readset);
147         if (aos_do_pollfd(fd, true, vfs_poll_notify, pfd, parg) == -ENOENT) {
148             return -1;
149         }
150         /* read\write\except event add one time only, so continue here */
151         return 0;
152     }
153 
154     if (writeset != NULL && FD_ISSET(fd, writeset)) {
155         poll_node_t *node;
156         if (fd < aos_vfs_fd_offset_get()) {
157             return 0;
158         }
159         node = aos_malloc(sizeof(poll_node_t));
160         if (node == NULL) {
161             errno = ENOMEM;
162             return -1;
163         }
164         memset(node, 0, sizeof(poll_node_t));
165 
166         pfd = &node->pfd;
167         pfd->fd = fd;
168         pfd->events = POLLOUT;
169         dlist_add(&node->next, list);
170         FD_CLR(fd, writeset);
171         if (aos_do_pollfd(fd, true, vfs_poll_notify, pfd, parg) == -ENOENT) {
172             return -1;
173         }
174         return 0;
175     }
176 
177     if (exceptset != NULL && FD_ISSET(fd, exceptset)) {
178         poll_node_t *node;
179         if (fd < aos_vfs_fd_offset_get()) {
180             return 0;
181         }
182         node = aos_malloc(sizeof(poll_node_t));
183         if (node == NULL) {
184             errno = ENOMEM;
185             return -1;
186         }
187         memset(node, 0, sizeof(poll_node_t));
188 
189         pfd = &node->pfd;
190         pfd->fd = fd;
191         pfd->events = POLLERR;
192         dlist_add(&node->next, list);
193         FD_CLR(fd, exceptset);
194         if (aos_do_pollfd(fd, true, vfs_poll_notify, pfd, parg) == -ENOENT) {
195             errno = ENOENT;
196             return -1;
197         }
198         return 0;
199     }
200     return 0;
201 }
202 
post_select(dlist_t * list,fd_set * readset,fd_set * writeset,fd_set * exceptset)203 static int post_select(dlist_t *list, fd_set *readset, fd_set *writeset, fd_set *exceptset)
204 {
205     int ret = 0;
206     dlist_t  *temp;
207     poll_node_t *node;
208 
209     dlist_for_each_entry_safe(list, temp, node, poll_node_t, next) {
210 
211         struct pollfd *pfd = &node->pfd;
212 
213         if (pfd->fd < aos_vfs_fd_offset_get()) {
214             continue;
215         }
216         /* unregister vfs event */
217         if (aos_do_pollfd(pfd->fd, false, NULL, NULL, NULL) == -ENOENT) {
218             continue;
219         }
220 
221         /* change poll event to select event */
222         if ((pfd->revents & POLLIN) && (readset != NULL)) {
223             FD_SET(pfd->fd, readset);
224             ret ++;
225         }
226 
227         if ((pfd->revents & POLLOUT) && (writeset != NULL)) {
228             FD_SET(pfd->fd, writeset);
229             ret ++;
230         }
231 
232         if ((pfd->revents & POLLERR) && (exceptset != NULL)) {
233             FD_SET(pfd->fd, exceptset);
234             ret ++;
235         }
236     }
237     return ret;
238 }
239 
aos_select(int maxfd,fd_set * readset,fd_set * writeset,fd_set * exceptset,struct timeval * timeout)240 int aos_select(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset,
241                struct timeval *timeout)
242 {
243     int i;
244     int ret = -1;
245     int nset = 0;
246     struct poll_arg parg;
247     dlist_t  *temp;
248     poll_node_t *node;
249     dlist_t vfs_list;
250 
251     dlist_init(&vfs_list);
252 
253     if (init_parg(&parg) < 0) {
254         goto err;
255     }
256 
257     for (i = 0; i < maxfd; i++) {
258         if (pre_select(i, &vfs_list, &parg, readset, writeset, exceptset) < 0) {
259             deinit_parg(&parg);
260             goto err;
261         }
262     }
263 
264     ret = wait_io(maxfd, readset, writeset, exceptset,  &parg, timeout);
265 
266     nset = ret + post_select(&vfs_list, readset, writeset, exceptset);
267 
268     deinit_parg(&parg);
269 err:
270     dlist_for_each_entry_safe(&vfs_list, temp, node, poll_node_t, next) {
271         dlist_del(&node->next);
272         aos_free(node);
273     }
274 
275     return ret < 0 ? -1 : nset;
276 }
277