Blame view

libmailutils/base/locker.c 19.6 KB
1
/* GNU Mailutils -- a suite of utilities for electronic mail
2 3
   Copyright (C) 1999, 2000, 2001, 2005, 2006, 2007, 2008, 2010 Free
   Software Foundation, Inc.
4

5 6 7
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
8
   version 3 of the License, or (at your option) any later version.
9

10
   This library is distributed in the hope that it will be useful,
11
   but WITHOUT ANY WARRANTY; without even the implied warranty of
12 13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
14

15
   You should have received a copy of the GNU Lesser General
16 17
   Public License along with this library.  If not, see 
   <http://www.gnu.org/licenses/>. */
18

19 20 21 22
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

23
#include <assert.h>
24
#include <errno.h>
25 26
#include <fcntl.h>
#include <limits.h>
27 28 29 30
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
31
#include <time.h>
32
#include <unistd.h>
33
#include <fcntl.h>
34
#include <utime.h>
35

36
#include <sys/param.h>
37
#include <sys/stat.h>
38
#include <sys/types.h>
39
#include <sys/wait.h>
40

41
#include <mailutils/errno.h>
42
#include <mailutils/locker.h>
43
#include <mailutils/util.h>
44

45
#define LOCKFILE_ATTR           0644
46

Alain Magloire authored
47
/* First draft by Brian Edmond. */
48
/* For subsequent modifications, see the GNU mailutils ChangeLog. */
49

50
struct _mu_locker
51
{
Sergey Poznyakoff authored
52 53
  unsigned refcnt;             /* Number of times mu_locker_lock was called */
  enum mu_locker_mode mode;    /* Current locking mode (if refcnt > 0) */
54 55

  char *file;
56
  int flags;
57 58 59
  int expire_time;
  int retries;
  int retry_sleep;
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74

  union lock_data {
    struct {
      char *dotlock;
      char *nfslock;
    } dot;
    
    struct {
      char *name;
    } external;
    
    struct {
      int fd;
    } kernel;
  } data;
75 76
};

Sergey Poznyakoff authored
77
#define MU_LOCKER_TYPE(l) MU_LOCKER_FLAG_TO_TYPE((l)->flags)
78

Sergey Poznyakoff authored
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
struct locker_tab
{
  int (*init) (mu_locker_t);
  void (*destroy) (mu_locker_t);
  int (*prelock) (mu_locker_t); 
  int (*lock) (mu_locker_t, enum mu_locker_mode);
  int (*unlock) (mu_locker_t);
};

static int init_dotlock (mu_locker_t);
static void destroy_dotlock (mu_locker_t);
static int lock_dotlock (mu_locker_t, enum mu_locker_mode);
static int unlock_dotlock (mu_locker_t);

static int init_external (mu_locker_t);
static void destroy_external (mu_locker_t);
static int lock_external (mu_locker_t, enum mu_locker_mode);
static int unlock_external (mu_locker_t);

static int init_kernel (mu_locker_t);
static int lock_kernel (mu_locker_t, enum mu_locker_mode);
static int unlock_kernel (mu_locker_t);

static int prelock_common (mu_locker_t);

static struct locker_tab locker_tab[] = {
Sergey Poznyakoff authored
105
  /* MU_LOCKER_TYPE_DOTLOCK */
Sergey Poznyakoff authored
106 107
  { init_dotlock, destroy_dotlock, prelock_common,
    lock_dotlock, unlock_dotlock },
Sergey Poznyakoff authored
108
  /* MU_LOCKER_TYPE_EXTERNAL */
Sergey Poznyakoff authored
109 110
  { init_external, destroy_external, prelock_common,
    lock_external, unlock_external },
Sergey Poznyakoff authored
111
  /* MU_LOCKER_TYPE_KERNEL */
Sergey Poznyakoff authored
112
  { init_kernel, NULL, NULL, lock_kernel, unlock_kernel },
Sergey Poznyakoff authored
113
  /* MU_LOCKER_TYPE_NULL */
Sergey Poznyakoff authored
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
  { NULL, NULL, NULL, NULL, NULL }
};

#define MU_LOCKER_NTYPES (sizeof (locker_tab) / sizeof (locker_tab[0]))


