1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2019 VMware Inc, Slavomir Kaslev <[email protected]>
4 *
5 */
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <getopt.h>
10 #include <grp.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/stat.h>
15 #include <unistd.h>
16
17 #include "trace-local.h"
18 #include "trace-msg.h"
19
make_dir(const char * path,mode_t mode)20 static int make_dir(const char *path, mode_t mode)
21 {
22 char buf[PATH_MAX+2], *p;
23
24 strncpy(buf, path, sizeof(buf));
25 if (buf[PATH_MAX])
26 return -E2BIG;
27
28 for (p = buf; *p; p++) {
29 p += strspn(p, "/");
30 p += strcspn(p, "/");
31 *p = '\0';
32 if (mkdir(buf, mode) < 0 && errno != EEXIST)
33 return -errno;
34 *p = '/';
35 }
36
37 return 0;
38 }
39
make_fifo(const char * path,mode_t mode)40 static int make_fifo(const char *path, mode_t mode)
41 {
42 struct stat st;
43
44 if (!stat(path, &st)) {
45 if (S_ISFIFO(st.st_mode))
46 return 0;
47 return -EEXIST;
48 }
49
50 if (mkfifo(path, mode))
51 return -errno;
52 return 0;
53 }
54
make_guest_dir(const char * guest)55 static int make_guest_dir(const char *guest)
56 {
57 char path[PATH_MAX];
58
59 snprintf(path, sizeof(path), GUEST_DIR_FMT, guest);
60 return make_dir(path, 0750);
61 }
62
make_guest_fifo(const char * guest,int cpu,mode_t mode)63 static int make_guest_fifo(const char *guest, int cpu, mode_t mode)
64 {
65 static const char *exts[] = {".in", ".out"};
66 char path[PATH_MAX];
67 int i, ret = 0;
68
69 for (i = 0; i < ARRAY_SIZE(exts); i++) {
70 snprintf(path, sizeof(path), GUEST_FIFO_FMT "%s",
71 guest, cpu, exts[i]);
72 ret = make_fifo(path, mode);
73 if (ret < 0)
74 break;
75 }
76
77 return ret;
78 }
79
make_guest_fifos(const char * guest,int nr_cpus,mode_t mode)80 static int make_guest_fifos(const char *guest, int nr_cpus, mode_t mode)
81 {
82 int i, ret = 0;
83 mode_t mask;
84
85 mask = umask(0);
86 for (i = 0; i < nr_cpus; i++) {
87 ret = make_guest_fifo(guest, i, mode);
88 if (ret < 0)
89 break;
90 }
91 umask(mask);
92
93 return ret;
94 }
95
get_guest_cpu_count(const char * guest)96 static int get_guest_cpu_count(const char *guest)
97 {
98 const char *cmd_fmt = "virsh vcpucount --maximum '%s' 2>/dev/null";
99 int nr_cpus = -1;
100 char cmd[1024];
101 FILE *f;
102
103 snprintf(cmd, sizeof(cmd), cmd_fmt, guest);
104 f = popen(cmd, "r");
105 if (!f)
106 return -errno;
107
108 fscanf(f, "%d", &nr_cpus);
109 pclose(f);
110
111 return nr_cpus;
112 }
113
attach_guest_fifos(const char * guest,int nr_cpus)114 static int attach_guest_fifos(const char *guest, int nr_cpus)
115 {
116 const char *cmd_fmt =
117 "virsh attach-device --config '%s' '%s' >/dev/null 2>/dev/null";
118 const char *xml_fmt =
119 "<channel type='pipe'>\n"
120 " <source path='%s'/>\n"
121 " <target type='virtio' name='%s%d'/>\n"
122 "</channel>";
123 char tmp_path[PATH_MAX], path[PATH_MAX];
124 char cmd[PATH_MAX], xml[PATH_MAX];
125 int i, fd, ret = 0;
126
127 #ifdef __ANDROID__
128 strcpy(tmp_path, "/data/local/tmp/pipexmlXXXXXX");
129 #else /* !__ANDROID__ */
130 strcpy(tmp_path, "/tmp/pipexmlXXXXXX");
131 #endif /* __ANDROID__ */
132
133 fd = mkstemp(tmp_path);
134 if (fd < 0)
135 return fd;
136
137 for (i = 0; i < nr_cpus; i++) {
138 snprintf(path, sizeof(path), GUEST_FIFO_FMT, guest, i);
139 snprintf(xml, sizeof(xml), xml_fmt, path, GUEST_PIPE_NAME, i);
140 pwrite(fd, xml, strlen(xml), 0);
141
142 snprintf(cmd, sizeof(cmd), cmd_fmt, guest, tmp_path);
143 errno = 0;
144 if (system(cmd) != 0) {
145 ret = -errno;
146 break;
147 }
148 }
149
150 close(fd);
151 unlink(tmp_path);
152
153 return ret;
154 }
155
do_setup_guest(const char * guest,int nr_cpus,mode_t mode,gid_t gid,bool attach)156 static void do_setup_guest(const char *guest, int nr_cpus,
157 mode_t mode, gid_t gid, bool attach)
158 {
159 gid_t save_egid;
160 int ret;
161
162 if (gid != -1) {
163 save_egid = getegid();
164 ret = setegid(gid);
165 if (ret < 0)
166 die("failed to set effective group ID");
167 }
168
169 ret = make_guest_dir(guest);
170 if (ret < 0)
171 die("failed to create guest directory for %s", guest);
172
173 ret = make_guest_fifos(guest, nr_cpus, mode);
174 if (ret < 0)
175 die("failed to create FIFOs for %s", guest);
176
177 if (attach) {
178 ret = attach_guest_fifos(guest, nr_cpus);
179 if (ret < 0)
180 die("failed to attach FIFOs to %s", guest);
181 }
182
183 if (gid != -1) {
184 ret = setegid(save_egid);
185 if (ret < 0)
186 die("failed to restore effective group ID");
187 }
188 }
189
trace_setup_guest(int argc,char ** argv)190 void trace_setup_guest(int argc, char **argv)
191 {
192 bool attach = false;
193 struct group *group;
194 mode_t mode = 0660;
195 int nr_cpus = -1;
196 gid_t gid = -1;
197 char *guest;
198
199 if (argc < 2)
200 usage(argv);
201
202 if (strcmp(argv[1], "setup-guest") != 0)
203 usage(argv);
204
205 for (;;) {
206 int c, option_index = 0;
207 static struct option long_options[] = {
208 {"help", no_argument, NULL, '?'},
209 {NULL, 0, NULL, 0}
210 };
211
212 c = getopt_long(argc-1, argv+1, "+hc:p:g:a",
213 long_options, &option_index);
214 if (c == -1)
215 break;
216 switch (c) {
217 case 'h':
218 usage(argv);
219 break;
220 case 'c':
221 nr_cpus = atoi(optarg);
222 break;
223 case 'p':
224 mode = strtol(optarg, NULL, 8);
225 break;
226 case 'g':
227 group = getgrnam(optarg);
228 if (!group)
229 die("group %s does not exist", optarg);
230 gid = group->gr_gid;
231 break;
232 case 'a':
233 attach = true;
234 break;
235 default:
236 usage(argv);
237 }
238 }
239
240 if (optind != argc-2)
241 usage(argv);
242
243 guest = argv[optind+1];
244
245 if (nr_cpus <= 0)
246 nr_cpus = get_guest_cpu_count(guest);
247
248 if (nr_cpus <= 0)
249 die("invalid number of cpus for guest %s", guest);
250
251 do_setup_guest(guest, nr_cpus, mode, gid, attach);
252 }
253