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

Last change on this file since 944 was 944, checked in by joergs, on Feb 22, 2011 at 7:42:38 PM

bugfix: LANG=C for executing commands

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