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

Last change on this file since 887 was 887, checked in by joergs, on Jun 26, 2010 at 12:03:52 AM

added function for bash completion and did code cleanup

  • Property keyword set to id
  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 42.2 KB
Line 
1#!/usr/bin/perl -w
2
3# $Id: dasscm 887 2010-06-25 22:03:52Z 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 887 2010-06-25 22:03:52Z 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# functions
960sub help(;@)
961{
962 if ( @_ == 0 ) {
963 usage();
964 } else {
965 print "help for @_: ...\n";
966 usage();
967 }
968}
969
970sub login(@)
971{
972 check_parameter( @_, 1 );
973 check_env();
974
975 my $input_username = $_[0];
976
977 if ( not $input_username ) {
978 my $output_username = "";
979 if ($DASSCM_USERNAME) {
980 $output_username = " ($DASSCM_USERNAME)";
981 }
982
983 print "Enter DASSCM user name", $output_username, ": ";
984 $input_username = <STDIN>;
985 chomp($input_username);
986
987 $input_username = $input_username || $DASSCM_USERNAME;
988 }
989
990 # hidden password input
991 print "Enter password for $input_username: ";
992 ReadMode('noecho');
993 my $input_password = <STDIN>;
994 ReadMode('normal');
995 chomp($input_password);
996 print "\n";
997
998 # checking checkout username/password
999 svn_check_credentials( $DASSCM_CHECKOUT_USERNAME,
1000 $DASSCM_CHECKOUT_PASSWORD );
1001 print "checkout access okay\n";
1002
1003 svn_check_credentials( $input_username, $input_password );
1004
1005 #
1006 # set environment variables
1007 #
1008 $ENV{'DASSCM_USERNAME'} = "$input_username";
1009 $ENV{'DASSCM_PASSWORD'} = "$input_password";
1010
1011 print "subversion access okay\n\n", "DASSCM_USERNAME: $input_username\n",
1012 "DASSCM_PASSWORD: (hidden)\n", "DASSCM_PROD: $DASSCM_PROD\n",
1013 "DASSCM_REPO: $DASSCM_REPO\n",
1014 "Server Repository: $DASSCM_SVN_REPOSITORY\n", "\n";
1015
1016 status();
1017
1018 print "\n[dasscm shell]\n\n";
1019 my $shell = $SHELL || "bash";
1020 exec($shell) or die "failed to start new shell";
1021}
1022
1023#
1024# initialize local checkout directory (initial checkout)
1025#
1026sub init(@)
1027{
1028 check_parameter( @_, 1 );
1029 check_env();
1030
1031 # don't do repository creation (svn mkdir) here,
1032 # because then their must be a lot of prior checks
1033
1034 # update complete repository
1035 # and create permission file
1036 my $retcode = run_interactive(
1037 "cd $DASSCM_LOCAL_REPOSITORY_BASE; $SVN checkout $svnCheckoutCredentials $svnOptions $DASSCM_SVN_REPOSITORY; mkdir -p `dirname $DASSCM_PERMISSION_FILE`; touch $DASSCM_PERMISSION_FILE"
1038 );
1039}
1040
1041sub ls(@)
1042{
1043 check_parameter( @_, 1 );
1044 check_env();
1045
1046 my @files = svn_ls(@_);
1047
1048 if (@files) {
1049 print join( "\n", @files );
1050 print "\n";
1051 }
1052}
1053
1054sub update(@)
1055{
1056 check_parameter( @_, 1 );
1057 check_env();
1058
1059 #
1060 # update local repository
1061 #
1062 svn_update();
1063}
1064
1065#
1066# helper function for "add" command
1067#
1068sub add_helper(@)
1069{
1070 (
1071 my $basename,
1072 my $dirname_prod,
1073 my $dirname_repo,
1074 my $filename_prod,
1075 my $filename_repo
1076 ) = get_filenames( $_[0] );
1077
1078 mkpath($dirname_repo);
1079 copy_file_to_repository( $filename_prod );
1080
1081 # already checked in?
1082 chdir $DASSCM_REPO;
1083
1084 # also add the path to filename.
1085 for my $dir ( split( '/', $dirname_prod ) ) {
1086 if ($dir) {
1087 my ( $rc, @out ) = run_command("$SVN add --non-recursive '$dir'");
1088 if ( $rc > 0 ) {
1089 print join( "\n", @out );
1090 }
1091 chdir $dir;
1092 }
1093 }
1094 my ( $rc, @out ) = run_command("$SVN add '$basename'");
1095 if ( $rc > 0 ) {
1096 print join( "\n", @out );
1097 }
1098 chdir $StartDirectory;
1099
1100}
1101
1102#
1103# adding new files (or directories)
1104#
1105sub add(@)
1106{
1107 check_parameter( @_, 1 );
1108 check_env();
1109
1110 #
1111 # update local repository
1112 #
1113 svn_update();
1114
1115 # get all regular files and links
1116 my $href_files = get_files(@_);
1117
1118 #print Dumper( $href_files );
1119
1120 my @files = @{ $href_files->{files} };
1121 my @links = @{ $href_files->{links} };
1122
1123 if (@files) {
1124 my $number = $#files + 1;
1125 print "files to check-in ($number): \n";
1126 print join( "\n", @files );
1127 print "\n";
1128 }
1129
1130 # TODO: check in links and also link target? At least warn about link target
1131 if (@links) {
1132 my $number = $#links + 1;
1133 print "\n";
1134 print "ignoring links ($number):\n";
1135 print join( "\n", @links );
1136 print "\n";
1137 }
1138
1139 # TODO: confirm
1140
1141 # copy files one by one to local repository
1142 for my $file (@files) {
1143
1144 # add file
1145 add_helper($file);
1146 }
1147
1148 # create new permissions file
1149 permissions();
1150
1151 # add permissions file
1152 add_helper($DASSCM_PERMISSION_FILE);
1153
1154 if ( $options{'message'} ) {
1155 $svnOptions .= " --message \"$options{'message'}\" ";
1156 }
1157
1158 # commit calls $EDITOR.
1159 # use "interactive" here, to display output
1160 my $retcode = run_interactive(
1161 "$SVN commit $svnOptions --username '$DASSCM_USERNAME' $svnPasswordCredentials $DASSCM_REPO"
1162 );
1163
1164 # svn commit does not deliever an error return code, if commit is canceld,
1165 # so a revert is performed in any case
1166 svn_revert();
1167}
1168
1169#
1170# checks in all modified files
1171#
1172sub commit(@)
1173{
1174 check_parameter( @_, 1 );
1175 check_env();
1176
1177 (
1178 my $basename,
1179 my $dirname_prod,
1180 my $dirname_repo,
1181 my $filename_prod,
1182 my $filename_repo
1183 ) = get_filenames( $_[0] );
1184
1185 #
1186 # update local repository
1187 #
1188 svn_update();
1189
1190 ( my $refChangedFiles, my $refRemovedFiles ) =
1191 getModifiedFiles($filename_prod);
1192 my %changedfiles = %{$refChangedFiles};
1193 my %removedfiles = %{$refRemovedFiles};
1194
1195 if (%removedfiles) {
1196 my $removedFilesString =
1197 '"' . join( '" "', values(%removedfiles) ) . '"';
1198 my ( $rc, @out ) = run_command("$SVN rm $removedFilesString");
1199 if ( $rc > 0 ) {
1200 print join( "\n", @out );
1201 }
1202 }
1203
1204 # copy files one by one to local repository
1205 for my $file ( keys(%changedfiles) ) {
1206 copy_file_to_repository($file);
1207 }
1208
1209 # create new permissions file
1210 permissions();
1211
1212 # add permissions file
1213 add_helper($DASSCM_PERMISSION_FILE);
1214
1215 if ( $options{'message'} ) {
1216 $svnOptions .= " --message \"$options{'message'}\" ";
1217 }
1218
1219 # commit calls $EDITOR.
1220 # use "interactive" here, to display output
1221 my $retcode = run_interactive(
1222 "$SVN commit $svnOptions --username '$DASSCM_USERNAME' $svnPasswordCredentials $DASSCM_REPO"
1223 );
1224
1225 # svn commit does not deliever an error return code, if commit is canceld,
1226 # so a revert is performed in any case
1227 svn_revert();
1228}
1229
1230#
1231# revert: copies files back from repository to system
1232#
1233sub revert(@)
1234{
1235 check_parameter( @_, 1 );
1236 check_env();
1237
1238 (
1239 my $basename,
1240 my $dirname_prod,
1241 my $dirname_repo,
1242 my $filename_prod,
1243 my $filename_repo
1244 ) = get_filenames( $_[0] );
1245
1246 # return code for the shell
1247 # default: error
1248 my $return_code = $RETURN_OK;
1249
1250 # cleanup repository
1251 cleanup();
1252 #svn_update();
1253
1254 ( my $refChangedFiles, my $refRemovedFiles, my $refUnknownFiles ) =
1255 getModifiedFiles($filename_prod);
1256 my %changedfiles = %{$refChangedFiles};
1257 my %removedfiles = %{$refRemovedFiles};
1258 my %unknownfiles = %{$refUnknownFiles};
1259
1260 if ( %removedfiles or %changedfiles or %unknownfiles ) {
1261
1262 if (%removedfiles) {
1263 print "DELETED files and directories. Recreated from repository:\n";
1264 my @removedPaths =
1265 ( sort { length $a > length $b } keys %removedfiles );
1266 print join( "\n", @removedPaths ) . "\n\n";
1267
1268 # copy files one by one from local repository to system
1269 # and also create directories
1270 # paths are sorted, so that directories are created first
1271 for my $real_path (@removedPaths) {
1272 if ( -d $removedfiles{"$real_path"} ) {
1273 mkpath("$real_path");
1274 } else {
1275 copy_file_from_repository_to_system( $real_path );
1276 }
1277 }
1278 }
1279
1280 if (%changedfiles) {
1281 print "MODIFIED files. Copied from repository to the system:\n";
1282 print join( "\n", ( keys %changedfiles ) ) . "\n\n";
1283
1284 # copy files one by one from local repository to system
1285 for my $real_file ( keys(%changedfiles) ) {
1286 copy_file_from_repository_to_system( $real_file );
1287 }
1288
1289 }
1290
1291 if (%unknownfiles) {
1292 print "UNKNOWN: insufficient permission to check files:\n";
1293 print join( "\n", ( keys %unknownfiles ) ) . "\n\n";
1294
1295 $return_code = $RETURN_NOK;
1296 }
1297
1298 } else {
1299 print "no modified files found in $dirname_repo\n";
1300 }
1301
1302 return $return_code;
1303}
1304
1305sub blame(@)
1306{
1307 check_parameter( @_, 1 );
1308 check_env();
1309
1310 (
1311 my $basename,
1312 my $dirname_prod,
1313 my $dirname_repo,
1314 my $filename_prod,
1315 my $filename_repo
1316 ) = get_filenames( $_[0] );
1317
1318 my $retcode = run_interactive("$SVN blame $svnOptions $filename_repo");
1319}
1320
1321sub diff(@)
1322{
1323 check_parameter( @_, 1 );
1324 check_env();
1325
1326 (
1327 my $basename,
1328 my $dirname_prod,
1329 my $dirname_repo,
1330 my $filename_prod,
1331 my $filename_repo
1332 ) = get_filenames( $_[0] );
1333
1334 #print "$basename,$dirname_prod,$dirname_repo\n";
1335
1336 svn_update();
1337
1338 ( my $rc_diff, my @diff_result ) =
1339 run_command( $diff . " $filename_repo $filename_prod" );
1340
1341 print @diff_result;
1342}
1343
1344sub status(@)
1345{
1346 check_parameter( @_, 1 );
1347 check_env();
1348
1349 (
1350 my $basename,
1351 my $dirname_prod,
1352 my $dirname_repo,
1353 my $filename_prod,
1354 my $filename_repo
1355 ) = get_filenames( $_[0] || "/" );
1356
1357 # return code for the shell
1358 # default: error
1359 my $return_code = $RETURN_NOK;
1360
1361 #
1362 # update local repository
1363 #
1364 #svn_update( $filename_prod );
1365
1366 # check, if permissions have changed
1367 permissions();
1368
1369 # get modified files
1370 ( my $refChangedFiles, my $refRemovedFiles, my $refUnknownFiles ) =
1371 getModifiedFiles($dirname_prod);
1372 my %changedfiles = %{$refChangedFiles};
1373 my %removedfiles = %{$refRemovedFiles};
1374 my %unknownfiles = %{$refUnknownFiles};
1375
1376 if ( %removedfiles or %changedfiles or %unknownfiles ) {
1377
1378 if (%removedfiles) {
1379 print "DELETED: files found in repository, but not in system:\n";
1380 print join( "\n", sort ( keys %removedfiles ) ) . "\n\n";
1381 }
1382
1383 if (%changedfiles) {
1384 print "MODIFIED: files differs between repository and system:\n";
1385 print join( "\n", ( keys %changedfiles ) ) . "\n\n";
1386 }
1387
1388 if (%unknownfiles) {
1389 print "UNKNOWN: insufficient permission to check files:\n";
1390 print join( "\n", ( keys %unknownfiles ) ) . "\n\n";
1391 }
1392
1393 } else {
1394 print "no modified files found in $dirname_repo\n";
1395 $return_code = $RETURN_OK;
1396 }
1397
1398 return $return_code;
1399}
1400
1401#
1402# return short status in Nagios plugin conform way
1403#
1404sub check()
1405{
1406 check_env();
1407
1408 # return code for the shell
1409 my $return_code = $RETURN_OK;
1410 my $return_string = "OK: no modified files";
1411
1412 # check, if permissions have changed
1413 permissions();
1414
1415 # get modified files
1416 ( my $refChangedFiles, my $refRemovedFiles, my $refUnknownFiles ) =
1417 getModifiedFiles("/");
1418 my %changedfiles = %{$refChangedFiles};
1419 my %removedfiles = %{$refRemovedFiles};
1420 my %unknownfiles = %{$refUnknownFiles};
1421
1422 if ( %removedfiles or %changedfiles ) {
1423 $return_string = "Warning: ";
1424 if (%changedfiles) {
1425 $return_string .=
1426 "changed: " . join( ", ", ( keys %changedfiles ) ) . ". ";
1427 }
1428 if (%removedfiles) {
1429 $return_string .=
1430 "removed: " . join( ", ", ( keys %removedfiles ) ) . ". ";
1431 }
1432 if (%unknownfiles) {
1433 $return_string .=
1434 "unknown: " . join( ", ", ( keys %unknownfiles ) ) . ". ";
1435 }
1436 $return_code = $RETURN_WARN;
1437 }
1438
1439 # addition nagios Service Status
1440 #Critical
1441 #Unknown
1442
1443 print "$return_string\n";
1444 return $return_code;
1445}
1446
1447sub permissions()
1448{
1449 check_env();
1450
1451 my $return_code = $RETURN_OK;
1452
1453 #
1454 # update local repository
1455 #
1456 #svn_update();
1457
1458 my $dir = $DASSCM_REPO;
1459 my @files = svn_ls("/");
1460
1461 if (@files) {
1462
1463 # generieren der Permissions
1464 my @permissions = generatePermissionList(@files);
1465 my $OUTFILE;
1466 my $tofile = 0; # Status für schreiben in File
1467
1468 if ( -w dirname($DASSCM_PERMISSION_FILE) ) {
1469
1470 # Verzeichnis existiert => schreiben
1471 open( OUTFILE, ">$DASSCM_PERMISSION_FILE" )
1472 || die("failed to write to $DASSCM_PERMISSION_FILE: $!");
1473 $tofile = 1; # Merken, daß in File geschrieben wird
1474 print OUTFILE "#\n";
1475 print OUTFILE "# created by dasscm permissions\n";
1476 print OUTFILE
1477 "# It is intended to be used for restoring permissions\n";
1478 print OUTFILE "#\n";
1479 } else {
1480
1481 if ( $command eq "permission" ) {
1482
1483 # Pfad für Sicherungsdatei existiert nicht => schreiben auf stdout
1484 # Alias Filehandle für stdout erzeugen
1485 $return_code = $RETURN_WARN;
1486 *OUTFILE = *STDOUT;
1487 } else {
1488
1489 # TODO: improve this. Check for diff?
1490 $return_code = $RETURN_CRIT;
1491 return $return_code;
1492 }
1493 }
1494
1495 foreach my $line (@permissions) {
1496 print OUTFILE "$line\n";
1497 }
1498
1499 if ($tofile) {
1500 close(OUTFILE);
1501 }
1502 }
1503
1504 return $return_code;
1505}
1506
1507#
1508# remove all uncommited changes in the repository
1509#
1510sub cleanup()
1511{
1512 check_env();
1513
1514 svn_revert($DASSCM_REPO);
1515 svn_remove_unknown_files($DASSCM_REPO);
1516}
1517
1518#
1519# used for bash completion
1520# prints the next possible command line parameters
1521#
1522sub complete(@)
1523{
1524 use Data::Dumper;
1525
1526 my $number_arguments = @_;
1527 my @input = @_;
1528
1529
1530 if ( $number_arguments <= 1 ) {
1531
1532 # complete dasscm commands
1533 my $input = $input[0] || "";
1534 foreach my $i ( keys %COMMANDS ) {
1535 $_ = $i;
1536 if( m/^$input/ ) {
1537 my $command = get_command_uniform_name($i);
1538 print $command, "\n";
1539 }
1540 }
1541 } else {
1542
1543 # complete dasscm parameter
1544 my $command = get_command_uniform_name($input[0]);
1545 my @params = get_command_possible_params($input[0]);
1546 if( $verbose ) { print "params: ", Dumper(@params); }
1547 if( defined($params[$number_arguments-2]) && $params[$number_arguments-2] ) {
1548 my $param = $params[$number_arguments-2];
1549 if( $param eq "PATH" ) {
1550 complete_path( $input[$number_arguments-1] );
1551 } elsif ( $param eq "REPOPATH" ) {
1552 complete_repopath( $input[$number_arguments-1] );
1553 } else {
1554 print "param: $param\n";
1555 print Dumper($param);
1556 }
1557 }
1558 }
1559}
1560
1561
1562
1563sub complete_path(@)
1564{
1565 check_parameter( @_, 1 );
1566 check_env();
1567
1568 (
1569 my $basename,
1570 my $dirname_prod,
1571 my $dirname_repo,
1572 my $filename_prod,
1573 my $filename_repo
1574 ) = get_filenames( $_[0] );
1575
1576 my $path = $filename_prod . "*";
1577
1578 my @files = glob( $path );
1579
1580 # if only one result is available
1581 # and this result is a directory,
1582 # add another result entry
1583 # (otherwise complete will stop here and continue with the next parameter)
1584 if( $#files == 0 ) {
1585 my $path = $files[0];
1586 if( -d $path ) {
1587 push( @files, $path . "/" );
1588 }
1589 }
1590
1591 if (@files) {
1592 print join( "\n", @files );
1593 print "\n";
1594 }
1595}
1596
1597
1598
1599sub complete_repopath(@)
1600{
1601 check_parameter( @_, 1 );
1602 check_env();
1603
1604 (
1605 my $basename,
1606 my $dirname_prod,
1607 my $dirname_repo,
1608 my $filename_prod,
1609 my $filename_repo
1610 ) = get_filenames( $_[0] );
1611
1612 my $path = $filename_repo . "*";
1613
1614 my @files = glob( $path );
1615
1616 # if only one result is available
1617 # and this result is a directory,
1618 # add another result entry
1619 # (otherwise complete will stop here and continue with the next parameter)
1620 if( $#files == 0 ) {
1621 my $path = $files[0];
1622 if( -d $path ) {
1623 push( @files, $path . "/" );
1624 }
1625 }
1626
1627 if (@files) {
1628 # remove DASSCM_REPO path again
1629 print join( "\n", map( { s|^${DASSCM_REPO}|/|; $_} @files ) );
1630 print "\n";
1631 }
1632
1633}
1634
1635
1636
1637#####################################################################
1638#
1639# main
1640#
1641
1642my $return_code = $RETURN_OK;
1643my $number_arguments = @ARGV;
1644
1645if ( $number_arguments > 0 ) {
1646
1647 # get subcommand and remove it from @ARGV
1648 $command = get_command_uniform_name($ARGV[0]);
1649 shift @ARGV;
1650
1651 $DASSCM_LOCAL_REPOSITORY_BASE = $config->{'DASSCM_LOCAL_REPOSITORY_BASE'};
1652 $DASSCM_REPOSITORY_NAME = $config->{'DASSCM_REPOSITORY_NAME'};
1653
1654 # TODO: check variables
1655 $DASSCM_SVN_REPOSITORY =
1656 $config->{'DASSCM_SVN_REPOSITORY_BASE'} . "/" . $DASSCM_REPOSITORY_NAME;
1657
1658 $DASSCM_CHECKOUT_USERNAME = $config->{'DASSCM_CHECKOUT_USERNAME'};
1659 $DASSCM_CHECKOUT_PASSWORD = $config->{'DASSCM_CHECKOUT_PASSWORD'};
1660
1661 #
1662 # if a user is given by dasscm configuration file, we use it.
1663 # Otherwise we expect that read-only account is configured
1664 # as local subversion configuration.
1665 # If this is also not the case,
1666 # user is required to type username and password.
1667 # This will be stored as local subversion configuration thereafter.
1668 #
1669 if ( $DASSCM_CHECKOUT_USERNAME && $DASSCM_CHECKOUT_PASSWORD ) {
1670 $svnCheckoutCredentials =
1671 " --username $DASSCM_CHECKOUT_USERNAME --password $DASSCM_CHECKOUT_PASSWORD ";
1672 }
1673
1674 $DASSCM_PERMISSION_FILE = $config->{'DASSCM_PERMISSION_FILE'}
1675 || "/etc/permissions.d/dasscm.permission_backup";
1676
1677 # get command line options and store them in options hash
1678 my $result = GetOptions( \%options, 'verbose', 'message=s' );
1679
1680 # print options
1681 foreach my $option ( keys %options ) {
1682 print "${option}: $options{$option}\n";
1683 }
1684
1685 # set verbose to command line option
1686 $verbose = $options{'verbose'};
1687
1688 #
1689 # action accordinly to command are taken
1690 # $command is rewritten in standard format,
1691 # so we can test for it later on more simply
1692 #
1693
1694 if( get_command_function( $command ) ) {
1695 &{get_command_function( $command )}( @ARGV );
1696 } else {
1697 print "unknown command: $command\n\n";
1698 usage();
1699 check_env();
1700 $return_code = $RETURN_NOK;
1701 }
1702}
1703
1704exit $return_code;
Note: See TracBrowser for help on using the repository browser.