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
acq_fcntl(int fd,int cmd,int mode)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
acq_flock(int fd,int mode)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
acq_run(int fd,lock_style_t style,boolean_t is_exclusive)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
main(int argc,char * argv[])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 }
162