blob: f0643fc7f4a411c9db755d28c106e47c85f4a568 [file] [log] [blame] [raw]
/*
* Copyright (C) 2000-2010, 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 <stdio.h>
#include <string.h>
#include <unistd.h>
#include "types.h"
#include "quota.h"
#include "vzerror.h"
#include "util.h"
#include "script.h"
#include "logger.h"
#define VZQUOTA "/usr/sbin/vzquota"
int is_vzquota_available(void)
{
return (access(VZQUOTA, X_OK) == 0);
}
void quota_inc(dq_param *param, int delta)
{
if (param->enable != YES)
return;
if (param->diskspace != NULL) {
param->diskspace[0] += delta;
param->diskspace[1] += delta;
}
if (param->diskinodes != NULL) {
param->diskinodes[0] += delta;
param->diskinodes[1] += delta;
}
}
int is_2nd_level_quota_on(const dq_param *param)
{
return (param->ugidlimit != NULL && *param->ugidlimit != 0);
}
int quota_set(envid_t veid, char *private, dq_param *param)
{
int i, ret;
char buf[64];
char *arg[24];
if (param->diskspace == NULL &&
param->diskinodes == NULL &&
param->exptime == NULL &&
param->ugidlimit == NULL &&
private == NULL)
{
return 0;
}
i = 0;
arg[i++] = strdup(VZQUOTA);
arg[i++] = strdup("setlimit");
snprintf(buf, sizeof(buf), "%d", veid);
arg[i++] = strdup(buf);
if (private != NULL) {
arg[i++] = strdup("-p");
arg[i++] = strdup(private);
}
if (param->diskspace != NULL) {
arg[i++] = strdup("-b");
snprintf(buf, sizeof(buf), "%lu", param->diskspace[0]);
arg[i++] = strdup(buf);
arg[i++] = strdup("-B");
snprintf(buf, sizeof(buf), "%lu", param->diskspace[1]);
arg[i++] = strdup(buf);
}
if (param->diskinodes != NULL) {
arg[i++] = strdup("-i");
snprintf(buf, sizeof(buf), "%lu", param->diskinodes[0]);
arg[i++] = strdup(buf);
arg[i++] = strdup("-I");
snprintf(buf, sizeof(buf), "%lu", param->diskinodes[1]);
arg[i++] = strdup(buf);
}
if (param->exptime != NULL) {
arg[i++] = strdup("-e");
snprintf(buf, sizeof(buf), "%lu", param->exptime[0]);
arg[i++] = strdup(buf);
arg[i++] = strdup("-n");
snprintf(buf, sizeof(buf), "%lu", param->exptime[0]);
arg[i++] = strdup(buf);
}
/* Set ugid limit */
if (is_2nd_level_quota_on(param)) {
arg[i++] = strdup("-u");
snprintf(buf, sizeof(buf), "%lu", *param->ugidlimit);
arg[i++] = strdup(buf);
}
arg[i] = NULL;
if ((ret = run_script(VZQUOTA, arg, NULL, 0))) {
logger(-1, 0, "vzquota setlimit failed [%d]", ret);
ret = VZ_DQ_SET;
}
free_arg(arg);
return ret;
}
int quota_init(envid_t veid, char *private, dq_param *param)
{
int i, ret;
char buf[64];
char *arg[24];
if (check_var(private,
"Error: Not enough parameters, private not set"))
{
return VZ_FS_NOPRVT;
}
if (check_var(param->diskspace,
"Error: Not enough parameters, diskspace quota not set"))
{
return VZ_DISKSPACE_NOT_SET;
}
if (check_var(param->diskinodes,
"Error: Not enough parameters, diskinodes quota not set"))
{
return VZ_DISKINODES_NOT_SET;
}
i = 0;
arg[i++] = strdup(VZQUOTA);
arg[i++] = strdup("init");
snprintf(buf, sizeof(buf), "%d", veid);
arg[i++] = strdup(buf);
/* Disk space */
arg[i++] = strdup("-b");
snprintf(buf, sizeof(buf), "%lu", param->diskspace[0]);
arg[i++] = strdup(buf);
arg[i++] = strdup("-B");
snprintf(buf, sizeof(buf), "%lu", param->diskspace[1]);
arg[i++] = strdup(buf);
/* Disk inodes */
arg[i++] = strdup("-i");
snprintf(buf, sizeof(buf), "%lu", param->diskinodes[0]);
arg[i++] = strdup(buf);
arg[i++] = strdup("-I");
snprintf(buf, sizeof(buf), "%lu", param->diskinodes[1]);
arg[i++] = strdup(buf);
/* CT private */
arg[i++] = strdup("-p");
arg[i++] = strdup(private);
/* Expiration time */
arg[i++] = strdup("-e");
snprintf(buf, sizeof(buf), "%lu",
param->exptime == NULL ? QUOTA_EXPTIME : param->exptime[0]);
arg[i++] = strdup(buf);
arg[i++] = strdup("-n");
arg[i++] = strdup(buf);
/* ugid limit */
arg[i++] = strdup("-s");
if (is_2nd_level_quota_on(param)) {
arg[i++] = strdup("1");
arg[i++] = strdup("-u");
snprintf(buf, sizeof(buf), "%lu", *param->ugidlimit);
arg[i++] = strdup(buf);
} else
arg[i++] = strdup("0");
arg[i] = NULL;
if ((ret = run_script(VZQUOTA, arg, NULL, 0))) {
logger(-1, 0, "vzquota init failed [%d]", ret);
ret = VZ_DQ_INIT;
}
free_arg(arg);
return ret;
}
/** Turn disk quota on.
*
* @param veid CT ID.
* @param private CT private area path.
* @param dq disk quota parameters.
* @return 0 on success.
*/
int quota_on(envid_t veid, char *private, dq_param *param)
{
int i, ret, ret2 = 0, tried = 0;
char buf[64];
char *arg[24];
if (check_var(param->diskspace,
"Error: Not enough parameters, diskspace quota not set"))
{
return VZ_DISKSPACE_NOT_SET;
}
if (check_var(param->diskinodes,
"Error: Not enough parameters, diskinodes quota not set"))
{
return VZ_DISKINODES_NOT_SET;
}
i = 0;
arg[i++] = strdup(VZQUOTA);
arg[i++] = strdup("on");
snprintf(buf, sizeof(buf), "%d", veid);
arg[i++] = strdup(buf);
/* Disk space */
arg[i++] = strdup("-b");
snprintf(buf, sizeof(buf), "%lu", param->diskspace[0]);
arg[i++] = strdup(buf);
arg[i++] = strdup("-B");
snprintf(buf, sizeof(buf), "%lu", param->diskspace[1]);
arg[i++] = strdup(buf);
/* Disk inodes */
arg[i++] = strdup("-i");
snprintf(buf, sizeof(buf), "%lu", param->diskinodes[0]);
arg[i++] = strdup(buf);
arg[i++] = strdup("-I");
snprintf(buf, sizeof(buf), "%lu", param->diskinodes[1]);
arg[i++] = strdup(buf);
/* Expiration time */
arg[i++] = strdup("-e");
snprintf(buf, sizeof(buf), "%lu",
param->exptime == NULL ? QUOTA_EXPTIME : param->exptime[0]);
arg[i++] = strdup(buf);
arg[i++] = strdup("-n");
arg[i++] = strdup(buf);
/* ugid limit */
arg[i++] = strdup("-s");
if (is_2nd_level_quota_on(param)) {
arg[i++] = strdup("1");
arg[i++] = strdup("-u");
snprintf(buf, sizeof(buf), "%lu", *param->ugidlimit);
arg[i++] = strdup(buf);
} else
arg[i++] = strdup("0");
arg[i] = 0;
retry:
if ((ret = run_script(VZQUOTA, arg, NULL, 0))) {
switch (ret) {
case EXITCODE_QUOTNOTEXIST:
if (!tried) {
tried++;
/* Initialize the quota and retry */
ret2 = quota_init(veid, private, param);
if (!ret2)
goto retry;
}
break;
case EXITCODE_QUOTARUN:
/* Ignore the error, run quota set instead */
ret = 0;
ret2 = quota_set(veid, private, param);
break;
}
}
free_arg(arg);
if (ret2)
return ret2;
if (ret) {
logger(-1, 0, "vzquota on failed [%d]", ret);
return VZ_DQ_ON;
}
return 0;
}
/** Turn disk quota off.
*
* @param veid CT ID.
* @param dq disk quota parameters.
* @return 0 on success.
*/
int quota_off(envid_t veid, int force)
{
int i, ret;
char buf[64];
char *arg[5];
i = 0;
arg[i++] = strdup(VZQUOTA);
arg[i++] = strdup("off");
snprintf(buf, sizeof(buf), "%d", veid);
arg[i++] = strdup(buf);
if (force)
arg[i++] = strdup("-f");
arg[i] = NULL;
if ((ret = run_script(VZQUOTA, arg, NULL, 0))) {
if (ret != EXITCODE_QUOTANOTRUN) {
logger(-1, 0, "vzquota off failed [%d]", ret);
ret = VZ_DQ_OFF;
}
else
ret = 0;
}
free_arg(arg);
return ret;
}
/** Disk quota management wrapper.
*
* @param veid CT ID.
* @param cmd quota commands (QUOTA_MARKDIRTY QUOTA_DROP QUOTA_STAT)
* @return 0 on success.
*/
int quota_ctl(envid_t veid, int cmd)
{
int i, ret;
char buf[64];
char *arg[6];
int quiet = 0;
i = 0;
arg[i++] = strdup(VZQUOTA);
switch (cmd) {
case QUOTA_MARKDIRTY:
arg[i++] =strdup( "setlimit");
snprintf(buf, sizeof(buf), "%d", veid);
arg[i++] = strdup(buf);
arg[i++] = strdup("-f");
quiet = 1;
break;
case QUOTA_DROP :
arg[i++] = strdup("drop");
snprintf(buf, sizeof(buf), "%d", veid);
arg[i++] = strdup(buf);
quiet = 1;
break;
case QUOTA_STAT :
arg[i++] = strdup("stat");
snprintf(buf, sizeof(buf), "%d", veid);
arg[i++] = strdup(buf);
arg[i++] = strdup("-f");
quiet = 1;
break;
case QUOTA_STAT2:
arg[i++] = strdup("stat");
snprintf(buf, sizeof(buf), "%d", veid);
arg[i++] = strdup(buf);
arg[i++] = strdup("-f");
arg[i++] = strdup("-t");
quiet = 1;
break;
case QUOTA_SHOW:
arg[i++] = strdup("show");
snprintf(buf, sizeof(buf), "%d", veid);
arg[i++] = strdup(buf);
quiet = 1;
break;
default :
logger(-1, 0, "quota_ctl: Unknown action %d", cmd);
return VZ_SYSTEM_ERROR;
}
arg[i] = NULL;
ret = run_script(VZQUOTA, arg, NULL, quiet);
free_arg(arg);
return ret;
}
/** Setup disk quota limits.
*
* @param veid CT ID.
* @param dq disk quota parameters.
* @return 0 on success.
*/
int vps_set_quota(envid_t veid, dq_param *dq)
{
int ret;
unsigned long *tmp_ugidlimit = NULL;
if (dq->enable == NO)
return 0;
if (dq->diskspace == NULL &&
dq->diskinodes == NULL &&
dq->exptime == NULL &&
dq->ugidlimit == NULL)
{
return 0;
}
if (quota_ctl(veid, QUOTA_STAT)) {
logger(-1, 0, "Error: Unable to apply new quota values:"
" quota not running");
return VZ_DQ_SET;
}
if (dq->ugidlimit != NULL) {
ret = quota_ctl(veid, QUOTA_STAT2);
if (ret == EXITCODE_UGID_NOTRUN && *dq->ugidlimit) {
logger(-1, 0, "Unable to apply new quota values:"
" ugid quota not initialized");
return VZ_DQ_UGID_NOTINITIALIZED;
} else if (!ret && !*dq->ugidlimit) {
logger(-1, 0, "WARNING: Unable to turn ugid quota"
" off. New parameters will be applied"
" during the next start");
tmp_ugidlimit = dq->ugidlimit;
dq->ugidlimit = NULL;
}
}
ret = quota_set(veid, NULL, dq);
if (tmp_ugidlimit != NULL)
dq->ugidlimit = tmp_ugidlimit;
return ret;
}
/** Turn disk quota on.
*
* @param veid CT ID.
* @param private CT private area path.
* @param dq disk quota parameters.
* @return 0 on success.
*/
int vps_quotaon(envid_t veid, char *private, dq_param *dq)
{
int ret;
if (dq == NULL)
return 0;
if (dq->enable == NO) {
// quota_ctl(veid, QUOTA_MARKDIRTY);
return 0;
} else {
if (quota_ctl(veid, QUOTA_SHOW) == EXITCODE_QUOTNOTEXIST) {
logger(0, 0, "Initializing quota ...");
if ((ret = quota_init(veid, private, dq)))
return ret;
}
}
if ((ret = quota_on(veid, private, dq)))
return ret;
return 0;
}
/** Turn disk quota off.
*
* @param veid CT ID.
* @param dq disk quota parameters.
* @return 0 on success.
*/
int vps_quotaoff(envid_t veid, dq_param *dq)
{
if (dq == NULL)
return 0;
if (dq->enable == NO)
return 0;
return quota_off(veid, 0);
}