client.c 21.3 KB
Newer Older
Warren Dukes's avatar
Warren Dukes committed
1
/* the Music Player Daemon (MPD)
2
 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
Warren Dukes's avatar
Warren Dukes committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 * This project's homepage is: http://www.musicpd.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

19
#include "client.h"
Warren Dukes's avatar
Warren Dukes committed
20 21 22 23
#include "command.h"
#include "conf.h"
#include "listen.h"
#include "permission.h"
24
#include "event_pipe.h"
25
#include "idle.h"
26
#include "main.h"
27
#include "config.h"
Max Kellermann's avatar
Max Kellermann committed
28

29
#include <glib.h>
30
#include <assert.h>
31 32
#include <unistd.h>
#include <string.h>
33 34 35
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
36 37 38 39 40

#ifdef WIN32
#include <ws2tcpip.h>
#include <winsock.h>
#else
41 42 43
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
44
#endif
45

46 47 48 49
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "client"
#define LOG_LEVEL_SECURE G_LOG_LEVEL_INFO

50
static const char GREETING[] = "OK MPD " PROTOCOL_VERSION "\n";
Warren Dukes's avatar
Warren Dukes committed
51

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
#define CLIENT_LIST_MODE_BEGIN			"command_list_begin"
#define CLIENT_LIST_OK_MODE_BEGIN			"command_list_ok_begin"
#define CLIENT_LIST_MODE_END				"command_list_end"
#define CLIENT_TIMEOUT_DEFAULT			(60)
#define CLIENT_MAX_CONNECTIONS_DEFAULT		(10)
#define CLIENT_MAX_COMMAND_LIST_DEFAULT		(2048*1024)
#define CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT	(8192*1024)

/* set this to zero to indicate we have no possible clients */
static unsigned int client_max_connections;	/*CLIENT_MAX_CONNECTIONS_DEFAULT; */
static int client_timeout = CLIENT_TIMEOUT_DEFAULT;
static size_t client_max_command_list_size =
    CLIENT_MAX_COMMAND_LIST_DEFAULT;
static size_t client_max_output_buffer_size =
    CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT;
Warren Dukes's avatar
Warren Dukes committed
67

68 69 70 71 72
struct deferred_buffer {
	size_t size;
	char data[sizeof(long)];
};

73
struct client {
74
	char buffer[4096];
Max Kellermann's avatar
Max Kellermann committed
75 76
	size_t bufferLength;
	size_t bufferPos;
77

78
	int fd;	/* file descriptor; -1 if expired */
79
	GIOChannel *channel;
80
	guint source_id;
81

82
	unsigned permission;
83 84 85 86

	/** the uid of the client process, or -1 if unknown */
	int uid;

Warren Dukes's avatar
Warren Dukes committed
87
	time_t lastTime;
88
	GSList *cmd_list;	/* for when in list mode */
89
	int cmd_list_OK;	/* print OK after each command execution */
Max Kellermann's avatar
Max Kellermann committed
90
	size_t cmd_list_size;	/* mem cmd_list consumes */
91
	GQueue *deferred_send;	/* for output if client is slow */
Max Kellermann's avatar
Max Kellermann committed
92
	size_t deferred_bytes;	/* mem deferred_send consumes */
93
	unsigned int num;	/* client number */
94

95
	char send_buf[4096];
Max Kellermann's avatar
Max Kellermann committed
96
	size_t send_buf_used;	/* bytes used this instance */
97 98 99 100 101 102 103

	/** is this client waiting for an "idle" response? */
	bool idle_waiting;

	/** idle flags pending on this client, to be sent as soon as
	    the client enters "idle" */
	unsigned idle_flags;
104 105 106

	/** idle flags that the client wants to receive */
	unsigned idle_subscriptions;
107
};
Warren Dukes's avatar
Warren Dukes committed
108

109
static GList *clients;
110
static unsigned num_clients;
111
static guint expire_source_id;
Warren Dukes's avatar
Warren Dukes committed
112

113
static void client_write_deferred(struct client *client);
Warren Dukes's avatar
Warren Dukes committed
114

115
static void client_write_output(struct client *client);
Warren Dukes's avatar
Warren Dukes committed
116