static int
stat_check (const char *file, int fd, int links)
{
  struct stat fn_stat;
  struct stat fd_stat;
  int err = 0;
  int localfd = -1;

  if (fd == -1)
    {
      localfd = open (file, O_RDONLY);
      
      if (localfd == -1)
	return errno;
      fd = localfd;
    }

  /* We should always be able to stat a valid fd, so this
     is an error condition. */
  if (lstat (file, &fn_stat) || fstat (fd, &fd_stat))
    err = errno;
  else
    {
      /* If the link and stat don't report the same info, or the
         file is a symlink, fail the locking. */
#define CHK(X) if(X) err = EINVAL

      CHK (!S_ISREG (fn_stat.st_mode));
      CHK (!S_ISREG (fd_stat.st_mode));
      CHK (fn_stat.st_nlink != links);
      CHK (fn_stat.st_dev != fd_stat.st_dev);
      CHK (fn_stat.st_ino != fd_stat.st_ino);
      CHK (fn_stat.st_mode != fd_stat.st_mode);
      CHK (fn_stat.st_nlink != fd_stat.st_nlink);
      CHK (fn_stat.st_uid != fd_stat.st_uid);
      CHK (fn_stat.st_gid != fd_stat.st_gid);
      CHK (fn_stat.st_rdev != fd_stat.st_rdev);

#undef CHK
    }
  if (localfd != -1)
    close (localfd);

  return err;
}

static int
check_file_permissions (const char *file)
{
  int fd = -1;
  int err = 0;

  if ((fd = open (file, O_RDONLY)) == -1)
    return errno == ENOENT ? 0 : errno;

  err = stat_check (file, fd, 1);
  close (fd);
  fd = -1;
  if (err)
    {
      if (err == EINVAL)
	err = MU_ERR_LOCK_BAD_FILE;
      return err;
    }

  return 0;
}

static int
prelock_common (mu_locker_t locker)
{
  /* Check if we are trying to lock a regular file, with a link count
     of 1, that we have permission to read, etc., or don't lock it. */
  return check_file_permissions (locker->file);
}


197 198 199 200 201
static int mu_locker_default_flags = MU_LOCKER_DEFAULT;
static time_t mu_locker_retry_timeout = MU_LOCKER_RETRY_SLEEP;
static size_t mu_locker_retry_count = MU_LOCKER_RETRIES;
static time_t mu_locker_expire_timeout = MU_LOCKER_EXPIRE_TIME;
static char *mu_locker_external_program = NULL;
202

203
int
204
mu_locker_set_default_flags (int flags, enum mu_locker_set_mode mode)
205
{
206 207
  switch (mode)
    {
208
    case mu_locker_assign:
209 210 211 212 213 214
      mu_locker_default_flags = flags;
      break;

    case mu_locker_set_bit:
      mu_locker_default_flags |= flags;
      break;
215

216 217 218
    case mu_locker_clear_bit:
      mu_locker_default_flags &= ~flags;
      break;
219

220 221 222
    default:
      return EINVAL;
    }
223 224 225
  return 0;
}

226
void
227
mu_locker_set_default_retry_timeout (time_t to)
228 229 230 231 232
{
  mu_locker_retry_timeout = to;
}

void
233
mu_locker_set_default_retry_count (size_t n)
234 235 236 237 238
{
  mu_locker_retry_count = n;
}

void
239
mu_locker_set_default_expire_timeout (time_t t)
240 241 242 243 244
{
  mu_locker_expire_timeout = t;
}

void
245
mu_locker_set_default_external_program (char *path)
246 247 248 249 250
{
  free (mu_locker_external_program);
  mu_locker_external_program = strdup (path);
}

251
int
Sergey Poznyakoff authored
252 253
mu_locker_mod_flags (mu_locker_t locker, int flags,
		     enum mu_locker_set_mode mode)
