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