foreground 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.
- data/.gitignore +19 -0
- data/Gemfile +4 -0
- data/Guardfile +13 -0
- data/LICENSE.txt +22 -0
- data/README.md +48 -0
- data/Rakefile +14 -0
- data/features/sample_daemon.feature +19 -0
- data/features/start_stop.feature +12 -0
- data/features/steps/sample_daemon_steps.rb +44 -0
- data/features/support/env.rb +5 -0
- data/foreground.gemspec +28 -0
- data/lib/foreground.rb +8 -0
- data/lib/foreground/cli.rb +41 -0
- data/lib/foreground/version.rb +3 -0
- data/script/bootstrap +2 -0
- data/spec/foreground/cli_spec.rb +38 -0
- data/spec/foreground/daemon_spec.rb +0 -0
- data/spec/spec_helper.rb +1 -0
- metadata +190 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
guard 'rspec', :version => 2, :cli => '--color' do
|
2
|
+
watch(%r{^spec/.+_spec\.rb$})
|
3
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
4
|
+
watch('spec/spec_helper.rb') { "spec" }
|
5
|
+
end
|
6
|
+
|
7
|
+
guard 'cucumber' do
|
8
|
+
watch(%r{^bin/.+$}) { 'features' }
|
9
|
+
watch(%r{^lib/.+$}) { 'features' }
|
10
|
+
watch(%r{^features/.+\.feature$})
|
11
|
+
watch(%r{^features/support/.+$}) { 'features' }
|
12
|
+
watch(%r{^features/steps/.+$}) { 'features' }
|
13
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Björn Albers
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# foreground
|
2
|
+
|
3
|
+
Control daemonizing background processes with launchd.
|
4
|
+
|
5
|
+
## Introduction
|
6
|
+
|
7
|
+
Some processes do unspeakable things, which makes it hard for launchd to
|
8
|
+
control them:
|
9
|
+
|
10
|
+
> A daemon or agent launched by launchd MUST NOT do the following in the process directly launched by launchd:
|
11
|
+
>
|
12
|
+
> * Call daemon(3).
|
13
|
+
> * Do the moral equivalent of daemon(3) by calling fork(2) and have the parent process exit(3) or _exit(2).
|
14
|
+
>
|
15
|
+
> -- [launchd.plist(5)](https://developer.apple.com/library/mac/#documentation/darwin/reference/manpages/man5/launchd.plist.5.html)
|
16
|
+
|
17
|
+
This tiny wrapper makes sure, that those processes are properly started
|
18
|
+
or stopped on behalf of launchd by mirroring the job's running state.
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
Install it via RubyGems:
|
23
|
+
|
24
|
+
$ gem install foreground
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
Wrap your daemon command inside your launchd.plist:
|
29
|
+
|
30
|
+
foreground --pid_file /tmp/foreground_sample_daemon.pid foreground_sample_daemon
|
31
|
+
|
32
|
+
## Limitations
|
33
|
+
|
34
|
+
You can't control the daemons stdout, stderr, etc. this way, because the daemon will probably manage this stuff itself.
|
35
|
+
|
36
|
+
## Contributing
|
37
|
+
|
38
|
+
1. Fork it and `script/bootstrap` your environment (Note: This also
|
39
|
+
performs a hard git reset to restore `bin/foreground_sample_daemon`,
|
40
|
+
which gets overwritten by bundler!)
|
41
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
42
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
43
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
44
|
+
5. Create new Pull Request
|
45
|
+
|
46
|
+
## Copyright
|
47
|
+
|
48
|
+
Copyright (c) 2012 Björn Albers (MIT License)
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'cucumber/rake/task'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
|
5
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
6
|
+
t.cucumber_opts = "--format pretty"
|
7
|
+
end
|
8
|
+
|
9
|
+
RSpec::Core::RakeTask.new do |t|
|
10
|
+
t.rspec_opts = %w[--color]
|
11
|
+
t.pattern = "./spec/**/*_spec.rb"
|
12
|
+
end
|
13
|
+
|
14
|
+
task :default => :features
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Feature: Sample daemon
|
2
|
+
|
3
|
+
In order to test foreground properly
|
4
|
+
As an BDD guy
|
5
|
+
I want a sample daemon along with cucumber steps
|
6
|
+
|
7
|
+
Scenario: Run sample daemon
|
8
|
+
When I successfully run `foreground_sample_daemon`
|
9
|
+
Then the sample daemon should run
|
10
|
+
And a file named "/tmp/foreground_sample_daemon.pid" should exist
|
11
|
+
|
12
|
+
Scenario: Run only one sample daemon at once
|
13
|
+
When I successfully run `foreground_sample_daemon`
|
14
|
+
And I successfully run `foreground_sample_daemon`
|
15
|
+
Then the sample daemon should run
|
16
|
+
|
17
|
+
Scenario: Don't mess up the system with running sample daemons
|
18
|
+
Then the sample daemon should not run
|
19
|
+
And a file named "/tmp/foreground_sample_daemon.pid" should not exist
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Feature: Start and stop
|
2
|
+
|
3
|
+
In order to start and stop a background daemon
|
4
|
+
As a funky user
|
5
|
+
I want foreground to start and stop that background daemon
|
6
|
+
|
7
|
+
Scenario: Start and stop sample daemon
|
8
|
+
When I run the sample daemon via foreground
|
9
|
+
Then the sample daemon should run
|
10
|
+
|
11
|
+
When I kill foreground
|
12
|
+
Then the sample daemon should not run
|
@@ -0,0 +1,44 @@
|
|
1
|
+
def sample_daemons
|
2
|
+
proclist = `/bin/ps -A -o pid,command`
|
3
|
+
proclist.to_a.grep(%r{^\s*\d+\s+ruby\s+.*foreground_sample_daemon$}) { |l| l[/^\s*(\d+)\s+/,1].to_i }
|
4
|
+
end
|
5
|
+
|
6
|
+
def kill_foreground
|
7
|
+
if @foreground
|
8
|
+
Process.kill(:TERM, @foreground) unless Process.waitpid(@foreground, Process::WNOHANG)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
After do
|
13
|
+
kill_foreground
|
14
|
+
sample_daemons.each { |pid| system("kill #{pid}") }
|
15
|
+
end
|
16
|
+
|
17
|
+
When /^I run the sample daemon via foreground$/ do
|
18
|
+
@foreground = fork do
|
19
|
+
exec('foreground --pid_file /tmp/foreground_sample_daemon.pid foreground_sample_daemon')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
When /^I kill foreground$/ do
|
24
|
+
kill_foreground
|
25
|
+
end
|
26
|
+
|
27
|
+
Then /^the sample daemon should run$/ do
|
28
|
+
steps %q{
|
29
|
+
Then 1 sample daemon should run
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
Then /^the sample daemon should not run$/ do
|
34
|
+
steps %q{
|
35
|
+
Then 0 sample daemon should run
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
Then /^(\d+) sample daemons? should run$/ do |expected|
|
40
|
+
expected = expected.to_i
|
41
|
+
actual = sample_daemons.count
|
42
|
+
actual.should eql(expected),
|
43
|
+
"Expected #{expected} running sample daemon, but got #{actual} instead!"
|
44
|
+
end
|
data/foreground.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'foreground/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = 'foreground'
|
8
|
+
gem.version = Foreground::VERSION
|
9
|
+
gem.authors = ["Bjo\314\210rn Albers"]
|
10
|
+
gem.email = ['bjoernalbers@googlemail.com']
|
11
|
+
gem.description = 'Control daemonizing background processes with launchd.'
|
12
|
+
gem.summary = "#{gem.name}-#{gem.version}"
|
13
|
+
gem.homepage = 'https://github.com/bjoernalbers/foreground'
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/foreground}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ['lib']
|
19
|
+
|
20
|
+
gem.add_dependency 'mixlib-cli', '~> 1.2.2'
|
21
|
+
|
22
|
+
gem.add_development_dependency 'aruba', '>= 0.4.11'
|
23
|
+
gem.add_development_dependency 'aruba-doubles', '~> 1.2.1'
|
24
|
+
gem.add_development_dependency 'guard-cucumber', '>= 0.7.5'
|
25
|
+
gem.add_development_dependency 'guard-rspec', '>= 0.5.1'
|
26
|
+
gem.add_development_dependency 'rake'
|
27
|
+
gem.add_development_dependency 'rb-fsevent', '>= 0.9.0' if RUBY_PLATFORM =~ /darwin/i
|
28
|
+
end
|
data/lib/foreground.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module Foreground
|
2
|
+
class CLI
|
3
|
+
include Mixlib::CLI
|
4
|
+
|
5
|
+
option :pid_file,
|
6
|
+
:short => '-p FILE',
|
7
|
+
:long => '--pid_file FILE',
|
8
|
+
:description => 'PID file for the daemon',
|
9
|
+
:required => true
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def run(argv=ARGV)
|
13
|
+
new.run(argv)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def run(argv)
|
18
|
+
cmd = parse_options(argv)
|
19
|
+
system(cmd.shelljoin)
|
20
|
+
watch
|
21
|
+
end
|
22
|
+
|
23
|
+
def watch
|
24
|
+
### <proof_of_concept>
|
25
|
+
#TODO: Unhackify this block of code!
|
26
|
+
STDOUT.sync = true
|
27
|
+
puts "hi, there!"
|
28
|
+
trap(:TERM) do
|
29
|
+
sleep 3 # Give the daemon time to write its PID file.
|
30
|
+
if File.exists?(config[:pid_file])
|
31
|
+
pid = File.read(config[:pid_file]).chomp.to_i
|
32
|
+
Process.kill(:TERM, pid)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
### </proof_of_concept>
|
36
|
+
|
37
|
+
#TODO: Implement watch feature!
|
38
|
+
loop { sleep 1 }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/script/bootstrap
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Foreground
|
4
|
+
describe CLI do
|
5
|
+
before do
|
6
|
+
CLI.stub(:system)
|
7
|
+
@argv = ['--pid_file', '/tmp/foreground_sample_daemon.pid', 'foreground_sample_daemon']
|
8
|
+
@cli = CLI.new
|
9
|
+
@cli.stub(:watch) # ...or we'll wait forever.
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '.run' do
|
13
|
+
it 'should run a new instance' do
|
14
|
+
cli = mock('cli')
|
15
|
+
cli.should_receive(:run).with(@argv)
|
16
|
+
CLI.should_receive(:new).and_return(cli)
|
17
|
+
CLI.run(@argv)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#run' do
|
22
|
+
it 'should parse argv' do
|
23
|
+
@cli.should_receive(:parse_options).with(@argv).and_return(@argv)
|
24
|
+
@cli.run(@argv)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should run the daemon' do
|
28
|
+
@cli.should_receive(:system).with('foreground_sample_daemon')
|
29
|
+
@cli.run(@argv)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should watch the daemon' do
|
33
|
+
@cli.should_receive(:watch)
|
34
|
+
@cli.run(@argv)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
File without changes
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'foreground'
|
metadata
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: foreground
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Björn Albers
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: mixlib-cli
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.2.2
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.2.2
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: aruba
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.4.11
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.4.11
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: aruba-doubles
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.2.1
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.2.1
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: guard-cucumber
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 0.7.5
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.7.5
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: guard-rspec
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 0.5.1
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 0.5.1
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: rake
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: rb-fsevent
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 0.9.0
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 0.9.0
|
126
|
+
description: Control daemonizing background processes with launchd.
|
127
|
+
email:
|
128
|
+
- bjoernalbers@googlemail.com
|
129
|
+
executables:
|
130
|
+
- foreground_sample_daemon
|
131
|
+
extensions: []
|
132
|
+
extra_rdoc_files: []
|
133
|
+
files:
|
134
|
+
- .gitignore
|
135
|
+
- Gemfile
|
136
|
+
- Guardfile
|
137
|
+
- LICENSE.txt
|
138
|
+
- README.md
|
139
|
+
- Rakefile
|
140
|
+
- bin/foreground_sample_daemon
|
141
|
+
- features/sample_daemon.feature
|
142
|
+
- features/start_stop.feature
|
143
|
+
- features/steps/sample_daemon_steps.rb
|
144
|
+
- features/support/env.rb
|
145
|
+
- foreground.gemspec
|
146
|
+
- lib/foreground.rb
|
147
|
+
- lib/foreground/cli.rb
|
148
|
+
- lib/foreground/version.rb
|
149
|
+
- script/bootstrap
|
150
|
+
- spec/foreground/cli_spec.rb
|
151
|
+
- spec/foreground/daemon_spec.rb
|
152
|
+
- spec/spec_helper.rb
|
153
|
+
homepage: https://github.com/bjoernalbers/foreground
|
154
|
+
licenses: []
|
155
|
+
post_install_message:
|
156
|
+
rdoc_options: []
|
157
|
+
require_paths:
|
158
|
+
- lib
|
159
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
160
|
+
none: false
|
161
|
+
requirements:
|
162
|
+
- - ! '>='
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
version: '0'
|
165
|
+
segments:
|
166
|
+
- 0
|
167
|
+
hash: -3670891717628661240
|
168
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
segments:
|
175
|
+
- 0
|
176
|
+
hash: -3670891717628661240
|
177
|
+
requirements: []
|
178
|
+
rubyforge_project:
|
179
|
+
rubygems_version: 1.8.24
|
180
|
+
signing_key:
|
181
|
+
specification_version: 3
|
182
|
+
summary: foreground-0.0.1
|
183
|
+
test_files:
|
184
|
+
- features/sample_daemon.feature
|
185
|
+
- features/start_stop.feature
|
186
|
+
- features/steps/sample_daemon_steps.rb
|
187
|
+
- features/support/env.rb
|
188
|
+
- spec/foreground/cli_spec.rb
|
189
|
+
- spec/foreground/daemon_spec.rb
|
190
|
+
- spec/spec_helper.rb
|