Merge commit 'origin/master-tx'
[platform/upstream/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
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 }