summaryrefslogtreecommitdiff
path: root/src/lib/ecore_con/efl_net_server_unix.c
diff options
context:
space:
mode:
authorGustavo Sverzut Barbieri <barbieri@profusion.mobi>2016-10-26 18:57:37 -0200
committerGustavo Sverzut Barbieri <barbieri@profusion.mobi>2016-10-26 19:01:03 -0200
commit651ff136163bf6fb4986f9dfaff09ca3f212178e (patch)
tree2e2781c5a21babafad44a820cc9bf142dc3afe60 /src/lib/ecore_con/efl_net_server_unix.c
parent84ee276b127443e20a0db70666feefcaf1823559 (diff)
addded efl_net_{socket,dialer,server}_unix
This introduces AF_UNIX server and dialer, these are not available on Windows as in that platform we'll create a custom class for native 'local' communication. In the future we can add a wrapper class Efl.Net.Local that will use the class for each platform, but won't expose its details. For instance, if we ever expose 'credentials' (which I didn't because they are not portable), then it doesn't make sense to try to match that on Windows. The 'Efl.Net.Local' would just stick to the basics: Reader, Writer and Closer APIs.
Diffstat (limited to '')
-rw-r--r--src/lib/ecore_con/efl_net_server_unix.c259
1 files changed, 259 insertions, 0 deletions
diff --git a/src/lib/ecore_con/efl_net_server_unix.c b/src/lib/ecore_con/efl_net_server_unix.c
new file mode 100644
index 0000000000..845df44259
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_server_unix.c
@@ -0,0 +1,259 @@
1#define EFL_NET_SERVER_UNIX_PROTECTED 1
2#define EFL_NET_SERVER_FD_PROTECTED 1
3#define EFL_NET_SERVER_PROTECTED 1
4#define EFL_LOOP_FD_PROTECTED 1
5
6#ifdef HAVE_CONFIG_H
7# include <config.h>
8#endif
9
10#include "Ecore.h"
11#include "Ecore_Con.h"
12#include "ecore_con_private.h"
13
14#ifdef HAVE_SYS_SOCKET_H
15# include <sys/socket.h>
16#endif
17#ifdef HAVE_SYS_UN_H
18#include <sys/un.h>
19#endif
20
21/* no include EVIL as it's not supposed to be compiled on Windows */
22
23#define MY_CLASS EFL_NET_SERVER_UNIX_CLASS
24
25typedef struct _Efl_Net_Server_Unix_Data
26{
27 Efl_Future *bind_job;
28 Eina_Bool unlink_before_bind;
29} Efl_Net_Server_Unix_Data;
30
31EOLIAN static void
32_efl_net_server_unix_efl_object_destructor(Eo *o, Efl_Net_Server_Unix_Data *pd EINA_UNUSED)
33{
34 SOCKET fd = efl_loop_fd_get(o);
35
36 if (fd != INVALID_SOCKET)
37 {
38 const char *address = efl_net_server_address_get(o);
39 if ((address) && (strncmp(address, "abstract:", strlen("abstract:")) != 0))
40 unlink(address);
41 }
42
43 efl_destructor(efl_super(o, MY_CLASS));
44}
45
46static void
47_efl_net_server_unix_bind_job(void *data, const Efl_Event *event EINA_UNUSED)
48{
49 Eo *o = data;
50 Efl_Net_Server_Unix_Data *pd = efl_data_scope_get(o, MY_CLASS);
51 const char *address = efl_net_server_address_get(o);
52 struct sockaddr_un addr = { .sun_family = AF_UNIX };
53 socklen_t addrlen;
54 SOCKET fd;
55 Eina_Error err = 0;
56 int r;
57
58 pd->bind_job = NULL;
59
60 efl_ref(o); /* we're emitting callbacks then continuing the workflow */
61
62 efl_net_server_fd_family_set(o, AF_UNIX);
63
64 do
65 {
66 fd = efl_net_socket4(AF_UNIX, SOCK_STREAM, 0,
67 efl_net_server_fd_close_on_exec_get(o));
68 if (fd == INVALID_SOCKET)
69 {
70 err = efl_net_socket_error_get();
71 ERR("socket(AF_UNIX, SOCK_STREAM, 0): %s", eina_error_msg_get(err));
72 goto error;
73 }
74
75 if (strncmp(address, "abstract:", strlen("abstract:")) == 0)
76 {
77 const char *path = address + strlen("abstract:");
78 if (strlen(path) + 2 > sizeof(addr.sun_path))
79 {
80 ERR("abstract path is too long: %s", path);
81 err = EFL_NET_SERVER_ERROR_COULDNT_RESOLVE_HOST;
82 }
83 addr.sun_path[0] = '\0';
84 memcpy(addr.sun_path + 1, path, strlen(path) + 1);
85 addrlen = strlen(path) + 2 + offsetof(struct sockaddr_un, sun_path);
86 }
87 else
88 {
89 const char *path = address;
90 if (strlen(path) + 1 > sizeof(addr.sun_path))
91 {
92 ERR("path is too long: %s", path);
93 err = EFL_NET_SERVER_ERROR_COULDNT_RESOLVE_HOST;
94 }
95 memcpy(addr.sun_path, path, strlen(path) + 1);
96 addrlen = strlen(path) + 1 + offsetof(struct sockaddr_un, sun_path);
97 }
98
99 if ((pd->unlink_before_bind) && (addr.sun_path[0] != '\0'))
100 {
101 DBG("unlinking AF_UNIX path '%s'", addr.sun_path);
102 unlink(addr.sun_path);
103 }
104
105 r = bind(fd, (struct sockaddr *)&addr, addrlen);
106 if (r != 0)
107 {
108 err = efl_net_socket_error_get();
109 if ((err == EADDRINUSE) && (pd->unlink_before_bind) && (addr.sun_path[0] != '\0'))
110 {
111 closesocket(fd);
112 err = 0;
113 continue;
114 }
115 DBG("bind(%d, %s): %s", fd, address, eina_error_msg_get(err));
116 goto error;
117 }
118 break;
119 }
120 while (pd->unlink_before_bind);
121
122 efl_loop_fd_set(o, fd);
123
124 r = listen(fd, 0);
125 if (r != 0)
126 {
127 err = efl_net_socket_error_get();
128 DBG("listen(%d): %s", fd, eina_error_msg_get(err));
129 goto error;
130 }
131
132 addrlen = sizeof(addr);
133 if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) != 0)
134 {
135 err = efl_net_socket_error_get();
136 ERR("getsockname(%d): %s", fd, eina_error_msg_get(err));
137 goto error;
138 }
139 else
140 {
141 char str[sizeof(addr) + sizeof("abstract:")];
142 if (!efl_net_unix_fmt(str, sizeof(str), fd, &addr, addrlen))
143 ERR("could not format unix address");
144 else
145 {
146 efl_net_server_address_set(o, str);
147 address = efl_net_server_address_get(o);
148 }
149 }
150
151 DBG("fd=%d serving at %s", fd, address);
152 efl_net_server_serving_set(o, EINA_TRUE);
153
154 error:
155 if (err)
156 {
157 efl_event_callback_call(o, EFL_NET_SERVER_EVENT_ERROR, &err);
158 if (fd) closesocket(fd);
159 efl_loop_fd_set(o, INVALID_SOCKET);
160 }
161
162 efl_unref(o);
163}
164
165EOLIAN static Eina_Error
166_efl_net_server_unix_efl_net_server_serve(Eo *o, Efl_Net_Server_Unix_Data *pd, const char *address)
167{
168 EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
169 EINA_SAFETY_ON_TRUE_RETURN_VAL(address[0] == '\0', EINVAL);
170
171 efl_net_server_address_set(o, address);
172
173 if (pd->bind_job)
174 efl_future_cancel(pd->bind_job);
175
176 efl_future_use(&pd->bind_job, efl_loop_job(efl_loop_get(o), o));
177 efl_future_then(pd->bind_job, _efl_net_server_unix_bind_job, NULL, NULL, o);
178 efl_future_link(o, pd->bind_job);
179
180 return 0;
181}
182
183static Efl_Callback_Array_Item *_efl_net_server_unix_client_cbs(void);
184
185static void
186_efl_net_server_unix_client_event_closed(void *data, const Efl_Event *event)
187{
188 Eo *server = data;
189 Eo *client = event->object;
190
191 efl_event_callback_array_del(client, _efl_net_server_unix_client_cbs(), server);
192 if (efl_parent_get(client) == server)
193 efl_parent_set(client, NULL);
194
195 efl_net_server_clients_count_set(server, efl_net_server_clients_count_get(server) - 1);
196}
197
198EFL_CALLBACKS_ARRAY_DEFINE(_efl_net_server_unix_client_cbs,
199 { EFL_IO_CLOSER_EVENT_CLOSED, _efl_net_server_unix_client_event_closed });
200
201static void
202_efl_net_server_unix_efl_net_server_fd_client_add(Eo *o, Efl_Net_Server_Unix_Data *pd EINA_UNUSED, int client_fd)
203{
204 Eo *client = efl_add(EFL_NET_SOCKET_UNIX_CLASS, o,
205 efl_event_callback_array_add(efl_added, _efl_net_server_unix_client_cbs(), o),
206 efl_io_closer_close_on_exec_set(efl_added, efl_net_server_fd_close_on_exec_get(o)),
207 efl_io_closer_close_on_destructor_set(efl_added, EINA_TRUE),
208 efl_loop_fd_set(efl_added, client_fd));
209 if (!client)
210 {
211 ERR("could not create client object fd=%d", client_fd);
212 closesocket(client_fd);
213 return;
214 }
215
216 efl_net_server_clients_count_set(o, efl_net_server_clients_count_get(o) + 1);
217 efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_ADD, client);
218
219 if (efl_ref_get(client) == 1) /* users must take a reference themselves */
220 {
221 DBG("client %s was not handled, closing it...",
222 efl_net_socket_address_remote_get(client));
223 efl_del(client);
224 }
225}
226
227static void
228_efl_net_server_unix_efl_net_server_fd_client_reject(Eo *o, Efl_Net_Server_Unix_Data *pd EINA_UNUSED, int client_fd)
229{
230 struct sockaddr_un addr;
231 socklen_t addrlen;
232 char str[sizeof(addr) + sizeof("abstract:")] = "";
233
234 addrlen = sizeof(addr);
235 if (getpeername(client_fd, (struct sockaddr *)&addr, &addrlen) != 0)
236 ERR("getpeername(%d): %s", client_fd, eina_error_msg_get(efl_net_socket_error_get()));
237 else
238 {
239 if (!efl_net_unix_fmt(str, sizeof(str), client_fd, &addr, addrlen))
240 ERR("could not format rejected client unix address fd=%d", client_fd);
241 }
242
243 closesocket(client_fd);
244 efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_REJECTED, str);
245}
246
247static void
248_efl_net_server_unix_unlink_before_bind_set(Eo *o EINA_UNUSED, Efl_Net_Server_Unix_Data *pd, Eina_Bool unlink_before_bind)
249{
250 pd->unlink_before_bind = unlink_before_bind;
251}
252
253static Eina_Bool
254_efl_net_server_unix_unlink_before_bind_get(Eo *o EINA_UNUSED, Efl_Net_Server_Unix_Data *pd)
255{
256 return pd->unlink_before_bind;
257}
258
259#include "efl_net_server_unix.eo.c"