Updated italian translatio
[midnight-commander.git] / edit / editlock.c
blob8249375364282671709bded2c17693d086de0ce3
1 /* editor file locking.
3 Copyright (C) 2003 the Free Software Foundation
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., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307, USA.
24 #include <config.h>
25 #include "edit.h"
26 #include "editlock.h"
28 #include "../src/wtools.h" /* edit_query_dialog () */
30 #define BUF_SIZE 255
31 #define PID_BUF_SIZE 10
33 struct lock_s {
34 char *who;
35 pid_t pid;
38 /* Locking scheme used in mcedit is based on a documentation found
39 in JED editor sources. Abstract from lock.c file (by John E. Davis):
41 The basic idea here is quite simple. Whenever a buffer is attached to
42 a file, and that buffer is modified, then attempt to lock the
43 file. Moreover, before writing to a file for any reason, lock the
44 file. The lock is really a protocol respected and not a real lock.
45 The protocol is this: If in the directory of the file is a
46 symbolic link with name ".#FILE", the FILE is considered to be locked
47 by the process specified by the link.
50 /* Build user@host.domain.pid string (need to be freed) */
51 static char *
52 lock_build_name (char *fname)
54 char host[BUF_SIZE], *user;
56 if (!
57 ((user = getpwuid (getuid ())->pw_name) || (user = getenv ("USER"))
58 || (user = getenv ("USERNAME")) || (user = getenv ("LOGNAME"))))
59 user = "";
61 /* TODO: Use FQDN, no clean interface, so requires lot of code */
62 if (gethostname (host, BUF_SIZE - 1) == -1)
63 *host = '\0';
65 return g_strdup_printf ("%s@%s.%d", user, host, getpid ());
68 /* Extract pid from user@host.domain.pid string */
69 static struct lock_s *
70 lock_extract_info (char *str)
72 int i;
73 char *p, *s;
74 static char pid[PID_BUF_SIZE], who[BUF_SIZE];
75 static struct lock_s lock;
77 for (p = str + strlen (str) - 1; p >= str; p--)
78 if (*p == '.')
79 break;
81 /* Everything before last '.' is user@host */
82 i = 0;
83 for (s = str; s < p && i < BUF_SIZE; s++)
84 who[i++] = *s;
85 who[i] = '\0';
87 /* Treat text between '.' and ':' or '\0' as pid */
88 i = 0;
89 for (p = p + 1;
90 p < str + strlen (str) && *p != ':' && i < PID_BUF_SIZE; p++)
91 pid[i++] = *p;
92 pid[i] = '\0';
94 lock.pid = (pid_t) atol (pid);
95 lock.who = who;
96 return &lock;
99 /* Extract user@host.domain.pid from lock file (static string) */
100 static char *
101 lock_get_info (char *lockfname)
103 int cnt;
104 static char buf[BUF_SIZE];
106 if ((cnt = readlink (lockfname, buf, BUF_SIZE - 1)) == -1 || !buf
107 || !*buf)
108 return NULL;
109 buf[cnt] = '\0';
110 return buf;
114 /* Tries to raise file lock
115 Returns 1 on success, 0 on failure, -1 if abort
116 Warning: Might do screen refresh and lose edit->force */
118 edit_lock_file (char *fname)
120 char *lockfname, *newlock, *msg, *lock;
121 struct stat statbuf;
122 struct lock_s *lockinfo;
124 /* Just to be sure (and don't lock new file) */
125 if (!fname || !*fname)
126 return 0;
128 /* Locking on VFS is not supported */
129 if (!vfs_file_is_local (fname))
130 return 0;
132 /* Check if already locked */
133 lockfname = g_strconcat (".#", fname, NULL);
134 if (lstat (lockfname, &statbuf) == 0) {
135 lock = lock_get_info (lockfname);
136 if (!lock) {
137 g_free (lockfname);
138 return 0;
140 lockinfo = lock_extract_info (lock);
142 /* Check if locking process alive, ask user if required */
143 if (!lockinfo->pid
144 || !(kill (lockinfo->pid, 0) == -1 && errno == ESRCH)) {
145 msg =
146 g_strdup_printf (_
147 ("File \"%s\" is already being edited\n"
148 "User: %s\nProcess ID: %d"), fname,
149 lockinfo->who, lockinfo->pid);
150 /* TODO: Implement "Abort" - needs to rewind undo stack */
151 switch (edit_query_dialog2
152 (_("File locked"), msg, _("&Grab lock"),
153 _("&Ignore lock"))) {
154 case 0:
155 break;
156 case 1:
157 case -1:
158 g_free (lockfname);
159 g_free (msg);
160 return 0;
162 g_free (msg);
164 unlink (lockfname);
167 /* Create lock symlink */
168 newlock = lock_build_name (fname);
169 if (symlink (newlock, lockfname) == -1) {
170 g_free (lockfname);
171 g_free (newlock);
172 return 0;
175 g_free (lockfname);
176 g_free (newlock);
177 return 1;
180 /* Lowers file lock if possible
181 Always returns 0 to make 'lock = edit_unlock_file (f)' possible */
183 edit_unlock_file (char *fname)
185 char *lockfname, *lock;
186 struct stat statbuf;
188 /* Just to be sure */
189 if (!fname || !*fname)
190 return 0;
192 lockfname = g_strconcat (".#", fname, NULL);
194 /* Check if lock exists */
195 if (lstat (lockfname, &statbuf) == -1) {
196 g_free (lockfname);
197 return 0;
200 lock = lock_get_info (lockfname);
201 if (lock) {
202 /* Don't touch if lock is not ours */
203 if (lock_extract_info (lock)->pid != getpid ()) {
204 g_free (lockfname);
205 return 0;
209 /* Remove lock */
210 unlink (lockfname);
211 g_free (lockfname);
212 return 0;