xref: /openbmc/qemu/qga/commands-posix.c (revision a1241094)
1c216e5adSMichael Roth /*
2c216e5adSMichael Roth  * QEMU Guest Agent POSIX-specific command implementations
3c216e5adSMichael Roth  *
4c216e5adSMichael Roth  * Copyright IBM Corp. 2011
5c216e5adSMichael Roth  *
6c216e5adSMichael Roth  * Authors:
7c216e5adSMichael Roth  *  Michael Roth      <mdroth@linux.vnet.ibm.com>
83424fc9fSMichal Privoznik  *  Michal Privoznik  <mprivozn@redhat.com>
9c216e5adSMichael Roth  *
10c216e5adSMichael Roth  * This work is licensed under the terms of the GNU GPL, version 2 or later.
11c216e5adSMichael Roth  * See the COPYING file in the top-level directory.
12c216e5adSMichael Roth  */
13c216e5adSMichael Roth 
144459bf38SPeter Maydell #include "qemu/osdep.h"
15c216e5adSMichael Roth #include <sys/ioctl.h>
169848f797STomáš Golembiovský #include <sys/utsname.h>
172c02cbf6SLuiz Capitulino #include <sys/wait.h>
1846d4c572STomoki Sekiyama #include <dirent.h>
19eb815e24SMarkus Armbruster #include "qga-qapi-commands.h"
20e688df6bSMarkus Armbruster #include "qapi/error.h"
217b1b5d19SPaolo Bonzini #include "qapi/qmp/qerror.h"
221de7afc9SPaolo Bonzini #include "qemu/host-utils.h"
2312505396SDenis V. Lunev #include "qemu/sockets.h"
24920639caSDaniel P. Berrange #include "qemu/base64.h"
25f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
265d3586b8SPhilippe Mathieu-Daudé #include "commands-common.h"
2722668881Szhenwei pi #include "block/nvme.h"
281a89a17bSMarc-André Lureau #include "cutils.h"
29c216e5adSMichael Roth 
30e674605fSTomáš Golembiovský #ifdef HAVE_UTMPX
31e674605fSTomáš Golembiovský #include <utmpx.h>
32e674605fSTomáš Golembiovský #endif
33e674605fSTomáš Golembiovský 
34e72c3f2eSMichael Roth #if defined(__linux__)
35e72c3f2eSMichael Roth #include <mntent.h>
3625b5ff1aSChen Hanxiao #include <sys/statvfs.h>
3722668881Szhenwei pi #include <linux/nvme_ioctl.h>
38e72c3f2eSMichael Roth 
39b616105aSTomáš Golembiovský #ifdef CONFIG_LIBUDEV
40b616105aSTomáš Golembiovský #include <libudev.h>
41b616105aSTomáš Golembiovský #endif
42e72c3f2eSMichael Roth #endif
43e72c3f2eSMichael Roth 
4459e35c7bSAndrew Deason #ifdef HAVE_GETIFADDRS
4559e35c7bSAndrew Deason #include <arpa/inet.h>
4659e35c7bSAndrew Deason #include <sys/socket.h>
4759e35c7bSAndrew Deason #include <net/if.h>
48*a1241094SAlexander Ivanov #include <net/ethernet.h>
4959e35c7bSAndrew Deason #include <sys/types.h>
5059e35c7bSAndrew Deason #ifdef CONFIG_SOLARIS
5159e35c7bSAndrew Deason #include <sys/sockio.h>
5259e35c7bSAndrew Deason #endif
5359e35c7bSAndrew Deason #endif
5459e35c7bSAndrew Deason 
5577dbc81bSMarkus Armbruster static void ga_wait_child(pid_t pid, int *status, Error **errp)
56d220a6dfSLuiz Capitulino {
57d220a6dfSLuiz Capitulino     pid_t rpid;
58d220a6dfSLuiz Capitulino 
59d220a6dfSLuiz Capitulino     *status = 0;
60d220a6dfSLuiz Capitulino 
61d220a6dfSLuiz Capitulino     do {
62d220a6dfSLuiz Capitulino         rpid = waitpid(pid, status, 0);
63d220a6dfSLuiz Capitulino     } while (rpid == -1 && errno == EINTR);
64d220a6dfSLuiz Capitulino 
65d220a6dfSLuiz Capitulino     if (rpid == -1) {
6677dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to wait for child (pid: %d)",
6777dbc81bSMarkus Armbruster                          pid);
68d220a6dfSLuiz Capitulino         return;
69d220a6dfSLuiz Capitulino     }
70d220a6dfSLuiz Capitulino 
71d220a6dfSLuiz Capitulino     g_assert(rpid == pid);
72d220a6dfSLuiz Capitulino }
73d220a6dfSLuiz Capitulino 
7477dbc81bSMarkus Armbruster void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp)
75c216e5adSMichael Roth {
76c216e5adSMichael Roth     const char *shutdown_flag;
77d220a6dfSLuiz Capitulino     Error *local_err = NULL;
78d220a6dfSLuiz Capitulino     pid_t pid;
793674838cSLuiz Capitulino     int status;
80c216e5adSMichael Roth 
81c8ec041dSAndrew Deason #ifdef CONFIG_SOLARIS
82c8ec041dSAndrew Deason     const char *powerdown_flag = "-i5";
83c8ec041dSAndrew Deason     const char *halt_flag = "-i0";
84c8ec041dSAndrew Deason     const char *reboot_flag = "-i6";
85e40762fcSAlexander Ivanov #elif defined(CONFIG_BSD)
86e40762fcSAlexander Ivanov     const char *powerdown_flag = "-p";
87e40762fcSAlexander Ivanov     const char *halt_flag = "-h";
88e40762fcSAlexander Ivanov     const char *reboot_flag = "-r";
89c8ec041dSAndrew Deason #else
90c8ec041dSAndrew Deason     const char *powerdown_flag = "-P";
91c8ec041dSAndrew Deason     const char *halt_flag = "-H";
92c8ec041dSAndrew Deason     const char *reboot_flag = "-r";
93c8ec041dSAndrew Deason #endif
94c8ec041dSAndrew Deason 
95c216e5adSMichael Roth     slog("guest-shutdown called, mode: %s", mode);
96c216e5adSMichael Roth     if (!has_mode || strcmp(mode, "powerdown") == 0) {
97c8ec041dSAndrew Deason         shutdown_flag = powerdown_flag;
98c216e5adSMichael Roth     } else if (strcmp(mode, "halt") == 0) {
99c8ec041dSAndrew Deason         shutdown_flag = halt_flag;
100c216e5adSMichael Roth     } else if (strcmp(mode, "reboot") == 0) {
101c8ec041dSAndrew Deason         shutdown_flag = reboot_flag;
102c216e5adSMichael Roth     } else {
10377dbc81bSMarkus Armbruster         error_setg(errp,
104d220a6dfSLuiz Capitulino                    "mode is invalid (valid values are: halt|powerdown|reboot");
105c216e5adSMichael Roth         return;
106c216e5adSMichael Roth     }
107c216e5adSMichael Roth 
108d5dd3498SLuiz Capitulino     pid = fork();
109d5dd3498SLuiz Capitulino     if (pid == 0) {
110c216e5adSMichael Roth         /* child, start the shutdown */
111c216e5adSMichael Roth         setsid();
1123674838cSLuiz Capitulino         reopen_fd_to_null(0);
1133674838cSLuiz Capitulino         reopen_fd_to_null(1);
1143674838cSLuiz Capitulino         reopen_fd_to_null(2);
115c216e5adSMichael Roth 
116c8ec041dSAndrew Deason #ifdef CONFIG_SOLARIS
117c8ec041dSAndrew Deason         execl("/sbin/shutdown", "shutdown", shutdown_flag, "-g0", "-y",
118c8ec041dSAndrew Deason               "hypervisor initiated shutdown", (char *)NULL);
119e40762fcSAlexander Ivanov #elif defined(CONFIG_BSD)
120e40762fcSAlexander Ivanov         execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
121e40762fcSAlexander Ivanov                "hypervisor initiated shutdown", (char *)NULL);
122c8ec041dSAndrew Deason #else
123fcc41961SMarc-André Lureau         execl("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0",
124fcc41961SMarc-André Lureau                "hypervisor initiated shutdown", (char *)NULL);
125c8ec041dSAndrew Deason #endif
1263674838cSLuiz Capitulino         _exit(EXIT_FAILURE);
127d5dd3498SLuiz Capitulino     } else if (pid < 0) {
12877dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to create child process");
129d5dd3498SLuiz Capitulino         return;
130d5dd3498SLuiz Capitulino     }
131d5dd3498SLuiz Capitulino 
132d220a6dfSLuiz Capitulino     ga_wait_child(pid, &status, &local_err);
13384d18f06SMarkus Armbruster     if (local_err) {
13477dbc81bSMarkus Armbruster         error_propagate(errp, local_err);
135d220a6dfSLuiz Capitulino         return;
136d220a6dfSLuiz Capitulino     }
137d220a6dfSLuiz Capitulino 
138d220a6dfSLuiz Capitulino     if (!WIFEXITED(status)) {
13977dbc81bSMarkus Armbruster         error_setg(errp, "child process has terminated abnormally");
140d220a6dfSLuiz Capitulino         return;
141d220a6dfSLuiz Capitulino     }
142d220a6dfSLuiz Capitulino 
143d220a6dfSLuiz Capitulino     if (WEXITSTATUS(status)) {
14477dbc81bSMarkus Armbruster         error_setg(errp, "child process has failed to shutdown");
145d220a6dfSLuiz Capitulino         return;
146d220a6dfSLuiz Capitulino     }
147d220a6dfSLuiz Capitulino 
148085d8134SPeter Maydell     /* succeeded */
149c216e5adSMichael Roth }
150c216e5adSMichael Roth 
1512c958923SMichal Privoznik void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
152a1bca57fSLei Li {
153a1bca57fSLei Li     int ret;
154a1bca57fSLei Li     int status;
155a1bca57fSLei Li     pid_t pid;
156a1bca57fSLei Li     Error *local_err = NULL;
157a1bca57fSLei Li     struct timeval tv;
1585c6096e5SCornelia Huck     static const char hwclock_path[] = "/sbin/hwclock";
1595c6096e5SCornelia Huck     static int hwclock_available = -1;
1605c6096e5SCornelia Huck 
1615c6096e5SCornelia Huck     if (hwclock_available < 0) {
1625c6096e5SCornelia Huck         hwclock_available = (access(hwclock_path, X_OK) == 0);
1635c6096e5SCornelia Huck     }
1645c6096e5SCornelia Huck 
1655c6096e5SCornelia Huck     if (!hwclock_available) {
1665c6096e5SCornelia Huck         error_setg(errp, QERR_UNSUPPORTED);
1675c6096e5SCornelia Huck         return;
1685c6096e5SCornelia Huck     }
169a1bca57fSLei Li 
1702c958923SMichal Privoznik     /* If user has passed a time, validate and set it. */
1712c958923SMichal Privoznik     if (has_time) {
17200d2f370SMarc-André Lureau         GDate date = { 0, };
17300d2f370SMarc-André Lureau 
174a1bca57fSLei Li         /* year-2038 will overflow in case time_t is 32bit */
175a1bca57fSLei Li         if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) {
176a1bca57fSLei Li             error_setg(errp, "Time %" PRId64 " is too large", time_ns);
177a1bca57fSLei Li             return;
178a1bca57fSLei Li         }
179a1bca57fSLei Li 
180a1bca57fSLei Li         tv.tv_sec = time_ns / 1000000000;
181a1bca57fSLei Li         tv.tv_usec = (time_ns % 1000000000) / 1000;
18200d2f370SMarc-André Lureau         g_date_set_time_t(&date, tv.tv_sec);
18300d2f370SMarc-André Lureau         if (date.year < 1970 || date.year >= 2070) {
18400d2f370SMarc-André Lureau             error_setg_errno(errp, errno, "Invalid time");
18500d2f370SMarc-André Lureau             return;
18600d2f370SMarc-André Lureau         }
187a1bca57fSLei Li 
188a1bca57fSLei Li         ret = settimeofday(&tv, NULL);
189a1bca57fSLei Li         if (ret < 0) {
190a1bca57fSLei Li             error_setg_errno(errp, errno, "Failed to set time to guest");
191a1bca57fSLei Li             return;
192a1bca57fSLei Li         }
1932c958923SMichal Privoznik     }
194a1bca57fSLei Li 
1952c958923SMichal Privoznik     /* Now, if user has passed a time to set and the system time is set, we
1962c958923SMichal Privoznik      * just need to synchronize the hardware clock. However, if no time was
1972c958923SMichal Privoznik      * passed, user is requesting the opposite: set the system time from the
1981634df56SAmos Kong      * hardware clock (RTC). */
199a1bca57fSLei Li     pid = fork();
200a1bca57fSLei Li     if (pid == 0) {
201a1bca57fSLei Li         setsid();
202a1bca57fSLei Li         reopen_fd_to_null(0);
203a1bca57fSLei Li         reopen_fd_to_null(1);
204a1bca57fSLei Li         reopen_fd_to_null(2);
205a1bca57fSLei Li 
2062c958923SMichal Privoznik         /* Use '/sbin/hwclock -w' to set RTC from the system time,
2072c958923SMichal Privoznik          * or '/sbin/hwclock -s' to set the system time from RTC. */
208fcc41961SMarc-André Lureau         execl(hwclock_path, "hwclock", has_time ? "-w" : "-s", NULL);
209a1bca57fSLei Li         _exit(EXIT_FAILURE);
210a1bca57fSLei Li     } else if (pid < 0) {
211a1bca57fSLei Li         error_setg_errno(errp, errno, "failed to create child process");
212a1bca57fSLei Li         return;
213a1bca57fSLei Li     }
214a1bca57fSLei Li 
215a1bca57fSLei Li     ga_wait_child(pid, &status, &local_err);
21684d18f06SMarkus Armbruster     if (local_err) {
217a1bca57fSLei Li         error_propagate(errp, local_err);
218a1bca57fSLei Li         return;
219a1bca57fSLei Li     }
220a1bca57fSLei Li 
221a1bca57fSLei Li     if (!WIFEXITED(status)) {
222a1bca57fSLei Li         error_setg(errp, "child process has terminated abnormally");
223a1bca57fSLei Li         return;
224a1bca57fSLei Li     }
225a1bca57fSLei Li 
226a1bca57fSLei Li     if (WEXITSTATUS(status)) {
227a1bca57fSLei Li         error_setg(errp, "hwclock failed to set hardware clock to system time");
228a1bca57fSLei Li         return;
229a1bca57fSLei Li     }
230a1bca57fSLei Li }
231a1bca57fSLei Li 
232895b00f6SMarc-André Lureau typedef enum {
233895b00f6SMarc-André Lureau     RW_STATE_NEW,
234895b00f6SMarc-André Lureau     RW_STATE_READING,
235895b00f6SMarc-André Lureau     RW_STATE_WRITING,
236895b00f6SMarc-André Lureau } RwState;
237895b00f6SMarc-André Lureau 
2385d3586b8SPhilippe Mathieu-Daudé struct GuestFileHandle {
239c216e5adSMichael Roth     uint64_t id;
240c216e5adSMichael Roth     FILE *fh;
241895b00f6SMarc-André Lureau     RwState state;
242c216e5adSMichael Roth     QTAILQ_ENTRY(GuestFileHandle) next;
2435d3586b8SPhilippe Mathieu-Daudé };
244c216e5adSMichael Roth 
245c216e5adSMichael Roth static struct {
246c216e5adSMichael Roth     QTAILQ_HEAD(, GuestFileHandle) filehandles;
247b4fe97c8SDenis V. Lunev } guest_file_state = {
248b4fe97c8SDenis V. Lunev     .filehandles = QTAILQ_HEAD_INITIALIZER(guest_file_state.filehandles),
249b4fe97c8SDenis V. Lunev };
250c216e5adSMichael Roth 
25139097dafSMichael Roth static int64_t guest_file_handle_add(FILE *fh, Error **errp)
252c216e5adSMichael Roth {
253c216e5adSMichael Roth     GuestFileHandle *gfh;
25439097dafSMichael Roth     int64_t handle;
25539097dafSMichael Roth 
25639097dafSMichael Roth     handle = ga_get_fd_handle(ga_state, errp);
257a903f40cSMarkus Armbruster     if (handle < 0) {
258a903f40cSMarkus Armbruster         return -1;
25939097dafSMichael Roth     }
260c216e5adSMichael Roth 
261f3a06403SMarkus Armbruster     gfh = g_new0(GuestFileHandle, 1);
26239097dafSMichael Roth     gfh->id = handle;
263c216e5adSMichael Roth     gfh->fh = fh;
264c216e5adSMichael Roth     QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
26539097dafSMichael Roth 
26639097dafSMichael Roth     return handle;
267c216e5adSMichael Roth }
268c216e5adSMichael Roth 
2695d3586b8SPhilippe Mathieu-Daudé GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp)
270c216e5adSMichael Roth {
271c216e5adSMichael Roth     GuestFileHandle *gfh;
272c216e5adSMichael Roth 
273c216e5adSMichael Roth     QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next)
274c216e5adSMichael Roth     {
275c216e5adSMichael Roth         if (gfh->id == id) {
276c216e5adSMichael Roth             return gfh;
277c216e5adSMichael Roth         }
278c216e5adSMichael Roth     }
279c216e5adSMichael Roth 
28077dbc81bSMarkus Armbruster     error_setg(errp, "handle '%" PRId64 "' has not been found", id);
281c216e5adSMichael Roth     return NULL;
282c216e5adSMichael Roth }
283c216e5adSMichael Roth 
284c689b4f1SLaszlo Ersek typedef const char * const ccpc;
285c689b4f1SLaszlo Ersek 
2868fe6bbcaSLaszlo Ersek #ifndef O_BINARY
2878fe6bbcaSLaszlo Ersek #define O_BINARY 0
2888fe6bbcaSLaszlo Ersek #endif
2898fe6bbcaSLaszlo Ersek 
290c689b4f1SLaszlo Ersek /* http://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html */
291c689b4f1SLaszlo Ersek static const struct {
292c689b4f1SLaszlo Ersek     ccpc *forms;
293c689b4f1SLaszlo Ersek     int oflag_base;
294c689b4f1SLaszlo Ersek } guest_file_open_modes[] = {
2958fe6bbcaSLaszlo Ersek     { (ccpc[]){ "r",          NULL }, O_RDONLY                                 },
2968fe6bbcaSLaszlo Ersek     { (ccpc[]){ "rb",         NULL }, O_RDONLY                      | O_BINARY },
2978fe6bbcaSLaszlo Ersek     { (ccpc[]){ "w",          NULL }, O_WRONLY | O_CREAT | O_TRUNC             },
2988fe6bbcaSLaszlo Ersek     { (ccpc[]){ "wb",         NULL }, O_WRONLY | O_CREAT | O_TRUNC  | O_BINARY },
2998fe6bbcaSLaszlo Ersek     { (ccpc[]){ "a",          NULL }, O_WRONLY | O_CREAT | O_APPEND            },
3008fe6bbcaSLaszlo Ersek     { (ccpc[]){ "ab",         NULL }, O_WRONLY | O_CREAT | O_APPEND | O_BINARY },
3018fe6bbcaSLaszlo Ersek     { (ccpc[]){ "r+",         NULL }, O_RDWR                                   },
3028fe6bbcaSLaszlo Ersek     { (ccpc[]){ "rb+", "r+b", NULL }, O_RDWR                        | O_BINARY },
3038fe6bbcaSLaszlo Ersek     { (ccpc[]){ "w+",         NULL }, O_RDWR   | O_CREAT | O_TRUNC             },
3048fe6bbcaSLaszlo Ersek     { (ccpc[]){ "wb+", "w+b", NULL }, O_RDWR   | O_CREAT | O_TRUNC  | O_BINARY },
3058fe6bbcaSLaszlo Ersek     { (ccpc[]){ "a+",         NULL }, O_RDWR   | O_CREAT | O_APPEND            },
3068fe6bbcaSLaszlo Ersek     { (ccpc[]){ "ab+", "a+b", NULL }, O_RDWR   | O_CREAT | O_APPEND | O_BINARY }
307c689b4f1SLaszlo Ersek };
308c689b4f1SLaszlo Ersek 
309c689b4f1SLaszlo Ersek static int
31077dbc81bSMarkus Armbruster find_open_flag(const char *mode_str, Error **errp)
311c689b4f1SLaszlo Ersek {
312c689b4f1SLaszlo Ersek     unsigned mode;
313c689b4f1SLaszlo Ersek 
314c689b4f1SLaszlo Ersek     for (mode = 0; mode < ARRAY_SIZE(guest_file_open_modes); ++mode) {
315c689b4f1SLaszlo Ersek         ccpc *form;
316c689b4f1SLaszlo Ersek 
317c689b4f1SLaszlo Ersek         form = guest_file_open_modes[mode].forms;
318c689b4f1SLaszlo Ersek         while (*form != NULL && strcmp(*form, mode_str) != 0) {
319c689b4f1SLaszlo Ersek             ++form;
320c689b4f1SLaszlo Ersek         }
321c689b4f1SLaszlo Ersek         if (*form != NULL) {
322c689b4f1SLaszlo Ersek             break;
323c689b4f1SLaszlo Ersek         }
324c689b4f1SLaszlo Ersek     }
325c689b4f1SLaszlo Ersek 
326c689b4f1SLaszlo Ersek     if (mode == ARRAY_SIZE(guest_file_open_modes)) {
32777dbc81bSMarkus Armbruster         error_setg(errp, "invalid file open mode '%s'", mode_str);
328c689b4f1SLaszlo Ersek         return -1;
329c689b4f1SLaszlo Ersek     }
330c689b4f1SLaszlo Ersek     return guest_file_open_modes[mode].oflag_base | O_NOCTTY | O_NONBLOCK;
331c689b4f1SLaszlo Ersek }
332c689b4f1SLaszlo Ersek 
333c689b4f1SLaszlo Ersek #define DEFAULT_NEW_FILE_MODE (S_IRUSR | S_IWUSR | \
334c689b4f1SLaszlo Ersek                                S_IRGRP | S_IWGRP | \
335c689b4f1SLaszlo Ersek                                S_IROTH | S_IWOTH)
336c689b4f1SLaszlo Ersek 
337c689b4f1SLaszlo Ersek static FILE *
33877dbc81bSMarkus Armbruster safe_open_or_create(const char *path, const char *mode, Error **errp)
339c689b4f1SLaszlo Ersek {
340c689b4f1SLaszlo Ersek     int oflag;
34169f56c14SMarc-André Lureau     int fd = -1;
34269f56c14SMarc-André Lureau     FILE *f = NULL;
343c689b4f1SLaszlo Ersek 
34469f56c14SMarc-André Lureau     oflag = find_open_flag(mode, errp);
34569f56c14SMarc-André Lureau     if (oflag < 0) {
34669f56c14SMarc-André Lureau         goto end;
34769f56c14SMarc-André Lureau     }
348c689b4f1SLaszlo Ersek 
349c689b4f1SLaszlo Ersek     /* If the caller wants / allows creation of a new file, we implement it
350c689b4f1SLaszlo Ersek      * with a two step process: open() + (open() / fchmod()).
351c689b4f1SLaszlo Ersek      *
352c689b4f1SLaszlo Ersek      * First we insist on creating the file exclusively as a new file. If
353c689b4f1SLaszlo Ersek      * that succeeds, we're free to set any file-mode bits on it. (The
354c689b4f1SLaszlo Ersek      * motivation is that we want to set those file-mode bits independently
355c689b4f1SLaszlo Ersek      * of the current umask.)
356c689b4f1SLaszlo Ersek      *
357c689b4f1SLaszlo Ersek      * If the exclusive creation fails because the file already exists
358c689b4f1SLaszlo Ersek      * (EEXIST is not possible for any other reason), we just attempt to
359c689b4f1SLaszlo Ersek      * open the file, but in this case we won't be allowed to change the
360c689b4f1SLaszlo Ersek      * file-mode bits on the preexistent file.
361c689b4f1SLaszlo Ersek      *
362c689b4f1SLaszlo Ersek      * The pathname should never disappear between the two open()s in
363c689b4f1SLaszlo Ersek      * practice. If it happens, then someone very likely tried to race us.
364c689b4f1SLaszlo Ersek      * In this case just go ahead and report the ENOENT from the second
365c689b4f1SLaszlo Ersek      * open() to the caller.
366c689b4f1SLaszlo Ersek      *
367c689b4f1SLaszlo Ersek      * If the caller wants to open a preexistent file, then the first
368c689b4f1SLaszlo Ersek      * open() is decisive and its third argument is ignored, and the second
369c689b4f1SLaszlo Ersek      * open() and the fchmod() are never called.
370c689b4f1SLaszlo Ersek      */
3711a89a17bSMarc-André Lureau     fd = qga_open_cloexec(path, oflag | ((oflag & O_CREAT) ? O_EXCL : 0), 0);
372c689b4f1SLaszlo Ersek     if (fd == -1 && errno == EEXIST) {
373c689b4f1SLaszlo Ersek         oflag &= ~(unsigned)O_CREAT;
3741a89a17bSMarc-André Lureau         fd = qga_open_cloexec(path, oflag, 0);
375c689b4f1SLaszlo Ersek     }
376c689b4f1SLaszlo Ersek     if (fd == -1) {
37769f56c14SMarc-André Lureau         error_setg_errno(errp, errno,
37869f56c14SMarc-André Lureau                          "failed to open file '%s' (mode: '%s')",
37969f56c14SMarc-André Lureau                          path, mode);
38069f56c14SMarc-André Lureau         goto end;
38169f56c14SMarc-André Lureau     }
38269f56c14SMarc-André Lureau 
383c689b4f1SLaszlo Ersek     if ((oflag & O_CREAT) && fchmod(fd, DEFAULT_NEW_FILE_MODE) == -1) {
38469f56c14SMarc-André Lureau         error_setg_errno(errp, errno, "failed to set permission "
385c689b4f1SLaszlo Ersek                          "0%03o on new file '%s' (mode: '%s')",
386c689b4f1SLaszlo Ersek                          (unsigned)DEFAULT_NEW_FILE_MODE, path, mode);
38769f56c14SMarc-André Lureau         goto end;
38869f56c14SMarc-André Lureau     }
389c689b4f1SLaszlo Ersek 
390c689b4f1SLaszlo Ersek     f = fdopen(fd, mode);
391c689b4f1SLaszlo Ersek     if (f == NULL) {
39269f56c14SMarc-André Lureau         error_setg_errno(errp, errno, "failed to associate stdio stream with "
39369f56c14SMarc-André Lureau                          "file descriptor %d, file '%s' (mode: '%s')",
39469f56c14SMarc-André Lureau                          fd, path, mode);
395c689b4f1SLaszlo Ersek     }
396c689b4f1SLaszlo Ersek 
39769f56c14SMarc-André Lureau end:
39869f56c14SMarc-André Lureau     if (f == NULL && fd != -1) {
399c689b4f1SLaszlo Ersek         close(fd);
4002b720018SLaszlo Ersek         if (oflag & O_CREAT) {
4012b720018SLaszlo Ersek             unlink(path);
4022b720018SLaszlo Ersek         }
403c689b4f1SLaszlo Ersek     }
40469f56c14SMarc-André Lureau     return f;
405c689b4f1SLaszlo Ersek }
406c689b4f1SLaszlo Ersek 
40777dbc81bSMarkus Armbruster int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode,
40877dbc81bSMarkus Armbruster                             Error **errp)
409c216e5adSMichael Roth {
410c216e5adSMichael Roth     FILE *fh;
411c689b4f1SLaszlo Ersek     Error *local_err = NULL;
41285b6f6f5SSimon Zolin     int64_t handle;
413c216e5adSMichael Roth 
414c216e5adSMichael Roth     if (!has_mode) {
415c216e5adSMichael Roth         mode = "r";
416c216e5adSMichael Roth     }
417c216e5adSMichael Roth     slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
418c689b4f1SLaszlo Ersek     fh = safe_open_or_create(path, mode, &local_err);
419c689b4f1SLaszlo Ersek     if (local_err != NULL) {
42077dbc81bSMarkus Armbruster         error_propagate(errp, local_err);
421c216e5adSMichael Roth         return -1;
422c216e5adSMichael Roth     }
423c216e5adSMichael Roth 
424c216e5adSMichael Roth     /* set fd non-blocking to avoid common use cases (like reading from a
425c216e5adSMichael Roth      * named pipe) from hanging the agent
426c216e5adSMichael Roth      */
427b0a8f9adSMarc-André Lureau     if (!g_unix_set_fd_nonblocking(fileno(fh), true, NULL)) {
428b0a8f9adSMarc-André Lureau         fclose(fh);
429b0a8f9adSMarc-André Lureau         error_setg_errno(errp, errno, "Failed to set FD nonblocking");
430b0a8f9adSMarc-André Lureau         return -1;
431b0a8f9adSMarc-André Lureau     }
432c216e5adSMichael Roth 
43377dbc81bSMarkus Armbruster     handle = guest_file_handle_add(fh, errp);
434a903f40cSMarkus Armbruster     if (handle < 0) {
43539097dafSMichael Roth         fclose(fh);
43639097dafSMichael Roth         return -1;
43739097dafSMichael Roth     }
43839097dafSMichael Roth 
439d607a523SStefan Weil     slog("guest-file-open, handle: %" PRId64, handle);
44039097dafSMichael Roth     return handle;
441c216e5adSMichael Roth }
442c216e5adSMichael Roth 
44377dbc81bSMarkus Armbruster void qmp_guest_file_close(int64_t handle, Error **errp)
444c216e5adSMichael Roth {
44577dbc81bSMarkus Armbruster     GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
446c216e5adSMichael Roth     int ret;
447c216e5adSMichael Roth 
448d607a523SStefan Weil     slog("guest-file-close called, handle: %" PRId64, handle);
449c216e5adSMichael Roth     if (!gfh) {
450c216e5adSMichael Roth         return;
451c216e5adSMichael Roth     }
452c216e5adSMichael Roth 
453c216e5adSMichael Roth     ret = fclose(gfh->fh);
4543ac4b7c5SLuiz Capitulino     if (ret == EOF) {
45577dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to close handle");
456c216e5adSMichael Roth         return;
457c216e5adSMichael Roth     }
458c216e5adSMichael Roth 
459c216e5adSMichael Roth     QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
460c216e5adSMichael Roth     g_free(gfh);
461c216e5adSMichael Roth }
462c216e5adSMichael Roth 
463ead83a13SPhilippe Mathieu-Daudé GuestFileRead *guest_file_read_unsafe(GuestFileHandle *gfh,
46477dbc81bSMarkus Armbruster                                       int64_t count, Error **errp)
465c216e5adSMichael Roth {
466c216e5adSMichael Roth     GuestFileRead *read_data = NULL;
467c216e5adSMichael Roth     guchar *buf;
468ead83a13SPhilippe Mathieu-Daudé     FILE *fh = gfh->fh;
469c216e5adSMichael Roth     size_t read_count;
470c216e5adSMichael Roth 
471895b00f6SMarc-André Lureau     /* explicitly flush when switching from writing to reading */
472895b00f6SMarc-André Lureau     if (gfh->state == RW_STATE_WRITING) {
473895b00f6SMarc-André Lureau         int ret = fflush(fh);
474895b00f6SMarc-André Lureau         if (ret == EOF) {
475895b00f6SMarc-André Lureau             error_setg_errno(errp, errno, "failed to flush file");
476895b00f6SMarc-André Lureau             return NULL;
477895b00f6SMarc-André Lureau         }
478895b00f6SMarc-André Lureau         gfh->state = RW_STATE_NEW;
479895b00f6SMarc-André Lureau     }
480895b00f6SMarc-André Lureau 
481c216e5adSMichael Roth     buf = g_malloc0(count + 1);
482c216e5adSMichael Roth     read_count = fread(buf, 1, count, fh);
483c216e5adSMichael Roth     if (ferror(fh)) {
48477dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to read file");
485c216e5adSMichael Roth     } else {
486c216e5adSMichael Roth         buf[read_count] = 0;
487f3a06403SMarkus Armbruster         read_data = g_new0(GuestFileRead, 1);
488c216e5adSMichael Roth         read_data->count = read_count;
489c216e5adSMichael Roth         read_data->eof = feof(fh);
490c216e5adSMichael Roth         if (read_count) {
491c216e5adSMichael Roth             read_data->buf_b64 = g_base64_encode(buf, read_count);
492c216e5adSMichael Roth         }
493895b00f6SMarc-André Lureau         gfh->state = RW_STATE_READING;
494c216e5adSMichael Roth     }
495c216e5adSMichael Roth     g_free(buf);
496c216e5adSMichael Roth     clearerr(fh);
497c216e5adSMichael Roth 
498c216e5adSMichael Roth     return read_data;
499c216e5adSMichael Roth }
500c216e5adSMichael Roth 
501c216e5adSMichael Roth GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
50277dbc81bSMarkus Armbruster                                      bool has_count, int64_t count,
50377dbc81bSMarkus Armbruster                                      Error **errp)
504c216e5adSMichael Roth {
505c216e5adSMichael Roth     GuestFileWrite *write_data = NULL;
506c216e5adSMichael Roth     guchar *buf;
507c216e5adSMichael Roth     gsize buf_len;
508c216e5adSMichael Roth     int write_count;
50977dbc81bSMarkus Armbruster     GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
510c216e5adSMichael Roth     FILE *fh;
511c216e5adSMichael Roth 
512c216e5adSMichael Roth     if (!gfh) {
513c216e5adSMichael Roth         return NULL;
514c216e5adSMichael Roth     }
515c216e5adSMichael Roth 
516c216e5adSMichael Roth     fh = gfh->fh;
517895b00f6SMarc-André Lureau 
518895b00f6SMarc-André Lureau     if (gfh->state == RW_STATE_READING) {
519895b00f6SMarc-André Lureau         int ret = fseek(fh, 0, SEEK_CUR);
520895b00f6SMarc-André Lureau         if (ret == -1) {
521895b00f6SMarc-André Lureau             error_setg_errno(errp, errno, "failed to seek file");
522895b00f6SMarc-André Lureau             return NULL;
523895b00f6SMarc-André Lureau         }
524895b00f6SMarc-André Lureau         gfh->state = RW_STATE_NEW;
525895b00f6SMarc-André Lureau     }
526895b00f6SMarc-André Lureau 
527920639caSDaniel P. Berrange     buf = qbase64_decode(buf_b64, -1, &buf_len, errp);
528920639caSDaniel P. Berrange     if (!buf) {
529920639caSDaniel P. Berrange         return NULL;
530920639caSDaniel P. Berrange     }
531c216e5adSMichael Roth 
532c216e5adSMichael Roth     if (!has_count) {
533c216e5adSMichael Roth         count = buf_len;
534c216e5adSMichael Roth     } else if (count < 0 || count > buf_len) {
53577dbc81bSMarkus Armbruster         error_setg(errp, "value '%" PRId64 "' is invalid for argument count",
536db3edb66SLuiz Capitulino                    count);
537c216e5adSMichael Roth         g_free(buf);
538c216e5adSMichael Roth         return NULL;
539c216e5adSMichael Roth     }
540c216e5adSMichael Roth 
541c216e5adSMichael Roth     write_count = fwrite(buf, 1, count, fh);
542c216e5adSMichael Roth     if (ferror(fh)) {
54377dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to write to file");
544d607a523SStefan Weil         slog("guest-file-write failed, handle: %" PRId64, handle);
545c216e5adSMichael Roth     } else {
546f3a06403SMarkus Armbruster         write_data = g_new0(GuestFileWrite, 1);
547c216e5adSMichael Roth         write_data->count = write_count;
548c216e5adSMichael Roth         write_data->eof = feof(fh);
549895b00f6SMarc-André Lureau         gfh->state = RW_STATE_WRITING;
550c216e5adSMichael Roth     }
551c216e5adSMichael Roth     g_free(buf);
552c216e5adSMichael Roth     clearerr(fh);
553c216e5adSMichael Roth 
554c216e5adSMichael Roth     return write_data;
555c216e5adSMichael Roth }
556c216e5adSMichael Roth 
557c216e5adSMichael Roth struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
5580b4b4938SEric Blake                                           GuestFileWhence *whence_code,
5590b4b4938SEric Blake                                           Error **errp)
560c216e5adSMichael Roth {
56177dbc81bSMarkus Armbruster     GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
562c216e5adSMichael Roth     GuestFileSeek *seek_data = NULL;
563c216e5adSMichael Roth     FILE *fh;
564c216e5adSMichael Roth     int ret;
5650a982b1bSEric Blake     int whence;
5660b4b4938SEric Blake     Error *err = NULL;
567c216e5adSMichael Roth 
568c216e5adSMichael Roth     if (!gfh) {
569c216e5adSMichael Roth         return NULL;
570c216e5adSMichael Roth     }
571c216e5adSMichael Roth 
5720a982b1bSEric Blake     /* We stupidly exposed 'whence':'int' in our qapi */
5730b4b4938SEric Blake     whence = ga_parse_whence(whence_code, &err);
5740b4b4938SEric Blake     if (err) {
5750b4b4938SEric Blake         error_propagate(errp, err);
5760a982b1bSEric Blake         return NULL;
5770a982b1bSEric Blake     }
5780a982b1bSEric Blake 
579c216e5adSMichael Roth     fh = gfh->fh;
580c216e5adSMichael Roth     ret = fseek(fh, offset, whence);
581c216e5adSMichael Roth     if (ret == -1) {
58277dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to seek file");
583895b00f6SMarc-André Lureau         if (errno == ESPIPE) {
584895b00f6SMarc-André Lureau             /* file is non-seekable, stdio shouldn't be buffering anyways */
585895b00f6SMarc-André Lureau             gfh->state = RW_STATE_NEW;
586895b00f6SMarc-André Lureau         }
587c216e5adSMichael Roth     } else {
58810b7c5ddSMarkus Armbruster         seek_data = g_new0(GuestFileSeek, 1);
589c216e5adSMichael Roth         seek_data->position = ftell(fh);
590c216e5adSMichael Roth         seek_data->eof = feof(fh);
591895b00f6SMarc-André Lureau         gfh->state = RW_STATE_NEW;
592c216e5adSMichael Roth     }
593c216e5adSMichael Roth     clearerr(fh);
594c216e5adSMichael Roth 
595c216e5adSMichael Roth     return seek_data;
596c216e5adSMichael Roth }
597c216e5adSMichael Roth 
59877dbc81bSMarkus Armbruster void qmp_guest_file_flush(int64_t handle, Error **errp)
599c216e5adSMichael Roth {
60077dbc81bSMarkus Armbruster     GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
601c216e5adSMichael Roth     FILE *fh;
602c216e5adSMichael Roth     int ret;
603c216e5adSMichael Roth 
604c216e5adSMichael Roth     if (!gfh) {
605c216e5adSMichael Roth         return;
606c216e5adSMichael Roth     }
607c216e5adSMichael Roth 
608c216e5adSMichael Roth     fh = gfh->fh;
609c216e5adSMichael Roth     ret = fflush(fh);
610c216e5adSMichael Roth     if (ret == EOF) {
61177dbc81bSMarkus Armbruster         error_setg_errno(errp, errno, "failed to flush file");
612895b00f6SMarc-André Lureau     } else {
613895b00f6SMarc-André Lureau         gfh->state = RW_STATE_NEW;
614c216e5adSMichael Roth     }
615c216e5adSMichael Roth }
616c216e5adSMichael Roth 
617eab5fd59SPaolo Bonzini #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
618518b0d80SAlexander Ivanov void free_fs_mount_list(FsMountList *mounts)
619c216e5adSMichael Roth {
620af02203fSPaolo Bonzini      FsMount *mount, *temp;
621c216e5adSMichael Roth 
6229e8aded4SMichael Roth      if (!mounts) {
6239e8aded4SMichael Roth          return;
6249e8aded4SMichael Roth      }
6259e8aded4SMichael Roth 
6269e8aded4SMichael Roth      QTAILQ_FOREACH_SAFE(mount, mounts, next, temp) {
6279e8aded4SMichael Roth          QTAILQ_REMOVE(mounts, mount, next);
628c216e5adSMichael Roth          g_free(mount->dirname);
629c216e5adSMichael Roth          g_free(mount->devtype);
630c216e5adSMichael Roth          g_free(mount);
631c216e5adSMichael Roth      }
6329e8aded4SMichael Roth }
633eab5fd59SPaolo Bonzini #endif
634eab5fd59SPaolo Bonzini 
635eab5fd59SPaolo Bonzini #if defined(CONFIG_FSFREEZE)
636bad0001eSAlexander Ivanov typedef enum {
637bad0001eSAlexander Ivanov     FSFREEZE_HOOK_THAW = 0,
638bad0001eSAlexander Ivanov     FSFREEZE_HOOK_FREEZE,
639bad0001eSAlexander Ivanov } FsfreezeHookArg;
640bad0001eSAlexander Ivanov 
641bad0001eSAlexander Ivanov static const char *fsfreeze_hook_arg_string[] = {
642bad0001eSAlexander Ivanov     "thaw",
643bad0001eSAlexander Ivanov     "freeze",
644bad0001eSAlexander Ivanov };
645bad0001eSAlexander Ivanov 
646bad0001eSAlexander Ivanov static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp)
647bad0001eSAlexander Ivanov {
648bad0001eSAlexander Ivanov     int status;
649bad0001eSAlexander Ivanov     pid_t pid;
650bad0001eSAlexander Ivanov     const char *hook;
651bad0001eSAlexander Ivanov     const char *arg_str = fsfreeze_hook_arg_string[arg];
652bad0001eSAlexander Ivanov     Error *local_err = NULL;
653bad0001eSAlexander Ivanov 
654bad0001eSAlexander Ivanov     hook = ga_fsfreeze_hook(ga_state);
655bad0001eSAlexander Ivanov     if (!hook) {
656bad0001eSAlexander Ivanov         return;
657bad0001eSAlexander Ivanov     }
658bad0001eSAlexander Ivanov     if (access(hook, X_OK) != 0) {
659bad0001eSAlexander Ivanov         error_setg_errno(errp, errno, "can't access fsfreeze hook '%s'", hook);
660bad0001eSAlexander Ivanov         return;
661bad0001eSAlexander Ivanov     }
662bad0001eSAlexander Ivanov 
663bad0001eSAlexander Ivanov     slog("executing fsfreeze hook with arg '%s'", arg_str);
664bad0001eSAlexander Ivanov     pid = fork();
665bad0001eSAlexander Ivanov     if (pid == 0) {
666bad0001eSAlexander Ivanov         setsid();
667bad0001eSAlexander Ivanov         reopen_fd_to_null(0);
668bad0001eSAlexander Ivanov         reopen_fd_to_null(1);
669bad0001eSAlexander Ivanov         reopen_fd_to_null(2);
670bad0001eSAlexander Ivanov 
671bad0001eSAlexander Ivanov         execl(hook, hook, arg_str, NULL);
672bad0001eSAlexander Ivanov         _exit(EXIT_FAILURE);
673bad0001eSAlexander Ivanov     } else if (pid < 0) {
674bad0001eSAlexander Ivanov         error_setg_errno(errp, errno, "failed to create child process");
675bad0001eSAlexander Ivanov         return;
676bad0001eSAlexander Ivanov     }
677bad0001eSAlexander Ivanov 
678bad0001eSAlexander Ivanov     ga_wait_child(pid, &status, &local_err);
679bad0001eSAlexander Ivanov     if (local_err) {
680bad0001eSAlexander Ivanov         error_propagate(errp, local_err);
681bad0001eSAlexander Ivanov         return;
682bad0001eSAlexander Ivanov     }
683bad0001eSAlexander Ivanov 
684bad0001eSAlexander Ivanov     if (!WIFEXITED(status)) {
685bad0001eSAlexander Ivanov         error_setg(errp, "fsfreeze hook has terminated abnormally");
686bad0001eSAlexander Ivanov         return;
687bad0001eSAlexander Ivanov     }
688bad0001eSAlexander Ivanov 
689bad0001eSAlexander Ivanov     status = WEXITSTATUS(status);
690bad0001eSAlexander Ivanov     if (status) {
691bad0001eSAlexander Ivanov         error_setg(errp, "fsfreeze hook has failed with status %d", status);
692bad0001eSAlexander Ivanov         return;
693bad0001eSAlexander Ivanov     }
694bad0001eSAlexander Ivanov }
695bad0001eSAlexander Ivanov 
696bad0001eSAlexander Ivanov /*
697bad0001eSAlexander Ivanov  * Return status of freeze/thaw
698bad0001eSAlexander Ivanov  */
699bad0001eSAlexander Ivanov GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
700bad0001eSAlexander Ivanov {
701bad0001eSAlexander Ivanov     if (ga_is_frozen(ga_state)) {
702bad0001eSAlexander Ivanov         return GUEST_FSFREEZE_STATUS_FROZEN;
703bad0001eSAlexander Ivanov     }
704bad0001eSAlexander Ivanov 
705bad0001eSAlexander Ivanov     return GUEST_FSFREEZE_STATUS_THAWED;
706bad0001eSAlexander Ivanov }
707bad0001eSAlexander Ivanov 
708bad0001eSAlexander Ivanov int64_t qmp_guest_fsfreeze_freeze(Error **errp)
709bad0001eSAlexander Ivanov {
710bad0001eSAlexander Ivanov     return qmp_guest_fsfreeze_freeze_list(false, NULL, errp);
711bad0001eSAlexander Ivanov }
712bad0001eSAlexander Ivanov 
713bad0001eSAlexander Ivanov int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
714bad0001eSAlexander Ivanov                                        strList *mountpoints,
715bad0001eSAlexander Ivanov                                        Error **errp)
716bad0001eSAlexander Ivanov {
717bad0001eSAlexander Ivanov     int ret;
718bad0001eSAlexander Ivanov     FsMountList mounts;
719bad0001eSAlexander Ivanov     Error *local_err = NULL;
720bad0001eSAlexander Ivanov 
721bad0001eSAlexander Ivanov     slog("guest-fsfreeze called");
722bad0001eSAlexander Ivanov 
723bad0001eSAlexander Ivanov     execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err);
724bad0001eSAlexander Ivanov     if (local_err) {
725bad0001eSAlexander Ivanov         error_propagate(errp, local_err);
726bad0001eSAlexander Ivanov         return -1;
727bad0001eSAlexander Ivanov     }
728bad0001eSAlexander Ivanov 
729bad0001eSAlexander Ivanov     QTAILQ_INIT(&mounts);
730bad0001eSAlexander Ivanov     if (!build_fs_mount_list(&mounts, &local_err)) {
731bad0001eSAlexander Ivanov         error_propagate(errp, local_err);
732bad0001eSAlexander Ivanov         return -1;
733bad0001eSAlexander Ivanov     }
734bad0001eSAlexander Ivanov 
735bad0001eSAlexander Ivanov     /* cannot risk guest agent blocking itself on a write in this state */
736bad0001eSAlexander Ivanov     ga_set_frozen(ga_state);
737bad0001eSAlexander Ivanov 
738bad0001eSAlexander Ivanov     ret = qmp_guest_fsfreeze_do_freeze_list(has_mountpoints, mountpoints,
739bad0001eSAlexander Ivanov                                             mounts, errp);
740bad0001eSAlexander Ivanov 
741bad0001eSAlexander Ivanov     free_fs_mount_list(&mounts);
742bad0001eSAlexander Ivanov     /* We may not issue any FIFREEZE here.
743bad0001eSAlexander Ivanov      * Just unset ga_state here and ready for the next call.
744bad0001eSAlexander Ivanov      */
745bad0001eSAlexander Ivanov     if (ret == 0) {
746bad0001eSAlexander Ivanov         ga_unset_frozen(ga_state);
747bad0001eSAlexander Ivanov     } else if (ret < 0) {
748bad0001eSAlexander Ivanov         qmp_guest_fsfreeze_thaw(NULL);
749bad0001eSAlexander Ivanov     }
750bad0001eSAlexander Ivanov     return ret;
751bad0001eSAlexander Ivanov }
752bad0001eSAlexander Ivanov 
753bad0001eSAlexander Ivanov int64_t qmp_guest_fsfreeze_thaw(Error **errp)
754bad0001eSAlexander Ivanov {
755bad0001eSAlexander Ivanov     int ret;
756bad0001eSAlexander Ivanov 
757bad0001eSAlexander Ivanov     ret = qmp_guest_fsfreeze_do_thaw(errp);
758bad0001eSAlexander Ivanov     if (ret >= 0) {
759bad0001eSAlexander Ivanov         ga_unset_frozen(ga_state);
760bad0001eSAlexander Ivanov         execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp);
761bad0001eSAlexander Ivanov     } else {
762bad0001eSAlexander Ivanov         ret = 0;
763bad0001eSAlexander Ivanov     }
764bad0001eSAlexander Ivanov 
765bad0001eSAlexander Ivanov     return ret;
766bad0001eSAlexander Ivanov }
767bad0001eSAlexander Ivanov 
768bad0001eSAlexander Ivanov static void guest_fsfreeze_cleanup(void)
769bad0001eSAlexander Ivanov {
770bad0001eSAlexander Ivanov     Error *err = NULL;
771bad0001eSAlexander Ivanov 
772bad0001eSAlexander Ivanov     if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) {
773bad0001eSAlexander Ivanov         qmp_guest_fsfreeze_thaw(&err);
774bad0001eSAlexander Ivanov         if (err) {
775bad0001eSAlexander Ivanov             slog("failed to clean up frozen filesystems: %s",
776bad0001eSAlexander Ivanov                  error_get_pretty(err));
777bad0001eSAlexander Ivanov             error_free(err);
778bad0001eSAlexander Ivanov         }
779bad0001eSAlexander Ivanov     }
780bad0001eSAlexander Ivanov }
781bad0001eSAlexander Ivanov #endif
782bad0001eSAlexander Ivanov 
783bad0001eSAlexander Ivanov /* linux-specific implementations. avoid this if at all possible. */
784bad0001eSAlexander Ivanov #if defined(__linux__)
785bad0001eSAlexander Ivanov #if defined(CONFIG_FSFREEZE)
786c216e5adSMichael Roth 
78746d4c572STomoki Sekiyama static char *get_pci_driver(char const *syspath, int pathlen, Error **errp)
78846d4c572STomoki Sekiyama {
78946d4c572STomoki Sekiyama     char *path;
79046d4c572STomoki Sekiyama     char *dpath;
79146d4c572STomoki Sekiyama     char *driver = NULL;
79246d4c572STomoki Sekiyama     char buf[PATH_MAX];
79346d4c572STomoki Sekiyama     ssize_t len;
79446d4c572STomoki Sekiyama 
79546d4c572STomoki Sekiyama     path = g_strndup(syspath, pathlen);
79646d4c572STomoki Sekiyama     dpath = g_strdup_printf("%s/driver", path);
79746d4c572STomoki Sekiyama     len = readlink(dpath, buf, sizeof(buf) - 1);
79846d4c572STomoki Sekiyama     if (len != -1) {
79946d4c572STomoki Sekiyama         buf[len] = 0;
8003e015d81SJulia Suvorova         driver = g_path_get_basename(buf);
80146d4c572STomoki Sekiyama     }
80246d4c572STomoki Sekiyama     g_free(dpath);
80346d4c572STomoki Sekiyama     g_free(path);
80446d4c572STomoki Sekiyama     return driver;
80546d4c572STomoki Sekiyama }
80646d4c572STomoki Sekiyama 
80746d4c572STomoki Sekiyama static int compare_uint(const void *_a, const void *_b)
80846d4c572STomoki Sekiyama {
80946d4c572STomoki Sekiyama     unsigned int a = *(unsigned int *)_a;
81046d4c572STomoki Sekiyama     unsigned int b = *(unsigned int *)_b;
81146d4c572STomoki Sekiyama 
81246d4c572STomoki Sekiyama     return a < b ? -1 : a > b ? 1 : 0;
81346d4c572STomoki Sekiyama }
81446d4c572STomoki Sekiyama 
81546d4c572STomoki Sekiyama /* Walk the specified sysfs and build a sorted list of host or ata numbers */
81646d4c572STomoki Sekiyama static int build_hosts(char const *syspath, char const *host, bool ata,
81746d4c572STomoki Sekiyama                        unsigned int *hosts, int hosts_max, Error **errp)
81846d4c572STomoki Sekiyama {
81946d4c572STomoki Sekiyama     char *path;
82046d4c572STomoki Sekiyama     DIR *dir;
82146d4c572STomoki Sekiyama     struct dirent *entry;
82246d4c572STomoki Sekiyama     int i = 0;
82346d4c572STomoki Sekiyama 
82446d4c572STomoki Sekiyama     path = g_strndup(syspath, host - syspath);
82546d4c572STomoki Sekiyama     dir = opendir(path);
82646d4c572STomoki Sekiyama     if (!dir) {
82746d4c572STomoki Sekiyama         error_setg_errno(errp, errno, "opendir(\"%s\")", path);
82846d4c572STomoki Sekiyama         g_free(path);
82946d4c572STomoki Sekiyama         return -1;
83046d4c572STomoki Sekiyama     }
83146d4c572STomoki Sekiyama 
83246d4c572STomoki Sekiyama     while (i < hosts_max) {
83346d4c572STomoki Sekiyama         entry = readdir(dir);
83446d4c572STomoki Sekiyama         if (!entry) {
83546d4c572STomoki Sekiyama             break;
83646d4c572STomoki Sekiyama         }
83746d4c572STomoki Sekiyama         if (ata && sscanf(entry->d_name, "ata%d", hosts + i) == 1) {
83846d4c572STomoki Sekiyama             ++i;
83946d4c572STomoki Sekiyama         } else if (!ata && sscanf(entry->d_name, "host%d", hosts + i) == 1) {
84046d4c572STomoki Sekiyama             ++i;
84146d4c572STomoki Sekiyama         }
84246d4c572STomoki Sekiyama     }
84346d4c572STomoki Sekiyama 
84446d4c572STomoki Sekiyama     qsort(hosts, i, sizeof(hosts[0]), compare_uint);
84546d4c572STomoki Sekiyama 
84646d4c572STomoki Sekiyama     g_free(path);
84746d4c572STomoki Sekiyama     closedir(dir);
84846d4c572STomoki Sekiyama     return i;
84946d4c572STomoki Sekiyama }
85046d4c572STomoki Sekiyama 
851d9fe4f0fSThomas Huth /*
852d9fe4f0fSThomas Huth  * Store disk device info for devices on the PCI bus.
853d9fe4f0fSThomas Huth  * Returns true if information has been stored, or false for failure.
854d9fe4f0fSThomas Huth  */
855d9fe4f0fSThomas Huth static bool build_guest_fsinfo_for_pci_dev(char const *syspath,
856d9fe4f0fSThomas Huth                                            GuestDiskAddress *disk,
85746d4c572STomoki Sekiyama                                            Error **errp)
85846d4c572STomoki Sekiyama {
85946d4c572STomoki Sekiyama     unsigned int pci[4], host, hosts[8], tgt[3];
86046d4c572STomoki Sekiyama     int i, nhosts = 0, pcilen;
861d9fe4f0fSThomas Huth     GuestPCIAddress *pciaddr = disk->pci_controller;
86246d4c572STomoki Sekiyama     bool has_ata = false, has_host = false, has_tgt = false;
86346d4c572STomoki Sekiyama     char *p, *q, *driver = NULL;
864d9fe4f0fSThomas Huth     bool ret = false;
86546d4c572STomoki Sekiyama 
86646d4c572STomoki Sekiyama     p = strstr(syspath, "/devices/pci");
86746d4c572STomoki Sekiyama     if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n",
86846d4c572STomoki Sekiyama                      pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) {
869743c71d0SMarc-André Lureau         g_debug("only pci device is supported: sysfs path '%s'", syspath);
870d9fe4f0fSThomas Huth         return false;
87146d4c572STomoki Sekiyama     }
87246d4c572STomoki Sekiyama 
873743c71d0SMarc-André Lureau     p += 12 + pcilen;
874743c71d0SMarc-André Lureau     while (true) {
875743c71d0SMarc-André Lureau         driver = get_pci_driver(syspath, p - syspath, errp);
876743c71d0SMarc-André Lureau         if (driver && (g_str_equal(driver, "ata_piix") ||
877743c71d0SMarc-André Lureau                        g_str_equal(driver, "sym53c8xx") ||
878743c71d0SMarc-André Lureau                        g_str_equal(driver, "virtio-pci") ||
879d48f61c8Szhenwei pi                        g_str_equal(driver, "ahci") ||
880d48f61c8Szhenwei pi                        g_str_equal(driver, "nvme"))) {
881743c71d0SMarc-André Lureau             break;
882743c71d0SMarc-André Lureau         }
883743c71d0SMarc-André Lureau 
884bb23a736SMarc-André Lureau         g_free(driver);
885743c71d0SMarc-André Lureau         if (sscanf(p, "/%x:%x:%x.%x%n",
886743c71d0SMarc-André Lureau                           pci, pci + 1, pci + 2, pci + 3, &pcilen) == 4) {
887743c71d0SMarc-André Lureau             p += pcilen;
888743c71d0SMarc-André Lureau             continue;
889743c71d0SMarc-André Lureau         }
890743c71d0SMarc-André Lureau 
891743c71d0SMarc-André Lureau         g_debug("unsupported driver or sysfs path '%s'", syspath);
892d9fe4f0fSThomas Huth         return false;
89346d4c572STomoki Sekiyama     }
89446d4c572STomoki Sekiyama 
89546d4c572STomoki Sekiyama     p = strstr(syspath, "/target");
89646d4c572STomoki Sekiyama     if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u",
89746d4c572STomoki Sekiyama                     tgt, tgt + 1, tgt + 2) == 3) {
89846d4c572STomoki Sekiyama         has_tgt = true;
89946d4c572STomoki Sekiyama     }
90046d4c572STomoki Sekiyama 
90146d4c572STomoki Sekiyama     p = strstr(syspath, "/ata");
90246d4c572STomoki Sekiyama     if (p) {
90346d4c572STomoki Sekiyama         q = p + 4;
90446d4c572STomoki Sekiyama         has_ata = true;
90546d4c572STomoki Sekiyama     } else {
90646d4c572STomoki Sekiyama         p = strstr(syspath, "/host");
90746d4c572STomoki Sekiyama         q = p + 5;
90846d4c572STomoki Sekiyama     }
90946d4c572STomoki Sekiyama     if (p && sscanf(q, "%u", &host) == 1) {
91046d4c572STomoki Sekiyama         has_host = true;
91146d4c572STomoki Sekiyama         nhosts = build_hosts(syspath, p, has_ata, hosts,
91201a6df1bSPhilippe Mathieu-Daudé                              ARRAY_SIZE(hosts), errp);
91346d4c572STomoki Sekiyama         if (nhosts < 0) {
91446d4c572STomoki Sekiyama             goto cleanup;
91546d4c572STomoki Sekiyama         }
91646d4c572STomoki Sekiyama     }
91746d4c572STomoki Sekiyama 
91846d4c572STomoki Sekiyama     pciaddr->domain = pci[0];
91946d4c572STomoki Sekiyama     pciaddr->bus = pci[1];
92046d4c572STomoki Sekiyama     pciaddr->slot = pci[2];
92146d4c572STomoki Sekiyama     pciaddr->function = pci[3];
92246d4c572STomoki Sekiyama 
92346d4c572STomoki Sekiyama     if (strcmp(driver, "ata_piix") == 0) {
92446d4c572STomoki Sekiyama         /* a host per ide bus, target*:0:<unit>:0 */
92546d4c572STomoki Sekiyama         if (!has_host || !has_tgt) {
92646d4c572STomoki Sekiyama             g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
92746d4c572STomoki Sekiyama             goto cleanup;
92846d4c572STomoki Sekiyama         }
92946d4c572STomoki Sekiyama         for (i = 0; i < nhosts; i++) {
93046d4c572STomoki Sekiyama             if (host == hosts[i]) {
93146d4c572STomoki Sekiyama                 disk->bus_type = GUEST_DISK_BUS_TYPE_IDE;
93246d4c572STomoki Sekiyama                 disk->bus = i;
93346d4c572STomoki Sekiyama                 disk->unit = tgt[1];
93446d4c572STomoki Sekiyama                 break;
93546d4c572STomoki Sekiyama             }
93646d4c572STomoki Sekiyama         }
93746d4c572STomoki Sekiyama         if (i >= nhosts) {
93846d4c572STomoki Sekiyama             g_debug("no host for '%s' (driver '%s')", syspath, driver);
93946d4c572STomoki Sekiyama             goto cleanup;
94046d4c572STomoki Sekiyama         }
94146d4c572STomoki Sekiyama     } else if (strcmp(driver, "sym53c8xx") == 0) {
94246d4c572STomoki Sekiyama         /* scsi(LSI Logic): target*:0:<unit>:0 */
94346d4c572STomoki Sekiyama         if (!has_tgt) {
94446d4c572STomoki Sekiyama             g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
94546d4c572STomoki Sekiyama             goto cleanup;
94646d4c572STomoki Sekiyama         }
94746d4c572STomoki Sekiyama         disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
94846d4c572STomoki Sekiyama         disk->unit = tgt[1];
94946d4c572STomoki Sekiyama     } else if (strcmp(driver, "virtio-pci") == 0) {
95046d4c572STomoki Sekiyama         if (has_tgt) {
95146d4c572STomoki Sekiyama             /* virtio-scsi: target*:0:0:<unit> */
95246d4c572STomoki Sekiyama             disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
95346d4c572STomoki Sekiyama             disk->unit = tgt[2];
95446d4c572STomoki Sekiyama         } else {
95546d4c572STomoki Sekiyama             /* virtio-blk: 1 disk per 1 device */
95646d4c572STomoki Sekiyama             disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO;
95746d4c572STomoki Sekiyama         }
95846d4c572STomoki Sekiyama     } else if (strcmp(driver, "ahci") == 0) {
95946d4c572STomoki Sekiyama         /* ahci: 1 host per 1 unit */
96046d4c572STomoki Sekiyama         if (!has_host || !has_tgt) {
96146d4c572STomoki Sekiyama             g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
96246d4c572STomoki Sekiyama             goto cleanup;
96346d4c572STomoki Sekiyama         }
96446d4c572STomoki Sekiyama         for (i = 0; i < nhosts; i++) {
96546d4c572STomoki Sekiyama             if (host == hosts[i]) {
96646d4c572STomoki Sekiyama                 disk->unit = i;
96746d4c572STomoki Sekiyama                 disk->bus_type = GUEST_DISK_BUS_TYPE_SATA;
96846d4c572STomoki Sekiyama                 break;
96946d4c572STomoki Sekiyama             }
97046d4c572STomoki Sekiyama         }
97146d4c572STomoki Sekiyama         if (i >= nhosts) {
97246d4c572STomoki Sekiyama             g_debug("no host for '%s' (driver '%s')", syspath, driver);
97346d4c572STomoki Sekiyama             goto cleanup;
97446d4c572STomoki Sekiyama         }
975d48f61c8Szhenwei pi     } else if (strcmp(driver, "nvme") == 0) {
976d48f61c8Szhenwei pi         disk->bus_type = GUEST_DISK_BUS_TYPE_NVME;
97746d4c572STomoki Sekiyama     } else {
97846d4c572STomoki Sekiyama         g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath);
97946d4c572STomoki Sekiyama         goto cleanup;
98046d4c572STomoki Sekiyama     }
98146d4c572STomoki Sekiyama 
982d9fe4f0fSThomas Huth     ret = true;
98346d4c572STomoki Sekiyama 
98446d4c572STomoki Sekiyama cleanup:
98546d4c572STomoki Sekiyama     g_free(driver);
986d9fe4f0fSThomas Huth     return ret;
987d9fe4f0fSThomas Huth }
988d9fe4f0fSThomas Huth 
98923843c12SThomas Huth /*
99023843c12SThomas Huth  * Store disk device info for non-PCI virtio devices (for example s390x
99123843c12SThomas Huth  * channel I/O devices). Returns true if information has been stored, or
99223843c12SThomas Huth  * false for failure.
99323843c12SThomas Huth  */
99423843c12SThomas Huth static bool build_guest_fsinfo_for_nonpci_virtio(char const *syspath,
99523843c12SThomas Huth                                                  GuestDiskAddress *disk,
99623843c12SThomas Huth                                                  Error **errp)
99723843c12SThomas Huth {
99823843c12SThomas Huth     unsigned int tgt[3];
99923843c12SThomas Huth     char *p;
100023843c12SThomas Huth 
100123843c12SThomas Huth     if (!strstr(syspath, "/virtio") || !strstr(syspath, "/block")) {
100223843c12SThomas Huth         g_debug("Unsupported virtio device '%s'", syspath);
100323843c12SThomas Huth         return false;
100423843c12SThomas Huth     }
100523843c12SThomas Huth 
100623843c12SThomas Huth     p = strstr(syspath, "/target");
100723843c12SThomas Huth     if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u",
100823843c12SThomas Huth                     &tgt[0], &tgt[1], &tgt[2]) == 3) {
100923843c12SThomas Huth         /* virtio-scsi: target*:0:<target>:<unit> */
101023843c12SThomas Huth         disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
101123843c12SThomas Huth         disk->bus = tgt[0];
101223843c12SThomas Huth         disk->target = tgt[1];
101323843c12SThomas Huth         disk->unit = tgt[2];
101423843c12SThomas Huth     } else {
101523843c12SThomas Huth         /* virtio-blk: 1 disk per 1 device */
101623843c12SThomas Huth         disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO;
101723843c12SThomas Huth     }
101823843c12SThomas Huth 
101923843c12SThomas Huth     return true;
102023843c12SThomas Huth }
102123843c12SThomas Huth 
10225b723a5dSThomas Huth /*
10235b723a5dSThomas Huth  * Store disk device info for CCW devices (s390x channel I/O devices).
10245b723a5dSThomas Huth  * Returns true if information has been stored, or false for failure.
10255b723a5dSThomas Huth  */
10265b723a5dSThomas Huth static bool build_guest_fsinfo_for_ccw_dev(char const *syspath,
10275b723a5dSThomas Huth                                            GuestDiskAddress *disk,
10285b723a5dSThomas Huth                                            Error **errp)
10295b723a5dSThomas Huth {
10305b723a5dSThomas Huth     unsigned int cssid, ssid, subchno, devno;
10315b723a5dSThomas Huth     char *p;
10325b723a5dSThomas Huth 
10335b723a5dSThomas Huth     p = strstr(syspath, "/devices/css");
10345b723a5dSThomas Huth     if (!p || sscanf(p + 12, "%*x/%x.%x.%x/%*x.%*x.%x/",
10355b723a5dSThomas Huth                      &cssid, &ssid, &subchno, &devno) < 4) {
10365b723a5dSThomas Huth         g_debug("could not parse ccw device sysfs path: %s", syspath);
10375b723a5dSThomas Huth         return false;
10385b723a5dSThomas Huth     }
10395b723a5dSThomas Huth 
10405b723a5dSThomas Huth     disk->has_ccw_address = true;
10415b723a5dSThomas Huth     disk->ccw_address = g_new0(GuestCCWAddress, 1);
10425b723a5dSThomas Huth     disk->ccw_address->cssid = cssid;
10435b723a5dSThomas Huth     disk->ccw_address->ssid = ssid;
10445b723a5dSThomas Huth     disk->ccw_address->subchno = subchno;
10455b723a5dSThomas Huth     disk->ccw_address->devno = devno;
10465b723a5dSThomas Huth 
10475b723a5dSThomas Huth     if (strstr(p, "/virtio")) {
10485b723a5dSThomas Huth         build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp);
10495b723a5dSThomas Huth     }
10505b723a5dSThomas Huth 
10515b723a5dSThomas Huth     return true;
10525b723a5dSThomas Huth }
10535b723a5dSThomas Huth 
1054d9fe4f0fSThomas Huth /* Store disk device info specified by @sysfs into @fs */
1055d9fe4f0fSThomas Huth static void build_guest_fsinfo_for_real_device(char const *syspath,
1056d9fe4f0fSThomas Huth                                                GuestFilesystemInfo *fs,
1057d9fe4f0fSThomas Huth                                                Error **errp)
1058d9fe4f0fSThomas Huth {
1059d9fe4f0fSThomas Huth     GuestDiskAddress *disk;
1060d9fe4f0fSThomas Huth     GuestPCIAddress *pciaddr;
1061d9fe4f0fSThomas Huth     bool has_hwinf;
106243dadc43SThomas Huth #ifdef CONFIG_LIBUDEV
106343dadc43SThomas Huth     struct udev *udev = NULL;
106443dadc43SThomas Huth     struct udev_device *udevice = NULL;
106543dadc43SThomas Huth #endif
1066d9fe4f0fSThomas Huth 
1067d9fe4f0fSThomas Huth     pciaddr = g_new0(GuestPCIAddress, 1);
106843dadc43SThomas Huth     pciaddr->domain = -1;                       /* -1 means field is invalid */
106943dadc43SThomas Huth     pciaddr->bus = -1;
107043dadc43SThomas Huth     pciaddr->slot = -1;
107143dadc43SThomas Huth     pciaddr->function = -1;
1072d9fe4f0fSThomas Huth 
1073d9fe4f0fSThomas Huth     disk = g_new0(GuestDiskAddress, 1);
1074d9fe4f0fSThomas Huth     disk->pci_controller = pciaddr;
107543dadc43SThomas Huth     disk->bus_type = GUEST_DISK_BUS_TYPE_UNKNOWN;
1076d9fe4f0fSThomas Huth 
107743dadc43SThomas Huth #ifdef CONFIG_LIBUDEV
107843dadc43SThomas Huth     udev = udev_new();
107943dadc43SThomas Huth     udevice = udev_device_new_from_syspath(udev, syspath);
108043dadc43SThomas Huth     if (udev == NULL || udevice == NULL) {
108143dadc43SThomas Huth         g_debug("failed to query udev");
108243dadc43SThomas Huth     } else {
108343dadc43SThomas Huth         const char *devnode, *serial;
108443dadc43SThomas Huth         devnode = udev_device_get_devnode(udevice);
108543dadc43SThomas Huth         if (devnode != NULL) {
108643dadc43SThomas Huth             disk->dev = g_strdup(devnode);
108743dadc43SThomas Huth             disk->has_dev = true;
108843dadc43SThomas Huth         }
108943dadc43SThomas Huth         serial = udev_device_get_property_value(udevice, "ID_SERIAL");
109043dadc43SThomas Huth         if (serial != NULL && *serial != 0) {
109143dadc43SThomas Huth             disk->serial = g_strdup(serial);
109243dadc43SThomas Huth             disk->has_serial = true;
109343dadc43SThomas Huth         }
109443dadc43SThomas Huth     }
109543dadc43SThomas Huth 
109643dadc43SThomas Huth     udev_unref(udev);
109743dadc43SThomas Huth     udev_device_unref(udevice);
109843dadc43SThomas Huth #endif
109943dadc43SThomas Huth 
110023843c12SThomas Huth     if (strstr(syspath, "/devices/pci")) {
1101d9fe4f0fSThomas Huth         has_hwinf = build_guest_fsinfo_for_pci_dev(syspath, disk, errp);
11025b723a5dSThomas Huth     } else if (strstr(syspath, "/devices/css")) {
11035b723a5dSThomas Huth         has_hwinf = build_guest_fsinfo_for_ccw_dev(syspath, disk, errp);
110423843c12SThomas Huth     } else if (strstr(syspath, "/virtio")) {
110523843c12SThomas Huth         has_hwinf = build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp);
110623843c12SThomas Huth     } else {
110723843c12SThomas Huth         g_debug("Unsupported device type for '%s'", syspath);
110823843c12SThomas Huth         has_hwinf = false;
110923843c12SThomas Huth     }
1110d9fe4f0fSThomas Huth 
111143dadc43SThomas Huth     if (has_hwinf || disk->has_dev || disk->has_serial) {
111254aa3de7SEric Blake         QAPI_LIST_PREPEND(fs->disk, disk);
1113d9fe4f0fSThomas Huth     } else {
111454aa3de7SEric Blake         qapi_free_GuestDiskAddress(disk);
1115d9fe4f0fSThomas Huth     }
111646d4c572STomoki Sekiyama }
111746d4c572STomoki Sekiyama 
111846d4c572STomoki Sekiyama static void build_guest_fsinfo_for_device(char const *devpath,
111946d4c572STomoki Sekiyama                                           GuestFilesystemInfo *fs,
112046d4c572STomoki Sekiyama                                           Error **errp);
112146d4c572STomoki Sekiyama 
112246d4c572STomoki Sekiyama /* Store a list of slave devices of virtual volume specified by @syspath into
112346d4c572STomoki Sekiyama  * @fs */
112446d4c572STomoki Sekiyama static void build_guest_fsinfo_for_virtual_device(char const *syspath,
112546d4c572STomoki Sekiyama                                                   GuestFilesystemInfo *fs,
112646d4c572STomoki Sekiyama                                                   Error **errp)
112746d4c572STomoki Sekiyama {
1128292743d9SMarkus Armbruster     Error *err = NULL;
112946d4c572STomoki Sekiyama     DIR *dir;
113046d4c572STomoki Sekiyama     char *dirpath;
1131e668d1b8Szhanghailiang     struct dirent *entry;
113246d4c572STomoki Sekiyama 
113346d4c572STomoki Sekiyama     dirpath = g_strdup_printf("%s/slaves", syspath);
113446d4c572STomoki Sekiyama     dir = opendir(dirpath);
113546d4c572STomoki Sekiyama     if (!dir) {
11368251a72fSMichael Roth         if (errno != ENOENT) {
113746d4c572STomoki Sekiyama             error_setg_errno(errp, errno, "opendir(\"%s\")", dirpath);
11388251a72fSMichael Roth         }
113946d4c572STomoki Sekiyama         g_free(dirpath);
114046d4c572STomoki Sekiyama         return;
114146d4c572STomoki Sekiyama     }
114246d4c572STomoki Sekiyama 
114346d4c572STomoki Sekiyama     for (;;) {
1144e668d1b8Szhanghailiang         errno = 0;
1145e668d1b8Szhanghailiang         entry = readdir(dir);
1146e668d1b8Szhanghailiang         if (entry == NULL) {
1147e668d1b8Szhanghailiang             if (errno) {
1148e668d1b8Szhanghailiang                 error_setg_errno(errp, errno, "readdir(\"%s\")", dirpath);
114946d4c572STomoki Sekiyama             }
115046d4c572STomoki Sekiyama             break;
115146d4c572STomoki Sekiyama         }
115246d4c572STomoki Sekiyama 
1153e668d1b8Szhanghailiang         if (entry->d_type == DT_LNK) {
1154e668d1b8Szhanghailiang             char *path;
1155e668d1b8Szhanghailiang 
1156e668d1b8Szhanghailiang             g_debug(" slave device '%s'", entry->d_name);
1157e668d1b8Szhanghailiang             path = g_strdup_printf("%s/slaves/%s", syspath, entry->d_name);
1158292743d9SMarkus Armbruster             build_guest_fsinfo_for_device(path, fs, &err);
1159e668d1b8Szhanghailiang             g_free(path);
116046d4c572STomoki Sekiyama 
1161292743d9SMarkus Armbruster             if (err) {
1162292743d9SMarkus Armbruster                 error_propagate(errp, err);
116346d4c572STomoki Sekiyama                 break;
116446d4c572STomoki Sekiyama             }
116546d4c572STomoki Sekiyama         }
116646d4c572STomoki Sekiyama     }
116746d4c572STomoki Sekiyama 
1168e668d1b8Szhanghailiang     g_free(dirpath);
116946d4c572STomoki Sekiyama     closedir(dir);
117046d4c572STomoki Sekiyama }
117146d4c572STomoki Sekiyama 
1172fed39564STomáš Golembiovský static bool is_disk_virtual(const char *devpath, Error **errp)
1173fed39564STomáš Golembiovský {
1174fed39564STomáš Golembiovský     g_autofree char *syspath = realpath(devpath, NULL);
1175fed39564STomáš Golembiovský 
1176fed39564STomáš Golembiovský     if (!syspath) {
1177fed39564STomáš Golembiovský         error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
1178fed39564STomáš Golembiovský         return false;
1179fed39564STomáš Golembiovský     }
1180fed39564STomáš Golembiovský     return strstr(syspath, "/devices/virtual/block/") != NULL;
1181fed39564STomáš Golembiovský }
1182fed39564STomáš Golembiovský 
118346d4c572STomoki Sekiyama /* Dispatch to functions for virtual/real device */
118446d4c572STomoki Sekiyama static void build_guest_fsinfo_for_device(char const *devpath,
118546d4c572STomoki Sekiyama                                           GuestFilesystemInfo *fs,
118646d4c572STomoki Sekiyama                                           Error **errp)
118746d4c572STomoki Sekiyama {
1188fed39564STomáš Golembiovský     ERRP_GUARD();
1189fed39564STomáš Golembiovský     g_autofree char *syspath = NULL;
1190fed39564STomáš Golembiovský     bool is_virtual = false;
119146d4c572STomoki Sekiyama 
1192fed39564STomáš Golembiovský     syspath = realpath(devpath, NULL);
119346d4c572STomoki Sekiyama     if (!syspath) {
1194bbb0151cSJohn Snow         if (errno != ENOENT) {
119546d4c572STomoki Sekiyama             error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
119646d4c572STomoki Sekiyama             return;
119746d4c572STomoki Sekiyama         }
119846d4c572STomoki Sekiyama 
1199bbb0151cSJohn Snow         /* ENOENT: This devpath may not exist because of container config */
1200bbb0151cSJohn Snow         if (!fs->name) {
1201bbb0151cSJohn Snow             fs->name = g_path_get_basename(devpath);
1202bbb0151cSJohn Snow         }
1203bbb0151cSJohn Snow         return;
1204bbb0151cSJohn Snow     }
1205bbb0151cSJohn Snow 
120646d4c572STomoki Sekiyama     if (!fs->name) {
12073e015d81SJulia Suvorova         fs->name = g_path_get_basename(syspath);
120846d4c572STomoki Sekiyama     }
120946d4c572STomoki Sekiyama 
121046d4c572STomoki Sekiyama     g_debug("  parse sysfs path '%s'", syspath);
1211fed39564STomáš Golembiovský     is_virtual = is_disk_virtual(syspath, errp);
1212fed39564STomáš Golembiovský     if (*errp != NULL) {
1213fed39564STomáš Golembiovský         return;
1214fed39564STomáš Golembiovský     }
1215fed39564STomáš Golembiovský     if (is_virtual) {
121646d4c572STomoki Sekiyama         build_guest_fsinfo_for_virtual_device(syspath, fs, errp);
121746d4c572STomoki Sekiyama     } else {
121846d4c572STomoki Sekiyama         build_guest_fsinfo_for_real_device(syspath, fs, errp);
121946d4c572STomoki Sekiyama     }
122046d4c572STomoki Sekiyama }
122146d4c572STomoki Sekiyama 
1222fed39564STomáš Golembiovský #ifdef CONFIG_LIBUDEV
1223fed39564STomáš Golembiovský 
1224fed39564STomáš Golembiovský /*
1225fed39564STomáš Golembiovský  * Wrapper around build_guest_fsinfo_for_device() for getting just
1226fed39564STomáš Golembiovský  * the disk address.
1227fed39564STomáš Golembiovský  */
1228fed39564STomáš Golembiovský static GuestDiskAddress *get_disk_address(const char *syspath, Error **errp)
1229fed39564STomáš Golembiovský {
1230fed39564STomáš Golembiovský     g_autoptr(GuestFilesystemInfo) fs = NULL;
1231fed39564STomáš Golembiovský 
1232fed39564STomáš Golembiovský     fs = g_new0(GuestFilesystemInfo, 1);
1233fed39564STomáš Golembiovský     build_guest_fsinfo_for_device(syspath, fs, errp);
1234fed39564STomáš Golembiovský     if (fs->disk != NULL) {
1235fed39564STomáš Golembiovský         return g_steal_pointer(&fs->disk->value);
1236fed39564STomáš Golembiovský     }
1237fed39564STomáš Golembiovský     return NULL;
1238fed39564STomáš Golembiovský }
1239fed39564STomáš Golembiovský 
1240fed39564STomáš Golembiovský static char *get_alias_for_syspath(const char *syspath)
1241fed39564STomáš Golembiovský {
1242fed39564STomáš Golembiovský     struct udev *udev = NULL;
1243fed39564STomáš Golembiovský     struct udev_device *udevice = NULL;
1244fed39564STomáš Golembiovský     char *ret = NULL;
1245fed39564STomáš Golembiovský 
1246fed39564STomáš Golembiovský     udev = udev_new();
1247fed39564STomáš Golembiovský     if (udev == NULL) {
1248fed39564STomáš Golembiovský         g_debug("failed to query udev");
1249fed39564STomáš Golembiovský         goto out;
1250fed39564STomáš Golembiovský     }
1251fed39564STomáš Golembiovský     udevice = udev_device_new_from_syspath(udev, syspath);
1252fed39564STomáš Golembiovský     if (udevice == NULL) {
1253fed39564STomáš Golembiovský         g_debug("failed to query udev for path: %s", syspath);
1254fed39564STomáš Golembiovský         goto out;
1255fed39564STomáš Golembiovský     } else {
1256fed39564STomáš Golembiovský         const char *alias = udev_device_get_property_value(
1257fed39564STomáš Golembiovský             udevice, "DM_NAME");
1258fed39564STomáš Golembiovský         /*
1259fed39564STomáš Golembiovský          * NULL means there was an error and empty string means there is no
1260fed39564STomáš Golembiovský          * alias. In case of no alias we return NULL instead of empty string.
1261fed39564STomáš Golembiovský          */
1262fed39564STomáš Golembiovský         if (alias == NULL) {
1263fed39564STomáš Golembiovský             g_debug("failed to query udev for device alias for: %s",
1264fed39564STomáš Golembiovský                 syspath);
1265fed39564STomáš Golembiovský         } else if (*alias != 0) {
1266fed39564STomáš Golembiovský             ret = g_strdup(alias);
1267fed39564STomáš Golembiovský         }
1268fed39564STomáš Golembiovský     }
1269fed39564STomáš Golembiovský 
1270fed39564STomáš Golembiovský out:
1271fed39564STomáš Golembiovský     udev_unref(udev);
1272fed39564STomáš Golembiovský     udev_device_unref(udevice);
1273fed39564STomáš Golembiovský     return ret;
1274fed39564STomáš Golembiovský }
1275fed39564STomáš Golembiovský 
1276fed39564STomáš Golembiovský static char *get_device_for_syspath(const char *syspath)
1277fed39564STomáš Golembiovský {
1278fed39564STomáš Golembiovský     struct udev *udev = NULL;
1279fed39564STomáš Golembiovský     struct udev_device *udevice = NULL;
1280fed39564STomáš Golembiovský     char *ret = NULL;
1281fed39564STomáš Golembiovský 
1282fed39564STomáš Golembiovský     udev = udev_new();
1283fed39564STomáš Golembiovský     if (udev == NULL) {
1284fed39564STomáš Golembiovský         g_debug("failed to query udev");
1285fed39564STomáš Golembiovský         goto out;
1286fed39564STomáš Golembiovský     }
1287fed39564STomáš Golembiovský     udevice = udev_device_new_from_syspath(udev, syspath);
1288fed39564STomáš Golembiovský     if (udevice == NULL) {
1289fed39564STomáš Golembiovský         g_debug("failed to query udev for path: %s", syspath);
1290fed39564STomáš Golembiovský         goto out;
1291fed39564STomáš Golembiovský     } else {
1292fed39564STomáš Golembiovský         ret = g_strdup(udev_device_get_devnode(udevice));
1293fed39564STomáš Golembiovský     }
1294fed39564STomáš Golembiovský 
1295fed39564STomáš Golembiovský out:
1296fed39564STomáš Golembiovský     udev_unref(udev);
1297fed39564STomáš Golembiovský     udev_device_unref(udevice);
1298fed39564STomáš Golembiovský     return ret;
1299fed39564STomáš Golembiovský }
1300fed39564STomáš Golembiovský 
1301fed39564STomáš Golembiovský static void get_disk_deps(const char *disk_dir, GuestDiskInfo *disk)
1302fed39564STomáš Golembiovský {
1303fed39564STomáš Golembiovský     g_autofree char *deps_dir = NULL;
1304fed39564STomáš Golembiovský     const gchar *dep;
1305fed39564STomáš Golembiovský     GDir *dp_deps = NULL;
1306fed39564STomáš Golembiovský 
1307fed39564STomáš Golembiovský     /* List dependent disks */
1308fed39564STomáš Golembiovský     deps_dir = g_strdup_printf("%s/slaves", disk_dir);
1309fed39564STomáš Golembiovský     g_debug("  listing entries in: %s", deps_dir);
1310fed39564STomáš Golembiovský     dp_deps = g_dir_open(deps_dir, 0, NULL);
1311fed39564STomáš Golembiovský     if (dp_deps == NULL) {
1312fed39564STomáš Golembiovský         g_debug("failed to list entries in %s", deps_dir);
1313fed39564STomáš Golembiovský         return;
1314fed39564STomáš Golembiovský     }
1315a8aa94b5SMichael Roth     disk->has_dependencies = true;
1316fed39564STomáš Golembiovský     while ((dep = g_dir_read_name(dp_deps)) != NULL) {
1317fed39564STomáš Golembiovský         g_autofree char *dep_dir = NULL;
1318fed39564STomáš Golembiovský         char *dev_name;
1319fed39564STomáš Golembiovský 
1320fed39564STomáš Golembiovský         /* Add dependent disks */
1321fed39564STomáš Golembiovský         dep_dir = g_strdup_printf("%s/%s", deps_dir, dep);
1322fed39564STomáš Golembiovský         dev_name = get_device_for_syspath(dep_dir);
1323fed39564STomáš Golembiovský         if (dev_name != NULL) {
1324fed39564STomáš Golembiovský             g_debug("  adding dependent device: %s", dev_name);
132554aa3de7SEric Blake             QAPI_LIST_PREPEND(disk->dependencies, dev_name);
1326fed39564STomáš Golembiovský         }
1327fed39564STomáš Golembiovský     }
1328fed39564STomáš Golembiovský     g_dir_close(dp_deps);
1329fed39564STomáš Golembiovský }
1330fed39564STomáš Golembiovský 
1331fed39564STomáš Golembiovský /*
1332fed39564STomáš Golembiovský  * Detect partitions subdirectory, name is "<disk_name><number>" or
1333fed39564STomáš Golembiovský  * "<disk_name>p<number>"
1334fed39564STomáš Golembiovský  *
1335fed39564STomáš Golembiovský  * @disk_name -- last component of /sys path (e.g. sda)
1336fed39564STomáš Golembiovský  * @disk_dir -- sys path of the disk (e.g. /sys/block/sda)
1337fed39564STomáš Golembiovský  * @disk_dev -- device node of the disk (e.g. /dev/sda)
1338fed39564STomáš Golembiovský  */
1339fed39564STomáš Golembiovský static GuestDiskInfoList *get_disk_partitions(
1340fed39564STomáš Golembiovský     GuestDiskInfoList *list,
1341fed39564STomáš Golembiovský     const char *disk_name, const char *disk_dir,
1342fed39564STomáš Golembiovský     const char *disk_dev)
1343fed39564STomáš Golembiovský {
134454aa3de7SEric Blake     GuestDiskInfoList *ret = list;
1345fed39564STomáš Golembiovský     struct dirent *de_disk;
1346fed39564STomáš Golembiovský     DIR *dp_disk = NULL;
1347fed39564STomáš Golembiovský     size_t len = strlen(disk_name);
1348fed39564STomáš Golembiovský 
1349fed39564STomáš Golembiovský     dp_disk = opendir(disk_dir);
1350fed39564STomáš Golembiovský     while ((de_disk = readdir(dp_disk)) != NULL) {
1351fed39564STomáš Golembiovský         g_autofree char *partition_dir = NULL;
1352fed39564STomáš Golembiovský         char *dev_name;
1353fed39564STomáš Golembiovský         GuestDiskInfo *partition;
1354fed39564STomáš Golembiovský 
1355fed39564STomáš Golembiovský         if (!(de_disk->d_type & DT_DIR)) {
1356fed39564STomáš Golembiovský             continue;
1357fed39564STomáš Golembiovský         }
1358fed39564STomáš Golembiovský 
1359fed39564STomáš Golembiovský         if (!(strncmp(disk_name, de_disk->d_name, len) == 0 &&
1360fed39564STomáš Golembiovský             ((*(de_disk->d_name + len) == 'p' &&
1361fed39564STomáš Golembiovský             isdigit(*(de_disk->d_name + len + 1))) ||
1362fed39564STomáš Golembiovský                 isdigit(*(de_disk->d_name + len))))) {
1363fed39564STomáš Golembiovský             continue;
1364fed39564STomáš Golembiovský         }
1365fed39564STomáš Golembiovský 
1366fed39564STomáš Golembiovský         partition_dir = g_strdup_printf("%s/%s",
1367fed39564STomáš Golembiovský             disk_dir, de_disk->d_name);
1368fed39564STomáš Golembiovský         dev_name = get_device_for_syspath(partition_dir);
1369fed39564STomáš Golembiovský         if (dev_name == NULL) {
1370fed39564STomáš Golembiovský             g_debug("Failed to get device name for syspath: %s",
1371fed39564STomáš Golembiovský                 disk_dir);
1372fed39564STomáš Golembiovský             continue;
1373fed39564STomáš Golembiovský         }
1374fed39564STomáš Golembiovský         partition = g_new0(GuestDiskInfo, 1);
1375fed39564STomáš Golembiovský         partition->name = dev_name;
1376fed39564STomáš Golembiovský         partition->partition = true;
1377bac9b87bSMarc-André Lureau         partition->has_dependencies = true;
1378fed39564STomáš Golembiovský         /* Add parent disk as dependent for easier tracking of hierarchy */
137954aa3de7SEric Blake         QAPI_LIST_PREPEND(partition->dependencies, g_strdup(disk_dev));
1380fed39564STomáš Golembiovský 
138154aa3de7SEric Blake         QAPI_LIST_PREPEND(ret, partition);
1382fed39564STomáš Golembiovský     }
1383fed39564STomáš Golembiovský     closedir(dp_disk);
1384fed39564STomáš Golembiovský 
1385fed39564STomáš Golembiovský     return ret;
1386fed39564STomáš Golembiovský }
1387fed39564STomáš Golembiovský 
138822668881Szhenwei pi static void get_nvme_smart(GuestDiskInfo *disk)
138922668881Szhenwei pi {
139022668881Szhenwei pi     int fd;
139122668881Szhenwei pi     GuestNVMeSmart *smart;
139222668881Szhenwei pi     NvmeSmartLog log = {0};
139322668881Szhenwei pi     struct nvme_admin_cmd cmd = {
139422668881Szhenwei pi         .opcode = NVME_ADM_CMD_GET_LOG_PAGE,
139522668881Szhenwei pi         .nsid = NVME_NSID_BROADCAST,
139622668881Szhenwei pi         .addr = (uintptr_t)&log,
139722668881Szhenwei pi         .data_len = sizeof(log),
139822668881Szhenwei pi         .cdw10 = NVME_LOG_SMART_INFO | (1 << 15) /* RAE bit */
139922668881Szhenwei pi                  | (((sizeof(log) >> 2) - 1) << 16)
140022668881Szhenwei pi     };
140122668881Szhenwei pi 
1402b9947c9cSMarc-André Lureau     fd = qga_open_cloexec(disk->name, O_RDONLY, 0);
140322668881Szhenwei pi     if (fd == -1) {
140422668881Szhenwei pi         g_debug("Failed to open device: %s: %s", disk->name, g_strerror(errno));
140522668881Szhenwei pi         return;
140622668881Szhenwei pi     }
140722668881Szhenwei pi 
140822668881Szhenwei pi     if (ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd)) {
140922668881Szhenwei pi         g_debug("Failed to get smart: %s: %s", disk->name, g_strerror(errno));
141022668881Szhenwei pi         close(fd);
141122668881Szhenwei pi         return;
141222668881Szhenwei pi     }
141322668881Szhenwei pi 
141422668881Szhenwei pi     disk->has_smart = true;
141522668881Szhenwei pi     disk->smart = g_new0(GuestDiskSmart, 1);
141622668881Szhenwei pi     disk->smart->type = GUEST_DISK_BUS_TYPE_NVME;
141722668881Szhenwei pi 
141822668881Szhenwei pi     smart = &disk->smart->u.nvme;
141922668881Szhenwei pi     smart->critical_warning = log.critical_warning;
142022668881Szhenwei pi     smart->temperature = lduw_le_p(&log.temperature); /* unaligned field */
142122668881Szhenwei pi     smart->available_spare = log.available_spare;
142222668881Szhenwei pi     smart->available_spare_threshold = log.available_spare_threshold;
142322668881Szhenwei pi     smart->percentage_used = log.percentage_used;
142422668881Szhenwei pi     smart->data_units_read_lo = le64_to_cpu(log.data_units_read[0]);
142522668881Szhenwei pi     smart->data_units_read_hi = le64_to_cpu(log.data_units_read[1]);
142622668881Szhenwei pi     smart->data_units_written_lo = le64_to_cpu(log.data_units_written[0]);
142722668881Szhenwei pi     smart->data_units_written_hi = le64_to_cpu(log.data_units_written[1]);
142822668881Szhenwei pi     smart->host_read_commands_lo = le64_to_cpu(log.host_read_commands[0]);
142922668881Szhenwei pi     smart->host_read_commands_hi = le64_to_cpu(log.host_read_commands[1]);
143022668881Szhenwei pi     smart->host_write_commands_lo = le64_to_cpu(log.host_write_commands[0]);
143122668881Szhenwei pi     smart->host_write_commands_hi = le64_to_cpu(log.host_write_commands[1]);
143222668881Szhenwei pi     smart->controller_busy_time_lo = le64_to_cpu(log.controller_busy_time[0]);
143322668881Szhenwei pi     smart->controller_busy_time_hi = le64_to_cpu(log.controller_busy_time[1]);
143422668881Szhenwei pi     smart->power_cycles_lo = le64_to_cpu(log.power_cycles[0]);
143522668881Szhenwei pi     smart->power_cycles_hi = le64_to_cpu(log.power_cycles[1]);
143622668881Szhenwei pi     smart->power_on_hours_lo = le64_to_cpu(log.power_on_hours[0]);
143722668881Szhenwei pi     smart->power_on_hours_hi = le64_to_cpu(log.power_on_hours[1]);
143822668881Szhenwei pi     smart->unsafe_shutdowns_lo = le64_to_cpu(log.unsafe_shutdowns[0]);
143922668881Szhenwei pi     smart->unsafe_shutdowns_hi = le64_to_cpu(log.unsafe_shutdowns[1]);
144022668881Szhenwei pi     smart->media_errors_lo = le64_to_cpu(log.media_errors[0]);
144122668881Szhenwei pi     smart->media_errors_hi = le64_to_cpu(log.media_errors[1]);
144222668881Szhenwei pi     smart->number_of_error_log_entries_lo =
144322668881Szhenwei pi         le64_to_cpu(log.number_of_error_log_entries[0]);
144422668881Szhenwei pi     smart->number_of_error_log_entries_hi =
144522668881Szhenwei pi         le64_to_cpu(log.number_of_error_log_entries[1]);
144622668881Szhenwei pi 
144722668881Szhenwei pi     close(fd);
144822668881Szhenwei pi }
144922668881Szhenwei pi 
145022668881Szhenwei pi static void get_disk_smart(GuestDiskInfo *disk)
145122668881Szhenwei pi {
145222668881Szhenwei pi     if (disk->has_address
145322668881Szhenwei pi         && (disk->address->bus_type == GUEST_DISK_BUS_TYPE_NVME)) {
145422668881Szhenwei pi         get_nvme_smart(disk);
145522668881Szhenwei pi     }
145622668881Szhenwei pi }
145722668881Szhenwei pi 
1458fed39564STomáš Golembiovský GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
1459fed39564STomáš Golembiovský {
146054aa3de7SEric Blake     GuestDiskInfoList *ret = NULL;
1461fed39564STomáš Golembiovský     GuestDiskInfo *disk;
1462fed39564STomáš Golembiovský     DIR *dp = NULL;
1463fed39564STomáš Golembiovský     struct dirent *de = NULL;
1464fed39564STomáš Golembiovský 
1465fed39564STomáš Golembiovský     g_debug("listing /sys/block directory");
1466fed39564STomáš Golembiovský     dp = opendir("/sys/block");
1467fed39564STomáš Golembiovský     if (dp == NULL) {
1468fed39564STomáš Golembiovský         error_setg_errno(errp, errno, "Can't open directory \"/sys/block\"");
1469fed39564STomáš Golembiovský         return NULL;
1470fed39564STomáš Golembiovský     }
1471fed39564STomáš Golembiovský     while ((de = readdir(dp)) != NULL) {
1472fed39564STomáš Golembiovský         g_autofree char *disk_dir = NULL, *line = NULL,
1473fed39564STomáš Golembiovský             *size_path = NULL;
1474fed39564STomáš Golembiovský         char *dev_name;
1475fed39564STomáš Golembiovský         Error *local_err = NULL;
1476fed39564STomáš Golembiovský         if (de->d_type != DT_LNK) {
1477fed39564STomáš Golembiovský             g_debug("  skipping entry: %s", de->d_name);
1478fed39564STomáš Golembiovský             continue;
1479fed39564STomáš Golembiovský         }
1480fed39564STomáš Golembiovský 
1481fed39564STomáš Golembiovský         /* Check size and skip zero-sized disks */
1482fed39564STomáš Golembiovský         g_debug("  checking disk size");
1483fed39564STomáš Golembiovský         size_path = g_strdup_printf("/sys/block/%s/size", de->d_name);
1484fed39564STomáš Golembiovský         if (!g_file_get_contents(size_path, &line, NULL, NULL)) {
1485fed39564STomáš Golembiovský             g_debug("  failed to read disk size");
1486fed39564STomáš Golembiovský             continue;
1487fed39564STomáš Golembiovský         }
1488fed39564STomáš Golembiovský         if (g_strcmp0(line, "0\n") == 0) {
1489fed39564STomáš Golembiovský             g_debug("  skipping zero-sized disk");
1490fed39564STomáš Golembiovský             continue;
1491fed39564STomáš Golembiovský         }
1492fed39564STomáš Golembiovský 
1493fed39564STomáš Golembiovský         g_debug("  adding %s", de->d_name);
1494fed39564STomáš Golembiovský         disk_dir = g_strdup_printf("/sys/block/%s", de->d_name);
1495fed39564STomáš Golembiovský         dev_name = get_device_for_syspath(disk_dir);
1496fed39564STomáš Golembiovský         if (dev_name == NULL) {
1497fed39564STomáš Golembiovský             g_debug("Failed to get device name for syspath: %s",
1498fed39564STomáš Golembiovský                 disk_dir);
1499fed39564STomáš Golembiovský             continue;
1500fed39564STomáš Golembiovský         }
1501fed39564STomáš Golembiovský         disk = g_new0(GuestDiskInfo, 1);
1502fed39564STomáš Golembiovský         disk->name = dev_name;
1503fed39564STomáš Golembiovský         disk->partition = false;
1504fed39564STomáš Golembiovský         disk->alias = get_alias_for_syspath(disk_dir);
1505fed39564STomáš Golembiovský         disk->has_alias = (disk->alias != NULL);
150654aa3de7SEric Blake         QAPI_LIST_PREPEND(ret, disk);
1507fed39564STomáš Golembiovský 
1508fed39564STomáš Golembiovský         /* Get address for non-virtual devices */
1509fed39564STomáš Golembiovský         bool is_virtual = is_disk_virtual(disk_dir, &local_err);
1510fed39564STomáš Golembiovský         if (local_err != NULL) {
1511fed39564STomáš Golembiovský             g_debug("  failed to check disk path, ignoring error: %s",
1512fed39564STomáš Golembiovský                 error_get_pretty(local_err));
1513fed39564STomáš Golembiovský             error_free(local_err);
1514fed39564STomáš Golembiovský             local_err = NULL;
1515fed39564STomáš Golembiovský             /* Don't try to get the address */
1516fed39564STomáš Golembiovský             is_virtual = true;
1517fed39564STomáš Golembiovský         }
1518fed39564STomáš Golembiovský         if (!is_virtual) {
1519fed39564STomáš Golembiovský             disk->address = get_disk_address(disk_dir, &local_err);
1520fed39564STomáš Golembiovský             if (local_err != NULL) {
1521fed39564STomáš Golembiovský                 g_debug("  failed to get device info, ignoring error: %s",
1522fed39564STomáš Golembiovský                     error_get_pretty(local_err));
1523fed39564STomáš Golembiovský                 error_free(local_err);
1524fed39564STomáš Golembiovský                 local_err = NULL;
1525fed39564STomáš Golembiovský             } else if (disk->address != NULL) {
1526fed39564STomáš Golembiovský                 disk->has_address = true;
1527fed39564STomáš Golembiovský             }
1528fed39564STomáš Golembiovský         }
1529fed39564STomáš Golembiovský 
1530fed39564STomáš Golembiovský         get_disk_deps(disk_dir, disk);
153122668881Szhenwei pi         get_disk_smart(disk);
1532fed39564STomáš Golembiovský         ret = get_disk_partitions(ret, de->d_name, disk_dir, dev_name);
1533fed39564STomáš Golembiovský     }
1534b1b9ab1cSMichael Roth 
1535b1b9ab1cSMichael Roth     closedir(dp);
1536b1b9ab1cSMichael Roth 
1537fed39564STomáš Golembiovský     return ret;
1538fed39564STomáš Golembiovský }
1539fed39564STomáš Golembiovský 
1540fed39564STomáš Golembiovský #else
1541fed39564STomáš Golembiovský 
1542fed39564STomáš Golembiovský GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
1543fed39564STomáš Golembiovský {
1544fed39564STomáš Golembiovský     error_setg(errp, QERR_UNSUPPORTED);
1545fed39564STomáš Golembiovský     return NULL;
1546fed39564STomáš Golembiovský }
1547fed39564STomáš Golembiovský 
1548fed39564STomáš Golembiovský #endif
1549fed39564STomáš Golembiovský 
155046d4c572STomoki Sekiyama /* Return a list of the disk device(s)' info which @mount lies on */
155146d4c572STomoki Sekiyama static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount,
155246d4c572STomoki Sekiyama                                                Error **errp)
155346d4c572STomoki Sekiyama {
155446d4c572STomoki Sekiyama     GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs));
155525b5ff1aSChen Hanxiao     struct statvfs buf;
155625b5ff1aSChen Hanxiao     unsigned long used, nonroot_total, fr_size;
155746d4c572STomoki Sekiyama     char *devpath = g_strdup_printf("/sys/dev/block/%u:%u",
155846d4c572STomoki Sekiyama                                     mount->devmajor, mount->devminor);
155946d4c572STomoki Sekiyama 
156046d4c572STomoki Sekiyama     fs->mountpoint = g_strdup(mount->dirname);
156146d4c572STomoki Sekiyama     fs->type = g_strdup(mount->devtype);
156246d4c572STomoki Sekiyama     build_guest_fsinfo_for_device(devpath, fs, errp);
156346d4c572STomoki Sekiyama 
156425b5ff1aSChen Hanxiao     if (statvfs(fs->mountpoint, &buf) == 0) {
156525b5ff1aSChen Hanxiao         fr_size = buf.f_frsize;
156625b5ff1aSChen Hanxiao         used = buf.f_blocks - buf.f_bfree;
156725b5ff1aSChen Hanxiao         nonroot_total = used + buf.f_bavail;
156825b5ff1aSChen Hanxiao         fs->used_bytes = used * fr_size;
156925b5ff1aSChen Hanxiao         fs->total_bytes = nonroot_total * fr_size;
157025b5ff1aSChen Hanxiao 
157125b5ff1aSChen Hanxiao         fs->has_total_bytes = true;
157225b5ff1aSChen Hanxiao         fs->has_used_bytes = true;
157325b5ff1aSChen Hanxiao     }
157425b5ff1aSChen Hanxiao 
157546d4c572STomoki Sekiyama     g_free(devpath);
157625b5ff1aSChen Hanxiao 
157746d4c572STomoki Sekiyama     return fs;
157846d4c572STomoki Sekiyama }
157946d4c572STomoki Sekiyama 
158046d4c572STomoki Sekiyama GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
158146d4c572STomoki Sekiyama {
158246d4c572STomoki Sekiyama     FsMountList mounts;
158346d4c572STomoki Sekiyama     struct FsMount *mount;
158454aa3de7SEric Blake     GuestFilesystemInfoList *ret = NULL;
158546d4c572STomoki Sekiyama     Error *local_err = NULL;
158646d4c572STomoki Sekiyama 
158746d4c572STomoki Sekiyama     QTAILQ_INIT(&mounts);
1588561bfcb6SMarc-André Lureau     if (!build_fs_mount_list(&mounts, &local_err)) {
158946d4c572STomoki Sekiyama         error_propagate(errp, local_err);
159046d4c572STomoki Sekiyama         return NULL;
159146d4c572STomoki Sekiyama     }
159246d4c572STomoki Sekiyama 
159346d4c572STomoki Sekiyama     QTAILQ_FOREACH(mount, &mounts, next) {
159446d4c572STomoki Sekiyama         g_debug("Building guest fsinfo for '%s'", mount->dirname);
159546d4c572STomoki Sekiyama 
159654aa3de7SEric Blake         QAPI_LIST_PREPEND(ret, build_guest_fsinfo(mount, &local_err));
159746d4c572STomoki Sekiyama         if (local_err) {
159846d4c572STomoki Sekiyama             error_propagate(errp, local_err);
159946d4c572STomoki Sekiyama             qapi_free_GuestFilesystemInfoList(ret);
160046d4c572STomoki Sekiyama             ret = NULL;
160146d4c572STomoki Sekiyama             break;
160246d4c572STomoki Sekiyama         }
160346d4c572STomoki Sekiyama     }
160446d4c572STomoki Sekiyama 
160546d4c572STomoki Sekiyama     free_fs_mount_list(&mounts);
160646d4c572STomoki Sekiyama     return ret;
160746d4c572STomoki Sekiyama }
1608e72c3f2eSMichael Roth #endif /* CONFIG_FSFREEZE */
1609c216e5adSMichael Roth 
1610eab5fd59SPaolo Bonzini #if defined(CONFIG_FSTRIM)
1611eab5fd59SPaolo Bonzini /*
1612eab5fd59SPaolo Bonzini  * Walk list of mounted file systems in the guest, and trim them.
1613eab5fd59SPaolo Bonzini  */
1614e82855d9SJustin Ossevoort GuestFilesystemTrimResponse *
1615e82855d9SJustin Ossevoort qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
1616eab5fd59SPaolo Bonzini {
1617e82855d9SJustin Ossevoort     GuestFilesystemTrimResponse *response;
1618e82855d9SJustin Ossevoort     GuestFilesystemTrimResult *result;
1619eab5fd59SPaolo Bonzini     int ret = 0;
1620eab5fd59SPaolo Bonzini     FsMountList mounts;
1621eab5fd59SPaolo Bonzini     struct FsMount *mount;
1622eab5fd59SPaolo Bonzini     int fd;
162373a652a1SJustin Ossevoort     struct fstrim_range r;
1624eab5fd59SPaolo Bonzini 
1625eab5fd59SPaolo Bonzini     slog("guest-fstrim called");
1626eab5fd59SPaolo Bonzini 
1627eab5fd59SPaolo Bonzini     QTAILQ_INIT(&mounts);
1628561bfcb6SMarc-André Lureau     if (!build_fs_mount_list(&mounts, errp)) {
1629e82855d9SJustin Ossevoort         return NULL;
1630eab5fd59SPaolo Bonzini     }
1631eab5fd59SPaolo Bonzini 
1632e82855d9SJustin Ossevoort     response = g_malloc0(sizeof(*response));
1633e82855d9SJustin Ossevoort 
1634eab5fd59SPaolo Bonzini     QTAILQ_FOREACH(mount, &mounts, next) {
1635e82855d9SJustin Ossevoort         result = g_malloc0(sizeof(*result));
1636e82855d9SJustin Ossevoort         result->path = g_strdup(mount->dirname);
1637e82855d9SJustin Ossevoort 
163854aa3de7SEric Blake         QAPI_LIST_PREPEND(response->paths, result);
1639e82855d9SJustin Ossevoort 
1640b9947c9cSMarc-André Lureau         fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0);
1641eab5fd59SPaolo Bonzini         if (fd == -1) {
1642e82855d9SJustin Ossevoort             result->error = g_strdup_printf("failed to open: %s",
1643e82855d9SJustin Ossevoort                                             strerror(errno));
1644e82855d9SJustin Ossevoort             result->has_error = true;
1645e82855d9SJustin Ossevoort             continue;
1646eab5fd59SPaolo Bonzini         }
1647eab5fd59SPaolo Bonzini 
1648e35916acSMichael Tokarev         /* We try to cull filesystems we know won't work in advance, but other
1649e35916acSMichael Tokarev          * filesystems may not implement fstrim for less obvious reasons.
1650e35916acSMichael Tokarev          * These will report EOPNOTSUPP; while in some other cases ENOTTY
1651e35916acSMichael Tokarev          * will be reported (e.g. CD-ROMs).
1652e82855d9SJustin Ossevoort          * Any other error means an unexpected error.
1653eab5fd59SPaolo Bonzini          */
165473a652a1SJustin Ossevoort         r.start = 0;
165573a652a1SJustin Ossevoort         r.len = -1;
165673a652a1SJustin Ossevoort         r.minlen = has_minimum ? minimum : 0;
1657eab5fd59SPaolo Bonzini         ret = ioctl(fd, FITRIM, &r);
1658eab5fd59SPaolo Bonzini         if (ret == -1) {
1659e82855d9SJustin Ossevoort             result->has_error = true;
1660e82855d9SJustin Ossevoort             if (errno == ENOTTY || errno == EOPNOTSUPP) {
1661e82855d9SJustin Ossevoort                 result->error = g_strdup("trim not supported");
1662e82855d9SJustin Ossevoort             } else {
1663e82855d9SJustin Ossevoort                 result->error = g_strdup_printf("failed to trim: %s",
1664e82855d9SJustin Ossevoort                                                 strerror(errno));
1665e82855d9SJustin Ossevoort             }
1666eab5fd59SPaolo Bonzini             close(fd);
1667e82855d9SJustin Ossevoort             continue;
1668eab5fd59SPaolo Bonzini         }
1669e82855d9SJustin Ossevoort 
1670e82855d9SJustin Ossevoort         result->has_minimum = true;
1671e82855d9SJustin Ossevoort         result->minimum = r.minlen;
1672e82855d9SJustin Ossevoort         result->has_trimmed = true;
1673e82855d9SJustin Ossevoort         result->trimmed = r.len;
1674eab5fd59SPaolo Bonzini         close(fd);
1675eab5fd59SPaolo Bonzini     }
1676eab5fd59SPaolo Bonzini 
1677eab5fd59SPaolo Bonzini     free_fs_mount_list(&mounts);
1678e82855d9SJustin Ossevoort     return response;
1679eab5fd59SPaolo Bonzini }
1680eab5fd59SPaolo Bonzini #endif /* CONFIG_FSTRIM */
1681eab5fd59SPaolo Bonzini 
1682eab5fd59SPaolo Bonzini 
168311d0f125SLuiz Capitulino #define LINUX_SYS_STATE_FILE "/sys/power/state"
168411d0f125SLuiz Capitulino #define SUSPEND_SUPPORTED 0
168511d0f125SLuiz Capitulino #define SUSPEND_NOT_SUPPORTED 1
168611d0f125SLuiz Capitulino 
16878b020b5eSDaniel Henrique Barboza typedef enum {
16888b020b5eSDaniel Henrique Barboza     SUSPEND_MODE_DISK = 0,
16898b020b5eSDaniel Henrique Barboza     SUSPEND_MODE_RAM = 1,
16908b020b5eSDaniel Henrique Barboza     SUSPEND_MODE_HYBRID = 2,
16918b020b5eSDaniel Henrique Barboza } SuspendMode;
16928b020b5eSDaniel Henrique Barboza 
16938b020b5eSDaniel Henrique Barboza /*
16948b020b5eSDaniel Henrique Barboza  * Executes a command in a child process using g_spawn_sync,
16958b020b5eSDaniel Henrique Barboza  * returning an int >= 0 representing the exit status of the
16968b020b5eSDaniel Henrique Barboza  * process.
16978b020b5eSDaniel Henrique Barboza  *
16988b020b5eSDaniel Henrique Barboza  * If the program wasn't found in path, returns -1.
16998b020b5eSDaniel Henrique Barboza  *
17008b020b5eSDaniel Henrique Barboza  * If a problem happened when creating the child process,
17018b020b5eSDaniel Henrique Barboza  * returns -1 and errp is set.
17028b020b5eSDaniel Henrique Barboza  */
17038b020b5eSDaniel Henrique Barboza static int run_process_child(const char *command[], Error **errp)
17048b020b5eSDaniel Henrique Barboza {
17058b020b5eSDaniel Henrique Barboza     int exit_status, spawn_flag;
17068b020b5eSDaniel Henrique Barboza     GError *g_err = NULL;
17078b020b5eSDaniel Henrique Barboza     bool success;
17088b020b5eSDaniel Henrique Barboza 
17098b020b5eSDaniel Henrique Barboza     spawn_flag = G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL |
17108b020b5eSDaniel Henrique Barboza                  G_SPAWN_STDERR_TO_DEV_NULL;
17118b020b5eSDaniel Henrique Barboza 
1712fcc41961SMarc-André Lureau     success =  g_spawn_sync(NULL, (char **)command, NULL, spawn_flag,
17138b020b5eSDaniel Henrique Barboza                             NULL, NULL, NULL, NULL,
17148b020b5eSDaniel Henrique Barboza                             &exit_status, &g_err);
17158b020b5eSDaniel Henrique Barboza 
17168b020b5eSDaniel Henrique Barboza     if (success) {
17178b020b5eSDaniel Henrique Barboza         return WEXITSTATUS(exit_status);
17188b020b5eSDaniel Henrique Barboza     }
17198b020b5eSDaniel Henrique Barboza 
17208b020b5eSDaniel Henrique Barboza     if (g_err && (g_err->code != G_SPAWN_ERROR_NOENT)) {
17218b020b5eSDaniel Henrique Barboza         error_setg(errp, "failed to create child process, error '%s'",
17228b020b5eSDaniel Henrique Barboza                    g_err->message);
17238b020b5eSDaniel Henrique Barboza     }
17248b020b5eSDaniel Henrique Barboza 
17258b020b5eSDaniel Henrique Barboza     g_error_free(g_err);
17268b020b5eSDaniel Henrique Barboza     return -1;
17278b020b5eSDaniel Henrique Barboza }
17288b020b5eSDaniel Henrique Barboza 
1729067927d6SDaniel Henrique Barboza static bool systemd_supports_mode(SuspendMode mode, Error **errp)
1730067927d6SDaniel Henrique Barboza {
1731067927d6SDaniel Henrique Barboza     const char *systemctl_args[3] = {"systemd-hibernate", "systemd-suspend",
1732067927d6SDaniel Henrique Barboza                                      "systemd-hybrid-sleep"};
1733067927d6SDaniel Henrique Barboza     const char *cmd[4] = {"systemctl", "status", systemctl_args[mode], NULL};
1734067927d6SDaniel Henrique Barboza     int status;
1735067927d6SDaniel Henrique Barboza 
1736992861fbSMarkus Armbruster     status = run_process_child(cmd, errp);
1737067927d6SDaniel Henrique Barboza 
1738067927d6SDaniel Henrique Barboza     /*
1739067927d6SDaniel Henrique Barboza      * systemctl status uses LSB return codes so we can expect
1740067927d6SDaniel Henrique Barboza      * status > 0 and be ok. To assert if the guest has support
1741067927d6SDaniel Henrique Barboza      * for the selected suspend mode, status should be < 4. 4 is
1742067927d6SDaniel Henrique Barboza      * the code for unknown service status, the return value when
1743067927d6SDaniel Henrique Barboza      * the service does not exist. A common value is status = 3
1744067927d6SDaniel Henrique Barboza      * (program is not running).
1745067927d6SDaniel Henrique Barboza      */
1746067927d6SDaniel Henrique Barboza     if (status > 0 && status < 4) {
1747067927d6SDaniel Henrique Barboza         return true;
1748067927d6SDaniel Henrique Barboza     }
1749067927d6SDaniel Henrique Barboza 
1750067927d6SDaniel Henrique Barboza     return false;
1751067927d6SDaniel Henrique Barboza }
1752067927d6SDaniel Henrique Barboza 
1753067927d6SDaniel Henrique Barboza static void systemd_suspend(SuspendMode mode, Error **errp)
1754067927d6SDaniel Henrique Barboza {
1755067927d6SDaniel Henrique Barboza     Error *local_err = NULL;
1756067927d6SDaniel Henrique Barboza     const char *systemctl_args[3] = {"hibernate", "suspend", "hybrid-sleep"};
1757067927d6SDaniel Henrique Barboza     const char *cmd[3] = {"systemctl", systemctl_args[mode], NULL};
1758067927d6SDaniel Henrique Barboza     int status;
1759067927d6SDaniel Henrique Barboza 
1760067927d6SDaniel Henrique Barboza     status = run_process_child(cmd, &local_err);
1761067927d6SDaniel Henrique Barboza 
1762067927d6SDaniel Henrique Barboza     if (status == 0) {
1763067927d6SDaniel Henrique Barboza         return;
1764067927d6SDaniel Henrique Barboza     }
1765067927d6SDaniel Henrique Barboza 
1766067927d6SDaniel Henrique Barboza     if ((status == -1) && !local_err) {
1767067927d6SDaniel Henrique Barboza         error_setg(errp, "the helper program 'systemctl %s' was not found",
1768067927d6SDaniel Henrique Barboza                    systemctl_args[mode]);
1769067927d6SDaniel Henrique Barboza         return;
1770067927d6SDaniel Henrique Barboza     }
1771067927d6SDaniel Henrique Barboza 
1772067927d6SDaniel Henrique Barboza     if (local_err) {
1773067927d6SDaniel Henrique Barboza         error_propagate(errp, local_err);
1774067927d6SDaniel Henrique Barboza     } else {
1775067927d6SDaniel Henrique Barboza         error_setg(errp, "the helper program 'systemctl %s' returned an "
1776067927d6SDaniel Henrique Barboza                    "unexpected exit status code (%d)",
1777067927d6SDaniel Henrique Barboza                    systemctl_args[mode], status);
1778067927d6SDaniel Henrique Barboza     }
1779067927d6SDaniel Henrique Barboza }
1780067927d6SDaniel Henrique Barboza 
17818b020b5eSDaniel Henrique Barboza static bool pmutils_supports_mode(SuspendMode mode, Error **errp)
178211d0f125SLuiz Capitulino {
17836b26e837SLuiz Capitulino     Error *local_err = NULL;
17848b020b5eSDaniel Henrique Barboza     const char *pmutils_args[3] = {"--hibernate", "--suspend",
17858b020b5eSDaniel Henrique Barboza                                    "--suspend-hybrid"};
17868b020b5eSDaniel Henrique Barboza     const char *cmd[3] = {"pm-is-supported", pmutils_args[mode], NULL};
1787dc8764f0SLuiz Capitulino     int status;
178811d0f125SLuiz Capitulino 
17898b020b5eSDaniel Henrique Barboza     status = run_process_child(cmd, &local_err);
1790304a0fcbSDaniel Henrique Barboza 
17918b020b5eSDaniel Henrique Barboza     if (status == SUSPEND_SUPPORTED) {
17928b020b5eSDaniel Henrique Barboza         return true;
1793304a0fcbSDaniel Henrique Barboza     }
1794304a0fcbSDaniel Henrique Barboza 
17958b020b5eSDaniel Henrique Barboza     if ((status == -1) && !local_err) {
17968b020b5eSDaniel Henrique Barboza         return false;
1797a5fcf0e3SDaniel Henrique Barboza     }
179811d0f125SLuiz Capitulino 
179984d18f06SMarkus Armbruster     if (local_err) {
180077dbc81bSMarkus Armbruster         error_propagate(errp, local_err);
18018b020b5eSDaniel Henrique Barboza     } else {
180277dbc81bSMarkus Armbruster         error_setg(errp,
18038b020b5eSDaniel Henrique Barboza                    "the helper program '%s' returned an unexpected exit"
18048b020b5eSDaniel Henrique Barboza                    " status code (%d)", "pm-is-supported", status);
1805dc8764f0SLuiz Capitulino     }
180611d0f125SLuiz Capitulino 
18078b020b5eSDaniel Henrique Barboza     return false;
1808a5fcf0e3SDaniel Henrique Barboza }
1809a5fcf0e3SDaniel Henrique Barboza 
18108b020b5eSDaniel Henrique Barboza static void pmutils_suspend(SuspendMode mode, Error **errp)
1811246d76ebSDaniel Henrique Barboza {
1812246d76ebSDaniel Henrique Barboza     Error *local_err = NULL;
18138b020b5eSDaniel Henrique Barboza     const char *pmutils_binaries[3] = {"pm-hibernate", "pm-suspend",
18148b020b5eSDaniel Henrique Barboza                                        "pm-suspend-hybrid"};
18158b020b5eSDaniel Henrique Barboza     const char *cmd[2] = {pmutils_binaries[mode], NULL};
1816246d76ebSDaniel Henrique Barboza     int status;
1817246d76ebSDaniel Henrique Barboza 
18188b020b5eSDaniel Henrique Barboza     status = run_process_child(cmd, &local_err);
1819246d76ebSDaniel Henrique Barboza 
18208b020b5eSDaniel Henrique Barboza     if (status == 0) {
1821246d76ebSDaniel Henrique Barboza         return;
1822246d76ebSDaniel Henrique Barboza     }
1823246d76ebSDaniel Henrique Barboza 
18248b020b5eSDaniel Henrique Barboza     if ((status == -1) && !local_err) {
18258b020b5eSDaniel Henrique Barboza         error_setg(errp, "the helper program '%s' was not found",
18268b020b5eSDaniel Henrique Barboza                    pmutils_binaries[mode]);
1827246d76ebSDaniel Henrique Barboza         return;
1828246d76ebSDaniel Henrique Barboza     }
1829246d76ebSDaniel Henrique Barboza 
1830246d76ebSDaniel Henrique Barboza     if (local_err) {
1831246d76ebSDaniel Henrique Barboza         error_propagate(errp, local_err);
18328b020b5eSDaniel Henrique Barboza     } else {
1833246d76ebSDaniel Henrique Barboza         error_setg(errp,
18348b020b5eSDaniel Henrique Barboza                    "the helper program '%s' returned an unexpected exit"
18358b020b5eSDaniel Henrique Barboza                    " status code (%d)", pmutils_binaries[mode], status);
18368b020b5eSDaniel Henrique Barboza     }
1837246d76ebSDaniel Henrique Barboza }
1838246d76ebSDaniel Henrique Barboza 
18398b020b5eSDaniel Henrique Barboza static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp)
1840a5fcf0e3SDaniel Henrique Barboza {
18418b020b5eSDaniel Henrique Barboza     const char *sysfile_strs[3] = {"disk", "mem", NULL};
18428b020b5eSDaniel Henrique Barboza     const char *sysfile_str = sysfile_strs[mode];
1843a5fcf0e3SDaniel Henrique Barboza     char buf[32]; /* hopefully big enough */
1844a5fcf0e3SDaniel Henrique Barboza     int fd;
1845a5fcf0e3SDaniel Henrique Barboza     ssize_t ret;
1846a5fcf0e3SDaniel Henrique Barboza 
18478b020b5eSDaniel Henrique Barboza     if (!sysfile_str) {
18488b020b5eSDaniel Henrique Barboza         error_setg(errp, "unknown guest suspend mode");
1849a5fcf0e3SDaniel Henrique Barboza         return false;
1850a5fcf0e3SDaniel Henrique Barboza     }
1851a5fcf0e3SDaniel Henrique Barboza 
1852a5fcf0e3SDaniel Henrique Barboza     fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
1853a5fcf0e3SDaniel Henrique Barboza     if (fd < 0) {
1854a5fcf0e3SDaniel Henrique Barboza         return false;
1855a5fcf0e3SDaniel Henrique Barboza     }
1856a5fcf0e3SDaniel Henrique Barboza 
1857a5fcf0e3SDaniel Henrique Barboza     ret = read(fd, buf, sizeof(buf) - 1);
1858d9c745c1SPaolo Bonzini     close(fd);
1859a5fcf0e3SDaniel Henrique Barboza     if (ret <= 0) {
1860a5fcf0e3SDaniel Henrique Barboza         return false;
1861a5fcf0e3SDaniel Henrique Barboza     }
1862a5fcf0e3SDaniel Henrique Barboza     buf[ret] = '\0';
1863a5fcf0e3SDaniel Henrique Barboza 
1864a5fcf0e3SDaniel Henrique Barboza     if (strstr(buf, sysfile_str)) {
1865a5fcf0e3SDaniel Henrique Barboza         return true;
1866a5fcf0e3SDaniel Henrique Barboza     }
1867a5fcf0e3SDaniel Henrique Barboza     return false;
1868a5fcf0e3SDaniel Henrique Barboza }
1869a5fcf0e3SDaniel Henrique Barboza 
18708b020b5eSDaniel Henrique Barboza static void linux_sys_state_suspend(SuspendMode mode, Error **errp)
1871246d76ebSDaniel Henrique Barboza {
1872246d76ebSDaniel Henrique Barboza     Error *local_err = NULL;
18738b020b5eSDaniel Henrique Barboza     const char *sysfile_strs[3] = {"disk", "mem", NULL};
18748b020b5eSDaniel Henrique Barboza     const char *sysfile_str = sysfile_strs[mode];
1875246d76ebSDaniel Henrique Barboza     pid_t pid;
1876246d76ebSDaniel Henrique Barboza     int status;
1877246d76ebSDaniel Henrique Barboza 
18788b020b5eSDaniel Henrique Barboza     if (!sysfile_str) {
1879246d76ebSDaniel Henrique Barboza         error_setg(errp, "unknown guest suspend mode");
1880246d76ebSDaniel Henrique Barboza         return;
1881246d76ebSDaniel Henrique Barboza     }
1882246d76ebSDaniel Henrique Barboza 
1883246d76ebSDaniel Henrique Barboza     pid = fork();
1884246d76ebSDaniel Henrique Barboza     if (!pid) {
1885246d76ebSDaniel Henrique Barboza         /* child */
1886246d76ebSDaniel Henrique Barboza         int fd;
1887246d76ebSDaniel Henrique Barboza 
1888246d76ebSDaniel Henrique Barboza         setsid();
1889246d76ebSDaniel Henrique Barboza         reopen_fd_to_null(0);
1890246d76ebSDaniel Henrique Barboza         reopen_fd_to_null(1);
1891246d76ebSDaniel Henrique Barboza         reopen_fd_to_null(2);
1892246d76ebSDaniel Henrique Barboza 
1893246d76ebSDaniel Henrique Barboza         fd = open(LINUX_SYS_STATE_FILE, O_WRONLY);
1894246d76ebSDaniel Henrique Barboza         if (fd < 0) {
1895246d76ebSDaniel Henrique Barboza             _exit(EXIT_FAILURE);
1896246d76ebSDaniel Henrique Barboza         }
1897246d76ebSDaniel Henrique Barboza 
1898246d76ebSDaniel Henrique Barboza         if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) {
1899246d76ebSDaniel Henrique Barboza             _exit(EXIT_FAILURE);
1900246d76ebSDaniel Henrique Barboza         }
1901246d76ebSDaniel Henrique Barboza 
1902246d76ebSDaniel Henrique Barboza         _exit(EXIT_SUCCESS);
1903246d76ebSDaniel Henrique Barboza     } else if (pid < 0) {
1904246d76ebSDaniel Henrique Barboza         error_setg_errno(errp, errno, "failed to create child process");
1905246d76ebSDaniel Henrique Barboza         return;
1906246d76ebSDaniel Henrique Barboza     }
1907246d76ebSDaniel Henrique Barboza 
1908246d76ebSDaniel Henrique Barboza     ga_wait_child(pid, &status, &local_err);
1909246d76ebSDaniel Henrique Barboza     if (local_err) {
1910246d76ebSDaniel Henrique Barboza         error_propagate(errp, local_err);
1911246d76ebSDaniel Henrique Barboza         return;
1912246d76ebSDaniel Henrique Barboza     }
1913246d76ebSDaniel Henrique Barboza 
1914246d76ebSDaniel Henrique Barboza     if (WEXITSTATUS(status)) {
1915246d76ebSDaniel Henrique Barboza         error_setg(errp, "child process has failed to suspend");
1916246d76ebSDaniel Henrique Barboza     }
1917246d76ebSDaniel Henrique Barboza 
1918246d76ebSDaniel Henrique Barboza }
1919246d76ebSDaniel Henrique Barboza 
19208b020b5eSDaniel Henrique Barboza static void guest_suspend(SuspendMode mode, Error **errp)
192111d0f125SLuiz Capitulino {
19227b376087SLuiz Capitulino     Error *local_err = NULL;
192373e1d8ebSDaniel Henrique Barboza     bool mode_supported = false;
192411d0f125SLuiz Capitulino 
192573e1d8ebSDaniel Henrique Barboza     if (systemd_supports_mode(mode, &local_err)) {
192673e1d8ebSDaniel Henrique Barboza         mode_supported = true;
1927067927d6SDaniel Henrique Barboza         systemd_suspend(mode, &local_err);
192873e1d8ebSDaniel Henrique Barboza     }
192973e1d8ebSDaniel Henrique Barboza 
1930067927d6SDaniel Henrique Barboza     if (!local_err) {
1931067927d6SDaniel Henrique Barboza         return;
1932067927d6SDaniel Henrique Barboza     }
1933067927d6SDaniel Henrique Barboza 
1934067927d6SDaniel Henrique Barboza     error_free(local_err);
19356a4a3853SVladimir Sementsov-Ogievskiy     local_err = NULL;
1936067927d6SDaniel Henrique Barboza 
193773e1d8ebSDaniel Henrique Barboza     if (pmutils_supports_mode(mode, &local_err)) {
193873e1d8ebSDaniel Henrique Barboza         mode_supported = true;
19398b020b5eSDaniel Henrique Barboza         pmutils_suspend(mode, &local_err);
194073e1d8ebSDaniel Henrique Barboza     }
194173e1d8ebSDaniel Henrique Barboza 
1942246d76ebSDaniel Henrique Barboza     if (!local_err) {
1943304a0fcbSDaniel Henrique Barboza         return;
1944304a0fcbSDaniel Henrique Barboza     }
1945304a0fcbSDaniel Henrique Barboza 
1946246d76ebSDaniel Henrique Barboza     error_free(local_err);
19476a4a3853SVladimir Sementsov-Ogievskiy     local_err = NULL;
194811d0f125SLuiz Capitulino 
194973e1d8ebSDaniel Henrique Barboza     if (linux_sys_state_supports_mode(mode, &local_err)) {
195073e1d8ebSDaniel Henrique Barboza         mode_supported = true;
19518b020b5eSDaniel Henrique Barboza         linux_sys_state_suspend(mode, &local_err);
195273e1d8ebSDaniel Henrique Barboza     }
195373e1d8ebSDaniel Henrique Barboza 
195473e1d8ebSDaniel Henrique Barboza     if (!mode_supported) {
19556a4a3853SVladimir Sementsov-Ogievskiy         error_free(local_err);
195673e1d8ebSDaniel Henrique Barboza         error_setg(errp,
195773e1d8ebSDaniel Henrique Barboza                    "the requested suspend mode is not supported by the guest");
1958b2322003SMarkus Armbruster     } else {
195977dbc81bSMarkus Armbruster         error_propagate(errp, local_err);
19607b376087SLuiz Capitulino     }
196111d0f125SLuiz Capitulino }
196211d0f125SLuiz Capitulino 
196377dbc81bSMarkus Armbruster void qmp_guest_suspend_disk(Error **errp)
196411d0f125SLuiz Capitulino {
1965304a0fcbSDaniel Henrique Barboza     guest_suspend(SUSPEND_MODE_DISK, errp);
196611d0f125SLuiz Capitulino }
196711d0f125SLuiz Capitulino 
196877dbc81bSMarkus Armbruster void qmp_guest_suspend_ram(Error **errp)
1969fbf42210SLuiz Capitulino {
1970304a0fcbSDaniel Henrique Barboza     guest_suspend(SUSPEND_MODE_RAM, errp);
1971fbf42210SLuiz Capitulino }
1972fbf42210SLuiz Capitulino 
197377dbc81bSMarkus Armbruster void qmp_guest_suspend_hybrid(Error **errp)
197495f4f404SLuiz Capitulino {
1975304a0fcbSDaniel Henrique Barboza     guest_suspend(SUSPEND_MODE_HYBRID, errp);
197695f4f404SLuiz Capitulino }
197795f4f404SLuiz Capitulino 
1978d2baff62SLaszlo Ersek /* Transfer online/offline status between @vcpu and the guest system.
1979d2baff62SLaszlo Ersek  *
1980d2baff62SLaszlo Ersek  * On input either @errp or *@errp must be NULL.
1981d2baff62SLaszlo Ersek  *
1982d2baff62SLaszlo Ersek  * In system-to-@vcpu direction, the following @vcpu fields are accessed:
1983d2baff62SLaszlo Ersek  * - R: vcpu->logical_id
1984d2baff62SLaszlo Ersek  * - W: vcpu->online
1985d2baff62SLaszlo Ersek  * - W: vcpu->can_offline
1986d2baff62SLaszlo Ersek  *
1987d2baff62SLaszlo Ersek  * In @vcpu-to-system direction, the following @vcpu fields are accessed:
1988d2baff62SLaszlo Ersek  * - R: vcpu->logical_id
1989d2baff62SLaszlo Ersek  * - R: vcpu->online
1990d2baff62SLaszlo Ersek  *
1991d2baff62SLaszlo Ersek  * Written members remain unmodified on error.
1992d2baff62SLaszlo Ersek  */
1993d2baff62SLaszlo Ersek static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu,
1994b4bf912aSIgor Mammedov                           char *dirpath, Error **errp)
1995d2baff62SLaszlo Ersek {
1996b4bf912aSIgor Mammedov     int fd;
1997b4bf912aSIgor Mammedov     int res;
1998d2baff62SLaszlo Ersek     int dirfd;
1999b4bf912aSIgor Mammedov     static const char fn[] = "online";
2000d2baff62SLaszlo Ersek 
2001d2baff62SLaszlo Ersek     dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
2002d2baff62SLaszlo Ersek     if (dirfd == -1) {
2003d2baff62SLaszlo Ersek         error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
2004b4bf912aSIgor Mammedov         return;
2005b4bf912aSIgor Mammedov     }
2006d2baff62SLaszlo Ersek 
2007d2baff62SLaszlo Ersek     fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR);
2008d2baff62SLaszlo Ersek     if (fd == -1) {
2009d2baff62SLaszlo Ersek         if (errno != ENOENT) {
2010d2baff62SLaszlo Ersek             error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn);
2011d2baff62SLaszlo Ersek         } else if (sys2vcpu) {
2012d2baff62SLaszlo Ersek             vcpu->online = true;
2013d2baff62SLaszlo Ersek             vcpu->can_offline = false;
2014d2baff62SLaszlo Ersek         } else if (!vcpu->online) {
2015d2baff62SLaszlo Ersek             error_setg(errp, "logical processor #%" PRId64 " can't be "
2016d2baff62SLaszlo Ersek                        "offlined", vcpu->logical_id);
2017d2baff62SLaszlo Ersek         } /* otherwise pretend successful re-onlining */
2018d2baff62SLaszlo Ersek     } else {
2019d2baff62SLaszlo Ersek         unsigned char status;
2020d2baff62SLaszlo Ersek 
2021d2baff62SLaszlo Ersek         res = pread(fd, &status, 1, 0);
2022d2baff62SLaszlo Ersek         if (res == -1) {
2023d2baff62SLaszlo Ersek             error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn);
2024d2baff62SLaszlo Ersek         } else if (res == 0) {
2025d2baff62SLaszlo Ersek             error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath,
2026d2baff62SLaszlo Ersek                        fn);
2027d2baff62SLaszlo Ersek         } else if (sys2vcpu) {
2028d2baff62SLaszlo Ersek             vcpu->online = (status != '0');
2029d2baff62SLaszlo Ersek             vcpu->can_offline = true;
2030d2baff62SLaszlo Ersek         } else if (vcpu->online != (status != '0')) {
2031d2baff62SLaszlo Ersek             status = '0' + vcpu->online;
2032d2baff62SLaszlo Ersek             if (pwrite(fd, &status, 1, 0) == -1) {
2033d2baff62SLaszlo Ersek                 error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath,
2034d2baff62SLaszlo Ersek                                  fn);
2035d2baff62SLaszlo Ersek             }
2036d2baff62SLaszlo Ersek         } /* otherwise pretend successful re-(on|off)-lining */
2037d2baff62SLaszlo Ersek 
2038d2baff62SLaszlo Ersek         res = close(fd);
2039d2baff62SLaszlo Ersek         g_assert(res == 0);
2040d2baff62SLaszlo Ersek     }
2041d2baff62SLaszlo Ersek 
2042d2baff62SLaszlo Ersek     res = close(dirfd);
2043d2baff62SLaszlo Ersek     g_assert(res == 0);
2044d2baff62SLaszlo Ersek }
2045d2baff62SLaszlo Ersek 
2046d2baff62SLaszlo Ersek GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
2047d2baff62SLaszlo Ersek {
2048c3033fd3SEric Blake     GuestLogicalProcessorList *head, **tail;
204927e7de3cSLin Ma     const char *cpu_dir = "/sys/devices/system/cpu";
205027e7de3cSLin Ma     const gchar *line;
205127e7de3cSLin Ma     g_autoptr(GDir) cpu_gdir = NULL;
2052d2baff62SLaszlo Ersek     Error *local_err = NULL;
2053d2baff62SLaszlo Ersek 
2054d2baff62SLaszlo Ersek     head = NULL;
2055c3033fd3SEric Blake     tail = &head;
205627e7de3cSLin Ma     cpu_gdir = g_dir_open(cpu_dir, 0, NULL);
2057d2baff62SLaszlo Ersek 
205827e7de3cSLin Ma     if (cpu_gdir == NULL) {
205927e7de3cSLin Ma         error_setg_errno(errp, errno, "failed to list entries: %s", cpu_dir);
206027e7de3cSLin Ma         return NULL;
206127e7de3cSLin Ma     }
206227e7de3cSLin Ma 
206327e7de3cSLin Ma     while (local_err == NULL && (line = g_dir_read_name(cpu_gdir)) != NULL) {
2064d2baff62SLaszlo Ersek         GuestLogicalProcessor *vcpu;
206527e7de3cSLin Ma         int64_t id;
206627e7de3cSLin Ma         if (sscanf(line, "cpu%" PRId64, &id)) {
206727e7de3cSLin Ma             g_autofree char *path = g_strdup_printf("/sys/devices/system/cpu/"
206827e7de3cSLin Ma                                                     "cpu%" PRId64 "/", id);
2069d2baff62SLaszlo Ersek             vcpu = g_malloc0(sizeof *vcpu);
2070b4bf912aSIgor Mammedov             vcpu->logical_id = id;
2071d2baff62SLaszlo Ersek             vcpu->has_can_offline = true; /* lolspeak ftw */
2072b4bf912aSIgor Mammedov             transfer_vcpu(vcpu, true, path, &local_err);
2073c3033fd3SEric Blake             QAPI_LIST_APPEND(tail, vcpu);
2074d2baff62SLaszlo Ersek         }
2075b4bf912aSIgor Mammedov     }
2076d2baff62SLaszlo Ersek 
2077d2baff62SLaszlo Ersek     if (local_err == NULL) {
2078d2baff62SLaszlo Ersek         /* there's no guest with zero VCPUs */
2079d2baff62SLaszlo Ersek         g_assert(head != NULL);
2080d2baff62SLaszlo Ersek         return head;
2081d2baff62SLaszlo Ersek     }
2082d2baff62SLaszlo Ersek 
2083d2baff62SLaszlo Ersek     qapi_free_GuestLogicalProcessorList(head);
2084d2baff62SLaszlo Ersek     error_propagate(errp, local_err);
2085d2baff62SLaszlo Ersek     return NULL;
2086d2baff62SLaszlo Ersek }
2087d2baff62SLaszlo Ersek 
2088cbb65fc2SLaszlo Ersek int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
2089cbb65fc2SLaszlo Ersek {
2090cbb65fc2SLaszlo Ersek     int64_t processed;
2091cbb65fc2SLaszlo Ersek     Error *local_err = NULL;
2092cbb65fc2SLaszlo Ersek 
2093cbb65fc2SLaszlo Ersek     processed = 0;
2094cbb65fc2SLaszlo Ersek     while (vcpus != NULL) {
2095b4bf912aSIgor Mammedov         char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/",
2096b4bf912aSIgor Mammedov                                      vcpus->value->logical_id);
2097b4bf912aSIgor Mammedov 
2098b4bf912aSIgor Mammedov         transfer_vcpu(vcpus->value, false, path, &local_err);
2099b4bf912aSIgor Mammedov         g_free(path);
2100cbb65fc2SLaszlo Ersek         if (local_err != NULL) {
2101cbb65fc2SLaszlo Ersek             break;
2102cbb65fc2SLaszlo Ersek         }
2103cbb65fc2SLaszlo Ersek         ++processed;
2104cbb65fc2SLaszlo Ersek         vcpus = vcpus->next;
2105cbb65fc2SLaszlo Ersek     }
2106cbb65fc2SLaszlo Ersek 
2107cbb65fc2SLaszlo Ersek     if (local_err != NULL) {
2108cbb65fc2SLaszlo Ersek         if (processed == 0) {
2109cbb65fc2SLaszlo Ersek             error_propagate(errp, local_err);
2110cbb65fc2SLaszlo Ersek         } else {
2111cbb65fc2SLaszlo Ersek             error_free(local_err);
2112cbb65fc2SLaszlo Ersek         }
2113cbb65fc2SLaszlo Ersek     }
2114cbb65fc2SLaszlo Ersek 
2115cbb65fc2SLaszlo Ersek     return processed;
2116cbb65fc2SLaszlo Ersek }
21174fd0642eSAlexander Ivanov #endif /* __linux__ */
2118cbb65fc2SLaszlo Ersek 
21194fd0642eSAlexander Ivanov #if defined(__linux__) || defined(__FreeBSD__)
2120215a2771SDaniel P. Berrange void qmp_guest_set_user_password(const char *username,
2121215a2771SDaniel P. Berrange                                  const char *password,
2122215a2771SDaniel P. Berrange                                  bool crypted,
2123215a2771SDaniel P. Berrange                                  Error **errp)
2124215a2771SDaniel P. Berrange {
2125215a2771SDaniel P. Berrange     Error *local_err = NULL;
2126215a2771SDaniel P. Berrange     char *passwd_path = NULL;
2127215a2771SDaniel P. Berrange     pid_t pid;
2128215a2771SDaniel P. Berrange     int status;
2129215a2771SDaniel P. Berrange     int datafd[2] = { -1, -1 };
2130215a2771SDaniel P. Berrange     char *rawpasswddata = NULL;
2131215a2771SDaniel P. Berrange     size_t rawpasswdlen;
2132215a2771SDaniel P. Berrange     char *chpasswddata = NULL;
2133215a2771SDaniel P. Berrange     size_t chpasswdlen;
2134215a2771SDaniel P. Berrange 
2135920639caSDaniel P. Berrange     rawpasswddata = (char *)qbase64_decode(password, -1, &rawpasswdlen, errp);
2136920639caSDaniel P. Berrange     if (!rawpasswddata) {
2137920639caSDaniel P. Berrange         return;
2138920639caSDaniel P. Berrange     }
2139215a2771SDaniel P. Berrange     rawpasswddata = g_renew(char, rawpasswddata, rawpasswdlen + 1);
2140215a2771SDaniel P. Berrange     rawpasswddata[rawpasswdlen] = '\0';
2141215a2771SDaniel P. Berrange 
2142215a2771SDaniel P. Berrange     if (strchr(rawpasswddata, '\n')) {
2143215a2771SDaniel P. Berrange         error_setg(errp, "forbidden characters in raw password");
2144215a2771SDaniel P. Berrange         goto out;
2145215a2771SDaniel P. Berrange     }
2146215a2771SDaniel P. Berrange 
2147215a2771SDaniel P. Berrange     if (strchr(username, '\n') ||
2148215a2771SDaniel P. Berrange         strchr(username, ':')) {
2149215a2771SDaniel P. Berrange         error_setg(errp, "forbidden characters in username");
2150215a2771SDaniel P. Berrange         goto out;
2151215a2771SDaniel P. Berrange     }
2152215a2771SDaniel P. Berrange 
21534fd0642eSAlexander Ivanov #ifdef __FreeBSD__
21544fd0642eSAlexander Ivanov     chpasswddata = g_strdup(rawpasswddata);
21554fd0642eSAlexander Ivanov     passwd_path = g_find_program_in_path("pw");
21564fd0642eSAlexander Ivanov #else
2157215a2771SDaniel P. Berrange     chpasswddata = g_strdup_printf("%s:%s\n", username, rawpasswddata);
2158215a2771SDaniel P. Berrange     passwd_path = g_find_program_in_path("chpasswd");
21594fd0642eSAlexander Ivanov #endif
21604fd0642eSAlexander Ivanov 
21614fd0642eSAlexander Ivanov     chpasswdlen = strlen(chpasswddata);
2162215a2771SDaniel P. Berrange 
2163215a2771SDaniel P. Berrange     if (!passwd_path) {
2164215a2771SDaniel P. Berrange         error_setg(errp, "cannot find 'passwd' program in PATH");
2165215a2771SDaniel P. Berrange         goto out;
2166215a2771SDaniel P. Berrange     }
2167215a2771SDaniel P. Berrange 
2168ed78331dSMarc-André Lureau     if (!g_unix_open_pipe(datafd, FD_CLOEXEC, NULL)) {
2169215a2771SDaniel P. Berrange         error_setg(errp, "cannot create pipe FDs");
2170215a2771SDaniel P. Berrange         goto out;
2171215a2771SDaniel P. Berrange     }
2172215a2771SDaniel P. Berrange 
2173215a2771SDaniel P. Berrange     pid = fork();
2174215a2771SDaniel P. Berrange     if (pid == 0) {
2175215a2771SDaniel P. Berrange         close(datafd[1]);
2176215a2771SDaniel P. Berrange         /* child */
2177215a2771SDaniel P. Berrange         setsid();
2178215a2771SDaniel P. Berrange         dup2(datafd[0], 0);
2179215a2771SDaniel P. Berrange         reopen_fd_to_null(1);
2180215a2771SDaniel P. Berrange         reopen_fd_to_null(2);
2181215a2771SDaniel P. Berrange 
21824fd0642eSAlexander Ivanov #ifdef __FreeBSD__
21834fd0642eSAlexander Ivanov         const char *h_arg;
21844fd0642eSAlexander Ivanov         h_arg = (crypted) ? "-H" : "-h";
21854fd0642eSAlexander Ivanov         execl(passwd_path, "pw", "usermod", "-n", username, h_arg, "0", NULL);
21864fd0642eSAlexander Ivanov #else
2187215a2771SDaniel P. Berrange         if (crypted) {
2188fcc41961SMarc-André Lureau             execl(passwd_path, "chpasswd", "-e", NULL);
2189215a2771SDaniel P. Berrange         } else {
2190fcc41961SMarc-André Lureau             execl(passwd_path, "chpasswd", NULL);
2191215a2771SDaniel P. Berrange         }
21924fd0642eSAlexander Ivanov #endif
2193215a2771SDaniel P. Berrange         _exit(EXIT_FAILURE);
2194215a2771SDaniel P. Berrange     } else if (pid < 0) {
2195215a2771SDaniel P. Berrange         error_setg_errno(errp, errno, "failed to create child process");
2196215a2771SDaniel P. Berrange         goto out;
2197215a2771SDaniel P. Berrange     }
2198215a2771SDaniel P. Berrange     close(datafd[0]);
2199215a2771SDaniel P. Berrange     datafd[0] = -1;
2200215a2771SDaniel P. Berrange 
2201215a2771SDaniel P. Berrange     if (qemu_write_full(datafd[1], chpasswddata, chpasswdlen) != chpasswdlen) {
2202215a2771SDaniel P. Berrange         error_setg_errno(errp, errno, "cannot write new account password");
2203215a2771SDaniel P. Berrange         goto out;
2204215a2771SDaniel P. Berrange     }
2205215a2771SDaniel P. Berrange     close(datafd[1]);
2206215a2771SDaniel P. Berrange     datafd[1] = -1;
2207215a2771SDaniel P. Berrange 
2208215a2771SDaniel P. Berrange     ga_wait_child(pid, &status, &local_err);
2209215a2771SDaniel P. Berrange     if (local_err) {
2210215a2771SDaniel P. Berrange         error_propagate(errp, local_err);
2211215a2771SDaniel P. Berrange         goto out;
2212215a2771SDaniel P. Berrange     }
2213215a2771SDaniel P. Berrange 
2214215a2771SDaniel P. Berrange     if (!WIFEXITED(status)) {
2215215a2771SDaniel P. Berrange         error_setg(errp, "child process has terminated abnormally");
2216215a2771SDaniel P. Berrange         goto out;
2217215a2771SDaniel P. Berrange     }
2218215a2771SDaniel P. Berrange 
2219215a2771SDaniel P. Berrange     if (WEXITSTATUS(status)) {
2220215a2771SDaniel P. Berrange         error_setg(errp, "child process has failed to set user password");
2221215a2771SDaniel P. Berrange         goto out;
2222215a2771SDaniel P. Berrange     }
2223215a2771SDaniel P. Berrange 
2224215a2771SDaniel P. Berrange out:
2225215a2771SDaniel P. Berrange     g_free(chpasswddata);
2226215a2771SDaniel P. Berrange     g_free(rawpasswddata);
2227215a2771SDaniel P. Berrange     g_free(passwd_path);
2228215a2771SDaniel P. Berrange     if (datafd[0] != -1) {
2229215a2771SDaniel P. Berrange         close(datafd[0]);
2230215a2771SDaniel P. Berrange     }
2231215a2771SDaniel P. Berrange     if (datafd[1] != -1) {
2232215a2771SDaniel P. Berrange         close(datafd[1]);
2233215a2771SDaniel P. Berrange     }
2234215a2771SDaniel P. Berrange }
22354fd0642eSAlexander Ivanov #else /* __linux__ || __FreeBSD__ */
22364fd0642eSAlexander Ivanov void qmp_guest_set_user_password(const char *username,
22374fd0642eSAlexander Ivanov                                  const char *password,
22384fd0642eSAlexander Ivanov                                  bool crypted,
22394fd0642eSAlexander Ivanov                                  Error **errp)
22404fd0642eSAlexander Ivanov {
22414fd0642eSAlexander Ivanov     error_setg(errp, QERR_UNSUPPORTED);
22424fd0642eSAlexander Ivanov }
22434fd0642eSAlexander Ivanov #endif /* __linux__ || __FreeBSD__ */
2244215a2771SDaniel P. Berrange 
22454fd0642eSAlexander Ivanov #ifdef __linux__
2246bd240fcaSzhanghailiang static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf,
2247bd240fcaSzhanghailiang                                int size, Error **errp)
2248bd240fcaSzhanghailiang {
2249bd240fcaSzhanghailiang     int fd;
2250bd240fcaSzhanghailiang     int res;
2251bd240fcaSzhanghailiang 
2252bd240fcaSzhanghailiang     errno = 0;
2253bd240fcaSzhanghailiang     fd = openat(dirfd, pathname, O_RDONLY);
2254bd240fcaSzhanghailiang     if (fd == -1) {
2255bd240fcaSzhanghailiang         error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
2256bd240fcaSzhanghailiang         return;
2257bd240fcaSzhanghailiang     }
2258bd240fcaSzhanghailiang 
2259bd240fcaSzhanghailiang     res = pread(fd, buf, size, 0);
2260bd240fcaSzhanghailiang     if (res == -1) {
2261bd240fcaSzhanghailiang         error_setg_errno(errp, errno, "pread sysfs file \"%s\"", pathname);
2262bd240fcaSzhanghailiang     } else if (res == 0) {
2263bd240fcaSzhanghailiang         error_setg(errp, "pread sysfs file \"%s\": unexpected EOF", pathname);
2264bd240fcaSzhanghailiang     }
2265bd240fcaSzhanghailiang     close(fd);
2266bd240fcaSzhanghailiang }
2267bd240fcaSzhanghailiang 
2268bd240fcaSzhanghailiang static void ga_write_sysfs_file(int dirfd, const char *pathname,
2269bd240fcaSzhanghailiang                                 const char *buf, int size, Error **errp)
2270bd240fcaSzhanghailiang {
2271bd240fcaSzhanghailiang     int fd;
2272bd240fcaSzhanghailiang 
2273bd240fcaSzhanghailiang     errno = 0;
2274bd240fcaSzhanghailiang     fd = openat(dirfd, pathname, O_WRONLY);
2275bd240fcaSzhanghailiang     if (fd == -1) {
2276bd240fcaSzhanghailiang         error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
2277bd240fcaSzhanghailiang         return;
2278bd240fcaSzhanghailiang     }
2279bd240fcaSzhanghailiang 
2280bd240fcaSzhanghailiang     if (pwrite(fd, buf, size, 0) == -1) {
2281bd240fcaSzhanghailiang         error_setg_errno(errp, errno, "pwrite sysfs file \"%s\"", pathname);
2282bd240fcaSzhanghailiang     }
2283bd240fcaSzhanghailiang 
2284bd240fcaSzhanghailiang     close(fd);
2285bd240fcaSzhanghailiang }
2286bd240fcaSzhanghailiang 
2287bd240fcaSzhanghailiang /* Transfer online/offline status between @mem_blk and the guest system.
2288bd240fcaSzhanghailiang  *
2289bd240fcaSzhanghailiang  * On input either @errp or *@errp must be NULL.
2290bd240fcaSzhanghailiang  *
2291bd240fcaSzhanghailiang  * In system-to-@mem_blk direction, the following @mem_blk fields are accessed:
2292bd240fcaSzhanghailiang  * - R: mem_blk->phys_index
2293bd240fcaSzhanghailiang  * - W: mem_blk->online
2294bd240fcaSzhanghailiang  * - W: mem_blk->can_offline
2295bd240fcaSzhanghailiang  *
2296bd240fcaSzhanghailiang  * In @mem_blk-to-system direction, the following @mem_blk fields are accessed:
2297bd240fcaSzhanghailiang  * - R: mem_blk->phys_index
2298bd240fcaSzhanghailiang  * - R: mem_blk->online
2299bd240fcaSzhanghailiang  *-  R: mem_blk->can_offline
2300bd240fcaSzhanghailiang  * Written members remain unmodified on error.
2301bd240fcaSzhanghailiang  */
2302bd240fcaSzhanghailiang static void transfer_memory_block(GuestMemoryBlock *mem_blk, bool sys2memblk,
2303bd240fcaSzhanghailiang                                   GuestMemoryBlockResponse *result,
2304bd240fcaSzhanghailiang                                   Error **errp)
2305bd240fcaSzhanghailiang {
2306bd240fcaSzhanghailiang     char *dirpath;
2307bd240fcaSzhanghailiang     int dirfd;
2308bd240fcaSzhanghailiang     char *status;
2309bd240fcaSzhanghailiang     Error *local_err = NULL;
2310bd240fcaSzhanghailiang 
2311bd240fcaSzhanghailiang     if (!sys2memblk) {
2312bd240fcaSzhanghailiang         DIR *dp;
2313bd240fcaSzhanghailiang 
2314bd240fcaSzhanghailiang         if (!result) {
2315bd240fcaSzhanghailiang             error_setg(errp, "Internal error, 'result' should not be NULL");
2316bd240fcaSzhanghailiang             return;
2317bd240fcaSzhanghailiang         }
2318bd240fcaSzhanghailiang         errno = 0;
2319bd240fcaSzhanghailiang         dp = opendir("/sys/devices/system/memory/");
2320bd240fcaSzhanghailiang          /* if there is no 'memory' directory in sysfs,
2321bd240fcaSzhanghailiang          * we think this VM does not support online/offline memory block,
2322bd240fcaSzhanghailiang          * any other solution?
2323bd240fcaSzhanghailiang          */
23249879f5acSPhilippe Mathieu-Daudé         if (!dp) {
23259879f5acSPhilippe Mathieu-Daudé             if (errno == ENOENT) {
2326bd240fcaSzhanghailiang                 result->response =
2327bd240fcaSzhanghailiang                     GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
23289879f5acSPhilippe Mathieu-Daudé             }
2329bd240fcaSzhanghailiang             goto out1;
2330bd240fcaSzhanghailiang         }
2331bd240fcaSzhanghailiang         closedir(dp);
2332bd240fcaSzhanghailiang     }
2333bd240fcaSzhanghailiang 
2334bd240fcaSzhanghailiang     dirpath = g_strdup_printf("/sys/devices/system/memory/memory%" PRId64 "/",
2335bd240fcaSzhanghailiang                               mem_blk->phys_index);
2336bd240fcaSzhanghailiang     dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
2337bd240fcaSzhanghailiang     if (dirfd == -1) {
2338bd240fcaSzhanghailiang         if (sys2memblk) {
2339bd240fcaSzhanghailiang             error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
2340bd240fcaSzhanghailiang         } else {
2341bd240fcaSzhanghailiang             if (errno == ENOENT) {
2342bd240fcaSzhanghailiang                 result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_NOT_FOUND;
2343bd240fcaSzhanghailiang             } else {
2344bd240fcaSzhanghailiang                 result->response =
2345bd240fcaSzhanghailiang                     GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
2346bd240fcaSzhanghailiang             }
2347bd240fcaSzhanghailiang         }
2348bd240fcaSzhanghailiang         g_free(dirpath);
2349bd240fcaSzhanghailiang         goto out1;
2350bd240fcaSzhanghailiang     }
2351bd240fcaSzhanghailiang     g_free(dirpath);
2352bd240fcaSzhanghailiang 
2353bd240fcaSzhanghailiang     status = g_malloc0(10);
2354bd240fcaSzhanghailiang     ga_read_sysfs_file(dirfd, "state", status, 10, &local_err);
2355bd240fcaSzhanghailiang     if (local_err) {
2356bd240fcaSzhanghailiang         /* treat with sysfs file that not exist in old kernel */
2357bd240fcaSzhanghailiang         if (errno == ENOENT) {
2358bd240fcaSzhanghailiang             error_free(local_err);
2359bd240fcaSzhanghailiang             if (sys2memblk) {
2360bd240fcaSzhanghailiang                 mem_blk->online = true;
2361bd240fcaSzhanghailiang                 mem_blk->can_offline = false;
2362bd240fcaSzhanghailiang             } else if (!mem_blk->online) {
2363bd240fcaSzhanghailiang                 result->response =
2364bd240fcaSzhanghailiang                     GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
2365bd240fcaSzhanghailiang             }
2366bd240fcaSzhanghailiang         } else {
2367bd240fcaSzhanghailiang             if (sys2memblk) {
2368bd240fcaSzhanghailiang                 error_propagate(errp, local_err);
2369bd240fcaSzhanghailiang             } else {
2370b368123dSMarkus Armbruster                 error_free(local_err);
2371bd240fcaSzhanghailiang                 result->response =
2372bd240fcaSzhanghailiang                     GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
2373bd240fcaSzhanghailiang             }
2374bd240fcaSzhanghailiang         }
2375bd240fcaSzhanghailiang         goto out2;
2376bd240fcaSzhanghailiang     }
2377bd240fcaSzhanghailiang 
2378bd240fcaSzhanghailiang     if (sys2memblk) {
2379bd240fcaSzhanghailiang         char removable = '0';
2380bd240fcaSzhanghailiang 
2381bd240fcaSzhanghailiang         mem_blk->online = (strncmp(status, "online", 6) == 0);
2382bd240fcaSzhanghailiang 
2383bd240fcaSzhanghailiang         ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err);
2384bd240fcaSzhanghailiang         if (local_err) {
238567cc32ebSVeres Lajos             /* if no 'removable' file, it doesn't support offline mem blk */
2386bd240fcaSzhanghailiang             if (errno == ENOENT) {
2387bd240fcaSzhanghailiang                 error_free(local_err);
2388bd240fcaSzhanghailiang                 mem_blk->can_offline = false;
2389bd240fcaSzhanghailiang             } else {
2390bd240fcaSzhanghailiang                 error_propagate(errp, local_err);
2391bd240fcaSzhanghailiang             }
2392bd240fcaSzhanghailiang         } else {
2393bd240fcaSzhanghailiang             mem_blk->can_offline = (removable != '0');
2394bd240fcaSzhanghailiang         }
2395bd240fcaSzhanghailiang     } else {
2396bd240fcaSzhanghailiang         if (mem_blk->online != (strncmp(status, "online", 6) == 0)) {
23977064024dSMarc-André Lureau             const char *new_state = mem_blk->online ? "online" : "offline";
2398bd240fcaSzhanghailiang 
2399bd240fcaSzhanghailiang             ga_write_sysfs_file(dirfd, "state", new_state, strlen(new_state),
2400bd240fcaSzhanghailiang                                 &local_err);
2401bd240fcaSzhanghailiang             if (local_err) {
2402bd240fcaSzhanghailiang                 error_free(local_err);
2403bd240fcaSzhanghailiang                 result->response =
2404bd240fcaSzhanghailiang                     GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
2405bd240fcaSzhanghailiang                 goto out2;
2406bd240fcaSzhanghailiang             }
2407bd240fcaSzhanghailiang 
2408bd240fcaSzhanghailiang             result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_SUCCESS;
2409bd240fcaSzhanghailiang             result->has_error_code = false;
2410bd240fcaSzhanghailiang         } /* otherwise pretend successful re-(on|off)-lining */
2411bd240fcaSzhanghailiang     }
2412bd240fcaSzhanghailiang     g_free(status);
2413bd240fcaSzhanghailiang     close(dirfd);
2414bd240fcaSzhanghailiang     return;
2415bd240fcaSzhanghailiang 
2416bd240fcaSzhanghailiang out2:
2417bd240fcaSzhanghailiang     g_free(status);
2418bd240fcaSzhanghailiang     close(dirfd);
2419bd240fcaSzhanghailiang out1:
2420bd240fcaSzhanghailiang     if (!sys2memblk) {
2421bd240fcaSzhanghailiang         result->has_error_code = true;
2422bd240fcaSzhanghailiang         result->error_code = errno;
2423bd240fcaSzhanghailiang     }
2424bd240fcaSzhanghailiang }
2425bd240fcaSzhanghailiang 
2426a065aaa9Szhanghailiang GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
2427a065aaa9Szhanghailiang {
2428c3033fd3SEric Blake     GuestMemoryBlockList *head, **tail;
2429bd240fcaSzhanghailiang     Error *local_err = NULL;
2430bd240fcaSzhanghailiang     struct dirent *de;
2431bd240fcaSzhanghailiang     DIR *dp;
2432bd240fcaSzhanghailiang 
2433bd240fcaSzhanghailiang     head = NULL;
2434c3033fd3SEric Blake     tail = &head;
2435bd240fcaSzhanghailiang 
2436bd240fcaSzhanghailiang     dp = opendir("/sys/devices/system/memory/");
2437bd240fcaSzhanghailiang     if (!dp) {
2438f693fe6eSMichael Roth         /* it's ok if this happens to be a system that doesn't expose
2439f693fe6eSMichael Roth          * memory blocks via sysfs, but otherwise we should report
2440f693fe6eSMichael Roth          * an error
2441f693fe6eSMichael Roth          */
2442f693fe6eSMichael Roth         if (errno != ENOENT) {
2443bd240fcaSzhanghailiang             error_setg_errno(errp, errno, "Can't open directory"
24449af9e0feSMarkus Armbruster                              "\"/sys/devices/system/memory/\"");
2445f693fe6eSMichael Roth         }
2446bd240fcaSzhanghailiang         return NULL;
2447bd240fcaSzhanghailiang     }
2448bd240fcaSzhanghailiang 
2449bd240fcaSzhanghailiang     /* Note: the phys_index of memory block may be discontinuous,
2450bd240fcaSzhanghailiang      * this is because a memblk is the unit of the Sparse Memory design, which
2451bd240fcaSzhanghailiang      * allows discontinuous memory ranges (ex. NUMA), so here we should
2452bd240fcaSzhanghailiang      * traverse the memory block directory.
2453bd240fcaSzhanghailiang      */
2454bd240fcaSzhanghailiang     while ((de = readdir(dp)) != NULL) {
2455bd240fcaSzhanghailiang         GuestMemoryBlock *mem_blk;
2456bd240fcaSzhanghailiang 
2457bd240fcaSzhanghailiang         if ((strncmp(de->d_name, "memory", 6) != 0) ||
2458bd240fcaSzhanghailiang             !(de->d_type & DT_DIR)) {
2459bd240fcaSzhanghailiang             continue;
2460bd240fcaSzhanghailiang         }
2461bd240fcaSzhanghailiang 
2462bd240fcaSzhanghailiang         mem_blk = g_malloc0(sizeof *mem_blk);
2463bd240fcaSzhanghailiang         /* The d_name is "memoryXXX",  phys_index is block id, same as XXX */
2464bd240fcaSzhanghailiang         mem_blk->phys_index = strtoul(&de->d_name[6], NULL, 10);
2465bd240fcaSzhanghailiang         mem_blk->has_can_offline = true; /* lolspeak ftw */
2466bd240fcaSzhanghailiang         transfer_memory_block(mem_blk, true, NULL, &local_err);
24674155c998SMarkus Armbruster         if (local_err) {
24684155c998SMarkus Armbruster             break;
24694155c998SMarkus Armbruster         }
2470bd240fcaSzhanghailiang 
2471c3033fd3SEric Blake         QAPI_LIST_APPEND(tail, mem_blk);
2472bd240fcaSzhanghailiang     }
2473bd240fcaSzhanghailiang 
2474bd240fcaSzhanghailiang     closedir(dp);
2475bd240fcaSzhanghailiang     if (local_err == NULL) {
2476bd240fcaSzhanghailiang         /* there's no guest with zero memory blocks */
2477bd240fcaSzhanghailiang         if (head == NULL) {
2478bd240fcaSzhanghailiang             error_setg(errp, "guest reported zero memory blocks!");
2479bd240fcaSzhanghailiang         }
2480bd240fcaSzhanghailiang         return head;
2481bd240fcaSzhanghailiang     }
2482bd240fcaSzhanghailiang 
2483bd240fcaSzhanghailiang     qapi_free_GuestMemoryBlockList(head);
2484bd240fcaSzhanghailiang     error_propagate(errp, local_err);
2485a065aaa9Szhanghailiang     return NULL;
2486a065aaa9Szhanghailiang }
2487a065aaa9Szhanghailiang 
2488a065aaa9Szhanghailiang GuestMemoryBlockResponseList *
2489a065aaa9Szhanghailiang qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
2490a065aaa9Szhanghailiang {
2491c3033fd3SEric Blake     GuestMemoryBlockResponseList *head, **tail;
249232ca7927Szhanghailiang     Error *local_err = NULL;
249332ca7927Szhanghailiang 
249432ca7927Szhanghailiang     head = NULL;
2495c3033fd3SEric Blake     tail = &head;
249632ca7927Szhanghailiang 
249732ca7927Szhanghailiang     while (mem_blks != NULL) {
249832ca7927Szhanghailiang         GuestMemoryBlockResponse *result;
249932ca7927Szhanghailiang         GuestMemoryBlock *current_mem_blk = mem_blks->value;
250032ca7927Szhanghailiang 
250132ca7927Szhanghailiang         result = g_malloc0(sizeof(*result));
250232ca7927Szhanghailiang         result->phys_index = current_mem_blk->phys_index;
250332ca7927Szhanghailiang         transfer_memory_block(current_mem_blk, false, result, &local_err);
250432ca7927Szhanghailiang         if (local_err) { /* should never happen */
250532ca7927Szhanghailiang             goto err;
250632ca7927Szhanghailiang         }
250732ca7927Szhanghailiang 
2508c3033fd3SEric Blake         QAPI_LIST_APPEND(tail, result);
250932ca7927Szhanghailiang         mem_blks = mem_blks->next;
251032ca7927Szhanghailiang     }
251132ca7927Szhanghailiang 
251232ca7927Szhanghailiang     return head;
251332ca7927Szhanghailiang err:
251432ca7927Szhanghailiang     qapi_free_GuestMemoryBlockResponseList(head);
251532ca7927Szhanghailiang     error_propagate(errp, local_err);
2516a065aaa9Szhanghailiang     return NULL;
2517a065aaa9Szhanghailiang }
2518a065aaa9Szhanghailiang 
2519a065aaa9Szhanghailiang GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
2520a065aaa9Szhanghailiang {
2521ef82b60bSzhanghailiang     Error *local_err = NULL;
2522ef82b60bSzhanghailiang     char *dirpath;
2523ef82b60bSzhanghailiang     int dirfd;
2524ef82b60bSzhanghailiang     char *buf;
2525ef82b60bSzhanghailiang     GuestMemoryBlockInfo *info;
2526ef82b60bSzhanghailiang 
2527ef82b60bSzhanghailiang     dirpath = g_strdup_printf("/sys/devices/system/memory/");
2528ef82b60bSzhanghailiang     dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
2529ef82b60bSzhanghailiang     if (dirfd == -1) {
2530ef82b60bSzhanghailiang         error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
2531ef82b60bSzhanghailiang         g_free(dirpath);
2532a065aaa9Szhanghailiang         return NULL;
2533a065aaa9Szhanghailiang     }
2534ef82b60bSzhanghailiang     g_free(dirpath);
2535ef82b60bSzhanghailiang 
2536ef82b60bSzhanghailiang     buf = g_malloc0(20);
2537ef82b60bSzhanghailiang     ga_read_sysfs_file(dirfd, "block_size_bytes", buf, 20, &local_err);
25388ce1ee46SShannon Zhao     close(dirfd);
2539ef82b60bSzhanghailiang     if (local_err) {
2540ef82b60bSzhanghailiang         g_free(buf);
2541ef82b60bSzhanghailiang         error_propagate(errp, local_err);
2542ef82b60bSzhanghailiang         return NULL;
2543ef82b60bSzhanghailiang     }
2544ef82b60bSzhanghailiang 
2545ef82b60bSzhanghailiang     info = g_new0(GuestMemoryBlockInfo, 1);
2546ef82b60bSzhanghailiang     info->size = strtol(buf, NULL, 16); /* the unit is bytes */
2547ef82b60bSzhanghailiang 
2548ef82b60bSzhanghailiang     g_free(buf);
2549ef82b60bSzhanghailiang 
2550ef82b60bSzhanghailiang     return info;
2551ef82b60bSzhanghailiang }
2552a065aaa9Szhanghailiang 
25533569664eSluzhipeng #define MAX_NAME_LEN 128
25543569664eSluzhipeng static GuestDiskStatsInfoList *guest_get_diskstats(Error **errp)
25553569664eSluzhipeng {
25563569664eSluzhipeng #ifdef CONFIG_LINUX
25573569664eSluzhipeng     GuestDiskStatsInfoList *head = NULL, **tail = &head;
25583569664eSluzhipeng     const char *diskstats = "/proc/diskstats";
25593569664eSluzhipeng     FILE *fp;
25603569664eSluzhipeng     size_t n;
25613569664eSluzhipeng     char *line = NULL;
25623569664eSluzhipeng 
25633569664eSluzhipeng     fp = fopen(diskstats, "r");
25643569664eSluzhipeng     if (fp  == NULL) {
25653569664eSluzhipeng         error_setg_errno(errp, errno, "open(\"%s\")", diskstats);
25663569664eSluzhipeng         return NULL;
25673569664eSluzhipeng     }
25683569664eSluzhipeng 
25693569664eSluzhipeng     while (getline(&line, &n, fp) != -1) {
25703569664eSluzhipeng         g_autofree GuestDiskStatsInfo *diskstatinfo = NULL;
25713569664eSluzhipeng         g_autofree GuestDiskStats *diskstat = NULL;
25723569664eSluzhipeng         char dev_name[MAX_NAME_LEN];
25733569664eSluzhipeng         unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks, dc_ticks, fl_ticks;
25743569664eSluzhipeng         unsigned long rd_ios, rd_merges_or_rd_sec, rd_ticks_or_wr_sec, wr_ios;
25753569664eSluzhipeng         unsigned long wr_merges, rd_sec_or_wr_ios, wr_sec;
25763569664eSluzhipeng         unsigned long dc_ios, dc_merges, dc_sec, fl_ios;
25773569664eSluzhipeng         unsigned int major, minor;
25783569664eSluzhipeng         int i;
25793569664eSluzhipeng 
25803569664eSluzhipeng         i = sscanf(line, "%u %u %s %lu %lu %lu"
25813569664eSluzhipeng                    "%lu %lu %lu %lu %u %u %u %u"
25823569664eSluzhipeng                    "%lu %lu %lu %u %lu %u",
25833569664eSluzhipeng                    &major, &minor, dev_name,
25843569664eSluzhipeng                    &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios,
25853569664eSluzhipeng                    &rd_ticks_or_wr_sec, &wr_ios, &wr_merges, &wr_sec,
25863569664eSluzhipeng                    &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks,
25873569664eSluzhipeng                    &dc_ios, &dc_merges, &dc_sec, &dc_ticks,
25883569664eSluzhipeng                    &fl_ios, &fl_ticks);
25893569664eSluzhipeng 
25903569664eSluzhipeng         if (i < 7) {
25913569664eSluzhipeng             continue;
25923569664eSluzhipeng         }
25933569664eSluzhipeng 
25943569664eSluzhipeng         diskstatinfo = g_new0(GuestDiskStatsInfo, 1);
25953569664eSluzhipeng         diskstatinfo->name = g_strdup(dev_name);
25963569664eSluzhipeng         diskstatinfo->major = major;
25973569664eSluzhipeng         diskstatinfo->minor = minor;
25983569664eSluzhipeng 
25993569664eSluzhipeng         diskstat = g_new0(GuestDiskStats, 1);
26003569664eSluzhipeng         if (i == 7) {
26013569664eSluzhipeng             diskstat->has_read_ios = true;
26023569664eSluzhipeng             diskstat->read_ios = rd_ios;
26033569664eSluzhipeng             diskstat->has_read_sectors = true;
26043569664eSluzhipeng             diskstat->read_sectors = rd_merges_or_rd_sec;
26053569664eSluzhipeng             diskstat->has_write_ios = true;
26063569664eSluzhipeng             diskstat->write_ios = rd_sec_or_wr_ios;
26073569664eSluzhipeng             diskstat->has_write_sectors = true;
26083569664eSluzhipeng             diskstat->write_sectors = rd_ticks_or_wr_sec;
26093569664eSluzhipeng         }
26103569664eSluzhipeng         if (i >= 14) {
26113569664eSluzhipeng             diskstat->has_read_ios = true;
26123569664eSluzhipeng             diskstat->read_ios = rd_ios;
26133569664eSluzhipeng             diskstat->has_read_sectors = true;
26143569664eSluzhipeng             diskstat->read_sectors = rd_sec_or_wr_ios;
26153569664eSluzhipeng             diskstat->has_read_merges = true;
26163569664eSluzhipeng             diskstat->read_merges = rd_merges_or_rd_sec;
26173569664eSluzhipeng             diskstat->has_read_ticks = true;
26183569664eSluzhipeng             diskstat->read_ticks = rd_ticks_or_wr_sec;
26193569664eSluzhipeng             diskstat->has_write_ios = true;
26203569664eSluzhipeng             diskstat->write_ios = wr_ios;
26213569664eSluzhipeng             diskstat->has_write_sectors = true;
26223569664eSluzhipeng             diskstat->write_sectors = wr_sec;
26233569664eSluzhipeng             diskstat->has_write_merges = true;
26243569664eSluzhipeng             diskstat->write_merges = wr_merges;
26253569664eSluzhipeng             diskstat->has_write_ticks = true;
26263569664eSluzhipeng             diskstat->write_ticks = wr_ticks;
26273569664eSluzhipeng             diskstat->has_ios_pgr = true;
26283569664eSluzhipeng             diskstat->ios_pgr = ios_pgr;
26293569664eSluzhipeng             diskstat->has_total_ticks = true;
26303569664eSluzhipeng             diskstat->total_ticks = tot_ticks;
26313569664eSluzhipeng             diskstat->has_weight_ticks = true;
26323569664eSluzhipeng             diskstat->weight_ticks = rq_ticks;
26333569664eSluzhipeng         }
26343569664eSluzhipeng         if (i >= 18) {
26353569664eSluzhipeng             diskstat->has_discard_ios = true;
26363569664eSluzhipeng             diskstat->discard_ios = dc_ios;
26373569664eSluzhipeng             diskstat->has_discard_merges = true;
26383569664eSluzhipeng             diskstat->discard_merges = dc_merges;
26393569664eSluzhipeng             diskstat->has_discard_sectors = true;
26403569664eSluzhipeng             diskstat->discard_sectors = dc_sec;
26413569664eSluzhipeng             diskstat->has_discard_ticks = true;
26423569664eSluzhipeng             diskstat->discard_ticks = dc_ticks;
26433569664eSluzhipeng         }
26443569664eSluzhipeng         if (i >= 20) {
26453569664eSluzhipeng             diskstat->has_flush_ios = true;
26463569664eSluzhipeng             diskstat->flush_ios = fl_ios;
26473569664eSluzhipeng             diskstat->has_flush_ticks = true;
26483569664eSluzhipeng             diskstat->flush_ticks = fl_ticks;
26493569664eSluzhipeng         }
26503569664eSluzhipeng 
26513569664eSluzhipeng         diskstatinfo->stats = g_steal_pointer(&diskstat);
26523569664eSluzhipeng         QAPI_LIST_APPEND(tail, diskstatinfo);
26533569664eSluzhipeng         diskstatinfo = NULL;
26543569664eSluzhipeng     }
26553569664eSluzhipeng     free(line);
26563569664eSluzhipeng     fclose(fp);
26573569664eSluzhipeng     return head;
26583569664eSluzhipeng #else
26593569664eSluzhipeng     g_debug("disk stats reporting available only for Linux");
26603569664eSluzhipeng     return NULL;
26613569664eSluzhipeng #endif
26623569664eSluzhipeng }
26633569664eSluzhipeng 
26643569664eSluzhipeng GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp)
26653569664eSluzhipeng {
26663569664eSluzhipeng     return guest_get_diskstats(errp);
26673569664eSluzhipeng }
26683569664eSluzhipeng 
26691db8a0b0Szhenwei pi GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
26701db8a0b0Szhenwei pi {
26711db8a0b0Szhenwei pi     GuestCpuStatsList *head = NULL, **tail = &head;
26721db8a0b0Szhenwei pi     const char *cpustats = "/proc/stat";
26731db8a0b0Szhenwei pi     int clk_tck = sysconf(_SC_CLK_TCK);
26741db8a0b0Szhenwei pi     FILE *fp;
26751db8a0b0Szhenwei pi     size_t n;
26761db8a0b0Szhenwei pi     char *line = NULL;
26771db8a0b0Szhenwei pi 
26781db8a0b0Szhenwei pi     fp = fopen(cpustats, "r");
26791db8a0b0Szhenwei pi     if (fp  == NULL) {
26801db8a0b0Szhenwei pi         error_setg_errno(errp, errno, "open(\"%s\")", cpustats);
26811db8a0b0Szhenwei pi         return NULL;
26821db8a0b0Szhenwei pi     }
26831db8a0b0Szhenwei pi 
26841db8a0b0Szhenwei pi     while (getline(&line, &n, fp) != -1) {
26851db8a0b0Szhenwei pi         GuestCpuStats *cpustat = NULL;
26861db8a0b0Szhenwei pi         GuestLinuxCpuStats *linuxcpustat;
26871db8a0b0Szhenwei pi         int i;
26881db8a0b0Szhenwei pi         unsigned long user, system, idle, iowait, irq, softirq, steal, guest;
26891db8a0b0Szhenwei pi         unsigned long nice, guest_nice;
26901db8a0b0Szhenwei pi         char name[64];
26911db8a0b0Szhenwei pi 
26921db8a0b0Szhenwei pi         i = sscanf(line, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
26931db8a0b0Szhenwei pi                    name, &user, &nice, &system, &idle, &iowait, &irq, &softirq,
26941db8a0b0Szhenwei pi                    &steal, &guest, &guest_nice);
26951db8a0b0Szhenwei pi 
26961db8a0b0Szhenwei pi         /* drop "cpu 1 2 3 ...", get "cpuX 1 2 3 ..." only */
26971db8a0b0Szhenwei pi         if ((i == EOF) || strncmp(name, "cpu", 3) || (name[3] == '\0')) {
26981db8a0b0Szhenwei pi             continue;
26991db8a0b0Szhenwei pi         }
27001db8a0b0Szhenwei pi 
27011db8a0b0Szhenwei pi         if (i < 5) {
27021db8a0b0Szhenwei pi             slog("Parsing cpu stat from %s failed, see \"man proc\"", cpustats);
27031db8a0b0Szhenwei pi             break;
27041db8a0b0Szhenwei pi         }
27051db8a0b0Szhenwei pi 
27061db8a0b0Szhenwei pi         cpustat = g_new0(GuestCpuStats, 1);
27071db8a0b0Szhenwei pi         cpustat->type = GUEST_CPU_STATS_TYPE_LINUX;
27081db8a0b0Szhenwei pi 
27091db8a0b0Szhenwei pi         linuxcpustat = &cpustat->u.q_linux;
27101db8a0b0Szhenwei pi         linuxcpustat->cpu = atoi(&name[3]);
27111db8a0b0Szhenwei pi         linuxcpustat->user = user * 1000 / clk_tck;
27121db8a0b0Szhenwei pi         linuxcpustat->nice = nice * 1000 / clk_tck;
27131db8a0b0Szhenwei pi         linuxcpustat->system = system * 1000 / clk_tck;
27141db8a0b0Szhenwei pi         linuxcpustat->idle = idle * 1000 / clk_tck;
27151db8a0b0Szhenwei pi 
27161db8a0b0Szhenwei pi         if (i > 5) {
27171db8a0b0Szhenwei pi             linuxcpustat->has_iowait = true;
27181db8a0b0Szhenwei pi             linuxcpustat->iowait = iowait * 1000 / clk_tck;
27191db8a0b0Szhenwei pi         }
27201db8a0b0Szhenwei pi 
27211db8a0b0Szhenwei pi         if (i > 6) {
27221db8a0b0Szhenwei pi             linuxcpustat->has_irq = true;
27231db8a0b0Szhenwei pi             linuxcpustat->irq = irq * 1000 / clk_tck;
27241db8a0b0Szhenwei pi             linuxcpustat->has_softirq = true;
27251db8a0b0Szhenwei pi             linuxcpustat->softirq = softirq * 1000 / clk_tck;
27261db8a0b0Szhenwei pi         }
27271db8a0b0Szhenwei pi 
27281db8a0b0Szhenwei pi         if (i > 8) {
27291db8a0b0Szhenwei pi             linuxcpustat->has_steal = true;
27301db8a0b0Szhenwei pi             linuxcpustat->steal = steal * 1000 / clk_tck;
27311db8a0b0Szhenwei pi         }
27321db8a0b0Szhenwei pi 
27331db8a0b0Szhenwei pi         if (i > 9) {
27341db8a0b0Szhenwei pi             linuxcpustat->has_guest = true;
27351db8a0b0Szhenwei pi             linuxcpustat->guest = guest * 1000 / clk_tck;
27361db8a0b0Szhenwei pi         }
27371db8a0b0Szhenwei pi 
27381db8a0b0Szhenwei pi         if (i > 10) {
27391db8a0b0Szhenwei pi             linuxcpustat->has_guest = true;
27401db8a0b0Szhenwei pi             linuxcpustat->guest = guest * 1000 / clk_tck;
27411db8a0b0Szhenwei pi             linuxcpustat->has_guestnice = true;
27421db8a0b0Szhenwei pi             linuxcpustat->guestnice = guest_nice * 1000 / clk_tck;
27431db8a0b0Szhenwei pi         }
27441db8a0b0Szhenwei pi 
27451db8a0b0Szhenwei pi         QAPI_LIST_APPEND(tail, cpustat);
27461db8a0b0Szhenwei pi     }
27471db8a0b0Szhenwei pi 
27481db8a0b0Szhenwei pi     free(line);
27491db8a0b0Szhenwei pi     fclose(fp);
27501db8a0b0Szhenwei pi     return head;
27511db8a0b0Szhenwei pi }
27521db8a0b0Szhenwei pi 
2753e72c3f2eSMichael Roth #else /* defined(__linux__) */
2754e72c3f2eSMichael Roth 
275577dbc81bSMarkus Armbruster void qmp_guest_suspend_disk(Error **errp)
2756e72c3f2eSMichael Roth {
2757c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2758e72c3f2eSMichael Roth }
2759e72c3f2eSMichael Roth 
276077dbc81bSMarkus Armbruster void qmp_guest_suspend_ram(Error **errp)
2761e72c3f2eSMichael Roth {
2762c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2763e72c3f2eSMichael Roth }
2764e72c3f2eSMichael Roth 
276577dbc81bSMarkus Armbruster void qmp_guest_suspend_hybrid(Error **errp)
2766e72c3f2eSMichael Roth {
2767c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2768e72c3f2eSMichael Roth }
2769e72c3f2eSMichael Roth 
2770d2baff62SLaszlo Ersek GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
2771d2baff62SLaszlo Ersek {
2772c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2773d2baff62SLaszlo Ersek     return NULL;
2774d2baff62SLaszlo Ersek }
2775d2baff62SLaszlo Ersek 
2776cbb65fc2SLaszlo Ersek int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
2777cbb65fc2SLaszlo Ersek {
2778c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2779cbb65fc2SLaszlo Ersek     return -1;
2780cbb65fc2SLaszlo Ersek }
2781cbb65fc2SLaszlo Ersek 
2782a065aaa9Szhanghailiang GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
2783a065aaa9Szhanghailiang {
2784c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2785a065aaa9Szhanghailiang     return NULL;
2786a065aaa9Szhanghailiang }
2787a065aaa9Szhanghailiang 
2788a065aaa9Szhanghailiang GuestMemoryBlockResponseList *
2789a065aaa9Szhanghailiang qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
2790a065aaa9Szhanghailiang {
2791c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2792a065aaa9Szhanghailiang     return NULL;
2793a065aaa9Szhanghailiang }
2794a065aaa9Szhanghailiang 
2795a065aaa9Szhanghailiang GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
2796a065aaa9Szhanghailiang {
2797c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
2798a065aaa9Szhanghailiang     return NULL;
2799a065aaa9Szhanghailiang }
2800a065aaa9Szhanghailiang 
2801e72c3f2eSMichael Roth #endif
2802e72c3f2eSMichael Roth 
280359e35c7bSAndrew Deason #ifdef HAVE_GETIFADDRS
280459e35c7bSAndrew Deason static GuestNetworkInterface *
280559e35c7bSAndrew Deason guest_find_interface(GuestNetworkInterfaceList *head,
280659e35c7bSAndrew Deason                      const char *name)
280759e35c7bSAndrew Deason {
280859e35c7bSAndrew Deason     for (; head; head = head->next) {
280959e35c7bSAndrew Deason         if (strcmp(head->value->name, name) == 0) {
281059e35c7bSAndrew Deason             return head->value;
281159e35c7bSAndrew Deason         }
281259e35c7bSAndrew Deason     }
281359e35c7bSAndrew Deason 
281459e35c7bSAndrew Deason     return NULL;
281559e35c7bSAndrew Deason }
281659e35c7bSAndrew Deason 
281759e35c7bSAndrew Deason static int guest_get_network_stats(const char *name,
281859e35c7bSAndrew Deason                        GuestNetworkInterfaceStat *stats)
281959e35c7bSAndrew Deason {
282070335c46SAndrew Deason #ifdef CONFIG_LINUX
282159e35c7bSAndrew Deason     int name_len;
282259e35c7bSAndrew Deason     char const *devinfo = "/proc/net/dev";
282359e35c7bSAndrew Deason     FILE *fp;
282459e35c7bSAndrew Deason     char *line = NULL, *colon;
282559e35c7bSAndrew Deason     size_t n = 0;
282659e35c7bSAndrew Deason     fp = fopen(devinfo, "r");
282759e35c7bSAndrew Deason     if (!fp) {
2828a539dc8aSAndrew Deason         g_debug("failed to open network stats %s: %s", devinfo,
2829a539dc8aSAndrew Deason                 g_strerror(errno));
283059e35c7bSAndrew Deason         return -1;
283159e35c7bSAndrew Deason     }
283259e35c7bSAndrew Deason     name_len = strlen(name);
283359e35c7bSAndrew Deason     while (getline(&line, &n, fp) != -1) {
283459e35c7bSAndrew Deason         long long dummy;
283559e35c7bSAndrew Deason         long long rx_bytes;
283659e35c7bSAndrew Deason         long long rx_packets;
283759e35c7bSAndrew Deason         long long rx_errs;
283859e35c7bSAndrew Deason         long long rx_dropped;
283959e35c7bSAndrew Deason         long long tx_bytes;
284059e35c7bSAndrew Deason         long long tx_packets;
284159e35c7bSAndrew Deason         long long tx_errs;
284259e35c7bSAndrew Deason         long long tx_dropped;
284359e35c7bSAndrew Deason         char *trim_line;
284459e35c7bSAndrew Deason         trim_line = g_strchug(line);
284559e35c7bSAndrew Deason         if (trim_line[0] == '\0') {
284659e35c7bSAndrew Deason             continue;
284759e35c7bSAndrew Deason         }
284859e35c7bSAndrew Deason         colon = strchr(trim_line, ':');
284959e35c7bSAndrew Deason         if (!colon) {
285059e35c7bSAndrew Deason             continue;
285159e35c7bSAndrew Deason         }
285259e35c7bSAndrew Deason         if (colon - name_len  == trim_line &&
285359e35c7bSAndrew Deason            strncmp(trim_line, name, name_len) == 0) {
285459e35c7bSAndrew Deason             if (sscanf(colon + 1,
285559e35c7bSAndrew Deason                 "%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld",
285659e35c7bSAndrew Deason                   &rx_bytes, &rx_packets, &rx_errs, &rx_dropped,
285759e35c7bSAndrew Deason                   &dummy, &dummy, &dummy, &dummy,
285859e35c7bSAndrew Deason                   &tx_bytes, &tx_packets, &tx_errs, &tx_dropped,
285959e35c7bSAndrew Deason                   &dummy, &dummy, &dummy, &dummy) != 16) {
286059e35c7bSAndrew Deason                 continue;
286159e35c7bSAndrew Deason             }
286259e35c7bSAndrew Deason             stats->rx_bytes = rx_bytes;
286359e35c7bSAndrew Deason             stats->rx_packets = rx_packets;
286459e35c7bSAndrew Deason             stats->rx_errs = rx_errs;
286559e35c7bSAndrew Deason             stats->rx_dropped = rx_dropped;
286659e35c7bSAndrew Deason             stats->tx_bytes = tx_bytes;
286759e35c7bSAndrew Deason             stats->tx_packets = tx_packets;
286859e35c7bSAndrew Deason             stats->tx_errs = tx_errs;
286959e35c7bSAndrew Deason             stats->tx_dropped = tx_dropped;
287059e35c7bSAndrew Deason             fclose(fp);
287159e35c7bSAndrew Deason             g_free(line);
287259e35c7bSAndrew Deason             return 0;
287359e35c7bSAndrew Deason         }
287459e35c7bSAndrew Deason     }
287559e35c7bSAndrew Deason     fclose(fp);
287659e35c7bSAndrew Deason     g_free(line);
287759e35c7bSAndrew Deason     g_debug("/proc/net/dev: Interface '%s' not found", name);
2878a539dc8aSAndrew Deason #else /* !CONFIG_LINUX */
2879a539dc8aSAndrew Deason     g_debug("Network stats reporting available only for Linux");
2880a539dc8aSAndrew Deason #endif /* !CONFIG_LINUX */
288159e35c7bSAndrew Deason     return -1;
288259e35c7bSAndrew Deason }
288359e35c7bSAndrew Deason 
2884*a1241094SAlexander Ivanov #ifndef __FreeBSD__
2885*a1241094SAlexander Ivanov /*
2886*a1241094SAlexander Ivanov  * Fill "buf" with MAC address by ifaddrs. Pointer buf must point to a
2887*a1241094SAlexander Ivanov  * buffer with ETHER_ADDR_LEN length at least.
2888*a1241094SAlexander Ivanov  *
2889*a1241094SAlexander Ivanov  * Returns false in case of an error, otherwise true. "obtained" argument
2890*a1241094SAlexander Ivanov  * is true if a MAC address was obtained successful, otherwise false.
2891*a1241094SAlexander Ivanov  */
2892*a1241094SAlexander Ivanov bool guest_get_hw_addr(struct ifaddrs *ifa, unsigned char *buf,
2893*a1241094SAlexander Ivanov                        bool *obtained, Error **errp)
2894*a1241094SAlexander Ivanov {
2895*a1241094SAlexander Ivanov     struct ifreq ifr;
2896*a1241094SAlexander Ivanov     int sock;
2897*a1241094SAlexander Ivanov 
2898*a1241094SAlexander Ivanov     *obtained = false;
2899*a1241094SAlexander Ivanov 
2900*a1241094SAlexander Ivanov     /* we haven't obtained HW address yet */
2901*a1241094SAlexander Ivanov     sock = socket(PF_INET, SOCK_STREAM, 0);
2902*a1241094SAlexander Ivanov     if (sock == -1) {
2903*a1241094SAlexander Ivanov         error_setg_errno(errp, errno, "failed to create socket");
2904*a1241094SAlexander Ivanov         return false;
2905*a1241094SAlexander Ivanov     }
2906*a1241094SAlexander Ivanov 
2907*a1241094SAlexander Ivanov     memset(&ifr, 0, sizeof(ifr));
2908*a1241094SAlexander Ivanov     pstrcpy(ifr.ifr_name, IF_NAMESIZE, ifa->ifa_name);
2909*a1241094SAlexander Ivanov     if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
2910*a1241094SAlexander Ivanov         /*
2911*a1241094SAlexander Ivanov          * We can't get the hw addr of this interface, but that's not a
2912*a1241094SAlexander Ivanov          * fatal error.
2913*a1241094SAlexander Ivanov          */
2914*a1241094SAlexander Ivanov         if (errno == EADDRNOTAVAIL) {
2915*a1241094SAlexander Ivanov             /* The interface doesn't have a hw addr (e.g. loopback). */
2916*a1241094SAlexander Ivanov             g_debug("failed to get MAC address of %s: %s",
2917*a1241094SAlexander Ivanov                     ifa->ifa_name, strerror(errno));
2918*a1241094SAlexander Ivanov         } else{
2919*a1241094SAlexander Ivanov             g_warning("failed to get MAC address of %s: %s",
2920*a1241094SAlexander Ivanov                       ifa->ifa_name, strerror(errno));
2921*a1241094SAlexander Ivanov         }
2922*a1241094SAlexander Ivanov     } else {
2923*a1241094SAlexander Ivanov #ifdef CONFIG_SOLARIS
2924*a1241094SAlexander Ivanov         memcpy(buf, &ifr.ifr_addr.sa_data, ETHER_ADDR_LEN);
2925*a1241094SAlexander Ivanov #else
2926*a1241094SAlexander Ivanov         memcpy(buf, &ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
2927*a1241094SAlexander Ivanov #endif
2928*a1241094SAlexander Ivanov         *obtained = true;
2929*a1241094SAlexander Ivanov     }
2930*a1241094SAlexander Ivanov     close(sock);
2931*a1241094SAlexander Ivanov     return true;
2932*a1241094SAlexander Ivanov }
2933*a1241094SAlexander Ivanov #endif /* __FreeBSD__ */
2934*a1241094SAlexander Ivanov 
293559e35c7bSAndrew Deason /*
293659e35c7bSAndrew Deason  * Build information about guest interfaces
293759e35c7bSAndrew Deason  */
293859e35c7bSAndrew Deason GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
293959e35c7bSAndrew Deason {
294059e35c7bSAndrew Deason     GuestNetworkInterfaceList *head = NULL, **tail = &head;
294159e35c7bSAndrew Deason     struct ifaddrs *ifap, *ifa;
294259e35c7bSAndrew Deason 
294359e35c7bSAndrew Deason     if (getifaddrs(&ifap) < 0) {
294459e35c7bSAndrew Deason         error_setg_errno(errp, errno, "getifaddrs failed");
294559e35c7bSAndrew Deason         goto error;
294659e35c7bSAndrew Deason     }
294759e35c7bSAndrew Deason 
294859e35c7bSAndrew Deason     for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
294959e35c7bSAndrew Deason         GuestNetworkInterface *info;
295059e35c7bSAndrew Deason         GuestIpAddressList **address_tail;
295159e35c7bSAndrew Deason         GuestIpAddress *address_item = NULL;
295259e35c7bSAndrew Deason         GuestNetworkInterfaceStat *interface_stat = NULL;
295359e35c7bSAndrew Deason         char addr4[INET_ADDRSTRLEN];
295459e35c7bSAndrew Deason         char addr6[INET6_ADDRSTRLEN];
2955*a1241094SAlexander Ivanov         unsigned char mac_addr[ETHER_ADDR_LEN];
2956*a1241094SAlexander Ivanov         bool obtained;
295759e35c7bSAndrew Deason         void *p;
295859e35c7bSAndrew Deason 
295959e35c7bSAndrew Deason         g_debug("Processing %s interface", ifa->ifa_name);
296059e35c7bSAndrew Deason 
296159e35c7bSAndrew Deason         info = guest_find_interface(head, ifa->ifa_name);
296259e35c7bSAndrew Deason 
296359e35c7bSAndrew Deason         if (!info) {
296459e35c7bSAndrew Deason             info = g_malloc0(sizeof(*info));
296559e35c7bSAndrew Deason             info->name = g_strdup(ifa->ifa_name);
296659e35c7bSAndrew Deason 
296759e35c7bSAndrew Deason             QAPI_LIST_APPEND(tail, info);
296859e35c7bSAndrew Deason         }
296959e35c7bSAndrew Deason 
2970aec0730eSAndrew Deason         if (!info->has_hardware_address) {
2971*a1241094SAlexander Ivanov             if (!guest_get_hw_addr(ifa, mac_addr, &obtained, errp)) {
297259e35c7bSAndrew Deason                 goto error;
297359e35c7bSAndrew Deason             }
2974*a1241094SAlexander Ivanov             if (obtained) {
297559e35c7bSAndrew Deason                 info->hardware_address =
297659e35c7bSAndrew Deason                     g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x",
297759e35c7bSAndrew Deason                                     (int) mac_addr[0], (int) mac_addr[1],
297859e35c7bSAndrew Deason                                     (int) mac_addr[2], (int) mac_addr[3],
297959e35c7bSAndrew Deason                                     (int) mac_addr[4], (int) mac_addr[5]);
298059e35c7bSAndrew Deason                 info->has_hardware_address = true;
298159e35c7bSAndrew Deason             }
2982aec0730eSAndrew Deason         }
298359e35c7bSAndrew Deason 
298459e35c7bSAndrew Deason         if (ifa->ifa_addr &&
298559e35c7bSAndrew Deason             ifa->ifa_addr->sa_family == AF_INET) {
298659e35c7bSAndrew Deason             /* interface with IPv4 address */
298759e35c7bSAndrew Deason             p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
298859e35c7bSAndrew Deason             if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
298959e35c7bSAndrew Deason                 error_setg_errno(errp, errno, "inet_ntop failed");
299059e35c7bSAndrew Deason                 goto error;
299159e35c7bSAndrew Deason             }
299259e35c7bSAndrew Deason 
299359e35c7bSAndrew Deason             address_item = g_malloc0(sizeof(*address_item));
299459e35c7bSAndrew Deason             address_item->ip_address = g_strdup(addr4);
299559e35c7bSAndrew Deason             address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;
299659e35c7bSAndrew Deason 
299759e35c7bSAndrew Deason             if (ifa->ifa_netmask) {
299859e35c7bSAndrew Deason                 /* Count the number of set bits in netmask.
299959e35c7bSAndrew Deason                  * This is safe as '1' and '0' cannot be shuffled in netmask. */
300059e35c7bSAndrew Deason                 p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;
300159e35c7bSAndrew Deason                 address_item->prefix = ctpop32(((uint32_t *) p)[0]);
300259e35c7bSAndrew Deason             }
300359e35c7bSAndrew Deason         } else if (ifa->ifa_addr &&
300459e35c7bSAndrew Deason                    ifa->ifa_addr->sa_family == AF_INET6) {
300559e35c7bSAndrew Deason             /* interface with IPv6 address */
300659e35c7bSAndrew Deason             p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
300759e35c7bSAndrew Deason             if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
300859e35c7bSAndrew Deason                 error_setg_errno(errp, errno, "inet_ntop failed");
300959e35c7bSAndrew Deason                 goto error;
301059e35c7bSAndrew Deason             }
301159e35c7bSAndrew Deason 
301259e35c7bSAndrew Deason             address_item = g_malloc0(sizeof(*address_item));
301359e35c7bSAndrew Deason             address_item->ip_address = g_strdup(addr6);
301459e35c7bSAndrew Deason             address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;
301559e35c7bSAndrew Deason 
301659e35c7bSAndrew Deason             if (ifa->ifa_netmask) {
301759e35c7bSAndrew Deason                 /* Count the number of set bits in netmask.
301859e35c7bSAndrew Deason                  * This is safe as '1' and '0' cannot be shuffled in netmask. */
301959e35c7bSAndrew Deason                 p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
302059e35c7bSAndrew Deason                 address_item->prefix =
302159e35c7bSAndrew Deason                     ctpop32(((uint32_t *) p)[0]) +
302259e35c7bSAndrew Deason                     ctpop32(((uint32_t *) p)[1]) +
302359e35c7bSAndrew Deason                     ctpop32(((uint32_t *) p)[2]) +
302459e35c7bSAndrew Deason                     ctpop32(((uint32_t *) p)[3]);
302559e35c7bSAndrew Deason             }
302659e35c7bSAndrew Deason         }
302759e35c7bSAndrew Deason 
302859e35c7bSAndrew Deason         if (!address_item) {
302959e35c7bSAndrew Deason             continue;
303059e35c7bSAndrew Deason         }
303159e35c7bSAndrew Deason 
303259e35c7bSAndrew Deason         address_tail = &info->ip_addresses;
303359e35c7bSAndrew Deason         while (*address_tail) {
303459e35c7bSAndrew Deason             address_tail = &(*address_tail)->next;
303559e35c7bSAndrew Deason         }
303659e35c7bSAndrew Deason         QAPI_LIST_APPEND(address_tail, address_item);
303759e35c7bSAndrew Deason 
303859e35c7bSAndrew Deason         info->has_ip_addresses = true;
303959e35c7bSAndrew Deason 
304059e35c7bSAndrew Deason         if (!info->has_statistics) {
304159e35c7bSAndrew Deason             interface_stat = g_malloc0(sizeof(*interface_stat));
304259e35c7bSAndrew Deason             if (guest_get_network_stats(info->name, interface_stat) == -1) {
304359e35c7bSAndrew Deason                 info->has_statistics = false;
304459e35c7bSAndrew Deason                 g_free(interface_stat);
304559e35c7bSAndrew Deason             } else {
304659e35c7bSAndrew Deason                 info->statistics = interface_stat;
304759e35c7bSAndrew Deason                 info->has_statistics = true;
304859e35c7bSAndrew Deason             }
304959e35c7bSAndrew Deason         }
305059e35c7bSAndrew Deason     }
305159e35c7bSAndrew Deason 
305259e35c7bSAndrew Deason     freeifaddrs(ifap);
305359e35c7bSAndrew Deason     return head;
305459e35c7bSAndrew Deason 
305559e35c7bSAndrew Deason error:
305659e35c7bSAndrew Deason     freeifaddrs(ifap);
305759e35c7bSAndrew Deason     qapi_free_GuestNetworkInterfaceList(head);
305859e35c7bSAndrew Deason     return NULL;
305959e35c7bSAndrew Deason }
306059e35c7bSAndrew Deason 
306159e35c7bSAndrew Deason #else
306259e35c7bSAndrew Deason 
306359e35c7bSAndrew Deason GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
306459e35c7bSAndrew Deason {
306559e35c7bSAndrew Deason     error_setg(errp, QERR_UNSUPPORTED);
306659e35c7bSAndrew Deason     return NULL;
306759e35c7bSAndrew Deason }
306859e35c7bSAndrew Deason 
306959e35c7bSAndrew Deason #endif /* HAVE_GETIFADDRS */
307059e35c7bSAndrew Deason 
3071d35d4cb5SMichael Roth #if !defined(CONFIG_FSFREEZE)
3072d35d4cb5SMichael Roth 
307346d4c572STomoki Sekiyama GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
307446d4c572STomoki Sekiyama {
3075c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
307646d4c572STomoki Sekiyama     return NULL;
307746d4c572STomoki Sekiyama }
307846d4c572STomoki Sekiyama 
307977dbc81bSMarkus Armbruster GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
3080d35d4cb5SMichael Roth {
3081c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
3082d35d4cb5SMichael Roth 
3083d35d4cb5SMichael Roth     return 0;
3084d35d4cb5SMichael Roth }
3085d35d4cb5SMichael Roth 
308677dbc81bSMarkus Armbruster int64_t qmp_guest_fsfreeze_freeze(Error **errp)
3087d35d4cb5SMichael Roth {
3088c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
3089d35d4cb5SMichael Roth 
3090d35d4cb5SMichael Roth     return 0;
3091d35d4cb5SMichael Roth }
3092d35d4cb5SMichael Roth 
3093e99bce20STomoki Sekiyama int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
3094e99bce20STomoki Sekiyama                                        strList *mountpoints,
3095e99bce20STomoki Sekiyama                                        Error **errp)
3096e99bce20STomoki Sekiyama {
3097c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
3098e99bce20STomoki Sekiyama 
3099e99bce20STomoki Sekiyama     return 0;
3100e99bce20STomoki Sekiyama }
3101e99bce20STomoki Sekiyama 
310277dbc81bSMarkus Armbruster int64_t qmp_guest_fsfreeze_thaw(Error **errp)
3103d35d4cb5SMichael Roth {
3104c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
3105d35d4cb5SMichael Roth 
3106d35d4cb5SMichael Roth     return 0;
3107d35d4cb5SMichael Roth }
3108fed39564STomáš Golembiovský 
3109fed39564STomáš Golembiovský GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
3110fed39564STomáš Golembiovský {
3111fed39564STomáš Golembiovský     error_setg(errp, QERR_UNSUPPORTED);
3112fed39564STomáš Golembiovský     return NULL;
3113fed39564STomáš Golembiovský }
3114fed39564STomáš Golembiovský 
31153569664eSluzhipeng GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp)
31163569664eSluzhipeng {
31173569664eSluzhipeng     error_setg(errp, QERR_UNSUPPORTED);
31183569664eSluzhipeng     return NULL;
31193569664eSluzhipeng }
31203569664eSluzhipeng 
31211db8a0b0Szhenwei pi GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
31221db8a0b0Szhenwei pi {
31231db8a0b0Szhenwei pi     error_setg(errp, QERR_UNSUPPORTED);
31241db8a0b0Szhenwei pi     return NULL;
31251db8a0b0Szhenwei pi }
31263569664eSluzhipeng 
3127eab5fd59SPaolo Bonzini #endif /* CONFIG_FSFREEZE */
3128d35d4cb5SMichael Roth 
3129eab5fd59SPaolo Bonzini #if !defined(CONFIG_FSTRIM)
3130e82855d9SJustin Ossevoort GuestFilesystemTrimResponse *
3131e82855d9SJustin Ossevoort qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
3132eab5fd59SPaolo Bonzini {
3133c6bd8c70SMarkus Armbruster     error_setg(errp, QERR_UNSUPPORTED);
3134e82855d9SJustin Ossevoort     return NULL;
3135eab5fd59SPaolo Bonzini }
3136d35d4cb5SMichael Roth #endif
3137d35d4cb5SMichael Roth 
31380e4ef702SThomas Huth /* add unsupported commands to the list of blocked RPCs */
31390e4ef702SThomas Huth GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
31401281c08aSTomoki Sekiyama {
31411281c08aSTomoki Sekiyama #if !defined(__linux__)
31421281c08aSTomoki Sekiyama     {
31431281c08aSTomoki Sekiyama         const char *list[] = {
31441281c08aSTomoki Sekiyama             "guest-suspend-disk", "guest-suspend-ram",
314559e35c7bSAndrew Deason             "guest-suspend-hybrid", "guest-get-vcpus", "guest-set-vcpus",
31460dd38a03Szhanghailiang             "guest-get-memory-blocks", "guest-set-memory-blocks",
314728d8dd35SBasil Salman             "guest-get-memory-block-size", "guest-get-memory-block-info",
314828d8dd35SBasil Salman             NULL};
31491281c08aSTomoki Sekiyama         char **p = (char **)list;
31501281c08aSTomoki Sekiyama 
31511281c08aSTomoki Sekiyama         while (*p) {
31520e4ef702SThomas Huth             blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
31531281c08aSTomoki Sekiyama         }
31541281c08aSTomoki Sekiyama     }
31551281c08aSTomoki Sekiyama #endif
31561281c08aSTomoki Sekiyama 
315759e35c7bSAndrew Deason #if !defined(HAVE_GETIFADDRS)
31580e4ef702SThomas Huth     blockedrpcs = g_list_append(blockedrpcs,
315959e35c7bSAndrew Deason                               g_strdup("guest-network-get-interfaces"));
316059e35c7bSAndrew Deason #endif
316159e35c7bSAndrew Deason 
31621281c08aSTomoki Sekiyama #if !defined(CONFIG_FSFREEZE)
31631281c08aSTomoki Sekiyama     {
31641281c08aSTomoki Sekiyama         const char *list[] = {
31651281c08aSTomoki Sekiyama             "guest-get-fsinfo", "guest-fsfreeze-status",
31661281c08aSTomoki Sekiyama             "guest-fsfreeze-freeze", "guest-fsfreeze-freeze-list",
3167fed39564STomáš Golembiovský             "guest-fsfreeze-thaw", "guest-get-fsinfo",
3168fed39564STomáš Golembiovský             "guest-get-disks", NULL};
31691281c08aSTomoki Sekiyama         char **p = (char **)list;
31701281c08aSTomoki Sekiyama 
31711281c08aSTomoki Sekiyama         while (*p) {
31720e4ef702SThomas Huth             blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
31731281c08aSTomoki Sekiyama         }
31741281c08aSTomoki Sekiyama     }
31751281c08aSTomoki Sekiyama #endif
31761281c08aSTomoki Sekiyama 
31771281c08aSTomoki Sekiyama #if !defined(CONFIG_FSTRIM)
31780e4ef702SThomas Huth     blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-fstrim"));
31791281c08aSTomoki Sekiyama #endif
31801281c08aSTomoki Sekiyama 
31810e4ef702SThomas Huth     blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-get-devices"));
31822e4211ceSTomáš Golembiovský 
31830e4ef702SThomas Huth     return blockedrpcs;
31841281c08aSTomoki Sekiyama }
31851281c08aSTomoki Sekiyama 
3186c216e5adSMichael Roth /* register init/cleanup routines for stateful command groups */
3187c216e5adSMichael Roth void ga_command_state_init(GAState *s, GACommandState *cs)
3188c216e5adSMichael Roth {
3189c216e5adSMichael Roth #if defined(CONFIG_FSFREEZE)
3190f22d85e9SMichael Roth     ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
3191c216e5adSMichael Roth #endif
3192c216e5adSMichael Roth }
3193161a56a9SVinzenz Feenstra 
3194e674605fSTomáš Golembiovský #ifdef HAVE_UTMPX
3195e674605fSTomáš Golembiovský 
3196161a56a9SVinzenz Feenstra #define QGA_MICRO_SECOND_TO_SECOND 1000000
3197161a56a9SVinzenz Feenstra 
3198161a56a9SVinzenz Feenstra static double ga_get_login_time(struct utmpx *user_info)
3199161a56a9SVinzenz Feenstra {
3200161a56a9SVinzenz Feenstra     double seconds = (double)user_info->ut_tv.tv_sec;
3201161a56a9SVinzenz Feenstra     double useconds = (double)user_info->ut_tv.tv_usec;
3202161a56a9SVinzenz Feenstra     useconds /= QGA_MICRO_SECOND_TO_SECOND;
3203161a56a9SVinzenz Feenstra     return seconds + useconds;
3204161a56a9SVinzenz Feenstra }
3205161a56a9SVinzenz Feenstra 
3206b90abbacSVladimir Sementsov-Ogievskiy GuestUserList *qmp_guest_get_users(Error **errp)
3207161a56a9SVinzenz Feenstra {
3208161a56a9SVinzenz Feenstra     GHashTable *cache = NULL;
320995b3a8c8SEric Blake     GuestUserList *head = NULL, **tail = &head;
3210161a56a9SVinzenz Feenstra     struct utmpx *user_info = NULL;
3211161a56a9SVinzenz Feenstra     gpointer value = NULL;
3212161a56a9SVinzenz Feenstra     GuestUser *user = NULL;
3213161a56a9SVinzenz Feenstra     double login_time = 0;
3214161a56a9SVinzenz Feenstra 
3215161a56a9SVinzenz Feenstra     cache = g_hash_table_new(g_str_hash, g_str_equal);
3216161a56a9SVinzenz Feenstra     setutxent();
3217161a56a9SVinzenz Feenstra 
3218161a56a9SVinzenz Feenstra     for (;;) {
3219161a56a9SVinzenz Feenstra         user_info = getutxent();
3220161a56a9SVinzenz Feenstra         if (user_info == NULL) {
3221161a56a9SVinzenz Feenstra             break;
3222161a56a9SVinzenz Feenstra         } else if (user_info->ut_type != USER_PROCESS) {
3223161a56a9SVinzenz Feenstra             continue;
3224161a56a9SVinzenz Feenstra         } else if (g_hash_table_contains(cache, user_info->ut_user)) {
3225161a56a9SVinzenz Feenstra             value = g_hash_table_lookup(cache, user_info->ut_user);
3226161a56a9SVinzenz Feenstra             user = (GuestUser *)value;
3227161a56a9SVinzenz Feenstra             login_time = ga_get_login_time(user_info);
3228161a56a9SVinzenz Feenstra             /* We're ensuring the earliest login time to be sent */
3229161a56a9SVinzenz Feenstra             if (login_time < user->login_time) {
3230161a56a9SVinzenz Feenstra                 user->login_time = login_time;
3231161a56a9SVinzenz Feenstra             }
3232161a56a9SVinzenz Feenstra             continue;
3233161a56a9SVinzenz Feenstra         }
3234161a56a9SVinzenz Feenstra 
323595b3a8c8SEric Blake         user = g_new0(GuestUser, 1);
323695b3a8c8SEric Blake         user->user = g_strdup(user_info->ut_user);
323795b3a8c8SEric Blake         user->login_time = ga_get_login_time(user_info);
3238161a56a9SVinzenz Feenstra 
323995b3a8c8SEric Blake         g_hash_table_insert(cache, user->user, user);
3240161a56a9SVinzenz Feenstra 
324195b3a8c8SEric Blake         QAPI_LIST_APPEND(tail, user);
3242161a56a9SVinzenz Feenstra     }
3243161a56a9SVinzenz Feenstra     endutxent();
3244161a56a9SVinzenz Feenstra     g_hash_table_destroy(cache);
3245161a56a9SVinzenz Feenstra     return head;
3246161a56a9SVinzenz Feenstra }
3247e674605fSTomáš Golembiovský 
3248e674605fSTomáš Golembiovský #else
3249e674605fSTomáš Golembiovský 
3250e674605fSTomáš Golembiovský GuestUserList *qmp_guest_get_users(Error **errp)
3251e674605fSTomáš Golembiovský {
3252e674605fSTomáš Golembiovský     error_setg(errp, QERR_UNSUPPORTED);
3253e674605fSTomáš Golembiovský     return NULL;
3254e674605fSTomáš Golembiovský }
3255e674605fSTomáš Golembiovský 
3256e674605fSTomáš Golembiovský #endif
32579848f797STomáš Golembiovský 
32589848f797STomáš Golembiovský /* Replace escaped special characters with theire real values. The replacement
32599848f797STomáš Golembiovský  * is done in place -- returned value is in the original string.
32609848f797STomáš Golembiovský  */
32619848f797STomáš Golembiovský static void ga_osrelease_replace_special(gchar *value)
32629848f797STomáš Golembiovský {
32639848f797STomáš Golembiovský     gchar *p, *p2, quote;
32649848f797STomáš Golembiovský 
32659848f797STomáš Golembiovský     /* Trim the string at first space or semicolon if it is not enclosed in
32669848f797STomáš Golembiovský      * single or double quotes. */
32679848f797STomáš Golembiovský     if ((value[0] != '"') || (value[0] == '\'')) {
32689848f797STomáš Golembiovský         p = strchr(value, ' ');
32699848f797STomáš Golembiovský         if (p != NULL) {
32709848f797STomáš Golembiovský             *p = 0;
32719848f797STomáš Golembiovský         }
32729848f797STomáš Golembiovský         p = strchr(value, ';');
32739848f797STomáš Golembiovský         if (p != NULL) {
32749848f797STomáš Golembiovský             *p = 0;
32759848f797STomáš Golembiovský         }
32769848f797STomáš Golembiovský         return;
32779848f797STomáš Golembiovský     }
32789848f797STomáš Golembiovský 
32799848f797STomáš Golembiovský     quote = value[0];
32809848f797STomáš Golembiovský     p2 = value;
32819848f797STomáš Golembiovský     p = value + 1;
32829848f797STomáš Golembiovský     while (*p != 0) {
32839848f797STomáš Golembiovský         if (*p == '\\') {
32849848f797STomáš Golembiovský             p++;
32859848f797STomáš Golembiovský             switch (*p) {
32869848f797STomáš Golembiovský             case '$':
32879848f797STomáš Golembiovský             case '\'':
32889848f797STomáš Golembiovský             case '"':
32899848f797STomáš Golembiovský             case '\\':
32909848f797STomáš Golembiovský             case '`':
32919848f797STomáš Golembiovský                 break;
32929848f797STomáš Golembiovský             default:
32939848f797STomáš Golembiovský                 /* Keep literal backslash followed by whatever is there */
32949848f797STomáš Golembiovský                 p--;
32959848f797STomáš Golembiovský                 break;
32969848f797STomáš Golembiovský             }
32979848f797STomáš Golembiovský         } else if (*p == quote) {
32989848f797STomáš Golembiovský             *p2 = 0;
32999848f797STomáš Golembiovský             break;
33009848f797STomáš Golembiovský         }
33019848f797STomáš Golembiovský         *(p2++) = *(p++);
33029848f797STomáš Golembiovský     }
33039848f797STomáš Golembiovský }
33049848f797STomáš Golembiovský 
33059848f797STomáš Golembiovský static GKeyFile *ga_parse_osrelease(const char *fname)
33069848f797STomáš Golembiovský {
33079848f797STomáš Golembiovský     gchar *content = NULL;
33089848f797STomáš Golembiovský     gchar *content2 = NULL;
33099848f797STomáš Golembiovský     GError *err = NULL;
33109848f797STomáš Golembiovský     GKeyFile *keys = g_key_file_new();
33119848f797STomáš Golembiovský     const char *group = "[os-release]\n";
33129848f797STomáš Golembiovský 
33139848f797STomáš Golembiovský     if (!g_file_get_contents(fname, &content, NULL, &err)) {
33149848f797STomáš Golembiovský         slog("failed to read '%s', error: %s", fname, err->message);
33159848f797STomáš Golembiovský         goto fail;
33169848f797STomáš Golembiovský     }
33179848f797STomáš Golembiovský 
33189848f797STomáš Golembiovský     if (!g_utf8_validate(content, -1, NULL)) {
33199848f797STomáš Golembiovský         slog("file is not utf-8 encoded: %s", fname);
33209848f797STomáš Golembiovský         goto fail;
33219848f797STomáš Golembiovský     }
33229848f797STomáš Golembiovský     content2 = g_strdup_printf("%s%s", group, content);
33239848f797STomáš Golembiovský 
33249848f797STomáš Golembiovský     if (!g_key_file_load_from_data(keys, content2, -1, G_KEY_FILE_NONE,
33259848f797STomáš Golembiovský                                    &err)) {
33269848f797STomáš Golembiovský         slog("failed to parse file '%s', error: %s", fname, err->message);
33279848f797STomáš Golembiovský         goto fail;
33289848f797STomáš Golembiovský     }
33299848f797STomáš Golembiovský 
33309848f797STomáš Golembiovský     g_free(content);
33319848f797STomáš Golembiovský     g_free(content2);
33329848f797STomáš Golembiovský     return keys;
33339848f797STomáš Golembiovský 
33349848f797STomáš Golembiovský fail:
33359848f797STomáš Golembiovský     g_error_free(err);
33369848f797STomáš Golembiovský     g_free(content);
33379848f797STomáš Golembiovský     g_free(content2);
33389848f797STomáš Golembiovský     g_key_file_free(keys);
33399848f797STomáš Golembiovský     return NULL;
33409848f797STomáš Golembiovský }
33419848f797STomáš Golembiovský 
33429848f797STomáš Golembiovský GuestOSInfo *qmp_guest_get_osinfo(Error **errp)
33439848f797STomáš Golembiovský {
33449848f797STomáš Golembiovský     GuestOSInfo *info = NULL;
33459848f797STomáš Golembiovský     struct utsname kinfo;
3346339ca68bSTomáš Golembiovský     GKeyFile *osrelease = NULL;
3347339ca68bSTomáš Golembiovský     const char *qga_os_release = g_getenv("QGA_OS_RELEASE");
33489848f797STomáš Golembiovský 
33499848f797STomáš Golembiovský     info = g_new0(GuestOSInfo, 1);
33509848f797STomáš Golembiovský 
33519848f797STomáš Golembiovský     if (uname(&kinfo) != 0) {
33529848f797STomáš Golembiovský         error_setg_errno(errp, errno, "uname failed");
33539848f797STomáš Golembiovský     } else {
33549848f797STomáš Golembiovský         info->has_kernel_version = true;
33559848f797STomáš Golembiovský         info->kernel_version = g_strdup(kinfo.version);
33569848f797STomáš Golembiovský         info->has_kernel_release = true;
33579848f797STomáš Golembiovský         info->kernel_release = g_strdup(kinfo.release);
33589848f797STomáš Golembiovský         info->has_machine = true;
33599848f797STomáš Golembiovský         info->machine = g_strdup(kinfo.machine);
33609848f797STomáš Golembiovský     }
33619848f797STomáš Golembiovský 
3362339ca68bSTomáš Golembiovský     if (qga_os_release != NULL) {
3363339ca68bSTomáš Golembiovský         osrelease = ga_parse_osrelease(qga_os_release);
3364339ca68bSTomáš Golembiovský     } else {
33659848f797STomáš Golembiovský         osrelease = ga_parse_osrelease("/etc/os-release");
33669848f797STomáš Golembiovský         if (osrelease == NULL) {
33679848f797STomáš Golembiovský             osrelease = ga_parse_osrelease("/usr/lib/os-release");
33689848f797STomáš Golembiovský         }
3369339ca68bSTomáš Golembiovský     }
33709848f797STomáš Golembiovský 
33719848f797STomáš Golembiovský     if (osrelease != NULL) {
33729848f797STomáš Golembiovský         char *value;
33739848f797STomáš Golembiovský 
33749848f797STomáš Golembiovský #define GET_FIELD(field, osfield) do { \
33759848f797STomáš Golembiovský     value = g_key_file_get_value(osrelease, "os-release", osfield, NULL); \
33769848f797STomáš Golembiovský     if (value != NULL) { \
33779848f797STomáš Golembiovský         ga_osrelease_replace_special(value); \
33789848f797STomáš Golembiovský         info->has_ ## field = true; \
33799848f797STomáš Golembiovský         info->field = value; \
33809848f797STomáš Golembiovský     } \
33819848f797STomáš Golembiovský } while (0)
33829848f797STomáš Golembiovský         GET_FIELD(id, "ID");
33839848f797STomáš Golembiovský         GET_FIELD(name, "NAME");
33849848f797STomáš Golembiovský         GET_FIELD(pretty_name, "PRETTY_NAME");
33859848f797STomáš Golembiovský         GET_FIELD(version, "VERSION");
33869848f797STomáš Golembiovský         GET_FIELD(version_id, "VERSION_ID");
33879848f797STomáš Golembiovský         GET_FIELD(variant, "VARIANT");
33889848f797STomáš Golembiovský         GET_FIELD(variant_id, "VARIANT_ID");
33899848f797STomáš Golembiovský #undef GET_FIELD
33909848f797STomáš Golembiovský 
33919848f797STomáš Golembiovský         g_key_file_free(osrelease);
33929848f797STomáš Golembiovský     }
33939848f797STomáš Golembiovský 
33949848f797STomáš Golembiovský     return info;
33959848f797STomáš Golembiovský }
33962e4211ceSTomáš Golembiovský 
33972e4211ceSTomáš Golembiovský GuestDeviceInfoList *qmp_guest_get_devices(Error **errp)
33982e4211ceSTomáš Golembiovský {
33992e4211ceSTomáš Golembiovský     error_setg(errp, QERR_UNSUPPORTED);
34002e4211ceSTomáš Golembiovský 
34012e4211ceSTomáš Golembiovský     return NULL;
34022e4211ceSTomáš Golembiovský }
3403548fb0daSMarc-André Lureau 
3404548fb0daSMarc-André Lureau #ifndef HOST_NAME_MAX
3405548fb0daSMarc-André Lureau # ifdef _POSIX_HOST_NAME_MAX
3406548fb0daSMarc-André Lureau #  define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
3407548fb0daSMarc-André Lureau # else
3408548fb0daSMarc-André Lureau #  define HOST_NAME_MAX 255
3409548fb0daSMarc-André Lureau # endif
3410548fb0daSMarc-André Lureau #endif
3411548fb0daSMarc-André Lureau 
3412548fb0daSMarc-André Lureau char *qga_get_host_name(Error **errp)
3413548fb0daSMarc-André Lureau {
3414548fb0daSMarc-André Lureau     long len = -1;
3415548fb0daSMarc-André Lureau     g_autofree char *hostname = NULL;
3416548fb0daSMarc-André Lureau 
3417548fb0daSMarc-André Lureau #ifdef _SC_HOST_NAME_MAX
3418548fb0daSMarc-André Lureau     len = sysconf(_SC_HOST_NAME_MAX);
3419548fb0daSMarc-André Lureau #endif /* _SC_HOST_NAME_MAX */
3420548fb0daSMarc-André Lureau 
3421548fb0daSMarc-André Lureau     if (len < 0) {
3422548fb0daSMarc-André Lureau         len = HOST_NAME_MAX;
3423548fb0daSMarc-André Lureau     }
3424548fb0daSMarc-André Lureau 
3425548fb0daSMarc-André Lureau     /* Unfortunately, gethostname() below does not guarantee a
3426548fb0daSMarc-André Lureau      * NULL terminated string. Therefore, allocate one byte more
3427548fb0daSMarc-André Lureau      * to be sure. */
3428548fb0daSMarc-André Lureau     hostname = g_new0(char, len + 1);
3429548fb0daSMarc-André Lureau 
3430548fb0daSMarc-André Lureau     if (gethostname(hostname, len) < 0) {
3431548fb0daSMarc-André Lureau         error_setg_errno(errp, errno,
3432548fb0daSMarc-André Lureau                          "cannot get hostname");
3433548fb0daSMarc-André Lureau         return NULL;
3434548fb0daSMarc-André Lureau     }
3435548fb0daSMarc-André Lureau 
3436548fb0daSMarc-André Lureau     return g_steal_pointer(&hostname);
3437548fb0daSMarc-André Lureau }
3438