module Doable::Helpers::Linux
Public Instance Methods
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 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
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 (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
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
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
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
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
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
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
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
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 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
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 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
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
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