blob: 176ef4c2a7a57f99da022890323d2fc32e6d942b [file] [log] [blame] [raw]
/*
* Copyright (C) 2000-2011, 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 <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/vzcalluser.h>
#include "vzerror.h"
#include "script.h"
#include "util.h"
#include "dev.h"
#include "env.h"
#include "logger.h"
#include "script.h"
static int dev_create(const char *root, dev_res *dev)
{
char buf1[STR_SIZE];
char buf2[STR_SIZE];
struct stat st, st2;
const char* udev_paths[] = {
"/lib/udev/devices",
"/etc/udev/devices",
NULL};
int i;
if (!dev->name)
return 0;
if (check_var(root, "VE_ROOT is not set"))
return VZ_VE_ROOT_NOTSET;
/* Get device information from CT0 and create it in CT */
snprintf(buf1, sizeof(buf1), "%s/dev/%s", root, dev->name);
snprintf(buf2, sizeof(buf2), "/dev/%s", dev->name);
if (stat(buf2, &st)) {
if (errno == ENOENT)
logger(-1, 0, "Incorrect name or no such device %s",
buf2);
else
logger(-1, errno, "Unable to stat device %s", buf2);
return VZ_SET_DEVICES;
}
if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
logger(-1, 0, "The %s is not block or character device", buf2);
return VZ_SET_DEVICES;
}
if (make_dir(buf1, 0))
return VZ_SET_DEVICES;
unlink(buf1);
if (mknod(buf1, st.st_mode, st.st_rdev)) {
logger(-1, errno, "Unable to create device %s", buf1);
return VZ_SET_DEVICES;
}
/* Try to create static device node for udev */
for (i = 0; udev_paths[i] != NULL; i++) {
if (stat(udev_paths[i], &st2) == 0) {
if (S_ISDIR(st2.st_mode)) {
snprintf(buf1, sizeof(buf1),
"%s/%s/%s",
root, udev_paths[i],
dev->name);
make_dir(buf1, 0);
unlink(buf1);
mknod(buf1, st.st_mode, st.st_rdev);
break;
}
}
}
return 0;
}
int set_devperm(vps_handler *h, envid_t veid, dev_res *dev)
{
struct vzctl_setdevperms devperms;
devperms.veid = veid;
devperms.dev = dev->dev;
devperms.mask = dev->mask;
devperms.type = dev->type;
if (ioctl(h->vzfd, VZCTL_SETDEVPERMS, &devperms)) {
logger(-1, errno, "Error setting device permissions");
return VZ_SET_DEVICES;
}
return 0;
}
/** Allow/disallow access to devices on host system from CT.
*
* @param h CT handler.
* @param veid CT ID.
* @param root CT root.
* @param dev devices list.
* @return 0 on success.
*/
int vps_set_devperm(vps_handler *h, envid_t veid, const char *root,
dev_param *dev)
{
int ret = 0;
dev_res *res;
list_head_t *dev_h = &dev->dev;
if (list_empty(dev_h))
return 0;
if (!vps_is_run(h, veid)) {
logger(-1, 0, "Unable to apply devperm: "
"container is not running");
return VZ_VE_NOT_RUNNING;
}
logger(0, 0, "Setting devices");
list_for_each(res, dev_h, list) {
if (res->name)
if ((ret = dev_create(root, res)))
goto out;
if ((ret = set_devperm(h, veid, res)))
goto out;
}
out:
return ret;
}
int add_dev_param(dev_param *dev, dev_res *res)
{
dev_res *tmp;
if (list_is_init(&dev->dev))
list_head_init(&dev->dev);
tmp = malloc(sizeof(*tmp));
if (tmp == NULL)
return -1;
memcpy(tmp, res, sizeof(*tmp));
list_add_tail(&tmp->list, &dev->dev);
res->name = NULL;
return 0;
}
static void free_dev(list_head_t *head)
{
dev_res *cur, *tmp;
list_for_each_safe(cur, tmp, head, list) {
list_del(&cur->list);
free(cur->name);
free(cur);
}
list_head_init(head);
}
void free_dev_param(dev_param *dev)
{
free_dev(&dev->dev);
}
static int run_pci_script(envid_t veid, int op, list_head_t *pci_h,
const char *ve_root)
{
char *argv[3];
char *envp[10];
char *script;
int ret;
char buf[STR_SIZE];
int i = 0;
if (list_empty(pci_h))
return 0;
snprintf(buf, sizeof(buf), "VEID=%d", veid);
envp[i++] = strdup(buf);
snprintf(buf, sizeof(buf), "VE_ROOT=%s", ve_root);
envp[i++] = strdup(buf);
snprintf(buf, sizeof(buf), "ADD=%d", op == ADD);
envp[i++] = strdup(buf);
envp[i++] = list2str("PCI", pci_h);
envp[i++] = strdup(ENV_PATH);
envp[i] = NULL;
script = VPS_PCI;
argv[0] = script;
argv[1] = NULL;
ret = run_script(script, argv, envp, 0);
free_arg(envp);
return ret;
}
int vps_set_pci(vps_handler *h, envid_t veid, int op, const char *root,
pci_param *pci)
{
list_head_t *pci_h = &pci->list;
if (list_empty(pci_h))
return 0;
if (!vps_is_run(h, veid)) {
logger(-1, 0, "Unable to configure PCI devices: "
"container is not running");
return VZ_VE_NOT_RUNNING;
}
logger(0, 0, "Setting PCI devices");
return run_pci_script(veid, op, pci_h, root);
}