4 Copyright (C) 2003-2023
5 Free Software Foundation, Inc.
10 This file is part of the Midnight Commander.
12 The Midnight Commander is free software: you can redistribute it
13 and/or modify it under the terms of the GNU General Public License as
14 published by the Free Software Foundation, either version 3 of the License,
15 or (at your option) any later version.
17 The Midnight Commander is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 * \brief Source: file locking
31 * Locking scheme is based on a documentation found
32 * in JED editor sources. Abstract from lock.c file (by John E. Davis):
34 * The basic idea here is quite simple. Whenever a buffer is attached to
35 * a file, and that buffer is modified, then attempt to lock the
36 * file. Moreover, before writing to a file for any reason, lock the
37 * file. The lock is really a protocol respected and not a real lock.
38 * The protocol is this: If in the directory of the file is a
39 * symbolic link with name ".#FILE", the FILE is considered to be locked
40 * by the process specified by the link.
45 #include <signal.h> /* kill() */
48 #include <sys/types.h>
57 #include "lib/global.h"
58 #include "lib/vfs/vfs.h"
59 #include "lib/util.h" /* tilde_expand() */
61 #include "lib/widget.h" /* query_dialog() */
63 /*** global variables ****************************************************************************/
65 /*** file scope macro definitions ****************************************************************/
68 #define PID_BUF_SIZE 10
70 /*** file scope type declarations ****************************************************************/
78 /*** forward declarations (file scope functions) *************************************************/
80 /*** file scope variables ************************************************************************/
82 /* --------------------------------------------------------------------------------------------- */
83 /*** file scope functions ************************************************************************/
84 /* --------------------------------------------------------------------------------------------- */
85 /** \fn static char * lock_build_name (void)
86 * \brief builds user@host.domain.pid string (need to be freed)
87 * \return a pointer to lock filename
91 lock_build_name (void)
94 const char *user
= NULL
;
97 pw
= getpwuid (getuid ());
101 user
= getenv ("USER");
103 user
= getenv ("USERNAME");
105 user
= getenv ("LOGNAME");
109 /** \todo Use FQDN, no clean interface, so requires lot of code */
110 if (gethostname (host
, sizeof (host
) - 1) == -1)
113 return g_strdup_printf ("%s@%s.%d", user
, host
, (int) getpid ());
116 /* --------------------------------------------------------------------------------------------- */
119 lock_build_symlink_name (const vfs_path_t
* fname_vpath
)
122 char *str_filename
, *str_dirname
, *symlink_name
;
124 /* get first path piece */
125 elpath
= vfs_path_get_by_index (fname_vpath
, 0)->path
;
127 str_filename
= g_path_get_basename (elpath
);
128 str_dirname
= g_path_get_dirname (elpath
);
129 symlink_name
= g_strconcat (str_dirname
, PATH_SEP_STR
".#", str_filename
, (char *) NULL
);
130 g_free (str_dirname
);
131 g_free (str_filename
);
136 /* --------------------------------------------------------------------------------------------- */
138 * Extract pid from user@host.domain.pid string
142 lock_extract_info (const char *str
)
146 static char pid
[PID_BUF_SIZE
], who
[BUF_SIZE
];
151 for (p
= str
+ len
- 1; p
>= str
&& *p
!= '.'; p
--)
154 /* Everything before last '.' is user@host */
155 for (i
= 0, s
= str
; i
< sizeof (who
) && s
< p
; i
++, s
++)
157 if (i
== sizeof (who
))
161 /* Treat text between '.' and ':' or '\0' as pid */
162 for (i
= 0, p
++, s
= str
+ len
; i
< sizeof (pid
) && p
< s
&& *p
!= ':'; i
++, p
++)
164 if (i
== sizeof (pid
))
168 lock
.pid
= (pid_t
) atol (pid
);
173 /* --------------------------------------------------------------------------------------------- */
175 * Extract user@host.domain.pid from lock file (static string)
179 lock_get_info (const char *lockfname
)
182 static char buf
[BUF_SIZE
];
184 cnt
= readlink (lockfname
, buf
, sizeof (buf
) - 1);
185 if (cnt
== -1 || *buf
== '\0')
191 /* --------------------------------------------------------------------------------------------- */
192 /*** public functions ****************************************************************************/
193 /* --------------------------------------------------------------------------------------------- */
195 /* Tries to raise file lock
196 Returns 1 on success, 0 on failure, -1 if abort
197 Warning: Might do screen refresh and lose edit->force */
200 lock_file (const vfs_path_t
* fname_vpath
)
202 char *lockfname
= NULL
, *newlock
, *msg
;
206 gboolean symlink_ok
= FALSE
;
209 if (fname_vpath
== NULL
)
212 elpath
= vfs_path_get_by_index (fname_vpath
, 0)->path
;
213 /* Just to be sure (and don't lock new file) */
217 /* Locking on VFS is not supported */
218 is_local
= vfs_file_is_local (fname_vpath
);
221 /* Check if already locked */
222 lockfname
= lock_build_symlink_name (fname_vpath
);
225 if (!is_local
|| lockfname
== NULL
)
228 if (lstat (lockfname
, &statbuf
) == 0)
232 lock
= lock_get_info (lockfname
);
235 lockinfo
= lock_extract_info (lock
);
237 /* Check if locking process alive, ask user if required */
238 if (lockinfo
->pid
== 0 || !(kill (lockinfo
->pid
, 0) == -1 && errno
== ESRCH
))
242 ("File \"%s\" is already being edited.\n"
243 "User: %s\nProcess ID: %d"), x_basename (lockfname
) + 2,
244 lockinfo
->who
, (int) lockinfo
->pid
);
245 /* TODO: Implement "Abort" - needs to rewind undo stack */
247 (_("File locked"), msg
, D_NORMAL
, 2, _("&Grab lock"), _("&Ignore lock")))
253 default: /* Esc Esc */
262 /* Create lock symlink */
263 newlock
= lock_build_name ();
264 symlink_ok
= (symlink (newlock
, lockfname
) != -1);
269 return symlink_ok
? 1 : 0;
272 /* --------------------------------------------------------------------------------------------- */
274 * Lowers file lock if possible
279 unlock_file (const vfs_path_t
* fname_vpath
)
284 if (fname_vpath
== NULL
)
287 elpath
= vfs_path_get_by_index (fname_vpath
, 0)->path
;
288 /* Just to be sure (and don't lock new file) */
292 lockfname
= lock_build_symlink_name (fname_vpath
);
293 if (lockfname
!= NULL
)
297 /* Check if lock exists */
298 if (lstat (lockfname
, &statbuf
) != -1)
302 lock
= lock_get_info (lockfname
);
303 /* Don't touch if lock is not ours */
304 if (lock
== NULL
|| lock_extract_info (lock
)->pid
== getpid ())
314 /* --------------------------------------------------------------------------------------------- */