###
###  Copyright 2000-2007 University of Illinois Board of Trustees
###  All rights reserved. 
###
###  PSGConf::Action::MkNod - Make a device node action type for psgconf
###
###  Campus Information Technologies and Educational Services
###  University of Illinois at Urbana-Champaign
###


package PSGConf::Action::MkNod;

use strict;

use File::Basename;
use File::Path;
use File::stat;
use Unix::Mknod qw(major minor makedev mknod);
use Fcntl ':mode';

use PSGConf::Action::MkDir;

our @ISA = qw(PSGConf::Action::MkDir);


###############################################################################
###  check method
###############################################################################

sub check
{
	my ($self, $psgconf) = @_;

	$self->{changed} = 1
		if (! -b $self->{name} && $self->{type} eq 'b');

	$self->{changed} = 1
		if (! -c $self->{name} && $self->{type} eq 'c');

     ### check for major/minor differences
     $self->{stat} = stat($self->{name});
	return $self->{changed}
		if (!defined $self->{stat});

	$self->{changed} = 1
     	if (major($self->{stat}->rdev) != $self->{major} ||
     		minor($self->{stat}->rdev) != $self->{minor});

	### check for permission differences
	$self->PSGConf::Action::ChMod::check($psgconf);

	### check for ownership differences
	$self->PSGConf::Action::ChOwn::check($psgconf);

	### check for group differences
	$self->PSGConf::Action::ChGrp::check($psgconf);

	return $self->{changed};
}


###############################################################################
###  diff method
###############################################################################

sub diff
{
	my ($self, $psgconf) = @_;
	my ($mknod);

	print "###############################################################################\n";
	print "### DIFF FOR $self->{name}\n";
	print "###          (original file does not exist)\n"
		if (! -e $self->{name});
	print "###############################################################################\n";
	if (-d $self->{name})
	{
		print "- directory: $self->{name}\n";
		$mknod=1;
	}
	elsif (-l $self->{name})
	{
		print "- symlink: $self->{name}\n";
		$mknod=1;
	}
	elsif (-f $self->{name})
	{
		print "- file: $self->{name}\n";
		$mknod=1;
	}
	elsif (( -b $self->{name} || -c $self->{name} )  &&
		((! -b $self->{name} && $self->{type} eq 'b') ||
		(! -c $self->{name} && $self->{type} eq 'c')))
	{
		print "- wrong node type: $self->{name}\n";
		$mknod=1;
	}
	elsif ( ! -e $self->{name} ) {
		$mknod=1;
	}

     if (defined($self->{stat}))
     {
          if (major($self->{stat}->rdev) != $self->{major} ||
          	minor($self->{stat}->rdev) != $self->{minor})
          {
               printf "- wrong major/minor numbers: %04o -> %04o, %04o -> %04o\n",
                      major($self->{stat}->rdev),
                      $self->{major},
                      minor($self->{stat}->rdev),
                      $self->{minor};
			$mknod=1;
		}
     }

	print "+ mknod: $self->{name} $self->{type} $self->{major} $self->{minor}\n"
		if ($mknod);
     print "\n";

	### check for permission differences
	$self->PSGConf::Action::ChMod::diff($psgconf);

	### check for ownership differences
	$self->PSGConf::Action::ChOwn::diff($psgconf);

	### check for group differences
	$self->PSGConf::Action::ChGrp::diff($psgconf);
}


###############################################################################
###  do() method
###############################################################################

sub do
{
	my ($self, $psgconf) = @_;
	my ($type);

	### nuke pre-existing file if applicable
	if ( -e $self->{name} &&
		(-f $self->{name}
		|| -l $self->{name} 
		|| ( ! -b $self->{name} && $self->{type} eq 'b')
		|| ( ! -c $self->{name} && $self->{type} eq 'c')
          || (major($self->{stat}->rdev) != $self->{major})
		|| (minor($self->{stat}->rdev) != $self->{minor})))
	{
		if (!$self->{backup})
		{
			rmtree($self->{name});
		}
		elsif (! $psgconf->save_file($self->{name}, $self->{backup}))
		{
			return -1;
		}
	}

	### mknod
	mkpath(dirname($self->{name}));

	if ( ! -e $self->{name} ) {
		$type = ($self->{type} eq 'b')? S_IFBLK: S_IFCHR;

		if (mknod (
			$self->{name},
			$type|$self->{mode},
			makedev($self->{major}, $self->{minor}))) {

          	warn "\t!!! mknod(\"$self->{name}\", $type|$self->{mode}, makedev($self->{major}, $self->{minor})): $!\n";
			return -1;
		}
	}

	### check for permission differences
	$self->PSGConf::Action::ChMod::do($psgconf);

	### check for ownership differences
	$self->PSGConf::Action::ChOwn::do($psgconf);

	### check for group differences
	$self->PSGConf::Action::ChGrp::do($psgconf);

	return 1;
}


###############################################################################
###  documentation
###############################################################################

1;

__END__

=head1 NAME

PSGConf::Action::MkNod - mknod action class for PSGConf

=head1 SYNOPSIS

  use PSGConf::Action::MkNod;

  $psgconf->register_actions(
		PSGConf::Action::MkNod->new(
			'name'	=> '/path/to/device',
			'type'	=> 'b|c',	# block or character
			'major'	=> number,
			'minor'	=> number,
			'mode'	=> file permissions,
			'uid'	=> User ID,
			'gid'	=> Group ID,
			...
		),
		...
	);

=head1 DESCRIPTION

The B<PSGConf::Action::MkNod> module provides a B<PSGConf> action class
for creating a block or character special file.

The B<PSGConf::Action::MkNod> class is derived from the
B<PSGConf::Action::MkDir> class, but it defines/overrides the
following methods:

=over 4

=item check()

If the file doesn't exist, is not a device, does not have the
correct permissions/major/minor then returns 1.  Otherwise,
returns 0.

=item diff()

Prints a message indicating what changes will be done to the file.

=item do()

Properly creates the special file, optionally backing up the original file.

=back

In addition to the attributes supported by the B<PSGConf::Action>
class, the B<PSGConf::Action::MkNod> class supports the following
attributes:

=over 4

=item I<type>

What type of special file to create, C<b> for block, C<c> for character.

=item I<major>

What the major number is for the special file.

=item I<minor>

What the minor number is for the special file.

=item I<mode>

What the mode (in an octal number) should be for the file.

=item I<uid>

Who should own the file as in the UID.

=item I<gid>

What group should own the file as in the GID.

=item I<backup>

A boolean flag to indicate whether a backup copy of the existing file
will be saved.  If enabled, existing files will be saved under their
original name but with the prefix ".<backupext>".  Default is no
backup.

=back

=head1 SEE ALSO

L<perl>

L<File::stat>

L<Fcntl>

L<Unix::Mknod>

L<PSGConf>

L<PSGConf::Action::MkDir>

=cut



syntax highlighted by Code2HTML, v. 0.9.1