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