54f410f29c2c626af457886f8d684074441ea7e0
[libvirt.git] / src / xen / xs_internal.c
1 /*
2  * xs_internal.c: access to Xen Store
3  *
4  * Copyright (C) 2006, 2009 Red Hat, Inc.
5  *
6  * See COPYING.LIB for the License of this software
7  *
8  * Daniel Veillard <veillard@redhat.com>
9  */
10
11 #include <config.h>
12
13 #include <stdio.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <stdlib.h>
17 #include <fcntl.h>
18 #include <sys/mman.h>
19 #include <sys/ioctl.h>
20
21 #include <stdint.h>
22
23 #include <xen/dom0_ops.h>
24 #include <xen/version.h>
25 #include <xen/xen.h>
26
27 #include <xs.h>
28
29 #include "virterror_internal.h"
30 #include "datatypes.h"
31 #include "driver.h"
32 #include "memory.h"
33 #include "event.h"
34 #include "logging.h"
35 #include "uuid.h"
36 #include "xen_driver.h"
37 #include "xs_internal.h"
38 #include "xen_hypervisor.h"
39
40 #define VIR_FROM_THIS VIR_FROM_XEN
41
42 #ifndef PROXY
43 static char *xenStoreDomainGetOSType(virDomainPtr domain);
44 static void xenStoreWatchEvent(int watch, int fd, int events, void *data);
45 static void xenStoreWatchListFree(xenStoreWatchListPtr list);
46
47 struct xenUnifiedDriver xenStoreDriver = {
48     xenStoreOpen, /* open */
49     xenStoreClose, /* close */
50     NULL, /* version */
51     NULL, /* hostname */
52     NULL, /* nodeGetInfo */
53     NULL, /* getCapabilities */
54     xenStoreListDomains, /* listDomains */
55     NULL, /* numOfDomains */
56     NULL, /* domainCreateXML */
57     NULL, /* domainSuspend */
58     NULL, /* domainResume */
59     xenStoreDomainShutdown, /* domainShutdown */
60     xenStoreDomainReboot, /* domainReboot */
61     NULL, /* domainDestroy */
62     xenStoreDomainGetOSType, /* domainGetOSType */
63     xenStoreDomainGetMaxMemory, /* domainGetMaxMemory */
64     NULL, /* domainSetMaxMemory */
65     xenStoreDomainSetMemory, /* domainSetMemory */
66     xenStoreGetDomainInfo, /* domainGetInfo */
67     NULL, /* domainSave */
68     NULL, /* domainRestore */
69     NULL, /* domainCoreDump */
70     NULL, /* domainSetVcpus */
71     NULL, /* domainPinVcpu */
72     NULL, /* domainGetVcpus */
73     NULL, /* domainGetMaxVcpus */
74     NULL, /* listDefinedDomains */
75     NULL, /* numOfDefinedDomains */
76     NULL, /* domainCreate */
77     NULL, /* domainDefineXML */
78     NULL, /* domainUndefine */
79     NULL, /* domainAttachDevice */
80     NULL, /* domainDetachDevice */
81     NULL, /* domainGetAutostart */
82     NULL, /* domainSetAutostart */
83     NULL, /* domainGetSchedulerType */
84     NULL, /* domainGetSchedulerParameters */
85     NULL, /* domainSetSchedulerParameters */
86 };
87
88 #endif /* ! PROXY */
89
90 #define virXenStoreError(conn, code, fmt...)                                 \
91         virReportErrorHelper(NULL, VIR_FROM_XENSTORE, code, __FILE__,      \
92                                __FUNCTION__, __LINE__, fmt)
93
94 /************************************************************************
95  *                                                                      *
96  *              Helper internal APIs                                    *
97  *                                                                      *
98  ************************************************************************/
99 #ifndef PROXY
100 /**
101  * virConnectDoStoreList:
102  * @conn: pointer to the hypervisor connection
103  * @path: the absolute path of the directory in the store to list
104  * @nb: OUT pointer to the number of items found
105  *
106  * Internal API querying the Xenstore for a list
107  *
108  * Returns a string which must be freed by the caller or NULL in case of error
109  */
110 static char **
111 virConnectDoStoreList(virConnectPtr conn, const char *path,
112                       unsigned int *nb)
113 {
114     xenUnifiedPrivatePtr priv;
115
116     if (conn == NULL)
117         return NULL;
118
119     priv = (xenUnifiedPrivatePtr) conn->privateData;
120     if (priv->xshandle == NULL || path == NULL || nb == NULL)
121         return (NULL);
122
123     return xs_directory (priv->xshandle, 0, path, nb);
124 }
125 #endif /* ! PROXY */
126
127 /**
128  * virDomainDoStoreQuery:
129  * @conn: pointer to the hypervisor connection
130  * @domid: id of the domain
131  * @path: the relative path of the data in the store to retrieve
132  *
133  * Internal API querying the Xenstore for a string value.
134  *
135  * Returns a string which must be freed by the caller or NULL in case of error
136  */
137 static char *
138 virDomainDoStoreQuery(virConnectPtr conn, int domid, const char *path)
139 {
140     char s[256];
141     unsigned int len = 0;
142     xenUnifiedPrivatePtr priv;
143
144     if (!conn)
145         return NULL;
146
147     priv = (xenUnifiedPrivatePtr) conn->privateData;
148     if (priv->xshandle == NULL)
149         return (NULL);
150
151     snprintf(s, 255, "/local/domain/%d/%s", domid, path);
152     s[255] = 0;
153
154     return xs_read(priv->xshandle, 0, &s[0], &len);
155 }
156
157 #ifndef PROXY
158 /**
159  * virDomainDoStoreWrite:
160  * @domain: a domain object
161  * @path: the relative path of the data in the store to retrieve
162  *
163  * Internal API setting up a string value in the Xenstore
164  * Requires write access to the XenStore
165  *
166  * Returns 0 in case of success, -1 in case of failure
167  */
168 static int
169 virDomainDoStoreWrite(virDomainPtr domain, const char *path,
170                       const char *value)
171 {
172     char s[256];
173     xenUnifiedPrivatePtr priv;
174     int ret = -1;
175
176     if (!VIR_IS_CONNECTED_DOMAIN(domain))
177         return (-1);
178
179     priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
180     if (priv->xshandle == NULL)
181         return (-1);
182     if (domain->conn->flags & VIR_CONNECT_RO)
183         return (-1);
184
185     snprintf(s, 255, "/local/domain/%d/%s", domain->id, path);
186     s[255] = 0;
187
188     if (xs_write(priv->xshandle, 0, &s[0], value, strlen(value)))
189         ret = 0;
190
191     return (ret);
192 }
193
194 /**
195  * virDomainGetVM:
196  * @domain: a domain object
197  *
198  * Internal API extracting a xenstore vm path.
199  *
200  * Returns the new string or NULL in case of error
201  */
202 static char *
203 virDomainGetVM(virDomainPtr domain)
204 {
205     char *vm;
206     char query[200];
207     unsigned int len;
208     xenUnifiedPrivatePtr priv;
209
210     if (!VIR_IS_CONNECTED_DOMAIN(domain))
211         return (NULL);
212
213     priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
214     if (priv->xshandle == NULL)
215         return (NULL);
216
217     snprintf(query, 199, "/local/domain/%d/vm", virDomainGetID(domain));
218     query[199] = 0;
219
220     vm = xs_read(priv->xshandle, 0, &query[0], &len);
221
222     return (vm);
223 }
224
225 /**
226  * virDomainGetVMInfo:
227  * @domain: a domain object
228  * @vm: the xenstore vm path
229  * @name: the value's path
230  *
231  * Internal API extracting one information the device used
232  * by the domain from xensttore
233  *
234  * Returns the new string or NULL in case of error
235  */
236 static char *
237 virDomainGetVMInfo(virDomainPtr domain, const char *vm, const char *name)
238 {
239     char s[256];
240     char *ret = NULL;
241     unsigned int len = 0;
242     xenUnifiedPrivatePtr priv;
243
244     if (!VIR_IS_CONNECTED_DOMAIN(domain))
245         return (NULL);
246
247     priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
248     if (priv->xshandle == NULL)
249         return (NULL);
250
251     snprintf(s, 255, "%s/%s", vm, name);
252     s[255] = 0;
253
254     ret = xs_read(priv->xshandle, 0, &s[0], &len);
255
256     return (ret);
257 }
258
259 #endif /* ! PROXY */
260
261 /************************************************************************
262  *                                                                      *
263  *              Canonical internal APIs                                 *
264  *                                                                      *
265  ************************************************************************/
266 /**
267  * xenStoreOpen:
268  * @conn: pointer to the connection block
269  * @name: URL for the target, NULL for local
270  * @flags: combination of virDrvOpenFlag(s)
271  *
272  * Connects to the Xen hypervisor.
273  *
274  * Returns 0 or -1 in case of error.
275  */
276 virDrvOpenStatus
277 xenStoreOpen(virConnectPtr conn,
278              virConnectAuthPtr auth ATTRIBUTE_UNUSED,
279              int flags ATTRIBUTE_UNUSED)
280 {
281     xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData;
282
283 #ifdef PROXY
284     priv->xshandle = xs_daemon_open_readonly();
285 #else
286     if (flags & VIR_CONNECT_RO)
287         priv->xshandle = xs_daemon_open_readonly();
288     else
289         priv->xshandle = xs_daemon_open();
290 #endif /* ! PROXY */
291
292     if (priv->xshandle == NULL) {
293         /*
294          * not being able to connect via the socket as an unprivileged
295          * user is rather normal, this should fallback to the proxy (or
296          * remote) mechanism.
297          */
298         if (xenHavePrivilege()) {
299             virXenStoreError(NULL, VIR_ERR_NO_XEN,
300                                  "%s", _("failed to connect to Xen Store"));
301         }
302         return (-1);
303     }
304
305 #ifndef PROXY
306     /* Init activeDomainList */
307     if (VIR_ALLOC(priv->activeDomainList) < 0) {
308         virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR,
309                                  "%s", _("failed to allocate activeDomainList"));
310         return -1;
311     }
312
313     /* Init watch list before filling in domInfoList,
314        so we can know if it is the first time through
315        when the callback fires */
316     if ( VIR_ALLOC(priv->xsWatchList) < 0 ) {
317         virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR,
318                                  "%s", _("failed to allocate xsWatchList"));
319         return -1;
320     }
321
322     /* This will get called once at start */
323     if ( xenStoreAddWatch(conn, "@releaseDomain",
324                      "releaseDomain", xenStoreDomainReleased, priv) < 0 )
325     {
326         virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR,
327                                  "%s", _("adding watch @releaseDomain"));
328         return -1;
329     }
330
331     /* The initial call of this will fill domInfoList */
332     if( xenStoreAddWatch(conn, "@introduceDomain",
333                      "introduceDomain", xenStoreDomainIntroduced, priv) < 0 )
334     {
335         virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR,
336                                  "%s", _("adding watch @introduceDomain"));
337         return -1;
338     }
339
340     /* Add an event handle */
341     if ((priv->xsWatch = virEventAddHandle(xs_fileno(priv->xshandle),
342                                            VIR_EVENT_HANDLE_READABLE,
343                                            xenStoreWatchEvent,
344                                            conn,
345                                            NULL)) < 0)
346         DEBUG0("Failed to add event handle, disabling events\n");
347
348 #endif //PROXY
349     return 0;
350 }
351
352 /**
353  * xenStoreClose:
354  * @conn: pointer to the connection block
355  *
356  * Close the connection to the Xen hypervisor.
357  *
358  * Returns 0 in case of success or -1 in case of error.
359  */
360 int
361 xenStoreClose(virConnectPtr conn)
362 {
363     xenUnifiedPrivatePtr priv;
364
365     if (conn == NULL) {
366         virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
367         return(-1);
368     }
369
370     priv = (xenUnifiedPrivatePtr) conn->privateData;
371
372 #ifndef PROXY
373     if (xenStoreRemoveWatch(conn, "@introduceDomain", "introduceDomain") < 0) {
374         DEBUG0("Warning, could not remove @introduceDomain watch");
375         /* not fatal */
376     }
377
378     if (xenStoreRemoveWatch(conn, "@releaseDomain", "releaseDomain") < 0) {
379         DEBUG0("Warning, could not remove @releaseDomain watch");
380         /* not fatal */
381     }
382
383     xenStoreWatchListFree(priv->xsWatchList);
384     priv->xsWatchList = NULL;
385     xenUnifiedDomainInfoListFree(priv->activeDomainList);
386     priv->activeDomainList = NULL;
387 #endif
388     if (priv->xshandle == NULL)
389         return(-1);
390
391 #ifndef PROXY
392     if (priv->xsWatch != -1)
393         virEventRemoveHandle(priv->xsWatch);
394 #endif
395     xs_daemon_close(priv->xshandle);
396     priv->xshandle = NULL;
397
398     return (0);
399 }
400
401 #ifndef PROXY
402 /**
403  * xenStoreGetDomainInfo:
404  * @domain: pointer to the domain block
405  * @info: the place where information should be stored
406  *
407  * Do an hypervisor call to get the related set of domain information.
408  *
409  * Returns 0 in case of success, -1 in case of error.
410  */
411 int
412 xenStoreGetDomainInfo(virDomainPtr domain, virDomainInfoPtr info)
413 {
414     char *tmp, **tmp2;
415     unsigned int nb_vcpus;
416     char request[200];
417     xenUnifiedPrivatePtr priv;
418
419     if (!VIR_IS_CONNECTED_DOMAIN(domain))
420         return (-1);
421
422     if ((domain == NULL) || (domain->conn == NULL) || (info == NULL)) {
423         virXenStoreError(domain ? domain->conn : NULL, VIR_ERR_INVALID_ARG,
424                          __FUNCTION__);
425         return(-1);
426     }
427
428     priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
429     if (priv->xshandle == NULL)
430         return(-1);
431
432     if (domain->id == -1)
433         return(-1);
434
435     tmp = virDomainDoStoreQuery(domain->conn, domain->id, "running");
436     if (tmp != NULL) {
437         if (tmp[0] == '1')
438             info->state = VIR_DOMAIN_RUNNING;
439         free(tmp);
440     } else {
441         info->state = VIR_DOMAIN_NOSTATE;
442     }
443     tmp = virDomainDoStoreQuery(domain->conn, domain->id, "memory/target");
444     if (tmp != NULL) {
445         info->memory = atol(tmp);
446         info->maxMem = atol(tmp);
447         free(tmp);
448     } else {
449         info->memory = 0;
450         info->maxMem = 0;
451     }
452 #if 0
453     /* doesn't seems to work */
454     tmp = virDomainDoStoreQuery(domain->conn, domain->id, "cpu_time");
455     if (tmp != NULL) {
456         info->cpuTime = atol(tmp);
457         free(tmp);
458     } else {
459         info->cpuTime = 0;
460     }
461 #endif
462     snprintf(request, 199, "/local/domain/%d/cpu", domain->id);
463     request[199] = 0;
464     tmp2 = virConnectDoStoreList(domain->conn, request, &nb_vcpus);
465     if (tmp2 != NULL) {
466         info->nrVirtCpu = nb_vcpus;
467         free(tmp2);
468     }
469     return (0);
470 }
471
472 /**
473  * xenStoreDomainSetMemory:
474  * @domain: pointer to the domain block
475  * @memory: the max memory size in kilobytes.
476  *
477  * Change the maximum amount of memory allowed in the xen store
478  *
479  * Returns 0 in case of success, -1 in case of error.
480  */
481 int
482 xenStoreDomainSetMemory(virDomainPtr domain, unsigned long memory)
483 {
484     int ret;
485     char value[20];
486
487     if ((domain == NULL) || (domain->conn == NULL) ||
488         (memory < 1024 * MIN_XEN_GUEST_SIZE)) {
489         virXenStoreError(domain ? domain->conn : NULL, VIR_ERR_INVALID_ARG,
490                          __FUNCTION__);
491         return(-1);
492     }
493     if (domain->id == -1)
494         return(-1);
495     if ((domain->id == 0) && (memory < (2 * MIN_XEN_GUEST_SIZE * 1024)))
496         return(-1);
497     snprintf(value, 19, "%lu", memory);
498     value[19] = 0;
499     ret = virDomainDoStoreWrite(domain, "memory/target", &value[0]);
500     if (ret < 0)
501         return (-1);
502     return (0);
503 }
504
505 /**
506  * xenStoreDomainGetMaxMemory:
507  * @domain: pointer to the domain block
508  *
509  * Ask the xenstore for the maximum memory allowed for a domain
510  *
511  * Returns the memory size in kilobytes or 0 in case of error.
512  */
513 unsigned long
514 xenStoreDomainGetMaxMemory(virDomainPtr domain)
515 {
516     char *tmp;
517     unsigned long ret = 0;
518     xenUnifiedPrivatePtr priv;
519
520     if (!VIR_IS_CONNECTED_DOMAIN(domain))
521         return (ret);
522     if (domain->id == -1)
523         return(-1);
524
525     priv = domain->conn->privateData;
526     xenUnifiedLock(priv);
527     tmp = virDomainDoStoreQuery(domain->conn, domain->id, "memory/target");
528     if (tmp != NULL) {
529         ret = (unsigned long) atol(tmp);
530         VIR_FREE(tmp);
531     }
532     xenUnifiedUnlock(priv);
533     return(ret);
534 }
535
536 /**
537  * xenStoreNumOfDomains:
538  * @conn: pointer to the hypervisor connection
539  *
540  * Provides the number of active domains.
541  *
542  * Returns the number of domain found or -1 in case of error
543  */
544 int
545 xenStoreNumOfDomains(virConnectPtr conn)
546 {
547     unsigned int num;
548     char **idlist;
549     int ret = -1;
550     xenUnifiedPrivatePtr priv;
551
552     if (conn == NULL) {
553         virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
554         return -1;
555     }
556
557     priv = (xenUnifiedPrivatePtr) conn->privateData;
558     if (priv->xshandle == NULL) {
559         virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
560         return(-1);
561     }
562     idlist = xs_directory(priv->xshandle, 0, "/local/domain", &num);
563     if (idlist) {
564         free(idlist);
565         ret = num;
566     }
567     return(ret);
568 }
569
570 /**
571  * xenStoreDoListDomains:
572  * @conn: pointer to the hypervisor connection
573  * @ids: array to collect the list of IDs of active domains
574  * @maxids: size of @ids
575  *
576  * Internal API: collect the list of active domains, and store
577  * their ID in @maxids. The driver lock must be held.
578  *
579  * Returns the number of domain found or -1 in case of error
580  */
581 static int
582 xenStoreDoListDomains(xenUnifiedPrivatePtr priv, int *ids, int maxids)
583 {
584     char **idlist = NULL, *endptr;
585     unsigned int num, i;
586     int ret = -1;
587     long id;
588
589     if (priv->xshandle == NULL)
590         goto out;
591
592     idlist = xs_directory (priv->xshandle, 0, "/local/domain", &num);
593     if (idlist == NULL)
594         goto out;
595
596     for (ret = 0, i = 0; (i < num) && (ret < maxids); i++) {
597         id = strtol(idlist[i], &endptr, 10);
598         if ((endptr == idlist[i]) || (*endptr != 0))
599             goto out;
600         ids[ret++] = (int) id;
601     }
602
603 out:
604     VIR_FREE (idlist);
605     return ret;
606 }
607
608 /**
609  * xenStoreListDomains:
610  * @conn: pointer to the hypervisor connection
611  * @ids: array to collect the list of IDs of active domains
612  * @maxids: size of @ids
613  *
614  * Collect the list of active domains, and store their ID in @maxids
615  *
616  * Returns the number of domain found or -1 in case of error
617  */
618 int
619 xenStoreListDomains(virConnectPtr conn, int *ids, int maxids)
620 {
621     xenUnifiedPrivatePtr priv;
622     int ret;
623
624     if ((conn == NULL) || (ids == NULL)) {
625         virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
626         return(-1);
627     }
628
629     priv = (xenUnifiedPrivatePtr) conn->privateData;
630
631     xenUnifiedLock(priv);
632     ret = xenStoreDoListDomains(priv, ids, maxids);
633     xenUnifiedUnlock(priv);
634
635     return(ret);
636 }
637
638 /**
639  * xenStoreLookupByName:
640  * @conn: A xend instance
641  * @name: The name of the domain
642  *
643  * Try to lookup a domain on the Xen Store based on its name.
644  *
645  * Returns a new domain object or NULL in case of failure
646  */
647 virDomainPtr
648 xenStoreLookupByName(virConnectPtr conn, const char *name)
649 {
650     virDomainPtr ret = NULL;
651     unsigned int num, i, len;
652     long id = -1;
653     char **idlist = NULL, *endptr;
654     char prop[200], *tmp;
655     int found = 0;
656     struct xend_domain *xenddomain = NULL;
657     xenUnifiedPrivatePtr priv;
658
659     if ((conn == NULL) || (name == NULL)) {
660         virXenStoreError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__);
661         return(NULL);
662     }
663
664     priv = (xenUnifiedPrivatePtr) conn->privateData;
665     if (priv->xshandle == NULL)
666         return(NULL);
667
668     idlist = xs_directory(priv->xshandle, 0, "/local/domain", &num);
669     if (idlist == NULL)
670         goto done;
671
672     for (i = 0; i < num; i++) {
673         id = strtol(idlist[i], &endptr, 10);
674         if ((endptr == idlist[i]) || (*endptr != 0)) {
675             goto done;
676         }
677 #if 0
678         if (virConnectCheckStoreID(conn, (int) id) < 0)
679             continue;
680 #endif
681         snprintf(prop, 199, "/local/domain/%s/name", idlist[i]);
682         prop[199] = 0;
683         tmp = xs_read(priv->xshandle, 0, prop, &len);
684         if (tmp != NULL) {
685             found = STREQ (name, tmp);
686             free(tmp);
687             if (found)
688                 break;
689         }
690     }
691     if (!found)
692         goto done;
693
694     ret = virGetDomain(conn, name, NULL);
695     if (ret == NULL)
696         goto done;
697
698     ret->id = id;
699
700 done:
701         free(xenddomain);
702         free(idlist);
703
704     return(ret);
705 }
706
707 /**
708  * xenStoreDomainShutdown:
709  * @domain: pointer to the Domain block
710  *
711  * Shutdown the domain, the OS is requested to properly shutdown
712  * and the domain may ignore it.  It will return immediately
713  * after queuing the request.
714  *
715  * Returns 0 in case of success, -1 in case of error.
716  */
717 int
718 xenStoreDomainShutdown(virDomainPtr domain)
719 {
720     int ret;
721     xenUnifiedPrivatePtr priv;
722
723     if ((domain == NULL) || (domain->conn == NULL)) {
724         virXenStoreError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG,
725                          __FUNCTION__);
726         return(-1);
727     }
728     if (domain->id == -1 || domain->id == 0)
729         return(-1);
730     /*
731      * this is very hackish, the domU kernel probes for a special
732      * node in the xenstore and launch the shutdown command if found.
733      */
734     priv = domain->conn->privateData;
735     xenUnifiedLock(priv);
736     ret = virDomainDoStoreWrite(domain, "control/shutdown", "poweroff");
737     xenUnifiedUnlock(priv);
738     return ret;
739 }
740
741 /**
742  * xenStoreDomainReboot:
743  * @domain: pointer to the Domain block
744  * @flags: extra flags for the reboot operation, not used yet
745  *
746  * Reboot the domain, the OS is requested to properly shutdown
747  * and reboot but the domain may ignore it.  It will return immediately
748  * after queuing the request.
749  *
750  * Returns 0 in case of success, -1 in case of error.
751  */
752 int
753 xenStoreDomainReboot(virDomainPtr domain, unsigned int flags ATTRIBUTE_UNUSED)
754 {
755     int ret;
756     xenUnifiedPrivatePtr priv;
757
758     if ((domain == NULL) || (domain->conn == NULL)) {
759         virXenStoreError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG,
760                          __FUNCTION__);
761         return(-1);
762     }
763     if (domain->id == -1 || domain->id == 0)
764         return(-1);
765     /*
766      * this is very hackish, the domU kernel probes for a special
767      * node in the xenstore and launch the shutdown command if found.
768      */
769     priv = domain->conn->privateData;
770     xenUnifiedLock(priv);
771     ret = virDomainDoStoreWrite(domain, "control/shutdown", "reboot");
772     xenUnifiedUnlock(priv);
773     return ret;
774 }
775
776 /*
777  * xenStoreDomainGetOSType:
778  * @domain: a domain object
779  *
780  * Get the type of domain operation system.
781  *
782  * Returns the new string or NULL in case of error, the string must be
783  *         freed by the caller.
784  */
785 static char *
786 xenStoreDomainGetOSType(virDomainPtr domain) {
787     char *vm, *str = NULL;
788
789     if ((domain == NULL) || (domain->conn == NULL)) {
790         virXenStoreError((domain ? domain->conn : NULL), VIR_ERR_INVALID_ARG,
791                          __FUNCTION__);
792         return(NULL);
793     }
794
795     vm = virDomainGetVM(domain);
796     if (vm) {
797         xenUnifiedPrivatePtr priv = domain->conn->privateData;
798         xenUnifiedLock(priv);
799         str = virDomainGetVMInfo(domain, vm, "image/ostype");
800         xenUnifiedUnlock(priv);
801         VIR_FREE(vm);
802     }
803
804     return (str);
805 }
806 #endif /* ! PROXY */
807
808 /**
809  * xenStoreDomainGetVNCPort:
810  * @conn: the hypervisor connection
811  * @domid: id of the domain
812  *
813  * Return the port number on which the domain is listening for VNC
814  * connections.
815  *
816  * The caller must hold the lock on the privateData
817  * associated with the 'conn' parameter.
818  *
819  * Returns the port number, -1 in case of error
820  */
821 int             xenStoreDomainGetVNCPort(virConnectPtr conn, int domid) {
822     char *tmp;
823     int ret = -1;
824
825     tmp = virDomainDoStoreQuery(conn, domid, "console/vnc-port");
826     if (tmp != NULL) {
827         char *end;
828         ret = strtol(tmp, &end, 10);
829         if (ret == 0 && end == tmp)
830             ret = -1;
831         free(tmp);
832     }
833     return(ret);
834 }
835
836 /**
837  * xenStoreDomainGetConsolePath:
838  * @conn: the hypervisor connection
839  * @domid: id of the domain
840  *
841  * Return the path to the psuedo TTY on which the guest domain's
842  * serial console is attached.
843  *
844  * Returns the path to the serial console. It is the callers
845  * responsibilty to free() the return string. Returns NULL
846  * on error
847  *
848  * The caller must hold the lock on the privateData
849  * associated with the 'conn' parameter.
850  */
851 char *          xenStoreDomainGetConsolePath(virConnectPtr conn, int domid) {
852   return virDomainDoStoreQuery(conn, domid, "console/tty");
853 }
854
855 #ifdef PROXY
856 /*
857  * xenStoreDomainGetOSTypeID:
858  * @conn: pointer to the connection.
859  * @id: the domain id
860  *
861  * Get the type of domain operation system.
862  *
863  * The caller must hold the lock on the privateData
864  * associated with the 'conn' parameter.
865  *
866  * Returns the new string or NULL in case of error, the string must be
867  *         freed by the caller.
868  */
869 char *
870 xenStoreDomainGetOSTypeID(virConnectPtr conn, int id) {
871     char *vm, *str = NULL;
872     char query[200];
873     unsigned int len;
874     xenUnifiedPrivatePtr priv;
875
876     if (id < 0)
877         return(NULL);
878
879     priv = (xenUnifiedPrivatePtr) conn->privateData;
880     if (priv->xshandle == NULL)
881         return (NULL);
882
883     snprintf(query, 199, "/local/domain/%d/vm", id);
884     query[199] = 0;
885
886     vm = xs_read(priv->xshandle, 0, &query[0], &len);
887
888     if (vm) {
889         snprintf(query, 199, "%s/image/ostype", vm);
890         str = xs_read(priv->xshandle, 0, &query[0], &len);
891         free(vm);
892     }
893     if (str == NULL)
894         str = strdup("linux");
895
896
897     return (str);
898 }
899 #endif /* PROXY */
900
901 /*
902  * xenStoreDomainGetNetworkID:
903  * @conn: pointer to the connection.
904  * @id: the domain id
905  * @mac: the mac address
906  *
907  * Get the reference (i.e. the string number) for the device on that domain
908  * which uses the given mac address
909  *
910  * The caller must hold the lock on the privateData
911  * associated with the 'conn' parameter.
912  *
913  * Returns the new string or NULL in case of error, the string must be
914  *         freed by the caller.
915  */
916 char *
917 xenStoreDomainGetNetworkID(virConnectPtr conn, int id, const char *mac) {
918     char dir[80], path[128], **list = NULL, *val = NULL;
919     unsigned int len, i, num;
920     char *ret = NULL;
921     xenUnifiedPrivatePtr priv;
922
923     if (id < 0)
924         return(NULL);
925
926     priv = (xenUnifiedPrivatePtr) conn->privateData;
927     if (priv->xshandle == NULL)
928         return (NULL);
929     if (mac == NULL)
930         return (NULL);
931
932     snprintf(dir, sizeof(dir), "/local/domain/0/backend/vif/%d", id);
933     list = xs_directory(priv->xshandle, 0, dir, &num);
934     if (list == NULL)
935         return(NULL);
936     for (i = 0; i < num; i++) {
937         snprintf(path, sizeof(path), "%s/%s/%s", dir, list[i], "mac");
938         if ((val = xs_read(priv->xshandle, 0, path, &len)) == NULL)
939             break;
940
941         bool match = (virMacAddrCompare(val, mac) == 0);
942
943         VIR_FREE(val);
944
945         if (match) {
946             ret = strdup(list[i]);
947             break;
948         }
949     }
950
951     VIR_FREE(list);
952     return(ret);
953 }
954
955 /*
956  * xenStoreDomainGetDiskID:
957  * @conn: pointer to the connection.
958  * @id: the domain id
959  * @dev: the virtual block device name
960  *
961  * Get the reference (i.e. the string number) for the device on that domain
962  * which uses the given virtual block device name
963  *
964  * The caller must hold the lock on the privateData
965  * associated with the 'conn' parameter.
966  *
967  * Returns the new string or NULL in case of error, the string must be
968  *         freed by the caller.
969  */
970 char *
971 xenStoreDomainGetDiskID(virConnectPtr conn, int id, const char *dev) {
972     char dir[80], path[128], **list = NULL, *val = NULL;
973     unsigned int devlen, len, i, num;
974     char *ret = NULL;
975     xenUnifiedPrivatePtr priv;
976
977     if (id < 0)
978         return(NULL);
979
980     priv = (xenUnifiedPrivatePtr) conn->privateData;
981     if (priv->xshandle == NULL)
982         return (NULL);
983     if (dev == NULL)
984         return (NULL);
985     devlen = strlen(dev);
986     if (devlen <= 0)
987         return (NULL);
988
989     snprintf(dir, sizeof(dir), "/local/domain/0/backend/vbd/%d", id);
990     list = xs_directory(priv->xshandle, 0, dir, &num);
991     if (list != NULL) {
992         for (i = 0; i < num; i++) {
993             snprintf(path, sizeof(path), "%s/%s/%s", dir, list[i], "dev");
994             val = xs_read(priv->xshandle, 0, path, &len);
995             if (val == NULL)
996                 break;
997             if ((devlen != len) || memcmp(val, dev, len)) {
998                 free (val);
999             } else {
1000                 ret = strdup(list[i]);
1001                 free (val);
1002                 free (list);
1003                 return (ret);
1004             }
1005         }
1006         free (list);
1007     }
1008     snprintf(dir, sizeof(dir), "/local/domain/0/backend/tap/%d", id);
1009     list = xs_directory(priv->xshandle, 0, dir, &num);
1010     if (list != NULL) {
1011         for (i = 0; i < num; i++) {
1012             snprintf(path, sizeof(path), "%s/%s/%s", dir, list[i], "dev");
1013             val = xs_read(priv->xshandle, 0, path, &len);
1014             if (val == NULL)
1015                 break;
1016             if ((devlen != len) || memcmp(val, dev, len)) {
1017                 free (val);
1018             } else {
1019                 ret = strdup(list[i]);
1020                 free (val);
1021                 free (list);
1022                 return (ret);
1023             }
1024         }
1025         free (list);
1026     }
1027     return (NULL);
1028 }
1029
1030 /*
1031  * The caller must hold the lock on the privateData
1032  * associated with the 'conn' parameter.
1033  */
1034 char *xenStoreDomainGetName(virConnectPtr conn,
1035                             int id) {
1036     char prop[200];
1037     xenUnifiedPrivatePtr priv;
1038     unsigned int len;
1039
1040     priv = (xenUnifiedPrivatePtr) conn->privateData;
1041     if (priv->xshandle == NULL)
1042         return(NULL);
1043
1044     snprintf(prop, 199, "/local/domain/%d/name", id);
1045     prop[199] = 0;
1046     return xs_read(priv->xshandle, 0, prop, &len);
1047 }
1048
1049 #ifndef PROXY
1050 /*
1051  * The caller must hold the lock on the privateData
1052  * associated with the 'conn' parameter.
1053  */
1054 int xenStoreDomainGetUUID(virConnectPtr conn,
1055                           int id,
1056                           unsigned char *uuid) {
1057     char prop[200];
1058     xenUnifiedPrivatePtr priv;
1059     unsigned int len;
1060     char *uuidstr;
1061     int ret = 0;
1062
1063     priv = (xenUnifiedPrivatePtr) conn->privateData;
1064     if (priv->xshandle == NULL)
1065         return -1;
1066
1067     snprintf(prop, 199, "/local/domain/%d/vm", id);
1068     prop[199] = 0;
1069     // This will return something like
1070     // /vm/00000000-0000-0000-0000-000000000000
1071     uuidstr = xs_read(priv->xshandle, 0, prop, &len);
1072
1073     // remove "/vm/"
1074     ret = virUUIDParse(uuidstr + 4, uuid);
1075
1076     VIR_FREE(uuidstr);
1077
1078     return ret;
1079 }
1080
1081 static void
1082 xenStoreWatchListFree(xenStoreWatchListPtr list)
1083 {
1084     int i;
1085     for (i=0; i<list->count; i++) {
1086         VIR_FREE(list->watches[i]->path);
1087         VIR_FREE(list->watches[i]->token);
1088         VIR_FREE(list->watches[i]);
1089     }
1090     VIR_FREE(list);
1091 }
1092
1093 /*
1094  * The caller must hold the lock on the privateData
1095  * associated with the 'conn' parameter.
1096  */
1097 int xenStoreAddWatch(virConnectPtr conn,
1098                      const char *path,
1099                      const char *token,
1100                      xenStoreWatchCallback cb,
1101                      void *opaque)
1102 {
1103     xenStoreWatchPtr watch;
1104     int n;
1105     xenStoreWatchListPtr list;
1106     xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData;
1107
1108     if (priv->xshandle == NULL)
1109         return -1;
1110
1111     list = priv->xsWatchList;
1112     if(!list)
1113         return -1;
1114
1115     /* check if we already have this callback on our list */
1116     for (n=0; n < list->count; n++) {
1117         if( STREQ(list->watches[n]->path, path) &&
1118             STREQ(list->watches[n]->token, token)) {
1119             virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR,
1120                                  "%s", _("watch already tracked"));
1121             return -1;
1122         }
1123     }
1124
1125     if (VIR_ALLOC(watch) < 0)
1126         return -1;
1127     watch->path   = strdup(path);
1128     watch->token  = strdup(token);
1129     watch->cb     = cb;
1130     watch->opaque = opaque;
1131
1132     /* Make space on list */
1133     n = list->count;
1134     if (VIR_REALLOC_N(list->watches, n + 1) < 0) {
1135         virXenStoreError(NULL, VIR_ERR_INTERNAL_ERROR,
1136                                  "%s", _("reallocating list"));
1137         VIR_FREE(watch);
1138         return -1;
1139     }
1140
1141     list->watches[n] = watch;
1142     list->count++;
1143
1144     conn->refs++;
1145
1146     return xs_watch(priv->xshandle, watch->path, watch->token);
1147 }
1148
1149 /*
1150  * The caller must hold the lock on the privateData
1151  * associated with the 'conn' parameter.
1152  */
1153 int xenStoreRemoveWatch(virConnectPtr conn,
1154                         const char *path,
1155                         const char *token)
1156 {
1157     int i;
1158     xenStoreWatchListPtr list;
1159     xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData;
1160
1161     if (priv->xshandle == NULL)
1162         return -1;
1163
1164     list = priv->xsWatchList;
1165     if(!list)
1166         return -1;
1167
1168     for (i = 0 ; i < list->count ; i++) {
1169         if( STREQ(list->watches[i]->path, path) &&
1170             STREQ(list->watches[i]->token, token)) {
1171
1172             if (!xs_unwatch(priv->xshandle,
1173                        list->watches[i]->path,
1174                        list->watches[i]->token))
1175             {
1176                 DEBUG0("WARNING: Could not remove watch");
1177                 /* Not fatal, continue */
1178             }
1179
1180             VIR_FREE(list->watches[i]->path);
1181             VIR_FREE(list->watches[i]->token);
1182             VIR_FREE(list->watches[i]);
1183
1184             if (i < (list->count - 1))
1185                 memmove(list->watches + i,
1186                         list->watches + i + 1,
1187                         sizeof(*(list->watches)) *
1188                                 (list->count - (i + 1)));
1189
1190             if (VIR_REALLOC_N(list->watches,
1191                               list->count - 1) < 0) {
1192                 ; /* Failure to reduce memory allocation isn't fatal */
1193             }
1194             list->count--;
1195             virUnrefConnect(conn);
1196             return 0;
1197         }
1198     }
1199     return -1;
1200 }
1201
1202 static xenStoreWatchPtr
1203 xenStoreFindWatch(xenStoreWatchListPtr list,
1204                   const char *path,
1205                   const char *token)
1206 {
1207     int i;
1208     for (i = 0 ; i < list->count ; i++)
1209         if( STREQ(path, list->watches[i]->path) &&
1210             STREQ(token, list->watches[i]->token) )
1211             return list->watches[i];
1212
1213     return NULL;
1214 }
1215
1216 static void
1217 xenStoreWatchEvent(int watch ATTRIBUTE_UNUSED,
1218                    int fd ATTRIBUTE_UNUSED,
1219                    int events,
1220                    void *data)
1221 {
1222     char                 **event;
1223     char                 *path;
1224     char                 *token;
1225     unsigned int         stringCount;
1226     xenStoreWatchPtr     sw;
1227
1228     virConnectPtr        conn = data;
1229     xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) conn->privateData;
1230
1231     if(!priv) return;
1232
1233     /* only set a watch on read and write events */
1234     if (events & (VIR_EVENT_HANDLE_ERROR | VIR_EVENT_HANDLE_HANGUP)) return;
1235
1236     xenUnifiedLock(priv);
1237
1238     if(!priv->xshandle)
1239         goto cleanup;
1240
1241     event = xs_read_watch(priv->xshandle, &stringCount);
1242     if (!event)
1243         goto cleanup;
1244
1245     path  = event[XS_WATCH_PATH];
1246     token = event[XS_WATCH_TOKEN];
1247
1248     sw = xenStoreFindWatch(priv->xsWatchList, path, token);
1249     if( sw )
1250         sw->cb(conn, path, token, sw->opaque);
1251     VIR_FREE(event);
1252
1253 cleanup:
1254     xenUnifiedUnlock(priv);
1255 }
1256
1257
1258 /*
1259  * The domain callback for the @introduceDomain watch
1260  *
1261  * The lock on 'priv' is held when calling this
1262  */
1263 int xenStoreDomainIntroduced(virConnectPtr conn,
1264                              const char *path ATTRIBUTE_UNUSED,
1265                              const char *token ATTRIBUTE_UNUSED,
1266                              void *opaque)
1267 {
1268     int i, j, found, missing = 0, retries = 20;
1269     int new_domain_cnt;
1270     int *new_domids;
1271     int nread;
1272
1273     xenUnifiedPrivatePtr priv = opaque;
1274
1275 retry:
1276     new_domain_cnt = xenStoreNumOfDomains(conn);
1277     if( VIR_ALLOC_N(new_domids,new_domain_cnt) < 0 ) {
1278         virReportOOMError(NULL);
1279         return -1;
1280     }
1281     nread = xenStoreDoListDomains(priv, new_domids, new_domain_cnt);
1282     if (nread != new_domain_cnt) {
1283         // mismatch. retry this read
1284         VIR_FREE(new_domids);
1285         goto retry;
1286     }
1287
1288     missing = 0;
1289     for (i=0 ; i < new_domain_cnt ; i++) {
1290         found = 0;
1291         for (j = 0 ; j < priv->activeDomainList->count ; j++) {
1292             if (priv->activeDomainList->doms[j]->id == new_domids[i]) {
1293                 found = 1;
1294                 break;
1295             }
1296         }
1297
1298         if (!found) {
1299             virDomainEventPtr event;
1300             char *name;
1301             unsigned char uuid[VIR_UUID_BUFLEN];
1302
1303             if (!(name = xenStoreDomainGetName(conn, new_domids[i]))) {
1304                 missing = 1;
1305                 continue;
1306             }
1307             if (xenStoreDomainGetUUID(conn, new_domids[i], uuid) < 0) {
1308                 missing = 1;
1309                 VIR_FREE(name);
1310                 continue;
1311             }
1312
1313             event = virDomainEventNew(new_domids[i], name, uuid,
1314                                       VIR_DOMAIN_EVENT_STARTED,
1315                                       VIR_DOMAIN_EVENT_STARTED_BOOTED);
1316             if (event)
1317                 xenUnifiedDomainEventDispatch(priv, event);
1318
1319             /* Add to the list */
1320             xenUnifiedAddDomainInfo(priv->activeDomainList,
1321                                     new_domids[i], name, uuid);
1322
1323             VIR_FREE(name);
1324         }
1325     }
1326     VIR_FREE(new_domids);
1327
1328     if (missing && retries--) {
1329         DEBUG0("Some domains were missing, trying again");
1330         usleep(100 * 1000);
1331         goto retry;
1332     }
1333     return 0;
1334 }
1335
1336 /*
1337  * The domain callback for the @destroyDomain watch
1338  *
1339  * The lock on 'priv' is held when calling this
1340  */
1341 int xenStoreDomainReleased(virConnectPtr conn,
1342                             const char *path  ATTRIBUTE_UNUSED,
1343                             const char *token ATTRIBUTE_UNUSED,
1344                             void *opaque)
1345 {
1346     int i, j, found, removed, retries = 20;
1347     int new_domain_cnt;
1348     int *new_domids;
1349     int nread;
1350
1351     xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) opaque;
1352
1353     if(!priv->activeDomainList->count) return 0;
1354
1355 retry:
1356     new_domain_cnt = xenStoreNumOfDomains(conn);
1357
1358     if( VIR_ALLOC_N(new_domids,new_domain_cnt) < 0 ) {
1359         virReportOOMError(NULL);
1360         return -1;
1361     }
1362     nread = xenStoreDoListDomains(priv, new_domids, new_domain_cnt);
1363     if (nread != new_domain_cnt) {
1364         // mismatch. retry this read
1365         VIR_FREE(new_domids);
1366         goto retry;
1367     }
1368
1369     removed = 0;
1370     for (j=0 ; j < priv->activeDomainList->count ; j++) {
1371         found = 0;
1372         for (i=0 ; i < new_domain_cnt ; i++) {
1373             if (priv->activeDomainList->doms[j]->id == new_domids[i]) {
1374                 found = 1;
1375                 break;
1376             }
1377         }
1378
1379         if (!found) {
1380             virDomainEventPtr event =
1381                 virDomainEventNew(-1,
1382                                   priv->activeDomainList->doms[j]->name,
1383                                   priv->activeDomainList->doms[j]->uuid,
1384                                   VIR_DOMAIN_EVENT_STOPPED,
1385                                   VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN);
1386             if (event)
1387                 xenUnifiedDomainEventDispatch(priv, event);
1388
1389             /* Remove from the list */
1390             xenUnifiedRemoveDomainInfo(priv->activeDomainList,
1391                                        priv->activeDomainList->doms[j]->id,
1392                                        priv->activeDomainList->doms[j]->name,
1393                                        priv->activeDomainList->doms[j]->uuid);
1394
1395             removed = 1;
1396         }
1397     }
1398
1399     VIR_FREE(new_domids);
1400
1401     if (!removed && retries--) {
1402         DEBUG0("No domains removed, retrying");
1403         usleep(100 * 1000);
1404         goto retry;
1405     }
1406     return 0;
1407 }
1408
1409 #endif //PROXY