source: dasscm/trunk/dasscm@ 797

Last change on this file since 797 was 797, checked in by joergs, on Sep 17, 2009 at 2:50:23 PM

bugfix: svn credentials at diff

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