debuggers.hg

view tools/libxl/libxl_device.c @ 22848:6341fe0f4e5a

Added tag 4.1.0-rc2 for changeset 9dca60d88c63
author Keir Fraser <keir@xen.org>
date Tue Jan 25 14:06:55 2011 +0000 (2011-01-25)
parents 197c0b40423a
children 6ec61438713a
line source
1 /*
2 * Copyright (C) 2009 Citrix Ltd.
3 * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
4 * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation; version 2.1 only. with the special
9 * exception on linking described in file LICENSE.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 */
17 #include "libxl_osdeps.h"
19 #include <string.h>
20 #include <stdio.h>
21 #include <sys/time.h> /* for struct timeval */
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <fcntl.h>
28 #include "libxl.h"
29 #include "libxl_internal.h"
31 static const char *string_of_kinds[] = {
32 [DEVICE_VIF] = "vif",
33 [DEVICE_VIF2] = "vif2",
34 [DEVICE_VBD] = "vbd",
35 [DEVICE_TAP] = "tap",
36 [DEVICE_QDISK] = "qdisk",
37 [DEVICE_PCI] = "pci",
38 [DEVICE_VFB] = "vfb",
39 [DEVICE_VKBD] = "vkbd",
40 [DEVICE_CONSOLE] = "console",
41 };
43 static char *libxl__device_frontend_path(libxl__gc *gc, libxl__device *device)
44 {
45 char *dom_path = libxl__xs_get_dompath(gc, device->domid);
47 /* Console 0 is a special case */
48 if (device->kind == DEVICE_CONSOLE && device->devid == 0)
49 return libxl__sprintf(gc, "%s/console", dom_path);
51 return libxl__sprintf(gc, "%s/device/%s/%d", dom_path,
52 string_of_kinds[device->kind], device->devid);
53 }
55 static char *libxl__device_backend_path(libxl__gc *gc, libxl__device *device)
56 {
57 char *dom_path = libxl__xs_get_dompath(gc, device->backend_domid);
59 return libxl__sprintf(gc, "%s/backend/%s/%u/%d", dom_path,
60 string_of_kinds[device->backend_kind],
61 device->domid, device->devid);
62 }
64 int libxl__device_generic_add(libxl_ctx *ctx, libxl__device *device,
65 char **bents, char **fents)
66 {
67 libxl__gc gc = LIBXL_INIT_GC(ctx);
68 char *frontend_path, *backend_path;
69 xs_transaction_t t;
70 struct xs_permissions frontend_perms[2];
71 struct xs_permissions backend_perms[2];
72 int rc;
74 if (!is_valid_device_kind(device->backend_kind) || !is_valid_device_kind(device->kind)) {
75 rc = ERROR_INVAL;
76 goto out;
77 }
79 frontend_path = libxl__device_frontend_path(&gc, device);
80 backend_path = libxl__device_backend_path(&gc, device);
82 frontend_perms[0].id = device->domid;
83 frontend_perms[0].perms = XS_PERM_NONE;
84 frontend_perms[1].id = device->backend_domid;
85 frontend_perms[1].perms = XS_PERM_READ;
87 backend_perms[0].id = device->backend_domid;
88 backend_perms[0].perms = XS_PERM_NONE;
89 backend_perms[1].id = device->domid;
90 backend_perms[1].perms = XS_PERM_READ;
92 retry_transaction:
93 t = xs_transaction_start(ctx->xsh);
94 /* FIXME: read frontend_path and check state before removing stuff */
96 if (fents) {
97 xs_rm(ctx->xsh, t, frontend_path);
98 xs_mkdir(ctx->xsh, t, frontend_path);
99 xs_set_permissions(ctx->xsh, t, frontend_path, frontend_perms, ARRAY_SIZE(frontend_perms));
100 xs_write(ctx->xsh, t, libxl__sprintf(&gc, "%s/backend", frontend_path), backend_path, strlen(backend_path));
101 libxl__xs_writev(&gc, t, frontend_path, fents);
102 }
104 if (bents) {
105 xs_rm(ctx->xsh, t, backend_path);
106 xs_mkdir(ctx->xsh, t, backend_path);
107 xs_set_permissions(ctx->xsh, t, backend_path, backend_perms, ARRAY_SIZE(backend_perms));
108 xs_write(ctx->xsh, t, libxl__sprintf(&gc, "%s/frontend", backend_path), frontend_path, strlen(frontend_path));
109 libxl__xs_writev(&gc, t, backend_path, bents);
110 }
112 if (!xs_transaction_end(ctx->xsh, t, 0)) {
113 if (errno == EAGAIN)
114 goto retry_transaction;
115 else
116 LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "xs transaction failed");
117 }
118 rc = 0;
119 out:
120 libxl__free_all(&gc);
121 return rc;
122 }
124 char *libxl__device_disk_string_of_phystype(libxl_disk_phystype phystype)
125 {
126 switch (phystype) {
127 case PHYSTYPE_QCOW: return "qcow";
128 case PHYSTYPE_QCOW2: return "qcow2";
129 case PHYSTYPE_VHD: return "vhd";
130 case PHYSTYPE_AIO: return "aio";
131 case PHYSTYPE_FILE: return "file";
132 case PHYSTYPE_PHY: return "phy";
133 default: return NULL;
134 }
135 }
137 char *libxl__device_disk_backend_type_of_phystype(libxl_disk_phystype phystype)
138 {
139 switch (phystype) {
140 case PHYSTYPE_QCOW: return "tap";
141 case PHYSTYPE_QCOW2: return "tap";
142 case PHYSTYPE_VHD: return "tap";
143 case PHYSTYPE_AIO: return "tap";
144 /* let's pretend file is tap:aio */
145 case PHYSTYPE_FILE: return "tap";
146 case PHYSTYPE_PHY: return "phy";
147 default: return NULL;
148 }
149 }
151 int libxl__device_physdisk_major_minor(const char *physpath, int *major, int *minor)
152 {
153 struct stat buf;
154 if (stat(physpath, &buf) < 0)
155 return -1;
156 *major = major(buf.st_rdev);
157 *minor = minor(buf.st_rdev);
158 return 0;
159 }
161 static int device_virtdisk_matches(const char *virtpath, const char *devtype,
162 int *index_r, int max_index,
163 int *partition_r, int max_partition) {
164 const char *p;
165 char *ep;
166 int tl, c;
167 long pl;
169 tl = strlen(devtype);
170 if (memcmp(virtpath, devtype, tl))
171 return 0;
173 /* We decode the drive letter as if it were in base 52
174 * with digits a-zA-Z, more or less */
175 *index_r = -1;
176 p = virtpath + tl;
177 for (;;) {
178 c = *p++;
179 if (c >= 'a' && c <= 'z') {
180 c -= 'a';
181 } else {
182 --p;
183 break;
184 }
185 (*index_r)++;
186 (*index_r) *= 26;
187 (*index_r) += c;
189 if (*index_r > max_index)
190 return 0;
191 }
193 if (!*p) {
194 *partition_r = 0;
195 return 1;
196 }
198 if (*p=='0')
199 return 0; /* leading zeroes not permitted in partition number */
201 pl = strtoul(p, &ep, 10);
202 if (pl > max_partition || *ep)
203 return 0;
205 *partition_r = pl;
206 return 1;
207 }
209 int libxl__device_disk_dev_number(char *virtpath)
210 {
211 int disk, partition;
212 char *ep;
213 unsigned long ul;
214 int chrused;
216 chrused = -1;
217 if ((sscanf(virtpath, "d%ip%i%n", &disk, &partition, &chrused) >= 2
218 && chrused == strlen(virtpath) && disk < (1<<20) && partition < 256)
219 ||
220 device_virtdisk_matches(virtpath, "xvd",
221 &disk, (1<<20)-1,
222 &partition, 255)) {
223 if (disk <= 15 && partition <= 15)
224 return (202 << 8) | (disk << 4) | partition;
225 else
226 return (1 << 28) | (disk << 8) | partition;
227 }
229 errno = 0;
230 ul = strtoul(virtpath, &ep, 0);
231 if (!errno && !*ep && ul <= INT_MAX)
232 return ul;
234 if (device_virtdisk_matches(virtpath, "hd",
235 &disk, 3,
236 &partition, 63)) {
237 return ((disk<2 ? 3 : 22) << 8) | ((disk & 1) << 6) | partition;
238 }
239 if (device_virtdisk_matches(virtpath, "sd",
240 &disk, 15,
241 &partition, 15)) {
242 return (8 << 8) | (disk << 4) | partition;
243 }
244 return -1;
245 }
247 int libxl__device_destroy(libxl_ctx *ctx, char *be_path, int force)
248 {
249 libxl__gc gc = LIBXL_INIT_GC(ctx);
250 xs_transaction_t t;
251 char *state_path = libxl__sprintf(&gc, "%s/state", be_path);
252 char *state = libxl__xs_read(&gc, XBT_NULL, state_path);
253 int rc = 0;
255 if (!state)
256 goto out;
257 if (atoi(state) != 4) {
258 xs_rm(ctx->xsh, XBT_NULL, be_path);
259 goto out;
260 }
262 retry_transaction:
263 t = xs_transaction_start(ctx->xsh);
264 xs_write(ctx->xsh, t, libxl__sprintf(&gc, "%s/online", be_path), "0", strlen("0"));
265 xs_write(ctx->xsh, t, state_path, "5", strlen("5"));
266 if (!xs_transaction_end(ctx->xsh, t, 0)) {
267 if (errno == EAGAIN)
268 goto retry_transaction;
269 else {
270 rc = -1;
271 goto out;
272 }
273 }
274 if (!force) {
275 xs_watch(ctx->xsh, state_path, be_path);
276 rc = 1;
277 }
278 out:
279 libxl__free_all(&gc);
280 return rc;
281 }
283 static int wait_for_dev_destroy(libxl_ctx *ctx, struct timeval *tv)
284 {
285 libxl__gc gc = LIBXL_INIT_GC(ctx);
286 int nfds, rc;
287 unsigned int n;
288 fd_set rfds;
289 char **l1 = NULL;
291 rc = 1;
292 nfds = xs_fileno(ctx->xsh) + 1;
293 FD_ZERO(&rfds);
294 FD_SET(xs_fileno(ctx->xsh), &rfds);
295 if (select(nfds, &rfds, NULL, NULL, tv) > 0) {
296 l1 = xs_read_watch(ctx->xsh, &n);
297 if (l1 != NULL) {
298 char *state = libxl__xs_read(&gc, XBT_NULL, l1[XS_WATCH_PATH]);
299 if (!state || atoi(state) == 6) {
300 xs_unwatch(ctx->xsh, l1[0], l1[1]);
301 xs_rm(ctx->xsh, XBT_NULL, l1[XS_WATCH_TOKEN]);
302 LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "Destroyed device backend at %s", l1[XS_WATCH_TOKEN]);
303 rc = 0;
304 }
305 free(l1);
306 }
307 }
308 libxl__free_all(&gc);
309 return rc;
310 }
312 int libxl__devices_destroy(libxl_ctx *ctx, uint32_t domid, int force)
313 {
314 libxl__gc gc = LIBXL_INIT_GC(ctx);
315 char *path, *be_path, *fe_path;
316 unsigned int num1, num2;
317 char **l1 = NULL, **l2 = NULL;
318 int i, j, n = 0, n_watches = 0;
319 flexarray_t *toremove;
321 toremove = flexarray_make(16, 1);
322 path = libxl__sprintf(&gc, "/local/domain/%d/device", domid);
323 l1 = libxl__xs_directory(&gc, XBT_NULL, path, &num1);
324 if (!l1) {
325 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "%s is empty", path);
326 goto out;
327 }
328 for (i = 0; i < num1; i++) {
329 if (!strcmp("vfs", l1[i]))
330 continue;
331 path = libxl__sprintf(&gc, "/local/domain/%d/device/%s", domid, l1[i]);
332 l2 = libxl__xs_directory(&gc, XBT_NULL, path, &num2);
333 if (!l2)
334 continue;
335 for (j = 0; j < num2; j++) {
336 fe_path = libxl__sprintf(&gc, "/local/domain/%d/device/%s/%s", domid, l1[i], l2[j]);
337 be_path = libxl__xs_read(&gc, XBT_NULL, libxl__sprintf(&gc, "%s/backend", fe_path));
338 if (be_path != NULL) {
339 if (libxl__device_destroy(ctx, be_path, force) > 0)
340 n_watches++;
341 flexarray_set(toremove, n++, libxl__dirname(&gc, be_path));
342 } else {
343 xs_rm(ctx->xsh, XBT_NULL, path);
344 }
345 }
346 }
348 /* console 0 frontend directory is not under /local/domain/<domid>/device */
349 fe_path = libxl__sprintf(&gc, "/local/domain/%d/console", domid);
350 be_path = libxl__xs_read(&gc, XBT_NULL, libxl__sprintf(&gc, "%s/backend", fe_path));
351 if (be_path && strcmp(be_path, "")) {
352 if (libxl__device_destroy(ctx, be_path, force) > 0)
353 n_watches++;
354 flexarray_set(toremove, n++, libxl__dirname(&gc, be_path));
355 }
357 if (!force) {
358 /* Linux-ism. Most implementations leave the timeout
359 * untouched after select. Linux, however, will chip
360 * away the elapsed time from it, which is what we
361 * need to enforce a single time span waiting for
362 * device destruction. */
363 struct timeval tv;
364 tv.tv_sec = LIBXL_DESTROY_TIMEOUT;
365 tv.tv_usec = 0;
366 while (n_watches > 0) {
367 if (wait_for_dev_destroy(ctx, &tv)) {
368 break;
369 } else {
370 n_watches--;
371 }
372 }
373 }
374 for (i = 0; i < n; i++) {
375 flexarray_get(toremove, i, (void**) &path);
376 xs_rm(ctx->xsh, XBT_NULL, path);
377 }
378 out:
379 flexarray_free(toremove);
380 libxl__free_all(&gc);
381 return 0;
382 }
384 int libxl__device_del(libxl_ctx *ctx, libxl__device *dev, int wait)
385 {
386 libxl__gc gc = LIBXL_INIT_GC(ctx);
387 char *backend_path;
388 int rc;
390 backend_path = libxl__device_backend_path(&gc, dev);
392 rc = libxl__device_destroy(ctx, backend_path, !wait);
393 if (rc == -1) {
394 rc = ERROR_FAIL;
395 goto out;
396 }
398 if (wait) {
399 struct timeval tv;
400 tv.tv_sec = LIBXL_DESTROY_TIMEOUT;
401 tv.tv_usec = 0;
402 (void)wait_for_dev_destroy(ctx, &tv);
403 }
405 rc = 0;
407 out:
408 libxl__free_all(&gc);
409 return rc;
410 }
412 int libxl__wait_for_device_model(libxl_ctx *ctx,
413 uint32_t domid, char *state,
414 int (*check_callback)(libxl_ctx *ctx,
415 uint32_t domid,
416 const char *state,
417 void *userdata),
418 void *check_callback_userdata)
419 {
420 libxl__gc gc = LIBXL_INIT_GC(ctx);
421 char *path;
422 char *p;
423 unsigned int len;
424 int rc = 0;
425 struct xs_handle *xsh;
426 int nfds;
427 fd_set rfds;
428 struct timeval tv;
429 unsigned int num;
430 char **l = NULL;
432 xsh = xs_daemon_open();
433 if (xsh == NULL) {
434 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Unable to open xenstore connection");
435 goto err;
436 }
438 path = libxl__sprintf(&gc, "/local/domain/0/device-model/%d/state", domid);
439 xs_watch(xsh, path, path);
440 tv.tv_sec = LIBXL_DEVICE_MODEL_START_TIMEOUT;
441 tv.tv_usec = 0;
442 nfds = xs_fileno(xsh) + 1;
443 while (rc > 0 || (!rc && tv.tv_sec > 0)) {
444 p = xs_read(xsh, XBT_NULL, path, &len);
445 if ( NULL == p )
446 goto again;
448 if ( NULL != state && strcmp(p, state) )
449 goto again;
451 if ( NULL != check_callback ) {
452 rc = (*check_callback)(ctx, domid, p, check_callback_userdata);
453 if ( rc > 0 )
454 goto again;
455 }
457 free(p);
458 xs_unwatch(xsh, path, path);
459 xs_daemon_close(xsh);
460 libxl__free_all(&gc);
461 return rc;
462 again:
463 free(p);
464 FD_ZERO(&rfds);
465 FD_SET(xs_fileno(xsh), &rfds);
466 rc = select(nfds, &rfds, NULL, NULL, &tv);
467 if (rc > 0) {
468 l = xs_read_watch(xsh, &num);
469 if (l != NULL)
470 free(l);
471 else
472 goto again;
473 }
474 }
475 xs_unwatch(xsh, path, path);
476 xs_daemon_close(xsh);
477 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Device Model not ready");
478 err:
479 libxl__free_all(&gc);
480 return -1;
481 }
483 int libxl__wait_for_backend(libxl_ctx *ctx, char *be_path, char *state)
484 {
485 libxl__gc gc = LIBXL_INIT_GC(ctx);
486 int watchdog = 100;
487 unsigned int len;
488 char *p;
489 char *path = libxl__sprintf(&gc, "%s/state", be_path);
490 int rc = -1;
492 while (watchdog > 0) {
493 p = xs_read(ctx->xsh, XBT_NULL, path, &len);
494 if (p == NULL) {
495 if (errno == ENOENT) {
496 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Backend %s does not exist",
497 be_path);
498 } else {
499 LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "Failed to access backend %s",
500 be_path);
501 }
502 goto out;
503 } else {
504 if (!strcmp(p, state)) {
505 rc = 0;
506 goto out;
507 } else {
508 usleep(100000);
509 watchdog--;
510 }
511 }
512 }
513 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Backend %s not ready", be_path);
514 out:
515 libxl__free_all(&gc);
516 return rc;
517 }