Max Kellermann's avatar
Max Kellermann committed
117
bool client_is_expired(const struct client *client)
118
{
119
	return client->fd < 0;
120 121
}

122 123 124 125 126
int client_get_uid(const struct client *client)
{
	return client->uid;
}

127
unsigned client_get_permission(const struct client *client)
128 129 130 131
{
	return client->permission;
}

132
void client_set_permission(struct client *client, unsigned permission)
133 134 135 136
{
	client->permission = permission;
}

137 138 139 140 141 142 143 144 145 146 147 148 149 150
static void
client_manager_expire(void);

/**
 * An idle event which calls client_manager_expire().
 */
static gboolean
client_manager_expire_event(G_GNUC_UNUSED gpointer data)
{
	expire_source_id = 0;
	client_manager_expire();
	return false;
}

151 152
static inline void client_set_expired(struct client *client)
{
153 154 155 156 157
	if (expire_source_id == 0 && client->fd >= 0)
		/* delayed deletion */
		expire_source_id = g_idle_add(client_manager_expire_event,
					      NULL);

158 159 160 161 162
	if (client->source_id != 0) {
		g_source_remove(client->source_id);
		client->source_id = 0;
	}

163 164 165 166 167
	if (client->channel != NULL) {
		g_io_channel_unref(client->channel);
		client->channel = NULL;
	}

168
	if (client->fd >= 0) {
169
		close(client->fd);
170 171
		client->fd = -1;
	}
172 173
}

174 175 176
static gboolean
client_in_event(GIOChannel *source, GIOCondition condition, gpointer data);

177
static void client_init(struct client *client, int fd)
Avuton Olrich's avatar
Avuton Olrich committed
178
{
179
	static unsigned int next_client_num;
180

Max Kellermann's avatar
Max Kellermann committed
181 182
	assert(fd >= 0);

183 184 185 186 187
	client->cmd_list_size = 0;
	client->cmd_list_OK = -1;
	client->bufferLength = 0;
	client->bufferPos = 0;
	client->fd = fd;
188 189

	client->channel = g_io_channel_unix_new(client->fd);
190 191
	client->source_id = g_io_add_watch(client->channel, G_IO_IN,
					   client_in_event, client);
192

193 194
	client->lastTime = time(NULL);
	client->cmd_list = NULL;
195
	client->deferred_send = g_queue_new();
196
	client->deferred_bytes = 0;
197
	client->num = next_client_num++;
198
	client->send_buf_used = 0;
Warren Dukes's avatar
Warren Dukes committed
199

200
	client->permission = getDefaultPermissions();
Warren Dukes's avatar
Warren Dukes committed
201

202
	write(fd, GREETING, sizeof(GREETING) - 1);
203 204
}

205
static void free_cmd_list(GSList *list)
206
{
207 208
	for (GSList *tmp = list; tmp != NULL; tmp = g_slist_next(tmp))
		g_free(tmp->data);
209

210
	g_slist_free(list);
Warren Dukes's avatar
Warren Dukes committed
211 212
}

213
static void new_cmd_list_ptr(struct client *client, char *s)
Avuton Olrich's avatar
Avuton Olrich committed
214
{
215
	client->cmd_list = g_slist_prepend(client->cmd_list, g_strdup(s));
216
}
Warren Dukes's avatar
Warren Dukes committed
217

218
static void
219
deferred_buffer_free(gpointer data, G_GNUC_UNUSED gpointer user_data)
220
{
221 222 223
	struct deferred_buffer *buffer = data;
	g_free(buffer);
}
224

225 226
static void client_close(struct client *client)
{
227
	assert(num_clients > 0);
228 229 230
	assert(clients != NULL);

	clients = g_list_remove(clients, client);
231
	--num_clients;
Warren Dukes's avatar
Warren Dukes committed
232

233 234
	client_set_expired(client);

235 236 237
	if (client->cmd_list) {
		free_cmd_list(client->cmd_list);
		client->cmd_list = NULL;
238
	}
Warren Dukes's avatar
Warren Dukes committed
239

240 241
	g_queue_foreach(client->deferred_send, deferred_buffer_free, NULL);
	g_queue_free(client->deferred_send);
Warren Dukes's avatar
Warren Dukes committed
242

243 244
	g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE,
	      "client %i: closed", client->num);
