Merge branch '2484_editor_get_prev_utf8_fix'
[midnight-commander.git] / lib / lock.c
blob4d32d464c8c78c90b852c1bf9962d659b199894a
1 /* 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
20 02110-1301, USA.
24 /** \file
25 * \brief Source: file locking
26 * \author Adam Byrtek
27 * \date 2003
29 * Locking scheme is based on a documentation found
30 * in JED editor sources. Abstract from lock.c file (by John E. Davis):
32 * The basic idea here is quite simple. Whenever a buffer is attached to
33 * a file, and that buffer is modified, then attempt to lock the
34 * file. Moreover, before writing to a file for any reason, lock the
35 * file. The lock is really a protocol respected and not a real lock.
36 * The protocol is this: If in the directory of the file is a
37 * symbolic link with name ".#FILE", the FILE is considered to be locked
38 * by the process specified by the link.
41 #include <config.h>
43 #include <signal.h> /* kill() */
44 #include <stdio.h>
45 #include <stdarg.h>
46 #include <sys/types.h>
47 #include <unistd.h>
48 #include <string.h>
49 #include <ctype.h>
50 #include <errno.h>
51 #include <sys/stat.h>
52 #include <pwd.h>
53 #include <stdlib.h>
55 #include "lib/global.h"
56 #include "lib/vfs/mc-vfs/vfs.h"
57 #include "lib/util.h" /* tilde_expand() */
58 #include "lib/lock.h"
59 #include "lib/widget.h" /* query_dialog() */
61 /*** global variables ****************************************************************************/
63 /*** file scope macro definitions ****************************************************************/
65 #define BUF_SIZE 255
66 #define PID_BUF_SIZE 10
68 /*** file scope type declarations ****************************************************************/
70 struct lock_s
72 char *who;
73 pid_t pid;
76 /*** file scope variables ************************************************************************/
78 /*** file scope functions ************************************************************************/
79 /* --------------------------------------------------------------------------------------------- */
80 /** \fn static char * lock_build_name (void)
81 * \brief builds user@host.domain.pid string (need to be freed)
82 * \return a pointer to lock filename
85 static char *
86 lock_build_name (void)
88 char host[BUF_SIZE];
89 const char *user = NULL;
90 struct passwd *pw;
92 pw = getpwuid (getuid ());
93 if (pw)
94 user = pw->pw_name;
95 if (!user)
96 user = getenv ("USER");
97 if (!user)
98 user = getenv ("USERNAME");
99 if (!user)
100 user = getenv ("LOGNAME");
101 if (!user)
102 user = "";
104 /** \todo Use FQDN, no clean interface, so requires lot of code */
105 if (gethostname (host, BUF_SIZE - 1) == -1)
106 *host = '\0';
108 return g_strdup_printf ("%s@%s.%d", user, host, (int) getpid ());
111 /* --------------------------------------------------------------------------------------------- */
113 static char *
114 lock_build_symlink_name (const char *fname)
116 char *fname_copy, *symlink_name;
117 char absolute_fname[PATH_MAX];
119 if (mc_realpath (fname, absolute_fname) == NULL)
120 return NULL;
122 fname = x_basename (absolute_fname);
123 fname_copy = g_strdup (fname);
124 absolute_fname[fname - absolute_fname] = '\0';
125 symlink_name = g_strconcat (absolute_fname, ".#", fname_copy, (char *) NULL);
126 g_free (fname_copy);
128 return symlink_name;
131 /* --------------------------------------------------------------------------------------------- */
133 * Extract pid from user@host.domain.pid string
136 static struct lock_s *
137 lock_extract_info (const char *str)
139 size_t i, len;
140 const char *p, *s;
141 static char pid[PID_BUF_SIZE], who[BUF_SIZE];
142 static struct lock_s lock;
144 len = strlen (str);
146 for (p = str + len - 1; p >= str; p--)
147 if (*p == '.')
148 break;
150 /* Everything before last '.' is user@host */
151 i = 0;
152 for (s = str; s < p && i < BUF_SIZE; s++)
153 who[i++] = *s;
154 who[i] = '\0';
156 /* Treat text between '.' and ':' or '\0' as pid */
157 i = 0;
158 for (p = p + 1; (p < str + len) && (*p != ':') && (i < PID_BUF_SIZE); p++)
159 pid[i++] = *p;
160 pid[i] = '\0';
162 lock.pid = (pid_t) atol (pid);
163 lock.who = who;
164 return &lock;
167 /* --------------------------------------------------------------------------------------------- */
169 * Extract user@host.domain.pid from lock file (static string)
172 static char *
173 lock_get_info (const char *lockfname)
175 int cnt;
176 static char buf[BUF_SIZE];
178 cnt = readlink (lockfname, buf, BUF_SIZE - 1);
179 if (cnt == -1 || *buf == '\0')
180 return NULL;
181 buf[cnt] = '\0';
182 return buf;
185 /* --------------------------------------------------------------------------------------------- */
186 /*** public functions ****************************************************************************/
187 /* --------------------------------------------------------------------------------------------- */
189 /* Tries to raise file lock
190 Returns 1 on success, 0 on failure, -1 if abort
191 Warning: Might do screen refresh and lose edit->force */
194 lock_file (const char *fname)
196 char *lockfname, *newlock, *msg, *lock;
197 struct stat statbuf;
198 struct lock_s *lockinfo;
199 gboolean symlink_ok;
201 /* Just to be sure (and don't lock new file) */
202 if (fname == NULL || *fname == '\0')
203 return 0;
205 fname = tilde_expand (fname);
207 /* Locking on VFS is not supported */
208 if (!vfs_file_is_local (fname))
210 g_free ((gpointer) fname);
211 return 0;
214 /* Check if already locked */
215 lockfname = lock_build_symlink_name (fname);
216 g_free ((gpointer) fname);
217 if (lockfname == NULL)
218 return 0;
220 if (lstat (lockfname, &statbuf) == 0)
222 lock = lock_get_info (lockfname);
223 if (lock == NULL)
225 g_free (lockfname);
226 return 0;
228 lockinfo = lock_extract_info (lock);
230 /* Check if locking process alive, ask user if required */
231 if (lockinfo->pid == 0 || !(kill (lockinfo->pid, 0) == -1 && errno == ESRCH))
233 msg =
234 g_strdup_printf (_
235 ("File \"%s\" is already being edited.\n"
236 "User: %s\nProcess ID: %d"), x_basename (lockfname) + 2,
237 lockinfo->who, (int) lockinfo->pid);
238 /* TODO: Implement "Abort" - needs to rewind undo stack */
239 switch (query_dialog
240 (_("File locked"), msg, D_NORMAL, 2, _("&Grab lock"), _("&Ignore lock")))
242 case 0:
243 break;
244 case 1:
245 case -1:
246 g_free (lockfname);
247 g_free (msg);
248 return 0;
250 g_free (msg);
252 unlink (lockfname);
255 /* Create lock symlink */
256 newlock = lock_build_name ();
257 symlink_ok = (symlink (newlock, lockfname) != -1);
258 g_free (newlock);
259 g_free (lockfname);
261 return symlink_ok ? 1 : 0;
264 /* --------------------------------------------------------------------------------------------- */
266 * Lowers file lock if possible
267 * @returns Always 0
271 unlock_file (const char *fname)
273 char *lockfname, *lock;
274 struct stat statbuf;
276 /* Just to be sure */
277 if (fname == NULL || *fname == '\0')
278 return 0;
280 fname = tilde_expand (fname);
281 lockfname = lock_build_symlink_name (fname);
282 g_free ((gpointer) fname);
284 if (lockfname == NULL)
285 return 0;
287 /* Check if lock exists */
288 if (lstat (lockfname, &statbuf) == -1)
290 g_free (lockfname);
291 return 0;
294 lock = lock_get_info (lockfname);
295 if (lock != NULL)
297 /* Don't touch if lock is not ours */
298 if (lock_extract_info (lock)->pid != getpid ())
300 g_free (lockfname);
301 return 0;
305 /* Remove lock */
306 unlink (lockfname);
307 g_free (lockfname);
308 return 0;
311 /* --------------------------------------------------------------------------------------------- */