2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* virt-aa-helper: wrapper program used by AppArmor security driver.
|
2010-05-12 17:23:06 +00:00
|
|
|
*
|
domain_conf: split source data out from ChrDef
This opens up the possibility of reusing the smaller ChrSourceDef
for both qemu monitor and a passthrough smartcard device.
* src/conf/domain_conf.h (_virDomainChrDef): Factor host
details...
(_virDomainChrSourceDef): ...into new struct.
(virDomainChrSourceDefFree): New prototype.
* src/conf/domain_conf.c (virDomainChrDefFree)
(virDomainChrDefParseXML, virDomainChrDefFormat): Split...
(virDomainChrSourceDefClear, virDomainChrSourceDefFree)
(virDomainChrSourceDefParseXML, virDomainChrSourceDefFormat):
...into new functions.
(virDomainChrDefParseTargetXML): Update clients to reflect type
split.
* src/vmx/vmx.c (virVMXParseSerial, virVMXParseParallel)
(virVMXFormatSerial, virVMXFormatParallel): Likewise.
* src/xen/xen_driver.c (xenUnifiedDomainOpenConsole): Likewise.
* src/xen/xend_internal.c (xenDaemonParseSxprChar)
(xenDaemonFormatSxprChr): Likewise.
* src/vbox/vbox_tmpl.c (vboxDomainDumpXML, vboxAttachSerial)
(vboxAttachParallel): Likewise.
* src/security/security_dac.c (virSecurityDACSetChardevLabel)
(virSecurityDACSetChardevCallback)
(virSecurityDACRestoreChardevLabel)
(virSecurityDACRestoreChardevCallback): Likewise.
* src/security/security_selinux.c (SELinuxSetSecurityChardevLabel)
(SELinuxSetSecurityChardevCallback)
(SELinuxRestoreSecurityChardevLabel)
(SELinuxSetSecurityChardevCallback): Likewise.
* src/security/virt-aa-helper.c (get_files): Likewise.
* src/lxc/lxc_driver.c (lxcVmStart, lxcDomainOpenConsole):
Likewise.
* src/uml/uml_conf.c (umlBuildCommandLineChr): Likewise.
* src/uml/uml_driver.c (umlIdentifyOneChrPTY, umlIdentifyChrPTY)
(umlDomainOpenConsole): Likewise.
* src/qemu/qemu_command.c (qemuBuildChrChardevStr)
(qemuBuildChrArgStr, qemuBuildCommandLine)
(qemuParseCommandLineChr): Likewise.
* src/qemu/qemu_domain.c (qemuDomainObjPrivateXMLFormat)
(qemuDomainObjPrivateXMLParse): Likewise.
* src/qemu/qemu_cgroup.c (qemuSetupChardevCgroup): Likewise.
* src/qemu/qemu_hotplug.c (qemuDomainAttachNetDevice): Likewise.
* src/qemu/qemu_driver.c (qemudFindCharDevicePTYsMonitor)
(qemudFindCharDevicePTYs, qemuPrepareChardevDevice)
(qemuPrepareMonitorChr, qemudShutdownVMDaemon)
(qemuDomainOpenConsole): Likewise.
* src/qemu/qemu_command.h (qemuBuildChrChardevStr)
(qemuBuildChrArgStr): Delete, now that they are static.
* src/libvirt_private.syms (domain_conf.h): New exports.
* cfg.mk (useless_free_options): Update list.
* tests/qemuxml2argvtest.c (testCompareXMLToArgvFiles): Update
tests.
2011-01-07 22:45:01 +00:00
|
|
|
* Copyright (C) 2010-2011 Red Hat, Inc.
|
2011-09-28 07:43:39 +00:00
|
|
|
* Copyright (C) 2009-2011 Canonical Ltd.
|
2009-10-08 14:34:22 +00:00
|
|
|
*
|
|
|
|
* See COPYING.LIB for the License of this software
|
|
|
|
*
|
|
|
|
* Author:
|
|
|
|
* Jamie Strandboge <jamie@canonical.com>
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/types.h>
|
2010-08-13 22:00:31 +00:00
|
|
|
#include <sys/stat.h>
|
2009-10-08 14:34:22 +00:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <sys/utsname.h>
|
2010-11-17 20:31:04 +00:00
|
|
|
#include <locale.h>
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
#include "buf.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "memory.h"
|
2011-06-08 13:17:17 +00:00
|
|
|
#include "command.h"
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
#include "security_driver.h"
|
|
|
|
#include "security_apparmor.h"
|
|
|
|
#include "domain_conf.h"
|
|
|
|
#include "xml.h"
|
|
|
|
#include "uuid.h"
|
|
|
|
#include "hostusb.h"
|
|
|
|
#include "pci.h"
|
2011-07-19 18:32:58 +00:00
|
|
|
#include "virfile.h"
|
2010-11-16 14:54:17 +00:00
|
|
|
#include "configmake.h"
|
2012-01-27 10:14:21 +00:00
|
|
|
#include "virrandom.h"
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2011-05-12 21:20:47 +00:00
|
|
|
#define VIR_FROM_THIS VIR_FROM_SECURITY
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
static char *progname;
|
|
|
|
|
|
|
|
typedef struct {
|
2010-06-15 16:58:58 +00:00
|
|
|
bool allowDiskFormatProbing;
|
2009-10-08 14:34:22 +00:00
|
|
|
char uuid[PROFILE_NAME_SIZE]; /* UUID of vm */
|
|
|
|
bool dryrun; /* dry run */
|
|
|
|
char cmd; /* 'c' create
|
|
|
|
* 'a' add (load)
|
|
|
|
* 'r' replace
|
|
|
|
* 'R' remove */
|
|
|
|
char *files; /* list of files */
|
|
|
|
virDomainDefPtr def; /* VM definition */
|
|
|
|
virCapsPtr caps; /* VM capabilities */
|
|
|
|
char *hvm; /* type of hypervisor (eg hvm, xen) */
|
2009-11-13 10:04:23 +00:00
|
|
|
char *arch; /* machine architecture */
|
2009-10-08 14:34:22 +00:00
|
|
|
int bits; /* bits in the guest */
|
2010-09-30 20:54:56 +00:00
|
|
|
char *newfile; /* newly added file */
|
|
|
|
bool append; /* append to .files instead of rewrite */
|
2009-10-08 14:34:22 +00:00
|
|
|
} vahControl;
|
|
|
|
|
|
|
|
static int
|
|
|
|
vahDeinit(vahControl * ctl)
|
|
|
|
{
|
|
|
|
if (ctl == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
VIR_FREE(ctl->def);
|
2010-05-17 20:38:59 +00:00
|
|
|
virCapabilitiesFree(ctl->caps);
|
2009-12-09 23:00:50 +00:00
|
|
|
VIR_FREE(ctl->files);
|
|
|
|
VIR_FREE(ctl->hvm);
|
|
|
|
VIR_FREE(ctl->arch);
|
2010-09-30 20:54:56 +00:00
|
|
|
VIR_FREE(ctl->newfile);
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Print usage
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
vah_usage(void)
|
|
|
|
{
|
2010-11-17 20:31:04 +00:00
|
|
|
printf(_("\n%s [options] [< def.xml]\n\n"
|
|
|
|
" Options:\n"
|
|
|
|
" -a | --add load profile\n"
|
|
|
|
" -c | --create create profile from template\n"
|
|
|
|
" -D | --delete unload and delete profile\n"
|
|
|
|
" -f | --add-file <file> add file to profile\n"
|
|
|
|
" -F | --append-file <file> append file to profile\n"
|
|
|
|
" -r | --replace reload profile\n"
|
|
|
|
" -R | --remove unload profile\n"
|
|
|
|
" -h | --help this help\n"
|
|
|
|
" -u | --uuid <uuid> uuid (profile name)\n"
|
|
|
|
"\n"), progname);
|
|
|
|
|
|
|
|
puts(_("This command is intended to be used by libvirtd "
|
|
|
|
"and not used directly.\n"));
|
2009-10-08 14:34:22 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
vah_error(vahControl * ctl, int doexit, const char *str)
|
|
|
|
{
|
2010-05-12 17:23:06 +00:00
|
|
|
fprintf(stderr, _("%s: error: %s%c"), progname, str, '\n');
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
if (doexit) {
|
|
|
|
if (ctl != NULL)
|
|
|
|
vahDeinit(ctl);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
vah_warning(const char *str)
|
|
|
|
{
|
2010-05-12 17:23:06 +00:00
|
|
|
fprintf(stderr, _("%s: warning: %s%c"), progname, str, '\n');
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
vah_info(const char *str)
|
|
|
|
{
|
2010-05-12 17:23:06 +00:00
|
|
|
fprintf(stderr, _("%s:\n%s%c"), progname, str, '\n');
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Replace @oldstr in @orig with @repstr
|
|
|
|
* @len is number of bytes allocated for @orig. Assumes @orig, @oldstr and
|
|
|
|
* @repstr are null terminated
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
replace_string(char *orig, const size_t len, const char *oldstr,
|
|
|
|
const char *repstr)
|
|
|
|
{
|
|
|
|
int idx;
|
|
|
|
char *pos = NULL;
|
|
|
|
char *tmp = NULL;
|
|
|
|
|
|
|
|
if ((pos = strstr(orig, oldstr)) == NULL) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("could not find replacement string"));
|
2009-10-08 14:34:22 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_ALLOC_N(tmp, len) < 0) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("could not allocate memory for string"));
|
2009-10-08 14:34:22 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
tmp[0] = '\0';
|
|
|
|
|
|
|
|
idx = abs(pos - orig);
|
|
|
|
|
|
|
|
/* copy everything up to oldstr */
|
|
|
|
strncat(tmp, orig, idx);
|
|
|
|
|
|
|
|
/* add the replacement string */
|
|
|
|
if (strlen(tmp) + strlen(repstr) > len - 1) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("not enough space in target buffer"));
|
2009-10-08 14:34:22 +00:00
|
|
|
VIR_FREE(tmp);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
strcat(tmp, repstr);
|
|
|
|
|
|
|
|
/* add everything after oldstr */
|
|
|
|
if (strlen(tmp) + strlen(orig) - (idx + strlen(oldstr)) > len - 1) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("not enough space in target buffer"));
|
2009-10-08 14:34:22 +00:00
|
|
|
VIR_FREE(tmp);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
strncat(tmp, orig + idx + strlen(oldstr),
|
|
|
|
strlen(orig) - (idx + strlen(oldstr)));
|
|
|
|
|
|
|
|
if (virStrcpy(orig, tmp, len) == NULL) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("error replacing string"));
|
2009-10-08 14:34:22 +00:00
|
|
|
VIR_FREE(tmp);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
VIR_FREE(tmp);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* run an apparmor_parser command
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
parserCommand(const char *profile_name, const char cmd)
|
|
|
|
{
|
2011-04-03 09:21:17 +00:00
|
|
|
int result = -1;
|
2009-10-08 14:34:22 +00:00
|
|
|
char flag[3];
|
2011-04-03 09:21:17 +00:00
|
|
|
char *profile;
|
2010-04-06 14:17:08 +00:00
|
|
|
int status;
|
|
|
|
int ret;
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
if (strchr("arR", cmd) == NULL) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("invalid flag"));
|
2009-10-08 14:34:22 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(flag, 3, "-%c", cmd);
|
|
|
|
|
2011-04-03 09:21:17 +00:00
|
|
|
if (virAsprintf(&profile, "%s/%s",
|
|
|
|
APPARMOR_DIR "/libvirt", profile_name) < 0) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("profile name exceeds maximum length"));
|
2009-10-08 14:34:22 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!virFileExists(profile)) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("profile does not exist"));
|
2011-04-03 09:21:17 +00:00
|
|
|
goto cleanup;
|
2009-10-08 14:34:22 +00:00
|
|
|
} else {
|
|
|
|
const char * const argv[] = {
|
|
|
|
"/sbin/apparmor_parser", flag, profile, NULL
|
|
|
|
};
|
2010-04-06 14:17:08 +00:00
|
|
|
if ((ret = virRun(argv, &status)) != 0 ||
|
|
|
|
(WIFEXITED(status) && WEXITSTATUS(status) != 0)) {
|
|
|
|
if (ret != 0) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("failed to run apparmor_parser"));
|
2011-04-03 09:21:17 +00:00
|
|
|
goto cleanup;
|
2010-05-12 17:23:06 +00:00
|
|
|
} else if (cmd == 'R' && WIFEXITED(status) &&
|
|
|
|
WEXITSTATUS(status) == 234) {
|
|
|
|
vah_warning(_("unable to unload already unloaded profile"));
|
2010-04-06 14:17:08 +00:00
|
|
|
} else {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("apparmor_parser exited with error"));
|
2011-04-03 09:21:17 +00:00
|
|
|
goto cleanup;
|
2010-04-06 14:17:08 +00:00
|
|
|
}
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-03 09:21:17 +00:00
|
|
|
result = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(profile);
|
|
|
|
|
|
|
|
return result;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the dynamic files
|
|
|
|
*/
|
|
|
|
static int
|
2010-09-30 20:54:56 +00:00
|
|
|
update_include_file(const char *include_file, const char *included_files,
|
|
|
|
bool append)
|
2009-10-08 14:34:22 +00:00
|
|
|
{
|
|
|
|
int rc = -1;
|
2011-05-07 11:13:41 +00:00
|
|
|
int plen, flen = 0;
|
2009-10-08 14:34:22 +00:00
|
|
|
int fd;
|
|
|
|
char *pcontent = NULL;
|
2010-09-30 20:54:56 +00:00
|
|
|
char *existing = NULL;
|
2009-10-08 14:34:22 +00:00
|
|
|
const char *warning =
|
|
|
|
"# DO NOT EDIT THIS FILE DIRECTLY. IT IS MANAGED BY LIBVIRT.\n";
|
|
|
|
|
2010-09-30 20:54:56 +00:00
|
|
|
if (virFileExists(include_file)) {
|
|
|
|
flen = virFileReadAll(include_file, MAX_FILE_LEN, &existing);
|
|
|
|
if (flen < 0)
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (append && virFileExists(include_file)) {
|
|
|
|
if (virAsprintf(&pcontent, "%s%s", existing, included_files) == -1) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("could not allocate memory for profile"));
|
2010-09-30 20:54:56 +00:00
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (virAsprintf(&pcontent, "%s%s", warning, included_files) == -1) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("could not allocate memory for profile"));
|
2010-09-30 20:54:56 +00:00
|
|
|
goto clean;
|
|
|
|
}
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
plen = strlen(pcontent);
|
|
|
|
if (plen > MAX_FILE_LEN) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("invalid length for new profile"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* only update the disk profile if it is different */
|
2010-09-30 20:54:56 +00:00
|
|
|
if (flen > 0 && flen == plen && STREQLEN(existing, pcontent, plen)) {
|
|
|
|
rc = 0;
|
|
|
|
goto clean;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* write the file */
|
|
|
|
if ((fd = open(include_file, O_CREAT | O_TRUNC | O_WRONLY, 0644)) == -1) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("failed to create include file"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (safewrite(fd, pcontent, plen) < 0) { /* don't write the '\0' */
|
2010-11-09 20:48:48 +00:00
|
|
|
VIR_FORCE_CLOSE(fd);
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("failed to write to profile"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
|
2010-11-09 20:48:48 +00:00
|
|
|
if (VIR_CLOSE(fd) != 0) {
|
2010-11-17 20:31:04 +00:00
|
|
|
vah_error(NULL, 0, _("failed to close or write to profile"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
|
|
|
|
clean:
|
|
|
|
VIR_FREE(pcontent);
|
2010-09-30 20:54:56 +00:00
|
|
|
VIR_FREE(existing);
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a profile based on a template
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
create_profile(const char *profile, const char *profile_name,
|
|
|
|
const char *profile_files)
|
|
|
|
{
|
2011-04-03 09:21:17 +00:00
|
|
|
char *template;
|
2009-10-08 14:34:22 +00:00
|
|
|
char *tcontent = NULL;
|
|
|
|
char *pcontent = NULL;
|
|
|
|
char *replace_name = NULL;
|
|
|
|
char *replace_files = NULL;
|
|
|
|
const char *template_name = "\nprofile LIBVIRT_TEMPLATE";
|
|
|
|
const char *template_end = "\n}";
|
|
|
|
int tlen, plen;
|
|
|
|
int fd;
|
|
|
|
int rc = -1;
|
|
|
|
|
|
|
|
if (virFileExists(profile)) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("profile exists"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2011-04-03 09:21:17 +00:00
|
|
|
if (virAsprintf(&template, "%s/TEMPLATE", APPARMOR_DIR "/libvirt") < 0) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("template name exceeds maximum length"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!virFileExists(template)) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("template does not exist"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((tlen = virFileReadAll(template, MAX_FILE_LEN, &tcontent)) < 0) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("failed to read AppArmor template"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strstr(tcontent, template_name) == NULL) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("no replacement string in template"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto clean_tcontent;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strstr(tcontent, template_end) == NULL) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("no replacement string in template"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto clean_tcontent;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* '\nprofile <profile_name>\0' */
|
|
|
|
if (virAsprintf(&replace_name, "\nprofile %s", profile_name) == -1) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("could not allocate memory for profile name"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto clean_tcontent;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* '\n<profile_files>\n}\0' */
|
|
|
|
if (virAsprintf(&replace_files, "\n%s\n}", profile_files) == -1) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("could not allocate memory for profile files"));
|
2009-10-08 14:34:22 +00:00
|
|
|
VIR_FREE(replace_name);
|
|
|
|
goto clean_tcontent;
|
|
|
|
}
|
|
|
|
|
|
|
|
plen = tlen + strlen(replace_name) - strlen(template_name) +
|
|
|
|
strlen(replace_files) - strlen(template_end) + 1;
|
|
|
|
if (plen > MAX_FILE_LEN || plen < tlen) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("invalid length for new profile"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto clean_replace;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_ALLOC_N(pcontent, plen) < 0) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("could not allocate memory for profile"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto clean_replace;
|
|
|
|
}
|
|
|
|
pcontent[0] = '\0';
|
|
|
|
strcpy(pcontent, tcontent);
|
|
|
|
|
|
|
|
if (replace_string(pcontent, plen, template_name, replace_name) < 0)
|
|
|
|
goto clean_all;
|
|
|
|
|
|
|
|
if (replace_string(pcontent, plen, template_end, replace_files) < 0)
|
|
|
|
goto clean_all;
|
|
|
|
|
|
|
|
/* write the file */
|
|
|
|
if ((fd = open(profile, O_CREAT | O_EXCL | O_WRONLY, 0644)) == -1) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("failed to create profile"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto clean_all;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (safewrite(fd, pcontent, plen - 1) < 0) { /* don't write the '\0' */
|
2010-11-09 20:48:48 +00:00
|
|
|
VIR_FORCE_CLOSE(fd);
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("failed to write to profile"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto clean_all;
|
|
|
|
}
|
|
|
|
|
2010-11-09 20:48:48 +00:00
|
|
|
if (VIR_CLOSE(fd) != 0) {
|
2010-11-17 20:31:04 +00:00
|
|
|
vah_error(NULL, 0, _("failed to close or write to profile"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto clean_all;
|
|
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
|
|
|
|
clean_all:
|
|
|
|
VIR_FREE(pcontent);
|
|
|
|
clean_replace:
|
|
|
|
VIR_FREE(replace_name);
|
|
|
|
VIR_FREE(replace_files);
|
|
|
|
clean_tcontent:
|
|
|
|
VIR_FREE(tcontent);
|
|
|
|
end:
|
2011-04-03 09:21:17 +00:00
|
|
|
VIR_FREE(template);
|
2009-10-08 14:34:22 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Load an existing profile
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
parserLoad(const char *profile_name)
|
|
|
|
{
|
|
|
|
return parserCommand(profile_name, 'a');
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove an existing profile
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
parserRemove(const char *profile_name)
|
|
|
|
{
|
|
|
|
return parserCommand(profile_name, 'R');
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Replace an existing profile
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
parserReplace(const char *profile_name)
|
|
|
|
{
|
|
|
|
return parserCommand(profile_name, 'r');
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
valid_uuid(const char *uuid)
|
|
|
|
{
|
|
|
|
unsigned char rawuuid[VIR_UUID_BUFLEN];
|
|
|
|
|
|
|
|
if (strlen(uuid) != PROFILE_NAME_SIZE - 1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!STRPREFIX(uuid, AA_PREFIX))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (virUUIDParse(uuid + strlen(AA_PREFIX), rawuuid) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
valid_name(const char *name)
|
|
|
|
{
|
|
|
|
/* just try to filter out any dangerous characters in the name that can be
|
|
|
|
* used to subvert the profile */
|
|
|
|
const char *bad = " /[]*";
|
|
|
|
|
|
|
|
if (strlen(name) == 0 || strlen(name) > PATH_MAX - 1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (strcspn(name, bad) != strlen(name))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* see if one of the strings in arr starts with str */
|
|
|
|
static int
|
|
|
|
array_starts_with(const char *str, const char * const *arr, const long size)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
if (strlen(str) < strlen(arr[i]))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (STRPREFIX(str, arr[i]))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Don't allow access to special files or restricted paths such as /bin, /sbin,
|
|
|
|
* /usr/bin, /usr/sbin and /etc. This is in an effort to prevent read/write
|
|
|
|
* access to system files which could be used to elevate privileges. This is a
|
|
|
|
* safety measure in case libvirtd is under a restrictive profile and is
|
|
|
|
* subverted and trying to escape confinement.
|
|
|
|
*
|
|
|
|
* Note that we cannot exclude block devices because they are valid devices.
|
|
|
|
* The TEMPLATE file can be adjusted to explicitly disallow these if needed.
|
|
|
|
*
|
|
|
|
* RETURN: -1 on error, 0 if ok, 1 if blocked
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
valid_path(const char *path, const bool readonly)
|
|
|
|
{
|
|
|
|
struct stat sb;
|
2010-04-06 15:57:36 +00:00
|
|
|
int npaths, opaths;
|
2009-10-08 14:34:22 +00:00
|
|
|
const char * const restricted[] = {
|
|
|
|
"/bin/",
|
|
|
|
"/etc/",
|
|
|
|
"/lib",
|
|
|
|
"/lost+found/",
|
|
|
|
"/proc/",
|
|
|
|
"/sbin/",
|
|
|
|
"/selinux/",
|
|
|
|
"/sys/",
|
|
|
|
"/usr/bin/",
|
|
|
|
"/usr/lib",
|
|
|
|
"/usr/sbin/",
|
|
|
|
"/usr/share/",
|
|
|
|
"/usr/local/bin/",
|
|
|
|
"/usr/local/etc/",
|
|
|
|
"/usr/local/lib",
|
|
|
|
"/usr/local/sbin/"
|
|
|
|
};
|
|
|
|
/* these paths are ok for readonly, but not read/write */
|
|
|
|
const char * const restricted_rw[] = {
|
|
|
|
"/boot/",
|
|
|
|
"/vmlinuz",
|
|
|
|
"/initrd",
|
|
|
|
"/initrd.img"
|
|
|
|
};
|
2010-04-06 15:57:36 +00:00
|
|
|
/* override the above with these */
|
|
|
|
const char * const override[] = {
|
|
|
|
"/sys/devices/pci" /* for hostdev pci devices */
|
|
|
|
};
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
if (path == NULL || strlen(path) > PATH_MAX - 1) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("bad pathname"));
|
2009-10-08 14:34:22 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Don't allow double quotes, since we use them to quote the filename
|
|
|
|
* and this will confuse the apparmor parser.
|
|
|
|
*/
|
|
|
|
if (strchr(path, '"') != NULL)
|
|
|
|
return 1;
|
|
|
|
|
2009-11-13 14:22:20 +00:00
|
|
|
/* Require an absolute path */
|
|
|
|
if (STRNEQLEN(path, "/", 1))
|
|
|
|
return 1;
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
if (!virFileExists(path))
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_warning(_("path does not exist, skipping file type checks"));
|
2009-10-08 14:34:22 +00:00
|
|
|
else {
|
|
|
|
if (stat(path, &sb) == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
switch (sb.st_mode & S_IFMT) {
|
|
|
|
case S_IFDIR:
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
case S_IFSOCK:
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-06 15:57:36 +00:00
|
|
|
opaths = sizeof(override)/sizeof *(override);
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
npaths = sizeof(restricted)/sizeof *(restricted);
|
2010-04-06 15:57:36 +00:00
|
|
|
if (array_starts_with(path, restricted, npaths) == 0 &&
|
|
|
|
array_starts_with(path, override, opaths) != 0)
|
|
|
|
return 1;
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
npaths = sizeof(restricted_rw)/sizeof *(restricted_rw);
|
|
|
|
if (!readonly) {
|
|
|
|
if (array_starts_with(path, restricted_rw, npaths) == 0)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-11-11 21:59:50 +00:00
|
|
|
static int
|
|
|
|
verify_xpath_context(xmlXPathContextPtr ctxt)
|
|
|
|
{
|
|
|
|
int rc = -1;
|
|
|
|
char *tmp = NULL;
|
|
|
|
|
|
|
|
if (!ctxt) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_warning(_("Invalid context"));
|
2010-11-11 21:59:50 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if have <name> */
|
|
|
|
if (!(tmp = virXPathString("string(./name[1])", ctxt))) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_warning(_("Could not find <name>"));
|
2010-11-11 21:59:50 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
VIR_FREE(tmp);
|
|
|
|
|
|
|
|
/* check if have <uuid> */
|
|
|
|
if (!(tmp = virXPathString("string(./uuid[1])", ctxt))) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_warning(_("Could not find <uuid>"));
|
2010-11-11 21:59:50 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
VIR_FREE(tmp);
|
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-11-13 10:04:23 +00:00
|
|
|
/*
|
|
|
|
* Parse the xml we received to fill in the following:
|
|
|
|
* ctl->hvm
|
|
|
|
* ctl->arch
|
|
|
|
* ctl->bits
|
|
|
|
*
|
|
|
|
* These are suitable for setting up a virCapsPtr
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
caps_mockup(vahControl * ctl, const char *xmlStr)
|
|
|
|
{
|
|
|
|
int rc = -1;
|
|
|
|
xmlDocPtr xml = NULL;
|
|
|
|
xmlXPathContextPtr ctxt = NULL;
|
|
|
|
|
2011-09-14 14:17:57 +00:00
|
|
|
if (!(xml = virXMLParseStringCtxt(xmlStr, _("(domain_definition)"),
|
2011-09-08 13:25:01 +00:00
|
|
|
&ctxt))) {
|
2009-11-13 10:04:23 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2011-08-18 21:37:14 +00:00
|
|
|
if (!xmlStrEqual(ctxt->node->name, BAD_CAST "domain")) {
|
2011-11-25 12:23:03 +00:00
|
|
|
vah_error(NULL, 0, _("unexpected root element, expecting <domain>"));
|
2009-11-13 10:04:23 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2010-11-11 21:59:50 +00:00
|
|
|
/* Quick sanity check for some required elements */
|
|
|
|
if (verify_xpath_context(ctxt) != 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2010-02-04 21:52:34 +00:00
|
|
|
ctl->hvm = virXPathString("string(./os/type[1])", ctxt);
|
2009-11-13 10:04:23 +00:00
|
|
|
if (!ctl->hvm || STRNEQ(ctl->hvm, "hvm")) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 0, _("os.type is not 'hvm'"));
|
2009-11-13 10:04:23 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2010-02-04 21:52:34 +00:00
|
|
|
ctl->arch = virXPathString("string(./os/type[1]/@arch)", ctxt);
|
2009-11-13 10:04:23 +00:00
|
|
|
if (!ctl->arch) {
|
|
|
|
/* The XML we are given should have an arch, but in case it doesn't,
|
|
|
|
* just use the host's arch.
|
|
|
|
*/
|
|
|
|
struct utsname utsname;
|
|
|
|
|
|
|
|
/* Really, this never fails - look at the man-page. */
|
|
|
|
uname (&utsname);
|
|
|
|
if ((ctl->arch = strdup(utsname.machine)) == NULL) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 0, _("could not allocate memory"));
|
2009-11-13 10:04:23 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (STREQ(ctl->arch, "x86_64"))
|
|
|
|
ctl->bits = 64;
|
|
|
|
else
|
|
|
|
ctl->bits = 32;
|
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
xmlFreeDoc (xml);
|
|
|
|
xmlXPathFreeContext(ctxt);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2011-11-07 10:17:28 +00:00
|
|
|
static int aaDefaultConsoleType(const char *ostype ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
return VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL;
|
|
|
|
}
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
static int
|
|
|
|
get_definition(vahControl * ctl, const char *xmlStr)
|
|
|
|
{
|
|
|
|
int rc = -1;
|
|
|
|
virCapsGuestPtr guest; /* this is freed when caps is freed */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* mock up some capabilities. We don't currently use these explicitly,
|
|
|
|
* but need them for virDomainDefParseString().
|
|
|
|
*/
|
2009-11-13 10:04:23 +00:00
|
|
|
if (caps_mockup(ctl, xmlStr) != 0)
|
|
|
|
goto exit;
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2009-11-13 10:04:23 +00:00
|
|
|
if ((ctl->caps = virCapabilitiesNew(ctl->arch, 1, 1)) == NULL) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 0, _("could not allocate memory"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2011-11-07 10:17:28 +00:00
|
|
|
ctl->caps->defaultConsoleTargetType = aaDefaultConsoleType;
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
if ((guest = virCapabilitiesAddGuest(ctl->caps,
|
|
|
|
ctl->hvm,
|
2009-11-13 10:04:23 +00:00
|
|
|
ctl->arch,
|
2009-10-08 14:34:22 +00:00
|
|
|
ctl->bits,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
0,
|
|
|
|
NULL)) == NULL) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 0, _("could not allocate memory"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2011-07-11 17:29:09 +00:00
|
|
|
ctl->def = virDomainDefParseString(ctl->caps, xmlStr, -1,
|
2010-04-06 14:47:33 +00:00
|
|
|
VIR_DOMAIN_XML_INACTIVE);
|
2009-10-08 14:34:22 +00:00
|
|
|
if (ctl->def == NULL) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 0, _("could not parse XML"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ctl->def->name) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 0, _("could not find name in XML"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (valid_name(ctl->def->name) != 0) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 0, _("bad name"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
|
|
|
|
exit:
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
vah_add_file(virBufferPtr buf, const char *path, const char *perms)
|
|
|
|
{
|
|
|
|
char *tmp = NULL;
|
|
|
|
int rc = -1;
|
|
|
|
bool readonly = true;
|
|
|
|
|
|
|
|
if (path == NULL)
|
|
|
|
return rc;
|
|
|
|
|
2009-11-13 14:22:20 +00:00
|
|
|
/* Skip files without an absolute path. Not having one confuses the
|
|
|
|
* apparmor parser and this also ensures things like tcp consoles don't
|
|
|
|
* get added to the profile.
|
|
|
|
*/
|
|
|
|
if (STRNEQLEN(path, "/", 1)) {
|
|
|
|
vah_warning(path);
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_warning(_(" skipped non-absolute path"));
|
2009-11-13 14:22:20 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
if (virFileExists(path)) {
|
|
|
|
if ((tmp = realpath(path, NULL)) == NULL) {
|
|
|
|
vah_error(NULL, 0, path);
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _(" could not find realpath for disk"));
|
2009-10-08 14:34:22 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
if ((tmp = strdup(path)) == NULL)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
if (strchr(perms, 'w') != NULL)
|
|
|
|
readonly = false;
|
|
|
|
|
|
|
|
rc = valid_path(tmp, readonly);
|
|
|
|
if (rc != 0) {
|
|
|
|
if (rc > 0) {
|
|
|
|
vah_error(NULL, 0, path);
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _(" skipped restricted file"));
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf, " \"%s\" %s,\n", tmp, perms);
|
2009-11-13 14:25:30 +00:00
|
|
|
if (readonly) {
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf, " # don't audit writes to readonly files\n");
|
|
|
|
virBufferAsprintf(buf, " deny \"%s\" w,\n", tmp);
|
2009-11-13 14:25:30 +00:00
|
|
|
}
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
clean:
|
2009-12-09 23:00:50 +00:00
|
|
|
VIR_FREE(tmp);
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2011-09-28 07:43:39 +00:00
|
|
|
static int
|
|
|
|
vah_add_file_chardev(virBufferPtr buf,
|
|
|
|
const char *path,
|
|
|
|
const char *perms,
|
|
|
|
const int type)
|
|
|
|
{
|
|
|
|
char *pipe_in;
|
|
|
|
char *pipe_out;
|
|
|
|
int rc = -1;
|
|
|
|
|
|
|
|
if (type == VIR_DOMAIN_CHR_TYPE_PIPE) {
|
|
|
|
/* add the pipe input */
|
|
|
|
if (virAsprintf(&pipe_in, "%s.in", path) == -1) {
|
|
|
|
vah_error(NULL, 0, _("could not allocate memory"));
|
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vah_add_file(buf, pipe_in, perms) != 0)
|
|
|
|
goto clean_pipe_in;
|
|
|
|
|
|
|
|
/* add the pipe output */
|
|
|
|
if (virAsprintf(&pipe_out, "%s.out", path) == -1) {
|
|
|
|
vah_error(NULL, 0, _("could not allocate memory"));
|
|
|
|
goto clean_pipe_in;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vah_add_file(buf, pipe_out, perms) != 0)
|
|
|
|
goto clean_pipe_out;
|
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
clean_pipe_out:
|
|
|
|
VIR_FREE(pipe_out);
|
|
|
|
clean_pipe_in:
|
|
|
|
VIR_FREE(pipe_in);
|
|
|
|
} else {
|
|
|
|
/* add the file */
|
|
|
|
if (vah_add_file(buf, path, perms) != 0)
|
|
|
|
goto clean;
|
|
|
|
rc = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
clean:
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
static int
|
2010-04-06 15:57:36 +00:00
|
|
|
file_iterate_hostdev_cb(usbDevice *dev ATTRIBUTE_UNUSED,
|
|
|
|
const char *file, void *opaque)
|
|
|
|
{
|
|
|
|
virBufferPtr buf = opaque;
|
|
|
|
return vah_add_file(buf, file, "rw");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
file_iterate_pci_cb(pciDevice *dev ATTRIBUTE_UNUSED,
|
|
|
|
const char *file, void *opaque)
|
2009-10-08 14:34:22 +00:00
|
|
|
{
|
|
|
|
virBufferPtr buf = opaque;
|
|
|
|
return vah_add_file(buf, file, "rw");
|
|
|
|
}
|
|
|
|
|
2010-06-15 15:40:47 +00:00
|
|
|
static int
|
|
|
|
add_file_path(virDomainDiskDefPtr disk,
|
|
|
|
const char *path,
|
|
|
|
size_t depth,
|
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
virBufferPtr buf = opaque;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (depth == 0) {
|
|
|
|
if (disk->readonly)
|
|
|
|
ret = vah_add_file(buf, path, "r");
|
|
|
|
else
|
|
|
|
ret = vah_add_file(buf, path, "rw");
|
|
|
|
} else {
|
|
|
|
ret = vah_add_file(buf, path, "r");
|
|
|
|
}
|
|
|
|
|
2010-07-23 16:27:43 +00:00
|
|
|
if (ret != 0)
|
|
|
|
ret = -1;
|
|
|
|
|
2010-06-15 15:40:47 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
static int
|
|
|
|
get_files(vahControl * ctl)
|
|
|
|
{
|
|
|
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
|
|
|
int rc = -1;
|
|
|
|
int i;
|
|
|
|
char *uuid;
|
|
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
|
|
|
|
|
|
/* verify uuid is same as what we were given on the command line */
|
|
|
|
virUUIDFormat(ctl->def->uuid, uuidstr);
|
|
|
|
if (virAsprintf(&uuid, "%s%s", AA_PREFIX, uuidstr) == -1) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 0, _("could not allocate memory"));
|
2009-10-08 14:34:22 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STRNEQ(uuid, ctl->uuid)) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 0, _("given uuid does not match XML uuid"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
|
2010-06-15 15:40:47 +00:00
|
|
|
for (i = 0; i < ctl->def->ndisks; i++) {
|
2010-07-23 16:09:46 +00:00
|
|
|
/* XXX passing ignoreOpenFailure = true to get back to the behavior
|
|
|
|
* from before using virDomainDiskDefForeachPath. actually we should
|
|
|
|
* be passing ignoreOpenFailure = false and handle open errors more
|
2012-01-12 18:24:45 +00:00
|
|
|
* careful than just ignoring them.
|
|
|
|
* XXX2 - if we knew the qemu user:group here we could send it in
|
|
|
|
* so that the open could be re-tried as that user:group.
|
|
|
|
*/
|
2010-06-15 15:40:47 +00:00
|
|
|
int ret = virDomainDiskDefForeachPath(ctl->def->disks[i],
|
2010-06-15 16:58:58 +00:00
|
|
|
ctl->allowDiskFormatProbing,
|
2010-07-23 16:09:46 +00:00
|
|
|
true,
|
2012-01-12 18:24:45 +00:00
|
|
|
-1, -1 /* current uid:gid */
|
2010-06-15 15:40:47 +00:00
|
|
|
add_file_path,
|
|
|
|
&buf);
|
|
|
|
if (ret != 0)
|
|
|
|
goto clean;
|
|
|
|
}
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
for (i = 0; i < ctl->def->nserials; i++)
|
2010-09-23 17:22:44 +00:00
|
|
|
if (ctl->def->serials[i] &&
|
domain_conf: split source data out from ChrDef
This opens up the possibility of reusing the smaller ChrSourceDef
for both qemu monitor and a passthrough smartcard device.
* src/conf/domain_conf.h (_virDomainChrDef): Factor host
details...
(_virDomainChrSourceDef): ...into new struct.
(virDomainChrSourceDefFree): New prototype.
* src/conf/domain_conf.c (virDomainChrDefFree)
(virDomainChrDefParseXML, virDomainChrDefFormat): Split...
(virDomainChrSourceDefClear, virDomainChrSourceDefFree)
(virDomainChrSourceDefParseXML, virDomainChrSourceDefFormat):
...into new functions.
(virDomainChrDefParseTargetXML): Update clients to reflect type
split.
* src/vmx/vmx.c (virVMXParseSerial, virVMXParseParallel)
(virVMXFormatSerial, virVMXFormatParallel): Likewise.
* src/xen/xen_driver.c (xenUnifiedDomainOpenConsole): Likewise.
* src/xen/xend_internal.c (xenDaemonParseSxprChar)
(xenDaemonFormatSxprChr): Likewise.
* src/vbox/vbox_tmpl.c (vboxDomainDumpXML, vboxAttachSerial)
(vboxAttachParallel): Likewise.
* src/security/security_dac.c (virSecurityDACSetChardevLabel)
(virSecurityDACSetChardevCallback)
(virSecurityDACRestoreChardevLabel)
(virSecurityDACRestoreChardevCallback): Likewise.
* src/security/security_selinux.c (SELinuxSetSecurityChardevLabel)
(SELinuxSetSecurityChardevCallback)
(SELinuxRestoreSecurityChardevLabel)
(SELinuxSetSecurityChardevCallback): Likewise.
* src/security/virt-aa-helper.c (get_files): Likewise.
* src/lxc/lxc_driver.c (lxcVmStart, lxcDomainOpenConsole):
Likewise.
* src/uml/uml_conf.c (umlBuildCommandLineChr): Likewise.
* src/uml/uml_driver.c (umlIdentifyOneChrPTY, umlIdentifyChrPTY)
(umlDomainOpenConsole): Likewise.
* src/qemu/qemu_command.c (qemuBuildChrChardevStr)
(qemuBuildChrArgStr, qemuBuildCommandLine)
(qemuParseCommandLineChr): Likewise.
* src/qemu/qemu_domain.c (qemuDomainObjPrivateXMLFormat)
(qemuDomainObjPrivateXMLParse): Likewise.
* src/qemu/qemu_cgroup.c (qemuSetupChardevCgroup): Likewise.
* src/qemu/qemu_hotplug.c (qemuDomainAttachNetDevice): Likewise.
* src/qemu/qemu_driver.c (qemudFindCharDevicePTYsMonitor)
(qemudFindCharDevicePTYs, qemuPrepareChardevDevice)
(qemuPrepareMonitorChr, qemudShutdownVMDaemon)
(qemuDomainOpenConsole): Likewise.
* src/qemu/qemu_command.h (qemuBuildChrChardevStr)
(qemuBuildChrArgStr): Delete, now that they are static.
* src/libvirt_private.syms (domain_conf.h): New exports.
* cfg.mk (useless_free_options): Update list.
* tests/qemuxml2argvtest.c (testCompareXMLToArgvFiles): Update
tests.
2011-01-07 22:45:01 +00:00
|
|
|
(ctl->def->serials[i]->source.type == VIR_DOMAIN_CHR_TYPE_PTY ||
|
|
|
|
ctl->def->serials[i]->source.type == VIR_DOMAIN_CHR_TYPE_DEV ||
|
|
|
|
ctl->def->serials[i]->source.type == VIR_DOMAIN_CHR_TYPE_FILE ||
|
|
|
|
ctl->def->serials[i]->source.type == VIR_DOMAIN_CHR_TYPE_PIPE) &&
|
|
|
|
ctl->def->serials[i]->source.data.file.path)
|
2011-09-28 07:43:39 +00:00
|
|
|
if (vah_add_file_chardev(&buf,
|
|
|
|
ctl->def->serials[i]->source.data.file.path,
|
|
|
|
"rw",
|
|
|
|
ctl->def->serials[i]->source.type) != 0)
|
2009-10-08 14:34:22 +00:00
|
|
|
goto clean;
|
|
|
|
|
Allow multiple consoles per virtual guest
While Xen only has a single paravirt console, UML, and
QEMU both support multiple paravirt consoles. The LXC
driver can also be trivially made to support multiple
consoles. This patch extends the XML to allow multiple
<console> elements in the XML. It also makes the UML
and QEMU drivers support this config.
* src/conf/domain_conf.c, src/conf/domain_conf.h: Allow
multiple <console> devices
* src/lxc/lxc_driver.c, src/xen/xen_driver.c,
src/xenxs/xen_sxpr.c, src/xenxs/xen_xm.c: Update for
internal API changes
* src/security/security_selinux.c, src/security/virt-aa-helper.c:
Only label consoles that aren't a copy of the serial device
* src/qemu/qemu_command.c, src/qemu/qemu_driver.c,
src/qemu/qemu_process.c, src/uml/uml_conf.c,
src/uml/uml_driver.c: Support multiple console devices
* tests/qemuxml2xmltest.c, tests/qemuxml2argvtest.c: Extra
tests for multiple virtio consoles. Set QEMU_CAPS_CHARDEV
for all console /channel tests
* tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-auto.args,
tests/qemuxml2argvdata/qemuxml2argv-channel-virtio.args
tests/qemuxml2argvdata/qemuxml2argv-console-virtio.args: Update
for correct chardev syntax
* tests/qemuxml2argvdata/qemuxml2argv-console-virtio-many.args,
tests/qemuxml2argvdata/qemuxml2argv-console-virtio-many.xml: New
test file
2011-02-23 18:27:23 +00:00
|
|
|
for (i = 0; i < ctl->def->nconsoles; i++)
|
|
|
|
if (ctl->def->consoles[i] &&
|
|
|
|
(ctl->def->consoles[i]->source.type == VIR_DOMAIN_CHR_TYPE_PTY ||
|
|
|
|
ctl->def->consoles[i]->source.type == VIR_DOMAIN_CHR_TYPE_DEV ||
|
|
|
|
ctl->def->consoles[i]->source.type == VIR_DOMAIN_CHR_TYPE_FILE ||
|
|
|
|
ctl->def->consoles[i]->source.type == VIR_DOMAIN_CHR_TYPE_PIPE) &&
|
|
|
|
ctl->def->consoles[i]->source.data.file.path)
|
|
|
|
if (vah_add_file(&buf,
|
|
|
|
ctl->def->consoles[i]->source.data.file.path, "rw") != 0)
|
|
|
|
goto clean;
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2010-09-23 17:22:44 +00:00
|
|
|
for (i = 0 ; i < ctl->def->nparallels; i++)
|
|
|
|
if (ctl->def->parallels[i] &&
|
domain_conf: split source data out from ChrDef
This opens up the possibility of reusing the smaller ChrSourceDef
for both qemu monitor and a passthrough smartcard device.
* src/conf/domain_conf.h (_virDomainChrDef): Factor host
details...
(_virDomainChrSourceDef): ...into new struct.
(virDomainChrSourceDefFree): New prototype.
* src/conf/domain_conf.c (virDomainChrDefFree)
(virDomainChrDefParseXML, virDomainChrDefFormat): Split...
(virDomainChrSourceDefClear, virDomainChrSourceDefFree)
(virDomainChrSourceDefParseXML, virDomainChrSourceDefFormat):
...into new functions.
(virDomainChrDefParseTargetXML): Update clients to reflect type
split.
* src/vmx/vmx.c (virVMXParseSerial, virVMXParseParallel)
(virVMXFormatSerial, virVMXFormatParallel): Likewise.
* src/xen/xen_driver.c (xenUnifiedDomainOpenConsole): Likewise.
* src/xen/xend_internal.c (xenDaemonParseSxprChar)
(xenDaemonFormatSxprChr): Likewise.
* src/vbox/vbox_tmpl.c (vboxDomainDumpXML, vboxAttachSerial)
(vboxAttachParallel): Likewise.
* src/security/security_dac.c (virSecurityDACSetChardevLabel)
(virSecurityDACSetChardevCallback)
(virSecurityDACRestoreChardevLabel)
(virSecurityDACRestoreChardevCallback): Likewise.
* src/security/security_selinux.c (SELinuxSetSecurityChardevLabel)
(SELinuxSetSecurityChardevCallback)
(SELinuxRestoreSecurityChardevLabel)
(SELinuxSetSecurityChardevCallback): Likewise.
* src/security/virt-aa-helper.c (get_files): Likewise.
* src/lxc/lxc_driver.c (lxcVmStart, lxcDomainOpenConsole):
Likewise.
* src/uml/uml_conf.c (umlBuildCommandLineChr): Likewise.
* src/uml/uml_driver.c (umlIdentifyOneChrPTY, umlIdentifyChrPTY)
(umlDomainOpenConsole): Likewise.
* src/qemu/qemu_command.c (qemuBuildChrChardevStr)
(qemuBuildChrArgStr, qemuBuildCommandLine)
(qemuParseCommandLineChr): Likewise.
* src/qemu/qemu_domain.c (qemuDomainObjPrivateXMLFormat)
(qemuDomainObjPrivateXMLParse): Likewise.
* src/qemu/qemu_cgroup.c (qemuSetupChardevCgroup): Likewise.
* src/qemu/qemu_hotplug.c (qemuDomainAttachNetDevice): Likewise.
* src/qemu/qemu_driver.c (qemudFindCharDevicePTYsMonitor)
(qemudFindCharDevicePTYs, qemuPrepareChardevDevice)
(qemuPrepareMonitorChr, qemudShutdownVMDaemon)
(qemuDomainOpenConsole): Likewise.
* src/qemu/qemu_command.h (qemuBuildChrChardevStr)
(qemuBuildChrArgStr): Delete, now that they are static.
* src/libvirt_private.syms (domain_conf.h): New exports.
* cfg.mk (useless_free_options): Update list.
* tests/qemuxml2argvtest.c (testCompareXMLToArgvFiles): Update
tests.
2011-01-07 22:45:01 +00:00
|
|
|
(ctl->def->parallels[i]->source.type == VIR_DOMAIN_CHR_TYPE_PTY ||
|
|
|
|
ctl->def->parallels[i]->source.type == VIR_DOMAIN_CHR_TYPE_DEV ||
|
|
|
|
ctl->def->parallels[i]->source.type == VIR_DOMAIN_CHR_TYPE_FILE ||
|
|
|
|
ctl->def->parallels[i]->source.type == VIR_DOMAIN_CHR_TYPE_PIPE) &&
|
|
|
|
ctl->def->parallels[i]->source.data.file.path)
|
2011-09-28 07:43:39 +00:00
|
|
|
if (vah_add_file_chardev(&buf,
|
|
|
|
ctl->def->parallels[i]->source.data.file.path,
|
|
|
|
"rw",
|
|
|
|
ctl->def->parallels[i]->source.type) != 0)
|
2010-09-23 17:22:44 +00:00
|
|
|
goto clean;
|
|
|
|
|
|
|
|
for (i = 0 ; i < ctl->def->nchannels; i++)
|
|
|
|
if (ctl->def->channels[i] &&
|
domain_conf: split source data out from ChrDef
This opens up the possibility of reusing the smaller ChrSourceDef
for both qemu monitor and a passthrough smartcard device.
* src/conf/domain_conf.h (_virDomainChrDef): Factor host
details...
(_virDomainChrSourceDef): ...into new struct.
(virDomainChrSourceDefFree): New prototype.
* src/conf/domain_conf.c (virDomainChrDefFree)
(virDomainChrDefParseXML, virDomainChrDefFormat): Split...
(virDomainChrSourceDefClear, virDomainChrSourceDefFree)
(virDomainChrSourceDefParseXML, virDomainChrSourceDefFormat):
...into new functions.
(virDomainChrDefParseTargetXML): Update clients to reflect type
split.
* src/vmx/vmx.c (virVMXParseSerial, virVMXParseParallel)
(virVMXFormatSerial, virVMXFormatParallel): Likewise.
* src/xen/xen_driver.c (xenUnifiedDomainOpenConsole): Likewise.
* src/xen/xend_internal.c (xenDaemonParseSxprChar)
(xenDaemonFormatSxprChr): Likewise.
* src/vbox/vbox_tmpl.c (vboxDomainDumpXML, vboxAttachSerial)
(vboxAttachParallel): Likewise.
* src/security/security_dac.c (virSecurityDACSetChardevLabel)
(virSecurityDACSetChardevCallback)
(virSecurityDACRestoreChardevLabel)
(virSecurityDACRestoreChardevCallback): Likewise.
* src/security/security_selinux.c (SELinuxSetSecurityChardevLabel)
(SELinuxSetSecurityChardevCallback)
(SELinuxRestoreSecurityChardevLabel)
(SELinuxSetSecurityChardevCallback): Likewise.
* src/security/virt-aa-helper.c (get_files): Likewise.
* src/lxc/lxc_driver.c (lxcVmStart, lxcDomainOpenConsole):
Likewise.
* src/uml/uml_conf.c (umlBuildCommandLineChr): Likewise.
* src/uml/uml_driver.c (umlIdentifyOneChrPTY, umlIdentifyChrPTY)
(umlDomainOpenConsole): Likewise.
* src/qemu/qemu_command.c (qemuBuildChrChardevStr)
(qemuBuildChrArgStr, qemuBuildCommandLine)
(qemuParseCommandLineChr): Likewise.
* src/qemu/qemu_domain.c (qemuDomainObjPrivateXMLFormat)
(qemuDomainObjPrivateXMLParse): Likewise.
* src/qemu/qemu_cgroup.c (qemuSetupChardevCgroup): Likewise.
* src/qemu/qemu_hotplug.c (qemuDomainAttachNetDevice): Likewise.
* src/qemu/qemu_driver.c (qemudFindCharDevicePTYsMonitor)
(qemudFindCharDevicePTYs, qemuPrepareChardevDevice)
(qemuPrepareMonitorChr, qemudShutdownVMDaemon)
(qemuDomainOpenConsole): Likewise.
* src/qemu/qemu_command.h (qemuBuildChrChardevStr)
(qemuBuildChrArgStr): Delete, now that they are static.
* src/libvirt_private.syms (domain_conf.h): New exports.
* cfg.mk (useless_free_options): Update list.
* tests/qemuxml2argvtest.c (testCompareXMLToArgvFiles): Update
tests.
2011-01-07 22:45:01 +00:00
|
|
|
(ctl->def->channels[i]->source.type == VIR_DOMAIN_CHR_TYPE_PTY ||
|
|
|
|
ctl->def->channels[i]->source.type == VIR_DOMAIN_CHR_TYPE_DEV ||
|
|
|
|
ctl->def->channels[i]->source.type == VIR_DOMAIN_CHR_TYPE_FILE ||
|
|
|
|
ctl->def->channels[i]->source.type == VIR_DOMAIN_CHR_TYPE_PIPE) &&
|
|
|
|
ctl->def->channels[i]->source.data.file.path)
|
2011-09-28 07:43:39 +00:00
|
|
|
if (vah_add_file_chardev(&buf,
|
|
|
|
ctl->def->channels[i]->source.data.file.path,
|
|
|
|
"rw",
|
|
|
|
ctl->def->channels[i]->source.type) != 0)
|
2010-09-23 17:22:44 +00:00
|
|
|
goto clean;
|
|
|
|
|
2010-04-06 20:53:43 +00:00
|
|
|
if (ctl->def->os.kernel)
|
2009-10-08 14:34:22 +00:00
|
|
|
if (vah_add_file(&buf, ctl->def->os.kernel, "r") != 0)
|
|
|
|
goto clean;
|
|
|
|
|
2010-04-06 20:53:43 +00:00
|
|
|
if (ctl->def->os.initrd)
|
2009-10-08 14:34:22 +00:00
|
|
|
if (vah_add_file(&buf, ctl->def->os.initrd, "r") != 0)
|
|
|
|
goto clean;
|
|
|
|
|
|
|
|
if (ctl->def->os.loader && ctl->def->os.loader)
|
|
|
|
if (vah_add_file(&buf, ctl->def->os.loader, "r") != 0)
|
2010-04-06 20:53:43 +00:00
|
|
|
goto clean;
|
|
|
|
|
|
|
|
if (ctl->def->ngraphics == 1 &&
|
|
|
|
ctl->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL)
|
|
|
|
if (vah_add_file(&buf, ctl->def->graphics[0]->data.sdl.xauth,
|
|
|
|
"r") != 0)
|
2009-10-08 14:34:22 +00:00
|
|
|
goto clean;
|
|
|
|
|
|
|
|
for (i = 0; i < ctl->def->nhostdevs; i++)
|
|
|
|
if (ctl->def->hostdevs[i]) {
|
|
|
|
virDomainHostdevDefPtr dev = ctl->def->hostdevs[i];
|
|
|
|
switch (dev->source.subsys.type) {
|
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: {
|
2010-02-04 23:25:16 +00:00
|
|
|
usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus,
|
2010-03-04 11:48:16 +00:00
|
|
|
dev->source.subsys.u.usb.device);
|
2010-01-11 16:40:46 +00:00
|
|
|
|
|
|
|
if (usb == NULL)
|
|
|
|
continue;
|
|
|
|
|
2010-04-06 15:57:36 +00:00
|
|
|
rc = usbDeviceFileIterate(usb, file_iterate_hostdev_cb, &buf);
|
2010-02-04 23:25:16 +00:00
|
|
|
usbFreeDevice(usb);
|
2010-01-11 16:40:46 +00:00
|
|
|
if (rc != 0)
|
|
|
|
goto clean;
|
2009-10-08 14:34:22 +00:00
|
|
|
break;
|
|
|
|
}
|
2010-04-06 15:57:36 +00:00
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: {
|
2010-02-04 23:25:16 +00:00
|
|
|
pciDevice *pci = pciGetDevice(
|
2009-10-08 14:34:22 +00:00
|
|
|
dev->source.subsys.u.pci.domain,
|
|
|
|
dev->source.subsys.u.pci.bus,
|
|
|
|
dev->source.subsys.u.pci.slot,
|
|
|
|
dev->source.subsys.u.pci.function);
|
|
|
|
|
|
|
|
if (pci == NULL)
|
|
|
|
continue;
|
|
|
|
|
2010-04-06 15:57:36 +00:00
|
|
|
rc = pciDeviceFileIterate(pci, file_iterate_pci_cb, &buf);
|
2010-02-04 23:25:16 +00:00
|
|
|
pciFreeDevice(pci);
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2010-04-06 15:57:36 +00:00
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
default:
|
|
|
|
rc = 0;
|
|
|
|
break;
|
|
|
|
} /* switch */
|
|
|
|
}
|
|
|
|
|
2010-09-30 20:54:56 +00:00
|
|
|
if (ctl->newfile)
|
|
|
|
if (vah_add_file(&buf, ctl->newfile, "rw") != 0)
|
2009-10-08 14:34:22 +00:00
|
|
|
goto clean;
|
|
|
|
|
|
|
|
if (virBufferError(&buf)) {
|
2009-12-09 23:00:50 +00:00
|
|
|
virBufferFreeAndReset(&buf);
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(NULL, 0, _("failed to allocate file buffer"));
|
2009-12-09 23:00:50 +00:00
|
|
|
goto clean;
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
ctl->files = virBufferContentAndReset(&buf);
|
|
|
|
|
|
|
|
clean:
|
|
|
|
VIR_FREE(uuid);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
vahParseArgv(vahControl * ctl, int argc, char **argv)
|
|
|
|
{
|
|
|
|
int arg, idx = 0;
|
|
|
|
struct option opt[] = {
|
2010-06-15 16:58:58 +00:00
|
|
|
{"probing", 1, 0, 'p' },
|
2009-10-08 14:34:22 +00:00
|
|
|
{"add", 0, 0, 'a'},
|
|
|
|
{"create", 0, 0, 'c'},
|
|
|
|
{"dryrun", 0, 0, 'd'},
|
|
|
|
{"delete", 0, 0, 'D'},
|
|
|
|
{"add-file", 0, 0, 'f'},
|
2010-09-30 20:54:56 +00:00
|
|
|
{"append-file", 0, 0, 'F'},
|
2009-10-08 14:34:22 +00:00
|
|
|
{"help", 0, 0, 'h'},
|
|
|
|
{"replace", 0, 0, 'r'},
|
|
|
|
{"remove", 0, 0, 'R'},
|
|
|
|
{"uuid", 1, 0, 'u'},
|
|
|
|
{0, 0, 0, 0}
|
|
|
|
};
|
|
|
|
|
2010-09-30 20:54:56 +00:00
|
|
|
while ((arg = getopt_long(argc, argv, "acdDhrRH:b:u:p:f:F:", opt,
|
2009-10-08 14:34:22 +00:00
|
|
|
&idx)) != -1) {
|
|
|
|
switch (arg) {
|
|
|
|
case 'a':
|
|
|
|
ctl->cmd = 'a';
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
ctl->cmd = 'c';
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
ctl->dryrun = true;
|
|
|
|
break;
|
|
|
|
case 'D':
|
|
|
|
ctl->cmd = 'D';
|
|
|
|
break;
|
|
|
|
case 'f':
|
2010-09-30 20:54:56 +00:00
|
|
|
case 'F':
|
|
|
|
if ((ctl->newfile = strdup(optarg)) == NULL)
|
2010-11-17 20:31:04 +00:00
|
|
|
vah_error(ctl, 1, _("could not allocate memory for disk"));
|
2010-09-30 20:54:56 +00:00
|
|
|
ctl->append = arg == 'F';
|
2009-10-08 14:34:22 +00:00
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
vah_usage();
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
ctl->cmd = 'r';
|
|
|
|
break;
|
|
|
|
case 'R':
|
|
|
|
ctl->cmd = 'R';
|
|
|
|
break;
|
|
|
|
case 'u':
|
|
|
|
if (strlen(optarg) > PROFILE_NAME_SIZE - 1)
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("invalid UUID"));
|
2009-10-08 14:34:22 +00:00
|
|
|
if (virStrcpy((char *) ctl->uuid, optarg,
|
|
|
|
PROFILE_NAME_SIZE) == NULL)
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("error copying UUID"));
|
2009-10-08 14:34:22 +00:00
|
|
|
break;
|
2010-06-15 16:58:58 +00:00
|
|
|
case 'p':
|
|
|
|
if (STREQ(optarg, "1"))
|
|
|
|
ctl->allowDiskFormatProbing = true;
|
|
|
|
else
|
|
|
|
ctl->allowDiskFormatProbing = false;
|
|
|
|
break;
|
2009-10-08 14:34:22 +00:00
|
|
|
default:
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("unsupported option"));
|
2009-10-08 14:34:22 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (strchr("acDrR", ctl->cmd) == NULL)
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("bad command"));
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
if (valid_uuid(ctl->uuid) != 0)
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("invalid UUID"));
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
if (!ctl->cmd) {
|
|
|
|
vah_usage();
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctl->cmd == 'c' || ctl->cmd == 'r') {
|
|
|
|
char *xmlStr = NULL;
|
|
|
|
if (virFileReadLimFD(STDIN_FILENO, MAX_FILE_LEN, &xmlStr) < 0)
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("could not read xml file"));
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
if (get_definition(ctl, xmlStr) != 0 || ctl->def == NULL) {
|
|
|
|
VIR_FREE(xmlStr);
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("could not get VM definition"));
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
VIR_FREE(xmlStr);
|
|
|
|
|
|
|
|
if (get_files(ctl) != 0)
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("invalid VM definition"));
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* virt-aa-helper -c -u UUID < file.xml
|
|
|
|
* virt-aa-helper -r -u UUID [-f <file>] < file.xml
|
|
|
|
* virt-aa-helper -a -u UUID
|
|
|
|
* virt-aa-helper -R -u UUID
|
|
|
|
* virt-aa-helper -D -u UUID
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
vahControl _ctl, *ctl = &_ctl;
|
|
|
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
|
|
|
int rc = -1;
|
2011-04-03 09:21:17 +00:00
|
|
|
char *profile = NULL;
|
|
|
|
char *include_file = NULL;
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2010-11-16 19:01:37 +00:00
|
|
|
if (setlocale(LC_ALL, "") == NULL ||
|
|
|
|
bindtextdomain(PACKAGE, LOCALEDIR) == NULL ||
|
|
|
|
textdomain(PACKAGE) == NULL) {
|
2010-11-17 20:31:04 +00:00
|
|
|
fprintf(stderr, _("%s: initialization failed\n"), argv[0]);
|
2010-11-16 19:01:37 +00:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
/* clear the environment */
|
|
|
|
environ = NULL;
|
|
|
|
if (setenv("PATH", "/sbin:/usr/sbin", 1) != 0) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("could not set PATH"));
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
2010-05-12 17:23:06 +00:00
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
if (setenv("IFS", " \t\n", 1) != 0) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("could not set IFS"));
|
2009-10-08 14:34:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!(progname = strrchr(argv[0], '/')))
|
|
|
|
progname = argv[0];
|
|
|
|
else
|
|
|
|
progname++;
|
|
|
|
|
|
|
|
memset(ctl, 0, sizeof(vahControl));
|
|
|
|
|
2012-01-27 10:14:21 +00:00
|
|
|
if (virRandomInitialize(time(NULL) ^ getpid()) < 0)
|
|
|
|
vah_error(ctl, 1, _("could not initialize random generator"));
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
if (vahParseArgv(ctl, argc, argv) != 0)
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("could not parse arguments"));
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2011-04-03 09:21:17 +00:00
|
|
|
if (virAsprintf(&profile, "%s/%s",
|
|
|
|
APPARMOR_DIR "/libvirt", ctl->uuid) < 0)
|
|
|
|
vah_error(ctl, 0, _("could not allocate memory"));
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2011-04-03 09:21:17 +00:00
|
|
|
if (virAsprintf(&include_file, "%s/%s.files",
|
|
|
|
APPARMOR_DIR "/libvirt", ctl->uuid) < 0)
|
|
|
|
vah_error(ctl, 0, _("could not allocate memory"));
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
if (ctl->cmd == 'a')
|
|
|
|
rc = parserLoad(ctl->uuid);
|
|
|
|
else if (ctl->cmd == 'R' || ctl->cmd == 'D') {
|
|
|
|
rc = parserRemove(ctl->uuid);
|
|
|
|
if (ctl->cmd == 'D') {
|
|
|
|
unlink(include_file);
|
|
|
|
unlink(profile);
|
|
|
|
}
|
|
|
|
} else if (ctl->cmd == 'c' || ctl->cmd == 'r') {
|
|
|
|
char *included_files = NULL;
|
|
|
|
|
2010-05-12 17:23:06 +00:00
|
|
|
if (ctl->cmd == 'c' && virFileExists(profile)) {
|
|
|
|
vah_error(ctl, 1, _("profile exists"));
|
|
|
|
}
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2010-09-30 20:54:56 +00:00
|
|
|
if (ctl->append && ctl->newfile) {
|
|
|
|
if (vah_add_file(&buf, ctl->newfile, "rw") != 0)
|
|
|
|
goto clean;
|
|
|
|
} else {
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(&buf, " \"%s/log/libvirt/**/%s.log\" w,\n",
|
2010-11-16 14:54:17 +00:00
|
|
|
LOCALSTATEDIR, ctl->def->name);
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(&buf, " \"%s/lib/libvirt/**/%s.monitor\" rw,\n",
|
2010-11-16 14:54:17 +00:00
|
|
|
LOCALSTATEDIR, ctl->def->name);
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(&buf, " \"%s/run/libvirt/**/%s.pid\" rwk,\n",
|
2010-11-16 14:54:17 +00:00
|
|
|
LOCALSTATEDIR, ctl->def->name);
|
2011-07-14 17:06:20 +00:00
|
|
|
virBufferAsprintf(&buf, " \"/run/libvirt/**/%s.pid\" rwk,\n",
|
|
|
|
ctl->def->name);
|
2011-12-02 19:10:58 +00:00
|
|
|
virBufferAsprintf(&buf, " \"%s/run/libvirt/**/*.tunnelmigrate.dest.%s\" rw,\n",
|
|
|
|
LOCALSTATEDIR, ctl->def->name);
|
|
|
|
virBufferAsprintf(&buf, " \"/run/libvirt/**/*.tunnelmigrate.dest.%s\" rw,\n",
|
|
|
|
ctl->def->name);
|
2010-09-30 20:54:56 +00:00
|
|
|
if (ctl->files)
|
2011-04-14 19:27:47 +00:00
|
|
|
virBufferAdd(&buf, ctl->files, -1);
|
2010-09-30 20:54:56 +00:00
|
|
|
}
|
2009-10-08 14:34:22 +00:00
|
|
|
|
2009-12-09 23:00:50 +00:00
|
|
|
if (virBufferError(&buf)) {
|
|
|
|
virBufferFreeAndReset(&buf);
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 1, _("failed to allocate buffer"));
|
2009-12-09 23:00:50 +00:00
|
|
|
}
|
2009-10-08 14:34:22 +00:00
|
|
|
|
|
|
|
included_files = virBufferContentAndReset(&buf);
|
|
|
|
|
|
|
|
/* (re)create the include file using included_files */
|
|
|
|
if (ctl->dryrun) {
|
|
|
|
vah_info(include_file);
|
|
|
|
vah_info(included_files);
|
|
|
|
rc = 0;
|
|
|
|
} else if ((rc = update_include_file(include_file,
|
2010-09-30 20:54:56 +00:00
|
|
|
included_files,
|
|
|
|
ctl->append)) != 0)
|
2009-10-08 14:34:22 +00:00
|
|
|
goto clean;
|
|
|
|
|
|
|
|
|
|
|
|
/* create the profile from TEMPLATE */
|
|
|
|
if (ctl->cmd == 'c') {
|
|
|
|
char *tmp = NULL;
|
|
|
|
if (virAsprintf(&tmp, " #include <libvirt/%s.files>\n",
|
|
|
|
ctl->uuid) == -1) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 0, _("could not allocate memory"));
|
2009-10-08 14:34:22 +00:00
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctl->dryrun) {
|
|
|
|
vah_info(profile);
|
|
|
|
vah_info(ctl->uuid);
|
|
|
|
vah_info(tmp);
|
|
|
|
rc = 0;
|
|
|
|
} else if ((rc = create_profile(profile, ctl->uuid, tmp)) != 0) {
|
2010-05-12 17:23:06 +00:00
|
|
|
vah_error(ctl, 0, _("could not create profile"));
|
2009-10-08 14:34:22 +00:00
|
|
|
unlink(include_file);
|
|
|
|
}
|
|
|
|
VIR_FREE(tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rc == 0 && !ctl->dryrun) {
|
|
|
|
if (ctl->cmd == 'c')
|
|
|
|
rc = parserLoad(ctl->uuid);
|
|
|
|
else
|
|
|
|
rc = parserReplace(ctl->uuid);
|
|
|
|
|
|
|
|
/* cleanup */
|
|
|
|
if (rc != 0) {
|
|
|
|
unlink(include_file);
|
|
|
|
if (ctl->cmd == 'c')
|
|
|
|
unlink(profile);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
clean:
|
|
|
|
VIR_FREE(included_files);
|
|
|
|
}
|
|
|
|
|
|
|
|
vahDeinit(ctl);
|
2011-04-03 09:21:17 +00:00
|
|
|
|
|
|
|
VIR_FREE(profile);
|
|
|
|
VIR_FREE(include_file);
|
|
|
|
|
2009-10-08 14:34:22 +00:00
|
|
|
exit(rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
|
|
|
|
}
|