[Utopia] [patch] mount/unmount when starting/stopping



Hey Robert,

So here's finally a patch that fixes the bug we've discussed somewhat at
length on utopia-list. This patch does the following:

 1. When g-v-m starts it attempts to silently mount all volumes (e.g.
    it doesn't popup any dialogs etc.).

 2. During the life of the g-v-m instance, a list of volumes that have
    mounted (including those mounted at startup) and are still mounted
    is maintained.

 3. When g-v-m is to be terminated (either through a 'die' signal from
    the session manager or through a SIGTERM) all volumes in the list
    mentioned in 2. are unmounted

 4. We no longer listen on the EjectPressed signal from HAL - this
    is removed from HAL as only a tiny fraction of drives implement it

 5. gvm_mount_device now calls mount(1) synchronously; we need that
    return value.

Patch is against CVS a few minutes ago - please apply.

Thanks
David 

 ChangeLog     |   21 ++
 src/manager.c |  292 +++++++++++++++++++++++++++++++++-----
 2 files changed, 277 insertions(+), 36 deletions(-)

Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gnome-volume-manager/ChangeLog,v
retrieving revision 1.87
diff -u -p -r1.87 ChangeLog
--- ChangeLog	13 Aug 2004 20:58:23 -0000	1.87
+++ ChangeLog	17 Aug 2004 13:33:50 -0000
@@ -1,3 +1,24 @@
+2004-08-17  David Zeuthen  <david fubar dk>
+
+	* src/manager.c
+	(gvm_device_mount): Mount synchronously so we know whether it
+	fails and can report
+	(gvm_device_unmount): New function
+	(hal_property_modified): Respect the list of ignored UDI's.
+	Maintain the list of volumes that have been mounted during our
+	lifetime
+	(hal_device_condition): Don't listen for the EjectPressed
+	condition; HAL no longer emits it (it only works on a tiny
+	fraction of devices)
+	(mount_all): New function
+	(unmount_all): New function
+	(handle_sigterm): New function
+	(sigterm_iochn_data): New function
+	(gvm_die): New function
+	(main): Setup handler for 'die' signal. Setup SIGTERM handler and
+	create a pipe and integrate this pipe into the mainloop. Invoke
+	mount_all
+
 2004-08-13  Adam Roben  <aroben fas harvard edu>
 
 	* src/manager.c: storage.drive_type belongs to the media's parent in
Index: src/manager.c
===================================================================
RCS file: /cvs/gnome/gnome-volume-manager/src/manager.c,v
retrieving revision 1.28
diff -u -p -r1.28 manager.c
--- src/manager.c	13 Aug 2004 20:58:23 -0000	1.28
+++ src/manager.c	17 Aug 2004 13:33:50 -0000
@@ -21,6 +21,7 @@
 #include <dbus/dbus.h>
 #include <dbus/dbus-glib.h>
 #include <libhal.h>
+#include <signal.h>
 
 #include "gvm.h"
 
@@ -47,11 +48,18 @@
 #define warn(fmt,arg...) g_warning("%s/%d: " fmt,__FILE__,__LINE__,##arg)
 
 #define BIN_MOUNT	"/bin/mount"	/* what we mount with */
+#define BIN_UMOUNT	"/bin/umount"	/* what we unmount with */
 #define NAUTILUS_COMMAND "/usr/bin/nautilus -n --no-desktop %m"
 
 static struct gvm_configuration config;
 static LibHalContext *hal_ctx;
 
+/** List of UDI's for volumes mounted when starting up */
+static GSList *mount_ignore_udi_list;
+
+/** List of UDI's of all volumes mounted during the lifetime of the program */
+static GSList *all_mounted_volumes;
+
 /*
  * gvm_load_config - synchronize gconf => config structure
  */
@@ -381,21 +389,55 @@ out:
  *
  * Note that this requires that the given device node is in /etc/fstab.  This
  * is intentional.
+ *
+ * @return TRUE iff the mount was succesful
  */
-static void
+static gboolean
 gvm_device_mount (char *device)
 {
 	char *argv[3];
 	GError *error = NULL;
+	gint exit_status;
 
 	argv[0] = BIN_MOUNT;
 	argv[1] = device;
 	argv[2] = NULL;
 
-	g_spawn_async (g_get_home_dir (), argv, NULL, 0, NULL,
-		       NULL, NULL, &error);
-	if (error)
+	if (!g_spawn_sync (g_get_home_dir (), argv, NULL, 0, NULL,
+			   NULL, NULL, NULL, &exit_status, &error)) {
+		warn ("failed to exec " BIN_MOUNT ": %s\n", error->message);
+		return FALSE;
+	}
+
+	return exit_status == 0;
+}
+
+/*
+ * gvm_device_unmount - use BIN_UMOUNT to unmount the given device node.
+ *
+ * Note that this requires that the given device node is in /etc/fstab.  This
+ * is intentional.
+ *
+ * @return TRUE iff the mount was succesful
+ */
+static gboolean
+gvm_device_unmount (char *device)
+{
+	char *argv[3];
+	GError *error = NULL;
+	gint exit_status;
+
+	argv[0] = BIN_UMOUNT;
+	argv[1] = device;
+	argv[2] = NULL;
+
+	if (!g_spawn_sync (g_get_home_dir (), argv, NULL, 0, NULL,
+			   NULL, NULL, NULL, &exit_status, &error)) {
 		warn ("failed to exec " BIN_MOUNT ": %s\n", error->message);
+		return FALSE;
+	}
+
+	return exit_status == 0;
 }
 
 /*
@@ -688,16 +730,60 @@ hal_property_modified (LibHalContext *ct
 		       dbus_bool_t is_removed __attribute__((__unused__)), 
 		       dbus_bool_t is_added __attribute__((__unused__)))
 {
-	if (!g_strcasecmp (key, "volume.is_mounted")) {
-		dbus_bool_t val;
+	dbus_bool_t val;
+	GSList *i;
+
+	if (g_strcasecmp (key, "volume.is_mounted") != 0)
+		return;
+	
+	val = hal_device_get_property_bool (hal_ctx, udi, key);
+	if (val == TRUE) {
+		gboolean ignore_mount;
+
+		dbg ("Mounted: %s\n", udi);
+
+		/* add to list of all volumes mounted during lifetime */
+		all_mounted_volumes = g_slist_append (all_mounted_volumes,
+						      g_strdup (udi));
+
+		ignore_mount = FALSE;
+		for (i = mount_ignore_udi_list; 
+		     i != NULL;
+		     i = g_slist_next (i)) {
+			if (strcmp (udi, (const char *)i->data) == 0) {
+				ignore_mount = TRUE;
+				g_free (i->data);
+				mount_ignore_udi_list = 
+					g_slist_remove_link (
+						mount_ignore_udi_list,
+						i);
+				break;
+			}
+		}
+		
 		
-		val = hal_device_get_property_bool (hal_ctx, udi, key);
-		if (val == TRUE) {
-			dbg ("Mounted: %s\n", udi);
+		/* only autorun if not in ignore list, cf.
+		 * function mount_all
+			 */
+		if (!ignore_mount)
 			gvm_device_autorun (udi);
-		} else
-			dbg ("Unmounted: %s\n", udi);
-	}	
+
+	} else {
+		dbg ("Unmounted: %s\n", udi);
+
+		/* remove from list of all volumes mounted during lifetime */
+
+		for (i=all_mounted_volumes; i != NULL; i = g_slist_next (i)) {
+			if (strcmp (udi, (const char *)i->data) == 0) {
+				g_free (i->data);
+				all_mounted_volumes = 
+					g_slist_remove_link (
+						all_mounted_volumes, i);
+				break;
+			}
+		}
+
+	}
 }
 
 /** Invoked when a device in the GDL emits a condition that cannot be
@@ -714,29 +800,6 @@ hal_device_condition (LibHalContext *ctx
 		      const char *condition_name __attribute__((__unused__)),
 		      DBusMessage * message __attribute__((__unused__)))
 {
-	if (!g_strcasecmp (condition_name, "EjectPressed")) {		
-		char *argv[3];
-		GError *error = NULL;
-		char *device;
-
-		device = hal_device_get_property_string (ctx, udi,
-							 "block.device");
-		if (!device)
-			warn ("cannot get block.device\n");
-		else if (config.eject_command) {
-			argv[0] = config.eject_command;
-			argv[1] = device;
-			argv[2] = NULL;
-
-			g_spawn_async (g_get_home_dir (), argv, NULL, 0, NULL,
-				       NULL, NULL, &error);
-			if (error)
-				warn ("failed to exec %s: %s\n",
-				      config.eject_command, error->message);
-		}
-
-		hal_free_string (device);
-	}
 }
 
 /** Invoked by libhal for integration with our mainloop. 
@@ -793,6 +856,147 @@ gvm_do_hal_init (LibHalFunctions *functi
 	return ctx;
 }
 
+/** Attempt to mount all volumes; should be called on startup.
+ *
+ *  @param  ctx                 LibHal context
+ */
+static void mount_all (LibHalContext *ctx)
+{
+	int i;
+	int num_volumes;
+	char **volumes;
+	char *udi;
+	char *device_file;
+
+	if (!config.automount_media)
+		return;
+
+	volumes = hal_find_device_by_capability (ctx, "volume", &num_volumes);
+	for (i = 0; i < num_volumes; i++) {
+		udi = volumes [i];
+
+		/* don't attempt to mount already mounted volumes */
+		if (!hal_device_property_exists (ctx, udi, 
+						"volume.is_mounted") ||
+		    hal_device_get_property_bool (ctx, udi, 
+						  "volume.is_mounted"))
+			continue;
+
+		/* only mount if the block device got a sensible filesystem */
+		if (!hal_device_property_exists (ctx, udi, 
+						"volume.is_filesystem") ||
+		    !hal_device_get_property_bool (ctx, udi, 
+						   "volume.is_filesystem"))
+			continue;
+
+		device_file = hal_device_get_property_string (ctx, udi,
+							      "block.device");
+
+		if (device_file != NULL ) {
+
+			if (gvm_device_mount (device_file)) {
+				/* yay, it worked; add to list of
+				 * udi's not to autorun from hal_property_
+				 * changed
+				 */
+
+				mount_ignore_udi_list = 
+					g_slist_append (mount_ignore_udi_list,
+							g_strdup (udi));
+
+			}
+
+			hal_free_string (device_file);
+		} else {
+			warn ("no device_file for udi=%s\n", udi);
+		}
+	}
+
+	hal_free_string_array (volumes);
+}
+
+/* Unmount all volumes that were mounted during the lifetime of this
+ * g-v-m instance
+ *
+ *  @param  ctx                 LibHal context
+ */
+static void
+unmount_all (LibHalContext *ctx)
+{
+	GSList *i;
+	char *device_file;
+	char *udi;
+
+	dbg ("unmounting all volumes that we saw mounted in our life\n");
+
+	for (i = all_mounted_volumes; i != NULL; i = g_slist_next (i)) {
+
+		udi = i->data;
+
+		device_file = hal_device_get_property_string (ctx, udi,
+							      "block.device");
+
+		if (device_file != NULL ) {
+			gvm_device_unmount (device_file);
+			hal_free_string (device_file);
+		} else {
+			warn ("no device_file for udi=%s\n", udi);
+		}
+	}
+}
+
+
+static int sigterm_unix_signal_pipe_fds[2];
+static GIOChannel *sigterm_iochn;
+
+static void 
+handle_sigterm (int value)
+{
+	static char marker[1] = {'S'};
+
+	/* write a 'S' character to the other end to tell about
+	 * the signal. Note that 'the other end' is a GIOChannel thingy
+	 * that is only called from the mainloop - thus this is how we
+	 * defer this since UNIX signal handlers are evil
+	 *
+	 * Oh, and write(2) is indeed reentrant */
+	write (sigterm_unix_signal_pipe_fds[1], marker, 1);
+}
+
+static gboolean
+sigterm_iochn_data (GIOChannel *source, 
+		    GIOCondition condition, 
+		    gpointer user_data)
+{
+	GError *err = NULL;
+	gchar data[1];
+	gsize bytes_read;
+
+	/* Empty the pipe */
+	if (G_IO_STATUS_NORMAL != 
+	    g_io_channel_read_chars (source, data, 1, &bytes_read, &err)) {
+		warn ("Error emptying callout notify pipe: %s",
+		      err->message);
+		g_error_free (err);
+		goto out;
+	}
+
+	dbg ("Recieved SIGTERM, initiating shutdown");
+	unmount_all (hal_ctx);
+	gtk_main_quit();
+
+out:
+	return TRUE;
+}
+
+static void
+gvm_die (GnomeClient *client, gpointer user_data) 
+{
+	dbg ("Recieved 'die', initiating shutdown");
+	unmount_all (hal_ctx);
+	gtk_main_quit ();
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -822,13 +1026,29 @@ main (int argc, char *argv[])
 	}
 
 	gtk_signal_connect (GTK_OBJECT (client), "die",
-			    GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+			    GTK_SIGNAL_FUNC (gvm_die), NULL);
 
 	hal_ctx = gvm_do_hal_init (&hal_functions);
 	if (!hal_ctx)
 		return 1;
 
 	gvm_init_config ();
+
+	/* SIGTERM handling via pipes  */
+	if (pipe (sigterm_unix_signal_pipe_fds) != 0) {
+		warn ("Could not setup pipe, errno=%d", errno);
+		return 1;
+	}
+	sigterm_iochn = g_io_channel_unix_new (sigterm_unix_signal_pipe_fds[0]);
+	if (sigterm_iochn == NULL) {
+		warn ("Could not create GIOChannel");
+		return 1;
+	}
+	g_io_add_watch (sigterm_iochn, G_IO_IN, sigterm_iochn_data, NULL);
+	signal (SIGTERM, handle_sigterm);
+
+
+	mount_all (hal_ctx);
 
 	gtk_main ();
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]