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 
startup(const char * op)220 static void startup(const char *op) {
221     xtl_log(&logger,XTL_DEBUG,0,program,"starting %s",op);
222 
223     xch = xc_interface_open(&logger,&logger,0);
224     if (!xch) fail(errno,"xc_interface_open failed");
225 }
226 
complete(int retval)227 static void complete(int retval) {
228     int errnoval = retval ? errno : 0; /* suppress irrelevant errnos */
229     xtl_log(&logger,XTL_DEBUG,errnoval,program,"complete r=%d",retval);
230     helper_stub_complete(retval,errnoval,0);
231     xc_interface_close(xch);
232     exit(0);
233 }
234 
main(int argc,char ** argv)235 int main(int argc, char **argv)
236 {
237     int r;
238     int send_back_fd, recv_fd;
239 
240 #define NEXTARG (++argv, assert(*argv), *argv)
241 
242     const char *mode = *++argv;
243     assert(mode);
244 
245     if (!strcmp(mode,"--save-domain")) {
246         static struct save_callbacks cb;
247 
248         io_fd =                             atoi(NEXTARG);
249         recv_fd =                           atoi(NEXTARG);
250         uint32_t dom =                      strtoul(NEXTARG,0,10);
251         uint32_t flags =                    strtoul(NEXTARG,0,10);
252         unsigned cbflags =                  strtoul(NEXTARG,0,10);
253         xc_stream_type_t stream_type =      strtoul(NEXTARG,0,10);
254         assert(!*++argv);
255 
256         helper_setcallbacks_save(&cb, cbflags);
257 
258         startup("save");
259         setup_signals(save_signal_handler);
260 
261         r = xc_domain_save(xch, io_fd, dom, flags, &cb, stream_type, recv_fd);
262         complete(r);
263 
264     } else if (!strcmp(mode,"--restore-domain")) {
265         static struct restore_callbacks cb;
266 
267         io_fd =                             atoi(NEXTARG);
268         send_back_fd =                      atoi(NEXTARG);
269         uint32_t dom =                      strtoul(NEXTARG,0,10);
270         unsigned store_evtchn =             strtoul(NEXTARG,0,10);
271         domid_t store_domid =               strtoul(NEXTARG,0,10);
272         unsigned console_evtchn =           strtoul(NEXTARG,0,10);
273         domid_t console_domid =             strtoul(NEXTARG,0,10);
274         unsigned cbflags =                  strtoul(NEXTARG,0,10);
275         xc_stream_type_t stream_type =      strtoul(NEXTARG,0,10);
276         assert(!*++argv);
277 
278         helper_setcallbacks_restore(&cb, cbflags);
279 
280         unsigned long store_mfn = 0;
281         unsigned long console_mfn = 0;
282 
283         startup("restore");
284         setup_signals(SIG_DFL);
285 
286         r = xc_domain_restore(xch, io_fd, dom, store_evtchn, &store_mfn,
287                               store_domid, console_evtchn, &console_mfn,
288                               console_domid, stream_type, &cb, send_back_fd);
289         helper_stub_restore_results(store_mfn,console_mfn,0);
290         complete(r);
291 
292     } else {
293         assert(!"unexpected mode argument");
294     }
295 }
296 
297 /*
298  * Local variables:
299  * mode: C
300  * c-basic-offset: 4
301  * indent-tabs-mode: nil
302  * End:
303  */
304