output: add _get_plugin()
[libmpdclient.git] / src / async.c
bloba8a8f41175181fdef7e91fcdfff34303e0979a38
1 /* libmpdclient
2 (c) 2003-2017 The Music Player Daemon Project
3 This project's homepage is: http://www.musicpd.org
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
9 - Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
12 - Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
20 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "iasync.h"
30 #include "buffer.h"
31 #include "ierror.h"
32 #include "quote.h"
33 #include "socket.h"
35 #include <mpd/socket.h>
37 #include <assert.h>
38 #include <stdbool.h>
39 #include <stdlib.h>
40 #include <sys/types.h>
41 #include <string.h>
42 #include <stdarg.h>
44 #ifdef _WIN32
45 # include <basetsd.h> /* for SSIZE_T */
46 #ifndef __MINGW32__
47 typedef SSIZE_T ssize_t;
48 #endif
49 #else
50 # include <sys/socket.h>
51 #endif
53 #ifndef MSG_DONTWAIT
54 #define MSG_DONTWAIT 0
55 #endif
57 struct mpd_async {
58 mpd_socket_t fd;
60 struct mpd_error_info error;
62 struct mpd_buffer input;
64 struct mpd_buffer output;
67 struct mpd_async *
68 mpd_async_new(int fd)
70 struct mpd_async *async;
72 assert(fd != MPD_INVALID_SOCKET);
74 async = malloc(sizeof(*async));
75 if (async == NULL)
76 return NULL;
78 async->fd = fd;
79 mpd_error_init(&async->error);
81 mpd_buffer_init(&async->input);
82 mpd_buffer_init(&async->output);
84 return async;
87 void
88 mpd_async_free(struct mpd_async *async)
90 assert(async != NULL);
92 mpd_socket_close(async->fd);
93 mpd_error_deinit(&async->error);
94 free(async);
97 enum mpd_error
98 mpd_async_get_error(const struct mpd_async *async)
100 assert(async != NULL);
102 return async->error.code;
105 const char *
106 mpd_async_get_error_message(const struct mpd_async *async)
108 assert(async != NULL);
110 return mpd_error_get_message(&async->error);
114 mpd_async_get_system_error(const struct mpd_async *async)
116 assert(async != NULL);
117 assert(async->error.code == MPD_ERROR_SYSTEM);
119 return async->error.system;
122 bool
123 mpd_async_copy_error(const struct mpd_async *async,
124 struct mpd_error_info *dest)
126 assert(async != NULL);
128 return mpd_error_copy(dest, &async->error);
132 mpd_async_get_fd(const struct mpd_async *async)
134 assert(async != NULL);
135 assert(async->fd != MPD_INVALID_SOCKET);
137 return async->fd;
140 void
141 mpd_async_set_keepalive(struct mpd_async *async,
142 bool keepalive)
144 assert(async != NULL);
145 assert(async->fd != MPD_INVALID_SOCKET);
147 mpd_socket_keepalive(async->fd, keepalive);
150 enum mpd_async_event
151 mpd_async_events(const struct mpd_async *async)
153 enum mpd_async_event events;
155 assert(async != NULL);
157 if (mpd_error_is_defined(&async->error))
158 return 0;
160 /* always listen to hangups and errors */
161 events = MPD_ASYNC_EVENT_HUP | MPD_ASYNC_EVENT_ERROR;
163 if (mpd_buffer_room(&async->input) > 0)
164 /* there's room left in the input buffer: attempt to
165 read */
166 events |= MPD_ASYNC_EVENT_READ;
168 if (mpd_buffer_size(&async->output) > 0)
169 /* there's data in the output buffer: attempt to
170 write */
171 events |= MPD_ASYNC_EVENT_WRITE;
173 return events;
176 static bool
177 mpd_async_read(struct mpd_async *async)
179 size_t room;
180 ssize_t nbytes;
182 assert(async != NULL);
183 assert(async->fd != MPD_INVALID_SOCKET);
184 assert(!mpd_error_is_defined(&async->error));
186 room = mpd_buffer_room(&async->input);
187 if (room == 0)
188 return true;
190 nbytes = recv(async->fd, mpd_buffer_write(&async->input), room,
191 MSG_DONTWAIT);
192 if (nbytes < 0) {
193 /* I/O error */
195 if (mpd_socket_ignore_errno(mpd_socket_errno()))
196 return true;
198 mpd_error_errno(&async->error);
199 return false;
202 if (nbytes == 0) {
203 mpd_error_code(&async->error, MPD_ERROR_CLOSED);
204 mpd_error_message(&async->error,
205 "Connection closed by the server");
206 return false;
209 mpd_buffer_expand(&async->input, (size_t)nbytes);
210 return true;
213 static bool
214 mpd_async_write(struct mpd_async *async)
216 size_t size;
217 ssize_t nbytes;
219 assert(async != NULL);
220 assert(async->fd != MPD_INVALID_SOCKET);
221 assert(!mpd_error_is_defined(&async->error));
223 size = mpd_buffer_size(&async->output);
224 if (size == 0)
225 return true;
227 nbytes = send(async->fd, mpd_buffer_read(&async->output), size,
228 MSG_DONTWAIT);
229 if (nbytes < 0) {
230 /* I/O error */
232 if (mpd_socket_ignore_errno(mpd_socket_errno()))
233 return true;
235 mpd_error_errno(&async->error);
236 return false;
239 mpd_buffer_consume(&async->output, (size_t)nbytes);
240 return true;
243 bool
244 mpd_async_io(struct mpd_async *async, enum mpd_async_event events)
246 bool success;
248 assert(async != NULL);
250 if (mpd_error_is_defined(&async->error))
251 return false;
253 if ((events & (MPD_ASYNC_EVENT_HUP|MPD_ASYNC_EVENT_ERROR)) != 0) {
254 mpd_error_code(&async->error, MPD_ERROR_CLOSED);
255 mpd_error_message(&async->error, "Socket connection aborted");
256 return false;
259 if (events & MPD_ASYNC_EVENT_READ) {
260 success = mpd_async_read(async);
261 if (!success)
262 return false;
265 assert(!mpd_error_is_defined(&async->error));
267 if (events & MPD_ASYNC_EVENT_WRITE) {
268 success = mpd_async_write(async);
269 if (!success)
270 return false;
273 assert(!mpd_error_is_defined(&async->error));
275 return true;
278 bool
279 mpd_async_send_command_v(struct mpd_async *async, const char *command,
280 va_list args)
282 size_t room, length;
283 char *dest, *end, *p;
284 const char *arg;
286 assert(async != NULL);
287 assert(command != NULL);
289 if (mpd_error_is_defined(&async->error))
290 return false;
292 room = mpd_buffer_room(&async->output);
293 length = strlen(command);
294 if (room <= length)
295 return false;
297 dest = mpd_buffer_write(&async->output);
298 /* -1 because we reserve space for the \n character */
299 end = dest + room - 1;
301 /* copy the command (no quoting, we asumme it is "clean") */
303 memcpy(dest, command, length);
304 p = dest + length;
306 /* now append all arguments (quoted) */
308 while ((arg = va_arg(args, const char *)) != NULL) {
309 /* append a space separator */
311 if (p >= end)
312 return false;
314 *p++ = ' ';
316 /* quote the argument into the destination buffer */
318 p = quote(p, end, arg);
319 assert(p == NULL || (p >= dest && p <= end));
320 if (p == NULL)
321 return false;
325 /* append the newline to finish this command */
327 *p++ = '\n';
329 mpd_buffer_expand(&async->output, p - dest);
330 return true;
333 bool
334 mpd_async_send_command(struct mpd_async *async, const char *command, ...)
336 va_list args;
337 bool success;
339 assert(async != NULL);
340 assert(command != NULL);
342 va_start(args, command);
343 success = mpd_async_send_command_v(async, command, args);
344 va_end(args);
346 return success;
349 char *
350 mpd_async_recv_line(struct mpd_async *async)
352 size_t size;
353 char *src, *newline;
355 assert(async != NULL);
357 size = mpd_buffer_size(&async->input);
358 if (size == 0)
359 return NULL;
361 src = mpd_buffer_read(&async->input);
362 assert(src != NULL);
363 newline = memchr(src, '\n', size);
364 if (newline == NULL) {
365 /* line is not finished yet */
366 if (mpd_buffer_full(&async->input)) {
367 /* .. but the buffer is full - line is too
368 long, abort connection and bail out */
369 mpd_error_code(&async->error, MPD_ERROR_MALFORMED);
370 mpd_error_message(&async->error,
371 "Response line too large");
374 return NULL;
377 *newline = 0;
378 mpd_buffer_consume(&async->input, newline + 1 - src);
380 return src;