Image of the glider from the Game of Life by John Conway
Skip to content

{ pts“” } Search Results

New Job

Well, today was the completion of Day 2 on the job. As many of you are aware, I was laid off from my job as a Linux instructor for Guru Labs last month. There is no doubt in my mind that being a Guru was the best job in the planet, so losing that job has introduced depression into my life. It doesn't matter the job, but it will always rank second to Guru Labs.

However, let me not put my new job in a bad light. It might be second place, but it's trailing first by mere inches. However, as with a close friend of mine, I would like to keep this job anonymous. This will give me a little freedom to post topics that are close to my heart, and share stories and experiences without putting my employer in the spotlight, or putting my job at risk. Don't get me wrong- I'm not going to post sensitive information, or any of that. Rather, I'll be able to keep my opinions as my opinions, and not those of my employer.

However, with that, I'll tell you that I am now working as a contractor to do system administration. I am a Red Hat / HPUX / Solaris system administrator. My job duties include those of most standard system administrators- rotating backups, installing new systems, configuring software, writing scripts, adding accounts and so forth. I administer around 35-40 servers for a semi-large development team, a database team and a number of configuration managers. My team is rather small, which encompasses a team lead (my boss), 3 system admins and 2 configuration managers. Today, my task was updating a RHEL 4 box with the latest versions of Python, Apache2 and PostgreSQL to power Trac.

With my new job, I'm happy. Very happy. This job seems to be at a reasonable pace, the team is very friendly, and the duties that I'll be covering are vast. I'll be able to get my hands dirty in many aspects of administration, such as storage management, networking, databases, installation, configuration, security and other things. Everything I taught to my students as an instructor, I'll be able to put to practice. Setting up LDAP with Kerberos? Sure. Customizing SELinux policy? You bet! Playing with tape robots? Of course. If the contract stays in play, or the company I'm contracting for picks me up, I could see myself staying there for some time. When a job is fun, it's hard to let go of the job.

GNU Shred

So, due to the bad shape of the economy I was let go from my position as a Linux instructor and guru. As unfortunate as it is, I have to press forward looking for the best job that fits my skill set.

However, the point of this post isn't to complain about being laid off, or the bad shape of the economy. Rather, while employed, I was given a 120 GB LaCie Rugged hard drive that was called the "gurudisk" (being a "Linux Guru" from "Guru Labs". Get it?). The gurudisk had everything on it necessary for easing the installation of Linux on computers. Kickstart and AutoYast files were used for automating the install of the instructor machine, while scripts and RPMs were used to automate the configuration and additional software installation of the instructor machine, and DHCP, DNS, TFTP and PXE, along with Kickstart and AutoYast files, were used for automating student machines. Using the gurudisk, I could do a full classroom install, complete with instructor machine and 20 student machines, in under an hour. The gurudisk held RHEL 5, RHEL 5.1, Fedora 6, SLES 10, SUSE Linux 10.1 and Oracle 4.5 disk ISOs and software, as well as RPMs, scripts and config files. It was truly a welcomed companion.

However, all of that can easily fit in 40 GB of space, so what to do with the rest of the 80 GB? Well, most of us began using that space for personal data. Music, videos, scripts, documents and so forth. I'm not one to carry music or movies with me, so that didn't interest me much. Rather, I wanted the ability to take the gurudisk further with using Ubuntu and Debian. So, I had an "isos" directory on my gurudisk, where I kept more updated ISOs, including RHEL 5.2, Fedora 9 and 10, Ubuntu 8.04 LTS, Debian 4.0, openSUSE 11, OpenSolaris, FreeBSD, OpenBSD, and others. At one point, I had an entire Ubuntu repository mirroring 8.04 and 8.10 on the gurudisk. Lastly, if that's not enough, I had VMWare, KVM, Xen and VirtualBox virtual machines with clean, vanilla installations of a few of the major distributions. I took advantage of my space, and it also came to the welcome approval of many students.

When news came yesterday that I had lost my job, and that I would need to turn in my gurudisk, I wanted to first get my Ubuntu mirror, virtual machines and ISOs off the disk. Then, I wanted to experience, first hand, "shredding" the data on the disk. Thus, we have now reached the topic of this post- GNU shred.

I had heard from students over and over again that zeroing out the drive using /dev/zero is not sufficient for secure data deletion. I full heartedly disagree, and I'm sure I'll bring out the emotion of many of you in the comments. Here's why I think /dev/zero is more than sufficient for secure data destruction:

  • On older dive encoding schemes, mainly RLL and MFM, data was not written in exactly the same spot every time. As such, there was left over charge from the previous write, and expensive data recovery hardware could use math and averages to discover what the data once was. As such, a method known as the "Gutmann Method" became the standard of destroying data. Patters of ones and zeros would be written to the disk, in such a way that maximizing flipping the bits, minimizing the average left over charge. After seven passes, the residual charge would be so minute, that it would be virtually impossible to recover the data. Do 35 passes, and the data is gone for sure.
  • Drives today do not use RLL or MFM encoding, and also, the bits are much more close together then they were in days gone by. The data has to be written in exactly the same spot, or data destruction is likely on other data existing on the disk. As such, there is no left over residual charge from rewriting the data. A single pass over existing data removes any existence of that data.
  • Supposedly, top secret, mega government, super computers administered my corporations with endless amounts of cash flow can recover data on ATA drives, even after seven passes. However, there has been no academic study, no scientific evidence, no hard cold proof. All we have is hearsay and rumors of people we know claimed to recover the data using these killer machines or algorithms on ATA drives.

So, with that in mind, after backing up my data, a single zeroing of the entire drive would be more than sufficient for a couple reasons. First, my bosses and company don't have the resources, the time and money, or the care to recover any data off of my gurudisk. Second, the data I was deleting wasn't necessarily personal, as no passwords or private keys or information was stored on the disk. So, even if the data could be recovered, of what use would it be to anyone? Little, if any. Chances are good that the drive will sit on a shelf, unattended and unused, and when it does make it back into commission, it will just be formatted with ext3, files put on, and used as any other drive. So, /dev/zero it is.

Or not.

I'm a mathematician at heart. I love math and logic puzzles as well as cryptography and many an algorithm. If I had the time and money, I would finish college, and get a Doctorate in Mathematics. However, that's a dream that just isn't realistic at this point in my life, but I still enjoy pulling out my HP49G+, and crunching the numbers. So, the algorithm used in Gutmann's Method is interesting. More interesting are the pseudo-random number generators used in cryptographic applications. So, I decided to give GNU shred a try, seeing as though it's part of coreutils, and see what the result is. I ran the following command:

shred -v -z /dev/sdc

This means that GNU shred will make 26 total passes, with the 26th pass being straight zeros to hide the fact that the disk has been shredded. Once finished, I'll add one final pass as an easter egg to the next person who gets the gurudisk. So, 27 total passes to the disk. What I'm mostly interested in, is the time it will take to finish. From my understanding, it will write pseudo-random numbers to the disk on the first, middle and second-to-last passes, due to passing '-z' to zero the shred. Writing random data to 120 GB of disk is going to take some time. In fact, I timed it, and it took 5 hours and 20 minutes. Which means it will last at least 16 hours to run to completion. But then there is the one and zero pattern writing that will take place in between. I would expect this to go substantially faster than writing random data, and it does- about three times as fast. Three passes can be completed in 5 hours and 20 minutes, give or take, based on the pattern. There are 23 final passes at this rate, which is approximately 41 hours. Add the 16 on top of that, and it's going to take 58 total hours to complete all 26 passes. That's almost 2 and a half days! In fact, as I'm writing this, it's 18:00 the next day, and I'm only on pass 11, writing the pattern "333333" in hexadecimal to the disk. The next pass will be my second random data pass. When I get out of bet tomorrow, I expect to be on pass 18, give or take.

I figure, even though I'm long past any possible data retrieval, it's fun to watch. Even more entertaining is the heat emanating off of the disk- it's fairly warm, which I guess makes sense, as the disks have been going non-stop for almost 24 hours. Would I recommend GNU shred for wiping your data? No. Again, /dev/zero will be more than sufficient, and fast too, at roughly 30 MB per second on a SATA or USB 2.0 disk. Which, by the way, this disk is connected via FireWire 400 (I'm not a fan of the USB speed burst). I'd love to see this run to completion, but I'll probably cancel it sometime tomorrow morning, install my easter egg, then be on my way to return the disk.

Long live hacking!

My ZSH Prompt Long Overdue

About a month ago, while teaching a class in Philadelphia, a student in my class had an interesting BASH prompt. In fact, not only had he customized a prompt for BASH, but one for CSH and KSH as well, as apparently, he spends adequate time in those shells during work. Well, this got me rethinking about my own ZSH prompt, and how I wanted to customize it to fit my needs. However, as things go with teaching on the road, I got a bit lazy, and didn't do much about it. Then, last week, while teaching a Linux security course, I had another student with an interesting take on his prompts. That must have been the straw that broke the camel's back, because I was determined by Friday to have my customized ZSH prompt.

First, the question begs why- why spend so much time configuring a silly prompt? Surely you can get the information you need from commands in the terminal. While this is true, I spend so much time in my terminal, that I want it to be functional, and well as attractive. After all, I probably spend three quarters of my working day behind the terminal. So, I might as well make it as informative as I can. With that, let's see what I've done to my prompt. First, a simple screenshot showing the cleanliness of the default promt:

Basic ZSH prompt

Here's what I wanted behind my prompt:

  • A matching theme with my .screenrc, Mutt theme and 88_madcows Irssi theme (yes, they all tie in together).
  • The continuation prompt should carry on the theme of the main prompt.
  • My username and host that I'm connected to
  • The history number of the last command executed.
  • A timestamp showing when I executed the last command.
  • The prompt should never be wider than the terminal we're attached to (we may need to truncate the path).
  • Display statuses of the following, only when needed:
  • Battery status of my laptop, if less than 100%.
  • The current GIT branch, if any.
  • The exit code of the last command, if any other than zero.
  • The screen number I'm attached to, if behind screen.
  • The number of jobs executing in the background, if any.

That's it. Not much really. Most of it is fairly straight forward. Some of those points will require some hacking, and a bit of logic, but for the most part, this shouldn't be too bad. So, with that, let's start dissecting my code line-by-line.

