source: trunk/dasscm/dasscm@ 229

Last change on this file since 229 was 228, checked in by joergs, on Dec 10, 2007 at 3:13:46 PM

extended error message

  • Property keyword set to id
  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 19.2 KB
Line 
1#!/usr/bin/perl -w
2
3# $Id: dasscm 228 2007-12-10 14:13:46Z joergs $
4
5use strict;
6
7use Env
8 qw($DASSCM_PROD $DASSCM_REPO $USER $DASSCM_USERNAME $DASSCM_USER $DASSCM_PASSWORD);
9use Cwd;
10use Getopt::Long;
11use File::Basename;
12use File::Compare;
13use File::Copy;
14use File::stat;
15use File::Path;
16use Term::ReadKey;
17
18#
19# used ConfigFile instead of SmartClient::Config,
20# because the huge amount of SmartClient dependencies
21#use SmartClient::Config;
22use ConfigFile;
23
24#####################################################################
25#
26# global
27#
28
29# file to store permissions
30my $permissions_file = "/etc/permissions.d/dasscm.permission_backup";
31
32# configuration file
33my $config_file = "/etc/dasscm.conf";
34my $config = ConfigFile::read_config_file($config_file);
35my $DASSCM_LOCAL_REPOSITORY_BASE;
36my $DASSCM_REPOSITORY_NAME;
37my $DASSCM_SVN_REPOSITORY;
38
39my $SVN = "svn ";
40my $svnOptions = "";
41my $svnCheckoutCredentials = "";
42my $svnPasswordCredentials = "";
43
44# command line options get stored in options hash
45my %options = ();
46
47# subcommand, that gets executed (add, commit, ...)
48my $command;
49
50my $verbose = 0;
51
52#####################################################################
53#
54# util functions
55#
56sub usage()
57{
58 print "usage: dasscm <subcommand> [options] [args]\n";
59 print "\n";
60 print "dasscm is intended to help versioning configuration files\n";
61 print "\n";
62 print "Available subcommands:\n";
63 print " help <subcommand>\n";
64 print " init\n";
65 print " login\n";
66 print " up\n";
67 print " ls\n";
68 print " add <filename>\n";
69 print " commit <filename>\n";
70 print " status <filename>\n";
71 print " diff <filename>\n";
72 print " permissions\n";
73 print "\n";
74 print "preperation:\n", " if dasscm is already configured,\n",
75 " use 'dasscm login' and than eg. 'add'.\n",
76 " The environment variables\n", " DASSCM_REPO\n", " DASSCM_PROD\n",
77 " DASSCM_USERNAME\n", " DASSCM_PASSWORD\n",
78 " are evaluated, but set automatically by 'dasscm login'.\n", "\n",
79 " If dasscm is not yet configured, read",
80 " /usr/share/doc/packages/dasscm/dasscm_howto.txt\n";
81}
82
83sub check_env()
84{
85
86 # DASSCM_PROD
87 if ( !$DASSCM_PROD ) {
88 $DASSCM_PROD = "/";
89 }
90
91 if ( !-d $DASSCM_PROD ) {
92 die "DASSCM_PROD ($DASSCM_PROD) is not set to a directory.\n";
93 }
94 if ($verbose) { print "DASSCM_PROD: " . $DASSCM_PROD . "\n"; }
95
96 # DASSCM_REPOSITORY_NAME
97 if ( !$DASSCM_REPOSITORY_NAME ) {
98 die
99 "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";
100 }
101
102 # DASSCM_REPO
103 if ( !$DASSCM_REPO ) {
104 if ( $DASSCM_LOCAL_REPOSITORY_BASE && $DASSCM_REPOSITORY_NAME ) {
105 $DASSCM_REPO =
106 $DASSCM_LOCAL_REPOSITORY_BASE . "/" . $DASSCM_REPOSITORY_NAME;
107 } else {
108 die
109 "Envirnonment variable DASSCM_REPO not set.\nSet DASSCM_REPO to the directory of the versioning system checkout for this machine.\n";
110 }
111 }
112 if ($verbose) { print "DASSCM_REPO: " . $DASSCM_REPO . "\n"; }
113
114 #
115 # check if local repository directory exist (if not creating by init)
116 #
117 if ( $command ne "init" ) {
118 if ( not -d $DASSCM_REPO ) {
119 die
120 "Can't access local repository DASSCM_REPO\n($DASSCM_REPO)\nCheck configuration and execute\n dasscm init\n";
121 }
122
123 #
124 # user settings
125 #
126
127 # DASSCM_USER is legacy. Use DASSCM_USERNAME instead
128 if ( !$DASSCM_USERNAME ) {
129 $DASSCM_USERNAME = $DASSCM_USER;
130 }
131
132 # user root is not allowed for checkins.
133 # if user is root, DASSCM_USER has to be set,
134 # otherwise USER can be used
135 if ( "$USER" eq "root" ) {
136 if ( ( not $DASSCM_USERNAME ) and ( $command ne "login" ) ) {
137 die
138 "Envirnonment variable DASSCM_USERNAME not set.\nSet DASSCM_USERNAME to your subversion user account.\n";
139 }
140 $svnOptions .= " --no-auth-cache ";
141 } elsif ( !$DASSCM_USERNAME ) {
142 $DASSCM_USERNAME = $USER;
143 }
144
145 #
146 # password
147 #
148 if ($DASSCM_PASSWORD) {
149 $svnPasswordCredentials = " --password $DASSCM_PASSWORD ";
150 }
151 }
152
153 #$svnOptions .= " --username $DASSCM_USERNAME "
154}
155
156sub check_parameter(@)
157{
158}
159
160sub get_filenames(@)
161{
162 my $filename_prod = $_[0];
163 if ( !( $filename_prod =~ m/^\// ) ) {
164 $filename_prod = cwd() . "/" . $filename_prod;
165 }
166
167 -r $filename_prod or die "$filename_prod is not accessable";
168
169 # TODO: dirname buggy: eg. "/etc/" is reduced to "/",
170 # "/etc" is used as filename
171 my $dirname_prod = dirname($filename_prod);
172 chdir $dirname_prod or die $!;
173 $dirname_prod = cwd();
174 my $basename = basename($filename_prod);
175
176 if ($verbose) {
177 print "dir: " . $dirname_prod . "\n";
178 print "fn: " . $basename . "\n";
179 }
180
181 my $dirname_repo = $DASSCM_REPO . "/" . $dirname_prod;
182 my $filename_repo = "$dirname_repo/$basename";
183
184 return (
185 $basename, $dirname_prod, $dirname_repo,
186 $filename_prod, $filename_repo
187 );
188}
189
190sub generatePermissionList
191{
192
193 # generieren der Zeilen für Permission-Savefile
194 my @files = @_;
195 my @permlist = ();
196 foreach my $file (@files) {
197 $file = "/" . $file;
198 if( -e $file ) {
199 my $info = stat( $file ) || die "failed to stat $file: aborting";
200 my $mode = get_type( $info->mode ) & 07777;
201 my $modestring = sprintf( "%04o", $mode );
202 my $uid = $info->uid;
203 my $uidname = getpwuid($uid);
204 my $gid = $info->gid;
205 my $gidname = getgrgid($gid);
206 push(
207 @permlist,
208 sprintf( "%-55s %-17s %4d",
209 $file, "${uidname}:${gidname}", $modestring )
210 );
211 } else {
212 print "failed to get status of $file. It exists in the repository, but not on the system\n";
213 }
214 }
215 return @permlist;
216}
217
218sub get_type
219{
220
221 # Funktion übernommen aus /usr/bin/chkstat
222 my $S_IFLNK = 0120000; # symbolic link
223 my $S_IFREG = 0100000; # regular file
224 my $S_IFDIR = 0040000; # directory
225 my $S_IFCHAR = 0020000; # character device
226 my $S_IFBLK = 0060000; # block device
227 my $S_IFFIFO = 0010000; # fifo
228 my $S_IFSOCK = 0140000; # socket
229 my $S_IFMT = 0170000; # type of file
230
231 my $S_m;
232 if ( ( $_[0] & $S_IFMT ) == $S_IFLNK ) { $S_m = $_[0] - $S_IFLNK; }
233 elsif ( ( $_[0] & $S_IFMT ) == $S_IFREG ) { $S_m = $_[0] - $S_IFREG; }
234 elsif ( ( $_[0] & $S_IFMT ) == $S_IFDIR ) { $S_m = $_[0] - $S_IFDIR; }
235 elsif ( ( $_[0] & $S_IFMT ) == $S_IFCHAR ) { $S_m = $_[0] - $S_IFCHAR; }
236 elsif ( ( $_[0] & $S_IFMT ) == $S_IFBLK ) { $S_m = $_[0] - $S_IFBLK; }
237 elsif ( ( $_[0] & $S_IFMT ) == $S_IFFIFO ) { $S_m = $_[0] - $S_IFFIFO; }
238 elsif ( ( $_[0] & $S_IFMT ) == $S_IFSOCK ) { $S_m = $_[0] - $S_IFSOCK; }
239 $S_m;
240}
241
242sub run_command
243{
244 my $command = shift;
245
246 #print "executing command: " . $command . "\n";
247
248 open( RESULT, $command . ' 2>&1 |' );
249 my @result = <RESULT>;
250 close(RESULT);
251 my $retcode = $? >> 8;
252
253 #print @result;
254 #if( $retcode ) { print "return code: " . $retcode . "\n"; }
255
256 return ( $retcode, @result );
257}
258
259sub run_interactive
260{
261
262 if ($verbose) {
263 print "run_interactive:" . join( " ", @_ ) . "\n";
264 }
265
266 system(@_);
267 if ( $? == -1 ) {
268 printf "failed to execute: $!\n";
269 } elsif ( $? & 127 ) {
270 printf "child died with signal %d, %s coredump\n", ( $? & 127 ),
271 ( $? & 128 ) ? 'with' : 'without';
272 } elsif ( $? >> 8 != 0 ) {
273 printf "child exited with value %d\n", $? >> 8;
274 }
275 return ( $? >> 8 );
276}
277
278sub svn_check_credentials( $$ )
279{
280 my $username = shift;
281 my $password = shift;
282
283 print "checking credentials ... ";
284
285 # Options for "svn info" are not supported by subversion 1.0.0 (SLES9),
286 # therefore switching to "svn status"
287 # ( my $rc_update, my @result ) =
288 # run_command(
289 # "$SVN info --non-interactive --no-auth-cache --username $username --password $password $DASSCM_SVN_REPOSITORY"
290 # );
291 #print @result;
292
293 ( my $rc_update, my @result ) =
294 run_command(
295 "$SVN ls --non-interactive --no-auth-cache --username $username --password $password $DASSCM_SVN_REPOSITORY"
296 );
297
298 if ( $rc_update != 0 ) {
299 print @result;
300 die;
301 }
302
303}
304
305sub svn_update( ;$ )
306{
307 my $update_path = shift || $DASSCM_REPO;
308 ( my $rc_update, my @result ) =
309 run_command(
310 "$SVN update --non-interactive $svnCheckoutCredentials $update_path");
311 print @result;
312 if ( $rc_update != 0 ) {
313 die;
314 }
315}
316
317sub svn_getStoredFiles( ;$ )
318{
319
320 # TODO: get_filenames?
321 #my $rel_path = shift || "";
322 #my $path = "${DASSCM_REPO}/${rel_path}";
323 my $path = ${DASSCM_REPO};
324
325 # svn ls -R is better, but much, much slower
326 # ( my $rc, my @result ) = run_command("$SVN ls --recursive $svnCheckoutCredentials $path");
327 ( my $rc, my @result ) =
328 run_command(
329 "cd $path && find | grep -v '/.svn' | sed -e 's/\.\\///' | grep -v '^\$'"
330 );
331 if ( $rc != 0 ) {
332 print @result;
333 die;
334 }
335 chomp(@result);
336 return @result;
337}
338
339#####################################################################
340#
341# functions
342
343sub help(;@)
344{
345 if ( @_ == 0 ) {
346 usage();
347 } else {
348 print "help for @_: ...\n";
349 usage();
350 }
351}
352
353sub login(@)
354{
355 check_parameter( @_, 1 );
356 check_env();
357
358 my $input_username = $1;
359
360 if ( not $input_username ) {
361 my $output_username = "";
362 if ($DASSCM_USERNAME) {
363 $output_username = " ($DASSCM_USERNAME)";
364 }
365
366 print "Enter DASSCM user name", $output_username, ": ";
367 $input_username = <STDIN>;
368 chomp($input_username);
369 }
370
371 # hidden password input
372 print "Enter DASSCM user password: ";
373 ReadMode('noecho');
374 my $input_password = <STDIN>;
375 ReadMode('normal');
376 chomp($input_password);
377 print "\n";
378
379 svn_check_credentials( $input_username, $input_password );
380
381 #
382 # set environment variables
383 #
384 $ENV{'DASSCM_USERNAME'} = $input_username;
385 $ENV{'DASSCM_PASSWORD'} = $input_password;
386
387 print "subversion access okay\n\n", "DASSCM_USERNAME: $input_username\n",
388 "DASSCM_PASSWORD: (hidden)\n", "DASSCM_PROD: $DASSCM_PROD\n",
389 "DASSCM_REPO: $DASSCM_REPO\n",
390 "Server Repository: $DASSCM_SVN_REPOSITORY\n", "\n", "[dasscm shell]\n\n";
391
392 exec("bash") or die "failed to start new shell";
393}
394
395sub init(@)
396{
397 check_parameter( @_, 1 );
398 check_env();
399
400 # update complete repository
401 # and create permission file
402 my $retcode =
403 run_interactive(
404 "cd $DASSCM_LOCAL_REPOSITORY_BASE; $SVN checkout $svnCheckoutCredentials $svnOptions $DASSCM_SVN_REPOSITORY; touch $permissions_file"
405 );
406}
407
408sub ls(@)
409{
410 check_parameter( @_, 1 );
411 check_env();
412
413 my @files = svn_getStoredFiles(@_);
414
415 print join( "\n", @files );
416 print "\n";
417}
418
419sub update(@)
420{
421 check_parameter( @_, 1 );
422 check_env();
423
424 #
425 # update local repository
426 #
427 svn_update();
428}
429
430sub add_helper(@)
431{
432 (
433 my $basename,
434 my $dirname_prod,
435 my $dirname_repo,
436 my $filename_prod,
437 my $filename_repo
438 )
439 = get_filenames( $_[0] );
440
441 if ( $command eq "add" ) {
442 mkpath($dirname_repo);
443 }
444
445 copy( $filename_prod, $filename_repo ) or die "failed to copy $filename_prod to repository: $!";
446
447 if ( $command eq "add" ) {
448
449 # already checked in?
450 chdir($DASSCM_REPO);
451
452 # also add the path to filename.
453 for my $dir ( split( '/', $dirname_prod ) ) {
454 if ($dir) {
455 my( $rc, @out ) = run_command("$SVN add --non-recursive \"" . $dir . "\"" );
456 if( $rc > 0 ) {
457 print join( "\n", @out );
458 }
459 chdir $dir;
460 }
461 }
462 my( $rc, @out ) = run_command("$SVN add \"" . $basename . "\"");
463 if( $rc > 0 ) {
464 print join( "\n", @out );
465 }
466 }
467}
468
469#
470# add (is used for command add and commit)
471#
472sub add(@)
473{
474 check_parameter( @_, 1 );
475 check_env();
476
477 #
478 # update local repository
479 #
480 svn_update();
481
482 # add file
483 add_helper( $_[0] );
484
485 # create new permissions file
486 permissions();
487
488 # add permissions file
489 add_helper($permissions_file);
490
491 if ( $options{'message'} ) {
492 $svnOptions .= " --message \"$options{'message'}\" ";
493 }
494
495 # commit calls $EDITOR. uses "interactive" here, to display output
496 my $retcode =
497 run_interactive(
498 "$SVN commit $svnOptions --username $DASSCM_USERNAME $svnPasswordCredentials $DASSCM_REPO"
499 );
500
501 #print $filename_prod. "\n";
502 #print $dirname_repo. "\n";
503}
504
505sub blame(@)
506{
507 check_parameter( @_, 1 );
508 check_env();
509
510 (
511 my $basename,
512 my $dirname_prod,
513 my $dirname_repo,
514 my $filename_prod,
515 my $filename_repo
516 )
517 = get_filenames( $_[0] );
518
519 my $retcode = run_interactive("$SVN blame $svnOptions $filename_repo");
520}
521
522sub diff(@)
523{
524 check_parameter( @_, 1 );
525 check_env();
526
527 (
528 my $basename,
529 my $dirname_prod,
530 my $dirname_repo,
531 my $filename_prod,
532 my $filename_repo
533 )
534 = get_filenames( $_[0] );
535
536 #print "$basename,$dirname_prod,$dirname_repo\n";
537
538 ( my $rc_update, my @result ) = run_command("$SVN update $filename_repo");
539 if ( $rc_update != 0 ) {
540 print @result;
541 die;
542 }
543
544 ( my $rc_diff, my @diff ) =
545 run_command("diff $filename_repo $filename_prod");
546 print @diff;
547}
548
549sub status(@)
550{
551 check_parameter( @_, 1 );
552 check_env();
553
554 #
555 # update local repository
556 #
557 svn_update();
558
559 # TODO: start at subdirectories ?
560 my $dir = $DASSCM_REPO;
561 my @files = svn_getStoredFiles($dir);
562
563 # Liste der geänderten Files ausgeben, falls nicht leer
564 if (@files) {
565
566 # stores result from status (cvscheck)
567 my %removedfiles = ();
568 my %changedfiles = ();
569
570 foreach my $file (@files) {
571
572 my $realfile = "/" . $file;
573 my $cvsworkfile = "${DASSCM_REPO}/${file}";
574
575 if ( -d $realfile ) {
576
577 # directory. do nothing
578 } elsif ( !-r $realfile ) {
579 $removedfiles{"$realfile"} = $cvsworkfile;
580 } else {
581 ( -r "$cvsworkfile" )
582 || die("Fehler: $cvsworkfile ist nicht lesbar");
583 if ( compare( $cvsworkfile, $realfile ) != 0 ) {
584 $changedfiles{"$realfile"} = $cvsworkfile;
585 }
586 }
587 }
588
589 if (%removedfiles) {
590 print "deleted files (found in repository, but not in system):\n";
591 foreach my $key ( values %removedfiles ) {
592 print "$key\n";
593 }
594 print "\n";
595 }
596
597 if (%changedfiles) {
598 print "modified files:\n";
599 foreach my $key ( keys %changedfiles ) {
600 print "$key\n";
601 }
602 }
603 } else {
604 print "no modified files found in $dir\n";
605 }
606
607 print "\n";
608}
609
610sub permissions(@)
611{
612 check_parameter( @_, 1 );
613 check_env();
614
615 #
616 # update local repository
617 #
618 #svn_update();
619
620 # TODO: start at subdirectories ?
621 my $dir = $DASSCM_REPO;
622 my @files = svn_getStoredFiles($dir);
623
624 if (@files) {
625
626 # generieren der Permissions
627 my @permissions = generatePermissionList(@files);
628 my $OUTFILE;
629 my $tofile = 0; # Status für schreiben in File
630
631 if ( -w dirname($permissions_file) ) {
632
633 # Verzeichnis existiert => schreiben
634 print "storing permissions in file $permissions_file\n";
635 open( OUTFILE, ">$permissions_file" )
636 || die("failed to write to $permissions_file: $!");
637 $tofile = 1; # Merken, daß in File geschrieben wird
638 print OUTFILE "#\n";
639 print OUTFILE "# created by dasscm permissions\n";
640 print OUTFILE
641 "# It is intended to be used for restoring permissions\n";
642 } else {
643
644 # Pfad für Sicherungsdatei existiert nicht => schreiben auf stdout
645 # Alias Filehandle für stdout erzeugen
646 *OUTFILE = *STDOUT;
647 }
648 foreach my $line (@permissions) {
649 print OUTFILE "$line\n";
650 }
651
652 if ($tofile) {
653 close(OUTFILE);
654 }
655 }
656}
657
658#####################################################################
659#
660# main
661#
662
663my $number_arguments = @ARGV;
664
665if ( $number_arguments > 0 ) {
666
667 # get subcommand and remove it from @ARGV
668 $command = $ARGV[0];
669 shift @ARGV;
670
671 $DASSCM_LOCAL_REPOSITORY_BASE = $config->{'DASSCM_LOCAL_REPOSITORY_BASE'};
672 $DASSCM_REPOSITORY_NAME = $config->{'DASSCM_REPOSITORY_NAME'};
673
674 # TODO: check variables
675 $DASSCM_SVN_REPOSITORY =
676 $config->{'DASSCM_SVN_REPOSITORY_BASE'} . "/" . $DASSCM_REPOSITORY_NAME;
677
678 my $DASSCM_CHECKOUT_USERNAME = $config->{'DASSCM_CHECKOUT_USERNAME'};
679 my $DASSCM_CHECKOUT_PASSWORD = $config->{'DASSCM_CHECKOUT_PASSWORD'};
680
681 #
682 # if a user is given by dasscm configuration file, we use it.
683 # Otherwise we expect that read-only account is configured
684 # as local subversion configuration.
685 # If this is also not the case,
686 # user is required to type username and password.
687 # This will be stored as local subversion configuration thereafter.
688 #
689 if ( $DASSCM_CHECKOUT_USERNAME && $DASSCM_CHECKOUT_PASSWORD ) {
690 $svnCheckoutCredentials =
691 " --username $DASSCM_CHECKOUT_USERNAME --password $DASSCM_CHECKOUT_PASSWORD ";
692 }
693
694 # get command line options and store them in options hash
695 my $result = GetOptions( \%options, 'verbose', 'message=s' );
696
697 # print options
698 foreach my $option ( keys %options ) {
699 print "${option}: $options{$option}\n";
700 }
701
702 # set verbose to command line option
703 $verbose = $options{'verbose'};
704
705 #
706 # action accordinly to command are taken
707 # $command is rewritten in standard format,
708 # so we can test for it later on more simply
709 #
710 $_ = $command;
711 if (m/help/i) {
712 help(@ARGV);
713 } elsif (m/login/i) {
714 $command = "login";
715 login(@ARGV);
716 } elsif (m/init/i) {
717 $command = "init";
718 init(@ARGV);
719 } elsif (m/ls/i) {
720 $command = "ls";
721 ls(@ARGV);
722 } elsif (m/up/i) {
723 $command = "update";
724 update(@ARGV);
725 } elsif (m/add/i) {
726 $command = "add";
727 add(@ARGV);
728 } elsif (m/commit/i) {
729 $command = "commit";
730 add(@ARGV);
731 } elsif (m/blame/i) {
732 $command = "blame";
733 blame(@ARGV);
734 } elsif (m/diff/i) {
735 $command = "diff";
736 diff(@ARGV);
737 } elsif (m/status/i) {
738 $command = "status";
739 status(@ARGV);
740 } elsif (m/permissions/i) {
741 $command = "permissions";
742 permissions(@ARGV);
743 } else {
744 print "unknown command: $command\n\n";
745 usage();
746 check_env();
747 }
748
749 # cleanup (svn-commit.tmp)
750 # commitall
751 # revert
752 # activate
753 # rm
754}
Note: See TracBrowser for help on using the repository browser.