1 /**
2  * Copyright (C) 2018-2022 Intel Corporation.
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <time.h>
9 #include <fcntl.h>
10 #include <string.h>
11 #include <errno.h>
12 #include <limits.h>
13 #include <pthread.h>
14 #include <dirent.h>
15 #include <sys/stat.h>
16 #include "acrnctl.h"
17 #include "acrn_mngr.h"
18 #include "mevent.h"
19 #include "pm.h"
20 
21 const char *state_str[] = {
22 	[VM_STATE_UNKNOWN] = "unknown",
23 	[VM_CREATED] = "stopped",
24 	[VM_STARTED] = "started",
25 	[VM_SUSPENDED] = "suspended",
26 	[VM_UNTRACKED] = "untracked",
27 };
28 
29 /* List head of all vm */
30 static pthread_mutex_t vmmngr_mutex = PTHREAD_MUTEX_INITIALIZER;
31 struct vmmngr_list_struct vmmngr_head = { NULL };
32 static unsigned long update_count = 0;
33 
vmmngr_find(const char * name)34 struct vmmngr_struct *vmmngr_find(const char *name)
35 {
36 	struct vmmngr_struct *s;
37 
38 	LIST_FOREACH(s, &vmmngr_head, list)
39 	    if (!strcmp(name, s->name))
40 		return s;
41 	return NULL;
42 }
43 
44 static int send_msg(const char *vmname, struct mngr_msg *req, struct mngr_msg *ack);
45 
query_state(const char * name)46 static int query_state(const char *name)
47 {
48 	struct mngr_msg req;
49 	struct mngr_msg ack;
50 	int ret;
51 
52 	req.magic = MNGR_MSG_MAGIC;
53 	req.msgid = DM_QUERY;
54 	req.timestamp = time(NULL);
55 
56 	ret = send_msg(name, &req, &ack);
57 	if (ret) {
58 		printf("%s: Error to quary %s state, err: %d\n", __func__, name, ret);
59 		return ret;
60 	}
61 
62 	if (ack.data.state < 0) {
63 		fprintf(stderr, "%s ack.data.state:%d\n", __FUNCTION__, ack.data.state);
64 	}
65 
66 	return ack.data.state;
67 }
68 
69 /*
70  * get vmname and pid from /run/acrn/mngr/[vmname].monitor.[pid].socket
71  */
_get_vmname_pid(const char * src,char * p_vmname,int max_len_vmname,int * pid)72 static inline int _get_vmname_pid(const char *src, char *p_vmname,
73 		int max_len_vmname, int *pid)
74 {
75 	char *p = NULL;
76 	long val64;
77 
78 	p = strchr(src, '.');
79 	/* p - src: length of the substring "vmname" in the sting "src" */
80 	if (!p || p - src == 0 || p - src >= max_len_vmname)
81 		return -1;
82 	else
83 		strncpy(p_vmname, src, p - src);
84 
85 	/* move the pointer to the "pid" in the string "src" */
86 	if (strncmp(".monitor.", p, strlen(".monitor.")))
87 		return -1;
88 	else
89 		p = p + strlen(".monitor.");
90 
91 	val64 = strtol(p, NULL, 10);
92 	if ((errno == ERANGE && (val64 == LONG_MAX || val64 == LONG_MIN))
93 			|| (errno != 0 && val64 == 0))
94 		return -1;
95 
96 	*pid = (int)val64;
97 
98 	p = strchr(p, '.');
99 	if (!p || strncmp(".socket", p, strlen(".socket")))
100 		return -1;
101 
102 	return 0;
103 }
104 
105 /* find all the running DM process, which has */
106 /* /run/acrn/mngr/[vmname].monitor.[pid].socket */
_scan_alive_vm(void)107 static void _scan_alive_vm(void)
108 {
109 	DIR *dir;
110 	struct dirent *entry;
111 	struct vmmngr_struct *vm;
112 	char name[PATH_LEN];
113 	int pid;
114 	int ret;
115 
116 	ret = check_dir(ACRN_DM_SOCK_PATH, CHK_ONLY);
117 	if (ret) {
118 		printf("%s: Failed to check directory %s, err: %d\n\
119 			Make sure the acrnd daemon is running ('systemctl status acrnd').\n",
120 			__func__, ACRN_DM_SOCK_PATH, ret);
121 		return;
122 	}
123 
124 	dir = opendir(ACRN_DM_SOCK_PATH);
125 	if (!dir) {
126 		printf("%s: Failed to open directory %s\n", __func__, ACRN_DM_SOCK_PATH);
127 		return;
128 	}
129 
130 	while ((entry = readdir(dir))) {
131 		memset(name, 0, sizeof(name));
132 		ret = _get_vmname_pid(entry->d_name, name, sizeof(name), &pid);
133 		if (ret < 0)
134 			continue;
135 
136 		if (name[sizeof(name) - 1]) {
137 			/* truncate name and go a head */
138 			name[sizeof(name) - 1] = 0;
139 			printf("%s: Truncate name as %s\n", __func__, name);
140 		}
141 
142 		vm = vmmngr_find(name);
143 
144 		if (!vm) {
145 			vm = calloc(1, sizeof(*vm));
146 			if (!vm) {
147 				printf("%s: Failed to alloc mem for %s\n", __func__, name);
148 				continue;
149 			}
150 			memcpy(vm->name, name, sizeof(vm->name) - 1);
151 			LIST_INSERT_HEAD(&vmmngr_head, vm, list);
152 		}
153 
154 		ret = query_state(name);
155 
156 		if (ret < 0)
157 			/* unsupport query */
158 			vm->state_tmp = VM_STARTED;
159 		else
160 			switch (ret) {
161 			case VM_SUSPEND_NONE:
162 				vm->state_tmp = VM_STARTED;
163 				break;
164 			case VM_SUSPEND_SUSPEND:
165 				vm->state_tmp = VM_SUSPENDED;
166 				break;
167 			default:
168 				fprintf(stderr, "Warnning: unknow vm state:0x%lx\n",
169 										vm->state);
170 				vm->state_tmp = VM_STATE_UNKNOWN;
171 			}
172 		vm->update = update_count;
173 	}
174 
175 	closedir(dir);
176 }
177 
178 /*
179  * get vmname and suffix from src,
180  * which has [vmname].[suffix]
181  */
_get_vmname_suffix(const char * src,char * name,int max_len_name,char * suffix,int max_len_suffix)182 static inline int _get_vmname_suffix(const char *src,
183 		char *name, int max_len_name, char *suffix, int max_len_suffix)
184 {
185 	char *p = NULL;
186 
187 	p = strchr(src, '.');
188 	/* p - src: length of the substring vmname in the string src*/
189 	if (!p || p - src == 0)
190 		return -1;
191 
192 	strncpy(name, src, p - src);
193 	if (p - src >= max_len_name) {
194 		/* truncate name and go a head */
195 		name[max_len_name - 1] = '\0';
196 		printf("%s: Truncate name as %s\n", __func__, name);
197 	}
198 
199 	strncpy(suffix, p + 1, max_len_suffix);
200 	if (strncmp(suffix, "sh", strlen("sh")))
201 		return -1;
202 
203 	return 0;
204 }
205 
_scan_added_vm(void)206 static void _scan_added_vm(void)
207 {
208 	DIR *dir;
209 	struct dirent *entry;
210 	struct vmmngr_struct *vm;
211 	char name[PATH_LEN];
212 	char suffix[PATH_LEN];
213 	int ret;
214 
215 	ret = check_dir(ACRN_CONF_PATH, CHK_ONLY);
216 	if (ret) {
217 		printf("%s: Failed to check directory %s, err: %d\n", __func__, ACRN_CONF_PATH, ret);
218 		return;
219 	}
220 
221 	ret = check_dir(ACRN_CONF_PATH_ADD, CHK_ONLY);
222 	if (ret) {
223 		printf("%s: Failed to check directory %s, err: %d\n", __func__, ACRN_CONF_PATH_ADD, ret);
224 		return;
225 	}
226 
227 	dir = opendir(ACRN_CONF_PATH_ADD);
228 	if (!dir) {
229 		printf("%s: Failed to open directory %s\n", __func__, ACRN_CONF_PATH_ADD);
230 		return;
231 	}
232 
233 	while ((entry = readdir(dir))) {
234 		ret = strnlen(entry->d_name, sizeof(entry->d_name));
235 		if (ret >= sizeof(name)) {
236 			continue;
237 		}
238 
239 		memset(name, 0, sizeof(name));
240 		memset(suffix, 0, sizeof(suffix));
241 		ret = _get_vmname_suffix(entry->d_name,
242 				name, sizeof(name), suffix, sizeof(suffix));
243 		if (ret < 0)
244 			continue;
245 
246 		vm = vmmngr_find(name);
247 
248 		if (!vm) {
249 			vm = calloc(1, sizeof(*vm));
250 			if (!vm) {
251 				printf("%s: Failed to alloc mem for %s\n", __func__, name);
252 				continue;
253 			}
254 			memcpy(vm->name, name, sizeof(vm->name) - 1);
255 			LIST_INSERT_HEAD(&vmmngr_head, vm, list);
256 		}
257 
258 		vm->state_tmp = VM_CREATED;
259 		vm->update = update_count;
260 	}
261 
262 	closedir(dir);
263 }
264 
_remove_dead_vm(void)265 static void _remove_dead_vm(void)
266 {
267 	struct vmmngr_struct *vm, *tvm;
268 
269 	list_foreach_safe(vm, &vmmngr_head, list, tvm) {
270 		if (vm->update == update_count) {
271 			vm->state = vm->state_tmp;
272 			continue;
273 		}
274 		LIST_REMOVE(vm, list);
275 		printf("%s: Removed dead %s\n", __func__, vm->name);
276 		free(vm);
277 	}
278 };
279 
vmmngr_update(void)280 void vmmngr_update(void)
281 {
282 	pthread_mutex_lock(&vmmngr_mutex);
283 	update_count++;
284 	_scan_added_vm();
285 	_scan_alive_vm();
286 	_remove_dead_vm();
287 	pthread_mutex_unlock(&vmmngr_mutex);
288 }
289 
290 /* helper functions */
shell_cmd(const char * cmd,char * outbuf,int len)291 int shell_cmd(const char *cmd, char *outbuf, int len)
292 {
293 	FILE *ptr;
294 	char cmd_buf[256];
295 	int ret;
296 
297 	if (!outbuf)
298 		return system(cmd);
299 
300 	memset(cmd_buf, 0, sizeof(cmd_buf));
301 	memset(outbuf, 0, len);
302 	if (snprintf(cmd_buf, sizeof(cmd_buf), "%s 2>&1", cmd) >= sizeof(cmd_buf)) {
303 		printf("ERROR: shell command is truncated\n");
304 		return -1;
305 	}
306 	ptr = popen(cmd_buf, "re");
307 	if (!ptr)
308 		return -1;
309 
310 	ret = fread(outbuf, 1, len, ptr);
311 	pclose(ptr);
312 
313 	return ret;
314 }
315 
send_msg(const char * vmname,struct mngr_msg * req,struct mngr_msg * ack)316 static int send_msg(const char *vmname, struct mngr_msg *req,
317 		    struct mngr_msg *ack)
318 {
319 	int fd, ret;
320 
321 	if (!vmname) {
322 		printf("No vmname provided\n");
323 		return -EINVAL;
324 	}
325 
326 	fd = mngr_open_un(vmname, MNGR_CLIENT);
327 	if (fd < 0) {
328 		printf("Unable to open vm %s socket. It may have been shutdown\n", vmname);
329 		return -1;
330 	}
331 
332 	ret = mngr_send_msg(fd, req, ack, 1);
333 	if (ret < 0) {
334 		printf("Unable to send msg to vm %s socket. It may have been shutdown\n", vmname);
335 		mngr_close(fd);
336 		return ret;
337 	}
338 
339 	mngr_close(fd);
340 
341 	return 0;
342 }
343 
list_vm()344 int list_vm()
345 {
346 	struct vmmngr_struct *s;
347 	int find = 0;
348 
349 	LIST_FOREACH(s, &vmmngr_head, list) {
350 		printf("%s\t\t%s\n", s->name, state_str[s->state]);
351 		find++;
352 	}
353 
354 	if (!find)
355 		printf("There are no VMs\n");
356 
357 	return 0;
358 }
359 
start_vm(const char * vmname)360 int start_vm(const char *vmname)
361 {
362 	char cmd[PATH_LEN + sizeof(ACRN_CONF_PATH_ADD) * 2 + MAX_VM_NAME_LEN * 2];
363 
364 	if (snprintf(cmd, sizeof(cmd), "bash %s/%s.sh $(cat %s/%s.args)",
365 			ACRN_CONF_PATH_ADD, vmname, ACRN_CONF_PATH_ADD, vmname) >= sizeof(cmd)) {
366 		printf("ERROR: command is truncated\n");
367 		return -1;
368 	}
369 
370 	return system(cmd);
371 }
372 
stop_vm(const char * vmname,int force)373 int stop_vm(const char *vmname, int force)
374 {
375 	struct mngr_msg req;
376 	struct mngr_msg ack;
377 
378 	req.magic = MNGR_MSG_MAGIC;
379 	req.msgid = DM_STOP;
380 	req.timestamp = time(NULL);
381 	req.data.acrnd_stop.force = force;
382 
383 	send_msg(vmname, &req, &ack);
384 	if (ack.data.err) {
385 		printf("Error happens when try to stop vm. errno(%d)\n",
386 			ack.data.err);
387 	}
388 
389 	return ack.data.err;
390 }
391 
resume_vm(const char * vmname,unsigned reason)392 int resume_vm(const char *vmname, unsigned reason)
393 {
394 	struct mngr_msg req;
395 	struct mngr_msg ack;
396 
397 	req.magic = MNGR_MSG_MAGIC;
398 	req.msgid = DM_RESUME;
399 	req.timestamp = time(NULL);
400 
401 	req.data.reason = reason;
402 
403 	send_msg(vmname, &req, &ack);
404 
405 	if (ack.data.err) {
406 		printf("Unable to resume vm. errno(%d)\n", ack.data.err);
407 	}
408 
409 	return ack.data.err;
410 }
411 
blkrescan_vm(const char * vmname,char * devargs)412 int blkrescan_vm(const char *vmname, char *devargs)
413 {
414 	struct mngr_msg req;
415 	struct mngr_msg ack;
416 
417 	req.magic = MNGR_MSG_MAGIC;
418 	req.msgid = DM_BLKRESCAN;
419 	req.timestamp = time(NULL);
420 	strncpy(req.data.devargs, devargs, PARAM_LEN - 1);
421 	req.data.devargs[PARAM_LEN - 1] = '\0';
422 
423 	send_msg(vmname, &req, &ack);
424 
425 	if (ack.data.err) {
426 		printf("Unable to rescan virtio-blk device in vm. errno(%d)\n", ack.data.err);
427 	}
428 
429 	return ack.data.err;
430 }
431