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

Last change on this file since 894 was 894, checked in by joergs, on Jun 26, 2010 at 2:40:09 PM

complete handles global options

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