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