|  | /* | 
|  | *  Copyright (C) 2000-2009, Parallels, Inc. All rights reserved. | 
|  | * | 
|  | *  This program is free software; you can redistribute it and/or modify | 
|  | *  it under the terms of the GNU General Public License as published by | 
|  | *  the Free Software Foundation; either version 2 of the License, or | 
|  | *  (at your option) any later version. | 
|  | * | 
|  | *  This program is distributed in the hope that it will be useful, | 
|  | *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | *  GNU General Public License for more details. | 
|  | * | 
|  | *  You should have received a copy of the GNU General Public License | 
|  | *  along with this program; if not, write to the Free Software | 
|  | *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
|  | */ | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <unistd.h> | 
|  | #include <sys/types.h> | 
|  | #include <dirent.h> | 
|  | #include <sys/stat.h> | 
|  | #include <fcntl.h> | 
|  | #include <stdio.h> | 
|  | #include <errno.h> | 
|  | #include <string.h> | 
|  | #include <signal.h> | 
|  | #include <sys/wait.h> | 
|  |  | 
|  | #include "list.h" | 
|  | #include "logger.h" | 
|  | #include "vzconfig.h" | 
|  | #include "vzerror.h" | 
|  | #include "util.h" | 
|  | #include "script.h" | 
|  | #include "lock.h" | 
|  | #include "modules.h" | 
|  | #include "create.h" | 
|  | #include "env.h" | 
|  |  | 
|  | #define BACKUP		0 | 
|  | #define DESTR		1 | 
|  |  | 
|  | const char destroy_dir_magic[]="vzctl-rm-me."; | 
|  |  | 
|  | static int destroydir(char *dir); | 
|  |  | 
|  | int del_dir(char *dir) | 
|  | { | 
|  | int ret; | 
|  | char *argv[4]; | 
|  |  | 
|  | argv[0] = "/bin/rm"; | 
|  | argv[1] = "-rf"; | 
|  | argv[2] = dir; | 
|  | argv[3] = NULL; | 
|  | ret = run_script("/bin/rm", argv, NULL, 0); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | int vps_destroy_dir(envid_t veid, char *dir) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | if (!quota_ctl(veid, QUOTA_STAT)) { | 
|  | if ((ret = quota_off(veid, 0))) | 
|  | if ((ret = quota_off(veid, 1))) | 
|  | return ret; | 
|  | } | 
|  | quota_ctl(veid, QUOTA_DROP); | 
|  | if ((ret = destroydir(dir))) | 
|  | return ret; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Removes all the directories under 'root' | 
|  | * those names start with 'destroy_dir_magic' | 
|  | */ | 
|  | static void _destroydir(const char *root) | 
|  | { | 
|  | char buf[STR_SIZE]; | 
|  | struct stat st; | 
|  | struct dirent *ep; | 
|  | DIR *dp; | 
|  | int del, ret; | 
|  |  | 
|  | do { | 
|  | if (!(dp = opendir(root))) | 
|  | return; | 
|  | del = 0; | 
|  | while ((ep = readdir(dp))) { | 
|  | if (strncmp(ep->d_name, destroy_dir_magic, | 
|  | sizeof(destroy_dir_magic) - 1)) | 
|  | { | 
|  | continue; | 
|  | } | 
|  | snprintf(buf, sizeof(buf), "%s/%s", root, ep->d_name); | 
|  | if (stat(buf, &st)) | 
|  | continue; | 
|  | if (!S_ISDIR(st.st_mode)) | 
|  | continue; | 
|  | snprintf(buf, sizeof(buf), "rm -rf %s/%s", | 
|  | root, ep->d_name); | 
|  | ret = system(buf); | 
|  | if (ret == -1 || WEXITSTATUS(ret)) | 
|  | sleep(10); | 
|  | del = 1; | 
|  | } | 
|  | closedir(dp); | 
|  | } while(del); | 
|  | } | 
|  |  | 
|  | static int _unlink(const char *s) | 
|  | { | 
|  | if (unlink(s)) { | 
|  | logger(-1, errno, "Unable to unlink %s", s); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  |  | 
|  | } | 
|  |  | 
|  | static int destroydir(char *dir) | 
|  | { | 
|  | char buf[STR_SIZE]; | 
|  | char tmp[STR_SIZE]; | 
|  | char *root; | 
|  | int fd_lock, pid; | 
|  | struct sigaction act, actold; | 
|  | int ret = 0; | 
|  | struct stat st; | 
|  |  | 
|  | if (lstat(dir, &st)) { | 
|  | if (errno != ENOENT) { | 
|  | logger(-1, errno, "Unable to lstat %s", dir); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (S_ISLNK(st.st_mode)) { | 
|  | int i; | 
|  | i = readlink(dir, tmp, sizeof(tmp) - 1); | 
|  | if (i == -1) { | 
|  | logger(-1, 0, "Unable to readlink %s", dir); | 
|  | return -1; | 
|  | } | 
|  | tmp[i] = '\0'; | 
|  | logger(-1, 0, "Warning: container private area %s " | 
|  | "is a symlink to %s.\n" | 
|  | "Not removing link destination, " | 
|  | "you have to do it manually.", | 
|  | dir, tmp); | 
|  | return _unlink(dir); | 
|  | } | 
|  |  | 
|  | if (!S_ISDIR(st.st_mode)) { | 
|  | logger(-1, 0, "Warning: container private area %s " | 
|  | "is not a directory", dir); | 
|  | return _unlink(dir); | 
|  | } | 
|  |  | 
|  | root = get_fs_root(dir); | 
|  | if (root == NULL) { | 
|  | logger(-1, 0, "Unable to get root for %s", dir); | 
|  | return -1; | 
|  | } | 
|  | snprintf(tmp, sizeof(tmp), "%s/vztmp", root); | 
|  | free(root); | 
|  | if (!stat_file(tmp)) { | 
|  | if (mkdir(tmp, 0755)) { | 
|  | logger(-1, errno, "Can't create tmp dir %s", tmp); | 
|  | return VZ_FS_DEL_PRVT; | 
|  | } | 
|  | } | 
|  | /* Fast/async removal -- first move to tmp dir */ | 
|  | snprintf(buf, sizeof(buf), "%s/%sXXXXXX", tmp, destroy_dir_magic); | 
|  | if (mkdtemp(buf) == NULL) { | 
|  | logger(-1, errno, "Unable to create temporary directory, " | 
|  | "mkdtemp(%s) failed", buf); | 
|  | return VZ_FS_DEL_PRVT; | 
|  | } | 
|  | if (rename(dir, buf)) { | 
|  | rmdir(buf); | 
|  | if (errno == EXDEV) { | 
|  | /* See http://bugzilla.openvz.org/457 */ | 
|  | logger(0, 0, "Warning: directory %s is not on the same" | 
|  | " filesystem as %s - doing slow/sync" | 
|  | " removal", dir, tmp); | 
|  | if (del_dir(dir)) | 
|  | return VZ_FS_DEL_PRVT; | 
|  | else | 
|  | return 0; | 
|  | } else { | 
|  | logger(-1, errno, "Can't move %s -> %s", dir, buf); | 
|  | return VZ_FS_DEL_PRVT; | 
|  | } | 
|  | } | 
|  | snprintf(buf, sizeof(buf), "%s/rm.lck", tmp); | 
|  | if ((fd_lock = _lock(buf, 0)) == -2) { | 
|  | /* Already locked */ | 
|  | return 0; | 
|  | } else if (fd_lock == -1) | 
|  | return VZ_FS_DEL_PRVT; | 
|  |  | 
|  | sigaction(SIGCHLD, NULL, &actold); | 
|  | sigemptyset(&act.sa_mask); | 
|  | act.sa_handler = SIG_IGN; | 
|  | act.sa_flags = SA_NOCLDSTOP; | 
|  | sigaction(SIGCHLD, &act, NULL); | 
|  |  | 
|  | if (!(pid = fork())) { | 
|  | int fd, i; | 
|  |  | 
|  | setsid(); | 
|  | fd = open("/dev/null", O_WRONLY); | 
|  | if (fd != -1) { | 
|  | close(0); | 
|  | close(1); | 
|  | close(2); | 
|  | dup2(fd, 1); | 
|  | dup2(fd, 2); | 
|  | } | 
|  | for (i = 3; i < 1024; i++) { | 
|  | if (i != fd_lock) | 
|  | close(i); | 
|  | } | 
|  | _destroydir(tmp); | 
|  | _unlock(fd_lock, buf); | 
|  | exit(0); | 
|  | } else if (pid < 0)  { | 
|  | logger(-1, errno, "destroydir: Unable to fork"); | 
|  | ret = VZ_RESOURCE_ERROR; | 
|  | } | 
|  | sleep(1); | 
|  | sigaction(SIGCHLD, &actold, NULL); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int destroy_dumpfile(envid_t veid, const char *dumpdir) | 
|  | { | 
|  | char buf[128]; | 
|  |  | 
|  | get_dump_file(veid, dumpdir, buf, sizeof(buf)); | 
|  | if (unlink(buf) == 0) | 
|  | return 0; | 
|  | else if (errno == ENOENT) | 
|  | return 0; | 
|  | else return -1; | 
|  | } | 
|  |  | 
|  | int vps_destroy(vps_handler *h, envid_t veid, fs_param *fs, cpt_param *cpt) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | if (check_var(fs->private, "VE_PRIVATE is not set")) | 
|  | return VZ_VE_PRIVATE_NOTSET; | 
|  | if (check_var(fs->root, "VE_ROOT is not set")) | 
|  | return VZ_VE_ROOT_NOTSET; | 
|  | if (vps_is_run(h, veid)) { | 
|  | logger(0, 0, "Container is currently running." | 
|  | " Stop it first."); | 
|  | return VZ_VE_RUNNING; | 
|  | } | 
|  | if (vps_is_mounted(fs->root, fs->private)) { | 
|  | logger(0, 0, "Container is currently mounted (umount first)"); | 
|  | return VZ_FS_MOUNTED; | 
|  | } | 
|  | logger(0, 0, "Destroying container private area: %s", fs->private); | 
|  | if ((ret = vps_destroy_dir(veid, fs->private))) | 
|  | return ret; | 
|  | move_config(veid, BACKUP); | 
|  | if (destroy_dumpfile(veid, cpt != NULL ? cpt->dumpdir : NULL) < 0) | 
|  | logger(-1, errno, "Warning: failed to remove dump file"); | 
|  | if (rmdir(fs->root) < 0) | 
|  | logger(-1, errno, "Warning: failed to remove %s", fs->root); | 
|  | logger(0, 0, "Container private area was destroyed"); | 
|  |  | 
|  | return 0; | 
|  | } |