1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2016 Joyent, Inc.
  14  */
  15 
  16 /*
  17  * Acquire the specified kind of lock with the specified parameters. After
  18  * acquiring the lock, a byte will be written to stdout. The program will
  19  * then wait for a byte to be written to stdin before exiting.
  20  *
  21  * Usage: <posix|ofd|flock> <shared|exclusive> <path>
  22  */
  23 
  24 #include "util.h"
  25 #include <err.h>
  26 #include <errno.h>
  27 #include <fcntl.h>
  28 #include <stdio.h>
  29 #include <stdlib.h>
  30 #include <strings.h>
  31 #include <sys/file.h>
  32 #include <unistd.h>
  33 
  34 
  35 static  void    acq_fcntl(int, int, int);
  36 static  void    acq_flock(int fd, int mode);
  37 static  void    acq_run(int, lock_style_t, boolean_t);
  38 
  39 
  40 static void
  41 acq_fcntl(int fd, int cmd, int mode)
  42 {
  43         struct flock fl;
  44         int ret, i;
  45 
  46         /*
  47          * Acquire the lock, and then try reacquiring it several times. Once we
  48          * have acquired the lock, trying to acquire it again should succeed,
  49          * and shouldn't upgrade, downgrade or free the lock.
  50          */
  51         for (i = 0; i < 3; i++) {
  52                 flock_reinit(&fl, mode);
  53                 flock_log("Acquiring lock (fcntl)...\n");
  54                 ret = fcntl(fd, cmd, &fl);
  55                 if (ret == -1) {
  56                         err(EXIT_FAILURE, "fcntl failed");
  57                 }
  58         }
  59 
  60 
  61         /* Let the parent know we have the lock and wait */
  62         flock_log("Waiting (fcntl)...\n");
  63         flock_alert(1);
  64         flock_block(0);
  65 
  66         /* Now unlock */
  67         flock_reinit(&fl, F_UNLCK);
  68         flock_log("Releasing lock (fcntl)...\n");
  69         ret = fcntl(fd, cmd, &fl);
  70         if (ret == -1) {
  71                 err(EXIT_FAILURE, "fcntl failed");
  72         }
  73 }
  74 
  75 
  76 static void
  77 acq_flock(int fd, int mode)
  78 {
  79         int ret, i;
  80 
  81         /*
  82          * Acquire the lock, and then try reacquiring it several times. Once we
  83          * have acquired the lock, trying to acquire it again should succeed,
  84          * and shouldn't upgrade, downgrade or free the lock.
  85          */
  86         for (i = 0; i < 3; i++) {
  87                 flock_log("Acquiring lock (flock)...\n");
  88                 ret = flock(fd, mode);
  89                 if (ret == -1) {
  90                         err(EXIT_FAILURE, "flock failed");
  91                 }
  92         }
  93 
  94         /* Wait to be okayed to unlock */
  95         flock_log("Waiting (flock)...\n");
  96         flock_alert(1);
  97         flock_block(0);
  98 
  99         /* Release lock */
 100         flock_log("Releasing lock (flock)...\n");
 101         ret = flock(fd, LOCK_UN);
 102         if (ret == -1) {
 103                 err(EXIT_FAILURE, "flock failed");
 104         }
 105 }
 106 
 107 
 108 static void
 109 acq_run(int fd, lock_style_t style, boolean_t is_exclusive)
 110 {
 111         switch (style) {
 112         case LSTYLE_POSIX:
 113                 acq_fcntl(fd, F_SETLKW, is_exclusive ? F_WRLCK : F_RDLCK);
 114                 break;
 115         case LSTYLE_OFD:
 116                 acq_fcntl(fd, F_OFD_SETLKW, is_exclusive ? F_WRLCK : F_RDLCK);
 117                 break;
 118         case LSTYLE_FLOCK:
 119                 acq_flock(fd, is_exclusive ? LOCK_EX : LOCK_SH);
 120                 break;
 121         default:
 122                 abort();
 123         }
 124 }
 125 
 126 
 127 int
 128 main(int argc, char *argv[])
 129 {
 130         char *modestr, *path;
 131         lock_style_t style;
 132         boolean_t is_exclusive;
 133         int fd;
 134 
 135         if (argc < 4) {
 136                 errx(EXIT_FAILURE, BAD_ARGS_MESSAGE, argc - 1);
 137         }
 138 
 139         modestr = argv[2];
 140         path = argv[3];
 141 
 142         style = flock_styleenum(argv[1]);
 143 
 144         if (strcmp(modestr, "shared") == 0) {
 145                 is_exclusive = B_FALSE;
 146         } else if (strcmp(modestr, "exclusive") == 0) {
 147                 is_exclusive = B_TRUE;
 148         } else {
 149                 errx(EXIT_FAILURE, BAD_MODE_MESSAGE);
 150         }
 151 
 152         boolean_t rdonly = style == LSTYLE_FLOCK || !is_exclusive;
 153         fd = open(path, rdonly ? O_RDONLY : O_WRONLY);
 154         if (fd == -1) {
 155                 err(EXIT_FAILURE, "Failed to open %s", path);
 156         }
 157 
 158         acq_run(fd, style, is_exclusive);
 159 
 160         return (0);
 161 }