diff options
Diffstat (limited to 'mate-session/gsm-autostart-app.c')
-rw-r--r-- | mate-session/gsm-autostart-app.c | 1144 |
1 files changed, 1144 insertions, 0 deletions
diff --git a/mate-session/gsm-autostart-app.c b/mate-session/gsm-autostart-app.c new file mode 100644 index 0000000..a696a96 --- /dev/null +++ b/mate-session/gsm-autostart-app.c @@ -0,0 +1,1144 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 Novell, Inc. + * Copyright (C) 2008 Red Hat, Inc. + * + * 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 + * Lesser 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 <config.h> + +#include <ctype.h> +#include <string.h> +#include <sys/wait.h> +#include <errno.h> + +#include <glib.h> +#include <gio/gio.h> + +#include <mateconf/mateconf-client.h> + +#include "gsm-autostart-app.h" +#include "gsm-util.h" + +enum { + AUTOSTART_LAUNCH_SPAWN = 0, + AUTOSTART_LAUNCH_ACTIVATE +}; + +enum { + GSM_CONDITION_NONE = 0, + GSM_CONDITION_IF_EXISTS = 1, + GSM_CONDITION_UNLESS_EXISTS = 2, + GSM_CONDITION_MATE = 3, + GSM_CONDITION_UNKNOWN = 4 +}; + +#define GSM_SESSION_CLIENT_DBUS_INTERFACE "org.mate.SessionClient" + +struct _GsmAutostartAppPrivate { + char *desktop_filename; + char *desktop_id; + char *startup_id; + + EggDesktopFile *desktop_file; + + /* desktop file state */ + char *condition_string; + gboolean condition; + gboolean autorestart; + + GFileMonitor *condition_monitor; + guint condition_notify_id; + + int launch_type; + GPid pid; + guint child_watch_id; + + DBusGProxy *proxy; + DBusGProxyCall *proxy_call; +}; + +enum { + CONDITION_CHANGED, + LAST_SIGNAL +}; + +enum { + PROP_0, + PROP_DESKTOP_FILENAME +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +#define GSM_AUTOSTART_APP_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), GSM_TYPE_AUTOSTART_APP, GsmAutostartAppPrivate)) + +G_DEFINE_TYPE (GsmAutostartApp, gsm_autostart_app, GSM_TYPE_APP) + +static void +gsm_autostart_app_init (GsmAutostartApp *app) +{ + app->priv = GSM_AUTOSTART_APP_GET_PRIVATE (app); + + app->priv->pid = -1; + app->priv->condition_monitor = NULL; + app->priv->condition = FALSE; +} + +static gboolean +is_disabled (GsmApp *app) +{ + GsmAutostartAppPrivate *priv; + + priv = GSM_AUTOSTART_APP (app)->priv; + + /* GSM_AUTOSTART_APP_ENABLED_KEY key, used by old mate-session */ + if (egg_desktop_file_has_key (priv->desktop_file, + GSM_AUTOSTART_APP_ENABLED_KEY, NULL) && + !egg_desktop_file_get_boolean (priv->desktop_file, + GSM_AUTOSTART_APP_ENABLED_KEY, NULL)) { + g_debug ("app %s is disabled by " GSM_AUTOSTART_APP_ENABLED_KEY, + gsm_app_peek_id (app)); + return TRUE; + } + + /* Hidden key, used by autostart spec */ + if (egg_desktop_file_get_boolean (priv->desktop_file, + EGG_DESKTOP_FILE_KEY_HIDDEN, NULL)) { + g_debug ("app %s is disabled by Hidden", + gsm_app_peek_id (app)); + return TRUE; + } + + /* Check OnlyShowIn/NotShowIn/TryExec */ + if (!egg_desktop_file_can_launch (priv->desktop_file, "MATE")) { + g_debug ("app %s not installed or not for MATE", + gsm_app_peek_id (app)); + return TRUE; + } + + /* Do not check AutostartCondition - this method is only to determine + if the app is unconditionally disabled */ + + return FALSE; +} + +static gboolean +parse_condition_string (const char *condition_string, + guint *condition_kindp, + char **keyp) +{ + const char *space; + const char *key; + int len; + guint kind; + + space = condition_string + strcspn (condition_string, " "); + len = space - condition_string; + key = space; + while (isspace ((unsigned char)*key)) { + key++; + } + + if (!g_ascii_strncasecmp (condition_string, "if-exists", len) && key) { + kind = GSM_CONDITION_IF_EXISTS; + } else if (!g_ascii_strncasecmp (condition_string, "unless-exists", len) && key) { + kind = GSM_CONDITION_UNLESS_EXISTS; + } else if (!g_ascii_strncasecmp (condition_string, "MATE", len)) { + kind = GSM_CONDITION_MATE; + } else { + key = NULL; + kind = GSM_CONDITION_UNKNOWN; + } + + if (keyp != NULL) { + *keyp = g_strdup (key); + } + + if (condition_kindp != NULL) { + *condition_kindp = kind; + } + + return (kind != GSM_CONDITION_UNKNOWN); +} + +static void +if_exists_condition_cb (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event, + GsmApp *app) +{ + GsmAutostartAppPrivate *priv; + gboolean condition = FALSE; + + priv = GSM_AUTOSTART_APP (app)->priv; + + switch (event) { + case G_FILE_MONITOR_EVENT_CREATED: + condition = TRUE; + break; + case G_FILE_MONITOR_EVENT_DELETED: + condition = FALSE; + break; + default: + /* Ignore any other monitor event */ + return; + } + + /* Emit only if the condition actually changed */ + if (condition != priv->condition) { + priv->condition = condition; + g_signal_emit (app, signals[CONDITION_CHANGED], 0, condition); + } +} + +static void +unless_exists_condition_cb (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event, + GsmApp *app) +{ + GsmAutostartAppPrivate *priv; + gboolean condition = FALSE; + + priv = GSM_AUTOSTART_APP (app)->priv; + + switch (event) { + case G_FILE_MONITOR_EVENT_CREATED: + condition = FALSE; + break; + case G_FILE_MONITOR_EVENT_DELETED: + condition = TRUE; + break; + default: + /* Ignore any other monitor event */ + return; + } + + /* Emit only if the condition actually changed */ + if (condition != priv->condition) { + priv->condition = condition; + g_signal_emit (app, signals[CONDITION_CHANGED], 0, condition); + } +} + +static void +mateconf_condition_cb (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + GsmApp *app; + GsmAutostartAppPrivate *priv; + gboolean condition; + + g_return_if_fail (GSM_IS_APP (user_data)); + + app = GSM_APP (user_data); + + priv = GSM_AUTOSTART_APP (app)->priv; + + condition = FALSE; + if (entry->value != NULL && entry->value->type == MATECONF_VALUE_BOOL) { + condition = mateconf_value_get_bool (entry->value); + } + + g_debug ("GsmAutostartApp: app:%s condition changed condition:%d", + gsm_app_peek_id (app), + condition); + + /* Emit only if the condition actually changed */ + if (condition != priv->condition) { + priv->condition = condition; + g_signal_emit (app, signals[CONDITION_CHANGED], 0, condition); + } +} + +static void +setup_condition_monitor (GsmAutostartApp *app) +{ + guint kind; + char *key; + gboolean res; + gboolean disabled; + + if (app->priv->condition_monitor != NULL) { + g_file_monitor_cancel (app->priv->condition_monitor); + } + + if (app->priv->condition_notify_id > 0) { + MateConfClient *client; + client = mateconf_client_get_default (); + mateconf_client_notify_remove (client, + app->priv->condition_notify_id); + app->priv->condition_notify_id = 0; + } + + if (app->priv->condition_string == NULL) { + return; + } + + /* if it is disabled outright there is no point in monitoring */ + if (is_disabled (GSM_APP (app))) { + return; + } + + key = NULL; + res = parse_condition_string (app->priv->condition_string, &kind, &key); + if (! res) { + g_free (key); + return; + } + + if (key == NULL) { + return; + } + + if (kind == GSM_CONDITION_IF_EXISTS) { + char *file_path; + GFile *file; + + file_path = g_build_filename (g_get_user_config_dir (), key, NULL); + + disabled = !g_file_test (file_path, G_FILE_TEST_EXISTS); + + file = g_file_new_for_path (file_path); + app->priv->condition_monitor = g_file_monitor_file (file, 0, NULL, NULL); + + g_signal_connect (app->priv->condition_monitor, "changed", + G_CALLBACK (if_exists_condition_cb), + app); + + g_object_unref (file); + g_free (file_path); + } else if (kind == GSM_CONDITION_UNLESS_EXISTS) { + char *file_path; + GFile *file; + + file_path = g_build_filename (g_get_user_config_dir (), key, NULL); + + disabled = g_file_test (file_path, G_FILE_TEST_EXISTS); + + file = g_file_new_for_path (file_path); + app->priv->condition_monitor = g_file_monitor_file (file, 0, NULL, NULL); + + g_signal_connect (app->priv->condition_monitor, "changed", + G_CALLBACK (unless_exists_condition_cb), + app); + + g_object_unref (file); + g_free (file_path); + } else if (kind == GSM_CONDITION_MATE) { + MateConfClient *client; + char *dir; + + client = mateconf_client_get_default (); + g_assert (MATECONF_IS_CLIENT (client)); + + disabled = !mateconf_client_get_bool (client, key, NULL); + + dir = g_path_get_dirname (key); + + mateconf_client_add_dir (client, + dir, + MATECONF_CLIENT_PRELOAD_NONE, NULL); + g_free (dir); + + app->priv->condition_notify_id = mateconf_client_notify_add (client, + key, + mateconf_condition_cb, + app, NULL, NULL); + g_object_unref (client); + } else { + disabled = TRUE; + } + + g_free (key); + + /* FIXME: cache the disabled value? */ +} + +static gboolean +load_desktop_file (GsmAutostartApp *app) +{ + char *dbus_name; + char *startup_id; + char *phase_str; + int phase; + gboolean res; + + if (app->priv->desktop_file == NULL) { + return FALSE; + } + + phase_str = egg_desktop_file_get_string (app->priv->desktop_file, + GSM_AUTOSTART_APP_PHASE_KEY, + NULL); + if (phase_str != NULL) { + if (strcmp (phase_str, "Initialization") == 0) { + phase = GSM_MANAGER_PHASE_INITIALIZATION; + } else if (strcmp (phase_str, "WindowManager") == 0) { + phase = GSM_MANAGER_PHASE_WINDOW_MANAGER; + } else if (strcmp (phase_str, "Panel") == 0) { + phase = GSM_MANAGER_PHASE_PANEL; + } else if (strcmp (phase_str, "Desktop") == 0) { + phase = GSM_MANAGER_PHASE_DESKTOP; + } else { + phase = GSM_MANAGER_PHASE_APPLICATION; + } + + g_free (phase_str); + } else { + phase = GSM_MANAGER_PHASE_APPLICATION; + } + + dbus_name = egg_desktop_file_get_string (app->priv->desktop_file, + GSM_AUTOSTART_APP_DBUS_NAME_KEY, + NULL); + if (dbus_name != NULL) { + app->priv->launch_type = AUTOSTART_LAUNCH_ACTIVATE; + } else { + app->priv->launch_type = AUTOSTART_LAUNCH_SPAWN; + } + + /* this must only be done on first load */ + switch (app->priv->launch_type) { + case AUTOSTART_LAUNCH_SPAWN: + startup_id = + egg_desktop_file_get_string (app->priv->desktop_file, + GSM_AUTOSTART_APP_STARTUP_ID_KEY, + NULL); + + if (startup_id == NULL) { + startup_id = gsm_util_generate_startup_id (); + } + break; + case AUTOSTART_LAUNCH_ACTIVATE: + startup_id = g_strdup (dbus_name); + break; + default: + g_assert_not_reached (); + } + + res = egg_desktop_file_has_key (app->priv->desktop_file, + GSM_AUTOSTART_APP_AUTORESTART_KEY, + NULL); + if (res) { + app->priv->autorestart = egg_desktop_file_get_boolean (app->priv->desktop_file, + GSM_AUTOSTART_APP_AUTORESTART_KEY, + NULL); + } else { + app->priv->autorestart = FALSE; + } + + g_free (app->priv->condition_string); + app->priv->condition_string = egg_desktop_file_get_string (app->priv->desktop_file, + "AutostartCondition", + NULL); + setup_condition_monitor (app); + + g_object_set (app, + "phase", phase, + "startup-id", startup_id, + NULL); + + g_free (startup_id); + g_free (dbus_name); + + return TRUE; +} + +static void +gsm_autostart_app_set_desktop_filename (GsmAutostartApp *app, + const char *desktop_filename) +{ + GError *error; + + if (app->priv->desktop_file != NULL) { + egg_desktop_file_free (app->priv->desktop_file); + app->priv->desktop_file = NULL; + g_free (app->priv->desktop_id); + } + + if (desktop_filename == NULL) { + return; + } + + app->priv->desktop_id = g_path_get_basename (desktop_filename); + + error = NULL; + app->priv->desktop_file = egg_desktop_file_new (desktop_filename, &error); + if (app->priv->desktop_file == NULL) { + g_warning ("Could not parse desktop file %s: %s", + desktop_filename, + error->message); + g_error_free (error); + return; + } +} + +static void +gsm_autostart_app_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsmAutostartApp *self; + + self = GSM_AUTOSTART_APP (object); + + switch (prop_id) { + case PROP_DESKTOP_FILENAME: + gsm_autostart_app_set_desktop_filename (self, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_autostart_app_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsmAutostartApp *self; + + self = GSM_AUTOSTART_APP (object); + + switch (prop_id) { + case PROP_DESKTOP_FILENAME: + if (self->priv->desktop_file != NULL) { + g_value_set_string (value, egg_desktop_file_get_source (self->priv->desktop_file)); + } else { + g_value_set_string (value, NULL); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_autostart_app_dispose (GObject *object) +{ + GsmAutostartAppPrivate *priv; + + priv = GSM_AUTOSTART_APP (object)->priv; + + if (priv->startup_id) { + g_free (priv->startup_id); + priv->startup_id = NULL; + } + + if (priv->condition_string) { + g_free (priv->condition_string); + priv->condition_string = NULL; + } + + if (priv->desktop_file) { + egg_desktop_file_free (priv->desktop_file); + priv->desktop_file = NULL; + } + + if (priv->desktop_id) { + g_free (priv->desktop_id); + priv->desktop_id = NULL; + } + + if (priv->child_watch_id > 0) { + g_source_remove (priv->child_watch_id); + priv->child_watch_id = 0; + } + + if (priv->proxy_call != NULL) { + dbus_g_proxy_cancel_call (priv->proxy, priv->proxy_call); + priv->proxy_call = NULL; + } + + if (priv->proxy != NULL) { + g_object_unref (priv->proxy); + priv->proxy = NULL; + } + + if (priv->condition_monitor) { + g_file_monitor_cancel (priv->condition_monitor); + } + + G_OBJECT_CLASS (gsm_autostart_app_parent_class)->dispose (object); +} + +static gboolean +is_running (GsmApp *app) +{ + GsmAutostartAppPrivate *priv; + gboolean is; + + priv = GSM_AUTOSTART_APP (app)->priv; + + /* is running if pid is still valid or + * or a client is connected + */ + /* FIXME: check client */ + is = (priv->pid != -1); + + return is; +} + +static gboolean +is_conditionally_disabled (GsmApp *app) +{ + GsmAutostartAppPrivate *priv; + gboolean res; + gboolean disabled; + char *key; + guint kind; + + priv = GSM_AUTOSTART_APP (app)->priv; + + /* Check AutostartCondition */ + if (priv->condition_string == NULL) { + return FALSE; + } + + key = NULL; + res = parse_condition_string (priv->condition_string, &kind, &key); + if (! res) { + g_free (key); + return TRUE; + } + + if (key == NULL) { + return TRUE; + } + + if (kind == GSM_CONDITION_IF_EXISTS) { + char *file_path; + + file_path = g_build_filename (g_get_user_config_dir (), key, NULL); + disabled = !g_file_test (file_path, G_FILE_TEST_EXISTS); + g_free (file_path); + } else if (kind == GSM_CONDITION_UNLESS_EXISTS) { + char *file_path; + + file_path = g_build_filename (g_get_user_config_dir (), key, NULL); + disabled = g_file_test (file_path, G_FILE_TEST_EXISTS); + g_free (file_path); + } else if (kind == GSM_CONDITION_MATE) { + MateConfClient *client; + client = mateconf_client_get_default (); + g_assert (MATECONF_IS_CLIENT (client)); + disabled = !mateconf_client_get_bool (client, key, NULL); + g_object_unref (client); + } else { + disabled = TRUE; + } + + /* Set initial condition */ + priv->condition = !disabled; + + g_free (key); + + return disabled; +} + +static void +app_exited (GPid pid, + int status, + GsmAutostartApp *app) +{ + g_debug ("GsmAutostartApp: (pid:%d) done (%s:%d)", + (int) pid, + WIFEXITED (status) ? "status" + : WIFSIGNALED (status) ? "signal" + : "unknown", + WIFEXITED (status) ? WEXITSTATUS (status) + : WIFSIGNALED (status) ? WTERMSIG (status) + : -1); + + g_spawn_close_pid (app->priv->pid); + app->priv->pid = -1; + app->priv->child_watch_id = 0; + + if (WIFEXITED (status)) { + gsm_app_exited (GSM_APP (app)); + } else if (WIFSIGNALED (status)) { + gsm_app_died (GSM_APP (app)); + } +} + +static int +_signal_pid (int pid, + int signal) +{ + int status = -1; + + /* perhaps block sigchld */ + g_debug ("GsmAutostartApp: sending signal %d to process %d", signal, pid); + errno = 0; + status = kill (pid, signal); + + if (status < 0) { + if (errno == ESRCH) { + g_warning ("Child process %d was already dead.", + (int)pid); + } else { + g_warning ("Couldn't kill child process %d: %s", + pid, + g_strerror (errno)); + } + } + + /* perhaps unblock sigchld */ + + return status; +} + +static gboolean +autostart_app_stop_spawn (GsmAutostartApp *app, + GError **error) +{ + int res; + + if (app->priv->pid < 1) { + g_set_error (error, + GSM_APP_ERROR, + GSM_APP_ERROR_STOP, + "Not running"); + return FALSE; + } + + res = _signal_pid (app->priv->pid, SIGTERM); + if (res != 0) { + g_set_error (error, + GSM_APP_ERROR, + GSM_APP_ERROR_STOP, + "Unable to stop: %s", + g_strerror (errno)); + return FALSE; + } + + return TRUE; +} + +static gboolean +autostart_app_stop_activate (GsmAutostartApp *app, + GError **error) +{ + return TRUE; +} + +static gboolean +gsm_autostart_app_stop (GsmApp *app, + GError **error) +{ + GsmAutostartApp *aapp; + gboolean ret; + + aapp = GSM_AUTOSTART_APP (app); + + g_return_val_if_fail (aapp->priv->desktop_file != NULL, FALSE); + + switch (aapp->priv->launch_type) { + case AUTOSTART_LAUNCH_SPAWN: + ret = autostart_app_stop_spawn (aapp, error); + break; + case AUTOSTART_LAUNCH_ACTIVATE: + ret = autostart_app_stop_activate (aapp, error); + break; + default: + g_assert_not_reached (); + break; + } + + return ret; +} + +static gboolean +autostart_app_start_spawn (GsmAutostartApp *app, + GError **error) +{ + char *env[2] = { NULL, NULL }; + gboolean success; + GError *local_error; + const char *startup_id; + char *command; + + startup_id = gsm_app_peek_startup_id (GSM_APP (app)); + g_assert (startup_id != NULL); + + env[0] = g_strdup_printf ("DESKTOP_AUTOSTART_ID=%s", startup_id); + + local_error = NULL; + command = egg_desktop_file_parse_exec (app->priv->desktop_file, + NULL, + &local_error); + if (command == NULL) { + g_warning ("Unable to parse command from '%s': %s", + egg_desktop_file_get_source (app->priv->desktop_file), + local_error->message); + g_error_free (local_error); + } + + g_debug ("GsmAutostartApp: starting %s: command=%s startup-id=%s", app->priv->desktop_id, command, startup_id); + g_free (command); + + g_free (app->priv->startup_id); + local_error = NULL; + success = egg_desktop_file_launch (app->priv->desktop_file, + NULL, + &local_error, + EGG_DESKTOP_FILE_LAUNCH_PUTENV, env, + EGG_DESKTOP_FILE_LAUNCH_FLAGS, G_SPAWN_DO_NOT_REAP_CHILD, + EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, &app->priv->pid, + EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID, &app->priv->startup_id, + NULL); + g_free (env[0]); + + if (success) { + g_debug ("GsmAutostartApp: started pid:%d", app->priv->pid); + app->priv->child_watch_id = g_child_watch_add (app->priv->pid, + (GChildWatchFunc)app_exited, + app); + } else { + g_set_error (error, + GSM_APP_ERROR, + GSM_APP_ERROR_START, + "Unable to start application: %s", local_error->message); + g_error_free (local_error); + } + + return success; +} + +static void +start_notify (DBusGProxy *proxy, + DBusGProxyCall *call, + GsmAutostartApp *app) +{ + gboolean res; + GError *error; + + error = NULL; + res = dbus_g_proxy_end_call (proxy, + call, + &error, + G_TYPE_INVALID); + app->priv->proxy_call = NULL; + + if (! res) { + g_warning ("GsmAutostartApp: Error starting application: %s", error->message); + g_error_free (error); + } else { + g_debug ("GsmAutostartApp: Started application %s", app->priv->desktop_id); + } +} + +static gboolean +autostart_app_start_activate (GsmAutostartApp *app, + GError **error) +{ + const char *name; + char *path; + char *arguments; + DBusGConnection *bus; + GError *local_error; + + local_error = NULL; + bus = dbus_g_bus_get (DBUS_BUS_SESSION, &local_error); + if (bus == NULL) { + if (local_error != NULL) { + g_warning ("error getting session bus: %s", local_error->message); + } + g_propagate_error (error, local_error); + return FALSE; + } + + name = gsm_app_peek_startup_id (GSM_APP (app)); + g_assert (name != NULL); + + path = egg_desktop_file_get_string (app->priv->desktop_file, + GSM_AUTOSTART_APP_DBUS_PATH_KEY, + NULL); + if (path == NULL) { + /* just pick one? */ + path = g_strdup ("/"); + } + + arguments = egg_desktop_file_get_string (app->priv->desktop_file, + GSM_AUTOSTART_APP_DBUS_ARGS_KEY, + NULL); + + app->priv->proxy = dbus_g_proxy_new_for_name (bus, + name, + path, + GSM_SESSION_CLIENT_DBUS_INTERFACE); + if (app->priv->proxy == NULL) { + g_set_error (error, + GSM_APP_ERROR, + GSM_APP_ERROR_START, + "Unable to start application: unable to create proxy for client"); + return FALSE; + } + + app->priv->proxy_call = dbus_g_proxy_begin_call (app->priv->proxy, + "Start", + (DBusGProxyCallNotify)start_notify, + app, + NULL, + G_TYPE_STRING, arguments, + G_TYPE_INVALID); + if (app->priv->proxy_call == NULL) { + g_object_unref (app->priv->proxy); + app->priv->proxy = NULL; + g_set_error (error, + GSM_APP_ERROR, + GSM_APP_ERROR_START, + "Unable to start application: unable to call Start on client"); + return FALSE; + } + + return TRUE; +} + +static gboolean +gsm_autostart_app_start (GsmApp *app, + GError **error) +{ + GsmAutostartApp *aapp; + gboolean ret; + + aapp = GSM_AUTOSTART_APP (app); + + g_return_val_if_fail (aapp->priv->desktop_file != NULL, FALSE); + + switch (aapp->priv->launch_type) { + case AUTOSTART_LAUNCH_SPAWN: + ret = autostart_app_start_spawn (aapp, error); + break; + case AUTOSTART_LAUNCH_ACTIVATE: + ret = autostart_app_start_activate (aapp, error); + break; + default: + g_assert_not_reached (); + break; + } + + return ret; +} + +static gboolean +gsm_autostart_app_restart (GsmApp *app, + GError **error) +{ + GError *local_error; + gboolean res; + + /* ignore stop errors - it is fine if it is already stopped */ + local_error = NULL; + res = gsm_app_stop (app, &local_error); + if (! res) { + g_debug ("GsmAutostartApp: Couldn't stop app: %s", local_error->message); + g_error_free (local_error); + } + + res = gsm_app_start (app, &local_error); + if (! res) { + g_propagate_error (error, local_error); + return FALSE; + } + + return TRUE; +} + +static gboolean +gsm_autostart_app_provides (GsmApp *app, + const char *service) +{ + char **provides; + gsize len; + gsize i; + GsmAutostartApp *aapp; + + g_return_val_if_fail (GSM_IS_APP (app), FALSE); + + aapp = GSM_AUTOSTART_APP (app); + + if (aapp->priv->desktop_file == NULL) { + return FALSE; + } + + provides = egg_desktop_file_get_string_list (aapp->priv->desktop_file, + GSM_AUTOSTART_APP_PROVIDES_KEY, + &len, NULL); + if (!provides) { + return FALSE; + } + + for (i = 0; i < len; i++) { + if (!strcmp (provides[i], service)) { + g_strfreev (provides); + return TRUE; + } + } + + g_strfreev (provides); + return FALSE; +} + +static gboolean +gsm_autostart_app_has_autostart_condition (GsmApp *app, + const char *condition) +{ + GsmAutostartApp *aapp; + + g_return_val_if_fail (GSM_IS_APP (app), FALSE); + g_return_val_if_fail (condition != NULL, FALSE); + + aapp = GSM_AUTOSTART_APP (app); + + if (aapp->priv->condition_string == NULL) { + return FALSE; + } + + if (strcmp (aapp->priv->condition_string, condition) == 0) { + return TRUE; + } + + return FALSE; +} + +static gboolean +gsm_autostart_app_get_autorestart (GsmApp *app) +{ + gboolean res; + gboolean autorestart; + + if (GSM_AUTOSTART_APP (app)->priv->desktop_file == NULL) { + return FALSE; + } + + autorestart = FALSE; + + res = egg_desktop_file_has_key (GSM_AUTOSTART_APP (app)->priv->desktop_file, + GSM_AUTOSTART_APP_AUTORESTART_KEY, + NULL); + if (res) { + autorestart = egg_desktop_file_get_boolean (GSM_AUTOSTART_APP (app)->priv->desktop_file, + GSM_AUTOSTART_APP_AUTORESTART_KEY, + NULL); + } + + return autorestart; +} + +static const char * +gsm_autostart_app_get_app_id (GsmApp *app) +{ + const char *location; + const char *slash; + + if (GSM_AUTOSTART_APP (app)->priv->desktop_file == NULL) { + return NULL; + } + + location = egg_desktop_file_get_source (GSM_AUTOSTART_APP (app)->priv->desktop_file); + + slash = strrchr (location, '/'); + if (slash != NULL) { + return slash + 1; + } else { + return location; + } +} + +static GObject * +gsm_autostart_app_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GsmAutostartApp *app; + + app = GSM_AUTOSTART_APP (G_OBJECT_CLASS (gsm_autostart_app_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + if (! load_desktop_file (app)) { + g_object_unref (app); + app = NULL; + } + + return G_OBJECT (app); +} + +static void +gsm_autostart_app_class_init (GsmAutostartAppClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GsmAppClass *app_class = GSM_APP_CLASS (klass); + + object_class->set_property = gsm_autostart_app_set_property; + object_class->get_property = gsm_autostart_app_get_property; + object_class->dispose = gsm_autostart_app_dispose; + object_class->constructor = gsm_autostart_app_constructor; + + app_class->impl_is_disabled = is_disabled; + app_class->impl_is_conditionally_disabled = is_conditionally_disabled; + app_class->impl_is_running = is_running; + app_class->impl_start = gsm_autostart_app_start; + app_class->impl_restart = gsm_autostart_app_restart; + app_class->impl_stop = gsm_autostart_app_stop; + app_class->impl_provides = gsm_autostart_app_provides; + app_class->impl_has_autostart_condition = gsm_autostart_app_has_autostart_condition; + app_class->impl_get_app_id = gsm_autostart_app_get_app_id; + app_class->impl_get_autorestart = gsm_autostart_app_get_autorestart; + + g_object_class_install_property (object_class, + PROP_DESKTOP_FILENAME, + g_param_spec_string ("desktop-filename", + "Desktop filename", + "Freedesktop .desktop file", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + signals[CONDITION_CHANGED] = + g_signal_new ("condition-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmAutostartAppClass, condition_changed), + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, + 1, + G_TYPE_BOOLEAN); + + g_type_class_add_private (object_class, sizeof (GsmAutostartAppPrivate)); +} + +GsmApp * +gsm_autostart_app_new (const char *desktop_file) +{ + GsmAutostartApp *app; + + app = g_object_new (GSM_TYPE_AUTOSTART_APP, + "desktop-filename", desktop_file, + NULL); + + return GSM_APP (app); +} |