254
{
Sergey Poznyakoff authored
255
  unsigned otype, ntype;
Sergey Poznyakoff authored
256
  int new_flags;
Sergey Poznyakoff authored
257 258 259
  
  if (!locker)
    return MU_ERR_LOCKER_NULL;
Sergey Poznyakoff authored
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278

  switch (mode)
    {
    case mu_locker_assign:
      new_flags = flags;
      break;

    case mu_locker_set_bit:
      new_flags = locker->flags | flags;
      break;

    case mu_locker_clear_bit:
      new_flags = locker->flags & ~flags;
      break;

    default:
      return EINVAL;
    }

Sergey Poznyakoff authored
279 280 281
  otype = MU_LOCKER_TYPE (locker);
  if (otype >= MU_LOCKER_NTYPES)
    return EINVAL;
Sergey Poznyakoff authored
282
  ntype = MU_LOCKER_FLAG_TO_TYPE (new_flags);
Sergey Poznyakoff authored
283
  if (ntype >= MU_LOCKER_NTYPES)
284
    return EINVAL;
285

Sergey Poznyakoff authored
286
  if (ntype != otype)
287
    {
Sergey Poznyakoff authored
288 289 290 291
      int rc;
      
      if (locker_tab[otype].destroy)
	locker_tab[otype].destroy (locker);
Sergey Poznyakoff authored
292 293
      locker->flags = new_flags;
      if (locker_tab[ntype].init)
294
	{
Sergey Poznyakoff authored
295
	  rc = locker_tab[ntype].init (locker);
Sergey Poznyakoff authored
296 297 298
	  if (rc)
	    locker->flags = MU_LOCKER_NULL;
	  return rc;
299 300
	}
    }
Sergey Poznyakoff authored
301
  else
Sergey Poznyakoff authored
302
    locker->flags = new_flags;
303 304 305 306

  return 0;
}

307
int
Sergey Poznyakoff authored
308 309 310 311 312 313
mu_locker_set_flags (mu_locker_t locker, int flags)
{
  return mu_locker_mod_flags (locker, flags, mu_locker_assign);
}

int
314
mu_locker_set_expire_time (mu_locker_t locker, int etime)
315
{
316 317 318
  if (!locker)
    return MU_ERR_LOCKER_NULL;

319
  if (etime <= 0)
320 321
    return EINVAL;

322
  locker->expire_time = etime;
323 324 325 326 327

  return 0;
}

int
328
mu_locker_set_retries (mu_locker_t locker, int retries)
329 330 331 332
{
  if (!locker)
    return MU_ERR_LOCKER_NULL;

333
  if (retries <= 0)
334 335 336 337 338 339 340 341
    return EINVAL;

  locker->retries = retries;

  return 0;
}

int
342
mu_locker_set_retry_sleep (mu_locker_t locker, int retry_sleep)
343 344 345 346
{
  if (!locker)
    return MU_ERR_LOCKER_NULL;

347
  if (retry_sleep <= 0)
348 349 350 351 352 353 354
    return EINVAL;

  locker->retry_sleep = retry_sleep;

  return 0;
}

355
int
356
mu_locker_set_external (mu_locker_t locker, const char* program)
357 358 359 360 361
{
  char* p = NULL;

  if (!locker)
    return MU_ERR_LOCKER_NULL;
Sergey Poznyakoff authored
362
  if (MU_LOCKER_TYPE (locker) != MU_LOCKER_TYPE_EXTERNAL)
363 364
    return EINVAL;
    
365
  /* program can be NULL */
366 367 368 369 370
  if (program != 0)
    {
      p = strdup (program);
      if (!p)
	return ENOMEM;
371 372
  }

373 374
  free (locker->data.external.name);
  locker->data.external.name = p;
375 376 377 378

  return 0;
}

379
int
380
mu_locker_get_flags (mu_locker_t locker, int *flags)
381 382 383 384
{
  if (!locker)
    return MU_ERR_LOCKER_NULL;

Sergey Poznyakoff authored
385
  if (!flags)
386 387 388 389 390 391 392 393
    return EINVAL;

  *flags = locker->flags;

  return 0;
}

int
394
mu_locker_get_expire_time (mu_locker_t locker, int *ptime)
395 396 397 398
{
  if (!locker)
    return MU_ERR_LOCKER_NULL;

399
  if (!ptime)
400 401
    return EINVAL;

402
  *ptime = locker->expire_time;
403 404 405 406 407

  return 0;
}

