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

Last change on this file since 915 was 915, checked in by joergs, on Jul 16, 2010 at 6:28:10 PM

replaced ReadMode('noecho') by to reduce dependencies

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