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 */
17
18// Package xenlight provides bindings to Xen's libxl C library.
19package xenlight
20
21/*
22
23#cgo LDFLAGS: -lxenlight -lyajl -lxentoollog
24#include <stdlib.h>
25#include <libxl.h>
26#include <libxl_utils.h>
27
28#define INVALID_DOMID_TYPED ((uint32_t) INVALID_DOMID)
29
30static const libxl_childproc_hooks childproc_hooks = { .chldowner = libxl_sigchld_owner_mainloop };
31
32void xenlight_set_chldproc(libxl_ctx *ctx) {
33	libxl_childproc_setmode(ctx, &childproc_hooks, NULL);
34}
35*/
36import "C"
37
38/*
39 * Other flags that may be needed at some point:
40 *  -lnl-route-3 -lnl-3
41 *
42 * To get back to static linking:
43 * #cgo LDFLAGS: -lxenlight -lyajl_s -lxengnttab -lxenstore -lxenguest -lxentoollog -lxenevtchn -lxenctrl -lxenforeignmemory -lxencall -lz -luuid -lutil
44 */
45
46import (
47	"fmt"
48	"os"
49	"os/signal"
50	"syscall"
51	"unsafe"
52)
53
54var libxlErrors = map[Error]string{
55	ErrorNonspecific:                  "Non-specific error",
56	ErrorVersion:                      "Wrong version",
57	ErrorFail:                         "Failed",
58	ErrorNi:                           "Not Implemented",
59	ErrorNomem:                        "No memory",
60	ErrorInval:                        "Invalid argument",
61	ErrorBadfail:                      "Bad Fail",
62	ErrorGuestTimedout:                "Guest timed out",
63	ErrorTimedout:                     "Timed out",
64	ErrorNoparavirt:                   "No Paravirtualization",
65	ErrorNotReady:                     "Not ready",
66	ErrorOseventRegFail:               "OS event registration failed",
67	ErrorBufferfull:                   "Buffer full",
68	ErrorUnknownChild:                 "Unknown child",
69	ErrorLockFail:                     "Lock failed",
70	ErrorJsonConfigEmpty:              "JSON config empty",
71	ErrorDeviceExists:                 "Device exists",
72	ErrorCheckpointDevopsDoesNotMatch: "Checkpoint devops does not match",
73	ErrorCheckpointDeviceNotSupported: "Checkpoint device not supported",
74	ErrorVnumaConfigInvalid:           "VNUMA config invalid",
75	ErrorDomainNotfound:               "Domain not found",
76	ErrorAborted:                      "Aborted",
77	ErrorNotfound:                     "Not found",
78	ErrorDomainDestroyed:              "Domain destroyed",
79	ErrorFeatureRemoved:               "Feature removed",
80}
81
82const (
83	DomidInvalid Domid = Domid(C.INVALID_DOMID_TYPED)
84)
85
86func (e Error) Error() string {
87	if s, ok := libxlErrors[e]; ok {
88		return s
89	}
90
91	return fmt.Sprintf("libxl error: %d", e)
92}
93
94// Context represents a libxl_ctx.
95type Context struct {
96	ctx         *C.libxl_ctx
97	logger      *C.xentoollog_logger_stdiostream
98	sigchld     chan os.Signal
99	sigchldDone chan struct{}
100}
101
102// Golang always unmasks SIGCHLD, and internally has ways of
103// distributing SIGCHLD to multiple recipients.  libxl has provision
104// for this model: just tell it when a SIGCHLD happened, and it will
105// look after its own processes.
106//
107// This should "play nicely" with other users of SIGCHLD as long as
108// they don't reap libxl's processes.
109//
110// Every context needs to be notified on each SIGCHLD; so spin up a
111// new goroutine for each context.  If there are a large number of
112// contexts, this means each context will be woken up looking through
113// its own list of children.
114//
115// The alternate would be to register a fork callback, such that the
116// xenlight package can make a single list of all children, and only
117// notify the specific libxl context(s) that have children woken.  But
118// it's not clear to me this will be much more work than having the
119// xenlight go library do the same thing; doing it in separate go
120// threads has the potential to do it in parallel.  Leave that as an
121// optimization for later if it turns out to be a bottleneck.
122func sigchldHandler(ctx *Context) {
123	for _ = range ctx.sigchld {
124		C.libxl_childproc_sigchld_occurred(ctx.ctx)
125	}
126	close(ctx.sigchldDone)
127}
128
129// NewContext returns a new Context.
130func NewContext() (ctx *Context, err error) {
131	ctx = &Context{}
132
133	defer func() {
134		if err != nil {
135			ctx.Close()
136			ctx = nil
137		}
138	}()
139
140	// Create a logger
141	ctx.logger = C.xtl_createlogger_stdiostream(C.stderr, C.XTL_ERROR, 0)
142
143	// Allocate a context
144	ret := C.libxl_ctx_alloc(&ctx.ctx, C.LIBXL_VERSION, 0,
145		(*C.xentoollog_logger)(unsafe.Pointer(ctx.logger)))
146	if ret != 0 {
147		return ctx, Error(ret)
148	}
149
150	// Tell libxl that we'll be dealing with SIGCHLD...
151	C.xenlight_set_chldproc(ctx.ctx)
152
153	// ...and arrange to keep that promise.
154	ctx.sigchld = make(chan os.Signal, 2)
155	ctx.sigchldDone = make(chan struct{}, 1)
156	signal.Notify(ctx.sigchld, syscall.SIGCHLD)
157
158	// This goroutine will run until the ctx.sigchld is closed in
159	// ctx.Close(); at which point it will close ctx.sigchldDone.
160	go sigchldHandler(ctx)
161
162	return ctx, nil
163}
164
165// Close closes the Context.
166func (ctx *Context) Close() error {
167	// Tell our SIGCHLD notifier to shut down, and wait for it to exit
168	// before we free the context.
169	if ctx.sigchld != nil {
170		signal.Stop(ctx.sigchld)
171		close(ctx.sigchld)
172
173		<-ctx.sigchldDone
174
175		ctx.sigchld = nil
176		ctx.sigchldDone = nil
177	}
178
179	if ctx.ctx != nil {
180		ret := C.libxl_ctx_free(ctx.ctx)
181		if ret != 0 {
182			return Error(ret)
183		}
184		ctx.ctx = nil
185	}
186
187	if ctx.logger != nil {
188		C.xtl_logger_destroy((*C.xentoollog_logger)(unsafe.Pointer(ctx.logger)))
189		ctx.logger = nil
190	}
191
192	return nil
193}
194
195/*
196 * Types: Builtins
197 */
198
199type Domid uint32
200
201// NameToDomid returns the Domid for a domain, given its name, if it exists.
202//
203// NameToDomid does not guarantee that the domid associated with name at
204// the time NameToDomid is called is the same as the domid associated with
205// name at the time NameToDomid returns.
206func (Ctx *Context) NameToDomid(name string) (Domid, error) {
207	var domid C.uint32_t
208
209	cname := C.CString(name)
210	defer C.free(unsafe.Pointer(cname))
211
212	if ret := C.libxl_name_to_domid(Ctx.ctx, cname, &domid); ret != 0 {
213		return DomidInvalid, Error(ret)
214	}
215
216	return Domid(domid), nil
217}
218
219// DomidToName returns the name for a domain, given its domid. If there
220// is no domain with the given domid, DomidToName will return the empty
221// string.
222//
223// DomidToName does not guarantee that the name (if any) associated with domid
224// at the time DomidToName is called is the same as the name (if any) associated
225// with domid at the time DomidToName returns.
226func (Ctx *Context) DomidToName(domid Domid) string {
227	cname := C.libxl_domid_to_name(Ctx.ctx, C.uint32_t(domid))
228	defer C.free(unsafe.Pointer(cname))
229
230	return C.GoString(cname)
231}
232
233// Devid is a device ID.
234type Devid int
235
236// Uuid is a domain UUID.
237type Uuid [16]byte
238
239// String formats a Uuid in the form "xxxx-xx-xx-xx-xxxxxx".
240func (u Uuid) String() string {
241	s := "%x%x%x%x-%x%x-%x%x-%x%x-%x%x%x%x%x%x"
242	opts := make([]interface{}, 16)
243
244	for i, v := range u {
245		opts[i] = v
246	}
247
248	return fmt.Sprintf(s, opts...)
249}
250
251func (u *Uuid) fromC(c *C.libxl_uuid) error {
252	for i := range *u {
253		u[i] = byte(c.uuid[i])
254	}
255
256	return nil
257}
258
259func (u *Uuid) toC(cu *C.libxl_uuid) error {
260	for i, v := range u {
261		cu.uuid[i] = C.uint8_t(v)
262	}
263
264	return nil
265}
266
267// defboolVal represents a defbool value.
268type defboolVal int
269
270const (
271	defboolDefault defboolVal = 0
272	defboolFalse   defboolVal = -1
273	defboolTrue    defboolVal = 1
274)
275
276// Defbool represents a libxl_defbool.
277type Defbool struct {
278	val defboolVal
279}
280
281func (d Defbool) String() string {
282	switch d.val {
283	case defboolDefault:
284		return "<default>"
285	case defboolFalse:
286		return "False"
287	case defboolTrue:
288		return "True"
289	}
290
291	return ""
292}
293
294// Set sets the value of the Defbool.
295func (d *Defbool) Set(b bool) {
296	if b {
297		d.val = defboolTrue
298		return
299	}
300	d.val = defboolFalse
301}
302
303// Unset resets the Defbool to default value.
304func (d *Defbool) Unset() {
305	d.val = defboolDefault
306}
307
308// SetIfDefault sets the value of Defbool only if
309// its current value is default.
310func (d *Defbool) SetIfDefault(b bool) {
311	if d.IsDefault() {
312		d.Set(b)
313	}
314}
315
316// IsDefault returns true if the value of Defbool
317// is default, returns false otherwise.
318func (d *Defbool) IsDefault() bool {
319	return d.val == defboolDefault
320}
321
322// Val returns the boolean value associated with the
323// Defbool value. An error is returned if the value
324// is default.
325func (d *Defbool) Val() (bool, error) {
326	if d.IsDefault() {
327		return false, fmt.Errorf("%v: cannot take value of default defbool", ErrorInval)
328	}
329
330	return (d.val > 0), nil
331}
332
333func (d *Defbool) fromC(c *C.libxl_defbool) error {
334	if C.libxl_defbool_is_default(*c) {
335		d.val = defboolDefault
336		return nil
337	}
338
339	if C.libxl_defbool_val(*c) {
340		d.val = defboolTrue
341		return nil
342	}
343
344	d.val = defboolFalse
345
346	return nil
347}
348
349func (d *Defbool) toC(cd *C.libxl_defbool) error {
350	if !d.IsDefault() {
351		val, _ := d.Val()
352		C.libxl_defbool_set(cd, C.bool(val))
353	}
354
355	return nil
356}
357
358// Mac represents a libxl_mac, or simply a MAC address.
359type Mac [6]byte
360
361// String formats a Mac address to string representation.
362func (mac Mac) String() string {
363	s := "%02x:%02x:%02x:%02x:%02x:%02x"
364	opts := make([]interface{}, 6)
365
366	for i, v := range mac {
367		opts[i] = v
368	}
369
370	return fmt.Sprintf(s, opts...)
371}
372
373func (mac *Mac) fromC(cmac *C.libxl_mac) error {
374	for i := range *mac {
375		mac[i] = byte(cmac[i])
376	}
377
378	return nil
379}
380
381func (mac Mac) toC(cm *C.libxl_mac) error {
382	for i, v := range mac {
383		(*cm)[i] = C.uint8_t(v)
384	}
385
386	return nil
387}
388
389// MsVmGenid represents a libxl_ms_vm_genid.
390type MsVmGenid [int(C.LIBXL_MS_VM_GENID_LEN)]byte
391
392func (mvg *MsVmGenid) fromC(cmvg *C.libxl_ms_vm_genid) error {
393	for i := range *mvg {
394		mvg[i] = byte(cmvg.bytes[i])
395	}
396
397	return nil
398}
399
400func (mvg *MsVmGenid) toC(cmvg *C.libxl_ms_vm_genid) error {
401	for i, v := range mvg {
402		cmvg.bytes[i] = C.uint8_t(v)
403	}
404
405	return nil
406}
407
408// EvLink represents a libxl_ev_link.
409//
410// Represented as an empty struct for now, as there is no
411// apparent need for the internals of this type to be exposed
412// through the Go package.
413type EvLink struct{}
414
415func (el *EvLink) fromC(cel *C.libxl_ev_link) error     { return nil }
416func (el *EvLink) toC(cel *C.libxl_ev_link) (err error) { return }
417
418// CpuidPolicyList represents a libxl_cpuid_policy_list.
419//
420// The value of CpuidPolicyList is honored when used as input to libxl. If
421// a struct contains a field of type CpuidPolicyList, that field will be left
422// empty when it is returned from libxl.
423type CpuidPolicyList string
424
425func (cpl *CpuidPolicyList) fromC(ccpl *C.libxl_cpuid_policy_list) error { *cpl = ""; return nil }
426
427func (cpl CpuidPolicyList) toC(ccpl *C.libxl_cpuid_policy_list) error {
428	if cpl == "" {
429		*ccpl = nil
430		return nil
431	}
432
433	s := C.CString(string(cpl))
434	defer C.free(unsafe.Pointer(s))
435
436	ret := C.libxl_cpuid_parse_config(ccpl, s)
437	if ret != 0 {
438		C.libxl_cpuid_dispose(ccpl)
439
440		// libxl_cpuid_parse_config doesn't return a normal libxl error.
441		return ErrorInval
442	}
443
444	return nil
445}
446
447// Hwcap represents a libxl_hwcap.
448type Hwcap [8]uint32
449
450func (hwcap *Hwcap) fromC(chwcap *C.libxl_hwcap) error {
451	for i := range *hwcap {
452		hwcap[i] = uint32(chwcap[i])
453	}
454
455	return nil
456}
457
458func (hwcap *Hwcap) toC(chwcap *C.libxl_hwcap) error {
459	for i, v := range hwcap {
460		(*chwcap)[i] = C.uint32_t(v)
461	}
462
463	return nil
464}
465
466// KeyValueList represents a libxl_key_value_list.
467//
468// Represented as an empty struct for now, as there is no
469// apparent need for this type to be exposed through the
470// Go package.
471type KeyValueList struct{}
472
473func (kvl KeyValueList) fromC(ckvl *C.libxl_key_value_list) error     { return nil }
474func (kvl KeyValueList) toC(ckvl *C.libxl_key_value_list) (err error) { return }
475
476// StringList represents a libxl_string_list.
477type StringList []string
478
479func (sl *StringList) fromC(csl *C.libxl_string_list) error {
480	size := int(C.libxl_string_list_length(csl))
481	list := (*[1 << 30]*C.char)(unsafe.Pointer(csl))[:size:size]
482
483	*sl = make([]string, size)
484
485	for i, v := range list {
486		(*sl)[i] = C.GoString(v)
487	}
488
489	return nil
490}
491
492func (sl StringList) toC(csl *C.libxl_string_list) error {
493	var char *C.char
494	size := len(sl)
495	*csl = (C.libxl_string_list)(C.malloc(C.ulong(size) * C.ulong(unsafe.Sizeof(char))))
496	clist := (*[1 << 30]*C.char)(unsafe.Pointer(csl))[:size:size]
497
498	for i, v := range sl {
499		clist[i] = C.CString(v)
500	}
501
502	return nil
503}
504
505// Bitmap represents a libxl_bitmap.
506//
507// Implement the Go bitmap type such that the underlying data can
508// easily be copied in and out.  NB that we still have to do copies
509// both directions, because cgo runtime restrictions forbid passing to
510// a C function a pointer to a Go-allocated structure which contains a
511// pointer.
512type Bitmap struct {
513	// typedef struct {
514	//     uint32_t size;          /* number of bytes in map */
515	//     uint8_t *map;
516	// } libxl_bitmap;
517	bitmap []C.uint8_t
518}
519
520func (bm *Bitmap) fromC(cbm *C.libxl_bitmap) error {
521	bm.bitmap = nil
522	if size := int(cbm.size); size > 0 {
523		// Alloc a Go slice for the bytes
524		bm.bitmap = make([]C.uint8_t, size)
525
526		// Make a slice pointing to the C array
527		cs := (*[1 << 30]C.uint8_t)(unsafe.Pointer(cbm._map))[:size:size]
528
529		// And copy the C array into the Go array
530		copy(bm.bitmap, cs)
531	}
532
533	return nil
534}
535
536func (bm *Bitmap) toC(cbm *C.libxl_bitmap) error {
537	size := len(bm.bitmap)
538	cbm.size = C.uint32_t(size)
539	if cbm.size > 0 {
540		cbm._map = (*C.uint8_t)(C.malloc(C.ulong(cbm.size) * C.sizeof_uint8_t))
541		cs := (*[1 << 31]C.uint8_t)(unsafe.Pointer(cbm._map))[:size:size]
542
543		copy(cs, bm.bitmap)
544	}
545
546	return nil
547}
548
549func (sr ShutdownReason) String() (str string) {
550	cstr := C.libxl_shutdown_reason_to_string(C.libxl_shutdown_reason(sr))
551	str = C.GoString(cstr)
552
553	return
554}
555
556func (dt DomainType) String() (str string) {
557	cstr := C.libxl_domain_type_to_string(C.libxl_domain_type(dt))
558	str = C.GoString(cstr)
559
560	return
561}
562
563// const char *libxl_scheduler_to_string(libxl_scheduler p);
564func (s Scheduler) String() string {
565	cs := C.libxl_scheduler_to_string(C.libxl_scheduler(s))
566	// No need to free const return value
567
568	return C.GoString(cs)
569}
570
571// int libxl_scheduler_from_string(const char *s, libxl_scheduler *e);
572func (s *Scheduler) FromString(gstr string) (err error) {
573	*s, err = SchedulerFromString(gstr)
574	return
575}
576
577func SchedulerFromString(name string) (s Scheduler, err error) {
578	cname := C.CString(name)
579	defer C.free(unsafe.Pointer(cname))
580
581	var cs C.libxl_scheduler
582
583	ret := C.libxl_scheduler_from_string(cname, &cs)
584	if ret != 0 {
585		err = Error(-ret)
586		return
587	}
588
589	s = Scheduler(cs)
590
591	return
592}
593
594// libxl_cpupoolinfo * libxl_list_cpupool(libxl_ctx*, int *nb_pool_out);
595// void libxl_cpupoolinfo_list_free(libxl_cpupoolinfo *list, int nb_pool);
596func (Ctx *Context) ListCpupool() (list []Cpupoolinfo) {
597	var nbPool C.int
598
599	c_cpupool_list := C.libxl_list_cpupool(Ctx.ctx, &nbPool)
600
601	defer C.libxl_cpupoolinfo_list_free(c_cpupool_list, nbPool)
602
603	if int(nbPool) == 0 {
604		return
605	}
606
607	// Magic
608	cpupoolListSlice := (*[1 << 30]C.libxl_cpupoolinfo)(unsafe.Pointer(c_cpupool_list))[:nbPool:nbPool]
609	for i := range cpupoolListSlice {
610		var info Cpupoolinfo
611		_ = info.fromC(&cpupoolListSlice[i])
612		list = append(list, info)
613	}
614
615	return
616}
617
618// int libxl_cpupool_info(libxl_ctx *ctx, libxl_cpupoolinfo *info, uint32_t poolid);
619func (Ctx *Context) CpupoolInfo(Poolid uint32) (pool Cpupoolinfo, err error) {
620	var c_cpupool C.libxl_cpupoolinfo
621
622	ret := C.libxl_cpupool_info(Ctx.ctx, &c_cpupool, C.uint32_t(Poolid))
623	if ret != 0 {
624		err = Error(-ret)
625		return
626	}
627	defer C.libxl_cpupoolinfo_dispose(&c_cpupool)
628
629	err = pool.fromC(&c_cpupool)
630
631	return
632}
633
634// int libxl_cpupool_create(libxl_ctx *ctx, const char *name,
635//                          libxl_scheduler sched,
636//                          libxl_bitmap cpumap, libxl_uuid *uuid,
637//                          uint32_t *poolid);
638// FIXME: uuid
639// FIXME: Setting poolid
640func (Ctx *Context) CpupoolCreate(Name string, Scheduler Scheduler, Cpumap Bitmap) (err error, Poolid uint32) {
641	poolid := C.uint32_t(C.LIBXL_CPUPOOL_POOLID_ANY)
642	name := C.CString(Name)
643	defer C.free(unsafe.Pointer(name))
644
645	// For now, just do what xl does, and make a new uuid every time we create the pool
646	var uuid C.libxl_uuid
647	C.libxl_uuid_generate(&uuid)
648
649	var cbm C.libxl_bitmap
650	if err = Cpumap.toC(&cbm); err != nil {
651		return
652	}
653	defer C.libxl_bitmap_dispose(&cbm)
654
655	ret := C.libxl_cpupool_create(Ctx.ctx, name, C.libxl_scheduler(Scheduler),
656		cbm, &uuid, &poolid)
657	if ret != 0 {
658		err = Error(-ret)
659		return
660	}
661
662	Poolid = uint32(poolid)
663
664	return
665}
666
667// int libxl_cpupool_destroy(libxl_ctx *ctx, uint32_t poolid);
668func (Ctx *Context) CpupoolDestroy(Poolid uint32) (err error) {
669	ret := C.libxl_cpupool_destroy(Ctx.ctx, C.uint32_t(Poolid))
670	if ret != 0 {
671		err = Error(-ret)
672		return
673	}
674
675	return
676}
677
678// int libxl_cpupool_cpuadd(libxl_ctx *ctx, uint32_t poolid, int cpu);
679func (Ctx *Context) CpupoolCpuadd(Poolid uint32, Cpu int) (err error) {
680	ret := C.libxl_cpupool_cpuadd(Ctx.ctx, C.uint32_t(Poolid), C.int(Cpu))
681	if ret != 0 {
682		err = Error(-ret)
683		return
684	}
685
686	return
687}
688
689// int libxl_cpupool_cpuadd_cpumap(libxl_ctx *ctx, uint32_t poolid,
690//                                 const libxl_bitmap *cpumap);
691func (Ctx *Context) CpupoolCpuaddCpumap(Poolid uint32, Cpumap Bitmap) (err error) {
692	var cbm C.libxl_bitmap
693	if err = Cpumap.toC(&cbm); err != nil {
694		return
695	}
696	defer C.libxl_bitmap_dispose(&cbm)
697
698	ret := C.libxl_cpupool_cpuadd_cpumap(Ctx.ctx, C.uint32_t(Poolid), &cbm)
699	if ret != 0 {
700		err = Error(-ret)
701		return
702	}
703
704	return
705}
706
707// int libxl_cpupool_cpuremove(libxl_ctx *ctx, uint32_t poolid, int cpu);
708func (Ctx *Context) CpupoolCpuremove(Poolid uint32, Cpu int) (err error) {
709	ret := C.libxl_cpupool_cpuremove(Ctx.ctx, C.uint32_t(Poolid), C.int(Cpu))
710	if ret != 0 {
711		err = Error(-ret)
712		return
713	}
714
715	return
716}
717
718// int libxl_cpupool_cpuremove_cpumap(libxl_ctx *ctx, uint32_t poolid,
719//                                    const libxl_bitmap *cpumap);
720func (Ctx *Context) CpupoolCpuremoveCpumap(Poolid uint32, Cpumap Bitmap) (err error) {
721	var cbm C.libxl_bitmap
722	if err = Cpumap.toC(&cbm); err != nil {
723		return
724	}
725	defer C.libxl_bitmap_dispose(&cbm)
726
727	ret := C.libxl_cpupool_cpuremove_cpumap(Ctx.ctx, C.uint32_t(Poolid), &cbm)
728	if ret != 0 {
729		err = Error(-ret)
730		return
731	}
732
733	return
734}
735
736// int libxl_cpupool_rename(libxl_ctx *ctx, const char *name, uint32_t poolid);
737func (Ctx *Context) CpupoolRename(Name string, Poolid uint32) (err error) {
738	name := C.CString(Name)
739	defer C.free(unsafe.Pointer(name))
740
741	ret := C.libxl_cpupool_rename(Ctx.ctx, name, C.uint32_t(Poolid))
742	if ret != 0 {
743		err = Error(-ret)
744		return
745	}
746
747	return
748}
749
750// int libxl_cpupool_cpuadd_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus);
751func (Ctx *Context) CpupoolCpuaddNode(Poolid uint32, Node int) (Cpus int, err error) {
752	ccpus := C.int(0)
753
754	ret := C.libxl_cpupool_cpuadd_node(Ctx.ctx, C.uint32_t(Poolid), C.int(Node), &ccpus)
755	if ret != 0 {
756		err = Error(-ret)
757		return
758	}
759
760	Cpus = int(ccpus)
761
762	return
763}
764
765// int libxl_cpupool_cpuremove_node(libxl_ctx *ctx, uint32_t poolid, int node, int *cpus);
766func (Ctx *Context) CpupoolCpuremoveNode(Poolid uint32, Node int) (Cpus int, err error) {
767	ccpus := C.int(0)
768
769	ret := C.libxl_cpupool_cpuremove_node(Ctx.ctx, C.uint32_t(Poolid), C.int(Node), &ccpus)
770	if ret != 0 {
771		err = Error(-ret)
772		return
773	}
774
775	Cpus = int(ccpus)
776
777	return
778}
779
780// int libxl_cpupool_movedomain(libxl_ctx *ctx, uint32_t poolid, uint32_t domid);
781func (Ctx *Context) CpupoolMovedomain(Poolid uint32, Id Domid) (err error) {
782	ret := C.libxl_cpupool_movedomain(Ctx.ctx, C.uint32_t(Poolid), C.uint32_t(Id))
783	if ret != 0 {
784		err = Error(-ret)
785		return
786	}
787
788	return
789}
790
791//
792// Utility functions
793//
794func (Ctx *Context) CpupoolFindByName(name string) (info Cpupoolinfo, found bool) {
795	plist := Ctx.ListCpupool()
796
797	for i := range plist {
798		if plist[i].PoolName == name {
799			found = true
800			info = plist[i]
801			return
802		}
803	}
804	return
805}
806
807func (Ctx *Context) CpupoolMakeFree(Cpumap Bitmap) (err error) {
808	plist := Ctx.ListCpupool()
809
810	for i := range plist {
811		var Intersection Bitmap
812		Intersection = Cpumap.And(plist[i].Cpumap)
813		if !Intersection.IsEmpty() {
814			err = Ctx.CpupoolCpuremoveCpumap(plist[i].Poolid, Intersection)
815			if err != nil {
816				return
817			}
818		}
819	}
820	return
821}
822
823/*
824 * Bitmap operations
825 */
826
827func (bm *Bitmap) Test(bit int) bool {
828	ubit := uint(bit)
829	if bit > bm.Max() || bm.bitmap == nil {
830		return false
831	}
832
833	return (bm.bitmap[bit/8] & (1 << (ubit & 7))) != 0
834}
835
836func (bm *Bitmap) Set(bit int) {
837	ibit := bit / 8
838	if ibit+1 > len(bm.bitmap) {
839		bm.bitmap = append(bm.bitmap, make([]C.uint8_t, ibit+1-len(bm.bitmap))...)
840	}
841
842	bm.bitmap[ibit] |= 1 << (uint(bit) & 7)
843}
844
845func (bm *Bitmap) SetRange(start int, end int) {
846	for i := start; i <= end; i++ {
847		bm.Set(i)
848	}
849}
850
851func (bm *Bitmap) Clear(bit int) {
852	ubit := uint(bit)
853	if bit > bm.Max() || bm.bitmap == nil {
854		return
855	}
856
857	bm.bitmap[bit/8] &= ^(1 << (ubit & 7))
858}
859
860func (bm *Bitmap) ClearRange(start int, end int) {
861	for i := start; i <= end; i++ {
862		bm.Clear(i)
863	}
864}
865
866func (bm *Bitmap) Max() int {
867	return len(bm.bitmap)*8 - 1
868}
869
870func (bm *Bitmap) IsEmpty() bool {
871	for i := 0; i < len(bm.bitmap); i++ {
872		if bm.bitmap[i] != 0 {
873			return false
874		}
875	}
876	return true
877}
878
879func (a Bitmap) And(b Bitmap) (c Bitmap) {
880	var max, min int
881	if len(a.bitmap) > len(b.bitmap) {
882		max = len(a.bitmap)
883		min = len(b.bitmap)
884	} else {
885		max = len(b.bitmap)
886		min = len(a.bitmap)
887	}
888	c.bitmap = make([]C.uint8_t, max)
889
890	for i := 0; i < min; i++ {
891		c.bitmap[i] = a.bitmap[i] & b.bitmap[i]
892	}
893	return
894}
895
896func (bm Bitmap) String() (s string) {
897	lastOnline := false
898	crange := false
899	printed := false
900	var i int
901	/// --x-xxxxx-x -> 2,4-8,10
902	/// --x-xxxxxxx -> 2,4-10
903	for i = 0; i <= bm.Max(); i++ {
904		if bm.Test(i) {
905			if !lastOnline {
906				// Switching offline -> online, print this cpu
907				if printed {
908					s += ","
909				}
910				s += fmt.Sprintf("%d", i)
911				printed = true
912			} else if !crange {
913				// last was online, but we're not in a range; print -
914				crange = true
915				s += "-"
916			} else {
917				// last was online, we're in a range,  nothing else to do
918			}
919			lastOnline = true
920		} else {
921			if lastOnline {
922				// Switching online->offline; do we need to end a range?
923				if crange {
924					s += fmt.Sprintf("%d", i-1)
925				}
926			}
927			lastOnline = false
928			crange = false
929		}
930	}
931	if lastOnline {
932		// Switching online->offline; do we need to end a range?
933		if crange {
934			s += fmt.Sprintf("%d", i-1)
935		}
936	}
937
938	return
939}
940
941//int libxl_get_max_cpus(libxl_ctx *ctx);
942func (Ctx *Context) GetMaxCpus() (maxCpus int, err error) {
943	ret := C.libxl_get_max_cpus(Ctx.ctx)
944	if ret < 0 {
945		err = Error(-ret)
946		return
947	}
948	maxCpus = int(ret)
949	return
950}
951
952//int libxl_get_online_cpus(libxl_ctx *ctx);
953func (Ctx *Context) GetOnlineCpus() (onCpus int, err error) {
954	ret := C.libxl_get_online_cpus(Ctx.ctx)
955	if ret < 0 {
956		err = Error(-ret)
957		return
958	}
959	onCpus = int(ret)
960	return
961}
962
963//int libxl_get_max_nodes(libxl_ctx *ctx);
964func (Ctx *Context) GetMaxNodes() (maxNodes int, err error) {
965	ret := C.libxl_get_max_nodes(Ctx.ctx)
966	if ret < 0 {
967		err = Error(-ret)
968		return
969	}
970	maxNodes = int(ret)
971	return
972}
973
974//int libxl_get_free_memory(libxl_ctx *ctx, uint64_t *memkb);
975func (Ctx *Context) GetFreeMemory() (memkb uint64, err error) {
976	var cmem C.uint64_t
977	ret := C.libxl_get_free_memory(Ctx.ctx, &cmem)
978
979	if ret < 0 {
980		err = Error(-ret)
981		return
982	}
983
984	memkb = uint64(cmem)
985	return
986
987}
988
989//int libxl_get_physinfo(libxl_ctx *ctx, libxl_physinfo *physinfo)
990func (Ctx *Context) GetPhysinfo() (physinfo *Physinfo, err error) {
991	var cphys C.libxl_physinfo
992	C.libxl_physinfo_init(&cphys)
993	defer C.libxl_physinfo_dispose(&cphys)
994
995	ret := C.libxl_get_physinfo(Ctx.ctx, &cphys)
996
997	if ret < 0 {
998		err = Error(ret)
999		return
1000	}
1001	err = physinfo.fromC(&cphys)
1002
1003	return
1004}
1005
1006//const libxl_version_info* libxl_get_version_info(libxl_ctx *ctx);
1007func (Ctx *Context) GetVersionInfo() (info *VersionInfo, err error) {
1008	var cinfo *C.libxl_version_info
1009
1010	cinfo = C.libxl_get_version_info(Ctx.ctx)
1011
1012	err = info.fromC(cinfo)
1013
1014	return
1015}
1016
1017func (Ctx *Context) DomainInfo(Id Domid) (di *Dominfo, err error) {
1018	var cdi C.libxl_dominfo
1019	C.libxl_dominfo_init(&cdi)
1020	defer C.libxl_dominfo_dispose(&cdi)
1021
1022	ret := C.libxl_domain_info(Ctx.ctx, &cdi, C.uint32_t(Id))
1023
1024	if ret != 0 {
1025		err = Error(-ret)
1026		return
1027	}
1028
1029	err = di.fromC(&cdi)
1030
1031	return
1032}
1033
1034func (Ctx *Context) DomainUnpause(Id Domid) (err error) {
1035	ret := C.libxl_domain_unpause(Ctx.ctx, C.uint32_t(Id), nil)
1036
1037	if ret != 0 {
1038		err = Error(-ret)
1039	}
1040	return
1041}
1042
1043//int libxl_domain_pause(libxl_ctx *ctx, uint32_t domain);
1044func (Ctx *Context) DomainPause(id Domid) (err error) {
1045	ret := C.libxl_domain_pause(Ctx.ctx, C.uint32_t(id), nil)
1046
1047	if ret != 0 {
1048		err = Error(-ret)
1049	}
1050	return
1051}
1052
1053//int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid);
1054func (Ctx *Context) DomainShutdown(id Domid) (err error) {
1055	ret := C.libxl_domain_shutdown(Ctx.ctx, C.uint32_t(id), nil)
1056
1057	if ret != 0 {
1058		err = Error(-ret)
1059	}
1060	return
1061}
1062
1063//int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid);
1064func (Ctx *Context) DomainReboot(id Domid) (err error) {
1065	ret := C.libxl_domain_reboot(Ctx.ctx, C.uint32_t(id), nil)
1066
1067	if ret != 0 {
1068		err = Error(-ret)
1069	}
1070	return
1071}
1072
1073//libxl_dominfo * libxl_list_domain(libxl_ctx*, int *nb_domain_out);
1074//void libxl_dominfo_list_free(libxl_dominfo *list, int nb_domain);
1075func (Ctx *Context) ListDomain() (glist []Dominfo) {
1076	var nbDomain C.int
1077	clist := C.libxl_list_domain(Ctx.ctx, &nbDomain)
1078	defer C.libxl_dominfo_list_free(clist, nbDomain)
1079
1080	if int(nbDomain) == 0 {
1081		return
1082	}
1083
1084	gslice := (*[1 << 30]C.libxl_dominfo)(unsafe.Pointer(clist))[:nbDomain:nbDomain]
1085	for i := range gslice {
1086		var info Dominfo
1087		_ = info.fromC(&gslice[i])
1088		glist = append(glist, info)
1089	}
1090
1091	return
1092}
1093
1094//libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid,
1095//				int *nb_vcpu, int *nr_cpus_out);
1096//void libxl_vcpuinfo_list_free(libxl_vcpuinfo *, int nr_vcpus);
1097func (Ctx *Context) ListVcpu(id Domid) (glist []Vcpuinfo) {
1098	var nbVcpu C.int
1099	var nrCpu C.int
1100
1101	clist := C.libxl_list_vcpu(Ctx.ctx, C.uint32_t(id), &nbVcpu, &nrCpu)
1102	defer C.libxl_vcpuinfo_list_free(clist, nbVcpu)
1103
1104	if int(nbVcpu) == 0 {
1105		return
1106	}
1107
1108	gslice := (*[1 << 30]C.libxl_vcpuinfo)(unsafe.Pointer(clist))[:nbVcpu:nbVcpu]
1109	for i := range gslice {
1110		var info Vcpuinfo
1111		_ = info.fromC(&gslice[i])
1112		glist = append(glist, info)
1113	}
1114
1115	return
1116}
1117
1118func (ct ConsoleType) String() (str string) {
1119	cstr := C.libxl_console_type_to_string(C.libxl_console_type(ct))
1120	str = C.GoString(cstr)
1121
1122	return
1123}
1124
1125//int libxl_console_get_tty(libxl_ctx *ctx, uint32_t domid, int cons_num,
1126//libxl_console_type type, char **path);
1127func (Ctx *Context) ConsoleGetTty(id Domid, consNum int, conType ConsoleType) (path string, err error) {
1128	var cpath *C.char
1129	ret := C.libxl_console_get_tty(Ctx.ctx, C.uint32_t(id), C.int(consNum), C.libxl_console_type(conType), &cpath)
1130	if ret != 0 {
1131		err = Error(-ret)
1132		return
1133	}
1134	defer C.free(unsafe.Pointer(cpath))
1135
1136	path = C.GoString(cpath)
1137	return
1138}
1139
1140//int libxl_primary_console_get_tty(libxl_ctx *ctx, uint32_t domid_vm,
1141//					char **path);
1142func (Ctx *Context) PrimaryConsoleGetTty(domid uint32) (path string, err error) {
1143	var cpath *C.char
1144	ret := C.libxl_primary_console_get_tty(Ctx.ctx, C.uint32_t(domid), &cpath)
1145	if ret != 0 {
1146		err = Error(-ret)
1147		return
1148	}
1149	defer C.free(unsafe.Pointer(cpath))
1150
1151	path = C.GoString(cpath)
1152	return
1153}
1154
1155// DeviceNicAdd adds a nic to a domain.
1156func (Ctx *Context) DeviceNicAdd(domid Domid, nic *DeviceNic) error {
1157	var cnic C.libxl_device_nic
1158
1159	if err := nic.toC(&cnic); err != nil {
1160		return err
1161	}
1162	defer C.libxl_device_nic_dispose(&cnic)
1163
1164	ret := C.libxl_device_nic_add(Ctx.ctx, C.uint32_t(domid), &cnic, nil)
1165	if ret != 0 {
1166		return Error(ret)
1167	}
1168
1169	return nil
1170}
1171
1172// DeviceNicRemove removes a nic from a domain.
1173func (Ctx *Context) DeviceNicRemove(domid Domid, nic *DeviceNic) error {
1174	var cnic C.libxl_device_nic
1175
1176	if err := nic.toC(&cnic); err != nil {
1177		return err
1178	}
1179	defer C.libxl_device_nic_dispose(&cnic)
1180
1181	ret := C.libxl_device_nic_remove(Ctx.ctx, C.uint32_t(domid), &cnic, nil)
1182	if ret != 0 {
1183		return Error(ret)
1184	}
1185
1186	return nil
1187}
1188
1189// DevicePciAdd is used to passthrough a PCI device to a domain.
1190func (Ctx *Context) DevicePciAdd(domid Domid, pci *DevicePci) error {
1191	var cpci C.libxl_device_pci
1192
1193	if err := pci.toC(&cpci); err != nil {
1194		return err
1195	}
1196	defer C.libxl_device_pci_dispose(&cpci)
1197
1198	ret := C.libxl_device_pci_add(Ctx.ctx, C.uint32_t(domid), &cpci, nil)
1199	if ret != 0 {
1200		return Error(ret)
1201	}
1202
1203	return nil
1204}
1205
1206// DevicePciRemove removes a PCI device from a domain.
1207func (Ctx *Context) DevicePciRemove(domid Domid, pci *DevicePci) error {
1208	var cpci C.libxl_device_pci
1209
1210	if err := pci.toC(&cpci); err != nil {
1211		return err
1212	}
1213	defer C.libxl_device_pci_dispose(&cpci)
1214
1215	ret := C.libxl_device_pci_remove(Ctx.ctx, C.uint32_t(domid), &cpci, nil)
1216	if ret != 0 {
1217		return Error(ret)
1218	}
1219
1220	return nil
1221}
1222
1223// DeviceUsbdevAdd adds a USB device to a domain.
1224func (Ctx *Context) DeviceUsbdevAdd(domid Domid, usbdev *DeviceUsbdev) error {
1225	var cusbdev C.libxl_device_usbdev
1226
1227	if err := usbdev.toC(&cusbdev); err != nil {
1228		return err
1229	}
1230	defer C.libxl_device_usbdev_dispose(&cusbdev)
1231
1232	ret := C.libxl_device_usbdev_add(Ctx.ctx, C.uint32_t(domid), &cusbdev, nil)
1233	if ret != 0 {
1234		return Error(ret)
1235	}
1236
1237	return nil
1238}
1239
1240// DeviceUsbdevRemove removes a USB device from a domain.
1241func (Ctx *Context) DeviceUsbdevRemove(domid Domid, usbdev *DeviceUsbdev) error {
1242	var cusbdev C.libxl_device_usbdev
1243
1244	if err := usbdev.toC(&cusbdev); err != nil {
1245		return err
1246	}
1247	defer C.libxl_device_usbdev_dispose(&cusbdev)
1248
1249	ret := C.libxl_device_usbdev_remove(Ctx.ctx, C.uint32_t(domid), &cusbdev, nil)
1250	if ret != 0 {
1251		return Error(ret)
1252	}
1253
1254	return nil
1255}
1256
1257// DomainCreateNew creates a new domain.
1258func (Ctx *Context) DomainCreateNew(config *DomainConfig) (Domid, error) {
1259	var cdomid C.uint32_t
1260	var cconfig C.libxl_domain_config
1261	err := config.toC(&cconfig)
1262	if err != nil {
1263		return Domid(0), fmt.Errorf("converting domain config to C: %v", err)
1264	}
1265	defer C.libxl_domain_config_dispose(&cconfig)
1266
1267	ret := C.libxl_domain_create_new(Ctx.ctx, &cconfig, &cdomid, nil, nil)
1268	if ret != 0 {
1269		return Domid(0), Error(ret)
1270	}
1271
1272	return Domid(cdomid), nil
1273}
1274