int
408
mu_locker_get_retries (mu_locker_t locker, int *retries)
409 410 411 412
{
  if (!locker)
    return MU_ERR_LOCKER_NULL;

413
  if (!retries)
414 415 416 417 418 419 420 421
    return EINVAL;

  *retries = locker->retries;

  return 0;
}

int
422
mu_locker_get_retry_sleep (mu_locker_t locker, int *retry_sleep)
423 424 425 426
{
  if (!locker)
    return MU_ERR_LOCKER_NULL;

427
  if (!retry_sleep)
428 429 430 431 432 433 434 435
    return EINVAL;

  *retry_sleep = locker->retry_sleep;

  return 0;
}

int
Sergey Poznyakoff authored
436
mu_locker_create (mu_locker_t *plocker, const char *fname, int flags)
437
{
Sergey Poznyakoff authored
438 439
  unsigned type;
  mu_locker_t l;
440
  char *filename;
Sergey Poznyakoff authored
441
  int err = 0;
442

Sergey Poznyakoff authored
443 444 445 446
  if (plocker == NULL)
    return MU_ERR_OUT_PTR_NULL;

  if (fname == NULL)
447
    return EINVAL;
448

449
  if ((err = mu_unroll_symlink (fname, &filename)))
Sergey Poznyakoff authored
450 451 452 453 454
    return err;

  l = calloc (1, sizeof (*l));

  if (l == NULL)
455 456 457 458 459 460
    {
      free (filename);
      return ENOMEM;
    }
  
  l->file = filename;
461
  
Sergey Poznyakoff authored
462
  if (l->file == NULL)
Alain Magloire authored
463
    {
Sergey Poznyakoff authored
464 465
      free (l);
      return ENOMEM;
Alain Magloire authored
466 467
    }

Sergey Poznyakoff authored
468 469 470 471 472 473 474 475 476 477 478 479 480 481
  if (strcmp (filename, "/dev/null") == 0)
    l->flags = MU_LOCKER_NULL;
  else if (flags)
    l->flags = flags;
  else
    l->flags = mu_locker_default_flags;

  l->expire_time = mu_locker_expire_timeout;
  l->retries = mu_locker_retry_count;
  l->retry_sleep = mu_locker_retry_timeout;

  type = MU_LOCKER_TYPE (l);

  if (type >= MU_LOCKER_NTYPES)
482
    {
Sergey Poznyakoff authored
483 484
      free (l->file);
      return EINVAL;
485 486
    }

Sergey Poznyakoff authored
487 488 489
  /* Initialize locker-type-specific data */
  err = locker_tab[type].init ? locker_tab[type].init (l) : 0;
  if (err)
Alain Magloire authored
490
    {
Sergey Poznyakoff authored
491 492 493
      mu_locker_destroy (&l);
      return err;
    }
494

Sergey Poznyakoff authored
495
  *plocker = l;
496

Sergey Poznyakoff authored
497 498
  return 0;
}
499

Sergey Poznyakoff authored
500 501 502 503 504 505 506
void
mu_locker_destroy (mu_locker_t *plocker)
{
  if (plocker && *plocker)
    {
      unsigned type = MU_LOCKER_TYPE (*plocker);
      if (type < MU_LOCKER_NTYPES)
Alain Magloire authored
507
	{
Sergey Poznyakoff authored
508 509 510 511 512
	  if (locker_tab[type].destroy)
	    locker_tab[type].destroy (*plocker);
	  free ((*plocker)->file);
	  free (*plocker);
	  *plocker = NULL;
513
	}
Alain Magloire authored
514
    }
Sergey Poznyakoff authored
515
}
Alain Magloire authored
516

