blob: 899848cb254adb090421f4f5a7625a988836c3c9 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 VMware Inc, Slavomir Kaslev <kaslevs@vmware.com>
*
*/
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <grp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "trace-local.h"
#include "trace-msg.h"
static int make_dir(const char *path, mode_t mode)
{
char buf[PATH_MAX+2], *p;
strncpy(buf, path, sizeof(buf));
if (buf[PATH_MAX])
return -E2BIG;
for (p = buf; *p; p++) {
p += strspn(p, "/");
p += strcspn(p, "/");
*p = '\0';
if (mkdir(buf, mode) < 0 && errno != EEXIST)
return -errno;
*p = '/';
}
return 0;
}
static int make_fifo(const char *path, mode_t mode)
{
struct stat st;
if (!stat(path, &st)) {
if (S_ISFIFO(st.st_mode))
return 0;
return -EEXIST;
}
if (mkfifo(path, mode))
return -errno;
return 0;
}
static int make_guest_dir(const char *guest)
{
char path[PATH_MAX];
snprintf(path, sizeof(path), GUEST_DIR_FMT, guest);
return make_dir(path, 0750);
}
static int make_guest_fifo(const char *guest, int cpu, mode_t mode)
{
static const char *exts[] = {".in", ".out"};
char path[PATH_MAX];
int i, ret = 0;
for (i = 0; i < ARRAY_SIZE(exts); i++) {
snprintf(path, sizeof(path), GUEST_FIFO_FMT "%s",
guest, cpu, exts[i]);
ret = make_fifo(path, mode);
if (ret < 0)
break;
}
return ret;
}
static int make_guest_fifos(const char *guest, int nr_cpus, mode_t mode)
{
int i, ret = 0;
mode_t mask;
mask = umask(0);
for (i = 0; i < nr_cpus; i++) {
ret = make_guest_fifo(guest, i, mode);
if (ret < 0)
break;
}
umask(mask);
return ret;
}
static int get_guest_cpu_count(const char *guest)
{
const char *cmd_fmt = "virsh vcpucount --maximum '%s' 2>/dev/null";
int nr_cpus = -1;
char cmd[1024];
FILE *f;
snprintf(cmd, sizeof(cmd), cmd_fmt, guest);
f = popen(cmd, "r");
if (!f)
return -errno;
fscanf(f, "%d", &nr_cpus);
pclose(f);
return nr_cpus;
}
static int attach_guest_fifos(const char *guest, int nr_cpus)
{
const char *cmd_fmt =
"virsh attach-device --config '%s' '%s' >/dev/null 2>/dev/null";
const char *xml_fmt =
"<channel type='pipe'>\n"
" <source path='%s'/>\n"
" <target type='virtio' name='%s%d'/>\n"
"</channel>";
char tmp_path[PATH_MAX], path[PATH_MAX];
char cmd[PATH_MAX], xml[PATH_MAX];
int i, fd, ret = 0;
strcpy(tmp_path, "/tmp/pipexmlXXXXXX");
fd = mkstemp(tmp_path);
if (fd < 0)
return fd;
for (i = 0; i < nr_cpus; i++) {
snprintf(path, sizeof(path), GUEST_FIFO_FMT, guest, i);
snprintf(xml, sizeof(xml), xml_fmt, path, GUEST_PIPE_NAME, i);
pwrite(fd, xml, strlen(xml), 0);
snprintf(cmd, sizeof(cmd), cmd_fmt, guest, tmp_path);
errno = 0;
if (system(cmd) != 0) {
ret = -errno;
break;
}
}
close(fd);
unlink(tmp_path);
return ret;
}
static void do_setup_guest(const char *guest, int nr_cpus,
mode_t mode, gid_t gid, bool attach)
{
gid_t save_egid;
int ret;
if (gid != -1) {
save_egid = getegid();
ret = setegid(gid);
if (ret < 0)
die("failed to set effective group ID");
}
ret = make_guest_dir(guest);
if (ret < 0)
die("failed to create guest directory for %s", guest);
ret = make_guest_fifos(guest, nr_cpus, mode);
if (ret < 0)
die("failed to create FIFOs for %s", guest);
if (attach) {
ret = attach_guest_fifos(guest, nr_cpus);
if (ret < 0)
die("failed to attach FIFOs to %s", guest);
}
if (gid != -1) {
ret = setegid(save_egid);
if (ret < 0)
die("failed to restore effective group ID");
}
}
void trace_setup_guest(int argc, char **argv)
{
bool attach = false;
struct group *group;
mode_t mode = 0660;
int nr_cpus = -1;
gid_t gid = -1;
char *guest;
if (argc < 2)
usage(argv);
if (strcmp(argv[1], "setup-guest") != 0)
usage(argv);
for (;;) {
int c, option_index = 0;
static struct option long_options[] = {
{"help", no_argument, NULL, '?'},
{NULL, 0, NULL, 0}
};
c = getopt_long(argc-1, argv+1, "+hc:p:g:a",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'h':
usage(argv);
break;
case 'c':
nr_cpus = atoi(optarg);
break;
case 'p':
mode = strtol(optarg, NULL, 8);
break;
case 'g':
group = getgrnam(optarg);
if (!group)
die("group %s does not exist", optarg);
gid = group->gr_gid;
break;
case 'a':
attach = true;
break;
default:
usage(argv);
}
}
if (optind != argc-2)
usage(argv);
guest = argv[optind+1];
if (nr_cpus <= 0)
nr_cpus = get_guest_cpu_count(guest);
if (nr_cpus <= 0)
die("invalid number of cpus for guest %s", guest);
do_setup_guest(guest, nr_cpus, mode, gid, attach);
}