WILLIAM L MARTIN
CSC 485 RESEARCH PAPER
INSTRUCTOR: DR. JAN PLAZA

 

PERL PROGRAMMING FOR SYSTEM ADMINISTRATION

INCLUDING A PROJECT TO WRITE PERL SCRIPTS, WHICH CONVERT THE GROUP DEFINITIONS TO THE LINUX DEFAULT.

Index:

  1. System Administration
    1. Why Should We Use Perl?
    2. Perl Is Not The Perfect Language
    3. More On Modules
      1. Installing Modules
  2. Research Project
    1. Procedure
    2. User Account Files
    3. Password File - /Etc/Passwd
    4. Group File - /Etc/Group
  3. Implimenting The Project
    1. The Perl Script Overview
    2. Changing The Password File
    3. Changing The Group File
    4. Changing The User's File Permissions
  4. The Finished Product
  5. Conclusion And Future Considerations
  6. Appendix
    1. Code
    2. References / Sources


SYSTEM ADMINISTRATION

System administration in a multi-platform environment is a complex and demanding task. This paper will address some issues a system administrator will face. We will construct administrative tools using Perl programming language.

 

WHY SHOULD WE USE PERL?

David N. Blank in his book, Perl for System Administration says, "System Administration is often a glue job; Perl is one of the best glue languages". I believe that Blank is implying that without system administration the computer networks would not hold together. To keep our systems together we need a language that will pull all the platforms together and keep them together.

Since Perl is an offspring of Unix Shells it easily adapts to Unix systems. Perl is equipped with system calls known from the C language. Perl is also is available on all platforms, which makes it useful for multi-platform networks. In addition to its flexibility Perl is also portable. Its portability is due to the extensive use of modules. With modules Perl's core language can be easily extended . Large collections of modules, for every administrative task have been implemented by a committed group of Perl developers.

 

PERL IS NOT THE PERFECT LANGUAGE

Although Perl is a good tool for most system administration tasks it has its drawbacks. Perl isn't the best of all the object-oriented languages (not needed in system administration anyway). Perl is not always simple to understand and can be written in many different ways to do the same task. For the inexperienced programmer, system administration can be dangerous because of Perl's ability to manipulate system files and structures.

 

MORE ON MODULES

NO NEED TO REINVENT CODE. If Perl is to be effectively used as a tool for system administration it is necessary for the administrator to use modules. It is not efficient to reinvent code that someone else has invented. Perl modules enable an administrator to find the necessary tools and use them. Much of the benefit of Perl comes from the freely available collections of modules.

The best source for downloading Perl Modules is CPAN (Comprehensive Perl Archive Network). http://www.cpan.org.

Another way to find and install modules (in Windows) is with an application called PPM (Perl Package Manager). This tool uses the web to locate, download, and install modules with virtually no effort. This tool is free and can be obtained from the ActiveState web site. http://www.activestate.com/Products/ActivePerl//docs/faq/ActivePerl-faq2.html.

There are also resources that come with the Perl documentation which is included with a normal Perl installation. One such file is called perlmodinstall.pod and can be accessed by typing perldoc perlmodinstall at the command line.

 

INSTALLING MODULES

 

RESEARCH PROJECT

Andrew Heuneman is the computer science lab administrator at Plattsburgh State University. He states that the default groups that are created for system users are done inefficiently and needs to be upgraded. Andrew states, "In Linux, the default is that every user has a default group of the same name as the user. On nexus6 that default was not used-most users got "users" as their default group but even that was not done consistently". As an example of how Perl can work for System Administration we will develop a possible solution.

PROCEDURE

In order to change to this new scheme a couple of files must be changed and a couple of things must be done to the files in the users home directory.

LINE IN CURRENT /etc/group:
users:x:100: 

 

/etc/group AFTER LINES ADDED:
...
mart1885:x:25000:
marc1234:x:25001:
suef2345:x:25002:
fred3456:x:25003:
Dorl4567:x:25004:
... etc. 

 

