From bbbb36cf1974ec63375915c081998cdd2ad881b0 Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Mon, 19 Nov 2001 01:48:58 +0000 Subject: /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 --- src/core/Makefile.am | 2 + src/core/core.c | 9 +- src/core/server-rec.h | 1 + src/core/servers.c | 11 ++- src/core/session.c | 233 +++++++++++++++++++++++++++++++++++++++++++++ src/core/session.h | 9 ++ src/fe-text/irssi.c | 2 + src/irc/core/Makefile.am | 1 + src/irc/core/irc-core.c | 5 + src/irc/core/irc-servers.c | 4 +- src/irc/core/irc-session.c | 95 ++++++++++++++++++ 11 files changed, 366 insertions(+), 6 deletions(-) create mode 100644 src/core/session.c create mode 100644 src/core/session.h create mode 100644 src/irc/core/irc-session.c (limited to 'src') 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); +} -- cgit v1.2.3