At first glance, you'll notice that I'm using two functions to pull this off. The first function is a special ZSH function called 'precmd()'. This function is always executed before a new prompt is drawn. Because we'll be creating custom variables, and using them in our prompt, we'll need to take advantage of this function. If we don't then we'll have to source our .zshrc every time we want our dynamic prompt updated. The second function is what actually draws the prompt. We'll look at that a bit later.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
    # let's get the current get branch that we are under
    # ripped from /etc/bash_completion.d/git from the git devs
    git_ps1 () {
        if which git > /dev/null; then
            local g="$(git rev-parse --git-dir 2>/dev/null)"
            if [ -n "$g" ]; then
                local r
                local b
                if [ -d "$g/rebase-apply" ]; then
                    if test -f "$g/rebase-apply/rebasing"; then
                        r="|REBASE"
                    elif test -f "$g/rebase-apply/applying"; then
                        r="|AM"
                    else
                        r="|AM/REBASE"
                    fi
                    b="$(git symbolic-ref HEAD 2>/dev/null)"
                elif [ -f "$g/rebase-merge/interactive" ]; then
                    r="|REBASE-i"
                    b="$(cat "$g/rebase-merge/head-name")"
                elif [ -d "$g/rebase-merge" ]; then
                    r="|REBASE-m"
                    b="$(cat "$g/rebase-merge/head-name")"
                elif [ -f "$g/MERGE_HEAD" ]; then
                    r="|MERGING"
                    b="$(git symbolic-ref HEAD 2>/dev/null)"
                else
                    if [ -f "$g/BISECT_LOG" ]; then
                        r="|BISECTING"
                    fi
                    if ! b="$(git symbolic-ref HEAD 2>/dev/null)"; then
                       if ! b="$(git describe --exact-match HEAD 2>/dev/null)"; then
                          b="$(cut -c1-7 "$g/HEAD")..."
                       fi
                    fi
                fi
                if [ -n "$1" ]; then
                     printf "$1" "${b##refs/heads/}$r"
                else
                     printf "%s" "${b##refs/heads/}$r"
                fi
            fi
        else
            printf ""
        fi
    }

    GITBRANCH=" $(git_ps1)"

Before I even begin, I want to start off with determining my GIT branch that I'm currently working on, if any. I use GIT extensively at work, and I've even setup my own GIT repository at home, to track my own config files and custom scripts that I want to hold on to. As such, if I'm ever on a GIT branch, I want that displayed in my prompt. As noticed in the comment before the code, this was pulled directly from the BASH completion package from the GIT developers? Why the extensive code? Well, when doing a rebase on the branch, I want to make sure I'm keeping track of my current location, as HEAD won't always be reliable.

