2 ** Copyright 2006 Double Precision, Inc. See COPYING for
3 ** distribution information.
9 #include "../numlib/numlib.h"
16 #include <sys/types.h>
23 struct ll_mail
*ll_mail_alloc(const char *filename
)
25 struct ll_mail
*p
=(struct ll_mail
*)malloc(sizeof(struct ll_mail
));
30 if ((p
->file
=strdup(filename
)) == NULL
)
47 ** For extra credit, we mark our territory.
50 static void getid(char *idbuf
)
52 libmail_str_pid_t(getpid(), idbuf
);
59 idbuf
[IDBUFSIZE
-NUMBUFSIZE
-10]=0;
61 if (gethostname(idbuf
, IDBUFSIZE
-NUMBUFSIZE
-10) < 0)
62 strcpy(idbuf
, "localhost");
65 static int writeid(char *idbuf
, int fd
)
71 int n
=write(fd
, idbuf
, l
);
82 static int readid(char *p
, int fd
)
103 static pid_t
getpidid(char *idbuf
, char *myidbuf
)
107 if ((idbuf
=strchr(idbuf
, ':')) == NULL
||
108 (myidbuf
=strchr(myidbuf
, ':')) == NULL
||
109 strcmp(idbuf
, myidbuf
))
115 int ll_mail_lock(struct ll_mail
*p
)
117 struct stat stat_buf
;
118 char idbuf
[IDBUFSIZE
];
119 char idbuf2
[IDBUFSIZE
];
121 char fn
[NUMBUFSIZE
*2 + 20];
127 if (p
->cclientfd
>= 0)
130 if (stat(p
->file
, &stat_buf
) < 0)
133 if (snprintf(fn
, sizeof(fn
), "/tmp/.%lx.%lx",
134 (unsigned long)stat_buf
.st_dev
,
135 (unsigned long)stat_buf
.st_ino
) < 0)
141 if ((f
=strdup(fn
)) == NULL
)
144 /* We do things a bit differently. First, try O_EXCL */
146 if ((fd
=open(f
, O_RDWR
|O_CREAT
|O_EXCL
, 0644)) >= 0)
148 struct stat stat_buf2
;
150 if (ll_lockfd(fd
, ll_writelock
, ll_whence_start
, 0) < 0 ||
151 fcntl(fd
, F_SETFD
, FD_CLOEXEC
) < 0 ||
152 writeid(idbuf
, fd
) < 0)
154 /* This shouldn't happen */
161 /* Rare race condition: */
163 if (fstat(fd
, &stat_buf
) < 0 ||
164 lstat(f
, &stat_buf2
) < 0 ||
165 stat_buf
.st_dev
!= stat_buf2
.st_dev
||
166 stat_buf
.st_ino
!= stat_buf2
.st_ino
)
180 ** An existing lockfile. See if it's tagged with another
181 ** pid on this server, which no longer exists.
184 if ((fd
=open(f
, O_RDONLY
)) >= 0)
188 if (readid(idbuf2
, fd
) == 0 &&
189 (p
=getpidid(idbuf2
, idbuf
)) != 0 &&
190 kill(p
, 0) < 0 && errno
== ESRCH
)
194 unlink(f
); /* Don't try again right away */
199 /* If we can't lock, someone must have it open, game over. */
201 if (p
== getpid() /* It's us! */
203 || ll_lockfd(fd
, ll_readlock
, ll_whence_start
, 0) < 0)
214 /* Stale 0-length lockfiles are blown away after 5 mins */
216 if (lstat(f
, &stat_buf
) == 0 && stat_buf
.st_size
== 0 &&
217 stat_buf
.st_mtime
+ 300 < time(NULL
))
230 /* Try to create a dot-lock */
232 static int try_dotlock(const char *tmpfile
,
236 static int try_mail_dotlock(const char *dotlock
, char *idbuf
)
238 char timebuf
[NUMBUFSIZE
];
239 char pidbuf
[NUMBUFSIZE
];
243 libmail_str_time_t(time(NULL
), timebuf
);
244 libmail_str_pid_t(getpid(), pidbuf
);
246 tmpname
=malloc(strlen(dotlock
) + strlen(timebuf
) + strlen(pidbuf
) +
252 strcpy(tmpname
, dotlock
);
253 strcat(tmpname
, ".");
254 strcat(tmpname
, timebuf
);
255 strcat(tmpname
, ".");
256 strcat(tmpname
, pidbuf
);
257 strcat(tmpname
, ".");
258 strcat(tmpname
, strchr(idbuf
, ':')+1);
260 rc
=try_dotlock(tmpname
, dotlock
, idbuf
);
265 static int try_dotlock(const char *tmpname
,
269 struct stat stat_buf
;
273 fd
=open(tmpname
, O_RDWR
| O_CREAT
, 0644);
278 if (writeid(idbuf
, fd
))
286 if (link(tmpname
, dotlock
) < 0 || stat(tmpname
, &stat_buf
) ||
287 stat_buf
.st_nlink
!= 2)
299 static void dotlock_exists(const char *dotlock
, char *myidbuf
,
302 char idbuf
[IDBUFSIZE
];
303 struct stat stat_buf
;
306 if ((fd
=open(dotlock
, O_RDONLY
)) >= 0)
311 ** Where the locking process is on the same server,
312 ** the decision is easy: does the process still exist,
316 if (readid(idbuf
, fd
) == 0 && (p
=getpidid(idbuf
, myidbuf
)))
318 if (p
== getpid() /* Possibly recycled PID */
319 || (kill(p
, 0) < 0 && errno
== ESRCH
))
322 if (unlink(dotlock
) == 0)
329 else if (timeout
> 0 && fstat(fd
, &stat_buf
) >= 0 &&
330 stat_buf
.st_mtime
< time(NULL
) - timeout
)
334 if (unlink(dotlock
) == 0)
347 static int ll_mail_open_do(struct ll_mail
*p
, int ro
)
350 char myidbuf
[IDBUFSIZE
];
356 if (p
->dotlock
) /* Already locked */
358 fd
=open(p
->file
, ro
? O_RDONLY
:O_RDWR
);
361 (ll_lockfd(fd
, ro
? ll_readlock
:ll_writelock
, 0, 0) < 0 ||
362 fcntl(fd
, F_SETFD
, FD_CLOEXEC
) < 0))
370 if ((dotlock
=malloc(strlen(p
->file
)+sizeof(".lock"))) == NULL
)
373 strcat(strcpy(dotlock
, p
->file
), ".lock");
375 if (try_mail_dotlock(dotlock
, myidbuf
) == 0)
377 fd
=open(p
->file
, ro
? O_RDONLY
:O_RDWR
);
380 (ll_lockfd(fd
, ro
? ll_readlock
:ll_writelock
, 0, 0) ||
381 fcntl(fd
, F_SETFD
, FD_CLOEXEC
) < 0))
394 ** Last fallback: for EEXIST, a read-only lock should suffice
395 ** In all other instances, we'll fallback to read/write or read-only
396 ** flock as last resort.
399 if ((errno
== EEXIST
&& ro
) || errno
== EPERM
|| errno
== EACCES
)
401 fd
=open(p
->file
, ro
? O_RDONLY
:O_RDWR
);
405 if (ll_lockfd(fd
, ro
? ll_readlock
:ll_writelock
,
407 fcntl(fd
, F_SETFD
, FD_CLOEXEC
) == 0)
417 ** If try_dotlock blew up for anything other than EEXIST, we don't
418 ** know what the deal is, so punt.
421 if (save_errno
!= EEXIST
)
427 dotlock_exists(dotlock
, myidbuf
, 300);
432 int ll_mail_open_ro(struct ll_mail
*p
)
434 return ll_mail_open_do(p
, 1);
437 int ll_mail_open(struct ll_mail
*p
)
439 return ll_mail_open_do(p
, 0);
442 void ll_mail_free(struct ll_mail
*p
)
444 char myid
[IDBUFSIZE
];
445 char idbuf
[IDBUFSIZE
];
449 if (p
->cclientfd
>= 0)
451 if (lseek(p
->cclientfd
, 0L, SEEK_SET
) == 0 &&
452 readid(idbuf
, p
->cclientfd
) == 0 &&
453 strcmp(myid
, idbuf
) == 0)
455 if (ftruncate(p
->cclientfd
, 0) >= 0)
456 unlink(p
->cclientfile
);
459 free(p
->cclientfile
);
464 int fd
=open(p
->dotlock
, O_RDONLY
);
468 if (readid(idbuf
, fd
) == 0 &&
469 strcmp(myid
, idbuf
) == 0)
487 int ll_dotlock(const char *dotlock
, const char *tmpfile
,
490 char myidbuf
[IDBUFSIZE
];
494 if (try_dotlock(tmpfile
, dotlock
, myidbuf
))
497 dotlock_exists(dotlock
, myidbuf
, timeout
);