source: trunk/dasscm/dasscm@ 221

Last change on this file since 221 was 220, checked in by joergs, on Oct 15, 2007 at 11:57:43 AM

workarounds for Subverison 1.0.0 (SLES9)

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