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 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
@@ -0,0 +1,8 @@
1
+ module XenStore
2
+ # Client provides high-level interaction with XenStore
3
+ class Client
4
+ def initialize(transport = nil)
5
+ @transport = transport || XenStore::Connection::UnixSocketConnection.new
6
+ end
7
+ end
8
+ end
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module XenStore
2
+ VERSION = '0.0.1'.freeze
3
+ end
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: []