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
Line 
1#!/usr/bin/perl -w
2
3# $Id: dasscm 285 2009-03-09 16:25: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# 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 285 2009-03-09 16:25:23Z 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 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
604sub getModifiedFiles( ;$ )
605{
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] );
614
615 my @files = svn_ls($filename_prod);
616
617 # stores result from status (cvscheck)
618 my %removedfiles = ();
619 my %changedfiles = ();
620 my %unknownfiles = ();
621
622 # create list of modified files
623 if (@files) {
624
625 foreach my $file (@files) {
626
627 my $realfile = $dirname_prod . $file;
628 my $cvsworkfile = $dirname_repo . $file;
629
630 if ( -d $realfile ) {
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;
638 } elsif ( !-r $realfile ) {
639 # don't have permission to read the file,
640 # can't check it
641 $unknownfiles{"$realfile"} = $cvsworkfile;
642 } else {
643 ( -r "$cvsworkfile" )
644 || fatalerror("failed to read $cvsworkfile");
645 if ( compare( $cvsworkfile, $realfile ) != 0 ) {
646 $changedfiles{"$realfile"} = $cvsworkfile;
647 }
648 }
649 }
650 }
651
652 return ( \%changedfiles, \%removedfiles, \%unknownfiles );
653}
654
655#
656# from an array of files/dirs,
657# generates list of files
658# sorted by type
659#
660sub get_files( @ )
661{
662 my @files = ();
663 my @links = ();
664 my @dirs = ();
665 my @others = ();
666
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 $_ ) {
679
680 # directories
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 );
693 }
694
695 # don't rely on others.
696 # If more specific file types are needed,
697 # they will be added
698 return {
699 files => \@files,
700 links => \@links,
701 dirs => \@dirs,
702 others => \@others
703 };
704}
705
706#####################################################################
707#
708# functions
709
710sub help(;@)
711{
712 if ( @_ == 0 ) {
713 usage();
714 } else {
715 print "help for @_: ...\n";
716 usage();
717 }
718}
719
720sub login(@)
721{
722 check_parameter( @_, 1 );
723 check_env();
724
725 my $input_username = $_[0];
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);
736
737 $input_username = $input_username || $DASSCM_USERNAME;
738 }
739
740 # hidden password input
741 print "Enter password for $input_username: ";
742 ReadMode('noecho');
743 my $input_password = <STDIN>;
744 ReadMode('normal');
745 chomp($input_password);
746 print "\n";
747
748 # checking checkout username/password
749 svn_check_credentials( $DASSCM_CHECKOUT_USERNAME,
750 $DASSCM_CHECKOUT_PASSWORD );
751 print "checkout access okay\n";
752
753 svn_check_credentials( $input_username, $input_password );
754
755 #
756 # set environment variables
757 #
758 $ENV{'DASSCM_USERNAME'} = "$input_username";
759 $ENV{'DASSCM_PASSWORD'} = "$input_password";
760
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",
764 "Server Repository: $DASSCM_SVN_REPOSITORY\n", "\n";
765
766 status();
767
768 print "\n[dasscm shell]\n\n";
769 my $shell = $SHELL || "bash";
770 exec($shell) or die "failed to start new shell";
771}
772
773#
774# initialize local checkout directory (initial checkout)
775#
776sub init(@)
777{
778 check_parameter( @_, 1 );
779 check_env();
780
781 # don't do repository creation (svn mkdir) here,
782 # because then their must be a lot of prior checks
783
784 # update complete repository
785 # and create permission file
786 my $retcode =
787 run_interactive(
788 "cd $DASSCM_LOCAL_REPOSITORY_BASE; $SVN checkout $svnCheckoutCredentials $svnOptions $DASSCM_SVN_REPOSITORY; mkdir -p `dirname $permissions_file`; touch $permissions_file"
789 );
790}
791
792sub ls(@)
793{
794 check_parameter( @_, 1 );
795 check_env();
796
797 my @files = svn_ls(@_);
798
799 if (@files) {
800 print join( "\n", @files );
801 print "\n";
802 }
803}
804
805sub update(@)
806{
807 check_parameter( @_, 1 );
808 check_env();
809
810 #
811 # update local repository
812 #
813 svn_update();
814}
815
816#
817# helper function for "add" command
818#
819sub add_helper(@)
820{
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] );
829
830 mkpath($dirname_repo);
831
832 # TODO: are permissions also copied?
833 copy( $filename_prod, $filename_repo )
834 or error "failed to copy $filename_prod to repository: $!";
835
836 # already checked in?
837 chdir $DASSCM_REPO;
838
839 # also add the path to filename.
840 for my $dir ( split( '/', $dirname_prod ) ) {
841 if ($dir) {
842 my ( $rc, @out ) = run_command("$SVN add --non-recursive '$dir'");
843 if ( $rc > 0 ) {
844 print join( "\n", @out );
845 }
846 chdir $dir;
847 }
848 }
849 my ( $rc, @out ) = run_command("$SVN add '$basename'");
850 if ( $rc > 0 ) {
851 print join( "\n", @out );
852 }
853 chdir $StartDirectory;
854
855}
856
857#
858# adding new files (or directories)
859#
860sub add(@)
861{
862 check_parameter( @_, 1 );
863 check_env();
864
865 #
866 # update local repository
867 #
868 svn_update();
869
870 # get all regular files and links
871 my $href_files = get_files(@_);
872
873 #print Dumper( $href_files );
874
875 my @files = @{ $href_files->{files} };
876 my @links = @{ $href_files->{links} };
877
878 if (@files) {
879 my $number = $#files + 1;
880 print "files to check-in ($number): \n";
881 print join( "\n", @files );
882 print "\n";
883 }
884
885 # TODO: check in links and also link target? At least warn about link target
886 if (@links) {
887 my $number = $#links + 1;
888 print "\n";
889 print "ignoring links ($number):\n";
890 print join( "\n", @links );
891 print "\n";
892 }
893
894 # TODO: confirm
895
896 # copy files one by one to local repository
897 for my $file (@files) {
898
899 # add file
900 add_helper($file);
901 }
902
903 # create new permissions file
904 permissions();
905
906 # add permissions file
907 add_helper($permissions_file);
908
909 if ( $options{'message'} ) {
910 $svnOptions .= " --message \"$options{'message'}\" ";
911 }
912
913 # commit calls $EDITOR.
914 # use "interactive" here, to display output
915 my $retcode =
916 run_interactive(
917 "$SVN commit $svnOptions --username '$DASSCM_USERNAME' $svnPasswordCredentials $DASSCM_REPO"
918 );
919
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();
923}
924
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
939 )
940 = get_filenames( $_[0] );
941
942 #
943 # update local repository
944 #
945 svn_update();
946
947 ( my $refChangedFiles, my $refRemovedFiles ) =
948 getModifiedFiles($filename_prod);
949 my %changedfiles = %{$refChangedFiles};
950 my %removedfiles = %{$refRemovedFiles};
951
952 if (%removedfiles) {
953 my $removedFilesString =
954 '"' . join( '" "', values(%removedfiles) ) . '"';
955 my ( $rc, @out ) = run_command("$SVN rm $removedFilesString");
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) ) {
963 copy_file_to_repository($file);
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 );
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();
986}
987
988sub blame(@)
989{
990 check_parameter( @_, 1 );
991 check_env();
992
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");
1003}
1004
1005sub diff(@)
1006{
1007 check_parameter( @_, 1 );
1008 check_env();
1009
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
1027 ( my $rc_diff, my @diff_result ) =
1028 run_command( $diff . " $filename_repo $filename_prod" );
1029
1030 print @diff_result;
1031}
1032
1033sub status(@)
1034{
1035 check_parameter( @_, 1 );
1036 check_env();
1037
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
1047 # return code for the shell
1048 # default: error
1049 my $return_code = $RETURN_NOK;
1050
1051 #
1052 # update local repository
1053 #
1054 #svn_update( $filename_prod );
1055
1056 # check, if permissions have changed
1057 permissions();
1058
1059 # get modified files
1060 ( my $refChangedFiles, my $refRemovedFiles, my $refUnknownFiles ) =
1061 getModifiedFiles($dirname_prod);
1062 my %changedfiles = %{$refChangedFiles};
1063 my %removedfiles = %{$refRemovedFiles};
1064 my %unknownfiles = %{$refUnknownFiles};
1065
1066 if ( %removedfiles or %changedfiles or %unknownfiles ) {
1067
1068 if (%removedfiles) {
1069 print "DELETED: files found in repository, but not in system:\n";
1070 print join( "\n", ( keys %removedfiles ) ) . "\n\n";
1071 }
1072
1073 if (%changedfiles) {
1074 print "MODIFIED: files differs between repository and system:\n";
1075 print join( "\n", ( keys %changedfiles ) ) . "\n\n";
1076 }
1077
1078 if (%unknownfiles) {
1079 print "UNKNOWN: insufficient permission to check files:\n";
1080 print join( "\n", ( keys %unknownfiles ) ) . "\n\n";
1081 }
1082
1083 } else {
1084 print "no modified files found in $dirname_repo\n";
1085 $return_code = $RETURN_OK;
1086 }
1087
1088 return $return_code;
1089}
1090
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
1102 # check, if permissions have changed
1103 permissions();
1104
1105 # get modified files
1106 ( my $refChangedFiles, my $refRemovedFiles, my $refUnknownFiles ) =
1107 getModifiedFiles( "/" );
1108 my %changedfiles = %{$refChangedFiles};
1109 my %removedfiles = %{$refRemovedFiles};
1110 my %unknownfiles = %{$refUnknownFiles};
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 }
1120 if (%unknownfiles) {
1121 $return_string .= "unknown: " . join( ", ", ( keys %unknownfiles ) ) . ". ";
1122 }
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
1135sub permissions()
1136{
1137 check_env();
1138
1139 my $return_code = $RETURN_OK;
1140
1141 #
1142 # update local repository
1143 #
1144 #svn_update();
1145
1146 my $dir = $DASSCM_REPO;
1147 my @files = svn_ls("/");
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
1155
1156 if ( -w dirname($permissions_file) ) {
1157
1158 # Verzeichnis existiert => schreiben
1159 open( OUTFILE, ">$permissions_file" )
1160 || die("failed to write to $permissions_file: $!");
1161 $tofile = 1; # Merken, daß in File geschrieben wird
1162 print OUTFILE "#\n";
1163 print OUTFILE "# created by dasscm permissions\n";
1164 print OUTFILE
1165 "# It is intended to be used for restoring permissions\n";
1166 } else {
1167
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 }
1178 }
1179
1180 foreach my $line (@permissions) {
1181 print OUTFILE "$line\n";
1182 }
1183
1184 if ($tofile) {
1185 close(OUTFILE);
1186 }
1187 }
1188
1189 return $return_code;
1190}
1191
1192#
1193# remove all uncommited changes in the repository
1194#
1195sub cleanup()
1196{
1197 check_env();
1198
1199 svn_revert($DASSCM_REPO);
1200 svn_remove_unknown_files($DASSCM_REPO);
1201}
1202
1203#####################################################################
1204#
1205# main
1206#
1207
1208my $return_code = $RETURN_OK;
1209my $number_arguments = @ARGV;
1210
1211if ( $number_arguments > 0 ) {
1212
1213 # get subcommand and remove it from @ARGV
1214 $command = $ARGV[0];
1215 shift @ARGV;
1216
1217 $DASSCM_LOCAL_REPOSITORY_BASE = $config->{'DASSCM_LOCAL_REPOSITORY_BASE'};
1218 $DASSCM_REPOSITORY_NAME = $config->{'DASSCM_REPOSITORY_NAME'};
1219
1220 # TODO: check variables
1221 $DASSCM_SVN_REPOSITORY =
1222 $config->{'DASSCM_SVN_REPOSITORY_BASE'} . "/" . $DASSCM_REPOSITORY_NAME;
1223
1224 $DASSCM_CHECKOUT_USERNAME = $config->{'DASSCM_CHECKOUT_USERNAME'};
1225 $DASSCM_CHECKOUT_PASSWORD = $config->{'DASSCM_CHECKOUT_PASSWORD'};
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
1241 my $result = GetOptions( \%options, 'verbose', 'message=s' );
1242
1243 # print options
1244 foreach my $option ( keys %options ) {
1245 print "${option}: $options{$option}\n";
1246 }
1247
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 #
1256 $_ = $command;
1257 if (m/^help$/i) {
1258 help(@ARGV);
1259 } elsif (m/^login$/i) {
1260 $command = "login";
1261 login(@ARGV);
1262 } elsif (m/^init$/i) {
1263 $command = "init";
1264 init(@ARGV);
1265 } elsif (m/^ls$/i) {
1266 $command = "ls";
1267 ls(@ARGV);
1268 } elsif ( (m/^update$/i) || (m/^up$/i) ) {
1269 $command = "update";
1270 update(@ARGV);
1271 } elsif (m/^add$/i) {
1272 $command = "add";
1273 add(@ARGV);
1274 } elsif ( (m/^commit$/i) || (m/^checkin$/i) || (m/^ci$/i) ) {
1275 $command = "commit";
1276 commit(@ARGV);
1277 } elsif (m/^blame$/i) {
1278 $command = "blame";
1279 blame(@ARGV);
1280 } elsif (m/^diff$/i) {
1281 $command = "diff";
1282 diff(@ARGV);
1283 } elsif ( (m/^status$/i) || (m/^st$/i) ) {
1284 $command = "status";
1285 $return_code = status(@ARGV);
1286 } elsif (m/^check$/i) {
1287 $command = "check";
1288 $return_code = check();
1289 } elsif (m/^permissions$/i) {
1290 $command = "permissions";
1291 $return_code = permissions();
1292 } elsif (m/^cleanup$/i) {
1293 $command = "cleanup";
1294 cleanup();
1295 } else {
1296 print "unknown command: $command\n\n";
1297 usage();
1298 check_env();
1299 $return_code = $RETURN_NOK;
1300 }
1301
1302 # revert
1303
1304}
1305
1306exit $return_code;
Note: See TracBrowser for help on using the repository browser.