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

Last change on this file since 930 was 930, checked in by joergs, on Nov 5, 2010 at 4:42:45 PM

use Term::ReadKey removed. it is not longer required

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