From b5f54a9b23e8d9418700494da9aa78d8db354c43 Mon Sep 17 00:00:00 2001 From: baude Date: Mon, 15 Mar 2021 14:52:43 -0500 Subject: introduce podman machine podman machine allows podman to create, manage, and interact with a vm running some form of linux (default is fcos). podman is then configured to be able to interact with the vm automatically. while this is usable on linux, the real push is to get this working on both current apple architectures in macos. Ashley Cui contributed to this PR and was a great help. [NO TESTS NEEDED] Signed-off-by: baude --- vendor/github.com/digitalocean/go-qemu/AUTHORS | 17 ++ vendor/github.com/digitalocean/go-qemu/LICENSE.md | 195 +++++++++++++++ .../github.com/digitalocean/go-qemu/qmp/README.md | 104 ++++++++ vendor/github.com/digitalocean/go-qemu/qmp/qmp.go | 94 +++++++ vendor/github.com/digitalocean/go-qemu/qmp/rpc.go | 107 ++++++++ .../github.com/digitalocean/go-qemu/qmp/socket.go | 274 +++++++++++++++++++++ .../digitalocean/go-qemu/qmp/socket_unix.go | 27 ++ .../digitalocean/go-qemu/qmp/socket_windows.go | 25 ++ 8 files changed, 843 insertions(+) create mode 100644 vendor/github.com/digitalocean/go-qemu/AUTHORS create mode 100644 vendor/github.com/digitalocean/go-qemu/LICENSE.md create mode 100644 vendor/github.com/digitalocean/go-qemu/qmp/README.md create mode 100644 vendor/github.com/digitalocean/go-qemu/qmp/qmp.go create mode 100644 vendor/github.com/digitalocean/go-qemu/qmp/rpc.go create mode 100644 vendor/github.com/digitalocean/go-qemu/qmp/socket.go create mode 100644 vendor/github.com/digitalocean/go-qemu/qmp/socket_unix.go create mode 100644 vendor/github.com/digitalocean/go-qemu/qmp/socket_windows.go (limited to 'vendor/github.com/digitalocean/go-qemu') diff --git a/vendor/github.com/digitalocean/go-qemu/AUTHORS b/vendor/github.com/digitalocean/go-qemu/AUTHORS new file mode 100644 index 000000000..e2836c3c7 --- /dev/null +++ b/vendor/github.com/digitalocean/go-qemu/AUTHORS @@ -0,0 +1,17 @@ +Maintainer +---------- +DigitalOcean, Inc + +Original Authors +---------------- +Ben LeMasurier +Matt Layher + +Contributors +------------ +David Anderson +Justin Kim +Luis Sagastume +Nedim Dedic +Roberto J Rojas +Marko Mudrinic diff --git a/vendor/github.com/digitalocean/go-qemu/LICENSE.md b/vendor/github.com/digitalocean/go-qemu/LICENSE.md new file mode 100644 index 000000000..f5f4b8b5e --- /dev/null +++ b/vendor/github.com/digitalocean/go-qemu/LICENSE.md @@ -0,0 +1,195 @@ +Apache License +============== + +_Version 2.0, January 2004_ +_<>_ + +### 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. + diff --git a/vendor/github.com/digitalocean/go-qemu/qmp/README.md b/vendor/github.com/digitalocean/go-qemu/qmp/README.md new file mode 100644 index 000000000..729280b31 --- /dev/null +++ b/vendor/github.com/digitalocean/go-qemu/qmp/README.md @@ -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) diff --git a/vendor/github.com/digitalocean/go-qemu/qmp/qmp.go b/vendor/github.com/digitalocean/go-qemu/qmp/qmp.go new file mode 100644 index 000000000..b856260d3 --- /dev/null +++ b/vendor/github.com/digitalocean/go-qemu/qmp/qmp.go @@ -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) +} diff --git a/vendor/github.com/digitalocean/go-qemu/qmp/rpc.go b/vendor/github.com/digitalocean/go-qemu/qmp/rpc.go new file mode 100644 index 000000000..cb11c71fe --- /dev/null +++ b/vendor/github.com/digitalocean/go-qemu/qmp/rpc.go @@ -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() { + // process events + for e := range events { + qe, err := qmpEvent(&e) + if err != nil { + close(c) + 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 +} diff --git a/vendor/github.com/digitalocean/go-qemu/qmp/socket.go b/vendor/github.com/digitalocean/go-qemu/qmp/socket.go new file mode 100644 index 000000000..541a88676 --- /dev/null +++ b/vendor/github.com/digitalocean/go-qemu/qmp/socket.go @@ -0,0 +1,274 @@ +// 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() + + 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 +} diff --git a/vendor/github.com/digitalocean/go-qemu/qmp/socket_unix.go b/vendor/github.com/digitalocean/go-qemu/qmp/socket_unix.go new file mode 100644 index 000000000..859ff64ea --- /dev/null +++ b/vendor/github.com/digitalocean/go-qemu/qmp/socket_unix.go @@ -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())) +} diff --git a/vendor/github.com/digitalocean/go-qemu/qmp/socket_windows.go b/vendor/github.com/digitalocean/go-qemu/qmp/socket_windows.go new file mode 100644 index 000000000..b8a611dbf --- /dev/null +++ b/vendor/github.com/digitalocean/go-qemu/qmp/socket_windows.go @@ -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 +} -- cgit v1.2.3-54-g00ecf