]> code.delx.au - pulseaudio/blob - src/pulsecore/iochannel.c
Merge branch 'master' of git://0pointer.de/pulseaudio into dbus-work
[pulseaudio] / src / pulsecore / iochannel.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as
9 published by the Free Software Foundation; either version 2.1 of the
10 License, or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdlib.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <errno.h>
31
32 #ifdef HAVE_SYS_SOCKET_H
33 #include <sys/socket.h>
34 #endif
35 #ifdef HAVE_SYS_UN_H
36 #include <sys/un.h>
37 #endif
38
39 #include "winsock.h"
40
41 #include <pulse/xmalloc.h>
42
43 #include <pulsecore/core-error.h>
44 #include <pulsecore/core-util.h>
45 #include <pulsecore/socket-util.h>
46 #include <pulsecore/log.h>
47 #include <pulsecore/macro.h>
48
49 #include "iochannel.h"
50
51 struct pa_iochannel {
52 int ifd, ofd;
53 int ifd_type, ofd_type;
54 pa_mainloop_api* mainloop;
55
56 pa_iochannel_cb_t callback;
57 void*userdata;
58
59 pa_bool_t readable;
60 pa_bool_t writable;
61 pa_bool_t hungup;
62
63 pa_bool_t no_close;
64
65 pa_io_event* input_event, *output_event;
66 };
67
68 static void enable_mainloop_sources(pa_iochannel *io) {
69 pa_assert(io);
70
71 if (io->input_event == io->output_event && io->input_event) {
72 pa_io_event_flags_t f = PA_IO_EVENT_NULL;
73 pa_assert(io->input_event);
74
75 if (!io->readable)
76 f |= PA_IO_EVENT_INPUT;
77 if (!io->writable)
78 f |= PA_IO_EVENT_OUTPUT;
79
80 io->mainloop->io_enable(io->input_event, f);
81 } else {
82 if (io->input_event)
83 io->mainloop->io_enable(io->input_event, io->readable ? PA_IO_EVENT_NULL : PA_IO_EVENT_INPUT);
84 if (io->output_event)
85 io->mainloop->io_enable(io->output_event, io->writable ? PA_IO_EVENT_NULL : PA_IO_EVENT_OUTPUT);
86 }
87 }
88
89 static void callback(pa_mainloop_api* m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
90 pa_iochannel *io = userdata;
91 pa_bool_t changed = FALSE;
92
93 pa_assert(m);
94 pa_assert(e);
95 pa_assert(fd >= 0);
96 pa_assert(userdata);
97
98 if ((f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) && !io->hungup) {
99 io->hungup = TRUE;
100 changed = TRUE;
101 }
102
103 if ((f & PA_IO_EVENT_INPUT) && !io->readable) {
104 io->readable = TRUE;
105 changed = TRUE;
106 pa_assert(e == io->input_event);
107 }
108
109 if ((f & PA_IO_EVENT_OUTPUT) && !io->writable) {
110 io->writable = TRUE;
111 changed = TRUE;
112 pa_assert(e == io->output_event);
113 }
114
115 if (changed) {
116 enable_mainloop_sources(io);
117
118 if (io->callback)
119 io->callback(io, io->userdata);
120 }
121 }
122
123 pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) {
124 pa_iochannel *io;
125
126 pa_assert(m);
127 pa_assert(ifd >= 0 || ofd >= 0);
128
129 io = pa_xnew(pa_iochannel, 1);
130 io->ifd = ifd;
131 io->ofd = ofd;
132 io->ifd_type = io->ofd_type = 0;
133 io->mainloop = m;
134
135 io->userdata = NULL;
136 io->callback = NULL;
137 io->readable = FALSE;
138 io->writable = FALSE;
139 io->hungup = FALSE;
140 io->no_close = FALSE;
141
142 io->input_event = io->output_event = NULL;
143
144 if (ifd == ofd) {
145 pa_assert(ifd >= 0);
146 pa_make_fd_nonblock(io->ifd);
147 io->input_event = io->output_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT|PA_IO_EVENT_OUTPUT, callback, io);
148 } else {
149
150 if (ifd >= 0) {
151 pa_make_fd_nonblock(io->ifd);
152 io->input_event = m->io_new(m, ifd, PA_IO_EVENT_INPUT, callback, io);
153 }
154
155 if (ofd >= 0) {
156 pa_make_fd_nonblock(io->ofd);
157 io->output_event = m->io_new(m, ofd, PA_IO_EVENT_OUTPUT, callback, io);
158 }
159 }
160
161 return io;
162 }
163
164 void pa_iochannel_free(pa_iochannel*io) {
165 pa_assert(io);
166
167 if (io->input_event)
168 io->mainloop->io_free(io->input_event);
169
170 if (io->output_event && (io->output_event != io->input_event))
171 io->mainloop->io_free(io->output_event);
172
173 if (!io->no_close) {
174 if (io->ifd >= 0)
175 pa_close(io->ifd);
176 if (io->ofd >= 0 && io->ofd != io->ifd)
177 pa_close(io->ofd);
178 }
179
180 pa_xfree(io);
181 }
182
183 pa_bool_t pa_iochannel_is_readable(pa_iochannel*io) {
184 pa_assert(io);
185
186 return io->readable || io->hungup;
187 }
188
189 pa_bool_t pa_iochannel_is_writable(pa_iochannel*io) {
190 pa_assert(io);
191
192 return io->writable && !io->hungup;
193 }
194
195 pa_bool_t pa_iochannel_is_hungup(pa_iochannel*io) {
196 pa_assert(io);
197
198 return io->hungup;
199 }
200
201 ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l) {
202 ssize_t r;
203
204 pa_assert(io);
205 pa_assert(data);
206 pa_assert(l);
207 pa_assert(io->ofd >= 0);
208
209 if ((r = pa_write(io->ofd, data, l, &io->ofd_type)) >= 0) {
210 io->writable = FALSE;
211 enable_mainloop_sources(io);
212 }
213
214 return r;
215 }
216
217 ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) {
218 ssize_t r;
219
220 pa_assert(io);
221 pa_assert(data);
222 pa_assert(io->ifd >= 0);
223
224 if ((r = pa_read(io->ifd, data, l, &io->ifd_type)) >= 0) {
225 io->readable = FALSE;
226 enable_mainloop_sources(io);
227 }
228
229 return r;
230 }
231
232 #ifdef HAVE_CREDS
233
234 pa_bool_t pa_iochannel_creds_supported(pa_iochannel *io) {
235 struct sockaddr_un sa;
236 socklen_t l;
237
238 pa_assert(io);
239 pa_assert(io->ifd >= 0);
240 pa_assert(io->ofd == io->ifd);
241
242 l = sizeof(sa);
243
244 if (getsockname(io->ifd, (struct sockaddr*) &sa, &l) < 0)
245 return 0;
246
247 return sa.sun_family == AF_UNIX;
248 }
249
250 int pa_iochannel_creds_enable(pa_iochannel *io) {
251 int t = 1;
252
253 pa_assert(io);
254 pa_assert(io->ifd >= 0);
255
256 if (setsockopt(io->ifd, SOL_SOCKET, SO_PASSCRED, &t, sizeof(t)) < 0) {
257 pa_log_error("setsockopt(SOL_SOCKET, SO_PASSCRED): %s", pa_cstrerror(errno));
258 return -1;
259 }
260
261 return 0;
262 }
263
264 ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l, const pa_creds *ucred) {
265 ssize_t r;
266 struct msghdr mh;
267 struct iovec iov;
268 union {
269 struct cmsghdr hdr;
270 uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
271 } cmsg;
272 struct ucred *u;
273
274 pa_assert(io);
275 pa_assert(data);
276 pa_assert(l);
277 pa_assert(io->ofd >= 0);
278
279 memset(&iov, 0, sizeof(iov));
280 iov.iov_base = (void*) data;
281 iov.iov_len = l;
282
283 memset(&cmsg, 0, sizeof(cmsg));
284 cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(struct ucred));
285 cmsg.hdr.cmsg_level = SOL_SOCKET;
286 cmsg.hdr.cmsg_type = SCM_CREDENTIALS;
287
288 u = (struct ucred*) CMSG_DATA(&cmsg.hdr);
289
290 u->pid = getpid();
291 if (ucred) {
292 u->uid = ucred->uid;
293 u->gid = ucred->gid;
294 } else {
295 u->uid = getuid();
296 u->gid = getgid();
297 }
298
299 memset(&mh, 0, sizeof(mh));
300 mh.msg_name = NULL;
301 mh.msg_namelen = 0;
302 mh.msg_iov = &iov;
303 mh.msg_iovlen = 1;
304 mh.msg_control = &cmsg;
305 mh.msg_controllen = sizeof(cmsg);
306 mh.msg_flags = 0;
307
308 if ((r = sendmsg(io->ofd, &mh, MSG_NOSIGNAL)) >= 0) {
309 io->writable = FALSE;
310 enable_mainloop_sources(io);
311 }
312
313 return r;
314 }
315
316 ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_creds *creds, pa_bool_t *creds_valid) {
317 ssize_t r;
318 struct msghdr mh;
319 struct iovec iov;
320 union {
321 struct cmsghdr hdr;
322 uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
323 } cmsg;
324
325 pa_assert(io);
326 pa_assert(data);
327 pa_assert(l);
328 pa_assert(io->ifd >= 0);
329 pa_assert(creds);
330 pa_assert(creds_valid);
331
332 memset(&iov, 0, sizeof(iov));
333 iov.iov_base = data;
334 iov.iov_len = l;
335
336 memset(&cmsg, 0, sizeof(cmsg));
337
338 memset(&mh, 0, sizeof(mh));
339 mh.msg_name = NULL;
340 mh.msg_namelen = 0;
341 mh.msg_iov = &iov;
342 mh.msg_iovlen = 1;
343 mh.msg_control = &cmsg;
344 mh.msg_controllen = sizeof(cmsg);
345 mh.msg_flags = 0;
346
347 if ((r = recvmsg(io->ifd, &mh, 0)) >= 0) {
348 struct cmsghdr *cmh;
349
350 *creds_valid = 0;
351
352 for (cmh = CMSG_FIRSTHDR(&mh); cmh; cmh = CMSG_NXTHDR(&mh, cmh)) {
353
354 if (cmh->cmsg_level == SOL_SOCKET && cmh->cmsg_type == SCM_CREDENTIALS) {
355 struct ucred u;
356 pa_assert(cmh->cmsg_len == CMSG_LEN(sizeof(struct ucred)));
357 memcpy(&u, CMSG_DATA(cmh), sizeof(struct ucred));
358
359 creds->gid = u.gid;
360 creds->uid = u.uid;
361 *creds_valid = TRUE;
362 break;
363 }
364 }
365
366 io->readable = FALSE;
367 enable_mainloop_sources(io);
368 }
369
370 return r;
371 }
372
373 #endif /* HAVE_CREDS */
374
375 void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_cb_t _callback, void *userdata) {
376 pa_assert(io);
377
378 io->callback = _callback;
379 io->userdata = userdata;
380 }
381
382 void pa_iochannel_set_noclose(pa_iochannel*io, pa_bool_t b) {
383 pa_assert(io);
384
385 io->no_close = !!b;
386 }
387
388 void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l) {
389 pa_assert(io);
390 pa_assert(s);
391 pa_assert(l);
392
393 pa_socket_peer_to_string(io->ifd, s, l);
394 }
395
396 int pa_iochannel_socket_set_rcvbuf(pa_iochannel *io, size_t l) {
397 pa_assert(io);
398
399 return pa_socket_set_rcvbuf(io->ifd, l);
400 }
401
402 int pa_iochannel_socket_set_sndbuf(pa_iochannel *io, size_t l) {
403 pa_assert(io);
404
405 return pa_socket_set_sndbuf(io->ofd, l);
406 }
407
408 pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io) {
409 pa_assert(io);
410
411 return io->mainloop;
412 }
413
414 int pa_iochannel_get_recv_fd(pa_iochannel *io) {
415 pa_assert(io);
416
417 return io->ifd;
418 }
419
420 int pa_iochannel_get_send_fd(pa_iochannel *io) {
421 pa_assert(io);
422
423 return io->ofd;
424 }
425
426 pa_bool_t pa_iochannel_socket_is_local(pa_iochannel *io) {
427 pa_assert(io);
428
429 if (pa_socket_is_local(io->ifd))
430 return TRUE;
431
432 if (io->ifd != io->ofd)
433 if (pa_socket_is_local(io->ofd))
434 return TRUE;
435
436 return FALSE;
437 }