xref: /openbmc/qemu/qga/commands-posix.c (revision 4fd0642e)
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 
44c6cd588bSAlexander Ivanov #ifdef __FreeBSD__
45c6cd588bSAlexander Ivanov /*
46c6cd588bSAlexander Ivanov  * The code under HAVE_GETIFADDRS condition can't be compiled in FreeBSD.
47c6cd588bSAlexander Ivanov  * Fix it in one of the following patches.
48c6cd588bSAlexander Ivanov  */
49c6cd588bSAlexander Ivanov #undef HAVE_GETIFADDRS
50c6cd588bSAlexander Ivanov #endif
51c6cd588bSAlexander Ivanov 
5259e35c7bSAndrew Deason #ifdef HAVE_GETIFADDRS
5359e35c7bSAndrew Deason #include <arpa/inet.h>
5459e35c7bSAndrew Deason #include <sys/socket.h>
5559e35c7bSAndrew Deason #include <net/if.h>
5659e35c7bSAndrew Deason #include <sys/types.h>
5759e35c7bSAndrew Deason #include <ifaddrs.h>
5859e35c7bSAndrew Deason #ifdef CONFIG_SOLARIS
5959e35c7bSAndrew Deason #include <sys/sockio.h>
6059e35c7bSAndrew Deason #endif
6159e35c7bSAndrew Deason #endif
6259e35c7bSAndrew Deason 
6377dbc81bSMarkus Armbruster static void ga_wait_child(pid_t pid, int *status, Error **errp)
64d220a6dfSLuiz Capitulino {
65d220a6dfSLuiz Capitulino     pid_t rpid;
66d220a6dfSLuiz Capitulino 
67d220a6dfSLuiz Capitulino     *status = 0;
68d220a6dfSLuiz Capitulino 
69d220a6dfSLuiz Capitulino     do {
70d220a6dfSLuiz Capitulino         rpid = waitpid(pid, status, 0);
71d220a6dfSLuiz Capitulino     } while (rpid == -1 && errno == EINTR);
72d220a6dfSLuiz Capitulino 
73d220a6dfSLuiz Capitulino     if (rpid == -1) {
7477dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to wait for child (pid: %d)",
7577dbc81bSMarkus Armbruster                          pid);
76d220a6dfSLuiz Capitulino         return;
77d220a6dfSLuiz Capitulino     }
78d220a6dfSLuiz Capitulino 
79d220a6dfSLuiz Capitulino     g_assert(rpid == pid);
80d220a6dfSLuiz Capitulino }
81d220a6dfSLuiz Capitulino 
8277dbc81bSMarkus Armbruster void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp)
83c216e5adSMichael Roth {
84c216e5adSMichael Roth     const char *shutdown_flag;
85d220a6dfSLuiz Capitulino     Error *local_err = NULL;
86d220a6dfSLuiz Capitulino     pid_t pid;
873674838cSLuiz Capitulino     int status;
88c216e5adSMichael Roth 
89c8ec041dSAndrew Deason #ifdef CONFIG_SOLARIS
90c8ec041dSAndrew Deason     const char *powerdown_flag = "-i5";
91c8ec041dSAndrew Deason     const char *halt_flag = "-i0";
92c8ec041dSAndrew Deason     const char *reboot_flag = "-i6";
93e40762fcSAlexander Ivanov #elif defined(CONFIG_BSD)
94e40762fcSAlexander Ivanov     const char *powerdown_flag = "-p";
95e40762fcSAlexander Ivanov     const char *halt_flag = "-h";
96e40762fcSAlexander Ivanov     const char *reboot_flag = "-r";
97c8ec041dSAndrew Deason #else
98c8ec041dSAndrew Deason     const char *powerdown_flag = "-P";
99c8ec041dSAndrew Deason     const char *halt_flag = "-H";
100c8ec041dSAndrew Deason     const char *reboot_flag = "-r";
101c8ec041dSAndrew Deason #endif
102c8ec041dSAndrew Deason 
103c216e5adSMichael Roth     slog("guest-shutdown called, mode: %s", mode);
104c216e5adSMichael Roth     if (!has_mode || strcmp(mode, "powerdown") == 0) {
105c8ec041dSAndrew Deason         shutdown_flag = powerdown_flag;
106c216e5adSMichael Roth     } else if (strcmp(mode, "halt") == 0) {
107c8ec041dSAndrew Deason         shutdown_flag = halt_flag;
108c216e5adSMichael Roth     } else if (strcmp(mode, "reboot") == 0) {
109c8ec041dSAndrew Deason         shutdown_flag = reboot_flag;
110c216e5adSMichael Roth     } else {
11177dbc81bSMarkus Armbruster         error_setg(errp,
112d220a6dfSLuiz Capitulino                    "mode is invalid (valid values are: halt|powerdown|reboot");
113c216e5adSMichael Roth         return;
114c216e5adSMichael Roth     }
115c216e5adSMichael Roth 
116d5dd3498SLuiz Capitulino     pid = fork();
117d5dd3498SLuiz Capitulino     if (pid == 0) {
118c216e5adSMichael Roth         /* child, start the shutdown */
119c216e5adSMichael Roth         setsid();
1203674838cSLuiz Capitulino         reopen_fd_to_null(0);
1213674838cSLuiz Capitulino         reopen_fd_to_null(1);
1223674838cSLuiz Capitulino         reopen_fd_to_null(2);
123c216e5adSMichael Roth 
124c8ec041dSAndrew Deason #ifdef CONFIG_SOLARIS
125c8ec041dSAndrew Deason         execl("/sbin/shutdown", "shutdown", shutdown_flag, "-g0", "-y",
126c8ec041dSAndrew Deason               "hypervisor initiated shutdown", (char *)NULL);
127e40762fcSAlexander Ivanov #elif defined(CONFIG_BSD)
128e40762fcSAlexander Ivanov         execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
129e40762fcSAlexander Ivanov                "hypervisor initiated shutdown", (char *)NULL);
130c8ec041dSAndrew Deason #else
131fcc41961SMarc-André Lureau         execl("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0",
132fcc41961SMarc-André Lureau                "hypervisor initiated shutdown", (char *)NULL);
133c8ec041dSAndrew Deason #endif
1343674838cSLuiz Capitulino         _exit(EXIT_FAILURE);
135d5dd3498SLuiz Capitulino     } else if (pid < 0) {
13677dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to create child process");
137d5dd3498SLuiz Capitulino         return;
138d5dd3498SLuiz Capitulino     }
139d5dd3498SLuiz Capitulino 
140d220a6dfSLuiz Capitulino     ga_wait_child(pid, &status, &local_err);
14184d18f06SMarkus Armbruster     if (local_err) {
14277dbc81bSMarkus Armbruster         error_propagate(errp, local_err);
143d220a6dfSLuiz Capitulino         return;
144d220a6dfSLuiz Capitulino     }
145d220a6dfSLuiz Capitulino 
146d220a6dfSLuiz Capitulino     if (!WIFEXITED(status)) {
14777dbc81bSMarkus Armbruster         error_setg(errp, "child process has terminated abnormally");
148d220a6dfSLuiz Capitulino         return;
149d220a6dfSLuiz Capitulino     }
150d220a6dfSLuiz Capitulino 
151d220a6dfSLuiz Capitulino     if (WEXITSTATUS(status)) {
15277dbc81bSMarkus Armbruster         error_setg(errp, "child process has failed to shutdown");
153d220a6dfSLuiz Capitulino         return;
154d220a6dfSLuiz Capitulino     }
155d220a6dfSLuiz Capitulino 
156085d8134SPeter Maydell     /* succeeded */
157c216e5adSMichael Roth }
158c216e5adSMichael Roth 
1592c958923SMichal Privoznik void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
160a1bca57fSLei Li {
161a1bca57fSLei Li     int ret;
162a1bca57fSLei Li     int status;
163a1bca57fSLei Li     pid_t pid;
164a1bca57fSLei Li     Error *local_err = NULL;
165a1bca57fSLei Li     struct timeval tv;
1665c6096e5SCornelia Huck     static const char hwclock_path[] = "/sbin/hwclock";
1675c6096e5SCornelia Huck     static int hwclock_available = -1;
1685c6096e5SCornelia Huck 
1695c6096e5SCornelia Huck     if (hwclock_available < 0) {
1705c6096e5SCornelia Huck         hwclock_available = (access(hwclock_path, X_OK) == 0);
1715c6096e5SCornelia Huck     }
1725c6096e5SCornelia Huck 
1735c6096e5SCornelia Huck     if (!hwclock_available) {
1745c6096e5SCornelia Huck         error_setg(errp, QERR_UNSUPPORTED);
1755c6096e5SCornelia Huck         return;
1765c6096e5SCornelia Huck     }
177a1bca57fSLei Li 
1782c958923SMichal Privoznik     /* If user has passed a time, validate and set it. */
1792c958923SMichal Privoznik     if (has_time) {
18000d2f370SMarc-André Lureau         GDate date = { 0, };
18100d2f370SMarc-André Lureau 
182a1bca57fSLei Li         /* year-2038 will overflow in case time_t is 32bit */
183a1bca57fSLei Li         if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) {
184a1bca57fSLei Li             error_setg(errp, "Time %" PRId64 " is too large", time_ns);
185a1bca57fSLei Li             return;
186a1bca57fSLei Li         }
187a1bca57fSLei Li 
188a1bca57fSLei Li         tv.tv_sec = time_ns / 1000000000;
189a1bca57fSLei Li         tv.tv_usec = (time_ns % 1000000000) / 1000;
19000d2f370SMarc-André Lureau         g_date_set_time_t(&date, tv.tv_sec);
19100d2f370SMarc-André Lureau         if (date.year < 1970 || date.year >= 2070) {
19200d2f370SMarc-André Lureau             error_setg_errno(errp, errno, "Invalid time");
19300d2f370SMarc-André Lureau             return;
19400d2f370SMarc-André Lureau         }
195a1bca57fSLei Li 
196a1bca57fSLei Li         ret = settimeofday(&tv, NULL);
197a1bca57fSLei Li         if (ret < 0) {
198a1bca57fSLei Li             error_setg_errno(errp, errno, "Failed to set time to guest");
199a1bca57fSLei Li             return;
200a1bca57fSLei Li         }
2012c958923SMichal Privoznik     }
202a1bca57fSLei Li 
2032c958923SMichal Privoznik     /* Now, if user has passed a time to set and the system time is set, we
2042c958923SMichal Privoznik      * just need to synchronize the hardware clock. However, if no time was
2052c958923SMichal Privoznik      * passed, user is requesting the opposite: set the system time from the
2061634df56SAmos Kong      * hardware clock (RTC). */
207a1bca57fSLei Li     pid = fork();
208a1bca57fSLei Li     if (pid == 0) {
209a1bca57fSLei Li         setsid();
210a1bca57fSLei Li         reopen_fd_to_null(0);
211a1bca57fSLei Li         reopen_fd_to_null(1);
212a1bca57fSLei Li         reopen_fd_to_null(2);
213a1bca57fSLei Li 
2142c958923SMichal Privoznik         /* Use '/sbin/hwclock -w' to set RTC from the system time,
2152c958923SMichal Privoznik          * or '/sbin/hwclock -s' to set the system time from RTC. */
216fcc41961SMarc-André Lureau         execl(hwclock_path, "hwclock", has_time ? "-w" : "-s", NULL);
217a1bca57fSLei Li         _exit(EXIT_FAILURE);
218a1bca57fSLei Li     } else if (pid < 0) {
219a1bca57fSLei Li         error_setg_errno(errp, errno, "failed to create child process");
220a1bca57fSLei Li         return;
221a1bca57fSLei Li     }
222a1bca57fSLei Li 
223a1bca57fSLei Li     ga_wait_child(pid, &status, &local_err);
22484d18f06SMarkus Armbruster     if (local_err) {
225a1bca57fSLei Li         error_propagate(errp, local_err);
226a1bca57fSLei Li         return;
227a1bca57fSLei Li     }
228a1bca57fSLei Li 
229a1bca57fSLei Li     if (!WIFEXITED(status)) {
230a1bca57fSLei Li         error_setg(errp, "child process has terminated abnormally");
231a1bca57fSLei Li         return;
232a1bca57fSLei Li     }
233a1bca57fSLei Li 
234a1bca57fSLei Li     if (WEXITSTATUS(status)) {
235a1bca57fSLei Li         error_setg(errp, "hwclock failed to set hardware clock to system time");
236a1bca57fSLei Li         return;
237a1bca57fSLei Li     }
238a1bca57fSLei Li }
239a1bca57fSLei Li 
240895b00f6SMarc-André Lureau typedef enum {
241895b00f6SMarc-André Lureau     RW_STATE_NEW,
242895b00f6SMarc-André Lureau     RW_STATE_READING,
243895b00f6SMarc-André Lureau     RW_STATE_WRITING,
244895b00f6SMarc-André Lureau } RwState;
245895b00f6SMarc-André Lureau 
2465d3586b8SPhilippe Mathieu-Daudé struct GuestFileHandle {
247c216e5adSMichael Roth     uint64_t id;
248c216e5adSMichael Roth     FILE *fh;
249895b00f6SMarc-André Lureau     RwState state;
250c216e5adSMichael Roth     QTAILQ_ENTRY(GuestFileHandle) next;
2515d3586b8SPhilippe Mathieu-Daudé };
252c216e5adSMichael Roth 
253c216e5adSMichael Roth static struct {
254c216e5adSMichael Roth     QTAILQ_HEAD(, GuestFileHandle) filehandles;
255b4fe97c8SDenis V. Lunev } guest_file_state = {
256b4fe97c8SDenis V. Lunev     .filehandles = QTAILQ_HEAD_INITIALIZER(guest_file_state.filehandles),
257b4fe97c8SDenis V. Lunev };
258c216e5adSMichael Roth 
25939097dafSMichael Roth static int64_t guest_file_handle_add(FILE *fh, Error **errp)
260c216e5adSMichael Roth {
261c216e5adSMichael Roth     GuestFileHandle *gfh;
26239097dafSMichael Roth     int64_t handle;
26339097dafSMichael Roth 
26439097dafSMichael Roth     handle = ga_get_fd_handle(ga_state, errp);
265a903f40cSMarkus Armbruster     if (handle < 0) {
266a903f40cSMarkus Armbruster         return -1;
26739097dafSMichael Roth     }
268c216e5adSMichael Roth 
269f3a06403SMarkus Armbruster     gfh = g_new0(GuestFileHandle, 1);
27039097dafSMichael Roth     gfh->id = handle;
271c216e5adSMichael Roth     gfh->fh = fh;
272c216e5adSMichael Roth     QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
27339097dafSMichael Roth 
27439097dafSMichael Roth     return handle;
275c216e5adSMichael Roth }
276c216e5adSMichael Roth 
2775d3586b8SPhilippe Mathieu-Daudé GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp)
278c216e5adSMichael Roth {
279c216e5adSMichael Roth     GuestFileHandle *gfh;
280c216e5adSMichael Roth 
281c216e5adSMichael Roth     QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next)
282c216e5adSMichael Roth     {
283c216e5adSMichael Roth         if (gfh->id == id) {
284c216e5adSMichael Roth             return gfh;
285c216e5adSMichael Roth         }
286c216e5adSMichael Roth     }
287c216e5adSMichael Roth 
28877dbc81bSMarkus Armbruster     error_setg(errp, "handle '%" PRId64 "' has not been found", id);
289c216e5adSMichael Roth     return NULL;
290c216e5adSMichael Roth }
291c216e5adSMichael Roth 
292c689b4f1SLaszlo Ersek typedef const char * const ccpc;
293c689b4f1SLaszlo Ersek 
2948fe6bbcaSLaszlo Ersek #ifndef O_BINARY
2958fe6bbcaSLaszlo Ersek #define O_BINARY 0
2968fe6bbcaSLaszlo Ersek #endif
2978fe6bbcaSLaszlo Ersek 
298c689b4f1SLaszlo Ersek /* http://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html */
299c689b4f1SLaszlo Ersek static const struct {
300c689b4f1SLaszlo Ersek     ccpc *forms;
301c689b4f1SLaszlo Ersek     int oflag_base;
302c689b4f1SLaszlo Ersek } guest_file_open_modes[] = {
3038fe6bbcaSLaszlo Ersek     { (ccpc[]){ "r",          NULL }, O_RDONLY                                 },
3048fe6bbcaSLaszlo Ersek     { (ccpc[]){ "rb",         NULL }, O_RDONLY                      | O_BINARY },
3058fe6bbcaSLaszlo Ersek     { (ccpc[]){ "w",          NULL }, O_WRONLY | O_CREAT | O_TRUNC             },
3068fe6bbcaSLaszlo Ersek     { (ccpc[]){ "wb",         NULL }, O_WRONLY | O_CREAT | O_TRUNC  | O_BINARY },
3078fe6bbcaSLaszlo Ersek     { (ccpc[]){ "a",          NULL }, O_WRONLY | O_CREAT | O_APPEND            },
3088fe6bbcaSLaszlo Ersek     { (ccpc[]){ "ab",         NULL }, O_WRONLY | O_CREAT | O_APPEND | O_BINARY },
3098fe6bbcaSLaszlo Ersek     { (ccpc[]){ "r+",         NULL }, O_RDWR                                   },
3108fe6bbcaSLaszlo Ersek     { (ccpc[]){ "rb+", "r+b", NULL }, O_RDWR                        | O_BINARY },
3118fe6bbcaSLaszlo Ersek     { (ccpc[]){ "w+",         NULL }, O_RDWR   | O_CREAT | O_TRUNC             },
3128fe6bbcaSLaszlo Ersek     { (ccpc[]){ "wb+", "w+b", NULL }, O_RDWR   | O_CREAT | O_TRUNC  | O_BINARY },
3138fe6bbcaSLaszlo Ersek     { (ccpc[]){ "a+",         NULL }, O_RDWR   | O_CREAT | O_APPEND            },
3148fe6bbcaSLaszlo Ersek     { (ccpc[]){ "ab+", "a+b", NULL }, O_RDWR   | O_CREAT | O_APPEND | O_BINARY }
315c689b4f1SLaszlo Ersek };
316c689b4f1SLaszlo Ersek 
317c689b4f1SLaszlo Ersek static int
31877dbc81bSMarkus Armbruster find_open_flag(const char *mode_str, Error **errp)
319c689b4f1SLaszlo Ersek {
320c689b4f1SLaszlo Ersek     unsigned mode;
321c689b4f1SLaszlo Ersek 
322c689b4f1SLaszlo Ersek     for (mode = 0; mode < ARRAY_SIZE(guest_file_open_modes); ++mode) {
323c689b4f1SLaszlo Ersek         ccpc *form;
324c689b4f1SLaszlo Ersek 
325c689b4f1SLaszlo Ersek         form = guest_file_open_modes[mode].forms;
326c689b4f1SLaszlo Ersek         while (*form != NULL && strcmp(*form, mode_str) != 0) {
327c689b4f1SLaszlo Ersek             ++form;
328c689b4f1SLaszlo Ersek         }
329c689b4f1SLaszlo Ersek         if (*form != NULL) {
330c689b4f1SLaszlo Ersek             break;
331c689b4f1SLaszlo Ersek         }
332c689b4f1SLaszlo Ersek     }
333c689b4f1SLaszlo Ersek 
334c689b4f1SLaszlo Ersek     if (mode == ARRAY_SIZE(guest_file_open_modes)) {
33577dbc81bSMarkus Armbruster         error_setg(errp, "invalid file open mode '%s'", mode_str);
336c689b4f1SLaszlo Ersek         return -1;
337c689b4f1SLaszlo Ersek     }
338c689b4f1SLaszlo Ersek     return guest_file_open_modes[mode].oflag_base | O_NOCTTY | O_NONBLOCK;
339c689b4f1SLaszlo Ersek }
340c689b4f1SLaszlo Ersek 
341c689b4f1SLaszlo Ersek #define DEFAULT_NEW_FILE_MODE (S_IRUSR | S_IWUSR | \
342c689b4f1SLaszlo Ersek                                S_IRGRP | S_IWGRP | \
343c689b4f1SLaszlo Ersek                                S_IROTH | S_IWOTH)
344c689b4f1SLaszlo Ersek 
345c689b4f1SLaszlo Ersek static FILE *
34677dbc81bSMarkus Armbruster safe_open_or_create(const char *path, const char *mode, Error **errp)
347c689b4f1SLaszlo Ersek {
348c689b4f1SLaszlo Ersek     int oflag;
34969f56c14SMarc-André Lureau     int fd = -1;
35069f56c14SMarc-André Lureau     FILE *f = NULL;
351c689b4f1SLaszlo Ersek 
35269f56c14SMarc-André Lureau     oflag = find_open_flag(mode, errp);
35369f56c14SMarc-André Lureau     if (oflag < 0) {
35469f56c14SMarc-André Lureau         goto end;
35569f56c14SMarc-André Lureau     }
356c689b4f1SLaszlo Ersek 
357c689b4f1SLaszlo Ersek     /* If the caller wants / allows creation of a new file, we implement it
358c689b4f1SLaszlo Ersek      * with a two step process: open() + (open() / fchmod()).
359c689b4f1SLaszlo Ersek      *
360c689b4f1SLaszlo Ersek      * First we insist on creating the file exclusively as a new file. If
361c689b4f1SLaszlo Ersek      * that succeeds, we're free to set any file-mode bits on it. (The
362c689b4f1SLaszlo Ersek      * motivation is that we want to set those file-mode bits independently
363c689b4f1SLaszlo Ersek      * of the current umask.)
364c689b4f1SLaszlo Ersek      *
365c689b4f1SLaszlo Ersek      * If the exclusive creation fails because the file already exists
366c689b4f1SLaszlo Ersek      * (EEXIST is not possible for any other reason), we just attempt to
367c689b4f1SLaszlo Ersek      * open the file, but in this case we won't be allowed to change the
368c689b4f1SLaszlo Ersek      * file-mode bits on the preexistent file.
369c689b4f1SLaszlo Ersek      *
370c689b4f1SLaszlo Ersek      * The pathname should never disappear between the two open()s in
371c689b4f1SLaszlo Ersek      * practice. If it happens, then someone very likely tried to race us.
372c689b4f1SLaszlo Ersek      * In this case just go ahead and report the ENOENT from the second
373c689b4f1SLaszlo Ersek      * open() to the caller.
374c689b4f1SLaszlo Ersek      *
375c689b4f1SLaszlo Ersek      * If the caller wants to open a preexistent file, then the first
376c689b4f1SLaszlo Ersek      * open() is decisive and its third argument is ignored, and the second
377c689b4f1SLaszlo Ersek      * open() and the fchmod() are never called.
378c689b4f1SLaszlo Ersek      */
3791a89a17bSMarc-André Lureau     fd = qga_open_cloexec(path, oflag | ((oflag & O_CREAT) ? O_EXCL : 0), 0);
380c689b4f1SLaszlo Ersek     if (fd == -1 && errno == EEXIST) {
381c689b4f1SLaszlo Ersek         oflag &= ~(unsigned)O_CREAT;
3821a89a17bSMarc-André Lureau         fd = qga_open_cloexec(path, oflag, 0);
383c689b4f1SLaszlo Ersek     }
384c689b4f1SLaszlo Ersek     if (fd == -1) {
38569f56c14SMarc-André Lureau         error_setg_errno(errp, errno,
38669f56c14SMarc-André Lureau                          "failed to open file '%s' (mode: '%s')",
38769f56c14SMarc-André Lureau                          path, mode);
38869f56c14SMarc-André Lureau         goto end;
38969f56c14SMarc-André Lureau     }
39069f56c14SMarc-André Lureau 
391c689b4f1SLaszlo Ersek     if ((oflag & O_CREAT) && fchmod(fd, DEFAULT_NEW_FILE_MODE) == -1) {
39269f56c14SMarc-André Lureau         error_setg_errno(errp, errno, "failed to set permission "
393c689b4f1SLaszlo Ersek                          "0%03o on new file '%s' (mode: '%s')",
394c689b4f1SLaszlo Ersek                          (unsigned)DEFAULT_NEW_FILE_MODE, path, mode);
39569f56c14SMarc-André Lureau         goto end;
39669f56c14SMarc-André Lureau     }
397c689b4f1SLaszlo Ersek 
398c689b4f1SLaszlo Ersek     f = fdopen(fd, mode);
399c689b4f1SLaszlo Ersek     if (f == NULL) {
40069f56c14SMarc-André Lureau         error_setg_errno(errp, errno, "failed to associate stdio stream with "
40169f56c14SMarc-André Lureau                          "file descriptor %d, file '%s' (mode: '%s')",
40269f56c14SMarc-André Lureau                          fd, path, mode);
403c689b4f1SLaszlo Ersek     }
404c689b4f1SLaszlo Ersek 
40569f56c14SMarc-André Lureau end:
40669f56c14SMarc-André Lureau     if (f == NULL && fd != -1) {
407c689b4f1SLaszlo Ersek         close(fd);
4082b720018SLaszlo Ersek         if (oflag & O_CREAT) {
4092b720018SLaszlo Ersek             unlink(path);
4102b720018SLaszlo Ersek         }
411c689b4f1SLaszlo Ersek     }
41269f56c14SMarc-André Lureau     return f;
413c689b4f1SLaszlo Ersek }
414c689b4f1SLaszlo Ersek 
41577dbc81bSMarkus Armbruster int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode,
41677dbc81bSMarkus Armbruster                             Error **errp)
417c216e5adSMichael Roth {
418c216e5adSMichael Roth     FILE *fh;
419c689b4f1SLaszlo Ersek     Error *local_err = NULL;
42085b6f6f5SSimon Zolin     int64_t handle;
421c216e5adSMichael Roth 
422c216e5adSMichael Roth     if (!has_mode) {
423c216e5adSMichael Roth         mode = "r";
424c216e5adSMichael Roth     }
425c216e5adSMichael Roth     slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
426c689b4f1SLaszlo Ersek     fh = safe_open_or_create(path, mode, &local_err);
427c689b4f1SLaszlo Ersek     if (local_err != NULL) {
42877dbc81bSMarkus Armbruster         error_propagate(errp, local_err);
429c216e5adSMichael Roth         return -1;
430c216e5adSMichael Roth     }
431c216e5adSMichael Roth 
432c216e5adSMichael Roth     /* set fd non-blocking to avoid common use cases (like reading from a
433c216e5adSMichael Roth      * named pipe) from hanging the agent
434c216e5adSMichael Roth      */
435b0a8f9adSMarc-André Lureau     if (!g_unix_set_fd_nonblocking(fileno(fh), true, NULL)) {
436b0a8f9adSMarc-André Lureau         fclose(fh);
437b0a8f9adSMarc-André Lureau         error_setg_errno(errp, errno, "Failed to set FD nonblocking");
438b0a8f9adSMarc-André Lureau         return -1;
439b0a8f9adSMarc-André Lureau     }
440c216e5adSMichael Roth 
44177dbc81bSMarkus Armbruster     handle = guest_file_handle_add(fh, errp);
442a903f40cSMarkus Armbruster     if (handle < 0) {
44339097dafSMichael Roth         fclose(fh);
44439097dafSMichael Roth         return -1;
44539097dafSMichael Roth     }
44639097dafSMichael Roth 
447d607a523SStefan Weil     slog("guest-file-open, handle: %" PRId64, handle);
44839097dafSMichael Roth     return handle;
449c216e5adSMichael Roth }
450c216e5adSMichael Roth 
45177dbc81bSMarkus Armbruster void qmp_guest_file_close(int64_t handle, Error **errp)
452c216e5adSMichael Roth {
45377dbc81bSMarkus Armbruster     GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
454c216e5adSMichael Roth     int ret;
455c216e5adSMichael Roth 
456d607a523SStefan Weil     slog("guest-file-close called, handle: %" PRId64, handle);
457c216e5adSMichael Roth     if (!gfh) {
458c216e5adSMichael Roth         return;
459c216e5adSMichael Roth     }
460c216e5adSMichael Roth 
461c216e5adSMichael Roth     ret = fclose(gfh->fh);
4623ac4b7c5SLuiz Capitulino     if (ret == EOF) {
46377dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to close handle");
464c216e5adSMichael Roth         return;
465c216e5adSMichael Roth     }
466c216e5adSMichael Roth 
467c216e5adSMichael Roth     QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
468c216e5adSMichael Roth     g_free(gfh);
469c216e5adSMichael Roth }
470c216e5adSMichael Roth 
471ead83a13SPhilippe Mathieu-Daudé GuestFileRead *guest_file_read_unsafe(GuestFileHandle *gfh,
47277dbc81bSMarkus Armbruster                                       int64_t count, Error **errp)
473c216e5adSMichael Roth {
474c216e5adSMichael Roth     GuestFileRead *read_data = NULL;
475c216e5adSMichael Roth     guchar *buf;
476ead83a13SPhilippe Mathieu-Daudé     FILE *fh = gfh->fh;
477c216e5adSMichael Roth     size_t read_count;
478c216e5adSMichael Roth 
479895b00f6SMarc-André Lureau     /* explicitly flush when switching from writing to reading */
480895b00f6SMarc-André Lureau     if (gfh->state == RW_STATE_WRITING) {
481895b00f6SMarc-André Lureau         int ret = fflush(fh);
482895b00f6SMarc-André Lureau         if (ret == EOF) {
483895b00f6SMarc-André Lureau             error_setg_errno(errp, errno, "failed to flush file");
484895b00f6SMarc-André Lureau             return NULL;
485895b00f6SMarc-André Lureau         }
486895b00f6SMarc-André Lureau         gfh->state = RW_STATE_NEW;
487895b00f6SMarc-André Lureau     }
488895b00f6SMarc-André Lureau 
489c216e5adSMichael Roth     buf = g_malloc0(count + 1);
490c216e5adSMichael Roth     read_count = fread(buf, 1, count, fh);
491c216e5adSMichael Roth     if (ferror(fh)) {
49277dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to read file");
493c216e5adSMichael Roth     } else {
494c216e5adSMichael Roth         buf[read_count] = 0;
495f3a06403SMarkus Armbruster         read_data = g_new0(GuestFileRead, 1);
496c216e5adSMichael Roth         read_data->count = read_count;
497c216e5adSMichael Roth         read_data->eof = feof(fh);
498c216e5adSMichael Roth         if (read_count) {
499c216e5adSMichael Roth             read_data->buf_b64 = g_base64_encode(buf, read_count);
500c216e5adSMichael Roth         }
501895b00f6SMarc-André Lureau         gfh->state = RW_STATE_READING;
502c216e5adSMichael Roth     }
503c216e5adSMichael Roth     g_free(buf);
504c216e5adSMichael Roth     clearerr(fh);
505c216e5adSMichael Roth 
506c216e5adSMichael Roth     return read_data;
507c216e5adSMichael Roth }
508c216e5adSMichael Roth 
509c216e5adSMichael Roth GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
51077dbc81bSMarkus Armbruster                                      bool has_count, int64_t count,
51177dbc81bSMarkus Armbruster                                      Error **errp)
512c216e5adSMichael Roth {
513c216e5adSMichael Roth     GuestFileWrite *write_data = NULL;
514c216e5adSMichael Roth     guchar *buf;
515c216e5adSMichael Roth     gsize buf_len;
516c216e5adSMichael Roth     int write_count;
51777dbc81bSMarkus Armbruster     GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
518c216e5adSMichael Roth     FILE *fh;
519c216e5adSMichael Roth 
520c216e5adSMichael Roth     if (!gfh) {
521c216e5adSMichael Roth         return NULL;
522c216e5adSMichael Roth     }
523c216e5adSMichael Roth 
524c216e5adSMichael Roth     fh = gfh->fh;
525895b00f6SMarc-André Lureau 
526895b00f6SMarc-André Lureau     if (gfh->state == RW_STATE_READING) {
527895b00f6SMarc-André Lureau         int ret = fseek(fh, 0, SEEK_CUR);
528895b00f6SMarc-André Lureau         if (ret == -1) {
529895b00f6SMarc-André Lureau             error_setg_errno(errp, errno, "failed to seek file");
530895b00f6SMarc-André Lureau             return NULL;
531895b00f6SMarc-André Lureau         }
532895b00f6SMarc-André Lureau         gfh->state = RW_STATE_NEW;
533895b00f6SMarc-André Lureau     }
534895b00f6SMarc-André Lureau 
535920639caSDaniel P. Berrange     buf = qbase64_decode(buf_b64, -1, &buf_len, errp);
536920639caSDaniel P. Berrange     if (!buf) {
537920639caSDaniel P. Berrange         return NULL;
538920639caSDaniel P. Berrange     }
539c216e5adSMichael Roth 
540c216e5adSMichael Roth     if (!has_count) {
541c216e5adSMichael Roth         count = buf_len;
542c216e5adSMichael Roth     } else if (count < 0 || count > buf_len) {
54377dbc81bSMarkus Armbruster         error_setg(errp, "value '%" PRId64 "' is invalid for argument count",
544db3edb66SLuiz Capitulino                    count);
545c216e5adSMichael Roth         g_free(buf);
546c216e5adSMichael Roth         return NULL;
547c216e5adSMichael Roth     }
548c216e5adSMichael Roth 
549c216e5adSMichael Roth     write_count = fwrite(buf, 1, count, fh);
550c216e5adSMichael Roth     if (ferror(fh)) {
55177dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to write to file");
552d607a523SStefan Weil         slog("guest-file-write failed, handle: %" PRId64, handle);
553c216e5adSMichael Roth     } else {
554f3a06403SMarkus Armbruster         write_data = g_new0(GuestFileWrite, 1);
555c216e5adSMichael Roth         write_data->count = write_count;
556c216e5adSMichael Roth         write_data->eof = feof(fh);
557895b00f6SMarc-André Lureau         gfh->state = RW_STATE_WRITING;
558c216e5adSMichael Roth     }
559c216e5adSMichael Roth     g_free(buf);
560c216e5adSMichael Roth     clearerr(fh);
561c216e5adSMichael Roth 
562c216e5adSMichael Roth     return write_data;
563c216e5adSMichael Roth }
564c216e5adSMichael Roth 
565c216e5adSMichael Roth struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
5660b4b4938SEric Blake                                           GuestFileWhence *whence_code,
5670b4b4938SEric Blake                                           Error **errp)
568c216e5adSMichael Roth {
56977dbc81bSMarkus Armbruster     GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
570c216e5adSMichael Roth     GuestFileSeek *seek_data = NULL;
571c216e5adSMichael Roth     FILE *fh;
572c216e5adSMichael Roth     int ret;
5730a982b1bSEric Blake     int whence;
5740b4b4938SEric Blake     Error *err = NULL;
575c216e5adSMichael Roth 
576c216e5adSMichael Roth     if (!gfh) {
577c216e5adSMichael Roth         return NULL;
578c216e5adSMichael Roth     }
579c216e5adSMichael Roth 
5800a982b1bSEric Blake     /* We stupidly exposed 'whence':'int' in our qapi */
5810b4b4938SEric Blake     whence = ga_parse_whence(whence_code, &err);
5820b4b4938SEric Blake     if (err) {
5830b4b4938SEric Blake         error_propagate(errp, err);
5840a982b1bSEric Blake         return NULL;
5850a982b1bSEric Blake     }
5860a982b1bSEric Blake 
587c216e5adSMichael Roth     fh = gfh->fh;
588c216e5adSMichael Roth     ret = fseek(fh, offset, whence);
589c216e5adSMichael Roth     if (ret == -1) {
59077dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to seek file");
591895b00f6SMarc-André Lureau         if (errno == ESPIPE) {
592895b00f6SMarc-André Lureau             /* file is non-seekable, stdio shouldn't be buffering anyways */
593895b00f6SMarc-André Lureau             gfh->state = RW_STATE_NEW;
594895b00f6SMarc-André Lureau         }
595c216e5adSMichael Roth     } else {
59610b7c5ddSMarkus Armbruster         seek_data = g_new0(GuestFileSeek, 1);
597c216e5adSMichael Roth         seek_data->position = ftell(fh);
598c216e5adSMichael Roth         seek_data->eof = feof(fh);
599895b00f6SMarc-André Lureau         gfh->state = RW_STATE_NEW;
600c216e5adSMichael Roth     }
601c216e5adSMichael Roth     clearerr(fh);
602c216e5adSMichael Roth 
603c216e5adSMichael Roth     return seek_data;
604c216e5adSMichael Roth }
605c216e5adSMichael Roth 
60677dbc81bSMarkus Armbruster void qmp_guest_file_flush(int64_t handle, Error **errp)
607c216e5adSMichael Roth {
60877dbc81bSMarkus Armbruster     GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
609c216e5adSMichael Roth     FILE *fh;
610c216e5adSMichael Roth     int ret;
611c216e5adSMichael Roth 
612c216e5adSMichael Roth     if (!gfh) {
613c216e5adSMichael Roth         return;
614c216e5adSMichael Roth     }
615c216e5adSMichael Roth 
616c216e5adSMichael Roth     fh = gfh->fh;
617c216e5adSMichael Roth     ret = fflush(fh);
618c216e5adSMichael Roth     if (ret == EOF) {
61977dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to flush file");
620895b00f6SMarc-André Lureau     } else {
621895b00f6SMarc-André Lureau         gfh->state = RW_STATE_NEW;
622c216e5adSMichael Roth     }
623c216e5adSMichael Roth }
624c216e5adSMichael Roth 
625eab5fd59SPaolo Bonzini #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
626518b0d80SAlexander Ivanov void free_fs_mount_list(FsMountList *mounts)
627c216e5adSMichael Roth {
628af02203fSPaolo Bonzini      FsMount *mount, *temp;
629c216e5adSMichael Roth 
6309e8aded4SMichael Roth      if (!mounts) {
6319e8aded4SMichael Roth          return;
6329e8aded4SMichael Roth      }
6339e8aded4SMichael Roth 
6349e8aded4SMichael Roth      QTAILQ_FOREACH_SAFE(mount, mounts, next, temp) {
6359e8aded4SMichael Roth          QTAILQ_REMOVE(mounts, mount, next);
636c216e5adSMichael Roth          g_free(mount->dirname);
637c216e5adSMichael Roth          g_free(mount->devtype);
638c216e5adSMichael Roth          g_free(mount);
639c216e5adSMichael Roth      }
6409e8aded4SMichael Roth }
641eab5fd59SPaolo Bonzini #endif
642eab5fd59SPaolo Bonzini 
643eab5fd59SPaolo Bonzini #if defined(CONFIG_FSFREEZE)
644bad0001eSAlexander Ivanov typedef enum {
645bad0001eSAlexander Ivanov     FSFREEZE_HOOK_THAW = 0,
646bad0001eSAlexander Ivanov     FSFREEZE_HOOK_FREEZE,
647bad0001eSAlexander Ivanov } FsfreezeHookArg;
648bad0001eSAlexander Ivanov 
649bad0001eSAlexander Ivanov static const char *fsfreeze_hook_arg_string[] = {
650bad0001eSAlexander Ivanov     "thaw",
651bad0001eSAlexander Ivanov     "freeze",
652bad0001eSAlexander Ivanov };
653bad0001eSAlexander Ivanov 
654bad0001eSAlexander Ivanov static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp)
655bad0001eSAlexander Ivanov {
656bad0001eSAlexander Ivanov     int status;
657bad0001eSAlexander Ivanov     pid_t pid;
658bad0001eSAlexander Ivanov     const char *hook;
659bad0001eSAlexander Ivanov     const char *arg_str = fsfreeze_hook_arg_string[arg];
660bad0001eSAlexander Ivanov     Error *local_err = NULL;
661bad0001eSAlexander Ivanov 
662bad0001eSAlexander Ivanov     hook = ga_fsfreeze_hook(ga_state);
663bad0001eSAlexander Ivanov     if (!hook) {
664bad0001eSAlexander Ivanov         return;
665bad0001eSAlexander Ivanov     }
666bad0001eSAlexander Ivanov     if (access(hook, X_OK) != 0) {
667bad0001eSAlexander Ivanov         error_setg_errno(errp, errno, "can't access fsfreeze hook '%s'", hook);
668bad0001eSAlexander Ivanov         return;
669bad0001eSAlexander Ivanov     }
670bad0001eSAlexander Ivanov 
671bad0001eSAlexander Ivanov     slog("executing fsfreeze hook with arg '%s'", arg_str);
672bad0001eSAlexander Ivanov     pid = fork();
673bad0001eSAlexander Ivanov     if (pid == 0) {
674bad0001eSAlexander Ivanov         setsid();
675bad0001eSAlexander Ivanov         reopen_fd_to_null(0);
676bad0001eSAlexander Ivanov         reopen_fd_to_null(1);
677bad0001eSAlexander Ivanov         reopen_fd_to_null(2);
678bad0001eSAlexander Ivanov 
679bad0001eSAlexander Ivanov         execl(hook, hook, arg_str, NULL);
680bad0001eSAlexander Ivanov         _exit(EXIT_FAILURE);
681bad0001eSAlexander Ivanov     } else if (pid < 0) {
682bad0001eSAlexander Ivanov         error_setg_errno(errp, errno, "failed to create child process");
683bad0001eSAlexander Ivanov         return;
684bad0001eSAlexander Ivanov     }
685bad0001eSAlexander Ivanov 
686bad0001eSAlexander Ivanov     ga_wait_child(pid, &status, &local_err);
687bad0001eSAlexander Ivanov     if (local_err) {
688bad0001eSAlexander Ivanov         error_propagate(errp, local_err);
689bad0001eSAlexander Ivanov         return;
690bad0001eSAlexander Ivanov     }
691bad0001eSAlexander Ivanov 
692bad0001eSAlexander Ivanov     if (!WIFEXITED(status)) {
693bad0001eSAlexander Ivanov         error_setg(errp, "fsfreeze hook has terminated abnormally");
694bad0001eSAlexander Ivanov         return;
695bad0001eSAlexander Ivanov     }
696bad0001eSAlexander Ivanov 
697bad0001eSAlexander Ivanov     status = WEXITSTATUS(status);
698bad0001eSAlexander Ivanov     if (status) {
699bad0001eSAlexander Ivanov         error_setg(errp, "fsfreeze hook has failed with status %d", status);
700bad0001eSAlexander Ivanov         return;
701bad0001eSAlexander Ivanov     }
702bad0001eSAlexander Ivanov }
703bad0001eSAlexander Ivanov 
704bad0001eSAlexander Ivanov /*
705bad0001eSAlexander Ivanov  * Return status of freeze/thaw
706bad0001eSAlexander Ivanov  */
707bad0001eSAlexander Ivanov GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
708bad0001eSAlexander Ivanov {
709bad0001eSAlexander Ivanov     if (ga_is_frozen(ga_state)) {
710bad0001eSAlexander Ivanov         return GUEST_FSFREEZE_STATUS_FROZEN;
711bad0001eSAlexander Ivanov     }
712bad0001eSAlexander Ivanov 
713bad0001eSAlexander Ivanov     return GUEST_FSFREEZE_STATUS_THAWED;
714bad0001eSAlexander Ivanov }
715bad0001eSAlexander Ivanov 
716bad0001eSAlexander Ivanov int64_t qmp_guest_fsfreeze_freeze(Error **errp)
717bad0001eSAlexander Ivanov {
718bad0001eSAlexander Ivanov     return qmp_guest_fsfreeze_freeze_list(false, NULL, errp);
719bad0001eSAlexander Ivanov }
720bad0001eSAlexander Ivanov 
721bad0001eSAlexander Ivanov int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
722bad0001eSAlexander Ivanov                                        strList *mountpoints,
723bad0001eSAlexander Ivanov                                        Error **errp)
724bad0001eSAlexander Ivanov {
725bad0001eSAlexander Ivanov     int ret;
726bad0001eSAlexander Ivanov     FsMountList mounts;
727bad0001eSAlexander Ivanov     Error *local_err = NULL;
728bad0001eSAlexander Ivanov 
729bad0001eSAlexander Ivanov     slog("guest-fsfreeze called");
730bad0001eSAlexander Ivanov 
731bad0001eSAlexander Ivanov     execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err);
732bad0001eSAlexander Ivanov     if (local_err) {
733bad0001eSAlexander Ivanov         error_propagate(errp, local_err);
734bad0001eSAlexander Ivanov         return -1;
735bad0001eSAlexander Ivanov     }
736bad0001eSAlexander Ivanov 
737bad0001eSAlexander Ivanov     QTAILQ_INIT(&mounts);
738bad0001eSAlexander Ivanov     if (!build_fs_mount_list(&mounts, &local_err)) {
739bad0001eSAlexander Ivanov         error_propagate(errp, local_err);
740bad0001eSAlexander Ivanov         return -1;
741bad0001eSAlexander Ivanov     }
742bad0001eSAlexander Ivanov 
743bad0001eSAlexander Ivanov     /* cannot risk guest agent blocking itself on a write in this state */
744bad0001eSAlexander Ivanov     ga_set_frozen(ga_state);
745bad0001eSAlexander Ivanov 
746bad0001eSAlexander Ivanov     ret = qmp_guest_fsfreeze_do_freeze_list(has_mountpoints, mountpoints,
747bad0001eSAlexander Ivanov                                             mounts, errp);
748bad0001eSAlexander Ivanov 
749bad0001eSAlexander Ivanov     free_fs_mount_list(&mounts);
750bad0001eSAlexander Ivanov     /* We may not issue any FIFREEZE here.
751bad0001eSAlexander Ivanov      * Just unset ga_state here and ready for the next call.
752bad0001eSAlexander Ivanov      */
753bad0001eSAlexander Ivanov     if (ret == 0) {
754bad0001eSAlexander Ivanov         ga_unset_frozen(ga_state);
755bad0001eSAlexander Ivanov     } else if (ret < 0) {
756bad0001eSAlexander Ivanov         qmp_guest_fsfreeze_thaw(NULL);
757bad0001eSAlexander Ivanov     }
758bad0001eSAlexander Ivanov     return ret;
759bad0001eSAlexander Ivanov }
760bad0001eSAlexander Ivanov 
761bad0001eSAlexander Ivanov int64_t qmp_guest_fsfreeze_thaw(Error **errp)
762bad0001eSAlexander Ivanov {
763bad0001eSAlexander Ivanov     int ret;
764bad0001eSAlexander Ivanov 
765bad0001eSAlexander Ivanov     ret = qmp_guest_fsfreeze_do_thaw(errp);
766bad0001eSAlexander Ivanov     if (ret >= 0) {
767bad0001eSAlexander Ivanov         ga_unset_frozen(ga_state);
768bad0001eSAlexander Ivanov         execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp);
769bad0001eSAlexander Ivanov     } else {
770bad0001eSAlexander Ivanov         ret = 0;
771bad0001eSAlexander Ivanov     }
772bad0001eSAlexander Ivanov 
773bad0001eSAlexander Ivanov     return ret;
774bad0001eSAlexander Ivanov }
775bad0001eSAlexander Ivanov 
776bad0001eSAlexander Ivanov static void guest_fsfreeze_cleanup(void)
777bad0001eSAlexander Ivanov {
778bad0001eSAlexander Ivanov     Error *err = NULL;
779bad0001eSAlexander Ivanov 
780bad0001eSAlexander Ivanov     if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) {
781bad0001eSAlexander Ivanov         qmp_guest_fsfreeze_thaw(&err);
782bad0001eSAlexander Ivanov         if (err) {
783bad0001eSAlexander Ivanov             slog("failed to clean up frozen filesystems: %s",
784bad0001eSAlexander Ivanov                  error_get_pretty(err));
785bad0001eSAlexander Ivanov             error_free(err);
786bad0001eSAlexander Ivanov         }
787bad0001eSAlexander Ivanov     }
788bad0001eSAlexander Ivanov }
789bad0001eSAlexander Ivanov #endif
790bad0001eSAlexander Ivanov 
791bad0001eSAlexander Ivanov /* linux-specific implementations. avoid this if at all possible. */
792bad0001eSAlexander Ivanov #if defined(__linux__)
793bad0001eSAlexander Ivanov #if defined(CONFIG_FSFREEZE)
794c216e5adSMichael Roth 
79546d4c572STomoki Sekiyama static char *get_pci_driver(char const *syspath, int pathlen, Error **errp)
79646d4c572STomoki Sekiyama {
79746d4c572STomoki Sekiyama     char *path;
79846d4c572STomoki Sekiyama     char *dpath;
79946d4c572STomoki Sekiyama     char *driver = NULL;
80046d4c572STomoki Sekiyama     char buf[PATH_MAX];
80146d4c572STomoki Sekiyama     ssize_t len;
80246d4c572STomoki Sekiyama 
80346d4c572STomoki Sekiyama     path = g_strndup(syspath, pathlen);
80446d4c572STomoki Sekiyama     dpath = g_strdup_printf("%s/driver", path);
80546d4c572STomoki Sekiyama     len = readlink(dpath, buf, sizeof(buf) - 1);
80646d4c572STomoki Sekiyama     if (len != -1) {
80746d4c572STomoki Sekiyama         buf[len] = 0;
8083e015d81SJulia Suvorova         driver = g_path_get_basename(buf);
80946d4c572STomoki Sekiyama     }
81046d4c572STomoki Sekiyama     g_free(dpath);
81146d4c572STomoki Sekiyama     g_free(path);
81246d4c572STomoki Sekiyama     return driver;
81346d4c572STomoki Sekiyama }
81446d4c572STomoki Sekiyama 
81546d4c572STomoki Sekiyama static int compare_uint(const void *_a, const void *_b)
81646d4c572STomoki Sekiyama {
81746d4c572STomoki Sekiyama     unsigned int a = *(unsigned int *)_a;
81846d4c572STomoki Sekiyama     unsigned int b = *(unsigned int *)_b;
81946d4c572STomoki Sekiyama 
82046d4c572STomoki Sekiyama     return a < b ? -1 : a > b ? 1 : 0;
82146d4c572STomoki Sekiyama }
82246d4c572STomoki Sekiyama 
82346d4c572STomoki Sekiyama /* Walk the specified sysfs and build a sorted list of host or ata numbers */
82446d4c572STomoki Sekiyama static int build_hosts(char const *syspath, char const *host, bool ata,
82546d4c572STomoki Sekiyama                        unsigned int *hosts, int hosts_max, Error **errp)
82646d4c572STomoki Sekiyama {
82746d4c572STomoki Sekiyama     char *path;
82846d4c572STomoki Sekiyama     DIR *dir;
82946d4c572STomoki Sekiyama     struct dirent *entry;
83046d4c572STomoki Sekiyama     int i = 0;
83146d4c572STomoki Sekiyama 
83246d4c572STomoki Sekiyama     path = g_strndup(syspath, host - syspath);
83346d4c572STomoki Sekiyama     dir = opendir(path);
83446d4c572STomoki Sekiyama     if (!dir) {
83546d4c572STomoki Sekiyama         error_setg_errno(errp, errno, "opendir(\"%s\")", path);
83646d4c572STomoki Sekiyama         g_free(path);
83746d4c572STomoki Sekiyama         return -1;
83846d4c572STomoki Sekiyama     }
83946d4c572STomoki Sekiyama 
84046d4c572STomoki Sekiyama     while (i < hosts_max) {
84146d4c572STomoki Sekiyama         entry = readdir(dir);
84246d4c572STomoki Sekiyama         if (!entry) {
84346d4c572STomoki Sekiyama             break;
84446d4c572STomoki Sekiyama         }
84546d4c572STomoki Sekiyama         if (ata && sscanf(entry->d_name, "ata%d", hosts + i) == 1) {
84646d4c572STomoki Sekiyama             ++i;
84746d4c572STomoki Sekiyama         } else if (!ata && sscanf(entry->d_name, "host%d", hosts + i) == 1) {
84846d4c572STomoki Sekiyama             ++i;
84946d4c572STomoki Sekiyama         }
85046d4c572STomoki Sekiyama     }
85146d4c572STomoki Sekiyama 
85246d4c572STomoki Sekiyama     qsort(hosts, i, sizeof(hosts[0]), compare_uint);
85346d4c572STomoki Sekiyama 
85446d4c572STomoki Sekiyama     g_free(path);
85546d4c572STomoki Sekiyama     closedir(dir);
85646d4c572STomoki Sekiyama     return i;
85746d4c572STomoki Sekiyama }
85846d4c572STomoki Sekiyama 
859d9fe4f0fSThomas Huth /*
860d9fe4f0fSThomas Huth  * Store disk device info for devices on the PCI bus.
861d9fe4f0fSThomas Huth  * Returns true if information has been stored, or false for failure.
862d9fe4f0fSThomas Huth  */
863d9fe4f0fSThomas Huth static bool build_guest_fsinfo_for_pci_dev(char const *syspath,
864d9fe4f0fSThomas Huth                                            GuestDiskAddress *disk,
86546d4c572STomoki Sekiyama                                            Error **errp)
86646d4c572STomoki Sekiyama {
86746d4c572STomoki Sekiyama     unsigned int pci[4], host, hosts[8], tgt[3];
86846d4c572STomoki Sekiyama     int i, nhosts = 0, pcilen;
869d9fe4f0fSThomas Huth     GuestPCIAddress *pciaddr = disk->pci_controller;
87046d4c572STomoki Sekiyama     bool has_ata = false, has_host = false, has_tgt = false;
87146d4c572STomoki Sekiyama     char *p, *q, *driver = NULL;
872d9fe4f0fSThomas Huth     bool ret = false;
87346d4c572STomoki Sekiyama 
87446d4c572STomoki Sekiyama     p = strstr(syspath, "/devices/pci");
87546d4c572STomoki Sekiyama     if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n",
87646d4c572STomoki Sekiyama                      pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) {
877743c71d0SMarc-André Lureau         g_debug("only pci device is supported: sysfs path '%s'", syspath);
878d9fe4f0fSThomas Huth         return false;
87946d4c572STomoki Sekiyama     }
88046d4c572STomoki Sekiyama 
881743c71d0SMarc-André Lureau     p += 12 + pcilen;
882743c71d0SMarc-André Lureau     while (true) {
883743c71d0SMarc-André Lureau         driver = get_pci_driver(syspath, p - syspath, errp);
884743c71d0SMarc-André Lureau         if (driver && (g_str_equal(driver, "ata_piix") ||
885743c71d0SMarc-André Lureau                        g_str_equal(driver, "sym53c8xx") ||
886743c71d0SMarc-André Lureau                        g_str_equal(driver, "virtio-pci") ||
887d48f61c8Szhenwei pi                        g_str_equal(driver, "ahci") ||
888d48f61c8Szhenwei pi                        g_str_equal(driver, "nvme"))) {
889743c71d0SMarc-André Lureau             break;
890743c71d0SMarc-André Lureau         }
891743c71d0SMarc-André Lureau 
892bb23a736SMarc-André Lureau         g_free(driver);
893743c71d0SMarc-André Lureau         if (sscanf(p, "/%x:%x:%x.%x%n",
894743c71d0SMarc-André Lureau                           pci, pci + 1, pci + 2, pci + 3, &pcilen) == 4) {
895743c71d0SMarc-André Lureau             p += pcilen;
896743c71d0SMarc-André Lureau             continue;
897743c71d0SMarc-André Lureau         }
898743c71d0SMarc-André Lureau 
899743c71d0SMarc-André Lureau         g_debug("unsupported driver or sysfs path '%s'", syspath);
900d9fe4f0fSThomas Huth         return false;
90146d4c572STomoki Sekiyama     }
90246d4c572STomoki Sekiyama 
90346d4c572STomoki Sekiyama     p = strstr(syspath, "/target");
90446d4c572STomoki Sekiyama     if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u",
90546d4c572STomoki Sekiyama                     tgt, tgt + 1, tgt + 2) == 3) {
90646d4c572STomoki Sekiyama         has_tgt = true;
90746d4c572STomoki Sekiyama     }
90846d4c572STomoki Sekiyama 
90946d4c572STomoki Sekiyama     p = strstr(syspath, "/ata");
91046d4c572STomoki Sekiyama     if (p) {
91146d4c572STomoki Sekiyama         q = p + 4;
91246d4c572STomoki Sekiyama         has_ata = true;
91346d4c572STomoki Sekiyama     } else {
91446d4c572STomoki Sekiyama         p = strstr(syspath, "/host");
91546d4c572STomoki Sekiyama         q = p + 5;
91646d4c572STomoki Sekiyama     }
91746d4c572STomoki Sekiyama     if (p && sscanf(q, "%u", &host) == 1) {
91846d4c572STomoki Sekiyama         has_host = true;
91946d4c572STomoki Sekiyama         nhosts = build_hosts(syspath, p, has_ata, hosts,
92001a6df1bSPhilippe Mathieu-Daudé                              ARRAY_SIZE(hosts), errp);
92146d4c572STomoki Sekiyama         if (nhosts < 0) {
92246d4c572STomoki Sekiyama             goto cleanup;
92346d4c572STomoki Sekiyama         }
92446d4c572STomoki Sekiyama     }
92546d4c572STomoki Sekiyama 
92646d4c572STomoki Sekiyama     pciaddr->domain = pci[0];
92746d4c572STomoki Sekiyama     pciaddr->bus = pci[1];
92846d4c572STomoki Sekiyama     pciaddr->slot = pci[2];
92946d4c572STomoki Sekiyama     pciaddr->function = pci[3];
93046d4c572STomoki Sekiyama 
93146d4c572STomoki Sekiyama     if (strcmp(driver, "ata_piix") == 0) {
93246d4c572STomoki Sekiyama         /* a host per ide bus, target*:0:<unit>:0 */
93346d4c572STomoki Sekiyama         if (!has_host || !has_tgt) {
93446d4c572STomoki Sekiyama             g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
93546d4c572STomoki Sekiyama             goto cleanup;
93646d4c572STomoki Sekiyama         }
93746d4c572STomoki Sekiyama         for (i = 0; i < nhosts; i++) {
93846d4c572STomoki Sekiyama             if (host == hosts[i]) {
93946d4c572STomoki Sekiyama                 disk->bus_type = GUEST_DISK_BUS_TYPE_IDE;
94046d4c572STomoki Sekiyama                 disk->bus = i;
94146d4c572STomoki Sekiyama                 disk->unit = tgt[1];
94246d4c572STomoki Sekiyama                 break;
94346d4c572STomoki Sekiyama             }
94446d4c572STomoki Sekiyama         }
94546d4c572STomoki Sekiyama         if (i >= nhosts) {
94646d4c572STomoki Sekiyama             g_debug("no host for '%s' (driver '%s')", syspath, driver);
94746d4c572STomoki Sekiyama             goto cleanup;
94846d4c572STomoki Sekiyama         }
94946d4c572STomoki Sekiyama     } else if (strcmp(driver, "sym53c8xx") == 0) {
95046d4c572STomoki Sekiyama         /* scsi(LSI Logic): target*:0:<unit>:0 */
95146d4c572STomoki Sekiyama         if (!has_tgt) {
95246d4c572STomoki Sekiyama             g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
95346d4c572STomoki Sekiyama             goto cleanup;
95446d4c572STomoki Sekiyama         }
95546d4c572STomoki Sekiyama         disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
95646d4c572STomoki Sekiyama         disk->unit = tgt[1];
95746d4c572STomoki Sekiyama     } else if (strcmp(driver, "virtio-pci") == 0) {
95846d4c572STomoki Sekiyama         if (has_tgt) {
95946d4c572STomoki Sekiyama             /* virtio-scsi: target*:0:0:<unit> */
96046d4c572STomoki Sekiyama             disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
96146d4c572STomoki Sekiyama             disk->unit = tgt[2];
96246d4c572STomoki Sekiyama         } else {
96346d4c572STomoki Sekiyama             /* virtio-blk: 1 disk per 1 device */
96446d4c572STomoki Sekiyama             disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO;
96546d4c572STomoki Sekiyama         }
96646d4c572STomoki Sekiyama     } else if (strcmp(driver, "ahci") == 0) {
96746d4c572STomoki Sekiyama         /* ahci: 1 host per 1 unit */
96846d4c572STomoki Sekiyama         if (!has_host || !has_tgt) {
96946d4c572STomoki Sekiyama             g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
97046d4c572STomoki Sekiyama             goto cleanup;
97146d4c572STomoki Sekiyama         }
97246d4c572STomoki Sekiyama         for (i = 0; i < nhosts; i++) {
97346d4c572STomoki Sekiyama             if (host == hosts[i]) {
97446d4c572STomoki Sekiyama                 disk->unit = i;
97546d4c572STomoki Sekiyama                 disk->bus_type = GUEST_DISK_BUS_TYPE_SATA;
97646d4c572STomoki Sekiyama                 break;
97746d4c572STomoki Sekiyama             }
97846d4c572STomoki Sekiyama         }
97946d4c572STomoki Sekiyama         if (i >= nhosts) {
98046d4c572STomoki Sekiyama             g_debug("no host for '%s' (driver '%s')", syspath, driver);
98146d4c572STomoki Sekiyama             goto cleanup;
98246d4c572STomoki Sekiyama         }
983d48f61c8Szhenwei pi     } else if (strcmp(driver, "nvme") == 0) {
984d48f61c8Szhenwei pi         disk->bus_type = GUEST_DISK_BUS_TYPE_NVME;
98546d4c572STomoki Sekiyama     } else {
98646d4c572STomoki Sekiyama         g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath);
98746d4c572STomoki Sekiyama         goto cleanup;
98846d4c572STomoki Sekiyama     }
98946d4c572STomoki Sekiyama 
990d9fe4f0fSThomas Huth     ret = true;
99146d4c572STomoki Sekiyama 
99246d4c572STomoki Sekiyama cleanup:
99346d4c572STomoki Sekiyama     g_free(driver);
994d9fe4f0fSThomas Huth     return ret;
995d9fe4f0fSThomas Huth }
996d9fe4f0fSThomas Huth 
99723843c12SThomas Huth /*
99823843c12SThomas Huth  * Store disk device info for non-PCI virtio devices (for example s390x
99923843c12SThomas Huth  * channel I/O devices). Returns true if information has been stored, or
100023843c12SThomas Huth  * false for failure.
100123843c12SThomas Huth  */
100223843c12SThomas Huth static bool build_guest_fsinfo_for_nonpci_virtio(char const *syspath,
100323843c12SThomas Huth                                                  GuestDiskAddress *disk,
100423843c12SThomas Huth                                                  Error **errp)
100523843c12SThomas Huth {
100623843c12SThomas Huth     unsigned int tgt[3];
100723843c12SThomas Huth     char *p;
100823843c12SThomas Huth 
100923843c12SThomas Huth     if (!strstr(syspath, "/virtio") || !strstr(syspath, "/block")) {
101023843c12SThomas Huth         g_debug("Unsupported virtio device '%s'", syspath);
101123843c12SThomas Huth         return false;
101223843c12SThomas Huth     }
101323843c12SThomas Huth 
101423843c12SThomas Huth     p = strstr(syspath, "/target");
101523843c12SThomas Huth     if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u",
101623843c12SThomas Huth                     &tgt[0], &tgt[1], &tgt[2]) == 3) {
101723843c12SThomas Huth         /* virtio-scsi: target*:0:<target>:<unit> */
101823843c12SThomas Huth         disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
101923843c12SThomas Huth         disk->bus = tgt[0];
102023843c12SThomas Huth         disk->target = tgt[1];
102123843c12SThomas Huth         disk->unit = tgt[2];
102223843c12SThomas Huth     } else {
102323843c12SThomas Huth         /* virtio-blk: 1 disk per 1 device */
102423843c12SThomas Huth         disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO;
102523843c12SThomas Huth     }
102623843c12SThomas Huth 
102723843c12SThomas Huth     return true;
102823843c12SThomas Huth }
102923843c12SThomas Huth 
10305b723a5dSThomas Huth /*
10315b723a5dSThomas Huth  * Store disk device info for CCW devices (s390x channel I/O devices).
10325b723a5dSThomas Huth  * Returns true if information has been stored, or false for failure.
10335b723a5dSThomas Huth  */
10345b723a5dSThomas Huth static bool build_guest_fsinfo_for_ccw_dev(char const *syspath,
10355b723a5dSThomas Huth                                            GuestDiskAddress *disk,
10365b723a5dSThomas Huth                                            Error **errp)
10375b723a5dSThomas Huth {
10385b723a5dSThomas Huth     unsigned int cssid, ssid, subchno, devno;
10395b723a5dSThomas Huth     char *p;
10405b723a5dSThomas Huth 
10415b723a5dSThomas Huth     p = strstr(syspath, "/devices/css");
10425b723a5dSThomas Huth     if (!p || sscanf(p + 12, "%*x/%x.%x.%x/%*x.%*x.%x/",
10435b723a5dSThomas Huth                      &cssid, &ssid, &subchno, &devno) < 4) {
10445b723a5dSThomas Huth         g_debug("could not parse ccw device sysfs path: %s", syspath);
10455b723a5dSThomas Huth         return false;
10465b723a5dSThomas Huth     }
10475b723a5dSThomas Huth 
10485b723a5dSThomas Huth     disk->has_ccw_address = true;
10495b723a5dSThomas Huth     disk->ccw_address = g_new0(GuestCCWAddress, 1);
10505b723a5dSThomas Huth     disk->ccw_address->cssid = cssid;
10515b723a5dSThomas Huth     disk->ccw_address->ssid = ssid;
10525b723a5dSThomas Huth     disk->ccw_address->subchno = subchno;
10535b723a5dSThomas Huth     disk->ccw_address->devno = devno;
10545b723a5dSThomas Huth 
10555b723a5dSThomas Huth     if (strstr(p, "/virtio")) {
10565b723a5dSThomas Huth         build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp);
10575b723a5dSThomas Huth     }
10585b723a5dSThomas Huth 
10595b723a5dSThomas Huth     return true;
10605b723a5dSThomas Huth }
10615b723a5dSThomas Huth 
1062d9fe4f0fSThomas Huth /* Store disk device info specified by @sysfs into @fs */
1063d9fe4f0fSThomas Huth static void build_guest_fsinfo_for_real_device(char const *syspath,
1064d9fe4f0fSThomas Huth                                                GuestFilesystemInfo *fs,
1065d9fe4f0fSThomas Huth                                                Error **errp)
1066d9fe4f0fSThomas Huth {
1067d9fe4f0fSThomas Huth     GuestDiskAddress *disk;
1068d9fe4f0fSThomas Huth     GuestPCIAddress *pciaddr;
1069d9fe4f0fSThomas Huth     bool has_hwinf;
107043dadc43SThomas Huth #ifdef CONFIG_LIBUDEV
107143dadc43SThomas Huth     struct udev *udev = NULL;
107243dadc43SThomas Huth     struct udev_device *udevice = NULL;
107343dadc43SThomas Huth #endif
1074d9fe4f0fSThomas Huth 
1075d9fe4f0fSThomas Huth     pciaddr = g_new0(GuestPCIAddress, 1);
107643dadc43SThomas Huth     pciaddr->domain = -1;                       /* -1 means field is invalid */
107743dadc43SThomas Huth     pciaddr->bus = -1;
107843dadc43SThomas Huth     pciaddr->slot = -1;
107943dadc43SThomas Huth     pciaddr->function = -1;
1080d9fe4f0fSThomas Huth 
1081d9fe4f0fSThomas Huth     disk = g_new0(GuestDiskAddress, 1);
1082d9fe4f0fSThomas Huth     disk->pci_controller = pciaddr;
108343dadc43SThomas Huth     disk->bus_type = GUEST_DISK_BUS_TYPE_UNKNOWN;
1084d9fe4f0fSThomas Huth 
108543dadc43SThomas Huth #ifdef CONFIG_LIBUDEV
108643dadc43SThomas Huth     udev = udev_new();
108743dadc43SThomas Huth     udevice = udev_device_new_from_syspath(udev, syspath);
108843dadc43SThomas Huth     if (udev == NULL || udevice == NULL) {
108943dadc43SThomas Huth         g_debug("failed to query udev");
109043dadc43SThomas Huth     } else {
109143dadc43SThomas Huth         const char *devnode, *serial;
109243dadc43SThomas Huth         devnode = udev_device_get_devnode(udevice);
109343dadc43SThomas Huth         if (devnode != NULL) {
109443dadc43SThomas Huth             disk->dev = g_strdup(devnode);
109543dadc43SThomas Huth             disk->has_dev = true;
109643dadc43SThomas Huth         }
109743dadc43SThomas Huth         serial = udev_device_get_property_value(udevice, "ID_SERIAL");
109843dadc43SThomas Huth         if (serial != NULL && *serial != 0) {
109943dadc43SThomas Huth             disk->serial = g_strdup(serial);
110043dadc43SThomas Huth             disk->has_serial = true;
110143dadc43SThomas Huth         }
110243dadc43SThomas Huth     }
110343dadc43SThomas Huth 
110443dadc43SThomas Huth     udev_unref(udev);
110543dadc43SThomas Huth     udev_device_unref(udevice);
110643dadc43SThomas Huth #endif
110743dadc43SThomas Huth 
110823843c12SThomas Huth     if (strstr(syspath, "/devices/pci")) {
1109d9fe4f0fSThomas Huth         has_hwinf = build_guest_fsinfo_for_pci_dev(syspath, disk, errp);
11105b723a5dSThomas Huth     } else if (strstr(syspath, "/devices/css")) {
11115b723a5dSThomas Huth         has_hwinf = build_guest_fsinfo_for_ccw_dev(syspath, disk, errp);
111223843c12SThomas Huth     } else if (strstr(syspath, "/virtio")) {
111323843c12SThomas Huth         has_hwinf = build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp);
111423843c12SThomas Huth     } else {
111523843c12SThomas Huth         g_debug("Unsupported device type for '%s'", syspath);
111623843c12SThomas Huth         has_hwinf = false;
111723843c12SThomas Huth     }
1118d9fe4f0fSThomas Huth 
111943dadc43SThomas Huth     if (has_hwinf || disk->has_dev || disk->has_serial) {
112054aa3de7SEric Blake         QAPI_LIST_PREPEND(fs->disk, disk);
1121d9fe4f0fSThomas Huth     } else {
112254aa3de7SEric Blake         qapi_free_GuestDiskAddress(disk);
1123d9fe4f0fSThomas Huth     }
112446d4c572STomoki Sekiyama }
112546d4c572STomoki Sekiyama 
112646d4c572STomoki Sekiyama static void build_guest_fsinfo_for_device(char const *devpath,
112746d4c572STomoki Sekiyama                                           GuestFilesystemInfo *fs,
112846d4c572STomoki Sekiyama                                           Error **errp);
112946d4c572STomoki Sekiyama 
113046d4c572STomoki Sekiyama /* Store a list of slave devices of virtual volume specified by @syspath into
113146d4c572STomoki Sekiyama  * @fs */
113246d4c572STomoki Sekiyama static void build_guest_fsinfo_for_virtual_device(char const *syspath,
113346d4c572STomoki Sekiyama                                                   GuestFilesystemInfo *fs,
113446d4c572STomoki Sekiyama                                                   Error **errp)
113546d4c572STomoki Sekiyama {
1136292743d9SMarkus Armbruster     Error *err = NULL;
113746d4c572STomoki Sekiyama     DIR *dir;
113846d4c572STomoki Sekiyama     char *dirpath;
1139e668d1b8Szhanghailiang     struct dirent *entry;
114046d4c572STomoki Sekiyama 
114146d4c572STomoki Sekiyama     dirpath = g_strdup_printf("%s/slaves", syspath);
114246d4c572STomoki Sekiyama     dir = opendir(dirpath);
114346d4c572STomoki Sekiyama     if (!dir) {
11448251a72fSMichael Roth         if (errno != ENOENT) {
114546d4c572STomoki Sekiyama             error_setg_errno(errp, errno, "opendir(\"%s\")", dirpath);
11468251a72fSMichael Roth         }
114746d4c572STomoki Sekiyama         g_free(dirpath);
114846d4c572STomoki Sekiyama         return;
114946d4c572STomoki Sekiyama     }
115046d4c572STomoki Sekiyama 
115146d4c572STomoki Sekiyama     for (;;) {
1152e668d1b8Szhanghailiang         errno = 0;
1153e668d1b8Szhanghailiang         entry = readdir(dir);
1154e668d1b8Szhanghailiang         if (entry == NULL) {
1155e668d1b8Szhanghailiang             if (errno) {
1156e668d1b8Szhanghailiang                 error_setg_errno(errp, errno, "readdir(\"%s\")", dirpath);
115746d4c572STomoki Sekiyama             }
115846d4c572STomoki Sekiyama             break;
115946d4c572STomoki Sekiyama         }
116046d4c572STomoki Sekiyama 
1161e668d1b8Szhanghailiang         if (entry->d_type == DT_LNK) {
1162e668d1b8Szhanghailiang             char *path;
1163e668d1b8Szhanghailiang 
1164e668d1b8Szhanghailiang             g_debug(" slave device '%s'", entry->d_name);
1165e668d1b8Szhanghailiang             path = g_strdup_printf("%s/slaves/%s", syspath, entry->d_name);
1166292743d9SMarkus Armbruster             build_guest_fsinfo_for_device(path, fs, &err);
1167e668d1b8Szhanghailiang             g_free(path);
116846d4c572STomoki Sekiyama 
1169292743d9SMarkus Armbruster             if (err) {
1170292743d9SMarkus Armbruster                 error_propagate(errp, err);
117146d4c572STomoki Sekiyama                 break;
117246d4c572STomoki Sekiyama             }
117346d4c572STomoki Sekiyama         }
117446d4c572STomoki Sekiyama     }
117546d4c572STomoki Sekiyama 
1176e668d1b8Szhanghailiang     g_free(dirpath);
117746d4c572STomoki Sekiyama     closedir(dir);
117846d4c572STomoki Sekiyama }
117946d4c572STomoki Sekiyama 
1180fed39564STomáš Golembiovský static bool is_disk_virtual(const char *devpath, Error **errp)
1181fed39564STomáš Golembiovský {
1182fed39564STomáš Golembiovský     g_autofree char *syspath = realpath(devpath, NULL);
1183fed39564STomáš Golembiovský 
1184fed39564STomáš Golembiovský     if (!syspath) {
1185fed39564STomáš Golembiovský         error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
1186fed39564STomáš Golembiovský         return false;
1187fed39564STomáš Golembiovský     }
1188fed39564STomáš Golembiovský     return strstr(syspath, "/devices/virtual/block/") != NULL;
1189fed39564STomáš Golembiovský }
1190fed39564STomáš Golembiovský 
119146d4c572STomoki Sekiyama /* Dispatch to functions for virtual/real device */
119246d4c572STomoki Sekiyama static void build_guest_fsinfo_for_device(char const *devpath,
119346d4c572STomoki Sekiyama                                           GuestFilesystemInfo *fs,
119446d4c572STomoki Sekiyama                                           Error **errp)
119546d4c572STomoki Sekiyama {
1196fed39564STomáš Golembiovský     ERRP_GUARD();
1197fed39564STomáš Golembiovský     g_autofree char *syspath = NULL;
1198fed39564STomáš Golembiovský     bool is_virtual = false;
119946d4c572STomoki Sekiyama 
1200fed39564STomáš Golembiovský     syspath = realpath(devpath, NULL);
120146d4c572STomoki Sekiyama     if (!syspath) {
1202bbb0151cSJohn Snow         if (errno != ENOENT) {
120346d4c572STomoki Sekiyama             error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
120446d4c572STomoki Sekiyama             return;
120546d4c572STomoki Sekiyama         }
120646d4c572STomoki Sekiyama 
1207bbb0151cSJohn Snow         /* ENOENT: This devpath may not exist because of container config */
1208bbb0151cSJohn Snow         if (!fs->name) {
1209bbb0151cSJohn Snow             fs->name = g_path_get_basename(devpath);
1210bbb0151cSJohn Snow         }
1211bbb0151cSJohn Snow         return;
1212bbb0151cSJohn Snow     }
1213bbb0151cSJohn Snow 
121446d4c572STomoki Sekiyama     if (!fs->name) {
12153e015d81SJulia Suvorova         fs->name = g_path_get_basename(syspath);
121646d4c572STomoki Sekiyama     }
121746d4c572STomoki Sekiyama 
121846d4c572STomoki Sekiyama     g_debug("  parse sysfs path '%s'", syspath);
1219fed39564STomáš Golembiovský     is_virtual = is_disk_virtual(syspath, errp);
1220fed39564STomáš Golembiovský     if (*errp != NULL) {
1221fed39564STomáš Golembiovský         return;
1222fed39564STomáš Golembiovský     }
1223fed39564STomáš Golembiovský     if (is_virtual) {
122446d4c572STomoki Sekiyama         build_guest_fsinfo_for_virtual_device(syspath, fs, errp);
122546d4c572STomoki Sekiyama     } else {
122646d4c572STomoki Sekiyama         build_guest_fsinfo_for_real_device(syspath, fs, errp);
122746d4c572STomoki Sekiyama     }
122846d4c572STomoki Sekiyama }
122946d4c572STomoki Sekiyama 
1230fed39564STomáš Golembiovský #ifdef CONFIG_LIBUDEV
1231fed39564STomáš Golembiovský 
1232fed39564STomáš Golembiovský /*
1233fed39564STomáš Golembiovský  * Wrapper around build_guest_fsinfo_for_device() for getting just
1234fed39564STomáš Golembiovský  * the disk address.
1235fed39564STomáš Golembiovský  */
1236fed39564STomáš Golembiovský static GuestDiskAddress *get_disk_address(const char *syspath, Error **errp)
1237fed39564STomáš Golembiovský {
1238fed39564STomáš Golembiovský     g_autoptr(GuestFilesystemInfo) fs = NULL;
1239fed39564STomáš Golembiovský 
1240fed39564STomáš Golembiovský     fs = g_new0(GuestFilesystemInfo, 1);
1241fed39564STomáš Golembiovský     build_guest_fsinfo_for_device(syspath, fs, errp);
1242fed39564STomáš Golembiovský     if (fs->disk != NULL) {
1243fed39564STomáš Golembiovský         return g_steal_pointer(&fs->disk->value);
1244fed39564STomáš Golembiovský     }
1245fed39564STomáš Golembiovský     return NULL;
1246fed39564STomáš Golembiovský }
1247fed39564STomáš Golembiovský 
1248fed39564STomáš Golembiovský static char *get_alias_for_syspath(const char *syspath)
1249fed39564STomáš Golembiovský {
1250fed39564STomáš Golembiovský     struct udev *udev = NULL;
1251fed39564STomáš Golembiovský     struct udev_device *udevice = NULL;
1252fed39564STomáš Golembiovský     char *ret = NULL;
1253fed39564STomáš Golembiovský 
1254fed39564STomáš Golembiovský     udev = udev_new();
1255fed39564STomáš Golembiovský     if (udev == NULL) {
1256fed39564STomáš Golembiovský         g_debug("failed to query udev");
1257fed39564STomáš Golembiovský         goto out;
1258fed39564STomáš Golembiovský     }
1259fed39564STomáš Golembiovský     udevice = udev_device_new_from_syspath(udev, syspath);
1260fed39564STomáš Golembiovský     if (udevice == NULL) {
1261fed39564STomáš Golembiovský         g_debug("failed to query udev for path: %s", syspath);
1262fed39564STomáš Golembiovský         goto out;
1263fed39564STomáš Golembiovský     } else {
1264fed39564STomáš Golembiovský         const char *alias = udev_device_get_property_value(
1265fed39564STomáš Golembiovský             udevice, "DM_NAME");
1266fed39564STomáš Golembiovský         /*
1267fed39564STomáš Golembiovský          * NULL means there was an error and empty string means there is no
1268fed39564STomáš Golembiovský          * alias. In case of no alias we return NULL instead of empty string.
1269fed39564STomáš Golembiovský          */
1270fed39564STomáš Golembiovský         if (alias == NULL) {
1271fed39564STomáš Golembiovský             g_debug("failed to query udev for device alias for: %s",
1272fed39564STomáš Golembiovský                 syspath);
1273fed39564STomáš Golembiovský         } else if (*alias != 0) {
1274fed39564STomáš Golembiovský             ret = g_strdup(alias);
1275fed39564STomáš Golembiovský         }
1276fed39564STomáš Golembiovský     }
1277fed39564STomáš Golembiovský 
1278fed39564STomáš Golembiovský out:
1279fed39564STomáš Golembiovský     udev_unref(udev);
1280fed39564STomáš Golembiovský     udev_device_unref(udevice);
1281fed39564STomáš Golembiovský     return ret;
1282fed39564STomáš Golembiovský }
1283fed39564STomáš Golembiovský 
1284fed39564STomáš Golembiovský static char *get_device_for_syspath(const char *syspath)
1285fed39564STomáš Golembiovský {
1286fed39564STomáš Golembiovský     struct udev *udev = NULL;
1287fed39564STomáš Golembiovský     struct udev_device *udevice = NULL;
1288fed39564STomáš Golembiovský     char *ret = NULL;
1289fed39564STomáš Golembiovský 
1290fed39564STomáš Golembiovský     udev = udev_new();
1291fed39564STomáš Golembiovský     if (udev == NULL) {
1292fed39564STomáš Golembiovský         g_debug("failed to query udev");
1293fed39564STomáš Golembiovský         goto out;
1294fed39564STomáš Golembiovský     }
1295fed39564STomáš Golembiovský     udevice = udev_device_new_from_syspath(udev, syspath);
1296fed39564STomáš Golembiovský     if (udevice == NULL) {
1297fed39564STomáš Golembiovský         g_debug("failed to query udev for path: %s", syspath);
1298fed39564STomáš Golembiovský         goto out;
1299fed39564STomáš Golembiovský     } else {
1300fed39564STomáš Golembiovský         ret = g_strdup(udev_device_get_devnode(udevice));
1301fed39564STomáš Golembiovský     }
1302fed39564STomáš Golembiovský 
1303fed39564STomáš Golembiovský out:
1304fed39564STomáš Golembiovský     udev_unref(udev);
1305fed39564STomáš Golembiovský     udev_device_unref(udevice);
1306fed39564STomáš Golembiovský     return ret;
1307fed39564STomáš Golembiovský }
1308fed39564STomáš Golembiovský 
1309fed39564STomáš Golembiovský static void get_disk_deps(const char *disk_dir, GuestDiskInfo *disk)
1310fed39564STomáš Golembiovský {
1311fed39564STomáš Golembiovský     g_autofree char *deps_dir = NULL;
1312fed39564STomáš Golembiovský     const gchar *dep;
1313fed39564STomáš Golembiovský     GDir *dp_deps = NULL;
1314fed39564STomáš Golembiovský 
1315fed39564STomáš Golembiovský     /* List dependent disks */
1316fed39564STomáš Golembiovský     deps_dir = g_strdup_printf("%s/slaves", disk_dir);
1317fed39564STomáš Golembiovský     g_debug("  listing entries in: %s", deps_dir);
1318fed39564STomáš Golembiovský     dp_deps = g_dir_open(deps_dir, 0, NULL);
1319fed39564STomáš Golembiovský     if (dp_deps == NULL) {
1320fed39564STomáš Golembiovský         g_debug("failed to list entries in %s", deps_dir);
1321fed39564STomáš Golembiovský         return;
1322fed39564STomáš Golembiovský     }
1323a8aa94b5SMichael Roth     disk->has_dependencies = true;
1324fed39564STomáš Golembiovský     while ((dep = g_dir_read_name(dp_deps)) != NULL) {
1325fed39564STomáš Golembiovský         g_autofree char *dep_dir = NULL;
1326fed39564STomáš Golembiovský         char *dev_name;
1327fed39564STomáš Golembiovský 
1328fed39564STomáš Golembiovský         /* Add dependent disks */
1329fed39564STomáš Golembiovský         dep_dir = g_strdup_printf("%s/%s", deps_dir, dep);
1330fed39564STomáš Golembiovský         dev_name = get_device_for_syspath(dep_dir);
1331fed39564STomáš Golembiovský         if (dev_name != NULL) {
1332fed39564STomáš Golembiovský             g_debug("  adding dependent device: %s", dev_name);
133354aa3de7SEric Blake             QAPI_LIST_PREPEND(disk->dependencies, dev_name);
1334fed39564STomáš Golembiovský         }
1335fed39564STomáš Golembiovský     }
1336fed39564STomáš Golembiovský     g_dir_close(dp_deps);
1337fed39564STomáš Golembiovský }
1338fed39564STomáš Golembiovský 
1339fed39564STomáš Golembiovský /*
1340fed39564STomáš Golembiovský  * Detect partitions subdirectory, name is "<disk_name><number>" or
1341fed39564STomáš Golembiovský  * "<disk_name>p<number>"
1342fed39564STomáš Golembiovský  *
1343fed39564STomáš Golembiovský  * @disk_name -- last component of /sys path (e.g. sda)
1344fed39564STomáš Golembiovský  * @disk_dir -- sys path of the disk (e.g. /sys/block/sda)
1345fed39564STomáš Golembiovský  * @disk_dev -- device node of the disk (e.g. /dev/sda)
1346fed39564STomáš Golembiovský  */
1347fed39564STomáš Golembiovský static GuestDiskInfoList *get_disk_partitions(
1348fed39564STomáš Golembiovský     GuestDiskInfoList *list,
1349fed39564STomáš Golembiovský     const char *disk_name, const char *disk_dir,
1350fed39564STomáš Golembiovský     const char *disk_dev)
1351fed39564STomáš Golembiovský {
135254aa3de7SEric Blake     GuestDiskInfoList *ret = list;
1353fed39564STomáš Golembiovský     struct dirent *de_disk;
1354fed39564STomáš Golembiovský     DIR *dp_disk = NULL;
1355fed39564STomáš Golembiovský     size_t len = strlen(disk_name);
1356fed39564STomáš Golembiovský 
1357fed39564STomáš Golembiovský     dp_disk = opendir(disk_dir);
1358fed39564STomáš Golembiovský     while ((de_disk = readdir(dp_disk)) != NULL) {
1359fed39564STomáš Golembiovský         g_autofree char *partition_dir = NULL;
1360fed39564STomáš Golembiovský         char *dev_name;
1361fed39564STomáš Golembiovský         GuestDiskInfo *partition;
1362fed39564STomáš Golembiovský 
1363fed39564STomáš Golembiovský         if (!(de_disk->d_type & DT_DIR)) {
1364fed39564STomáš Golembiovský             continue;
1365fed39564STomáš Golembiovský         }
1366fed39564STomáš Golembiovský 
1367fed39564STomáš Golembiovský         if (!(strncmp(disk_name, de_disk->d_name, len) == 0 &&
1368fed39564STomáš Golembiovský             ((*(de_disk->d_name + len) == 'p' &&
1369fed39564STomáš Golembiovský             isdigit(*(de_disk->d_name + len + 1))) ||
1370fed39564STomáš Golembiovský                 isdigit(*(de_disk->d_name + len))))) {
1371fed39564STomáš Golembiovský             continue;
1372fed39564STomáš Golembiovský         }
1373fed39564STomáš Golembiovský 
1374fed39564STomáš Golembiovský         partition_dir = g_strdup_printf("%s/%s",
1375fed39564STomáš Golembiovský             disk_dir, de_disk->d_name);
1376fed39564STomáš Golembiovský         dev_name = get_device_for_syspath(partition_dir);
1377fed39564STomáš Golembiovský         if (dev_name == NULL) {
1378fed39564STomáš Golembiovský             g_debug("Failed to get device name for syspath: %s",
1379fed39564STomáš Golembiovský                 disk_dir);
1380fed39564STomáš Golembiovský             continue;
1381fed39564STomáš Golembiovský         }
1382fed39564STomáš Golembiovský         partition = g_new0(GuestDiskInfo, 1);
1383fed39564STomáš Golembiovský         partition->name = dev_name;
1384fed39564STomáš Golembiovský         partition->partition = true;
1385bac9b87bSMarc-André Lureau         partition->has_dependencies = true;
1386fed39564STomáš Golembiovský         /* Add parent disk as dependent for easier tracking of hierarchy */
138754aa3de7SEric Blake         QAPI_LIST_PREPEND(partition->dependencies, g_strdup(disk_dev));
1388fed39564STomáš Golembiovský 
138954aa3de7SEric Blake         QAPI_LIST_PREPEND(ret, partition);
1390fed39564STomáš Golembiovský     }
1391fed39564STomáš Golembiovský     closedir(dp_disk);
1392fed39564STomáš Golembiovský 
1393fed39564STomáš Golembiovský     return ret;
1394fed39564STomáš Golembiovský }
1395fed39564STomáš Golembiovský 
139622668881Szhenwei pi static void get_nvme_smart(GuestDiskInfo *disk)
139722668881Szhenwei pi {
139822668881Szhenwei pi     int fd;
139922668881Szhenwei pi     GuestNVMeSmart *smart;
140022668881Szhenwei pi     NvmeSmartLog log = {0};
140122668881Szhenwei pi     struct nvme_admin_cmd cmd = {
140222668881Szhenwei pi         .opcode = NVME_ADM_CMD_GET_LOG_PAGE,
140322668881Szhenwei pi         .nsid = NVME_NSID_BROADCAST,
140422668881Szhenwei pi         .addr = (uintptr_t)&log,
140522668881Szhenwei pi         .data_len = sizeof(log),
140622668881Szhenwei pi         .cdw10 = NVME_LOG_SMART_INFO | (1 << 15) /* RAE bit */
140722668881Szhenwei pi                  | (((sizeof(log) >> 2) - 1) << 16)
140822668881Szhenwei pi     };
140922668881Szhenwei pi 
1410b9947c9cSMarc-André Lureau     fd = qga_open_cloexec(disk->name, O_RDONLY, 0);
141122668881Szhenwei pi     if (fd == -1) {
141222668881Szhenwei pi         g_debug("Failed to open device: %s: %s", disk->name, g_strerror(errno));
141322668881Szhenwei pi         return;
141422668881Szhenwei pi     }
141522668881Szhenwei pi 
141622668881Szhenwei pi     if (ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd)) {
141722668881Szhenwei pi         g_debug("Failed to get smart: %s: %s", disk->name, g_strerror(errno));
141822668881Szhenwei pi         close(fd);
141922668881Szhenwei pi         return;
142022668881Szhenwei pi     }
142122668881Szhenwei pi 
142222668881Szhenwei pi     disk->has_smart = true;
142322668881Szhenwei pi     disk->smart = g_new0(GuestDiskSmart, 1);
142422668881Szhenwei pi     disk->smart->type = GUEST_DISK_BUS_TYPE_NVME;
142522668881Szhenwei pi 
142622668881Szhenwei pi     smart = &disk->smart->u.nvme;
142722668881Szhenwei pi     smart->critical_warning = log.critical_warning;
142822668881Szhenwei pi     smart->temperature = lduw_le_p(&log.temperature); /* unaligned field */
142922668881Szhenwei pi     smart->available_spare = log.available_spare;
143022668881Szhenwei pi     smart->available_spare_threshold = log.available_spare_threshold;
143122668881Szhenwei pi     smart->percentage_used = log.percentage_used;
143222668881Szhenwei pi     smart->data_units_read_lo = le64_to_cpu(log.data_units_read[0]);
143322668881Szhenwei pi     smart->data_units_read_hi = le64_to_cpu(log.data_units_read[1]);
143422668881Szhenwei pi     smart->data_units_written_lo = le64_to_cpu(log.data_units_written[0]);
143522668881Szhenwei pi     smart->data_units_written_hi = le64_to_cpu(log.data_units_written[1]);
143622668881Szhenwei pi     smart->host_read_commands_lo = le64_to_cpu(log.host_read_commands[0]);
143722668881Szhenwei pi     smart->host_read_commands_hi = le64_to_cpu(log.host_read_commands[1]);
143822668881Szhenwei pi     smart->host_write_commands_lo = le64_to_cpu(log.host_write_commands[0]);
143922668881Szhenwei pi     smart->host_write_commands_hi = le64_to_cpu(log.host_write_commands[1]);
144022668881Szhenwei pi     smart->controller_busy_time_lo = le64_to_cpu(log.controller_busy_time[0]);
144122668881Szhenwei pi     smart->controller_busy_time_hi = le64_to_cpu(log.controller_busy_time[1]);
144222668881Szhenwei pi     smart->power_cycles_lo = le64_to_cpu(log.power_cycles[0]);
144322668881Szhenwei pi     smart->power_cycles_hi = le64_to_cpu(log.power_cycles[1]);
144422668881Szhenwei pi     smart->power_on_hours_lo = le64_to_cpu(log.power_on_hours[0]);
144522668881Szhenwei pi     smart->power_on_hours_hi = le64_to_cpu(log.power_on_hours[1]);
144622668881Szhenwei pi     smart->unsafe_shutdowns_lo = le64_to_cpu(log.unsafe_shutdowns[0]);
144722668881Szhenwei pi     smart->unsafe_shutdowns_hi = le64_to_cpu(log.unsafe_shutdowns[1]);
144822668881Szhenwei pi     smart->media_errors_lo = le64_to_cpu(log.media_errors[0]);
144922668881Szhenwei pi     smart->media_errors_hi = le64_to_cpu(log.media_errors[1]);
145022668881Szhenwei pi     smart->number_of_error_log_entries_lo =
145122668881Szhenwei pi         le64_to_cpu(log.number_of_error_log_entries[0]);
145222668881Szhenwei pi     smart->number_of_error_log_entries_hi =
145322668881Szhenwei pi         le64_to_cpu(log.number_of_error_log_entries[1]);
145422668881Szhenwei pi 
145522668881Szhenwei pi     close(fd);
145622668881Szhenwei pi }
145722668881Szhenwei pi 
145822668881Szhenwei pi static void get_disk_smart(GuestDiskInfo *disk)
145922668881Szhenwei pi {
146022668881Szhenwei pi     if (disk->has_address
146122668881Szhenwei pi         && (disk->address->bus_type == GUEST_DISK_BUS_TYPE_NVME)) {
146222668881Szhenwei pi         get_nvme_smart(disk);
146322668881Szhenwei pi     }
146422668881Szhenwei pi }
146522668881Szhenwei pi 
1466fed39564STomáš Golembiovský GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
1467fed39564STomáš Golembiovský {
146854aa3de7SEric Blake     GuestDiskInfoList *ret = NULL;
1469fed39564STomáš Golembiovský     GuestDiskInfo *disk;
1470fed39564STomáš Golembiovský     DIR *dp = NULL;
1471fed39564STomáš Golembiovský     struct dirent *de = NULL;
1472fed39564STomáš Golembiovský 
1473fed39564STomáš Golembiovský     g_debug("listing /sys/block directory");
1474fed39564STomáš Golembiovský     dp = opendir("/sys/block");
1475fed39564STomáš Golembiovský     if (dp == NULL) {
1476fed39564STomáš Golembiovský         error_setg_errno(errp, errno, "Can't open directory \"/sys/block\"");
1477fed39564STomáš Golembiovský         return NULL;
1478fed39564STomáš Golembiovský     }
1479fed39564STomáš Golembiovský     while ((de = readdir(dp)) != NULL) {
1480fed39564STomáš Golembiovský         g_autofree char *disk_dir = NULL, *line = NULL,
1481fed39564STomáš Golembiovský             *size_path = NULL;
1482fed39564STomáš Golembiovský         char *dev_name;
1483fed39564STomáš Golembiovský         Error *local_err = NULL;
1484fed39564STomáš Golembiovský         if (de->d_type != DT_LNK) {
1485fed39564STomáš Golembiovský             g_debug("  skipping entry: %s", de->d_name);
1486fed39564STomáš Golembiovský             continue;
1487fed39564STomáš Golembiovský         }
1488fed39564STomáš Golembiovský 
1489fed39564STomáš Golembiovský         /* Check size and skip zero-sized disks */
1490fed39564STomáš Golembiovský         g_debug("  checking disk size");
1491fed39564STomáš Golembiovský         size_path = g_strdup_printf("/sys/block/%s/size", de->d_name);
1492fed39564STomáš Golembiovský         if (!g_file_get_contents(size_path, &line, NULL, NULL)) {
1493fed39564STomáš Golembiovský             g_debug("  failed to read disk size");
1494fed39564STomáš Golembiovský             continue;
1495fed39564STomáš Golembiovský         }
1496fed39564STomáš Golembiovský         if (g_strcmp0(line, "0\n") == 0) {
1497fed39564STomáš Golembiovský             g_debug("  skipping zero-sized disk");
1498fed39564STomáš Golembiovský             continue;
1499fed39564STomáš Golembiovský         }
1500fed39564STomáš Golembiovský 
1501fed39564STomáš Golembiovský         g_debug("  adding %s", de->d_name);
1502fed39564STomáš Golembiovský         disk_dir = g_strdup_printf("/sys/block/%s", de->d_name);
1503fed39564STomáš Golembiovský         dev_name = get_device_for_syspath(disk_dir);
1504fed39564STomáš Golembiovský         if (dev_name == NULL) {
1505fed39564STomáš Golembiovský             g_debug("Failed to get device name for syspath: %s",
1506fed39564STomáš Golembiovský                 disk_dir);
1507fed39564STomáš Golembiovský             continue;
1508fed39564STomáš Golembiovský         }
1509fed39564STomáš Golembiovský         disk = g_new0(GuestDiskInfo, 1);
1510fed39564STomáš Golembiovský         disk->name = dev_name;
1511fed39564STomáš Golembiovský         disk->partition = false;
1512fed39564STomáš Golembiovský         disk->alias = get_alias_for_syspath(disk_dir);
1513fed39564STomáš Golembiovský         disk->has_alias = (disk->alias != NULL);
151454aa3de7SEric Blake         QAPI_LIST_PREPEND(ret, disk);
1515fed39564STomáš Golembiovský 
1516fed39564STomáš Golembiovský         /* Get address for non-virtual devices */
1517fed39564STomáš Golembiovský         bool is_virtual = is_disk_virtual(disk_dir, &local_err);
1518fed39564STomáš Golembiovský         if (local_err != NULL) {
1519fed39564STomáš Golembiovský             g_debug("  failed to check disk path, ignoring error: %s",
1520fed39564STomáš Golembiovský                 error_get_pretty(local_err));
1521fed39564STomáš Golembiovský             error_free(local_err);
1522fed39564STomáš Golembiovský             local_err = NULL;
1523fed39564STomáš Golembiovský             /* Don't try to get the address */
1524fed39564STomáš Golembiovský             is_virtual = true;
1525fed39564STomáš Golembiovský         }
1526fed39564STomáš Golembiovský         if (!is_virtual) {
1527fed39564STomáš Golembiovský             disk->address = get_disk_address(disk_dir, &local_err);
1528fed39564STomáš Golembiovský             if (local_err != NULL) {
1529fed39564STomáš Golembiovský                 g_debug("  failed to get device info, ignoring error: %s",
1530fed39564STomáš Golembiovský                     error_get_pretty(local_err));
1531fed39564STomáš Golembiovský                 error_free(local_err);
1532fed39564STomáš Golembiovský                 local_err = NULL;
1533fed39564STomáš Golembiovský             } else if (disk->address != NULL) {
1534fed39564STomáš Golembiovský                 disk->has_address = true;
1535fed39564STomáš Golembiovský             }
1536fed39564STomáš Golembiovský         }
1537fed39564STomáš Golembiovský 
1538fed39564STomáš Golembiovský         get_disk_deps(disk_dir, disk);
153922668881Szhenwei pi         get_disk_smart(disk);
1540fed39564STomáš Golembiovský         ret = get_disk_partitions(ret, de->d_name, disk_dir, dev_name);
1541fed39564STomáš Golembiovský     }
1542b1b9ab1cSMichael Roth 
1543b1b9ab1cSMichael Roth     closedir(dp);
1544b1b9ab1cSMichael Roth 
1545fed39564STomáš Golembiovský     return ret;
1546fed39564STomáš Golembiovský }
1547fed39564STomáš Golembiovský 
1548fed39564STomáš Golembiovský #else
1549fed39564STomáš Golembiovský 
1550fed39564STomáš Golembiovský GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
1551fed39564STomáš Golembiovský {
1552fed39564STomáš Golembiovský     error_setg(errp, QERR_UNSUPPORTED);
1553fed39564STomáš Golembiovský     return NULL;
1554fed39564STomáš Golembiovský }
1555fed39564STomáš Golembiovský 
1556fed39564STomáš Golembiovský #endif
1557fed39564STomáš Golembiovský 
155846d4c572STomoki Sekiyama /* Return a list of the disk device(s)' info which @mount lies on */
155946d4c572STomoki Sekiyama static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount,
156046d4c572STomoki Sekiyama                                                Error **errp)
156146d4c572STomoki Sekiyama {
156246d4c572STomoki Sekiyama     GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs));
156325b5ff1aSChen Hanxiao     struct statvfs buf;
156425b5ff1aSChen Hanxiao     unsigned long used, nonroot_total, fr_size;
156546d4c572STomoki Sekiyama     char *devpath = g_strdup_printf("/sys/dev/block/%u:%u",
156646d4c572STomoki Sekiyama                                     mount->devmajor, mount->devminor);
156746d4c572STomoki Sekiyama 
156846d4c572STomoki Sekiyama     fs->mountpoint = g_strdup(mount->dirname);
156946d4c572STomoki Sekiyama     fs->type = g_strdup(mount->devtype);
157046d4c572STomoki Sekiyama     build_guest_fsinfo_for_device(devpath, fs, errp);
157146d4c572STomoki Sekiyama 
157225b5ff1aSChen Hanxiao     if (statvfs(fs->mountpoint, &buf) == 0) {
157325b5ff1aSChen Hanxiao         fr_size = buf.f_frsize;
157425b5ff1aSChen Hanxiao         used = buf.f_blocks - buf.f_bfree;
157525b5ff1aSChen Hanxiao         nonroot_total = used + buf.f_bavail;
157625b5ff1aSChen Hanxiao         fs->used_bytes = used * fr_size;
157725b5ff1aSChen Hanxiao         fs->total_bytes = nonroot_total * fr_size;
157825b5ff1aSChen Hanxiao 
157925b5ff1aSChen Hanxiao         fs->has_total_bytes = true;
158025b5ff1aSChen Hanxiao         fs->has_used_bytes = true;
158125b5ff1aSChen Hanxiao     }
158225b5ff1aSChen Hanxiao 
158346d4c572STomoki Sekiyama     g_free(devpath);
158425b5ff1aSChen Hanxiao 
158546d4c572STomoki Sekiyama     return fs;
158646d4c572STomoki Sekiyama }
158746d4c572STomoki Sekiyama 
158846d4c572STomoki Sekiyama GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
158946d4c572STomoki Sekiyama {
159046d4c572STomoki Sekiyama     FsMountList mounts;
159146d4c572STomoki Sekiyama     struct FsMount *mount;
159254aa3de7SEric Blake     GuestFilesystemInfoList *ret = NULL;
159346d4c572STomoki Sekiyama     Error *local_err = NULL;
159446d4c572STomoki Sekiyama 
159546d4c572STomoki Sekiyama     QTAILQ_INIT(&mounts);
1596561bfcb6SMarc-André Lureau     if (!build_fs_mount_list(&mounts, &local_err)) {
159746d4c572STomoki Sekiyama         error_propagate(errp, local_err);
159846d4c572STomoki Sekiyama         return NULL;
159946d4c572STomoki Sekiyama     }
160046d4c572STomoki Sekiyama 
160146d4c572STomoki Sekiyama     QTAILQ_FOREACH(mount, &mounts, next) {
160246d4c572STomoki Sekiyama         g_debug("Building guest fsinfo for '%s'", mount->dirname);
160346d4c572STomoki Sekiyama 
160454aa3de7SEric Blake         QAPI_LIST_PREPEND(ret, build_guest_fsinfo(mount, &local_err));
160546d4c572STomoki Sekiyama         if (local_err) {
160646d4c572STomoki Sekiyama             error_propagate(errp, local_err);
160746d4c572STomoki Sekiyama             qapi_free_GuestFilesystemInfoList(ret);
160846d4c572STomoki Sekiyama             ret = NULL;
160946d4c572STomoki Sekiyama             break;
161046d4c572STomoki Sekiyama         }
161146d4c572STomoki Sekiyama     }
161246d4c572STomoki Sekiyama 
161346d4c572STomoki Sekiyama     free_fs_mount_list(&mounts);
161446d4c572STomoki Sekiyama     return ret;
161546d4c572STomoki Sekiyama }
1616e72c3f2eSMichael Roth #endif /* CONFIG_FSFREEZE */
1617c216e5adSMichael Roth 
1618eab5fd59SPaolo Bonzini #if defined(CONFIG_FSTRIM)
1619eab5fd59SPaolo Bonzini /*
1620eab5fd59SPaolo Bonzini  * Walk list of mounted file systems in the guest, and trim them.
1621eab5fd59SPaolo Bonzini  */
1622e82855d9SJustin Ossevoort GuestFilesystemTrimResponse *
1623e82855d9SJustin Ossevoort qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
1624eab5fd59SPaolo Bonzini {
1625e82855d9SJustin Ossevoort     GuestFilesystemTrimResponse *response;
1626e82855d9SJustin Ossevoort     GuestFilesystemTrimResult *result;
1627eab5fd59SPaolo Bonzini     int ret = 0;
1628eab5fd59SPaolo Bonzini     FsMountList mounts;
1629eab5fd59SPaolo Bonzini     struct FsMount *mount;
1630eab5fd59SPaolo Bonzini     int fd;
163173a652a1SJustin Ossevoort     struct fstrim_range r;
1632eab5fd59SPaolo Bonzini 
1633eab5fd59SPaolo Bonzini     slog("guest-fstrim called");
1634eab5fd59SPaolo Bonzini 
1635eab5fd59SPaolo Bonzini     QTAILQ_INIT(&mounts);
1636561bfcb6SMarc-André Lureau     if (!build_fs_mount_list(&mounts, errp)) {
1637e82855d9SJustin Ossevoort         return NULL;
1638eab5fd59SPaolo Bonzini     }
1639eab5fd59SPaolo Bonzini 
1640e82855d9SJustin Ossevoort     response = g_malloc0(sizeof(*response));
1641e82855d9SJustin Ossevoort 
1642eab5fd59SPaolo Bonzini     QTAILQ_FOREACH(mount, &mounts, next) {
1643e82855d9SJustin Ossevoort         result = g_malloc0(sizeof(*result));
1644e82855d9SJustin Ossevoort         result->path = g_strdup(mount->dirname);
1645e82855d9SJustin Ossevoort 
164654aa3de7SEric Blake         QAPI_LIST_PREPEND(response->paths, result);
1647e82855d9SJustin Ossevoort 
1648b9947c9cSMarc-André Lureau         fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0);
1649eab5fd59SPaolo Bonzini         if (fd == -1) {
1650e82855d9SJustin Ossevoort             result->error = g_strdup_printf("failed to open: %s",
1651e82855d9SJustin Ossevoort                                             strerror(errno));
1652e82855d9SJustin Ossevoort             result->has_error = true;
1653e82855d9SJustin Ossevoort             continue;
1654eab5fd59SPaolo Bonzini         }
1655eab5fd59SPaolo Bonzini 
1656e35916acSMichael Tokarev         /* We try to cull filesystems we know won't work in advance, but other
1657e35916acSMichael Tokarev          * filesystems may not implement fstrim for less obvious reasons.
1658e35916acSMichael Tokarev          * These will report EOPNOTSUPP; while in some other cases ENOTTY
1659e35916acSMichael Tokarev          * will be reported (e.g. CD-ROMs).
1660e82855d9SJustin Ossevoort          * Any other error means an unexpected error.
1661eab5fd59SPaolo Bonzini          */
166273a652a1SJustin Ossevoort         r.start = 0;
166373a652a1SJustin Ossevoort         r.len = -1;
166473a652a1SJustin Ossevoort         r.minlen = has_minimum ? minimum : 0;
1665eab5fd59SPaolo Bonzini         ret = ioctl(fd, FITRIM, &r);
1666eab5fd59SPaolo Bonzini         if (ret == -1) {
1667e82855d9SJustin Ossevoort             result->has_error = true;
1668e82855d9SJustin Ossevoort             if (errno == ENOTTY || errno == EOPNOTSUPP) {
1669e82855d9SJustin Ossevoort                 result->error = g_strdup("trim not supported");
1670e82855d9SJustin Ossevoort             } else {
1671e82855d9SJustin Ossevoort                 result->error = g_strdup_printf("failed to trim: %s",
1672e82855d9SJustin Ossevoort                                                 strerror(errno));
1673e82855d9SJustin Ossevoort             }
1674eab5fd59SPaolo Bonzini             close(fd);
1675e82855d9SJustin Ossevoort             continue;
1676eab5fd59SPaolo Bonzini         }
1677e82855d9SJustin Ossevoort 
1678e82855d9SJustin Ossevoort         result->has_minimum = true;
1679e82855d9SJustin Ossevoort         result->minimum = r.minlen;
1680e82855d9SJustin Ossevoort         result->has_trimmed = true;
1681e82855d9SJustin Ossevoort         result->trimmed = r.len;
1682eab5fd59SPaolo Bonzini         close(fd);
1683eab5fd59SPaolo Bonzini     }
1684eab5fd59SPaolo Bonzini 
1685eab5fd59SPaolo Bonzini     free_fs_mount_list(&mounts);
1686e82855d9SJustin Ossevoort     return response;
1687eab5fd59SPaolo Bonzini }
1688eab5fd59SPaolo Bonzini #endif /* CONFIG_FSTRIM */
1689eab5fd59SPaolo Bonzini 
1690eab5fd59SPaolo Bonzini 
169111d0f125SLuiz Capitulino #define LINUX_SYS_STATE_FILE "/sys/power/state"
169211d0f125SLuiz Capitulino #define SUSPEND_SUPPORTED 0
169311d0f125SLuiz Capitulino #define SUSPEND_NOT_SUPPORTED 1
169411d0f125SLuiz Capitulino 
16958b020b5eSDaniel Henrique Barboza typedef enum {
16968b020b5eSDaniel Henrique Barboza     SUSPEND_MODE_DISK = 0,
16978b020b5eSDaniel Henrique Barboza     SUSPEND_MODE_RAM = 1,
16988b020b5eSDaniel Henrique Barboza     SUSPEND_MODE_HYBRID = 2,
16998b020b5eSDaniel Henrique Barboza } SuspendMode;
17008b020b5eSDaniel Henrique Barboza 
17018b020b5eSDaniel Henrique Barboza /*
17028b020b5eSDaniel Henrique Barboza  * Executes a command in a child process using g_spawn_sync,
17038b020b5eSDaniel Henrique Barboza  * returning an int >= 0 representing the exit status of the
17048b020b5eSDaniel Henrique Barboza  * process.
17058b020b5eSDaniel Henrique Barboza  *
17068b020b5eSDaniel Henrique Barboza  * If the program wasn't found in path, returns -1.
17078b020b5eSDaniel Henrique Barboza  *
17088b020b5eSDaniel Henrique Barboza  * If a problem happened when creating the child process,
17098b020b5eSDaniel Henrique Barboza  * returns -1 and errp is set.
17108b020b5eSDaniel Henrique Barboza  */
17118b020b5eSDaniel Henrique Barboza static int run_process_child(const char *command[], Error **errp)
17128b020b5eSDaniel Henrique Barboza {
17138b020b5eSDaniel Henrique Barboza     int exit_status, spawn_flag;
17148b020b5eSDaniel Henrique Barboza     GError *g_err = NULL;
17158b020b5eSDaniel Henrique Barboza     bool success;
17168b020b5eSDaniel Henrique Barboza 
17178b020b5eSDaniel Henrique Barboza     spawn_flag = G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL |
17188b020b5eSDaniel Henrique Barboza                  G_SPAWN_STDERR_TO_DEV_NULL;
17198b020b5eSDaniel Henrique Barboza 
1720fcc41961SMarc-André Lureau     success =  g_spawn_sync(NULL, (char **)command, NULL, spawn_flag,
17218b020b5eSDaniel Henrique Barboza                             NULL, NULL, NULL, NULL,
17228b020b5eSDaniel Henrique Barboza                             &exit_status, &g_err);
17238b020b5eSDaniel Henrique Barboza 
17248b020b5eSDaniel Henrique Barboza     if (success) {
17258b020b5eSDaniel Henrique Barboza         return WEXITSTATUS(exit_status);
17268b020b5eSDaniel Henrique Barboza     }
17278b020b5eSDaniel Henrique Barboza 
17288b020b5eSDaniel Henrique Barboza     if (g_err && (g_err->code != G_SPAWN_ERROR_NOENT)) {
17298b020b5eSDaniel Henrique Barboza         error_setg(errp, "failed to create child process, error '%s'",
17308b020b5eSDaniel Henrique Barboza                    g_err->message);
17318b020b5eSDaniel Henrique Barboza     }
17328b020b5eSDaniel Henrique Barboza 
17338b020b5eSDaniel Henrique Barboza     g_error_free(g_err);
17348b020b5eSDaniel Henrique Barboza     return -1;
17358b020b5eSDaniel Henrique Barboza }
17368b020b5eSDaniel Henrique Barboza 
1737067927d6SDaniel Henrique Barboza static bool systemd_supports_mode(SuspendMode mode, Error **errp)
1738067927d6SDaniel Henrique Barboza {
1739067927d6SDaniel Henrique Barboza     const char *systemctl_args[3] = {"systemd-hibernate", "systemd-suspend",
1740067927d6SDaniel Henrique Barboza                                      "systemd-hybrid-sleep"};
1741067927d6SDaniel Henrique Barboza     const char *cmd[4] = {"systemctl", "status", systemctl_args[mode], NULL};
1742067927d6SDaniel Henrique Barboza     int status;
1743067927d6SDaniel Henrique Barboza 
1744992861fbSMarkus Armbruster     status = run_process_child(cmd, errp);
1745067927d6SDaniel Henrique Barboza 
1746067927d6SDaniel Henrique Barboza     /*
1747067927d6SDaniel Henrique Barboza      * systemctl status uses LSB return codes so we can expect
1748067927d6SDaniel Henrique Barboza      * status > 0 and be ok. To assert if the guest has support
1749067927d6SDaniel Henrique Barboza      * for the selected suspend mode, status should be < 4. 4 is
1750067927d6SDaniel Henrique Barboza      * the code for unknown service status, the return value when
1751067927d6SDaniel Henrique Barboza      * the service does not exist. A common value is status = 3
1752067927d6SDaniel Henrique Barboza      * (program is not running).
1753067927d6SDaniel Henrique Barboza      */
1754067927d6SDaniel Henrique Barboza     if (status > 0 && status < 4) {
1755067927d6SDaniel Henrique Barboza         return true;
1756067927d6SDaniel Henrique Barboza     }
1757067927d6SDaniel Henrique Barboza 
1758067927d6SDaniel Henrique Barboza     return false;
1759067927d6SDaniel Henrique Barboza }
1760067927d6SDaniel Henrique Barboza 
1761067927d6SDaniel Henrique Barboza static void systemd_suspend(SuspendMode mode, Error **errp)
1762067927d6SDaniel Henrique Barboza {
1763067927d6SDaniel Henrique Barboza     Error *local_err = NULL;
1764067927d6SDaniel Henrique Barboza     const char *systemctl_args[3] = {"hibernate", "suspend", "hybrid-sleep"};
1765067927d6SDaniel Henrique Barboza     const char *cmd[3] = {"systemctl", systemctl_args[mode], NULL};
1766067927d6SDaniel Henrique Barboza     int status;
1767067927d6SDaniel Henrique Barboza 
1768067927d6SDaniel Henrique Barboza     status = run_process_child(cmd, &local_err);
1769067927d6SDaniel Henrique Barboza 
1770067927d6SDaniel Henrique Barboza     if (status == 0) {
1771067927d6SDaniel Henrique Barboza         return;
1772067927d6SDaniel Henrique Barboza     }
1773067927d6SDaniel Henrique Barboza 
1774067927d6SDaniel Henrique Barboza     if ((status == -1) && !local_err) {
1775067927d6SDaniel Henrique Barboza         error_setg(errp, "the helper program 'systemctl %s' was not found",
1776067927d6SDaniel Henrique Barboza                    systemctl_args[mode]);
1777067927d6SDaniel Henrique Barboza         return;
1778067927d6SDaniel Henrique Barboza     }
1779067927d6SDaniel Henrique Barboza 
1780067927d6SDaniel Henrique Barboza     if (local_err) {
1781067927d6SDaniel Henrique Barboza         error_propagate(errp, local_err);
1782067927d6SDaniel Henrique Barboza     } else {
1783067927d6SDaniel Henrique Barboza         error_setg(errp, "the helper program 'systemctl %s' returned an "
1784067927d6SDaniel Henrique Barboza                    "unexpected exit status code (%d)",
1785067927d6SDaniel Henrique Barboza                    systemctl_args[mode], status);
1786067927d6SDaniel Henrique Barboza     }
1787067927d6SDaniel Henrique Barboza }
1788067927d6SDaniel Henrique Barboza 
17898b020b5eSDaniel Henrique Barboza static bool pmutils_supports_mode(SuspendMode mode, Error **errp)
179011d0f125SLuiz Capitulino {
17916b26e837SLuiz Capitulino     Error *local_err = NULL;
17928b020b5eSDaniel Henrique Barboza     const char *pmutils_args[3] = {"--hibernate", "--suspend",
17938b020b5eSDaniel Henrique Barboza                                    "--suspend-hybrid"};
17948b020b5eSDaniel Henrique Barboza     const char *cmd[3] = {"pm-is-supported", pmutils_args[mode], NULL};
1795dc8764f0SLuiz Capitulino     int status;
179611d0f125SLuiz Capitulino 
17978b020b5eSDaniel Henrique Barboza     status = run_process_child(cmd, &local_err);
1798304a0fcbSDaniel Henrique Barboza 
17998b020b5eSDaniel Henrique Barboza     if (status == SUSPEND_SUPPORTED) {
18008b020b5eSDaniel Henrique Barboza         return true;
1801304a0fcbSDaniel Henrique Barboza     }
1802304a0fcbSDaniel Henrique Barboza 
18038b020b5eSDaniel Henrique Barboza     if ((status == -1) && !local_err) {
18048b020b5eSDaniel Henrique Barboza         return false;
1805a5fcf0e3SDaniel Henrique Barboza     }
180611d0f125SLuiz Capitulino 
180784d18f06SMarkus Armbruster     if (local_err) {
180877dbc81bSMarkus Armbruster         error_propagate(errp, local_err);
18098b020b5eSDaniel Henrique Barboza     } else {
181077dbc81bSMarkus Armbruster         error_setg(errp,
18118b020b5eSDaniel Henrique Barboza                    "the helper program '%s' returned an unexpected exit"
18128b020b5eSDaniel Henrique Barboza                    " status code (%d)", "pm-is-supported", status);
1813dc8764f0SLuiz Capitulino     }
181411d0f125SLuiz Capitulino 
18158b020b5eSDaniel Henrique Barboza     return false;
1816a5fcf0e3SDaniel Henrique Barboza }
1817a5fcf0e3SDaniel Henrique Barboza 
18188b020b5eSDaniel Henrique Barboza static void pmutils_suspend(SuspendMode mode, Error **errp)
1819246d76ebSDaniel Henrique Barboza {
1820246d76ebSDaniel Henrique Barboza     Error *local_err = NULL;
18218b020b5eSDaniel Henrique Barboza     const char *pmutils_binaries[3] = {"pm-hibernate", "pm-suspend",
18228b020b5eSDaniel Henrique Barboza                                        "pm-suspend-hybrid"};
18238b020b5eSDaniel Henrique Barboza     const char *cmd[2] = {pmutils_binaries[mode], NULL};
1824246d76ebSDaniel Henrique Barboza     int status;
1825246d76ebSDaniel Henrique Barboza 
18268b020b5eSDaniel Henrique Barboza     status = run_process_child(cmd, &local_err);
1827246d76ebSDaniel Henrique Barboza 
18288b020b5eSDaniel Henrique Barboza     if (status == 0) {
1829246d76ebSDaniel Henrique Barboza         return;
1830246d76ebSDaniel Henrique Barboza     }
1831246d76ebSDaniel Henrique Barboza 
18328b020b5eSDaniel Henrique Barboza     if ((status == -1) && !local_err) {
18338b020b5eSDaniel Henrique Barboza         error_setg(errp, "the helper program '%s' was not found",
18348b020b5eSDaniel Henrique Barboza                    pmutils_binaries[mode]);
1835246d76ebSDaniel Henrique Barboza         return;
1836246d76ebSDaniel Henrique Barboza     }
1837246d76ebSDaniel Henrique Barboza 
1838246d76ebSDaniel Henrique Barboza     if (local_err) {
1839246d76ebSDaniel Henrique Barboza         error_propagate(errp, local_err);
18408b020b5eSDaniel Henrique Barboza     } else {
1841246d76ebSDaniel Henrique Barboza         error_setg(errp,
18428b020b5eSDaniel Henrique Barboza                    "the helper program '%s' returned an unexpected exit"
18438b020b5eSDaniel Henrique Barboza                    " status code (%d)", pmutils_binaries[mode], status);
18448b020b5eSDaniel Henrique Barboza     }
1845246d76ebSDaniel Henrique Barboza }
1846246d76ebSDaniel Henrique Barboza 
18478b020b5eSDaniel Henrique Barboza static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp)
1848a5fcf0e3SDaniel Henrique Barboza {
18498b020b5eSDaniel Henrique Barboza     const char *sysfile_strs[3] = {"disk", "mem", NULL};
18508b020b5eSDaniel Henrique Barboza     const char *sysfile_str = sysfile_strs[mode];
1851a5fcf0e3SDaniel Henrique Barboza     char buf[32]; /* hopefully big enough */
1852a5fcf0e3SDaniel Henrique Barboza     int fd;
1853a5fcf0e3SDaniel Henrique Barboza     ssize_t ret;
1854a5fcf0e3SDaniel Henrique Barboza 
18558b020b5eSDaniel Henrique Barboza     if (!sysfile_str) {
18568b020b5eSDaniel Henrique Barboza         error_setg(errp, "unknown guest suspend mode");
1857a5fcf0e3SDaniel Henrique Barboza         return false;
1858a5fcf0e3SDaniel Henrique Barboza     }
1859a5fcf0e3SDaniel Henrique Barboza 
1860a5fcf0e3SDaniel Henrique Barboza     fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
1861a5fcf0e3SDaniel Henrique Barboza     if (fd < 0) {
1862a5fcf0e3SDaniel Henrique Barboza         return false;
1863a5fcf0e3SDaniel Henrique Barboza     }
1864a5fcf0e3SDaniel Henrique Barboza 
1865a5fcf0e3SDaniel Henrique Barboza     ret = read(fd, buf, sizeof(buf) - 1);
1866d9c745c1SPaolo Bonzini     close(fd);
1867a5fcf0e3SDaniel Henrique Barboza     if (ret <= 0) {
1868a5fcf0e3SDaniel Henrique Barboza         return false;
1869a5fcf0e3SDaniel Henrique Barboza     }
1870a5fcf0e3SDaniel Henrique Barboza     buf[ret] = '\0';
1871a5fcf0e3SDaniel Henrique Barboza 
1872a5fcf0e3SDaniel Henrique Barboza     if (strstr(buf, sysfile_str)) {
1873a5fcf0e3SDaniel Henrique Barboza         return true;
1874a5fcf0e3SDaniel Henrique Barboza     }
1875a5fcf0e3SDaniel Henrique Barboza     return false;
1876a5fcf0e3SDaniel Henrique Barboza }
1877a5fcf0e3SDaniel Henrique Barboza 
18788b020b5eSDaniel Henrique Barboza static void linux_sys_state_suspend(SuspendMode mode, Error **errp)
1879246d76ebSDaniel Henrique Barboza {
1880246d76ebSDaniel Henrique Barboza     Error *local_err = NULL;
18818b020b5eSDaniel Henrique Barboza     const char *sysfile_strs[3] = {"disk", "mem", NULL};
18828b020b5eSDaniel Henrique Barboza     const char *sysfile_str = sysfile_strs[mode];
1883246d76ebSDaniel Henrique Barboza     pid_t pid;
1884246d76ebSDaniel Henrique Barboza     int status;
1885246d76ebSDaniel Henrique Barboza 
18868b020b5eSDaniel Henrique Barboza     if (!sysfile_str) {
1887246d76ebSDaniel Henrique Barboza         error_setg(errp, "unknown guest suspend mode");
1888246d76ebSDaniel Henrique Barboza         return;
1889246d76ebSDaniel Henrique Barboza     }
1890246d76ebSDaniel Henrique Barboza 
1891246d76ebSDaniel Henrique Barboza     pid = fork();
1892246d76ebSDaniel Henrique Barboza     if (!pid) {
1893246d76ebSDaniel Henrique Barboza         /* child */
1894246d76ebSDaniel Henrique Barboza         int fd;
1895246d76ebSDaniel Henrique Barboza 
1896246d76ebSDaniel Henrique Barboza         setsid();
1897246d76ebSDaniel Henrique Barboza         reopen_fd_to_null(0);
1898246d76ebSDaniel Henrique Barboza         reopen_fd_to_null(1);
1899246d76ebSDaniel Henrique Barboza         reopen_fd_to_null(2);
1900246d76ebSDaniel Henrique Barboza 
1901246d76ebSDaniel Henrique Barboza         fd = open(LINUX_SYS_STATE_FILE, O_WRONLY);
1902246d76ebSDaniel Henrique Barboza         if (fd < 0) {
1903246d76ebSDaniel Henrique Barboza             _exit(EXIT_FAILURE);
1904246d76ebSDaniel Henrique Barboza         }
1905246d76ebSDaniel Henrique Barboza 
1906246d76ebSDaniel Henrique Barboza         if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) {
1907246d76ebSDaniel Henrique Barboza             _exit(EXIT_FAILURE);
1908246d76ebSDaniel Henrique Barboza         }
1909246d76ebSDaniel Henrique Barboza 
1910246d76ebSDaniel Henrique Barboza         _exit(EXIT_SUCCESS);
1911246d76ebSDaniel Henrique Barboza     } else if (pid < 0) {
1912246d76ebSDaniel Henrique Barboza         error_setg_errno(errp, errno, "failed to create child process");
1913246d76ebSDaniel Henrique Barboza         return;
1914246d76ebSDaniel Henrique Barboza     }
1915246d76ebSDaniel Henrique Barboza 
1916246d76ebSDaniel Henrique Barboza     ga_wait_child(pid, &status, &local_err);
1917246d76ebSDaniel Henrique Barboza     if (local_err) {
1918246d76ebSDaniel Henrique Barboza         error_propagate(errp, local_err);
1919246d76ebSDaniel Henrique Barboza         return;
1920246d76ebSDaniel Henrique Barboza     }
1921246d76ebSDaniel Henrique Barboza 
1922246d76ebSDaniel Henrique Barboza     if (WEXITSTATUS(status)) {
1923246d76ebSDaniel Henrique Barboza         error_setg(errp, "child process has failed to suspend");
1924246d76ebSDaniel Henrique Barboza     }
1925246d76ebSDaniel Henrique Barboza 
1926246d76ebSDaniel Henrique Barboza }
1927246d76ebSDaniel Henrique Barboza 
19288b020b5eSDaniel Henrique Barboza static void guest_suspend(SuspendMode mode, Error **errp)
192911d0f125SLuiz Capitulino {
19307b376087SLuiz Capitulino     Error *local_err = NULL;
193173e1d8ebSDaniel Henrique Barboza     bool mode_supported = false;
193211d0f125SLuiz Capitulino 
193373e1d8ebSDaniel Henrique Barboza     if (systemd_supports_mode(mode, &local_err)) {
193473e1d8ebSDaniel Henrique Barboza         mode_supported = true;
1935067927d6SDaniel Henrique Barboza         systemd_suspend(mode, &local_err);
193673e1d8ebSDaniel Henrique Barboza     }
193773e1d8ebSDaniel Henrique Barboza 
1938067927d6SDaniel Henrique Barboza     if (!local_err) {
1939067927d6SDaniel Henrique Barboza         return;
1940067927d6SDaniel Henrique Barboza     }
1941067927d6SDaniel Henrique Barboza 
1942067927d6SDaniel Henrique Barboza     error_free(local_err);
19436a4a3853SVladimir Sementsov-Ogievskiy     local_err = NULL;
1944067927d6SDaniel Henrique Barboza 
194573e1d8ebSDaniel Henrique Barboza     if (pmutils_supports_mode(mode, &local_err)) {
194673e1d8ebSDaniel Henrique Barboza         mode_supported = true;
19478b020b5eSDaniel Henrique Barboza         pmutils_suspend(mode, &local_err);
194873e1d8ebSDaniel Henrique Barboza     }
194973e1d8ebSDaniel Henrique Barboza 
1950246d76ebSDaniel Henrique Barboza     if (!local_err) {
1951304a0fcbSDaniel Henrique Barboza         return;
1952304a0fcbSDaniel Henrique Barboza     }
1953304a0fcbSDaniel Henrique Barboza 
1954246d76ebSDaniel Henrique Barboza     error_free(local_err);
19556a4a3853SVladimir Sementsov-Ogievskiy     local_err = NULL;
195611d0f125SLuiz Capitulino 
195773e1d8ebSDaniel Henrique Barboza     if (linux_sys_state_supports_mode(mode, &local_err)) {
195873e1d8ebSDaniel Henrique Barboza         mode_supported = true;
19598b020b5eSDaniel Henrique Barboza         linux_sys_state_suspend(mode, &local_err);
196073e1d8ebSDaniel Henrique Barboza     }
196173e1d8ebSDaniel Henrique Barboza 
196273e1d8ebSDaniel Henrique Barboza     if (!mode_supported) {
19636a4a3853SVladimir Sementsov-Ogievskiy         error_free(local_err);
196473e1d8ebSDaniel Henrique Barboza         error_setg(errp,
196573e1d8ebSDaniel Henrique Barboza                    "the requested suspend mode is not supported by the guest");
1966b2322003SMarkus Armbruster     } else {
196777dbc81bSMarkus Armbruster         error_propagate(errp, local_err);
19687b376087SLuiz Capitulino     }
196911d0f125SLuiz Capitulino }
197011d0f125SLuiz Capitulino 
197177dbc81bSMarkus Armbruster void qmp_guest_suspend_disk(Error **errp)
197211d0f125SLuiz Capitulino {
1973304a0fcbSDaniel Henrique Barboza     guest_suspend(SUSPEND_MODE_DISK, errp);
197411d0f125SLuiz Capitulino }
197511d0f125SLuiz Capitulino 
197677dbc81bSMarkus Armbruster void qmp_guest_suspend_ram(Error **errp)
1977fbf42210SLuiz Capitulino {
1978304a0fcbSDaniel Henrique Barboza     guest_suspend(SUSPEND_MODE_RAM, errp);
1979fbf42210SLuiz Capitulino }
1980fbf42210SLuiz Capitulino 
198177dbc81bSMarkus Armbruster void qmp_guest_suspend_hybrid(Error **errp)
198295f4f404SLuiz Capitulino {
1983304a0fcbSDaniel Henrique Barboza     guest_suspend(SUSPEND_MODE_HYBRID, errp);
198495f4f404SLuiz Capitulino }
198595f4f404SLuiz Capitulino 
1986d2baff62SLaszlo Ersek /* Transfer online/offline status between @vcpu and the guest system.
1987d2baff62SLaszlo Ersek  *
1988d2baff62SLaszlo Ersek  * On input either @errp or *@errp must be NULL.
1989d2baff62SLaszlo Ersek  *
1990d2baff62SLaszlo Ersek  * In system-to-@vcpu direction, the following @vcpu fields are accessed:
1991d2baff62SLaszlo Ersek  * - R: vcpu->logical_id
1992d2baff62SLaszlo Ersek  * - W: vcpu->online
1993d2baff62SLaszlo Ersek  * - W: vcpu->can_offline
1994d2baff62SLaszlo Ersek  *
1995d2baff62SLaszlo Ersek  * In @vcpu-to-system direction, the following @vcpu fields are accessed:
1996d2baff62SLaszlo Ersek  * - R: vcpu->logical_id
1997d2baff62SLaszlo Ersek  * - R: vcpu->online
1998d2baff62SLaszlo Ersek  *
1999d2baff62SLaszlo Ersek  * Written members remain unmodified on error.
2000d2baff62SLaszlo Ersek  */
2001d2baff62SLaszlo Ersek static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu,
2002b4bf912aSIgor Mammedov                           char *dirpath, Error **errp)
2003d2baff62SLaszlo Ersek {
2004b4bf912aSIgor Mammedov     int fd;
2005b4bf912aSIgor Mammedov     int res;
2006d2baff62SLaszlo Ersek     int dirfd;
2007b4bf912aSIgor Mammedov     static const char fn[] = "online";
2008d2baff62SLaszlo Ersek 
2009d2baff62SLaszlo Ersek     dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
2010d2baff62SLaszlo Ersek     if (dirfd == -1) {
2011d2baff62SLaszlo Ersek         error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
2012b4bf912aSIgor Mammedov         return;
2013b4bf912aSIgor Mammedov     }
2014d2baff62SLaszlo Ersek 
2015d2baff62SLaszlo Ersek     fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR);
2016d2baff62SLaszlo Ersek     if (fd == -1) {
2017d2baff62SLaszlo Ersek         if (errno != ENOENT) {
2018d2baff62SLaszlo Ersek             error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn);
2019d2baff62SLaszlo Ersek         } else if (sys2vcpu) {
2020d2baff62SLaszlo Ersek             vcpu->online = true;
2021d2baff62SLaszlo Ersek             vcpu->can_offline = false;
2022d2baff62SLaszlo Ersek         } else if (!vcpu->online) {
2023d2baff62SLaszlo Ersek             error_setg(errp, "logical processor #%" PRId64 " can't be "
2024d2baff62SLaszlo Ersek                        "offlined", vcpu->logical_id);
2025d2baff62SLaszlo Ersek         } /* otherwise pretend successful re-onlining */
2026d2baff62SLaszlo Ersek     } else {
2027d2baff62SLaszlo Ersek         unsigned char status;
2028d2baff62SLaszlo Ersek 
2029d2baff62SLaszlo Ersek         res = pread(fd, &status, 1, 0);
2030d2baff62SLaszlo Ersek         if (res == -1) {
2031d2baff62SLaszlo Ersek             error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn);
2032d2baff62SLaszlo Ersek         } else if (res == 0) {
2033d2baff62SLaszlo Ersek             error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath,
2034d2baff62SLaszlo Ersek                        fn);
2035d2baff62SLaszlo Ersek         } else if (sys2vcpu) {
2036d2baff62SLaszlo Ersek             vcpu->online = (status != '0');
2037d2baff62SLaszlo Ersek             vcpu->can_offline = true;
2038d2baff62SLaszlo Ersek         } else if (vcpu->online != (status != '0')) {
2039d2baff62SLaszlo Ersek             status = '0' + vcpu->online;
2040d2baff62SLaszlo Ersek             if (pwrite(fd, &status, 1, 0) == -1) {
2041d2baff62SLaszlo Ersek                 error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath,
2042d2baff62SLaszlo Ersek                                  fn);
2043d2baff62SLaszlo Ersek             }
2044d2baff62SLaszlo Ersek         } /* otherwise pretend successful re-(on|off)-lining */
2045d2baff62SLaszlo Ersek 
2046d2baff62SLaszlo Ersek         res = close(fd);
2047d2baff62SLaszlo Ersek         g_assert(res == 0);
2048d2baff62SLaszlo Ersek     }
2049d2baff62SLaszlo Ersek 
2050d2baff62SLaszlo Ersek     res = close(dirfd);
2051d2baff62SLaszlo Ersek     g_assert(res == 0);
2052d2baff62SLaszlo Ersek }
2053d2baff62SLaszlo Ersek 
2054d2baff62SLaszlo Ersek GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
2055d2baff62SLaszlo Ersek {
2056c3033fd3SEric Blake     GuestLogicalProcessorList *head, **tail;
205727e7de3cSLin Ma     const char *cpu_dir = "/sys/devices/system/cpu";
205827e7de3cSLin Ma     const gchar *line;
205927e7de3cSLin Ma     g_autoptr(GDir) cpu_gdir = NULL;
2060d2baff62SLaszlo Ersek     Error *local_err = NULL;
2061d2baff62SLaszlo Ersek 
2062d2baff62SLaszlo Ersek     head = NULL;
2063c3033fd3SEric Blake     tail = &head;
206427e7de3cSLin Ma     cpu_gdir = g_dir_open(cpu_dir, 0, NULL);
2065d2baff62SLaszlo Ersek 
206627e7de3cSLin Ma     if (cpu_gdir == NULL) {
206727e7de3cSLin Ma         error_setg_errno(errp, errno, "failed to list entries: %s", cpu_dir);
206827e7de3cSLin Ma         return NULL;
206927e7de3cSLin Ma     }
207027e7de3cSLin Ma 
207127e7de3cSLin Ma     while (local_err == NULL && (line = g_dir_read_name(cpu_gdir)) != NULL) {
2072d2baff62SLaszlo Ersek         GuestLogicalProcessor *vcpu;
207327e7de3cSLin Ma         int64_t id;
207427e7de3cSLin Ma         if (sscanf(line, "cpu%" PRId64, &id)) {
207527e7de3cSLin Ma             g_autofree char *path = g_strdup_printf("/sys/devices/system/cpu/"
207627e7de3cSLin Ma                                                     "cpu%" PRId64 "/", id);
2077d2baff62SLaszlo Ersek             vcpu = g_malloc0(sizeof *vcpu);
2078b4bf912aSIgor Mammedov             vcpu->logical_id = id;
2079d2baff62SLaszlo Ersek             vcpu->has_can_offline = true; /* lolspeak ftw */
2080b4bf912aSIgor Mammedov             transfer_vcpu(vcpu, true, path, &local_err);
2081c3033fd3SEric Blake             QAPI_LIST_APPEND(tail, vcpu);
2082d2baff62SLaszlo Ersek         }
2083b4bf912aSIgor Mammedov     }
2084d2baff62SLaszlo Ersek 
2085d2baff62SLaszlo Ersek     if (local_err == NULL) {
2086d2baff62SLaszlo Ersek         /* there's no guest with zero VCPUs */
2087d2baff62SLaszlo Ersek         g_assert(head != NULL);
2088d2baff62SLaszlo Ersek         return head;
2089d2baff62SLaszlo Ersek     }
2090d2baff62SLaszlo Ersek 
2091d2baff62SLaszlo Ersek     qapi_free_GuestLogicalProcessorList(head);
2092d2baff62SLaszlo Ersek     error_propagate(errp, local_err);
2093d2baff62SLaszlo Ersek     return NULL;
2094d2baff62SLaszlo Ersek }
2095d2baff62SLaszlo Ersek 
2096cbb65fc2SLaszlo Ersek int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
2097cbb65fc2SLaszlo Ersek {
2098cbb65fc2SLaszlo Ersek     int64_t processed;
2099cbb65fc2SLaszlo Ersek     Error *local_err = NULL;
2100cbb65fc2SLaszlo Ersek 
2101cbb65fc2SLaszlo Ersek     processed = 0;
2102cbb65fc2SLaszlo Ersek     while (vcpus != NULL) {
2103b4bf912aSIgor Mammedov         char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/",
2104b4bf912aSIgor Mammedov                                      vcpus->value->logical_id);
2105b4bf912aSIgor Mammedov 
2106b4bf912aSIgor Mammedov         transfer_vcpu(vcpus->value, false, path, &local_err);
2107b4bf912aSIgor Mammedov         g_free(path);
2108cbb65fc2SLaszlo Ersek         if (local_err != NULL) {
2109cbb65fc2SLaszlo Ersek             break;
2110cbb65fc2SLaszlo Ersek         }
2111cbb65fc2SLaszlo Ersek         ++processed;
2112cbb65fc2SLaszlo Ersek         vcpus = vcpus->next;
2113cbb65fc2SLaszlo Ersek     }
2114cbb65fc2SLaszlo Ersek 
2115cbb65fc2SLaszlo Ersek     if (local_err != NULL) {
2116cbb65fc2SLaszlo Ersek         if (processed == 0) {
2117cbb65fc2SLaszlo Ersek             error_propagate(errp, local_err);
2118cbb65fc2SLaszlo Ersek         } else {
2119cbb65fc2SLaszlo Ersek             error_free(local_err);
2120cbb65fc2SLaszlo Ersek         }
2121cbb65fc2SLaszlo Ersek     }
2122cbb65fc2SLaszlo Ersek 
2123cbb65fc2SLaszlo Ersek     return processed;
2124cbb65fc2SLaszlo Ersek }
2125*4fd0642eSAlexander Ivanov #endif /* __linux__ */
2126cbb65fc2SLaszlo Ersek 
2127*4fd0642eSAlexander Ivanov #if defined(__linux__) || defined(__FreeBSD__)
2128215a2771SDaniel P. Berrange void qmp_guest_set_user_password(const char *username,
2129215a2771SDaniel P. Berrange                                  const char *password,
2130215a2771SDaniel P. Berrange                                  bool crypted,
2131215a2771SDaniel P. Berrange                                  Error **errp)
2132215a2771SDaniel P. Berrange {
2133215a2771SDaniel P. Berrange     Error *local_err = NULL;
2134215a2771SDaniel P. Berrange     char *passwd_path = NULL;
2135215a2771SDaniel P. Berrange     pid_t pid;
2136215a2771SDaniel P. Berrange     int status;
2137215a2771SDaniel P. Berrange     int datafd[2] = { -1, -1 };
2138215a2771SDaniel P. Berrange     char *rawpasswddata = NULL;
2139215a2771SDaniel P. Berrange     size_t rawpasswdlen;
2140215a2771SDaniel P. Berrange     char *chpasswddata = NULL;
2141215a2771SDaniel P. Berrange     size_t chpasswdlen;
2142215a2771SDaniel P. Berrange 
2143920639caSDaniel P. Berrange     rawpasswddata = (char *)qbase64_decode(password, -1, &rawpasswdlen, errp);
2144920639caSDaniel P. Berrange     if (!rawpasswddata) {
2145920639caSDaniel P. Berrange         return;
2146920639caSDaniel P. Berrange     }
2147215a2771SDaniel P. Berrange     rawpasswddata = g_renew(char, rawpasswddata, rawpasswdlen + 1);
2148215a2771SDaniel P. Berrange     rawpasswddata[rawpasswdlen] = '\0';
2149215a2771SDaniel P. Berrange 
2150215a2771SDaniel P. Berrange     if (strchr(rawpasswddata, '\n')) {
2151215a2771SDaniel P. Berrange         error_setg(errp, "forbidden characters in raw password");
2152215a2771SDaniel P. Berrange         goto out;
2153215a2771SDaniel P. Berrange     }
2154215a2771SDaniel P. Berrange 
2155215a2771SDaniel P. Berrange     if (strchr(username, '\n') ||
2156215a2771SDaniel P. Berrange         strchr(username, ':')) {
2157215a2771SDaniel P. Berrange         error_setg(errp, "forbidden characters in username");
2158215a2771SDaniel P. Berrange         goto out;
2159215a2771SDaniel P. Berrange     }
2160215a2771SDaniel P. Berrange 
2161*4fd0642eSAlexander Ivanov #ifdef __FreeBSD__
2162*4fd0642eSAlexander Ivanov     chpasswddata = g_strdup(rawpasswddata);
2163*4fd0642eSAlexander Ivanov     passwd_path = g_find_program_in_path("pw");
2164*4fd0642eSAlexander Ivanov #else
2165215a2771SDaniel P. Berrange     chpasswddata = g_strdup_printf("%s:%s\n", username, rawpasswddata);
2166215a2771SDaniel P. Berrange     passwd_path = g_find_program_in_path("chpasswd");
2167*4fd0642eSAlexander Ivanov #endif
2168*4fd0642eSAlexander Ivanov 
2169*4fd0642eSAlexander Ivanov     chpasswdlen = strlen(chpasswddata);
2170215a2771SDaniel P. Berrange 
2171215a2771SDaniel P. Berrange     if (!passwd_path) {
2172215a2771SDaniel P. Berrange         error_setg(errp, "cannot find 'passwd' program in PATH");
2173215a2771SDaniel P. Berrange         goto out;
2174215a2771SDaniel P. Berrange     }
2175215a2771SDaniel P. Berrange 
2176ed78331dSMarc-André Lureau     if (!g_unix_open_pipe(datafd, FD_CLOEXEC, NULL)) {
2177215a2771SDaniel P. Berrange         error_setg(errp, "cannot create pipe FDs");
2178215a2771SDaniel P. Berrange         goto out;
2179215a2771SDaniel P. Berrange     }
2180215a2771SDaniel P. Berrange 
2181215a2771SDaniel P. Berrange     pid = fork();
2182215a2771SDaniel P. Berrange     if (pid == 0) {
2183215a2771SDaniel P. Berrange         close(datafd[1]);
2184215a2771SDaniel P. Berrange         /* child */
2185215a2771SDaniel P. Berrange         setsid();
2186215a2771SDaniel P. Berrange         dup2(datafd[0], 0);
2187215a2771SDaniel P. Berrange         reopen_fd_to_null(1);
2188215a2771SDaniel P. Berrange         reopen_fd_to_null(2);
2189215a2771SDaniel P. Berrange 
2190*4fd0642eSAlexander Ivanov #ifdef __FreeBSD__
2191*4fd0642eSAlexander Ivanov         const char *h_arg;
2192*4fd0642eSAlexander Ivanov         h_arg = (crypted) ? "-H" : "-h";
2193*4fd0642eSAlexander Ivanov         execl(passwd_path, "pw", "usermod", "-n", username, h_arg, "0", NULL);
2194*4fd0642eSAlexander Ivanov #else
2195215a2771SDaniel P. Berrange         if (crypted) {
2196fcc41961SMarc-André Lureau             execl(passwd_path, "chpasswd", "-e", NULL);
2197215a2771SDaniel P. Berrange         } else {
2198fcc41961SMarc-André Lureau             execl(passwd_path, "chpasswd", NULL);
2199215a2771SDaniel P. Berrange         }
2200*4fd0642eSAlexander Ivanov #endif
2201215a2771SDaniel P. Berrange         _exit(EXIT_FAILURE);
2202215a2771SDaniel P. Berrange     } else if (pid < 0) {
2203215a2771SDaniel P. Berrange         error_setg_errno(errp, errno, "failed to create child process");
2204215a2771SDaniel P. Berrange         goto out;
2205215a2771SDaniel P. Berrange     }
2206215a2771SDaniel P. Berrange     close(datafd[0]);
2207215a2771SDaniel P. Berrange     datafd[0] = -1;
2208215a2771SDaniel P. Berrange 
2209215a2771SDaniel P. Berrange     if (qemu_write_full(datafd[1], chpasswddata, chpasswdlen) != chpasswdlen) {
2210215a2771SDaniel P. Berrange         error_setg_errno(errp, errno, "cannot write new account password");
2211215a2771SDaniel P. Berrange         goto out;
2212215a2771SDaniel P. Berrange     }
2213215a2771SDaniel P. Berrange     close(datafd[1]);
2214215a2771SDaniel P. Berrange     datafd[1] = -1;
2215215a2771SDaniel P. Berrange 
2216215a2771SDaniel P. Berrange     ga_wait_child(pid, &status, &local_err);
2217215a2771SDaniel P. Berrange     if (local_err) {
2218215a2771SDaniel P. Berrange         error_propagate(errp, local_err);
2219215a2771SDaniel P. Berrange         goto out;
2220215a2771SDaniel P. Berrange     }
2221215a2771SDaniel P. Berrange 
2222215a2771SDaniel P. Berrange     if (!WIFEXITED(status)) {
2223215a2771SDaniel P. Berrange         error_setg(errp, "child process has terminated abnormally");
2224215a2771SDaniel P. Berrange         goto out;
2225215a2771SDaniel P. Berrange     }
2226215a2771SDaniel P. Berrange 
2227215a2771SDaniel P. Berrange     if (WEXITSTATUS(status)) {
2228215a2771SDaniel P. Berrange         error_setg(errp, "child process has failed to set user password");
2229215a2771SDaniel P. Berrange         goto out;
2230215a2771SDaniel P. Berrange     }
2231215a2771SDaniel P. Berrange 
2232215a2771SDaniel P. Berrange out:
2233215a2771SDaniel P. Berrange     g_free(chpasswddata);
2234215a2771SDaniel P. Berrange     g_free(rawpasswddata);
2235215a2771SDaniel P. Berrange     g_free(passwd_path);
2236215a2771SDaniel P. Berrange     if (datafd[0] != -1) {
2237215a2771SDaniel P. Berrange         close(datafd[0]);
2238215a2771SDaniel P. Berrange     }
2239215a2771SDaniel P. Berrange     if (datafd[1] != -1) {
2240215a2771SDaniel P. Berrange         close(datafd[1]);
2241215a2771SDaniel P. Berrange     }
2242215a2771SDaniel P. Berrange }
2243*4fd0642eSAlexander Ivanov #else /* __linux__ || __FreeBSD__ */
2244*4fd0642eSAlexander Ivanov void qmp_guest_set_user_password(const char *username,
2245*4fd0642eSAlexander Ivanov                                  const char *password,
2246*4fd0642eSAlexander Ivanov                                  bool crypted,
2247*4fd0642eSAlexander Ivanov                                  Error **errp)
2248*4fd0642eSAlexander Ivanov {
2249*4fd0642eSAlexander Ivanov     error_setg(errp, QERR_UNSUPPORTED);
2250*4fd0642eSAlexander Ivanov }
2251*4fd0642eSAlexander Ivanov #endif /* __linux__ || __FreeBSD__ */
2252215a2771SDaniel P. Berrange 
2253*4fd0642eSAlexander Ivanov #ifdef __linux__
2254bd240fcaSzhanghailiang static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf,
2255bd240fcaSzhanghailiang                                int size, Error **errp)
2256bd240fcaSzhanghailiang {
2257bd240fcaSzhanghailiang     int fd;
2258bd240fcaSzhanghailiang     int res;
2259bd240fcaSzhanghailiang 
2260bd240fcaSzhanghailiang     errno = 0;
2261bd240fcaSzhanghailiang     fd = openat(dirfd, pathname, O_RDONLY);
2262bd240fcaSzhanghailiang     if (fd == -1) {
2263bd240fcaSzhanghailiang         error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
2264bd240fcaSzhanghailiang         return;
2265bd240fcaSzhanghailiang     }
2266bd240fcaSzhanghailiang 
2267bd240fcaSzhanghailiang     res = pread(fd, buf, size, 0);
2268bd240fcaSzhanghailiang     if (res == -1) {
2269bd240fcaSzhanghailiang         error_setg_errno(errp, errno, "pread sysfs file \"%s\"", pathname);
2270bd240fcaSzhanghailiang     } else if (res == 0) {
2271bd240fcaSzhanghailiang         error_setg(errp, "pread sysfs file \"%s\": unexpected EOF", pathname);
2272bd240fcaSzhanghailiang     }
2273bd240fcaSzhanghailiang     close(fd);
2274bd240fcaSzhanghailiang }
2275bd240fcaSzhanghailiang 
2276bd240fcaSzhanghailiang static void ga_write_sysfs_file(int dirfd, const char *pathname,
2277bd240fcaSzhanghailiang                                 const char *buf, int size, Error **errp)
2278bd240fcaSzhanghailiang {
2279bd240fcaSzhanghailiang     int fd;
2280bd240fcaSzhanghailiang 
2281bd240fcaSzhanghailiang     errno = 0;
2282bd240fcaSzhanghailiang     fd = openat(dirfd, pathname, O_WRONLY);
2283bd240fcaSzhanghailiang     if (fd == -1) {
2284bd240fcaSzhanghailiang         error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
2285bd240fcaSzhanghailiang         return;
2286bd240fcaSzhanghailiang     }
2287bd240fcaSzhanghailiang 
2288bd240fcaSzhanghailiang     if (pwrite(fd, buf, size, 0) == -1) {
2289bd240fcaSzhanghailiang         error_setg_errno(errp, errno, "pwrite sysfs file \"%s\"", pathname);
2290bd240fcaSzhanghailiang     }
2291bd240fcaSzhanghailiang 
2292bd240fcaSzhanghailiang     close(fd);
2293bd240fcaSzhanghailiang }
2294bd240fcaSzhanghailiang 
2295bd240fcaSzhanghailiang /* Transfer online/offline status between @mem_blk and the guest system.
2296bd240fcaSzhanghailiang  *
2297bd240fcaSzhanghailiang  * On input either @errp or *@errp must be NULL.
2298bd240fcaSzhanghailiang  *
2299bd240fcaSzhanghailiang  * In system-to-@mem_blk direction, the following @mem_blk fields are accessed:
2300bd240fcaSzhanghailiang  * - R: mem_blk->phys_index
2301bd240fcaSzhanghailiang  * - W: mem_blk->online
2302bd240fcaSzhanghailiang  * - W: mem_blk->can_offline
2303bd240fcaSzhanghailiang  *
2304bd240fcaSzhanghailiang  * In @mem_blk-to-system direction, the following @mem_blk fields are accessed:
2305bd240fcaSzhanghailiang  * - R: mem_blk->phys_index
2306bd240fcaSzhanghailiang  * - R: mem_blk->online
2307bd240fcaSzhanghailiang  *-  R: mem_blk->can_offline
2308bd240fcaSzhanghailiang  * Written members remain unmodified on error.
2309bd240fcaSzhanghailiang  */
2310bd240fcaSzhanghailiang static void transfer_memory_block(GuestMemoryBlock *mem_blk, bool sys2memblk,
2311bd240fcaSzhanghailiang                                   GuestMemoryBlockResponse *result,
2312bd240fcaSzhanghailiang                                   Error **errp)
2313bd240fcaSzhanghailiang {
2314bd240fcaSzhanghailiang     char *dirpath;
2315bd240fcaSzhanghailiang     int dirfd;
2316bd240fcaSzhanghailiang     char *status;
2317bd240fcaSzhanghailiang     Error *local_err = NULL;
2318bd240fcaSzhanghailiang 
2319bd240fcaSzhanghailiang     if (!sys2memblk) {
2320bd240fcaSzhanghailiang         DIR *dp;
2321bd240fcaSzhanghailiang 
2322bd240fcaSzhanghailiang         if (!result) {
2323bd240fcaSzhanghailiang             error_setg(errp, "Internal error, 'result' should not be NULL");
2324bd240fcaSzhanghailiang             return;
2325bd240fcaSzhanghailiang         }
2326bd240fcaSzhanghailiang         errno = 0;
2327bd240fcaSzhanghailiang         dp = opendir("/sys/devices/system/memory/");
2328bd240fcaSzhanghailiang          /* if there is no 'memory' directory in sysfs,
2329bd240fcaSzhanghailiang          * we think this VM does not support online/offline memory block,
2330bd240fcaSzhanghailiang          * any other solution?
2331bd240fcaSzhanghailiang          */
23329879f5acSPhilippe Mathieu-Daudé         if (!dp) {
23339879f5acSPhilippe Mathieu-Daudé             if (errno == ENOENT) {
2334bd240fcaSzhanghailiang                 result->response =
2335bd240fcaSzhanghailiang                     GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
23369879f5acSPhilippe Mathieu-Daudé             }
2337bd240fcaSzhanghailiang             goto out1;
2338bd240fcaSzhanghailiang         }
2339bd240fcaSzhanghailiang         closedir(dp);
2340bd240fcaSzhanghailiang     }
2341bd240fcaSzhanghailiang 
2342bd240fcaSzhanghailiang     dirpath = g_strdup_printf("/sys/devices/system/memory/memory%" PRId64 "/",
2343bd240fcaSzhanghailiang                               mem_blk->phys_index);
2344bd240fcaSzhanghailiang     dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
2345bd240fcaSzhanghailiang     if (dirfd == -1) {
2346bd240fcaSzhanghailiang         if (sys2memblk) {
2347bd240fcaSzhanghailiang             error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
2348bd240fcaSzhanghailiang         } else {
2349bd240fcaSzhanghailiang             if (errno == ENOENT) {
2350bd240fcaSzhanghailiang                 result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_NOT_FOUND;
2351bd240fcaSzhanghailiang             } else {
2352bd240fcaSzhanghailiang                 result->response =
2353bd240fcaSzhanghailiang                     GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
2354bd240fcaSzhanghailiang             }
2355bd240fcaSzhanghailiang         }
2356bd240fcaSzhanghailiang         g_free(dirpath);
2357bd240fcaSzhanghailiang         goto out1;
2358bd240fcaSzhanghailiang     }
2359bd240fcaSzhanghailiang     g_free(dirpath);
2360bd240fcaSzhanghailiang 
2361bd240fcaSzhanghailiang     status = g_malloc0(10);
2362bd240fcaSzhanghailiang     ga_read_sysfs_file(dirfd, "state", status, 10, &local_err);
2363bd240fcaSzhanghailiang     if (local_err) {
2364bd240fcaSzhanghailiang         /* treat with sysfs file that not exist in old kernel */
2365bd240fcaSzhanghailiang         if (errno == ENOENT) {
2366bd240fcaSzhanghailiang             error_free(local_err);
2367bd240fcaSzhanghailiang             if (sys2memblk) {
2368bd240fcaSzhanghailiang                 mem_blk->online = true;
2369bd240fcaSzhanghailiang                 mem_blk->can_offline = false;
2370bd240fcaSzhanghailiang             } else if (!mem_blk->online) {
2371bd240fcaSzhanghailiang                 result->response =
2372bd240fcaSzhanghailiang                     GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
2373bd240fcaSzhanghailiang             }
2374bd240fcaSzhanghailiang         } else {
2375bd240fcaSzhanghailiang             if (sys2memblk) {
2376bd240fcaSzhanghailiang                 error_propagate(errp, local_err);
2377bd240fcaSzhanghailiang             } else {
2378b368123dSMarkus Armbruster                 error_free(local_err);
2379bd240fcaSzhanghailiang                 result->response =
2380bd240fcaSzhanghailiang                     GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
2381bd240fcaSzhanghailiang             }
2382bd240fcaSzhanghailiang         }
2383bd240fcaSzhanghailiang         goto out2;
2384bd240fcaSzhanghailiang     }
2385bd240fcaSzhanghailiang 
2386bd240fcaSzhanghailiang     if (sys2memblk) {
2387bd240fcaSzhanghailiang         char removable = '0';
2388bd240fcaSzhanghailiang 
2389bd240fcaSzhanghailiang         mem_blk->online = (strncmp(status, "online", 6) == 0);
2390bd240fcaSzhanghailiang 
2391bd240fcaSzhanghailiang         ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err);
2392bd240fcaSzhanghailiang         if (local_err) {
239367cc32ebSVeres Lajos             /* if no 'removable' file, it doesn't support offline mem blk */
2394bd240fcaSzhanghailiang             if (errno == ENOENT) {
2395bd240fcaSzhanghailiang                 error_free(local_err);
2396bd240fcaSzhanghailiang                 mem_blk->can_offline = false;
2397bd240fcaSzhanghailiang             } else {
2398bd240fcaSzhanghailiang                 error_propagate(errp, local_err);
2399bd240fcaSzhanghailiang             }
2400bd240fcaSzhanghailiang         } else {
2401bd240fcaSzhanghailiang             mem_blk->can_offline = (removable != '0');
2402bd240fcaSzhanghailiang         }
2403bd240fcaSzhanghailiang     } else {
2404bd240fcaSzhanghailiang         if (mem_blk->online != (strncmp(status, "online", 6) == 0)) {
24057064024dSMarc-André Lureau             const char *new_state = mem_blk->online ? "online" : "offline";
2406bd240fcaSzhanghailiang 
2407bd240fcaSzhanghailiang             ga_write_sysfs_file(dirfd, "state", new_state, strlen(new_state),
2408bd240fcaSzhanghailiang                                 &local_err);
2409bd240fcaSzhanghailiang             if (local_err) {
2410bd240fcaSzhanghailiang                 error_free(local_err);
2411bd240fcaSzhanghailiang                 result->response =
2412bd240fcaSzhanghailiang                     GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
2413bd240fcaSzhanghailiang                 goto out2;
2414bd240fcaSzhanghailiang             }
2415bd240fcaSzhanghailiang 
2416bd240fcaSzhanghailiang             result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_SUCCESS;
2417bd240fcaSzhanghailiang             result->has_error_code = false;
2418bd240fcaSzhanghailiang         } /* otherwise pretend successful re-(on|off)-lining */
2419bd240fcaSzhanghailiang     }
2420bd240fcaSzhanghailiang     g_free(status);
2421bd240fcaSzhanghailiang     close(dirfd);
2422bd240fcaSzhanghailiang     return;
2423bd240fcaSzhanghailiang 
2424bd240fcaSzhanghailiang out2:
2425bd240fcaSzhanghailiang     g_free(status);
2426bd240fcaSzhanghailiang     close(dirfd);
2427bd240fcaSzhanghailiang out1:
2428bd240fcaSzhanghailiang     if (!sys2memblk) {
2429bd240fcaSzhanghailiang         result->has_error_code = true;
2430bd240fcaSzhanghailiang         result->error_code = errno;
2431bd240fcaSzhanghailiang     }
2432bd240fcaSzhanghailiang }
2433bd240fcaSzhanghailiang 
2434a065aaa9Szhanghailiang GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
2435a065aaa9Szhanghailiang {
2436c3033fd3SEric Blake     GuestMemoryBlockList *head, **tail;
2437bd240fcaSzhanghailiang     Error *local_err = NULL;
2438bd240fcaSzhanghailiang     struct dirent *de;
2439bd240fcaSzhanghailiang     DIR *dp;
2440bd240fcaSzhanghailiang 
2441bd240fcaSzhanghailiang     head = NULL;
2442c3033fd3SEric Blake     tail = &head;
2443bd240fcaSzhanghailiang 
2444bd240fcaSzhanghailiang     dp = opendir("/sys/devices/system/memory/");
2445bd240fcaSzhanghailiang     if (!dp) {
2446f693fe6eSMichael Roth         /* it's ok if this happens to be a system that doesn't expose
2447f693fe6eSMichael Roth          * memory blocks via sysfs, but otherwise we should report
2448f693fe6eSMichael Roth          * an error
2449f693fe6eSMichael Roth          */
2450f693fe6eSMichael Roth         if (errno != ENOENT) {
2451bd240fcaSzhanghailiang             error_setg_errno(errp, errno, "Can't open directory"
24529af9e0feSMarkus Armbruster                              "\"/sys/devices/system/memory/\"");
2453f693fe6eSMichael Roth         }
2454bd240fcaSzhanghailiang         return NULL;
2455bd240fcaSzhanghailiang     }
2456bd240fcaSzhanghailiang 
2457bd240fcaSzhanghailiang     /* Note: the phys_index of memory block may be discontinuous,
2458bd240fcaSzhanghailiang      * this is because a memblk is the unit of the Sparse Memory design, which
2459bd240fcaSzhanghailiang      * allows discontinuous memory ranges (ex. NUMA), so here we should
2460bd240fcaSzhanghailiang      * traverse the memory block directory.
2461bd240fcaSzhanghailiang      */
2462bd240fcaSzhanghailiang     while ((de = readdir(dp)) != NULL) {
2463bd240fcaSzhanghailiang         GuestMemoryBlock *mem_blk;
2464bd240fcaSzhanghailiang 
2465bd240fcaSzhanghailiang         if ((strncmp(de->d_name, "memory", 6) != 0) ||
2466bd240fcaSzhanghailiang             !(de->d_type & DT_DIR)) {
2467bd240fcaSzhanghailiang             continue;
2468bd240fcaSzhanghailiang         }
2469bd240fcaSzhanghailiang 
2470bd240fcaSzhanghailiang         mem_blk = g_malloc0(sizeof *mem_blk);
2471bd240fcaSzhanghailiang         /* The d_name is "memoryXXX",  phys_index is block id, same as XXX */
2472bd240fcaSzhanghailiang         mem_blk->phys_index = strtoul(&de->d_name[6], NULL, 10);
2473bd240fcaSzhanghailiang         mem_blk->has_can_offline = true; /* lolspeak ftw */
2474bd240fcaSzhanghailiang         transfer_memory_block(mem_blk, true, NULL, &local_err);
24754155c998SMarkus Armbruster         if (local_err) {
24764155c998SMarkus Armbruster             break;
24774155c998SMarkus Armbruster         }
2478bd240fcaSzhanghailiang 
2479c3033fd3SEric Blake         QAPI_LIST_APPEND(tail, mem_blk);
2480bd240fcaSzhanghailiang     }
2481bd240fcaSzhanghailiang 
2482bd240fcaSzhanghailiang     closedir(dp);
2483bd240fcaSzhanghailiang     if (local_err == NULL) {
2484bd240fcaSzhanghailiang         /* there's no guest with zero memory blocks */
2485bd240fcaSzhanghailiang         if (head == NULL) {
2486bd240fcaSzhanghailiang             error_setg(errp, "guest reported zero memory blocks!");
2487bd240fcaSzhanghailiang         }
2488bd240fcaSzhanghailiang         return head;
2489bd240fcaSzhanghailiang     }
2490bd240fcaSzhanghailiang 
2491bd240fcaSzhanghailiang     qapi_free_GuestMemoryBlockList(head);
2492bd240fcaSzhanghailiang     error_propagate(errp, local_err);
2493a065aaa9Szhanghailiang     return NULL;
2494a065aaa9Szhanghailiang }
2495a065aaa9Szhanghailiang 
2496a065aaa9Szhanghailiang GuestMemoryBlockResponseList *
2497a065aaa9Szhanghailiang qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
2498a065aaa9Szhanghailiang {
2499c3033fd3SEric Blake     GuestMemoryBlockResponseList *head, **tail;
250032ca7927Szhanghailiang     Error *local_err = NULL;
250132ca7927Szhanghailiang 
250232ca7927Szhanghailiang     head = NULL;
2503c3033fd3SEric Blake     tail = &head;
250432ca7927Szhanghailiang 
250532ca7927Szhanghailiang     while (mem_blks != NULL) {
250632ca7927Szhanghailiang         GuestMemoryBlockResponse *result;
250732ca7927Szhanghailiang         GuestMemoryBlock *current_mem_blk = mem_blks->value;
250832ca7927Szhanghailiang 
250932ca7927Szhanghailiang         result = g_malloc0(sizeof(*result));
251032ca7927Szhanghailiang         result->phys_index = current_mem_blk->phys_index;
251132ca7927Szhanghailiang         transfer_memory_block(current_mem_blk, false, result, &local_err);
251232ca7927Szhanghailiang         if (local_err) { /* should never happen */
251332ca7927Szhanghailiang             goto err;
251432ca7927Szhanghailiang         }
251532ca7927Szhanghailiang 
2516c3033fd3SEric Blake         QAPI_LIST_APPEND(tail, result);
251732ca7927Szhanghailiang         mem_blks = mem_blks->next;
251832ca7927Szhanghailiang     }
251932ca7927Szhanghailiang 
252032ca7927Szhanghailiang     return head;
252132ca7927Szhanghailiang err:
252232ca7927Szhanghailiang     qapi_free_GuestMemoryBlockResponseList(head);
252332ca7927Szhanghailiang     error_propagate(errp, local_err);
2524a065aaa9Szhanghailiang     return NULL;
2525a065aaa9Szhanghailiang }
2526a065aaa9Szhanghailiang 
2527a065aaa9Szhanghailiang GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
2528a065aaa9Szhanghailiang {
2529ef82b60bSzhanghailiang     Error *local_err = NULL;
2530ef82b60bSzhanghailiang     char *dirpath;
2531ef82b60bSzhanghailiang     int dirfd;
2532ef82b60bSzhanghailiang     char *buf;
2533ef82b60bSzhanghailiang     GuestMemoryBlockInfo *info;
2534ef82b60bSzhanghailiang 
2535ef82b60bSzhanghailiang     dirpath = g_strdup_printf("/sys/devices/system/memory/");
2536ef82b60bSzhanghailiang     dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
2537ef82b60bSzhanghailiang     if (dirfd == -1) {
2538ef82b60bSzhanghailiang         error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
2539ef82b60bSzhanghailiang         g_free(dirpath);
2540a065aaa9Szhanghailiang         return NULL;
2541a065aaa9Szhanghailiang     }
2542ef82b60bSzhanghailiang     g_free(dirpath);
2543ef82b60bSzhanghailiang 
2544ef82b60bSzhanghailiang     buf = g_malloc0(20);
2545ef82b60bSzhanghailiang     ga_read_sysfs_file(dirfd, "block_size_bytes", buf, 20, &local_err);
25468ce1ee46SShannon Zhao     close(dirfd);
2547ef82b60bSzhanghailiang     if (local_err) {
2548ef82b60bSzhanghailiang         g_free(buf);
2549ef82b60bSzhanghailiang         error_propagate(errp, local_err);
2550ef82b60bSzhanghailiang         return NULL;
2551ef82b60bSzhanghailiang     }
2552ef82b60bSzhanghailiang 
2553ef82b60bSzhanghailiang     info = g_new0(GuestMemoryBlockInfo, 1);
2554ef82b60bSzhanghailiang     info->size = strtol(buf, NULL, 16); /* the unit is bytes */
2555ef82b60bSzhanghailiang 
2556ef82b60bSzhanghailiang     g_free(buf);
2557ef82b60bSzhanghailiang 
2558ef82b60bSzhanghailiang     return info;
2559ef82b60bSzhanghailiang }
2560a065aaa9Szhanghailiang 
25613569664eSluzhipeng #define MAX_NAME_LEN 128
25623569664eSluzhipeng static GuestDiskStatsInfoList *guest_get_diskstats(Error **errp)
25633569664eSluzhipeng {
25643569664eSluzhipeng #ifdef CONFIG_LINUX
25653569664eSluzhipeng     GuestDiskStatsInfoList *head = NULL, **tail = &head;
25663569664eSluzhipeng     const char *diskstats = "/proc/diskstats";
25673569664eSluzhipeng     FILE *fp;
25683569664eSluzhipeng     size_t n;
25693569664eSluzhipeng     char *line = NULL;
25703569664eSluzhipeng 
25713569664eSluzhipeng     fp = fopen(diskstats, "r");
25723569664eSluzhipeng     if (fp  == NULL) {
25733569664eSluzhipeng         error_setg_errno(errp, errno, "open(\"%s\")", diskstats);
25743569664eSluzhipeng         return NULL;
25753569664eSluzhipeng     }
25763569664eSluzhipeng 
25773569664eSluzhipeng     while (getline(&line, &n, fp) != -1) {
25783569664eSluzhipeng         g_autofree GuestDiskStatsInfo *diskstatinfo = NULL;
25793569664eSluzhipeng         g_autofree GuestDiskStats *diskstat = NULL;
25803569664eSluzhipeng         char dev_name[MAX_NAME_LEN];
25813569664eSluzhipeng         unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks, dc_ticks, fl_ticks;
25823569664eSluzhipeng         unsigned long rd_ios, rd_merges_or_rd_sec, rd_ticks_or_wr_sec, wr_ios;
25833569664eSluzhipeng         unsigned long wr_merges, rd_sec_or_wr_ios, wr_sec;
25843569664eSluzhipeng         unsigned long dc_ios, dc_merges, dc_sec, fl_ios;
25853569664eSluzhipeng         unsigned int major, minor;
25863569664eSluzhipeng         int i;
25873569664eSluzhipeng 
25883569664eSluzhipeng         i = sscanf(line, "%u %u %s %lu %lu %lu"
25893569664eSluzhipeng                    "%lu %lu %lu %lu %u %u %u %u"
25903569664eSluzhipeng                    "%lu %lu %lu %u %lu %u",
25913569664eSluzhipeng                    &major, &minor, dev_name,
25923569664eSluzhipeng                    &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios,
25933569664eSluzhipeng                    &rd_ticks_or_wr_sec, &wr_ios, &wr_merges, &wr_sec,
25943569664eSluzhipeng                    &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks,
25953569664eSluzhipeng                    &dc_ios, &dc_merges, &dc_sec, &dc_ticks,
25963569664eSluzhipeng                    &fl_ios, &fl_ticks);
25973569664eSluzhipeng 
25983569664eSluzhipeng         if (i < 7) {
25993569664eSluzhipeng             continue;
26003569664eSluzhipeng         }
26013569664eSluzhipeng 
26023569664eSluzhipeng         diskstatinfo = g_new0(GuestDiskStatsInfo, 1);
26033569664eSluzhipeng         diskstatinfo->name = g_strdup(dev_name);
26043569664eSluzhipeng         diskstatinfo->major = major;
26053569664eSluzhipeng         diskstatinfo->minor = minor;
26063569664eSluzhipeng 
26073569664eSluzhipeng         diskstat = g_new0(GuestDiskStats, 1);
26083569664eSluzhipeng         if (i == 7) {
26093569664eSluzhipeng             diskstat->has_read_ios = true;
26103569664eSluzhipeng             diskstat->read_ios = rd_ios;
26113569664eSluzhipeng             diskstat->has_read_sectors = true;
26123569664eSluzhipeng             diskstat->read_sectors = rd_merges_or_rd_sec;
26133569664eSluzhipeng             diskstat->has_write_ios = true;
26143569664eSluzhipeng             diskstat->write_ios = rd_sec_or_wr_ios;
26153569664eSluzhipeng             diskstat->has_write_sectors = true;
26163569664eSluzhipeng             diskstat->write_sectors = rd_ticks_or_wr_sec;
26173569664eSluzhipeng         }
26183569664eSluzhipeng         if (i >= 14) {
26193569664eSluzhipeng             diskstat->has_read_ios = true;
26203569664eSluzhipeng             diskstat->read_ios = rd_ios;
26213569664eSluzhipeng             diskstat->has_read_sectors = true;
26223569664eSluzhipeng             diskstat->read_sectors = rd_sec_or_wr_ios;
26233569664eSluzhipeng             diskstat->has_read_merges = true;
26243569664eSluzhipeng             diskstat->read_merges = rd_merges_or_rd_sec;
26253569664eSluzhipeng             diskstat->has_read_ticks = true;
26263569664eSluzhipeng             diskstat->read_ticks = rd_ticks_or_wr_sec;
26273569664eSluzhipeng             diskstat->has_write_ios = true;
26283569664eSluzhipeng             diskstat->write_ios = wr_ios;
26293569664eSluzhipeng             diskstat->has_write_sectors = true;
26303569664eSluzhipeng             diskstat->write_sectors = wr_sec;
26313569664eSluzhipeng             diskstat->has_write_merges = true;
26323569664eSluzhipeng             diskstat->write_merges = wr_merges;
26333569664eSluzhipeng             diskstat->has_write_ticks = true;
26343569664eSluzhipeng             diskstat->write_ticks = wr_ticks;
26353569664eSluzhipeng             diskstat->has_ios_pgr = true;
26363569664eSluzhipeng             diskstat->ios_pgr = ios_pgr;
26373569664eSluzhipeng             diskstat->has_total_ticks = true;
26383569664eSluzhipeng             diskstat->total_ticks = tot_ticks;
26393569664eSluzhipeng             diskstat->has_weight_ticks = true;
26403569664eSluzhipeng             diskstat->weight_ticks = rq_ticks;
26413569664eSluzhipeng         }
26423569664eSluzhipeng         if (i >= 18) {
26433569664eSluzhipeng             diskstat->has_discard_ios = true;
26443569664eSluzhipeng             diskstat->discard_ios = dc_ios;
26453569664eSluzhipeng             diskstat->has_discard_merges = true;
26463569664eSluzhipeng             diskstat->discard_merges = dc_merges;
26473569664eSluzhipeng             diskstat->has_discard_sectors = true;
26483569664eSluzhipeng             diskstat->discard_sectors = dc_sec;
26493569664eSluzhipeng             diskstat->has_discard_ticks = true;
26503569664eSluzhipeng             diskstat->discard_ticks = dc_ticks;
26513569664eSluzhipeng         }
26523569664eSluzhipeng         if (i >= 20) {
26533569664eSluzhipeng             diskstat->has_flush_ios = true;
26543569664eSluzhipeng             diskstat->flush_ios = fl_ios;
26553569664eSluzhipeng             diskstat->has_flush_ticks = true;
26563569664eSluzhipeng             diskstat->flush_ticks = fl_ticks;
26573569664eSluzhipeng         }
26583569664eSluzhipeng 
26593569664eSluzhipeng         diskstatinfo->stats = g_steal_pointer(&diskstat);
26603569664eSluzhipeng         QAPI_LIST_APPEND(tail, diskstatinfo);
26613569664eSluzhipeng         diskstatinfo = NULL;
26623569664eSluzhipeng     }
26633569664eSluzhipeng     free(line);
26643569664eSluzhipeng     fclose(fp);
26653569664eSluzhipeng     return head;
26663569664eSluzhipeng #else
26673569664eSluzhipeng     g_debug("disk stats reporting available only for Linux");
26683569664eSluzhipeng     return NULL;
26693569664eSluzhipeng #endif
26703569664eSluzhipeng }
26713569664eSluzhipeng 
26723569664eSluzhipeng GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp)
26733569664eSluzhipeng {
26743569664eSluzhipeng     return guest_get_diskstats(errp);
26753569664eSluzhipeng }
26763569664eSluzhipeng 
26771db8a0b0Szhenwei pi GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
26781db8a0b0Szhenwei pi {
26791db8a0b0Szhenwei pi     GuestCpuStatsList *head = NULL, **tail = &head;
26801db8a0b0Szhenwei pi     const char *cpustats = "/proc/stat";
26811db8a0b0Szhenwei pi     int clk_tck = sysconf(_SC_CLK_TCK);
26821db8a0b0Szhenwei pi     FILE *fp;
26831db8a0b0Szhenwei pi     size_t n;
26841db8a0b0Szhenwei pi     char *line = NULL;
26851db8a0b0Szhenwei pi 
26861db8a0b0Szhenwei pi     fp = fopen(cpustats, "r");
26871db8a0b0Szhenwei pi     if (fp  == NULL) {
26881db8a0b0Szhenwei pi         error_setg_errno(errp, errno, "open(\"%s\")", cpustats);
26891db8a0b0Szhenwei pi         return NULL;
26901db8a0b0Szhenwei pi     }
26911db8a0b0Szhenwei pi 
26921db8a0b0Szhenwei pi     while (getline(&line, &n, fp) != -1) {
26931db8a0b0Szhenwei pi         GuestCpuStats *cpustat = NULL;
26941db8a0b0Szhenwei pi         GuestLinuxCpuStats *linuxcpustat;
26951db8a0b0Szhenwei pi         int i;
26961db8a0b0Szhenwei pi         unsigned long user, system, idle, iowait, irq, softirq, steal, guest;
26971db8a0b0Szhenwei pi         unsigned long nice, guest_nice;
26981db8a0b0Szhenwei pi         char name[64];
26991db8a0b0Szhenwei pi 
27001db8a0b0Szhenwei pi         i = sscanf(line, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
27011db8a0b0Szhenwei pi                    name, &user, &nice, &system, &idle, &iowait, &irq, &softirq,
27021db8a0b0Szhenwei pi                    &steal, &guest, &guest_nice);
27031db8a0b0Szhenwei pi 
27041db8a0b0Szhenwei pi         /* drop "cpu 1 2 3 ...", get "cpuX 1 2 3 ..." only */
27051db8a0b0Szhenwei pi         if ((i == EOF) || strncmp(name, "cpu", 3) || (name[3] == '\0')) {
27061db8a0b0Szhenwei pi             continue;
27071db8a0b0Szhenwei pi         }
27081db8a0b0Szhenwei pi 
27091db8a0b0Szhenwei pi         if (i < 5) {
27101db8a0b0Szhenwei pi             slog("Parsing cpu stat from %s failed, see \"man proc\"", cpustats);
27111db8a0b0Szhenwei pi             break;
27121db8a0b0Szhenwei pi         }
27131db8a0b0Szhenwei pi 
27141db8a0b0Szhenwei pi         cpustat = g_new0(GuestCpuStats, 1);
27151db8a0b0Szhenwei pi         cpustat->type = GUEST_CPU_STATS_TYPE_LINUX;
27161db8a0b0Szhenwei pi 
27171db8a0b0Szhenwei pi         linuxcpustat = &cpustat->u.q_linux;
27181db8a0b0Szhenwei pi         linuxcpustat->cpu = atoi(&name[3]);
27191db8a0b0Szhenwei pi         linuxcpustat->user = user * 1000 / clk_tck;
27201db8a0b0Szhenwei pi         linuxcpustat->nice = nice * 1000 / clk_tck;
27211db8a0b0Szhenwei pi         linuxcpustat->system = system * 1000 / clk_tck;
27221db8a0b0Szhenwei pi         linuxcpustat->idle = idle * 1000 / clk_tck;
27231db8a0b0Szhenwei pi 
27241db8a0b0Szhenwei pi         if (i > 5) {
27251db8a0b0Szhenwei pi             linuxcpustat->has_iowait = true;
27261db8a0b0Szhenwei pi             linuxcpustat->iowait = iowait * 1000 / clk_tck;
27271db8a0b0Szhenwei pi         }
27281db8a0b0Szhenwei pi 
27291db8a0b0Szhenwei pi         if (i > 6) {
27301db8a0b0Szhenwei pi             linuxcpustat->has_irq = true;
27311db8a0b0Szhenwei pi             linuxcpustat->irq = irq * 1000 / clk_tck;
27321db8a0b0Szhenwei pi             linuxcpustat->has_softirq = true;
27331db8a0b0Szhenwei pi             linuxcpustat->softirq = softirq * 1000 / clk_tck;
27341db8a0b0Szhenwei pi         }
27351db8a0b0Szhenwei pi 
27361db8a0b0Szhenwei pi         if (i > 8) {
27371db8a0b0Szhenwei pi             linuxcpustat->has_steal = true;
27381db8a0b0Szhenwei pi             linuxcpustat->steal = steal * 1000 / clk_tck;
27391db8a0b0Szhenwei pi         }
27401db8a0b0Szhenwei pi 
27411db8a0b0Szhenwei pi         if (i > 9) {
27421db8a0b0Szhenwei pi             linuxcpustat->has_guest = true;
27431db8a0b0Szhenwei pi             linuxcpustat->guest = guest * 1000 / clk_tck;
27441db8a0b0Szhenwei pi         }
27451db8a0b0Szhenwei pi 
27461db8a0b0Szhenwei pi         if (i > 10) {
27471db8a0b0Szhenwei pi             linuxcpustat->has_guest = true;
27481db8a0b0Szhenwei pi             linuxcpustat->guest = guest * 1000 / clk_tck;
27491db8a0b0Szhenwei pi             linuxcpustat->has_guestnice = true;
27501db8a0b0Szhenwei pi             linuxcpustat->guestnice = guest_nice * 1000 / clk_tck;
27511db8a0b0Szhenwei pi         }
27521db8a0b0Szhenwei pi 
27531db8a0b0Szhenwei pi         QAPI_LIST_APPEND(tail, cpustat);
27541db8a0b0Szhenwei pi     }
27551db8a0b0Szhenwei pi 
27561db8a0b0Szhenwei pi     free(line);
27571db8a0b0Szhenwei pi     fclose(fp);
27581db8a0b0Szhenwei pi     return head;
27591db8a0b0Szhenwei pi }
27601db8a0b0Szhenwei pi 
2761e72c3f2eSMichael Roth #else /* defined(__linux__) */
2762e72c3f2eSMichael Roth 
276377dbc81bSMarkus Armbruster void qmp_guest_suspend_disk(Error **errp)
2764e72c3f2eSMichael Roth {
2765c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2766e72c3f2eSMichael Roth }
2767e72c3f2eSMichael Roth 
276877dbc81bSMarkus Armbruster void qmp_guest_suspend_ram(Error **errp)
2769e72c3f2eSMichael Roth {
2770c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2771e72c3f2eSMichael Roth }
2772e72c3f2eSMichael Roth 
277377dbc81bSMarkus Armbruster void qmp_guest_suspend_hybrid(Error **errp)
2774e72c3f2eSMichael Roth {
2775c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2776e72c3f2eSMichael Roth }
2777e72c3f2eSMichael Roth 
2778d2baff62SLaszlo Ersek GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
2779d2baff62SLaszlo Ersek {
2780c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2781d2baff62SLaszlo Ersek     return NULL;
2782d2baff62SLaszlo Ersek }
2783d2baff62SLaszlo Ersek 
2784cbb65fc2SLaszlo Ersek int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
2785cbb65fc2SLaszlo Ersek {
2786c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2787cbb65fc2SLaszlo Ersek     return -1;
2788cbb65fc2SLaszlo Ersek }
2789cbb65fc2SLaszlo Ersek 
2790a065aaa9Szhanghailiang GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
2791a065aaa9Szhanghailiang {
2792c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2793a065aaa9Szhanghailiang     return NULL;
2794a065aaa9Szhanghailiang }
2795a065aaa9Szhanghailiang 
2796a065aaa9Szhanghailiang GuestMemoryBlockResponseList *
2797a065aaa9Szhanghailiang qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
2798a065aaa9Szhanghailiang {
2799c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2800a065aaa9Szhanghailiang     return NULL;
2801a065aaa9Szhanghailiang }
2802a065aaa9Szhanghailiang 
2803a065aaa9Szhanghailiang GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
2804a065aaa9Szhanghailiang {
2805c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2806a065aaa9Szhanghailiang     return NULL;
2807a065aaa9Szhanghailiang }
2808a065aaa9Szhanghailiang 
2809e72c3f2eSMichael Roth #endif
2810e72c3f2eSMichael Roth 
281159e35c7bSAndrew Deason #ifdef HAVE_GETIFADDRS
281259e35c7bSAndrew Deason static GuestNetworkInterface *
281359e35c7bSAndrew Deason guest_find_interface(GuestNetworkInterfaceList *head,
281459e35c7bSAndrew Deason                      const char *name)
281559e35c7bSAndrew Deason {
281659e35c7bSAndrew Deason     for (; head; head = head->next) {
281759e35c7bSAndrew Deason         if (strcmp(head->value->name, name) == 0) {
281859e35c7bSAndrew Deason             return head->value;
281959e35c7bSAndrew Deason         }
282059e35c7bSAndrew Deason     }
282159e35c7bSAndrew Deason 
282259e35c7bSAndrew Deason     return NULL;
282359e35c7bSAndrew Deason }
282459e35c7bSAndrew Deason 
282559e35c7bSAndrew Deason static int guest_get_network_stats(const char *name,
282659e35c7bSAndrew Deason                        GuestNetworkInterfaceStat *stats)
282759e35c7bSAndrew Deason {
282870335c46SAndrew Deason #ifdef CONFIG_LINUX
282959e35c7bSAndrew Deason     int name_len;
283059e35c7bSAndrew Deason     char const *devinfo = "/proc/net/dev";
283159e35c7bSAndrew Deason     FILE *fp;
283259e35c7bSAndrew Deason     char *line = NULL, *colon;
283359e35c7bSAndrew Deason     size_t n = 0;
283459e35c7bSAndrew Deason     fp = fopen(devinfo, "r");
283559e35c7bSAndrew Deason     if (!fp) {
2836a539dc8aSAndrew Deason         g_debug("failed to open network stats %s: %s", devinfo,
2837a539dc8aSAndrew Deason                 g_strerror(errno));
283859e35c7bSAndrew Deason         return -1;
283959e35c7bSAndrew Deason     }
284059e35c7bSAndrew Deason     name_len = strlen(name);
284159e35c7bSAndrew Deason     while (getline(&line, &n, fp) != -1) {
284259e35c7bSAndrew Deason         long long dummy;
284359e35c7bSAndrew Deason         long long rx_bytes;
284459e35c7bSAndrew Deason         long long rx_packets;
284559e35c7bSAndrew Deason         long long rx_errs;
284659e35c7bSAndrew Deason         long long rx_dropped;
284759e35c7bSAndrew Deason         long long tx_bytes;
284859e35c7bSAndrew Deason         long long tx_packets;
284959e35c7bSAndrew Deason         long long tx_errs;
285059e35c7bSAndrew Deason         long long tx_dropped;
285159e35c7bSAndrew Deason         char *trim_line;
285259e35c7bSAndrew Deason         trim_line = g_strchug(line);
285359e35c7bSAndrew Deason         if (trim_line[0] == '\0') {
285459e35c7bSAndrew Deason             continue;
285559e35c7bSAndrew Deason         }
285659e35c7bSAndrew Deason         colon = strchr(trim_line, ':');
285759e35c7bSAndrew Deason         if (!colon) {
285859e35c7bSAndrew Deason             continue;
285959e35c7bSAndrew Deason         }
286059e35c7bSAndrew Deason         if (colon - name_len  == trim_line &&
286159e35c7bSAndrew Deason            strncmp(trim_line, name, name_len) == 0) {
286259e35c7bSAndrew Deason             if (sscanf(colon + 1,
286359e35c7bSAndrew Deason                 "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld",
286459e35c7bSAndrew Deason                   &rx_bytes, &rx_packets, &rx_errs, &rx_dropped,
286559e35c7bSAndrew Deason                   &dummy, &dummy, &dummy, &dummy,
286659e35c7bSAndrew Deason                   &tx_bytes, &tx_packets, &tx_errs, &tx_dropped,
286759e35c7bSAndrew Deason                   &dummy, &dummy, &dummy, &dummy) != 16) {
286859e35c7bSAndrew Deason                 continue;
286959e35c7bSAndrew Deason             }
287059e35c7bSAndrew Deason             stats->rx_bytes = rx_bytes;
287159e35c7bSAndrew Deason             stats->rx_packets = rx_packets;
287259e35c7bSAndrew Deason             stats->rx_errs = rx_errs;
287359e35c7bSAndrew Deason             stats->rx_dropped = rx_dropped;
287459e35c7bSAndrew Deason             stats->tx_bytes = tx_bytes;
287559e35c7bSAndrew Deason             stats->tx_packets = tx_packets;
287659e35c7bSAndrew Deason             stats->tx_errs = tx_errs;
287759e35c7bSAndrew Deason             stats->tx_dropped = tx_dropped;
287859e35c7bSAndrew Deason             fclose(fp);
287959e35c7bSAndrew Deason             g_free(line);
288059e35c7bSAndrew Deason             return 0;
288159e35c7bSAndrew Deason         }
288259e35c7bSAndrew Deason     }
288359e35c7bSAndrew Deason     fclose(fp);
288459e35c7bSAndrew Deason     g_free(line);
288559e35c7bSAndrew Deason     g_debug("/proc/net/dev: Interface '%s' not found", name);
2886a539dc8aSAndrew Deason #else /* !CONFIG_LINUX */
2887a539dc8aSAndrew Deason     g_debug("Network stats reporting available only for Linux");
2888a539dc8aSAndrew Deason #endif /* !CONFIG_LINUX */
288959e35c7bSAndrew Deason     return -1;
289059e35c7bSAndrew Deason }
289159e35c7bSAndrew Deason 
289259e35c7bSAndrew Deason /*
289359e35c7bSAndrew Deason  * Build information about guest interfaces
289459e35c7bSAndrew Deason  */
289559e35c7bSAndrew Deason GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
289659e35c7bSAndrew Deason {
289759e35c7bSAndrew Deason     GuestNetworkInterfaceList *head = NULL, **tail = &head;
289859e35c7bSAndrew Deason     struct ifaddrs *ifap, *ifa;
289959e35c7bSAndrew Deason 
290059e35c7bSAndrew Deason     if (getifaddrs(&ifap) < 0) {
290159e35c7bSAndrew Deason         error_setg_errno(errp, errno, "getifaddrs failed");
290259e35c7bSAndrew Deason         goto error;
290359e35c7bSAndrew Deason     }
290459e35c7bSAndrew Deason 
290559e35c7bSAndrew Deason     for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
290659e35c7bSAndrew Deason         GuestNetworkInterface *info;
290759e35c7bSAndrew Deason         GuestIpAddressList **address_tail;
290859e35c7bSAndrew Deason         GuestIpAddress *address_item = NULL;
290959e35c7bSAndrew Deason         GuestNetworkInterfaceStat *interface_stat = NULL;
291059e35c7bSAndrew Deason         char addr4[INET_ADDRSTRLEN];
291159e35c7bSAndrew Deason         char addr6[INET6_ADDRSTRLEN];
291259e35c7bSAndrew Deason         int sock;
291359e35c7bSAndrew Deason         struct ifreq ifr;
291459e35c7bSAndrew Deason         unsigned char *mac_addr;
291559e35c7bSAndrew Deason         void *p;
291659e35c7bSAndrew Deason 
291759e35c7bSAndrew Deason         g_debug("Processing %s interface", ifa->ifa_name);
291859e35c7bSAndrew Deason 
291959e35c7bSAndrew Deason         info = guest_find_interface(head, ifa->ifa_name);
292059e35c7bSAndrew Deason 
292159e35c7bSAndrew Deason         if (!info) {
292259e35c7bSAndrew Deason             info = g_malloc0(sizeof(*info));
292359e35c7bSAndrew Deason             info->name = g_strdup(ifa->ifa_name);
292459e35c7bSAndrew Deason 
292559e35c7bSAndrew Deason             QAPI_LIST_APPEND(tail, info);
292659e35c7bSAndrew Deason         }
292759e35c7bSAndrew Deason 
2928aec0730eSAndrew Deason         if (!info->has_hardware_address) {
292959e35c7bSAndrew Deason             /* we haven't obtained HW address yet */
293059e35c7bSAndrew Deason             sock = socket(PF_INET, SOCK_STREAM, 0);
293159e35c7bSAndrew Deason             if (sock == -1) {
293259e35c7bSAndrew Deason                 error_setg_errno(errp, errno, "failed to create socket");
293359e35c7bSAndrew Deason                 goto error;
293459e35c7bSAndrew Deason             }
293559e35c7bSAndrew Deason 
293659e35c7bSAndrew Deason             memset(&ifr, 0, sizeof(ifr));
293759e35c7bSAndrew Deason             pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->name);
293859e35c7bSAndrew Deason             if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
2939aec0730eSAndrew Deason                 /*
2940aec0730eSAndrew Deason                  * We can't get the hw addr of this interface, but that's not a
2941aec0730eSAndrew Deason                  * fatal error. Don't set info->hardware_address, but keep
2942aec0730eSAndrew Deason                  * going.
2943aec0730eSAndrew Deason                  */
2944aec0730eSAndrew Deason                 if (errno == EADDRNOTAVAIL) {
2945aec0730eSAndrew Deason                     /* The interface doesn't have a hw addr (e.g. loopback). */
2946aec0730eSAndrew Deason                     g_debug("failed to get MAC address of %s: %s",
2947aec0730eSAndrew Deason                             ifa->ifa_name, strerror(errno));
2948aec0730eSAndrew Deason                 } else{
2949aec0730eSAndrew Deason                     g_warning("failed to get MAC address of %s: %s",
2950aec0730eSAndrew Deason                               ifa->ifa_name, strerror(errno));
295159e35c7bSAndrew Deason                 }
295259e35c7bSAndrew Deason 
2953aec0730eSAndrew Deason             } else {
295470335c46SAndrew Deason #ifdef CONFIG_SOLARIS
295570335c46SAndrew Deason                 mac_addr = (unsigned char *) &ifr.ifr_addr.sa_data;
295670335c46SAndrew Deason #else
295759e35c7bSAndrew Deason                 mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
295870335c46SAndrew Deason #endif
295959e35c7bSAndrew Deason                 info->hardware_address =
296059e35c7bSAndrew Deason                     g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x",
296159e35c7bSAndrew Deason                                     (int) mac_addr[0], (int) mac_addr[1],
296259e35c7bSAndrew Deason                                     (int) mac_addr[2], (int) mac_addr[3],
296359e35c7bSAndrew Deason                                     (int) mac_addr[4], (int) mac_addr[5]);
296459e35c7bSAndrew Deason 
296559e35c7bSAndrew Deason                 info->has_hardware_address = true;
296659e35c7bSAndrew Deason             }
2967aec0730eSAndrew Deason             close(sock);
2968aec0730eSAndrew Deason         }
296959e35c7bSAndrew Deason 
297059e35c7bSAndrew Deason         if (ifa->ifa_addr &&
297159e35c7bSAndrew Deason             ifa->ifa_addr->sa_family == AF_INET) {
297259e35c7bSAndrew Deason             /* interface with IPv4 address */
297359e35c7bSAndrew Deason             p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
297459e35c7bSAndrew Deason             if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
297559e35c7bSAndrew Deason                 error_setg_errno(errp, errno, "inet_ntop failed");
297659e35c7bSAndrew Deason                 goto error;
297759e35c7bSAndrew Deason             }
297859e35c7bSAndrew Deason 
297959e35c7bSAndrew Deason             address_item = g_malloc0(sizeof(*address_item));
298059e35c7bSAndrew Deason             address_item->ip_address = g_strdup(addr4);
298159e35c7bSAndrew Deason             address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;
298259e35c7bSAndrew Deason 
298359e35c7bSAndrew Deason             if (ifa->ifa_netmask) {
298459e35c7bSAndrew Deason                 /* Count the number of set bits in netmask.
298559e35c7bSAndrew Deason                  * This is safe as '1' and '0' cannot be shuffled in netmask. */
298659e35c7bSAndrew Deason                 p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;
298759e35c7bSAndrew Deason                 address_item->prefix = ctpop32(((uint32_t *) p)[0]);
298859e35c7bSAndrew Deason             }
298959e35c7bSAndrew Deason         } else if (ifa->ifa_addr &&
299059e35c7bSAndrew Deason                    ifa->ifa_addr->sa_family == AF_INET6) {
299159e35c7bSAndrew Deason             /* interface with IPv6 address */
299259e35c7bSAndrew Deason             p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
299359e35c7bSAndrew Deason             if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
299459e35c7bSAndrew Deason                 error_setg_errno(errp, errno, "inet_ntop failed");
299559e35c7bSAndrew Deason                 goto error;
299659e35c7bSAndrew Deason             }
299759e35c7bSAndrew Deason 
299859e35c7bSAndrew Deason             address_item = g_malloc0(sizeof(*address_item));
299959e35c7bSAndrew Deason             address_item->ip_address = g_strdup(addr6);
300059e35c7bSAndrew Deason             address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;
300159e35c7bSAndrew Deason 
300259e35c7bSAndrew Deason             if (ifa->ifa_netmask) {
300359e35c7bSAndrew Deason                 /* Count the number of set bits in netmask.
300459e35c7bSAndrew Deason                  * This is safe as '1' and '0' cannot be shuffled in netmask. */
300559e35c7bSAndrew Deason                 p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
300659e35c7bSAndrew Deason                 address_item->prefix =
300759e35c7bSAndrew Deason                     ctpop32(((uint32_t *) p)[0]) +
300859e35c7bSAndrew Deason                     ctpop32(((uint32_t *) p)[1]) +
300959e35c7bSAndrew Deason                     ctpop32(((uint32_t *) p)[2]) +
301059e35c7bSAndrew Deason                     ctpop32(((uint32_t *) p)[3]);
301159e35c7bSAndrew Deason             }
301259e35c7bSAndrew Deason         }
301359e35c7bSAndrew Deason 
301459e35c7bSAndrew Deason         if (!address_item) {
301559e35c7bSAndrew Deason             continue;
301659e35c7bSAndrew Deason         }
301759e35c7bSAndrew Deason 
301859e35c7bSAndrew Deason         address_tail = &info->ip_addresses;
301959e35c7bSAndrew Deason         while (*address_tail) {
302059e35c7bSAndrew Deason             address_tail = &(*address_tail)->next;
302159e35c7bSAndrew Deason         }
302259e35c7bSAndrew Deason         QAPI_LIST_APPEND(address_tail, address_item);
302359e35c7bSAndrew Deason 
302459e35c7bSAndrew Deason         info->has_ip_addresses = true;
302559e35c7bSAndrew Deason 
302659e35c7bSAndrew Deason         if (!info->has_statistics) {
302759e35c7bSAndrew Deason             interface_stat = g_malloc0(sizeof(*interface_stat));
302859e35c7bSAndrew Deason             if (guest_get_network_stats(info->name, interface_stat) == -1) {
302959e35c7bSAndrew Deason                 info->has_statistics = false;
303059e35c7bSAndrew Deason                 g_free(interface_stat);
303159e35c7bSAndrew Deason             } else {
303259e35c7bSAndrew Deason                 info->statistics = interface_stat;
303359e35c7bSAndrew Deason                 info->has_statistics = true;
303459e35c7bSAndrew Deason             }
303559e35c7bSAndrew Deason         }
303659e35c7bSAndrew Deason     }
303759e35c7bSAndrew Deason 
303859e35c7bSAndrew Deason     freeifaddrs(ifap);
303959e35c7bSAndrew Deason     return head;
304059e35c7bSAndrew Deason 
304159e35c7bSAndrew Deason error:
304259e35c7bSAndrew Deason     freeifaddrs(ifap);
304359e35c7bSAndrew Deason     qapi_free_GuestNetworkInterfaceList(head);
304459e35c7bSAndrew Deason     return NULL;
304559e35c7bSAndrew Deason }
304659e35c7bSAndrew Deason 
304759e35c7bSAndrew Deason #else
304859e35c7bSAndrew Deason 
304959e35c7bSAndrew Deason GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
305059e35c7bSAndrew Deason {
305159e35c7bSAndrew Deason     error_setg(errp, QERR_UNSUPPORTED);
305259e35c7bSAndrew Deason     return NULL;
305359e35c7bSAndrew Deason }
305459e35c7bSAndrew Deason 
305559e35c7bSAndrew Deason #endif /* HAVE_GETIFADDRS */
305659e35c7bSAndrew Deason 
3057d35d4cb5SMichael Roth #if !defined(CONFIG_FSFREEZE)
3058d35d4cb5SMichael Roth 
305946d4c572STomoki Sekiyama GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
306046d4c572STomoki Sekiyama {
3061c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
306246d4c572STomoki Sekiyama     return NULL;
306346d4c572STomoki Sekiyama }
306446d4c572STomoki Sekiyama 
306577dbc81bSMarkus Armbruster GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
3066d35d4cb5SMichael Roth {
3067c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
3068d35d4cb5SMichael Roth 
3069d35d4cb5SMichael Roth     return 0;
3070d35d4cb5SMichael Roth }
3071d35d4cb5SMichael Roth 
307277dbc81bSMarkus Armbruster int64_t qmp_guest_fsfreeze_freeze(Error **errp)
3073d35d4cb5SMichael Roth {
3074c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
3075d35d4cb5SMichael Roth 
3076d35d4cb5SMichael Roth     return 0;
3077d35d4cb5SMichael Roth }
3078d35d4cb5SMichael Roth 
3079e99bce20STomoki Sekiyama int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
3080e99bce20STomoki Sekiyama                                        strList *mountpoints,
3081e99bce20STomoki Sekiyama                                        Error **errp)
3082e99bce20STomoki Sekiyama {
3083c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
3084e99bce20STomoki Sekiyama 
3085e99bce20STomoki Sekiyama     return 0;
3086e99bce20STomoki Sekiyama }
3087e99bce20STomoki Sekiyama 
308877dbc81bSMarkus Armbruster int64_t qmp_guest_fsfreeze_thaw(Error **errp)
3089d35d4cb5SMichael Roth {
3090c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
3091d35d4cb5SMichael Roth 
3092d35d4cb5SMichael Roth     return 0;
3093d35d4cb5SMichael Roth }
3094fed39564STomáš Golembiovský 
3095fed39564STomáš Golembiovský GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
3096fed39564STomáš Golembiovský {
3097fed39564STomáš Golembiovský     error_setg(errp, QERR_UNSUPPORTED);
3098fed39564STomáš Golembiovský     return NULL;
3099fed39564STomáš Golembiovský }
3100fed39564STomáš Golembiovský 
31013569664eSluzhipeng GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp)
31023569664eSluzhipeng {
31033569664eSluzhipeng     error_setg(errp, QERR_UNSUPPORTED);
31043569664eSluzhipeng     return NULL;
31053569664eSluzhipeng }
31063569664eSluzhipeng 
31071db8a0b0Szhenwei pi GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
31081db8a0b0Szhenwei pi {
31091db8a0b0Szhenwei pi     error_setg(errp, QERR_UNSUPPORTED);
31101db8a0b0Szhenwei pi     return NULL;
31111db8a0b0Szhenwei pi }
31123569664eSluzhipeng 
3113eab5fd59SPaolo Bonzini #endif /* CONFIG_FSFREEZE */
3114d35d4cb5SMichael Roth 
3115eab5fd59SPaolo Bonzini #if !defined(CONFIG_FSTRIM)
3116e82855d9SJustin Ossevoort GuestFilesystemTrimResponse *
3117e82855d9SJustin Ossevoort qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
3118eab5fd59SPaolo Bonzini {
3119c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
3120e82855d9SJustin Ossevoort     return NULL;
3121eab5fd59SPaolo Bonzini }
3122d35d4cb5SMichael Roth #endif
3123d35d4cb5SMichael Roth 
31240e4ef702SThomas Huth /* add unsupported commands to the list of blocked RPCs */
31250e4ef702SThomas Huth GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
31261281c08aSTomoki Sekiyama {
31271281c08aSTomoki Sekiyama #if !defined(__linux__)
31281281c08aSTomoki Sekiyama     {
31291281c08aSTomoki Sekiyama         const char *list[] = {
31301281c08aSTomoki Sekiyama             "guest-suspend-disk", "guest-suspend-ram",
313159e35c7bSAndrew Deason             "guest-suspend-hybrid", "guest-get-vcpus", "guest-set-vcpus",
31320dd38a03Szhanghailiang             "guest-get-memory-blocks", "guest-set-memory-blocks",
313328d8dd35SBasil Salman             "guest-get-memory-block-size", "guest-get-memory-block-info",
313428d8dd35SBasil Salman             NULL};
31351281c08aSTomoki Sekiyama         char **p = (char **)list;
31361281c08aSTomoki Sekiyama 
31371281c08aSTomoki Sekiyama         while (*p) {
31380e4ef702SThomas Huth             blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
31391281c08aSTomoki Sekiyama         }
31401281c08aSTomoki Sekiyama     }
31411281c08aSTomoki Sekiyama #endif
31421281c08aSTomoki Sekiyama 
314359e35c7bSAndrew Deason #if !defined(HAVE_GETIFADDRS)
31440e4ef702SThomas Huth     blockedrpcs = g_list_append(blockedrpcs,
314559e35c7bSAndrew Deason                               g_strdup("guest-network-get-interfaces"));
314659e35c7bSAndrew Deason #endif
314759e35c7bSAndrew Deason 
31481281c08aSTomoki Sekiyama #if !defined(CONFIG_FSFREEZE)
31491281c08aSTomoki Sekiyama     {
31501281c08aSTomoki Sekiyama         const char *list[] = {
31511281c08aSTomoki Sekiyama             "guest-get-fsinfo", "guest-fsfreeze-status",
31521281c08aSTomoki Sekiyama             "guest-fsfreeze-freeze", "guest-fsfreeze-freeze-list",
3153fed39564STomáš Golembiovský             "guest-fsfreeze-thaw", "guest-get-fsinfo",
3154fed39564STomáš Golembiovský             "guest-get-disks", NULL};
31551281c08aSTomoki Sekiyama         char **p = (char **)list;
31561281c08aSTomoki Sekiyama 
31571281c08aSTomoki Sekiyama         while (*p) {
31580e4ef702SThomas Huth             blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
31591281c08aSTomoki Sekiyama         }
31601281c08aSTomoki Sekiyama     }
31611281c08aSTomoki Sekiyama #endif
31621281c08aSTomoki Sekiyama 
31631281c08aSTomoki Sekiyama #if !defined(CONFIG_FSTRIM)
31640e4ef702SThomas Huth     blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-fstrim"));
31651281c08aSTomoki Sekiyama #endif
31661281c08aSTomoki Sekiyama 
31670e4ef702SThomas Huth     blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-get-devices"));
31682e4211ceSTomáš Golembiovský 
31690e4ef702SThomas Huth     return blockedrpcs;
31701281c08aSTomoki Sekiyama }
31711281c08aSTomoki Sekiyama 
3172c216e5adSMichael Roth /* register init/cleanup routines for stateful command groups */
3173c216e5adSMichael Roth void ga_command_state_init(GAState *s, GACommandState *cs)
3174c216e5adSMichael Roth {
3175c216e5adSMichael Roth #if defined(CONFIG_FSFREEZE)
3176f22d85e9SMichael Roth     ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
3177c216e5adSMichael Roth #endif
3178c216e5adSMichael Roth }
3179161a56a9SVinzenz Feenstra 
3180e674605fSTomáš Golembiovský #ifdef HAVE_UTMPX
3181e674605fSTomáš Golembiovský 
3182161a56a9SVinzenz Feenstra #define QGA_MICRO_SECOND_TO_SECOND 1000000
3183161a56a9SVinzenz Feenstra 
3184161a56a9SVinzenz Feenstra static double ga_get_login_time(struct utmpx *user_info)
3185161a56a9SVinzenz Feenstra {
3186161a56a9SVinzenz Feenstra     double seconds = (double)user_info->ut_tv.tv_sec;
3187161a56a9SVinzenz Feenstra     double useconds = (double)user_info->ut_tv.tv_usec;
3188161a56a9SVinzenz Feenstra     useconds /= QGA_MICRO_SECOND_TO_SECOND;
3189161a56a9SVinzenz Feenstra     return seconds + useconds;
3190161a56a9SVinzenz Feenstra }
3191161a56a9SVinzenz Feenstra 
3192b90abbacSVladimir Sementsov-Ogievskiy GuestUserList *qmp_guest_get_users(Error **errp)
3193161a56a9SVinzenz Feenstra {
3194161a56a9SVinzenz Feenstra     GHashTable *cache = NULL;
319595b3a8c8SEric Blake     GuestUserList *head = NULL, **tail = &head;
3196161a56a9SVinzenz Feenstra     struct utmpx *user_info = NULL;
3197161a56a9SVinzenz Feenstra     gpointer value = NULL;
3198161a56a9SVinzenz Feenstra     GuestUser *user = NULL;
3199161a56a9SVinzenz Feenstra     double login_time = 0;
3200161a56a9SVinzenz Feenstra 
3201161a56a9SVinzenz Feenstra     cache = g_hash_table_new(g_str_hash, g_str_equal);
3202161a56a9SVinzenz Feenstra     setutxent();
3203161a56a9SVinzenz Feenstra 
3204161a56a9SVinzenz Feenstra     for (;;) {
3205161a56a9SVinzenz Feenstra         user_info = getutxent();
3206161a56a9SVinzenz Feenstra         if (user_info == NULL) {
3207161a56a9SVinzenz Feenstra             break;
3208161a56a9SVinzenz Feenstra         } else if (user_info->ut_type != USER_PROCESS) {
3209161a56a9SVinzenz Feenstra             continue;
3210161a56a9SVinzenz Feenstra         } else if (g_hash_table_contains(cache, user_info->ut_user)) {
3211161a56a9SVinzenz Feenstra             value = g_hash_table_lookup(cache, user_info->ut_user);
3212161a56a9SVinzenz Feenstra             user = (GuestUser *)value;
3213161a56a9SVinzenz Feenstra             login_time = ga_get_login_time(user_info);
3214161a56a9SVinzenz Feenstra             /* We're ensuring the earliest login time to be sent */
3215161a56a9SVinzenz Feenstra             if (login_time < user->login_time) {
3216161a56a9SVinzenz Feenstra                 user->login_time = login_time;
3217161a56a9SVinzenz Feenstra             }
3218161a56a9SVinzenz Feenstra             continue;
3219161a56a9SVinzenz Feenstra         }
3220161a56a9SVinzenz Feenstra 
322195b3a8c8SEric Blake         user = g_new0(GuestUser, 1);
322295b3a8c8SEric Blake         user->user = g_strdup(user_info->ut_user);
322395b3a8c8SEric Blake         user->login_time = ga_get_login_time(user_info);
3224161a56a9SVinzenz Feenstra 
322595b3a8c8SEric Blake         g_hash_table_insert(cache, user->user, user);
3226161a56a9SVinzenz Feenstra 
322795b3a8c8SEric Blake         QAPI_LIST_APPEND(tail, user);
3228161a56a9SVinzenz Feenstra     }
3229161a56a9SVinzenz Feenstra     endutxent();
3230161a56a9SVinzenz Feenstra     g_hash_table_destroy(cache);
3231161a56a9SVinzenz Feenstra     return head;
3232161a56a9SVinzenz Feenstra }
3233e674605fSTomáš Golembiovský 
3234e674605fSTomáš Golembiovský #else
3235e674605fSTomáš Golembiovský 
3236e674605fSTomáš Golembiovský GuestUserList *qmp_guest_get_users(Error **errp)
3237e674605fSTomáš Golembiovský {
3238e674605fSTomáš Golembiovský     error_setg(errp, QERR_UNSUPPORTED);
3239e674605fSTomáš Golembiovský     return NULL;
3240e674605fSTomáš Golembiovský }
3241e674605fSTomáš Golembiovský 
3242e674605fSTomáš Golembiovský #endif
32439848f797STomáš Golembiovský 
32449848f797STomáš Golembiovský /* Replace escaped special characters with theire real values. The replacement
32459848f797STomáš Golembiovský  * is done in place -- returned value is in the original string.
32469848f797STomáš Golembiovský  */
32479848f797STomáš Golembiovský static void ga_osrelease_replace_special(gchar *value)
32489848f797STomáš Golembiovský {
32499848f797STomáš Golembiovský     gchar *p, *p2, quote;
32509848f797STomáš Golembiovský 
32519848f797STomáš Golembiovský     /* Trim the string at first space or semicolon if it is not enclosed in
32529848f797STomáš Golembiovský      * single or double quotes. */
32539848f797STomáš Golembiovský     if ((value[0] != '"') || (value[0] == '\'')) {
32549848f797STomáš Golembiovský         p = strchr(value, ' ');
32559848f797STomáš Golembiovský         if (p != NULL) {
32569848f797STomáš Golembiovský             *p = 0;
32579848f797STomáš Golembiovský         }
32589848f797STomáš Golembiovský         p = strchr(value, ';');
32599848f797STomáš Golembiovský         if (p != NULL) {
32609848f797STomáš Golembiovský             *p = 0;
32619848f797STomáš Golembiovský         }
32629848f797STomáš Golembiovský         return;
32639848f797STomáš Golembiovský     }
32649848f797STomáš Golembiovský 
32659848f797STomáš Golembiovský     quote = value[0];
32669848f797STomáš Golembiovský     p2 = value;
32679848f797STomáš Golembiovský     p = value + 1;
32689848f797STomáš Golembiovský     while (*p != 0) {
32699848f797STomáš Golembiovský         if (*p == '\\') {
32709848f797STomáš Golembiovský             p++;
32719848f797STomáš Golembiovský             switch (*p) {
32729848f797STomáš Golembiovský             case '$':
32739848f797STomáš Golembiovský             case '\'':
32749848f797STomáš Golembiovský             case '"':
32759848f797STomáš Golembiovský             case '\\':
32769848f797STomáš Golembiovský             case '`':
32779848f797STomáš Golembiovský                 break;
32789848f797STomáš Golembiovský             default:
32799848f797STomáš Golembiovský                 /* Keep literal backslash followed by whatever is there */
32809848f797STomáš Golembiovský                 p--;
32819848f797STomáš Golembiovský                 break;
32829848f797STomáš Golembiovský             }
32839848f797STomáš Golembiovský         } else if (*p == quote) {
32849848f797STomáš Golembiovský             *p2 = 0;
32859848f797STomáš Golembiovský             break;
32869848f797STomáš Golembiovský         }
32879848f797STomáš Golembiovský         *(p2++) = *(p++);
32889848f797STomáš Golembiovský     }
32899848f797STomáš Golembiovský }
32909848f797STomáš Golembiovský 
32919848f797STomáš Golembiovský static GKeyFile *ga_parse_osrelease(const char *fname)
32929848f797STomáš Golembiovský {
32939848f797STomáš Golembiovský     gchar *content = NULL;
32949848f797STomáš Golembiovský     gchar *content2 = NULL;
32959848f797STomáš Golembiovský     GError *err = NULL;
32969848f797STomáš Golembiovský     GKeyFile *keys = g_key_file_new();
32979848f797STomáš Golembiovský     const char *group = "[os-release]\n";
32989848f797STomáš Golembiovský 
32999848f797STomáš Golembiovský     if (!g_file_get_contents(fname, &content, NULL, &err)) {
33009848f797STomáš Golembiovský         slog("failed to read '%s', error: %s", fname, err->message);
33019848f797STomáš Golembiovský         goto fail;
33029848f797STomáš Golembiovský     }
33039848f797STomáš Golembiovský 
33049848f797STomáš Golembiovský     if (!g_utf8_validate(content, -1, NULL)) {
33059848f797STomáš Golembiovský         slog("file is not utf-8 encoded: %s", fname);
33069848f797STomáš Golembiovský         goto fail;
33079848f797STomáš Golembiovský     }
33089848f797STomáš Golembiovský     content2 = g_strdup_printf("%s%s", group, content);
33099848f797STomáš Golembiovský 
33109848f797STomáš Golembiovský     if (!g_key_file_load_from_data(keys, content2, -1, G_KEY_FILE_NONE,
33119848f797STomáš Golembiovský                                    &err)) {
33129848f797STomáš Golembiovský         slog("failed to parse file '%s', error: %s", fname, err->message);
33139848f797STomáš Golembiovský         goto fail;
33149848f797STomáš Golembiovský     }
33159848f797STomáš Golembiovský 
33169848f797STomáš Golembiovský     g_free(content);
33179848f797STomáš Golembiovský     g_free(content2);
33189848f797STomáš Golembiovský     return keys;
33199848f797STomáš Golembiovský 
33209848f797STomáš Golembiovský fail:
33219848f797STomáš Golembiovský     g_error_free(err);
33229848f797STomáš Golembiovský     g_free(content);
33239848f797STomáš Golembiovský     g_free(content2);
33249848f797STomáš Golembiovský     g_key_file_free(keys);
33259848f797STomáš Golembiovský     return NULL;
33269848f797STomáš Golembiovský }
33279848f797STomáš Golembiovský 
33289848f797STomáš Golembiovský GuestOSInfo *qmp_guest_get_osinfo(Error **errp)
33299848f797STomáš Golembiovský {
33309848f797STomáš Golembiovský     GuestOSInfo *info = NULL;
33319848f797STomáš Golembiovský     struct utsname kinfo;
3332339ca68bSTomáš Golembiovský     GKeyFile *osrelease = NULL;
3333339ca68bSTomáš Golembiovský     const char *qga_os_release = g_getenv("QGA_OS_RELEASE");
33349848f797STomáš Golembiovský 
33359848f797STomáš Golembiovský     info = g_new0(GuestOSInfo, 1);
33369848f797STomáš Golembiovský 
33379848f797STomáš Golembiovský     if (uname(&kinfo) != 0) {
33389848f797STomáš Golembiovský         error_setg_errno(errp, errno, "uname failed");
33399848f797STomáš Golembiovský     } else {
33409848f797STomáš Golembiovský         info->has_kernel_version = true;
33419848f797STomáš Golembiovský         info->kernel_version = g_strdup(kinfo.version);
33429848f797STomáš Golembiovský         info->has_kernel_release = true;
33439848f797STomáš Golembiovský         info->kernel_release = g_strdup(kinfo.release);
33449848f797STomáš Golembiovský         info->has_machine = true;
33459848f797STomáš Golembiovský         info->machine = g_strdup(kinfo.machine);
33469848f797STomáš Golembiovský     }
33479848f797STomáš Golembiovský 
3348339ca68bSTomáš Golembiovský     if (qga_os_release != NULL) {
3349339ca68bSTomáš Golembiovský         osrelease = ga_parse_osrelease(qga_os_release);
3350339ca68bSTomáš Golembiovský     } else {
33519848f797STomáš Golembiovský         osrelease = ga_parse_osrelease("/etc/os-release");
33529848f797STomáš Golembiovský         if (osrelease == NULL) {
33539848f797STomáš Golembiovský             osrelease = ga_parse_osrelease("/usr/lib/os-release");
33549848f797STomáš Golembiovský         }
3355339ca68bSTomáš Golembiovský     }
33569848f797STomáš Golembiovský 
33579848f797STomáš Golembiovský     if (osrelease != NULL) {
33589848f797STomáš Golembiovský         char *value;
33599848f797STomáš Golembiovský 
33609848f797STomáš Golembiovský #define GET_FIELD(field, osfield) do { \
33619848f797STomáš Golembiovský     value = g_key_file_get_value(osrelease, "os-release", osfield, NULL); \
33629848f797STomáš Golembiovský     if (value != NULL) { \
33639848f797STomáš Golembiovský         ga_osrelease_replace_special(value); \
33649848f797STomáš Golembiovský         info->has_ ## field = true; \
33659848f797STomáš Golembiovský         info->field = value; \
33669848f797STomáš Golembiovský     } \
33679848f797STomáš Golembiovský } while (0)
33689848f797STomáš Golembiovský         GET_FIELD(id, "ID");
33699848f797STomáš Golembiovský         GET_FIELD(name, "NAME");
33709848f797STomáš Golembiovský         GET_FIELD(pretty_name, "PRETTY_NAME");
33719848f797STomáš Golembiovský         GET_FIELD(version, "VERSION");
33729848f797STomáš Golembiovský         GET_FIELD(version_id, "VERSION_ID");
33739848f797STomáš Golembiovský         GET_FIELD(variant, "VARIANT");
33749848f797STomáš Golembiovský         GET_FIELD(variant_id, "VARIANT_ID");
33759848f797STomáš Golembiovský #undef GET_FIELD
33769848f797STomáš Golembiovský 
33779848f797STomáš Golembiovský         g_key_file_free(osrelease);
33789848f797STomáš Golembiovský     }
33799848f797STomáš Golembiovský 
33809848f797STomáš Golembiovský     return info;
33819848f797STomáš Golembiovský }
33822e4211ceSTomáš Golembiovský 
33832e4211ceSTomáš Golembiovský GuestDeviceInfoList *qmp_guest_get_devices(Error **errp)
33842e4211ceSTomáš Golembiovský {
33852e4211ceSTomáš Golembiovský     error_setg(errp, QERR_UNSUPPORTED);
33862e4211ceSTomáš Golembiovský 
33872e4211ceSTomáš Golembiovský     return NULL;
33882e4211ceSTomáš Golembiovský }
3389548fb0daSMarc-André Lureau 
3390548fb0daSMarc-André Lureau #ifndef HOST_NAME_MAX
3391548fb0daSMarc-André Lureau # ifdef _POSIX_HOST_NAME_MAX
3392548fb0daSMarc-André Lureau #  define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
3393548fb0daSMarc-André Lureau # else
3394548fb0daSMarc-André Lureau #  define HOST_NAME_MAX 255
3395548fb0daSMarc-André Lureau # endif
3396548fb0daSMarc-André Lureau #endif
3397548fb0daSMarc-André Lureau 
3398548fb0daSMarc-André Lureau char *qga_get_host_name(Error **errp)
3399548fb0daSMarc-André Lureau {
3400548fb0daSMarc-André Lureau     long len = -1;
3401548fb0daSMarc-André Lureau     g_autofree char *hostname = NULL;
3402548fb0daSMarc-André Lureau 
3403548fb0daSMarc-André Lureau #ifdef _SC_HOST_NAME_MAX
3404548fb0daSMarc-André Lureau     len = sysconf(_SC_HOST_NAME_MAX);
3405548fb0daSMarc-André Lureau #endif /* _SC_HOST_NAME_MAX */
3406548fb0daSMarc-André Lureau 
3407548fb0daSMarc-André Lureau     if (len < 0) {
3408548fb0daSMarc-André Lureau         len = HOST_NAME_MAX;
3409548fb0daSMarc-André Lureau     }
3410548fb0daSMarc-André Lureau 
3411548fb0daSMarc-André Lureau     /* Unfortunately, gethostname() below does not guarantee a
3412548fb0daSMarc-André Lureau      * NULL terminated string. Therefore, allocate one byte more
3413548fb0daSMarc-André Lureau      * to be sure. */
3414548fb0daSMarc-André Lureau     hostname = g_new0(char, len + 1);
3415548fb0daSMarc-André Lureau 
3416548fb0daSMarc-André Lureau     if (gethostname(hostname, len) < 0) {
3417548fb0daSMarc-André Lureau         error_setg_errno(errp, errno,
3418548fb0daSMarc-André Lureau                          "cannot get hostname");
3419548fb0daSMarc-André Lureau         return NULL;
3420548fb0daSMarc-André Lureau     }
3421548fb0daSMarc-André Lureau 
3422548fb0daSMarc-André Lureau     return g_steal_pointer(&hostname);
3423548fb0daSMarc-André Lureau }
3424