Sergey Poznyakoff authored
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
int
_mu_locker_lock (mu_locker_t lock, enum mu_locker_mode mode)
{
  int rc;
  unsigned type;
  unsigned retries = 1;
  
  if (lock == NULL || (type = MU_LOCKER_TYPE (lock)) >= MU_LOCKER_NTYPES)
    return EINVAL;

  if (locker_tab[type].prelock && (rc = locker_tab[type].prelock (lock)))
    return rc;
  
  /* Is the lock already applied? */
  if (lock->refcnt > 0)
532
    {
Sergey Poznyakoff authored
533 534 535
      lock->refcnt++;
      if (mode == lock->mode)
	return 0;
536 537
    }

Sergey Poznyakoff authored
538 539 540 541 542 543
  lock->mode = mode;

  if (lock->flags & MU_LOCKER_RETRY)
    retries = lock->retries;

  if (locker_tab[type].lock)
544
    {
Sergey Poznyakoff authored
545 546 547 548 549 550 551 552
      while (retries--)
	{
	  rc = locker_tab[type].lock (lock, mode);
	  if (rc == EAGAIN && retries)
	    {
	      sleep (lock->retry_sleep);
	      continue;
	    }
553

Sergey Poznyakoff authored
554 555
	  if (rc == 0)
	    lock->refcnt++;
556

Sergey Poznyakoff authored
557 558 559 560 561 562 563
	  break;
	}
    }
  else
    rc = 0;
  
  return rc;
564 565 566
}

int
Sergey Poznyakoff authored
567
mu_locker_lock (mu_locker_t lock)
568
{
Sergey Poznyakoff authored
569
  return _mu_locker_lock (lock, mu_lck_exc);
570 571 572
}

int
573
mu_locker_unlock (mu_locker_t lock)
574
{
575
  int rc = 0;
Sergey Poznyakoff authored
576 577
  unsigned type;
  
578 579 580
  if (!lock)
    return MU_ERR_LOCKER_NULL;

581
  if (lock->refcnt == 0)
582 583
    return MU_ERR_LOCK_NOT_HELD;

584 585
  if ((rc = check_file_permissions (lock->file)))
    return rc;
586

Sergey Poznyakoff authored
587 588
  if (--lock->refcnt > 0)
    return 0;
589

Sergey Poznyakoff authored
590 591 592 593 594 595
  type = MU_LOCKER_TYPE (lock);
  if (locker_tab[type].unlock)
    rc = locker_tab[type].unlock (lock);
  else
    rc = 0;
  
596
  return rc;
597
}
598

599
int
600
mu_locker_remove_lock (mu_locker_t lock)
601 602 603 604
{
  if (!lock)
    return MU_ERR_LOCKER_NULL;

605 606
  /* Force the reference count to 1 to unlock the file. */
  lock->refcnt = 1;
Sergey Poznyakoff authored
607
  return mu_locker_unlock (lock);
608 609
}

Sergey Poznyakoff authored
610 611 612

#define DOTLOCK_SUFFIX ".lock"

613 614
/* expire a stale lock (if MU_LOCKER_PID or MU_LOCKER_TIME) */
static void
615
expire_stale_lock (mu_locker_t lock)
616 617
{
  int stale = 0;
618
  int fd = open (lock->data.dot.dotlock, O_RDONLY);
619 620 621 622 623
  if (fd == -1)
    return;

  /* Check to see if this process is still running.  */
  if (lock->flags & MU_LOCKER_PID)
624
    {
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
      char buf[16];
      pid_t pid;
      int nread = read (fd, buf, sizeof (buf) - 1);
      if (nread > 0)
	{
	  buf[nread] = '\0';
	  pid = strtol (buf, NULL, 10);
	  if (pid > 0)
	    {
	      /* Process is gone so we try to remove the lock. */
	      if (kill (pid, 0) == -1)
		stale = 1;
	    }
	  else
	    stale = 1;		/* Corrupted file, remove the lock. */
	}
641
    }
642
  
643 644 645 646
  /* Check to see if the lock expired.  */
  if (lock->flags & MU_LOCKER_TIME)
    {
      struct stat stbuf;
647

648 649 650 651 652
      fstat (fd, &stbuf);
      /* The lock has expired. */
      if ((time (NULL) - stbuf.st_mtime) > lock->expire_time)
	stale = 1;
    }
653

654 655
  close (fd);
  if (stale)
656
    unlink (lock->data.dot.dotlock);
657 658 659
}

