editlock.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /* editor file locking.
  2. Copyright (C) 2003 the Free Software Foundation
  3. Authors: 2003 Adam Byrtek
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  15. 02111-1307, USA.
  16. */
  17. #include <config.h>
  18. #include <signal.h> /* kill() */
  19. #include "edit.h"
  20. #include "editlock.h"
  21. #include "../src/wtools.h" /* edit_query_dialog () */
  22. #define BUF_SIZE 255
  23. #define PID_BUF_SIZE 10
  24. struct lock_s {
  25. char *who;
  26. pid_t pid;
  27. };
  28. /* Locking scheme used in mcedit is based on a documentation found
  29. in JED editor sources. Abstract from lock.c file (by John E. Davis):
  30. The basic idea here is quite simple. Whenever a buffer is attached to
  31. a file, and that buffer is modified, then attempt to lock the
  32. file. Moreover, before writing to a file for any reason, lock the
  33. file. The lock is really a protocol respected and not a real lock.
  34. The protocol is this: If in the directory of the file is a
  35. symbolic link with name ".#FILE", the FILE is considered to be locked
  36. by the process specified by the link.
  37. */
  38. /* Build user@host.domain.pid string (need to be freed) */
  39. static char *
  40. lock_build_name (const char *fname)
  41. {
  42. char host[BUF_SIZE];
  43. const char *user;
  44. if (!
  45. ((user = getpwuid (getuid ())->pw_name) || (user = getenv ("USER"))
  46. || (user = getenv ("USERNAME")) || (user = getenv ("LOGNAME"))))
  47. user = "";
  48. /* TODO: Use FQDN, no clean interface, so requires lot of code */
  49. if (gethostname (host, BUF_SIZE - 1) == -1)
  50. *host = '\0';
  51. return g_strdup_printf ("%s@%s.%d", user, host, (int) getpid ());
  52. }
  53. /* Extract pid from user@host.domain.pid string */
  54. static struct lock_s *
  55. lock_extract_info (const char *str)
  56. {
  57. int i;
  58. const char *p, *s;
  59. static char pid[PID_BUF_SIZE], who[BUF_SIZE];
  60. static struct lock_s lock;
  61. for (p = str + strlen (str) - 1; p >= str; p--)
  62. if (*p == '.')
  63. break;
  64. /* Everything before last '.' is user@host */
  65. i = 0;
  66. for (s = str; s < p && i < BUF_SIZE; s++)
  67. who[i++] = *s;
  68. who[i] = '\0';
  69. /* Treat text between '.' and ':' or '\0' as pid */
  70. i = 0;
  71. for (p = p + 1;
  72. p < str + strlen (str) && *p != ':' && i < PID_BUF_SIZE; p++)
  73. pid[i++] = *p;
  74. pid[i] = '\0';
  75. lock.pid = (pid_t) atol (pid);
  76. lock.who = who;
  77. return &lock;
  78. }
  79. /* Extract user@host.domain.pid from lock file (static string) */
  80. static char *
  81. lock_get_info (const char *lockfname)
  82. {
  83. int cnt;
  84. static char buf[BUF_SIZE];
  85. if ((cnt = readlink (lockfname, buf, BUF_SIZE - 1)) == -1 || !buf
  86. || !*buf)
  87. return NULL;
  88. buf[cnt] = '\0';
  89. return buf;
  90. }
  91. /* Tries to raise file lock
  92. Returns 1 on success, 0 on failure, -1 if abort
  93. Warning: Might do screen refresh and lose edit->force */
  94. int
  95. edit_lock_file (const char *fname)
  96. {
  97. char *lockfname, *newlock, *msg, *lock;
  98. struct stat statbuf;
  99. struct lock_s *lockinfo;
  100. /* Just to be sure (and don't lock new file) */
  101. if (!fname || !*fname)
  102. return 0;
  103. /* Locking on VFS is not supported */
  104. if (!vfs_file_is_local (fname))
  105. return 0;
  106. /* Check if already locked */
  107. lockfname = g_strconcat (".#", fname, (char *) NULL);
  108. if (lstat (lockfname, &statbuf) == 0) {
  109. lock = lock_get_info (lockfname);
  110. if (!lock) {
  111. g_free (lockfname);
  112. return 0;
  113. }
  114. lockinfo = lock_extract_info (lock);
  115. /* Check if locking process alive, ask user if required */
  116. if (!lockinfo->pid
  117. || !(kill (lockinfo->pid, 0) == -1 && errno == ESRCH)) {
  118. msg =
  119. g_strdup_printf (_
  120. ("File \"%s\" is already being edited\n"
  121. "User: %s\nProcess ID: %d"), fname,
  122. lockinfo->who, (int) lockinfo->pid);
  123. /* TODO: Implement "Abort" - needs to rewind undo stack */
  124. switch (edit_query_dialog2
  125. (_("File locked"), msg, _("&Grab lock"),
  126. _("&Ignore lock"))) {
  127. case 0:
  128. break;
  129. case 1:
  130. case -1:
  131. g_free (lockfname);
  132. g_free (msg);
  133. return 0;
  134. }
  135. g_free (msg);
  136. }
  137. unlink (lockfname);
  138. }
  139. /* Create lock symlink */
  140. newlock = lock_build_name (fname);
  141. if (symlink (newlock, lockfname) == -1) {
  142. g_free (lockfname);
  143. g_free (newlock);
  144. return 0;
  145. }
  146. g_free (lockfname);
  147. g_free (newlock);
  148. return 1;
  149. }
  150. /* Lowers file lock if possible
  151. Always returns 0 to make 'lock = edit_unlock_file (f)' possible */
  152. int
  153. edit_unlock_file (const char *fname)
  154. {
  155. char *lockfname, *lock;
  156. struct stat statbuf;
  157. /* Just to be sure */
  158. if (!fname || !*fname)
  159. return 0;
  160. lockfname = g_strconcat (".#", fname, (char *) NULL);
  161. /* Check if lock exists */
  162. if (lstat (lockfname, &statbuf) == -1) {
  163. g_free (lockfname);
  164. return 0;
  165. }
  166. lock = lock_get_info (lockfname);
  167. if (lock) {
  168. /* Don't touch if lock is not ours */
  169. if (lock_extract_info (lock)->pid != getpid ()) {
  170. g_free (lockfname);
  171. return 0;
  172. }
  173. }
  174. /* Remove lock */
  175. unlink (lockfname);
  176. g_free (lockfname);
  177. return 0;
  178. }