245
	free(client);
Warren Dukes's avatar
Warren Dukes committed
246 247
}

248 249
static const char *
sockaddr_to_tmp_string(const struct sockaddr *addr)
Avuton Olrich's avatar
Avuton Olrich committed
250
{
251 252 253
	const char *hostname;

	switch (addr->sa_family) {
254
#ifdef HAVE_TCP
255 256 257 258 259 260
	case AF_INET:
		hostname = (const char *)inet_ntoa(((const struct sockaddr_in *)
						    addr)->sin_addr);
		if (!hostname)
			hostname = "error getting ipv4 address";
		break;
Warren Dukes's avatar
Warren Dukes committed
261
#ifdef HAVE_IPV6
262 263 264 265 266 267 268 269 270 271 272
	case AF_INET6:
		{
			static char host[INET6_ADDRSTRLEN + 1];
			memset(host, 0, INET6_ADDRSTRLEN + 1);
			if (inet_ntop(AF_INET6, (const void *)
				      &(((const struct sockaddr_in6 *)addr)->
					sin6_addr), host,
				      INET6_ADDRSTRLEN)) {
				hostname = (const char *)host;
			} else {
				hostname = "error getting ipv6 address";
Warren Dukes's avatar
Warren Dukes committed
273
			}
274 275
		}
		break;
Warren Dukes's avatar
Warren Dukes committed
276
#endif
277 278
#endif /* HAVE_TCP */
#ifdef HAVE_UN
279 280 281
	case AF_UNIX:
		hostname = "local connection";
		break;
282
#endif /* HAVE_UN */
283 284
	default:
		hostname = "unknown";
Warren Dukes's avatar
Warren Dukes committed
285
	}
286

287 288 289
	return hostname;
}

290
void client_new(int fd, const struct sockaddr *addr, int uid)
291 292 293 294
{
	struct client *client;

	if (num_clients >= client_max_connections) {
295
		g_warning("Max Connections Reached!");
296
		close(fd);
297 298 299
		return;
	}

300
	client = g_new0(struct client, 1);
301
	clients = g_list_prepend(clients, client);
302
	++num_clients;
303

304
	client_init(client, fd);
305
	client->uid = uid;
306 307 308
	g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE,
	      "client %i: opened from %s\n", client->num,
	      sockaddr_to_tmp_string(addr));
Warren Dukes's avatar
Warren Dukes committed
309 310
}

