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