Merge lp:~ubuntu-branches/ubuntu/maverick/libvirt/maverick-201009152343 into lp:ubuntu/maverick/libvirt
- Maverick (10.10)
- maverick-201009152343
- Merge into maverick
Proposed by
James Westby
Status: | Work in progress |
---|---|
Proposed branch: | lp:~ubuntu-branches/ubuntu/maverick/libvirt/maverick-201009152343 |
Merge into: | lp:ubuntu/maverick/libvirt |
Diff against target: |
2282 lines (+2266/-0) (has conflicts) 2 files modified
.pc/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch/src/uml/uml_driver.c (+2211/-0) debian/patches/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch (+55/-0) Conflict adding file .pc/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch. Moved existing file to .pc/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch.moved. Conflict adding file debian/patches/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch. Moved existing file to debian/patches/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch.moved. |
To merge this branch: | bzr merge lp:~ubuntu-branches/ubuntu/maverick/libvirt/maverick-201009152343 |
Related bugs: |
Commit message
Description of the change
The package history in the archive and the history in the bzr branch differ. As the archive is authoritative the history of lp:ubuntu/maverick/libvirt now reflects that and the old bzr branch has been pushed to lp:~ubuntu-branches/ubuntu/maverick/libvirt/maverick-201009152343. A merge should be performed if necessary.
To post a comment you must log in.
Unmerged revisions
- 105. By Soren Hansen
-
releasing version 0.8.3-1ubuntu11
- 104. By Soren Hansen
-
9026-Rebuild-
network- filter- for-UML- guests- on-updates. patch.
NWFilter updates are supposed to be immediate. Add this support to
the UML driver. - 103. By Soren Hansen
-
Merge previous work branch that diverged due to quilt madness.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added directory '.pc/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch' |
2 | === renamed directory '.pc/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch' => '.pc/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch.moved' |
3 | === added file '.pc/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch/.timestamp' |
4 | === added directory '.pc/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch/src' |
5 | === added directory '.pc/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch/src/uml' |
6 | === added file '.pc/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch/src/uml/uml_driver.c' |
7 | --- .pc/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch/src/uml/uml_driver.c 1970-01-01 00:00:00 +0000 |
8 | +++ .pc/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch/src/uml/uml_driver.c 2010-09-15 23:50:58 +0000 |
9 | @@ -0,0 +1,2211 @@ |
10 | +/* |
11 | + * uml_driver.c: core driver methods for managing UML guests |
12 | + * |
13 | + * Copyright (C) 2006-2010 Red Hat, Inc. |
14 | + * Copyright (C) 2006-2008 Daniel P. Berrange |
15 | + * |
16 | + * This library is free software; you can redistribute it and/or |
17 | + * modify it under the terms of the GNU Lesser General Public |
18 | + * License as published by the Free Software Foundation; either |
19 | + * version 2.1 of the License, or (at your option) any later version. |
20 | + * |
21 | + * This library is distributed in the hope that it will be useful, |
22 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
23 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
24 | + * Lesser General Public License for more details. |
25 | + * |
26 | + * You should have received a copy of the GNU Lesser General Public |
27 | + * License along with this library; if not, write to the Free Software |
28 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
29 | + * |
30 | + * Author: Daniel P. Berrange <berrange@redhat.com> |
31 | + */ |
32 | + |
33 | +#include <config.h> |
34 | + |
35 | +#include <sys/types.h> |
36 | +#include <sys/poll.h> |
37 | +#include <dirent.h> |
38 | +#include <limits.h> |
39 | +#include <string.h> |
40 | +#include <stdio.h> |
41 | +#include <stdarg.h> |
42 | +#include <stdlib.h> |
43 | +#include <unistd.h> |
44 | +#include <errno.h> |
45 | +#include <sys/utsname.h> |
46 | +#include <sys/stat.h> |
47 | +#include <fcntl.h> |
48 | +#include <signal.h> |
49 | +#include <paths.h> |
50 | +#include <pwd.h> |
51 | +#include <stdio.h> |
52 | +#include <sys/wait.h> |
53 | +#include <sys/ioctl.h> |
54 | +#include <sys/inotify.h> |
55 | +#include <sys/un.h> |
56 | + |
57 | +#include "uml_driver.h" |
58 | +#include "uml_conf.h" |
59 | +#include "event.h" |
60 | +#include "buf.h" |
61 | +#include "util.h" |
62 | +#include "nodeinfo.h" |
63 | +#include "stats_linux.h" |
64 | +#include "capabilities.h" |
65 | +#include "memory.h" |
66 | +#include "uuid.h" |
67 | +#include "domain_conf.h" |
68 | +#include "datatypes.h" |
69 | +#include "logging.h" |
70 | +#include "domain_nwfilter.h" |
71 | + |
72 | +#define VIR_FROM_THIS VIR_FROM_UML |
73 | + |
74 | +/* For storing short-lived temporary files. */ |
75 | +#define TEMPDIR LOCAL_STATE_DIR "/cache/libvirt" |
76 | + |
77 | +typedef struct _umlDomainObjPrivate umlDomainObjPrivate; |
78 | +typedef umlDomainObjPrivate *umlDomainObjPrivatePtr; |
79 | +struct _umlDomainObjPrivate { |
80 | + int monitor; |
81 | + int monitorWatch; |
82 | +}; |
83 | + |
84 | + |
85 | +static int umlShutdown(void); |
86 | + |
87 | +static void *umlDomainObjPrivateAlloc(void) |
88 | +{ |
89 | + umlDomainObjPrivatePtr priv; |
90 | + |
91 | + if (VIR_ALLOC(priv) < 0) |
92 | + return NULL; |
93 | + |
94 | + priv->monitor = -1; |
95 | + priv->monitorWatch = -1; |
96 | + |
97 | + return priv; |
98 | +} |
99 | + |
100 | +static void umlDomainObjPrivateFree(void *data) |
101 | +{ |
102 | + umlDomainObjPrivatePtr priv = data; |
103 | + |
104 | + VIR_FREE(priv); |
105 | +} |
106 | + |
107 | + |
108 | +static void umlDriverLock(struct uml_driver *driver) |
109 | +{ |
110 | + virMutexLock(&driver->lock); |
111 | +} |
112 | +static void umlDriverUnlock(struct uml_driver *driver) |
113 | +{ |
114 | + virMutexUnlock(&driver->lock); |
115 | +} |
116 | + |
117 | + |
118 | +static int umlOpenMonitor(struct uml_driver *driver, |
119 | + virDomainObjPtr vm); |
120 | +static int umlReadPidFile(struct uml_driver *driver, |
121 | + virDomainObjPtr vm); |
122 | + |
123 | +static int umlSetCloseExec(int fd) { |
124 | + int flags; |
125 | + if ((flags = fcntl(fd, F_GETFD)) < 0) |
126 | + goto error; |
127 | + flags |= FD_CLOEXEC; |
128 | + if ((fcntl(fd, F_SETFD, flags)) < 0) |
129 | + goto error; |
130 | + return 0; |
131 | + error: |
132 | + VIR_ERROR0(_("Failed to set close-on-exec file descriptor flag")); |
133 | + return -1; |
134 | +} |
135 | + |
136 | +static int umlStartVMDaemon(virConnectPtr conn, |
137 | + struct uml_driver *driver, |
138 | + virDomainObjPtr vm); |
139 | + |
140 | +static void umlShutdownVMDaemon(virConnectPtr conn, |
141 | + struct uml_driver *driver, |
142 | + virDomainObjPtr vm); |
143 | + |
144 | + |
145 | +static int umlMonitorCommand(const struct uml_driver *driver, |
146 | + const virDomainObjPtr vm, |
147 | + const char *cmd, |
148 | + char **reply); |
149 | + |
150 | +static struct uml_driver *uml_driver = NULL; |
151 | + |
152 | +struct umlAutostartData { |
153 | + struct uml_driver *driver; |
154 | + virConnectPtr conn; |
155 | +}; |
156 | + |
157 | +static void |
158 | +umlAutostartDomain(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaque) |
159 | +{ |
160 | + virDomainObjPtr vm = payload; |
161 | + const struct umlAutostartData *data = opaque; |
162 | + |
163 | + virDomainObjLock(vm); |
164 | + if (vm->autostart && |
165 | + !virDomainObjIsActive(vm)) { |
166 | + virResetLastError(); |
167 | + if (umlStartVMDaemon(data->conn, data->driver, vm) < 0) { |
168 | + virErrorPtr err = virGetLastError(); |
169 | + VIR_ERROR(_("Failed to autostart VM '%s': %s"), |
170 | + vm->def->name, err ? err->message : _("unknown error")); |
171 | + } |
172 | + } |
173 | + virDomainObjUnlock(vm); |
174 | +} |
175 | + |
176 | +static void |
177 | +umlAutostartConfigs(struct uml_driver *driver) { |
178 | + /* XXX: Figure out a better way todo this. The domain |
179 | + * startup code needs a connection handle in order |
180 | + * to lookup the bridge associated with a virtual |
181 | + * network |
182 | + */ |
183 | + virConnectPtr conn = virConnectOpen(driver->privileged ? |
184 | + "uml:///system" : |
185 | + "uml:///session"); |
186 | + /* Ignoring NULL conn which is mostly harmless here */ |
187 | + |
188 | + struct umlAutostartData data = { driver, conn }; |
189 | + |
190 | + virHashForEach(driver->domains.objs, umlAutostartDomain, &data); |
191 | + |
192 | + if (conn) |
193 | + virConnectClose(conn); |
194 | +} |
195 | + |
196 | + |
197 | +static int |
198 | +umlIdentifyOneChrPTY(struct uml_driver *driver, |
199 | + virDomainObjPtr dom, |
200 | + virDomainChrDefPtr def, |
201 | + const char *dev) |
202 | +{ |
203 | + char *cmd; |
204 | + char *res = NULL; |
205 | + int retries = 0; |
206 | + if (virAsprintf(&cmd, "config %s%d", dev, def->target.port) < 0) { |
207 | + virReportOOMError(); |
208 | + return -1; |
209 | + } |
210 | +requery: |
211 | + if (umlMonitorCommand(driver, dom, cmd, &res) < 0) |
212 | + return -1; |
213 | + |
214 | + if (res && STRPREFIX(res, "pts:")) { |
215 | + VIR_FREE(def->data.file.path); |
216 | + if ((def->data.file.path = strdup(res + 4)) == NULL) { |
217 | + virReportOOMError(); |
218 | + VIR_FREE(res); |
219 | + VIR_FREE(cmd); |
220 | + return -1; |
221 | + } |
222 | + } else if (!res || STRPREFIX(res, "pts")) { |
223 | + /* It can take a while to startup, so retry for |
224 | + upto 5 seconds */ |
225 | + /* XXX should do this in a better non-blocking |
226 | + way somehow ...perhaps register a timer */ |
227 | + if (retries++ < 50) { |
228 | + usleep(1000*10); |
229 | + goto requery; |
230 | + } |
231 | + } |
232 | + |
233 | + VIR_FREE(cmd); |
234 | + VIR_FREE(res); |
235 | + return 0; |
236 | +} |
237 | + |
238 | +static int |
239 | +umlIdentifyChrPTY(struct uml_driver *driver, |
240 | + virDomainObjPtr dom) |
241 | +{ |
242 | + int i; |
243 | + |
244 | + if (dom->def->console && |
245 | + dom->def->console->type == VIR_DOMAIN_CHR_TYPE_PTY) |
246 | + if (umlIdentifyOneChrPTY(driver, dom, |
247 | + dom->def->console, "con") < 0) |
248 | + return -1; |
249 | + |
250 | + for (i = 0 ; i < dom->def->nserials; i++) |
251 | + if (dom->def->serials[i]->type == VIR_DOMAIN_CHR_TYPE_PTY && |
252 | + umlIdentifyOneChrPTY(driver, dom, |
253 | + dom->def->serials[i], "ssl") < 0) |
254 | + return -1; |
255 | + |
256 | + return 0; |
257 | +} |
258 | + |
259 | +static void |
260 | +umlInotifyEvent(int watch, |
261 | + int fd, |
262 | + int events ATTRIBUTE_UNUSED, |
263 | + void *data) |
264 | +{ |
265 | + char buf[1024]; |
266 | + struct inotify_event *e; |
267 | + int got; |
268 | + char *tmp, *name; |
269 | + struct uml_driver *driver = data; |
270 | + virDomainObjPtr dom; |
271 | + |
272 | + umlDriverLock(driver); |
273 | + if (watch != driver->inotifyWatch) |
274 | + goto cleanup; |
275 | + |
276 | +reread: |
277 | + got = read(fd, buf, sizeof(buf)); |
278 | + if (got == -1) { |
279 | + if (errno == EINTR) |
280 | + goto reread; |
281 | + goto cleanup; |
282 | + } |
283 | + |
284 | + tmp = buf; |
285 | + while (got) { |
286 | + if (got < sizeof(struct inotify_event)) |
287 | + goto cleanup; /* bad */ |
288 | + |
289 | + e = (struct inotify_event *)tmp; |
290 | + tmp += sizeof(struct inotify_event); |
291 | + got -= sizeof(struct inotify_event); |
292 | + |
293 | + if (got < e->len) |
294 | + goto cleanup; |
295 | + |
296 | + tmp += e->len; |
297 | + got -= e->len; |
298 | + |
299 | + name = (char *)&(e->name); |
300 | + |
301 | + dom = virDomainFindByName(&driver->domains, name); |
302 | + |
303 | + if (!dom) { |
304 | + continue; |
305 | + } |
306 | + |
307 | + if (e->mask & IN_DELETE) { |
308 | + VIR_DEBUG("Got inotify domain shutdown '%s'", name); |
309 | + if (!virDomainObjIsActive(dom)) { |
310 | + virDomainObjUnlock(dom); |
311 | + continue; |
312 | + } |
313 | + |
314 | + umlShutdownVMDaemon(NULL, driver, dom); |
315 | + } else if (e->mask & (IN_CREATE | IN_MODIFY)) { |
316 | + VIR_DEBUG("Got inotify domain startup '%s'", name); |
317 | + if (virDomainObjIsActive(dom)) { |
318 | + virDomainObjUnlock(dom); |
319 | + continue; |
320 | + } |
321 | + |
322 | + if (umlReadPidFile(driver, dom) < 0) { |
323 | + virDomainObjUnlock(dom); |
324 | + continue; |
325 | + } |
326 | + |
327 | + dom->def->id = driver->nextvmid++; |
328 | + dom->state = VIR_DOMAIN_RUNNING; |
329 | + |
330 | + if (umlOpenMonitor(driver, dom) < 0) { |
331 | + VIR_WARN0("Could not open monitor for new domain"); |
332 | + umlShutdownVMDaemon(NULL, driver, dom); |
333 | + } else if (umlIdentifyChrPTY(driver, dom) < 0) { |
334 | + VIR_WARN0("Could not identify charater devices for new domain"); |
335 | + umlShutdownVMDaemon(NULL, driver, dom); |
336 | + } |
337 | + } |
338 | + virDomainObjUnlock(dom); |
339 | + } |
340 | + |
341 | +cleanup: |
342 | + umlDriverUnlock(driver); |
343 | +} |
344 | + |
345 | +/** |
346 | + * umlStartup: |
347 | + * |
348 | + * Initialization function for the Uml daemon |
349 | + */ |
350 | +static int |
351 | +umlStartup(int privileged) { |
352 | + uid_t uid = geteuid(); |
353 | + char *base = NULL; |
354 | + char driverConf[PATH_MAX]; |
355 | + char *userdir = NULL; |
356 | + |
357 | + if (VIR_ALLOC(uml_driver) < 0) |
358 | + return -1; |
359 | + |
360 | + uml_driver->privileged = privileged; |
361 | + |
362 | + if (virMutexInit(¨_driver->lock) < 0) { |
363 | + VIR_FREE(uml_driver); |
364 | + return -1; |
365 | + } |
366 | + umlDriverLock(uml_driver); |
367 | + |
368 | + /* Don't have a dom0 so start from 1 */ |
369 | + uml_driver->nextvmid = 1; |
370 | + uml_driver->inotifyWatch = -1; |
371 | + |
372 | + if (virDomainObjListInit(¨_driver->domains) < 0) |
373 | + goto error; |
374 | + |
375 | + userdir = virGetUserDirectory(uid); |
376 | + if (!userdir) |
377 | + goto error; |
378 | + |
379 | + if (privileged) { |
380 | + if (virAsprintf(¨_driver->logDir, |
381 | + "%s/log/libvirt/uml", LOCAL_STATE_DIR) == -1) |
382 | + goto out_of_memory; |
383 | + |
384 | + if ((base = strdup (SYSCONF_DIR "/libvirt")) == NULL) |
385 | + goto out_of_memory; |
386 | + } else { |
387 | + |
388 | + if (virAsprintf(¨_driver->logDir, |
389 | + "%s/.libvirt/uml/log", userdir) == -1) |
390 | + goto out_of_memory; |
391 | + |
392 | + if (virAsprintf(&base, "%s/.libvirt", userdir) == -1) |
393 | + goto out_of_memory; |
394 | + } |
395 | + |
396 | + if (virAsprintf(¨_driver->monitorDir, |
397 | + "%s/.uml", userdir) == -1) |
398 | + goto out_of_memory; |
399 | + |
400 | + /* Configuration paths are either ~/.libvirt/uml/... (session) or |
401 | + * /etc/libvirt/uml/... (system). |
402 | + */ |
403 | + if (snprintf (driverConf, sizeof(driverConf), "%s/uml.conf", base) == -1) |
404 | + goto out_of_memory; |
405 | + driverConf[sizeof(driverConf)-1] = '\0'; |
406 | + |
407 | + if (virAsprintf(¨_driver->configDir, "%s/uml", base) == -1) |
408 | + goto out_of_memory; |
409 | + |
410 | + if (virAsprintf(¨_driver->autostartDir, "%s/uml/autostart", base) == -1) |
411 | + goto out_of_memory; |
412 | + |
413 | + VIR_FREE(base); |
414 | + |
415 | + if ((uml_driver->caps = umlCapsInit()) == NULL) |
416 | + goto out_of_memory; |
417 | + |
418 | + uml_driver->caps->privateDataAllocFunc = umlDomainObjPrivateAlloc; |
419 | + uml_driver->caps->privateDataFreeFunc = umlDomainObjPrivateFree; |
420 | + |
421 | + if ((uml_driver->inotifyFD = inotify_init()) < 0) { |
422 | + VIR_ERROR0(_("cannot initialize inotify")); |
423 | + goto error; |
424 | + } |
425 | + |
426 | + if (virFileMakePath(uml_driver->monitorDir) != 0) { |
427 | + char ebuf[1024]; |
428 | + VIR_ERROR(_("Failed to create monitor directory %s: %s"), |
429 | + uml_driver->monitorDir, virStrerror(errno, ebuf, sizeof ebuf)); |
430 | + goto error; |
431 | + } |
432 | + |
433 | + VIR_INFO("Adding inotify watch on %s", uml_driver->monitorDir); |
434 | + if (inotify_add_watch(uml_driver->inotifyFD, |
435 | + uml_driver->monitorDir, |
436 | + IN_CREATE | IN_MODIFY | IN_DELETE) < 0) { |
437 | + goto error; |
438 | + } |
439 | + |
440 | + if ((uml_driver->inotifyWatch = |
441 | + virEventAddHandle(uml_driver->inotifyFD, POLLIN, |
442 | + umlInotifyEvent, uml_driver, NULL)) < 0) |
443 | + goto error; |
444 | + |
445 | + if (virDomainLoadAllConfigs(uml_driver->caps, |
446 | + ¨_driver->domains, |
447 | + uml_driver->configDir, |
448 | + uml_driver->autostartDir, |
449 | + 0, NULL, NULL) < 0) |
450 | + goto error; |
451 | + |
452 | + umlAutostartConfigs(uml_driver); |
453 | + |
454 | + umlDriverUnlock(uml_driver); |
455 | + VIR_FREE(userdir); |
456 | + |
457 | + return 0; |
458 | + |
459 | +out_of_memory: |
460 | + VIR_ERROR0(_("umlStartup: out of memory")); |
461 | + |
462 | +error: |
463 | + VIR_FREE(userdir); |
464 | + VIR_FREE(base); |
465 | + umlDriverUnlock(uml_driver); |
466 | + umlShutdown(); |
467 | + return -1; |
468 | +} |
469 | + |
470 | +/** |
471 | + * umlReload: |
472 | + * |
473 | + * Function to restart the Uml daemon, it will recheck the configuration |
474 | + * files and update its state and the networking |
475 | + */ |
476 | +static int |
477 | +umlReload(void) { |
478 | + if (!uml_driver) |
479 | + return 0; |
480 | + |
481 | + umlDriverLock(uml_driver); |
482 | + virDomainLoadAllConfigs(uml_driver->caps, |
483 | + ¨_driver->domains, |
484 | + uml_driver->configDir, |
485 | + uml_driver->autostartDir, |
486 | + 0, NULL, NULL); |
487 | + |
488 | + umlAutostartConfigs(uml_driver); |
489 | + umlDriverUnlock(uml_driver); |
490 | + |
491 | + return 0; |
492 | +} |
493 | + |
494 | +/** |
495 | + * umlActive: |
496 | + * |
497 | + * Checks if the Uml daemon is active, i.e. has an active domain or |
498 | + * an active network |
499 | + * |
500 | + * Returns 1 if active, 0 otherwise |
501 | + */ |
502 | +static int |
503 | +umlActive(void) { |
504 | + int active = 0; |
505 | + |
506 | + if (!uml_driver) |
507 | + return 0; |
508 | + |
509 | + umlDriverLock(uml_driver); |
510 | + active = virDomainObjListNumOfDomains(¨_driver->domains, 1); |
511 | + umlDriverUnlock(uml_driver); |
512 | + |
513 | + return active; |
514 | +} |
515 | + |
516 | +static void |
517 | +umlShutdownOneVM(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaque) |
518 | +{ |
519 | + virDomainObjPtr dom = payload; |
520 | + struct uml_driver *driver = opaque; |
521 | + |
522 | + virDomainObjLock(dom); |
523 | + if (virDomainObjIsActive(dom)) |
524 | + umlShutdownVMDaemon(NULL, driver, dom); |
525 | + virDomainObjUnlock(dom); |
526 | +} |
527 | + |
528 | +/** |
529 | + * umlShutdown: |
530 | + * |
531 | + * Shutdown the Uml daemon, it will stop all active domains and networks |
532 | + */ |
533 | +static int |
534 | +umlShutdown(void) { |
535 | + if (!uml_driver) |
536 | + return -1; |
537 | + |
538 | + umlDriverLock(uml_driver); |
539 | + if (uml_driver->inotifyWatch != -1) |
540 | + virEventRemoveHandle(uml_driver->inotifyWatch); |
541 | + close(uml_driver->inotifyFD); |
542 | + virCapabilitiesFree(uml_driver->caps); |
543 | + |
544 | + /* shutdown active VMs |
545 | + * XXX allow them to stay around & reconnect */ |
546 | + virHashForEach(uml_driver->domains.objs, umlShutdownOneVM, uml_driver); |
547 | + |
548 | + virDomainObjListDeinit(¨_driver->domains); |
549 | + |
550 | + VIR_FREE(uml_driver->logDir); |
551 | + VIR_FREE(uml_driver->configDir); |
552 | + VIR_FREE(uml_driver->autostartDir); |
553 | + VIR_FREE(uml_driver->monitorDir); |
554 | + |
555 | + if (uml_driver->brctl) |
556 | + brShutdown(uml_driver->brctl); |
557 | + |
558 | + umlDriverUnlock(uml_driver); |
559 | + virMutexDestroy(¨_driver->lock); |
560 | + VIR_FREE(uml_driver); |
561 | + |
562 | + return 0; |
563 | +} |
564 | + |
565 | + |
566 | +static int umlReadPidFile(struct uml_driver *driver, |
567 | + virDomainObjPtr vm) |
568 | +{ |
569 | + int rc = -1; |
570 | + FILE *file; |
571 | + char *pidfile = NULL; |
572 | + int retries = 0; |
573 | + |
574 | + vm->pid = -1; |
575 | + if (virAsprintf(&pidfile, "%s/%s/pid", |
576 | + driver->monitorDir, vm->def->name) < 0) { |
577 | + virReportOOMError(); |
578 | + return -1; |
579 | + } |
580 | + |
581 | +reopen: |
582 | + if (!(file = fopen(pidfile, "r"))) { |
583 | + if (errno == ENOENT && |
584 | + retries++ < 50) { |
585 | + usleep(1000 * 100); |
586 | + goto reopen; |
587 | + } |
588 | + goto cleanup; |
589 | + } |
590 | + |
591 | + if (fscanf(file, "%d", &vm->pid) != 1) { |
592 | + errno = EINVAL; |
593 | + fclose(file); |
594 | + goto cleanup; |
595 | + } |
596 | + |
597 | + if (fclose(file) < 0) |
598 | + goto cleanup; |
599 | + |
600 | + rc = 0; |
601 | + |
602 | + cleanup: |
603 | + if (rc != 0) |
604 | + virReportSystemError(errno, |
605 | + _("failed to read pid: %s"), |
606 | + pidfile); |
607 | + VIR_FREE(pidfile); |
608 | + return rc; |
609 | +} |
610 | + |
611 | +static int umlMonitorAddress(const struct uml_driver *driver, |
612 | + virDomainObjPtr vm, |
613 | + struct sockaddr_un *addr) { |
614 | + char *sockname; |
615 | + int retval = 0; |
616 | + |
617 | + if (virAsprintf(&sockname, "%s/%s/mconsole", |
618 | + driver->monitorDir, vm->def->name) < 0) { |
619 | + virReportOOMError(); |
620 | + return -1; |
621 | + } |
622 | + |
623 | + memset(addr, 0, sizeof *addr); |
624 | + addr->sun_family = AF_UNIX; |
625 | + if (virStrcpyStatic(addr->sun_path, sockname) == NULL) { |
626 | + umlReportError(VIR_ERR_INTERNAL_ERROR, |
627 | + _("Unix path %s too long for destination"), sockname); |
628 | + retval = -1; |
629 | + } |
630 | + VIR_FREE(sockname); |
631 | + return retval; |
632 | +} |
633 | + |
634 | +static int umlOpenMonitor(struct uml_driver *driver, |
635 | + virDomainObjPtr vm) { |
636 | + struct sockaddr_un addr; |
637 | + struct stat sb; |
638 | + int retries = 0; |
639 | + umlDomainObjPrivatePtr priv = vm->privateData; |
640 | + |
641 | + if (umlMonitorAddress(driver, vm, &addr) < 0) |
642 | + return -1; |
643 | + |
644 | + VIR_DEBUG("Dest address for monitor is '%s'", addr.sun_path); |
645 | +restat: |
646 | + if (stat(addr.sun_path, &sb) < 0) { |
647 | + if (errno == ENOENT && |
648 | + retries++ < 50) { |
649 | + usleep(1000 * 100); |
650 | + goto restat; |
651 | + } |
652 | + return -1; |
653 | + } |
654 | + |
655 | + if ((priv->monitor = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0) { |
656 | + virReportSystemError(errno, |
657 | + "%s", _("cannot open socket")); |
658 | + return -1; |
659 | + } |
660 | + |
661 | + memset(addr.sun_path, 0, sizeof addr.sun_path); |
662 | + sprintf(addr.sun_path + 1, "libvirt-uml-%u", vm->pid); |
663 | + VIR_DEBUG("Reply address for monitor is '%s'", addr.sun_path+1); |
664 | + if (bind(priv->monitor, (struct sockaddr *)&addr, sizeof addr) < 0) { |
665 | + virReportSystemError(errno, |
666 | + "%s", _("cannot bind socket")); |
667 | + close(priv->monitor); |
668 | + priv->monitor = -1; |
669 | + return -1; |
670 | + } |
671 | + |
672 | + return 0; |
673 | +} |
674 | + |
675 | + |
676 | +#define MONITOR_MAGIC 0xcafebabe |
677 | +#define MONITOR_BUFLEN 512 |
678 | +#define MONITOR_VERSION 2 |
679 | + |
680 | +struct monitor_request { |
681 | + uint32_t magic; |
682 | + uint32_t version; |
683 | + uint32_t length; |
684 | + char data[MONITOR_BUFLEN]; |
685 | +}; |
686 | + |
687 | +struct monitor_response { |
688 | + uint32_t error; |
689 | + uint32_t extra; |
690 | + uint32_t length; |
691 | + char data[MONITOR_BUFLEN]; |
692 | +}; |
693 | + |
694 | + |
695 | +static int umlMonitorCommand(const struct uml_driver *driver, |
696 | + const virDomainObjPtr vm, |
697 | + const char *cmd, |
698 | + char **reply) |
699 | +{ |
700 | + struct monitor_request req; |
701 | + struct monitor_response res; |
702 | + char *retdata = NULL; |
703 | + int retlen = 0, ret = 0; |
704 | + struct sockaddr_un addr; |
705 | + unsigned int addrlen; |
706 | + umlDomainObjPrivatePtr priv = vm->privateData; |
707 | + |
708 | + VIR_DEBUG("Run command '%s'", cmd); |
709 | + |
710 | + *reply = NULL; |
711 | + |
712 | + if (umlMonitorAddress(driver, vm, &addr) < 0) |
713 | + return -1; |
714 | + |
715 | + memset(&req, 0, sizeof(req)); |
716 | + req.magic = MONITOR_MAGIC; |
717 | + req.version = MONITOR_VERSION; |
718 | + req.length = strlen(cmd); |
719 | + if (req.length > (MONITOR_BUFLEN-1)) { |
720 | + virReportSystemError(EINVAL, |
721 | + _("cannot send too long command %s (%d bytes)"), |
722 | + cmd, req.length); |
723 | + return -1; |
724 | + } |
725 | + if (virStrcpyStatic(req.data, cmd) == NULL) { |
726 | + umlReportError(VIR_ERR_INTERNAL_ERROR, |
727 | + _("Command %s too long for destination"), cmd); |
728 | + return -1; |
729 | + } |
730 | + |
731 | + if (sendto(priv->monitor, &req, sizeof req, 0, |
732 | + (struct sockaddr *)&addr, sizeof addr) != (sizeof req)) { |
733 | + virReportSystemError(errno, |
734 | + _("cannot send command %s"), |
735 | + cmd); |
736 | + return -1; |
737 | + } |
738 | + |
739 | + do { |
740 | + ssize_t nbytes; |
741 | + addrlen = sizeof(addr); |
742 | + nbytes = recvfrom(priv->monitor, &res, sizeof res, 0, |
743 | + (struct sockaddr *)&addr, &addrlen); |
744 | + if (nbytes < 0) { |
745 | + if (errno == EAGAIN || errno == EINTR) |
746 | + continue; |
747 | + virReportSystemError(errno, _("cannot read reply %s"), cmd); |
748 | + goto error; |
749 | + } |
750 | + /* Ensure res.length is safe to read before validating its value. */ |
751 | + if (nbytes < offsetof(struct monitor_request, data) || |
752 | + nbytes < offsetof(struct monitor_request, data) + res.length) { |
753 | + virReportSystemError(0, _("incomplete reply %s"), cmd); |
754 | + goto error; |
755 | + } |
756 | + |
757 | + if (VIR_REALLOC_N(retdata, retlen + res.length) < 0) { |
758 | + virReportOOMError(); |
759 | + goto error; |
760 | + } |
761 | + memcpy(retdata + retlen, res.data, res.length); |
762 | + retlen += res.length - 1; |
763 | + retdata[retlen] = '\0'; |
764 | + |
765 | + if (res.error) |
766 | + ret = -1; |
767 | + |
768 | + } while (res.extra); |
769 | + |
770 | + VIR_DEBUG("Command reply is '%s'", NULLSTR(retdata)); |
771 | + |
772 | + if (ret < 0) |
773 | + VIR_FREE(retdata); |
774 | + else |
775 | + *reply = retdata; |
776 | + |
777 | + return ret; |
778 | + |
779 | +error: |
780 | + VIR_FREE(retdata); |
781 | + return -1; |
782 | +} |
783 | + |
784 | + |
785 | +static int umlCleanupTapDevices(virConnectPtr conn ATTRIBUTE_UNUSED, |
786 | + virDomainObjPtr vm) { |
787 | + int i; |
788 | + int err; |
789 | + int ret = 0; |
790 | + brControl *brctl = NULL; |
791 | + VIR_ERROR0(_("Cleanup tap")); |
792 | + if (brInit(&brctl) < 0) |
793 | + return -1; |
794 | + |
795 | + for (i = 0 ; i < vm->def->nnets ; i++) { |
796 | + virDomainNetDefPtr def = vm->def->nets[i]; |
797 | + |
798 | + if (def->type != VIR_DOMAIN_NET_TYPE_BRIDGE && |
799 | + def->type != VIR_DOMAIN_NET_TYPE_NETWORK) |
800 | + continue; |
801 | + |
802 | + VIR_ERROR(_("Cleanup '%s'"), def->ifname); |
803 | + err = brDeleteTap(brctl, def->ifname); |
804 | + if (err) { |
805 | + VIR_ERROR(_("Cleanup failed %d"), err); |
806 | + ret = -1; |
807 | + } |
808 | + } |
809 | + VIR_ERROR0(_("Cleanup tap done")); |
810 | + brShutdown(brctl); |
811 | + return ret; |
812 | +} |
813 | + |
814 | +static int umlStartVMDaemon(virConnectPtr conn, |
815 | + struct uml_driver *driver, |
816 | + virDomainObjPtr vm) { |
817 | + const char **argv = NULL, **tmp; |
818 | + const char **progenv = NULL; |
819 | + int i, ret; |
820 | + pid_t pid; |
821 | + char *logfile; |
822 | + int logfd = -1; |
823 | + struct stat sb; |
824 | + fd_set keepfd; |
825 | + char ebuf[1024]; |
826 | + umlDomainObjPrivatePtr priv = vm->privateData; |
827 | + |
828 | + FD_ZERO(&keepfd); |
829 | + |
830 | + if (virDomainObjIsActive(vm)) { |
831 | + umlReportError(VIR_ERR_INTERNAL_ERROR, "%s", |
832 | + _("VM is already active")); |
833 | + return -1; |
834 | + } |
835 | + |
836 | + if (!vm->def->os.kernel) { |
837 | + umlReportError(VIR_ERR_INTERNAL_ERROR, "%s", |
838 | + _("no kernel specified")); |
839 | + return -1; |
840 | + } |
841 | + /* Make sure the binary we are about to try exec'ing exists. |
842 | + * Technically we could catch the exec() failure, but that's |
843 | + * in a sub-process so its hard to feed back a useful error |
844 | + */ |
845 | + if (stat(vm->def->os.kernel, &sb) < 0) { |
846 | + virReportSystemError(errno, |
847 | + _("Cannot find UML kernel %s"), |
848 | + vm->def->os.kernel); |
849 | + return -1; |
850 | + } |
851 | + |
852 | + if (virFileMakePath(driver->logDir) != 0) { |
853 | + virReportSystemError(errno, |
854 | + _("cannot create log directory %s"), |
855 | + driver->logDir); |
856 | + return -1; |
857 | + } |
858 | + |
859 | + if (virAsprintf(&logfile, "%s/%s.log", |
860 | + driver->logDir, vm->def->name) < 0) { |
861 | + virReportOOMError(); |
862 | + return -1; |
863 | + } |
864 | + |
865 | + if ((logfd = open(logfile, O_CREAT | O_TRUNC | O_WRONLY, |
866 | + S_IRUSR | S_IWUSR)) < 0) { |
867 | + virReportSystemError(errno, |
868 | + _("failed to create logfile %s"), |
869 | + logfile); |
870 | + VIR_FREE(logfile); |
871 | + return -1; |
872 | + } |
873 | + VIR_FREE(logfile); |
874 | + |
875 | + if (umlSetCloseExec(logfd) < 0) { |
876 | + virReportSystemError(errno, |
877 | + "%s", _("Unable to set VM logfile close-on-exec flag")); |
878 | + close(logfd); |
879 | + return -1; |
880 | + } |
881 | + |
882 | + if (umlBuildCommandLine(conn, driver, vm, &keepfd, |
883 | + &argv, &progenv) < 0) { |
884 | + close(logfd); |
885 | + virDomainConfVMNWFilterTeardown(vm); |
886 | + umlCleanupTapDevices(conn, vm); |
887 | + return -1; |
888 | + } |
889 | + |
890 | + tmp = progenv; |
891 | + while (*tmp) { |
892 | + if (safewrite(logfd, *tmp, strlen(*tmp)) < 0) |
893 | + VIR_WARN("Unable to write envv to logfile: %s", |
894 | + virStrerror(errno, ebuf, sizeof ebuf)); |
895 | + if (safewrite(logfd, " ", 1) < 0) |
896 | + VIR_WARN("Unable to write envv to logfile: %s", |
897 | + virStrerror(errno, ebuf, sizeof ebuf)); |
898 | + tmp++; |
899 | + } |
900 | + tmp = argv; |
901 | + while (*tmp) { |
902 | + if (safewrite(logfd, *tmp, strlen(*tmp)) < 0) |
903 | + VIR_WARN("Unable to write argv to logfile: %s", |
904 | + virStrerror(errno, ebuf, sizeof ebuf)); |
905 | + if (safewrite(logfd, " ", 1) < 0) |
906 | + VIR_WARN("Unable to write argv to logfile: %s", |
907 | + virStrerror(errno, ebuf, sizeof ebuf)); |
908 | + tmp++; |
909 | + } |
910 | + if (safewrite(logfd, "\n", 1) < 0) |
911 | + VIR_WARN("Unable to write argv to logfile: %s", |
912 | + virStrerror(errno, ebuf, sizeof ebuf)); |
913 | + |
914 | + priv->monitor = -1; |
915 | + |
916 | + ret = virExecDaemonize(argv, progenv, &keepfd, &pid, |
917 | + -1, &logfd, &logfd, |
918 | + VIR_EXEC_CLEAR_CAPS, |
919 | + NULL, NULL, NULL); |
920 | + close(logfd); |
921 | + |
922 | + /* |
923 | + * At the moment, the only thing that populates keepfd is |
924 | + * umlBuildCommandLineChr. We want to close every fd it opens. |
925 | + */ |
926 | + for (i = 0; i < FD_SETSIZE; i++) |
927 | + if (FD_ISSET(i, &keepfd)) |
928 | + close(i); |
929 | + |
930 | + for (i = 0 ; argv[i] ; i++) |
931 | + VIR_FREE(argv[i]); |
932 | + VIR_FREE(argv); |
933 | + |
934 | + for (i = 0 ; progenv[i] ; i++) |
935 | + VIR_FREE(progenv[i]); |
936 | + VIR_FREE(progenv); |
937 | + |
938 | + if (ret < 0) { |
939 | + virDomainConfVMNWFilterTeardown(vm); |
940 | + umlCleanupTapDevices(conn, vm); |
941 | + } |
942 | + |
943 | + |
944 | + /* NB we don't mark it running here - we do that async |
945 | + with inotify */ |
946 | + /* XXX what if someone else tries to start it again |
947 | + before we get the inotification ? Sounds like |
948 | + trouble.... */ |
949 | + |
950 | + return ret; |
951 | +} |
952 | + |
953 | +static void umlShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED, |
954 | + struct uml_driver *driver ATTRIBUTE_UNUSED, |
955 | + virDomainObjPtr vm) |
956 | +{ |
957 | + int ret; |
958 | + umlDomainObjPrivatePtr priv = vm->privateData; |
959 | + |
960 | + if (!virDomainObjIsActive(vm)) |
961 | + return; |
962 | + |
963 | + virKillProcess(vm->pid, SIGTERM); |
964 | + |
965 | + if (priv->monitor != -1) |
966 | + close(priv->monitor); |
967 | + priv->monitor = -1; |
968 | + |
969 | + if ((ret = waitpid(vm->pid, NULL, 0)) != vm->pid) { |
970 | + VIR_WARN("Got unexpected pid %d != %d", |
971 | + ret, vm->pid); |
972 | + } |
973 | + |
974 | + vm->pid = -1; |
975 | + vm->def->id = -1; |
976 | + vm->state = VIR_DOMAIN_SHUTOFF; |
977 | + |
978 | + virDomainConfVMNWFilterTeardown(vm); |
979 | + umlCleanupTapDevices(conn, vm); |
980 | + |
981 | + if (vm->newDef) { |
982 | + virDomainDefFree(vm->def); |
983 | + vm->def = vm->newDef; |
984 | + vm->def->id = -1; |
985 | + vm->newDef = NULL; |
986 | + } |
987 | +} |
988 | + |
989 | + |
990 | +static virDrvOpenStatus umlOpen(virConnectPtr conn, |
991 | + virConnectAuthPtr auth ATTRIBUTE_UNUSED, |
992 | + int flags ATTRIBUTE_UNUSED) { |
993 | + if (conn->uri == NULL) { |
994 | + if (uml_driver == NULL) |
995 | + return VIR_DRV_OPEN_DECLINED; |
996 | + |
997 | + conn->uri = xmlParseURI(uml_driver->privileged ? |
998 | + "uml:///system" : |
999 | + "uml:///session"); |
1000 | + if (!conn->uri) { |
1001 | + virReportOOMError(); |
1002 | + return VIR_DRV_OPEN_ERROR; |
1003 | + } |
1004 | + } else { |
1005 | + if (conn->uri->scheme == NULL || |
1006 | + STRNEQ (conn->uri->scheme, "uml")) |
1007 | + return VIR_DRV_OPEN_DECLINED; |
1008 | + |
1009 | + /* Allow remote driver to deal with URIs with hostname server */ |
1010 | + if (conn->uri->server != NULL) |
1011 | + return VIR_DRV_OPEN_DECLINED; |
1012 | + |
1013 | + |
1014 | + /* Check path and tell them correct path if they made a mistake */ |
1015 | + if (uml_driver->privileged) { |
1016 | + if (STRNEQ (conn->uri->path, "/system") && |
1017 | + STRNEQ (conn->uri->path, "/session")) { |
1018 | + umlReportError(VIR_ERR_INTERNAL_ERROR, |
1019 | + _("unexpected UML URI path '%s', try uml:///system"), |
1020 | + conn->uri->path); |
1021 | + return VIR_DRV_OPEN_ERROR; |
1022 | + } |
1023 | + } else { |
1024 | + if (STRNEQ (conn->uri->path, "/session")) { |
1025 | + umlReportError(VIR_ERR_INTERNAL_ERROR, |
1026 | + _("unexpected UML URI path '%s', try uml:///session"), |
1027 | + conn->uri->path); |
1028 | + return VIR_DRV_OPEN_ERROR; |
1029 | + } |
1030 | + } |
1031 | + |
1032 | + /* URI was good, but driver isn't active */ |
1033 | + if (uml_driver == NULL) { |
1034 | + umlReportError(VIR_ERR_INTERNAL_ERROR, "%s", |
1035 | + _("uml state driver is not active")); |
1036 | + return VIR_DRV_OPEN_ERROR; |
1037 | + } |
1038 | + } |
1039 | + |
1040 | + conn->privateData = uml_driver; |
1041 | + |
1042 | + return VIR_DRV_OPEN_SUCCESS; |
1043 | +} |
1044 | + |
1045 | +static int umlClose(virConnectPtr conn) { |
1046 | + /*struct uml_driver *driver = conn->privateData;*/ |
1047 | + |
1048 | + conn->privateData = NULL; |
1049 | + |
1050 | + return 0; |
1051 | +} |
1052 | + |
1053 | +static const char *umlGetType(virConnectPtr conn ATTRIBUTE_UNUSED) { |
1054 | + return "UML"; |
1055 | +} |
1056 | + |
1057 | + |
1058 | +static int umlIsSecure(virConnectPtr conn ATTRIBUTE_UNUSED) |
1059 | +{ |
1060 | + /* Trivially secure, since always inside the daemon */ |
1061 | + return 1; |
1062 | +} |
1063 | + |
1064 | + |
1065 | +static int umlIsEncrypted(virConnectPtr conn ATTRIBUTE_UNUSED) |
1066 | +{ |
1067 | + /* Not encrypted, but remote driver takes care of that */ |
1068 | + return 0; |
1069 | +} |
1070 | + |
1071 | + |
1072 | +static char *umlGetCapabilities(virConnectPtr conn) { |
1073 | + struct uml_driver *driver = (struct uml_driver *)conn->privateData; |
1074 | + char *xml; |
1075 | + |
1076 | + umlDriverLock(driver); |
1077 | + if ((xml = virCapabilitiesFormatXML(driver->caps)) == NULL) |
1078 | + virReportOOMError(); |
1079 | + umlDriverUnlock(driver); |
1080 | + |
1081 | + return xml; |
1082 | +} |
1083 | + |
1084 | + |
1085 | + |
1086 | +static int umlGetProcessInfo(unsigned long long *cpuTime, int pid) { |
1087 | + char proc[PATH_MAX]; |
1088 | + FILE *pidinfo; |
1089 | + unsigned long long usertime, systime; |
1090 | + |
1091 | + if (snprintf(proc, sizeof(proc), "/proc/%d/stat", pid) >= (int)sizeof(proc)) { |
1092 | + return -1; |
1093 | + } |
1094 | + |
1095 | + if (!(pidinfo = fopen(proc, "r"))) { |
1096 | + /*printf("cannot read pid info");*/ |
1097 | + /* VM probably shut down, so fake 0 */ |
1098 | + *cpuTime = 0; |
1099 | + return 0; |
1100 | + } |
1101 | + |
1102 | + if (fscanf(pidinfo, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %llu %llu", &usertime, &systime) != 2) { |
1103 | + umlDebug("not enough arg"); |
1104 | + fclose(pidinfo); |
1105 | + return -1; |
1106 | + } |
1107 | + |
1108 | + /* We got jiffies |
1109 | + * We want nanoseconds |
1110 | + * _SC_CLK_TCK is jiffies per second |
1111 | + * So calulate thus.... |
1112 | + */ |
1113 | + *cpuTime = 1000ull * 1000ull * 1000ull * (usertime + systime) / (unsigned long long)sysconf(_SC_CLK_TCK); |
1114 | + |
1115 | + umlDebug("Got %llu %llu %llu", usertime, systime, *cpuTime); |
1116 | + |
1117 | + fclose(pidinfo); |
1118 | + |
1119 | + return 0; |
1120 | +} |
1121 | + |
1122 | + |
1123 | +static virDomainPtr umlDomainLookupByID(virConnectPtr conn, |
1124 | + int id) { |
1125 | + struct uml_driver *driver = (struct uml_driver *)conn->privateData; |
1126 | + virDomainObjPtr vm; |
1127 | + virDomainPtr dom = NULL; |
1128 | + |
1129 | + umlDriverLock(driver); |
1130 | + vm = virDomainFindByID(&driver->domains, id); |
1131 | + umlDriverUnlock(driver); |
1132 | + |
1133 | + if (!vm) { |
1134 | + umlReportError(VIR_ERR_NO_DOMAIN, NULL); |
1135 | + goto cleanup; |
1136 | + } |
1137 | + |
1138 | + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); |
1139 | + if (dom) dom->id = vm->def->id; |
1140 | + |
1141 | +cleanup: |
1142 | + if (vm) |
1143 | + virDomainObjUnlock(vm); |
1144 | + return dom; |
1145 | +} |
1146 | + |
1147 | +static virDomainPtr umlDomainLookupByUUID(virConnectPtr conn, |
1148 | + const unsigned char *uuid) { |
1149 | + struct uml_driver *driver = (struct uml_driver *)conn->privateData; |
1150 | + virDomainObjPtr vm; |
1151 | + virDomainPtr dom = NULL; |
1152 | + |
1153 | + umlDriverLock(driver); |
1154 | + vm = virDomainFindByUUID(&driver->domains, uuid); |
1155 | + umlDriverUnlock(driver); |
1156 | + |
1157 | + if (!vm) { |
1158 | + umlReportError(VIR_ERR_NO_DOMAIN, NULL); |
1159 | + goto cleanup; |
1160 | + } |
1161 | + |
1162 | + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); |
1163 | + if (dom) dom->id = vm->def->id; |
1164 | + |
1165 | +cleanup: |
1166 | + if (vm) |
1167 | + virDomainObjUnlock(vm); |
1168 | + return dom; |
1169 | +} |
1170 | + |
1171 | +static virDomainPtr umlDomainLookupByName(virConnectPtr conn, |
1172 | + const char *name) { |
1173 | + struct uml_driver *driver = (struct uml_driver *)conn->privateData; |
1174 | + virDomainObjPtr vm; |
1175 | + virDomainPtr dom = NULL; |
1176 | + |
1177 | + umlDriverLock(driver); |
1178 | + vm = virDomainFindByName(&driver->domains, name); |
1179 | + umlDriverUnlock(driver); |
1180 | + |
1181 | + if (!vm) { |
1182 | + umlReportError(VIR_ERR_NO_DOMAIN, NULL); |
1183 | + goto cleanup; |
1184 | + } |
1185 | + |
1186 | + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); |
1187 | + if (dom) dom->id = vm->def->id; |
1188 | + |
1189 | +cleanup: |
1190 | + if (vm) |
1191 | + virDomainObjUnlock(vm); |
1192 | + return dom; |
1193 | +} |
1194 | + |
1195 | + |
1196 | +static int umlDomainIsActive(virDomainPtr dom) |
1197 | +{ |
1198 | + struct uml_driver *driver = dom->conn->privateData; |
1199 | + virDomainObjPtr obj; |
1200 | + int ret = -1; |
1201 | + |
1202 | + umlDriverLock(driver); |
1203 | + obj = virDomainFindByUUID(&driver->domains, dom->uuid); |
1204 | + umlDriverUnlock(driver); |
1205 | + if (!obj) { |
1206 | + umlReportError(VIR_ERR_NO_DOMAIN, NULL); |
1207 | + goto cleanup; |
1208 | + } |
1209 | + ret = virDomainObjIsActive(obj); |
1210 | + |
1211 | +cleanup: |
1212 | + if (obj) |
1213 | + virDomainObjUnlock(obj); |
1214 | + return ret; |
1215 | +} |
1216 | + |
1217 | + |
1218 | +static int umlDomainIsPersistent(virDomainPtr dom) |
1219 | +{ |
1220 | + struct uml_driver *driver = dom->conn->privateData; |
1221 | + virDomainObjPtr obj; |
1222 | + int ret = -1; |
1223 | + |
1224 | + umlDriverLock(driver); |
1225 | + obj = virDomainFindByUUID(&driver->domains, dom->uuid); |
1226 | + umlDriverUnlock(driver); |
1227 | + if (!obj) { |
1228 | + umlReportError(VIR_ERR_NO_DOMAIN, NULL); |
1229 | + goto cleanup; |
1230 | + } |
1231 | + ret = obj->persistent; |
1232 | + |
1233 | +cleanup: |
1234 | + if (obj) |
1235 | + virDomainObjUnlock(obj); |
1236 | + return ret; |
1237 | +} |
1238 | + |
1239 | + |
1240 | +static int umlGetVersion(virConnectPtr conn, unsigned long *version) { |
1241 | + struct uml_driver *driver = conn->privateData; |
1242 | + struct utsname ut; |
1243 | + int ret = -1; |
1244 | + |
1245 | + umlDriverLock(driver); |
1246 | + |
1247 | + if (driver->umlVersion == 0) { |
1248 | + uname(&ut); |
1249 | + |
1250 | + if (virParseVersionString(ut.release, &driver->umlVersion) < 0) { |
1251 | + umlReportError(VIR_ERR_INTERNAL_ERROR, |
1252 | + _("cannot parse version %s"), ut.release); |
1253 | + goto cleanup; |
1254 | + } |
1255 | + } |
1256 | + |
1257 | + *version = driver->umlVersion; |
1258 | + ret = 0; |
1259 | + |
1260 | +cleanup: |
1261 | + umlDriverUnlock(driver); |
1262 | + return ret; |
1263 | +} |
1264 | + |
1265 | +static int umlListDomains(virConnectPtr conn, int *ids, int nids) { |
1266 | + struct uml_driver *driver = conn->privateData; |
1267 | + int n; |
1268 | + |
1269 | + umlDriverLock(driver); |
1270 | + n = virDomainObjListGetActiveIDs(&driver->domains, ids, nids); |
1271 | + umlDriverUnlock(driver); |
1272 | + |
1273 | + return n; |
1274 | +} |
1275 | +static int umlNumDomains(virConnectPtr conn) { |
1276 | + struct uml_driver *driver = conn->privateData; |
1277 | + int n; |
1278 | + |
1279 | + umlDriverLock(driver); |
1280 | + n = virDomainObjListNumOfDomains(&driver->domains, 1); |
1281 | + umlDriverUnlock(driver); |
1282 | + |
1283 | + return n; |
1284 | +} |
1285 | +static virDomainPtr umlDomainCreate(virConnectPtr conn, const char *xml, |
1286 | + unsigned int flags) { |
1287 | + struct uml_driver *driver = conn->privateData; |
1288 | + virDomainDefPtr def; |
1289 | + virDomainObjPtr vm = NULL; |
1290 | + virDomainPtr dom = NULL; |
1291 | + |
1292 | + virCheckFlags(0, NULL); |
1293 | + |
1294 | + umlDriverLock(driver); |
1295 | + if (!(def = virDomainDefParseString(driver->caps, xml, |
1296 | + VIR_DOMAIN_XML_INACTIVE))) |
1297 | + goto cleanup; |
1298 | + |
1299 | + if (virDomainObjIsDuplicate(&driver->domains, def, 1) < 0) |
1300 | + goto cleanup; |
1301 | + |
1302 | + if (!(vm = virDomainAssignDef(driver->caps, |
1303 | + &driver->domains, |
1304 | + def, false))) |
1305 | + goto cleanup; |
1306 | + def = NULL; |
1307 | + |
1308 | + if (umlStartVMDaemon(conn, driver, vm) < 0) { |
1309 | + virDomainRemoveInactive(&driver->domains, |
1310 | + vm); |
1311 | + vm = NULL; |
1312 | + goto cleanup; |
1313 | + } |
1314 | + |
1315 | + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); |
1316 | + if (dom) dom->id = vm->def->id; |
1317 | + |
1318 | +cleanup: |
1319 | + virDomainDefFree(def); |
1320 | + if (vm) |
1321 | + virDomainObjUnlock(vm); |
1322 | + umlDriverUnlock(driver); |
1323 | + return dom; |
1324 | +} |
1325 | + |
1326 | + |
1327 | +static int umlDomainShutdown(virDomainPtr dom) { |
1328 | + struct uml_driver *driver = dom->conn->privateData; |
1329 | + virDomainObjPtr vm; |
1330 | + char *info = NULL; |
1331 | + int ret = -1; |
1332 | + |
1333 | + umlDriverLock(driver); |
1334 | + vm = virDomainFindByID(&driver->domains, dom->id); |
1335 | + umlDriverUnlock(driver); |
1336 | + if (!vm) { |
1337 | + umlReportError(VIR_ERR_INVALID_DOMAIN, |
1338 | + _("no domain with matching id %d"), dom->id); |
1339 | + goto cleanup; |
1340 | + } |
1341 | + |
1342 | +#if 0 |
1343 | + if (umlMonitorCommand(driver, vm, "system_powerdown", &info) < 0) { |
1344 | + umlReportError(VIR_ERR_OPERATION_FAILED, "%s", |
1345 | + _("shutdown operation failed")); |
1346 | + goto cleanup; |
1347 | + } |
1348 | + ret = 0; |
1349 | +#endif |
1350 | + |
1351 | +cleanup: |
1352 | + VIR_FREE(info); |
1353 | + if (vm) |
1354 | + virDomainObjUnlock(vm); |
1355 | + return ret; |
1356 | +} |
1357 | + |
1358 | + |
1359 | +static int umlDomainDestroy(virDomainPtr dom) { |
1360 | + struct uml_driver *driver = dom->conn->privateData; |
1361 | + virDomainObjPtr vm; |
1362 | + int ret = -1; |
1363 | + |
1364 | + umlDriverLock(driver); |
1365 | + vm = virDomainFindByID(&driver->domains, dom->id); |
1366 | + if (!vm) { |
1367 | + umlReportError(VIR_ERR_INVALID_DOMAIN, |
1368 | + _("no domain with matching id %d"), dom->id); |
1369 | + goto cleanup; |
1370 | + } |
1371 | + |
1372 | + umlShutdownVMDaemon(dom->conn, driver, vm); |
1373 | + if (!vm->persistent) { |
1374 | + virDomainRemoveInactive(&driver->domains, |
1375 | + vm); |
1376 | + vm = NULL; |
1377 | + } |
1378 | + ret = 0; |
1379 | + |
1380 | +cleanup: |
1381 | + if (vm) |
1382 | + virDomainObjUnlock(vm); |
1383 | + umlDriverUnlock(driver); |
1384 | + return ret; |
1385 | +} |
1386 | + |
1387 | + |
1388 | +static char *umlDomainGetOSType(virDomainPtr dom) { |
1389 | + struct uml_driver *driver = dom->conn->privateData; |
1390 | + virDomainObjPtr vm; |
1391 | + char *type = NULL; |
1392 | + |
1393 | + umlDriverLock(driver); |
1394 | + vm = virDomainFindByUUID(&driver->domains, dom->uuid); |
1395 | + umlDriverUnlock(driver); |
1396 | + if (!vm) { |
1397 | + umlReportError(VIR_ERR_INVALID_DOMAIN, "%s", |
1398 | + _("no domain with matching uuid")); |
1399 | + goto cleanup; |
1400 | + } |
1401 | + |
1402 | + if (!(type = strdup(vm->def->os.type))) |
1403 | + virReportOOMError(); |
1404 | + |
1405 | +cleanup: |
1406 | + if (vm) |
1407 | + virDomainObjUnlock(vm); |
1408 | + return type; |
1409 | +} |
1410 | + |
1411 | +/* Returns max memory in kb, 0 if error */ |
1412 | +static unsigned long umlDomainGetMaxMemory(virDomainPtr dom) { |
1413 | + struct uml_driver *driver = dom->conn->privateData; |
1414 | + virDomainObjPtr vm; |
1415 | + unsigned long ret = 0; |
1416 | + |
1417 | + umlDriverLock(driver); |
1418 | + vm = virDomainFindByUUID(&driver->domains, dom->uuid); |
1419 | + umlDriverUnlock(driver); |
1420 | + |
1421 | + if (!vm) { |
1422 | + char uuidstr[VIR_UUID_STRING_BUFLEN]; |
1423 | + |
1424 | + virUUIDFormat(dom->uuid, uuidstr); |
1425 | + umlReportError(VIR_ERR_INVALID_DOMAIN, |
1426 | + _("no domain with matching uuid '%s'"), uuidstr); |
1427 | + goto cleanup; |
1428 | + } |
1429 | + ret = vm->def->maxmem; |
1430 | + |
1431 | +cleanup: |
1432 | + if (vm) |
1433 | + virDomainObjUnlock(vm); |
1434 | + return ret; |
1435 | +} |
1436 | + |
1437 | +static int umlDomainSetMaxMemory(virDomainPtr dom, unsigned long newmax) { |
1438 | + struct uml_driver *driver = dom->conn->privateData; |
1439 | + virDomainObjPtr vm; |
1440 | + int ret = -1; |
1441 | + |
1442 | + umlDriverLock(driver); |
1443 | + vm = virDomainFindByUUID(&driver->domains, dom->uuid); |
1444 | + umlDriverUnlock(driver); |
1445 | + |
1446 | + if (!vm) { |
1447 | + char uuidstr[VIR_UUID_STRING_BUFLEN]; |
1448 | + |
1449 | + virUUIDFormat(dom->uuid, uuidstr); |
1450 | + umlReportError(VIR_ERR_INVALID_DOMAIN, |
1451 | + _("no domain with matching uuid '%s'"), uuidstr); |
1452 | + goto cleanup; |
1453 | + } |
1454 | + |
1455 | + if (newmax < vm->def->memory) { |
1456 | + umlReportError(VIR_ERR_INVALID_ARG, "%s", |
1457 | + _("cannot set max memory lower than current memory")); |
1458 | + goto cleanup; |
1459 | + } |
1460 | + |
1461 | + vm->def->maxmem = newmax; |
1462 | + ret = 0; |
1463 | + |
1464 | +cleanup: |
1465 | + if (vm) |
1466 | + virDomainObjUnlock(vm); |
1467 | + return ret; |
1468 | +} |
1469 | + |
1470 | +static int umlDomainSetMemory(virDomainPtr dom, unsigned long newmem) { |
1471 | + struct uml_driver *driver = dom->conn->privateData; |
1472 | + virDomainObjPtr vm; |
1473 | + int ret = -1; |
1474 | + |
1475 | + umlDriverLock(driver); |
1476 | + vm = virDomainFindByUUID(&driver->domains, dom->uuid); |
1477 | + umlDriverUnlock(driver); |
1478 | + |
1479 | + if (!vm) { |
1480 | + char uuidstr[VIR_UUID_STRING_BUFLEN]; |
1481 | + |
1482 | + virUUIDFormat(dom->uuid, uuidstr); |
1483 | + umlReportError(VIR_ERR_INVALID_DOMAIN, |
1484 | + _("no domain with matching uuid '%s'"), uuidstr); |
1485 | + goto cleanup; |
1486 | + } |
1487 | + |
1488 | + if (virDomainObjIsActive(vm)) { |
1489 | + umlReportError(VIR_ERR_NO_SUPPORT, "%s", |
1490 | + _("cannot set memory of an active domain")); |
1491 | + goto cleanup; |
1492 | + } |
1493 | + |
1494 | + if (newmem > vm->def->maxmem) { |
1495 | + umlReportError(VIR_ERR_INVALID_ARG, "%s", |
1496 | + _("cannot set memory higher than max memory")); |
1497 | + goto cleanup; |
1498 | + } |
1499 | + |
1500 | + vm->def->memory = newmem; |
1501 | + ret = 0; |
1502 | + |
1503 | +cleanup: |
1504 | + if (vm) |
1505 | + virDomainObjUnlock(vm); |
1506 | + return ret; |
1507 | +} |
1508 | + |
1509 | +static int umlDomainGetInfo(virDomainPtr dom, |
1510 | + virDomainInfoPtr info) { |
1511 | + struct uml_driver *driver = dom->conn->privateData; |
1512 | + virDomainObjPtr vm; |
1513 | + int ret = -1; |
1514 | + |
1515 | + umlDriverLock(driver); |
1516 | + vm = virDomainFindByUUID(&driver->domains, dom->uuid); |
1517 | + umlDriverUnlock(driver); |
1518 | + |
1519 | + if (!vm) { |
1520 | + umlReportError(VIR_ERR_INVALID_DOMAIN, "%s", |
1521 | + _("no domain with matching uuid")); |
1522 | + goto cleanup; |
1523 | + } |
1524 | + |
1525 | + info->state = vm->state; |
1526 | + |
1527 | + if (!virDomainObjIsActive(vm)) { |
1528 | + info->cpuTime = 0; |
1529 | + } else { |
1530 | + if (umlGetProcessInfo(&(info->cpuTime), vm->pid) < 0) { |
1531 | + umlReportError(VIR_ERR_OPERATION_FAILED, "%s", |
1532 | + _("cannot read cputime for domain")); |
1533 | + goto cleanup; |
1534 | + } |
1535 | + } |
1536 | + |
1537 | + info->maxMem = vm->def->maxmem; |
1538 | + info->memory = vm->def->memory; |
1539 | + info->nrVirtCpu = vm->def->vcpus; |
1540 | + ret = 0; |
1541 | + |
1542 | +cleanup: |
1543 | + if (vm) |
1544 | + virDomainObjUnlock(vm); |
1545 | + return ret; |
1546 | +} |
1547 | + |
1548 | + |
1549 | +static char *umlDomainDumpXML(virDomainPtr dom, |
1550 | + int flags ATTRIBUTE_UNUSED) { |
1551 | + struct uml_driver *driver = dom->conn->privateData; |
1552 | + virDomainObjPtr vm; |
1553 | + char *ret = NULL; |
1554 | + |
1555 | + umlDriverLock(driver); |
1556 | + vm = virDomainFindByUUID(&driver->domains, dom->uuid); |
1557 | + umlDriverUnlock(driver); |
1558 | + |
1559 | + if (!vm) { |
1560 | + umlReportError(VIR_ERR_INVALID_DOMAIN, "%s", |
1561 | + _("no domain with matching uuid")); |
1562 | + goto cleanup; |
1563 | + } |
1564 | + |
1565 | + ret = virDomainDefFormat((flags & VIR_DOMAIN_XML_INACTIVE) && vm->newDef ? |
1566 | + vm->newDef : vm->def, |
1567 | + flags); |
1568 | + |
1569 | +cleanup: |
1570 | + if (vm) |
1571 | + virDomainObjUnlock(vm); |
1572 | + return ret; |
1573 | +} |
1574 | + |
1575 | + |
1576 | +static int umlListDefinedDomains(virConnectPtr conn, |
1577 | + char **const names, int nnames) { |
1578 | + struct uml_driver *driver = conn->privateData; |
1579 | + int n; |
1580 | + |
1581 | + umlDriverLock(driver); |
1582 | + n = virDomainObjListGetInactiveNames(&driver->domains, names, nnames); |
1583 | + umlDriverUnlock(driver); |
1584 | + |
1585 | + return n; |
1586 | +} |
1587 | + |
1588 | +static int umlNumDefinedDomains(virConnectPtr conn) { |
1589 | + struct uml_driver *driver = conn->privateData; |
1590 | + int n; |
1591 | + |
1592 | + umlDriverLock(driver); |
1593 | + n = virDomainObjListNumOfDomains(&driver->domains, 0); |
1594 | + umlDriverUnlock(driver); |
1595 | + |
1596 | + return n; |
1597 | +} |
1598 | + |
1599 | + |
1600 | +static int umlDomainStartWithFlags(virDomainPtr dom, unsigned int flags) { |
1601 | + struct uml_driver *driver = dom->conn->privateData; |
1602 | + virDomainObjPtr vm; |
1603 | + int ret = -1; |
1604 | + |
1605 | + virCheckFlags(0, -1); |
1606 | + |
1607 | + umlDriverLock(driver); |
1608 | + vm = virDomainFindByUUID(&driver->domains, dom->uuid); |
1609 | + |
1610 | + if (!vm) { |
1611 | + umlReportError(VIR_ERR_INVALID_DOMAIN, "%s", |
1612 | + _("no domain with matching uuid")); |
1613 | + goto cleanup; |
1614 | + } |
1615 | + |
1616 | + ret = umlStartVMDaemon(dom->conn, driver, vm); |
1617 | + |
1618 | +cleanup: |
1619 | + if (vm) |
1620 | + virDomainObjUnlock(vm); |
1621 | + umlDriverUnlock(driver); |
1622 | + return ret; |
1623 | +} |
1624 | + |
1625 | +static int umlDomainStart(virDomainPtr dom) { |
1626 | + return umlDomainStartWithFlags(dom, 0); |
1627 | +} |
1628 | + |
1629 | +static virDomainPtr umlDomainDefine(virConnectPtr conn, const char *xml) { |
1630 | + struct uml_driver *driver = conn->privateData; |
1631 | + virDomainDefPtr def; |
1632 | + virDomainObjPtr vm = NULL; |
1633 | + virDomainPtr dom = NULL; |
1634 | + |
1635 | + umlDriverLock(driver); |
1636 | + if (!(def = virDomainDefParseString(driver->caps, xml, |
1637 | + VIR_DOMAIN_XML_INACTIVE))) |
1638 | + goto cleanup; |
1639 | + |
1640 | + if (virDomainObjIsDuplicate(&driver->domains, def, 0) < 0) |
1641 | + goto cleanup; |
1642 | + |
1643 | + if (!(vm = virDomainAssignDef(driver->caps, |
1644 | + &driver->domains, |
1645 | + def, false))) |
1646 | + goto cleanup; |
1647 | + def = NULL; |
1648 | + vm->persistent = 1; |
1649 | + |
1650 | + if (virDomainSaveConfig(driver->configDir, |
1651 | + vm->newDef ? vm->newDef : vm->def) < 0) { |
1652 | + virDomainRemoveInactive(&driver->domains, |
1653 | + vm); |
1654 | + vm = NULL; |
1655 | + goto cleanup; |
1656 | + } |
1657 | + |
1658 | + dom = virGetDomain(conn, vm->def->name, vm->def->uuid); |
1659 | + if (dom) dom->id = vm->def->id; |
1660 | + |
1661 | +cleanup: |
1662 | + virDomainDefFree(def); |
1663 | + if (vm) |
1664 | + virDomainObjUnlock(vm); |
1665 | + umlDriverUnlock(driver); |
1666 | + return dom; |
1667 | +} |
1668 | + |
1669 | +static int umlDomainUndefine(virDomainPtr dom) { |
1670 | + struct uml_driver *driver = dom->conn->privateData; |
1671 | + virDomainObjPtr vm; |
1672 | + int ret = -1; |
1673 | + |
1674 | + umlDriverLock(driver); |
1675 | + vm = virDomainFindByUUID(&driver->domains, dom->uuid); |
1676 | + if (!vm) { |
1677 | + umlReportError(VIR_ERR_INVALID_DOMAIN, "%s", |
1678 | + _("no domain with matching uuid")); |
1679 | + goto cleanup; |
1680 | + } |
1681 | + |
1682 | + if (virDomainObjIsActive(vm)) { |
1683 | + umlReportError(VIR_ERR_INTERNAL_ERROR, "%s", |
1684 | + _("cannot delete active domain")); |
1685 | + goto cleanup; |
1686 | + } |
1687 | + |
1688 | + if (!vm->persistent) { |
1689 | + umlReportError(VIR_ERR_INTERNAL_ERROR, "%s", |
1690 | + _("cannot undefine transient domain")); |
1691 | + goto cleanup; |
1692 | + } |
1693 | + |
1694 | + if (virDomainDeleteConfig(driver->configDir, driver->autostartDir, vm) < 0) |
1695 | + goto cleanup; |
1696 | + |
1697 | + virDomainRemoveInactive(&driver->domains, |
1698 | + vm); |
1699 | + vm = NULL; |
1700 | + ret = 0; |
1701 | + |
1702 | +cleanup: |
1703 | + if (vm) |
1704 | + virDomainObjUnlock(vm); |
1705 | + umlDriverUnlock(driver); |
1706 | + return ret; |
1707 | +} |
1708 | + |
1709 | + |
1710 | +static int umlDomainAttachUmlDisk(struct uml_driver *driver, |
1711 | + virDomainObjPtr vm, |
1712 | + virDomainDiskDefPtr disk) |
1713 | +{ |
1714 | + int i; |
1715 | + char *cmd = NULL; |
1716 | + char *reply = NULL; |
1717 | + |
1718 | + for (i = 0 ; i < vm->def->ndisks ; i++) { |
1719 | + if (STREQ(vm->def->disks[i]->dst, disk->dst)) { |
1720 | + umlReportError(VIR_ERR_OPERATION_FAILED, |
1721 | + _("target %s already exists"), disk->dst); |
1722 | + return -1; |
1723 | + } |
1724 | + } |
1725 | + |
1726 | + if (!disk->src) { |
1727 | + umlReportError(VIR_ERR_INTERNAL_ERROR, |
1728 | + "%s", _("disk source path is missing")); |
1729 | + goto error; |
1730 | + } |
1731 | + |
1732 | + if (virAsprintf(&cmd, "config %s=%s", disk->dst, disk->src) < 0) { |
1733 | + virReportOOMError(); |
1734 | + return -1; |
1735 | + } |
1736 | + |
1737 | + if (umlMonitorCommand(driver, vm, cmd, &reply) < 0) |
1738 | + goto error; |
1739 | + |
1740 | + if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0) { |
1741 | + virReportOOMError(); |
1742 | + goto error; |
1743 | + } |
1744 | + |
1745 | + virDomainDiskInsertPreAlloced(vm->def, disk); |
1746 | + |
1747 | + VIR_FREE(reply); |
1748 | + VIR_FREE(cmd); |
1749 | + |
1750 | + return 0; |
1751 | + |
1752 | +error: |
1753 | + |
1754 | + VIR_FREE(reply); |
1755 | + VIR_FREE(cmd); |
1756 | + |
1757 | + return -1; |
1758 | +} |
1759 | + |
1760 | + |
1761 | +static int umlDomainAttachDevice(virDomainPtr dom, const char *xml) |
1762 | +{ |
1763 | + struct uml_driver *driver = dom->conn->privateData; |
1764 | + virDomainObjPtr vm; |
1765 | + virDomainDeviceDefPtr dev = NULL; |
1766 | + int ret = -1; |
1767 | + |
1768 | + umlDriverLock(driver); |
1769 | + |
1770 | + vm = virDomainFindByUUID(&driver->domains, dom->uuid); |
1771 | + if (!vm) { |
1772 | + char uuidstr[VIR_UUID_STRING_BUFLEN]; |
1773 | + virUUIDFormat(dom->uuid, uuidstr); |
1774 | + umlReportError(VIR_ERR_NO_DOMAIN, |
1775 | + _("no domain with matching uuid '%s'"), uuidstr); |
1776 | + goto cleanup; |
1777 | + } |
1778 | + |
1779 | + if (!virDomainObjIsActive(vm)) { |
1780 | + umlReportError(VIR_ERR_OPERATION_INVALID, |
1781 | + "%s", _("cannot attach device on inactive domain")); |
1782 | + goto cleanup; |
1783 | + } |
1784 | + |
1785 | + dev = virDomainDeviceDefParse(driver->caps, vm->def, xml, |
1786 | + VIR_DOMAIN_XML_INACTIVE); |
1787 | + |
1788 | + if (dev == NULL) |
1789 | + goto cleanup; |
1790 | + |
1791 | + if (dev->type == VIR_DOMAIN_DEVICE_DISK) { |
1792 | + if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_UML) { |
1793 | + ret = umlDomainAttachUmlDisk(driver, vm, dev->data.disk); |
1794 | + if (ret == 0) |
1795 | + dev->data.disk = NULL; |
1796 | + } else { |
1797 | + umlReportError(VIR_ERR_CONFIG_UNSUPPORTED, |
1798 | + _("disk bus '%s' cannot be hotplugged."), |
1799 | + virDomainDiskBusTypeToString(dev->data.disk->bus)); |
1800 | + } |
1801 | + } else { |
1802 | + umlReportError(VIR_ERR_CONFIG_UNSUPPORTED, |
1803 | + _("device type '%s' cannot be attached"), |
1804 | + virDomainDeviceTypeToString(dev->type)); |
1805 | + goto cleanup; |
1806 | + } |
1807 | + |
1808 | +cleanup: |
1809 | + |
1810 | + virDomainDeviceDefFree(dev); |
1811 | + if (vm) |
1812 | + virDomainObjUnlock(vm); |
1813 | + umlDriverUnlock(driver); |
1814 | + return ret; |
1815 | +} |
1816 | + |
1817 | + |
1818 | +static int umlDomainAttachDeviceFlags(virDomainPtr dom, |
1819 | + const char *xml, |
1820 | + unsigned int flags) { |
1821 | + if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) { |
1822 | + umlReportError(VIR_ERR_OPERATION_INVALID, |
1823 | + "%s", _("cannot modify the persistent configuration of a domain")); |
1824 | + return -1; |
1825 | + } |
1826 | + |
1827 | + return umlDomainAttachDevice(dom, xml); |
1828 | +} |
1829 | + |
1830 | + |
1831 | +static int umlDomainDetachUmlDisk(struct uml_driver *driver, |
1832 | + virDomainObjPtr vm, |
1833 | + virDomainDeviceDefPtr dev) |
1834 | +{ |
1835 | + int i, ret = -1; |
1836 | + virDomainDiskDefPtr detach = NULL; |
1837 | + char *cmd; |
1838 | + char *reply; |
1839 | + |
1840 | + for (i = 0 ; i < vm->def->ndisks ; i++) { |
1841 | + if (STREQ(vm->def->disks[i]->dst, dev->data.disk->dst)) { |
1842 | + break; |
1843 | + } |
1844 | + } |
1845 | + |
1846 | + if (i == vm->def->ndisks) { |
1847 | + umlReportError(VIR_ERR_OPERATION_FAILED, |
1848 | + _("disk %s not found"), dev->data.disk->dst); |
1849 | + return -1; |
1850 | + } |
1851 | + |
1852 | + detach = vm->def->disks[i]; |
1853 | + |
1854 | + if (virAsprintf(&cmd, "remove %s", detach->dst) < 0) { |
1855 | + virReportOOMError(); |
1856 | + return -1; |
1857 | + } |
1858 | + |
1859 | + if (umlMonitorCommand(driver, vm, cmd, &reply) < 0) |
1860 | + goto cleanup; |
1861 | + |
1862 | + virDomainDiskRemove(vm->def, i); |
1863 | + |
1864 | + virDomainDiskDefFree(detach); |
1865 | + |
1866 | + ret = 0; |
1867 | + |
1868 | + VIR_FREE(reply); |
1869 | + |
1870 | +cleanup: |
1871 | + VIR_FREE(cmd); |
1872 | + |
1873 | + return ret; |
1874 | +} |
1875 | + |
1876 | + |
1877 | +static int umlDomainDetachDevice(virDomainPtr dom, const char *xml) { |
1878 | + struct uml_driver *driver = dom->conn->privateData; |
1879 | + virDomainObjPtr vm; |
1880 | + virDomainDeviceDefPtr dev = NULL; |
1881 | + int ret = -1; |
1882 | + |
1883 | + umlDriverLock(driver); |
1884 | + vm = virDomainFindByUUID(&driver->domains, dom->uuid); |
1885 | + if (!vm) { |
1886 | + char uuidstr[VIR_UUID_STRING_BUFLEN]; |
1887 | + virUUIDFormat(dom->uuid, uuidstr); |
1888 | + umlReportError(VIR_ERR_NO_DOMAIN, |
1889 | + _("no domain with matching uuid '%s'"), uuidstr); |
1890 | + goto cleanup; |
1891 | + } |
1892 | + |
1893 | + if (!virDomainObjIsActive(vm)) { |
1894 | + umlReportError(VIR_ERR_OPERATION_INVALID, |
1895 | + "%s", _("cannot detach device on inactive domain")); |
1896 | + goto cleanup; |
1897 | + } |
1898 | + |
1899 | + dev = virDomainDeviceDefParse(driver->caps, vm->def, xml, |
1900 | + VIR_DOMAIN_XML_INACTIVE); |
1901 | + if (dev == NULL) |
1902 | + goto cleanup; |
1903 | + |
1904 | + if (dev->type == VIR_DOMAIN_DEVICE_DISK && |
1905 | + dev->data.disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) { |
1906 | + if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_UML) |
1907 | + ret = umlDomainDetachUmlDisk(driver, vm, dev); |
1908 | + else { |
1909 | + umlReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", |
1910 | + _("This type of disk cannot be hot unplugged")); |
1911 | + } |
1912 | + } else { |
1913 | + umlReportError(VIR_ERR_CONFIG_UNSUPPORTED, |
1914 | + "%s", _("This type of device cannot be hot unplugged")); |
1915 | + } |
1916 | + |
1917 | +cleanup: |
1918 | + virDomainDeviceDefFree(dev); |
1919 | + if (vm) |
1920 | + virDomainObjUnlock(vm); |
1921 | + umlDriverUnlock(driver); |
1922 | + return ret; |
1923 | +} |
1924 | + |
1925 | + |
1926 | +static int umlDomainDetachDeviceFlags(virDomainPtr dom, |
1927 | + const char *xml, |
1928 | + unsigned int flags) { |
1929 | + if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) { |
1930 | + umlReportError(VIR_ERR_OPERATION_INVALID, |
1931 | + "%s", _("cannot modify the persistent configuration of a domain")); |
1932 | + return -1; |
1933 | + } |
1934 | + |
1935 | + return umlDomainDetachDevice(dom, xml); |
1936 | +} |
1937 | + |
1938 | + |
1939 | +static int umlDomainGetAutostart(virDomainPtr dom, |
1940 | + int *autostart) { |
1941 | + struct uml_driver *driver = dom->conn->privateData; |
1942 | + virDomainObjPtr vm; |
1943 | + int ret = -1; |
1944 | + |
1945 | + umlDriverLock(driver); |
1946 | + vm = virDomainFindByUUID(&driver->domains, dom->uuid); |
1947 | + |
1948 | + if (!vm) { |
1949 | + umlReportError(VIR_ERR_INVALID_DOMAIN, "%s", |
1950 | + _("no domain with matching uuid")); |
1951 | + goto cleanup; |
1952 | + } |
1953 | + |
1954 | + *autostart = vm->autostart; |
1955 | + ret = 0; |
1956 | + |
1957 | +cleanup: |
1958 | + if (vm) |
1959 | + virDomainObjUnlock(vm); |
1960 | + umlDriverUnlock(driver); |
1961 | + return ret; |
1962 | +} |
1963 | + |
1964 | +static int umlDomainSetAutostart(virDomainPtr dom, |
1965 | + int autostart) { |
1966 | + struct uml_driver *driver = dom->conn->privateData; |
1967 | + virDomainObjPtr vm; |
1968 | + char *configFile = NULL, *autostartLink = NULL; |
1969 | + int ret = -1; |
1970 | + |
1971 | + umlDriverLock(driver); |
1972 | + vm = virDomainFindByUUID(&driver->domains, dom->uuid); |
1973 | + |
1974 | + if (!vm) { |
1975 | + umlReportError(VIR_ERR_INVALID_DOMAIN, "%s", |
1976 | + _("no domain with matching uuid")); |
1977 | + goto cleanup; |
1978 | + } |
1979 | + |
1980 | + if (!vm->persistent) { |
1981 | + umlReportError(VIR_ERR_INTERNAL_ERROR, "%s", |
1982 | + _("cannot set autostart for transient domain")); |
1983 | + goto cleanup; |
1984 | + } |
1985 | + |
1986 | + autostart = (autostart != 0); |
1987 | + |
1988 | + if (vm->autostart != autostart) { |
1989 | + if ((configFile = virDomainConfigFile(driver->configDir, vm->def->name)) == NULL) |
1990 | + goto cleanup; |
1991 | + if ((autostartLink = virDomainConfigFile(driver->autostartDir, vm->def->name)) == NULL) |
1992 | + goto cleanup; |
1993 | + |
1994 | + if (autostart) { |
1995 | + int err; |
1996 | + |
1997 | + if ((err = virFileMakePath(driver->autostartDir))) { |
1998 | + virReportSystemError(err, |
1999 | + _("cannot create autostart directory %s"), |
2000 | + driver->autostartDir); |
2001 | + goto cleanup; |
2002 | + } |
2003 | + |
2004 | + if (symlink(configFile, autostartLink) < 0) { |
2005 | + virReportSystemError(errno, |
2006 | + _("Failed to create symlink '%s to '%s'"), |
2007 | + autostartLink, configFile); |
2008 | + goto cleanup; |
2009 | + } |
2010 | + } else { |
2011 | + if (unlink(autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) { |
2012 | + virReportSystemError(errno, |
2013 | + _("Failed to delete symlink '%s'"), |
2014 | + autostartLink); |
2015 | + goto cleanup; |
2016 | + } |
2017 | + } |
2018 | + |
2019 | + vm->autostart = autostart; |
2020 | + } |
2021 | + ret = 0; |
2022 | + |
2023 | +cleanup: |
2024 | + VIR_FREE(configFile); |
2025 | + VIR_FREE(autostartLink); |
2026 | + if (vm) |
2027 | + virDomainObjUnlock(vm); |
2028 | + umlDriverUnlock(driver); |
2029 | + return ret; |
2030 | +} |
2031 | + |
2032 | + |
2033 | +static int |
2034 | +umlDomainBlockPeek (virDomainPtr dom, |
2035 | + const char *path, |
2036 | + unsigned long long offset, size_t size, |
2037 | + void *buffer, |
2038 | + unsigned int flags ATTRIBUTE_UNUSED) |
2039 | +{ |
2040 | + struct uml_driver *driver = dom->conn->privateData; |
2041 | + virDomainObjPtr vm; |
2042 | + int fd = -1, ret = -1, i; |
2043 | + |
2044 | + umlDriverLock(driver); |
2045 | + vm = virDomainFindByUUID(&driver->domains, dom->uuid); |
2046 | + umlDriverUnlock(driver); |
2047 | + |
2048 | + if (!vm) { |
2049 | + umlReportError(VIR_ERR_INVALID_DOMAIN, "%s", |
2050 | + _("no domain with matching uuid")); |
2051 | + goto cleanup; |
2052 | + } |
2053 | + |
2054 | + if (!path || path[0] == '\0') { |
2055 | + umlReportError(VIR_ERR_INVALID_ARG, "%s", |
2056 | + _("NULL or empty path")); |
2057 | + goto cleanup; |
2058 | + } |
2059 | + |
2060 | + /* Check the path belongs to this domain. */ |
2061 | + for (i = 0 ; i < vm->def->ndisks ; i++) { |
2062 | + if (vm->def->disks[i]->src != NULL && |
2063 | + STREQ (vm->def->disks[i]->src, path)) { |
2064 | + ret = 0; |
2065 | + break; |
2066 | + } |
2067 | + } |
2068 | + |
2069 | + if (ret == 0) { |
2070 | + ret = -1; |
2071 | + /* The path is correct, now try to open it and get its size. */ |
2072 | + fd = open (path, O_RDONLY); |
2073 | + if (fd == -1) { |
2074 | + virReportSystemError(errno, |
2075 | + _("cannot open %s"), path); |
2076 | + goto cleanup; |
2077 | + } |
2078 | + |
2079 | + /* Seek and read. */ |
2080 | + /* NB. Because we configure with AC_SYS_LARGEFILE, off_t should |
2081 | + * be 64 bits on all platforms. |
2082 | + */ |
2083 | + if (lseek (fd, offset, SEEK_SET) == (off_t) -1 || |
2084 | + saferead (fd, buffer, size) == (ssize_t) -1) { |
2085 | + virReportSystemError(errno, |
2086 | + _("cannot read %s"), path); |
2087 | + goto cleanup; |
2088 | + } |
2089 | + |
2090 | + ret = 0; |
2091 | + } else { |
2092 | + umlReportError(VIR_ERR_INVALID_ARG, "%s", |
2093 | + _("invalid path")); |
2094 | + } |
2095 | + |
2096 | +cleanup: |
2097 | + if (fd >= 0) close (fd); |
2098 | + if (vm) |
2099 | + virDomainObjUnlock(vm); |
2100 | + return ret; |
2101 | +} |
2102 | + |
2103 | + |
2104 | + |
2105 | +static virDriver umlDriver = { |
2106 | + VIR_DRV_UML, |
2107 | + "UML", |
2108 | + umlOpen, /* open */ |
2109 | + umlClose, /* close */ |
2110 | + NULL, /* supports_feature */ |
2111 | + umlGetType, /* type */ |
2112 | + umlGetVersion, /* version */ |
2113 | + NULL, /* libvirtVersion (impl. in libvirt.c) */ |
2114 | + virGetHostname, /* getHostname */ |
2115 | + NULL, /* getMaxVcpus */ |
2116 | + nodeGetInfo, /* nodeGetInfo */ |
2117 | + umlGetCapabilities, /* getCapabilities */ |
2118 | + umlListDomains, /* listDomains */ |
2119 | + umlNumDomains, /* numOfDomains */ |
2120 | + umlDomainCreate, /* domainCreateXML */ |
2121 | + umlDomainLookupByID, /* domainLookupByID */ |
2122 | + umlDomainLookupByUUID, /* domainLookupByUUID */ |
2123 | + umlDomainLookupByName, /* domainLookupByName */ |
2124 | + NULL, /* domainSuspend */ |
2125 | + NULL, /* domainResume */ |
2126 | + umlDomainShutdown, /* domainShutdown */ |
2127 | + NULL, /* domainReboot */ |
2128 | + umlDomainDestroy, /* domainDestroy */ |
2129 | + umlDomainGetOSType, /* domainGetOSType */ |
2130 | + umlDomainGetMaxMemory, /* domainGetMaxMemory */ |
2131 | + umlDomainSetMaxMemory, /* domainSetMaxMemory */ |
2132 | + umlDomainSetMemory, /* domainSetMemory */ |
2133 | + umlDomainGetInfo, /* domainGetInfo */ |
2134 | + NULL, /* domainSave */ |
2135 | + NULL, /* domainRestore */ |
2136 | + NULL, /* domainCoreDump */ |
2137 | + NULL, /* domainSetVcpus */ |
2138 | + NULL, /* domainPinVcpu */ |
2139 | + NULL, /* domainGetVcpus */ |
2140 | + NULL, /* domainGetMaxVcpus */ |
2141 | + NULL, /* domainGetSecurityLabel */ |
2142 | + NULL, /* nodeGetSecurityModel */ |
2143 | + umlDomainDumpXML, /* domainDumpXML */ |
2144 | + NULL, /* domainXMLFromNative */ |
2145 | + NULL, /* domainXMLToNative */ |
2146 | + umlListDefinedDomains, /* listDefinedDomains */ |
2147 | + umlNumDefinedDomains, /* numOfDefinedDomains */ |
2148 | + umlDomainStart, /* domainCreate */ |
2149 | + umlDomainStartWithFlags, /* domainCreateWithFlags */ |
2150 | + umlDomainDefine, /* domainDefineXML */ |
2151 | + umlDomainUndefine, /* domainUndefine */ |
2152 | + umlDomainAttachDevice, /* domainAttachDevice */ |
2153 | + umlDomainAttachDeviceFlags, /* domainAttachDeviceFlags */ |
2154 | + umlDomainDetachDevice, /* domainDetachDevice */ |
2155 | + umlDomainDetachDeviceFlags, /* domainDetachDeviceFlags */ |
2156 | + NULL, /* domainUpdateDeviceFlags */ |
2157 | + umlDomainGetAutostart, /* domainGetAutostart */ |
2158 | + umlDomainSetAutostart, /* domainSetAutostart */ |
2159 | + NULL, /* domainGetSchedulerType */ |
2160 | + NULL, /* domainGetSchedulerParameters */ |
2161 | + NULL, /* domainSetSchedulerParameters */ |
2162 | + NULL, /* domainMigratePrepare */ |
2163 | + NULL, /* domainMigratePerform */ |
2164 | + NULL, /* domainMigrateFinish */ |
2165 | + NULL, /* domainBlockStats */ |
2166 | + NULL, /* domainInterfaceStats */ |
2167 | + NULL, /* domainMemoryStats */ |
2168 | + umlDomainBlockPeek, /* domainBlockPeek */ |
2169 | + NULL, /* domainMemoryPeek */ |
2170 | + NULL, /* domainGetBlockInfo */ |
2171 | + nodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ |
2172 | + nodeGetFreeMemory, /* getFreeMemory */ |
2173 | + NULL, /* domainEventRegister */ |
2174 | + NULL, /* domainEventDeregister */ |
2175 | + NULL, /* domainMigratePrepare2 */ |
2176 | + NULL, /* domainMigrateFinish2 */ |
2177 | + NULL, /* nodeDeviceDettach */ |
2178 | + NULL, /* nodeDeviceReAttach */ |
2179 | + NULL, /* nodeDeviceReset */ |
2180 | + NULL, /* domainMigratePrepareTunnel */ |
2181 | + umlIsEncrypted, /* isEncrypted */ |
2182 | + umlIsSecure, /* isSecure */ |
2183 | + umlDomainIsActive, /* domainIsActive */ |
2184 | + umlDomainIsPersistent, /* domainIsPersistent */ |
2185 | + NULL, /* cpuCompare */ |
2186 | + NULL, /* cpuBaseline */ |
2187 | + NULL, /* domainGetJobInfo */ |
2188 | + NULL, /* domainAbortJob */ |
2189 | + NULL, /* domainMigrateSetMaxDowntime */ |
2190 | + NULL, /* domainEventRegisterAny */ |
2191 | + NULL, /* domainEventDeregisterAny */ |
2192 | + NULL, /* domainManagedSave */ |
2193 | + NULL, /* domainHasManagedSaveImage */ |
2194 | + NULL, /* domainManagedSaveRemove */ |
2195 | + NULL, /* domainSnapshotCreateXML */ |
2196 | + NULL, /* domainSnapshotDumpXML */ |
2197 | + NULL, /* domainSnapshotNum */ |
2198 | + NULL, /* domainSnapshotListNames */ |
2199 | + NULL, /* domainSnapshotLookupByName */ |
2200 | + NULL, /* domainHasCurrentSnapshot */ |
2201 | + NULL, /* domainSnapshotCurrent */ |
2202 | + NULL, /* domainRevertToSnapshot */ |
2203 | + NULL, /* domainSnapshotDelete */ |
2204 | + NULL, /* qemuDomainMonitorCommand */ |
2205 | +}; |
2206 | + |
2207 | + |
2208 | +static virStateDriver umlStateDriver = { |
2209 | + .name = "UML", |
2210 | + .initialize = umlStartup, |
2211 | + .cleanup = umlShutdown, |
2212 | + .reload = umlReload, |
2213 | + .active = umlActive, |
2214 | +}; |
2215 | + |
2216 | +int umlRegister(void) { |
2217 | + virRegisterDriver(¨Driver); |
2218 | + virRegisterStateDriver(¨StateDriver); |
2219 | + return 0; |
2220 | +} |
2221 | |
2222 | === added file 'debian/patches/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch' |
2223 | --- debian/patches/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch 1970-01-01 00:00:00 +0000 |
2224 | +++ debian/patches/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch 2010-09-15 23:50:58 +0000 |
2225 | @@ -0,0 +1,55 @@ |
2226 | +From daa597d7b0a147ae8942390345b90f444185e9df Mon Sep 17 00:00:00 2001 |
2227 | +From: Soren Hansen <soren@linux2go.dk> |
2228 | +Date: Mon, 13 Sep 2010 23:25:53 +0200 |
2229 | +Subject: [PATCH] Rebuild network filter for UML guests on updates |
2230 | + |
2231 | +When nwfilter support was added to UML, I didn't realise the UML driver |
2232 | +needed instrumentation to make updating nwfilters on the fly work. This |
2233 | +patch adds this bit of glue. |
2234 | + |
2235 | +Signed-off-by: Soren Hansen <soren@linux2go.dk> |
2236 | +--- |
2237 | + src/uml/uml_driver.c | 18 ++++++++++++++++++ |
2238 | + 1 files changed, 18 insertions(+), 0 deletions(-) |
2239 | + |
2240 | +diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c |
2241 | +index 40345d5..9101928 100644 |
2242 | +--- a/src/uml/uml_driver.c |
2243 | ++++ b/src/uml/uml_driver.c |
2244 | +@@ -2198,6 +2198,18 @@ static virDriver umlDriver = { |
2245 | + NULL, /* qemuDomainMonitorCommand */ |
2246 | + }; |
2247 | + |
2248 | ++static int |
2249 | ++umlVMFilterRebuild(virConnectPtr conn ATTRIBUTE_UNUSED, |
2250 | ++ virHashIterator iter, void *data) |
2251 | ++{ |
2252 | ++ struct uml_driver *driver = uml_driver; |
2253 | ++ |
2254 | ++ umlDriverLock(driver); |
2255 | ++ virHashForEach(uml_driver->domains.objs, iter, data); |
2256 | ++ umlDriverUnlock(driver); |
2257 | ++ |
2258 | ++ return 0; |
2259 | ++} |
2260 | + |
2261 | + static virStateDriver umlStateDriver = { |
2262 | + .name = "UML", |
2263 | +@@ -2207,8 +2219,14 @@ static virStateDriver umlStateDriver = { |
2264 | + .active = umlActive, |
2265 | + }; |
2266 | + |
2267 | ++static virNWFilterCallbackDriver umlCallbackDriver = { |
2268 | ++ .name = "UML", |
2269 | ++ .vmFilterRebuild = umlVMFilterRebuild, |
2270 | ++}; |
2271 | ++ |
2272 | + int umlRegister(void) { |
2273 | + virRegisterDriver(¨Driver); |
2274 | + virRegisterStateDriver(¨StateDriver); |
2275 | ++ virNWFilterRegisterCallbackDriver(¨CallbackDriver); |
2276 | + return 0; |
2277 | + } |
2278 | +-- |
2279 | +1.7.1 |
2280 | + |
2281 | |
2282 | === renamed file 'debian/patches/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch' => 'debian/patches/9026-Rebuild-network-filter-for-UML-guests-on-updates.patch.moved' |