diff --git a/src/core/core.c b/src/core/core.c index 2102ebcd..642aa47b 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -24,6 +24,7 @@ #include "pidwait.h" #include "net-disconnect.h" +#include "net-sendbuffer.h" #include "signals.h" #include "settings.h" @@ -41,6 +42,7 @@ void core_init(void) pidwait_init(); net_disconnect_init(); + net_sendbuffer_init(); signals_init(); settings_init(); commands_init(); @@ -61,6 +63,7 @@ void core_deinit(void) commands_deinit(); settings_deinit(); signals_deinit(); + net_sendbuffer_deinit(); net_disconnect_deinit(); pidwait_deinit(); diff --git a/src/core/net-sendbuffer.c b/src/core/net-sendbuffer.c new file mode 100644 index 00000000..e048decf --- /dev/null +++ b/src/core/net-sendbuffer.c @@ -0,0 +1,165 @@ +/* + net-sendbuffer.c : Buffered send() + + Copyright (C) 1998-2000 Timo Sirainen + + 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 "module.h" + +#include "network.h" +#include "net-sendbuffer.h" + +struct _NET_SENDBUF_REC { + int handle; + + int bufsize; + int bufpos; + char *buffer; /* Buffer is NULL until it's actually needed. */ +}; + +static GSList *buffers; +static int timeout_tag; + +/* Create new buffer - if `bufsize' is zero or less, DEFAULT_BUFFER_SIZE + is used */ +NET_SENDBUF_REC *net_sendbuffer_create(int handle, int bufsize) +{ + NET_SENDBUF_REC *rec; + + rec = g_new0(NET_SENDBUF_REC, 1); + rec->handle = handle; + rec->bufsize = bufsize > 0 ? bufsize : DEFAULT_BUFFER_SIZE; + + buffers = g_slist_append(buffers, rec); + return rec; +} + +/* Destroy the buffer. `close' specifies if socket handle should be closed. */ +void net_sendbuffer_destroy(NET_SENDBUF_REC *rec, int close) +{ + buffers = g_slist_remove(buffers, rec); + + if (close) net_disconnect(rec->handle); + g_free_not_null(rec->buffer); + g_free(rec); +} + +/* Transmit all data from buffer - return TRUE if successful */ +static int buffer_send(NET_SENDBUF_REC *rec) +{ + int ret; + + ret = net_transmit(rec->handle, rec->buffer, rec->bufpos); + if (ret < 0 || rec->bufpos == ret) { + /* error/all sent - don't try to send it anymore */ + g_free_and_null(rec->buffer); + return TRUE; + } + + if (ret > 0) { + rec->bufpos -= ret; + memmove(rec->buffer, rec->buffer+ret, rec->bufpos); + } + return FALSE; +} + +static int sig_sendbuffer(void) +{ + GSList *tmp; + int stop; + + stop = TRUE; + for (tmp = buffers; tmp != NULL; tmp = tmp->next) { + NET_SENDBUF_REC *rec = tmp->data; + + if (rec->buffer != NULL) { + if (!buffer_send(rec)) + stop = FALSE; + } + } + + if (stop) timeout_tag = -1; + return !stop; +} + +/* Add `data' to transmit buffer - return FALSE if buffer is full */ +static int buffer_add(NET_SENDBUF_REC *rec, void *data, int size) +{ + if (rec->buffer == NULL) { + rec->buffer = g_malloc(rec->bufsize); + rec->bufpos = 0; + } + + if (rec->bufpos+size > rec->bufsize) + return FALSE; + + memcpy(rec->buffer+rec->bufpos, data, size); + rec->bufpos += size; + return TRUE; +} + +/* Send data, if all of it couldn't be sent immediately, it will be resent + automatically after a while. Returns -1 if some unrecoverable error + occured. */ +int net_sendbuffer_send(NET_SENDBUF_REC *rec, void *data, int size) +{ + int ret; + + g_return_val_if_fail(rec != NULL, -1); + g_return_val_if_fail(data != NULL, -1); + if (size <= 0) return 0; + + if (rec->buffer == NULL) { + /* nothing in buffer - transmit immediately */ + ret = net_transmit(rec->handle, data, size); + if (ret < 0) return -1; + size -= ret; + data = ((char *) data) + ret; + } + + if (size > 0) { + /* everything couldn't be sent. */ + if (timeout_tag == -1) { + timeout_tag = g_timeout_add(100, (GSourceFunc) + sig_sendbuffer, NULL); + } + + if (!buffer_add(rec, data, size)) + return -1; + } + + return 0; +} + +/* Returns the socket handle */ +int net_sendbuffer_handle(NET_SENDBUF_REC *rec) +{ + g_return_val_if_fail(rec != NULL, -1); + + return rec->handle; +} + +void net_sendbuffer_init(void) +{ + timeout_tag = -1; + buffers = NULL; +} + +void net_sendbuffer_deinit(void) +{ + if (timeout_tag != -1) g_source_remove(timeout_tag); +} diff --git a/src/core/net-sendbuffer.h b/src/core/net-sendbuffer.h new file mode 100644 index 00000000..6b2ff372 --- /dev/null +++ b/src/core/net-sendbuffer.h @@ -0,0 +1,25 @@ +#ifndef __NET_SENDBUFFER_H +#define __NET_SENDBUFFER_H + +#define DEFAULT_BUFFER_SIZE 8192 + +typedef struct _NET_SENDBUF_REC NET_SENDBUF_REC; + +/* Create new buffer - if `bufsize' is zero or less, DEFAULT_BUFFER_SIZE + is used */ +NET_SENDBUF_REC *net_sendbuffer_create(int handle, int bufsize); +/* Destroy the buffer. `close' specifies if socket handle should be closed. */ +void net_sendbuffer_destroy(NET_SENDBUF_REC *rec, int close); + +/* Send data, if all of it couldn't be sent immediately, it will be resent + automatically after a while. Returns -1 if some unrecoverable error + occured. */ +int net_sendbuffer_send(NET_SENDBUF_REC *rec, void *data, int size); + +/* Returns the socket handle */ +int net_sendbuffer_handle(NET_SENDBUF_REC *rec); + +void net_sendbuffer_init(void); +void net_sendbuffer_deinit(void); + +#endif diff --git a/src/core/server.c b/src/core/server.c index f46893e4..e96574b7 100644 --- a/src/core/server.c +++ b/src/core/server.c @@ -24,6 +24,7 @@ #include "signals.h" #include "line-split.h" #include "net-nonblock.h" +#include "net-sendbuffer.h" #include "rawlog.h" #include "misc.h" #include "server.h" @@ -124,15 +125,16 @@ static void server_connect_callback_init(SERVER_REC *server, int handle) signal_emit("server connected", 1, server); } -static void server_connect_callback_readpipe(SERVER_REC *server, int handle) +static void server_connect_callback_readpipe(SERVER_REC *server) { SERVER_CONNECT_REC *conn; RESOLVED_IP_REC iprec; + int handle; g_source_remove(server->connect_tag); server->connect_tag = -1; - net_gethostbyname_return(handle, &iprec); + net_gethostbyname_return(server->connect_pipe[0], &iprec); close(server->connect_pipe[0]); close(server->connect_pipe[1]); @@ -141,11 +143,11 @@ static void server_connect_callback_readpipe(SERVER_REC *server, int handle) server->connect_pipe[1] = -1; conn = server->connrec; - server->handle = iprec.error != 0 ? -1 : + handle = iprec.error != 0 ? -1 : net_connect_ip(&iprec.ip, conn->proxy != NULL ? conn->proxy_port : conn->port, conn->own_ip != NULL ? conn->own_ip : NULL); - if (server->handle == -1) { + if (handle == -1) { /* failed */ if (iprec.error == 0 || !net_hosterror_notfound(iprec.error)) { /* reconnect back only if either @@ -162,7 +164,8 @@ static void server_connect_callback_readpipe(SERVER_REC *server, int handle) return; } - server->connect_tag = g_input_add(server->handle, G_INPUT_WRITE|G_INPUT_READ|G_INPUT_EXCEPTION, + server->handle = net_sendbuffer_create(handle, 0); + server->connect_tag = g_input_add(handle, G_INPUT_WRITE|G_INPUT_READ|G_INPUT_EXCEPTION, (GInputFunction) server_connect_callback_init, server); signal_emit("server connecting", 2, server, &iprec.ip); } @@ -179,13 +182,10 @@ int server_connect(SERVER_REC *server) } server->tag = server_create_tag(server->connrec); - server->handle = -1; - server->connect_pid = net_gethostbyname_nonblock(server->connrec->proxy != NULL ? server->connrec->proxy : server->connrec->address, server->connect_pipe[1]); - server->connect_tag = g_input_add(server->connect_pipe[0], G_INPUT_READ, (GInputFunction) server_connect_callback_readpipe, server); @@ -212,8 +212,8 @@ void server_disconnect(SERVER_REC *server) signal_emit("server disconnected", 1, server); - if (server->handle != -1) - net_disconnect(server->handle); + if (server->handle != NULL) + net_sendbuffer_destroy(server->handle, TRUE); MODULE_DATA_DEINIT(server); rawlog_destroy(server->rawlog); diff --git a/src/irc/core/irc-server.c b/src/irc/core/irc-server.c index 376b503e..1439157d 100644 --- a/src/irc/core/irc-server.c +++ b/src/irc/core/irc-server.c @@ -21,6 +21,7 @@ #include "module.h" #include "net-nonblock.h" +#include "net-sendbuffer.h" #include "line-split.h" #include "signals.h" #include "modules.h" @@ -208,16 +209,17 @@ static void sig_disconnected(IRC_SERVER_REC *server) g_slist_foreach(server->cmdqueue, (GFunc) g_free, NULL); g_slist_free(server->cmdqueue); - if (server->handle != -1) { + if (server->handle != NULL) { if (!chans || server->connection_lost) - net_disconnect(server->handle); + net_sendbuffer_destroy(server->handle, TRUE); else { /* we were on some channels, try to let the server disconnect so that our quit message is guaranteed to get displayed */ - net_disconnect_later(server->handle); + net_disconnect_later(net_sendbuffer_handle(server->handle)); + net_sendbuffer_destroy(server->handle, FALSE); } - server->handle = -1; + server->handle = NULL; } irc_server_connect_free(server->connrec); @@ -239,7 +241,7 @@ static void server_cmd_timeout(IRC_SERVER_REC *server, GTimeVal *now) { long usecs; char *cmd; - int len, ret, add_rawlog; + int len, add_rawlog; if (!irc_server_check(server)) return; @@ -262,22 +264,16 @@ static void server_cmd_timeout(IRC_SERVER_REC *server, GTimeVal *now) add_rawlog = !server->cmd_last_split; - ret = net_transmit(server->handle, cmd, len); - if (ret != len) { - /* we didn't transmit all data, try again a bit later.. */ - if (ret > 0) { - cmd = g_strdup((char *) (server->cmdqueue->data) + ret); - g_free(server->cmdqueue->data); - server->cmdqueue->data = cmd; - } - server->cmd_last_split = TRUE; - server->cmdcount++; - } else { - memcpy(&server->last_cmd, now, sizeof(GTimeVal)); - if (server->cmd_last_split) - server->cmd_last_split = FALSE; + if (net_sendbuffer_send(server->handle, cmd, len) == -1) { + /* something bad happened */ + g_warning("net_sendbuffer_send() failed: %s", g_strerror(errno)); + return; } + memcpy(&server->last_cmd, now, sizeof(GTimeVal)); + if (server->cmd_last_split) + server->cmd_last_split = FALSE; + if (add_rawlog) { /* add to rawlog without CR+LF */ int slen; @@ -288,11 +284,9 @@ static void server_cmd_timeout(IRC_SERVER_REC *server, GTimeVal *now) cmd[slen-2] = '\r'; } - if (ret == len) { - /* remove from queue */ - g_free(cmd); - server->cmdqueue = g_slist_remove(server->cmdqueue, cmd); - } + /* remove from queue */ + g_free(cmd); + server->cmdqueue = g_slist_remove(server->cmdqueue, cmd); } /* check every now and then if there's data to be sent in command buffer */ diff --git a/src/irc/core/irc.c b/src/irc/core/irc.c index d464b098..c58b5db1 100644 --- a/src/irc/core/irc.c +++ b/src/irc/core/irc.c @@ -21,6 +21,7 @@ #include "module.h" #include "modules.h" #include "network.h" +#include "net-sendbuffer.h" #include "line-split.h" #include "rawlog.h" @@ -38,7 +39,7 @@ static int signal_server_incoming; static void cmd_send(IRC_SERVER_REC *server, const char *cmd, int send_now, int immediate) { char str[513], *ptr; - int len, ret; + int len; server->cmdcount++; @@ -53,25 +54,22 @@ static void cmd_send(IRC_SERVER_REC *server, const char *cmd, int send_now, int ptr = str; if (send_now) { - ret = net_transmit(server->handle, str, len); - if (ret == len) { - g_get_current_time(&server->last_cmd); - return; + if (net_sendbuffer_send(server->handle, str, len) == -1) { + /* something bad happened */ + g_warning("net_sendbuffer_send() failed: %s", + g_strerror(errno)); } - /* we didn't transmit all data, try again a bit later.. */ - ptr += ret; - server->cmd_last_split = TRUE; + g_get_current_time(&server->last_cmd); + return; } /* add to queue */ ptr = g_strdup(ptr); if (!immediate) server->cmdqueue = g_slist_append(server->cmdqueue, ptr); - else if (send_now) - server->cmdqueue = g_slist_prepend(server->cmdqueue, ptr); else - server->cmdqueue = g_slist_insert(server->cmdqueue, ptr, 1); + server->cmdqueue = g_slist_prepend(server->cmdqueue, ptr); } /* Send command to IRC server */ @@ -280,7 +278,8 @@ static int irc_receive_line(SERVER_REC *server, char **str) g_return_val_if_fail(server != NULL, -1); g_return_val_if_fail(str != NULL, -1); - recvlen = net_receive(server->handle, tmpbuf, sizeof(tmpbuf)); + recvlen = net_receive(net_sendbuffer_handle(server->handle), + tmpbuf, sizeof(tmpbuf)); ret = line_split(tmpbuf, recvlen, str, (LINEBUF_REC **) &server->buffer); if (ret == -1) { @@ -346,7 +345,8 @@ static void irc_init_server(IRC_SERVER_REC *server) g_return_if_fail(server != NULL); server->readtag = - g_input_add(server->handle, G_INPUT_READ, + g_input_add(net_sendbuffer_handle(server->handle), + G_INPUT_READ, (GInputFunction) irc_parse_incoming, server); } diff --git a/src/irc/dcc/dcc-chat.c b/src/irc/dcc/dcc-chat.c index 83330c6a..8cae1265 100644 --- a/src/irc/dcc/dcc-chat.c +++ b/src/irc/dcc/dcc-chat.c @@ -23,6 +23,7 @@ #include "commands.h" #include "network.h" #include "net-nonblock.h" +#include "net-sendbuffer.h" #include "line-split.h" #include "settings.h" @@ -281,7 +282,7 @@ static void cmd_dcc_chat(const char *data, IRC_SERVER_REC *server) if (server == NULL || !server->connected) cmd_param_error(CMDERR_NOT_CONNECTED); - if (net_getsockname(server->handle, &own_ip, NULL) == -1) + if (net_getsockname(net_sendbuffer_handle(server->handle), &own_ip, NULL) == -1) cmd_param_error(CMDERR_ERRNO); port = settings_get_int("dcc_port"); diff --git a/src/irc/dcc/dcc-files.c b/src/irc/dcc/dcc-files.c index 68df8033..e367f23f 100644 --- a/src/irc/dcc/dcc-files.c +++ b/src/irc/dcc/dcc-files.c @@ -22,6 +22,7 @@ #include "signals.h" #include "commands.h" #include "network.h" +#include "net-sendbuffer.h" #include "line-split.h" #include "misc.h" #include "settings.h" @@ -541,7 +542,7 @@ static void cmd_dcc_send(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *i lseek(hfile, 0, SEEK_SET); /* get the IP address we use with IRC server */ - if (net_getsockname(chat != NULL ? chat->handle : server->handle, &own_ip, NULL) == -1) { + if (net_getsockname(chat != NULL ? chat->handle : net_sendbuffer_handle(server->handle), &own_ip, NULL) == -1) { close(hfile); cmd_param_error(CMDERR_ERRNO); }