source: dasscm/trunk/usr/bin/dasscm@ 888

Last change on this file since 888 was 888, checked in by joergs, on Jun 26, 2010 at 12:16:44 AM

cleanup

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