[pve-devel] [RFC PATCH qemu-server] add qemumonitor.c
Alexandre DERUMIER
aderumier at odiso.com
Tue Oct 9 16:50:13 CEST 2018
>>this adds a program that can listen to qemu qmp events on a given socket
Does it work in parallel with sending qmp command ?
As far I remember, some year ago, it was not possible to have 2 qmp clients at
the same time. (and needed some kind of proxy betweens clients and qemu)
Maybe more complex, but t could be great to be able to catch any events,
and use them in QemuServer.pm for example.
----- Mail original -----
De: "Dominik Csapak" <d.csapak at proxmox.com>
À: "pve-devel" <pve-devel at pve.proxmox.com>
Envoyé: Mardi 9 Octobre 2018 14:49:22
Objet: [pve-devel] [RFC PATCH qemu-server] add qemumonitor.c
this adds a program that can listen to qemu qmp events on a given socket
and if a shutdown event followed by a disconnected socket occurs,
execute the given script with the given and additional arguments
this is useful if we want to cleanup after the qemu process exited,
e.g. tap devices, vgpus, etc.
also we could implement a 'proper' reboot with applying pending changes
and a stop/reboot hoook
for now, this needs a not-yet applied patch[1] to qemu
but this should be trivial to backport
1: https://lists.gnu.org/archive/html/qemu-devel/2018-10/msg01271.html
Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
sending this as rfc, without makefile/manpage/inclusion in the package/use/
build-dependencies/etc.
location and name of it are ofc subject to change :)
i just want a general feedback of the code and the interface
i had imagined starting this tool after a qemu start
with a 'qm cleanup ID' tool to do the general cleanup
the program links against libjansson4, a ~75k library with only libc6 as
dependency, and the program uses about 100k RSS memory,
so i think this is an acceptable overhead
for a vm (with possibly multiple gbs of ram)
qemumonitor.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 166 insertions(+)
create mode 100644 qemumonitor.c
diff --git a/qemumonitor.c b/qemumonitor.c
new file mode 100644
index 0000000..13dcfa2
--- /dev/null
+++ b/qemumonitor.c
@@ -0,0 +1,166 @@
+/*
+
+ Copyright (C) 2018 Proxmox Server Solutions GmbH
+
+ Copyright: qemumonitor is under GNU GPL, the GNU General Public License.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 dated June, 1991.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+
+ Author: Dominik Csapak <d.csapak at proxmox.com>
+
+ qemumonitor connects to a given qmp socket, and waits for a
+ shutdown event followed by the closing of the socket,
+ it then calls the given script with following arguments
+
+ SCRIPT [ARGUMENTS] <graceful> <guest> <was_reset>
+
+ parameter explanation:
+
+ graceful:
+ 1|0 depending if it saw a shutdown event before the socket closed
+
+ guest:
+ 1|0 depending if the shutdown was requested from the guest
+
+ was_reset:
+ 1|0 depending if the shutdown was actually a request
+
+*/
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <jansson.h>
+
+typedef enum { false, true } bool;
+typedef enum { STATE_PRECONNECTING, STATE_CONNECTING, STATE_CONNECTED } state_t;
+
+#define QMP_ANSWER "{ \"execute\":\"qmp_capabilities\" }\n"
+
+void usage(char *name);
+
+void usage(char *name)
+{
+ fprintf(stderr, "Usage: %s SOCKET SCRIPT [ARGUMENTS..]\n", name);
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc < 3) {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ ssize_t len;
+ bool graceful_shutdown = false;
+ bool guest_requested = false;
+ bool was_reset = false;
+
+ struct sockaddr_un serv_addr;
+ int sock;
+ FILE *socketfile;
+
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock == -1) {
+ fprintf(stderr, "cannot create unix socket: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ memset(&serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sun_family = AF_UNIX;
+ memcpy(&(serv_addr.sun_path), argv[1], strlen(argv[1]));
+
+ if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
+ close(sock);
+ fprintf(stderr, "error connecting to %s: %s\n", argv[1], strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ socketfile = fdopen(sock, "r");
+ if (socketfile == NULL) {
+ fclose(socketfile);
+ fprintf(stderr, "error opening %s: %s\n", argv[1], strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ json_t *json;
+ json_error_t err;
+ bool guest;
+ bool reset;
+ const char *event;
+ state_t qmp_state = STATE_PRECONNECTING;
+
+ while (!feof(socketfile)) {
+ json = json_loadf(socketfile, JSON_DISABLE_EOF_CHECK, &err);
+ if (json == NULL) {
+ // ignore parser errors
+ continue;
+ }
+ switch (qmp_state) {
+ case STATE_PRECONNECTING:
+ if (json_unpack(json, "{s: {s:{}, s:[]}}", "QMP", "version",
+ "capabilities") == 0) {
+ do {
+ len = write(sock, QMP_ANSWER, sizeof(QMP_ANSWER) - 1);
+ } while (len < 0 && errno == EINTR);
+ qmp_state = STATE_CONNECTING;
+ }
+ break;
+ case STATE_CONNECTING:
+ if (json_unpack(json, "{s:{}}", "return") == 0) {
+ qmp_state = STATE_CONNECTED;
+ if (daemon(0,0) == -1) {
+ fprintf(stderr, "cannot daemonize: %s\n",
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+ break;
+ case STATE_CONNECTED:
+ if (json_unpack(json, "{s:s, s:{s:b, s:b}}", "event", &event,
+ "data", "guest", &guest, "was_reset", &reset) == 0) {
+ if (strncmp("SHUTDOWN", event, sizeof("SHUTDOWN")) == 0) {
+ guest_requested = guest;
+ was_reset = reset;
+ graceful_shutdown = true;
+ }
+ }
+ break;
+ }
+ json_decref(json);
+ }
+ fclose(socketfile);
+
+ char *script = argv[2];
+ char **args = (char **)malloc((unsigned long)(argc+2)*sizeof(char *));
+ for (int i = 0; i < argc - 2; i++) {
+ args[i] = argv[i+2];
+ }
+
+ for (int i = argc - 2; i < argc + 1; i++) {
+ args[i] = malloc(2*sizeof(char));
+ }
+
+ snprintf(args[argc-2], 2, "%d", graceful_shutdown);
+ snprintf(args[argc-1], 2, "%d", guest_requested);
+ snprintf(args[argc], 2, "%d", was_reset);
+ args[argc+1] = NULL;
+ execvp(script, args);
+ exit(1);
+}
--
2.11.0
_______________________________________________
pve-devel mailing list
pve-devel at pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
More information about the pve-devel
mailing list