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