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 (domid != INVALID_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_domain_userdata(libxl__gc * gc,uint32_t domid)403 libxl__domain_userdata_lock *libxl__lock_domain_userdata(libxl__gc *gc,
404                                                          uint32_t domid)
405 {
406     libxl__domain_userdata_lock *lock = NULL;
407     const char *lockfile;
408     int fd;
409     struct stat stab, fstab;
410 
411     lockfile = libxl__userdata_path(gc, domid, "domain-userdata-lock", "l");
412     if (!lockfile) goto out;
413 
414     lock = libxl__zalloc(NOGC, sizeof(libxl__domain_userdata_lock));
415     lock->path = libxl__strdup(NOGC, lockfile);
416 
417     while (true) {
418         libxl__carefd_begin();
419         fd = open(lockfile, O_RDWR|O_CREAT, 0666);
420         if (fd < 0)
421             LOGED(ERROR, domid,
422                   "cannot open lockfile %s, errno=%d", lockfile, errno);
423         lock->carefd = libxl__carefd_opened(CTX, fd);
424         if (fd < 0) goto out;
425 
426         /* Lock the file in exclusive mode, wait indefinitely to
427          * acquire the lock
428          */
429         while (flock(fd, LOCK_EX)) {
430             switch (errno) {
431             case EINTR:
432                 /* Signal received, retry */
433                 continue;
434             default:
435                 /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */
436                 LOGED(ERROR, domid,
437                       "unexpected error while trying to lock %s, fd=%d, errno=%d",
438                       lockfile, fd, errno);
439                 goto out;
440             }
441         }
442 
443         if (fstat(fd, &fstab)) {
444             LOGED(ERROR, domid, "cannot fstat %s, fd=%d, errno=%d",
445                   lockfile, fd, errno);
446             goto out;
447         }
448         if (stat(lockfile, &stab)) {
449             if (errno != ENOENT) {
450                 LOGED(ERROR, domid, "cannot stat %s, errno=%d", lockfile, errno);
451                 goto out;
452             }
453         } else {
454             if (stab.st_dev == fstab.st_dev && stab.st_ino == fstab.st_ino)
455                 break;
456         }
457 
458         libxl__carefd_close(lock->carefd);
459     }
460 
461     /* Check the domain is still there, if not we should release the
462      * lock and clean up.
463      */
464     if (libxl_domain_info(CTX, NULL, domid))
465         goto out;
466 
467     return lock;
468 
469 out:
470     if (lock) libxl__unlock_domain_userdata(lock);
471     return NULL;
472 }
473 
libxl__unlock_domain_userdata(libxl__domain_userdata_lock * lock)474 void libxl__unlock_domain_userdata(libxl__domain_userdata_lock *lock)
475 {
476     /* It's important to unlink the file before closing fd to avoid
477      * the following race (if close before unlink):
478      *
479      *   P1 LOCK                         P2 UNLOCK
480      *   fd1 = open(lockfile)
481      *                                   close(fd2)
482      *   flock(fd1)
483      *   fstat and stat check success
484      *                                   unlink(lockfile)
485      *   return lock
486      *
487      * In above case P1 thinks it has got hold of the lock but
488      * actually lock is released by P2 (lockfile unlinked).
489      */
490     if (lock->path) unlink(lock->path);
491     if (lock->carefd) libxl__carefd_close(lock->carefd);
492     free(lock->path);
493     free(lock);
494 }
495 
libxl__get_domain_configuration(libxl__gc * gc,uint32_t domid,libxl_domain_config * d_config)496 int libxl__get_domain_configuration(libxl__gc *gc, uint32_t domid,
497                                     libxl_domain_config *d_config)
498 {
499     uint8_t *data = NULL;
500     int rc, len;
501 
502     rc = libxl__userdata_retrieve(gc, domid, "libxl-json", &data, &len);
503     if (rc) {
504         LOGEVD(ERROR, rc, domid,
505               "failed to retrieve domain configuration");
506         rc = ERROR_FAIL;
507         goto out;
508     }
509 
510     if (len == 0) {
511         /* No logging, not necessary an error from caller's PoV. */
512         rc = ERROR_JSON_CONFIG_EMPTY;
513         goto out;
514     }
515     rc = libxl_domain_config_from_json(CTX, d_config, (const char *)data);
516 
517 out:
518     free(data);
519     return rc;
520 }
521 
libxl__set_domain_configuration(libxl__gc * gc,uint32_t domid,libxl_domain_config * d_config)522 int libxl__set_domain_configuration(libxl__gc *gc, uint32_t domid,
523                                     libxl_domain_config *d_config)
524 {
525     char *d_config_json;
526     int rc;
527 
528     d_config_json = libxl_domain_config_to_json(CTX, d_config);
529     if (!d_config_json) {
530         LOGED(ERROR, domid,
531               "failed to convert domain configuration to JSON");
532         rc = ERROR_FAIL;
533         goto out;
534     }
535 
536     rc = libxl__userdata_store(gc, domid, "libxl-json",
537                                (const uint8_t *)d_config_json,
538                                strlen(d_config_json) + 1 /* include '\0' */);
539     if (rc) {
540         LOGEVD(ERROR, rc, domid, "failed to store domain configuration");
541         rc = ERROR_FAIL;
542         goto out;
543     }
544 
545 out:
546     free(d_config_json);
547     return rc;
548 }
549 
libxl__update_domain_configuration(libxl__gc * gc,libxl_domain_config * dst,const libxl_domain_config * src)550 void libxl__update_domain_configuration(libxl__gc *gc,
551                                         libxl_domain_config *dst,
552                                         const libxl_domain_config *src)
553 {
554     int i, idx, num;
555     const struct libxl_device_type *dt;
556 
557     for (idx = 0;; idx++) {
558         dt = device_type_tbl[idx];
559         if (!dt)
560             break;
561 
562         num = *libxl__device_type_get_num(dt, src);
563         if (!dt->update_config || !num)
564             continue;
565 
566         for (i = 0; i < num; i++)
567             dt->update_config(gc, libxl__device_type_get_elem(dt, dst, i),
568                                   libxl__device_type_get_elem(dt, src, i));
569     }
570 
571     /* update guest UUID */
572     libxl_uuid_copy(CTX, &dst->c_info.uuid, &src->c_info.uuid);
573 
574     /* video ram */
575     dst->b_info.video_memkb = src->b_info.video_memkb;
576 }
577 
578 /*
579  * Local variables:
580  * mode: C
581  * c-basic-offset: 4
582  * indent-tabs-mode: nil
583  * End:
584  */
585