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 if (!dm_version) {
391 return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
392 }
393
394 if (libxl_device_model_version_from_string(dm_version, &value) < 0) {
395 LOGD(ERROR, domid, "fatal: %s contain a wrong value (%s)", path, dm_version);
396 return -1;
397 }
398 return value;
399 }
400
401 /* Portability note: this lock utilises flock(2) so a proper implementation of
402 * flock(2) is required.
403 */
libxl__lock_file(libxl__gc * gc,const char * lockfile)404 libxl__flock *libxl__lock_file(libxl__gc *gc, const char *lockfile)
405 {
406 libxl__flock *lock;
407 int fd;
408 struct stat stab, fstab;
409
410 lock = libxl__zalloc(NOGC, sizeof(libxl__flock));
411 lock->path = libxl__strdup(NOGC, lockfile);
412
413 while (true) {
414 libxl__carefd_begin();
415 fd = open(lockfile, O_RDWR|O_CREAT, 0666);
416 if (fd < 0)
417 LOGE(ERROR,
418 "cannot open lockfile %s, errno=%d",
419 lockfile, errno);
420 lock->carefd = libxl__carefd_opened(CTX, fd);
421 if (fd < 0) goto out;
422
423 /* Lock the file in exclusive mode, wait indefinitely to
424 * acquire the lock
425 */
426 while (flock(fd, LOCK_EX)) {
427 switch (errno) {
428 case EINTR:
429 /* Signal received, retry */
430 continue;
431 default:
432 /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */
433 LOGE(ERROR,
434 "unexpected error while trying to lock %s, fd=%d, errno=%d",
435 lockfile, fd, errno);
436 goto out;
437 }
438 }
439
440 if (fstat(fd, &fstab)) {
441 LOGE(ERROR, "cannot fstat %s, fd=%d, errno=%d",
442 lockfile, fd, errno);
443 goto out;
444 }
445 if (stat(lockfile, &stab)) {
446 if (errno != ENOENT) {
447 LOGE(ERROR, "cannot stat %s, errno=%d", lockfile, errno);
448 goto out;
449 }
450 } else {
451 if (stab.st_dev == fstab.st_dev && stab.st_ino == fstab.st_ino)
452 break;
453 }
454
455 libxl__carefd_close(lock->carefd);
456 }
457
458 return lock;
459
460 out:
461 if (lock) libxl__unlock_file(lock);
462 return NULL;
463 }
464
libxl__unlock_file(libxl__flock * lock)465 void libxl__unlock_file(libxl__flock *lock)
466 {
467 /* It's important to unlink the file before closing fd to avoid
468 * the following race (if close before unlink):
469 *
470 * P1 LOCK P2 UNLOCK
471 * fd1 = open(lockfile)
472 * close(fd2)
473 * flock(fd1)
474 * fstat and stat check success
475 * unlink(lockfile)
476 * return lock
477 *
478 * In above case P1 thinks it has got hold of the lock but
479 * actually lock is released by P2 (lockfile unlinked).
480 */
481 if (lock->path) unlink(lock->path);
482 if (lock->carefd) libxl__carefd_close(lock->carefd);
483 free(lock->path);
484 free(lock);
485 }
486
libxl__lock_domain_userdata(libxl__gc * gc,uint32_t domid)487 libxl__flock *libxl__lock_domain_userdata(libxl__gc *gc, uint32_t domid)
488 {
489 const char *lockfile;
490 libxl__flock *lock;
491
492 lockfile = libxl__userdata_path(gc, domid, "domain-userdata-lock", "l");
493 if (!lockfile) return NULL;
494
495 lock = libxl__lock_file(gc, lockfile);
496
497 /* Check the domain is still there, if not we should release the
498 * lock and clean up.
499 */
500 if (libxl_domain_info(CTX, NULL, domid)) {
501 libxl__unlock_file(lock);
502 return NULL;
503 }
504
505 return lock;
506 }
507
libxl__lock_domid_history(libxl__gc * gc)508 libxl__flock *libxl__lock_domid_history(libxl__gc *gc)
509 {
510 const char *lockfile;
511
512 lockfile = libxl__domid_history_path(gc, ".lock");
513 if (!lockfile) return NULL;
514
515 return libxl__lock_file(gc, lockfile);
516 }
517
libxl__get_domain_configuration(libxl__gc * gc,uint32_t domid,libxl_domain_config * d_config)518 int libxl__get_domain_configuration(libxl__gc *gc, uint32_t domid,
519 libxl_domain_config *d_config)
520 {
521 uint8_t *data = NULL;
522 int rc, len;
523
524 rc = libxl__userdata_retrieve(gc, domid, "libxl-json", &data, &len);
525 if (rc) {
526 LOGEVD(ERROR, rc, domid,
527 "failed to retrieve domain configuration");
528 rc = ERROR_FAIL;
529 goto out;
530 }
531
532 if (len == 0) {
533 /* No logging, not necessary an error from caller's PoV. */
534 rc = ERROR_JSON_CONFIG_EMPTY;
535 goto out;
536 }
537 rc = libxl_domain_config_from_json(CTX, d_config, (const char *)data);
538
539 out:
540 free(data);
541 return rc;
542 }
543
libxl__set_domain_configuration(libxl__gc * gc,uint32_t domid,libxl_domain_config * d_config)544 int libxl__set_domain_configuration(libxl__gc *gc, uint32_t domid,
545 libxl_domain_config *d_config)
546 {
547 char *d_config_json;
548 int rc;
549
550 d_config_json = libxl_domain_config_to_json(CTX, d_config);
551 if (!d_config_json) {
552 LOGED(ERROR, domid,
553 "failed to convert domain configuration to JSON");
554 rc = ERROR_FAIL;
555 goto out;
556 }
557
558 rc = libxl__userdata_store(gc, domid, "libxl-json",
559 (const uint8_t *)d_config_json,
560 strlen(d_config_json) + 1 /* include '\0' */);
561 if (rc) {
562 LOGEVD(ERROR, rc, domid, "failed to store domain configuration");
563 rc = ERROR_FAIL;
564 goto out;
565 }
566
567 out:
568 free(d_config_json);
569 return rc;
570 }
571
libxl__update_domain_configuration(libxl__gc * gc,libxl_domain_config * dst,const libxl_domain_config * src)572 void libxl__update_domain_configuration(libxl__gc *gc,
573 libxl_domain_config *dst,
574 const libxl_domain_config *src)
575 {
576 int i, idx, num;
577 const libxl__device_type *dt;
578
579 for (idx = 0;; idx++) {
580 dt = device_type_tbl[idx];
581 if (!dt)
582 break;
583
584 num = *libxl__device_type_get_num(dt, src);
585 if (!dt->update_config || !num)
586 continue;
587
588 for (i = 0; i < num; i++)
589 dt->update_config(gc, libxl__device_type_get_elem(dt, dst, i),
590 libxl__device_type_get_elem(dt, src, i));
591 }
592
593 /* update guest UUID */
594 libxl_uuid_copy(CTX, &dst->c_info.uuid, &src->c_info.uuid);
595
596 /* video ram */
597 dst->b_info.video_memkb = src->b_info.video_memkb;
598
599 libxl__arch_update_domain_config(gc, dst, src);
600 }
601
ev_slowlock_init_internal(libxl__ev_slowlock * lock,const char * userdata_userid)602 static void ev_slowlock_init_internal(libxl__ev_slowlock *lock,
603 const char *userdata_userid)
604 {
605 libxl__ev_child_init(&lock->child);
606 lock->userdata_userid = userdata_userid;
607 lock->path = NULL;
608 lock->fd = -1;
609 lock->held = false;
610 }
611
libxl__ev_devlock_init(libxl__ev_slowlock * lock)612 void libxl__ev_devlock_init(libxl__ev_slowlock *lock)
613 {
614 ev_slowlock_init_internal(lock, "libxl-device-changes-lock");
615 }
616
libxl__ev_qmplock_init(libxl__ev_slowlock * lock)617 void libxl__ev_qmplock_init(libxl__ev_slowlock *lock)
618 {
619 ev_slowlock_init_internal(lock, "qmp-socket-lock");
620 }
621
622 static void ev_lock_prepare_fork(libxl__egc *egc, libxl__ev_slowlock *lock);
623 static void ev_lock_child_callback(libxl__egc *egc, libxl__ev_child *child,
624 pid_t pid, int status);
625
libxl__ev_slowlock_lock(libxl__egc * egc,libxl__ev_slowlock * lock)626 void libxl__ev_slowlock_lock(libxl__egc *egc, libxl__ev_slowlock *lock)
627 {
628 STATE_AO_GC(lock->ao);
629 const char *lockfile;
630
631 lockfile = libxl__userdata_path(gc, lock->domid,
632 lock->userdata_userid, "l");
633 if (!lockfile) goto out;
634 lock->path = libxl__strdup(NOGC, lockfile);
635
636 ev_lock_prepare_fork(egc, lock);
637 return;
638 out:
639 lock->callback(egc, lock, ERROR_LOCK_FAIL);
640 }
641
ev_lock_prepare_fork(libxl__egc * egc,libxl__ev_slowlock * lock)642 static void ev_lock_prepare_fork(libxl__egc *egc, libxl__ev_slowlock *lock)
643 {
644 STATE_AO_GC(lock->ao);
645 pid_t pid;
646 int fd;
647
648 /* Convenience aliases */
649 libxl_domid domid = lock->domid;
650 const char *lockfile = lock->path;
651
652 lock->fd = open(lockfile, O_RDWR|O_CREAT, 0666);
653 if (lock->fd < 0) {
654 LOGED(ERROR, domid, "cannot open lockfile %s", lockfile);
655 goto out;
656 }
657 fd = lock->fd;
658
659 /* Enable this optimisation only in releases, so the fork code is
660 * exercised while libxl is built with debug=y. */
661 #ifndef CONFIG_DEBUG
662 /*
663 * We try to grab the lock before forking as it is likely to be free.
664 * Even though we are supposed to CTX_UNLOCK before attempting to grab
665 * the ev_lock, it is fine to do a non-blocking request now with the
666 * CTX_LOCK held as if that fails we'll try again in a fork (CTX_UNLOCK
667 * will be called in libxl), that will avoid deadlocks.
668 */
669 int r = flock(fd, LOCK_EX | LOCK_NB);
670 if (!r) {
671 libxl_fd_set_cloexec(CTX, fd, 1);
672 /* We held a lock, no need to fork but we need to check it. */
673 ev_lock_child_callback(egc, &lock->child, 0, 0);
674 return;
675 }
676 #endif
677
678 pid = libxl__ev_child_fork(gc, &lock->child, ev_lock_child_callback);
679 if (pid < 0)
680 goto out;
681 if (!pid) {
682 /* child */
683 int exit_val = 0;
684
685 /* Lock the file in exclusive mode, wait indefinitely to
686 * acquire the lock */
687 while (flock(fd, LOCK_EX)) {
688 switch (errno) {
689 case EINTR:
690 /* Signal received, retry */
691 continue;
692 default:
693 /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */
694 LOGED(ERROR, domid,
695 "unexpected error while trying to lock %s, fd=%d",
696 lockfile, fd);
697 exit_val = 1;
698 break;
699 }
700 }
701 _exit(exit_val);
702 }
703
704 /* Now that the child has the fd, set cloexec in the parent to prevent
705 * more leakage than necessary */
706 libxl_fd_set_cloexec(CTX, fd, 1);
707 return;
708 out:
709 libxl__ev_slowlock_unlock(gc, lock);
710 lock->callback(egc, lock, ERROR_LOCK_FAIL);
711 }
712
ev_lock_child_callback(libxl__egc * egc,libxl__ev_child * child,pid_t pid,int status)713 static void ev_lock_child_callback(libxl__egc *egc, libxl__ev_child *child,
714 pid_t pid, int status)
715 {
716 EGC_GC;
717 libxl__ev_slowlock *lock = CONTAINER_OF(child, *lock, child);
718 struct stat stab, fstab;
719 int rc = ERROR_LOCK_FAIL;
720
721 /* Convenience aliases */
722 int fd = lock->fd;
723 const char *lockfile = lock->path;
724 libxl_domid domid = lock->domid;
725
726 if (status) {
727 libxl_report_child_exitstatus(CTX, XTL_ERROR, "flock child",
728 pid, status);
729 goto out;
730 }
731
732 if (fstat(fd, &fstab)) {
733 LOGED(ERROR, domid, "cannot fstat %s, fd=%d", lockfile, fd);
734 goto out;
735 }
736 if (stat(lockfile, &stab)) {
737 if (errno != ENOENT) {
738 LOGED(ERROR, domid, "cannot stat %s", lockfile);
739 goto out;
740 }
741 } else {
742 if (stab.st_dev == fstab.st_dev && stab.st_ino == fstab.st_ino) {
743 /* We held the lock */
744 lock->held = true;
745 rc = 0;
746 goto out;
747 }
748 }
749
750 /* We didn't grab the lock, let's try again */
751 flock(lock->fd, LOCK_UN);
752 close(lock->fd);
753 lock->fd = -1;
754 ev_lock_prepare_fork(egc, lock);
755 return;
756
757 out:
758 if (lock->held) {
759 /* Check the domain is still there, if not we should release the
760 * lock and clean up. */
761 if (libxl_domain_info(CTX, NULL, domid))
762 rc = ERROR_LOCK_FAIL;
763 }
764 if (rc) {
765 LOGD(ERROR, domid, "Failed to grab lock for %s",
766 lock->userdata_userid);
767 libxl__ev_slowlock_unlock(gc, lock);
768 }
769 lock->callback(egc, lock, rc);
770 }
771
libxl__ev_slowlock_unlock(libxl__gc * gc,libxl__ev_slowlock * lock)772 void libxl__ev_slowlock_unlock(libxl__gc *gc, libxl__ev_slowlock *lock)
773 {
774 int r;
775
776 assert(!libxl__ev_child_inuse(&lock->child));
777
778 /* See the rationale in libxl__unlock_domain_userdata()
779 * about why we do unlink() before unlock(). */
780
781 if (lock->path && lock->held)
782 unlink(lock->path);
783
784 if (lock->fd >= 0) {
785 /* We need to call unlock as the fd may have leaked into other
786 * processes */
787 r = flock(lock->fd, LOCK_UN);
788 if (r)
789 LOGED(ERROR, lock->domid, "failed to unlock fd=%d, path=%s",
790 lock->fd, lock->path);
791 close(lock->fd);
792 }
793 free(lock->path);
794 ev_slowlock_init_internal(lock, lock->userdata_userid);
795 }
796
libxl__ev_slowlock_dispose(libxl__gc * gc,libxl__ev_slowlock * lock)797 void libxl__ev_slowlock_dispose(libxl__gc *gc, libxl__ev_slowlock *lock)
798 {
799 libxl__ev_child_kill_deregister(lock->ao, &lock->child, SIGKILL);
800 libxl__ev_slowlock_unlock(gc, lock);
801 }
802
803 /*
804 * Local variables:
805 * mode: C
806 * c-basic-offset: 4
807 * indent-tabs-mode: nil
808 * End:
809 */
810