module Doable::Helpers::Linux

Public Instance Methods

add_bashrc_entry(user, entry) click to toggle source

Add an entry to a user’s ~/.bashrc @param user [String] User to add bashrc entries to @param entry [String] Entry to add to user’s .bashrc

# File lib/doable/helpers/linux.rb, line 58
def add_bashrc_entry(user, entry)
  File.open(File.expand_path("~#{user}/.bashrc"), "a") {|f| f.puts entry }
end
apply_patch(options, patch_file, path = nil, user = nil) click to toggle source

Apply a patch file @param options [String] Options to be added to the ‘patch` system command @param patch_file [String] Path to the patch file to apply @param path [String] Optionally cd to this directory before patching @param user [String] Optionally apply patch as this user

# File lib/doable/helpers/linux.rb, line 41
def apply_patch(options, patch_file, path = nil, user = nil)
  cmd = "patch #{options} < #{patch_file}"
    
  olddir = `pwd`.chomp
  Dir.chdir(path) if path
  tee 'pwd'
  if user
    run_as_user user, cmd
  else
    tee cmd
  end
  Dir.chdir(olddir) if path
end
check_disk_space(directory, min_space) click to toggle source

Finds the mount point for a directory, then checks it for a minimum number of kilobytes @param directory [String] Directory from which check starts @param min_space [Fixnum] Minumum amount of space required on mount-point discovered from install_dir

# File lib/doable/helpers/linux.rb, line 224
def check_disk_space(directory, min_space)
  all_filesystems = `df -PB 1024`.chomp.split("\n").collect {|fs| fs.split if fs.match(/^\/dev/) }.compact
  best_fit = nil
  current_test = directory
  until best_fit
    matching_fs = all_filesystems.collect {|fs| fs if File.expand_path(fs[5]) == File.expand_path(current_test) }.compact
    if matching_fs.size == 1
      best_fit = matching_fs.flatten
    else
      current_test = File.dirname(current_test)
    end
  end

  log "Closest matching filesystem is '#{best_fit[5]}'. Testing for free space..."
  if Integer(best_fit[3]) >= min_space
    log "Found sufficient space #{best_fit[3]} for install on #{best_fit[5]} with an install directory of #{install_dir}."
  else
    log "Insufficient space #{best_fit[3]} for install on #{best_fit[5]} with an install directory of #{install_dir}!", :error
  end
end
check_for_rpms(rpms) click to toggle source

Check for RPMs (kind of RHEL / SuSE specific) @raise [InvalidRPMList] @raise [NotApplicable] @raise [MissingRPMs] @param rpms [Array<String>] RPMs to verify return [Boolean]

# File lib/doable/helpers/linux.rb, line 101
def check_for_rpms(rpms)
  raise InvalidRPMList unless rpms.kind_of?(Array)
  # Split the list of all installed RPMs into an Array containing only the RPM's base name
  system_rpms = `rpm -qa`.chomp.split("\n").collect {|rpm| rpm.split(/-[0-9]/)[0]}
  i686_rpms = `rpm -qa`.chomp.split("\n").collect {|rpm| rpm.split(/-[0-9]/)[0] if rpm.match(/\.i686$/)}.compact
  raise NotApplicable unless $?.success?
  missing_rpms = rpms.collect {|rpm| rpm unless rpm.match(/\.i686$/) }.compact - system_rpms  # calculate what RPMs are not installed
  missing_rpms = (missing_rpms + (rpms.collect {|rpm| rpm.split('.')[0] if rpm.match(/\.i686$/) }.compact - i686_rpms).compact.map {|r| "#{r}.i686" } ).compact
  if missing_rpms.size > 0
    log "Missing RPMs: #{missing_rpms.join(', ')}", :error
    raise MissingRPMs
  else
    return true
  end
end
chmod(permission, file_list, options = {recursive: false}) click to toggle source

Used like Unix chmod @param permission [String, Fixnum] Permissions to set @param file_list [Array<String>,String] List of files to change the permissions of

# File lib/doable/helpers/linux.rb, line 26
def chmod(permission, file_list, options = {recursive: false})
  if options[:recursive]
    options.delete :recursive
    FileUtils.chmod_R(permission, file_list, options)
  else
    options.delete(:recursive) if options.has_key?(:recursive)
    FileUtils.chmod(permission, file_list, options)
  end
end
chown(user, group, file_list, options = {recursive: false}) click to toggle source

Used like Unix chown @param user [String] User to set as the owner @param group [String] Group to set as the owner @param file_list [Array<String>,String] List of files to change the owner of

# File lib/doable/helpers/linux.rb, line 13
def chown(user, group, file_list, options = {recursive: false})
  if options[:recursive]
    options.delete :recursive
    FileUtils.chown_R(user, group, file_list, options)
  else
    options.delete(:recursive) if options.has_key?(:recursive)
    FileUtils.chown(user, group, file_list, options)
  end
end
find_executable(name) click to toggle source

Require an executable to be in $PATH @raise [MissingExecutable] @param name [String] Required executable

# File lib/doable/helpers/linux.rb, line 91
def find_executable(name)
  have_executable?(name) ? true : raise(MissingExecutable, name)
end
find_or_create_group(group) click to toggle source

Ensure a group exists @raise [DuplicateGroups] @raise [FailedGroupAdd] @param group [String] Group to create or verify @return [Boolean]

# File lib/doable/helpers/linux.rb, line 189
def find_or_create_group(group)
  all_groups = `getent group`.chomp.split("\n").map {|line| line.split(':')}
  this_group = all_groups.collect {|g| g if g[0] == group}.compact
  if this_group.size > 1
    log "Multiple instances of group #{group} found. This is known to cause problems.", :error
    raise DuplicateGroups
  elsif this_group.size == 1
    log "Group '#{group}' already exists with gid: #{this_group.flatten[2]}. Skipping creation of group...", :warn
  else
    log "Creating group '#{group}'..."
    `groupadd -r "#{group}"`
    raise FailedGroupAdd unless $?.success?
    return $?.success?
  end
end
find_or_create_user(user, homedir, options = {}) click to toggle source

Ensure a user exists on the system @raise [DuplicateUsers] @raise [WrongHomedir] @param user [String] User to add or verify @param homedir [String] User home directory @return [Boolean]

# File lib/doable/helpers/linux.rb, line 123
def find_or_create_user(user, homedir, options = {})
  # Create an Array of Arrays storing all known system users and their attributes
  all_users = `getent passwd`.chomp.split("\n").map {|line| line.split(':')}
  # Determine if the user we want exists
  this_user = all_users.collect {|u| u if u[0] == user}.compact
  if this_user.size > 1
    log "Multiple instances of user #{user} found. This is known to cause problems.", :error
    raise DuplicateUsers
  elsif this_user.size == 1
    log "User '#{user}' already exists with uid: #{this_user.flatten[2]}. Skipping creation of user...", :warn
    full_home = File.expand_path(homedir)
    current_home = File.expand_path(this_user.flatten[5])
    if (full_home != current_home) and (homedir != current_home)
      log "User's home directory is set to #{current_home}, not #{homedir}!", :error
      raise WrongHomedir
    end
  else
    log "Creating #{user} user..."
    shell = options[:shell] ? options[:shell] : '/bin/bash'
    `useradd -d "#{homedir}" -s "#{shell}" -mr "#{user}"`
  end
          
  return true
end
find_processes(regex) click to toggle source

Get a list of process matching a Regexp @param regex [Regexp] Regular expression to use for finding processes @return [Array<String>] Array of PIDs that match the regex

# File lib/doable/helpers/linux.rb, line 65
def find_processes(regex)
  `ps aux`.chomp.split("\n").map {|l| l.split(nil, 11) }.collect {|p| p if p[10].match(regex)}.compact
end
find_user(user, noisy = false) click to toggle source

Die if a user doesn’t exist @raise [DuplicateUsers] @raise [MissingUser] @param user [String] User to verify @param noisy [Boolean] Should extra info be logged? @return [Array]

# File lib/doable/helpers/linux.rb, line 167
def find_user(user, noisy = false)
  # Create an Array of Arrays storing all known system users and their attributes
  all_users = `getent passwd`.chomp.split("\n").map {|line| line.split(':')}
  # Determine if the user we want exists
  this_user = all_users.collect {|u| u if u[0] == user}.compact
  if this_user.size > 1
    log "Multiple instances of user #{user} found. This is known to cause problems.", :error
    raise DuplicateUsers
  elsif this_user.size < 1
    log "Missing Critical User '#{user}'!", :error
    raise MissingUser
  else
    log "Found required user '#{user}' with uid #{this_user.flatten[2]}..." if noisy
  end
  return this_user.flatten
end
have_executable?(name) click to toggle source

Does the system have an executable in $PATH? If not, return false. @param name [String] Executable to verify @return [String, false]

# File lib/doable/helpers/linux.rb, line 79
def have_executable?(name)
  app_path = `which #{name}`.chomp
  if app_path.match(name)
    return app_path
  else
    return false
  end
