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