From 56d2857f95dc91d7e7b200285437aa57e46c84e2 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Fri, 23 Feb 2007 09:07:41 +0000 Subject: [PATCH] Fri Feb 23 09:03:19 IST 2007 Mark McLoughlin * qemud/driver.c: maintain the autostart flag on disk using symlinks from the "autostart" directories to the corresponding config files. * qemud/internal.h: add paths to the autostart links to the vm/network structures and paths to the autostart dirs to the server struct. * qemud/qemud.c: initialize the server autostart dir patches. * qemud/conf.h: expose qemudEnsureDir() * qemud/conf.c: check the autostart symlinks when loading config files at startup. --- ChangeLog | 18 ++++++ qemud/conf.c | 143 ++++++++++++++++++++++++++++++++++++++++++++--- qemud/conf.h | 1 + qemud/driver.c | 60 ++++++++++++++++++++ qemud/internal.h | 4 ++ qemud/qemud.c | 15 +++++ 6 files changed, 234 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index ed0057ac21..449f04ae8a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +Fri Feb 23 09:03:19 IST 2007 Mark McLoughlin + + * qemud/driver.c: maintain the autostart flag on disk + using symlinks from the "autostart" directories to + the corresponding config files. + + * qemud/internal.h: add paths to the autostart links + to the vm/network structures and paths to the autostart + dirs to the server struct. + + * qemud/qemud.c: initialize the server autostart dir + patches. + + * qemud/conf.h: expose qemudEnsureDir() + + * qemud/conf.c: check the autostart symlinks when + loading config files at startup. + Fri Feb 23 09:00:13 IST 2007 Mark McLoughlin * qemud/protocol.h: add the (domain/network)(Get/Set)Autostart diff --git a/qemud/conf.c b/qemud/conf.c index 3f9a6a77e3..d662c58f81 100644 --- a/qemud/conf.c +++ b/qemud/conf.c @@ -137,7 +137,7 @@ qemudMakeConfigPath(const char *configDir, return 0; } -static int +int qemudEnsureDir(const char *path) { struct stat st; @@ -1287,6 +1287,14 @@ qemudSaveVMDef(struct qemud_server *server, "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 *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); @@ -1734,11 +1750,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; @@ -1764,6 +1874,11 @@ qemudLoadConfig(struct qemud_server *server, 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; } @@ -1771,7 +1886,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; @@ -1797,12 +1913,18 @@ qemudLoadNetworkConfig(struct qemud_server *server, 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; @@ -1818,6 +1940,7 @@ int qemudScanConfigDir(struct qemud_server *server, while ((entry = readdir(dir))) { char xml[QEMUD_MAX_XML_LEN]; char path[PATH_MAX]; + char autostartLink[PATH_MAX]; if (entry->d_name[0] == '.') continue; @@ -1828,13 +1951,19 @@ int qemudScanConfigDir(struct qemud_server *server, 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); @@ -1844,9 +1973,9 @@ int qemudScanConfigDir(struct qemud_server *server, /* 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 */ diff --git a/qemud/conf.h b/qemud/conf.h index 548484cc20..9069ee6842 100644 --- a/qemud/conf.h +++ b/qemud/conf.h @@ -34,6 +34,7 @@ int qemudScanConfigs (struct qemud_server *server); 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); diff --git a/qemud/driver.c b/qemud/driver.c index 8aa049f026..1aa3b396a4 100644 --- a/qemud/driver.c +++ b/qemud/driver.c @@ -502,7 +502,12 @@ int qemudDomainUndefine(struct qemud_server *server, const unsigned char *uuid) 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_server *server, 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_server *server, const unsigned char *uuid) 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 qemud_server *server, 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; diff --git a/qemud/internal.h b/qemud/internal.h index e575d5bb7c..e43222fff5 100644 --- a/qemud/internal.h +++ b/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; diff --git a/qemud/qemud.c b/qemud/qemud.c index 36ad1616af..3961c6415e 100644 --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -357,6 +357,8 @@ static int qemudListenUnix(struct qemud_server *server, 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, "@%s/.libvirt/qemud-sock", pw->pw_dir) >= maxlen) goto snprintf_error; } @@ -430,6 +444,7 @@ static struct qemud_server *qemudInitialize(int sys, int sigread) { roSockname[0] = '\0'; if (qemudInitPaths(sys, server->configDir, server->networkConfigDir, + server->autostartDir, server->networkAutostartDir, sockname, roSockname, PATH_MAX) < 0) goto cleanup;