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