]> code.delx.au - pulseaudio/blob - src/socket-client.c
add dependency script
[pulseaudio] / src / socket-client.c
1 #include <unistd.h>
2 #include <stdio.h>
3 #include <errno.h>
4 #include <string.h>
5 #include <assert.h>
6 #include <stdlib.h>
7 #include <sys/un.h>
8 #include <netinet/in.h>
9 #include <arpa/inet.h>
10
11 #include "socket-client.h"
12 #include "socket-util.h"
13 #include "util.h"
14
15 struct pa_socket_client {
16 struct pa_mainloop_api *mainloop;
17 int fd;
18
19 void *io_source, *fixed_source;
20 void (*callback)(struct pa_socket_client*c, struct pa_iochannel *io, void *userdata);
21 void *userdata;
22 };
23
24 static struct pa_socket_client*pa_socket_client_new(struct pa_mainloop_api *m) {
25 struct pa_socket_client *c;
26 assert(m);
27
28 c = malloc(sizeof(struct pa_socket_client));
29 assert(c);
30 c->mainloop = m;
31 c->fd = -1;
32 c->io_source = c->fixed_source = NULL;
33 c->callback = NULL;
34 c->userdata = NULL;
35 return c;
36 }
37
38 static void do_call(struct pa_socket_client *c) {
39 struct pa_iochannel *io;
40 int error, lerror;
41 assert(c && c->callback);
42
43 lerror = sizeof(error);
44 if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &error, &lerror) < 0) {
45 fprintf(stderr, "getsockopt(): %s\n", strerror(errno));
46 goto failed;
47 }
48
49 if (lerror != sizeof(error)) {
50 fprintf(stderr, "getsocktop() returned invalid size.\n");
51 goto failed;
52 }
53
54 if (error != 0) {
55 fprintf(stderr, "connect(): %s\n", strerror(error));
56 goto failed;
57 }
58
59 io = pa_iochannel_new(c->mainloop, c->fd, c->fd);
60 assert(io);
61 c->fd = -1;
62 c->callback(c, io, c->userdata);
63
64 return;
65
66 failed:
67 close(c->fd);
68 c->fd = -1;
69 c->callback(c, NULL, c->userdata);
70 return;
71 }
72
73 static void connect_fixed_cb(struct pa_mainloop_api *m, void *id, void *userdata) {
74 struct pa_socket_client *c = userdata;
75 assert(m && c && c->fixed_source == id);
76 m->cancel_fixed(m, c->fixed_source);
77 c->fixed_source = NULL;
78 do_call(c);
79 }
80
81 static void connect_io_cb(struct pa_mainloop_api*m, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
82 struct pa_socket_client *c = userdata;
83 assert(m && c && c->io_source == id && fd >= 0 && events == PA_MAINLOOP_API_IO_EVENT_OUTPUT);
84 m->cancel_io(m, c->io_source);
85 c->io_source = NULL;
86 do_call(c);
87 }
88
89 static int do_connect(struct pa_socket_client *c, const struct sockaddr *sa, socklen_t len) {
90 int r;
91 assert(c && sa && len);
92
93 pa_make_nonblock_fd(c->fd);
94
95 if ((r = connect(c->fd, sa, len)) < 0) {
96 if (r != EINPROGRESS) {
97 fprintf(stderr, "connect(): %s\n", strerror(errno));
98 return -1;
99 }
100
101 c->io_source = c->mainloop->source_io(c->mainloop, c->fd, PA_MAINLOOP_API_IO_EVENT_OUTPUT, connect_io_cb, c);
102 assert(c->io_source);
103 } else {
104 c->fixed_source = c->mainloop->source_fixed(c->mainloop, connect_fixed_cb, c);
105 assert(c->fixed_source);
106 }
107
108 return 0;
109 }
110
111 struct pa_socket_client* pa_socket_client_new_ipv4(struct pa_mainloop_api *m, uint32_t address, uint16_t port) {
112 struct pa_socket_client *c;
113 struct sockaddr_in sa;
114 assert(m && address && port);
115
116 c = pa_socket_client_new(m);
117 assert(c);
118
119 if ((c->fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
120 fprintf(stderr, "socket(): %s\n", strerror(errno));
121 goto fail;
122 }
123
124 pa_socket_tcp_low_delay(c->fd);
125
126 sa.sin_family = AF_INET;
127 sa.sin_port = htons(port);
128 sa.sin_addr.s_addr = htonl(address);
129
130 if (do_connect(c, (struct sockaddr*) &sa, sizeof(sa)) < 0)
131 goto fail;
132
133 return c;
134
135 fail:
136 pa_socket_client_free(c);
137 return NULL;
138 }
139
140 struct pa_socket_client* pa_socket_client_new_unix(struct pa_mainloop_api *m, const char *filename) {
141 struct pa_socket_client *c;
142 struct sockaddr_un sa;
143 assert(m && filename);
144
145 c = pa_socket_client_new(m);
146 assert(c);
147
148 if ((c->fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
149 fprintf(stderr, "socket(): %s\n", strerror(errno));
150 goto fail;
151 }
152
153 pa_socket_low_delay(c->fd);
154
155 sa.sun_family = AF_LOCAL;
156 strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1);
157 sa.sun_path[sizeof(sa.sun_path) - 1] = 0;
158
159 if (do_connect(c, (struct sockaddr*) &sa, sizeof(sa)) < 0)
160 goto fail;
161
162 return c;
163
164 fail:
165 pa_socket_client_free(c);
166 return NULL;
167 }
168
169 void pa_socket_client_free(struct pa_socket_client *c) {
170 assert(c && c->mainloop);
171 if (c->io_source)
172 c->mainloop->cancel_io(c->mainloop, c->io_source);
173 if (c->fixed_source)
174 c->mainloop->cancel_fixed(c->mainloop, c->fixed_source);
175 if (c->fd >= 0)
176 close(c->fd);
177 free(c);
178 }
179
180 void pa_socket_client_set_callback(struct pa_socket_client *c, void (*on_connection)(struct pa_socket_client *c, struct pa_iochannel*io, void *userdata), void *userdata) {
181 assert(c);
182 c->callback = on_connection;
183 c->userdata = userdata;
184 }