| /* | 
 |  *  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 "types.h" | 
 | #include "quota.h" | 
 | #include "vzerror.h" | 
 | #include "util.h" | 
 | #include "script.h" | 
 | #include "logger.h" | 
 |  | 
 | #define VZQUOTA		"/usr/sbin/vzquota" | 
 |  | 
 | 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); | 
 | } |