source: trunk/dasscm/dasscm@ 222

Last change on this file since 222 was 222, checked in by joergs, on Oct 15, 2007 at 1:33:13 PM

added other dass-it svn servers

  • Property keyword set to id
  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 18.7 KB
Line 
1#!/usr/bin/perl -w
2
3# $Id: dasscm 222 2007-10-15 11:33:13Z 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 print @result;
295 die;
296 }
297
298}
299
300sub svn_update( ;$ )
301{
302 my $update_path = shift || $DASSCM_REPO;
303 ( my $rc_update, my @result ) =
304 run_command(
305 "$SVN update --non-interactive $svnCheckoutCredentials $update_path");
306 print @result;
307 if ( $rc_update != 0 ) {
308 die;
309 }
310}
311
312sub svn_getStoredFiles( ;$ )
313{
314
315 # TODO: get_filenames?
316 #my $rel_path = shift || "";
317 #my $path = "${DASSCM_REPO}/${rel_path}";
318 my $path = ${DASSCM_REPO};
319
320 # svn ls -R is better, but much, much slower
321 # ( my $rc, my @result ) = run_command("$SVN ls --recursive $svnCheckoutCredentials $path");
322 ( my $rc, my @result ) =
323 run_command(
324 "cd $path && find | grep -v '/.svn' | sed -e 's/\.\\///' | grep -v '^\$'"
325 );
326 if ( $rc != 0 ) {
327 print @result;
328 die;
329 }
330 chomp(@result);
331 return @result;
332}
333
334#####################################################################
335#
336# functions
337
338sub help(;@)
339{
340 if ( @_ == 0 ) {
341 usage();
342 } else {
343 print "help for @_: ...\n";
344 usage();
345 }
346}
347
348sub login(@)
349{
350 check_parameter( @_, 1 );
351 check_env();
352
353 my $input_username = $1;
354
355 if ( not $input_username ) {
356 my $output_username = "";
357 if ($DASSCM_USERNAME) {
358 $output_username = " ($DASSCM_USERNAME)";
359 }
360
361 print "Enter DASSCM user name", $output_username, ": ";
362 $input_username = <STDIN>;
363 chomp($input_username);
364 }
365
366 # hidden password input
367 print "Enter DASSCM user password: ";
368 ReadMode('noecho');
369 my $input_password = <STDIN>;
370 ReadMode('normal');
371 chomp($input_password);
372 print "\n";
373
374 svn_check_credentials( $input_username, $input_password );
375
376 #
377 # set environment variables
378 #
379 $ENV{'DASSCM_USERNAME'} = $input_username;
380 $ENV{'DASSCM_PASSWORD'} = $input_password;
381
382 print "subversion access okay\n\n", "DASSCM_USERNAME: $input_username\n",
383 "DASSCM_PASSWORD: (hidden)\n", "DASSCM_PROD: $DASSCM_PROD\n",
384 "DASSCM_REPO: $DASSCM_REPO\n",
385 "Server Repository: $DASSCM_SVN_REPOSITORY\n", "\n", "[dasscm shell]\n\n";
386
387 exec("bash") or die "failed to start new shell";
388}
389
390sub init(@)
391{
392 check_parameter( @_, 1 );
393 check_env();
394
395 # update complete repository
396 # and create permission file
397 my $retcode =
398 run_interactive(
399 "cd $DASSCM_LOCAL_REPOSITORY_BASE; $SVN checkout --non-interactive $svnCheckoutCredentials $svnOptions $DASSCM_SVN_REPOSITORY; touch $permissions_file"
400 );
401}
402
403sub ls(@)
404{
405 check_parameter( @_, 1 );
406 check_env();
407
408 my @files = svn_getStoredFiles(@_);
409
410 print join( "\n", @files );
411 print "\n";
412}
413
414sub update(@)
415{
416 check_parameter( @_, 1 );
417 check_env();
418
419 #
420 # update local repository
421 #
422 svn_update();
423}
424
425sub add_helper(@)
426{
427 (
428 my $basename,
429 my $dirname_prod,
430 my $dirname_repo,
431 my $filename_prod,
432 my $filename_repo
433 )
434 = get_filenames( $_[0] );
435
436 if ( $command eq "add" ) {
437 mkpath($dirname_repo);
438 }
439
440 copy( $filename_prod, $filename_repo ) or die $!;
441
442 if ( $command eq "add" ) {
443
444 # already checked in?
445 chdir($DASSCM_REPO);
446
447 # also add the path to filename.
448 for my $dir ( split( '/', $dirname_prod ) ) {
449 if ($dir) {
450 run_command("$SVN add --non-recursive $dir");
451 chdir $dir;
452 }
453 }
454 run_command("$SVN add $basename");
455 }
456}
457
458#
459# add (is used for command add and commit)
460#
461sub add(@)
462{
463 check_parameter( @_, 1 );
464 check_env();
465
466 #
467 # update local repository
468 #
469 svn_update();
470
471 # add file
472 add_helper( $_[0] );
473
474 # create new permissions file
475 permissions();
476
477 # add permissions file
478 add_helper($permissions_file);
479
480 if ( $options{'message'} ) {
481 $svnOptions .= " --message \"$options{'message'}\" ";
482 }
483
484 # commit calls $EDITOR. uses "interactive" here, to display output
485 my $retcode =
486 run_interactive(
487 "$SVN commit $svnOptions --username $DASSCM_USERNAME $svnPasswordCredentials $DASSCM_REPO"
488 );
489
490 #print $filename_prod. "\n";
491 #print $dirname_repo. "\n";
492}
493
494sub blame(@)
495{
496 check_parameter( @_, 1 );
497 check_env();
498
499 (
500 my $basename,
501 my $dirname_prod,
502 my $dirname_repo,
503 my $filename_prod,
504 my $filename_repo
505 )
506 = get_filenames( $_[0] );
507
508 my $retcode = run_interactive("$SVN blame $svnOptions $filename_repo");
509}
510
511sub diff(@)
512{
513 check_parameter( @_, 1 );
514 check_env();
515
516 (
517 my $basename,
518 my $dirname_prod,
519 my $dirname_repo,
520 my $filename_prod,
521 my $filename_repo
522 )
523 = get_filenames( $_[0] );
524
525 #print "$basename,$dirname_prod,$dirname_repo\n";
526
527 ( my $rc_update, my @result ) = run_command("$SVN update $filename_repo");
528 if ( $rc_update != 0 ) {
529 print @result;
530 die;
531 }
532
533 ( my $rc_diff, my @diff ) =
534 run_command("diff $filename_repo $filename_prod");
535 print @diff;
536}
537
538sub status(@)
539{
540 check_parameter( @_, 1 );
541 check_env();
542
543 #
544 # update local repository
545 #
546 svn_update();
547
548 # TODO: start at subdirectories ?
549 my $dir = $DASSCM_REPO;
550 my @files = svn_getStoredFiles($dir);
551
552 # Liste der geänderten Files ausgeben, falls nicht leer
553 if (@files) {
554
555 # stores result from status (cvscheck)
556 my %removedfiles = ();
557 my %changedfiles = ();
558
559 foreach my $file (@files) {
560
561 my $realfile = "/" . $file;
562 my $cvsworkfile = "${DASSCM_REPO}/${file}";
563
564 if ( -d $realfile ) {
565
566 # directory. do nothing
567 } elsif ( !-r $realfile ) {
568 $removedfiles{"$realfile"} = $cvsworkfile;
569 } else {
570 ( -r "$cvsworkfile" )
571 || die("Fehler: $cvsworkfile ist nicht lesbar");
572 if ( compare( $cvsworkfile, $realfile ) != 0 ) {
573 $changedfiles{"$realfile"} = $cvsworkfile;
574 }
575 }
576 }
577
578 if (%removedfiles) {
579 print "deleted files (found in repository, but not in system):\n";
580 foreach my $key ( values %removedfiles ) {
581 print "$key\n";
582 }
583 print "\n";
584 }
585
586 if (%changedfiles) {
587 print "modified files:\n";
588 foreach my $key ( keys %changedfiles ) {
589 print "$key\n";
590 }
591 }
592 } else {
593 print "no modified files found in $dir\n";
594 }
595
596 print "\n";
597}
598
599sub permissions(@)
600{
601 check_parameter( @_, 1 );
602 check_env();
603
604 #
605 # update local repository
606 #
607 #svn_update();
608
609 # TODO: start at subdirectories ?
610 my $dir = $DASSCM_REPO;
611 my @files = svn_getStoredFiles($dir);
612
613 if (@files) {
614
615 # generieren der Permissions
616 my @permissions = generatePermissionList(@files);
617 my $OUTFILE;
618 my $tofile = 0; # Status für schreiben in File
619
620 if ( -w dirname($permissions_file) ) {
621
622 # Verzeichnis existiert => schreiben
623 print "storing permissions in file $permissions_file\n";
624 open( OUTFILE, ">$permissions_file" )
625 || die("failed to write to $permissions_file: $!");
626 $tofile = 1; # Merken, daß in File geschrieben wird
627 print OUTFILE "#\n";
628 print OUTFILE "# created by dasscm permissions\n";
629 print OUTFILE
630 "# It is intended to be used for restoring permissions\n";
631 } else {
632
633 # Pfad für Sicherungsdatei existiert nicht => schreiben auf stdout
634 # Alias Filehandle für stdout erzeugen
635 *OUTFILE = *STDOUT;
636 }
637 foreach my $line (@permissions) {
638 print OUTFILE "$line\n";
639 }
640
641 if ($tofile) {
642 close(OUTFILE);
643 }
644 }
645}
646
647#####################################################################
648#
649# main
650#
651
652my $number_arguments = @ARGV;
653
654if ( $number_arguments > 0 ) {
655
656 # get subcommand and remove it from @ARGV
657 $command = $ARGV[0];
658 shift @ARGV;
659
660 $DASSCM_LOCAL_REPOSITORY_BASE = $config->{'DASSCM_LOCAL_REPOSITORY_BASE'};
661 $DASSCM_REPOSITORY_NAME = $config->{'DASSCM_REPOSITORY_NAME'};
662
663 # TODO: check variables
664 $DASSCM_SVN_REPOSITORY =
665 $config->{'DASSCM_SVN_REPOSITORY_BASE'} . "/" . $DASSCM_REPOSITORY_NAME;
666
667 my $DASSCM_CHECKOUT_USERNAME = $config->{'DASSCM_CHECKOUT_USERNAME'};
668 my $DASSCM_CHECKOUT_PASSWORD = $config->{'DASSCM_CHECKOUT_PASSWORD'};
669
670 #
671 # if a user is given by dasscm configuration file, we use it.
672 # Otherwise we expect that read-only account is configured
673 # as local subversion configuration.
674 # If this is also not the case,
675 # user is required to type username and password.
676 # This will be stored as local subversion configuration thereafter.
677 #
678 if ( $DASSCM_CHECKOUT_USERNAME && $DASSCM_CHECKOUT_PASSWORD ) {
679 $svnCheckoutCredentials =
680 " --username $DASSCM_CHECKOUT_USERNAME --password $DASSCM_CHECKOUT_PASSWORD ";
681 }
682
683 # get command line options and store them in options hash
684 my $result = GetOptions( \%options, 'verbose', 'message=s' );
685
686 # print options
687 foreach my $option ( keys %options ) {
688 print "${option}: $options{$option}\n";
689 }
690
691 # set verbose to command line option
692 $verbose = $options{'verbose'};
693
694 #
695 # action accordinly to command are taken
696 # $command is rewritten in standard format,
697 # so we can test for it later on more simply
698 #
699 $_ = $command;
700 if (m/help/i) {
701 help(@ARGV);
702 } elsif (m/login/i) {
703 $command = "login";
704 login(@ARGV);
705 } elsif (m/init/i) {
706 $command = "init";
707 init(@ARGV);
708 } elsif (m/ls/i) {
709 $command = "ls";
710 ls(@ARGV);
711 } elsif (m/up/i) {
712 $command = "update";
713 update(@ARGV);
714 } elsif (m/add/i) {
715 $command = "add";
716 add(@ARGV);
717 } elsif (m/commit/i) {
718 $command = "commit";
719 add(@ARGV);
720 } elsif (m/blame/i) {
721 $command = "blame";
722 blame(@ARGV);
723 } elsif (m/diff/i) {
724 $command = "diff";
725 diff(@ARGV);
726 } elsif (m/status/i) {
727 $command = "status";
728 status(@ARGV);
729 } elsif (m/permissions/i) {
730 $command = "permissions";
731 permissions(@ARGV);
732 } else {
733 print "unknown command: $command\n\n";
734 usage();
735 check_env();
736 }
737
738 # cleanup (svn-commit.tmp)
739 # commitall
740 # revert
741 # activate
742}
Note: See TracBrowser for help on using the repository browser.