311
static int client_process_line(struct client *client, char *line)
Avuton Olrich's avatar
Avuton Olrich committed
312
{
313
	int ret = 1;
314

315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
	if (strcmp(line, "noidle") == 0) {
		if (client->idle_waiting) {
			/* send empty idle response and leave idle mode */
			client->idle_waiting = false;
			command_success(client);
			client_write_output(client);
		}

		/* do nothing if the client wasn't idling: the client
		   has already received the full idle response from
		   client_idle_notify(), which he can now evaluate */

		return 0;
	} else if (client->idle_waiting) {
		/* during idle mode, clients must not send anything
		   except "noidle" */
331 332
		g_warning("client %i: command \"%s\" during idle",
			  client->num, line);
333 334 335
		return COMMAND_RETURN_CLOSE;
	}

336 337
	if (client->cmd_list_OK >= 0) {
		if (strcmp(line, CLIENT_LIST_MODE_END) == 0) {
338 339
			g_debug("client %i: process command list",
				client->num);
340 341 342 343 344 345

			/* for scalability reasons, we have prepended
			   each new command; now we have to reverse it
			   to restore the correct order */
			client->cmd_list = g_slist_reverse(client->cmd_list);

Max Kellermann's avatar
Max Kellermann committed
346 347 348
			ret = command_process_list(client,
						   client->cmd_list_OK,
						   client->cmd_list);
349 350
			g_debug("client %i: process command "
				"list returned %i", client->num, ret);
351

352
			if (ret == COMMAND_RETURN_CLOSE ||
353
			    client_is_expired(client))
354 355
				return COMMAND_RETURN_CLOSE;

Avuton Olrich's avatar
Avuton Olrich committed
356
			if (ret == 0)
357
				command_success(client);
358

359 360 361 362
			client_write_output(client);
			free_cmd_list(client->cmd_list);
			client->cmd_list = NULL;
			client->cmd_list_OK = -1;
Avuton Olrich's avatar
Avuton Olrich committed
363
		} else {
364
			size_t len = strlen(line) + 1;
365 366 367
			client->cmd_list_size += len;
			if (client->cmd_list_size >
			    client_max_command_list_size) {
368 369 370 371 372
				g_warning("client %i: command list size (%lu) "
					  "is larger than the max (%lu)",
					  client->num,
					  (unsigned long)client->cmd_list_size,
					  (unsigned long)client_max_command_list_size);
373
				return COMMAND_RETURN_CLOSE;
374
			} else
375
				new_cmd_list_ptr(client, line);
Warren Dukes's avatar
Warren Dukes committed
376
		}
Avuton Olrich's avatar
Avuton Olrich committed
377
	} else {
378 379
		if (strcmp(line, CLIENT_LIST_MODE_BEGIN) == 0) {
			client->cmd_list_OK = 0;
380
			ret = 1;
381 382
		} else if (strcmp(line, CLIENT_LIST_OK_MODE_BEGIN) == 0) {
			client->cmd_list_OK = 1;
383
			ret = 1;
Avuton Olrich's avatar
Avuton Olrich committed
384
		} else {
385 386
			g_debug("client %i: process command \"%s\"",
				client->num, line);
Max Kellermann's avatar
Max Kellermann committed
387
			ret = command_process(client, line);
388 389
			g_debug("client %i: command returned %i",
				client->num, ret);
390 391

			if (ret == COMMAND_RETURN_CLOSE ||
392
			    client_is_expired(client))
393 394 395
				return COMMAND_RETURN_CLOSE;

			if (ret == 0)
396
				command_success(client);
397

398
			client_write_output(client);
399
		}
Warren Dukes's avatar
Warren Dukes committed
400 401
	}

402 403 404
	return ret;
}

405
static int client_input_received(struct client *client, size_t bytesRead)
Avuton Olrich's avatar
Avuton Olrich committed
406
{
407
	char *start = client->buffer + client->bufferPos, *end;
408
	char *newline, *next;
409
	int ret;
410

411 412 413
	assert(client->bufferPos <= client->bufferLength);
	assert(client->bufferLength + bytesRead <= sizeof(client->buffer));

414
	client->bufferLength += bytesRead;
415
	end = client->buffer + client->bufferLength;
416

417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
	/* process all lines */
	while ((newline = memchr(start, '\n', end - start)) != NULL) {
		next = newline + 1;

		if (newline > start && newline[-1] == '\r')
			--newline;
		*newline = 0;

		ret = client_process_line(client, start);
		if (ret == COMMAND_RETURN_KILL ||
		    ret == COMMAND_RETURN_CLOSE)
			return ret;
		if (client_is_expired(client))
			return COMMAND_RETURN_CLOSE;

		start = next;
	}

	/* mark consumed lines */
	client->bufferPos = start - client->buffer;

	/* if we're have reached the buffer's end, close the gab at
	   the beginning */
	if (client->bufferLength == sizeof(client->buffer)) {
		if (client->bufferPos == 0) {
442 443
			g_warning("client %i: buffer overflow",
				  client->num);
444
			return COMMAND_RETURN_CLOSE;
445
		}
446 447 448 449 450 451 452
		assert(client->bufferLength >= client->bufferPos
		       && "bufferLength >= bufferPos");
		client->bufferLength -= client->bufferPos;
		memmove(client->buffer,
			client->buffer + client->bufferPos,
			client->bufferLength);
		client->bufferPos = 0;
453 454
	}

455
	return 0;
456 457
}

