launchr 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/ext/extconf.rb ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'mkmf'
4
+ create_makefile("launchd-socket-listener-unload")
5
+
6
+ makefile = File.read("Makefile")
7
+ makefile.gsub!(/-bundle ?/,"")
8
+ makefile.gsub!(/\$\(TARGET\)\.bundle/,"$(TARGET)")
9
+
10
+ File.open("Makefile","w") do |f|
11
+ f.puts makefile
12
+ end
13
+
14
+
@@ -0,0 +1,160 @@
1
+ //
2
+ // launchd-socket-listener-unload.cpp
3
+ // http://fazekasmiklos.blogspot.com/2007/04/howto-unload-launchd-deamon-from-itself.html
4
+ //
5
+ // Instructions by Dreamcat4 (dreamcat4@gmail.com)
6
+ //
7
+ // To compile:
8
+ // $ g++ launchd-socket-listener-unload.cpp -o launchd-socket-listener-unload
9
+ //
10
+ // A small program which unloads the launchd job that started it
11
+ // Use with the below configuration to trigger the real job to load
12
+ //
13
+ // Create a plist file for this daemon called "launchd.myjob.label-loader.plist"
14
+ // Put the listener socket definitions here
15
+ //
16
+ //
17
+ // <key>Program</key>
18
+ // <string>/path/to/launchd-socket-listener-unload</string>
19
+ // <key>ServiceIPC</key>
20
+ // <true/>
21
+ // <key>Sockets</key>
22
+ // <dict>
23
+ // <key>Listeners</key>
24
+ // <dict>
25
+ // <!-- Service name (/etc/services), or a tcp port number -->
26
+ // <!-- or can be anything else launchd supports (udp,etc) -->
27
+ // <key>SockServiceName</key>
28
+ // <string>8080</string>
29
+ // </dict>
30
+ // </dict>
31
+ //
32
+ // You probably already have made a 2nd plist file called "launchd_myjob_label.plist"
33
+ // Add to this file a KeepAlive directive, which depends on the first job not running
34
+ //
35
+ // keep alive -> other job enabled -> false
36
+ //
37
+ // <key>KeepAlive</key>
38
+ // <dict>
39
+ // <key>OtherJobEnabled</key>
40
+ // <dict>
41
+ // <key>launchd.myjob.label-loader</key>
42
+ // <false/>
43
+ // </dict>
44
+ // </dict>
45
+ //
46
+ //
47
+ // This job will then start when the first job is unloaded, which is
48
+ // whenever the first job is triggered by one of the listener sockets
49
+ //
50
+ // Bear in mind,
51
+ // The very first http request / connection attempt will be dropped
52
+ // So a connecting client will have to try again in order to establish
53
+ // a connection to the real job.
54
+
55
+ #include <stdlib.h>
56
+ #include <errno.h>
57
+ #include <syslog.h>
58
+
59
+ #include "launch.h"
60
+
61
+ class LaunchD
62
+ {
63
+ public:
64
+ LaunchD()
65
+ {
66
+ startedWithLaunchD = false;
67
+ me = 0;
68
+ CheckIn();
69
+ }
70
+
71
+ ~LaunchD()
72
+ {
73
+ if (me) {
74
+ launch_data_free(me);
75
+ }
76
+ }
77
+
78
+ bool CheckIn(bool allowRunWithoutLaunchd = true)
79
+ {
80
+ launch_data_t msg, resp;
81
+ msg = launch_data_new_string(LAUNCH_KEY_CHECKIN);
82
+ if ((resp = launch_msg(msg)) == NULL) {
83
+ if (allowRunWithoutLaunchd) {
84
+ startedWithLaunchD = false;
85
+ return false;
86
+ }
87
+ syslog(LOG_ERR,"Checkin with launchd failed: %m");
88
+ exit(EXIT_FAILURE);
89
+ }
90
+ launch_data_free(msg);
91
+ if (LAUNCH_DATA_ERRNO == launch_data_get_type(resp)) {
92
+ errno = launch_data_get_errno(resp);
93
+ if (errno == EACCES) {
94
+ syslog(LOG_ERR, "Check-in failed. Did you forget to set"
95
+ "ServiceIPC == true in your plist?");
96
+ } else {
97
+ syslog(LOG_ERR, "Check-in failed: %m");
98
+ }
99
+ exit(EXIT_FAILURE);
100
+ }
101
+ launch_data_t tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_LABEL);
102
+ me = launch_data_copy(tmp);
103
+ if(me)
104
+ {
105
+ syslog(LOG_ERR, "%s triggered\n",job_label());
106
+ }
107
+ startedWithLaunchD = true;
108
+ return true;
109
+ }
110
+
111
+ const char* job_label()
112
+ {
113
+ return launch_data_get_string(me);
114
+ }
115
+
116
+ bool Stop()
117
+ {
118
+ if (startedWithLaunchD) {
119
+ launch_data_t resp;
120
+ launch_data_t msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
121
+ if (! launch_data_dict_insert(msg,me,LAUNCH_KEY_REMOVEJOB)) {
122
+ syslog(LOG_ERR, "launch_data_dict_insert failed!\n");
123
+ return false;
124
+ }
125
+ if (! launch_data_dict_insert(msg,me,LAUNCH_KEY_STOPJOB)) {
126
+ syslog(LOG_ERR, "launch_data_dict_insert failed!\n");
127
+ return false;
128
+ }
129
+ syslog(LOG_ERR, "%s unloaded\n", job_label());
130
+ resp = launch_msg(msg);
131
+ syslog(LOG_ERR, "%s ...not unloaded. Failure when trying to unload %s.\n", job_label(), job_label());
132
+ if (resp == NULL) {
133
+ syslog(LOG_ERR, "launch_msg() LAUNCH_KEY_STOPJOB failed!\n");
134
+ return false;
135
+ }
136
+ if (LAUNCH_DATA_ERRNO == launch_data_get_type(resp)) {
137
+ errno = launch_data_get_errno(resp);
138
+ if (errno == EACCES) {
139
+ syslog(LOG_ERR, "Stop request failed EACCESS!");
140
+ } else {
141
+ syslog(LOG_ERR, "Check-in failed: %m");
142
+ }
143
+ exit(EXIT_FAILURE);
144
+ }
145
+ launch_data_free(msg);
146
+ }
147
+ return true;
148
+ }
149
+ private:
150
+ bool startedWithLaunchD;
151
+ launch_data_t me;
152
+ };
153
+
154
+
155
+ int main()
156
+ {
157
+ LaunchD ld;
158
+ ld.Stop();
159
+ }
160
+
@@ -0,0 +1,9 @@
1
+ Feature: something something
2
+ In order to something something
3
+ A user something something
4
+ something something something
5
+
6
+ Scenario: something something
7
+ Given inspiration
8
+ When I create a sweet new gem
9
+ Then everyone should see how awesome I am
File without changes
@@ -0,0 +1,14 @@
1
+ require 'bundler'
2
+ begin
3
+ Bundler.setup(:runtime, :development)
4
+ rescue Bundler::BundlerError => e
5
+ $stderr.puts e.message
6
+ $stderr.puts "Run `bundle install` to install missing gems"
7
+ exit e.status_code
8
+ end
9
+
10
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
11
+ require 'launchr'
12
+
13
+ require 'spec/expectations'
14
+
data/lib/launchr.rb ADDED
@@ -0,0 +1,105 @@
1
+
2
+ dir = File.dirname(__FILE__)
3
+ $LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir)
4
+
5
+ require 'launchr/path'
6
+
7
+ module Launchr
8
+ class << self
9
+ def config
10
+ @config ||= {}
11
+ end
12
+
13
+ def load_default
14
+ if Launchr::Path.launchr_default_boot.exist?
15
+ config[:boot] = true
16
+ else
17
+ config[:boot] = nil
18
+ end
19
+ end
20
+
21
+ def import_args
22
+ if config[:args][:user]
23
+ config[:boot] = nil
24
+
25
+ elsif config[:args][:boot]
26
+ config[:boot] = true
27
+ end
28
+ end
29
+
30
+ def label
31
+ # "com.github.homebrew.launchr"
32
+ "com.github.launchr"
33
+ end
34
+
35
+ def launchctl_timeout
36
+ 5
37
+ end
38
+
39
+ def real_user
40
+ Etc.getpwuid(Process.uid).name
41
+ end
42
+
43
+ def real_group
44
+ Etc.getgrgid(Process.gid).name
45
+ end
46
+
47
+ def superuser?
48
+ real_user == "root"
49
+ end
50
+
51
+ def sudo_user
52
+ if ENV["SUDO_USER"]
53
+ ENV["SUDO_USER"]
54
+ elsif ENV["SUDO_UID"]
55
+ Etc.getpwuid(ENV["SUDO_UID"].to_i).name
56
+ else
57
+ nil
58
+ end
59
+ end
60
+
61
+ def sudo_group
62
+ if ENV["SUDO_GROUP"]
63
+ ENV["SUDO_GROUP"]
64
+ elsif ENV["SUDO_GID"]
65
+ Etc.getgrgid(ENV["SUDO_GID"].to_i).name
66
+ else
67
+ nil
68
+ end
69
+ end
70
+
71
+ def user
72
+ if superuser?
73
+ sudo_user || real_user
74
+ else
75
+ real_user
76
+ end
77
+ end
78
+
79
+ def group
80
+ if superuser?
81
+ sudo_group || real_group
82
+ else
83
+ real_group
84
+ end
85
+ end
86
+
87
+ def uid
88
+ Etc.getpwnam(user).uid
89
+ end
90
+
91
+ def gid
92
+ Etc.getgrnam(group).gid
93
+ end
94
+
95
+ def version
96
+ if Launchr::Path.launchr_version.exist?
97
+ Launchr::Path.launchr_version.read.strip
98
+ else
99
+ "0.0.0"
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+
@@ -0,0 +1,24 @@
1
+
2
+ require 'launchr/cli'
3
+ require 'launchr/commands'
4
+
5
+ module Launchr
6
+ # The Launchr Application Object. Instantiated for command-line mode
7
+ # @see Launchr::CLI
8
+ class Application
9
+
10
+ def initialize *args, &blk
11
+ @cli = Launchr::CLI.new
12
+
13
+ Launchr.load_default
14
+
15
+ Launchr.config[:args] = @cli.parse
16
+ Launchr.import_args
17
+
18
+ @commands = Launchr::Commands.new
19
+ @commands.run
20
+ end
21
+ end
22
+ end
23
+
24
+
@@ -0,0 +1,142 @@
1
+
2
+ require 'launchr/mixin/mixlib_cli'
3
+
4
+ module Launchr
5
+ # Defines options for the +launchr+ command line utility
6
+ class CLI
7
+ include Launchr::Mixlib::CLI
8
+
9
+ # The Launchr CLI Options
10
+ def self.launchr_cli_options
11
+
12
+ spaced_summary true
13
+ # banner false
14
+ summary_indent ""
15
+ summary_width 29
16
+
17
+ header "brew launchd - an extension to start and stop Launchd services."
18
+ header " `man brew-launchd` for more information"
19
+ header ""
20
+ banner "Usage: brew launchd [options]"
21
+
22
+ argument :start,
23
+ :long => "start service,(s)",
24
+ :type => Array,
25
+ :description => ["Start launchd service(s)",
26
+ "Equivalent to launchctl load -w files..."],
27
+ :example => "start dnsmasq memcached couchdb",
28
+ :default => nil
29
+
30
+ argument :stop,
31
+ :long => "stop service,(s)",
32
+ :type => Array,
33
+ :description => ["Stop launchd service(s)",
34
+ "Equivalent to launchctl unload -w files..."],
35
+ :example => "stop mamcached dnsmasq",
36
+ :default => nil
37
+
38
+ argument :restart,
39
+ :long => "restart service,(s)",
40
+ :type => Array,
41
+ :description => ["Restart launchd service(s)"],
42
+ :example => "restart couchdb",
43
+ :default => nil
44
+
45
+ option :user,
46
+ :indent => true,
47
+ :long => "--user",
48
+ :description => ["At user login.",
49
+ "Otherwise, the default setting will be used."],
50
+ :example => "start --user openvpn ddclient",
51
+ :requires => Proc.new { |args| (args[:boot] ^ args[:user]) && true || raise("--boot|--user") },
52
+ :default => nil
53
+
54
+ option :boot,
55
+ :indent => true,
56
+ :long => "--boot",
57
+ :description => ["At boot time. Requires sudo/root privelidges.",
58
+ "Otherwise, the default setting will be used."],
59
+ :example => "sudo brew start --boot nginx mysql",
60
+ :requires => Proc.new { |args| (args[:boot] ^ args[:user]) && true || raise("--boot|--user") },
61
+ :default => nil
62
+
63
+
64
+ argument :info,
65
+ :long => "info [service,(s)]",
66
+ :type => Array,
67
+ :proc => Proc.new { |l| (l == true) ? [] : l },
68
+ :description => ["Info for launchd service(s)","With no arguments prints info for all services."],
69
+ :example => "brew launchd info",
70
+ :default => nil
71
+
72
+ argument :clean,
73
+ :long => "clean",
74
+ :description => ["Clean missing/broken launchd service(s)."],
75
+ :example => ["brew launchd clean", "sudo brew launchd clean"],
76
+ :default => nil
77
+
78
+ argument :default,
79
+ :long => "default [--user|--boot]",
80
+ :description => [
81
+ "Set the default target to start launchd services.",
82
+ "The initial setting, --user will start daemons at",
83
+ "user login - from the Loginwindow (not over ssh).",
84
+ " ",
85
+ "Wheras --boot will set services to start at boot",
86
+ "time. But be aware that brew should be installed",
87
+ "to the root filesystem, not on a mounted volume."],
88
+
89
+ :example => [
90
+ "brew launchd default --boot",
91
+ "brew launchd default --user"
92
+ ],
93
+ :requires => Proc.new { |args| (args[:boot] ^ args[:user]) && true || raise("--boot|--user") },
94
+ :default => nil
95
+
96
+ option :help,
97
+ :long => "--help",
98
+ :description => "Show this message",
99
+ :show_options => true,
100
+ :exit => 0
101
+
102
+ option :version,
103
+ :long => "--version",
104
+ :description => "Print version information",
105
+ :default => nil
106
+ end
107
+ launchr_cli_options
108
+
109
+ def parse argv=ARGV
110
+ parse_options(argv)
111
+
112
+ [:start,:stop,:restart].each do |cmd|
113
+ case config[cmd]
114
+ when Array
115
+ [:user, :boot].each do |level|
116
+ if config[cmd].include? "--#{level}"
117
+ config[level] = true
118
+ end
119
+ end
120
+ config[cmd] -= ["--user","--boot"]
121
+ end
122
+ end
123
+
124
+ raise "Please choose one of --user|--boot" if config[:user] && config[:boot]
125
+
126
+ unless filtered_argv.empty?
127
+ start_stop_restart_value = [config[:start],config[:stop],config[:restart],config[:info]].compact!
128
+
129
+ if start_stop_restart_value.size == 1
130
+ services = *start_stop_restart_value
131
+ extra_services = filtered_argv
132
+
133
+ services << extra_services
134
+ services.flatten!
135
+ end
136
+ end
137
+
138
+ config
139
+ end
140
+
141
+ end
142
+ end