source: trunk/dasscm/dasscm@ 288

Last change on this file since 288 was 288, checked in by joergs, on Mar 10, 2009 at 10:13:10 PM

bugfix: single file ls (or commit) also as filename instead of pathname

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