various modernizations
[profile/ivi/pulseaudio.git] / 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 #ifdef HAVE_SYS_SOCKET_H
32 #include <sys/socket.h>
33 #endif
34 #ifdef HAVE_SYS_UN_H
35 #include <sys/un.h>
36 #endif
37
38 #include "winsock.h"
39
40 #include <pulse/xmalloc.h>
41
42 #include <pulsecore/core-error.h>
43 #include <pulsecore/core-util.h>
44 #include <pulsecore/socket-util.h>
45 #include <pulsecore/log.h>
46 #include <pulsecore/macro.h>
47
48 #include "iochannel.h"
49
50 struct pa_iochannel {
51     int ifd, ofd;
52     int ifd_type, ofd_type;
53     pa_mainloop_api* mainloop;
54
55     pa_iochannel_cb_t callback;
56     void*userdata;
57
58     pa_bool_t readable:1;
59     pa_bool_t writable:1;
60     pa_bool_t hungup:1;
61     pa_bool_t no_close:1;
62
63     pa_io_event* input_event, *output_event;
64 };
65
66 static void callback(pa_mainloop_api* m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata);
67
68 static void delete_events(pa_iochannel *io) {
69     pa_assert(io);
70
71     if (io->input_event)
72         io->mainloop->io_free(io->input_event);
73
74     if (io->output_event && io->output_event != io->input_event)
75         io->mainloop->io_free(io->output_event);
76
77     io->input_event = io->output_event = NULL;
78 }
79
80 static void enable_events(pa_iochannel *io) {
81     pa_assert(io);
82
83     if (io->hungup) {
84         delete_events(io);
85         return;
86     }
87
88     if (io->ifd == io->ofd && io->ifd >= 0) {
89         pa_io_event_flags_t f = PA_IO_EVENT_NULL;
90
91         if (!io->readable)
92             f |= PA_IO_EVENT_INPUT;
93         if (!io->writable)
94             f |= PA_IO_EVENT_OUTPUT;
95
96         pa_assert(io->input_event == io->output_event);
97
98         if (f != PA_IO_EVENT_NULL) {
99             if (io->input_event)
100                 io->mainloop->io_enable(io->input_event, f);
101             else
102                 io->input_event = io->output_event = io->mainloop->io_new(io->mainloop, io->ifd, f, callback, io);
103         } else
104             delete_events(io);
105
106     } else {
107
108         if (io->ifd >= 0) {
109             if (!io->readable) {
110                 if (io->input_event)
111                     io->mainloop->io_enable(io->input_event, PA_IO_EVENT_INPUT);
112                 else
113                     io->input_event = io->mainloop->io_new(io->mainloop, io->ifd, PA_IO_EVENT_INPUT, callback, io);
114             } else if (io->input_event) {
115                 io->mainloop->io_free(io->input_event);
116                 io->input_event = NULL;
117             }
118         }
119
120         if (io->ofd >= 0) {
121             if (!io->writable) {
122                 if (io->output_event)
123                     io->mainloop->io_enable(io->output_event, PA_IO_EVENT_OUTPUT);
124                 else
125                     io->output_event = io->mainloop->io_new(io->mainloop, io->ofd, PA_IO_EVENT_OUTPUT, callback, io);
126             } else if (io->input_event) {
127                 io->mainloop->io_free(io->output_event);
128                 io->output_event = NULL;
129             }
130         }
131     }
132 }
133
134 static void callback(pa_mainloop_api* m, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
135     pa_iochannel *io = userdata;
136     pa_bool_t changed = FALSE;
137
138     pa_assert(m);
139     pa_assert(e);
140     pa_assert(fd >= 0);
141     pa_assert(userdata);
142
143     if ((f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) && !io->hungup) {
144         io->hungup = TRUE;
145         changed = TRUE;
146     }
147
148     if ((f & PA_IO_EVENT_INPUT) && !io->readable) {
149         io->readable = TRUE;
150         changed = TRUE;
151         pa_assert(e == io->input_event);
152     }
153
154     if ((f & PA_IO_EVENT_OUTPUT) && !io->writable) {
155         io->writable = TRUE;
156         changed = TRUE;
157         pa_assert(e == io->output_event);
158     }
159
160     if (changed) {
161         enable_events(io);
162
163         if (io->callback)
164             io->callback(io, io->userdata);
165     }
166 }
167
168 pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) {
169     pa_iochannel *io;
170
171     pa_assert(m);
172     pa_assert(ifd >= 0 || ofd >= 0);
173
174     io = pa_xnew0(pa_iochannel, 1);
175     io->ifd = ifd;
176     io->ofd = ofd;
177     io->mainloop = m;
178
179     if (io->ifd >= 0)
180         pa_make_fd_nonblock(io->ifd);
181
182     if (io->ofd >= 0 && io->ofd != io->ifd)
183         pa_make_fd_nonblock(io->ofd);
184
185     enable_events(io);
186     return io;
187 }
188
189 void pa_iochannel_free(pa_iochannel*io) {
190     pa_assert(io);
191
192     delete_events(io);
193
194     if (!io->no_close) {
195         if (io->ifd >= 0)
196             pa_close(io->ifd);
197         if (io->ofd >= 0 && io->ofd != io->ifd)
198             pa_close(io->ofd);
199     }
200
201     pa_xfree(io);
202 }
203
204 pa_bool_t pa_iochannel_is_readable(pa_iochannel*io) {
205     pa_assert(io);
206
207     return io->readable || io->hungup;
208 }
209
210 pa_bool_t pa_iochannel_is_writable(pa_iochannel*io) {
211     pa_assert(io);
212
213     return io->writable && !io->hungup;
214 }
215
216 pa_bool_t pa_iochannel_is_hungup(pa_iochannel*io) {
217     pa_assert(io);
218
219     return io->hungup;
220 }
221
222 ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l) {
223     ssize_t r;
224
225     pa_assert(io);
226     pa_assert(data);
227     pa_assert(l);
228     pa_assert(io->ofd >= 0);
229
230     if ((r = pa_write(io->ofd, data, l, &io->ofd_type)) >= 0) {
231         io->writable = io->hungup = FALSE;
232         enable_events(io);
233     }
234
235     return r;
236 }
237
238 ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) {
239     ssize_t r;
240
241     pa_assert(io);
242     pa_assert(data);
243     pa_assert(io->ifd >= 0);
244
245     if ((r = pa_read(io->ifd, data, l, &io->ifd_type)) >= 0) {
246
247         /* We also reset the hangup flag here to ensure that another
248          * IO callback is triggered so that we will again call into
249          * user code */
250         io->readable = io->hungup = FALSE;
251         enable_events(io);
252     }
253
254     return r;
255 }
256
257 #ifdef HAVE_CREDS
258
259 pa_bool_t pa_iochannel_creds_supported(pa_iochannel *io) {
260     struct {
261         struct sockaddr sa;
262         struct sockaddr_un un;
263         struct sockaddr_storage storage;
264     } sa;
265
266     socklen_t l;
267
268     pa_assert(io);
269     pa_assert(io->ifd >= 0);
270     pa_assert(io->ofd == io->ifd);
271
272     l = sizeof(sa);
273     if (getsockname(io->ifd, &sa.sa, &l) < 0)
274         return FALSE;
275
276     return sa.sa.sa_family == AF_UNIX;
277 }
278
279 int pa_iochannel_creds_enable(pa_iochannel *io) {
280     int t = 1;
281
282     pa_assert(io);
283     pa_assert(io->ifd >= 0);
284
285     if (setsockopt(io->ifd, SOL_SOCKET, SO_PASSCRED, &t, sizeof(t)) < 0) {
286         pa_log_error("setsockopt(SOL_SOCKET, SO_PASSCRED): %s", pa_cstrerror(errno));
287         return -1;
288     }
289
290     return 0;
291 }
292
293 ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l, const pa_creds *ucred) {
294     ssize_t r;
295     struct msghdr mh;
296     struct iovec iov;
297     union {
298         struct cmsghdr hdr;
299         uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
300     } cmsg;
301     struct ucred *u;
302
303     pa_assert(io);
304     pa_assert(data);
305     pa_assert(l);
306     pa_assert(io->ofd >= 0);
307
308     pa_zero(iov);
309     iov.iov_base = (void*) data;
310     iov.iov_len = l;
311
312     pa_zero(cmsg);
313     cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(struct ucred));
314     cmsg.hdr.cmsg_level = SOL_SOCKET;
315     cmsg.hdr.cmsg_type = SCM_CREDENTIALS;
316
317     u = (struct ucred*) CMSG_DATA(&cmsg.hdr);
318
319     u->pid = getpid();
320     if (ucred) {
321         u->uid = ucred->uid;
322         u->gid = ucred->gid;
323     } else {
324         u->uid = getuid();
325         u->gid = getgid();
326     }
327
328     pa_zero(mh);
329     mh.msg_iov = &iov;
330     mh.msg_iovlen = 1;
331     mh.msg_control = &cmsg;
332     mh.msg_controllen = sizeof(cmsg);
333
334     if ((r = sendmsg(io->ofd, &mh, MSG_NOSIGNAL)) >= 0) {
335         io->writable = io->hungup = FALSE;
336         enable_events(io);
337     }
338
339     return r;
340 }
341
342 ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_creds *creds, pa_bool_t *creds_valid) {
343     ssize_t r;
344     struct msghdr mh;
345     struct iovec iov;
346     union {
347         struct cmsghdr hdr;
348         uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
349     } cmsg;
350
351     pa_assert(io);
352     pa_assert(data);
353     pa_assert(l);
354     pa_assert(io->ifd >= 0);
355     pa_assert(creds);
356     pa_assert(creds_valid);
357
358     pa_zero(iov);
359     iov.iov_base = data;
360     iov.iov_len = l;
361
362     pa_zero(cmsg);
363     pa_zero(mh);
364     mh.msg_iov = &iov;
365     mh.msg_iovlen = 1;
366     mh.msg_control = &cmsg;
367     mh.msg_controllen = sizeof(cmsg);
368
369     if ((r = recvmsg(io->ifd, &mh, 0)) >= 0) {
370         struct cmsghdr *cmh;
371
372         *creds_valid = FALSE;
373
374         for (cmh = CMSG_FIRSTHDR(&mh); cmh; cmh = CMSG_NXTHDR(&mh, cmh)) {
375
376             if (cmh->cmsg_level == SOL_SOCKET && cmh->cmsg_type == SCM_CREDENTIALS) {
377                 struct ucred u;
378                 pa_assert(cmh->cmsg_len == CMSG_LEN(sizeof(struct ucred)));
379                 memcpy(&u, CMSG_DATA(cmh), sizeof(struct ucred));
380
381                 creds->gid = u.gid;
382                 creds->uid = u.uid;
383                 *creds_valid = TRUE;
384                 break;
385             }
386         }
387
388         io->readable = io->hungup = FALSE;
389         enable_events(io);
390     }
391
392     return r;
393 }
394
395 #endif /* HAVE_CREDS */
396
397 void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_cb_t _callback, void *userdata) {
398     pa_assert(io);
399
400     io->callback = _callback;
401     io->userdata = userdata;
402 }
403
404 void pa_iochannel_set_noclose(pa_iochannel*io, pa_bool_t b) {
405     pa_assert(io);
406
407     io->no_close = !!b;
408 }
409
410 void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l) {
411     pa_assert(io);
412     pa_assert(s);
413     pa_assert(l);
414
415     pa_socket_peer_to_string(io->ifd, s, l);
416 }
417
418 int pa_iochannel_socket_set_rcvbuf(pa_iochannel *io, size_t l) {
419     pa_assert(io);
420
421     return pa_socket_set_rcvbuf(io->ifd, l);
422 }
423
424 int pa_iochannel_socket_set_sndbuf(pa_iochannel *io, size_t l) {
425     pa_assert(io);
426
427     return pa_socket_set_sndbuf(io->ofd, l);
428 }
429
430 pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io) {
431     pa_assert(io);
432
433     return io->mainloop;
434 }
435
436 int pa_iochannel_get_recv_fd(pa_iochannel *io) {
437     pa_assert(io);
438
439     return io->ifd;
440 }
441
442 int pa_iochannel_get_send_fd(pa_iochannel *io) {
443     pa_assert(io);
444
445     return io->ofd;
446 }
447
448 pa_bool_t pa_iochannel_socket_is_local(pa_iochannel *io) {
449     pa_assert(io);
450
451     if (pa_socket_is_local(io->ifd))
452         return TRUE;
453
454     if (io->ifd != io->ofd)
455         if (pa_socket_is_local(io->ofd))
456             return TRUE;
457
458     return FALSE;
459 }