static int
Sergey Poznyakoff authored
660
init_dotlock (mu_locker_t locker)
661
{
Sergey Poznyakoff authored
662
  char *tmp, *p;
663

Sergey Poznyakoff authored
664 665 666 667 668 669 670 671
  /* Make sure the spool directory is writable */
  tmp = strdup (locker->file);
  if (!tmp)
    return ENOMEM;

  strcpy (tmp, locker->file);
  p = strrchr (tmp, '/');
  if (!p)
672
    {
Sergey Poznyakoff authored
673 674 675 676
      free (tmp);
      tmp = strdup (".");
      if (!tmp)
	return ENOMEM;
677
    }
678
  else
Sergey Poznyakoff authored
679
    *p = 0; 
680

Sergey Poznyakoff authored
681 682 683 684 685 686
  if (access (tmp, W_OK))
    {
      /* Fallback to kernel locking */
      free (tmp);
      return mu_locker_set_flags (locker,
			   MU_LOCKER_KERNEL|MU_LOCKER_OPTIONS(locker->flags));
687
    }
Sergey Poznyakoff authored
688 689 690 691 692 693 694 695 696 697
  
  free (tmp);
  
  locker->data.dot.dotlock = malloc (strlen (locker->file)
				     + sizeof (DOTLOCK_SUFFIX));
  
  if (!locker->data.dot.dotlock)
    return ENOMEM;
  strcpy (locker->data.dot.dotlock, locker->file);
  strcat (locker->data.dot.dotlock, DOTLOCK_SUFFIX);
698

Sergey Poznyakoff authored
699
  return 0;
700 701
}

Sergey Poznyakoff authored
702 703
static void
destroy_dotlock (mu_locker_t locker)
704
{
Sergey Poznyakoff authored
705
  free (locker->data.dot.dotlock);
Sergey Poznyakoff authored
706
  free (locker->data.dot.nfslock);
707 708
}

Sergey Poznyakoff authored
709 710
static int
lock_dotlock (mu_locker_t locker, enum mu_locker_mode mode)
711
{
712 713
  int rc;
  char *host = NULL;
714 715 716 717 718 719
  char pid[11];		/* 10 is strlen(2^32 = 4294967296) */
  char now[11];
  size_t sz = 0;
  int err = 0;
  int fd;
    
Sergey Poznyakoff authored
720
  if (locker->data.dot.nfslock)
721
    {
Sergey Poznyakoff authored
722 723
      unlink (locker->data.dot.nfslock);
      free (locker->data.dot.nfslock);
Sergey Poznyakoff authored
724
      locker->data.dot.nfslock = NULL;
725 726
    }

Sergey Poznyakoff authored
727
  expire_stale_lock (locker);
728 729 730

  /* build the NFS hitching-post to the lock file */

731 732 733
  rc = mu_get_host_name (&host);
  if (rc)
    return rc;
734

735
  snprintf (now, sizeof (now), "%lu", (unsigned long) time (0));
736 737
  now[sizeof (now) - 1] = 0;

738
  snprintf (pid, sizeof (pid), "%lu", (unsigned long) getpid ());
739 740
  pid[sizeof (pid) - 1] = 0;
		  
Sergey Poznyakoff authored
741
  sz = strlen (locker->file) + 1 /* "." */
742 743 744 745
    + strlen (pid) + 1 /* "." */
    + strlen (now) + 1 /* "." */
    + strlen (host) + 1;
  
Sergey Poznyakoff authored
746
  locker->data.dot.nfslock = malloc (sz);
747
  
Sergey Poznyakoff authored
748
  if (!locker->data.dot.nfslock)
749 750 751 752 753
    {
      free (host);
      return ENOMEM;
    }

Sergey Poznyakoff authored
754 755
  snprintf (locker->data.dot.nfslock, sz, "%s.%s.%s.%s",
	    locker->file, pid, now, host);
756
  free (host);
757
  
Sergey Poznyakoff authored
758
  fd = open (locker->data.dot.nfslock,
759 760 761 762 763 764 765 766 767 768 769
	     O_WRONLY | O_CREAT | O_EXCL, LOCKFILE_ATTR);
  if (fd == -1)
    {
      if (errno == EEXIST)
	return EAGAIN;
      else
	return errno;
    }
  close (fd);
  
  /* Try to link to the lockfile. */
Sergey Poznyakoff authored
770
  if (link (locker->data.dot.nfslock, locker->data.dot.dotlock) == -1)
771
    {
Sergey Poznyakoff authored
772
      unlink (locker->data.dot.nfslock);
773 774 775 776 777
      if (errno == EEXIST)
	return MU_ERR_LOCK_CONFLICT;
      return errno;
    }

Sergey Poznyakoff authored
778
  if ((fd = open (locker->data.dot.dotlock, O_RDWR)) == -1)
779
    {
Sergey Poznyakoff authored
780
      unlink (locker->data.dot.nfslock);
781 782 783
      return errno;
    }
  
Sergey Poznyakoff authored
784
  err = stat_check (locker->data.dot.nfslock, fd, 2);
785 786
  if (err)
    {
Sergey Poznyakoff authored
787
      unlink (locker->data.dot.nfslock);
788 789 790 791 792
      if (err == EINVAL)
	return MU_ERR_LOCK_BAD_LOCK;
      return errno;
    }

Sergey Poznyakoff authored
793
  unlink (locker->data.dot.nfslock);
794

Sergey Poznyakoff authored
795 796
  /* FIXME: If no errors, we have the lock. */
  assert (locker->refcnt == 0);
797

Sergey Poznyakoff authored
798
  if (locker->flags & MU_LOCKER_PID)
799 800 801 802 803
    {
      char buf[16];
      sprintf (buf, "%ld", (long) getpid ());
      write (fd, buf, strlen (buf));
    }
Sergey Poznyakoff authored
804
  close (fd);
805
  return 0;
Sergey Poznyakoff authored
806
}
807

