source: trunk/dasscm/dasscm@ 285

Last change on this file since 285 was 285, checked in by joergs, on Mar 9, 2009 at 5:25:23 PM

remove unknown files

  • Property keyword set to id
  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 32.6 KB
RevLine 
[186]1#!/usr/bin/perl -w
2
3# $Id: dasscm 285 2009-03-09 16:25:23Z joergs $
4
5use strict;
6
[208]7use Env
[235]8 qw($DASSCM_PROD $DASSCM_REPO $USER $DASSCM_USERNAME $DASSCM_USER $DASSCM_PASSWORD $SHELL);
[186]9use Cwd;
[214]10use Getopt::Long;
[186]11use File::Basename;
[209]12use File::Compare;
13use File::Copy;
[237]14use File::Find;
[186]15use File::stat;
16use File::Path;
[214]17use Term::ReadKey;
[239]18
[234]19#use Data::Dumper;
[186]20
[189]21#####################################################################
22#
[186]23# global
[189]24#
[205]25
[253]26# shell exit codes
[252]27my $RETURN_OK = 0;
28my $RETURN_NOK = 1;
29
[277]30# Nagios return codes
31my $RETURN_WARN = 1;
32my $RETURN_CRIT = 2;
33my $RETURN_UNKNOWN = 3;
34
[215]35# file to store permissions
36my $permissions_file = "/etc/permissions.d/dasscm.permission_backup";
[220]37
[238]38# documentation file (for usage)
39my $doc_file = "/usr/share/doc/packages/dasscm/dasscm_howto.txt";
40
[253]41# commands that require write access (and therefore a login)
42# to the repository server
[274]43my @COMMANDS_REQUIRE_WRITE = ( "add", "commit" );
[252]44
[215]45# configuration file
[205]46my $config_file = "/etc/dasscm.conf";
[234]47my $config = get_config($config_file);
[205]48my $DASSCM_LOCAL_REPOSITORY_BASE;
49my $DASSCM_REPOSITORY_NAME;
50my $DASSCM_SVN_REPOSITORY;
[247]51my $DASSCM_CHECKOUT_USERNAME;
52my $DASSCM_CHECKOUT_PASSWORD;
[205]53
[238]54# current directory at program start
55my $StartDirectory = cwd();
56
[268]57my $diff = "diff --exclude .svn ";
[205]58my $SVN = "svn ";
59my $svnOptions = "";
60my $svnCheckoutCredentials = "";
61my $svnPasswordCredentials = "";
[275]62
[268]63# flag. Set to true by svn_update
64# This prevents, that svn_update is called multiple times
65my $svnRepositoryIsUptodate = 0;
[205]66
[196]67# command line options get stored in options hash
[205]68my %options = ();
69
[197]70# subcommand, that gets executed (add, commit, ...)
[196]71my $command;
[186]72
[205]73my $verbose = 0;
74
[189]75#####################################################################
76#
[186]77# util functions
[189]78#
[187]79sub usage()
80{
[283]81 print '$Id: dasscm 285 2009-03-09 16:25:23Z joergs $';
82 print "\n\n";
[205]83 print "usage: dasscm <subcommand> [options] [args]\n";
84 print "\n";
85 print "dasscm is intended to help versioning configuration files\n";
86 print "\n";
87 print "Available subcommands:\n";
[215]88 print " help <subcommand>\n";
[205]89 print " init\n";
[274]90 print " login <username>\n";
91 print " up <path>\n";
92 print " ls <path>\n";
93 print " add <path>\n";
94 print " commit <path>\n";
95 print " diff <path>\n";
96 print " status <path>\n";
[277]97 print " check\n";
[274]98 print " cleanup\n";
[215]99 print " permissions\n";
[205]100 print "\n";
[238]101 print "preparation:\n", " if dasscm is already configured,\n",
[236]102 " use 'dasscm login' and then eg. 'add'.\n",
[220]103 " The environment variables\n", " DASSCM_REPO\n", " DASSCM_PROD\n",
104 " DASSCM_USERNAME\n", " DASSCM_PASSWORD\n",
105 " are evaluated, but set automatically by 'dasscm login'.\n", "\n",
[239]106 " If dasscm is not yet configured, read", " $doc_file\n";
[187]107}
108
[233]109sub warning(@)
110{
111 print "Warning: " . join( "\n ", @_ ) . "\n";
112}
113
114sub error(@)
115{
116 print "Error: " . join( "\n ", @_ ) . "\n";
117}
118
119sub fatalerror(@)
120{
[239]121 error(@_);
122
[233]123 #print "Exiting\n";
[239]124 exit 1;
[233]125}
126
[238]127#
128# reading config file and return key/value pairs as hash
129#
[234]130sub get_config
131{
[239]132 my $file = $_[0];
[234]133
[239]134 if ( !$file ) {
[234]135 fatalerror( "failed to open config file" . $file );
136 }
137
138 my $data = {};
139
140 # try to open config file
141 if ( !open( FH, $file ) ) {
142 fatalerror( "failed to open config file" . $file );
143 } else {
144 while (<FH>) {
145 chomp;
146 if (/^#/) {
147 next;
148 }
149 if ( $_ =~ /=/g ) {
[239]150
[238]151 # splitting in 2 fields at maximum
[234]152 my ( $option, $value ) = split( /=/, $_, 2 );
153 $option =~ s/^\s+//g;
154 $option =~ s/\s+$//g;
155 $option =~ s/\"+//g;
156 $value =~ s/^\s+//g;
157 $value =~ s/\s+$//g;
158 $value =~ s/\"+//g;
159
160 if ( length($option) ) {
161 $data->{$option} = $value;
162 }
163 }
164 }
165 }
166 close(FH);
167
168 return $data;
169}
170
[270]171#
172# check and evaluate environment variables
173#
[186]174sub check_env()
175{
[205]176
177 # DASSCM_PROD
178 if ( !$DASSCM_PROD ) {
179 $DASSCM_PROD = "/";
180 }
181
182 if ( !-d $DASSCM_PROD ) {
183 die "DASSCM_PROD ($DASSCM_PROD) is not set to a directory.\n";
184 }
185 if ($verbose) { print "DASSCM_PROD: " . $DASSCM_PROD . "\n"; }
186
187 # DASSCM_REPOSITORY_NAME
[208]188 if ( !$DASSCM_REPOSITORY_NAME ) {
189 die
190 "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";
191 }
[205]192
193 # DASSCM_REPO
194 if ( !$DASSCM_REPO ) {
195 if ( $DASSCM_LOCAL_REPOSITORY_BASE && $DASSCM_REPOSITORY_NAME ) {
196 $DASSCM_REPO =
197 $DASSCM_LOCAL_REPOSITORY_BASE . "/" . $DASSCM_REPOSITORY_NAME;
198 } else {
199 die
200 "Envirnonment variable DASSCM_REPO not set.\nSet DASSCM_REPO to the directory of the versioning system checkout for this machine.\n";
201 }
202 }
[215]203 if ($verbose) { print "DASSCM_REPO: " . $DASSCM_REPO . "\n"; }
[205]204
205 #
[248]206 # subversion checkout user
207 #
[252]208 if ( !$DASSCM_CHECKOUT_USERNAME ) {
209 fatalerror(
[248]210 "variable DASSCM_CHECKOUT_USERNAME is not defined.",
[252]211 "Use file $config_file to configure it."
212 );
[248]213 }
214
[252]215 if ( !$DASSCM_CHECKOUT_PASSWORD ) {
216 fatalerror(
[248]217 "variable DASSCM_CHECKOUT_PASSWORD is not defined.",
[252]218 "Use file $config_file to configure it."
219 );
[248]220 }
221
222 #
[239]223 # check if local repository directory exist
[235]224 # (if not creating by init)
[205]225 #
226 if ( $command ne "init" ) {
227 if ( not -d $DASSCM_REPO ) {
228 die
[208]229 "Can't access local repository DASSCM_REPO\n($DASSCM_REPO)\nCheck configuration and execute\n dasscm init\n";
[205]230 }
[208]231
[205]232 #
233 # user settings
234 #
[208]235
[205]236 # DASSCM_USER is legacy. Use DASSCM_USERNAME instead
[208]237 if ( !$DASSCM_USERNAME ) {
238 $DASSCM_USERNAME = $DASSCM_USER;
[205]239 }
240
241 # user root is not allowed for checkins.
242 # if user is root, DASSCM_USER has to be set,
243 # otherwise USER can be used
244 if ( "$USER" eq "root" ) {
[252]245 if ( ( not $DASSCM_USERNAME )
246 and ( grep { m|^$command$| } @COMMANDS_REQUIRE_WRITE ) )
247 {
248
249 #( $command ne "login" ) and ( $command ne "status" ) ) {
250 fatalerror(
251 "Envirnonment variable DASSCM_USERNAME not set.",
252 "Set DASSCM_USERNAME to your subversion user account or",
253 "use 'dasscm login'"
254 );
[205]255 }
256 $svnOptions .= " --no-auth-cache ";
257 } elsif ( !$DASSCM_USERNAME ) {
258 $DASSCM_USERNAME = $USER;
259 }
260
261 #
262 # password
263 #
[208]264 if ($DASSCM_PASSWORD) {
[267]265 $svnPasswordCredentials = " --password '$DASSCM_PASSWORD' ";
[205]266 }
267 }
268
269 #$svnOptions .= " --username $DASSCM_USERNAME "
[186]270}
271
[270]272#
273# has been intendend,
274# to check addtitional parameters.
275# Currently not used.
276#
[186]277sub check_parameter(@)
278{
279}
280
[238]281#
[239]282# generate from (relative) filename
[238]283# all required file and directory names:
284# $basename, $dirname_prod, $dirname_repo,
285# $filename_prod, $filename_repo
286#
[187]287sub get_filenames(@)
288{
[250]289 my $filename_prod = $_[0] || ".";
[237]290
[238]291 # make filename absolut
[205]292 if ( !( $filename_prod =~ m/^\// ) ) {
[270]293 $filename_prod = cwd() . '/' . $filename_prod;
[205]294 }
[187]295
[239]296 if ( not -r $filename_prod ) {
297 fatalerror( $filename_prod . " is not accessable" );
[233]298 }
[205]299
[274]300 # dirname buggy: eg. "/etc/" is reduced to "/",
301 # "/etc" is used as filename
302 # herefore make sure, that if filename is a directory,
303 # it will end by "/"
[275]304 if ( ( -d $filename_prod ) and ( !( $filename_prod =~ m/\/$/ ) ) ) {
[270]305 $filename_prod = $filename_prod . '/';
306 }
[238]307
[270]308 ( my $basename, my $dirname_prod ) = fileparse($filename_prod);
309
[238]310 # uses chdir to determine real directory in a unique way
[205]311 chdir $dirname_prod or die $!;
[270]312 $dirname_prod = cwd() . '/';
[238]313 chdir $StartDirectory;
314
[214]315 if ($verbose) {
316 print "dir: " . $dirname_prod . "\n";
317 print "fn: " . $basename . "\n";
318 }
[205]319
320 my $dirname_repo = $DASSCM_REPO . "/" . $dirname_prod;
321 my $filename_repo = "$dirname_repo/$basename";
322
323 return (
324 $basename, $dirname_prod, $dirname_repo,
325 $filename_prod, $filename_repo
326 );
[187]327}
328
[271]329sub copy_file_to_repository( $ )
330{
331 my $filename = shift;
[256]332
[271]333 (
334 my $basename,
335 my $dirname_prod,
336 my $dirname_repo,
337 my $filename_prod,
338 my $filename_repo
[275]339 )
340 = get_filenames($filename);
[271]341
342 # TODO: are permissions also copied?
343 copy( $filename_prod, $filename_repo )
[275]344 or error "failed to copy $filename_prod to repository: $!";
[271]345}
346
[256]347#
348# creates a file with permissions
349#
[215]350sub generatePermissionList
[209]351{
352
[215]353 # generieren der Zeilen für Permission-Savefile
354 my @files = @_;
355 my @permlist = ();
356 foreach my $file (@files) {
[227]357 $file = "/" . $file;
[239]358 if ( -e $file ) {
359 my $info = stat($file) || die "failed to stat $file: aborting";
360 my $mode = get_type( $info->mode ) & 07777;
[227]361 my $modestring = sprintf( "%04o", $mode );
[278]362 my $uidnumber = $info->uid;
363 my $uid = getpwuid($uidnumber) || $uidnumber;
364 my $gidnumber = $info->gid;
365 my $gid = getgrgid($gidnumber) || $gidnumber;
[227]366 push(
367 @permlist,
368 sprintf( "%-55s %-17s %4d",
[278]369 $file, "${uid}:${gid}", $modestring )
[227]370 );
371 }
[215]372 }
373 return @permlist;
374}
[209]375
[215]376sub get_type
377{
[209]378
[215]379 # Funktion übernommen aus /usr/bin/chkstat
380 my $S_IFLNK = 0120000; # symbolic link
381 my $S_IFREG = 0100000; # regular file
382 my $S_IFDIR = 0040000; # directory
383 my $S_IFCHAR = 0020000; # character device
384 my $S_IFBLK = 0060000; # block device
385 my $S_IFFIFO = 0010000; # fifo
386 my $S_IFSOCK = 0140000; # socket
387 my $S_IFMT = 0170000; # type of file
[209]388
[215]389 my $S_m;
390 if ( ( $_[0] & $S_IFMT ) == $S_IFLNK ) { $S_m = $_[0] - $S_IFLNK; }
391 elsif ( ( $_[0] & $S_IFMT ) == $S_IFREG ) { $S_m = $_[0] - $S_IFREG; }
392 elsif ( ( $_[0] & $S_IFMT ) == $S_IFDIR ) { $S_m = $_[0] - $S_IFDIR; }
393 elsif ( ( $_[0] & $S_IFMT ) == $S_IFCHAR ) { $S_m = $_[0] - $S_IFCHAR; }
394 elsif ( ( $_[0] & $S_IFMT ) == $S_IFBLK ) { $S_m = $_[0] - $S_IFBLK; }
395 elsif ( ( $_[0] & $S_IFMT ) == $S_IFFIFO ) { $S_m = $_[0] - $S_IFFIFO; }
396 elsif ( ( $_[0] & $S_IFMT ) == $S_IFSOCK ) { $S_m = $_[0] - $S_IFSOCK; }
397 $S_m;
[209]398}
399
[186]400sub run_command
401{
[205]402 my $command = shift;
[186]403
[205]404 #print "executing command: " . $command . "\n";
[186]405
[205]406 open( RESULT, $command . ' 2>&1 |' );
407 my @result = <RESULT>;
408 close(RESULT);
409 my $retcode = $? >> 8;
[186]410
[205]411 #print @result;
412 #if( $retcode ) { print "return code: " . $retcode . "\n"; }
[186]413
[205]414 return ( $retcode, @result );
[186]415}
416
[205]417sub run_interactive
418{
[186]419
[208]420 if ($verbose) {
[205]421 print "run_interactive:" . join( " ", @_ ) . "\n";
422 }
[196]423
[205]424 system(@_);
425 if ( $? == -1 ) {
426 printf "failed to execute: $!\n";
427 } elsif ( $? & 127 ) {
428 printf "child died with signal %d, %s coredump\n", ( $? & 127 ),
429 ( $? & 128 ) ? 'with' : 'without';
430 } elsif ( $? >> 8 != 0 ) {
431 printf "child exited with value %d\n", $? >> 8;
432 }
433 return ( $? >> 8 );
434}
435
[247]436sub svn_check_credentials( $$;$$ )
[196]437{
[205]438 my $username = shift;
439 my $password = shift;
440
[252]441 # check silently are allow user interaction?
[247]442 my $interactive = shift || 0;
[220]443
[247]444 # default: exit program, if repository is not accessable
445 # (do not exit for 'init')
446 my $fatalerror = shift || 1;
447
448 print "checking credentials ";
449
[239]450 if ( !$username ) {
451 fatalerror("no username given");
[238]452 }
453
[239]454 if ( !$password ) {
455 fatalerror("no password given");
[238]456 }
457
[247]458 print "for " . $username . "@" . $DASSCM_SVN_REPOSITORY . ": ";
459
[220]460 # Options for "svn info" are not supported by subversion 1.0.0 (SLES9),
461 # therefore switching to "svn status"
462 # ( my $rc_update, my @result ) =
463 # run_command(
464 # "$SVN info --non-interactive --no-auth-cache --username $username --password $password $DASSCM_SVN_REPOSITORY"
465 # );
466 #print @result;
467
[247]468 my $rc_update;
[252]469 if ($interactive) {
470 $rc_update =
471 run_interactive(
[267]472 "$SVN ls --no-auth-cache --username '$username' --password '$password' $DASSCM_SVN_REPOSITORY"
[252]473 );
[247]474 } else {
475 ( $rc_update, my @result ) =
[252]476 run_command(
[267]477 "$SVN ls --non-interactive --no-auth-cache --username '$username' --password '$password' $DASSCM_SVN_REPOSITORY"
[252]478 );
479
[247]480 if ( $rc_update != 0 ) {
481 print "\n", @result;
[252]482 if ($fatalerror) {
[247]483 fatalerror();
484 }
485 return;
486 }
[205]487 }
488
[247]489 # return success
490 return $rc_update == 0;
[196]491}
492
[205]493sub svn_update( ;$ )
494{
[270]495 my $update_path = shift || "";
496
497 # use this flag to do only one update per run
[275]498 if ( !$svnRepositoryIsUptodate ) {
[268]499 ( my $rc_update, my @result ) =
[275]500 run_command(
501 "$SVN update --non-interactive $svnCheckoutCredentials '$DASSCM_REPO/$update_path'"
502 );
[268]503 print @result;
504 if ( $rc_update != 0 ) {
[276]505 error( "failed to update local repository ($update_path)" );
[270]506 } elsif ( not $update_path ) {
[275]507
[270]508 # set this flag if a full update is done
[268]509 $svnRepositoryIsUptodate = 1;
510 }
[205]511 }
[215]512}
[196]513
[271]514sub svn_ls( ;@ )
[215]515{
[270]516 (
517 my $basename,
518 my $dirname_prod,
519 my $dirname_repo,
520 my $filename_prod,
521 my $filename_repo
522 )
523 = get_filenames( $_[0] );
[220]524
[218]525 # svn ls -R is better, but much, much slower
526 # ( my $rc, my @result ) = run_command("$SVN ls --recursive $svnCheckoutCredentials $path");
[270]527
[271]528 my @files = ();
529 my @links = ();
530 my @dirs = ();
531 my @others = ();
532
[275]533 if ( -f $filename_prod ) {
534 @files = ($filename_prod);
[271]535 } elsif ( -d $dirname_repo ) {
536 find(
537 {
538 wanted => sub {
539 my $name = $File::Find::name;
540 $name =~ s|^$dirname_repo||;
541 if ( $name =~ m/\.svn/ ) {
[275]542
[271]543 # skip svn meta data
544 } elsif ( -l $_ ) {
[275]545
[271]546 # soft link
547 # important: check for links first
548 # to exclude them from further checks
549 push( @links, $name );
550 } elsif ( -d $_ ) {
[275]551
[271]552 # directories
553 push( @dirs, $name );
554 } elsif ( -f $_ ) {
[275]555
[271]556 # regular file
557 push( @files, $name );
558 } else {
559 push( @others, $name );
560 }
[275]561 }
[271]562 },
[275]563 ($dirname_repo)
[271]564 );
[215]565 }
[271]566
567 return @files;
[205]568}
569
[274]570sub svn_revert( ;$ )
571{
572 my $path = shift || $DASSCM_REPO;
573
[275]574 ( my $rc_update, my @result ) = run_command("$SVN revert -R '$path'");
[274]575
576 if ( $rc_update != 0 ) {
577 print "\n", @result;
[275]578 error("failed to revert subversion repository changes");
[274]579 }
580}
581
[285]582sub svn_remove_unknown_files( ;$ )
583{
584 my $path = shift || $DASSCM_REPO;
585
586 ( my $rc_update, my @result ) = run_command("$SVN status '$path'" );
587
588 if ( $rc_update != 0 ) {
589 print "\n", @result;
590 error("failed to receive subversion repository information");
591 } else {
592 foreach (@result) {
593 if( s/^\? +// ) {
594 chomp;
595 # if file is unknown to subversion (line starts with "?")
596 # remove it
597 print "removing $_\n";
598 unlink( $_ );
599 }
600 }
601 }
602}
603
[268]604sub getModifiedFiles( ;$ )
605{
[270]606 (
607 my $basename,
608 my $dirname_prod,
609 my $dirname_repo,
610 my $filename_prod,
611 my $filename_repo
612 )
613 = get_filenames( $_[0] );
[268]614
[275]615 my @files = svn_ls($filename_prod);
[270]616
[268]617 # stores result from status (cvscheck)
618 my %removedfiles = ();
619 my %changedfiles = ();
[278]620 my %unknownfiles = ();
[268]621
622 # create list of modified files
623 if (@files) {
624
625 foreach my $file (@files) {
626
[271]627 my $realfile = $dirname_prod . $file;
628 my $cvsworkfile = $dirname_repo . $file;
[268]629
630 if ( -d $realfile ) {
[278]631 # directory
632 if( !-d "$cvsworkfile" ) {
633 # real is directory, repository is not. This is a problem
634 $changedfiles{"$realfile"} = $cvsworkfile;
635 }
636 } elsif ( !-e $realfile ) {
637 $removedfiles{"$realfile"} = $cvsworkfile;
[268]638 } elsif ( !-r $realfile ) {
[278]639 # don't have permission to read the file,
640 # can't check it
641 $unknownfiles{"$realfile"} = $cvsworkfile;
[268]642 } else {
643 ( -r "$cvsworkfile" )
[278]644 || fatalerror("failed to read $cvsworkfile");
[268]645 if ( compare( $cvsworkfile, $realfile ) != 0 ) {
646 $changedfiles{"$realfile"} = $cvsworkfile;
647 }
648 }
649 }
650 }
651
[278]652 return ( \%changedfiles, \%removedfiles, \%unknownfiles );
[268]653}
654
[238]655#
656# from an array of files/dirs,
657# generates list of files
658# sorted by type
659#
[237]660sub get_files( @ )
661{
662 my @files = ();
663 my @links = ();
664 my @dirs = ();
665 my @others = ();
666
[239]667 if (@_) {
668 find(
669 {
670 wanted => sub {
671 my $fullname = cwd() . "/" . $_;
672 if ( -l $_ ) {
673
674 # soft link
675 # important: check for links first
676 # to exclude them from further checks
677 push( @links, $fullname );
678 } elsif ( -d $_ ) {
[271]679
680 # directories
[239]681 push( @dirs, $fullname );
682 } elsif ( -f $_ ) {
683
684 # regular file
685 push( @files, $fullname );
686 } else {
687 push( @others, $fullname );
688 }
689 }
690 },
691 @_
692 );
[237]693 }
694
[239]695 # don't rely on others.
[237]696 # If more specific file types are needed,
697 # they will be added
698 return {
[239]699 files => \@files,
700 links => \@links,
701 dirs => \@dirs,
702 others => \@others
703 };
[237]704}
705
[189]706#####################################################################
707#
[186]708# functions
709
710sub help(;@)
711{
[205]712 if ( @_ == 0 ) {
713 usage();
714 } else {
715 print "help for @_: ...\n";
[214]716 usage();
[205]717 }
[186]718}
719
[203]720sub login(@)
721{
[205]722 check_parameter( @_, 1 );
723 check_env();
[203]724
[235]725 my $input_username = $_[0];
[214]726
727 if ( not $input_username ) {
728 my $output_username = "";
729 if ($DASSCM_USERNAME) {
730 $output_username = " ($DASSCM_USERNAME)";
731 }
732
733 print "Enter DASSCM user name", $output_username, ": ";
734 $input_username = <STDIN>;
735 chomp($input_username);
[247]736
737 $input_username = $input_username || $DASSCM_USERNAME;
[205]738 }
[203]739
[205]740 # hidden password input
[247]741 print "Enter password for $input_username: ";
[205]742 ReadMode('noecho');
743 my $input_password = <STDIN>;
744 ReadMode('normal');
745 chomp($input_password);
[220]746 print "\n";
[203]747
[247]748 # checking checkout username/password
[252]749 svn_check_credentials( $DASSCM_CHECKOUT_USERNAME,
750 $DASSCM_CHECKOUT_PASSWORD );
[247]751 print "checkout access okay\n";
[205]752
[247]753 svn_check_credentials( $input_username, $input_password );
754
[205]755 #
756 # set environment variables
757 #
[267]758 $ENV{'DASSCM_USERNAME'} = "$input_username";
759 $ENV{'DASSCM_PASSWORD'} = "$input_password";
[205]760
[209]761 print "subversion access okay\n\n", "DASSCM_USERNAME: $input_username\n",
762 "DASSCM_PASSWORD: (hidden)\n", "DASSCM_PROD: $DASSCM_PROD\n",
763 "DASSCM_REPO: $DASSCM_REPO\n",
[278]764 "Server Repository: $DASSCM_SVN_REPOSITORY\n", "\n";
[205]765
[278]766 status();
767
768 print "\n[dasscm shell]\n\n";
[235]769 my $shell = $SHELL || "bash";
770 exec($shell) or die "failed to start new shell";
[203]771}
772
[260]773#
774# initialize local checkout directory (initial checkout)
775#
[205]776sub init(@)
777{
778 check_parameter( @_, 1 );
779 check_env();
780
[235]781 # don't do repository creation (svn mkdir) here,
782 # because then their must be a lot of prior checks
783
[205]784 # update complete repository
[216]785 # and create permission file
[208]786 my $retcode =
787 run_interactive(
[252]788 "cd $DASSCM_LOCAL_REPOSITORY_BASE; $SVN checkout $svnCheckoutCredentials $svnOptions $DASSCM_SVN_REPOSITORY; mkdir -p `dirname $permissions_file`; touch $permissions_file"
[208]789 );
[205]790}
791
[215]792sub ls(@)
[186]793{
[205]794 check_parameter( @_, 1 );
795 check_env();
[186]796
[271]797 my @files = svn_ls(@_);
[215]798
[275]799 if (@files) {
[274]800 print join( "\n", @files );
801 print "\n";
802 }
[215]803}
804
805sub update(@)
806{
807 check_parameter( @_, 1 );
808 check_env();
809
810 #
811 # update local repository
812 #
813 svn_update();
814}
815
[237]816#
817# helper function for "add" command
818#
[215]819sub add_helper(@)
820{
[205]821 (
822 my $basename,
823 my $dirname_prod,
824 my $dirname_repo,
825 my $filename_prod,
826 my $filename_repo
827 )
828 = get_filenames( $_[0] );
[186]829
[274]830 mkpath($dirname_repo);
[186]831
[238]832 # TODO: are permissions also copied?
[239]833 copy( $filename_prod, $filename_repo )
834 or error "failed to copy $filename_prod to repository: $!";
[205]835
[274]836 # already checked in?
837 chdir $DASSCM_REPO;
[205]838
[274]839 # also add the path to filename.
840 for my $dir ( split( '/', $dirname_prod ) ) {
841 if ($dir) {
[275]842 my ( $rc, @out ) = run_command("$SVN add --non-recursive '$dir'");
[274]843 if ( $rc > 0 ) {
844 print join( "\n", @out );
[205]845 }
[274]846 chdir $dir;
[205]847 }
848 }
[275]849 my ( $rc, @out ) = run_command("$SVN add '$basename'");
[274]850 if ( $rc > 0 ) {
851 print join( "\n", @out );
852 }
853 chdir $StartDirectory;
854
[215]855}
[205]856
[215]857#
[274]858# adding new files (or directories)
[215]859#
860sub add(@)
861{
862 check_parameter( @_, 1 );
863 check_env();
864
865 #
866 # update local repository
867 #
868 svn_update();
869
[237]870 # get all regular files and links
[239]871 my $href_files = get_files(@_);
[220]872
[237]873 #print Dumper( $href_files );
874
[239]875 my @files = @{ $href_files->{files} };
876 my @links = @{ $href_files->{links} };
[237]877
[239]878 if (@files) {
[237]879 my $number = $#files + 1;
880 print "files to check-in ($number): \n";
881 print join( "\n", @files );
882 print "\n";
883 }
884
[268]885 # TODO: check in links and also link target? At least warn about link target
[239]886 if (@links) {
[237]887 my $number = $#links + 1;
888 print "\n";
889 print "ignoring links ($number):\n";
890 print join( "\n", @links );
891 print "\n";
892 }
893
[238]894 # TODO: confirm
895
[237]896 # copy files one by one to local repository
897 for my $file (@files) {
[239]898
[233]899 # add file
[239]900 add_helper($file);
[233]901 }
902
[215]903 # create new permissions file
904 permissions();
[220]905
[215]906 # add permissions file
[220]907 add_helper($permissions_file);
[215]908
[205]909 if ( $options{'message'} ) {
910 $svnOptions .= " --message \"$options{'message'}\" ";
911 }
912
[239]913 # commit calls $EDITOR.
[237]914 # use "interactive" here, to display output
[215]915 my $retcode =
[205]916 run_interactive(
[267]917 "$SVN commit $svnOptions --username '$DASSCM_USERNAME' $svnPasswordCredentials $DASSCM_REPO"
[208]918 );
[205]919
[274]920 # svn commit does not deliever an error return code, if commit is canceld,
921 # so a revert is performed in any case
922 svn_revert();
[186]923}
924
[271]925#
926# checks in all modified files
927#
928sub commit(@)
929{
930 check_parameter( @_, 1 );
931 check_env();
932
933 (
934 my $basename,
935 my $dirname_prod,
936 my $dirname_repo,
937 my $filename_prod,
938 my $filename_repo
[275]939 )
940 = get_filenames( $_[0] );
[271]941
942 #
943 # update local repository
944 #
945 svn_update();
946
[275]947 ( my $refChangedFiles, my $refRemovedFiles ) =
948 getModifiedFiles($filename_prod);
[271]949 my %changedfiles = %{$refChangedFiles};
950 my %removedfiles = %{$refRemovedFiles};
951
[275]952 if (%removedfiles) {
953 my $removedFilesString =
954 '"' . join( '" "', values(%removedfiles) ) . '"';
955 my ( $rc, @out ) = run_command("$SVN rm $removedFilesString");
[271]956 if ( $rc > 0 ) {
957 print join( "\n", @out );
958 }
959 }
960
961 # copy files one by one to local repository
962 for my $file ( keys(%changedfiles) ) {
[275]963 copy_file_to_repository($file);
[271]964 }
965
966 # create new permissions file
967 permissions();
968
969 # add permissions file
970 add_helper($permissions_file);
971
972 if ( $options{'message'} ) {
973 $svnOptions .= " --message \"$options{'message'}\" ";
974 }
975
976 # commit calls $EDITOR.
977 # use "interactive" here, to display output
978 my $retcode =
979 run_interactive(
980 "$SVN commit $svnOptions --username '$DASSCM_USERNAME' $svnPasswordCredentials $DASSCM_REPO"
981 );
[274]982
983 # svn commit does not deliever an error return code, if commit is canceld,
984 # so a revert is performed in any case
985 svn_revert();
[271]986}
987
[193]988sub blame(@)
989{
[205]990 check_parameter( @_, 1 );
991 check_env();
[193]992
[205]993 (
994 my $basename,
995 my $dirname_prod,
996 my $dirname_repo,
997 my $filename_prod,
998 my $filename_repo
999 )
1000 = get_filenames( $_[0] );
1001
1002 my $retcode = run_interactive("$SVN blame $svnOptions $filename_repo");
[193]1003}
1004
[187]1005sub diff(@)
1006{
[205]1007 check_parameter( @_, 1 );
1008 check_env();
[187]1009
[205]1010 (
1011 my $basename,
1012 my $dirname_prod,
1013 my $dirname_repo,
1014 my $filename_prod,
1015 my $filename_repo
1016 )
1017 = get_filenames( $_[0] );
1018
1019 #print "$basename,$dirname_prod,$dirname_repo\n";
1020
1021 ( my $rc_update, my @result ) = run_command("$SVN update $filename_repo");
1022 if ( $rc_update != 0 ) {
1023 print @result;
1024 die;
1025 }
1026
[238]1027 ( my $rc_diff, my @diff_result ) =
[239]1028 run_command( $diff . " $filename_repo $filename_prod" );
[238]1029
1030 print @diff_result;
[187]1031}
1032
[209]1033sub status(@)
1034{
1035 check_parameter( @_, 1 );
1036 check_env();
1037
[270]1038 (
1039 my $basename,
1040 my $dirname_prod,
1041 my $dirname_repo,
1042 my $filename_prod,
1043 my $filename_repo
1044 )
1045 = get_filenames( $_[0] || "/" );
1046
[252]1047 # return code for the shell
1048 # default: error
1049 my $return_code = $RETURN_NOK;
1050
[209]1051 #
1052 # update local repository
1053 #
[278]1054 #svn_update( $filename_prod );
[209]1055
[278]1056 # check, if permissions have changed
1057 permissions();
1058
1059 # get modified files
1060 ( my $refChangedFiles, my $refRemovedFiles, my $refUnknownFiles ) =
[275]1061 getModifiedFiles($dirname_prod);
[268]1062 my %changedfiles = %{$refChangedFiles};
1063 my %removedfiles = %{$refRemovedFiles};
[278]1064 my %unknownfiles = %{$refUnknownFiles};
[209]1065
[278]1066 if ( %removedfiles or %changedfiles or %unknownfiles ) {
1067
[215]1068 if (%removedfiles) {
[278]1069 print "DELETED: files found in repository, but not in system:\n";
1070 print join( "\n", ( keys %removedfiles ) ) . "\n\n";
[209]1071 }
1072
[215]1073 if (%changedfiles) {
[278]1074 print "MODIFIED: files differs between repository and system:\n";
1075 print join( "\n", ( keys %changedfiles ) ) . "\n\n";
[209]1076 }
[278]1077
1078 if (%unknownfiles) {
1079 print "UNKNOWN: insufficient permission to check files:\n";
1080 print join( "\n", ( keys %unknownfiles ) ) . "\n\n";
1081 }
1082
[209]1083 } else {
[270]1084 print "no modified files found in $dirname_repo\n";
[252]1085 $return_code = $RETURN_OK;
[209]1086 }
[215]1087
[252]1088 return $return_code;
[215]1089}
[209]1090
[277]1091#
1092# return short status in Nagios plugin conform way
1093#
1094sub check()
1095{
1096 check_env();
1097
1098 # return code for the shell
1099 my $return_code = $RETURN_OK;
1100 my $return_string = "OK: no modified files";
1101
[278]1102 # check, if permissions have changed
1103 permissions();
1104
1105 # get modified files
1106 ( my $refChangedFiles, my $refRemovedFiles, my $refUnknownFiles ) =
[277]1107 getModifiedFiles( "/" );
1108 my %changedfiles = %{$refChangedFiles};
1109 my %removedfiles = %{$refRemovedFiles};
[278]1110 my %unknownfiles = %{$refUnknownFiles};
[277]1111
1112 if ( %removedfiles or %changedfiles ) {
1113 $return_string = "Warning: ";
1114 if( %changedfiles ) {
1115 $return_string .= "changed: " . join( ", ", ( keys %changedfiles ) ) . ". ";
1116 }
1117 if( %removedfiles ) {
1118 $return_string .= "removed: " . join( ", ", ( keys %removedfiles ) ) . ". ";
1119 }
[278]1120 if (%unknownfiles) {
1121 $return_string .= "unknown: " . join( ", ", ( keys %unknownfiles ) ) . ". ";
1122 }
[277]1123 $return_code = $RETURN_WARN;
1124 }
1125
1126 # addition nagios Service Status
1127 #Critical
1128 #Unknown
1129
1130 print $return_string . "\n";
1131 return $return_code;
1132}
1133
1134
[270]1135sub permissions()
[215]1136{
1137 check_env();
1138
[278]1139 my $return_code = $RETURN_OK;
1140
[215]1141 #
1142 # update local repository
1143 #
1144 #svn_update();
1145
[220]1146 my $dir = $DASSCM_REPO;
[275]1147 my @files = svn_ls("/");
[215]1148
1149 if (@files) {
1150
1151 # generieren der Permissions
1152 my @permissions = generatePermissionList(@files);
1153 my $OUTFILE;
1154 my $tofile = 0; # Status für schreiben in File
[220]1155
[215]1156 if ( -w dirname($permissions_file) ) {
1157
1158 # Verzeichnis existiert => schreiben
1159 open( OUTFILE, ">$permissions_file" )
[216]1160 || die("failed to write to $permissions_file: $!");
[215]1161 $tofile = 1; # Merken, daß in File geschrieben wird
1162 print OUTFILE "#\n";
1163 print OUTFILE "# created by dasscm permissions\n";
[220]1164 print OUTFILE
1165 "# It is intended to be used for restoring permissions\n";
[215]1166 } else {
1167
[278]1168 if( $command eq "permission" ) {
1169 # Pfad für Sicherungsdatei existiert nicht => schreiben auf stdout
1170 # Alias Filehandle für stdout erzeugen
1171 $return_code = $RETURN_WARN;
1172 *OUTFILE = *STDOUT;
1173 } else {
1174 # TODO: improve this. Check for diff?
1175 $return_code = $RETURN_CRIT;
1176 return $return_code;
1177 }
[215]1178 }
[278]1179
[215]1180 foreach my $line (@permissions) {
1181 print OUTFILE "$line\n";
1182 }
1183
[220]1184 if ($tofile) {
[215]1185 close(OUTFILE);
1186 }
1187 }
[278]1188
1189 return $return_code;
[209]1190}
1191
[274]1192#
1193# remove all uncommited changes in the repository
1194#
1195sub cleanup()
1196{
1197 check_env();
[268]1198
[275]1199 svn_revert($DASSCM_REPO);
[285]1200 svn_remove_unknown_files($DASSCM_REPO);
[274]1201}
1202
[189]1203#####################################################################
1204#
[186]1205# main
[189]1206#
[186]1207
[252]1208my $return_code = $RETURN_OK;
[186]1209my $number_arguments = @ARGV;
1210
[205]1211if ( $number_arguments > 0 ) {
[186]1212
[205]1213 # get subcommand and remove it from @ARGV
1214 $command = $ARGV[0];
1215 shift @ARGV;
[196]1216
[205]1217 $DASSCM_LOCAL_REPOSITORY_BASE = $config->{'DASSCM_LOCAL_REPOSITORY_BASE'};
1218 $DASSCM_REPOSITORY_NAME = $config->{'DASSCM_REPOSITORY_NAME'};
[196]1219
[205]1220 # TODO: check variables
1221 $DASSCM_SVN_REPOSITORY =
1222 $config->{'DASSCM_SVN_REPOSITORY_BASE'} . "/" . $DASSCM_REPOSITORY_NAME;
1223
[247]1224 $DASSCM_CHECKOUT_USERNAME = $config->{'DASSCM_CHECKOUT_USERNAME'};
1225 $DASSCM_CHECKOUT_PASSWORD = $config->{'DASSCM_CHECKOUT_PASSWORD'};
[205]1226
1227 #
1228 # if a user is given by dasscm configuration file, we use it.
1229 # Otherwise we expect that read-only account is configured
1230 # as local subversion configuration.
1231 # If this is also not the case,
1232 # user is required to type username and password.
1233 # This will be stored as local subversion configuration thereafter.
1234 #
1235 if ( $DASSCM_CHECKOUT_USERNAME && $DASSCM_CHECKOUT_PASSWORD ) {
1236 $svnCheckoutCredentials =
1237 " --username $DASSCM_CHECKOUT_USERNAME --password $DASSCM_CHECKOUT_PASSWORD ";
1238 }
1239
1240 # get command line options and store them in options hash
[214]1241 my $result = GetOptions( \%options, 'verbose', 'message=s' );
[205]1242
1243 # print options
1244 foreach my $option ( keys %options ) {
[215]1245 print "${option}: $options{$option}\n";
[205]1246 }
1247
[214]1248 # set verbose to command line option
1249 $verbose = $options{'verbose'};
1250
1251 #
1252 # action accordinly to command are taken
1253 # $command is rewritten in standard format,
1254 # so we can test for it later on more simply
1255 #
[205]1256 $_ = $command;
[274]1257 if (m/^help$/i) {
[205]1258 help(@ARGV);
[274]1259 } elsif (m/^login$/i) {
[208]1260 $command = "login";
[205]1261 login(@ARGV);
[274]1262 } elsif (m/^init$/i) {
[208]1263 $command = "init";
[205]1264 init(@ARGV);
[274]1265 } elsif (m/^ls$/i) {
[215]1266 $command = "ls";
1267 ls(@ARGV);
[274]1268 } elsif ( (m/^update$/i) || (m/^up$/i) ) {
[215]1269 $command = "update";
1270 update(@ARGV);
[274]1271 } elsif (m/^add$/i) {
[205]1272 $command = "add";
1273 add(@ARGV);
[274]1274 } elsif ( (m/^commit$/i) || (m/^checkin$/i) || (m/^ci$/i) ) {
[205]1275 $command = "commit";
[271]1276 commit(@ARGV);
[274]1277 } elsif (m/^blame$/i) {
[208]1278 $command = "blame";
[205]1279 blame(@ARGV);
[274]1280 } elsif (m/^diff$/i) {
[208]1281 $command = "diff";
[205]1282 diff(@ARGV);
[274]1283 } elsif ( (m/^status$/i) || (m/^st$/i) ) {
[252]1284 $command = "status";
1285 $return_code = status(@ARGV);
[277]1286 } elsif (m/^check$/i) {
1287 $command = "check";
1288 $return_code = check();
[274]1289 } elsif (m/^permissions$/i) {
[215]1290 $command = "permissions";
[278]1291 $return_code = permissions();
[274]1292 } elsif (m/^cleanup$/i) {
1293 $command = "cleanup";
1294 cleanup();
[205]1295 } else {
[215]1296 print "unknown command: $command\n\n";
[205]1297 usage();
1298 check_env();
[252]1299 $return_code = $RETURN_NOK;
[205]1300 }
1301
1302 # revert
[252]1303
[186]1304}
[252]1305
1306exit $return_code;
Note: See TracBrowser for help on using the repository browser.