tutorial 3: developing shell scripts

reminder

Remember to do all of these exercises using a copy of the files in the demos directory which you have placed into your own working directory. If you make a mistake you can always take another copy.

Getting started

Edit a file e.g. myshell.sh containing as the first line:

#!/bin/sh

Follow this with a set of command lines, e.g. to list a directory and count the files in this directory. Give execute rights to your script using:

chmod +x myshell.sh
To execute it use the command:
./myshell.sh

The menu skeleton script

Copy, run and inspect the source code for the menu script. Run it with and without the debug parameter e.g. to run it in debug mode the command line would be:

./menu debug

Introduce a selection of deliberate bugs into the script one at a time, and investigate how you might find these bugs if you did not know where/what they were.

Modify your version of menu so that the first option lists the contents of the current working directory, the second option displays the path of this directory, and the third option changes this directory to a path (relative or absolute) as input by the shell script user.

Try listing a very large directory (e.g. /etc ) using your version, which requires more than one page of output. Develop it further to pipe the output of the ls command to make use of the less command, so the script user can page the output data using less.

Introduce a test within the script to see if the path which the user wishes to change to is a directory, and avoid attempting to change unless the request is valid. If it isn't a directory, output a suitable error to the script user instead. You may need to scan through the man page for bash to find the section containing tests of this kind.

To do this enhancement, you will need to find out the size of the directory first before listing it. Count the number of lines in this listing and if it is less than 10 display it without using less. If it is 10 lines or more display the directory using less.

The convert script

This Bourne shell script is based on a similar structure to the login records analysis program (logan) which is explained in the lecture notes. Read the source code for convert and describe in your log book what it does. Run this program to convert a set of measurements in the measurements file in yards, feet and inches into a combined total expressed in meters.

Update this program to use internal Bash shell arithmetic using the let builtin function instead of the outdated use of the external expr program. Test that this updated program gives the same results.

Automating addition of a system user

Objective

You may be able to complete some or all of this exercise, but the more you can do, the more you will learn. This exercise involves writing a shell script to automate as much of this task as your level of access to the system allows, and then considering further development of this program for use within a college environment where a Unix-type system is used by hundreds of students, who arrive and leave at the start and end of courses.

The purpose of your shell program initially is to prompt the administrator and query the system for all information needed, and carry out all neccessary steps to modify files /etc/passwd and /etc/shadow so that a single new user with a password and login account can be added to your system. A real add-user script would also create a home directory for the new user, assigning the correct ownership and permissions, but until you have administrator rights on a system of your own and feel confident enough to run your script with administrator privileges, you are asked to write the part which creates the home directory and assigns user and group ownership to it, but comment it out, leaving this part untested. A real add_user script would also copy the (hidden system) files in /etc/skel into the new users' home directory.

If you don't have administrator privileges on your own system, your shell program for adding a user will have to add lines for a new user to copies or made up examples of the shadow, passwd and group files. You will only need to add a line to the group file if the user is in a new group.

Research

Take copies of /etc/passwd and /etc/group and read these. Read the following manual entries: passwd(5), group(5), shadow(5), crypt(3), date(1), chown(1).

To read passwd(5) you should use the command: man 5 passwd

If you just use the man passwd command, without the section number, you will get passwd(1), which is the passwd command not the passwd file. Typical lines from these files might include:

From /etc/passwd :

peter:x:501:555:Peter Smith:/home/peter:/bin/bash
From /etc/group :
students:x:555:

You won't have rights to read the /etc/shadow file unless you have a system of your own with administrator privileges. Here is a typical line you might find in /etc/shadow to get you started:

peter:12CsGd8FRcMSM:11995:0:99999:7:::

Generating the encrypted password

The hashed password: 12CsGd8FRcMSM was generated using the following 'C' program demonstrating simple use of the crypt(3) system call:

#define _XOPEN_SOURCE
#include <unistd.h>
#include <stdio.h>
int main(void){
       char *salt="12",*key="password";
       printf("%s\n",crypt(key,salt));
       return 0;
}

Peter had chosen the very weak password of "password". The crypt(3) library function is used to generate a one-way encrypted, hashed password, of the form used in the shadow file. As stated in crypt(3), you must use the -lcrypt option of gcc when you compile a file containing GNU crypt. Here is the command I used:

gcc crypt.c -lcrypt -o crypt

You are asked to develop this program so that the shell script user is prompted for the plain text password to use. Your 'C' program making use of crypt(3) to encrypt this password will generate the hashed string suitable to insert into the shadow file. You can use a fixed salt as in the above example. (We will look at how you might generate random password and salt strings later.)

Counting days

The number: 11995 was the number of days from the Unix epoch (1/1/1970) when the password was last changed. You can get the current date by using date(1), and obtain the number of days since the epoch with some additional shell arithmetic if you wish. Otherwise if you just use 0 for this the login and password facilities should still work.

questions and further development

What changes would you have to make to this program to enable it to create user accounts for groups of up to 100 students at time ?

Would it be efficient or secure to type their passwords or other details in on the keyboard? What difference would it make if user names and passwords were already available in the form of a text file ?

How would you go about automating as much of this job as possible ?

Consider how you would write a script to remove groups of users from the system. (Hint: once you have identified the line number in a file to delete, an appropriate sed command will remove this line from a set of lines within a pipeline.) You may identify groups either by using the /etc/group file, or by using numeric ranges, e.g. usr10000 - usr10099 .

Write your answers and printed copies of the source code you develop to answer these questions in your logbook.