]>
git.proxmox.com Git - qemu-server.git/blob - qmeventd/qmeventd.c
3 Copyright (C) 2018 Proxmox Server Solutions GmbH
5 Copyright: qmeventd is under GNU GPL, the GNU General Public License.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; version 2 dated June, 1991.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 Author: Dominik Csapak <d.csapak@proxmox.com>
23 qmeventd listens on a given socket, and waits for qemu processes
26 it then waits for shutdown events followed by the closing of the socket,
27 it then calls /usr/sbin/qm cleanup with following arguments
29 /usr/sbin/qm cleanup VMID <graceful> <guest>
31 parameter explanation:
34 1|0 depending if it saw a shutdown event before the socket closed
37 1|0 depending if the shutdown was requested from the guest
52 #include <sys/epoll.h>
53 #include <sys/socket.h>
54 #include <sys/types.h>
61 static int verbose
= 0;
62 static int epoll_fd
= 0;
63 static const char *progname
;
71 fprintf(stderr
, "Usage: %s [-f] [-v] PATH\n", progname
);
72 fprintf(stderr
, " -f run in foreground (default: false)\n");
73 fprintf(stderr
, " -v verbose (default: false)\n");
74 fprintf(stderr
, " PATH use PATH for socket\n");
78 get_pid_from_fd(int fd
)
80 struct ucred credentials
= { .pid
= 0, .uid
= 0, .gid
= 0 };
81 socklen_t len
= sizeof(struct ucred
);
82 log_neg(getsockopt(fd
, SOL_SOCKET
, SO_PEERCRED
, &credentials
, &len
), "getsockopt");
83 return credentials
.pid
;
87 * reads the vmid from /proc/<pid>/cmdline
88 * after the '-id' argument
91 get_vmid_from_pid(pid_t pid
)
93 char filename
[32] = { 0 };
94 int len
= snprintf(filename
, sizeof(filename
), "/proc/%d/cmdline", pid
);
96 fprintf(stderr
, "error during snprintf for %d: %s\n", pid
,
100 if ((size_t)len
>= sizeof(filename
)) {
101 fprintf(stderr
, "error: pid %d too long\n", pid
);
104 FILE *fp
= fopen(filename
, "re");
106 fprintf(stderr
, "error opening %s: %s\n", filename
, strerror(errno
));
110 unsigned long vmid
= 0;
114 while ((rc
= getdelim(&buf
, &buflen
, '\0', fp
)) >= 0) {
115 if (!strcmp(buf
, "-id")) {
124 if (getdelim(&buf
, &buflen
, '\0', fp
) >= 0) {
125 if (buf
[0] == '-' || buf
[0] == '\0') {
126 fprintf(stderr
, "invalid vmid %s\n", buf
);
132 vmid
= strtoul(buf
, &endptr
, 10);
136 } else if (*endptr
!= '\0') {
137 fprintf(stderr
, "invalid vmid %s\n", buf
);
145 fprintf(stderr
, "error parsing vmid for %d: %s\n", pid
, strerror(errno
));
154 must_write(int fd
, const char *buf
, size_t len
)
158 wlen
= write(fd
, buf
, len
);
159 } while (wlen
< 0 && errno
== EINTR
);
161 return (wlen
== (ssize_t
)len
);
165 * qmp handling functions
169 handle_qmp_handshake(struct Client
*client
)
171 VERBOSE_PRINT("%s: got QMP handshake\n", client
->vmid
);
172 static const char qmp_answer
[] = "{\"execute\":\"qmp_capabilities\"}\n";
173 if (!must_write(client
->fd
, qmp_answer
, sizeof(qmp_answer
) - 1)) {
174 fprintf(stderr
, "%s: cannot complete handshake\n", client
->vmid
);
175 cleanup_client(client
);
180 handle_qmp_event(struct Client
*client
, struct json_object
*obj
)
182 struct json_object
*event
;
183 if (!json_object_object_get_ex(obj
, "event", &event
)) {
186 VERBOSE_PRINT("%s: got QMP event: %s\n", client
->vmid
,
187 json_object_get_string(event
));
188 // event, check if shutdown and get guest parameter
189 if (!strcmp(json_object_get_string(event
), "SHUTDOWN")) {
190 client
->graceful
= 1;
191 struct json_object
*data
;
192 struct json_object
*guest
;
193 if (json_object_object_get_ex(obj
, "data", &data
) &&
194 json_object_object_get_ex(data
, "guest", &guest
))
196 client
->guest
= (unsigned short)json_object_get_boolean(guest
);
202 * client management functions
206 add_new_client(int client_fd
)
208 struct Client
*client
= calloc(sizeof(struct Client
), 1);
209 client
->fd
= client_fd
;
210 client
->pid
= get_pid_from_fd(client_fd
);
211 if (client
->pid
== 0) {
212 fprintf(stderr
, "could not get pid from client\n");
215 unsigned long vmid
= get_vmid_from_pid(client
->pid
);
216 int res
= snprintf(client
->vmid
, sizeof(client
->vmid
), "%lu", vmid
);
217 if (vmid
== 0 || res
< 0 || res
>= (int)sizeof(client
->vmid
)) {
218 fprintf(stderr
, "could not get vmid from pid %d\n", client
->pid
);
222 struct epoll_event ev
;
224 ev
.data
.ptr
= client
;
225 res
= epoll_ctl(epoll_fd
, EPOLL_CTL_ADD
, client_fd
, &ev
);
227 perror("epoll_ctl client add");
231 VERBOSE_PRINT("added new client, pid: %d, vmid: %s\n", client
->pid
,
236 (void)close(client_fd
);
241 cleanup_client(struct Client
*client
)
243 VERBOSE_PRINT("%s: client exited, status: graceful: %d, guest: %d\n",
244 client
->vmid
, client
->graceful
, client
->guest
);
245 log_neg(epoll_ctl(epoll_fd
, EPOLL_CTL_DEL
, client
->fd
, NULL
), "epoll del");
246 (void)close(client
->fd
);
248 unsigned short graceful
= client
->graceful
;
249 unsigned short guest
= client
->guest
;
250 char vmid
[sizeof(client
->vmid
)];
251 strncpy(vmid
, client
->vmid
, sizeof(vmid
));
253 VERBOSE_PRINT("%s: executing cleanup\n", vmid
);
257 fprintf(stderr
, "fork failed: %s\n", strerror(errno
));
261 char *script
= "/usr/sbin/qm";
267 graceful
? "1" : "0",
272 execvp(script
, args
);
279 handle_client(struct Client
*client
)
281 VERBOSE_PRINT("%s: entering handle\n", client
->vmid
);
284 len
= read(client
->fd
, (client
->buf
+client
->buflen
),
285 sizeof(client
->buf
) - client
->buflen
);
286 } while (len
< 0 && errno
== EINTR
);
289 if (!(errno
== EAGAIN
|| errno
== EWOULDBLOCK
)) {
290 log_neg((int)len
, "read");
291 cleanup_client(client
);
294 } else if (len
== 0) {
295 VERBOSE_PRINT("%s: got EOF\n", client
->vmid
);
296 cleanup_client(client
);
300 VERBOSE_PRINT("%s: read %ld bytes\n", client
->vmid
, len
);
301 client
->buflen
+= len
;
303 struct json_tokener
*tok
= json_tokener_new();
304 struct json_object
*jobj
= NULL
;
305 enum json_tokener_error jerr
= json_tokener_success
;
306 while (jerr
== json_tokener_success
&& client
->buflen
!= 0) {
307 jobj
= json_tokener_parse_ex(tok
, client
->buf
, (int)client
->buflen
);
308 jerr
= json_tokener_get_error(tok
);
309 unsigned int offset
= (unsigned int)tok
->char_offset
;
311 case json_tokener_success
:
312 // move rest from buffer to front
313 memmove(client
->buf
, client
->buf
+ offset
, client
->buflen
- offset
);
314 client
->buflen
-= offset
;
315 if (json_object_is_type(jobj
, json_type_object
)) {
316 struct json_object
*obj
;
317 if (json_object_object_get_ex(jobj
, "QMP", &obj
)) {
318 handle_qmp_handshake(client
);
319 } else if (json_object_object_get_ex(jobj
, "event", &obj
)) {
320 handle_qmp_event(client
, jobj
);
321 } // else ignore message
324 case json_tokener_continue
:
325 if (client
->buflen
>= sizeof(client
->buf
)) {
326 VERBOSE_PRINT("%s, msg too large, discarding buffer\n",
328 memset(client
->buf
, 0, sizeof(client
->buf
));
330 } // else we have enough space try again after next read
333 VERBOSE_PRINT("%s: parse error: %d, discarding buffer\n",
335 memset(client
->buf
, 0, client
->buflen
);
339 json_object_put(jobj
);
341 json_tokener_free(tok
);
346 main(int argc
, char *argv
[])
350 char *socket_path
= NULL
;
353 while ((opt
= getopt(argc
, argv
, "hfv")) != -1) {
371 if (optind
>= argc
) {
376 signal(SIGCHLD
, SIG_IGN
);
378 socket_path
= argv
[optind
];
380 int sock
= socket(AF_UNIX
, SOCK_STREAM
, 0);
381 bail_neg(sock
, "socket");
383 struct sockaddr_un addr
;
384 memset(&addr
, 0, sizeof(addr
));
385 addr
.sun_family
= AF_UNIX
;
386 strncpy(addr
.sun_path
, socket_path
, sizeof(addr
.sun_path
) - 1);
389 bail_neg(bind(sock
, (struct sockaddr
*)&addr
, sizeof(addr
)), "bind");
391 struct epoll_event ev
, events
[1];
392 epoll_fd
= epoll_create1(EPOLL_CLOEXEC
);
393 bail_neg(epoll_fd
, "epoll_create1");
397 bail_neg(epoll_ctl(epoll_fd
, EPOLL_CTL_ADD
, sock
, &ev
), "epoll_ctl");
399 bail_neg(listen(sock
, 10), "listen");
402 bail_neg(daemon(0, 1), "daemon");
408 nevents
= epoll_wait(epoll_fd
, events
, 1, -1);
409 if (nevents
< 0 && errno
== EINTR
) {
410 // signal happened, try again
413 bail_neg(nevents
, "epoll_wait");
415 for (int n
= 0; n
< nevents
; n
++) {
416 if (events
[n
].data
.fd
== sock
) {
418 int conn_sock
= accept4(sock
, NULL
, NULL
,
419 SOCK_NONBLOCK
| SOCK_CLOEXEC
);
420 log_neg(conn_sock
, "accept");
421 if (conn_sock
> -1) {
422 add_new_client(conn_sock
);
425 handle_client((struct Client
*)events
[n
].data
.ptr
);