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