]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/lxclock.c
github: Update for main branch
[mirror_lxc.git] / src / lxc / lxclock.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include "config.h"
4
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <malloc.h>
8 #include <pthread.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <sys/file.h>
12 #include <unistd.h>
13
14 #include "lxc.h"
15
16 #include "log.h"
17 #include "lxclock.h"
18 #include "memory_utils.h"
19 #include "utils.h"
20
21 #ifdef MUTEX_DEBUGGING
22 #include <execinfo.h>
23 #endif
24
25 #define MAX_STACKDEPTH 25
26
27 lxc_log_define(lxclock, lxc);
28
29 #ifdef MUTEX_DEBUGGING
30 static pthread_mutex_t thread_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
31
32 static inline void dump_stacktrace(void)
33 {
34 void *array[MAX_STACKDEPTH];
35 size_t size;
36 char **strings;
37
38 size = backtrace(array, MAX_STACKDEPTH);
39 strings = backtrace_symbols(array, size);
40
41 /* Using fprintf here as our logging module is not thread safe. */
42 fprintf(stderr, "\tObtained %zu stack frames\n", size);
43
44 for (int i = 0; i < size; i++)
45 fprintf(stderr, "\t\t%s\n", strings[i]);
46
47 free(strings);
48 }
49 #else
50 static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER;
51
52 static inline void dump_stacktrace(void) {;}
53 #endif
54
55 static void lock_mutex(pthread_mutex_t *l)
56 {
57 int ret;
58
59 ret = pthread_mutex_lock(l);
60 if (ret != 0) {
61 SYSERROR("Failed to acquire mutex");
62 dump_stacktrace();
63 _exit(EXIT_FAILURE);
64 }
65 }
66
67 static void unlock_mutex(pthread_mutex_t *l)
68 {
69 int ret;
70
71 ret = pthread_mutex_unlock(l);
72 if (ret != 0) {
73 SYSERROR("Failed to release mutex");
74 dump_stacktrace();
75 _exit(EXIT_FAILURE);
76 }
77 }
78
79 static char *lxclock_name(const char *p, const char *n)
80 {
81 __do_free char *dest = NULL, *rundir = NULL;
82 int ret;
83 size_t len;
84
85 /* lockfile will be:
86 * "/run" + "/lxc/lock/$lxcpath/$lxcname + '\0' if root
87 * or
88 * $XDG_RUNTIME_DIR + "/lxc/lock/$lxcpath/$lxcname + '\0' if non-root
89 */
90
91 /* length of "/lxc/lock/" + $lxcpath + "/" + "." + $lxcname + '\0' */
92 len = STRLITERALLEN("/lxc/lock/") + strlen(n) + strlen(p) + 3;
93
94 rundir = get_rundir();
95 if (!rundir)
96 return NULL;
97
98 len += strlen(rundir);
99
100 dest = malloc(len);
101 if (!dest)
102 return NULL;
103
104 ret = strnprintf(dest, len, "%s/lxc/lock/%s", rundir, p);
105 if (ret < 0)
106 return NULL;
107
108 ret = lxc_mkdir_p(dest, 0755);
109 if (ret < 0)
110 return NULL;
111
112 ret = strnprintf(dest, len, "%s/lxc/lock/%s/.%s", rundir, p, n);
113 if (ret < 0)
114 return NULL;
115
116 return move_ptr(dest);
117 }
118
119 static sem_t *lxc_new_unnamed_sem(void)
120 {
121 __do_free sem_t *s = NULL;
122 int ret;
123
124 s = malloc(sizeof(*s));
125 if (!s)
126 return ret_set_errno(NULL, ENOMEM);
127
128 ret = sem_init(s, 0, 1);
129 if (ret < 0)
130 return NULL;
131
132 return move_ptr(s);
133 }
134
135 struct lxc_lock *lxc_newlock(const char *lxcpath, const char *name)
136 {
137 __do_free struct lxc_lock *l = NULL;
138
139 l = zalloc(sizeof(*l));
140 if (!l)
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 {
150 l->type = LXC_LOCK_ANON_SEM;
151 l->u.sem = lxc_new_unnamed_sem();
152 if (!l->u.sem)
153 return ret_set_errno(NULL, ENOMEM);
154 }
155
156 return move_ptr(l);
157 }
158
159 int lxclock(struct lxc_lock *l, int timeout)
160 {
161 int ret = -1;
162 struct flock lk;
163
164 switch (l->type) {
165 case LXC_LOCK_ANON_SEM:
166 if (!timeout) {
167 ret = sem_wait(l->u.sem);
168 } else {
169 struct timespec ts;
170
171 ret = clock_gettime(CLOCK_REALTIME, &ts);
172 if (ret < 0)
173 return -2;
174
175 ts.tv_sec += timeout;
176 ret = sem_timedwait(l->u.sem, &ts);
177 }
178
179 break;
180 case LXC_LOCK_FLOCK:
181 if (timeout)
182 return log_error(-2, "Timeouts are not supported with file locks");
183
184 if (!l->u.f.fname)
185 return log_error(-2, "No filename set for file lock");
186
187 if (l->u.f.fd < 0) {
188 l->u.f.fd = open(l->u.f.fname, O_CREAT | O_RDWR | O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, S_IWUSR | S_IRUSR);
189 if (l->u.f.fd < 0)
190 return log_error_errno(-2, errno, "Failed to open \"%s\"", l->u.f.fname);
191 }
192
193 memset(&lk, 0, sizeof(struct flock));
194
195 lk.l_type = F_WRLCK;
196 lk.l_whence = SEEK_SET;
197
198 ret = fcntl(l->u.f.fd, F_OFD_SETLKW, &lk);
199 if (ret < 0 && errno == EINVAL)
200 ret = flock(l->u.f.fd, LOCK_EX);
201 break;
202 default:
203 return ret_set_errno(-1, EINVAL);
204 }
205
206 return ret;
207 }
208
209 int lxcunlock(struct lxc_lock *l)
210 {
211 struct flock lk;
212 int ret = 0;
213
214 switch (l->type) {
215 case LXC_LOCK_ANON_SEM:
216 if (!l->u.sem)
217 return -2;
218
219 ret = sem_post(l->u.sem);
220 break;
221 case LXC_LOCK_FLOCK:
222 if (l->u.f.fd < 0)
223 return -2;
224
225 memset(&lk, 0, sizeof(struct flock));
226
227 lk.l_type = F_UNLCK;
228 lk.l_whence = SEEK_SET;
229
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);
233
234 close_prot_errno_disarm(l->u.f.fd);
235 break;
236 default:
237 return ret_set_errno(-1, EINVAL);
238 }
239
240 return ret;
241 }
242
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 */
250 void lxc_putlock(struct lxc_lock *l)
251 {
252 if (!l)
253 return;
254
255 switch (l->type) {
256 case LXC_LOCK_ANON_SEM:
257 if (l->u.sem) {
258 sem_destroy(l->u.sem);
259 free_disarm(l->u.sem);
260 }
261 break;
262 case LXC_LOCK_FLOCK:
263 close_prot_errno_disarm(l->u.f.fd);
264 free_disarm(l->u.f.fname);
265 break;
266 }
267
268 free(l);
269 }
270
271 void process_lock(void)
272 {
273 lock_mutex(&thread_mutex);
274 }
275
276 void process_unlock(void)
277 {
278 unlock_mutex(&thread_mutex);
279 }
280
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
295 ret = lxclock(c->privlock, 0);
296 if (ret < 0)
297 return ret;
298
299 ret = lxclock(c->slock, 0);
300 if (ret < 0) {
301 lxcunlock(c->privlock);
302 return ret;
303 }
304
305 return 0;
306 }
307
308 void container_disk_unlock(struct lxc_container *c)
309 {
310 lxcunlock(c->slock);
311 lxcunlock(c->privlock);
312 }