Agrego la factory basada en sockets unix y un bloqueo de archivos.

master-1.22
Félix Arreola Rodríguez 2021-07-24 01:21:36 -05:00
parent 0c216e68f6
commit bf285963f3
2 changed files with 262 additions and 214 deletions

View File

@ -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);

View File

@ -26,6 +26,7 @@
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <glib.h>
#include <glib/gstdio.h>
@ -34,6 +35,11 @@
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <semaphore.h>
#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[] =
"<node name='/org/mate/Terminal'>"
"<interface name='org.mate.Terminal.Factory'>"
"<method name='HandleArguments'>"
"<arg type='ay' name='working_directory' direction='in' />"
"<arg type='ay' name='display_name' direction='in' />"
"<arg type='ay' name='startup_id' direction='in' />"
"<arg type='ay' name='environment' direction='in' />"
"<arg type='i' name='workspace' direction='in' />"
"<arg type='ay' name='arguments' direction='in' />"
"</method>"
"</interface>"
"</node>";
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);