xsrb 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/xsrb/client.rb +8 -0
- data/lib/xsrb/connection.rb +59 -0
- data/lib/xsrb/exceptions.rb +18 -0
- data/lib/xsrb/packet.rb +55 -0
- data/lib/xsrb/transport.rb +70 -0
- data/lib/xsrb/utils.rb +164 -0
- data/lib/xsrb/version.rb +3 -0
- data/lib/xsrb.rb +16 -0
- metadata +51 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 464c4bdf14e7cf84d74d07e4dccfaba34058b60f
|
4
|
+
data.tar.gz: 1dd6eda8bb0c16767511ae203096e0a07804b672
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7d063acdbee201dbd0101dcf03b83de240ab7dab3bb797e3016e240dee45e9dfccd3ff92bec3e6be66fcfd2a9e2bae3ff62300b5172ffd834ddb369add69bdbf
|
7
|
+
data.tar.gz: d66fee09e9bc180f4fe4e31407f82266b2a9c566392a516337c1b812e5a75749c1b585a020587bee48280fff7355e3739307106910770a0426db83355efa01f2
|
data/lib/xsrb/client.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'xsrb/exceptions'
|
2
|
+
require 'xsrb/utils'
|
3
|
+
|
4
|
+
module XenStore
|
5
|
+
module Connection
|
6
|
+
# Generic class which implements a connection which send Packets to a
|
7
|
+
# XenStore daemon.
|
8
|
+
class PacketConnection
|
9
|
+
def initialize(transport)
|
10
|
+
@transport = transport
|
11
|
+
end
|
12
|
+
|
13
|
+
# Serialize a +Packet+ and send over the wire to XenStored
|
14
|
+
#
|
15
|
+
# @param pkt [Packet] The +Packet+ of data to send to xenstored.
|
16
|
+
def send(pkt)
|
17
|
+
data = pkt.pack
|
18
|
+
@transport.send data
|
19
|
+
end
|
20
|
+
|
21
|
+
# Receive a +Packet+ from XenStored
|
22
|
+
#
|
23
|
+
# @return [Packet] The latest packet received from XenStore.
|
24
|
+
# Will block until either a packet is received
|
25
|
+
# or an error occurs.
|
26
|
+
def recv
|
27
|
+
header = @transport.recv Packet.header_size
|
28
|
+
|
29
|
+
op, rq_id, tx_id, len = header.unpack('IIII')
|
30
|
+
if len > 4096
|
31
|
+
raise XenStore::Exceptions::InvalidPayload,
|
32
|
+
"Payload too large (#{l})"
|
33
|
+
end
|
34
|
+
|
35
|
+
body = @transport.recv len
|
36
|
+
Packet.new(op, body, rq_id, tx_id)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# A +PacketConnection+ implementation which communicates with XenStored over
|
41
|
+
# a UNIX socket.
|
42
|
+
class UnixSocketConnection < PacketConnection
|
43
|
+
def initialize(path = nil)
|
44
|
+
@path = path || XenStore::Utils.unix_socket_path
|
45
|
+
transport = XenStore::Transport::UnixSocketTransport.new @path
|
46
|
+
super(transport)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# A +PacketConnection+ implementation
|
51
|
+
class XenBusConnection < PacketConnection
|
52
|
+
def initialize(path = nil)
|
53
|
+
@path = path || XenStore::Utils.xenbus_path
|
54
|
+
transport = XenStore::Transport::XenBusTransport.new @path
|
55
|
+
super(transport)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module XenStore
|
2
|
+
module Exceptions
|
3
|
+
class XenStoreException < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
class InvalidPermission < XenStoreException
|
7
|
+
end
|
8
|
+
|
9
|
+
class InvalidPath < XenStoreException
|
10
|
+
end
|
11
|
+
|
12
|
+
class InvalidPayload < XenStoreException
|
13
|
+
end
|
14
|
+
|
15
|
+
class InvalidOperation < XenStoreException
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/xsrb/packet.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module XenStore
|
2
|
+
# An individual Packet of data sent to XenStore.
|
3
|
+
class Packet
|
4
|
+
def initialize(op, payload, rq_id, tx_id = 0)
|
5
|
+
if payload.length > 4096
|
6
|
+
raise XenStore::Exceptions::InvalidPayload,
|
7
|
+
"Payload too large (#{l}): #{payload}"
|
8
|
+
end
|
9
|
+
|
10
|
+
@op = check_op op
|
11
|
+
@rq_id = rq_id
|
12
|
+
@tx_id = tx_id
|
13
|
+
@payload = payload.to_s + XenStore::NUL
|
14
|
+
end
|
15
|
+
|
16
|
+
# Convert to a binary representation for transport to the xenstored
|
17
|
+
#
|
18
|
+
# @return [String] A binary version of the +Packet+.
|
19
|
+
def pack
|
20
|
+
packdata = [@op, @rq_id, @tx_id, @payload.length]
|
21
|
+
packdata.pack('IIII') + @payload
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
# Check if the provided operation is valid and raise a
|
26
|
+
# +XenStore::Exceptions::InvalidOperation+ exception if not.
|
27
|
+
#
|
28
|
+
# @param op Should be either a symbol or a uint. If a symbol
|
29
|
+
# then it will be used as a key to lookup the value
|
30
|
+
# in the +XenStore::OPERATIONS+ hash.
|
31
|
+
def check_op(op)
|
32
|
+
if op.is_a? Symbol
|
33
|
+
unless XenStore::OPERATIONS.key? op
|
34
|
+
raise XenStore::Exceptions::InvalidOperation, op.to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
XenStore::OPERATIONS[op]
|
38
|
+
else
|
39
|
+
unless XenStore::OPERATIONS.values.include? op
|
40
|
+
raise XenStore::Exceptions::InvalidOperation, op.to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
op
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Get size of each packet header
|
48
|
+
#
|
49
|
+
# @return [Integer] The size in bytes of each Packet header.
|
50
|
+
def header_size
|
51
|
+
4 * (32 / 8)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module XenStore
|
4
|
+
# Module to hold various types of Transport which allow communication
|
5
|
+
# with XenStore in various ways.
|
6
|
+
module Transport
|
7
|
+
# A Transport implementation which communicates with XenStore over the
|
8
|
+
# special device on UNIX-like operating systems.
|
9
|
+
class XenBusTransport
|
10
|
+
def initialize(path = nil)
|
11
|
+
path ||= XenStore::Utils.xenbus_path
|
12
|
+
@file = File.open(path, 'wb')
|
13
|
+
|
14
|
+
# Ensure the file is closed when this object is garbage collected
|
15
|
+
ObjectSpace.define_finalizer(self, proc { @file.close })
|
16
|
+
end
|
17
|
+
|
18
|
+
def send(data)
|
19
|
+
size = data.length
|
20
|
+
# Errno::EPIPE if other end disconnects
|
21
|
+
size -= @file.write while size
|
22
|
+
end
|
23
|
+
|
24
|
+
def recv(size)
|
25
|
+
chunks = []
|
26
|
+
while size
|
27
|
+
chunk = @file.read(size)
|
28
|
+
raise Errno::ECONNRESET unless chunk
|
29
|
+
|
30
|
+
chunks << read
|
31
|
+
size -= read.length
|
32
|
+
end
|
33
|
+
chunks.join ''
|
34
|
+
end
|
35
|
+
|
36
|
+
def close
|
37
|
+
@file.close
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# A Transport implementation which communicates with XenStore over a
|
42
|
+
# UNIX socket.
|
43
|
+
class UnixSocketTransport
|
44
|
+
def initialize(path = nil)
|
45
|
+
path ||= XenStore::Utils.unix_socket_path
|
46
|
+
@sock = UNIXSocket.new path
|
47
|
+
|
48
|
+
# Ensure the socket is closed when this object is garbage collected
|
49
|
+
ObjectSpace.define_finalizer(self, proc { @sock.close })
|
50
|
+
end
|
51
|
+
|
52
|
+
def send(data)
|
53
|
+
@sock.write(data)
|
54
|
+
end
|
55
|
+
|
56
|
+
def recv(size)
|
57
|
+
chunks = []
|
58
|
+
while size
|
59
|
+
chunks << @sock.read(size)
|
60
|
+
size -= chunks[-1].length
|
61
|
+
end
|
62
|
+
chunks.join ''
|
63
|
+
end
|
64
|
+
|
65
|
+
def close
|
66
|
+
@sock.close
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/xsrb/utils.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
require 'xsrb/exceptions'
|
5
|
+
|
6
|
+
#
|
7
|
+
module XenStore
|
8
|
+
# xs_wire.h uses uint32_t
|
9
|
+
MAX_UINT = (2**32).freeze
|
10
|
+
NUL = "\x00".freeze
|
11
|
+
CAPS_FILE = '/proc/xen/capabilities'.freeze
|
12
|
+
|
13
|
+
# Check whether we are in dom0
|
14
|
+
begin
|
15
|
+
CONTROL_D = File.open(CAPS_FILE, 'rb', &:read) == "control_d\n"
|
16
|
+
rescue Errno::ENOENT
|
17
|
+
CONTROL_D = false
|
18
|
+
end
|
19
|
+
|
20
|
+
OPERATIONS = {
|
21
|
+
debug: 0,
|
22
|
+
directory: 1,
|
23
|
+
read: 2,
|
24
|
+
get_perms: 3,
|
25
|
+
watch: 4,
|
26
|
+
unwatch: 5,
|
27
|
+
transaction_start: 6,
|
28
|
+
transaction_end: 7,
|
29
|
+
introduce: 8,
|
30
|
+
release: 9,
|
31
|
+
get_domain_path: 10,
|
32
|
+
write: 11,
|
33
|
+
mkdir: 12,
|
34
|
+
rm: 13,
|
35
|
+
set_perms: 14,
|
36
|
+
watch_event: 15,
|
37
|
+
error: 16,
|
38
|
+
is_domain_introduced: 17,
|
39
|
+
resume: 18,
|
40
|
+
set_target: 19,
|
41
|
+
restrict: 20,
|
42
|
+
reset_watches: 21,
|
43
|
+
invalid: MAX_UINT
|
44
|
+
}.freeze
|
45
|
+
|
46
|
+
# XenStore::Utils implements utility methods which are unlikely
|
47
|
+
# to be required by users but are used by the rest of the module
|
48
|
+
module Utils
|
49
|
+
@reqid = -1
|
50
|
+
|
51
|
+
@path_regex = Regexp.new '\A[a-zA-Z0-9\-/_@]+\x00?\z'
|
52
|
+
@watch_path_regex = Regexp.new '\A@(?:introduceDomain|releaseDomain)\x00?\z'
|
53
|
+
@perms_regex = Regexp.new '\A[wrbn]\d+\z'
|
54
|
+
|
55
|
+
@errno_exception_map = Hash[
|
56
|
+
Errno.constants.collect do |n|
|
57
|
+
[Errno.const_get(n)::Errno, Errno.const_get(n)]
|
58
|
+
end.reverse
|
59
|
+
].freeze
|
60
|
+
|
61
|
+
class << self
|
62
|
+
# Convert an error number or symbol to an Errno exception
|
63
|
+
#
|
64
|
+
# @param n [Integer, Symbol] An +Integer+ or +symbol+ representing the
|
65
|
+
# Errno exception to return.
|
66
|
+
# @return [Exception] The +Exception+ representing the provided type
|
67
|
+
# of error.
|
68
|
+
def error(n)
|
69
|
+
if n.is_a? Integer
|
70
|
+
@errno_exception_map[n]
|
71
|
+
else
|
72
|
+
Errno.send(n)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Get the next request ID to contact XenStore with.
|
77
|
+
#
|
78
|
+
# @return [Integer] The next ID in the sequence.
|
79
|
+
def next_request_id
|
80
|
+
@reqid += 1
|
81
|
+
|
82
|
+
# Ensure no larger than uint32_t which is used in xs_wire.h
|
83
|
+
@reqid %= MAX_UINT
|
84
|
+
end
|
85
|
+
|
86
|
+
# Get the path of the XenStore unix socket.
|
87
|
+
#
|
88
|
+
# @return [String] The path to the XenStore unix socket.
|
89
|
+
def unix_socket_path
|
90
|
+
dp = '/var/run/xenstored'
|
91
|
+
ENV['XENSTORED_PATH'] || File.join(ENV['XENSTORED_RUNDIR'] || dp,
|
92
|
+
'socket')
|
93
|
+
end
|
94
|
+
|
95
|
+
# Raise an exception if the provided path is invalid.
|
96
|
+
#
|
97
|
+
# @param path [String] The XenStore path to check.
|
98
|
+
# @return [String] The valid path.
|
99
|
+
def valid_path?(path)
|
100
|
+
pathname = Pathname.new path
|
101
|
+
max_len = pathname.absolute? ? 3072 : 2048
|
102
|
+
|
103
|
+
if path.length > max_len
|
104
|
+
raise XenStore::Exceptions::InvalidPath,
|
105
|
+
"Path too long: #{path}"
|
106
|
+
end
|
107
|
+
|
108
|
+
unless @path_regex =~ path
|
109
|
+
raise XenStore::Exceptions::InvalidPath,
|
110
|
+
path.to_s
|
111
|
+
end
|
112
|
+
|
113
|
+
path
|
114
|
+
end
|
115
|
+
|
116
|
+
# Raise an exception if the provided XenStore watch path is invalid.
|
117
|
+
#
|
118
|
+
# @param path [String] The XenStore watch path to check.
|
119
|
+
# @return [String] The valid path.
|
120
|
+
def valid_watch_path?(path)
|
121
|
+
if path.starts_with?('@') && (@watch_path_regex !~ path)
|
122
|
+
raise XenStore::Exceptions::InvalidPath, path.to_s
|
123
|
+
end
|
124
|
+
|
125
|
+
valid_path? path
|
126
|
+
end
|
127
|
+
|
128
|
+
# Check if every member of a list of permissions strings is valid.
|
129
|
+
#
|
130
|
+
# @param perms [Array, String] An +Array+ of XenStore permissions
|
131
|
+
# specifications
|
132
|
+
# @return [Array] The list of permissions.
|
133
|
+
def valid_permissions?(perms)
|
134
|
+
perms = [perms] if perms.is_a? String
|
135
|
+
perms.each do |perm|
|
136
|
+
unless perm =~ @perms_regex
|
137
|
+
raise XenStore::Exceptions::InvalidPermission,
|
138
|
+
"Invalid permission string: #{perm}"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Get the XenBus path on this system
|
144
|
+
#
|
145
|
+
# @return [String] The path to the XenBus device
|
146
|
+
def xenbus_path
|
147
|
+
default = '/dev/xen/xenbus'
|
148
|
+
host_os = RbConfig::CONFIG['host_os']
|
149
|
+
|
150
|
+
case host_os
|
151
|
+
when 'netbsd'
|
152
|
+
'/kern/xen/xenbus'
|
153
|
+
when 'linux'
|
154
|
+
File.readable?('/dev/xen/xenbus') ? '/proc/xen/xenbus' : default
|
155
|
+
when /mswin|windows/i
|
156
|
+
raise NotImplementedError,
|
157
|
+
"OS '#{RbConfig::CONFIG['host_os']}' is not supported"
|
158
|
+
else
|
159
|
+
default
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
data/lib/xsrb/version.rb
ADDED
data/lib/xsrb.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'logging'
|
2
|
+
|
3
|
+
require 'xsrb/version'
|
4
|
+
require 'xsrb/utils'
|
5
|
+
|
6
|
+
Logging.logger.root.level = :warn
|
7
|
+
|
8
|
+
# Module XenStore implements XenStore access APIs on Ruby.
|
9
|
+
# The implementation is modelled on the pyxs python module.
|
10
|
+
module XenStore
|
11
|
+
autoload :Client, 'xsrb/client'
|
12
|
+
autoload :Connection, 'xsrb/connection'
|
13
|
+
autoload :Exceptions, 'xsrb/exceptions'
|
14
|
+
autoload :Packet, 'xsrb/packet'
|
15
|
+
autoload :Transport, 'xsrb/transport'
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xsrb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- joelnb
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-05-13 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Pure Ruby XenStore Bindings based on pyxs (https://github.com/selectel/pyxs)
|
14
|
+
email: joelnbarnham@gmail.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/xsrb.rb
|
20
|
+
- lib/xsrb/client.rb
|
21
|
+
- lib/xsrb/connection.rb
|
22
|
+
- lib/xsrb/exceptions.rb
|
23
|
+
- lib/xsrb/packet.rb
|
24
|
+
- lib/xsrb/transport.rb
|
25
|
+
- lib/xsrb/utils.rb
|
26
|
+
- lib/xsrb/version.rb
|
27
|
+
homepage: https://github.com/joelnb/xsrb
|
28
|
+
licenses:
|
29
|
+
- MIT
|
30
|
+
metadata: {}
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
requirements: []
|
46
|
+
rubyforge_project:
|
47
|
+
rubygems_version: 2.6.13
|
48
|
+
signing_key:
|
49
|
+
specification_version: 4
|
50
|
+
summary: Ruby XenStore API Bindings
|
51
|
+
test_files: []
|