458
static int client_read(struct client *client)
Avuton Olrich's avatar
Avuton Olrich committed
459
{
460
	ssize_t bytesRead;
461

462 463 464
	assert(client->bufferPos <= client->bufferLength);
	assert(client->bufferLength < sizeof(client->buffer));

465 466
	bytesRead = read(client->fd,
			 client->buffer + client->bufferLength,
467
			 sizeof(client->buffer) - client->bufferLength);
468

Avuton Olrich's avatar
Avuton Olrich committed
469
	if (bytesRead > 0)
470
		return client_input_received(client, bytesRead);
471 472
	else if (bytesRead < 0 && errno == EINTR)
		/* try again later, after select() */
Avuton Olrich's avatar
Avuton Olrich committed
473
		return 0;
474 475 476
	else
		/* peer disconnected or I/O error */
		return COMMAND_RETURN_CLOSE;
Warren Dukes's avatar
Warren Dukes committed
477 478
}

479 480 481 482 483 484 485 486 487
static gboolean
client_out_event(G_GNUC_UNUSED GIOChannel *source,
		 G_GNUC_UNUSED GIOCondition condition,
		 gpointer data);

static gboolean
client_in_event(G_GNUC_UNUSED GIOChannel *source,
		G_GNUC_UNUSED GIOCondition condition,
		gpointer data)
Avuton Olrich's avatar
Avuton Olrich committed
488
{
489 490
	struct client *client = data;
	int ret;
Warren Dukes's avatar
Warren Dukes committed
491

492
	assert(!client_is_expired(client));
Warren Dukes's avatar
Warren Dukes committed
493

494
	client->lastTime = time(NULL);
Warren Dukes's avatar
Warren Dukes committed
495

496 497 498 499
	ret = client_read(client);
	switch (ret) {
	case COMMAND_RETURN_KILL:
		client_close(client);
500
		g_main_loop_quit(main_loop);
501
		return false;
Warren Dukes's avatar
Warren Dukes committed
502

503 504 505 506
	case COMMAND_RETURN_CLOSE:
		client_close(client);
		return false;
	}
Warren Dukes's avatar
Warren Dukes committed
507

508 509 510
	if (client_is_expired(client)) {
		client_close(client);
		return false;
Warren Dukes's avatar
Warren Dukes committed
511 512
	}

513 514
	if (!g_queue_is_empty(client->deferred_send)) {
		/* deferred buffers exist: schedule write */
515 516
		client->source_id = g_io_add_watch(client->channel, G_IO_OUT,
						   client_out_event, client);
517 518
		return false;
	}
Warren Dukes's avatar
Warren Dukes committed
519

520 521 522
	/* read more */
	return true;
}
Warren Dukes's avatar
Warren Dukes committed
523

524 525 526 527 528 529
static gboolean
client_out_event(G_GNUC_UNUSED GIOChannel *source,
		 G_GNUC_UNUSED GIOCondition condition,
		 gpointer data)
{
	struct client *client = data;
Warren Dukes's avatar
Warren Dukes committed
530

531
	assert(!client_is_expired(client));
532

533
	client_write_deferred(client);
534

535 536 537
	if (client_is_expired(client)) {
		client_close(client);
		return false;
538
	}
Warren Dukes's avatar
Warren Dukes committed
539

540
	client->lastTime = time(NULL);
541

542 543 544
	if (g_queue_is_empty(client->deferred_send)) {
		/* done sending deferred buffers exist: schedule
		   read */
545 546
		client->source_id = g_io_add_watch(client->channel, G_IO_IN,
						   client_in_event, client);
547
		return false;
Warren Dukes's avatar
Warren Dukes committed
548 549
	}

550 551
	/* write more */
	return true;
Warren Dukes's avatar
Warren Dukes committed
552 553
}

