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