1 #define _GNU_SOURCE
2 #include <stdbool.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <time.h>
7 #include <xenctrl.h>
8 #include <xenguest.h>
9 
10 #include "xenstore.h"
11 
12 /* Add a string plus terminating 0 byte to buf, returning new len. */
add_to_buf(char ** buf,const char * val,int len)13 static int add_to_buf(char **buf, const char *val, int len)
14 {
15     int vallen = strlen(val) + 1;
16 
17     if (len < 0)
18         return -1;
19 
20     *buf = realloc(*buf, len + vallen);
21     if (!*buf)
22         return -1;
23 
24     strcpy(*buf + len, val);
25 
26     return len + vallen;
27 }
28 
live_update_start(struct xs_handle * xsh,bool force,unsigned int to)29 static int live_update_start(struct xs_handle *xsh, bool force, unsigned int to)
30 {
31     int len = 0;
32     char *buf = NULL, *ret;
33     time_t time_start;
34 
35     if (asprintf(&ret, "%u", to) < 0)
36         return 1;
37     len = add_to_buf(&buf, "-s", len);
38     len = add_to_buf(&buf, "-t", len);
39     len = add_to_buf(&buf, ret, len);
40     free(ret);
41     if (force)
42         len = add_to_buf(&buf, "-F", len);
43     if (len < 0)
44         return 1;
45 
46     ret = strdup("BUSY");
47     if (!ret) {
48         free(buf);
49         return 1;
50     }
51 
52     for (time_start = time(NULL); time(NULL) - time_start < to;) {
53         free(ret);
54         ret = xs_control_command(xsh, "live-update", buf, len);
55         if (!ret)
56             goto err;
57         if (strcmp(ret, "BUSY"))
58             break;
59         sleep(1);
60     }
61 
62     if (strcmp(ret, "OK"))
63         goto err;
64 
65     free(buf);
66     free(ret);
67 
68     return 0;
69 
70  err:
71     fprintf(stderr, "Starting live update failed:\n%s\n",
72             ret ? : strerror(errno));
73     free(buf);
74     free(ret);
75 
76     return 3;
77 }
78 
live_update_cmdline(struct xs_handle * xsh,const char * cmdline)79 static int live_update_cmdline(struct xs_handle *xsh, const char *cmdline)
80 {
81     int len = 0, rc = 0;
82     char *buf = NULL, *ret;
83 
84     len = add_to_buf(&buf, "-c", len);
85     len = add_to_buf(&buf, cmdline, len);
86     if (len < 0)
87         return 1;
88 
89     ret = xs_control_command(xsh, "live-update", buf, len);
90     free(buf);
91     if (!ret || strcmp(ret, "OK")) {
92         fprintf(stderr, "Setting update binary failed:\n%s\n",
93                 ret ? : strerror(errno));
94         rc = 3;
95     }
96     free(ret);
97 
98     return rc;
99 }
100 
send_kernel_blob(struct xs_handle * xsh,const char * binary)101 static int send_kernel_blob(struct xs_handle *xsh, const char *binary)
102 {
103     int rc = 0, len = 0;
104     xc_interface *xch;
105     struct xc_dom_image *dom;
106     char *ret, *buf = NULL;
107     size_t off, sz;
108 #define BLOB_CHUNK_SZ 2048
109 
110     xch = xc_interface_open(NULL, NULL, 0);
111     if (!xch) {
112         fprintf(stderr, "xc_interface_open() failed\n");
113         return 1;
114     }
115 
116     dom = xc_dom_allocate(xch, NULL, NULL);
117     if (!dom) {
118         rc = 1;
119         goto out_close;
120     }
121 
122     rc = xc_dom_kernel_file(dom, binary);
123     if (rc) {
124         rc = 1;
125         goto out_rel;
126     }
127 
128     if (asprintf(&ret, "%zu", dom->kernel_size) < 0) {
129         rc = 1;
130         goto out_rel;
131     }
132     len = add_to_buf(&buf, "-b", len);
133     len = add_to_buf(&buf, ret, len);
134     free(ret);
135     if (len < 0) {
136         rc = 1;
137         goto out_rel;
138     }
139     ret = xs_control_command(xsh, "live-update", buf, len);
140     free(buf);
141     if (!ret || strcmp(ret, "OK")) {
142         fprintf(stderr, "Starting live update failed:\n%s\n",
143                 ret ? : strerror(errno));
144         rc = 3;
145     }
146     free(ret);
147     if (rc)
148         goto out_rel;
149 
150     /* buf capable to hold "-d" <1..2048> BLOB_CHUNK_SZ and a terminating 0. */
151     buf = malloc(3 + 5 + BLOB_CHUNK_SZ + 1);
152     if (!buf) {
153         rc = 1;
154         goto out_rel;
155     }
156 
157     strcpy(buf, "-d");
158     sz = BLOB_CHUNK_SZ;
159     for (off = 0; off < dom->kernel_size; off += BLOB_CHUNK_SZ) {
160         if (dom->kernel_size - off < BLOB_CHUNK_SZ)
161             sz = dom->kernel_size - off;
162         sprintf(buf + 3, "%zu", sz);
163         len = 3 + strlen(buf + 3) + 1;
164         memcpy(buf + len, dom->kernel_blob + off, sz);
165         buf[len + sz] = 0;
166         len += sz + 1;
167         ret = xs_control_command(xsh, "live-update", buf, len);
168         if (!ret || strcmp(ret, "OK")) {
169             fprintf(stderr, "Transfer of new binary failed:\n%s\n",
170                     ret ? : strerror(errno));
171             rc = 3;
172             free(ret);
173             break;
174         }
175         free(ret);
176     }
177 
178     free(buf);
179 
180  out_rel:
181     xc_dom_release(dom);
182 
183  out_close:
184     xc_interface_close(xch);
185 
186     return rc;
187 }
188 
189 /*
190  * Live update of Xenstore stubdom
191  *
192  * Sequence of actions:
193  * 1. transfer new stubdom binary
194  *    a) specify size
195  *    b) transfer unpacked binary in chunks
196  * 2. transfer new cmdline (optional)
197  * 3. start update (includes flags)
198  */
live_update_stubdom(struct xs_handle * xsh,const char * binary,const char * cmdline,bool force,unsigned int to)199 static int live_update_stubdom(struct xs_handle *xsh, const char *binary,
200                                const char *cmdline, bool force, unsigned int to)
201 {
202     int rc;
203 
204     rc = send_kernel_blob(xsh, binary);
205     if (rc)
206         goto abort;
207 
208     if (cmdline) {
209         rc = live_update_cmdline(xsh, cmdline);
210         if (rc)
211             goto abort;
212     }
213 
214     rc = live_update_start(xsh, force, to);
215     if (rc)
216         goto abort;
217 
218     return 0;
219 
220  abort:
221     xs_control_command(xsh, "live-update", "-a", 3);
222     return rc;
223 }
224 
225 /*
226  * Live update of Xenstore daemon
227  *
228  * Sequence of actions:
229  * 1. transfer new binary filename
230  * 2. transfer new cmdline (optional)
231  * 3. start update (includes flags)
232  */
live_update_daemon(struct xs_handle * xsh,const char * binary,const char * cmdline,bool force,unsigned int to)233 static int live_update_daemon(struct xs_handle *xsh, const char *binary,
234                               const char *cmdline, bool force, unsigned int to)
235 {
236     int len = 0, rc;
237     char *buf = NULL, *ret;
238 
239     len = add_to_buf(&buf, "-f", len);
240     len = add_to_buf(&buf, binary, len);
241     if (len < 0)
242         return 1;
243     ret = xs_control_command(xsh, "live-update", buf, len);
244     free(buf);
245     if (!ret || strcmp(ret, "OK")) {
246         fprintf(stderr, "Setting update binary failed:\n%s\n",
247                 ret ? : strerror(errno));
248         free(ret);
249         return 3;
250     }
251     free(ret);
252 
253     if (cmdline) {
254         rc = live_update_cmdline(xsh, cmdline);
255         if (rc)
256             goto abort;
257     }
258 
259     rc = live_update_start(xsh, force, to);
260     if (rc)
261         goto abort;
262 
263     return 0;
264 
265  abort:
266     xs_control_command(xsh, "live-update", "-a", 3);
267     return rc;
268 }
269 
live_update(struct xs_handle * xsh,int argc,char ** argv)270 static int live_update(struct xs_handle *xsh, int argc, char **argv)
271 {
272     int rc = 0;
273     unsigned int i, to = 60;
274     char *binary = NULL, *cmdline = NULL, *val;
275     bool force = false;
276 
277     for (i = 0; i < argc; i++) {
278         if (!strcmp(argv[i], "-c")) {
279             i++;
280             if (i == argc) {
281                 fprintf(stderr, "Missing command line value\n");
282                 rc = 2;
283                 goto out;
284             }
285             cmdline = argv[i];
286         } else if (!strcmp(argv[i], "-t")) {
287             i++;
288             if (i == argc) {
289                 fprintf(stderr, "Missing timeout value\n");
290                 rc = 2;
291                 goto out;
292             }
293             to = atoi(argv[i]);
294         } else if (!strcmp(argv[i], "-F"))
295             force = true;
296         else
297             binary = argv[i];
298     }
299 
300     if (!binary) {
301         fprintf(stderr, "Missing binary specification\n");
302         rc = 2;
303         goto out;
304     }
305 
306     val = xs_read(xsh, XBT_NULL, "/tool/xenstored/domid", &i);
307     if (val)
308         rc = live_update_stubdom(xsh, binary, cmdline, force, to);
309     else
310         rc = live_update_daemon(xsh, binary, cmdline, force, to);
311 
312     free(val);
313 
314  out:
315     return rc;
316 }
317 
main(int argc,char ** argv)318 int main(int argc, char **argv)
319 {
320     struct xs_handle *xsh;
321     char *par = NULL;
322     char *ret;
323     unsigned int p;
324     int rc = 0, len = 0;
325 
326     if (argc < 2) {
327         fprintf(stderr, "Usage:\n"
328                 "%s <command> [<arg>...]\n", argv[0]);
329         rc = 2;
330         goto out;
331     }
332 
333     xsh = xs_open(0);
334     if (xsh == NULL) {
335         fprintf(stderr, "Failed to contact Xenstored.\n");
336         rc = 1;
337         goto out;
338     }
339 
340     if (!strcmp(argv[1], "live-update")) {
341         rc = live_update(xsh, argc - 2, argv + 2);
342         goto out_close;
343     }
344 
345     for (p = 2; p < argc; p++)
346         len = add_to_buf(&par, argv[p], len);
347     if (len < 0) {
348         fprintf(stderr, "Allocation error.\n");
349         rc = 1;
350         goto out_close;
351     }
352 
353     ret = xs_control_command(xsh, argv[1], par, len);
354     if (!ret) {
355         rc = 3;
356         if (errno == EINVAL) {
357             ret = xs_control_command(xsh, "help", NULL, 0);
358             if (ret)
359                 fprintf(stderr, "Command not supported. Valid commands are:\n"
360                                 "%s\n", ret);
361             else
362                 fprintf(stderr, "Error when executing command.\n");
363         } else
364             fprintf(stderr, "Error %d when trying to execute command.\n",
365                     errno);
366     } else if (strlen(ret) > 0)
367         printf("%s\n", ret);
368 
369  out_close:
370     xs_close(xsh);
371 
372  out:
373     free(par);
374     return rc;
375 }
376