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

Fellow monks,

Disclamer: I must appologize in advance. This question is about perl, but the way it is peppered throughout our C code is not the best. Yet, for historic reasons, complete C code overhaul is out of the question.

Description: We have a C program that sits in a while(1) loop. Every now and again, this code decides to archive some data (append to existing file) by first fetching the existing file from the server. The archiving routine is done via perl's Net::FTP module. Since many workstations are running this code at the same time and the archiving is done to a single server, there are lots of lock files in place to inforce mutual exclusion.

Problem: The program is started on a workstation (I've seen the problem on just one workstation, but I think others have the problem as well). Time after time the archiving works fine. However some time later (still the same process, so nothing has changed), I get an error

Can't locate vars.pm in @INC (@INC contains: /opt/kiS600/lib/perl/sun4 +-solaris-thread-multi /opt/kiS600/lib/perl /usr/local/ActivePerl-5.6/ +lib/5.6.1/sun4-solaris-thread-multi /usr/local/ActivePerl-5.6/lib/5.6 +.1 /usr/local/ActivePerl-5.6/lib/site_perl/5.6.1/sun4-solaris-thread- +multi /usr/local/ActivePerl-5.6/lib/site_perl/5.6.1 /usr/local/Active +Perl-5.6/lib/site_perl .) at /usr/local/ActivePerl-5.6/lib/site_perl/ +5.6.1/Net/FTP.pm line 14. BEGIN failed--compilation aborted at /usr/local/ActivePerl-5.6/lib/sit +e_perl/5.6.1/Net/FTP.pm line 14. Compilation failed in require at archive_getfile.pl line 3. BEGIN failed--compilation aborted at archive_getfile.pl line 3.
What does this mean? How could the same instance of the C process be both successful and unsuccessful? Perl version v5.6.1 built for sun4-solaris-thread-multi and OS is SunOS 5.8 Generic_108528-13 sun4u sparc SUNW,Ultra-5_10. Here's the C code.

/* this function creates and executes a perl script to fetch a remote +file via FTP */ int archive_getfile(const char *remotepath,const char *remotefilename, +const char *localfilename,const char *ip,const char *user,const char +*pw,int failifnotfound) { const char *perlfilename = "archive_getfile.pl"; FILE *perlfile; int r; char line[90]; int verbose = 0; int logintimeout; #ifdef DEBUG_ARCHIVE printf("archive_getfile(%s/%s -> %s)\n",remotepath,remotefilename, +localfilename); #endif perlfile = fopen(perlfilename,"w"); if (!perlfile) { perror("Error creating archive_getfile.pl"); return -1; } fprintf(perlfile,"$|++;\nuse strict;\n"); fprintf(perlfile,"use Net::FTP;\n"); fprintf(perlfile,"my $ip = '%s';\n", ip); fprintf(perlfile,"my $user = '%s';\n", user); fprintf(perlfile,"my $passwd = '%s';\n", pw); fprintf(perlfile,"my $remotepath = '%s';\n", remotepath); fprintf(perlfile,"my $remotefilename = '%s';\n", remotefilename); fprintf(perlfile,"my $localfilename = '%s';\n", localfilename); fprintf(perlfile,"my $global_lock = $remotefilename . '.lock';\n") +; fprintf(perlfile,"my $local_lock = $global_lock . '.local';\n"); fprintf(perlfile,"my $global_delete = $global_lock . '.delete';\n" +); fprintf(perlfile,"my $myname = `uname -n`;\n"); fprintf(perlfile,"my $pid = $$;\n"); fprintf(perlfile,"my $exitcode = 0;\n"); fprintf(perlfile,"my $locked = 0;\n"); fprintf(perlfile,"my $waittime = 0;\n"); fprintf(perlfile,"\n"); fprintf(perlfile,"$SIG{__DIE__}=sub {Finish(-1)};\n"); fprintf(perlfile,"\n"); fprintf(perlfile,"open(LOCK_LOCAL,\"> $local_lock\") || die \"Can' +t write to $local_lock: $!\n\";\n"); fprintf(perlfile,"chomp $myname;\n"); fprintf(perlfile,"print LOCK_LOCAL \"$myname,$pid\n\";\n"); fprintf(perlfile,"close(LOCK_LOCAL);\n"); fprintf(perlfile,"\n"); fprintf(perlfile,"my $ftp = Net::FTP->new($ip, Debug=>0, Timeout=> +500);\n"); fprintf(perlfile,"if($ftp->login($user, $passwd)) {\n"); fprintf(perlfile," if($ftp->cwd($remotepath)) {\n"); fprintf(perlfile," do {\n"); fprintf(perlfile," if(!$ftp->append($local_lock, $global_lock +)) {\n"); fprintf(perlfile," print \"Could not append $local_lock to +$global_lock\n\";\n"); fprintf(perlfile," $exitcode = -40;\n"); fprintf(perlfile," Finish($exitcode);\n"); fprintf(perlfile," }\n"); fprintf(perlfile," if (!$ftp->get($global_lock)) {\n"); fprintf(perlfile," print \"Could not get $global_lock\n\";\ +n"); fprintf(perlfile," $exitcode = -41;\n"); fprintf(perlfile," Finish($exitcode);\n"); fprintf(perlfile," }\n"); fprintf(perlfile," open(LOCK, $global_lock) || die \"Can't op +en $global_lock: $!\n\";\n"); fprintf(perlfile," my $line = <LOCK>;\n"); fprintf(perlfile," chomp $line;\n"); fprintf(perlfile," my ($lock_name, $lock_pid) = split(/,/,$li +ne);\n"); fprintf(perlfile," close(LOCK);\n"); fprintf(perlfile," if (($lock_name eq $myname) && ($lock_pid +== $pid)) {\n"); fprintf(perlfile," $locked = 1;\n"); fprintf(perlfile," } else {\n"); fprintf(perlfile," if ($waittime > 120) {\n"); fprintf(perlfile," print \"Deleting persistant $global_lo +ck\n\";\n"); fprintf(perlfile," my $delete_locked = 0;\n"); fprintf(perlfile," $waittime = 0;\n"); fprintf(perlfile," do {\n"); fprintf(perlfile," if(!$ftp->append($local_lock, $globa +l_delete)) {\n"); fprintf(perlfile," print \"Could not append $local_lo +ck to $global_delete\n\";\n"); fprintf(perlfile," $exitcode = -42;\n"); fprintf(perlfile," Finish($exitcode);\n"); fprintf(perlfile," }\n"); fprintf(perlfile," if (!$ftp->get($global_delete)) {\n" +); fprintf(perlfile," print \"Could not get $global_dele +te\n\";\n"); fprintf(perlfile," $exitcode = -43;\n"); fprintf(perlfile," Finish($exitcode);\n"); fprintf(perlfile," }\n"); fprintf(perlfile," open(LOCK, $global_delete) || die \" +Can't open $global_delete: $!\n\";\n"); fprintf(perlfile," my $line = <LOCK>;\n"); fprintf(perlfile," chomp $line;\n"); fprintf(perlfile," my ($lock_name, $lock_pid) = split(/ +,/,$line);\n"); fprintf(perlfile," close(LOCK);\n"); fprintf(perlfile," if (($lock_name eq $myname) && ($loc +k_pid == $pid)) {\n"); fprintf(perlfile," $delete_locked = 1;\n"); fprintf(perlfile," $ftp->delete($global_lock);\n"); fprintf(perlfile," } else {\n"); fprintf(perlfile," if ($waittime > 30) {\n"); fprintf(perlfile," print \"Call Marty. Something th +at should not have happened, did!\n\";\n"); fprintf(perlfile," $exitcode = -44;\n"); fprintf(perlfile," Finish($exitcode);\n"); fprintf(perlfile," } else {\n"); fprintf(perlfile," print \"Delete lock for $remotef +ilename is locked - waiting 3 seconds before retry.\n\";\n"); fprintf(perlfile," sleep(3);\n"); fprintf(perlfile," $waittime += 3;\n"); fprintf(perlfile," }\n"); fprintf(perlfile," }\n"); fprintf(perlfile," } while (!$delete_locked);\n"); fprintf(perlfile," } else {\n"); fprintf(perlfile," my $sleeptime = int(rand(10))+1;\n"); fprintf(perlfile," print \"File $remotefilename is locked + by $lock_name ($lock_pid)- waiting $sleeptime seconds before retry.\ +n\";\n"); fprintf(perlfile," sleep($sleeptime);\n"); fprintf(perlfile," $waittime += $sleeptime;\n"); fprintf(perlfile," }\n"); fprintf(perlfile," }\n"); fprintf(perlfile," } while (!$locked);\n"); fprintf(perlfile," $ftp->delete($global_delete);\n"); if(verbose) fprintf(perlfile," print 'Got my own lockfile\n';\n +"); fprintf(perlfile," if($ftp->get($remotefilename, $localfilename +)) {\n"); if(verbose) fprintf(perlfile," print \"Retrieved $remotepath/ +$remotefilename\n\";\n"); fprintf(perlfile," } else {\n"); if(failifnotfound) fprintf(perlfile," print \"Could not retri +eve file $remotepath/$remotefilename\n\";\n"); fprintf(perlfile," $exitcode = -100;\n"); fprintf(perlfile," }\n"); fprintf(perlfile," } else {\n"); fprintf(perlfile," print \"Could not cd to $remotepath\n\";\n") +; fprintf(perlfile," $exitcode = -30;\n"); fprintf(perlfile," }\n"); fprintf(perlfile," $ftp->quit;\n"); fprintf(perlfile,"} else {\n"); fprintf(perlfile," print \"Could not login to $ip as $user\n\";\n +"); fprintf(perlfile," $exitcode = -20;\n"); fprintf(perlfile,"}\n"); fprintf(perlfile,"Finish($exitcode);\n"); fprintf(perlfile,"\n"); fprintf(perlfile,"sub Finish {\n"); fprintf(perlfile," unlink($global_lock);\n"); fprintf(perlfile," unlink($local_lock);\n"); fprintf(perlfile," unlink($global_delete);\n"); fprintf(perlfile," exit shift;\n"); fprintf(perlfile,"}\n"); fclose(perlfile); logintimeout = 50; sprintf(line,"perl %s",perlfilename); do { r = system(line); r = r & 0xff00; r >>= 8; r = (char)r; if(r==-20) sleep(10); } while(r==-20 && logintimeout--); remove(perlfilename); return r; }

Replies are listed 'Best First'.
Re: Can't locate vars.pm ... sometimes
by rinceWind (Monsignor) on Jun 24, 2004 at 14:51 UTC
    Perl is complaining about vars.pm, which is a standard pragma, and should be there.

    Could you please get to a command line, and run the command 'perl -V' and post the output.

    --
    I'm Not Just Another Perl Hacker

      kthmgr@myhost<17> perl -V Summary of my perl5 (revision 5.0 version 6 subversion 1) configuratio +n: Platform: osname=solaris, osvers=2.6, archname=sun4-solaris-thread-multi uname='sunos sparky 5.6 generic_105181-26 sun4u sparc sunw,ultra-5 +_10 ' config_args='-des -Dcc=gcc -Dcf_by=ActiveState -Dcf_email=ActivePe +rl@ActiveState.com -Uinstallusrbinperl -Ud_sigsetjmp -Dusethreads -Du +seithreads -Dinc_version_list=5.6.0/$archname 5.6.0 -Dprefix=/usr/loc +al/ActivePerl-5.6' hint=recommended, useposix=true, d_sigaction=define usethreads=define use5005threads=undef useithreads=define usemulti +plicity=define useperlio=undef d_sfio=undef uselargefiles=define usesocks=undef use64bitint=undef use64bitall=undef uselongdouble=undef Compiler: cc='gcc', ccflags ='-DUSE_REENTRANT_API -D_POSIX_PTHREAD_SEMANTICS + -D_REENTRANT -fno-strict-aliasing -I/usr/local/include -D_LARGEFILE_ +SOURCE -D_FILE_OFFSET_BITS=64', optimize='-O', cppflags='-DUSE_REENTRANT_API -D_POSIX_PTHREAD_SEMANTICS -D_REENTR +ANT -fno-strict-aliasing -I/usr/local/include' ccversion='', gccversion='2.95.2 19991024 (release)', gccosandvers +='solaris2.6' intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=4321 d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=1 +6 ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', + lseeksize=8 alignbytes=8, usemymalloc=n, prototype=define Linker and Libraries: ld='gcc', ldflags =' -L/usr/local/lib ' libpth=/usr/local/lib /usr/lib /usr/ccs/lib libs=-lsocket -lnsl -ldl -lm -lposix4 -lpthread -lc perllibs=-lsocket -lnsl -ldl -lm -lposix4 -lpthread -lc libc=/lib/libc.so, so=so, useshrplib=false, libperl=libperl.a Dynamic Linking: dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags=' ' cccdlflags='-fPIC', lddlflags='-G -L/usr/local/lib' Characteristics of this binary (from libperl): Compile-time options: MULTIPLICITY USE_ITHREADS USE_LARGE_FILES PERL +_IMPLICIT_CONTEXT Locally applied patches: ActivePerl Build 629 Built under solaris Compiled at Aug 20 2001 15:57:49 %ENV: PERL5LIB="/opt/kiS600/lib/perl" @INC: /opt/kiS600/lib/perl/sun4-solaris-thread-multi /opt/kiS600/lib/perl /usr/local/ActivePerl-5.6/lib/5.6.1/sun4-solaris-thread-multi /usr/local/ActivePerl-5.6/lib/5.6.1 /usr/local/ActivePerl-5.6/lib/site_perl/5.6.1/sun4-solaris-thread- +multi /usr/local/ActivePerl-5.6/lib/site_perl/5.6.1 /usr/local/ActivePerl-5.6/lib/site_perl . kthmgr@myhost<18>
Re: Can't locate vars.pm ... sometimes
by iburrell (Chaplain) on Jun 24, 2004 at 16:06 UTC
    Since you are generating and running the Perl script from inside the C program, the problem is likely in how you are running Perl from the C program. It is quite possible that environment is wrong. Check the PATH and PERL5LIB. Check which Perl is being used. It is possible that there is an old perl version installed which is getting run instead of the one you expect.

    Why can't you pull the Perl code out of the C program? Put the Perl code in its own file. Then have the C program call the script with the arguments it needs. Much simpler, and you can debug or use the Perl script outside of the C program.

      That is actually what the code does. The perl code is generated by the C, then sprintf(cmd,"perl perlfile.pl"); system(cmd);.Also, like I said earlier, it is just ONE process that is both successful and unsuccessful at the same time. Since the process is started, the environment is not changing, so I don't see how that could have any effect on anything.
        I meant don't generate the Perl code from the C code. Save the perl script in a file, put it some place where it can be run, and run it with a similar command line but passing the username or password as arguments. This will make it much easier to maintain and test the Perl script.

        Is the C process continually running and it sometimes fails in the same execution of the C process? Is there anywhere else in the code where the C program gets forked, or changes the environment?

        Do you have multiple perl installations? Is there any possibility it could be running different ones if the PATH changes? It is suspicious that there are two different sets of directories in the library list. Try specifying the full path to the perl program you want to use.

        Other things to try are printing out the command before running. Save the perl scripts and try running them manually with different environments. Compare the perl scripts to see if they are different for failing and successful execution. Print out the environment before running the command.

Re: Can't locate vars.pm ... sometimes
by graff (Chancellor) on Jun 25, 2004 at 02:22 UTC
    You say it happens on one workstation (but it might happen on others), and it happens only intermittently (it works fine on most iterations, but now and then an iteration fails).

    How stable is your solaris network? The error message seems to be saying that when the perl interpreter is trying to load the Net::FTP module, that module includes a "use vars;" statement, and the interpreter fails to find vars.pm. So on that basis, I'd say it has nothing to do with how the C program is creating and running the perl script.

    Maybe it just happens occasionally that an iteration fires off when a particular disk volume has been unmounted? or when a particular server has gone down? or when some poor sysadmin is mucking with symlinks that involve some of the perl library paths or causing some similar form of mischief?

    Good luck with tracking that down. In the meantime, I would like to reinforce and amplify the suggestion in an earlier reply:

    Write the perl script JUST ONCE, and KEEP IT THE SAME. You do not need to rewrite it every time you want to run it. Looking at how the C code is writing the perl code, there seem to be just six parameters that could change from one iteration to the next (ip, user, pw, remotepath, remotefilename, localfilename). It would be simple for a single, unchanging copy of the perl script to read these six values from a parameter file written by the C code.

    (You could even put the six params on the command line that the C code executes to run the script, but some people have security concerns about password strings being provided on a command line. I presume you may be writing and deleting the script each time because it contains a user name and password -- but you can write a delete a parameter file just as easily, in fact easier.)

    If there is a chance that multiple machines might be running the same C code at the same time, and repeatedly writing their perl scripts to the same directory, then you really do have to work hard on file locking. But if you have just one read-only copy of the perl script (modified to read the six variable values from a given param file), and configure the C code to just write a distinct param file for each iteration, everything becomes a lot simpler and more robust. (Plus you can change/upgrade/fix the behavior of the perl script without having to recompile the C code.)

    In other words, the C code just has to do this:

    parfile = fopen( uniq_param_name, "w" ); fprintf( parfile, %s %s %s %s %s %s\n", ip, user, pw, remotepath, remotefilename, localfilename ); fclose( parfile ); ... sprintf(line, "perl ftp_script.perl %s", uniq_param_name); do { r = system( line ); ... } ...
    Meanwhile, the perl script would be just the same as the C code currently writes it, except that the first executatble statements would be:
    my $parfile = shift; open( PAR, $parfile ); $_ = <>; my ( $ip, $user, $passwd, $remotepath, $remotefilename, $localfilename + ) = split; close PAR; unlink $parfile; # why wait for the C code to do this? ...