sockd 0.1.0

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: d0a446b2a9f0577de1dc8f750e0d49f913490f12
4
+ data.tar.gz: 0e6d76f5c7d9443182ed00888445be74080b1067
5
+ SHA512:
6
+ metadata.gz: cee27594593028ab2417be7cf4898fc3f6be8b0087bc54fe97a3dae408b212fc986ca3777efcba4f65ba88d0e493ecdffeb97fbbd88851e8fe61402cef0e6d83
7
+ data.tar.gz: c1c8c2f2ead721ba3b756802626ead69d5a3262931cd0255f3de4072a13b1cdab8cff3d4a4debf6516f10bb51ecfb4e78592a1684dc1329004367b178d01fc19
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
@@ -0,0 +1 @@
1
+ 1.5:5b9af9bc-a6a4-4a38-bcee-3dd882a77565
@@ -0,0 +1 @@
1
+ 1426302069
@@ -0,0 +1 @@
1
+ 5b9af9bc-a6a4-4a38-bcee-3dd882a77565
@@ -0,0 +1 @@
1
+ c2db229506d4472b8fb6d1e0d697d20f
@@ -0,0 +1,27 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIIEowIBAAKCAQEA7xjuC39O1pFg27/RWTeSTOwaGWqCYka1WpLBRd1nr/2mZw81
3
+ yjBGmWRwkD686TMZR+id5x+4bDTE9VasiZ3OU9EX6thL+8epVPdkBBRaQqluXHzW
4
+ soLZ1sP8GprJkTMEzUJyBPiVEl/feVW55yzTHWAgDUWWrp4qd7VvRmk4e0W25NRF
5
+ 69EMMvHMaFneHYJZ9BiDh2w2OAM1ehdyHU4ruWm8UTjwEzpT98fJSsUhBcColtZT
6
+ J7+cN8ksSiaihUlbJjzSS36CWrX5jBXyE4RYCFSEoHzp3vdO756j9bQqfdqMLfS4
7
+ 25C2azX6bQehlR3eSxK5CHixgzcWyyb2ZnT5TQIDAQABAoIBAQDB9BwuMXCXuEi1
8
+ Gi5NzDmesqwdT/xCko9M0N19ujQBXKae1YTR5kVu6z4wlOQT3AK3BWkJ8v5csJXR
9
+ WyUNYXjdHzHirOE+dmHTbfexI31wtBa1agOTvXfOsx8Pyd6XLabIhw0NIUV0KVeU
10
+ CsmKUR9UpgR6H6gzMFTM1N5WZMaPn5VatDCv8/1n3wju+7CHClPuPdYfAoNNTPY0
11
+ uIMkMVwguO9EjgNitbzfickQNT37UNyKXyqccVfdyvR7nnrs8QMIUH6Ny+dHwVei
12
+ +mkeOmEK8+vzCz/6XaRkgZ8lp3tKDRgaoUqIIK2zHGoS3YfugkbMhjI8E8NFpXVi
13
+ plNRzYQVAoGBAPyTEsXD/1qVvIibowPzA/kvb1QAzkdosxY2X5O7eYbGWB7bQSvt
14
+ hdp48/EalWOjp0CsUYCmXZRjU7BfIodbvVn87hAS2cc1BHqAgFmrx31jp/qfc4Zc
15
+ iqjJ/dnUto/yw/9ymg6uAc4dWQkzFsUj/gpEFJYuDsjZxCDFEe4DUUiLAoGBAPJX
16
+ EInRvuq6351Yq5YqHSuKhCxDAsF5Us+9dQqglQ5C7T/XuTb3bbgryvKU05FUVTQ4
17
+ zI6cRbDKkamOMK3wFdSX7It6h7VYqnuyb+BB2kx/GYkguI2Gh2jDvu0GS8RmnVOe
18
+ MzAACZW56jBCH8TIo1WyYbgnFsi5br9W/JrABiiHAoGAaaTKsVWttSH0Xd9bbAd1
19
+ ngCtPCGqJkwwvBW6HHzJ9Al6Sk3DELCk0CDPSAWqfqaxCmgJHxp3Ad4Z3yertZo1
20
+ tTIKEUcRODWAzgx5owgqxNOuKzIOjCChDsCOIVLLmaIdQPeaYJF/x8qWtzZbS9Im
21
+ K/0V70sMfA457sr/GOLiWMkCgYBG4tTBm4q+2n7CZcd7G0KBE/lpgQhzU2u3vHFZ
22
+ 9HLlQwpm3FEfFO0RLC4nGwVvwz1REzQymt9/wSEueZsfpdSc5PuaKPM9RnxHxoAX
23
+ Fuwl6B+uDtYs/6boLCnS5z6Oan2mkeXjKn+jPkiUaSMlypKMZKtU1IpDmIoeifFF
24
+ ytmCTQKBgHOzJIaY5b/s0mRbBGKYPT7idRWT0a1XVvAsJOTTuYNsbzGpWGcNPr+n
25
+ YmO2cl10W4YntFG3+U7uZYdEIqNK+mc1hs801dKmGdJqeBGwf0N6memJMLRZG+HX
26
+ FI8PY96eiNVJfIjG/tORr443ziJTlWJJf//pOvHEtQE54erC6ocI
27
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1 @@
1
+ {"virtualbox":{"/tmp/vagrant-opsworks":{"guestpath":"/tmp/vagrant-opsworks","hostpath":"/Users/mike/.vagrant.d/boxes/ubuntu1404-opsworks/0/virtualbox","disabled":false},"/vagrant":{"guestpath":"/vagrant","hostpath":"/Users/mike/Projects/sockd","disabled":false}}}
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sockd.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 PixelCog Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Sockd
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'sockd'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install sockd
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/[my-github-username]/sockd/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/Vagrantfile ADDED
@@ -0,0 +1,4 @@
1
+ Vagrant.configure("2") do |config|
2
+ config.vm.box = "ubuntu1404-opsworks"
3
+ config.vm.network "private_network", ip: "10.10.10.11"
4
+ end
@@ -0,0 +1,15 @@
1
+ module Sockd
2
+
3
+ class SockdError < StandardError
4
+ end
5
+
6
+ class OptionParserError < SockdError
7
+ end
8
+
9
+ class BadCommandError < SockdError
10
+ end
11
+
12
+ class ProcError < SockdError
13
+ end
14
+
15
+ end
@@ -0,0 +1,117 @@
1
+ require "fileutils"
2
+ require "optparse"
3
+ require "shellwords"
4
+ require "sockd/errors"
5
+
6
+ module Sockd
7
+ class OptionParser
8
+
9
+ attr_accessor :name, :options, :callback
10
+
11
+ def initialize(name = nil, defaults = {}, &block)
12
+ @name = name || File.basename($0)
13
+ @options = defaults.replace({
14
+ host: "127.0.0.1",
15
+ port: 0,
16
+ socket: false,
17
+ daemonize: true,
18
+ pid_path: "/var/run/#{safe_name}.pid",
19
+ log_path: false,
20
+ force: false,
21
+ user: nil,
22
+ group: nil,
23
+ verbose: false
24
+ }.merge(defaults))
25
+ @callback = block if block_given?
26
+ end
27
+
28
+ def safe_name
29
+ name.gsub(/(^[0-9]*|[^0-9a-z])/i, '')
30
+ end
31
+
32
+ def parser
33
+ @parser ||= ::OptionParser.new do |opts|
34
+ opts.summary_width = 25
35
+ opts.banner = <<-EOF.gsub /^[ ]{8}/, ''
36
+ Usage: #{name} [options] <command> [<message>]
37
+
38
+ Commands:
39
+ #{name} run server without forking
40
+ #{name} start start as a daemon
41
+ #{name} stop [-f] stop a running daemon
42
+ #{name} restart stop, then start the daemon
43
+ #{name} send <message> send a message to a running daemon
44
+ #{name} <message> send a message (command implied)
45
+
46
+ Options:
47
+ EOF
48
+
49
+ instance_exec(opts, callback) if callback
50
+
51
+ opts.on("-p", "--port PORT", String, "Listen on TCP port PORT (default: #{options[:port]})") do |x|
52
+ options[:port] = x
53
+ # prefer TCP connection if explicitly setting a port
54
+ options[:socket] = nil
55
+ end
56
+
57
+ opts.on("-H", "--host HOST", String, "Listen on HOST (default: #{options[:host]})") do |x|
58
+ options[:host] = x
59
+ # prefer TCP connection if explicitly setting a host
60
+ options[:socket] = nil
61
+ end
62
+
63
+ opts.on("-s", "--socket [SOCKET]", String, "Listen on Unix socket path (disables network support)", "(default: #{options[:socket]})") do |x|
64
+ options[:socket] = File.expand_path(x)
65
+ end
66
+
67
+ opts.on("-P", "--pid FILE", String, "Where to write the PID file", "(default: #{options[:pid_path]})") do |x|
68
+ options[:pid_path] = File.expand_path(x)
69
+ end
70
+
71
+ opts.on("-l", "--log FILE", String, "Where to write the log file", "(default: #{options[:log_path]})") do |x|
72
+ options[:log_path] = File.expand_path(x)
73
+ end
74
+
75
+ opts.on("-u", "--user USER", String, "Assume the identity of USER when running as a daemon (default: #{options[:user]})") do |x|
76
+ options[:user] = x
77
+ end
78
+
79
+ opts.on("-g", "--group GROUP", String, "Assume group GROUP when running as a daemon (default: #{options[:group]})") do |x|
80
+ options[:group] = x
81
+ end
82
+
83
+ opts.on("-f", "--force", String, "Force kill if SIGTERM fails when running 'stop' command") do
84
+ options[:force] = true
85
+ end
86
+
87
+ opts.separator "\n Additional Options:"
88
+
89
+ opts.on_tail("-h", "--help", "Display this usage information.") do
90
+ puts "\n#{opts}\n"
91
+ exit
92
+ end
93
+ end
94
+ end
95
+
96
+ def parse!(argv = nil)
97
+ argv ||= ARGV.dup
98
+ argv = Shellwords.shellwords argv if argv.is_a? String
99
+
100
+ parser.parse! argv
101
+
102
+ if argv.empty?
103
+ argv.push 'start'
104
+ options[:daemonize] = false
105
+ end
106
+ argv.unshift 'send' unless %w(start stop restart send).include?(argv.first)
107
+
108
+ argv
109
+ rescue ::OptionParser::InvalidOption, ::OptionParser::MissingArgument => e
110
+ raise OptionParserError.new e
111
+ end
112
+
113
+ def to_s
114
+ parser.to_s
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,308 @@
1
+ require "logger"
2
+ require "socket"
3
+ require "fileutils"
4
+ require "sockd/errors"
5
+
6
+ module Sockd
7
+ class Runner
8
+
9
+ attr_reader :options, :name
10
+
11
+ class << self
12
+ def define(*args, &block)
13
+ self.new(*args, &block)
14
+ end
15
+ end
16
+
17
+ def initialize(name, options = {}, &block)
18
+ @name = name
19
+ @options = {
20
+ :host => "127.0.0.1",
21
+ :port => 0,
22
+ :socket => false,
23
+ :daemonize => true,
24
+ :pid_path => "/var/run/#{safe_name}.pid",
25
+ :log_path => false,
26
+ :force => false,
27
+ :user => nil,
28
+ :group => nil
29
+ }.merge(options)
30
+
31
+ [:setup, :teardown, :handle].each do |opt|
32
+ self.public_send(opt, &options[opt]) if options[opt].respond_to?(:call)
33
+ end
34
+
35
+ yield self if block_given?
36
+ end
37
+
38
+ # merge options when set with self.options = {...}
39
+ def options=(val)
40
+ @options.merge!(val)
41
+ end
42
+
43
+ # generate a path-safe and username-safe string from our daemon name
44
+ def safe_name
45
+ name.gsub(/(^[0-9]*|[^0-9a-z])/i, '')
46
+ end
47
+
48
+ # define a "setup" callback by providing a block, or trigger the callback
49
+ # @runner.setup { |opts| Server.new(...) }
50
+ def setup(&block)
51
+ return self if block_given? && @setup = block
52
+ @setup.call(self) if @setup
53
+ end
54
+
55
+ # define a "teardown" callback by providing a block, or trigger the callback
56
+ # @runner.teardown { log "shutting down" }
57
+ def teardown(&block)
58
+ return self if block_given? && @teardown = block
59
+ @teardown.call(self) if @teardown
60
+ end
61
+
62
+ # define our socket handler by providing a block, or trigger the callback
63
+ # with the provided message
64
+ # @runner.handle { |msg| if msg == 'foo' then return 'bar' ... }
65
+ def handle(message = nil, &block)
66
+ return self if block_given? && @handle = block
67
+ @handle || (raise SockdError, "No message handler provided.")
68
+ @handle.call(message, self)
69
+ end
70
+
71
+ # call one of start, stop, restart, or send
72
+ def run(method, *args)
73
+ if %w(start stop restart send).include?(method)
74
+ begin
75
+ self.public_send method.to_sym, *args
76
+ rescue ArgumentError => e
77
+ raise unless e.backtrace[1].include? "in `public_send"
78
+ raise BadCommandError, "wrong number of arguments for command: #{method}"
79
+ end
80
+ else
81
+ raise BadCommandError, "invalid command: #{method}"
82
+ end
83
+ end
84
+
85
+ # start our service
86
+ def start
87
+ if options[:daemonize]
88
+ pid = daemon_running?
89
+ raise ProcError, "#{name} process already running (#{pid})" if pid
90
+ log "starting #{name} process..."
91
+ return self unless daemonize
92
+ end
93
+
94
+ drop_privileges options[:user], options[:group]
95
+
96
+ setup
97
+
98
+ on_interrupt do |signal|
99
+ log "#{signal} received, shutting down..."
100
+ teardown
101
+ exit 130
102
+ end
103
+
104
+ serve
105
+ end
106
+
107
+ # stop our service
108
+ def stop
109
+ if daemon_running?
110
+ pid = stored_pid
111
+ Process.kill('TERM', pid)
112
+ log "SIGTERM sent to #{name} (#{pid})"
113
+ if !wait_until(2) { daemon_stopped? pid } && options[:force]
114
+ Process.kill('KILL', pid)
115
+ log "SIGKILL sent to #{name} (#{pid})"
116
+ end
117
+ raise ProcError.new("unable to stop #{name} process") if daemon_running?
118
+ else
119
+ log "#{name} process not running"
120
+ end
121
+ self
122
+ end
123
+
124
+ # restart our service
125
+ def restart
126
+ stop
127
+ start
128
+ end
129
+
130
+ # send a message to a running service and return the response
131
+ def send(*args)
132
+ raise ArgumentError if args.empty?
133
+ message = args.join(' ')
134
+ response = nil
135
+ begin
136
+ client do |sock|
137
+ sock.write message + "\r\n"
138
+ response = sock.gets
139
+ end
140
+ rescue Errno::ECONNREFUSED, Errno::ENOENT
141
+ unless daemon_running?
142
+ abort "#{name} process not running"
143
+ end
144
+ abort "unable to establish connection"
145
+ end
146
+ puts response
147
+ end
148
+
149
+ protected
150
+
151
+ # run a server loop, passing data off to our handler
152
+ def serve
153
+ server do |server|
154
+ log "listening on " + server.local_address.inspect_sockaddr
155
+ while 1
156
+ sock = server.accept
157
+ begin
158
+ # wait for input
159
+ if IO.select([sock], nil, nil, 2.0)
160
+ handle sock
161
+ else
162
+ log "connection timed out"
163
+ end
164
+ rescue Errno::EPIPE, Errno::ECONNRESET
165
+ log "connection broken"
166
+ end
167
+ sock.close unless sock.closed?
168
+ end
169
+ end
170
+ end
171
+
172
+ # return a UNIXServer or TCPServer instance depending on config
173
+ def server(&block)
174
+ if options[:socket]
175
+ UNIXServer.open(options[:socket], &block)
176
+ else
177
+ TCPServer.open(options[:host], options[:port], &block)
178
+ end
179
+ rescue Errno::EACCES
180
+ sock = options[:socket] || "#{options[:host]}:#{options[:port]}"
181
+ raise ProcError, "unable to open socket: #{sock} (check permissions)"
182
+ end
183
+
184
+ # return a UNIXSocket or TCPSocket instance depending on config
185
+ def client(&block)
186
+ if options[:socket]
187
+ UNIXSocket.open(options[:socket], &block)
188
+ else
189
+ TCPSocket.open(options[:host], options[:port], &block)
190
+ end
191
+ rescue Errno::EACCES
192
+ sock = options[:socket] || "#{options[:host]}:#{options[:port]}"
193
+ raise ProcError, "unable to open socket: #{sock} (check permissions)"
194
+ end
195
+
196
+ # handle process termination signals
197
+ def on_interrupt(&block)
198
+ trap("INT") { yield "SIGINT" }
199
+ trap("QUIT") { yield "SIGQUIT" }
200
+ trap("TERM") { yield "SIGTERM" }
201
+ end
202
+
203
+ # daemonize a process. returns true from the forked process, false otherwise
204
+ def daemonize
205
+
206
+ # ensure pid file and log file are writable if provided
207
+ pid_path = options[:pid_path] ? writable_file(options[:pid_path]) : nil
208
+ log_path = options[:log_path] ? writable_file(options[:log_path]) : nil
209
+
210
+ unless fork
211
+ Process.setsid
212
+ exit if fork
213
+ File.umask 0000
214
+ Dir.chdir "/"
215
+
216
+ # save pid file
217
+ File.open(pid_path, 'w') { |f| f.write Process.pid } if pid_path
218
+
219
+ # redirect our io
220
+ setup_logging(log_path)
221
+
222
+ # trap and ignore SIGHUP
223
+ Signal.trap('HUP') {}
224
+
225
+ # trap reopen our log files on SIGUSR1
226
+ Signal.trap('USR1') { setup_logging(log_path) }
227
+
228
+ return true
229
+ end
230
+
231
+ Process.waitpid
232
+ unless wait_until { daemon_running? }
233
+ raise ProcError, "failed to start #{@name} service"
234
+ end
235
+ end
236
+
237
+ # returns the process id if a daemon is running with our pid file
238
+ def daemon_running?(pid = nil)
239
+ pid ||= stored_pid
240
+ Process.kill(0, pid) if pid
241
+ pid
242
+ rescue Errno::ESRCH
243
+ false
244
+ end
245
+
246
+ # reverse of daemon_running?
247
+ def daemon_stopped?(pid = nil)
248
+ !daemon_running? pid
249
+ end
250
+
251
+ # drop privileges to the specified user and group
252
+ def drop_privileges(user, group)
253
+ uid = Etc.getpwnam(user).uid if user
254
+ gid = Etc.getgrnam(group).gid if group
255
+ gid = Etc.getpwnam(user).gid if group.nil? && user
256
+
257
+ Process::Sys.setuid(uid) if uid
258
+ Process::Sys.setgid(gid) if gid
259
+ rescue ArgumentError => e
260
+ # user or group does not exist
261
+ raise ProcError, "unable to drop privileges (#{e})"
262
+ end
263
+
264
+ # redirect our output as per configuration
265
+ def setup_logging(log_path)
266
+ log_path ||= '/dev/null'
267
+ $stdin.reopen '/dev/null'
268
+ $stdout.reopen(log_path, 'a')
269
+ $stderr.reopen $stdout
270
+ $stdout.sync = true
271
+ end
272
+
273
+ # returns the pid stored in our pid_path
274
+ def stored_pid
275
+ return false unless options[:pid_path]
276
+ path = File.expand_path(options[:pid_path])
277
+ return false unless File.file?(path) && !File.zero?(path)
278
+ File.read(path).chomp.to_i
279
+ end
280
+
281
+ # ensure a writable file exists at the specified path
282
+ def writable_file(path)
283
+ path = File.expand_path(path)
284
+ begin
285
+ FileUtils.mkdir_p(File.dirname(path), :mode => 0755)
286
+ FileUtils.touch path
287
+ File.chmod(0644, path)
288
+ rescue Errno::EACCES, Errno::EISDIR
289
+ end
290
+ unless File.file?(path) && File.writable?(path)
291
+ raise ProcError, "unable to open file: #{path} (check permissions)"
292
+ end
293
+ path
294
+ end
295
+
296
+ def wait_until(timer = 5, interval = 0.1, &block)
297
+ until timer < 0 or block.call
298
+ timer -= interval
299
+ sleep interval
300
+ end
301
+ timer > 0
302
+ end
303
+
304
+ def log(message)
305
+ puts Time.now.strftime('%Y-%m-%d %H:%M:%S: ') + message
306
+ end
307
+ end
308
+ end
@@ -0,0 +1,3 @@
1
+ module Sockd
2
+ VERSION = "0.1.0"
3
+ end
data/lib/sockd.rb ADDED
@@ -0,0 +1,31 @@
1
+ require "sockd/errors"
2
+ require "sockd/optparse"
3
+ require "sockd/runner"
4
+ require "sockd/version"
5
+
6
+ module Sockd
7
+
8
+ # instantiate a new sockd service
9
+ def self.define(name, options = {}, &block)
10
+ Runner.define(name, options, &block)
11
+ end
12
+
13
+ # instantiate command line option parser
14
+ def self.optparse(name, defaults = {}, &block)
15
+ OptionParser.new(name, defaults, &block)
16
+ end
17
+
18
+ # instantiate and run a sockd service using command line arguments
19
+ def self.run(name, options = {}, &block)
20
+ parser = optparse(name, options)
21
+ argv = parser.parse!
22
+ define(name, options, &block).run(*argv)
23
+ rescue OptionParserError, BadCommandError => e
24
+ warn "Error: #{e.message}"
25
+ warn "#{parser}\n"
26
+ exit
27
+ rescue SockdError => e
28
+ warn "Error: #{e.message}"
29
+ exit
30
+ end
31
+ end
data/sockd.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sockd/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "sockd"
8
+ s.version = Sockd::VERSION
9
+ s.license = "MIT"
10
+
11
+ s.summary = "A framework for single-threaded ruby socket daemons"
12
+ s.description = "Sockd makes it easy to create a single-threaded daemon which can listen on a TCP or Unix socket and respond to commands"
13
+
14
+ s.authors = ["Mike Greiling"]
15
+ s.email = "mike@pixelcog.com"
16
+ s.homepage = "http://pixelcog.com/"
17
+
18
+ s.files = `git ls-files -z`.split("\x0")
19
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_development_dependency "bundler", "~> 1.7"
23
+ s.add_development_dependency "rake", "~> 10.0"
24
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sockd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Mike Greiling
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: Sockd makes it easy to create a single-threaded daemon which can listen
42
+ on a TCP or Unix socket and respond to commands
43
+ email: mike@pixelcog.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - .gitignore
49
+ - .vagrant/machines/default/virtualbox/action_provision
50
+ - .vagrant/machines/default/virtualbox/action_set_name
51
+ - .vagrant/machines/default/virtualbox/id
52
+ - .vagrant/machines/default/virtualbox/index_uuid
53
+ - .vagrant/machines/default/virtualbox/private_key
54
+ - .vagrant/machines/default/virtualbox/synced_folders
55
+ - Gemfile
56
+ - LICENSE
57
+ - README.md
58
+ - Rakefile
59
+ - Vagrantfile
60
+ - lib/sockd.rb
61
+ - lib/sockd/errors.rb
62
+ - lib/sockd/optparse.rb
63
+ - lib/sockd/runner.rb
64
+ - lib/sockd/version.rb
65
+ - sockd.gemspec
66
+ homepage: http://pixelcog.com/
67
+ licenses:
68
+ - MIT
69
+ metadata: {}
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubyforge_project:
86
+ rubygems_version: 2.0.14
87
+ signing_key:
88
+ specification_version: 4
89
+ summary: A framework for single-threaded ruby socket daemons
90
+ test_files: []