home *** CD-ROM | disk | FTP | other *** search
/ kermit.columbia.edu / kermit.columbia.edu.tar / kermit.columbia.edu / sredird / telnetcpcd-1.09.tar.gz / telnetcpcd-1.09.tar / uucplocks.c < prev    next >
C/C++ Source or Header  |  2003-08-12  |  11KB  |  394 lines

  1. /*
  2.     uucplocks.c
  3.  
  4.     Copyright (c) 2002,2003 Thomas J Pinkl <tom@pinkl.com>
  5.  
  6.     UUCP locking functions.
  7.  
  8.     Version 1.00    05/25/1999        Initial version
  9.     Version 1.01    12/10/2001        Modified for telnetcpcd
  10.     Version 1.02    05/15/2002        Modified uucp_lock_filename() such that 
  11.                                     it doesn't need to call mybasename().
  12.     Version 1.03    05/16/2002        Added uulog() and changed all of the 
  13.                                     error logging to use it.
  14.     Version 1.04    05/22/2002        Added conditional code for OSR5 to 
  15.                                     uucp_lock_filename() to force the lock 
  16.                                     file name to lower case.
  17. */
  18.  
  19. #include "telnetcpcd.h"
  20.  
  21. /*
  22.     create a uucp lock file and write the specified pid 
  23.     to it.
  24.  
  25.     returns a ptr to the lock file name on success, NULL on failure.
  26. */
  27. char *uucp_lock(char *device,pid_t pid)
  28. {
  29.     extern int errno;
  30.     int ret;                            /* gp int */
  31.     mode_t mode;                        /* file mode */
  32.     pid_t lockpid;                        /* pid which owns lock file */
  33.     char *tmpname;                        /* tmp file name */
  34.     char *lockfile;                        /* lock file name */
  35.  
  36.     tmpname = uucp_tmp_filename(pid);    /* ptr to tmp file name */
  37.     mode = 0644;                        /* must be writable by owner */
  38.  
  39.     /* create tmp file and write pid to it */
  40.     ret = write_pidfile(tmpname,O_WRONLY|O_CREAT,mode,pid);
  41.     if (ret != 0) return(NULL);
  42.  
  43.     lockfile = uucp_lock_filename(device); /* ptr to lock file name */
  44.     ret = link(tmpname,lockfile);        /* link() is atomic */
  45.     if (ret != 0) {                        /* if link() failed */
  46.         if (errno == EEXIST) {            /* existing lock file */
  47.             /*
  48.                 07/30/2003 - There's a race condition here.  If 2 (or more) processes 
  49.                 simultaneously try to lock the same device AND both detect a stale lock AND 
  50.                 one process removes the stale lock, creates a new lock, and verifies it 
  51.                 BEFORE the other process removes what WAS a stale lock (but isn't any more), 
  52.                 then you have a situation where both processes believe they've locked the 
  53.                 same device.
  54.  
  55.                 08/01/2003 - to avoid (but not completely eliminate) this race condition,
  56.                 I've moved the unlink() call before any calls to uulog() and I've 
  57.                 added a sleep before we try to call link() again.  The sleep is what 
  58.                 avoids the race condition.  It needs to be long enough (I choose 3 seconds) 
  59.                 that OTHER paths through this code will execute to completion (or up to the 
  60.                 same sleep statement) BEFORE we call link() again.  And it makes it LESS 
  61.                 likely that, given 2 processes executing this same code block, one will be 
  62.                 able to sleep AND call link() successfully, BEFORE the other calls unlink(). 
  63.  
  64.                 It should be noted that moving the unlink() call before the uulog() 
  65.                 call also plays a part.  Since the uulog() call involves I/O, it might cause 
  66.                 the calling process to block, yielding the CPU to another process.  By calling
  67.                 unlink() first, it's more likely to be executed in the same quantum where we 
  68.                 detect the stale lock.
  69.             */
  70.             if (uucp_verify_lock(lockfile,&lockpid) == 1) {
  71.                 ret = unlink(lockfile);            /* remove stale lock */
  72.                 if (ret == 0) {
  73.                     uulog(LOG_INFO,"removed stale uucp lock %s, owned by PID %d",lockfile,lockpid);
  74.                 } else {
  75.                     uulog(LOG_ERR,"unlink(%s) error: %s",lockfile,strerror(errno));
  76.                 }
  77.                 sleep(3);                        /* avoid race condition */
  78.                 ret = link(tmpname,lockfile);    /* try link() again */
  79.             }
  80.         }
  81.     }
  82.     if (ret != 0) {                        /* if link() failed */
  83.         if (errno != EEXIST) {            /* not due to existing lock file */
  84.             uulog(LOG_ERR,"link(%s,%s) error: %s",tmpname,lockfile,strerror(errno));
  85.             uulog(LOG_ERR,"error creating uucp lock file %s",lockfile);
  86.         }
  87.         unlink(tmpname);                /* remove tmp file */
  88.         return(NULL);                    /* fail */
  89.     }
  90.     
  91.     /* else, link() was successful */
  92.  
  93.     unlink(tmpname);                    /* remove tmp file */
  94.  
  95.     /* verify that we really got the lock */
  96.     ret = uucp_verify_lock(lockfile,&lockpid);
  97.     if ((ret == 0) && (lockpid == pid)) {
  98.         uulog(LOG_INFO,"created uucp lock file %s",lockfile);
  99.         return(lockfile);                /* success */
  100.     } else {
  101.         uulog(LOG_INFO,"error verifying uucp lock file %s",lockfile);
  102.         return(NULL);                    /* fail */
  103.     }
  104. }
  105.  
  106. /*
  107.     return a ptr to a temporary file in the uucp lock 
  108.     directory.
  109. */
  110. char *uucp_tmp_filename(pid_t pid)
  111. {
  112.     static char tmpname[PATH_MAX];        /* tmp file name */
  113.     char *template;
  114.     char *p;                            /* gp ptr */
  115.  
  116.     template = uucp_lock_template();    /* get ptr to "/path/to/lock/dir/LCK..%s" */
  117.     sprintf(tmpname,template,"tmp.");
  118.     p = tmpname + strlen(tmpname);        /* point to the null */
  119.     sprintf(p,"%d",pid);
  120.     return(tmpname);
  121. }
  122.  
  123. /*
  124.     return a ptr to the uucp lock file name for the specified 
  125.     device.
  126. */
  127. char *uucp_lock_filename(char *device)
  128. {
  129.     static char lockfile[PATH_MAX];        /* lock file name */
  130.     char *template;
  131.     char *name;
  132. #ifdef OSR5
  133.     char *lcname;
  134.     char *p;
  135.     int c;
  136. #endif
  137.     struct stat st;
  138.  
  139.     template = uucp_lock_template();    /* get ptr to "/path/to/lock/dir/LCK..%s" */
  140.     lockfile[0] = '\0';                    /* init lockfile */
  141.  
  142.     if (device != NULL) {
  143.         name = strrchr(device,'/');        /* find last '/' in device name */
  144.         if (name != NULL) {
  145.             name++;                        /* point to char after the last '/' */
  146.         } else {
  147.             name = device;
  148.         }
  149. #ifdef OSR5
  150.         lcname = strdup(name);            /* make a copy of the string */
  151.         if (lcname != NULL) {            /* force it to lower case */
  152.             p = lcname;
  153.             while (*p != '\0') {
  154.                 if (isupper((int) *p)) {
  155.                     c = tolower((int) *p);
  156.                     *p = c;
  157.                 }
  158.                 p++;
  159.             }
  160.             name = lcname;                /* use the lower case version */
  161.         }
  162. #endif
  163.         if (strstr(template,"%s") != NULL) {    /* template contains "%s" */
  164.             sprintf(lockfile,template,name);
  165.         } else {
  166.             /* assume SVR4 style names, "LK.%03u.%03u.%03u" */
  167.             if (stat(device,&st) == 0) {
  168.                 sprintf(lockfile,template,major(st.st_dev),major(st.st_rdev),minor(st.st_rdev));
  169.                                                 /* device, major device, minor device */
  170.             }
  171.         }
  172.         if (lockfile[0] == '\0') {
  173.             sprintf(lockfile,template,name);
  174.         }
  175.     } else {
  176.         sprintf(lockfile,template,"null");
  177.     }
  178. #ifdef OSR5
  179.     if (lcname != NULL) {
  180.         free(lcname);                    /* free the string copy */
  181.     }
  182. #endif
  183.     return(lockfile);
  184. }
  185.  
  186. char *uucp_lock_template(void)
  187. {
  188. #ifdef HAVE_CONFIG_T
  189.     extern struct config_t conf;
  190. #endif
  191.     static char template[PATH_MAX];        /* file name template */
  192.     char *p;
  193.  
  194. #ifdef HAVE_CONFIG_T
  195.     /* uucp lock directory */
  196.     if (conf.lockdir != NULL) {
  197.         strcpy(template,conf.lockdir);
  198.     } else {
  199.         strcpy(template,UUCPLOCK_DIR);
  200.     }
  201.  
  202.     /* append '/' */
  203.     strcat(template,"/");
  204.  
  205.     /* append the lock template, eg. "LCK..%s" */
  206.     if (conf.locktemplate != NULL) {
  207.         strcat(template,conf.locktemplate);
  208.     } else {
  209.         strcat(template,UUCPLOCK_TMPL);
  210.     }
  211. #else
  212.     /* uucp lock prefix */
  213.     strcpy(template,UUCP_LOCK_PREFIX);
  214. #endif    /* HAVE_CONFIG_T */
  215.  
  216.     /* append "%s" if the template does NOT contain a '%' */
  217.     if ((p = strchr(template,'%')) == NULL) {
  218.         strcat(template,"%s");
  219.     }
  220.  
  221.     return(template);
  222. }
  223.  
  224. /*
  225.     remove the uucp lock file for the specified device.
  226.  
  227.     returns 0 on success, non-zero on failure.
  228. */
  229. int uucp_unlock(char *device)
  230. {
  231.     extern int errno;
  232.     char *lockfile;                        /* lock file name */
  233.     int ret;                            /* gp int */
  234.  
  235.     lockfile = uucp_lock_filename(device); /* ptr to lock file name */
  236.     ret = unlink(lockfile);
  237.     if (ret == 0) {
  238.         uulog(LOG_INFO,"removed uucp lock file %s",lockfile);
  239.     } else {
  240.         uulog(LOG_ERR,"unlink(%s) error: %s",lockfile,strerror(errno));
  241.     }
  242.     return(ret);
  243. }
  244.  
  245. /*
  246.     read the uucp lock file whose pathname is specified 
  247.     and verify that the pid contained within, is valid.
  248.  
  249.     if we can open and read the lock file: returns 0 if the 
  250.     lock is valid (ie. the pid still exists), returns 1 if 
  251.     the lock is invalid (ie. the pid doesn't exist). 
  252.     returns -1 if we can't open or read the lock file.
  253. */
  254. int uucp_verify_lock(char *path,pid_t *ret_pid)
  255. {
  256.     extern int errno;
  257.     int fd;                                /* fd for file */
  258.     struct stat sbuf;                    /* stat buffer */
  259.     char apid[12];                        /* ascii pid */
  260.     char *p;
  261.     int ret;                            /* gp int */
  262.     pid_t pid;                            /* process id */
  263.     int error;                            /* error flag */
  264.  
  265.     if (ret_pid != NULL) {
  266.         *ret_pid = 0;                    /* init returned pid */
  267.     }
  268.  
  269.     fd = open(path,O_RDONLY);            /* open file */
  270.     if (fd < 0) {
  271.         uulog(LOG_ERR,"open(%s,O_RDONLY) error: %s",path,strerror(errno));
  272.         return(-1);                        /* fail */
  273.     }
  274.     if (fstat(fd,&sbuf) != 0) {            /* get file status */
  275.         uulog(LOG_ERR,"fstat() error: %s",strerror(errno));
  276.         close(fd);                        /* close file */
  277.         return(-1);                        /* fail */
  278.     }
  279.     error = 0;                            /* clear error flag */
  280.     if (sbuf.st_size == sizeof(pid)) {    /* binary pid */
  281.         ret = read(fd,&pid,sizeof(pid));/* read it */
  282.         if (ret != sizeof(pid)) {
  283.             ++error;
  284.             uulog(LOG_ERR,"error reading pid from %s: %s",path,strerror(errno));
  285.         }
  286.     } else if (sbuf.st_size < sizeof(apid)) {    /* ascii pid */
  287.         ret = read(fd,apid,sbuf.st_size);        /* read it */
  288.         if (ret != sbuf.st_size) {
  289.             ++error;
  290.             uulog(LOG_ERR,"error reading pid from %s: %s",path,strerror(errno));
  291.         } else {
  292.             apid[ret] = '\0';            /* null terminate */
  293.  
  294.             /* remove CR and LF chars */
  295.             p = apid + ret - 1;            /* point to last char */
  296.             while ((*p == 0x0d) || (*p == 0x0a)) {
  297.                 *p-- = '\0';            /* replace CR or LF with null */
  298.                 if (p == apid) break;    /* back to the beginning */
  299.             }
  300.  
  301.             pid = atoi(apid);            /* ascii -> number */
  302.         }
  303.     } else {
  304.         uulog(LOG_ERR,"bad size (%lu) for lock file %s",sbuf.st_size,path);
  305.         ++error;
  306.     }
  307.     close(fd);                            /* close file */
  308.     if (error) return(-1);                /* fail */
  309.  
  310.     if (ret_pid != NULL) {
  311.         *ret_pid = pid;                    /* return the pid */
  312.     }
  313.  
  314.     uulog(LOG_INFO,"lock file %s contains pid %d",path,pid);
  315.     if (pid < 1) return(-1);            /* fail */
  316.     ret = kill(pid,0);                    /* is pid valid? */
  317.     if ((ret == -1) && (errno == ESRCH)) 
  318.         return(1);                        /* pid doesn't exist */
  319.     return(0);                            /* pid exists */
  320. }
  321.  
  322. /*
  323.     open (and possibly create) the file whose pathname 
  324.     is specified and write the specified pid to it.
  325.  
  326.     returns 0 on success, non-zero on failure.
  327. */
  328. int write_pidfile(char *path,int flags,mode_t mode,pid_t pid)
  329. {
  330.     extern int errno;
  331.     int fd;                                /* fd for file */
  332. #ifdef ASCIIPID
  333.     char apid[12];                        /* ascii pid */
  334.     int len;                            /* gp length var */
  335. #endif
  336.  
  337.     fd = open(path,flags,mode);            /* open/creat file */
  338.     if (fd < 0) {
  339.         uulog(LOG_ERR,"open(%s,...) error: %s",path,strerror(errno));
  340.         return(fd);                        /* fail */
  341.     }
  342. #ifdef ASCIIPID
  343.     sprintf(apid,"% 10d\n",pid);
  344.     len = strlen(apid);                    /* length of pid string */
  345.     if (write(fd,apid,len) != len) {
  346. #else
  347.     if (write(fd,&pid,sizeof(pid)) != sizeof(pid)) {
  348. #endif
  349.         uulog(LOG_ERR,"error writing pid to %s: %s",path,strerror(errno));
  350.         close(fd);                        /* close file */
  351.         unlink(path);                    /* remove it */
  352.         return(1);                        /* fail */
  353.     }
  354.     close(fd);                            /* close file */
  355.     chmod(path,mode);                    /* set file mode */
  356.     return(0);                            /* success */
  357. }
  358.  
  359. /*
  360.     write our printf-style arguments to either:
  361.  
  362.         1.  syslog()
  363.     or    2.  the debug log
  364.  
  365.     the 'level' parameter is one of LOG_ERR, LOG_INFO, etc...
  366.  
  367.     returns nothing.
  368. */
  369. void uulog(int level,char *fmt, ...)
  370. {
  371.     static char str[1024];
  372.     va_list args;
  373.  
  374.     /* sanity checks */
  375.     if (fmt == NULL) return;
  376.  
  377.     va_start(args,fmt);
  378.     vsnprintf(str,sizeof(str),fmt,args);
  379.  
  380. #ifdef UULOG_SYSLOG
  381.     syslog(level,"%s",str);
  382. #else
  383.     if (level == LOG_ERR) {
  384.         debug(DBG_ERR,"%s",str);
  385.     } else if (level == LOG_INFO) {
  386.         debug(DBG_INF,"%s",str);
  387.     } else {
  388.         debug(DBG_VINF,"%s",str);
  389.     }
  390. #endif    /* UULOG_SYSLOG */
  391.  
  392.     va_end(args);
  393. }
  394.