An Introduction to Linux

The Linux Graphical User Interface

What follows is a brief overview of how to use Linux from the command line using a terminal window and is usually that part most people find most intimidating. In fact most people seem to believe that this is the only way to use a Linux system which may explain why it scares them away. Overwhelmed by what they perceive to be an impossibly difficult interface, these folks flock to the more colorful and, presumably, easier graphical interface offered by Microsoft Windows and the Apple Mac.

It's only later, after the user has gained some skill, that the power and ease of management of Linux becomes desirable. Yet Linux provides the same graphical interface as their less competent competitors with all the same features and capabilities and with many more configurable options. It's even possible to mimic the look and feel of Windows and Macs so that the user can work with his computer in a familiar environment. The choice to work exclusively in the graphical environment is up to the user. If the user wants to get adventurous, there is always the command line and the command line is the best way to do the really cool stuff.

Your Login Environment
(Assumes a Redhat variant)

To access the Linux system you have to have an account. The account will have a login name and a password, you will use these to login. Once the login is accepted Linux will set your environment and execute any commands it finds your .bash_profile file (assuming your shell is bash . If the shell is sh or ksh .profile will execute, if it's csh .cshrc will be executed). When all is ready Linux will present you with a prompt, usually the dollar sign ($). The prompt tells you that you can enter commands.

To see the environment:

env

Or

set

The system will show you a list of variables that affect the way your commands will be executed. Since they are variables, you can change them to any value you like, although some settings may not work the way you expect.

The first file Linux executes after all of this is the .bash_profile (notice that there is a dot at the beginning of the file name . You can put any shell command in this file. Normally Linux will execute the commands in the system setup file to provide a basic environment. This file is called, /etc/bash_profile . This file is going to run regardless of anything else but you can change whatever that file does by adding commands to your personal .bash_profile file. Some of the things you might want to change are:

PS1 This is the prompt ($ by default)

HOME This can be any directory but is usually your "HOME" directory

PATH This is where Linux looks to find executable files

Notice that these are variables and they set the environment. They are, therefore, called environment variables. All environment variables are written as all caps (upper-case only). These variables are always available and can be used within shell programs. When any variable (environment or otherwise) is used it will be prefixed by the dollar sign ($) so the environment variable PS1, for instance, is referred to as $PS1.

To give a variable a value, the dollar sign is not used, instead you just use its name:

PS1='$HOSTNAME ${PWD#*}> '

Notice that there are no space between the variable name and whatever is being assigned to it. The quotes are required to make sure everything you are assigning is properly included in the variable. An interesting aside here, the single quotes (') are used to pass the quoted string to the shell with no parsing at all. The shell will strip off the quotes and then interpret the command string. With the double quotes (") the string would have be interpreted (parsed) before the shell got it and would've have seen something entirely different. These two kinds of quotes will confuse you for a while so experiment to see how they affect the way the shell works.

You can also add or change aliases . An alias is a command that executes other commands. An alias is useful if you want to modify that way a standard command works. An alias looks like this:

alias dir='ls -lA|grep "^d"'
alias web='cd /var/www/htdocs'
alias md='mkdir $@'
alias copy='cp $1 $2'

An alias is handy since you can quickly create your own commands. An alias is also executed before any command in your PATH so it takes precedence over existing commands of the same name (so be careful with alias names!). In the example above, alias dir='ls -lA|grep "^d"' creates a command called, dir and assigns it a value that will produce the same output as the ls -lA that's piped into grep which will return only strings that begin with a "d" character. It just lists directories.

You can also create a "shell function". A shell function is a complete shell program within another shell program (a shell program is called a script ). A shell function can be used as a sub-routine which means that it can be called within a script based on whatever condition you specify. This gives a shell script the power and flexibility of a real programming language. A function looks like this:

checkname()
{
while [ "$ans" = "" ]
do
echo "Enter your name ... "
read "ans"
done
}
checkname

Here the function is the function name followed by empty parentheses. On the next line is the left curly brace. This starts the function definition. Then there's some regular shell commands and then the right curly brace that ends the function definition. Now it's a sub-routine can be called from anywhere within the main script. function can also be executed from the commandline. To see what functions are defined type functions at the prompt. To see aliases, type aliases.

Using these features you can create a very sophisticated program that executes every time you login. You should experiment with the .bash_profile shell script to see how you can change your environment and tailor it to your needs.

After making a change to the .bash_profile you will want to execute it to see if everything works:

. .bash_profile

That's a dot (.) followed by a space and then .bash_profile. The first dot causes the shell to execute .bash_profile file as if you had just logged in.

If you have used Windows or DOS you will notice some differences in the way things are done. A directory uses the forward slash character (/) to separate directory elements under Linux instead of the backslash (\). The cd command by itself will return you to your HOME directory rather than just display the current directory.

There are no drive letters like C: or D:. Linux sees everything as a directory beneath the root (/) directory regardless of where it is on a disk or even which disk it may be on if you have more than one physical disk. Every part of a command must be separated from every other part by a space or tab. Linux uses the minus sign character (-) to include a command option while Windows and DOS use the slash character (/). Linux is case-sensitive. A command like LS is different than ls. Windows and DOS change everything to upper-case and can't tell the difference between upper and lower case. Linux has an on-line manual that explains every command in detail.

There are many other differences, large and small, between Windows/DOS and Linux, so much of what you may have understood from Windows and DOS may not be that helpful when using Linux. Past experience is helpful of course, but there's still much to learn.

A Linux command (actually a shell command since the shell is the mechanism used to parse and process commands) sees a command as a collection of pieces. The first piece is the command itself. Following the command there can be arguments. A command can be something like:

ls

an argument to ls might be an option that changes they way it works:

ls -lA

An option is always prefixed by the minus sign character (-) and is used to modify the command. There can be other arguments like a file or directory:

ls -lA /etc

In the example above there are three arguments: the command itself, the options and the directory (the object of the command). The command is an argument because we are actually asking the shell to process the command (the shell is the subject). Because the entire command line has a subject, a "verb" (the command and its options) and an object, it has a "syntax" or "grammar" and the shell is very picky about how you talk to it.

Since the usual way to number things when speaking to a computer is to begin with zero, the first argument is zero (0), the next is one (1) and then two (2) and so on. What's particularly useful about this is that these arguments are also variables. This makes it possible to refer to each part of a command as a series of variables beginning with zero:

$0 $1 $2 ... $n

So in the example above, ls is variable $0, the option -lA is $1 and the directory, /etc , is $2. These kinds of variables are called "positional parameters" and are defined by their position on the command line. The shell knows many kinds of variables and any of them can be used in a shell script. This capability makes it possible to write shell scripts that can "know" just about anything about the world they live in. Because of this flexibility, almost all of the Linux configuration is constructed using shell scripts.

The System Configuration Environment

Since the Linux system is configured almost entirely by shell scripts, it's fairly easy to examine and even change the system by modifying these scripts. Most of the major Linux system configuration files are located in the /etc directory. Within this directory are lots of configuration scripts and sub-directories containing still more scripts. Many of these scripts depend on still other scripts and, often, various non-script configuration files (often named with a .conf extension).

There are several directories that contain information Linux needs to start and run most of the network and system services . A service in this case is one or more programs that provide some feature required to make the system useful. A service will often refer to some other service so that configuring one can affect another.

The location of system files will vary depending on the distribution and version of the UNIX or Linux you're using. /etc/init.d is common but so is /etc/rc.d/init.d and the /etc/xinetd.d directory can be in other places than what I'm describing here (a Redhat Linux system). Some systems also use an /etc/inetd.conf file to manage network services. The log file locations can also vary as well as the location of the sendmail files and directories. You may have to poke around to find everything but regardless of the actual locations, their purpose is the same.

Two directories in particular are essential. The /etc/xinetd.d directory contains files that instruct some service on how it should operate. These are not scripts but rather lists of options a service uses to operate. The /etc/init.d directory contains a collection of shell scripts that are used to start and stop various services (typically, servers like apache or DNS). These are scripts that test the current environment for some odds and ends and then start a program based on that environment. In both of these directories the programs that the files and scripts refer to are called,

Daemons
A daemon is a program that is started (usually by a script) and continues running until you explicitly stop it or re-boot the machine. They will sit quietly in the "background" waiting for another program to request it to perform its service. Once the daemon has done its thing, it goes back into the background and waits for another request.

A somewhat complex example is the email daemon. This daemon is called sendmail and is used to process all the email services. When the system boots it will start all the daemons you have activated. If sendmail is configured to run, the boot process will run the script /etc/init.d/sendmail and pass it the option to start:

/etc/init.d/sendmail start

The sendmail script will first perform a bunch of checks to see how it should behave. If these checks pass, the script will start the sendmail daemon using whatever options you have configured.

During all of this, the script or sendmail itself will refer to configuration files located in the /etc/mail directory. This directory contains the files that actually determine how sendmail will work. Most of this information will be in the etc/mail/sendmail.cf file. Sendmail reads this file every time it starts so if you change something in this file you have to re-start sendmail. Most likely you will never even look at any of these files, but it's good to know where the are and what they do.

To see what daemons are configured to start when the machine boots:

chkconfig --list

This will display a listing of all the currently configured daemons. The list has the daemon on the left-most column and the various "run levels" in which it is supposed to run. A run level is a point in the boot process. Unlike Windows or Macs, Linux doesn't have to boot all the way before you can begin using it. The boot can be in stages. These stages, or run levels, provide some capability unique to that run level. The run levels are:

  • 0 shutdown the machine
  • 1 Single-user mode. Only someone at the system console can use the system.
  • 2 Multi-user mode. Here many people can log in and execute commands.
  • 3 The is the same as multi-user except networking is now enabled
  • 4 This run level isn't used (possibly reserved)
  • 5 This is the same as run level 3 except that the Graphical User Interface is enabled.
If you use the chkconfig -list command for sendmail, for instance, you will see the run levels in which it becomes active. It may not be active (on) in any run level if you aren't running a mail server.

The start up script for sendmail in /etc/init.d/sendmail will check to see if the daemon is enabled. If so the script will continue starting sendmail. If not it will just quit and do nothing.

To see which daemons get started and if there were start up problems, you can view the system log file located in /var/log/messages or /var/log/maillog (for sendmail). The Linux system logs just about everything that happens so you can check these logs if there is a problem or you suspect something is amiss.

There are several ways to view a file. Since most of the files you want to examine are plain text almost any configuration file can be viewed. The viewers are:

more

displays one screen-full of the file at a time. Hit the question mark at any time to get help on how to continue.
less
like more only less (some would say it was the other way around)
pr
this is mostly to display file for printing and rarely used.
cat
this will just dump the entire file without stopping This command is useful if you suspect a file has "special characters". These characters are supposed to be non-printing but they can interfere with the display. Use the -v option if you suspect these characters are in the file.
You can also use an editor to view files. The best all purpose editor is vi . Other editors are ed, emacs , sed and even awk . Theses editors let you view a file and also change it if you want.

To view the system log file, then, try:

more /var/log/messages

You can just view the last few lines of a file:

tail /var/log/maillog

Or maybe view only the first few lines:

head /var/log/lastlog

Since you will probably want to view some of these files fairly often, you might benefit

from creating an alias in your . bash_profile file:

alias syslog='tail -200 /var/log/messages'

alias maillog='tail -100 /var/log/maillog'

Now you just have to type the alias name to view the file.

The vi Editor
So how do you change a file anyway? The best all-purpose editor for creating and editing programs and scripts is vi . This editor has lots of very useful features for working with shell scripts and is by far the most convenient way manage editing tasks. Unfortunately, it is also kind of tricky to master. The best way to master vi is by simply using it. A good place to start is with the . bash_profile file. As mentioned above, this file is used to set up the environment for you after you've logged in. Like most shell commands, vi needs an argument, something to work on. This will be a file of course and, since .bash_profile is the file we want to edit:

vi .bash_profile

This will "open" the file and display the first line of the file. If it's a new file there won't be a first line so vi will display the tilde character (~). There are several ways to enter new text depending on where the cursor is located. Normally the cursor will be positioned on the first character of the first line.

The vi editor is a "screen editor" meaning that it displays one screen full of the file at a time and also that that can move around within that screen to edit stuff. To do all this it uses commands, usually of one letter. There are several way to access these commands. The simplest commands are those having the simplest function.

To quit vi :q
To write the changes to disk :w
To both write and quit vi :wq
To force and action use the "bang" first :!
To move the cursor to the bottom of the file :$
To move to the top of the file :^
To delete a range of lines :1-4d
To substitute one string with another :s/old_string/new_string/
To search for a string of characters /some string of characters/

Notice that these commands use another character at the beginning of the command. The colon character (:) is used to tell vi that the action applies to the entire file in most cases. The slash character is used to specify some string of characters ("words"). This hints at the most basic functions of vi, but to actually edit the file you have to use another kind of command. These commands don't use the colon (:), you just enter the command:

a add (append) text to the right of the cursor Hit the Esc key to exit
i insert text to the left of the cursor Hit the Esc key to exit
o create an empty line below the current line and enter editing mode Hit the Esc key to exit
O create an empty above the current line and enter editing mode Hit the Esc key to exit
D delete everything to the end of the current line
dd deleted the entire line
yy copy the current line
p paste deleted or copied text below the current line
P paste deleted or copied test above the current line
w skip to the beginning of the next "word"
W skip to the beginning of the next including any punctuation
e skip to the end of the current "word"
E skip to the end of the current "word" including any punctuation

These commands are used when you are actually editing a file. When in the text entry mode (a,i,o,O) you have to hit the Esc key to exit the editing mode. There's lots of other commands but these are enough to get you started.

Now we can edit the .bash_profile file (letters that are bold are commands you will enter:

vi .bash_profile

:$

a

alias syslog='tail -100 /var/log/messages'
alias maillog='tail -100 /var/log/maillog'
alias vi='/bin/vi'
alias dir='ls -lA|grep ^d'

Hit the Esc Key here ...

At this point you've added some text to the bottom of the file. If you write this file to disk and then quit,

:!wq

this file will now be permanently stored on the disk.

Now lets add something else:

vi .bash_profile
o
export PS1='$HOSTNAME ${PWD#*}> '
export PATH='$PATH:/usr/sbin:/usr/local/bin'

Hit the Esc Key here and then write and quit:

:wq!

You can edit, save and edit again as often as you want. Since this is your startup file that will execute every time you login, let's execute it now to see what it does:

. .bash_profile

Notice the dots (.). They're important, they tell the shell to execute this file and preserve the settings it may have changed. This technique is necessary because the shell will usually execute a copy of itself to run the script. This copy will execute and then disappear returning control back the original shell. Whatever that copy (sub-shell) does is forgotten as soon as it finishes so any settings it may have used are also forgotten. The dot (.) command tells the original shell to not use a sub-shell but to run the script itself. Doing it this way means that the environment settings won't be forgotten but will instead become part of the original shell. Sounds complicated but it makes sense once you've used it a few times.

So the dot command above will replace the existing environment with the settings added to the .bash_profile file. Now you can see the effect of the changes. If you don't like the changes, edit the .bash_profile file again, change stuff, write and quit vi and then run the dot command again.

Any changes to the shell's environment while in a sub-shell will be lost when the sub-shell finishes. To preserve changes to the environment use the export directive to force the change to exist in all subsequent sub-shells. If export is used in the .bash_profile file then that environment change will persist in all the sub-shells. Any changes to the shell's environment while in a sub-shell will be lost when the sub-shell finishes. To preserve changes to the environment use the export directive to force the change to exist in all subsequent sub-shells. If export is used in the .bash_profile file then that environment change will persist in all the sub-shells."

If you enter the .bash_profile entries above and run the dot command on .bash_profile, you will immediately see the difference. The prompt (the PS1 variable) will be different, you can just type, syslog to see the last 100 lines of the system log file /var/log/messages or maillog to see the /var/log/maillog file. Type dir (the alias you created).

It's important to understand that UNIX (and, later, Linux) has always been a multi-user and multi-tasking operating system. This means that many users can be executing many commands at the same time. To keep everything straight, each user is given a copy of the shell at login. This copy belongs to that user and that user can modify the environment using the .bash_profile startup file. The shell that belongs to a user can "spawn" (start) sub-shells and each sub-shell will belong to the original login shell for that user. This makes the multi-user, multi-tasking capability possible. Understanding this design is necessary to effectively manage your environment.

Creating Shell Scripts

A shell script is a plain text file that contains commands that can be executed as a group rather than one at a time from the command line. Not only is a script convenient, it's extremely powerful since you can construct some very complex commands specific to your needs. The .bash_profile file discussed earlier is a shell script but it doesn't really do much until you makes some changes to it. Once you have a working script, all you have to do to execute it is change its permissions and then just type the file name of the script to execute its commands.

A file or directory will always have some permissions that determine what kind of file it is and who can use it. These permissions assume three classes of possible users: the owner of the file, the group of users to which the owner belongs, and everyone else. Each of these classes of users will have three possible kinds of access: Read, Write and Execute. Read permission means that the file can viewed, write permission means that the file can be modified and execute permission means that the file is a program or script that can be run as a command.

Type

Owner

Group

Others

File

read

write

execute

read

write

execute

read

write

execute

1/0

1/0

1/0

1/0

1/0

1/0

1/0

1/0

1/0

NOTE:
You can combine the bits in a binary number and get the decimal value for those bits. Starting at the right-most bit (bit 0, the least significant bit) find the decimal value of the highest possible binary number for that bit position and then add those together to get the decimal value for all the bits that are set to 1.

If a bit is set to one in a bit position in the table, it has the decimal value shown. If more than one bit position is set to 1 then you add those bits together. If bit position 0 (decimal value 1) and bit position 6 (decimal value 64) are set to one and all the others are set to 0 then add 64 and 1 together for a decimal value of 65. All we care about are the bit positions that are set to 1 and we just add their decimal values together.

This kind of Binary Coded Decimal is used to set file and directory permissions and other stuff like a netmask or IP addresses. A "bit-mapped" value is different in that a bit set to 1 is used to "flag" something as in a IP packet or enable something in a configuration file.

Now, where was I ...

binary

1

1

1

1

0

1

1

0

1

octal

7

5

5

To provide read, write and execute permissions to the owner, use an octal 7. For read and execute permission use octal 5. A shell script will usually have at least access level 5.

To modify a file's permissions, use the chmod command:

chmod 755 file_name

Each class of user is identified using a 1 (one) for the kind of permission being granted. If that permission is one then access is granted, if it is empty (zero) then permission is denied.

Since there are three possible binary states for each permissions class, the permission for a class of user can be represented as an octal number. To see a file's permissions use the ls command with the -l option:

ls -l file_name
-rwxr-xr-x 1 0 0 1203 Jul 3 2004 file_name

The first column of information shows that this file has the permissions set to binary 111101101 which is octal 755 and this gives the owner read, write and execute permissions, the user's group has read and execute permissions and everyone else read and execute permissions. This is the normal permissions setting for a shell script file.

Pretty boring stuff but you really do need to understand how it works. There's a setting that usually gives all new files a default permissions setting. This is umask command:

umask 022

What this means is that all new files will be set to the "mask" of the this number. A mask in this sense means that every zero will create a one in the permissions and every 1 will create a zero in the permissions. Confusing? Of course but here's what's happening:

All Ones

111

111

111

Mask

000

010

010

Permissions

111

101

101

This uses binary addition which I won't go into here but it works (trust me) and Linux assumes a umask of 022 for all new files you create. This is usually what you want. All new files you create will also automatically belong to you and the group you belong to is also determined by Linux so you don't have to think about that at least.

So now you know all about file permissions and file ownership. You now need to create a shell script. This is the most interesting and challenging use of the Linux operating system since you can create all manner of useful programs for yourself. As a Systems Administrator you absolutely have to know how to create shell scripts, there's no more effective way to manage the system. While the Graphical User Interface (GUI) can do most of the systems management chores, there's far more possible when you work from the command line. Enough said, let's do it.

vi get_name
a
echo -e "Enter your name ... \c"
read "name"
if [ "$name" = "" ]
then
echo "You aren't serious are you."
exit 0
fi
f1=`echo "$name"|cut -c1|tr [a-z] [A-Z]`
f2=`echo "$name"|cut -c2-`
name=${f1}${f2}
echo "Hello $name"

Simple script really but it's doing quite a lot. The echo command will display the following word or string. If the string contains spaces or tabs use the double quotes ("). It's usually best to always use the double quotes whether you need them or not. The -e option tells the echo command to interpret the \c notation at the end of the string. (this is a special character that affects how echo displays the string, see man echo ). The string is a prompt asking for user input.

Next is the read command. This will take everything you type up to hitting the Enter key and store it in a variable. The variable here is named, name. It is quoted because we want to catch the possibility that user enters nothing (just hits Enter). By using the quotes we can tell if the variable, name, is null or unset. The difference is that null means the variable exists but has no value. An unset variable doesn't even exist.

The if statement will compare (test, see man test ) the variable name with null. By using the quotes around the variable, we can see if it's null or unset. The null value is just the two double quotes within nothing in between. Without these quotes, the if statement will fail if the variable is empty because the variable is unset and therefore can't be tested. With the quotes we can see that the variable exists but is empty. If the shell tries to test an unset variable it's going to fail so use the quotes.

If it turns out that the variable is null (you didn't enter anything), the script will echo a string of text and then exit. A shell script always have an exit value to let you know if the script completed without errors. An exit value of zero means that the script has no errors. There's no benefit to returning a failure exit code because the script worked correctly even though the user misused it. In other words the script is ok.

If the user entered something the script will assume that it's his (or her) name and pass the value of the name variable to the next step. When the shell sees the fi it knows that the if statement has completed and move to the next line. The next line in this case will create another variable named, f1 .

Since the name variable has a usable value (presumably a name), we can use that value to define the f1 variable. Here we use a variable type called, command substitution .

This type of variable will have the value of a command we execute. The command will output something and that is what will become the value of the variable we want to define.

We use the echo command again to display the value of the name variable we acquired earlier from the read command. This time though we don't display it to the screen, we send it to a "pipe" instead. A pipe (|) is a mechanism to take the output of a command and dump it into the input of another command. Think of it as an actual pipe with one end connected to one command and the other end to some other command. A simple concept that is hard to explain, just use it to see how it works.

The cut command that follows the pipe will take the value of the name variable and process it using the -c option. The cut command is used to cut up a string and display the results. The -c option means to cut some number of characters. Here we just care about the first character of the name variable. We take this character and pass it to another pipe and from there to the tr command. the tr command will "translate" the character passed to it to something else. We use it here to convert any lower case letter to upper case. We capitalize it.

On the next line we do pretty much the same thing except we take all the characters of the name variable except the first and we don't care about capitalization.

Next we combine the two variables we just created to create a new value for the name variable. Now we take this new name variable and echo it back to the user. He (or she) will see their name displayed and properly capitalized in case they neglected to do it.

Not terribly exciting but it demonstrates some of the cool things you can do with a shell script. With just the few commands we used above, you can device some pretty sophisticated scripts that can give the appearance of intelligence and choice.

For more detail on these commands use the on-line manual for the commands. Type man and then one of:

read

exit

cut

tr

echo

test

To find about more about variables, man bash . The manual entry for bash has all manner of detail about creating scripts. While the prose is difficult and terse, there's most of what you need to know about creating scripts hidden in its pages. Read it and then read again and then go back and read it some more. It is the authoritative source of almost everything you need to know.

You can create little shell scripts to view log files and collect other information that can make maintaining a Linux system easier. These kinds of scripts don't have to be very elaborate and, in some cases, can be replaced with an alias or shell function. Just for practice though we'll create some scripts to show some of the techniques available.

vi chklogs
a
if [ "$1" = "" ]
then
log="/var/log/messages"
else
if [ -f "/var/log/$1" ]
then
log="/var/log/$1"
fi
more $log

Hit the Esc key to get out of the editing mode and then:

: wq!
chmod 755 chklogs

This script will expect you to enter the log file you want to view. The "$1" variable means the first argument after the command (the shell script name). if you don't provide a log file to view, the script will assume that you want to view the system log at /var/log/messages . If you do provide a log file on the command line, the script will use that file name if it really exists. In both cases the script will use the log file name you provide or the default to create the variable log .

All of this was done using an if statement. The if statement will first check (test) to see if there is an argument to the command. There are several ways to indicate a test condition. I always use the square brackets ([]) since, for me, it's easier to read. The test will compare the positional parameter variable $1 to null. If true ($1 is null, notice the double quotes!), then the variable log is assigned a default value (/var/log/messages). If false then $1 has a value and execution drops down to the else part of the if statement. Here we test to see if the file exists (-f). If so we assign the log variable the value of $1 since we know we have a real log file name.

Next we use the log variable as an argument to the more command which will display the file named by the log variable one screen at a time. The logic of the script is quite simple but preparing everything is somewhat involved. All we really wanted to do was view a file but we had to make sure the user entered something we can use. To ensure that the script will work we have to test the user input before using it. Lots of scripts have to do this checking to prevent confusing errors.

After writing and quitting vi we use the chmod command to change the permissions of the script to make it executable. Now we can just type the name of the script and provide a log file to view (or use the default /var/log/messages file):

chklogs

or

chklogs maillog

Let's create little script to monitor the system login information files for changes. If someone hacks into the machine and modifies these files by adding a login, we can devise a test to alert us.

All user accounts are stored in the /etc/passwd file. This file has all the information about a user needed to permit him or her to login. In most systems the password information is stored in another file, /etc/shadow that can only be viewed by root (the system owner and manager, a very powerful special account). The /etc/shadow file contains the passwords for every user in encrypted form. While the encryption is pretty much impossible to crack, keeping even the encrypted form hidden makes it even more difficult to hack into a user's account.

vi chkpw
a
if [ `sum /etc/passwd|awk '{print $1,$2}'` != `cat /etc/p.sum ]
then
mail -s "Password file has changed" < /etc/pw_alert root
fi
if [ `sum /etc/shadow|awk '{print $1,$2}'` != `cat /etc/s.sum ]
then
mail -s "Shadow file has changed" < /etc/pw_alert root
fi
sum /etc/passwd|awk '{print $1.$2}' > /etc/p.sum
sum /etc/shadow|awk '{print $1.$2}' > /etc/s.sum
chmod 600 p.sum s.sum

Hit Esc to quit editing mode

:wq!
chmod 755 chkpw

The first time this script is run the root user will get an email saying the password and shadow files have changed. That's because the stuff we're checking for doesn't exist yet. You can ignore this email.

What this script is doing is testing the output of a command and comparing that to the output of another command. This is accomplished using command substitution variables. This kind of variables uses the "grave accent" character (`) to enclose the command. This kind of quote character tells the shell to execute the command it contains.

The first command is sum . This command will do a "checksum" calculation on a file (or string). This will output a pair of numbers that will be unique for the file. It's very difficult to change a file without also changing its check sum so it's a handy way to see if a file has been modified. We are testing the checksum on the /etc/passwd file.

This is followed by a pipe which passes the output of the sum command to another command, awk . The awk command is a very powerful command and almost a programming language in itself (see my awk guide Here). What we want here is to grab just the first two "words" (fields) of the awk output. The sum command will output three fields (words). The last field is the file name being checked. We don't care about that so we have awk just display the first two, numeric fields. The awk command understands positional parameters so you can use them the same way as in a regular script: $1, $2, $3, etc. Since we only care about the first two fields (positional parameters $1 and $2) we tell awk to only display those two using the built-in awk print statement.

The test operator != says, "is not equal to" (the bang (!) means "not") so we are comparing the output of the first command substitution to the output of the next command substitution.

This second command substitution is the output we get with, cat /etc/p.sum .

The cat command will just dump the contents of a file. When compared we expect both commands to output exactly the same two numeric fields; the output of both commands should be identical. If they are not the same we use the mail command to send the root user an alert message.. The -s option to mail lets us create a subject line for the email. We use the < operator to pass to the mail command the contents of a file. This < operator is called "input re-direction" and tells the shell to use this file for input to the command instead of the keyboard. There is also an output re-direction operator that sends the output of a file or command to another file.

The very same logic is used to run the very same tests on the /etc/shadow file. If either test fails, root gets email with an alert message. After all this we use the sum command to replace the current contents of the two check file with new checksums. This has the effect of updating these check files for future checks. After these files are updated, we ensure that they can only be read or written by root and no one else by using the chmod command.

All we need now is to create the alert file that is mailed to root:

vi pw_alert
a
There have been changes to /etc/passwd or /etc/shadow since the last check

Hit the Esc key

:wq!

Now we have a way to track changes to user account information and any attempt to create a new account.

About Quoting

The shell uses different kinds of quoting for different purposes. These quotes are:

Grave accent

`

command substitution

single quote

'

strip any "special" meanings from a string

double quote

"

shell interprets special characters

backslash

\

strip special meaning from the next character

curly brace

{}

enclose a variable to preserve its name

If you use the wrong quote there will be an error or other strange behavior. When using quotes in a script make sure to provide both an opening and closing quote of the same type (except for the backslash). The bash manual entry ( man bash ) has more detail. In almost every case you should use the double quotes when creating or referring to a variable, "$1" is safer than just $1.

If you need to include a "special character" in a string put the backslash in front of it like this:

echo -e "You owe me \$${100}!"

The backslash in front of the first dollar sign will cause the shell to display the dollar sign as just another character. The second dollar sign is not quoted by the backslash so the shell will assume it is a variable. By enclosing the variable in the "curly braces" ({}), we can put another character right next to the variable name. Without the curly braces any character following the variable name will seen by the shell as part of the variable name and that will most likely not work as expected.

About "Words" in the shell

The shell chops up the command line into pieces ( arguments ) by using an environment variable called the "Internal Field Separator" or IFS. This will be the space , the tab and the newline (the Enter key). Because of this behavior, every argument on the command must be separated from every other by a space or tab. While in DOS or Windows it's ok to type a command like,

dir/w

with Linux something like,

ls-l

will be interpreted by the shell as the single command, ls-l . It is seen as one "word". These "words" (arguments) are more correctly called, fields, since many shell commands (like awk ) refer to them as fields in their documentation. It also makes it easier to visualize a command or string as having fields since quite often you are creating or modifying files that are flat-file databases where the notion of records and fields is essential to understanding their function.

Regular Expressions

One of the handiest features of the Linux shell are "regular expression". Unfortunately, it's also one of the trickiest to master. It's worth the trouble to learn what they are and how they are used but it takes some time. A regular expression is a way to create or find a string without specifying it explicitly, or sometimes, even knowing what it is. This is done by substituting a special notation in the place of the some number of characters. An example would be to take a string of characters like, "Bill writes excellent documentation for Linux!" and replace some parts of the string with special characters, like this:

grep "^[Bb]i.*excel*x$" some_file

Believe it or not this will match the string. The grep command is used to locate a string in a file. I suggest using the double quotes (") around the regular expression. If it finds a match it will display the entire line that contains the match. The regular expression begins with the caret character (^). This tells grep to start looking for a string that begins with the following character. The notation [Bb] will search for a string having either the upper case or lower case B. So we want a string that begins with either a B or b. The square brackets ([]) will represent one character only but that character can be any one of those listed with the brackets. To represent a range of characters, use this [m-r] or [Aa-Cc].

Following the closing bracket is the letter i with no spaces and then a dot. This means that the second character in the string will be a lower case i. the dot (.) represents any character and can be a letter, a number or even punctuation. Next is the asterisk (*, no space). This will match anything at all, even nothing. Next comes the letters, excel. Then another asterisk (matching anything, of any length). Now comes the letter x and, finally, a dollar sign ($). The dollar sign means the end of the line. Put it all together and you can match the example string exactly.

Of course this formula can match lots of other strings too. To ensure that you only get the string you really want, construct the regular expression using some unique combination of characters.

You can use a regular expression almost anywhere. With the ls command, for instance, you can match files having some unique part of the file's name:

ls -l *gz
ls -lA .*
ls report[0-9].*

You will see lots of regular expressions used in the various system configuration files so you will definitely need to understand them to manage these files. The best source of detailed information is in the manual entry for ed ( man ed ) which, alas, is difficult to read and understand but the authority on regular expressions. While ed is one of the oldest UNIX editors, and rarely used for interactive editing, it is extremely handy within a shell script.

vi ed_script
a
while [ "$name" = "" ]
do
echo -e "Enter your login name, fool ... \c"
read "name"
continue
done
if [ `grep -c ":${name}: /etc/passwd` -ge 1 ]
then
ed /etc/passwd < /^${name}: /
s/:\/bin\/sh/\/bin\/bash
fi
w
q
!

chmod 755 ed_script

In this script we're going to use the while statement. The while statement will evaluate the stuff within the square brackets and, if the evaluation (test) is true it will drop down to line after the do statement. The test is to see if the $name variable is null. If so (or if it's unset) the script execution will display the echo line. The first time the script its executed the $name variable won't be set so the echo statement will execute. This prompts the user to enter something. If this step assigns something to the $name variable, then the while statement is satisfied and sends the shell to the line following the done statement. If the user just hits the Enter key, the $name variable will still have no value so the continue statement forces the shell to go back and start over at the while statement line.

Once we've gotten some user input, we cause the shell to use the if statement to perform another test. This time we use grep to find a string in the /etc/passwd file. The -c option to grep means to just return the count of lines that match. If there is at least one line matching (the -ge operator in the test means, "greater than or equal to") then we know we have a string to work on.

Now that we have our matching string, we use the ed command to edit the /etc/passwd file. The notation < here document. This kind of operation will execute the command that called it as if was executed from the command line. The << part creates the here document and the bang (!, or some other character if you like) tells the shell that everything up the line containing only a bang (!) is to be executed. When the shell script sees the bang character on a line by itself, it knows that it has reached the end of the here document. You can learn more about the here document in the manual entry for bash ( man bash ).

The line:

/^${name}: /

is the same as in the vi editor. The slash (/) characters enclose a regular expression. If the regular expression matches a string in the file that ed is editing, ed jumps to the line having that regular expression. Once at that line, we can give ed some commands that affects just that line.

The next line:

s/:\/bin\/sh/\/bin\/bash

is again like vi. We are telling the ed editor to substitute a string that begins with the colon character (:). The notation \/ is how we use the backslash to quote the next character (quoting with the backslash (\) removes any special meaning from the following character. This is also known as "escaping" a character's special meaning). So what we are saying is, "substitute a string that begins with the colon (:) followed by the slash character (/), the word bin and then another slash character, and then the word sh with the string, "/ bin/bash ". Pay attention to the "escaped" slash characters. Four of them are escaped with the backslash and two are not. The two that are not escaped are used to replace the first string with the second.

Once we've done all that, we send the ed command a w command and then q command. This will write the changes to the file and then quit. After that is the bang character (!) that ends the here document. The here document is very handy but it's probably the only time you will use the ed editor.

There's one more script we should try. Suppose we have directory of files that we want to change the permissions and ownership on. Ordinarily we would have to run the chmod and chown command on each file and directory and that could take quite while. Let's use a script:

vi fix_files
a
for i in `ls /var/spool/mail`
do
echo "## Changing permissions and ownership on $i ##"
chmod 760 /var/spool/mail/$i
chown ${i}.mail /var/spool/mail/$i
done

Hit the Esc key and then

:wq!
chmod 755 fix_files

This time we will use the for statement to create the script. The for statement will use a list of things to look at which in this case is the output of the command substitution for the ls command. The for statement will take each line passed to it by ls and put it in the variable we've named " i ". it will then jump to the line after the do statement and execute the lines it finds there. What we're doing this time is using echo to display a informative string to the user. It will also display the current value of the $i variable.

After that the script will use the chmod command to change the permissions for the file named by the current value of $i. Next it executes the chown command to change the file's ownership and group still using the current value of $i. When the shell sees the done statement it goes back to the top and gets the next line from the list and performs the same operations on it. When the list has displayed everything, it's next value will be null and the for statement just quits.

These examples are just some ideas on how the shell can used to create some really useful scripts. Obviously all the techniques I've demonstrated can be combined into much more complex and comprehensive scripts. If you're clever, you can make the machine appear almost intelligent. When you understand the various capabilities of the shell, you can read and fully comprehend the system configuration files. By modifying the system shell scripts, you can add features, enable and disable stuff and customize things to suit yourself.

If you find that you are frequently typing the same commands over and over again, consider creating a script (or alias) to automate the task. A good candidate might be something like:

find . /depth -print |awk '{print $3}'|sed "s/\.tar$/\.tar_file/"

This command is a pain to type and prone to typing mistakes. A shell script can perform the same function plus have lots of extra features making the command even more powerful. The easiest way to being is to just create very simple scripts and add features. Test the script,

bash -x script_name

This will display each line of the script as it is executed and show the processing of all tests and the assignment of all variables. You can use the output to find out where the script failed or why something like a variable wasn't set or had the wrong value.

Systems Administration

The main function of a Systems Administrator is to make sure that the machine he or she manages behaves like it's supposed to. While it might seem that setting things up right in the first place would be enough but it rarely is. Once things are working, the Systems Administrator has to also monitor everything to make sure it keeps working. It often happens that a configuration will have to change as features are added or changed or when something begins to fail or behave erratically.

One of the most common causes for a configuration change is that the original purpose for having the machine in the first place, changes. You may have planned on it having it perform some set of tasks and then decided to add some functionality. There may also be a need to enhance its performance or enable additional security. The point is that the way a computer is used can evolve so you have to know manage the changes.

A second function of a Systems Administrator has to do with the fact that a Linux machine is meant to be networked to other computers. Most of the Linux operating system assumes it will have a role in a network To use Linux for a stand-alone home PC is to waste much of its capability. Linux is a networked operating system by design.

Almost every administration task on Linux can be done using a GUI (Graphical User Interface). The default login session will start the GUI and setup a standard windowing display with icons and mouse support. The main system menu will contain sub-menus you can access to manage almost everything. This makes it pretty easy to manage the system and it's possible that you could do everything using this interface. Even so, if don't know how to read, modify and create shell scripts, you will eventually run into some very confusing problems. Use the GUI for simplicity but be prepared to verify any changes by examining the files and scripts that are created or modified.

Because you will use Linux in a network, it will help to understand what that means. A network is supposed to allow computers to exchange resources. A resource can be a file or printer or information. In a Linux network a resource can also be a "server". A server is a program that provides a service to other (client) computers. Linux supports all the major servers like email, the web, ftp, DNS, DHCP, telnet and a bunch of others.

The client will connect to the Linux machine (usually called the server because it provides the service you want to access) and request a service.

The Linux machine will start a server program (usually a daemon ) to handle the request. The services use a set of standards called protocols to handle requests. these protocols are very precisely defined and well documented (most often by an RFC , R equest F or C omment). The most common protocol for most services is TCP/IP (Transmission Control Protocol / Internet Protocol). TCP/IP provides the connection information for networked computers. Within this information will be a request for a specific application protocol like ftp or httpd . The TCP/IP and application protocol information is contained in a packet . The packet will also contain the data the application is supposed to process.

The file /etc/services lists all the most common protocols. The file also specifies which port the application will use. A port in this case is a location that specifies how the application protocol will communicate with the application program (daemon). The combination of the requesting computer's IP address and the port number is called a socket . If a computer wants to telnet to the Linux server, for instance, it will send a TCP/IP protocol request packet requesting a connection to the telnet port (23). If the Linux server accepts the request it passes it to the telnet daemon (usually /sbin/in.telnetd) and that program will process the request according to the telnet protocol (as defined in an RFC).

Most of the common daemons are located in either /etc/xinetd.d or /etc/init.d . The xinetd.d files are actually setup files that provide arguments to the daemon to be started. The files in init.d are shell scripts that execute to provide the daemon's environment. There are also files in the /etc directory that provide startup configuration for various other applications (not necessarily daemons). These files end with the string, .conf and are arguments to the application.

There are some servers and applications that are installed in other places. The webserver daemon, httpd , is located at /etc/httpd/bin/httpd with its configuration file in /etc/httpd/conf . The ftp server may be located in a directory named after the daemon's name like /etc/vsftpd . Almost all of the configuration files will be somewhere in the /etc directory but they might also be in /opt or /usr/local . Since they are started by scripts in /etc/init.d or configuration files in /etc/xinetd.d directories, you can view these files to find out where the daemon and configuration files are located. Another good reason to understand shell scripting.

There are also lots of configuration files in the directories named rc*.d The * will be number from 0 to 6. These numbers represent the "run-level" to which the system was booted (run-level 5 is the default). These files are actually links to the files in /etc/init.d . These links will execute a script in /etc/init.d .

Most of the system logs will be in the /var/log directory although some (like httpd ) will keep their logs elsewhere. You can view these logs to find out how things are working or why they failed. Most log files have the same format.

The date and time of the entry and which user or which program was running and any additional information or error messages that the program output, one entry per line. These logs can provide all the information you will need to get a good start on locating and correcting any failures. Some handy network commands for managing the system are:

netstat

reports what the network cards are seeing

ifconfig

shows the configuration for the network cards

chkconfig

shows or enables/disables network services

route

show the routing configuration

traceroute

see how packets get to another computer

ping

see if another computer is "up"

nslookup

discover a computer's name or IP address

There are also quite a few very handy add-on programs for managing and monitoring the network:

webmin

This provides a great web interface to manage most of the system

nmap

This is part of Linux but it has options you can add. For scanning ports

nessus

a nice graphical port scanner

snort

a packet sniffer to see how your ports are being accessed. Also a good IDS

tripwire

monitors system files to detect changes

mrtg

use to create web based graphs of network traffic usage

When you decide to add a program (package) to Linux you first download it (try rpmfind.org). You will usually end up with a file having a name that ends: .tar.gz . The gz part means it's compressed (zipped) and the tar part means that it's an archive. The steps to install the file are usually:

tar -zxvf some_file.tar
cd some_file
. ./configure
make
make install

This will unpack the archive and install it. There is also usually a README file and a INSTALL file. Read these to see if you need to do anything else. The configure shell script will also have information that may make the installation easier.

There are also packages that have been configured to install themselves. Redhat Linux use the rpm (Redhat Package Management) command to install a package:

rpm -i some_file.rpm

Debian uses a command called, aptget to do the same thing. Once a package is installed it becomes part of the system. You should check the log files after a new installation to make sure it isn't causing problems. An RPM usually installs itself in the /usr/local/ directory.

The best way to become a competent Systems Administrator is to constantly watch the logs and install monitoring software like tripwire to make sure there are no changes that you haven't authorized. If you are administering a network server be very careful about what daemons are enabled and who has access to what. A good place to start is by creating an /etc/hosts.allow file. This will invoke tcpwrappers to allow or deny access based on hostname or IP address. Make sure that every user account has a valid password and pay attention to how users are using their accounts by checking the logs.

You'll also have to spend a lot of time digging through the on-line manual. The entries are difficult to read and even more difficult to comprehend but, once you've done it a few times it gets easier. Linux also provides a utility for a quick summary of a command:

info cat

There's also hundreds of very handy web sites where you can refer to case studies and special or interesting uses. Web searches can also turn up some specific answers especially for unusual log file entries. Cut and paste a log file error into a Web search and see what happens (use double quotes to make the whole string one single search term.

Use vi or more to view the various configuration files and server startup scripts to see how they are constructed. You will learn how shell scripts are written as well as discover how the system is set up. Poke around and look at everything. Don't change anything though, just look and learn. Write simple shell scripts and, once they're working, add features and try stuff out. If a script fails it will usually display an error message that you can use to correct problems.

Copy and existing shell script to your HOME directory and play with it. change little pieces and see what happens. Copy the /etc/passwd file to your HOME directory and practice finding information using commands like grep and awk, ed, sed and here documents. The /etc/passwd file is cool because each item of specific information is separated by a colon (:) from every other item. You can, therefore, extract stuff by keying on the colon character. Experiment, play and learn. You will be surprised how quickly you begin making sense of it all.

Return to the Contents Page