minor fixes
[profile/ivi/pulseaudio.git] / polyp / socket-client.c
1 /* $Id$ */
2
3 /***
4   This file is part of polypaudio.
5  
6   polypaudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as
8   published by the Free Software Foundation; either version 2.1 of the
9   License, or (at your option) any later version.
10  
11   polypaudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   Lesser General Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public
17   License along with polypaudio; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <unistd.h>
27 #include <stdio.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <assert.h>
31 #include <stdlib.h>
32 #include <sys/un.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #include <netdb.h>
36
37 #include "socket-client.h"
38 #include "socket-util.h"
39 #include "util.h"
40 #include "xmalloc.h"
41 #include "log.h"
42 #include "parseaddr.h"
43
44 struct pa_socket_client {
45     int ref;
46     struct pa_mainloop_api *mainloop;
47     int fd;
48     struct pa_io_event *io_event;
49     struct pa_defer_event *defer_event;
50     void (*callback)(struct pa_socket_client*c, struct pa_iochannel *io, void *userdata);
51     void *userdata;
52     int local;
53 };
54
55 static struct pa_socket_client*pa_socket_client_new(struct pa_mainloop_api *m) {
56     struct pa_socket_client *c;
57     assert(m);
58
59     c = pa_xmalloc(sizeof(struct pa_socket_client));
60     c->ref = 1;
61     c->mainloop = m;
62     c->fd = -1;
63     c->io_event = NULL;
64     c->defer_event = NULL;
65     c->callback = NULL;
66     c->userdata = NULL;
67     c->local = 0;
68     return c;
69 }
70
71 static void do_call(struct pa_socket_client *c) {
72     struct pa_iochannel *io = NULL;
73     int error;
74     socklen_t lerror;
75     assert(c && c->callback);
76
77     pa_socket_client_ref(c);
78     
79     lerror = sizeof(error);
80     if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &error, &lerror) < 0) {
81         pa_log(__FILE__": getsockopt(): %s\n", strerror(errno));
82         goto finish;
83     }
84
85     if (lerror != sizeof(error)) {
86         pa_log(__FILE__": getsockopt() returned invalid size.\n");
87         goto finish;
88     }
89
90     if (error != 0) {
91 /*         pa_log(__FILE__": connect(): %s\n", strerror(error)); */
92         errno = error;
93         goto finish;
94     }
95         
96     io = pa_iochannel_new(c->mainloop, c->fd, c->fd);
97     assert(io);
98     
99 finish:
100     if (!io)
101         close(c->fd);
102     c->fd = -1;
103     
104     assert(c->callback);
105     c->callback(c, io, c->userdata);
106     
107     pa_socket_client_unref(c);
108 }
109
110 static void connect_fixed_cb(struct pa_mainloop_api *m, struct pa_defer_event *e, void *userdata) {
111     struct pa_socket_client *c = userdata;
112     assert(m && c && c->defer_event == e);
113     m->defer_free(c->defer_event);
114     c->defer_event = NULL;
115     do_call(c);
116 }
117
118 static void connect_io_cb(struct pa_mainloop_api*m, struct pa_io_event *e, int fd, enum pa_io_event_flags f, void *userdata) {
119     struct pa_socket_client *c = userdata;
120     assert(m && c && c->io_event == e && fd >= 0);
121     m->io_free(c->io_event);
122     c->io_event = NULL;
123     do_call(c);
124 }
125
126 static int do_connect(struct pa_socket_client *c, const struct sockaddr *sa, socklen_t len) {
127     int r;
128     assert(c && sa && len);
129     
130     pa_make_nonblock_fd(c->fd);
131     
132     if ((r = connect(c->fd, sa, len)) < 0) {
133         if (errno != EINPROGRESS) {
134             /*pa_log(__FILE__": connect(): %s\n", strerror(errno));*/
135             return -1;
136         }
137
138         c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c);
139         assert(c->io_event);
140     } else {
141         c->defer_event = c->mainloop->defer_new(c->mainloop, connect_fixed_cb, c);
142         assert(c->defer_event);
143     }
144
145     return 0;
146 }
147
148 struct pa_socket_client* pa_socket_client_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port) {
149     struct sockaddr_in sa;
150     assert(m && port > 0);
151
152     memset(&sa, 0, sizeof(sa));
153     sa.sin_family = AF_INET;
154     sa.sin_port = htons(port);
155     sa.sin_addr.s_addr = htonl(address);
156
157     return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
158 }
159
160 struct pa_socket_client* pa_socket_client_new_unix(struct pa_mainloop_api *m, const char *filename) {
161     struct sockaddr_un sa;
162     assert(m && filename);
163     
164     memset(&sa, 0, sizeof(sa));
165     sa.sun_family = AF_LOCAL;
166     strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1);
167     sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
168
169     return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
170 }
171
172 struct pa_socket_client* pa_socket_client_new_sockaddr(struct pa_mainloop_api *m, const struct sockaddr *sa, size_t salen) {
173     struct pa_socket_client *c;
174     assert(m && sa);
175     c = pa_socket_client_new(m);
176     assert(c);
177
178     switch (sa->sa_family) {
179         case AF_UNIX:
180             c->local = 1;
181             break;
182             
183         case AF_INET:
184             c->local = ((const struct sockaddr_in*) sa)->sin_addr.s_addr == INADDR_LOOPBACK;
185             break;
186             
187         case AF_INET6:
188             c->local = memcmp(&((const struct sockaddr_in6*) sa)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)) == 0;
189             break;
190             
191         default:
192             c->local = 0;
193     }
194     
195     if ((c->fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) {
196         pa_log(__FILE__": socket(): %s\n", strerror(errno));
197         goto fail;
198     }
199
200     pa_fd_set_cloexec(c->fd, 1);
201     if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6)
202         pa_socket_tcp_low_delay(c->fd);
203     else
204         pa_socket_low_delay(c->fd);
205
206     if (do_connect(c, sa, salen) < 0)
207         goto fail;
208     
209     return c;
210
211 fail:
212     pa_socket_client_unref(c);
213     return NULL;
214     
215 }
216
217 void socket_client_free(struct pa_socket_client *c) {
218     assert(c && c->mainloop);
219     if (c->io_event)
220         c->mainloop->io_free(c->io_event);
221     if (c->defer_event)
222         c->mainloop->defer_free(c->defer_event);
223     if (c->fd >= 0)
224         close(c->fd);
225     pa_xfree(c);
226 }
227
228 void pa_socket_client_unref(struct pa_socket_client *c) {
229     assert(c && c->ref >= 1);
230
231     if (!(--(c->ref)))
232         socket_client_free(c);
233 }
234
235 struct pa_socket_client* pa_socket_client_ref(struct pa_socket_client *c) {
236     assert(c && c->ref >= 1);
237     c->ref++;
238     return c;
239 }
240
241 void pa_socket_client_set_callback(struct pa_socket_client *c, void (*on_connection)(struct pa_socket_client *c, struct pa_iochannel*io, void *userdata), void *userdata) {
242     assert(c);
243     c->callback = on_connection;
244     c->userdata = userdata;
245 }
246
247 struct pa_socket_client* pa_socket_client_new_ipv6(struct pa_mainloop_api *m, uint8_t address[16], uint16_t port) {
248     struct sockaddr_in6 sa;
249     
250     memset(&sa, 0, sizeof(sa));
251     sa.sin6_family = AF_INET6;
252     sa.sin6_port = htons(port);
253     memcpy(&sa.sin6_addr, address, sizeof(sa.sin6_addr));
254
255     return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa));
256 }
257
258 struct pa_socket_client* pa_socket_client_new_string(struct pa_mainloop_api *m, const char*name, uint16_t default_port) {
259     struct pa_socket_client *c = NULL;
260     struct pa_parsed_address a;
261     assert(m && name);
262
263     if (pa_parse_address(name, &a) < 0)
264         return NULL;
265
266     if (!a.port)
267         a.port = default_port;
268     
269     switch (a.type) {
270         case PA_PARSED_ADDRESS_UNIX:
271             c = pa_socket_client_new_unix(m, a.path_or_host);
272             break;
273
274         case PA_PARSED_ADDRESS_TCP4:  /* Fallthrough */
275         case PA_PARSED_ADDRESS_TCP6:  /* Fallthrough */
276         case PA_PARSED_ADDRESS_TCP_AUTO:{
277             int ret;
278             struct addrinfo hints, *res;
279
280             memset(&hints, 0, sizeof(hints));
281             hints.ai_family = a.type == PA_PARSED_ADDRESS_TCP4 ? AF_INET : (a.type == PA_PARSED_ADDRESS_TCP6 ? AF_INET6 : AF_UNSPEC);
282             
283             ret = getaddrinfo(a.path_or_host, NULL, &hints, &res);
284
285             if (ret < 0 || !res || !res->ai_addr)
286                 goto finish;
287
288             if (res->ai_family == AF_INET) {
289                 if (res->ai_addrlen != sizeof(struct sockaddr_in))
290                     goto finish;
291                 assert(res->ai_addr->sa_family == res->ai_family);
292                 
293                 ((struct sockaddr_in*) res->ai_addr)->sin_port = htons(a.port);
294             } else if (res->ai_family == AF_INET6) {
295                 if (res->ai_addrlen != sizeof(struct sockaddr_in6))
296                     goto finish;
297                 assert(res->ai_addr->sa_family == res->ai_family);
298                 
299                 ((struct sockaddr_in6*) res->ai_addr)->sin6_port = htons(a.port);
300             } else
301                 goto finish;
302
303             c = pa_socket_client_new_sockaddr(m, res->ai_addr, res->ai_addrlen);
304             freeaddrinfo(res);
305         }
306     }
307
308 finish:
309     pa_xfree(a.path_or_host);
310     return c;
311     
312 }
313
314 /* Return non-zero when the target sockaddr is considered
315    local. "local" means UNIX socket or TCP socket on localhost. Other
316    local IP addresses are not considered local. */
317 int pa_socket_client_is_local(struct pa_socket_client *c) {
318     assert(c);
319     return c->local;
320 }