Actually implement the autostart API in qemud by creating
symlinks in the autostart directories.
Signed-off-by: Mark McLoughlin <markmc(a)redhat.com>
Index: libvirt/qemud/internal.h
===================================================================
--- libvirt.orig/qemud/internal.h
+++ libvirt/qemud/internal.h
@@ -205,6 +205,7 @@ struct qemud_vm {
int ntapfds;
char configFile[PATH_MAX];
+ char autostartLink[PATH_MAX];
struct qemud_vm_def *def; /* The current definition */
struct qemud_vm_def *newDef; /* New definition to activate at shutdown */
@@ -241,6 +242,7 @@ struct qemud_network_def {
/* Virtual Network runtime state */
struct qemud_network {
char configFile[PATH_MAX];
+ char autostartLink[PATH_MAX];
struct qemud_network_def *def; /* The current definition */
struct qemud_network_def *newDef; /* New definition to activate at shutdown */
@@ -293,6 +295,8 @@ struct qemud_server {
iptablesContext *iptables;
char configDir[PATH_MAX];
char networkConfigDir[PATH_MAX];
+ char autostartDir[PATH_MAX];
+ char networkAutostartDir[PATH_MAX];
char errorMessage[QEMUD_MAX_ERROR_LEN];
int errorCode;
unsigned int shutdown : 1;
Index: libvirt/qemud/conf.c
===================================================================
--- libvirt.orig/qemud/conf.c
+++ libvirt/qemud/conf.c
@@ -137,7 +137,7 @@ qemudMakeConfigPath(const char *configDi
return 0;
}
-static int
+int
qemudEnsureDir(const char *path)
{
struct stat st;
@@ -1287,6 +1287,14 @@ qemudSaveVMDef(struct qemud_server *serv
"cannot construct config file path");
return -1;
}
+
+ if (qemudMakeConfigPath(server->autostartDir, def->name, ".xml",
+ vm->autostartLink, PATH_MAX) < 0) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "cannot construct autostart link path");
+ vm->configFile[0] = '\0';
+ return -1;
+ }
}
return qemudSaveConfig(server, vm, def);
@@ -1666,6 +1674,14 @@ qemudSaveNetworkDef(struct qemud_server
"cannot construct config file path");
return -1;
}
+
+ if (qemudMakeConfigPath(server->networkAutostartDir, def->name,
".xml",
+ network->autostartLink, PATH_MAX) < 0) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "cannot construct autostart link path");
+ network->configFile[0] = '\0';
+ return -1;
+ }
}
return qemudSaveNetworkConfig(server, network, def);
@@ -1735,11 +1751,105 @@ compareFileToNameSuffix(const char *file
return 0;
}
+static int
+checkLinkPointsTo(const char *checkLink,
+ const char *checkDest)
+{
+ char dest[PATH_MAX];
+ char real[PATH_MAX];
+ char checkReal[PATH_MAX];
+ int n;
+ int passed = 0;
+
+ /* read the link destination */
+ if ((n = readlink(checkLink, dest, PATH_MAX)) < 0) {
+ switch (errno) {
+ case ENOENT:
+ case ENOTDIR:
+ break;
+
+ case EINVAL:
+ qemudLog(QEMUD_WARN, "Autostart file '%s' is not a
symlink",
+ checkLink);
+ break;
+
+ default:
+ qemudLog(QEMUD_WARN, "Failed to read autostart symlink '%s':
%s",
+ checkLink, strerror(errno));
+ break;
+ }
+
+ goto failed;
+ } else if (n >= PATH_MAX) {
+ qemudLog(QEMUD_WARN, "Symlink '%s' contents too long to fit in
buffer",
+ checkLink);
+ goto failed;
+ }
+
+ dest[n] = '\0';
+
+ /* make absolute */
+ if (dest[0] != '/') {
+ char dir[PATH_MAX];
+ char tmp[PATH_MAX];
+ char *p;
+
+ strncpy(dir, checkLink, PATH_MAX);
+ dir[PATH_MAX] = '\0';
+
+ if (!(p = strrchr(dir, '/'))) {
+ qemudLog(QEMUD_WARN, "Symlink path '%s' is not absolute",
checkLink);
+ goto failed;
+ }
+
+ if (p == dir) /* handle unlikely root dir case */
+ p++;
+
+ *p = '\0';
+
+ if (qemudMakeConfigPath(dir, dest, NULL, tmp, PATH_MAX) < 0) {
+ qemudLog(QEMUD_WARN, "Path '%s/%s' is too long", dir,
dest);
+ goto failed;
+ }
+
+ strncpy(dest, tmp, PATH_MAX);
+ dest[PATH_MAX] = '\0';
+ }
+
+ /* canonicalize both paths */
+ if (!realpath(dest, real)) {
+ qemudLog(QEMUD_WARN, "Failed to expand path '%s' :%s",
+ dest, strerror(errno));
+ strncpy(real, dest, PATH_MAX);
+ real[PATH_MAX] = '\0';
+ }
+
+ if (!realpath(checkDest, checkReal)) {
+ qemudLog(QEMUD_WARN, "Failed to expand path '%s' :%s",
+ checkDest, strerror(errno));
+ strncpy(checkReal, checkDest, PATH_MAX);
+ checkReal[PATH_MAX] = '\0';
+ }
+
+ /* compare */
+ if (strcmp(checkReal, real) != 0) {
+ qemudLog(QEMUD_WARN, "Autostart link '%s' is not a symlink to
'%s', ignoring",
+ checkLink, checkReal);
+ goto failed;
+ }
+
+ passed = 1;
+
+ failed:
+ return passed;
+}
+
static struct qemud_vm *
qemudLoadConfig(struct qemud_server *server,
const char *file,
const char *path,
- const char *xml) {
+ const char *xml,
+ const char *autostartLink) {
struct qemud_vm_def *def;
struct qemud_vm *vm;
@@ -1765,6 +1875,11 @@ qemudLoadConfig(struct qemud_server *ser
strncpy(vm->configFile, path, PATH_MAX);
vm->configFile[PATH_MAX-1] = '\0';
+ strncpy(vm->autostartLink, autostartLink, PATH_MAX);
+ vm->autostartLink[PATH_MAX-1] = '\0';
+
+ vm->autostart = checkLinkPointsTo(vm->autostartLink, vm->configFile);
+
return vm;
}
@@ -1772,7 +1887,8 @@ static struct qemud_network *
qemudLoadNetworkConfig(struct qemud_server *server,
const char *file,
const char *path,
- const char *xml) {
+ const char *xml,
+ const char *autostartLink) {
struct qemud_network_def *def;
struct qemud_network *network;
@@ -1798,12 +1914,18 @@ qemudLoadNetworkConfig(struct qemud_serv
strncpy(network->configFile, path, PATH_MAX);
network->configFile[PATH_MAX-1] = '\0';
+ strncpy(network->autostartLink, autostartLink, PATH_MAX);
+ network->autostartLink[PATH_MAX-1] = '\0';
+
+ network->autostart = checkLinkPointsTo(network->autostartLink,
network->configFile);
+
return network;
}
static
int qemudScanConfigDir(struct qemud_server *server,
const char *configDir,
+ const char *autostartDir,
int isGuest) {
DIR *dir;
struct dirent *entry;
@@ -1819,6 +1941,7 @@ int qemudScanConfigDir(struct qemud_serv
while ((entry = readdir(dir))) {
char xml[QEMUD_MAX_XML_LEN];
char path[PATH_MAX];
+ char autostartLink[PATH_MAX];
if (entry->d_name[0] == '.')
continue;
@@ -1829,13 +1952,19 @@ int qemudScanConfigDir(struct qemud_serv
continue;
}
+ if (qemudMakeConfigPath(autostartDir, entry->d_name, NULL, autostartLink,
PATH_MAX) < 0) {
+ qemudLog(QEMUD_WARN, "Autostart link path '%s/%s' is too
long",
+ autostartDir, entry->d_name);
+ continue;
+ }
+
if (!qemudReadFile(path, xml, QEMUD_MAX_XML_LEN))
continue;
if (isGuest)
- qemudLoadConfig(server, entry->d_name, path, xml);
+ qemudLoadConfig(server, entry->d_name, path, xml, autostartLink);
else
- qemudLoadNetworkConfig(server, entry->d_name, path, xml);
+ qemudLoadNetworkConfig(server, entry->d_name, path, xml, autostartLink);
}
closedir(dir);
@@ -1845,9 +1974,9 @@ int qemudScanConfigDir(struct qemud_serv
/* Scan for all guest and network config files */
int qemudScanConfigs(struct qemud_server *server) {
- if (qemudScanConfigDir(server, server->configDir, 1) < 0)
+ if (qemudScanConfigDir(server, server->configDir, server->autostartDir, 1) <
0)
return -1;
- return qemudScanConfigDir(server, server->networkConfigDir, 0);
+ return qemudScanConfigDir(server, server->networkConfigDir,
server->networkAutostartDir, 0);
}
/* Simple grow-on-demand string buffer */
Index: libvirt/qemud/driver.c
===================================================================
--- libvirt.orig/qemud/driver.c
+++ libvirt/qemud/driver.c
@@ -502,7 +502,12 @@ int qemudDomainUndefine(struct qemud_ser
if (qemudDeleteConfig(server, vm->configFile, vm->def->name) < 0)
return -1;
+ if (unlink(vm->autostartLink) < 0 && errno != ENOENT && errno
!= ENOTDIR)
+ qemudLog(QEMUD_WARN, "Failed to delete autostart link '%s':
%s",
+ vm->autostartLink, strerror(errno));
+
vm->configFile[0] = '\0';
+ vm->autostartLink[0] = '\0';
qemudRemoveInactiveVM(server, vm);
@@ -539,6 +544,31 @@ int qemudDomainSetAutostart(struct qemud
if (vm->autostart == autostart)
return 0;
+ if (autostart) {
+ int err;
+
+ if ((err = qemudEnsureDir(server->autostartDir))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "cannot create autostart directory %s: %s",
+ server->autostartDir, strerror(err));
+ return -1;
+ }
+
+ if (symlink(vm->configFile, vm->autostartLink) < 0) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "Failed to create symlink '%s' to '%s':
%s",
+ vm->autostartLink, vm->configFile, strerror(errno));
+ return -1;
+ }
+ } else {
+ if (unlink(vm->autostartLink) < 0 && errno != ENOENT &&
errno != ENOTDIR) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "Failed to delete symlink '%s': %s",
+ vm->autostartLink, strerror(errno));
+ return -1;
+ }
+ }
+
vm->autostart = autostart;
return 0;
@@ -657,7 +687,12 @@ int qemudNetworkUndefine(struct qemud_se
if (qemudDeleteConfig(server, network->configFile, network->def->name) <
0)
return -1;
+ if (unlink(network->autostartLink) < 0 && errno != ENOENT &&
errno != ENOTDIR)
+ qemudLog(QEMUD_WARN, "Failed to delete autostart link '%s':
%s",
+ network->autostartLink, strerror(errno));
+
network->configFile[0] = '\0';
+ network->autostartLink[0] = '\0';
qemudRemoveInactiveNetwork(server, network);
@@ -750,6 +785,31 @@ int qemudNetworkSetAutostart(struct qemu
if (network->autostart == autostart)
return 0;
+ if (autostart) {
+ int err;
+
+ if ((err = qemudEnsureDir(server->networkAutostartDir))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "cannot create autostart directory %s: %s",
+ server->networkAutostartDir, strerror(err));
+ return -1;
+ }
+
+ if (symlink(network->configFile, network->autostartLink) < 0) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "Failed to create symlink '%s' to '%s':
%s",
+ network->autostartLink, network->configFile,
strerror(errno));
+ return -1;
+ }
+ } else {
+ if (unlink(network->autostartLink) < 0 && errno != ENOENT
&& errno != ENOTDIR) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "Failed to delete symlink '%s': %s",
+ network->autostartLink, strerror(errno));
+ return -1;
+ }
+ }
+
network->autostart = autostart;
return 0;
Index: libvirt/qemud/qemud.c
===================================================================
--- libvirt.orig/qemud/qemud.c
+++ libvirt/qemud/qemud.c
@@ -357,6 +357,8 @@ static int qemudListenUnix(struct qemud_
static int qemudInitPaths(int sys,
char *configDir,
char *networkConfigDir,
+ char *autostartDir,
+ char *networkAutostartDir,
char *sockname,
char *roSockname,
int maxlen) {
@@ -376,6 +378,12 @@ static int qemudInitPaths(int sys,
if (snprintf(networkConfigDir, maxlen, "%s/libvirt/qemu/networks",
SYSCONF_DIR) >= maxlen)
goto snprintf_error;
+ if (snprintf(autostartDir, maxlen, "%s/libvirt/qemu/autostart",
SYSCONF_DIR) >= maxlen)
+ goto snprintf_error;
+
+ if (snprintf(networkAutostartDir, maxlen,
"%s/libvirt/qemu/networks/autostart", SYSCONF_DIR) >= maxlen)
+ goto snprintf_error;
+
if (snprintf(sockname, maxlen, "%s/run/libvirt/qemud-sock",
LOCAL_STATE_DIR) >= maxlen)
goto snprintf_error;
@@ -400,6 +408,12 @@ static int qemudInitPaths(int sys,
if (snprintf(networkConfigDir, maxlen, "%s/.libvirt/qemu/networks",
pw->pw_dir) >= maxlen)
goto snprintf_error;
+ if (snprintf(autostartDir, maxlen, "%s/.libvirt/qemu/autostart",
pw->pw_dir) >= maxlen)
+ goto snprintf_error;
+
+ if (snprintf(networkAutostartDir, maxlen,
"%s/.libvirt/qemu/networks/autostart", pw->pw_dir) >= maxlen)
+ goto snprintf_error;
+
if (snprintf(sockname, maxlen, "(a)%s/.libvirt/qemud-sock",
pw->pw_dir) >= maxlen)
goto snprintf_error;
}
@@ -430,6 +444,7 @@ static struct qemud_server *qemudInitial
roSockname[0] = '\0';
if (qemudInitPaths(sys, server->configDir, server->networkConfigDir,
+ server->autostartDir, server->networkAutostartDir,
sockname, roSockname, PATH_MAX) < 0)
goto cleanup;
Index: libvirt/qemud/conf.h
===================================================================
--- libvirt.orig/qemud/conf.h
+++ libvirt/qemud/conf.h
@@ -34,6 +34,7 @@ int qemudScanConfigs
int qemudDeleteConfig (struct qemud_server *server,
const char *configFile,
const char *name);
+int qemudEnsureDir (const char *path);
void qemudFreeVMDef (struct qemud_vm_def *vm);
void qemudFreeVM (struct qemud_vm *vm);
--