1 /*
2 * Copyright (C) 2009 Citrix Ltd.
3 * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published
7 * by the Free Software Foundation; version 2.1 only. with the special
8 * exception on linking described in file LICENSE.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 */
15
16 #include "libxl_osdeps.h" /* must come before any other headers */
17
18 #include "libxl_internal.h"
19 #include "libxl_arch.h"
20
libxl__alloc_failed(libxl_ctx * ctx,const char * func,size_t nmemb,size_t size)21 void libxl__alloc_failed(libxl_ctx *ctx, const char *func,
22 size_t nmemb, size_t size) {
23 #define M "libxl: FATAL ERROR: memory allocation failure"
24 #define M_SIZE M " (%s, %lu x %lu)\n"
25 #define M_NSIZE M " (%s)\n"
26 if (size) {
27 libxl__log(ctx, XTL_CRITICAL, ENOMEM, 0, 0, func, INVALID_DOMID,
28 M_SIZE, func, (unsigned long)nmemb, (unsigned long)size);
29 fprintf(stderr, M_SIZE, func, (unsigned long)nmemb,
30 (unsigned long)size);
31 } else {
32 libxl__log(ctx, XTL_CRITICAL, ENOMEM, 0, 0, func, INVALID_DOMID,
33 M_NSIZE, func);
34 fprintf(stderr, M_NSIZE, func);
35
36 }
37
38 fflush(stderr);
39 abort();
40 #undef M_NSIZE
41 #undef M_SIZE
42 #undef M
43 }
44
libxl__ptr_add(libxl__gc * gc,void * ptr)45 void libxl__ptr_add(libxl__gc *gc, void *ptr)
46 {
47 int i;
48
49 if (!libxl__gc_is_real(gc))
50 return;
51
52 if (!ptr)
53 return;
54
55 /* fast case: we have space in the array for storing the pointer */
56 for (i = 0; i < gc->alloc_maxsize; i++) {
57 if (!gc->alloc_ptrs[i]) {
58 gc->alloc_ptrs[i] = ptr;
59 return;
60 }
61 }
62 int new_maxsize = gc->alloc_maxsize * 2 + 25;
63 assert(new_maxsize < INT_MAX / sizeof(void*) / 2);
64 gc->alloc_ptrs = realloc(gc->alloc_ptrs, new_maxsize * sizeof(void *));
65 if (!gc->alloc_ptrs)
66 libxl__alloc_failed(CTX, __func__, new_maxsize, sizeof(void*));
67
68 gc->alloc_ptrs[gc->alloc_maxsize++] = ptr;
69
70 while (gc->alloc_maxsize < new_maxsize)
71 gc->alloc_ptrs[gc->alloc_maxsize++] = 0;
72
73 return;
74 }
75
libxl__free_all(libxl__gc * gc)76 void libxl__free_all(libxl__gc *gc)
77 {
78 void *ptr;
79 int i;
80
81 assert(libxl__gc_is_real(gc));
82
83 for (i = 0; i < gc->alloc_maxsize; i++) {
84 ptr = gc->alloc_ptrs[i];
85 gc->alloc_ptrs[i] = NULL;
86 free(ptr);
87 }
88 free(gc->alloc_ptrs);
89 gc->alloc_ptrs = 0;
90 gc->alloc_maxsize = 0;
91 }
92
libxl__malloc(libxl__gc * gc,size_t size)93 void *libxl__malloc(libxl__gc *gc, size_t size)
94 {
95 void *ptr = malloc(size);
96 if (!ptr) libxl__alloc_failed(CTX, __func__, size, 1);
97
98 libxl__ptr_add(gc, ptr);
99 return ptr;
100 }
101
libxl__zalloc(libxl__gc * gc,size_t size)102 void *libxl__zalloc(libxl__gc *gc, size_t size)
103 {
104 void *ptr = calloc(size, 1);
105 if (!ptr) libxl__alloc_failed(CTX, __func__, size, 1);
106
107 libxl__ptr_add(gc, ptr);
108 return ptr;
109 }
110
libxl__calloc(libxl__gc * gc,size_t nmemb,size_t size)111 void *libxl__calloc(libxl__gc *gc, size_t nmemb, size_t size)
112 {
113 void *ptr = calloc(nmemb, size);
114 if (!ptr) libxl__alloc_failed(CTX, __func__, nmemb, size);
115
116 libxl__ptr_add(gc, ptr);
117 return ptr;
118 }
119
libxl__realloc(libxl__gc * gc,void * ptr,size_t new_size)120 void *libxl__realloc(libxl__gc *gc, void *ptr, size_t new_size)
121 {
122 void *new_ptr = realloc(ptr, new_size);
123 int i = 0;
124
125 if (new_ptr == NULL && new_size != 0)
126 libxl__alloc_failed(CTX, __func__, new_size, 1);
127
128 if (ptr == NULL) {
129 libxl__ptr_add(gc, new_ptr);
130 } else if (new_ptr != ptr && libxl__gc_is_real(gc)) {
131 for (i = 0; ; i++) {
132 assert(i < gc->alloc_maxsize);
133 if (gc->alloc_ptrs[i] == ptr) {
134 gc->alloc_ptrs[i] = new_ptr;
135 break;
136 }
137 }
138 }
139
140 return new_ptr;
141 }
142
libxl__vsprintf(libxl__gc * gc,const char * fmt,va_list ap)143 char *libxl__vsprintf(libxl__gc *gc, const char *fmt, va_list ap)
144 {
145 char *s;
146 va_list aq;
147 int ret;
148
149 va_copy(aq, ap);
150 ret = vsnprintf(NULL, 0, fmt, aq);
151 va_end(aq);
152
153 assert(ret >= 0);
154
155 s = libxl__zalloc(gc, ret + 1);
156 va_copy(aq, ap);
157 ret = vsnprintf(s, ret + 1, fmt, aq);
158 va_end(aq);
159
160 return s;
161 }
162
libxl__sprintf(libxl__gc * gc,const char * fmt,...)163 char *libxl__sprintf(libxl__gc *gc, const char *fmt, ...)
164 {
165 char *s;
166 va_list ap;
167
168 va_start(ap, fmt);
169 s = libxl__vsprintf(gc, fmt, ap);
170 va_end(ap);
171
172 return s;
173 }
174
libxl__strdup(libxl__gc * gc,const char * c)175 char *libxl__strdup(libxl__gc *gc, const char *c)
176 {
177 char *s;
178
179 if (!c) return NULL;
180
181 s = strdup(c);
182
183 if (!s) libxl__alloc_failed(CTX, __func__, strlen(c), 1);
184
185 libxl__ptr_add(gc, s);
186
187 return s;
188 }
189
libxl__strndup(libxl__gc * gc,const char * c,size_t n)190 char *libxl__strndup(libxl__gc *gc, const char *c, size_t n)
191 {
192 char *s;
193
194 if (!c) return NULL;
195
196 s = strndup(c, n);
197
198 if (!s) libxl__alloc_failed(CTX, __func__, n, 1);
199
200 libxl__ptr_add(gc, s);
201
202 return s;
203 }
204
libxl__dirname(libxl__gc * gc,const char * s)205 char *libxl__dirname(libxl__gc *gc, const char *s)
206 {
207 char *c = strrchr(s, '/');
208
209 if (!c)
210 return NULL;
211
212 return libxl__strndup(gc, s, c - s);
213 }
214
libxl__logv(libxl_ctx * ctx,xentoollog_level msglevel,int errnoval,const char * file,int line,const char * func,uint32_t domid,const char * fmt,va_list ap)215 void libxl__logv(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval,
216 const char *file, int line, const char *func,
217 uint32_t domid, const char *fmt, va_list ap)
218 {
219 /* WARNING this function may not call any libxl-provided
220 * memory allocation function, as those may
221 * call libxl__alloc_failed which calls libxl__logv. */
222 char *enomem = "[out of memory formatting log message]";
223 char *base = NULL;
224 int rc, esave;
225 char fileline[256];
226 char domain[256];
227
228 esave = errno;
229
230 rc = vasprintf(&base, fmt, ap);
231 if (rc<0) { base = enomem; goto x; }
232
233 fileline[0] = 0;
234 if (file) snprintf(fileline, sizeof(fileline), "%s:%d",file,line);
235 fileline[sizeof(fileline)-1] = 0;
236
237 domain[0] = 0;
238 if (libxl_domid_valid_guest(domid))
239 snprintf(domain, sizeof(domain), "Domain %"PRIu32":", domid);
240 x:
241 xtl_log(ctx->lg, msglevel, errnoval, "libxl",
242 "%s%s%s%s%s" "%s",
243 fileline, func&&file?":":"", func?func:"", func||file?": ":"",
244 domain, base);
245 if (base != enomem) free(base);
246 errno = esave;
247 }
248
libxl__log(libxl_ctx * ctx,xentoollog_level msglevel,int errnoval,const char * file,int line,const char * func,uint32_t domid,const char * fmt,...)249 void libxl__log(libxl_ctx *ctx, xentoollog_level msglevel, int errnoval,
250 const char *file, int line, const char *func,
251 uint32_t domid, const char *fmt, ...)
252 {
253 va_list ap;
254 va_start(ap, fmt);
255 libxl__logv(ctx, msglevel, errnoval, file, line, func, domid, fmt, ap);
256 va_end(ap);
257 }
258
libxl__abs_path(libxl__gc * gc,const char * s,const char * path)259 char *libxl__abs_path(libxl__gc *gc, const char *s, const char *path)
260 {
261 if (s[0] == '/') return libxl__strdup(gc, s);
262 return GCSPRINTF("%s/%s", path, s);
263 }
264
265
libxl__file_reference_map(libxl__file_reference * f)266 int libxl__file_reference_map(libxl__file_reference *f)
267 {
268 struct stat st_buf;
269 int ret, fd;
270 void *data;
271
272 if (f->mapped)
273 return 0;
274
275 fd = open(f->path, O_RDONLY);
276 if (fd < 0)
277 return ERROR_FAIL;
278
279 ret = fstat(fd, &st_buf);
280 if (ret < 0)
281 goto out;
282
283 ret = -1;
284 data = mmap(NULL, st_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
285 if (data == MAP_FAILED)
286 goto out;
287
288 f->mapped = 1;
289 f->data = data;
290 f->size = st_buf.st_size;
291
292 ret = 0;
293 out:
294 close(fd);
295
296 return ret == 0 ? 0 : ERROR_FAIL;
297 }
298
libxl__file_reference_unmap(libxl__file_reference * f)299 int libxl__file_reference_unmap(libxl__file_reference *f)
300 {
301 int ret;
302
303 if (!f->mapped)
304 return 0;
305
306 ret = munmap(f->data, f->size);
307 if (ret == 0) {
308 f->mapped = 0;
309 f->data = NULL;
310 f->size = 0;
311 return 0;
312 }
313
314 return ERROR_FAIL;
315 }
316
libxl__parse_mac(const char * s,libxl_mac mac)317 _hidden int libxl__parse_mac(const char *s, libxl_mac mac)
318 {
319 const char *tok;
320 char *endptr;
321 int i;
322
323 for (i = 0, tok = s; *tok && (i < 6); ++i, tok = endptr) {
324 mac[i] = strtol(tok, &endptr, 16);
325 if (endptr != (tok + 2) || (*endptr != '\0' && *endptr != ':') )
326 return ERROR_INVAL;
327 if (*endptr == ':')
328 endptr++;
329 }
330 if ( i != 6 )
331 return ERROR_INVAL;
332
333 return 0;
334 }
335
libxl__compare_macs(libxl_mac * a,libxl_mac * b)336 _hidden int libxl__compare_macs(libxl_mac *a, libxl_mac *b)
337 {
338 int i;
339
340 for (i = 0; i<6; i++) {
341 if ((*a)[i] != (*b)[i])
342 return (*a)[i] - (*b)[i];
343 }
344
345 return 0;
346 }
347
libxl__mac_is_default(libxl_mac * mac)348 _hidden int libxl__mac_is_default(libxl_mac *mac)
349 {
350 return (!(*mac)[0] && !(*mac)[1] && !(*mac)[2] &&
351 !(*mac)[3] && !(*mac)[4] && !(*mac)[5]);
352 }
353
libxl__init_recursive_mutex(libxl_ctx * ctx,pthread_mutex_t * lock)354 _hidden int libxl__init_recursive_mutex(libxl_ctx *ctx, pthread_mutex_t *lock)
355 {
356 GC_INIT(ctx);
357 pthread_mutexattr_t attr;
358 int rc = 0;
359
360 if (pthread_mutexattr_init(&attr) != 0) {
361 LOGE(ERROR, "Failed to init mutex attributes");
362 rc = ERROR_FAIL;
363 goto out;
364 }
365 if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) {
366 LOGE(ERROR, "Failed to set mutex attributes");
367 rc = ERROR_FAIL;
368 goto out;
369 }
370 if (pthread_mutex_init(lock, &attr) != 0) {
371 LOGE(ERROR, "Failed to init mutex");
372 rc = ERROR_FAIL;
373 goto out;
374 }
375 out:
376 pthread_mutexattr_destroy(&attr);
377 GC_FREE;
378 return rc;
379 }
380
libxl__device_model_version_running(libxl__gc * gc,uint32_t domid)381 int libxl__device_model_version_running(libxl__gc *gc, uint32_t domid)
382 {
383 char *path = NULL;
384 char *dm_version = NULL;
385 libxl_device_model_version value;
386
387 path = libxl__xs_libxl_path(gc, domid);
388 path = GCSPRINTF("%s/dm-version", path);
389 dm_version = libxl__xs_read(gc, XBT_NULL, path);
390
391 if (!dm_version ||
392 libxl_device_model_version_from_string(dm_version, &value) < 0) {
393 LOGD(ERROR, domid, "fatal: %s contain a wrong value (%s)", path, dm_version);
394 return -1;
395 }
396 return value;
397 }
398
399 /* Portability note: this lock utilises flock(2) so a proper implementation of
400 * flock(2) is required.
401 */
libxl__lock_file(libxl__gc * gc,const char * lockfile)402 libxl__flock *libxl__lock_file(libxl__gc *gc, const char *lockfile)
403 {
404 libxl__flock *lock;
405 int fd;
406 struct stat stab, fstab;
407
408 lock = libxl__zalloc(NOGC, sizeof(libxl__flock));
409 lock->path = libxl__strdup(NOGC, lockfile);
410
411 while (true) {
412 libxl__carefd_begin();
413 fd = open(lockfile, O_RDWR|O_CREAT, 0666);
414 if (fd < 0)
415 LOGE(ERROR,
416 "cannot open lockfile %s, errno=%d",
417 lockfile, errno);
418 lock->carefd = libxl__carefd_opened(CTX, fd);
419 if (fd < 0) goto out;
420
421 /* Lock the file in exclusive mode, wait indefinitely to
422 * acquire the lock
423 */
424 while (flock(fd, LOCK_EX)) {
425 switch (errno) {
426 case EINTR:
427 /* Signal received, retry */
428 continue;
429 default:
430 /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */
431 LOGE(ERROR,
432 "unexpected error while trying to lock %s, fd=%d, errno=%d",
433 lockfile, fd, errno);
434 goto out;
435 }
436 }
437
438 if (fstat(fd, &fstab)) {
439 LOGE(ERROR, "cannot fstat %s, fd=%d, errno=%d",
440 lockfile, fd, errno);
441 goto out;
442 }
443 if (stat(lockfile, &stab)) {
444 if (errno != ENOENT) {
445 LOGE(ERROR, "cannot stat %s, errno=%d", lockfile, errno);
446 goto out;
447 }
448 } else {
449 if (stab.st_dev == fstab.st_dev && stab.st_ino == fstab.st_ino)
450 break;
451 }
452
453 libxl__carefd_close(lock->carefd);
454 }
455
456 return lock;
457
458 out:
459 if (lock) libxl__unlock_file(lock);
460 return NULL;
461 }
462
libxl__unlock_file(libxl__flock * lock)463 void libxl__unlock_file(libxl__flock *lock)
464 {
465 /* It's important to unlink the file before closing fd to avoid
466 * the following race (if close before unlink):
467 *
468 * P1 LOCK P2 UNLOCK
469 * fd1 = open(lockfile)
470 * close(fd2)
471 * flock(fd1)
472 * fstat and stat check success
473 * unlink(lockfile)
474 * return lock
475 *
476 * In above case P1 thinks it has got hold of the lock but
477 * actually lock is released by P2 (lockfile unlinked).
478 */
479 if (lock->path) unlink(lock->path);
480 if (lock->carefd) libxl__carefd_close(lock->carefd);
481 free(lock->path);
482 free(lock);
483 }
484
libxl__lock_domain_userdata(libxl__gc * gc,uint32_t domid)485 libxl__flock *libxl__lock_domain_userdata(libxl__gc *gc, uint32_t domid)
486 {
487 const char *lockfile;
488 libxl__flock *lock;
489
490 lockfile = libxl__userdata_path(gc, domid, "domain-userdata-lock", "l");
491 if (!lockfile) return NULL;
492
493 lock = libxl__lock_file(gc, lockfile);
494
495 /* Check the domain is still there, if not we should release the
496 * lock and clean up.
497 */
498 if (libxl_domain_info(CTX, NULL, domid)) {
499 libxl__unlock_file(lock);
500 return NULL;
501 }
502
503 return lock;
504 }
505
libxl__lock_domid_history(libxl__gc * gc)506 libxl__flock *libxl__lock_domid_history(libxl__gc *gc)
507 {
508 const char *lockfile;
509
510 lockfile = libxl__domid_history_path(gc, ".lock");
511 if (!lockfile) return NULL;
512
513 return libxl__lock_file(gc, lockfile);
514 }
515
libxl__get_domain_configuration(libxl__gc * gc,uint32_t domid,libxl_domain_config * d_config)516 int libxl__get_domain_configuration(libxl__gc *gc, uint32_t domid,
517 libxl_domain_config *d_config)
518 {
519 uint8_t *data = NULL;
520 int rc, len;
521
522 rc = libxl__userdata_retrieve(gc, domid, "libxl-json", &data, &len);
523 if (rc) {
524 LOGEVD(ERROR, rc, domid,
525 "failed to retrieve domain configuration");
526 rc = ERROR_FAIL;
527 goto out;
528 }
529
530 if (len == 0) {
531 /* No logging, not necessary an error from caller's PoV. */
532 rc = ERROR_JSON_CONFIG_EMPTY;
533 goto out;
534 }
535 rc = libxl_domain_config_from_json(CTX, d_config, (const char *)data);
536
537 out:
538 free(data);
539 return rc;
540 }
541
libxl__set_domain_configuration(libxl__gc * gc,uint32_t domid,libxl_domain_config * d_config)542 int libxl__set_domain_configuration(libxl__gc *gc, uint32_t domid,
543 libxl_domain_config *d_config)
544 {
545 char *d_config_json;
546 int rc;
547
548 d_config_json = libxl_domain_config_to_json(CTX, d_config);
549 if (!d_config_json) {
550 LOGED(ERROR, domid,
551 "failed to convert domain configuration to JSON");
552 rc = ERROR_FAIL;
553 goto out;
554 }
555
556 rc = libxl__userdata_store(gc, domid, "libxl-json",
557 (const uint8_t *)d_config_json,
558 strlen(d_config_json) + 1 /* include '\0' */);
559 if (rc) {
560 LOGEVD(ERROR, rc, domid, "failed to store domain configuration");
561 rc = ERROR_FAIL;
562 goto out;
563 }
564
565 out:
566 free(d_config_json);
567 return rc;
568 }
569
libxl__update_domain_configuration(libxl__gc * gc,libxl_domain_config * dst,const libxl_domain_config * src)570 void libxl__update_domain_configuration(libxl__gc *gc,
571 libxl_domain_config *dst,
572 const libxl_domain_config *src)
573 {
574 int i, idx, num;
575 const libxl__device_type *dt;
576
577 for (idx = 0;; idx++) {
578 dt = device_type_tbl[idx];
579 if (!dt)
580 break;
581
582 num = *libxl__device_type_get_num(dt, src);
583 if (!dt->update_config || !num)
584 continue;
585
586 for (i = 0; i < num; i++)
587 dt->update_config(gc, libxl__device_type_get_elem(dt, dst, i),
588 libxl__device_type_get_elem(dt, src, i));
589 }
590
591 /* update guest UUID */
592 libxl_uuid_copy(CTX, &dst->c_info.uuid, &src->c_info.uuid);
593
594 /* video ram */
595 dst->b_info.video_memkb = src->b_info.video_memkb;
596
597 libxl__arch_update_domain_config(gc, dst, src);
598 }
599
ev_slowlock_init_internal(libxl__ev_slowlock * lock,const char * userdata_userid)600 static void ev_slowlock_init_internal(libxl__ev_slowlock *lock,
601 const char *userdata_userid)
602 {
603 libxl__ev_child_init(&lock->child);
604 lock->userdata_userid = userdata_userid;
605 lock->path = NULL;
606 lock->fd = -1;
607 lock->held = false;
608 }
609
libxl__ev_devlock_init(libxl__ev_slowlock * lock)610 void libxl__ev_devlock_init(libxl__ev_slowlock *lock)
611 {
612 ev_slowlock_init_internal(lock, "libxl-device-changes-lock");
613 }
614
libxl__ev_qmplock_init(libxl__ev_slowlock * lock)615 void libxl__ev_qmplock_init(libxl__ev_slowlock *lock)
616 {
617 ev_slowlock_init_internal(lock, "qmp-socket-lock");
618 }
619
620 static void ev_lock_prepare_fork(libxl__egc *egc, libxl__ev_slowlock *lock);
621 static void ev_lock_child_callback(libxl__egc *egc, libxl__ev_child *child,
622 pid_t pid, int status);
623
libxl__ev_slowlock_lock(libxl__egc * egc,libxl__ev_slowlock * lock)624 void libxl__ev_slowlock_lock(libxl__egc *egc, libxl__ev_slowlock *lock)
625 {
626 STATE_AO_GC(lock->ao);
627 const char *lockfile;
628
629 lockfile = libxl__userdata_path(gc, lock->domid,
630 lock->userdata_userid, "l");
631 if (!lockfile) goto out;
632 lock->path = libxl__strdup(NOGC, lockfile);
633
634 ev_lock_prepare_fork(egc, lock);
635 return;
636 out:
637 lock->callback(egc, lock, ERROR_LOCK_FAIL);
638 }
639
ev_lock_prepare_fork(libxl__egc * egc,libxl__ev_slowlock * lock)640 static void ev_lock_prepare_fork(libxl__egc *egc, libxl__ev_slowlock *lock)
641 {
642 STATE_AO_GC(lock->ao);
643 pid_t pid;
644 int fd;
645
646 /* Convenience aliases */
647 libxl_domid domid = lock->domid;
648 const char *lockfile = lock->path;
649
650 lock->fd = open(lockfile, O_RDWR|O_CREAT, 0666);
651 if (lock->fd < 0) {
652 LOGED(ERROR, domid, "cannot open lockfile %s", lockfile);
653 goto out;
654 }
655 fd = lock->fd;
656
657 /* Enable this optimisation only in releases, so the fork code is
658 * exercised while libxl is built with debug=y. */
659 #ifndef CONFIG_DEBUG
660 /*
661 * We try to grab the lock before forking as it is likely to be free.
662 * Even though we are supposed to CTX_UNLOCK before attempting to grab
663 * the ev_lock, it is fine to do a non-blocking request now with the
664 * CTX_LOCK held as if that fails we'll try again in a fork (CTX_UNLOCK
665 * will be called in libxl), that will avoid deadlocks.
666 */
667 int r = flock(fd, LOCK_EX | LOCK_NB);
668 if (!r) {
669 libxl_fd_set_cloexec(CTX, fd, 1);
670 /* We held a lock, no need to fork but we need to check it. */
671 ev_lock_child_callback(egc, &lock->child, 0, 0);
672 return;
673 }
674 #endif
675
676 pid = libxl__ev_child_fork(gc, &lock->child, ev_lock_child_callback);
677 if (pid < 0)
678 goto out;
679 if (!pid) {
680 /* child */
681 int exit_val = 0;
682
683 /* Lock the file in exclusive mode, wait indefinitely to
684 * acquire the lock */
685 while (flock(fd, LOCK_EX)) {
686 switch (errno) {
687 case EINTR:
688 /* Signal received, retry */
689 continue;
690 default:
691 /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */
692 LOGED(ERROR, domid,
693 "unexpected error while trying to lock %s, fd=%d",
694 lockfile, fd);
695 exit_val = 1;
696 break;
697 }
698 }
699 _exit(exit_val);
700 }
701
702 /* Now that the child has the fd, set cloexec in the parent to prevent
703 * more leakage than necessary */
704 libxl_fd_set_cloexec(CTX, fd, 1);
705 return;
706 out:
707 libxl__ev_slowlock_unlock(gc, lock);
708 lock->callback(egc, lock, ERROR_LOCK_FAIL);
709 }
710
ev_lock_child_callback(libxl__egc * egc,libxl__ev_child * child,pid_t pid,int status)711 static void ev_lock_child_callback(libxl__egc *egc, libxl__ev_child *child,
712 pid_t pid, int status)
713 {
714 EGC_GC;
715 libxl__ev_slowlock *lock = CONTAINER_OF(child, *lock, child);
716 struct stat stab, fstab;
717 int rc = ERROR_LOCK_FAIL;
718
719 /* Convenience aliases */
720 int fd = lock->fd;
721 const char *lockfile = lock->path;
722 libxl_domid domid = lock->domid;
723
724 if (status) {
725 libxl_report_child_exitstatus(CTX, XTL_ERROR, "flock child",
726 pid, status);
727 goto out;
728 }
729
730 if (fstat(fd, &fstab)) {
731 LOGED(ERROR, domid, "cannot fstat %s, fd=%d", lockfile, fd);
732 goto out;
733 }
734 if (stat(lockfile, &stab)) {
735 if (errno != ENOENT) {
736 LOGED(ERROR, domid, "cannot stat %s", lockfile);
737 goto out;
738 }
739 } else {
740 if (stab.st_dev == fstab.st_dev && stab.st_ino == fstab.st_ino) {
741 /* We held the lock */
742 lock->held = true;
743 rc = 0;
744 goto out;
745 }
746 }
747
748 /* We didn't grab the lock, let's try again */
749 flock(lock->fd, LOCK_UN);
750 close(lock->fd);
751 lock->fd = -1;
752 ev_lock_prepare_fork(egc, lock);
753 return;
754
755 out:
756 if (lock->held) {
757 /* Check the domain is still there, if not we should release the
758 * lock and clean up. */
759 if (libxl_domain_info(CTX, NULL, domid))
760 rc = ERROR_LOCK_FAIL;
761 }
762 if (rc) {
763 LOGD(ERROR, domid, "Failed to grab lock for %s",
764 lock->userdata_userid);
765 libxl__ev_slowlock_unlock(gc, lock);
766 }
767 lock->callback(egc, lock, rc);
768 }
769
libxl__ev_slowlock_unlock(libxl__gc * gc,libxl__ev_slowlock * lock)770 void libxl__ev_slowlock_unlock(libxl__gc *gc, libxl__ev_slowlock *lock)
771 {
772 int r;
773
774 assert(!libxl__ev_child_inuse(&lock->child));
775
776 /* See the rationale in libxl__unlock_domain_userdata()
777 * about why we do unlink() before unlock(). */
778
779 if (lock->path && lock->held)
780 unlink(lock->path);
781
782 if (lock->fd >= 0) {
783 /* We need to call unlock as the fd may have leaked into other
784 * processes */
785 r = flock(lock->fd, LOCK_UN);
786 if (r)
787 LOGED(ERROR, lock->domid, "failed to unlock fd=%d, path=%s",
788 lock->fd, lock->path);
789 close(lock->fd);
790 }
791 free(lock->path);
792 ev_slowlock_init_internal(lock, lock->userdata_userid);
793 }
794
libxl__ev_slowlock_dispose(libxl__gc * gc,libxl__ev_slowlock * lock)795 void libxl__ev_slowlock_dispose(libxl__gc *gc, libxl__ev_slowlock *lock)
796 {
797 libxl__ev_child_kill_deregister(lock->ao, &lock->child, SIGKILL);
798 libxl__ev_slowlock_unlock(gc, lock);
799 }
800
801 /*
802 * Local variables:
803 * mode: C
804 * c-basic-offset: 4
805 * indent-tabs-mode: nil
806 * End:
807 */
808