tail -f findings.out

A better Ruby prompt

The Interactive Ruby Shell, or irb, is indispensable for trying out Ruby code rapidly, seeing what works and what’s elegant. But the defaults aren’t quite optimal.

Tab completion

It lacks tab completion by default. Having this available can save you a lot of time, especially as you are learning the language. To turn it on, simply add the option “-r irb/completion” to require that functionality. You can also add it to a running session by entering “require ‘irb/completion’”. After setting this you can press Tab after a dot following an object to see what methods are available for it (not all methods shown in image):

tab-complete

Now you can type the method you want to use from the list. You can also type part of a method name and press Tab to view matches on what you have so far.

Appearance

The default prompt can also be a little too wordy:

default-irb

This shows the program name (always “irb(main)” for the irb command), the line number within said program, and the indentation level. This last value is incremented as you move into loops and other multi-line structures. If you don’t want this meta information, however, just call irb with the “–simple prompt” option:

simple-irb

Aliases

I combine the above customizations into two Bash aliases for simpler access. Just add the following to your ~/.bashrc:

1
2
alias r="irb --simple-prompt -r irb/completion"
alias irb="irb -r irb/completion"

Now you can get to a simpler prompt for quick checks, or the full prompt for more complete documentation, making a tutorial, etc. And both have the lovely tab completion.

[EDIT, 2009-07-17]: As helpfully pointed out by commenter Vorian, the improvements above can also be attained using configuration options placed into ~/.irbrc. After hearing about this, I looked around for example .irbrc files and put together a decent one. Check out this post for more details.

Tags: , , ,
June 21, 2009 - 9:36 AM Comments (3)

Useful grep incantations

grep is one of the core utilities on nearly every *nix-based system. If you’ve been using Linux for more than the most casual activities, you’ve probably used it. While its basic use is quite simple, there are a few additional options and related utilities that can come in handy.

Don’t pipe cat’ed files to grep

Before covering the options, I’d like to note that it’s not necessary to cat a file and pipe its contents to grep in order to use it. For some reason this tends to be the early way people, including me, use grep:

1
cat file.txt | grep "string"

Instead, skip the middleman and send the file right to grep:

1
grep "string" file.txt

This has the same result, and saves you some characters.

Basic options

These are the more common, but still quite useful, options:

  • -i: Perform case-insensitive matching
  • -r: Look in subdirectories in the specified directory recursively
  • -v: Invert match set; useful for filtering out matches
  • -e PATTERN: Search for the specified regex pattern (The regex set in grep is somewhat limited. For a more full-featured implementation, check out egrep.)
  • -A NUM / -B NUM: Include NUM lines of context before (-B) and/or after (-A) the matching lines. -C NUM allows you to specify the number of lines to be included before and after matches.

Additional options

Here are some less common options and groups that I’ve found quite useful:

  • -l (lower case L): This only returns the filenames of files containing matches. To add filenames to the results and also show the matching lines, use -H.
  • -o: Returns only the matched part of matching lines. Useful with -i to see all case variations of a term. It’s also useful to include -n, to show the line numbers of the matching lines.
  • Putting these together, this recursively finds instances of FOO in current directory and children:
    1
    grep -Hnr FOO *

    This returns: FILENAME:LINENUM:LINE. And since I don’t like remembering groups of options, I created an alias:

    1
    alias grepword="grep -Hnr"

    grepword-example

  • -w: Only show lines matching with whole words, not parts of words.
  • -c: Instead of piping results to wc -l to find the number of matches, use -c to display this.

Coloration

I love using color on the command prompt to make the information presented clearer. grep facilitates this with two options. These can be set in ~/.bashrc:

1
2
3
4
# Turn on grep coloration:
export GREP_OPTIONS='--color=auto'
# Specify a light green color for matching words:
export GREP_COLOR='1;32'

Then run “. ~/.bashrc” to source the file, and try out your new grep:
grep coloring
These same values may be specified through command line options as well. See this page for details. For selection the right color, check out this reference script.

While grep is quite wonderful, there is actually something better for certain uses: ack-grep. Read more about it in this post. For everyday searching through files, I’ve found ack-grep to be much easier to use and more informative. It’s still essential to understand the original grep as well, in order to construct useful and interesting scripts.

Tags: ,
June 11, 2009 - 1:03 AM Comments (2)

Watching a fork bomb explode

Fork bombFork bombs are simple pieces of programming logic that can bring down the mightiest of servers in seconds (in fact, the faster the server, the faster it goes down). They are quite easy to protect against, but some operating systems aren’t protected by default. Below I provide an example of how fast an unprotected server is taken down by a fork bomb “going off”, as well as how to protect your own servers.

A fork bomb is simply a running process that forks other running processes, creating an exponential number of processes and rapidly using up all available processes. In other words: a program that spawns so many programs that the system becomes unusable, a denial of service attack. Fork bombs are a species of wabbit, a class of programs utilizing infinite self-replication but that aren’t worms or viruses. This bash implementation is the most famous:

1
:(){ :|:& };:

While fairly hard to read for those uninitiated in the school of arcane Bashery, it is elegant, and I find it quite beautiful. This page explains how it works in some detail. In short, a function is defined that calls itself, pipes its output to another instance of itself, then pushes that child into the background, allowing the function definition to terminate. Once the parent function is then called… the fun begins. In a somewhat less obscure representation:

