1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 /*
4 * Live Update interfaces for Xen Store Daemon.
5 * Copyright (C) 2022 Juergen Gross, SUSE LLC
6 */
7
8 #include <assert.h>
9 #include <ctype.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <syslog.h>
13 #include <time.h>
14
15 #include "talloc.h"
16 #include "core.h"
17 #include "domain.h"
18 #include "lu.h"
19 #include "watch.h"
20
21 #ifndef NO_LIVE_UPDATE
22 struct live_update *lu_status;
23
lu_destroy(void * data)24 static int lu_destroy(void *data)
25 {
26 lu_destroy_arch(data);
27 lu_status = NULL;
28
29 return 0;
30 }
31
lu_begin(struct connection * conn)32 const char *lu_begin(struct connection *conn)
33 {
34 if (lu_status)
35 return "live-update session already active.";
36
37 lu_status = talloc_zero(conn, struct live_update);
38 if (!lu_status)
39 return "Allocation failure.";
40 lu_status->conn = conn;
41 talloc_set_destructor(lu_status, lu_destroy);
42
43 return NULL;
44 }
45
lu_get_connection(void)46 struct connection *lu_get_connection(void)
47 {
48 return lu_status ? lu_status->conn : NULL;
49 }
50
lu_write_response(FILE * fp)51 unsigned int lu_write_response(FILE *fp)
52 {
53 struct xsd_sockmsg msg;
54
55 assert(lu_status);
56
57 msg = lu_status->in->hdr.msg;
58
59 msg.len = sizeof("OK");
60 if (fp && fwrite(&msg, sizeof(msg), 1, fp) != 1)
61 return 0;
62 if (fp && fwrite("OK", msg.len, 1, fp) != 1)
63 return 0;
64
65 return sizeof(msg) + msg.len;
66 }
67
lu_is_pending(void)68 bool lu_is_pending(void)
69 {
70 return lu_status != NULL;
71 }
72
lu_read_state(void)73 void lu_read_state(void)
74 {
75 struct lu_dump_state state = {};
76 struct xs_state_record_header *head;
77 void *ctx = talloc_new(NULL); /* Work context for subfunctions. */
78 struct xs_state_preamble *pre;
79
80 syslog(LOG_INFO, "live-update: read state\n");
81 lu_get_dump_state(&state);
82 if (state.size == 0)
83 barf_perror("No state found after live-update");
84
85 pre = state.buf;
86 if (memcmp(pre->ident, XS_STATE_IDENT, sizeof(pre->ident)) ||
87 pre->version != htobe32(XS_STATE_VERSION) ||
88 pre->flags != XS_STATE_FLAGS)
89 barf("Unknown record identifier");
90 for (head = state.buf + sizeof(*pre);
91 head->type != XS_STATE_TYPE_END &&
92 (void *)head - state.buf < state.size;
93 head = (void *)head + sizeof(*head) + head->length) {
94 switch (head->type) {
95 case XS_STATE_TYPE_GLOBAL:
96 read_state_global(ctx, head + 1);
97 break;
98 case XS_STATE_TYPE_CONN:
99 read_state_connection(ctx, head + 1);
100 break;
101 case XS_STATE_TYPE_WATCH:
102 read_state_watch(ctx, head + 1);
103 break;
104 case XS_STATE_TYPE_TA:
105 xprintf("live-update: ignore transaction record\n");
106 break;
107 case XS_STATE_TYPE_NODE:
108 read_state_node(ctx, head + 1);
109 break;
110 default:
111 xprintf("live-update: unknown state record %08x\n",
112 head->type);
113 break;
114 }
115 }
116
117 lu_close_dump_state(&state);
118
119 talloc_free(ctx);
120
121 /*
122 * We may have missed the VIRQ_DOM_EXC notification and a domain may
123 * have died while we were live-updating. So check all the domains are
124 * still alive.
125 */
126 check_domains();
127 }
128
lu_abort(const void * ctx,struct connection * conn)129 static const char *lu_abort(const void *ctx, struct connection *conn)
130 {
131 syslog(LOG_INFO, "live-update: abort\n");
132
133 if (!lu_status)
134 return "No live-update session active.";
135
136 /* Destructor will do the real abort handling. */
137 talloc_free(lu_status);
138
139 return NULL;
140 }
141
lu_cmdline(const void * ctx,struct connection * conn,const char * cmdline)142 static const char *lu_cmdline(const void *ctx, struct connection *conn,
143 const char *cmdline)
144 {
145 syslog(LOG_INFO, "live-update: cmdline %s\n", cmdline);
146
147 if (!lu_status || lu_status->conn != conn)
148 return "Not in live-update session.";
149
150 lu_status->cmdline = talloc_strdup(lu_status, cmdline);
151 if (!lu_status->cmdline)
152 return "Allocation failure.";
153
154 return NULL;
155 }
156
lu_check_lu_allowed(void)157 static bool lu_check_lu_allowed(void)
158 {
159 struct connection *conn;
160 time_t now = time(NULL);
161 unsigned int ta_total = 0, ta_long = 0;
162
163 list_for_each_entry(conn, &connections, list) {
164 if (conn->ta_start_time) {
165 ta_total++;
166 if (now - conn->ta_start_time >= lu_status->timeout)
167 ta_long++;
168 }
169 }
170
171 /*
172 * Allow LiveUpdate if one of the following conditions is met:
173 * - There is no active transactions
174 * - All transactions are long running (e.g. they have been
175 * active for more than lu_status->timeout sec) and the admin as
176 * requested to force the operation.
177 */
178 return ta_total ? (lu_status->force && ta_long == ta_total) : true;
179 }
180
lu_reject_reason(const void * ctx)181 static const char *lu_reject_reason(const void *ctx)
182 {
183 char *ret = NULL;
184 struct connection *conn;
185 time_t now = time(NULL);
186
187 list_for_each_entry(conn, &connections, list) {
188 unsigned long tdiff = now - conn->ta_start_time;
189
190 if (conn->ta_start_time && (tdiff >= lu_status->timeout)) {
191 ret = talloc_asprintf(ctx, "%s\nDomain %u: %ld s",
192 ret ? : "Domains with long running transactions:",
193 conn->id, tdiff);
194 }
195 }
196
197 return ret ? (const char *)ret : "Overlapping transactions";
198 }
199
lu_dump_state(const void * ctx,struct connection * conn)200 static const char *lu_dump_state(const void *ctx, struct connection *conn)
201 {
202 FILE *fp;
203 const char *ret;
204 struct xs_state_record_header end;
205 struct xs_state_preamble pre;
206
207 fp = lu_dump_open(ctx);
208 if (!fp)
209 return "Dump state open error";
210
211 memcpy(pre.ident, XS_STATE_IDENT, sizeof(pre.ident));
212 pre.version = htobe32(XS_STATE_VERSION);
213 pre.flags = XS_STATE_FLAGS;
214 if (fwrite(&pre, sizeof(pre), 1, fp) != 1) {
215 ret = "Dump write error";
216 goto out;
217 }
218
219 ret = dump_state_global(fp);
220 if (ret)
221 goto out;
222 ret = dump_state_connections(fp);
223 if (ret)
224 goto out;
225 ret = dump_state_nodes(fp, ctx);
226 if (ret)
227 goto out;
228
229 end.type = XS_STATE_TYPE_END;
230 end.length = 0;
231 if (fwrite(&end, sizeof(end), 1, fp) != 1)
232 ret = "Dump write error";
233
234 out:
235 lu_dump_close(fp);
236
237 return ret;
238 }
239
lu_activate_binary(const void * ctx)240 static const char *lu_activate_binary(const void *ctx)
241 {
242 int argc;
243 char **argv;
244 unsigned int i;
245
246 if (lu_status->cmdline) {
247 argc = 4; /* At least one arg + progname + "-U" + NULL. */
248 for (i = 0; lu_status->cmdline[i]; i++)
249 if (isspace(lu_status->cmdline[i]))
250 argc++;
251 argv = talloc_array(ctx, char *, argc);
252 if (!argv)
253 return "Allocation failure.";
254
255 i = 0;
256 argc = 1;
257 argv[1] = strtok(lu_status->cmdline, " \t");
258 while (argv[argc]) {
259 if (!strcmp(argv[argc], "-U"))
260 i = 1;
261 argc++;
262 argv[argc] = strtok(NULL, " \t");
263 }
264
265 if (!i) {
266 argv[argc++] = "-U";
267 argv[argc] = NULL;
268 }
269 } else {
270 for (i = 0; i < orig_argc; i++)
271 if (!strcmp(orig_argv[i], "-U"))
272 break;
273
274 argc = orig_argc;
275 argv = talloc_array(ctx, char *, orig_argc + 2);
276 if (!argv)
277 return "Allocation failure.";
278
279 memcpy(argv, orig_argv, orig_argc * sizeof(*argv));
280 if (i == orig_argc)
281 argv[argc++] = "-U";
282 argv[argc] = NULL;
283 }
284
285 domain_deinit();
286
287 return lu_exec(ctx, argc, argv);
288 }
289
do_lu_start(struct delayed_request * req)290 static bool do_lu_start(struct delayed_request *req)
291 {
292 time_t now = time(NULL);
293 const char *ret;
294 struct buffered_data *saved_in;
295 struct connection *conn = req->data;
296
297 /*
298 * Cancellation may have been requested asynchronously. In this
299 * case, lu_status will be NULL.
300 */
301 if (!lu_status) {
302 ret = "Cancellation was requested";
303 goto out;
304 }
305
306 assert(lu_status->conn == conn);
307
308 if (!lu_check_lu_allowed()) {
309 if (now < lu_status->started_at + lu_status->timeout)
310 return false;
311 if (!lu_status->force) {
312 ret = lu_reject_reason(req);
313 goto out;
314 }
315 }
316
317 assert(req->in == lu_status->in);
318 /* Dump out internal state, including "OK" for live update. */
319 ret = lu_dump_state(req->in, conn);
320 if (!ret) {
321 /* Perform the activation of new binary. */
322 ret = lu_activate_binary(req->in);
323 }
324
325 /* We will reach this point only in case of failure. */
326 out:
327 /*
328 * send_reply() will send the response for conn->in. Save the current
329 * conn->in and restore it afterwards.
330 */
331 saved_in = conn->in;
332 conn->in = req->in;
333 send_reply(conn, XS_CONTROL, ret, strlen(ret) + 1);
334 conn->in = saved_in;
335 talloc_free(lu_status);
336
337 return true;
338 }
339
lu_start(const void * ctx,struct connection * conn,bool force,unsigned int to)340 static const char *lu_start(const void *ctx, struct connection *conn,
341 bool force, unsigned int to)
342 {
343 syslog(LOG_INFO, "live-update: start, force=%d, to=%u\n", force, to);
344
345 if (!lu_status || lu_status->conn != conn)
346 return "Not in live-update session.";
347
348 #ifdef __MINIOS__
349 if (lu_status->kernel_size != lu_status->kernel_off)
350 return "Kernel not complete.";
351 #endif
352
353 lu_status->force = force;
354 lu_status->timeout = to;
355 lu_status->started_at = time(NULL);
356 lu_status->in = conn->in;
357
358 errno = delay_request(conn, conn->in, do_lu_start, conn, false);
359
360 return NULL;
361 }
362
do_control_lu(const void * ctx,struct connection * conn,const char ** vec,int num)363 int do_control_lu(const void *ctx, struct connection *conn, const char **vec,
364 int num)
365 {
366 const char *ret = NULL;
367 unsigned int i;
368 bool force = false;
369 unsigned int to = 0;
370
371 if (num < 1)
372 return EINVAL;
373
374 if (!strcmp(vec[0], "-a")) {
375 if (num == 1)
376 ret = lu_abort(ctx, conn);
377 else
378 return EINVAL;
379 } else if (!strcmp(vec[0], "-c")) {
380 if (num == 2)
381 ret = lu_cmdline(ctx, conn, vec[1]);
382 else
383 return EINVAL;
384 } else if (!strcmp(vec[0], "-s")) {
385 for (i = 1; i < num; i++) {
386 if (!strcmp(vec[i], "-F"))
387 force = true;
388 else if (!strcmp(vec[i], "-t") && i < num - 1) {
389 i++;
390 to = atoi(vec[i]);
391 } else
392 return EINVAL;
393 }
394 ret = lu_start(ctx, conn, force, to);
395 if (!ret)
396 return errno;
397 } else {
398 ret = lu_arch(ctx, conn, vec, num);
399 if (!ret && errno)
400 return errno;
401 }
402
403 if (!ret)
404 ret = "OK";
405 send_reply(conn, XS_CONTROL, ret, strlen(ret) + 1);
406 return 0;
407 }
408 #endif
409