summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTimo Sirainen <cras@irssi.org>2001-11-19 01:48:58 +0000
committercras <cras@dbcabf3a-b0e7-0310-adc4-f8d773084564>2001-11-19 01:48:58 +0000
commitbbbb36cf1974ec63375915c081998cdd2ad881b0 (patch)
treef2344f172aa55ec87730071a789d8267007a4c8d /src
parentff2357f16b46ce70d881face6a6e595ba24355b9 (diff)
downloadirssi-bbbb36cf1974ec63375915c081998cdd2ad881b0.zip
/UPGRADE - upgrade-on-the-fly feature. Currently only moves the active
server connections to the new irssi process, but that should be enough to never quit from IRC again :) git-svn-id: http://svn.irssi.org/repos/irssi/trunk@2070 dbcabf3a-b0e7-0310-adc4-f8d773084564
Diffstat (limited to 'src')
-rw-r--r--src/core/Makefile.am2
-rw-r--r--src/core/core.c9
-rw-r--r--src/core/server-rec.h1
-rw-r--r--src/core/servers.c11
-rw-r--r--src/core/session.c233
-rw-r--r--src/core/session.h9
-rw-r--r--src/fe-text/irssi.c2
-rw-r--r--src/irc/core/Makefile.am1
-rw-r--r--src/irc/core/irc-core.c5
-rw-r--r--src/irc/core/irc-servers.c4
-rw-r--r--src/irc/core/irc-session.c95
11 files changed, 366 insertions, 6 deletions
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index 9f5298f7..e2007ecc 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -45,6 +45,7 @@ libcore_a_SOURCES = \
servers.c \
servers-reconnect.c \
servers-setup.c \
+ session.c \
settings.c \
signals.c \
special-vars.c \
@@ -92,6 +93,7 @@ noinst_HEADERS = \
servers.h \
servers-reconnect.h \
servers-setup.h \
+ session.h \
settings.h \
signals.h \
special-vars.h \
diff --git a/src/core/core.c b/src/core/core.c
index ef2af512..7fd00465 100644
--- a/src/core/core.c
+++ b/src/core/core.c
@@ -29,6 +29,7 @@
#include "net-sendbuffer.h"
#include "signals.h"
#include "settings.h"
+#include "session.h"
#include "chat-protocols.h"
#include "servers.h"
@@ -186,7 +187,9 @@ void core_init_paths(int argc, char *argv[])
if (irssi_dir == NULL)
irssi_dir = g_strdup_printf(IRSSI_DIR_FULL, g_get_home_dir());
if (irssi_config_file == NULL)
- irssi_config_file = g_strdup_printf("%s/config", irssi_dir);
+ irssi_config_file = g_strdup_printf("%s/config", irssi_dir);
+
+ session_set_binary(argv[0]);
}
static void sig_irssi_init_finished(void)
@@ -213,7 +216,8 @@ void core_init(int argc, char *argv[])
settings_init();
commands_init();
- nickmatch_cache_init();
+ nickmatch_cache_init();
+ session_init();
chat_protocols_init();
chatnets_init();
@@ -267,6 +271,7 @@ void core_deinit(void)
chatnets_deinit();
chat_protocols_deinit();
+ session_deinit();
nickmatch_cache_deinit();
commands_deinit();
settings_deinit();
diff --git a/src/core/server-rec.h b/src/core/server-rec.h
index 52526fde..967da1f2 100644
--- a/src/core/server-rec.h
+++ b/src/core/server-rec.h
@@ -12,6 +12,7 @@ char *nick; /* current nick */
unsigned int connected:1; /* connected to server */
unsigned int connection_lost:1; /* Connection lost unintentionally */
+unsigned int session_reconnect:1; /* Connected to this server with /UPGRADE */
NET_SENDBUF_REC *handle;
int readtag; /* input tag */
diff --git a/src/core/servers.c b/src/core/servers.c
index fe21f131..c7ad061c 100644
--- a/src/core/servers.c
+++ b/src/core/servers.c
@@ -196,10 +196,15 @@ static void server_connect_callback_readpipe(SERVER_REC *server)
own_ip = ip == NULL ? NULL :
(IPADDR_IS_V6(ip) ? conn->own_ip6 : conn->own_ip4);
- if (ip != NULL)
- signal_emit("server connecting", 2, server, ip);
+ handle = NULL;
+ if (ip != NULL) {
+ /* allow "server connecting" signal to create the
+ connection handle */
+ signal_emit("server connecting", 3, server, ip, &handle);
+ if (handle == NULL)
+ handle = net_connect_ip(ip, port, own_ip);
+ }
- handle = ip == NULL ? NULL : net_connect_ip(ip, port, own_ip);
if (handle == NULL) {
/* failed */
if (iprec.error != 0 && net_hosterror_notfound(iprec.error)) {
diff --git a/src/core/session.c b/src/core/session.c
new file mode 100644
index 00000000..1f990972
--- /dev/null
+++ b/src/core/session.c
@@ -0,0 +1,233 @@
+/*
+ session.c : irssi
+
+ Copyright (C) 2001 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 "signals.h"
+#include "commands.h"
+#include "args.h"
+#include "net-sendbuffer.h"
+#include "lib-config/iconfig.h"
+
+#include "chat-protocols.h"
+#include "servers.h"
+#include "servers-setup.h"
+
+static char *session_file;
+static const char *irssi_binary; /* from argv[0] */
+
+static GIOChannel *next_handle;
+
+void session_set_binary(const char *path)
+{
+ irssi_binary = path;
+}
+
+static void cmd_upgrade(const char *data)
+{
+ CONFIG_REC *session;
+ GSList *file_handles;
+ const char *args[10];
+ char *session_file;
+ int pid, n;
+
+ if (*data == '\0')
+ data = irssi_binary;
+
+ /* make sure we can execute it */
+ if (access(data, X_OK) != 0)
+ cmd_return_error(CMDERR_ERRNO);
+
+ /* save the session */
+ session_file = g_strdup_printf("%s/session.%d", get_irssi_dir(), getpid());
+ unlink(session_file);
+ session = config_open(session_file, 0600);
+
+ file_handles = NULL;
+ signal_emit("session save", 2, session, &file_handles);
+ config_write(session, NULL, -1);
+ config_close(session);
+
+ /* start it .. */
+ pid = fork();
+ if (pid == -1)
+ cmd_return_error(CMDERR_ERRNO);
+
+ if (pid == 0) {
+ /* we're the child - we want to send the server connections
+ to the new binary here */
+ g_free(session_file);
+ exit(0);
+ } else {
+ /* we're the old process - exec() the new binary here so
+ the TTY won't get lost */
+ signal_emit("session clean", 0);
+ for (n = 3; n < 256; n++) {
+ if (g_slist_find(file_handles, GINT_TO_POINTER(n)) == NULL)
+ close(n);
+ }
+ g_slist_free(file_handles),
+
+ args[0] = data;
+ args[1] = "--session";
+ args[2] = session_file;
+ args[3] = "-!";
+ args[4] = NULL;
+ execvp(args[0], (char **) args);
+
+ fprintf(stderr, "exec: %s: %s\n", args[0], g_strerror(errno));
+ _exit(-1);
+ }
+}
+
+static void session_save_server(SERVER_REC *server, CONFIG_REC *config,
+ CONFIG_NODE *node, GSList **file_handles)
+{
+ int handle;
+
+ node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+
+ config_node_set_str(config, node, "chat_type",
+ chat_protocol_find_id(server->chat_type)->name);
+ config_node_set_str(config, node, "address", server->connrec->address);
+ config_node_set_int(config, node, "port", server->connrec->port);
+ config_node_set_str(config, node, "chatnet", server->connrec->chatnet);
+ config_node_set_str(config, node, "password", server->connrec->password);
+ config_node_set_str(config, node, "nick", server->nick);
+
+ handle = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle));
+ *file_handles = g_slist_append(*file_handles, GINT_TO_POINTER(handle));
+ config_node_set_int(config, node, "handle", handle);
+
+ signal_emit("session save server", 4,
+ server, config, node, file_handles);
+}
+
+static void session_restore_server(CONFIG_NODE *node)
+{
+ CHAT_PROTOCOL_REC *proto;
+ SERVER_CONNECT_REC *conn;
+ SERVER_REC *server;
+ const char *chat_type, *address, *chatnet, *password, *nick;
+ int port, handle;
+
+ chat_type = config_node_get_str(node, "chat_type", NULL);
+ address = config_node_get_str(node, "address", NULL);
+ port = config_node_get_int(node, "port", 0);
+ chatnet = config_node_get_str(node, "chatnet", NULL);
+ password = config_node_get_str(node, "password", NULL);
+ nick = config_node_get_str(node, "nick", NULL);
+ handle = config_node_get_int(node, "handle", -1);
+
+ if (chat_type == NULL || address == NULL || nick == NULL || handle < 0)
+ return;
+
+ proto = chat_protocol_find(chat_type);
+ if (proto == NULL || proto->not_initialized)
+ return;
+
+ conn = server_create_conn(proto->id, address, port,
+ chatnet, password, nick);
+ if (conn != NULL) {
+ next_handle = g_io_channel_unix_new(handle);
+ conn->reconnection = TRUE;
+
+ server = proto->server_connect(conn);
+ server->session_reconnect = TRUE;
+
+ signal_emit("session restore server", 2, server, node);
+ }
+}
+
+static void sig_session_save(CONFIG_REC *config, GSList **file_handles)
+{
+ CONFIG_NODE *node;
+ GSList *tmp;
+
+ node = config_node_traverse(config, "(servers", TRUE);
+ for (tmp = servers; tmp != NULL; tmp = tmp->next)
+ session_save_server(tmp->data, config, node, file_handles);
+}
+
+static void sig_session_restore(CONFIG_REC *config)
+{
+ CONFIG_NODE *node;
+ GSList *tmp;
+
+ node = config_node_traverse(config, "(servers", FALSE);
+ if (node != NULL) {
+ for (tmp = node->value; tmp != NULL; tmp = config_node_next(tmp))
+ session_restore_server(tmp->data);
+ }
+}
+
+static void sig_init_finished(void)
+{
+ CONFIG_REC *session;
+
+ if (session_file == NULL)
+ return;
+
+ session = config_open(session_file, -1);
+ if (session == NULL)
+ return;
+
+ config_parse(session);
+ signal_emit("session restore", 1, session);
+ config_close(session);
+
+ unlink(session_file);
+ session_file = NULL;
+}
+
+static void sig_connecting(SERVER_REC *server, IPADDR *ip, GIOChannel **handle)
+{
+ *handle = next_handle;
+ next_handle = NULL;
+}
+
+void session_init(void)
+{
+ static struct poptOption options[] = {
+ { "session", 0, POPT_ARG_STRING, &session_file, 0, "", "" },
+ { NULL, '\0', 0, NULL }
+ };
+
+ session_file = NULL;
+ args_register(options);
+
+ command_bind("upgrade", NULL, (SIGNAL_FUNC) cmd_upgrade);
+
+ signal_add("session save", (SIGNAL_FUNC) sig_session_save);
+ signal_add("session restore", (SIGNAL_FUNC) sig_session_restore);
+ signal_add("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
+
+ signal_add("server connecting", (SIGNAL_FUNC) sig_connecting);
+}
+
+void session_deinit(void)
+{
+ command_unbind("upgrade", (SIGNAL_FUNC) cmd_upgrade);
+
+ signal_remove("session save", (SIGNAL_FUNC) sig_session_save);
+ signal_remove("session restore", (SIGNAL_FUNC) sig_session_restore);
+ signal_remove("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
+
+ signal_remove("server connecting", (SIGNAL_FUNC) sig_connecting);
+}
diff --git a/src/core/session.h b/src/core/session.h
new file mode 100644
index 00000000..a0e789d1
--- /dev/null
+++ b/src/core/session.h
@@ -0,0 +1,9 @@
+#ifndef __SESSION_H
+#define __SESSION_H
+
+void session_set_binary(const char *path);
+
+void session_init(void);
+void session_deinit(void);
+
+#endif
diff --git a/src/fe-text/irssi.c b/src/fe-text/irssi.c
index 919802fc..01c5d4f4 100644
--- a/src/fe-text/irssi.c
+++ b/src/fe-text/irssi.c
@@ -140,6 +140,7 @@ static void textui_init(void)
theme_register(gui_text_formats);
signal_add("gui exit", (SIGNAL_FUNC) sig_exit);
+ signal_add("session clean", (SIGNAL_FUNC) term_deinit);
}
static void textui_finish_init(void)
@@ -195,6 +196,7 @@ static void textui_deinit(void)
dirty_check(); /* one last time to print any quit messages */
signal_remove("gui exit", (SIGNAL_FUNC) sig_exit);
+ signal_remove("session clean", (SIGNAL_FUNC) term_deinit);
lastlog_deinit();
statusbar_deinit();
diff --git a/src/irc/core/Makefile.am b/src/irc/core/Makefile.am
index 9a12b7f4..fb84a0b5 100644
--- a/src/irc/core/Makefile.am
+++ b/src/irc/core/Makefile.am
@@ -25,6 +25,7 @@ libirc_core_a_SOURCES = \
irc-servers.c \
irc-servers-reconnect.c \
irc-servers-setup.c \
+ irc-session.c \
lag.c \
massjoin.c \
modes.c \
diff --git a/src/irc/core/irc-core.c b/src/irc/core/irc-core.c
index 8906efbe..efede641 100644
--- a/src/irc/core/irc-core.c
+++ b/src/irc/core/irc-core.c
@@ -36,6 +36,9 @@
void irc_expandos_init(void);
void irc_expandos_deinit(void);
+void irc_session_init(void);
+void irc_session_deinit(void);
+
void lag_init(void);
void lag_deinit(void);
@@ -100,6 +103,7 @@ void irc_core_init(void)
chat_protocol_register(rec);
g_free(rec);
+ irc_session_init();
irc_chatnets_init();
irc_servers_init();
irc_channels_init();
@@ -130,6 +134,7 @@ void irc_core_deinit(void)
irc_irc_deinit();
irc_servers_deinit();
irc_chatnets_deinit();
+ irc_session_deinit();
chat_protocol_unregister("IRC");
}
diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c
index 3314aa24..1a082e82 100644
--- a/src/irc/core/irc-servers.c
+++ b/src/irc/core/irc-servers.c
@@ -249,7 +249,9 @@ static void sig_connected(IRC_SERVER_REC *server)
return;
server->splits = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal);
- server_init(server);
+
+ if (!server->session_reconnect)
+ server_init(server);
}
static void sig_disconnected(IRC_SERVER_REC *server)
diff --git a/src/irc/core/irc-session.c b/src/irc/core/irc-session.c
new file mode 100644
index 00000000..226a7489
--- /dev/null
+++ b/src/irc/core/irc-session.c
@@ -0,0 +1,95 @@
+/*
+ irc-session.c : irssi
+
+ Copyright (C) 2001 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 "signals.h"
+#include "lib-config/iconfig.h"
+
+#include "irc-servers.h"
+#include "irc-channels.h"
+
+static void sig_session_save_server(IRC_SERVER_REC *server, CONFIG_REC *config,
+ CONFIG_NODE *node)
+{
+ char *chans;
+
+ if (!IS_IRC_SERVER(server))
+ return;
+
+ config_node_set_str(config, node, "real_address", server->real_address);
+ config_node_set_str(config, node, "userhost", server->userhost);
+
+ chans = irc_server_get_channels(server);
+ config_node_set_str(config, node, "channels", chans);
+ g_free(chans);
+}
+
+static void sig_session_restore_server(IRC_SERVER_REC *server,
+ CONFIG_NODE *node)
+{
+ if (!IS_IRC_SERVER(server))
+ return;
+
+ if (server->real_address == NULL)
+ server->real_address = g_strdup(config_node_get_str(node, "real_address", NULL));
+ server->userhost = g_strdup(config_node_get_str(node, "userhost", NULL));
+
+ g_free_not_null(server->connrec->channels);
+ server->connrec->channels = g_strdup(config_node_get_str(node, "channels", NULL));
+}
+
+static void sig_connected(IRC_SERVER_REC *server)
+{
+ GSList *tmp;
+ char *str;
+
+ if (!IS_IRC_SERVER(server) || !server->session_reconnect)
+ return;
+
+ str = g_strdup_printf("%s :Restoring connection to %s",
+ server->nick, server->connrec->address);
+ signal_emit("event 001", 3, server, str, server->real_address);
+ g_free(str);
+
+ /* send join events for each channel and ask names list for them */
+ for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
+ CHANNEL_REC *rec = tmp->data;
+
+ signal_emit("event join", 4, server, rec->name,
+ server->nick, server->userhost);
+ irc_send_cmdv(server, "NAMES %s", rec->name);
+ }
+}
+
+void irc_session_init(void)
+{
+ signal_add("session save server", (SIGNAL_FUNC) sig_session_save_server);
+ signal_add("session restore server", (SIGNAL_FUNC) sig_session_restore_server);
+
+ signal_add("server connected", (SIGNAL_FUNC) sig_connected);
+}
+
+void irc_session_deinit(void)
+{
+ signal_remove("session save server", (SIGNAL_FUNC) sig_session_save_server);
+ signal_remove("session restore server", (SIGNAL_FUNC) sig_session_restore_server);
+
+ signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
+}