[libvirt] [PATCH] Add some docs about the RPC protocol and APIs
by Daniel P. Berrange
From: "Daniel P. Berrange" <berrange(a)redhat.com>
* remote.html.in: Remove obsolete notes about internals of the
RPC protocol
* internals/rpc.html.in: Extensive docs on RPC protocol/API
* sitemap.html.in: Add new page
---
docs/internals/rpc.html.in | 876 ++++++++++++++++++++++++++++++++++++++++++++
docs/remote.html.in | 45 ---
docs/sitemap.html.in | 4 +
3 files changed, 880 insertions(+), 45 deletions(-)
create mode 100644 docs/internals/rpc.html.in
diff --git a/docs/internals/rpc.html.in b/docs/internals/rpc.html.in
new file mode 100644
index 0000000..761832a
--- /dev/null
+++ b/docs/internals/rpc.html.in
@@ -0,0 +1,876 @@
+<html>
+ <body>
+ <h1>libvirt RPC infrastructure</h1>
+
+ <ul id="toc"></ul>
+
+ <p>
+ libvirt includes a basic protocol and code to implement
+ an extensible, secure client/server RPC service. This was
+ originally designed for communication between the libvirt
+ client library and the libvirtd daemon. It is also also
+ used for communication to the virtlockd daemon and (soon)
+ for the libvirt_lxc controller process. This document
+ provides an overview of the protocol and structure / operation
+ of the internal RPC library APIs.
+ </p>
+
+
+ <h2><a name="protocol">RPC protocol</a></h2>
+
+ <p>
+ libvirt uses a simple, variable length, packet based RPC protocol.
+ All structured data within packets is encoded using the
+ <a href="http://en.wikipedia.org/wiki/External_Data_Representation">XDR standard</a>
+ as currently defined by <a href="https://tools.ietf.org/html/rfc4506">RFC 4506</a>.
+ On any connection running the RPC protocol, there can be multiple
+ programs active, each supporting one or more versions. A program
+ defines a set of procedures that it supports. The procedures can
+ support call+reply method invocation, asynchronous events,
+ and generic data streams. Method invocations can be overlapped,
+ so waiting for a reply to one will not block the receipt of the
+ reply to another outstanding method. The protocol was loosely
+ inspired by the design of SunRPC. The definition of the RC
+ protocol is in the file <code>src/rpc/virnetprotocol.x</code>
+ in the libvirt source tree.
+ </p>
+
+ <h3><a href="protocolframing">Packet framing</a></h3>
+
+ <p>
+ On the wire, there is no explicit packet framing marker. Instead
+ each packet is preceeded by an unsigned 32-bit integer giving
+ the total length of the packet in bytes. This length includes
+ the 4-bytes of the length word itself. Conceptually the framing
+ looks like this:
+ </p>
+
+<pre>
+|~~~ Packet 1 ~~~|~~~ Packet 2 ~~~|~~~ Packet 3 ~~~|~~~
+
++-------+------------+-------+------------+-------+------------+...
+| n=U32 | (n-4) * U8 | n=U32 | (n-4) * U8 | n=U32 | (n-4) * U8 |
++-------+------------+-------+------------+-------+------------+...
+
+|~ Len ~|~ Data ~|~ Len ~|~ Data ~|~ Len ~|~ Data ~|~
+
+</pre>
+
+ <h3><a href="protocoldata">Packet data</a></h3>
+
+ <p>
+ The data in each packet is split into two parts, a short
+ fixed length header, followed by a variable length payload.
+ So a packet from the illustration above is more correctly
+ shown as
+ </p>
+
+<pre>
+
++-------+-------------+---------------....---+
+| n=U32 | 6*U32 | (n-(7*4))*U8 |
++-------+-------------+---------------....---+
+
+|~ Len ~|~ Header ~|~ Payload .... ~|
+</pre>
+
+
+ <h3><a href="protocolheader">Packet header</a></h3>
+ <p>
+ The header contains 6 fields, encoded as signed/unsigned 32-bit
+ integers.
+ </p>
+
+ <pre>
++---------------+
+| program=U32 |
++---------------+
+| version=U32 |
++---------------+
+| procedure=S32 |
++---------------+
+| type=S32 |
++---------------+
+| serial=U32 |
++---------------+
+| status=S32 |
++---------------+
+ </pre>
+
+ <dl>
+ <dt><code>program</code></dt>
+ <dd>
+ This is an arbitrarily chosen number that will uniquely
+ identify the "service" running over the stream.
+ </dd>
+ <dt><code>version</code></dt>
+ <dd>
+ This is the version number of the program, by convention
+ starting from '1'. When an incompatible change is made
+ to a program, the version number is incremented. Ideally
+ both versions will then be supported on the wire in
+ parallel for backwards compatibility.
+ </dd>
+ <dt><code>procedure</code></dt>
+ <dd>
+ This is an arbitrarily chosen number that will uniqely
+ identify the method call, or event associated with the
+ packet. By convention, procedure numbers start from 1
+ and are assigned monotonically thereafter.
+ </dd>
+ <dt><code>type</code></dt>
+ <dd>
+ <p>
+ This can be one of the following enumeration values
+ </p>
+ <ol>
+ <li>call: invocation of a method call</li>
+ <li>reply: completion of a method call</li>
+ <li>event: an asynchronous event</li>
+ <li>stream: control info or data from a stream</li>
+ </ol>
+ </dd>
+ <dt><code>serial</code></dt>
+ <dd>
+ This is an number that starts from 1 and increases
+ each time a method call packet is sent. A reply or
+ stream packet will have a serial number matching the
+ original method call packet serial. Events always
+ have the serial number set to 0.
+ </dd>
+ <dt><code>status</code></dt>
+ <dd>
+ <p>
+ This can one of the following enumeration values
+ </p>
+ <ol>
+ <li>ok: a normal packet. this is always set for method calls or events.
+ For replies it indicates succesful completion of the method. For
+ streams it indicates confirmation of the end of file on the stream.</li>
+ <li>error: for replies this 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</li>
+ <li>continue: for streams this indicates that further data packets
+ will be following</li>
+ </ol>
+ </dl>
+
+ <h3><a href="protocolpayload">Packet payload</a></h3>
+
+ <p>
+ The payload of a packet will vary depending on the <code>type</code>
+ and <code>status</code> fields from the header.
+ </p>
+
+ <ul>
+ <li>type=call: the in parameters for the method call, XDR encoded</li>
+ <li>type=reply+status=ok: the return value and/or out parameters for the method call, XDR encoded</li>
+ <li>type=reply+status=error: the error information for the method, a virErrorPtr XDR encoded</li>
+ <li>type=event: the parameters for the event, XDR encoded</li>
+ <li>type=stream+status=ok: no payload</li>
+ <li>type=stream+status=error: the error information for the method, a virErrorPtr XDR encoded</li>
+ <li>type=stream+status=continue: the raw bytes of data for the stream. No XDR encoding</li>
+ </ul>
+
+ <p>
+ For the exact payload information for each procedure, consult the XDR protocol
+ definition for the program+version in question
+ </p>
+
+ <h3><a name="wireexamples">Wire examples</a></h3>
+
+ <p>
+ The following diagrams illustrate some example packet exchanges
+ between a client and server
+ </p>
+
+ <h4><a name="wireexamplescall">Method call</a></h4>
+
+ <p>
+ A single method call and succesful
+ reply, for a program=8, version=1, procedure=3, which 10 bytes worth
+ of input args, and 4 bytes worth of return values. The overall input
+ packet length is is 4 + 24 + 10 == 38, and output packet length 32
+ </p>
+
+ <pre>
+ +--+-----------------------+-----------+
+ C --> |38| 8 | 1 | 3 | 0 | 1 | 0 | .o.oOo.o. | --> S (call)
+ +--+-----------------------+-----------+
+
+ +--+-----------------------+--------+
+ C <-- |32| 8 | 1 | 3 | 1 | 1 | 0 | .o.oOo | <-- S (reply)
+ +--+-----------------------+--------+
+ </pre>
+
+ <h4><a name="wireexamplescallerr">Method call with error</a></h4>
+
+ <p>
+ An unsuccessful method call will instead return an error object
+ </p>
+
+ <pre>
+ +--+-----------------------+-----------+
+ C --> |38| 8 | 1 | 3 | 0 | 1 | 0 | .o.oOo.o. | --> S (call)
+ +--+-----------------------+-----------+
+
+ +--+-----------------------+--------------------------+
+ C <-- |48| 8 | 1 | 3 | 2 | 1 | 0 | .o.oOo.o.oOo.o.oOo.o.oOo | <-- S (error)
+ +--+-----------------------+--------------------------+
+ </pre>
+
+ <h4><a name="wireexamplescallup">Method call with upload stream</a></h4>
+
+ <p>
+ A method call which also involves uploading some data over
+ a stream will result in
+ </p>
+
+ <pre>
+ +--+-----------------------+-----------+
+ C --> |38| 8 | 1 | 3 | 0 | 1 | 0 | .o.oOo.o. | --> S (call)
+ +--+-----------------------+-----------+
+
+ +--+-----------------------+--------+
+ C <-- |32| 8 | 1 | 3 | 1 | 1 | 0 | .o.oOo | <-- S (reply)
+ +--+-----------------------+--------+
+
+ +--+-----------------------+-------------....-------+
+ C --> |38| 8 | 1 | 3 | 3 | 1 | 2 | .o.oOo.o.oOo....o.oOo. | --> S (stream data up)
+ +--+-----------------------+-------------....-------+
+ +--+-----------------------+-------------....-------+
+ C --> |38| 8 | 1 | 3 | 3 | 1 | 2 | .o.oOo.o.oOo....o.oOo. | --> S (stream data up)
+ +--+-----------------------+-------------....-------+
+ +--+-----------------------+-------------....-------+
+ C --> |38| 8 | 1 | 3 | 3 | 1 | 2 | .o.oOo.o.oOo....o.oOo. | --> S (stream data up)
+ +--+-----------------------+-------------....-------+
+ ...
+ +--+-----------------------+-------------....-------+
+ C --> |38| 8 | 1 | 3 | 3 | 1 | 2 | .o.oOo.o.oOo....o.oOo. | --> S (stream data up)
+ +--+-----------------------+-------------....-------+
+ +--+-----------------------+
+ C --> |24| 8 | 1 | 3 | 3 | 1 | 0 | --> S (stream finish)
+ +--+-----------------------+
+ +--+-----------------------+
+ C <-- |24| 8 | 1 | 3 | 3 | 1 | 0 | <-- S (stream finish)
+ +--+-----------------------+
+ </pre>
+
+ <h4><a name="wireexamplescallbi">Method call bidirectional stream</a></h4>
+
+ <p>
+ A method call which also involves a bi-directional stream will
+ result in
+ </p>
+
+ <pre>
+ +--+-----------------------+-----------+
+ C --> |38| 8 | 1 | 3 | 0 | 1 | 0 | .o.oOo.o. | --> S (call)
+ +--+-----------------------+-----------+
+
+ +--+-----------------------+--------+
+ C <-- |32| 8 | 1 | 3 | 1 | 1 | 0 | .o.oOo | <-- S (reply)
+ +--+-----------------------+--------+
+
+ +--+-----------------------+-------------....-------+
+ C --> |38| 8 | 1 | 3 | 3 | 1 | 2 | .o.oOo.o.oOo....o.oOo. | --> S (stream data up)
+ +--+-----------------------+-------------....-------+
+ +--+-----------------------+-------------....-------+
+ C --> |38| 8 | 1 | 3 | 3 | 1 | 2 | .o.oOo.o.oOo....o.oOo. | --> S (stream data up)
+ +--+-----------------------+-------------....-------+
+ +--+-----------------------+-------------....-------+
+ C <-- |38| 8 | 1 | 3 | 3 | 1 | 2 | .o.oOo.o.oOo....o.oOo. | <-- S (stream data down)
+ +--+-----------------------+-------------....-------+
+ +--+-----------------------+-------------....-------+
+ C --> |38| 8 | 1 | 3 | 3 | 1 | 2 | .o.oOo.o.oOo....o.oOo. | --> S (stream data up)
+ +--+-----------------------+-------------....-------+
+ +--+-----------------------+-------------....-------+
+ C --> |38| 8 | 1 | 3 | 3 | 1 | 2 | .o.oOo.o.oOo....o.oOo. | --> S (stream data up)
+ +--+-----------------------+-------------....-------+
+ +--+-----------------------+-------------....-------+
+ C <-- |38| 8 | 1 | 3 | 3 | 1 | 2 | .o.oOo.o.oOo....o.oOo. | <-- S (stream data down)
+ +--+-----------------------+-------------....-------+
+ +--+-----------------------+-------------....-------+
+ C <-- |38| 8 | 1 | 3 | 3 | 1 | 2 | .o.oOo.o.oOo....o.oOo. | <-- S (stream data down)
+ +--+-----------------------+-------------....-------+
+ +--+-----------------------+-------------....-------+
+ C <-- |38| 8 | 1 | 3 | 3 | 1 | 2 | .o.oOo.o.oOo....o.oOo. | <-- S (stream data down)
+ +--+-----------------------+-------------....-------+
+ +--+-----------------------+-------------....-------+
+ C --> |38| 8 | 1 | 3 | 3 | 1 | 2 | .o.oOo.o.oOo....o.oOo. | --> S (stream data up)
+ +--+-----------------------+-------------....-------+
+ ..
+ +--+-----------------------+-------------....-------+
+ C --> |38| 8 | 1 | 3 | 3 | 1 | 2 | .o.oOo.o.oOo....o.oOo. | --> S (stream data up)
+ +--+-----------------------+-------------....-------+
+ +--+-----------------------+
+ C --> |24| 8 | 1 | 3 | 3 | 1 | 0 | --> S (stream finish)
+ +--+-----------------------+
+ +--+-----------------------+
+ C <-- |24| 8 | 1 | 3 | 3 | 1 | 0 | <-- S (stream finish)
+ +--+-----------------------+
+ </pre>
+
+
+ <h4><a name="wireexamplescallmany">Method calls overlapping</a></h4>
+ <pre>
+ +--+-----------------------+-----------+
+ C --> |38| 8 | 1 | 3 | 0 | 1 | 0 | .o.oOo.o. | --> S (call 1)
+ +--+-----------------------+-----------+
+ +--+-----------------------+-----------+
+ C --> |38| 8 | 1 | 3 | 0 | 2 | 0 | .o.oOo.o. | --> S (call 2)
+ +--+-----------------------+-----------+
+ +--+-----------------------+--------+
+ C <-- |32| 8 | 1 | 3 | 1 | 2 | 0 | .o.oOo | <-- S (reply 2)
+ +--+-----------------------+--------+
+ +--+-----------------------+-----------+
+ C --> |38| 8 | 1 | 3 | 0 | 3 | 0 | .o.oOo.o. | --> S (call 3)
+ +--+-----------------------+-----------+
+ +--+-----------------------+--------+
+ C <-- |32| 8 | 1 | 3 | 1 | 3 | 0 | .o.oOo | <-- S (reply 3)
+ +--+-----------------------+--------+
+ +--+-----------------------+-----------+
+ C --> |38| 8 | 1 | 3 | 0 | 4 | 0 | .o.oOo.o. | --> S (call 4)
+ +--+-----------------------+-----------+
+ +--+-----------------------+--------+
+ C <-- |32| 8 | 1 | 3 | 1 | 1 | 0 | .o.oOo | <-- S (reply 1)
+ +--+-----------------------+--------+
+ +--+-----------------------+--------+
+ C <-- |32| 8 | 1 | 3 | 1 | 4 | 0 | .o.oOo | <-- S (reply 4)
+ +--+-----------------------+--------+
+ </pre>
+
+
+ <h2><a name="security">RPC security</a></h2>
+
+ <p>
+ There are various things to consider to ensure an implementation
+ of the RPC protocol can be satisfactorily secured
+ </p>
+
+ <h3><a name="securitytls">Authentication/encryption</a></h3>
+
+ <p>
+ The basic RPC protocol does not define or require any specific
+ authentication/encryption capabilities. A generic solution to
+ providing encryption for the protocol is to run the protocol
+ over a TLS encrypted data stream. x509 certificate checks can
+ be done to form a crude authentication mechanism. It is also
+ possible for an RPC program to negotiate an encryption /
+ authentication capability, such as SASL, which may then also
+ provide per-packet data encryption. Finally the protocol data
+ stream can of course be tunnelled over transports such as SSH.
+ </p>
+
+ <h3><a name="securitylimits">Data limits</a></h3>
+
+ <p>
+ Although the protocol itself defines many arbitrary sized data values in the
+ payloads, to avoid denial of service attack there are a number of size limit
+ checks prior to encoding or decoding data. There is a limit on the maximum
+ size of a single RPC message, limit on the maximum string length, and limits
+ on any other parameter which uses a variable length array. These limits can
+ be raised, subject to agreement between client/server, without otherwise
+ breaking compatibility of the RPC data on the wire.
+ </p>
+
+ <h3><a name="securityvalidate">Data validation</a></h3>
+
+ <p>
+ It is important that all data be fully validated before performing
+ any actions based on the data. When reading an RPC packet, the
+ first four bytes must be read and the max packet size limit validated,
+ before any attempt is made to read the variable length packet data.
+ After a complete packet has been read, the header must be decoded
+ and all 6 fields fully validated, before attempting to dispatch
+ the payload. Once dispatched, the payload can be decoded and passed
+ onto the appropriate API for execution. The RPC code must not take
+ any action based on the payload, since it has no way to validate
+ the semantics of the payload data. It must delegate this to the
+ execution API (eg corresponding libvirt public API).
+ </p>
+
+ <h2><a name="internals">RPC internal APIs</a></h2>
+
+ <p>
+ The generic internal RPC library code lives in the <code>src/rpc/</code>
+ directory of the libvirt source tree. Unless otherwise noted, the
+ objects are all threadsafe. The core object types and their
+ purposes are:
+ </p>
+
+ <h3><a name="apioverview">Overview of RPC objects</a></h3>
+
+ <p>
+ The following is a high level overview of the role of each
+ of the main RPC objects
+ </p>
+
+ <dl>
+ <dt><code>virNetSASLContextPtr</code> (virnetsaslcontext.h)</dt>
+ <dd>The virNetSASLContext APIs maintain SASL state for a network
+ service (server or client). This is primarily used on the server
+ to provide a whitelist of allowed SASL usernames for clients.
+ </dd>
+
+ <dt><code>virNetSASLSessionPtr</code> (virnetsaslcontext.h)</dt>
+ <dd>The virNetSASLSession APIs maintain SASL state for a single
+ network connection (socket). This is used to perform the multi-step
+ SASL handshake and perform encryption/decryption of data once
+ authenticated, via integration with virNetSocket.
+ </dd>
+
+ <dt><code>virNetTLSContextPtr</code> (virnettlscontext.h)</dt>
+ <dd>The virNetTLSContext APIs maintain TLS state for a network
+ service (server or client). This is primarily used on the server
+ to provide a whitelist of allowed x509 distinguished names, as
+ well as diffie-hellman keys. It can also do validation of
+ x509 certificates prior to initiating a connection, in order
+ to improve detection of configuration errors.
+ </dd>
+
+ <dt><code>virNetTLSSessionPtr</code> (virnettlscontext.h)</dt>
+ <dd>The virNetTLSSession APIs maintain TLS state for a single
+ network connection (socket). This is used to perform the multi-step
+ TLS handshake and perform encryption/decryption of data once
+ authenticated, via integration with virNetSocket.
+ </dd>
+
+ <dt><code>virNetSocketPtr</code> (virnetsocket.h)</dt>
+ <dd>The virNetSocket APIs provide a higher level wrapper around
+ the raw BSD sockets and getaddrinfo APIs. They allow for creation
+ of both server and client sockets. Data transports supported are
+ TCP, UNIX, SSH tunnel or external command tunnel. Internally the
+ TCP socket impl uses the getaddrinfo info APIs to ensure correct
+ protocol independant behaviour, thus supporting both IPv4 and IPv6.
+ The socket APIs can be associated with a virNetSASLSessionPtr or
+ virNetTLSSessionPtr object to allow seemless encryption/decryption
+ of all writes and reads. For UNIX sockets it is possible to obtain
+ the remote client user ID and process ID. Integration with the
+ libvirt event loop also allows use of callbacks for notification
+ of various I/O conditions
+ </dd>
+
+ <dt><code>virNetMessagePtr</code> (virnetmessage.h)</dt>
+ <dd>The virNetMessage APIs provide a wrapper around the libxdr
+ API calls, to facilitate processing and creation of RPC
+ packets. There are convenience APIs for encoding/encoding the
+ packet headers, encoding/decoding the payload using an XDR
+ filter, encoding/decoding a raw payload (for streams), and
+ encoding a virErrorPtr object. There is also a means to
+ add to/serve from a linked-list queue of messages.</dd>
+
+ <dt><code>virNetClientPtr</code> (virnetclient.h)</dt>
+ <dd>The virNetClient APIs provide a way to connect to a
+ remote server and run one or more RPC protocols over
+ the connection. Connections can be made over TCP, UNIX
+ sockets, SSH tunnels, or external command tunnels. There
+ is support for both TLS and SASL session encryption.
+ The client also supports management of multiple data streams
+ over each connection. Each client object can be used from
+ multiple threads concurrently, with method calls/replies
+ being interleaved on the wire as required.
+ </dd>
+
+ <dt><code>virNetClientProgramPtr</code> (virnetclientprogram.h)</dt>
+ <dd>The virNetClientProgram APIs are used to register a
+ program+version with the connection. This then enables
+ invocation of method calls, receipt of asynchronous
+ events and use of data streams, within that program+version.
+ When created a set of callbacks must be supplied to take
+ care of dispatching any incoming asynchronous events.
+ </dd>
+
+ <dt><code>virNetClientStreamPtr</code> (virnetclientstream.h)</dt>
+ <dd>The virNetClientStream APIs are used to control transmission and
+ receipt of data over a stream active on a client. Streams provide
+ a low latency, unlimited length, bi-directional raw data exchange
+ mechanism layered over the RPC connection
+ </dd>
+
+ <dt><code>virNetServerPtr</code> (virnetserver.h)</dt>
+ <dd>The virNetServer APIs are used to manage a network server. A
+ server exposed one or more programs, over one or more services.
+ It manages multiple client connections invoking multiple RPC
+ calls in parallel, with dispatch across multiple worker threads.
+ </dd>
+
+ <dt><code>virNetServerMDNSPtr</code> (virnetservermdns.h)</dt>
+ <dd>The virNetServerMDNS APIs are used to advertize a server
+ across the local network, enabling clients to automatically
+ detect the existance of remote services. This is done by
+ interfacing with the Avahi mDNS advertisement service.
+ </dd>
+
+ <dt><code>virNetServerClientPtr</code> (virnetserverclient.h)</dt>
+ <dd>The virNetServerClient APIs are used to manage I/O related
+ to a single client network connection. It handles initial
+ validation and routing of incoming RPC packets, and transmission
+ of outgoing packets.
+ </dd>
+
+ <dt><code>virNetServerProgramPtr</code> (virnetserverprogram.h)</dt>
+ <dd>The virNetServerProgram APIs are used to provide the implementation
+ of a single program/version set. Primarily this includes a set of
+ callbacks used to actually invoke the APIs corresponding to
+ program procedure numbers. It is responsible for all the serialization
+ of payloads to/from XDR.</dd>
+
+ <dt><code>virNetServerServicePtr</code> (virnetserverservice.h)</dt>
+ <dd>The virNetServerService APIs are used to connect the server to
+ one or more network protocols. A single service may involve multiple
+ sockets (ie both IPv4 and IPv6). A service also has an associated
+ authentication policy for incoming clients.
+ </dd>
+ </dl>
+
+ <h3><a name="apiclientdispatch">Client RPC dispatch</a></h3>
+
+ <p>
+ The client RPC code must allow for multiple overlapping RPC method
+ calls to be invoked, transmission & receipt of data for mutliple
+ streams and receipt of asynchronous events. Understandably this
+ involves coordination of multiple threads.
+ </p>
+
+ <p>
+ The core requirement in the client dispatch code is that only
+ one thread is allowed to be performing I/O on the socket at
+ any time. This thread is said to be "holding the buck". When
+ any other thread comes along and needs todo I/O it must place
+ its packets on a queue and delegate processing of them to the
+ thread that has the buck. This thread will send out the method
+ call, and if it sees a reply will pass it back to the waiting
+ thread. If the other thread's reply hasn't arrived, by the time
+ the main thread has got its own reply, then it will transfer
+ responsibility for I/O to the thread that has been waiting the
+ longest. It is said to be "passing the buck" for I/O.
+ </p>
+
+ <p>
+ When no thread is performing any RPC method call, or sending
+ stream data there is still a need to monitor the socket for
+ incoming I/O related to asynchronous events, or stream data
+ receipt. For this task, a watch is registered with the event
+ loop which triggers whenever the socket is readable. This
+ watch is automatically disabled whenever any other thread
+ grabs the buck, and re-enabled when the buck is released.
+ </p>
+
+ <h4><a name="apiclientdispatchex1">Example with buck passing</a></h4>
+
+ <p>
+ In the first example, a second thread issues a API call
+ while the first thread holds the buck. The reply to the
+ first call arrives first, so the buck is passed to the
+ second thread.
+ </p>
+
+ <pre>
+ Thread-1
+ |
+ V
+ Call API1()
+ |
+ V
+ Grab Buck
+ | Thread-2
+ V |
+ Send method1 V
+ | Call API2()
+ V |
+ Wait I/O V
+ |<--------Queue method2
+ V |
+ Send method2 V
+ | Wait for buck
+ V |
+ Wait I/O |
+ | |
+ V |
+ Recv reply1 |
+ | |
+ V |
+ Pass the buck----->|
+ | V
+ V Wait I/O
+ Return API1() |
+ V
+ Recv reply2
+ |
+ V
+ Release the buck
+ |
+ V
+ Return API2()
+ </pre>
+
+ <h4><a name="apiclientdispatchex2">Example without buck passing</a></h4>
+
+ <p>
+ In this second example, a second thread issues an API call
+ which is sent and replied to, before the first thread's
+ API call has completed. The first thread thus notifies
+ the second that its reply is ready, and there is no need
+ to pass the buck
+ </p>
+
+ <pre>
+ Thread-1
+ |
+ V
+ Call API1()
+ |
+ V
+ Grab Buck
+ | Thread-2
+ V |
+ Send method1 V
+ | Call API2()
+ V |
+ Wait I/O V
+ |<--------Queue method2
+ V |
+ Send method2 V
+ | Wait for buck
+ V |
+ Wait I/O |
+ | |
+ V |
+ Recv reply2 |
+ | |
+ V |
+ Notify reply2------>|
+ | V
+ V Return API2()
+ Wait I/O
+ |
+ V
+ Recv reply1
+ |
+ V
+ Release the buck
+ |
+ V
+ Return API1()
+ </pre>
+
+ <h4><a name="apiclientdispatchex3">Example with async events</a></h4>
+
+ <p>
+ In this example, only one thread is present and it has to
+ deal with some async events arriving. The events are actually
+ dispatched to the application from the event loop thread
+ </p>
+
+ <pre>
+ Thread-1
+ |
+ V
+ Call API1()
+ |
+ V
+ Grab Buck
+ |
+ V
+ Send method1
+ |
+ V
+ Wait I/O
+ | Event thread
+ V ...
+ Recv event1 |
+ | V
+ V Wait for timer/fd
+ Queue event1 |
+ | V
+ V Timer fires
+ Wait I/O |
+ | V
+ V Emit event1
+ Recv reply1 |
+ | V
+ V Wait for timer/fd
+ Return API1() |
+ ...
+ </pre>
+
+ <h3><a name="apiserverdispatch">Server RPC dispatch</a></h3>
+
+ <p>
+ The RPC server code must support receipt of incoming RPC requests from
+ multiple client connections, and parallel processing of all RPC
+ requests, even many from a single client. This goal is achieved through
+ a combination of event driven I/O, and multiple processing threads.
+ </p>
+
+ <p>
+ The main libvirt event loop thread is responsible for performing all
+ socket I/O. It will read incoming packets from clients and willl
+ transmit outgoing packets to clients. It will handle the I/O to/from
+ streams associated with client API calls. When doing client I/O it
+ will also take pass the data through any applicable encryption layer
+ (through use of the virNetSocket / virNetTLSSession and virNetSASLSession
+ integration). What is paramount is that the event loop thread never
+ do any task that can take a non-trivial amount of time.
+ </p>
+
+ <p>
+ When reading packets, the event loop will first read the 4 byte length
+ word. This is validated to make sure it does not exceed the maximum
+ permissible packet size, and the client is set to allow receipt of the
+ rest of the packet data. Once a complete packet has been received, the
+ next step is to decode the RPC header. The header is validated to
+ ensure the request is sensible, ie the server should not receive a
+ method reply from a client. If the client has not yet authenticated,
+ a security check is also applied to make sure the procedure is on the
+ whitelist of those allowed prior to auth. If the packet is a method
+ call, it will be placed on a global processing queue. The event loop
+ thread is now done with the packet for the time being.
+ </p>
+
+ <p>
+ The server has a pool of worker threads, which wait for method call
+ packets to be queued. One of them will grab the new method call off
+ the queue for processing. The first step is to decode the payload of
+ the packet to extract the method call arguments. The worker does not
+ attempt todo any semantic validation of the arguments, except to make
+ sure the size of any variable length fields is below defined limits.
+ </p>
+
+ <p>
+ The worker now invokes the libvirt API call that corresponds to the
+ procedure number in the packet header. The worker is thus kept busy
+ until the API call completes. The implemementation of the API call
+ is responsible for doing semantic validation of parameters and any
+ MAC security checks on the objects affected.
+ </p>
+
+ <p>
+ Once the API call has completed, the worker thread will take the
+ return value and output parameters, or error object and encode
+ them into a reply packet. Again it does not attempt todo any
+ semantic validation of output data, aside from variable length
+ field limit checks. The worker thread puts the reply packet onto
+ the transmission queue for the client. The worker is now finished
+ and goes back to wait for another incoming method call.
+ </p>
+
+ <p>
+ The main event loop is back in charge and when the client socket
+ becomes writable, it will start sending the method reply packet
+ back to the client.
+ </p>
+
+ <p>
+ At any time the libvirt connection object can emit asynchronous
+ events. These are handled by callbacks in the main event thread.
+ The callback will simply encode the event parameters into a new
+ data packet and place the packet on the client transmission
+ queue.
+ </p>
+
+ <p>
+ Incoming and outgoing stream packets are also directly handled
+ by the main event thread. When an incoming stream packet is
+ received, instead of placing it in the global dispatch queue
+ for the worker threads, it is sidetracked into a per-stream
+ processing queue. When the stream becomes writable, queued
+ incoming stream packets will be processed, passing their data
+ payload onto the stream. Conversely when the stream becomes
+ readable, chunks of data will be read from it, encoded into
+ new outgoing packets, and placed on the client's transmit
+ queue
+ </p>
+
+ <h4><a name="apiserverdispatchex1">Example with overlapping methods</a></h4>
+
+ <p>
+ This example illustrates processing of two incoming methods with
+ overlapping execution
+ </p>
+
+ <pre>
+ Event thread Worker 1 Worker 2
+ | | |
+ V V V
+ Wait I/O Wait Job Wait Job
+ | | |
+ V | |
+ Recv method1 | |
+ | | |
+ V | |
+ Queue method1 V |
+ | Serve method1 |
+ V | |
+ Wait I/O V |
+ | Call API1() |
+ V | |
+ Recv method2 | |
+ | | |
+ V | |
+ Queue method2 | V
+ | | Serve method2
+ V V |
+ Wait I/O Return API1() V
+ | | Call API2()
+ | V |
+ V Queue reply1 |
+ Send reply1 | |
+ | V V
+ V Wait Job Return API2()
+ Wait I/O | |
+ | ... V
+ V Queue reply2
+ Send reply2 |
+ | V
+ V Wait Job
+ Wait I/O |
+ | ...
+ ...
+ </pre>
+
+ <h4><a name="apiserverdispatchex2">Example with stream data</a></h4>
+
+ <p>
+ This example illustrates processing of stream data
+ </p>
+
+ <pre>
+ Event thread
+ |
+ V
+ Wait I/O
+ |
+ V
+ Recv stream1
+ |
+ V
+ Queue stream1
+ |
+ V
+ Wait I/O
+ |
+ V
+ Recv stream2
+ |
+ V
+ Queue stream2
+ |
+ V
+ Wait I/O
+ |
+ V
+ Write stream1
+ |
+ V
+ Write stream2
+ |
+ V
+ Wait I/O
+ |
+ ...
+ </pre>
+
+ </body>
+</html>
diff --git a/docs/remote.html.in b/docs/remote.html.in
index b554950..6a8e830 100644
--- a/docs/remote.html.in
+++ b/docs/remote.html.in
@@ -53,9 +53,6 @@ machines through authenticated and encrypted connections.
<li>
<a href="#Remote_limitations">Limitations</a>
</li>
- <li>
- <a href="#Remote_implementation_notes">Implementation notes</a>
- </li>
</ul>
<h3>
<a name="Remote_basic_usage">Basic usage</a>
@@ -880,47 +877,5 @@ just read-write/read-only as at present.
<p>
Please come and discuss these issues and more on <a href="https://www.redhat.com/mailman/listinfo/libvir-list" title="libvir-list mailing list">the mailing list</a>.
</p>
- <h3>
- <a name="Remote_implementation_notes">Implementation notes</a>
- </h3>
- <p>
-The current implementation uses <a href="http://en.wikipedia.org/wiki/External_Data_Representation" title="External Data Representation">XDR</a>-encoded packets with a
-simple remote procedure call implementation which also supports
-asynchronous messaging and asynchronous and out-of-order replies,
-although these latter features are not used at the moment.
-</p>
- <p>
-The implementation should be considered <b>strictly internal</b> to
-libvirt and <b>subject to change at any time without notice</b>. If
-you wish to talk to libvirtd, link to libvirt. If there is a problem
-that means you think you need to use the protocol directly, please
-first discuss this on <a href="https://www.redhat.com/mailman/listinfo/libvir-list" title="libvir-list mailing list">the mailing list</a>.
-</p>
- <p>
-The messaging protocol is described in
-<code>qemud/remote_protocol.x</code>.
-</p>
- <p>
-Authentication and encryption (for TLS) is done using <a href="http://www.gnu.org/software/gnutls/" title="GnuTLS project page">GnuTLS</a> and the RPC protocol is unaware of this layer.
-</p>
- <p>
-Protocol messages are sent using a simple 32 bit length word (encoded
-XDR int) followed by the message header (XDR
-<code>remote_message_header</code>) followed by the message body. The
-length count includes the length word itself, and is measured in
-bytes. Maximum message size is <code>REMOTE_MESSAGE_MAX</code> and to
-avoid denial of services attacks on the XDR decoders strings are
-individually limited to <code>REMOTE_STRING_MAX</code> bytes. In the
-TLS case, messages may be split over TLS records, but a TLS record
-cannot contain parts of more than one message. In the common RPC case
-a single <code>REMOTE_CALL</code> message is sent from client to
-server, and the server then replies synchronously with a single
-<code>REMOTE_REPLY</code> message, but other forms of messaging are
-also possible.
-</p>
- <p>
-The protocol contains support for multiple program types and protocol
-versioning, modelled after SunRPC.
-</p>
</body>
</html>
diff --git a/docs/sitemap.html.in b/docs/sitemap.html.in
index 897ee94..f50a8d2 100644
--- a/docs/sitemap.html.in
+++ b/docs/sitemap.html.in
@@ -289,6 +289,10 @@
<span>Spawning commands from libvirt driver code</span>
</li>
<li>
+ <a href="internals/rpc.html">RPC protocol & APIs</a>
+ <span>RPC protocol information and API / dispatch guide</span>
+ </li>
+ <li>
<a href="internals/locking.html">Lock managers</a>
<span>Use lock managers to protect disk content</span>
</li>
--
1.7.6
13 years, 8 months
[libvirt] [PATCH v2] Implement DNS SRV record into the bridge driver
by Michal Novotny
Hi,
this is the second version of my patch to the bridge driver and
libvirt XML file to include support for the SRV records in the DNS.
The syntax is based on DNSMasq man page and tests for both xml2xml
and xml2argv were added as well.
Differences between v1 and v2:
- A minor rewrite of integer parsing functionality
- Extend tests to test with both minimal and full set of attributes
- Check for service name length implemented
Signed-off-by: Michal Novotny <minovotn(a)redhat.com>
---
docs/formatnetwork.html.in | 12 ++
docs/schemas/network.rng | 26 ++++
src/conf/network_conf.c | 130 +++++++++++++++++++-
src/conf/network_conf.h | 16 +++
src/network/bridge_driver.c | 43 +++++++
.../nat-network-dns-srv-record-minimal.argv | 1 +
.../nat-network-dns-srv-record-minimal.xml | 26 ++++
.../nat-network-dns-srv-record.argv | 1 +
.../nat-network-dns-srv-record.xml | 26 ++++
tests/networkxml2argvtest.c | 2 +
.../nat-network-dns-srv-record-minimal.xml | 26 ++++
.../nat-network-dns-srv-record.xml | 26 ++++
.../nat-network-dns-srv-record-minimal.xml | 26 ++++
.../nat-network-dns-srv-record.xml | 26 ++++
tests/networkxml2xmltest.c | 2 +
16 files changed, 391 insertions(+), 2 deletions(-)
create mode 100644 tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv
create mode 100644 tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.xml
create mode 100644 tests/networkxml2argvdata/nat-network-dns-srv-record.argv
create mode 100644 tests/networkxml2argvdata/nat-network-dns-srv-record.xml
create mode 100644 tests/networkxml2xmlin/nat-network-dns-srv-record-minimal.xml
create mode 100644 tests/networkxml2xmlin/nat-network-dns-srv-record.xml
create mode 100644 tests/networkxml2xmlout/nat-network-dns-srv-record-minimal.xml
create mode 100644 tests/networkxml2xmlout/nat-network-dns-srv-record.xml
diff --git a/docs/formatnetwork.html.in b/docs/formatnetwork.html.in
index 99031d0..51b1581 100644
--- a/docs/formatnetwork.html.in
+++ b/docs/formatnetwork.html.in
@@ -342,6 +342,7 @@
<mac address='00:16:3E:5D:C7:9E'/>
<dns>
<txt name="example" value="example value" />
+ <srv service="name" protocol="tcp" domain="test-domain-name" target="." port="1024" priority="10" weight="10"/>
</dns>
<ip address="192.168.122.1" netmask="255.255.255.0">
<dhcp>
@@ -390,6 +391,17 @@
<span class="since">Since 0.9.3</span>
</dd>
</dl>
+ <dl>
+ <dt><code>srv</code></dt>
+ <dd>The <code>dns</code> element can have also 0 or more <code>srv</code>
+ record elements. Each <code>srv</code> record element defines a DNS SRV record
+ and has 2 mandatory and 5 optional attributes. The mandatory attributes
+ are service name and protocol (tcp, udp) and the optional attributes are
+ target, port, priority, weight and domain as defined in DNS server SRV
+ RFC (RFC 2782).
+ <span class="since">Since 0.9.5</span>
+ </dd>
+ </dl>
</dd>
<dt><code>ip</code></dt>
<dd>The <code>address</code> attribute defines an IPv4 address in
diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng
index 1c44471..dae2799 100644
--- a/docs/schemas/network.rng
+++ b/docs/schemas/network.rng
@@ -138,6 +138,19 @@
</element>
</zeroOrMore>
<zeroOrMore>
+ <element name="srv">
+ <attribute name="service"><text/></attribute>
+ <attribute name="protocol"><ref name="protocol"/></attribute>
+ <optional>
+ <attribute name="domain"><ref name="dnsName"/></attribute>
+ <attribute name="target"><text/></attribute>
+ <attribute name="port"><ref name="unsignedShort"/></attribute>
+ <attribute name="priority"><ref name="unsignedShort"/></attribute>
+ <attribute name="weight"><ref name="unsignedShort"/></attribute>
+ </optional>
+ </element>
+ </zeroOrMore>
+ <zeroOrMore>
<element name="host">
<attribute name="ip"><ref name="ipv4Addr"/></attribute>
<oneOrMore>
@@ -206,6 +219,19 @@
</element>
</define>
+ <define name='unsignedShort'>
+ <data type='integer'>
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">65535</param>
+ </data>
+ </define>
+
+ <define name='protocol'>
+ <data type='string'>
+ <param name='pattern'>(tcp)|(udp)</param>
+ </data>
+ </define>
+
<define name='addr-family'>
<data type='string'>
<param name="pattern">(ipv4)|(ipv6)</param>
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index b11c482..517c4d6 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -137,6 +137,15 @@ static void virNetworkDNSDefFree(virNetworkDNSDefPtr def)
}
VIR_FREE(def->hosts);
}
+ if (def->nsrvrecords) {
+ while (def->nsrvrecords--) {
+ VIR_FREE(def->srvrecords[def->nsrvrecords].domain);
+ VIR_FREE(def->srvrecords[def->nsrvrecords].service);
+ VIR_FREE(def->srvrecords[def->nsrvrecords].protocol);
+ VIR_FREE(def->srvrecords[def->nsrvrecords].target);
+ }
+ VIR_FREE(def->srvrecords);
+ }
VIR_FREE(def);
}
}
@@ -552,8 +561,99 @@ error:
}
static int
+virNetworkDNSSrvDefParseXML(virNetworkDNSDefPtr def,
+ xmlNodePtr cur,
+ xmlXPathContextPtr ctxt)
+{
+ char *domain;
+ char *service;
+ char *protocol;
+ char *target;
+ int port;
+ int priority;
+ int weight;
+ int ret = 0;
+ char xpath[1024] = { 0 };
+
+ if (!(service = virXMLPropString(cur, "service"))) {
+ virNetworkReportError(VIR_ERR_XML_DETAIL,
+ "%s", _("Missing required service attribute in dns srv record"));
+ goto error;
+ }
+
+ if (strlen(service) > DNS_RECORD_LENGTH_SRV) {
+ virNetworkReportError(VIR_ERR_XML_DETAIL,
+ "%s", _("Service name is too long, limit is %d bytes"), DNS_RECORD_LENGTH_SRV);
+ goto error;
+ }
+
+ if (!(protocol = virXMLPropString(cur, "protocol"))) {
+ virNetworkReportError(VIR_ERR_XML_DETAIL,
+ _("Missing required protocol attribute in dns srv record '%s'"), service);
+ goto error;
+ }
+
+ /* Check whether protocol value is the supported one */
+ if (STRNEQ(protocol, "tcp") && (STRNEQ(protocol, "udp"))) {
+ virNetworkReportError(VIR_ERR_XML_DETAIL,
+ _("Invalid protocol attribute value '%s'"), protocol);
+ goto error;
+ }
+
+ if (VIR_REALLOC_N(def->srvrecords, def->nsrvrecords + 1) < 0) {
+ virReportOOMError();
+ goto error;
+ }
+
+ def->srvrecords[def->nsrvrecords].service = service;
+ def->srvrecords[def->nsrvrecords].protocol = protocol;
+ def->srvrecords[def->nsrvrecords].domain = NULL;
+ def->srvrecords[def->nsrvrecords].target = NULL;
+ def->srvrecords[def->nsrvrecords].port = 0;
+ def->srvrecords[def->nsrvrecords].priority = 0;
+ def->srvrecords[def->nsrvrecords].weight = 0;
+
+ /* Following attributes are optional but we had to make sure their NULL above */
+ if ((target = virXMLPropString(cur, "target")) && (domain = virXMLPropString(cur, "domain"))) {
+ snprintf(xpath, sizeof(xpath), "string(//network/dns/srv[@service='%s']/@port)", service);
+ if (virXPathInt(xpath, ctxt, &port))
+ def->srvrecords[def->nsrvrecords].port = port;
+
+ snprintf(xpath, sizeof(xpath), "string(//network/dns/srv[@service='%s']/@priority)", service);
+ if (virXPathInt(xpath, ctxt, &priority))
+ def->srvrecords[def->nsrvrecords].priority = priority;
+
+ snprintf(xpath, sizeof(xpath), "string(//network/dns/srv[@service='%s']/@weight)", service);
+ if (virXPathInt(xpath, ctxt, &weight))
+ def->srvrecords[def->nsrvrecords].weight = weight;
+
+ def->srvrecords[def->nsrvrecords].domain = domain;
+ def->srvrecords[def->nsrvrecords].target = target;
+ def->srvrecords[def->nsrvrecords].port = port;
+ def->srvrecords[def->nsrvrecords].priority = priority;
+ def->srvrecords[def->nsrvrecords].weight = weight;
+ }
+
+ def->nsrvrecords++;
+
+ goto cleanup;
+
+error:
+ VIR_FREE(domain);
+ VIR_FREE(service);
+ VIR_FREE(protocol);
+ VIR_FREE(target);
+
+ ret = 1;
+
+cleanup:
+ return ret;
+}
+
+static int
virNetworkDNSDefParseXML(virNetworkDNSDefPtr *dnsdef,
- xmlNodePtr node)
+ xmlNodePtr node,
+ xmlXPathContextPtr ctxt)
{
xmlNodePtr cur;
int ret = -1;
@@ -598,6 +698,11 @@ virNetworkDNSDefParseXML(virNetworkDNSDefPtr *dnsdef,
name = NULL;
value = NULL;
} else if (cur->type == XML_ELEMENT_NODE &&
+ xmlStrEqual(cur->name, BAD_CAST "srv")) {
+ ret = virNetworkDNSSrvDefParseXML(def, cur, ctxt);
+ if (ret < 0)
+ goto error;
+ } else if (cur->type == XML_ELEMENT_NODE &&
xmlStrEqual(cur->name, BAD_CAST "host")) {
ret = virNetworkDNSHostsDefParseXML(def, cur);
if (ret < 0)
@@ -888,7 +993,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
dnsNode = virXPathNode("./dns", ctxt);
if (dnsNode != NULL) {
- if (virNetworkDNSDefParseXML(&def->dns, dnsNode) < 0)
+ if (virNetworkDNSDefParseXML(&def->dns, dnsNode, ctxt) < 0)
goto error;
}
@@ -1147,6 +1252,27 @@ virNetworkDNSDefFormat(virBufferPtr buf,
def->txtrecords[i].value);
}
+ for (i = 0 ; i < def->nsrvrecords ; i++) {
+ if (def->srvrecords[i].service && def->srvrecords[i].protocol) {
+ virBufferAsprintf(buf, " <srv service='%s' protocol='%s' ",
+ def->srvrecords[i].service,
+ def->srvrecords[i].protocol);
+
+ if (def->srvrecords[i].domain)
+ virBufferAsprintf(buf, "domain='%s' ", def->srvrecords[i].domain);
+ if (def->srvrecords[i].target)
+ virBufferAsprintf(buf, "target='%s' ", def->srvrecords[i].target);
+ if (def->srvrecords[i].port)
+ virBufferAsprintf(buf, "port='%d' ", def->srvrecords[i].port);
+ if (def->srvrecords[i].priority)
+ virBufferAsprintf(buf, "priority='%d' ", def->srvrecords[i].priority);
+ if (def->srvrecords[i].weight)
+ virBufferAsprintf(buf, "weight='%d' ", def->srvrecords[i].weight);
+
+ virBufferAsprintf(buf, "/>\n");
+ }
+ }
+
if (def->nhosts) {
int ii, j;
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index 869085e..5f05a3a 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -24,6 +24,8 @@
#ifndef __NETWORK_CONF_H__
# define __NETWORK_CONF_H__
+#define DNS_RECORD_LENGTH_SRV (512 - 30) /* Limit minus overhead as mentioned in RFC-2782 */
+
# include <libxml/parser.h>
# include <libxml/tree.h>
# include <libxml/xpath.h>
@@ -67,6 +69,18 @@ struct _virNetworkDNSTxtRecordsDef {
char *value;
};
+typedef struct _virNetworkDNSSrvRecordsDef virNetworkDNSSrvRecordsDef;
+typedef virNetworkDNSSrvRecordsDef *virNetworkDNSSrvRecordsDefPtr;
+struct _virNetworkDNSSrvRecordsDef {
+ char *domain;
+ char *service;
+ char *protocol;
+ char *target;
+ int port;
+ int priority;
+ int weight;
+};
+
struct _virNetworkDNSHostsDef {
virSocketAddr ip;
int nnames;
@@ -80,6 +94,8 @@ struct _virNetworkDNSDef {
virNetworkDNSTxtRecordsDefPtr txtrecords;
unsigned int nhosts;
virNetworkDNSHostsDefPtr hosts;
+ unsigned int nsrvrecords;
+ virNetworkDNSSrvRecordsDefPtr srvrecords;
};
typedef struct _virNetworkDNSDef *virNetworkDNSDefPtr;
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index c90db63..f4d952f 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -559,6 +559,49 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
virCommandAddArgPair(cmd, "--txt-record", record);
VIR_FREE(record);
}
+
+ for (i = 0; i < dns->nsrvrecords; i++) {
+ char *record = NULL;
+ char *recordPort = NULL;
+ char *recordPriority = NULL;
+ char *recordWeight = NULL;
+
+ if (dns->srvrecords[i].service && dns->srvrecords[i].protocol) {
+ if (dns->srvrecords[i].port) {
+ if (virAsprintf(&recordPort, "%d", dns->srvrecords[i].port) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ }
+ if (dns->srvrecords[i].priority) {
+ if (virAsprintf(&recordPriority, "%d", dns->srvrecords[i].priority) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ }
+ if (dns->srvrecords[i].weight) {
+ if (virAsprintf(&recordWeight, "%d", dns->srvrecords[i].weight) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ }
+
+ if (virAsprintf(&record, "%s.%s.%s,%s,%s,%s,%s",
+ dns->srvrecords[i].service,
+ dns->srvrecords[i].protocol,
+ dns->srvrecords[i].domain ? dns->srvrecords[i].domain : "",
+ dns->srvrecords[i].target ? dns->srvrecords[i].target : "",
+ recordPort ? recordPort : "",
+ recordPriority ? recordPriority : "",
+ recordWeight ? recordWeight : "") < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ virCommandAddArgPair(cmd, "--srv-host", record);
+ VIR_FREE(record);
+ }
+ }
}
/*
diff --git a/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv
new file mode 100644
index 0000000..174f751
--- /dev/null
+++ b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv
@@ -0,0 +1 @@
+/usr/sbin/dnsmasq --strict-order --bind-interfaces --conf-file= --except-interface lo --srv-host=name.tcp.,,,, --listen-address 192.168.122.1 --listen-address 192.168.123.1 --listen-address 2001:db8:ac10:fe01::1 --listen-address 2001:db8:ac10:fd01::1 --listen-address 10.24.10.1 --dhcp-range 192.168.122.2,192.168.122.254 --dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases --dhcp-lease-max=253 --dhcp-no-override --dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile
\ No newline at end of file
diff --git a/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.xml b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.xml
new file mode 100644
index 0000000..e9b7680
--- /dev/null
+++ b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.xml
@@ -0,0 +1,26 @@
+<network>
+ <name>default</name>
+ <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+ <forward dev='eth1' mode='nat'>
+ <interface dev='eth1'/>
+ </forward>
+ <bridge name='virbr0' stp='on' delay='0' />
+ <dns>
+ <srv service='name' protocol='tcp' />
+ </dns>
+ <ip address='192.168.122.1' netmask='255.255.255.0'>
+ <dhcp>
+ <range start='192.168.122.2' end='192.168.122.254' />
+ <host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10' />
+ <host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11' />
+ </dhcp>
+ </ip>
+ <ip family='ipv4' address='192.168.123.1' netmask='255.255.255.0'>
+ </ip>
+ <ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'>
+ </ip>
+ <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
+ </ip>
+ <ip family='ipv4' address='10.24.10.1'>
+ </ip>
+</network>
diff --git a/tests/networkxml2argvdata/nat-network-dns-srv-record.argv b/tests/networkxml2argvdata/nat-network-dns-srv-record.argv
new file mode 100644
index 0000000..2ea9809
--- /dev/null
+++ b/tests/networkxml2argvdata/nat-network-dns-srv-record.argv
@@ -0,0 +1 @@
+/usr/sbin/dnsmasq --strict-order --bind-interfaces --conf-file= --except-interface lo --srv-host=name.tcp.test-domain-name,.,1024,10,10 --listen-address 192.168.122.1 --listen-address 192.168.123.1 --listen-address 2001:db8:ac10:fe01::1 --listen-address 2001:db8:ac10:fd01::1 --listen-address 10.24.10.1 --dhcp-range 192.168.122.2,192.168.122.254 --dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases --dhcp-lease-max=253 --dhcp-no-override --dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile
\ No newline at end of file
diff --git a/tests/networkxml2argvdata/nat-network-dns-srv-record.xml b/tests/networkxml2argvdata/nat-network-dns-srv-record.xml
new file mode 100644
index 0000000..4be85b5
--- /dev/null
+++ b/tests/networkxml2argvdata/nat-network-dns-srv-record.xml
@@ -0,0 +1,26 @@
+<network>
+ <name>default</name>
+ <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+ <forward dev='eth1' mode='nat'>
+ <interface dev='eth1'/>
+ </forward>
+ <bridge name='virbr0' stp='on' delay='0' />
+ <dns>
+ <srv service='name' protocol='tcp' domain='test-domain-name' target='.' port='1024' priority='10' weight='10' />
+ </dns>
+ <ip address='192.168.122.1' netmask='255.255.255.0'>
+ <dhcp>
+ <range start='192.168.122.2' end='192.168.122.254' />
+ <host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10' />
+ <host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11' />
+ </dhcp>
+ </ip>
+ <ip family='ipv4' address='192.168.123.1' netmask='255.255.255.0'>
+ </ip>
+ <ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'>
+ </ip>
+ <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
+ </ip>
+ <ip family='ipv4' address='10.24.10.1'>
+ </ip>
+</network>
diff --git a/tests/networkxml2argvtest.c b/tests/networkxml2argvtest.c
index 4a11d6f..2dd9b7f 100644
--- a/tests/networkxml2argvtest.c
+++ b/tests/networkxml2argvtest.c
@@ -120,6 +120,8 @@ mymain(void)
DO_TEST("netboot-network");
DO_TEST("netboot-proxy-network");
DO_TEST("nat-network-dns-txt-record");
+ DO_TEST("nat-network-dns-srv-record");
+ DO_TEST("nat-network-dns-srv-record-minimal");
DO_TEST("nat-network-dns-hosts");
return (ret==0 ? EXIT_SUCCESS : EXIT_FAILURE);
diff --git a/tests/networkxml2xmlin/nat-network-dns-srv-record-minimal.xml b/tests/networkxml2xmlin/nat-network-dns-srv-record-minimal.xml
new file mode 100644
index 0000000..e9b7680
--- /dev/null
+++ b/tests/networkxml2xmlin/nat-network-dns-srv-record-minimal.xml
@@ -0,0 +1,26 @@
+<network>
+ <name>default</name>
+ <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+ <forward dev='eth1' mode='nat'>
+ <interface dev='eth1'/>
+ </forward>
+ <bridge name='virbr0' stp='on' delay='0' />
+ <dns>
+ <srv service='name' protocol='tcp' />
+ </dns>
+ <ip address='192.168.122.1' netmask='255.255.255.0'>
+ <dhcp>
+ <range start='192.168.122.2' end='192.168.122.254' />
+ <host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10' />
+ <host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11' />
+ </dhcp>
+ </ip>
+ <ip family='ipv4' address='192.168.123.1' netmask='255.255.255.0'>
+ </ip>
+ <ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'>
+ </ip>
+ <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
+ </ip>
+ <ip family='ipv4' address='10.24.10.1'>
+ </ip>
+</network>
diff --git a/tests/networkxml2xmlin/nat-network-dns-srv-record.xml b/tests/networkxml2xmlin/nat-network-dns-srv-record.xml
new file mode 100644
index 0000000..4be85b5
--- /dev/null
+++ b/tests/networkxml2xmlin/nat-network-dns-srv-record.xml
@@ -0,0 +1,26 @@
+<network>
+ <name>default</name>
+ <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+ <forward dev='eth1' mode='nat'>
+ <interface dev='eth1'/>
+ </forward>
+ <bridge name='virbr0' stp='on' delay='0' />
+ <dns>
+ <srv service='name' protocol='tcp' domain='test-domain-name' target='.' port='1024' priority='10' weight='10' />
+ </dns>
+ <ip address='192.168.122.1' netmask='255.255.255.0'>
+ <dhcp>
+ <range start='192.168.122.2' end='192.168.122.254' />
+ <host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10' />
+ <host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11' />
+ </dhcp>
+ </ip>
+ <ip family='ipv4' address='192.168.123.1' netmask='255.255.255.0'>
+ </ip>
+ <ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'>
+ </ip>
+ <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
+ </ip>
+ <ip family='ipv4' address='10.24.10.1'>
+ </ip>
+</network>
diff --git a/tests/networkxml2xmlout/nat-network-dns-srv-record-minimal.xml b/tests/networkxml2xmlout/nat-network-dns-srv-record-minimal.xml
new file mode 100644
index 0000000..e9b7680
--- /dev/null
+++ b/tests/networkxml2xmlout/nat-network-dns-srv-record-minimal.xml
@@ -0,0 +1,26 @@
+<network>
+ <name>default</name>
+ <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+ <forward dev='eth1' mode='nat'>
+ <interface dev='eth1'/>
+ </forward>
+ <bridge name='virbr0' stp='on' delay='0' />
+ <dns>
+ <srv service='name' protocol='tcp' />
+ </dns>
+ <ip address='192.168.122.1' netmask='255.255.255.0'>
+ <dhcp>
+ <range start='192.168.122.2' end='192.168.122.254' />
+ <host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10' />
+ <host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11' />
+ </dhcp>
+ </ip>
+ <ip family='ipv4' address='192.168.123.1' netmask='255.255.255.0'>
+ </ip>
+ <ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'>
+ </ip>
+ <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
+ </ip>
+ <ip family='ipv4' address='10.24.10.1'>
+ </ip>
+</network>
diff --git a/tests/networkxml2xmlout/nat-network-dns-srv-record.xml b/tests/networkxml2xmlout/nat-network-dns-srv-record.xml
new file mode 100644
index 0000000..4be85b5
--- /dev/null
+++ b/tests/networkxml2xmlout/nat-network-dns-srv-record.xml
@@ -0,0 +1,26 @@
+<network>
+ <name>default</name>
+ <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+ <forward dev='eth1' mode='nat'>
+ <interface dev='eth1'/>
+ </forward>
+ <bridge name='virbr0' stp='on' delay='0' />
+ <dns>
+ <srv service='name' protocol='tcp' domain='test-domain-name' target='.' port='1024' priority='10' weight='10' />
+ </dns>
+ <ip address='192.168.122.1' netmask='255.255.255.0'>
+ <dhcp>
+ <range start='192.168.122.2' end='192.168.122.254' />
+ <host mac='00:16:3e:77:e2:ed' name='a.example.com' ip='192.168.122.10' />
+ <host mac='00:16:3e:3e:a9:1a' name='b.example.com' ip='192.168.122.11' />
+ </dhcp>
+ </ip>
+ <ip family='ipv4' address='192.168.123.1' netmask='255.255.255.0'>
+ </ip>
+ <ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'>
+ </ip>
+ <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
+ </ip>
+ <ip family='ipv4' address='10.24.10.1'>
+ </ip>
+</network>
diff --git a/tests/networkxml2xmltest.c b/tests/networkxml2xmltest.c
index 5cdbedb..b9f6546 100644
--- a/tests/networkxml2xmltest.c
+++ b/tests/networkxml2xmltest.c
@@ -87,6 +87,8 @@ mymain(void)
DO_TEST("netboot-network");
DO_TEST("netboot-proxy-network");
DO_TEST("nat-network-dns-txt-record");
+ DO_TEST("nat-network-dns-srv-record");
+ DO_TEST("nat-network-dns-srv-record-minimal");
DO_TEST("nat-network-dns-hosts");
DO_TEST("8021Qbh-net");
DO_TEST("direct-net");
--
1.7.4.4
13 years, 8 months
[libvirt] [PATCH v2.5] daemon: Add early libvirtd start verbose errors.
by Peter Krempa
Early errors during start of libvirtd didn't have
an error reporting mechanism and caused libvirtd
to exit silently (only the return value indicated
an error).
Libvirt logging is initialized very early using
enviroment variables and the internal error reporting
API is used to report early errors.
v2 changes:
- print errors unconditionaly before logging starts
- fix message to US spelling
v2.5 changes:
- initialize logging from enviroment
- log all early errors using VIR_ERROR
fixes: https://bugzilla.redhat.com/show_bug.cgi?id=728654
---
daemon/libvirtd.c | 41 ++++++++++++++++++++++++++++++-----------
1 files changed, 30 insertions(+), 11 deletions(-)
diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c
index 53f1002..aa69f80 100644
--- a/daemon/libvirtd.c
+++ b/daemon/libvirtd.c
@@ -1285,11 +1285,14 @@ int main(int argc, char **argv) {
{0, 0, 0, 0}
};
+ /* initialize early logging */
+ virLogSetFromEnv();
+
if (setlocale (LC_ALL, "") == NULL ||
bindtextdomain (PACKAGE, LOCALEDIR) == NULL ||
textdomain(PACKAGE) == NULL ||
virInitialize() < 0) {
- fprintf(stderr, _("%s: initialization failed\n"), argv[0]);
+ VIR_ERROR(_("%s: initialization failed"), argv[0]);
exit(EXIT_FAILURE);
}
@@ -1328,14 +1331,18 @@ int main(int argc, char **argv) {
case 'p':
VIR_FREE(pid_file);
- if (!(pid_file = strdup(optarg)))
+ if (!(pid_file = strdup(optarg))) {
+ VIR_ERROR(_("Can't allocate memory"));
exit(EXIT_FAILURE);
+ }
break;
case 'f':
VIR_FREE(remote_config_file);
- if (!(remote_config_file = strdup(optarg)))
+ if (!(remote_config_file = strdup(optarg))) {
+ VIR_ERROR(_("Can't allocate memory"));
exit(EXIT_FAILURE);
+ }
break;
case OPT_VERSION:
@@ -1347,27 +1354,33 @@ int main(int argc, char **argv) {
return 2;
default:
- fprintf (stderr, _("%s: internal error: unknown flag: %c\n"),
- argv[0], c);
+ VIR_ERROR(_("%s: internal error: unknown flag: %c"),
+ argv[0], c);
exit (EXIT_FAILURE);
}
}
- if (!(config = daemonConfigNew(privileged)))
+ if (!(config = daemonConfigNew(privileged))) {
+ VIR_ERROR(_("Can't create initial configuration"));
exit(EXIT_FAILURE);
+ }
/* No explicit config, so try and find a default one */
if (remote_config_file == NULL) {
implicit_conf = true;
if (daemonConfigFilePath(privileged,
- &remote_config_file) < 0)
+ &remote_config_file) < 0) {
+ VIR_ERROR(_("Can't determine config path"));
exit(EXIT_FAILURE);
+ }
}
/* Read the config file if it exists*/
if (remote_config_file &&
- daemonConfigLoad(config, remote_config_file, implicit_conf) < 0)
+ daemonConfigLoad(config, remote_config_file, implicit_conf) < 0) {
+ VIR_ERROR(_("Can't load config file '%s'"), remote_config_file);
exit(EXIT_FAILURE);
+ }
if (config->host_uuid &&
virSetHostUUIDStr(config->host_uuid) < 0) {
@@ -1375,19 +1388,25 @@ int main(int argc, char **argv) {
exit(EXIT_FAILURE);
}
- if (daemonSetupLogging(config, privileged, verbose, godaemon) < 0)
+ if (daemonSetupLogging(config, privileged, verbose, godaemon) < 0) {
+ VIR_ERROR(_("Can't initialize logging"));
exit(EXIT_FAILURE);
+ }
if (!pid_file && privileged &&
daemonPidFilePath(privileged,
- &pid_file) < 0)
+ &pid_file) < 0) {
+ VIR_ERROR(_("Can't determine pid file path."));
exit(EXIT_FAILURE);
+ }
if (daemonUnixSocketPaths(config,
privileged,
&sock_file,
- &sock_file_ro) < 0)
+ &sock_file_ro) < 0) {
+ VIR_ERROR(_("Can't determine socket paths"));
exit(EXIT_FAILURE);
+ }
if (godaemon) {
char ebuf[1024];
--
1.7.6
13 years, 8 months
[libvirt] [PATCH v3] Add support for fd: protocol
by Corey Bryant
sVirt provides SELinux MAC isolation for Qemu guest processes and their
corresponding resources (image files). sVirt provides this support
by labeling guests and resources with security labels that are stored
in file system extended attributes. Some file systems, such as NFS, do
not support the extended attribute security namespace, which is needed
for image file isolation when using the sVirt SELinux security driver
in libvirt.
The proposed solution entails a combination of Qemu, libvirt, and
SELinux patches that work together to isolate multiple guests' images
when they're stored in the same NFS mount. This results in an
environment where sVirt isolation and NFS image file isolation can both
be provided.
This patch contains the Qemu code to support this solution. I would
like to solicit input from the libvirt community prior to starting
the libvirt patch.
Currently, Qemu opens an image file in addition to performing the
necessary read and write operations. The proposed solution will move
the open out of Qemu and into libvirt. Once libvirt opens an image
file for the guest, it will pass the file descriptor to Qemu via a
new fd: protocol.
If the image file resides in an NFS mount, the following SELinux policy
changes will provide image isolation:
- A new SELinux boolean is created (e.g. virt_read_write_nfs) to
allow Qemu (svirt_t) to only have SELinux read and write
permissions on nfs_t files
- Qemu (svirt_t) also gets SELinux use permissions on libvirt
(virtd_t) file descriptors
Following is a sample invocation of Qemu using the fd: protocol on
the command line:
qemu -drive file=fd:4,format=qcow2
The fd: protocol is also supported by the drive_add monitor command.
This requires that the specified file descriptor is passed to the
monitor alongside a prior getfd monitor command.
There are some additional features provided by certain image types
where Qemu reopens the image file. All of these scenarios will be
unsupported for the fd: protocol, at least for this patch:
- The -snapshot command line option
- The savevm monitor command
- The snapshot_blkdev monitor command
- Use of copy-on-write image files
- The -cdrom command line option
- The -drive command line option with media=cdrom
- The change monitor command
The thought is that this support can be added in the future, but is
not required for the initial fd: support.
This patch was tested with the following formats: raw, cow, qcow,
qcow2, qed, and vmdk, using the fd: protocol from the command line
and the monitor. Tests were also run to verify existing file name
support and qemu-img were not regressed. Non-valid file descriptors,
fd: without format, snapshot and backing files, and cdrom were also
tested.
v2:
- Add drive_add monitor command support
- Fence off unsupported features that re-open image file
v3:
- Fence off cdrom and change monitor command support
Signed-off-by: Corey Bryant <coreyb(a)linux.vnet.ibm.com>
---
block.c | 16 ++++++++++
block.h | 1 +
block/cow.c | 5 +++
block/qcow.c | 5 +++
block/qcow2.c | 5 +++
block/qed.c | 4 ++
block/raw-posix.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++------
block/vmdk.c | 5 +++
block_int.h | 1 +
blockdev.c | 19 ++++++++++++
monitor.c | 5 +++
monitor.h | 1 +
qemu-options.hx | 8 +++--
qemu-tool.c | 5 +++
14 files changed, 149 insertions(+), 12 deletions(-)
diff --git a/block.c b/block.c
index 24a25d5..500db84 100644
--- a/block.c
+++ b/block.c
@@ -536,6 +536,10 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
char tmp_filename[PATH_MAX];
char backing_filename[PATH_MAX];
+ if (bdrv_is_fd_protocol(bs)) {
+ return -ENOTSUP;
+ }
+
/* if snapshot, we create a temporary backing file and open it
instead of opening 'filename' directly */
@@ -585,6 +589,10 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
/* Find the right image format driver */
if (!drv) {
+ /* format must be specified for fd: protocol */
+ if (bdrv_is_fd_protocol(bs)) {
+ return -ENOTSUP;
+ }
ret = find_image_format(filename, &drv);
}
@@ -1460,6 +1468,11 @@ int bdrv_enable_write_cache(BlockDriverState *bs)
return bs->enable_write_cache;
}
+int bdrv_is_fd_protocol(BlockDriverState *bs)
+{
+ return bs->fd_protocol;
+}
+
/* XXX: no longer used */
void bdrv_set_change_cb(BlockDriverState *bs,
void (*change_cb)(void *opaque, int reason),
@@ -1964,6 +1977,9 @@ int bdrv_snapshot_create(BlockDriverState *bs,
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOMEDIUM;
+ if (bdrv_is_fd_protocol(bs)) {
+ return -ENOTSUP;
+ }
if (drv->bdrv_snapshot_create)
return drv->bdrv_snapshot_create(bs, sn_info);
if (bs->file)
diff --git a/block.h b/block.h
index 859d1d9..0417b69 100644
--- a/block.h
+++ b/block.h
@@ -182,6 +182,7 @@ int bdrv_is_removable(BlockDriverState *bs);
int bdrv_is_read_only(BlockDriverState *bs);
int bdrv_is_sg(BlockDriverState *bs);
int bdrv_enable_write_cache(BlockDriverState *bs);
+int bdrv_is_fd_protocol(BlockDriverState *bs);
int bdrv_is_inserted(BlockDriverState *bs);
int bdrv_media_changed(BlockDriverState *bs);
int bdrv_is_locked(BlockDriverState *bs);
diff --git a/block/cow.c b/block/cow.c
index 4cf543c..e17f8e7 100644
--- a/block/cow.c
+++ b/block/cow.c
@@ -82,6 +82,11 @@ static int cow_open(BlockDriverState *bs, int flags)
pstrcpy(bs->backing_file, sizeof(bs->backing_file),
cow_header.backing_file);
+ if (bs->backing_file[0] != '\0' && bdrv_is_fd_protocol(bs)) {
+ /* backing file currently not supported by fd: protocol */
+ goto fail;
+ }
+
bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header);
s->cow_sectors_offset = (bitmap_size + 511) & ~511;
return 0;
diff --git a/block/qcow.c b/block/qcow.c
index 227b104..964d411 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -157,6 +157,11 @@ static int qcow_open(BlockDriverState *bs, int flags)
if (bdrv_pread(bs->file, header.backing_file_offset, bs->backing_file, len) != len)
goto fail;
bs->backing_file[len] = '\0';
+
+ if (bs->backing_file[0] != '\0' && bdrv_is_fd_protocol(bs)) {
+ /* backing file currently not supported by fd: protocol */
+ goto fail;
+ }
}
return 0;
diff --git a/block/qcow2.c b/block/qcow2.c
index 48e1b95..7f6a4fa 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -270,6 +270,11 @@ static int qcow2_open(BlockDriverState *bs, int flags)
goto fail;
}
bs->backing_file[len] = '\0';
+
+ if (bs->backing_file[0] != '\0' && bdrv_is_fd_protocol(bs)) {
+ ret = -ENOTSUP;
+ goto fail;
+ }
}
if (qcow2_read_snapshots(bs) < 0) {
ret = -EINVAL;
diff --git a/block/qed.c b/block/qed.c
index 3970379..5028897 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -446,6 +446,10 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags)
return ret;
}
+ if (bs->backing_file[0] != '\0' && bdrv_is_fd_protocol(bs)) {
+ return -ENOTSUP;
+ }
+
if (s->header.features & QED_F_BACKING_FORMAT_NO_PROBE) {
pstrcpy(bs->backing_format, sizeof(bs->backing_format), "raw");
}
diff --git a/block/raw-posix.c b/block/raw-posix.c
index 34b64aa..cec4d36 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -28,6 +28,7 @@
#include "block_int.h"
#include "module.h"
#include "block/raw-posix-aio.h"
+#include "monitor.h"
#ifdef CONFIG_COCOA
#include <paths.h>
@@ -185,7 +186,8 @@ static int raw_open_common(BlockDriverState *bs, const char *filename,
int bdrv_flags, int open_flags)
{
BDRVRawState *s = bs->opaque;
- int fd, ret;
+ int fd = -1;
+ int ret;
ret = raw_normalize_devicepath(&filename);
if (ret != 0) {
@@ -207,15 +209,17 @@ static int raw_open_common(BlockDriverState *bs, const char *filename,
if (!(bdrv_flags & BDRV_O_CACHE_WB))
s->open_flags |= O_DSYNC;
- s->fd = -1;
- fd = qemu_open(filename, s->open_flags, 0644);
- if (fd < 0) {
- ret = -errno;
- if (ret == -EROFS)
- ret = -EACCES;
- return ret;
+ if (s->fd == -1) {
+ fd = qemu_open(filename, s->open_flags, 0644);
+ if (fd < 0) {
+ ret = -errno;
+ if (ret == -EROFS) {
+ ret = -EACCES;
+ }
+ return ret;
+ }
+ s->fd = fd;
}
- s->fd = fd;
s->aligned_buf = NULL;
if ((bdrv_flags & BDRV_O_NOCACHE)) {
@@ -272,6 +276,7 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags)
{
BDRVRawState *s = bs->opaque;
+ s->fd = -1;
s->type = FTYPE_FILE;
return raw_open_common(bs, filename, flags, 0);
}
@@ -892,6 +897,60 @@ static BlockDriver bdrv_file = {
.create_options = raw_create_options,
};
+static int raw_open_fd(BlockDriverState *bs, const char *filename, int flags)
+{
+ BDRVRawState *s = bs->opaque;
+ const char *fd_str;
+ int fd;
+
+ /* extract the file descriptor - fail if it's not fd: */
+ if (!strstart(filename, "fd:", &fd_str)) {
+ return -EINVAL;
+ }
+
+ if (!qemu_isdigit(fd_str[0])) {
+ /* get fd from monitor */
+ fd = qemu_get_fd(fd_str);
+ if (fd == -1) {
+ return -EBADF;
+ }
+ } else {
+ char *endptr = NULL;
+
+ fd = strtol(fd_str, &endptr, 10);
+ if (*endptr || (fd == 0 && fd_str == endptr)) {
+ return -EBADF;
+ }
+ }
+
+ s->fd = fd;
+ s->type = FTYPE_FILE;
+
+ return raw_open_common(bs, filename, flags, 0);
+}
+
+static BlockDriver bdrv_file_fd = {
+ .format_name = "file",
+ .protocol_name = "fd",
+ .instance_size = sizeof(BDRVRawState),
+ .bdrv_probe = NULL, /* no probe for protocols */
+ .bdrv_file_open = raw_open_fd,
+ .bdrv_read = raw_read,
+ .bdrv_write = raw_write,
+ .bdrv_close = raw_close,
+ .bdrv_flush = raw_flush,
+ .bdrv_discard = raw_discard,
+
+ .bdrv_aio_readv = raw_aio_readv,
+ .bdrv_aio_writev = raw_aio_writev,
+ .bdrv_aio_flush = raw_aio_flush,
+
+ .bdrv_truncate = raw_truncate,
+ .bdrv_getlength = raw_getlength,
+
+ .create_options = raw_create_options,
+};
+
/***********************************************/
/* host device */
@@ -1000,6 +1059,7 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
}
#endif
+ s->fd = -1;
s->type = FTYPE_FILE;
#if defined(__linux__)
{
@@ -1170,6 +1230,7 @@ static int floppy_open(BlockDriverState *bs, const char *filename, int flags)
BDRVRawState *s = bs->opaque;
int ret;
+ s->fd = -1;
s->type = FTYPE_FD;
/* open will not fail even if no floppy is inserted, so add O_NONBLOCK */
@@ -1288,6 +1349,7 @@ static int cdrom_open(BlockDriverState *bs, const char *filename, int flags)
{
BDRVRawState *s = bs->opaque;
+ s->fd = -1;
s->type = FTYPE_CD;
/* open will not fail even if no CD is inserted, so add O_NONBLOCK */
@@ -1517,6 +1579,7 @@ static void bdrv_file_init(void)
* Register all the drivers. Note that order is important, the driver
* registered last will get probed first.
*/
+ bdrv_register(&bdrv_file_fd);
bdrv_register(&bdrv_file);
bdrv_register(&bdrv_host_device);
#ifdef __linux__
diff --git a/block/vmdk.c b/block/vmdk.c
index 922b23d..2ea808e 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -353,6 +353,11 @@ static int vmdk_parent_open(BlockDriverState *bs)
return -1;
pstrcpy(bs->backing_file, end_name - p_name + 1, p_name);
+
+ if (bs->backing_file[0] != '\0' && bdrv_is_fd_protocol(bs)) {
+ /* backing file currently not supported by fd: protocol */
+ return -1;
+ }
}
return 0;
diff --git a/block_int.h b/block_int.h
index 1e265d2..441049c 100644
--- a/block_int.h
+++ b/block_int.h
@@ -152,6 +152,7 @@ struct BlockDriverState {
int encrypted; /* if true, the media is encrypted */
int valid_key; /* if true, a valid encryption key has been set */
int sg; /* if true, the device is a /dev/sg* */
+ int fd_protocol; /* if true, the fd: protocol was specified */
/* event callback when inserting/removing */
void (*change_cb)(void *opaque, int reason);
void *change_opaque;
diff --git a/blockdev.c b/blockdev.c
index c263663..5cb7b56 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -542,6 +542,14 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
+ if (strncmp(file, "fd:", 3) == 0) {
+ if (media == MEDIA_CDROM) {
+ error_report("CD-ROM not supported by fd: protocol");
+ goto err;
+ }
+ dinfo->bdrv->fd_protocol = 1;
+ }
+
ret = bdrv_open(dinfo->bdrv, file, bdrv_flags, drv);
if (ret < 0) {
error_report("could not open disk image %s: %s",
@@ -602,6 +610,12 @@ int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data)
goto out;
}
+ if (bdrv_is_fd_protocol(bs)) {
+ qerror_report(QERR_UNSUPPORTED);
+ ret = -1;
+ goto out;
+ }
+
pstrcpy(old_filename, sizeof(old_filename), bs->filename);
old_drv = bs->drv;
@@ -719,6 +733,11 @@ int do_change_block(Monitor *mon, const char *device,
BlockDriver *drv = NULL;
int bdrv_flags;
+ if (strncmp(filename, "fd:", 3) == 0) {
+ qerror_report(QERR_UNSUPPORTED);
+ return -1;
+ }
+
bs = bdrv_find(device);
if (!bs) {
qerror_report(QERR_DEVICE_NOT_FOUND, device);
diff --git a/monitor.c b/monitor.c
index a6388a9..e521b60 100644
--- a/monitor.c
+++ b/monitor.c
@@ -2832,6 +2832,11 @@ int monitor_get_fd(Monitor *mon, const char *fdname)
return -1;
}
+int qemu_get_fd(const char *fdname)
+{
+ return cur_mon ? monitor_get_fd(cur_mon, fdname) : -1;
+}
+
static const mon_cmd_t mon_cmds[] = {
#include "hmp-commands.h"
{ NULL, NULL, },
diff --git a/monitor.h b/monitor.h
index 4f2d328..de5b987 100644
--- a/monitor.h
+++ b/monitor.h
@@ -51,6 +51,7 @@ int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs,
void *opaque);
int monitor_get_fd(Monitor *mon, const char *fdname);
+int qemu_get_fd(const char *fdname);
void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
GCC_FMT_ATTR(2, 0);
diff --git a/qemu-options.hx b/qemu-options.hx
index cb3347e..ab28541 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -116,7 +116,7 @@ using @file{/dev/cdrom} as filename (@pxref{host_drives}).
ETEXI
DEF("drive", HAS_ARG, QEMU_OPTION_drive,
- "-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n"
+ "-drive [file=[fd:]file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n"
" [,cyls=c,heads=h,secs=s[,trans=t]][,snapshot=on|off]\n"
" [,cache=writethrough|writeback|none|unsafe][,format=f]\n"
" [,serial=s][,addr=A][,id=name][,aio=threads|native]\n"
@@ -129,10 +129,12 @@ STEXI
Define a new drive. Valid options are:
@table @option
-@item file=@var{file}
+@item file=[fd:]@var{file}
This option defines which disk image (@pxref{disk_images}) to use with
this drive. If the filename contains comma, you must double it
-(for instance, "file=my,,file" to use file "my,file").
+(for instance, "file=my,,file" to use file "my,file"). @option{fd:}@var{file}
+specifies the file descriptor of an already open disk image.
+@option{format=}@var{format} is required by @option{fd:}@var{file}.
@item if=@var{interface}
This option defines on which type on interface the drive is connected.
Available types are: ide, scsi, sd, mtd, floppy, pflash, virtio.
diff --git a/qemu-tool.c b/qemu-tool.c
index 41e5c41..8fe6b8c 100644
--- a/qemu-tool.c
+++ b/qemu-tool.c
@@ -96,3 +96,8 @@ int64_t qemu_get_clock_ns(QEMUClock *clock)
{
return 0;
}
+
+int qemu_get_fd(const char *fdname)
+{
+ return -1;
+}
--
1.7.3.4
13 years, 8 months
[libvirt] [PATCH] daemon: Add early libvirtd start verbose errors.
by Peter Krempa
Early errors during start of libvirtd didn't have
an error reporting mechanism and caused libvirtd
to exit silently (only the return value indicated
an error). This patch adds error messages printed
to stderr if verbose parameter is specified to the
daemon.
fixes: https://bugzilla.redhat.com/show_bug.cgi?id=728654
---
daemon/libvirtd.c | 40 ++++++++++++++++++++++++++++++++--------
1 files changed, 32 insertions(+), 8 deletions(-)
diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c
index 53f1002..c3867af 100644
--- a/daemon/libvirtd.c
+++ b/daemon/libvirtd.c
@@ -1328,14 +1328,20 @@ int main(int argc, char **argv) {
case 'p':
VIR_FREE(pid_file);
- if (!(pid_file = strdup(optarg)))
+ if (!(pid_file = strdup(optarg))) {
+ if (verbose)
+ fprintf(stderr, _("ERROR: Can't allocate memory\n"));
exit(EXIT_FAILURE);
+ }
break;
case 'f':
VIR_FREE(remote_config_file);
- if (!(remote_config_file = strdup(optarg)))
+ if (!(remote_config_file = strdup(optarg))) {
+ if (verbose)
+ fprintf(stderr, _("ERROR: Can't allocate memory\n"));
exit(EXIT_FAILURE);
+ }
break;
case OPT_VERSION:
@@ -1353,21 +1359,30 @@ int main(int argc, char **argv) {
}
}
- if (!(config = daemonConfigNew(privileged)))
+ if (!(config = daemonConfigNew(privileged))) {
+ if (verbose)
+ fprintf(stderr, _("ERROR: Can't create configuration\n"));
exit(EXIT_FAILURE);
+ }
/* No explicit config, so try and find a default one */
if (remote_config_file == NULL) {
implicit_conf = true;
if (daemonConfigFilePath(privileged,
- &remote_config_file) < 0)
+ &remote_config_file) < 0) {
+ if (verbose)
+ fprintf(stderr, _("ERROR: Can't determine config path\n"));
exit(EXIT_FAILURE);
+ }
}
/* Read the config file if it exists*/
if (remote_config_file &&
- daemonConfigLoad(config, remote_config_file, implicit_conf) < 0)
+ daemonConfigLoad(config, remote_config_file, implicit_conf) < 0) {
+ if (verbose)
+ fprintf(stderr, _("ERROR: Can't load config file '%s'\n"), remote_config_file);
exit(EXIT_FAILURE);
+ }
if (config->host_uuid &&
virSetHostUUIDStr(config->host_uuid) < 0) {
@@ -1375,19 +1390,28 @@ int main(int argc, char **argv) {
exit(EXIT_FAILURE);
}
- if (daemonSetupLogging(config, privileged, verbose, godaemon) < 0)
+ if (daemonSetupLogging(config, privileged, verbose, godaemon) < 0) {
+ if (verbose)
+ fprintf(stderr, _("ERROR: Can't initialise logging\n"));
exit(EXIT_FAILURE);
+ }
+
+ /* error logging is up, use libvirt's error logging from now */
if (!pid_file && privileged &&
daemonPidFilePath(privileged,
- &pid_file) < 0)
+ &pid_file) < 0) {
+ VIR_ERROR(_("Can't determine pid file path."));
exit(EXIT_FAILURE);
+ }
if (daemonUnixSocketPaths(config,
privileged,
&sock_file,
- &sock_file_ro) < 0)
+ &sock_file_ro) < 0) {
+ VIR_ERROR(_("Can't determine socket paths"));
exit(EXIT_FAILURE);
+ }
if (godaemon) {
char ebuf[1024];
--
1.7.6
13 years, 8 months
[libvirt] [PATCH] Bugfix: Check stdoutWatch before removing the handler.
by Juerg Haefliger
---
tools/console.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/tools/console.c b/tools/console.c
index 11087e5..171ebc9 100644
--- a/tools/console.c
+++ b/tools/console.c
@@ -95,7 +95,7 @@ virConsoleShutdown(virConsolePtr con)
virStreamFree(con->st);
if (con->stdinWatch != -1)
virEventRemoveHandle(con->stdinWatch);
- if (con->stdinWatch != -1)
+ if (con->stdoutWatch != -1)
virEventRemoveHandle(con->stdoutWatch);
con->stdinWatch = -1;
con->stdoutWatch = -1;
--
1.7.4.1
13 years, 8 months
[libvirt] [PATCH] virsh: add snapshot-parent
by Eric Blake
Down the road, I want to add virDomainSnapshotGetParent, and use
the new API rather than xml scraping; but this virsh command can
be implemented even without the new API.
* tools/virsh.c (cmdSnapshotParent): New command.
* tools/virsh.pod (snapshot-parent): Document it.
---
Adding this will make it a bit easier to track relationships between
snapshots, especially since 'virsh snapshot-list' covers everything
except the relationship.
tools/virsh.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
tools/virsh.pod | 4 +++
2 files changed, 82 insertions(+), 0 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c
index 1d660d0..ee08c01 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -12285,6 +12285,82 @@ cleanup:
}
/*
+ * "snapshot-parent" command
+ */
+static const vshCmdInfo info_snapshot_parent[] = {
+ {"help", N_("Get the name of the parent of the current snapshot")},
+ {"desc", N_("Extract the snapshot's parent, if any")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_snapshot_parent[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"snapshotname", VSH_OT_DATA, VSH_OFLAG_REQ, N_("snapshot name")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdSnapshotParent(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ bool ret = false;
+ const char *name = NULL;
+ virDomainSnapshotPtr snapshot = NULL;
+ char *xml = NULL;
+ char *parent = NULL;
+ xmlDocPtr xmldoc = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+
+ if (!vshConnectionUsability(ctl, ctl->conn))
+ goto cleanup;
+
+ dom = vshCommandOptDomain(ctl, cmd, NULL);
+ if (dom == NULL)
+ goto cleanup;
+
+ if (vshCommandOptString(cmd, "snapshotname", &name) <= 0)
+ goto cleanup;
+
+ snapshot = virDomainSnapshotLookupByName(dom, name, 0);
+ if (snapshot == NULL)
+ goto cleanup;
+
+ xml = virDomainSnapshotGetXMLDesc(snapshot, 0);
+ if (!xml)
+ goto cleanup;
+
+ xmldoc = xmlReadDoc((const xmlChar *) xml, "domainsnapshot.xml", NULL,
+ XML_PARSE_NOENT | XML_PARSE_NONET |
+ XML_PARSE_NOWARNING);
+ if (!xmldoc)
+ goto cleanup;
+ ctxt = xmlXPathNewContext(xmldoc);
+ if (!ctxt)
+ goto cleanup;
+
+ parent = virXPathString("string(/domainsnapshot/parent/name)", ctxt);
+ if (!parent)
+ goto cleanup;
+
+ vshPrint(ctl, "%s", parent);
+
+ ret = true;
+
+cleanup:
+ VIR_FREE(parent);
+ xmlXPathFreeContext(ctxt);
+ if (xmldoc)
+ xmlFreeDoc(xmldoc);
+ VIR_FREE(xml);
+ if (snapshot)
+ virDomainSnapshotFree(snapshot);
+ if (dom)
+ virDomainFree(dom);
+
+ return ret;
+}
+
+/*
* "snapshot-revert" command
*/
static const vshCmdInfo info_snapshot_revert[] = {
@@ -12754,6 +12830,8 @@ static const vshCmdDef snapshotCmds[] = {
info_snapshot_dumpxml, 0},
{"snapshot-list", cmdSnapshotList, opts_snapshot_list,
info_snapshot_list, 0},
+ {"snapshot-parent", cmdSnapshotParent, opts_snapshot_parent,
+ info_snapshot_parent, 0},
{"snapshot-revert", cmdDomainSnapshotRevert, opts_snapshot_revert,
info_snapshot_revert, 0},
{NULL, NULL, NULL, NULL, 0}
diff --git a/tools/virsh.pod b/tools/virsh.pod
index a6af1e6..1893c23 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -1583,6 +1583,10 @@ List all of the available snapshots for the given domain.
Output the snapshot XML for the domain's snapshot named I<snapshot>.
+=item B<snapshot-parent> I<domain> I<snapshot>
+
+Output the name of the parent snapshot for the given I<snapshot>, if any.
+
=item B<snapshot-revert> I<domain> I<snapshot>
Revert the given domain to the snapshot specified by I<snapshot>. Be aware
--
1.7.4.4
13 years, 8 months
[libvirt] [PATCH] qemu: avoid dereference of null pointer
by ajia@redhat.com
* src/qemu/qemu_driver.c: avoid dereference of null pointer.
Signed-off-by: Alex Jia <ajia(a)redhat.com>
---
src/qemu/qemu_driver.c | 27 ++++++++++++++++++---------
1 files changed, 18 insertions(+), 9 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index ce19be7..28ffff7 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -5697,7 +5697,8 @@ static int qemuDomainSetBlkioParameters(virDomainPtr dom,
continue;
}
- persistentDef->blkio.weight = params[i].value.ui;
+ if (params[i].value.ul)
+ persistentDef->blkio.weight = params[i].value.ui;
} else {
qemuReportError(VIR_ERR_INVALID_ARG,
_("Parameter `%s' not supported"), param->field);
@@ -5837,7 +5838,8 @@ static int qemuDomainGetBlkioParameters(virDomainPtr dom,
"%s", _("Field blkio weight too long for destination"));
goto cleanup;
}
- param->value.ui = persistentDef->blkio.weight;
+ if (persistentDef->blkio.weight)
+ param->value.ui = persistentDef->blkio.weight;
break;
default:
@@ -5946,7 +5948,8 @@ static int qemuDomainSetMemoryParameters(virDomainPtr dom,
}
if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
- persistentDef->mem.hard_limit = params[i].value.ul;
+ if (params[i].value.ul)
+ persistentDef->mem.hard_limit = params[i].value.ul;
}
} else if (STREQ(param->field, VIR_DOMAIN_MEMORY_SOFT_LIMIT)) {
int rc;
@@ -5967,7 +5970,8 @@ static int qemuDomainSetMemoryParameters(virDomainPtr dom,
}
if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
- persistentDef->mem.soft_limit = params[i].value.ul;
+ if (params[i].value.ul)
+ persistentDef->mem.soft_limit = params[i].value.ul;
}
} else if (STREQ(param->field, VIR_DOMAIN_MEMORY_SWAP_HARD_LIMIT)) {
int rc;
@@ -5987,7 +5991,8 @@ static int qemuDomainSetMemoryParameters(virDomainPtr dom,
}
}
if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
- persistentDef->mem.swap_hard_limit = params[i].value.ul;
+ if (params[i].value.ul)
+ persistentDef->mem.swap_hard_limit = params[i].value.ul;
}
} else if (STREQ(param->field, VIR_DOMAIN_MEMORY_MIN_GUARANTEE)) {
qemuReportError(VIR_ERR_INVALID_ARG,
@@ -6107,7 +6112,8 @@ static int qemuDomainGetMemoryParameters(virDomainPtr dom,
"%s", _("Field memory hard limit too long for destination"));
goto cleanup;
}
- param->value.ul = persistentDef->mem.hard_limit;
+ if (persistentDef->mem.hard_limit)
+ param->value.ul = persistentDef->mem.hard_limit;
break;
case 1: /* fill memory soft limit here */
@@ -6404,7 +6410,8 @@ static int qemuSetSchedulerParametersFlags(virDomainPtr dom,
goto cleanup;
}
- vm->def->cputune.shares = params[i].value.ul;
+ if (params[i].value.ul)
+ vm->def->cputune.shares = params[i].value.ul;
}
if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
@@ -6428,7 +6435,8 @@ static int qemuSetSchedulerParametersFlags(virDomainPtr dom,
}
if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
- vmdef->cputune.period = params[i].value.ul;
+ if (params[i].value.ul)
+ vmdef->cputune.period = params[i].value.ul;
}
} else if (STREQ(param->field, "vcpu_quota")) {
if (param->type != VIR_TYPED_PARAM_LLONG) {
@@ -6448,7 +6456,8 @@ static int qemuSetSchedulerParametersFlags(virDomainPtr dom,
}
if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
- vmdef->cputune.quota = params[i].value.l;
+ if (params[i].value.ul)
+ vmdef->cputune.quota = params[i].value.l;
}
} else {
qemuReportError(VIR_ERR_INVALID_ARG,
--
1.7.5.1
13 years, 8 months
[libvirt] [PATCH] conf: eliminate redundat VIR_ALLOC of first element of hosts.
by Laine Stump
virNetworkDNSHostsDefParseXML was calling VIR_ALLOC(def->hosts) if
def->nhosts was 0. This is a waste of time, though, since
VIR_REALLOC_N is called a few lines further down, prior to any use of
def->hosts.
---
src/conf/network_conf.c | 8 --------
1 files changed, 0 insertions(+), 8 deletions(-)
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index e055094..109739f 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -495,14 +495,6 @@ virNetworkDNSHostsDefParseXML(virNetworkDNSDefPtr def,
virSocketAddr inaddr;
int ret = -1;
- if (def->hosts == NULL) {
- if (VIR_ALLOC(def->hosts) < 0) {
- virReportOOMError();
- goto error;
- }
- def->nhosts = 0;
- }
-
if (!(ip = virXMLPropString(node, "ip")) ||
(virSocketParseAddr(ip, &inaddr, AF_UNSPEC) < 0)) {
virNetworkReportError(VIR_ERR_XML_DETAIL,
--
1.7.3.4
13 years, 8 months