sockd 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []