]> Git Repo - qemu.git/blame_incremental - qga/commands-posix.c
qga: linux: report disk serial number
[qemu.git] / qga / commands-posix.c
... / ...
CommitLineData
1/*
2 * QEMU Guest Agent POSIX-specific command implementations
3 *
4 * Copyright IBM Corp. 2011
5 *
6 * Authors:
7 * Michael Roth <[email protected]>
8 * Michal Privoznik <[email protected]>
9 *
10 * This work is licensed under the terms of the GNU GPL, version 2 or later.
11 * See the COPYING file in the top-level directory.
12 */
13
14#include "qemu/osdep.h"
15#include <sys/ioctl.h>
16#include <sys/utsname.h>
17#include <sys/wait.h>
18#include <dirent.h>
19#include "guest-agent-core.h"
20#include "qga-qapi-commands.h"
21#include "qapi/error.h"
22#include "qapi/qmp/qerror.h"
23#include "qemu/queue.h"
24#include "qemu/host-utils.h"
25#include "qemu/sockets.h"
26#include "qemu/base64.h"
27#include "qemu/cutils.h"
28
29#ifdef HAVE_UTMPX
30#include <utmpx.h>
31#endif
32
33#ifndef CONFIG_HAS_ENVIRON
34#ifdef __APPLE__
35#include <crt_externs.h>
36#define environ (*_NSGetEnviron())
37#else
38extern char **environ;
39#endif
40#endif
41
42#if defined(__linux__)
43#include <mntent.h>
44#include <linux/fs.h>
45#include <ifaddrs.h>
46#include <arpa/inet.h>
47#include <sys/socket.h>
48#include <net/if.h>
49#include <sys/statvfs.h>
50
51#ifdef CONFIG_LIBUDEV
52#include <libudev.h>
53#endif
54
55#ifdef FIFREEZE
56#define CONFIG_FSFREEZE
57#endif
58#ifdef FITRIM
59#define CONFIG_FSTRIM
60#endif
61#endif
62
63static void ga_wait_child(pid_t pid, int *status, Error **errp)
64{
65 pid_t rpid;
66
67 *status = 0;
68
69 do {
70 rpid = waitpid(pid, status, 0);
71 } while (rpid == -1 && errno == EINTR);
72
73 if (rpid == -1) {
74 error_setg_errno(errp, errno, "failed to wait for child (pid: %d)",
75 pid);
76 return;
77 }
78
79 g_assert(rpid == pid);
80}
81
82void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp)
83{
84 const char *shutdown_flag;
85 Error *local_err = NULL;
86 pid_t pid;
87 int status;
88
89 slog("guest-shutdown called, mode: %s", mode);
90 if (!has_mode || strcmp(mode, "powerdown") == 0) {
91 shutdown_flag = "-P";
92 } else if (strcmp(mode, "halt") == 0) {
93 shutdown_flag = "-H";
94 } else if (strcmp(mode, "reboot") == 0) {
95 shutdown_flag = "-r";
96 } else {
97 error_setg(errp,
98 "mode is invalid (valid values are: halt|powerdown|reboot");
99 return;
100 }
101
102 pid = fork();
103 if (pid == 0) {
104 /* child, start the shutdown */
105 setsid();
106 reopen_fd_to_null(0);
107 reopen_fd_to_null(1);
108 reopen_fd_to_null(2);
109
110 execle("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0",
111 "hypervisor initiated shutdown", (char*)NULL, environ);
112 _exit(EXIT_FAILURE);
113 } else if (pid < 0) {
114 error_setg_errno(errp, errno, "failed to create child process");
115 return;
116 }
117
118 ga_wait_child(pid, &status, &local_err);
119 if (local_err) {
120 error_propagate(errp, local_err);
121 return;
122 }
123
124 if (!WIFEXITED(status)) {
125 error_setg(errp, "child process has terminated abnormally");
126 return;
127 }
128
129 if (WEXITSTATUS(status)) {
130 error_setg(errp, "child process has failed to shutdown");
131 return;
132 }
133
134 /* succeeded */
135}
136
137int64_t qmp_guest_get_time(Error **errp)
138{
139 int ret;
140 qemu_timeval tq;
141
142 ret = qemu_gettimeofday(&tq);
143 if (ret < 0) {
144 error_setg_errno(errp, errno, "Failed to get time");
145 return -1;
146 }
147
148 return tq.tv_sec * 1000000000LL + tq.tv_usec * 1000;
149}
150
151void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
152{
153 int ret;
154 int status;
155 pid_t pid;
156 Error *local_err = NULL;
157 struct timeval tv;
158
159 /* If user has passed a time, validate and set it. */
160 if (has_time) {
161 GDate date = { 0, };
162
163 /* year-2038 will overflow in case time_t is 32bit */
164 if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) {
165 error_setg(errp, "Time %" PRId64 " is too large", time_ns);
166 return;
167 }
168
169 tv.tv_sec = time_ns / 1000000000;
170 tv.tv_usec = (time_ns % 1000000000) / 1000;
171 g_date_set_time_t(&date, tv.tv_sec);
172 if (date.year < 1970 || date.year >= 2070) {
173 error_setg_errno(errp, errno, "Invalid time");
174 return;
175 }
176
177 ret = settimeofday(&tv, NULL);
178 if (ret < 0) {
179 error_setg_errno(errp, errno, "Failed to set time to guest");
180 return;
181 }
182 }
183
184 /* Now, if user has passed a time to set and the system time is set, we
185 * just need to synchronize the hardware clock. However, if no time was
186 * passed, user is requesting the opposite: set the system time from the
187 * hardware clock (RTC). */
188 pid = fork();
189 if (pid == 0) {
190 setsid();
191 reopen_fd_to_null(0);
192 reopen_fd_to_null(1);
193 reopen_fd_to_null(2);
194
195 /* Use '/sbin/hwclock -w' to set RTC from the system time,
196 * or '/sbin/hwclock -s' to set the system time from RTC. */
197 execle("/sbin/hwclock", "hwclock", has_time ? "-w" : "-s",
198 NULL, environ);
199 _exit(EXIT_FAILURE);
200 } else if (pid < 0) {
201 error_setg_errno(errp, errno, "failed to create child process");
202 return;
203 }
204
205 ga_wait_child(pid, &status, &local_err);
206 if (local_err) {
207 error_propagate(errp, local_err);
208 return;
209 }
210
211 if (!WIFEXITED(status)) {
212 error_setg(errp, "child process has terminated abnormally");
213 return;
214 }
215
216 if (WEXITSTATUS(status)) {
217 error_setg(errp, "hwclock failed to set hardware clock to system time");
218 return;
219 }
220}
221
222typedef enum {
223 RW_STATE_NEW,
224 RW_STATE_READING,
225 RW_STATE_WRITING,
226} RwState;
227
228typedef struct GuestFileHandle {
229 uint64_t id;
230 FILE *fh;
231 RwState state;
232 QTAILQ_ENTRY(GuestFileHandle) next;
233} GuestFileHandle;
234
235static struct {
236 QTAILQ_HEAD(, GuestFileHandle) filehandles;
237} guest_file_state = {
238 .filehandles = QTAILQ_HEAD_INITIALIZER(guest_file_state.filehandles),
239};
240
241static int64_t guest_file_handle_add(FILE *fh, Error **errp)
242{
243 GuestFileHandle *gfh;
244 int64_t handle;
245
246 handle = ga_get_fd_handle(ga_state, errp);
247 if (handle < 0) {
248 return -1;
249 }
250
251 gfh = g_new0(GuestFileHandle, 1);
252 gfh->id = handle;
253 gfh->fh = fh;
254 QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
255
256 return handle;
257}
258
259static GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp)
260{
261 GuestFileHandle *gfh;
262
263 QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next)
264 {
265 if (gfh->id == id) {
266 return gfh;
267 }
268 }
269
270 error_setg(errp, "handle '%" PRId64 "' has not been found", id);
271 return NULL;
272}
273
274typedef const char * const ccpc;
275
276#ifndef O_BINARY
277#define O_BINARY 0
278#endif
279
280/* http://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html */
281static const struct {
282 ccpc *forms;
283 int oflag_base;
284} guest_file_open_modes[] = {
285 { (ccpc[]){ "r", NULL }, O_RDONLY },
286 { (ccpc[]){ "rb", NULL }, O_RDONLY | O_BINARY },
287 { (ccpc[]){ "w", NULL }, O_WRONLY | O_CREAT | O_TRUNC },
288 { (ccpc[]){ "wb", NULL }, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY },
289 { (ccpc[]){ "a", NULL }, O_WRONLY | O_CREAT | O_APPEND },
290 { (ccpc[]){ "ab", NULL }, O_WRONLY | O_CREAT | O_APPEND | O_BINARY },
291 { (ccpc[]){ "r+", NULL }, O_RDWR },
292 { (ccpc[]){ "rb+", "r+b", NULL }, O_RDWR | O_BINARY },
293 { (ccpc[]){ "w+", NULL }, O_RDWR | O_CREAT | O_TRUNC },
294 { (ccpc[]){ "wb+", "w+b", NULL }, O_RDWR | O_CREAT | O_TRUNC | O_BINARY },
295 { (ccpc[]){ "a+", NULL }, O_RDWR | O_CREAT | O_APPEND },
296 { (ccpc[]){ "ab+", "a+b", NULL }, O_RDWR | O_CREAT | O_APPEND | O_BINARY }
297};
298
299static int
300find_open_flag(const char *mode_str, Error **errp)
301{
302 unsigned mode;
303
304 for (mode = 0; mode < ARRAY_SIZE(guest_file_open_modes); ++mode) {
305 ccpc *form;
306
307 form = guest_file_open_modes[mode].forms;
308 while (*form != NULL && strcmp(*form, mode_str) != 0) {
309 ++form;
310 }
311 if (*form != NULL) {
312 break;
313 }
314 }
315
316 if (mode == ARRAY_SIZE(guest_file_open_modes)) {
317 error_setg(errp, "invalid file open mode '%s'", mode_str);
318 return -1;
319 }
320 return guest_file_open_modes[mode].oflag_base | O_NOCTTY | O_NONBLOCK;
321}
322
323#define DEFAULT_NEW_FILE_MODE (S_IRUSR | S_IWUSR | \
324 S_IRGRP | S_IWGRP | \
325 S_IROTH | S_IWOTH)
326
327static FILE *
328safe_open_or_create(const char *path, const char *mode, Error **errp)
329{
330 Error *local_err = NULL;
331 int oflag;
332
333 oflag = find_open_flag(mode, &local_err);
334 if (local_err == NULL) {
335 int fd;
336
337 /* If the caller wants / allows creation of a new file, we implement it
338 * with a two step process: open() + (open() / fchmod()).
339 *
340 * First we insist on creating the file exclusively as a new file. If
341 * that succeeds, we're free to set any file-mode bits on it. (The
342 * motivation is that we want to set those file-mode bits independently
343 * of the current umask.)
344 *
345 * If the exclusive creation fails because the file already exists
346 * (EEXIST is not possible for any other reason), we just attempt to
347 * open the file, but in this case we won't be allowed to change the
348 * file-mode bits on the preexistent file.
349 *
350 * The pathname should never disappear between the two open()s in
351 * practice. If it happens, then someone very likely tried to race us.
352 * In this case just go ahead and report the ENOENT from the second
353 * open() to the caller.
354 *
355 * If the caller wants to open a preexistent file, then the first
356 * open() is decisive and its third argument is ignored, and the second
357 * open() and the fchmod() are never called.
358 */
359 fd = open(path, oflag | ((oflag & O_CREAT) ? O_EXCL : 0), 0);
360 if (fd == -1 && errno == EEXIST) {
361 oflag &= ~(unsigned)O_CREAT;
362 fd = open(path, oflag);
363 }
364
365 if (fd == -1) {
366 error_setg_errno(&local_err, errno, "failed to open file '%s' "
367 "(mode: '%s')", path, mode);
368 } else {
369 qemu_set_cloexec(fd);
370
371 if ((oflag & O_CREAT) && fchmod(fd, DEFAULT_NEW_FILE_MODE) == -1) {
372 error_setg_errno(&local_err, errno, "failed to set permission "
373 "0%03o on new file '%s' (mode: '%s')",
374 (unsigned)DEFAULT_NEW_FILE_MODE, path, mode);
375 } else {
376 FILE *f;
377
378 f = fdopen(fd, mode);
379 if (f == NULL) {
380 error_setg_errno(&local_err, errno, "failed to associate "
381 "stdio stream with file descriptor %d, "
382 "file '%s' (mode: '%s')", fd, path, mode);
383 } else {
384 return f;
385 }
386 }
387
388 close(fd);
389 if (oflag & O_CREAT) {
390 unlink(path);
391 }
392 }
393 }
394
395 error_propagate(errp, local_err);
396 return NULL;
397}
398
399int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode,
400 Error **errp)
401{
402 FILE *fh;
403 Error *local_err = NULL;
404 int64_t handle;
405
406 if (!has_mode) {
407 mode = "r";
408 }
409 slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
410 fh = safe_open_or_create(path, mode, &local_err);
411 if (local_err != NULL) {
412 error_propagate(errp, local_err);
413 return -1;
414 }
415
416 /* set fd non-blocking to avoid common use cases (like reading from a
417 * named pipe) from hanging the agent
418 */
419 qemu_set_nonblock(fileno(fh));
420
421 handle = guest_file_handle_add(fh, errp);
422 if (handle < 0) {
423 fclose(fh);
424 return -1;
425 }
426
427 slog("guest-file-open, handle: %" PRId64, handle);
428 return handle;
429}
430
431void qmp_guest_file_close(int64_t handle, Error **errp)
432{
433 GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
434 int ret;
435
436 slog("guest-file-close called, handle: %" PRId64, handle);
437 if (!gfh) {
438 return;
439 }
440
441 ret = fclose(gfh->fh);
442 if (ret == EOF) {
443 error_setg_errno(errp, errno, "failed to close handle");
444 return;
445 }
446
447 QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
448 g_free(gfh);
449}
450
451struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
452 int64_t count, Error **errp)
453{
454 GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
455 GuestFileRead *read_data = NULL;
456 guchar *buf;
457 FILE *fh;
458 size_t read_count;
459
460 if (!gfh) {
461 return NULL;
462 }
463
464 if (!has_count) {
465 count = QGA_READ_COUNT_DEFAULT;
466 } else if (count < 0 || count >= UINT32_MAX) {
467 error_setg(errp, "value '%" PRId64 "' is invalid for argument count",
468 count);
469 return NULL;
470 }
471
472 fh = gfh->fh;
473
474 /* explicitly flush when switching from writing to reading */
475 if (gfh->state == RW_STATE_WRITING) {
476 int ret = fflush(fh);
477 if (ret == EOF) {
478 error_setg_errno(errp, errno, "failed to flush file");
479 return NULL;
480 }
481 gfh->state = RW_STATE_NEW;
482 }
483
484 buf = g_malloc0(count+1);
485 read_count = fread(buf, 1, count, fh);
486 if (ferror(fh)) {
487 error_setg_errno(errp, errno, "failed to read file");
488 slog("guest-file-read failed, handle: %" PRId64, handle);
489 } else {
490 buf[read_count] = 0;
491 read_data = g_new0(GuestFileRead, 1);
492 read_data->count = read_count;
493 read_data->eof = feof(fh);
494 if (read_count) {
495 read_data->buf_b64 = g_base64_encode(buf, read_count);
496 }
497 gfh->state = RW_STATE_READING;
498 }
499 g_free(buf);
500 clearerr(fh);
501
502 return read_data;
503}
504
505GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
506 bool has_count, int64_t count,
507 Error **errp)
508{
509 GuestFileWrite *write_data = NULL;
510 guchar *buf;
511 gsize buf_len;
512 int write_count;
513 GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
514 FILE *fh;
515
516 if (!gfh) {
517 return NULL;
518 }
519
520 fh = gfh->fh;
521
522 if (gfh->state == RW_STATE_READING) {
523 int ret = fseek(fh, 0, SEEK_CUR);
524 if (ret == -1) {
525 error_setg_errno(errp, errno, "failed to seek file");
526 return NULL;
527 }
528 gfh->state = RW_STATE_NEW;
529 }
530
531 buf = qbase64_decode(buf_b64, -1, &buf_len, errp);
532 if (!buf) {
533 return NULL;
534 }
535
536 if (!has_count) {
537 count = buf_len;
538 } else if (count < 0 || count > buf_len) {
539 error_setg(errp, "value '%" PRId64 "' is invalid for argument count",
540 count);
541 g_free(buf);
542 return NULL;
543 }
544
545 write_count = fwrite(buf, 1, count, fh);
546 if (ferror(fh)) {
547 error_setg_errno(errp, errno, "failed to write to file");
548 slog("guest-file-write failed, handle: %" PRId64, handle);
549 } else {
550 write_data = g_new0(GuestFileWrite, 1);
551 write_data->count = write_count;
552 write_data->eof = feof(fh);
553 gfh->state = RW_STATE_WRITING;
554 }
555 g_free(buf);
556 clearerr(fh);
557
558 return write_data;
559}
560
561struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
562 GuestFileWhence *whence_code,
563 Error **errp)
564{
565 GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
566 GuestFileSeek *seek_data = NULL;
567 FILE *fh;
568 int ret;
569 int whence;
570 Error *err = NULL;
571
572 if (!gfh) {
573 return NULL;
574 }
575
576 /* We stupidly exposed 'whence':'int' in our qapi */
577 whence = ga_parse_whence(whence_code, &err);
578 if (err) {
579 error_propagate(errp, err);
580 return NULL;
581 }
582
583 fh = gfh->fh;
584 ret = fseek(fh, offset, whence);
585 if (ret == -1) {
586 error_setg_errno(errp, errno, "failed to seek file");
587 if (errno == ESPIPE) {
588 /* file is non-seekable, stdio shouldn't be buffering anyways */
589 gfh->state = RW_STATE_NEW;
590 }
591 } else {
592 seek_data = g_new0(GuestFileSeek, 1);
593 seek_data->position = ftell(fh);
594 seek_data->eof = feof(fh);
595 gfh->state = RW_STATE_NEW;
596 }
597 clearerr(fh);
598
599 return seek_data;
600}
601
602void qmp_guest_file_flush(int64_t handle, Error **errp)
603{
604 GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
605 FILE *fh;
606 int ret;
607
608 if (!gfh) {
609 return;
610 }
611
612 fh = gfh->fh;
613 ret = fflush(fh);
614 if (ret == EOF) {
615 error_setg_errno(errp, errno, "failed to flush file");
616 } else {
617 gfh->state = RW_STATE_NEW;
618 }
619}
620
621/* linux-specific implementations. avoid this if at all possible. */
622#if defined(__linux__)
623
624#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
625typedef struct FsMount {
626 char *dirname;
627 char *devtype;
628 unsigned int devmajor, devminor;
629 QTAILQ_ENTRY(FsMount) next;
630} FsMount;
631
632typedef QTAILQ_HEAD(FsMountList, FsMount) FsMountList;
633
634static void free_fs_mount_list(FsMountList *mounts)
635{
636 FsMount *mount, *temp;
637
638 if (!mounts) {
639 return;
640 }
641
642 QTAILQ_FOREACH_SAFE(mount, mounts, next, temp) {
643 QTAILQ_REMOVE(mounts, mount, next);
644 g_free(mount->dirname);
645 g_free(mount->devtype);
646 g_free(mount);
647 }
648}
649
650static int dev_major_minor(const char *devpath,
651 unsigned int *devmajor, unsigned int *devminor)
652{
653 struct stat st;
654
655 *devmajor = 0;
656 *devminor = 0;
657
658 if (stat(devpath, &st) < 0) {
659 slog("failed to stat device file '%s': %s", devpath, strerror(errno));
660 return -1;
661 }
662 if (S_ISDIR(st.st_mode)) {
663 /* It is bind mount */
664 return -2;
665 }
666 if (S_ISBLK(st.st_mode)) {
667 *devmajor = major(st.st_rdev);
668 *devminor = minor(st.st_rdev);
669 return 0;
670 }
671 return -1;
672}
673
674/*
675 * Walk the mount table and build a list of local file systems
676 */
677static void build_fs_mount_list_from_mtab(FsMountList *mounts, Error **errp)
678{
679 struct mntent *ment;
680 FsMount *mount;
681 char const *mtab = "/proc/self/mounts";
682 FILE *fp;
683 unsigned int devmajor, devminor;
684
685 fp = setmntent(mtab, "r");
686 if (!fp) {
687 error_setg(errp, "failed to open mtab file: '%s'", mtab);
688 return;
689 }
690
691 while ((ment = getmntent(fp))) {
692 /*
693 * An entry which device name doesn't start with a '/' is
694 * either a dummy file system or a network file system.
695 * Add special handling for smbfs and cifs as is done by
696 * coreutils as well.
697 */
698 if ((ment->mnt_fsname[0] != '/') ||
699 (strcmp(ment->mnt_type, "smbfs") == 0) ||
700 (strcmp(ment->mnt_type, "cifs") == 0)) {
701 continue;
702 }
703 if (dev_major_minor(ment->mnt_fsname, &devmajor, &devminor) == -2) {
704 /* Skip bind mounts */
705 continue;
706 }
707
708 mount = g_new0(FsMount, 1);
709 mount->dirname = g_strdup(ment->mnt_dir);
710 mount->devtype = g_strdup(ment->mnt_type);
711 mount->devmajor = devmajor;
712 mount->devminor = devminor;
713
714 QTAILQ_INSERT_TAIL(mounts, mount, next);
715 }
716
717 endmntent(fp);
718}
719
720static void decode_mntname(char *name, int len)
721{
722 int i, j = 0;
723 for (i = 0; i <= len; i++) {
724 if (name[i] != '\\') {
725 name[j++] = name[i];
726 } else if (name[i + 1] == '\\') {
727 name[j++] = '\\';
728 i++;
729 } else if (name[i + 1] >= '0' && name[i + 1] <= '3' &&
730 name[i + 2] >= '0' && name[i + 2] <= '7' &&
731 name[i + 3] >= '0' && name[i + 3] <= '7') {
732 name[j++] = (name[i + 1] - '0') * 64 +
733 (name[i + 2] - '0') * 8 +
734 (name[i + 3] - '0');
735 i += 3;
736 } else {
737 name[j++] = name[i];
738 }
739 }
740}
741
742static void build_fs_mount_list(FsMountList *mounts, Error **errp)
743{
744 FsMount *mount;
745 char const *mountinfo = "/proc/self/mountinfo";
746 FILE *fp;
747 char *line = NULL, *dash;
748 size_t n;
749 char check;
750 unsigned int devmajor, devminor;
751 int ret, dir_s, dir_e, type_s, type_e, dev_s, dev_e;
752
753 fp = fopen(mountinfo, "r");
754 if (!fp) {
755 build_fs_mount_list_from_mtab(mounts, errp);
756 return;
757 }
758
759 while (getline(&line, &n, fp) != -1) {
760 ret = sscanf(line, "%*u %*u %u:%u %*s %n%*s%n%c",
761 &devmajor, &devminor, &dir_s, &dir_e, &check);
762 if (ret < 3) {
763 continue;
764 }
765 dash = strstr(line + dir_e, " - ");
766 if (!dash) {
767 continue;
768 }
769 ret = sscanf(dash, " - %n%*s%n %n%*s%n%c",
770 &type_s, &type_e, &dev_s, &dev_e, &check);
771 if (ret < 1) {
772 continue;
773 }
774 line[dir_e] = 0;
775 dash[type_e] = 0;
776 dash[dev_e] = 0;
777 decode_mntname(line + dir_s, dir_e - dir_s);
778 decode_mntname(dash + dev_s, dev_e - dev_s);
779 if (devmajor == 0) {
780 /* btrfs reports major number = 0 */
781 if (strcmp("btrfs", dash + type_s) != 0 ||
782 dev_major_minor(dash + dev_s, &devmajor, &devminor) < 0) {
783 continue;
784 }
785 }
786
787 mount = g_new0(FsMount, 1);
788 mount->dirname = g_strdup(line + dir_s);
789 mount->devtype = g_strdup(dash + type_s);
790 mount->devmajor = devmajor;
791 mount->devminor = devminor;
792
793 QTAILQ_INSERT_TAIL(mounts, mount, next);
794 }
795 free(line);
796
797 fclose(fp);
798}
799#endif
800
801#if defined(CONFIG_FSFREEZE)
802
803static char *get_pci_driver(char const *syspath, int pathlen, Error **errp)
804{
805 char *path;
806 char *dpath;
807 char *driver = NULL;
808 char buf[PATH_MAX];
809 ssize_t len;
810
811 path = g_strndup(syspath, pathlen);
812 dpath = g_strdup_printf("%s/driver", path);
813 len = readlink(dpath, buf, sizeof(buf) - 1);
814 if (len != -1) {
815 buf[len] = 0;
816 driver = g_path_get_basename(buf);
817 }
818 g_free(dpath);
819 g_free(path);
820 return driver;
821}
822
823static int compare_uint(const void *_a, const void *_b)
824{
825 unsigned int a = *(unsigned int *)_a;
826 unsigned int b = *(unsigned int *)_b;
827
828 return a < b ? -1 : a > b ? 1 : 0;
829}
830
831/* Walk the specified sysfs and build a sorted list of host or ata numbers */
832static int build_hosts(char const *syspath, char const *host, bool ata,
833 unsigned int *hosts, int hosts_max, Error **errp)
834{
835 char *path;
836 DIR *dir;
837 struct dirent *entry;
838 int i = 0;
839
840 path = g_strndup(syspath, host - syspath);
841 dir = opendir(path);
842 if (!dir) {
843 error_setg_errno(errp, errno, "opendir(\"%s\")", path);
844 g_free(path);
845 return -1;
846 }
847
848 while (i < hosts_max) {
849 entry = readdir(dir);
850 if (!entry) {
851 break;
852 }
853 if (ata && sscanf(entry->d_name, "ata%d", hosts + i) == 1) {
854 ++i;
855 } else if (!ata && sscanf(entry->d_name, "host%d", hosts + i) == 1) {
856 ++i;
857 }
858 }
859
860 qsort(hosts, i, sizeof(hosts[0]), compare_uint);
861
862 g_free(path);
863 closedir(dir);
864 return i;
865}
866
867/* Store disk device info specified by @sysfs into @fs */
868static void build_guest_fsinfo_for_real_device(char const *syspath,
869 GuestFilesystemInfo *fs,
870 Error **errp)
871{
872 unsigned int pci[4], host, hosts[8], tgt[3];
873 int i, nhosts = 0, pcilen;
874 GuestDiskAddress *disk;
875 GuestPCIAddress *pciaddr;
876 GuestDiskAddressList *list = NULL;
877 bool has_ata = false, has_host = false, has_tgt = false;
878 char *p, *q, *driver = NULL;
879#ifdef CONFIG_LIBUDEV
880 struct udev *udev = NULL;
881 struct udev_device *udevice = NULL;
882#endif
883
884 p = strstr(syspath, "/devices/pci");
885 if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n",
886 pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) {
887 g_debug("only pci device is supported: sysfs path '%s'", syspath);
888 return;
889 }
890
891 p += 12 + pcilen;
892 while (true) {
893 driver = get_pci_driver(syspath, p - syspath, errp);
894 if (driver && (g_str_equal(driver, "ata_piix") ||
895 g_str_equal(driver, "sym53c8xx") ||
896 g_str_equal(driver, "virtio-pci") ||
897 g_str_equal(driver, "ahci"))) {
898 break;
899 }
900
901 g_free(driver);
902 if (sscanf(p, "/%x:%x:%x.%x%n",
903 pci, pci + 1, pci + 2, pci + 3, &pcilen) == 4) {
904 p += pcilen;
905 continue;
906 }
907
908 g_debug("unsupported driver or sysfs path '%s'", syspath);
909 return;
910 }
911
912 p = strstr(syspath, "/target");
913 if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u",
914 tgt, tgt + 1, tgt + 2) == 3) {
915 has_tgt = true;
916 }
917
918 p = strstr(syspath, "/ata");
919 if (p) {
920 q = p + 4;
921 has_ata = true;
922 } else {
923 p = strstr(syspath, "/host");
924 q = p + 5;
925 }
926 if (p && sscanf(q, "%u", &host) == 1) {
927 has_host = true;
928 nhosts = build_hosts(syspath, p, has_ata, hosts,
929 ARRAY_SIZE(hosts), errp);
930 if (nhosts < 0) {
931 goto cleanup;
932 }
933 }
934
935 pciaddr = g_malloc0(sizeof(*pciaddr));
936 pciaddr->domain = pci[0];
937 pciaddr->bus = pci[1];
938 pciaddr->slot = pci[2];
939 pciaddr->function = pci[3];
940
941 disk = g_malloc0(sizeof(*disk));
942 disk->pci_controller = pciaddr;
943
944 list = g_malloc0(sizeof(*list));
945 list->value = disk;
946
947#ifdef CONFIG_LIBUDEV
948 udev = udev_new();
949 udevice = udev_device_new_from_syspath(udev, syspath);
950 if (udev == NULL || udevice == NULL) {
951 g_debug("failed to query udev");
952 } else {
953 const char *serial;
954 serial = udev_device_get_property_value(udevice, "ID_SERIAL");
955 if (serial != NULL && *serial != 0) {
956 disk->serial = g_strdup(serial);
957 disk->has_serial = true;
958 }
959 }
960#endif
961
962 if (strcmp(driver, "ata_piix") == 0) {
963 /* a host per ide bus, target*:0:<unit>:0 */
964 if (!has_host || !has_tgt) {
965 g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
966 goto cleanup;
967 }
968 for (i = 0; i < nhosts; i++) {
969 if (host == hosts[i]) {
970 disk->bus_type = GUEST_DISK_BUS_TYPE_IDE;
971 disk->bus = i;
972 disk->unit = tgt[1];
973 break;
974 }
975 }
976 if (i >= nhosts) {
977 g_debug("no host for '%s' (driver '%s')", syspath, driver);
978 goto cleanup;
979 }
980 } else if (strcmp(driver, "sym53c8xx") == 0) {
981 /* scsi(LSI Logic): target*:0:<unit>:0 */
982 if (!has_tgt) {
983 g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
984 goto cleanup;
985 }
986 disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
987 disk->unit = tgt[1];
988 } else if (strcmp(driver, "virtio-pci") == 0) {
989 if (has_tgt) {
990 /* virtio-scsi: target*:0:0:<unit> */
991 disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
992 disk->unit = tgt[2];
993 } else {
994 /* virtio-blk: 1 disk per 1 device */
995 disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO;
996 }
997 } else if (strcmp(driver, "ahci") == 0) {
998 /* ahci: 1 host per 1 unit */
999 if (!has_host || !has_tgt) {
1000 g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
1001 goto cleanup;
1002 }
1003 for (i = 0; i < nhosts; i++) {
1004 if (host == hosts[i]) {
1005 disk->unit = i;
1006 disk->bus_type = GUEST_DISK_BUS_TYPE_SATA;
1007 break;
1008 }
1009 }
1010 if (i >= nhosts) {
1011 g_debug("no host for '%s' (driver '%s')", syspath, driver);
1012 goto cleanup;
1013 }
1014 } else {
1015 g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath);
1016 goto cleanup;
1017 }
1018
1019 list->next = fs->disk;
1020 fs->disk = list;
1021 goto out;
1022
1023cleanup:
1024 if (list) {
1025 qapi_free_GuestDiskAddressList(list);
1026 }
1027out:
1028 g_free(driver);
1029#ifdef CONFIG_LIBUDEV
1030 udev_unref(udev);
1031 udev_device_unref(udevice);
1032#endif
1033 return;
1034}
1035
1036static void build_guest_fsinfo_for_device(char const *devpath,
1037 GuestFilesystemInfo *fs,
1038 Error **errp);
1039
1040/* Store a list of slave devices of virtual volume specified by @syspath into
1041 * @fs */
1042static void build_guest_fsinfo_for_virtual_device(char const *syspath,
1043 GuestFilesystemInfo *fs,
1044 Error **errp)
1045{
1046 DIR *dir;
1047 char *dirpath;
1048 struct dirent *entry;
1049
1050 dirpath = g_strdup_printf("%s/slaves", syspath);
1051 dir = opendir(dirpath);
1052 if (!dir) {
1053 if (errno != ENOENT) {
1054 error_setg_errno(errp, errno, "opendir(\"%s\")", dirpath);
1055 }
1056 g_free(dirpath);
1057 return;
1058 }
1059
1060 for (;;) {
1061 errno = 0;
1062 entry = readdir(dir);
1063 if (entry == NULL) {
1064 if (errno) {
1065 error_setg_errno(errp, errno, "readdir(\"%s\")", dirpath);
1066 }
1067 break;
1068 }
1069
1070 if (entry->d_type == DT_LNK) {
1071 char *path;
1072
1073 g_debug(" slave device '%s'", entry->d_name);
1074 path = g_strdup_printf("%s/slaves/%s", syspath, entry->d_name);
1075 build_guest_fsinfo_for_device(path, fs, errp);
1076 g_free(path);
1077
1078 if (*errp) {
1079 break;
1080 }
1081 }
1082 }
1083
1084 g_free(dirpath);
1085 closedir(dir);
1086}
1087
1088/* Dispatch to functions for virtual/real device */
1089static void build_guest_fsinfo_for_device(char const *devpath,
1090 GuestFilesystemInfo *fs,
1091 Error **errp)
1092{
1093 char *syspath = realpath(devpath, NULL);
1094
1095 if (!syspath) {
1096 error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
1097 return;
1098 }
1099
1100 if (!fs->name) {
1101 fs->name = g_path_get_basename(syspath);
1102 }
1103
1104 g_debug(" parse sysfs path '%s'", syspath);
1105
1106 if (strstr(syspath, "/devices/virtual/block/")) {
1107 build_guest_fsinfo_for_virtual_device(syspath, fs, errp);
1108 } else {
1109 build_guest_fsinfo_for_real_device(syspath, fs, errp);
1110 }
1111
1112 free(syspath);
1113}
1114
1115/* Return a list of the disk device(s)' info which @mount lies on */
1116static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount,
1117 Error **errp)
1118{
1119 GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs));
1120 struct statvfs buf;
1121 unsigned long used, nonroot_total, fr_size;
1122 char *devpath = g_strdup_printf("/sys/dev/block/%u:%u",
1123 mount->devmajor, mount->devminor);
1124
1125 fs->mountpoint = g_strdup(mount->dirname);
1126 fs->type = g_strdup(mount->devtype);
1127 build_guest_fsinfo_for_device(devpath, fs, errp);
1128
1129 if (statvfs(fs->mountpoint, &buf) == 0) {
1130 fr_size = buf.f_frsize;
1131 used = buf.f_blocks - buf.f_bfree;
1132 nonroot_total = used + buf.f_bavail;
1133 fs->used_bytes = used * fr_size;
1134 fs->total_bytes = nonroot_total * fr_size;
1135
1136 fs->has_total_bytes = true;
1137 fs->has_used_bytes = true;
1138 }
1139
1140 g_free(devpath);
1141
1142 return fs;
1143}
1144
1145GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
1146{
1147 FsMountList mounts;
1148 struct FsMount *mount;
1149 GuestFilesystemInfoList *new, *ret = NULL;
1150 Error *local_err = NULL;
1151
1152 QTAILQ_INIT(&mounts);
1153 build_fs_mount_list(&mounts, &local_err);
1154 if (local_err) {
1155 error_propagate(errp, local_err);
1156 return NULL;
1157 }
1158
1159 QTAILQ_FOREACH(mount, &mounts, next) {
1160 g_debug("Building guest fsinfo for '%s'", mount->dirname);
1161
1162 new = g_malloc0(sizeof(*ret));
1163 new->value = build_guest_fsinfo(mount, &local_err);
1164 new->next = ret;
1165 ret = new;
1166 if (local_err) {
1167 error_propagate(errp, local_err);
1168 qapi_free_GuestFilesystemInfoList(ret);
1169 ret = NULL;
1170 break;
1171 }
1172 }
1173
1174 free_fs_mount_list(&mounts);
1175 return ret;
1176}
1177
1178
1179typedef enum {
1180 FSFREEZE_HOOK_THAW = 0,
1181 FSFREEZE_HOOK_FREEZE,
1182} FsfreezeHookArg;
1183
1184static const char *fsfreeze_hook_arg_string[] = {
1185 "thaw",
1186 "freeze",
1187};
1188
1189static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp)
1190{
1191 int status;
1192 pid_t pid;
1193 const char *hook;
1194 const char *arg_str = fsfreeze_hook_arg_string[arg];
1195 Error *local_err = NULL;
1196
1197 hook = ga_fsfreeze_hook(ga_state);
1198 if (!hook) {
1199 return;
1200 }
1201 if (access(hook, X_OK) != 0) {
1202 error_setg_errno(errp, errno, "can't access fsfreeze hook '%s'", hook);
1203 return;
1204 }
1205
1206 slog("executing fsfreeze hook with arg '%s'", arg_str);
1207 pid = fork();
1208 if (pid == 0) {
1209 setsid();
1210 reopen_fd_to_null(0);
1211 reopen_fd_to_null(1);
1212 reopen_fd_to_null(2);
1213
1214 execle(hook, hook, arg_str, NULL, environ);
1215 _exit(EXIT_FAILURE);
1216 } else if (pid < 0) {
1217 error_setg_errno(errp, errno, "failed to create child process");
1218 return;
1219 }
1220
1221 ga_wait_child(pid, &status, &local_err);
1222 if (local_err) {
1223 error_propagate(errp, local_err);
1224 return;
1225 }
1226
1227 if (!WIFEXITED(status)) {
1228 error_setg(errp, "fsfreeze hook has terminated abnormally");
1229 return;
1230 }
1231
1232 status = WEXITSTATUS(status);
1233 if (status) {
1234 error_setg(errp, "fsfreeze hook has failed with status %d", status);
1235 return;
1236 }
1237}
1238
1239/*
1240 * Return status of freeze/thaw
1241 */
1242GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
1243{
1244 if (ga_is_frozen(ga_state)) {
1245 return GUEST_FSFREEZE_STATUS_FROZEN;
1246 }
1247
1248 return GUEST_FSFREEZE_STATUS_THAWED;
1249}
1250
1251int64_t qmp_guest_fsfreeze_freeze(Error **errp)
1252{
1253 return qmp_guest_fsfreeze_freeze_list(false, NULL, errp);
1254}
1255
1256/*
1257 * Walk list of mounted file systems in the guest, and freeze the ones which
1258 * are real local file systems.
1259 */
1260int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
1261 strList *mountpoints,
1262 Error **errp)
1263{
1264 int ret = 0, i = 0;
1265 strList *list;
1266 FsMountList mounts;
1267 struct FsMount *mount;
1268 Error *local_err = NULL;
1269 int fd;
1270
1271 slog("guest-fsfreeze called");
1272
1273 execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err);
1274 if (local_err) {
1275 error_propagate(errp, local_err);
1276 return -1;
1277 }
1278
1279 QTAILQ_INIT(&mounts);
1280 build_fs_mount_list(&mounts, &local_err);
1281 if (local_err) {
1282 error_propagate(errp, local_err);
1283 return -1;
1284 }
1285
1286 /* cannot risk guest agent blocking itself on a write in this state */
1287 ga_set_frozen(ga_state);
1288
1289 QTAILQ_FOREACH_REVERSE(mount, &mounts, FsMountList, next) {
1290 /* To issue fsfreeze in the reverse order of mounts, check if the
1291 * mount is listed in the list here */
1292 if (has_mountpoints) {
1293 for (list = mountpoints; list; list = list->next) {
1294 if (strcmp(list->value, mount->dirname) == 0) {
1295 break;
1296 }
1297 }
1298 if (!list) {
1299 continue;
1300 }
1301 }
1302
1303 fd = qemu_open(mount->dirname, O_RDONLY);
1304 if (fd == -1) {
1305 error_setg_errno(errp, errno, "failed to open %s", mount->dirname);
1306 goto error;
1307 }
1308
1309 /* we try to cull filesystems we know won't work in advance, but other
1310 * filesystems may not implement fsfreeze for less obvious reasons.
1311 * these will report EOPNOTSUPP. we simply ignore these when tallying
1312 * the number of frozen filesystems.
1313 * if a filesystem is mounted more than once (aka bind mount) a
1314 * consecutive attempt to freeze an already frozen filesystem will
1315 * return EBUSY.
1316 *
1317 * any other error means a failure to freeze a filesystem we
1318 * expect to be freezable, so return an error in those cases
1319 * and return system to thawed state.
1320 */
1321 ret = ioctl(fd, FIFREEZE);
1322 if (ret == -1) {
1323 if (errno != EOPNOTSUPP && errno != EBUSY) {
1324 error_setg_errno(errp, errno, "failed to freeze %s",
1325 mount->dirname);
1326 close(fd);
1327 goto error;
1328 }
1329 } else {
1330 i++;
1331 }
1332 close(fd);
1333 }
1334
1335 free_fs_mount_list(&mounts);
1336 /* We may not issue any FIFREEZE here.
1337 * Just unset ga_state here and ready for the next call.
1338 */
1339 if (i == 0) {
1340 ga_unset_frozen(ga_state);
1341 }
1342 return i;
1343
1344error:
1345 free_fs_mount_list(&mounts);
1346 qmp_guest_fsfreeze_thaw(NULL);
1347 return 0;
1348}
1349
1350/*
1351 * Walk list of frozen file systems in the guest, and thaw them.
1352 */
1353int64_t qmp_guest_fsfreeze_thaw(Error **errp)
1354{
1355 int ret;
1356 FsMountList mounts;
1357 FsMount *mount;
1358 int fd, i = 0, logged;
1359 Error *local_err = NULL;
1360
1361 QTAILQ_INIT(&mounts);
1362 build_fs_mount_list(&mounts, &local_err);
1363 if (local_err) {
1364 error_propagate(errp, local_err);
1365 return 0;
1366 }
1367
1368 QTAILQ_FOREACH(mount, &mounts, next) {
1369 logged = false;
1370 fd = qemu_open(mount->dirname, O_RDONLY);
1371 if (fd == -1) {
1372 continue;
1373 }
1374 /* we have no way of knowing whether a filesystem was actually unfrozen
1375 * as a result of a successful call to FITHAW, only that if an error
1376 * was returned the filesystem was *not* unfrozen by that particular
1377 * call.
1378 *
1379 * since multiple preceding FIFREEZEs require multiple calls to FITHAW
1380 * to unfreeze, continuing issuing FITHAW until an error is returned,
1381 * in which case either the filesystem is in an unfreezable state, or,
1382 * more likely, it was thawed previously (and remains so afterward).
1383 *
1384 * also, since the most recent successful call is the one that did
1385 * the actual unfreeze, we can use this to provide an accurate count
1386 * of the number of filesystems unfrozen by guest-fsfreeze-thaw, which
1387 * may * be useful for determining whether a filesystem was unfrozen
1388 * during the freeze/thaw phase by a process other than qemu-ga.
1389 */
1390 do {
1391 ret = ioctl(fd, FITHAW);
1392 if (ret == 0 && !logged) {
1393 i++;
1394 logged = true;
1395 }
1396 } while (ret == 0);
1397 close(fd);
1398 }
1399
1400 ga_unset_frozen(ga_state);
1401 free_fs_mount_list(&mounts);
1402
1403 execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp);
1404
1405 return i;
1406}
1407
1408static void guest_fsfreeze_cleanup(void)
1409{
1410 Error *err = NULL;
1411
1412 if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) {
1413 qmp_guest_fsfreeze_thaw(&err);
1414 if (err) {
1415 slog("failed to clean up frozen filesystems: %s",
1416 error_get_pretty(err));
1417 error_free(err);
1418 }
1419 }
1420}
1421#endif /* CONFIG_FSFREEZE */
1422
1423#if defined(CONFIG_FSTRIM)
1424/*
1425 * Walk list of mounted file systems in the guest, and trim them.
1426 */
1427GuestFilesystemTrimResponse *
1428qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
1429{
1430 GuestFilesystemTrimResponse *response;
1431 GuestFilesystemTrimResultList *list;
1432 GuestFilesystemTrimResult *result;
1433 int ret = 0;
1434 FsMountList mounts;
1435 struct FsMount *mount;
1436 int fd;
1437 Error *local_err = NULL;
1438 struct fstrim_range r;
1439
1440 slog("guest-fstrim called");
1441
1442 QTAILQ_INIT(&mounts);
1443 build_fs_mount_list(&mounts, &local_err);
1444 if (local_err) {
1445 error_propagate(errp, local_err);
1446 return NULL;
1447 }
1448
1449 response = g_malloc0(sizeof(*response));
1450
1451 QTAILQ_FOREACH(mount, &mounts, next) {
1452 result = g_malloc0(sizeof(*result));
1453 result->path = g_strdup(mount->dirname);
1454
1455 list = g_malloc0(sizeof(*list));
1456 list->value = result;
1457 list->next = response->paths;
1458 response->paths = list;
1459
1460 fd = qemu_open(mount->dirname, O_RDONLY);
1461 if (fd == -1) {
1462 result->error = g_strdup_printf("failed to open: %s",
1463 strerror(errno));
1464 result->has_error = true;
1465 continue;
1466 }
1467
1468 /* We try to cull filesystems we know won't work in advance, but other
1469 * filesystems may not implement fstrim for less obvious reasons.
1470 * These will report EOPNOTSUPP; while in some other cases ENOTTY
1471 * will be reported (e.g. CD-ROMs).
1472 * Any other error means an unexpected error.
1473 */
1474 r.start = 0;
1475 r.len = -1;
1476 r.minlen = has_minimum ? minimum : 0;
1477 ret = ioctl(fd, FITRIM, &r);
1478 if (ret == -1) {
1479 result->has_error = true;
1480 if (errno == ENOTTY || errno == EOPNOTSUPP) {
1481 result->error = g_strdup("trim not supported");
1482 } else {
1483 result->error = g_strdup_printf("failed to trim: %s",
1484 strerror(errno));
1485 }
1486 close(fd);
1487 continue;
1488 }
1489
1490 result->has_minimum = true;
1491 result->minimum = r.minlen;
1492 result->has_trimmed = true;
1493 result->trimmed = r.len;
1494 close(fd);
1495 }
1496
1497 free_fs_mount_list(&mounts);
1498 return response;
1499}
1500#endif /* CONFIG_FSTRIM */
1501
1502
1503#define LINUX_SYS_STATE_FILE "/sys/power/state"
1504#define SUSPEND_SUPPORTED 0
1505#define SUSPEND_NOT_SUPPORTED 1
1506
1507typedef enum {
1508 SUSPEND_MODE_DISK = 0,
1509 SUSPEND_MODE_RAM = 1,
1510 SUSPEND_MODE_HYBRID = 2,
1511} SuspendMode;
1512
1513/*
1514 * Executes a command in a child process using g_spawn_sync,
1515 * returning an int >= 0 representing the exit status of the
1516 * process.
1517 *
1518 * If the program wasn't found in path, returns -1.
1519 *
1520 * If a problem happened when creating the child process,
1521 * returns -1 and errp is set.
1522 */
1523static int run_process_child(const char *command[], Error **errp)
1524{
1525 int exit_status, spawn_flag;
1526 GError *g_err = NULL;
1527 bool success;
1528
1529 spawn_flag = G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL |
1530 G_SPAWN_STDERR_TO_DEV_NULL;
1531
1532 success = g_spawn_sync(NULL, (char **)command, environ, spawn_flag,
1533 NULL, NULL, NULL, NULL,
1534 &exit_status, &g_err);
1535
1536 if (success) {
1537 return WEXITSTATUS(exit_status);
1538 }
1539
1540 if (g_err && (g_err->code != G_SPAWN_ERROR_NOENT)) {
1541 error_setg(errp, "failed to create child process, error '%s'",
1542 g_err->message);
1543 }
1544
1545 g_error_free(g_err);
1546 return -1;
1547}
1548
1549static bool systemd_supports_mode(SuspendMode mode, Error **errp)
1550{
1551 Error *local_err = NULL;
1552 const char *systemctl_args[3] = {"systemd-hibernate", "systemd-suspend",
1553 "systemd-hybrid-sleep"};
1554 const char *cmd[4] = {"systemctl", "status", systemctl_args[mode], NULL};
1555 int status;
1556
1557 status = run_process_child(cmd, &local_err);
1558
1559 /*
1560 * systemctl status uses LSB return codes so we can expect
1561 * status > 0 and be ok. To assert if the guest has support
1562 * for the selected suspend mode, status should be < 4. 4 is
1563 * the code for unknown service status, the return value when
1564 * the service does not exist. A common value is status = 3
1565 * (program is not running).
1566 */
1567 if (status > 0 && status < 4) {
1568 return true;
1569 }
1570
1571 if (local_err) {
1572 error_propagate(errp, local_err);
1573 }
1574
1575 return false;
1576}
1577
1578static void systemd_suspend(SuspendMode mode, Error **errp)
1579{
1580 Error *local_err = NULL;
1581 const char *systemctl_args[3] = {"hibernate", "suspend", "hybrid-sleep"};
1582 const char *cmd[3] = {"systemctl", systemctl_args[mode], NULL};
1583 int status;
1584
1585 status = run_process_child(cmd, &local_err);
1586
1587 if (status == 0) {
1588 return;
1589 }
1590
1591 if ((status == -1) && !local_err) {
1592 error_setg(errp, "the helper program 'systemctl %s' was not found",
1593 systemctl_args[mode]);
1594 return;
1595 }
1596
1597 if (local_err) {
1598 error_propagate(errp, local_err);
1599 } else {
1600 error_setg(errp, "the helper program 'systemctl %s' returned an "
1601 "unexpected exit status code (%d)",
1602 systemctl_args[mode], status);
1603 }
1604}
1605
1606static bool pmutils_supports_mode(SuspendMode mode, Error **errp)
1607{
1608 Error *local_err = NULL;
1609 const char *pmutils_args[3] = {"--hibernate", "--suspend",
1610 "--suspend-hybrid"};
1611 const char *cmd[3] = {"pm-is-supported", pmutils_args[mode], NULL};
1612 int status;
1613
1614 status = run_process_child(cmd, &local_err);
1615
1616 if (status == SUSPEND_SUPPORTED) {
1617 return true;
1618 }
1619
1620 if ((status == -1) && !local_err) {
1621 return false;
1622 }
1623
1624 if (local_err) {
1625 error_propagate(errp, local_err);
1626 } else {
1627 error_setg(errp,
1628 "the helper program '%s' returned an unexpected exit"
1629 " status code (%d)", "pm-is-supported", status);
1630 }
1631
1632 return false;
1633}
1634
1635static void pmutils_suspend(SuspendMode mode, Error **errp)
1636{
1637 Error *local_err = NULL;
1638 const char *pmutils_binaries[3] = {"pm-hibernate", "pm-suspend",
1639 "pm-suspend-hybrid"};
1640 const char *cmd[2] = {pmutils_binaries[mode], NULL};
1641 int status;
1642
1643 status = run_process_child(cmd, &local_err);
1644
1645 if (status == 0) {
1646 return;
1647 }
1648
1649 if ((status == -1) && !local_err) {
1650 error_setg(errp, "the helper program '%s' was not found",
1651 pmutils_binaries[mode]);
1652 return;
1653 }
1654
1655 if (local_err) {
1656 error_propagate(errp, local_err);
1657 } else {
1658 error_setg(errp,
1659 "the helper program '%s' returned an unexpected exit"
1660 " status code (%d)", pmutils_binaries[mode], status);
1661 }
1662}
1663
1664static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp)
1665{
1666 const char *sysfile_strs[3] = {"disk", "mem", NULL};
1667 const char *sysfile_str = sysfile_strs[mode];
1668 char buf[32]; /* hopefully big enough */
1669 int fd;
1670 ssize_t ret;
1671
1672 if (!sysfile_str) {
1673 error_setg(errp, "unknown guest suspend mode");
1674 return false;
1675 }
1676
1677 fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
1678 if (fd < 0) {
1679 return false;
1680 }
1681
1682 ret = read(fd, buf, sizeof(buf) - 1);
1683 close(fd);
1684 if (ret <= 0) {
1685 return false;
1686 }
1687 buf[ret] = '\0';
1688
1689 if (strstr(buf, sysfile_str)) {
1690 return true;
1691 }
1692 return false;
1693}
1694
1695static void linux_sys_state_suspend(SuspendMode mode, Error **errp)
1696{
1697 Error *local_err = NULL;
1698 const char *sysfile_strs[3] = {"disk", "mem", NULL};
1699 const char *sysfile_str = sysfile_strs[mode];
1700 pid_t pid;
1701 int status;
1702
1703 if (!sysfile_str) {
1704 error_setg(errp, "unknown guest suspend mode");
1705 return;
1706 }
1707
1708 pid = fork();
1709 if (!pid) {
1710 /* child */
1711 int fd;
1712
1713 setsid();
1714 reopen_fd_to_null(0);
1715 reopen_fd_to_null(1);
1716 reopen_fd_to_null(2);
1717
1718 fd = open(LINUX_SYS_STATE_FILE, O_WRONLY);
1719 if (fd < 0) {
1720 _exit(EXIT_FAILURE);
1721 }
1722
1723 if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) {
1724 _exit(EXIT_FAILURE);
1725 }
1726
1727 _exit(EXIT_SUCCESS);
1728 } else if (pid < 0) {
1729 error_setg_errno(errp, errno, "failed to create child process");
1730 return;
1731 }
1732
1733 ga_wait_child(pid, &status, &local_err);
1734 if (local_err) {
1735 error_propagate(errp, local_err);
1736 return;
1737 }
1738
1739 if (WEXITSTATUS(status)) {
1740 error_setg(errp, "child process has failed to suspend");
1741 }
1742
1743}
1744
1745static void guest_suspend(SuspendMode mode, Error **errp)
1746{
1747 Error *local_err = NULL;
1748 bool mode_supported = false;
1749
1750 if (systemd_supports_mode(mode, &local_err)) {
1751 mode_supported = true;
1752 systemd_suspend(mode, &local_err);
1753 }
1754
1755 if (!local_err) {
1756 return;
1757 }
1758
1759 error_free(local_err);
1760
1761 if (pmutils_supports_mode(mode, &local_err)) {
1762 mode_supported = true;
1763 pmutils_suspend(mode, &local_err);
1764 }
1765
1766 if (!local_err) {
1767 return;
1768 }
1769
1770 error_free(local_err);
1771
1772 if (linux_sys_state_supports_mode(mode, &local_err)) {
1773 mode_supported = true;
1774 linux_sys_state_suspend(mode, &local_err);
1775 }
1776
1777 if (!mode_supported) {
1778 error_setg(errp,
1779 "the requested suspend mode is not supported by the guest");
1780 } else if (local_err) {
1781 error_propagate(errp, local_err);
1782 }
1783}
1784
1785void qmp_guest_suspend_disk(Error **errp)
1786{
1787 guest_suspend(SUSPEND_MODE_DISK, errp);
1788}
1789
1790void qmp_guest_suspend_ram(Error **errp)
1791{
1792 guest_suspend(SUSPEND_MODE_RAM, errp);
1793}
1794
1795void qmp_guest_suspend_hybrid(Error **errp)
1796{
1797 guest_suspend(SUSPEND_MODE_HYBRID, errp);
1798}
1799
1800static GuestNetworkInterfaceList *
1801guest_find_interface(GuestNetworkInterfaceList *head,
1802 const char *name)
1803{
1804 for (; head; head = head->next) {
1805 if (strcmp(head->value->name, name) == 0) {
1806 break;
1807 }
1808 }
1809
1810 return head;
1811}
1812
1813static int guest_get_network_stats(const char *name,
1814 GuestNetworkInterfaceStat *stats)
1815{
1816 int name_len;
1817 char const *devinfo = "/proc/net/dev";
1818 FILE *fp;
1819 char *line = NULL, *colon;
1820 size_t n = 0;
1821 fp = fopen(devinfo, "r");
1822 if (!fp) {
1823 return -1;
1824 }
1825 name_len = strlen(name);
1826 while (getline(&line, &n, fp) != -1) {
1827 long long dummy;
1828 long long rx_bytes;
1829 long long rx_packets;
1830 long long rx_errs;
1831 long long rx_dropped;
1832 long long tx_bytes;
1833 long long tx_packets;
1834 long long tx_errs;
1835 long long tx_dropped;
1836 char *trim_line;
1837 trim_line = g_strchug(line);
1838 if (trim_line[0] == '\0') {
1839 continue;
1840 }
1841 colon = strchr(trim_line, ':');
1842 if (!colon) {
1843 continue;
1844 }
1845 if (colon - name_len == trim_line &&
1846 strncmp(trim_line, name, name_len) == 0) {
1847 if (sscanf(colon + 1,
1848 "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld",
1849 &rx_bytes, &rx_packets, &rx_errs, &rx_dropped,
1850 &dummy, &dummy, &dummy, &dummy,
1851 &tx_bytes, &tx_packets, &tx_errs, &tx_dropped,
1852 &dummy, &dummy, &dummy, &dummy) != 16) {
1853 continue;
1854 }
1855 stats->rx_bytes = rx_bytes;
1856 stats->rx_packets = rx_packets;
1857 stats->rx_errs = rx_errs;
1858 stats->rx_dropped = rx_dropped;
1859 stats->tx_bytes = tx_bytes;
1860 stats->tx_packets = tx_packets;
1861 stats->tx_errs = tx_errs;
1862 stats->tx_dropped = tx_dropped;
1863 fclose(fp);
1864 g_free(line);
1865 return 0;
1866 }
1867 }
1868 fclose(fp);
1869 g_free(line);
1870 g_debug("/proc/net/dev: Interface '%s' not found", name);
1871 return -1;
1872}
1873
1874/*
1875 * Build information about guest interfaces
1876 */
1877GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
1878{
1879 GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
1880 struct ifaddrs *ifap, *ifa;
1881
1882 if (getifaddrs(&ifap) < 0) {
1883 error_setg_errno(errp, errno, "getifaddrs failed");
1884 goto error;
1885 }
1886
1887 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
1888 GuestNetworkInterfaceList *info;
1889 GuestIpAddressList **address_list = NULL, *address_item = NULL;
1890 GuestNetworkInterfaceStat *interface_stat = NULL;
1891 char addr4[INET_ADDRSTRLEN];
1892 char addr6[INET6_ADDRSTRLEN];
1893 int sock;
1894 struct ifreq ifr;
1895 unsigned char *mac_addr;
1896 void *p;
1897
1898 g_debug("Processing %s interface", ifa->ifa_name);
1899
1900 info = guest_find_interface(head, ifa->ifa_name);
1901
1902 if (!info) {
1903 info = g_malloc0(sizeof(*info));
1904 info->value = g_malloc0(sizeof(*info->value));
1905 info->value->name = g_strdup(ifa->ifa_name);
1906
1907 if (!cur_item) {
1908 head = cur_item = info;
1909 } else {
1910 cur_item->next = info;
1911 cur_item = info;
1912 }
1913 }
1914
1915 if (!info->value->has_hardware_address &&
1916 ifa->ifa_flags & SIOCGIFHWADDR) {
1917 /* we haven't obtained HW address yet */
1918 sock = socket(PF_INET, SOCK_STREAM, 0);
1919 if (sock == -1) {
1920 error_setg_errno(errp, errno, "failed to create socket");
1921 goto error;
1922 }
1923
1924 memset(&ifr, 0, sizeof(ifr));
1925 pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->value->name);
1926 if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
1927 error_setg_errno(errp, errno,
1928 "failed to get MAC address of %s",
1929 ifa->ifa_name);
1930 close(sock);
1931 goto error;
1932 }
1933
1934 close(sock);
1935 mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
1936
1937 info->value->hardware_address =
1938 g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x",
1939 (int) mac_addr[0], (int) mac_addr[1],
1940 (int) mac_addr[2], (int) mac_addr[3],
1941 (int) mac_addr[4], (int) mac_addr[5]);
1942
1943 info->value->has_hardware_address = true;
1944 }
1945
1946 if (ifa->ifa_addr &&
1947 ifa->ifa_addr->sa_family == AF_INET) {
1948 /* interface with IPv4 address */
1949 p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
1950 if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
1951 error_setg_errno(errp, errno, "inet_ntop failed");
1952 goto error;
1953 }
1954
1955 address_item = g_malloc0(sizeof(*address_item));
1956 address_item->value = g_malloc0(sizeof(*address_item->value));
1957 address_item->value->ip_address = g_strdup(addr4);
1958 address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;
1959
1960 if (ifa->ifa_netmask) {
1961 /* Count the number of set bits in netmask.
1962 * This is safe as '1' and '0' cannot be shuffled in netmask. */
1963 p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;
1964 address_item->value->prefix = ctpop32(((uint32_t *) p)[0]);
1965 }
1966 } else if (ifa->ifa_addr &&
1967 ifa->ifa_addr->sa_family == AF_INET6) {
1968 /* interface with IPv6 address */
1969 p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
1970 if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
1971 error_setg_errno(errp, errno, "inet_ntop failed");
1972 goto error;
1973 }
1974
1975 address_item = g_malloc0(sizeof(*address_item));
1976 address_item->value = g_malloc0(sizeof(*address_item->value));
1977 address_item->value->ip_address = g_strdup(addr6);
1978 address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;
1979
1980 if (ifa->ifa_netmask) {
1981 /* Count the number of set bits in netmask.
1982 * This is safe as '1' and '0' cannot be shuffled in netmask. */
1983 p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
1984 address_item->value->prefix =
1985 ctpop32(((uint32_t *) p)[0]) +
1986 ctpop32(((uint32_t *) p)[1]) +
1987 ctpop32(((uint32_t *) p)[2]) +
1988 ctpop32(((uint32_t *) p)[3]);
1989 }
1990 }
1991
1992 if (!address_item) {
1993 continue;
1994 }
1995
1996 address_list = &info->value->ip_addresses;
1997
1998 while (*address_list && (*address_list)->next) {
1999 address_list = &(*address_list)->next;
2000 }
2001
2002 if (!*address_list) {
2003 *address_list = address_item;
2004 } else {
2005 (*address_list)->next = address_item;
2006 }
2007
2008 info->value->has_ip_addresses = true;
2009
2010 if (!info->value->has_statistics) {
2011 interface_stat = g_malloc0(sizeof(*interface_stat));
2012 if (guest_get_network_stats(info->value->name,
2013 interface_stat) == -1) {
2014 info->value->has_statistics = false;
2015 g_free(interface_stat);
2016 } else {
2017 info->value->statistics = interface_stat;
2018 info->value->has_statistics = true;
2019 }
2020 }
2021 }
2022
2023 freeifaddrs(ifap);
2024 return head;
2025
2026error:
2027 freeifaddrs(ifap);
2028 qapi_free_GuestNetworkInterfaceList(head);
2029 return NULL;
2030}
2031
2032#define SYSCONF_EXACT(name, errp) sysconf_exact((name), #name, (errp))
2033
2034static long sysconf_exact(int name, const char *name_str, Error **errp)
2035{
2036 long ret;
2037
2038 errno = 0;
2039 ret = sysconf(name);
2040 if (ret == -1) {
2041 if (errno == 0) {
2042 error_setg(errp, "sysconf(%s): value indefinite", name_str);
2043 } else {
2044 error_setg_errno(errp, errno, "sysconf(%s)", name_str);
2045 }
2046 }
2047 return ret;
2048}
2049
2050/* Transfer online/offline status between @vcpu and the guest system.
2051 *
2052 * On input either @errp or *@errp must be NULL.
2053 *
2054 * In system-to-@vcpu direction, the following @vcpu fields are accessed:
2055 * - R: vcpu->logical_id
2056 * - W: vcpu->online
2057 * - W: vcpu->can_offline
2058 *
2059 * In @vcpu-to-system direction, the following @vcpu fields are accessed:
2060 * - R: vcpu->logical_id
2061 * - R: vcpu->online
2062 *
2063 * Written members remain unmodified on error.
2064 */
2065static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu,
2066 char *dirpath, Error **errp)
2067{
2068 int fd;
2069 int res;
2070 int dirfd;
2071 static const char fn[] = "online";
2072
2073 dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
2074 if (dirfd == -1) {
2075 error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
2076 return;
2077 }
2078
2079 fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR);
2080 if (fd == -1) {
2081 if (errno != ENOENT) {
2082 error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn);
2083 } else if (sys2vcpu) {
2084 vcpu->online = true;
2085 vcpu->can_offline = false;
2086 } else if (!vcpu->online) {
2087 error_setg(errp, "logical processor #%" PRId64 " can't be "
2088 "offlined", vcpu->logical_id);
2089 } /* otherwise pretend successful re-onlining */
2090 } else {
2091 unsigned char status;
2092
2093 res = pread(fd, &status, 1, 0);
2094 if (res == -1) {
2095 error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn);
2096 } else if (res == 0) {
2097 error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath,
2098 fn);
2099 } else if (sys2vcpu) {
2100 vcpu->online = (status != '0');
2101 vcpu->can_offline = true;
2102 } else if (vcpu->online != (status != '0')) {
2103 status = '0' + vcpu->online;
2104 if (pwrite(fd, &status, 1, 0) == -1) {
2105 error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath,
2106 fn);
2107 }
2108 } /* otherwise pretend successful re-(on|off)-lining */
2109
2110 res = close(fd);
2111 g_assert(res == 0);
2112 }
2113
2114 res = close(dirfd);
2115 g_assert(res == 0);
2116}
2117
2118GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
2119{
2120 int64_t current;
2121 GuestLogicalProcessorList *head, **link;
2122 long sc_max;
2123 Error *local_err = NULL;
2124
2125 current = 0;
2126 head = NULL;
2127 link = &head;
2128 sc_max = SYSCONF_EXACT(_SC_NPROCESSORS_CONF, &local_err);
2129
2130 while (local_err == NULL && current < sc_max) {
2131 GuestLogicalProcessor *vcpu;
2132 GuestLogicalProcessorList *entry;
2133 int64_t id = current++;
2134 char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/",
2135 id);
2136
2137 if (g_file_test(path, G_FILE_TEST_EXISTS)) {
2138 vcpu = g_malloc0(sizeof *vcpu);
2139 vcpu->logical_id = id;
2140 vcpu->has_can_offline = true; /* lolspeak ftw */
2141 transfer_vcpu(vcpu, true, path, &local_err);
2142 entry = g_malloc0(sizeof *entry);
2143 entry->value = vcpu;
2144 *link = entry;
2145 link = &entry->next;
2146 }
2147 g_free(path);
2148 }
2149
2150 if (local_err == NULL) {
2151 /* there's no guest with zero VCPUs */
2152 g_assert(head != NULL);
2153 return head;
2154 }
2155
2156 qapi_free_GuestLogicalProcessorList(head);
2157 error_propagate(errp, local_err);
2158 return NULL;
2159}
2160
2161int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
2162{
2163 int64_t processed;
2164 Error *local_err = NULL;
2165
2166 processed = 0;
2167 while (vcpus != NULL) {
2168 char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/",
2169 vcpus->value->logical_id);
2170
2171 transfer_vcpu(vcpus->value, false, path, &local_err);
2172 g_free(path);
2173 if (local_err != NULL) {
2174 break;
2175 }
2176 ++processed;
2177 vcpus = vcpus->next;
2178 }
2179
2180 if (local_err != NULL) {
2181 if (processed == 0) {
2182 error_propagate(errp, local_err);
2183 } else {
2184 error_free(local_err);
2185 }
2186 }
2187
2188 return processed;
2189}
2190
2191void qmp_guest_set_user_password(const char *username,
2192 const char *password,
2193 bool crypted,
2194 Error **errp)
2195{
2196 Error *local_err = NULL;
2197 char *passwd_path = NULL;
2198 pid_t pid;
2199 int status;
2200 int datafd[2] = { -1, -1 };
2201 char *rawpasswddata = NULL;
2202 size_t rawpasswdlen;
2203 char *chpasswddata = NULL;
2204 size_t chpasswdlen;
2205
2206 rawpasswddata = (char *)qbase64_decode(password, -1, &rawpasswdlen, errp);
2207 if (!rawpasswddata) {
2208 return;
2209 }
2210 rawpasswddata = g_renew(char, rawpasswddata, rawpasswdlen + 1);
2211 rawpasswddata[rawpasswdlen] = '\0';
2212
2213 if (strchr(rawpasswddata, '\n')) {
2214 error_setg(errp, "forbidden characters in raw password");
2215 goto out;
2216 }
2217
2218 if (strchr(username, '\n') ||
2219 strchr(username, ':')) {
2220 error_setg(errp, "forbidden characters in username");
2221 goto out;
2222 }
2223
2224 chpasswddata = g_strdup_printf("%s:%s\n", username, rawpasswddata);
2225 chpasswdlen = strlen(chpasswddata);
2226
2227 passwd_path = g_find_program_in_path("chpasswd");
2228
2229 if (!passwd_path) {
2230 error_setg(errp, "cannot find 'passwd' program in PATH");
2231 goto out;
2232 }
2233
2234 if (pipe(datafd) < 0) {
2235 error_setg(errp, "cannot create pipe FDs");
2236 goto out;
2237 }
2238
2239 pid = fork();
2240 if (pid == 0) {
2241 close(datafd[1]);
2242 /* child */
2243 setsid();
2244 dup2(datafd[0], 0);
2245 reopen_fd_to_null(1);
2246 reopen_fd_to_null(2);
2247
2248 if (crypted) {
2249 execle(passwd_path, "chpasswd", "-e", NULL, environ);
2250 } else {
2251 execle(passwd_path, "chpasswd", NULL, environ);
2252 }
2253 _exit(EXIT_FAILURE);
2254 } else if (pid < 0) {
2255 error_setg_errno(errp, errno, "failed to create child process");
2256 goto out;
2257 }
2258 close(datafd[0]);
2259 datafd[0] = -1;
2260
2261 if (qemu_write_full(datafd[1], chpasswddata, chpasswdlen) != chpasswdlen) {
2262 error_setg_errno(errp, errno, "cannot write new account password");
2263 goto out;
2264 }
2265 close(datafd[1]);
2266 datafd[1] = -1;
2267
2268 ga_wait_child(pid, &status, &local_err);
2269 if (local_err) {
2270 error_propagate(errp, local_err);
2271 goto out;
2272 }
2273
2274 if (!WIFEXITED(status)) {
2275 error_setg(errp, "child process has terminated abnormally");
2276 goto out;
2277 }
2278
2279 if (WEXITSTATUS(status)) {
2280 error_setg(errp, "child process has failed to set user password");
2281 goto out;
2282 }
2283
2284out:
2285 g_free(chpasswddata);
2286 g_free(rawpasswddata);
2287 g_free(passwd_path);
2288 if (datafd[0] != -1) {
2289 close(datafd[0]);
2290 }
2291 if (datafd[1] != -1) {
2292 close(datafd[1]);
2293 }
2294}
2295
2296static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf,
2297 int size, Error **errp)
2298{
2299 int fd;
2300 int res;
2301
2302 errno = 0;
2303 fd = openat(dirfd, pathname, O_RDONLY);
2304 if (fd == -1) {
2305 error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
2306 return;
2307 }
2308
2309 res = pread(fd, buf, size, 0);
2310 if (res == -1) {
2311 error_setg_errno(errp, errno, "pread sysfs file \"%s\"", pathname);
2312 } else if (res == 0) {
2313 error_setg(errp, "pread sysfs file \"%s\": unexpected EOF", pathname);
2314 }
2315 close(fd);
2316}
2317
2318static void ga_write_sysfs_file(int dirfd, const char *pathname,
2319 const char *buf, int size, Error **errp)
2320{
2321 int fd;
2322
2323 errno = 0;
2324 fd = openat(dirfd, pathname, O_WRONLY);
2325 if (fd == -1) {
2326 error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
2327 return;
2328 }
2329
2330 if (pwrite(fd, buf, size, 0) == -1) {
2331 error_setg_errno(errp, errno, "pwrite sysfs file \"%s\"", pathname);
2332 }
2333
2334 close(fd);
2335}
2336
2337/* Transfer online/offline status between @mem_blk and the guest system.
2338 *
2339 * On input either @errp or *@errp must be NULL.
2340 *
2341 * In system-to-@mem_blk direction, the following @mem_blk fields are accessed:
2342 * - R: mem_blk->phys_index
2343 * - W: mem_blk->online
2344 * - W: mem_blk->can_offline
2345 *
2346 * In @mem_blk-to-system direction, the following @mem_blk fields are accessed:
2347 * - R: mem_blk->phys_index
2348 * - R: mem_blk->online
2349 *- R: mem_blk->can_offline
2350 * Written members remain unmodified on error.
2351 */
2352static void transfer_memory_block(GuestMemoryBlock *mem_blk, bool sys2memblk,
2353 GuestMemoryBlockResponse *result,
2354 Error **errp)
2355{
2356 char *dirpath;
2357 int dirfd;
2358 char *status;
2359 Error *local_err = NULL;
2360
2361 if (!sys2memblk) {
2362 DIR *dp;
2363
2364 if (!result) {
2365 error_setg(errp, "Internal error, 'result' should not be NULL");
2366 return;
2367 }
2368 errno = 0;
2369 dp = opendir("/sys/devices/system/memory/");
2370 /* if there is no 'memory' directory in sysfs,
2371 * we think this VM does not support online/offline memory block,
2372 * any other solution?
2373 */
2374 if (!dp) {
2375 if (errno == ENOENT) {
2376 result->response =
2377 GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
2378 }
2379 goto out1;
2380 }
2381 closedir(dp);
2382 }
2383
2384 dirpath = g_strdup_printf("/sys/devices/system/memory/memory%" PRId64 "/",
2385 mem_blk->phys_index);
2386 dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
2387 if (dirfd == -1) {
2388 if (sys2memblk) {
2389 error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
2390 } else {
2391 if (errno == ENOENT) {
2392 result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_NOT_FOUND;
2393 } else {
2394 result->response =
2395 GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
2396 }
2397 }
2398 g_free(dirpath);
2399 goto out1;
2400 }
2401 g_free(dirpath);
2402
2403 status = g_malloc0(10);
2404 ga_read_sysfs_file(dirfd, "state", status, 10, &local_err);
2405 if (local_err) {
2406 /* treat with sysfs file that not exist in old kernel */
2407 if (errno == ENOENT) {
2408 error_free(local_err);
2409 if (sys2memblk) {
2410 mem_blk->online = true;
2411 mem_blk->can_offline = false;
2412 } else if (!mem_blk->online) {
2413 result->response =
2414 GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
2415 }
2416 } else {
2417 if (sys2memblk) {
2418 error_propagate(errp, local_err);
2419 } else {
2420 result->response =
2421 GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
2422 }
2423 }
2424 goto out2;
2425 }
2426
2427 if (sys2memblk) {
2428 char removable = '0';
2429
2430 mem_blk->online = (strncmp(status, "online", 6) == 0);
2431
2432 ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err);
2433 if (local_err) {
2434 /* if no 'removable' file, it doesn't support offline mem blk */
2435 if (errno == ENOENT) {
2436 error_free(local_err);
2437 mem_blk->can_offline = false;
2438 } else {
2439 error_propagate(errp, local_err);
2440 }
2441 } else {
2442 mem_blk->can_offline = (removable != '0');
2443 }
2444 } else {
2445 if (mem_blk->online != (strncmp(status, "online", 6) == 0)) {
2446 const char *new_state = mem_blk->online ? "online" : "offline";
2447
2448 ga_write_sysfs_file(dirfd, "state", new_state, strlen(new_state),
2449 &local_err);
2450 if (local_err) {
2451 error_free(local_err);
2452 result->response =
2453 GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
2454 goto out2;
2455 }
2456
2457 result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_SUCCESS;
2458 result->has_error_code = false;
2459 } /* otherwise pretend successful re-(on|off)-lining */
2460 }
2461 g_free(status);
2462 close(dirfd);
2463 return;
2464
2465out2:
2466 g_free(status);
2467 close(dirfd);
2468out1:
2469 if (!sys2memblk) {
2470 result->has_error_code = true;
2471 result->error_code = errno;
2472 }
2473}
2474
2475GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
2476{
2477 GuestMemoryBlockList *head, **link;
2478 Error *local_err = NULL;
2479 struct dirent *de;
2480 DIR *dp;
2481
2482 head = NULL;
2483 link = &head;
2484
2485 dp = opendir("/sys/devices/system/memory/");
2486 if (!dp) {
2487 /* it's ok if this happens to be a system that doesn't expose
2488 * memory blocks via sysfs, but otherwise we should report
2489 * an error
2490 */
2491 if (errno != ENOENT) {
2492 error_setg_errno(errp, errno, "Can't open directory"
2493 "\"/sys/devices/system/memory/\"");
2494 }
2495 return NULL;
2496 }
2497
2498 /* Note: the phys_index of memory block may be discontinuous,
2499 * this is because a memblk is the unit of the Sparse Memory design, which
2500 * allows discontinuous memory ranges (ex. NUMA), so here we should
2501 * traverse the memory block directory.
2502 */
2503 while ((de = readdir(dp)) != NULL) {
2504 GuestMemoryBlock *mem_blk;
2505 GuestMemoryBlockList *entry;
2506
2507 if ((strncmp(de->d_name, "memory", 6) != 0) ||
2508 !(de->d_type & DT_DIR)) {
2509 continue;
2510 }
2511
2512 mem_blk = g_malloc0(sizeof *mem_blk);
2513 /* The d_name is "memoryXXX", phys_index is block id, same as XXX */
2514 mem_blk->phys_index = strtoul(&de->d_name[6], NULL, 10);
2515 mem_blk->has_can_offline = true; /* lolspeak ftw */
2516 transfer_memory_block(mem_blk, true, NULL, &local_err);
2517
2518 entry = g_malloc0(sizeof *entry);
2519 entry->value = mem_blk;
2520
2521 *link = entry;
2522 link = &entry->next;
2523 }
2524
2525 closedir(dp);
2526 if (local_err == NULL) {
2527 /* there's no guest with zero memory blocks */
2528 if (head == NULL) {
2529 error_setg(errp, "guest reported zero memory blocks!");
2530 }
2531 return head;
2532 }
2533
2534 qapi_free_GuestMemoryBlockList(head);
2535 error_propagate(errp, local_err);
2536 return NULL;
2537}
2538
2539GuestMemoryBlockResponseList *
2540qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
2541{
2542 GuestMemoryBlockResponseList *head, **link;
2543 Error *local_err = NULL;
2544
2545 head = NULL;
2546 link = &head;
2547
2548 while (mem_blks != NULL) {
2549 GuestMemoryBlockResponse *result;
2550 GuestMemoryBlockResponseList *entry;
2551 GuestMemoryBlock *current_mem_blk = mem_blks->value;
2552
2553 result = g_malloc0(sizeof(*result));
2554 result->phys_index = current_mem_blk->phys_index;
2555 transfer_memory_block(current_mem_blk, false, result, &local_err);
2556 if (local_err) { /* should never happen */
2557 goto err;
2558 }
2559 entry = g_malloc0(sizeof *entry);
2560 entry->value = result;
2561
2562 *link = entry;
2563 link = &entry->next;
2564 mem_blks = mem_blks->next;
2565 }
2566
2567 return head;
2568err:
2569 qapi_free_GuestMemoryBlockResponseList(head);
2570 error_propagate(errp, local_err);
2571 return NULL;
2572}
2573
2574GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
2575{
2576 Error *local_err = NULL;
2577 char *dirpath;
2578 int dirfd;
2579 char *buf;
2580 GuestMemoryBlockInfo *info;
2581
2582 dirpath = g_strdup_printf("/sys/devices/system/memory/");
2583 dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
2584 if (dirfd == -1) {
2585 error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
2586 g_free(dirpath);
2587 return NULL;
2588 }
2589 g_free(dirpath);
2590
2591 buf = g_malloc0(20);
2592 ga_read_sysfs_file(dirfd, "block_size_bytes", buf, 20, &local_err);
2593 close(dirfd);
2594 if (local_err) {
2595 g_free(buf);
2596 error_propagate(errp, local_err);
2597 return NULL;
2598 }
2599
2600 info = g_new0(GuestMemoryBlockInfo, 1);
2601 info->size = strtol(buf, NULL, 16); /* the unit is bytes */
2602
2603 g_free(buf);
2604
2605 return info;
2606}
2607
2608#else /* defined(__linux__) */
2609
2610void qmp_guest_suspend_disk(Error **errp)
2611{
2612 error_setg(errp, QERR_UNSUPPORTED);
2613}
2614
2615void qmp_guest_suspend_ram(Error **errp)
2616{
2617 error_setg(errp, QERR_UNSUPPORTED);
2618}
2619
2620void qmp_guest_suspend_hybrid(Error **errp)
2621{
2622 error_setg(errp, QERR_UNSUPPORTED);
2623}
2624
2625GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
2626{
2627 error_setg(errp, QERR_UNSUPPORTED);
2628 return NULL;
2629}
2630
2631GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
2632{
2633 error_setg(errp, QERR_UNSUPPORTED);
2634 return NULL;
2635}
2636
2637int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
2638{
2639 error_setg(errp, QERR_UNSUPPORTED);
2640 return -1;
2641}
2642
2643void qmp_guest_set_user_password(const char *username,
2644 const char *password,
2645 bool crypted,
2646 Error **errp)
2647{
2648 error_setg(errp, QERR_UNSUPPORTED);
2649}
2650
2651GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
2652{
2653 error_setg(errp, QERR_UNSUPPORTED);
2654 return NULL;
2655}
2656
2657GuestMemoryBlockResponseList *
2658qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
2659{
2660 error_setg(errp, QERR_UNSUPPORTED);
2661 return NULL;
2662}
2663
2664GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
2665{
2666 error_setg(errp, QERR_UNSUPPORTED);
2667 return NULL;
2668}
2669
2670#endif
2671
2672#if !defined(CONFIG_FSFREEZE)
2673
2674GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
2675{
2676 error_setg(errp, QERR_UNSUPPORTED);
2677 return NULL;
2678}
2679
2680GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
2681{
2682 error_setg(errp, QERR_UNSUPPORTED);
2683
2684 return 0;
2685}
2686
2687int64_t qmp_guest_fsfreeze_freeze(Error **errp)
2688{
2689 error_setg(errp, QERR_UNSUPPORTED);
2690
2691 return 0;
2692}
2693
2694int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
2695 strList *mountpoints,
2696 Error **errp)
2697{
2698 error_setg(errp, QERR_UNSUPPORTED);
2699
2700 return 0;
2701}
2702
2703int64_t qmp_guest_fsfreeze_thaw(Error **errp)
2704{
2705 error_setg(errp, QERR_UNSUPPORTED);
2706
2707 return 0;
2708}
2709#endif /* CONFIG_FSFREEZE */
2710
2711#if !defined(CONFIG_FSTRIM)
2712GuestFilesystemTrimResponse *
2713qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
2714{
2715 error_setg(errp, QERR_UNSUPPORTED);
2716 return NULL;
2717}
2718#endif
2719
2720/* add unsupported commands to the blacklist */
2721GList *ga_command_blacklist_init(GList *blacklist)
2722{
2723#if !defined(__linux__)
2724 {
2725 const char *list[] = {
2726 "guest-suspend-disk", "guest-suspend-ram",
2727 "guest-suspend-hybrid", "guest-network-get-interfaces",
2728 "guest-get-vcpus", "guest-set-vcpus",
2729 "guest-get-memory-blocks", "guest-set-memory-blocks",
2730 "guest-get-memory-block-size", NULL};
2731 char **p = (char **)list;
2732
2733 while (*p) {
2734 blacklist = g_list_append(blacklist, g_strdup(*p++));
2735 }
2736 }
2737#endif
2738
2739#if !defined(CONFIG_FSFREEZE)
2740 {
2741 const char *list[] = {
2742 "guest-get-fsinfo", "guest-fsfreeze-status",
2743 "guest-fsfreeze-freeze", "guest-fsfreeze-freeze-list",
2744 "guest-fsfreeze-thaw", "guest-get-fsinfo", NULL};
2745 char **p = (char **)list;
2746
2747 while (*p) {
2748 blacklist = g_list_append(blacklist, g_strdup(*p++));
2749 }
2750 }
2751#endif
2752
2753#if !defined(CONFIG_FSTRIM)
2754 blacklist = g_list_append(blacklist, g_strdup("guest-fstrim"));
2755#endif
2756
2757 return blacklist;
2758}
2759
2760/* register init/cleanup routines for stateful command groups */
2761void ga_command_state_init(GAState *s, GACommandState *cs)
2762{
2763#if defined(CONFIG_FSFREEZE)
2764 ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
2765#endif
2766}
2767
2768#ifdef HAVE_UTMPX
2769
2770#define QGA_MICRO_SECOND_TO_SECOND 1000000
2771
2772static double ga_get_login_time(struct utmpx *user_info)
2773{
2774 double seconds = (double)user_info->ut_tv.tv_sec;
2775 double useconds = (double)user_info->ut_tv.tv_usec;
2776 useconds /= QGA_MICRO_SECOND_TO_SECOND;
2777 return seconds + useconds;
2778}
2779
2780GuestUserList *qmp_guest_get_users(Error **err)
2781{
2782 GHashTable *cache = NULL;
2783 GuestUserList *head = NULL, *cur_item = NULL;
2784 struct utmpx *user_info = NULL;
2785 gpointer value = NULL;
2786 GuestUser *user = NULL;
2787 GuestUserList *item = NULL;
2788 double login_time = 0;
2789
2790 cache = g_hash_table_new(g_str_hash, g_str_equal);
2791 setutxent();
2792
2793 for (;;) {
2794 user_info = getutxent();
2795 if (user_info == NULL) {
2796 break;
2797 } else if (user_info->ut_type != USER_PROCESS) {
2798 continue;
2799 } else if (g_hash_table_contains(cache, user_info->ut_user)) {
2800 value = g_hash_table_lookup(cache, user_info->ut_user);
2801 user = (GuestUser *)value;
2802 login_time = ga_get_login_time(user_info);
2803 /* We're ensuring the earliest login time to be sent */
2804 if (login_time < user->login_time) {
2805 user->login_time = login_time;
2806 }
2807 continue;
2808 }
2809
2810 item = g_new0(GuestUserList, 1);
2811 item->value = g_new0(GuestUser, 1);
2812 item->value->user = g_strdup(user_info->ut_user);
2813 item->value->login_time = ga_get_login_time(user_info);
2814
2815 g_hash_table_insert(cache, item->value->user, item->value);
2816
2817 if (!cur_item) {
2818 head = cur_item = item;
2819 } else {
2820 cur_item->next = item;
2821 cur_item = item;
2822 }
2823 }
2824 endutxent();
2825 g_hash_table_destroy(cache);
2826 return head;
2827}
2828
2829#else
2830
2831GuestUserList *qmp_guest_get_users(Error **errp)
2832{
2833 error_setg(errp, QERR_UNSUPPORTED);
2834 return NULL;
2835}
2836
2837#endif
2838
2839/* Replace escaped special characters with theire real values. The replacement
2840 * is done in place -- returned value is in the original string.
2841 */
2842static void ga_osrelease_replace_special(gchar *value)
2843{
2844 gchar *p, *p2, quote;
2845
2846 /* Trim the string at first space or semicolon if it is not enclosed in
2847 * single or double quotes. */
2848 if ((value[0] != '"') || (value[0] == '\'')) {
2849 p = strchr(value, ' ');
2850 if (p != NULL) {
2851 *p = 0;
2852 }
2853 p = strchr(value, ';');
2854 if (p != NULL) {
2855 *p = 0;
2856 }
2857 return;
2858 }
2859
2860 quote = value[0];
2861 p2 = value;
2862 p = value + 1;
2863 while (*p != 0) {
2864 if (*p == '\\') {
2865 p++;
2866 switch (*p) {
2867 case '$':
2868 case '\'':
2869 case '"':
2870 case '\\':
2871 case '`':
2872 break;
2873 default:
2874 /* Keep literal backslash followed by whatever is there */
2875 p--;
2876 break;
2877 }
2878 } else if (*p == quote) {
2879 *p2 = 0;
2880 break;
2881 }
2882 *(p2++) = *(p++);
2883 }
2884}
2885
2886static GKeyFile *ga_parse_osrelease(const char *fname)
2887{
2888 gchar *content = NULL;
2889 gchar *content2 = NULL;
2890 GError *err = NULL;
2891 GKeyFile *keys = g_key_file_new();
2892 const char *group = "[os-release]\n";
2893
2894 if (!g_file_get_contents(fname, &content, NULL, &err)) {
2895 slog("failed to read '%s', error: %s", fname, err->message);
2896 goto fail;
2897 }
2898
2899 if (!g_utf8_validate(content, -1, NULL)) {
2900 slog("file is not utf-8 encoded: %s", fname);
2901 goto fail;
2902 }
2903 content2 = g_strdup_printf("%s%s", group, content);
2904
2905 if (!g_key_file_load_from_data(keys, content2, -1, G_KEY_FILE_NONE,
2906 &err)) {
2907 slog("failed to parse file '%s', error: %s", fname, err->message);
2908 goto fail;
2909 }
2910
2911 g_free(content);
2912 g_free(content2);
2913 return keys;
2914
2915fail:
2916 g_error_free(err);
2917 g_free(content);
2918 g_free(content2);
2919 g_key_file_free(keys);
2920 return NULL;
2921}
2922
2923GuestOSInfo *qmp_guest_get_osinfo(Error **errp)
2924{
2925 GuestOSInfo *info = NULL;
2926 struct utsname kinfo;
2927 GKeyFile *osrelease = NULL;
2928 const char *qga_os_release = g_getenv("QGA_OS_RELEASE");
2929
2930 info = g_new0(GuestOSInfo, 1);
2931
2932 if (uname(&kinfo) != 0) {
2933 error_setg_errno(errp, errno, "uname failed");
2934 } else {
2935 info->has_kernel_version = true;
2936 info->kernel_version = g_strdup(kinfo.version);
2937 info->has_kernel_release = true;
2938 info->kernel_release = g_strdup(kinfo.release);
2939 info->has_machine = true;
2940 info->machine = g_strdup(kinfo.machine);
2941 }
2942
2943 if (qga_os_release != NULL) {
2944 osrelease = ga_parse_osrelease(qga_os_release);
2945 } else {
2946 osrelease = ga_parse_osrelease("/etc/os-release");
2947 if (osrelease == NULL) {
2948 osrelease = ga_parse_osrelease("/usr/lib/os-release");
2949 }
2950 }
2951
2952 if (osrelease != NULL) {
2953 char *value;
2954
2955#define GET_FIELD(field, osfield) do { \
2956 value = g_key_file_get_value(osrelease, "os-release", osfield, NULL); \
2957 if (value != NULL) { \
2958 ga_osrelease_replace_special(value); \
2959 info->has_ ## field = true; \
2960 info->field = value; \
2961 } \
2962} while (0)
2963 GET_FIELD(id, "ID");
2964 GET_FIELD(name, "NAME");
2965 GET_FIELD(pretty_name, "PRETTY_NAME");
2966 GET_FIELD(version, "VERSION");
2967 GET_FIELD(version_id, "VERSION_ID");
2968 GET_FIELD(variant, "VARIANT");
2969 GET_FIELD(variant_id, "VARIANT_ID");
2970#undef GET_FIELD
2971
2972 g_key_file_free(osrelease);
2973 }
2974
2975 return info;
2976}
This page took 0.055771 seconds and 4 git commands to generate.