/* * Copyright (C) 2009 Citrix Ltd. * Author Vincent Hanquez * Author Stefano Stabellini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" #include #include #include #include #include #include #include #include #include /* for write, unlink and close */ #include #include #include #include "libxl.h" #include "libxl_utils.h" #include "libxl_internal.h" #include "flexarray.h" #define PAGE_TO_MEMKB(pages) ((pages) * 4) int libxl_ctx_init(struct libxl_ctx *ctx, int version) { if (version != LIBXL_VERSION) return ERROR_VERSION; memset(ctx, 0, sizeof(struct libxl_ctx)); ctx->alloc_maxsize = 256; ctx->alloc_ptrs = calloc(ctx->alloc_maxsize, sizeof(void *)); if (!ctx->alloc_ptrs) return ERROR_NOMEM; memset(&ctx->version_info, 0, sizeof(libxl_version_info)); ctx->xch = xc_interface_open(); if (ctx->xch == -1) { free(ctx->alloc_ptrs); return ERROR_FAIL; } ctx->xsh = xs_daemon_open(); if (!ctx->xsh) { xc_interface_close(ctx->xch); free(ctx->alloc_ptrs); return ERROR_FAIL; } return 0; } int libxl_ctx_free(struct libxl_ctx *ctx) { libxl_free_all(ctx); free(ctx->alloc_ptrs); xc_interface_close(ctx->xch); if (ctx->xsh) xs_daemon_close(ctx->xsh); return 0; } int libxl_ctx_set_log(struct libxl_ctx *ctx, libxl_log_callback log_callback, void *log_data) { ctx->log_callback = log_callback; ctx->log_userdata = log_data; return 0; } /******************************************************************************/ int libxl_domain_make(struct libxl_ctx *ctx, libxl_domain_create_info *info, uint32_t *domid) { int flags, ret, i, rc; char *uuid_string; char *rw_paths[] = { "device", "device/suspend/event-channel" , "data"}; char *ro_paths[] = { "cpu", "memory", "device", "error", "drivers", "control", "attr", "messages" }; char *dom_path, *vm_path, *vss_path; struct xs_permissions roperm[2]; struct xs_permissions rwperm[1]; xs_transaction_t t; xen_domain_handle_t handle; uuid_string = libxl_uuid2string(ctx, info->uuid); if (!uuid_string) return ERROR_NOMEM; flags = info->hvm ? XEN_DOMCTL_CDF_hvm_guest : 0; flags |= info->hap ? XEN_DOMCTL_CDF_hap : 0; flags |= info->oos ? 0 : XEN_DOMCTL_CDF_oos_off; *domid = -1; /* Ultimately, handle is an array of 16 uint8_t, same as uuid */ memcpy(handle, info->uuid, sizeof(xen_domain_handle_t)); ret = xc_domain_create(ctx->xch, info->ssidref, handle, flags, domid); if (ret < 0) { XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, ret, "domain creation fail"); return ERROR_FAIL; } ret = xc_cpupool_movedomain(ctx->xch, info->poolid, *domid); if (ret < 0) { XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, ret, "domain move fail"); return ERROR_FAIL; } dom_path = libxl_xs_get_dompath(ctx, *domid); if (!dom_path) return ERROR_FAIL; vm_path = libxl_sprintf(ctx, "/vm/%s", uuid_string); vss_path = libxl_sprintf(ctx, "/vss/%s", uuid_string); if (!vm_path || !vss_path) { XL_LOG(ctx, XL_LOG_ERROR, "cannot allocate create paths"); return ERROR_FAIL; } roperm[0].id = 0; roperm[0].perms = XS_PERM_NONE; roperm[1].id = *domid; roperm[1].perms = XS_PERM_READ; rwperm[0].id = *domid; rwperm[0].perms = XS_PERM_NONE; retry_transaction: t = xs_transaction_start(ctx->xsh); xs_rm(ctx->xsh, t, dom_path); xs_mkdir(ctx->xsh, t, dom_path); xs_set_permissions(ctx->xsh, t, dom_path, roperm, ARRAY_SIZE(roperm)); xs_rm(ctx->xsh, t, vm_path); xs_mkdir(ctx->xsh, t, vm_path); xs_set_permissions(ctx->xsh, t, vm_path, roperm, ARRAY_SIZE(roperm)); xs_rm(ctx->xsh, t, vss_path); xs_mkdir(ctx->xsh, t, vss_path); xs_set_permissions(ctx->xsh, t, vss_path, rwperm, ARRAY_SIZE(rwperm)); xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/vm", dom_path), vm_path, strlen(vm_path)); xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/vss", dom_path), vss_path, strlen(vss_path)); rc = libxl_domain_rename(ctx, *domid, 0, info->name, t); if (rc) return rc; for (i = 0; i < ARRAY_SIZE(rw_paths); i++) { char *path = libxl_sprintf(ctx, "%s/%s", dom_path, rw_paths[i]); xs_mkdir(ctx->xsh, t, path); xs_set_permissions(ctx->xsh, t, path, rwperm, ARRAY_SIZE(rwperm)); libxl_free(ctx, path); } for (i = 0; i < ARRAY_SIZE(ro_paths); i++) { char *path = libxl_sprintf(ctx, "%s/%s", dom_path, ro_paths[i]); xs_mkdir(ctx->xsh, t, path); xs_set_permissions(ctx->xsh, t, path, roperm, ARRAY_SIZE(roperm)); libxl_free(ctx, path); } xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/uuid", vm_path), uuid_string, strlen(uuid_string)); xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/name", vm_path), info->name, strlen(info->name)); xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/pool_name", vm_path), info->poolname, strlen(info->poolname)); libxl_xs_writev(ctx, t, dom_path, info->xsdata); libxl_xs_writev(ctx, t, libxl_sprintf(ctx, "%s/platform", dom_path), info->platformdata); xs_write(ctx->xsh, t, libxl_sprintf(ctx, "%s/control/platform-feature-multiprocessor-suspend", dom_path), "1", 1); if (!xs_transaction_end(ctx->xsh, t, 0)) if (errno == EAGAIN) goto retry_transaction; return 0; } int libxl_domain_rename(struct libxl_ctx *ctx, uint32_t domid, const char *old_name, const char *new_name, xs_transaction_t trans) { char *dom_path = 0; const char *name_path; char *got_old_name; unsigned int got_old_len; xs_transaction_t our_trans = 0; int rc; dom_path = libxl_xs_get_dompath(ctx, domid); if (!dom_path) goto x_nomem; name_path= libxl_sprintf(ctx, "%s/name", dom_path); if (!name_path) goto x_nomem; retry_transaction: if (!trans) { trans = our_trans = xs_transaction_start(ctx->xsh); if (!our_trans) { XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, errno, "create xs transaction for domain (re)name"); goto x_fail; } } if (old_name) { got_old_name = xs_read(ctx->xsh, trans, name_path, &got_old_len); if (!got_old_name) { XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, errno, "check old name" " for domain %"PRIu32" allegedly named `%s'", domid, old_name); goto x_fail; } if (strcmp(old_name, got_old_name)) { XL_LOG(ctx, XL_LOG_ERROR, "domain %"PRIu32" allegedly named " "`%s' is actually named `%s' - racing ?", domid, old_name, got_old_name); free(got_old_name); goto x_fail; } free(got_old_name); } if (!xs_write(ctx->xsh, trans, name_path, new_name, strlen(new_name))) { XL_LOG(ctx, XL_LOG_ERROR, "failed to write new name `%s'" " for domain %"PRIu32" previously named `%s'", domid, new_name, old_name); goto x_fail; } if (our_trans) { if (!xs_transaction_end(ctx->xsh, our_trans, 0)) { trans = our_trans = 0; if (errno != EAGAIN) { XL_LOG(ctx, XL_LOG_ERROR, "failed to commit new name `%s'" " for domain %"PRIu32" previously named `%s'", domid, new_name, old_name); goto x_fail; } XL_LOG(ctx, XL_LOG_DEBUG, "need to retry rename transaction" " for domain %"PRIu32" (name_path=\"%s\", new_name=\"%s\")", domid, name_path, new_name); goto retry_transaction; } our_trans = 0; } rc = 0; x_rc: if (dom_path) libxl_free(ctx, dom_path); if (our_trans) xs_transaction_end(ctx->xsh, our_trans, 1); return rc; x_fail: rc = ERROR_FAIL; goto x_rc; x_nomem: rc = ERROR_NOMEM; goto x_rc; } int libxl_domain_build(struct libxl_ctx *ctx, libxl_domain_build_info *info, uint32_t domid, libxl_domain_build_state *state) { char **vments = NULL, **localents = NULL; int i, ret; ret = build_pre(ctx, domid, info, state); if (ret) goto out; if (info->hvm) { ret = build_hvm(ctx, domid, info, state); if (ret) goto out; vments = libxl_calloc(ctx, 5, sizeof(char *)); vments[0] = "rtc/timeoffset"; vments[1] = (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : ""; vments[2] = "image/ostype"; vments[3] = "hvm"; } else { ret = build_pv(ctx, domid, info, state); if (ret) goto out; vments = libxl_calloc(ctx, 9, sizeof(char *)); i = 0; vments[i++] = "image/ostype"; vments[i++] = "linux"; vments[i++] = "image/kernel"; vments[i++] = (char*) info->kernel; if (info->u.pv.ramdisk) { vments[i++] = "image/ramdisk"; vments[i++] = (char*) info->u.pv.ramdisk; } if (info->u.pv.cmdline) { vments[i++] = "image/cmdline"; vments[i++] = (char*) info->u.pv.cmdline; } } ret = build_post(ctx, domid, info, state, vments, localents); out: return ret; } int libxl_domain_restore(struct libxl_ctx *ctx, libxl_domain_build_info *info, uint32_t domid, int fd, libxl_domain_build_state *state, libxl_device_model_info *dm_info) { char **vments = NULL, **localents = NULL; int i, ret, esave, flags; ret = build_pre(ctx, domid, info, state); if (ret) goto out; ret = restore_common(ctx, domid, info, state, fd); if (ret) goto out; if (info->hvm) { vments = libxl_calloc(ctx, 5, sizeof(char *)); vments[0] = "rtc/timeoffset"; vments[1] = (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : ""; vments[2] = "image/ostype"; vments[3] = "hvm"; } else { vments = libxl_calloc(ctx, 9, sizeof(char *)); i = 0; vments[i++] = "image/ostype"; vments[i++] = "linux"; vments[i++] = "image/kernel"; vments[i++] = (char*) info->kernel; if (info->u.pv.ramdisk) { vments[i++] = "image/ramdisk"; vments[i++] = (char*) info->u.pv.ramdisk; } if (info->u.pv.cmdline) { vments[i++] = "image/cmdline"; vments[i++] = (char*) info->u.pv.cmdline; } } ret = build_post(ctx, domid, info, state, vments, localents); if (ret) goto out; if (info->hvm) asprintf(&(dm_info->saved_state), "/var/lib/xen/qemu-save.%d", domid); else dm_info->saved_state = NULL; out: esave = errno; flags = fcntl(fd, F_GETFL); if (flags == -1) { XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "unable to get flags on restore fd"); } else { flags &= ~O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) == -1) XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "unable to put restore fd" " back to blocking mode"); } errno = esave; return ret; } int libxl_domain_resume(struct libxl_ctx *ctx, uint32_t domid) { if (is_hvm(ctx, domid)) { XL_LOG(ctx, XL_LOG_DEBUG, "Called domain_resume on " "non-cooperative hvm domain %u", domid); return ERROR_NI; } if (xc_domain_resume(ctx->xch, domid, 1)) { XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "xc_domain_resume failed for domain %u", domid); return ERROR_FAIL; } if (!xs_resume_domain(ctx->xsh, domid)) { XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "xs_resume_domain failed for domain %u", domid); return ERROR_FAIL; } return 0; } static void xcinfo2xlinfo(const xc_domaininfo_t *xcinfo, struct libxl_dominfo *xlinfo) { memcpy(&(xlinfo->uuid), xcinfo->handle, sizeof(xen_domain_handle_t)); xlinfo->domid = xcinfo->domain; if (xcinfo->flags & XEN_DOMINF_dying) xlinfo->dying = 1; else if (xcinfo->flags & XEN_DOMINF_paused) xlinfo->paused = 1; else if (xcinfo->flags & XEN_DOMINF_blocked || xcinfo->flags & XEN_DOMINF_running) xlinfo->running = 1; xlinfo->max_memkb = PAGE_TO_MEMKB(xcinfo->tot_pages); xlinfo->cpu_time = xcinfo->cpu_time; xlinfo->vcpu_max_id = xcinfo->max_vcpu_id; xlinfo->vcpu_online = xcinfo->nr_online_vcpus; } struct libxl_dominfo * libxl_list_domain(struct libxl_ctx *ctx, int *nb_domain) { struct libxl_dominfo *ptr; int i, ret; xc_domaininfo_t info[1024]; int size = 1024; ptr = calloc(size, sizeof(struct libxl_dominfo)); if (!ptr) return NULL; ret = xc_domain_getinfolist(ctx->xch, 0, 1024, info); if (ret<0) return NULL; for (i = 0; i < ret; i++) { xcinfo2xlinfo(&info[i], &ptr[i]); } *nb_domain = ret; return ptr; } int libxl_domain_info(struct libxl_ctx *ctx, struct libxl_dominfo *info_r, uint32_t domid) { xc_domaininfo_t xcinfo; int ret; ret = xc_domain_getinfolist(ctx->xch, domid, 1, &xcinfo); if (ret<0) return ERROR_FAIL; if (ret==0 || xcinfo.domain != domid) return ERROR_INVAL; xcinfo2xlinfo(&xcinfo, info_r); return 0; } struct libxl_poolinfo * libxl_list_pool(struct libxl_ctx *ctx, int *nb_pool) { struct libxl_poolinfo *ptr; int i, ret; xc_cpupoolinfo_t info[256]; int size = 256; ptr = calloc(size, sizeof(struct libxl_poolinfo)); if (!ptr) return NULL; ret = xc_cpupool_getinfo(ctx->xch, 0, 256, info); if (ret<0) return NULL; for (i = 0; i < ret; i++) { ptr[i].poolid = info[i].cpupool_id; } *nb_pool = ret; return ptr; } /* this API call only list VM running on this host. a VM can be an aggregate of multiple domains. */ struct libxl_vminfo * libxl_list_vm(struct libxl_ctx *ctx, int *nb_vm) { struct libxl_vminfo *ptr; int index, i, ret; xc_domaininfo_t info[1024]; int size = 1024; ptr = calloc(size, sizeof(struct libxl_dominfo)); if (!ptr) return NULL; ret = xc_domain_getinfolist(ctx->xch, 1, 1024, info); for (index = i = 0; i < ret; i++) { if (libxl_is_stubdom(ctx, info[i].domain, NULL)) continue; memcpy(&(ptr[index].uuid), info[i].handle, sizeof(xen_domain_handle_t)); ptr[index].domid = info[i].domain; index++; } *nb_vm = index; return ptr; } int libxl_domain_suspend(struct libxl_ctx *ctx, libxl_domain_suspend_info *info, uint32_t domid, int fd) { int hvm = is_hvm(ctx, domid); int live = info != NULL && info->flags & XL_SUSPEND_LIVE; int debug = info != NULL && info->flags & XL_SUSPEND_LIVE; core_suspend(ctx, domid, fd, hvm, live, debug); if (hvm) save_device_model(ctx, domid, fd); return 0; } int libxl_domain_pause(struct libxl_ctx *ctx, uint32_t domid) { xc_domain_pause(ctx->xch, domid); return 0; } int libxl_domain_unpause(struct libxl_ctx *ctx, uint32_t domid) { char *path; char *state; if (is_hvm(ctx, domid)) { path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d/state", domid); state = libxl_xs_read(ctx, XBT_NULL, path); if (state != NULL && !strcmp(state, "paused")) { libxl_xs_write(ctx, XBT_NULL, libxl_sprintf(ctx, "/local/domain/0/device-model/%d/command", domid), "continue", strlen("continue")); libxl_wait_for_device_model(ctx, domid, "running", NULL, NULL); } } xc_domain_unpause(ctx->xch, domid); return 0; } static char *req_table[] = { [0] = "poweroff", [1] = "reboot", [2] = "suspend", [3] = "crash", [4] = "halt", }; int libxl_domain_shutdown(struct libxl_ctx *ctx, uint32_t domid, int req) { char *shutdown_path; char *dom_path; if (req > ARRAY_SIZE(req_table)) return ERROR_INVAL; dom_path = libxl_xs_get_dompath(ctx, domid); if (!dom_path) return ERROR_FAIL; shutdown_path = libxl_sprintf(ctx, "%s/control/shutdown", dom_path); xs_write(ctx->xsh, XBT_NULL, shutdown_path, req_table[req], strlen(req_table[req])); if (/* hvm */ 0) { unsigned long acpi_s_state = 0; unsigned long pvdriver = 0; xc_get_hvm_param(ctx->xch, domid, HVM_PARAM_ACPI_S_STATE, &acpi_s_state); xc_get_hvm_param(ctx->xch, domid, HVM_PARAM_CALLBACK_IRQ, &pvdriver); if (!pvdriver && acpi_s_state != 0) xc_domain_shutdown(ctx->xch, domid, req); } return 0; } int libxl_get_wait_fd(struct libxl_ctx *ctx, int *fd) { *fd = xs_fileno(ctx->xsh); return 0; } int libxl_wait_for_domain_death(struct libxl_ctx *ctx, uint32_t domid, libxl_waiter *waiter) { waiter->path = strdup("@releaseDomain"); asprintf(&(waiter->token), "%d", DOMAIN_DEATH); if (!xs_watch(ctx->xsh, waiter->path, waiter->token)) return -1; return 0; } int libxl_wait_for_disk_ejects(struct libxl_ctx *ctx, uint32_t guest_domid, libxl_device_disk *disks, int num_disks, libxl_waiter *waiter) { int i; uint32_t domid = libxl_get_stubdom_id(ctx, guest_domid); if (!domid) domid = guest_domid; for (i = 0; i < num_disks; i++) { asprintf(&(waiter[i].path), "%s/device/vbd/%d/eject", libxl_xs_get_dompath(ctx, domid), device_disk_dev_number(disks[i].virtpath)); asprintf(&(waiter[i].token), "%d", DISK_EJECT); xs_watch(ctx->xsh, waiter->path, waiter->token); } return 0; } int libxl_get_event(struct libxl_ctx *ctx, libxl_event *event) { unsigned int num; char **events = xs_read_watch(ctx->xsh, &num); if (num != 2) { free(events); return -1; } event->path = strdup(events[XS_WATCH_PATH]); event->token = strdup(events[XS_WATCH_TOKEN]); event->type = atoi(event->token); free(events); return 0; } int libxl_stop_waiting(struct libxl_ctx *ctx, libxl_waiter *waiter) { if (!xs_unwatch(ctx->xsh, waiter->path, waiter->token)) return -1; else return 0; } int libxl_free_event(libxl_event *event) { free(event->path); free(event->token); return 0; } int libxl_free_waiter(libxl_waiter *waiter) { free(waiter->path); free(waiter->token); return 0; } int libxl_event_get_domain_death_info(struct libxl_ctx *ctx, uint32_t domid, libxl_event *event, xc_domaininfo_t *info) { int rc = 0, ret; if (event && event->type == DOMAIN_DEATH) { ret = xc_domain_getinfolist(ctx->xch, domid, 1, info); if (ret == 1 && info->domain == domid) { if (info->flags & XEN_DOMINF_running || (!(info->flags & XEN_DOMINF_shutdown) && !(info->flags & XEN_DOMINF_dying))) goto out; rc = 1; goto out; } memset(info, 0, sizeof(xc_dominfo_t)); rc = 1; goto out; } out: return rc; } int libxl_event_get_disk_eject_info(struct libxl_ctx *ctx, uint32_t domid, libxl_event *event, libxl_device_disk *disk) { if (event && event->type == DISK_EJECT) { char *path; char *backend; char *value = libxl_xs_read(ctx, XBT_NULL, event->path); if (!value || strcmp(value, "eject")) return 0; path = strdup(event->path); path[strlen(path) - 6] = '\0'; backend = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/backend", path)); disk->backend_domid = 0; disk->domid = domid; disk->physpath = NULL; disk->phystype = 0; /* this value is returned to the user: do not free right away */ disk->virtpath = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/dev", backend)); disk->unpluggable = 1; disk->readwrite = 0; disk->is_cdrom = 1; free(path); return 1; } return 0; } static int libxl_destroy_device_model(struct libxl_ctx *ctx, uint32_t domid) { char *pid; int ret; pid = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "/local/domain/%d/image/device-model-pid", domid)); if (!pid) { int stubdomid = libxl_get_stubdom_id(ctx, domid); if (!stubdomid) { XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "Couldn't find device model's pid"); return -1; } XL_LOG(ctx, XL_LOG_ERROR, "Device model is a stubdom, domid=%d\n", stubdomid); return libxl_domain_destroy(ctx, stubdomid, 0); } xs_rm(ctx->xsh, XBT_NULL, libxl_sprintf(ctx, "/local/domain/0/device-model/%d", domid)); ret = kill(atoi(pid), SIGHUP); if (ret < 0 && errno == ESRCH) { XL_LOG(ctx, XL_LOG_DEBUG, "Device Model already exited"); ret = 0; } else if (ret == 0) { XL_LOG(ctx, XL_LOG_DEBUG, "Device Model signaled"); ret = 0; } else { XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "failed to kill Device Model [%d]", atoi(pid)); } return ret; } int libxl_domain_destroy(struct libxl_ctx *ctx, uint32_t domid, int force) { char *dom_path; char *vm_path, *vss_path, *xapi_path; int rc, dm_present; if (is_hvm(ctx, domid)) { dm_present = 1; } else { char *pid; pid = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "/local/domain/%d/image/device-model-pid", domid)); dm_present = (pid != NULL); libxl_free(ctx, pid); } dom_path = libxl_xs_get_dompath(ctx, domid); if (!dom_path) return -1; if (libxl_device_pci_shutdown(ctx, domid) < 0) XL_LOG(ctx, XL_LOG_ERROR, "pci shutdown failed for domid %d", domid); if (dm_present) { xs_write(ctx->xsh, XBT_NULL, libxl_sprintf(ctx, "/local/domain/0/device-model/%d/command", domid), "shutdown", strlen("shutdown")); } rc = xc_domain_pause(ctx->xch, domid); if (rc < 0) { XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "xc_domain_pause failed for %d", domid); return -1; } if (dm_present) { if (libxl_destroy_device_model(ctx, domid) < 0) XL_LOG(ctx, XL_LOG_ERROR, "libxl_destroy_device_model failed for %d", domid); } if (libxl_devices_destroy(ctx, domid, force) < 0) XL_LOG(ctx, XL_LOG_ERROR, "libxl_destroy_devices failed for %d", domid); if (!xs_rm(ctx->xsh, XBT_NULL, dom_path)) XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "xs_rm failed for %s", dom_path); vm_path = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "/local/domain/%d/vm", domid)); if (vm_path) if (!xs_rm(ctx->xsh, XBT_NULL, vm_path)) XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "xs_rm failed for %s", vm_path); vss_path = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "/local/domain/%d/vss", domid)); if (vss_path) if (!xs_rm(ctx->xsh, XBT_NULL, vss_path)) XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "xs_rm failed for %s", vss_path); xapi_path = libxl_sprintf(ctx, "/xapi/%u", domid); if (xapi_path) if (!xs_rm(ctx->xsh, XBT_NULL, xapi_path)) XL_LOG_ERRNO(ctx, XL_LOG_ERROR, "xs_rm failed for %s", xapi_path); libxl__userdata_destroyall(ctx, domid); rc = xc_domain_destroy(ctx->xch, domid); if (rc < 0) { XL_LOG_ERRNOVAL(ctx, XL_LOG_ERROR, rc, "xc_domain_destroy failed for %d", domid); return -1; } return 0; } int libxl_console_attach(struct libxl_ctx *ctx, uint32_t domid, int cons_num) { char *cmd = libxl_sprintf( ctx, "%s/xenconsole %d --num %d", libxl_private_bindir_path(), domid, cons_num); return (system(cmd) != 0) ? ERROR_FAIL : 0; } static char ** libxl_build_device_model_args(struct libxl_ctx *ctx, libxl_device_model_info *info, libxl_device_nic *vifs, int num_vifs) { int num = 0, i; flexarray_t *dm_args; dm_args = flexarray_make(16, 1); if (!dm_args) return NULL; flexarray_set(dm_args, num++, "qemu-dm"); flexarray_set(dm_args, num++, "-d"); flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%d", info->domid)); if (info->dom_name) { flexarray_set(dm_args, num++, "-domain-name"); flexarray_set(dm_args, num++, info->dom_name); } if (info->vnc || info->vncdisplay || info->vnclisten || info->vncunused) { flexarray_set(dm_args, num++, "-vnc"); if (info->vncdisplay) { if (info->vnclisten && strchr(info->vnclisten, ':') == NULL) { flexarray_set( dm_args, num++, libxl_sprintf(ctx, "%s:%d%s", info->vnclisten, info->vncdisplay, info->vncpasswd ? ",password" : "")); } else { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "127.0.0.1:%d", info->vncdisplay)); } } else if (info->vnclisten) { if (strchr(info->vnclisten, ':') != NULL) { flexarray_set(dm_args, num++, info->vnclisten); } else { flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%s:0", info->vnclisten)); } } else { flexarray_set(dm_args, num++, "127.0.0.1:0"); } if (info->vncunused) { flexarray_set(dm_args, num++, "-vncunused"); } } if (info->sdl || info->opengl) { flexarray_set(dm_args, num++, "-sdl"); if (info->opengl) { flexarray_set(dm_args, num++, "-disable-opengl"); } } if (info->keymap) { flexarray_set(dm_args, num++, "-k"); flexarray_set(dm_args, num++, info->keymap); } if (info->nographic && (!info->sdl && !info->vnc)) { flexarray_set(dm_args, num++, "-nographic"); } if (info->serial) { flexarray_set(dm_args, num++, "-serial"); flexarray_set(dm_args, num++, info->serial); } if (info->type == XENFV) { if (info->videoram) { flexarray_set(dm_args, num++, "-videoram"); flexarray_set(dm_args, num++, libxl_sprintf(ctx, "%d", info->videoram)); } if (info->stdvga) { flexarray_set(dm_args, num++, "-std-vga"); } if (info->boot) { flexarray_set(dm_args, num++, "-boot"); flexarray_set(dm_args, num++, info->boot); } if (info->usb) { flexarray_set(dm_args, num++, "-usb"); if (info->usbdevice) { flexarray_set(dm_args, num++, "-usbdevice"); flexarray_set(dm_args, num++, info->usbdevice); } } if (info->apic) { flexarray_set(dm_args, num++, "-acpi"); } for (i = 0; i < num_vifs; i++) { if (vifs[i].nictype == NICTYPE_IOEMU) { char *smac = libxl_sprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x", vifs[i].mac[0], vifs[i].mac[1], vifs[i].mac[2], vifs[i].mac[3], vifs[i].mac[4], vifs[i].mac[5]); if (!vifs[i].ifname) vifs[i].ifname = libxl_sprintf(ctx, "tap%d.%d", info->domid, vifs[i].devid - 1); flexarray_set(dm_args, num++, "-net"); flexarray_set(dm_args, num++, libxl_sprintf(ctx, "nic,vlan=%d,macaddr=%s,model=%s", vifs[i].devid, smac, vifs[i].model)); flexarray_set(dm_args, num++, "-net"); flexarray_set(dm_args, num++, libxl_sprintf(ctx, "tap,vlan=%d,ifname=%s,bridge=%s", vifs[i].devid, vifs[i].ifname, vifs[i].bridge)); } } } if (info->saved_state) { flexarray_set(dm_args, num++, "-loadvm"); flexarray_set(dm_args, num++, info->saved_state); } for (i = 0; info->extra && info->extra[i] != NULL; i++) flexarray_set(dm_args, num++, info->extra[i]); flexarray_set(dm_args, num++, "-M"); if (info->type == XENPV) flexarray_set(dm_args, num++, "xenpv"); else flexarray_set(dm_args, num++, "xenfv"); flexarray_set(dm_args, num++, NULL); return (char **) flexarray_contents(dm_args); } void dm_xenstore_record_pid(void *for_spawn, pid_t innerchild) { struct libxl_device_model_starting *starting = for_spawn; char *kvs[3]; int rc; struct xs_handle *xsh; xsh = xs_daemon_open(); /* we mustn't use the parent's handle in the child */ kvs[0] = "image/device-model-pid"; asprintf(&kvs[1], "%d", innerchild); kvs[2] = NULL; rc = xs_writev(xsh, XBT_NULL, starting->dom_path, kvs); if (rc) return; xs_daemon_close(xsh); } static int libxl_vfb_and_vkb_from_device_model_info(struct libxl_ctx *ctx, libxl_device_model_info *info, libxl_device_vfb *vfb, libxl_device_vkb *vkb) { memset(vfb, 0x00, sizeof(libxl_device_vfb)); memset(vkb, 0x00, sizeof(libxl_device_vkb)); vfb->backend_domid = 0; vfb->devid = 0; vfb->vnc = info->vnc; vfb->vnclisten = info->vnclisten; vfb->vncdisplay = info->vncdisplay; vfb->vncunused = info->vncunused; vfb->vncpasswd = info->vncpasswd; vfb->keymap = info->keymap; vfb->sdl = info->sdl; vfb->opengl = info->opengl; vkb->backend_domid = 0; vkb->devid = 0; return 0; } static int libxl_write_dmargs(struct libxl_ctx *ctx, int domid, int guest_domid, char **args) { int i; char *vm_path; char *dmargs, *path; int dmargs_size; struct xs_permissions roperm[2]; xs_transaction_t t; roperm[0].id = 0; roperm[0].perms = XS_PERM_NONE; roperm[1].id = domid; roperm[1].perms = XS_PERM_READ; vm_path = libxl_xs_read(ctx, XBT_NULL, libxl_sprintf(ctx, "/local/domain/%d/vm", guest_domid)); i = 0; dmargs_size = 0; while (args[i] != NULL) { dmargs_size = dmargs_size + strlen(args[i]) + 1; i++; } dmargs_size++; dmargs = (char *) malloc(dmargs_size); i = 1; dmargs[0] = '\0'; while (args[i] != NULL) { if (strcmp(args[i], "-sdl") && strcmp(args[i], "-M") && strcmp(args[i], "xenfv")) { strcat(dmargs, " "); strcat(dmargs, args[i]); } i++; } path = libxl_sprintf(ctx, "%s/image/dmargs", vm_path); retry_transaction: t = xs_transaction_start(ctx->xsh); xs_write(ctx->xsh, t, path, dmargs, strlen(dmargs)); xs_set_permissions(ctx->xsh, t, path, roperm, ARRAY_SIZE(roperm)); xs_set_permissions(ctx->xsh, t, libxl_sprintf(ctx, "%s/rtc/timeoffset", vm_path), roperm, ARRAY_SIZE(roperm)); if (!xs_transaction_end(ctx->xsh, t, 0)) if (errno == EAGAIN) goto retry_transaction; free(dmargs); return 0; } static int libxl_create_stubdom(struct libxl_ctx *ctx, libxl_device_model_info *info, libxl_device_disk *disks, int num_disks, libxl_device_nic *vifs, int num_vifs, libxl_device_vfb *vfb, libxl_device_vkb *vkb, libxl_device_model_starting **starting_r) { int i, num_console = 1, ret; libxl_device_console *console; libxl_domain_create_info c_info; libxl_domain_build_info b_info; libxl_domain_build_state state; uint32_t domid; char **args; struct xs_permissions perm[2]; xs_transaction_t t; libxl_device_model_starting *dm_starting = 0; args = libxl_build_device_model_args(ctx, info, vifs, num_vifs); if (!args) return ERROR_FAIL; memset(&c_info, 0x00, sizeof(libxl_domain_create_info)); c_info.hvm = 0; c_info.name = libxl_sprintf(ctx, "%s-dm", libxl_domid_to_name(ctx, info->domid)); for (i = 0; i < 16; i++) c_info.uuid[i] = info->uuid[i]; memset(&b_info, 0x00, sizeof(libxl_domain_build_info)); b_info.max_vcpus = 1; b_info.max_memkb = 32 * 1024; b_info.target_memkb = b_info.max_memkb; b_info.kernel = libxl_abs_path(ctx, "ioemu-stubdom.gz", libxl_xenfirmwaredir_path()); b_info.u.pv.cmdline = libxl_sprintf(ctx, " -d %d", info->domid); b_info.u.pv.ramdisk = ""; b_info.u.pv.features = ""; b_info.hvm = 0; ret = libxl_domain_make(ctx, &c_info, &domid); if (ret) return ret; ret = libxl_domain_build(ctx, &b_info, domid, &state); if (ret) return ret; libxl_write_dmargs(ctx, domid, info->domid, args); libxl_xs_write(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/image/device-model-domid", libxl_xs_get_dompath(ctx, info->domid)), "%d", domid); libxl_xs_write(ctx, XBT_NULL, libxl_sprintf(ctx, "%s/target", libxl_xs_get_dompath(ctx, domid)), "%d", info->domid); xc_domain_set_target(ctx->xch, domid, info->domid); xs_set_target(ctx->xsh, domid, info->domid); perm[0].id = domid; perm[0].perms = XS_PERM_NONE; perm[1].id = info->domid; perm[1].perms = XS_PERM_READ; retry_transaction: t = xs_transaction_start(ctx->xsh); xs_mkdir(ctx->xsh, t, libxl_sprintf(ctx, "/local/domain/0/device-model/%d", info->domid)); xs_set_permissions(ctx->xsh, t, libxl_sprintf(ctx, "/local/domain/0/device-model/%d", info->domid), perm, ARRAY_SIZE(perm)); xs_mkdir(ctx->xsh, t, libxl_sprintf(ctx, "/local/domain/%d/device/vfs", domid)); xs_set_permissions(ctx->xsh, t, libxl_sprintf(ctx, "/local/domain/%d/device/vfs",domid), perm, ARRAY_SIZE(perm)); if (!xs_transaction_end(ctx->xsh, t, 0)) if (errno == EAGAIN) goto retry_transaction; for (i = 0; i < num_disks; i++) { disks[i].domid = domid; ret = libxl_device_disk_add(ctx, domid, &disks[i]); if (ret) return ret; } for (i = 0; i < num_vifs; i++) { vifs[i].domid = domid; ret = libxl_device_nic_add(ctx, domid, &vifs[i]); if (ret) return ret; } vfb->domid = domid; ret = libxl_device_vfb_add(ctx, domid, vfb); if (ret) return ret; vkb->domid = domid; ret = libxl_device_vkb_add(ctx, domid, vkb); if (ret) return ret; if (info->serial) num_console++; console = libxl_calloc(ctx, num_console, sizeof(libxl_device_console)); if (!console) return ERROR_NOMEM; for (i = 0; i < num_console; i++) { console[i].devid = i; console[i].constype = CONSTYPE_IOEMU; console[i].domid = domid; if (!i) console[i].build_state = &state; ret = libxl_device_console_add(ctx, domid, &console[i]); if (ret) return ret; } if (libxl_create_xenpv_qemu(ctx, vfb, num_console, console, &dm_starting) < 0) { free(args); return -1; } if (libxl_confirm_device_model_startup(ctx, dm_starting) < 0) { free(args); return -1; } libxl_domain_unpause(ctx, domid); if (starting_r) { *starting_r = libxl_calloc(ctx, sizeof(libxl_device_model_starting), 1); (*starting_r)->domid = info->domid; (*starting_r)->dom_path = libxl_xs_get_dompath(ctx, info->domid); (*starting_r)->for_spawn = NULL; } free(args); return 0; } int libxl_create_device_model(struct libxl_ctx *ctx, libxl_device_model_info *info, libxl_device_disk *disks, int num_disks, libxl_device_nic *vifs, int num_vifs, libxl_device_model_starting **starting_r) { char *path, *logfile; int logfile_w, null; int rc; char **args; struct libxl_device_model_starting buf_starting, *p; xs_transaction_t t; char *vm_path; char **pass_stuff; if (strstr(info->device_model, "stubdom-dm")) { libxl_device_vfb vfb; libxl_device_vkb vkb; libxl_vfb_and_vkb_from_device_model_info(ctx, info, &vfb, &vkb); return libxl_create_stubdom(ctx, info, disks, num_disks, vifs, num_vifs, &vfb, &vkb, starting_r); } args = libxl_build_device_model_args(ctx, info, vifs, num_vifs); if (!args) return ERROR_FAIL; path = libxl_sprintf(ctx, "/local/domain/0/device-model/%d", info->domid); xs_mkdir(ctx->xsh, XBT_NULL, path); libxl_create_logfile(ctx, libxl_sprintf(ctx, "qemu-dm-%s", info->dom_name), &logfile); logfile_w = open(logfile, O_WRONLY|O_CREAT, 0644); free(logfile); null = open("/de except (ProxyError, http.HttpError, tcp.NetLibError), v: err = flow.Error(self.flow.request, str(v)) err._send(self.masterq) class ServerConnection(tcp.TCPClient): def __init__(self, config, host, port): tcp.TCPClient.__init__(self, host, port) self.config = config self.requestcount = 0 def connect(self, scheme): tcp.TCPClient.connect(self) if scheme == "https": clientcert = None if self.config.clientcerts: path = os.path.join(self.config.clientcerts, self.host) + ".pem" if os.path.exists(clientcert): clientcert = path try: self.convert_to_ssl(clientcert=clientcert, sni=self.host) except tcp.NetLibError, v: raise ProxyError(400, str(v)) def send(self, request): self.requestcount += 1 try: d = request._assemble() if not d: raise ProxyError(502, "Incomplete request could not not be readied for transmission.") self.wfile.write(d) self.wfile.flush() except socket.error, err: raise ProxyError(502, 'Error sending data to "%s": %s' % (request.host, err)) def terminate(self): try: if not self.wfile.closed: self.wfile.flush() self.connection.close() except IOError: pass class ProxyHandler(tcp.BaseHandler): def __init__(self, config, connection, client_address, server, mqueue, server_version): self.mqueue, self.server_version = mqueue, server_version self.config = config self.server_conn = None self.proxy_connect_state = None self.sni = None tcp.BaseHandler.__init__(self, connection, client_address, server) def handle(self): cc = flow.ClientConnect(self.client_address) self.log(cc, "connect") cc._send(self.mqueue) while self.handle_request(cc) and not cc.close: pass cc.close = True cd = flow.ClientDisconnect(cc) self.log( cc, "disconnect", [ "handled %s requests"%cc.requestcount] ) cd._send(self.mqueue) def server_connect(self, scheme, host, port): sc = self.server_conn if sc and (host, port) != (sc.host, sc.port): sc.terminate() self.server_conn = None if not self.server_conn: try: self.server_conn = ServerConnection(self.config, host, port) self.server_conn.connect(scheme) except tcp.NetLibError, v: raise ProxyError(502, v) def handle_request(self, cc): try: request, err = None, None request = self.read_request(cc) if request is None: return cc.requestcount += 1 app = self.server.apps.get(request) if app: err = app.serve(request, self.wfile) if err: self.log(cc, "Error in wsgi app.", err.split("\n")) return else: request = request._send(self.mqueue) if request is None: return if isinstance(request, flow.Response): response = request request = False response = response._send(self.mqueue) else: if self.config.reverse_proxy: scheme, host, port = self.config.reverse_proxy else: scheme, host, port = request.scheme, request.host, request.port self.server_connect(scheme, host, port) self.server_conn.send(request) httpversion, code, msg, headers, content = http.read_response( self.server_conn.rfile, request.method, self.config.body_size_limit ) response = flow.Response( request, httpversion, code, msg, headers, content, self.server_conn.cert ) response = response._send(self.mqueue) if response is None: self.server_conn.terminate() if response is None: return self.send_response(response) if request and http.request_connection_close(request.httpversion, request.headers): return # We could keep the client connection when the server # connection needs to go away. However, we want to mimic # behaviour as closely as possible to the client, so we # disconnect. if http.response_connection_close(response.httpversion, response.headers): return except (IOError, ProxyError, http.HttpError, tcp.NetLibDisconnect), e: if hasattr(e, "code"): cc.error = "%s: %s"%(e.code, e.msg) else: cc.error = str(e) if request: err = flow.Error(request, cc.error) err._send(self.mqueue) self.log( cc, cc.error, ["url: %s"%request.get_url()] ) else: self.log(cc, cc.error) if isinstance(e, ProxyError): self.send_error(e.code, e.msg) else: return True def log(self, cc, msg, subs=()): msg = [ "%s:%s: "%cc.address + msg ] for i in subs: msg.append(" -> "+i) msg = "\n".join(msg) l = Log(msg) l._send(self.mqueue) def find_cert(self, host, port, sni): if self.config.certfile: return self.config.certfile else: sans = [] if not self.config.no_upstream_cert: try: cert = certutils.get_remote_cert(host, port, sni) except tcp.NetLibError, v: raise ProxyError(502, "Unable to get remote cert: %s"%str(v)) sans = cert.altnames host = cert.cn.decode("utf8").encode("idna") ret = certutils.dummy_cert(self.config.certdir, self.config.cacert, host, sans) time.sleep(self.config.cert_wait_time) if not ret: raise ProxyError(502, "mitmproxy: Unable to generate dummy cert.") return ret def get_line(self, fp): """ Get a line, possibly preceded by a blank. """ line = fp.readline() if line == "\r\n" or line == "\n": # Possible leftover from previous message line = fp.readline() return line def handle_sni(self, conn): sn = conn.get_servername() if sn: self.sni = sn.decode("utf8").encode("idna") def read_request(self, client_conn): if self.config.transparent_proxy: host, port = self.config.transparent_proxy["resolver"].original_addr(self.connection) if not self.ssl_established and (port in self.config.transparent_proxy["sslports"]): scheme = "https" certfile = self.find_cert(host, port, None) try: self.convert_to_ssl(certfile, self.config.certfile or self.config.cacert) except tcp.NetLibError, v: raise ProxyError(400, str(v)) else: scheme = "http" host = self.sni or host line = self.get_line(self.rfile) if line == "": return None r = http.parse_init_http(line) if not r: raise ProxyError(400, "Bad HTTP request line: %s"%repr(line)) method, path, httpversion = r headers = http.read_headers(self.rfile) if headers is None: raise ProxyError(400, "Invalid headers") content = http.read_http_body_request( self.rfile, self.wfile, headers, httpversion, self.config.body_size_limit ) return flow.Request(client_conn, httpversion, host, port, scheme, method, path, headers, content) elif self.config.reverse_proxy: line = self.get_line(self.rfile) if line == "": return None scheme, host, port = self.config.reverse_proxy r = http.parse_init_http(line) if not r: raise ProxyError(400, "Bad HTTP request line: %s"%repr(line)) method, path, httpversion = r headers = http.read_headers(self.rfile) if headers is None: raise ProxyError(400, "Invalid headers") content = http.read_http_body_request( self.rfile, self.wfile, headers, httpversion, self.config.body_size_limit ) return flow.Request(client_conn, httpversion, host, port, "http", method, path, headers, content) else: line = self.get_line(self.rfile) if line == "": return None if line.startswith("CONNECT"): r = http.parse_init_connect(line) if not r: raise ProxyError(400, "Bad HTTP request line: %s"%repr(line)) host, port, httpversion = r # FIXME: Discard additional headers sent to the proxy. Should I expose # these to users? while 1: d = self.rfile.readline() if d == '\r\n' or d == '\n': break self.wfile.write( 'HTTP/1.1 200 Connection established\r\n' + ('Proxy-agent: %s\r\n'%self.server_version) + '\r\n' ) self.wfile.flush() certfile = self.find_cert(host, port, None) try: self.convert_to_ssl(certfile, self.config.certfile or self.config.cacert) except tcp.NetLibError, v: raise ProxyError(400, str(v)) self.proxy_connect_state = (host, port, httpversion) line = self.rfile.readline(line) if self.proxy_connect_state: host, port, httpversion = self.proxy_connect_state r = http.parse_init_http(line) if not r: raise ProxyError(400, "Bad HTTP request line: %s"%repr(line)) method, path, httpversion = r headers = http.read_headers(self.rfile) if headers is None: raise ProxyError(400, "Invalid headers") content = http.read_http_body_request( self.rfile, self.wfile, headers, httpversion, self.config.body_size_limit ) return flow.Request(client_conn, httpversion, host, port, "https", method, path, headers, content) else: r = http.parse_init_proxy(line) if not r: raise ProxyError(400, "Bad HTTP request line: %s"%repr(line)) method, scheme, host, port, path, httpversion = http.parse_init_proxy(line) headers = http.read_headers(self.rfile) if headers is None: raise ProxyError(400, "Invalid headers") content = http.read_http_body_request( self.rfile, self.wfile, headers, httpversion, self.config.body_size_limit ) return flow.Request(client_conn, httpversion, host, port, scheme, method, path, headers, content) def send_response(self, response): d = response._assemble() if not d: raise ProxyError(502, "Incomplete response could not not be readied for transmission.") self.wfile.write(d) self.wfile.flush() def send_error(self, code, body): try: response = http_status.RESPONSES.get(code, "Unknown") self.wfile.write("HTTP/1.1 %s %s\r\n" % (code, response)) self.wfile.write("Server: %s\r\n"%self.server_version) self.wfile.write("Connection: close\r\n") self.wfile.write("Content-type: text/html\r\n") self.wfile.write("\r\n") self.wfile.write('<html><head>\n<title>%d %s</title>\n</head>\n' '<body>\n%s\n</body>\n</html>' % (code, response, body)) self.wfile.flush() except: pass class ProxyServerError(Exception): pass class ProxyServer(tcp.TCPServer): allow_reuse_address = True bound = True def __init__(self, config, port, address='', server_version=version.NAMEVERSION): """ Raises ProxyServerError if there's a startup problem. """ self.config, self.port, self.address = config, port, address self.server_version = server_version try: tcp.TCPServer.__init__(self, (address, port)) except socket.error, v: raise ProxyServerError('Error starting proxy server: ' + v.strerror) self.masterq = None if config.certdir: self.certdir = config.certdir self.remove_certdir = False else: self.certdir = tempfile.mkdtemp(prefix="mitmproxy") config.certdir = self.certdir self.remove_certdir = True self.apps = AppRegistry() def start_slave(self, klass, masterq): slave = klass(masterq, self) slave.start() def set_mqueue(self, q): self.masterq = q def handle_connection(self, request, client_address): h = ProxyHandler(self.config, request, client_address, self, self.masterq, self.server_version) h.handle() h.finish() def handle_shutdown(self): try: if self.remove_certdir: shutil.rmtree(self.certdir) except OSError: pass class AppRegistry: def __init__(self): self.apps = {} def add(self, app, domain, port): """ Add a WSGI app to the registry, to be served for requests to the specified domain, on the specified port. """ self.apps[(domain, port)] = wsgi.WSGIAdaptor(app, domain, port, version.NAMEVERSION) def get(self, request): """ Returns an WSGIAdaptor instance if request matches an app, or None. """ if (request.host, request.port) in self.apps: return self.apps[(request.host, request.port)] if "host" in request.headers: host = request.headers["host"][0] return self.apps.get((host, request.port), None) class DummyServer: bound = False def __init__(self, config): self.config = config def start_slave(self, klass, masterq): pass def shutdown(self): pass # Command-line utils def certificate_option_group(parser): group = optparse.OptionGroup(parser, "SSL") group.add_option( "--cert", action="store", type = "str", dest="cert", default=None, help = "User-created SSL certificate file." ) group.add_option( "--client-certs", action="store", type = "str", dest = "clientcerts", default=None, help = "Client certificate directory." ) parser.add_option_group(group) TRANSPARENT_SSL_PORTS = [443, 8443] def process_proxy_options(parser, options): if options.cert: options.cert = os.path.expanduser(options.cert) if not os.path.exists(options.cert): parser.error("Manually created certificate does not exist: %s"%options.cert) cacert = os.path.join(options.confdir, "mitmproxy-ca.pem") cacert = os.path.expanduser(cacert) if not os.path.exists(cacert): certutils.dummy_ca(cacert) if getattr(options, "cache", None) is not None: options.cache = os.path.expanduser(options.cache) body_size_limit = utils.parse_size(options.body_size_limit) if options.reverse_proxy and options.transparent_proxy: parser.errror("Can't set both reverse proxy and transparent proxy.") if options.transparent_proxy: if not platform.resolver: parser.error("Transparent mode not supported on this platform.") trans = dict( resolver = platform.resolver, sslports = TRANSPARENT_SSL_PORTS ) else: trans = None if options.reverse_proxy: rp = utils.parse_proxy_spec(options.reverse_proxy) if not rp: parser.error("Invalid reverse proxy specification: %s"%options.reverse_proxy) else: rp = None if options.clientcerts: options.clientcerts = os.path.expanduser(options.clientcerts) if not os.path.exists(options.clientcerts) or not os.path.isdir(options.clientcerts): parser.error("Client certificate directory does not exist or is not a directory: %s"%options.clientcerts) if options.certdir: options.certdir = os.path.expanduser(options.certdir) if not os.path.exists(options.certdir) or not os.path.isdir(options.certdir): parser.error("Dummy cert directory does not exist or is not a directory: %s"%options.certdir) return ProxyConfig( certfile = options.cert, cacert = cacert, clientcerts = options.clientcerts, cert_wait_time = options.cert_wait_time, body_size_limit = body_size_limit, no_upstream_cert = options.no_upstream_cert, reverse_proxy = rp, transparent_proxy = trans, certdir = options.certdir )