get rid of svn $ keywords
[pulseaudio-mirror.git] / src / pulsecore / iochannel.c
blobb40c9815643db5a524b14c3714eda2d875aeda65
1 /***
2 This file is part of PulseAudio.
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
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.
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.
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 ***/
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <stdlib.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <errno.h>
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
39 #include "winsock.h"
41 #include <pulse/xmalloc.h>
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>
49 #include "iochannel.h"
51 struct pa_iochannel {
52 int ifd, ofd;
53 int ifd_type, ofd_type;
54 pa_mainloop_api* mainloop;
56 pa_iochannel_cb_t callback;
57 void*userdata;
59 pa_bool_t readable;
60 pa_bool_t writable;
61 pa_bool_t hungup;
63 pa_bool_t no_close;
65 pa_io_event* input_event, *output_event;
68 static void enable_mainloop_sources(pa_iochannel *io) {
69 pa_assert(io);
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);
75 if (!io->readable)
76 f |= PA_IO_EVENT_INPUT;
77 if (!io->writable)
78 f |= PA_IO_EVENT_OUTPUT;
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);
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;
93 pa_assert(m);
94 pa_assert(e);
95 pa_assert(fd >= 0);
96 pa_assert(userdata);
98 if ((f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) && !io->hungup) {
99 io->hungup = TRUE;
100 changed = TRUE;
103 if ((f & PA_IO_EVENT_INPUT) && !io->readable) {
104 io->readable = TRUE;
105 changed = TRUE;
106 pa_assert(e == io->input_event);
109 if ((f & PA_IO_EVENT_OUTPUT) && !io->writable) {
110 io->writable = TRUE;
111 changed = TRUE;
112 pa_assert(e == io->output_event);
115 if (changed) {
116 enable_mainloop_sources(io);
118 if (io->callback)
119 io->callback(io, io->userdata);
123 pa_iochannel* pa_iochannel_new(pa_mainloop_api*m, int ifd, int ofd) {
124 pa_iochannel *io;
126 pa_assert(m);
127 pa_assert(ifd >= 0 || ofd >= 0);
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;
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;
142 io->input_event = io->output_event = NULL;
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 {
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);
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);
161 return io;
164 void pa_iochannel_free(pa_iochannel*io) {
165 pa_assert(io);
167 if (io->input_event)
168 io->mainloop->io_free(io->input_event);
170 if (io->output_event && (io->output_event != io->input_event))
171 io->mainloop->io_free(io->output_event);
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);
180 pa_xfree(io);
183 pa_bool_t pa_iochannel_is_readable(pa_iochannel*io) {
184 pa_assert(io);
186 return io->readable || io->hungup;
189 pa_bool_t pa_iochannel_is_writable(pa_iochannel*io) {
190 pa_assert(io);
192 return io->writable && !io->hungup;
195 pa_bool_t pa_iochannel_is_hungup(pa_iochannel*io) {
196 pa_assert(io);
198 return io->hungup;
201 ssize_t pa_iochannel_write(pa_iochannel*io, const void*data, size_t l) {
202 ssize_t r;
204 pa_assert(io);
205 pa_assert(data);
206 pa_assert(l);
207 pa_assert(io->ofd >= 0);
209 if ((r = pa_write(io->ofd, data, l, &io->ofd_type)) >= 0) {
210 io->writable = FALSE;
211 enable_mainloop_sources(io);
214 return r;
217 ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) {
218 ssize_t r;
220 pa_assert(io);
221 pa_assert(data);
222 pa_assert(io->ifd >= 0);
224 if ((r = pa_read(io->ifd, data, l, &io->ifd_type)) >= 0) {
225 io->readable = FALSE;
226 enable_mainloop_sources(io);
229 return r;
232 #ifdef HAVE_CREDS
234 pa_bool_t pa_iochannel_creds_supported(pa_iochannel *io) {
235 struct sockaddr_un sa;
236 socklen_t l;
238 pa_assert(io);
239 pa_assert(io->ifd >= 0);
240 pa_assert(io->ofd == io->ifd);
242 l = sizeof(sa);
244 if (getsockname(io->ifd, (struct sockaddr*) &sa, &l) < 0)
245 return 0;
247 return sa.sun_family == AF_UNIX;
250 int pa_iochannel_creds_enable(pa_iochannel *io) {
251 int t = 1;
253 pa_assert(io);
254 pa_assert(io->ifd >= 0);
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;
261 return 0;
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;
274 pa_assert(io);
275 pa_assert(data);
276 pa_assert(l);
277 pa_assert(io->ofd >= 0);
279 memset(&iov, 0, sizeof(iov));
280 iov.iov_base = (void*) data;
281 iov.iov_len = l;
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;
288 u = (struct ucred*) CMSG_DATA(&cmsg.hdr);
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();
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;
308 if ((r = sendmsg(io->ofd, &mh, MSG_NOSIGNAL)) >= 0) {
309 io->writable = FALSE;
310 enable_mainloop_sources(io);
313 return r;
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;
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);
332 memset(&iov, 0, sizeof(iov));
333 iov.iov_base = data;
334 iov.iov_len = l;
336 memset(&cmsg, 0, sizeof(cmsg));
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;
347 if ((r = recvmsg(io->ifd, &mh, 0)) >= 0) {
348 struct cmsghdr *cmh;
350 *creds_valid = 0;
352 for (cmh = CMSG_FIRSTHDR(&mh); cmh; cmh = CMSG_NXTHDR(&mh, cmh)) {
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));
359 creds->gid = u.gid;
360 creds->uid = u.uid;
361 *creds_valid = TRUE;
362 break;
366 io->readable = FALSE;
367 enable_mainloop_sources(io);
370 return r;
373 #endif /* HAVE_CREDS */
375 void pa_iochannel_set_callback(pa_iochannel*io, pa_iochannel_cb_t _callback, void *userdata) {
376 pa_assert(io);
378 io->callback = _callback;
379 io->userdata = userdata;
382 void pa_iochannel_set_noclose(pa_iochannel*io, pa_bool_t b) {
383 pa_assert(io);
385 io->no_close = !!b;
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);
393 pa_socket_peer_to_string(io->ifd, s, l);
396 int pa_iochannel_socket_set_rcvbuf(pa_iochannel *io, size_t l) {
397 pa_assert(io);
399 return pa_socket_set_rcvbuf(io->ifd, l);
402 int pa_iochannel_socket_set_sndbuf(pa_iochannel *io, size_t l) {
403 pa_assert(io);
405 return pa_socket_set_sndbuf(io->ofd, l);
408 pa_mainloop_api* pa_iochannel_get_mainloop_api(pa_iochannel *io) {
409 pa_assert(io);
411 return io->mainloop;
414 int pa_iochannel_get_recv_fd(pa_iochannel *io) {
415 pa_assert(io);
417 return io->ifd;
420 int pa_iochannel_get_send_fd(pa_iochannel *io) {
421 pa_assert(io);
423 return io->ofd;
426 pa_bool_t pa_iochannel_socket_is_local(pa_iochannel *io) {
427 pa_assert(io);
429 if (pa_socket_is_local(io->ifd))
430 return TRUE;
432 if (io->ifd != io->ofd)
433 if (pa_socket_is_local(io->ofd))
434 return TRUE;
436 return FALSE;