1 /*
2 * virdomainobjlist.c: domain objects list utilities
3 *
4 * Copyright (C) 2006-2015 Red Hat, Inc.
5 * Copyright (C) 2006-2008 Daniel P. Berrange
6 * Copyright (c) 2015 SUSE LINUX Products GmbH, Nuernberg, Germany.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library. If not, see
20 * <http://www.gnu.org/licenses/>.
21 */
22
23 #include <config.h>
24
25 #include "internal.h"
26 #include "datatypes.h"
27 #include "virdomainobjlist.h"
28 #include "checkpoint_conf.h"
29 #include "snapshot_conf.h"
30 #include "viralloc.h"
31 #include "virfile.h"
32 #include "virlog.h"
33 #include "virstring.h"
34 #include "virdomainsnapshotobjlist.h"
35 #include "virdomaincheckpointobjlist.h"
36
37 #define VIR_FROM_THIS VIR_FROM_DOMAIN
38
39 VIR_LOG_INIT("conf.virdomainobjlist");
40
41 static virClass *virDomainObjListClass;
42 static void virDomainObjListDispose(void *obj);
43
44
45 struct _virDomainObjList {
46 virObjectRWLockable parent;
47
48 /* uuid string -> virDomainObj mapping
49 * for O(1), lockless lookup-by-uuid */
50 GHashTable *objs;
51
52 /* name -> virDomainObj mapping for O(1),
53 * lockless lookup-by-name */
54 GHashTable *objsName;
55 };
56
57
virDomainObjListOnceInit(void)58 static int virDomainObjListOnceInit(void)
59 {
60 if (!VIR_CLASS_NEW(virDomainObjList, virClassForObjectRWLockable()))
61 return -1;
62
63 return 0;
64 }
65
66 VIR_ONCE_GLOBAL_INIT(virDomainObjList);
67
virDomainObjListNew(void)68 virDomainObjList *virDomainObjListNew(void)
69 {
70 virDomainObjList *doms;
71
72 if (virDomainObjListInitialize() < 0)
73 return NULL;
74
75 if (!(doms = virObjectRWLockableNew(virDomainObjListClass)))
76 return NULL;
77
78 doms->objs = virHashNew(virObjectFreeHashData);
79 doms->objsName = virHashNew(virObjectFreeHashData);
80 return doms;
81 }
82
83
virDomainObjListDispose(void * obj)84 static void virDomainObjListDispose(void *obj)
85 {
86 virDomainObjList *doms = obj;
87
88 virHashFree(doms->objs);
89 virHashFree(doms->objsName);
90 }
91
92
virDomainObjListSearchID(const void * payload,const char * name G_GNUC_UNUSED,const void * data)93 static int virDomainObjListSearchID(const void *payload,
94 const char *name G_GNUC_UNUSED,
95 const void *data)
96 {
97 virDomainObj *obj = (virDomainObj *)payload;
98 const int *id = data;
99 int want = 0;
100
101 virObjectLock(obj);
102 if (virDomainObjIsActive(obj) &&
103 obj->def->id == *id)
104 want = 1;
105 virObjectUnlock(obj);
106 return want;
107 }
108
109
110 virDomainObj *
virDomainObjListFindByID(virDomainObjList * doms,int id)111 virDomainObjListFindByID(virDomainObjList *doms,
112 int id)
113 {
114 virDomainObj *obj;
115
116 virObjectRWLockRead(doms);
117 obj = virHashSearch(doms->objs, virDomainObjListSearchID, &id, NULL);
118 virObjectRef(obj);
119 virObjectRWUnlock(doms);
120 if (obj) {
121 virObjectLock(obj);
122 if (obj->removing)
123 virDomainObjEndAPI(&obj);
124 }
125
126 return obj;
127 }
128
129
130 static virDomainObj *
virDomainObjListFindByUUIDLocked(virDomainObjList * doms,const unsigned char * uuid)131 virDomainObjListFindByUUIDLocked(virDomainObjList *doms,
132 const unsigned char *uuid)
133 {
134 char uuidstr[VIR_UUID_STRING_BUFLEN];
135 virDomainObj *obj;
136
137 virUUIDFormat(uuid, uuidstr);
138 obj = virHashLookup(doms->objs, uuidstr);
139 if (obj) {
140 virObjectRef(obj);
141 virObjectLock(obj);
142 }
143 return obj;
144 }
145
146
147 /**
148 * @doms: Domain object list
149 * @uuid: UUID to search the doms->objs table
150 *
151 * Lookup the @uuid in the doms->objs hash table and return a
152 * locked and ref counted domain object if found. Caller is
153 * expected to use the virDomainObjEndAPI when done with the object.
154 */
155 virDomainObj *
virDomainObjListFindByUUID(virDomainObjList * doms,const unsigned char * uuid)156 virDomainObjListFindByUUID(virDomainObjList *doms,
157 const unsigned char *uuid)
158 {
159 virDomainObj *obj;
160
161 virObjectRWLockRead(doms);
162 obj = virDomainObjListFindByUUIDLocked(doms, uuid);
163 virObjectRWUnlock(doms);
164
165 if (obj && obj->removing)
166 virDomainObjEndAPI(&obj);
167
168 return obj;
169 }
170
171
172 static virDomainObj *
virDomainObjListFindByNameLocked(virDomainObjList * doms,const char * name)173 virDomainObjListFindByNameLocked(virDomainObjList *doms,
174 const char *name)
175 {
176 virDomainObj *obj;
177
178 obj = virHashLookup(doms->objsName, name);
179 if (obj) {
180 virObjectRef(obj);
181 virObjectLock(obj);
182 }
183 return obj;
184 }
185
186
187 /**
188 * @doms: Domain object list
189 * @name: Name to search the doms->objsName table
190 *
191 * Lookup the @name in the doms->objsName hash table and return a
192 * locked and ref counted domain object if found. Caller is expected
193 * to use the virDomainObjEndAPI when done with the object.
194 */
195 virDomainObj *
virDomainObjListFindByName(virDomainObjList * doms,const char * name)196 virDomainObjListFindByName(virDomainObjList *doms,
197 const char *name)
198 {
199 virDomainObj *obj;
200
201 virObjectRWLockRead(doms);
202 obj = virDomainObjListFindByNameLocked(doms, name);
203 virObjectRWUnlock(doms);
204
205 if (obj && obj->removing)
206 virDomainObjEndAPI(&obj);
207
208 return obj;
209 }
210
211
212 /**
213 * @doms: Domain object list pointer
214 * @vm: Domain object to be added
215 *
216 * Upon entry @vm should have at least 1 ref and be locked.
217 *
218 * Add the @vm into the @doms->objs and @doms->objsName hash
219 * tables. Once successfully added into a table, increase the
220 * reference count since upon removal in virHashRemoveEntry
221 * the virObjectUnref will be called since the hash tables were
222 * configured to call virObjectFreeHashData when the object is
223 * removed from the hash table.
224 *
225 * Returns 0 on success with 3 references and locked
226 * -1 on failure with 1 reference and locked
227 */
228 static int
virDomainObjListAddObjLocked(virDomainObjList * doms,virDomainObj * vm)229 virDomainObjListAddObjLocked(virDomainObjList *doms,
230 virDomainObj *vm)
231 {
232 char uuidstr[VIR_UUID_STRING_BUFLEN];
233
234 virUUIDFormat(vm->def->uuid, uuidstr);
235 if (virHashAddEntry(doms->objs, uuidstr, vm) < 0)
236 return -1;
237 virObjectRef(vm);
238
239 if (virHashAddEntry(doms->objsName, vm->def->name, vm) < 0) {
240 virHashRemoveEntry(doms->objs, uuidstr);
241 return -1;
242 }
243 virObjectRef(vm);
244
245 return 0;
246 }
247
248
249 /*
250 * virDomainObjListAddLocked:
251 *
252 * If flags & VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE then
253 * this will refuse updating an existing def if the
254 * current def is Live
255 *
256 * If flags & VIR_DOMAIN_OBJ_LIST_ADD_LIVE then
257 * the @def being added is assumed to represent a
258 * live config, not a future inactive config
259 *
260 * Upon successful return the virDomain object is the owner of
261 * @def and callers should use @vm->def if they need to access
262 * the definition as @def is set to NULL.
263 *
264 * The returned @vm from this function will be locked and ref
265 * counted. The caller is expected to use virDomainObjEndAPI
266 * when it completes usage.
267 */
268 static virDomainObj *
virDomainObjListAddLocked(virDomainObjList * doms,virDomainDef ** def,virDomainXMLOption * xmlopt,unsigned int flags,virDomainDef ** oldDef)269 virDomainObjListAddLocked(virDomainObjList *doms,
270 virDomainDef **def,
271 virDomainXMLOption *xmlopt,
272 unsigned int flags,
273 virDomainDef **oldDef)
274 {
275 virDomainObj *vm;
276 char uuidstr[VIR_UUID_STRING_BUFLEN];
277
278 if (oldDef)
279 *oldDef = NULL;
280
281 /* See if a VM with matching UUID already exists */
282 if ((vm = virDomainObjListFindByUUIDLocked(doms, (*def)->uuid))) {
283 if (vm->removing) {
284 virReportError(VIR_ERR_OPERATION_FAILED,
285 _("domain '%s' is already being removed"),
286 vm->def->name);
287 goto error;
288 } else if (STRNEQ(vm->def->name, (*def)->name)) {
289 /* UUID matches, but if names don't match, refuse it */
290 virUUIDFormat(vm->def->uuid, uuidstr);
291 virReportError(VIR_ERR_OPERATION_FAILED,
292 _("domain '%s' is already defined with uuid %s"),
293 vm->def->name, uuidstr);
294 goto error;
295 }
296
297 if (flags & VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE) {
298 /* UUID & name match, but if VM is already active, refuse it */
299 if (virDomainObjIsActive(vm)) {
300 virReportError(VIR_ERR_OPERATION_INVALID,
301 _("domain '%s' is already active"),
302 vm->def->name);
303 goto error;
304 }
305 if (!vm->persistent) {
306 virReportError(VIR_ERR_OPERATION_INVALID,
307 _("domain '%s' is already being started"),
308 vm->def->name);
309 goto error;
310 }
311 }
312
313 virDomainObjAssignDef(vm,
314 def,
315 !!(flags & VIR_DOMAIN_OBJ_LIST_ADD_LIVE),
316 oldDef);
317 } else {
318 /* UUID does not match, but if a name matches, refuse it */
319 if ((vm = virDomainObjListFindByNameLocked(doms, (*def)->name))) {
320 virUUIDFormat(vm->def->uuid, uuidstr);
321 virReportError(VIR_ERR_OPERATION_FAILED,
322 _("domain '%s' already exists with uuid %s"),
323 (*def)->name, uuidstr);
324 goto error;
325 }
326
327 if (!(vm = virDomainObjNew(xmlopt)))
328 goto error;
329 vm->def = g_steal_pointer(def);
330
331 if (virDomainObjListAddObjLocked(doms, vm) < 0) {
332 *def = g_steal_pointer(&vm->def);
333 goto error;
334 }
335 }
336
337 return vm;
338
339 error:
340 virDomainObjEndAPI(&vm);
341 return NULL;
342 }
343
344
345 virDomainObj *
virDomainObjListAdd(virDomainObjList * doms,virDomainDef ** def,virDomainXMLOption * xmlopt,unsigned int flags,virDomainDef ** oldDef)346 virDomainObjListAdd(virDomainObjList *doms,
347 virDomainDef **def,
348 virDomainXMLOption *xmlopt,
349 unsigned int flags,
350 virDomainDef **oldDef)
351 {
352 virDomainObj *ret;
353
354 virObjectRWLockWrite(doms);
355 ret = virDomainObjListAddLocked(doms, def, xmlopt, flags, oldDef);
356 virObjectRWUnlock(doms);
357 return ret;
358 }
359
360
361 /* The caller must hold lock on 'doms' in addition to 'virDomainObjListRemove'
362 * requirements
363 *
364 * Can be used to remove current element while iterating with
365 * virDomainObjListForEach
366 */
367 void
virDomainObjListRemoveLocked(virDomainObjList * doms,virDomainObj * dom)368 virDomainObjListRemoveLocked(virDomainObjList *doms,
369 virDomainObj *dom)
370 {
371 char uuidstr[VIR_UUID_STRING_BUFLEN];
372
373 virUUIDFormat(dom->def->uuid, uuidstr);
374
375 virHashRemoveEntry(doms->objs, uuidstr);
376 virHashRemoveEntry(doms->objsName, dom->def->name);
377 }
378
379
380 /**
381 * @doms: Pointer to the domain object list
382 * @dom: Domain pointer from either after Add or FindBy* API where the
383 * @dom was successfully added to both the doms->objs and ->objsName
384 * hash tables that now would need to be removed.
385 *
386 * The caller must hold a lock on the driver owning 'doms',
387 * and must also have locked and ref counted 'dom', to ensure
388 * no one else is either waiting for 'dom' or still using it.
389 *
390 * When this function returns, @dom will be removed from the hash
391 * tables and returned with lock and refcnt that was present upon entry.
392 */
393 void
virDomainObjListRemove(virDomainObjList * doms,virDomainObj * dom)394 virDomainObjListRemove(virDomainObjList *doms,
395 virDomainObj *dom)
396 {
397 dom->removing = true;
398 virObjectRef(dom);
399 virObjectUnlock(dom);
400 virObjectRWLockWrite(doms);
401 virObjectLock(dom);
402 virDomainObjListRemoveLocked(doms, dom);
403 virObjectUnref(dom);
404 virObjectRWUnlock(doms);
405 }
406
407
408 /**
409 * virDomainObjListRename:
410 *
411 * The caller must hold a lock on dom. Callbacks should not
412 * sleep/wait otherwise operations on all domains will be blocked
413 * as the callback is called with domains lock hold. Domain lock
414 * is dropped/reacquired during this operation thus domain
415 * consistency must not rely on this lock solely.
416 */
417 int
virDomainObjListRename(virDomainObjList * doms,virDomainObj * dom,const char * new_name,unsigned int flags,virDomainObjListRenameCallback callback,void * opaque)418 virDomainObjListRename(virDomainObjList *doms,
419 virDomainObj *dom,
420 const char *new_name,
421 unsigned int flags,
422 virDomainObjListRenameCallback callback,
423 void *opaque)
424 {
425 int ret = -1;
426 char *old_name = NULL;
427 int rc;
428
429 if (STREQ(dom->def->name, new_name)) {
430 virReportError(VIR_ERR_OPERATION_INVALID, "%s",
431 _("Can't rename domain to itself"));
432 return ret;
433 }
434
435 old_name = g_strdup(dom->def->name);
436
437 /* doms and dom locks must be attained in right order thus relock dom. */
438 /* dom reference is touched for the benefit of those callers that
439 * hold a lock on dom but not refcount it. */
440 virObjectRef(dom);
441 virObjectUnlock(dom);
442 virObjectRWLockWrite(doms);
443 virObjectLock(dom);
444 virObjectUnref(dom);
445
446 if (virHashLookup(doms->objsName, new_name) != NULL) {
447 virReportError(VIR_ERR_OPERATION_INVALID,
448 _("domain with name '%s' already exists"),
449 new_name);
450 goto cleanup;
451 }
452
453 if (virHashAddEntry(doms->objsName, new_name, dom) < 0)
454 goto cleanup;
455
456 /* Increment the refcnt for @new_name. We're about to remove
457 * the @old_name which will cause the refcnt to be decremented
458 * via the virObjectUnref call made during the virObjectFreeHashData
459 * as a result of removing something from the object list hash
460 * table as set up during virDomainObjListNew. */
461 virObjectRef(dom);
462
463 rc = callback(dom, new_name, flags, opaque);
464 virHashRemoveEntry(doms->objsName, rc < 0 ? new_name : old_name);
465 if (rc < 0)
466 goto cleanup;
467
468 ret = 0;
469 cleanup:
470 virObjectRWUnlock(doms);
471 VIR_FREE(old_name);
472 return ret;
473 }
474
475
476 static virDomainObj *
virDomainObjListLoadConfig(virDomainObjList * doms,virDomainXMLOption * xmlopt,const char * configDir,const char * autostartDir,const char * name,virDomainLoadConfigNotify notify,void * opaque)477 virDomainObjListLoadConfig(virDomainObjList *doms,
478 virDomainXMLOption *xmlopt,
479 const char *configDir,
480 const char *autostartDir,
481 const char *name,
482 virDomainLoadConfigNotify notify,
483 void *opaque)
484 {
485 char *configFile = NULL, *autostartLink = NULL;
486 virDomainDef *def = NULL;
487 virDomainObj *dom;
488 int autostart;
489 virDomainDef *oldDef = NULL;
490
491 if ((configFile = virDomainConfigFile(configDir, name)) == NULL)
492 goto error;
493 if (!(def = virDomainDefParseFile(configFile, xmlopt, NULL,
494 VIR_DOMAIN_DEF_PARSE_INACTIVE |
495 VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE |
496 VIR_DOMAIN_DEF_PARSE_ALLOW_POST_PARSE_FAIL)))
497 goto error;
498
499 if ((autostartLink = virDomainConfigFile(autostartDir, name)) == NULL)
500 goto error;
501
502 if ((autostart = virFileLinkPointsTo(autostartLink, configFile)) < 0)
503 goto error;
504
505 if (!(dom = virDomainObjListAddLocked(doms, &def, xmlopt, 0, &oldDef)))
506 goto error;
507
508 dom->autostart = autostart;
509
510 if (notify)
511 (*notify)(dom, oldDef == NULL, opaque);
512
513 virDomainDefFree(oldDef);
514 VIR_FREE(configFile);
515 VIR_FREE(autostartLink);
516 return dom;
517
518 error:
519 VIR_FREE(configFile);
520 VIR_FREE(autostartLink);
521 virDomainDefFree(def);
522 return NULL;
523 }
524
525
526 static virDomainObj *
virDomainObjListLoadStatus(virDomainObjList * doms,const char * statusDir,const char * name,virDomainXMLOption * xmlopt,virDomainLoadConfigNotify notify,void * opaque)527 virDomainObjListLoadStatus(virDomainObjList *doms,
528 const char *statusDir,
529 const char *name,
530 virDomainXMLOption *xmlopt,
531 virDomainLoadConfigNotify notify,
532 void *opaque)
533 {
534 char *statusFile = NULL;
535 virDomainObj *obj = NULL;
536 char uuidstr[VIR_UUID_STRING_BUFLEN];
537
538 if ((statusFile = virDomainConfigFile(statusDir, name)) == NULL)
539 goto error;
540
541 if (!(obj = virDomainObjParseFile(statusFile, xmlopt,
542 VIR_DOMAIN_DEF_PARSE_STATUS |
543 VIR_DOMAIN_DEF_PARSE_ACTUAL_NET |
544 VIR_DOMAIN_DEF_PARSE_PCI_ORIG_STATES |
545 VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE |
546 VIR_DOMAIN_DEF_PARSE_ALLOW_POST_PARSE_FAIL)))
547 goto error;
548
549 virUUIDFormat(obj->def->uuid, uuidstr);
550
551 if (virHashLookup(doms->objs, uuidstr) != NULL) {
552 virReportError(VIR_ERR_INTERNAL_ERROR,
553 _("unexpected domain %s already exists"),
554 obj->def->name);
555 goto error;
556 }
557
558 if (virDomainObjListAddObjLocked(doms, obj) < 0)
559 goto error;
560
561 if (notify)
562 (*notify)(obj, 1, opaque);
563
564 VIR_FREE(statusFile);
565 return obj;
566
567 error:
568 virDomainObjEndAPI(&obj);
569 VIR_FREE(statusFile);
570 return NULL;
571 }
572
573
574 int
virDomainObjListLoadAllConfigs(virDomainObjList * doms,const char * configDir,const char * autostartDir,bool liveStatus,virDomainXMLOption * xmlopt,virDomainLoadConfigNotify notify,void * opaque)575 virDomainObjListLoadAllConfigs(virDomainObjList *doms,
576 const char *configDir,
577 const char *autostartDir,
578 bool liveStatus,
579 virDomainXMLOption *xmlopt,
580 virDomainLoadConfigNotify notify,
581 void *opaque)
582 {
583 g_autoptr(DIR) dir = NULL;
584 struct dirent *entry;
585 int ret = -1;
586 int rc;
587
588 VIR_INFO("Scanning for configs in %s", configDir);
589
590 if ((rc = virDirOpenIfExists(&dir, configDir)) <= 0)
591 return rc;
592
593 virObjectRWLockWrite(doms);
594
595 while ((ret = virDirRead(dir, &entry, configDir)) > 0) {
596 virDomainObj *dom;
597
598 if (!virStringStripSuffix(entry->d_name, ".xml"))
599 continue;
600
601 /* NB: ignoring errors, so one malformed config doesn't
602 kill the whole process */
603 VIR_INFO("Loading config file '%s.xml'", entry->d_name);
604 if (liveStatus)
605 dom = virDomainObjListLoadStatus(doms,
606 configDir,
607 entry->d_name,
608 xmlopt,
609 notify,
610 opaque);
611 else
612 dom = virDomainObjListLoadConfig(doms,
613 xmlopt,
614 configDir,
615 autostartDir,
616 entry->d_name,
617 notify,
618 opaque);
619 if (dom) {
620 if (!liveStatus)
621 dom->persistent = 1;
622 virDomainObjEndAPI(&dom);
623 } else {
624 VIR_ERROR(_("Failed to load config for domain '%s'"), entry->d_name);
625 }
626 }
627
628 virObjectRWUnlock(doms);
629 return ret;
630 }
631
632
633 struct virDomainObjListData {
634 virDomainObjListACLFilter filter;
635 virConnectPtr conn;
636 bool active;
637 int count;
638 };
639
640
641 static int
virDomainObjListCount(void * payload,const char * name G_GNUC_UNUSED,void * opaque)642 virDomainObjListCount(void *payload,
643 const char *name G_GNUC_UNUSED,
644 void *opaque)
645 {
646 virDomainObj *obj = payload;
647 struct virDomainObjListData *data = opaque;
648 virObjectLock(obj);
649 if (data->filter &&
650 !data->filter(data->conn, obj->def))
651 goto cleanup;
652 if (virDomainObjIsActive(obj)) {
653 if (data->active)
654 data->count++;
655 } else {
656 if (!data->active)
657 data->count++;
658 }
659 cleanup:
660 virObjectUnlock(obj);
661 return 0;
662 }
663
664
665 int
virDomainObjListNumOfDomains(virDomainObjList * doms,bool active,virDomainObjListACLFilter filter,virConnectPtr conn)666 virDomainObjListNumOfDomains(virDomainObjList *doms,
667 bool active,
668 virDomainObjListACLFilter filter,
669 virConnectPtr conn)
670 {
671 struct virDomainObjListData data = { filter, conn, active, 0 };
672 virObjectRWLockRead(doms);
673 virHashForEach(doms->objs, virDomainObjListCount, &data);
674 virObjectRWUnlock(doms);
675 return data.count;
676 }
677
678
679 struct virDomainIDData {
680 virDomainObjListACLFilter filter;
681 virConnectPtr conn;
682 int numids;
683 int maxids;
684 int *ids;
685 };
686
687
688 static int
virDomainObjListCopyActiveIDs(void * payload,const char * name G_GNUC_UNUSED,void * opaque)689 virDomainObjListCopyActiveIDs(void *payload,
690 const char *name G_GNUC_UNUSED,
691 void *opaque)
692 {
693 virDomainObj *obj = payload;
694 struct virDomainIDData *data = opaque;
695 virObjectLock(obj);
696 if (data->filter &&
697 !data->filter(data->conn, obj->def))
698 goto cleanup;
699 if (virDomainObjIsActive(obj) && data->numids < data->maxids)
700 data->ids[data->numids++] = obj->def->id;
701 cleanup:
702 virObjectUnlock(obj);
703 return 0;
704 }
705
706
707 int
virDomainObjListGetActiveIDs(virDomainObjList * doms,int * ids,int maxids,virDomainObjListACLFilter filter,virConnectPtr conn)708 virDomainObjListGetActiveIDs(virDomainObjList *doms,
709 int *ids,
710 int maxids,
711 virDomainObjListACLFilter filter,
712 virConnectPtr conn)
713 {
714 struct virDomainIDData data = { filter, conn,
715 0, maxids, ids };
716 virObjectRWLockRead(doms);
717 virHashForEach(doms->objs, virDomainObjListCopyActiveIDs, &data);
718 virObjectRWUnlock(doms);
719 return data.numids;
720 }
721
722
723 struct virDomainNameData {
724 virDomainObjListACLFilter filter;
725 virConnectPtr conn;
726 int oom;
727 int numnames;
728 int maxnames;
729 char **const names;
730 };
731
732
733 static int
virDomainObjListCopyInactiveNames(void * payload,const char * name G_GNUC_UNUSED,void * opaque)734 virDomainObjListCopyInactiveNames(void *payload,
735 const char *name G_GNUC_UNUSED,
736 void *opaque)
737 {
738 virDomainObj *obj = payload;
739 struct virDomainNameData *data = opaque;
740
741 if (data->oom)
742 return 0;
743
744 virObjectLock(obj);
745 if (data->filter &&
746 !data->filter(data->conn, obj->def))
747 goto cleanup;
748 if (!virDomainObjIsActive(obj) && data->numnames < data->maxnames) {
749 data->names[data->numnames] = g_strdup(obj->def->name);
750 data->numnames++;
751 }
752
753 cleanup:
754 virObjectUnlock(obj);
755 return 0;
756 }
757
758
759 int
virDomainObjListGetInactiveNames(virDomainObjList * doms,char ** const names,int maxnames,virDomainObjListACLFilter filter,virConnectPtr conn)760 virDomainObjListGetInactiveNames(virDomainObjList *doms,
761 char **const names,
762 int maxnames,
763 virDomainObjListACLFilter filter,
764 virConnectPtr conn)
765 {
766 struct virDomainNameData data = { filter, conn,
767 0, 0, maxnames, names };
768 size_t i;
769 virObjectRWLockRead(doms);
770 virHashForEach(doms->objs, virDomainObjListCopyInactiveNames, &data);
771 virObjectRWUnlock(doms);
772 if (data.oom) {
773 for (i = 0; i < data.numnames; i++)
774 VIR_FREE(data.names[i]);
775 return -1;
776 }
777
778 return data.numnames;
779 }
780
781
782 struct virDomainListIterData {
783 virDomainObjListIterator callback;
784 void *opaque;
785 int ret;
786 };
787
788
789 static int
virDomainObjListHelper(void * payload,const char * name G_GNUC_UNUSED,void * opaque)790 virDomainObjListHelper(void *payload,
791 const char *name G_GNUC_UNUSED,
792 void *opaque)
793 {
794 struct virDomainListIterData *data = opaque;
795
796 if (data->callback(payload, data->opaque) < 0)
797 data->ret = -1;
798 return 0;
799 }
800
801
802 /**
803 * virDomainObjListForEach:
804 * @doms: Pointer to the domain object list
805 * @modify: Whether to lock @doms for modify operation
806 * @callback: callback to run over each domain on the list
807 * @opaque: opaque data to pass to @callback
808 *
809 * For every domain on the list (@doms) run @callback on it. If
810 * @callback fails (i.e. returns a negative value), the iteration
811 * carries still on until all domains are visited. Moreover, if
812 * @callback wants to modify the list of domains (@doms) then
813 * @modify must be set to true.
814 *
815 * Returns: 0 on success,
816 * -1 otherwise.
817 */
818 int
virDomainObjListForEach(virDomainObjList * doms,bool modify,virDomainObjListIterator callback,void * opaque)819 virDomainObjListForEach(virDomainObjList *doms,
820 bool modify,
821 virDomainObjListIterator callback,
822 void *opaque)
823 {
824 struct virDomainListIterData data = {
825 callback, opaque, 0,
826 };
827
828 if (modify)
829 virObjectRWLockWrite(doms);
830 else
831 virObjectRWLockRead(doms);
832 virHashForEachSafe(doms->objs, virDomainObjListHelper, &data);
833 virObjectRWUnlock(doms);
834 return data.ret;
835 }
836
837
838 #define MATCH(FLAG) (filter & (FLAG))
839 static bool
virDomainObjMatchFilter(virDomainObj * vm,unsigned int filter)840 virDomainObjMatchFilter(virDomainObj *vm,
841 unsigned int filter)
842 {
843 /* filter by active state */
844 if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_ACTIVE) &&
845 !((MATCH(VIR_CONNECT_LIST_DOMAINS_ACTIVE) &&
846 virDomainObjIsActive(vm)) ||
847 (MATCH(VIR_CONNECT_LIST_DOMAINS_INACTIVE) &&
848 !virDomainObjIsActive(vm))))
849 return false;
850
851 /* filter by persistence */
852 if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_PERSISTENT) &&
853 !((MATCH(VIR_CONNECT_LIST_DOMAINS_PERSISTENT) &&
854 vm->persistent) ||
855 (MATCH(VIR_CONNECT_LIST_DOMAINS_TRANSIENT) &&
856 !vm->persistent)))
857 return false;
858
859 /* filter by domain state */
860 if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_STATE)) {
861 int st = virDomainObjGetState(vm, NULL);
862 if (!((MATCH(VIR_CONNECT_LIST_DOMAINS_RUNNING) &&
863 st == VIR_DOMAIN_RUNNING) ||
864 (MATCH(VIR_CONNECT_LIST_DOMAINS_PAUSED) &&
865 st == VIR_DOMAIN_PAUSED) ||
866 (MATCH(VIR_CONNECT_LIST_DOMAINS_SHUTOFF) &&
867 st == VIR_DOMAIN_SHUTOFF) ||
868 (MATCH(VIR_CONNECT_LIST_DOMAINS_OTHER) &&
869 (st != VIR_DOMAIN_RUNNING &&
870 st != VIR_DOMAIN_PAUSED &&
871 st != VIR_DOMAIN_SHUTOFF))))
872 return false;
873 }
874
875 /* filter by existence of managed save state */
876 if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_MANAGEDSAVE) &&
877 !((MATCH(VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE) &&
878 vm->hasManagedSave) ||
879 (MATCH(VIR_CONNECT_LIST_DOMAINS_NO_MANAGEDSAVE) &&
880 !vm->hasManagedSave)))
881 return false;
882
883 /* filter by autostart option */
884 if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_AUTOSTART) &&
885 !((MATCH(VIR_CONNECT_LIST_DOMAINS_AUTOSTART) && vm->autostart) ||
886 (MATCH(VIR_CONNECT_LIST_DOMAINS_NO_AUTOSTART) && !vm->autostart)))
887 return false;
888
889 /* filter by snapshot existence */
890 if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_SNAPSHOT)) {
891 int nsnap = virDomainSnapshotObjListNum(vm->snapshots, NULL, 0);
892 if (!((MATCH(VIR_CONNECT_LIST_DOMAINS_HAS_SNAPSHOT) && nsnap > 0) ||
893 (MATCH(VIR_CONNECT_LIST_DOMAINS_NO_SNAPSHOT) && nsnap <= 0)))
894 return false;
895 }
896
897 /* filter by checkpoint existence */
898 if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_CHECKPOINT)) {
899 int nchk = virDomainListCheckpoints(vm->checkpoints, NULL, NULL,
900 NULL, 0);
901 if (!((MATCH(VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT) && nchk > 0) ||
902 (MATCH(VIR_CONNECT_LIST_DOMAINS_NO_CHECKPOINT) && nchk <= 0)))
903 return false;
904 }
905
906 return true;
907 }
908 #undef MATCH
909
910
911 struct virDomainListData {
912 virDomainObj **vms;
913 size_t nvms;
914 };
915
916
917 static int
virDomainObjListCollectIterator(void * payload,const char * name G_GNUC_UNUSED,void * opaque)918 virDomainObjListCollectIterator(void *payload,
919 const char *name G_GNUC_UNUSED,
920 void *opaque)
921 {
922 struct virDomainListData *data = opaque;
923
924 data->vms[data->nvms++] = virObjectRef(payload);
925 return 0;
926 }
927
928
929 static void
virDomainObjListFilter(virDomainObj *** list,size_t * nvms,virConnectPtr conn,virDomainObjListACLFilter filter,unsigned int flags)930 virDomainObjListFilter(virDomainObj ***list,
931 size_t *nvms,
932 virConnectPtr conn,
933 virDomainObjListACLFilter filter,
934 unsigned int flags)
935 {
936 size_t i = 0;
937
938 while (i < *nvms) {
939 virDomainObj *vm = (*list)[i];
940
941 virObjectLock(vm);
942
943 /* do not list the object if:
944 * 1) it's being removed.
945 * 2) connection does not have ACL to see it
946 * 3) it doesn't match the filter
947 */
948 if (vm->removing ||
949 (filter && !filter(conn, vm->def)) ||
950 !virDomainObjMatchFilter(vm, flags)) {
951 virDomainObjEndAPI(&vm);
952 VIR_DELETE_ELEMENT(*list, i, *nvms);
953 continue;
954 }
955
956 virObjectUnlock(vm);
957 i++;
958 }
959 }
960
961
962 int
virDomainObjListCollect(virDomainObjList * domlist,virConnectPtr conn,virDomainObj *** vms,size_t * nvms,virDomainObjListACLFilter filter,unsigned int flags)963 virDomainObjListCollect(virDomainObjList *domlist,
964 virConnectPtr conn,
965 virDomainObj ***vms,
966 size_t *nvms,
967 virDomainObjListACLFilter filter,
968 unsigned int flags)
969 {
970 struct virDomainListData data = { NULL, 0 };
971
972 virObjectRWLockRead(domlist);
973 data.vms = g_new0(virDomainObj *, virHashSize(domlist->objs));
974
975 virHashForEach(domlist->objs, virDomainObjListCollectIterator, &data);
976 virObjectRWUnlock(domlist);
977
978 virDomainObjListFilter(&data.vms, &data.nvms, conn, filter, flags);
979
980 *nvms = data.nvms;
981 *vms = data.vms;
982
983 return 0;
984 }
985
986
987 int
virDomainObjListConvert(virDomainObjList * domlist,virConnectPtr conn,virDomainPtr * doms,size_t ndoms,virDomainObj *** vms,size_t * nvms,virDomainObjListACLFilter filter,unsigned int flags,bool skip_missing)988 virDomainObjListConvert(virDomainObjList *domlist,
989 virConnectPtr conn,
990 virDomainPtr *doms,
991 size_t ndoms,
992 virDomainObj ***vms,
993 size_t *nvms,
994 virDomainObjListACLFilter filter,
995 unsigned int flags,
996 bool skip_missing)
997 {
998 char uuidstr[VIR_UUID_STRING_BUFLEN];
999 virDomainObj *vm;
1000 size_t i;
1001
1002 *nvms = 0;
1003 *vms = NULL;
1004
1005 virObjectRWLockRead(domlist);
1006 for (i = 0; i < ndoms; i++) {
1007 virDomainPtr dom = doms[i];
1008
1009 virUUIDFormat(dom->uuid, uuidstr);
1010
1011 if (!(vm = virHashLookup(domlist->objs, uuidstr))) {
1012 if (skip_missing)
1013 continue;
1014
1015 virObjectRWUnlock(domlist);
1016 virReportError(VIR_ERR_NO_DOMAIN,
1017 _("no domain with matching uuid '%s' (%s)"),
1018 uuidstr, dom->name);
1019 goto error;
1020 }
1021
1022 virObjectRef(vm);
1023
1024 VIR_APPEND_ELEMENT(*vms, *nvms, vm);
1025 }
1026 virObjectRWUnlock(domlist);
1027
1028 virDomainObjListFilter(vms, nvms, conn, filter, flags);
1029
1030 return 0;
1031
1032 error:
1033 virObjectListFreeCount(*vms, *nvms);
1034 *vms = NULL;
1035 *nvms = 0;
1036
1037 return -1;
1038 }
1039
1040
1041 int
virDomainObjListExport(virDomainObjList * domlist,virConnectPtr conn,virDomainPtr ** domains,virDomainObjListACLFilter filter,unsigned int flags)1042 virDomainObjListExport(virDomainObjList *domlist,
1043 virConnectPtr conn,
1044 virDomainPtr **domains,
1045 virDomainObjListACLFilter filter,
1046 unsigned int flags)
1047 {
1048 virDomainObj **vms = NULL;
1049 virDomainPtr *doms = NULL;
1050 size_t nvms = 0;
1051 size_t i;
1052 int ret = -1;
1053
1054 if (virDomainObjListCollect(domlist, conn, &vms, &nvms, filter, flags) < 0)
1055 return -1;
1056
1057 if (domains) {
1058 doms = g_new0(virDomainPtr, nvms + 1);
1059
1060 for (i = 0; i < nvms; i++) {
1061 virDomainObj *vm = vms[i];
1062
1063 virObjectLock(vm);
1064 doms[i] = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
1065 virObjectUnlock(vm);
1066
1067 if (!doms[i])
1068 goto cleanup;
1069 }
1070
1071 *domains = g_steal_pointer(&doms);
1072 }
1073
1074 ret = nvms;
1075
1076 cleanup:
1077 virObjectListFree(doms);
1078 virObjectListFreeCount(vms, nvms);
1079 return ret;
1080 }
1081