--- /dev/null
+module AIDS
+ RUNLEVEL_SINGLE = 'S'
+ RUNLEVEL_MULTI_TO_SINGLE = '1'
+ RUNLEVEL_MULTI = '2'
+ RUNLEVEL_HALT = '0'
+ RUNLEVEL_REBOOT = '6'
+
+ class Service
+ attr_reader :name
+
+ def initialize(name)
+ if name.nil? or name.empty? or not name.is_a?(String)
+ raise Exception.new("Service name must be a non-empty string, got #{name.inspect}.")
+ end
+ name = $1 if name =~ %r{^/etc/init\.d/(.+)$}
+ if name =~ /[^[:alnum:]\-.]/
+ raise Exception.new("Invalid init script name: #{name}.")
+ end
+ unless File.exist?("/etc/init.d/#{name}")
+ raise Exception.new("Unknown service: #{name}.")
+ end
+ @name = name
+ end
+
+ def enable!
+ start_on_runlevel!(RUNLEVEL_MULTI)
+ end
+
+ def disable!
+ stop_on_runlevel!(RUNLEVEL_MULTI)
+ end
+
+ def enabled?
+ started_on_runlevel?(RUNLEVEL_MULTI)
+ end
+
+ def status
+ if started_on_runlevel?(RUNLEVEL_MULTI)
+ :enabled
+ elsif stopped_on_runlevel?(RUNLEVEL_MULTI)
+ :disabled
+ else
+ :unknown
+ end
+ end
+
+ private
+
+ def started_on_runlevel?(runlevel)
+ AIDS.validate_runlevel(runlevel)
+ not Dir.glob("/etc/rc#{runlevel}.d/S[0-9][0-9]#{@name}").empty?
+ end
+
+ def stopped_on_runlevel?(runlevel)
+ AIDS.validate_runlevel(runlevel)
+ not Dir.glob("/etc/rc#{runlevel}.d/K[0-9][0-9]#{@name}").empty?
+ end
+
+ def start_on_runlevel!(runlevel)
+ AIDS.validate_runlevel(runlevel)
+ return true if started_on_runlevel?(runlevel)
+ updatercd(:enable, runlevel)
+ end
+
+ def stop_on_runlevel!(runlevel)
+ AIDS.validate_runlevel(runlevel)
+ return true if stopped_on_runlevel?(runlevel)
+ updatercd(:disable, runlevel)
+ end
+
+ def set_default_runlevels!
+ updatercd(:remove)
+ updatercd(:defaults)
+ end
+
+ def updatercd(action, runlevel=nil)
+ unless [:enable, :disable, :remove, :defaults].include?(action)
+ raise Exception.new("Invalid action for updatercd: #{action}.")
+ end
+ AIDS.validate_runlevel(runlevel) if runlevel
+ # update-rc.d will baulk at being told to do anything with
+ # these runlevels.
+ if ['0', '1', '6'].include?(runlevel.to_s)
+ raise Exception.new("Unable to comply: update-rc.d is balls.")
+ end
+ pid = Process.fork do
+ $stdout.close
+ $stderr.close
+ Kernel.exec(
+ '/usr/sbin/update-rc.d',
+ @name,
+ action.to_s,
+ runlevel.to_s
+ )
+ end
+ Process.wait(pid)
+ if (rc = $?.exitstatus) != 0
+ raise Exception.new("update-rc.d returned #{rc}")
+ end
+ end
+ end
+
+ def self.get_all_services
+ Dir.glob("/etc/init.d/*").map do |f|
+ next unless File.executable?(f)
+ Service.new(f)
+ end.compact
+ end
+
+ def self.validate_runlevel(runlevel)
+ unless ['0', '1', '2', '3', '4', '5', '6', 'S'].include?(runlevel.to_s)
+ raise Exception.new("Invalid runlevel: #{runlevel}.")
+ end
+ end
+end