tcsh/tcsh-6.18.00-history-file-locking.patch
Vojtech Vitek (V-Teq) 66fbce9c1e Update to tcsh-6.18.00 - update sources & remove obsolte patches
- Remove obsolete patches: tcsh-6.15.00-ca-color.patch,
  tcsh-6.17.00-tc-color.patch, tcsh-6.17.00-mh-color.patch,
  tcsh-6.17.00-history.patch, tcsh-6.17.00-printexitvalue.patch,
  tcsh-6.17.00-testsuite.patch, tcsh-6.17.00-negative_jobs.patch,
  tcsh-6.17.00-wait-intr.patch, tcsh-6.17.00-dont-set-empty-remotehost.patch,
  tcsh-6.17.00-dont-print-history-on-verbose.patch, tcsh-6.14.00-set.patch,
  tcsh-6.17.00-extrafork.patch, tcsh-6.17.00-avoid-null-cwd.patch,
  tcsh-6.17.00-avoid-infinite-loop-pendjob-xprintf.patch,
  tcsh-6.17.00-variable-names.patch,
  tcsh-6.17.00-handle-signals-before-flush.patch
  tcsh-6.17.00-status-pipeline-backquote-list-of-cmds.patch (reverted!)
2012-03-20 18:01:00 +01:00

453 lines
12 KiB
Diff

