1/*
2 * Copyright (C) 2016 George W. Dunlap, Citrix Systems UK Ltd
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation;
7 * version 2.1 of the License.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; If not, see <http://www.gnu.org/licenses/>.
16 */
17package xenlight
18
19/*
20#cgo LDFLAGS: -lxenlight -lyajl -lxentoollog
21#include <stdlib.h>
22#include <libxl.h>
23*/
24import "C"
25
26/*
27 * Other flags that may be needed at some point:
28 *  -lnl-route-3 -lnl-3
29 *
30 * To get back to static linking:
31 * #cgo LDFLAGS: -lxenlight -lyajl_s -lxengnttab -lxenstore -lxenguest -lxentoollog -lxenevtchn -lxenctrl -lblktapctl -lxenforeignmemory -lxencall -lz -luuid -lutil
32 */
33
34import (
35	"fmt"
36	"time"
37	"unsafe"
38)
39
40/*
41 * Errors
42 */
43
44type Error int
45
46const (
47	ErrorNonspecific                  = Error(-C.ERROR_NONSPECIFIC)
48	ErrorVersion                      = Error(-C.ERROR_VERSION)
49	ErrorFail                         = Error(-C.ERROR_FAIL)
50	ErrorNi                           = Error(-C.ERROR_NI)
51	ErrorNomem                        = Error(-C.ERROR_NOMEM)
52	ErrorInval                        = Error(-C.ERROR_INVAL)
53	ErrorBadfail                      = Error(-C.ERROR_BADFAIL)
54	ErrorGuestTimedout                = Error(-C.ERROR_GUEST_TIMEDOUT)
55	ErrorTimedout                     = Error(-C.ERROR_TIMEDOUT)
56	ErrorNoparavirt                   = Error(-C.ERROR_NOPARAVIRT)
57	ErrorNotReady                     = Error(-C.ERROR_NOT_READY)
58	ErrorOseventRegFail               = Error(-C.ERROR_OSEVENT_REG_FAIL)
59	ErrorBufferfull                   = Error(-C.ERROR_BUFFERFULL)
60	ErrorUnknownChild                 = Error(-C.ERROR_UNKNOWN_CHILD)
61	ErrorLockFail                     = Error(-C.ERROR_LOCK_FAIL)
62	ErrorJsonConfigEmpty              = Error(-C.ERROR_JSON_CONFIG_EMPTY)
63	ErrorDeviceExists                 = Error(-C.ERROR_DEVICE_EXISTS)
64	ErrorCheckpointDevopsDoesNotMatch = Error(-C.ERROR_CHECKPOINT_DEVOPS_DOES_NOT_MATCH)
65	ErrorCheckpointDeviceNotSupported = Error(-C.ERROR_CHECKPOINT_DEVICE_NOT_SUPPORTED)
66	ErrorVnumaConfigInvalid           = Error(-C.ERROR_VNUMA_CONFIG_INVALID)
67	ErrorDomainNotfound               = Error(-C.ERROR_DOMAIN_NOTFOUND)
68	ErrorAborted                      = Error(-C.ERROR_ABORTED)
69	ErrorNotfound                     = Error(-C.ERROR_NOTFOUND)
70	ErrorDomainDestroyed              = Error(-C.ERROR_DOMAIN_DESTROYED)
71	ErrorFeatureRemoved               = Error(-C.ERROR_FEATURE_REMOVED)
72)
73
74var errors = [...]string{
75	ErrorNonspecific:                  "Non-specific error",
76	ErrorVersion:                      "Wrong version",
77	ErrorFail:                         "Failed",
78	ErrorNi:                           "Not Implemented",
79	ErrorNomem:                        "No memory",
80	ErrorInval:                        "Invalid argument",
81	ErrorBadfail:                      "Bad Fail",
82	ErrorGuestTimedout:                "Guest timed out",
83	ErrorTimedout:                     "Timed out",
84	ErrorNoparavirt:                   "No Paravirtualization",
85	ErrorNotReady:                     "Not ready",
86	ErrorOseventRegFail:               "OS event registration failed",
87	ErrorBufferfull:                   "Buffer full",
88	ErrorUnknownChild:                 "Unknown child",
89	ErrorLockFail:                     "Lock failed",
90	ErrorJsonConfigEmpty:              "JSON config empty",
91	ErrorDeviceExists:                 "Device exists",
92	ErrorCheckpointDevopsDoesNotMatch: "Checkpoint devops does not match",
93	ErrorCheckpointDeviceNotSupported: "Checkpoint device not supported",
94	ErrorVnumaConfigInvalid:           "VNUMA config invalid",
95	ErrorDomainNotfound:               "Domain not found",
96	ErrorAborted:                      "Aborted",
97	ErrorNotfound:                     "Not found",
98	ErrorDomainDestroyed:              "Domain destroyed",
99	ErrorFeatureRemoved:               "Feature removed",
100}
101
102func (e Error) Error() string {
103	if 0 < int(e) && int(e) < len(errors) {
104		s := errors[e]
105		if s != "" {
106			return s
107		}
108	}
109	return fmt.Sprintf("libxl error: %d", -e)
110
111}
112
113/*
114 * Types: Builtins
115 */
116
117type Domid uint32
118
119type MemKB uint64
120
121type Uuid C.libxl_uuid
122
123type Context struct {
124	ctx    *C.libxl_ctx
125	logger *C.xentoollog_logger_stdiostream
126}
127
128type Hwcap []C.uint32_t
129
130func (chwcap C.libxl_hwcap) toGo() (ghwcap Hwcap) {
131	// Alloc a Go slice for the bytes
132	size := 8
133	ghwcap = make([]C.uint32_t, size)
134
135	// Make a slice pointing to the C array
136	mapslice := (*[1 << 30]C.uint32_t)(unsafe.Pointer(&chwcap[0]))[:size:size]
137
138	// And copy the C array into the Go array
139	copy(ghwcap, mapslice)
140
141	return
142}
143
144// typedef struct {
145//     uint32_t size;          /* number of bytes in map */
146//     uint8_t *map;
147// } libxl_bitmap;
148
149// Implement the Go bitmap type such that the underlying data can
150// easily be copied in and out.  NB that we still have to do copies
151// both directions, because cgo runtime restrictions forbid passing to
152// a C function a pointer to a Go-allocated structure which contains a
153// pointer.
154type Bitmap struct {
155	bitmap []C.uint8_t
156}
157
158/*
159 * Types: IDL
160 *
161 * FIXME: Generate these automatically from the IDL
162 */
163
164type Physinfo struct {
165	ThreadsPerCore    uint32
166	CoresPerSocket    uint32
167	MaxCpuId          uint32
168	NrCpus            uint32
169	CpuKhz            uint32
170	TotalPages        uint64
171	FreePages         uint64
172	ScrubPages        uint64
173	OutstandingPages  uint64
174	SharingFreedPages uint64
175	SharingUsedFrames uint64
176	NrNodes           uint32
177	HwCap             Hwcap
178	CapHvm            bool
179	CapHvmDirectio    bool
180}
181
182func (cphys *C.libxl_physinfo) toGo() (physinfo *Physinfo) {
183
184	physinfo = &Physinfo{}
185	physinfo.ThreadsPerCore = uint32(cphys.threads_per_core)
186	physinfo.CoresPerSocket = uint32(cphys.cores_per_socket)
187	physinfo.MaxCpuId = uint32(cphys.max_cpu_id)
188	physinfo.NrCpus = uint32(cphys.nr_cpus)
189	physinfo.CpuKhz = uint32(cphys.cpu_khz)
190	physinfo.TotalPages = uint64(cphys.total_pages)
191	physinfo.FreePages = uint64(cphys.free_pages)
192	physinfo.ScrubPages = uint64(cphys.scrub_pages)
193	physinfo.ScrubPages = uint64(cphys.scrub_pages)
194	physinfo.SharingFreedPages = uint64(cphys.sharing_freed_pages)
195	physinfo.SharingUsedFrames = uint64(cphys.sharing_used_frames)
196	physinfo.NrNodes = uint32(cphys.nr_nodes)
197	physinfo.HwCap = cphys.hw_cap.toGo()
198	physinfo.CapHvm = bool(cphys.cap_hvm)
199	physinfo.CapHvmDirectio = bool(cphys.cap_hvm_directio)
200
201	return
202}
203
204type VersionInfo struct {
205	XenVersionMajor int
206	XenVersionMinor int
207	XenVersionExtra string
208	Compiler        string
209	CompileBy       string
210	CompileDomain   string
211	CompileDate     string
212	Capabilities    string
213	Changeset       string
214	VirtStart       uint64
215	Pagesize        int
216	Commandline     string
217	BuildId         string
218}
219
220func (cinfo *C.libxl_version_info) toGo() (info *VersionInfo) {
221	info = &VersionInfo{}
222	info.XenVersionMajor = int(cinfo.xen_version_major)
223	info.XenVersionMinor = int(cinfo.xen_version_minor)
224	info.XenVersionExtra = C.GoString(cinfo.xen_version_extra)
225	info.Compiler = C.GoString(cinfo.compiler)
226	info.CompileBy = C.GoString(cinfo.compile_by)
227	info.CompileDomain = C.GoString(cinfo.compile_domain)
228	info.CompileDate = C.GoString(cinfo.compile_date)
229	info.Capabilities = C.GoString(cinfo.capabilities)
230	info.Changeset = C.GoString(cinfo.changeset)
231	info.VirtStart = uint64(cinfo.virt_start)
232	info.Pagesize = int(cinfo.pagesize)
233	info.Commandline = C.GoString(cinfo.commandline)
234	info.BuildId = C.GoString(cinfo.build_id)
235
236	return
237}
238
239type ShutdownReason int32
240
241const (
242	ShutdownReasonUnknown   = ShutdownReason(C.LIBXL_SHUTDOWN_REASON_UNKNOWN)
243	ShutdownReasonPoweroff  = ShutdownReason(C.LIBXL_SHUTDOWN_REASON_POWEROFF)
244	ShutdownReasonReboot    = ShutdownReason(C.LIBXL_SHUTDOWN_REASON_REBOOT)
245	ShutdownReasonSuspend   = ShutdownReason(C.LIBXL_SHUTDOWN_REASON_SUSPEND)
246	ShutdownReasonCrash     = ShutdownReason(C.LIBXL_SHUTDOWN_REASON_CRASH)
247	ShutdownReasonWatchdog  = ShutdownReason(C.LIBXL_SHUTDOWN_REASON_WATCHDOG)
248	ShutdownReasonSoftReset = ShutdownReason(C.LIBXL_SHUTDOWN_REASON_SOFT_RESET)
249)
250
251func (sr ShutdownReason) String() (str string) {
252	cstr := C.libxl_shutdown_reason_to_string(C.libxl_shutdown_reason(sr))
253	str = C.GoString(cstr)
254
255	return
256}
257
258type DomainType int32
259
260const (
261	DomainTypeInvalid = DomainType(C.LIBXL_DOMAIN_TYPE_INVALID)
262	DomainTypeHvm     = DomainType(C.LIBXL_DOMAIN_TYPE_HVM)
263	DomainTypePv      = DomainType(C.LIBXL_DOMAIN_TYPE_PV)
264)
265
266func (dt DomainType) String() (str string) {
267	cstr := C.libxl_domain_type_to_string(C.libxl_domain_type(dt))
268	str = C.GoString(cstr)
269
270	return
271}
272
273type Dominfo struct {
274	Uuid      Uuid
275	Domid     Domid
276	Ssidref   uint32
277	SsidLabel string
278	Running   bool
279	Blocked   bool
280	Paused    bool
281	Shutdown  bool
282	Dying     bool
283	NeverStop bool
284
285	ShutdownReason   int32
286	OutstandingMemkb MemKB
287	CurrentMemkb     MemKB
288	SharedMemkb      MemKB
289	PagedMemkb       MemKB
290	MaxMemkb         MemKB
291	CpuTime          time.Duration
292	VcpuMaxId        uint32
293	VcpuOnline       uint32
294	Cpupool          uint32
295	DomainType       int32
296}
297
298func (cdi *C.libxl_dominfo) toGo() (di *Dominfo) {
299
300	di = &Dominfo{}
301	di.Uuid = Uuid(cdi.uuid)
302	di.Domid = Domid(cdi.domid)
303	di.Ssidref = uint32(cdi.ssidref)
304	di.SsidLabel = C.GoString(cdi.ssid_label)
305	di.Running = bool(cdi.running)
306	di.Blocked = bool(cdi.blocked)
307	di.Paused = bool(cdi.paused)
308	di.Shutdown = bool(cdi.shutdown)
309	di.Dying = bool(cdi.dying)
310	di.NeverStop = bool(cdi.never_stop)
311	di.ShutdownReason = int32(cdi.shutdown_reason)
312	di.OutstandingMemkb = MemKB(cdi.outstanding_memkb)
313	di.CurrentMemkb = MemKB(cdi.current_memkb)
314	di.SharedMemkb = MemKB(cdi.shared_memkb)
315	di.PagedMemkb = MemKB(cdi.paged_memkb)
316	di.MaxMemkb = MemKB(cdi.max_memkb)
317	di.CpuTime = time.Duration(cdi.cpu_time)
318	di.VcpuMaxId = uint32(cdi.vcpu_max_id)
319	di.VcpuOnline = uint32(cdi.vcpu_online)
320	di.Cpupool = uint32(cdi.cpupool)
321	di.DomainType = int32(cdi.domain_type)
322
323	return
324}
325
326// # Consistent with values defined in domctl.h
327// # Except unknown which we have made up
328// libxl_scheduler = Enumeration("scheduler", [
329//     (0, "unknown"),
330//     (4, "sedf"),
331//     (5, "credit"),
332//     (6, "credit2"),
333//     (7, "arinc653"),
334//     (8, "rtds"),
335//     ])
336type Scheduler int
337
338var (
339	SchedulerUnknown  Scheduler = C.LIBXL_SCHEDULER_UNKNOWN
340	SchedulerSedf     Scheduler = C.LIBXL_SCHEDULER_SEDF
341	SchedulerCredit   Scheduler = C.LIBXL_SCHEDULER_CREDIT
342	SchedulerCredit2  Scheduler = C.LIBXL_SCHEDULER_CREDIT2
343	SchedulerArinc653 Scheduler = C.LIBXL_SCHEDULER_ARINC653
344	SchedulerRTDS     Scheduler = C.LIBXL_SCHEDULER_RTDS
345)
346
347// const char *libxl_scheduler_to_string(libxl_scheduler p);
348func (s Scheduler) String() string {
349	cs := C.libxl_scheduler_to_string(C.libxl_scheduler(s))
350	// No need to free const return value
351
352	return C.GoString(cs)
353}
354
355// int libxl_scheduler_from_string(const char *s, libxl_scheduler *e);
356func (s *Scheduler) FromString(gstr string) (err error) {
357	*s, err = SchedulerFromString(gstr)
358	return
359}
360
361func SchedulerFromString(name string) (s Scheduler, err error) {
362	cname := C.CString(name)
363	defer C.free(unsafe.Pointer(cname))
364
365	var cs C.libxl_scheduler
366
367	ret := C.libxl_scheduler_from_string(cname, &cs)
368	if ret != 0 {
369		err = Error(-ret)
370		return
371	}
372
373	s = Scheduler(cs)
374
375	return
376}
377
378// libxl_cpupoolinfo = Struct("cpupoolinfo", [
379//     ("poolid",      uint32),
380//     ("pool_name",   string),
381//     ("sched",       libxl_scheduler),
382//     ("n_dom",       uint32),
383//     ("cpumap",      libxl_bitmap)
384//     ], dir=DIR_OUT)
385
386type CpupoolInfo struct {
387	Poolid      uint32
388	PoolName    string
389	Scheduler   Scheduler
390	DomainCount int
391	Cpumap      Bitmap
392}
393
394func (cci C.libxl_cpupoolinfo) toGo() (gci CpupoolInfo) {
395	gci.Poolid = uint32(cci.poolid)
396	gci.PoolName = C.GoString(cci.pool_name)
397	gci.Scheduler = Scheduler(cci.sched)
398	gci.DomainCount = int(cci.n_dom)
399	gci.Cpumap = cci.cpumap.toGo()
400
401	return
402}
403
404// libxl_cpupoolinfo * libxl_list_cpupool(libxl_ctx*, int *nb_pool_out);
405// void libxl_cpupoolinfo_list_free(libxl_cpupoolinfo *list, int nb_pool);
406func (Ctx *Context) ListCpupool() (list []CpupoolInfo) {
407	err := Ctx.CheckOpen()
408	if err != nil {
409		return
410	}
411
412	var nbPool C.int
413
414	c_cpupool_list := C.libxl_list_cpupool(Ctx.ctx, &nbPool)
415
416	defer C.libxl_cpupoolinfo_list_free(c_cpupool_list, nbPool)
417
418	if int(nbPool) == 0 {
419		return
420	}
421
422	// Magic
423	cpupoolListSlice := (*[1 << 30]C.libxl_cpupoolinfo)(unsafe.Pointer(c_cpupool_list))[:nbPool:nbPool]
424	for i := range cpupoolListSlice {
425		info := cpupoolListSlice[i].toGo()
426		list = append(list, info)
427	}
428
429	return
430}
431
432// int libxl_cpupool_info(libxl_ctx *ctx, libxl_cpupoolinfo *info, uint32_t poolid);
433func (Ctx *Context) CpupoolInfo(Poolid uint32) (pool CpupoolInfo) {
434	err := Ctx.CheckOpen()
435	if err != nil {
436		return
437	}
438
439	var c_cpupool C.libxl_cpupoolinfo
440
441	ret := C.libxl_cpupool_info(Ctx.ctx, &c_cpupool, C.uint32_t(Poolid))
442	if ret != 0 {
443		err = Error(-ret)
444		return
445	}
446	defer C.libxl_cpupoolinfo_dispose(&c_cpupool)
447
448	pool = c_cpupool.toGo()
449
450	return
451}
452
453// int libxl_cpupool_create(libxl_ctx *ctx, const char *name,
454//                          libxl_scheduler sched,
455//                          libxl_bitmap cpumap, libxl_uuid *uuid,
456//                          uint32_t *poolid);
457// FIXME: uuid
458// FIXME: Setting poolid
459func (Ctx *Context) CpupoolCreate(Name string, Scheduler Scheduler, Cpumap Bitmap) (err error, Poolid uint32) {
460	err = Ctx.CheckOpen()
461	if err != nil {
462		return
463	}
464
465	poolid := C.uint32_t(C.LIBXL_CPUPOOL_POOLID_ANY)
466	name := C.CString(Name)
467	defer C.free(unsafe.Pointer(name))
468
469	// For now, just do what xl does, and make a new uuid every time we create the pool
470	var uuid C.libxl_uuid
471	C.libxl_uuid_generate(&uuid)
472
473	cbm := Cpumap.toC()
474	defer C.libxl_bitmap_dispose(&cbm)
475
476	ret := C.libxl_cpupool_create(Ctx.ctx, name, C.libxl_scheduler(Scheduler),
477		cbm, &uuid, &poolid)
478	if ret != 0 {
479		err = Error(-ret)
480		return
481	}
482
483	Poolid = uint32(poolid)
484
485	return
486}
487
488// int libxl_cpupool_destroy(libxl_ctx *ctx, uint32_t poolid);
489func (Ctx *Context) CpupoolDestroy(Poolid uint32) (err error) {
490	err = Ctx.CheckOpen()
491	if err != nil {
492		return
493	}
494
495	ret := C.libxl_cpupool_destroy(Ctx.ctx, C.uint32_t(Poolid))
496	if ret != 0 {
497		err = Error(-ret)
498		return
499	}
500
501	return
502}
503
504// int libxl_cpupool_cpuadd(libxl_ctx *ctx, uint32_t poolid, int cpu);
505func (Ctx *Context) CpupoolCpuadd(Poolid uint32, Cpu int) (err error) {
506	err = Ctx.CheckOpen()
507	if err != nil {
508		return
509	}
510
511	ret := C.libxl_cpupool_cpuadd(Ctx.ctx, C.uint32_t(Poolid), C.int(Cpu))
512	if ret != 0 {
513		err = Error(-ret)
514		return
515	}
516
517	return
518}
519
520// int libxl_cpupool_cpuadd_cpumap(libxl_ctx *ctx, uint32_t poolid,
521//                                 const libxl_bitmap *cpumap);
522func (Ctx *Context) CpupoolCpuaddCpumap(Poolid uint32, Cpumap Bitmap) (err error) {
523	err = Ctx.CheckOpen()
524	if err != nil {
525		return
526	}
527
528	cbm := Cpumap.toC()
529	defer C.libxl_bitmap_dispose(&cbm)
530
531	ret := C.libxl_cpupool_cpuadd_cpumap(Ctx.ctx, C.uint32_t(Poolid), &cbm)
532	if ret != 0 {
533		err = Error(-ret)
534		return
535	}
536
537	return
538}
539
540// int libxl_cpupool_cpuremove(libxl_ctx *ctx, uint32_t poolid, int cpu);
541func (Ctx *Context) CpupoolCpuremove(Poolid uint32, Cpu int) (err error) {
542	err = Ctx.CheckOpen()
543	if err != nil {
544		return
545	}
546
547	ret := C.libxl_cpupool_cpuremove(Ctx.ctx, C.uint32_t(Poolid), C.int(Cpu))
548	if ret != 0 {
549		err = Error(-ret)
550		return
551	}
552
553	return
554}
555
556// int libxl_cpupool_cpuremove_cpumap(libxl_ctx *ctx, uint32_t poolid,
557//                                    const libxl_bitmap *cpumap);
558func (Ctx *Context) CpupoolCpuremoveCpumap(Poolid uint32, Cpumap Bitmap) (err error) {
559	err = Ctx.CheckOpen()
560	if err != nil {
561		return
562	}
563
564	cbm := Cpumap.toC()
565	defer C.libxl_bitmap_dispose(&cbm)
566
567	ret := C.libxl_cpupool_cpuremove_cpumap(Ctx.ctx, C.uint32_t(Poolid), &cbm)
568	if ret != 0 {
569		err = Error(-ret)
570		return
571	}
572
573	return
574}
575
576// int libxl_cpupool_rename(libxl_ctx *ctx, const char *name, uint32_t poolid);
577func (Ctx *Context) CpupoolRename(Name string, Poolid uint32) (err error) {
578	err = Ctx.CheckOpen()
579	if err != nil {
580		return
581	}
582
583	name := C.CString(Name)
584	defer C.free(unsafe.Pointer(name))
585
586	ret := C.libxl_cpupool_rename(Ctx.ctx, name, C.uint32_t(Poolid))
587	if ret != 0 {
588		err = Error(-ret)
589		return
590	}
591
592	return
593}
594
595// int libxl_cpupool_cpuadd_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus);
596func (Ctx *Context) CpupoolCpuaddNode(Poolid uint32, Node int) (Cpus int, err error) {
597	err = Ctx.CheckOpen()
598	if err != nil {
599		return
600	}
601
602	ccpus := C.int(0)
603
604	ret := C.libxl_cpupool_cpuadd_node(Ctx.ctx, C.uint32_t(Poolid), C.int(Node), &ccpus)
605	if ret != 0 {
606		err = Error(-ret)
607		return
608	}
609
610	Cpus = int(ccpus)
611
612	return
613}
614
615// int libxl_cpupool_cpuremove_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus);
616func (Ctx *Context) CpupoolCpuremoveNode(Poolid uint32, Node int) (Cpus int, err error) {
617	err = Ctx.CheckOpen()
618	if err != nil {
619		return
620	}
621
622	ccpus := C.int(0)
623
624	ret := C.libxl_cpupool_cpuremove_node(Ctx.ctx, C.uint32_t(Poolid), C.int(Node), &ccpus)
625	if ret != 0 {
626		err = Error(-ret)
627		return
628	}
629
630	Cpus = int(ccpus)
631
632	return
633}
634
635// int libxl_cpupool_movedomain(libxl_ctx *ctx, uint32_t poolid, uint32_t domid);
636func (Ctx *Context) CpupoolMovedomain(Poolid uint32, Id Domid) (err error) {
637	err = Ctx.CheckOpen()
638	if err != nil {
639		return
640	}
641
642	ret := C.libxl_cpupool_movedomain(Ctx.ctx, C.uint32_t(Poolid), C.uint32_t(Id))
643	if ret != 0 {
644		err = Error(-ret)
645		return
646	}
647
648	return
649}
650
651//
652// Utility functions
653//
654func (Ctx *Context) CpupoolFindByName(name string) (info CpupoolInfo, found bool) {
655	plist := Ctx.ListCpupool()
656
657	for i := range plist {
658		if plist[i].PoolName == name {
659			found = true
660			info = plist[i]
661			return
662		}
663	}
664	return
665}
666
667func (Ctx *Context) CpupoolMakeFree(Cpumap Bitmap) (err error) {
668	plist := Ctx.ListCpupool()
669
670	for i := range plist {
671		var Intersection Bitmap
672		Intersection = Cpumap.And(plist[i].Cpumap)
673		if !Intersection.IsEmpty() {
674			err = Ctx.CpupoolCpuremoveCpumap(plist[i].Poolid, Intersection)
675			if err != nil {
676				return
677			}
678		}
679	}
680	return
681}
682
683/*
684 * Bitmap operations
685 */
686
687// Return a Go bitmap which is a copy of the referred C bitmap.
688func (cbm C.libxl_bitmap) toGo() (gbm Bitmap) {
689	// Alloc a Go slice for the bytes
690	size := int(cbm.size)
691	gbm.bitmap = make([]C.uint8_t, size)
692
693	// Make a slice pointing to the C array
694	mapslice := (*[1 << 30]C.uint8_t)(unsafe.Pointer(cbm._map))[:size:size]
695
696	// And copy the C array into the Go array
697	copy(gbm.bitmap, mapslice)
698
699	return
700}
701
702// Must be C.libxl_bitmap_dispose'd of afterwards
703func (gbm Bitmap) toC() (cbm C.libxl_bitmap) {
704	C.libxl_bitmap_init(&cbm)
705
706	size := len(gbm.bitmap)
707	cbm._map = (*C.uint8_t)(C.malloc(C.size_t(size)))
708	cbm.size = C.uint32_t(size)
709	if cbm._map == nil {
710		panic("C.calloc failed!")
711	}
712
713	// Make a slice pointing to the C array
714	mapslice := (*[1 << 30]C.uint8_t)(unsafe.Pointer(cbm._map))[:size:size]
715
716	// And copy the Go array into the C array
717	copy(mapslice, gbm.bitmap)
718
719	return
720}
721
722func (bm *Bitmap) Test(bit int) bool {
723	ubit := uint(bit)
724	if bit > bm.Max() || bm.bitmap == nil {
725		return false
726	}
727
728	return (bm.bitmap[bit/8] & (1 << (ubit & 7))) != 0
729}
730
731func (bm *Bitmap) Set(bit int) {
732	ibit := bit / 8
733	if ibit+1 > len(bm.bitmap) {
734		bm.bitmap = append(bm.bitmap, make([]C.uint8_t, ibit+1-len(bm.bitmap))...)
735	}
736
737	bm.bitmap[ibit] |= 1 << (uint(bit) & 7)
738}
739
740func (bm *Bitmap) SetRange(start int, end int) {
741	for i := start; i <= end; i++ {
742		bm.Set(i)
743	}
744}
745
746func (bm *Bitmap) Clear(bit int) {
747	ubit := uint(bit)
748	if bit > bm.Max() || bm.bitmap == nil {
749		return
750	}
751
752	bm.bitmap[bit/8] &= ^(1 << (ubit & 7))
753}
754
755func (bm *Bitmap) ClearRange(start int, end int) {
756	for i := start; i <= end; i++ {
757		bm.Clear(i)
758	}
759}
760
761func (bm *Bitmap) Max() int {
762	return len(bm.bitmap)*8 - 1
763}
764
765func (bm *Bitmap) IsEmpty() bool {
766	for i := 0; i < len(bm.bitmap); i++ {
767		if bm.bitmap[i] != 0 {
768			return false
769		}
770	}
771	return true
772}
773
774func (a Bitmap) And(b Bitmap) (c Bitmap) {
775	var max, min int
776	if len(a.bitmap) > len(b.bitmap) {
777		max = len(a.bitmap)
778		min = len(b.bitmap)
779	} else {
780		max = len(b.bitmap)
781		min = len(a.bitmap)
782	}
783	c.bitmap = make([]C.uint8_t, max)
784
785	for i := 0; i < min; i++ {
786		c.bitmap[i] = a.bitmap[i] & b.bitmap[i]
787	}
788	return
789}
790
791func (bm Bitmap) String() (s string) {
792	lastOnline := false
793	crange := false
794	printed := false
795	var i int
796	/// --x-xxxxx-x -> 2,4-8,10
797	/// --x-xxxxxxx -> 2,4-10
798	for i = 0; i <= bm.Max(); i++ {
799		if bm.Test(i) {
800			if !lastOnline {
801				// Switching offline -> online, print this cpu
802				if printed {
803					s += ","
804				}
805				s += fmt.Sprintf("%d", i)
806				printed = true
807			} else if !crange {
808				// last was online, but we're not in a range; print -
809				crange = true
810				s += "-"
811			} else {
812				// last was online, we're in a range,  nothing else to do
813			}
814			lastOnline = true
815		} else {
816			if lastOnline {
817				// Switching online->offline; do we need to end a range?
818				if crange {
819					s += fmt.Sprintf("%d", i-1)
820				}
821			}
822			lastOnline = false
823			crange = false
824		}
825	}
826	if lastOnline {
827		// Switching online->offline; do we need to end a range?
828		if crange {
829			s += fmt.Sprintf("%d", i-1)
830		}
831	}
832
833	return
834}
835
836/*
837 * Context
838 */
839var Ctx Context
840
841func (Ctx *Context) IsOpen() bool {
842	return Ctx.ctx != nil
843}
844
845func (Ctx *Context) Open() (err error) {
846	if Ctx.ctx != nil {
847		return
848	}
849
850	Ctx.logger = C.xtl_createlogger_stdiostream(C.stderr, C.XTL_ERROR, 0)
851	if Ctx.logger == nil {
852		err = fmt.Errorf("Cannot open stdiostream")
853		return
854	}
855
856	ret := C.libxl_ctx_alloc(&Ctx.ctx, C.LIBXL_VERSION,
857		0, unsafe.Pointer(Ctx.logger))
858
859	if ret != 0 {
860		err = Error(-ret)
861	}
862	return
863}
864
865func (Ctx *Context) Close() (err error) {
866	ret := C.libxl_ctx_free(Ctx.ctx)
867	Ctx.ctx = nil
868
869	if ret != 0 {
870		err = Error(-ret)
871	}
872	C.xtl_logger_destroy(unsafe.Pointer(Ctx.logger))
873	return
874}
875
876func (Ctx *Context) CheckOpen() (err error) {
877	if Ctx.ctx == nil {
878		err = fmt.Errorf("Context not opened")
879	}
880	return
881}
882
883//int libxl_get_max_cpus(libxl_ctx *ctx);
884func (Ctx *Context) GetMaxCpus() (maxCpus int, err error) {
885	err = Ctx.CheckOpen()
886	if err != nil {
887		return
888	}
889
890	ret := C.libxl_get_max_cpus(Ctx.ctx)
891	if ret < 0 {
892		err = Error(-ret)
893		return
894	}
895	maxCpus = int(ret)
896	return
897}
898
899//int libxl_get_online_cpus(libxl_ctx *ctx);
900func (Ctx *Context) GetOnlineCpus() (onCpus int, err error) {
901	err = Ctx.CheckOpen()
902	if err != nil {
903		return
904	}
905
906	ret := C.libxl_get_online_cpus(Ctx.ctx)
907	if ret < 0 {
908		err = Error(-ret)
909		return
910	}
911	onCpus = int(ret)
912	return
913}
914
915//int libxl_get_max_nodes(libxl_ctx *ctx);
916func (Ctx *Context) GetMaxNodes() (maxNodes int, err error) {
917	err = Ctx.CheckOpen()
918	if err != nil {
919		return
920	}
921	ret := C.libxl_get_max_nodes(Ctx.ctx)
922	if ret < 0 {
923		err = Error(-ret)
924		return
925	}
926	maxNodes = int(ret)
927	return
928}
929
930//int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb);
931func (Ctx *Context) GetFreeMemory() (memkb uint64, err error) {
932	err = Ctx.CheckOpen()
933	if err != nil {
934		return
935	}
936	var cmem C.uint64_t
937	ret := C.libxl_get_free_memory(Ctx.ctx, &cmem)
938
939	if ret < 0 {
940		err = Error(-ret)
941		return
942	}
943
944	memkb = uint64(cmem)
945	return
946
947}
948
949//int libxl_get_physinfo(libxl_ctx *ctx, libxl_physinfo *physinfo)
950func (Ctx *Context) GetPhysinfo() (physinfo *Physinfo, err error) {
951	err = Ctx.CheckOpen()
952	if err != nil {
953		return
954	}
955	var cphys C.libxl_physinfo
956	C.libxl_physinfo_init(&cphys)
957	defer C.libxl_physinfo_dispose(&cphys)
958
959	ret := C.libxl_get_physinfo(Ctx.ctx, &cphys)
960
961	if ret < 0 {
962		err = Error(ret)
963		return
964	}
965	physinfo = cphys.toGo()
966
967	return
968}
969
970//const libxl_version_info* libxl_get_version_info(libxl_ctx *ctx);
971func (Ctx *Context) GetVersionInfo() (info *VersionInfo, err error) {
972	err = Ctx.CheckOpen()
973	if err != nil {
974		return
975	}
976
977	var cinfo *C.libxl_version_info
978
979	cinfo = C.libxl_get_version_info(Ctx.ctx)
980
981	info = cinfo.toGo()
982
983	return
984}
985
986func (Ctx *Context) DomainInfo(Id Domid) (di *Dominfo, err error) {
987	err = Ctx.CheckOpen()
988	if err != nil {
989		return
990	}
991
992	var cdi C.libxl_dominfo
993	C.libxl_dominfo_init(&cdi)
994	defer C.libxl_dominfo_dispose(&cdi)
995
996	ret := C.libxl_domain_info(Ctx.ctx, &cdi, C.uint32_t(Id))
997
998	if ret != 0 {
999		err = Error(-ret)
1000		return
1001	}
1002
1003	di = cdi.toGo()
1004
1005	return
1006}
1007
1008func (Ctx *Context) DomainUnpause(Id Domid) (err error) {
1009	err = Ctx.CheckOpen()
1010	if err != nil {
1011		return
1012	}
1013
1014	ret := C.libxl_domain_unpause(Ctx.ctx, C.uint32_t(Id))
1015
1016	if ret != 0 {
1017		err = Error(-ret)
1018	}
1019	return
1020}
1021
1022//int libxl_domain_pause(libxl_ctx *ctx, uint32_t domain);
1023func (Ctx *Context) DomainPause(id Domid) (err error) {
1024	err = Ctx.CheckOpen()
1025	if err != nil {
1026		return
1027	}
1028
1029	ret := C.libxl_domain_pause(Ctx.ctx, C.uint32_t(id))
1030
1031	if ret != 0 {
1032		err = Error(-ret)
1033	}
1034	return
1035}
1036
1037//int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid);
1038func (Ctx *Context) DomainShutdown(id Domid) (err error) {
1039	err = Ctx.CheckOpen()
1040	if err != nil {
1041		return
1042	}
1043
1044	ret := C.libxl_domain_shutdown(Ctx.ctx, C.uint32_t(id))
1045
1046	if ret != 0 {
1047		err = Error(-ret)
1048	}
1049	return
1050}
1051
1052//int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid);
1053func (Ctx *Context) DomainReboot(id Domid) (err error) {
1054	err = Ctx.CheckOpen()
1055	if err != nil {
1056		return
1057	}
1058
1059	ret := C.libxl_domain_reboot(Ctx.ctx, C.uint32_t(id))
1060
1061	if ret != 0 {
1062		err = Error(-ret)
1063	}
1064	return
1065}
1066
1067//libxl_dominfo * libxl_list_domain(libxl_ctx*, int *nb_domain_out);
1068//void libxl_dominfo_list_free(libxl_dominfo *list, int nb_domain);
1069func (Ctx *Context) ListDomain() (glist []Dominfo) {
1070	err := Ctx.CheckOpen()
1071	if err != nil {
1072		return
1073	}
1074
1075	var nbDomain C.int
1076	clist := C.libxl_list_domain(Ctx.ctx, &nbDomain)
1077	defer C.libxl_dominfo_list_free(clist, nbDomain)
1078
1079	if int(nbDomain) == 0 {
1080		return
1081	}
1082
1083	gslice := (*[1 << 30]C.libxl_dominfo)(unsafe.Pointer(clist))[:nbDomain:nbDomain]
1084	for i := range gslice {
1085		info := gslice[i].toGo()
1086		glist = append(glist, *info)
1087	}
1088
1089	return
1090}
1091
1092type Vcpuinfo struct {
1093	Vcpuid     uint32
1094	Cpu        uint32
1095	Online     bool
1096	Blocked    bool
1097	Running    bool
1098	VCpuTime   time.Duration
1099	Cpumap     Bitmap
1100	CpumapSoft Bitmap
1101}
1102
1103func (cvci C.libxl_vcpuinfo) toGo() (gvci Vcpuinfo) {
1104	gvci.Vcpuid = uint32(cvci.vcpuid)
1105	gvci.Cpu = uint32(cvci.cpu)
1106	gvci.Online = bool(cvci.online)
1107	gvci.Blocked = bool(cvci.blocked)
1108	gvci.Running = bool(cvci.running)
1109	gvci.VCpuTime = time.Duration(cvci.vcpu_time)
1110	gvci.Cpumap = cvci.cpumap.toGo()
1111	gvci.CpumapSoft = cvci.cpumap_soft.toGo()
1112
1113	return
1114}
1115
1116//libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid,
1117//				int *nb_vcpu, int *nr_cpus_out);
1118//void libxl_vcpuinfo_list_free(libxl_vcpuinfo *, int nr_vcpus);
1119func (Ctx *Context) ListVcpu(id Domid) (glist []Vcpuinfo) {
1120	err := Ctx.CheckOpen()
1121	if err != nil {
1122		return
1123	}
1124
1125	var nbVcpu C.int
1126	var nrCpu C.int
1127
1128	clist := C.libxl_list_vcpu(Ctx.ctx, C.uint32_t(id), &nbVcpu, &nrCpu)
1129	defer C.libxl_vcpuinfo_list_free(clist, nbVcpu)
1130
1131	if int(nbVcpu) == 0 {
1132		return
1133	}
1134
1135	gslice := (*[1 << 30]C.libxl_vcpuinfo)(unsafe.Pointer(clist))[:nbVcpu:nbVcpu]
1136	for i := range gslice {
1137		info := gslice[i].toGo()
1138		glist = append(glist, info)
1139	}
1140
1141	return
1142}
1143
1144type ConsoleType int
1145
1146const (
1147	ConsoleTypeUnknown = ConsoleType(C.LIBXL_CONSOLE_TYPE_UNKNOWN)
1148	ConsoleTypeSerial  = ConsoleType(C.LIBXL_CONSOLE_TYPE_SERIAL)
1149	ConsoleTypePV      = ConsoleType(C.LIBXL_CONSOLE_TYPE_PV)
1150)
1151
1152func (ct ConsoleType) String() (str string) {
1153	cstr := C.libxl_console_type_to_string(C.libxl_console_type(ct))
1154	str = C.GoString(cstr)
1155
1156	return
1157}
1158
1159//int libxl_console_get_tty(libxl_ctx *ctx, uint32_t domid, int cons_num,
1160//libxl_console_type type, char **path);
1161func (Ctx *Context) ConsoleGetTty(id Domid, consNum int, conType ConsoleType) (path string, err error) {
1162	err = Ctx.CheckOpen()
1163	if err != nil {
1164		return
1165	}
1166
1167	var cpath *C.char
1168	ret := C.libxl_console_get_tty(Ctx.ctx, C.uint32_t(id), C.int(consNum), C.libxl_console_type(conType), &cpath)
1169	if ret != 0 {
1170		err = Error(-ret)
1171		return
1172	}
1173	defer C.free(cpath)
1174
1175	path = C.GoString(cpath)
1176	return
1177}
1178
1179//int libxl_primary_console_get_tty(libxl_ctx *ctx, uint32_t domid_vm,
1180//					char **path);
1181func (Ctx *Context) PrimaryConsoleGetTty(domid uint32) (path string, err error) {
1182	err = Ctx.CheckOpen()
1183	if err != nil {
1184		return
1185	}
1186
1187	var cpath *C.char
1188	ret := C.libxl_primary_console_get_tty(Ctx.ctx, C.uint32_t(domid), &cpath)
1189	if ret != 0 {
1190		err = Error(-ret)
1191		return
1192	}
1193	defer C.free(cpath)
1194
1195	path = C.GoString(cpath)
1196	return
1197}
1198