]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/lxclock.c
Merge pull request #3956 from brauner/2021-08-27.list
[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
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 (int 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 __do_free char *dest = NULL, *rundir = NULL;
84 int ret;
85 size_t len;
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 return NULL;
105
106 ret = strnprintf(dest, len, "%s/lxc/lock/%s", rundir, p);
107 if (ret < 0)
108 return NULL;
109
110 ret = mkdir_p(dest, 0755);
111 if (ret < 0)
112 return NULL;
113
114 ret = strnprintf(dest, len, "%s/lxc/lock/%s/.%s", rundir, p, n);
115 if (ret < 0)
116 return NULL;
117
118 return move_ptr(dest);
119 }
120
121 static sem_t *lxc_new_unnamed_sem(void)
122 {
123 __do_free sem_t *s = NULL;
124 int ret;
125
126 s = malloc(sizeof(*s));
127 if (!s)
128 return ret_set_errno(NULL, ENOMEM);
129
130 ret = sem_init(s, 0, 1);
131 if (ret < 0)
132 return NULL;
133
134 return move_ptr(s);
135 }
136
137 struct lxc_lock *lxc_newlock(const char *lxcpath, const char *name)
138 {
139 __do_free struct lxc_lock *l = NULL;
140
141 l = zalloc(sizeof(*l));
142 if (!l)
143 return ret_set_errno(NULL, ENOMEM);
144
145 if (name) {
146 l->type = LXC_LOCK_FLOCK;
147 l->u.f.fname = lxclock_name(lxcpath, name);
148 if (!l->u.f.fname)
149 return ret_set_errno(NULL, ENOMEM);
150 l->u.f.fd = -EBADF;
151 } else {
152 l->type = LXC_LOCK_ANON_SEM;
153 l->u.sem = lxc_new_unnamed_sem();
154 if (!l->u.sem)
155 return ret_set_errno(NULL, ENOMEM);
156 }
157
158 return move_ptr(l);
159 }
160
161 int lxclock(struct lxc_lock *l, int timeout)
162 {
163 int ret = -1;
164 struct flock lk;
165
166 switch (l->type) {
167 case LXC_LOCK_ANON_SEM:
168 if (!timeout) {
169 ret = sem_wait(l->u.sem);
170 } else {
171 struct timespec ts;
172
173 ret = clock_gettime(CLOCK_REALTIME, &ts);
174 if (ret < 0)
175 return -2;
176
177 ts.tv_sec += timeout;
178 ret = sem_timedwait(l->u.sem, &ts);
179 }
180
181 break;
182 case LXC_LOCK_FLOCK:
183 if (timeout)
184 return log_error(-2, "Timeouts are not supported with file locks");
185
186 if (!l->u.f.fname)
187 return log_error(-2, "No filename set for file lock");
188
189 if (l->u.f.fd < 0) {
190 l->u.f.fd = open(l->u.f.fname, O_CREAT | O_RDWR | O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, S_IWUSR | S_IRUSR);
191 if (l->u.f.fd < 0)
192 return log_error_errno(-2, errno, "Failed to open \"%s\"", l->u.f.fname);
193 }
194
195 memset(&lk, 0, sizeof(struct flock));
196
197 lk.l_type = F_WRLCK;
198 lk.l_whence = SEEK_SET;
199
200 ret = fcntl(l->u.f.fd, F_OFD_SETLKW, &lk);
201 if (ret < 0 && errno == EINVAL)
202 ret = flock(l->u.f.fd, LOCK_EX);
203 break;
204 default:
205 return ret_set_errno(-1, EINVAL);
206 }
207
208 return ret;
209 }
210
211 int lxcunlock(struct lxc_lock *l)
212 {
213 struct flock lk;
214 int ret = 0;
215
216 switch (l->type) {
217 case LXC_LOCK_ANON_SEM:
218 if (!l->u.sem)
219 return -2;
220
221 ret = sem_post(l->u.sem);
222 break;
223 case LXC_LOCK_FLOCK:
224 if (l->u.f.fd < 0)
225 return -2;
226
227 memset(&lk, 0, sizeof(struct flock));
228
229 lk.l_type = F_UNLCK;
230 lk.l_whence = SEEK_SET;
231
232 ret = fcntl(l->u.f.fd, F_OFD_SETLK, &lk);
233 if (ret < 0 && errno == EINVAL)
234 ret = flock(l->u.f.fd, LOCK_EX | LOCK_NB);
235
236 close_prot_errno_disarm(l->u.f.fd);
237 break;
238 default:
239 return ret_set_errno(-1, EINVAL);
240 }
241
242 return ret;
243 }
244
245 /*
246 * lxc_putlock() is only called when a container_new() fails,
247 * or during container_put(), which is already guaranteed to
248 * only be done by one task.
249 * So the only exclusion we need to provide here is for regular
250 * thread safety (i.e. file descriptor table changes).
251 */
252 void lxc_putlock(struct lxc_lock *l)
253 {
254 if (!l)
255 return;
256
257 switch (l->type) {
258 case LXC_LOCK_ANON_SEM:
259 if (l->u.sem) {
260 sem_destroy(l->u.sem);
261 free_disarm(l->u.sem);
262 }
263 break;
264 case LXC_LOCK_FLOCK:
265 close_prot_errno_disarm(l->u.f.fd);
266 free_disarm(l->u.f.fname);
267 break;
268 }
269
270 free(l);
271 }
272
273 void process_lock(void)
274 {
275 lock_mutex(&thread_mutex);
276 }
277
278 void process_unlock(void)
279 {
280 unlock_mutex(&thread_mutex);
281 }
282
283 int container_mem_lock(struct lxc_container *c)
284 {
285 return lxclock(c->privlock, 0);
286 }
287
288 void container_mem_unlock(struct lxc_container *c)
289 {
290 lxcunlock(c->privlock);
291 }
292
293 int container_disk_lock(struct lxc_container *c)
294 {
295 int ret;
296
297 ret = lxclock(c->privlock, 0);
298 if (ret < 0)
299 return ret;
300
301 ret = lxclock(c->slock, 0);
302 if (ret < 0) {
303 lxcunlock(c->privlock);
304 return ret;
305 }
306
307 return 0;
308 }
309
310 void container_disk_unlock(struct lxc_container *c)
311 {
312 lxcunlock(c->slock);
313 lxcunlock(c->privlock);
314 }