This commit is contained in:
Joshua Bell 2026-01-26 21:41:26 -06:00
parent 07ec5529ac
commit f501abe660
532 changed files with 271781 additions and 0 deletions

1
vendor/github.com/digitalocean/go-libvirt/.gitignore generated vendored Normal file
View file

@ -0,0 +1 @@
.idea/

28
vendor/github.com/digitalocean/go-libvirt/AUTHORS generated vendored Normal file
View file

@ -0,0 +1,28 @@
Maintainer
----------
DigitalOcean, Inc
Original Authors
----------------
Ben LeMasurier <blemasurier@digitalocean.com>
Matt Layher <mlayher@digitalocean.com>
Contributors
------------
Justin Kim <justin@digitalocean.com>
Ricky Medina <rm@do.co>
Charlie Drage <charlie@charliedrage.com>
Michael Koppmann <me@mkoppmann.at>
Simarpreet Singh <simar@linux.com>
Alexander Polyakov <apolyakov@beget.com>
Amanda Andrade <amanda.andrade@serpro.gov.br>
Geoff Hickey <ghickey@digitalocean.com>
Yuriy Taraday <yorik.sar@gmail.com>
Sylvain Baubeau <sbaubeau@redhat.com>
David Schneider <dsbrng25b@gmail.com>
Alec Hothan <ahothan@gmail.com>
Akos Varga <vrgakos@gmail.com>
Peter Kurfer <peter.kurfer@gmail.com>
Sam Roberts <sroberts@digitalocean.com>
Moritz Wanzenböck <moritz.wanzenboeck@linbit.com>
Jenni Griesmann <jgriesmann@digitalocean.com>

View file