Some Lines in current /etc/passwd file:
root2:x:0:0::/usr/local/custom2/home:/bin/tcsh
csc221class:x:5461:6001::/usr/users/csc221class:/bin/tcsh
plazaja:x:294:26:Jan Plaza:/usr/users/plazaja:/bin/tcsh
mart1885:x:5130:100:William Martin:/usr/users/mart1885:/bin/tcsh
mart6844:x:755:100:Ayme Martinez:/usr/users/mart6844:/bin/bash
matu8063:x:831:100:Melissa Matusiak:/usr/users/matu8063:/bin/bash
mccl1070:x:621:100:Jacob McClenahan:/usr/users/mccl1070:/bin/tcsh
mcfa9172:x:617:100:Francis McFadyen:/usr/users/mcfa9172:/bin/bash 

 

Desired etc/passwd format:
root2:x:0:0::/usr/local/custom2/home:/bin/tcsh
csc221class:x:1001:1001::/usr/users/csc221class:/bin/tcsh
plazaja:x:594:594:Jan Plaza:/usr/users/plazaja:/bin/tcsh
mart1885:x:15000:15000:William:/usr/users/mart1885:/bin/tcsh
marc1234:x:15001:15001:Marcy:/usr/users/marc1234:/bin/tcsh
suef2345:x:15002:15002:Suzy:/usr/users/suef2345:/bin/tcsh
fred3456:x:15003:15003:Fred:/usr/users/fred3456:/bin/tcsh
Dorl4567:x:15004:15004:Dory:/usr/users/Dorl4567:/bin/tcsh 

 

Current users directory (typical example)
-rw-------    1 mart1885 users         186 Feb 12 19:24 file.txt
-rwxr-xr-x    1 mart1885 users         293 Feb 12 19:24 file2.doc
-rwxr-xr-x    1 mart1885 project1      888 Feb 12 19:24 file3.csv
-rw-------    1 mart1885 users        1590 Sep  1  1999 file4.c
drwxr-xr-x   10 mart1885 users        4096 Jan 22  1999 file5.cp
drwx------    4 mart1885 users        4096 Oct 25 12:04 file6.pl
-rw-r--r--    1 mart1885 users         496 Feb 12 19:24 file7.ada
-rw-------    1 mart1885 users       12053 Sep  1  1999 file8.s
drwx------    3 mart1885 users        4096 Dec 15 14:55 hello.c

 

 

Proposed users directory (typical example)
-rw-------    1 mart1885 mart1885         186 Feb 12 19:24 file.txt
-rwxr-xr-x    1 mart1885 mart1885         293 Feb 12 19:24 file2.doc
-rwxr-xr-x    1 mart1885 project1         888 Feb 12 19:24 file3.csv
-rw-------    1 mart1885 mart1885        1590 Sep  1  1999 file4.c
drwxr-xr-x   10 mart1885 mart1885        4096 Jan 22  1999 file5.cp
drwx------    4 mart1885 mart1885        4096 Oct 25 12:04 file6.pl
-rw-r--r--    1 mart1885 mart1885         496 Feb 12 19:24 file7.ada
-rw-------    1 mart1885 mart1885       12053 Sep  1  1999 file8.s
drwx------    3 mart1885 mart1885        4096 Dec 15 14:55 hello.c

 


 

USER ACCOUNT FILES

In order to address the project we first need to understand user account files. Our project will involve Linux user identity so NT/windows will not be covered. Two files will be involved in this project, /etc/passwd and etc/group since they contain the information that the system needs for user identification. We will leave out etc/shadow in this example because the file that we use in our endeavor will contain only the user id name, not the number.

 

PASSWORD FILE - /etc/passwd

The ASCII text file /etc/passwd is consistent across all the Unix based systems. By this I mean that the Linux format of the password file is the same as a SunOS format. Each line is colon separated and contains the following (IN ORDER FROM LEFT TO RIGHT):

