summaryrefslogtreecommitdiff
path: root/src/filelock.c
diff options
context:
space:
mode:
authorRichard M. Stallman <rms@gnu.org>1991-01-14 01:21:14 +0000
committerRichard M. Stallman <rms@gnu.org>1991-01-14 01:21:14 +0000
commit8489eb6781563a09a2a275e6cb4c4fe30dae5b7c (patch)
tree7b0e9c6cbe3218f3abe6d8175e42038f626d01ee /src/filelock.c
parent5aafeb12a3aef35d3455df8a1c4bdd72d56c67e2 (diff)
downloademacs-8489eb6781563a09a2a275e6cb4c4fe30dae5b7c.tar.gz
Initial revision
Diffstat (limited to 'src/filelock.c')
-rw-r--r--src/filelock.c359
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 */