1 /* SPDX-License-Identifier: LGPL-2.1+ */
15 #include <lxc/lxccontainer.h>
22 #ifdef MUTEX_DEBUGGING
26 #define MAX_STACKDEPTH 25
28 lxc_log_define(lxclock
, lxc
);
30 #ifdef MUTEX_DEBUGGING
31 static pthread_mutex_t thread_mutex
= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
;
33 static inline void dump_stacktrace(void)
35 void *array
[MAX_STACKDEPTH
];
40 size
= backtrace(array
, MAX_STACKDEPTH
);
41 strings
= backtrace_symbols(array
, size
);
43 /* Using fprintf here as our logging module is not thread safe. */
44 fprintf(stderr
, "\tObtained %zu stack frames\n", size
);
46 for (i
= 0; i
< size
; i
++)
47 fprintf(stderr
, "\t\t%s\n", strings
[i
]);
52 static pthread_mutex_t thread_mutex
= PTHREAD_MUTEX_INITIALIZER
;
54 static inline void dump_stacktrace(void) {;}
57 static void lock_mutex(pthread_mutex_t
*l
)
61 ret
= pthread_mutex_lock(l
);
63 SYSERROR("Failed to acquire mutex");
69 static void unlock_mutex(pthread_mutex_t
*l
)
73 ret
= pthread_mutex_unlock(l
);
75 SYSERROR("Failed to release mutex");
81 static char *lxclock_name(const char *p
, const char *n
)
88 * "/run" + "/lxc/lock/$lxcpath/$lxcname + '\0' if root
90 * $XDG_RUNTIME_DIR + "/lxc/lock/$lxcpath/$lxcname + '\0' if non-root
93 /* length of "/lxc/lock/" + $lxcpath + "/" + "." + $lxcname + '\0' */
94 len
= STRLITERALLEN("/lxc/lock/") + strlen(n
) + strlen(p
) + 3;
96 rundir
= get_rundir();
100 len
+= strlen(rundir
);
108 ret
= snprintf(dest
, len
, "%s/lxc/lock/%s", rundir
, p
);
109 if (ret
< 0 || (size_t)ret
>= len
) {
115 ret
= mkdir_p(dest
, 0755);
122 ret
= snprintf(dest
, len
, "%s/lxc/lock/%s/.%s", rundir
, p
, n
);
124 if (ret
< 0 || (size_t)ret
>= len
) {
132 static sem_t
*lxc_new_unnamed_sem(void)
137 s
= malloc(sizeof(*s
));
141 ret
= sem_init(s
, 0, 1);
150 struct lxc_lock
*lxc_newlock(const char *lxcpath
, const char *name
)
154 l
= malloc(sizeof(*l
));
159 l
->type
= LXC_LOCK_ANON_SEM
;
160 l
->u
.sem
= lxc_new_unnamed_sem();
169 l
->type
= LXC_LOCK_FLOCK
;
170 l
->u
.f
.fname
= lxclock_name(lxcpath
, name
);
183 int lxclock(struct lxc_lock
*l
, int timeout
)
186 int ret
= -1, saved_errno
= errno
;
189 case LXC_LOCK_ANON_SEM
:
191 ret
= sem_wait(l
->u
.sem
);
197 ret
= clock_gettime(CLOCK_REALTIME
, &ts
);
203 ts
.tv_sec
+= timeout
;
204 ret
= sem_timedwait(l
->u
.sem
, &ts
);
213 ERROR("Timeouts are not supported with file locks");
218 ERROR("No filename set for file lock");
222 if (l
->u
.f
.fd
== -1) {
223 l
->u
.f
.fd
= open(l
->u
.f
.fname
, O_CREAT
| O_RDWR
| O_NOFOLLOW
| O_CLOEXEC
| O_NOCTTY
, S_IWUSR
| S_IRUSR
);
224 if (l
->u
.f
.fd
== -1) {
225 SYSERROR("Failed to open \"%s\"", l
->u
.f
.fname
);
231 memset(&lk
, 0, sizeof(struct flock
));
234 lk
.l_whence
= SEEK_SET
;
236 ret
= fcntl(l
->u
.f
.fd
, F_OFD_SETLKW
, &lk
);
239 ret
= flock(l
->u
.f
.fd
, LOCK_EX
);
251 int lxcunlock(struct lxc_lock
*l
)
254 int ret
= 0, saved_errno
= errno
;
257 case LXC_LOCK_ANON_SEM
:
261 ret
= sem_post(l
->u
.sem
);
267 if (l
->u
.f
.fd
!= -1) {
268 memset(&lk
, 0, sizeof(struct flock
));
271 lk
.l_whence
= SEEK_SET
;
273 ret
= fcntl(l
->u
.f
.fd
, F_OFD_SETLK
, &lk
);
276 ret
= flock(l
->u
.f
.fd
, LOCK_EX
| LOCK_NB
);
294 * lxc_putlock() is only called when a container_new() fails,
295 * or during container_put(), which is already guaranteed to
296 * only be done by one task.
297 * So the only exclusion we need to provide here is for regular
298 * thread safety (i.e. file descriptor table changes).
300 void lxc_putlock(struct lxc_lock
*l
)
306 case LXC_LOCK_ANON_SEM
:
308 sem_destroy(l
->u
.sem
);
315 if (l
->u
.f
.fd
!= -1) {
329 void process_lock(void)
331 lock_mutex(&thread_mutex
);
334 void process_unlock(void)
336 unlock_mutex(&thread_mutex
);
339 int container_mem_lock(struct lxc_container
*c
)
341 return lxclock(c
->privlock
, 0);
344 void container_mem_unlock(struct lxc_container
*c
)
346 lxcunlock(c
->privlock
);
349 int container_disk_lock(struct lxc_container
*c
)
353 ret
= lxclock(c
->privlock
, 0);
357 ret
= lxclock(c
->slock
, 0);
359 lxcunlock(c
->privlock
);
366 void container_disk_unlock(struct lxc_container
*c
)
369 lxcunlock(c
->privlock
);