net-server 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: befb3239d96b9cb8652bd568665eb15fe7b17d06
4
+ data.tar.gz: 9c7ae1db07ae3a5ce071ec49c346b02bb8d659ff
5
+ SHA512:
6
+ metadata.gz: a331a3bf1acb62e4d1eb820a642e1195376923ff724cae5abdef51f057737780573585f1f90c163ae1656f40920a488cda7c92c170aea04b1d667d96898881f4
7
+ data.tar.gz: 22c82197fa151e9aaa114b44f199d70de7c59e64ed3a45b6d8103999064524f594ba6f0ba7407e12d5bc4fe56e87002a870ddf08066420d02c3c4c57d782ab62
@@ -0,0 +1,2 @@
1
+ # v1.0
2
+ This is the initial load of the gem. This gem was taken from an earlier work of mine, so as far as I know, it is bug-free (but never count on code being bug free). If you find a problem, report it to me at mjwelchphd@gmail.com, or FORK the library, fix the bug, then add a Pull Request.
@@ -0,0 +1,158 @@
1
+ # Net::Server
2
+ The Net::Server Ruby gem is a server for web apps. It's purpose is to establish one or more listeners, and create a process to handle each incoming request.
3
+
4
+ #### It has the following features:
5
+ 1. It can listen on any number of ports simultaneously.
6
+ 2. It starts a separate receiver process to handle each connection.
7
+ 3. The server may run as root, but the processes will lose their root privileges soon after creation. This is a security feature.
8
+ 4. The receiver processes can switch on full encryption (STARTTLS in a mail server, for example).
9
+ 5. A log file will be used, if it exists. You create the log using the 'logger' gem.
10
+ 7. Runs until terminated by a `KILL -INT <pid>` or `^C`.
11
+ 9. Two example programs are included.
12
+
13
+ ### TODO
14
+ * RSPEC tests.
15
+
16
+ # Gem Dependancies
17
+ This gem requires the following:
18
+ ```ruby
19
+ require 'openssl'
20
+ require 'socket'
21
+ ```
22
+ Both of these packages are found in the Ruby Standard Library (stdib 2.2.2 at the time of this writing). They are required in the gem itself, so you don't have to require them.
23
+
24
+ # Creating a Self-Signed Certificate
25
+ Use OpenSSL to create a self-signed certificate for testing as follows:
26
+ ```bash
27
+ $ openssl req -x509 -newkey rsa:2048 -keyout example.key -out example.crt -days 9000 -nodes
28
+ $ chmod 400 example.key
29
+ $ chmod 444 example.crt
30
+ ```
31
+ Put the two files anywhere you want, and specify their path in the start call (see the last line of the example programs). If you don't specify these two files, they will default to the ones that come with the gem, but since anyone can download the gem and look at the files, they are not secure.
32
+
33
+ # How to Get Net::Server Gem
34
+
35
+ To install the gem, simply use the *gem* application:
36
+ ```bash
37
+ $ sudo gem install net-server
38
+ ```
39
+ Alternately, you can clone the project on GitHub at:
40
+ ```bash
41
+ https://github.com/mjwelchphd/net-server
42
+ ```
43
+ and build it yourself.
44
+
45
+ The example programs are only found in the source on GitHub at github.com/mjwelchphd/net-server.
46
+
47
+ If you're using Builder, follow your normal routine for adding gems.
48
+
49
+ # How to Build a Basic Server
50
+
51
+ The basic server looks like this:
52
+ ```ruby
53
+ require 'net/server'
54
+
55
+ class Receiver
56
+
57
+ def receive(local_port, local_hostname, remote_port, remote_hostname, remote_ip)
58
+ <send and receive data>
59
+ end
60
+
61
+ end
62
+
63
+ LOG = Logger::new(<log-file-path>, <log-file-name>)
64
+
65
+ Net::Server.new(<options>).start
66
+ ```
67
+ There are two test applications included in the gem in the examples folder. Test1 just echos a telnet connection. Test2 implements a simple email receiver you can test with swaks.
68
+
69
+ ## Options
70
+
71
+ Option | Default | Description
72
+ --- | --- | ---
73
+ :server_name | "example.com" | This name is only used in error messages.
74
+ :listening_ports | ["25","486","587"] | An array of one or more ports to listen on.
75
+ :private_key | Internal key | The key for encrypting/decrypting the data when in TLS mode.
76
+ :certificate | Internal self-signed certificate | The certificate for encrypting/decrypting the data when in TLS mode. This may be your own self-signed certificate, or one you purchase from a Certificate Authority, or you can become a Certificate Authority and sign your own.
77
+ :user_name | nil | This name is the user name to which each process will be switched after it is created. If it is nil, the ownership of the process will not be changed after creation. If you are using a port less than 1024, you must start the server as root, and the user name and group name of the process _must be_ specified.
78
+ :group_name | nil | This name is the group name to which each process will be switched after it is created.
79
+ :working_directory | the current path | The location of the program running the server.
80
+ :pid_file | "pid" | The PID of the server will be stored in this file.
81
+ :daemon | false | If this option is true, the server will be started as a daemon.
82
+
83
+ ## Logging
84
+
85
+ The log file must be created by the 'logger' gem, and must be named LOG.
86
+
87
+ ## Terminating the Server
88
+
89
+ ### HUP and INT (^C) traps
90
+
91
+ A `kill -INT <pid>` or `<ctrl-C>` will terminate the server.
92
+ ```bash
93
+ $ test1.rb
94
+ ^C
95
+ test1 terminated by admin ^C
96
+ $
97
+ ```
98
+ or
99
+ ```bash
100
+ sudo kill -INT `cat /run/net-server/net-server.pid`
101
+ ```
102
+
103
+ A `kill -HUP <pid>` will activate a restart method, if you have one defined in your code. For example, if you put this in your code:
104
+ ```ruby
105
+ class Server
106
+ def restart
107
+ puts "I just got a HUP request."
108
+ end
109
+ end
110
+ ```
111
+ then at another terminal enter:
112
+ ```ruby
113
+ $ ps ax | grep test1
114
+ 823 pts/0 Sl+ 0:00 test1.rb
115
+ 829 pts/1 S+ 0:00 grep --color=auto test1
116
+ $ kill -hup 823
117
+ ```
118
+ or
119
+ ```bash
120
+ kill -HUP `cat pid`
121
+ ```
122
+ it will result in:
123
+ ```bash
124
+ net-server received a HUP request
125
+ I just got a HUP request.
126
+ ```
127
+ at the terminal where `test1.rb` is running, with no other action. The first message comes from `net-server` itself, and the second comes from the `def restart` you defined.
128
+
129
+ #### Example
130
+
131
+ If you define your options in a class variable Hash object, say @options, if the values change, the new values take effect immediately, except for :daemon which requires a manual restart. For instance,
132
+
133
+ ```ruby
134
+ @options = {
135
+ :server_name=>"www.example.com",
136
+ :private_key=>"server.key",
137
+ :certificate=>"server.crt",
138
+ :listening_ports=>['2000','2001']
139
+ :user_name=>"myusername",
140
+ :group_name=>"mygroupname",
141
+ :daemon = true
142
+ }
143
+ Net::Server::new(@options).start
144
+ ```
145
+ starts the server, then
146
+ ```ruby
147
+ class Server
148
+ def restart
149
+ @options = {
150
+ :user_name=>"yourusername",
151
+ :group_name=>"yourgroupname"}
152
+ end
153
+ end
154
+ ```
155
+ will change the user name and group name for all new processes when the HUP is received.
156
+
157
+
158
+ FIN
@@ -0,0 +1,17 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIICqDCCAZACCQDi3G+g2cQKIjANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDDAtl
3
+ eGFtcGxlLmNvbTAeFw0xNjA5MTIwMzIxMTFaFw00NDAxMjgwMzIxMTFaMBYxFDAS
4
+ BgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
5
+ AQEAyjrhith3b09fjtDIFm+PO4MvyjN75Q5FyNJrB7Nwbtrs0U+cM+LqUa7IogV5
6
+ LNXNOq1NxX3b8AmrZ3PRJ0OU+SWcMQcQLWb027R2agHHH4M7haYtpZh33u7+cLo9
7
+ sCwm7d2S9ShgFPNVeIMuEjbwXKj1beHTYbljeEFULgxsqrvUJWm1A+WgPFc/9QyX
8
+ os7gv9VG0//qlCAY91QEx7LGN+XEhzSzfOHnAwYeB8qYKbf7CRK4hua2zoCZQB0d
9
+ o4IHm+aS6HSSsnTQ/JHWegjy/yyJAenseXRdmz1O4yj+nw5yFocz7TbqgQm5jqgV
10
+ acEmX2b6tiuOlwc9Hm9x6Z0BLQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCyCw/H
11
+ XPxV1P6WVZFgQ65/IKZoJY6JSZ88ILw0jzN/B+/ItfBk250n1VoDv9sNXeDg6Sym
12
+ cNlJlS5nwAzVE+drELu6xMbAOX0+y4KHhhYTrl+8CnXWXVVGsUQhHu8GljfMbM+s
13
+ VUNrhWO8XRrQ4u4wqnh1kR1pe2xUhuV3dkespWI4LRjNnsEmPw3Rg5BkY8VQRKSR
14
+ Srif5BlisnwE1WDefaIiE7Ydgs93o1FxZchvBnlHrtxnCG+bXIlO29fRdX8rUpH/
15
+ zgONQ97oJHZ4N29AEYO8dxnEt8e9eioeQU+xeU8HRw36F+JajiqhCOwgpz7Vv4rn
16
+ HmueeHzDxJaGD+xv
17
+ -----END CERTIFICATE-----
@@ -0,0 +1,27 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIIEpAIBAAKCAQEAyjrhith3b09fjtDIFm+PO4MvyjN75Q5FyNJrB7Nwbtrs0U+c
3
+ M+LqUa7IogV5LNXNOq1NxX3b8AmrZ3PRJ0OU+SWcMQcQLWb027R2agHHH4M7haYt
4
+ pZh33u7+cLo9sCwm7d2S9ShgFPNVeIMuEjbwXKj1beHTYbljeEFULgxsqrvUJWm1
5
+ A+WgPFc/9QyXos7gv9VG0//qlCAY91QEx7LGN+XEhzSzfOHnAwYeB8qYKbf7CRK4
6
+ hua2zoCZQB0do4IHm+aS6HSSsnTQ/JHWegjy/yyJAenseXRdmz1O4yj+nw5yFocz
7
+ 7TbqgQm5jqgVacEmX2b6tiuOlwc9Hm9x6Z0BLQIDAQABAoIBAEfEoao+rBQqnQT3
8
+ /ypHKRO7uMgPgVaGvClK04UGH06YDxcxI4QecX3Pg5BMVPaeYZkDS+hchCMpq1Sp
9
+ e35ts36/5DBaC1mxghA+eQ+h1eLPFd6WXPi5pUDOnCmxTpco9B/SVIcAbyjAOoLs
10
+ Ovtn1+FqbL80N2ok+rIArTkyW5YzNNT84P5nMLGL5PvA40F/dGhkIUutX73F8jvU
11
+ th3OLyf2MTYKfq7J79z3CGrClZmdcD70h5oKcVH0xFISODqwW9+NUuCaZke0LLPm
12
+ TLQlFdKxfu5rVoBW6DGsK+7q4LSG41MkXTyTi2yIW70ls++KakJi4QcYetX/XPTa
13
+ Xd1YkhkCgYEA5Gqj7wLIpvdtyIbM08xF7eUhuXxx42OHAvirFu/l9WipTRBvoLg2
14
+ CCOWAr/1UyWDeIJAEOjsplKEN0NH1Yx9+jgXbzEU1rVtW8+Oyd64/FJqjICZPatp
15
+ DBTxyPy1849PcyhlFHMiPiaP8+bcuy8HVeLDd27gujjJDtNYL9mAxBcCgYEA4qay
16
+ 86WqUCZqjOXrEPh4/VWqT0lvvkoBaOx3ScoapXZlKFJAf1lNd7pEvvTZ/6X5XCbr
17
+ 8VA+wUB1kQAKjVBPxideIyUOKboJJfgJDWqTaiPtOdCbO9+pcEsu/1Ep+64prU1e
18
+ dX85McHKGRJZEPQOqIXT+M+m6iJ5Dr63KMtKO1sCgYEA5EJ9WFhb1B7nIsEy52T3
19
+ bOjRbt8hoK7ROPLvZpiOIVRZ+501MFNmC3QkcNMLge+3FjJze3KJKxzC68bcfldL
20
+ fUWYhZFy1a2wf1NHygw8qEpkF8xbVvl4nI5BoSyJV8AbEWWIvYrg6WL38DEA3D3/
21
+ AqD93Nh80xv0MFCbjBW7TDkCgYA5MOks89ui996hCQ8krB4Thr8/3H723EO4zxpW
22
+ o0nQAK0L3J1rxQP4NydLrAsqKB5g821L6fy71OEVopYHDIHktWBaq+oD+259hzX5
23
+ ja/+82vTz+Cr3gcqT75fLILUgCECGui/60dqV8UASJHy5jKgsYxIV1V99Q7o+pTL
24
+ FWj4ywKBgQCtJ/xOSowLSdEYGRq//iNKaBSAutaV26J5WVxxev3wUehZpvRGtqI5
25
+ CiddA8vlR0EiA/SINQ0MZtk9kwvWYVq/jkWGttK6gwU26ZDUR7xUOMs17m3ttUn7
26
+ mjkuyOITFCRxf0x0LcoFQWHlc5lC6Y/ZKF3dK6jPD74B5/UUeQ8sCA==
27
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,237 @@
1
+ # = net/server.rb
2
+ #
3
+ # Copyright (c) 2016 Michael J. Welch, Ph.D.
4
+ #
5
+ # Written and maintained by Michael J. Welch, Ph.D. <mjwelchphd@gmail.com>
6
+ #
7
+ # This work is not derived from any other work. It is original software.
8
+ #
9
+ # Documented by Michael J. Welch, Ph.D. <mjwelchphd@gmail.com>
10
+ #
11
+ # This program is free software. You can re-distribute and/or
12
+ # modify this program under the same terms as Ruby itself.
13
+ #
14
+ # See the README.md for documentation.
15
+ #
16
+
17
+ require 'openssl'
18
+ require 'socket'
19
+
20
+ module Net
21
+
22
+ # :stopdoc:
23
+ class ServerTerminate < Exception; end
24
+ class ServerQuit < Exception; end
25
+ # :startdoc:
26
+
27
+ # == An Internet server
28
+ class Server
29
+
30
+ private
31
+
32
+ def initialize(options={})
33
+ path = __FILE__.chomp('server.rb')
34
+ @option_list = [[:server_name, "example.com"], [:listening_ports, ["25","486","587"]], \
35
+ [:private_key, "#{path}server.key"], [:certificate, "#{path}server.crt"], \
36
+ [:user_name, nil], [:group_name, nil], [:working_directory, File::realpath('.')], \
37
+ [:pid_file, "pid"], [:daemon, false]]
38
+ @options = {}
39
+ @option_list.each do |key,value|
40
+ @options[key] = if options.has_key?(key) then options[key] else value end
41
+ end
42
+ end # initialize
43
+
44
+ include Socket::Constants
45
+
46
+ #
47
+ # This is the code executed after the process has been
48
+ # forked and root privileges have been dropped.
49
+ #
50
+ def process_call(connection, local_port, remote_port, remote_ip, remote_hostname, remote_service)
51
+ begin
52
+ Signal.trap("INT") { } # ignore ^C in the child process
53
+ LOG.info("%06d"%Process::pid) {"Connection accepted on port #{local_port} from port #{remote_port} at #{remote_ip} (#{remote_hostname})"} if LOG
54
+
55
+ # a new object is created here to provide separation between server and receiver
56
+ # this call receives the email and does basic validation
57
+ Receiver::new(connection).receive(local_port, Socket::gethostname, remote_port, remote_hostname, remote_ip)
58
+ rescue ServerQuit
59
+ # nothing to do here
60
+ end
61
+ end # process_call
62
+
63
+ #
64
+ # This method drops the process's root privileges for security reasons.
65
+ #
66
+ def drop_root_privileges(user_name, group_name, working_directory)
67
+ # drop root privileges
68
+ if Process::Sys.getuid==0
69
+ user = Etc::getpwnam(user_name)
70
+ group = Etc::getgrnam(group_name)
71
+ Dir.chdir(user.dir)
72
+ Dir.chdir(working_directory) if not working_directory.nil?
73
+ Process::GID.change_privilege(group.gid)
74
+ Process::UID.change_privilege(user.uid)
75
+ end
76
+ end # drop_root_privileges
77
+
78
+ #
79
+ # both the AF_INET and AF_INET6 families use this DRY method
80
+ # to bind to the socket.
81
+ #
82
+ def bind_socket(family,port,ip)
83
+ socket = Socket.new(family, SOCK_STREAM, 0)
84
+ sockaddr = Socket.sockaddr_in(port.to_i,ip)
85
+ socket.setsockopt(:SOCKET, :REUSEADDR, true)
86
+ socket.bind(sockaddr)
87
+ socket.listen(0)
88
+ return socket
89
+ end # bind_socket
90
+
91
+ #
92
+ # The listening thread is established in this method depending on the ListenPort
93
+ # argument passed to it -- it can be '<ipv6>/<port>', '<ipv4>:<port>', or just '<port>'.
94
+ #
95
+ def listening_thread(local_port)
96
+ LOG.info("%06d"%Process::pid) {"listening on port #{local_port}..."} if LOG
97
+
98
+ # establish an SSL context
99
+ $ctx = OpenSSL::SSL::SSLContext.new
100
+ $ctx.key = $prv
101
+ $ctx.cert = $crt
102
+
103
+ # check the parameter to see if it's valid
104
+ m = /^(([0-9a-fA-F]{0,4}:{0,1}){1,8})\/([0-9]{1,5})|(([0-9]{1,3}\.{0,1}){4}):([0-9]{1,5})|([0-9]{1,5})$/.match(local_port)
105
+ #<MatchData "2001:4800:7817:104:be76:4eff:fe05:3b18/2000" 1:"2001:4800:7817:104:be76:4eff:fe05:3b18" 2:"3b18" 3:"2000" 4:nil 5:nil 6:nil 7:nil>
106
+ #<MatchData "23.253.107.107:2000" 1:nil 2:nil 3:nil 4:"23.253.107.107" 5:"107" 6:"2000" 7:nil>
107
+ #<MatchData "2000" 1:nil 2:nil 3:nil 4:nil 5:nil 6:nil 7:"2000">
108
+ case
109
+ when !m[1].nil? # it's AF_INET6
110
+ socket = bind_socket(AF_INET6,m[3],m[1])
111
+ when !m[4].nil? # it's AF_INET
112
+ socket = bind_socket(AF_INET,m[6],m[4])
113
+ when !m[7].nil?
114
+ socket = bind_socket(AF_INET6,m[7],"0:0:0:0:0:0:0:0")
115
+ else
116
+ raise ArgumentError.new(local_port)
117
+ end # case
118
+ ssl_server = OpenSSL::SSL::SSLServer.new(socket, $ctx);
119
+
120
+ # main listening loop starts in non-encrypted mode
121
+ ssl_server.start_immediately = false
122
+ loop do
123
+ # we can't use threads because if we drop root privileges on any thread,
124
+ # they will be dropped for all threads in the process--so we have to fork
125
+ # a process here in order that the reception be able to drop root privileges
126
+ # and run at a user level--this is a security precaution--the other reason
127
+ # to use processes is that they can be run on multiple processors
128
+ connection = ssl_server.accept
129
+ Process::fork do
130
+ # now we're in the child process
131
+ begin
132
+ drop_root_privileges(@options[:user_name],@options[:group_name],@options[:working_directory]) if !@options[:user_name].nil?
133
+ remote_hostname, remote_service = connection.io.remote_address.getnameinfo
134
+ remote_ip, remote_port = connection.io.remote_address.ip_unpack
135
+ process_call(connection, local_port, remote_port.to_s, remote_ip, remote_hostname, remote_service)
136
+ ensure
137
+ # here we close the child's copy of the connection --
138
+ # since the parent already closed it's copy, this
139
+ # one will send a FIN to the client, so the client
140
+ # can terminate gracefully
141
+ connection.close
142
+ LOG.info("%06d"%Process::pid) {"Connection closed on port #{local_port} by #{@options[:server_name]}"} if LOG
143
+ # and finally, close the child's link to the log
144
+ LOG.close if LOG
145
+ end
146
+ # the child process ends here
147
+ end # fork
148
+ # now we're in the parent process
149
+ # here we close the parent's copy of the connection --
150
+ # the child (created by the Process::fork above) has another copy --
151
+ # if this one is not closed, when the child closes it's copy,
152
+ # the child's copy won't send a FIN to the client -- the FIN
153
+ # is only sent when the last process holding a copy to the
154
+ # socket closes it's copy
155
+ connection.close
156
+ end # loop
157
+ end # listening_thread
158
+
159
+ public
160
+
161
+ #
162
+ # This is the main setup and loop.
163
+ #
164
+ def start
165
+ # generate the first log messages
166
+ LOG.info("%06d"%Process::pid) {"Starting RubyMTA at #{Time.now.strftime("%Y-%m-%d %H:%M:%S %Z")}, pid=#{Process::pid}"} if LOG
167
+ LOG.info("%06d"%Process::pid) {"Options specified: #{ARGV.join(", ")}"} if LOG && ARGV.size>0
168
+
169
+ # get the certificates, if any; they're needed for STARTTLS
170
+ # we do this before daemonizing because the working folder might change
171
+ $prv = if @options[:private_key] then OpenSSL::PKey::RSA.new File.read(@options[:private_key]) else nil end
172
+ $crt = if @options[:certificate] then OpenSSL::X509::Certificate.new File.read(@options[:certificate]) else nil end
173
+
174
+ # daemonize it if the option was set--it doesn't have to be root to daemonize it
175
+ Process::daemon if @options[:daemon]
176
+
177
+ # get the process ID and the user id AFTER demonizing, if that was requested
178
+ pid = Process::pid
179
+ uid = Process::Sys.getuid
180
+ gid = Process::Sys.getgid
181
+
182
+ LOG.info("%06d"%Process::pid) {"Daemonized at #{Time.now.strftime("%Y-%m-%d %H:%M:%S %Z")}, pid=#{pid}, uid=#{uid}, gid=#{gid}"} if LOG && @options[:daemon]
183
+
184
+ # store the pid of the server session
185
+ begin
186
+ puts "RubyMTA running as PID=>#{pid}, UID=>#{uid}, GID=>#{gid}"
187
+ File.open(@options[:pid_file],"w") { |f| f.write(pid.to_s) }
188
+ rescue Errno::EACCES => e
189
+ LOG.warn("%06d"%Process::pid) {"The pid couldn't be written. To save the pid, create a directory for '#{@options[:pid_file]}' with r/w permissions for this user."} if LOG
190
+ LOG.warn("%06d"%Process::pid) {"Proceeding without writing the pid."} if LOG
191
+ end
192
+
193
+ # if ssltransportagent was started as root, make sure UserName and
194
+ # GroupName have values because we have to drop root privileges
195
+ # after we fork a process for the receiver
196
+ if uid==0 # it's root
197
+ if @options[:user_name].nil? || @options[:group_name].nil?
198
+ LOG.error("%06d"%Process::pid) {"ssltransportagent can't be started as root unless UserName and GroupName are set."} if LOG
199
+ exit(1)
200
+ end
201
+ end
202
+
203
+ # this is the main loop which runs until admin enters ^C
204
+ Signal.trap("INT") { raise ServerTerminate.new }
205
+ Signal.trap("HUP") { restart if defined?(restart) }
206
+ Signal.trap("CHLD") do
207
+ begin
208
+ Process.wait(-1, Process::WNOHANG)
209
+ rescue Errno::ECHILD => e
210
+ # ignore the error
211
+ end
212
+ end # trap-chld
213
+ threads = []
214
+ # start the server on multiple ports (the usual case)
215
+ begin
216
+ @options[:listening_ports].each do |port|
217
+ threads << Thread.start(port) do |port|
218
+ listening_thread(port)
219
+ end
220
+ end
221
+ # the joins are done ONLY after all threads are started
222
+ threads.each { |thread| thread.join }
223
+ rescue ServerTerminate
224
+ LOG.info("%06d"%Process::pid) {"#{@options[:server_name]} terminated by admin ^C"} if LOG
225
+ end
226
+
227
+ # attempt to remove the pid file
228
+ begin
229
+ File.delete(@options[:pid_file])
230
+ rescue Errno::ENOENT => e
231
+ LOG.warn("%06d"%Process::pid) {"No such file: #{e.inspect}"} if LOG
232
+ rescue Errno::EACCES, Errno::EPERM
233
+ LOG.warn("%06d"%Process::pid) {"Permission denied: #{e.inspect}"} if LOG
234
+ end
235
+ end # start
236
+ end
237
+ end
@@ -0,0 +1,4 @@
1
+ module Net
2
+ BUILD_VERSION = "1.0.0"
3
+ BUILD_DATE = "2015-09-13"
4
+ end
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: net-server
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Michael J. Welch, Ph.D.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-09-13 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Ruby Net server.
14
+ email:
15
+ - mjwelchphd@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - CHANGELOG.md
21
+ - README.md
22
+ - lib/net/server.crt
23
+ - lib/net/server.key
24
+ - lib/net/server.rb
25
+ - lib/net/version.rb
26
+ homepage: https://github.com/mjwelchphd/net-server
27
+ licenses:
28
+ - MIT
29
+ metadata: {}
30
+ post_install_message:
31
+ rdoc_options: []
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ required_rubygems_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubyforge_project:
46
+ rubygems_version: 2.4.6
47
+ signing_key:
48
+ specification_version: 4
49
+ summary: Ruby Net server.
50
+ test_files: []