modernize pa_iochannel a bit, add pa_iochannel_get_send_fd()
[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     uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))];
271     struct ucred *u;
272     struct cmsghdr *cmsg;
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_data, 0, sizeof(cmsg_data));
284     cmsg = (struct cmsghdr*)  cmsg_data;
285     cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
286     cmsg->cmsg_level = SOL_SOCKET;
287     cmsg->cmsg_type = SCM_CREDENTIALS;
288
289     u = (struct ucred*) CMSG_DATA(cmsg);
290
291     u->pid = getpid();
292     if (ucred) {
293         u->uid = ucred->uid;
294         u->gid = ucred->gid;
295     } else {
296         u->uid = getuid();
297         u->gid = getgid();
298     }
299
300     memset(&mh, 0, sizeof(mh));
301     mh.msg_name = NULL;
302     mh.msg_namelen = 0;
303     mh.msg_iov = &iov;
304     mh.msg_iovlen = 1;
305     mh.msg_control = cmsg_data;
306     mh.msg_controllen = sizeof(cmsg_data);
307     mh.msg_flags = 0;
308
309     if ((r = sendmsg(io->ofd, &mh, MSG_NOSIGNAL)) >= 0) {
310         io->writable = FALSE;
311         enable_mainloop_sources(io);
312     }
313
314     return r;
315 }
316
317 ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_creds *creds, pa_bool_t *creds_valid) {
318     ssize_t r;
319     struct msghdr mh;
320     struct iovec iov;
321     uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))];
322
323     pa_assert(io);
324     pa_assert(data);
325     pa_assert(l);
326     pa_assert(io->ifd >= 0);
327     pa_assert(creds);
328     pa_assert(creds_valid);
329
330     memset(&iov, 0, sizeof(iov));
331     iov.iov_base = data;
332     iov.iov_len = l;
333
334     memset(cmsg_data, 0, sizeof(cmsg_data));
335
336     memset(&mh, 0, sizeof(mh));
337     mh.msg_name = NULL;
338     mh.msg_namelen = 0;
339     mh.msg_iov = &iov;
340     mh.msg_iovlen = 1;
341     mh.msg_control = cmsg_data;
342     mh.msg_controllen = sizeof(cmsg_data);
343     mh.msg_flags = 0;
344
345     if ((r = recvmsg(io->ifd, &mh, 0)) >= 0) {
346         struct cmsghdr *cmsg;
347
348         *creds_valid = 0;
349
350         for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
351
352             if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) {
353                 struct ucred u;
354                 pa_assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)));
355                 memcpy(&u, CMSG_DATA(cmsg), sizeof(struct ucred));
356
357                 creds->gid = u.gid;
358                 creds->uid = u.uid;
359                 *creds_valid = TRUE;
360                 break;
361             }
362         }
363
364         io->readable = FALSE;
365         enable_mainloop_sources(io);
366     }
367
368     return r;
369 }
370
371 #endif /* HAVE_CREDS */
372
373 void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_cb_t _callback, void *userdata) {
374     pa_assert(io);
375
376     io->callback = _callback;
377     io->userdata = userdata;
378 }
379
380 void pa_iochannel_set_noclose(pa_iochannel*io, pa_bool_t b) {
381     pa_assert(io);
382
383     io->no_close = !!b;
384 }
385
386 void pa_iochannel_socket_peer_to_string(pa_iochannel*io, char*s, size_t l) {
387     pa_assert(io);
388     pa_assert(s);
389     pa_assert(l);
390
391     pa_socket_peer_to_string(io->ifd, s, l);
392 }
393
394 int pa_iochannel_socket_set_rcvbuf(pa_iochannel *io, size_t l) {
395     pa_assert(io);
396
397     return pa_socket_set_rcvbuf(io->ifd, l);
398 }
399
400 int pa_iochannel_socket_set_sndbuf(pa_iochannel *io, size_t l) {
401     pa_assert(io);
402
403     return pa_socket_set_sndbuf(io->ofd, l);
404 }
405
406 pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io) {
407     pa_assert(io);
408
409     return io->mainloop;
410 }
411
412 int pa_iochannel_get_recv_fd(pa_iochannel *io) {
413     pa_assert(io);
414
415     return io->ifd;
416 }
417
418 int pa_iochannel_get_send_fd(pa_iochannel *io) {
419     pa_assert(io);
420
421     return io->ofd;
422 }