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
Line 
1#!/usr/bin/perl -w
2
3# $Id: dasscm 220 2007-10-15 09:57:43Z 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 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}
212
213sub get_type
214{
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
225
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;
235}
236
237sub run_command
238{
239 my $command = shift;
240
241 #print "executing command: " . $command . "\n";
242
243 open( RESULT, $command . ' 2>&1 |' );
244 my @result = <RESULT>;
245 close(RESULT);
246 my $retcode = $? >> 8;
247
248 #print @result;
249 #if( $retcode ) { print "return code: " . $retcode . "\n"; }
250
251 return ( $retcode, @result );
252}
253
254sub run_interactive
255{
256
257 if ($verbose) {
258 print "run_interactive:" . join( " ", @_ ) . "\n";
259 }
260
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( $$ )
274{
275 my $username = shift;
276 my $password = shift;
277
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
288 ( my $rc_update, my @result ) =
289 run_command(
290 "$SVN ls --non-interactive --no-auth-cache --username $username --password $password $DASSCM_SVN_REPOSITORY"
291 );
292
293 if ( $rc_update != 0 ) {
294 die;
295 }
296
297}
298
299sub svn_update( ;$ )
300{
301 my $update_path = shift || $DASSCM_REPO;
302 ( my $rc_update, my @result ) =
303 run_command(
304 "$SVN update --non-interactive $svnCheckoutCredentials $update_path");
305 print @result;
306 if ( $rc_update != 0 ) {
307 die;
308 }
309}
310
311sub svn_getStoredFiles( ;$ )
312{
313
314 # TODO: get_filenames?
315 #my $rel_path = shift || "";
316 #my $path = "${DASSCM_REPO}/${rel_path}";
317 my $path = ${DASSCM_REPO};
318
319 # svn ls -R is better, but much, much slower
320 # ( my $rc, my @result ) = run_command("$SVN ls --recursive $svnCheckoutCredentials $path");
321 ( my $rc, my @result ) =
322 run_command(
323 "cd $path && find | grep -v '/.svn' | sed -e 's/\.\\///' | grep -v '^\$'"
324 );
325 if ( $rc != 0 ) {
326 print @result;
327 die;
328 }
329 chomp(@result);
330 return @result;
331}
332
333#####################################################################
334#
335# functions
336
337sub help(;@)
338{
339 if ( @_ == 0 ) {
340 usage();
341 } else {
342 print "help for @_: ...\n";
343 usage();
344 }
345}
346
347sub login(@)
348{
349 check_parameter( @_, 1 );
350 check_env();
351
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);
363 }
364
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);
371 print "\n";
372
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
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";
385
386 exec("bash") or die "failed to start new shell";
387}
388
389sub init(@)
390{
391 check_parameter( @_, 1 );
392 check_env();
393
394 # update complete repository
395 # and create permission file
396 my $retcode =
397 run_interactive(
398 "cd $DASSCM_LOCAL_REPOSITORY_BASE; $SVN checkout $svnCheckoutCredentials $svnOptions $DASSCM_SVN_REPOSITORY; touch $permissions_file"
399 );
400}
401
402sub ls(@)
403{
404 check_parameter( @_, 1 );
405 check_env();
406
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{
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] );
434
435 if ( $command eq "add" ) {
436 mkpath($dirname_repo);
437 }
438
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 }
455}
456
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] );
472
473 # create new permissions file
474 permissions();
475
476 # add permissions file
477 add_helper($permissions_file);
478
479 if ( $options{'message'} ) {
480 $svnOptions .= " --message \"$options{'message'}\" ";
481 }
482
483 # commit calls $EDITOR. uses "interactive" here, to display output
484 my $retcode =
485 run_interactive(
486 "$SVN commit $svnOptions --username $DASSCM_USERNAME $svnPasswordCredentials $DASSCM_REPO"
487 );
488
489 #print $filename_prod. "\n";
490 #print $dirname_repo. "\n";
491}
492
493sub blame(@)
494{
495 check_parameter( @_, 1 );
496 check_env();
497
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");
508}
509
510sub diff(@)
511{
512 check_parameter( @_, 1 );
513 check_env();
514
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;
535}
536
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 ?
548 my $dir = $DASSCM_REPO;
549 my @files = svn_getStoredFiles($dir);
550
551 # Liste der geänderten Files ausgeben, falls nicht leer
552 if (@files) {
553
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 ) {
580 print "$key\n";
581 }
582 print "\n";
583 }
584
585 if (%changedfiles) {
586 print "modified files:\n";
587 foreach my $key ( keys %changedfiles ) {
588 print "$key\n";
589 }
590 }
591 } else {
592 print "no modified files found in $dir\n";
593 }
594
595 print "\n";
596}
597
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 ?
609 my $dir = $DASSCM_REPO;
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
618
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" )
624 || die("failed to write to $permissions_file: $!");
625 $tofile = 1; # Merken, daß in File geschrieben wird
626 print OUTFILE "#\n";
627 print OUTFILE "# created by dasscm permissions\n";
628 print OUTFILE
629 "# It is intended to be used for restoring permissions\n";
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
640 if ($tofile) {
641 close(OUTFILE);
642 }
643 }
644}
645
646#####################################################################
647#
648# main
649#
650
651my $number_arguments = @ARGV;
652
653if ( $number_arguments > 0 ) {
654
655 # get subcommand and remove it from @ARGV
656 $command = $ARGV[0];
657 shift @ARGV;
658
659 $DASSCM_LOCAL_REPOSITORY_BASE = $config->{'DASSCM_LOCAL_REPOSITORY_BASE'};
660 $DASSCM_REPOSITORY_NAME = $config->{'DASSCM_REPOSITORY_NAME'};
661
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
683 my $result = GetOptions( \%options, 'verbose', 'message=s' );
684
685 # print options
686 foreach my $option ( keys %options ) {
687 print "${option}: $options{$option}\n";
688 }
689
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 #
698 $_ = $command;
699 if (m/help/i) {
700 help(@ARGV);
701 } elsif (m/login/i) {
702 $command = "login";
703 login(@ARGV);
704 } elsif (m/init/i) {
705 $command = "init";
706 init(@ARGV);
707 } elsif (m/ls/i) {
708 $command = "ls";
709 ls(@ARGV);
710 } elsif (m/up/i) {
711 $command = "update";
712 update(@ARGV);
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) {
720 $command = "blame";
721 blame(@ARGV);
722 } elsif (m/diff/i) {
723 $command = "diff";
724 diff(@ARGV);
725 } elsif (m/status/i) {
726 $command = "status";
727 status(@ARGV);
728 } elsif (m/permissions/i) {
729 $command = "permissions";
730 permissions(@ARGV);
731 } else {
732 print "unknown command: $command\n\n";
733 usage();
734 check_env();
735 }
736
737 # cleanup (svn-commit.tmp)
738 # commitall
739 # revert
740 # activate
741}
Note: See TracBrowser for help on using the repository browser.