source: dasscm/trunk/usr/bin/dasscm @ 1069

Last change on this file since 1069 was 1069, checked in by joergs, 11 years ago

initial

  • Property keyword set to id
  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 48.4 KB
Line 
1#!/usr/bin/perl
2
3# $Id: dasscm 1069 2012-08-17 11:23:16Z joergs $
4
5use warnings;
6use strict;
7
8use Env
9  qw($DASSCM_PROD $DASSCM_REPO $USER $DASSCM_USERNAME $DASSCM_USER $DASSCM_PASSWORD $SHELL);
10use Cwd;
11use Getopt::Long;
12use File::Basename;
13use File::Compare;
14## used system("cp -a"), because File::Copy does not keep permissions
15##use File::Copy;
16use File::Find;
17use File::stat;
18use File::Path;
19## Term::ReadKey (ReadMode('noecho')) replaced by "stty" to reduce dependencies
20##use Term::ReadKey;
21
22use Data::Dumper;
23
24#####################################################################
25#
26# global
27#
28
29# shell exit codes
30my $RETURN_OK  = 0;
31my $RETURN_NOK = 1;
32
33# Nagios return codes
34my $RETURN_WARN    = 1;
35my $RETURN_CRIT    = 2;
36my $RETURN_UNKNOWN = 3;
37
38# documentation file (for usage)
39my $doc_file = "/usr/share/doc/packages/dasscm/dasscm_howto.txt";
40
41my $config_file = "/etc/dasscm.conf";
42my $config      = get_config($config_file);
43
44my @OPTIONS_GLOBAL = ( 'help', 'verbose' );
45
46# command called => command definition key
47my %COMMANDS = (
48    'help'              => 'help',
49    'login'             => 'login',
50    'init'              => 'init',
51    'ls'                => 'ls',
52    'update'            => 'update',
53    'up'                => 'update',
54    'add'               => 'add',
55    'commit'            => 'commit',
56    'checkin'           => 'commit',
57    'ci'                => 'commit',
58    'revert'            => 'revert',
59    'blame'             => 'blame',
60    'diff'              => 'diff',
61    'status'            => 'status',
62    'st'                => 'status',
63    'check'             => 'check',
64    'permissions'       => 'permissions',
65    'cleanup'           => 'cleanup',
66    'complete'          => 'complete',
67    'complete_path'     => 'complete_path',
68    'complete_repopath' => 'complete_repopath',
69    'plugins'           => 'plugins',
70);
71
72# desc: description (eg. for usage)
73# params: parameters
74#           CMD
75#           USER
76#           PATH_PROD
77#           PATH_REPO
78# require:
79#           WRITE commands that require write access (and therefore a login)
80my %COMMAND_DEFINITIONS = (
81    'help' => {
82        'desc'     => ["print help and usage information"],
83        'params'   => ["CMD"],
84        'function' => \&help,
85    },
86    'login' => {
87        'desc'     => ["user login to Subversion repositoty"],
88        'params'   => ["USER"],
89        'function' => \&login
90    },
91    'init' => {
92        'desc' => [
93            "initialize local subversion checkout.",
94            "This is the first thing to do (after configuring $config_file)"
95        ],
96        'params'   => [],
97        'function' => \&init
98    },
99    'ls' => {
100        'desc'     => ["list file from repository"],
101        'params'   => ["PATH_REPO"],
102        'function' => \&ls
103    },
104    'update' => {
105        'desc' => [
106            "update local repository checkout",
107            "Normally, this is done automatically"
108        ],
109        'params'   => ["PATH_REPO"],
110        'function' => \&update
111    },
112    'add' => {
113        'desc' => [
114            "add a file to the subversion repository",
115            "Unlike the native svn command,",
116            "dasscm adds and immediatly submits a file to the subversion repository"
117        ],
118        'params'   => ["PATH_PROD"],
119        'options'  => [ 'verbose', 'message=s' ],
120        'require'  => ["WRITE"],
121        'function' => \&add
122    },
123    'commit' => {
124        'desc' => ["commit a changed file to the subversion repository"],
125        ## TODO: only modified files
126        'params'   => ["PATH_REPO"],
127        'options'  => [ 'verbose', 'message=s' ],
128        'require'  => ["WRITE"],
129        'function' => \&commit
130    },
131    'revert' => {
132        'desc' => [
133            "revert local changes back to version from the repository (see diff)"
134        ],
135        'params'   => ["PATH_REPO"],
136        'function' => \&revert
137    },
138    'blame' => {
139        'desc' => ['like "svn blame"'],
140        ## TODO: only files from PATH_REPO
141        'params'   => ["PATH_REPO"],
142        'function' => \&blame
143    },
144    'diff' => {
145        'desc' => [
146            'display the differences between files on the system and the repository'
147        ],
148        'params'   => ["PATH_REPO"],
149        'function' => \&diff
150    },
151    'status' => {
152        'desc' => [
153            'display status information about modified and deleted files.',
154            'If no path is given "/" is assumed',
155            '(in contract to "svn" with assumes ".")'
156        ],
157        'params'   => ["PATH_REPO"],
158        'function' => \&status
159    },
160    'check' => {
161        'desc'     => ["perform Nagios NRPE conform check"],
162        'params'   => [],
163        'function' => \&check
164    },
165    'permissions' => {
166        'desc' =>
167          ["internal, print permissions for all files in the repository"],
168        'params'   => [],
169        'function' => \&permissions
170    },
171    'cleanup' => {
172        'desc'     => ["internal, used to clean repository checkout"],
173        'params'   => [],
174        'function' => \&cleanup
175    },
176    'complete' => {
177        'desc'     => ["internal, used for bash completion"],
178        'params'   => ["CMD"],
179        'function' => \&complete
180    },
181    'complete_path' => {
182        'desc'     => ["internal, used for bash completion"],
183        'params'   => [],
184        'function' => \&complete_path
185    },
186    'complete_repopath' => {
187        'desc'     => ["internal, used for bash completion"],
188        'params'   => [],
189        'function' => \&complete_repopath
190    },
191    'plugins' => {
192        'desc'     => ["internal, perform plugins"],
193        'params'   => [],
194        'function' => \&perform_plugins
195    },
196
197);
198
199# configuration file
200my $DASSCM_LOCAL_REPOSITORY_BASE;
201my $DASSCM_REPOSITORY_NAME;
202my $DASSCM_PLUGIN_RESULTS_PATH;
203my $DASSCM_SVN_REPOSITORY;
204my $DASSCM_CHECKOUT_USERNAME;
205my $DASSCM_CHECKOUT_PASSWORD;
206my $DASSCM_GID;
207my @DASSCM_ADDITIONAL_FILES;
208
209# current directory at program start
210my $StartDirectory = cwd();
211
212my $diff                   = "diff --exclude .svn ";
213my $SVN                    = "svn ";
214my $svnOptions             = "";
215my $svnCheckoutCredentials = "";
216my $svnPasswordCredentials = "";
217
218# flag. Set to true by svn_update
219# This prevents, that svn_update is called multiple times
220my $svnRepositoryIsUptodate = 0;
221
222# command line options get stored in options hash
223my %options = ();
224
225# subcommand, that gets executed (add, commit, ...)
226my $command;
227
228my $verbose = 0;
229
230#####################################################################
231#
232# util functions
233#
234sub usage()
235{
236    print '$Id: dasscm 1069 2012-08-17 11:23:16Z joergs $';
237    print "\n\n";
238    print "usage: dasscm <subcommand> [options] [args]\n";
239    print "\n";
240    print "dasscm is intended to help versioning configuration files\n";
241    print "\n";
242    print "Available subcommands:\n";
243    foreach my $i ( sort keys(%COMMAND_DEFINITIONS) ) {
244        print "    ", $i, " ", join( " ", get_command_possible_params($i) ),
245          "\n";
246        foreach my $line ( get_command_desc($i) ) {
247            print " " x 20, $line, "\n";
248        }
249    }
250    print "\n";
251    print "If dasscm is not yet configured, read $doc_file\n";
252}
253
254sub warning(@)
255{
256    print "Warning: " . join( "\n         ", @_ ) . "\n";
257}
258
259sub error(@)
260{
261    print "Error:   " . join( "\n         ", @_ ) . "\n";
262}
263
264sub fatalerror(@)
265{
266    error(@_);
267
268    #print "Exiting\n";
269    exit 1;
270}
271
272#
273# reading config file and return key/value pairs as hash
274#
275sub get_config
276{
277    my $file = $_[0];
278
279    if ( !$file ) {
280        fatalerror( "failed to open config file" . $file );
281    }
282
283    my $data = {};
284
285    # try to open config file
286    if ( !open( FH, $file ) ) {
287        fatalerror( "failed to open config file" . $file );
288    } else {
289        while (<FH>) {
290            chomp;
291            if (/^#/) {
292                next;
293            }
294            if ( $_ =~ /=/g ) {
295
296                # splitting in 2 fields at maximum
297                my ( $option, $value ) = split( /=/, $_, 2 );
298                $option =~ s/^\s+//g;
299                $option =~ s/\s+$//g;
300                $option =~ s/\"+//g;
301                $value  =~ s/^\s+//g;
302                $value  =~ s/\s+$//g;
303                $value  =~ s/\"+//g;
304
305                if ( length($option) ) {
306                    $data->{$option} = $value;
307                }
308            }
309        }
310    }
311    close(FH);
312
313    return $data;
314}
315
316#
317# check and evaluate environment variables
318#
319sub check_env()
320{
321
322    # DASSCM_PROD
323    if ( !$DASSCM_PROD ) {
324        $DASSCM_PROD = "/";
325    }
326
327    if ( !-d $DASSCM_PROD ) {
328        die "DASSCM_PROD ($DASSCM_PROD) is not set to a directory.\n";
329    }
330    if ($verbose) { print "DASSCM_PROD: " . $DASSCM_PROD . "\n"; }
331
332    # DASSCM_REPOSITORY_NAME
333    if ( !$DASSCM_REPOSITORY_NAME ) {
334        die
335          "Variable DASSCM_REPOSITORY_NAME is not defined.\nIt needs to be a unique name.\nNormally the full qualified host name is used.\nUse file $config_file to configure it.\n";
336    }
337
338    # DASSCM_REPO
339    if ( !$DASSCM_REPO ) {
340        if ( $DASSCM_LOCAL_REPOSITORY_BASE && $DASSCM_REPOSITORY_NAME ) {
341            $DASSCM_REPO =
342              $DASSCM_LOCAL_REPOSITORY_BASE . "/" . $DASSCM_REPOSITORY_NAME;
343        } else {
344            die
345              "Envirnonment variable DASSCM_REPO not set.\nSet DASSCM_REPO to the directory of the versioning system checkout for this machine.\n";
346        }
347    }
348    $DASSCM_REPO = normalize_path($DASSCM_REPO);
349    if ($verbose) { print "DASSCM_REPO: " . $DASSCM_REPO . "\n"; }
350
351    #
352    # subversion checkout user
353    #
354    if ( !$DASSCM_CHECKOUT_USERNAME ) {
355        fatalerror(
356            "variable DASSCM_CHECKOUT_USERNAME is not defined.",
357            "Use file $config_file to configure it."
358        );
359    }
360
361    if ( !$DASSCM_CHECKOUT_PASSWORD ) {
362        fatalerror(
363            "variable DASSCM_CHECKOUT_PASSWORD is not defined.",
364            "Use file $config_file to configure it."
365        );
366    }
367
368    #
369    # check if local repository directory exist
370    # (if not creating by init)
371    #
372    if ( $command ne "init" ) {
373        if ( not -d $DASSCM_REPO ) {
374            fatalerror(
375                "Can't access local repository DASSCM_REPO",
376                "($DASSCM_REPO)",
377                "Check configuration and execute",
378                "dasscm init"
379            );
380        }
381
382        #
383        # user settings
384        #
385
386        # DASSCM_USER is legacy. Use DASSCM_USERNAME instead
387        if ( !$DASSCM_USERNAME ) {
388            $DASSCM_USERNAME = $DASSCM_USER;
389        }
390
391        # user root is not allowed for checkins.
392        # if user is root, DASSCM_USER has to be set,
393        # otherwise USER can be used
394        if ( "$USER" eq "root" ) {
395            if (    ( not $DASSCM_USERNAME )
396                and ( get_command_requires_write($command) ) )
397            {
398
399                #( $command ne "login" ) and ( $command ne "status" ) ) {
400                fatalerror(
401                    "Envirnonment variable DASSCM_USERNAME not set.",
402                    "Set DASSCM_USERNAME to your subversion user account or",
403                    "use 'dasscm login'"
404                );
405            }
406            $svnOptions .= " --no-auth-cache ";
407        } elsif ( !$DASSCM_USERNAME ) {
408            $DASSCM_USERNAME = $USER;
409        }
410
411        #
412        # password
413        #
414        if ($DASSCM_PASSWORD) {
415            $svnPasswordCredentials = " --password '$DASSCM_PASSWORD' ";
416        }
417    }
418
419    #$svnOptions .= " --username $DASSCM_USERNAME "
420   
421    #
422    # prepare file permissions
423    # (read-write access for group "dasscm",
424    # if this group exists)
425    #
426    (my $gname, my $gpw, $DASSCM_GID, my $members) = getgrnam( "dasscm" );
427    if( $DASSCM_GID ) {
428        umask 0007
429    }
430}
431
432#
433# has been intendend,
434# to check addtitional parameters.
435# Currently not used.
436#
437sub check_parameter(@)
438{
439}
440
441sub get_command_uniform_name( $ )
442{
443    my $command_abbrivation = $_[0];
444    if ( defined( $COMMANDS{$command_abbrivation} ) ) {
445        return $COMMANDS{$command_abbrivation};
446    }
447    return;
448}
449
450sub get_command_desc( $ )
451{
452    my $command = get_command_uniform_name( $_[0] );
453    my @desc    = ();
454    if ( $command && defined( $COMMAND_DEFINITIONS{$command}{'desc'} ) ) {
455        @desc = @{ $COMMAND_DEFINITIONS{$command}{'desc'} };
456    }
457    return @desc;
458}
459
460sub get_command_function( $ )
461{
462    my $command = get_command_uniform_name( $_[0] );
463    my $func;
464    if ( $command && defined( $COMMAND_DEFINITIONS{$command}{'function'} ) ) {
465        $func = $COMMAND_DEFINITIONS{$command}{'function'};
466    }
467    return $func;
468}
469
470sub get_command_possible_params( $ )
471{
472    my $command = get_command_uniform_name( $_[0] );
473    my @params  = ();
474    if ( $command && defined( $COMMAND_DEFINITIONS{$command}{'params'} ) ) {
475        @params = @{ $COMMAND_DEFINITIONS{$command}{'params'} };
476    }
477    return @params;
478}
479
480sub get_command_possible_options( $ )
481{
482    my $command = get_command_uniform_name( $_[0] );
483    my @params  = ();
484    if ( $command && defined( $COMMAND_DEFINITIONS{$command}{'options'} ) ) {
485        @params = @{ $COMMAND_DEFINITIONS{$command}{'options'} };
486    }
487    return @params;
488}
489
490sub get_command_requirements( $ )
491{
492    my $command      = get_command_uniform_name( $_[0] );
493    my @requirements = ();
494    if ( $command && defined( $COMMAND_DEFINITIONS{$command}{'require'} ) ) {
495        @requirements = @{ $COMMAND_DEFINITIONS{$command}{'require'} };
496    }
497    return @requirements;
498}
499
500sub get_command_requires_write( $ )
501{
502    return grep( /^WRITE$/, get_command_requirements( $_[0] ) );
503}
504
505#
506# normalize path namens:
507#   - directories should end with "/"
508#   - use only single "/"
509#
510sub normalize_path($)
511{
512    my $path = shift || "";
513
514    if ( $path =~ m|^/| ) {
515
516        # full path
517        if ( -d $path ) {
518
519            # ensure, a directory ends with '/'
520            $path .= '/';
521        }
522    } elsif ( -d cwd() . '/' . $path ) {
523
524        # ensure, a directory ends with '/'
525        $path .= '/';
526    }
527
528    # remove double (triple) slashes (/)
529    $path =~ s|/[/]*|/|g;
530
531    # remove self reference path
532    $path =~ s|/./|/|g;
533
534    return $path;
535}
536
537#
538# generate from (relative) filename
539# all required file and directory names:
540# $basename,      $dirname_prod, $dirname_repo,
541# $filename_prod, $filename_repo
542#
543sub get_filenames(@)
544{
545    my $filename_prod = $_[0] || ".";
546
547    # make filename absolut
548    if ( !( $filename_prod =~ m/^\// ) ) {
549        $filename_prod = cwd() . '/' . $filename_prod;
550    }
551
552    # file must be readable.
553    # The only exceptions are,
554    # - if the file parameter is to be completed or
555    # - if a file should be reverted
556    if ( $command ne "revert" && $command !~ m/^complete/ ) {
557        if ( not -r $filename_prod ) {
558            fatalerror( $filename_prod . " is not accessable" );
559        }
560    }
561
562    # dirname buggy: eg. "/etc/" is reduced to "/",
563    # "/etc" is used as filename
564    # herefore make sure, that if filename is a directory,
565    # it will end by "/"
566    $filename_prod = normalize_path($filename_prod);
567
568    ( my $basename, my $dirname_prod ) = fileparse($filename_prod);
569
570    # normalize path.
571    # not done for reverting, because in this case, the directory may not exist
572    # and the correct path should already be stored in the repository
573    if ( $command ne "revert" ) {
574
575        # uses chdir to determine real directory in a unique way
576        chdir $dirname_prod
577          or fatalerror( "failed to access directory $dirname_prod: " . $! );
578        $dirname_prod = normalize_path( cwd() );
579        chdir $StartDirectory;
580    }
581
582    my $dirname_repo  = normalize_path( $DASSCM_REPO . "/" . $dirname_prod );
583    my $filename_repo = normalize_path("$dirname_repo/$basename");
584
585    if ($verbose) {
586        print "filename_repo:  " . $filename_repo . "\n";
587        print "dirname_repo:   " . $dirname_repo . "\n";
588        print "filename_prod:  " . $filename_prod . "\n";
589        print "dirname_prod:   " . $dirname_prod . "\n";
590        print "basename:       " . $basename . "\n";
591    }
592
593    return (
594        $basename,      $dirname_prod, $dirname_repo,
595        $filename_prod, $filename_repo
596    );
597}
598
599sub copy_file_to_repository( $ )
600{
601    my $filename = shift;
602
603    (
604        my $basename,
605        my $dirname_prod,
606        my $dirname_repo,
607        my $filename_prod,
608        my $filename_repo
609    ) = get_filenames($filename);
610
611    #copy( $filename_prod, $filename_repo )
612    ( my $rc, my @result ) =
613      run_command("cp -a \"$filename_prod\" \"$filename_repo\"");
614    if ( $rc != 0 ) {
615        error( "failed to copy $filename_prod to repository: ", @result );
616    }
617
618    # return success
619    return $rc == 0;
620}
621
622sub copy_file_from_repository_to_system( $ )
623{
624    my $filename = shift;
625
626    (
627        my $basename,
628        my $dirname_prod,
629        my $dirname_repo,
630        my $filename_prod,
631        my $filename_repo
632    ) = get_filenames($filename);
633
634    ( my $rc, my @result ) =
635      run_command("cp -a \"$filename_repo\" \"$filename_prod\"");
636    if ( $rc != 0 ) {
637        error( "failed to copy $filename_repo to $filename_prod: ", @result );
638    }
639
640    # return success
641    return $rc == 0;
642}
643
644#
645# creates a file with permissions
646#
647sub generatePermissionList
648{
649
650    # generieren der Zeilen für Permission-Savefile
651    my @files    = @_;
652    my @permlist = ();
653    foreach my $file (@files) {
654        $file = "/" . $file;
655        if ( -e $file ) {
656            my $info = stat($file) || die "failed to stat $file: aborting";
657            my $mode = get_type( $info->mode ) & 07777;
658            my $modestring = sprintf( "%04o", $mode );
659            my $uidnumber  = $info->uid;
660            my $uid        = getpwuid($uidnumber) || $uidnumber;
661            my $gidnumber  = $info->gid;
662            my $gid        = getgrgid($gidnumber) || $gidnumber;
663            push(
664                @permlist,
665                sprintf( "%-55s %-17s %4d",
666                    $file, "${uid}:${gid}", $modestring )
667            );
668        }
669    }
670    return @permlist;
671}
672
673sub get_type
674{
675
676    # Funktion übernommen aus /usr/bin/chkstat
677    my $S_IFLNK  = 0120000;    # symbolic link
678    my $S_IFREG  = 0100000;    # regular file
679    my $S_IFDIR  = 0040000;    # directory
680    my $S_IFCHAR = 0020000;    # character device
681    my $S_IFBLK  = 0060000;    # block device
682    my $S_IFFIFO = 0010000;    # fifo
683    my $S_IFSOCK = 0140000;    # socket
684    my $S_IFMT   = 0170000;    # type of file
685
686    my $S_m;
687    if    ( ( $_[0] & $S_IFMT ) == $S_IFLNK )  { $S_m = $_[0] - $S_IFLNK; }
688    elsif ( ( $_[0] & $S_IFMT ) == $S_IFREG )  { $S_m = $_[0] - $S_IFREG; }
689    elsif ( ( $_[0] & $S_IFMT ) == $S_IFDIR )  { $S_m = $_[0] - $S_IFDIR; }
690    elsif ( ( $_[0] & $S_IFMT ) == $S_IFCHAR ) { $S_m = $_[0] - $S_IFCHAR; }
691    elsif ( ( $_[0] & $S_IFMT ) == $S_IFBLK )  { $S_m = $_[0] - $S_IFBLK; }
692    elsif ( ( $_[0] & $S_IFMT ) == $S_IFFIFO ) { $S_m = $_[0] - $S_IFFIFO; }
693    elsif ( ( $_[0] & $S_IFMT ) == $S_IFSOCK ) { $S_m = $_[0] - $S_IFSOCK; }
694    $S_m;
695}
696
697sub run_command
698{
699    my $command = shift;
700
701    if ($verbose) {
702        print "executing command: " . $command . "\n";
703    }
704
705    my @result;
706    if(  open( RESULT, $command . ' 2>&1 |' ) ) {
707        @result = <RESULT>;
708        close(RESULT);
709    }
710    my $retcode = $? >> 8;
711
712    if ($verbose) {
713        print @result;
714        if( $retcode ) { print "return code: " . $retcode . "\n"; }
715    }
716
717    return ( $retcode, @result );
718}
719
720sub run_interactive
721{
722
723    if ($verbose) {
724        print "run_interactive:" . join( " ", @_ ) . "\n";
725    }
726
727    system(@_);
728    if ( $? == -1 ) {
729        printf "failed to execute: $!\n";
730    } elsif ( $? & 127 ) {
731        printf "child died with signal %d, %s coredump\n", ( $? & 127 ),
732          ( $? & 128 ) ? 'with' : 'without';
733    } elsif ( $? >> 8 != 0 ) {
734        printf "child exited with value %d\n", $? >> 8;
735    }
736    return ( $? >> 8 );
737}
738
739#
740# en- or disable echo mode.
741# used for reading passwords from STDIN
742#
743sub setEchoMode( $ )
744{
745    my $mode = shift;
746    if ($mode) {
747        run_command("stty echo");
748    } else {
749        run_command("stty -echo");
750    }
751}
752
753sub write_array_to_file( $@ )
754{
755    my $filename = shift;
756    my @array    = @_;
757
758    if ( -e $filename && !-w $filename ) {
759        warning( "failed to write to $filename:", "permission denied" );
760        return;
761    }
762
763    if ( !-w dirname($filename) ) {
764        warning( "failed to write to $filename:", "directory does not exist" );
765        return;
766    }
767
768    # directory exists => write
769    if ( !open( OUTFILE, ">$filename" ) ) {
770        warning("failed to open $filename: $!");
771        return;
772    }
773
774    foreach my $line (@array) {
775        print OUTFILE "$line";
776    }
777    close(OUTFILE);
778
779    # if group dasscm exists,
780    # create plugin results with group membership dasscm
781    if( $DASSCM_GID ) {
782        chown( -1, $DASSCM_GID, $filename );
783    }
784
785    return 1;
786}
787
788sub perform_plugins()
789{
790    check_env();
791
792    my @plugin_results = ();
793
794    # get all defined plugins.
795    # Plugin definitions starting with DASSCM_PLUGIN_
796    my @plugins = grep( /^DASSCM_PLUGIN_CMD_/, keys( %{$config} ) );
797
798    for my $plugin (@plugins) {
799        my $plugin_name = substr( $plugin, length("DASSCM_PLUGIN_CMD_") );
800        my $plugin_test = $config->{ 'DASSCM_PLUGIN_TEST_' . $plugin_name };
801        if ($verbose) { print "Plugin $plugin_name: "; }
802        # all plugins are executed with LANG settings C
803        # bash -c is used, to supress all output
804        # (otherwise there are problem with && commands)
805        ( my $rc_test, my @result_test ) = run_command( 'LANG=C bash -c "' . $plugin_test . '"' );
806        if ( $rc_test != 0 ) {
807            if ($verbose) { print "skipped\n"; }
808        } else {
809            if ($verbose) { print "$config->{$plugin}\n"; }
810            ( my $rc, my @result ) = run_command( 'LANG=C bash -c "' . $config->{$plugin} . '"' );
811            if ( $rc != 0 ) {
812                warning("failed to run plugin $plugin");
813            } else {
814                my $plugin_result_file =
815                  $DASSCM_PLUGIN_RESULTS_PATH . "/" . $plugin_name;
816                write_array_to_file( $plugin_result_file, @result );
817                push @plugin_results, $plugin_result_file;
818            }
819        }
820    }
821    return @plugin_results;
822}
823
824sub svn_check_credentials( $$;$$ )
825{
826    my $username = shift;
827    my $password = shift;
828
829    # check silently are allow user interaction?
830    my $interactive = shift || 0;
831
832    # default: exit program, if repository is not accessable
833    #   (do not exit for 'init')
834    my $fatalerror = shift || 1;
835
836    print "checking credentials ";
837
838    if ( !$username ) {
839        fatalerror("no username given");
840    }
841
842    if ( !$password ) {
843        fatalerror("no password given");
844    }
845
846    print "for " . $username . "@" . $DASSCM_SVN_REPOSITORY . ": ";
847
848    # Options for "svn info" are not supported by subversion 1.0.0 (SLES9),
849    # therefore switching to "svn status"
850    # ( my $rc_update, my @result ) =
851    #     run_command(
852    #     "$SVN info --non-interactive --no-auth-cache --username $username --password $password $DASSCM_SVN_REPOSITORY"
853    #     );
854    #print @result;
855
856    my $rc_update;
857    if ($interactive) {
858        $rc_update = run_interactive(
859            "$SVN ls --no-auth-cache --username '$username' --password '$password' $DASSCM_SVN_REPOSITORY"
860        );
861    } else {
862        ( $rc_update, my @result ) = run_command(
863            "$SVN ls --non-interactive --no-auth-cache --username '$username' --password '$password' $DASSCM_SVN_REPOSITORY"
864        );
865
866        if ( $rc_update != 0 ) {
867            print "\n", @result;
868            if ($fatalerror) {
869                fatalerror();
870            }
871            return;
872        }
873    }
874
875    # return success
876    return $rc_update == 0;
877}
878
879sub svn_update( ;$ )
880{
881    my $update_path = shift || "";
882
883    # return value
884    my $update_ok = 1;
885
886    # use this flag to do only one update per run
887    if ( !$svnRepositoryIsUptodate ) {
888        ( my $rc_update, my @result ) = run_command(
889            "$SVN update --non-interactive $svnCheckoutCredentials '$DASSCM_REPO/$update_path'"
890        );
891        print @result;
892        if ( $rc_update != 0 ) {
893            error("failed to update local repository ($update_path)");
894            $update_ok = 0;
895        } elsif ( not $update_path ) {
896
897            # set this flag if a full update is done
898            $svnRepositoryIsUptodate = 1;
899        }
900    }
901    return $update_ok;
902}
903
904sub svn_ls( ;@ )
905{
906    (
907        my $basename,
908        my $dirname_prod,
909        my $dirname_repo,
910        my $filename_prod,
911        my $filename_repo
912    ) = get_filenames( $_[0] );
913
914    # svn ls -R is better, but much, much slower
915    # ( my $rc, my @result ) = run_command("$SVN ls --recursive $svnCheckoutCredentials $path");
916
917    my @files  = ();
918    my @links  = ();
919    my @dirs   = ();
920    my @others = ();
921
922    find(
923        {
924            wanted => sub {
925                my $name = normalize_path($File::Find::name);
926                $name =~ s|^$dirname_repo||;
927
928                #print "($name)\n";# . $File::Find::dir . "\n";
929                if ( not $name ) {
930
931                    # name string is empty (top directory).
932                    # do nothing
933                } elsif ( $name =~ m/\.svn/ ) {
934
935                    # skip svn meta data
936                } elsif ( -l $_ ) {
937
938                    # soft link
939                    # important: check for links first
940                    #   to exclude them from further checks
941                    push( @links, $name );
942                } elsif ( -d $_ ) {
943
944                    # directories
945                    push( @dirs, $name );
946                } elsif ( -f $_ ) {
947
948                    # regular file
949                    push( @files, $name );
950                } else {
951                    push( @others, $name );
952                }
953              }
954        },
955        ($filename_repo)
956    );
957
958    return ( sort( @dirs, @files ) );
959}
960
961sub svn_revert( ;$ )
962{
963    my $path = shift || $DASSCM_REPO;
964
965    ( my $rc_update, my @result ) = run_command("$SVN revert -R '$path'");
966
967    if ( $rc_update != 0 ) {
968        print "\n", @result;
969        error("failed to revert subversion repository changes");
970    }
971}
972
973sub svn_remove_unknown_files( ;$ )
974{
975    my $path = shift || $DASSCM_REPO;
976
977    ( my $rc_update, my @result ) = run_command("$SVN status '$path'");
978
979    if ( $rc_update != 0 ) {
980        print "\n", @result;
981        error("failed to receive subversion repository information");
982    } else {
983        foreach (@result) {
984            if (s/^\? +//) {
985                chomp;
986
987                # if file is unknown to subversion (line starts with "?")
988                # remove it
989                print "removing $_\n";
990
991                # unlink doesn't work recursive, there "rm -rf" is used
992                #unlink($_);
993                system("rm -rf $_");
994            }
995        }
996    }
997}
998
999sub getModifiedFiles( ;$ )
1000{
1001    (
1002        my $basename,
1003        my $dirname_prod,
1004        my $dirname_repo,
1005        my $filename_prod,
1006        my $filename_repo
1007    ) = get_filenames( $_[0] );
1008
1009    my @files = svn_ls($filename_prod);
1010
1011    # stores result from status (cvscheck)
1012    my %removedfiles = ();
1013    my %changedfiles = ();
1014    my %unknownfiles = ();
1015
1016    # create list of modified files
1017    if (@files) {
1018
1019        foreach my $file (@files) {
1020
1021            my $realfile    = $dirname_prod . $file;
1022            my $cvsworkfile = $dirname_repo . $file;
1023
1024            if ( -d $realfile ) {
1025
1026                # directory
1027                if ( !-d "$cvsworkfile" ) {
1028
1029                    # real is directory, repository is not. This is a problem
1030                    $changedfiles{"$realfile"} = $cvsworkfile;
1031                }
1032            } elsif ( !-e $realfile ) {
1033                $removedfiles{"$realfile"} = $cvsworkfile;
1034            } elsif ( !-r $realfile ) {
1035
1036                # don't have permission to read the file,
1037                # can't check it
1038                $unknownfiles{"$realfile"} = $cvsworkfile;
1039            } else {
1040                ( -r "$cvsworkfile" )
1041                  || fatalerror("failed to read $cvsworkfile");
1042                if ( compare( $cvsworkfile, $realfile ) != 0 ) {
1043                    $changedfiles{"$realfile"} = $cvsworkfile;
1044                }
1045            }
1046        }
1047    }
1048
1049    return ( \%changedfiles, \%removedfiles, \%unknownfiles );
1050}
1051
1052#
1053# from an array of files/dirs,
1054# generates list of files
1055# sorted by type
1056#
1057sub get_files( @ )
1058{
1059    my @files  = ();
1060    my @links  = ();
1061    my @dirs   = ();
1062    my @others = ();
1063
1064    if (@_) {
1065        find(
1066            {
1067                wanted => sub {
1068                    my $fullname = cwd() . "/" . $_;
1069                    if ( -l $_ ) {
1070
1071                        # soft link
1072                        # important: check for links first
1073                        #   to exclude them from further checks
1074                        push( @links, $fullname );
1075                    } elsif ( -d $_ ) {
1076
1077                        # directories
1078                        push( @dirs, $fullname );
1079                    } elsif ( -f $_ ) {
1080
1081                        # regular file
1082                        push( @files, $fullname );
1083                    } else {
1084                        push( @others, $fullname );
1085                    }
1086                  }
1087            },
1088            @_
1089        );
1090    }
1091
1092    # don't rely on others.
1093    # If more specific file types are needed,
1094    # they will be added
1095    return {
1096        files  => \@files,
1097        links  => \@links,
1098        dirs   => \@dirs,
1099        others => \@others
1100    };
1101}
1102
1103sub print_files_hash( $ )
1104{
1105    my $href_files = shift;
1106
1107    my @files = @{ $href_files->{files} };
1108    my @links = @{ $href_files->{links} };
1109
1110    if (@files) {
1111        my $number = $#files + 1;
1112        print "files to check-in ($number): \n";
1113        print join( "\n", @files );
1114        print "\n";
1115    }
1116
1117    # TODO: check in links and also link target? At least warn about link target
1118    if (@links) {
1119        my $number = $#links + 1;
1120        print "\n";
1121        print "ignoring links ($number):\n";
1122        print join( "\n", @links );
1123        print "\n";
1124    }
1125
1126}
1127
1128#
1129# use globbing to get lsit of files
1130# that matches the given prefix
1131# used for bash completion
1132#
1133sub get_complete_path_globbing( $ )
1134{
1135    my $path = shift;
1136
1137    # add globbing
1138    $path .= "*";
1139
1140    # get files
1141    my @files = glob($path);
1142
1143    if ( $#files == 0 ) {
1144
1145        # if only one result is available
1146        # and this result is a directory,
1147        # add another result entry
1148        # (directory with and withour trainling /),
1149        # otherwise complete will stop here and continue with the next parameter
1150        my $path = normalize_path( $files[0] );
1151        if ( -d $path ) {
1152            @files = ( substr( $path, 0, -1 ), $path );
1153        }
1154    } else {
1155
1156        # add "/" to all directories
1157        @files = map( { normalize_path($_) } @files );
1158    }
1159
1160    return @files;
1161}
1162
1163#####################################################################
1164#
1165# functions
1166sub help(;@)
1167{
1168    if ( not @_ ) {
1169        usage();
1170    } else {
1171        print "help for ", join( " ", @_ ), ": ...\n";
1172        usage();
1173    }
1174
1175    return $RETURN_OK;
1176}
1177
1178sub login(@)
1179{
1180    check_parameter( @_, 1 );
1181    check_env();
1182
1183    my $input_username = $_[0];
1184
1185    if ( not $input_username ) {
1186        my $output_username = "";
1187        if ($DASSCM_USERNAME) {
1188            $output_username = " ($DASSCM_USERNAME)";
1189        }
1190
1191        print "Enter DASSCM user name", $output_username, ": ";
1192        $input_username = <STDIN>;
1193        chomp($input_username);
1194
1195        $input_username = $input_username || $DASSCM_USERNAME;
1196    }
1197
1198    # hidden password input
1199    print "Enter password for $input_username: ";
1200    setEchoMode(0);
1201    my $input_password = <STDIN>;
1202    setEchoMode(1);
1203    chomp($input_password);
1204    print "\n";
1205
1206    # checking checkout username/password
1207    svn_check_credentials( $DASSCM_CHECKOUT_USERNAME,
1208        $DASSCM_CHECKOUT_PASSWORD );
1209    print "checkout access okay\n";
1210
1211    svn_check_credentials( $input_username, $input_password );
1212
1213    #
1214    # set environment variables
1215    #
1216    $ENV{'DASSCM_USERNAME'} = "$input_username";
1217    $ENV{'DASSCM_PASSWORD'} = "$input_password";
1218
1219    print "subversion access okay\n\n", "DASSCM_USERNAME:   $input_username\n",
1220      "DASSCM_PASSWORD:   (hidden)\n", "DASSCM_PROD:       $DASSCM_PROD\n",
1221      "DASSCM_REPO:       $DASSCM_REPO\n",
1222      "Server Repository: $DASSCM_SVN_REPOSITORY\n", "\n";
1223
1224    status();
1225
1226    print "\n[dasscm shell]\n\n";
1227    my $shell = $SHELL || "bash";
1228    exec($shell) or die "failed to start new shell";
1229}
1230
1231#
1232# initialize local checkout directory (initial checkout)
1233#
1234sub init(@)
1235{
1236    check_parameter( @_, 1 );
1237    check_env();
1238
1239    # don't do repository creation (svn mkdir) here,
1240    # because then their must be a lot of prior checks
1241
1242    # update complete repository
1243    my $retcode = run_interactive(
1244        "cd $DASSCM_LOCAL_REPOSITORY_BASE; $SVN checkout $svnCheckoutCredentials $svnOptions $DASSCM_SVN_REPOSITORY"
1245    );
1246
1247    return $retcode;
1248}
1249
1250sub ls(@)
1251{
1252    my $return_code = $RETURN_OK;
1253    check_parameter( @_, 1 );
1254    check_env();
1255
1256    my @files = svn_ls(@_);
1257
1258    if (@files) {
1259        print join( "\n", @files );
1260        print "\n";
1261    }
1262    return $return_code;
1263}
1264
1265sub update(@)
1266{
1267    my $return_code = $RETURN_OK;
1268    check_parameter( @_, 1 );
1269    check_env();
1270
1271    #
1272    # update local repository
1273    #
1274    if( ! svn_update() ) {
1275        $return_code = $RETURN_NOK;
1276    }
1277    return $return_code;
1278}
1279
1280#
1281# helper function for "add" command
1282#
1283sub add_helper(@)
1284{
1285    (
1286        my $basename,
1287        my $dirname_prod,
1288        my $dirname_repo,
1289        my $filename_prod,
1290        my $filename_repo
1291    ) = get_filenames( $_[0] );
1292
1293    mkpath($dirname_repo);
1294    copy_file_to_repository($filename_prod);
1295
1296    # already checked in?
1297    chdir $DASSCM_REPO;
1298
1299    # also add the path to filename.
1300    for my $dir ( split( '/', $dirname_prod ) ) {
1301        if ($dir) {
1302            my ( $rc, @out ) = run_command("$SVN add --non-recursive '$dir'");
1303            if ( $rc > 0 ) {
1304                print join( "\n", @out );
1305            }
1306            chdir $dir;
1307        }
1308    }
1309    my ( $rc, @out ) = run_command("$SVN add '$basename'");
1310    if ( $rc > 0 ) {
1311        print join( "\n", @out );
1312    }
1313    chdir $StartDirectory;
1314
1315}
1316
1317sub add_helper_multi(@)
1318{
1319
1320    # get all regular files and links
1321    my $href_files = get_files(@_);
1322
1323    #print Dumper( $href_files );
1324
1325    my @files = @{ $href_files->{files} };
1326    my @links = @{ $href_files->{links} };
1327
1328    # copy files one by one to local repository
1329    for my $file (@files) {
1330
1331        # add file
1332        add_helper($file);
1333    }
1334
1335    return $href_files;
1336}
1337
1338#
1339# adding new files (or directories)
1340#
1341sub add(@)
1342{
1343    check_parameter( @_, 1 );
1344    check_env();
1345
1346    #
1347    # update local repository
1348    #
1349    svn_update();
1350
1351    # add files to repository, print information about added files
1352    print_files_hash( add_helper_multi(@_) );
1353
1354    # perform plugins and add additional files, like plugin results
1355    perform_plugins();
1356    add_helper_multi(@DASSCM_ADDITIONAL_FILES);
1357
1358    if ( $options{'message'} ) {
1359        $svnOptions .= " --message \"$options{'message'}\" ";
1360    }
1361
1362    # commit calls $EDITOR.
1363    # use "interactive" here, to display output
1364    my $retcode = run_interactive(
1365        "$SVN commit $svnOptions --username '$DASSCM_USERNAME' $svnPasswordCredentials $DASSCM_REPO"
1366    );
1367
1368    # svn commit does not deliever an error return code, if commit is canceld,
1369    # so a revert is performed in any case
1370    svn_revert();
1371    return $retcode;
1372}
1373
1374#
1375# checks in all modified files
1376#
1377sub commit(@)
1378{
1379    check_parameter( @_, 1 );
1380    check_env();
1381
1382    (
1383        my $basename,
1384        my $dirname_prod,
1385        my $dirname_repo,
1386        my $filename_prod,
1387        my $filename_repo
1388    ) = get_filenames( $_[0] );
1389
1390    #
1391    # update local repository
1392    #
1393    svn_update();
1394
1395    ( my $refChangedFiles, my $refRemovedFiles ) =
1396      getModifiedFiles($filename_prod);
1397    my %changedfiles = %{$refChangedFiles};
1398    my %removedfiles = %{$refRemovedFiles};
1399
1400    if (%removedfiles) {
1401        my $removedFilesString =
1402          '"' . join( '" "', values(%removedfiles) ) . '"';
1403        my ( $rc, @out ) = run_command("$SVN rm $removedFilesString");
1404        if ( $rc > 0 ) {
1405            print join( "\n", @out );
1406        }
1407    }
1408
1409    # copy files one by one to local repository
1410    for my $file ( keys(%changedfiles) ) {
1411        copy_file_to_repository($file);
1412    }
1413
1414    perform_plugins();
1415    add_helper_multi(@DASSCM_ADDITIONAL_FILES);
1416
1417    if ( $options{'message'} ) {
1418        $svnOptions .= " --message \"$options{'message'}\" ";
1419    }
1420
1421    # commit calls $EDITOR.
1422    # use "interactive" here, to display output
1423    my $retcode = run_interactive(
1424        "$SVN commit $svnOptions --username '$DASSCM_USERNAME' $svnPasswordCredentials $DASSCM_REPO"
1425    );
1426
1427    # svn commit does not deliever an error return code, if commit is canceld,
1428    # so a revert is performed in any case
1429    svn_revert();
1430    return $retcode;
1431}
1432
1433#
1434# revert: copies files back from repository to system
1435#
1436sub revert(@)
1437{
1438    check_parameter( @_, 1 );
1439    check_env();
1440
1441    (
1442        my $basename,
1443        my $dirname_prod,
1444        my $dirname_repo,
1445        my $filename_prod,
1446        my $filename_repo
1447    ) = get_filenames( $_[0] );
1448
1449    # return code for the shell
1450    # default: error
1451    my $return_code = $RETURN_OK;
1452
1453    # cleanup repository
1454    cleanup();
1455
1456    #svn_update();
1457
1458    ( my $refChangedFiles, my $refRemovedFiles, my $refUnknownFiles ) =
1459      getModifiedFiles($filename_prod);
1460    my %changedfiles = %{$refChangedFiles};
1461    my %removedfiles = %{$refRemovedFiles};
1462    my %unknownfiles = %{$refUnknownFiles};
1463
1464    if ( %removedfiles or %changedfiles or %unknownfiles ) {
1465
1466        if (%removedfiles) {
1467            print "DELETED files and directories. Recreated from repository:\n";
1468            my @removedPaths =
1469              ( sort { length $a > length $b } keys %removedfiles );
1470            print join( "\n", @removedPaths ) . "\n\n";
1471
1472            # copy files one by one from local repository to system
1473            # and also create directories
1474            # paths are sorted, so that directories are created first
1475            for my $real_path (@removedPaths) {
1476                if ( -d $removedfiles{"$real_path"} ) {
1477                    mkpath("$real_path");
1478                } else {
1479                    copy_file_from_repository_to_system($real_path);
1480                }
1481            }
1482        }
1483
1484        if (%changedfiles) {
1485            print "MODIFIED files. Copied from repository to the system:\n";
1486            print join( "\n", ( keys %changedfiles ) ) . "\n\n";
1487
1488            # copy files one by one from local repository to system
1489            for my $real_file ( keys(%changedfiles) ) {
1490                copy_file_from_repository_to_system($real_file);
1491            }
1492
1493        }
1494
1495        if (%unknownfiles) {
1496            print "UNKNOWN: insufficient permission to check files:\n";
1497            print join( "\n", ( keys %unknownfiles ) ) . "\n\n";
1498
1499            $return_code = $RETURN_NOK;
1500        }
1501
1502    } else {
1503        print "no modified files found in $dirname_repo\n";
1504    }
1505
1506    return $return_code;
1507}
1508
1509sub blame(@)
1510{
1511    check_parameter( @_, 1 );
1512    check_env();
1513
1514    (
1515        my $basename,
1516        my $dirname_prod,
1517        my $dirname_repo,
1518        my $filename_prod,
1519        my $filename_repo
1520    ) = get_filenames( $_[0] );
1521
1522    my $retcode = run_interactive("$SVN blame --non-interactive $svnCheckoutCredentials $svnOptions $filename_repo");
1523    return $retcode;
1524}
1525
1526sub diff(@)
1527{
1528    check_parameter( @_, 1 );
1529    check_env();
1530
1531    (
1532        my $basename,
1533        my $dirname_prod,
1534        my $dirname_repo,
1535        my $filename_prod,
1536        my $filename_repo
1537    ) = get_filenames( $_[0] );
1538
1539    #print "$basename,$dirname_prod,$dirname_repo\n";
1540
1541    svn_update();
1542
1543    ( my $rc_diff, my @diff_result ) =
1544      run_command( $diff . " $filename_repo $filename_prod" );
1545
1546    print @diff_result;
1547    return $rc_diff;
1548}
1549
1550sub status(@)
1551{
1552    check_parameter( @_, 1 );
1553    check_env();
1554
1555    (
1556        my $basename,
1557        my $dirname_prod,
1558        my $dirname_repo,
1559        my $filename_prod,
1560        my $filename_repo
1561    ) = get_filenames( $_[0] || "/" );
1562
1563    # return code for the shell
1564    # default: error
1565    my $return_code = $RETURN_NOK;
1566
1567    #
1568    # update local repository
1569    #
1570    #svn_update( $filename_prod );
1571
1572    # perform plugins (required to see changes in plugin results)
1573    perform_plugins();
1574
1575    # get modified files
1576    ( my $refChangedFiles, my $refRemovedFiles, my $refUnknownFiles ) =
1577      getModifiedFiles($dirname_prod);
1578    my %changedfiles = %{$refChangedFiles};
1579    my %removedfiles = %{$refRemovedFiles};
1580    my %unknownfiles = %{$refUnknownFiles};
1581
1582    if ( %removedfiles or %changedfiles or %unknownfiles ) {
1583
1584        if (%removedfiles) {
1585            print "DELETED: files found in repository, but not in system:\n";
1586            print join( "\n", sort ( keys %removedfiles ) ) . "\n\n";
1587        }
1588
1589        if (%changedfiles) {
1590            print "MODIFIED: files differs between repository and system:\n";
1591            print join( "\n", ( keys %changedfiles ) ) . "\n\n";
1592        }
1593
1594        if (%unknownfiles) {
1595            print "UNKNOWN: insufficient permission to check files:\n";
1596            print join( "\n", ( keys %unknownfiles ) ) . "\n\n";
1597        }
1598
1599    } else {
1600        print "no modified files found in $dirname_repo\n";
1601        $return_code = $RETURN_OK;
1602    }
1603
1604    return $return_code;
1605}
1606
1607#
1608# return short status in Nagios plugin conform way
1609#
1610sub check()
1611{
1612    check_env();
1613
1614    # return code for the shell
1615    my $return_code   = $RETURN_OK;
1616    my $return_string = "OK: no modified files";
1617
1618    # perform plugins (required to see changes in plugin results)
1619    perform_plugins();
1620
1621    # get modified files
1622    ( my $refChangedFiles, my $refRemovedFiles, my $refUnknownFiles ) =
1623      getModifiedFiles("/");
1624    my %changedfiles = %{$refChangedFiles};
1625    my %removedfiles = %{$refRemovedFiles};
1626    my %unknownfiles = %{$refUnknownFiles};
1627
1628    if ( %removedfiles or %changedfiles ) {
1629        $return_string = "Warning: ";
1630        if (%changedfiles) {
1631            $return_string .=
1632              "changed: " . join( ", ", ( keys %changedfiles ) ) . ". ";
1633        }
1634        if (%removedfiles) {
1635            $return_string .=
1636              "removed: " . join( ", ", ( keys %removedfiles ) ) . ". ";
1637        }
1638        if (%unknownfiles) {
1639            $return_string .=
1640              "unknown: " . join( ", ", ( keys %unknownfiles ) ) . ". ";
1641        }
1642        $return_code = $RETURN_WARN;
1643    }
1644
1645    # addition nagios Service Status
1646    #Critical
1647    #Unknown
1648
1649    print "$return_string\n";
1650    return $return_code;
1651}
1652
1653sub permissions()
1654{
1655    check_env();
1656
1657    my $return_code = $RETURN_OK;
1658
1659    #
1660    # update local repository
1661    #
1662    #svn_update();
1663
1664    my $dir   = $DASSCM_REPO;
1665    my @files = svn_ls("/");
1666
1667    if (@files) {
1668
1669        print "#\n";
1670        print "# created by dasscm permissions\n";
1671        print "# It is intended to be used for restoring permissions\n";
1672        print "#\n";
1673
1674        # generate and print permissions
1675        foreach my $line ( generatePermissionList(@files) ) {
1676            print "$line\n";
1677        }
1678
1679    }
1680
1681    return $return_code;
1682}
1683
1684#
1685# remove all uncommited changes in the repository
1686#
1687sub cleanup()
1688{
1689    my $return_code = $RETURN_OK;
1690
1691    check_env();
1692
1693    svn_revert($DASSCM_REPO);
1694    svn_remove_unknown_files($DASSCM_REPO);
1695
1696    return $return_code;
1697}
1698
1699#
1700# used for bash completion
1701# prints the next possible command line parameters
1702#
1703sub complete(@)
1704{
1705    my @input            = @_;
1706    my %options_complete = ();
1707
1708    my $return_code = $RETURN_OK;
1709
1710    # check and remove global options. if options are wrong, nothing to do
1711    @ARGV = @input;
1712    if ( GetOptions( \%options_complete, @OPTIONS_GLOBAL ) ) {
1713        my $number_arguments = @input;
1714        if ( $number_arguments <= 1 ) {
1715
1716            # complete dasscm commands
1717            my $input = $input[0] || "";
1718            map { m/^$input/ && print $_, "\n" } ( keys %COMMANDS );
1719        } else {
1720
1721            # complete dasscm parameter
1722            my $command = get_command_uniform_name( $input[0] );
1723            if ($command) {
1724
1725                # remove command
1726                shift @input;
1727
1728                # check and remove options
1729                my @options = get_command_possible_options($command);
1730                @ARGV = @input;
1731                if (   ( not @options )
1732                    || ( GetOptions( \%options_complete, @options ) ) )
1733                {
1734
1735                    my @params = get_command_possible_params($command);
1736                    if ($verbose) { print "params: ", Dumper(@params); }
1737
1738                    my $number_arguments = @input;
1739
1740                    #print "input: ", join( ",", @input ), " (", $number_arguments, ")\n";
1741
1742                    if ( $number_arguments > 0 ) {
1743                        my $parameter_number = $number_arguments - 1;
1744                        if ( defined( $params[$parameter_number] )
1745                            && $params[$parameter_number] )
1746                        {
1747                            my $param = $params[$parameter_number];
1748                            if ($verbose) {
1749                                print "param used: ", $param, "\n";
1750                            }
1751                            if ( $param eq "PATH_PROD" ) {
1752                                complete_path(
1753                                    $input[ $number_arguments - 1 ] );
1754                            } elsif ( $param eq "PATH_REPO" ) {
1755                                complete_repopath(
1756                                    $input[ $number_arguments - 1 ] );
1757                            }
1758                        }
1759                    }
1760                }
1761            }
1762        }
1763    }
1764    return $return_code;
1765}
1766
1767sub complete_path(@)
1768{
1769    my $return_code = $RETURN_OK;
1770    check_parameter( @_, 1 );
1771    check_env();
1772
1773    (
1774        my $basename,
1775        my $dirname_prod,
1776        my $dirname_repo,
1777        my $filename_prod,
1778        my $filename_repo
1779    ) = get_filenames( $_[0] );
1780
1781    my @files = get_complete_path_globbing($filename_prod);
1782
1783    if (@files) {
1784        print join( "\n", @files );
1785        print "\n";
1786    }
1787    return $return_code;
1788}
1789
1790sub complete_repopath(@)
1791{
1792    my $return_code = $RETURN_OK;
1793    check_parameter( @_, 1 );
1794    check_env();
1795
1796    (
1797        my $basename,
1798        my $dirname_prod,
1799        my $dirname_repo,
1800        my $filename_prod,
1801        my $filename_repo
1802    ) = get_filenames( $_[0] );
1803
1804    my @files = get_complete_path_globbing($filename_repo);
1805
1806    if (@files) {
1807
1808        # remove DASSCM_REPO path again
1809        print join(
1810            "\n",
1811            map( {
1812                    s|^${DASSCM_REPO}|/|;
1813                      $_
1814                } @files )
1815        );
1816        print "\n";
1817    }
1818    return $return_code;
1819}
1820
1821#####################################################################
1822#
1823# main
1824#
1825
1826my $return_code      = $RETURN_OK;
1827my $number_arguments = @ARGV;
1828
1829# global options
1830# stops at first non-option
1831Getopt::Long::Configure('require_order');
1832if ( not GetOptions( \%options, @OPTIONS_GLOBAL ) ) {
1833    usage();
1834    exit $RETURN_NOK;
1835}
1836
1837# set verbose to command line option
1838$verbose = $options{'verbose'};
1839
1840if ( $options{'help'} ) {
1841    help(@ARGV);
1842    exit;
1843}
1844
1845# get subcommand and remove it from @ARGV
1846if ( defined( $ARGV[0] ) ) {
1847    $command = get_command_uniform_name( $ARGV[0] );
1848    shift @ARGV;
1849}
1850
1851if ( not defined($command) ) {
1852    usage();
1853    exit $RETURN_NOK;
1854}
1855
1856$DASSCM_LOCAL_REPOSITORY_BASE = $config->{'DASSCM_LOCAL_REPOSITORY_BASE'};
1857$DASSCM_REPOSITORY_NAME       = $config->{'DASSCM_REPOSITORY_NAME'};
1858
1859$DASSCM_PLUGIN_RESULTS_PATH =
1860  $config->{'DASSCM_LOCAL_REPOSITORY_BASE'} . "/" . "plugin-results/";
1861
1862# get list of additional directories and files, seperated by blank (" ")
1863# these files are always stored in subversion
1864if ( $config->{'DASSCM_ADDITIONAL_FILES'} ) {
1865    @DASSCM_ADDITIONAL_FILES = split / /, $config->{'DASSCM_ADDITIONAL_FILES'};
1866} else {
1867    @DASSCM_ADDITIONAL_FILES = ( $DASSCM_PLUGIN_RESULTS_PATH );
1868}
1869
1870# TODO: check variables
1871$DASSCM_SVN_REPOSITORY =
1872  $config->{'DASSCM_SVN_REPOSITORY_BASE'} . "/" . $DASSCM_REPOSITORY_NAME;
1873
1874$DASSCM_CHECKOUT_USERNAME = $config->{'DASSCM_CHECKOUT_USERNAME'};
1875$DASSCM_CHECKOUT_PASSWORD = $config->{'DASSCM_CHECKOUT_PASSWORD'};
1876
1877#
1878# if a user is given by dasscm configuration file, we use it.
1879# Otherwise we expect that read-only account is configured
1880# as local subversion configuration.
1881# If this is also not the case,
1882# user is required to type username and password.
1883# This will be stored as local subversion configuration thereafter.
1884#
1885if ( $DASSCM_CHECKOUT_USERNAME && $DASSCM_CHECKOUT_PASSWORD ) {
1886    $svnCheckoutCredentials =
1887      " --username $DASSCM_CHECKOUT_USERNAME --password $DASSCM_CHECKOUT_PASSWORD ";
1888}
1889
1890
1891# check for command options
1892my @cmd_options = get_command_possible_options($command);
1893if (@cmd_options) {
1894
1895    # get command line options and store them in options hash
1896    my $result = GetOptions( \%options, @cmd_options );
1897
1898    # print options
1899    foreach my $option ( keys %options ) {
1900        print "${option}: $options{$option}\n";
1901    }
1902}
1903
1904#
1905# action accordinly to command are taken
1906#
1907$return_code = &{ get_command_function($command) }(@ARGV);
1908
1909exit $return_code;
Note: See TracBrowser for help on using the repository browser.