1
2
3
4
5
6
7
8
9
10
11
    # The following 9 lines of code comes directly from Phil!'s ZSH prompt
    # http://aperiodic.net/phil/prompt/
    local TERMWIDTH
    (( TERMWIDTH = ${COLUMNS} - 1 ))

    local PROMPTSIZE=${#${(%):--- %D{%R.%S %a %b %d %Y}\! ---}}
    local PWDSIZE=${#${(%):-%~}}

    if [[ "$PROMPTSIZE + $PWDSIZE" -gt $TERMWIDTH ]]; then
    (( PR_PWDLEN = $TERMWIDTH - $PROMPTSIZE ))
    fi

Next, it's time to determine the terminal width. As mentioned above, I don't want my prompt to be longer than my terminal and wrap, so as such, I need some logic to figure this out. After several hours of hacking at this, I couldn't get it right. So, after a bit of searching, I found Phil Gold's ZSH prompt, and decided to copy the code verbatim, and modify it to fit my needs. Phil is using a right hand prompt, and I'm not. As such, there was some code I could cut out, to get exactly what I needed. He does a good job explaining the logic, so I'll let you read it all there.

1
2
3
4
5
6
    # set a simple variable to show when in screen
    if [[ -n "${WINDOW}" ]]; then
        SCREEN=" S:${WINDOW}"
    else
        SCREEN=""
    fi

Screen is my best friend. I use it to the point, where I only need one terminal open for all my separation needs. However, as much as I use it, it's not open all the time. As such, when it's open, I want to show it in my prompt. When it is not open, I don't want it cluttering the prompt up. As such, if a value exists in the $WINDOW variable (this is set when screen is executed, then I set the $SCREEN variable. Otherwise, I don't bother.

1
2
3
4
5
6
    # check if jobs are executing
    if [[ $(jobs | wc -l) -gt 0 ]]; then
        JOBS=" J:%j"
    else
        JOBS=""
    fi

I seem to background jobs from time-to-time. Usually when testing an application, or executing an application from the terminal. Although not critical, it's useful knowing if jobs are running in the background or not. Again, I don't want this cluttering up my terminal if jobs aren't running.

1
2
3
4
5
    # I want to know my battery percentage when less than 100%.
    if which ibam &> /dev/null; then
        BATTSTATE="$(ibam --percentbattery)"
        BATTPRCNT="${BATTSTATE[(f)1][(w)-2]}"
        BATTTIME="${BATTSTATE[(f)2][(w)-1]}"

Most of my time computing is spent on my laptop. Although there are several utilities that show my current battery percentage, I figured why not put it into my prompt? One thing that converted me to ZSH was it's powerful scripting capabilities. Here, I'm creating three variables for ultimately getting the data I want. In the BATTPRCNT, I find the data by grabbing the first line in the list (BATTSTATE[(f)1]) and the second-to-last word ([(w)-2]). For the remaining time, I get this information from the second line, and last word in the list.

1
2
3
4
5
6
7
8
9
10
11
12
        PR_BATTERY=" B:${BATTPRCNT}%% (${BATTTIME})"
        if [[ "${BATTPRCNT}" -lt 15 ]]; then
            PR_BATTERY="$PR_BRIGHT_RED$PR_BATTERY"
        elif [[ "${BATTPRCNT}" -lt 50 ]]; then
            PR_BATTERY="$PR_BRIGHT_YELLOW$PR_BATTERY"
        elif [[ "${BATTPRCNT}" -lt 100 ]]; then
            PR_BATTERY="$PR_RESET$PR_BATTERY"
        fi
    else
        PR_BATTERY=""
    fi
}

Now that I have the battery data that I want, I format it to fit my needs. If the battery percentage is less than 100% and greater than 49%, I want the color to be the same as the rest of the prompt- normal. If it's less than 50% but greater than 14%, I want the color to be yellow. If it's less than 15%, then the color should be red. You'll notice that there are variables for colorizing the value. These are defined the next function below. Lastly, if the battery is 100%, then don't show the information at all. In order to retrieve this information, I use the "ibam" utility. It's installed on my laptop, so that's all that matters to me. This concludes the precmd() function. Now, to drawing the prompt itself.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
setprompt () {
    # Need this, so the prompt will work
    setopt prompt_subst

    # let's load colors into our environment, then set them
    autoload colors zsh/terminfo

    if [[ "$terminfo[colors]" -gt 8 ]]; then
        colors
    fi

    for COLOR in RED GREEN YELLOW WHITE BLACK; do
        eval PR_$COLOR='%{$fg[${(L)COLOR}]%}'
        eval PR_BRIGHT_$COLOR='%{$fg_bold[${(L)COLOR}]%}'
    done

    PR_RESET="%{$reset_color%}"

Now, we move onto drawing the prompt. This function is defined by myself, and is not a ZSH builtin. First, and foremost, I need to set the prompt_subst ZSH option. This is needed to substitute the variables in my prompt with what was defined earlier

Then, we need to load colors into the prompt. ZSH has an awesome autoload utility for loading modules into the namespace. Here, I'm loading colors and terminfo, necessary for setting the appropriate colors. Then, we discover if we can even use colors. One thing I wanted about my prompt, was the ability to fail gracefully, if colors aren't supported. Here, I make that possible. Then it's on to defining the colors themselves. A simple 'for' loop makes this possible, and we cycle through only the colors needed by the prompt. I'm setting both bold an normal colors, so I can take advantage of them for a little styling of the prompt. I'm also creating a RESET variable for resetting the color back to non-colored, normal text.

Thanks to lists, again, I can grab foreground and background colors as needed. Also, because I created a 'COLOR' variable, I need to lowercase the variable when making the assignments, as the '$fg[]' list is expecting the color in lowercase. Why not just do it all in lowercase? I'm a fan of uppercase variables in my scripts, and I keep this behavior here. So, '$fg[${(L)COLOR}] will lowercase the value of '$COLOR'.

1
2
3
4
5
6
7
8
9
10
11
    # Finally, let's set the prompt
    PROMPT='\
${PR_BRIGHT_BLACK}<${PR_RESET}${PR_RED}<${PR_BRIGHT_RED}<${PR_RESET} \
%D{%R.%S %a %b %d %Y}${PR_RED}!${PR_RESET}%$PR_PWDLEN<...<%~%<< \

${PR_BRIGHT_BLACK}<${PR_RESET}${PR_RED}<${PR_BRIGHT_RED}<\
${PR_RESET} %n@%m${PR_RED}!${PR_RESET}H:%h${SCREEN}${JOBS}%(?.. E:%?)\
${PR_BATTERY}${GITBRANCH}\

${PR_BRIGHT_BLACK}>${PR_RESET}${PR_GREEN}>${PR_BRIGHT_GREEN}>\
${PR_BRIGHT_WHITE} '

The 'PROMPT' variable is the same as the 'PS1' variable in ZSH, so I use it here. This is the prompt string itself, doing all the formatting and placement of variables. Notice that whenever I define a bright color, before I can use a non-bright color, I have to reset it first. I'm sure there's a better way to do this, but it works fine for me. Also, the 'PROMPT' variable must be enclosed in single quotes, or some of the variables will not be evaluated.

1
2
3
4
5
6
    # Of course we need a matching continuation prompt
    PROMPT2='\
${PR_BRIGHT_BLACK}>${PR_RESET}${PR_GREEN}>${PR_BRIGHT_GREEN}>\
${PR_RESET} %_ ${PR_BRIGHT_BLACK}>${PR_RESET}${PR_GREEN}>\
${PR_BRIGHT_GREEN}>${PR_BRIGHT_WHITE} '

}

Of course, it would be silly to not define a matching continuation prompt. The 'PROMPT2' variable in ZSH is the same as the 'PS2' variable, so again, I prefer to use that variable name. Below is a screenshot showing every aspect of the prompt, except for the GIT branch. As you can see, I'm in a directory structure that normally would be longer than the width of the terminal, I'm behind screen in window #0, running 2 backgrounded jobs, there was an exit code of '42' from the previous executable, my battery has a 94% charge, the previous executable was run at 23:05 on November 22, 2008 and I'm about to execute history item number 1573.

Basic ZSH prompt
1
setprompt

Lastly, we call the function to draw the prompt, and we're off! Since creating this prompt, I've thought about other useful data that I could use for the prompt. Maybe I'll get around to that, but right now, I'm very happy with the final status. The post is informative, but not intrusive. It contains both function and form. It's bright to grab your attention, but not overbearing to distract you for your work. It conserves real estate, yet is loaded with information. Yeah. I won't be changing shells, or prompts, anytime soon. 🙂

The entire source can be downloaded here.

Cheers!

GNU Screen

The key to being an effective administrator is understanding the software that you administer. Software administrators are nothing more than employees who are well rounded in software applications, their versions, how they are configured and installed and lastly how to troubleshoot them should something go wrong. Effective administrators also have a set of tools under their belt that helps make their job easier. The tool that is the subject of this post is GNU screen.

First, we need to get a good grasp of screen. Screen is what we call a "terminal multiplexer". A user attaches himself to a console on the machine, then at that users whim, detach from the console, but still leave it installed and its attached processes running on the system. What makes screen so attractive is the ability to reattach to that console, with its currently running processes available. Generally, screen is run on a remote SSH server, so the administrator can log into that box from anywhere, reattach his running screen session, and continue working on his applications like he never left the machine.

Heres how it works. Log into your local machine, and type:

$ who am i
aaron    pts/1        2008-10-12 05:43 (:0.0)

This shows me logged in as aaron, and attached to pseudo terminal 1. I logged into the system on October 12, 2008 at 5:43 in the morning (my daughter won't sleep). Now, let's execute screen, and run a couple of commands:

$ screen
$ who am i
aaron    pts/4        2008-10-12 05:45 (:0:S.1)
$ cat /dev/zero > /dev/null &

Still logged in as aaron, but this time, I'm attached to pseudo terminal 4. Also, we ran a pretty worthless command, however, it will illustrate the point. Detach from your screen session by typing the following keyboard sequence:

ctrl-a d

This will "detach" you from pseudo terminal 4, but still leave the device created, will all running processes attached to it:

[detached]
$ ls /dev/pts/4
/dev/pts/4
$ ps -ef | grep cat
aaron    27684 27494 93 05:49 pts/4    00:00:05 cat /dev/zero

The process that we ran in screen a moment ago is still running. In my case, it has a process ID of 27684 with a parent process ID of 27494 and attached to pseudo terminal 4, just like we would expect. I wonder what process ID 27494 is...

$ pstree -p | grep -B 1 27494
        |-screen(27492)-+-ssh(27493)
        |               `-zsh(27494)---cat(27684)

(The 'pstree' command, if run on its own, will show your the processes running on your box in a tree-like structure, showing parent and child processes).

As we can clearly see, 'screen' is process ID 27492, with a child process of ZSH, my favorite shell, running as process ID 27494. ZSH is currently running our 'cat' command as process ID 27684, as we just discovered.

Here's what is absolutely great about GNU screen: I can detach from sessions and reattach to those sessions at will. Suppose I ran that command on my box at home. When I get to work, I want to keep an eye on it. All I need to do now, is login remotely to my machine (hopefully, it's running an SSH server), and reattach my screen session:

% ssh helios.cocyt.us
Password:
$ who am i
aaron    pts/0        2008-10-12 06:00 (hades.cocyt.us)
$ screen -r
$ who am i
aaron    pts/4        2008-10-12 06:01 (:0:S.1)
$ kill 27494
[1]  + terminated  cat /dev/zero > /dev/null
$ exit
[screen is terminating]

Not only can we detach and attach ourselves from and to these processes respectfully, we can also create new terminals in screen, split our screen horizontally, add multiuser sessions, lock the screen from intrusion, and many other features. The capabilities of screen go well beyond what we've discussed here. Suffice it to say that GNU screen is one of the most powerful tools that a system administrator can have in his tool belt.

GNU screen is available in the Fedora, openSUSE and Ubuntu repositories, as well as Red Hat Enterprise Linux and SUSE Linux Enterprise Server and Desktop.

New Irssi Theme - 88_madcows

Yesterday, while students were working on labs, and I was bored stiff, I decided to update my Irssi installation running on my Debian SPARC box. I spent some time cleaning up the config (not by hand, mind you, but through the options in the interface) by updating the servers I connect to, the channels I want to autojoin, the networks, updating aliases and scripts, and a number of other things. Then, I realized it was time for a theme other than default.

I had run the madcow theme in the past, but I wasn't happy with a few aspects of it. For example, I didn't like the nicks being right-aligned based on the last character of their nick. Because of this, the developer of the the theme chose a max length for the nicks that meant cutting off any nicks that extended past that max. Why not just left-align it to conserve space? Also, there were aspects of the theme that didn't tie in well with itself. So, after several hours of hacking, I finally settled on my new theme. It's not a big departure from the madcow theme, as I loved the color scheme and much of the formatting, and wanted to keep it. However, I also wanted to change what was driving me nuts about the theme. Now, I'm no Irssi theme hacker, so I'm sure there are better ways to reach some of the formatting decisions that I made, but it's working well, so I'm happy.

I decided to name the theme 88_madcows, after my IRC nick 'eightyeight' and keeping homage to the madcow theme developed by dubkat. Below is a screenshot of the theme in action. If you would like to download it, I've provided a link as well. I would recommend running the following commands in your Irssi for maximum theme integration (I have usercount.pl, trackbar.pl and nicklist.pl installed):

/set hilight_color %g
/set hilight_act_color %R
/set trackbar_style %g

Full 1680x1050 screenshot here (using the Tango color scheme in gnome-terminal).
Theme download here.

Linux Must Be Laughable

Today, I found myself featured on the Linux Haters Blog. I've heard of this blog before, but didn't give it much thought, as most of the posts coming from the author are nothing more than Microsoft fan-boy fanaticism. It's hard to take any of the posts seriously. After reading his post regarding a couple of mine, I just thought to myself, "Oh, brother. Is this guy for real, or is he all about the press, leading many, many readers on?". I spent some time on the blog, digging through posts and comments, and I could find nothing intellectual stimulating conversation above "Linux SUX, Windows RULES!". So, this post is challenge the author to produce something intellectual that will actually show some logic behind his posts (and maybe the commentators behind their comments).

My challenge is a simple question: What does it take to make a stock Microsoft Windows install usable? Let's take a look, comparing a stock XP Professional install with an Ubuntu 8.04 install, and see who wins out.

First, let's look at the installer itself. I'm a system administrator, and I want to get XP and Ubuntu installed as quickly as possible on as many machines as possible. What flexibility do I have with the Windows XP Professional CD as far as meeting this need? Well, as far as I can see, I only have the CD to do the install. I have to sit through each screen by hand, clicking through the dialogs one by one until the install finishes. During this install, I am plagued with entering in a different serial number in each computer, unless I was able to purchase a multi-install key, which I still have to enter by hand on each machine. Because I'm limited to only optical media for my installation method, it will take about 45 minutes to complete a single install. Of course, most administrators would use some disk imaging software, like Norton Ghost, but that means I need to purchase a 3rd party utility to make this task successful. With Ubuntu however, I have the ability to install the operating system automatically using a few built-in utilities. Kickstart, Pressed and the hybrid Kickseed give me the ability to completely automate the install hands off. Further, Ubuntu gives me the ability to use repositories where the software for the operating system exists. I can access these repositories via HTTP, FTP or NFS. Just being on a 100baseT full switched network will be incredibly faster than CDROM. I can complete a fully Ubuntu 8.04 desktop install in less than 15 minutes-- on ALL machines.

Second, let's compare security on the operating systems. With Ubuntu, by default, if services are setup, they are only listening on the local interface, localhost. Coupled with AppArmor, I have a Mandatory Access Control system keeping my processes in check with my files. A default firewall is disabled, but can be enabled with the Netfilter kernel module, and built easily with the uncomplicated "ufw" command. Users created on the system are not administrators, so system-wide security vulnerabilities introduced through the user and highly improbable. Antivirus software, as well as software needed to remove malware, spyware, etc. is not needed, as the security design behind the operating system does not let this software grow beyond the user's home directory. Updates will most likely be waiting on first boot, to patch any security vulnerabilities and bugs with the system. Updates will be ongoing frequently throughout the time using Ubuntu. On the other hand, Windows XP has left me with absolutely nothing. No firewall software. No MAC software, although Vista with UAC addressed this. Newly created users on the system are administrators by default, so creating havoc on the box, and even the network, is as easy as getting online. The latest service pack will be waiting for me, and updates will be continuous throughout my use of XP. Windows has shown a bad track record with viruses and badware, yet on a default install, I'm left with nothing to guard myself. Sure, there are third party utilities to help me address these issues, but I will need to purchase them separately, and get them installed after XP finishes its install. Further, the default services are listening on all interfaces, making me vulnerable to an attack.

Now on to productivity software. After installing these machines, I need them ready for the corporate environment. I'll need email clients to synchronize with my backend servers, regardless of what they're running. I'll need office productivity software in the way of word processing and spreadsheets. I'll need PDF creators and viewers. I'll need a compressing utility, as well as encryption, due to the nature of sensitive emails. Instant messaging is a must for internal communication. With Ubuntu, OpenOffice.org is shipped and installed by default providing the employees the necessary tools to begin working. Evolution is provided for email communication, which gives me the ability to connect to POP3(s), IMAP(s) and Exchange servers. Ubuntu ships with Evince as the default PDF viewer, and a PDF "printer" is installed by default, giving me easy access to create PDFs. Three compression utilities, zip, gzip and bzip2, coupled with GNU tar, give me the ability to archive and compress anything on disk. GnuPG is installed by default for encrypting those sensitive emails. Lastly, Pidgin is my mult-protocol application for using instant messaging, giving me the ability to connect to Jabber, MSN, Yahoo, AIM, ICQ, Novell groupwise, and many, many others simultaneously. As for Windows, I have Notepad and Wordpad installed for my "word processing". There is no spreadsheet application installed. Outlook express is available as a minimal email client. There is no PDF creator or viewer. Zip is provided for compression, but no encryption application is installed. A Windows Messenger application is installed for instant messaging. Of course, many third party utilities can meet many of these needs, but none of them are provided by default

Lastly, the need for remote administration. Being a system administrator, I'll need the ability to connect remotely to each machine, and administer it as needed, whether stuff breaks, I need to install/remove software, or other administration tasks. XP Professional has given me the ability to utilize the RDP protocol through remote desktop. RDP uses encryption by default, however, due to the nature of XP, I can only login via RDP when the user on the other end has logged off. XP only allows a single user logged in at any given time. Unfortunately, however, there is no scripting language provided by the operating system, so writing simple scripts to automate tasks for me is not possible. Again, I can install plenty of third party utilities to meet these needs. On the other hand, Ubuntu has given me OpenSSH, which also does encryption by default. Further, because Ubuntu is a mult-user operating system, I can administer the machine while the user is still using it. Installed are several different scripting languages and compiled languages to make automating tasks a breeze. Perl, Python, BASH, C and C++ are all installed by default.

Looking at these comparisons, Ubuntu 8.04 comes well ahead as a usable desktop on a default install where Windows horribly fails. This recalls to mind the Mac and PC commercials. Remember the first commercial, where Mac and PC were "born" by being unboxed? Mac was ready for primetime, while PC had service pack updates to process, third party utilities to install, and security software to configure. It was going to be a while before PC could be on the same usable level as Mac out of the box. I'm seeing the same thing here.

After a default install, I could see several scenarios where a default install just wont meet my needs, but third party utilities will. Norton Ghost, Microsoft Office, McAfee Antivirus, Windows Defender, Lavasoft Adaware, Spybot Search and Destroy, PGP, PDF utilities, better IM client, scripting language, and so forth. The third party list for getting a usable Windows desktop gets long fairly quickly.

So, I guess Linux must be laughable. It sure isn't an operating system defective administrators would want to use. It just makes life too easy, both for the user and the administrator.

DASH on Ubuntu

A couple recent posts have started on the Utah Open Source Planet regarding popd and pushd not being available on a default Ubuntu install. As discovered, popd and pushd are shell built-ins for the BASH shell, and not provided by the Debian Almquist Shell (DASH). Why has Ubuntu made the change from BASH to DASH as the default shell? Well, there some reasons for it, as identified by the Ubuntu Wiki.

BASH is full-featured bloat. Yes, bloat. If System V Init scripts are relying on BASH to start their service, your boot process will be slower. DASH, in comparison is light and snappy, thus greatly improving the time it takes your computer to boot. If your scripts are adhering to POSIX standards, and are using /bin/sh rather than /bin/bash, you shouldn't notice any problems. However, if your scripts are relying on /bin/bash features, such as popd or pushd, and you change the interpreter at the top of your script to /bin/sh, you'll have some breakage.

For what it's worth, this isn't anything new with 8.04. DASH became the default in Ubuntu with 6.10, so we've had it in this manner for some time. If you would like to change it, then point the symbolic link from /bin/sh to /bin/bash rather than /bin/dash, and you're done. However, as you may have noticed, it could cause some breakage if you Bourne-compatible scripts contain "Bashisms".

Personally, I recommend the Z-shell (ZSH) to anyone looking for an alternative to DASH or BASH. Much more capable, flexible and configurable shell.

Managing Services in Ubuntu, Part II: Managing Runlevels

In my last post, I gave you a glimpse to runlevels, their purpose, and how they affect the overall operation of your box. Today, in this post, I am going to dig deeper into runlevels, as well as manipulating services in specific runlevels. I'm going to show you why you want to do this, as well as how. We'll learn about the 'update-rc.d' command mainly, spending a great deal of time with examples and demos.

Before I begin, however, I want to make something painfully clear. Linux gives you the awesome power to completely break your box. I'm talking to the point where not even a backup would work, and you're left with the task of reinstalling the operating system. However, on the flip side, Linux gives you the awesome power to manage and administer your box. Intense flexibility and unbelievable portability. Today, if you're following along with the examples given in this tutorial, it is possible to leave your box in an unbootable state, or in a state of undesired operation. Of course, this is not what we want. So, if you are going to follow along, I would recommend following along in an Ubuntu virtual machine. Otherwise, take extra care to follow the tutorial exactly, and all should be well.

Ok, in my last post, I talked about what a runlevel is and what it does to your box. However, I never really mentioned why you should care, or how to to manage a specific runlevel. I plan on discussing those items here. First, is why you should care about runlevels. I've seen the argument that runlevels add nothing to your box, and managing services is trivial, so runlevels just add an extra layer of complexity. I disagree. For me, runlevels add a layer of dependability. The whole idea of runlevels is to maintain a level of predictability with your box. I should be able, at any given time during the day, predict what is running on my box. If not, then I've given up a level of security. By not knowing how my box is performing, or what it is executing, I've opened a door for potential vulnerability. However, if I can predict the state of my box, regardless of time of day, stress it's under or number of users logged in, then I've maintained a level of security and dependability. As such, I've done my job well as a system administrator. Runlevels give me that predictability, as well as flexibility in the state I want my box operating under.

As a review, let's look at the runlevels in Ubuntu:

0: Halt / Shut down.  All processes are stopped, all filesystems are unmounted, and all users taken offline.  Power can now be safely removed.
1: Single user mode.  Only root allowed login (requires password).  All filesystems listed in /etc/fstab are mounted.  Limited set of services started.
2-5: Multi-user mode.  All filesystems listed in /etc/fstab mounted, graphical environment (if installed) brought up.  Full standard operational mode.
6: Reboot.  As with runlevel 0, all processes stopped, all filesystems unmounted, all users taken offline, the box rebooted, and taken to the default runlevel.

If you did your homework in the last post, you probably changed your default runlevel in your /etc/event.d/rc-default file. If you changed this to runlevel 6, you probably noticed a problem: your box infinitely rebooted. Unfortunately, I did not give you a way out in the last post. I'll do that now.

When booting the box, you can bypass the defined default runlevel by passing 's' or 'S' as a kernel argument in GRUB. When your box is booting, press the [Esc] key before the timeout ends, and you'll be taken to the GRUB menu. You'll have the ability to use your arrow keys to highlight an entry. From there, there is a prompt at the bottom of the menu telling you what you can do to that entry that is highlighted. For example, you can press the 'e' key to edit that line. Go ahead, and do that to one of the highlighted entries. You'll notice that it takes you to a new menu, showing an initrd (initial ramdisk, used by the kernel to load some necessary drivers for the system), the kernel itself, and a memory test for your RAM. Select the kernel entry, and hit the 'e' key again to edit the kernel. At the end of the line, enter 's' or 'S', and press enter. Press the 'b' key to boot the kernel now, and you will boot into an sulogin prompt. If you enabled the root account, which is disabled by default on an Ubuntu box, you will be prompted for the root password, WHICH IS IN PLAIN TEXT ECHOED TO THE SCREEN (it's a bug, we're working on it). If the root account is not enabled, then you'll be thrown to a root prompt, at which you can edit your /etc/event.d/rc-default file to change the default runlevel to the appropriate entry.

Woah. Wait a minute. You mean, all I have to do is enter an 's' at the kernel line in GRUB, and I get root access to the machine?! Yes. Any box that is physically available can be easily compromised, even with harddrive disk encryption. So, considering the physical security of your box is something that you'll obviously want to take into account as a system administrator. However, we're off topic a bit, so let's get back on target.

Runlevels are nothing more than your box running in a specific state. Fortunately, we can change the desired state that we want our box to be running in by adjusting the runlevel. This is done with the update-rc.d command.

At this point, let's play with a package for this tutorial. I've chosen sendmail as our culprit, so if you don't already have it installed, run the following command below. If you do have sendmail installed, and would rather use another service package, go for it. We'll use sendmail in this post:

aaron@kratos:~ 1354 % sudo aptitude -R install sendmail
Password:

In my last post, sendmail was one of the services on my box that was starting when I enter runlevel 2. Maybe I don't want sendmail to start, or rather, if it is already started, when I enter runlevel 2, I want to kill it. In other words, I don't want it running for runlevel 2. How can I make this change?

Well, first, I could just delete the soft link from the runlevel directory /etc/rc2.d/:

aaron@kratos:~ 1355 % sudo rm /etc/rc2.d/S21sendmail
Password:

That would definitely keep it from starting when I enter runlevel 2, but what if I wanted to kill it if it was already started from a previous runlevel? Just deleting the soft link won't do it. I need to turn it into a K-script. Further, deleting and creating soft links in my /etc/rc[0-6].d/ directories by hand is a bit of a pain. This is where the update-rc.d command comes in:

aaron@kratos:~ 1356 % sudo update-rc.d -f sendmail remove
Password:
 Removing any system startup links for /etc/init.d/sendmail ...
   /etc/rc0.d/K19sendmail
   /etc/rc1.d/K19sendmail
   /etc/rc2.d/S21sendmail
   /etc/rc3.d/S21sendmail
   /etc/rc4.d/S21sendmail
   /etc/rc5.d/S21sendmail
   /etc/rc6.d/K19sendmail
aaron@kratos:~ 1357 % sudo update-rc.d sendmail stop 20 0 1 2 3 4 5 6 . 
 Adding system startup for /etc/init.d/sendmail ...
   /etc/rc0.d/K20sendmail -> ../init.d/sendmail
   /etc/rc1.d/K20sendmail -> ../init.d/sendmail
   /etc/rc2.d/K20sendmail -> ../init.d/sendmail
   /etc/rc3.d/K20sendmail -> ../init.d/sendmail
   /etc/rc4.d/K20sendmail -> ../init.d/sendmail
   /etc/rc5.d/K20sendmail -> ../init.d/sendmail
   /etc/rc6.d/K20sendmail -> ../init.d/sendmail

We'll get into the syntax a bit later, but these commands, as observed, removed any existing soft links that previously existed, and created new K-scripts for all of my runlevels.

First off, why two separate commands? Well, by Debian policy, no package upgrade will ever overwrite a previous configuration. This includes updating soft links in the runlevel directories. This also ensures persistent changes and allows the system administrator to prevent daemons from launching. So, if soft links already exist, they first need to be removed, then new links created.

Second, you'll notice that I need to specifically state when I want the service to start and when I want it to stop during the runlevel process. This is intentional, and offers a great deal of flexibility to the system administrator. Remember, that all the soft links either start with an S for starting the service, or a K for stopping (killing) the service. Each soft link is processed in alphanumeric order, so there is a number following the K or S telling the order in which I want the scripts to execute.

There is something to be said about logic behind the order of when you are starting and stopping services. For example, you may want to restore your firewall before enabling your network device. Further, you may want your network brought up before you begin starting services. Ultimately, the choice is up to you.

Let's continue with managing our runlevels, and learn the syntax of the update-rc.d command. There are four possible ways in which I can use the update-rc.d command. Let's start with the first- defaults. As our symlinks already exist, we'll need to remove them:

aaron@kratos:~ 1358 % sudo update-rc.d -f sendmail remove
Password:
[... snip ...]
aaron@kratos:~ 1359 % sudo update-rc.d sendmail defaults
[... snip ...]

(I'll be snipping some output from here on out). As we can see, the defaults for sendmail were to stop it at position 20 for runlevels 0, 1 and 6, and to start it at position 20 for runlevels 2 through 5. Actually, position 20 is the default for any package, regardless. This means apache, postfix, squid, ssh, etc. This may not be what we want. Maybe I want to affect sendmail at a different position.

The 2nd possible syntax of the update-rc.d command to to change that default position to another number for all runlevels. Remember to remove our soft links first, then create new ones:

aaron@kratos:~ 1360 % sudo update-rc.d -f sendmail remove
Password:
[... snip ...]
aaron@kratos:~ 1361 % sudo update-rc.d sendmail defaults 10
[... snip ...]

As we can see, I was able to change the position of stopping and starting sendmail from 20 to 10. This affected all runlevels 0 through 6. However, I may not want to affect all runlevels the same. Maybe I want to treat runlevels 0, 1 and 6 differently than 2 through 5.

Which brings us to our 3rd possible syntax for the update-rc.d command- using the default runlevels, but changing when it's stopped and started. Again, let's remove our symlinks, and create new ones:

aaron@kratos:~ 1362 % sudo update-rc.d -f sendmail remove
Password:
[... snip ...]
aaron@kratos:~ 1363 % sudo update-rc.d sendmail defaults 10 21
[... snip ...]

As we can see, the default runlevels 0, 1 and 6 were changed to stop sendmail at position 21, and the default runlevels 2 through 5 were changed to start sendmail at position 10. So, I can change the starting and stopping position using the default runlevels. Cool.

Finally, let's look at our last usage of the update-rc.d command: full flexibility of when I start and stop the service in each individual runlevel. I bet you haven't forgotten to remove your soft links before creating new ones, have you:

aaron@kratos:~ 1364 % sudo update-rc.d -f sendmail remove
Password:
[... snip ...]
aaron@kratos:~ 1365 % sudo update-rc.d sendmail start 10 2 3 . start 20 4 5 . stop 19 0 1 6 .
[... snip ...]

Here, as we can see, I affected the starting position for sendmail in runlevels 2 and 3 to be 10. For runlevels 4 and 5, I wanted sendmail to be at position 20. Finally, for runlevels 0, 1 and 6, I wanted to kill sendmail at position 19. You'll also notice that the syntax is fairly straight-forward:

update-rc.d [service] start|stop [position] [runlevel] [runlevel] [runlevel] ... [.]

The first argument, as has been consistent up to this point, is the service. Then, I mention whether or not I want to create K-scripts or S-scripts by calling either the start or stop option. Both start and stop have a set of arguments, the first being the position I'm going to affect. Then, following the position, I give a space-separated list of runlevels followed by a '.' (dot) to terminate either that start or stop call.

Let's look at a couple more examples of this 4th syntax, so we can get a better feel for it:

aaron@kratos:~ 1366 % sudo update-rc.d -f sendmail remove
Password:
[... snip ...]
aaron@kratos:~ 1367 % sudo update-rc.d sendmail start 45  2 3 4 5 . stop 1 0 1 6 .
[... snip ...]

The above example starts sendmail at position 45 for runlevels 2 through 5 and stops sendmail at position 1 for runlevels 0, 1 and 6. I should probably point out, that you most likely don't want to start a service for runlevels 0 and 6, for obvious reasons. One more example:

aaron@kratos:~ 1368 % sudo update-rc.d -f sendmail remove
Password:
[... snip ...]
aaron@kratos:~ 1369 % sudo update-rc.d sendmail stop 0 0 1 2 3 4 5 6 .
[... snip ...]

In that example, I set sendmail to stop at position 0 for all runlevels, thus effectively disabling the service, regardless of the state of my box. If I wanted sendmail to start, I would need to call it directly:

aaron@kratos:~ 1370 % sudo /etc/init.d/sendmail start
Password:
 * Starting Mail Transport Agent (MTA) sendmail                          [ OK ] 

One final note about managing runlevels. All this is fine and dandy with the update-rc.d command, however, we're just removing and creating soft links. We are not affecting the running process at all. In other words, just because I've configured sendmail to start in runlevels 2-5 doesn't mean that I've started the service. If sendmail is not running, I will still have to start it by hand. Same goes for creating K-scripts in those directories. The service won't be stopped because I have created those K-scripts. I'll still have to stop it by hand. All we're doing is affecting if it starts or stops when I enter a new runlevel.

There is some philosophy about when to start and stop services, and definitely some logic behind the order. However, the position at when to start and stop these services is entirely up to you. If you find that you're starting a service too early (or too late), change the soft links. It may be of interest to you to start your X display manager first, before starting services. Now that you're familiar with the update-rc.d command, you know how to do this (this approach may have some consequences as some services, such as xfs or gdm may provide dependency to X).

At this point, we'll stop here with this post in the series, and pick up again tomorrow with more service management. If you followed both of these posts, you should be fairly comfortable with runlevels, and managing when to start and stop services in specific runlevels. You're turning into a system adminstrator.

Managing Services in Ubuntu, Part I: An Introduction to Runlevels

As many of you are probably already aware, I'm a Linux trainer for Guru Labs. We do all Linux training, including Red Hat, Fedora, SUSE, and others. If your company is looking for good, solid Linux training, Gurus at Guru Labs are the cream of the crop, and I'd highly recommend it. I've been doing a lot of Red Hat training lately, and during the training, I cover how to manage services on a Red Hat system. Lately, I've been meaning to translate this over to Ubuntu, and put it in a series of posts. As a result, the subject of this post is all about learning runlevels, and how to manage services effectively, such as Apache, Squid, SSH, and so on, in Ubuntu. So, let's get started.

First off, to understand services, we need to understand runlevels. Runlevels are a way to automatically start and stop services, thus effectively controlling what is running on your box. When you enter a specific runlevel, you are telling your box that you want to stop a specific set of services and/or start a specific set of services. In other words, think of runlevels as categorizing your system. For example, if I enter runlevel 1, I may want to tell my box to go into "single user mode", thus effectively knocking everyone off except for the root user. Also, I may want to stop all services, including networking, taking my box off the network for troubleshooting reasons. Thus, stop Apache, Squid, SSH, and so on, leaving my box in maintenance mode.

On all Linux and Unix systems, runlevels are treated differently. For example, runlevels in Red Hat have different meaning than runlevels in OpenBSD which have a different meaning than Solaris which finally have a different meaning for Ubuntu. Of course, in this post, I'm only gong to worry about Ubuntu runlevels. If you're curious about the runlevels on other systems, there is a great page here dscribing the differences.

So, let's look at the runlevels that affect Ubuntu. They are as follows:

0: Halt / Shut down.  All processes are stopped, all filesystems are unmounted, and all users taken offline.  Power can now be safely removed.
1: Single user mode.  Only root allowed login (requires password).  All filesystems listed in /etc/fstab are mounted.  Limited set of services started.
2-5: Multi-user mode.  All filesystems listed in /etc/fstab mounted, graphical environment (if installed) brought up.  Full standard operational mode.
6: Reboot.  As with runlevel 0, all processes stopped, all filesystems unmounted, all users taken offline, the box rebooted, and taken to the default runlevel.

Ubuntu does have one extra special runlevel that in processed before entering any of the regular runlevels above. It's known as 'sulogin', and it is considered an emergency mode if the system will not boot, or there are other troubles. it is denoted by "S" as it's runlevel characterization. We will not worry about runlevel S in this post.

Each Unix and Linux operating system has a default runlevel in which it will boot into. If init is the daemon manager for your system, then you will find an /etc/inittab file that will specify your default runlevel. However, Ubuntu has replaced init with Upstart, an event-driven daemon manager. As such, there is no /etc/inittab file. However, there is an /etc/event.d/rc-default file, which as probably guessed, specifies your default runlevel. Let's take a look at the file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
aaron@kratos:~ 1346 % cat /etc/event.d/rc-default
# rc - runlevel compatibility
#
# This task guesses what the "default runlevel" should be and starts the
# appropriate script.

start on stopped rcS

script
        runlevel --reboot || true

        if grep -q -w -- "-s\|single\|S" /proc/cmdline; then
            telinit S
        elif [ -r /etc/inittab ]; then
            RL="$(sed -n -e "/^id:[0-9]*:initdefault:/{s/^id://;s/:.*//;p}" /etc/inittab || true)"
            if [ -n "$RL" ]; then
                telinit $RL
            else
                telinit 2
            fi
        else
            telinit 2
        fi
end script

Parsing through that file, we see that first, it tests to see if we are running in runlevel S, or 'sulogin'. If not, then we have a series of checks to find our default runlevel. The first check is to see if there is in fact an /etc/inittab file existing. If so, we look for the line that says "id:?:initdefault:", where "?" could be any valid number 0 through 9. If that number exists, it is our default runlevel, otherwise, runlevel 2 will be our default, as none of the checks would pass.

So, as discovered, runlevel 2 is the default runlevel on Ubuntu. Furthermore, runlevels 2 through 5 are all exactly the same by default. Thus, entering runlevel 4 will have no effect on your box if already running in runlevel 2. What's the point of having so many if they're the same then? Well, a few reasons. First, if you followed that link above, showing the different runlevels on different systems, 0 through 6 is not uncommon, and many of the Unices and Linuxes take advantage of every one of them. So, I guess you could say they're present for historical reasons. However, having that many runlevels gives the system administrator the flexibility to manage a specific runlevel at his/her convenience. For example, maybe runlevel 2 should be treated differently than 3 through 5, starting a different set of services than the rest. Maybe I don't want runlevel 2 to start the graphical mode, but keep it text-based only. This could be useful in troubleshooting my graphical display without it running. You get the idea.

Ok, fine. The next question inevitably asked, is how can I tell what runlevel I'm currently in? Simple, just run the 'runlevel' command:

aaron@kratos:~ 1347 % runlevel
N 2

"N" tells me that there was "None" for my previous runlevel. In other words, this box was just booted into my current runlevel, which I can see as the 2nd identifier, "2". This is one thing that you may like or hate about the 'runlevel' command, but it only shows you your current runlevel and the previous runlevel. No more, no less. However, generally, this is sufficient.

As such, the next question you are probably asking yourself, is how do I change runlevels? This is done with the 'telinit' command. The command tells Upstart, our services manager, what services to stop and start when we enter a specific runlevel. For example, if I wanted to reboot my box from the command line, knowing that runlevel 6 is a reboot, I could enter, as root:

aaron@kratos:~ 1348 % sudo telinit 6
Password:

Ok. So, now we've learned what runlevels are, how to check my current running runlevel, and even how to change to a different runlevel. The next concept in this post is to see what runlevels are doing to the system under the hood. In other words, how does the system know that I want to knock everyone off the box except for root in runlevel 1? How does the system know to start the graphical display in runlevels 2 through 5? The answers to these questions lie in simple shell scripts.

Each runlevel has it's own directory, which contains what we call "K-scripts" and "S-scripts". These directories reside in /etc. In fact, let's get back to our terminal, and type the following, noticing the output:

aaron@kratos:~ 1349 % ls -ld /etc/rc*/
drwxr-xr-x 2 root root 4096 2008-02-16 18:06 /etc/rc0.d/
drwxr-xr-x 2 root root 4096 2008-02-16 18:06 /etc/rc1.d/
drwxr-xr-x 2 root root 4096 2008-02-16 18:06 /etc/rc2.d/
drwxr-xr-x 2 root root 4096 2008-02-16 18:06 /etc/rc3.d/
drwxr-xr-x 2 root root 4096 2008-02-16 18:06 /etc/rc4.d/
drwxr-xr-x 2 root root 4096 2008-02-16 18:06 /etc/rc5.d/
drwxr-xr-x 2 root root 4096 2008-02-16 18:06 /etc/rc6.d/
drwxr-xr-x 2 root root 4096 2007-10-15 17:36 /etc/rcS.d/

That's interesting. I have directories that have the same number as runlevels. Could this be a coincidence? Hardly. When I enter "telinit 4" on the command line, for example, I'm telling Upstart to enter /etc/rc4.d, and execute the K-scripts and S-scripts found in that directory in a specific order. Same goes for entering "telinit 2", "telinit 0" or "telinit 6". Every directory having a specific set of shell scripts that it's going to execute. So, let's dig into one of these directories. Let's start with /etc/rc0.d/:

aaron@kratos:~ 1350 % ls /etc/rc0.d/ 
K01gdm           K20postfix                          S15wpa-ifupdown
K01usplash       K25hwclock.sh                       S20sendsigs
K16avahi-daemon  K50alsa-utils                       S30urandom
K16dhcdbd        K59mountoverflowtmp                 S31umountnfs.sh
K18consolekit    K99laptop-mode                      S40umountfs
K19sendmail      README                              S60umountroot
K20apport        S01linux-restricted-modules-common  S90halt

Here, I can see only 20 scripts and a README file listed. I notice further that I have my K-scripts and S-scripts that I've mentioned earlier. In fact, the K-scripts and S-scripts are numbered. This has to do with order in which to execute those scripts. Let's look at them in detail. First, Upstart is going to execute the K-scripts then it will execute the S-scripts. K-scripts stop a service, while S-scripts start a service. So, the first script it will execute when it enters this directory is "K01gdm". This script manages my graphical display. In other words, I'm going to kill my X session before doing anything else. Next, I'll stop usplash followed my avahi-doemon, etc.

Once all the K-scripts are executed, then I execute the S-scripts. Just as with the K-scripts, they are also executed in order. So, the first script I start is "S01-linux-restricted-modules-common". This script manages my restricted drivers for the kernel, returning nothing more than the exit code for /sbin/lrm-manager, and logging the result. Looking further, I have "S15wpa-ifupdown" which is managing my WPA helper library for wireless devices. We notice further that the last script executed in this directory is "S90halt", which shuts the box down fully. Each of those scripts, as mentioned before, are just shell scripts, and can be opened with any text editor. If you're curious about what a script is doing, open it up and read it.

Let's look at runlevel 2 this time:

aaron@kratos:~ 1351 % ls /etc/rc2.d 
README                       S20hotkey-setup   S30gdm
S05vbesave                   S20makedev        S50ntp
S10acpid                     S20nvidia-kernel  S89anacron
S10powernowd.early           S20postfix        S89atd
S10sysklogd                  S20powernowd      S89cron
S10xserver-xorg-input-wacom  S20rsync          S98usplash
S11klogd                     S21sendmail       S99acpi-support
S12dbus                      S22consolekit     S99laptop-mode
S12hal                       S24avahi-daemon   S99rc.local
S19cupsys                    S24dhcdbd         S99rmnologin
S20apport                    S25bluetooth      S99stop-readahead

First off, we may notice that there are no K-scripts in the runlevel 2 directory. In other words, we aren't interested in stopping any services when we enter runlevel 2. Rather, we are only interested in starting services. Second, we may notice a logical order to the scripts. For example, we start power management right out the gate with S10acpid and S10powernowd.early. Next, we start S10sysklogd, which is our generic logging daemon for logging services. It makes sense to start this daemon before starting specific services, as if they fail to start, I will have a log as to the reason. Also notice that anacron is starting before cron (S89anacron to S89cron). This makes sense, as we want to run any cron jobs that weren't run before running them again. The other daemons start in their numerical order as specified.

Looking in that directory is a bit deceptive, however. Although we see our K-scripts and S-scripts in each directory, they are actually not separate scripts. They are one script actually residing in /etc/init.d/. Let's take a look:

aaron@kratos:~ 1352 % ls -l /etc/rc2.d
total 4
-rw-r--r-- 1 root root 556 2007-10-04 05:17 README
lrwxrwxrwx 1 root root  17 2008-01-09 00:45 S05vbesave -> ../init.d/vbesave
lrwxrwxrwx 1 root root  15 2008-01-09 00:45 S10acpid -> ../init.d/acpid
lrwxrwxrwx 1 root root  25 2008-01-09 00:45 S10powernowd.early -> ../init.d/powernowd.early
lrwxrwxrwx 1 root root  18 2008-01-09 00:45 S10sysklogd -> ../init.d/sysklogd
lrwxrwxrwx 1 root root  34 2008-01-09 00:45 S10xserver-xorg-input-wacom -> ../init.d/xserver-xorg-input-wacom
lrwxrwxrwx 1 root root  15 2008-01-09 00:45 S11klogd -> ../init.d/klogd
lrwxrwxrwx 1 root root  14 2008-01-09 00:45 S12dbus -> ../init.d/dbus
lrwxrwxrwx 1 root root  13 2008-01-09 00:45 S12hal -> ../init.d/hal
lrwxrwxrwx 1 root root  16 2008-01-09 00:45 S19cupsys -> ../init.d/cupsys
lrwxrwxrwx 1 root root  16 2008-01-09 00:45 S20apport -> ../init.d/apport
lrwxrwxrwx 1 root root  22 2008-01-09 00:45 S20hotkey-setup -> ../init.d/hotkey-setup
lrwxrwxrwx 1 root root  17 2008-01-09 00:45 S20makedev -> ../init.d/makedev
lrwxrwxrwx 1 root root  23 2008-01-09 00:45 S20nvidia-kernel -> ../init.d/nvidia-kernel
lrwxrwxrwx 1 root root  17 2008-02-16 15:44 S20postfix -> ../init.d/postfix
lrwxrwxrwx 1 root root  19 2008-01-09 00:45 S20powernowd -> ../init.d/powernowd
lrwxrwxrwx 1 root root  15 2008-01-09 00:45 S20rsync -> ../init.d/rsync
lrwxrwxrwx 1 root root  18 2008-02-16 18:06 S21sendmail -> ../init.d/sendmail
lrwxrwxrwx 1 root root  20 2008-01-09 00:45 S22consolekit -> ../init.d/consolekit
lrwxrwxrwx 1 root root  22 2008-01-09 00:45 S24avahi-daemon -> ../init.d/avahi-daemon
lrwxrwxrwx 1 root root  16 2008-01-09 00:45 S24dhcdbd -> ../init.d/dhcdbd
lrwxrwxrwx 1 root root  19 2008-01-09 00:45 S25bluetooth -> ../init.d/bluetooth
lrwxrwxrwx 1 root root  13 2008-01-09 00:45 S30gdm -> ../init.d/gdm
lrwxrwxrwx 1 root root  13 2008-01-12 13:41 S50ntp -> ../init.d/ntp
lrwxrwxrwx 1 root root  17 2008-01-09 00:45 S89anacron -> ../init.d/anacron
lrwxrwxrwx 1 root root  13 2008-01-09 00:45 S89atd -> ../init.d/atd
lrwxrwxrwx 1 root root  14 2008-01-09 00:45 S89cron -> ../init.d/cron
lrwxrwxrwx 1 root root  17 2008-01-09 00:45 S98usplash -> ../init.d/usplash
lrwxrwxrwx 1 root root  22 2008-01-09 00:45 S99acpi-support -> ../init.d/acpi-support
lrwxrwxrwx 1 root root  21 2008-01-09 00:45 S99laptop-mode -> ../init.d/laptop-mode
lrwxrwxrwx 1 root root  18 2008-01-09 00:45 S99rc.local -> ../init.d/rc.local
lrwxrwxrwx 1 root root  19 2008-01-09 00:45 S99rmnologin -> ../init.d/rmnologin
lrwxrwxrwx 1 root root  24 2008-01-09 00:45 S99stop-readahead -> ../init.d/stop-readahead

Ahh. We can see that each script in the /etc/rc2.d/ directory is actually a soft link pointing to a script of the same name in the /etc/init.d/ directory. This is consistent with /etc/rc[0-6].d/ directories as well. Every file in those directories just pointing to a single script residing in /etc/init.d/. This makes it easy to manage runlevels, as I'm sure you could guess. Because there is only one script, I can create symbolic links in each runlevel directory to start and stop that specific service. As mentioned before, if it's a K-script, it will stop the service in /etc/init.d/, if it's an S-script, it will start the service in /etc/init.d/. This would be the same as calling the script directly:

aaron@kratos:~ 1353 % sudo /etc/init.d/sendmail start
Password:
 * Starting Mail Transport Agent (MTA) sendmail                          [ OK ]

The above is exactly the same as S21sendmail in the /etc/rc2.d/ directory. In fact, if you call the script directly, with no arguments, you can see that it contains a list of valid options, where start and stop are the only requirements:

aaron@kratos:~ 1354 % sudo /etc/init.d/sendmail
Password:
Invalid command <>
Usage: /etc/init.d/sendmail <command>
        Where <command> is one of the following
          start|stop|restart|restart-if-running
          reload-if-running|reload|force-reload
          newaliases|hoststat|purgestat|mailstats|mailq|runq|control
          status|debug|clean

As we can see, the sendmail script has start, stop, restart, restart-if-running, reload-if-running, reload, force-reload, newaliases, hoststat, purgestat, mailstats, mailq, runq, control, status, debug and clean as valid arguments. However, we'll talk more about the /etc/init.d/ directory in the next post in this series. For now, suffice it to say that the /etc/init.d/ directory contains all my shell scripts that a bunch of soft links point to from /etc/rc[0-6].d/. Specifying a runlevel to enter just means to either call the /etc/init.d/ script with a start option, or with a stop option, pending on whether or not it is an S-script or K-script respectively.

This is a good stopping point before continuing on to the next part of the series: Controlling Runlevels. Hopefully now, you understand the different runlevels, how to enter a runlevel, and what a specific runlevel is doing to your box. We looked at the various commands for identifying what runlevel the box is running in and how to change runlevels. We looked at some shell scripts, seeing what arguments are available when calling a daemon directly, and we found out how to tell what our default runlevel is. In other words, you should be fairly fluent on identifying runlevels now.

For the second post in this series, we'll talk about how to manage those runlevels, giving us the flexibility to control exactly what is started and stopped when I enter a specific runlevel. We'll pretty much be spending our entire time on one command, running through lots of examples.

I now have some homework for you to familiarize yourself with the concept of runlevels:

1) Spend some time looking through the various /etc/rc[0-6].d/ directories, identifying K-scripts and S-scripts.  Do not make any changes or modify any files.
2) Open up and read the shell scripts found in those directories, and see if you can figure out what is happening with each daemon when that script stops or starts the service.
3) Spend some time changing runlevels with the 'telinit' command, and see how it affects your box.
4) Change your default runlevel by changing the /etc/event.d/rc-default file (MAKE A BACKUP BEFORE CHANGING THE FILE).  Reboot your box, to see if it worked.
5) If you have access to a Fedora or Red Hat-based distribution, see how the runlevels differ, and see if you can do the previous 4 steps with that distro as well.

Why Upstart Is Good For Your Distro

Disclaimer: I don't know enough about Upstart to hold a low-level conversation about it. What I do know of Upstart is what I have read on the Ubuntu wiki, the Upstart FAQ and a few docs scattered here and there around the web. If I have misrepresented Upstart, its goals or intentions, or SysVinit for that matter, please email me and let me know. Thanks.

I recently discovered that Fedora 9 will be replacing SysVinit with Upstart. To me, this is good news. To others, maybe not so much. So, the question you may be asking is, what is SysVinit and what is upstart?

First, a little background. SysVinit, pronounced "System 5 init" or "Sys 5 init" or just "init" is a the first daemon that runs on a Unix or Linux system. As such, it typically has the PID of 1. Also, every other process also running on the box is a child process of init. What does this mean? It means that if you stop init, you will effectively stop every process on your box, basically inappropriately shutting down.

For many years, SysVinit has been the defacto in managing processes on your box. Not only processes, but also the order of booting and maintaining run levels, among other things. Because it has existed for so long, certainly there is no need to replace it. I mean, if there were issues with SysVinit, we would have discovered them by now, right? One would think.

Actually, developers the world over have noticed the shortcomings of SysVinit, and have made replacements, some of which are already deployed. Common ones include:

  • initng- an init replacement designed to speed up boot by starting processes asynchronously.
  • eINIT- similar to initng, but removing the need for shell scripts.
  • Upstart- also similar to initng, but event driven. Developed by Scott Remnant, and used in Ubuntu.
  • launchd- an init replacement designed by Apple found in Mac OS X.
  • Service Management Utility- an init replacement developed my Sun and used since Solaris 10

So, why the need to replace SysVinit? Let's look over some common scenarios, and see where SysVinit falls short. All of these scenarios can be found on the Replacementinit page of the Ubuntu wiki:

  • Jean is a power user who wishes to use a USB disk for part of her filesystem. This currently frequently fails because the USB disk sometimes takes longer to initialize than the boot process takes to get to the point where it mounts the filesystem. She would rather the boot process was robust, and the disk was mounted when initialized.
  • Corey is the administrator of a number of servers, and has problems with certain daemons that frequently crash. He would prefer the daemons to be automatically restarted if this happens, to avoid loss of service.
  • iPod in, and remember to stop it afterwards. She would rather the system started and stopped the software automatically based on the presence of her iPod. (maybe edgy+1)
  • Ethan is a software developer. He has a script that he wishes to run hourly, provided that the script is not still running from before. He would rather the task scheduler could take care of that for him, than have to reinvent a lock around the task. (edgy+1)
  • Katie is a database administrator. She wishes the database to be automatically backed up whenever the server is shutdown, whether for upgrade or system reboot. There is currently no way for her to set a task to be run when a service is stopped.
  • Justin is an ordinary user with a low-end system. He would rather services and hardware handlers were started only when needed, rather than on all systems.
  • Carla is a system administrator. She needs to be able to tell which services failed to start on boot, examine why, and see which services are currently running.
  • Thomas is a system administrator. He frequently gets frustrated that there is no consistency to how tasks are added to the system. A script to perform a task at shutdown must be written and activated completely differently to one performed when the system is started. (edgy+1)
  • Marie is a security consultant. She has discovered several problems with processes that run task scripts not providing a consistent environment, including potential problems such as leaving file descriptors open. (edgy+1)
  • Hugo is an ordinary user and has to frequently reboot his computer. He would prefer that shutting down and booting up took as little time as possible.
  • Helen is an experienced UNIX user, with multiple years of experience. She does not wish to have to relearn that which she has learned already, and would rather continue using the tools that she is used to and only learn the newer ones when necessary.
  • Matthieu is a distribution developer who maintains several packages that provide services or perform tasks. He does not want to have to update his packages until he is ready to take advantage of new features or abilities, his existing scripts should continue to work unmodified in their original locations.

If you read through that list, hopefully you can see where SysVinit falls short. Because of the dynamic nature of removable hardware, and a very robust kernel, SysVinit no longer meets the needs of our users. We need something more. Something that will speed up the boot process. Something that will launch processes based on events. Thus, the reason for Upstart.

The idea of an event driven system is nothing new. Crond, atd and inetd are all event driven tools that we've used on Unix and Linux for years. So, why not start jobs based on listening events, such as plugging in a USB disk, mounting /usr when needed and unmounting when not needed, etc? This is the design of Upstart.

First off, Upstart is 100% backwards compatible with SysVinit scripts. All of your /etc/init.d/ goodness is still available. We want to meet those use cases listed above, without modifying existing init scripts. Further, because Upstart is event driven, we can also replace crond, atd and inetd, while still maintaining backwards compatibility.

Ubuntu has been using Upstart since their 6.10 release. Nearly 18 months later, the reception of Upstart has been largely positive to the point that the Fedora Project is replacing init with Upstart in their Fedora 9 release. This also means it's very likely that we'll see it filter it's way to RHEL 6, which means CentOS and even OEL. Other Linux and Unix variants are also encouraged to drop the aging SysVinit with Upstart.

It just makes sense.

SGID Bug

There is a very obvious bug when attempting to set SGID on a directory when you are not a member of the group that has ownership on that directory. For example, follow along with me in the following example on your box:

[Wed 08/01/30 06:55 MST][pts/1][x86_64/linux-gnu/2.6.22-14-generic][4.3.4]
<aaron@kratos:~>
zsh 162 % mkdir ~/tmp
[Wed 08/01/30 06:56 MST][pts/1][x86_64/linux-gnu/2.6.22-14-generic][4.3.4]
<aaron@kratos:~>
zsh 163 % ls -ld ~/tmp
drwxr-xr-x 2 aaron aaron 4096 2008-01-30 06:56 /home/aaron/tmp
[Wed 08/01/30 06:56 MST][pts/1][x86_64/linux-gnu/2.6.22-14-generic][4.3.4]
<aaron@kratos:~>
zsh 164 % sudo -i
Password or swipe finger: 
root@kratos:~# chown .root /home/aaron/tmp/
root@kratos:~# exit
logout
[Wed 08/01/30 06:57 MST][pts/1][x86_64/linux-gnu/2.6.22-14-generic][4.3.4]
<aaron@kratos:~>
zsh 165 % ls -ld ~/tmp
drwxr-xr-x 2 aaron root 4096 2008-01-30 06:56 /home/aaron/tmp

So far, all we've done is create a directory in your home named 'tmp'. We showed that your User Private Group is the group owner of the directory. As such, we elevated ourselves to root, and changed group ownership of that directory to root, and showed it as thus at the end. On a side note, you can tell I'm on a Debian-based system, as my umask is set to 0022 by default, rather than the more sane 0002 with the User Private Group scheme in place.

Now, at this point, I'm going to assume that your unprivileged account is not a member of the root group. If it is, the remainder of this exercise will not work for you. Also, one may question the integrity of your choice putting your account in the root group. However, the point of this exercise is to show a bug with how SGID works on directories that you do not have group access to. Let's continue:

[Wed 08/01/30 07:01 MST][pts/1][x86_64/linux-gnu/2.6.22-14-generic][4.3.4]
<aaron@kratos:~>
zsh 166 % chmod g+s ~/tmp
[Wed 08/01/30 07:03 MST][pts/1][x86_64/linux-gnu/2.6.22-14-generic][4.3.4]
<aaron@kratos:~>
zsh 167 % ls -ld ~/tmp
drwxr-xr-x 2 aaron root 4096 2008-01-30 06:56 /home/aaron/tmp

Wait a minute. Shouldn't the permissions be 'drwxr-sr-x'? Actually, no. The results above are expected. Setting SGID on a directory is a special permission. If you are not a member of the group that owns the directory, then you can't set the SGID bit. Makes sense, just like you can't change the group of the directory to a group that you are not a member of. Just because you own the directory, doesn't give you total control over it.

Ok, so if the expected behavior is what we see above, then you're probably asking "Where's the bug?". Follow along with me on last time:

[Wed 08/01/30 07:03 MST][pts/1][x86_64/linux-gnu/2.6.22-14-generic][4.3.4]
<aaron@kratos:~>
zsh 168 % chmod g+s ~/tmp
[Wed 08/01/30 07:06 MST][pts/1][x86_64/linux-gnu/2.6.22-14-generic][4.3.4]
<aaron@kratos:~>
zsh 169 % echo $?
0

First off, as we noticed above, it's not setting SGID. So, if not, it should be sending a message, such as "Permission denied" to STDERR. However, there is no such error message. Further, look at the exit status code. What's this? The exit code of chmod g+s is 0, meaning it succeeded (as shown by echoing the special variable '$?'). It did? It successfully set SGID? Not from what I can tell. So, not only is it not displaying an error to screen, it's returning the wrong exit code!

I don't know about you, but this is a BIG bug. Who's maintaining the 'coreutils' package and can this bug, as well as the cp bug mentioned in my previous post, get fixed? I'll post this to Launchpad.

Who Mom Likes

When you travel as much as I do, you have the opportunity- nay, the responsibility- to read man pages. While sharpening the saw, so-to-speak, on increasing my teaching skills, I came across the following command:

[Fri 08/01/11 06:05 MST][pts/1][x86_64/linux-gnu/2.6.22-14-generic][4.3.4]
<aaron@kratos:~>
zsh 31 % who am i
aaron    pts/1        2008-01-11 06:05 (:0.0)

Nothing special about that, but I thought to myself, "Are 'am' and 'i' really options to the 'who' command, or is it just one of those things?" So, i pulled up the man page on 'who'. Looking through the options, I don't see an 'am' or 'i' option. However, at the bottom of the man page, right before the AUTHOR section, the following paragraph is as follows:

       If FILE is not specified, use /var/run/utmp.  /var/log/wtmp as FILE  is
       common.   If  ARG1  ARG2  given, -m presumed: ‘am i’ or ‘mom likes’ are
       usual.

The '-m' option just means to print only the user and hostname associated with STDIN. However, the arguments 'mom' and 'likes' are also valid, instead of 'am' and 'i'. Are you serious?! So, from the terminal yet again, the following command is run:

[Fri 08/01/11 06:05 MST][pts/1][x86_64/linux-gnu/2.6.22-14-generic][4.3.4]
<aaron@kratos:~>
zsh 32 % who mom likes
aaron    pts/1        2008-01-11 06:05 (:0.0)

I always knew mom liked me best! 🙂

Pimp My Irssi

Warning: This post could be very long in many RSS readers and planets. Thus, going against how I feel about chopped RSS, I think it would be wise to do so here. At the bottom of the post, there is a "Continue Reading" link. If you're curious, please click through.

Per John Anderson's post about pimping his Irssi, I thought I would share my pimped Irssi. Here are a couple listings in my ~/.irssi/ directory.

[Sun 08/01/06 07:39 MST][pts/5][i686/linux-gnu/2.6.15-28-386][4.2.5]
<aaron@achilles:~/.irssi>
zsh 13 % ls -l *.theme        
-rw-r----- 1 aaron aaron 8472 2008-01-05 19:18 default.theme
-rw-r--r-- 1 aaron aaron 2690 2008-01-05 19:18 oscar.theme
[Sun 08/01/06 07:39 MST][pts/5][i686/linux-gnu/2.6.15-28-386][4.2.5]
<aaron@achilles:~/.irssi>
zsh 14 % ls -l scripts        
total 104
-rw-r--r-- 1 aaron aaron 70493 2007-10-03 16:43 adv_windowlist.pl
-rw-r--r-- 1 aaron aaron  1665 2007-10-03 16:26 autorejoin.pl
drwxr-xr-x 2 aaron aaron  4096 2008-01-05 19:01 autorun
-rw-r--r-- 1 aaron aaron  2191 2007-10-03 16:33 fnotify.pl
-rw-r--r-- 1 aaron aaron  7720 2007-10-03 16:28 screen_away.pl
-rw-r--r-- 1 aaron aaron  7730 2008-01-05 19:00 trackbar.pl
[Sun 08/01/06 07:39 MST][pts/5][i686/linux-gnu/2.6.15-28-386][4.2.5]
<aaron@achilles:~/.irssi>
zsh 15 % ls -l scripts/autorun
total 0
lrwxrwxrwx 1 aaron aaron 16 2007-10-03 16:26 autorejoin.pl -> ../autorejoin.pl
lrwxrwxrwx 1 aaron aaron 13 2007-12-18 10:56 fnotify.pl -> ../fnotify.pl
lrwxrwxrwx 1 aaron aaron 17 2007-10-03 16:29 screen_away.pl -> ../screen_away.pl
lrwxrwxrwx 1 aaron aaron 14 2008-01-05 19:01 trackbar.pl -> ../trackbar.pl

First off, I just found the oscar theme last night, and I love it. I visited the main Irssi site to see if there were any new themes added. To my surprise, there were quite a few. After spending some time analyzing screenshots, and testing a few out, I decided on oscar. Clean theme with green highlights and accents. There is an error when you load the theme, however, that you will notice in your status window. Not sure where the error is coming from.

Continue reading ›

Testing AlphaNumeric Arguments In Bash

Spending the evening working on my shell scripting, I thought I would jump into "Wicked Cool Shell Scripts" by Dave Taylor. In his script validalnum.sh, he has a test case to check if a user entered in valid alphabetic or numeric characters. His result is elegant and clean. I've changed up the script a bit for clarity:

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash

echo -n "Enter alphanumeric input: "
read input

compressed="$(echo $input | sed -e 's/[^[:alnum:]]//g')"

if [ "$compressed" != "$input" ] ; then
    echo "Input not valid."
else
    echo "Input valid."
fi

In this example, the user is asked to enter input that can be any combination of letters and numbers, regardless of case. If the user enters punctuation, the test case fails, and the user is notified of such. Otherwise, the test case passes, and everyone is happy.

I want to call to your attention the cornerstone of this script, however:

1
compressed="$(echo $input | sed -e 's/[^[:alnum:]]//g')"

The variable $compressed is holding only alphanumeric characters. This is done by taking the user input, and piping it to the stream editor sed. With sed, we are searching for any character in the string that is not a number or a letter. If such a character exists, we remove the character altogether. Thus, if $compressed removes any characters, then it does not match what the user entered, and our test will fail. If no characters were removed, then no punctuation exists in the input, and our test case will pass.

I thought this was most clever, and just had to share, hoping others benefit from this simple example. I also hope that Dave is not mad at me for taking an example, changing it up a bit, and presenting it on this blog. Thanks Dave.

Lastb And BTMP

Dear Lazyweb-

On my 64-bit Debian systems, and my Ubuntu systems, as root, the command 'lastb' is returning nothing. Lastb should return all failed login attempts to STDOUT. In order to do so, it needs to read the /var/log/btmp file. Upon examination of this file, it is empty. This puzzles me, as I know that I have failed logins before, so this file should contain those attempts. I only discovered this, as a friend of mine running Red Hat Enterprise Server 5.1 was having the same problem. Steve Dibb has also noticed this on his Gentoo system. This is disconcerting, as it should be logging the failed attempts, both local and remote. Now, before we start coming up with solutions, let me tell you what I've attempted thus far:

First off, the file itself. If you run 'lastb' as root, and get an error that the file is missing, then create the file as root with the touch command. However, the file already exists on my system, and here are it's permissions:

root@kratos:~# ls -l /var/log/btmp
-rw-rw---- 1 root utmp 0 Dec 18 16:18 /var/log/btmp

I intentionally removed the read bit from the other mode set, as this file could be a potential haven for crackers to find mistyped usernames as passwords. Everything permission-wise looks in order. What further complicates the mystery, is /var/log/wtmp is logging successful attempts as it should be, and the 'last' command, when run as root, returns those results. Knowing that these files are logged by init tells me that utmp, the process that writes the files, is in fact working. Why it writes only the successful attempts and not the failed ones is where the mystery lies.

The /etc/login.defs file defines where we should log failed attempts:

root@kratos: ~# cat /etc/login.defs | grep btmp
# last, when invoked as lastb, will read /var/log/btmp, so...
FTMP_FILE       /var/log/btmp

The FTMP_FILE is defined as /var/log/btmp in the login.defs file. So, the last thing that I can think of that is creating any issues whatsoever would be PAM. pam_tally.so is mentioned in /etc/login.defs, and it is the only file in /lib/securitty/ (pam_tally.so) that mentions /var/log/faillog. However, btmp is not mentioned.

So, I'm at a loss. I can't for the life of me figure out why utmp is not logging to /var/log/btmp for failed login attempts. If anyone reading this has any ideas, I'm all ears.

Yours truly-
Aaron