Imported Upstream version 1.2.5
[archive/platform/upstream/libvirt.git] / src / bhyve / bhyve_process.c
1 /*
2  * bhyve_process.c: bhyve process management
3  *
4  * Copyright (C) 2014 Roman Bogorodskiy
5  *
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.
10  *
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.
15  *
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/>.
19  *
20  */
21
22 #include <config.h>
23
24 #include <fcntl.h>
25 #include <kvm.h>
26 #include <sys/param.h>
27 #include <sys/types.h>
28 #include <sys/sysctl.h>
29 #include <sys/user.h>
30 #include <sys/ioctl.h>
31 #include <net/if.h>
32 #include <net/if_tap.h>
33
34 #include "bhyve_process.h"
35 #include "bhyve_command.h"
36 #include "datatypes.h"
37 #include "virerror.h"
38 #include "virlog.h"
39 #include "virfile.h"
40 #include "viralloc.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"
48
49 #define VIR_FROM_THIS   VIR_FROM_BHYVE
50
51 VIR_LOG_INIT("bhyve.bhyve_process");
52
53 static virDomainObjPtr
54 bhyveProcessAutoDestroy(virDomainObjPtr vm,
55                         virConnectPtr conn ATTRIBUTE_UNUSED,
56                         void *opaque)
57 {
58     bhyveConnPtr driver = opaque;
59
60     virBhyveProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_DESTROYED);
61
62     if (!vm->persistent) {
63         virDomainObjListRemove(driver->domains, vm);
64         vm = NULL;
65     }
66
67     return vm;
68 }
69
70 static void
71 bhyveNetCleanup(virDomainObjPtr vm)
72 {
73     size_t i;
74
75     for (i = 0; i < vm->def->nnets; i++) {
76         virDomainNetDefPtr net = vm->def->nets[i];
77         int actualType = virDomainNetGetActualType(net);
78
79         if (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) {
80             ignore_value(virNetDevBridgeRemovePort(
81                             virDomainNetGetActualBridgeName(net),
82                             net->ifname));
83             ignore_value(virNetDevTapDelete(net->ifname));
84         }
85     }
86 }
87
88 int
89 virBhyveProcessStart(virConnectPtr conn,
90                      bhyveConnPtr driver,
91                      virDomainObjPtr vm,
92                      virDomainRunningReason reason,
93                      unsigned int flags)
94 {
95     char *logfile = NULL;
96     int logfd = -1;
97     off_t pos = -1;
98     char ebuf[1024];
99     virCommandPtr cmd = NULL;
100     virCommandPtr load_cmd = NULL;
101     bhyveConnPtr privconn = conn->privateData;
102     int ret = -1;
103
104     if (virAsprintf(&logfile, "%s/%s.log",
105                     BHYVE_LOG_DIR, vm->def->name) < 0)
106        return -1;
107
108
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'"),
113                              logfile);
114         goto cleanup;
115     }
116
117     VIR_FREE(privconn->pidfile);
118     if (!(privconn->pidfile = virPidFileBuildPath(BHYVE_STATE_DIR,
119                                                   vm->def->name))) {
120         virReportSystemError(errno,
121                              "%s", _("Failed to build pidfile path"));
122         goto cleanup;
123     }
124
125     if (unlink(privconn->pidfile) < 0 &&
126         errno != ENOENT) {
127         virReportSystemError(errno,
128                              _("Cannot remove state PID file %s"),
129                              privconn->pidfile);
130         goto cleanup;
131     }
132
133     /* Call bhyve to start the VM */
134     if (!(cmd = virBhyveProcessBuildBhyveCmd(driver,
135                                              vm->def,
136                                              false)))
137         goto cleanup;
138
139     virCommandSetOutputFD(cmd, &logfd);
140     virCommandSetErrorFD(cmd, &logfd);
141     virCommandWriteArgLog(cmd, logfd);
142     virCommandSetPidFile(cmd, privconn->pidfile);
143     virCommandDaemonize(cmd);
144
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)))
149         goto cleanup;
150     virCommandSetOutputFD(load_cmd, &logfd);
151     virCommandSetErrorFD(load_cmd, &logfd);
152
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)));
158
159     VIR_DEBUG("Loading domain '%s'", vm->def->name);
160     if (virCommandRun(load_cmd, NULL) < 0)
161         goto cleanup;
162
163     /* Now we can start the domain */
164     VIR_DEBUG("Starting domain '%s'", vm->def->name);
165     if (virCommandRun(cmd, NULL) < 0)
166         goto cleanup;
167
168     if (virPidFileReadPath(privconn->pidfile, &vm->pid) < 0) {
169         virReportError(VIR_ERR_INTERNAL_ERROR,
170                        _("Domain %s didn't show up"), vm->def->name);
171         goto cleanup;
172     }
173
174     if (flags & VIR_BHYVE_PROCESS_START_AUTODESTROY &&
175         virCloseCallbacksSet(driver->closeCallbacks, vm,
176                              conn, bhyveProcessAutoDestroy) < 0)
177         goto cleanup;
178
179     vm->def->id = vm->pid;
180     virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, reason);
181
182     ret = 0;
183
184  cleanup:
185     if (ret < 0) {
186         virCommandPtr destroy_cmd;
187         if ((destroy_cmd = virBhyveProcessBuildDestroyCmd(driver,
188                                                           vm->def)) != NULL) {
189             virCommandSetOutputFD(load_cmd, &logfd);
190             virCommandSetErrorFD(load_cmd, &logfd);
191             ignore_value(virCommandRun(destroy_cmd, NULL));
192             virCommandFree(destroy_cmd);
193         }
194
195         bhyveNetCleanup(vm);
196     }
197
198     virCommandFree(load_cmd);
199     virCommandFree(cmd);
200     VIR_FREE(logfile);
201     VIR_FORCE_CLOSE(logfd);
202     return ret;
203 }
204
205 int
206 virBhyveProcessStop(bhyveConnPtr driver,
207                     virDomainObjPtr vm,
208                     virDomainShutoffReason reason ATTRIBUTE_UNUSED)
209 {
210     int ret = -1;
211     virCommandPtr cmd = NULL;
212
213     if (!virDomainObjIsActive(vm)) {
214         VIR_DEBUG("VM '%s' not active", vm->def->name);
215         return 0;
216     }
217
218     if (vm->pid <= 0) {
219         virReportError(VIR_ERR_INTERNAL_ERROR,
220                        _("Invalid PID %d for VM"),
221                        (int)vm->pid);
222         return -1;
223     }
224
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)",
228                  vm->def->name,
229                  (int)vm->pid);
230
231     /* Cleanup network interfaces */
232     bhyveNetCleanup(vm);
233
234     /* No matter if shutdown was successful or not, we
235      * need to unload the VM */
236     if (!(cmd = virBhyveProcessBuildDestroyCmd(driver, vm->def)))
237         goto cleanup;
238
239     if (virCommandRun(cmd, NULL) < 0)
240         goto cleanup;
241
242     ret = 0;
243
244     virCloseCallbacksUnset(driver->closeCallbacks, vm,
245                            bhyveProcessAutoDestroy);
246
247     virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, reason);
248     vm->pid = -1;
249     vm->def->id = -1;
250
251  cleanup:
252     virCommandFree(cmd);
253     return ret;
254 }
255
256 int
257 virBhyveGetDomainTotalCpuStats(virDomainObjPtr vm,
258                                unsigned long long *cpustats)
259 {
260     struct kinfo_proc *kp;
261     kvm_t *kd;
262     char errbuf[_POSIX2_LINE_MAX];
263     int nprocs;
264     int ret = -1;
265
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"),
269                        errbuf);
270         return -1;
271
272     }
273
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"),
278                        (int)vm->pid);
279         goto cleanup;
280     }
281
282     *cpustats = kp->ki_runtime * 1000ull;
283
284     ret = 0;
285
286  cleanup:
287     kvm_close(kd);
288
289     return ret;
290 }