On Mon, 18 Dec 2006 00:33:58 +0100
Bill Allombert <[EMAIL PROTECTED]> wrote:

> > > What worry me about the patch is the fact that create_lock() and
> > > check_dpkglock() are not performed in the same order. In particular, if
> > > create_lock() fail we exit with error 0 instead of 1 thus maybe
> > > concealing a real error.
> > 
> > You used to exit with 1 only if both creat_lock and check_dpkglock were
> > false at the same time. If that is what you want, that is easy to fix. 
> > New patch attached. The locking logic is now the same as in the original.
> 
> If I am not mistaken, your new patch adds a race condition between
> create_lock() and check_dpkglock().  If dpkg release the lock after
> create_lock() and before check_dpkglock(), then we do exit(1) while we
> should not. In the original version, check_dpkglock was checked first
> so this race condition was not possible.

I don't think that can happen. In normal circumstances we are a child
process of dpkg until the fork. So it can't release the lock in between
those two calls; it is waiting for us.

Anyway, in the attached patch I swapped the order of check_dpkglock and
create_lock again to match the original.

> Sorry to be so slow dealing with this issue...

That's OK. I'm happy we're dealing with it know. And it is good you are 
reviewing the patch thoroughly.

grts Tim
--- update-menus/update-menus.cc--	2006-10-31 17:00:50.000000000 +0100
+++ update-menus/update-menus.cc	2006-12-18 10:58:58.000000000 +0100
@@ -792,77 +792,54 @@
   int child;
   int parentpid;
   int i,r;
-
   // This function checks to see if dpkg is running, and if
   // so, forks into the background, to let dpkg go on installing
   // packages. After dpkg finished (and wrote the new packages file),
   // this function wakes up, and returns.
-  
-  // Writing the console output correctly is actually non-trivial.
-  // The problem is that we in the following if statement
-  //     if(child=fork())
-  //       exit(0);                            //parent process
-  //     else
-  //       do something (and write to stdout); //child `background' process
-  // want to write to stdout. But if we write to stdout there,
-  // the exit(0) may already have occured, and dpkg may already
-  // have started writing stuff to the console. To prevent that,
-  // I use signals: the `background' process sents a signal to the
-  // parent once it's written everything it wants to stdout,
-  // and only after the parent received the signal it will exit(0);
-  // [Oh god! (added by Bill trying to understand the fork() business)]
 
+  
+  // Check if the dpkg lock is taken
   if (check_dpkglock()) {
-    sigset_t sig,oldsig;
 
-    // Apparently libc2 on 2.0 kernels, with threading on, blocks
-    // SIGUSR1. This blocking would be inherited by children, so
-    // as apt was compiled with -lpthread, this caused problems in 
-    // update-menus. I now get rid of that by using
-    //  - SIGUSR2 instead of SIGUSR1,
-    //  - sigprocmask to unblock SIGUSR2.
-    // Either one of those solutions should be enough, though.
-
-    sigemptyset(&sig);
-    sigaddset(&sig,SIGUSR2);
-    sigprocmask(SIG_UNBLOCK,&sig,&oldsig);
+    // If we can't get the u-m lock, probably another process is waiting 
+    // for dpkg. We can savely exit.
+    r = create_lock();
+    if (!r)
+	exit(0);
+
+    // OK, we need to fork, create log file name and tell user about it.
+    stdoutfile = string("/tmp/update-menus.")+itostring(getpid());
+    config.report(String::compose(_("Waiting for dpkg to finish (will fork to background).\n"
+            "(checking %1)"), DPKG_LOCKFILE),
+            configinfo::report_normal);
+    config.report(String::compose(_("Further output (if any) will appear in %1."), stdoutfile),
+            configinfo::report_normal);
 
-    signal(SIGUSR2, exit_on_signal);
+    // Now do the fork
     parentpid=getpid();
     if ((child=fork())) {
       if (child==-1) {
         perror("update-menus: fork");
         exit(1);
       }
-      pause();
-    } else {
-      r = create_lock();
-      if (r) {
-        stdoutfile = string("/tmp/update-menus.")+itostring(getpid());
-        config.report(String::compose(_("Waiting for dpkg to finish (forking to background).\n"
-            "(checking %1)"), DPKG_LOCKFILE),
-            configinfo::report_normal);
-        config.report(String::compose(_("Further output (if any) will appear in %1."), stdoutfile),
-            configinfo::report_normal);
-        // Close all fd's except the lock fd, for daemon mode.
-        for (i=0;i<32;i++) {
-          if (i != r)
-              close(i);
-        }
-        kill(parentpid, SIGUSR2);
-
-        while(check_dpkglock())
-            sleep(2);
-      } else {
-        // Exit without doing anything. Kill parent too!
-        kill(parentpid,SIGUSR2);
-        exit(0);
+      exit(0);
+    } else {      
+      // Close all fd's except the lock fd, for daemon mode.
+      for (i=0;i<32;i++) {
+        if (i != r)
+            close(i);
       }
+	
+      // Sit and wait until dpkg is finished ...
+      while(check_dpkglock())
+          sleep(2);
     }
   } else {
+    // Dpkg is not locked. Try to get the u-m lock. If we can't get it, 
+    // there may be a real problem. 
     r = create_lock();
     if (!r)
-        exit(1);
+	exit(1);
 
     config.report(_("Dpkg is not locking dpkg status area, good."),
         configinfo::report_verbose);

Attachment: signature.asc
Description: PGP signature

Reply via email to