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);
signature.asc
Description: PGP signature