Example of etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:
daemon:x:2:2:daemon:/sbin:
adm:x:3:4:adm:/var/adm:
lp:x:4:7:lp:/var/spool/lpd:
mail:x:8:12:mail:/var/spool/mail:
news:x:9:13:news:/var/spool/news:
operator:x:11:0:operator:/root:
games:x:12:100:games:/usr/games:
gopher:x:13:30:gopher:/usr/lib/gopher-data:
ftp:x:14:50:FTP User:/var/ftp:
nobody:x:99:99:Nobody:/:
xfs:x:43:43:X Font Server:/etc/X11/fs:/bin/false
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/bin/false
rpc:x:32:32:Portmapper RPC user:/:/bin/false
mailnull:x:47:47::/var/spool/mqueue:/dev/null
mart1885:x:500:500:William:/home/mart1885:/bin/bash
marc1234:x:501:501:Marcy:/home/marc1234:/bin/bash
suef2345:x:502:502:Suzy:/home/suef2345:/bin/bash
fred3456:x:503:503:Fred:/home/fred3456:/bin/bash
Dorl4567:x:504:504:Dory:/home/Dorl4567:/bin/bash 

 

 

If you would like to see your own line in the passwd file type $ grep yourloginname /etc/passwd

 

 

GROUP FILE - /etc/group

Group ids (GID), group names, and group members are kept tin the /etc/group file. When an account becomes a part of several groups it is added to numerous lines in the file. Each line is colon separated and contains the following (IN ORDER FROM LEFT TO RIGHT):

EXAMPLE OF /etc/group
root:x:0:root
bin:x:1:root,bin,daemon
daemon:x:2:root,bin,daemon
sys:x:3:root,bin,adm
adm:x:4:root,adm,daemon
lp:x:7:daemon,LP
rpcuser:x:29:
rpc:x:32:
slocate:x:21:
mart1885:x:500:
marc1234:x:501:
suef2345:x:502:
fred3456:x:503:
Dorl4567:x:504: 

 

 


 

 

 

IMPLEMENTING THE PROJECT

Now we are ready to use our knowledge of user account files and design some applications to migrate our user accounts to the new structure. This section will explain the methods of implementation and the results.

THE PERL SCRIPT OVERVIEW

The design of the proposed Perl Script was done in concideration to the requests of Dr. Plaza. He suggested taking a user one at a time and change the files necessary for that user only. In addition we will start the new uid's at 10000 because there are currently no uid's at that range.

The script will look into the /etc/passwd file and test each line for user type. Since the majority of the users are students we will be searching for user-name that begin with 2 to 4 characters and end with 4 digits. Once found, that users uid and group id will be changed . The name, user's old id, and home directory will be noted. The file will be opened read and copied into a temp file called passwd.tmp. Once we have located a student and changed their line to include the new uid and group id, we will write passwd.tmp back into the original passwd file. Just to play it safe we will have the script back up the original files before we do any file manipulation.

#BACKUP FILES
system ("cp /etc/passwd /etc/passwd.bkp");
print "\n     Backed up passwd file to passwd.bkp\n";
system ("cp /etc/group /etc/group.bkp");
print  "     Backed up group file to passwd.bkp\n\n";

 

Here 'system("system call");' is how one would make system calls from Perl. The Perl interpreter forks a process which is the system call, then returns control back to the program after it has completed.

Once the passwd file has been changed then the new group name and id will be appended to the etc/group file. As explained in earlier sections the names and numbers will reflect the user-name and uid.

The hardest part takes place after the group file has been changed. We must recursively go into the users home directory and change the files groups. The files whose group ownership is not 100 or users will not be touched. It is also very necessary to not follow the symbolic links. We will use Perl and Linux commands that only look at files properties and not what they point to.

It is thought and suggested that the etc/shadow file must also be used but after talking to the labs administrator I have decided to leave it alone. On some systems the shadow file contains the uid number followed by the encrypted password and other stuff. In our instance the shadow file only contains the uid name and doesn't need to be changed so we will leave that file alone.

CHANGING THE PASSWORD FILE

Of the three processes in our example project, changing the password file is the most interesting. Here we can see the beauty of Perl as it searches with ease then makes our changes without a fuss. Since we want our code to have three procedures, we construct a procedure named change_passwd. In Perl procedures are called subroutines and arguments are passed to it in an array @_. In this example we constructed a subroutine that takes two arguments $id and $min_uid. With $id we can specify the id number that we will replace the old students id with. The $min_uid will be a search argument (he minimum uid that we will search for).