@ -0,0 +1,27 @@
Contributing
============
The `go-libvirt` project makes use of the [GitHub Flow](https://guides.github.com/introduction/flow/)
for contributions.
If you'd like to contribute to the project, please
[open an issue](https://github.com/digitalocean/go-libvirt/issues/new) or find an
[existing issue](https://github.com/digitalocean/go-libvirt/issues) that you'd like
to take on. This ensures that efforts are not duplicated, and that a new feature
aligns with the focus of the rest of the repository.
Once your suggestion has been submitted and discussed, please be sure that your
code meets the following criteria:
- code is completely `gofmt`'d
- new features or codepaths have appropriate test coverage
- `go test ./...` passes
- `go vet ./...` passes
- `golint ./...` returns no warnings, including documentation comment warnings
In addition, if this is your first time contributing to the `go-libvirt` project,
add your name and email address to the
[AUTHORS](https://github.com/digitalocean/go-libvirt/blob/master/AUTHORS) file
under the "Contributors" section using the format:
`First Last <email@example.com>`.
Finally, submit a pull request for review!

195
vendor/github.com/digitalocean/go-libvirt/LICENSE.md generated vendored Normal file
View file

@ -0,0 +1,195 @@
Apache License
==============
_Version 2.0, January 2004_
_&lt;<http://www.apache.org/licenses/>&gt;_
### Terms and Conditions for use, reproduction, and distribution
#### 1. Definitions
“License” shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
“Licensor” shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
“Legal Entity” shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, “control” means **(i)** the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
outstanding shares, or **(iii)** beneficial ownership of such entity.
“You” (or “Your”) shall mean an individual or Legal Entity exercising
permissions granted by this License.
“Source” form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
“Object” form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
“Work” shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
“Derivative Works” shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
“Contribution” shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
“submitted” means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as “Not a Contribution.”
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
#### 2. Grant of Copyright License
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
#### 3. Grant of Patent License
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
#### 4. Redistribution
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
this License; and
* **(b)** You must cause any modified files to carry prominent notices stating that You
changed the files; and
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
#### 5. Submission of Contributions
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
#### 6. Trademarks
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
#### 7. Disclaimer of Warranty
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
#### 8. Limitation of Liability
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
#### 9. Accepting Warranty or Additional Liability
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
_END OF TERMS AND CONDITIONS_
### APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets `[]` replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same “printed page” as the copyright notice for easier identification within
third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

280
vendor/github.com/digitalocean/go-libvirt/README.md generated vendored Normal file
View file

@ -0,0 +1,280 @@
libvirt
[![GoDoc](http://godoc.org/github.com/digitalocean/go-libvirt?status.svg)](http://godoc.org/github.com/digitalocean/go-libvirt)
[![Build Status](https://github.com/digitalocean/go-libvirt/actions/workflows/main.yml/badge.svg)](https://github.com/digitalocean/go-libvirt/actions/)
[![Report Card](https://goreportcard.com/badge/github.com/digitalocean/go-libvirt)](https://goreportcard.com/report/github.com/digitalocean/go-libvirt)
====
Package `go-libvirt` provides a pure Go interface for interacting with libvirt.
Rather than using libvirt's C bindings, this package makes use of
libvirt's RPC interface, as documented [here](https://libvirt.org/kbase/internals/rpc.html).
Connections to the libvirt server may be local, or remote. RPC packets are encoded
using the XDR standard as defined by [RFC 4506](https://tools.ietf.org/html/rfc4506.html).
libvirt's RPC interface is quite extensive, and changes from one version to the
next, so this project uses a pair of code generators to build the go bindings.
The code generators should be run whenever you want to build go-libvirt for a
new version of libvirt. See the next section for directions on re-generating
go-libvirt.
[Pull requests are welcome](https://github.com/digitalocean/go-libvirt/blob/master/CONTRIBUTING.md)!
Running the Code Generators
---------------------------
The code generator doesn't run automatically when you build go-libvirt. It's
meant to be run manually any time you change the version of libvirt you're
using. When you download go-libvirt it will come with generated files
corresponding to a particular version of libvirt. You can use the library as-is,
but the generated code may be missing libvirt functions, if you're using a newer
version of libvirt, or it may have extra functions that will return
'unimplemented' errors if you try to call them. If this is a problem, you should
re-run the code generator. To do this, follow these steps:
- First, download a copy of the libvirt sources corresponding to the version you
want to use.
- Change directories into where you've unpacked your distribution of libvirt.
- The second step depends on the version of libvirt you'd like to build against.
It's not necessary to actually build libvirt, but it is necessary to run libvirt's
"configure" step because it generates required files.
- For libvirt < v6.7.0:
- `$ mkdir build; cd build`
- `$ ../autogen.sh`
- For libvirt >= v6.7.0:
- `$ meson setup build`
- Finally, set the environment variable `LIBVIRT_SOURCE` to the directory you
put libvirt into, and run `go generate ./...` from the go-libvirt directory.
This runs both of the go-libvirt's code generators.
How to Use This Library
-----------------------
Once you've vendored go-libvirt into your project, you'll probably want to call
some libvirt functions. There's some example code below showing how to connect
to libvirt and make one such call, but once you get past the introduction you'll
next want to call some other libvirt functions. How do you find them?
Start with the [libvirt API reference](https://libvirt.org/html/index.html).
Let's say you want to gracefully shutdown a VM, and after reading through the
libvirt docs you determine that virDomainShutdown() is the function you want to
call to do that. Where's that function in go-libvirt? We transform the names
slightly when building the go bindings. There's no need for a global prefix like
"vir" in Go, since all our functions are inside the package namespace, so we
drop it. That means the Go function for `virDomainShutdown()` is just `DomainShutdown()`,
and sure enough, you can find the Go function `DomainShutdown()` in libvirt.gen.go,
with parameters and return values equivalent to those documented in the API
reference.
Suppose you then decide you need more control over your shutdown, so you switch
over to `virDomainShutdownFlags()`. As its name suggests, this function takes a
flag parameter which has possible values specified in an enum called
`virDomainShutdownFlagValues`. Flag types like this are a little tricky for the
code generator, because the C functions just take an integer type - only the
libvirt documentation actually ties the flags to the enum types. In most cases
though we're able to generate a wrapper function with a distinct flag type,
making it easier for Go tooling to suggest possible flag values while you're
working. Checking the documentation for this function:
`godoc github.com/digitalocean/go-libvirt DomainShutdownFlags`
returns this:
`func (l *Libvirt) DomainShutdownFlags(Dom Domain, Flags DomainShutdownFlagValues) (err error)`
If you want to see the possible flag values, `godoc` can help again:
```
$ godoc github.com/digitalocean/go-libvirt DomainShutdownFlagValues
type DomainShutdownFlagValues int32
DomainShutdownFlagValues as declared in libvirt/libvirt-domain.h:1121
const (
DomainShutdownDefault DomainShutdownFlagValues = iota
DomainShutdownAcpiPowerBtn DomainShutdownFlagValues = 1
DomainShutdownGuestAgent DomainShutdownFlagValues = 2
DomainShutdownInitctl DomainShutdownFlagValues = 4
DomainShutdownSignal DomainShutdownFlagValues = 8
DomainShutdownParavirt DomainShutdownFlagValues = 16
)
DomainShutdownFlagValues enumeration from libvirt/libvirt-domain.h:1121
```
One other suggestion: most of the code in go-libvirt is now generated, but a few
hand-written routines still exist in libvirt.go, and wrap calls to the generated
code with slightly different parameters or return values. We suggest avoiding
these hand-written routines and calling the generated routines in libvirt.gen.go
instead. Over time these handwritten routines will be removed from go-libvirt.
Warning
-------
While these package are reasonably well-tested and have seen some use inside of
DigitalOcean, there may be subtle bugs which could cause the packages to act
in unexpected ways. Use at your own risk!
In addition, the API is not considered stable at this time. If you would like
to include package `libvirt` in a project, we highly recommend vendoring it into
your project.
Example
-------
```go
package main
import (
"fmt"
"log"
"net"
"time"
"github.com/digitalocean/go-libvirt"
)
func main() {
// This dials libvirt on the local machine, but you can substitute the first
// two parameters with "tcp", "<ip address>:<port>" to connect to libvirt on
// a remote machine.
c, err := net.DialTimeout("unix", "/var/run/libvirt/libvirt-sock", 2*time.Second)
if err != nil {
log.Fatalf("failed to dial libvirt: %v", err)
}
l := libvirt.New(c)
if err := l.Connect(); err != nil {
log.Fatalf("failed to connect: %v", err)
}
v, err := l.Version()
if err != nil {
log.Fatalf("failed to retrieve libvirt version: %v", err)
}
fmt.Println("Version:", v)
domains, err := l.Domains()
if err != nil {
log.Fatalf("failed to retrieve domains: %v", err)
}
fmt.Println("ID\tName\t\tUUID")
fmt.Printf("--------------------------------------------------------\n")
for _, d := range domains {
fmt.Printf("%d\t%s\t%x\n", d.ID, d.Name, d.UUID)
}
if err := l.Disconnect(); err != nil {
log.Fatalf("failed to disconnect: %v", err)
}
}
```
```
Version: 1.3.4
ID Name UUID
--------------------------------------------------------
1 Test-1 dc329f87d4de47198cfd2e21c6105b01
2 Test-2 dc229f87d4de47198cfd2e21c6105b01
```
Example (Connect to libvirt via TLS over TCP)
-------
```go
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"log"
"github.com/digitalocean/go-libvirt"
)
func main() {
// This dials libvirt on the local machine
// It connects to libvirt via TLS over TCP
// To connect to a remote machine, you need to have the ca/cert/key of it.
keyFileXML, err := ioutil.ReadFile("/etc/pki/libvirt/private/clientkey.pem")
if err != nil {
log.Fatalf("%v", err)
}
certFileXML, err := ioutil.ReadFile("/etc/pki/libvirt/clientcert.pem")
if err != nil {
log.Fatalf("%v", err)
}
caFileXML, err := ioutil.ReadFile("/etc/pki/CA/cacert.pem")
if err != nil {
log.Fatalf("%v", err)
}
cert, err := tls.X509KeyPair([]byte(certFileXML), []byte(keyFileXML))
if err != nil {
log.Fatalf("%v", err)
}
roots := x509.NewCertPool()
roots.AppendCertsFromPEM([]byte(caFileXML))
config := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: roots,
}
// Use host name or IP which is valid in certificate
addr := "10.10.10.10"
port := "16514"
c, err := tls.Dial("tcp", addr + ":" + port, config)
if err != nil {
log.Fatalf("failed to dial libvirt: %v", err)
}
// Drop a byte before libvirt.New(c)
// More details at https://github.com/digitalocean/go-libvirt/issues/89
// Remove this line if the issue does not exist any more
c.Read(make([]byte, 1))
l := libvirt.New(c)
if err := l.Connect(); err != nil {
log.Fatalf("failed to connect: %v", err)
}
v, err := l.Version()
if err != nil {
log.Fatalf("failed to retrieve libvirt version: %v", err)
}
fmt.Println("Version:", v)
// Return both running and stopped VMs
flags := libvirt.ConnectListDomainsActive | libvirt.ConnectListDomainsInactive
domains, _, err := l.ConnectListAllDomains(1, flags)
if err != nil {
log.Fatalf("failed to retrieve domains: %v", err)
}
fmt.Println("ID\tName\t\tUUID")
fmt.Println("--------------------------------------------------------")
for _, d := range domains {
fmt.Printf("%d\t%s\t%x\n", d.ID, d.Name, d.UUID)
}
if err := l.Disconnect(); err != nil {
log.Fatalf("failed to disconnect: %v", err)
}
}
```
Running the Integration Tests
-----------------------------
Github actions workflows are defined in .github/worflows and can be triggered
manually in the github GUI after pushing a branch. There are not currently
convenient scripts for setting up and running integration tests locally, but
installing libvirt and defining only the artifacts described by the files in
testdata should be sufficient to be able to run the integration test file against.

2500
vendor/github.com/digitalocean/go-libvirt/const.gen.go generated vendored Normal file

File diff suppressed because it is too large Load diff

70
vendor/github.com/digitalocean/go-libvirt/doc.go generated vendored Normal file
View file

@ -0,0 +1,70 @@
// Copyright 2016 The go-libvirt Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package libvirt is a pure Go interface to libvirt.
//
// Rather than using Libvirt's C bindings, this package makes use of Libvirt's
// RPC interface, as documented here: https://libvirt.org/internals/rpc.html.
// Connections to the libvirt server may be local, or remote. RPC packets are
// encoded using the XDR standard as defined by RFC 4506.
//
// Example usage:
//
// package main
//
// import (
// "fmt"
// "log"
// "net"
// "time"
//
// "github.com/digitalocean/go-libvirt"
// )
//
// func main() {
// // This dials libvirt on the local machine, but you can substitute the first
// // two parameters with "tcp", "<ip address>:<port>" to connect to libvirt on
// // a remote machine.
// c, err := net.DialTimeout("unix", "/var/run/libvirt/libvirt-sock", 2*time.Second)
// if err != nil {
// log.Fatalf("failed to dial libvirt: %v", err)
// }
//
// l := libvirt.New(c)
// if err := l.Connect(); err != nil {
// log.Fatalf("failed to connect: %v", err)
// }
//
// v, err := l.Version()
// if err != nil {
// log.Fatalf("failed to retrieve libvirt version: %v", err)
// }
// fmt.Println("Version:", v)
//
// domains, err := l.Domains()
// if err != nil {
// log.Fatalf("failed to retrieve domains: %v", err)
// }
//
// fmt.Println("ID\tName\t\tUUID")
// fmt.Printf("--------------------------------------------------------\n")
// for _, d := range domains {
// fmt.Printf("%d\t%s\t%x\n", d.ID, d.Name, d.UUID)
// }
//
// if err := l.Disconnect(); err != nil {
// log.Fatalf("failed to disconnect: %v", err)
// }
// }
package libvirt

View file

@ -0,0 +1,47 @@
// Copyright 2018 The go-libvirt Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Code generated by internal/lvgen/generate.go. DO NOT EDIT.
//
// To regenerate, run 'go generate' in internal/lvgen.
//
package constants
// These are libvirt procedure numbers which correspond to each respective
// API call between remote_internal driver and libvirtd. Each procedure is
// identified by a unique number.
const (
// From enums:
// QEMUProcDomainMonitorCommand is libvirt's QEMU_PROC_DOMAIN_MONITOR_COMMAND
QEMUProcDomainMonitorCommand = 1
// QEMUProcDomainAttach is libvirt's QEMU_PROC_DOMAIN_ATTACH
QEMUProcDomainAttach = 2
// QEMUProcDomainAgentCommand is libvirt's QEMU_PROC_DOMAIN_AGENT_COMMAND
QEMUProcDomainAgentCommand = 3
// QEMUProcConnectDomainMonitorEventRegister is libvirt's QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_REGISTER
QEMUProcConnectDomainMonitorEventRegister = 4
// QEMUProcConnectDomainMonitorEventDeregister is libvirt's QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_DEREGISTER
QEMUProcConnectDomainMonitorEventDeregister = 5
// QEMUProcDomainMonitorEvent is libvirt's QEMU_PROC_DOMAIN_MONITOR_EVENT
QEMUProcDomainMonitorEvent = 6
// From consts:
// QEMUProgram is libvirt's QEMU_PROGRAM
QEMUProgram = 0x20008087
// QEMUProtocolVersion is libvirt's QEMU_PROTOCOL_VERSION
QEMUProtocolVersion = 1
)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,20 @@
// Copyright 2020 The go-libvirt Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package event
// Event represents an internal Event.
type Event interface {
GetCallbackID() int32
}

View file

@ -0,0 +1,157 @@
// Copyright 2020 The go-libvirt Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package event
import (
"context"
)
// emptyEvent is used as a zero-value. Clients will never receive one of these;
// they are only here to satisfy the compiler. See the comments in process() for
// more information.
type emptyEvent struct{}
func (emptyEvent) GetCallbackID() int32 { return 0 }
// Stream is an unbounded buffered event channel. The implementation
// consists of a pair of unbuffered channels and a goroutine to manage them.
// Client behavior will not cause incoming events to block.
type Stream struct {
// Program specifies the source of the events - libvirt or QEMU.
Program uint32
// CallbackID is returned by the event registration call.
CallbackID int32
// manage unbounded channel behavior.
queue []Event
qlen chan (chan int)
in, out chan Event
// terminates processing
shutdown context.CancelFunc
}
// NewStream configures a new Event Stream. Incoming events are appended to a
// queue, which is then relayed to the listening client. Client behavior will
// not cause incoming events to block. It is the responsibility of the caller
// to terminate the Stream via Shutdown() when no longer in use.
func NewStream(program uint32, cbID int32) *Stream {
s := &Stream{
Program: program,
CallbackID: cbID,
in: make(chan Event),
out: make(chan Event),
qlen: make(chan (chan int)),
}
// Start the processing loop, which will return a routine we can use to
// shut the queue down later.
s.shutdown = s.start()
return s
}
// Len will return the current count of events in the internal queue for a
// stream. It does this by sending a message to the stream's process() loop,
// which will then write the current length to the channel contained in that
// message.
func (s *Stream) Len() int {
// Send a request to the process() loop to get the current length of the
// queue
ch := make(chan int)
s.qlen <- ch
return <-ch
}
// Recv returns the next available event from the Stream's queue.
func (s *Stream) Recv() chan Event {
return s.out
}
// Push appends a new event to the queue.
func (s *Stream) Push(e Event) {
s.in <- e
}
// Shutdown gracefully terminates Stream processing, releasing all internal
// resources. Events which have not yet been received by the client will be
// dropped. Subsequent calls to Shutdown() are idempotent.
func (s *Stream) Shutdown() {
if s.shutdown != nil {
s.shutdown()
}
}
// start starts the event processing loop, which will continue to run until
// terminated by the returned context.CancelFunc.
func (s *Stream) start() context.CancelFunc {
ctx, cancel := context.WithCancel(context.Background())
go s.process(ctx)
return cancel
}
// process manages an Stream's lifecycle until canceled by the provided context.
// Incoming events are appended to a queue which is then relayed to the
// listening client. New events pushed onto the queue will not block if the
// client is not actively polling for them; the stream will buffer them
// internally.
func (s *Stream) process(ctx context.Context) {
// Close the output channel so that clients know this stream is finished.
// We don't close s.in to avoid creating a race with the stream's Push()
// function.
defer close(s.out)
// This function is used to retrieve the next event from the queue, to be
// sent to the client. If there are no more events to send, it returns a nil
// channel and a zero-value event.
nextEvent := func() (chan Event, Event) {
sendCh := chan Event(nil)
next := Event(emptyEvent{})
if len(s.queue) > 0 {
sendCh = s.out
next = s.queue[0]
}
return sendCh, next
}
// The select statement in this loop relies on the fact that a send to a nil
// channel will block forever. If we have no entries in the queue, the
// sendCh variable will be nil, so the clause that attempts to send an event
// to the client will never complete. Clients will never receive an
// emptyEvent.
for {
sendCh, nextEvt := nextEvent()
select {
// new event received, append to queue
case e := <-s.in:
s.queue = append(s.queue, e)
case lenCh := <-s.qlen:
lenCh <- len(s.queue)
// client received an event, pop from queue
case sendCh <- nextEvt:
s.queue = s.queue[1:]
// shutdown requested
case <-ctx.Done():
return
}
}
}

View file

@ -0,0 +1,13 @@
Copyright (c) 2012-2014 Dave Collins <dave@davec.name>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View file

@ -0,0 +1,896 @@
/*
* Copyright (c) 2012-2014 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package xdr
import (
"fmt"
"io"
"math"
"reflect"
"time"
)
var (
errMaxSlice = "data exceeds max slice limit"
errIODecode = "%s while decoding %d bytes"
)
/*
Unmarshal parses XDR-encoded data into the value pointed to by v reading from
reader r and returning the total number of bytes read. An addressable pointer
must be provided since Unmarshal needs to both store the result of the decode as
well as obtain target type information. Unmarhsal traverses v recursively and
automatically indirects pointers through arbitrary depth, allocating them as
necessary, to decode the data into the underlying value pointed to.
Unmarshal uses reflection to determine the type of the concrete value contained
by v and performs a mapping of underlying XDR types to Go types as follows:
Go Type <- XDR Type
--------------------
int8, int16, int32, int <- XDR Integer
uint8, uint16, uint32, uint <- XDR Unsigned Integer
int64 <- XDR Hyper Integer
uint64 <- XDR Unsigned Hyper Integer
bool <- XDR Boolean
float32 <- XDR Floating-Point
float64 <- XDR Double-Precision Floating-Point
string <- XDR String
byte <- XDR Integer
[]byte <- XDR Variable-Length Opaque Data
[#]byte <- XDR Fixed-Length Opaque Data
[]<type> <- XDR Variable-Length Array
[#]<type> <- XDR Fixed-Length Array
struct <- XDR Structure
map <- XDR Variable-Length Array of two-element XDR Structures
time.Time <- XDR String encoded with RFC3339 nanosecond precision
Notes and Limitations:
* Automatic unmarshalling of variable and fixed-length arrays of uint8s
requires a special struct tag `xdropaque:"false"` since byte slices
and byte arrays are assumed to be opaque data and byte is a Go alias
for uint8 thus indistinguishable under reflection
* Cyclic data structures are not supported and will result in infinite
loops
If any issues are encountered during the unmarshalling process, an
UnmarshalError is returned with a human readable description as well as
an ErrorCode value for further inspection from sophisticated callers. Some
potential issues are unsupported Go types, attempting to decode a value which is
too large to fit into a specified Go type, and exceeding max slice limitations.
*/
func Unmarshal(r io.Reader, v interface{}) (int, error) {
d := Decoder{r: r}
return d.Decode(v)
}
// UnmarshalLimited is identical to Unmarshal but it sets maxReadSize in order
// to cap reads.
func UnmarshalLimited(r io.Reader, v interface{}, maxSize uint) (int, error) {
d := Decoder{r: r, maxReadSize: maxSize}
return d.Decode(v)
}
// TypeDecoder lets a caller provide a custom decode routine for a custom type.
type TypeDecoder interface {
Decode(*Decoder, reflect.Value) (int, error)
}
// A Decoder wraps an io.Reader that is expected to provide an XDR-encoded byte
// stream and provides several exposed methods to manually decode various XDR
// primitives without relying on reflection. The NewDecoder function can be
// used to get a new Decoder directly.
//
// Typically, Unmarshal should be used instead of manual decoding. A Decoder
// is exposed so it is possible to perform manual decoding should it be
// necessary in complex scenarios where automatic reflection-based decoding
// won't work.
type Decoder struct {
r io.Reader
// maxReadSize is the default maximum bytes an element can contain. 0
// is unlimited and provides backwards compatability. Setting it to a
// non-zero value caps reads.
maxReadSize uint
// customTypes is a map allowing the caller to provide decoder routines for
// custom types known only to itself.
customTypes map[string]TypeDecoder
}
// DecodeInt treats the next 4 bytes as an XDR encoded integer and returns the
// result as an int32 along with the number of bytes actually read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining.
//
// Reference:
// RFC Section 4.1 - Integer
// 32-bit big-endian signed integer in range [-2147483648, 2147483647]
func (d *Decoder) DecodeInt() (int32, int, error) {
var buf [4]byte
n, err := io.ReadFull(d.r, buf[:])
if err != nil {
msg := fmt.Sprintf(errIODecode, err.Error(), 4)
err := unmarshalError("DecodeInt", ErrIO, msg, buf[:n], err)
return 0, n, err
}
rv := int32(buf[3]) | int32(buf[2])<<8 |
int32(buf[1])<<16 | int32(buf[0])<<24
return rv, n, nil
}
// DecodeUint treats the next 4 bytes as an XDR encoded unsigned integer and
// returns the result as a uint32 along with the number of bytes actually read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining.
//
// Reference:
// RFC Section 4.2 - Unsigned Integer
// 32-bit big-endian unsigned integer in range [0, 4294967295]
func (d *Decoder) DecodeUint() (uint32, int, error) {
var buf [4]byte
n, err := io.ReadFull(d.r, buf[:])
if err != nil {
msg := fmt.Sprintf(errIODecode, err.Error(), 4)
err := unmarshalError("DecodeUint", ErrIO, msg, buf[:n], err)
return 0, n, err
}
rv := uint32(buf[3]) | uint32(buf[2])<<8 |
uint32(buf[1])<<16 | uint32(buf[0])<<24
return rv, n, nil
}
// DecodeEnum treats the next 4 bytes as an XDR encoded enumeration value and
// returns the result as an int32 after verifying that the value is in the
// provided map of valid values. It also returns the number of bytes actually
// read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining or
// the parsed enumeration value is not one of the provided valid values.
//
// Reference:
// RFC Section 4.3 - Enumeration
// Represented as an XDR encoded signed integer
func (d *Decoder) DecodeEnum(validEnums map[int32]bool) (int32, int, error) {
val, n, err := d.DecodeInt()
if err != nil {
return 0, n, err
}
if !validEnums[val] {
err := unmarshalError("DecodeEnum", ErrBadEnumValue,
"invalid enum", val, nil)
return 0, n, err
}
return val, n, nil
}
// DecodeBool treats the next 4 bytes as an XDR encoded boolean value and
// returns the result as a bool along with the number of bytes actually read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining or
// the parsed value is not a 0 or 1.
//
// Reference:
// RFC Section 4.4 - Boolean
// Represented as an XDR encoded enumeration where 0 is false and 1 is true
func (d *Decoder) DecodeBool() (bool, int, error) {
val, n, err := d.DecodeInt()
if err != nil {
return false, n, err
}
switch val {
case 0:
return false, n, nil
case 1:
return true, n, nil
}
err = unmarshalError("DecodeBool", ErrBadEnumValue, "bool not 0 or 1",
val, nil)
return false, n, err
}
// DecodeHyper treats the next 8 bytes as an XDR encoded hyper value and
// returns the result as an int64 along with the number of bytes actually read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining.
//
// Reference:
// RFC Section 4.5 - Hyper Integer
// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807]
func (d *Decoder) DecodeHyper() (int64, int, error) {
var buf [8]byte
n, err := io.ReadFull(d.r, buf[:])
if err != nil {
msg := fmt.Sprintf(errIODecode, err.Error(), 8)
err := unmarshalError("DecodeHyper", ErrIO, msg, buf[:n], err)
return 0, n, err
}
rv := int64(buf[7]) | int64(buf[6])<<8 |
int64(buf[5])<<16 | int64(buf[4])<<24 |
int64(buf[3])<<32 | int64(buf[2])<<40 |
int64(buf[1])<<48 | int64(buf[0])<<56
return rv, n, err
}
// DecodeUhyper treats the next 8 bytes as an XDR encoded unsigned hyper value
// and returns the result as a uint64 along with the number of bytes actually
// read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining.
//
// Reference:
// RFC Section 4.5 - Unsigned Hyper Integer
// 64-bit big-endian unsigned integer in range [0, 18446744073709551615]
func (d *Decoder) DecodeUhyper() (uint64, int, error) {
var buf [8]byte
n, err := io.ReadFull(d.r, buf[:])
if err != nil {
msg := fmt.Sprintf(errIODecode, err.Error(), 8)
err := unmarshalError("DecodeUhyper", ErrIO, msg, buf[:n], err)
return 0, n, err
}
rv := uint64(buf[7]) | uint64(buf[6])<<8 |
uint64(buf[5])<<16 | uint64(buf[4])<<24 |
uint64(buf[3])<<32 | uint64(buf[2])<<40 |
uint64(buf[1])<<48 | uint64(buf[0])<<56
return rv, n, nil
}
// DecodeFloat treats the next 4 bytes as an XDR encoded floating point and
// returns the result as a float32 along with the number of bytes actually read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining.
//
// Reference:
// RFC Section 4.6 - Floating Point
// 32-bit single-precision IEEE 754 floating point
func (d *Decoder) DecodeFloat() (float32, int, error) {
var buf [4]byte
n, err := io.ReadFull(d.r, buf[:])
if err != nil {
msg := fmt.Sprintf(errIODecode, err.Error(), 4)
err := unmarshalError("DecodeFloat", ErrIO, msg, buf[:n], err)
return 0, n, err
}
val := uint32(buf[3]) | uint32(buf[2])<<8 |
uint32(buf[1])<<16 | uint32(buf[0])<<24
return math.Float32frombits(val), n, nil
}
// DecodeDouble treats the next 8 bytes as an XDR encoded double-precision
// floating point and returns the result as a float64 along with the number of
// bytes actually read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining.
//
// Reference:
// RFC Section 4.7 - Double-Precision Floating Point
// 64-bit double-precision IEEE 754 floating point
func (d *Decoder) DecodeDouble() (float64, int, error) {
var buf [8]byte
n, err := io.ReadFull(d.r, buf[:])
if err != nil {
msg := fmt.Sprintf(errIODecode, err.Error(), 8)
err := unmarshalError("DecodeDouble", ErrIO, msg, buf[:n], err)
return 0, n, err
}
val := uint64(buf[7]) | uint64(buf[6])<<8 |
uint64(buf[5])<<16 | uint64(buf[4])<<24 |
uint64(buf[3])<<32 | uint64(buf[2])<<40 |
uint64(buf[1])<<48 | uint64(buf[0])<<56
return math.Float64frombits(val), n, nil
}
// RFC Section 4.8 - Quadruple-Precision Floating Point
// 128-bit quadruple-precision floating point
// Not Implemented
// DecodeFixedOpaque treats the next 'size' bytes as XDR encoded opaque data and
// returns the result as a byte slice along with the number of bytes actually
// read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining to
// satisfy the passed size, including the necessary padding to make it a
// multiple of 4.
//
// Reference:
// RFC Section 4.9 - Fixed-Length Opaque Data
// Fixed-length uninterpreted data zero-padded to a multiple of four
func (d *Decoder) DecodeFixedOpaque(size int32) ([]byte, int, error) {
// Nothing to do if size is 0.
if size == 0 {
return nil, 0, nil
}
pad := (4 - (size % 4)) % 4
paddedSize := size + pad
if uint(paddedSize) > uint(math.MaxInt32) {
err := unmarshalError("DecodeFixedOpaque", ErrOverflow,
errMaxSlice, paddedSize, nil)
return nil, 0, err
}
buf := make([]byte, paddedSize)
n, err := io.ReadFull(d.r, buf)
if err != nil {
msg := fmt.Sprintf(errIODecode, err.Error(), paddedSize)
err := unmarshalError("DecodeFixedOpaque", ErrIO, msg, buf[:n],
err)
return nil, n, err
}
return buf[0:size], n, nil
}
// DecodeOpaque treats the next bytes as variable length XDR encoded opaque
// data and returns the result as a byte slice along with the number of bytes
// actually read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining or
// the opaque data is larger than the max length of a Go slice.
//
// Reference:
// RFC Section 4.10 - Variable-Length Opaque Data
// Unsigned integer length followed by fixed opaque data of that length
func (d *Decoder) DecodeOpaque() ([]byte, int, error) {
dataLen, n, err := d.DecodeUint()
if err != nil {
return nil, n, err
}
if uint(dataLen) > uint(math.MaxInt32) ||
(d.maxReadSize != 0 && uint(dataLen) > d.maxReadSize) {
err := unmarshalError("DecodeOpaque", ErrOverflow, errMaxSlice,
dataLen, nil)
return nil, n, err
}
rv, n2, err := d.DecodeFixedOpaque(int32(dataLen))
n += n2
if err != nil {
return nil, n, err
}
return rv, n, nil
}
// DecodeString treats the next bytes as a variable length XDR encoded string
// and returns the result as a string along with the number of bytes actually
// read. Character encoding is assumed to be UTF-8 and therefore ASCII
// compatible. If the underlying character encoding is not compatibile with
// this assumption, the data can instead be read as variable-length opaque data
// (DecodeOpaque) and manually converted as needed.
//
// An UnmarshalError is returned if there are insufficient bytes remaining or
// the string data is larger than the max length of a Go slice.
//
// Reference:
// RFC Section 4.11 - String
// Unsigned integer length followed by bytes zero-padded to a multiple of
// four
func (d *Decoder) DecodeString() (string, int, error) {
dataLen, n, err := d.DecodeUint()
if err != nil {
return "", n, err
}
if uint(dataLen) > uint(math.MaxInt32) ||
(d.maxReadSize != 0 && uint(dataLen) > d.maxReadSize) {
err = unmarshalError("DecodeString", ErrOverflow, errMaxSlice,
dataLen, nil)
return "", n, err
}
opaque, n2, err := d.DecodeFixedOpaque(int32(dataLen))
n += n2
if err != nil {
return "", n, err
}
return string(opaque), n, nil
}
// decodeFixedArray treats the next bytes as a series of XDR encoded elements
// of the same type as the array represented by the reflection value and decodes
// each element into the passed array. The ignoreOpaque flag controls whether
// or not uint8 (byte) elements should be decoded individually or as a fixed
// sequence of opaque data. It returns the the number of bytes actually read.
//
// An UnmarshalError is returned if any issues are encountered while decoding
// the array elements.
//
// Reference:
// RFC Section 4.12 - Fixed-Length Array
// Individually XDR encoded array elements
func (d *Decoder) decodeFixedArray(v reflect.Value, ignoreOpaque bool) (int, error) {
// Treat [#]byte (byte is alias for uint8) as opaque data unless
// ignored.
if !ignoreOpaque && v.Type().Elem().Kind() == reflect.Uint8 {
data, n, err := d.DecodeFixedOpaque(int32(v.Len()))
if err != nil {
return n, err
}
reflect.Copy(v, reflect.ValueOf(data))
return n, nil
}
// Decode each array element.
var n int
for i := 0; i < v.Len(); i++ {
n2, err := d.decode(v.Index(i))
n += n2
if err != nil {
return n, err
}
}
return n, nil
}
// decodeArray treats the next bytes as a variable length series of XDR encoded
// elements of the same type as the array represented by the reflection value.
// The number of elements is obtained by first decoding the unsigned integer
// element count. Then each element is decoded into the passed array. The
// ignoreOpaque flag controls whether or not uint8 (byte) elements should be
// decoded individually or as a variable sequence of opaque data. It returns
// the number of bytes actually read.
//
// An UnmarshalError is returned if any issues are encountered while decoding
// the array elements.
//
// Reference:
// RFC Section 4.13 - Variable-Length Array
// Unsigned integer length followed by individually XDR encoded array
// elements
func (d *Decoder) decodeArray(v reflect.Value, ignoreOpaque bool) (int, error) {
dataLen, n, err := d.DecodeUint()
if err != nil {
return n, err
}
if uint(dataLen) > uint(math.MaxInt32) ||
(d.maxReadSize != 0 && uint(dataLen) > d.maxReadSize) {
err := unmarshalError("decodeArray", ErrOverflow, errMaxSlice,
dataLen, nil)
return n, err
}
// Allocate storage for the slice elements (the underlying array) if
// existing slice does not have enough capacity.
sliceLen := int(dataLen)
if v.Cap() < sliceLen {
v.Set(reflect.MakeSlice(v.Type(), sliceLen, sliceLen))
}
if v.Len() < sliceLen {
v.SetLen(sliceLen)
}
// Treat []byte (byte is alias for uint8) as opaque data unless ignored.
if !ignoreOpaque && v.Type().Elem().Kind() == reflect.Uint8 {
data, n2, err := d.DecodeFixedOpaque(int32(sliceLen))
n += n2
if err != nil {
return n, err
}
v.SetBytes(data)
return n, nil
}
// Decode each slice element.
for i := 0; i < sliceLen; i++ {
n2, err := d.decode(v.Index(i))
n += n2
if err != nil {
return n, err
}
}
return n, nil
}
// decodeStruct treats the next bytes as a series of XDR encoded elements
// of the same type as the exported fields of the struct represented by the
// passed reflection value. Pointers are automatically indirected and
// allocated as necessary. It returns the the number of bytes actually read.
//
// An UnmarshalError is returned if any issues are encountered while decoding
// the elements.
//
// Reference:
// RFC Section 4.14 - Structure
// XDR encoded elements in the order of their declaration in the struct
func (d *Decoder) decodeStruct(v reflect.Value) (int, error) {
var n int
vt := v.Type()
for i := 0; i < v.NumField(); i++ {
// Skip unexported fields.
vtf := vt.Field(i)
if vtf.PkgPath != "" {
continue
}
// Indirect through pointers allocating them as needed and
// ensure the field is settable.
vf := v.Field(i)
vf, err := d.indirect(vf)
if err != nil {
return n, err
}
if !vf.CanSet() {
msg := fmt.Sprintf("can't decode to unsettable '%v'",
vf.Type().String())
err := unmarshalError("decodeStruct", ErrNotSettable,
msg, nil, nil)
return n, err
}
// Handle non-opaque data to []uint8 and [#]uint8 based on
// struct tag.
tag := vtf.Tag.Get("xdropaque")
if tag == "false" {
switch vf.Kind() {
case reflect.Slice:
n2, err := d.decodeArray(vf, true)
n += n2
if err != nil {
return n, err
}
continue
case reflect.Array:
n2, err := d.decodeFixedArray(vf, true)
n += n2
if err != nil {
return n, err
}
continue
}
}
// Decode each struct field.
n2, err := d.decode(vf)
n += n2
if err != nil {
return n, err
}
}
return n, nil
}
// RFC Section 4.15 - Discriminated Union
// RFC Section 4.16 - Void
// RFC Section 4.17 - Constant
// RFC Section 4.18 - Typedef
// RFC Section 4.19 - Optional data
// RFC Sections 4.15 though 4.19 only apply to the data specification language
// which is not implemented by this package. In the case of discriminated
// unions, struct tags are used to perform a similar function.
// decodeMap treats the next bytes as an XDR encoded variable array of 2-element
// structures whose fields are of the same type as the map keys and elements
// represented by the passed reflection value. Pointers are automatically
// indirected and allocated as necessary. It returns the the number of bytes
// actually read.
//
// An UnmarshalError is returned if any issues are encountered while decoding
// the elements.
func (d *Decoder) decodeMap(v reflect.Value) (int, error) {
dataLen, n, err := d.DecodeUint()
if err != nil {
return n, err
}
// Allocate storage for the underlying map if needed.
vt := v.Type()
if v.IsNil() {
v.Set(reflect.MakeMap(vt))
}
// Decode each key and value according to their type.
keyType := vt.Key()
elemType := vt.Elem()
for i := uint32(0); i < dataLen; i++ {
key := reflect.New(keyType).Elem()
n2, err := d.decode(key)
n += n2
if err != nil {
return n, err
}
val := reflect.New(elemType).Elem()
n2, err = d.decode(val)
n += n2
if err != nil {
return n, err
}
v.SetMapIndex(key, val)
}
return n, nil
}
// decodeInterface examines the interface represented by the passed reflection
// value to detect whether it is an interface that can be decoded into and
// if it is, extracts the underlying value to pass back into the decode function
// for decoding according to its type. It returns the the number of bytes
// actually read.
//
// An UnmarshalError is returned if any issues are encountered while decoding
// the interface.
func (d *Decoder) decodeInterface(v reflect.Value) (int, error) {
if v.IsNil() || !v.CanInterface() {
msg := fmt.Sprintf("can't decode to nil interface")
err := unmarshalError("decodeInterface", ErrNilInterface, msg,
nil, nil)
return 0, err
}
// Extract underlying value from the interface and indirect through
// pointers allocating them as needed.
ve := reflect.ValueOf(v.Interface())
ve, err := d.indirect(ve)
if err != nil {
return 0, err
}
if !ve.CanSet() {
msg := fmt.Sprintf("can't decode to unsettable '%v'",
ve.Type().String())
err := unmarshalError("decodeInterface", ErrNotSettable, msg,
nil, nil)
return 0, err
}
return d.decode(ve)
}
// decode is the main workhorse for unmarshalling via reflection. It uses
// the passed reflection value to choose the XDR primitives to decode from
// the encapsulated reader. It is a recursive function,
// so cyclic data structures are not supported and will result in an infinite
// loop. It returns the the number of bytes actually read.
func (d *Decoder) decode(v reflect.Value) (int, error) {
if !v.IsValid() {
msg := fmt.Sprintf("type '%s' is not valid", v.Kind().String())
err := unmarshalError("decode", ErrUnsupportedType, msg, nil, nil)
return 0, err
}
// Indirect through pointers allocating them as needed.
ve, err := d.indirect(v)
if err != nil {
return 0, err
}
// Handle time.Time values by decoding them as an RFC3339 formatted
// string with nanosecond precision. Check the type string rather
// than doing a full blown conversion to interface and type assertion
// since checking a string is much quicker.
switch ve.Type().String() {
case "time.Time":
// Read the value as a string and parse it.
timeString, n, err := d.DecodeString()
if err != nil {
return n, err
}
ttv, err := time.Parse(time.RFC3339, timeString)
if err != nil {
err := unmarshalError("decode", ErrParseTime,
err.Error(), timeString, err)
return n, err
}
ve.Set(reflect.ValueOf(ttv))
return n, nil
}
// If this type is in our custom types map, call the decode routine set up
// for it.
if dt, ok := d.customTypes[ve.Type().String()]; ok {
return dt.Decode(d, v)
}
// Handle native Go types.
switch ve.Kind() {
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int:
i, n, err := d.DecodeInt()
if err != nil {
return n, err
}
if ve.OverflowInt(int64(i)) {
msg := fmt.Sprintf("signed integer too large to fit '%s'",
ve.Kind().String())
err = unmarshalError("decode", ErrOverflow, msg, i, nil)
return n, err
}
ve.SetInt(int64(i))
return n, nil
case reflect.Int64:
i, n, err := d.DecodeHyper()
if err != nil {
return n, err
}
ve.SetInt(i)
return n, nil
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint:
ui, n, err := d.DecodeUint()
if err != nil {
return n, err
}
if ve.OverflowUint(uint64(ui)) {
msg := fmt.Sprintf("unsigned integer too large to fit '%s'",
ve.Kind().String())
err = unmarshalError("decode", ErrOverflow, msg, ui, nil)
return n, err
}
ve.SetUint(uint64(ui))
return n, nil
case reflect.Uint64:
ui, n, err := d.DecodeUhyper()
if err != nil {
return n, err
}
ve.SetUint(ui)
return n, nil
case reflect.Bool:
b, n, err := d.DecodeBool()
if err != nil {
return n, err
}
ve.SetBool(b)
return n, nil
case reflect.Float32:
f, n, err := d.DecodeFloat()
if err != nil {
return n, err
}
ve.SetFloat(float64(f))
return n, nil
case reflect.Float64:
f, n, err := d.DecodeDouble()
if err != nil {
return n, err
}
ve.SetFloat(f)
return n, nil
case reflect.String:
s, n, err := d.DecodeString()
if err != nil {
return n, err
}
ve.SetString(s)
return n, nil
case reflect.Array:
n, err := d.decodeFixedArray(ve, false)
if err != nil {
return n, err
}
return n, nil
case reflect.Slice:
n, err := d.decodeArray(ve, false)
if err != nil {
return n, err
}
return n, nil
case reflect.Struct:
n, err := d.decodeStruct(ve)
if err != nil {
return n, err
}
return n, nil
case reflect.Map:
n, err := d.decodeMap(ve)
if err != nil {
return n, err
}
return n, nil
case reflect.Interface:
n, err := d.decodeInterface(ve)
if err != nil {
return n, err
}
return n, nil
}
// The only unhandled types left are unsupported. At the time of this
// writing the only remaining unsupported types that exist are
// reflect.Uintptr and reflect.UnsafePointer.
msg := fmt.Sprintf("unsupported Go type '%s'", ve.Kind().String())
err = unmarshalError("decode", ErrUnsupportedType, msg, nil, nil)
return 0, err
}
// indirect dereferences pointers allocating them as needed until it reaches
// a non-pointer. This allows transparent decoding through arbitrary levels
// of indirection.
func (d *Decoder) indirect(v reflect.Value) (reflect.Value, error) {
rv := v
for rv.Kind() == reflect.Ptr {
// Allocate pointer if needed.
isNil := rv.IsNil()
if isNil && !rv.CanSet() {
msg := fmt.Sprintf("unable to allocate pointer for '%v'",
rv.Type().String())
err := unmarshalError("indirect", ErrNotSettable, msg,
nil, nil)
return rv, err
}
if isNil {
rv.Set(reflect.New(rv.Type().Elem()))
}
rv = rv.Elem()
}
return rv, nil
}
// Decode operates identically to the Unmarshal function with the exception of
// using the reader associated with the Decoder as the source of XDR-encoded
// data instead of a user-supplied reader. See the Unmarhsal documentation for
// specifics.
func (d *Decoder) Decode(v interface{}) (int, error) {
if v == nil {
msg := "can't unmarshal to nil interface"
return 0, unmarshalError("Unmarshal", ErrNilInterface, msg, nil,
nil)
}
vv := reflect.ValueOf(v)
if vv.Kind() != reflect.Ptr {
msg := fmt.Sprintf("can't unmarshal to non-pointer '%v' - use "+
"& operator", vv.Type().String())
err := unmarshalError("Unmarshal", ErrBadArguments, msg, nil, nil)
return 0, err
}
if vv.IsNil() && !vv.CanSet() {
msg := fmt.Sprintf("can't unmarshal to unsettable '%v' - use "+
"& operator", vv.Type().String())
err := unmarshalError("Unmarshal", ErrNotSettable, msg, nil, nil)
return 0, err
}
return d.decode(vv)
}
// NewDecoder returns a Decoder that can be used to manually decode XDR data
// from a provided reader. Typically, Unmarshal should be used instead of
// manually creating a Decoder.
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{r: r}
}
// NewDecoderLimited is identical to NewDecoder but it sets maxReadSize in
// order to cap reads.
func NewDecoderLimited(r io.Reader, maxSize uint) *Decoder {
return &Decoder{r: r, maxReadSize: maxSize}
}
// NewDecoderCustomTypes returns a decoder with support for custom types known
// to the caller. The second parameter is a map of the type name to the decoder
// routine. When the decoder finds a type matching one of the entries in the map
// it will call the custom routine for that type.
func NewDecoderCustomTypes(r io.Reader, maxSize uint, ct map[string]TypeDecoder) *Decoder {
return &Decoder{r: r, maxReadSize: maxSize, customTypes: ct}
}

View file

@ -0,0 +1,171 @@
/*
* Copyright (c) 2012-2014 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
Package xdr implements the data representation portion of the External Data
Representation (XDR) standard protocol as specified in RFC 4506 (obsoletes
RFC 1832 and RFC 1014).
The XDR RFC defines both a data specification language and a data
representation standard. This package implements methods to encode and decode
XDR data per the data representation standard with the exception of 128-bit
quadruple-precision floating points. It does not currently implement parsing of
the data specification language. In other words, the ability to automatically
generate Go code by parsing an XDR data specification file (typically .x
extension) is not supported. In practice, this limitation of the package is
fairly minor since it is largely unnecessary due to the reflection capabilities
of Go as described below.
This package provides two approaches for encoding and decoding XDR data:
1) Marshal/Unmarshal functions which automatically map between XDR and Go types
2) Individual Encoder/Decoder objects to manually work with XDR primitives
For the Marshal/Unmarshal functions, Go reflection capabilities are used to
choose the type of the underlying XDR data based upon the Go type to encode or
the target Go type to decode into. A description of how each type is mapped is
provided below, however one important type worth reviewing is Go structs. In
the case of structs, each exported field (first letter capitalized) is reflected
and mapped in order. As a result, this means a Go struct with exported fields
of the appropriate types listed in the expected order can be used to
automatically encode / decode the XDR data thereby eliminating the need to write
a lot of boilerplate code to encode/decode and error check each piece of XDR
data as is typically required with C based XDR libraries.
Go Type to XDR Type Mappings
The following chart shows an overview of how Go types are mapped to XDR types
for automatic marshalling and unmarshalling. The documentation for the Marshal
and Unmarshal functions has specific details of how the mapping proceeds.
Go Type <-> XDR Type
--------------------
int8, int16, int32, int <-> XDR Integer
uint8, uint16, uint32, uint <-> XDR Unsigned Integer
int64 <-> XDR Hyper Integer
uint64 <-> XDR Unsigned Hyper Integer
bool <-> XDR Boolean
float32 <-> XDR Floating-Point
float64 <-> XDR Double-Precision Floating-Point
string <-> XDR String
byte <-> XDR Integer
[]byte <-> XDR Variable-Length Opaque Data
[#]byte <-> XDR Fixed-Length Opaque Data
[]<type> <-> XDR Variable-Length Array
[#]<type> <-> XDR Fixed-Length Array
struct <-> XDR Structure
map <-> XDR Variable-Length Array of two-element XDR Structures
time.Time <-> XDR String encoded with RFC3339 nanosecond precision
Notes and Limitations:
* Automatic marshalling and unmarshalling of variable and fixed-length
arrays of uint8s require a special struct tag `xdropaque:"false"`
since byte slices and byte arrays are assumed to be opaque data and
byte is a Go alias for uint8 thus indistinguishable under reflection
* Channel, complex, and function types cannot be encoded
* Interfaces without a concrete value cannot be encoded
* Cyclic data structures are not supported and will result in infinite
loops
* Strings are marshalled and unmarshalled with UTF-8 character encoding
which differs from the XDR specification of ASCII, however UTF-8 is
backwards compatible with ASCII so this should rarely cause issues
Encoding
To encode XDR data, use the Marshal function.
func Marshal(w io.Writer, v interface{}) (int, error)
For example, given the following code snippet:
type ImageHeader struct {
Signature [3]byte
Version uint32
IsGrayscale bool
NumSections uint32
}
h := ImageHeader{[3]byte{0xAB, 0xCD, 0xEF}, 2, true, 10}
var w bytes.Buffer
bytesWritten, err := xdr.Marshal(&w, &h)
// Error check elided
The result, encodedData, will then contain the following XDR encoded byte
sequence:
0xAB, 0xCD, 0xEF, 0x00,
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x0A
In addition, while the automatic marshalling discussed above will work for the
vast majority of cases, an Encoder object is provided that can be used to
manually encode XDR primitives for complex scenarios where automatic
reflection-based encoding won't work. The included examples provide a sample of
manual usage via an Encoder.
Decoding
To decode XDR data, use the Unmarshal function.
func Unmarshal(r io.Reader, v interface{}) (int, error)
For example, given the following code snippet:
type ImageHeader struct {
Signature [3]byte
Version uint32
IsGrayscale bool
NumSections uint32
}
// Using output from the Encoding section above.
encodedData := []byte{
0xAB, 0xCD, 0xEF, 0x00,
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x0A,
}
var h ImageHeader
bytesRead, err := xdr.Unmarshal(bytes.NewReader(encodedData), &h)
// Error check elided
The struct instance, h, will then contain the following values:
h.Signature = [3]byte{0xAB, 0xCD, 0xEF}
h.Version = 2
h.IsGrayscale = true
h.NumSections = 10
In addition, while the automatic unmarshalling discussed above will work for the
vast majority of cases, a Decoder object is provided that can be used to
manually decode XDR primitives for complex scenarios where automatic
reflection-based decoding won't work. The included examples provide a sample of
manual usage via a Decoder.
Errors
All errors are either of type UnmarshalError or MarshalError. Both provide
human-readable output as well as an ErrorCode field which can be inspected by
sophisticated callers if necessary.
See the documentation of UnmarshalError, MarshalError, and ErrorCode for further
details.
*/
package xdr

View file

@ -0,0 +1,669 @@
/*
* Copyright (c) 2012-2014 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package xdr
import (
"fmt"
"io"
"math"
"reflect"
"time"
)
var errIOEncode = "%s while encoding %d bytes"
/*
Marshal writes the XDR encoding of v to writer w and returns the number of bytes
written. It traverses v recursively and automatically indirects pointers
through arbitrary depth to encode the actual value pointed to.
Marshal uses reflection to determine the type of the concrete value contained by
v and performs a mapping of Go types to the underlying XDR types as follows:
Go Type -> XDR Type
--------------------
int8, int16, int32, int -> XDR Integer
uint8, uint16, uint32, uint -> XDR Unsigned Integer
int64 -> XDR Hyper Integer
uint64 -> XDR Unsigned Hyper Integer
bool -> XDR Boolean
float32 -> XDR Floating-Point
float64 -> XDR Double-Precision Floating-Point
string -> XDR String
byte -> XDR Integer
[]byte -> XDR Variable-Length Opaque Data
[#]byte -> XDR Fixed-Length Opaque Data
[]<type> -> XDR Variable-Length Array
[#]<type> -> XDR Fixed-Length Array
struct -> XDR Structure
map -> XDR Variable-Length Array of two-element XDR Structures
time.Time -> XDR String encoded with RFC3339 nanosecond precision
Notes and Limitations:
* Automatic marshalling of variable and fixed-length arrays of uint8s
requires a special struct tag `xdropaque:"false"` since byte slices and
byte arrays are assumed to be opaque data and byte is a Go alias for uint8
thus indistinguishable under reflection
* Channel, complex, and function types cannot be encoded
* Interfaces without a concrete value cannot be encoded
* Cyclic data structures are not supported and will result in infinite loops
* Strings are marshalled with UTF-8 character encoding which differs from
the XDR specification of ASCII, however UTF-8 is backwards compatible with
ASCII so this should rarely cause issues
If any issues are encountered during the marshalling process, a MarshalError is
returned with a human readable description as well as an ErrorCode value for
further inspection from sophisticated callers. Some potential issues are
unsupported Go types, attempting to encode more opaque data than can be
represented by a single opaque XDR entry, and exceeding max slice limitations.
*/
func Marshal(w io.Writer, v interface{}) (int, error) {
enc := Encoder{w: w}
return enc.Encode(v)
}
// An Encoder wraps an io.Writer that will receive the XDR encoded byte stream.
// See NewEncoder.
type Encoder struct {
w io.Writer
}
// EncodeInt writes the XDR encoded representation of the passed 32-bit signed
// integer to the encapsulated writer and returns the number of bytes written.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.1 - Integer
// 32-bit big-endian signed integer in range [-2147483648, 2147483647]
func (enc *Encoder) EncodeInt(v int32) (int, error) {
var b [4]byte
b[0] = byte(v >> 24)
b[1] = byte(v >> 16)
b[2] = byte(v >> 8)
b[3] = byte(v)
n, err := enc.w.Write(b[:])
if err != nil {
msg := fmt.Sprintf(errIOEncode, err.Error(), 4)
err := marshalError("EncodeInt", ErrIO, msg, b[:n], err)
return n, err
}
return n, nil
}
// EncodeUint writes the XDR encoded representation of the passed 32-bit
// unsigned integer to the encapsulated writer and returns the number of bytes
// written.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.2 - Unsigned Integer
// 32-bit big-endian unsigned integer in range [0, 4294967295]
func (enc *Encoder) EncodeUint(v uint32) (int, error) {
var b [4]byte
b[0] = byte(v >> 24)
b[1] = byte(v >> 16)
b[2] = byte(v >> 8)
b[3] = byte(v)
n, err := enc.w.Write(b[:])
if err != nil {
msg := fmt.Sprintf(errIOEncode, err.Error(), 4)
err := marshalError("EncodeUint", ErrIO, msg, b[:n], err)
return n, err
}
return n, nil
}
// EncodeEnum treats the passed 32-bit signed integer as an enumeration value
// and, if it is in the list of passed valid enumeration values, writes the XDR
// encoded representation of it to the encapsulated writer. It returns the
// number of bytes written.
//
// A MarshalError is returned if the enumeration value is not one of the
// provided valid values or if writing the data fails.
//
// Reference:
// RFC Section 4.3 - Enumeration
// Represented as an XDR encoded signed integer
func (enc *Encoder) EncodeEnum(v int32, validEnums map[int32]bool) (int, error) {
if !validEnums[v] {
err := marshalError("EncodeEnum", ErrBadEnumValue,
"invalid enum", v, nil)
return 0, err
}
return enc.EncodeInt(v)
}
// EncodeBool writes the XDR encoded representation of the passed boolean to the
// encapsulated writer and returns the number of bytes written.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.4 - Boolean
// Represented as an XDR encoded enumeration where 0 is false and 1 is true
func (enc *Encoder) EncodeBool(v bool) (int, error) {
i := int32(0)
if v == true {
i = 1
}
return enc.EncodeInt(i)
}
// EncodeHyper writes the XDR encoded representation of the passed 64-bit
// signed integer to the encapsulated writer and returns the number of bytes
// written.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.5 - Hyper Integer
// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807]
func (enc *Encoder) EncodeHyper(v int64) (int, error) {
var b [8]byte
b[0] = byte(v >> 56)
b[1] = byte(v >> 48)
b[2] = byte(v >> 40)
b[3] = byte(v >> 32)
b[4] = byte(v >> 24)
b[5] = byte(v >> 16)
b[6] = byte(v >> 8)
b[7] = byte(v)
n, err := enc.w.Write(b[:])
if err != nil {
msg := fmt.Sprintf(errIOEncode, err.Error(), 8)
err := marshalError("EncodeHyper", ErrIO, msg, b[:n], err)
return n, err
}
return n, nil
}
// EncodeUhyper writes the XDR encoded representation of the passed 64-bit
// unsigned integer to the encapsulated writer and returns the number of bytes
// written.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.5 - Unsigned Hyper Integer
// 64-bit big-endian unsigned integer in range [0, 18446744073709551615]
func (enc *Encoder) EncodeUhyper(v uint64) (int, error) {
var b [8]byte
b[0] = byte(v >> 56)
b[1] = byte(v >> 48)
b[2] = byte(v >> 40)
b[3] = byte(v >> 32)
b[4] = byte(v >> 24)
b[5] = byte(v >> 16)
b[6] = byte(v >> 8)
b[7] = byte(v)
n, err := enc.w.Write(b[:])
if err != nil {
msg := fmt.Sprintf(errIOEncode, err.Error(), 8)
err := marshalError("EncodeUhyper", ErrIO, msg, b[:n], err)
return n, err
}
return n, nil
}
// EncodeFloat writes the XDR encoded representation of the passed 32-bit
// (single-precision) floating point to the encapsulated writer and returns the
// number of bytes written.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.6 - Floating Point
// 32-bit single-precision IEEE 754 floating point
func (enc *Encoder) EncodeFloat(v float32) (int, error) {
ui := math.Float32bits(v)
return enc.EncodeUint(ui)
}
// EncodeDouble writes the XDR encoded representation of the passed 64-bit
// (double-precision) floating point to the encapsulated writer and returns the
// number of bytes written.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.7 - Double-Precision Floating Point
// 64-bit double-precision IEEE 754 floating point
func (enc *Encoder) EncodeDouble(v float64) (int, error) {
ui := math.Float64bits(v)
return enc.EncodeUhyper(ui)
}
// RFC Section 4.8 - Quadruple-Precision Floating Point
// 128-bit quadruple-precision floating point
// Not Implemented
// EncodeFixedOpaque treats the passed byte slice as opaque data of a fixed
// size and writes the XDR encoded representation of it to the encapsulated
// writer. It returns the number of bytes written.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.9 - Fixed-Length Opaque Data
// Fixed-length uninterpreted data zero-padded to a multiple of four
func (enc *Encoder) EncodeFixedOpaque(v []byte) (int, error) {
l := len(v)
pad := (4 - (l % 4)) % 4
// Write the actual bytes.
n, err := enc.w.Write(v)
if err != nil {
msg := fmt.Sprintf(errIOEncode, err.Error(), len(v))
err := marshalError("EncodeFixedOpaque", ErrIO, msg, v[:n], err)
return n, err
}
// Write any padding if needed.
if pad > 0 {
b := make([]byte, pad)
n2, err := enc.w.Write(b)
n += n2
if err != nil {
written := make([]byte, l+n2)
copy(written, v)
copy(written[l:], b[:n2])
msg := fmt.Sprintf(errIOEncode, err.Error(), l+pad)
err := marshalError("EncodeFixedOpaque", ErrIO, msg,
written, err)
return n, err
}
}
return n, nil
}
// EncodeOpaque treats the passed byte slice as opaque data of a variable
// size and writes the XDR encoded representation of it to the encapsulated
// writer. It returns the number of bytes written.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.10 - Variable-Length Opaque Data
// Unsigned integer length followed by fixed opaque data of that length
func (enc *Encoder) EncodeOpaque(v []byte) (int, error) {
// Length of opaque data.
n, err := enc.EncodeUint(uint32(len(v)))
if err != nil {
return n, err
}
n2, err := enc.EncodeFixedOpaque(v)
n += n2
return n, err
}
// EncodeString writes the XDR encoded representation of the passed string
// to the encapsulated writer and returns the number of bytes written.
// Character encoding is assumed to be UTF-8 and therefore ASCII compatible. If
// the underlying character encoding is not compatible with this assumption, the
// data can instead be written as variable-length opaque data (EncodeOpaque) and
// manually converted as needed.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.11 - String
// Unsigned integer length followed by bytes zero-padded to a multiple of four
func (enc *Encoder) EncodeString(v string) (int, error) {
// Length of string.
n, err := enc.EncodeUint(uint32(len(v)))
if err != nil {
return n, err
}
n2, err := enc.EncodeFixedOpaque([]byte(v))
n += n2
return n, err
}
// encodeFixedArray writes the XDR encoded representation of each element
// in the passed array represented by the reflection value to the encapsulated
// writer and returns the number of bytes written. The ignoreOpaque flag
// controls whether or not uint8 (byte) elements should be encoded individually
// or as a fixed sequence of opaque data.
//
// A MarshalError is returned if any issues are encountered while encoding
// the array elements.
//
// Reference:
// RFC Section 4.12 - Fixed-Length Array
// Individually XDR encoded array elements
func (enc *Encoder) encodeFixedArray(v reflect.Value, ignoreOpaque bool) (int, error) {
// Treat [#]byte (byte is alias for uint8) as opaque data unless ignored.
if !ignoreOpaque && v.Type().Elem().Kind() == reflect.Uint8 {
// Create a slice of the underlying array for better efficiency
// when possible. Can't create a slice of an unaddressable
// value.
if v.CanAddr() {
return enc.EncodeFixedOpaque(v.Slice(0, v.Len()).Bytes())
}
// When the underlying array isn't addressable fall back to
// copying the array into a new slice. This is rather ugly, but
// the inability to create a constant slice from an
// unaddressable array is a limitation of Go.
slice := make([]byte, v.Len(), v.Len())
reflect.Copy(reflect.ValueOf(slice), v)
return enc.EncodeFixedOpaque(slice)
}
// Encode each array element.
var n int
for i := 0; i < v.Len(); i++ {
n2, err := enc.encode(v.Index(i))
n += n2
if err != nil {
return n, err
}
}
return n, nil
}
// encodeArray writes an XDR encoded integer representing the number of
// elements in the passed slice represented by the reflection value followed by
// the XDR encoded representation of each element in slice to the encapsulated
// writer and returns the number of bytes written. The ignoreOpaque flag
// controls whether or not uint8 (byte) elements should be encoded individually
// or as a variable sequence of opaque data.
//
// A MarshalError is returned if any issues are encountered while encoding
// the array elements.
//
// Reference:
// RFC Section 4.13 - Variable-Length Array
// Unsigned integer length followed by individually XDR encoded array elements
func (enc *Encoder) encodeArray(v reflect.Value, ignoreOpaque bool) (int, error) {
numItems := uint32(v.Len())
n, err := enc.EncodeUint(numItems)
if err != nil {
return n, err
}
n2, err := enc.encodeFixedArray(v, ignoreOpaque)
n += n2
return n, err
}
// encodeStruct writes an XDR encoded representation of each value in the
// exported fields of the struct represented by the passed reflection value to
// the encapsulated writer and returns the number of bytes written. Pointers
// are automatically indirected through arbitrary depth to encode the actual
// value pointed to.
//
// A MarshalError is returned if any issues are encountered while encoding
// the elements.
//
// Reference:
// RFC Section 4.14 - Structure
// XDR encoded elements in the order of their declaration in the struct
func (enc *Encoder) encodeStruct(v reflect.Value) (int, error) {
var n int
vt := v.Type()
for i := 0; i < v.NumField(); i++ {
// Skip unexported fields and indirect through pointers.
vtf := vt.Field(i)
if vtf.PkgPath != "" {
continue
}
vf := v.Field(i)
vf = enc.indirect(vf)
// Handle non-opaque data to []uint8 and [#]uint8 based on struct tag.
tag := vtf.Tag.Get("xdropaque")
if tag == "false" {
switch vf.Kind() {
case reflect.Slice:
n2, err := enc.encodeArray(vf, true)
n += n2
if err != nil {
return n, err
}
continue
case reflect.Array:
n2, err := enc.encodeFixedArray(vf, true)
n += n2
if err != nil {
return n, err
}
continue
}
}
// Encode each struct field.
n2, err := enc.encode(vf)
n += n2
if err != nil {
return n, err
}
}
return n, nil
}
// RFC Section 4.15 - Discriminated Union
// RFC Section 4.16 - Void
// RFC Section 4.17 - Constant
// RFC Section 4.18 - Typedef
// RFC Section 4.19 - Optional data
// RFC Sections 4.15 though 4.19 only apply to the data specification language
// which is not implemented by this package. In the case of discriminated
// unions, struct tags are used to perform a similar function.
// encodeMap treats the map represented by the passed reflection value as a
// variable-length array of 2-element structures whose fields are of the same
// type as the map keys and elements and writes its XDR encoded representation
// to the encapsulated writer. It returns the number of bytes written.
//
// A MarshalError is returned if any issues are encountered while encoding
// the elements.
func (enc *Encoder) encodeMap(v reflect.Value) (int, error) {
// Number of elements.
n, err := enc.EncodeUint(uint32(v.Len()))
if err != nil {
return n, err
}
// Encode each key and value according to their type.
for _, key := range v.MapKeys() {
n2, err := enc.encode(key)
n += n2
if err != nil {
return n, err
}
n2, err = enc.encode(v.MapIndex(key))
n += n2
if err != nil {
return n, err
}
}
return n, nil
}
// encodeInterface examines the interface represented by the passed reflection
// value to detect whether it is an interface that can be encoded if it is,
// extracts the underlying value to pass back into the encode function for
// encoding according to its type.
//
// A MarshalError is returned if any issues are encountered while encoding
// the interface.
func (enc *Encoder) encodeInterface(v reflect.Value) (int, error) {
if v.IsNil() || !v.CanInterface() {
msg := fmt.Sprintf("can't encode nil interface")
err := marshalError("encodeInterface", ErrNilInterface, msg,
nil, nil)
return 0, err
}
// Extract underlying value from the interface and indirect through pointers.
ve := reflect.ValueOf(v.Interface())
ve = enc.indirect(ve)
return enc.encode(ve)
}
// encode is the main workhorse for marshalling via reflection. It uses
// the passed reflection value to choose the XDR primitives to encode into
// the encapsulated writer and returns the number of bytes written. It is a
// recursive function, so cyclic data structures are not supported and will
// result in an infinite loop.
func (enc *Encoder) encode(v reflect.Value) (int, error) {
if !v.IsValid() {
msg := fmt.Sprintf("type '%s' is not valid", v.Kind().String())
err := marshalError("encode", ErrUnsupportedType, msg, nil, nil)
return 0, err
}
// Indirect through pointers to get at the concrete value.
ve := enc.indirect(v)
// Handle time.Time values by encoding them as an RFC3339 formatted
// string with nanosecond precision. Check the type string before
// doing a full blown conversion to interface and type assertion since
// checking a string is much quicker.
if ve.Type().String() == "time.Time" && ve.CanInterface() {
viface := ve.Interface()
if tv, ok := viface.(time.Time); ok {
return enc.EncodeString(tv.Format(time.RFC3339Nano))
}
}
// Handle native Go types.
switch ve.Kind() {
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int:
return enc.EncodeInt(int32(ve.Int()))
case reflect.Int64:
return enc.EncodeHyper(ve.Int())
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint:
return enc.EncodeUint(uint32(ve.Uint()))
case reflect.Uint64:
return enc.EncodeUhyper(ve.Uint())
case reflect.Bool:
return enc.EncodeBool(ve.Bool())
case reflect.Float32:
return enc.EncodeFloat(float32(ve.Float()))
case reflect.Float64:
return enc.EncodeDouble(ve.Float())
case reflect.String:
return enc.EncodeString(ve.String())
case reflect.Array:
return enc.encodeFixedArray(ve, false)
case reflect.Slice:
return enc.encodeArray(ve, false)
case reflect.Struct:
return enc.encodeStruct(ve)
case reflect.Map:
return enc.encodeMap(ve)
case reflect.Interface:
return enc.encodeInterface(ve)
}
// The only unhandled types left are unsupported. At the time of this
// writing the only remaining unsupported types that exist are
// reflect.Uintptr and reflect.UnsafePointer.
msg := fmt.Sprintf("unsupported Go type '%s'", ve.Kind().String())
err := marshalError("encode", ErrUnsupportedType, msg, nil, nil)
return 0, err
}
// indirect dereferences pointers until it reaches a non-pointer. This allows
// transparent encoding through arbitrary levels of indirection.
func (enc *Encoder) indirect(v reflect.Value) reflect.Value {
rv := v
for rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
return rv
}
// Encode operates identically to the Marshal function with the exception of
// using the writer associated with the Encoder for the destination of the
// XDR-encoded data instead of a user-supplied writer. See the Marshal
// documentation for specifics.
func (enc *Encoder) Encode(v interface{}) (int, error) {
if v == nil {
msg := "can't marshal nil interface"
err := marshalError("Marshal", ErrNilInterface, msg, nil, nil)
return 0, err
}
vv := reflect.ValueOf(v)
vve := vv
for vve.Kind() == reflect.Ptr {
if vve.IsNil() {
msg := fmt.Sprintf("can't marshal nil pointer '%v'",
vv.Type().String())
err := marshalError("Marshal", ErrBadArguments, msg,
nil, nil)
return 0, err
}
vve = vve.Elem()
}
return enc.encode(vve)
}
// NewEncoder returns an object that can be used to manually choose fields to
// XDR encode to the passed writer w. Typically, Marshal should be used instead
// of manually creating an Encoder. An Encoder, along with several of its
// methods to encode XDR primitives, is exposed so it is possible to perform
// manual encoding of data without relying on reflection should it be necessary
// in complex scenarios where automatic reflection-based encoding won't work.
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{w: w}
}

View file

@ -0,0 +1,177 @@
/*
* Copyright (c) 2012-2014 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package xdr
import "fmt"
// ErrorCode identifies a kind of error.
type ErrorCode int
const (
// ErrBadArguments indicates arguments passed to the function are not
// what was expected.
ErrBadArguments ErrorCode = iota
// ErrUnsupportedType indicates the Go type is not a supported type for
// marshalling and unmarshalling XDR data.
ErrUnsupportedType
// ErrBadEnumValue indicates an enumeration value is not in the list of
// valid values.
ErrBadEnumValue
// ErrNotSettable indicates an interface value cannot be written to.
// This usually means the interface value was not passed with the &
// operator, but it can also happen if automatic pointer allocation
// fails.
ErrNotSettable
// ErrOverflow indicates that the data in question is too large to fit
// into the corresponding Go or XDR data type. For example, an integer
// decoded from XDR that is too large to fit into a target type of int8,
// or opaque data that exceeds the max length of a Go slice.
ErrOverflow
// ErrNilInterface indicates an interface with no concrete type
// information was encountered. Type information is necessary to
// perform mapping between XDR and Go types.
ErrNilInterface
// ErrIO indicates an error was encountered while reading or writing to
// an io.Reader or io.Writer, respectively. The actual underlying error
// will be available via the Err field of the MarshalError or
// UnmarshalError struct.
ErrIO
// ErrParseTime indicates an error was encountered while parsing an
// RFC3339 formatted time value. The actual underlying error will be
// available via the Err field of the UnmarshalError struct.
ErrParseTime
)
// Map of ErrorCode values back to their constant names for pretty printing.
var errorCodeStrings = map[ErrorCode]string{
ErrBadArguments: "ErrBadArguments",
ErrUnsupportedType: "ErrUnsupportedType",
ErrBadEnumValue: "ErrBadEnumValue",
ErrNotSettable: "ErrNotSettable",
ErrOverflow: "ErrOverflow",
ErrNilInterface: "ErrNilInterface",
ErrIO: "ErrIO",
ErrParseTime: "ErrParseTime",
}
// String returns the ErrorCode as a human-readable name.
func (e ErrorCode) String() string {
if s := errorCodeStrings[e]; s != "" {
return s
}
return fmt.Sprintf("Unknown ErrorCode (%d)", e)
}
// UnmarshalError describes a problem encountered while unmarshaling data.
// Some potential issues are unsupported Go types, attempting to decode a value
// which is too large to fit into a specified Go type, and exceeding max slice
// limitations.
type UnmarshalError struct {
ErrorCode ErrorCode // Describes the kind of error
Func string // Function name
Value interface{} // Value actually parsed where appropriate
Description string // Human readable description of the issue
Err error // The underlying error for IO errors
}
// Error satisfies the error interface and prints human-readable errors.
func (e *UnmarshalError) Error() string {
switch e.ErrorCode {
case ErrBadEnumValue, ErrOverflow, ErrIO, ErrParseTime:
return fmt.Sprintf("xdr:%s: %s - read: '%v'", e.Func,
e.Description, e.Value)
}
return fmt.Sprintf("xdr:%s: %s", e.Func, e.Description)
}
// unmarshalError creates an error given a set of arguments and will copy byte
// slices into the Value field since they might otherwise be changed from from
// the original value.
func unmarshalError(f string, c ErrorCode, desc string, v interface{}, err error) *UnmarshalError {
e := &UnmarshalError{ErrorCode: c, Func: f, Description: desc, Err: err}
switch t := v.(type) {
case []byte:
slice := make([]byte, len(t))
copy(slice, t)
e.Value = slice
default:
e.Value = v
}
return e
}
// IsIO returns a boolean indicating whether the error is known to report that
// the underlying reader or writer encountered an ErrIO.
func IsIO(err error) bool {
switch e := err.(type) {
case *UnmarshalError:
return e.ErrorCode == ErrIO
case *MarshalError:
return e.ErrorCode == ErrIO
}
return false
}
// MarshalError describes a problem encountered while marshaling data.
// Some potential issues are unsupported Go types, attempting to encode more
// opaque data than can be represented by a single opaque XDR entry, and
// exceeding max slice limitations.
type MarshalError struct {
ErrorCode ErrorCode // Describes the kind of error
Func string // Function name
Value interface{} // Value actually parsed where appropriate
Description string // Human readable description of the issue
Err error // The underlying error for IO errors
}
// Error satisfies the error interface and prints human-readable errors.
func (e *MarshalError) Error() string {
switch e.ErrorCode {
case ErrIO:
return fmt.Sprintf("xdr:%s: %s - wrote: '%v'", e.Func,
e.Description, e.Value)
case ErrBadEnumValue:
return fmt.Sprintf("xdr:%s: %s - value: '%v'", e.Func,
e.Description, e.Value)
}
return fmt.Sprintf("xdr:%s: %s", e.Func, e.Description)
}
// marshalError creates an error given a set of arguments and will copy byte
// slices into the Value field since they might otherwise be changed from from
// the original value.
func marshalError(f string, c ErrorCode, desc string, v interface{}, err error) *MarshalError {
e := &MarshalError{ErrorCode: c, Func: f, Description: desc, Err: err}
switch t := v.(type) {
case []byte:
slice := make([]byte, len(t))
copy(slice, t)
e.Value = slice
default:
e.Value = v
}
return e
}

769
vendor/github.com/digitalocean/go-libvirt/libvirt.go generated vendored Normal file
View file

@ -0,0 +1,769 @@
// Copyright 2018 The go-libvirt Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package libvirt
// We'll use c-for-go to extract the consts and typedefs from the libvirt
// sources so we don't have to duplicate them here.
//go:generate scripts/gen-consts.sh
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"net"
"sync"
"syscall"
"time"
"github.com/digitalocean/go-libvirt/internal/constants"
"github.com/digitalocean/go-libvirt/internal/event"
xdr "github.com/digitalocean/go-libvirt/internal/go-xdr/xdr2"
"github.com/digitalocean/go-libvirt/socket"
"github.com/digitalocean/go-libvirt/socket/dialers"
)
// ErrEventsNotSupported is returned by Events() if event streams
// are unsupported by either QEMU or libvirt.
var ErrEventsNotSupported = errors.New("event monitor is not supported")
// ConnectURI defines a type for driver URIs for libvirt
// the defined constants are *not* exhaustive as there are also options
// e.g. to connect remote via SSH
type ConnectURI string
const (
// QEMUSystem connects to a QEMU system mode daemon
QEMUSystem ConnectURI = "qemu:///system"
// QEMUSession connects to a QEMU session mode daemon (unprivileged)
QEMUSession ConnectURI = "qemu:///session"
// XenSystem connects to a Xen system mode daemon
XenSystem ConnectURI = "xen:///system"
//TestDefault connect to default mock driver
TestDefault ConnectURI = "test:///default"
// disconnectedTimeout is how long to wait for disconnect cleanup to
// complete
disconnectTimeout = 5 * time.Second
)
// Libvirt implements libvirt's remote procedure call protocol.
type Libvirt struct {
// socket connection
socket *socket.Socket
// closed after cleanup complete following the underlying connection to
// libvirt being disconnected.
disconnected chan struct{}
// method callbacks
cmux sync.RWMutex
callbacks map[int32]chan response
// event listeners
emux sync.RWMutex
events map[int32]*event.Stream
// next request serial number
s int32
}
// DomainEvent represents a libvirt domain event.
type DomainEvent struct {
CallbackID int32
Domain Domain
Event string
Seconds uint64
Microseconds uint32
Padding uint8
Details []byte
}
// GetCallbackID returns the callback ID of a QEMU domain event.
func (de DomainEvent) GetCallbackID() int32 {
return de.CallbackID
}
// GetCallbackID returns the callback ID of a libvirt lifecycle event.
func (m DomainEventCallbackLifecycleMsg) GetCallbackID() int32 {
return m.CallbackID
}
// qemuError represents a QEMU process error.
type qemuError struct {
Error struct {
Class string `json:"class"`
Description string `json:"desc"`
} `json:"error"`
}
// Capabilities returns an XML document describing the host's capabilties.
func (l *Libvirt) Capabilities() ([]byte, error) {
caps, err := l.ConnectGetCapabilities()
return []byte(caps), err
}
// called at connection time, authenticating with all supported auth types
func (l *Libvirt) authenticate() error {
// libvirt requires that we call auth-list prior to connecting,
// even when no authentication is used.
resp, err := l.AuthList()
if err != nil {
return err
}
for _, auth := range resp {
switch auth {
case constants.AuthNone:
case constants.AuthPolkit:
_, err := l.AuthPolkit()
if err != nil {
return err
}
default:
continue
}
break
}
return nil
}
func (l *Libvirt) initLibvirtComms(uri ConnectURI) error {
payload := struct {
Padding [3]byte
Name string
Flags uint32
}{
Padding: [3]byte{0x1, 0x0, 0x0},
Name: string(uri),
Flags: 0,
}
buf, err := encode(&payload)
if err != nil {
return err
}
err = l.authenticate()
if err != nil {
return err
}
_, err = l.request(constants.ProcConnectOpen, constants.Program, buf)
if err != nil {
return err
}
return nil
}
// ConnectToURI establishes communication with the specified libvirt driver
// The underlying libvirt socket connection will be created via the dialer.
// Since the connection can be lost, the Disconnected function can be used
// to monitor for a lost connection.
func (l *Libvirt) ConnectToURI(uri ConnectURI) error {
err := l.socket.Connect()
if err != nil {
return err
}
// Start watching the underlying socket connection immediately.
// If we don't, and Libvirt goes away partway through initLibvirtComms,
// then the callbacks that initLibvirtComms has registered will never
// be closed, and therefore it will be stuck waiting for data from a
// channel that will never arrive.
go l.waitAndDisconnect()
err = l.initLibvirtComms(uri)
if err != nil {
l.socket.Disconnect()
return err
}
l.disconnected = make(chan struct{})
return nil
}
// Connect establishes communication with the libvirt server.
// The underlying libvirt socket connection will be created via the dialer.
// Since the connection can be lost, the Disconnected function can be used
// to monitor for a lost connection.
func (l *Libvirt) Connect() error {
return l.ConnectToURI(QEMUSystem)
}
// Disconnect shuts down communication with the libvirt server and closes the
// underlying net.Conn.
func (l *Libvirt) Disconnect() error {
// Ordering is important here. We want to make sure the connection is closed
// before unsubscribing and deregistering the events and requests, to
// prevent new requests from racing.
_, err := l.request(constants.ProcConnectClose, constants.Program, nil)
// syscall.EINVAL is returned by the socket pkg when things have already
// been disconnected.
if err != nil && err != syscall.EINVAL {
return err
}
err = l.socket.Disconnect()
if err != nil {
return err
}
// wait for the listen goroutine to detect the lost connection and clean up
// to happen once it returns. Safeguard with a timeout.
// Things not fully cleaned up is better than a deadlock.
select {
case <-l.disconnected:
case <-time.After(disconnectTimeout):
}
return err
}
// Disconnected allows callers to detect if the underlying connection
// to libvirt has been closed. If the returned channel is closed, then
// the connection to libvirt has been lost (or disconnected intentionally).
func (l *Libvirt) Disconnected() <-chan struct{} {
return l.disconnected
}
// Domains returns a list of all domains managed by libvirt.
//
// Deprecated: use ConnectListAllDomains instead.
func (l *Libvirt) Domains() ([]Domain, error) {
// these are the flags as passed by `virsh list --all`
flags := ConnectListDomainsActive | ConnectListDomainsInactive
domains, _, err := l.ConnectListAllDomains(1, flags)
return domains, err
}
// DomainState returns state of the domain managed by libvirt.
//
// Deprecated: use DomainGetState instead.
func (l *Libvirt) DomainState(dom string) (DomainState, error) {
d, err := l.lookup(dom)
if err != nil {
return DomainNostate, err
}
state, _, err := l.DomainGetState(d, 0)
return DomainState(state), err
}
// SubscribeQEMUEvents streams domain events until the provided context is
// cancelled. If a problem is encountered setting up the event monitor
// connection an error will be returned. Errors encountered during streaming
// will cause the returned event channel to be closed. QEMU domain events.
func (l *Libvirt) SubscribeQEMUEvents(ctx context.Context, dom string) (<-chan DomainEvent, error) {
d, err := l.lookup(dom)
if err != nil {
return nil, err
}
callbackID, err := l.QEMUConnectDomainMonitorEventRegister([]Domain{d}, nil, 0)
if err != nil {
return nil, err
}
stream := event.NewStream(constants.QEMUProgram, callbackID)
l.addStream(stream)
ch := make(chan DomainEvent)
go func() {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
defer l.unsubscribeQEMUEvents(stream)
defer stream.Shutdown()
defer close(ch)
for {
select {
case ev, ok := <-stream.Recv():
if !ok {
return
}
ch <- *ev.(*DomainEvent)
case <-ctx.Done():
return
}
}
}()
return ch, nil
}
// unsubscribeQEMUEvents stops the flow of events from QEMU through libvirt.
func (l *Libvirt) unsubscribeQEMUEvents(stream *event.Stream) error {
err := l.QEMUConnectDomainMonitorEventDeregister(stream.CallbackID)
l.removeStream(stream.CallbackID)
return err
}
// SubscribeEvents allows the caller to subscribe to any of the event types
// supported by libvirt. The events will continue to be streamed until the
// caller cancels the provided context. After canceling the context, callers
// should wait until the channel is closed to be sure they're collected all the
// events.
func (l *Libvirt) SubscribeEvents(ctx context.Context, eventID DomainEventID,
dom OptDomain) (<-chan interface{}, error) {
callbackID, err := l.ConnectDomainEventCallbackRegisterAny(int32(eventID), nil)
if err != nil {
return nil, err
}
stream := event.NewStream(constants.QEMUProgram, callbackID)
l.addStream(stream)
ch := make(chan interface{})
go func() {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
defer l.unsubscribeEvents(stream)
defer stream.Shutdown()
defer func() { close(ch) }()
for {
select {
case ev, ok := <-stream.Recv():
if !ok {
return
}
ch <- ev
case <-ctx.Done():
return
}
}
}()
return ch, nil
}
// unsubscribeEvents stops the flow of the specified events from libvirt. There
// are two steps to this process: a call to libvirt to deregister our callback,
// and then removing the callback from the list used by the `Route` function. If
// the deregister call fails, we'll return the error, but still remove the
// callback from the list. That's ok; if any events arrive after this point, the
// Route function will drop them when it finds no registered handler.
func (l *Libvirt) unsubscribeEvents(stream *event.Stream) error {
err := l.ConnectDomainEventCallbackDeregisterAny(stream.CallbackID)
l.removeStream(stream.CallbackID)
return err
}
// LifecycleEvents streams lifecycle events until the provided context is
// cancelled. If a problem is encountered setting up the event monitor
// connection, an error will be returned. Errors encountered during streaming
// will cause the returned event channel to be closed.
func (l *Libvirt) LifecycleEvents(ctx context.Context) (<-chan DomainEventLifecycleMsg, error) {
callbackID, err := l.ConnectDomainEventCallbackRegisterAny(int32(DomainEventIDLifecycle), nil)
if err != nil {
return nil, err
}
stream := event.NewStream(constants.Program, callbackID)
l.addStream(stream)
ch := make(chan DomainEventLifecycleMsg)
go func() {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
defer l.unsubscribeEvents(stream)
defer stream.Shutdown()
defer func() { close(ch) }()
for {
select {
case ev, ok := <-stream.Recv():
if !ok {
return
}
ch <- ev.(*DomainEventCallbackLifecycleMsg).Msg
case <-ctx.Done():
return
}
}
}()
return ch, nil
}
// Run executes the given QAPI command against a domain's QEMU instance.
// For a list of available QAPI commands, see:
// http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD
func (l *Libvirt) Run(dom string, cmd []byte) ([]byte, error) {
d, err := l.lookup(dom)
if err != nil {
return nil, err
}
payload := struct {
Domain Domain
Command []byte
Flags uint32
}{
Domain: d,
Command: cmd,
Flags: 0,
}
buf, err := encode(&payload)
if err != nil {
return nil, err
}
res, err := l.request(constants.QEMUProcDomainMonitorCommand, constants.QEMUProgram, buf)
if err != nil {
return nil, err
}
// check for QEMU process errors
if err = getQEMUError(res); err != nil {
return nil, err
}
r := bytes.NewReader(res.Payload)
dec := xdr.NewDecoder(r)
data, _, err := dec.DecodeFixedOpaque(int32(r.Len()))
if err != nil {
return nil, err
}
// drop QMP control characters from start of line, and drop
// any trailing NULL characters from the end
return bytes.TrimRight(data[4:], "\x00"), nil
}
// Secrets returns all secrets managed by the libvirt daemon.
//
// Deprecated: use ConnectListAllSecrets instead.
func (l *Libvirt) Secrets() ([]Secret, error) {
secrets, _, err := l.ConnectListAllSecrets(1, 0)
return secrets, err
}
// StoragePool returns the storage pool associated with the provided name.
// An error is returned if the requested storage pool is not found.
//
// Deprecated: use StoragePoolLookupByName instead.
func (l *Libvirt) StoragePool(name string) (StoragePool, error) {
return l.StoragePoolLookupByName(name)
}
// StoragePools returns a list of defined storage pools. Pools are filtered by
// the provided flags. See StoragePools*.
//
// Deprecated: use ConnectListAllStoragePools instead.
func (l *Libvirt) StoragePools(flags ConnectListAllStoragePoolsFlags) ([]StoragePool, error) {
pools, _, err := l.ConnectListAllStoragePools(1, flags)
return pools, err
}
// Undefine undefines the domain specified by dom, e.g., 'prod-lb-01'.
// The flags argument allows additional options to be specified such as
// cleaning up snapshot metadata. For more information on available
// flags, see DomainUndefine*.
//
// Deprecated: use DomainUndefineFlags instead.
func (l *Libvirt) Undefine(dom string, flags DomainUndefineFlagsValues) error {
d, err := l.lookup(dom)
if err != nil {
return err
}
return l.DomainUndefineFlags(d, flags)
}
// Destroy destroys the domain specified by dom, e.g., 'prod-lb-01'.
// The flags argument allows additional options to be specified such as
// allowing a graceful shutdown with SIGTERM than SIGKILL.
// For more information on available flags, see DomainDestroy*.
//
// Deprecated: use DomainDestroyFlags instead.
func (l *Libvirt) Destroy(dom string, flags DomainDestroyFlagsValues) error {
d, err := l.lookup(dom)
if err != nil {
return err
}
return l.DomainDestroyFlags(d, flags)
}
// XML returns a domain's raw XML definition, akin to `virsh dumpxml <domain>`.
// See DomainXMLFlag* for optional flags.
//
// Deprecated: use DomainGetXMLDesc instead.
func (l *Libvirt) XML(dom string, flags DomainXMLFlags) ([]byte, error) {
d, err := l.lookup(dom)
if err != nil {
return nil, err
}
xml, err := l.DomainGetXMLDesc(d, flags)
return []byte(xml), err
}
// DefineXML defines a domain, but does not start it.
//
// Deprecated: use DomainDefineXMLFlags instead.
func (l *Libvirt) DefineXML(x []byte, flags DomainDefineFlags) error {
_, err := l.DomainDefineXMLFlags(string(x), flags)
return err
}
// Version returns the version of the libvirt daemon.
//
// Deprecated: use ConnectGetLibVersion instead.
func (l *Libvirt) Version() (string, error) {
ver, err := l.ConnectGetLibVersion()
if err != nil {
return "", err
}
// The version is provided as an int following this formula:
// version * 1,000,000 + minor * 1000 + micro
// See src/libvirt-host.c # virConnectGetLibVersion
major := ver / 1000000
ver %= 1000000
minor := ver / 1000
ver %= 1000
micro := ver
versionString := fmt.Sprintf("%d.%d.%d", major, minor, micro)
return versionString, nil
}
// Shutdown shuts down a domain. Note that the guest OS may ignore the request.
// If flags is set to 0 then the hypervisor will choose the method of shutdown it considers best.
//
// Deprecated: use DomainShutdownFlags instead.
func (l *Libvirt) Shutdown(dom string, flags DomainShutdownFlagValues) error {
d, err := l.lookup(dom)
if err != nil {
return err
}
return l.DomainShutdownFlags(d, flags)
}
// Reboot reboots the domain. Note that the guest OS may ignore the request.
// If flags is set to zero, then the hypervisor will choose the method of shutdown it considers best.
//
// Deprecated: use DomainReboot instead.
func (l *Libvirt) Reboot(dom string, flags DomainRebootFlagValues) error {
d, err := l.lookup(dom)
if err != nil {
return err
}
return l.DomainReboot(d, flags)
}
// Reset resets domain immediately without any guest OS shutdown
//
// Deprecated: use DomainReset instead.
func (l *Libvirt) Reset(dom string) error {
d, err := l.lookup(dom)
if err != nil {
return err
}
return l.DomainReset(d, 0)
}
// BlockLimit contains a name and value pair for a Get/SetBlockIOTune limit. The
// Name field is the name of the limit (to see a list of the limits that can be
// applied, execute the 'blkdeviotune' command on a VM in virsh). Callers can
// use the QEMUBlockIO... constants below for the Name value. The Value field is
// the limit to apply.
type BlockLimit struct {
Name string
Value uint64
}
// SetBlockIOTune changes the per-device block I/O tunables within a guest.
// Parameters are the name of the VM, the name of the disk device to which the
// limits should be applied, and 1 or more BlockLimit structs containing the
// actual limits.
//
// The limits which can be applied here are enumerated in the QEMUBlockIO...
// constants above, and you can also see the full list by executing the
// 'blkdeviotune' command on a VM in virsh.
//
// Example usage:
// SetBlockIOTune("vm-name", "vda", BlockLimit{libvirt.QEMUBlockIOWriteBytesSec, 1000000})
//
// Deprecated: use DomainSetBlockIOTune instead.
func (l *Libvirt) SetBlockIOTune(dom string, disk string, limits ...BlockLimit) error {
d, err := l.lookup(dom)
if err != nil {
return err
}
params := make([]TypedParam, len(limits))
for ix, limit := range limits {
tpval := NewTypedParamValueUllong(limit.Value)
params[ix] = TypedParam{Field: limit.Name, Value: *tpval}
}
return l.DomainSetBlockIOTune(d, disk, params, uint32(DomainAffectLive))
}
// GetBlockIOTune returns a slice containing the current block I/O tunables for
// a disk.
//
// Deprecated: use DomainGetBlockIOTune instead.
func (l *Libvirt) GetBlockIOTune(dom string, disk string) ([]BlockLimit, error) {
d, err := l.lookup(dom)
if err != nil {
return nil, err
}
lims, _, err := l.DomainGetBlockIOTune(d, []string{disk}, 32, uint32(TypedParamStringOkay))
if err != nil {
return nil, err
}
var limits []BlockLimit
// now decode each of the returned TypedParams. To do this we read the field
// name and type, then use the type information to decode the value.
for _, lim := range lims {
var l BlockLimit
name := lim.Field
switch lim.Value.I.(type) {
case uint64:
l = BlockLimit{Name: name, Value: lim.Value.I.(uint64)}
}
limits = append(limits, l)
}
return limits, nil
}
// lookup returns a domain as seen by libvirt.
func (l *Libvirt) lookup(name string) (Domain, error) {
return l.DomainLookupByName(name)
}
// getQEMUError checks the provided response for QEMU process errors.
// If an error is found, it is extracted an returned, otherwise nil.
func getQEMUError(r response) error {
pl := bytes.NewReader(r.Payload)
dec := xdr.NewDecoder(pl)
s, _, err := dec.DecodeString()
if err != nil {
return err
}
var e qemuError
if err = json.Unmarshal([]byte(s), &e); err != nil {
return err
}
if e.Error.Description != "" {
return errors.New(e.Error.Description)
}
return nil
}
func (l *Libvirt) waitAndDisconnect() {
// wait for the socket to indicate if/when it's been disconnected
<-l.socket.Disconnected()
// close event streams
l.removeAllStreams()
// Deregister all callbacks to prevent blocking on clients with
// outstanding requests
l.deregisterAll()
select {
case <-l.disconnected:
// l.disconnected is already closed, i.e., Libvirt.ConnectToURI
// was unable to complete all phases of its connection and
// so this hadn't been assigned to an open channel yet (it
// is set to a closed channel in Libvirt.New*)
//
// Just return to avoid closing an already-closed channel.
return
default:
// if we make it here then reading from l.disconnected is blocking,
// which suggests that it is open and must be closed.
}
close(l.disconnected)
}
// NewWithDialer configures a new Libvirt object that can be used to perform
// RPCs via libvirt's socket. The actual connection will not be established
// until Connect is called. The same Libvirt object may be used to re-connect
// multiple times.
func NewWithDialer(dialer socket.Dialer) *Libvirt {
l := &Libvirt{
s: 0,
disconnected: make(chan struct{}),
callbacks: make(map[int32]chan response),
events: make(map[int32]*event.Stream),
}
l.socket = socket.New(dialer, l)
// we start with a closed channel since that indicates no connection
close(l.disconnected)
return l
}
// New configures a new Libvirt RPC connection.
// This function only remains to retain backwards compatability.
// When Libvirt's Connect function is called, the Dial will simply return the
// connection passed in here and start a goroutine listening/reading from it.
// If at any point the Disconnect function is called, any subsequent Connect
// call will simply return an already closed connection.
//
// Deprecated: Please use NewWithDialer.
func New(conn net.Conn) *Libvirt {
return NewWithDialer(dialers.NewAlreadyConnected(conn))
}
// NetworkUpdateCompat is a wrapper over NetworkUpdate which swaps `Command` and `Section` when needed.
// This function must be used instead of NetworkUpdate to be sure that the
// NetworkUpdate call works both with older and newer libvirtd connections.
//
// libvirt on-wire protocol had a bug for a long time where Command and Section
// were reversed. It's been fixed in newer libvirt versions, and backported to
// some older versions. This helper detects what argument order libvirtd expects
// and makes the correct NetworkUpdate call.
func (l *Libvirt) NetworkUpdateCompat(Net Network, Command NetworkUpdateCommand, Section NetworkUpdateSection, ParentIndex int32, XML string, Flags NetworkUpdateFlags) (err error) {
// This is defined in libvirt/src/libvirt_internal.h and thus not available in go-libvirt autogenerated code
const virDrvFeatureNetworkUpdateHasCorrectOrder = 16
hasCorrectOrder, err := l.ConnectSupportsFeature(virDrvFeatureNetworkUpdateHasCorrectOrder)
if err != nil {
return fmt.Errorf("failed to confirm argument order for NetworkUpdate: %w", err)
}
// https://gitlab.com/libvirt/libvirt/-/commit/b0f78d626a18bcecae3a4d165540ab88bfbfc9ee
if hasCorrectOrder == 0 {
return l.NetworkUpdate(Net, uint32(Section), uint32(Command), ParentIndex, XML, Flags)
}
return l.NetworkUpdate(Net, uint32(Command), uint32(Section), ParentIndex, XML, Flags)
}

64
vendor/github.com/digitalocean/go-libvirt/libvirt.yml generated vendored Normal file
View file

@ -0,0 +1,64 @@
# Configuration file for c-for-go, which go-libvirt uses to translate the const
# and type definitions from the C-language sources in the libvirt project into
# Go. This file is used by the c-for-go binary (github.com/xlab/c-for-go), which
# is called when 'go generate' is run. See libvirt.go for the command line used.
---
GENERATOR:
PackageName: libvirt
PackageLicense: |
Copyright 2018 The go-libvirt Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Includes: []
PARSER:
# We can't use environment variables here, but we don't want to process the
# libvirt version installed in the system folders (if any). Instead we'll
# rely on our caller to link the libvirt source directory to lv_source/, and
# run on that code. This isn't ideal, but changes to c-for-go are needed to
# fix it.
IncludePaths: [./lv_source/include, ./lv_source/build/include]
SourcesPaths:
- libvirt/libvirt.h
- libvirt/virterror.h
TRANSLATOR:
ConstRules:
defines: eval
Rules:
global:
- {action: accept, from: "^vir"}
post-global:
- {action: replace, from: "^vir"}
- {load: snakecase}
# Follow golint's capitalization conventions.
- {action: replace, from: "Api([A-Z]|$)", to: "API$1"}
- {action: replace, from: "Cpu([A-Z]|$)", to: "CPU$1"}
- {action: replace, from: "Dns([A-Z]|$)", to: "DNS$1"}
- {action: replace, from: "Eof([A-Z]|$)", to: "EOF$1"}
- {action: replace, from: "Id([A-Z]|$)", to: "ID$1"}
- {action: replace, from: "Ip([A-Z]|$)", to: "IP$1"}
- {action: replace, from: "Tls([A-Z]|$)", to: "TLS$1"}
- {action: replace, from: "Uuid([A-Z]|$)", to: "UUID$1"}
- {action: replace, from: "Uri([A-Z]|$)", to: "URI$1"}
- {action: replace, from: "Vcpu([A-Z]|$)", to: "VCPU$1"}
- {action: replace, from: "Xml([A-Z]|$)", to: "XML$1"}
- {action: replace, from: "Rpc([A-Z]|$)", to: "RPC$1"}
- {action: replace, from: "Ssh([A-Z]|$)", to: "SSH$1"}
- {action: replace, from: "Http([A-Z]|$)", to: "HTTP$1"}
- {transform: unexport, from: "^From"}
const:
- {action: accept, from: "^VIR_"}
# Special case to prevent a collision with a type:
- {action: replace, from: "^VIR_DOMAIN_JOB_OPERATION", to: "VIR_DOMAIN_JOB_OPERATION_STR"}
- {transform: lower}

View file

@ -0,0 +1,292 @@
// Copyright 2018 The go-libvirt Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Code generated by internal/lvgen/generate.go. DO NOT EDIT.
//
// To regenerate, run 'go generate' in internal/lvgen.
//
package libvirt
import (
"bytes"
"io"
"github.com/digitalocean/go-libvirt/internal/constants"
"github.com/digitalocean/go-libvirt/internal/go-xdr/xdr2"
)
// References to prevent "imported and not used" errors.
var (
_ = bytes.Buffer{}
_ = io.Copy
_ = constants.Program
_ = xdr.Unmarshal
)
//
// Typedefs:
//
//
// Enums:
//
// QEMUProcedure is libvirt's qemu_procedure
type QEMUProcedure int32
//
// Structs:
//
// QEMUDomainMonitorCommandArgs is libvirt's qemu_domain_monitor_command_args
type QEMUDomainMonitorCommandArgs struct {
Dom Domain
Cmd string
Flags uint32
}
// QEMUDomainMonitorCommandRet is libvirt's qemu_domain_monitor_command_ret
type QEMUDomainMonitorCommandRet struct {
Result string
}
// QEMUDomainAttachArgs is libvirt's qemu_domain_attach_args
type QEMUDomainAttachArgs struct {
PidValue uint32
Flags uint32
}
// QEMUDomainAttachRet is libvirt's qemu_domain_attach_ret
type QEMUDomainAttachRet struct {
Dom Domain
}
// QEMUDomainAgentCommandArgs is libvirt's qemu_domain_agent_command_args
type QEMUDomainAgentCommandArgs struct {
Dom Domain
Cmd string
Timeout int32
Flags uint32
}
// QEMUDomainAgentCommandRet is libvirt's qemu_domain_agent_command_ret
type QEMUDomainAgentCommandRet struct {
Result OptString
}
// QEMUConnectDomainMonitorEventRegisterArgs is libvirt's qemu_connect_domain_monitor_event_register_args
type QEMUConnectDomainMonitorEventRegisterArgs struct {
Dom OptDomain
Event OptString
Flags uint32
}
// QEMUConnectDomainMonitorEventRegisterRet is libvirt's qemu_connect_domain_monitor_event_register_ret
type QEMUConnectDomainMonitorEventRegisterRet struct {
CallbackID int32
}
// QEMUConnectDomainMonitorEventDeregisterArgs is libvirt's qemu_connect_domain_monitor_event_deregister_args
type QEMUConnectDomainMonitorEventDeregisterArgs struct {
CallbackID int32
}
// QEMUDomainMonitorEventMsg is libvirt's qemu_domain_monitor_event_msg
type QEMUDomainMonitorEventMsg struct {
CallbackID int32
Dom Domain
Event string
Seconds int64
Micros uint32
Details OptString
}
// QEMUDomainMonitorCommand is the go wrapper for QEMU_PROC_DOMAIN_MONITOR_COMMAND.
func (l *Libvirt) QEMUDomainMonitorCommand(Dom Domain, Cmd string, Flags uint32) (rResult string, err error) {
var buf []byte
args := QEMUDomainMonitorCommandArgs {
Dom: Dom,
Cmd: Cmd,
Flags: Flags,
}
buf, err = encode(&args)
if err != nil {
return
}
var r response
r, err = l.requestStream(1, constants.QEMUProgram, buf, nil, nil)
if err != nil {
return
}
// Return value unmarshaling
tpd := typedParamDecoder{}
ct := map[string]xdr.TypeDecoder{"libvirt.TypedParam": tpd}
rdr := bytes.NewReader(r.Payload)
dec := xdr.NewDecoderCustomTypes(rdr, 0, ct)
// Result: string
_, err = dec.Decode(&rResult)
if err != nil {
return
}
return
}
// QEMUDomainAttach is the go wrapper for QEMU_PROC_DOMAIN_ATTACH.
func (l *Libvirt) QEMUDomainAttach(PidValue uint32, Flags uint32) (rDom Domain, err error) {
var buf []byte
args := QEMUDomainAttachArgs {
PidValue: PidValue,
Flags: Flags,
}
buf, err = encode(&args)
if err != nil {
return
}
var r response
r, err = l.requestStream(2, constants.QEMUProgram, buf, nil, nil)
if err != nil {
return
}
// Return value unmarshaling
tpd := typedParamDecoder{}
ct := map[string]xdr.TypeDecoder{"libvirt.TypedParam": tpd}
rdr := bytes.NewReader(r.Payload)
dec := xdr.NewDecoderCustomTypes(rdr, 0, ct)
// Dom: Domain
_, err = dec.Decode(&rDom)
if err != nil {
return
}
return
}
// QEMUDomainAgentCommand is the go wrapper for QEMU_PROC_DOMAIN_AGENT_COMMAND.
func (l *Libvirt) QEMUDomainAgentCommand(Dom Domain, Cmd string, Timeout int32, Flags uint32) (rResult OptString, err error) {
var buf []byte
args := QEMUDomainAgentCommandArgs {
Dom: Dom,
Cmd: Cmd,
Timeout: Timeout,
Flags: Flags,
}
buf, err = encode(&args)
if err != nil {
return
}
var r response
r, err = l.requestStream(3, constants.QEMUProgram, buf, nil, nil)
if err != nil {
return
}
// Return value unmarshaling
tpd := typedParamDecoder{}
ct := map[string]xdr.TypeDecoder{"libvirt.TypedParam": tpd}
rdr := bytes.NewReader(r.Payload)
dec := xdr.NewDecoderCustomTypes(rdr, 0, ct)
// Result: OptString
_, err = dec.Decode(&rResult)
if err != nil {
return
}
return
}
// QEMUConnectDomainMonitorEventRegister is the go wrapper for QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_REGISTER.
func (l *Libvirt) QEMUConnectDomainMonitorEventRegister(Dom OptDomain, Event OptString, Flags uint32) (rCallbackID int32, err error) {
var buf []byte
args := QEMUConnectDomainMonitorEventRegisterArgs {
Dom: Dom,
Event: Event,
Flags: Flags,
}
buf, err = encode(&args)
if err != nil {
return
}
var r response
r, err = l.requestStream(4, constants.QEMUProgram, buf, nil, nil)
if err != nil {
return
}
// Return value unmarshaling
tpd := typedParamDecoder{}
ct := map[string]xdr.TypeDecoder{"libvirt.TypedParam": tpd}
rdr := bytes.NewReader(r.Payload)
dec := xdr.NewDecoderCustomTypes(rdr, 0, ct)
// CallbackID: int32
_, err = dec.Decode(&rCallbackID)
if err != nil {
return
}
return
}
// QEMUConnectDomainMonitorEventDeregister is the go wrapper for QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_DEREGISTER.
func (l *Libvirt) QEMUConnectDomainMonitorEventDeregister(CallbackID int32) (err error) {
var buf []byte
args := QEMUConnectDomainMonitorEventDeregisterArgs {
CallbackID: CallbackID,
}
buf, err = encode(&args)
if err != nil {
return
}
_, err = l.requestStream(5, constants.QEMUProgram, buf, nil, nil)
if err != nil {
return
}
return
}
// QEMUDomainMonitorEvent is the go wrapper for QEMU_PROC_DOMAIN_MONITOR_EVENT.
func (l *Libvirt) QEMUDomainMonitorEvent() (err error) {
var buf []byte
_, err = l.requestStream(6, constants.QEMUProgram, buf, nil, nil)
if err != nil {
return
}
return
}

File diff suppressed because it is too large Load diff

413
vendor/github.com/digitalocean/go-libvirt/rpc.go generated vendored Normal file
View file

@ -0,0 +1,413 @@
// Copyright 2018 The go-libvirt Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package libvirt
import (
"bytes"
"errors"
"fmt"
"io"
"reflect"
"strings"
"sync/atomic"
"github.com/digitalocean/go-libvirt/internal/constants"
"github.com/digitalocean/go-libvirt/internal/event"
xdr "github.com/digitalocean/go-libvirt/internal/go-xdr/xdr2"
"github.com/digitalocean/go-libvirt/socket"
)
// ErrUnsupported is returned if a procedure is not supported by libvirt
var ErrUnsupported = errors.New("unsupported procedure requested")
// internal rpc response
type response struct {
Payload []byte
Status uint32
}
// Error reponse from libvirt
type Error struct {
Code uint32
Message string
}
func (e Error) Error() string {
return e.Message
}
// checkError is used to check whether an error is a libvirtError, and if it is,
// whether its error code matches the one passed in. It will return false if
// these conditions are not met.
func checkError(err error, expectedError ErrorNumber) bool {
for err != nil {
e, ok := err.(Error)
if ok {
return e.Code == uint32(expectedError)
}
err = errors.Unwrap(err)
}
return false
}
// IsNotFound detects libvirt's ERR_NO_DOMAIN.
func IsNotFound(err error) bool {
return checkError(err, ErrNoDomain)
}
// callback sends RPC responses to respective callers.
func (l *Libvirt) callback(id int32, res response) {
l.cmux.Lock()
defer l.cmux.Unlock()
c, ok := l.callbacks[id]
if !ok {
return
}
c <- res
}
// Route sends incoming packets to their listeners.
func (l *Libvirt) Route(h *socket.Header, buf []byte) {
// Route events to their respective listener
var event event.Event
switch {
case h.Program == constants.QEMUProgram && h.Procedure == constants.QEMUProcDomainMonitorEvent:
event = &DomainEvent{}
case h.Program == constants.Program && h.Procedure == constants.ProcDomainEventCallbackLifecycle:
event = &DomainEventCallbackLifecycleMsg{}
}
if event != nil {
err := eventDecoder(buf, event)
if err != nil { // event was malformed, drop.
return
}
l.stream(event)
return
}
// send response to caller
l.callback(h.Serial, response{Payload: buf, Status: h.Status})
}
// serial provides atomic access to the next sequential request serial number.
func (l *Libvirt) serial() int32 {
return atomic.AddInt32(&l.s, 1)
}
// stream decodes and relays domain events to their respective listener.
func (l *Libvirt) stream(e event.Event) {
l.emux.RLock()
defer l.emux.RUnlock()
q, ok := l.events[e.GetCallbackID()]
if !ok {
return
}
q.Push(e)
}
// addStream configures the routing for an event stream.
func (l *Libvirt) addStream(s *event.Stream) {
l.emux.Lock()
defer l.emux.Unlock()
l.events[s.CallbackID] = s
}
// removeStream deletes an event stream. The caller should first notify libvirt
// to stop sending events for this stream. Subsequent calls to removeStream are
// idempotent and return nil.
func (l *Libvirt) removeStream(id int32) error {
l.emux.Lock()
defer l.emux.Unlock()
// if the event is already removed, just return nil
q, ok := l.events[id]
if ok {
delete(l.events, id)
q.Shutdown()
}
return nil
}
// removeAllStreams deletes all event streams. This is meant to be used to
// clean up only once the underlying connection to libvirt is disconnected and
// thus does not attempt to notify libvirt to stop sending events.
func (l *Libvirt) removeAllStreams() {
l.emux.Lock()
defer l.emux.Unlock()
for _, ev := range l.events {
ev.Shutdown()
delete(l.events, ev.CallbackID)
}
}
// register configures a method response callback
func (l *Libvirt) register(id int32, c chan response) {
l.cmux.Lock()
defer l.cmux.Unlock()
l.callbacks[id] = c
}
// deregister destroys a method response callback. It is the responsibility of
// the caller to manage locking (l.cmux) during this call.
func (l *Libvirt) deregister(id int32) {
_, ok := l.callbacks[id]
if !ok {
return
}
close(l.callbacks[id])
delete(l.callbacks, id)
}
// deregisterAll closes all waiting callback channels. This is used to clean up
// if the connection to libvirt is lost. Callers waiting for responses will
// return an error when the response channel is closed, rather than just
// hanging.
func (l *Libvirt) deregisterAll() {
l.cmux.Lock()
defer l.cmux.Unlock()
for id := range l.callbacks {
l.deregister(id)
}
}
// request performs a libvirt RPC request.
// returns response returned by server.
// if response is not OK, decodes error from it and returns it.
func (l *Libvirt) request(proc uint32, program uint32, payload []byte) (response, error) {
return l.requestStream(proc, program, payload, nil, nil)
}
// requestStream performs a libvirt RPC request. The `out` and `in` parameters
// are optional, and should be nil when RPC endpoints don't return a stream.
func (l *Libvirt) requestStream(proc uint32, program uint32, payload []byte,
out io.Reader, in io.Writer) (response, error) {
serial := l.serial()
c := make(chan response)
l.register(serial, c)
defer func() {
l.cmux.Lock()
defer l.cmux.Unlock()
l.deregister(serial)
}()
err := l.socket.SendPacket(serial, proc, program, payload, socket.Call,
socket.StatusOK)
if err != nil {
return response{}, err
}
resp, err := l.getResponse(c)
if err != nil {
return resp, err
}
if out != nil {
abort := make(chan bool)
outErr := make(chan error)
go func() {
outErr <- l.socket.SendStream(serial, proc, program, out, abort)
}()
// Even without incoming stream server sends confirmation once all data is received
resp, err = l.processIncomingStream(c, in)
if err != nil {
abort <- true
return resp, err
}
err = <-outErr
if err != nil {
return response{}, err
}
}
switch in {
case nil:
return resp, nil
default:
return l.processIncomingStream(c, in)
}
}
// processIncomingStream is called once we've successfully sent a request to
// libvirt. It writes the responses back to the stream passed by the caller
// until libvirt sends a packet with statusOK or an error.
func (l *Libvirt) processIncomingStream(c chan response, inStream io.Writer) (response, error) {
for {
resp, err := l.getResponse(c)
if err != nil {
return resp, err
}
// StatusOK indicates end of stream
if resp.Status == socket.StatusOK {
return resp, nil
}
// FIXME: this smells.
// StatusError is handled in getResponse, so this must be StatusContinue
// StatusContinue is only valid here for stream packets
// libvirtd breaks protocol and returns StatusContinue with an
// empty response Payload when the stream finishes
if len(resp.Payload) == 0 {
return resp, nil
}
if inStream != nil {
_, err = inStream.Write(resp.Payload)
if err != nil {
return response{}, err
}
}
}
}
func (l *Libvirt) getResponse(c chan response) (response, error) {
resp := <-c
if resp.Status == socket.StatusError {
return resp, decodeError(resp.Payload)
}
return resp, nil
}
// encode XDR encodes the provided data.
func encode(data interface{}) ([]byte, error) {
var buf bytes.Buffer
_, err := xdr.Marshal(&buf, data)
return buf.Bytes(), err
}
// decodeError extracts an error message from the provider buffer.
func decodeError(buf []byte) error {
dec := xdr.NewDecoder(bytes.NewReader(buf))
e := struct {
Code uint32
DomainID uint32
Padding uint8
Message string
Level uint32
}{}
_, err := dec.Decode(&e)
if err != nil {
return err
}
if strings.Contains(e.Message, "unknown procedure") {
return ErrUnsupported
}
// if libvirt returns ERR_OK, ignore the error
if ErrorNumber(e.Code) == ErrOk {
return nil
}
return Error{Code: uint32(e.Code), Message: e.Message}
}
// eventDecoder decodes an event from a xdr buffer.
func eventDecoder(buf []byte, e interface{}) error {
dec := xdr.NewDecoder(bytes.NewReader(buf))
_, err := dec.Decode(e)
return err
}
type typedParamDecoder struct{}
// Decode decodes a TypedParam. These are part of the libvirt spec, and not xdr
// proper. TypedParams contain a name, which is called Field for some reason,
// and a Value, which itself has a "discriminant" - an integer enum encoding the
// actual type, and a value, the length of which varies based on the actual
// type.
func (tpd typedParamDecoder) Decode(d *xdr.Decoder, v reflect.Value) (int, error) {
// Get the name of the typed param first
name, n, err := d.DecodeString()
if err != nil {
return n, err
}
val, n2, err := tpd.decodeTypedParamValue(d)
n += n2
if err != nil {
return n, err
}
tp := &TypedParam{Field: name, Value: *val}
v.Set(reflect.ValueOf(*tp))
return n, nil
}
// decodeTypedParamValue decodes the Value part of a TypedParam.
func (typedParamDecoder) decodeTypedParamValue(d *xdr.Decoder) (*TypedParamValue, int, error) {
// All TypedParamValues begin with a uint32 discriminant that tells us what
// type they are.
discriminant, n, err := d.DecodeUint()
if err != nil {
return nil, n, err
}
var n2 int
var tpv *TypedParamValue
switch discriminant {
case 1:
var val int32
n2, err = d.Decode(&val)
tpv = &TypedParamValue{D: discriminant, I: val}
case 2:
var val uint32
n2, err = d.Decode(&val)
tpv = &TypedParamValue{D: discriminant, I: val}
case 3:
var val int64
n2, err = d.Decode(&val)
tpv = &TypedParamValue{D: discriminant, I: val}
case 4:
var val uint64
n2, err = d.Decode(&val)
tpv = &TypedParamValue{D: discriminant, I: val}
case 5:
var val float64
n2, err = d.Decode(&val)
tpv = &TypedParamValue{D: discriminant, I: val}
case 6:
var val int32
n2, err = d.Decode(&val)
tpv = &TypedParamValue{D: discriminant, I: val}
case 7:
var val string
n2, err = d.Decode(&val)
tpv = &TypedParamValue{D: discriminant, I: val}
default:
err = fmt.Errorf("invalid parameter type %v", discriminant)
}
n += n2
return tpv, n, err
}

View file

@ -0,0 +1,26 @@
package dialers
import (
"net"
)
// AlreadyConnected implements a dialer interface for a connection that was
// established prior to initializing the socket object. This exists solely
// for backwards compatability with the previous implementation of Libvirt
// that took an already established connection.
type AlreadyConnected struct {
c net.Conn
}
// NewAlreadyConnected is a noop dialer to simply use a connection previously
// established. This means any re-dial attempts simply won't work.
func NewAlreadyConnected(c net.Conn) AlreadyConnected {
return AlreadyConnected{c}
}
// Dial just returns the connection previously established.
// If at some point it is disconnected by the client, this obviously does *not*
// re-dial and will simply return the already closed connection.
func (a AlreadyConnected) Dial() (net.Conn, error) {
return a.c, nil
}

View file

@ -0,0 +1,57 @@
package dialers
import (
"net"
"time"
)
const (
// defaultSocket specifies the default path to the libvirt unix socket.
defaultSocket = "/var/run/libvirt/libvirt-sock"
// defaultLocalTimeout specifies the default libvirt dial timeout.
defaultLocalTimeout = 15 * time.Second
)
// Local implements connecting to a local libvirtd over the unix socket.
type Local struct {
timeout time.Duration
socket string
}
// LocalOption is a function for setting local socket options.
type LocalOption func(*Local)
// WithLocalTimeout sets the dial timeout.
func WithLocalTimeout(timeout time.Duration) LocalOption {
return func(l *Local) {
l.timeout = timeout
}
}
// WithSocket sets the path to the local libvirt socket.
func WithSocket(socket string) LocalOption {
return func(l *Local) {
l.socket = socket
}
}
// NewLocal is a default dialer to simply connect to a locally running libvirt's
// socket.
func NewLocal(opts ...LocalOption) *Local {
l := &Local{
timeout: defaultLocalTimeout,
socket: defaultSocket,
}
for _, opt := range opts {
opt(l)
}
return l
}
// Dial connects to a local socket
func (l *Local) Dial() (net.Conn, error) {
return net.DialTimeout("unix", l.socket, l.timeout)
}

View file

@ -0,0 +1,61 @@
package dialers
import (
"net"
"time"
)
const (
// defaultRemotePort specifies the default libvirtd port.
defaultRemotePort = "16509"
// defaultRemoteTimeout specifies the default libvirt dial timeout.
defaultRemoteTimeout = 20 * time.Second
)
// Remote implements connecting to a remote server's libvirt using tcp
type Remote struct {
timeout time.Duration
host, port string
}
// RemoteOption is a function for setting remote dialer options.
type RemoteOption func(*Remote)
// WithRemoteTimeout sets the dial timeout.
func WithRemoteTimeout(timeout time.Duration) RemoteOption {
return func(r *Remote) {
r.timeout = timeout
}
}
// UsePort sets the port to dial for libirt on the target host server.
func UsePort(port string) RemoteOption {
return func(r *Remote) {
r.port = port
}
}
// NewRemote is a dialer for connecting to libvirt running on another server.
func NewRemote(hostAddr string, opts ...RemoteOption) *Remote {
r := &Remote{
timeout: defaultRemoteTimeout,
host: hostAddr,
port: defaultRemotePort,
}
for _, opt := range opts {
opt(r)
}
return r
}
// Dial connects to libvirt running on another server.
func (r *Remote) Dial() (net.Conn, error) {
return net.DialTimeout(
"tcp",
net.JoinHostPort(r.host, r.port),
r.timeout,
)
}

View file

@ -0,0 +1,376 @@
package socket
import (
"bufio"
"encoding/binary"
"errors"
"io"
"net"
"sync"
"syscall"
"time"
"unsafe"
"github.com/digitalocean/go-libvirt/internal/constants"
)
const disconnectTimeout = 5 * time.Second
// request and response statuses
const (
// StatusOK is always set for method calls or events.
// For replies it indicates successful completion of the method.
// For streams it indicates confirmation of the end of file on the stream.
StatusOK = iota
// StatusError for replies indicates that the method call failed
// and error information is being returned. For streams this indicates
// that not all data was sent and the stream has aborted.
StatusError
// StatusContinue is only used for streams.
// This indicates that further data packets will be following.
StatusContinue
)
// request and response types
const (
// Call is used when making calls to the remote server.
Call = iota
// Reply indicates a server reply.
Reply
// Message is an asynchronous notification.
Message
// Stream represents a stream data packet.
Stream
// CallWithFDs is used by a client to indicate the request has
// arguments with file descriptors.
CallWithFDs
// ReplyWithFDs is used by a server to indicate the request has
// arguments with file descriptors.
ReplyWithFDs
)
// Dialer is an interface for connecting to libvirt's underlying socket.
type Dialer interface {
Dial() (net.Conn, error)
}
// Router is an interface used to route packets to the appropriate clients.
type Router interface {
Route(*Header, []byte)
}
// Socket represents a libvirt Socket and its connection state
type Socket struct {
dialer Dialer
router Router
conn net.Conn
reader *bufio.Reader
writer *bufio.Writer
// used to serialize any Socket writes and any updates to conn, r, or w
mu *sync.Mutex
// disconnected is closed when the listen goroutine associated with a
// Socket connection has returned.
disconnected chan struct{}
}
// packet represents a RPC request or response.
type packet struct {
// Size of packet, in bytes, including length.
// Len + Header + Payload
Len uint32
Header Header
}
// Global packet instance, for use with unsafe.Sizeof()
var _p packet
// Header is a libvirt rpc packet header
type Header struct {
// Program identifier
Program uint32
// Program version
Version uint32
// Remote procedure identifier
Procedure uint32
// Call type, e.g., Reply
Type uint32
// Call serial number
Serial int32
// Request status, e.g., StatusOK
Status uint32
}
// New initializes a new type for managing the Socket.
func New(dialer Dialer, router Router) *Socket {
s := &Socket{
dialer: dialer,
router: router,
disconnected: make(chan struct{}),
mu: &sync.Mutex{},
}
// we start with a closed channel since that indicates no connection
close(s.disconnected)
return s
}
// Connect uses the dialer provided on creation to establish
// underlying physical connection to the desired libvirt.
func (s *Socket) Connect() error {
s.mu.Lock()
defer s.mu.Unlock()
if !s.isDisconnected() {
return errors.New("already connected to socket")
}
conn, err := s.dialer.Dial()
if err != nil {
return err
}
s.conn = conn
s.reader = bufio.NewReader(conn)
s.writer = bufio.NewWriter(conn)
s.disconnected = make(chan struct{})
go s.listenAndRoute()
return nil
}
// Disconnect closes the Socket connection to libvirt and waits for the reader
// gorouting to shut down.
func (s *Socket) Disconnect() error {
// just return if we're already disconnected
if s.isDisconnected() {
return nil
}
err := s.conn.Close()
if err != nil {
return err
}
// now we wait for the reader to return so as not to avoid it nil
// referencing
// Put this in a select,
// and have it only nil out the conn value if it doesn't fail
select {
case <-s.disconnected:
case <-time.After(disconnectTimeout):
return errors.New("timed out waiting for Disconnect cleanup")
}
return nil
}
// Disconnected returns a channel that will be closed once the current
// connection is closed. This can happen due to an explicit call to Disconnect
// from the client, or due to non-temporary Read or Write errors encountered.
func (s *Socket) Disconnected() <-chan struct{} {
return s.disconnected
}
// isDisconnected is a non-blocking function to query whether a connection
// is disconnected or not.
func (s *Socket) isDisconnected() bool {
select {
case <-s.disconnected:
return true
default:
return false
}
}
// listenAndRoute reads packets from the Socket and calls the provided
// Router function to route them
func (s *Socket) listenAndRoute() {
// only returns once it detects a non-temporary error related to the
// underlying connection
listen(s.reader, s.router)
// signal any clients listening that the connection has been disconnected
close(s.disconnected)
}
// listen processes incoming data and routes
// responses to their respective callback handler.
func listen(s io.Reader, router Router) {
for {
// response packet length
length, err := pktlen(s)
if err != nil {
if isTemporary(err) {
continue
}
// connection is no longer valid, so shutdown
return
}
// response header
h, err := extractHeader(s)
if err != nil {
// invalid packet
continue
}
// payload: packet length minus what was previously read
size := int(length) - int(unsafe.Sizeof(_p))
buf := make([]byte, size)
_, err = io.ReadFull(s, buf)
if err != nil {
// invalid packet
continue
}
// route response to caller
router.Route(h, buf)
}
}
// isTemporary returns true if the error returned from a read is transient.
// If the error type is an OpError, check whether the net connection
// error condition is temporary (which means we can keep using the
// connection).
// Errors not of the net.OpError type tend to be things like io.EOF,
// syscall.EINVAL, or io.ErrClosedPipe (i.e. all things that
// indicate the connection in use is no longer valid.)
func isTemporary(err error) bool {
opErr, ok := err.(*net.OpError)
if ok {
return opErr.Temporary()
}
return false
}
// pktlen returns the length of an incoming RPC packet. Read errors will
// result in a returned response length of 0 and a non-nil error.
func pktlen(r io.Reader) (uint32, error) {
buf := make([]byte, unsafe.Sizeof(_p.Len))
// extract the packet's length from the header
_, err := io.ReadFull(r, buf)
if err != nil {
return 0, err
}
return binary.BigEndian.Uint32(buf), nil
}
// extractHeader returns the decoded header from an incoming response.
func extractHeader(r io.Reader) (*Header, error) {
buf := make([]byte, unsafe.Sizeof(_p.Header))
// extract the packet's header from r
_, err := io.ReadFull(r, buf)
if err != nil {
return nil, err
}
return &Header{
Program: binary.BigEndian.Uint32(buf[0:4]),
Version: binary.BigEndian.Uint32(buf[4:8]),
Procedure: binary.BigEndian.Uint32(buf[8:12]),
Type: binary.BigEndian.Uint32(buf[12:16]),
Serial: int32(binary.BigEndian.Uint32(buf[16:20])),
Status: binary.BigEndian.Uint32(buf[20:24]),
}, nil
}
// SendPacket sends a packet to libvirt on the socket connection.
func (s *Socket) SendPacket(
serial int32,
proc uint32,
program uint32,
payload []byte,
typ uint32,
status uint32,
) error {
p := packet{
Header: Header{
Program: program,
Version: constants.ProtocolVersion,
Procedure: proc,
Type: typ,
Serial: serial,
Status: status,
},
}
size := int(unsafe.Sizeof(p.Len)) + int(unsafe.Sizeof(p.Header))
if payload != nil {
size += len(payload)
}
p.Len = uint32(size)
if s.isDisconnected() {
// this mirrors what a lot of net code return on use of a no
// longer valid connection
return syscall.EINVAL
}
s.mu.Lock()
defer s.mu.Unlock()
err := binary.Write(s.writer, binary.BigEndian, p)
if err != nil {
return err
}
// write payload
if payload != nil {
err = binary.Write(s.writer, binary.BigEndian, payload)
if err != nil {
return err
}
}
return s.writer.Flush()
}
// SendStream sends a stream of packets to libvirt on the socket connection.
func (s *Socket) SendStream(serial int32, proc uint32, program uint32,
stream io.Reader, abort chan bool) error {
// Keep total packet length under 4 MiB to follow possible limitation in libvirt server code
buf := make([]byte, 4*MiB-unsafe.Sizeof(_p))
for {
select {
case <-abort:
return s.SendPacket(serial, proc, program, nil, Stream, StatusError)
default:
}
n, err := stream.Read(buf)
if n > 0 {
err2 := s.SendPacket(serial, proc, program, buf[:n], Stream, StatusContinue)
if err2 != nil {
return err2
}
}
if err != nil {
if err == io.EOF {
return s.SendPacket(serial, proc, program, nil, Stream, StatusOK)
}
// keep original error
err2 := s.SendPacket(serial, proc, program, nil, Stream, StatusError)
if err2 != nil {
return err2
}
return err
}
}
}

View file

@ -0,0 +1,27 @@
// Copyright 2016 The go-libvirt Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This module provides different units of measurement to make other
// code more readable.
package socket
const (
// B - byte
B = 1
// KiB - kibibyte
KiB = 1024 * B
// MiB - mebibyte
MiB = 1024 * KiB
)

17
vendor/github.com/digitalocean/go-qemu/AUTHORS generated vendored Normal file
View file

@ -0,0 +1,17 @@
Maintainer
----------
DigitalOcean, Inc
Original Authors
----------------
Ben LeMasurier <blemasurier@digitalocean.com>
Matt Layher <mlayher@digitalocean.com>
Contributors
------------
David Anderson <dave@natulte.net>
Justin Kim <justin@digitalocean.com>
Luis Sagastume <lsagastume1990@gmail.com>
Nedim Dedic <nedim_dedic@yahoo.com>
Roberto J Rojas <robertojrojas@gmail.com>
Marko Mudrinic <mudrinic.mare@gmail.com>

195
vendor/github.com/digitalocean/go-qemu/LICENSE.md generated vendored Normal file
View file

@ -0,0 +1,195 @@
Apache License
==============
_Version 2.0, January 2004_
_&lt;<http://www.apache.org/licenses/>&gt;_
### Terms and Conditions for use, reproduction, and distribution
#### 1. Definitions
“License” shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
“Licensor” shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
“Legal Entity” shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, “control” means **(i)** the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
outstanding shares, or **(iii)** beneficial ownership of such entity.
“You” (or “Your”) shall mean an individual or Legal Entity exercising
permissions granted by this License.
“Source” form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
“Object” form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
“Work” shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
“Derivative Works” shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
“Contribution” shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
“submitted” means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as “Not a Contribution.”
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
#### 2. Grant of Copyright License
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
#### 3. Grant of Patent License
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
#### 4. Redistribution
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
this License; and
* **(b)** You must cause any modified files to carry prominent notices stating that You
changed the files; and
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
#### 5. Submission of Contributions
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
#### 6. Trademarks
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
#### 7. Disclaimer of Warranty
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
#### 8. Limitation of Liability
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
#### 9. Accepting Warranty or Additional Liability
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
_END OF TERMS AND CONDITIONS_
### APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets `[]` replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same “printed page” as the copyright notice for easier identification within
third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

104
vendor/github.com/digitalocean/go-qemu/qmp/README.md generated vendored Normal file
View file

@ -0,0 +1,104 @@
QMP
===
Package `qmp` enables interaction with QEMU instances via the QEMU Machine Protocol (QMP).
## Available Drivers
### Libvirt
If your environment is managed by Libvirt, QMP interaction must be proxied through the Libvirt daemon. This can be be done through two available drivers:
#### RPC
The RPC driver provides a pure Go implementation of Libvirt's RPC protocol.
```go
//conn, err := net.DialTimeout("unix", "/var/run/libvirt/libvirt-sock", 2*time.Second)
conn, err := net.DialTimeout("tcp", "192.168.1.1:16509", 2*time.Second)
monitor := libvirtrpc.New("stage-lb-1", conn)
```
#### virsh
A connection to the monitor socket is provided by proxing requests through the `virsh` executable.
```go
monitor, err := qmp.NewLibvirtMonitor("qemu:///system", "stage-lb-1")
```
### Socket
If your QEMU instances are not managed by libvirt, direct communication over its UNIX socket is available.
```go
monitor, err := qmp.NewSocketMonitor("unix", "/var/lib/qemu/example.monitor", 2*time.Second)
```
## Examples
Using the above to establish a new `qmp.Monitor`, the following examples provide a brief overview of QMP usage.
_error checking omitted for the sake of brevity._
### Command Execution
```go
type StatusResult struct {
ID string `json:"id"`
Return struct {
Running bool `json:"running"`
Singlestep bool `json:"singlestep"`
Status string `json:"status"`
} `json:"return"`
}
monitor.Connect()
defer monitor.Disconnect()
cmd := []byte(`{ "execute": "query-status" }`)
raw, _ := monitor.Run(cmd)
var result StatusResult
json.Unmarshal(raw, &result)
fmt.Println(result.Return.Status)
```
```
running
```
### Event Monitor
```go
monitor.Connect()
defer monitor.Disconnect()
stream, _ := monitor.Events()
for e := range stream {
log.Printf("EVENT: %s", e.Event)
}
```
```
$ virsh reboot example
Domain example is being rebooted
```
```
EVENT: POWERDOWN
EVENT: SHUTDOWN
EVENT: STOP
EVENT: RESET
EVENT: RESUME
EVENT: RESET
...
```
## More information
* [QEMU QMP Wiki](http://wiki.qemu.org/QMP)
* [QEMU QMP Intro](http://git.qemu.org/?p=qemu.git;a=blob_plain;f=docs/qmp-intro.txt;hb=HEAD)
* [QEMU QMP Events](http://git.qemu.org/?p=qemu.git;a=blob_plain;f=docs/qmp-events.txt;hb=HEAD)
* [QEMU QMP Spec](http://git.qemu.org/?p=qemu.git;a=blob_plain;f=docs/qmp-spec.txt;hb=HEAD)

94
vendor/github.com/digitalocean/go-qemu/qmp/qmp.go generated vendored Normal file
View file

@ -0,0 +1,94 @@
// Copyright 2016 The go-qemu Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package qmp enables interaction with QEMU instances
// via the QEMU Machine Protocol (QMP).
package qmp
import (
"context"
"errors"
"fmt"
)
// ErrEventsNotSupported is returned by Events() if event streams
// are unsupported by either QEMU or libvirt.
var ErrEventsNotSupported = errors.New("event monitor is not supported")
// Monitor represents a QEMU Machine Protocol socket.
// See: http://wiki.qemu.org/QMP
type Monitor interface {
Connect() error
Disconnect() error
Run(command []byte) (out []byte, err error)
Events(context.Context) (events <-chan Event, err error)
}
// Command represents a QMP command.
type Command struct {
// Name of the command to run
Execute string `json:"execute"`
// Optional arguments for the above command.
Args interface{} `json:"arguments,omitempty"`
}
type response struct {
ID string `json:"id"`
Return interface{} `json:"return,omitempty"`
Error struct {
Class string `json:"class"`
Desc string `json:"desc"`
} `json:"error,omitempty"`
}
func (r *response) Err() error {
if r.Error.Desc == "" {
return nil
}
return errors.New(r.Error.Desc)
}
// Event represents a QEMU QMP event.
// See http://wiki.qemu.org/QMP
type Event struct {
// Event name, e.g., BLOCK_JOB_COMPLETE
Event string `json:"event"`
// Arbitrary event data
Data map[string]interface{} `json:"data"`
// Event timestamp, provided by QEMU.
Timestamp struct {
Seconds int64 `json:"seconds"`
Microseconds int64 `json:"microseconds"`
} `json:"timestamp"`
}
// Version is the QEMU version structure returned when a QMP connection is
// initiated.
type Version struct {
Package string `json:"package"`
QEMU struct {
Major int `json:"major"`
Micro int `json:"micro"`
Minor int `json:"minor"`
} `json:"qemu"`
}
func (v Version) String() string {
q := v.QEMU
return fmt.Sprintf("%d.%d.%d", q.Major, q.Minor, q.Micro)
}

107
vendor/github.com/digitalocean/go-qemu/qmp/rpc.go generated vendored Normal file
View file

@ -0,0 +1,107 @@
// Copyright 2016 The go-qemu Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package qmp
import (
"context"
"encoding/json"
"net"
"github.com/digitalocean/go-libvirt"
)
var _ Monitor = &LibvirtRPCMonitor{}
// A LibvirtRPCMonitor implements LibVirt's remote procedure call protocol.
type LibvirtRPCMonitor struct {
l *libvirt.Libvirt
// Domain name as seen by libvirt, e.g., stage-lb-1
Domain string
}
// NewLibvirtRPCMonitor configures a new Libvirt RPC Monitor connection.
// The provided domain should be the name of the domain as seen
// by libvirt, e.g., stage-lb-1.
func NewLibvirtRPCMonitor(domain string, conn net.Conn) *LibvirtRPCMonitor {
l := libvirt.New(conn)
return &LibvirtRPCMonitor{
l: l,
Domain: domain,
}
}
// Connect establishes communication with the libvirt server.
// The underlying libvirt socket connection must be previously established.
func (rpc *LibvirtRPCMonitor) Connect() error {
return rpc.l.Connect()
}
// Disconnect shuts down communication with the libvirt server
// and closes the underlying net.Conn.
func (rpc *LibvirtRPCMonitor) Disconnect() error {
return rpc.l.Disconnect()
}
// Events streams QEMU QMP Events until the provided context is cancelled.
// If a problem is encountered setting up the event monitor connection
// an error will be returned. Errors encountered during streaming will
// cause the returned event channel to be closed.
func (rpc *LibvirtRPCMonitor) Events(ctx context.Context) (<-chan Event, error) {
events, err := rpc.l.SubscribeQEMUEvents(ctx, rpc.Domain)
if err != nil {
return nil, err
}
c := make(chan Event)
go func() {
defer close(c)
// process events
for e := range events {
qe, err := qmpEvent(&e)
if err != nil {
break
}
c <- *qe
}
}()
return c, nil
}
// Run executes the given QAPI command against a domain's QEMU instance.
// For a list of available QAPI commands, see:
// http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD
func (rpc *LibvirtRPCMonitor) Run(cmd []byte) ([]byte, error) {
return rpc.l.Run(rpc.Domain, cmd)
}
// qmpEvent takes a libvirt DomainEvent and returns the QMP equivalent.
func qmpEvent(e *libvirt.DomainEvent) (*Event, error) {
var qe Event
if e.Details != nil {
if err := json.Unmarshal(e.Details, &qe.Data); err != nil {
return nil, err
}
}
qe.Event = e.Event
qe.Timestamp.Seconds = int64(e.Seconds)
qe.Timestamp.Microseconds = int64(e.Microseconds)
return &qe, nil
}

279
vendor/github.com/digitalocean/go-qemu/qmp/socket.go generated vendored Normal file
View file

@ -0,0 +1,279 @@
// Copyright 2016 The go-qemu Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package qmp
import (
"bufio"
"context"
"encoding/json"
"fmt"
"io"
"net"
"os"
"sync"
"sync/atomic"
"time"
)
// A SocketMonitor is a Monitor which speaks directly to a QEMU Machine Protocol
// (QMP) socket. Communication is performed directly using a QEMU monitor socket,
// typically using a UNIX socket or TCP connection. Multiple connections to the
// same domain are not permitted, and will result in the monitor blocking until
// the existing connection is closed.
type SocketMonitor struct {
// QEMU version reported by a connected monitor socket.
Version *Version
// QEMU QMP capabiltiies reported by a connected monitor socket.
Capabilities []string
// Underlying connection
c net.Conn
// Serialize running command against domain
mu sync.Mutex
// Send command responses and errors
stream <-chan streamResponse
// Send domain events to listeners when available
listeners *int32
events <-chan Event
}
// NewSocketMonitor configures a connection to the provided QEMU monitor socket.
// An error is returned if the socket cannot be successfully dialed, or the
// dial attempt times out.
//
// NewSocketMonitor may dial the QEMU socket using a variety of connection types:
// NewSocketMonitor("unix", "/var/lib/qemu/example.monitor", 2 * time.Second)
// NewSocketMonitor("tcp", "8.8.8.8:4444", 2 * time.Second)
func NewSocketMonitor(network, addr string, timeout time.Duration) (*SocketMonitor, error) {
c, err := net.DialTimeout(network, addr, timeout)
if err != nil {
return nil, err
}
mon := &SocketMonitor{
c: c,
listeners: new(int32),
}
return mon, nil
}
// Listen creates a new SocketMonitor listening for a single connection to the provided socket file or address.
// An error is returned if unable to listen at the specified file path or port.
//
// Listen will wait for a QEMU socket connection using a variety connection types:
// Listen("unix", "/var/lib/qemu/example.monitor")
// Listen("tcp", "0.0.0.0:4444")
func Listen(network, addr string) (*SocketMonitor, error) {
l, err := net.Listen(network, addr)
if err != nil {
return nil, err
}
c, err := l.Accept()
defer l.Close()
if err != nil {
return nil, err
}
mon := &SocketMonitor{
c: c,
listeners: new(int32),
}
return mon, nil
}
// Disconnect closes the QEMU monitor socket connection.
func (mon *SocketMonitor) Disconnect() error {
atomic.StoreInt32(mon.listeners, 0)
err := mon.c.Close()
if mon.stream != nil {
for range mon.stream {
}
}
return err
}
// qmpCapabilities is the command which must be executed to perform the
// QEMU QMP handshake.
const qmpCapabilities = "qmp_capabilities"
// Connect sets up a QEMU QMP connection by connecting directly to the QEMU
// monitor socket. An error is returned if the capabilities handshake does
// not succeed.
func (mon *SocketMonitor) Connect() error {
enc := json.NewEncoder(mon.c)
dec := json.NewDecoder(mon.c)
// Check for banner on startup
var ban banner
if err := dec.Decode(&ban); err != nil {
return err
}
mon.Version = &ban.QMP.Version
mon.Capabilities = ban.QMP.Capabilities
// Issue capabilities handshake
cmd := Command{Execute: qmpCapabilities}
if err := enc.Encode(cmd); err != nil {
return err
}
// Check for no error on return
var r response
if err := dec.Decode(&r); err != nil {
return err
}
if err := r.Err(); err != nil {
return err
}
// Initialize socket listener for command responses and asynchronous
// events
events := make(chan Event)
stream := make(chan streamResponse)
go mon.listen(mon.c, events, stream)
mon.events = events
mon.stream = stream
return nil
}
// Events streams QEMU QMP Events.
// Events should only be called once per Socket. If used with a qemu.Domain,
// qemu.Domain.Events should be called to retrieve events instead.
func (mon *SocketMonitor) Events(context.Context) (<-chan Event, error) {
atomic.AddInt32(mon.listeners, 1)
return mon.events, nil
}
// listen listens for incoming data from a QEMU monitor socket. It determines
// if the data is an asynchronous event or a response to a command, and returns
// the data on the appropriate channel.
func (mon *SocketMonitor) listen(r io.Reader, events chan<- Event, stream chan<- streamResponse) {
defer close(events)
defer close(stream)
scanner := bufio.NewScanner(r)
for scanner.Scan() {
var e Event
b := scanner.Bytes()
if err := json.Unmarshal(b, &e); err != nil {
continue
}
// If data does not have an event type, it must be in response to a command.
if e.Event == "" {
stream <- streamResponse{buf: b}
continue
}
// If nobody is listening for events, do not bother sending them.
if atomic.LoadInt32(mon.listeners) == 0 {
continue
}
events <- e
}
if err := scanner.Err(); err != nil {
stream <- streamResponse{err: err}
}
}
// Run executes the given QAPI command against a domain's QEMU instance.
// For a list of available QAPI commands, see:
// http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD
func (mon *SocketMonitor) Run(command []byte) ([]byte, error) {
// Just call RunWithFile with no file
return mon.RunWithFile(command, nil)
}
// RunWithFile behaves like Run but allows for passing a file through out-of-band data.
func (mon *SocketMonitor) RunWithFile(command []byte, file *os.File) ([]byte, error) {
// Only allow a single command to be run at a time to ensure that responses
// to a command cannot be mixed with responses from another command
mon.mu.Lock()
defer mon.mu.Unlock()
if file == nil {
// Just send a normal command through.
if _, err := mon.c.Write(command); err != nil {
return nil, err
}
} else {
unixConn, ok := mon.c.(*net.UnixConn)
if !ok {
return nil, fmt.Errorf("RunWithFile only works with unix monitor sockets")
}
oobSupported := false
for _, capability := range mon.Capabilities {
if capability == "oob" {
oobSupported = true
break
}
}
if !oobSupported {
return nil, fmt.Errorf("The QEMU server doesn't support oob (needed for RunWithFile)")
}
// Send the command along with the file descriptor.
oob := getUnixRights(file)
if _, _, err := unixConn.WriteMsgUnix(command, oob, nil); err != nil {
return nil, err
}
}
// Wait for a response or error to our command
res := <-mon.stream
if res.err != nil {
return nil, res.err
}
// Check for QEMU errors
var r response
if err := json.Unmarshal(res.buf, &r); err != nil {
return nil, err
}
if err := r.Err(); err != nil {
return nil, err
}
return res.buf, nil
}
// banner is a wrapper type around a Version.
type banner struct {
QMP struct {
Capabilities []string `json:"capabilities"`
Version Version `json:"version"`
} `json:"QMP"`
}
// streamResponse is a struct sent over a channel in response to a command.
type streamResponse struct {
buf []byte
err error
}

View file

@ -0,0 +1,27 @@
// Copyright 2016 The go-qemu Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !windows
package qmp
import (
"os"
"golang.org/x/sys/unix"
)
func getUnixRights(file *os.File) []byte {
return unix.UnixRights(int(file.Fd()))
}

View file

@ -0,0 +1,25 @@
// Copyright 2016 The go-qemu Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build windows
package qmp
import (
"os"
)
func getUnixRights(file *os.File) []byte {
return nil
}