"Fossies" - the Fresh Open Source Software Archive

Member "jed-0.99-19/src/lock.c" (14 Dec 2009, 10237 Bytes) of package /linux/misc/jed-0.99-19.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "lock.c" see the Fossies "Dox" file reference documentation.

    1 /* -*- mode: C; mode: fold; -*- */
    2 /* Copyright (c) 1999, 2000, 2002, 2003, 2004, 2005, 2006 John E. Davis
    3  * This file is part of JED editor library source.
    4  *
    5  * You may distribute this file under the terms the GNU General Public
    6  * License.  See the file COPYING for more information.
    7  */
    8 #include "config.h"
    9 #include "jed-feat.h"
   10 
   11 /*{{{ Include Files */
   12 #include <stdio.h>
   13 #include <string.h>
   14 
   15 #ifdef HAVE_STDLIB_H
   16 # include <stdlib.h>
   17 #endif
   18 
   19 #ifdef HAVE_UNISTD_H
   20 # include <unistd.h>
   21 #endif
   22 
   23 
   24 #include <sys/types.h>
   25 #include <errno.h>
   26 #include <signal.h>
   27 #include <sys/stat.h>
   28 #include <slang.h>
   29 
   30 #include "jdmacros.h"
   31 
   32 #include "buffer.h"
   33 #include "file.h"
   34 #include "userinfo.h"
   35 #include "ledit.h"
   36 #include "misc.h"
   37 
   38 /*}}}*/
   39 
   40 #if JED_HAS_EMACS_LOCKING && !defined(HAVE_SYMLINK)
   41 # undef JED_HAS_EMACS_LOCKING
   42 # define JED_HAS_EMACS_LOCKING 0
   43 #endif
   44 
   45 #if !JED_HAS_EMACS_LOCKING /*{{{*/
   46 int jed_lock_buffer_file (Buffer *b)
   47 {
   48    (void) b;
   49    return 0;
   50 }
   51 
   52 int jed_unlock_buffer_file (Buffer *b)
   53 {
   54    (void) b;
   55    return 0;
   56 }
   57 int jed_lock_file (char *file)
   58 {
   59    (void) file;
   60    return 0;
   61 }
   62 int jed_unlock_file (char *file)
   63 {
   64    (void) file;
   65    return 0;
   66 }
   67 
   68 /*}}}*/
   69 #else  /* Rest of file */
   70 
   71 /* The basic idea here is quite simple.  Whenever a buffer is attached to
   72  * a file, and that buffer is modified, then attempt to lock the
   73  * file. Moreover, before writing to a file for any reason, lock the
   74  * file. The lock is really a protocol respected and not a real lock.
   75  * The protocol is this: If in the directory of the file is a
   76  * symbolic link with name ".#FILE", the FILE is considered to be locked
   77  * by the process specified by the link.
   78  * 
   79  * Here are the scenerios requiring a lock:
   80  * 
   81  *    1.  When a buffer attached to a file becomes modified.
   82  *    2.  When appending or writing to a file.
   83  * 
   84  * Suppose that a buffer has not been modified but one appends a
   85  * region of the buffer to some file.  Then while that file is being
   86  * written, it should be locked and then released when finished.
   87  * However, suppose another buffer has that file locked. Then in
   88  * order to write to it, either the lock must be stolen or ignored.
   89  * In either of these cases, the user is responsible to what happens
   90  * to the text in the other buffer.  In fact, if the user elects to
   91  * steal the lock from the other buffer, then that lock will be
   92  * deleted after the file has been modfied.  Of course these same comments
   93  * apply if another process owns the lock.
   94  * 
   95  * Now consider the case when a buffer is modified and has the file locked.
   96  * When the buffer is saved, the file will already be locked and there is no
   97  * need to lock it again.
   98  */
   99 
  100 
  101 typedef struct
  102 {
  103    char *host;
  104    char *user;
  105    int pid;
  106 }
  107 Lock_Info_Type;
  108 
  109 static char *make_lockfile_name (char *file)
  110 {
  111    unsigned int len;
  112    char *buf, *dir, *name;
  113 
  114    file = jed_expand_link (file);
  115    if (file == NULL)
  116      return NULL;
  117    
  118    if (-1 == jed_dirfile_to_dir_file (file, &dir, &name))
  119      {
  120     SLfree (file);
  121     return NULL;
  122      }
  123    SLfree (file);
  124 
  125    len = strlen (dir) + strlen (name) + 3;
  126    if (NULL != (buf = SLmalloc (len)))
  127      sprintf (buf, "%s.#%s", dir, name);
  128 
  129    SLfree (dir);
  130    SLfree (name);
  131    return buf;
  132 }
  133 
  134 static void free_lock_info (Lock_Info_Type *l)
  135 {
  136    SLang_free_slstring (l->host);
  137    SLang_free_slstring (l->user);
  138 }
  139 
  140 static int create_lock_info (Lock_Info_Type *l)
  141 {
  142    char *host, *user;
  143 
  144    memset ((char *) l, 0, sizeof (Lock_Info_Type));
  145 
  146    if (NULL == (host = jed_get_hostname ()))
  147      return -1;
  148 
  149    if (NULL == (user = jed_get_username ()))
  150      {
  151     SLang_free_slstring (host);
  152     return -1;
  153      }
  154    l->host = host;
  155    l->user = user;
  156    l->pid = getpid ();
  157    return 0;
  158 }
  159 
  160 /*
  161  * If 0 is returned, then proceed with no lock.  If 1, then force
  162  * the lock, if -1 then abort with no lock.
  163  */
  164 static int ask_about_lock (char *file, Lock_Info_Type *l)
  165 {
  166    char *buf;
  167    unsigned int len;
  168 
  169    if (Batch)
  170      {
  171     jed_verror ("%s is locked by %s@%s.d.", file, l->user, l->host, l->pid);
  172     return -1;
  173      }
  174 
  175    len = 64 + strlen (file) 
  176      + strlen (l->host) + strlen (l->user);
  177    
  178    if (NULL == (buf = SLmalloc (len)))
  179      return -1;
  180 
  181    sprintf (buf, "%s is locked by %s@%s.%d.  (S)teal, (P)roceed, (A)bort?",
  182         file, l->user, l->host, l->pid);
  183 
  184    while (1)
  185      {
  186     switch (jed_get_mini_response (buf))
  187       {
  188        case 's':
  189        case 'S':
  190          SLfree (buf);
  191          return 1;
  192          
  193        case 'P':
  194        case 'p':
  195        case 'q':
  196        case 'Q':
  197          SLfree (buf);
  198          return 0;
  199          
  200        case 'A':
  201        case 'a':
  202          SLfree (buf);
  203          jed_verror ("%s is locked by another process", file);
  204          return -1;
  205       }
  206     if (SLang_get_error ())
  207       {
  208          SLfree (buf);
  209          return -1;
  210       }
  211     
  212     jed_beep ();
  213      }
  214 }
  215 
  216    
  217 /* Returns 0 if file does not exist, or 1 with info, or -1 for error */
  218 static int get_lock_info (char *lockfile, Lock_Info_Type *l)
  219 {
  220    struct stat st;
  221    char buf[1024];
  222    char *b;
  223    char *user, *host;
  224    int n;
  225 
  226    memset ((char *) l, 0, sizeof (Lock_Info_Type));
  227 
  228    if (-1 == lstat (lockfile, &st))
  229      {
  230     if (errno == ENOENT)
  231       return 0;
  232     return -1;
  233      }
  234 
  235    if (((st.st_mode & S_IFMT) & S_IFLNK) == 0)
  236      return -1;
  237 
  238    n = readlink (lockfile, buf, sizeof (buf)-1);
  239    if ((n == -1)
  240        || (n >= (int)sizeof(buf)-1))
  241      return -1;
  242    
  243    buf[n] = 0;
  244    
  245    /* The format is: username@host.pid:boot_time */
  246    b = strchr (buf, '@');
  247    if (b == NULL)
  248      return -1;
  249 
  250    *b++ = 0;
  251 
  252    user = SLang_create_slstring (buf);
  253    if (user == NULL)
  254      return -1;
  255    
  256    host = b;
  257    b += strlen (b);
  258    while (b > host)
  259      {
  260     b--;
  261     if (*b == '.')
  262       break;
  263      }
  264    if (b == host)
  265      {
  266     SLang_free_slstring (user);
  267     return -1;
  268      }
  269    *b++ = 0;
  270    
  271    if (NULL == (host = SLang_create_slstring (host)))
  272      {
  273     SLang_free_slstring (user);
  274     return -1;
  275      }
  276    
  277    l->pid = atoi (b);
  278    l->host = host;
  279    l->user = user;
  280 
  281    return 1;
  282 }
  283 
  284 /* Returns 1 upon success, 0 if lock exists, or -1 upon failure.  If force
  285  * is non-zero, then this function will not return 0.
  286  */
  287 static int perform_lock (char *lockfile, Lock_Info_Type *l, int force)
  288 {
  289    unsigned int len;
  290    char *buf;
  291    int status;
  292    int not_supported = 0;
  293 
  294    len = 32 + strlen (l->host) + strlen (l->user);
  295    if (NULL == (buf = SLmalloc (len)))
  296      return -1;
  297 
  298    sprintf (buf, "%s@%s.%d", l->user, l->host, l->pid);
  299    if (0 == symlink (buf, lockfile))
  300      {
  301     SLfree (buf);
  302     return 1;
  303      }
  304 
  305    if (not_supported 
  306 # ifdef EPERM
  307        || (errno == EPERM)
  308 # endif
  309 # ifdef EOPNOTSUPP
  310        || (errno == EOPNOTSUPP)
  311 # endif
  312 # ifdef ENOSYS
  313        || (errno == ENOSYS)
  314 # endif
  315        )
  316      {
  317     jed_vmessage (0, "File system does not support symbolic links: file not locked.");
  318     SLfree (buf);
  319     return 1;
  320      }
  321 
  322 # ifdef EACCES
  323    if (errno == EACCES)
  324      {
  325     jed_vmessage (0, "No permission to lock file");
  326     SLfree (buf);
  327     return 1;
  328      }
  329 #endif
  330    if (force == 0)
  331      {
  332     SLfree (buf);
  333     if (errno == EEXIST)
  334       return 0;
  335     
  336     return -1;
  337      }
  338    
  339    (void) unlink (lockfile);
  340    status = symlink (buf, lockfile);
  341    if (status == -1)
  342      jed_vmessage (0, "Unable to create lockfile %s", lockfile);
  343    else
  344      status = 1;
  345 
  346    SLfree (buf);
  347    return status;
  348 }
  349 
  350 /* Return 0 if same, -1 if same host, 1 if different host */
  351 static int compare_lock_info (Lock_Info_Type *a, Lock_Info_Type *b)
  352 {   
  353    if (a->host != b->host)
  354      return 1;
  355 
  356    if ((a->pid != b->pid)
  357        || (a->user != b->user))
  358      return -1;
  359    
  360    return 0;
  361 }
  362 
  363 int jed_lock_file (char *file)
  364 {
  365    char *lockfile;
  366    Lock_Info_Type l0;
  367    int force;
  368    int status;
  369 
  370    lockfile = make_lockfile_name (file);
  371    if (lockfile == NULL)
  372      return -1;
  373 
  374    if (-1 == create_lock_info (&l0))
  375      {
  376     SLfree (lockfile);
  377     return -1;
  378      }
  379 
  380    force = 0;
  381    while (1)
  382      {
  383     Lock_Info_Type l1;
  384 
  385     status = perform_lock (lockfile, &l0, force);
  386     if (status == 1)           /* lock succeeded */
  387       break;
  388     
  389     if (status == -1)          /* error */
  390       break;
  391     
  392     /* Lock already exists.  Let's see who owns it */
  393     status = get_lock_info (lockfile, &l1);
  394     if (status == -1)
  395       break;
  396     
  397     if (status == 0)           /* lock nolonger exists, try again */
  398       continue;
  399 
  400     status = compare_lock_info (&l0, &l1);
  401     if (status == 0)
  402       {
  403          /* We already own this lock */
  404          free_lock_info (&l1);
  405          status = 1;
  406          break;
  407       }
  408     
  409     if (status == -1)
  410       {
  411          /* Some process on this machine owns it.  See if the process is
  412           * alive.
  413           */
  414          if (l1.pid <= 0)
  415            status = 2;
  416          else if (-1 == kill (l1.pid, 0))
  417            {
  418 # ifdef ESRCH
  419           if (errno == ESRCH) /* Doesn't exist */
  420             status = 2;
  421 # endif
  422            }
  423       }
  424     
  425     if (status != 2)
  426       status = ask_about_lock (file, &l1);
  427 
  428     free_lock_info (&l1);
  429 
  430     if (status <= 0)
  431       break;
  432     
  433     force = 1;
  434      }
  435 
  436    SLfree (lockfile);
  437    free_lock_info (&l0);
  438 
  439    if (status == -1)
  440      return -1;
  441    
  442    return 0;
  443 }
  444 
  445 int jed_unlock_file (char *file)
  446 {
  447    Lock_Info_Type l0, l1;
  448    char *lockfile;
  449    int status;
  450 
  451    lockfile = make_lockfile_name (file);
  452    if (lockfile == NULL)
  453      return -1;
  454 
  455    if (-1 == create_lock_info (&l0))
  456      {
  457     SLfree (lockfile);
  458     return -1;
  459      }
  460 
  461    status = get_lock_info (lockfile, &l1);
  462    if (status <= 0)
  463      {
  464     free_lock_info (&l0);
  465     SLfree (lockfile);
  466     return status;
  467      }
  468 
  469    if (0 == compare_lock_info (&l0, &l1))
  470      (void) unlink (lockfile);
  471 
  472    free_lock_info (&l0);
  473    free_lock_info (&l1);
  474    SLfree (lockfile);
  475 
  476    return 0;
  477 }
  478 
  479 
  480 int jed_lock_buffer_file (Buffer *b)
  481 {
  482    int status;
  483    char *file;
  484 
  485    if ((b->file[0] == 0) || (b->flags & BUFFER_NON_LOCKING))
  486      return 0;
  487     
  488    file = jed_dir_file_merge (b->dir, b->file);
  489    status = jed_lock_file (file);
  490    SLfree (file);
  491 
  492    return status;
  493 }
  494 
  495 int jed_unlock_buffer_file (Buffer *b)
  496 {
  497    int status;
  498    char *file;
  499 
  500    if ((b->file[0] == 0) || (b->flags & BUFFER_NON_LOCKING))
  501      return 0;
  502 
  503    file = jed_dir_file_merge (b->dir, b->file);
  504    status = jed_unlock_file (file);
  505    SLfree (file);
  506    
  507    return status;
  508 }
  509 
  510 int jed_unlock_buffer_files (void)
  511 {
  512    int status;
  513    Buffer *b;
  514    
  515    status = 0;
  516    b = CBuf;
  517    do
  518      {
  519     b = b->next;
  520     
  521     if (-1 == jed_unlock_buffer_file (b))
  522       status = -1;
  523      }
  524    while (b != CBuf);
  525 
  526    return status;
  527 }
  528    
  529 #endif                     /* JED_HAS_EMACS_LOCKING */