xref: /openbmc/qemu/qga/commands-posix.c (revision 01dc0651)
1c216e5adSMichael Roth /*
2c216e5adSMichael Roth  * QEMU Guest Agent POSIX-specific command implementations
3c216e5adSMichael Roth  *
4c216e5adSMichael Roth  * Copyright IBM Corp. 2011
5c216e5adSMichael Roth  *
6c216e5adSMichael Roth  * Authors:
7c216e5adSMichael Roth  *  Michael Roth      <mdroth@linux.vnet.ibm.com>
83424fc9fSMichal Privoznik  *  Michal Privoznik  <mprivozn@redhat.com>
9c216e5adSMichael Roth  *
10c216e5adSMichael Roth  * This work is licensed under the terms of the GNU GPL, version 2 or later.
11c216e5adSMichael Roth  * See the COPYING file in the top-level directory.
12c216e5adSMichael Roth  */
13c216e5adSMichael Roth 
144459bf38SPeter Maydell #include "qemu/osdep.h"
15c216e5adSMichael Roth #include <sys/ioctl.h>
169848f797STomáš Golembiovský #include <sys/utsname.h>
172c02cbf6SLuiz Capitulino #include <sys/wait.h>
1846d4c572STomoki Sekiyama #include <dirent.h>
19eb815e24SMarkus Armbruster #include "qga-qapi-commands.h"
20e688df6bSMarkus Armbruster #include "qapi/error.h"
217b1b5d19SPaolo Bonzini #include "qapi/qmp/qerror.h"
221de7afc9SPaolo Bonzini #include "qemu/host-utils.h"
2312505396SDenis V. Lunev #include "qemu/sockets.h"
24920639caSDaniel P. Berrange #include "qemu/base64.h"
25f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
265d3586b8SPhilippe Mathieu-Daudé #include "commands-common.h"
2722668881Szhenwei pi #include "block/nvme.h"
281a89a17bSMarc-André Lureau #include "cutils.h"
29c216e5adSMichael Roth 
30e674605fSTomáš Golembiovský #ifdef HAVE_UTMPX
31e674605fSTomáš Golembiovský #include <utmpx.h>
32e674605fSTomáš Golembiovský #endif
33e674605fSTomáš Golembiovský 
34e72c3f2eSMichael Roth #if defined(__linux__)
35e72c3f2eSMichael Roth #include <mntent.h>
3625b5ff1aSChen Hanxiao #include <sys/statvfs.h>
3722668881Szhenwei pi #include <linux/nvme_ioctl.h>
38e72c3f2eSMichael Roth 
39b616105aSTomáš Golembiovský #ifdef CONFIG_LIBUDEV
40b616105aSTomáš Golembiovský #include <libudev.h>
41b616105aSTomáš Golembiovský #endif
42e72c3f2eSMichael Roth #endif
43e72c3f2eSMichael Roth 
4459e35c7bSAndrew Deason #ifdef HAVE_GETIFADDRS
4559e35c7bSAndrew Deason #include <arpa/inet.h>
4659e35c7bSAndrew Deason #include <sys/socket.h>
4759e35c7bSAndrew Deason #include <net/if.h>
4828236ad8SBrad Smith #if defined(__NetBSD__) || defined(__OpenBSD__)
4928236ad8SBrad Smith #include <net/if_arp.h>
5028236ad8SBrad Smith #include <netinet/if_ether.h>
5128236ad8SBrad Smith #else
52a1241094SAlexander Ivanov #include <net/ethernet.h>
5328236ad8SBrad Smith #endif
5459e35c7bSAndrew Deason #ifdef CONFIG_SOLARIS
5559e35c7bSAndrew Deason #include <sys/sockio.h>
5659e35c7bSAndrew Deason #endif
5759e35c7bSAndrew Deason #endif
5859e35c7bSAndrew Deason 
ga_wait_child(pid_t pid,int * status,Error ** errp)5977dbc81bSMarkus Armbruster static void ga_wait_child(pid_t pid, int *status, Error **errp)
60d220a6dfSLuiz Capitulino {
61d220a6dfSLuiz Capitulino     pid_t rpid;
62d220a6dfSLuiz Capitulino 
63d220a6dfSLuiz Capitulino     *status = 0;
64d220a6dfSLuiz Capitulino 
6537b0b24eSNikita Ivanov     rpid = RETRY_ON_EINTR(waitpid(pid, status, 0));
66d220a6dfSLuiz Capitulino 
67d220a6dfSLuiz Capitulino     if (rpid == -1) {
6877dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to wait for child (pid: %d)",
6977dbc81bSMarkus Armbruster                          pid);
70d220a6dfSLuiz Capitulino         return;
71d220a6dfSLuiz Capitulino     }
72d220a6dfSLuiz Capitulino 
73d220a6dfSLuiz Capitulino     g_assert(rpid == pid);
74d220a6dfSLuiz Capitulino }
75d220a6dfSLuiz Capitulino 
qmp_guest_shutdown(const char * mode,Error ** errp)7691eab32aSMarkus Armbruster void qmp_guest_shutdown(const char *mode, Error **errp)
77c216e5adSMichael Roth {
78c216e5adSMichael Roth     const char *shutdown_flag;
79d220a6dfSLuiz Capitulino     Error *local_err = NULL;
80d220a6dfSLuiz Capitulino     pid_t pid;
813674838cSLuiz Capitulino     int status;
82c216e5adSMichael Roth 
83c8ec041dSAndrew Deason #ifdef CONFIG_SOLARIS
84c8ec041dSAndrew Deason     const char *powerdown_flag = "-i5";
85c8ec041dSAndrew Deason     const char *halt_flag = "-i0";
86c8ec041dSAndrew Deason     const char *reboot_flag = "-i6";
87e40762fcSAlexander Ivanov #elif defined(CONFIG_BSD)
88e40762fcSAlexander Ivanov     const char *powerdown_flag = "-p";
89e40762fcSAlexander Ivanov     const char *halt_flag = "-h";
90e40762fcSAlexander Ivanov     const char *reboot_flag = "-r";
91c8ec041dSAndrew Deason #else
92c8ec041dSAndrew Deason     const char *powerdown_flag = "-P";
93c8ec041dSAndrew Deason     const char *halt_flag = "-H";
94c8ec041dSAndrew Deason     const char *reboot_flag = "-r";
95c8ec041dSAndrew Deason #endif
96c8ec041dSAndrew Deason 
97c216e5adSMichael Roth     slog("guest-shutdown called, mode: %s", mode);
9891eab32aSMarkus Armbruster     if (!mode || strcmp(mode, "powerdown") == 0) {
99c8ec041dSAndrew Deason         shutdown_flag = powerdown_flag;
100c216e5adSMichael Roth     } else if (strcmp(mode, "halt") == 0) {
101c8ec041dSAndrew Deason         shutdown_flag = halt_flag;
102c216e5adSMichael Roth     } else if (strcmp(mode, "reboot") == 0) {
103c8ec041dSAndrew Deason         shutdown_flag = reboot_flag;
104c216e5adSMichael Roth     } else {
10577dbc81bSMarkus Armbruster         error_setg(errp,
106d220a6dfSLuiz Capitulino                    "mode is invalid (valid values are: halt|powerdown|reboot");
107c216e5adSMichael Roth         return;
108c216e5adSMichael Roth     }
109c216e5adSMichael Roth 
110d5dd3498SLuiz Capitulino     pid = fork();
111d5dd3498SLuiz Capitulino     if (pid == 0) {
112c216e5adSMichael Roth         /* child, start the shutdown */
113c216e5adSMichael Roth         setsid();
1143674838cSLuiz Capitulino         reopen_fd_to_null(0);
1153674838cSLuiz Capitulino         reopen_fd_to_null(1);
1163674838cSLuiz Capitulino         reopen_fd_to_null(2);
117c216e5adSMichael Roth 
118c8ec041dSAndrew Deason #ifdef CONFIG_SOLARIS
119c8ec041dSAndrew Deason         execl("/sbin/shutdown", "shutdown", shutdown_flag, "-g0", "-y",
120c8ec041dSAndrew Deason               "hypervisor initiated shutdown", (char *)NULL);
121e40762fcSAlexander Ivanov #elif defined(CONFIG_BSD)
122e40762fcSAlexander Ivanov         execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
123e40762fcSAlexander Ivanov                "hypervisor initiated shutdown", (char *)NULL);
124c8ec041dSAndrew Deason #else
125fcc41961SMarc-André Lureau         execl("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0",
126fcc41961SMarc-André Lureau                "hypervisor initiated shutdown", (char *)NULL);
127c8ec041dSAndrew Deason #endif
1283674838cSLuiz Capitulino         _exit(EXIT_FAILURE);
129d5dd3498SLuiz Capitulino     } else if (pid < 0) {
13077dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to create child process");
131d5dd3498SLuiz Capitulino         return;
132d5dd3498SLuiz Capitulino     }
133d5dd3498SLuiz Capitulino 
134d220a6dfSLuiz Capitulino     ga_wait_child(pid, &status, &local_err);
13584d18f06SMarkus Armbruster     if (local_err) {
13677dbc81bSMarkus Armbruster         error_propagate(errp, local_err);
137d220a6dfSLuiz Capitulino         return;
138d220a6dfSLuiz Capitulino     }
139d220a6dfSLuiz Capitulino 
140d220a6dfSLuiz Capitulino     if (!WIFEXITED(status)) {
14177dbc81bSMarkus Armbruster         error_setg(errp, "child process has terminated abnormally");
142d220a6dfSLuiz Capitulino         return;
143d220a6dfSLuiz Capitulino     }
144d220a6dfSLuiz Capitulino 
145d220a6dfSLuiz Capitulino     if (WEXITSTATUS(status)) {
14677dbc81bSMarkus Armbruster         error_setg(errp, "child process has failed to shutdown");
147d220a6dfSLuiz Capitulino         return;
148d220a6dfSLuiz Capitulino     }
149d220a6dfSLuiz Capitulino 
150085d8134SPeter Maydell     /* succeeded */
151c216e5adSMichael Roth }
152c216e5adSMichael Roth 
qmp_guest_set_time(bool has_time,int64_t time_ns,Error ** errp)1532c958923SMichal Privoznik void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
154a1bca57fSLei Li {
155a1bca57fSLei Li     int ret;
156a1bca57fSLei Li     int status;
157a1bca57fSLei Li     pid_t pid;
158a1bca57fSLei Li     Error *local_err = NULL;
159a1bca57fSLei Li     struct timeval tv;
1605c6096e5SCornelia Huck     static const char hwclock_path[] = "/sbin/hwclock";
1615c6096e5SCornelia Huck     static int hwclock_available = -1;
1625c6096e5SCornelia Huck 
1635c6096e5SCornelia Huck     if (hwclock_available < 0) {
1645c6096e5SCornelia Huck         hwclock_available = (access(hwclock_path, X_OK) == 0);
1655c6096e5SCornelia Huck     }
1665c6096e5SCornelia Huck 
1675c6096e5SCornelia Huck     if (!hwclock_available) {
1685c6096e5SCornelia Huck         error_setg(errp, QERR_UNSUPPORTED);
1695c6096e5SCornelia Huck         return;
1705c6096e5SCornelia Huck     }
171a1bca57fSLei Li 
1722c958923SMichal Privoznik     /* If user has passed a time, validate and set it. */
1732c958923SMichal Privoznik     if (has_time) {
17400d2f370SMarc-André Lureau         GDate date = { 0, };
17500d2f370SMarc-André Lureau 
176a1bca57fSLei Li         /* year-2038 will overflow in case time_t is 32bit */
177a1bca57fSLei Li         if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) {
178a1bca57fSLei Li             error_setg(errp, "Time %" PRId64 " is too large", time_ns);
179a1bca57fSLei Li             return;
180a1bca57fSLei Li         }
181a1bca57fSLei Li 
182a1bca57fSLei Li         tv.tv_sec = time_ns / 1000000000;
183a1bca57fSLei Li         tv.tv_usec = (time_ns % 1000000000) / 1000;
18400d2f370SMarc-André Lureau         g_date_set_time_t(&date, tv.tv_sec);
18500d2f370SMarc-André Lureau         if (date.year < 1970 || date.year >= 2070) {
18600d2f370SMarc-André Lureau             error_setg_errno(errp, errno, "Invalid time");
18700d2f370SMarc-André Lureau             return;
18800d2f370SMarc-André Lureau         }
189a1bca57fSLei Li 
190a1bca57fSLei Li         ret = settimeofday(&tv, NULL);
191a1bca57fSLei Li         if (ret < 0) {
192a1bca57fSLei Li             error_setg_errno(errp, errno, "Failed to set time to guest");
193a1bca57fSLei Li             return;
194a1bca57fSLei Li         }
1952c958923SMichal Privoznik     }
196a1bca57fSLei Li 
1972c958923SMichal Privoznik     /* Now, if user has passed a time to set and the system time is set, we
1982c958923SMichal Privoznik      * just need to synchronize the hardware clock. However, if no time was
1992c958923SMichal Privoznik      * passed, user is requesting the opposite: set the system time from the
2001634df56SAmos Kong      * hardware clock (RTC). */
201a1bca57fSLei Li     pid = fork();
202a1bca57fSLei Li     if (pid == 0) {
203a1bca57fSLei Li         setsid();
204a1bca57fSLei Li         reopen_fd_to_null(0);
205a1bca57fSLei Li         reopen_fd_to_null(1);
206a1bca57fSLei Li         reopen_fd_to_null(2);
207a1bca57fSLei Li 
2082c958923SMichal Privoznik         /* Use '/sbin/hwclock -w' to set RTC from the system time,
2092c958923SMichal Privoznik          * or '/sbin/hwclock -s' to set the system time from RTC. */
210fcc41961SMarc-André Lureau         execl(hwclock_path, "hwclock", has_time ? "-w" : "-s", NULL);
211a1bca57fSLei Li         _exit(EXIT_FAILURE);
212a1bca57fSLei Li     } else if (pid < 0) {
213a1bca57fSLei Li         error_setg_errno(errp, errno, "failed to create child process");
214a1bca57fSLei Li         return;
215a1bca57fSLei Li     }
216a1bca57fSLei Li 
217a1bca57fSLei Li     ga_wait_child(pid, &status, &local_err);
21884d18f06SMarkus Armbruster     if (local_err) {
219a1bca57fSLei Li         error_propagate(errp, local_err);
220a1bca57fSLei Li         return;
221a1bca57fSLei Li     }
222a1bca57fSLei Li 
223a1bca57fSLei Li     if (!WIFEXITED(status)) {
224a1bca57fSLei Li         error_setg(errp, "child process has terminated abnormally");
225a1bca57fSLei Li         return;
226a1bca57fSLei Li     }
227a1bca57fSLei Li 
228a1bca57fSLei Li     if (WEXITSTATUS(status)) {
229a1bca57fSLei Li         error_setg(errp, "hwclock failed to set hardware clock to system time");
230a1bca57fSLei Li         return;
231a1bca57fSLei Li     }
232a1bca57fSLei Li }
233a1bca57fSLei Li 
234895b00f6SMarc-André Lureau typedef enum {
235895b00f6SMarc-André Lureau     RW_STATE_NEW,
236895b00f6SMarc-André Lureau     RW_STATE_READING,
237895b00f6SMarc-André Lureau     RW_STATE_WRITING,
238895b00f6SMarc-André Lureau } RwState;
239895b00f6SMarc-André Lureau 
2405d3586b8SPhilippe Mathieu-Daudé struct GuestFileHandle {
241c216e5adSMichael Roth     uint64_t id;
242c216e5adSMichael Roth     FILE *fh;
243895b00f6SMarc-André Lureau     RwState state;
244c216e5adSMichael Roth     QTAILQ_ENTRY(GuestFileHandle) next;
2455d3586b8SPhilippe Mathieu-Daudé };
246c216e5adSMichael Roth 
247c216e5adSMichael Roth static struct {
248c216e5adSMichael Roth     QTAILQ_HEAD(, GuestFileHandle) filehandles;
249b4fe97c8SDenis V. Lunev } guest_file_state = {
250b4fe97c8SDenis V. Lunev     .filehandles = QTAILQ_HEAD_INITIALIZER(guest_file_state.filehandles),
251b4fe97c8SDenis V. Lunev };
252c216e5adSMichael Roth 
guest_file_handle_add(FILE * fh,Error ** errp)25339097dafSMichael Roth static int64_t guest_file_handle_add(FILE *fh, Error **errp)
254c216e5adSMichael Roth {
255c216e5adSMichael Roth     GuestFileHandle *gfh;
25639097dafSMichael Roth     int64_t handle;
25739097dafSMichael Roth 
25839097dafSMichael Roth     handle = ga_get_fd_handle(ga_state, errp);
259a903f40cSMarkus Armbruster     if (handle < 0) {
260a903f40cSMarkus Armbruster         return -1;
26139097dafSMichael Roth     }
262c216e5adSMichael Roth 
263f3a06403SMarkus Armbruster     gfh = g_new0(GuestFileHandle, 1);
26439097dafSMichael Roth     gfh->id = handle;
265c216e5adSMichael Roth     gfh->fh = fh;
266c216e5adSMichael Roth     QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
26739097dafSMichael Roth 
26839097dafSMichael Roth     return handle;
269c216e5adSMichael Roth }
270c216e5adSMichael Roth 
guest_file_handle_find(int64_t id,Error ** errp)2715d3586b8SPhilippe Mathieu-Daudé GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp)
272c216e5adSMichael Roth {
273c216e5adSMichael Roth     GuestFileHandle *gfh;
274c216e5adSMichael Roth 
275c216e5adSMichael Roth     QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next)
276c216e5adSMichael Roth     {
277c216e5adSMichael Roth         if (gfh->id == id) {
278c216e5adSMichael Roth             return gfh;
279c216e5adSMichael Roth         }
280c216e5adSMichael Roth     }
281c216e5adSMichael Roth 
28277dbc81bSMarkus Armbruster     error_setg(errp, "handle '%" PRId64 "' has not been found", id);
283c216e5adSMichael Roth     return NULL;
284c216e5adSMichael Roth }
285c216e5adSMichael Roth 
286c689b4f1SLaszlo Ersek typedef const char * const ccpc;
287c689b4f1SLaszlo Ersek 
2888fe6bbcaSLaszlo Ersek #ifndef O_BINARY
2898fe6bbcaSLaszlo Ersek #define O_BINARY 0
2908fe6bbcaSLaszlo Ersek #endif
2918fe6bbcaSLaszlo Ersek 
292c689b4f1SLaszlo Ersek /* http://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html */
293c689b4f1SLaszlo Ersek static const struct {
294c689b4f1SLaszlo Ersek     ccpc *forms;
295c689b4f1SLaszlo Ersek     int oflag_base;
296c689b4f1SLaszlo Ersek } guest_file_open_modes[] = {
2978fe6bbcaSLaszlo Ersek     { (ccpc[]){ "r",          NULL }, O_RDONLY                                 },
2988fe6bbcaSLaszlo Ersek     { (ccpc[]){ "rb",         NULL }, O_RDONLY                      | O_BINARY },
2998fe6bbcaSLaszlo Ersek     { (ccpc[]){ "w",          NULL }, O_WRONLY | O_CREAT | O_TRUNC             },
3008fe6bbcaSLaszlo Ersek     { (ccpc[]){ "wb",         NULL }, O_WRONLY | O_CREAT | O_TRUNC  | O_BINARY },
3018fe6bbcaSLaszlo Ersek     { (ccpc[]){ "a",          NULL }, O_WRONLY | O_CREAT | O_APPEND            },
3028fe6bbcaSLaszlo Ersek     { (ccpc[]){ "ab",         NULL }, O_WRONLY | O_CREAT | O_APPEND | O_BINARY },
3038fe6bbcaSLaszlo Ersek     { (ccpc[]){ "r+",         NULL }, O_RDWR                                   },
3048fe6bbcaSLaszlo Ersek     { (ccpc[]){ "rb+", "r+b", NULL }, O_RDWR                        | O_BINARY },
3058fe6bbcaSLaszlo Ersek     { (ccpc[]){ "w+",         NULL }, O_RDWR   | O_CREAT | O_TRUNC             },
3068fe6bbcaSLaszlo Ersek     { (ccpc[]){ "wb+", "w+b", NULL }, O_RDWR   | O_CREAT | O_TRUNC  | O_BINARY },
3078fe6bbcaSLaszlo Ersek     { (ccpc[]){ "a+",         NULL }, O_RDWR   | O_CREAT | O_APPEND            },
3088fe6bbcaSLaszlo Ersek     { (ccpc[]){ "ab+", "a+b", NULL }, O_RDWR   | O_CREAT | O_APPEND | O_BINARY }
309c689b4f1SLaszlo Ersek };
310c689b4f1SLaszlo Ersek 
311c689b4f1SLaszlo Ersek static int
find_open_flag(const char * mode_str,Error ** errp)31277dbc81bSMarkus Armbruster find_open_flag(const char *mode_str, Error **errp)
313c689b4f1SLaszlo Ersek {
314c689b4f1SLaszlo Ersek     unsigned mode;
315c689b4f1SLaszlo Ersek 
316c689b4f1SLaszlo Ersek     for (mode = 0; mode < ARRAY_SIZE(guest_file_open_modes); ++mode) {
317c689b4f1SLaszlo Ersek         ccpc *form;
318c689b4f1SLaszlo Ersek 
319c689b4f1SLaszlo Ersek         form = guest_file_open_modes[mode].forms;
320c689b4f1SLaszlo Ersek         while (*form != NULL && strcmp(*form, mode_str) != 0) {
321c689b4f1SLaszlo Ersek             ++form;
322c689b4f1SLaszlo Ersek         }
323c689b4f1SLaszlo Ersek         if (*form != NULL) {
324c689b4f1SLaszlo Ersek             break;
325c689b4f1SLaszlo Ersek         }
326c689b4f1SLaszlo Ersek     }
327c689b4f1SLaszlo Ersek 
328c689b4f1SLaszlo Ersek     if (mode == ARRAY_SIZE(guest_file_open_modes)) {
32977dbc81bSMarkus Armbruster         error_setg(errp, "invalid file open mode '%s'", mode_str);
330c689b4f1SLaszlo Ersek         return -1;
331c689b4f1SLaszlo Ersek     }
332c689b4f1SLaszlo Ersek     return guest_file_open_modes[mode].oflag_base | O_NOCTTY | O_NONBLOCK;
333c689b4f1SLaszlo Ersek }
334c689b4f1SLaszlo Ersek 
335c689b4f1SLaszlo Ersek #define DEFAULT_NEW_FILE_MODE (S_IRUSR | S_IWUSR | \
336c689b4f1SLaszlo Ersek                                S_IRGRP | S_IWGRP | \
337c689b4f1SLaszlo Ersek                                S_IROTH | S_IWOTH)
338c689b4f1SLaszlo Ersek 
339c689b4f1SLaszlo Ersek static FILE *
safe_open_or_create(const char * path,const char * mode,Error ** errp)34077dbc81bSMarkus Armbruster safe_open_or_create(const char *path, const char *mode, Error **errp)
341c689b4f1SLaszlo Ersek {
342c689b4f1SLaszlo Ersek     int oflag;
34369f56c14SMarc-André Lureau     int fd = -1;
34469f56c14SMarc-André Lureau     FILE *f = NULL;
345c689b4f1SLaszlo Ersek 
34669f56c14SMarc-André Lureau     oflag = find_open_flag(mode, errp);
34769f56c14SMarc-André Lureau     if (oflag < 0) {
34869f56c14SMarc-André Lureau         goto end;
34969f56c14SMarc-André Lureau     }
350c689b4f1SLaszlo Ersek 
351c689b4f1SLaszlo Ersek     /* If the caller wants / allows creation of a new file, we implement it
352c689b4f1SLaszlo Ersek      * with a two step process: open() + (open() / fchmod()).
353c689b4f1SLaszlo Ersek      *
354c689b4f1SLaszlo Ersek      * First we insist on creating the file exclusively as a new file. If
355c689b4f1SLaszlo Ersek      * that succeeds, we're free to set any file-mode bits on it. (The
356c689b4f1SLaszlo Ersek      * motivation is that we want to set those file-mode bits independently
357c689b4f1SLaszlo Ersek      * of the current umask.)
358c689b4f1SLaszlo Ersek      *
359c689b4f1SLaszlo Ersek      * If the exclusive creation fails because the file already exists
360c689b4f1SLaszlo Ersek      * (EEXIST is not possible for any other reason), we just attempt to
361c689b4f1SLaszlo Ersek      * open the file, but in this case we won't be allowed to change the
362c689b4f1SLaszlo Ersek      * file-mode bits on the preexistent file.
363c689b4f1SLaszlo Ersek      *
364c689b4f1SLaszlo Ersek      * The pathname should never disappear between the two open()s in
365c689b4f1SLaszlo Ersek      * practice. If it happens, then someone very likely tried to race us.
366c689b4f1SLaszlo Ersek      * In this case just go ahead and report the ENOENT from the second
367c689b4f1SLaszlo Ersek      * open() to the caller.
368c689b4f1SLaszlo Ersek      *
369c689b4f1SLaszlo Ersek      * If the caller wants to open a preexistent file, then the first
370c689b4f1SLaszlo Ersek      * open() is decisive and its third argument is ignored, and the second
371c689b4f1SLaszlo Ersek      * open() and the fchmod() are never called.
372c689b4f1SLaszlo Ersek      */
3731a89a17bSMarc-André Lureau     fd = qga_open_cloexec(path, oflag | ((oflag & O_CREAT) ? O_EXCL : 0), 0);
374c689b4f1SLaszlo Ersek     if (fd == -1 && errno == EEXIST) {
375c689b4f1SLaszlo Ersek         oflag &= ~(unsigned)O_CREAT;
3761a89a17bSMarc-André Lureau         fd = qga_open_cloexec(path, oflag, 0);
377c689b4f1SLaszlo Ersek     }
378c689b4f1SLaszlo Ersek     if (fd == -1) {
37969f56c14SMarc-André Lureau         error_setg_errno(errp, errno,
38069f56c14SMarc-André Lureau                          "failed to open file '%s' (mode: '%s')",
38169f56c14SMarc-André Lureau                          path, mode);
38269f56c14SMarc-André Lureau         goto end;
38369f56c14SMarc-André Lureau     }
38469f56c14SMarc-André Lureau 
385c689b4f1SLaszlo Ersek     if ((oflag & O_CREAT) && fchmod(fd, DEFAULT_NEW_FILE_MODE) == -1) {
38669f56c14SMarc-André Lureau         error_setg_errno(errp, errno, "failed to set permission "
387c689b4f1SLaszlo Ersek                          "0%03o on new file '%s' (mode: '%s')",
388c689b4f1SLaszlo Ersek                          (unsigned)DEFAULT_NEW_FILE_MODE, path, mode);
38969f56c14SMarc-André Lureau         goto end;
39069f56c14SMarc-André Lureau     }
391c689b4f1SLaszlo Ersek 
392c689b4f1SLaszlo Ersek     f = fdopen(fd, mode);
393c689b4f1SLaszlo Ersek     if (f == NULL) {
39469f56c14SMarc-André Lureau         error_setg_errno(errp, errno, "failed to associate stdio stream with "
39569f56c14SMarc-André Lureau                          "file descriptor %d, file '%s' (mode: '%s')",
39669f56c14SMarc-André Lureau                          fd, path, mode);
397c689b4f1SLaszlo Ersek     }
398c689b4f1SLaszlo Ersek 
39969f56c14SMarc-André Lureau end:
40069f56c14SMarc-André Lureau     if (f == NULL && fd != -1) {
401c689b4f1SLaszlo Ersek         close(fd);
4022b720018SLaszlo Ersek         if (oflag & O_CREAT) {
4032b720018SLaszlo Ersek             unlink(path);
4042b720018SLaszlo Ersek         }
405c689b4f1SLaszlo Ersek     }
40669f56c14SMarc-André Lureau     return f;
407c689b4f1SLaszlo Ersek }
408c689b4f1SLaszlo Ersek 
qmp_guest_file_open(const char * path,const char * mode,Error ** errp)40991eab32aSMarkus Armbruster int64_t qmp_guest_file_open(const char *path, const char *mode,
41077dbc81bSMarkus Armbruster                             Error **errp)
411c216e5adSMichael Roth {
412c216e5adSMichael Roth     FILE *fh;
413c689b4f1SLaszlo Ersek     Error *local_err = NULL;
41485b6f6f5SSimon Zolin     int64_t handle;
415c216e5adSMichael Roth 
41691eab32aSMarkus Armbruster     if (!mode) {
417c216e5adSMichael Roth         mode = "r";
418c216e5adSMichael Roth     }
419c216e5adSMichael Roth     slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
420c689b4f1SLaszlo Ersek     fh = safe_open_or_create(path, mode, &local_err);
421c689b4f1SLaszlo Ersek     if (local_err != NULL) {
42277dbc81bSMarkus Armbruster         error_propagate(errp, local_err);
423c216e5adSMichael Roth         return -1;
424c216e5adSMichael Roth     }
425c216e5adSMichael Roth 
426c216e5adSMichael Roth     /* set fd non-blocking to avoid common use cases (like reading from a
427c216e5adSMichael Roth      * named pipe) from hanging the agent
428c216e5adSMichael Roth      */
429b0a8f9adSMarc-André Lureau     if (!g_unix_set_fd_nonblocking(fileno(fh), true, NULL)) {
430b0a8f9adSMarc-André Lureau         fclose(fh);
431b0a8f9adSMarc-André Lureau         error_setg_errno(errp, errno, "Failed to set FD nonblocking");
432b0a8f9adSMarc-André Lureau         return -1;
433b0a8f9adSMarc-André Lureau     }
434c216e5adSMichael Roth 
43577dbc81bSMarkus Armbruster     handle = guest_file_handle_add(fh, errp);
436a903f40cSMarkus Armbruster     if (handle < 0) {
43739097dafSMichael Roth         fclose(fh);
43839097dafSMichael Roth         return -1;
43939097dafSMichael Roth     }
44039097dafSMichael Roth 
441d607a523SStefan Weil     slog("guest-file-open, handle: %" PRId64, handle);
44239097dafSMichael Roth     return handle;
443c216e5adSMichael Roth }
444c216e5adSMichael Roth 
qmp_guest_file_close(int64_t handle,Error ** errp)44577dbc81bSMarkus Armbruster void qmp_guest_file_close(int64_t handle, Error **errp)
446c216e5adSMichael Roth {
44777dbc81bSMarkus Armbruster     GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
448c216e5adSMichael Roth     int ret;
449c216e5adSMichael Roth 
450d607a523SStefan Weil     slog("guest-file-close called, handle: %" PRId64, handle);
451c216e5adSMichael Roth     if (!gfh) {
452c216e5adSMichael Roth         return;
453c216e5adSMichael Roth     }
454c216e5adSMichael Roth 
455c216e5adSMichael Roth     ret = fclose(gfh->fh);
4563ac4b7c5SLuiz Capitulino     if (ret == EOF) {
45777dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to close handle");
458c216e5adSMichael Roth         return;
459c216e5adSMichael Roth     }
460c216e5adSMichael Roth 
461c216e5adSMichael Roth     QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
462c216e5adSMichael Roth     g_free(gfh);
463c216e5adSMichael Roth }
464c216e5adSMichael Roth 
guest_file_read_unsafe(GuestFileHandle * gfh,int64_t count,Error ** errp)465ead83a13SPhilippe Mathieu-Daudé GuestFileRead *guest_file_read_unsafe(GuestFileHandle *gfh,
46677dbc81bSMarkus Armbruster                                       int64_t count, Error **errp)
467c216e5adSMichael Roth {
468c216e5adSMichael Roth     GuestFileRead *read_data = NULL;
469c216e5adSMichael Roth     guchar *buf;
470ead83a13SPhilippe Mathieu-Daudé     FILE *fh = gfh->fh;
471c216e5adSMichael Roth     size_t read_count;
472c216e5adSMichael Roth 
473895b00f6SMarc-André Lureau     /* explicitly flush when switching from writing to reading */
474895b00f6SMarc-André Lureau     if (gfh->state == RW_STATE_WRITING) {
475895b00f6SMarc-André Lureau         int ret = fflush(fh);
476895b00f6SMarc-André Lureau         if (ret == EOF) {
477895b00f6SMarc-André Lureau             error_setg_errno(errp, errno, "failed to flush file");
478895b00f6SMarc-André Lureau             return NULL;
479895b00f6SMarc-André Lureau         }
480895b00f6SMarc-André Lureau         gfh->state = RW_STATE_NEW;
481895b00f6SMarc-André Lureau     }
482895b00f6SMarc-André Lureau 
483c216e5adSMichael Roth     buf = g_malloc0(count + 1);
484c216e5adSMichael Roth     read_count = fread(buf, 1, count, fh);
485c216e5adSMichael Roth     if (ferror(fh)) {
48677dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to read file");
487c216e5adSMichael Roth     } else {
488c216e5adSMichael Roth         buf[read_count] = 0;
489f3a06403SMarkus Armbruster         read_data = g_new0(GuestFileRead, 1);
490c216e5adSMichael Roth         read_data->count = read_count;
491c216e5adSMichael Roth         read_data->eof = feof(fh);
492c216e5adSMichael Roth         if (read_count) {
493c216e5adSMichael Roth             read_data->buf_b64 = g_base64_encode(buf, read_count);
494c216e5adSMichael Roth         }
495895b00f6SMarc-André Lureau         gfh->state = RW_STATE_READING;
496c216e5adSMichael Roth     }
497c216e5adSMichael Roth     g_free(buf);
498c216e5adSMichael Roth     clearerr(fh);
499c216e5adSMichael Roth 
500c216e5adSMichael Roth     return read_data;
501c216e5adSMichael Roth }
502c216e5adSMichael Roth 
qmp_guest_file_write(int64_t handle,const char * buf_b64,bool has_count,int64_t count,Error ** errp)503c216e5adSMichael Roth GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
50477dbc81bSMarkus Armbruster                                      bool has_count, int64_t count,
50577dbc81bSMarkus Armbruster                                      Error **errp)
506c216e5adSMichael Roth {
507c216e5adSMichael Roth     GuestFileWrite *write_data = NULL;
508c216e5adSMichael Roth     guchar *buf;
509c216e5adSMichael Roth     gsize buf_len;
510c216e5adSMichael Roth     int write_count;
51177dbc81bSMarkus Armbruster     GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
512c216e5adSMichael Roth     FILE *fh;
513c216e5adSMichael Roth 
514c216e5adSMichael Roth     if (!gfh) {
515c216e5adSMichael Roth         return NULL;
516c216e5adSMichael Roth     }
517c216e5adSMichael Roth 
518c216e5adSMichael Roth     fh = gfh->fh;
519895b00f6SMarc-André Lureau 
520895b00f6SMarc-André Lureau     if (gfh->state == RW_STATE_READING) {
521895b00f6SMarc-André Lureau         int ret = fseek(fh, 0, SEEK_CUR);
522895b00f6SMarc-André Lureau         if (ret == -1) {
523895b00f6SMarc-André Lureau             error_setg_errno(errp, errno, "failed to seek file");
524895b00f6SMarc-André Lureau             return NULL;
525895b00f6SMarc-André Lureau         }
526895b00f6SMarc-André Lureau         gfh->state = RW_STATE_NEW;
527895b00f6SMarc-André Lureau     }
528895b00f6SMarc-André Lureau 
529920639caSDaniel P. Berrange     buf = qbase64_decode(buf_b64, -1, &buf_len, errp);
530920639caSDaniel P. Berrange     if (!buf) {
531920639caSDaniel P. Berrange         return NULL;
532920639caSDaniel P. Berrange     }
533c216e5adSMichael Roth 
534c216e5adSMichael Roth     if (!has_count) {
535c216e5adSMichael Roth         count = buf_len;
536c216e5adSMichael Roth     } else if (count < 0 || count > buf_len) {
53777dbc81bSMarkus Armbruster         error_setg(errp, "value '%" PRId64 "' is invalid for argument count",
538db3edb66SLuiz Capitulino                    count);
539c216e5adSMichael Roth         g_free(buf);
540c216e5adSMichael Roth         return NULL;
541c216e5adSMichael Roth     }
542c216e5adSMichael Roth 
543c216e5adSMichael Roth     write_count = fwrite(buf, 1, count, fh);
544c216e5adSMichael Roth     if (ferror(fh)) {
54577dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to write to file");
546d607a523SStefan Weil         slog("guest-file-write failed, handle: %" PRId64, handle);
547c216e5adSMichael Roth     } else {
548f3a06403SMarkus Armbruster         write_data = g_new0(GuestFileWrite, 1);
549c216e5adSMichael Roth         write_data->count = write_count;
550c216e5adSMichael Roth         write_data->eof = feof(fh);
551895b00f6SMarc-André Lureau         gfh->state = RW_STATE_WRITING;
552c216e5adSMichael Roth     }
553c216e5adSMichael Roth     g_free(buf);
554c216e5adSMichael Roth     clearerr(fh);
555c216e5adSMichael Roth 
556c216e5adSMichael Roth     return write_data;
557c216e5adSMichael Roth }
558c216e5adSMichael Roth 
qmp_guest_file_seek(int64_t handle,int64_t offset,GuestFileWhence * whence_code,Error ** errp)559c216e5adSMichael Roth struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
5600b4b4938SEric Blake                                           GuestFileWhence *whence_code,
5610b4b4938SEric Blake                                           Error **errp)
562c216e5adSMichael Roth {
56377dbc81bSMarkus Armbruster     GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
564c216e5adSMichael Roth     GuestFileSeek *seek_data = NULL;
565c216e5adSMichael Roth     FILE *fh;
566c216e5adSMichael Roth     int ret;
5670a982b1bSEric Blake     int whence;
5680b4b4938SEric Blake     Error *err = NULL;
569c216e5adSMichael Roth 
570c216e5adSMichael Roth     if (!gfh) {
571c216e5adSMichael Roth         return NULL;
572c216e5adSMichael Roth     }
573c216e5adSMichael Roth 
5740a982b1bSEric Blake     /* We stupidly exposed 'whence':'int' in our qapi */
5750b4b4938SEric Blake     whence = ga_parse_whence(whence_code, &err);
5760b4b4938SEric Blake     if (err) {
5770b4b4938SEric Blake         error_propagate(errp, err);
5780a982b1bSEric Blake         return NULL;
5790a982b1bSEric Blake     }
5800a982b1bSEric Blake 
581c216e5adSMichael Roth     fh = gfh->fh;
582c216e5adSMichael Roth     ret = fseek(fh, offset, whence);
583c216e5adSMichael Roth     if (ret == -1) {
58477dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to seek file");
585895b00f6SMarc-André Lureau         if (errno == ESPIPE) {
586895b00f6SMarc-André Lureau             /* file is non-seekable, stdio shouldn't be buffering anyways */
587895b00f6SMarc-André Lureau             gfh->state = RW_STATE_NEW;
588895b00f6SMarc-André Lureau         }
589c216e5adSMichael Roth     } else {
59010b7c5ddSMarkus Armbruster         seek_data = g_new0(GuestFileSeek, 1);
591c216e5adSMichael Roth         seek_data->position = ftell(fh);
592c216e5adSMichael Roth         seek_data->eof = feof(fh);
593895b00f6SMarc-André Lureau         gfh->state = RW_STATE_NEW;
594c216e5adSMichael Roth     }
595c216e5adSMichael Roth     clearerr(fh);
596c216e5adSMichael Roth 
597c216e5adSMichael Roth     return seek_data;
598c216e5adSMichael Roth }
599c216e5adSMichael Roth 
qmp_guest_file_flush(int64_t handle,Error ** errp)60077dbc81bSMarkus Armbruster void qmp_guest_file_flush(int64_t handle, Error **errp)
601c216e5adSMichael Roth {
60277dbc81bSMarkus Armbruster     GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
603c216e5adSMichael Roth     FILE *fh;
604c216e5adSMichael Roth     int ret;
605c216e5adSMichael Roth 
606c216e5adSMichael Roth     if (!gfh) {
607c216e5adSMichael Roth         return;
608c216e5adSMichael Roth     }
609c216e5adSMichael Roth 
610c216e5adSMichael Roth     fh = gfh->fh;
611c216e5adSMichael Roth     ret = fflush(fh);
612c216e5adSMichael Roth     if (ret == EOF) {
61377dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to flush file");
614895b00f6SMarc-André Lureau     } else {
615895b00f6SMarc-André Lureau         gfh->state = RW_STATE_NEW;
616c216e5adSMichael Roth     }
617c216e5adSMichael Roth }
618c216e5adSMichael Roth 
619eab5fd59SPaolo Bonzini #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
free_fs_mount_list(FsMountList * mounts)620518b0d80SAlexander Ivanov void free_fs_mount_list(FsMountList *mounts)
621c216e5adSMichael Roth {
622af02203fSPaolo Bonzini      FsMount *mount, *temp;
623c216e5adSMichael Roth 
6249e8aded4SMichael Roth      if (!mounts) {
6259e8aded4SMichael Roth          return;
6269e8aded4SMichael Roth      }
6279e8aded4SMichael Roth 
6289e8aded4SMichael Roth      QTAILQ_FOREACH_SAFE(mount, mounts, next, temp) {
6299e8aded4SMichael Roth          QTAILQ_REMOVE(mounts, mount, next);
630c216e5adSMichael Roth          g_free(mount->dirname);
631c216e5adSMichael Roth          g_free(mount->devtype);
632c216e5adSMichael Roth          g_free(mount);
633c216e5adSMichael Roth      }
6349e8aded4SMichael Roth }
635eab5fd59SPaolo Bonzini #endif
636eab5fd59SPaolo Bonzini 
637eab5fd59SPaolo Bonzini #if defined(CONFIG_FSFREEZE)
638bad0001eSAlexander Ivanov typedef enum {
639bad0001eSAlexander Ivanov     FSFREEZE_HOOK_THAW = 0,
640bad0001eSAlexander Ivanov     FSFREEZE_HOOK_FREEZE,
641bad0001eSAlexander Ivanov } FsfreezeHookArg;
642bad0001eSAlexander Ivanov 
643bad0001eSAlexander Ivanov static const char *fsfreeze_hook_arg_string[] = {
644bad0001eSAlexander Ivanov     "thaw",
645bad0001eSAlexander Ivanov     "freeze",
646bad0001eSAlexander Ivanov };
647bad0001eSAlexander Ivanov 
execute_fsfreeze_hook(FsfreezeHookArg arg,Error ** errp)648bad0001eSAlexander Ivanov static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp)
649bad0001eSAlexander Ivanov {
650bad0001eSAlexander Ivanov     int status;
651bad0001eSAlexander Ivanov     pid_t pid;
652bad0001eSAlexander Ivanov     const char *hook;
653bad0001eSAlexander Ivanov     const char *arg_str = fsfreeze_hook_arg_string[arg];
654bad0001eSAlexander Ivanov     Error *local_err = NULL;
655bad0001eSAlexander Ivanov 
656bad0001eSAlexander Ivanov     hook = ga_fsfreeze_hook(ga_state);
657bad0001eSAlexander Ivanov     if (!hook) {
658bad0001eSAlexander Ivanov         return;
659bad0001eSAlexander Ivanov     }
660bad0001eSAlexander Ivanov     if (access(hook, X_OK) != 0) {
661bad0001eSAlexander Ivanov         error_setg_errno(errp, errno, "can't access fsfreeze hook '%s'", hook);
662bad0001eSAlexander Ivanov         return;
663bad0001eSAlexander Ivanov     }
664bad0001eSAlexander Ivanov 
665bad0001eSAlexander Ivanov     slog("executing fsfreeze hook with arg '%s'", arg_str);
666bad0001eSAlexander Ivanov     pid = fork();
667bad0001eSAlexander Ivanov     if (pid == 0) {
668bad0001eSAlexander Ivanov         setsid();
669bad0001eSAlexander Ivanov         reopen_fd_to_null(0);
670bad0001eSAlexander Ivanov         reopen_fd_to_null(1);
671bad0001eSAlexander Ivanov         reopen_fd_to_null(2);
672bad0001eSAlexander Ivanov 
673bad0001eSAlexander Ivanov         execl(hook, hook, arg_str, NULL);
674bad0001eSAlexander Ivanov         _exit(EXIT_FAILURE);
675bad0001eSAlexander Ivanov     } else if (pid < 0) {
676bad0001eSAlexander Ivanov         error_setg_errno(errp, errno, "failed to create child process");
677bad0001eSAlexander Ivanov         return;
678bad0001eSAlexander Ivanov     }
679bad0001eSAlexander Ivanov 
680bad0001eSAlexander Ivanov     ga_wait_child(pid, &status, &local_err);
681bad0001eSAlexander Ivanov     if (local_err) {
682bad0001eSAlexander Ivanov         error_propagate(errp, local_err);
683bad0001eSAlexander Ivanov         return;
684bad0001eSAlexander Ivanov     }
685bad0001eSAlexander Ivanov 
686bad0001eSAlexander Ivanov     if (!WIFEXITED(status)) {
687bad0001eSAlexander Ivanov         error_setg(errp, "fsfreeze hook has terminated abnormally");
688bad0001eSAlexander Ivanov         return;
689bad0001eSAlexander Ivanov     }
690bad0001eSAlexander Ivanov 
691bad0001eSAlexander Ivanov     status = WEXITSTATUS(status);
692bad0001eSAlexander Ivanov     if (status) {
693bad0001eSAlexander Ivanov         error_setg(errp, "fsfreeze hook has failed with status %d", status);
694bad0001eSAlexander Ivanov         return;
695bad0001eSAlexander Ivanov     }
696bad0001eSAlexander Ivanov }
697bad0001eSAlexander Ivanov 
698bad0001eSAlexander Ivanov /*
699bad0001eSAlexander Ivanov  * Return status of freeze/thaw
700bad0001eSAlexander Ivanov  */
qmp_guest_fsfreeze_status(Error ** errp)701bad0001eSAlexander Ivanov GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
702bad0001eSAlexander Ivanov {
703bad0001eSAlexander Ivanov     if (ga_is_frozen(ga_state)) {
704bad0001eSAlexander Ivanov         return GUEST_FSFREEZE_STATUS_FROZEN;
705bad0001eSAlexander Ivanov     }
706bad0001eSAlexander Ivanov 
707bad0001eSAlexander Ivanov     return GUEST_FSFREEZE_STATUS_THAWED;
708bad0001eSAlexander Ivanov }
709bad0001eSAlexander Ivanov 
qmp_guest_fsfreeze_freeze(Error ** errp)710bad0001eSAlexander Ivanov int64_t qmp_guest_fsfreeze_freeze(Error **errp)
711bad0001eSAlexander Ivanov {
712bad0001eSAlexander Ivanov     return qmp_guest_fsfreeze_freeze_list(false, NULL, errp);
713bad0001eSAlexander Ivanov }
714bad0001eSAlexander Ivanov 
qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,strList * mountpoints,Error ** errp)715bad0001eSAlexander Ivanov int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
716bad0001eSAlexander Ivanov                                        strList *mountpoints,
717bad0001eSAlexander Ivanov                                        Error **errp)
718bad0001eSAlexander Ivanov {
719bad0001eSAlexander Ivanov     int ret;
720bad0001eSAlexander Ivanov     FsMountList mounts;
721bad0001eSAlexander Ivanov     Error *local_err = NULL;
722bad0001eSAlexander Ivanov 
723bad0001eSAlexander Ivanov     slog("guest-fsfreeze called");
724bad0001eSAlexander Ivanov 
725bad0001eSAlexander Ivanov     execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err);
726bad0001eSAlexander Ivanov     if (local_err) {
727bad0001eSAlexander Ivanov         error_propagate(errp, local_err);
728bad0001eSAlexander Ivanov         return -1;
729bad0001eSAlexander Ivanov     }
730bad0001eSAlexander Ivanov 
731bad0001eSAlexander Ivanov     QTAILQ_INIT(&mounts);
732bad0001eSAlexander Ivanov     if (!build_fs_mount_list(&mounts, &local_err)) {
733bad0001eSAlexander Ivanov         error_propagate(errp, local_err);
734bad0001eSAlexander Ivanov         return -1;
735bad0001eSAlexander Ivanov     }
736bad0001eSAlexander Ivanov 
737bad0001eSAlexander Ivanov     /* cannot risk guest agent blocking itself on a write in this state */
738bad0001eSAlexander Ivanov     ga_set_frozen(ga_state);
739bad0001eSAlexander Ivanov 
740bad0001eSAlexander Ivanov     ret = qmp_guest_fsfreeze_do_freeze_list(has_mountpoints, mountpoints,
741bad0001eSAlexander Ivanov                                             mounts, errp);
742bad0001eSAlexander Ivanov 
743bad0001eSAlexander Ivanov     free_fs_mount_list(&mounts);
744bad0001eSAlexander Ivanov     /* We may not issue any FIFREEZE here.
745bad0001eSAlexander Ivanov      * Just unset ga_state here and ready for the next call.
746bad0001eSAlexander Ivanov      */
747bad0001eSAlexander Ivanov     if (ret == 0) {
748bad0001eSAlexander Ivanov         ga_unset_frozen(ga_state);
749bad0001eSAlexander Ivanov     } else if (ret < 0) {
750bad0001eSAlexander Ivanov         qmp_guest_fsfreeze_thaw(NULL);
751bad0001eSAlexander Ivanov     }
752bad0001eSAlexander Ivanov     return ret;
753bad0001eSAlexander Ivanov }
754bad0001eSAlexander Ivanov 
qmp_guest_fsfreeze_thaw(Error ** errp)755bad0001eSAlexander Ivanov int64_t qmp_guest_fsfreeze_thaw(Error **errp)
756bad0001eSAlexander Ivanov {
757bad0001eSAlexander Ivanov     int ret;
758bad0001eSAlexander Ivanov 
759bad0001eSAlexander Ivanov     ret = qmp_guest_fsfreeze_do_thaw(errp);
760bad0001eSAlexander Ivanov     if (ret >= 0) {
761bad0001eSAlexander Ivanov         ga_unset_frozen(ga_state);
762bad0001eSAlexander Ivanov         execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp);
763bad0001eSAlexander Ivanov     } else {
764bad0001eSAlexander Ivanov         ret = 0;
765bad0001eSAlexander Ivanov     }
766bad0001eSAlexander Ivanov 
767bad0001eSAlexander Ivanov     return ret;
768bad0001eSAlexander Ivanov }
769bad0001eSAlexander Ivanov 
guest_fsfreeze_cleanup(void)770bad0001eSAlexander Ivanov static void guest_fsfreeze_cleanup(void)
771bad0001eSAlexander Ivanov {
772bad0001eSAlexander Ivanov     Error *err = NULL;
773bad0001eSAlexander Ivanov 
774bad0001eSAlexander Ivanov     if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) {
775bad0001eSAlexander Ivanov         qmp_guest_fsfreeze_thaw(&err);
776bad0001eSAlexander Ivanov         if (err) {
777bad0001eSAlexander Ivanov             slog("failed to clean up frozen filesystems: %s",
778bad0001eSAlexander Ivanov                  error_get_pretty(err));
779bad0001eSAlexander Ivanov             error_free(err);
780bad0001eSAlexander Ivanov         }
781bad0001eSAlexander Ivanov     }
782bad0001eSAlexander Ivanov }
783bad0001eSAlexander Ivanov #endif
784bad0001eSAlexander Ivanov 
785bad0001eSAlexander Ivanov /* linux-specific implementations. avoid this if at all possible. */
786bad0001eSAlexander Ivanov #if defined(__linux__)
787bad0001eSAlexander Ivanov #if defined(CONFIG_FSFREEZE)
788c216e5adSMichael Roth 
get_pci_driver(char const * syspath,int pathlen,Error ** errp)78946d4c572STomoki Sekiyama static char *get_pci_driver(char const *syspath, int pathlen, Error **errp)
79046d4c572STomoki Sekiyama {
79146d4c572STomoki Sekiyama     char *path;
79246d4c572STomoki Sekiyama     char *dpath;
79346d4c572STomoki Sekiyama     char *driver = NULL;
79446d4c572STomoki Sekiyama     char buf[PATH_MAX];
79546d4c572STomoki Sekiyama     ssize_t len;
79646d4c572STomoki Sekiyama 
79746d4c572STomoki Sekiyama     path = g_strndup(syspath, pathlen);
79846d4c572STomoki Sekiyama     dpath = g_strdup_printf("%s/driver", path);
79946d4c572STomoki Sekiyama     len = readlink(dpath, buf, sizeof(buf) - 1);
80046d4c572STomoki Sekiyama     if (len != -1) {
80146d4c572STomoki Sekiyama         buf[len] = 0;
8023e015d81SJulia Suvorova         driver = g_path_get_basename(buf);
80346d4c572STomoki Sekiyama     }
80446d4c572STomoki Sekiyama     g_free(dpath);
80546d4c572STomoki Sekiyama     g_free(path);
80646d4c572STomoki Sekiyama     return driver;
80746d4c572STomoki Sekiyama }
80846d4c572STomoki Sekiyama 
compare_uint(const void * _a,const void * _b)80946d4c572STomoki Sekiyama static int compare_uint(const void *_a, const void *_b)
81046d4c572STomoki Sekiyama {
81146d4c572STomoki Sekiyama     unsigned int a = *(unsigned int *)_a;
81246d4c572STomoki Sekiyama     unsigned int b = *(unsigned int *)_b;
81346d4c572STomoki Sekiyama 
81446d4c572STomoki Sekiyama     return a < b ? -1 : a > b ? 1 : 0;
81546d4c572STomoki Sekiyama }
81646d4c572STomoki Sekiyama 
81746d4c572STomoki Sekiyama /* Walk the specified sysfs and build a sorted list of host or ata numbers */
build_hosts(char const * syspath,char const * host,bool ata,unsigned int * hosts,int hosts_max,Error ** errp)81846d4c572STomoki Sekiyama static int build_hosts(char const *syspath, char const *host, bool ata,
81946d4c572STomoki Sekiyama                        unsigned int *hosts, int hosts_max, Error **errp)
82046d4c572STomoki Sekiyama {
82146d4c572STomoki Sekiyama     char *path;
82246d4c572STomoki Sekiyama     DIR *dir;
82346d4c572STomoki Sekiyama     struct dirent *entry;
82446d4c572STomoki Sekiyama     int i = 0;
82546d4c572STomoki Sekiyama 
82646d4c572STomoki Sekiyama     path = g_strndup(syspath, host - syspath);
82746d4c572STomoki Sekiyama     dir = opendir(path);
82846d4c572STomoki Sekiyama     if (!dir) {
82946d4c572STomoki Sekiyama         error_setg_errno(errp, errno, "opendir(\"%s\")", path);
83046d4c572STomoki Sekiyama         g_free(path);
83146d4c572STomoki Sekiyama         return -1;
83246d4c572STomoki Sekiyama     }
83346d4c572STomoki Sekiyama 
83446d4c572STomoki Sekiyama     while (i < hosts_max) {
83546d4c572STomoki Sekiyama         entry = readdir(dir);
83646d4c572STomoki Sekiyama         if (!entry) {
83746d4c572STomoki Sekiyama             break;
83846d4c572STomoki Sekiyama         }
83946d4c572STomoki Sekiyama         if (ata && sscanf(entry->d_name, "ata%d", hosts + i) == 1) {
84046d4c572STomoki Sekiyama             ++i;
84146d4c572STomoki Sekiyama         } else if (!ata && sscanf(entry->d_name, "host%d", hosts + i) == 1) {
84246d4c572STomoki Sekiyama             ++i;
84346d4c572STomoki Sekiyama         }
84446d4c572STomoki Sekiyama     }
84546d4c572STomoki Sekiyama 
84646d4c572STomoki Sekiyama     qsort(hosts, i, sizeof(hosts[0]), compare_uint);
84746d4c572STomoki Sekiyama 
84846d4c572STomoki Sekiyama     g_free(path);
84946d4c572STomoki Sekiyama     closedir(dir);
85046d4c572STomoki Sekiyama     return i;
85146d4c572STomoki Sekiyama }
85246d4c572STomoki Sekiyama 
853d9fe4f0fSThomas Huth /*
854d9fe4f0fSThomas Huth  * Store disk device info for devices on the PCI bus.
855d9fe4f0fSThomas Huth  * Returns true if information has been stored, or false for failure.
856d9fe4f0fSThomas Huth  */
build_guest_fsinfo_for_pci_dev(char const * syspath,GuestDiskAddress * disk,Error ** errp)857d9fe4f0fSThomas Huth static bool build_guest_fsinfo_for_pci_dev(char const *syspath,
858d9fe4f0fSThomas Huth                                            GuestDiskAddress *disk,
85946d4c572STomoki Sekiyama                                            Error **errp)
86046d4c572STomoki Sekiyama {
86146d4c572STomoki Sekiyama     unsigned int pci[4], host, hosts[8], tgt[3];
86246d4c572STomoki Sekiyama     int i, nhosts = 0, pcilen;
863d9fe4f0fSThomas Huth     GuestPCIAddress *pciaddr = disk->pci_controller;
86446d4c572STomoki Sekiyama     bool has_ata = false, has_host = false, has_tgt = false;
86546d4c572STomoki Sekiyama     char *p, *q, *driver = NULL;
866d9fe4f0fSThomas Huth     bool ret = false;
86746d4c572STomoki Sekiyama 
86846d4c572STomoki Sekiyama     p = strstr(syspath, "/devices/pci");
86946d4c572STomoki Sekiyama     if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n",
87046d4c572STomoki Sekiyama                      pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) {
871743c71d0SMarc-André Lureau         g_debug("only pci device is supported: sysfs path '%s'", syspath);
872d9fe4f0fSThomas Huth         return false;
87346d4c572STomoki Sekiyama     }
87446d4c572STomoki Sekiyama 
875743c71d0SMarc-André Lureau     p += 12 + pcilen;
876743c71d0SMarc-André Lureau     while (true) {
877743c71d0SMarc-André Lureau         driver = get_pci_driver(syspath, p - syspath, errp);
878743c71d0SMarc-André Lureau         if (driver && (g_str_equal(driver, "ata_piix") ||
879743c71d0SMarc-André Lureau                        g_str_equal(driver, "sym53c8xx") ||
880743c71d0SMarc-André Lureau                        g_str_equal(driver, "virtio-pci") ||
881d48f61c8Szhenwei pi                        g_str_equal(driver, "ahci") ||
8825a954e02SKfir Manor                        g_str_equal(driver, "nvme") ||
8835a954e02SKfir Manor                        g_str_equal(driver, "xhci_hcd") ||
8845a954e02SKfir Manor                        g_str_equal(driver, "ehci-pci"))) {
885743c71d0SMarc-André Lureau             break;
886743c71d0SMarc-André Lureau         }
887743c71d0SMarc-André Lureau 
888bb23a736SMarc-André Lureau         g_free(driver);
889743c71d0SMarc-André Lureau         if (sscanf(p, "/%x:%x:%x.%x%n",
890743c71d0SMarc-André Lureau                           pci, pci + 1, pci + 2, pci + 3, &pcilen) == 4) {
891743c71d0SMarc-André Lureau             p += pcilen;
892743c71d0SMarc-André Lureau             continue;
893743c71d0SMarc-André Lureau         }
894743c71d0SMarc-André Lureau 
895743c71d0SMarc-André Lureau         g_debug("unsupported driver or sysfs path '%s'", syspath);
896d9fe4f0fSThomas Huth         return false;
89746d4c572STomoki Sekiyama     }
89846d4c572STomoki Sekiyama 
89946d4c572STomoki Sekiyama     p = strstr(syspath, "/target");
90046d4c572STomoki Sekiyama     if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u",
90146d4c572STomoki Sekiyama                     tgt, tgt + 1, tgt + 2) == 3) {
90246d4c572STomoki Sekiyama         has_tgt = true;
90346d4c572STomoki Sekiyama     }
90446d4c572STomoki Sekiyama 
90546d4c572STomoki Sekiyama     p = strstr(syspath, "/ata");
90646d4c572STomoki Sekiyama     if (p) {
90746d4c572STomoki Sekiyama         q = p + 4;
90846d4c572STomoki Sekiyama         has_ata = true;
90946d4c572STomoki Sekiyama     } else {
91046d4c572STomoki Sekiyama         p = strstr(syspath, "/host");
91146d4c572STomoki Sekiyama         q = p + 5;
91246d4c572STomoki Sekiyama     }
91346d4c572STomoki Sekiyama     if (p && sscanf(q, "%u", &host) == 1) {
91446d4c572STomoki Sekiyama         has_host = true;
91546d4c572STomoki Sekiyama         nhosts = build_hosts(syspath, p, has_ata, hosts,
91601a6df1bSPhilippe Mathieu-Daudé                              ARRAY_SIZE(hosts), errp);
91746d4c572STomoki Sekiyama         if (nhosts < 0) {
91846d4c572STomoki Sekiyama             goto cleanup;
91946d4c572STomoki Sekiyama         }
92046d4c572STomoki Sekiyama     }
92146d4c572STomoki Sekiyama 
92246d4c572STomoki Sekiyama     pciaddr->domain = pci[0];
92346d4c572STomoki Sekiyama     pciaddr->bus = pci[1];
92446d4c572STomoki Sekiyama     pciaddr->slot = pci[2];
92546d4c572STomoki Sekiyama     pciaddr->function = pci[3];
92646d4c572STomoki Sekiyama 
92746d4c572STomoki Sekiyama     if (strcmp(driver, "ata_piix") == 0) {
92846d4c572STomoki Sekiyama         /* a host per ide bus, target*:0:<unit>:0 */
92946d4c572STomoki Sekiyama         if (!has_host || !has_tgt) {
93046d4c572STomoki Sekiyama             g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
93146d4c572STomoki Sekiyama             goto cleanup;
93246d4c572STomoki Sekiyama         }
93346d4c572STomoki Sekiyama         for (i = 0; i < nhosts; i++) {
93446d4c572STomoki Sekiyama             if (host == hosts[i]) {
93546d4c572STomoki Sekiyama                 disk->bus_type = GUEST_DISK_BUS_TYPE_IDE;
93646d4c572STomoki Sekiyama                 disk->bus = i;
93746d4c572STomoki Sekiyama                 disk->unit = tgt[1];
93846d4c572STomoki Sekiyama                 break;
93946d4c572STomoki Sekiyama             }
94046d4c572STomoki Sekiyama         }
94146d4c572STomoki Sekiyama         if (i >= nhosts) {
94246d4c572STomoki Sekiyama             g_debug("no host for '%s' (driver '%s')", syspath, driver);
94346d4c572STomoki Sekiyama             goto cleanup;
94446d4c572STomoki Sekiyama         }
94546d4c572STomoki Sekiyama     } else if (strcmp(driver, "sym53c8xx") == 0) {
94646d4c572STomoki Sekiyama         /* scsi(LSI Logic): target*:0:<unit>:0 */
94746d4c572STomoki Sekiyama         if (!has_tgt) {
94846d4c572STomoki Sekiyama             g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
94946d4c572STomoki Sekiyama             goto cleanup;
95046d4c572STomoki Sekiyama         }
95146d4c572STomoki Sekiyama         disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
95246d4c572STomoki Sekiyama         disk->unit = tgt[1];
95346d4c572STomoki Sekiyama     } else if (strcmp(driver, "virtio-pci") == 0) {
95446d4c572STomoki Sekiyama         if (has_tgt) {
95546d4c572STomoki Sekiyama             /* virtio-scsi: target*:0:0:<unit> */
95646d4c572STomoki Sekiyama             disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
95746d4c572STomoki Sekiyama             disk->unit = tgt[2];
95846d4c572STomoki Sekiyama         } else {
95946d4c572STomoki Sekiyama             /* virtio-blk: 1 disk per 1 device */
96046d4c572STomoki Sekiyama             disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO;
96146d4c572STomoki Sekiyama         }
96246d4c572STomoki Sekiyama     } else if (strcmp(driver, "ahci") == 0) {
96346d4c572STomoki Sekiyama         /* ahci: 1 host per 1 unit */
96446d4c572STomoki Sekiyama         if (!has_host || !has_tgt) {
96546d4c572STomoki Sekiyama             g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
96646d4c572STomoki Sekiyama             goto cleanup;
96746d4c572STomoki Sekiyama         }
96846d4c572STomoki Sekiyama         for (i = 0; i < nhosts; i++) {
96946d4c572STomoki Sekiyama             if (host == hosts[i]) {
97046d4c572STomoki Sekiyama                 disk->unit = i;
97146d4c572STomoki Sekiyama                 disk->bus_type = GUEST_DISK_BUS_TYPE_SATA;
97246d4c572STomoki Sekiyama                 break;
97346d4c572STomoki Sekiyama             }
97446d4c572STomoki Sekiyama         }
97546d4c572STomoki Sekiyama         if (i >= nhosts) {
97646d4c572STomoki Sekiyama             g_debug("no host for '%s' (driver '%s')", syspath, driver);
97746d4c572STomoki Sekiyama             goto cleanup;
97846d4c572STomoki Sekiyama         }
979d48f61c8Szhenwei pi     } else if (strcmp(driver, "nvme") == 0) {
980d48f61c8Szhenwei pi         disk->bus_type = GUEST_DISK_BUS_TYPE_NVME;
9815a954e02SKfir Manor     } else if (strcmp(driver, "ehci-pci") == 0 || strcmp(driver, "xhci_hcd") == 0) {
9825a954e02SKfir Manor         disk->bus_type = GUEST_DISK_BUS_TYPE_USB;
98346d4c572STomoki Sekiyama     } else {
98446d4c572STomoki Sekiyama         g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath);
98546d4c572STomoki Sekiyama         goto cleanup;
98646d4c572STomoki Sekiyama     }
98746d4c572STomoki Sekiyama 
988d9fe4f0fSThomas Huth     ret = true;
98946d4c572STomoki Sekiyama 
99046d4c572STomoki Sekiyama cleanup:
99146d4c572STomoki Sekiyama     g_free(driver);
992d9fe4f0fSThomas Huth     return ret;
993d9fe4f0fSThomas Huth }
994d9fe4f0fSThomas Huth 
99523843c12SThomas Huth /*
99623843c12SThomas Huth  * Store disk device info for non-PCI virtio devices (for example s390x
99723843c12SThomas Huth  * channel I/O devices). Returns true if information has been stored, or
99823843c12SThomas Huth  * false for failure.
99923843c12SThomas Huth  */
build_guest_fsinfo_for_nonpci_virtio(char const * syspath,GuestDiskAddress * disk,Error ** errp)100023843c12SThomas Huth static bool build_guest_fsinfo_for_nonpci_virtio(char const *syspath,
100123843c12SThomas Huth                                                  GuestDiskAddress *disk,
100223843c12SThomas Huth                                                  Error **errp)
100323843c12SThomas Huth {
100423843c12SThomas Huth     unsigned int tgt[3];
100523843c12SThomas Huth     char *p;
100623843c12SThomas Huth 
100723843c12SThomas Huth     if (!strstr(syspath, "/virtio") || !strstr(syspath, "/block")) {
100823843c12SThomas Huth         g_debug("Unsupported virtio device '%s'", syspath);
100923843c12SThomas Huth         return false;
101023843c12SThomas Huth     }
101123843c12SThomas Huth 
101223843c12SThomas Huth     p = strstr(syspath, "/target");
101323843c12SThomas Huth     if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u",
101423843c12SThomas Huth                     &tgt[0], &tgt[1], &tgt[2]) == 3) {
101523843c12SThomas Huth         /* virtio-scsi: target*:0:<target>:<unit> */
101623843c12SThomas Huth         disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
101723843c12SThomas Huth         disk->bus = tgt[0];
101823843c12SThomas Huth         disk->target = tgt[1];
101923843c12SThomas Huth         disk->unit = tgt[2];
102023843c12SThomas Huth     } else {
102123843c12SThomas Huth         /* virtio-blk: 1 disk per 1 device */
102223843c12SThomas Huth         disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO;
102323843c12SThomas Huth     }
102423843c12SThomas Huth 
102523843c12SThomas Huth     return true;
102623843c12SThomas Huth }
102723843c12SThomas Huth 
10285b723a5dSThomas Huth /*
10295b723a5dSThomas Huth  * Store disk device info for CCW devices (s390x channel I/O devices).
10305b723a5dSThomas Huth  * Returns true if information has been stored, or false for failure.
10315b723a5dSThomas Huth  */
build_guest_fsinfo_for_ccw_dev(char const * syspath,GuestDiskAddress * disk,Error ** errp)10325b723a5dSThomas Huth static bool build_guest_fsinfo_for_ccw_dev(char const *syspath,
10335b723a5dSThomas Huth                                            GuestDiskAddress *disk,
10345b723a5dSThomas Huth                                            Error **errp)
10355b723a5dSThomas Huth {
10365b723a5dSThomas Huth     unsigned int cssid, ssid, subchno, devno;
10375b723a5dSThomas Huth     char *p;
10385b723a5dSThomas Huth 
10395b723a5dSThomas Huth     p = strstr(syspath, "/devices/css");
10405b723a5dSThomas Huth     if (!p || sscanf(p + 12, "%*x/%x.%x.%x/%*x.%*x.%x/",
10415b723a5dSThomas Huth                      &cssid, &ssid, &subchno, &devno) < 4) {
10425b723a5dSThomas Huth         g_debug("could not parse ccw device sysfs path: %s", syspath);
10435b723a5dSThomas Huth         return false;
10445b723a5dSThomas Huth     }
10455b723a5dSThomas Huth 
10465b723a5dSThomas Huth     disk->ccw_address = g_new0(GuestCCWAddress, 1);
10475b723a5dSThomas Huth     disk->ccw_address->cssid = cssid;
10485b723a5dSThomas Huth     disk->ccw_address->ssid = ssid;
10495b723a5dSThomas Huth     disk->ccw_address->subchno = subchno;
10505b723a5dSThomas Huth     disk->ccw_address->devno = devno;
10515b723a5dSThomas Huth 
10525b723a5dSThomas Huth     if (strstr(p, "/virtio")) {
10535b723a5dSThomas Huth         build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp);
10545b723a5dSThomas Huth     }
10555b723a5dSThomas Huth 
10565b723a5dSThomas Huth     return true;
10575b723a5dSThomas Huth }
10585b723a5dSThomas Huth 
1059d9fe4f0fSThomas Huth /* Store disk device info specified by @sysfs into @fs */
build_guest_fsinfo_for_real_device(char const * syspath,GuestFilesystemInfo * fs,Error ** errp)1060d9fe4f0fSThomas Huth static void build_guest_fsinfo_for_real_device(char const *syspath,
1061d9fe4f0fSThomas Huth                                                GuestFilesystemInfo *fs,
1062d9fe4f0fSThomas Huth                                                Error **errp)
1063d9fe4f0fSThomas Huth {
1064d9fe4f0fSThomas Huth     GuestDiskAddress *disk;
1065d9fe4f0fSThomas Huth     GuestPCIAddress *pciaddr;
1066d9fe4f0fSThomas Huth     bool has_hwinf;
106743dadc43SThomas Huth #ifdef CONFIG_LIBUDEV
106843dadc43SThomas Huth     struct udev *udev = NULL;
106943dadc43SThomas Huth     struct udev_device *udevice = NULL;
107043dadc43SThomas Huth #endif
1071d9fe4f0fSThomas Huth 
1072d9fe4f0fSThomas Huth     pciaddr = g_new0(GuestPCIAddress, 1);
107343dadc43SThomas Huth     pciaddr->domain = -1;                       /* -1 means field is invalid */
107443dadc43SThomas Huth     pciaddr->bus = -1;
107543dadc43SThomas Huth     pciaddr->slot = -1;
107643dadc43SThomas Huth     pciaddr->function = -1;
1077d9fe4f0fSThomas Huth 
1078d9fe4f0fSThomas Huth     disk = g_new0(GuestDiskAddress, 1);
1079d9fe4f0fSThomas Huth     disk->pci_controller = pciaddr;
108043dadc43SThomas Huth     disk->bus_type = GUEST_DISK_BUS_TYPE_UNKNOWN;
1081d9fe4f0fSThomas Huth 
108243dadc43SThomas Huth #ifdef CONFIG_LIBUDEV
108343dadc43SThomas Huth     udev = udev_new();
108443dadc43SThomas Huth     udevice = udev_device_new_from_syspath(udev, syspath);
108543dadc43SThomas Huth     if (udev == NULL || udevice == NULL) {
108643dadc43SThomas Huth         g_debug("failed to query udev");
108743dadc43SThomas Huth     } else {
108843dadc43SThomas Huth         const char *devnode, *serial;
108943dadc43SThomas Huth         devnode = udev_device_get_devnode(udevice);
109043dadc43SThomas Huth         if (devnode != NULL) {
109143dadc43SThomas Huth             disk->dev = g_strdup(devnode);
109243dadc43SThomas Huth         }
109343dadc43SThomas Huth         serial = udev_device_get_property_value(udevice, "ID_SERIAL");
109443dadc43SThomas Huth         if (serial != NULL && *serial != 0) {
109543dadc43SThomas Huth             disk->serial = g_strdup(serial);
109643dadc43SThomas Huth         }
109743dadc43SThomas Huth     }
109843dadc43SThomas Huth 
109943dadc43SThomas Huth     udev_unref(udev);
110043dadc43SThomas Huth     udev_device_unref(udevice);
110143dadc43SThomas Huth #endif
110243dadc43SThomas Huth 
110323843c12SThomas Huth     if (strstr(syspath, "/devices/pci")) {
1104d9fe4f0fSThomas Huth         has_hwinf = build_guest_fsinfo_for_pci_dev(syspath, disk, errp);
11055b723a5dSThomas Huth     } else if (strstr(syspath, "/devices/css")) {
11065b723a5dSThomas Huth         has_hwinf = build_guest_fsinfo_for_ccw_dev(syspath, disk, errp);
110723843c12SThomas Huth     } else if (strstr(syspath, "/virtio")) {
110823843c12SThomas Huth         has_hwinf = build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp);
110923843c12SThomas Huth     } else {
111023843c12SThomas Huth         g_debug("Unsupported device type for '%s'", syspath);
111123843c12SThomas Huth         has_hwinf = false;
111223843c12SThomas Huth     }
1113d9fe4f0fSThomas Huth 
111491eab32aSMarkus Armbruster     if (has_hwinf || disk->dev || disk->serial) {
111554aa3de7SEric Blake         QAPI_LIST_PREPEND(fs->disk, disk);
1116d9fe4f0fSThomas Huth     } else {
111754aa3de7SEric Blake         qapi_free_GuestDiskAddress(disk);
1118d9fe4f0fSThomas Huth     }
111946d4c572STomoki Sekiyama }
112046d4c572STomoki Sekiyama 
112146d4c572STomoki Sekiyama static void build_guest_fsinfo_for_device(char const *devpath,
112246d4c572STomoki Sekiyama                                           GuestFilesystemInfo *fs,
112346d4c572STomoki Sekiyama                                           Error **errp);
112446d4c572STomoki Sekiyama 
112546d4c572STomoki Sekiyama /* Store a list of slave devices of virtual volume specified by @syspath into
112646d4c572STomoki Sekiyama  * @fs */
build_guest_fsinfo_for_virtual_device(char const * syspath,GuestFilesystemInfo * fs,Error ** errp)112746d4c572STomoki Sekiyama static void build_guest_fsinfo_for_virtual_device(char const *syspath,
112846d4c572STomoki Sekiyama                                                   GuestFilesystemInfo *fs,
112946d4c572STomoki Sekiyama                                                   Error **errp)
113046d4c572STomoki Sekiyama {
1131292743d9SMarkus Armbruster     Error *err = NULL;
113246d4c572STomoki Sekiyama     DIR *dir;
113346d4c572STomoki Sekiyama     char *dirpath;
1134e668d1b8Szhanghailiang     struct dirent *entry;
113546d4c572STomoki Sekiyama 
113646d4c572STomoki Sekiyama     dirpath = g_strdup_printf("%s/slaves", syspath);
113746d4c572STomoki Sekiyama     dir = opendir(dirpath);
113846d4c572STomoki Sekiyama     if (!dir) {
11398251a72fSMichael Roth         if (errno != ENOENT) {
114046d4c572STomoki Sekiyama             error_setg_errno(errp, errno, "opendir(\"%s\")", dirpath);
11418251a72fSMichael Roth         }
114246d4c572STomoki Sekiyama         g_free(dirpath);
114346d4c572STomoki Sekiyama         return;
114446d4c572STomoki Sekiyama     }
114546d4c572STomoki Sekiyama 
114646d4c572STomoki Sekiyama     for (;;) {
1147e668d1b8Szhanghailiang         errno = 0;
1148e668d1b8Szhanghailiang         entry = readdir(dir);
1149e668d1b8Szhanghailiang         if (entry == NULL) {
1150e668d1b8Szhanghailiang             if (errno) {
1151e668d1b8Szhanghailiang                 error_setg_errno(errp, errno, "readdir(\"%s\")", dirpath);
115246d4c572STomoki Sekiyama             }
115346d4c572STomoki Sekiyama             break;
115446d4c572STomoki Sekiyama         }
115546d4c572STomoki Sekiyama 
1156e668d1b8Szhanghailiang         if (entry->d_type == DT_LNK) {
1157e668d1b8Szhanghailiang             char *path;
1158e668d1b8Szhanghailiang 
1159e668d1b8Szhanghailiang             g_debug(" slave device '%s'", entry->d_name);
1160e668d1b8Szhanghailiang             path = g_strdup_printf("%s/slaves/%s", syspath, entry->d_name);
1161292743d9SMarkus Armbruster             build_guest_fsinfo_for_device(path, fs, &err);
1162e668d1b8Szhanghailiang             g_free(path);
116346d4c572STomoki Sekiyama 
1164292743d9SMarkus Armbruster             if (err) {
1165292743d9SMarkus Armbruster                 error_propagate(errp, err);
116646d4c572STomoki Sekiyama                 break;
116746d4c572STomoki Sekiyama             }
116846d4c572STomoki Sekiyama         }
116946d4c572STomoki Sekiyama     }
117046d4c572STomoki Sekiyama 
1171e668d1b8Szhanghailiang     g_free(dirpath);
117246d4c572STomoki Sekiyama     closedir(dir);
117346d4c572STomoki Sekiyama }
117446d4c572STomoki Sekiyama 
is_disk_virtual(const char * devpath,Error ** errp)1175fed39564STomáš Golembiovský static bool is_disk_virtual(const char *devpath, Error **errp)
1176fed39564STomáš Golembiovský {
1177fed39564STomáš Golembiovský     g_autofree char *syspath = realpath(devpath, NULL);
1178fed39564STomáš Golembiovský 
1179fed39564STomáš Golembiovský     if (!syspath) {
1180fed39564STomáš Golembiovský         error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
1181fed39564STomáš Golembiovský         return false;
1182fed39564STomáš Golembiovský     }
1183fed39564STomáš Golembiovský     return strstr(syspath, "/devices/virtual/block/") != NULL;
1184fed39564STomáš Golembiovský }
1185fed39564STomáš Golembiovský 
118646d4c572STomoki Sekiyama /* Dispatch to functions for virtual/real device */
build_guest_fsinfo_for_device(char const * devpath,GuestFilesystemInfo * fs,Error ** errp)118746d4c572STomoki Sekiyama static void build_guest_fsinfo_for_device(char const *devpath,
118846d4c572STomoki Sekiyama                                           GuestFilesystemInfo *fs,
118946d4c572STomoki Sekiyama                                           Error **errp)
119046d4c572STomoki Sekiyama {
1191fed39564STomáš Golembiovský     ERRP_GUARD();
1192fed39564STomáš Golembiovský     g_autofree char *syspath = NULL;
1193fed39564STomáš Golembiovský     bool is_virtual = false;
119446d4c572STomoki Sekiyama 
1195fed39564STomáš Golembiovský     syspath = realpath(devpath, NULL);
119646d4c572STomoki Sekiyama     if (!syspath) {
1197bbb0151cSJohn Snow         if (errno != ENOENT) {
119846d4c572STomoki Sekiyama             error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
119946d4c572STomoki Sekiyama             return;
120046d4c572STomoki Sekiyama         }
120146d4c572STomoki Sekiyama 
1202bbb0151cSJohn Snow         /* ENOENT: This devpath may not exist because of container config */
1203bbb0151cSJohn Snow         if (!fs->name) {
1204bbb0151cSJohn Snow             fs->name = g_path_get_basename(devpath);
1205bbb0151cSJohn Snow         }
1206bbb0151cSJohn Snow         return;
1207bbb0151cSJohn Snow     }
1208bbb0151cSJohn Snow 
120946d4c572STomoki Sekiyama     if (!fs->name) {
12103e015d81SJulia Suvorova         fs->name = g_path_get_basename(syspath);
121146d4c572STomoki Sekiyama     }
121246d4c572STomoki Sekiyama 
121346d4c572STomoki Sekiyama     g_debug("  parse sysfs path '%s'", syspath);
1214fed39564STomáš Golembiovský     is_virtual = is_disk_virtual(syspath, errp);
1215fed39564STomáš Golembiovský     if (*errp != NULL) {
1216fed39564STomáš Golembiovský         return;
1217fed39564STomáš Golembiovský     }
1218fed39564STomáš Golembiovský     if (is_virtual) {
121946d4c572STomoki Sekiyama         build_guest_fsinfo_for_virtual_device(syspath, fs, errp);
122046d4c572STomoki Sekiyama     } else {
122146d4c572STomoki Sekiyama         build_guest_fsinfo_for_real_device(syspath, fs, errp);
122246d4c572STomoki Sekiyama     }
122346d4c572STomoki Sekiyama }
122446d4c572STomoki Sekiyama 
1225fed39564STomáš Golembiovský #ifdef CONFIG_LIBUDEV
1226fed39564STomáš Golembiovský 
1227fed39564STomáš Golembiovský /*
1228fed39564STomáš Golembiovský  * Wrapper around build_guest_fsinfo_for_device() for getting just
1229fed39564STomáš Golembiovský  * the disk address.
1230fed39564STomáš Golembiovský  */
get_disk_address(const char * syspath,Error ** errp)1231fed39564STomáš Golembiovský static GuestDiskAddress *get_disk_address(const char *syspath, Error **errp)
1232fed39564STomáš Golembiovský {
1233fed39564STomáš Golembiovský     g_autoptr(GuestFilesystemInfo) fs = NULL;
1234fed39564STomáš Golembiovský 
1235fed39564STomáš Golembiovský     fs = g_new0(GuestFilesystemInfo, 1);
1236fed39564STomáš Golembiovský     build_guest_fsinfo_for_device(syspath, fs, errp);
1237fed39564STomáš Golembiovský     if (fs->disk != NULL) {
1238fed39564STomáš Golembiovský         return g_steal_pointer(&fs->disk->value);
1239fed39564STomáš Golembiovský     }
1240fed39564STomáš Golembiovský     return NULL;
1241fed39564STomáš Golembiovský }
1242fed39564STomáš Golembiovský 
get_alias_for_syspath(const char * syspath)1243fed39564STomáš Golembiovský static char *get_alias_for_syspath(const char *syspath)
1244fed39564STomáš Golembiovský {
1245fed39564STomáš Golembiovský     struct udev *udev = NULL;
1246fed39564STomáš Golembiovský     struct udev_device *udevice = NULL;
1247fed39564STomáš Golembiovský     char *ret = NULL;
1248fed39564STomáš Golembiovský 
1249fed39564STomáš Golembiovský     udev = udev_new();
1250fed39564STomáš Golembiovský     if (udev == NULL) {
1251fed39564STomáš Golembiovský         g_debug("failed to query udev");
1252fed39564STomáš Golembiovský         goto out;
1253fed39564STomáš Golembiovský     }
1254fed39564STomáš Golembiovský     udevice = udev_device_new_from_syspath(udev, syspath);
1255fed39564STomáš Golembiovský     if (udevice == NULL) {
1256fed39564STomáš Golembiovský         g_debug("failed to query udev for path: %s", syspath);
1257fed39564STomáš Golembiovský         goto out;
1258fed39564STomáš Golembiovský     } else {
1259fed39564STomáš Golembiovský         const char *alias = udev_device_get_property_value(
1260fed39564STomáš Golembiovský             udevice, "DM_NAME");
1261fed39564STomáš Golembiovský         /*
1262fed39564STomáš Golembiovský          * NULL means there was an error and empty string means there is no
1263fed39564STomáš Golembiovský          * alias. In case of no alias we return NULL instead of empty string.
1264fed39564STomáš Golembiovský          */
1265fed39564STomáš Golembiovský         if (alias == NULL) {
1266fed39564STomáš Golembiovský             g_debug("failed to query udev for device alias for: %s",
1267fed39564STomáš Golembiovský                 syspath);
1268fed39564STomáš Golembiovský         } else if (*alias != 0) {
1269fed39564STomáš Golembiovský             ret = g_strdup(alias);
1270fed39564STomáš Golembiovský         }
1271fed39564STomáš Golembiovský     }
1272fed39564STomáš Golembiovský 
1273fed39564STomáš Golembiovský out:
1274fed39564STomáš Golembiovský     udev_unref(udev);
1275fed39564STomáš Golembiovský     udev_device_unref(udevice);
1276fed39564STomáš Golembiovský     return ret;
1277fed39564STomáš Golembiovský }
1278fed39564STomáš Golembiovský 
get_device_for_syspath(const char * syspath)1279fed39564STomáš Golembiovský static char *get_device_for_syspath(const char *syspath)
1280fed39564STomáš Golembiovský {
1281fed39564STomáš Golembiovský     struct udev *udev = NULL;
1282fed39564STomáš Golembiovský     struct udev_device *udevice = NULL;
1283fed39564STomáš Golembiovský     char *ret = NULL;
1284fed39564STomáš Golembiovský 
1285fed39564STomáš Golembiovský     udev = udev_new();
1286fed39564STomáš Golembiovský     if (udev == NULL) {
1287fed39564STomáš Golembiovský         g_debug("failed to query udev");
1288fed39564STomáš Golembiovský         goto out;
1289fed39564STomáš Golembiovský     }
1290fed39564STomáš Golembiovský     udevice = udev_device_new_from_syspath(udev, syspath);
1291fed39564STomáš Golembiovský     if (udevice == NULL) {
1292fed39564STomáš Golembiovský         g_debug("failed to query udev for path: %s", syspath);
1293fed39564STomáš Golembiovský         goto out;
1294fed39564STomáš Golembiovský     } else {
1295fed39564STomáš Golembiovský         ret = g_strdup(udev_device_get_devnode(udevice));
1296fed39564STomáš Golembiovský     }
1297fed39564STomáš Golembiovský 
1298fed39564STomáš Golembiovský out:
1299fed39564STomáš Golembiovský     udev_unref(udev);
1300fed39564STomáš Golembiovský     udev_device_unref(udevice);
1301fed39564STomáš Golembiovský     return ret;
1302fed39564STomáš Golembiovský }
1303fed39564STomáš Golembiovský 
get_disk_deps(const char * disk_dir,GuestDiskInfo * disk)1304fed39564STomáš Golembiovský static void get_disk_deps(const char *disk_dir, GuestDiskInfo *disk)
1305fed39564STomáš Golembiovský {
1306fed39564STomáš Golembiovský     g_autofree char *deps_dir = NULL;
1307fed39564STomáš Golembiovský     const gchar *dep;
1308fed39564STomáš Golembiovský     GDir *dp_deps = NULL;
1309fed39564STomáš Golembiovský 
1310fed39564STomáš Golembiovský     /* List dependent disks */
1311fed39564STomáš Golembiovský     deps_dir = g_strdup_printf("%s/slaves", disk_dir);
1312fed39564STomáš Golembiovský     g_debug("  listing entries in: %s", deps_dir);
1313fed39564STomáš Golembiovský     dp_deps = g_dir_open(deps_dir, 0, NULL);
1314fed39564STomáš Golembiovský     if (dp_deps == NULL) {
1315fed39564STomáš Golembiovský         g_debug("failed to list entries in %s", deps_dir);
1316fed39564STomáš Golembiovský         return;
1317fed39564STomáš Golembiovský     }
1318a8aa94b5SMichael Roth     disk->has_dependencies = true;
1319fed39564STomáš Golembiovský     while ((dep = g_dir_read_name(dp_deps)) != NULL) {
1320fed39564STomáš Golembiovský         g_autofree char *dep_dir = NULL;
1321fed39564STomáš Golembiovský         char *dev_name;
1322fed39564STomáš Golembiovský 
1323fed39564STomáš Golembiovský         /* Add dependent disks */
1324fed39564STomáš Golembiovský         dep_dir = g_strdup_printf("%s/%s", deps_dir, dep);
1325fed39564STomáš Golembiovský         dev_name = get_device_for_syspath(dep_dir);
1326fed39564STomáš Golembiovský         if (dev_name != NULL) {
1327fed39564STomáš Golembiovský             g_debug("  adding dependent device: %s", dev_name);
132854aa3de7SEric Blake             QAPI_LIST_PREPEND(disk->dependencies, dev_name);
1329fed39564STomáš Golembiovský         }
1330fed39564STomáš Golembiovský     }
1331fed39564STomáš Golembiovský     g_dir_close(dp_deps);
1332fed39564STomáš Golembiovský }
1333fed39564STomáš Golembiovský 
1334fed39564STomáš Golembiovský /*
1335fed39564STomáš Golembiovský  * Detect partitions subdirectory, name is "<disk_name><number>" or
1336fed39564STomáš Golembiovský  * "<disk_name>p<number>"
1337fed39564STomáš Golembiovský  *
1338fed39564STomáš Golembiovský  * @disk_name -- last component of /sys path (e.g. sda)
1339fed39564STomáš Golembiovský  * @disk_dir -- sys path of the disk (e.g. /sys/block/sda)
1340fed39564STomáš Golembiovský  * @disk_dev -- device node of the disk (e.g. /dev/sda)
1341fed39564STomáš Golembiovský  */
get_disk_partitions(GuestDiskInfoList * list,const char * disk_name,const char * disk_dir,const char * disk_dev)1342fed39564STomáš Golembiovský static GuestDiskInfoList *get_disk_partitions(
1343fed39564STomáš Golembiovský     GuestDiskInfoList *list,
1344fed39564STomáš Golembiovský     const char *disk_name, const char *disk_dir,
1345fed39564STomáš Golembiovský     const char *disk_dev)
1346fed39564STomáš Golembiovský {
134754aa3de7SEric Blake     GuestDiskInfoList *ret = list;
1348fed39564STomáš Golembiovský     struct dirent *de_disk;
1349fed39564STomáš Golembiovský     DIR *dp_disk = NULL;
1350fed39564STomáš Golembiovský     size_t len = strlen(disk_name);
1351fed39564STomáš Golembiovský 
1352fed39564STomáš Golembiovský     dp_disk = opendir(disk_dir);
1353fed39564STomáš Golembiovský     while ((de_disk = readdir(dp_disk)) != NULL) {
1354fed39564STomáš Golembiovský         g_autofree char *partition_dir = NULL;
1355fed39564STomáš Golembiovský         char *dev_name;
1356fed39564STomáš Golembiovský         GuestDiskInfo *partition;
1357fed39564STomáš Golembiovský 
1358fed39564STomáš Golembiovský         if (!(de_disk->d_type & DT_DIR)) {
1359fed39564STomáš Golembiovský             continue;
1360fed39564STomáš Golembiovský         }
1361fed39564STomáš Golembiovský 
1362fed39564STomáš Golembiovský         if (!(strncmp(disk_name, de_disk->d_name, len) == 0 &&
1363fed39564STomáš Golembiovský             ((*(de_disk->d_name + len) == 'p' &&
1364fed39564STomáš Golembiovský             isdigit(*(de_disk->d_name + len + 1))) ||
1365fed39564STomáš Golembiovský                 isdigit(*(de_disk->d_name + len))))) {
1366fed39564STomáš Golembiovský             continue;
1367fed39564STomáš Golembiovský         }
1368fed39564STomáš Golembiovský 
1369fed39564STomáš Golembiovský         partition_dir = g_strdup_printf("%s/%s",
1370fed39564STomáš Golembiovský             disk_dir, de_disk->d_name);
1371fed39564STomáš Golembiovský         dev_name = get_device_for_syspath(partition_dir);
1372fed39564STomáš Golembiovský         if (dev_name == NULL) {
1373fed39564STomáš Golembiovský             g_debug("Failed to get device name for syspath: %s",
1374fed39564STomáš Golembiovský                 disk_dir);
1375fed39564STomáš Golembiovský             continue;
1376fed39564STomáš Golembiovský         }
1377fed39564STomáš Golembiovský         partition = g_new0(GuestDiskInfo, 1);
1378fed39564STomáš Golembiovský         partition->name = dev_name;
1379fed39564STomáš Golembiovský         partition->partition = true;
1380bac9b87bSMarc-André Lureau         partition->has_dependencies = true;
1381fed39564STomáš Golembiovský         /* Add parent disk as dependent for easier tracking of hierarchy */
138254aa3de7SEric Blake         QAPI_LIST_PREPEND(partition->dependencies, g_strdup(disk_dev));
1383fed39564STomáš Golembiovský 
138454aa3de7SEric Blake         QAPI_LIST_PREPEND(ret, partition);
1385fed39564STomáš Golembiovský     }
1386fed39564STomáš Golembiovský     closedir(dp_disk);
1387fed39564STomáš Golembiovský 
1388fed39564STomáš Golembiovský     return ret;
1389fed39564STomáš Golembiovský }
1390fed39564STomáš Golembiovský 
get_nvme_smart(GuestDiskInfo * disk)139122668881Szhenwei pi static void get_nvme_smart(GuestDiskInfo *disk)
139222668881Szhenwei pi {
139322668881Szhenwei pi     int fd;
139422668881Szhenwei pi     GuestNVMeSmart *smart;
139522668881Szhenwei pi     NvmeSmartLog log = {0};
139622668881Szhenwei pi     struct nvme_admin_cmd cmd = {
139722668881Szhenwei pi         .opcode = NVME_ADM_CMD_GET_LOG_PAGE,
139822668881Szhenwei pi         .nsid = NVME_NSID_BROADCAST,
139922668881Szhenwei pi         .addr = (uintptr_t)&log,
140022668881Szhenwei pi         .data_len = sizeof(log),
140122668881Szhenwei pi         .cdw10 = NVME_LOG_SMART_INFO | (1 << 15) /* RAE bit */
140222668881Szhenwei pi                  | (((sizeof(log) >> 2) - 1) << 16)
140322668881Szhenwei pi     };
140422668881Szhenwei pi 
1405b9947c9cSMarc-André Lureau     fd = qga_open_cloexec(disk->name, O_RDONLY, 0);
140622668881Szhenwei pi     if (fd == -1) {
140722668881Szhenwei pi         g_debug("Failed to open device: %s: %s", disk->name, g_strerror(errno));
140822668881Szhenwei pi         return;
140922668881Szhenwei pi     }
141022668881Szhenwei pi 
141122668881Szhenwei pi     if (ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd)) {
141222668881Szhenwei pi         g_debug("Failed to get smart: %s: %s", disk->name, g_strerror(errno));
141322668881Szhenwei pi         close(fd);
141422668881Szhenwei pi         return;
141522668881Szhenwei pi     }
141622668881Szhenwei pi 
141722668881Szhenwei pi     disk->smart = g_new0(GuestDiskSmart, 1);
141822668881Szhenwei pi     disk->smart->type = GUEST_DISK_BUS_TYPE_NVME;
141922668881Szhenwei pi 
142022668881Szhenwei pi     smart = &disk->smart->u.nvme;
142122668881Szhenwei pi     smart->critical_warning = log.critical_warning;
142222668881Szhenwei pi     smart->temperature = lduw_le_p(&log.temperature); /* unaligned field */
142322668881Szhenwei pi     smart->available_spare = log.available_spare;
142422668881Szhenwei pi     smart->available_spare_threshold = log.available_spare_threshold;
142522668881Szhenwei pi     smart->percentage_used = log.percentage_used;
142622668881Szhenwei pi     smart->data_units_read_lo = le64_to_cpu(log.data_units_read[0]);
142722668881Szhenwei pi     smart->data_units_read_hi = le64_to_cpu(log.data_units_read[1]);
142822668881Szhenwei pi     smart->data_units_written_lo = le64_to_cpu(log.data_units_written[0]);
142922668881Szhenwei pi     smart->data_units_written_hi = le64_to_cpu(log.data_units_written[1]);
143022668881Szhenwei pi     smart->host_read_commands_lo = le64_to_cpu(log.host_read_commands[0]);
143122668881Szhenwei pi     smart->host_read_commands_hi = le64_to_cpu(log.host_read_commands[1]);
143222668881Szhenwei pi     smart->host_write_commands_lo = le64_to_cpu(log.host_write_commands[0]);
143322668881Szhenwei pi     smart->host_write_commands_hi = le64_to_cpu(log.host_write_commands[1]);
143422668881Szhenwei pi     smart->controller_busy_time_lo = le64_to_cpu(log.controller_busy_time[0]);
143522668881Szhenwei pi     smart->controller_busy_time_hi = le64_to_cpu(log.controller_busy_time[1]);
143622668881Szhenwei pi     smart->power_cycles_lo = le64_to_cpu(log.power_cycles[0]);
143722668881Szhenwei pi     smart->power_cycles_hi = le64_to_cpu(log.power_cycles[1]);
143822668881Szhenwei pi     smart->power_on_hours_lo = le64_to_cpu(log.power_on_hours[0]);
143922668881Szhenwei pi     smart->power_on_hours_hi = le64_to_cpu(log.power_on_hours[1]);
144022668881Szhenwei pi     smart->unsafe_shutdowns_lo = le64_to_cpu(log.unsafe_shutdowns[0]);
144122668881Szhenwei pi     smart->unsafe_shutdowns_hi = le64_to_cpu(log.unsafe_shutdowns[1]);
144222668881Szhenwei pi     smart->media_errors_lo = le64_to_cpu(log.media_errors[0]);
144322668881Szhenwei pi     smart->media_errors_hi = le64_to_cpu(log.media_errors[1]);
144422668881Szhenwei pi     smart->number_of_error_log_entries_lo =
144522668881Szhenwei pi         le64_to_cpu(log.number_of_error_log_entries[0]);
144622668881Szhenwei pi     smart->number_of_error_log_entries_hi =
144722668881Szhenwei pi         le64_to_cpu(log.number_of_error_log_entries[1]);
144822668881Szhenwei pi 
144922668881Szhenwei pi     close(fd);
145022668881Szhenwei pi }
145122668881Szhenwei pi 
get_disk_smart(GuestDiskInfo * disk)145222668881Szhenwei pi static void get_disk_smart(GuestDiskInfo *disk)
145322668881Szhenwei pi {
145491eab32aSMarkus Armbruster     if (disk->address
145522668881Szhenwei pi         && (disk->address->bus_type == GUEST_DISK_BUS_TYPE_NVME)) {
145622668881Szhenwei pi         get_nvme_smart(disk);
145722668881Szhenwei pi     }
145822668881Szhenwei pi }
145922668881Szhenwei pi 
qmp_guest_get_disks(Error ** errp)1460fed39564STomáš Golembiovský GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
1461fed39564STomáš Golembiovský {
146254aa3de7SEric Blake     GuestDiskInfoList *ret = NULL;
1463fed39564STomáš Golembiovský     GuestDiskInfo *disk;
1464fed39564STomáš Golembiovský     DIR *dp = NULL;
1465fed39564STomáš Golembiovský     struct dirent *de = NULL;
1466fed39564STomáš Golembiovský 
1467fed39564STomáš Golembiovský     g_debug("listing /sys/block directory");
1468fed39564STomáš Golembiovský     dp = opendir("/sys/block");
1469fed39564STomáš Golembiovský     if (dp == NULL) {
1470fed39564STomáš Golembiovský         error_setg_errno(errp, errno, "Can't open directory \"/sys/block\"");
1471fed39564STomáš Golembiovský         return NULL;
1472fed39564STomáš Golembiovský     }
1473fed39564STomáš Golembiovský     while ((de = readdir(dp)) != NULL) {
1474fed39564STomáš Golembiovský         g_autofree char *disk_dir = NULL, *line = NULL,
1475fed39564STomáš Golembiovský             *size_path = NULL;
1476fed39564STomáš Golembiovský         char *dev_name;
1477fed39564STomáš Golembiovský         Error *local_err = NULL;
1478fed39564STomáš Golembiovský         if (de->d_type != DT_LNK) {
1479fed39564STomáš Golembiovský             g_debug("  skipping entry: %s", de->d_name);
1480fed39564STomáš Golembiovský             continue;
1481fed39564STomáš Golembiovský         }
1482fed39564STomáš Golembiovský 
1483fed39564STomáš Golembiovský         /* Check size and skip zero-sized disks */
1484fed39564STomáš Golembiovský         g_debug("  checking disk size");
1485fed39564STomáš Golembiovský         size_path = g_strdup_printf("/sys/block/%s/size", de->d_name);
1486fed39564STomáš Golembiovský         if (!g_file_get_contents(size_path, &line, NULL, NULL)) {
1487fed39564STomáš Golembiovský             g_debug("  failed to read disk size");
1488fed39564STomáš Golembiovský             continue;
1489fed39564STomáš Golembiovský         }
1490fed39564STomáš Golembiovský         if (g_strcmp0(line, "0\n") == 0) {
1491fed39564STomáš Golembiovský             g_debug("  skipping zero-sized disk");
1492fed39564STomáš Golembiovský             continue;
1493fed39564STomáš Golembiovský         }
1494fed39564STomáš Golembiovský 
1495fed39564STomáš Golembiovský         g_debug("  adding %s", de->d_name);
1496fed39564STomáš Golembiovský         disk_dir = g_strdup_printf("/sys/block/%s", de->d_name);
1497fed39564STomáš Golembiovský         dev_name = get_device_for_syspath(disk_dir);
1498fed39564STomáš Golembiovský         if (dev_name == NULL) {
1499fed39564STomáš Golembiovský             g_debug("Failed to get device name for syspath: %s",
1500fed39564STomáš Golembiovský                 disk_dir);
1501fed39564STomáš Golembiovský             continue;
1502fed39564STomáš Golembiovský         }
1503fed39564STomáš Golembiovský         disk = g_new0(GuestDiskInfo, 1);
1504fed39564STomáš Golembiovský         disk->name = dev_name;
1505fed39564STomáš Golembiovský         disk->partition = false;
1506fed39564STomáš Golembiovský         disk->alias = get_alias_for_syspath(disk_dir);
150754aa3de7SEric Blake         QAPI_LIST_PREPEND(ret, disk);
1508fed39564STomáš Golembiovský 
1509fed39564STomáš Golembiovský         /* Get address for non-virtual devices */
1510fed39564STomáš Golembiovský         bool is_virtual = is_disk_virtual(disk_dir, &local_err);
1511fed39564STomáš Golembiovský         if (local_err != NULL) {
1512fed39564STomáš Golembiovský             g_debug("  failed to check disk path, ignoring error: %s",
1513fed39564STomáš Golembiovský                 error_get_pretty(local_err));
1514fed39564STomáš Golembiovský             error_free(local_err);
1515fed39564STomáš Golembiovský             local_err = NULL;
1516fed39564STomáš Golembiovský             /* Don't try to get the address */
1517fed39564STomáš Golembiovský             is_virtual = true;
1518fed39564STomáš Golembiovský         }
1519fed39564STomáš Golembiovský         if (!is_virtual) {
1520fed39564STomáš Golembiovský             disk->address = get_disk_address(disk_dir, &local_err);
1521fed39564STomáš Golembiovský             if (local_err != NULL) {
1522fed39564STomáš Golembiovský                 g_debug("  failed to get device info, ignoring error: %s",
1523fed39564STomáš Golembiovský                     error_get_pretty(local_err));
1524fed39564STomáš Golembiovský                 error_free(local_err);
1525fed39564STomáš Golembiovský                 local_err = NULL;
1526fed39564STomáš Golembiovský             }
1527fed39564STomáš Golembiovský         }
1528fed39564STomáš Golembiovský 
1529fed39564STomáš Golembiovský         get_disk_deps(disk_dir, disk);
153022668881Szhenwei pi         get_disk_smart(disk);
1531fed39564STomáš Golembiovský         ret = get_disk_partitions(ret, de->d_name, disk_dir, dev_name);
1532fed39564STomáš Golembiovský     }
1533b1b9ab1cSMichael Roth 
1534b1b9ab1cSMichael Roth     closedir(dp);
1535b1b9ab1cSMichael Roth 
1536fed39564STomáš Golembiovský     return ret;
1537fed39564STomáš Golembiovský }
1538fed39564STomáš Golembiovský 
1539fed39564STomáš Golembiovský #else
1540fed39564STomáš Golembiovský 
qmp_guest_get_disks(Error ** errp)1541fed39564STomáš Golembiovský GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
1542fed39564STomáš Golembiovský {
1543fed39564STomáš Golembiovský     error_setg(errp, QERR_UNSUPPORTED);
1544fed39564STomáš Golembiovský     return NULL;
1545fed39564STomáš Golembiovský }
1546fed39564STomáš Golembiovský 
1547fed39564STomáš Golembiovský #endif
1548fed39564STomáš Golembiovský 
154946d4c572STomoki Sekiyama /* Return a list of the disk device(s)' info which @mount lies on */
build_guest_fsinfo(struct FsMount * mount,Error ** errp)155046d4c572STomoki Sekiyama static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount,
155146d4c572STomoki Sekiyama                                                Error **errp)
155246d4c572STomoki Sekiyama {
155346d4c572STomoki Sekiyama     GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs));
155425b5ff1aSChen Hanxiao     struct statvfs buf;
155525b5ff1aSChen Hanxiao     unsigned long used, nonroot_total, fr_size;
155646d4c572STomoki Sekiyama     char *devpath = g_strdup_printf("/sys/dev/block/%u:%u",
155746d4c572STomoki Sekiyama                                     mount->devmajor, mount->devminor);
155846d4c572STomoki Sekiyama 
155946d4c572STomoki Sekiyama     fs->mountpoint = g_strdup(mount->dirname);
156046d4c572STomoki Sekiyama     fs->type = g_strdup(mount->devtype);
156146d4c572STomoki Sekiyama     build_guest_fsinfo_for_device(devpath, fs, errp);
156246d4c572STomoki Sekiyama 
156325b5ff1aSChen Hanxiao     if (statvfs(fs->mountpoint, &buf) == 0) {
156425b5ff1aSChen Hanxiao         fr_size = buf.f_frsize;
156525b5ff1aSChen Hanxiao         used = buf.f_blocks - buf.f_bfree;
156625b5ff1aSChen Hanxiao         nonroot_total = used + buf.f_bavail;
156725b5ff1aSChen Hanxiao         fs->used_bytes = used * fr_size;
156825b5ff1aSChen Hanxiao         fs->total_bytes = nonroot_total * fr_size;
156925b5ff1aSChen Hanxiao 
157025b5ff1aSChen Hanxiao         fs->has_total_bytes = true;
157125b5ff1aSChen Hanxiao         fs->has_used_bytes = true;
157225b5ff1aSChen Hanxiao     }
157325b5ff1aSChen Hanxiao 
157446d4c572STomoki Sekiyama     g_free(devpath);
157525b5ff1aSChen Hanxiao 
157646d4c572STomoki Sekiyama     return fs;
157746d4c572STomoki Sekiyama }
157846d4c572STomoki Sekiyama 
qmp_guest_get_fsinfo(Error ** errp)157946d4c572STomoki Sekiyama GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
158046d4c572STomoki Sekiyama {
158146d4c572STomoki Sekiyama     FsMountList mounts;
158246d4c572STomoki Sekiyama     struct FsMount *mount;
158354aa3de7SEric Blake     GuestFilesystemInfoList *ret = NULL;
158446d4c572STomoki Sekiyama     Error *local_err = NULL;
158546d4c572STomoki Sekiyama 
158646d4c572STomoki Sekiyama     QTAILQ_INIT(&mounts);
1587561bfcb6SMarc-André Lureau     if (!build_fs_mount_list(&mounts, &local_err)) {
158846d4c572STomoki Sekiyama         error_propagate(errp, local_err);
158946d4c572STomoki Sekiyama         return NULL;
159046d4c572STomoki Sekiyama     }
159146d4c572STomoki Sekiyama 
159246d4c572STomoki Sekiyama     QTAILQ_FOREACH(mount, &mounts, next) {
159346d4c572STomoki Sekiyama         g_debug("Building guest fsinfo for '%s'", mount->dirname);
159446d4c572STomoki Sekiyama 
159554aa3de7SEric Blake         QAPI_LIST_PREPEND(ret, build_guest_fsinfo(mount, &local_err));
159646d4c572STomoki Sekiyama         if (local_err) {
159746d4c572STomoki Sekiyama             error_propagate(errp, local_err);
159846d4c572STomoki Sekiyama             qapi_free_GuestFilesystemInfoList(ret);
159946d4c572STomoki Sekiyama             ret = NULL;
160046d4c572STomoki Sekiyama             break;
160146d4c572STomoki Sekiyama         }
160246d4c572STomoki Sekiyama     }
160346d4c572STomoki Sekiyama 
160446d4c572STomoki Sekiyama     free_fs_mount_list(&mounts);
160546d4c572STomoki Sekiyama     return ret;
160646d4c572STomoki Sekiyama }
1607e72c3f2eSMichael Roth #endif /* CONFIG_FSFREEZE */
1608c216e5adSMichael Roth 
1609eab5fd59SPaolo Bonzini #if defined(CONFIG_FSTRIM)
1610eab5fd59SPaolo Bonzini /*
1611eab5fd59SPaolo Bonzini  * Walk list of mounted file systems in the guest, and trim them.
1612eab5fd59SPaolo Bonzini  */
1613e82855d9SJustin Ossevoort GuestFilesystemTrimResponse *
qmp_guest_fstrim(bool has_minimum,int64_t minimum,Error ** errp)1614e82855d9SJustin Ossevoort qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
1615eab5fd59SPaolo Bonzini {
1616e82855d9SJustin Ossevoort     GuestFilesystemTrimResponse *response;
1617e82855d9SJustin Ossevoort     GuestFilesystemTrimResult *result;
1618eab5fd59SPaolo Bonzini     int ret = 0;
1619eab5fd59SPaolo Bonzini     FsMountList mounts;
1620eab5fd59SPaolo Bonzini     struct FsMount *mount;
1621eab5fd59SPaolo Bonzini     int fd;
162273a652a1SJustin Ossevoort     struct fstrim_range r;
1623eab5fd59SPaolo Bonzini 
1624eab5fd59SPaolo Bonzini     slog("guest-fstrim called");
1625eab5fd59SPaolo Bonzini 
1626eab5fd59SPaolo Bonzini     QTAILQ_INIT(&mounts);
1627561bfcb6SMarc-André Lureau     if (!build_fs_mount_list(&mounts, errp)) {
1628e82855d9SJustin Ossevoort         return NULL;
1629eab5fd59SPaolo Bonzini     }
1630eab5fd59SPaolo Bonzini 
1631e82855d9SJustin Ossevoort     response = g_malloc0(sizeof(*response));
1632e82855d9SJustin Ossevoort 
1633eab5fd59SPaolo Bonzini     QTAILQ_FOREACH(mount, &mounts, next) {
1634e82855d9SJustin Ossevoort         result = g_malloc0(sizeof(*result));
1635e82855d9SJustin Ossevoort         result->path = g_strdup(mount->dirname);
1636e82855d9SJustin Ossevoort 
163754aa3de7SEric Blake         QAPI_LIST_PREPEND(response->paths, result);
1638e82855d9SJustin Ossevoort 
1639b9947c9cSMarc-André Lureau         fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0);
1640eab5fd59SPaolo Bonzini         if (fd == -1) {
1641e82855d9SJustin Ossevoort             result->error = g_strdup_printf("failed to open: %s",
1642e82855d9SJustin Ossevoort                                             strerror(errno));
1643e82855d9SJustin Ossevoort             continue;
1644eab5fd59SPaolo Bonzini         }
1645eab5fd59SPaolo Bonzini 
1646e35916acSMichael Tokarev         /* We try to cull filesystems we know won't work in advance, but other
1647e35916acSMichael Tokarev          * filesystems may not implement fstrim for less obvious reasons.
1648e35916acSMichael Tokarev          * These will report EOPNOTSUPP; while in some other cases ENOTTY
1649e35916acSMichael Tokarev          * will be reported (e.g. CD-ROMs).
1650e82855d9SJustin Ossevoort          * Any other error means an unexpected error.
1651eab5fd59SPaolo Bonzini          */
165273a652a1SJustin Ossevoort         r.start = 0;
165373a652a1SJustin Ossevoort         r.len = -1;
165473a652a1SJustin Ossevoort         r.minlen = has_minimum ? minimum : 0;
1655eab5fd59SPaolo Bonzini         ret = ioctl(fd, FITRIM, &r);
1656eab5fd59SPaolo Bonzini         if (ret == -1) {
1657e82855d9SJustin Ossevoort             if (errno == ENOTTY || errno == EOPNOTSUPP) {
1658e82855d9SJustin Ossevoort                 result->error = g_strdup("trim not supported");
1659e82855d9SJustin Ossevoort             } else {
1660e82855d9SJustin Ossevoort                 result->error = g_strdup_printf("failed to trim: %s",
1661e82855d9SJustin Ossevoort                                                 strerror(errno));
1662e82855d9SJustin Ossevoort             }
1663eab5fd59SPaolo Bonzini             close(fd);
1664e82855d9SJustin Ossevoort             continue;
1665eab5fd59SPaolo Bonzini         }
1666e82855d9SJustin Ossevoort 
1667e82855d9SJustin Ossevoort         result->has_minimum = true;
1668e82855d9SJustin Ossevoort         result->minimum = r.minlen;
1669e82855d9SJustin Ossevoort         result->has_trimmed = true;
1670e82855d9SJustin Ossevoort         result->trimmed = r.len;
1671eab5fd59SPaolo Bonzini         close(fd);
1672eab5fd59SPaolo Bonzini     }
1673eab5fd59SPaolo Bonzini 
1674eab5fd59SPaolo Bonzini     free_fs_mount_list(&mounts);
1675e82855d9SJustin Ossevoort     return response;
1676eab5fd59SPaolo Bonzini }
1677eab5fd59SPaolo Bonzini #endif /* CONFIG_FSTRIM */
1678eab5fd59SPaolo Bonzini 
1679eab5fd59SPaolo Bonzini 
168011d0f125SLuiz Capitulino #define LINUX_SYS_STATE_FILE "/sys/power/state"
168111d0f125SLuiz Capitulino #define SUSPEND_SUPPORTED 0
168211d0f125SLuiz Capitulino #define SUSPEND_NOT_SUPPORTED 1
168311d0f125SLuiz Capitulino 
16848b020b5eSDaniel Henrique Barboza typedef enum {
16858b020b5eSDaniel Henrique Barboza     SUSPEND_MODE_DISK = 0,
16868b020b5eSDaniel Henrique Barboza     SUSPEND_MODE_RAM = 1,
16878b020b5eSDaniel Henrique Barboza     SUSPEND_MODE_HYBRID = 2,
16888b020b5eSDaniel Henrique Barboza } SuspendMode;
16898b020b5eSDaniel Henrique Barboza 
16908b020b5eSDaniel Henrique Barboza /*
16918b020b5eSDaniel Henrique Barboza  * Executes a command in a child process using g_spawn_sync,
16928b020b5eSDaniel Henrique Barboza  * returning an int >= 0 representing the exit status of the
16938b020b5eSDaniel Henrique Barboza  * process.
16948b020b5eSDaniel Henrique Barboza  *
16958b020b5eSDaniel Henrique Barboza  * If the program wasn't found in path, returns -1.
16968b020b5eSDaniel Henrique Barboza  *
16978b020b5eSDaniel Henrique Barboza  * If a problem happened when creating the child process,
16988b020b5eSDaniel Henrique Barboza  * returns -1 and errp is set.
16998b020b5eSDaniel Henrique Barboza  */
run_process_child(const char * command[],Error ** errp)17008b020b5eSDaniel Henrique Barboza static int run_process_child(const char *command[], Error **errp)
17018b020b5eSDaniel Henrique Barboza {
17028b020b5eSDaniel Henrique Barboza     int exit_status, spawn_flag;
17038b020b5eSDaniel Henrique Barboza     GError *g_err = NULL;
17048b020b5eSDaniel Henrique Barboza     bool success;
17058b020b5eSDaniel Henrique Barboza 
17068b020b5eSDaniel Henrique Barboza     spawn_flag = G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL |
17078b020b5eSDaniel Henrique Barboza                  G_SPAWN_STDERR_TO_DEV_NULL;
17088b020b5eSDaniel Henrique Barboza 
1709fcc41961SMarc-André Lureau     success =  g_spawn_sync(NULL, (char **)command, NULL, spawn_flag,
17108b020b5eSDaniel Henrique Barboza                             NULL, NULL, NULL, NULL,
17118b020b5eSDaniel Henrique Barboza                             &exit_status, &g_err);
17128b020b5eSDaniel Henrique Barboza 
17138b020b5eSDaniel Henrique Barboza     if (success) {
17148b020b5eSDaniel Henrique Barboza         return WEXITSTATUS(exit_status);
17158b020b5eSDaniel Henrique Barboza     }
17168b020b5eSDaniel Henrique Barboza 
17178b020b5eSDaniel Henrique Barboza     if (g_err && (g_err->code != G_SPAWN_ERROR_NOENT)) {
17188b020b5eSDaniel Henrique Barboza         error_setg(errp, "failed to create child process, error '%s'",
17198b020b5eSDaniel Henrique Barboza                    g_err->message);
17208b020b5eSDaniel Henrique Barboza     }
17218b020b5eSDaniel Henrique Barboza 
17228b020b5eSDaniel Henrique Barboza     g_error_free(g_err);
17238b020b5eSDaniel Henrique Barboza     return -1;
17248b020b5eSDaniel Henrique Barboza }
17258b020b5eSDaniel Henrique Barboza 
systemd_supports_mode(SuspendMode mode,Error ** errp)1726067927d6SDaniel Henrique Barboza static bool systemd_supports_mode(SuspendMode mode, Error **errp)
1727067927d6SDaniel Henrique Barboza {
1728067927d6SDaniel Henrique Barboza     const char *systemctl_args[3] = {"systemd-hibernate", "systemd-suspend",
1729067927d6SDaniel Henrique Barboza                                      "systemd-hybrid-sleep"};
1730067927d6SDaniel Henrique Barboza     const char *cmd[4] = {"systemctl", "status", systemctl_args[mode], NULL};
1731067927d6SDaniel Henrique Barboza     int status;
1732067927d6SDaniel Henrique Barboza 
1733992861fbSMarkus Armbruster     status = run_process_child(cmd, errp);
1734067927d6SDaniel Henrique Barboza 
1735067927d6SDaniel Henrique Barboza     /*
1736067927d6SDaniel Henrique Barboza      * systemctl status uses LSB return codes so we can expect
1737067927d6SDaniel Henrique Barboza      * status > 0 and be ok. To assert if the guest has support
1738067927d6SDaniel Henrique Barboza      * for the selected suspend mode, status should be < 4. 4 is
1739067927d6SDaniel Henrique Barboza      * the code for unknown service status, the return value when
1740067927d6SDaniel Henrique Barboza      * the service does not exist. A common value is status = 3
1741067927d6SDaniel Henrique Barboza      * (program is not running).
1742067927d6SDaniel Henrique Barboza      */
1743067927d6SDaniel Henrique Barboza     if (status > 0 && status < 4) {
1744067927d6SDaniel Henrique Barboza         return true;
1745067927d6SDaniel Henrique Barboza     }
1746067927d6SDaniel Henrique Barboza 
1747067927d6SDaniel Henrique Barboza     return false;
1748067927d6SDaniel Henrique Barboza }
1749067927d6SDaniel Henrique Barboza 
systemd_suspend(SuspendMode mode,Error ** errp)1750067927d6SDaniel Henrique Barboza static void systemd_suspend(SuspendMode mode, Error **errp)
1751067927d6SDaniel Henrique Barboza {
1752067927d6SDaniel Henrique Barboza     Error *local_err = NULL;
1753067927d6SDaniel Henrique Barboza     const char *systemctl_args[3] = {"hibernate", "suspend", "hybrid-sleep"};
1754067927d6SDaniel Henrique Barboza     const char *cmd[3] = {"systemctl", systemctl_args[mode], NULL};
1755067927d6SDaniel Henrique Barboza     int status;
1756067927d6SDaniel Henrique Barboza 
1757067927d6SDaniel Henrique Barboza     status = run_process_child(cmd, &local_err);
1758067927d6SDaniel Henrique Barboza 
1759067927d6SDaniel Henrique Barboza     if (status == 0) {
1760067927d6SDaniel Henrique Barboza         return;
1761067927d6SDaniel Henrique Barboza     }
1762067927d6SDaniel Henrique Barboza 
1763067927d6SDaniel Henrique Barboza     if ((status == -1) && !local_err) {
1764067927d6SDaniel Henrique Barboza         error_setg(errp, "the helper program 'systemctl %s' was not found",
1765067927d6SDaniel Henrique Barboza                    systemctl_args[mode]);
1766067927d6SDaniel Henrique Barboza         return;
1767067927d6SDaniel Henrique Barboza     }
1768067927d6SDaniel Henrique Barboza 
1769067927d6SDaniel Henrique Barboza     if (local_err) {
1770067927d6SDaniel Henrique Barboza         error_propagate(errp, local_err);
1771067927d6SDaniel Henrique Barboza     } else {
1772067927d6SDaniel Henrique Barboza         error_setg(errp, "the helper program 'systemctl %s' returned an "
1773067927d6SDaniel Henrique Barboza                    "unexpected exit status code (%d)",
1774067927d6SDaniel Henrique Barboza                    systemctl_args[mode], status);
1775067927d6SDaniel Henrique Barboza     }
1776067927d6SDaniel Henrique Barboza }
1777067927d6SDaniel Henrique Barboza 
pmutils_supports_mode(SuspendMode mode,Error ** errp)17788b020b5eSDaniel Henrique Barboza static bool pmutils_supports_mode(SuspendMode mode, Error **errp)
177911d0f125SLuiz Capitulino {
17806b26e837SLuiz Capitulino     Error *local_err = NULL;
17818b020b5eSDaniel Henrique Barboza     const char *pmutils_args[3] = {"--hibernate", "--suspend",
17828b020b5eSDaniel Henrique Barboza                                    "--suspend-hybrid"};
17838b020b5eSDaniel Henrique Barboza     const char *cmd[3] = {"pm-is-supported", pmutils_args[mode], NULL};
1784dc8764f0SLuiz Capitulino     int status;
178511d0f125SLuiz Capitulino 
17868b020b5eSDaniel Henrique Barboza     status = run_process_child(cmd, &local_err);
1787304a0fcbSDaniel Henrique Barboza 
17888b020b5eSDaniel Henrique Barboza     if (status == SUSPEND_SUPPORTED) {
17898b020b5eSDaniel Henrique Barboza         return true;
1790304a0fcbSDaniel Henrique Barboza     }
1791304a0fcbSDaniel Henrique Barboza 
17928b020b5eSDaniel Henrique Barboza     if ((status == -1) && !local_err) {
17938b020b5eSDaniel Henrique Barboza         return false;
1794a5fcf0e3SDaniel Henrique Barboza     }
179511d0f125SLuiz Capitulino 
179684d18f06SMarkus Armbruster     if (local_err) {
179777dbc81bSMarkus Armbruster         error_propagate(errp, local_err);
17988b020b5eSDaniel Henrique Barboza     } else {
179977dbc81bSMarkus Armbruster         error_setg(errp,
18008b020b5eSDaniel Henrique Barboza                    "the helper program '%s' returned an unexpected exit"
18018b020b5eSDaniel Henrique Barboza                    " status code (%d)", "pm-is-supported", status);
1802dc8764f0SLuiz Capitulino     }
180311d0f125SLuiz Capitulino 
18048b020b5eSDaniel Henrique Barboza     return false;
1805a5fcf0e3SDaniel Henrique Barboza }
1806a5fcf0e3SDaniel Henrique Barboza 
pmutils_suspend(SuspendMode mode,Error ** errp)18078b020b5eSDaniel Henrique Barboza static void pmutils_suspend(SuspendMode mode, Error **errp)
1808246d76ebSDaniel Henrique Barboza {
1809246d76ebSDaniel Henrique Barboza     Error *local_err = NULL;
18108b020b5eSDaniel Henrique Barboza     const char *pmutils_binaries[3] = {"pm-hibernate", "pm-suspend",
18118b020b5eSDaniel Henrique Barboza                                        "pm-suspend-hybrid"};
18128b020b5eSDaniel Henrique Barboza     const char *cmd[2] = {pmutils_binaries[mode], NULL};
1813246d76ebSDaniel Henrique Barboza     int status;
1814246d76ebSDaniel Henrique Barboza 
18158b020b5eSDaniel Henrique Barboza     status = run_process_child(cmd, &local_err);
1816246d76ebSDaniel Henrique Barboza 
18178b020b5eSDaniel Henrique Barboza     if (status == 0) {
1818246d76ebSDaniel Henrique Barboza         return;
1819246d76ebSDaniel Henrique Barboza     }
1820246d76ebSDaniel Henrique Barboza 
18218b020b5eSDaniel Henrique Barboza     if ((status == -1) && !local_err) {
18228b020b5eSDaniel Henrique Barboza         error_setg(errp, "the helper program '%s' was not found",
18238b020b5eSDaniel Henrique Barboza                    pmutils_binaries[mode]);
1824246d76ebSDaniel Henrique Barboza         return;
1825246d76ebSDaniel Henrique Barboza     }
1826246d76ebSDaniel Henrique Barboza 
1827246d76ebSDaniel Henrique Barboza     if (local_err) {
1828246d76ebSDaniel Henrique Barboza         error_propagate(errp, local_err);
18298b020b5eSDaniel Henrique Barboza     } else {
1830246d76ebSDaniel Henrique Barboza         error_setg(errp,
18318b020b5eSDaniel Henrique Barboza                    "the helper program '%s' returned an unexpected exit"
18328b020b5eSDaniel Henrique Barboza                    " status code (%d)", pmutils_binaries[mode], status);
18338b020b5eSDaniel Henrique Barboza     }
1834246d76ebSDaniel Henrique Barboza }
1835246d76ebSDaniel Henrique Barboza 
linux_sys_state_supports_mode(SuspendMode mode,Error ** errp)18368b020b5eSDaniel Henrique Barboza static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp)
1837a5fcf0e3SDaniel Henrique Barboza {
18388b020b5eSDaniel Henrique Barboza     const char *sysfile_strs[3] = {"disk", "mem", NULL};
18398b020b5eSDaniel Henrique Barboza     const char *sysfile_str = sysfile_strs[mode];
1840a5fcf0e3SDaniel Henrique Barboza     char buf[32]; /* hopefully big enough */
1841a5fcf0e3SDaniel Henrique Barboza     int fd;
1842a5fcf0e3SDaniel Henrique Barboza     ssize_t ret;
1843a5fcf0e3SDaniel Henrique Barboza 
18448b020b5eSDaniel Henrique Barboza     if (!sysfile_str) {
18458b020b5eSDaniel Henrique Barboza         error_setg(errp, "unknown guest suspend mode");
1846a5fcf0e3SDaniel Henrique Barboza         return false;
1847a5fcf0e3SDaniel Henrique Barboza     }
1848a5fcf0e3SDaniel Henrique Barboza 
1849a5fcf0e3SDaniel Henrique Barboza     fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
1850a5fcf0e3SDaniel Henrique Barboza     if (fd < 0) {
1851a5fcf0e3SDaniel Henrique Barboza         return false;
1852a5fcf0e3SDaniel Henrique Barboza     }
1853a5fcf0e3SDaniel Henrique Barboza 
1854a5fcf0e3SDaniel Henrique Barboza     ret = read(fd, buf, sizeof(buf) - 1);
1855d9c745c1SPaolo Bonzini     close(fd);
1856a5fcf0e3SDaniel Henrique Barboza     if (ret <= 0) {
1857a5fcf0e3SDaniel Henrique Barboza         return false;
1858a5fcf0e3SDaniel Henrique Barboza     }
1859a5fcf0e3SDaniel Henrique Barboza     buf[ret] = '\0';
1860a5fcf0e3SDaniel Henrique Barboza 
1861a5fcf0e3SDaniel Henrique Barboza     if (strstr(buf, sysfile_str)) {
1862a5fcf0e3SDaniel Henrique Barboza         return true;
1863a5fcf0e3SDaniel Henrique Barboza     }
1864a5fcf0e3SDaniel Henrique Barboza     return false;
1865a5fcf0e3SDaniel Henrique Barboza }
1866a5fcf0e3SDaniel Henrique Barboza 
linux_sys_state_suspend(SuspendMode mode,Error ** errp)18678b020b5eSDaniel Henrique Barboza static void linux_sys_state_suspend(SuspendMode mode, Error **errp)
1868246d76ebSDaniel Henrique Barboza {
1869246d76ebSDaniel Henrique Barboza     Error *local_err = NULL;
18708b020b5eSDaniel Henrique Barboza     const char *sysfile_strs[3] = {"disk", "mem", NULL};
18718b020b5eSDaniel Henrique Barboza     const char *sysfile_str = sysfile_strs[mode];
1872246d76ebSDaniel Henrique Barboza     pid_t pid;
1873246d76ebSDaniel Henrique Barboza     int status;
1874246d76ebSDaniel Henrique Barboza 
18758b020b5eSDaniel Henrique Barboza     if (!sysfile_str) {
1876246d76ebSDaniel Henrique Barboza         error_setg(errp, "unknown guest suspend mode");
1877246d76ebSDaniel Henrique Barboza         return;
1878246d76ebSDaniel Henrique Barboza     }
1879246d76ebSDaniel Henrique Barboza 
1880246d76ebSDaniel Henrique Barboza     pid = fork();
1881246d76ebSDaniel Henrique Barboza     if (!pid) {
1882246d76ebSDaniel Henrique Barboza         /* child */
1883246d76ebSDaniel Henrique Barboza         int fd;
1884246d76ebSDaniel Henrique Barboza 
1885246d76ebSDaniel Henrique Barboza         setsid();
1886246d76ebSDaniel Henrique Barboza         reopen_fd_to_null(0);
1887246d76ebSDaniel Henrique Barboza         reopen_fd_to_null(1);
1888246d76ebSDaniel Henrique Barboza         reopen_fd_to_null(2);
1889246d76ebSDaniel Henrique Barboza 
1890246d76ebSDaniel Henrique Barboza         fd = open(LINUX_SYS_STATE_FILE, O_WRONLY);
1891246d76ebSDaniel Henrique Barboza         if (fd < 0) {
1892246d76ebSDaniel Henrique Barboza             _exit(EXIT_FAILURE);
1893246d76ebSDaniel Henrique Barboza         }
1894246d76ebSDaniel Henrique Barboza 
1895246d76ebSDaniel Henrique Barboza         if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) {
1896246d76ebSDaniel Henrique Barboza             _exit(EXIT_FAILURE);
1897246d76ebSDaniel Henrique Barboza         }
1898246d76ebSDaniel Henrique Barboza 
1899246d76ebSDaniel Henrique Barboza         _exit(EXIT_SUCCESS);
1900246d76ebSDaniel Henrique Barboza     } else if (pid < 0) {
1901246d76ebSDaniel Henrique Barboza         error_setg_errno(errp, errno, "failed to create child process");
1902246d76ebSDaniel Henrique Barboza         return;
1903246d76ebSDaniel Henrique Barboza     }
1904246d76ebSDaniel Henrique Barboza 
1905246d76ebSDaniel Henrique Barboza     ga_wait_child(pid, &status, &local_err);
1906246d76ebSDaniel Henrique Barboza     if (local_err) {
1907246d76ebSDaniel Henrique Barboza         error_propagate(errp, local_err);
1908246d76ebSDaniel Henrique Barboza         return;
1909246d76ebSDaniel Henrique Barboza     }
1910246d76ebSDaniel Henrique Barboza 
1911246d76ebSDaniel Henrique Barboza     if (WEXITSTATUS(status)) {
1912246d76ebSDaniel Henrique Barboza         error_setg(errp, "child process has failed to suspend");
1913246d76ebSDaniel Henrique Barboza     }
1914246d76ebSDaniel Henrique Barboza 
1915246d76ebSDaniel Henrique Barboza }
1916246d76ebSDaniel Henrique Barboza 
guest_suspend(SuspendMode mode,Error ** errp)19178b020b5eSDaniel Henrique Barboza static void guest_suspend(SuspendMode mode, Error **errp)
191811d0f125SLuiz Capitulino {
19197b376087SLuiz Capitulino     Error *local_err = NULL;
192073e1d8ebSDaniel Henrique Barboza     bool mode_supported = false;
192111d0f125SLuiz Capitulino 
192273e1d8ebSDaniel Henrique Barboza     if (systemd_supports_mode(mode, &local_err)) {
192373e1d8ebSDaniel Henrique Barboza         mode_supported = true;
1924067927d6SDaniel Henrique Barboza         systemd_suspend(mode, &local_err);
192573e1d8ebSDaniel Henrique Barboza 
1926067927d6SDaniel Henrique Barboza         if (!local_err) {
1927067927d6SDaniel Henrique Barboza             return;
1928067927d6SDaniel Henrique Barboza         }
192986dcb6abSMark Somerville     }
1930067927d6SDaniel Henrique Barboza 
1931067927d6SDaniel Henrique Barboza     error_free(local_err);
19326a4a3853SVladimir Sementsov-Ogievskiy     local_err = NULL;
1933067927d6SDaniel Henrique Barboza 
193473e1d8ebSDaniel Henrique Barboza     if (pmutils_supports_mode(mode, &local_err)) {
193573e1d8ebSDaniel Henrique Barboza         mode_supported = true;
19368b020b5eSDaniel Henrique Barboza         pmutils_suspend(mode, &local_err);
193773e1d8ebSDaniel Henrique Barboza 
1938246d76ebSDaniel Henrique Barboza         if (!local_err) {
1939304a0fcbSDaniel Henrique Barboza             return;
1940304a0fcbSDaniel Henrique Barboza         }
194186dcb6abSMark Somerville     }
1942304a0fcbSDaniel Henrique Barboza 
1943246d76ebSDaniel Henrique Barboza     error_free(local_err);
19446a4a3853SVladimir Sementsov-Ogievskiy     local_err = NULL;
194511d0f125SLuiz Capitulino 
194673e1d8ebSDaniel Henrique Barboza     if (linux_sys_state_supports_mode(mode, &local_err)) {
194773e1d8ebSDaniel Henrique Barboza         mode_supported = true;
19488b020b5eSDaniel Henrique Barboza         linux_sys_state_suspend(mode, &local_err);
194973e1d8ebSDaniel Henrique Barboza     }
195073e1d8ebSDaniel Henrique Barboza 
195173e1d8ebSDaniel Henrique Barboza     if (!mode_supported) {
19526a4a3853SVladimir Sementsov-Ogievskiy         error_free(local_err);
195373e1d8ebSDaniel Henrique Barboza         error_setg(errp,
195473e1d8ebSDaniel Henrique Barboza                    "the requested suspend mode is not supported by the guest");
1955b2322003SMarkus Armbruster     } else {
195677dbc81bSMarkus Armbruster         error_propagate(errp, local_err);
19577b376087SLuiz Capitulino     }
195811d0f125SLuiz Capitulino }
195911d0f125SLuiz Capitulino 
qmp_guest_suspend_disk(Error ** errp)196077dbc81bSMarkus Armbruster void qmp_guest_suspend_disk(Error **errp)
196111d0f125SLuiz Capitulino {
1962304a0fcbSDaniel Henrique Barboza     guest_suspend(SUSPEND_MODE_DISK, errp);
196311d0f125SLuiz Capitulino }
196411d0f125SLuiz Capitulino 
qmp_guest_suspend_ram(Error ** errp)196577dbc81bSMarkus Armbruster void qmp_guest_suspend_ram(Error **errp)
1966fbf42210SLuiz Capitulino {
1967304a0fcbSDaniel Henrique Barboza     guest_suspend(SUSPEND_MODE_RAM, errp);
1968fbf42210SLuiz Capitulino }
1969fbf42210SLuiz Capitulino 
qmp_guest_suspend_hybrid(Error ** errp)197077dbc81bSMarkus Armbruster void qmp_guest_suspend_hybrid(Error **errp)
197195f4f404SLuiz Capitulino {
1972304a0fcbSDaniel Henrique Barboza     guest_suspend(SUSPEND_MODE_HYBRID, errp);
197395f4f404SLuiz Capitulino }
197495f4f404SLuiz Capitulino 
1975d2baff62SLaszlo Ersek /* Transfer online/offline status between @vcpu and the guest system.
1976d2baff62SLaszlo Ersek  *
1977d2baff62SLaszlo Ersek  * On input either @errp or *@errp must be NULL.
1978d2baff62SLaszlo Ersek  *
1979d2baff62SLaszlo Ersek  * In system-to-@vcpu direction, the following @vcpu fields are accessed:
1980d2baff62SLaszlo Ersek  * - R: vcpu->logical_id
1981d2baff62SLaszlo Ersek  * - W: vcpu->online
1982d2baff62SLaszlo Ersek  * - W: vcpu->can_offline
1983d2baff62SLaszlo Ersek  *
1984d2baff62SLaszlo Ersek  * In @vcpu-to-system direction, the following @vcpu fields are accessed:
1985d2baff62SLaszlo Ersek  * - R: vcpu->logical_id
1986d2baff62SLaszlo Ersek  * - R: vcpu->online
1987d2baff62SLaszlo Ersek  *
1988d2baff62SLaszlo Ersek  * Written members remain unmodified on error.
1989d2baff62SLaszlo Ersek  */
transfer_vcpu(GuestLogicalProcessor * vcpu,bool sys2vcpu,char * dirpath,Error ** errp)1990d2baff62SLaszlo Ersek static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu,
1991b4bf912aSIgor Mammedov                           char *dirpath, Error **errp)
1992d2baff62SLaszlo Ersek {
1993b4bf912aSIgor Mammedov     int fd;
1994b4bf912aSIgor Mammedov     int res;
1995d2baff62SLaszlo Ersek     int dirfd;
1996b4bf912aSIgor Mammedov     static const char fn[] = "online";
1997d2baff62SLaszlo Ersek 
1998d2baff62SLaszlo Ersek     dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
1999d2baff62SLaszlo Ersek     if (dirfd == -1) {
2000d2baff62SLaszlo Ersek         error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
2001b4bf912aSIgor Mammedov         return;
2002b4bf912aSIgor Mammedov     }
2003d2baff62SLaszlo Ersek 
2004d2baff62SLaszlo Ersek     fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR);
2005d2baff62SLaszlo Ersek     if (fd == -1) {
2006d2baff62SLaszlo Ersek         if (errno != ENOENT) {
2007d2baff62SLaszlo Ersek             error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn);
2008d2baff62SLaszlo Ersek         } else if (sys2vcpu) {
2009d2baff62SLaszlo Ersek             vcpu->online = true;
2010d2baff62SLaszlo Ersek             vcpu->can_offline = false;
2011d2baff62SLaszlo Ersek         } else if (!vcpu->online) {
2012d2baff62SLaszlo Ersek             error_setg(errp, "logical processor #%" PRId64 " can't be "
2013d2baff62SLaszlo Ersek                        "offlined", vcpu->logical_id);
2014d2baff62SLaszlo Ersek         } /* otherwise pretend successful re-onlining */
2015d2baff62SLaszlo Ersek     } else {
2016d2baff62SLaszlo Ersek         unsigned char status;
2017d2baff62SLaszlo Ersek 
2018d2baff62SLaszlo Ersek         res = pread(fd, &status, 1, 0);
2019d2baff62SLaszlo Ersek         if (res == -1) {
2020d2baff62SLaszlo Ersek             error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn);
2021d2baff62SLaszlo Ersek         } else if (res == 0) {
2022d2baff62SLaszlo Ersek             error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath,
2023d2baff62SLaszlo Ersek                        fn);
2024d2baff62SLaszlo Ersek         } else if (sys2vcpu) {
2025d2baff62SLaszlo Ersek             vcpu->online = (status != '0');
2026d2baff62SLaszlo Ersek             vcpu->can_offline = true;
2027d2baff62SLaszlo Ersek         } else if (vcpu->online != (status != '0')) {
2028d2baff62SLaszlo Ersek             status = '0' + vcpu->online;
2029d2baff62SLaszlo Ersek             if (pwrite(fd, &status, 1, 0) == -1) {
2030d2baff62SLaszlo Ersek                 error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath,
2031d2baff62SLaszlo Ersek                                  fn);
2032d2baff62SLaszlo Ersek             }
2033d2baff62SLaszlo Ersek         } /* otherwise pretend successful re-(on|off)-lining */
2034d2baff62SLaszlo Ersek 
2035d2baff62SLaszlo Ersek         res = close(fd);
2036d2baff62SLaszlo Ersek         g_assert(res == 0);
2037d2baff62SLaszlo Ersek     }
2038d2baff62SLaszlo Ersek 
2039d2baff62SLaszlo Ersek     res = close(dirfd);
2040d2baff62SLaszlo Ersek     g_assert(res == 0);
2041d2baff62SLaszlo Ersek }
2042d2baff62SLaszlo Ersek 
qmp_guest_get_vcpus(Error ** errp)2043d2baff62SLaszlo Ersek GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
2044d2baff62SLaszlo Ersek {
2045c3033fd3SEric Blake     GuestLogicalProcessorList *head, **tail;
204627e7de3cSLin Ma     const char *cpu_dir = "/sys/devices/system/cpu";
204727e7de3cSLin Ma     const gchar *line;
204827e7de3cSLin Ma     g_autoptr(GDir) cpu_gdir = NULL;
2049d2baff62SLaszlo Ersek     Error *local_err = NULL;
2050d2baff62SLaszlo Ersek 
2051d2baff62SLaszlo Ersek     head = NULL;
2052c3033fd3SEric Blake     tail = &head;
205327e7de3cSLin Ma     cpu_gdir = g_dir_open(cpu_dir, 0, NULL);
2054d2baff62SLaszlo Ersek 
205527e7de3cSLin Ma     if (cpu_gdir == NULL) {
205627e7de3cSLin Ma         error_setg_errno(errp, errno, "failed to list entries: %s", cpu_dir);
205727e7de3cSLin Ma         return NULL;
205827e7de3cSLin Ma     }
205927e7de3cSLin Ma 
206027e7de3cSLin Ma     while (local_err == NULL && (line = g_dir_read_name(cpu_gdir)) != NULL) {
2061d2baff62SLaszlo Ersek         GuestLogicalProcessor *vcpu;
206227e7de3cSLin Ma         int64_t id;
206327e7de3cSLin Ma         if (sscanf(line, "cpu%" PRId64, &id)) {
206427e7de3cSLin Ma             g_autofree char *path = g_strdup_printf("/sys/devices/system/cpu/"
206527e7de3cSLin Ma                                                     "cpu%" PRId64 "/", id);
2066d2baff62SLaszlo Ersek             vcpu = g_malloc0(sizeof *vcpu);
2067b4bf912aSIgor Mammedov             vcpu->logical_id = id;
2068d2baff62SLaszlo Ersek             vcpu->has_can_offline = true; /* lolspeak ftw */
2069b4bf912aSIgor Mammedov             transfer_vcpu(vcpu, true, path, &local_err);
2070c3033fd3SEric Blake             QAPI_LIST_APPEND(tail, vcpu);
2071d2baff62SLaszlo Ersek         }
2072b4bf912aSIgor Mammedov     }
2073d2baff62SLaszlo Ersek 
2074d2baff62SLaszlo Ersek     if (local_err == NULL) {
2075d2baff62SLaszlo Ersek         /* there's no guest with zero VCPUs */
2076d2baff62SLaszlo Ersek         g_assert(head != NULL);
2077d2baff62SLaszlo Ersek         return head;
2078d2baff62SLaszlo Ersek     }
2079d2baff62SLaszlo Ersek 
2080d2baff62SLaszlo Ersek     qapi_free_GuestLogicalProcessorList(head);
2081d2baff62SLaszlo Ersek     error_propagate(errp, local_err);
2082d2baff62SLaszlo Ersek     return NULL;
2083d2baff62SLaszlo Ersek }
2084d2baff62SLaszlo Ersek 
qmp_guest_set_vcpus(GuestLogicalProcessorList * vcpus,Error ** errp)2085cbb65fc2SLaszlo Ersek int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
2086cbb65fc2SLaszlo Ersek {
2087cbb65fc2SLaszlo Ersek     int64_t processed;
2088cbb65fc2SLaszlo Ersek     Error *local_err = NULL;
2089cbb65fc2SLaszlo Ersek 
2090cbb65fc2SLaszlo Ersek     processed = 0;
2091cbb65fc2SLaszlo Ersek     while (vcpus != NULL) {
2092b4bf912aSIgor Mammedov         char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/",
2093b4bf912aSIgor Mammedov                                      vcpus->value->logical_id);
2094b4bf912aSIgor Mammedov 
2095b4bf912aSIgor Mammedov         transfer_vcpu(vcpus->value, false, path, &local_err);
2096b4bf912aSIgor Mammedov         g_free(path);
2097cbb65fc2SLaszlo Ersek         if (local_err != NULL) {
2098cbb65fc2SLaszlo Ersek             break;
2099cbb65fc2SLaszlo Ersek         }
2100cbb65fc2SLaszlo Ersek         ++processed;
2101cbb65fc2SLaszlo Ersek         vcpus = vcpus->next;
2102cbb65fc2SLaszlo Ersek     }
2103cbb65fc2SLaszlo Ersek 
2104cbb65fc2SLaszlo Ersek     if (local_err != NULL) {
2105cbb65fc2SLaszlo Ersek         if (processed == 0) {
2106cbb65fc2SLaszlo Ersek             error_propagate(errp, local_err);
2107cbb65fc2SLaszlo Ersek         } else {
2108cbb65fc2SLaszlo Ersek             error_free(local_err);
2109cbb65fc2SLaszlo Ersek         }
2110cbb65fc2SLaszlo Ersek     }
2111cbb65fc2SLaszlo Ersek 
2112cbb65fc2SLaszlo Ersek     return processed;
2113cbb65fc2SLaszlo Ersek }
21144fd0642eSAlexander Ivanov #endif /* __linux__ */
2115cbb65fc2SLaszlo Ersek 
21164fd0642eSAlexander Ivanov #if defined(__linux__) || defined(__FreeBSD__)
qmp_guest_set_user_password(const char * username,const char * password,bool crypted,Error ** errp)2117215a2771SDaniel P. Berrange void qmp_guest_set_user_password(const char *username,
2118215a2771SDaniel P. Berrange                                  const char *password,
2119215a2771SDaniel P. Berrange                                  bool crypted,
2120215a2771SDaniel P. Berrange                                  Error **errp)
2121215a2771SDaniel P. Berrange {
2122215a2771SDaniel P. Berrange     Error *local_err = NULL;
2123215a2771SDaniel P. Berrange     char *passwd_path = NULL;
2124215a2771SDaniel P. Berrange     pid_t pid;
2125215a2771SDaniel P. Berrange     int status;
2126215a2771SDaniel P. Berrange     int datafd[2] = { -1, -1 };
2127215a2771SDaniel P. Berrange     char *rawpasswddata = NULL;
2128215a2771SDaniel P. Berrange     size_t rawpasswdlen;
2129215a2771SDaniel P. Berrange     char *chpasswddata = NULL;
2130215a2771SDaniel P. Berrange     size_t chpasswdlen;
2131215a2771SDaniel P. Berrange 
2132920639caSDaniel P. Berrange     rawpasswddata = (char *)qbase64_decode(password, -1, &rawpasswdlen, errp);
2133920639caSDaniel P. Berrange     if (!rawpasswddata) {
2134920639caSDaniel P. Berrange         return;
2135920639caSDaniel P. Berrange     }
2136215a2771SDaniel P. Berrange     rawpasswddata = g_renew(char, rawpasswddata, rawpasswdlen + 1);
2137215a2771SDaniel P. Berrange     rawpasswddata[rawpasswdlen] = '\0';
2138215a2771SDaniel P. Berrange 
2139215a2771SDaniel P. Berrange     if (strchr(rawpasswddata, '\n')) {
2140215a2771SDaniel P. Berrange         error_setg(errp, "forbidden characters in raw password");
2141215a2771SDaniel P. Berrange         goto out;
2142215a2771SDaniel P. Berrange     }
2143215a2771SDaniel P. Berrange 
2144215a2771SDaniel P. Berrange     if (strchr(username, '\n') ||
2145215a2771SDaniel P. Berrange         strchr(username, ':')) {
2146215a2771SDaniel P. Berrange         error_setg(errp, "forbidden characters in username");
2147215a2771SDaniel P. Berrange         goto out;
2148215a2771SDaniel P. Berrange     }
2149215a2771SDaniel P. Berrange 
21504fd0642eSAlexander Ivanov #ifdef __FreeBSD__
21514fd0642eSAlexander Ivanov     chpasswddata = g_strdup(rawpasswddata);
21524fd0642eSAlexander Ivanov     passwd_path = g_find_program_in_path("pw");
21534fd0642eSAlexander Ivanov #else
2154215a2771SDaniel P. Berrange     chpasswddata = g_strdup_printf("%s:%s\n", username, rawpasswddata);
2155215a2771SDaniel P. Berrange     passwd_path = g_find_program_in_path("chpasswd");
21564fd0642eSAlexander Ivanov #endif
21574fd0642eSAlexander Ivanov 
21584fd0642eSAlexander Ivanov     chpasswdlen = strlen(chpasswddata);
2159215a2771SDaniel P. Berrange 
2160215a2771SDaniel P. Berrange     if (!passwd_path) {
2161215a2771SDaniel P. Berrange         error_setg(errp, "cannot find 'passwd' program in PATH");
2162215a2771SDaniel P. Berrange         goto out;
2163215a2771SDaniel P. Berrange     }
2164215a2771SDaniel P. Berrange 
2165ed78331dSMarc-André Lureau     if (!g_unix_open_pipe(datafd, FD_CLOEXEC, NULL)) {
2166215a2771SDaniel P. Berrange         error_setg(errp, "cannot create pipe FDs");
2167215a2771SDaniel P. Berrange         goto out;
2168215a2771SDaniel P. Berrange     }
2169215a2771SDaniel P. Berrange 
2170215a2771SDaniel P. Berrange     pid = fork();
2171215a2771SDaniel P. Berrange     if (pid == 0) {
2172215a2771SDaniel P. Berrange         close(datafd[1]);
2173215a2771SDaniel P. Berrange         /* child */
2174215a2771SDaniel P. Berrange         setsid();
2175215a2771SDaniel P. Berrange         dup2(datafd[0], 0);
2176215a2771SDaniel P. Berrange         reopen_fd_to_null(1);
2177215a2771SDaniel P. Berrange         reopen_fd_to_null(2);
2178215a2771SDaniel P. Berrange 
21794fd0642eSAlexander Ivanov #ifdef __FreeBSD__
21804fd0642eSAlexander Ivanov         const char *h_arg;
21814fd0642eSAlexander Ivanov         h_arg = (crypted) ? "-H" : "-h";
21824fd0642eSAlexander Ivanov         execl(passwd_path, "pw", "usermod", "-n", username, h_arg, "0", NULL);
21834fd0642eSAlexander Ivanov #else
2184215a2771SDaniel P. Berrange         if (crypted) {
2185fcc41961SMarc-André Lureau             execl(passwd_path, "chpasswd", "-e", NULL);
2186215a2771SDaniel P. Berrange         } else {
2187fcc41961SMarc-André Lureau             execl(passwd_path, "chpasswd", NULL);
2188215a2771SDaniel P. Berrange         }
21894fd0642eSAlexander Ivanov #endif
2190215a2771SDaniel P. Berrange         _exit(EXIT_FAILURE);
2191215a2771SDaniel P. Berrange     } else if (pid < 0) {
2192215a2771SDaniel P. Berrange         error_setg_errno(errp, errno, "failed to create child process");
2193215a2771SDaniel P. Berrange         goto out;
2194215a2771SDaniel P. Berrange     }
2195215a2771SDaniel P. Berrange     close(datafd[0]);
2196215a2771SDaniel P. Berrange     datafd[0] = -1;
2197215a2771SDaniel P. Berrange 
2198215a2771SDaniel P. Berrange     if (qemu_write_full(datafd[1], chpasswddata, chpasswdlen) != chpasswdlen) {
2199215a2771SDaniel P. Berrange         error_setg_errno(errp, errno, "cannot write new account password");
2200215a2771SDaniel P. Berrange         goto out;
2201215a2771SDaniel P. Berrange     }
2202215a2771SDaniel P. Berrange     close(datafd[1]);
2203215a2771SDaniel P. Berrange     datafd[1] = -1;
2204215a2771SDaniel P. Berrange 
2205215a2771SDaniel P. Berrange     ga_wait_child(pid, &status, &local_err);
2206215a2771SDaniel P. Berrange     if (local_err) {
2207215a2771SDaniel P. Berrange         error_propagate(errp, local_err);
2208215a2771SDaniel P. Berrange         goto out;
2209215a2771SDaniel P. Berrange     }
2210215a2771SDaniel P. Berrange 
2211215a2771SDaniel P. Berrange     if (!WIFEXITED(status)) {
2212215a2771SDaniel P. Berrange         error_setg(errp, "child process has terminated abnormally");
2213215a2771SDaniel P. Berrange         goto out;
2214215a2771SDaniel P. Berrange     }
2215215a2771SDaniel P. Berrange 
2216215a2771SDaniel P. Berrange     if (WEXITSTATUS(status)) {
2217215a2771SDaniel P. Berrange         error_setg(errp, "child process has failed to set user password");
2218215a2771SDaniel P. Berrange         goto out;
2219215a2771SDaniel P. Berrange     }
2220215a2771SDaniel P. Berrange 
2221215a2771SDaniel P. Berrange out:
2222215a2771SDaniel P. Berrange     g_free(chpasswddata);
2223215a2771SDaniel P. Berrange     g_free(rawpasswddata);
2224215a2771SDaniel P. Berrange     g_free(passwd_path);
2225215a2771SDaniel P. Berrange     if (datafd[0] != -1) {
2226215a2771SDaniel P. Berrange         close(datafd[0]);
2227215a2771SDaniel P. Berrange     }
2228215a2771SDaniel P. Berrange     if (datafd[1] != -1) {
2229215a2771SDaniel P. Berrange         close(datafd[1]);
2230215a2771SDaniel P. Berrange     }
2231215a2771SDaniel P. Berrange }
22324fd0642eSAlexander Ivanov #else /* __linux__ || __FreeBSD__ */
qmp_guest_set_user_password(const char * username,const char * password,bool crypted,Error ** errp)22334fd0642eSAlexander Ivanov void qmp_guest_set_user_password(const char *username,
22344fd0642eSAlexander Ivanov                                  const char *password,
22354fd0642eSAlexander Ivanov                                  bool crypted,
22364fd0642eSAlexander Ivanov                                  Error **errp)
22374fd0642eSAlexander Ivanov {
22384fd0642eSAlexander Ivanov     error_setg(errp, QERR_UNSUPPORTED);
22394fd0642eSAlexander Ivanov }
22404fd0642eSAlexander Ivanov #endif /* __linux__ || __FreeBSD__ */
2241215a2771SDaniel P. Berrange 
22424fd0642eSAlexander Ivanov #ifdef __linux__
ga_read_sysfs_file(int dirfd,const char * pathname,char * buf,int size,Error ** errp)2243bd240fcaSzhanghailiang static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf,
2244bd240fcaSzhanghailiang                                int size, Error **errp)
2245bd240fcaSzhanghailiang {
2246bd240fcaSzhanghailiang     int fd;
2247bd240fcaSzhanghailiang     int res;
2248bd240fcaSzhanghailiang 
2249bd240fcaSzhanghailiang     errno = 0;
2250bd240fcaSzhanghailiang     fd = openat(dirfd, pathname, O_RDONLY);
2251bd240fcaSzhanghailiang     if (fd == -1) {
2252bd240fcaSzhanghailiang         error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
2253bd240fcaSzhanghailiang         return;
2254bd240fcaSzhanghailiang     }
2255bd240fcaSzhanghailiang 
2256bd240fcaSzhanghailiang     res = pread(fd, buf, size, 0);
2257bd240fcaSzhanghailiang     if (res == -1) {
2258bd240fcaSzhanghailiang         error_setg_errno(errp, errno, "pread sysfs file \"%s\"", pathname);
2259bd240fcaSzhanghailiang     } else if (res == 0) {
2260bd240fcaSzhanghailiang         error_setg(errp, "pread sysfs file \"%s\": unexpected EOF", pathname);
2261bd240fcaSzhanghailiang     }
2262bd240fcaSzhanghailiang     close(fd);
2263bd240fcaSzhanghailiang }
2264bd240fcaSzhanghailiang 
ga_write_sysfs_file(int dirfd,const char * pathname,const char * buf,int size,Error ** errp)2265bd240fcaSzhanghailiang static void ga_write_sysfs_file(int dirfd, const char *pathname,
2266bd240fcaSzhanghailiang                                 const char *buf, int size, Error **errp)
2267bd240fcaSzhanghailiang {
2268bd240fcaSzhanghailiang     int fd;
2269bd240fcaSzhanghailiang 
2270bd240fcaSzhanghailiang     errno = 0;
2271bd240fcaSzhanghailiang     fd = openat(dirfd, pathname, O_WRONLY);
2272bd240fcaSzhanghailiang     if (fd == -1) {
2273bd240fcaSzhanghailiang         error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
2274bd240fcaSzhanghailiang         return;
2275bd240fcaSzhanghailiang     }
2276bd240fcaSzhanghailiang 
2277bd240fcaSzhanghailiang     if (pwrite(fd, buf, size, 0) == -1) {
2278bd240fcaSzhanghailiang         error_setg_errno(errp, errno, "pwrite sysfs file \"%s\"", pathname);
2279bd240fcaSzhanghailiang     }
2280bd240fcaSzhanghailiang 
2281bd240fcaSzhanghailiang     close(fd);
2282bd240fcaSzhanghailiang }
2283bd240fcaSzhanghailiang 
2284bd240fcaSzhanghailiang /* Transfer online/offline status between @mem_blk and the guest system.
2285bd240fcaSzhanghailiang  *
2286bd240fcaSzhanghailiang  * On input either @errp or *@errp must be NULL.
2287bd240fcaSzhanghailiang  *
2288bd240fcaSzhanghailiang  * In system-to-@mem_blk direction, the following @mem_blk fields are accessed:
2289bd240fcaSzhanghailiang  * - R: mem_blk->phys_index
2290bd240fcaSzhanghailiang  * - W: mem_blk->online
2291bd240fcaSzhanghailiang  * - W: mem_blk->can_offline
2292bd240fcaSzhanghailiang  *
2293bd240fcaSzhanghailiang  * In @mem_blk-to-system direction, the following @mem_blk fields are accessed:
2294bd240fcaSzhanghailiang  * - R: mem_blk->phys_index
2295bd240fcaSzhanghailiang  * - R: mem_blk->online
2296bd240fcaSzhanghailiang  *-  R: mem_blk->can_offline
2297bd240fcaSzhanghailiang  * Written members remain unmodified on error.
2298bd240fcaSzhanghailiang  */
transfer_memory_block(GuestMemoryBlock * mem_blk,bool sys2memblk,GuestMemoryBlockResponse * result,Error ** errp)2299bd240fcaSzhanghailiang static void transfer_memory_block(GuestMemoryBlock *mem_blk, bool sys2memblk,
2300bd240fcaSzhanghailiang                                   GuestMemoryBlockResponse *result,
2301bd240fcaSzhanghailiang                                   Error **errp)
2302bd240fcaSzhanghailiang {
2303bd240fcaSzhanghailiang     char *dirpath;
2304bd240fcaSzhanghailiang     int dirfd;
2305bd240fcaSzhanghailiang     char *status;
2306bd240fcaSzhanghailiang     Error *local_err = NULL;
2307bd240fcaSzhanghailiang 
2308bd240fcaSzhanghailiang     if (!sys2memblk) {
2309bd240fcaSzhanghailiang         DIR *dp;
2310bd240fcaSzhanghailiang 
2311bd240fcaSzhanghailiang         if (!result) {
2312bd240fcaSzhanghailiang             error_setg(errp, "Internal error, 'result' should not be NULL");
2313bd240fcaSzhanghailiang             return;
2314bd240fcaSzhanghailiang         }
2315bd240fcaSzhanghailiang         errno = 0;
2316bd240fcaSzhanghailiang         dp = opendir("/sys/devices/system/memory/");
2317bd240fcaSzhanghailiang          /* if there is no 'memory' directory in sysfs,
2318bd240fcaSzhanghailiang          * we think this VM does not support online/offline memory block,
2319bd240fcaSzhanghailiang          * any other solution?
2320bd240fcaSzhanghailiang          */
23219879f5acSPhilippe Mathieu-Daudé         if (!dp) {
23229879f5acSPhilippe Mathieu-Daudé             if (errno == ENOENT) {
2323bd240fcaSzhanghailiang                 result->response =
2324bd240fcaSzhanghailiang                     GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
23259879f5acSPhilippe Mathieu-Daudé             }
2326bd240fcaSzhanghailiang             goto out1;
2327bd240fcaSzhanghailiang         }
2328bd240fcaSzhanghailiang         closedir(dp);
2329bd240fcaSzhanghailiang     }
2330bd240fcaSzhanghailiang 
2331bd240fcaSzhanghailiang     dirpath = g_strdup_printf("/sys/devices/system/memory/memory%" PRId64 "/",
2332bd240fcaSzhanghailiang                               mem_blk->phys_index);
2333bd240fcaSzhanghailiang     dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
2334bd240fcaSzhanghailiang     if (dirfd == -1) {
2335bd240fcaSzhanghailiang         if (sys2memblk) {
2336bd240fcaSzhanghailiang             error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
2337bd240fcaSzhanghailiang         } else {
2338bd240fcaSzhanghailiang             if (errno == ENOENT) {
2339bd240fcaSzhanghailiang                 result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_NOT_FOUND;
2340bd240fcaSzhanghailiang             } else {
2341bd240fcaSzhanghailiang                 result->response =
2342bd240fcaSzhanghailiang                     GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
2343bd240fcaSzhanghailiang             }
2344bd240fcaSzhanghailiang         }
2345bd240fcaSzhanghailiang         g_free(dirpath);
2346bd240fcaSzhanghailiang         goto out1;
2347bd240fcaSzhanghailiang     }
2348bd240fcaSzhanghailiang     g_free(dirpath);
2349bd240fcaSzhanghailiang 
2350bd240fcaSzhanghailiang     status = g_malloc0(10);
2351bd240fcaSzhanghailiang     ga_read_sysfs_file(dirfd, "state", status, 10, &local_err);
2352bd240fcaSzhanghailiang     if (local_err) {
2353bd240fcaSzhanghailiang         /* treat with sysfs file that not exist in old kernel */
2354bd240fcaSzhanghailiang         if (errno == ENOENT) {
2355bd240fcaSzhanghailiang             error_free(local_err);
2356bd240fcaSzhanghailiang             if (sys2memblk) {
2357bd240fcaSzhanghailiang                 mem_blk->online = true;
2358bd240fcaSzhanghailiang                 mem_blk->can_offline = false;
2359bd240fcaSzhanghailiang             } else if (!mem_blk->online) {
2360bd240fcaSzhanghailiang                 result->response =
2361bd240fcaSzhanghailiang                     GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
2362bd240fcaSzhanghailiang             }
2363bd240fcaSzhanghailiang         } else {
2364bd240fcaSzhanghailiang             if (sys2memblk) {
2365bd240fcaSzhanghailiang                 error_propagate(errp, local_err);
2366bd240fcaSzhanghailiang             } else {
2367b368123dSMarkus Armbruster                 error_free(local_err);
2368bd240fcaSzhanghailiang                 result->response =
2369bd240fcaSzhanghailiang                     GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
2370bd240fcaSzhanghailiang             }
2371bd240fcaSzhanghailiang         }
2372bd240fcaSzhanghailiang         goto out2;
2373bd240fcaSzhanghailiang     }
2374bd240fcaSzhanghailiang 
2375bd240fcaSzhanghailiang     if (sys2memblk) {
2376bd240fcaSzhanghailiang         char removable = '0';
2377bd240fcaSzhanghailiang 
2378bd240fcaSzhanghailiang         mem_blk->online = (strncmp(status, "online", 6) == 0);
2379bd240fcaSzhanghailiang 
2380bd240fcaSzhanghailiang         ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err);
2381bd240fcaSzhanghailiang         if (local_err) {
238267cc32ebSVeres Lajos             /* if no 'removable' file, it doesn't support offline mem blk */
2383bd240fcaSzhanghailiang             if (errno == ENOENT) {
2384bd240fcaSzhanghailiang                 error_free(local_err);
2385bd240fcaSzhanghailiang                 mem_blk->can_offline = false;
2386bd240fcaSzhanghailiang             } else {
2387bd240fcaSzhanghailiang                 error_propagate(errp, local_err);
2388bd240fcaSzhanghailiang             }
2389bd240fcaSzhanghailiang         } else {
2390bd240fcaSzhanghailiang             mem_blk->can_offline = (removable != '0');
2391bd240fcaSzhanghailiang         }
2392bd240fcaSzhanghailiang     } else {
2393bd240fcaSzhanghailiang         if (mem_blk->online != (strncmp(status, "online", 6) == 0)) {
23947064024dSMarc-André Lureau             const char *new_state = mem_blk->online ? "online" : "offline";
2395bd240fcaSzhanghailiang 
2396bd240fcaSzhanghailiang             ga_write_sysfs_file(dirfd, "state", new_state, strlen(new_state),
2397bd240fcaSzhanghailiang                                 &local_err);
2398bd240fcaSzhanghailiang             if (local_err) {
2399bd240fcaSzhanghailiang                 error_free(local_err);
2400bd240fcaSzhanghailiang                 result->response =
2401bd240fcaSzhanghailiang                     GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
2402bd240fcaSzhanghailiang                 goto out2;
2403bd240fcaSzhanghailiang             }
2404bd240fcaSzhanghailiang 
2405bd240fcaSzhanghailiang             result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_SUCCESS;
2406bd240fcaSzhanghailiang             result->has_error_code = false;
2407bd240fcaSzhanghailiang         } /* otherwise pretend successful re-(on|off)-lining */
2408bd240fcaSzhanghailiang     }
2409bd240fcaSzhanghailiang     g_free(status);
2410bd240fcaSzhanghailiang     close(dirfd);
2411bd240fcaSzhanghailiang     return;
2412bd240fcaSzhanghailiang 
2413bd240fcaSzhanghailiang out2:
2414bd240fcaSzhanghailiang     g_free(status);
2415bd240fcaSzhanghailiang     close(dirfd);
2416bd240fcaSzhanghailiang out1:
2417bd240fcaSzhanghailiang     if (!sys2memblk) {
2418bd240fcaSzhanghailiang         result->has_error_code = true;
2419bd240fcaSzhanghailiang         result->error_code = errno;
2420bd240fcaSzhanghailiang     }
2421bd240fcaSzhanghailiang }
2422bd240fcaSzhanghailiang 
qmp_guest_get_memory_blocks(Error ** errp)2423a065aaa9Szhanghailiang GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
2424a065aaa9Szhanghailiang {
2425c3033fd3SEric Blake     GuestMemoryBlockList *head, **tail;
2426bd240fcaSzhanghailiang     Error *local_err = NULL;
2427bd240fcaSzhanghailiang     struct dirent *de;
2428bd240fcaSzhanghailiang     DIR *dp;
2429bd240fcaSzhanghailiang 
2430bd240fcaSzhanghailiang     head = NULL;
2431c3033fd3SEric Blake     tail = &head;
2432bd240fcaSzhanghailiang 
2433bd240fcaSzhanghailiang     dp = opendir("/sys/devices/system/memory/");
2434bd240fcaSzhanghailiang     if (!dp) {
2435f693fe6eSMichael Roth         /* it's ok if this happens to be a system that doesn't expose
2436f693fe6eSMichael Roth          * memory blocks via sysfs, but otherwise we should report
2437f693fe6eSMichael Roth          * an error
2438f693fe6eSMichael Roth          */
2439f693fe6eSMichael Roth         if (errno != ENOENT) {
2440bd240fcaSzhanghailiang             error_setg_errno(errp, errno, "Can't open directory"
24419af9e0feSMarkus Armbruster                              "\"/sys/devices/system/memory/\"");
2442f693fe6eSMichael Roth         }
2443bd240fcaSzhanghailiang         return NULL;
2444bd240fcaSzhanghailiang     }
2445bd240fcaSzhanghailiang 
2446bd240fcaSzhanghailiang     /* Note: the phys_index of memory block may be discontinuous,
2447bd240fcaSzhanghailiang      * this is because a memblk is the unit of the Sparse Memory design, which
2448bd240fcaSzhanghailiang      * allows discontinuous memory ranges (ex. NUMA), so here we should
2449bd240fcaSzhanghailiang      * traverse the memory block directory.
2450bd240fcaSzhanghailiang      */
2451bd240fcaSzhanghailiang     while ((de = readdir(dp)) != NULL) {
2452bd240fcaSzhanghailiang         GuestMemoryBlock *mem_blk;
2453bd240fcaSzhanghailiang 
2454bd240fcaSzhanghailiang         if ((strncmp(de->d_name, "memory", 6) != 0) ||
2455bd240fcaSzhanghailiang             !(de->d_type & DT_DIR)) {
2456bd240fcaSzhanghailiang             continue;
2457bd240fcaSzhanghailiang         }
2458bd240fcaSzhanghailiang 
2459bd240fcaSzhanghailiang         mem_blk = g_malloc0(sizeof *mem_blk);
2460bd240fcaSzhanghailiang         /* The d_name is "memoryXXX",  phys_index is block id, same as XXX */
2461bd240fcaSzhanghailiang         mem_blk->phys_index = strtoul(&de->d_name[6], NULL, 10);
2462bd240fcaSzhanghailiang         mem_blk->has_can_offline = true; /* lolspeak ftw */
2463bd240fcaSzhanghailiang         transfer_memory_block(mem_blk, true, NULL, &local_err);
24644155c998SMarkus Armbruster         if (local_err) {
24654155c998SMarkus Armbruster             break;
24664155c998SMarkus Armbruster         }
2467bd240fcaSzhanghailiang 
2468c3033fd3SEric Blake         QAPI_LIST_APPEND(tail, mem_blk);
2469bd240fcaSzhanghailiang     }
2470bd240fcaSzhanghailiang 
2471bd240fcaSzhanghailiang     closedir(dp);
2472bd240fcaSzhanghailiang     if (local_err == NULL) {
2473bd240fcaSzhanghailiang         /* there's no guest with zero memory blocks */
2474bd240fcaSzhanghailiang         if (head == NULL) {
2475bd240fcaSzhanghailiang             error_setg(errp, "guest reported zero memory blocks!");
2476bd240fcaSzhanghailiang         }
2477bd240fcaSzhanghailiang         return head;
2478bd240fcaSzhanghailiang     }
2479bd240fcaSzhanghailiang 
2480bd240fcaSzhanghailiang     qapi_free_GuestMemoryBlockList(head);
2481bd240fcaSzhanghailiang     error_propagate(errp, local_err);
2482a065aaa9Szhanghailiang     return NULL;
2483a065aaa9Szhanghailiang }
2484a065aaa9Szhanghailiang 
2485a065aaa9Szhanghailiang GuestMemoryBlockResponseList *
qmp_guest_set_memory_blocks(GuestMemoryBlockList * mem_blks,Error ** errp)2486a065aaa9Szhanghailiang qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
2487a065aaa9Szhanghailiang {
2488c3033fd3SEric Blake     GuestMemoryBlockResponseList *head, **tail;
248932ca7927Szhanghailiang     Error *local_err = NULL;
249032ca7927Szhanghailiang 
249132ca7927Szhanghailiang     head = NULL;
2492c3033fd3SEric Blake     tail = &head;
249332ca7927Szhanghailiang 
249432ca7927Szhanghailiang     while (mem_blks != NULL) {
249532ca7927Szhanghailiang         GuestMemoryBlockResponse *result;
249632ca7927Szhanghailiang         GuestMemoryBlock *current_mem_blk = mem_blks->value;
249732ca7927Szhanghailiang 
249832ca7927Szhanghailiang         result = g_malloc0(sizeof(*result));
249932ca7927Szhanghailiang         result->phys_index = current_mem_blk->phys_index;
250032ca7927Szhanghailiang         transfer_memory_block(current_mem_blk, false, result, &local_err);
250132ca7927Szhanghailiang         if (local_err) { /* should never happen */
250232ca7927Szhanghailiang             goto err;
250332ca7927Szhanghailiang         }
250432ca7927Szhanghailiang 
2505c3033fd3SEric Blake         QAPI_LIST_APPEND(tail, result);
250632ca7927Szhanghailiang         mem_blks = mem_blks->next;
250732ca7927Szhanghailiang     }
250832ca7927Szhanghailiang 
250932ca7927Szhanghailiang     return head;
251032ca7927Szhanghailiang err:
251132ca7927Szhanghailiang     qapi_free_GuestMemoryBlockResponseList(head);
251232ca7927Szhanghailiang     error_propagate(errp, local_err);
2513a065aaa9Szhanghailiang     return NULL;
2514a065aaa9Szhanghailiang }
2515a065aaa9Szhanghailiang 
qmp_guest_get_memory_block_info(Error ** errp)2516a065aaa9Szhanghailiang GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
2517a065aaa9Szhanghailiang {
2518ef82b60bSzhanghailiang     Error *local_err = NULL;
2519ef82b60bSzhanghailiang     char *dirpath;
2520ef82b60bSzhanghailiang     int dirfd;
2521ef82b60bSzhanghailiang     char *buf;
2522ef82b60bSzhanghailiang     GuestMemoryBlockInfo *info;
2523ef82b60bSzhanghailiang 
2524ef82b60bSzhanghailiang     dirpath = g_strdup_printf("/sys/devices/system/memory/");
2525ef82b60bSzhanghailiang     dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
2526ef82b60bSzhanghailiang     if (dirfd == -1) {
2527ef82b60bSzhanghailiang         error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
2528ef82b60bSzhanghailiang         g_free(dirpath);
2529a065aaa9Szhanghailiang         return NULL;
2530a065aaa9Szhanghailiang     }
2531ef82b60bSzhanghailiang     g_free(dirpath);
2532ef82b60bSzhanghailiang 
2533ef82b60bSzhanghailiang     buf = g_malloc0(20);
2534ef82b60bSzhanghailiang     ga_read_sysfs_file(dirfd, "block_size_bytes", buf, 20, &local_err);
25358ce1ee46SShannon Zhao     close(dirfd);
2536ef82b60bSzhanghailiang     if (local_err) {
2537ef82b60bSzhanghailiang         g_free(buf);
2538ef82b60bSzhanghailiang         error_propagate(errp, local_err);
2539ef82b60bSzhanghailiang         return NULL;
2540ef82b60bSzhanghailiang     }
2541ef82b60bSzhanghailiang 
2542ef82b60bSzhanghailiang     info = g_new0(GuestMemoryBlockInfo, 1);
2543ef82b60bSzhanghailiang     info->size = strtol(buf, NULL, 16); /* the unit is bytes */
2544ef82b60bSzhanghailiang 
2545ef82b60bSzhanghailiang     g_free(buf);
2546ef82b60bSzhanghailiang 
2547ef82b60bSzhanghailiang     return info;
2548ef82b60bSzhanghailiang }
2549a065aaa9Szhanghailiang 
25503569664eSluzhipeng #define MAX_NAME_LEN 128
guest_get_diskstats(Error ** errp)25513569664eSluzhipeng static GuestDiskStatsInfoList *guest_get_diskstats(Error **errp)
25523569664eSluzhipeng {
25533569664eSluzhipeng #ifdef CONFIG_LINUX
25543569664eSluzhipeng     GuestDiskStatsInfoList *head = NULL, **tail = &head;
25553569664eSluzhipeng     const char *diskstats = "/proc/diskstats";
25563569664eSluzhipeng     FILE *fp;
25573569664eSluzhipeng     size_t n;
25583569664eSluzhipeng     char *line = NULL;
25593569664eSluzhipeng 
25603569664eSluzhipeng     fp = fopen(diskstats, "r");
25613569664eSluzhipeng     if (fp  == NULL) {
25623569664eSluzhipeng         error_setg_errno(errp, errno, "open(\"%s\")", diskstats);
25633569664eSluzhipeng         return NULL;
25643569664eSluzhipeng     }
25653569664eSluzhipeng 
25663569664eSluzhipeng     while (getline(&line, &n, fp) != -1) {
25673569664eSluzhipeng         g_autofree GuestDiskStatsInfo *diskstatinfo = NULL;
25683569664eSluzhipeng         g_autofree GuestDiskStats *diskstat = NULL;
25693569664eSluzhipeng         char dev_name[MAX_NAME_LEN];
25703569664eSluzhipeng         unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks, dc_ticks, fl_ticks;
25713569664eSluzhipeng         unsigned long rd_ios, rd_merges_or_rd_sec, rd_ticks_or_wr_sec, wr_ios;
25723569664eSluzhipeng         unsigned long wr_merges, rd_sec_or_wr_ios, wr_sec;
25733569664eSluzhipeng         unsigned long dc_ios, dc_merges, dc_sec, fl_ios;
25743569664eSluzhipeng         unsigned int major, minor;
25753569664eSluzhipeng         int i;
25763569664eSluzhipeng 
25773569664eSluzhipeng         i = sscanf(line, "%u %u %s %lu %lu %lu"
25783569664eSluzhipeng                    "%lu %lu %lu %lu %u %u %u %u"
25793569664eSluzhipeng                    "%lu %lu %lu %u %lu %u",
25803569664eSluzhipeng                    &major, &minor, dev_name,
25813569664eSluzhipeng                    &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios,
25823569664eSluzhipeng                    &rd_ticks_or_wr_sec, &wr_ios, &wr_merges, &wr_sec,
25833569664eSluzhipeng                    &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks,
25843569664eSluzhipeng                    &dc_ios, &dc_merges, &dc_sec, &dc_ticks,
25853569664eSluzhipeng                    &fl_ios, &fl_ticks);
25863569664eSluzhipeng 
25873569664eSluzhipeng         if (i < 7) {
25883569664eSluzhipeng             continue;
25893569664eSluzhipeng         }
25903569664eSluzhipeng 
25913569664eSluzhipeng         diskstatinfo = g_new0(GuestDiskStatsInfo, 1);
25923569664eSluzhipeng         diskstatinfo->name = g_strdup(dev_name);
25933569664eSluzhipeng         diskstatinfo->major = major;
25943569664eSluzhipeng         diskstatinfo->minor = minor;
25953569664eSluzhipeng 
25963569664eSluzhipeng         diskstat = g_new0(GuestDiskStats, 1);
25973569664eSluzhipeng         if (i == 7) {
25983569664eSluzhipeng             diskstat->has_read_ios = true;
25993569664eSluzhipeng             diskstat->read_ios = rd_ios;
26003569664eSluzhipeng             diskstat->has_read_sectors = true;
26013569664eSluzhipeng             diskstat->read_sectors = rd_merges_or_rd_sec;
26023569664eSluzhipeng             diskstat->has_write_ios = true;
26033569664eSluzhipeng             diskstat->write_ios = rd_sec_or_wr_ios;
26043569664eSluzhipeng             diskstat->has_write_sectors = true;
26053569664eSluzhipeng             diskstat->write_sectors = rd_ticks_or_wr_sec;
26063569664eSluzhipeng         }
26073569664eSluzhipeng         if (i >= 14) {
26083569664eSluzhipeng             diskstat->has_read_ios = true;
26093569664eSluzhipeng             diskstat->read_ios = rd_ios;
26103569664eSluzhipeng             diskstat->has_read_sectors = true;
26113569664eSluzhipeng             diskstat->read_sectors = rd_sec_or_wr_ios;
26123569664eSluzhipeng             diskstat->has_read_merges = true;
26133569664eSluzhipeng             diskstat->read_merges = rd_merges_or_rd_sec;
26143569664eSluzhipeng             diskstat->has_read_ticks = true;
26153569664eSluzhipeng             diskstat->read_ticks = rd_ticks_or_wr_sec;
26163569664eSluzhipeng             diskstat->has_write_ios = true;
26173569664eSluzhipeng             diskstat->write_ios = wr_ios;
26183569664eSluzhipeng             diskstat->has_write_sectors = true;
26193569664eSluzhipeng             diskstat->write_sectors = wr_sec;
26203569664eSluzhipeng             diskstat->has_write_merges = true;
26213569664eSluzhipeng             diskstat->write_merges = wr_merges;
26223569664eSluzhipeng             diskstat->has_write_ticks = true;
26233569664eSluzhipeng             diskstat->write_ticks = wr_ticks;
26243569664eSluzhipeng             diskstat->has_ios_pgr = true;
26253569664eSluzhipeng             diskstat->ios_pgr = ios_pgr;
26263569664eSluzhipeng             diskstat->has_total_ticks = true;
26273569664eSluzhipeng             diskstat->total_ticks = tot_ticks;
26283569664eSluzhipeng             diskstat->has_weight_ticks = true;
26293569664eSluzhipeng             diskstat->weight_ticks = rq_ticks;
26303569664eSluzhipeng         }
26313569664eSluzhipeng         if (i >= 18) {
26323569664eSluzhipeng             diskstat->has_discard_ios = true;
26333569664eSluzhipeng             diskstat->discard_ios = dc_ios;
26343569664eSluzhipeng             diskstat->has_discard_merges = true;
26353569664eSluzhipeng             diskstat->discard_merges = dc_merges;
26363569664eSluzhipeng             diskstat->has_discard_sectors = true;
26373569664eSluzhipeng             diskstat->discard_sectors = dc_sec;
26383569664eSluzhipeng             diskstat->has_discard_ticks = true;
26393569664eSluzhipeng             diskstat->discard_ticks = dc_ticks;
26403569664eSluzhipeng         }
26413569664eSluzhipeng         if (i >= 20) {
26423569664eSluzhipeng             diskstat->has_flush_ios = true;
26433569664eSluzhipeng             diskstat->flush_ios = fl_ios;
26443569664eSluzhipeng             diskstat->has_flush_ticks = true;
26453569664eSluzhipeng             diskstat->flush_ticks = fl_ticks;
26463569664eSluzhipeng         }
26473569664eSluzhipeng 
26483569664eSluzhipeng         diskstatinfo->stats = g_steal_pointer(&diskstat);
26493569664eSluzhipeng         QAPI_LIST_APPEND(tail, diskstatinfo);
26503569664eSluzhipeng         diskstatinfo = NULL;
26513569664eSluzhipeng     }
26523569664eSluzhipeng     free(line);
26533569664eSluzhipeng     fclose(fp);
26543569664eSluzhipeng     return head;
26553569664eSluzhipeng #else
26563569664eSluzhipeng     g_debug("disk stats reporting available only for Linux");
26573569664eSluzhipeng     return NULL;
26583569664eSluzhipeng #endif
26593569664eSluzhipeng }
26603569664eSluzhipeng 
qmp_guest_get_diskstats(Error ** errp)26613569664eSluzhipeng GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp)
26623569664eSluzhipeng {
26633569664eSluzhipeng     return guest_get_diskstats(errp);
26643569664eSluzhipeng }
26653569664eSluzhipeng 
qmp_guest_get_cpustats(Error ** errp)26661db8a0b0Szhenwei pi GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
26671db8a0b0Szhenwei pi {
26681db8a0b0Szhenwei pi     GuestCpuStatsList *head = NULL, **tail = &head;
26691db8a0b0Szhenwei pi     const char *cpustats = "/proc/stat";
26701db8a0b0Szhenwei pi     int clk_tck = sysconf(_SC_CLK_TCK);
26711db8a0b0Szhenwei pi     FILE *fp;
26721db8a0b0Szhenwei pi     size_t n;
26731db8a0b0Szhenwei pi     char *line = NULL;
26741db8a0b0Szhenwei pi 
26751db8a0b0Szhenwei pi     fp = fopen(cpustats, "r");
26761db8a0b0Szhenwei pi     if (fp  == NULL) {
26771db8a0b0Szhenwei pi         error_setg_errno(errp, errno, "open(\"%s\")", cpustats);
26781db8a0b0Szhenwei pi         return NULL;
26791db8a0b0Szhenwei pi     }
26801db8a0b0Szhenwei pi 
26811db8a0b0Szhenwei pi     while (getline(&line, &n, fp) != -1) {
26821db8a0b0Szhenwei pi         GuestCpuStats *cpustat = NULL;
26831db8a0b0Szhenwei pi         GuestLinuxCpuStats *linuxcpustat;
26841db8a0b0Szhenwei pi         int i;
26851db8a0b0Szhenwei pi         unsigned long user, system, idle, iowait, irq, softirq, steal, guest;
26861db8a0b0Szhenwei pi         unsigned long nice, guest_nice;
26871db8a0b0Szhenwei pi         char name[64];
26881db8a0b0Szhenwei pi 
26891db8a0b0Szhenwei pi         i = sscanf(line, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
26901db8a0b0Szhenwei pi                    name, &user, &nice, &system, &idle, &iowait, &irq, &softirq,
26911db8a0b0Szhenwei pi                    &steal, &guest, &guest_nice);
26921db8a0b0Szhenwei pi 
26931db8a0b0Szhenwei pi         /* drop "cpu 1 2 3 ...", get "cpuX 1 2 3 ..." only */
26941db8a0b0Szhenwei pi         if ((i == EOF) || strncmp(name, "cpu", 3) || (name[3] == '\0')) {
26951db8a0b0Szhenwei pi             continue;
26961db8a0b0Szhenwei pi         }
26971db8a0b0Szhenwei pi 
26981db8a0b0Szhenwei pi         if (i < 5) {
26991db8a0b0Szhenwei pi             slog("Parsing cpu stat from %s failed, see \"man proc\"", cpustats);
27001db8a0b0Szhenwei pi             break;
27011db8a0b0Szhenwei pi         }
27021db8a0b0Szhenwei pi 
27031db8a0b0Szhenwei pi         cpustat = g_new0(GuestCpuStats, 1);
27041db8a0b0Szhenwei pi         cpustat->type = GUEST_CPU_STATS_TYPE_LINUX;
27051db8a0b0Szhenwei pi 
27061db8a0b0Szhenwei pi         linuxcpustat = &cpustat->u.q_linux;
27071db8a0b0Szhenwei pi         linuxcpustat->cpu = atoi(&name[3]);
27081db8a0b0Szhenwei pi         linuxcpustat->user = user * 1000 / clk_tck;
27091db8a0b0Szhenwei pi         linuxcpustat->nice = nice * 1000 / clk_tck;
27101db8a0b0Szhenwei pi         linuxcpustat->system = system * 1000 / clk_tck;
27111db8a0b0Szhenwei pi         linuxcpustat->idle = idle * 1000 / clk_tck;
27121db8a0b0Szhenwei pi 
27131db8a0b0Szhenwei pi         if (i > 5) {
27141db8a0b0Szhenwei pi             linuxcpustat->has_iowait = true;
27151db8a0b0Szhenwei pi             linuxcpustat->iowait = iowait * 1000 / clk_tck;
27161db8a0b0Szhenwei pi         }
27171db8a0b0Szhenwei pi 
27181db8a0b0Szhenwei pi         if (i > 6) {
27191db8a0b0Szhenwei pi             linuxcpustat->has_irq = true;
27201db8a0b0Szhenwei pi             linuxcpustat->irq = irq * 1000 / clk_tck;
27211db8a0b0Szhenwei pi             linuxcpustat->has_softirq = true;
27221db8a0b0Szhenwei pi             linuxcpustat->softirq = softirq * 1000 / clk_tck;
27231db8a0b0Szhenwei pi         }
27241db8a0b0Szhenwei pi 
27251db8a0b0Szhenwei pi         if (i > 8) {
27261db8a0b0Szhenwei pi             linuxcpustat->has_steal = true;
27271db8a0b0Szhenwei pi             linuxcpustat->steal = steal * 1000 / clk_tck;
27281db8a0b0Szhenwei pi         }
27291db8a0b0Szhenwei pi 
27301db8a0b0Szhenwei pi         if (i > 9) {
27311db8a0b0Szhenwei pi             linuxcpustat->has_guest = true;
27321db8a0b0Szhenwei pi             linuxcpustat->guest = guest * 1000 / clk_tck;
27331db8a0b0Szhenwei pi         }
27341db8a0b0Szhenwei pi 
27351db8a0b0Szhenwei pi         if (i > 10) {
27361db8a0b0Szhenwei pi             linuxcpustat->has_guest = true;
27371db8a0b0Szhenwei pi             linuxcpustat->guest = guest * 1000 / clk_tck;
27381db8a0b0Szhenwei pi             linuxcpustat->has_guestnice = true;
27391db8a0b0Szhenwei pi             linuxcpustat->guestnice = guest_nice * 1000 / clk_tck;
27401db8a0b0Szhenwei pi         }
27411db8a0b0Szhenwei pi 
27421db8a0b0Szhenwei pi         QAPI_LIST_APPEND(tail, cpustat);
27431db8a0b0Szhenwei pi     }
27441db8a0b0Szhenwei pi 
27451db8a0b0Szhenwei pi     free(line);
27461db8a0b0Szhenwei pi     fclose(fp);
27471db8a0b0Szhenwei pi     return head;
27481db8a0b0Szhenwei pi }
27491db8a0b0Szhenwei pi 
2750e72c3f2eSMichael Roth #else /* defined(__linux__) */
2751e72c3f2eSMichael Roth 
qmp_guest_suspend_disk(Error ** errp)275277dbc81bSMarkus Armbruster void qmp_guest_suspend_disk(Error **errp)
2753e72c3f2eSMichael Roth {
2754c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2755e72c3f2eSMichael Roth }
2756e72c3f2eSMichael Roth 
qmp_guest_suspend_ram(Error ** errp)275777dbc81bSMarkus Armbruster void qmp_guest_suspend_ram(Error **errp)
2758e72c3f2eSMichael Roth {
2759c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2760e72c3f2eSMichael Roth }
2761e72c3f2eSMichael Roth 
qmp_guest_suspend_hybrid(Error ** errp)276277dbc81bSMarkus Armbruster void qmp_guest_suspend_hybrid(Error **errp)
2763e72c3f2eSMichael Roth {
2764c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2765e72c3f2eSMichael Roth }
2766e72c3f2eSMichael Roth 
qmp_guest_get_vcpus(Error ** errp)2767d2baff62SLaszlo Ersek GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
2768d2baff62SLaszlo Ersek {
2769c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2770d2baff62SLaszlo Ersek     return NULL;
2771d2baff62SLaszlo Ersek }
2772d2baff62SLaszlo Ersek 
qmp_guest_set_vcpus(GuestLogicalProcessorList * vcpus,Error ** errp)2773cbb65fc2SLaszlo Ersek int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
2774cbb65fc2SLaszlo Ersek {
2775c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2776cbb65fc2SLaszlo Ersek     return -1;
2777cbb65fc2SLaszlo Ersek }
2778cbb65fc2SLaszlo Ersek 
qmp_guest_get_memory_blocks(Error ** errp)2779a065aaa9Szhanghailiang GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
2780a065aaa9Szhanghailiang {
2781c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2782a065aaa9Szhanghailiang     return NULL;
2783a065aaa9Szhanghailiang }
2784a065aaa9Szhanghailiang 
2785a065aaa9Szhanghailiang GuestMemoryBlockResponseList *
qmp_guest_set_memory_blocks(GuestMemoryBlockList * mem_blks,Error ** errp)2786a065aaa9Szhanghailiang qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
2787a065aaa9Szhanghailiang {
2788c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2789a065aaa9Szhanghailiang     return NULL;
2790a065aaa9Szhanghailiang }
2791a065aaa9Szhanghailiang 
qmp_guest_get_memory_block_info(Error ** errp)2792a065aaa9Szhanghailiang GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
2793a065aaa9Szhanghailiang {
2794c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2795a065aaa9Szhanghailiang     return NULL;
2796a065aaa9Szhanghailiang }
2797a065aaa9Szhanghailiang 
2798e72c3f2eSMichael Roth #endif
2799e72c3f2eSMichael Roth 
280059e35c7bSAndrew Deason #ifdef HAVE_GETIFADDRS
280159e35c7bSAndrew Deason static GuestNetworkInterface *
guest_find_interface(GuestNetworkInterfaceList * head,const char * name)280259e35c7bSAndrew Deason guest_find_interface(GuestNetworkInterfaceList *head,
280359e35c7bSAndrew Deason                      const char *name)
280459e35c7bSAndrew Deason {
280559e35c7bSAndrew Deason     for (; head; head = head->next) {
280659e35c7bSAndrew Deason         if (strcmp(head->value->name, name) == 0) {
280759e35c7bSAndrew Deason             return head->value;
280859e35c7bSAndrew Deason         }
280959e35c7bSAndrew Deason     }
281059e35c7bSAndrew Deason 
281159e35c7bSAndrew Deason     return NULL;
281259e35c7bSAndrew Deason }
281359e35c7bSAndrew Deason 
guest_get_network_stats(const char * name,GuestNetworkInterfaceStat * stats)281459e35c7bSAndrew Deason static int guest_get_network_stats(const char *name,
281559e35c7bSAndrew Deason                        GuestNetworkInterfaceStat *stats)
281659e35c7bSAndrew Deason {
281770335c46SAndrew Deason #ifdef CONFIG_LINUX
281859e35c7bSAndrew Deason     int name_len;
281959e35c7bSAndrew Deason     char const *devinfo = "/proc/net/dev";
282059e35c7bSAndrew Deason     FILE *fp;
282159e35c7bSAndrew Deason     char *line = NULL, *colon;
282259e35c7bSAndrew Deason     size_t n = 0;
282359e35c7bSAndrew Deason     fp = fopen(devinfo, "r");
282459e35c7bSAndrew Deason     if (!fp) {
2825a539dc8aSAndrew Deason         g_debug("failed to open network stats %s: %s", devinfo,
2826a539dc8aSAndrew Deason                 g_strerror(errno));
282759e35c7bSAndrew Deason         return -1;
282859e35c7bSAndrew Deason     }
282959e35c7bSAndrew Deason     name_len = strlen(name);
283059e35c7bSAndrew Deason     while (getline(&line, &n, fp) != -1) {
283159e35c7bSAndrew Deason         long long dummy;
283259e35c7bSAndrew Deason         long long rx_bytes;
283359e35c7bSAndrew Deason         long long rx_packets;
283459e35c7bSAndrew Deason         long long rx_errs;
283559e35c7bSAndrew Deason         long long rx_dropped;
283659e35c7bSAndrew Deason         long long tx_bytes;
283759e35c7bSAndrew Deason         long long tx_packets;
283859e35c7bSAndrew Deason         long long tx_errs;
283959e35c7bSAndrew Deason         long long tx_dropped;
284059e35c7bSAndrew Deason         char *trim_line;
284159e35c7bSAndrew Deason         trim_line = g_strchug(line);
284259e35c7bSAndrew Deason         if (trim_line[0] == '\0') {
284359e35c7bSAndrew Deason             continue;
284459e35c7bSAndrew Deason         }
284559e35c7bSAndrew Deason         colon = strchr(trim_line, ':');
284659e35c7bSAndrew Deason         if (!colon) {
284759e35c7bSAndrew Deason             continue;
284859e35c7bSAndrew Deason         }
284959e35c7bSAndrew Deason         if (colon - name_len  == trim_line &&
285059e35c7bSAndrew Deason            strncmp(trim_line, name, name_len) == 0) {
285159e35c7bSAndrew Deason             if (sscanf(colon + 1,
285259e35c7bSAndrew Deason                 "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld",
285359e35c7bSAndrew Deason                   &rx_bytes, &rx_packets, &rx_errs, &rx_dropped,
285459e35c7bSAndrew Deason                   &dummy, &dummy, &dummy, &dummy,
285559e35c7bSAndrew Deason                   &tx_bytes, &tx_packets, &tx_errs, &tx_dropped,
285659e35c7bSAndrew Deason                   &dummy, &dummy, &dummy, &dummy) != 16) {
285759e35c7bSAndrew Deason                 continue;
285859e35c7bSAndrew Deason             }
285959e35c7bSAndrew Deason             stats->rx_bytes = rx_bytes;
286059e35c7bSAndrew Deason             stats->rx_packets = rx_packets;
286159e35c7bSAndrew Deason             stats->rx_errs = rx_errs;
286259e35c7bSAndrew Deason             stats->rx_dropped = rx_dropped;
286359e35c7bSAndrew Deason             stats->tx_bytes = tx_bytes;
286459e35c7bSAndrew Deason             stats->tx_packets = tx_packets;
286559e35c7bSAndrew Deason             stats->tx_errs = tx_errs;
286659e35c7bSAndrew Deason             stats->tx_dropped = tx_dropped;
286759e35c7bSAndrew Deason             fclose(fp);
286859e35c7bSAndrew Deason             g_free(line);
286959e35c7bSAndrew Deason             return 0;
287059e35c7bSAndrew Deason         }
287159e35c7bSAndrew Deason     }
287259e35c7bSAndrew Deason     fclose(fp);
287359e35c7bSAndrew Deason     g_free(line);
287459e35c7bSAndrew Deason     g_debug("/proc/net/dev: Interface '%s' not found", name);
2875a539dc8aSAndrew Deason #else /* !CONFIG_LINUX */
2876a539dc8aSAndrew Deason     g_debug("Network stats reporting available only for Linux");
2877a539dc8aSAndrew Deason #endif /* !CONFIG_LINUX */
287859e35c7bSAndrew Deason     return -1;
287959e35c7bSAndrew Deason }
288059e35c7bSAndrew Deason 
288128236ad8SBrad Smith #ifndef CONFIG_BSD
2882a1241094SAlexander Ivanov /*
2883a1241094SAlexander Ivanov  * Fill "buf" with MAC address by ifaddrs. Pointer buf must point to a
2884a1241094SAlexander Ivanov  * buffer with ETHER_ADDR_LEN length at least.
2885a1241094SAlexander Ivanov  *
2886a1241094SAlexander Ivanov  * Returns false in case of an error, otherwise true. "obtained" argument
2887a1241094SAlexander Ivanov  * is true if a MAC address was obtained successful, otherwise false.
2888a1241094SAlexander Ivanov  */
guest_get_hw_addr(struct ifaddrs * ifa,unsigned char * buf,bool * obtained,Error ** errp)2889a1241094SAlexander Ivanov bool guest_get_hw_addr(struct ifaddrs *ifa, unsigned char *buf,
2890a1241094SAlexander Ivanov                        bool *obtained, Error **errp)
2891a1241094SAlexander Ivanov {
2892a1241094SAlexander Ivanov     struct ifreq ifr;
2893a1241094SAlexander Ivanov     int sock;
2894a1241094SAlexander Ivanov 
2895a1241094SAlexander Ivanov     *obtained = false;
2896a1241094SAlexander Ivanov 
2897a1241094SAlexander Ivanov     /* we haven't obtained HW address yet */
2898a1241094SAlexander Ivanov     sock = socket(PF_INET, SOCK_STREAM, 0);
2899a1241094SAlexander Ivanov     if (sock == -1) {
2900a1241094SAlexander Ivanov         error_setg_errno(errp, errno, "failed to create socket");
2901a1241094SAlexander Ivanov         return false;
2902a1241094SAlexander Ivanov     }
2903a1241094SAlexander Ivanov 
2904a1241094SAlexander Ivanov     memset(&ifr, 0, sizeof(ifr));
2905a1241094SAlexander Ivanov     pstrcpy(ifr.ifr_name, IF_NAMESIZE, ifa->ifa_name);
2906a1241094SAlexander Ivanov     if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
2907a1241094SAlexander Ivanov         /*
2908a1241094SAlexander Ivanov          * We can't get the hw addr of this interface, but that's not a
2909a1241094SAlexander Ivanov          * fatal error.
2910a1241094SAlexander Ivanov          */
2911a1241094SAlexander Ivanov         if (errno == EADDRNOTAVAIL) {
2912a1241094SAlexander Ivanov             /* The interface doesn't have a hw addr (e.g. loopback). */
2913a1241094SAlexander Ivanov             g_debug("failed to get MAC address of %s: %s",
2914a1241094SAlexander Ivanov                     ifa->ifa_name, strerror(errno));
2915a1241094SAlexander Ivanov         } else{
2916a1241094SAlexander Ivanov             g_warning("failed to get MAC address of %s: %s",
2917a1241094SAlexander Ivanov                       ifa->ifa_name, strerror(errno));
2918a1241094SAlexander Ivanov         }
2919a1241094SAlexander Ivanov     } else {
2920a1241094SAlexander Ivanov #ifdef CONFIG_SOLARIS
2921a1241094SAlexander Ivanov         memcpy(buf, &ifr.ifr_addr.sa_data, ETHER_ADDR_LEN);
2922a1241094SAlexander Ivanov #else
2923a1241094SAlexander Ivanov         memcpy(buf, &ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
2924a1241094SAlexander Ivanov #endif
2925a1241094SAlexander Ivanov         *obtained = true;
2926a1241094SAlexander Ivanov     }
2927a1241094SAlexander Ivanov     close(sock);
2928a1241094SAlexander Ivanov     return true;
2929a1241094SAlexander Ivanov }
293028236ad8SBrad Smith #endif /* CONFIG_BSD */
2931a1241094SAlexander Ivanov 
293259e35c7bSAndrew Deason /*
293359e35c7bSAndrew Deason  * Build information about guest interfaces
293459e35c7bSAndrew Deason  */
qmp_guest_network_get_interfaces(Error ** errp)293559e35c7bSAndrew Deason GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
293659e35c7bSAndrew Deason {
293759e35c7bSAndrew Deason     GuestNetworkInterfaceList *head = NULL, **tail = &head;
293859e35c7bSAndrew Deason     struct ifaddrs *ifap, *ifa;
293959e35c7bSAndrew Deason 
294059e35c7bSAndrew Deason     if (getifaddrs(&ifap) < 0) {
294159e35c7bSAndrew Deason         error_setg_errno(errp, errno, "getifaddrs failed");
294259e35c7bSAndrew Deason         goto error;
294359e35c7bSAndrew Deason     }
294459e35c7bSAndrew Deason 
294559e35c7bSAndrew Deason     for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
294659e35c7bSAndrew Deason         GuestNetworkInterface *info;
294759e35c7bSAndrew Deason         GuestIpAddressList **address_tail;
294859e35c7bSAndrew Deason         GuestIpAddress *address_item = NULL;
294959e35c7bSAndrew Deason         GuestNetworkInterfaceStat *interface_stat = NULL;
295059e35c7bSAndrew Deason         char addr4[INET_ADDRSTRLEN];
295159e35c7bSAndrew Deason         char addr6[INET6_ADDRSTRLEN];
2952a1241094SAlexander Ivanov         unsigned char mac_addr[ETHER_ADDR_LEN];
2953a1241094SAlexander Ivanov         bool obtained;
295459e35c7bSAndrew Deason         void *p;
295559e35c7bSAndrew Deason 
295659e35c7bSAndrew Deason         g_debug("Processing %s interface", ifa->ifa_name);
295759e35c7bSAndrew Deason 
295859e35c7bSAndrew Deason         info = guest_find_interface(head, ifa->ifa_name);
295959e35c7bSAndrew Deason 
296059e35c7bSAndrew Deason         if (!info) {
296159e35c7bSAndrew Deason             info = g_malloc0(sizeof(*info));
296259e35c7bSAndrew Deason             info->name = g_strdup(ifa->ifa_name);
296359e35c7bSAndrew Deason 
296459e35c7bSAndrew Deason             QAPI_LIST_APPEND(tail, info);
296559e35c7bSAndrew Deason         }
296659e35c7bSAndrew Deason 
296791eab32aSMarkus Armbruster         if (!info->hardware_address) {
2968a1241094SAlexander Ivanov             if (!guest_get_hw_addr(ifa, mac_addr, &obtained, errp)) {
296959e35c7bSAndrew Deason                 goto error;
297059e35c7bSAndrew Deason             }
2971a1241094SAlexander Ivanov             if (obtained) {
297259e35c7bSAndrew Deason                 info->hardware_address =
297359e35c7bSAndrew Deason                     g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x",
297459e35c7bSAndrew Deason                                     (int) mac_addr[0], (int) mac_addr[1],
297559e35c7bSAndrew Deason                                     (int) mac_addr[2], (int) mac_addr[3],
297659e35c7bSAndrew Deason                                     (int) mac_addr[4], (int) mac_addr[5]);
297759e35c7bSAndrew Deason             }
2978aec0730eSAndrew Deason         }
297959e35c7bSAndrew Deason 
298059e35c7bSAndrew Deason         if (ifa->ifa_addr &&
298159e35c7bSAndrew Deason             ifa->ifa_addr->sa_family == AF_INET) {
298259e35c7bSAndrew Deason             /* interface with IPv4 address */
298359e35c7bSAndrew Deason             p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
298459e35c7bSAndrew Deason             if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
298559e35c7bSAndrew Deason                 error_setg_errno(errp, errno, "inet_ntop failed");
298659e35c7bSAndrew Deason                 goto error;
298759e35c7bSAndrew Deason             }
298859e35c7bSAndrew Deason 
298959e35c7bSAndrew Deason             address_item = g_malloc0(sizeof(*address_item));
299059e35c7bSAndrew Deason             address_item->ip_address = g_strdup(addr4);
299159e35c7bSAndrew Deason             address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;
299259e35c7bSAndrew Deason 
299359e35c7bSAndrew Deason             if (ifa->ifa_netmask) {
299459e35c7bSAndrew Deason                 /* Count the number of set bits in netmask.
299559e35c7bSAndrew Deason                  * This is safe as '1' and '0' cannot be shuffled in netmask. */
299659e35c7bSAndrew Deason                 p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;
299759e35c7bSAndrew Deason                 address_item->prefix = ctpop32(((uint32_t *) p)[0]);
299859e35c7bSAndrew Deason             }
299959e35c7bSAndrew Deason         } else if (ifa->ifa_addr &&
300059e35c7bSAndrew Deason                    ifa->ifa_addr->sa_family == AF_INET6) {
300159e35c7bSAndrew Deason             /* interface with IPv6 address */
300259e35c7bSAndrew Deason             p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
300359e35c7bSAndrew Deason             if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
300459e35c7bSAndrew Deason                 error_setg_errno(errp, errno, "inet_ntop failed");
300559e35c7bSAndrew Deason                 goto error;
300659e35c7bSAndrew Deason             }
300759e35c7bSAndrew Deason 
300859e35c7bSAndrew Deason             address_item = g_malloc0(sizeof(*address_item));
300959e35c7bSAndrew Deason             address_item->ip_address = g_strdup(addr6);
301059e35c7bSAndrew Deason             address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;
301159e35c7bSAndrew Deason 
301259e35c7bSAndrew Deason             if (ifa->ifa_netmask) {
301359e35c7bSAndrew Deason                 /* Count the number of set bits in netmask.
301459e35c7bSAndrew Deason                  * This is safe as '1' and '0' cannot be shuffled in netmask. */
301559e35c7bSAndrew Deason                 p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
301659e35c7bSAndrew Deason                 address_item->prefix =
301759e35c7bSAndrew Deason                     ctpop32(((uint32_t *) p)[0]) +
301859e35c7bSAndrew Deason                     ctpop32(((uint32_t *) p)[1]) +
301959e35c7bSAndrew Deason                     ctpop32(((uint32_t *) p)[2]) +
302059e35c7bSAndrew Deason                     ctpop32(((uint32_t *) p)[3]);
302159e35c7bSAndrew Deason             }
302259e35c7bSAndrew Deason         }
302359e35c7bSAndrew Deason 
302459e35c7bSAndrew Deason         if (!address_item) {
302559e35c7bSAndrew Deason             continue;
302659e35c7bSAndrew Deason         }
302759e35c7bSAndrew Deason 
302859e35c7bSAndrew Deason         address_tail = &info->ip_addresses;
302959e35c7bSAndrew Deason         while (*address_tail) {
303059e35c7bSAndrew Deason             address_tail = &(*address_tail)->next;
303159e35c7bSAndrew Deason         }
303259e35c7bSAndrew Deason         QAPI_LIST_APPEND(address_tail, address_item);
303359e35c7bSAndrew Deason 
303459e35c7bSAndrew Deason         info->has_ip_addresses = true;
303559e35c7bSAndrew Deason 
303691eab32aSMarkus Armbruster         if (!info->statistics) {
303759e35c7bSAndrew Deason             interface_stat = g_malloc0(sizeof(*interface_stat));
303859e35c7bSAndrew Deason             if (guest_get_network_stats(info->name, interface_stat) == -1) {
303959e35c7bSAndrew Deason                 g_free(interface_stat);
304059e35c7bSAndrew Deason             } else {
304159e35c7bSAndrew Deason                 info->statistics = interface_stat;
304259e35c7bSAndrew Deason             }
304359e35c7bSAndrew Deason         }
304459e35c7bSAndrew Deason     }
304559e35c7bSAndrew Deason 
304659e35c7bSAndrew Deason     freeifaddrs(ifap);
304759e35c7bSAndrew Deason     return head;
304859e35c7bSAndrew Deason 
304959e35c7bSAndrew Deason error:
305059e35c7bSAndrew Deason     freeifaddrs(ifap);
305159e35c7bSAndrew Deason     qapi_free_GuestNetworkInterfaceList(head);
305259e35c7bSAndrew Deason     return NULL;
305359e35c7bSAndrew Deason }
305459e35c7bSAndrew Deason 
305559e35c7bSAndrew Deason #else
305659e35c7bSAndrew Deason 
qmp_guest_network_get_interfaces(Error ** errp)305759e35c7bSAndrew Deason GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
305859e35c7bSAndrew Deason {
305959e35c7bSAndrew Deason     error_setg(errp, QERR_UNSUPPORTED);
306059e35c7bSAndrew Deason     return NULL;
306159e35c7bSAndrew Deason }
306259e35c7bSAndrew Deason 
306359e35c7bSAndrew Deason #endif /* HAVE_GETIFADDRS */
306459e35c7bSAndrew Deason 
3065d35d4cb5SMichael Roth #if !defined(CONFIG_FSFREEZE)
3066d35d4cb5SMichael Roth 
qmp_guest_get_fsinfo(Error ** errp)306746d4c572STomoki Sekiyama GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
306846d4c572STomoki Sekiyama {
3069c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
307046d4c572STomoki Sekiyama     return NULL;
307146d4c572STomoki Sekiyama }
307246d4c572STomoki Sekiyama 
qmp_guest_fsfreeze_status(Error ** errp)307377dbc81bSMarkus Armbruster GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
3074d35d4cb5SMichael Roth {
3075c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
3076d35d4cb5SMichael Roth 
3077d35d4cb5SMichael Roth     return 0;
3078d35d4cb5SMichael Roth }
3079d35d4cb5SMichael Roth 
qmp_guest_fsfreeze_freeze(Error ** errp)308077dbc81bSMarkus Armbruster int64_t qmp_guest_fsfreeze_freeze(Error **errp)
3081d35d4cb5SMichael Roth {
3082c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
3083d35d4cb5SMichael Roth 
3084d35d4cb5SMichael Roth     return 0;
3085d35d4cb5SMichael Roth }
3086d35d4cb5SMichael Roth 
qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,strList * mountpoints,Error ** errp)3087e99bce20STomoki Sekiyama int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
3088e99bce20STomoki Sekiyama                                        strList *mountpoints,
3089e99bce20STomoki Sekiyama                                        Error **errp)
3090e99bce20STomoki Sekiyama {
3091c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
3092e99bce20STomoki Sekiyama 
3093e99bce20STomoki Sekiyama     return 0;
3094e99bce20STomoki Sekiyama }
3095e99bce20STomoki Sekiyama 
qmp_guest_fsfreeze_thaw(Error ** errp)309677dbc81bSMarkus Armbruster int64_t qmp_guest_fsfreeze_thaw(Error **errp)
3097d35d4cb5SMichael Roth {
3098c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
3099d35d4cb5SMichael Roth 
3100d35d4cb5SMichael Roth     return 0;
3101d35d4cb5SMichael Roth }
3102fed39564STomáš Golembiovský 
qmp_guest_get_disks(Error ** errp)3103fed39564STomáš Golembiovský GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
3104fed39564STomáš Golembiovský {
3105fed39564STomáš Golembiovský     error_setg(errp, QERR_UNSUPPORTED);
3106fed39564STomáš Golembiovský     return NULL;
3107fed39564STomáš Golembiovský }
3108fed39564STomáš Golembiovský 
qmp_guest_get_diskstats(Error ** errp)31093569664eSluzhipeng GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp)
31103569664eSluzhipeng {
31113569664eSluzhipeng     error_setg(errp, QERR_UNSUPPORTED);
31123569664eSluzhipeng     return NULL;
31133569664eSluzhipeng }
31143569664eSluzhipeng 
qmp_guest_get_cpustats(Error ** errp)31151db8a0b0Szhenwei pi GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
31161db8a0b0Szhenwei pi {
31171db8a0b0Szhenwei pi     error_setg(errp, QERR_UNSUPPORTED);
31181db8a0b0Szhenwei pi     return NULL;
31191db8a0b0Szhenwei pi }
31203569664eSluzhipeng 
3121eab5fd59SPaolo Bonzini #endif /* CONFIG_FSFREEZE */
3122d35d4cb5SMichael Roth 
3123eab5fd59SPaolo Bonzini #if !defined(CONFIG_FSTRIM)
3124e82855d9SJustin Ossevoort GuestFilesystemTrimResponse *
qmp_guest_fstrim(bool has_minimum,int64_t minimum,Error ** errp)3125e82855d9SJustin Ossevoort qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
3126eab5fd59SPaolo Bonzini {
3127c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
3128e82855d9SJustin Ossevoort     return NULL;
3129eab5fd59SPaolo Bonzini }
3130d35d4cb5SMichael Roth #endif
3131d35d4cb5SMichael Roth 
31320e4ef702SThomas Huth /* add unsupported commands to the list of blocked RPCs */
ga_command_init_blockedrpcs(GList * blockedrpcs)31330e4ef702SThomas Huth GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
31341281c08aSTomoki Sekiyama {
31351281c08aSTomoki Sekiyama #if !defined(__linux__)
31361281c08aSTomoki Sekiyama     {
31371281c08aSTomoki Sekiyama         const char *list[] = {
31381281c08aSTomoki Sekiyama             "guest-suspend-disk", "guest-suspend-ram",
313959e35c7bSAndrew Deason             "guest-suspend-hybrid", "guest-get-vcpus", "guest-set-vcpus",
31400dd38a03Szhanghailiang             "guest-get-memory-blocks", "guest-set-memory-blocks",
314128d8dd35SBasil Salman             "guest-get-memory-block-size", "guest-get-memory-block-info",
314228d8dd35SBasil Salman             NULL};
31431281c08aSTomoki Sekiyama         char **p = (char **)list;
31441281c08aSTomoki Sekiyama 
31451281c08aSTomoki Sekiyama         while (*p) {
31460e4ef702SThomas Huth             blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
31471281c08aSTomoki Sekiyama         }
31481281c08aSTomoki Sekiyama     }
31491281c08aSTomoki Sekiyama #endif
31501281c08aSTomoki Sekiyama 
315159e35c7bSAndrew Deason #if !defined(HAVE_GETIFADDRS)
31520e4ef702SThomas Huth     blockedrpcs = g_list_append(blockedrpcs,
315359e35c7bSAndrew Deason                               g_strdup("guest-network-get-interfaces"));
315459e35c7bSAndrew Deason #endif
315559e35c7bSAndrew Deason 
31561281c08aSTomoki Sekiyama #if !defined(CONFIG_FSFREEZE)
31571281c08aSTomoki Sekiyama     {
31581281c08aSTomoki Sekiyama         const char *list[] = {
31591281c08aSTomoki Sekiyama             "guest-get-fsinfo", "guest-fsfreeze-status",
31601281c08aSTomoki Sekiyama             "guest-fsfreeze-freeze", "guest-fsfreeze-freeze-list",
3161fed39564STomáš Golembiovský             "guest-fsfreeze-thaw", "guest-get-fsinfo",
3162fed39564STomáš Golembiovský             "guest-get-disks", NULL};
31631281c08aSTomoki Sekiyama         char **p = (char **)list;
31641281c08aSTomoki Sekiyama 
31651281c08aSTomoki Sekiyama         while (*p) {
31660e4ef702SThomas Huth             blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
31671281c08aSTomoki Sekiyama         }
31681281c08aSTomoki Sekiyama     }
31691281c08aSTomoki Sekiyama #endif
31701281c08aSTomoki Sekiyama 
31711281c08aSTomoki Sekiyama #if !defined(CONFIG_FSTRIM)
31720e4ef702SThomas Huth     blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-fstrim"));
31731281c08aSTomoki Sekiyama #endif
31741281c08aSTomoki Sekiyama 
31750e4ef702SThomas Huth     blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-get-devices"));
31762e4211ceSTomáš Golembiovský 
31770e4ef702SThomas Huth     return blockedrpcs;
31781281c08aSTomoki Sekiyama }
31791281c08aSTomoki Sekiyama 
3180c216e5adSMichael Roth /* register init/cleanup routines for stateful command groups */
ga_command_state_init(GAState * s,GACommandState * cs)3181c216e5adSMichael Roth void ga_command_state_init(GAState *s, GACommandState *cs)
3182c216e5adSMichael Roth {
3183c216e5adSMichael Roth #if defined(CONFIG_FSFREEZE)
3184f22d85e9SMichael Roth     ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
3185c216e5adSMichael Roth #endif
3186c216e5adSMichael Roth }
3187161a56a9SVinzenz Feenstra 
3188e674605fSTomáš Golembiovský #ifdef HAVE_UTMPX
3189e674605fSTomáš Golembiovský 
3190161a56a9SVinzenz Feenstra #define QGA_MICRO_SECOND_TO_SECOND 1000000
3191161a56a9SVinzenz Feenstra 
ga_get_login_time(struct utmpx * user_info)3192161a56a9SVinzenz Feenstra static double ga_get_login_time(struct utmpx *user_info)
3193161a56a9SVinzenz Feenstra {
3194161a56a9SVinzenz Feenstra     double seconds = (double)user_info->ut_tv.tv_sec;
3195161a56a9SVinzenz Feenstra     double useconds = (double)user_info->ut_tv.tv_usec;
3196161a56a9SVinzenz Feenstra     useconds /= QGA_MICRO_SECOND_TO_SECOND;
3197161a56a9SVinzenz Feenstra     return seconds + useconds;
3198161a56a9SVinzenz Feenstra }
3199161a56a9SVinzenz Feenstra 
qmp_guest_get_users(Error ** errp)3200b90abbacSVladimir Sementsov-Ogievskiy GuestUserList *qmp_guest_get_users(Error **errp)
3201161a56a9SVinzenz Feenstra {
3202161a56a9SVinzenz Feenstra     GHashTable *cache = NULL;
320395b3a8c8SEric Blake     GuestUserList *head = NULL, **tail = &head;
3204161a56a9SVinzenz Feenstra     struct utmpx *user_info = NULL;
3205161a56a9SVinzenz Feenstra     gpointer value = NULL;
3206161a56a9SVinzenz Feenstra     GuestUser *user = NULL;
3207161a56a9SVinzenz Feenstra     double login_time = 0;
3208161a56a9SVinzenz Feenstra 
3209161a56a9SVinzenz Feenstra     cache = g_hash_table_new(g_str_hash, g_str_equal);
3210161a56a9SVinzenz Feenstra     setutxent();
3211161a56a9SVinzenz Feenstra 
3212161a56a9SVinzenz Feenstra     for (;;) {
3213161a56a9SVinzenz Feenstra         user_info = getutxent();
3214161a56a9SVinzenz Feenstra         if (user_info == NULL) {
3215161a56a9SVinzenz Feenstra             break;
3216161a56a9SVinzenz Feenstra         } else if (user_info->ut_type != USER_PROCESS) {
3217161a56a9SVinzenz Feenstra             continue;
3218161a56a9SVinzenz Feenstra         } else if (g_hash_table_contains(cache, user_info->ut_user)) {
3219161a56a9SVinzenz Feenstra             value = g_hash_table_lookup(cache, user_info->ut_user);
3220161a56a9SVinzenz Feenstra             user = (GuestUser *)value;
3221161a56a9SVinzenz Feenstra             login_time = ga_get_login_time(user_info);
3222161a56a9SVinzenz Feenstra             /* We're ensuring the earliest login time to be sent */
3223161a56a9SVinzenz Feenstra             if (login_time < user->login_time) {
3224161a56a9SVinzenz Feenstra                 user->login_time = login_time;
3225161a56a9SVinzenz Feenstra             }
3226161a56a9SVinzenz Feenstra             continue;
3227161a56a9SVinzenz Feenstra         }
3228161a56a9SVinzenz Feenstra 
322995b3a8c8SEric Blake         user = g_new0(GuestUser, 1);
323095b3a8c8SEric Blake         user->user = g_strdup(user_info->ut_user);
323195b3a8c8SEric Blake         user->login_time = ga_get_login_time(user_info);
3232161a56a9SVinzenz Feenstra 
323395b3a8c8SEric Blake         g_hash_table_insert(cache, user->user, user);
3234161a56a9SVinzenz Feenstra 
323595b3a8c8SEric Blake         QAPI_LIST_APPEND(tail, user);
3236161a56a9SVinzenz Feenstra     }
3237161a56a9SVinzenz Feenstra     endutxent();
3238161a56a9SVinzenz Feenstra     g_hash_table_destroy(cache);
3239161a56a9SVinzenz Feenstra     return head;
3240161a56a9SVinzenz Feenstra }
3241e674605fSTomáš Golembiovský 
3242e674605fSTomáš Golembiovský #else
3243e674605fSTomáš Golembiovský 
qmp_guest_get_users(Error ** errp)3244e674605fSTomáš Golembiovský GuestUserList *qmp_guest_get_users(Error **errp)
3245e674605fSTomáš Golembiovský {
3246e674605fSTomáš Golembiovský     error_setg(errp, QERR_UNSUPPORTED);
3247e674605fSTomáš Golembiovský     return NULL;
3248e674605fSTomáš Golembiovský }
3249e674605fSTomáš Golembiovský 
3250e674605fSTomáš Golembiovský #endif
32519848f797STomáš Golembiovský 
3252*01dc0651SMichael Tokarev /* Replace escaped special characters with their real values. The replacement
32539848f797STomáš Golembiovský  * is done in place -- returned value is in the original string.
32549848f797STomáš Golembiovský  */
ga_osrelease_replace_special(gchar * value)32559848f797STomáš Golembiovský static void ga_osrelease_replace_special(gchar *value)
32569848f797STomáš Golembiovský {
32579848f797STomáš Golembiovský     gchar *p, *p2, quote;
32589848f797STomáš Golembiovský 
32599848f797STomáš Golembiovský     /* Trim the string at first space or semicolon if it is not enclosed in
32609848f797STomáš Golembiovský      * single or double quotes. */
32619848f797STomáš Golembiovský     if ((value[0] != '"') || (value[0] == '\'')) {
32629848f797STomáš Golembiovský         p = strchr(value, ' ');
32639848f797STomáš Golembiovský         if (p != NULL) {
32649848f797STomáš Golembiovský             *p = 0;
32659848f797STomáš Golembiovský         }
32669848f797STomáš Golembiovský         p = strchr(value, ';');
32679848f797STomáš Golembiovský         if (p != NULL) {
32689848f797STomáš Golembiovský             *p = 0;
32699848f797STomáš Golembiovský         }
32709848f797STomáš Golembiovský         return;
32719848f797STomáš Golembiovský     }
32729848f797STomáš Golembiovský 
32739848f797STomáš Golembiovský     quote = value[0];
32749848f797STomáš Golembiovský     p2 = value;
32759848f797STomáš Golembiovský     p = value + 1;
32769848f797STomáš Golembiovský     while (*p != 0) {
32779848f797STomáš Golembiovský         if (*p == '\\') {
32789848f797STomáš Golembiovský             p++;
32799848f797STomáš Golembiovský             switch (*p) {
32809848f797STomáš Golembiovský             case '$':
32819848f797STomáš Golembiovský             case '\'':
32829848f797STomáš Golembiovský             case '"':
32839848f797STomáš Golembiovský             case '\\':
32849848f797STomáš Golembiovský             case '`':
32859848f797STomáš Golembiovský                 break;
32869848f797STomáš Golembiovský             default:
32879848f797STomáš Golembiovský                 /* Keep literal backslash followed by whatever is there */
32889848f797STomáš Golembiovský                 p--;
32899848f797STomáš Golembiovský                 break;
32909848f797STomáš Golembiovský             }
32919848f797STomáš Golembiovský         } else if (*p == quote) {
32929848f797STomáš Golembiovský             *p2 = 0;
32939848f797STomáš Golembiovský             break;
32949848f797STomáš Golembiovský         }
32959848f797STomáš Golembiovský         *(p2++) = *(p++);
32969848f797STomáš Golembiovský     }
32979848f797STomáš Golembiovský }
32989848f797STomáš Golembiovský 
ga_parse_osrelease(const char * fname)32999848f797STomáš Golembiovský static GKeyFile *ga_parse_osrelease(const char *fname)
33009848f797STomáš Golembiovský {
33019848f797STomáš Golembiovský     gchar *content = NULL;
33029848f797STomáš Golembiovský     gchar *content2 = NULL;
33039848f797STomáš Golembiovský     GError *err = NULL;
33049848f797STomáš Golembiovský     GKeyFile *keys = g_key_file_new();
33059848f797STomáš Golembiovský     const char *group = "[os-release]\n";
33069848f797STomáš Golembiovský 
33079848f797STomáš Golembiovský     if (!g_file_get_contents(fname, &content, NULL, &err)) {
33089848f797STomáš Golembiovský         slog("failed to read '%s', error: %s", fname, err->message);
33099848f797STomáš Golembiovský         goto fail;
33109848f797STomáš Golembiovský     }
33119848f797STomáš Golembiovský 
33129848f797STomáš Golembiovský     if (!g_utf8_validate(content, -1, NULL)) {
33139848f797STomáš Golembiovský         slog("file is not utf-8 encoded: %s", fname);
33149848f797STomáš Golembiovský         goto fail;
33159848f797STomáš Golembiovský     }
33169848f797STomáš Golembiovský     content2 = g_strdup_printf("%s%s", group, content);
33179848f797STomáš Golembiovský 
33189848f797STomáš Golembiovský     if (!g_key_file_load_from_data(keys, content2, -1, G_KEY_FILE_NONE,
33199848f797STomáš Golembiovský                                    &err)) {
33209848f797STomáš Golembiovský         slog("failed to parse file '%s', error: %s", fname, err->message);
33219848f797STomáš Golembiovský         goto fail;
33229848f797STomáš Golembiovský     }
33239848f797STomáš Golembiovský 
33249848f797STomáš Golembiovský     g_free(content);
33259848f797STomáš Golembiovský     g_free(content2);
33269848f797STomáš Golembiovský     return keys;
33279848f797STomáš Golembiovský 
33289848f797STomáš Golembiovský fail:
33299848f797STomáš Golembiovský     g_error_free(err);
33309848f797STomáš Golembiovský     g_free(content);
33319848f797STomáš Golembiovský     g_free(content2);
33329848f797STomáš Golembiovský     g_key_file_free(keys);
33339848f797STomáš Golembiovský     return NULL;
33349848f797STomáš Golembiovský }
33359848f797STomáš Golembiovský 
qmp_guest_get_osinfo(Error ** errp)33369848f797STomáš Golembiovský GuestOSInfo *qmp_guest_get_osinfo(Error **errp)
33379848f797STomáš Golembiovský {
33389848f797STomáš Golembiovský     GuestOSInfo *info = NULL;
33399848f797STomáš Golembiovský     struct utsname kinfo;
3340339ca68bSTomáš Golembiovský     GKeyFile *osrelease = NULL;
3341339ca68bSTomáš Golembiovský     const char *qga_os_release = g_getenv("QGA_OS_RELEASE");
33429848f797STomáš Golembiovský 
33439848f797STomáš Golembiovský     info = g_new0(GuestOSInfo, 1);
33449848f797STomáš Golembiovský 
33459848f797STomáš Golembiovský     if (uname(&kinfo) != 0) {
33469848f797STomáš Golembiovský         error_setg_errno(errp, errno, "uname failed");
33479848f797STomáš Golembiovský     } else {
33489848f797STomáš Golembiovský         info->kernel_version = g_strdup(kinfo.version);
33499848f797STomáš Golembiovský         info->kernel_release = g_strdup(kinfo.release);
33509848f797STomáš Golembiovský         info->machine = g_strdup(kinfo.machine);
33519848f797STomáš Golembiovský     }
33529848f797STomáš Golembiovský 
3353339ca68bSTomáš Golembiovský     if (qga_os_release != NULL) {
3354339ca68bSTomáš Golembiovský         osrelease = ga_parse_osrelease(qga_os_release);
3355339ca68bSTomáš Golembiovský     } else {
33569848f797STomáš Golembiovský         osrelease = ga_parse_osrelease("/etc/os-release");
33579848f797STomáš Golembiovský         if (osrelease == NULL) {
33589848f797STomáš Golembiovský             osrelease = ga_parse_osrelease("/usr/lib/os-release");
33599848f797STomáš Golembiovský         }
3360339ca68bSTomáš Golembiovský     }
33619848f797STomáš Golembiovský 
33629848f797STomáš Golembiovský     if (osrelease != NULL) {
33639848f797STomáš Golembiovský         char *value;
33649848f797STomáš Golembiovský 
33659848f797STomáš Golembiovský #define GET_FIELD(field, osfield) do { \
33669848f797STomáš Golembiovský     value = g_key_file_get_value(osrelease, "os-release", osfield, NULL); \
33679848f797STomáš Golembiovský     if (value != NULL) { \
33689848f797STomáš Golembiovský         ga_osrelease_replace_special(value); \
33699848f797STomáš Golembiovský         info->field = value; \
33709848f797STomáš Golembiovský     } \
33719848f797STomáš Golembiovský } while (0)
33729848f797STomáš Golembiovský         GET_FIELD(id, "ID");
33739848f797STomáš Golembiovský         GET_FIELD(name, "NAME");
33749848f797STomáš Golembiovský         GET_FIELD(pretty_name, "PRETTY_NAME");
33759848f797STomáš Golembiovský         GET_FIELD(version, "VERSION");
33769848f797STomáš Golembiovský         GET_FIELD(version_id, "VERSION_ID");
33779848f797STomáš Golembiovský         GET_FIELD(variant, "VARIANT");
33789848f797STomáš Golembiovský         GET_FIELD(variant_id, "VARIANT_ID");
33799848f797STomáš Golembiovský #undef GET_FIELD
33809848f797STomáš Golembiovský 
33819848f797STomáš Golembiovský         g_key_file_free(osrelease);
33829848f797STomáš Golembiovský     }
33839848f797STomáš Golembiovský 
33849848f797STomáš Golembiovský     return info;
33859848f797STomáš Golembiovský }
33862e4211ceSTomáš Golembiovský 
qmp_guest_get_devices(Error ** errp)33872e4211ceSTomáš Golembiovský GuestDeviceInfoList *qmp_guest_get_devices(Error **errp)
33882e4211ceSTomáš Golembiovský {
33892e4211ceSTomáš Golembiovský     error_setg(errp, QERR_UNSUPPORTED);
33902e4211ceSTomáš Golembiovský 
33912e4211ceSTomáš Golembiovský     return NULL;
33922e4211ceSTomáš Golembiovský }
3393548fb0daSMarc-André Lureau 
3394548fb0daSMarc-André Lureau #ifndef HOST_NAME_MAX
3395548fb0daSMarc-André Lureau # ifdef _POSIX_HOST_NAME_MAX
3396548fb0daSMarc-André Lureau #  define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
3397548fb0daSMarc-André Lureau # else
3398548fb0daSMarc-André Lureau #  define HOST_NAME_MAX 255
3399548fb0daSMarc-André Lureau # endif
3400548fb0daSMarc-André Lureau #endif
3401548fb0daSMarc-André Lureau 
qga_get_host_name(Error ** errp)3402548fb0daSMarc-André Lureau char *qga_get_host_name(Error **errp)
3403548fb0daSMarc-André Lureau {
3404548fb0daSMarc-André Lureau     long len = -1;
3405548fb0daSMarc-André Lureau     g_autofree char *hostname = NULL;
3406548fb0daSMarc-André Lureau 
3407548fb0daSMarc-André Lureau #ifdef _SC_HOST_NAME_MAX
3408548fb0daSMarc-André Lureau     len = sysconf(_SC_HOST_NAME_MAX);
3409548fb0daSMarc-André Lureau #endif /* _SC_HOST_NAME_MAX */
3410548fb0daSMarc-André Lureau 
3411548fb0daSMarc-André Lureau     if (len < 0) {
3412548fb0daSMarc-André Lureau         len = HOST_NAME_MAX;
3413548fb0daSMarc-André Lureau     }
3414548fb0daSMarc-André Lureau 
3415548fb0daSMarc-André Lureau     /* Unfortunately, gethostname() below does not guarantee a
3416548fb0daSMarc-André Lureau      * NULL terminated string. Therefore, allocate one byte more
3417548fb0daSMarc-André Lureau      * to be sure. */
3418548fb0daSMarc-André Lureau     hostname = g_new0(char, len + 1);
3419548fb0daSMarc-André Lureau 
3420548fb0daSMarc-André Lureau     if (gethostname(hostname, len) < 0) {
3421548fb0daSMarc-André Lureau         error_setg_errno(errp, errno,
3422548fb0daSMarc-André Lureau                          "cannot get hostname");
3423548fb0daSMarc-André Lureau         return NULL;
3424548fb0daSMarc-André Lureau     }
3425548fb0daSMarc-André Lureau 
3426548fb0daSMarc-André Lureau     return g_steal_pointer(&hostname);
3427548fb0daSMarc-André Lureau }
3428