554
void client_manager_init(void)
Avuton Olrich's avatar
Avuton Olrich committed
555 556 557
{
	char *test;
	ConfigParam *param;
Warren Dukes's avatar
Warren Dukes committed
558

559
	param = getConfigParam(CONF_CONN_TIMEOUT);
Warren Dukes's avatar
Warren Dukes committed
560

Avuton Olrich's avatar
Avuton Olrich committed
561
	if (param) {
562 563
		client_timeout = strtol(param->value, &test, 10);
		if (*test != '\0' || client_timeout <= 0) {
564 565 566
			g_error("connection timeout \"%s\" is not a positive "
				"integer, line %i",
				CONF_CONN_TIMEOUT, param->line);
567
		}
Warren Dukes's avatar
Warren Dukes committed
568 569
	}

570 571
	param = getConfigParam(CONF_MAX_CONN);

Avuton Olrich's avatar
Avuton Olrich committed
572
	if (param) {
573 574
		client_max_connections = strtol(param->value, &test, 10);
		if (*test != '\0' || client_max_connections <= 0) {
575 576 577
			g_error("max connections \"%s\" is not a positive integer"
				", line %i",
				param->value, param->line);
578
		}
Avuton Olrich's avatar
Avuton Olrich committed
579
	} else
580
		client_max_connections = CLIENT_MAX_CONNECTIONS_DEFAULT;
Warren Dukes's avatar
Warren Dukes committed
581

582 583
	param = getConfigParam(CONF_MAX_COMMAND_LIST_SIZE);

Avuton Olrich's avatar
Avuton Olrich committed
584
	if (param) {
585 586
		long tmp = strtol(param->value, &test, 10);
		if (*test != '\0' || tmp <= 0) {
587 588 589
			g_error("max command list size \"%s\" is not a positive "
				"integer, line %i",
				param->value, param->line);
590
		}
591
		client_max_command_list_size = tmp * 1024;
Warren Dukes's avatar
Warren Dukes committed
592 593
	}

594 595
	param = getConfigParam(CONF_MAX_OUTPUT_BUFFER_SIZE);

Avuton Olrich's avatar
Avuton Olrich committed
596
	if (param) {
597 598
		long tmp = strtol(param->value, &test, 10);
		if (*test != '\0' || tmp <= 0) {
599 600 601
			g_error("max output buffer size \"%s\" is not a positive "
				"integer, line %i",
				param->value, param->line);
602
		}
603
		client_max_output_buffer_size = tmp * 1024;
604
	}
Warren Dukes's avatar
Warren Dukes committed
605 606
}

607
static void client_close_all(void)
Avuton Olrich's avatar
Avuton Olrich committed
608
{
609 610
	while (clients != NULL) {
		struct client *client = clients->data;
611 612

		client_close(client);
613 614 615
	}

	assert(num_clients == 0);
Warren Dukes's avatar
Warren Dukes committed
616 617
}

618
void client_manager_deinit(void)
Avuton Olrich's avatar
Avuton Olrich committed
619
{
620
	client_close_all();
Warren Dukes's avatar
Warren Dukes committed
621

622
	client_max_connections = 0;
623 624 625

	if (expire_source_id != 0)
		g_source_remove(expire_source_id);
Warren Dukes's avatar
Warren Dukes committed
626 627
}

628
static void
629
client_check_expired_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
Avuton Olrich's avatar
Avuton Olrich committed
630
{
631 632 633 634 635 636 637 638 639 640 641
	struct client *client = data;

	if (client_is_expired(client)) {
		g_debug("client %i: expired", client->num);
		client_close(client);
	} else if (!client->idle_waiting && /* idle clients
					       never expire */
		   time(NULL) - client->lastTime >
		   client_timeout) {
		g_debug("client %i: timeout", client->num);
		client_close(client);
Warren Dukes's avatar
Warren Dukes committed
642 643 644
	}
}

645 646 647 648 649 650
static void
client_manager_expire(void)
{
	g_list_foreach(clients, client_check_expired_callback, NULL);
}

