1/*++ 2/* NAME 3/* dot_lockfile 3 4/* SUMMARY 5/* dotlock file management 6/* SYNOPSIS 7/* #include <dot_lockfile.h> 8/* 9/* int dot_lockfile(path, why) 10/* const char *path; 11/* VSTRING *why; 12/* 13/* void dot_unlockfile(path) 14/* const char *path; 15/* DESCRIPTION 16/* dot_lockfile() constructs a lock file name by appending ".lock" to 17/* \fIpath\fR and creates the named file exclusively. It tries several 18/* times and attempts to break stale locks. A negative result value 19/* means no lock file could be created. 20/* 21/* dot_unlockfile() attempts to remove the lock file created by 22/* dot_lockfile(). The operation always succeeds, and therefore 23/* it preserves the errno value. 24/* 25/* Arguments: 26/* .IP path 27/* Name of the file to be locked or unlocked. 28/* .IP why 29/* A null pointer, or storage for the reason why a lock file could 30/* not be created. 31/* DIAGNOSTICS 32/* dot_lockfile() returns 0 upon success. In case of failure, the 33/* result is -1, and the errno variable is set appropriately: 34/* EEXIST when a "fresh" lock file already exists; other values as 35/* appropriate. 36/* CONFIGURATION PARAMETERS 37/* deliver_lock_attempts, how many times to try to create a lock 38/* deliver_lock_delay, how long to wait between attempts 39/* stale_lock_time, when to break a stale lock 40/* LICENSE 41/* .ad 42/* .fi 43/* The Secure Mailer license must be distributed with this software. 44/* AUTHOR(S) 45/* Wietse Venema 46/* IBM T.J. Watson Research 47/* P.O. Box 704 48/* Yorktown Heights, NY 10598, USA 49/*--*/ 50 51/* System library. */ 52 53#include "sys_defs.h" 54#include <sys/stat.h> 55#include <stdlib.h> 56#include <unistd.h> 57#include <fcntl.h> 58#include <errno.h> 59#include <time.h> 60 61/* Utility library. */ 62 63#include <vstring.h> 64#include <stringops.h> 65#include <mymalloc.h> 66#include <iostuff.h> 67#include <warn_stat.h> 68 69/* Global library. */ 70 71#include "mail_params.h" 72#include "dot_lockfile.h" 73 74/* Application-specific. */ 75 76#define MILLION 1000000 77 78/* dot_lockfile - create user.lock file */ 79 80int dot_lockfile(const char *path, VSTRING *why) 81{ 82 char *lock_file; 83 int count; 84 struct stat st; 85 int fd; 86 int status = -1; 87 88 lock_file = concatenate(path, ".lock", (char *) 0); 89 90 for (count = 1; /* void */ ; count++) { 91 92 /* 93 * Attempt to create the lock. This code relies on O_EXCL | O_CREAT 94 * to not follow symlinks. With NFS file systems this operation can 95 * at the same time succeed and fail with errno of EEXIST. 96 */ 97 if ((fd = open(lock_file, O_WRONLY | O_EXCL | O_CREAT, 0)) >= 0) { 98 close(fd); 99 status = 0; 100 break; 101 } 102 if (count >= var_flock_tries) 103 break; 104 105 /* 106 * We can deal only with "file exists" errors. Any other error means 107 * we better give up trying. 108 */ 109 if (errno != EEXIST) 110 break; 111 112 /* 113 * Break the lock when it is too old. Give up when we are unable to 114 * remove a stale lock. 115 */ 116 if (stat(lock_file, &st) == 0) 117 if (time((time_t *) 0) > st.st_ctime + var_flock_stale) 118 if (unlink(lock_file) < 0) 119 if (errno != ENOENT) 120 break; 121 122 rand_sleep(var_flock_delay * MILLION, var_flock_delay * MILLION / 2); 123 } 124 if (status && why) 125 vstring_sprintf(why, "unable to create lock file %s: %m", lock_file); 126 127 myfree(lock_file); 128 return (status); 129} 130 131/* dot_unlockfile - remove .lock file */ 132 133void dot_unlockfile(const char *path) 134{ 135 char *lock_file; 136 int saved_errno = errno; 137 138 lock_file = concatenate(path, ".lock", (char *) 0); 139 (void) unlink(lock_file); 140 myfree(lock_file); 141 errno = saved_errno; 142} 143 144#ifdef TEST 145 146 /* 147 * Test program for setting a .lock file. 148 * 149 * Usage: dot_lockfile filename 150 * 151 * Creates filename.lock and removes it. 152 */ 153#include <msg.h> 154#include <vstream.h> 155#include <msg_vstream.h> 156#include <mail_conf.h> 157 158int main(int argc, char **argv) 159{ 160 VSTRING *why = vstring_alloc(100); 161 162 msg_vstream_init(argv[0], VSTREAM_ERR); 163 if (argc != 2) 164 msg_fatal("usage: %s file-to-be-locked", argv[0]); 165 mail_conf_read(); 166 if (dot_lockfile(argv[1], why) < 0) 167 msg_fatal("%s", vstring_str(why)); 168 dot_unlockfile(argv[1]); 169 vstring_free(why); 170 return (0); 171} 172 173#endif 174