2004-02-24 00:41:20 +01:00
|
|
|
/* the Music Player Daemon (MPD)
|
2006-07-14 21:37:45 +02:00
|
|
|
* (c)2003-2006 by Warren Dukes (warren.dukes@gmail.com)
|
2004-02-24 00:41:20 +01:00
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "interface.h"
|
|
|
|
#include "command.h"
|
|
|
|
#include "conf.h"
|
|
|
|
#include "list.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "listen.h"
|
|
|
|
#include "playlist.h"
|
|
|
|
#include "permission.h"
|
2006-07-30 05:43:38 +02:00
|
|
|
#include "sllist.h"
|
2006-07-30 05:57:29 +02:00
|
|
|
#include "utils.h"
|
2007-01-11 21:41:17 +01:00
|
|
|
#include "ioops.h"
|
2004-02-24 00:41:20 +01:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <assert.h>
|
2004-04-01 05:48:51 +02:00
|
|
|
#include <sys/select.h>
|
2004-02-24 00:41:20 +01:00
|
|
|
#include <sys/time.h>
|
2004-04-01 05:48:51 +02:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
2004-02-24 00:41:20 +01:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <signal.h>
|
|
|
|
|
2006-08-10 23:15:06 +02:00
|
|
|
#define GREETING "OK MPD " PROTOCOL_VERSION "\n"
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2004-11-14 06:54:00 +01:00
|
|
|
#define INTERFACE_MAX_BUFFER_LENGTH (40960)
|
|
|
|
#define INTERFACE_LIST_MODE_BEGIN "command_list_begin"
|
|
|
|
#define INTERFACE_LIST_OK_MODE_BEGIN "command_list_ok_begin"
|
|
|
|
#define INTERFACE_LIST_MODE_END "command_list_end"
|
|
|
|
#define INTERFACE_DEFAULT_OUT_BUFFER_SIZE (4096)
|
|
|
|
#define INTERFACE_TIMEOUT_DEFAULT (60)
|
|
|
|
#define INTERFACE_MAX_CONNECTIONS_DEFAULT (10)
|
2004-10-28 07:14:55 +02:00
|
|
|
#define INTERFACE_MAX_COMMAND_LIST_DEFAULT (2048*1024)
|
2005-04-27 17:54:26 +02:00
|
|
|
#define INTERFACE_MAX_OUTPUT_BUFFER_SIZE_DEFAULT (8192*1024)
|
2004-10-28 07:14:55 +02:00
|
|
|
|
2004-10-31 16:37:54 +01:00
|
|
|
/* set this to zero to indicate we have no possible interfaces */
|
2007-01-14 04:07:53 +01:00
|
|
|
static int interface_max_connections; /*INTERFACE_MAX_CONNECTIONS_DEFAULT; */
|
2004-10-28 07:14:55 +02:00
|
|
|
static int interface_timeout = INTERFACE_TIMEOUT_DEFAULT;
|
2006-07-20 18:02:40 +02:00
|
|
|
static size_t interface_max_command_list_size =
|
|
|
|
INTERFACE_MAX_COMMAND_LIST_DEFAULT;
|
2004-10-28 07:14:55 +02:00
|
|
|
static size_t interface_max_output_buffer_size =
|
2006-07-20 18:02:40 +02:00
|
|
|
INTERFACE_MAX_OUTPUT_BUFFER_SIZE_DEFAULT;
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2007-01-11 21:41:17 +01:00
|
|
|
/* List of registered external IO handlers */
|
|
|
|
static struct ioOps *ioList;
|
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
/* maybe make conf option for this, or... 32 might be good enough */
|
|
|
|
static long int interface_list_cache_size = 32;
|
|
|
|
|
|
|
|
/* shared globally between all interfaces: */
|
2007-01-14 04:07:53 +01:00
|
|
|
static struct strnode *list_cache;
|
|
|
|
static struct strnode *list_cache_head;
|
|
|
|
static struct strnode *list_cache_tail;
|
2006-07-30 05:43:38 +02:00
|
|
|
|
2004-02-24 00:41:20 +01:00
|
|
|
typedef struct _Interface {
|
2004-11-14 06:38:49 +01:00
|
|
|
char buffer[INTERFACE_MAX_BUFFER_LENGTH];
|
2004-02-24 00:41:20 +01:00
|
|
|
int bufferLength;
|
2004-11-14 06:38:49 +01:00
|
|
|
int bufferPos;
|
2006-07-20 20:53:56 +02:00
|
|
|
int fd; /* file descriptor */
|
2006-07-17 23:46:32 +02:00
|
|
|
int permission;
|
2004-02-24 00:41:20 +01:00
|
|
|
time_t lastTime;
|
2006-07-30 05:43:38 +02:00
|
|
|
struct strnode *cmd_list; /* for when in list mode */
|
|
|
|
struct strnode *cmd_list_tail; /* for when in list mode */
|
|
|
|
int cmd_list_OK; /* print OK after each command execution */
|
|
|
|
int cmd_list_size; /* mem cmd_list consumes */
|
|
|
|
int cmd_list_dup; /* has the cmd_list been copied to private space? */
|
|
|
|
struct sllnode *deferred_send; /* for output if client is slow */
|
|
|
|
int deferred_bytes; /* mem deferred_send consumes */
|
2006-07-20 20:53:56 +02:00
|
|
|
int expired; /* set whether this interface should be closed on next
|
|
|
|
check of old interfaces */
|
|
|
|
int num; /* interface number */
|
2006-07-30 05:43:38 +02:00
|
|
|
|
|
|
|
char *send_buf;
|
|
|
|
int send_buf_used; /* bytes used this instance */
|
|
|
|
int send_buf_size; /* bytes usable this instance */
|
|
|
|
int send_buf_alloc; /* bytes actually allocated */
|
2004-02-24 00:41:20 +01:00
|
|
|
} Interface;
|
|
|
|
|
2007-01-14 04:07:53 +01:00
|
|
|
static Interface *interfaces;
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-14 21:06:25 +02:00
|
|
|
static void flushInterfaceBuffer(Interface * interface);
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-14 21:06:25 +02:00
|
|
|
static void printInterfaceOutBuffer(Interface * interface);
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
#ifdef SO_SNDBUF
|
|
|
|
static int get_default_snd_buf_size(Interface * interface)
|
|
|
|
{
|
|
|
|
int new_size;
|
|
|
|
socklen_t sockOptLen = sizeof(int);
|
|
|
|
|
|
|
|
if (getsockopt(interface->fd, SOL_SOCKET, SO_SNDBUF,
|
|
|
|
(char *)&new_size, &sockOptLen) < 0) {
|
|
|
|
DEBUG("problem getting sockets send buffer size\n");
|
|
|
|
return INTERFACE_DEFAULT_OUT_BUFFER_SIZE;
|
|
|
|
}
|
|
|
|
if (new_size > 0)
|
|
|
|
return new_size;
|
|
|
|
DEBUG("sockets send buffer size is not positive\n");
|
|
|
|
return INTERFACE_DEFAULT_OUT_BUFFER_SIZE;
|
|
|
|
}
|
|
|
|
#else /* !SO_SNDBUF */
|
|
|
|
static int get_default_snd_buf_size(Interface * interface)
|
|
|
|
{
|
|
|
|
return INTERFACE_DEFAULT_OUT_BUFFER_SIZE;
|
|
|
|
}
|
|
|
|
#endif /* !SO_SNDBUF */
|
|
|
|
|
|
|
|
static void set_send_buf_size(Interface * interface)
|
|
|
|
{
|
|
|
|
int new_size = get_default_snd_buf_size(interface);
|
|
|
|
if (interface->send_buf_size != new_size) {
|
|
|
|
interface->send_buf_size = new_size;
|
|
|
|
/* don't resize to get smaller, only bigger */
|
|
|
|
if (interface->send_buf_alloc < new_size) {
|
|
|
|
if (interface->send_buf)
|
|
|
|
free(interface->send_buf);
|
2006-08-26 08:25:57 +02:00
|
|
|
interface->send_buf = xmalloc(new_size);
|
2006-07-30 05:43:38 +02:00
|
|
|
interface->send_buf_alloc = new_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
static void openInterface(Interface * interface, int fd)
|
|
|
|
{
|
2004-02-24 00:41:20 +01:00
|
|
|
int flags;
|
2006-07-20 18:02:40 +02:00
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
assert(interface->fd < 0);
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
interface->cmd_list_size = 0;
|
|
|
|
interface->cmd_list_dup = 0;
|
|
|
|
interface->cmd_list_OK = -1;
|
2004-02-24 00:41:20 +01:00
|
|
|
interface->bufferLength = 0;
|
2004-11-14 06:38:49 +01:00
|
|
|
interface->bufferPos = 0;
|
2004-02-24 00:41:20 +01:00
|
|
|
interface->fd = fd;
|
2006-07-20 18:02:40 +02:00
|
|
|
while ((flags = fcntl(fd, F_GETFL)) < 0 && errno == EINTR) ;
|
2006-07-30 05:43:38 +02:00
|
|
|
while (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0 && errno == EINTR) ;
|
2004-02-24 00:41:20 +01:00
|
|
|
interface->lastTime = time(NULL);
|
2006-07-30 05:43:38 +02:00
|
|
|
interface->cmd_list = NULL;
|
|
|
|
interface->cmd_list_tail = NULL;
|
|
|
|
interface->deferred_send = NULL;
|
2004-02-24 00:41:20 +01:00
|
|
|
interface->expired = 0;
|
2006-07-30 05:43:38 +02:00
|
|
|
interface->deferred_bytes = 0;
|
|
|
|
interface->send_buf_used = 0;
|
2004-02-24 00:41:20 +01:00
|
|
|
|
|
|
|
interface->permission = getDefaultPermissions();
|
2006-07-30 05:43:38 +02:00
|
|
|
set_send_buf_size(interface);
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
xwrite(fd, GREETING, strlen(GREETING));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_cmd_list(struct strnode *list)
|
|
|
|
{
|
|
|
|
struct strnode *tmp = list;
|
|
|
|
|
|
|
|
while (tmp) {
|
|
|
|
struct strnode *next = tmp->next;
|
|
|
|
if (tmp >= list_cache_head && tmp <= list_cache_tail) {
|
|
|
|
/* inside list_cache[] array */
|
|
|
|
tmp->data = NULL;
|
|
|
|
tmp->next = NULL;
|
2006-07-20 18:02:40 +02:00
|
|
|
} else
|
2006-07-30 05:43:38 +02:00
|
|
|
free(tmp);
|
|
|
|
tmp = next;
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
2006-07-30 05:43:38 +02:00
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
static void cmd_list_clone(Interface * interface)
|
|
|
|
{
|
|
|
|
struct strnode *new = dup_strlist(interface->cmd_list);
|
|
|
|
free_cmd_list(interface->cmd_list);
|
|
|
|
interface->cmd_list = new;
|
|
|
|
interface->cmd_list_dup = 1;
|
|
|
|
|
|
|
|
/* new tail */
|
|
|
|
while (new && new->next)
|
|
|
|
new = new->next;
|
|
|
|
interface->cmd_list_tail = new;
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
static void new_cmd_list_ptr(Interface * interface, char *s, const int size)
|
2006-07-20 18:02:40 +02:00
|
|
|
{
|
2006-07-30 05:43:38 +02:00
|
|
|
int i;
|
|
|
|
struct strnode *new;
|
|
|
|
|
|
|
|
if (!interface->cmd_list_dup) {
|
|
|
|
for (i = interface_list_cache_size - 1; i >= 0; --i) {
|
|
|
|
if (list_cache[i].data)
|
|
|
|
continue;
|
|
|
|
new = &(list_cache[i]);
|
|
|
|
new->data = s;
|
|
|
|
/* implied in free_cmd_list() and init: */
|
|
|
|
/* last->next->next = NULL; */
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
/* allocate from the heap */
|
|
|
|
new = interface->cmd_list_dup ? new_strnode_dup(s, size)
|
|
|
|
: new_strnode(s);
|
|
|
|
out:
|
|
|
|
if (interface->cmd_list) {
|
|
|
|
interface->cmd_list_tail->next = new;
|
|
|
|
interface->cmd_list_tail = new;
|
|
|
|
} else
|
|
|
|
interface->cmd_list = interface->cmd_list_tail = new;
|
|
|
|
}
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
static void closeInterface(Interface * interface)
|
|
|
|
{
|
|
|
|
struct sllnode *buf;
|
|
|
|
if (interface->fd < 0)
|
|
|
|
return;
|
|
|
|
xclose(interface->fd);
|
|
|
|
interface->fd = -1;
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
if (interface->cmd_list) {
|
|
|
|
free_cmd_list(interface->cmd_list);
|
|
|
|
interface->cmd_list = NULL;
|
|
|
|
}
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
if ((buf = interface->deferred_send)) {
|
|
|
|
do {
|
|
|
|
struct sllnode *prev = buf;
|
|
|
|
buf = buf->next;
|
|
|
|
free(prev);
|
|
|
|
} while (buf);
|
|
|
|
interface->deferred_send = NULL;
|
|
|
|
}
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
SECURE("interface %i: closed\n", interface->num);
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
void openAInterface(int fd, struct sockaddr *addr)
|
|
|
|
{
|
2004-02-24 00:41:20 +01:00
|
|
|
int i;
|
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
for (i = 0; i < interface_max_connections
|
|
|
|
&& interfaces[i].fd >= 0; i++) /* nothing */ ;
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
if (i == interface_max_connections) {
|
2004-02-24 00:41:20 +01:00
|
|
|
ERROR("Max Connections Reached!\n");
|
2006-07-30 05:43:38 +02:00
|
|
|
xclose(fd);
|
2006-07-20 18:02:40 +02:00
|
|
|
} else {
|
|
|
|
SECURE("interface %i: opened from ", i);
|
|
|
|
switch (addr->sa_family) {
|
2004-02-24 00:41:20 +01:00
|
|
|
case AF_INET:
|
2004-02-25 20:13:10 +01:00
|
|
|
{
|
2006-07-20 18:02:40 +02:00
|
|
|
char *host = inet_ntoa(((struct sockaddr_in *)
|
|
|
|
addr)->sin_addr);
|
|
|
|
if (host) {
|
|
|
|
SECURE("%s\n", host);
|
|
|
|
} else {
|
2004-02-25 20:13:10 +01:00
|
|
|
SECURE("error getting ipv4 address\n");
|
|
|
|
}
|
|
|
|
}
|
2004-02-24 00:41:20 +01:00
|
|
|
break;
|
|
|
|
#ifdef HAVE_IPV6
|
|
|
|
case AF_INET6:
|
|
|
|
{
|
2006-07-20 18:02:40 +02:00
|
|
|
char host[INET6_ADDRSTRLEN + 1];
|
|
|
|
memset(host, 0, INET6_ADDRSTRLEN + 1);
|
|
|
|
if (inet_ntop(AF_INET6, (void *)
|
|
|
|
&(((struct sockaddr_in6 *)addr)->
|
|
|
|
sin6_addr), host,
|
|
|
|
INET6_ADDRSTRLEN)) {
|
|
|
|
SECURE("%s\n", host);
|
|
|
|
} else {
|
2004-02-25 20:13:10 +01:00
|
|
|
SECURE("error getting ipv6 address\n");
|
|
|
|
}
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case AF_UNIX:
|
|
|
|
SECURE("local connection\n");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
SECURE("unknown\n");
|
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
openInterface(&(interfaces[i]), fd);
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
static int processLineOfInput(Interface * interface)
|
|
|
|
{
|
2004-11-14 06:38:49 +01:00
|
|
|
int ret = 1;
|
2006-07-20 18:02:40 +02:00
|
|
|
char *line = interface->buffer + interface->bufferPos;
|
2004-11-14 06:38:49 +01:00
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
if (interface->cmd_list_OK >= 0) {
|
2006-07-20 18:02:40 +02:00
|
|
|
if (strcmp(line, INTERFACE_LIST_MODE_END) == 0) {
|
2004-11-14 06:38:49 +01:00
|
|
|
DEBUG("interface %i: process command "
|
2006-07-20 18:02:40 +02:00
|
|
|
"list\n", interface->num);
|
2006-07-30 05:43:38 +02:00
|
|
|
ret = processListOfCommands(interface->fd,
|
2006-07-20 18:02:40 +02:00
|
|
|
&(interface->permission),
|
|
|
|
&(interface->expired),
|
2006-07-30 05:43:38 +02:00
|
|
|
interface->cmd_list_OK,
|
|
|
|
interface->cmd_list);
|
2004-11-14 06:38:49 +01:00
|
|
|
DEBUG("interface %i: process command "
|
2006-07-20 18:02:40 +02:00
|
|
|
"list returned %i\n", interface->num, ret);
|
|
|
|
if (ret == 0)
|
2006-07-30 05:43:38 +02:00
|
|
|
commandSuccess(interface->fd);
|
2006-07-20 18:02:40 +02:00
|
|
|
else if (ret == COMMAND_RETURN_CLOSE
|
2006-07-30 05:43:38 +02:00
|
|
|
|| interface->expired)
|
2004-11-14 06:38:49 +01:00
|
|
|
closeInterface(interface);
|
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
printInterfaceOutBuffer(interface);
|
|
|
|
free_cmd_list(interface->cmd_list);
|
|
|
|
interface->cmd_list = NULL;
|
|
|
|
interface->cmd_list_OK = -1;
|
2006-07-20 18:02:40 +02:00
|
|
|
} else {
|
2006-07-30 05:43:38 +02:00
|
|
|
size_t len = strlen(line) + 1;
|
|
|
|
interface->cmd_list_size += len;
|
|
|
|
if (interface->cmd_list_size >
|
2006-07-20 18:02:40 +02:00
|
|
|
interface_max_command_list_size) {
|
2004-11-14 06:38:49 +01:00
|
|
|
ERROR("interface %i: command "
|
2006-07-30 05:43:38 +02:00
|
|
|
"list size (%i) is "
|
2006-07-20 18:02:40 +02:00
|
|
|
"larger than the max "
|
2006-08-18 09:02:54 +02:00
|
|
|
"(%li)\n",
|
2006-07-20 18:02:40 +02:00
|
|
|
interface->num,
|
2006-07-30 05:43:38 +02:00
|
|
|
interface->cmd_list_size,
|
2006-08-18 09:02:54 +02:00
|
|
|
(long)interface_max_command_list_size);
|
2004-11-14 06:38:49 +01:00
|
|
|
closeInterface(interface);
|
2006-05-10 04:17:47 +02:00
|
|
|
ret = COMMAND_RETURN_CLOSE;
|
2006-07-30 05:43:38 +02:00
|
|
|
} else
|
|
|
|
new_cmd_list_ptr(interface, line, len);
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
} else {
|
|
|
|
if (strcmp(line, INTERFACE_LIST_MODE_BEGIN) == 0) {
|
2006-07-30 05:43:38 +02:00
|
|
|
interface->cmd_list_OK = 0;
|
2004-11-14 06:38:49 +01:00
|
|
|
ret = 1;
|
2006-07-20 18:02:40 +02:00
|
|
|
} else if (strcmp(line, INTERFACE_LIST_OK_MODE_BEGIN) == 0) {
|
2006-07-30 05:43:38 +02:00
|
|
|
interface->cmd_list_OK = 1;
|
2004-11-14 06:38:49 +01:00
|
|
|
ret = 1;
|
2006-07-20 18:02:40 +02:00
|
|
|
} else {
|
2004-11-14 06:38:49 +01:00
|
|
|
DEBUG("interface %i: process command \"%s\"\n",
|
2006-07-20 18:02:40 +02:00
|
|
|
interface->num, line);
|
2006-07-30 05:43:38 +02:00
|
|
|
ret = processCommand(interface->fd,
|
2006-07-20 18:02:40 +02:00
|
|
|
&(interface->permission), line);
|
2004-11-14 06:38:49 +01:00
|
|
|
DEBUG("interface %i: command returned %i\n",
|
2006-07-20 18:02:40 +02:00
|
|
|
interface->num, ret);
|
|
|
|
if (ret == 0)
|
2006-07-30 05:43:38 +02:00
|
|
|
commandSuccess(interface->fd);
|
2006-07-20 18:02:40 +02:00
|
|
|
else if (ret == COMMAND_RETURN_CLOSE
|
|
|
|
|| interface->expired) {
|
2004-11-14 06:38:49 +01:00
|
|
|
closeInterface(interface);
|
|
|
|
}
|
|
|
|
printInterfaceOutBuffer(interface);
|
|
|
|
}
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
|
2004-11-14 06:38:49 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
static int processBytesRead(Interface * interface, int bytesRead)
|
|
|
|
{
|
2005-02-07 05:01:41 +01:00
|
|
|
int ret = 0;
|
2006-07-30 05:43:38 +02:00
|
|
|
char *buf_tail = &(interface->buffer[interface->bufferLength - 1]);
|
2004-11-14 06:38:49 +01:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
while (bytesRead > 0) {
|
2004-11-14 06:38:49 +01:00
|
|
|
interface->bufferLength++;
|
|
|
|
bytesRead--;
|
2006-07-30 05:43:38 +02:00
|
|
|
buf_tail++;
|
|
|
|
if (*buf_tail == '\n') {
|
|
|
|
*buf_tail = '\0';
|
|
|
|
if (interface->bufferLength - interface->bufferPos > 1) {
|
|
|
|
if (*(buf_tail - 1) == '\r')
|
|
|
|
*(buf_tail - 1) = '\0';
|
|
|
|
}
|
2006-07-14 20:47:55 +02:00
|
|
|
ret = processLineOfInput(interface);
|
2004-11-14 06:38:49 +01:00
|
|
|
interface->bufferPos = interface->bufferLength;
|
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
if (interface->bufferLength == INTERFACE_MAX_BUFFER_LENGTH) {
|
|
|
|
if (interface->bufferPos == 0) {
|
2004-11-14 06:38:49 +01:00
|
|
|
ERROR("interface %i: buffer overflow\n",
|
2006-07-20 18:02:40 +02:00
|
|
|
interface->num);
|
2004-11-14 06:54:00 +01:00
|
|
|
closeInterface(interface);
|
2004-11-14 06:38:49 +01:00
|
|
|
return 1;
|
|
|
|
}
|
2006-07-30 05:43:38 +02:00
|
|
|
if (interface->cmd_list_OK >= 0 &&
|
|
|
|
!interface->cmd_list_dup)
|
|
|
|
cmd_list_clone(interface);
|
2006-07-20 18:02:40 +02:00
|
|
|
interface->bufferLength -= interface->bufferPos;
|
|
|
|
memmove(interface->buffer,
|
|
|
|
interface->buffer + interface->bufferPos,
|
|
|
|
interface->bufferLength);
|
2004-11-14 06:38:49 +01:00
|
|
|
interface->bufferPos = 0;
|
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
if (ret == COMMAND_RETURN_KILL || ret == COMMAND_RETURN_CLOSE) {
|
|
|
|
return ret;
|
2005-08-18 18:46:41 +02:00
|
|
|
}
|
|
|
|
|
2004-11-14 06:38:49 +01:00
|
|
|
}
|
|
|
|
|
2005-02-07 05:01:41 +01:00
|
|
|
return ret;
|
2004-11-14 06:38:49 +01:00
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
static int interfaceReadInput(Interface * interface)
|
|
|
|
{
|
2004-11-16 02:41:05 +01:00
|
|
|
int bytesRead;
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
bytesRead = read(interface->fd,
|
|
|
|
interface->buffer + interface->bufferLength,
|
|
|
|
INTERFACE_MAX_BUFFER_LENGTH - interface->bufferLength);
|
2004-11-16 02:41:05 +01:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
if (bytesRead > 0)
|
|
|
|
return processBytesRead(interface, bytesRead);
|
|
|
|
else if (bytesRead == 0 || (bytesRead < 0 && errno != EINTR)) {
|
2004-11-16 02:41:05 +01:00
|
|
|
closeInterface(interface);
|
2006-07-20 18:02:40 +02:00
|
|
|
} else
|
|
|
|
return 0;
|
2004-11-14 06:38:49 +01:00
|
|
|
|
2004-02-24 00:41:20 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
static void addInterfacesReadyToReadAndListenSocketToFdSet(fd_set * fds,
|
|
|
|
int *fdmax)
|
|
|
|
{
|
2004-02-24 00:41:20 +01:00
|
|
|
int i;
|
|
|
|
|
|
|
|
FD_ZERO(fds);
|
2004-11-03 20:42:54 +01:00
|
|
|
addListenSocketsToFdSet(fds, fdmax);
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
for (i = 0; i < interface_max_connections; i++) {
|
2006-07-30 05:43:38 +02:00
|
|
|
if (interfaces[i].fd >= 0 && !interfaces[i].expired
|
|
|
|
&& !interfaces[i].deferred_send) {
|
2006-07-20 18:02:40 +02:00
|
|
|
FD_SET(interfaces[i].fd, fds);
|
|
|
|
if (*fdmax < interfaces[i].fd)
|
|
|
|
*fdmax = interfaces[i].fd;
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
static void addInterfacesForBufferFlushToFdSet(fd_set * fds, int *fdmax)
|
|
|
|
{
|
2004-02-24 00:41:20 +01:00
|
|
|
int i;
|
|
|
|
|
|
|
|
FD_ZERO(fds);
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
for (i = 0; i < interface_max_connections; i++) {
|
2006-07-30 05:43:38 +02:00
|
|
|
if (interfaces[i].fd >= 0 && !interfaces[i].expired
|
|
|
|
&& interfaces[i].deferred_send) {
|
2006-07-20 18:02:40 +02:00
|
|
|
FD_SET(interfaces[i].fd, fds);
|
|
|
|
if (*fdmax < interfaces[i].fd)
|
|
|
|
*fdmax = interfaces[i].fd;
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
static void closeNextErroredInterface(void)
|
|
|
|
{
|
2004-02-24 00:41:20 +01:00
|
|
|
fd_set fds;
|
|
|
|
struct timeval tv;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
tv.tv_sec = 0;
|
|
|
|
tv.tv_usec = 0;
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
for (i = 0; i < interface_max_connections; i++) {
|
2006-07-30 05:43:38 +02:00
|
|
|
if (interfaces[i].fd >= 0) {
|
2004-02-24 00:41:20 +01:00
|
|
|
FD_ZERO(&fds);
|
2006-07-20 18:02:40 +02:00
|
|
|
FD_SET(interfaces[i].fd, &fds);
|
|
|
|
if (select(FD_SETSIZE, &fds, NULL, NULL, &tv) < 0) {
|
2004-02-24 00:41:20 +01:00
|
|
|
closeInterface(&interfaces[i]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
int doIOForInterfaces(void)
|
|
|
|
{
|
2004-02-24 00:41:20 +01:00
|
|
|
fd_set rfds;
|
|
|
|
fd_set wfds;
|
2007-01-11 21:41:17 +01:00
|
|
|
fd_set efds;
|
2004-02-24 00:41:20 +01:00
|
|
|
struct timeval tv;
|
|
|
|
int i;
|
|
|
|
int selret;
|
2005-03-12 23:38:49 +01:00
|
|
|
int fdmax;
|
2004-02-24 00:41:20 +01:00
|
|
|
|
|
|
|
tv.tv_sec = 1;
|
|
|
|
tv.tv_usec = 0;
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
while (1) {
|
2005-03-12 23:38:49 +01:00
|
|
|
fdmax = 0;
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2007-01-11 21:41:17 +01:00
|
|
|
FD_ZERO( &rfds );
|
|
|
|
FD_ZERO( &wfds );
|
|
|
|
FD_ZERO( &efds );
|
2006-07-20 18:02:40 +02:00
|
|
|
addInterfacesReadyToReadAndListenSocketToFdSet(&rfds, &fdmax);
|
|
|
|
addInterfacesForBufferFlushToFdSet(&wfds, &fdmax);
|
2005-03-12 23:38:49 +01:00
|
|
|
|
2007-01-11 21:41:17 +01:00
|
|
|
// Add fds for all registered IO handlers
|
|
|
|
if( ioList ) {
|
2007-01-14 03:08:20 +01:00
|
|
|
struct ioOps *o = ioList;
|
|
|
|
while( o ) {
|
|
|
|
struct ioOps *current = o;
|
2007-01-11 21:41:17 +01:00
|
|
|
int fdnum;
|
|
|
|
assert( current->fdset );
|
|
|
|
fdnum = current->fdset( &rfds, &wfds, &efds );
|
|
|
|
if( fdmax < fdnum )
|
|
|
|
fdmax = fdnum;
|
2007-01-14 03:08:20 +01:00
|
|
|
o = o->next;
|
2007-01-11 21:41:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
selret = select(fdmax + 1, &rfds, &wfds, &efds, &tv);
|
|
|
|
|
|
|
|
if (selret < 0 && errno == EINTR)
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Consume fds for all registered IO handlers
|
|
|
|
if( ioList ) {
|
2007-01-14 03:08:20 +01:00
|
|
|
struct ioOps *o = ioList;
|
|
|
|
while( o ) {
|
|
|
|
struct ioOps *current = o;
|
2007-01-11 21:41:17 +01:00
|
|
|
assert( current->consume );
|
|
|
|
selret = current->consume( selret, &rfds, &wfds, &efds );
|
2007-01-14 03:08:20 +01:00
|
|
|
o = o->next;
|
2007-01-11 21:41:17 +01:00
|
|
|
}
|
|
|
|
}
|
2005-03-12 23:38:49 +01:00
|
|
|
|
2007-01-11 21:41:17 +01:00
|
|
|
if (selret == 0)
|
2006-07-20 18:02:40 +02:00
|
|
|
break;
|
2005-03-12 23:38:49 +01:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
if (selret < 0) {
|
2004-02-24 00:41:20 +01:00
|
|
|
closeNextErroredInterface();
|
|
|
|
continue;
|
|
|
|
}
|
2005-03-12 23:38:49 +01:00
|
|
|
|
|
|
|
getConnections(&rfds);
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
for (i = 0; i < interface_max_connections; i++) {
|
2006-07-30 05:43:38 +02:00
|
|
|
if (interfaces[i].fd >= 0
|
2006-07-20 18:02:40 +02:00
|
|
|
&& FD_ISSET(interfaces[i].fd, &rfds)) {
|
|
|
|
if (COMMAND_RETURN_KILL ==
|
|
|
|
interfaceReadInput(&(interfaces[i]))) {
|
2004-02-24 00:41:20 +01:00
|
|
|
return COMMAND_RETURN_KILL;
|
|
|
|
}
|
|
|
|
interfaces[i].lastTime = time(NULL);
|
|
|
|
}
|
2006-07-30 05:43:38 +02:00
|
|
|
if (interfaces[i].fd >= 0
|
2006-07-20 18:02:40 +02:00
|
|
|
&& FD_ISSET(interfaces[i].fd, &wfds)) {
|
2004-02-24 00:41:20 +01:00
|
|
|
flushInterfaceBuffer(&interfaces[i]);
|
|
|
|
interfaces[i].lastTime = time(NULL);
|
|
|
|
}
|
|
|
|
}
|
2005-03-12 23:38:49 +01:00
|
|
|
|
2004-02-24 00:41:20 +01:00
|
|
|
tv.tv_sec = 0;
|
|
|
|
tv.tv_usec = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
void initInterfaces(void)
|
|
|
|
{
|
2004-02-24 00:41:20 +01:00
|
|
|
int i;
|
2006-07-20 18:02:40 +02:00
|
|
|
char *test;
|
|
|
|
ConfigParam *param;
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2004-10-28 07:14:55 +02:00
|
|
|
param = getConfigParam(CONF_CONN_TIMEOUT);
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
if (param) {
|
|
|
|
interface_timeout = strtol(param->value, &test, 10);
|
|
|
|
if (*test != '\0' || interface_timeout <= 0) {
|
2004-10-28 07:14:55 +02:00
|
|
|
ERROR("connection timeout \"%s\" is not a positive "
|
2006-07-20 18:02:40 +02:00
|
|
|
"integer, line %i\n", CONF_CONN_TIMEOUT,
|
|
|
|
param->line);
|
2004-10-28 07:14:55 +02:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
|
2004-10-28 07:14:55 +02:00
|
|
|
param = getConfigParam(CONF_MAX_CONN);
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
if (param) {
|
2004-10-28 07:14:55 +02:00
|
|
|
interface_max_connections = strtol(param->value, &test, 10);
|
2006-07-20 18:02:40 +02:00
|
|
|
if (*test != '\0' || interface_max_connections <= 0) {
|
2004-10-28 07:14:55 +02:00
|
|
|
ERROR("max connections \"%s\" is not a positive integer"
|
2006-07-20 18:02:40 +02:00
|
|
|
", line %i\n", param->value, param->line);
|
2004-10-28 07:14:55 +02:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
} else
|
|
|
|
interface_max_connections = INTERFACE_MAX_CONNECTIONS_DEFAULT;
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2004-10-28 07:14:55 +02:00
|
|
|
param = getConfigParam(CONF_MAX_COMMAND_LIST_SIZE);
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
if (param) {
|
2006-08-01 06:18:41 +02:00
|
|
|
interface_max_command_list_size = strtol(param->value,
|
|
|
|
&test, 10);
|
2006-07-20 18:02:40 +02:00
|
|
|
if (*test != '\0' || interface_max_command_list_size <= 0) {
|
2004-10-28 07:14:55 +02:00
|
|
|
ERROR("max command list size \"%s\" is not a positive "
|
2006-07-20 18:02:40 +02:00
|
|
|
"integer, line %i\n", param->value, param->line);
|
2004-10-28 07:14:55 +02:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
interface_max_command_list_size *= 1024;
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
|
2004-10-28 07:14:55 +02:00
|
|
|
param = getConfigParam(CONF_MAX_OUTPUT_BUFFER_SIZE);
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
if (param) {
|
2006-08-01 06:18:41 +02:00
|
|
|
interface_max_output_buffer_size = strtol(param->value,
|
|
|
|
&test, 10);
|
2006-07-20 18:02:40 +02:00
|
|
|
if (*test != '\0' || interface_max_output_buffer_size <= 0) {
|
2004-10-28 07:14:55 +02:00
|
|
|
ERROR("max output buffer size \"%s\" is not a positive "
|
2006-07-20 18:02:40 +02:00
|
|
|
"integer, line %i\n", param->value, param->line);
|
2004-10-28 07:14:55 +02:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
interface_max_output_buffer_size *= 1024;
|
2004-10-28 07:14:55 +02:00
|
|
|
}
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-08-26 08:25:57 +02:00
|
|
|
interfaces = xmalloc(sizeof(Interface) * interface_max_connections);
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-08-26 08:25:57 +02:00
|
|
|
list_cache = xcalloc(interface_list_cache_size, sizeof(struct strnode));
|
2006-07-30 05:43:38 +02:00
|
|
|
list_cache_head = &(list_cache[0]);
|
|
|
|
list_cache_tail = &(list_cache[interface_list_cache_size - 1]);
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
for (i = 0; i < interface_max_connections; i++) {
|
2006-07-30 05:43:38 +02:00
|
|
|
interfaces[i].fd = -1;
|
|
|
|
interfaces[i].send_buf = NULL;
|
|
|
|
interfaces[i].send_buf_size = 0;
|
|
|
|
interfaces[i].send_buf_alloc = 0;
|
2004-02-24 00:41:20 +01:00
|
|
|
interfaces[i].num = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
static void closeAllInterfaces(void)
|
|
|
|
{
|
2004-02-24 00:41:20 +01:00
|
|
|
int i;
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
for (i = 0; i < interface_max_connections; i++) {
|
2006-07-30 05:43:38 +02:00
|
|
|
if (interfaces[i].fd > 0)
|
2004-02-24 00:41:20 +01:00
|
|
|
closeInterface(&(interfaces[i]));
|
2006-07-30 05:43:38 +02:00
|
|
|
if (interfaces[i].send_buf)
|
|
|
|
free(interfaces[i].send_buf);
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
2006-07-30 05:43:38 +02:00
|
|
|
free(list_cache);
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
void freeAllInterfaces(void)
|
|
|
|
{
|
2004-02-24 00:41:20 +01:00
|
|
|
closeAllInterfaces();
|
|
|
|
|
|
|
|
free(interfaces);
|
2004-07-12 16:58:26 +02:00
|
|
|
|
|
|
|
interface_max_connections = 0;
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
void closeOldInterfaces(void)
|
|
|
|
{
|
2004-02-24 00:41:20 +01:00
|
|
|
int i;
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
for (i = 0; i < interface_max_connections; i++) {
|
2006-07-30 05:43:38 +02:00
|
|
|
if (interfaces[i].fd > 0) {
|
2006-07-20 18:02:40 +02:00
|
|
|
if (interfaces[i].expired) {
|
|
|
|
DEBUG("interface %i: expired\n", i);
|
2005-03-19 13:33:34 +01:00
|
|
|
closeInterface(&(interfaces[i]));
|
2006-07-20 18:02:40 +02:00
|
|
|
} else if (time(NULL) - interfaces[i].lastTime >
|
|
|
|
interface_timeout) {
|
|
|
|
DEBUG("interface %i: timeout\n", i);
|
2005-03-19 13:33:34 +01:00
|
|
|
closeInterface(&(interfaces[i]));
|
|
|
|
}
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
static void flushInterfaceBuffer(Interface * interface)
|
|
|
|
{
|
2006-07-30 05:43:38 +02:00
|
|
|
struct sllnode *buf;
|
2004-02-24 00:41:20 +01:00
|
|
|
int ret = 0;
|
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
buf = interface->deferred_send;
|
|
|
|
while (buf) {
|
|
|
|
ret = write(interface->fd, buf->data, buf->size);
|
|
|
|
if (ret < 0)
|
2006-07-20 18:02:40 +02:00
|
|
|
break;
|
2006-07-30 05:43:38 +02:00
|
|
|
else if (ret < buf->size) {
|
|
|
|
interface->deferred_bytes -= ret;
|
2006-08-01 06:18:41 +02:00
|
|
|
buf->data = (char *)buf->data + ret;
|
2006-07-30 05:43:38 +02:00
|
|
|
buf->size -= ret;
|
2006-07-20 18:02:40 +02:00
|
|
|
} else {
|
2006-07-30 05:43:38 +02:00
|
|
|
struct sllnode *tmp = buf;
|
|
|
|
interface->deferred_bytes -= (buf->size +
|
|
|
|
sizeof(struct sllnode));
|
|
|
|
buf = buf->next;
|
|
|
|
free(tmp);
|
|
|
|
interface->deferred_send = buf;
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
interface->lastTime = time(NULL);
|
|
|
|
}
|
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
if (!interface->deferred_send) {
|
|
|
|
DEBUG("interface %i: buffer empty %i\n", interface->num,
|
|
|
|
interface->deferred_bytes);
|
|
|
|
assert(interface->deferred_bytes == 0);
|
2006-07-20 18:02:40 +02:00
|
|
|
} else if (ret < 0 && errno != EAGAIN && errno != EINTR) {
|
2004-02-24 00:41:20 +01:00
|
|
|
/* cause interface to close */
|
|
|
|
DEBUG("interface %i: problems flushing buffer\n",
|
2006-07-20 18:02:40 +02:00
|
|
|
interface->num);
|
2006-07-30 05:43:38 +02:00
|
|
|
buf = interface->deferred_send;
|
|
|
|
do {
|
|
|
|
struct sllnode *prev = buf;
|
|
|
|
buf = buf->next;
|
|
|
|
free(prev);
|
|
|
|
} while (buf);
|
|
|
|
interface->deferred_send = NULL;
|
|
|
|
interface->deferred_bytes = 0;
|
2004-02-24 00:41:20 +01:00
|
|
|
interface->expired = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
int interfacePrintWithFD(int fd, char *buffer, int buflen)
|
|
|
|
{
|
2007-01-14 04:07:53 +01:00
|
|
|
static int i;
|
2004-02-24 00:41:20 +01:00
|
|
|
int copylen;
|
2006-07-20 18:02:40 +02:00
|
|
|
Interface *interface;
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
assert(fd > 0);
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
if (i >= interface_max_connections ||
|
2006-07-30 05:43:38 +02:00
|
|
|
interfaces[i].fd < 0 || interfaces[i].fd != fd) {
|
2006-07-20 18:02:40 +02:00
|
|
|
for (i = 0; i < interface_max_connections; i++) {
|
2006-07-30 05:43:38 +02:00
|
|
|
if (interfaces[i].fd == fd)
|
2006-07-20 18:02:40 +02:00
|
|
|
break;
|
2004-06-15 20:06:21 +02:00
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
if (i == interface_max_connections)
|
|
|
|
return -1;
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* if fd isn't found or interfaces is going to be closed, do nothing */
|
2006-07-20 18:02:40 +02:00
|
|
|
if (interfaces[i].expired)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
interface = interfaces + i;
|
|
|
|
|
|
|
|
while (buflen > 0 && !interface->expired) {
|
2006-07-30 05:43:38 +02:00
|
|
|
int left = interface->send_buf_size - interface->send_buf_used;
|
|
|
|
copylen = buflen > left ? left : buflen;
|
|
|
|
memcpy(interface->send_buf + interface->send_buf_used, buffer,
|
2006-07-20 18:02:40 +02:00
|
|
|
copylen);
|
|
|
|
buflen -= copylen;
|
2006-07-30 05:43:38 +02:00
|
|
|
interface->send_buf_used += copylen;
|
2006-07-20 18:02:40 +02:00
|
|
|
buffer += copylen;
|
2006-07-30 05:43:38 +02:00
|
|
|
if (interface->send_buf_used >= interface->send_buf_size)
|
2004-02-24 00:41:20 +01:00
|
|
|
printInterfaceOutBuffer(interface);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-07-20 18:02:40 +02:00
|
|
|
static void printInterfaceOutBuffer(Interface * interface)
|
|
|
|
{
|
2004-02-24 00:41:20 +01:00
|
|
|
int ret;
|
2006-07-30 05:43:38 +02:00
|
|
|
struct sllnode *buf;
|
2004-02-24 00:41:20 +01:00
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
if (interface->fd < 0 || interface->expired ||
|
|
|
|
!interface->send_buf_used)
|
2004-02-24 00:41:20 +01:00
|
|
|
return;
|
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
if ((buf = interface->deferred_send)) {
|
|
|
|
interface->deferred_bytes += sizeof(struct sllnode)
|
|
|
|
+ interface->send_buf_used;
|
|
|
|
if (interface->deferred_bytes >
|
2006-07-20 18:02:40 +02:00
|
|
|
interface_max_output_buffer_size) {
|
2006-07-30 05:43:38 +02:00
|
|
|
ERROR("interface %i: output buffer size (%li) is "
|
|
|
|
"larger than the max (%li)\n",
|
2006-07-20 18:02:40 +02:00
|
|
|
interface->num,
|
2006-07-30 05:43:38 +02:00
|
|
|
(long)interface->deferred_bytes,
|
|
|
|
(long)interface_max_output_buffer_size);
|
2004-02-24 00:41:20 +01:00
|
|
|
/* cause interface to close */
|
|
|
|
interface->expired = 1;
|
2006-07-30 05:43:38 +02:00
|
|
|
do {
|
|
|
|
struct sllnode *prev = buf;
|
|
|
|
buf = buf->next;
|
|
|
|
free(prev);
|
|
|
|
} while (buf);
|
|
|
|
interface->deferred_send = NULL;
|
|
|
|
interface->deferred_bytes = 0;
|
2006-07-20 18:02:40 +02:00
|
|
|
} else {
|
2006-07-30 05:43:38 +02:00
|
|
|
while (buf->next)
|
|
|
|
buf = buf->next;
|
|
|
|
buf->next = new_sllnode(interface->send_buf,
|
|
|
|
interface->send_buf_used);
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
2006-07-20 18:02:40 +02:00
|
|
|
} else {
|
2006-07-30 05:43:38 +02:00
|
|
|
if ((ret = write(interface->fd, interface->send_buf,
|
|
|
|
interface->send_buf_used)) < 0) {
|
2006-07-20 18:02:40 +02:00
|
|
|
if (errno == EAGAIN || errno == EINTR) {
|
2006-07-30 05:43:38 +02:00
|
|
|
interface->deferred_send =
|
|
|
|
new_sllnode(interface->send_buf,
|
|
|
|
interface->send_buf_used);
|
2006-07-20 18:02:40 +02:00
|
|
|
} else {
|
2004-02-24 00:41:20 +01:00
|
|
|
DEBUG("interface %i: problems writing\n",
|
2006-07-20 18:02:40 +02:00
|
|
|
interface->num);
|
2004-02-24 00:41:20 +01:00
|
|
|
interface->expired = 1;
|
|
|
|
return;
|
|
|
|
}
|
2006-07-30 05:43:38 +02:00
|
|
|
} else if (ret < interface->send_buf_used) {
|
|
|
|
interface->deferred_send =
|
|
|
|
new_sllnode(interface->send_buf + ret,
|
|
|
|
interface->send_buf_used - ret);
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
2006-07-30 05:43:38 +02:00
|
|
|
if (interface->deferred_send) {
|
2006-07-20 18:02:40 +02:00
|
|
|
DEBUG("interface %i: buffer created\n", interface->num);
|
2006-07-30 05:43:38 +02:00
|
|
|
interface->deferred_bytes =
|
|
|
|
interface->deferred_send->size
|
|
|
|
+ sizeof(struct sllnode);
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-30 05:43:38 +02:00
|
|
|
interface->send_buf_used = 0;
|
2004-02-24 00:41:20 +01:00
|
|
|
}
|
2007-01-11 21:41:17 +01:00
|
|
|
|
|
|
|
// From ioops.h:
|
|
|
|
void registerIO( struct ioOps *ops )
|
|
|
|
{
|
|
|
|
assert( ops != NULL );
|
|
|
|
|
|
|
|
ops->next = ioList;
|
|
|
|
ioList = ops;
|
|
|
|
ops->prev = NULL;
|
|
|
|
if( ops->next )
|
|
|
|
ops->next->prev = ops;
|
|
|
|
}
|
|
|
|
|
|
|
|
void deregisterIO( struct ioOps *ops )
|
|
|
|
{
|
|
|
|
assert( ops != NULL );
|
|
|
|
|
|
|
|
if( ioList == ops )
|
|
|
|
ioList = ops->next;
|
|
|
|
else if( ops->prev != NULL )
|
|
|
|
ops->prev->next = ops->next;
|
|
|
|
}
|