Removed /usr/local from CDPATH
[clearscm.git] / CCDB / mspb.pl
1 #!/usr/bin/perl
2
3 =pod
4
5 =head1 NAME $RCSfile: mspb.pl,v $
6
7 MultiSite PlayBack: This script updates the CCDB database by playing back
8 multisite transcations.
9
10 =head1 VERSION
11
12 =over
13
14 =item Author
15
16 Andrew DeFaria <Andrew@ClearSCM.com>
17
18 =item Revision
19
20 $Revision: 1.2 $
21
22 =item Created:
23
24 Fri Mar 11 19:09:52 PST 2011
25
26 =item Modified:
27
28 $Date: 2011/05/05 18:39:56 $
29
30 =back
31
32 =head1 SYNOPSIS
33
34  Usage mspb.pl: [-u|sage] [-ve|rbose] [-deb|ug] [-vo|b <vob>]
35  
36  Where:
37    -u|sage:       Displays usage
38  
39    -ve|rbose:     Be verbose
40    -deb|ug:       Output debug messages
41    
42    -vo|b <vob>:   Vob to process (Default: All vobs)
43      
44 =head1 DESCRIPTION
45
46 This script updates the CCDB database with Clearcase UCM meta data by playing
47 back multisite transactions.
48
49 If no parameters are specified then mspb attempts to replay all transactions
50 from all vobs listed in CCDB. To add a new vob use -vob. Epoch numbers are kept
51 in CCDB to keep track of the last oplog operation that had been played back for
52 the vob.
53
54 Note that only certain transactions are played back, those that correspond to
55 actions important to the metadata kept in CCDB. Also, if a transaction fails,
56 i.e. the add or deletion of a record fails, then the error is silently ignored.
57 This allows you to playback transactions without worry that replaying an 
58 already played transcation will cause the data to become out of sync. This is 
59 much like multisite's syncreplica itself.
60
61 =cut
62
63 use strict;
64 use warnings;
65
66 use FindBin;
67 use Getopt::Long;
68
69 use lib "$FindBin::Bin/lib", "$FindBin::Bin/../lib";
70
71 use CCDB;
72 use Clearcase;
73 use Clearcase::Element;
74 use Clearcase::UCM::Activity;
75 use Clearcase::Vob;
76 use DateUtils;
77 use Display;
78 use Logger;
79 use TimeUtils;
80 use Utils;
81
82 my $VERSION  = '$Revision: 1.2 $';
83   ($VERSION) = ($VERSION =~ /\$Revision: (.*) /);
84
85 my (%opts, %totals, $log);
86
87 my $ccdb = CCDB->new;
88
89 sub ParseOplog ($) {
90   my ($oplog) = @_;
91
92   my %record;
93     
94   while (<$oplog>) {
95     last if /^$/;
96     
97     if (/(\S+)= (.*)/) {
98       my $key   = $1;
99       my $value = $2;
100       
101       # Special casing op_time. For some odd reason we have more than one value
102       # on a single line. One case of this is where the keyword is "op_time". 
103       # We've seen lines like this:
104       #
105       #   op_time= 2008-10-15T18:48:39Z  create_time= 2008-10-15T18:48:39Z
106       #
107       # So by now $value = '2008-10-15T18:48:39Z  create_time= 2008-10-15T18:48:39Z'
108       # so we'll split it based on '  '.
109       if ($key eq 'op_time') {
110         # Note: 2 spaces!
111         ($value, $_) = split /  /, $value;
112         
113         # Set op_time
114         $record{$key} = $value;
115         
116         # Now parse $create time
117         if (/(\S+)= (.*)/) {
118           $key   = $1;
119           $value = $2;
120         } # if
121       } # if
122       
123       # Some values are wrapped in quotes
124       if ($value =~ /(\'|\")(.*)(\'|\")/) {
125         $value = $2;
126       } # if
127       
128       # If a key occurs multiple times then make its value an array
129       if ($record{$key}) {
130         if (ref $record{$key} eq 'ARRAY') {
131           push @{$record{$key}}, $value;
132         } else {
133           $record{$key} = [ $record{$key}, $value];
134         } # if
135       } else {
136         $record{$key} = $value;
137       } # if
138     } # if
139   } # while
140   
141   return %record;  
142 } # ParseOplog
143
144 sub GetSubmittedDate ($$) {
145   my ($activity, $pvob) = @_;
146   
147   $pvob = Clearcase::vobtag $pvob;
148   
149   my $cmd = "describe -fmt \"%Na\" activity:$activity\@$pvob";
150   
151   my ($status, @output) = $Clearcase::CC->execute ($cmd);
152   
153   unless ($status) {
154     if ($output[0]) {
155       foreach (split / /, $output[0]) {
156         if (/(\w+)=(.+)/) {
157           if ($1 =~ /submit_time/i) {
158             my ($year, $mon, $mday, $hour, $min, $sec) = ymdhms $2;
159             return "$year-$mon-$mday $hour:$min:$sec";
160           } # if
161         } # if
162       } # foreach
163     } # if
164   } # unless
165   
166   return;
167 } # GetSubmittedDate
168
169 sub AddActivity ($%) {
170   my ($pvob, %oplog) = @_;
171   
172   my ($cmd, $err, $status, $msg, @output, %existingRec);
173   
174   $totals{'Activities processed'}++;
175
176   undef %existingRec;
177   
178   # Add an activity (if not already existing)      
179   %existingRec = $ccdb->GetActivity ($oplog{name_p}, $pvob);
180   
181   unless (%existingRec) {
182     my $submitted = GetSubmittedDate $oplog{name_p}, $pvob;
183     
184     my $cmd = "describe -fmt \"%[owner]p\" activity:$oplog{name_p}\@"
185             . Clearcase::vobtag $pvob;
186             
187     my ($status, @output) = $Clearcase::CC->execute ($cmd);
188
189     ($err, $msg) = $ccdb->AddActivity ({
190       oid       => $oplog{activity_oid},
191       name      => $oplog{name_p},
192       pvob      => $pvob,
193       owner     => $output[0],
194       submitted => $submitted,
195     });
196
197     return ($err, $msg)
198       if $err;
199       
200     $totals{'Activities added'}++;
201   } # unless
202     
203   # Add a stream (if not already existing)
204   $cmd = "lsactivity -fmt \"%[stream]p\" $oplog{name_p}\@"
205        . Clearcase::vobtag $pvob;
206           
207   ($status, @output) = $Clearcase::CC->execute ($cmd);
208   
209   if ($status) {
210     # There are times when an activity is subsequently deleted. Since we are
211     # playing back Multisite transactions we will see the mkactivity first, then
212     # later see a corresponding rmactivity. Since Multisite has already played
213     # all of these transactions to Clearcase itself the activity is gone - and
214     # therefore the lsactivity will fail to find the stream. If that's the case
215     # then just return. If not then issue a warning.
216     unless ($output[0] =~ /activity not found/) {
217       $log->warn ("Can't find stream for activity:$oplog{name_p}\@"
218          . Clearcase::vobtag $pvob
219       );
220     } # unless
221     
222     return (1, "Unable to execute command: $cmd (Status: $status)\n" 
223           . join ("\n", @output))
224   } # if
225
226   undef %existingRec;
227   
228   %existingRec = $ccdb->GetStream ($output[0], $pvob);
229   
230   unless (%existingRec) {
231     ($err, $msg) = $ccdb->AddStream ({
232       name => $output[0], 
233       pvob => $pvob
234     });
235
236     if ($err) {
237       $log->warn ("Unable to add stream:$output[0]\@"
238                . Clearcase::vobtag $pvob
239                . " (Error: $err)\n$msg");
240       
241       return ($err, $msg);
242     } # if
243       
244     $totals{'Streams added'}++;
245   } # unless
246
247   undef %existingRec;
248   
249   # Link them (if not already linked)
250   %existingRec = $ccdb->GetStreamActivityXref (
251     $output[0], 
252     $oplog{name_p},
253     $pvob
254   );
255   
256   unless (%existingRec) {
257     ($err, $msg) = $ccdb->AddStreamActivityXref ({
258       stream   => $output[0],
259       activity => $oplog{name_p},
260       pvob     => $pvob,
261     });
262
263     if ($err) {
264       $log->warn ("Unable to add stream_activity_xref:$output[0]\@"
265                . Clearcase::vobtag $pvob
266                . " activity:$oplog{name_p} (Error: $err)\n$msg");
267       
268       return ($err, $msg);
269     } # if
270       
271     $totals{'Stream/Activity Xrefs added'}++;
272   } # unless
273   
274   return;
275 } # AddActivity
276
277 sub AddStream ($%) {
278   my ($pvob, %oplog) = @_;
279   
280   my ($err, $msg);
281   
282   $totals{'Streams processed'}++;
283
284   # Add a stream (if not already existing)      
285   my %existingRec = $ccdb->GetStream ($oplog{name_p}, $pvob);
286   
287   unless (%existingRec) {
288     my $pvobTag = Clearcase::vobtag $pvob;
289     my $cmd     = "lsstream -fmt \"%[project]p\" $oplog{name_p}\@$pvobTag";
290     
291     my ($status, @output) = $Clearcase::CC->execute ($cmd);
292     
293     if ($status) {
294       $log->err ("Unable to execute command: $cmd (Status: $status)"
295               . join ("\n", @output));
296       return ($status, join ("\n", @output));            
297     } # if
298     
299     ($err, $msg) = $ccdb->AddStream ({
300       oid     => $oplog{activity_oid},
301       name    => $oplog{name_p},
302       project => $output[0],
303       pvob    => $pvob,
304     });
305
306     unless ($err) {
307       $totals{'Streams added'}++;
308     } # unless
309   } # unless
310     
311   return ($err, $msg); 
312 } # AddStream
313
314 sub ProcessActivity ($$%) {
315   my ($operation, $pvob, %oplog) = @_;
316   
317   # Many operations in Multisite's oplog have an op of mkactivity but are 
318   # actually operations on other objects based on actype_oid. The following are 
319   # actype_oid values:
320   my @validActypes = (
321     'activity',
322     'folder',
323     'project',
324     'stream',
325     'timeline',
326     'internal',
327   );
328   
329   # We only handle activity and stream here
330   my $actype;
331   
332   if ($oplog{actype_oid}) {
333     $actype = $Clearcase::CC->oid2name ($oplog{actype_oid}, $pvob);
334   } else {
335     if ($operation eq 'rmactivity' and $oplog{comment}) {
336       $actype = 'activity';
337     } else {
338       return;
339     } # if
340   } # if
341   
342   my ($err, $msg);
343   
344   if ($operation eq 'mkactivity') {
345     if ($actype eq 'activity') {
346       AddActivity $pvob, %oplog;
347     } elsif ($actype eq 'stream') {
348       AddStream $pvob, %oplog;
349     } # if
350   } elsif ($operation eq 'rmactivity') {
351     if ($actype eq 'activity') {
352       # For rmactivity there's nothing but the comment to go on to get the
353       # activity's name. The comment must be of a format of "Destroyed activity
354       # "<activity_name>@<pvob>"." complete with nested double quotes. 
355       my ($activity, $pvob);
356       
357       # Note: There are rmactivity's that lack the comment! Nothing we can do
358       # with these except to ignore them!
359       return
360         unless $oplog{comment};
361       
362       # Note: <pvob> is a vob tag of the variety of the client. So, for example,
363       # it can be a Windows style pvob (e.g. \\pvob) even though we are running
364       # on a Linux machine where the pvob needs to be /vob/pvob!
365       if ($oplog{comment} =~ /Destroyed activity \"activity:(\S+)\@(\S+)\"/) {
366         $activity = $1;
367         $pvob     = Clearcase::vobname ($2);
368       } # if
369       
370       return
371         unless ($activity or $pvob);
372         
373       $totals{'Activities processed'}++;
374       $totals{'Activities deleted'}++;
375
376       return $ccdb->DeleteActivity ($activity, $pvob);
377     } elsif ($actype eq 'stream') {
378       # Note: I have yet to see an rmactivity stream with even an actype_oid!
379       $totals{'Streams processed'}++;
380       $totals{'Streams deleted'}++;
381
382       return $ccdb->DeleteStreamOID ($oplog{activity_oid});
383     } # if
384   } # if
385   
386   return;
387 } # ProcessActivity
388
389 sub ProcessBaseline ($$%) {
390   my ($operation, $pvob, %oplog) = @_;
391   
392   my ($cmd, $err, $status, $msg, @output, %existingRec);
393
394   my $pvobTag = Clearcase::vobtag $pvob;
395   
396   $totals{'Baselines processed'}++;
397
398   if ($operation eq 'mkcheckpoint') {
399     undef %existingRec;
400     
401     # Add an activity (if not already existing)      
402     %existingRec = $ccdb->GetBaseline ($oplog{name_p}, $pvob);
403     
404     unless (%existingRec) {
405       ($err, $msg) = $ccdb->AddBaseline ({
406         oid  => $oplog{checkpoint_oid},
407         name => $oplog{name_p},
408         pvob => $pvob,
409       });
410   
411       return ($err, $msg)
412         if $err;
413         
414       $totals{'Baselines added'}++;
415     } # unless
416     
417     # Add a stream_baseline_xref entry
418     $cmd = "lsbl -fmt \"%[bl_stream]p\" $oplog{name_p}\@$pvobTag";
419     
420     ($status, @output) = $Clearcase::CC->execute ($cmd);
421     
422     if ($status) {
423       $log->err ("Unable to execute command: $cmd (Status: $status)"
424               . join ("\n", @output));
425       return;            
426     } # if
427     
428     ($err, $msg) = $ccdb->AddStreamBaselineXref ({
429       stream   => $output[0],
430       baseline => $oplog{name_p},
431       pvob     => $pvob,
432     });
433
434     return ($err, $msg)
435       if $err;
436
437     $totals{'Stream/Baseline Xrefs added'}++;
438         
439     return
440       unless $oplog{activity_oid};
441       
442     # Loop through activities
443     my @activities = ref $oplog{activity_oid} eq 'ARRAY'
444                    ? @{$oplog{activity_oid}}
445                    : ($oplog{activity_oid});
446                   
447     foreach (@activities) {
448       my $activity = $Clearcase::CC->oid2name ($_, $pvob);
449
450       # I think $activity will be blank if after this mkcheckpoint somebody
451       # did an rmactivity...      
452       next
453         unless $activity;
454         
455       # Check to see if the activity exists
456       undef %existingRec;
457     
458       %existingRec = $ccdb->GetActivity ($activity, $pvob);
459       
460       unless (%existingRec) {
461         ($err, $msg) = $ccdb->AddActivity ({
462           name => $activity,
463           pvob => $pvob,
464         });
465         
466         return ($err, $msg)
467           if $err;
468       } # unless
469       
470       # Link them (if not already linked)
471       %existingRec = $ccdb->GetBaselineActivityXref (
472         $oplog{name_p}, $activity, $pvob
473       );
474     
475       unless (%existingRec) {
476         ($err, $msg) = $ccdb->AddBaselineActivityXref ({
477           baseline => $oplog{name_p},
478           activity => $activity,
479           pvob     => $pvob,
480         });
481   
482         if ($err) {
483           $log->warn ("Unable to add baseline_activity_xref:$output[0]\@"
484                     . "$pvobTag baseline:$oplog{name_p} activity:$_ (Error:"
485                     . "$err)\n$msg");
486         
487           return ($err, $msg);
488         } # if
489         
490         $totals{'Baseline/Activity Xrefs added'}++;
491       } # unless
492     } # foreach
493   } elsif ($operation eq 'rmcheckpoint') {
494     $totals{'Baselines deleted'}++;
495     
496     return $ccdb->DeleteBaselineOID ($oplog{checkpoint_oid});
497   } # if
498   
499   return;  
500 } # ProcessBaseline
501
502 sub ProcessElement ($$%) {
503   my ($operation, $vob, %oplog) = @_;
504   
505   return
506     unless $oplog{version_oid};
507   
508   my $elementVersion = $Clearcase::CC->oid2name ($oplog{version_oid}, $vob);
509   my ($element, $version) = split /$Clearcase::SFX/, $elementVersion;
510   
511   # Remove VOBTAG_PREFIX from $element
512   $element = '/' . Clearcase::vobname $element;
513   
514   my $cmd = "describe -fmt \"%[activity]Xp\" oid:$oplog{version_oid}\@"
515           . Clearcase::vobtag $vob;
516   
517   my ($status, @output) = $Clearcase::CC->execute ($cmd);
518   
519   if ($status) {
520     $log->err ("Unable to execute command: $cmd (Status: $status)"
521             . join ("\n", @output));
522     return;            
523   } # if
524   
525   # If this operation is not attached to an activity then we're not interested.
526   return
527     unless $output[0];
528   
529   my ($activity, $pvob) = split /\@/, $output[0];
530   
531   # Remove leading "activity:"
532   $activity = substr $activity, 9;
533   
534   # Fix $pvob
535   $pvob = Clearcase::vobname $pvob;
536     
537   my ($err, $msg, %existingRec);
538   
539   if ($operation eq 'checkin'
540    or $operation eq 'checkout') {
541     %existingRec = $ccdb->GetChangeset ($activity, $element, $version, $pvob);
542     
543     unless (%existingRec) {
544       my $create_time = $oplog{create_time};
545
546       # Create time from Multisite are of the format: 2008-10-15T18:48:39Z
547       $create_time =~ s/T/ /;
548       $create_time =~ s/Z//;
549     
550       ($err, $msg) = $ccdb->AddChangeset ({
551         activity => $activity,
552         element  => $element,
553         version  => $version,
554         pvob     => $pvob,
555         created  => $create_time,  
556       });
557     
558       if ($err) {
559         $log->err ("Unable to AddChangeset ($activity, $element, $version, " 
560                  . "$pvob)\n$msg");
561               
562          return ($err, $msg);
563       } # if
564       
565       # Update Activity's submitted field (if this create time gt submitted)
566       my %activity = $ccdb->GetActivity ($activity, $pvob);
567       
568       if (%activity) {
569         $activity{submitted} ||= $create_time;
570         
571         if ($create_time ge $activity{submitted}) {
572           $activity{submitted} = $create_time;
573           
574           my ($err, $msg) = $ccdb->UpdateActivity (
575             $activity,
576             $pvob,
577             \%activity,
578           );
579           
580           $log->err ("Unable to update activity: $activity pvob: $pvob - "
581                   . " submitted: $create_time")
582             if $err;
583         } # if
584       } # if
585
586       $totals{'Changesets added'}++;
587     } # unless
588   } elsif ($operation eq 'uncheckout'
589         or $operation eq 'rmver') {
590     %existingRec = $ccdb->GetChangeset ($activity, $element, $version, $pvob);
591     
592     if (%existingRec) {
593       ($err, $msg) = $ccdb->DeleteChangeset (
594         $activity,
595         $element,
596         $version,
597         $pvob,
598       );
599       
600       if ($err) {
601         $log->err ("Unable to DeleteChangeset ($activity, $element, $version, " 
602                  . "$pvob)\n$msg");
603                 
604         return ($err, $msg);
605       } # if
606
607       $totals{'Changesets deleted'}++;
608     } # if
609   } elsif ($operation eq 'rmelem') {
610     %existingRec = $ccdb->GetChangeset ($activity, $element, $version, $pvob);
611     
612     if (%existingRec) {
613       ($err, $msg) = $ccdb->DeleteElementAll ($element);
614
615       if ($err) {
616         $log->err ("Unable to DeleteElementAll ($element)\n$msg");
617                 
618         return ($err, $msg);
619       } # if
620
621       $totals{'Elements removed'}++;
622     } # if
623   } # if
624   
625   return;
626 } # ProcessElement
627
628 sub ProcessRename ($%) {
629   my ($vob, %oplog) = @_;
630   
631   return 
632     unless $oplog{comment};
633     
634   my $object;
635     
636   # Parse comment to find what got renamed and the from and to names
637   if ($oplog{comment} =~ /Changed name of (.+?) from \"(\S+)\" to \"(\S+)\"/) {
638        $object = $1;
639     my $from   = $2;
640     my $to     = $3;
641     
642     # Only interested in these objects
643     return
644       unless $object =~ /activity/i or
645              $object =~ /baseline/i or
646              $object =~ /stream/i;
647              
648     my %update = (
649       name => $to,
650     );
651     
652     my $method = 'Update' . ucfirst $object;
653     
654     my ($err, $str) = $ccdb->$method ($from, $vob, \%update);
655     
656     if ($err) {
657       $log->err ("Unable to rename $object from $from -> $to (pvob:$vob");
658              
659       return;
660     } # if;
661   } # if
662
663   if ($object eq 'activity') {
664     $totals{'Activities renamed'}++;
665   } elsif ($object eq 'baseline') {
666     $totals{'Baselines renamed'}++;
667   } elsif ($object eq 'stream') {
668     $totals{'Streams renamed'}++;
669   } # if  
670
671   return;
672 } # ProcessRename
673
674 sub ProcessOperation ($$%) {
675   my ($operation, $vob, %oplog) = @_;
676   
677   # For now let's only process the activity opcodes... We'll add more later.
678   my @interestingOpcodes = (
679     'checkin',
680     'checkout',
681     'mkactivity',
682     'mkcheckpoint',
683     'rename',
684     'rmactivity',
685     'rmcheckpoint',
686     'rmelem',
687     'rmver',
688     'uncheckout',
689 #    'mkattr',
690 #    'mkhlink',
691 #    'setpvar',
692   );  
693
694   return 
695     unless InArray $operation, @interestingOpcodes;
696     
697   if ($operation eq 'mkactivity'
698    or $operation eq 'rmactivity') {
699     return ProcessActivity ($operation, $vob, %oplog);
700   } elsif ($operation eq 'mkcheckpoint'
701         or $operation eq 'rmcheckpoint') {
702     return ProcessBaseline ($operation, $vob, %oplog);
703   } elsif ($operation eq 'checkin'
704         or $operation eq 'checkout'
705         or $operation eq 'rmelem'
706         or $operation eq 'rmver'
707         or $operation eq 'uncheckout') {
708     return ProcessElement ($operation, $vob, %oplog);
709   } elsif ($operation eq 'rename') {
710     return ProcessRename ($vob, %oplog);
711   } # if
712 } # ProcessOperation
713
714 sub ProcessOplog (%) {
715   my (%vob) = @_;
716   
717   # Start dumpoplog off at the appropriate oplog number
718   my $cmd = 'multitool dumpoplog -long -invob '
719           . Clearcase::vobtag ($vob{name})
720           . " -from $vob{epoch}";
721
722   # Start a pipe
723   open my $oplog, "$cmd|"
724     or error "Cannot execute $cmd", 1;
725
726   my $inRecord;
727   
728   while (<$oplog>) {
729     # Look for the next oplog entry
730     if (/(\d+):/) {
731       $vob{epoch} = $1;
732       $inRecord = 1;
733       next;
734     } elsif (/^$/) {
735       $inRecord = 0;
736       next;
737     } elsif (!$inRecord) {
738       next;
739     } # if
740     
741     my ($operation, $status, @output);
742     
743     if (/op= (\S+)/) {
744       $operation = $1;
745     } else {
746       $operation = '';
747     } # if
748
749     ProcessOperation $operation, $vob{name}, ParseOplog $oplog;
750
751     # Update vob's last_oplog
752     my ($err, $msg) = $ccdb->UpdateVob ($vob{name}, \%vob);
753     
754     $log->err ("Unable to update vob:$vob{name}\'s epoch to "
755              . $vob{epoch})
756       if $err;      
757   } # while
758   
759   close $oplog;
760   
761   return;
762 } # ProcessOplog
763
764 sub ProcessVob ($) {
765   my ($name) = @_;
766   
767   my ($err, $msg);
768
769   my %vob = $ccdb->GetVob ($name);
770   
771   $log->msg ("Processing vob:$name ($vob{type})");
772     
773   unless (%vob) {
774     my $vob = Clearcase::Vob->new (Clearcase::vobtag $name);
775     
776     ($err, $msg) = $ccdb->AddVob ({
777       name => $name,
778       type => $vob->vob_registry_attributes !~ /ucmvob/ ? 'base' : 'ucm', 
779     });
780   
781     if ($err) {
782       $log->err ("Unable to add vob $name (Error: $err)\n$msg");
783     } else {
784       $totals{'Vobs added'}++;
785     } # if
786
787     %vob = $ccdb->GetVob ($name);
788   } # unless
789   
790   ProcessOplog %vob;
791 } # ProcessVob
792
793 sub EndProcess {
794   $totals{Errors}   = $log->errors;
795   $totals{Warnings} = $log->warnings;
796
797   Stats \%totals, $log;
798 } # EndProcess
799
800 # Main
801 local $| = 1;
802
803 my $startTime = time;
804
805 GetOptions (
806   \%opts,
807   'verbose' => sub { set_verbose },
808   'usage'   => sub { Usage },
809   'vob=s',
810 ) or Usage "Unknown option";
811
812 $log = Logger->new;
813
814 $SIG{__DIE__} = $SIG{INT} = $SIG{ABRT} = $SIG{QUIT} = $SIG{USR2} = 'EndProcess';
815
816 my @vobs;
817
818 if ($opts{vob}) {
819   push @vobs, $opts{vob};
820 } else {
821   # Do UCM vobs first
822   my (@ucmvobs, @basevobs);
823   
824   push @ucmvobs, $$_{name}
825     foreach ($ccdb->FindVob ('*', 'ucm'));
826   
827   # Add on base vobs
828   push @basevobs, $$_{name}
829     foreach ($ccdb->FindVob ('*', 'base'));
830     
831   push @vobs, $_ foreach (sort @ucmvobs);
832   push @vobs, $_ foreach (sort @basevobs);
833 } # if
834
835 if (@vobs == 1) {
836   $log->msg ('1 vob to process');
837 } else {
838   $log->msg (scalar @vobs . ' vobs to process');
839 } # if
840
841 foreach (@vobs) {
842   ProcessVob $_;
843   
844   $totals{'Vobs processed'}++;
845 } # foreach
846
847 display_duration $startTime, $log;
848
849 $totals{Errors}   = $log->errors;
850 $totals{Warnings} = $log->warnings;
851
852 Stats \%totals, $log;