From: Steven McDonald Date: Sat, 13 Oct 2012 02:57:01 +0000 (+1100) Subject: Initial working prototype X-Git-Tag: v0.1~6 X-Git-Url: http://git.steven-mcdonald.id.au/?a=commitdiff_plain;h=16da2ea2a6c6d9b70ea3b3424d80b0f8a68b6cec;p=aids.git Initial working prototype --- 16da2ea2a6c6d9b70ea3b3424d80b0f8a68b6cec diff --git a/bin/aids b/bin/aids new file mode 100755 index 0000000..7b3fa50 --- /dev/null +++ b/bin/aids @@ -0,0 +1,48 @@ +#!/usr/bin/ruby1.8 + +require 'lib/aids' + +def main(argv) + svc = AIDS::Service.new(argv[1]) if argv[1] + case argv[0] + when 'enable' + usage unless svc + svc.enable! + when 'disable' + usage unless svc + svc.disable! + when 'list' + if svc + puts svc.status + else + AIDS.get_all_services.sort_by{|s|s.name}.each do |s| + puts "#{s.name}: #{s.status}" + end + end + else + usage + end +end + +def usage + $stderr.puts <<-EOF +AIDS - Assistant for Initialisation of Debian Services + +Usage: #{$0} command [service] + +Command may be one of: + + - enable + Enables the service. + + - disable + Disables the service. + + - list + Lists the current status of the service, or of all services if + none is provided. + EOF + exit 1 +end + +main(ARGV) if $0 == __FILE__ diff --git a/lib/aids.rb b/lib/aids.rb new file mode 100644 index 0000000..c2a7e66 --- /dev/null +++ b/lib/aids.rb @@ -0,0 +1,115 @@ +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