source: trunk/dasscm/dasscm@ 282

Last change on this file since 282 was 278, checked in by joergs, on Mar 6, 2009 at 10:38:05 PM

login + status, status + permission. Handle Unknown (unreadable) files. BUGFIX: uid and gid numbers, if no names are available

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