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