Actually implement the autostart API in qemud by creating symlinks in the autostart directories. Signed-off-by: Mark McLoughlin <markmc@xxxxxxxxxx> 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, "@%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); --