1 /* editor file locking.
3 Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
5 Authors: 2003 Adam Byrtek
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 #include <signal.h> /* kill() */
29 #include <sys/types.h>
39 #include <mhl/memory.h>
40 #include <mhl/string.h>
42 #include "../src/global.h"
47 #include "../src/wtools.h" /* edit_query_dialog () */
50 #define PID_BUF_SIZE 10
57 /* Locking scheme used in mcedit is based on a documentation found
58 in JED editor sources. Abstract from lock.c file (by John E. Davis):
60 The basic idea here is quite simple. Whenever a buffer is attached to
61 a file, and that buffer is modified, then attempt to lock the
62 file. Moreover, before writing to a file for any reason, lock the
63 file. The lock is really a protocol respected and not a real lock.
64 The protocol is this: If in the directory of the file is a
65 symbolic link with name ".#FILE", the FILE is considered to be locked
66 by the process specified by the link.
69 /* Build user@host.domain.pid string (need to be freed) */
71 lock_build_name (void)
74 const char *user
= NULL
;
77 pw
= getpwuid (getuid ());
78 if (pw
) user
= pw
->pw_name
;
79 if (!user
) user
= getenv ("USER");
80 if (!user
) user
= getenv ("USERNAME");
81 if (!user
) user
= getenv ("LOGNAME");
84 /* TODO: Use FQDN, no clean interface, so requires lot of code */
85 if (gethostname (host
, BUF_SIZE
- 1) == -1)
88 return g_strdup_printf ("%s@%s.%d", user
, host
, (int) getpid ());
92 lock_build_symlink_name (const char *fname
)
94 char *fname_copy
, *symlink_name
;
95 char absolute_fname
[PATH_MAX
];
97 if (mc_realpath (fname
, absolute_fname
) == NULL
)
100 fname
= x_basename (absolute_fname
);
101 fname_copy
= g_strdup (fname
);
102 absolute_fname
[fname
- absolute_fname
] = '\0';
103 symlink_name
= g_strconcat (absolute_fname
, ".#", fname_copy
, (char *) NULL
);
109 /* Extract pid from user@host.domain.pid string */
110 static struct lock_s
*
111 lock_extract_info (const char *str
)
115 static char pid
[PID_BUF_SIZE
], who
[BUF_SIZE
];
116 static struct lock_s lock
;
118 for (p
= str
+ strlen (str
) - 1; p
>= str
; p
--)
122 /* Everything before last '.' is user@host */
124 for (s
= str
; s
< p
&& i
< BUF_SIZE
; s
++)
128 /* Treat text between '.' and ':' or '\0' as pid */
131 p
< str
+ strlen (str
) && *p
!= ':' && i
< PID_BUF_SIZE
; p
++)
135 lock
.pid
= (pid_t
) atol (pid
);
140 /* Extract user@host.domain.pid from lock file (static string) */
142 lock_get_info (const char *lockfname
)
145 static char buf
[BUF_SIZE
];
147 if ((cnt
= readlink (lockfname
, buf
, BUF_SIZE
- 1)) == -1 || !*buf
)
154 /* Tries to raise file lock
155 Returns 1 on success, 0 on failure, -1 if abort
156 Warning: Might do screen refresh and lose edit->force */
158 edit_lock_file (const char *fname
)
160 char *lockfname
, *newlock
, *msg
, *lock
;
162 struct lock_s
*lockinfo
;
164 /* Just to be sure (and don't lock new file) */
165 if (!fname
|| !*fname
)
168 /* Locking on VFS is not supported */
169 if (!vfs_file_is_local (fname
))
172 /* Check if already locked */
173 lockfname
= lock_build_symlink_name (fname
);
174 if (lockfname
== NULL
)
176 if (lstat (lockfname
, &statbuf
) == 0) {
177 lock
= lock_get_info (lockfname
);
182 lockinfo
= lock_extract_info (lock
);
184 /* Check if locking process alive, ask user if required */
186 || !(kill (lockinfo
->pid
, 0) == -1 && errno
== ESRCH
)) {
189 ("File \"%s\" is already being edited\n"
190 "User: %s\nProcess ID: %d"), x_basename (lockfname
) + 2,
191 lockinfo
->who
, (int) lockinfo
->pid
);
192 /* TODO: Implement "Abort" - needs to rewind undo stack */
193 switch (edit_query_dialog2
194 (_("File locked"), msg
, _("&Grab lock"),
195 _("&Ignore lock"))) {
209 /* Create lock symlink */
210 newlock
= lock_build_name ();
211 if (symlink (newlock
, lockfname
) == -1) {
222 /* Lowers file lock if possible
223 Always returns 0 to make 'lock = edit_unlock_file (f)' possible */
225 edit_unlock_file (const char *fname
)
227 char *lockfname
, *lock
;
230 /* Just to be sure */
231 if (!fname
|| !*fname
)
234 lockfname
= lock_build_symlink_name (fname
);
235 if (lockfname
== NULL
)
238 /* Check if lock exists */
239 if (lstat (lockfname
, &statbuf
) == -1) {
244 lock
= lock_get_info (lockfname
);
246 /* Don't touch if lock is not ours */
247 if (lock_extract_info (lock
)->pid
!= getpid ()) {