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 #include "libxl_osdeps.h"
16
17 #include "libxl_internal.h"
18
19 /* stream_fd is as from the caller (eventually, the application).
20 * It may be 0, 1 or 2, in which case we need to dup it elsewhere.
21 * The actual fd value is not included in the supplied argnums; rather
22 * it will be automatically supplied by run_helper as the 2nd argument.
23 *
24 * preserve_fds are fds that the caller is intending to pass to the
25 * helper so which need cloexec clearing. They may not be 0, 1 or 2.
26 * An entry may be -1 in which case it will be ignored.
27 */
28 static void run_helper(libxl__egc *egc, libxl__save_helper_state *shs,
29 const char *mode_arg,
30 int stream_fd, int back_channel_fd,
31 const int *preserve_fds, int num_preserve_fds,
32 const unsigned long *argnums, int num_argnums);
33
34 static void helper_failed(libxl__egc*, libxl__save_helper_state *shs, int rc);
35 static void helper_stop(libxl__egc *egc, libxl__ao_abortable*, int rc);
36 static void helper_stdout_readable(libxl__egc *egc, libxl__ev_fd *ev,
37 int fd, short events, short revents);
38 static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
39 pid_t pid, int status);
40 static void helper_done(libxl__egc *egc, libxl__save_helper_state *shs);
41
42 /*----- entrypoints -----*/
43
libxl__xc_domain_restore(libxl__egc * egc,libxl__domain_create_state * dcs,libxl__save_helper_state * shs)44 void libxl__xc_domain_restore(libxl__egc *egc, libxl__domain_create_state *dcs,
45 libxl__save_helper_state *shs)
46 {
47 STATE_AO_GC(dcs->ao);
48
49 /* Convenience aliases */
50 const uint32_t domid = dcs->guest_domid;
51 const int restore_fd = dcs->libxc_fd;
52 const int send_back_fd = dcs->send_back_fd;
53 libxl__domain_build_state *const state = &dcs->build_state;
54
55 unsigned cbflags =
56 libxl__srm_callout_enumcallbacks_restore(&shs->callbacks.restore.a);
57
58 const unsigned long argnums[] = {
59 domid,
60 state->store_port,
61 state->store_domid, state->console_port,
62 state->console_domid,
63 cbflags, dcs->restore_params.checkpointed_stream,
64 };
65
66 shs->ao = ao;
67 shs->domid = domid;
68 shs->recv_callback = libxl__srm_callout_received_restore;
69 if (dcs->restore_params.checkpointed_stream ==
70 LIBXL_CHECKPOINTED_STREAM_COLO)
71 shs->completion_callback = libxl__colo_restore_teardown;
72 else
73 shs->completion_callback = libxl__xc_domain_restore_done;
74 shs->caller_state = dcs;
75 shs->need_results = 1;
76
77 run_helper(egc, shs, "--restore-domain", restore_fd, send_back_fd, 0, 0,
78 argnums, ARRAY_SIZE(argnums));
79 }
80
libxl__xc_domain_save(libxl__egc * egc,libxl__domain_save_state * dss,libxl__save_helper_state * shs)81 void libxl__xc_domain_save(libxl__egc *egc, libxl__domain_save_state *dss,
82 libxl__save_helper_state *shs)
83 {
84 STATE_AO_GC(dss->ao);
85
86 unsigned cbflags =
87 libxl__srm_callout_enumcallbacks_save(&shs->callbacks.save.a);
88
89 const unsigned long argnums[] = {
90 dss->domid, dss->xcflags, cbflags,
91 dss->checkpointed_stream,
92 };
93
94 shs->ao = ao;
95 shs->domid = dss->domid;
96 shs->recv_callback = libxl__srm_callout_received_save;
97 shs->completion_callback = libxl__xc_domain_save_done;
98 shs->caller_state = dss;
99 shs->need_results = 0;
100
101 run_helper(egc, shs, "--save-domain", dss->fd, dss->recv_fd,
102 NULL, 0,
103 argnums, ARRAY_SIZE(argnums));
104 return;
105 }
106
107
libxl__xc_domain_saverestore_async_callback_done(libxl__egc * egc,libxl__save_helper_state * shs,int return_value)108 void libxl__xc_domain_saverestore_async_callback_done(libxl__egc *egc,
109 libxl__save_helper_state *shs, int return_value)
110 {
111 shs->egc = egc;
112 libxl__srm_callout_sendreply(return_value, shs);
113 shs->egc = 0;
114 }
115
libxl__save_helper_init(libxl__save_helper_state * shs)116 void libxl__save_helper_init(libxl__save_helper_state *shs)
117 {
118 libxl__ao_abortable_init(&shs->abrt);
119 libxl__ev_fd_init(&shs->readable);
120 libxl__ev_child_init(&shs->child);
121 }
122
123 /*----- helper execution -----*/
124
125 /* This function can not fail. */
dup_cloexec(libxl__gc * gc,int fd,const char * what)126 static int dup_cloexec(libxl__gc *gc, int fd, const char *what)
127 {
128 int dup_fd = fd;
129
130 if (fd <= 2) {
131 dup_fd = dup(fd);
132 if (dup_fd < 0) {
133 LOGE(ERROR,"dup %s", what);
134 exit(-1);
135 }
136 }
137 libxl_fd_set_cloexec(CTX, dup_fd, 0);
138
139 return dup_fd;
140 }
141
142 /*
143 * Both save and restore share four parameters:
144 * 1) Path to libxl-save-helper.
145 * 2) --[restore|save]-domain.
146 * 3) stream file descriptor.
147 * 4) back channel file descriptor.
148 * n) save/restore specific parameters.
149 * 5) A \0 at the end.
150 */
151 #define HELPER_NR_ARGS 5
run_helper(libxl__egc * egc,libxl__save_helper_state * shs,const char * mode_arg,int stream_fd,int back_channel_fd,const int * preserve_fds,int num_preserve_fds,const unsigned long * argnums,int num_argnums)152 static void run_helper(libxl__egc *egc, libxl__save_helper_state *shs,
153 const char *mode_arg,
154 int stream_fd, int back_channel_fd,
155 const int *preserve_fds, int num_preserve_fds,
156 const unsigned long *argnums, int num_argnums)
157 {
158 STATE_AO_GC(shs->ao);
159 const char *args[HELPER_NR_ARGS + num_argnums];
160 const char **arg = args;
161 int i, rc;
162
163 /* Resources we must free */
164 libxl__carefd *childs_pipes[2] = { 0,0 };
165
166 /* Convenience aliases */
167 const uint32_t domid = shs->domid;
168
169 shs->rc = 0;
170 shs->completed = 0;
171 shs->pipes[0] = shs->pipes[1] = 0;
172 libxl__save_helper_init(shs);
173
174 shs->abrt.ao = shs->ao;
175 shs->abrt.callback = helper_stop;
176 rc = libxl__ao_abortable_register(&shs->abrt);
177 if (rc) goto out;
178
179 shs->stdin_what = GCSPRINTF("domain %"PRIu32" save/restore helper"
180 " stdin pipe", domid);
181 shs->stdout_what = GCSPRINTF("domain %"PRIu32" save/restore helper"
182 " stdout pipe", domid);
183
184 *arg++ = getenv("LIBXL_SAVE_HELPER") ?: LIBEXEC_BIN "/" "libxl-save-helper";
185 *arg++ = mode_arg;
186 const char **stream_fd_arg = arg++;
187 const char **back_channel_fd_arg = arg++;
188 for (i=0; i<num_argnums; i++)
189 *arg++ = GCSPRINTF("%lu", argnums[i]);
190 *arg++ = 0;
191 assert(arg == args + ARRAY_SIZE(args));
192
193 libxl__carefd_begin();
194 int childfd;
195 for (childfd=0; childfd<2; childfd++) {
196 /* Setting up the pipe for the child's fd childfd */
197 int fds[2];
198 if (libxl_pipe(CTX,fds)) {
199 rc = ERROR_FAIL;
200 libxl__carefd_unlock();
201 goto out;
202 }
203 int childs_end = childfd==0 ? 0 /*read*/ : 1 /*write*/;
204 int our_end = childfd==0 ? 1 /*write*/ : 0 /*read*/;
205 childs_pipes[childfd] = libxl__carefd_record(CTX, fds[childs_end]);
206 shs->pipes[childfd] = libxl__carefd_record(CTX, fds[our_end]);
207 }
208 libxl__carefd_unlock();
209
210 pid_t pid = libxl__ev_child_fork(gc, &shs->child, helper_exited);
211 if (!pid) {
212 stream_fd = dup_cloexec(gc, stream_fd, "migration stream fd");
213 *stream_fd_arg = GCSPRINTF("%d", stream_fd);
214
215 if (back_channel_fd >= 0)
216 back_channel_fd = dup_cloexec(gc, back_channel_fd,
217 "migration back channel fd");
218 *back_channel_fd_arg = GCSPRINTF("%d", back_channel_fd);
219
220 for (i=0; i<num_preserve_fds; i++)
221 if (preserve_fds[i] >= 0) {
222 assert(preserve_fds[i] > 2);
223 libxl_fd_set_cloexec(CTX, preserve_fds[i], 0);
224 }
225
226 libxl__exec(gc,
227 libxl__carefd_fd(childs_pipes[0]),
228 libxl__carefd_fd(childs_pipes[1]),
229 -1,
230 args[0], (char**)args, 0);
231 }
232
233 libxl__carefd_close(childs_pipes[0]);
234 libxl__carefd_close(childs_pipes[1]);
235
236 rc = libxl__ev_fd_register(gc, &shs->readable, helper_stdout_readable,
237 libxl__carefd_fd(shs->pipes[1]), POLLIN|POLLPRI);
238 if (rc) goto out;
239 return;
240
241 out:
242 libxl__carefd_close(childs_pipes[0]);
243 libxl__carefd_close(childs_pipes[1]);
244 helper_failed(egc, shs, rc);;
245 }
246
helper_failed(libxl__egc * egc,libxl__save_helper_state * shs,int rc)247 static void helper_failed(libxl__egc *egc, libxl__save_helper_state *shs,
248 int rc)
249 {
250 STATE_AO_GC(shs->ao);
251
252 if (!shs->rc)
253 shs->rc = rc;
254
255 libxl__ev_fd_deregister(gc, &shs->readable);
256
257 if (!libxl__save_helper_inuse(shs)) {
258 helper_done(egc, shs);
259 return;
260 }
261
262 libxl__kill(gc, shs->child.pid, SIGKILL, "save/restore helper");
263 }
264
helper_stop(libxl__egc * egc,libxl__ao_abortable * abrt,int rc)265 static void helper_stop(libxl__egc *egc, libxl__ao_abortable *abrt, int rc)
266 {
267 libxl__save_helper_state *shs = CONTAINER_OF(abrt, *shs, abrt);
268 STATE_AO_GC(shs->ao);
269
270 if (!libxl__save_helper_inuse(shs)) {
271 helper_failed(egc, shs, rc);
272 return;
273 }
274
275 if (!shs->rc)
276 shs->rc = rc;
277
278 libxl__kill(gc, shs->child.pid, SIGTERM, "save/restore helper");
279 }
280
libxl__save_helper_abort(libxl__egc * egc,libxl__save_helper_state * shs)281 void libxl__save_helper_abort(libxl__egc *egc,
282 libxl__save_helper_state *shs)
283 {
284 helper_stop(egc, &shs->abrt, ERROR_FAIL);
285 }
286
helper_stdout_readable(libxl__egc * egc,libxl__ev_fd * ev,int fd,short events,short revents)287 static void helper_stdout_readable(libxl__egc *egc, libxl__ev_fd *ev,
288 int fd, short events, short revents)
289 {
290 libxl__save_helper_state *shs = CONTAINER_OF(ev, *shs, readable);
291 STATE_AO_GC(shs->ao);
292 int rc, errnoval;
293
294 if (revents & (POLLERR|POLLPRI)) {
295 LOGD(ERROR, shs->domid, "%s signaled POLLERR|POLLPRI (%#x)",
296 shs->stdout_what, revents);
297 rc = ERROR_FAIL;
298 out:
299 /* this is here because otherwise we bypass the decl of msg[] */
300 helper_failed(egc, shs, rc);
301 return;
302 }
303
304 uint16_t msglen;
305 errnoval = libxl_read_exactly(CTX, fd, &msglen, sizeof(msglen),
306 shs->stdout_what, "ipc msg header");
307 if (errnoval) { rc = ERROR_FAIL; goto out; }
308
309 unsigned char msg[msglen];
310 errnoval = libxl_read_exactly(CTX, fd, msg, msglen,
311 shs->stdout_what, "ipc msg body");
312 if (errnoval) { rc = ERROR_FAIL; goto out; }
313
314 shs->egc = egc;
315 shs->recv_callback(msg, msglen, shs);
316 shs->egc = 0;
317 return;
318 }
319
helper_exited(libxl__egc * egc,libxl__ev_child * ch,pid_t pid,int status)320 static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
321 pid_t pid, int status)
322 {
323 libxl__save_helper_state *shs = CONTAINER_OF(ch, *shs, child);
324 STATE_AO_GC(shs->ao);
325
326 /* Convenience aliases */
327 const uint32_t domid = shs->domid;
328
329 const char *what =
330 GCSPRINTF("domain %"PRIu32" save/restore helper", domid);
331
332 if (status) {
333 libxl_report_child_exitstatus(CTX, XTL_ERROR, what, pid, status);
334 if (!shs->rc)
335 shs->rc = ERROR_FAIL;
336 }
337
338 if (shs->need_results) {
339 if (!shs->rc) {
340 LOGD(ERROR,shs->domid,"%s exited without providing results",what);
341 shs->rc = ERROR_FAIL;
342 }
343 }
344
345 if (!shs->completed) {
346 if (!shs->rc) {
347 LOGD(ERROR,shs->domid,"%s exited without signaling completion",what);
348 shs->rc = ERROR_FAIL;
349 }
350 }
351
352 helper_done(egc, shs);
353 return;
354 }
355
helper_done(libxl__egc * egc,libxl__save_helper_state * shs)356 static void helper_done(libxl__egc *egc, libxl__save_helper_state *shs)
357 {
358 STATE_AO_GC(shs->ao);
359
360 libxl__ao_abortable_deregister(&shs->abrt);
361 libxl__ev_fd_deregister(gc, &shs->readable);
362 libxl__carefd_close(shs->pipes[0]); shs->pipes[0] = 0;
363 libxl__carefd_close(shs->pipes[1]); shs->pipes[1] = 0;
364 assert(!libxl__save_helper_inuse(shs));
365
366 shs->egc = egc;
367 shs->completion_callback(egc, shs->caller_state,
368 shs->rc, shs->retval, shs->errnoval);
369 shs->egc = 0;
370 }
371
372 /*----- generic helpers for the autogenerated code -----*/
373
374 const libxl__srm_save_autogen_callbacks*
libxl__srm_callout_get_callbacks_save(void * user)375 libxl__srm_callout_get_callbacks_save(void *user)
376 {
377 libxl__save_helper_state *shs = user;
378 return &shs->callbacks.save.a;
379 }
380
381 const libxl__srm_restore_autogen_callbacks*
libxl__srm_callout_get_callbacks_restore(void * user)382 libxl__srm_callout_get_callbacks_restore(void *user)
383 {
384 libxl__save_helper_state *shs = user;
385 return &shs->callbacks.restore.a;
386 }
387
libxl__srm_callout_sendreply(int r,void * user)388 void libxl__srm_callout_sendreply(int r, void *user)
389 {
390 libxl__save_helper_state *shs = user;
391 libxl__egc *egc = shs->egc;
392 STATE_AO_GC(shs->ao);
393 int errnoval;
394
395 errnoval = libxl_write_exactly(CTX, libxl__carefd_fd(shs->pipes[0]),
396 &r, sizeof(r), shs->stdin_what,
397 "callback return value");
398 if (errnoval)
399 helper_failed(egc, shs, ERROR_FAIL);
400 }
401
libxl__srm_callout_callback_log(uint32_t level,uint32_t errnoval,const char * context,const char * formatted,void * user)402 void libxl__srm_callout_callback_log(uint32_t level, uint32_t errnoval,
403 const char *context, const char *formatted, void *user)
404 {
405 libxl__save_helper_state *shs = user;
406 STATE_AO_GC(shs->ao);
407 xtl_log(CTX->lg, level, errnoval, context, "%s", formatted);
408 }
409
libxl__srm_callout_callback_progress(const char * context,const char * doing_what,unsigned long done,unsigned long total,void * user)410 void libxl__srm_callout_callback_progress(const char *context,
411 const char *doing_what, unsigned long done,
412 unsigned long total, void *user)
413 {
414 libxl__save_helper_state *shs = user;
415 STATE_AO_GC(shs->ao);
416 xtl_progress(CTX->lg, context, doing_what, done, total);
417 }
418
libxl__srm_callout_callback_complete(int retval,int errnoval,void * user)419 int libxl__srm_callout_callback_complete(int retval, int errnoval,
420 void *user)
421 {
422 libxl__save_helper_state *shs = user;
423 STATE_AO_GC(shs->ao);
424
425 shs->completed = 1;
426 shs->retval = retval;
427 shs->errnoval = errnoval;
428 libxl__ev_fd_deregister(gc, &shs->readable);
429 return 0;
430 }
431