Initial revision
[bpt/emacs.git] / src / filelock.c
CommitLineData
8489eb67
RS
1/* Copyright (C) 1985, 1986, 1987 Free Software Foundation, Inc.
2
3This file is part of GNU Emacs.
4
5GNU Emacs is free software; you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation; either version 1, or (at your option)
8any later version.
9
10GNU Emacs is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with GNU Emacs; see the file COPYING. If not, write to
17the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
18
19
20#include <sys/types.h>
21#include <sys/stat.h>
22#include "config.h"
23#include <pwd.h>
24#include <errno.h>
25#include <sys/file.h>
26#ifdef USG
27#include <fcntl.h>
28#endif /* USG */
29
30#undef NULL
31#include "lisp.h"
32#include "paths.h"
33#include "buffer.h"
34
35extern int errno;
36
37#ifdef CLASH_DETECTION
38
39/* If system does not have symbolic links, it does not have lstat.
40 In that case, use ordinary stat instead. */
41
42#ifndef S_IFLNK
43#define lstat stat
44#endif
45
46static Lisp_Object
47lock_file_owner_name (lfname)
48 char *lfname;
49{
50 struct stat s;
51 struct passwd *the_pw;
52 extern struct passwd *getpwuid ();
53
54 if (lstat (lfname, &s) == 0)
55 the_pw = getpwuid (s.st_uid);
56 return (the_pw == 0 ? Qnil : build_string (the_pw->pw_name));
57}
58
59
60/* lock_file locks file fn,
61 meaning it serves notice on the world that you intend to edit that file.
62 This should be done only when about to modify a file-visiting
63 buffer previously unmodified.
64 Do not (normally) call lock_buffer for a buffer already modified,
65 as either the file is already locked, or the user has already
66 decided to go ahead without locking.
67
68 When lock_buffer returns, either the lock is locked for us,
69 or the user has said to go ahead without locking.
70
71 If the file is locked by someone else, lock_buffer calls
72 ask-user-about-lock (a Lisp function) with two arguments,
73 the file name and the name of the user who did the locking.
74 This function can signal an error, or return t meaning
75 take away the lock, or return nil meaning ignore the lock. */
76
77/* The lock file name is the file name with "/" replaced by "!"
78 and put in the Emacs lock directory. */
79/* (ie., /ka/king/junk.tex -> /!/!ka!king!junk.tex). */
80
81void
82lock_file (fn)
83 register Lisp_Object fn;
84{
85 register Lisp_Object attack;
86 register char *lfname;
87
88 /* Create the name of the lock-file for file fn */
89 lfname = (char *) alloca (XSTRING (fn)->size + strlen (PATH_LOCK) + 1);
90 fill_in_lock_file_name (lfname, fn);
91
92 /* See if this file is visited and has changed on disk since it was visited. */
93 {
94 register Lisp_Object subject_buf = Fget_file_buffer (fn);
95 if (!NULL (subject_buf)
96 && NULL (Fverify_visited_file_modtime (subject_buf))
97 && !NULL (Ffile_exists_p (fn)))
98 call1 (intern ("ask-user-about-supersession-threat"), fn);
99 }
100
101 /* Try to lock the lock. */
102 if (lock_if_free (lfname) <= 0)
103 /* Return now if we have locked it, or if lock dir does not exist */
104 return;
105
106 /* Else consider breaking the lock */
107 attack = call2 (intern ("ask-user-about-lock"), fn,
108 lock_file_owner_name (lfname));
109 if (!NULL (attack))
110 /* User says take the lock */
111 {
112 lock_superlock (lfname);
113 lock_file_1 (lfname, O_WRONLY) ;
114 unlink (PATH_SUPERLOCK);
115 return;
116 }
117 /* User says ignore the lock */
118}
119
120fill_in_lock_file_name (lockfile, fn)
121 register char *lockfile;
122 register Lisp_Object fn;
123{
124 register char *p;
125
126 strcpy (lockfile, PATH_LOCK);
127
128 p = lockfile + strlen (lockfile);
129
130 strcpy (p, XSTRING (fn)->data);
131
132 for (; *p; p++)
133 {
134 if (*p == '/')
135 *p = '!';
136 }
137}
138
139/* Lock the lock file named LFNAME.
140 If MODE is O_WRONLY, we do so even if it is already locked.
141 If MODE is O_WRONLY | O_EXCL | O_CREAT, we do so only if it is free.
142 Return 1 if successful, 0 if not. */
143
144int
145lock_file_1 (lfname, mode)
146 int mode; char *lfname;
147{
148 register int fd;
149 char buf[20];
150
151 if ((fd = open (lfname, mode, 0666)) >= 0)
152 {
153#ifdef USG
154 chmod (lfname, 0666);
155#else
156 fchmod (fd, 0666);
157#endif
158 sprintf (buf, "%d ", getpid ());
159 write (fd, buf, strlen (buf));
160 close (fd);
161 return 1;
162 }
163 else
164 return 0;
165}
166
167/* Lock the lock named LFNAME if possible.
168 Return 0 in that case.
169 Return positive if lock is really locked by someone else.
170 Return -1 if cannot lock for any other reason. */
171
172int
173lock_if_free (lfname)
174 register char *lfname;
175{
176 register int clasher;
177
178 while (lock_file_1 (lfname, O_WRONLY | O_EXCL | O_CREAT) == 0)
179 {
180 if (errno != EEXIST)
181 return -1;
182 clasher = current_lock_owner (lfname);
183 if (clasher != 0)
184 if (clasher != getpid ())
185 return (clasher);
186 else return (0);
187 /* Try again to lock it */
188 }
189 return 0;
190}
191
192/* Return the pid of the process that claims to own the lock file LFNAME,
193 or 0 if nobody does or the lock is obsolete,
194 or -1 if something is wrong with the locking mechanism. */
195
196int
197current_lock_owner (lfname)
198 char *lfname;
199{
200 int owner = current_lock_owner_1 (lfname);
201 if (owner == 0 && errno == ENOENT)
202 return (0);
203 /* Is it locked by a process that exists? */
204 if (owner != 0 && (kill (owner, 0) >= 0 || errno == EPERM))
205 return (owner);
206 if (unlink (lfname) < 0)
207 return (-1);
208 return (0);
209}
210
211int
212current_lock_owner_1 (lfname)
213 char *lfname;
214{
215 register int fd;
216 char buf[20];
217 int tem;
218
219 fd = open (lfname, O_RDONLY, 0666);
220 if (fd < 0)
221 return 0;
222 tem = read (fd, buf, sizeof buf);
223 close (fd);
224 return (tem <= 0 ? 0 : atoi (buf));
225}
226
227\f
228void
229unlock_file (fn)
230 register Lisp_Object fn;
231{
232 register char *lfname;
233
234 lfname = (char *) alloca (XSTRING (fn)->size + strlen (PATH_LOCK) + 1);
235 fill_in_lock_file_name (lfname, fn);
236
237 lock_superlock (lfname);
238
239 if (current_lock_owner_1 (lfname) == getpid ())
240 unlink (lfname);
241
242 unlink (PATH_SUPERLOCK);
243}
244
245lock_superlock (lfname)
246 char *lfname;
247{
248 register int i, fd;
249
250 for (i = -20; i < 0 && (fd = open (PATH_SUPERLOCK,
251 O_WRONLY | O_EXCL | O_CREAT, 0666)) < 0;
252 i++)
253 {
254 if (errno != EEXIST)
255 return;
256 sleep (1);
257 }
258 if (fd >= 0)
259 {
260#ifdef USG
261 chmod (PATH_SUPERLOCK, 0666);
262#else
263 fchmod (fd, 0666);
264#endif
265 write (fd, lfname, strlen (lfname));
266 close (fd);
267 }
268}
269
270void
271unlock_all_files ()
272{
273 register Lisp_Object tail;
274 register struct buffer *b;
275
276 for (tail = Vbuffer_alist; XGCTYPE (tail) == Lisp_Cons;
277 tail = XCONS (tail)->cdr)
278 {
279 b = XBUFFER (XCONS (XCONS (tail)->car)->cdr);
280 if (XTYPE (b->filename) == Lisp_String &&
281 b->save_modified < BUF_MODIFF (b))
282 unlock_file (b->filename);
283 }
284}
285
286\f
287DEFUN ("lock-buffer", Flock_buffer, Slock_buffer,
288 0, 1, 0,
289 "Lock FILE, if current buffer is modified.\n\
290FILE defaults to current buffer's visited file,\n\
291or else nothing is done if current buffer isn't visiting a file.")
292 (fn)
293 Lisp_Object fn;
294{
295 if (NULL (fn))
296 fn = current_buffer->filename;
297 else
298 CHECK_STRING (fn, 0);
299 if (current_buffer->save_modified < MODIFF
300 && !NULL (fn))
301 lock_file (fn);
302 return Qnil;
303}
304
305DEFUN ("unlock-buffer", Funlock_buffer, Sunlock_buffer,
306 0, 0, 0,
307 "Unlock the file visited in the current buffer,\n\
308if it should normally be locked.")
309 ()
310{
311 if (current_buffer->save_modified < MODIFF &&
312 XTYPE (current_buffer->filename) == Lisp_String)
313 unlock_file (current_buffer->filename);
314 return Qnil;
315}
316
317\f
318/* Unlock the file visited in buffer BUFFER. */
319
320unlock_buffer (buffer)
321 struct buffer *buffer;
322{
323 if (buffer->save_modified < BUF_MODIFF (buffer) &&
324 XTYPE (buffer->filename) == Lisp_String)
325 unlock_file (buffer->filename);
326}
327
328DEFUN ("file-locked-p", Ffile_locked_p, Sfile_locked_p, 0, 1, 0,
329 "Return nil if the FILENAME is not locked,\n\
330t if it is locked by you, else a string of the name of the locker.")
331 (fn)
332 Lisp_Object fn;
333{
334 register char *lfname;
335 int owner;
336
337 fn = Fexpand_file_name (fn, Qnil);
338
339 /* Create the name of the lock-file for file filename */
340 lfname = (char *) alloca (XSTRING (fn)->size + strlen (PATH_LOCK) + 1);
341 fill_in_lock_file_name (lfname, fn);
342
343 owner = current_lock_owner (lfname);
344 if (owner <= 0)
345 return (Qnil);
346 else if (owner == getpid ())
347 return (Qt);
348
349 return (lock_file_owner_name (lfname));
350}
351
352syms_of_filelock ()
353{
354 defsubr (&Sunlock_buffer);
355 defsubr (&Slock_buffer);
356 defsubr (&Sfile_locked_p);
357}
358
359#endif /* CLASH_DETECTION */