diff options
author | Richard M. Stallman <rms@gnu.org> | 1991-01-14 01:21:14 +0000 |
---|---|---|
committer | Richard M. Stallman <rms@gnu.org> | 1991-01-14 01:21:14 +0000 |
commit | 8489eb6781563a09a2a275e6cb4c4fe30dae5b7c (patch) | |
tree | 7b0e9c6cbe3218f3abe6d8175e42038f626d01ee /src/filelock.c | |
parent | 5aafeb12a3aef35d3455df8a1c4bdd72d56c67e2 (diff) | |
download | emacs-8489eb6781563a09a2a275e6cb4c4fe30dae5b7c.tar.gz |
Initial revision
Diffstat (limited to 'src/filelock.c')
-rw-r--r-- | src/filelock.c | 359 |
1 files changed, 359 insertions, 0 deletions
diff --git a/src/filelock.c b/src/filelock.c new file mode 100644 index 00000000000..81e72432c06 --- /dev/null +++ b/src/filelock.c @@ -0,0 +1,359 @@ +/* Copyright (C) 1985, 1986, 1987 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include <sys/types.h> +#include <sys/stat.h> +#include "config.h" +#include <pwd.h> +#include <errno.h> +#include <sys/file.h> +#ifdef USG +#include <fcntl.h> +#endif /* USG */ + +#undef NULL +#include "lisp.h" +#include "paths.h" +#include "buffer.h" + +extern int errno; + +#ifdef CLASH_DETECTION + +/* If system does not have symbolic links, it does not have lstat. + In that case, use ordinary stat instead. */ + +#ifndef S_IFLNK +#define lstat stat +#endif + +static Lisp_Object +lock_file_owner_name (lfname) + char *lfname; +{ + struct stat s; + struct passwd *the_pw; + extern struct passwd *getpwuid (); + + if (lstat (lfname, &s) == 0) + the_pw = getpwuid (s.st_uid); + return (the_pw == 0 ? Qnil : build_string (the_pw->pw_name)); +} + + +/* lock_file locks file fn, + meaning it serves notice on the world that you intend to edit that file. + This should be done only when about to modify a file-visiting + buffer previously unmodified. + Do not (normally) call lock_buffer for a buffer already modified, + as either the file is already locked, or the user has already + decided to go ahead without locking. + + When lock_buffer returns, either the lock is locked for us, + or the user has said to go ahead without locking. + + If the file is locked by someone else, lock_buffer calls + ask-user-about-lock (a Lisp function) with two arguments, + the file name and the name of the user who did the locking. + This function can signal an error, or return t meaning + take away the lock, or return nil meaning ignore the lock. */ + +/* The lock file name is the file name with "/" replaced by "!" + and put in the Emacs lock directory. */ +/* (ie., /ka/king/junk.tex -> /!/!ka!king!junk.tex). */ + +void +lock_file (fn) + register Lisp_Object fn; +{ + register Lisp_Object attack; + register char *lfname; + + /* Create the name of the lock-file for file fn */ + lfname = (char *) alloca (XSTRING (fn)->size + strlen (PATH_LOCK) + 1); + fill_in_lock_file_name (lfname, fn); + + /* See if this file is visited and has changed on disk since it was visited. */ + { + register Lisp_Object subject_buf = Fget_file_buffer (fn); + if (!NULL (subject_buf) + && NULL (Fverify_visited_file_modtime (subject_buf)) + && !NULL (Ffile_exists_p (fn))) + call1 (intern ("ask-user-about-supersession-threat"), fn); + } + + /* Try to lock the lock. */ + if (lock_if_free (lfname) <= 0) + /* Return now if we have locked it, or if lock dir does not exist */ + return; + + /* Else consider breaking the lock */ + attack = call2 (intern ("ask-user-about-lock"), fn, + lock_file_owner_name (lfname)); + if (!NULL (attack)) + /* User says take the lock */ + { + lock_superlock (lfname); + lock_file_1 (lfname, O_WRONLY) ; + unlink (PATH_SUPERLOCK); + return; + } + /* User says ignore the lock */ +} + +fill_in_lock_file_name (lockfile, fn) + register char *lockfile; + register Lisp_Object fn; +{ + register char *p; + + strcpy (lockfile, PATH_LOCK); + + p = lockfile + strlen (lockfile); + + strcpy (p, XSTRING (fn)->data); + + for (; *p; p++) + { + if (*p == '/') + *p = '!'; + } +} + +/* Lock the lock file named LFNAME. + If MODE is O_WRONLY, we do so even if it is already locked. + If MODE is O_WRONLY | O_EXCL | O_CREAT, we do so only if it is free. + Return 1 if successful, 0 if not. */ + +int +lock_file_1 (lfname, mode) + int mode; char *lfname; +{ + register int fd; + char buf[20]; + + if ((fd = open (lfname, mode, 0666)) >= 0) + { +#ifdef USG + chmod (lfname, 0666); +#else + fchmod (fd, 0666); +#endif + sprintf (buf, "%d ", getpid ()); + write (fd, buf, strlen (buf)); + close (fd); + return 1; + } + else + return 0; +} + +/* Lock the lock named LFNAME if possible. + Return 0 in that case. + Return positive if lock is really locked by someone else. + Return -1 if cannot lock for any other reason. */ + +int +lock_if_free (lfname) + register char *lfname; +{ + register int clasher; + + while (lock_file_1 (lfname, O_WRONLY | O_EXCL | O_CREAT) == 0) + { + if (errno != EEXIST) + return -1; + clasher = current_lock_owner (lfname); + if (clasher != 0) + if (clasher != getpid ()) + return (clasher); + else return (0); + /* Try again to lock it */ + } + return 0; +} + +/* Return the pid of the process that claims to own the lock file LFNAME, + or 0 if nobody does or the lock is obsolete, + or -1 if something is wrong with the locking mechanism. */ + +int +current_lock_owner (lfname) + char *lfname; +{ + int owner = current_lock_owner_1 (lfname); + if (owner == 0 && errno == ENOENT) + return (0); + /* Is it locked by a process that exists? */ + if (owner != 0 && (kill (owner, 0) >= 0 || errno == EPERM)) + return (owner); + if (unlink (lfname) < 0) + return (-1); + return (0); +} + +int +current_lock_owner_1 (lfname) + char *lfname; +{ + register int fd; + char buf[20]; + int tem; + + fd = open (lfname, O_RDONLY, 0666); + if (fd < 0) + return 0; + tem = read (fd, buf, sizeof buf); + close (fd); + return (tem <= 0 ? 0 : atoi (buf)); +} + + +void +unlock_file (fn) + register Lisp_Object fn; +{ + register char *lfname; + + lfname = (char *) alloca (XSTRING (fn)->size + strlen (PATH_LOCK) + 1); + fill_in_lock_file_name (lfname, fn); + + lock_superlock (lfname); + + if (current_lock_owner_1 (lfname) == getpid ()) + unlink (lfname); + + unlink (PATH_SUPERLOCK); +} + +lock_superlock (lfname) + char *lfname; +{ + register int i, fd; + + for (i = -20; i < 0 && (fd = open (PATH_SUPERLOCK, + O_WRONLY | O_EXCL | O_CREAT, 0666)) < 0; + i++) + { + if (errno != EEXIST) + return; + sleep (1); + } + if (fd >= 0) + { +#ifdef USG + chmod (PATH_SUPERLOCK, 0666); +#else + fchmod (fd, 0666); +#endif + write (fd, lfname, strlen (lfname)); + close (fd); + } +} + +void +unlock_all_files () +{ + register Lisp_Object tail; + register struct buffer *b; + + for (tail = Vbuffer_alist; XGCTYPE (tail) == Lisp_Cons; + tail = XCONS (tail)->cdr) + { + b = XBUFFER (XCONS (XCONS (tail)->car)->cdr); + if (XTYPE (b->filename) == Lisp_String && + b->save_modified < BUF_MODIFF (b)) + unlock_file (b->filename); + } +} + + +DEFUN ("lock-buffer", Flock_buffer, Slock_buffer, + 0, 1, 0, + "Lock FILE, if current buffer is modified.\n\ +FILE defaults to current buffer's visited file,\n\ +or else nothing is done if current buffer isn't visiting a file.") + (fn) + Lisp_Object fn; +{ + if (NULL (fn)) + fn = current_buffer->filename; + else + CHECK_STRING (fn, 0); + if (current_buffer->save_modified < MODIFF + && !NULL (fn)) + lock_file (fn); + return Qnil; +} + +DEFUN ("unlock-buffer", Funlock_buffer, Sunlock_buffer, + 0, 0, 0, + "Unlock the file visited in the current buffer,\n\ +if it should normally be locked.") + () +{ + if (current_buffer->save_modified < MODIFF && + XTYPE (current_buffer->filename) == Lisp_String) + unlock_file (current_buffer->filename); + return Qnil; +} + + +/* Unlock the file visited in buffer BUFFER. */ + +unlock_buffer (buffer) + struct buffer *buffer; +{ + if (buffer->save_modified < BUF_MODIFF (buffer) && + XTYPE (buffer->filename) == Lisp_String) + unlock_file (buffer->filename); +} + +DEFUN ("file-locked-p", Ffile_locked_p, Sfile_locked_p, 0, 1, 0, + "Return nil if the FILENAME is not locked,\n\ +t if it is locked by you, else a string of the name of the locker.") + (fn) + Lisp_Object fn; +{ + register char *lfname; + int owner; + + fn = Fexpand_file_name (fn, Qnil); + + /* Create the name of the lock-file for file filename */ + lfname = (char *) alloca (XSTRING (fn)->size + strlen (PATH_LOCK) + 1); + fill_in_lock_file_name (lfname, fn); + + owner = current_lock_owner (lfname); + if (owner <= 0) + return (Qnil); + else if (owner == getpid ()) + return (Qt); + + return (lock_file_owner_name (lfname)); +} + +syms_of_filelock () +{ + defsubr (&Sunlock_buffer); + defsubr (&Slock_buffer); + defsubr (&Sfile_locked_p); +} + +#endif /* CLASH_DETECTION */ |