]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/lxclock.c
lxccontainer: use thread-safe *_OFD_* locks
[mirror_lxc.git] / src / lxc / lxclock.c
1 /* liblxcapi
2 *
3 * Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>.
4 * Copyright © 2012 Canonical Ltd.
5 *
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
19 */
20
21 #define _GNU_SOURCE
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <malloc.h>
25 #include <pthread.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <sys/file.h>
29 #include <unistd.h>
30
31 #include <lxc/lxccontainer.h>
32
33 #include "lxclock.h"
34 #include "utils.h"
35 #include "log.h"
36
37 #ifdef MUTEX_DEBUGGING
38 #include <execinfo.h>
39 #endif
40
41 #define MAX_STACKDEPTH 25
42
43 lxc_log_define(lxc_lock, lxc);
44
45 #ifdef MUTEX_DEBUGGING
46 static pthread_mutex_t thread_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
47
48 static inline void dump_stacktrace(void)
49 {
50 void *array[MAX_STACKDEPTH];
51 size_t size;
52 char **strings;
53 size_t i;
54
55 size = backtrace(array, MAX_STACKDEPTH);
56 strings = backtrace_symbols(array, size);
57
58 /* Using fprintf here as our logging module is not thread safe. */
59 fprintf(stderr, "\tObtained %zu stack frames\n", size);
60
61 for (i = 0; i < size; i++)
62 fprintf(stderr, "\t\t%s\n", strings[i]);
63
64 free(strings);
65 }
66 #else
67 static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER;
68
69 static inline void dump_stacktrace(void) {;}
70 #endif
71
72 static void lock_mutex(pthread_mutex_t *l)
73 {
74 int ret;
75
76 ret = pthread_mutex_lock(l);
77 if (ret != 0) {
78 fprintf(stderr, "%s - Failed acquire mutex", strerror(ret));
79 dump_stacktrace();
80 _exit(EXIT_FAILURE);
81 }
82 }
83
84 static void unlock_mutex(pthread_mutex_t *l)
85 {
86 int ret;
87
88 ret = pthread_mutex_unlock(l);
89 if (ret != 0) {
90 fprintf(stderr, "%s - Failed to release mutex", strerror(ret));
91 dump_stacktrace();
92 _exit(EXIT_FAILURE);
93 }
94 }
95
96 static char *lxclock_name(const char *p, const char *n)
97 {
98 int ret;
99 int len;
100 char *dest;
101 char *rundir;
102
103 /* lockfile will be:
104 * "/run" + "/lxc/lock/$lxcpath/$lxcname + '\0' if root
105 * or
106 * $XDG_RUNTIME_DIR + "/lxc/lock/$lxcpath/$lxcname + '\0' if non-root
107 */
108
109 /* length of "/lxc/lock/" + $lxcpath + "/" + "." + $lxcname + '\0' */
110 len = strlen("/lxc/lock/") + strlen(n) + strlen(p) + 3;
111 rundir = get_rundir();
112 if (!rundir)
113 return NULL;
114 len += strlen(rundir);
115
116 if ((dest = malloc(len)) == NULL) {
117 free(rundir);
118 return NULL;
119 }
120
121 ret = snprintf(dest, len, "%s/lxc/lock/%s", rundir, p);
122 if (ret < 0 || ret >= len) {
123 free(dest);
124 free(rundir);
125 return NULL;
126 }
127 ret = mkdir_p(dest, 0755);
128 if (ret < 0) {
129 free(dest);
130 free(rundir);
131 return NULL;
132 }
133
134 ret = snprintf(dest, len, "%s/lxc/lock/%s/.%s", rundir, p, n);
135 free(rundir);
136 if (ret < 0 || ret >= len) {
137 free(dest);
138 return NULL;
139 }
140 return dest;
141 }
142
143 static sem_t *lxc_new_unnamed_sem(void)
144 {
145 sem_t *s;
146 int ret;
147
148 s = malloc(sizeof(*s));
149 if (!s)
150 return NULL;
151 ret = sem_init(s, 0, 1);
152 if (ret) {
153 free(s);
154 return NULL;
155 }
156 return s;
157 }
158
159 struct lxc_lock *lxc_newlock(const char *lxcpath, const char *name)
160 {
161 struct lxc_lock *l;
162
163 l = malloc(sizeof(*l));
164 if (!l)
165 goto out;
166
167 if (!name) {
168 l->type = LXC_LOCK_ANON_SEM;
169 l->u.sem = lxc_new_unnamed_sem();
170 if (!l->u.sem) {
171 free(l);
172 l = NULL;
173 }
174 goto out;
175 }
176
177 l->type = LXC_LOCK_FLOCK;
178 l->u.f.fname = lxclock_name(lxcpath, name);
179 if (!l->u.f.fname) {
180 free(l);
181 l = NULL;
182 goto out;
183 }
184 l->u.f.fd = -1;
185
186 out:
187 return l;
188 }
189
190 int lxclock(struct lxc_lock *l, int timeout)
191 {
192 int ret = -1, saved_errno = errno;
193 struct flock lk;
194
195 switch(l->type) {
196 case LXC_LOCK_ANON_SEM:
197 if (!timeout) {
198 ret = sem_wait(l->u.sem);
199 if (ret < 0)
200 saved_errno = errno;
201 } else {
202 struct timespec ts;
203 if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
204 ret = -2;
205 goto out;
206 }
207 ts.tv_sec += timeout;
208 ret = sem_timedwait(l->u.sem, &ts);
209 if (ret < 0)
210 saved_errno = errno;
211 }
212 break;
213 case LXC_LOCK_FLOCK:
214 ret = -2;
215 if (timeout) {
216 ERROR("Error: timeout not supported with flock");
217 goto out;
218 }
219 if (!l->u.f.fname) {
220 ERROR("Error: filename not set for flock");
221 goto out;
222 }
223 if (l->u.f.fd == -1) {
224 l->u.f.fd = open(l->u.f.fname, O_CREAT | O_RDWR | O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, S_IWUSR | S_IRUSR);
225 if (l->u.f.fd == -1) {
226 ERROR("Error opening %s", l->u.f.fname);
227 saved_errno = errno;
228 goto out;
229 }
230 }
231 memset(&lk, 0, sizeof(struct flock));
232 lk.l_type = F_WRLCK;
233 lk.l_whence = SEEK_SET;
234 ret = fcntl(l->u.f.fd, F_OFD_SETLKW, &lk);
235 if (ret < 0) {
236 if (errno == EINVAL)
237 ret = flock(l->u.f.fd, LOCK_EX);
238 saved_errno = errno;
239 }
240 break;
241 }
242
243 out:
244 errno = saved_errno;
245 return ret;
246 }
247
248 int lxcunlock(struct lxc_lock *l)
249 {
250 int ret = 0, saved_errno = errno;
251 struct flock lk;
252
253 switch(l->type) {
254 case LXC_LOCK_ANON_SEM:
255 if (!l->u.sem)
256 ret = -2;
257 else {
258 ret = sem_post(l->u.sem);
259 saved_errno = errno;
260 }
261 break;
262 case LXC_LOCK_FLOCK:
263 if (l->u.f.fd != -1) {
264 memset(&lk, 0, sizeof(struct flock));
265 lk.l_type = F_UNLCK;
266 lk.l_whence = SEEK_SET;
267 ret = fcntl(l->u.f.fd, F_OFD_SETLK, &lk);
268 if (ret < 0) {
269 if (errno == EINVAL)
270 ret = flock(l->u.f.fd, LOCK_EX | LOCK_NB);
271 saved_errno = errno;
272 }
273 close(l->u.f.fd);
274 l->u.f.fd = -1;
275 } else
276 ret = -2;
277 break;
278 }
279
280 errno = saved_errno;
281 return ret;
282 }
283
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 */
291 void lxc_putlock(struct lxc_lock *l)
292 {
293 if (!l)
294 return;
295 switch(l->type) {
296 case LXC_LOCK_ANON_SEM:
297 if (l->u.sem) {
298 sem_destroy(l->u.sem);
299 free(l->u.sem);
300 l->u.sem = NULL;
301 }
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 }
308 free(l->u.f.fname);
309 l->u.f.fname = NULL;
310 break;
311 }
312 free(l);
313 }
314
315 void process_lock(void)
316 {
317 lock_mutex(&thread_mutex);
318 }
319
320 void process_unlock(void)
321 {
322 unlock_mutex(&thread_mutex);
323 }
324
325 int container_mem_lock(struct lxc_container *c)
326 {
327 return lxclock(c->privlock, 0);
328 }
329
330 void container_mem_unlock(struct lxc_container *c)
331 {
332 lxcunlock(c->privlock);
333 }
334
335 int container_disk_lock(struct lxc_container *c)
336 {
337 int ret;
338
339 if ((ret = lxclock(c->privlock, 0)))
340 return ret;
341 if ((ret = lxclock(c->slock, 0))) {
342 lxcunlock(c->privlock);
343 return ret;
344 }
345 return 0;
346 }
347
348 void container_disk_unlock(struct lxc_container *c)
349 {
350 lxcunlock(c->slock);
351 lxcunlock(c->privlock);
352 }