The plan for this subroutine will be:

  1. open file.
  2. create temp file.
  3. copy each line into temp file until we find the line we want to change.
  4. change line and add it to temp file.
  5. finish copying file into temp file then close files.
  6. overite file with temp file.
  7. destroy temp file and return arguments.

When we open files in Perl we use the > symbol to signify write and >> to signify append, otherwise they are opened for reading. Once open we search for the first student that has uid below 10000 (useful after we have already changed the file in a previous loop). We will search for the student pattern with the line:

m/^[a-z]{2,4}[0-9]{4}/

 

Here is where Perl is good. This line says search for instances that start with 2 to 4 characters (a to z) and that end with 4 digits (0-9). Another good Perl tool used in this example is the split call @line = split (/:/,$_);. What this does is split the line into the array @line with the delimiters : . This way we are able to pick out and change specific fields in each line of the passwd file. Here is our subroutine:

sub change_passwd {
   my ($id, $min_uid) = @_;

   my $file_name     = "/etc/passwd";  #Password file
   my $temp_file     = "passwd.tmp";   #temp file
   my $flag          = 0;              #did we find someone?


   #OPEN FILES
   open(FILE, $file_name) or die "Error opening $file_name: $!\n";
   open(NEW_FILE, ">$temp_file") or
	die "Error opening $temp_file: $!\n";

   #COPY FILE AND CHANGE THE NEXT INSTANCE OF STUDENT WITH ID < $id
   while (<FILE>){
        @line = split (/:/,$_);
        if (($line[2] < $min_uid) &&
            (m/^[a-z]{2,4}[0-9]{4}/) &&
            ($flag == 0)){  #HAS IT BEEN CHANGED ALREADY AND
                            #DOES IT MATCH THE STUDENT PATTERN AND
                            #HAS ONE STUDENT BEEN CHANGED ALREADY?
		$flag = 1;
		$home_dir = $line[5];# need this to change file ownerships
		$student = $line[0];
		$old_id = $line[2];
	print NEW_FILE "$student:$line[1]:$id:$id:$line[4]:$line[5]:$line[6]";
	   print"Found $line[4] and changed user id $line[2] to $id\n";
		next;
	};
	print NEW_FILE;
   }

   #COPY NEW FILE BACK INTO passwd, DESTROY passwd.bkp, AND CLOSE FILE
   close(FILE); close(NEW_FILE);
   open(FILE, ">$file_name") or die "Error opening $file_name: $!\n";
   open(NEW_FILE, $temp_file) or
	die "Error opening $temp_file: $!\n\n";

   while (<NEW_FILE>) {
      print FILE;
   }

   close(FILE); close(NEW_FILE);
   system("rm passwd.tmp");

   #RETURN INFO
   if ($flag == 1){
      return($home_dir, $student, $old_id);
   }else{
      return (" ", " ", 0);
   }
}

 

Our example subroutine will exit and pass back to the calling routine three arguments $home_dir, $student, and $old_id . We will need the home directory of the student found for our third subroutine explained later. $student will hold the uid name and we will keep track of the students old uid with $old_id. These, we will also need in our other subroutines later.

 

CHANGING THE GROUP FILE

Changing the group file will be the easiest part of the project code. Our game plan will be to open a file and append our new group. Notice that when we opened the group file we used >> to denote appending.

open(FILE, ">>$file_name") or die "Error opening $file_name: $!\n";

 

Here we are asking Perl to open the file for appending with the filehandle FILE, but if it cant, die gracefully by printing an error message. Here is our subroutine that changes the group file using the student uid number and name as arguments (obtained by the subroutine change_passwd):

sub change_group {
   my ($id, $student) = @_;

   my $file_name     = "/etc/group";	#Group file

   #OPEN FILE
   open(FILE, ">>$file_name") or die "Error opening $file_name: $!\n";

   #APPEND NEW GROUP
   print FILE "$student:\:$id:\n";
   print"Added $student to group file with group id: $id\n\n";

   #CLOSE FILE
   close(FILE);
}

 

 

 

CHANGING THE USER'S FILE PERMISSIONS

