huge code refactoring

- split the main procedure in many procedures
- catch most function call return value
- add new command parameters
- add a verbose mode
This commit is contained in:
Solene Rapenne 2022-03-14 18:00:49 +01:00
parent 2242e428ad
commit 83009b96c9
1 changed files with 174 additions and 76 deletions

View File

@ -11,6 +11,29 @@ use File::HomeDir;
use File::Path qw(make_path);
use 5.010;
my $verbose = 0;
my ($start, $stop) = (0, 0);
# display usage and quit
sub usage {
say "usage: $0 [-v] -d directory -u user (start|stop|restart)";
say " $0 -t conf";
exit 1;
}
# display a message only if verbose flag is used
sub saydebug {
my $msg = shift;
say STDERR "DEBUG: $msg" if $verbose;
}
# display a message before exiting with status 2
sub trap_error {
my $msg = shift;
say STDERR "FATAL: $msg";
exit 2;
}
# return a perl structure by reader a YAML file
sub read_yml_file_to_struct {
my $file = shift;
@ -21,7 +44,6 @@ sub read_yml_file_to_struct {
return Load($data);
}
# some files or directories may be included in a listed directory
# remove those from the list and print a warning
sub remove_transclusion {
@ -31,12 +53,12 @@ sub remove_transclusion {
for(my $i=0; $i <= $#directories; $i++) {
my $dir = $directories[$i];
next if ($dir eq "");
next if ( $dir eq "") ;
# check if a file is contained by a directory
foreach my $file (@files) {
my($filename, $dirs, $suffix) = fileparse($file);
if($dirs =~ m/^$dir/) {
if( $dirs =~ m/^$dir/ ) {
say STDERR "WARNING: $dir contains $file";
$file = "";
}
@ -47,10 +69,10 @@ sub remove_transclusion {
# we can't remove directly from the array because we
# are iterating in it in the main loop
for(my $j=0; $j <= $#directories; $j++) {
if($j != $i) {
if( $j != $i ) {
my $dir2 = $directories[$j];
next if ($dir2 eq "");
if($dir =~ m/$dir2/) {
next if ( $dir2 eq "" );
if( $dir =~ m/$dir2/ ) {
say STDERR "WARNING: $dir2 contains $dir";
$directories[$i] = "";
}
@ -67,85 +89,161 @@ sub remove_transclusion {
return $data;
}
my %opts;
my ($impermanence_home, $persist_home, $configuration_file, $data, $ret);
getopts("t:d:u:", \%opts);
# create all the links in the ramdisk
sub populate_ramdisk {
my ($data, $persist_home, $impermanence_home, $user) = @_;
saydebug("create the symlinks for files set");
create_links(@{$data->{"files"}}, $persist_home, $impermanence_home, $user);
# check if using test mode to validate a configuration file
if(defined $opts{t}) {
say STDERR "WARNING: test mode enabled";
$configuration_file = $opts{t};
} else {
saydebug("create the symlinks for directories set");
create_links(@{$data->{"directories"}}, $persist_home, $impermanence_home, $user);
}
# -d and -u flags are mandatory
if(! defined $opts{d} || ! defined $opts{u}) {
say STDERR "usage: $0 [-t] -d directory -u user";
exit 1;
# check if the mountpoint is already mounted with mfs
sub is_mounted {
my $impermanence_home = shift;
my $ret;
my $mounted = 0;
# is this already mounted?
$ret = `df $impermanence_home | awk -v dest=$impermanence_home '/^mfs/ && \$NF == dest { print }'`;
if ( $ret =~ m/^mfs/ ) {
$mounted = 1;
}
return $mounted;
}
# mount the destination with a ramdisk only if not currently mounted
sub mount_mfs {
my ($user, $impermanence_home, $data) = @_;
my $ret;
saydebug("finding a ffs mountpoint to use for mfs");
if( is_mounted($impermanence_home) ) {
trap_error("ERROR: $impermanence_home is already mounted with MFS");
}
my $fs = `swapctl | tail -n 1 | cut -d ' ' -f 1 | tr -d '\n'`;
saydebug "mount the destination using mount_mfs from $fs";
$ret = system("mount_mfs", "-s", $data->{size}, $fs, $impermanence_home);
if( $ret != 0 ) {
trap_error("ERROR: mounting the mfs filesystem errored with error $ret");
} else {
saydebug("mount_mfs done on $impermanence_home");
}
}
# create the symbolic links listed in the yml file into the ramdisk destination
sub create_links {
my ($list, $persist_home, $impermanence_home, $user) = @_;
foreach ($list) {
my $old_file = $persist_home."/".$user."/".$_;
my $new_file = $impermanence_home."/".$_;
my ($filename, $dirs, $suffix) = fileparse($new_file);
# recursively create missing directories to hold files
# give ownership to the user and apply chmod 700
if ( ! -e $dirs ) {
make_path($dirs, { chmod => 0700, owner => $user });
}
if( symlink($old_file, $new_file) == 0 ) {
trap_error("symlink $old_file to $new_file");
} else {
saydebug("ln -s $old_file $new_file");
}
}
}
sub main {
my %opts;
my ($impermanence_home, $persist_home, $configuration_file, $data, $ret);
# define command line parameters
getopts("vt:d:u:", \%opts);
# verbose mode for debug output
if( defined $opts{v} ) {
$verbose = 1;
}
# test if root
if($< != 0) {
say STDERR "$0 must be run as root.";
exit 1;
# check if using test mode to validate a configuration file
if( defined $opts{t} ) {
say STDERR "WARNING: test mode enabled";
$configuration_file = $opts{t};
# non-test mode, mount the ramdisk and populates it
} else {
# -d and -u flags are mandatory
if( ! defined $opts{d} || ! defined $opts{u} ) {
usage();
}
# test if the script is running as root
if( $< != 0 ) {
trap_error("$0 must be run as root.");
}
$impermanence_home = File::HomeDir->users_home($opts{u});
if( $impermanence_home !~ m|^/| ) {
trap_error("The user \$HOME doesn't start with / , its value is $impermanence_home");
}
if( ! -d $impermanence_home ) {
trap_error("The user \$HOME $impermanence_home doesn't exist");
}
$persist_home = $opts{d};
if( $persist_home !~ m|^/| ) {
trap_error("The persistent directory $persist_home must be an absolute path");
}
if( ! -d $persist_home ) {
trap_error("The persistent directory $persist_home doesn't exist");
}
$configuration_file = $persist_home."/".$opts{u}."/impermanence.yml";
}
$impermanence_home = File::HomeDir->users_home($opts{u});
$persist_home = $opts{d};
$configuration_file = $persist_home."/".$opts{u}."/impermanence.yml";
}
if ( ! -f $configuration_file ) {
say STDERR "The file ".$configuration_file." can't be found";
exit 1;
}
$data = read_yml_file_to_struct($configuration_file);
$data = remove_transclusion $data;
if(defined $opts{t}) {
exit 0;
}
# is this already mounted?
$ret = `df $impermanence_home | awk -v dest=$impermanence_home '/^mfs/ && \$NF == dest { print }'`;
if ($ret =~ m/^mfs/) {
say STDERR "ERROR: $impermanence_home is already mounted with MFS";
exit 2;
}
my $fs = `swapctl | tail -n 1 | cut -d ' ' -f 1 | tr -d '\n'`;
$ret = system("mount_mfs", "-s", $data->{size}, $fs, $impermanence_home);
if($ret != 0) {
say STDERR "ERROR: mounting the mfs filesystem errored with error $ret";
exit 2;
}
foreach (@{$data->{files}}) {
my $old_file = $persist_home."/".$opts{u}."/".$_;
my $new_file = $impermanence_home."/".$_;
my ($filename, $dirs, $suffix) = fileparse($new_file);
# recursively create missing directories to hold files
# give ownership to the user and apply chmod 700
if ( ! -e $dirs ) {
make_path($dirs, { chmod => 0700, owner => $opts{u} });
# exit if the configuration file is not available
if( ! -f $configuration_file ) {
trap_error("The file ".$configuration_file." can't be found");
}
symlink($old_file, $new_file);
}
foreach (@{$data->{directories}}) {
my $old_dir = $persist_home."/".$opts{u}."/".$_;
my $new_dir = $impermanence_home."/".$_;
# read file and sanitize content
$data = read_yml_file_to_struct($configuration_file);
$data = remove_transclusion($data);
my ($dir, $dirs, $suffix) = fileparse($new_dir);
say "$old_dir / $new_dir / $dir / $dirs ";
# recursively create missing directories to hold dir symlinks
# give ownership to the user and apply chmod 700
if ( ! -e $dirs ) {
make_path($dirs, { chmod => 0700, owner => $opts{u} });
# display result and stop if in test mode
if( defined $opts{t} ) {
print Dumper $data;
exit 0;
}
symlink($old_dir, $new_dir);
if( $ARGV[0] eq "start" ) {
$start = 1;
}elsif( $ARGV[0] eq "restart" ) {
$stop = 1;
$start = 1;
}elsif( $ARGV[0] eq "stop" ) {
$stop = 1;
}else{
usage();
}
if( $stop && is_mounted($impermanence_home) ) {
my $status = system("umount", $impermanence_home);
if( $status != 0 ) {
trap_error("umount did exit with status $status");
}
}
if( $start ) {
mount_mfs($opts{u}, $impermanence_home, $data);
populate_ramdisk($data, $persist_home, $impermanence_home, $opts{u});
}
}
main()