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

Last change on this file since 893 was 893, checked in by joergs, on Jun 26, 2010 at 2:13:20 PM

better option handling

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