From d73747f08a9576f360e0e0b6c5f537547c30c5fa Mon Sep 17 00:00:00 2001
From: "Vojtech Vitek (V-Teq)" <vvitek@redhat.com>
Date: Thu, 15 Mar 2012 03:13:01 +0100
Subject: [PATCH] Implement .history file locking - shared readers, exclusive
writer
Originally reported at Red Hat Bugzilla:
https://bugzilla.redhat.com/show_bug.cgi?id=648592
Patch by Vojtech Vitek (V-Teq) <vvitek@redhat.com>
---
sh.c | 91 ++++++++++++++++++++++++++++++++++++++++++++----------------
sh.decls.h | 5 ++-
sh.dir.c | 2 +-
sh.dol.c | 2 +-
sh.err.c | 16 ++++++++++
sh.h | 17 +++++++++++
sh.hist.c | 62 +++++++++++++++++++++++------------------
sh.lex.c | 8 ++--
sh.sem.c | 2 +-
9 files changed, 145 insertions(+), 60 deletions(-)
diff --git a/sh.c b/sh.c
index dcd9116..aaf32bc 100644
--- a/sh.c
+++ b/sh.c
@@ -140,6 +140,7 @@ struct saved_state {
int cantell;
struct Bin B;
int justpr;
+ int close_unit;
};
static int srccat (Char *, Char *);
@@ -1369,7 +1370,7 @@ main(int argc, char **argv)
/*
* Source history before .login so that it is available in .login
*/
- loadhist(NULL, 0);
+ loadhist(NULL, FD_RDLCK);
#ifndef LOGINFIRST
if (loginsh)
(void) srccat(varval(STRhome), STRsldotlogin);
@@ -1522,20 +1523,52 @@ static int
#else
int
#endif /*WINNT_NATIVE*/
-srcfile(const char *f, int onlyown, int flag, Char **av)
+srcfile(const char *f, int onlyown, int flg, Char **av)
{
- int unit;
-
- if ((unit = xopen(f, O_RDONLY|O_LARGEFILE)) == -1)
- return 0;
- cleanup_push(&unit, open_cleanup);
- unit = dmove(unit, -1);
- cleanup_ignore(&unit);
- cleanup_until(&unit);
-
- (void) close_on_exec(unit, 1);
- srcunit(unit, onlyown, flag, av);
- return 1;
+ int *unit, copy;
+
+ unit = xmalloc(sizeof(*unit));
+ cleanup_push(unit, xfree);
+ *unit = xopen(f, O_LARGEFILE |
+ ((flg & FD_WRLCK) ? (O_CREAT|O_RDWR) : O_RDONLY), 0600);
+
+ if (*unit == -1) {
+ if (!bequiet)
+ stderror(ERR_SYSTEM, f, strerror(errno));
+ return -1;
+ }
+
+ cleanup_push(unit, open_cleanup);
+ *unit = dmove(*unit, -1);
+ (void) close_on_exec(*unit, 1);
+ copy = *unit;
+
+ if (flg & (FD_WRLCK | FD_RDLCK)) {
+ struct flock fl;
+
+ fl.l_type = (flg & FD_WRLCK) ? F_WRLCK : F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+
+ if (fcntl(*unit, F_SETLKW, &fl) != -1)
+ cleanup_push(unit, fcntl_cleanup);
+ }
+
+ srcunit(copy, onlyown, (flg & (SRC_HFLAG | SRC_MFLAG)), av);
+
+ /* Close the unit, if we don't want to leave it open & locked. */
+ if ((flg & (FD_WRLCK | FD_RDLCK)) && (!(flg & FD_LEAVE_LCKD))) {
+ cleanup_until(unit); /* fcntl_cleanup */
+ if (!(flg & FD_LEAVE_OPEN)) {
+ cleanup_until(unit); /* open_cleanup */
+ cleanup_until(unit); /* xfree */
+ }
+
+ return -1; /* Invalid file descriptor. */
+ }
+
+ return *unit;
}
@@ -1593,10 +1626,14 @@ st_save(struct saved_state *st, int unit, int hflg, Char **al, Char **av)
st->onelflg = onelflg;
st->enterhist = enterhist;
st->justpr = justpr;
- if (hflg)
+ if (hflg && SRC_HFLAG)
st->HIST = HIST;
else
st->HIST = '\0';
+ if (hflg && FD_LEAVE_OPEN)
+ st->close_unit = 0;
+ else
+ st->close_unit = 1;
st->cantell = cantell;
cpybin(st->B, B);
@@ -1668,7 +1705,8 @@ st_restore(void *xst)
}
cpybin(B, st->B);
- xclose(SHIN);
+ if (st->close_unit)
+ xclose(SHIN);
insource = st->insource;
SHIN = st->SHIN;
@@ -2153,24 +2191,25 @@ process(int catch)
}
/*ARGSUSED*/
-void
-dosource(Char **t, struct command *c)
+int
+dosource(Char **t, struct command *c, int flg)
{
Char *f;
- int hflg = 0;
char *file;
+ int fd;
+ int hflg = 0;
USE(c);
t++;
if (*t && eq(*t, STRmh)) {
if (*++t == NULL)
stderror(ERR_NAME | ERR_HFLAG);
- hflg++;
+ hflg |= SRC_HFLAG;
}
else if (*t && eq(*t, STRmm)) {
if (*++t == NULL)
stderror(ERR_NAME | ERR_MFLAG);
- hflg = 2;
+ hflg |= SRC_MFLAG;
}
f = globone(*t++, G_ERROR);
@@ -2178,9 +2217,13 @@ dosource(Char **t, struct command *c)
cleanup_push(file, xfree);
xfree(f);
t = glob_all_or_error(t);
- if ((!srcfile(file, 0, hflg, t)) && (!hflg) && (!bequiet))
- stderror(ERR_SYSTEM, file, strerror(errno));
- cleanup_until(file);
+ fd = srcfile(file, 0, (hflg | flg), t);
+
+ /* Postpone fd cleanup, which is on the top of cleaning stack. */
+ cleanup_ignore(file);
+ xfree(file);
+
+ return fd; /* File descriptor or -1. */
}
/*
diff --git a/sh.decls.h b/sh.decls.h
index db90288..d4332f3 100644
--- a/sh.decls.h
+++ b/sh.decls.h
@@ -37,7 +37,7 @@
* sh.c
*/
extern Char *gethdir (const Char *);
-extern void dosource (Char **, struct command *);
+extern int dosource (Char **, struct command *, int);
extern void exitstat (void);
extern void goodbye (Char **, struct command *);
extern void importpath (Char *);
@@ -98,6 +98,7 @@ extern void cleanup_until_mark(void);
extern size_t cleanup_push_mark(void);
extern void cleanup_pop_mark(size_t);
extern void open_cleanup(void *);
+extern void fcntl_cleanup(void *);
extern void opendir_cleanup(void *);
extern void sigint_cleanup(void *);
extern void sigprocmask_cleanup(void *);
@@ -219,7 +220,7 @@ extern struct Hist *enthist (int, struct wordent *, int, int, int);
extern void savehist (struct wordent *, int);
extern char *fmthist (int, ptr_t);
extern void rechist (Char *, int);
-extern void loadhist (Char *, int);
+extern int loadhist (Char *, int);
extern void displayHistStats(const char *);
/*
diff --git a/sh.dir.c b/sh.dir.c
index ab89855..4bfe430 100644
--- a/sh.dir.c
+++ b/sh.dir.c
@@ -1342,7 +1342,7 @@ loaddirs(Char *fname)
loaddirs_cmd[1] = fname;
else
loaddirs_cmd[1] = STRtildotdirs;
- dosource(loaddirs_cmd, NULL);
+ dosource(loaddirs_cmd, NULL, 0);
cleanup_until(&bequiet);
}
diff --git a/sh.dol.c b/sh.dol.c
index 45b10e0..02dfe98 100644
--- a/sh.dol.c
+++ b/sh.dol.c
@@ -1110,6 +1110,6 @@ again:
*obp = 0;
tmp = short2str(obuf);
(void) xwrite(0, tmp, strlen (tmp));
- (void) lseek(0, (off_t) 0, L_SET);
+ (void) lseek(0, 0, SEEK_SET);
cleanup_until(&inheredoc);
}
diff --git a/sh.err.c b/sh.err.c
index e157d6a..29d41c3 100644
--- a/sh.err.c
+++ b/sh.err.c
@@ -514,6 +514,22 @@ open_cleanup(void *xptr)
}
void
+fcntl_cleanup(void *xptr)
+{
+ int *ptr;
+ struct flock fl;
+
+ ptr = xptr;
+
+ fl.l_type = F_UNLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+
+ fcntl(*ptr, F_SETLK, &fl);
+}
+
+void
opendir_cleanup(void *xdir)
{
DIR *dir;
diff --git a/sh.h b/sh.h
index 691add3..558b093 100644
--- a/sh.h
+++ b/sh.h
@@ -50,6 +50,23 @@
# include <inttypes.h>
#endif
+#include <unistd.h>
+#include <fcntl.h>
+/*
+ * Source flags (representing -h or -m).
+ *
+ * File locking flags.
+ * - shared and exclusive (read and write) access to a file
+ * - currently used in sh.c and sh.hist.c files while acessing
+ * ~/.history file only.
+ */
+#define SRC_HFLAG 0x01 /* History flag */
+#define SRC_MFLAG 0x02 /* Merge flag */
+#define FD_WRLCK 0x04 /* Write lock */
+#define FD_RDLCK 0x08 /* Read lock */
+#define FD_LEAVE_OPEN 0x10 /* Leave file open */
+#define FD_LEAVE_LCKD 0x20 /* Leave file locked */
+
#if !defined(HAVE_STDINT_H) && !defined(HAVE_INTTYPES_H) && !defined(WINNT_NATIVE)
typedef unsigned long intptr_t;
#endif
diff --git a/sh.hist.c b/sh.hist.c
index 6a12737..7df84e5 100644
--- a/sh.hist.c
+++ b/sh.hist.c
@@ -1173,7 +1173,7 @@ dohist(Char **vp, struct command *c)
}
if (hflg & (HIST_LOAD | HIST_MERGE))
- loadhist(*vp, (hflg & HIST_MERGE) ? 1 : 0);
+ loadhist(*vp, ((hflg & HIST_MERGE) ? SRC_MFLAG : 0) | FD_RDLCK);
else if (hflg & HIST_SAVE)
rechist(*vp, 1);
else {
@@ -1224,8 +1224,8 @@ fmthist(int fmt, ptr_t ptr)
void
rechist(Char *fname, int ref)
{
- Char *snum;
- int fp, ftmp, oldidfds;
+ Char *snum;
+ int fd = -1, ftmp, oldidfds;
struct varent *shist;
static Char *dumphist[] = {STRhistory, STRmhT, 0, 0};
@@ -1239,7 +1239,6 @@ rechist(Char *fname, int ref)
((snum = varval(STRhistory)) == STRNULL))
snum = STRmaxint;
-
if (fname == NULL) {
if ((fname = varval(STRhistfile)) == STRNULL)
fname = Strspl(varval(STRhome), &STRtildothist[1]);
@@ -1255,15 +1254,12 @@ rechist(Char *fname, int ref)
* with numerous shells being in simultaneous use. Imagine
* any kind of window system. All these shells 'share' the same
* ~/.history file for recording their command line history.
- * Currently the automatic merge can only succeed when the shells
- * nicely quit one after another.
- *
- * Users that like to nuke their environment require here an atomic
- * loadhist-creat-dohist(dumphist)-close
- * sequence.
*
- * jw.
- */
+ * Atomic merge loadhist-creat/ftrunc-dohist(dumphist)-close
+ * implemented using fcntl (shared readers, exclusive writer)
+ * by Vojtech Vitek (V-Teq) <vvitek@redhat.com>.
+ */
+
/*
* We need the didfds stuff before loadhist otherwise
* exec in a script will fail to print if merge is set.
@@ -1271,22 +1267,34 @@ rechist(Char *fname, int ref)
*/
oldidfds = didfds;
didfds = 0;
- if ((shist = adrof(STRsavehist)) != NULL && shist->vec != NULL)
- if (shist->vec[1] && eq(shist->vec[1], STRmerge))
- loadhist(fname, 1);
-
- fp = xcreat(short2str(fname), 0600);
- cleanup_until(fname);
- if (fp == -1) {
- didfds = oldidfds;
- return;
+ if ((shist = adrof(STRsavehist)) != NULL && shist->vec != NULL
+ && shist->vec[1] && eq(shist->vec[1], STRmerge)) {
+ /* Read .history file, leave it's fd open for writing. */
+ fd = loadhist(fname, SRC_MFLAG|FD_WRLCK|FD_LEAVE_OPEN|FD_LEAVE_LCKD);
+ if (fd != -1) {
+ /* Truncate the .history file. */
+ (void) ftruncate(fd, 0);
+ (void) lseek(fd, 0, SEEK_SET);
+ }
}
- ftmp = SHOUT;
- SHOUT = fp;
- dumphist[2] = snum;
- dohist(dumphist, NULL);
- xclose(fp);
- SHOUT = ftmp;
+
+ /* Open .history file for writing (if not open yet). */
+ if (fd == -1) {
+ fd = xopen(short2str(fname), O_LARGEFILE|O_CREAT|O_WRONLY|O_TRUNC, 0600);
+ cleanup_until(fname);
+ if (fd != -1)
+ cleanup_push(&fd, open_cleanup);
+ }
+
+ if (fd != -1) {
+ ftmp = SHOUT;
+ SHOUT = fd;
+ dumphist[2] = snum;
+ /* Write history to the .history file. */
+ dohist(dumphist, NULL);
+ SHOUT = ftmp;
+ }
+
didfds = oldidfds;
}
diff --git a/sh.lex.c b/sh.lex.c
index 80643f7..e128789 100644
--- a/sh.lex.c
+++ b/sh.lex.c
@@ -1595,7 +1595,7 @@ wide_read(int fildes, Char *buf, size_t nchars, int use_fclens)
/* Throwing away possible partial multibyte characters on error if the
stream is not seekable */
err = errno;
- lseek(fildes, -(off_t)partial, L_INCR);
+ lseek(fildes, -partial, SEEK_CUR);
errno = err;
return res != 0 ? res : r;
}
@@ -1610,7 +1610,7 @@ bgetc(void)
if (cantell) {
if (fseekp < fbobp || fseekp > feobp) {
fbobp = feobp = fseekp;
- (void) lseek(SHIN, fseekp, L_SET);
+ (void) lseek(SHIN, fseekp, SEEK_SET);
}
if (fseekp == feobp) {
#ifdef WIDE_STRINGS
@@ -1814,7 +1814,7 @@ btell(struct Ain *l)
void
btoeof(void)
{
- (void) lseek(SHIN, (off_t) 0, L_XTND);
+ (void) lseek(SHIN, 0, SEEK_END);
aret = TCSH_F_SEEK;
fseekp = feobp;
alvec = NULL;
@@ -1832,7 +1832,7 @@ settell(void)
cantell = 0;
if (arginp || onelflg || intty)
return;
- if ((x = lseek(SHIN, (off_t) 0, L_INCR)) == -1)
+ if ((x = lseek(SHIN, 0, SEEK_CUR)) == -1)
return;
fbuf = xcalloc(2, sizeof(Char **));
fblocks = 1;
diff --git a/sh.sem.c b/sh.sem.c
index c880974..765bf50 100644
--- a/sh.sem.c
+++ b/sh.sem.c
@@ -892,7 +892,7 @@ doio(struct command *t, int *pipein, int *pipeout)
fd = xopen(tmp, O_WRONLY|O_APPEND|O_LARGEFILE);
#else /* !O_APPEND */
fd = xopen(tmp, O_WRONLY|O_LARGEFILE);
- (void) lseek(fd, (off_t) 0, L_XTND);
+ (void) lseek(fd, 0, SEEK_END);
#endif /* O_APPEND */
}
else
--
1.7.6.5