postfix_daemon 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
+ SHA256:
3
+ metadata.gz: 223491e9a76a10604afa95773510d3a217345f67d33839d4a2accef7c8202d67
4
+ data.tar.gz: 5ae079ffdb492bc9fb52c80e793cafcb1ceedb956237b1418b05dcfde93c967a
5
+ SHA512:
6
+ metadata.gz: 3cd7e3a6715ecb3f6efec02901c1835453b7763c4e9df0c5522eb0e04a1d3a37a6bd45aff22ae051278fe684b3c6000a12d981b77a141d2ed47f5aaca4434ee0
7
+ data.tar.gz: 5e6076d6dd6cc709ba2417569a581c6e7b95f83c269c8daa1bbeb0259c20db4e9ede45ff68d3e038f2b8e0267bdf661b0bfefb48b148802455ce3d5a6fe2f87d
data/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # PostfixDaemon
2
+
3
+ Postfixのデーモンプログラムを作るためのライブラリです。
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'postfix_daemon'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install postfix_daemon
20
+
21
+ ## Usage
22
+
23
+ プログラムを書きます。
24
+
25
+ 入力を大文字に変換して返すプログラムの例:
26
+
27
+ ```ruby
28
+ #!/path/to/ruby
29
+
30
+ require 'postfix_daemon'
31
+
32
+ # エラー終了してもどこにも出力されないのでリダイレクトしといた方がよさそう
33
+ $stderr.reopen("/tmp/error.log", "a+")
34
+
35
+ PostfixDaemon.start do |socket, addr|
36
+ socket.puts "you are #{addr.inspect}"
37
+ while s = socket.gets
38
+ s = s.force_encoding("utf-8").scrub
39
+ socket.puts s.upcase
40
+ end
41
+ end
42
+ ```
43
+
44
+ プログラムを /usr/lib/postfix/sbin に置きます。
45
+
46
+ ```
47
+ # cp hoge.rb /usr/lib/postfix/sbin/hoge
48
+ # chmod +x /usr/lib/postfix/sbin/hoge
49
+ ```
50
+
51
+ master.cf に追加:
52
+
53
+ ```
54
+ 12345 inet - - - - - hoge
55
+ ```
56
+
57
+ postfix reload
58
+
59
+ ```
60
+ # postfix reload
61
+ ```
62
+
63
+ ```
64
+ % nc localhost 12345
65
+ you are #<Addrinfo: 127.0.0.1:59312 TCP>
66
+ abcdefg
67
+ ABCDEFG
68
+ ^C
69
+ ```
@@ -0,0 +1,137 @@
1
+ require "postfix_daemon/version"
2
+
3
+ require 'optparse'
4
+ require 'socket'
5
+ require 'syslog'
6
+
7
+ # Postfix daemon program
8
+ class PostfixDaemon
9
+ DEFAULT_MAX_IDLE = 100
10
+ DEFAULT_MAX_USE = 100
11
+
12
+ MASTER_STATUS_FD = 5
13
+ MASTER_LISTEN_FD = 6
14
+
15
+ # @param args [Array<String>] command line arguments
16
+ # @yield [socket, addr]
17
+ # @yieldparam socket [Socket]
18
+ # @yieldparam addr [Addrinfo]
19
+ def self.start(args=ARGV, &block)
20
+ self.new(args).run(&block)
21
+ end
22
+
23
+ # @param args [Array<String>] command line arguments
24
+ # @param setup [Proc] called on starting
25
+ # @param service [Proc] called when client connect
26
+ def initialize(args, setup: nil, service: nil)
27
+ @args = args.dup
28
+ @setup = setup
29
+ @service = service
30
+ @nsocks = nil
31
+ @max_idle = nil
32
+ @max_use = nil
33
+ end
34
+
35
+ # @yield [socket, addr]
36
+ # @yieldparam socket [Socket]
37
+ # @yieldparam addr [Addrinfo]
38
+ def run(&block)
39
+ parse_args
40
+ setup
41
+ @setup.call if @setup
42
+ @service = block if block
43
+ raise 'block required' unless @service
44
+ main
45
+ end
46
+
47
+ private
48
+
49
+ # general Postfix server options:
50
+ # -c master.cf chroot is y, so this process should chroot to queue_directory.
51
+ # -d not daemon mode
52
+ # -D debug mode
53
+ # -i # set max_idle=#
54
+ # -l master.cf maxproc is 1
55
+ # -m # set max_use=#
56
+ # -n name master.cf service name
57
+ # -o param=value param=value
58
+ # -s # number of server sockets
59
+ # -S stdin stream mode
60
+ # -t type master.cf service type (inet, unix, pass)
61
+ # -u master.cf unpriv is y, so this process should setuid to mail_owner.
62
+ # -v verbose mode
63
+ # -V message to stderr
64
+ # -z master.cf maxproc is 0
65
+ #
66
+ # This library support only '-s', '-i', '-m' option.
67
+ def parse_args
68
+ @args.extend OptionParser::Arguable
69
+ opts = @args.getopts('cdDi:lm:n:o:s:St:uvVz')
70
+ @nsocks = opts['s'] ? opts['s'].to_i : 1
71
+ @max_idle = opts['i'] ? opts['i'].to_i : DEFAULT_MAX_IDLE
72
+ @max_use = opts['m'] ? opts['m'].to_i : DEFAULT_MAX_USE
73
+ $VERBOSE = true if opts['v']
74
+ end
75
+
76
+ def setup
77
+ Syslog.open(File.basename($0), nil, Syslog::LOG_MAIL) unless Syslog.opened?
78
+ @socks = []
79
+ fd = MASTER_LISTEN_FD
80
+ @nsocks.times do
81
+ @socks.push Socket.for_fd(fd)
82
+ fd += 1
83
+ end
84
+ @stat_fd = IO.for_fd(MASTER_STATUS_FD)
85
+ @generation = ENV['GENERATION'].to_i(8)
86
+ end
87
+
88
+ def log(message)
89
+ Syslog.log(Syslog::LOG_INFO, message) if $VERBOSE
90
+ end
91
+
92
+ def main
93
+ used_count = 0
94
+ while used_count < @max_use
95
+ sock, addr = accept
96
+ break unless sock
97
+ to_master(0) or break
98
+ used_count += 1
99
+ begin
100
+ @service.call(sock, addr)
101
+ ensure
102
+ sock.close rescue nil
103
+ to_master(1) or break
104
+ end
105
+ end
106
+ rescue => e
107
+ log "#{e.class}: #{e.message}"
108
+ raise e
109
+ end
110
+
111
+ # @return [Socket, Addrinfo]
112
+ def accept
113
+ while true
114
+ rs, = select(@socks + [@stat_fd], nil, nil, @max_idle)
115
+ unless rs
116
+ log 'idle timeout'
117
+ return nil
118
+ end
119
+ if rs.include? @stat_fd
120
+ log 'master disconnect'
121
+ return nil
122
+ end
123
+ sock, addr = rs.first.accept_nonblock(exception: false)
124
+ return sock, addr unless sock == :wait_readable
125
+ end
126
+ end
127
+
128
+ # @param x [Integer]
129
+ # @return [Boolean]
130
+ def to_master(x)
131
+ @stat_fd.syswrite [Process.pid, @generation, x].pack('iIi')
132
+ return true
133
+ rescue Errno::EPIPE
134
+ log 'master disconnect'
135
+ return false
136
+ end
137
+ end
@@ -0,0 +1,3 @@
1
+ class PostfixDaemon
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ require 'postfix_daemon'
3
+
4
+ PostfixDaemon.start do |socket|
5
+ while s = socket.gets
6
+ socket.puts s.upcase
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: postfix_daemon
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - TOMITA Masahiro
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-03-05 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.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
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
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: Postfix daemon
56
+ email:
57
+ - tommy@tmtm.org
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - README.md
63
+ - lib/postfix_daemon.rb
64
+ - lib/postfix_daemon/version.rb
65
+ - sample/postfix_daemon-sample.rb
66
+ homepage: https://github.com/tmtm/postfix_daemon
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.7.4
87
+ signing_key:
88
+ specification_version: 4
89
+ summary: Postfix daemon
90
+ test_files: []