Reboot requires more sophistication and is left as a future work item -- but at least part of the plumbing is in place. --- Looking for feedback. I'm pretty unfamiliar with libvirt; maybe this isn't quite the right way to do this. Sorry. If you want me to rework it in some way, just let me know. I tried to model this off of DrvQEMU, which I knew to support this behavior. Reboot should probably be processed on a threadpool thread outside of the event loop, so I omitted it for now. P.S., the 'read-non-seekable' check test seems to just hang forever on FreeBSD. I haven't dug into it, but POSIX FIFO behavior is pretty weird. --- src/Makefile.am | 2 + src/bhyve/bhyve_monitor.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++ src/bhyve/bhyve_monitor.h | 36 +++++++++ src/bhyve/bhyve_process.c | 14 +++- 4 files changed, 233 insertions(+), 3 deletions(-) create mode 100644 src/bhyve/bhyve_monitor.c create mode 100644 src/bhyve/bhyve_monitor.h diff --git a/src/Makefile.am b/src/Makefile.am index d8fe624..b6c1701 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -833,6 +833,8 @@ BHYVE_DRIVER_SOURCES = \ bhyve/bhyve_domain.h \ bhyve/bhyve_driver.h \ bhyve/bhyve_driver.c \ + bhyve/bhyve_monitor.c \ + bhyve/bhyve_monitor.h \ bhyve/bhyve_process.c \ bhyve/bhyve_process.h \ bhyve/bhyve_utils.h \ diff --git a/src/bhyve/bhyve_monitor.c b/src/bhyve/bhyve_monitor.c new file mode 100644 index 0000000..cd3cf6e --- /dev/null +++ b/src/bhyve/bhyve_monitor.c @@ -0,0 +1,184 @@ +/* + * bhyve_monitor.c: Tear-down or reboot bhyve domains on guest shutdown + * + * Copyright (C) 2014 Conrad Meyer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Conrad Meyer <cse.cem@xxxxxxxxx> + */ + +#include <config.h> + +#include <sys/types.h> +#include <sys/event.h> +#include <sys/time.h> +#include <sys/wait.h> + +#include "bhyve_monitor.h" +#include "bhyve_process.h" +#include "viralloc.h" +#include "virerror.h" +#include "virfile.h" +#include "virlog.h" + +#define VIR_FROM_THIS VIR_FROM_BHYVE + +VIR_LOG_INIT("bhyve.bhyve_monitor"); + +struct _bhyveMonitor { + int kq; + int watch; + virDomainObjPtr vm; + bhyveConnPtr driver; +}; + +static void +bhyveMonitorIO(int watch, int kq, int events ATTRIBUTE_UNUSED, void *opaque) +{ + const struct timespec zerowait = {}; + bhyveMonitorPtr mon = opaque; + struct kevent kev; + int rc, status; + + if (watch != mon->watch || kq != mon->kq) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("event from unexpected fd %d!=%d / watch %d!=%d"), + mon->kq, kq, mon->watch, watch); + return; + } + + rc = kevent(kq, NULL, 0, &kev, 1, &zerowait); + if (rc < 0) { + virReportSystemError(errno, "%s", _("Unable to query kqueue")); + return; + } + + if (rc == 0) + return; + + if ((kev.flags & EV_ERROR) != 0) { + virReportSystemError(kev.data, "%s", _("Unable to query kqueue")); + return; + } + + if (kev.filter == EVFILT_PROC && (kev.fflags & NOTE_EXIT) != 0) { + if ((pid_t)kev.ident != mon->vm->pid) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("event from unexpected proc %ju!=%ju"), + (uintmax_t)mon->vm->pid, (uintmax_t)kev.ident); + return; + } + + status = kev.data; + if (WIFSIGNALED(status) && WCOREDUMP(status)) { + VIR_ERROR("Guest %s got signal %d and crashed", mon->vm->def->name, + WTERMSIG(status)); + virBhyveProcessStop(mon->driver, mon->vm, + VIR_DOMAIN_SHUTOFF_CRASHED); + } else if (WIFEXITED(status)) { + if (WEXITSTATUS(status) == 0) { + /* 0 - reboot */ + /* TODO: Implementing reboot is a little more complicated. */ + VIR_INFO("Guest %s rebooted; destroying domain.", + mon->vm->def->name); + virBhyveProcessStop(mon->driver, mon->vm, + VIR_DOMAIN_SHUTOFF_SHUTDOWN); + } else if (WEXITSTATUS(status) < 3) { + /* 1 - shutdown, 2 - halt, 3 - triple fault. others - error */ + VIR_INFO("Guest %s shut itself down; destroying domain.", + mon->vm->def->name); + virBhyveProcessStop(mon->driver, mon->vm, + VIR_DOMAIN_SHUTOFF_SHUTDOWN); + } else { + VIR_INFO("Guest %s had an error and exited with status %d; destroying domain.", + mon->vm->def->name, WEXITSTATUS(status)); + virBhyveProcessStop(mon->driver, mon->vm, + VIR_DOMAIN_SHUTOFF_UNKNOWN); + } + } + } +} + +static void +bhyveMonitorRelease(void *opaque) +{ + bhyveMonitorPtr mon = opaque; + + VIR_FORCE_CLOSE(mon->kq); + virObjectUnref(mon->vm); + VIR_FREE(mon); +} + +bhyveMonitorPtr +bhyveMonitorOpen(virDomainObjPtr vm, bhyveConnPtr driver) +{ + bhyveMonitorPtr mon; + struct kevent kev; + int rc; + + if (VIR_ALLOC(mon) < 0) + return NULL; + + mon->vm = virObjectRef(vm); + mon->driver = driver; + + mon->kq = kqueue(); + if (mon->kq < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, "%s", + _("Unable to create kqueue")); + goto cleanup; + } + + EV_SET(&kev, vm->pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, mon); + rc = kevent(mon->kq, &kev, 1, NULL, 0, NULL); + if (rc < 0) { + virReportError(VIR_ERR_SYSTEM_ERROR, "%s", + _("Unable to register process kevent")); + goto cleanup; + } + + mon->watch = virEventAddHandle(mon->kq, + VIR_EVENT_HANDLE_READABLE | + VIR_EVENT_HANDLE_ERROR | + VIR_EVENT_HANDLE_HANGUP, + bhyveMonitorIO, + mon, + bhyveMonitorRelease); + if (mon->watch < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unable to register monitor events")); + goto cleanup; + } + + return mon; + + cleanup: + bhyveMonitorRelease(mon); + return NULL; +} + +void +bhyveMonitorClose(bhyveMonitorPtr mon) +{ + + if (mon == NULL) + return; + + if (mon->watch > 0) + virEventRemoveHandle(mon->watch); + else + bhyveMonitorRelease(mon); +} diff --git a/src/bhyve/bhyve_monitor.h b/src/bhyve/bhyve_monitor.h new file mode 100644 index 0000000..226d878 --- /dev/null +++ b/src/bhyve/bhyve_monitor.h @@ -0,0 +1,36 @@ +/* + * bhyve_monitor.h: Tear-down or reboot bhyve domains on guest shutdown + * + * Copyright (C) 2014 Conrad Meyer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Conrad Meyer <cse.cem@xxxxxxxxx> + */ + +#ifndef BHYVE_MONITOR_H +# define BHYVE_MONITOR_H + +# include "internal.h" +# include "domain_conf.h" +# include "bhyve_utils.h" + +typedef struct _bhyveMonitor bhyveMonitor; +typedef bhyveMonitor *bhyveMonitorPtr; + +bhyveMonitorPtr bhyveMonitorOpen(virDomainObjPtr vm, bhyveConnPtr driver); +void bhyveMonitorClose(bhyveMonitorPtr mon); + +#endif /* BHYVE_MONITOR_H */ diff --git a/src/bhyve/bhyve_process.c b/src/bhyve/bhyve_process.c index a30e36a..284641a 100644 --- a/src/bhyve/bhyve_process.c +++ b/src/bhyve/bhyve_process.c @@ -32,8 +32,9 @@ #include <net/if_tap.h> #include "bhyve_device.h" -#include "bhyve_process.h" #include "bhyve_command.h" +#include "bhyve_monitor.h" +#include "bhyve_process.h" #include "datatypes.h" #include "virerror.h" #include "virlog.h" @@ -209,6 +210,7 @@ virBhyveProcessStart(virConnectPtr conn, vm->def->id = vm->pid; virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, reason); + vm->privateData = bhyveMonitorOpen(vm, driver); if (virDomainSaveStatus(driver->xmlopt, BHYVE_STATE_DIR, @@ -268,6 +270,9 @@ virBhyveProcessStop(bhyveConnPtr driver, return -1; } + if (vm->privateData != NULL) + bhyveMonitorClose((bhyveMonitorPtr)vm->privateData); + /* First, try to kill 'bhyve' process */ if (virProcessKillPainfully(vm->pid, true) != 0) VIR_WARN("Failed to gracefully stop bhyve VM '%s' (pid: %d)", @@ -371,9 +376,12 @@ virBhyveProcessReconnect(virDomainObjPtr vm, goto cleanup; proc_argv = kvm_getargv(data->kd, kp, 0); - if (proc_argv && proc_argv[0]) - if (STREQ(expected_proctitle, proc_argv[0])) + if (proc_argv && proc_argv[0]) { + if (STREQ(expected_proctitle, proc_argv[0])) { ret = 0; + vm->privateData = bhyveMonitorOpen(vm, data->driver); + } + } cleanup: if (ret < 0) { -- 1.9.3 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list