1 /*
2  * Copyright (C) 2014      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 /*
20  * Infrastructure for converting a legacy migration stream into a
21  * libxl v2 stream.
22  *
23  * This is done by fork()ing the python conversion script, which takes
24  * in a legacy stream, and puts out a suitably-formatted v2 stream.
25  */
26 
27 static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
28                           pid_t pid, int status);
29 static void helper_stop(libxl__egc *egc, libxl__ao_abortable *abrt, int rc);
30 static void helper_done(libxl__egc *egc,
31                         libxl__conversion_helper_state *chs);
32 
33 /*----- Entrypoints -----*/
34 
libxl__conversion_helper_init(libxl__conversion_helper_state * chs)35 void libxl__conversion_helper_init(libxl__conversion_helper_state *chs)
36 {
37     assert(chs->ao);
38 
39     chs->v2_carefd = NULL;
40     chs->rc = 0;
41     libxl__ao_abortable_init(&chs->abrt);
42     libxl__ev_child_init(&chs->child);
43 }
44 
libxl__convert_legacy_stream(libxl__egc * egc,libxl__conversion_helper_state * chs)45 int libxl__convert_legacy_stream(libxl__egc *egc,
46                                  libxl__conversion_helper_state *chs)
47 {
48     STATE_AO_GC(chs->ao);
49     libxl__carefd *child_in = NULL, *child_out = NULL;
50     int rc = 0;
51 
52     chs->abrt.ao = chs->ao;
53     chs->abrt.callback = helper_stop;
54     rc = libxl__ao_abortable_register(&chs->abrt);
55     if (rc) goto err;
56 
57     libxl__carefd_begin();
58     int fds[2];
59     if (libxl_pipe(CTX, fds)) {
60         rc = ERROR_FAIL;
61         libxl__carefd_unlock();
62         goto err;
63     }
64     child_out = libxl__carefd_record(CTX, fds[0]);
65     child_in  = libxl__carefd_record(CTX, fds[1]);
66     libxl__carefd_unlock();
67 
68     pid_t pid = libxl__ev_child_fork(gc, &chs->child, helper_exited);
69     if (!pid) {
70         char * const args[] =
71         {
72             getenv("LIBXL_CONVERT_HELPER") ?:
73                 LIBEXEC_BIN "/convert-legacy-stream",
74             "--in",     GCSPRINTF("%d", chs->legacy_fd),
75             "--out",    GCSPRINTF("%d", fds[1]),
76             /*
77              * The width calculation is an assumption for the common
78              * case.  The conversion script needs to know the width of
79              * the toolstack which saved the legacy stream.
80              *
81              * In the overwhelming majority of cases, the width of the
82              * saving toolstack will be the same as our current
83              * width.  To avoid extending the libxl API with a
84              * parameter intended to disappear shortly, this option
85              * has not been exposed to the caller.
86              *
87              * If more complicated conversion is required, the
88              * conversion script can be instantiated manually, which
89              * will bypass all of this conversion logic.
90              */
91             "--width",  sizeof(unsigned long) == 8 ? "64" : "32",
92 
93             "--guest",  chs->hvm ? "hvm" : "pv",
94             "--format", "libxl",
95             /* "--verbose", */
96             NULL,
97         };
98 
99         libxl_fd_set_cloexec(CTX, chs->legacy_fd, 0);
100         libxl_fd_set_cloexec(CTX, libxl__carefd_fd(child_in), 0);
101 
102         libxl__exec(gc,
103                     -1, -1, -1,
104                     args[0], args, NULL);
105     }
106 
107     libxl__carefd_close(child_in);
108     chs->v2_carefd = child_out;
109 
110     assert(!rc);
111     return rc;
112 
113  err:
114     libxl__ao_abortable_deregister(&chs->abrt);
115     assert(rc);
116     return rc;
117 }
118 
libxl__conversion_helper_abort(libxl__egc * egc,libxl__conversion_helper_state * chs,int rc)119 void libxl__conversion_helper_abort(libxl__egc *egc,
120                                     libxl__conversion_helper_state *chs,
121                                     int rc)
122 {
123     STATE_AO_GC(chs->ao);
124     assert(rc);
125 
126     if (libxl__conversion_helper_inuse(chs)) {
127 
128         if (!chs->rc)
129             chs->rc = rc;
130 
131         libxl__kill(gc, chs->child.pid, SIGTERM, "conversion helper");
132     }
133 }
134 
135 /*----- State handling -----*/
136 
helper_stop(libxl__egc * egc,libxl__ao_abortable * abrt,int rc)137 static void helper_stop(libxl__egc *egc, libxl__ao_abortable *abrt, int rc)
138 {
139     libxl__conversion_helper_state *chs = CONTAINER_OF(abrt, *chs, abrt);
140     STATE_AO_GC(chs->ao);
141 
142     libxl__conversion_helper_abort(egc, chs, rc);
143 }
144 
helper_exited(libxl__egc * egc,libxl__ev_child * ch,pid_t pid,int status)145 static void helper_exited(libxl__egc *egc, libxl__ev_child *ch,
146                           pid_t pid, int status)
147 {
148     libxl__conversion_helper_state *chs = CONTAINER_OF(ch, *chs, child);
149     STATE_AO_GC(chs->ao);
150 
151     if (status) {
152         libxl_report_child_exitstatus(
153             CTX, chs->rc ? XTL_DEBUG : XTL_ERROR,
154             "conversion helper", pid, status);
155 
156         if (!chs->rc)
157             chs->rc = ERROR_FAIL;
158     }
159 
160     helper_done(egc, chs);
161 }
162 
helper_done(libxl__egc * egc,libxl__conversion_helper_state * chs)163 static void helper_done(libxl__egc *egc,
164                         libxl__conversion_helper_state *chs)
165 {
166     STATE_AO_GC(chs->ao);
167 
168     assert(!libxl__conversion_helper_inuse(chs));
169 
170     libxl__ao_abortable_deregister(&chs->abrt);
171 
172     chs->completion_callback(egc, chs, chs->rc);
173 }
174