Operating Systems and Systems Integration Assignment 1: Shell Programming — Solutions Submission: by 5pm, Monday, 10 February 2003 Where: Electronically via this url: http://ictlab.tyict.vtc.edu.hk/perl2/submit. cgi You can submit as many times as you like; a later submission will never overwrite an earlier submission. Format: Must be plain text. Submissions in proprietary formats (i.e., Word documents) will be copied straight to /dev/null and will receive ZERO MARKS. You can put the shell scripts together as a .zip file or as a tarball. Do not submit .rar archives. Add your email address Put your email address in a comment on the second line of your submissions, so that I can return them marked to you. I will not return assignments that do not have an email address on the second line of each file. Weighting: This assignment shall provide 20% of your Continuous Assessment mark, i.e., 10% of the total marks for this subject. Marking Criteria: Originality is essential. Efficient implementations get more marks. Elegant and well-structured solutions get more marks. Flexible solutions get more marks (i.e., offer more useful options to the user). Original submissions that answer the first four of these questions will receive a bonus of 5% if they are submitted one week before the deadline. Cheating: Your work must be original. Copying will be severely dealt with. 1 1.1 Background Example Programs and Online Resources I have written a number of example shell scripts—please download them, run them, examine them, understand them. Get them from http://ictlab.tyict.vtc.edu.hk/ ossi/lectures/shell/bin/. My colleague Joe Lee, the lecturer for the part time version of this subject, wrote some of these examples too. Thanks, Joe. Please also remember that there are hundreds of excellent examples in the freely downloadable book Advanced Bash Scripting Guide at http://tldp.org/LDP/abs/html/ index.html. There is also an excellent book about the awk programming language, available from http://www.ssc.com/ssc/eap/. Note: I have put one copy of the book, Learning the Bash Shell, 2nd edition, by Cameron Newham and Bill Rosenblat, in the library open reserve. Another copy is on the shelves for loan. I have also purchased some other books on shell programming for Nick Urbanik ver. 1.3 Assignment 1: Shell Programming — Operating Systems and Systems Integration Solutions 2 the library, and expect them to be available after Christmas. One book you may find helpful is UNIX Power Tools, 3rd edition. 1.2 Set User ID and Set Group ID Permissions Files that have the “set user id bit” (suid) set, and those which have the “set group id bit” (sgid) set may be security risks, since a program file with the permission suid will execute as the user that owns the file, while a program with the sgid permission will execute as the group owner of the file. You can set the suid permission on a file like this: $ sudo chmod u+s file You can remove the suid permission from the file called file with this command: $ sudo chmod u-s file Similarly, you can add the sgid permission with $ sudo chmod g+s file Read the documentation for chmod with $ info chmod or $ pinfo chmod My preferred way to read info documentation is in emacs by: • start  emacs ¨¤ § Control-h © ¥ go to the top info menu i • type  ¦ to § ¤ • type ¨ I.e., hold down the ¦ Control ¥ key, and press the ¦ ¥ h key, then release them both, §¤ then press the ¦ ¥ i key. §¤ m chmod © • Click on hyperlinks with the middle mouse button, and also click on the arrows at the top of emacs. You can use the -perms option to find to identify files that have any particular permissions. 1.3 Some uses of the RPM Package Manager, rpm Using the rpm Package Manager, rpm, you can: • determine which software package a file belongs to: $ rpm -qf file and determine what the installed permissions were for the files that belong to a software package called package : Nick Urbanik ver. 1.3 Assignment 1: Shell Programming — Operating Systems and Systems Integration Solutions 3 rpm -qlv package e.g., $ rpm -qlv unix2dos -rwxr-xr-x 1 root drwxr-xr-x 2 root -rw-r--r-1 root -r--r--r-1 root root root root root 15589 0 1199 1105 Jun 24 08:57 /usr/bin/unix2dos Jun 24 08:57 /usr/share/doc/unix2dos-2.2 Oct 9 1996 /usr/share/doc/unix2dos-2.2/COPYRIGHT Jun 24 08:57 /usr/share/man/man1/unix2dos.1.gz • You can also verify that a package is correctly installed: $ rpm -V package Please see my chapter on documentation in the workshop notes. Also, see the chapters on rpm in the Red Hat Reference Guide. Perhaps most useful is chapter 5 of the book Maximum RPM, particularly the subsection called –queryformat — Construct a Custom Query Response on the web page http://ictlab.tyict. vtc.edu.hk/doc/maximum-rpm-1.0/html/s1-rpm-query-parts.html, also available from http://www.rpm.org/max-rpm/s1-rpm-query-parts.html. Note that this is part of the rpm package maximum-rpm-1.0-0.20020905.noarch.rpm on the documentation cdrom for Red hat 8.0, also downloadable from http:// ictlab.tyict.vtc.edu.hk/ftp/redhat-8.0/doc/RedHat/RPMS/index-en.html. Assignment Requirements If these requirements are not clear, please come and ask for clarification. I will then update this document with the clarification. Note the version number at the bottom right of each page. 1. Write a shell script that will display all the times that the computer was booted, as recorded in all the files /var/log/messages*. A Poss. Soln: #! /bin/sh # Question 1, shell programming assignment, 2003. fgrep −h 'klogd startup succeeded' /var /log /messages∗ | cut −c −15 2. Write a shell script that will show the number of times that the user root has logged in each day of at least the last four weeks. Do not include days that the root user did not log in at all. Note that the last command is useful here. Do man last for more information. Note that the /var/log/wtmp log is rotated every month so that there is at least four weeks of data available before the old log is deleted. At the start of every month, the following happens automatically: • /var/log/wtmp.1 is deleted Nick Urbanik ver. 1.3 Assignment 1: Shell Programming — Operating Systems and Systems Integration Solutions 4 • /var/log/wtmp is renamed to /var/log/wtmp.1 • A new, empty /var/log/wtmp log file is created. The behaviour is controlled by the logrotate software package, and in particular, by the following extract from /etc/logrotate.conf: # no packages own wtmp -- we’ll rotate them here /var/log/wtmp { monthly create 0664 root utmp rotate 1 } Read the manual page: man logrotate A Poss. Soln: #! /bin/sh # Question 2, shell programming assignment, 2003. user =root [ $# -eq 1 ] && user=$1 for wtmp in /var /log /wtmp∗;do last −R −f $wtmp $user done \ | egrep −v '^\s*$|^wtmp.* begins' \ | awk '{$1="";$2="";print}' \ | cut −b 3−12 \ | uniq −c 3. Write a shell script that will show all failed attempts to log in as root for each day of at least the last four weeks. Provide options to sort by date, or by the number of failed attempts. I suggest that you investigate the man page for the lastb command. You will need to create a file /var/log/btmp and process output from the command lastb. A Poss. Soln: #! /bin/sh # Question 3, shell programming assignment, 2003. # method using lastb: prog =‘basename $0‘ usage() { echo "Usage: $prog [-f] [-d] [-u user]" echo "Show bad login attempts." echo −− "-f sort output by days of most frequent bad attempts first" echo −− "-d sort output by date (default)" echo −− "-u user get output for user instead of root" exit 1 } USER=root SORTBY =date while getopts ":fdu:" opt Nick Urbanik ver. 1.3 Assignment 1: Shell Programming — Operating Systems and Systems Integration do case $opt in f ) SORTBY =frequency ;; d) SORTBY =date ;; u) USER=$OPTARG ;; ∗) usage ;; esac done shift $((OPTIND − 1)) Solutions 5 TMPFILE =‘mktemp /tmp/${prog }.XXXXXX ‘ | | exit 1 trap "rm $TMPFILE" HUP INT QUIT PIPE ALRM TERM EXIT lastb $USER > "$TMPFILE" if [ "$SORTBY" = "date" ] then cat "$TMPFILE" exit 0 elif [ "$SORTBY" = "frequency" ] then lastb −R $USER \ | egrep −v '^\s*$|^btmp begins' \ | awk '{$1="";$2="";print}' \ | cut −b 3−12 \ | uniq −c \ | sort −bnr \ | awk '{$1="";printf "%s %s %2d\n", $2, $3, $4}' \ | while read date do fgrep "$date" "$TMPFILE" done fi 4. Write a shell script that will list the size of each directory given on the command line, sorted by size. The size includes disk space used by the directory and all the files and subdirectories inside it. The script should take options to sort with smallest first, and with largest first. A Poss. Soln: #! /bin/sh # Question 4, shell programming assignment, 2003. prog =‘basename $0‘ usage() { echo "Usage: $prog [-s] [-l] dir. . ." echo "print disk usage of each directory" echo −− "-s smallest first" echo −− "-l largest first" exit 1 } SORTBY =largest first while getopts ":sl" opt do case $opt in s) SORTBY =smallest first ;; l) SORTBY =largest first ;; Nick Urbanik ver. 1.3 Assignment 1: Shell Programming — Operating Systems and Systems Integration ∗) usage esac done shift $((OPTIND − 1)) ;; Solutions 6 SORTOPT =−r if [ "$SORTBY" = "smallest_first" ]; then SORTOPT = fi du −s "$@" | sort −n ${SORTOPT } 5. Write a shell script that can search for all files below any given directory, and provide a nice report on all ordinary files that have the suid bit set, and also on files that have the sgid bit set. Refer to section 1.2. A Poss. Soln: #! /bin/sh # Question 5, shell programming assignment, 2003. prog =$(basename $0) usage() { echo "Usage: $prog" exit 1 } UG TMPFILE =$(mktemp /tmp/${prog }.XXXXXX ) | | exit 1 U TMPFILE =$(mktemp /tmp/${prog }.XXXXXX ) | | exit 1 G TMPFILE =$(mktemp /tmp/${prog }.XXXXXX ) | | exit 1 trap "rm $UG_TMPFILE $U_TMPFILE $G_TMPFILE" HUP INT QUIT PIPE ALRM TERM EXIT find $PWD −type f \( −perm −6000 −fprintf ${UG TMPFILE } "%m\t%u\t%g\t%p\n" −o \ −perm −4000 −fprintf ${U TMPFILE } "%m\t%u\t%g\t%p\n" −o \ −perm −2000 −fprintf ${G TMPFILE } "%m\t%u\t%g\t%p\n" \) UG LINES=$(cat ${UG TMPFILE } | wc −l) U LINES=$(cat ${U TMPFILE } | wc −l) G LINES=$(cat ${G TMPFILE } | wc −l) if [ "$UG_LINES" −gt 0 ] then echo "Ordinary files that are BOTH SUID and SGID:" cat "$UG_TMPFILE" fi if [ "$U_LINES" −gt 0 ] then [ "$UG_LINES" −gt 0 ] && echo echo "Ordinary files that are SUID only:" cat "$U_TMPFILE" fi if [ "$G_LINES" −gt 0 ] then [ "$U_LINES" −gt 0 −o "$UG_LINES" −gt 0 ] && echo echo "Ordinary files that are SGID only:" cat "$G_TMPFILE" fi Nick Urbanik ver. 1.3 Assignment 1: Shell Programming — Operating Systems and Systems Integration Solutions 7 6. Extend the program from question 5 so that the script can look up each file in the rpm package manager database and determine if the original state of the file had the suid (or sgid) permission set. Refer to section 1.3. A Poss. Soln: #! /bin/sh # Question 6, shell programming assignment, 2003. prog =$(basename $0) usage() { echo "Usage: $prog [-v] [-d directory]" echo −− "-v verbose mode (show all SUID, SGID files)" echo "Default: only show SUID, SGID files where permission or ownership" echo "does not match original installed state." echo −− "-d directory: examine files below directory instead of current dir." exit 1 } unset START DIRECTORY VERBOSE =0 while getopts ":vd:" opt do case $opt in v ) VERBOSE =1 ;; d) START DIRECTORY =$OPTARG ;; ∗) usage ;; esac done UG TMPFILE =$(mktemp /tmp/${prog }.XXXXXX ) | | exit 1 U TMPFILE =$(mktemp /tmp/${prog }.XXXXXX ) | | exit 1 G TMPFILE =$(mktemp /tmp/${prog }.XXXXXX ) | | exit 1 RPM TMPFILE =$(mktemp /tmp/${prog }.XXXXXX ) | | exit 1 trap "rm $UG_TMPFILE $U_TMPFILE $G_TMPFILE $RPM_TMPFILE" \ HUP INT QUIT PIPE ALRM TERM EXIT if [ "$START_DIRECTORY" ]; then cd "$START_DIRECTORY" > /dev /null 2>&1 \ | | { echo "Cannot cd to $START_DIRECTORY"; exit 1; } fi find $PWD −path '/proc' −prune −o \ −type f \( −perm −6000 −fprintf ${UG TMPFILE } "%m\t%u\t%g\t%p\n" −o \ −perm −4000 −fprintf ${U TMPFILE } "%m\t%u\t%g\t%p\n" −o \ −perm −2000 −fprintf ${G TMPFILE } "%m\t%u\t%g\t%p\n" \) for file in $(cat $UG TMPFILE $U TMPFILE $G TMPFILE | cut −f 4−) do if ! ORIG STATE =$(fgrep $fi e ${RPM TMPFILE }) then rpm −q −−qf \ '[%6o{FILEMODES}\t%{FILEUSERNAME}\t%{FILEGROUPNAME}\t%{FILENAMES}\n]' \ −f $fi e \ | cut −c 3−6,8− > ${RPM TMPFILE } > if ! ORIG STATE =$(fgrep $fi e ${RPM TMPFILE }) then # This file is not owned by any RPM package. # More important to show it!!!!!! Nick Urbanik ver. 1.3 Assignment 1: Shell Programming — Operating Systems and Systems Integration NOT RPM=1 else NOT RPM=0 fi Solutions 8 fi CURRENT STATE =$(cat $UG TMPFILE $U TMPFILE $G TMPFILE | grep "${file}$") set −− $CURRENT STATE mode=$1 user =$2 group=$3 if [ "$NOT_RPM" = "1" ]; then printf "NOT RPM: %-10s %-10s %-14s %s\n" \ $mode $user $group $fi e continue fi set −− $ORIG STATE orig mode=$1 orig user =$2 orig group=$3 if [ "$mode" != "$orig_mode" −o \ "$user" != "$orig_user" −o \ "$group" != "$orig_group" ] then if [ "$mode" != "$orig_mode" ]; then modeinfo="$orig_mode=>$mode" else modeinfo="$mode" fi if [ "$user" != "$orig_user" ]; then userinfo="$orig_user=>$user" else userinfo="$user" fi if [ "$group" != "$orig_group" ]; then groupinfo="$orig_group=>$group" else groupinfo="$group" fi printf "WARNING: %-10s %-10s %-14s %s\n" \ $modeinfo $userinfo $groupinfo $fi e elif [ "$VERBOSE" = "1" ]; then printf "okay: %-10s %-10s %-14s %s\n" \ $mode $user $group $fi e fi done Nick Urbanik ver. 1.3