launchd_tools 0.5.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7a74ee78c83f4de133e4d2ae13f21ead304210ef
4
+ data.tar.gz: ca34827f49bf559be77e8c250471cfa7aa6c2eca
5
+ SHA512:
6
+ metadata.gz: 03f429b7f92e27e6aa3db2ab370ae418d1a1d0a8d53f18edbb15d926e42b2e4e36a4f2088c919b8733e86eb89abd8808c089b6bc80b034a532000bbc6398edea
7
+ data.tar.gz: 65c19da4950484a251c349c244e57cad2b76f115e5650f65eb747de9441c6bcf5545c14645cdbf29001f477c0b93ef9feb907fa86aab2f86f3e6d7ea3c770630
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in launchd_tools.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Kyle Crawford
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,108 @@
1
+ # Launchd Tools
2
+
3
+ Convert from command to launchd plist or from launchd plist to command
4
+
5
+ ## Installation
6
+
7
+ $ gem install launchd_tools
8
+
9
+ ## Command Usage
10
+
11
+ Output the command and arguments from a launchd plist:
12
+
13
+ $ launchd2cmd /path/to/launchd.plist [another/launchd.plist]
14
+
15
+ Create a launchd plist from a command and arguments:
16
+
17
+ $ cmd2launchd /path/to/daemon [arg] [-option]
18
+
19
+ ## Examples
20
+
21
+ ####launchd2cmd with multiple directories:
22
+
23
+ $ launchd2cmd /System/Library/LaunchAgents /System/Library/LaunchDaemons
24
+ ...
25
+ # /System/Library/LaunchDaemons/tftp.plist
26
+ /usr/libexec/tftpd -i /private/tftpboot
27
+
28
+ ####launchd2cmd wildcards/globbing:
29
+
30
+ $ launchd2cmd /Library/Launch*/com.googlecode.munki.*
31
+ ...
32
+ # /Library/LaunchDaemons/com.googlecode.munki.managedsoftwareupdate-manualcheck.plist
33
+ /usr/local/munki/supervisor --timeout 43200 -- /usr/local/munki/managedsoftwareupdate --manualcheck
34
+
35
+ ####cmd2launchd creating a launchd plist skeleton:
36
+
37
+ $ cmd2launchd /usr/local/bin/daemond -d --mode foreground
38
+ <?xml version='1.0' encoding='UTF-8'?>
39
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
40
+ <plist version='1.0'>
41
+ <dict>
42
+ <key>ProgramArguments</key>
43
+ <array>
44
+ <string>/usr/local/bin/daemond</string>
45
+ <string>-d</string>
46
+ <string>--mode</string>
47
+ <string>foreground</string>
48
+ </array>
49
+ </dict>
50
+ </plist>
51
+
52
+ ## Usage from Ruby
53
+
54
+ ### Parsing a launchd plist
55
+
56
+ ```ruby
57
+ >> require 'launchd_tools/path'
58
+ => false
59
+ >> sshd_plist_path =
60
+ LaunchdTools::Path.new("/System/Library/LaunchDaemons/ssh.plist")
61
+ => #<LaunchdTools::Path:0x007f9a7b1a5f50
62
+ @path="/System/Library/LaunchDaemons/ssh.plist">
63
+ >> sshd_job = sshd_plist_path.parse
64
+ => #<LaunchdTools::LaunchdJob:0x007f9a7aa6cc70
65
+ @attributes={"EnvironmentVariables"=>[],
66
+ "ProgramArguments"=>["/usr/sbin/sshd", "-i"]}>
67
+ >> sshd_job.program_arguments
68
+ => ["/usr/sbin/sshd", "-i"]
69
+ ```
70
+
71
+ ### Creating a launchd plist
72
+
73
+ ```ruby
74
+ >> require 'launchd_tools/launchd_plist'
75
+ => true
76
+ >> plist = LaunchdTools::LaunchdPlist.new
77
+ => #<LaunchdTools::LaunchdPlist:0x007fde1c434c78 @doc=<UNDEFINED> ...
78
+ </>, @program_args_array_element=<array/>>
79
+ >> plist.add_program_args(["/usr/local/bin/daemon", "-f", "-o",
80
+ "/var/log/daemon.log"])
81
+ => ["/usr/local/bin/daemon", "-f", "-o", "/var/log/daemon.log"]
82
+ >> puts plist.to_s
83
+ <?xml version='1.0' encoding='UTF-8'?>
84
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
85
+ "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
86
+ <plist version='1.0'>
87
+ <dict>
88
+ <key>ProgramArguments</key>
89
+ <array>
90
+ <string>/usr/local/bin/daemon</string>
91
+ <string>-f</string>
92
+ <string>-o</string>
93
+ <string>/var/log/daemon.log</string>
94
+ </array>
95
+ </dict>
96
+ </plist>
97
+ ```
98
+
99
+
100
+ ## Contributing
101
+
102
+ 1. Fork it
103
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
104
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
105
+ 4. Push to the branch (`git push origin my-new-feature`)
106
+ 5. Create new Pull Request
107
+
108
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/TODO ADDED
@@ -0,0 +1,3 @@
1
+ Release package made with fpm
2
+ Output errors to stderr
3
+ Test for exit statuses
data/bin/cmd2launchd ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'launchd_tools/cmd2launchd_cli'
4
+
5
+ LaunchdTools::Cmd2LaunchdCli.new(ARGV).run
data/bin/launchd2cmd ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'launchd_tools/launchd2cmd_cli'
4
+
5
+ LaunchdTools::Launchd2CmdCli.new(ARGV).run
@@ -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 'launchd_tools/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "launchd_tools"
8
+ spec.version = LaunchdTools::VERSION
9
+ spec.authors = ["Kyle Crawford"]
10
+ spec.email = ["kcrwfrd@gmail.com"]
11
+ spec.description = %q{Provides tools for converting from command line arguments to a formatted launchd plist and vice versa}
12
+ spec.summary = %q{launchd tools convert from command to launchd and launchd to command}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rspec"
23
+ spec.add_development_dependency "rake"
24
+ end
@@ -0,0 +1,38 @@
1
+ require 'launchd_tools/launchd_plist'
2
+
3
+ module LaunchdTools
4
+ class Cmd2LaunchdCli
5
+
6
+ attr_reader :args
7
+ def initialize(args)
8
+ if args.empty?
9
+ show_usage
10
+ exit 0
11
+ elsif args.length == 1
12
+ case args.first
13
+ when "--help"
14
+ show_usage
15
+ exit 0
16
+ when "-h"
17
+ show_usage
18
+ exit 0
19
+ when "--version"
20
+ puts LaunchdTools::VERSION
21
+ exit 0
22
+ end
23
+ end
24
+ @args = args
25
+ end
26
+
27
+ def show_usage
28
+ puts "Usage: #{$0} command [arg1] [arg2]"
29
+ end
30
+
31
+ def run
32
+ plist = LaunchdPlist.new
33
+ plist.add_program_args(args)
34
+ puts plist.to_s
35
+ end
36
+ end
37
+ end
38
+
@@ -0,0 +1,31 @@
1
+ require 'rexml/document'
2
+ include REXML
3
+ module LaunchdTools
4
+ class EnvironmentParser
5
+ attr_reader :xml_doc, :element
6
+ def initialize(xml_doc)
7
+ @xml_doc = xml_doc
8
+ end
9
+
10
+ def element
11
+ @element ||= REXML::XPath.first(xml_doc, "plist/dict/key[text()='EnvironmentVariables']/following-sibling::dict")
12
+ end
13
+
14
+ def extract_env
15
+ env = {}
16
+ REXML::XPath.match(element, 'key').each do |environment_key|
17
+ env[environment_key.text] = environment_key.next_sibling.next_sibling.text
18
+ end
19
+ env
20
+ end
21
+
22
+ def parse
23
+ if element
24
+ extract_env
25
+ else
26
+ {}
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ module LaunchdTools
2
+ class EnvironmentVariables
3
+ attr_reader :variables
4
+ def initialize(variables = {})
5
+ @variables = variables
6
+ end
7
+
8
+ def to_a
9
+ env_items = []
10
+ variables.each do |key,value|
11
+ env_items << "#{key}=#{value}"
12
+ end
13
+ env_items
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,82 @@
1
+ require 'launchd_tools/path'
2
+ require 'optparse'
3
+ module LaunchdTools
4
+ class Launchd2CmdCli
5
+ attr_reader :paths, :args
6
+ def initialize(args)
7
+ showing_help = false
8
+ opt_parser = OptionParser.new do |opt|
9
+ opt.banner = "Usage: #{$0} path/to/launchd.plist"
10
+ opt.separator ""
11
+ opt.separator "Options"
12
+ opt.on("--help", "-h", "Displays this help message") do
13
+ showing_help = true
14
+ end
15
+ opt.on("--version", "outputs version information for this tool") do
16
+ puts LaunchdTools::VERSION
17
+ end
18
+ opt.separator ""
19
+ end
20
+
21
+ opt_parser.parse!(args)
22
+
23
+ if args.empty? || showing_help
24
+ puts opt_parser
25
+ else
26
+ @args = args
27
+ @paths = extract_paths(args)
28
+ end
29
+ end
30
+
31
+ def extract_paths(path_args)
32
+ path_args = path_args.map do |path_arg|
33
+ if File.directory?(path_arg)
34
+ path_arg += "/" if path_arg[-1] != "/"
35
+ path_arg += "*"
36
+ end
37
+ path_arg
38
+ end
39
+ Dir.glob(path_args)
40
+ end
41
+
42
+ def run
43
+ errors = 0
44
+ if paths
45
+ if paths.length > 0
46
+ errors = process_each_path
47
+ else
48
+ args.each {|arg| puts "No launchd job found at '#{arg}'" }
49
+ exit 1
50
+ end
51
+ end
52
+ exit 2 if errors > 0
53
+ end
54
+
55
+ def process_path(path_string)
56
+ error_count = 0
57
+ begin
58
+ path = Path.new(path_string).validate
59
+ puts "# #{path.expanded}"
60
+ puts path.parse.to_s
61
+ rescue LaunchdTools::Path::UnparsablePlist
62
+ puts "Error: unable to parse launchd job\n"
63
+ error_count = 1
64
+ rescue LaunchdTools::Path::PermissionsError
65
+ require 'etc'
66
+ username = Etc.getpwuid(Process.euid).name
67
+ puts "Error: user #{username} does not have access to read launchd job\n"
68
+ error_count = 1
69
+ end
70
+ return error_count
71
+ end
72
+
73
+ def process_each_path
74
+ error_count = 0
75
+ puts
76
+ paths.each do |path_string|
77
+ error_count += process_path(path_string)
78
+ end
79
+ return error_count
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,45 @@
1
+ module LaunchdTools
2
+ class LaunchdJob
3
+ attr_reader :attributes
4
+ def initialize(attributes = {})
5
+ @attributes = attributes
6
+ end
7
+
8
+ def environment_variables
9
+ attributes.fetch('EnvironmentVariables', {})
10
+ end
11
+
12
+ def program_arguments
13
+ attributes.fetch('ProgramArguments', [])
14
+ end
15
+
16
+ def to_s
17
+ escaped_args = program_arguments.map {|arg| shellescape(arg) }
18
+ puts (environment_variables + escaped_args).join(" ")
19
+ end
20
+
21
+ # from ruby's shellescape
22
+ # included here for ruby 1.8 compatibility
23
+ def shellescape(str)
24
+ str = str.to_s
25
+
26
+ # An empty argument will be skipped, so return empty quotes.
27
+ return "''" if str.empty?
28
+
29
+ str = str.dup
30
+
31
+ # Treat multibyte characters as is. It is caller's responsibility
32
+ # to encode the string in the right encoding for the shell
33
+ # environment.
34
+ str.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/, "\\\\\\1")
35
+
36
+ # A LF cannot be escaped with a backslash because a backslash + LF
37
+ # combo is regarded as line continuation and simply ignored.
38
+ str.gsub!(/\n/, "'\n'")
39
+
40
+ return str
41
+ end
42
+
43
+ end
44
+ end
45
+
@@ -0,0 +1,44 @@
1
+ require 'rexml/document'
2
+ include REXML
3
+ module LaunchdTools
4
+ class LaunchdPlist
5
+ attr_reader :doc, :program_args_array_element
6
+
7
+ def initialize
8
+ base = %q[<?xml version='1.0' encoding='UTF-8'?>
9
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">]
10
+ @doc = Document.new(base)
11
+ plist_element = Element.new('plist')
12
+ doc.add_element(plist_element, {"version" => "1.0"})
13
+ base_dictionary = Element.new('dict')
14
+ plist_element.add_element(base_dictionary)
15
+ program_args_key_element = Element.new('key')
16
+ program_args_key_element.text = 'ProgramArguments'
17
+ base_dictionary.add_element(program_args_key_element)
18
+ @program_args_array_element = Element.new "array"
19
+ base_dictionary.add_element(program_args_array_element)
20
+ end
21
+
22
+
23
+ def add_program_arg(arg)
24
+ string_element = Element.new "string"
25
+ string_element.text = arg
26
+ program_args_array_element << string_element
27
+ end
28
+
29
+ def add_program_args(args)
30
+ args.each do |arg|
31
+ add_program_arg(arg)
32
+ end
33
+ end
34
+
35
+ def to_s
36
+ formatter = REXML::Formatters::Pretty.new # (2)
37
+ formatter.compact = true
38
+ xml_string = String.new
39
+ formatter.write(doc, xml_string)
40
+ xml_string
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,47 @@
1
+ require 'launchd_tools/path_parser'
2
+ require 'launchd_tools/path_content'
3
+
4
+ module LaunchdTools
5
+ class Path
6
+
7
+ class UnparsablePlist < Exception
8
+ end
9
+
10
+ class PermissionsError < Exception
11
+ end
12
+
13
+ attr_reader :path
14
+
15
+ def initialize(path)
16
+ @path = path
17
+ end
18
+
19
+ def validate
20
+ raise PathMissingError unless File.exist?(path)
21
+ self
22
+ end
23
+
24
+ def content
25
+ PathContent.new(path).to_s
26
+ end
27
+
28
+ def parse
29
+ begin
30
+ path_parser.parse
31
+ rescue Errno::EACCES
32
+ raise PermissionsError.new
33
+ rescue
34
+ raise UnparsablePlist.new
35
+ end
36
+ end
37
+
38
+ def path_parser
39
+ PathParser.new(self)
40
+ end
41
+
42
+ def expanded
43
+ File.expand_path(path)
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,24 @@
1
+ module LaunchdTools
2
+ class PathContent
3
+ attr_reader :path
4
+ def initialize(path)
5
+ @path = path
6
+ end
7
+
8
+ def to_s
9
+ if binary?
10
+ `plutil -convert xml1 -o /dev/stdout '#{path}'`
11
+ else
12
+ File.read(path)
13
+ end
14
+ end
15
+
16
+ def binary?
17
+ f = File.open(path, "r")
18
+ f.read(6) == "bplist"
19
+ ensure
20
+ f.close if f
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,34 @@
1
+ require 'rexml/document'
2
+ require 'launchd_tools/launchd_job'
3
+ require 'launchd_tools/environment_parser'
4
+ require 'launchd_tools/environment_variables'
5
+ require 'launchd_tools/program_args_parser'
6
+
7
+ include REXML
8
+ module LaunchdTools
9
+ class PathParser
10
+ attr_reader :path, :xml_doc
11
+ def initialize(path)
12
+ @path = path
13
+ end
14
+
15
+ # returns a parsed launchd job
16
+ def parse
17
+ LaunchdJob.new({ 'EnvironmentVariables' => parse_env, 'ProgramArguments' => parse_program_args })
18
+ end
19
+
20
+ def parse_env
21
+ variables = EnvironmentParser.new(xml_doc).parse
22
+ EnvironmentVariables.new(variables).to_a
23
+ end
24
+
25
+ def parse_program_args
26
+ ProgramArgsParser.new(xml_doc).parse
27
+ end
28
+
29
+ def xml_doc
30
+ @xml_doc ||= Document.new(path.content)
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,21 @@
1
+ require 'rexml/document'
2
+ include REXML
3
+ module LaunchdTools
4
+ class ProgramArgsParser
5
+ attr_reader :xml_doc, :element
6
+ def initialize(xml_doc)
7
+ @xml_doc = xml_doc
8
+ end
9
+
10
+ def parse
11
+ element = REXML::XPath.first(xml_doc, "plist/dict/key[text()='ProgramArguments']/following-sibling::array")
12
+ if element
13
+ args_strings = XPath.match(element, 'string')
14
+ args_strings.map {|e| e.text }
15
+ else
16
+ program_string_element = REXML::XPath.first(xml_doc, "plist/dict/key[text()='Program']/following-sibling::string")
17
+ [program_string_element.text]
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ module LaunchdTools
2
+ VERSION = "0.5.0"
3
+ end
@@ -0,0 +1,5 @@
1
+ require "launchd_tools/version"
2
+
3
+ module LaunchdTools
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,21 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key>
6
+ <string>com.apple.dt.CommandLineTools.installondemand</string>
7
+ <key>MachServices</key>
8
+ <dict>
9
+ <key>com.apple.dt.CommandLineTools.installondemand</key>
10
+ <true/>
11
+ </dict>
12
+ <key>OnDemand</key>
13
+ <true/>
14
+ <key>EnableTransactions</key>
15
+ <true/>
16
+ <key>Program</key>
17
+ <string>/System/Library/CoreServices/Install Command Line Developer Tools.app/Contents/MacOS/Install Command Line Developer Tools</string>
18
+ <key>LimitLoadToSessionType</key>
19
+ <string>Aqua</string>
20
+ </dict>
21
+ </plist>
@@ -0,0 +1,50 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key>
6
+ <string>com.apple.storeagent</string>
7
+ <key>MachServices</key>
8
+ <dict>
9
+ <key>com.apple.storeagent</key>
10
+ <true/>
11
+ <key>com.apple.storeagent-xpc</key>
12
+ <true/>
13
+ <key>com.apple.storeagent.storekit</key>
14
+ <string></string>
15
+ </dict>
16
+ <key>OnDemand</key>
17
+ <true/>
18
+ <key>Program</key>
19
+ <string>/System/Library/PrivateFrameworks/CommerceKit.framework/Versions/A/Resources/storeagent</string>
20
+ <key>EnableTransactions</key>
21
+ <true/>
22
+ <key>LimitLoadToSessionType</key>
23
+ <array>
24
+ <string>LoginWindow</string>
25
+ <string>Aqua</string>
26
+ </array>
27
+ <key>LaunchEvents</key>
28
+ <dict>
29
+ <key>com.apple.usernotificationcenter.matching</key>
30
+ <dict>
31
+ <key>store</key>
32
+ <dict>
33
+ <key>bundleid</key>
34
+ <string>com.apple.appstore</string>
35
+ <key>delay registration</key>
36
+ <true/>
37
+ <key>events</key>
38
+ <array>
39
+ <string>didDismissAlert</string>
40
+ <string>didActivateNotification</string>
41
+ <string>didDeliverNotification</string>
42
+ <string>didSnoozeAlert</string>
43
+ <string>didRemoveDeliveredNotifications</string>
44
+ <string>didExpireNotifications</string>
45
+ </array>
46
+ </dict>
47
+ </dict>
48
+ </dict>
49
+ </dict>
50
+ </plist>
@@ -0,0 +1,41 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Disabled</key>
6
+ <true/>
7
+ <key>Label</key>
8
+ <string>com.openssh.sshd</string>
9
+ <key>Program</key>
10
+ <string>/usr/libexec/sshd-keygen-wrapper</string>
11
+ <key>ProgramArguments</key>
12
+ <array>
13
+ <string>/usr/sbin/sshd</string>
14
+ <string>-i</string>
15
+ </array>
16
+ <key>Sockets</key>
17
+ <dict>
18
+ <key>Listeners</key>
19
+ <dict>
20
+ <key>SockServiceName</key>
21
+ <string>ssh</string>
22
+ <key>Bonjour</key>
23
+ <array>
24
+ <string>ssh</string>
25
+ <string>sftp-ssh</string>
26
+ </array>
27
+ </dict>
28
+ </dict>
29
+ <key>inetdCompatibility</key>
30
+ <dict>
31
+ <key>Wait</key>
32
+ <false/>
33
+ </dict>
34
+ <key>StandardErrorPath</key>
35
+ <string>/dev/null</string>
36
+ <key>SHAuthorizationRight</key>
37
+ <string>system.preferences</string>
38
+ <key>POSIXSpawnType</key>
39
+ <string>Interactive</string>
40
+ </dict>
41
+ </plist>
@@ -0,0 +1,25 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Disabled</key>
6
+ <true/>
7
+ <key>EnvironmentVariables</key>
8
+ <dict>
9
+ <key>XPC_SERVICES_UNAVAILABLE</key>
10
+ <string>1</string>
11
+ </dict>
12
+ <key>Label</key>
13
+ <string>org.apache.httpd</string>
14
+ <key>OnDemand</key>
15
+ <false/>
16
+ <key>ProgramArguments</key>
17
+ <array>
18
+ <string>/usr/sbin/httpd</string>
19
+ <string>-D</string>
20
+ <string>FOREGROUND</string>
21
+ </array>
22
+ <key>SHAuthorizationRight</key>
23
+ <string>system.preferences</string>
24
+ </dict>
25
+ </plist>
@@ -0,0 +1,25 @@
1
+ require 'tempfile'
2
+ describe 'running cmd2launchd' do
3
+ it "converts right back to original commmand using launchd2cmd" do
4
+ t = Tempfile.new(File.basename(__FILE__))
5
+ temp_file_path = t.path
6
+ t.close
7
+ output = `bundle exec bin/cmd2launchd ls -la /tmp > #{temp_file_path} && bundle exec bin/launchd2cmd #{temp_file_path}`
8
+ expect(output.chomp).to include("ls -la /tmp")
9
+ File.unlink(temp_file_path)
10
+ end
11
+
12
+ context "no arguments" do
13
+ it "outputs usage info" do
14
+ output = `bundle exec bin/cmd2launchd`
15
+ expect(output.chomp).to include("Usage: ")
16
+ end
17
+ end
18
+
19
+ context "help option" do
20
+ it "outputs usage info" do
21
+ output = `bundle exec bin/cmd2launchd -h`
22
+ expect(output.chomp).to include("Usage: ")
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,82 @@
1
+ describe 'running launchd2cmd' do
2
+
3
+ context 'launch agent' do
4
+ it 'outputs the command' do
5
+ output = `bundle exec bin/launchd2cmd spec/fixtures/LaunchAgents/com.apple.storeagent.plist`
6
+ expect(output.chomp).to include("/System/Library/PrivateFrameworks/CommerceKit.framework/Versions/A/Resources/storeagent")
7
+ end
8
+ it "outputs a comment line with path to the plist" do
9
+ output = `bundle exec bin/launchd2cmd spec/fixtures/LaunchAgents/com.apple.storeagent.plist`
10
+ expect(output.chomp).to include("# #{File.expand_path("spec/fixtures/LaunchAgents/com.apple.storeagent.plist")}")
11
+ end
12
+ end
13
+
14
+ context "no arguments" do
15
+ it "outputs usage info" do
16
+ output = `bundle exec bin/launchd2cmd`
17
+ expect(output.chomp).to include("Usage: ")
18
+ end
19
+ end
20
+
21
+ context "help option" do
22
+ it "outputs usage info" do
23
+ output = `bundle exec bin/launchd2cmd -h`
24
+ expect(output.chomp).to include("Usage: ")
25
+ end
26
+ end
27
+
28
+ context "inaccessible file" do
29
+ it "outputs relevant error message" do
30
+ inaccessible_path = 'spec/fixtures/LaunchDaemons/inaccessible.plist'
31
+ File.chmod(0000, inaccessible_path)
32
+ output = `bundle exec bin/launchd2cmd #{inaccessible_path}`
33
+ File.chmod(0644, inaccessible_path)
34
+ require 'etc'
35
+ username = Etc.getpwuid(Process.euid).name
36
+ expect(output).to include("Error: user #{username} does not have access to read launchd job")
37
+ end
38
+ end
39
+
40
+ context "unparsable file" do
41
+ it "outputs relevant error message" do
42
+ output = `bundle exec bin/launchd2cmd #{__FILE__}`
43
+ expect(output).to include("Error: unable to parse launchd job")
44
+ end
45
+ end
46
+
47
+ context 'bad file paths' do
48
+ it "outputs relevant error message" do
49
+ output = `bundle exec bin/launchd2cmd not/a/real/path and/another/bad/path`
50
+ expect(output).to include("No launchd job found at 'not/a/real/path'")
51
+ expect(output).to include("No launchd job found at 'and/another/bad/path'")
52
+ end
53
+ end
54
+ context 'launch daemon' do
55
+ context 'with environment variables' do
56
+ it 'includes argument and environment variables in output' do
57
+ output = `bundle exec bin/launchd2cmd spec/fixtures/LaunchDaemons/org.apache.httpd.plist`
58
+ expect(output.chomp).to include("XPC_SERVICES_UNAVAILABLE=1 /usr/sbin/httpd -D FOREGROUND")
59
+ end
60
+ end
61
+ end
62
+ context "binary plist" do
63
+ it "handles it without error" do
64
+ output = `bundle exec bin/launchd2cmd spec/fixtures/LaunchDaemons/com.apple.opendirectoryd.plist`
65
+ expect(output.chomp).to include("__CFPREFERENCES_AVOID_DAEMON=1 __CF_USER_TEXT_ENCODING=0x0:0:0 /usr/libexec/opendirectoryd")
66
+ end
67
+ end
68
+
69
+ context "args are directories" do
70
+ it "lists contents of directory" do
71
+ command = "bundle exec bin/launchd2cmd spec/fixtures/LaunchAgents > /dev/null"
72
+ expect(system(command)).to be_true
73
+ end
74
+ end
75
+
76
+ context "program arguments contain spaces" do
77
+ it "escapes them for use in the shell" do
78
+ output = `bundle exec bin/launchd2cmd spec/fixtures/LaunchAgents/com.apple.dt.CommandLineTools.installondemand.plist`
79
+ expect(output.chomp).to include("/System/Library/CoreServices/Install\\ Command\\ Line\\ Developer\\ Tools.app/Contents/MacOS/Install\\ Command\\ Line\\ Developer\\ Tools")
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,17 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+
12
+ # Run specs in random order to surface order dependencies. If you find an
13
+ # order dependency and want to debug it, you can fix the order by providing
14
+ # the seed, which is printed after each run.
15
+ # --seed 1234
16
+ config.order = 'random'
17
+ end
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: launchd_tools
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - Kyle Crawford
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-23 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.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Provides tools for converting from command line arguments to a formatted
56
+ launchd plist and vice versa
57
+ email:
58
+ - kcrwfrd@gmail.com
59
+ executables:
60
+ - cmd2launchd
61
+ - launchd2cmd
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - ".gitignore"
66
+ - ".rspec"
67
+ - Gemfile
68
+ - LICENSE.txt
69
+ - README.md
70
+ - Rakefile
71
+ - TODO
72
+ - bin/cmd2launchd
73
+ - bin/launchd2cmd
74
+ - launchd_tools.gemspec
75
+ - lib/launchd_tools.rb
76
+ - lib/launchd_tools/cmd2launchd_cli.rb
77
+ - lib/launchd_tools/environment_parser.rb
78
+ - lib/launchd_tools/environment_variables.rb
79
+ - lib/launchd_tools/launchd2cmd_cli.rb
80
+ - lib/launchd_tools/launchd_job.rb
81
+ - lib/launchd_tools/launchd_plist.rb
82
+ - lib/launchd_tools/path.rb
83
+ - lib/launchd_tools/path_content.rb
84
+ - lib/launchd_tools/path_parser.rb
85
+ - lib/launchd_tools/program_args_parser.rb
86
+ - lib/launchd_tools/version.rb
87
+ - spec/fixtures/LaunchAgents/com.apple.dt.CommandLineTools.installondemand.plist
88
+ - spec/fixtures/LaunchAgents/com.apple.storeagent.plist
89
+ - spec/fixtures/LaunchDaemons/com.apple.opendirectoryd.plist
90
+ - spec/fixtures/LaunchDaemons/inaccessible.plist
91
+ - spec/fixtures/LaunchDaemons/org.apache.httpd.plist
92
+ - spec/integration/cmd2launchd_spec.rb
93
+ - spec/integration/launchd2cmd_spec.rb
94
+ - spec/spec_helper.rb
95
+ homepage: ''
96
+ licenses:
97
+ - MIT
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 2.2.1
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: launchd tools convert from command to launchd and launchd to command
119
+ test_files:
120
+ - spec/fixtures/LaunchAgents/com.apple.dt.CommandLineTools.installondemand.plist
121
+ - spec/fixtures/LaunchAgents/com.apple.storeagent.plist
122
+ - spec/fixtures/LaunchDaemons/com.apple.opendirectoryd.plist
123
+ - spec/fixtures/LaunchDaemons/inaccessible.plist
124
+ - spec/fixtures/LaunchDaemons/org.apache.httpd.plist
125
+ - spec/integration/cmd2launchd_spec.rb
126
+ - spec/integration/launchd2cmd_spec.rb
127
+ - spec/spec_helper.rb