The subroutine that changes the files' memberships in a students home directory will be the projects hardest task. Because Perl has the ability to do system calls and has file manipulation tools, our task can be done without too much hassel.

First off we needed a routine that would return a files owner and group. Let's make a subroutine called file_stat. Perl has a function called stat(). This function returns a 13-element list about a file and its statistics. To inquire about all files including symbolic links we cannot use stat, but instead we will use lstat(). lstat, when called on a symbolic link, returns info about that link and not the file that it points to. Now we have a routine that we need:

sub file_stat {
        my($file) = @_;
	     my($dev,   $ino,   $mode,  $nlink,
              $uid,   $gid,   $rdev,  $size,
              $atime, $mtime, $ctime, $blksize,
              $blocks) = lstat($file);

	return ($uid, $gid);
}

 

Now that we have that out of the way we will do three things:

  1. Recursively search the home directory tree making sure each file is owned by the student
  2. Search for files with the group membership that we want to change
  3. Change the group membership to the desired one but with symbolic links change only the link and not what it points to

Our subroutine will take four arguments $dir, $id_to_change, $id, and $oid . $dir will be the student home directory which was obtained by the change_passwd subroutine. $id_to_change and $id are the ids to switch when we find the $id_to_change. The $oid argument is used to check to make sure the file is not owned by the student's old uid.

When we find a file that has a group membership that we want to change we will use the system call chgrp:

if ($grp_id == $id_to_change){
   system ("chgrp", "-h", "$id", "$name");
}

 

This is where we must be careful that we do not follow symbolic links. According to the chgrp manpage, the -h argument is used to change the group of a symbolic link itself. It is important to note that this will fail if the system does not provide the lchown system call. If this call fails then the group does not get changed. The above code asks Perl, if this is a group id that we want to change then change this group but not the files that it may point to. We use \'$name\' for the file names that may have spaces in it like Red Hat Erratta.html. If we did not use quotes then the program would try to change the group of the files Red, Hat, and Erratta.html (would produce an error). Note that when we need to use a symbol that Perl might use in the wrong context we use a \, like the newline \n.

Sometimes we need to check and change the group of a directory. Perl comes to the rescue again by enabling us to test a name in the directory to see what it is. Here is how we will test for a directory and to make sure that it is not a link.

if (-d $name && !-l $name){
         ...
}

And so our subroutine is finished:

sub dir_scan{
   my ($dir, $id_to_change, $id, $oid) = @_;
   use Cwd;

   Chadic($dir) or die "Cant change to $dir:$!\n";
   open-air(DIR, ".") or die "Can't open $dir:$!\n";
   my @names = readdir(DIR) or die "Can't read $dir:$!\n";
   closed(DIR);

   foreach my $name (@names){
      next if ($name eq "..");
      ($usr_id, $grp_id) = &file_stat($name);

      if ($usr_id == $oid){
         chown($id, $grp_id, $name);
      }

      if ($name eq "."){
          if ($grp_id == $id_to_change){
             system ("chgrp -h $id \'$name\'");
          }
          next;
      }

      if (-d $name && !-l $name){
         if ($grp_id == $id_to_change){
            system ("chgrp -h $id \'$name\'");;
         }

         my $bookmark = &cwd; #we must mark our spot before recursing
         &dir_scan($name, $id_to_change, $id);
         Chadic($bookmark) or die "Cant change to $bookmark:$!\n";
         next;
      }

      if ($grp_id == $id_to_change){
         system ("chgrp -h $id \'$name\'");
      }
   }
}

 

THE FINISHED PRODUCT

Finally we put all our subroutines into a loop:

while(1){
	#CHANGE PASSWORD FILE
   ($home_directory, $stdnt, $old_id) =
                                     &change_passwd($count, $min_id);

   if ($old_id == 0){ #Will be zero if no more students!
      print"     There are no more students!\n";
      last;
   };

   #CHANGE GROUP FILE
   &change_group($count, $stdnt);

   #CHANGE USERS FILES IN HOME DIRECTORY
   &dir_scan($home_directory, 100, $count, $old_id);

   print "Would you like to continue? ";
   $answer = ; chomp $answer;
   last if($answer ne "y" && $answer NE "Y");
   print"\n\n";
   $count ++;
}

 