Sergey Poznyakoff authored
808 809
static int
unlock_dotlock (mu_locker_t locker)
810
{
Sergey Poznyakoff authored
811
  if (unlink (locker->data.dot.dotlock) == -1)
812 813 814 815
    {
      int err = errno;
      if (err == ENOENT)
	{
Sergey Poznyakoff authored
816
	  locker->refcnt = 0; /*FIXME?*/
817 818 819 820 821 822 823 824 825
	  err = MU_ERR_LOCK_NOT_HELD;
	  return err;
	}
      return err;
    }
  return 0;
}

int
Sergey Poznyakoff authored
826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849
mu_locker_touchlock (mu_locker_t lock)
{
  if (!lock)
    return MU_ERR_LOCKER_NULL;

  if (MU_LOCKER_TYPE (lock) != MU_LOCKER_TYPE_DOTLOCK)
    return 0;
  
  if (lock->refcnt > 0)
    return utime (lock->data.dot.dotlock, NULL);

  return MU_ERR_LOCK_NOT_HELD;
}


/* Kernel locking */
static int
init_kernel (mu_locker_t locker)
{
  return 0;
}

static int
lock_kernel (mu_locker_t locker, enum mu_locker_mode mode)
850 851 852 853
{
  int fd;
  struct flock fl;

Sergey Poznyakoff authored
854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871
  switch (mode)
    {
    case mu_lck_shr:
    case mu_lck_opt:
      mode = O_RDONLY;
      fl.l_type = F_RDLCK;
      break;

    case mu_lck_exc:
      mode = O_RDWR;
      fl.l_type = F_WRLCK;
      break;

    default:
      return EINVAL;
    }
  
  fd = open (locker->file, O_RDWR);
872 873
  if (fd == -1)
    return errno;
Sergey Poznyakoff authored
874 875
  locker->data.kernel.fd = fd;
  
876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891
  fl.l_whence = SEEK_SET;
  fl.l_start = 0;
  fl.l_len = 0; /* Lock entire file */
  if (fcntl (fd, F_SETLK, &fl))
    {
#ifdef EACCESS      
      if (errno == EACCESS)
	return EAGAIN;
#endif
      if (errno == EAGAIN)
	return EAGAIN;
      return errno;
    }
  return 0;
}

Sergey Poznyakoff authored
892 893
static int
unlock_kernel (mu_locker_t locker)
894 895 896 897 898 899 900
{
  struct flock fl;

  fl.l_type = F_UNLCK;
  fl.l_whence = SEEK_SET;
  fl.l_start = 0;
  fl.l_len = 0; /* Unlock entire file */
Sergey Poznyakoff authored
901
  if (fcntl (locker->data.kernel.fd, F_SETLK, &fl))
902 903 904 905 906 907 908 909 910
    {
#ifdef EACCESS
      if (errno == EACCESS)
	return EAGAIN;
#endif
      if (errno == EAGAIN)
	return EAGAIN;
      return errno;
    }
Sergey Poznyakoff authored
911 912 913 914 915 916 917 918 919 920 921
  close (locker->data.kernel.fd);
  return 0;
}

