/src/openvswitch/lib/lockfile.c
Line | Count | Source |
1 | | /* Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. |
2 | | * |
3 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | * you may not use this file except in compliance with the License. |
5 | | * You may obtain a copy of the License at: |
6 | | * |
7 | | * http://www.apache.org/licenses/LICENSE-2.0 |
8 | | * |
9 | | * Unless required by applicable law or agreed to in writing, software |
10 | | * distributed under the License is distributed on an "AS IS" BASIS, |
11 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | * See the License for the specific language governing permissions and |
13 | | * limitations under the License. |
14 | | */ |
15 | | |
16 | | #include <config.h> |
17 | | |
18 | | #include "lockfile.h" |
19 | | |
20 | | #include <errno.h> |
21 | | #include <fcntl.h> |
22 | | #include <stdlib.h> |
23 | | #include <string.h> |
24 | | #include <sys/stat.h> |
25 | | #include <unistd.h> |
26 | | |
27 | | #include "coverage.h" |
28 | | #include "hash.h" |
29 | | #include "openvswitch/hmap.h" |
30 | | #include "ovs-thread.h" |
31 | | #include "timeval.h" |
32 | | #include "util.h" |
33 | | #include "openvswitch/vlog.h" |
34 | | |
35 | | VLOG_DEFINE_THIS_MODULE(lockfile); |
36 | | |
37 | | COVERAGE_DEFINE(lockfile_lock); |
38 | | COVERAGE_DEFINE(lockfile_error); |
39 | | COVERAGE_DEFINE(lockfile_unlock); |
40 | | |
41 | | struct lockfile { |
42 | | struct hmap_node hmap_node; |
43 | | char *name; |
44 | | dev_t device; |
45 | | ino_t inode; |
46 | | int fd; |
47 | | }; |
48 | | |
49 | | /* Lock table. |
50 | | * |
51 | | * We have to do this stupid dance because POSIX says that closing *any* file |
52 | | * descriptor for a file on which a process holds a lock drops *all* locks on |
53 | | * that file. That means that we can't afford to open a lockfile more than |
54 | | * once. */ |
55 | | static struct ovs_mutex lock_table_mutex = OVS_MUTEX_INITIALIZER; |
56 | | static struct hmap lock_table__ = HMAP_INITIALIZER(&lock_table__); |
57 | | static struct hmap *const lock_table OVS_GUARDED_BY(lock_table_mutex) |
58 | | = &lock_table__; |
59 | | |
60 | | static void lockfile_unhash(struct lockfile *); |
61 | | static int lockfile_try_lock(const char *name, pid_t *pidp, |
62 | | struct lockfile **lockfilep) |
63 | | OVS_REQUIRES(lock_table_mutex); |
64 | | static void lockfile_do_unlock(struct lockfile * lockfile) |
65 | | OVS_REQUIRES(lock_table_mutex); |
66 | | |
67 | | /* Returns the name of the lockfile that would be created for locking a file |
68 | | * named 'filename_'. The caller is responsible for freeing the returned name, |
69 | | * with free(), when it is no longer needed. */ |
70 | | char * |
71 | | lockfile_name(const char *filename_) |
72 | 0 | { |
73 | 0 | char *filename; |
74 | 0 | const char *slash; |
75 | 0 | char *lockname; |
76 | | |
77 | | /* If 'filename_' is a symlink, base the name of the lockfile on the |
78 | | * symlink's target rather than the name of the symlink. That way, if a |
79 | | * file is symlinked, but there is no symlink for its lockfile, then there |
80 | | * is only a single lockfile for both the source and the target of the |
81 | | * symlink, not one for each. */ |
82 | 0 | filename = follow_symlinks(filename_); |
83 | 0 | slash = strrchr(filename, '/'); |
84 | |
|
85 | 0 | lockname = (slash |
86 | 0 | ? xasprintf("%.*s/.%s.~lock~", |
87 | 0 | (int) (slash - filename), filename, slash + 1) |
88 | 0 | : xasprintf(".%s.~lock~", filename)); |
89 | 0 | free(filename); |
90 | |
|
91 | 0 | return lockname; |
92 | 0 | } |
93 | | |
94 | | /* Locks the configuration file against modification by other processes and |
95 | | * re-reads it from disk. |
96 | | * |
97 | | * Returns 0 on success, otherwise a positive errno value. On success, |
98 | | * '*lockfilep' is set to point to a new "struct lockfile *" that may be |
99 | | * unlocked with lockfile_unlock(). On failure, '*lockfilep' is set to |
100 | | * NULL. Will not block if the lock cannot be immediately acquired. */ |
101 | | int |
102 | | lockfile_lock(const char *file, struct lockfile **lockfilep) |
103 | 0 | { |
104 | | /* Only exclusive ("write") locks are supported. This is not a problem |
105 | | * because the Open vSwitch code that currently uses lock files does so in |
106 | | * stylized ways such that any number of readers may access a file while it |
107 | | * is being written. */ |
108 | 0 | char *lock_name; |
109 | 0 | pid_t pid; |
110 | 0 | int error; |
111 | |
|
112 | 0 | COVERAGE_INC(lockfile_lock); |
113 | |
|
114 | 0 | lock_name = lockfile_name(file); |
115 | |
|
116 | 0 | ovs_mutex_lock(&lock_table_mutex); |
117 | 0 | error = lockfile_try_lock(lock_name, &pid, lockfilep); |
118 | 0 | ovs_mutex_unlock(&lock_table_mutex); |
119 | |
|
120 | 0 | if (error) { |
121 | 0 | COVERAGE_INC(lockfile_error); |
122 | 0 | if (error == EACCES) { |
123 | 0 | error = EAGAIN; |
124 | 0 | } |
125 | 0 | if (pid == getpid()) { |
126 | 0 | VLOG_WARN("%s: cannot lock file because this process has already " |
127 | 0 | "locked it", lock_name); |
128 | 0 | } else if (pid) { |
129 | 0 | VLOG_WARN("%s: cannot lock file because it is already locked by " |
130 | 0 | "pid %ld", lock_name, (long int) pid); |
131 | 0 | } else { |
132 | 0 | VLOG_WARN("%s: failed to lock file: %s", |
133 | 0 | lock_name, ovs_strerror(error)); |
134 | 0 | } |
135 | 0 | } |
136 | |
|
137 | 0 | free(lock_name); |
138 | 0 | return error; |
139 | 0 | } |
140 | | |
141 | | /* Unlocks 'lockfile', which must have been created by a call to |
142 | | * lockfile_lock(), and frees 'lockfile'. */ |
143 | | void |
144 | | lockfile_unlock(struct lockfile *lockfile) |
145 | 0 | { |
146 | 0 | if (lockfile) { |
147 | 0 | ovs_mutex_lock(&lock_table_mutex); |
148 | 0 | lockfile_do_unlock(lockfile); |
149 | 0 | ovs_mutex_unlock(&lock_table_mutex); |
150 | |
|
151 | 0 | COVERAGE_INC(lockfile_unlock); |
152 | 0 | free(lockfile->name); |
153 | 0 | free(lockfile); |
154 | 0 | } |
155 | 0 | } |
156 | | |
157 | | /* Marks all the currently locked lockfiles as no longer locked. It makes |
158 | | * sense to call this function after fork(), because a child created by fork() |
159 | | * does not hold its parents' locks. */ |
160 | | void |
161 | | lockfile_postfork(void) |
162 | 0 | { |
163 | 0 | struct lockfile *lockfile; |
164 | |
|
165 | 0 | ovs_mutex_lock(&lock_table_mutex); |
166 | 0 | HMAP_FOR_EACH (lockfile, hmap_node, lock_table) { |
167 | 0 | if (lockfile->fd >= 0) { |
168 | 0 | VLOG_WARN("%s: child does not inherit lock", lockfile->name); |
169 | 0 | lockfile_unhash(lockfile); |
170 | 0 | } |
171 | 0 | } |
172 | 0 | ovs_mutex_unlock(&lock_table_mutex); |
173 | 0 | } |
174 | | |
175 | | static uint32_t |
176 | | lockfile_hash(dev_t device, ino_t inode) |
177 | 0 | { |
178 | 0 | return hash_bytes(&device, sizeof device, |
179 | 0 | hash_bytes(&inode, sizeof inode, 0)); |
180 | 0 | } |
181 | | |
182 | | static struct lockfile * |
183 | | lockfile_find(dev_t device, ino_t inode) OVS_REQUIRES(lock_table_mutex) |
184 | 0 | { |
185 | 0 | struct lockfile *lockfile; |
186 | |
|
187 | 0 | HMAP_FOR_EACH_WITH_HASH (lockfile, hmap_node, |
188 | 0 | lockfile_hash(device, inode), lock_table) { |
189 | 0 | if (lockfile->device == device && lockfile->inode == inode) { |
190 | 0 | return lockfile; |
191 | 0 | } |
192 | 0 | } |
193 | 0 | return NULL; |
194 | 0 | } |
195 | | |
196 | | static void |
197 | | lockfile_unhash(struct lockfile *lockfile) OVS_REQUIRES(lock_table_mutex) |
198 | 0 | { |
199 | 0 | if (lockfile->fd >= 0) { |
200 | 0 | close(lockfile->fd); |
201 | 0 | lockfile->fd = -1; |
202 | 0 | hmap_remove(lock_table, &lockfile->hmap_node); |
203 | 0 | } |
204 | 0 | } |
205 | | |
206 | | static struct lockfile * |
207 | | lockfile_register(const char *name, dev_t device, ino_t inode, int fd) |
208 | | OVS_REQUIRES(lock_table_mutex) |
209 | 0 | { |
210 | 0 | struct lockfile *lockfile; |
211 | |
|
212 | 0 | lockfile = lockfile_find(device, inode); |
213 | 0 | if (lockfile) { |
214 | 0 | VLOG_ERR("%s: lock file disappeared and reappeared!", name); |
215 | 0 | lockfile_unhash(lockfile); |
216 | 0 | } |
217 | |
|
218 | 0 | lockfile = xmalloc(sizeof *lockfile); |
219 | 0 | lockfile->name = xstrdup(name); |
220 | 0 | lockfile->device = device; |
221 | 0 | lockfile->inode = inode; |
222 | 0 | lockfile->fd = fd; |
223 | 0 | hmap_insert(lock_table, &lockfile->hmap_node, |
224 | 0 | lockfile_hash(device, inode)); |
225 | 0 | return lockfile; |
226 | 0 | } |
227 | | |
228 | | static void |
229 | | lockfile_do_unlock(struct lockfile *lockfile) |
230 | 0 | { |
231 | 0 | lockfile_unhash(lockfile); |
232 | 0 | } |
233 | | |
234 | | static int |
235 | | lockfile_try_lock(const char *name, pid_t *pidp, struct lockfile **lockfilep) |
236 | | OVS_REQUIRES(lock_table_mutex) |
237 | 0 | { |
238 | 0 | struct flock l; |
239 | 0 | struct stat s; |
240 | 0 | int error; |
241 | 0 | int fd; |
242 | |
|
243 | 0 | *lockfilep = NULL; |
244 | 0 | *pidp = 0; |
245 | | |
246 | | /* Check whether we've already got a lock on that file. */ |
247 | 0 | if (!stat(name, &s)) { |
248 | 0 | if (lockfile_find(s.st_dev, s.st_ino)) { |
249 | 0 | *pidp = getpid(); |
250 | 0 | return EDEADLK; |
251 | 0 | } |
252 | 0 | } else if (errno != ENOENT) { |
253 | 0 | VLOG_WARN("%s: failed to stat lock file: %s", |
254 | 0 | name, ovs_strerror(errno)); |
255 | 0 | return errno; |
256 | 0 | } |
257 | | |
258 | | /* Open the lock file. */ |
259 | 0 | fd = open(name, O_RDWR | O_CREAT, 0600); |
260 | 0 | if (fd < 0) { |
261 | 0 | VLOG_WARN("%s: failed to open lock file: %s", |
262 | 0 | name, ovs_strerror(errno)); |
263 | 0 | return errno; |
264 | 0 | } |
265 | | |
266 | | /* Get the inode and device number for the lock table. */ |
267 | 0 | if (fstat(fd, &s)) { |
268 | 0 | VLOG_ERR("%s: failed to fstat lock file: %s", |
269 | 0 | name, ovs_strerror(errno)); |
270 | 0 | close(fd); |
271 | 0 | return errno; |
272 | 0 | } |
273 | | |
274 | | /* Try to lock the file. */ |
275 | 0 | memset(&l, 0, sizeof l); |
276 | 0 | l.l_type = F_WRLCK; |
277 | 0 | l.l_whence = SEEK_SET; |
278 | 0 | l.l_start = 0; |
279 | 0 | l.l_len = 0; |
280 | |
|
281 | 0 | error = fcntl(fd, F_SETLK, &l) == -1 ? errno : 0; |
282 | |
|
283 | 0 | if (!error) { |
284 | 0 | *lockfilep = lockfile_register(name, s.st_dev, s.st_ino, fd); |
285 | 0 | } else { |
286 | 0 | if (!fcntl(fd, F_GETLK, &l) && l.l_type != F_UNLCK) { |
287 | 0 | *pidp = l.l_pid; |
288 | 0 | } |
289 | 0 | close(fd); |
290 | 0 | } |
291 | 0 | return error; |
292 | 0 | } |