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