blob: e1af22e536528d313edf2e63f4fb0af35b1a2042 [file] [log] [blame] [raw]
/*
* 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"
#include "image.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;
logger(0, 0, "Destroying container private area: %s", dir);
if (!ve_private_is_ploop(dir)) {
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, errno, "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())) {
setsid();
close_fds(1, fd_lock, -1);
_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);
_unlock(fd_lock, buf);
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;
}
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;
}