]> code.delx.au - pulseaudio/blob - src/pulsecore/iochannel.c
6f58ae755ac4639212fb9a47e414fa021733964e
[pulseaudio] / 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 <assert.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <errno.h>
34
35 #ifdef HAVE_SYS_SOCKET_H
36 #include <sys/socket.h>
37 #endif
38 #ifdef HAVE_SYS_UN_H
39 #include <sys/un.h>
40 #endif
41
42 #include "winsock.h"
43
44 #include <pulse/xmalloc.h>
45
46 #include <pulsecore/core-error.h>
47 #include <pulsecore/core-util.h>
48 #include <pulsecore/socket-util.h>
49 #include <pulsecore/log.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 int readable;
62 int writable;
63 int hungup;
64
65 int no_close;
66
67 pa_io_event* input_event, *output_event;
68 };
69
70 static void enable_mainloop_sources(pa_iochannel *io) {
71 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 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 int changed = 0;
94
95 assert(m);
96 assert(e);
97 assert(fd >= 0);
98 assert(userdata);
99
100 if ((f & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) && !io->hungup) {
101 io->hungup = 1;
102 changed = 1;
103 }
104
105 if ((f & PA_IO_EVENT_INPUT) && !io->readable) {
106 io->readable = 1;
107 changed = 1;
108 assert(e == io->input_event);
109 }
110
111 if ((f & PA_IO_EVENT_OUTPUT) && !io->writable) {
112 io->writable = 1;
113 changed = 1;
114 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 assert(m);
129 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 = 0;
140 io->writable = 0;
141 io->hungup = 0;
142 io->no_close = 0;
143
144 io->input_event = io->output_event = NULL;
145
146 if (ifd == ofd) {
147 assert(ifd >= 0);
148 pa_make_nonblock_fd(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_nonblock_fd(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_nonblock_fd(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 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 int pa_iochannel_is_readable(pa_iochannel*io) {
186 assert(io);
187
188 return io->readable || io->hungup;
189 }
190
191 int pa_iochannel_is_writable(pa_iochannel*io) {
192 assert(io);
193
194 return io->writable && !io->hungup;
195 }
196
197 int pa_iochannel_is_hungup(pa_iochannel*io) {
198 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 assert(io);
207 assert(data);
208 assert(l);
209 assert(io->ofd >= 0);
210
211 r = pa_write(io->ofd, data, l, &io->ofd_type);
212 if (r >= 0) {
213 io->writable = 0;
214 enable_mainloop_sources(io);
215 }
216
217 return r;
218 }
219
220 ssize_t pa_iochannel_read(pa_iochannel*io, void*data, size_t l) {
221 ssize_t r;
222
223 assert(io);
224 assert(data);
225 assert(io->ifd >= 0);
226
227 r = pa_read(io->ifd, data, l, &io->ifd_type);
228 if (r >= 0) {
229 io->readable = 0;
230 enable_mainloop_sources(io);
231 }
232
233 return r;
234 }
235
236 #ifdef HAVE_CREDS
237
238 int pa_iochannel_creds_supported(pa_iochannel *io) {
239 struct sockaddr_un sa;
240 socklen_t l;
241
242 assert(io);
243 assert(io->ifd >= 0);
244 assert(io->ofd == io->ifd);
245
246 l = sizeof(sa);
247
248 if (getsockname(io->ifd, (struct sockaddr*) &sa, &l) < 0)
249 return 0;
250
251 return sa.sun_family == AF_UNIX;
252 }
253
254 int pa_iochannel_creds_enable(pa_iochannel *io) {
255 int t = 1;
256
257 assert(io);
258 assert(io->ifd >= 0);
259
260 if (setsockopt(io->ifd, SOL_SOCKET, SO_PASSCRED, &t, sizeof(t)) < 0) {
261 pa_log_error("setsockopt(SOL_SOCKET, SO_PASSCRED): %s", pa_cstrerror(errno));
262 return -1;
263 }
264
265 return 0;
266 }
267
268 ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l, const pa_creds *ucred) {
269 ssize_t r;
270 struct msghdr mh;
271 struct iovec iov;
272 uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))];
273 struct ucred *u;
274 struct cmsghdr *cmsg;
275
276 assert(io);
277 assert(data);
278 assert(l);
279 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_data, 0, sizeof(cmsg_data));
286 cmsg = (struct cmsghdr*) cmsg_data;
287 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
288 cmsg->cmsg_level = SOL_SOCKET;
289 cmsg->cmsg_type = SCM_CREDENTIALS;
290
291 u = (struct ucred*) CMSG_DATA(cmsg);
292
293 u->pid = getpid();
294 if (ucred) {
295 u->uid = ucred->uid;
296 u->gid = ucred->gid;
297 } else {
298 u->uid = getuid();
299 u->gid = getgid();
300 }
301
302 memset(&mh, 0, sizeof(mh));
303 mh.msg_name = NULL;
304 mh.msg_namelen = 0;
305 mh.msg_iov = &iov;
306 mh.msg_iovlen = 1;
307 mh.msg_control = cmsg_data;
308 mh.msg_controllen = sizeof(cmsg_data);
309 mh.msg_flags = 0;
310
311 if ((r = sendmsg(io->ofd, &mh, MSG_NOSIGNAL)) >= 0) {
312 io->writable = 0;
313 enable_mainloop_sources(io);
314 }
315
316 return r;
317 }
318
319 ssize_t pa_iochannel_read_with_creds(pa_iochannel*io, void*data, size_t l, pa_creds *creds, int *creds_valid) {
320 ssize_t r;
321 struct msghdr mh;
322 struct iovec iov;
323 uint8_t cmsg_data[CMSG_SPACE(sizeof(struct ucred))];
324
325 assert(io);
326 assert(data);
327 assert(l);
328 assert(io->ifd >= 0);
329 assert(creds);
330 assert(creds_valid);
331
332 memset(&iov, 0, sizeof(iov));
333 iov.iov_base = data;
334 iov.iov_len = l;
335
336 memset(cmsg_data, 0, sizeof(cmsg_data));
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_data;
344 mh.msg_controllen = sizeof(cmsg_data);
345 mh.msg_flags = 0;
346
347 if ((r = recvmsg(io->ifd, &mh, 0)) >= 0) {
348 struct cmsghdr *cmsg;
349
350 *creds_valid = 0;
351
352 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
353
354 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) {
355 struct ucred u;
356 assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)));
357 memcpy(&u, CMSG_DATA(cmsg), sizeof(struct ucred));
358
359 creds->gid = u.gid;
360 creds->uid = u.uid;
361 *creds_valid = 1;
362 break;
363 }
364 }
365
366 io->readable = 0;
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 assert(io);
377
378 io->callback = _callback;
379 io->userdata = userdata;
380 }
381
382 void pa_iochannel_set_noclose(pa_iochannel*io, int b) {
383 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 assert(io);
390 assert(s);
391 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 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 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 assert(io);
410
411 return io->mainloop;
412 }
413
414 int pa_iochannel_get_recv_fd(pa_iochannel *io) {
415 assert(io);
416
417 return io->ifd;
418 }