Now we have a System Administration tool done using the Perl language and thus our project example is finished. This code was tested on a mock Linux system created to mirror our own labs scheme.

 


 

 

 

CONCLUSION AND FUTURE CONSIDERATIONS

I have shown that Perl is a good choice for System Administration and have demonstrated it by changing the group scheme to a Linux like scheme. We have done this by writing some example code and hopefully you have seen that Perl has roots in Unix shells and easily makes system calls. Also you have seen some of Perl's many tools for doing administrative tasks.

I am a mountain biker at heart and I wouldn't be caught dead without my Allen (wrench) set. With this tool a person can completely take apart a modern mountain bike and put it back together. Perl is to the System Administrator that this tool is to me.

As with any project the ultimate goal is to learn something new and expand our knowledge. I hope that you have benefited as much as I have from this project. In the future Perl could be used in the lab for tools that enable students to create their own groups. Perl is also effective on the windows platform and could be used for user accounts on the Windows 2000 side of our lab.


APPENDIX:

Here is an example of the latest code in its entirety (newscheme.pl):

#/usr/bin/perl

my $count = 10019; #START COUNT HERE
my $min_id = 10000; #CHANGE ALL UIDS LESS THAN THIS
my $old_id = 1;    #INITIALIZE FLAG

#####change_passwd###################################################
# This sub-program opens the etc/passwd file and copies it to
# passwd.tmp in this directory untill it finds the first
# student (2 to 4 letters followed by 4 digits).  When
# encountered the user id is changed to the initial id number.
# This new id is given by the variable $id in which is
# incremented. The passwd.tmp is then used to overwrite the
# /etc/passwd file.
#
# INPUT:        the start id number and the number that all
#               ids < will be changed(parameters)
# OUTPUT:       updated passwd file as well as passwd.bkp
# DEPENDENCIES: none
# DATE:         March 26, 2001
# AUTHOR:       William Martin
#####################################################################
sub change_passwd {
   my ($id, $min_uid) = @_;

   my $file_name     = "/etc/passwd";  #Password file
   my $temp_file     = "passwd.tmp";   #temp file
   my $flag          = 0;              #did we find someone?


   #OPEN FILES
   open(FILE, $file_name) or die "Error opening $file_name: $!\n";
   open(NEW_FILE, ">$temp_file") or
	die "Error opening $temp_file: $!\n";

   #COPY FILE AND CHANGE THE NEXT INSTANCE OF STUDENT WITH ID < $id
   while (){
        @line = split (/:/,$_);
        if (($line[2] < $min_uid) &&
            (m/^[AZ]{2,4}[0-9]{4}/) &&
            ($flag == 0)){  #HAS IT BEEN CHANGED ALREADY AND
                            #DOES IT MATCH THE STUDENT PATTERN AND
                            #HAS ONE STUDENT BEEN CHANGED ALREADY?
		$flag = 1;
		$home_dir = $line[5];# need this to change file ownerships
		$student = $line[0];
		$old_id = $line[2];
	print NEW_FILE "$student:$line[1]:$id:$id:$line[4]:$line[5]:$line[6]";
	   print"Found $line[4] and changed user id $line[2] to $id\n";
		next;
	};
	print NEW_FILE;
   }

   #COPY NEW FILE BACK INTO passwd, DESTROY passwd.bkp, AND CLOSE FILE
   close(FILE); close(NEW_FILE);
   open(FILE, ">$file_name") or die "Error opening $file_name: $!\n";
   open(NEW_FILE, $temp_file) or
	die "Error opening $temp_file: $!\n\n";

   while () {
      print FILE;
   }

   close(FILE); close(NEW_FILE);
   system("rm passwd.tmp");

   #RETURN INFO
   if ($flag == 1){
      return($home_dir, $student, $old_id);
   }else{
      return (" ", " ", 0);
   }
}
#####################################################################
#####change_group####################################################
# This subprogram opens the etc/group file and appends the user
# $student and gives it a group id of $id (which is what we
# changed the user id to in the passwd file.
#
# INPUT:        none
# OUTPUT:       new file called group.new in the calling dir.
# DEPENDENCIES: none
# DATE:         March 26, 2001
# AUTHOR:       William Martin
#####################################################################
sub change_group {
   my ($id, $student) = @_;

   my $file_name     = "/etc/group";	#Group file

   #OPEN FILE
   open(FILE, ">>$file_name") or die "Error opening $file_name: $!\n";

   #APPEND NEW GROUP
   print FILE "$student:\:$id:\n";
   print"Added $student to group file with group id: $id\n\n";

   #CLOSE FILE
   close(FILE);
}
#####################################################################
####file_stat#########################################################
# This subprogram returns a group id from a 13 element list given
# by the lstat function.  lstat returns the stats of files and not
# files pointed to by symbolic links.
#
# INPUT:        file name in this directory
# OUTPUT:       group id of this file
# DEPENDENCIES: be sure that you are currently in the directory
#               that this file is in.
# DATE:         April 2, 2001
# AUTHOR:       William Martin
#####################################################################