1
2
3
bomb() {
 bomb | bomb &
}; bomb

There are implementations for many languages listed here. They work when an operating system doesn’t set a limit on how many processes a given user can spawn. To see if a system does have a limit set, run:

1
2
ulimit -u
unlimited

This was on Ubuntu 9.10 Desktop. Checking RHEL 5.2, CentOS 5 and 5.2, and Ubuntu 7.10 Server, there was a limit, on the order of tens of thousands of processes. Not limiting the number appropriately is unwise, for there definitely is a limit on that resource, in direct proportion with the amount of memory on your system. The file /proc/sys/kernel/threads-max provides the default allowed amount based on your physical memory. However, the 2.6.x kernel allows this value to be changed at runtime. For more information, check out TLDP’s section on kernel process management.

The Test

Now for the test. I had a server that needed to be decommissioned. It hadn’t been used for some time, everything needed was pulled off, sensitive things removed. All that was left was to press “Cancel Account” with the hosting company. But why such a boring demise? Instead I decided to test how fast a fork bomb would make the box unusable. I first created a file, “heartbeat”:

1
2
3
4
5
6
#!/bin/bash
while [ 1 ]
    do
        date; uptime
        sleep 1
    done

I would call this script on the remote server from my box. This would show me that it was alive every second, as well as what the load average was on the box. Then I created a fork bomb script, using the more memorable Python implementation:

1
2
3
4
5
#!/usr/bin/env python
import os
print "Bombs away."
while True:
    os.fork()

This too would be called on the remote server from my box. I started the heartbeat in one terminal, then fired off the fork bomb script in another. The result:

fork-bombed-small

In just a few seconds, the server was down. Not just stunned, mind you. On the floor in another dimension. Expiration was so rapid that my heartbeat script didn’t have time to register and return the clearly heightened load average. And I didn’t get to see the print statement output from the fork bomb script. To fix that, I might have changed line three to:

1
2
3
import sys
print "Bombs away."
sys.stdout.flush()

While we’re on the topic of improvements, I should have recorded the kernel version of the server. I believe it was CentOS 5.X, so likely a 2.6.X kernel. Another useful feature would have been to call both of these scripts from one script, first calling heartbeat, sleeping one second, printing an indicator, then calling the fork bomb script. If anyone tries this and gets a more accurate survival time, drop me a comment.

Protection against fork bombs

While the classic fork bomb shown above is something you aren’t likely to encounter without malicious users hanging around, the same sort of situation can arise by means of programming mistakes and poor test coverage as well. So how do you protect a server against this sort of vulnerability? It’s quite simple: set a limit on the number of processes users can initiate at any one time. I ran across several articles stating that to do this you need to edit /etc/security/limits.conf with sudo. This page describes the syntax of the file. The file should also contain an explanation in the comments at the top of the file. You’d likely want to add something similar to the following:

1
*    -    nproc    200

This will limit the number of processes that each user can initiate to 200. The “-” specifies that the value is both “hard” and “soft”. A hard value can’t be overridden with a higher number by the specified user(s), while a soft one is imposed on the user by default. “hard” and “soft” may be used separately if needed. You can also replace the * wildcard with a specific user or group, say if you have an untrusted group of users.

However, on my system (and a few others I looked at) these directives are ignored. To get the limits enforced, I had to add the following to /etc/profile:

1
ulimit -u 300

This limited the number of processes all users logging in would be able to spawn at any one time. It also capped the limit the user could set for himself.

As to selecting the limit amount, you’ll need to do some testing to fit your needs. On my Ubuntu desktop machines at home, I have 45 – 55 running processes that were spawned by my user at any one time. This includes GNOME, Firefox, conky, IM, twitter client, my terminals, etc. Basically a full-featured modern desktop. The machine serving this blog, without a GUI, has far less processes per user on average. So, something on the order of a few hundred processes is the max you’ll likely need to set. To see how many processes are active from your system’s users, you can use the following:

1
2
3
4
5
6
# View number of processes from all users:
sudo ps aux | sort | cut -f1 -d" " | wc -l
# View all unique users with processes
sudo ps aux | sort | cut -f1 -d" " | uniq
# Count of processes started by specified user:
sudo ps -U root | wc -l

You can also test out various limits and their effects via:

1
ulimit -u NUM

This only sets the limit temporarily. Once you log out, it goes back to the default. To make the change permanent, edit /etc/profile as described above. Once you have a limit in place, running a fork bomb will result in an error about resources not being available, instead of a dead box. Here I switch to a test user, set its limit of number of processes, then run a fork bomb:
Fork bomb with ulimit in place
The bomb continues until it reaches the process cap. At this point you can Ctrl+c out, saving your box.

A benefit to placing the limit in /etc/profile instead of limits.conf is that the root user is not subject to the limits placed in limits.conf. A disadvantage is that you lose the granularity of controlling limits per group and user.

Tags: , , ,
June 10, 2009 - 10:24 PM No Comments

Twitter links powered by Tweet This v1.6.1, a WordPress plugin for Twitter.