static virCommandPtr iptablesCommandNew(const char *table, const char *chain, int family, int action) { virCommandPtr cmd = NULL; virIpTablesInitialize(); #if HAVE_FIREWALLD if (firewall_cmd_path) { cmd = virCommandNew(firewall_cmd_path); virCommandAddArgList(cmd, "--direct", "--passthrough", (family == AF_INET6) ? "ipv6" : "ipv4", NULL); } #endif if (cmd == NULL) { cmd = virCommandNew((family == AF_INET6) ? IP6TABLES_PATH : IPTABLES_PATH); if (iptables_supports_xlock) virCommandAddArgList(cmd, "-w", NULL); } virCommandAddArgList(cmd, "--table", table, action == ADD ? "--insert" : "--delete", chain, NULL); return cmd; }
/** * virNetDevBandwidthClear: * @ifname: on which interface * * This function tries to disable QoS on specified interface * by deleting root and ingress qdisc. However, this may fail * if we try to remove the default one. * * Return 0 on success, -1 otherwise. */ int virNetDevBandwidthClear(const char *ifname) { int ret = 0; int dummy; /* for ignoring the exit status */ virCommandPtr cmd = NULL; cmd = virCommandNew(TC); virCommandAddArgList(cmd, "qdisc", "del", "dev", ifname, "root", NULL); if (virCommandRun(cmd, &dummy) < 0) ret = -1; virCommandFree(cmd); cmd = virCommandNew(TC); virCommandAddArgList(cmd, "qdisc", "del", "dev", ifname, "ingress", NULL); if (virCommandRun(cmd, &dummy) < 0) ret = -1; virCommandFree(cmd); return ret; }
/* * virNetDevBandwidthUnplug: * @brname: from which bridge are we unplugging * @id: unique identifier (MUST be greater than 2) * * Remove QoS settings from bridge. * * Returns 0 on success, -1 otherwise. */ int virNetDevBandwidthUnplug(const char *brname, unsigned int id) { int ret = -1; int cmd_ret = 0; virCommandPtr cmd = NULL; char *class_id = NULL; char *qdisc_id = NULL; char *filter_id = NULL; if (id <= 2) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid class ID %d"), id); return -1; } if (virAsprintf(&class_id, "1:%x", id) < 0 || virAsprintf(&qdisc_id, "%x:", id) < 0 || virAsprintf(&filter_id, "%u", id) < 0) { virReportOOMError(); goto cleanup; } cmd = virCommandNew(TC); virCommandAddArgList(cmd, "qdisc", "del", "dev", brname, "handle", qdisc_id, NULL); /* Don't threat tc errors as fatal, but * try to remove as much as possible */ if (virCommandRun(cmd, &cmd_ret) < 0) goto cleanup; virCommandFree(cmd); cmd = virCommandNew(TC); virCommandAddArgList(cmd, "filter", "del", "dev", brname, "prio", filter_id, NULL); if (virCommandRun(cmd, &cmd_ret) < 0) goto cleanup; cmd = virCommandNew(TC); virCommandAddArgList(cmd, "class", "del", "dev", brname, "classid", class_id, NULL); if (virCommandRun(cmd, &cmd_ret) < 0) goto cleanup; ret = 0; cleanup: VIR_FREE(filter_id); VIR_FREE(qdisc_id); VIR_FREE(class_id); virCommandFree(cmd); return ret; }
int virBhyveProbeCaps(unsigned int *caps) { char *binary, *help; virCommandPtr cmd = NULL; int ret = 0, exit; binary = virFindFileInPath("bhyve"); if (binary == NULL) goto out; if (!virFileIsExecutable(binary)) goto out; cmd = virCommandNew(binary); virCommandAddArg(cmd, "-h"); virCommandSetErrorBuffer(cmd, &help); if (virCommandRun(cmd, &exit) < 0) { ret = -1; goto out; } if (strstr(help, "-u:") != NULL) *caps |= BHYVE_CAP_RTC_UTC; out: VIR_FREE(help); virCommandFree(cmd); VIR_FREE(binary); return ret; }
int virBhyveProbeGrubCaps(virBhyveGrubCapsFlags *caps) { char *binary, *help; virCommandPtr cmd; int ret, exit; ret = 0; *caps = 0; cmd = NULL; help = NULL; binary = virFindFileInPath("grub-bhyve"); if (binary == NULL) goto out; if (!virFileIsExecutable(binary)) goto out; cmd = virCommandNew(binary); virCommandAddArg(cmd, "--help"); virCommandSetOutputBuffer(cmd, &help); if (virCommandRun(cmd, &exit) < 0) { ret = -1; goto out; } if (strstr(help, "--cons-dev") != NULL) *caps |= BHYVE_GRUB_CAP_CONSDEV; out: VIR_FREE(help); virCommandFree(cmd); VIR_FREE(binary); return ret; }
int libxlDomainGetEmulatorType(const virDomainDef *def) { int ret = LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN; virCommandPtr cmd = NULL; char *output = NULL; if (def->os.type == VIR_DOMAIN_OSTYPE_HVM) { if (def->emulator) { if (!virFileExists(def->emulator)) goto cleanup; cmd = virCommandNew(def->emulator); virCommandAddArgList(cmd, "-help", NULL); virCommandSetOutputBuffer(cmd, &output); if (virCommandRun(cmd, NULL) < 0) goto cleanup; if (strstr(output, LIBXL_QEMU_DM_STR)) ret = LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL; } } cleanup: VIR_FREE(output); virCommandFree(cmd); return ret; }
/** * virNetDevBandwidthUpdateRate: * @ifname: interface name * @classid: ID of class to update * @new_rate: new rate * * This function updates the 'rate' attribute of HTB class. * It can be used whenever a new interface is plugged to a * bridge to adjust average throughput of non guaranteed * NICs. * * Returns 0 on success, -1 otherwise. */ int virNetDevBandwidthUpdateRate(const char *ifname, const char *class_id, virNetDevBandwidthPtr bandwidth, unsigned long long new_rate) { int ret = -1; virCommandPtr cmd = NULL; char *rate = NULL; char *ceil = NULL; if (virAsprintf(&rate, "%llukbps", new_rate) < 0 || virAsprintf(&ceil, "%llukbps", bandwidth->in->peak ? bandwidth->in->peak : bandwidth->in->average) < 0) { virReportOOMError(); goto cleanup; } cmd = virCommandNew(TC); virCommandAddArgList(cmd, "class", "change", "dev", ifname, "classid", class_id, "htb", "rate", rate, "ceil", ceil, NULL); if (virCommandRun(cmd, NULL) < 0) goto cleanup; ret = 0; cleanup: virCommandFree(cmd); VIR_FREE(rate); VIR_FREE(ceil); return ret; }
/** * virNetDevMidonetBindPort: * @ifname: the network interface name * @virtualport: the midonet specific fields * * Bind an interface to a Midonet virtual port * * Returns 0 in case of success or -1 in case of failure. */ int virNetDevMidonetBindPort(const char *ifname, virNetDevVPortProfilePtr virtualport) { int ret = -1; virCommandPtr cmd = NULL; char virtportuuid[VIR_UUID_STRING_BUFLEN]; virUUIDFormat(virtualport->interfaceID, virtportuuid); cmd = virCommandNew(MMCTL); virCommandAddArgList(cmd, "--bind-port", virtportuuid, ifname, NULL); if (virCommandRun(cmd, NULL) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to bind port %s to the virtual port %s"), ifname, virtportuuid); goto cleanup; } ret = 0; cleanup: virCommandFree(cmd); return ret; }
virCommandPtr virBhyveProcessBuildBhyveCmd(bhyveConnPtr driver ATTRIBUTE_UNUSED, virDomainObjPtr vm) { /* * /usr/sbin/bhyve -c 2 -m 256 -AI -H -P \ * -s 0:0,hostbridge \ * -s 1:0,virtio-net,tap0 \ * -s 2:0,ahci-hd,${IMG} \ * -S 31,uart,stdio \ * vm0 */ virCommandPtr cmd = virCommandNew(BHYVE); /* CPUs */ virCommandAddArg(cmd, "-c"); virCommandAddArgFormat(cmd, "%d", vm->def->vcpus); /* Memory */ virCommandAddArg(cmd, "-m"); virCommandAddArgFormat(cmd, "%llu", VIR_DIV_UP(vm->def->mem.max_balloon, 1024)); /* Options */ if (vm->def->features[VIR_DOMAIN_FEATURE_ACPI] == VIR_DOMAIN_FEATURE_STATE_ON) virCommandAddArg(cmd, "-A"); /* Create an ACPI table */ if (vm->def->features[VIR_DOMAIN_FEATURE_APIC] == VIR_DOMAIN_FEATURE_STATE_ON) virCommandAddArg(cmd, "-I"); /* Present ioapic to the guest */ /* Clarification about -H and -P flags from Peter Grehan: * -H and -P flags force the guest to exit when it executes IA32 HLT and PAUSE * instructions respectively. * * For the HLT exit, bhyve uses that to infer that the guest is idling and can * be put to sleep until an external event arrives. If this option is not used, * the guest will always use 100% of CPU on the host. * * The PAUSE exit is most useful when there are large numbers of guest VMs running, * since it forces the guest to exit when it spins on a lock acquisition. */ virCommandAddArg(cmd, "-H"); /* vmexit from guest on hlt */ virCommandAddArg(cmd, "-P"); /* vmexit from guest on pause */ virCommandAddArgList(cmd, "-s", "0:0,hostbridge", NULL); /* Devices */ if (bhyveBuildNetArgStr(vm->def, cmd) < 0) goto error; if (bhyveBuildDiskArgStr(vm->def, cmd) < 0) goto error; if (bhyveBuildConsoleArgStr(vm->def, cmd) < 0) goto error; virCommandAddArg(cmd, vm->def->name); return cmd; error: virCommandFree(cmd); return NULL; }
static int virIpTablesOnceInit(void) { virCommandPtr cmd; int status; #if HAVE_FIREWALLD firewall_cmd_path = virFindFileInPath("firewall-cmd"); if (!firewall_cmd_path) { VIR_INFO("firewall-cmd not found on system. " "firewalld support disabled for iptables."); } else { cmd = virCommandNew(firewall_cmd_path); virCommandAddArgList(cmd, "--state", NULL); /* don't log non-zero status */ if (virCommandRun(cmd, &status) < 0 || status != 0) { VIR_INFO("firewall-cmd found but disabled for iptables"); VIR_FREE(firewall_cmd_path); firewall_cmd_path = NULL; } else { VIR_INFO("using firewalld for iptables commands"); } virCommandFree(cmd); } if (firewall_cmd_path) return 0; #endif cmd = virCommandNew(IPTABLES_PATH); virCommandAddArgList(cmd, "-w", "-L", "-n", NULL); /* don't log non-zero status */ if (virCommandRun(cmd, &status) < 0 || status != 0) { VIR_INFO("xtables locking not supported by your iptables"); } else { VIR_INFO("using xtables locking for iptables"); iptables_supports_xlock = true; } virCommandFree(cmd); return 0; }
virCommandPtr virBhyveProcessBuildDestroyCmd(bhyveConnPtr driver ATTRIBUTE_UNUSED, virDomainObjPtr vm) { virCommandPtr cmd = virCommandNew(BHYVECTL); virCommandAddArg(cmd, "--destroy"); virCommandAddArgPair(cmd, "--vm", vm->def->name); return cmd; }
/* * Create a new command with a NULL terminated * list of args, starting with the binary to run */ virCommandPtr virCommandNewArgList(const char *binary, ...) { virCommandPtr cmd = virCommandNew(binary); va_list list; const char *arg; if (!cmd || cmd->has_error) return NULL; va_start(list, binary); while ((arg = va_arg(list, const char *)) != NULL) virCommandAddArg(cmd, arg); va_end(list); return cmd; }
/** * lxcContainerBuildInitCmd: * @vmDef: pointer to vm definition structure * * Build a virCommandPtr for launching the container 'init' process * * Returns a virCommandPtr */ static virCommandPtr lxcContainerBuildInitCmd(virDomainDefPtr vmDef) { char uuidstr[VIR_UUID_STRING_BUFLEN]; virCommandPtr cmd; virUUIDFormat(vmDef->uuid, uuidstr); cmd = virCommandNew(vmDef->os.init); virCommandAddEnvString(cmd, "PATH=/bin:/sbin"); virCommandAddEnvString(cmd, "TERM=linux"); virCommandAddEnvString(cmd, "container=lxc-libvirt"); virCommandAddEnvPair(cmd, "LIBVIRT_LXC_UUID", uuidstr); virCommandAddEnvPair(cmd, "LIBVIRT_LXC_NAME", vmDef->name); if (vmDef->os.cmdline) virCommandAddEnvPair(cmd, "LIBVIRT_LXC_CMDLINE", vmDef->os.cmdline); return cmd; }
virCommandPtr virBhyveProcessBuildLoadCmd(bhyveConnPtr driver ATTRIBUTE_UNUSED, virDomainObjPtr vm) { virCommandPtr cmd; virDomainDiskDefPtr disk; if (vm->def->ndisks != 1) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("domain should have one and only one disk defined")); return NULL; } disk = vm->def->disks[0]; if (disk->device != VIR_DOMAIN_DISK_DEVICE_DISK) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("unsupported disk device")); return NULL; } if (disk->type != VIR_DOMAIN_DISK_TYPE_FILE) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("unsupported disk type")); return NULL; } cmd = virCommandNew(BHYVELOAD); /* Memory */ virCommandAddArg(cmd, "-m"); virCommandAddArgFormat(cmd, "%llu", VIR_DIV_UP(vm->def->mem.max_balloon, 1024)); /* Image path */ virCommandAddArg(cmd, "-d"); virCommandAddArg(cmd, disk->src); /* VM name */ virCommandAddArg(cmd, vm->def->name); return cmd; }
/* * @sourceList: Pointer to a storage pool source list * * Use the pvs command to fill the list of pv_name and vg_name associated * into the passed sourceList. * * Returns 0 if successful, -1 and sets error on failure */ static int virStorageBackendLogicalGetPoolSources(virStoragePoolSourceListPtr sourceList) { /* * # pvs --noheadings -o pv_name,vg_name * /dev/sdb * /dev/sdc VolGroup00 */ const char *regexes[] = { "^\\s*(\\S+)\\s+(\\S+)\\s*$" }; int vars[] = { 2 }; virCommandPtr cmd; int ret = -1; /* * NOTE: ignoring errors here; this is just to "touch" any logical volumes * that might be hanging around, so if this fails for some reason, the * worst that happens is that scanning doesn't pick everything up */ cmd = virCommandNew(VGSCAN); if (virCommandRun(cmd, NULL) < 0) VIR_WARN("Failure when running vgscan to refresh physical volumes"); virCommandFree(cmd); cmd = virCommandNewArgList(PVS, "--noheadings", "-o", "pv_name,vg_name", NULL); if (virCommandRunRegex(cmd, 1, regexes, vars, virStorageBackendLogicalFindPoolSourcesFunc, sourceList, "pvs") < 0) goto cleanup; ret = 0; cleanup: virCommandFree(cmd); return ret; }
/** * virNodeSuspend: * @cmdString: pointer to the command string this thread has to execute. * * Actually perform the suspend operation by invoking the command. * Give a short delay before executing the command so as to give a chance * to virNodeSuspendForDuration() to return the status to the caller. * If we don't give this delay, that function will not be able to return * the status, since the suspend operation would have begun and hence no * data can be sent through the connection to the caller. However, with * this delay added, the status return is best-effort only. */ static void virNodeSuspend(void *cmdString) { virCommandPtr suspendCmd = virCommandNew((const char *)cmdString); /* * Delay for sometime so that the function nodeSuspendForDuration() * can return the status to the caller. */ sleep(SUSPEND_DELAY); if (virCommandRun(suspendCmd, NULL) < 0) VIR_WARN("Failed to suspend the host"); virCommandFree(suspendCmd); /* * Now that we have resumed from suspend or the suspend failed, * reset 'aboutToSuspend' flag. */ virNodeSuspendLock(); aboutToSuspend = false; virNodeSuspendUnlock(); }
static int virEbTablesOnceInit(void) { firewall_cmd_path = virFindFileInPath("firewall-cmd"); if (!firewall_cmd_path) { VIR_INFO("firewall-cmd not found on system. " "firewalld support disabled for ebtables."); } else { virCommandPtr cmd = virCommandNew(firewall_cmd_path); int status; virCommandAddArgList(cmd, "--state", NULL); if (virCommandRun(cmd, &status) < 0 || status != 0) { VIR_INFO("firewall-cmd found but disabled for ebtables"); VIR_FREE(firewall_cmd_path); firewall_cmd_path = NULL; } else { VIR_INFO("using firewalld for ebtables commands"); } virCommandFree(cmd); } return 0; }
/* * qemuTPMEmulatorStop * @swtpmStateDir: A directory where the socket is located * @shortName: short and unique name of the domain * * Gracefully stop the swptm */ static void qemuTPMEmulatorStop(const char *swtpmStateDir, const char *shortName) { virCommandPtr cmd; char *pathname; char *errbuf = NULL; if (qemuTPMEmulatorInit() < 0) return; if (!(pathname = qemuTPMCreateEmulatorSocket(swtpmStateDir, shortName))) return; if (!virFileExists(pathname)) goto cleanup; cmd = virCommandNew(swtpm_ioctl); if (!cmd) goto cleanup; virCommandAddArgList(cmd, "--unix", pathname, "-s", NULL); virCommandSetErrorBuffer(cmd, &errbuf); ignore_value(virCommandRun(cmd, NULL)); virCommandFree(cmd); /* clean up the socket */ unlink(pathname); cleanup: VIR_FREE(pathname); VIR_FREE(errbuf); }
/** * qemuCreateInBridgePortWithHelper: * @cfg: the configuration object in which the helper name is looked up * @brname: the bridge name * @ifname: the returned interface name * @macaddr: the returned MAC address * @tapfd: file descriptor return value for the new tap device * @flags: OR of virNetDevTapCreateFlags: * VIR_NETDEV_TAP_CREATE_VNET_HDR * - Enable IFF_VNET_HDR on the tap device * * This function creates a new tap device on a bridge using an external * helper. The final name for the bridge will be stored in @ifname. * * Returns 0 in case of success or -1 on failure */ static int qemuCreateInBridgePortWithHelper(virQEMUDriverConfigPtr cfg, const char *brname, char **ifname, int *tapfd, unsigned int flags) { virCommandPtr cmd; char *errbuf = NULL, *cmdstr = NULL; int pair[2] = { -1, -1 }; if ((flags & ~VIR_NETDEV_TAP_CREATE_VNET_HDR) != VIR_NETDEV_TAP_CREATE_IFUP) return -1; if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) < 0) { virReportSystemError(errno, "%s", _("failed to create socket")); return -1; } if (!virFileIsExecutable(cfg->bridgeHelperName)) { virReportSystemError(errno, _("'%s' is not a suitable bridge helper"), cfg->bridgeHelperName); return -1; } cmd = virCommandNew(cfg->bridgeHelperName); if (flags & VIR_NETDEV_TAP_CREATE_VNET_HDR) virCommandAddArgFormat(cmd, "--use-vnet"); virCommandAddArgFormat(cmd, "--br=%s", brname); virCommandAddArgFormat(cmd, "--fd=%d", pair[1]); virCommandSetErrorBuffer(cmd, &errbuf); virCommandDoAsyncIO(cmd); virCommandPassFD(cmd, pair[1], VIR_COMMAND_PASS_FD_CLOSE_PARENT); virCommandClearCaps(cmd); #ifdef CAP_NET_ADMIN virCommandAllowCap(cmd, CAP_NET_ADMIN); #endif if (virCommandRunAsync(cmd, NULL) < 0) { *tapfd = -1; goto cleanup; } do { *tapfd = recvfd(pair[0], 0); } while (*tapfd < 0 && errno == EINTR); if (*tapfd < 0) { char ebuf[1024]; char *errstr = NULL; if (!(cmdstr = virCommandToString(cmd))) goto cleanup; virCommandAbort(cmd); if (errbuf && *errbuf && virAsprintf(&errstr, "\nstderr=%s", errbuf) < 0) goto cleanup; virReportError(VIR_ERR_INTERNAL_ERROR, _("%s: failed to communicate with bridge helper: %s%s"), cmdstr, virStrerror(errno, ebuf, sizeof(ebuf)), errstr ? errstr : ""); VIR_FREE(errstr); goto cleanup; } if (virNetDevTapGetName(*tapfd, ifname) < 0 || virCommandWait(cmd, NULL) < 0) { VIR_FORCE_CLOSE(*tapfd); *tapfd = -1; } cleanup: VIR_FREE(cmdstr); VIR_FREE(errbuf); virCommandFree(cmd); VIR_FORCE_CLOSE(pair[0]); return *tapfd < 0 ? -1 : 0; }
/** * virNetDevOpenvswitchAddPort: * @brname: the bridge name * @ifname: the network interface name * @macaddr: the mac address of the virtual interface * @vmuuid: the Domain UUID that has this interface * @ovsport: the ovs specific fields * * Add an interface to the OVS bridge * * Returns 0 in case of success or -1 in case of failure. */ int virNetDevOpenvswitchAddPort(const char *brname, const char *ifname, const virMacAddr *macaddr, const unsigned char *vmuuid, virNetDevVPortProfilePtr ovsport, virNetDevVlanPtr virtVlan) { int ret = -1; size_t i = 0; virCommandPtr cmd = NULL; char macaddrstr[VIR_MAC_STRING_BUFLEN]; char ifuuidstr[VIR_UUID_STRING_BUFLEN]; char vmuuidstr[VIR_UUID_STRING_BUFLEN]; char *attachedmac_ex_id = NULL; char *ifaceid_ex_id = NULL; char *profile_ex_id = NULL; char *vmid_ex_id = NULL; virBuffer buf = VIR_BUFFER_INITIALIZER; virMacAddrFormat(macaddr, macaddrstr); virUUIDFormat(ovsport->interfaceID, ifuuidstr); virUUIDFormat(vmuuid, vmuuidstr); if (virAsprintf(&attachedmac_ex_id, "external-ids:attached-mac=\"%s\"", macaddrstr) < 0) goto cleanup; if (virAsprintf(&ifaceid_ex_id, "external-ids:iface-id=\"%s\"", ifuuidstr) < 0) goto cleanup; if (virAsprintf(&vmid_ex_id, "external-ids:vm-id=\"%s\"", vmuuidstr) < 0) goto cleanup; if (ovsport->profileID[0] != '\0') { if (virAsprintf(&profile_ex_id, "external-ids:port-profile=\"%s\"", ovsport->profileID) < 0) goto cleanup; } cmd = virCommandNew(OVSVSCTL); virCommandAddArgList(cmd, "--timeout=5", "--", "--if-exists", "del-port", ifname, "--", "add-port", brname, ifname, NULL); if (virtVlan && virtVlan->nTags > 0) { switch (virtVlan->nativeMode) { case VIR_NATIVE_VLAN_MODE_TAGGED: virCommandAddArg(cmd, "vlan_mode=native-tagged"); virCommandAddArgFormat(cmd, "tag=%d", virtVlan->nativeTag); break; case VIR_NATIVE_VLAN_MODE_UNTAGGED: virCommandAddArg(cmd, "vlan_mode=native-untagged"); virCommandAddArgFormat(cmd, "tag=%d", virtVlan->nativeTag); break; case VIR_NATIVE_VLAN_MODE_DEFAULT: default: break; } if (virtVlan->trunk) { virBufferAddLit(&buf, "trunk="); /* * Trunk ports have at least one VLAN. Do the first one * outside the "for" loop so we can put a "," at the * start of the for loop if there are more than one VLANs * on this trunk port. */ virBufferAsprintf(&buf, "%d", virtVlan->tag[i]); for (i = 1; i < virtVlan->nTags; i++) { virBufferAddLit(&buf, ","); virBufferAsprintf(&buf, "%d", virtVlan->tag[i]); } if (virBufferCheckError(&buf) < 0) goto cleanup; virCommandAddArg(cmd, virBufferCurrentContent(&buf)); } else if (virtVlan->nTags) { virCommandAddArgFormat(cmd, "tag=%d", virtVlan->tag[0]); } } if (ovsport->profileID[0] == '\0') { virCommandAddArgList(cmd, "--", "set", "Interface", ifname, attachedmac_ex_id, "--", "set", "Interface", ifname, ifaceid_ex_id, "--", "set", "Interface", ifname, vmid_ex_id, "--", "set", "Interface", ifname, "external-ids:iface-status=active", NULL); } else { virCommandAddArgList(cmd, "--", "set", "Interface", ifname, attachedmac_ex_id, "--", "set", "Interface", ifname, ifaceid_ex_id, "--", "set", "Interface", ifname, vmid_ex_id, "--", "set", "Interface", ifname, profile_ex_id, "--", "set", "Interface", ifname, "external-ids:iface-status=active", NULL); } if (virCommandRun(cmd, NULL) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to add port %s to OVS bridge %s"), ifname, brname); goto cleanup; } ret = 0; cleanup: virBufferFreeAndReset(&buf); VIR_FREE(attachedmac_ex_id); VIR_FREE(ifaceid_ex_id); VIR_FREE(vmid_ex_id); VIR_FREE(profile_ex_id); virCommandFree(cmd); return ret; }
} /** * virNetDevOpenvswitchRemovePort: * @ifname: the network interface name * * Deletes an interface from a OVS bridge * * Returns 0 in case of success or -1 in case of failure. */ int virNetDevOpenvswitchRemovePort(const char *brname ATTRIBUTE_UNUSED, const char *ifname) { int ret = -1; virCommandPtr cmd = NULL; cmd = virCommandNew(OVSVSCTL); virCommandAddArgList(cmd, "--timeout=5", "--", "--if-exists", "del-port", ifname, NULL); if (virCommandRun(cmd, NULL) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to delete port %s from OVS"), ifname); goto cleanup; } ret = 0; cleanup: virCommandFree(cmd); return ret; } /**
VIR_FREE(expectlog); VIR_FREE(expectname); return ret; } /* * Run program, no args, inherit all ENV, keep CWD. * Only stdin/out/err open * No slot for return status must log error. */ static int test0(const void *unused ATTRIBUTE_UNUSED) { virCommandPtr cmd; int ret = -1; cmd = virCommandNew(abs_builddir "/commandhelper-doesnotexist"); if (virCommandRun(cmd, NULL) == 0) goto cleanup; if (virGetLastError() == NULL) goto cleanup; virResetLastError(); ret = 0; cleanup: virCommandFree(cmd); return ret; } /*
/* * Constructs a argv suitable for launching uml with config defined * for a given virtual machine. */ virCommandPtr umlBuildCommandLine(virConnectPtr conn, struct uml_driver *driver, virDomainObjPtr vm) { size_t i, j; virCommandPtr cmd; cmd = virCommandNew(vm->def->os.kernel); virCommandAddEnvPassCommon(cmd); //virCommandAddArgPair(cmd, "con0", "fd:0,fd:1"); virCommandAddArgFormat(cmd, "mem=%lluK", vm->def->mem.cur_balloon); virCommandAddArgPair(cmd, "umid", vm->def->name); virCommandAddArgPair(cmd, "uml_dir", driver->monitorDir); if (vm->def->os.root) virCommandAddArgPair(cmd, "root", vm->def->os.root); for (i = 0; i < vm->def->ndisks; i++) { virDomainDiskDefPtr disk = vm->def->disks[i]; if (!STRPREFIX(disk->dst, "ubd")) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported disk type '%s'"), disk->dst); goto error; } virCommandAddArgPair(cmd, disk->dst, virDomainDiskGetSource(disk)); } for (i = 0; i < vm->def->nnets; i++) { char *ret = umlBuildCommandLineNet(conn, vm->def, vm->def->nets[i], i); if (!ret) goto error; virCommandAddArg(cmd, ret); VIR_FREE(ret); } for (i = 0; i < UML_MAX_CHAR_DEVICE; i++) { virDomainChrDefPtr chr = NULL; char *ret = NULL; for (j = 0; j < vm->def->nconsoles; j++) if (vm->def->consoles[j]->target.port == i) chr = vm->def->consoles[j]; if (chr) ret = umlBuildCommandLineChr(chr, "con", cmd); if (!ret) if (virAsprintf(&ret, "con%zu=none", i) < 0) goto error; virCommandAddArg(cmd, ret); VIR_FREE(ret); } for (i = 0; i < UML_MAX_CHAR_DEVICE; i++) { virDomainChrDefPtr chr = NULL; char *ret = NULL; for (j = 0; j < vm->def->nserials; j++) if (vm->def->serials[j]->target.port == i) chr = vm->def->serials[j]; if (chr) ret = umlBuildCommandLineChr(chr, "ssl", cmd); if (!ret) if (virAsprintf(&ret, "ssl%zu=none", i) < 0) goto error; virCommandAddArg(cmd, ret); VIR_FREE(ret); } if (vm->def->os.cmdline) { char *args, *next_arg; char *cmdline; if (VIR_STRDUP(cmdline, vm->def->os.cmdline) < 0) goto error; args = cmdline; while (*args == ' ') args++; while (*args) { next_arg = umlNextArg(args); virCommandAddArg(cmd, args); args = next_arg; } VIR_FREE(cmdline); } return cmd; error: virCommandFree(cmd); return NULL; }
/** * virNetDevVethCreate: * @veth1: pointer to name for parent end of veth pair * @veth2: pointer to return name for container end of veth pair * * Creates a veth device pair using the ip command: * ip link add veth1 type veth peer name veth2 * If veth1 points to NULL on entry, it will be a valid interface on * return. veth2 should point to NULL on entry. * * NOTE: If veth1 and veth2 names are not specified, ip will auto assign * names. There seems to be two problems here - * 1) There doesn't seem to be a way to determine the names of the * devices that it creates. They show up in ip link show and * under /sys/class/net/ however there is no guarantee that they * are the devices that this process just created. * 2) Once one of the veth devices is moved to another namespace, it * is no longer visible in the parent namespace. This seems to * confuse the name assignment causing it to fail with File exists. * Because of these issues, this function currently allocates names * prior to using the ip command, and returns any allocated names * to the caller. * * Returns 0 on success or -1 in case of error */ int virNetDevVethCreate(char** veth1, char** veth2) { int ret = -1; char *veth1auto = NULL; char *veth2auto = NULL; int vethNum = 0; virCommandPtr cmd = NULL; size_t i; /* * We might race with other containers, but this is reasonably * unlikely, so don't do too many retries for device creation */ if (virNetDevVethCreateMutexInitialize() < 0) return -1; virMutexLock(&virNetDevVethCreateMutex); #define MAX_VETH_RETRIES 10 for (i = 0; i < MAX_VETH_RETRIES; i++) { int status; if (!*veth1) { int veth1num; if ((veth1num = virNetDevVethGetFreeNum(vethNum)) < 0) goto cleanup; if (virAsprintf(&veth1auto, "vnet%d", veth1num) < 0) goto cleanup; vethNum = veth1num + 1; } if (!*veth2) { int veth2num; if ((veth2num = virNetDevVethGetFreeNum(vethNum)) < 0) goto cleanup; if (virAsprintf(&veth2auto, "vnet%d", veth2num) < 0) goto cleanup; vethNum = veth2num + 1; } cmd = virCommandNew("ip"); virCommandAddArgList(cmd, "link", "add", *veth1 ? *veth1 : veth1auto, "type", "veth", "peer", "name", *veth2 ? *veth2 : veth2auto, NULL); if (virCommandRun(cmd, &status) < 0) goto cleanup; if (status == 0) { if (veth1auto) { *veth1 = veth1auto; veth1auto = NULL; } if (veth2auto) { *veth2 = veth2auto; veth2auto = NULL; } VIR_DEBUG("Create Host: %s guest: %s", *veth1, *veth2); ret = 0; goto cleanup; } VIR_DEBUG("Failed to create veth host: %s guest: %s: %d", *veth1 ? *veth1 : veth1auto, *veth1 ? *veth1 : veth1auto, status); VIR_FREE(veth1auto); VIR_FREE(veth2auto); virCommandFree(cmd); cmd = NULL; } virReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to allocate free veth pair after %d attempts"), MAX_VETH_RETRIES); cleanup: virMutexUnlock(&virNetDevVethCreateMutex); virCommandFree(cmd); VIR_FREE(veth1auto); VIR_FREE(veth2auto); return ret; }
/* * virNetDevBandwidthPlug: * @brname: name of the bridge * @net_bandwidth: QoS settings on @brname * @ifmac: MAC of interface * @bandwidth: QoS settings for interface * @id: unique ID (MUST be greater than 2) * * Set bridge part of interface QoS settings, e.g. guaranteed * bandwidth. @id is an unique ID (among @brname) from which * other identifiers for class, qdisc and filter are derived. * However, two classes were already set up (by * virNetDevBandwidthSet). That's why this @id MUST be greater * than 2. You may want to keep passed @id, as it is used later * by virNetDevBandwidthUnplug. * * Returns: * 0 if QoS set successfully * -1 otherwise. */ int virNetDevBandwidthPlug(const char *brname, virNetDevBandwidthPtr net_bandwidth, const virMacAddrPtr ifmac_ptr, virNetDevBandwidthPtr bandwidth, unsigned int id) { int ret = -1; virCommandPtr cmd = NULL; char *class_id = NULL; char *qdisc_id = NULL; char *filter_id = NULL; char *floor = NULL; char *ceil = NULL; unsigned char ifmac[VIR_MAC_BUFLEN]; char ifmacStr[VIR_MAC_STRING_BUFLEN]; char *mac[2] = {NULL, NULL}; if (id <= 2) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid class ID %d"), id); return -1; } virMacAddrGetRaw(ifmac_ptr, ifmac); virMacAddrFormat(ifmac_ptr, ifmacStr); if (!net_bandwidth || !net_bandwidth->in) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Bridge '%s' has no QoS set, therefore " "unable to set 'floor' on '%s'"), brname, ifmacStr); return -1; } if (virAsprintf(&class_id, "1:%x", id) < 0 || virAsprintf(&qdisc_id, "%x:", id) < 0 || virAsprintf(&filter_id, "%u", id) < 0 || virAsprintf(&mac[0], "0x%02x%02x%02x%02x", ifmac[2], ifmac[3], ifmac[4], ifmac[5]) < 0 || virAsprintf(&mac[1], "0x%02x%02x", ifmac[0], ifmac[1]) < 0 || virAsprintf(&floor, "%llukbps", bandwidth->in->floor) < 0 || virAsprintf(&ceil, "%llukbps", net_bandwidth->in->peak ? net_bandwidth->in->peak : net_bandwidth->in->average) < 0) { virReportOOMError(); goto cleanup; } cmd = virCommandNew(TC); virCommandAddArgList(cmd, "class", "add", "dev", brname, "parent", "1:1", "classid", class_id, "htb", "rate", floor, "ceil", ceil, NULL); if (virCommandRun(cmd, NULL) < 0) goto cleanup; virCommandFree(cmd); cmd = virCommandNew(TC); virCommandAddArgList(cmd, "qdisc", "add", "dev", brname, "parent", class_id, "handle", qdisc_id, "sfq", "perturb", "10", NULL); if (virCommandRun(cmd, NULL) < 0) goto cleanup; virCommandFree(cmd); cmd = virCommandNew(TC); /* Okay, this not nice. But since libvirt does not know anything about * interface IP address(es), and tc fw filter simply refuse to use ebtables * marks, we need to use u32 selector to match MAC address. * If libvirt will ever know something, remove this FIXME */ virCommandAddArgList(cmd, "filter", "add", "dev", brname, "protocol", "ip", "prio", filter_id, "u32", "match", "u16", "0x0800", "0xffff", "at", "-2", "match", "u32", mac[0], "0xffffffff", "at", "-12", "match", "u16", mac[1], "0xffff", "at", "-14", "flowid", class_id, NULL); if (virCommandRun(cmd, NULL) < 0) goto cleanup; ret = 0; cleanup: VIR_FREE(mac[1]); VIR_FREE(mac[0]); VIR_FREE(ceil); VIR_FREE(floor); VIR_FREE(filter_id); VIR_FREE(qdisc_id); VIR_FREE(class_id); virCommandFree(cmd); return ret; }
static virCommandPtr virLXCProcessBuildControllerCmd(virLXCDriverPtr driver, virDomainObjPtr vm, int nveths, char **veths, int *ttyFDs, size_t nttyFDs, int *files, size_t nfiles, int handshakefd) { size_t i; char *filterstr; char *outputstr; virCommandPtr cmd; virLXCDriverConfigPtr cfg = virLXCDriverGetConfig(driver); cmd = virCommandNew(vm->def->emulator); /* The controller may call ip command, so we have to retain PATH. */ virCommandAddEnvPassBlockSUID(cmd, "PATH", "/bin:/usr/bin"); virCommandAddEnvFormat(cmd, "LIBVIRT_DEBUG=%d", virLogGetDefaultPriority()); if (virLogGetNbFilters() > 0) { filterstr = virLogGetFilters(); if (!filterstr) { virReportOOMError(); goto cleanup; } virCommandAddEnvPair(cmd, "LIBVIRT_LOG_FILTERS", filterstr); VIR_FREE(filterstr); } if (cfg->log_libvirtd) { if (virLogGetNbOutputs() > 0) { outputstr = virLogGetOutputs(); if (!outputstr) { virReportOOMError(); goto cleanup; } virCommandAddEnvPair(cmd, "LIBVIRT_LOG_OUTPUTS", outputstr); VIR_FREE(outputstr); } } else { virCommandAddEnvFormat(cmd, "LIBVIRT_LOG_OUTPUTS=%d:stderr", virLogGetDefaultPriority()); } virCommandAddArgList(cmd, "--name", vm->def->name, NULL); for (i = 0; i < nttyFDs; i++) { virCommandAddArg(cmd, "--console"); virCommandAddArgFormat(cmd, "%d", ttyFDs[i]); virCommandPassFD(cmd, ttyFDs[i], 0); } for (i = 0; i < nfiles; i++) { virCommandAddArg(cmd, "--passfd"); virCommandAddArgFormat(cmd, "%d", files[i]); virCommandPassFD(cmd, files[i], 0); } virCommandAddArgPair(cmd, "--security", virSecurityManagerGetModel(driver->securityManager)); virCommandAddArg(cmd, "--handshake"); virCommandAddArgFormat(cmd, "%d", handshakefd); virCommandAddArg(cmd, "--background"); for (i = 0; i < nveths; i++) { virCommandAddArgList(cmd, "--veth", veths[i], NULL); } virCommandPassFD(cmd, handshakefd, 0); return cmd; cleanup: virCommandFree(cmd); virObjectUnref(cfg); return NULL; }
int vars[] = { 2 }; virCommandPtr cmd; char *retval = NULL; virStoragePoolSourceList sourceList; size_t i; virCheckFlags(0, NULL); /* * NOTE: ignoring errors here; this is just to "touch" any logical volumes * that might be hanging around, so if this fails for some reason, the * worst that happens is that scanning doesn't pick everything up */ cmd = virCommandNew(VGSCAN); if (virCommandRun(cmd, NULL) < 0) VIR_WARN("Failure when running vgscan to refresh physical volumes"); virCommandFree(cmd); memset(&sourceList, 0, sizeof(sourceList)); sourceList.type = VIR_STORAGE_POOL_LOGICAL; cmd = virCommandNewArgList(PVS, "--noheadings", "-o", "pv_name,vg_name", NULL); if (virCommandRunRegex(cmd, 1, regexes, vars, virStorageBackendLogicalFindPoolSourcesFunc, &sourceList, "pvs") < 0) { virCommandFree(cmd);
/* * qemuTPMEmulatorBuildCommand: * * @tpm: TPM definition * @vmname: The name of the VM * @vmuuid: The UUID of the VM * @privileged: whether we are running in privileged mode * @swtpm_user: The uid for the swtpm to run as (drop privileges to from root) * @swtpm_group: The gid for the swtpm to run as * @swtpmStateDir: the directory where swtpm writes the pid file and creates the * Unix socket * @shortName: the short name of the VM * * Create the virCommand use for starting the emulator * Do some initializations on the way, such as creation of storage * and emulator setup. */ static virCommandPtr qemuTPMEmulatorBuildCommand(virDomainTPMDefPtr tpm, const char *vmname, const unsigned char *vmuuid, bool privileged, uid_t swtpm_user, gid_t swtpm_group, const char *swtpmStateDir, const char *shortName) { virCommandPtr cmd = NULL; bool created = false; char *pidfile; if (qemuTPMCreateEmulatorStorage(tpm->data.emulator.storagepath, &created, swtpm_user, swtpm_group) < 0) return NULL; if (created && qemuTPMEmulatorRunSetup(tpm->data.emulator.storagepath, vmname, vmuuid, privileged, swtpm_user, swtpm_group, tpm->data.emulator.logfile, tpm->version) < 0) goto error; unlink(tpm->data.emulator.source.data.nix.path); cmd = virCommandNew(swtpm_path); if (!cmd) goto error; virCommandClearCaps(cmd); virCommandAddArgList(cmd, "socket", "--daemon", "--ctrl", NULL); virCommandAddArgFormat(cmd, "type=unixio,path=%s,mode=0600", tpm->data.emulator.source.data.nix.path); virCommandAddArg(cmd, "--tpmstate"); virCommandAddArgFormat(cmd, "dir=%s,mode=0600", tpm->data.emulator.storagepath); virCommandAddArg(cmd, "--log"); virCommandAddArgFormat(cmd, "file=%s", tpm->data.emulator.logfile); virCommandSetUID(cmd, swtpm_user); virCommandSetGID(cmd, swtpm_group); switch (tpm->version) { case VIR_DOMAIN_TPM_VERSION_1_2: break; case VIR_DOMAIN_TPM_VERSION_2_0: virCommandAddArg(cmd, "--tpm2"); break; case VIR_DOMAIN_TPM_VERSION_DEFAULT: case VIR_DOMAIN_TPM_VERSION_LAST: break; } if (!(pidfile = qemuTPMEmulatorCreatePidFilename(swtpmStateDir, shortName))) goto error; virCommandAddArg(cmd, "--pid"); virCommandAddArgFormat(cmd, "file=%s", pidfile); VIR_FREE(pidfile); return cmd; error: if (created) qemuTPMDeleteEmulatorStorage(tpm); virCommandFree(cmd); return NULL; }
/* * qemuTPMEmulatorRunSetup * * @storagepath: path to the directory for TPM state * @vmname: the name of the VM * @vmuuid: the UUID of the VM * @privileged: whether we are running in privileged mode * @swtpm_user: The userid to switch to when setting up the TPM; * typically this should be the uid of 'tss' or 'root' * @swtpm_group: The group id to switch to * @logfile: The file to write the log into; it must be writable * for the user given by userid or 'tss' * @tpmversion: The version of the TPM, either a TPM 1.2 or TPM 2 * * Setup the external swtpm by creating endorsement key and * certificates for it. */ static int qemuTPMEmulatorRunSetup(const char *storagepath, const char *vmname, const unsigned char *vmuuid, bool privileged, uid_t swtpm_user, gid_t swtpm_group, const char *logfile, const virDomainTPMVersion tpmversion) { virCommandPtr cmd = NULL; int exitstatus; int ret = -1; char uuid[VIR_UUID_STRING_BUFLEN]; char *vmid = NULL; if (!privileged && tpmversion == VIR_DOMAIN_TPM_VERSION_1_2) return virFileWriteStr(logfile, _("Did not create EK and certificates since " "this requires privileged mode for a " "TPM 1.2\n"), 0600); cmd = virCommandNew(swtpm_setup); if (!cmd) goto cleanup; virUUIDFormat(vmuuid, uuid); if (virAsprintf(&vmid, "%s:%s", vmname, uuid) < 0) goto cleanup; virCommandSetUID(cmd, swtpm_user); virCommandSetGID(cmd, swtpm_group); switch (tpmversion) { case VIR_DOMAIN_TPM_VERSION_1_2: break; case VIR_DOMAIN_TPM_VERSION_2_0: virCommandAddArgList(cmd, "--tpm2", NULL); break; case VIR_DOMAIN_TPM_VERSION_DEFAULT: case VIR_DOMAIN_TPM_VERSION_LAST: break; } virCommandAddArgList(cmd, "--tpm-state", storagepath, "--vmid", vmid, "--logfile", logfile, "--createek", "--create-ek-cert", "--create-platform-cert", "--lock-nvram", "--not-overwrite", NULL); virCommandClearCaps(cmd); if (virCommandRun(cmd, &exitstatus) < 0 || exitstatus != 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not run '%s'. exitstatus: %d; " "Check error log '%s' for details."), swtpm_setup, exitstatus, logfile); goto cleanup; } ret = 0; cleanup: VIR_FREE(vmid); virCommandFree(cmd); return ret; }
/** * virNetDevBandwidthSet: * @ifname: on which interface * @bandwidth: rates to set (may be NULL) * @hierarchical_class: whether to create hierarchical class * * This function enables QoS on specified interface * and set given traffic limits for both, incoming * and outgoing traffic. Any previous setting get * overwritten. If @hierarchical_class is TRUE, create * hierarchical class. It is used to guarantee minimal * throughput ('floor' attribute in NIC). * * Return 0 on success, -1 otherwise. */ int virNetDevBandwidthSet(const char *ifname, virNetDevBandwidthPtr bandwidth, bool hierarchical_class) { int ret = -1; virCommandPtr cmd = NULL; char *average = NULL; char *peak = NULL; char *burst = NULL; if (!bandwidth) { /* nothing to be enabled */ ret = 0; goto cleanup; } virNetDevBandwidthClear(ifname); if (bandwidth->in && bandwidth->in->average) { if (virAsprintf(&average, "%llukbps", bandwidth->in->average) < 0) goto cleanup; if (bandwidth->in->peak && (virAsprintf(&peak, "%llukbps", bandwidth->in->peak) < 0)) goto cleanup; if (bandwidth->in->burst && (virAsprintf(&burst, "%llukb", bandwidth->in->burst) < 0)) goto cleanup; cmd = virCommandNew(TC); virCommandAddArgList(cmd, "qdisc", "add", "dev", ifname, "root", "handle", "1:", "htb", "default", hierarchical_class ? "2" : "1", NULL); if (virCommandRun(cmd, NULL) < 0) goto cleanup; /* If we are creating a hierarchical class, all non guaranteed traffic * goes to the 1:2 class which will adjust 'rate' dynamically as NICs * with guaranteed throughput are plugged and unplugged. Class 1:1 * exists so we don't exceed the maximum limit for the network. For each * NIC with guaranteed throughput a separate classid will be created. * NB '1:' is just a shorter notation of '1:0'. * * To get a picture how this works: * * +-----+ +---------+ +-----------+ +-----------+ +-----+ * | | | qdisc | | class 1:1 | | class 1:2 | | | * | NIC | | def 1:2 | | rate | | rate | | sfq | * | | --> | | --> | peak | -+-> | peak | --> | | * +-----+ +---------+ +-----------+ | +-----------+ +-----+ * | * | +-----------+ +-----+ * | | class 1:3 | | | * | | rate | | sfq | * +-> | peak | --> | | * | +-----------+ +-----+ * ... * | +-----------+ +-----+ * | | class 1:n | | | * | | rate | | sfq | * +-> | peak | --> | | * +-----------+ +-----+ * * After the routing decision, when is it clear a packet is to be sent * via a particular NIC, it is sent to the root qdisc (queueing * discipline). In this case HTB (Hierarchical Token Bucket). It has * only one direct child class (with id 1:1) which shapes the overall * rate that is sent through the NIC. This class has at least one child * (1:2) which is meant for all non-privileged (non guaranteed) traffic * from all domains. Then, for each interface with guaranteed * throughput, a separate class (1:n) is created. Imagine a class is a * box. Whenever a packet ends up in a class it is stored in this box * until the kernel sends it, then it is removed from box. Packets are * placed into boxes based on rules (filters) - e.g. depending on * destination IP/MAC address. If there is no rule to be applied, the * root qdisc has a default where such packets go (1:2 in this case). * Packets come in over and over again and boxes get filled more and * more. Imagine that kernel sends packets just once a second. So it * starts to traverse through this tree. It starts with the root qdisc * and through 1:1 it gets to 1:2. It sends packets up to 1:2's 'rate'. * Then it moves to 1:3 and again sends packets up to 1:3's 'rate'. The * whole process is repeated until 1:n is processed. So now we have * ensured each class its guaranteed bandwidth. If the sum of sent data * doesn't exceed the 'rate' in 1:1 class, we can go further and send * more packets. The rest of available bandwidth is distributed to the * 1:2,1:3...1:n classes by ratio of their 'rate'. As soon as the root * 'rate' limit is reached or there are no more packets to send, we stop * sending and wait another second. Each class has an SFQ qdisc which * shuffles packets in boxes stochastically, so one sender cannot * starve others. * * Therefore, whenever we want to plug in a new guaranteed interface, we * need to create a new class and adjust the 'rate' of the 1:2 class. * When unplugging we do the exact opposite - remove the associated * class, and adjust the 'rate'. * * This description is rather long, but it is still a good idea to read * it before you dig into the code. */ if (hierarchical_class) { virCommandFree(cmd); cmd = virCommandNew(TC); virCommandAddArgList(cmd, "class", "add", "dev", ifname, "parent", "1:", "classid", "1:1", "htb", "rate", average, "ceil", peak ? peak : average, NULL); if (virCommandRun(cmd, NULL) < 0) goto cleanup; } virCommandFree(cmd); cmd = virCommandNew(TC); virCommandAddArgList(cmd,"class", "add", "dev", ifname, "parent", hierarchical_class ? "1:1" : "1:", "classid", hierarchical_class ? "1:2" : "1:1", "htb", "rate", average, NULL); if (peak) virCommandAddArgList(cmd, "ceil", peak, NULL); if (burst) virCommandAddArgList(cmd, "burst", burst, NULL); if (virCommandRun(cmd, NULL) < 0) goto cleanup; virCommandFree(cmd); cmd = virCommandNew(TC); virCommandAddArgList(cmd, "qdisc", "add", "dev", ifname, "parent", hierarchical_class ? "1:2" : "1:1", "handle", "2:", "sfq", "perturb", "10", NULL); if (virCommandRun(cmd, NULL) < 0) goto cleanup; virCommandFree(cmd); cmd = virCommandNew(TC); virCommandAddArgList(cmd,"filter", "add", "dev", ifname, "parent", "1:0", "protocol", "ip", "handle", "1", "fw", "flowid", "1", NULL); if (virCommandRun(cmd, NULL) < 0) goto cleanup; VIR_FREE(average); VIR_FREE(peak); VIR_FREE(burst); } if (bandwidth->out) { if (virAsprintf(&average, "%llukbps", bandwidth->out->average) < 0) goto cleanup; if (virAsprintf(&burst, "%llukb", bandwidth->out->burst ? bandwidth->out->burst : bandwidth->out->average) < 0) goto cleanup; virCommandFree(cmd); cmd = virCommandNew(TC); virCommandAddArgList(cmd, "qdisc", "add", "dev", ifname, "ingress", NULL); if (virCommandRun(cmd, NULL) < 0) goto cleanup; virCommandFree(cmd); cmd = virCommandNew(TC); virCommandAddArgList(cmd, "filter", "add", "dev", ifname, "parent", "ffff:", "protocol", "ip", "u32", "match", "ip", "src", "0.0.0.0/0", "police", "rate", average, "burst", burst, "mtu", "64kb", "drop", "flowid", ":1", NULL); if (virCommandRun(cmd, NULL) < 0) goto cleanup; } ret = 0; cleanup: virCommandFree(cmd); VIR_FREE(average); VIR_FREE(peak); VIR_FREE(burst); return ret; }