]> code.delx.au - pulseaudio/blob - polyp/socket-client.c
implement proper logging
[pulseaudio] / 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 General Public License as published
8 by the Free Software Foundation; either version 2 of the License,
9 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 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 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
36 #include "socket-client.h"
37 #include "socket-util.h"
38 #include "util.h"
39 #include "xmalloc.h"
40 #include "log.h"
41
42 struct pa_socket_client {
43 int ref;
44 struct pa_mainloop_api *mainloop;
45 int fd;
46 struct pa_io_event *io_event;
47 struct pa_defer_event *defer_event;
48 void (*callback)(struct pa_socket_client*c, struct pa_iochannel *io, void *userdata);
49 void *userdata;
50 };
51
52 static struct pa_socket_client*pa_socket_client_new(struct pa_mainloop_api *m) {
53 struct pa_socket_client *c;
54 assert(m);
55
56 c = pa_xmalloc(sizeof(struct pa_socket_client));
57 c->ref = 1;
58 c->mainloop = m;
59 c->fd = -1;
60 c->io_event = NULL;
61 c->defer_event = NULL;
62 c->callback = NULL;
63 c->userdata = NULL;
64 return c;
65 }
66
67 static void do_call(struct pa_socket_client *c) {
68 struct pa_iochannel *io = NULL;
69 int error;
70 socklen_t lerror;
71 assert(c && c->callback);
72
73 pa_socket_client_ref(c);
74
75 lerror = sizeof(error);
76 if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &error, &lerror) < 0) {
77 pa_log(__FILE__": getsockopt(): %s\n", strerror(errno));
78 goto finish;
79 }
80
81 if (lerror != sizeof(error)) {
82 pa_log(__FILE__": getsockopt() returned invalid size.\n");
83 goto finish;
84 }
85
86 if (error != 0) {
87 pa_log(__FILE__": connect(): %s\n", strerror(error));
88 goto finish;
89 }
90
91 io = pa_iochannel_new(c->mainloop, c->fd, c->fd);
92 assert(io);
93
94 finish:
95 if (!io)
96 close(c->fd);
97 c->fd = -1;
98
99 assert(c->callback);
100 c->callback(c, io, c->userdata);
101
102 pa_socket_client_unref(c);
103 }
104
105 static void connect_fixed_cb(struct pa_mainloop_api *m, struct pa_defer_event *e, void *userdata) {
106 struct pa_socket_client *c = userdata;
107 assert(m && c && c->defer_event == e);
108 m->defer_free(c->defer_event);
109 c->defer_event = NULL;
110 do_call(c);
111 }
112
113 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) {
114 struct pa_socket_client *c = userdata;
115 assert(m && c && c->io_event == e && fd >= 0);
116 m->io_free(c->io_event);
117 c->io_event = NULL;
118 do_call(c);
119 }
120
121 static int do_connect(struct pa_socket_client *c, const struct sockaddr *sa, socklen_t len) {
122 int r;
123 assert(c && sa && len);
124
125 pa_make_nonblock_fd(c->fd);
126
127 if ((r = connect(c->fd, sa, len)) < 0) {
128 if (errno != EINPROGRESS) {
129 /*pa_log(__FILE__": connect(): %s\n", strerror(errno));*/
130 return -1;
131 }
132
133 c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c);
134 assert(c->io_event);
135 } else {
136 c->defer_event = c->mainloop->defer_new(c->mainloop, connect_fixed_cb, c);
137 assert(c->defer_event);
138 }
139
140 return 0;
141 }
142
143 struct pa_socket_client* pa_socket_client_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port) {
144 struct pa_socket_client *c;
145 struct sockaddr_in sa;
146 assert(m && address && port);
147
148 c = pa_socket_client_new(m);
149 assert(c);
150
151 if ((c->fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
152 pa_log(__FILE__": socket(): %s\n", strerror(errno));
153 goto fail;
154 }
155
156 pa_fd_set_cloexec(c->fd, 1);
157 pa_socket_tcp_low_delay(c->fd);
158
159 sa.sin_family = AF_INET;
160 sa.sin_port = htons(port);
161 sa.sin_addr.s_addr = htonl(address);
162
163 if (do_connect(c, (struct sockaddr*) &sa, sizeof(sa)) < 0)
164 goto fail;
165
166 return c;
167
168 fail:
169 pa_socket_client_unref(c);
170 return NULL;
171 }
172
173 struct pa_socket_client* pa_socket_client_new_unix(struct pa_mainloop_api *m, const char *filename) {
174 struct pa_socket_client *c;
175 struct sockaddr_un sa;
176 assert(m && filename);
177
178 c = pa_socket_client_new(m);
179 assert(c);
180
181 if ((c->fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
182 pa_log(__FILE__": socket(): %s\n", strerror(errno));
183 goto fail;
184 }
185
186 pa_fd_set_cloexec(c->fd, 1);
187 pa_socket_low_delay(c->fd);
188
189 sa.sun_family = AF_LOCAL;
190 strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1);
191 sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
192
193 if (do_connect(c, (struct sockaddr*) &sa, sizeof(sa)) < 0)
194 goto fail;
195
196 return c;
197
198 fail:
199 pa_socket_client_unref(c);
200 return NULL;
201 }
202
203 struct pa_socket_client* pa_socket_client_new_sockaddr(struct pa_mainloop_api *m, const struct sockaddr *sa, size_t salen) {
204 struct pa_socket_client *c;
205 assert(m && sa);
206 c = pa_socket_client_new(m);
207 assert(c);
208
209 if ((c->fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) {
210 pa_log(__FILE__": socket(): %s\n", strerror(errno));
211 goto fail;
212 }
213
214 pa_fd_set_cloexec(c->fd, 1);
215 if (sa->sa_family == AF_INET)
216 pa_socket_tcp_low_delay(c->fd);
217 else
218 pa_socket_low_delay(c->fd);
219
220 if (do_connect(c, sa, salen) < 0)
221 goto fail;
222
223 return c;
224
225 fail:
226 pa_socket_client_unref(c);
227 return NULL;
228
229 }
230
231 void socket_client_free(struct pa_socket_client *c) {
232 assert(c && c->mainloop);
233 if (c->io_event)
234 c->mainloop->io_free(c->io_event);
235 if (c->defer_event)
236 c->mainloop->defer_free(c->defer_event);
237 if (c->fd >= 0)
238 close(c->fd);
239 pa_xfree(c);
240 }
241
242 void pa_socket_client_unref(struct pa_socket_client *c) {
243 assert(c && c->ref >= 1);
244
245 if (!(--(c->ref)))
246 socket_client_free(c);
247 }
248
249 struct pa_socket_client* pa_socket_client_ref(struct pa_socket_client *c) {
250 assert(c && c->ref >= 1);
251 c->ref++;
252 return c;
253 }
254
255 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) {
256 assert(c);
257 c->callback = on_connection;
258 c->userdata = userdata;
259 }