launchdr 3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ lib/launchdr/launchd.rb
2
+ lib/launchdr/property_list.rb
3
+ lib/launchdr/task.rb
4
+ lib/launchdr.rb
5
+ Rakefile.rb
6
+ README.markdown
7
+ .manifest
@@ -0,0 +1,66 @@
1
+ LaunchDoctor
2
+ ============
3
+ I've got a PhD in [launchd][], and I'm not afraid to use it!
4
+
5
+ [launchd]: <http://en.wikipedia.org/wiki/Launchd> "launchd on Wikipedia"
6
+
7
+ Usage
8
+ -----
9
+ LaunchDoctor is really simple. It is essentially an easy interface to
10
+ [launchd][]'s [property list][plist] files.
11
+
12
+ There are three ways to use LaunchDoctor - the simplest being directly
13
+ creating property lists and treating them as hashes:
14
+
15
+ require 'launchdr'
16
+ plist = LaunchDr::Launchd.new "name.elliottcable.launchdr.test"
17
+ plist[:program_arguments] = ['/Applications/Calculator.app/Contents/MacOS/Calculator']
18
+ plist.dump LaunchDr::Launchd::Paths[:user_agent]
19
+
20
+ The second is to use the common block idiom, provided by the `LaunchDr()`
21
+ method:
22
+
23
+ require 'launchdr'
24
+ LaunchDr "name.elliottcable.launchdr.test" do |plist|
25
+ plist[:program_arguments] = ['/Applications/Calculator.app/Contents/MacOS/Calculator']
26
+ end
27
+
28
+ LaunchDoctor will automatically add the property list to `launchctl`, and then
29
+ start it running. Once you run the above snippet, the target will immediately
30
+ be launched for the first time.
31
+
32
+ LaunchDoctor can write (`dump`) the property lists to any place on your disk,
33
+ but the idiom method assumes you're going to want to use one of the
34
+ directories that launchd checks for property lists. These are stored in the
35
+ `Launchd::Paths` array. The default is to place it in the user-owned agents
36
+ directory at `~/Library/LaunchAgents`.
37
+
38
+ LaunchDoctor also preforms some 'prettification' on the keys provided by
39
+ launchd's property list structure. All of the keys on the
40
+ [`launchd.plist` manpage][manpage] are available, but they can also be used as
41
+ true 'Ruby-ish' symbol keys. All of the following are legal:
42
+
43
+ plist["UserName"] = "elliottcable"
44
+ plist[:UserName] = "elliottcable"
45
+ plist[:user_name] = "elliottcable"
46
+
47
+ Finally, you can use the Rake task interface to the last method. It simply
48
+ wraps the last method inside a rake task. This method is really great if you
49
+ want to provide a way to let users make your gem's binary run all the time:
50
+
51
+ require 'launchdr/task'
52
+ LaunchDr::Task.new :launchd, :bin => 'jello', :arguments => ['-D', 'shortener', 'grabup']
53
+
54
+ This isn't very flexible, but it's not very complicated either. If you need
55
+ more control over the plist, just use the second method inside a `task` block.
56
+ This method defaults to making your gem's binary `run_at_load` and be
57
+ `keep_alive` as well, so it won't die.
58
+
59
+ [plist]: <http://en.wikipedia.org/wiki/Property_list> "Property list on Wikipedia"
60
+ [manpage]: <http://developer.apple.com/DOCUMENTATION/DARWIN/Reference/ManPages/man5/launchd.plist.5.html> "Mac OS X Manual Page for launchd.plist(5)"
61
+ [rake]: <http://rake.rubyforge.org/> "Rake's RDocs"
62
+
63
+ Requirements
64
+ ------------
65
+ - Plist - `gem install plist`
66
+ - Ruby Facets - `gem install facets`
@@ -0,0 +1,40 @@
1
+ ($:.unshift File.expand_path(File.join( File.dirname(__FILE__), 'lib' ))).uniq!
2
+ require 'launchdr'
3
+
4
+ # =======================
5
+ # = Gem packaging tasks =
6
+ # =======================
7
+ begin
8
+ require 'echoe'
9
+
10
+ task :package => :'package:package'
11
+ task :install => :'package:install'
12
+ task :manifest => :'package:manifest'
13
+ task :clobber => :'package:clobber'
14
+ namespace :package do
15
+ Echoe.new('launchdr', LaunchDr::Version) do |g|
16
+ g.author = ['elliottcable']
17
+ g.email = ['launchdr@elliottcable.com']
18
+ g.summary = "One stop shop for launchd property list creation. The doctor is *in*!"
19
+ g.url = 'http://github.com/elliottcable/launchdr'
20
+ g.dependencies = ['plist', 'facets', 'uuid']
21
+ g.development_dependencies = ['echoe >= 3.0.2']
22
+ g.manifest_name = '.manifest' # I don't want this showing up <,<
23
+ g.retain_gemspec = true # perfect for GitHub
24
+ g.rakefile_name = 'Rakefile.rb' # It's a Ruby file, why not have .rb?
25
+ g.ignore_pattern = /^\.git\/|\.gemspec/
26
+ end
27
+
28
+ desc 'tests packaged files to ensure they are all present'
29
+ task :verify => :package do
30
+ # An error message will be displayed if files are missing
31
+ if system %(ruby -e "require 'rubygems'; require 'pkg/launchdr-#{LaunchDr::Version}/lib/launchdr'")
32
+ puts "\nThe library files are present"
33
+ end
34
+ end
35
+ end
36
+
37
+ rescue LoadError
38
+ desc 'You need the `echoe` gem to package LaunchDr'
39
+ task :package
40
+ end
@@ -0,0 +1,43 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{launchdr}
5
+ s.version = "3"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["elliottcable"]
9
+ s.date = %q{2009-02-11}
10
+ s.description = %q{One stop shop for launchd property list creation. The doctor is *in*!}
11
+ s.email = ["launchdr@elliottcable.com"]
12
+ s.extra_rdoc_files = ["lib/launchdr/launchd.rb", "lib/launchdr/property_list.rb", "lib/launchdr/task.rb", "lib/launchdr.rb", "README.markdown"]
13
+ s.files = ["lib/launchdr/launchd.rb", "lib/launchdr/property_list.rb", "lib/launchdr/task.rb", "lib/launchdr.rb", "Rakefile.rb", "README.markdown", ".manifest", "launchdr.gemspec"]
14
+ s.has_rdoc = true
15
+ s.homepage = %q{http://github.com/elliottcable/launchdr}
16
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Launchdr", "--main", "README.markdown"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{launchdr}
19
+ s.rubygems_version = %q{1.3.1}
20
+ s.summary = %q{One stop shop for launchd property list creation. The doctor is *in*!}
21
+
22
+ if s.respond_to? :specification_version then
23
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
+ s.specification_version = 2
25
+
26
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
+ s.add_runtime_dependency(%q<plist>, [">= 0"])
28
+ s.add_runtime_dependency(%q<facets>, [">= 0"])
29
+ s.add_runtime_dependency(%q<uuid>, [">= 0"])
30
+ s.add_development_dependency(%q<echoe>, [">= 0", "= 3.0.2"])
31
+ else
32
+ s.add_dependency(%q<plist>, [">= 0"])
33
+ s.add_dependency(%q<facets>, [">= 0"])
34
+ s.add_dependency(%q<uuid>, [">= 0"])
35
+ s.add_dependency(%q<echoe>, [">= 0", "= 3.0.2"])
36
+ end
37
+ else
38
+ s.add_dependency(%q<plist>, [">= 0"])
39
+ s.add_dependency(%q<facets>, [">= 0"])
40
+ s.add_dependency(%q<uuid>, [">= 0"])
41
+ s.add_dependency(%q<echoe>, [">= 0", "= 3.0.2"])
42
+ end
43
+ end
@@ -0,0 +1,36 @@
1
+ require 'launchdr/property_list'
2
+ require 'launchdr/launchd'
3
+
4
+
5
+ def LaunchDr label, opts = {}
6
+ LaunchDr.create label, opts, &Proc.new
7
+ end
8
+ module LaunchDr
9
+ Version = 3
10
+ Options = Hash.new
11
+
12
+ def self.create label, opts = {}
13
+ plist = Launchd.new label
14
+
15
+ yield plist if block_given?
16
+
17
+ raise "You must run as a root user and allow_system_agents! if you wish to preform the dangerous act of writing to #{Launchd::Paths[:system_agent]}!" unless !(opts[:type] == :system_agent) or Options[:system_agents_allowed]
18
+ raise "You must run as a root user, allow_system_agents!, *and* allow_system_daemons! if you wish to preform the very dangerous act of writing to #{Launchd::Paths[:system_daemon]}!" unless !(opts[:type] == :system_daemon) or Options[:system_daemon_allowed]
19
+ path = Launchd::Paths[opts[:type] || :user_agent]
20
+ raise "Type error! Must be one of #{Launchd::Paths.keys.join(", ")}" unless path
21
+
22
+ plist.dump File.expand_path(path)
23
+ plist.load!
24
+ end
25
+
26
+ def self.allow_system_agents!
27
+ Options[:system_agents_allowed] = true
28
+ end
29
+
30
+ def self.allow_system_daemons!
31
+ raise "You must allow System-owned agents to allow System-owned daemons!" unless Options[:system_agents_allowed]
32
+ Options[:system_daemon_allowed] = true
33
+ end
34
+
35
+
36
+ end
@@ -0,0 +1,43 @@
1
+ require 'facets/string'
2
+
3
+ module LaunchDr
4
+
5
+ class Launchd < PropertyList
6
+ Paths = {
7
+ :user_agent => "~/Library/LaunchAgents",
8
+ :agent => "/Library/LaunchAgents",
9
+ :daemon => "/Library/LaunchDaemons",
10
+ :system_agent => "/System/Library/LaunchAgents",
11
+ :system_daemon => "/System/Library/LaunchDaemons"
12
+ }
13
+
14
+ def [] key
15
+ super(key.to_s.titlecase.camelcase)
16
+ end
17
+
18
+ def []= key, value
19
+ super(key.to_s.titlecase.camelcase, value)
20
+ end
21
+
22
+ # Adds the property list to `launchctl`'s indexes, and starts it (unless
23
+ # disabled).
24
+ def load!
25
+ system "launchctl load #{file}"
26
+ end
27
+
28
+ # Removes the propertly list from `launchctl`'s indexes.
29
+ def unload!
30
+ system "launchctl unload #{file}"
31
+ end
32
+
33
+ # Finds the plist file, if it's been created
34
+ def file
35
+ Paths.values.each do |dir|
36
+ file = File.expand_path(File.join(dir, self[:label] + '.plist'))
37
+ return file if File.file? file
38
+ end
39
+ raise "#{self[:label]} doesn't exist in any of the default locations. Did you #dump it?"
40
+ end
41
+ end
42
+
43
+ end
@@ -0,0 +1,38 @@
1
+ require 'plist'
2
+ require 'uuid'
3
+
4
+ module LaunchDr
5
+
6
+ class PropertyList
7
+ attr_reader :elements
8
+
9
+ def initialize label = nil, elements = {}
10
+ @elements = elements
11
+ @elements['Label'] = label || "rb.launchdr.#{UUID.new.generate}"
12
+ end
13
+
14
+ def [] key
15
+ @elements[key.to_s]
16
+ end
17
+
18
+ def []= key, value
19
+ @elements[key] = value
20
+ end
21
+
22
+ def to_plist
23
+ @elements.to_plist
24
+ end
25
+
26
+ def dump path
27
+ out = File.new(File.expand_path(File.join(path, @elements['Label'] + '.plist')), 'w+')
28
+ out.puts Plist::Emit.dump @elements
29
+ out.close
30
+ end
31
+
32
+ def self.load filename
33
+ elements = Plist::parse_xml File.new(File.expand_path filename, 'r')
34
+ new elements[:label], elements
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,33 @@
1
+ require 'launchdr'
2
+
3
+ module LaunchDr
4
+
5
+ module Task
6
+ def self.new name = :launchd, opts = {}
7
+ raise "`LaunchDr.task`'s options must include the name of the binary for your gem!" unless opts[:bin]
8
+ opts = {:desc => 'Creates a launchd property list for this gem', :arguments => []}.merge opts
9
+
10
+ desc opts[:desc] unless opts[:no_desc]
11
+ task name do
12
+ path = File.expand_path File.join('', opts[:bin])
13
+ unless File.file? path
14
+ path = File.expand_path File.join(Gem::default_bindir, opts[:bin])
15
+ unless File.file? path
16
+ path = File.expand_path %x[which "#{opts[:bin]}"].chomp
17
+ end
18
+ end
19
+ unless File.file? path
20
+ raise "** Unable to locate binary #{opts[:bin]}"
21
+ end
22
+ LaunchDr "rb.launchdr.#{opts[:bin]}" do |plist|
23
+ plist[:program_arguments] = [path, opts[:arguments]].flatten
24
+ plist[:keep_alive] = true
25
+ plist[:run_at_load] = true
26
+ end
27
+ puts "** `#{[path, opts[:arguments]].flatten.join(' ')}` will now be run on startup!"
28
+ end
29
+
30
+ end
31
+ end
32
+
33
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: launchdr
3
+ version: !ruby/object:Gem::Version
4
+ hash: -124656629444251359
5
+ prerelease: false
6
+ segments:
7
+ - 3
8
+ version: "3"
9
+ platform: ruby
10
+ authors:
11
+ - elliottcable
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+
16
+ date: 2009-02-11 00:00:00 -09:00
17
+ default_executable:
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: plist
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ hash: 134023568094289244
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: facets
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ hash: 134023568094289244
42
+ segments:
43
+ - 0
44
+ version: "0"
45
+ type: :runtime
46
+ version_requirements: *id002
47
+ - !ruby/object:Gem::Dependency
48
+ name: uuid
49
+ prerelease: false
50
+ requirement: &id003 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ hash: 134023568094289244
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ type: :runtime
60
+ version_requirements: *id003
61
+ - !ruby/object:Gem::Dependency
62
+ name: echoe
63
+ prerelease: false
64
+ requirement: &id004 !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ hash: 134023568094289244
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ - - "="
74
+ - !ruby/object:Gem::Version
75
+ hash: 1556383981505405372
76
+ segments:
77
+ - 3
78
+ - 0
79
+ - 2
80
+ version: 3.0.2
81
+ type: :development
82
+ version_requirements: *id004
83
+ description: One stop shop for launchd property list creation. The doctor is *in*!
84
+ email:
85
+ - launchdr@elliottcable.com
86
+ executables: []
87
+
88
+ extensions: []
89
+
90
+ extra_rdoc_files:
91
+ - lib/launchdr/launchd.rb
92
+ - lib/launchdr/property_list.rb
93
+ - lib/launchdr/task.rb
94
+ - lib/launchdr.rb
95
+ - README.markdown
96
+ files:
97
+ - lib/launchdr/launchd.rb
98
+ - lib/launchdr/property_list.rb
99
+ - lib/launchdr/task.rb
100
+ - lib/launchdr.rb
101
+ - Rakefile.rb
102
+ - README.markdown
103
+ - .manifest
104
+ - launchdr.gemspec
105
+ has_rdoc: true
106
+ homepage: http://github.com/elliottcable/launchdr
107
+ licenses: []
108
+
109
+ post_install_message:
110
+ rdoc_options:
111
+ - --line-numbers
112
+ - --inline-source
113
+ - --title
114
+ - Launchdr
115
+ - --main
116
+ - README.markdown
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ hash: 134023568094289244
125
+ segments:
126
+ - 0
127
+ version: "0"
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ hash: 1922007725579590311
134
+ segments:
135
+ - 1
136
+ - 2
137
+ version: "1.2"
138
+ requirements: []
139
+
140
+ rubyforge_project: launchdr
141
+ rubygems_version: 1.3.7
142
+ signing_key:
143
+ specification_version: 2
144
+ summary: One stop shop for launchd property list creation. The doctor is *in*!
145
+ test_files: []
146
+