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

Last change on this file since 895 was 895, checked in by joergs, on Jun 26, 2010 at 3:11:21 PM

completion options handling for sub-commands

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