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