651
static void client_write_deferred(struct client *client)
Avuton Olrich's avatar
Avuton Olrich committed
652
{
Max Kellermann's avatar
Max Kellermann committed
653
	ssize_t ret = 0;
Warren Dukes's avatar
Warren Dukes committed
654

655 656 657 658
	while (!g_queue_is_empty(client->deferred_send)) {
		struct deferred_buffer *buf =
			g_queue_peek_head(client->deferred_send);

Max Kellermann's avatar
Max Kellermann committed
659
		assert(buf->size > 0);
660
		assert(buf->size <= client->deferred_bytes);
Max Kellermann's avatar
Max Kellermann committed
661

662
		ret = write(client->fd, buf->data, buf->size);
663
		if (ret < 0)
Avuton Olrich's avatar
Avuton Olrich committed
664
			break;
Max Kellermann's avatar
Max Kellermann committed
665
		else if ((size_t)ret < buf->size) {
666 667
			assert(client->deferred_bytes >= (size_t)ret);
			client->deferred_bytes -= ret;
668
			buf->size -= ret;
669
			memmove(buf->data, buf->data + ret, buf->size);
670
			break;
Avuton Olrich's avatar
Avuton Olrich committed
671
		} else {
672 673
			size_t decr = sizeof(*buf) -
				sizeof(buf->data) + buf->size;
674

675 676
			assert(client->deferred_bytes >= decr);
			client->deferred_bytes -= decr;
677 678
			free(buf);
			g_queue_pop_head(client->deferred_send);
Warren Dukes's avatar
Warren Dukes committed
679
		}
680
		client->lastTime = time(NULL);
Warren Dukes's avatar
Warren Dukes committed
681 682
	}

683
	if (g_queue_is_empty(client->deferred_send)) {
684 685
		g_debug("client %i: buffer empty %lu", client->num,
			(unsigned long)client->deferred_bytes);
686
		assert(client->deferred_bytes == 0);
Avuton Olrich's avatar
Avuton Olrich committed
687
	} else if (ret < 0 && errno != EAGAIN && errno != EINTR) {
688
		/* cause client to close */
689 690
		g_debug("client %i: problems flushing buffer",
			client->num);
691
		client_set_expired(client);
Warren Dukes's avatar
Warren Dukes committed
692 693 694
	}
}

695 696 697
static void client_defer_output(struct client *client,
				const void *data, size_t length)
{
698 699
	size_t alloc;
	struct deferred_buffer *buf;
700

Max Kellermann's avatar
Max Kellermann committed
701
	assert(length > 0);
702

703 704
	alloc = sizeof(*buf) - sizeof(buf->data) + length;
	client->deferred_bytes += alloc;
705
	if (client->deferred_bytes > client_max_output_buffer_size) {
706 707 708 709 710
		g_warning("client %i: output buffer size (%lu) is "
			  "larger than the max (%lu)",
			  client->num,
			  (unsigned long)client->deferred_bytes,
			  (unsigned long)client_max_output_buffer_size);
711
		/* cause client to close */
712
		client_set_expired(client);
713
		return;
714
	}
715

716 717 718 719 720
	buf = g_malloc(alloc);
	buf->size = length;
	memcpy(buf->data, data, length);

	g_queue_push_tail(client->deferred_send, buf);
721 722
}

723 724
static void client_write_direct(struct client *client,
				const char *data, size_t length)
Avuton Olrich's avatar
Avuton Olrich committed
725
{
Max Kellermann's avatar
Max Kellermann committed
726
	ssize_t ret;
Warren Dukes's avatar
Warren Dukes committed
727

Max Kellermann's avatar
Max Kellermann committed
728
	assert(length > 0);
729
	assert(g_queue_is_empty(client->deferred_send));
730 731 732

	if ((ret = write(client->fd, data, length)) < 0) {
		if (errno == EAGAIN || errno == EINTR) {
733
			client_defer_output(client, data, length);
734
		} else {
735
			g_debug("client %i: problems writing", client->num);
736
			client_set_expired(client);
737 738 739
			return;
		}
	} else if ((size_t)ret < client->send_buf_used) {
740
		client_defer_output(client, data + ret, length - ret);
741 742
	}

743
	if (!g_queue_is_empty(client->deferred_send))
744
		g_debug("client %i: buffer created", client->num);
745 746 747 748
}

static void client_write_output(struct client *client)
{
749
	if (client_is_expired(client) || !client->send_buf_used)
Warren Dukes's avatar
Warren Dukes committed
750 751
		return;

752
	if (!g_queue_is_empty(client->deferred_send)) {
753 754
		client_defer_output(client, client->send_buf,
				    client->send_buf_used);
755 756 757 758 759 760 761 762 763 764

		/* try to flush the deferred buffers now; the current
		   server command may take too long to finish, and
		   meanwhile try to feed output to the client,
		   otherwise it will time out.  One reason why
		   deferring is slow might be that currently each
		   client_write() allocates a new deferred buffer.
		   This should be optimized after MPD 0.14. */
		client_write_deferred(client);
	} else
765 766
		client_write_direct(client, client->send_buf,
				    client->send_buf_used);
Warren Dukes's avatar
Warren Dukes committed
767

768
	client->send_buf_used = 0;
Warren Dukes's avatar
Warren Dukes committed
769
}
770