static int
init_external (mu_locker_t locker)
{
  if (!(locker->data.external.name = strdup (mu_locker_external_program ?
					     mu_locker_external_program :
					     MU_LOCKER_EXTERNAL_PROGRAM)))
    return ENOMEM;
922 923 924
  return 0;
}

Sergey Poznyakoff authored
925 926 927 928 929 930
static void
destroy_external (mu_locker_t locker)
{
  free (locker->data.external.name);
}

931 932 933 934 935 936
/*
  Estimate 1 decimal digit per 3 bits, + 1 for round off.
*/
#define DEC_DIGS_PER_INT (sizeof(int) * 8 / 3 + 1)

static int
Sergey Poznyakoff authored
937
external_locker (mu_locker_t l, int lock)
938 939
{
  int err = 0;
940
  char *av[6];
941 942 943 944 945 946 947
  int ac = 0;
  char aforce[3 + DEC_DIGS_PER_INT + 1];
  char aretry[3 + DEC_DIGS_PER_INT + 1];
  int status = 0;

  assert (l);
  assert (l->flags & MU_LOCKER_EXTERNAL);
Sergey Poznyakoff authored
948
  /* FIXME */
949 950 951
  assert (lock == !l->refcnt);
  /* lock is true, refcnt is 0 or lock is false and refcnt is 1 */

952 953
  av[ac++] = l->data.external.name ?
                   l->data.external.name : MU_LOCKER_EXTERNAL_PROGRAM;
954 955 956 957 958 959 960

  if (l->flags & MU_LOCKER_TIME)
    {
      snprintf (aforce, sizeof (aforce), "-f%d", l->expire_time);
      aforce[sizeof (aforce) - 1] = 0;
      av[ac++] = aforce;
    }
961
  
962 963 964 965 966 967
  if (l->flags & MU_LOCKER_RETRY)
    {
      snprintf (aretry, sizeof (aretry), "-r%d", l->retries);
      aretry[sizeof (aretry) - 1] = 0;
      av[ac++] = aretry;
    }
968

Sergey Poznyakoff authored
969 970
  if (!lock)
    av[ac++] = "-u";
971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989

  av[ac++] = l->file;

  av[ac++] = NULL;

  if ((err = mu_spawnvp (av[0], av, &status)))
    return err;

  if (!WIFEXITED (status))
    {
      err = MU_ERR_LOCK_EXT_KILLED;
    }
  else
    {
      switch (WEXITSTATUS (status))
	{
	case 127:
	  err = MU_ERR_LOCK_EXT_FAIL;
	  break;
Sergey Poznyakoff authored
990
	  
991 992
	case MU_DL_EX_OK:
	  err = 0;
993
	  l->refcnt = lock;
994
	  break;
Sergey Poznyakoff authored
995
	  
996 997 998
	case MU_DL_EX_NEXIST:
	  err = MU_ERR_LOCK_NOT_HELD;
	  break;
Sergey Poznyakoff authored
999
	  
1000 1001 1002
	case MU_DL_EX_EXIST:
	  err = MU_ERR_LOCK_CONFLICT;
	  break;
Sergey Poznyakoff authored
1003
	  
1004 1005 1006
	case MU_DL_EX_PERM:
	  err = EPERM;
	  break;
Sergey Poznyakoff authored
1007
	  
1008 1009 1010 1011 1012 1013
	default:
	case MU_DL_EX_ERROR:
	  err = MU_ERR_LOCK_EXT_ERR;
	  break;
	}
    }
1014 1015 1016 1017

  return err;
}

Sergey Poznyakoff authored
1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
static int
lock_external (mu_locker_t locker, enum mu_locker_mode mode)
{
  return external_locker (locker, 1);
}

static int
unlock_external (mu_locker_t locker)
{
  return external_locker (locker, 0);
}