Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Filesystem and SMB/NFS share Access

by PerlingAround (Novice)
on Oct 09, 2018 at 13:11 UTC ( #1223731=perlquestion: print w/replies, xml ) Need Help??

PerlingAround has asked for the wisdom of the Perl Monks concerning the following question:

Hey, first and foremost, I'd like to say hi to make sure you know this is my first post here. So please be gentle with me for now

Now, I recently started working with perl and my first big task was to write up a script to test whether or not I could access a certain Filesystem/Folder/Share and create Files, write into them and read from them.

We are using a Monitoring Tool that runs perl scripts on machines and based on their output generates events that tell us whether or not "something" was successful. What I set out to do, was create a Script, that given a path (-f) a type (-t (local, NFS, Samba/SMB(mounted and via UNC))) a Searchpattern (-s) as well as Username and Password were needed, would create the file whichs path I have provided, write the Searchpattern into it, close the handle, open the file for reading and then would be successful if what I read was equal to our pattern. The Script is also supposed to run on, at least, Windows and Linux.

My first successful script worked well on local files. But it was the easiest and pretty much most basic part. My Priority is now to check if I can access a Samba share from a Windows Machine via a UNC path. My prior research was fruitless and led me to the Filesys::smbclient module. But I am confused in how to install/use it. Installing it on my test-system led to it asking about the libsmbclient.so/h, which, some more research later, was revealed to be part of a Samba suite. I am quite new at all this, so I am certainly a bit overwhelmed here.

Since the script is supposed to handle multiple combinations of OS, Path-Formats and Shares I dont know if I can install Filesys::smbclient everwywhere. I dont know if what I want would be achievable without it, and I am quite unsure about how to proceed here. As I said, I am to prioritize accessing a SMB Share via UNC from a Windows Machine, create a file and write into and read from it.

I came here looking to see if any of you would know how to achieve this goal.

Having received my first reply, I was reminded, that I should state some restrictions I am under while creating this script. First, it will have to work with as few additional Modules as possible. Since we are using a perl distribution that is shipped with another software package, it would be hard to make sure that every machine we want to use it on has, or can, install additional modules. Secondly, I was told to try and find a way to do it, without temporarily mounting the share as a drive. But since I am, as I had remarked earlier, quite new at this, I don't quite know, and can't seem to find information on, how to do that.

use Getopt::Std; shift(@ARGV); &getopts("f:t:s:u:p:"); print "ZAXML-TXT\n"; my $Filepfad; my $Volumetype; my $Username; my $Password; my $Searchpattern; print "${opt_f}"; if ($opt_f) { $Filepfad=$opt_f; } else { print "CURRVALUE_STRING: a File-Path must be given\n"; print "RETURNID: 1\n"; exit 0; } if ($opt_t){ if ($opt_t ne "UNC" && $opt_t ne "local" && $opt_t ne "NFS" && $op +t_t ne "SMB") { print "CURRVALUE_STRING: Filesystem-type must be UNC, local, N +FS or SMB\n"; print "RETURNID: 1\n"; exit 0; } elsif ($opt_t eq "SMB") { $Volumetype=$opt_t; if ($opt_u && $opt_p) { $Username=$opt_u; $Password=$opt_s; } else { print "CURRVALUE_STRING: Username and Password are require +d for access to SMB-Shares\n"; print "RETURNID: 1\n"; exit 0; } } else { $Volumetype=$opt_t; } } else { print "CURRVALUE_STRING: Volumetype must be given\n"; print "RETURNID: 1\n"; exit 0; } if ($opt_u){ $Username=$opt_u; } if ($opt_p){ $Password=$opt_p; } if($opt_s) { $Searchpattern = $opt_s; } else { print "CURRVALUE_STRING: a Searchpattern must be given\n"; print "RETURNID: 1\n"; exit 0; } my $os=$^O; my $last_slash; if ($os eq "MSWin32" && $Volumetype eq "local") { $last_slash = rindex($Filepfad, "\\",); } else { $last_slash = rindex($Filepfad, "/",); } my $Test_Volume=substr($Filepfad,0, $last_slash); if (!(-d $Test_Volume)) { print "CURRVALUE_STRING: Volume does not exist, isn't mounted or c +an't be read\n"; print "RETURNID: 2\n"; exit 0; } if (-f $Filepfad) { if ( ! unlink($Filepfad) ) { print "CURRVALUE_STRING: can't delete existing File ${Filepfad +}\n"; print "RETURNID: 2\n"; exit 0; } } my $Filehandle; if(!(open($Filehandle, '>', $Filepfad))) { print "CURRVALUE_STRING: can't create new file ${Filepfad} for wri +te access \n"; print "RETURNID: 2\n"; exit 0; } if(!(print $Filehandle "Test")) { print "CURRVALUE_STRING: Couldn't write into File\n"; print "RETURNID: 2\n"; exit 0; } if(!(close $Filehandle)) { print "CURRVALUE_STRING: can't close file ${Filepfad} after writin +g\n"; print "RETURNID: 2\n"; exit 0; } if(!(open($Filehandle, '<', $Filepfad))) { print "CURRVALUE_STRING: can't create new file ${Filepfad} for rea +d access \n"; print "RETURNID: 2\n"; exit 0; } while ($Content = <$Filehandle>) { chomp $Content; if ($Content eq $Searchpattern) { print "CURRVALUE_STRING: Files can be written and read\n"; print "RETURNID: 3\n"; exit 0; } else { print "CURRVALUE_STRING: File can be written but not r +ead or file-content does not match Searchpattern\n"; print "RETURNID: 4\n"; exit 0; } #else }#while

Whenever I print something, it's something needed by our monitoring tool to extract whether or not my attempts where successful. Some things will clearly have to change, but this is the barebones script I came up with that works perfectly well on local directories.

Replies are listed 'Best First'.
Re: Filesystem and SMB/NFS share Access
by haukex (Chancellor) on Oct 09, 2018 at 20:31 UTC

    First of all, a couple of general recommendations to improve the code:

    • Always Use strict and warnings. Although your current program only needs a few fixups, those pragmas will help you avoid typos and other mistakes.

    • Use consistent indentation. perltidy can help.

    • Reduce repetitions. I would have written something like this:

      sub exit_with_msg { my ($message,$returnid) = @_; print "CURRVALUE_STRING: $message\n"; print "RETURNID: $returnid\n"; exit 0; }

      And then used it everywhere like e.g. exit_with_msg("a File-Path must be given",1);. This will shorten your code significantly.

    • In the same vein, you seem to be assigning to $Username and $Password twice, and I'm wondering if this is correct: $Password = $opt_s;

    Now, moving on to your task, note that the core Perl module File::Spec is good for cross-platform handling of filenames. For example, you can use its splitpath method to find out the volume of a path, instead of manually splitting like you are currently doing.

    As for your main question, unfortunately I'm a bit rusty on my Perl+Windows+UNC. So it's a little unclear to me how you want to go about testing the files on network shares: do you want to map the shares to network drives? A quick search found several threads with information:

    For example, most of these threads mention Win32::FileOp as a possibility. It's also possible to shell out to run commands like NET USE, but running external commands is usually a tricky topic, which I wrote about at length here (in this case for example, I might recommend the latest version of IPC::Run3 because it should provide for the "best" quoting of command arguments).

      Thanks for the reply, a few things to address here.

      I wasn't my, nor my colleagues, first concern whether or not the code is short. Or effective. While I appreciate the advice for further projects it's not quite my focal point at the moment.

      The thing that is bothering me, is that since the Script is supposed to work on machines that dont quite belong to me (we are managing Servers for multiple customers) it is supposed to work "out of the box" meaning, it's supposed to work with the modules that our monitoring software delivers together with it's perl distribution which means, that it should, in general, work without further modules.

      Another restriction would be, that the colleague/person I got the task from, doesnt want the path to be mounted via a "lettered" drive. Which means they dont want something like "net use" or "pushd". Since it is not quite in my place to challenge that, it is more or less a restriction I have to live with.

      Also thanks for addressing the misslabeling of the Password assignment. I assign the option twice, because it sometime is optional and sometimes is not. Since the script, at least in the future, is also supposed to handle local filesystems and other types of shares (I think locally mounted shares wouldn't be that complicated, especially if they dont require authentication, since they would work just like local files), I basically said, that if the user is saying that I am supposed to handle an SMB share, it forces a password/username to be given, if I dont enter that conditional though, meaning, if the Filesystem given doesnt require authentication, then Password and username can be assigned but no Error will be generated if they are not given.

      As it stands, the script quite possible needs to be revised almost entirely sooner or later anyway.

        So maybe you want a better formatted and more perlish version of your script:

        use strict; use warnings; use Getopt::Std; #shift(@ARGV); my %opt=(); getopts("f:t:s:u:p:", \%opt); print "ZAXML-TXT\n"; my $Filepfad; my $Volumetype; my $Username; my $Password; my $Searchpattern; my ($opt_file, $opt_type, $opt_user, $opt_passwd, $opt_search); ($opt_file, $opt_type, $opt_user, $opt_passwd, $opt_search) = ($opt{f} +, $opt{t}, $opt{u}, $opt{p}, $opt{s}); print "$opt{f}\n"; if ($opt_file) { $Filepfad = $opt_file; } else { exit_with_msg( "A File-Path must be given", 1 ); } if ($opt_type) { if ( $opt_type ne "UNC" && $opt_type ne "local" && $opt_type ne "N +FS" && $opt_type ne "SMB" ) { exit_with_msg( "Filesystem-type must be UNC, local, NFS or SMB +", 1 ); } elsif ( $opt_type eq "SMB" ) { $Volumetype = $opt_type; if ( $opt_user && $opt_passwd ) { $Username = $opt_user; $Password = $opt_passwd; } else { exit_with_msg( "Username and Password are required for acc +ess to SMB-Shares", 1 ); } } else { $Volumetype = $opt_type; } } else { exit_with_msg( "Volumetype must be given", 1 ); } if ($opt_user) { $Username = $opt_user; } if ($opt_passwd) { $Password = $opt_passwd; } if ($opt_search) { $Searchpattern = $opt_search; } else { exit_with_msg( "A Searchpattern must be given", 1 ); } my $os = $^O; my $last_slash; if ( $os eq "MSWin32" && $Volumetype eq "local" ) { $last_slash = rindex( $Filepfad, "\\", ); } else { $last_slash = rindex( $Filepfad, "/", ); } my $Test_Volume = substr( $Filepfad, 0, $last_slash ); print "Test_Volume: $Test_Volume\n"; if ( !-d $Test_Volume ) { exit_with_msg( "Volume does not exist, isn't mounted or can't be r +ead", 2 ); } if ( -f $Filepfad ) { if ( !unlink($Filepfad) ) { exit_with_msg( "Can't delete existing File $Filepfad", 2 ); } } open my $Filehandle, '>', $Filepfad or exit_with_msg( "Can't create new file ${Filepfad} for write acc +ess", 2 ); print {$Filehandle} "Test\n" or exit_with_msg( "Couldn't write into File", 2 ); close $Filehandle or exit_with_msg( "Can't close file ${Filepfad} after writing", 2 +); open $Filehandle, '<', $Filepfad or exit_with_msg( "Can't create new file ${Filepfad} for read acce +ss", 2 ); while ( my $Content = <$Filehandle> ) { chomp $Content; if ( $Content eq $Searchpattern ) { exit_with_msg( "Files can be written and read", 3 ); } else { exit_with_msg( "File can be written but not read or file-conte +nt does not match Searchpattern", 4 ); } } #end while 1; sub exit_with_msg { my ( $message, $returnid ) = @_; print "CURRVALUE_STRING: $message\n"; print "RETURNID: $returnid\n"; exit 0; }

        I also want to mention that I personally do not like Getopt::Std, I would prefer Getopt::Long. This is a core module, so you are able to use it without installing any package from CPAN.

        Switching to Getopt::Long is left as an exercise for the OP.

        Also I included the new sub exit_with_msg inspired by haukex.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1223731]
Approved by marto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others musing on the Monastery: (3)
As of 2019-12-14 02:44 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?