2 * bhyve_process.c: bhyve process management
4 * Copyright (C) 2014 Roman Bogorodskiy
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library 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 GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see
18 * <http://www.gnu.org/licenses/>.
26 #include <sys/param.h>
27 #include <sys/types.h>
28 #include <sys/sysctl.h>
30 #include <sys/ioctl.h>
32 #include <net/if_tap.h>
34 #include "bhyve_process.h"
35 #include "bhyve_command.h"
36 #include "datatypes.h"
41 #include "vircommand.h"
42 #include "virstring.h"
43 #include "virpidfile.h"
44 #include "virprocess.h"
45 #include "virnetdev.h"
46 #include "virnetdevbridge.h"
47 #include "virnetdevtap.h"
49 #define VIR_FROM_THIS VIR_FROM_BHYVE
51 VIR_LOG_INIT("bhyve.bhyve_process");
53 static virDomainObjPtr
54 bhyveProcessAutoDestroy(virDomainObjPtr vm,
55 virConnectPtr conn ATTRIBUTE_UNUSED,
58 bhyveConnPtr driver = opaque;
60 virBhyveProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_DESTROYED);
62 if (!vm->persistent) {
63 virDomainObjListRemove(driver->domains, vm);
71 bhyveNetCleanup(virDomainObjPtr vm)
75 for (i = 0; i < vm->def->nnets; i++) {
76 virDomainNetDefPtr net = vm->def->nets[i];
77 int actualType = virDomainNetGetActualType(net);
79 if (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) {
80 ignore_value(virNetDevBridgeRemovePort(
81 virDomainNetGetActualBridgeName(net),
83 ignore_value(virNetDevTapDelete(net->ifname));
89 virBhyveProcessStart(virConnectPtr conn,
92 virDomainRunningReason reason,
99 virCommandPtr cmd = NULL;
100 virCommandPtr load_cmd = NULL;
101 bhyveConnPtr privconn = conn->privateData;
104 if (virAsprintf(&logfile, "%s/%s.log",
105 BHYVE_LOG_DIR, vm->def->name) < 0)
109 if ((logfd = open(logfile, O_WRONLY | O_APPEND | O_CREAT,
110 S_IRUSR | S_IWUSR)) < 0) {
111 virReportSystemError(errno,
112 _("Failed to open '%s'"),
117 VIR_FREE(privconn->pidfile);
118 if (!(privconn->pidfile = virPidFileBuildPath(BHYVE_STATE_DIR,
120 virReportSystemError(errno,
121 "%s", _("Failed to build pidfile path"));
125 if (unlink(privconn->pidfile) < 0 &&
127 virReportSystemError(errno,
128 _("Cannot remove state PID file %s"),
133 /* Call bhyve to start the VM */
134 if (!(cmd = virBhyveProcessBuildBhyveCmd(driver,
139 virCommandSetOutputFD(cmd, &logfd);
140 virCommandSetErrorFD(cmd, &logfd);
141 virCommandWriteArgLog(cmd, logfd);
142 virCommandSetPidFile(cmd, privconn->pidfile);
143 virCommandDaemonize(cmd);
145 /* Now bhyve command is constructed, meaning the
146 * domain is ready to be started, so we can build
147 * and execute bhyveload command */
148 if (!(load_cmd = virBhyveProcessBuildLoadCmd(driver, vm->def)))
150 virCommandSetOutputFD(load_cmd, &logfd);
151 virCommandSetErrorFD(load_cmd, &logfd);
153 /* Log generated command line */
154 virCommandWriteArgLog(load_cmd, logfd);
155 if ((pos = lseek(logfd, 0, SEEK_END)) < 0)
156 VIR_WARN("Unable to seek to end of logfile: %s",
157 virStrerror(errno, ebuf, sizeof(ebuf)));
159 VIR_DEBUG("Loading domain '%s'", vm->def->name);
160 if (virCommandRun(load_cmd, NULL) < 0)
163 /* Now we can start the domain */
164 VIR_DEBUG("Starting domain '%s'", vm->def->name);
165 if (virCommandRun(cmd, NULL) < 0)
168 if (virPidFileReadPath(privconn->pidfile, &vm->pid) < 0) {
169 virReportError(VIR_ERR_INTERNAL_ERROR,
170 _("Domain %s didn't show up"), vm->def->name);
174 if (flags & VIR_BHYVE_PROCESS_START_AUTODESTROY &&
175 virCloseCallbacksSet(driver->closeCallbacks, vm,
176 conn, bhyveProcessAutoDestroy) < 0)
179 vm->def->id = vm->pid;
180 virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, reason);
186 virCommandPtr destroy_cmd;
187 if ((destroy_cmd = virBhyveProcessBuildDestroyCmd(driver,
189 virCommandSetOutputFD(load_cmd, &logfd);
190 virCommandSetErrorFD(load_cmd, &logfd);
191 ignore_value(virCommandRun(destroy_cmd, NULL));
192 virCommandFree(destroy_cmd);
198 virCommandFree(load_cmd);
201 VIR_FORCE_CLOSE(logfd);
206 virBhyveProcessStop(bhyveConnPtr driver,
208 virDomainShutoffReason reason ATTRIBUTE_UNUSED)
211 virCommandPtr cmd = NULL;
213 if (!virDomainObjIsActive(vm)) {
214 VIR_DEBUG("VM '%s' not active", vm->def->name);
219 virReportError(VIR_ERR_INTERNAL_ERROR,
220 _("Invalid PID %d for VM"),
225 /* First, try to kill 'bhyve' process */
226 if (virProcessKillPainfully(vm->pid, true) != 0)
227 VIR_WARN("Failed to gracefully stop bhyve VM '%s' (pid: %d)",
231 /* Cleanup network interfaces */
234 /* No matter if shutdown was successful or not, we
235 * need to unload the VM */
236 if (!(cmd = virBhyveProcessBuildDestroyCmd(driver, vm->def)))
239 if (virCommandRun(cmd, NULL) < 0)
244 virCloseCallbacksUnset(driver->closeCallbacks, vm,
245 bhyveProcessAutoDestroy);
247 virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, reason);
257 virBhyveGetDomainTotalCpuStats(virDomainObjPtr vm,
258 unsigned long long *cpustats)
260 struct kinfo_proc *kp;
262 char errbuf[_POSIX2_LINE_MAX];
266 if ((kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf)) == NULL) {
267 virReportError(VIR_ERR_SYSTEM_ERROR,
268 _("Unable to get kvm descriptor: %s"),
274 kp = kvm_getprocs(kd, KERN_PROC_PID, vm->pid, &nprocs);
275 if (kp == NULL || nprocs != 1) {
276 virReportError(VIR_ERR_SYSTEM_ERROR,
277 _("Unable to obtain information about pid: %d"),
282 *cpustats = kp->ki_runtime * 1000ull;