1 /*
2  * Copyright (C) 2012      Citrix Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published
6  * by the Free Software Foundation; version 2.1 only. with the special
7  * exception on linking described in file LICENSE.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Lesser General Public License for more details.
13  */
14 
15 /*
16  * The libxl-save-helper utility speaks a protocol to its caller for
17  * the callbacks.  The protocol is as follows.
18  *
19  * The helper talks on stdin and stdout, in binary in machine
20  * endianness.  The helper speaks first, and only when it has a
21  * callback to make.  It writes a 16-bit number being the message
22  * length, and then the message body.
23  *
24  * Each message starts with a 16-bit number indicating which of the
25  * messages it is, and then some arguments in a binary marshalled form.
26  * If the callback does not need a reply (it returns void), the helper
27  * just continues.  Otherwise the helper waits for its caller to send a
28  * single int which is to be the return value from the callback.
29  *
30  * Where feasible the stubs and callbacks have prototypes identical to
31  * those required by xc_domain_save and xc_domain_restore, so that the
32  * autogenerated functions can be used/provided directly.
33  *
34  * The actual messages are in the array @msgs in libxl_save_msgs_gen.pl
35  */
36 
37 #include "libxl_osdeps.h"
38 
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <assert.h>
42 #include <inttypes.h>
43 #include <fcntl.h>
44 #include <signal.h>
45 
46 #include "libxl.h"
47 #include "libxl_utils.h"
48 
49 #include "xenctrl.h"
50 #include "xenguest.h"
51 #include "_libxl_save_msgs_helper.h"
52 
53 /*----- logger -----*/
54 
55 __attribute__((format(printf, 5, 0)))
tellparent_vmessage(xentoollog_logger * logger_in,xentoollog_level level,int errnoval,const char * context,const char * format,va_list al)56 static void tellparent_vmessage(xentoollog_logger *logger_in,
57                                 xentoollog_level level,
58                                 int errnoval,
59                                 const char *context,
60                                 const char *format,
61                                 va_list al)
62 {
63     char *formatted;
64     int r = vasprintf(&formatted, format, al);
65     if (r < 0) { perror("memory allocation failed during logging"); exit(-1); }
66     helper_stub_log(level, errnoval, context, formatted, 0);
67     free(formatted);
68 }
69 
tellparent_progress(struct xentoollog_logger * logger_in,const char * context,const char * doing_what,int percent,unsigned long done,unsigned long total)70 static void tellparent_progress(struct xentoollog_logger *logger_in,
71                                 const char *context,
72                                 const char *doing_what, int percent,
73                                 unsigned long done, unsigned long total)
74 {
75     helper_stub_progress(context, doing_what, done, total, 0);
76 }
77 
tellparent_destroy(struct xentoollog_logger * logger_in)78 static void tellparent_destroy(struct xentoollog_logger *logger_in)
79 {
80     abort();
81 }
82 
83 /*----- globals -----*/
84 
85 static const char *program = "libxl-save-helper";
86 static xentoollog_logger logger = {
87     tellparent_vmessage,
88     tellparent_progress,
89     tellparent_destroy,
90 };
91 static xc_interface *xch;
92 static int io_fd;
93 
94 /*----- error handling -----*/
95 
96 static void fail(int errnoval, const char *fmt, ...)
97     __attribute__((noreturn,format(printf,2,3)));
fail(int errnoval,const char * fmt,...)98 static void fail(int errnoval, const char *fmt, ...)
99 {
100     va_list al;
101     va_start(al,fmt);
102     xtl_logv(&logger,XTL_ERROR,errnoval,program,fmt,al);
103     exit(-1);
104 }
105 
read_exactly(int fd,void * buf,size_t len)106 static int read_exactly(int fd, void *buf, size_t len)
107 /* returns 0 if we get eof, even if we got it midway through; 1 if ok */
108 {
109     while (len) {
110         ssize_t r = read(fd, buf, len);
111         if (r<=0) return r;
112         assert(r <= len);
113         len -= r;
114         buf = (char*)buf + r;
115     }
116     return 1;
117 }
118 
xmalloc(size_t sz)119 static void *xmalloc(size_t sz)
120 {
121     if (!sz) return 0;
122     void *r = malloc(sz);
123     if (!r) { perror("memory allocation failed"); exit(-1); }
124     return r;
125 }
126 
127 /*----- signal handling -----*/
128 
129 static int unwriteable_fd;
130 
save_signal_handler(int num)131 static void save_signal_handler(int num)
132 {
133     /*
134      * We want to be able to interrupt save.  But the code in libxc
135      * which does the actual saving is straight-through, and we need
136      * to execute its error path to put the guest back to sanity.
137      *
138      * So what we do is this: when we get the signal, we dup2
139      * the result of open("/dev/null",O_RDONLY) onto the output fd.
140      *
141      * This is guaranteed to 1. interrupt libxc's write (causing it to
142      * return short, or maybe EINTR); 2. make the next write give
143      * EBADF, so that: 3. at latest, libxc will notice when it next
144      * tries to write data and will then go into its cleanup path.
145      *
146      * We make no effort here to sanitise the resulting errors.
147      * That's libxl's job.
148      */
149     int esave = errno;
150 
151     int r = dup2(unwriteable_fd, io_fd);
152     if (r != io_fd)
153         /* we can't write an xtl message because we might end up
154          * interleaving on our control stream; we can't use stdio
155          * because it's not async-signal-safe */
156         abort();
157 
158     errno = esave;
159 }
160 
setup_signals(void (* handler)(int))161 static void setup_signals(void (*handler)(int))
162 {
163     struct sigaction sa;
164     sigset_t spmask;
165     int r;
166 
167     unwriteable_fd = open("/dev/null",O_RDONLY);
168     if (unwriteable_fd < 0) fail(errno,"open /dev/null for reading");
169 
170     LIBXL_FILLZERO(sa);
171     sa.sa_handler = handler;
172     sigemptyset(&sa.sa_mask);
173     r = sigaction(SIGTERM, &sa, 0);
174     if (r) fail(errno,"sigaction SIGTERM failed");
175 
176     sigemptyset(&spmask);
177     sigaddset(&spmask,SIGTERM);
178     r = sigprocmask(SIG_UNBLOCK,&spmask,0);
179     if (r) fail(errno,"sigprocmask unblock SIGTERM failed");
180 }
181 
182 /*----- helper functions called by autogenerated stubs -----*/
183 
helper_allocbuf(int len,void * user)184 unsigned char * helper_allocbuf(int len, void *user)
185 {
186     return xmalloc(len);
187 }
188 
transmit(const unsigned char * msg,int len,void * user)189 static void transmit(const unsigned char *msg, int len, void *user)
190 {
191     while (len) {
192         int r = write(1, msg, len);
193         if (r<0) { perror("write"); exit(-1); }
194         assert(r >= 0);
195         assert(r <= len);
196         len -= r;
197         msg += r;
198     }
199 }
200 
helper_transmitmsg(unsigned char * msg_freed,int len_in,void * user)201 void helper_transmitmsg(unsigned char *msg_freed, int len_in, void *user)
202 {
203     assert(len_in < 64*1024);
204     uint16_t len = len_in;
205     transmit((const void*)&len, sizeof(len), user);
206     transmit(msg_freed, len, user);
207     free(msg_freed);
208 }
209 
helper_getreply(void * user)210 int helper_getreply(void *user)
211 {
212     int v;
213     int r = read_exactly(0, &v, sizeof(v));
214     if (r<=0) exit(-2);
215     return v;
216 }
217 
218 /*----- other callbacks -----*/
219 
220 static struct save_callbacks helper_save_callbacks;
221 
startup(const char * op)222 static void startup(const char *op) {
223     xtl_log(&logger,XTL_DEBUG,0,program,"starting %s",op);
224 
225     xch = xc_interface_open(&logger,&logger,0);
226     if (!xch) fail(errno,"xc_interface_open failed");
227 }
228 
complete(int retval)229 static void complete(int retval) {
230     int errnoval = retval ? errno : 0; /* suppress irrelevant errnos */
231     xtl_log(&logger,XTL_DEBUG,errnoval,program,"complete r=%d",retval);
232     helper_stub_complete(retval,errnoval,0);
233     xc_interface_close(xch);
234     exit(0);
235 }
236 
237 static struct restore_callbacks helper_restore_callbacks;
238 
main(int argc,char ** argv)239 int main(int argc, char **argv)
240 {
241     int r;
242     int send_back_fd, recv_fd;
243 
244 #define NEXTARG (++argv, assert(*argv), *argv)
245 
246     const char *mode = *++argv;
247     assert(mode);
248 
249     if (!strcmp(mode,"--save-domain")) {
250 
251         io_fd =                             atoi(NEXTARG);
252         recv_fd =                           atoi(NEXTARG);
253         uint32_t dom =                      strtoul(NEXTARG,0,10);
254         uint32_t flags =                    strtoul(NEXTARG,0,10);
255         int hvm =                           atoi(NEXTARG);
256         unsigned cbflags =                  strtoul(NEXTARG,0,10);
257         xc_migration_stream_t stream_type = strtoul(NEXTARG,0,10);
258         assert(!*++argv);
259 
260         helper_setcallbacks_save(&helper_save_callbacks, cbflags);
261 
262         startup("save");
263         setup_signals(save_signal_handler);
264 
265         r = xc_domain_save(xch, io_fd, dom, flags, &helper_save_callbacks,
266                            hvm, stream_type, recv_fd);
267         complete(r);
268 
269     } else if (!strcmp(mode,"--restore-domain")) {
270 
271         io_fd =                             atoi(NEXTARG);
272         send_back_fd =                      atoi(NEXTARG);
273         uint32_t dom =                      strtoul(NEXTARG,0,10);
274         unsigned store_evtchn =             strtoul(NEXTARG,0,10);
275         domid_t store_domid =               strtoul(NEXTARG,0,10);
276         unsigned console_evtchn =           strtoul(NEXTARG,0,10);
277         domid_t console_domid =             strtoul(NEXTARG,0,10);
278         unsigned int hvm =                  strtoul(NEXTARG,0,10);
279         unsigned int pae =                  strtoul(NEXTARG,0,10);
280         unsigned cbflags =                  strtoul(NEXTARG,0,10);
281         xc_migration_stream_t stream_type = strtoul(NEXTARG,0,10);
282         assert(!*++argv);
283 
284         helper_setcallbacks_restore(&helper_restore_callbacks, cbflags);
285 
286         unsigned long store_mfn = 0;
287         unsigned long console_mfn = 0;
288 
289         startup("restore");
290         setup_signals(SIG_DFL);
291 
292         r = xc_domain_restore(xch, io_fd, dom, store_evtchn, &store_mfn,
293                               store_domid, console_evtchn, &console_mfn,
294                               console_domid, hvm, pae,
295                               stream_type,
296                               &helper_restore_callbacks, send_back_fd);
297         helper_stub_restore_results(store_mfn,console_mfn,0);
298         complete(r);
299 
300     } else {
301         assert(!"unexpected mode argument");
302     }
303 }
304 
305 /*
306  * Local variables:
307  * mode: C
308  * c-basic-offset: 4
309  * indent-tabs-mode: nil
310  * End:
311  */
312