sub file_stat {
        my($file) = @_;
	my($dev,   $ino,   $mode,  $nlink,
              $uid,   $gid,   $rdev,  $size,
              $atime, $mtime, $ctime, $blksize,
              $blocks) = lstat($file);

	return ($uid, $gid);
}
#####################################################################
#####dir_scan########################################################
# This subprogram opens the users directory ($dir) and changes all
# the files that belong to the group specified by $id_to_change.  It
# then changes all the files to belong to the group specified by $id.
# This subprogram recurses throu the tree from the users home
# directory obtained from the passwrd file.
#
# INPUT:        users home directory, group id to change,
#               new group id, old uid
# OUTPUT:       All filles in the users home directory are changed
#               to belong to the new group id if they were prevously
#               owned by the $id_to_change group.
# DEPENDENCIES: sub file_stat
# DATE:         March 26, 2001
# AUTHOR:       William Martin
#####################################################################

sub dir_scan{
   my ($dir, $id_to_change, $id, $oid) = @_;
   use Cwd;

   Chadic($dir) or die "Cant change to $dir:$!\n";
   open-air(DIR, ".") or die "Can't open $dir:$!\n";
   my @names = readdir(DIR) or die "Can't read $dir:$!\n";
   closed(DIR);

   foreach my $name (@names){
      next if ($name eq "..");
      ($usr_id, $grp_id) = &file_stat($name);

      if ($usr_id == $oid){
         chown($id, $grp_id, $name);
      }

      if ($name eq "."){
          if ($grp_id == $id_to_change){
             system ("chgrp", "-h", "$id", "$name");
          }
          next;
      }

      if (-d $name && !-l $name){
         if ($grp_id == $id_to_change){
            system ("chgrp", "-h", "$id", "$name");;
         }

         my $bookmark = &cwd; #we must mark our spot before recursing
         &dir_scan($name, $id_to_change, $id);
         Chadic($bookmark) or die "Cant change to $bookmark:$!\n";
         next;
      }

      if ($grp_id == $id_to_change){
         system ("chgrp -h $id \'$name\'");
      }
   }
}

#####################################################################
#MAIN################################################################

#BACKUP FILES
system ("cp /etc/passwd /etc/passwd.bkp");
print "\n     Backed up passwd file to passwd.bkp\n";
system ("cp /etc/group /etc/group.bkp");
print  "     Backed up group file to passwd.bkp\n\n";

while(1){
	#CHANGE PASSWORD FILE
   ($home_directory, $stdnt, $old_id) =
                                     &change_passwd($count, $min_id);

   if ($old_id == 0){ #Will be zero if no more students!
      print"     There are no more students!\n";
      last;
   };

   #CHANGE GROUP FILE
   &change_group($count, $stdnt);

   #CHANGE USERS FILES IN HOME DIRECTORY
   &dir_scan($home_directory, 100, $count, $old_id);

   print "Would you like to continue? ";
   $answer = ; chomp $answer;
   last if($answer NE "y" && $answer NE "Y");
   print"\n\n";
   $count ++;
}

 

 

References / Sources

back to main