end
kill(pid, signal = "TERM") click to toggle source

Kill a process @param pid [Fixnum,String] PID to kill @param signal [String] Signal to kill PID with

# File lib/doable/helpers/linux.rb, line 72
def kill(pid, signal = "TERM")
  Process.kill(signal, pid)
end
run_as_user(user, command) click to toggle source

Linux specific way to run something as another user. Command must be passed as a String. @param user [String] User that will run command @param command [String] Command to be run by user

# File lib/doable/helpers/linux.rb, line 248
def run_as_user(user, command)
  log "Running '#{command}' as '#{user}'..."
  tee("su -l #{user} -c \"#{command}\"")
end
set_nofile(user, limit) click to toggle source

Set the max number of open files for a user (nofiles) (sets both hard and soft, errors out if user is already listed in limits.conf) Currently does NOT verify the existence of the specified user on the system @param user [String] User to set ‘nofile’ ulimit security setting for @param limit [Fixnum] Limit to set for user @return [Boolean]

# File lib/doable/helpers/linux.rb, line 259
def set_nofile(user, limit)
  # See if the user is already in the limits.conf file
  user_entries = `grep -E '^#{user}(\s|\t)+' /etc/security/limits.conf`.chomp.split("\n")
  if user_entries.empty?
    # Doing this the lazy way for now... this is probably dangerous for very large files

    # Open and copy the current content of the limits file plus a few extra lines towards the end
    current_file  = File.readlines("/etc/security/limits.conf")
    temporary     = Tempfile.new("new_limits")
    current_file[0..(current_file.size - 2)].each {|l| temporary.write l }

    temporary.write "#{user}\t\tsoft\tnofile\t\t#{limit}\n"
    temporary.write "#{user}\t\thard\tnofile\t\t#{limit}\n"
    temporary.write "\n"
    temporary.write current_file[current_file.size - 1]
    temporary.rewind

    # Create a backup of the current file
    FileUtils.cp "/etc/security/limits.conf", "/etc/security/limits.conf.#{Time.now.to_i}"
    # Clear out the current file
    File.truncate("/etc/security/limits.conf", 0)
    # Copy the temporary (new) file to the existing
    current_file = File.new("/etc/security/limits.conf", "w")
    temporary.each {|l| current_file.puts l }
    # Close everything up
    temporary.close
    temporary.unlink
    current_file.close
    return true
  else
    log "User '#{user}' already has a specific entry in /etc/security/limits.conf. Ensure 'nofile' is at least #{limit}.", :warn
    return false
  end
end
set_user_password(user, new_pass) click to toggle source

Sets a user’s password @note This may only work on RHEL (and the like), as chpasswd may not be available on other OS’s @raise [FailedPasswordChange] @param user [String] User to set password for @param new_pass [String] New password for user @return [Boolean]

# File lib/doable/helpers/linux.rb, line 154
def set_user_password(user, new_pass)
  log "Setting password for #{user}..."
  `echo "#{user}:#{new_pass}" | chpasswd`
  raise FailedPasswordChange unless $?.success?
  return $?.success?
end
update_user_gid(user, gid) click to toggle source

Set a user’s gid (default group) @raise [FailedUserModification] @param user [String] User to update @param gid [String,Fixnum] Group to set as default for user

# File lib/doable/helpers/linux.rb, line 209
def update_user_gid(user, gid)
  user_details = find_user(user)
  if user_details[3] != gid
    log "Setting default group for #{user} to #{gid}..."
    `usermod -g #{gid} "#{user}"`
    raise FailedUserModification unless $?.success?
    log "Fixing permissions..."
    `chown -R #{user}:#{gid} "#{user_details[5]}"`
  end
  return gid
end