Killing All Processes for a Given User

Problem

You want an easy way to kill all the running processes of a user whose processes get out of control.

Solution

You can send a Unix signal (including the deadly SIGTERM or the even deadlier SIGKILL) from Ruby with the Process.kill method. But how to get the list of processes for a given user? The simplest way is to call out to the unix ps command and parse the output. Running ps -u#{username} gives us the processes for a particular user.

	#!/usr/bin/ruby -w
	# banish.rb
	def signal_all(username, signal)
	 lookup_uid(username)
	 killed = 0
	 %x{ps -u#{username}}.each_with_index do |proc, i|
	 next if i == 0 # Skip the header provided by ps
	 pid = proc.split[0].to_i
	 begin
	 Process.kill(signal, pid)
	 rescue SystemCallError => e
	 raise e unless e.errno == Errno::ESRCH
	 end
	 killed += 1
	 end
	 return killed
	end

There are a couple things to look out for here.

  • ps dumps a big error message if we pass in the name of a nonexistent user. It would look better if we could handle that error ourselves. Thats what the call to lookup_uid will do.
  • ps prints out a header as its first line. We want to skip that line because it doesn represent a process.
  • Killing a process also kills all of its children. This can be a problem if the child process shows up later in the ps list: killing it again will raise a SystemCallError. We deal with that possibility by catching and ignoring that particular SystemCallError. We still count the process as "killed," though.

Heres the implementation of lookup_id:

	def lookup_uid(username)
	 require etc
	 begin
	 user = Etc.getpwnam(username)
	 rescue ArgumentError
	 raise ArgumentError, "No such user: #{username}"
	 end
	 return user.uid
	end

Now all that remains is the command-line interface:

	require optparse
	signal = "SIGHUP"
	opts = OptionParser.new do |opts|
	 opts.banner = "Usage: #{__FILE__} [-9] [USERNAME]"
	 opts.on("-9", "--with-extreme-prejudice",
	 "Send an uncatchable kill signal.") { signal = "SIGKILL" }
	end
	opts.parse!(ARGV)

	if ARGV.size != 1
	 $stderr.puts opts.banner
	 exit
	end

	username = ARGV[0]
	if username == "root"
	 $stderr.puts "Sorry, killing all of roots processes would bring down the system."
	 exit
	end
	puts "Killed #{signal_all(username, signal)} process(es)."

As root, you can do some serious damage with this tool:

	$ ./banish.rb peon
	5 process(es) killed

Discussion

The main problem with banish.rb as written is that it depends on an external program. Whats worse, it depends on parsing the human-readable output of an external program. For a quick script this is fine, but this would be more reliable as a self-contained program.

You can get a Ruby interface to the Unix process table by installing the sysproctable library. This makes it easy to treat the list of currently running processes as a Ruby data structure. Heres an alternate implementation of signal_all that uses sys-proctable instead of invoking a separate program. Note that, unlike the other implementation, this one actually uses the return value of lookup_uid:

	def signal_all(username, signal)
	 uid = lookup_uid(username)
	 require sys/proctable
	 killed = 0
	 Sys::ProcTable.ps.each do |proc|
	 if proc.uid == uid
	 begin
	 Process.kill(signal, proc.pid)
	 rescue SystemCallError => e
	 raise e unless e.errno == Errno::ESRCH
	 end
	 killed += 1
	 end
	 end
	 return killed
	end

See Also

  • sys-proctable is in the RAA at http://raa.ruby-lang.org/project/sys-proctable/; its one of the sysutils packages: see http://rubyforge.org/projects/sysutils for the others
  • To write an equivalent program for Windows, youd either use WMIthrough Rubys win32ole standard library, or install a native binary of GNUs ps and use win32-process


About the Authors

Lucas Carlson is a professional Ruby programmer who specializes in Rails web development. He has authored a half-dozen libraries and contributed to many others, including Rails and RedCloth. He lives in Portland, Oregon and maintains a web site at http://rufy.com/.

Leonard Richardson has been programming since he was eight years old. Recently, the quality of his code has improved somewhat. He is responsible for libraries in many languages, including Rubyful Soup. A California native, he now works in New York and maintains a web site at http://www.crummy.com/.



Colophon

The animal on the cover of Ruby Cookbook is a side-striped jackal (Canis adustus), found mostly in central and southern Africa. These jackals avoid the open, preferring thickly wooded areas on the edge of savannas and forests. They occasionally make their way into cities. Side-striped jackals are rare but not considered endangered. There are reserves for these jackals at the Serengeti National Park in Tanzania and at the Akagera National Park in Rwanda.

Side-striped jackals are about 15 inches tall and weigh between 16 and 26 pounds. This jackal has a light grey coat with a white stripe from shoulder to hip, and a white-tipped tail. The diet of side-striped jackals consists largely of wild fruits, small mammals, and insects. They also eat carrion and are adept scavengers; they will follow a lion or other big cat to a kill. The jackals usually live singly or in pairs, but they sometimes gather in family units of up to six members. Their lifespan is about 10 to 12 years.

Jackals have been an object of superstition because of their association with carrion and death, and because of their eerie nocturnal noises: they hoot, yap, and make a kind of screaming yell. Perhaps because jackals were often found prowling and hunting the edges of the desert near cemeteries, the ancient Egyptian god of embalming and gatekeeper of the path of the dead, Anubis, was depicted as a jackalheaded man. Anubis served as a psychopomp, conducting souls to the underworld, where he weighed their hearts on a scale to determine whether they would be admitted to the underworld or cast to the crocodile-headed demon, Ammit.

The cover image is from Lydekkers Royal History. The cover font is Adobe ITC Garamond. The text font is Linotype Birka; the heading font is Adobe Myriad Condensed; and the code font is LucasFonts TheSans Mono Condensed.

Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net