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