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

Last change on this file since 1069 was 1069, checked in by joergs, on Aug 17, 2012 at 1:23:16 PM

initial

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