source: trunk/dasscm/dasscm@ 284

Last change on this file since 284 was 284, checked in by joergs, on Mar 9, 2009 at 4:34:15 PM

removed unnecessary print for files only in repository

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