1 /*
2     Interactive commands for Xen Store Daemon.
3     Copyright (C) 2017 Juergen Gross, SUSE Linux GmbH
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program; If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include <errno.h>
20 #include <stdarg.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 #include <xen-tools/xenstore-common.h>
27 
28 #include "utils.h"
29 #include "talloc.h"
30 #include "core.h"
31 #include "control.h"
32 #include "domain.h"
33 #include "lu.h"
34 
35 struct cmd_s {
36 	char *cmd;
37 	int (*func)(const void *, struct connection *, const char **, int);
38 	char *pars;
39 	/*
40 	 * max_pars can be used to limit the size of the parameter vector,
41 	 * e.g. in case of large binary parts in the parameters.
42 	 * The command is included in the count, so 1 means just the command
43 	 * without any parameter.
44 	 * 0 == no limit (the default)
45 	 */
46 	unsigned int max_pars;
47 };
48 
do_control_check(const void * ctx,struct connection * conn,const char ** vec,int num)49 static int do_control_check(const void *ctx, struct connection *conn,
50 			    const char **vec, int num)
51 {
52 	if (num)
53 		return EINVAL;
54 
55 	check_store();
56 
57 	send_ack(conn, XS_CONTROL);
58 	return 0;
59 }
60 
do_control_log(const void * ctx,struct connection * conn,const char ** vec,int num)61 static int do_control_log(const void *ctx, struct connection *conn,
62 			  const char **vec, int num)
63 {
64 	int ret;
65 
66 	if (num == 0) {
67 		char *resp = talloc_asprintf(ctx, "Log switch settings:\n");
68 		unsigned int idx;
69 		bool on;
70 
71 		if (!resp)
72 			return ENOMEM;
73 		for (idx = 0; trace_switches[idx]; idx++) {
74 			on = trace_flags & (1u << idx);
75 			resp = talloc_asprintf_append(resp, "%-8s: %s\n",
76 						      trace_switches[idx],
77 						      on ? "on" : "off");
78 			if (!resp)
79 				return ENOMEM;
80 		}
81 
82 		send_reply(conn, XS_CONTROL, resp, strlen(resp) + 1);
83 		return 0;
84 	}
85 
86 	if (num != 1)
87 		return EINVAL;
88 
89 	if (!strcmp(vec[0], "on"))
90 		reopen_log();
91 	else if (!strcmp(vec[0], "off"))
92 		close_log();
93 	else {
94 		ret = set_trace_switch(vec[0]);
95 		if (ret)
96 			return ret;
97 	}
98 
99 	send_ack(conn, XS_CONTROL);
100 	return 0;
101 }
102 
quota_show_current(const void * ctx,struct connection * conn,const struct quota * quotas)103 static int quota_show_current(const void *ctx, struct connection *conn,
104 			      const struct quota *quotas)
105 {
106 	char *resp;
107 	unsigned int i;
108 
109 	resp = talloc_strdup(ctx, "Quota settings:\n");
110 	if (!resp)
111 		return ENOMEM;
112 
113 	for (i = 0; i < ACC_N; i++) {
114 		if (!quotas[i].name)
115 			continue;
116 		resp = talloc_asprintf_append(resp, "%-17s: %8d %s\n",
117 					      quotas[i].name, quotas[i].val,
118 					      quotas[i].descr);
119 		if (!resp)
120 			return ENOMEM;
121 	}
122 
123 	send_reply(conn, XS_CONTROL, resp, strlen(resp) + 1);
124 
125 	return 0;
126 }
127 
quota_set(const void * ctx,struct connection * conn,const char ** vec,int num,struct quota * quotas)128 static int quota_set(const void *ctx, struct connection *conn,
129 		     const char **vec, int num, struct quota *quotas)
130 {
131 	unsigned int i;
132 	int val;
133 
134 	if (num != 2)
135 		return EINVAL;
136 
137 	val = atoi(vec[1]);
138 	if (val < 1)
139 		return EINVAL;
140 
141 	for (i = 0; i < ACC_N; i++) {
142 		if (quotas[i].name && !strcmp(vec[0], quotas[i].name)) {
143 			quotas[i].val = val;
144 			send_ack(conn, XS_CONTROL);
145 			return 0;
146 		}
147 	}
148 
149 	return EINVAL;
150 }
151 
quota_get(const void * ctx,struct connection * conn,const char ** vec,int num)152 static int quota_get(const void *ctx, struct connection *conn,
153 		     const char **vec, int num)
154 {
155 	if (num != 1)
156 		return EINVAL;
157 
158 	return domain_get_quota(ctx, conn, atoi(vec[0]));
159 }
160 
quota_max(const void * ctx,struct connection * conn,const char ** vec,int num)161 static int quota_max(const void *ctx, struct connection *conn,
162 		     const char **vec, int num)
163 {
164 	if (num > 1)
165 		return EINVAL;
166 
167 	if (num == 1) {
168 		if (!strcmp(vec[0], "-r"))
169 			domain_reset_global_acc();
170 		else
171 			return EINVAL;
172 	}
173 
174 	return domain_max_global_acc(ctx, conn);
175 }
176 
do_control_quota(const void * ctx,struct connection * conn,const char ** vec,int num)177 static int do_control_quota(const void *ctx, struct connection *conn,
178 			    const char **vec, int num)
179 {
180 	if (num == 0)
181 		return quota_show_current(ctx, conn, hard_quotas);
182 
183 	if (!strcmp(vec[0], "set"))
184 		return quota_set(ctx, conn, vec + 1, num - 1, hard_quotas);
185 
186 	if (!strcmp(vec[0], "max"))
187 		return quota_max(ctx, conn, vec + 1, num - 1);
188 
189 	return quota_get(ctx, conn, vec, num);
190 }
191 
do_control_quota_s(const void * ctx,struct connection * conn,const char ** vec,int num)192 static int do_control_quota_s(const void *ctx, struct connection *conn,
193 			      const char **vec, int num)
194 {
195 	if (num == 0)
196 		return quota_show_current(ctx, conn, soft_quotas);
197 
198 	if (!strcmp(vec[0], "set"))
199 		return quota_set(ctx, conn, vec + 1, num - 1, soft_quotas);
200 
201 	return EINVAL;
202 }
203 
do_control_logfile(const void * ctx,struct connection * conn,const char ** vec,int num)204 static int do_control_logfile(const void *ctx, struct connection *conn,
205 			      const char **vec, int num)
206 {
207 	if (num != 1)
208 		return EINVAL;
209 
210 	close_log();
211 	talloc_free(tracefile);
212 	tracefile = absolute_filename(NULL, vec[0]);
213 	reopen_log();
214 
215 	send_ack(conn, XS_CONTROL);
216 	return 0;
217 }
218 
do_control_memreport(const void * ctx,struct connection * conn,const char ** vec,int num)219 static int do_control_memreport(const void *ctx, struct connection *conn,
220 				const char **vec, int num)
221 {
222 	FILE *fp;
223 	const char *filename;
224 	int fd;
225 
226 	if (num > 1)
227 		return EINVAL;
228 
229 	if (num == 0) {
230 		if (tracefd < 0) {
231 			if (!tracefile)
232 				return EBADF;
233 			fp = fopen(tracefile, "a");
234 		} else {
235 			/*
236 			 * Use dup() in order to avoid closing the file later
237 			 * with fclose() which will release stream resources.
238 			 */
239 			fd = dup(tracefd);
240 			if (fd < 0)
241 				return EBADF;
242 			fp = fdopen(fd, "a");
243 			if (!fp)
244 				close(fd);
245 		}
246 	} else {
247 		filename = absolute_filename(ctx, vec[0]);
248 		if (!filename)
249 			return ENOMEM;
250 		fp = fopen(filename, "a");
251 	}
252 
253 	if (!fp)
254 		return EBADF;
255 
256 	talloc_report_full(NULL, fp);
257 	fclose(fp);
258 
259 	send_ack(conn, XS_CONTROL);
260 	return 0;
261 }
262 
do_control_print(const void * ctx,struct connection * conn,const char ** vec,int num)263 static int do_control_print(const void *ctx, struct connection *conn,
264 			    const char **vec, int num)
265 {
266 	if (num != 1)
267 		return EINVAL;
268 
269 	xprintf("control: %s", vec[0]);
270 
271 	send_ack(conn, XS_CONTROL);
272 	return 0;
273 }
274 
275 static int do_control_help(const void *, struct connection *, const char **,
276 			   int);
277 
278 static struct cmd_s cmds[] = {
279 	{ "check", do_control_check, "" },
280 	{ "log", do_control_log, "[on|off|+<switch>|-<switch>]" },
281 
282 #ifndef NO_LIVE_UPDATE
283 	/*
284 	 * The parameters are those of the xenstore-control utility!
285 	 * Depending on environment (Mini-OS or daemon) the live-update
286 	 * sequence is split into several sub-operations:
287 	 * 1. Specification of new binary
288 	 *    daemon:  -f <filename>
289 	 *    Mini-OS: -b <binary-size>
290 	 *             -d <size> <data-bytes> (multiple of those)
291 	 * 2. New command-line (optional): -c <cmdline>
292 	 * 3. Start of update: -s [-F] [-t <timeout>]
293 	 * Any sub-operation needs to respond with the string "OK" in case
294 	 * of success, any other response indicates failure.
295 	 * A started live-update sequence can be aborted via "-a" (not
296 	 * needed in case of failure for the first or last live-update
297 	 * sub-operation).
298 	 */
299 	{ "live-update", do_control_lu,
300 		"[-c <cmdline>] [-F] [-t <timeout>] <file>\n"
301 		"    Default timeout is 60 seconds.", 5 },
302 #endif
303 	{ "logfile", do_control_logfile, "<file>" },
304 	{ "memreport", do_control_memreport, "[<file>]" },
305 	{ "print", do_control_print, "<string>" },
306 	{ "quota", do_control_quota,
307 		"[set <name> <val>|<domid>|max [-r]]" },
308 	{ "quota-soft", do_control_quota_s, "[set <name> <val>]" },
309 	{ "help", do_control_help, "" },
310 };
311 
do_control_help(const void * ctx,struct connection * conn,const char ** vec,int num)312 static int do_control_help(const void *ctx, struct connection *conn,
313 			   const char **vec, int num)
314 {
315 	int cmd;
316 	char *resp;
317 
318 	if (num)
319 		return EINVAL;
320 
321 	resp = talloc_asprintf(ctx, "%s", "");
322 	if (!resp)
323 		return ENOMEM;
324 	for (cmd = 0; cmd < ARRAY_SIZE(cmds); cmd++) {
325 		resp = talloc_asprintf_append(resp, "%-15s %s\n",
326 					      cmds[cmd].cmd, cmds[cmd].pars);
327 		if (!resp)
328 			return ENOMEM;
329 	}
330 
331 	send_reply(conn, XS_CONTROL, resp, strlen(resp) + 1);
332 	return 0;
333 }
334 
do_control(const void * ctx,struct connection * conn,struct buffered_data * in)335 int do_control(const void *ctx, struct connection *conn,
336 	       struct buffered_data *in)
337 {
338 	unsigned int cmd, num, off;
339 	const char **vec = NULL;
340 
341 	if (domain_is_unprivileged(conn))
342 		return EACCES;
343 
344 	off = get_string(in, 0);
345 	if (!off)
346 		return EINVAL;
347 	for (cmd = 0; cmd < ARRAY_SIZE(cmds); cmd++)
348 		if (streq(in->buffer, cmds[cmd].cmd))
349 			break;
350 	if (cmd == ARRAY_SIZE(cmds))
351 		return EINVAL;
352 
353 	num = xenstore_count_strings(in->buffer, in->used);
354 	if (cmds[cmd].max_pars)
355 		num = min(num, cmds[cmd].max_pars);
356 	vec = (const char **)talloc_array(ctx, char *, num);
357 	if (!vec)
358 		return ENOMEM;
359 	if (get_strings(in, vec, num) < num)
360 		return EIO;
361 
362 	return cmds[cmd].func(ctx, conn, vec + 1, num - 1);
363 }
364