Package: tcsh
Version: 6.17.02-4.1
Severity: normal
Tags: patch

An acknowleged problem with tcsh's savehist merge feature is that it
doesn't operate atomically, and so may corrupt the history file if
many shells exit at once, such as will happen when logging out of a
gnome session with many tcsh terminals open.  This became a recurrent
problem for me recently.

I include a possible patch to deal with this: creating and locking a
.history.lock file during savehist merge operations.  After applying
the patch you can activate the feature by adding "lock" to the savehist
value:

  set savehist=(999 merge lock)

As currenly implemented, (999 lock) will be ignored, as will (999 lock
merge).  It has to be (<number> merge lock).

I've used fcntl in order to have some hope that this works for
NFS-mounted home directories, but I've no idea whether its use could
introduce portability problems --- I've only tried it out on my amd64
Debian Linux system, where it *appears* to work.  Hope it proves
useful.

Regards,
Conrad

-- System Information:
Debian Release: wheezy/sid
  APT prefers testing
  APT policy: (500, 'testing')
Architecture: amd64 (x86_64)

Kernel: Linux 2.6.39-2-amd64 (SMP w/8 CPU cores)
Locale: LANG=en_GB.UTF-8, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash

Versions of packages tcsh depends on:
ii  libc6                         2.13-7     Embedded GNU C Library: Shared lib
ii  libncurses5                   5.9-1      shared libraries for terminal hand

tcsh recommends no packages.

tcsh suggests no packages.

-- no debconf information
diff -rc tcsh-6.17.02~/debian/changelog tcsh-6.17.02/debian/changelog
*** tcsh-6.17.02~/debian/changelog	2011-07-06 19:20:17.000000000 +0100
--- tcsh-6.17.02/debian/changelog	2011-07-06 19:20:36.428843121 +0100
***************
*** 1,3 ****
--- 1,11 ----
+ tcsh (6.17.02-4.1) unstable; urgency=low
+ 
+   * Non-maintainer upload.
+   * Add 'lock' option to savehist merge, so it doesn't corrupt the history
+     when you log out of X with multiple shells open.
+ 
+  -- Conrad J.C. Hughes (for Debian package stuff) <[email protected]>  Wed, 06 Jul 2011 19:20:30 +0100
+ 
  tcsh (6.17.02-4) unstable; urgency=high
  
    * new maintainer Closes: #592093
diff -rc tcsh-6.17.02~/sh.decls.h tcsh-6.17.02/sh.decls.h
*** tcsh-6.17.02~/sh.decls.h	2010-05-12 16:01:52.000000000 +0100
--- tcsh-6.17.02/sh.decls.h	2011-07-06 19:20:39.336895138 +0100
***************
*** 285,290 ****
--- 285,291 ----
  extern	void		  xclose	(int);
  extern	void		  xclosedir	(DIR *);
  extern	int		  xcreat	(const char *, mode_t);
+ extern	int		  xlockfcntl    (int, int, struct flock *);
  extern	struct group	 *xgetgrgid	(gid_t);
  extern	struct passwd	 *xgetpwnam	(const char *);
  extern	struct passwd	 *xgetpwuid	(uid_t);
diff -rc tcsh-6.17.02~/sh.hist.c tcsh-6.17.02/sh.hist.c
*** tcsh-6.17.02~/sh.hist.c	2010-05-12 17:26:26.000000000 +0100
--- tcsh-6.17.02/sh.hist.c	2011-07-06 19:20:39.336895138 +0100
***************
*** 1214,1219 ****
--- 1214,1241 ----
      }
  }
  
+ /* Bundle lock file descriptor and path name together for lockfile_cleanup().
+  */
+ struct lockfile {
+   int   fd;
+   Char *path;
+ };
+ 
+ /* Close and unlink lock file and free its path.
+  */
+ static void
+ lockfile_cleanup(void *v_description)
+ {
+     struct lockfile *description = v_description;
+     if (description->fd != -1) {
+       /* Unlink while we have it open to eliminate race conditions, not that I
+        * guess it matters.  May cause trouble under NT? */
+       unlink(short2str(description->path));
+       xclose(description->fd);
+     }
+     xfree(description->path);
+ }
+ 
  /* Save history before exiting the shell. */
  void
  rechist(Char *fname, int ref)
***************
*** 1221,1226 ****
--- 1243,1249 ----
      Char    *snum;
      int     fp, ftmp, oldidfds;
      struct varent *shist;
+     struct lockfile lock_info;
      static Char   *dumphist[] = {STRhistory, STRmhT, 0, 0};
  
      if (fname == NULL && !ref) 
***************
*** 1267,1272 ****
--- 1290,1310 ----
      didfds = 0;
      if ((shist = adrof(STRsavehist)) != NULL && shist->vec != NULL)
  	if (shist->vec[1] && eq(shist->vec[1], STRmerge)) {
+             if (shist->vec[2] && eq(shist->vec[2], STRlock)) {
+                 lock_info.path = Strspl(fname, STRdotlock);
+                 lock_info.fd = xcreat(short2str(lock_info.path), 0600);
+                 if (lock_info.fd != -1) {
+                     struct flock lock_details;
+                     lock_details.l_type = F_WRLCK;
+                     lock_details.l_whence = SEEK_SET;
+                     lock_details.l_start = 0;
+                     lock_details.l_len = 0; /* Whole file */
+                     /* If it fails --- well, we tried... */
+                     (void) xlockfcntl(lock_info.fd, F_SETLKW, &lock_details);
+                 }
+                 /* Free the path, and if appropriate close&unlink the file. */
+                 cleanup_push(&lock_info, lockfile_cleanup);
+             }
  	    /*
  	     * Unset verbose while we read the history file. From:
  	     * [email protected] (Jeffrey Bastian)
diff -rc tcsh-6.17.02~/sh.misc.c tcsh-6.17.02/sh.misc.c
*** tcsh-6.17.02~/sh.misc.c	2010-05-08 01:41:58.000000000 +0100
--- tcsh-6.17.02/sh.misc.c	2011-07-06 19:20:39.336895138 +0100
***************
*** 553,558 ****
--- 553,568 ----
      return res;
  }
  
+ int
+ xlockfcntl(int fd, int cmd, struct flock *lock_details)
+ {
+     int res;
+ 
+     while ((res = fcntl(fd, cmd, lock_details)) == -1 && errno == EINTR)
+ 	handle_pending_signals();
+     return res;
+ }
+ 
  #ifdef HAVE_DUP2
  static int
  xdup2(int fildes, int fildes2)
diff -rc tcsh-6.17.02~/tc.const.c tcsh-6.17.02/tc.const.c
*** tcsh-6.17.02~/tc.const.c	2010-05-12 16:35:37.000000000 +0100
--- tcsh-6.17.02/tc.const.c	2011-07-06 19:20:39.336895138 +0100
***************
*** 112,117 ****
--- 112,119 ----
  Char STRmm[]		= { '-', 'm', '\0' };
  Char STRmr[]		= { '-', 'r', '\0' };
  Char STRmerge[]		= { 'm', 'e', 'r', 'g', 'e', '\0' };
+ Char STRlock[]		= { 'l', 'o', 'c', 'k', '\0' };
+ Char STRdotlock[]	= { '.', 'l', 'o', 'c', 'k', '\0' };
  Char STRtildothist[]	= { '~', '/', '.', 'h', 'i', 's', 't', 'o', 'r', 
  			    'y', '\0' };
  

Reply via email to