771 772 773 774 775 776 777
void client_write(struct client *client, const char *buffer, size_t buflen)
{
	/* if the client is going to be closed, do nothing */
	if (client_is_expired(client))
		return;

	while (buflen > 0 && !client_is_expired(client)) {
778
		size_t copylen;
779

780
		assert(client->send_buf_used < sizeof(client->send_buf));
781

782 783 784 785
		copylen = sizeof(client->send_buf) - client->send_buf_used;
		if (copylen > buflen)
			copylen = buflen;

786 787 788 789 790
		memcpy(client->send_buf + client->send_buf_used, buffer,
		       copylen);
		buflen -= copylen;
		client->send_buf_used += copylen;
		buffer += copylen;
791
		if (client->send_buf_used >= sizeof(client->send_buf))
792 793 794 795 796 797 798 799
			client_write_output(client);
	}
}

void client_puts(struct client *client, const char *s)
{
	client_write(client, s, strlen(s));
}
800 801 802 803 804 805 806 807 808 809 810 811 812 813 814

void client_vprintf(struct client *client, const char *fmt, va_list args)
{
	va_list tmp;
	int length;
	char *buffer;

	va_copy(tmp, args);
	length = vsnprintf(NULL, 0, fmt, tmp);
	va_end(tmp);

	if (length <= 0)
		/* wtf.. */
		return;

815
	buffer = g_malloc(length + 1);
816 817 818 819 820
	vsnprintf(buffer, length + 1, fmt, args);
	client_write(client, buffer, length);
	free(buffer);
}

821
G_GNUC_PRINTF(2, 3) void client_printf(struct client *client, const char *fmt, ...)
822 823 824 825 826 827 828
{
	va_list args;

	va_start(args, fmt);
	client_vprintf(client, fmt, args);
	va_end(args);
}
829 830 831 832 833 834 835 836

/**
 * Send "idle" response to this client.
 */
static void
client_idle_notify(struct client *client)
{
	unsigned flags, i;
837
	const char *const* idle_names;
838 839 840 841 842 843 844 845

	assert(client->idle_waiting);
	assert(client->idle_flags != 0);

	flags = client->idle_flags;
	client->idle_flags = 0;
	client->idle_waiting = false;

846 847 848
	idle_names = idle_get_names();
	for (i = 0; idle_names[i]; ++i) {
		if (flags & (1 << i) & client->idle_subscriptions)
849 850 851 852 853 854 855 856
			client_printf(client, "changed: %s\n",
				      idle_names[i]);
	}

	client_puts(client, "OK\n");
	client->lastTime = time(NULL);
}

857 858
static void
client_idle_callback(gpointer data, gpointer user_data)
859
{
860 861
	struct client *client = data;
	unsigned flags = GPOINTER_TO_UINT(user_data);
862

863 864
	if (client_is_expired(client))
		return;
865

866 867 868 869 870
	client->idle_flags |= flags;
	if (client->idle_waiting
	    && (client->idle_flags & client->idle_subscriptions)) {
		client_idle_notify(client);
		client_write_output(client);
871 872 873
	}
}

874 875 876 877 878 879 880
void client_manager_idle_add(unsigned flags)
{
	assert(flags != 0);

	g_list_foreach(clients, client_idle_callback, GUINT_TO_POINTER(flags));
}

881
bool client_idle_wait(struct client *client, unsigned flags)
882 883 884 885
{
	assert(!client->idle_waiting);

	client->idle_waiting = true;
886
	client->idle_subscriptions = flags;
887

888
	if (client->idle_flags & client->idle_subscriptions) {
889 890 891 892 893
		client_idle_notify(client);
		return true;
	} else
		return false;
}