Bug#293214: different locking approach

Michael Vogt Michael Vogt <mvogt@acm.org>, 293214@bugs.debian.org
Mon, 28 Feb 2005 17:00:35 +0100


--45Z9DzgjV8m4Oswq
Content-Type: text/plain; charset=iso-8859-1
Content-Disposition: inline

Hi Gustavo,

I got a bugreport from a user about a left-over lockfile from gksu. I
attached a patch that implements locking in the same way as
apt/synaptic use it. It should be more robust but also it has the
drawback that it will not work on nfs homedirs that don't support
locking and read-only homedirs.

I hope you like the patch.

Cheers,
 Michael

-- 
Linux is not The Answer. Yes is the answer. Linux is The Question. - Neo

--45Z9DzgjV8m4Oswq
Content-Type: text/plain; charset=iso-8859-1
Content-Disposition: attachment; filename="gksu-fcntl-locks.diff"

diff -ru orig/gksu-1.2.3/gksu/gksu.c gksu-1.2.3/gksu/gksu.c
--- orig/gksu-1.2.3/gksu/gksu.c	2005-02-28 16:14:17.532791080 +0100
+++ gksu-1.2.3/gksu/gksu.c	2005-02-28 16:51:35.302598360 +0100
@@ -137,11 +137,97 @@
     }
 }
 
-void
+pid_t test_lock(const char* fname)
+{
+   int FD = open(fname, 0);
+   if(FD < 0) {
+      if(errno == ENOENT) {
+	 // File does not exist
+	 return 0; 
+      } else {
+	 perror("open");
+	 return(-1);
+      }
+   }
+   struct flock fl;
+   fl.l_type = F_WRLCK;
+   fl.l_whence = SEEK_SET;
+   fl.l_start = 0;
+   fl.l_len = 0;
+   if (fcntl(FD, F_GETLK, &fl) < 0) {
+      g_critical("fcntl error");
+      close(FD);
+      return(-1);
+   }
+   close(FD);
+   // lock is available
+   if(fl.l_type == F_UNLCK)
+      return(0);
+   // file is locked by another process
+   return (fl.l_pid);
+}
+
+int get_lock(const char *File)
+{
+   int FD = open(File,O_RDWR | O_CREAT | O_TRUNC,0640);
+   if (FD < 0)
+   {
+      // Read only .. cant have locking problems there.
+      if (errno == EROFS)
+      {
+	 g_warning(_("Not using locking for read only lock file %s"),File);
+	 return dup(0);       // Need something for the caller to close
+      }
+      
+      // Feh.. We do this to distinguish the lock vs open case..
+      errno = EPERM;
+      return -1;
+   }
+   fcntl(FD,F_SETFD, FD_CLOEXEC);
+      
+   // Aquire a write lock
+   struct flock fl;
+   fl.l_type = F_WRLCK;
+   fl.l_whence = SEEK_SET;
+   fl.l_start = 0;
+   fl.l_len = 0;
+   if (fcntl(FD,F_SETLK,&fl) == -1)
+   {
+      if (errno == ENOLCK)
+      {
+	 g_warning(_("Not using locking for nfs mounted lock file %s"), File);
+	 return dup(0);       // Need something for the caller to close	 
+      }      
+      
+      int Tmp = errno;
+      close(FD);
+      errno = Tmp;
+      return -1;
+   }
+
+   return FD;
+}
+
+int
 grab_keyboard_and_mouse (GtkWidget *dialog)
 {
   GdkGrabStatus status;
   gint grab_tries = 0;
+  gint lock = -1;
+  
+  gchar *fname = g_strdup_printf ("%s/.gksu.lock", getenv ("HOME"));
+  pid_t pid = test_lock (fname);
+
+  if (pid != 0)
+    {
+      g_warning ("Lock taken by pid: %i. Exiting.", pid);
+      exit (0);
+    }
+  
+  lock = get_lock(fname);
+  if( lock < 0)
+    g_warning ("Unable to create lock file.");
+  g_free (fname);
   
   gtk_widget_show_all (dialog);
 
@@ -179,16 +265,21 @@
     }
   
   gdk_x11_grab_server();
+  return lock;
 }
 
 void
-ungrab_keyboard_and_mouse ()
+ungrab_keyboard_and_mouse (int lock)
 {
+
   /* Ungrab */
   XUngrabServer(GDK_DISPLAY());
   gdk_pointer_ungrab(GDK_CURRENT_TIME);
   gdk_keyboard_ungrab(GDK_CURRENT_TIME);
   gdk_flush();
+
+  close(lock);
+ 
 }
 
 #define GKSU_CONFFILE "/etc/gksu.conf"
@@ -282,12 +373,12 @@
       else if (!strcmp ("sudo-mode", key))
 	{
 	  if (!strcasecmp ("yes", value))
-	    sudo_mode = FALSE;
+	    sudo_mode = TRUE;
 	}
       else if (!strcmp ("prompt", key))
 	{
 	  if (!strcasecmp ("yes", value))
-	    prompt = FALSE;
+	    prompt = TRUE;
 	}
 
       g_strfreev (tmp);
@@ -463,7 +554,7 @@
   if (force_grab)
     grab = TRUE;
 
-  if (prompt && (!grab))
+  if (prompt)
     {
       GtkWidget *d;
       
@@ -494,13 +585,14 @@
 	  g_free (msg);
 	}
 
+      int lock;
       if (grab)
-	grab_keyboard_and_mouse (dialog);
+	lock = grab_keyboard_and_mouse (dialog);
 
       retvalue = gtk_dialog_run (GTK_DIALOG(dialog));
       gtk_widget_hide (dialog);
       if (grab)
-	ungrab_keyboard_and_mouse ();
+	ungrab_keyboard_and_mouse (lock);
 
       /* 
 	 the user may have pressed cancel or

--45Z9DzgjV8m4Oswq--