diff --git a/src/terminal-app.c b/src/terminal-app.c index 1a51cf0..b35f6ab 100644 --- a/src/terminal-app.c +++ b/src/terminal-app.c @@ -775,6 +775,8 @@ static gchar **terminal_app_profile_list_from_dir (TerminalApp *app) { g_ptr_array_add (builder_str, NULL); profile_list = (gchar **) g_ptr_array_free (builder_str, FALSE); + + g_dir_close (dir); } //g_ptr_array_unref (builder_str); diff --git a/src/terminal.c b/src/terminal.c index 41ee52c..e4a65ae 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -34,6 +35,11 @@ #include #include +#include +#include +#include +#include + #include "terminal-accels.h" #include "terminal-app.h" #include "terminal-debug.h" @@ -41,9 +47,12 @@ #include "terminal-options.h" #include "terminal-util.h" -#define TERMINAL_FACTORY_SERVICE_NAME_PREFIX "org.mate.Terminal.Display" -#define TERMINAL_FACTORY_SERVICE_PATH "/org/mate/Terminal/Factory" -#define TERMINAL_FACTORY_INTERFACE_NAME "org.mate.Terminal.Factory" +#define TERMINAL_FACTORY_UNIX_FOLDER "gatuno-terminal" +#define TERMINAL_FACTORY_SERVICE_NAME_PREFIX "factory" + +int factory_fd_lock = -1; +int factory_unix_fd = -1; +guint factory_unix_watch = 0; static char * ay_to_string (GVariant *variant, @@ -130,181 +139,101 @@ typedef struct int argc; } OwnData; -static void -method_call_cb (GDBusConnection *connection, - const char *sender, - const char *object_path, - const char *interface_name, - const char *method_name, - GVariant *parameters, - GDBusMethodInvocation *invocation, - gpointer user_data) -{ - if (g_strcmp0 (method_name, "HandleArguments") == 0) - { - TerminalOptions *options = NULL; - GVariant *v_wd, *v_display, *v_sid, *v_envv, *v_argv; - char *working_directory = NULL, *display_name = NULL, *startup_id = NULL; - int initial_workspace = -1; - char **envv = NULL, **argv = NULL; - int argc; - GError *error = NULL; +gboolean unix_socket_dgram_recv (GIOChannel *source, GIOCondition condition, gpointer data) { + char *message = NULL, byte; + int message_len; + int s; + + TerminalOptions *options = NULL; + GVariant *v_wd, *v_display, *v_sid, *v_envv, *v_argv; + char *working_directory = NULL, *display_name = NULL, *startup_id = NULL; + int initial_workspace = -1; + char **envv = NULL, **argv = NULL; + int argc; + GError *error = NULL; + GVariant *parameters; + + s = g_io_channel_unix_get_fd (source); + + message_len = recvfrom (s, &byte, 1, MSG_TRUNC | MSG_PEEK, NULL, NULL); + + message = g_malloc (message_len); + + if (message == NULL) { + return TRUE; + } + + message_len = recvfrom (s, message, message_len, 0, NULL, NULL); + if (message_len < 0) { + g_free (message); + + return TRUE; + } + + parameters = g_variant_new_from_data (G_VARIANT_TYPE ("(ayayayayiay)"), message, message_len, FALSE, NULL, NULL); + + g_variant_get (parameters, "(@ay@ay@ay@ayi@ay)", + &v_wd, &v_display, &v_sid, &v_envv, &initial_workspace, &v_argv); - g_variant_get (parameters, "(@ay@ay@ay@ayi@ay)", - &v_wd, &v_display, &v_sid, &v_envv, &initial_workspace, &v_argv); + working_directory = ay_to_string (v_wd, &error); + if (error) + goto out; + display_name = ay_to_string (v_display, &error); + if (error) + goto out; + startup_id = ay_to_string (v_sid, &error); + if (error) + goto out; + envv = ay_to_strv (v_envv, NULL); + argv = ay_to_strv (v_argv, &argc); - working_directory = ay_to_string (v_wd, &error); - if (error) - goto out; - display_name = ay_to_string (v_display, &error); - if (error) - goto out; - startup_id = ay_to_string (v_sid, &error); - if (error) - goto out; - envv = ay_to_strv (v_envv, NULL); - argv = ay_to_strv (v_argv, &argc); + _terminal_debug_print (TERMINAL_DEBUG_FACTORY, + "Factory invoked with working-dir='%s' display='%s' startup-id='%s'" + "workspace='%d'\n", + working_directory ? working_directory : "(null)", + display_name ? display_name : "(null)", + startup_id ? startup_id : "(null)", + initial_workspace); - _terminal_debug_print (TERMINAL_DEBUG_FACTORY, - "Factory invoked with working-dir='%s' display='%s' startup-id='%s'" - "workspace='%d'\n", - working_directory ? working_directory : "(null)", - display_name ? display_name : "(null)", - startup_id ? startup_id : "(null)", - initial_workspace); - - options = terminal_options_parse (working_directory, - display_name, - startup_id, - envv, - TRUE, - TRUE, - &argc, &argv, - &error, - NULL); - - if (options != NULL) - { - options->initial_workspace = initial_workspace; - - terminal_app_handle_options (terminal_app_get (), options, FALSE /* no resume */, &error); - terminal_options_free (options); - } + options = terminal_options_parse (working_directory, + display_name, + startup_id, + envv, + TRUE, + TRUE, + &argc, &argv, + &error, + NULL); + + if (options != NULL) { + options->initial_workspace = initial_workspace; + terminal_app_handle_options (terminal_app_get (), options, FALSE /* no resume */, &error); + terminal_options_free (options); + } out: - g_variant_unref (v_wd); - g_free (working_directory); - g_variant_unref (v_display); - g_free (display_name); - g_variant_unref (v_sid); - g_free (startup_id); - g_variant_unref (v_envv); - g_strfreev (envv); - g_variant_unref (v_argv); - g_strfreev (argv); + g_variant_unref (v_wd); + g_free (working_directory); + g_variant_unref (v_display); + g_free (display_name); + g_variant_unref (v_sid); + g_free (startup_id); + g_variant_unref (v_envv); + g_strfreev (envv); + g_variant_unref (v_argv); + g_strfreev (argv); - if (error == NULL) - { - g_dbus_method_invocation_return_value (invocation, g_variant_new ("()")); - } - else - { - g_dbus_method_invocation_return_gerror (invocation, error); - g_error_free (error); - } - } -} - -static void -bus_acquired_cb (GDBusConnection *connection, - const char *name, - gpointer user_data) -{ - static const char dbus_introspection_xml[] = - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - ""; - - static const GDBusInterfaceVTable interface_vtable = - { - method_call_cb, - NULL, - NULL, - }; - - OwnData *data = (OwnData *) user_data; - GDBusNodeInfo *introspection_data; - guint registration_id; - GError *error = NULL; - - _terminal_debug_print (TERMINAL_DEBUG_FACTORY, - "Bus %s acquired\n", name); - - introspection_data = g_dbus_node_info_new_for_xml (dbus_introspection_xml, NULL); - g_assert (introspection_data != NULL); - - registration_id = g_dbus_connection_register_object (connection, - TERMINAL_FACTORY_SERVICE_PATH, - introspection_data->interfaces[0], - &interface_vtable, - NULL, NULL, - &error); - g_dbus_node_info_unref (introspection_data); - - if (registration_id == 0) - { - g_printerr ("Failed to register object: %s\n", error->message); + if (error != NULL) { g_error_free (error); - data->exit_code = EXIT_FAILURE; - gtk_main_quit (); } + + g_free (message); + return TRUE; } -static void -name_acquired_cb (GDBusConnection *connection, - const char *name, - gpointer user_data) + +static void send_ask_open (const char *name, OwnData *data) { - OwnData *data = (OwnData *) user_data; - GError *error = NULL; - - _terminal_debug_print (TERMINAL_DEBUG_FACTORY, - "Acquired the name %s on the session bus\n", name); - - if (data->options == NULL) - { - /* Name re-acquired!? */ - g_assert_not_reached (); - } - - - if (!terminal_app_handle_options (terminal_app_get (), data->options, TRUE /* do resume */, &error)) - { - g_printerr ("Failed to handle options: %s\n", error->message); - g_error_free (error); - data->exit_code = EXIT_FAILURE; - gtk_main_quit (); - } - - terminal_options_free (data->options); - data->options = NULL; -} - -static void -name_lost_cb (GDBusConnection *connection, - const char *name, - gpointer user_data) -{ - OwnData *data = (OwnData *) user_data; GError *error = NULL; char **envv; int i; @@ -313,26 +242,25 @@ name_lost_cb (GDBusConnection *connection, GString *string; char *s; gsize len; - + struct sockaddr_un unix_dest; + int fd_u, ret; + _terminal_debug_print (TERMINAL_DEBUG_FACTORY, - "Lost the name %s on the session bus\n", name); - - /* Couldn't get the connection? No way to continue! */ - if (connection == NULL) - { - data->exit_code = EXIT_FAILURE; - gtk_main_quit (); - return; - } + "Sending message to %s on the unix socket\n", name); if (data->options == NULL) { /* Already handled */ data->exit_code = EXIT_SUCCESS; - gtk_main_quit (); return; } - + fd_u = socket (AF_UNIX, SOCK_DGRAM, 0); + + if (fd_u < 0) { + data->exit_code = EXIT_FAILURE; + return; + } + _terminal_debug_print (TERMINAL_DEBUG_FACTORY, "Forwarding arguments to existing instance\n"); @@ -373,8 +301,22 @@ name_lost_cb (GDBusConnection *connection, s = g_string_free (string, FALSE); g_variant_builder_add (&builder, "@ay", g_variant_new_from_data (G_VARIANT_TYPE ("ay"), s, len, TRUE, g_free, s)); - - value = g_dbus_connection_call_sync (connection, + + GVariant *todo; + todo = g_variant_builder_end (&builder); + + unix_dest.sun_family = AF_UNIX; + g_snprintf (unix_dest.sun_path, sizeof (unix_dest.sun_path), "%s%s%s%s%s", g_get_user_runtime_dir (), G_DIR_SEPARATOR_S, TERMINAL_FACTORY_UNIX_FOLDER, G_DIR_SEPARATOR_S, name); + + g_free (subcadena); + ret = sendto (fd_u, g_variant_get_data (todo), g_variant_get_size (todo), 0, (struct sockaddr *) &unix_dest, sizeof (unix_dest)); + if (ret < 0) { + g_printerr ("Failed to forward arguments: \n"); + data->exit_code = EXIT_FAILURE; + } + + close (fd_u); + /*value = g_dbus_connection_call_sync (connection, data->factory_name, TERMINAL_FACTORY_SERVICE_PATH, TERMINAL_FACTORY_INTERFACE_NAME, @@ -384,29 +326,17 @@ name_lost_cb (GDBusConnection *connection, G_DBUS_CALL_FLAGS_NONE, -1, NULL, - &error); - if (value == NULL) - { - g_printerr ("Failed to forward arguments: %s\n", error->message); - g_error_free (error); - data->exit_code = EXIT_FAILURE; - gtk_main_quit (); - } - else - { - g_variant_unref (value); - data->exit_code = EXIT_SUCCESS; - } + &error);*/ + g_variant_unref (todo); + data->exit_code = EXIT_SUCCESS; terminal_options_free (data->options); data->options = NULL; - - gtk_main_quit (); } /* Settings storage works as follows: - * /apps/mate-terminal/global/ - * /apps/mate-terminal/profiles/Foo/ + * ~/.config/gatuno-terminal/config + * ~/.config/gatuno-terminal/profiles/Foo/ * * It's somewhat tricky to manage the profiles/ dir since we need to track * the list of profiles, but GSettings doesn't have a concept of notifying that @@ -482,8 +412,11 @@ get_factory_name_for_display (const char *display_name) { GString *name; const char *p; + int len; + + len = strlen (TERMINAL_FACTORY_SERVICE_NAME_PREFIX) + strlen (display_name) + 10 /* NUL */; - name = g_string_sized_new (strlen (TERMINAL_FACTORY_SERVICE_NAME_PREFIX) + strlen (display_name) + 1 /* NUL */); + name = g_string_sized_new (len); g_string_append (name, TERMINAL_FACTORY_SERVICE_NAME_PREFIX); for (p = display_name; *p; ++p) @@ -500,6 +433,83 @@ get_factory_name_for_display (const char *display_name) return g_string_free (name, FALSE); } +void create_factory_unix_socket_folder (void) { + char path[8192]; + + g_snprintf (path, sizeof (path), "%s%s%s", g_get_user_runtime_dir (), G_DIR_SEPARATOR_S, TERMINAL_FACTORY_UNIX_FOLDER); + + /* Crear el directorio de los sockets unix */ + g_mkdir_with_parents (path, 0700); +} + +int get_lock_factory (char *factory_name) { + char path[8192]; + struct flock fl; + int fd; + + g_snprintf (path, sizeof (path), "%s%s%s%s%s.lock", g_get_user_runtime_dir (), G_DIR_SEPARATOR_S, TERMINAL_FACTORY_UNIX_FOLDER, G_DIR_SEPARATOR_S, factory_name); + + fd = open (path, O_RDWR | O_CREAT, 0600); + + fl.l_start = 0; + fl.l_len = 0; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + if (fcntl(fd, F_SETLK, &fl) < 0) { + //fputs("Another instance of this program is running.\n", stderr); + return -1; + } + + factory_fd_lock = fd; + return 0; +} + +void release_lock_factory (void) { + if (factory_fd_lock != -1) { + close (factory_fd_lock); + factory_fd_lock = -1; + } +} + +int do_bind_factory_unix_socket (char *factory_name) { + int s, ret; + struct sockaddr_un name; + + s = socket (AF_UNIX, SOCK_DGRAM, 0); + + if (s < 0) { + return -1; + } + + name.sun_family = AF_UNIX; + g_snprintf (name.sun_path, sizeof (name.sun_path), "%s%s%s%s%s", g_get_user_runtime_dir (), G_DIR_SEPARATOR_S, TERMINAL_FACTORY_UNIX_FOLDER, G_DIR_SEPARATOR_S, factory_name); + + unlink (name.sun_path); + + ret = bind (s, (struct sockaddr *) &name, sizeof (name)); + if (ret < 0) { + close (s); + + return -1; + } + + return s; +} + +void release_factory_unix_socket (char *factory_name) { + struct sockaddr_un name; + if (factory_unix_fd != -1) { + g_source_remove (factory_unix_watch); + factory_unix_watch = 0; + + close (factory_unix_fd); + factory_unix_fd = -1; + + g_snprintf (name.sun_path, sizeof (name.sun_path), "%s%s%s%s%s", g_get_user_runtime_dir (), G_DIR_SEPARATOR_S, TERMINAL_FACTORY_UNIX_FOLDER, G_DIR_SEPARATOR_S, factory_name); + unlink (name.sun_path); + } +} + static int get_initial_workspace (void) { @@ -583,7 +593,7 @@ main (int argc, char **argv) g_error_free (error); exit (EXIT_FAILURE); } - + g_set_application_name (_("Terminal")); /* Unset the these env variables, so they doesn't end up @@ -611,29 +621,65 @@ main (int argc, char **argv) { OwnData *data; guint owner_id; - + GError * error = NULL; data = g_new (OwnData, 1); data->factory_name = get_factory_name_for_display (display_name); data->options = options; data->exit_code = -1; data->argv = argv_copy; data->argc = argc_copy; - + GIOChannel *io_channel; + gtk_init(&argc, &argv); options->initial_workspace = get_initial_workspace (); - - owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, - data->factory_name, - G_BUS_NAME_OWNER_FLAGS_NONE, - bus_acquired_cb, - name_acquired_cb, - name_lost_cb, - data, NULL); - - gtk_main (); - + + /* El proceso del factory funciona así: + * Si me pidieron usar el factory, intentar conseguir el candado. + * Si obtenemos el candado, poner el socket a la escucha. + * Si obtenemos el socket a la escucha, somos el factory. Lanzar terminal + * Si no consigo el socket a la escucha, error. Lanzar terminal como si no hubiera factory. + * Si no obtengo el candado, enviar mensaje a la otra terminal. + */ + create_factory_unix_socket_folder (); + + if (get_lock_factory (data->factory_name) == 0) { + factory_unix_fd = do_bind_factory_unix_socket (data->factory_name); + + if (factory_unix_fd >= 0) { + /* Soy la factory */ + _terminal_debug_print (TERMINAL_DEBUG_FACTORY, "Acquired the name %s on the unix socket\n", data->factory_name); + /* Instalar un vigilador del socket unix */ + io_channel = g_io_channel_unix_new (factory_unix_fd); + + factory_unix_watch = g_io_add_watch (io_channel, G_IO_IN, unix_socket_dgram_recv, NULL); + } else { + /* ¿Tengo lock pero no pude conseguir el socket unix? Raro, soltar el lock, puesto que no puedo recibir las peticiones del factory */ + release_lock_factory (); + + /* Correr la terminal de a solas */ + } + + /* Correr la terminal */ + if (!terminal_app_handle_options (terminal_app_get (), data->options, TRUE /* do resume */, &error)) + { + g_printerr ("Failed to handle options: %s\n", error->message); + g_error_free (error); + data->exit_code = EXIT_FAILURE; + } else { + terminal_options_free (data->options); + data->options = NULL; + + gtk_main (); + } + + release_factory_unix_socket (data->factory_name); + release_lock_factory (); + } else { + /* Ya hay otra factory corriendo, enviar el mensaje */ + send_ask_open (data->factory_name, data); + } + ret = data->exit_code; - g_bus_unown_name (owner_id); g_free (data->factory_name); g_free (data);