]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/lxclock.c
confile: cleanup parse_line()
[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 if (!name)
173 free(l->u.sem);
174 free(l);
175 l = NULL;
176 goto on_error;
177 }
178
179 l->u.f.fd = -1;
180
181 on_error:
182 return l;
183 }
184
185 int lxclock(struct lxc_lock *l, int timeout)
186 {
187 struct flock lk;
188 int ret = -1, saved_errno = errno;
189
190 switch(l->type) {
191 case LXC_LOCK_ANON_SEM:
192 if (!timeout) {
193 ret = sem_wait(l->u.sem);
194 if (ret < 0)
195 saved_errno = errno;
196 } else {
197 struct timespec ts;
198
199 ret = clock_gettime(CLOCK_REALTIME, &ts);
200 if (ret < 0) {
201 ret = -2;
202 goto on_error;
203 }
204
205 ts.tv_sec += timeout;
206 ret = sem_timedwait(l->u.sem, &ts);
207 if (ret < 0)
208 saved_errno = errno;
209 }
210
211 break;
212 case LXC_LOCK_FLOCK:
213 ret = -2;
214 if (timeout) {
215 ERROR("Timeouts are not supported with file locks");
216 goto on_error;
217 }
218
219 if (!l->u.f.fname) {
220 ERROR("No filename set for file lock");
221 goto on_error;
222 }
223
224 if (l->u.f.fd == -1) {
225 l->u.f.fd = open(l->u.f.fname, O_CREAT | O_RDWR | O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, S_IWUSR | S_IRUSR);
226 if (l->u.f.fd == -1) {
227 SYSERROR("Failed to open \"%s\"", l->u.f.fname);
228 saved_errno = errno;
229 goto on_error;
230 }
231 }
232
233 memset(&lk, 0, sizeof(struct flock));
234
235 lk.l_type = F_WRLCK;
236 lk.l_whence = SEEK_SET;
237
238 ret = fcntl(l->u.f.fd, F_OFD_SETLKW, &lk);
239 if (ret < 0) {
240 if (errno == EINVAL)
241 ret = flock(l->u.f.fd, LOCK_EX);
242 saved_errno = errno;
243 }
244
245 break;
246 }
247
248 on_error:
249 errno = saved_errno;
250 return ret;
251 }
252
253 int lxcunlock(struct lxc_lock *l)
254 {
255 struct flock lk;
256 int ret = 0, saved_errno = errno;
257
258 switch (l->type) {
259 case LXC_LOCK_ANON_SEM:
260 if (!l->u.sem) {
261 ret = -2;
262 } else {
263 ret = sem_post(l->u.sem);
264 saved_errno = errno;
265 }
266
267 break;
268 case LXC_LOCK_FLOCK:
269 if (l->u.f.fd != -1) {
270 memset(&lk, 0, sizeof(struct flock));
271
272 lk.l_type = F_UNLCK;
273 lk.l_whence = SEEK_SET;
274
275 ret = fcntl(l->u.f.fd, F_OFD_SETLK, &lk);
276 if (ret < 0) {
277 if (errno == EINVAL)
278 ret = flock(l->u.f.fd, LOCK_EX | LOCK_NB);
279 saved_errno = errno;
280 }
281
282 close(l->u.f.fd);
283 l->u.f.fd = -1;
284 } else {
285 ret = -2;
286 }
287
288 break;
289 }
290
291 errno = saved_errno;
292 return ret;
293 }
294
295 /*
296 * lxc_putlock() is only called when a container_new() fails,
297 * or during container_put(), which is already guaranteed to
298 * only be done by one task.
299 * So the only exclusion we need to provide here is for regular
300 * thread safety (i.e. file descriptor table changes).
301 */
302 void lxc_putlock(struct lxc_lock *l)
303 {
304 if (!l)
305 return;
306
307 switch(l->type) {
308 case LXC_LOCK_ANON_SEM:
309 if (l->u.sem) {
310 sem_destroy(l->u.sem);
311 free(l->u.sem);
312 l->u.sem = NULL;
313 }
314
315 break;
316 case LXC_LOCK_FLOCK:
317 if (l->u.f.fd != -1) {
318 close(l->u.f.fd);
319 l->u.f.fd = -1;
320 }
321
322 free(l->u.f.fname);
323 l->u.f.fname = NULL;
324
325 break;
326 }
327
328 free(l);
329 }
330
331 void process_lock(void)
332 {
333 lock_mutex(&thread_mutex);
334 }
335
336 void process_unlock(void)
337 {
338 unlock_mutex(&thread_mutex);
339 }
340
341 int container_mem_lock(struct lxc_container *c)
342 {
343 return lxclock(c->privlock, 0);
344 }
345
346 void container_mem_unlock(struct lxc_container *c)
347 {
348 lxcunlock(c->privlock);
349 }
350
351 int container_disk_lock(struct lxc_container *c)
352 {
353 int ret;
354
355 ret = lxclock(c->privlock, 0);
356 if (ret < 0)
357 return ret;
358
359 ret = lxclock(c->slock, 0);
360 if (ret < 0) {
361 lxcunlock(c->privlock);
362 return ret;
363 }
364
365 return 0;
366 }
367
368 void container_disk_unlock(struct lxc_container *c)
369 {
370 lxcunlock(c->slock);
371 lxcunlock(c->privlock);
372 }