]>
Commit | Line | Data |
---|---|---|
72d0e1cb SG |
1 | /* liblxcapi |
2 | * | |
3 | * Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>. | |
4 | * Copyright © 2012 Canonical Ltd. | |
5 | * | |
d75462e4 SH |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | ||
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | ||
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
72d0e1cb SG |
19 | */ |
20 | ||
052616eb | 21 | #define _GNU_SOURCE |
72d0e1cb SG |
22 | #include "lxclock.h" |
23 | #include <malloc.h> | |
8173e600 | 24 | #include <stdio.h> |
df271a59 SH |
25 | #include <errno.h> |
26 | #include <unistd.h> | |
93dc5327 | 27 | #include <fcntl.h> |
469b5787 | 28 | #include <stdlib.h> |
052616eb | 29 | #include <pthread.h> |
f2363e38 | 30 | |
5cee8c50 | 31 | #include <lxc/lxccontainer.h> |
72d0e1cb | 32 | |
f2363e38 ÇO |
33 | #include "utils.h" |
34 | #include "log.h" | |
35 | ||
41f68357 ÇO |
36 | #ifdef MUTEX_DEBUGGING |
37 | #include <execinfo.h> | |
38 | #endif | |
39 | ||
5b28d063 SH |
40 | #define MAX_STACKDEPTH 25 |
41 | ||
72d0e1cb SG |
42 | #define OFLAG (O_CREAT | O_RDWR) |
43 | #define SEMMODE 0660 | |
44 | #define SEMVALUE 1 | |
45 | #define SEMVALUE_LOCKED 0 | |
72d0e1cb | 46 | |
df271a59 | 47 | lxc_log_define(lxc_lock, lxc); |
72d0e1cb | 48 | |
052616eb | 49 | #ifdef MUTEX_DEBUGGING |
74a3920a | 50 | static pthread_mutex_t thread_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; |
41f68357 | 51 | |
74a3920a | 52 | static inline void dump_stacktrace(void) |
41f68357 ÇO |
53 | { |
54 | void *array[MAX_STACKDEPTH]; | |
55 | size_t size; | |
56 | char **strings; | |
57 | size_t i; | |
58 | ||
59 | size = backtrace(array, MAX_STACKDEPTH); | |
60 | strings = backtrace_symbols(array, size); | |
61 | ||
62 | // Using fprintf here as our logging module is not thread safe | |
63 | fprintf(stderr, "\tObtained %zd stack frames.\n", size); | |
64 | ||
65 | for (i = 0; i < size; i++) | |
66 | fprintf(stderr, "\t\t%s\n", strings[i]); | |
67 | ||
68 | free (strings); | |
69 | } | |
052616eb | 70 | #else |
74a3920a | 71 | static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; |
41f68357 | 72 | |
74a3920a | 73 | static inline void dump_stacktrace(void) {;} |
052616eb | 74 | #endif |
df271a59 | 75 | |
74a3920a | 76 | static void lock_mutex(pthread_mutex_t *l) |
41f68357 ÇO |
77 | { |
78 | int ret; | |
79 | ||
80 | if ((ret = pthread_mutex_lock(l)) != 0) { | |
03fadd16 | 81 | fprintf(stderr, "pthread_mutex_lock returned:%d %s\n", ret, strerror(ret)); |
41f68357 ÇO |
82 | dump_stacktrace(); |
83 | exit(1); | |
84 | } | |
85 | } | |
86 | ||
74a3920a | 87 | static void unlock_mutex(pthread_mutex_t *l) |
41f68357 ÇO |
88 | { |
89 | int ret; | |
90 | ||
91 | if ((ret = pthread_mutex_unlock(l)) != 0) { | |
03fadd16 | 92 | fprintf(stderr, "pthread_mutex_unlock returned:%d %s\n", ret, strerror(ret)); |
41f68357 ÇO |
93 | dump_stacktrace(); |
94 | exit(1); | |
95 | } | |
96 | } | |
97 | ||
df271a59 | 98 | static char *lxclock_name(const char *p, const char *n) |
72d0e1cb SG |
99 | { |
100 | int ret; | |
469b5787 SH |
101 | int len; |
102 | char *dest; | |
44b9ae4b | 103 | char *rundir; |
71b0fed6 | 104 | |
469b5787 | 105 | /* lockfile will be: |
72cf81f6 | 106 | * "/run" + "/lxc/lock/$lxcpath/$lxcname + '\0' if root |
469b5787 | 107 | * or |
72cf81f6 | 108 | * $XDG_RUNTIME_DIR + "/lxc/lock/$lxcpath/$lxcname + '\0' if non-root |
469b5787 SH |
109 | */ |
110 | ||
72cf81f6 SH |
111 | /* length of "/lxc/lock/" + $lxcpath + "/" + "." + $lxcname + '\0' */ |
112 | len = strlen("/lxc/lock/") + strlen(n) + strlen(p) + 3; | |
9e60f51d | 113 | rundir = get_rundir(); |
97a696c6 SG |
114 | if (!rundir) |
115 | return NULL; | |
469b5787 SH |
116 | len += strlen(rundir); |
117 | ||
44b9ae4b SG |
118 | if ((dest = malloc(len)) == NULL) { |
119 | free(rundir); | |
72d0e1cb | 120 | return NULL; |
44b9ae4b | 121 | } |
469b5787 | 122 | |
72cf81f6 | 123 | ret = snprintf(dest, len, "%s/lxc/lock/%s", rundir, p); |
72d0e1cb SG |
124 | if (ret < 0 || ret >= len) { |
125 | free(dest); | |
44b9ae4b | 126 | free(rundir); |
72d0e1cb SG |
127 | return NULL; |
128 | } | |
5cee8c50 | 129 | ret = mkdir_p(dest, 0755); |
5cee8c50 | 130 | if (ret < 0) { |
72cf81f6 SH |
131 | free(dest); |
132 | free(rundir); | |
133 | return NULL; | |
134 | } | |
72d0e1cb | 135 | |
72cf81f6 | 136 | ret = snprintf(dest, len, "%s/lxc/lock/%s/.%s", rundir, p, n); |
44b9ae4b | 137 | free(rundir); |
df271a59 SH |
138 | if (ret < 0 || ret >= len) { |
139 | free(dest); | |
140 | return NULL; | |
141 | } | |
142 | return dest; | |
72d0e1cb SG |
143 | } |
144 | ||
145 | static sem_t *lxc_new_unnamed_sem(void) | |
146 | { | |
87677950 SH |
147 | sem_t *s; |
148 | int ret; | |
72d0e1cb | 149 | |
87677950 SH |
150 | s = malloc(sizeof(*s)); |
151 | if (!s) | |
152 | return NULL; | |
153 | ret = sem_init(s, 0, 1); | |
bdb539b8 SH |
154 | if (ret) { |
155 | free(s); | |
87677950 | 156 | return NULL; |
bdb539b8 | 157 | } |
87677950 | 158 | return s; |
72d0e1cb SG |
159 | } |
160 | ||
df271a59 | 161 | struct lxc_lock *lxc_newlock(const char *lxcpath, const char *name) |
72d0e1cb | 162 | { |
df271a59 | 163 | struct lxc_lock *l; |
72d0e1cb | 164 | |
df271a59 SH |
165 | l = malloc(sizeof(*l)); |
166 | if (!l) | |
167 | goto out; | |
72d0e1cb | 168 | |
df271a59 SH |
169 | if (!name) { |
170 | l->type = LXC_LOCK_ANON_SEM; | |
171 | l->u.sem = lxc_new_unnamed_sem(); | |
41c3b7c7 DE |
172 | if (!l->u.sem) { |
173 | free(l); | |
174 | l = NULL; | |
175 | } | |
df271a59 SH |
176 | goto out; |
177 | } | |
178 | ||
179 | l->type = LXC_LOCK_FLOCK; | |
180 | l->u.f.fname = lxclock_name(lxcpath, name); | |
181 | if (!l->u.f.fname) { | |
182 | free(l); | |
183 | l = NULL; | |
184 | goto out; | |
185 | } | |
186 | l->u.f.fd = -1; | |
187 | ||
188 | out: | |
df271a59 | 189 | return l; |
72d0e1cb SG |
190 | } |
191 | ||
df271a59 | 192 | int lxclock(struct lxc_lock *l, int timeout) |
72d0e1cb | 193 | { |
5cee8c50 | 194 | int ret = -1, saved_errno = errno; |
93dc5327 | 195 | struct flock lk; |
72d0e1cb | 196 | |
df271a59 SH |
197 | switch(l->type) { |
198 | case LXC_LOCK_ANON_SEM: | |
199 | if (!timeout) { | |
200 | ret = sem_wait(l->u.sem); | |
201 | if (ret == -1) | |
202 | saved_errno = errno; | |
203 | } else { | |
204 | struct timespec ts; | |
205 | if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { | |
206 | ret = -2; | |
207 | goto out; | |
208 | } | |
209 | ts.tv_sec += timeout; | |
210 | ret = sem_timedwait(l->u.sem, &ts); | |
211 | if (ret == -1) | |
212 | saved_errno = errno; | |
213 | } | |
214 | break; | |
215 | case LXC_LOCK_FLOCK: | |
216 | ret = -2; | |
217 | if (timeout) { | |
218 | ERROR("Error: timeout not supported with flock"); | |
219 | ret = -2; | |
220 | goto out; | |
221 | } | |
222 | if (!l->u.f.fname) { | |
223 | ERROR("Error: filename not set for flock"); | |
224 | ret = -2; | |
225 | goto out; | |
226 | } | |
227 | if (l->u.f.fd == -1) { | |
228 | l->u.f.fd = open(l->u.f.fname, O_RDWR|O_CREAT, | |
229 | S_IWUSR | S_IRUSR); | |
230 | if (l->u.f.fd == -1) { | |
231 | ERROR("Error opening %s", l->u.f.fname); | |
232 | goto out; | |
233 | } | |
234 | } | |
93dc5327 SH |
235 | lk.l_type = F_WRLCK; |
236 | lk.l_whence = SEEK_SET; | |
237 | lk.l_start = 0; | |
238 | lk.l_len = 0; | |
239 | ret = fcntl(l->u.f.fd, F_SETLKW, &lk); | |
df271a59 SH |
240 | if (ret == -1) |
241 | saved_errno = errno; | |
242 | break; | |
72d0e1cb SG |
243 | } |
244 | ||
df271a59 | 245 | out: |
df271a59 | 246 | errno = saved_errno; |
72d0e1cb SG |
247 | return ret; |
248 | } | |
249 | ||
df271a59 | 250 | int lxcunlock(struct lxc_lock *l) |
72d0e1cb | 251 | { |
5cee8c50 | 252 | int ret = 0, saved_errno = errno; |
93dc5327 | 253 | struct flock lk; |
df271a59 SH |
254 | |
255 | switch(l->type) { | |
256 | case LXC_LOCK_ANON_SEM: | |
257 | if (!l->u.sem) | |
258 | ret = -2; | |
659aa061 | 259 | else { |
df271a59 SH |
260 | ret = sem_post(l->u.sem); |
261 | saved_errno = errno; | |
659aa061 | 262 | } |
df271a59 SH |
263 | break; |
264 | case LXC_LOCK_FLOCK: | |
265 | if (l->u.f.fd != -1) { | |
93dc5327 SH |
266 | lk.l_type = F_UNLCK; |
267 | lk.l_whence = SEEK_SET; | |
268 | lk.l_start = 0; | |
269 | lk.l_len = 0; | |
270 | ret = fcntl(l->u.f.fd, F_SETLK, &lk); | |
271 | if (ret < 0) | |
df271a59 SH |
272 | saved_errno = errno; |
273 | close(l->u.f.fd); | |
274 | l->u.f.fd = -1; | |
275 | } else | |
276 | ret = -2; | |
277 | break; | |
278 | } | |
279 | ||
df271a59 SH |
280 | errno = saved_errno; |
281 | return ret; | |
282 | } | |
283 | ||
5cee8c50 SH |
284 | /* |
285 | * lxc_putlock() is only called when a container_new() fails, | |
286 | * or during container_put(), which is already guaranteed to | |
287 | * only be done by one task. | |
288 | * So the only exclusion we need to provide here is for regular | |
289 | * thread safety (i.e. file descriptor table changes). | |
290 | */ | |
df271a59 SH |
291 | void lxc_putlock(struct lxc_lock *l) |
292 | { | |
df271a59 | 293 | if (!l) |
5cee8c50 | 294 | return; |
df271a59 SH |
295 | switch(l->type) { |
296 | case LXC_LOCK_ANON_SEM: | |
41c3b7c7 | 297 | if (l->u.sem) { |
527dacf6 | 298 | sem_destroy(l->u.sem); |
41c3b7c7 DE |
299 | free(l->u.sem); |
300 | l->u.sem = NULL; | |
301 | } | |
df271a59 SH |
302 | break; |
303 | case LXC_LOCK_FLOCK: | |
304 | if (l->u.f.fd != -1) { | |
305 | close(l->u.f.fd); | |
306 | l->u.f.fd = -1; | |
307 | } | |
f10fad2f ME |
308 | free(l->u.f.fname); |
309 | l->u.f.fname = NULL; | |
df271a59 SH |
310 | break; |
311 | } | |
41c3b7c7 | 312 | free(l); |
5cee8c50 SH |
313 | } |
314 | ||
025ed0f3 | 315 | void process_lock(void) |
5cee8c50 | 316 | { |
41f68357 | 317 | lock_mutex(&thread_mutex); |
5cee8c50 SH |
318 | } |
319 | ||
320 | void process_unlock(void) | |
321 | { | |
41f68357 ÇO |
322 | unlock_mutex(&thread_mutex); |
323 | } | |
052616eb | 324 | |
67c4fe9b | 325 | /* One thread can do fork() while another one is holding a mutex. |
ec64264d | 326 | * There is only one thread in child just after the fork(), so no one will ever release that mutex. |
67c4fe9b SG |
327 | * We setup a "child" fork handler to unlock the mutex just after the fork(). |
328 | * For several mutex types, unlocking an unlocked mutex can lead to undefined behavior. | |
329 | * One way to deal with it is to setup "prepare" fork handler | |
330 | * to lock the mutex before fork() and both "parent" and "child" fork handlers | |
331 | * to unlock the mutex. | |
332 | * This forbids doing fork() while explicitly holding the lock. | |
333 | */ | |
babbea4b | 334 | #ifdef HAVE_PTHREAD_ATFORK |
67c4fe9b SG |
335 | __attribute__((constructor)) |
336 | static void process_lock_setup_atfork(void) | |
337 | { | |
338 | pthread_atfork(process_lock, process_unlock, process_unlock); | |
339 | } | |
babbea4b | 340 | #endif |
67c4fe9b | 341 | |
5cee8c50 SH |
342 | int container_mem_lock(struct lxc_container *c) |
343 | { | |
344 | return lxclock(c->privlock, 0); | |
345 | } | |
346 | ||
347 | void container_mem_unlock(struct lxc_container *c) | |
348 | { | |
349 | lxcunlock(c->privlock); | |
350 | } | |
351 | ||
352 | int container_disk_lock(struct lxc_container *c) | |
353 | { | |
354 | int ret; | |
355 | ||
356 | if ((ret = lxclock(c->privlock, 0))) | |
357 | return ret; | |
358 | if ((ret = lxclock(c->slock, 0))) { | |
359 | lxcunlock(c->privlock); | |
360 | return ret; | |
361 | } | |
362 | return 0; | |
363 | } | |
364 | ||
365 | void container_disk_unlock(struct lxc_container *c) | |
366 | { | |
367 | lxcunlock(c->slock); | |
368 | lxcunlock(c->privlock); | |
369 | } |