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