|  | /* | 
|  | *  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 <errno.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <sys/stat.h> | 
|  | #include <netinet/in.h> | 
|  | #include <linux/vzcalluser.h> | 
|  |  | 
|  | #include "vzerror.h" | 
|  | #include "logger.h" | 
|  | #include "res.h" | 
|  | #include "exec.h" | 
|  | #include "dist.h" | 
|  | #include "util.h" | 
|  | #include "env.h" | 
|  | #include "vps_configure.h" | 
|  | #include "list.h" | 
|  | #include "image.h" | 
|  |  | 
|  |  | 
|  | static struct vps_state{ | 
|  | char *name; | 
|  | int id; | 
|  | } vps_states[] = { | 
|  | {"starting", STATE_STARTING}, | 
|  | {"running", STATE_RUNNING}, | 
|  | {"stopping", STATE_STOPPING}, | 
|  | {"restoring", STATE_RESTORING}, | 
|  | {"checkpointing", STATE_CHECKPOINTING}, | 
|  | }; | 
|  |  | 
|  | const char *state2str(int state) | 
|  | { | 
|  | unsigned int i; | 
|  |  | 
|  | for (i = 0; i < ARRAY_SIZE(vps_states); i++) | 
|  | if (vps_states[i].id == state) | 
|  | return vps_states[i].name; | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static const char *get_local_ip(vps_param *param) | 
|  | { | 
|  | list_head_t *h = ¶m->res.net.ip; | 
|  | const char *ip, *mask; | 
|  | static char dst[INET6_ADDRSTRLEN]; | 
|  |  | 
|  | /* Use first IP in the CT config */ | 
|  | if (param->g_param != NULL && !param->res.net.delall) | 
|  | h = ¶m->g_param->res.net.ip; | 
|  | if (list_empty(h)) | 
|  | h = ¶m->res.net.ip; | 
|  | if (list_empty(h)) | 
|  | return NULL; | 
|  |  | 
|  | ip = list_first_entry(h, ip_param, list)->val; | 
|  | mask = strchr(ip, '/'); | 
|  | if (!mask) | 
|  | return ip; | 
|  | return strncpy(dst, ip, mask - ip); | 
|  | } | 
|  |  | 
|  | static int vps_hostnm_configure(vps_handler *h, envid_t veid, | 
|  | dist_actions *actions, const char *root, | 
|  | char *hostname,	const char *ip, int state) | 
|  | { | 
|  | char *envp[5]; | 
|  | const char *script; | 
|  | int ret; | 
|  | char vps_state[32]; | 
|  | char hostnm[STR_SIZE]; | 
|  | const char *str_state; | 
|  | char ipnm[STR_SIZE]; | 
|  |  | 
|  | if (hostname == NULL) | 
|  | return 0; | 
|  | script = actions->set_hostname; | 
|  | if (script == NULL) { | 
|  | logger(0, 0, "Warning: set_hostname action script is not" | 
|  | " specified"); | 
|  | return 0; | 
|  | } | 
|  | str_state = state2str(state); | 
|  | snprintf(vps_state, sizeof(vps_state), "VE_STATE=%s", str_state); | 
|  | envp[0] = vps_state; | 
|  | snprintf(hostnm, sizeof(hostnm), "HOSTNM=%s", hostname); | 
|  | envp[1] = hostnm; | 
|  | envp[2] = ENV_PATH; | 
|  | envp[3] = NULL; | 
|  | if (ip != NULL) { | 
|  | snprintf(ipnm, sizeof(ipnm), "IP_ADDR=%s", ip); | 
|  | envp[3] = ipnm; | 
|  | } | 
|  | envp[4] = NULL; | 
|  | ret = vps_exec_script(h, veid, root, NULL, envp, script, DIST_FUNC, | 
|  | SCRIPT_EXEC_TIMEOUT); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int vps_dns_configure(vps_handler *h, envid_t veid, | 
|  | dist_actions *actions, const char *root, misc_param *net, int state) | 
|  | { | 
|  | char *envp[10]; | 
|  | char *str; | 
|  | const char *script; | 
|  | int ret, i; | 
|  |  | 
|  | if (list_empty(&net->searchdomain) && | 
|  | list_empty(&net->nameserver)) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  | script = actions->set_dns; | 
|  | if (script == NULL) { | 
|  | logger(0, 0, "Warning: set_dns action script is not specified"); | 
|  | return 0; | 
|  | } | 
|  | i = 0; | 
|  | if (!list_empty(&net->searchdomain)) { | 
|  | str = list2str("SEARCHDOMAIN", &net->searchdomain); | 
|  | if (str != NULL) | 
|  | envp[i++] = str; | 
|  | } | 
|  | if (!list_empty(&net->nameserver)) { | 
|  | str = list2str("NAMESERVER", &net->nameserver); | 
|  | if (str != NULL) | 
|  | envp[i++] = str; | 
|  | } | 
|  | envp[i++] = strdup(ENV_PATH); | 
|  | envp[i] = NULL; | 
|  | ret = vps_exec_script(h, veid, root, NULL, envp, script, DIST_FUNC, | 
|  | SCRIPT_EXEC_TIMEOUT); | 
|  | free_arg(envp); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | int vps_pw_configure(vps_handler *h, envid_t veid, dist_actions *actions, | 
|  | const char *root, list_head_t *pw) | 
|  | { | 
|  | char *envp[3]; | 
|  | const char *script; | 
|  | int ret; | 
|  | char *str; | 
|  |  | 
|  | if (list_empty(pw) || actions == NULL) | 
|  | return 0; | 
|  | script = actions->set_userpass; | 
|  | if (script == NULL) { | 
|  | logger(0, 0, "Warning: set_userpass action script is not" | 
|  | " specified"); | 
|  | return 0; | 
|  | } | 
|  | str = list2str("USERPW", pw); | 
|  | envp[0] = str; | 
|  | envp[1] = ENV_PATH; | 
|  | envp[2] = NULL; | 
|  | if ((ret = vps_exec_script(h, veid, root, NULL, envp, script, DIST_FUNC, | 
|  | SCRIPT_EXEC_TIMEOUT))) | 
|  | { | 
|  | ret = VZ_CHANGEPASS; | 
|  | logger(0, 0, "Password change failed"); | 
|  | } | 
|  | free(str); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int vps_quota_configure(vps_handler *h, envid_t veid, | 
|  | const dist_actions *actions, const fs_param *fs, const dq_param *dq, | 
|  | int state) | 
|  | { | 
|  | char *envp[6]; | 
|  | const char *script; | 
|  | int ret, i; | 
|  | char buf[64]; | 
|  | const char *str_state; | 
|  |  | 
|  | if (dq->enable == NO) | 
|  | return 0; | 
|  | if (dq->ugidlimit == NULL) | 
|  | return 0; | 
|  | script = actions->set_ugid_quota; | 
|  | if (script == NULL) { | 
|  | logger(0, 0, "Warning: set_ugid_quota action script is not" | 
|  | " specified"); | 
|  | return 0; | 
|  | } | 
|  | i = 0; | 
|  | str_state = state2str(state); | 
|  | snprintf(buf, sizeof(buf), "VE_STATE=%s", str_state); | 
|  | envp[i++] = strdup(buf); | 
|  | if (*dq->ugidlimit)  { | 
|  | if (!ve_private_is_ploop(fs->private)) { | 
|  | /* simfs/vzquota case */ | 
|  | struct stat st; | 
|  | const char *fs_name; | 
|  |  | 
|  | if (stat(fs->root, &st)) { | 
|  | logger(-1, errno, "Unable to stat %s", | 
|  | fs->root); | 
|  | return VZ_ERROR_SET_USER_QUOTA; | 
|  | } | 
|  | fs_name = vz_fs_get_name(); | 
|  | snprintf(buf, sizeof(buf), "DEVFS=%s", fs_name); | 
|  | envp[i++] = strdup(buf); | 
|  | snprintf(buf, sizeof(buf), "MINOR=%d", minor(st.st_dev)); | 
|  | envp[i++] = strdup(buf); | 
|  | snprintf(buf, sizeof(buf), "MAJOR=%d", major(st.st_dev)); | 
|  | envp[i++] = strdup(buf); | 
|  | } | 
|  | else { | 
|  | /* ploop/native quota case */ | 
|  | snprintf(buf, sizeof(buf), "UGIDLIMIT=%lu", | 
|  | *dq->ugidlimit); | 
|  | envp[i++] = strdup(buf); | 
|  | } | 
|  | } | 
|  | envp[i++] = strdup(ENV_PATH); | 
|  | envp[i] = NULL; | 
|  | ret = vps_exec_script(h, veid, fs->root, NULL, envp, script, DIST_FUNC, | 
|  | SCRIPT_EXEC_TIMEOUT); | 
|  | free_arg(envp); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | int need_configure(vps_res *res) | 
|  | { | 
|  | if (res->misc.hostname == NULL && | 
|  | !res->net.delall && | 
|  | list_empty(&res->net.ip) && | 
|  | list_empty(&res->misc.nameserver) && | 
|  | list_empty(&res->misc.searchdomain) && | 
|  | res->dq.ugidlimit == NULL) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Setup (add/delete) IP address(es) inside container | 
|  | */ | 
|  | int vps_ip_configure(vps_handler *h, envid_t veid, dist_actions *actions, | 
|  | const char *root, int op, net_param *net, int state) | 
|  | { | 
|  | char *envp[6]; | 
|  | char *str; | 
|  | const char *script = NULL; | 
|  | int ret, i; | 
|  | char vps_state[32]; | 
|  | char *ipv6_net = "IPV6=yes"; | 
|  | const char *str_state; | 
|  | char *delall = "IPDELALL=yes"; | 
|  |  | 
|  | if (list_empty(&net->ip) && !net->delall && state != STATE_STARTING) | 
|  | return 0; | 
|  | if (actions == NULL) | 
|  | return 0; | 
|  | switch (op) { | 
|  | case ADD: | 
|  | script = actions->add_ip; | 
|  | if (script == NULL) { | 
|  | logger(0, 0, "Warning: add_ip action script" | 
|  | " is not specified"); | 
|  | return 0; | 
|  | } | 
|  | break; | 
|  | case DEL: | 
|  | script = actions->del_ip; | 
|  | if (script == NULL) { | 
|  | logger(0, 0, "Warning: del_ip action script" | 
|  | " is not specified"); | 
|  | return 0; | 
|  | } | 
|  | break; | 
|  | } | 
|  | i = 0; | 
|  | str_state = state2str(state); | 
|  | snprintf(vps_state, sizeof(vps_state), "VE_STATE=%s", str_state); | 
|  | envp[i++] = vps_state; | 
|  | str = list2str("IP_ADDR", &net->ip); | 
|  | if (str != NULL) | 
|  | envp[i++] = str; | 
|  | if (net->delall) | 
|  | envp[i++] = delall; | 
|  | if (net->ipv6_net == YES) | 
|  | envp[i++] = ipv6_net; | 
|  | envp[i++] = ENV_PATH; | 
|  | envp[i] = NULL; | 
|  | ret = vps_exec_script(h, veid, root, NULL, envp, script, DIST_FUNC, | 
|  | SCRIPT_EXEC_TIMEOUT); | 
|  | free(str); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | int vps_configure(vps_handler *h, envid_t veid, dist_actions *actions, | 
|  | const fs_param *fs, vps_param *param, int state) | 
|  | { | 
|  | int ret; | 
|  | vps_res *res = ¶m->res; | 
|  |  | 
|  | if (!need_configure(res)) | 
|  | return 0; | 
|  | if (!vps_is_run(h, veid)) { | 
|  | logger(0, 0, "Unable to configure container: not running"); | 
|  | return VZ_VE_NOT_RUNNING; | 
|  | } | 
|  | if (actions == NULL) { | 
|  | logger(0, 0, "Dist action not loaded"); | 
|  | return -1; | 
|  | } | 
|  | if ((ret = vps_hostnm_configure(h, veid, actions, fs->root, | 
|  | res->misc.hostname, get_local_ip(param), state))) | 
|  | { | 
|  | return ret; | 
|  | } | 
|  | if ((ret = vps_dns_configure(h, veid, actions, fs->root, &res->misc, | 
|  | state))) | 
|  | { | 
|  | return ret; | 
|  | } | 
|  | if ((ret = vps_quota_configure(h, veid, actions, fs, &res->dq, | 
|  | state))) | 
|  | { | 
|  | return ret; | 
|  | } | 
|  | return 0; | 
|  | } |