Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3042 lines
100 KiB

  1. #!perl -w
  2. package raiseall;
  3. use strict;
  4. use Win32::File qw(GetAttributes SetAttributes);
  5. use File::Basename qw(basename);
  6. use Carp;
  7. use lib $ENV{ "RazzleToolPath" };
  8. use lib $ENV{ "RazzleToolPath" }. "\\PostBuildScripts";
  9. use PbuildEnv;
  10. use ParseArgs;
  11. use Logmsg;
  12. use cksku;
  13. use cklang;
  14. use BuildName;
  15. use GetIniSetting;
  16. # Set the script name for logging
  17. $ENV{ "SCRIPT_NAME" } = "raiseall.pl";
  18. # Platform = Architecutre + Debug_Type (e.g. x86fre)
  19. my( $Language, $Build_Number, $Architecture, $Debug_Type, $Time_Stamp,
  20. $Platform, $Quality, $Op, $fOverride_Safe, $fSafe, $fReplaceAtNumberedLink );
  21. my @ConglomeratorSkus = ('symbolcd', 'ddk', 'hal', 'ifs', 'pdk', 'mantis', 'opk', 'mui', 'wsrm');
  22. my %Ordered_Qualities = ( pre => 1,
  23. bvt => 2,
  24. tst => 3,
  25. sav => 4,
  26. idw => 5,
  27. ids => 6,
  28. idc => 7 );
  29. #########################################################################
  30. # #
  31. # Server/Client split sku associations #
  32. # #
  33. my @ServerSkus = ( 'ads', 'bin', 'bla', 'dtc', 'sbs', 'srv', 'sym', 'resources', @ConglomeratorSkus );
  34. my @ClientSkus = ( 'bin', 'sym', 'winpe', 'upgadv', 'resources', @ConglomeratorSkus );
  35. #my @ClientSkus = ( 'bin', 'per', 'pro', 'sym', 'winpe', 'upgadv', 'resources', @ConglomeratorSkus );
  36. # #
  37. # #
  38. #########################################################################
  39. sub globex($);
  40. sub Main {
  41. my $local_release_path = GetReleasePath();
  42. if ( !defined $local_release_path ) {
  43. errmsg( "Unable to determine release path on current machine, exiting." );
  44. return;
  45. }
  46. # Search for builds availabe to release
  47. my $build_criteria = "$local_release_path\\$Language\\$Build_Number.$Platform.$ENV{_BuildBranch}.$Time_Stamp";
  48. my @available_builds = grep { $_ if ( -d $_ ) } globex $build_criteria;
  49. #
  50. # Check for conglomerator skus
  51. #
  52. #push @available_builds, grep { $_ if ( -d $_ ) } globex "$local_release_path\\$Language\\$Build_Number.$ENV{_BuildBranch}.$Time_Stamp";
  53. push @available_builds, grep { $_ if ( -d $_ ) } globex "$local_release_path\\$Language\\$Build_Number.$ENV{_BuildBranch}";
  54. #
  55. # Check for language neutral skus
  56. #
  57. push @available_builds, grep { $_ if ( -d $_ ) } globex "$local_release_path\\misc\\$Build_Number.$ENV{_BuildBranch}";
  58. dbgmsg( "Available builds: " );
  59. dbgmsg( $_ ) foreach @available_builds;
  60. if ( !@available_builds ) {
  61. if ( $! != 2 ) {
  62. errmsg("Error querying for builds matching $build_criteria ($!)");
  63. }
  64. else {
  65. errmsg("No builds found matching $build_criteria");
  66. }
  67. return;
  68. }
  69. if ( $Op eq 'raise' ) {
  70. return RaiseAll( $local_release_path, \@available_builds );
  71. }
  72. elsif ( $Op eq "lower" ) {
  73. return LowerAll( $local_release_path, \@available_builds );
  74. }
  75. else {
  76. errmsg( "Internal error -- operation = \"$Op\"" );
  77. return;
  78. }
  79. }
  80. sub RaiseAll {
  81. my $local_release_path = shift;
  82. my $all_available_builds = shift;
  83. # Build number is required
  84. if ( $Build_Number eq '*' ) {
  85. errmsg( "Must specify build number." );
  86. return;;
  87. }
  88. # Quality is required
  89. if ( !defined $Quality ) {
  90. errmsg( "Must specify a quality." );
  91. return;
  92. }
  93. # For raiseall operations we only want to deal with the
  94. # newest build of a set of builds with the same build number
  95. my @available_builds;
  96. my ($cur_build_no_ts, $last_build_no_ts) = ('foo', 'foo');
  97. my @sorted_all_available_builds = sort @$all_available_builds;
  98. for ( my $i = $#sorted_all_available_builds; $i >= 0; $i-- ) {
  99. # Standard builds
  100. if ( $sorted_all_available_builds[$i] =~ /(\d+\.[^\.]+\.[^\.]+)\.\d+-\d+$/ ) {
  101. $cur_build_no_ts = $1;
  102. }
  103. # Conglomerator builds (no time-stamp currently)
  104. elsif ( $sorted_all_available_builds[$i] =~ /([^\\]+)\\(\d+\.[^\.]+)(?:\.\d+-\d+)?$/ ) {
  105. $cur_build_no_ts = "$1\\$2";
  106. }
  107. else {
  108. errmsg( "Unrecognized build format: $sorted_all_available_builds[$i]" );
  109. return;
  110. }
  111. if ( $cur_build_no_ts ne $last_build_no_ts ) {
  112. push @available_builds, $sorted_all_available_builds[$i];
  113. $last_build_no_ts = $cur_build_no_ts;
  114. }
  115. else {
  116. dbgmsg( "Skipping older build: ". basename($sorted_all_available_builds[$i]) );
  117. }
  118. }
  119. # Check available builds for outstanding release
  120. # errors; if there are none, set the *.qly file
  121. # to the appropriate value
  122. my $build;
  123. my $srvrel_errors = 0;
  124. foreach $build ( @available_builds ) {
  125. my $sBldName = basename($build);
  126. if ( -e "$ENV{temp}\\$sBldName\\SrvRelFailed.txt" ) {
  127. errmsg( "SrvRel failures on ". $sBldName );
  128. errmsg( "Please fix errors and remove $ENV{temp}\\$sBldName\\SrvRelFailed.txt" );
  129. $srvrel_errors++;
  130. }
  131. # Update the QLY file if this is not a conglomerator build
  132. elsif ( IsOSBuild( $build ) &&
  133. !ModifyQlyFile( basename($build), $Quality, "$build\\build_logs" ) ) {
  134. errmsg( "Failed updating the QLY file for ". basename($build). ", exiting." );
  135. return;
  136. }
  137. }
  138. if ( $srvrel_errors ) {
  139. errmsg( "Aborting due to $srvrel_errors SrvRel failure". ($srvrel_errors > 1?"s.":".") );
  140. return;
  141. }
  142. #
  143. # Setup parameters needed in ForEachBuild functions
  144. #
  145. my @accessed_shares = ();
  146. my %named_parameters = (
  147. ComputerName => $ENV{COMPUTERNAME},
  148. AccessedShares => \@accessed_shares,
  149. Quality => $Quality,
  150. Language => $Language,
  151. OverrideSafe => $fOverride_Safe
  152. );
  153. #
  154. # Setup the local shares on the release servers
  155. #
  156. logmsg( "Creating release shares..." );
  157. if ( !ForEachBuild( \&CreateSecuredBuildShare,
  158. \%named_parameters,
  159. $Language,
  160. \@available_builds,
  161. $fSafe ) ) {
  162. errmsg( "Failed to create shares, exiting." );
  163. return;
  164. }
  165. # Print out shares created / checked if in debug mode
  166. dbgmsg( "$_ share created/verified." ) foreach @accessed_shares;
  167. #
  168. # Do the DFS work
  169. #
  170. my ( $dfs_map, $dfs_lock ) = GetDfsAccess();
  171. return if ( !defined $dfs_map );
  172. # Next few functions need DFS access
  173. $named_parameters{ DfsMap } = $dfs_map;
  174. logmsg( "Checking numbered shares for older timestamps..." );
  175. if ( !ForEachBuild( \&UnmapInconsistentNumberedLinks,
  176. \%named_parameters,
  177. $Language,
  178. \@available_builds,
  179. $fSafe ) ) {
  180. errmsg( "Problem occurred checking numbered shares" );
  181. $dfs_lock->Unlock();
  182. return;
  183. }
  184. logmsg( "Finding/lowering any old DFS links at same quality..." );
  185. if ( !ForEachBuild( \&RemoveOldLatestDotQlyDfsLinks,
  186. \%named_parameters,
  187. $Language,
  188. \@available_builds,
  189. $fSafe ) ) {
  190. errmsg( "Problem verifying/lowering latest.$Quality and associated DFS links" );
  191. $dfs_lock->Unlock();
  192. return;
  193. }
  194. logmsg( "Creating/verifying DFS links..." );
  195. if ( !ForEachBuild( \&CreateDfsMappings,
  196. \%named_parameters,
  197. $Language,
  198. \@available_builds,
  199. $fSafe ) ) {
  200. errmsg( "Problem creating/verifying DFS links." );
  201. $dfs_lock->Unlock();
  202. return;
  203. }
  204. #
  205. # Do symbol server ops
  206. #
  207. # Override standard SKUs with our own
  208. @{$named_parameters{ DefinedSkus }} = ('sym');
  209. # Only want 'sym' on OS builds, not conglomerator builds
  210. my @os_builds = grep { $_ if IsOSBuild($_) } @available_builds;
  211. logmsg( "Checking symbol server state..." );
  212. if ( !ForEachBuild( \&CreateSymbolServerLink,
  213. \%named_parameters,
  214. $Language,
  215. \@os_builds,
  216. $fSafe ) ) {
  217. errmsg( "Problem updating/verifying symbols server DFS links." );
  218. $dfs_lock->Unlock();
  219. return;
  220. }
  221. logmsg( "Flushing outstanding DFS operations..." );
  222. if ( !(tied %$dfs_map)->Flush() ) {
  223. errmsg( "Problem processing DFS commands." );
  224. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  225. dfs_lock->Unlock();
  226. return;
  227. }
  228. #
  229. # Release the inter-machine lock
  230. #
  231. $dfs_lock->Unlock();
  232. # Done if we are not touching latest.*
  233. return 1 if ( $fSafe );
  234. #
  235. # If we are raising to BVT quality, check INI
  236. # file to determine if we should run BVT tests
  237. #
  238. # TODO: a release server may have more than one architecture
  239. # and may have just a fre or chk version, so this call
  240. # probably needs to be put in its own function and
  241. # used with ForEachBuild(), specifying many more
  242. # parameters than just -l and -p
  243. #
  244. if ( lc $Quality eq "bvt") {
  245. my $run_boot_test = &GetIniSetting::GetSettingQuietEx( $ENV{ "_BuildBranch" }, $Language, "RunBVT" );
  246. if (defined $run_boot_test &&
  247. lc $run_boot_test eq "true") {
  248. logmsg( "Starting BVT tests..." );
  249. # run tests - spit out warning if there are problems
  250. if ( !ExecuteProgram::ExecuteProgram("perl $ENV{RazzleToolPath}\\postbuildscripts\\assignbvt.pl -l:$Language -p:". ($Architecture eq '*'?"":$Architecture)) ) {
  251. wrnmsg( "Problem accessing bvt machine (error returned from assignbvt.pl)." );
  252. wrnmsg( " ". ExecuteProgram::GetLastCommand() );
  253. wrnmsg( " $_" ) foreach ExecuteProgram::GetLastOutput();
  254. }
  255. }
  256. }
  257. return 1;
  258. }
  259. sub LowerAll {
  260. my $local_release_path = shift;
  261. my $all_available_builds = shift;
  262. my @available_builds = sort @$all_available_builds;
  263. # Build number is normally required
  264. if ( $Build_Number eq '*' ) {
  265. if ( defined $fOverride_Safe ) {
  266. wrnmsg( "Forcibly lowering all builds on machine." );
  267. }
  268. else {
  269. errmsg( "Must specify build number." );
  270. return;;
  271. }
  272. }
  273. # Setting quality is pointless -- let the user know
  274. wrnmsg( "Quality flag '$Quality' ignored for lower operation." ) if ( defined $Quality );
  275. # Setting 'safe' is also pointless
  276. wrnmsg( "-safe ignored for lowerall operation." ) if ( defined $fSafe );
  277. # remove the *.qly file for all OS (not conglomerator) builds
  278. logmsg( "Removing the *.qly file..." );
  279. my $build;
  280. foreach $build ( @available_builds ) {
  281. if ( IsOSBuild( $build ) &&
  282. !DeleteQlyFile( "$build\\build_logs" ) ) {
  283. wrnmsg( "Failed removing the QLY file from ". basename($build) );
  284. return;
  285. }
  286. }
  287. #
  288. # Setup parameters needed in ForEachBuild functions
  289. #
  290. my @accessed_shares = ();
  291. my %named_parameters = (
  292. ComputerName => $ENV{COMPUTERNAME},
  293. AccessedShares => \@accessed_shares,
  294. Language => $Language,
  295. OverrideSafe => $fOverride_Safe
  296. );
  297. #
  298. # Lower the local shares on the release servers
  299. #
  300. logmsg( "Lowering release shares..." );
  301. if ( !ForEachBuild( \&LowerBuildShare,
  302. \%named_parameters,
  303. $Language,
  304. \@available_builds,
  305. $fSafe ) ) {
  306. errmsg( "Failed to remove shares, exiting." );
  307. return;
  308. }
  309. # Print out shares deleted / checked if in debug mode
  310. dbgmsg( "$_ share removed/verified." ) foreach @accessed_shares;
  311. #
  312. # Do the DFS work
  313. #
  314. my ( $dfs_map, $dfs_lock ) = GetDfsAccess();
  315. return if ( !defined $dfs_map );
  316. # Need DFS access in the next few functions
  317. $named_parameters{ DfsMap } = $dfs_map;
  318. logmsg( "Removing appropriate DFS links..." );
  319. if ( !ForEachBuild( \&RemoveDfsMappings,
  320. \%named_parameters,
  321. $Language,
  322. \@available_builds,
  323. $fSafe ) ) {
  324. errmsg( "Problem verifying/removing DFS links" );
  325. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  326. $dfs_lock->Unlock();
  327. return;
  328. }
  329. #
  330. # Do symbol server ops
  331. #
  332. # Note: this is explicitly the last step taken as
  333. # we special-case the symbol server "sku" to
  334. # only remove the link if it is the last one
  335. # remaining. This is done so that the
  336. # symbol link is torn down only when all
  337. # instances of the correspondig build have
  338. # been lowered.
  339. #
  340. # Override standard SKUs with our own
  341. @{$named_parameters{ DefinedSkus }} = ('sym');
  342. # Only want 'sym' on OS builds, not conglomerator builds
  343. my @os_builds = grep { $_ if IsOSBuild( $_ ) } @available_builds;
  344. logmsg( "Checking symbol server state..." );
  345. if ( !ForEachBuild( \&RemoveSymbolServerLink,
  346. \%named_parameters,
  347. $Language,
  348. \@os_builds,
  349. $fSafe ) ) {
  350. errmsg( "Problem removing symbols server DFS links." );
  351. $dfs_lock->Unlock();
  352. return;
  353. }
  354. logmsg( "Flushing outstanding DFS operations..." );
  355. if ( !(tied %$dfs_map)->Flush() ) {
  356. errmsg( "Problem processing DFS commands." );
  357. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  358. dfs_lock->Unlock();
  359. return;
  360. }
  361. #
  362. # Release the inter-machine lock
  363. #
  364. $dfs_lock->Unlock();
  365. return 1;
  366. }
  367. sub GetDfsAccess {
  368. my $dfs_root = &GetIniSetting::GetSettingQuietEx( $ENV{ "_BuildBranch" }, $Language, "DFSRootName" );
  369. if ( !defined $dfs_root ) {
  370. errmsg( "Unable to find 'DFSRootName' in INI file for $ENV{_BuildBranch}." );
  371. return;
  372. }
  373. dbgmsg( "Using DFS root $dfs_root" );
  374. my $lock_location = &GetIniSetting::GetSettingQuietEx( $ENV{ "_BuildBranch" }, $Language, "DFSSemaphore" );
  375. if ( !$lock_location ) {
  376. errmsg( "Unable to find DFSSemaphore value in INI file." );
  377. return;
  378. }
  379. # Still using a DFS hash that only gets updated on first
  380. # retrieval -- therefore we need to do our best to have
  381. # only one machine at a time updating the DFS. To that
  382. # extent we have setup a lock mechanism on a globally
  383. # accessible share (with 1 minute MAXIMUM intervals
  384. # between checks - 60000 ms = 1 min)
  385. my $synchronize_lock = new MultiMachineLock ($lock_location, 60000);
  386. if ( !defined $synchronize_lock ) {
  387. errmsg( "Problem acquiring lock using $lock_location" );
  388. return;
  389. }
  390. else {
  391. logmsg( "Acquiring lock for exclusive DFS access..." );
  392. while ( !$synchronize_lock->Lock() ) {
  393. # If we timed out, wait some more
  394. if ( 2 == $synchronize_lock->GetLastError() ) {
  395. # Last error message should contain the machine we are waiting on
  396. timemsg( $_ ) foreach ( split /\n/, $synchronize_lock->GetLastErrorMessage() );
  397. }
  398. else {
  399. errmsg ("Failed to acquire lock.");
  400. errmsg( $_ ) foreach ( split /\n/, $synchronize_lock->GetLastErrorMessage() );
  401. return;
  402. }
  403. }
  404. }
  405. # access DFS through a tied hash
  406. logmsg( "Accessing DFS information..." );
  407. my %dfs_map;
  408. if ( ! tie %dfs_map, 'StaticDfsMap', $dfs_root ) {
  409. errmsg( "Error accessing DFS." );
  410. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  411. $synchronize_lock->Unlock();
  412. return;
  413. }
  414. return (\%dfs_map, $synchronize_lock);
  415. }
  416. sub IsOSBuild {
  417. my $build_name = shift;
  418. return scalar($build_name =~ /\d+\.[^\.]+\.[^\.]+\.\d+-\d+$/)?1:0;
  419. }
  420. sub AllowQualityTransition {
  421. my $last_quality = shift;
  422. my $cur_quality = shift;
  423. if ( !exists $Ordered_Qualities{lc $last_quality} ||
  424. !exists $Ordered_Qualities{lc $cur_quality} ) {
  425. return;
  426. }
  427. # Allow transition to sav from any previous quality
  428. elsif ( lc $cur_quality eq 'sav' ) {
  429. return 1;
  430. }
  431. # Allow transition from pre/bvt to anything else
  432. elsif ( lc $last_quality eq 'pre' ||
  433. lc $last_quality eq 'bvt' ) {
  434. return 1;
  435. }
  436. # Don't allow transition from anything else to pre/bvt
  437. elsif ( lc $cur_quality eq 'pre' ||
  438. lc $cur_quality eq 'bvt' ) {
  439. return;
  440. }
  441. # Otherwise allow transitions based on order specified in %Ordered_Qualities
  442. elsif ( $Ordered_Qualities{lc $cur_quality} >= $Ordered_Qualities{lc $last_quality} ) {
  443. return 1;
  444. }
  445. else {
  446. return;
  447. }
  448. }
  449. sub CreateSymbolServerLink {
  450. my $named_parameters = shift;
  451. my ( $dfs_access, $build_number,
  452. $full_build_path, $branch, $platform, $quality, $language,
  453. $build_name ) =
  454. GetNamedParameters( $named_parameters,
  455. q(DfsMap), q(BuildNumber),
  456. q(FullBuildPath), q(Branch), q(Platform), q(Quality), q(Language),
  457. q(BuildName) );
  458. return if ( !defined $dfs_access );
  459. # Get current symbol server share
  460. my ( $current_symsrv_share, $sub_path ) = GetShareName( $named_parameters );
  461. if ( !defined $current_symsrv_share ) {
  462. dbgmsg( "No symbol server share defined for $branch -- skipping" );
  463. return 1;
  464. }
  465. # Default writeable share is just the $'d version of the non-writeable one
  466. my $current_writeable_symsrv_path = $current_symsrv_share;
  467. $current_writeable_symsrv_path =~ s/(.)$/$1\$/;
  468. # attach the subpath (if there is one)
  469. $current_writeable_symsrv_path .= "\\$sub_path" if ( $sub_path );
  470. # Default build_logs directory is a sibling to default symbols share
  471. my $current_symsrv_build_logs = $current_writeable_symsrv_path;
  472. $current_symsrv_build_logs =~ s/[^\\]+$/build_logs/;
  473. logmsg( "Verifying symbol server has updated shares for $platform..." );
  474. # Verify that the symbol server has the current build
  475. if ( ! -e $current_writeable_symsrv_path ) {
  476. wrnmsg( "Cannot see $current_writeable_symsrv_path ($!)." );
  477. return 1;
  478. }
  479. # Try to make the build_logs directory if it doesn't already exist;
  480. if ( ! -e $current_symsrv_build_logs &&
  481. ! ( mkdir $current_symsrv_build_logs, 0777 ) ) {
  482. errmsg( "Failed to create $current_symsrv_build_logs ($!)." );
  483. return;
  484. }
  485. # For some reason we maintain a QLY file in two locations
  486. if ( !ModifyQlyFile( $build_name, $quality, $current_writeable_symsrv_path ) ) {
  487. wrnmsg ( "Problem verifying/creating $quality.qly file on $current_writeable_symsrv_path." );
  488. #return;
  489. }
  490. if ( !ModifyQlyFile( $build_name, $quality, $current_symsrv_build_logs ) ) {
  491. wrnmsg ( "Problem verifying/creating $quality.qly file on $current_symsrv_build_logs." );
  492. #return;
  493. }
  494. # Done if going to SAV quality
  495. return 1 if ( lc $quality eq 'sav' );
  496. #
  497. # DFS work
  498. #
  499. # Check if we need to lower any latest.* shares for this build
  500. if ( !RemoveOldLatestDotQlyDfsLinks( $named_parameters ) ) {
  501. errmsg( "Problem verifying/lowering latest symbol server shares for $platform." );
  502. return;
  503. }
  504. # Remove any older numbered shares (older in timestamp)
  505. if ( !UnmapInconsistentNumberedLinks ( $named_parameters ) ) {
  506. errmsg( "Problem occurred checking numbered shares" );
  507. return;
  508. }
  509. # Now create the DFS link if necessary
  510. if ( !CreateDfsMappings( $named_parameters ) ) {
  511. errmsg( "Problem verifying/creating symbol server DFS links for $platform." );
  512. return;
  513. }
  514. return 1;
  515. }
  516. sub RemoveSymbolServerLink {
  517. my $named_parameters = shift;
  518. my ( $dfs_access, $platform, $branch,
  519. $build_number, $build_name ) =
  520. GetNamedParameters( $named_parameters,
  521. q(DfsMap), q(Platform), q(Branch),
  522. q(BuildNumber), q(BuildName) );
  523. return if ( !defined $dfs_access );
  524. # Get current symbol server share
  525. my ( $current_symsrv_share, $sub_path ) = GetShareName( $named_parameters );
  526. if ( !defined $current_symsrv_share ) {
  527. dbgmsg( "No symbol server share defined for $branch -- skipping." );
  528. return 1;
  529. }
  530. # Default writeable share is just the $'d version of the non-writeable one
  531. my $current_writeable_symsrv_path = $current_symsrv_share;
  532. $current_writeable_symsrv_path =~ s/(.)$/$1\$/;
  533. # attach the subpath (if there is one)
  534. $current_writeable_symsrv_path .= "\\$sub_path" if ( $sub_path );
  535. # Default build_logs directory is a sibling to default symbols share
  536. my $current_symsrv_build_logs = $current_writeable_symsrv_path;
  537. $current_symsrv_build_logs =~ s/[^\\]+$/build_logs/;
  538. # Now attach subpath (if any) to standard symbol share
  539. $current_symsrv_share .= "\\$sub_path" if ( $sub_path );
  540. # Need all prior DFS actions to have completed
  541. if ( !(tied %$dfs_access)->Flush() ) {
  542. errmsg( "Problem processing DFS commands." );
  543. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  544. return;
  545. }
  546. # We only remove the symbol link when it is the last
  547. # link left -- we will assume if it is the last
  548. # link under the numbered share that it is the last
  549. # relevant link under the latest.* shares as well
  550. $named_parameters->{ BuildID } = $build_number;
  551. if ( !IsLastLinkUnderParent( $named_parameters ) ) {
  552. dbgmsg( "$build_name: leaving symbol links untouched" );
  553. return 1;
  554. }
  555. logmsg( "Verifying symbol server links have been removed for $platform..." );
  556. #
  557. # NOTE: for now we will disable the removal of the .qly flag from the symbols shares. That way, if
  558. # a build was raised to IDx quality then its symbols will stay at IDx quality even after its
  559. # release shares are lowered. Symbols at IDx quality are not removed by deletebuild.cmd
  560. #
  561. # Remove the QLY files
  562. #
  563. # For some reason we maintain a QLY file in two locations
  564. #dbgmsg ( "Deleting QLY file from $current_writeable_symsrv_path" );
  565. #if ( !DeleteQlyFile( $current_writeable_symsrv_path ) ) {
  566. # errmsg ( "Problem reading/removing QLY file from $current_writeable_symsrv_path." );
  567. # return;
  568. #}
  569. #dbgmsg( "Deleting QLY file from $current_symsrv_build_logs" );
  570. #if ( !DeleteQlyFile( $current_symsrv_build_logs ) ) {
  571. # errmsg ( "Problem reading/removing QLY file from $current_symsrv_build_logs." );
  572. # return;
  573. #}
  574. #
  575. # Now remove the DFS link
  576. #
  577. if ( !RemoveDfsMappings( $named_parameters ) ) {
  578. errmsg( "Problem verifying/removing symbol server DFS links for $platform." );
  579. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  580. return;
  581. }
  582. return 1;
  583. }
  584. sub CreateDfsMappings {
  585. my $named_parameters = shift;
  586. my ($dfs_access, $computer_name, $quality, $build_number, $language,
  587. $platform, $time_stamp, $branch, $sku, $fsafe, $fconglomerator_build,
  588. $flang_neutral_build ) =
  589. GetNamedParameters( $named_parameters,
  590. q(DfsMap), q(ComputerName), q(Quality), q(BuildNumber), q(Language),
  591. q(Platform), q(TimeStamp), q(Branch), q(Sku), q(SafeFlag), q(IsConglomeratorBuild),
  592. q(IsLangNeutralBuild) );
  593. return if ( !defined $dfs_access );
  594. # Done if going to SAV quality
  595. return 1 if ( lc $quality eq 'sav' );
  596. # Done if we are at a different and LOWER quality than other builds
  597. # in our numbered links (e.g. if our build number has been
  598. # raised to TST and we are just being raised to BVT, we don't
  599. # want to put ourselves in the number link yet)
  600. if ( IsQualityHigherAtNumberedLink( $named_parameters )) {
  601. wrnmsg( "Not creating DFS links!" );
  602. return 1;
  603. }
  604. # latest.* DFS link
  605. # only gets created if the current branch is
  606. # responsible for producing the current SKU
  607. my $latest_dfs_link;
  608. $named_parameters->{ BuildID } = "latest.$quality";
  609. $latest_dfs_link = GetDfsLinkName( $named_parameters );
  610. if ( !defined $latest_dfs_link ) {
  611. errmsg( "Do not know how to create latest.* DFS links for $branch / $sku." );
  612. return;
  613. }
  614. # Create directory structure for $latest_dfs_link in the DFS replica servers
  615. if( !$fconglomerator_build && !$flang_neutral_build )
  616. {
  617. return if( !CreateDirsInReplicaServers($named_parameters));
  618. }
  619. # build number link
  620. $named_parameters->{ BuildID } = $build_number;
  621. my $numerical_dfs_link = GetDfsLinkName( $named_parameters );
  622. if ( !defined $numerical_dfs_link ) {
  623. errmsg( "Do not know how to create numbered DFS links for $branch / $sku." );
  624. return;
  625. }
  626. # Create directory structure for $numerical_dfs_link in the DFS replica servers
  627. if( !$fconglomerator_build && !$flang_neutral_build )
  628. {
  629. return if( !CreateDirsInReplicaServers($named_parameters));
  630. }
  631. # \\machine\share[\<subdir>] that makes up local release path
  632. my ( $machine_share, $sub_path ) = GetShareName( $named_parameters );
  633. if ( !defined $machine_share ) {
  634. errmsg( "Do not know how to create local shares for $branch / $sku." );
  635. return;
  636. }
  637. # Tack on the subpath to the share if it exists
  638. $machine_share .= "\\$sub_path" if ( $sub_path );
  639. # Do the DFS mappings
  640. dbgmsg( "Adding/Verifying: $numerical_dfs_link contains $machine_share" );
  641. if ( !($dfs_access->{$numerical_dfs_link} = $machine_share) ) {
  642. errmsg( "Problem mapping to DFS." );
  643. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  644. return;
  645. }
  646. # if lang=cov
  647. # map <branch>\usa\<buildNumber>\<archType>.cov to <branch>\cov\<buildNumber>\<archType>
  648. if( lc $language eq "cov" && !$fconglomerator_build && !$flang_neutral_build )
  649. {
  650. dbgmsg( "Adding/Verifying: <archType>.cov links contains $machine_share" );
  651. my $altCovLink = $numerical_dfs_link;
  652. $altCovLink =~ s/cov/usa/i;
  653. $altCovLink =~ s/$platform/$platform.cov/;
  654. if ( !($dfs_access->{$altCovLink} = $machine_share) ) {
  655. errmsg( "Problem mapping to DFS." );
  656. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  657. return;
  658. }
  659. }
  660. # Link language neutral builds to misc directory as well
  661. if ( $flang_neutral_build) {
  662. my $misc_dfs_link = $numerical_dfs_link;
  663. $misc_dfs_link =~ s/$language/misc/i;
  664. if ( !($dfs_access->{$misc_dfs_link} = $machine_share) ) {
  665. errmsg( "Problem mapping to DFS." );
  666. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  667. return;
  668. }
  669. }
  670. if ( defined $latest_dfs_link &&
  671. !$fsafe ) {
  672. dbgmsg( "Adding/Verifying: $latest_dfs_link contains $machine_share" );
  673. if ( !($dfs_access->{$latest_dfs_link} = $machine_share) ) {
  674. errmsg( "Problem mapping to DFS." );
  675. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  676. return;
  677. }
  678. }
  679. # Time for some special magic on the DFS servers:
  680. # hide/unhide directories and create *.BLD/*.SRV files.
  681. # Skip this step for conglomerator& neutral builds
  682. if ( !$fconglomerator_build && !$flang_neutral_build &&
  683. !MagicDFSSettings( $named_parameters ) ) {
  684. wrnmsg( "Problem verifying/writing to DFS server machines." );
  685. }
  686. return 1;
  687. }
  688. sub CreateDirsInReplicaServers
  689. {
  690. my $named_parameters = shift;
  691. foreach ( GetBldFileDfsPathNames( $named_parameters ) )
  692. {
  693. if( !(-e $_ ))
  694. {
  695. if( !ExecuteProgram::ExecuteProgram( "md $_" ) )
  696. {
  697. errmsg( " ". ExecuteProgram::GetLastCommand() );
  698. errmsg( " $_" ) foreach ExecuteProgram::GetLastOutput();
  699. return 0;
  700. }
  701. }
  702. }
  703. return 1;
  704. }
  705. sub IsQualityHigherAtNumberedLink {
  706. my $named_parameters = shift;
  707. my ( $dfs_access, $quality, $build_number, $branch, $sku ) =
  708. GetNamedParameters( $named_parameters,
  709. q(DfsMap), q(Quality), q(BuildNumber), q(Branch), q(Sku) );
  710. return if ( !defined $dfs_access );
  711. # build number link
  712. $named_parameters->{ BuildID } = $build_number;
  713. my $numerical_dfs_link = GetDfsLinkName( $named_parameters );
  714. # Don't know if quality is higher at numbered
  715. # link, if we don't know how to make the link
  716. return 0 if ( !defined $numerical_dfs_link );
  717. # If there are no builds, then we are OK
  718. return 0 if ( !exists $dfs_access->{$numerical_dfs_link} );
  719. my @current_builds = @{$dfs_access->{$numerical_dfs_link}};
  720. # Otherwise attempt to find a latest.* link for one of the shares
  721. foreach my $share_to_check ( @current_builds ) {
  722. my @links_to_share = @{ $dfs_access->{$share_to_check} };
  723. next if ( !@links_to_share );
  724. foreach my $link ( @links_to_share ) {
  725. my $build_id = GetBuildIdFromDfsLink( $named_parameters, $link );
  726. # Determine if this is a latest.* link
  727. if ( defined $build_id &&
  728. $build_id =~ /^latest\.(.*)$/ ) {
  729. # Compare quality with our current quality
  730. if ( lc $1 ne lc $quality &&
  731. !AllowQualityTransition( $1, $quality ) ) {
  732. wrnmsg( "Other builds linked to $build_number are at $1 quality:" );
  733. wrnmsg( "($share_to_check)." );
  734. return 1;
  735. }
  736. }
  737. }
  738. }
  739. # Did not find a latest.* higher than our current quality
  740. return 0;
  741. }
  742. sub UnmapInconsistentNumberedLinks {
  743. my $named_parameters = shift;
  744. my ($dfs_access, $build_number, $build_time, $branch, $sku,
  745. $quality) =
  746. GetNamedParameters( $named_parameters,
  747. q(DfsMap), q(BuildNumber), q(TimeStamp), q(Branch), q(Sku),
  748. q(Quality) );
  749. # Done if going to SAV quality
  750. return 1 if ( lc $quality eq 'sav' );
  751. $named_parameters->{ BuildID } = $build_number;
  752. my $dfs_numbered_link = GetDfsLinkName( $named_parameters );
  753. if ( !defined $dfs_numbered_link ) {
  754. errmsg( "Do not know how to create DFS links for $branch / $sku." );
  755. return;
  756. }
  757. my @current_builds_linked_at_number;
  758. @current_builds_linked_at_number = @{$dfs_access->{$dfs_numbered_link}} if ( exists $dfs_access->{$dfs_numbered_link} );
  759. my $ffound_inconsistency;
  760. # Check for any shares linked to number that do not match our specifications
  761. foreach ( @current_builds_linked_at_number ) {
  762. my ($current_share_build_number, $current_share_build_time) =
  763. GetBuildNumberAndTsFromShare( $named_parameters, $_ );
  764. if ( !defined $current_share_build_number ) {
  765. errmsg( "Can't determine build number at $dfs_numbered_link from $current_builds_linked_at_number[0] for $branch / $sku." );
  766. return;
  767. }
  768. # If the build number is not what we expected then something is really wrong
  769. if ( $build_number != $current_share_build_number ) {
  770. errmsg( "Share $_ linked at $dfs_numbered_link" );
  771. return;
  772. }
  773. # Check time-stamp
  774. if ( $build_time lt $current_share_build_time ) {
  775. if ( $fReplaceAtNumberedLink ) {
  776. wrnmsg( "Replacing $build_number ($current_share_build_time) which appears newer than $build_number ($build_time)." );
  777. $ffound_inconsistency = 1;
  778. last;
  779. }
  780. else {
  781. errmsg( "Won't replace $build_number ($current_share_build_time) with $build_number ($build_time) without use of -replace" );
  782. return;
  783. }
  784. }
  785. elsif ( $build_time gt $current_share_build_time ) {
  786. dbgmsg( "Replacing links of older build at $build_number: $build_number ($current_share_build_time)" );
  787. $ffound_inconsistency = 1;
  788. last;
  789. }
  790. }
  791. # Unmap the share if an inconsistency was found
  792. if ( $ffound_inconsistency ) {
  793. return if ( !RemoveDfsLink( $named_parameters ) );
  794. }
  795. return 1;
  796. }
  797. sub RemoveOldLatestDotQlyDfsLinks {
  798. my $named_parameters = shift;
  799. my ($dfs_access, $computer_name, $quality, $build_number, $build_name,
  800. $build_time, $language, $platform, $branch, $sku,
  801. $fsafe ) =
  802. GetNamedParameters( $named_parameters,
  803. q(DfsMap), q(ComputerName), q(Quality), q(BuildNumber), q(BuildName),
  804. q(TimeStamp), q(Language), q(Platform), q(Branch), q(Sku),
  805. q(SafeFlag) );
  806. return if ( !defined $dfs_access );
  807. # force release is an option
  808. my $override_safe = $named_parameters->{ q(OverrideSafe) };
  809. # Done if going to SAV quality
  810. return 1 if ( lc $quality eq 'sav' );
  811. my @build_ids = GetBuildIdsFromCurrentLinks( $named_parameters );
  812. # Leave latest.<quality> shares alone
  813. # with the exception of pre and bvt
  814. # if -safe was specified
  815. if ( !$fsafe ) {
  816. $named_parameters->{ BuildID } = "latest.$quality";
  817. my $latest_dfs_link = GetDfsLinkName( $named_parameters );
  818. if ( !defined $latest_dfs_link ) {
  819. errmsg( "Do not know how to create DFS links for $branch / $sku." );
  820. return;
  821. }
  822. my @current_latest_builds;
  823. @current_latest_builds = @{$dfs_access->{$latest_dfs_link}} if ( exists $dfs_access->{$latest_dfs_link} );
  824. # Trying to enforce rule that you cannot move from a release quality
  825. # latest.* (tst, idw, ids) to a non-release quality (pre, bvt)
  826. foreach ( @build_ids ) {
  827. if ( /^latest\.(.+)$/ &&
  828. !AllowQualityTransition( $1, $quality ) ) {
  829. if ( !defined $override_safe ) {
  830. errmsg( "Not allowed to move from '$1' to '$quality' quality." );
  831. return;
  832. }
  833. else {
  834. wrnmsg( "Forcibly moving from '$1' to '$quality' quality." );
  835. }
  836. }
  837. }
  838. my $fforce_safe_flag;
  839. my ($current_latest_build_number, $current_latest_build_time) = ('', 0);
  840. # while we are at we will check for inconsistencies
  841. my ($last_build_number, $last_build_time);
  842. my $ffound_inconsistency;
  843. foreach ( @current_latest_builds ) {
  844. ($current_latest_build_number, $current_latest_build_time) =
  845. GetBuildNumberAndTsFromShare( $named_parameters, $_ );
  846. if ( !defined $current_latest_build_number ) {
  847. errmsg( "Can't determine build number at $latest_dfs_link from $current_latest_builds[0] for $branch / $sku." );
  848. return;
  849. }
  850. # Consistency check
  851. if ( $last_build_number && $last_build_number ne $current_latest_build_number ||
  852. $last_build_time && $last_build_time ne $current_latest_build_time ) {
  853. wrnmsg( "Inconsistent links found at $latest_dfs_link:" );
  854. wrnmsg( "$current_latest_build_number ($current_latest_build_time) and $last_build_number ($last_build_time)" );
  855. $ffound_inconsistency = 1;
  856. }
  857. ($last_build_number, $last_build_time) = ($current_latest_build_number, $current_latest_build_time);
  858. }
  859. # If we found an inconsistency, let's nuke the share
  860. if ( $ffound_inconsistency ) {
  861. wrnmsg( "Links were found to be inconsisent -- removing share" );
  862. $current_latest_build_number = 1;
  863. }
  864. # Usually don't want to overwrite newer builds
  865. if ( $current_latest_build_number &&
  866. ($current_latest_build_number > $build_number ||
  867. $current_latest_build_number == $build_number &&
  868. $current_latest_build_time gt $build_time) )
  869. {
  870. if ( !$override_safe ) {
  871. dbgmsg( "Current build for $platform / $sku at $quality is $current_latest_build_number ($current_latest_build_time), switching to '-safe' mode." );
  872. $named_parameters->{ForceSafeFlag}{$build_name} = 1;
  873. $fforce_safe_flag = 1;
  874. } else {
  875. wrnmsg( "Forcibly moving latest.$quality from $current_latest_build_number ($current_latest_build_time) to $build_number ($build_time)." );
  876. }
  877. }
  878. # Make sure we didn't set -safe flag for user
  879. if ( !$fforce_safe_flag ) {
  880. # If current latest.* build exists and does
  881. # not have our build number and timestamp, lower it
  882. if ( $current_latest_build_number &&
  883. ($current_latest_build_number != $build_number ||
  884. $current_latest_build_time ne $build_time) ) {
  885. if ( !$ffound_inconsistency ) {
  886. dbgmsg( "Lowering $latest_dfs_link because $current_latest_build_number ($current_latest_build_time) != current build $build_number ($build_time)." );
  887. }
  888. $named_parameters->{ BuildID } = "latest.$quality";
  889. return if ( !RemoveDfsLink( $named_parameters ) );
  890. }
  891. # Also need to lower the current numbered share if we are the
  892. # first entry in our latest.* link, as we don't want other
  893. # shares that are not up at our quality to be accessible
  894. # through the same number as ourselves
  895. if ( !exists $dfs_access->{$latest_dfs_link} ) {
  896. $named_parameters->{ BuildID } = $build_number;
  897. return if ( !RemoveDfsLink( $named_parameters ) );
  898. }
  899. }
  900. }
  901. #
  902. # Special rules for BVT and PRE:
  903. #
  904. # If we are currently at latest.bvt or latest.pre
  905. # and are moving to another quality,
  906. # remove latest.<bvt/pre>
  907. #
  908. foreach ( @build_ids ) {
  909. if ( /^latest\.(bvt|pre)$/i &&
  910. lc $1 ne lc $quality ) {
  911. $named_parameters->{ BuildID } = "latest.$1";
  912. return if ( !RemoveDfsLink( $named_parameters ) );
  913. }
  914. }
  915. return 1;
  916. }
  917. # FUTURE:
  918. # This function in most cases would seem to do the same
  919. # as delete <share_name> already implemtened in the tied hash
  920. # Might consider removing this logic...
  921. sub RemoveDfsMappings {
  922. my $named_parameters = shift;
  923. my ( $dfs_access, $branch, $language, $sku, $build_number,
  924. $flang_neutral_build, $platform, $fconglomerator_build) =
  925. GetNamedParameters( $named_parameters,
  926. q(DfsMap), q(Branch), q(Language), q(Sku), q(BuildNumber),
  927. q(IsLangNeutralBuild), q(Platform), q(IsConglomeratorBuild));
  928. my @build_ids = GetBuildIdsFromCurrentLinks( $named_parameters );
  929. my ( $share_name, $sub_path ) = GetShareName( $named_parameters );
  930. if ( !defined $share_name ) {
  931. dbgmsg( "No local share definition for $branch / $sku -- skipping." );
  932. return 1;
  933. }
  934. $share_name .= "\\$sub_path" if ( $sub_path );
  935. my $fsuccess = 1;
  936. foreach ( @build_ids ) {
  937. $named_parameters->{BuildID} = $_;
  938. my $dfs_link = GetDfsLinkName( $named_parameters );
  939. if ( !defined $dfs_link ) {
  940. errmsg( "Do not know how to create DFS links for $branch / $sku." );
  941. return;
  942. }
  943. $fsuccess &&= (tied %$dfs_access)->RemoveShareFromLink( $dfs_link, $share_name );
  944. # Add to support coverage build
  945. # Remove <branch>\usa\<archType>.cov links
  946. if( lc $language eq "cov" && !$fconglomerator_build && !$flang_neutral_build ){
  947. my $altLink = $dfs_link;
  948. $altLink =~ s/cov/usa/i;
  949. $altLink =~ s/$platform/$platform.cov/;
  950. $fsuccess &&= (tied %$dfs_access)->RemoveShareFromLink( $altLink, $share_name );
  951. }
  952. }
  953. # If we are a misc link and the only remaining link is
  954. # the misc DFS link, remove it (we're assuming we must have
  955. # just tore down the last language still raised for it)
  956. if ( $flang_neutral_build ) {
  957. my @links_to_share = @{$dfs_access->{$share_name}} if exists $dfs_access->{$share_name};
  958. $named_parameters->{BuildID} = $build_number;
  959. my $misc_dfs_link = GetDfsLinkName( $named_parameters );
  960. if ( !defined $misc_dfs_link ) {
  961. errmsg( "Do not know how to create DFS links for $branch / $sku." );
  962. return;
  963. }
  964. $misc_dfs_link =~ s/$language/misc/i;
  965. if ( !@links_to_share ||
  966. !grep {!/\Q$misc_dfs_link\E/i} @links_to_share ) {
  967. $fsuccess &&= delete $dfs_access->{$share_name};
  968. }
  969. }
  970. return $fsuccess;
  971. }
  972. sub IsSkuLinkedToLatest {
  973. my $named_parameters = shift;
  974. my ( $branch, $sku ) =
  975. GetNamedParameters( $named_parameters,
  976. q(Branch), q(Sku) );
  977. return if ( !defined $branch );
  978. if ( IsClientSku( $named_parameters ) && IsClientBranch( $named_parameters ) ||
  979. IsServerSku( $named_parameters ) && IsServerBranch( $named_parameters ) ) {
  980. return 1;
  981. }
  982. else {
  983. return 0;
  984. }
  985. }
  986. sub IsServerSku {
  987. my $named_parameters = shift;
  988. my ( $sku ) =
  989. GetNamedParameters( $named_parameters,
  990. q(Sku) );
  991. return if ( !defined $sku );
  992. return scalar grep { lc$sku eq $_ } @ServerSkus;
  993. }
  994. sub IsClientSku {
  995. my $named_parameters = shift;
  996. my ( $sku ) =
  997. GetNamedParameters( $named_parameters,
  998. q(Sku) );
  999. return if ( !defined $sku );
  1000. return scalar grep { lc$sku eq $_ } @ClientSkus;
  1001. }
  1002. sub IsServerBranch {
  1003. my $named_parameters = shift;
  1004. my ( $branch, $language ) =
  1005. GetNamedParameters( $named_parameters,
  1006. q(Branch), q(Language) );
  1007. return if ( !defined $branch );
  1008. if ( !exists $named_parameters->{ServerBranches}{$branch} ) {
  1009. $named_parameters->{ServerBranches}{$branch} = (&GetIniSetting::GetSettingQuietEx( $branch, $language, 'DFSLatestServerSkus' )?1:0);
  1010. dbgmsg( "$branch is a server branch." ) if ( $named_parameters->{ServerBranches}{$branch} );
  1011. }
  1012. return $named_parameters->{ServerBranches}{$branch};
  1013. }
  1014. sub IsClientBranch {
  1015. my $named_parameters = shift;
  1016. my ( $branch, $language ) =
  1017. GetNamedParameters( $named_parameters,
  1018. q(Branch), q(Language) );
  1019. return if ( !defined $branch );
  1020. if ( !exists $named_parameters->{ClientBranches}{$branch} ) {
  1021. $named_parameters->{ClientBranches}{$branch} = (&GetIniSetting::GetSettingQuietEx( $branch, $language, 'DFSLatestClientSkus' )?1:0);
  1022. dbgmsg( "$branch is a client branch." ) if ( $named_parameters->{ClientBranches}{$branch} );
  1023. }
  1024. return $named_parameters->{ClientBranches}{$branch};
  1025. }
  1026. sub GetBldFileExtension {
  1027. my $named_parameters = shift;
  1028. my ( $branch ) =
  1029. GetNamedParameters( $named_parameters,
  1030. q(Branch) );
  1031. return if ( !defined $branch );
  1032. # IF this branch is a client branch, or this
  1033. # branch is both client and server, use BLD
  1034. if ( IsClientBranch( $named_parameters ) ) {
  1035. return 'BLD';
  1036. }
  1037. else {
  1038. return 'SRV';
  1039. }
  1040. }
  1041. sub RemoveDfsLink {
  1042. my $named_parameters = shift;
  1043. my ( $dfs_access, $branch, $build_id, $sku ) =
  1044. GetNamedParameters( $named_parameters,
  1045. q(DfsMap), q(Branch), q(BuildID), q(Sku) );
  1046. return if ! defined ( $build_id );
  1047. if ( IsLastLinkUnderParent( $named_parameters ) ) {
  1048. # Delete <build>.BLD / <build>.SRV file
  1049. #
  1050. # If the BLD/SRV file still exists when we unmap the link,
  1051. # there will no longer be a DFS link but the directory
  1052. # structure will remain, making it look like there is
  1053. # still a DFS link
  1054. # If we are the last numbered link, assume we are the
  1055. # last relevant latest.* link as well, and remove
  1056. # BLD/SRV files from latest.* as well (this code was added
  1057. # for the XP client/server split)
  1058. my @build_ids;
  1059. if ( $build_id =~ /^\d+$/ ) {
  1060. @build_ids = GetBuildIdsFromCurrentLinks( $named_parameters );
  1061. }
  1062. else {
  1063. @build_ids = ($build_id);
  1064. }
  1065. my $bldfile_ext = GetBldFileExtension( $named_parameters );
  1066. foreach ( @build_ids ) {
  1067. $named_parameters->{BuildID} = $_;
  1068. foreach ( GetBldFileDfsPathNames( $named_parameters ) ) {
  1069. dbgmsg( "Removing $bldfile_ext file from link's parent directory" );
  1070. my @bld_files = grep {$_ if ( ! -d $_ )} globex "$_\\*.$bldfile_ext";
  1071. # 0 = success
  1072. # 2 = couldn't find file (may be a failure or there may be no BLD file;
  1073. # have to treat as a success as there is no way
  1074. # to tell the difference - $^E doesn't help)
  1075. if ( $! != 0 && $! != 2 ) {
  1076. wrnmsg( "Trying to find existing $bldfile_ext file: $!" );
  1077. }
  1078. # Delete all BLD/SRV files
  1079. foreach ( @bld_files ) {
  1080. if ( !unlink $_ ) { wrnmsg( "Unable to delete $_ ($!)." ) }
  1081. }
  1082. }
  1083. }
  1084. # Make sure we are pointing at the right build_id after all of this
  1085. $named_parameters->{BuildID} = $build_id;
  1086. }
  1087. #
  1088. # Now actually remove the DFS link
  1089. #
  1090. my $dfs_link = GetDfsLinkName( $named_parameters );
  1091. if ( !defined $dfs_link ) {
  1092. errmsg( "Do not know how to create DFS links for $branch / $sku." );
  1093. return;
  1094. }
  1095. dbgmsg( "Removing/Verifying $dfs_link from DFS." );
  1096. if ( !delete $dfs_access->{$dfs_link} ) {
  1097. errmsg( "Problem lowering $dfs_link from DFS." );
  1098. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  1099. return;
  1100. }
  1101. return 1;
  1102. }
  1103. sub MagicDFSSettings {
  1104. my $named_parameters = shift;
  1105. my ($dfs_access, $branch, $build_number, $build_name,
  1106. $language, $sku, $platform, $quality,
  1107. $fsafe ) =
  1108. GetNamedParameters( $named_parameters,
  1109. q(DfsMap), q(Branch), q(BuildNumber), q(BuildName),
  1110. q(Language), q(Sku), q(Platform), q(Quality),
  1111. q(SafeFlag) );
  1112. return if ( !defined $dfs_access );
  1113. #
  1114. # We need all new DFS links to exist in order to find
  1115. # new paths, so flush any outstanding commands here
  1116. #
  1117. if ( !(tied %$dfs_access)->Flush() ) {
  1118. errmsg( "Problem processing DFS commands." );
  1119. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  1120. return;
  1121. }
  1122. #
  1123. # Hide / Unhide paths on the DFS machine as we move
  1124. # between pre/bvt and other qualities
  1125. #
  1126. # NOTE: following original code's precedent and not
  1127. # hiding the latest.* subdirs
  1128. #
  1129. $named_parameters->{ BuildID } = $build_number;
  1130. foreach ( GetHideableDfsPathNames( $named_parameters ) ) {
  1131. my $attribs = 0; # set to avoid warning / actually first used to get return value
  1132. my $fhidden;
  1133. my $fhide_share;
  1134. if ( !GetAttributes( $_, $attribs ) ) {
  1135. wrnmsg( "Unable to get current hidden/unhidden status of $_ ($^E) -- skipping." );
  1136. }
  1137. $fhidden = $attribs & 2;
  1138. $fhide_share = ($quality =~ /^(bvt|pre)$/i);
  1139. # See if we need to switch hidden attribute on/off
  1140. if ( $fhide_share &&
  1141. !$fhidden &&
  1142. !SetAttributes( $_, $attribs | 2 ) ) {
  1143. wrnmsg( "Could not hide $_ ($^E)." );
  1144. }
  1145. elsif ( !$fhide_share &&
  1146. $fhidden &&
  1147. !SetAttributes( $_, $attribs & ~2 ) ) {
  1148. wrnmsg( "Could not unhide $_ ($^E)." );
  1149. }
  1150. }
  1151. #
  1152. # Write <build>.BLD / <build>.SRV file
  1153. #
  1154. my @builds_to_touch = ( $build_number );
  1155. if ( !$fsafe ) { push @builds_to_touch, "latest.$quality" }
  1156. my $bldfile_ext = GetBldFileExtension( $named_parameters );
  1157. foreach ( @builds_to_touch ) {
  1158. $named_parameters->{ BuildID } = $_;
  1159. foreach ( GetBldFileDfsPathNames( $named_parameters ) ) {
  1160. my $fbldfile_already_set;
  1161. my @bld_files = grep { $_ if ( ! -d $_ ) } globex "$_\\*.$bldfile_ext";
  1162. # 0 = success
  1163. # 2 = couldn't find file (may be a failure or there may be no BLD file;
  1164. # have to treat as a success as there is no way
  1165. # to tell the difference - $^E doesn't help)
  1166. if ( $! != 0 && $! != 2 ) {
  1167. wrnmsg( "Trying to find existing $bldfile_ext file: $!" );
  1168. }
  1169. # If there is more than one, delete them all and start anew
  1170. elsif ( @bld_files > 1 &&
  1171. ! unlink @bld_files ) {
  1172. wrnmsg( "Multiple *.$bldfile_ext files found at $_ -- unable to delete ($!)." );
  1173. }
  1174. # If there is just one, see if it matches current criteria
  1175. elsif ( 1 == @bld_files ) {
  1176. if ( $bld_files[0] =~ /\\$build_name.$bldfile_ext$/ ) {
  1177. unless ( open BLDFILE, "$bld_files[0]" ) {
  1178. wrnmsg( "Unable to read $bld_files[0] ($!)." );
  1179. next;
  1180. }
  1181. my $cur_bld_quality = <BLDFILE>;
  1182. close BLDFILE;
  1183. chomp $cur_bld_quality;
  1184. if ( lc $quality eq lc $cur_bld_quality ) {
  1185. # Current bld file matches all of our criteria
  1186. $fbldfile_already_set = 1;
  1187. }
  1188. }
  1189. # If something about the current bld file did not match,
  1190. # delete it in preparation for creating a new one
  1191. if ( !$fbldfile_already_set &&
  1192. ! unlink $bld_files[0] ) {
  1193. wrnmsg( "Unable to delete $bld_files[0] ($!)." );
  1194. }
  1195. }
  1196. # Write out new BLD file if necessary
  1197. if ( !$fbldfile_already_set ) {
  1198. unless ( open WRITE_BLDFILE, "> $_\\$build_name.$bldfile_ext" ) {
  1199. errmsg( "Unable to write $_\\$build_name.$bldfile_ext ($!)." );
  1200. return;
  1201. }
  1202. print WRITE_BLDFILE "$quality\n";
  1203. close WRITE_BLDFILE
  1204. }
  1205. }
  1206. }
  1207. return 1;
  1208. }
  1209. sub GetNamedParameters {
  1210. my $named_parameters = shift;
  1211. my ( $name, @parameter_list );
  1212. while ( $name = shift ) {
  1213. if ( !exists $named_parameters->{ $name } ) {
  1214. my $called_by = (caller 1)[3];
  1215. errmsg( "Undefined parameter \"$name\" to $called_by." );
  1216. return;
  1217. }
  1218. push @parameter_list, $named_parameters->{ $name };
  1219. }
  1220. return @parameter_list;
  1221. }
  1222. sub ForEachBuild {
  1223. my $func = shift;
  1224. my $named_parameters = shift;
  1225. my $language = shift;
  1226. my $builds = shift;
  1227. my $fsafe = shift;
  1228. my $build;
  1229. foreach $build ( @$builds ) {
  1230. my ( $build_number, $arch, $debug_type, $branch, $time_stamp );
  1231. my $fconglomerator_build = 0;
  1232. my $flang_neutral_build = 0;
  1233. # Normal build
  1234. if ( IsOSBuild( $build ) ) {
  1235. ( $build_number, $arch, $debug_type, $branch, $time_stamp ) = build_name_parts( $build );
  1236. }
  1237. # Conglomerator skus
  1238. elsif ( $build =~ /([^\\]+)\\(\d+)\.([^\.]+)(?:\.(\d+-\d+))?$/ ) {
  1239. ( $build_number, $arch, $debug_type, $branch, $time_stamp ) = ( $2, '', '', $3, $4 || 0 );
  1240. $flang_neutral_build = (lc$1 eq 'misc')?1:0;
  1241. $fconglomerator_build = 1;
  1242. }
  1243. if ( !defined $build_number ) {
  1244. errmsg( "Unable to determine build information for $build." );
  1245. return;
  1246. }
  1247. my $platform = "$arch$debug_type";
  1248. # use already defined skus or default to looking them up
  1249. my @skus;
  1250. if ( exists $named_parameters->{ DefinedSkus } ) {
  1251. @skus = @{$named_parameters->{ DefinedSkus }};
  1252. }
  1253. elsif ( $fconglomerator_build ) {
  1254. @skus = GetSkusForConglomeratorBuild( $build );
  1255. }
  1256. else {
  1257. @skus = GetSkusForBuild( $build, $language, $debug_type, $arch );
  1258. }
  1259. if ( !@skus ) {
  1260. wrnmsg( "No skus defined for $build ($language) - skipping." );
  1261. # Skip this build
  1262. next;
  1263. }
  1264. # Setup common parameters for functions
  1265. $named_parameters->{ FullBuildPath } = $build;
  1266. if ( $fconglomerator_build ) {
  1267. ($named_parameters->{ BuildName }) = $build =~ /\\([^\\]+\\[^\\]+)$/;
  1268. }
  1269. else {
  1270. $named_parameters->{ BuildName } = basename( $build );
  1271. }
  1272. $named_parameters->{ BuildNumber } = $build_number;
  1273. $named_parameters->{ DebugType } = $debug_type;
  1274. $named_parameters->{ Branch } = $branch;
  1275. $named_parameters->{ Platform } = $platform;
  1276. $named_parameters->{ TimeStamp } = $time_stamp;
  1277. $named_parameters->{ IsConglomeratorBuild } = $fconglomerator_build;
  1278. $named_parameters->{ IsLangNeutralBuild } = $flang_neutral_build;
  1279. if ( defined $fsafe ||
  1280. $named_parameters->{ForceSafeFlag}{$named_parameters->{ BuildName }}) {
  1281. $named_parameters->{ SafeFlag } = 1;
  1282. }
  1283. else {
  1284. $named_parameters->{ SafeFlag } = 0;
  1285. }
  1286. # Loop through all supported flavors
  1287. my $sku;
  1288. foreach $sku ( @skus ) {
  1289. $named_parameters->{ Sku } = $sku;
  1290. # Client/server split: skip skus
  1291. # that are not associated with branch
  1292. if ( !IsSkuLinkedToLatest( $named_parameters ) ) {
  1293. # Handle absurd pro ia64 sku even during C/S split
  1294. if ( !(lc $arch eq 'ia64' and lc $sku eq 'pro') ) {
  1295. next;
  1296. }
  1297. }
  1298. if ( !&$func( $named_parameters ) ) {
  1299. errmsg( "Problem occurred during processing of $sku." );
  1300. return;
  1301. }
  1302. }
  1303. }
  1304. return 1;
  1305. }
  1306. sub IsLastLinkUnderParent {
  1307. my $named_parameters = shift;
  1308. my ( $dfs_access, $build_id, $branch, $sku ) =
  1309. GetNamedParameters( $named_parameters,
  1310. q(DfsMap), q(BuildID), q(Branch), q(Sku) );
  1311. return if ( !defined $dfs_access );
  1312. my $dfs_link = GetDfsLinkName( $named_parameters );
  1313. if ( !defined $dfs_link ) {
  1314. errmsg( "Do not know how to create DFS links for $branch / $sku." );
  1315. return;
  1316. }
  1317. my ( $cur_share_name, $sub_path ) = GetShareName( $named_parameters );
  1318. if ( !defined $cur_share_name ) {
  1319. errmsg( "Do not know how to create release shares for $branch / $sku." );
  1320. return;
  1321. }
  1322. $cur_share_name .= "\\$sub_path" if ( $sub_path );
  1323. #
  1324. # First check that we are the last entry in the current dfs link
  1325. #
  1326. my @cur_dfs_links;
  1327. @cur_dfs_links = @{ $dfs_access->{$dfs_link} } if ( exists $dfs_access->{$dfs_link} );
  1328. if ( !@cur_dfs_links ||
  1329. @cur_dfs_links != 1 ||
  1330. lc $cur_share_name ne lc $cur_dfs_links[0] ) {
  1331. return;
  1332. }
  1333. #
  1334. # Now check that we are the last link under our parent directory
  1335. #
  1336. # Construct full path to dfs link's parent directory
  1337. my $full_dfs_link = (tied %$dfs_access)->GetDfsRoot(). "\\$dfs_link";
  1338. my ($parent_directory) = $dfs_link =~ /^(.*)\\.*$/;
  1339. $parent_directory = (tied %$dfs_access)->GetDfsRoot(). '\\'. $parent_directory;
  1340. # Loop through all directory entries looking for
  1341. # any subdirectories that do not match our share
  1342. # name
  1343. my @parent_entries = grep { $_ if ( -d $_ ) } globex "$parent_directory\\*";
  1344. foreach ( @parent_entries ) {
  1345. # It is a directory -- if it isn't us assume
  1346. # it refers to another existing link
  1347. # WARNING: Special case the 'test' subdirectory
  1348. # so we don't count it during
  1349. # this check (we will clean up if
  1350. # all that is stopping us is 'test')
  1351. if ( lc $_ ne lc $full_dfs_link &&
  1352. $_ !~ /\\test$/i ) {
  1353. return;
  1354. }
  1355. }
  1356. # We must be the only link under our parent directory
  1357. return 1;
  1358. }
  1359. sub GetSkusForBuild {
  1360. my $build = shift;
  1361. my $language = shift;
  1362. my $build_type = shift;
  1363. my $architecture = shift;
  1364. my %skus = cksku::GetSkus( $language, build_arch( $build ) );
  1365. my @returned_skus;
  1366. @returned_skus = ( sort keys %skus );
  1367. # WinPE image
  1368. if ( lc $language eq 'usa' &&
  1369. lc $build_type eq 'fre' ) {
  1370. push @returned_skus, 'winpe';
  1371. }
  1372. # UpgAdv image
  1373. if ( lc $architecture eq 'x86' && lc $build_type eq 'fre'
  1374. && lc $language ne 'psu' && lc $language ne 'mir' && lc $language ne 'fe' )
  1375. {
  1376. push @returned_skus, 'upgadv';
  1377. }
  1378. # Another special case for PSU/FE:
  1379. # need to link to 'resources'
  1380. if ( lc $language ne 'usa' ||
  1381. lc $language ne 'mir') {
  1382. push @returned_skus, 'resources';
  1383. }
  1384. # Special case 'bin' -- everyone gets one
  1385. push @returned_skus, 'bin';
  1386. return @returned_skus;
  1387. }
  1388. sub GetSkusForConglomeratorBuild {
  1389. my $build = shift;
  1390. my @possible_skus = @ConglomeratorSkus;
  1391. # BensonT: Non US builds do not use symbolcd
  1392. if (lc$Language ne "usa") {
  1393. @possible_skus = grep {lc$_ ne 'symbolcd'} @possible_skus;
  1394. }
  1395. my @skus;
  1396. foreach ( @possible_skus )
  1397. {
  1398. push @skus, $_ if ( -e "$build\\$_" );
  1399. }
  1400. return @skus;
  1401. }
  1402. #
  1403. # Returns:
  1404. #
  1405. # ( share, subpath )
  1406. # An array consisting of the full-share name (\\server\share) and
  1407. # a subpath. \\server\share\subpath would take you to your
  1408. # expected destination
  1409. #
  1410. sub GetShareName {
  1411. my $named_parameters = shift;
  1412. my ( $computer_name, $build_name, $build_number,
  1413. $language, $platform, $branch,
  1414. $sku, $fconglomerator_build, $flang_neutral_build,
  1415. $timestamp ) =
  1416. GetNamedParameters( $named_parameters,
  1417. q(ComputerName), q(BuildName), q(BuildNumber),
  1418. q(Language), q(Platform), q(Branch),
  1419. q(Sku), q(IsConglomeratorBuild), q(IsLangNeutralBuild),
  1420. q(TimeStamp) );
  1421. return if ( !defined $computer_name );
  1422. my $lcsku = lc $sku;
  1423. # symbol server shares
  1424. if ( $lcsku eq 'sym' ) {
  1425. my $symbol_server_share = &GetIniSetting::GetSettingQuietEx( $branch, $language, 'SymFarm' );
  1426. if ( !$symbol_server_share ) {
  1427. dbgmsg( "No INI file setting for 'SymFarm' in $branch / SymFarm." );
  1428. return;
  1429. }
  1430. return ("$symbol_server_share", "$language\\$build_name\\symbols.pri");
  1431. }
  1432. # special "skus"
  1433. if ( $fconglomerator_build ) {
  1434. return ("\\\\$computer_name\\$build_number.$branch.".($flang_neutral_build?'misc':$language), $sku);
  1435. }
  1436. # Standard skus
  1437. else {
  1438. # note the special case for "bin"
  1439. my $subdir;
  1440. $subdir = ($lcsku eq 'bin'?"":$sku);
  1441. return ("\\\\$computer_name\\$build_number.$platform.$branch.$timestamp.$language", $subdir);
  1442. }
  1443. }
  1444. sub GetBuildNumberAndTsFromShare {
  1445. my $named_parameters = shift;
  1446. my $build = shift;
  1447. my ( $branch, $language, $platform, $sku, $fconglomerator_build,
  1448. $flang_neutral_build ) =
  1449. GetNamedParameters( $named_parameters,
  1450. q(Branch), q(Language), q(Platform), q(Sku), q(IsConglomeratorBuild),
  1451. q(IsLangNeutralBuild) );
  1452. my ($build_number, $timestamp);
  1453. #
  1454. # Note: may have to reach into a share and
  1455. # extract some information if it is
  1456. # not readily apparent from the name
  1457. # of the share (as is the case with main)
  1458. #
  1459. # Symbol server points to shares
  1460. # containing the fully-qualified build-name
  1461. # (build_num.platform.branch.time)
  1462. if ( lc $sku eq 'sym' ) {
  1463. $build_number = build_number( $build );
  1464. $timestamp = build_date( $build );
  1465. }
  1466. # Special "skus"
  1467. elsif ( $fconglomerator_build &&
  1468. $build =~ /^\\\\[^\\]+\\(\d+)\.[^\.]+(?:\.(\d+-\d+))?\.([^\\]+)\\$sku$/i &&
  1469. $flang_neutral_build && lc$3 eq 'misc' ||
  1470. !$flang_neutral_build && lc$3 eq lc$language ) {
  1471. $build_number = $1;
  1472. $timestamp = $2 || 0;
  1473. }
  1474. # Standard skus
  1475. elsif ( !$fconglomerator_build &&
  1476. $build =~ /^\\\\[^\\]+\\(\d+)\.$platform\.[^\.]+\.(\d+-\d+)\.$language(?:\\$sku)?$/i ) {
  1477. $build_number = $1;
  1478. $timestamp = $2;
  1479. }
  1480. else {
  1481. # Error - don't recognize the format
  1482. return;
  1483. }
  1484. return ($build_number, $timestamp);
  1485. }
  1486. sub GetDfsLinkName {
  1487. my $named_parameters = shift;
  1488. my ($branch, $language, $build_id, $platform, $sku,
  1489. $fconglomerator_build ) =
  1490. GetNamedParameters( $named_parameters,
  1491. q(Branch), q(Language), q(BuildID), q(Platform), q(Sku),
  1492. q(IsConglomeratorBuild) );
  1493. return if ( !defined $branch );
  1494. my $lcsku = lc $sku;
  1495. # Used to add .srv onto skus that appear for both
  1496. # the client and server in the latest.* links
  1497. my $sku_name_suffix = '';
  1498. # If we haven't looked for DFSAlternateBranchName
  1499. # in the INI file, do so now
  1500. my $dfs_branch_name;
  1501. if ( !exists $named_parameters->{DefaultDFSBranches}{$branch} ) {
  1502. my $alt_branch_name = &GetIniSetting::GetSettingQuietEx( $branch, $language, 'DFSAlternateBranchName' );
  1503. # Default to the branch name
  1504. if ( !$alt_branch_name) {
  1505. $named_parameters->{DefaultDFSBranches}{$branch} = $branch;
  1506. }
  1507. else {
  1508. $named_parameters->{DefaultDFSBranches}{$branch} = $alt_branch_name;
  1509. }
  1510. }
  1511. $dfs_branch_name = $named_parameters->{DefaultDFSBranches}{$branch};
  1512. # Special behaviors on latest.* links
  1513. if ( $build_id =~ /^latest/i ) {
  1514. # If we haven't looked for DFSAlternateLatestBranchName
  1515. # in the INI file, do so now
  1516. if ( !exists $named_parameters->{LatestDFSBranches}{$branch} ) {
  1517. my $dfs_latest_branch_name = &GetIniSetting::GetSettingQuietEx( $branch, $language, 'DFSAlternateLatestBranchName' );
  1518. # Default to the current DFS branch name
  1519. if ( !$dfs_latest_branch_name) {
  1520. $named_parameters->{LatestDFSBranches}{$branch} = $named_parameters->{DefaultDFSBranches}{$branch};
  1521. }
  1522. else {
  1523. $named_parameters->{LatestDFSBranches}{$branch} = $dfs_latest_branch_name;
  1524. }
  1525. }
  1526. $dfs_branch_name = $named_parameters->{LatestDFSBranches}{$branch};
  1527. # If both client and serer support a sku,
  1528. # append .srv onto the server version.
  1529. # If this branch is both client and server,
  1530. # then don't use the .srv extension
  1531. if ( IsClientSku( $named_parameters ) &&
  1532. IsServerSku( $named_parameters ) &&
  1533. IsServerBranch( $named_parameters ) &&
  1534. !IsClientBranch( $named_parameters ) ) {
  1535. $sku_name_suffix = '.srv';
  1536. }
  1537. }
  1538. # Special "skus"
  1539. if ( $fconglomerator_build ) {
  1540. return "$dfs_branch_name\\$language\\$build_id\\". ($lcsku eq 'symbolcd'?'symcd2':$lcsku). $sku_name_suffix;
  1541. }
  1542. # Standard skus and symbol server sku
  1543. else {
  1544. return "$dfs_branch_name\\$language\\$build_id\\$platform\\$lcsku". $sku_name_suffix;
  1545. }
  1546. }
  1547. sub GetBuildIdsFromCurrentLinks {
  1548. my $named_parameters = shift;
  1549. my ($dfs_access, $build_number, $branch, $platform, $language, $sku ) =
  1550. GetNamedParameters( $named_parameters,
  1551. q(DfsMap), q(BuildNumber), q(Branch), q(Platform), q(Language), q(Sku) );
  1552. return if ( !defined $dfs_access );
  1553. my ( $cur_share_name, $sub_path ) = GetShareName( $named_parameters );
  1554. if ( !defined $cur_share_name ) {
  1555. errmsg( "Do not know how to create release shares for $branch / $sku." );
  1556. return;
  1557. }
  1558. $cur_share_name .= "\\$sub_path" if ( $sub_path );
  1559. my @build_ids;
  1560. # Return all recognized build IDs we are currently linked to
  1561. # (expecting <build_number> and latest.* entries)
  1562. my @current_links = @{ $dfs_access->{$cur_share_name} } if ( exists $dfs_access->{$cur_share_name} );
  1563. foreach ( @current_links ) {
  1564. my $next_build_id;
  1565. $next_build_id = GetBuildIdFromDfsLink( $named_parameters, $_ );
  1566. if ( $next_build_id ) {
  1567. push @build_ids, $next_build_id;
  1568. }
  1569. }
  1570. return @build_ids;
  1571. }
  1572. sub GetBuildIdFromDfsLink {
  1573. my $named_parameters = shift;
  1574. my $build = shift;
  1575. my ( $branch, $platform, $language, $sku,
  1576. $fconglomerator_build, $flang_neutral_build ) =
  1577. GetNamedParameters( $named_parameters,
  1578. q(Branch), q(Platform), q(Language), q(Sku),
  1579. q(IsConglomeratorBuild), q(IsLangNeutralBuild) );
  1580. my $lcsku = lc $sku;
  1581. my $build_id;
  1582. # Special "skus"
  1583. if ( $fconglomerator_build ) {
  1584. my $lang = $flang_neutral_build?'misc':$language;
  1585. my $dfs_sku_name = $lcsku eq 'symbolcd'?'symcd2':$sku;
  1586. $dfs_sku_name .= '(?:\.srv)?';
  1587. ($build_id) = $build =~ /[^\\]+\\$lang\\([^\\]+)\\$dfs_sku_name$/i;
  1588. }
  1589. # Standard skus and symbol server sku
  1590. else {
  1591. ($build_id) = $build =~ /[^\\]+\\$language\\([^\\]+)\\$platform\\$sku(?:\.srv)?$/i;
  1592. }
  1593. return $build_id;
  1594. }
  1595. sub GetWriteableDfsShares {
  1596. my $named_parameters = shift;
  1597. my ($dfs_access, $branch) =
  1598. GetNamedParameters( $named_parameters,
  1599. q(DfsMap), q(Branch) );
  1600. return if ( !defined $dfs_access );
  1601. # All known branches use the same writeable share format
  1602. if ( $branch ) # TRUE
  1603. {
  1604. my @dfs_servers = (tied %$dfs_access)->GetDfsHosts();
  1605. if ( !@dfs_servers ) {
  1606. errmsg( "Unable to retrieve hosting machines for DFS root ". (tied %$dfs_access)->GetDfsRoot(). "." );
  1607. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  1608. return;
  1609. }
  1610. # Point to writeable shares
  1611. s/(\\\\[^\\]+).*/$1\\writer\$\\release/ foreach ( @dfs_servers );
  1612. return @dfs_servers;
  1613. }
  1614. # Unknown branch
  1615. else {
  1616. return;
  1617. }
  1618. }
  1619. sub GetHideableDfsPathNames {
  1620. my $named_parameters = shift;
  1621. my ( $dfs_access, $branch, $language, $build_id, $sku ) =
  1622. GetNamedParameters( $named_parameters,
  1623. q(DfsMap), q(Branch), q(Language), q(BuildID), q(Sku) );
  1624. return if ( !defined $dfs_access );
  1625. # Get writeable DFS share for our branch
  1626. my @writeable_dfs_shares = GetWriteableDfsShares( $named_parameters );
  1627. if ( !@writeable_dfs_shares ) {
  1628. dbgmsg( "Unknown writeable DFS share for $branch." );
  1629. return;
  1630. }
  1631. my $dfs_link = GetDfsLinkName( $named_parameters );
  1632. if ( !defined $dfs_link ) {
  1633. errmsg( "Do not know how to create $build_id DFS link for $branch / $sku." );
  1634. return;
  1635. }
  1636. # Want the path up to latest.* or #
  1637. # (build_id): main\usa\<build_id>
  1638. $dfs_link =~ s/^(.*$build_id)\\.*$/$1/;
  1639. # All known branches use the same writeable share format
  1640. if ( $branch ) # TRUE
  1641. {
  1642. return map {"$_\\$dfs_link"} @writeable_dfs_shares;
  1643. }
  1644. # Unknown branch
  1645. else {
  1646. return;
  1647. }
  1648. }
  1649. sub GetBldFileDfsPathNames {
  1650. my $named_parameters = shift;
  1651. my ( $dfs_access, $branch, $language, $build_id,
  1652. $platform, $sku ) =
  1653. GetNamedParameters( $named_parameters,
  1654. q(DfsMap), q(Branch), q(Language), q(BuildID),
  1655. q(Platform), q(Sku) );
  1656. return if ( !defined $dfs_access );
  1657. # Get writeable DFS share for our branch
  1658. my @writeable_dfs_shares = GetWriteableDfsShares( $named_parameters );
  1659. if ( !@writeable_dfs_shares ) {
  1660. dbgmsg( "Unknown writeable DFS share for $branch." );
  1661. return;
  1662. }
  1663. my $dfs_link = GetDfsLinkName( $named_parameters );
  1664. if ( !defined $dfs_link ) {
  1665. errmsg( "Do not know how to create $build_id DFS link for $branch / $sku." );
  1666. return;
  1667. }
  1668. # Want the path up to platform: main\usa\<build_id>\<platform>
  1669. $dfs_link =~ s/^(.*$platform)\\.*$/$1/;
  1670. # All known branches use the same writeable share format
  1671. if ( $branch ) # TRUE
  1672. {
  1673. return map {"$_\\$dfs_link"} @writeable_dfs_shares;
  1674. }
  1675. # Unknown branch
  1676. else {
  1677. return;
  1678. }
  1679. }
  1680. sub ModifyQlyFile {
  1681. my $build = shift;
  1682. my $quality = shift;
  1683. my $path_to_quality_file = shift;
  1684. my ( @existing_qlyfiles, $qly_file );
  1685. my ( $fqlyfile_already_set );
  1686. # Check for existing QLY files
  1687. @existing_qlyfiles = grep { $_ if ( ! -d $_ ) } globex "$path_to_quality_file\\*.qly";
  1688. # 0 = success
  1689. # 2 = couldn't find file (may be a failure or there may be no QLY file;
  1690. # have to treat as a success as there is no way
  1691. # to tell the difference - $^E doesn't help)
  1692. if ( $! != 0 && $! != 2 ) {
  1693. errmsg( "Trying to find existing QLY file: $!" );
  1694. return;
  1695. }
  1696. # Remove any QLY files that don't match current status
  1697. # -- there should never be more than one, but we
  1698. # should handle that case correctly
  1699. undef $fqlyfile_already_set;
  1700. foreach $qly_file ( @existing_qlyfiles ) {
  1701. my ($qly_file_quality) = $qly_file =~ /\\([^\\]+)\.qly$/;
  1702. if ( lc $qly_file_quality eq lc $quality ) {
  1703. # Check for correct information inside
  1704. my $qly_information = ReadQlyFile( $qly_file );
  1705. if ( !defined $qly_information ) {
  1706. wrnmsg( "Problem reading $qly_file" );
  1707. }
  1708. elsif ( $qly_information =~ /^$build$/i ) {
  1709. $fqlyfile_already_set = 1;
  1710. next;
  1711. }
  1712. }
  1713. elsif ( !AllowQualityTransition( $qly_file_quality, $quality ) ) {
  1714. errmsg( "Not allowed to go from '$qly_file_quality' to '$quality' quality!" );
  1715. return;
  1716. }
  1717. # Remove the QLY file
  1718. unless ( unlink $qly_file ) {
  1719. errmsg( "Could not delete $qly_file ($!)" );
  1720. return;
  1721. }
  1722. }
  1723. # Create new QLY file if necessary
  1724. if ( !$fqlyfile_already_set ) {
  1725. unless ( open QLYFILE, "> $path_to_quality_file\\$quality.qly" ) {
  1726. errmsg( "Could not write to $path_to_quality_file\\$quality.qly ($!)" );
  1727. return;
  1728. }
  1729. print QLYFILE "$build\n";
  1730. close QLYFILE;
  1731. }
  1732. return 1;
  1733. }
  1734. sub ReadQlyFile {
  1735. my $qlyfile_path = shift;
  1736. my ( $qlyfile_contents );
  1737. unless ( open QLYFILE, $qlyfile_path ) {
  1738. errmsg( "Failure opening $qlyfile_path ($!)" );
  1739. return;
  1740. }
  1741. $qlyfile_contents = <QLYFILE>;
  1742. close QLYFILE;
  1743. chomp $qlyfile_contents;
  1744. return $qlyfile_contents;
  1745. }
  1746. sub DeleteQlyFile {
  1747. my $path_to_quality_file = shift;
  1748. my @existing_qlyfiles;
  1749. # Check for existing QLY files
  1750. @existing_qlyfiles = grep { $_ if ( ! -d $_ ) } globex "$path_to_quality_file\\*.qly";
  1751. # 0 = success
  1752. # 2 = couldn't find file (may be a failure or there may be no QLY file;
  1753. # have to treat as a success as there is no way
  1754. # to tell the difference - $^E doesn't help)
  1755. if ( $! != 0 && $! != 2 ) {
  1756. errmsg( "Trying to find existing QLY file: $!" );
  1757. return;
  1758. }
  1759. my $qly_file;
  1760. foreach $qly_file ( @existing_qlyfiles ) {
  1761. if ( !unlink $qly_file ) {
  1762. wrnmsg( "Unable to remove $qly_file ($!)" );
  1763. }
  1764. }
  1765. return 1;
  1766. }
  1767. sub CreateSecuredBuildShare
  1768. {
  1769. my $named_parameters = shift;
  1770. my ( $accessed_shares, $full_build_path, $computer_name,
  1771. $quality, $build_number, $language,
  1772. $platform, $branch, $sku ) =
  1773. GetNamedParameters( $named_parameters,
  1774. q(AccessedShares), q(FullBuildPath), q(ComputerName),
  1775. q(Quality), q(BuildNumber), q(Language),
  1776. q(Platform), q(Branch), q(Sku) );
  1777. return if ( !defined $accessed_shares );
  1778. my $fshare_already_exists;
  1779. my $sMember;
  1780. my $sBVTAccessGroup;
  1781. my ( @share_members, @remove_permissions );
  1782. my ( $build_share, $sub_path ) = GetShareName( $named_parameters );
  1783. if ( !defined $build_share )
  1784. {
  1785. errmsg( "Unable to determine release share for $branch / $sku." );
  1786. return;
  1787. }
  1788. # Optimization: check if we have already looked at this share
  1789. # (especially useful when each sku uses the same share)
  1790. foreach ( @$accessed_shares )
  1791. {
  1792. return 1 if ( lc $_ eq lc $build_share );
  1793. }
  1794. # Choose share permissions based on quality
  1795. if ( lc $quality eq "pre" || lc $quality eq "bvt" )
  1796. {
  1797. my $share_member_string = &GetIniSetting::GetSettingQuietEx( $branch, $language, "BVTMembers" );
  1798. if ( $share_member_string )
  1799. {
  1800. @share_members = split ' ', $share_member_string;
  1801. }
  1802. # INI file must specify permissions
  1803. else {
  1804. logmsg( "No entries specified in INI file for BVTMembers" );
  1805. }
  1806. #Get the BVT access group
  1807. $sBVTAccessGroup = &GetIniSetting::GetSettingQuietEx( $branch, $language, "BVTAccessGroup" );
  1808. if ( $sBVTAccessGroup)
  1809. {
  1810. $sBVTAccessGroup =~ s/_/ /g;
  1811. push @share_members, $sBVTAccessGroup;
  1812. }
  1813. }
  1814. else
  1815. {
  1816. my $share_member_string = &GetIniSetting::GetSettingQuietEx( $branch, $language, "ReleaseAccess" );
  1817. if ( $share_member_string )
  1818. {
  1819. @share_members = split ' ', $share_member_string;
  1820. }
  1821. # INI file must specify permissions
  1822. else
  1823. {
  1824. errmsg( "No entries specified in INI file for ReleaseAccess" );
  1825. return;
  1826. }
  1827. }
  1828. # Create share if it doesn't already exist
  1829. $fshare_already_exists = ( -e $build_share );
  1830. # This will set the current permissions and create the share if necessary
  1831. dbgmsg( "Setting share permissions for $build_share:" );
  1832. dbgmsg( " $_" ) foreach @share_members;
  1833. #BUG FIX: If the share does not exist, create it and then remove access to everyone
  1834. if(!$fshare_already_exists)
  1835. {
  1836. if (!ExecuteProgram::ExecuteProgram( "rmtshare.exe $build_share=$full_build_path /remove everyone"))
  1837. {
  1838. errmsg( "Failed creating the share for $full_build_path." );
  1839. errmsg( " ". ExecuteProgram::GetLastCommand() );
  1840. errmsg( " $_" ) foreach ExecuteProgram::GetLastOutput();
  1841. return;
  1842. }
  1843. }
  1844. foreach $sMember (@share_members)
  1845. {
  1846. my $iRes = 0;
  1847. $iRes = !ExecuteProgram::ExecuteProgram("rmtshare.exe $build_share /GRANT\"$sMember\":R > NUL");
  1848. if ( $iRes )
  1849. {
  1850. logmsg( "Failed granting access for $sMember." );
  1851. logmsg( " " . ExecuteProgram::GetLastCommand() );
  1852. logmsg( " $_" ) foreach ExecuteProgram::GetLastOutput();
  1853. }
  1854. }
  1855. # Remove any old permissions from an existing share
  1856. # We don't want to drop the share and recreate it
  1857. # because current connections will be dropped and
  1858. # we decided that was bad
  1859. if ( $fshare_already_exists ) {
  1860. my %hash_share_members;
  1861. $hash_share_members{ lc $_ } = 1 foreach @share_members;
  1862. if ( !ExecuteProgram::ExecuteProgram("rmtshare.exe $build_share") ) {
  1863. errmsg( "Failure executing ". ExecuteProgram::GetLastCommand() );
  1864. errmsg ( " $_" ) foreach ExecuteProgram::GetLastOutput();
  1865. return;
  1866. }
  1867. my @current_share_permissions = ExecuteProgram::GetLastOutput();
  1868. undef @remove_permissions;
  1869. my $permission;
  1870. my $interesting_lines;
  1871. foreach $permission ( @current_share_permissions ) {
  1872. my $user_name;
  1873. # skip empty lines
  1874. next if ( $permission eq "" );
  1875. # Deal with no permissions set
  1876. if ( $permission eq "No permission specified." ) {
  1877. wrnmsg( "Share $build_share has no default permissions." );
  1878. last;
  1879. }
  1880. # Otherwise we only care about the lines between
  1881. # 'Permissions:' and 'The command completed successfully.'
  1882. if ( $permission eq 'Permissions:' ) {
  1883. $interesting_lines = 1;
  1884. next;
  1885. }
  1886. last if ( $permission eq 'The command completed successfully.' );
  1887. next if ( !$interesting_lines );
  1888. # Only need to verify name and not permissions as rmtshare should
  1889. # have already set the correct permissions for the name.
  1890. # Note that rmtshare also prepends a backslash for builtin
  1891. # groups, but you cannot use the backslash when specifying
  1892. # the name on the command line, so strip out any
  1893. # prepending backslash characters. This also catches
  1894. # local groups as they are reported as <machine>\Group on
  1895. # remote machines, but can only be added and removed as 'Group".
  1896. if($permission =~ /BVT/)
  1897. {
  1898. next;
  1899. }
  1900. ($user_name) = $permission =~ /^\s*\\?(?:$computer_name\\)?(\S+)\s*\:/i;
  1901. if ( !$user_name ) {
  1902. errmsg( "Problem parsing user from rmtshare output:" );
  1903. errmsg( " $permission" );
  1904. return;
  1905. }
  1906. if ( !exists $hash_share_members{ lc $user_name } ) {
  1907. push @remove_permissions, $user_name;
  1908. }
  1909. }
  1910. # Now actually call rmtshare to remove unrecognized users
  1911. if ( @remove_permissions ) {
  1912. dbgmsg( "Removing permissions from $build_share:" );
  1913. dbgmsg( " $_" ) foreach @remove_permissions;
  1914. if ( !ExecuteProgram::ExecuteProgram( "rmtshare.exe $build_share ". join " ", map { "/REMOVE $_" } @remove_permissions ) ) {
  1915. errmsg( "Failed removing old permissions from $build_share." );
  1916. errmsg ( " ". ExecuteProgram::GetLastCommand() );
  1917. errmsg ( " $_" ) foreach ExecuteProgram::GetLastOutput();
  1918. return;
  1919. }
  1920. }
  1921. }
  1922. # Verify that share is reachable
  1923. # This is making the assumption that we
  1924. # are allowed access to the share
  1925. if ( ! -e $build_share ) {
  1926. errmsg( "Unable to access $build_share ($!)" );
  1927. return;
  1928. }
  1929. # Add to accessed shares array
  1930. push @$accessed_shares, $build_share;
  1931. return 1;
  1932. }
  1933. sub LowerBuildShare {
  1934. my $named_parameters = shift;
  1935. my ( $accessed_shares, $branch, $sku, $lang ) =
  1936. GetNamedParameters( $named_parameters,
  1937. q(AccessedShares), q(Branch), q(Sku), q(Language) );
  1938. my ( $build_share, $sub_path ) = GetShareName( $named_parameters );
  1939. if ( !defined $build_share) {
  1940. errmsg( "Unable to determine release share for $branch / $sku." );
  1941. return;
  1942. }
  1943. # Optimization: check if we have already removed this share
  1944. # (especially useful when each sku uses the same share)
  1945. foreach ( @$accessed_shares ) {
  1946. return 1 if ( lc $_ eq lc $build_share );
  1947. }
  1948. # Check if the share exists
  1949. my $fshare_exists = ( -e $build_share );
  1950. # Remove the share
  1951. if ( $fshare_exists ) {
  1952. if ( !ExecuteProgram::ExecuteProgram( "rmtshare.exe $build_share /DELETE" ) ) {
  1953. errmsg( "Failed removing share $build_share." );
  1954. errmsg( " ". ExecuteProgram::GetLastCommand() );
  1955. errmsg( " $_") foreach ExecuteProgram::GetLastOutput();
  1956. return;
  1957. }
  1958. }
  1959. # If the share doesn't exist we are done,
  1960. # but do a sanity check on the reason
  1961. elsif ( $! != 2 ) {
  1962. errmsg( "Unable to verify share $build_share ($!)." );
  1963. return;
  1964. }
  1965. # Add to accessed shares array
  1966. push @$accessed_shares, $build_share;
  1967. return 1;
  1968. }
  1969. sub GetReleasePath {
  1970. my( @net_share_output, @net_share_local_path_entry );
  1971. my( $field_name, $field_value );
  1972. if ( !ExecuteProgram::ExecuteProgram("net share release") ) {
  1973. errmsg( "Unable to find share \'release\'.");
  1974. errmsg( " ". ExecuteProgram::GetLastCommand() );
  1975. errmsg( " $_" ) foreach ExecuteProgram::GetLastOutput();
  1976. return;
  1977. }
  1978. @net_share_output = ExecuteProgram::GetLastOutput();
  1979. # note that we are using the second line of the net share
  1980. # output to get this data. if this changes, it'll break.
  1981. @net_share_local_path_entry = split /\s+/, $net_share_output[1];
  1982. dbgmsg( "Path information for release is '". (join " ", @net_share_local_path_entry). "'" );
  1983. if ( $net_share_local_path_entry[0] ne "Path" ) {
  1984. wrnmsg( "Second line of net share does not start with " .
  1985. "\'Path\' -- possibly another language?");
  1986. }
  1987. dbgmsg( "Release path is $net_share_local_path_entry[1]");
  1988. return $net_share_local_path_entry[1];
  1989. }
  1990. #
  1991. # This is a wrapper to glob that expects and
  1992. # returns paths using '\' as the path separator
  1993. #
  1994. sub globex ($)
  1995. {
  1996. my $match_criteria = shift;
  1997. croak "Must specify parameter for globex" if ( !defined $match_criteria );
  1998. # Need to use '/' for UNC paths, so just convert to all '/''s
  1999. $match_criteria =~ s/\\/\//g;
  2000. # Return the results, converting back to '\'
  2001. return grep { s/\//\\/g } glob( $match_criteria );
  2002. }
  2003. parseargs('?' => \&Usage,
  2004. 'lower' => sub { $Op = 'lower' },
  2005. 'n:' => \$Build_Number,
  2006. 'q:' => \&ValidateAndSetQuality,
  2007. 'a:' => \$Architecture,
  2008. 't:' => \$Debug_Type,
  2009. 'time:' => \$Time_Stamp,
  2010. 'f' => \$fOverride_Safe,
  2011. 'safe' => \$fSafe,
  2012. 'replace' => \$fReplaceAtNumberedLink,
  2013. 'd' => \$Logmsg::DEBUG);
  2014. # 'l:' => \$Language, # Done by ParseEnv
  2015. Usage() if ( @ARGV );
  2016. $Language = $ENV{LANG};
  2017. # Initialize with default values if not specified
  2018. $Op ||= 'raise';
  2019. $Build_Number ||= '*';
  2020. $Architecture ||= '*';
  2021. $Time_Stamp ||= '*';
  2022. $Debug_Type ||= '*';
  2023. $Platform = "$Architecture$Debug_Type";
  2024. if ( !Main() ) {
  2025. errmsg( "Terminated abnormally." );
  2026. 1;
  2027. }
  2028. else {
  2029. logmsg( "Completed successfully." );
  2030. 0;
  2031. }
  2032. sub ValidateAndSetQuality {
  2033. my $quality = shift;
  2034. if ( exists $Ordered_Qualities{ lc $quality } ) {
  2035. $Quality = $quality;
  2036. return 1;
  2037. }
  2038. else {
  2039. errmsg( "Unrecognized quality \'$quality\'" );
  2040. exit( 1 );
  2041. }
  2042. }
  2043. sub Usage {
  2044. my $name = $0;
  2045. print "$name\n";
  2046. $name =~ s/.*?([^\\]+)$/$1/;
  2047. print <<USAGE;
  2048. $name -n:\# -q:qly [-lower] [-a:arc] [-l:lng] [-d] [-?]
  2049. -n:# Raise build number #
  2050. -q:qly Raise to 'qly' quality
  2051. -lower Lower a build and remove from dfs
  2052. -a:arc Act on architecture 'arc' (e.g. ??????)
  2053. -t:typ Act on debug type 'typ' (e.g. fre or chk)
  2054. -time:yymmdd-hhmm Use time-stamp criteria
  2055. -l:lng Raise for language 'lng' (from codes.txt)
  2056. -safe Used to raise an old build to TST/IDx (RAISE only)
  2057. -replace Will replace "newer" builds at a numbered link
  2058. -f Force an action that would not normally be taken
  2059. -d Debug mode
  2060. -? Display usage (this screen)
  2061. $name will issue the appropriate DFS commands to raise
  2062. a build to a specific quality.
  2063. USAGE
  2064. exit(1);
  2065. }
  2066. package ExecuteProgram;
  2067. my $Last_Command;
  2068. my @Last_Output;
  2069. sub ExecuteProgram {
  2070. $Last_Command = shift;
  2071. @Last_Output = `$Last_Command`;
  2072. chomp $_ foreach ( @Last_Output );
  2073. if ( $? ) {
  2074. return;
  2075. }
  2076. else {
  2077. return 1;
  2078. }
  2079. }
  2080. sub GetLastCommand {
  2081. return $Last_Command;
  2082. }
  2083. sub GetLastOutput {
  2084. return @Last_Output;
  2085. }
  2086. # Object to control synchronization among machines
  2087. package MultiMachineLock;
  2088. use Win32::ChangeNotify;
  2089. use Win32::Event;
  2090. sub new {
  2091. my $class = shift;
  2092. my $spool_path = shift;
  2093. my $timeout = shift;
  2094. return if not defined $spool_path;
  2095. my $instance = {
  2096. Path => $spool_path,
  2097. Timeout => $timeout || INFINITE, # default timeout is INFINITE
  2098. File => "$ENV{COMPUTERNAME}.txt",
  2099. NotificationObj => undef,
  2100. LocalSyncObj => undef,
  2101. fHaveLocalSync => 0,
  2102. Count => 0,
  2103. LastErrorMsg => "",
  2104. LastErrorValue => 0
  2105. };
  2106. return bless $instance;
  2107. }
  2108. sub Lock {
  2109. my $self = shift;
  2110. my $wait_return;
  2111. return unless ref $self;
  2112. # If we already hold the lock just bump up the ref-count
  2113. if ( 0 != $self->{Count} ) {
  2114. $self->{Count}++;
  2115. return 1;
  2116. }
  2117. # Need to synchronize local access (only one program
  2118. # at a time from the same machine can hold the lock)
  2119. if ( !defined $self->{LocalSyncObj} ) {
  2120. my $lock_path = $self->{Path};
  2121. $lock_path =~ s/\\\\/remote\\/;
  2122. $lock_path =~ s/\\/\//g;
  2123. $self->{LocalSyncObj} = new Win32::Event ( 0, 1, "Global\\$lock_path" );
  2124. if ( !defined $self->{LocalSyncObj} ) {
  2125. $self->{LastErrorMsg} = "Unable to create a local event ($^E).";
  2126. $self->{LastErrorValue} = 1;
  2127. return;
  2128. }
  2129. }
  2130. if ( !$self->{fHaveLocalSync} ) {
  2131. $wait_return = $self->{LocalSyncObj}->wait( $self->{Timeout} );
  2132. # Handle timeout
  2133. if ( 0 == $wait_return ) {
  2134. $self->{LastErrorMsg} = "Waiting on another process on machine.";
  2135. $self->{LastErrorValue} = 2; # 2 = timeout
  2136. return;
  2137. }
  2138. # Handle error condition
  2139. elsif ( 1 != $wait_return ) {
  2140. $self->{LastErrorMsg} = "Unexpected error waiting for local synchronization (wait returned ". $wait_return. ")";
  2141. $self->{LastErrorValue} = 1;
  2142. return;
  2143. }
  2144. $self->{fHaveLocalSync} = 1;
  2145. }
  2146. # Check for our existence before
  2147. # we create a file in the queue
  2148. # (this will be the case if something bad
  2149. # happened on the last run and we are
  2150. # rerunning the command to recover)
  2151. if ( ! -e "$self->{Path}\\$self->{File}" ) {
  2152. unless ( open SPOOLFILE, "> $self->{Path}\\$self->{File}" ) {
  2153. $self->{LastErrorMsg} = "Failed to create synchronization file '$self->{Path}\\$self->{File}' ($!).";
  2154. $self->{LastErrorValue} = 1;
  2155. return;
  2156. }
  2157. print SPOOLFILE "This file is used to synchronize access among machines.";
  2158. close SPOOLFILE;
  2159. }
  2160. # Create the notification object if this is the first time we were called
  2161. $self->{NotificationObj} ||= new Win32::ChangeNotify( $self->{Path}, 0, FILE_NOTIFY_CHANGE_FILE_NAME );
  2162. if ( !defined ${$self->{NotificationObj}} ) {
  2163. $self->{LastErrorMsg} = "Failed creating monitor object ($^E).";
  2164. $self->{LastErrorValue} = 1;
  2165. return;
  2166. }
  2167. # Check our position in the 'queue' at the beginning
  2168. if ( !ExecuteProgram::ExecuteProgram("dir /b /od $self->{Path}") ) {
  2169. $self->{LastErrorMsg} = "Unable to view directory $self->{Path}.\n";
  2170. $self->{LastErrorMsg} .= " ". ExecuteProgram::GetLastCommand(). "\n";
  2171. $self->{LastErrorMsg} .= " $_\n" foreach ExecuteProgram::GetLastOutput();
  2172. $self->{LastErrorValue} = 1;
  2173. return;
  2174. }
  2175. my @ordered_spool_directory = ExecuteProgram::GetLastOutput();
  2176. # We are at the top
  2177. if ( $ordered_spool_directory[0] eq $self->{File} ) {
  2178. $self->{Count}++;
  2179. return 1;
  2180. }
  2181. # Recheck our position in the 'queue' everytime
  2182. # the state of the directory changes until we
  2183. # are in the top position or we hit the timeout
  2184. # value with no activity
  2185. while ( 1 ) {
  2186. $self->{NotificationObj}->reset();
  2187. $wait_return = $self->{NotificationObj}->wait( $self->{Timeout} );
  2188. if ( !ExecuteProgram::ExecuteProgram("dir /b /od $self->{Path}") ) {
  2189. $self->{LastErrorMsg} = "Unable to view directory $self->{Path}.\n";
  2190. $self->{LastErrorMsg} .= " ". ExecuteProgram::GetLastCommand(). "\n";
  2191. $self->{LastErrorMsg} .= " $_\n" foreach ExecuteProgram::GetLastOutput();
  2192. $self->{LastErrorValue} = 1;
  2193. return;
  2194. }
  2195. @ordered_spool_directory = ExecuteProgram::GetLastOutput();
  2196. # We are at the top
  2197. if ( $ordered_spool_directory[0] eq $self->{File} ) {
  2198. $self->{Count}++;
  2199. return 1;
  2200. }
  2201. # Still waiting for others
  2202. else {
  2203. # Make sure we are still in the queue
  2204. my $found_self;
  2205. foreach ( @ordered_spool_directory ) {
  2206. $found_self = 1 if ( $_ eq "$self->{File}" );
  2207. }
  2208. if ( !$found_self ) {
  2209. # Lost our synchronization file -- recreating...
  2210. unless ( open SPOOLFILE, "> $self->{Path}\\$self->{File}" ) {
  2211. $self->{LastErrorMsg} = "Lost our synchronization file '$self->{Path}\\$self->{File}' -- failed to recreate. ($!).";
  2212. $self->{LastErrorValue} = 1;
  2213. return;
  2214. }
  2215. print SPOOLFILE "This file is used to synchronize access among machines.";
  2216. close SPOOLFILE;
  2217. }
  2218. }
  2219. # Handle timeout
  2220. if ( 0 == $wait_return ) {
  2221. my $machine_name = $ordered_spool_directory[0];
  2222. $machine_name =~ s/\.txt$//;
  2223. $self->{LastErrorMsg} = "Waiting on $machine_name ($self->{Path}\\$ordered_spool_directory[0]).";
  2224. $self->{LastErrorValue} = 2; # 2 = timeout
  2225. return;
  2226. }
  2227. # Handle error condition
  2228. elsif ( 1 != $wait_return ) {
  2229. $self->{LastErrorMsg} = "Unexpected error waiting on directory to change (wait returned ". $wait_return. ")";
  2230. $self->{LastErrorValue} = 1;
  2231. return;
  2232. }
  2233. }
  2234. $self->{LastErrorMsg} = "Jumped out of an infinte loop -- no idea how we got here";
  2235. $self->{LastErrorValue} = 1;
  2236. return;
  2237. }
  2238. sub Unlock {
  2239. my $self = shift;
  2240. return unless ref $self;
  2241. # Can't release a lock we don't hold
  2242. if ( 0 == $self->{Count} ) {
  2243. return;
  2244. }
  2245. # Decrement lock count
  2246. if ( 0 == --$self->{Count} ) {
  2247. if ( ! unlink "$self->{Path}\\$self->{File}" ) {
  2248. $self->{LastErrorMsg} = "Failed to delete synchronization file ($self->{Path}\\$self->{File}). May need to delete by hand.";
  2249. $self->{LastErrorValue} = 1;
  2250. }
  2251. $self->{LocalSyncObj}->set();
  2252. }
  2253. return 1;
  2254. }
  2255. sub GetLastError {
  2256. my $self = shift;
  2257. return unless ref $self;
  2258. return $self->{LastErrorValue};
  2259. }
  2260. sub GetLastErrorMessage {
  2261. my $self = shift;
  2262. return unless ref $self;
  2263. return $self->{LastErrorMsg};
  2264. }
  2265. sub DESTROY {
  2266. my $self = shift;
  2267. return unless ref $self;
  2268. if ( $self->{Count} > 0 ) {
  2269. #print "Abandoning lock file -- attempting to delete.";
  2270. $self->{Count} = 1;
  2271. return $self->Unlock();
  2272. } else {
  2273. if ( $self->{fHaveLocalSync} ) {
  2274. $self->{LocalSyncObj}->set();
  2275. }
  2276. return 1;
  2277. }
  2278. }
  2279. package StaticDfsMap;
  2280. my $Last_Error_Msg;
  2281. sub TIEHASH {
  2282. my $class = shift;
  2283. my $dfs_root = shift;
  2284. my $instance;
  2285. my ($dfs_view_info, $cur_dfs_link);
  2286. my $fchild_dfs_servers;
  2287. return if ( @_ or !defined $dfs_root );
  2288. # Check current dfscmd.exe version
  2289. if ( !VerifyDfsCmdVer() ) {
  2290. return; # Error message should already be set
  2291. }
  2292. # Remove trailing backslash (if it exists) from DFS root
  2293. $dfs_root =~ s/\\$//;
  2294. # Read in current DFS view
  2295. if ( !ExecuteProgram::ExecuteProgram("dfscmd.exe /view $dfs_root /batch") ) {
  2296. $Last_Error_Msg = "Failed viewing dfs root $dfs_root.\n";
  2297. $Last_Error_Msg .= " ". ExecuteProgram::GetLastCommand(). "\n";
  2298. $Last_Error_Msg .= " $_\n" foreach ExecuteProgram::GetLastOutput();
  2299. return;
  2300. }
  2301. my @full_dfs_view = ExecuteProgram::GetLastOutput();
  2302. $instance = {
  2303. "DFS Root" => $dfs_root };
  2304. # "DFS Work Root" => {}
  2305. # "DFS Servers" => (),
  2306. # "Map" => {}
  2307. # Expected output:
  2308. # dfscmd /map "\\<dfs_root>\<link_name>" "\\<server>\<share>" "<comment>"
  2309. my %dfs_map;
  2310. foreach my $dfs_view_info ( @full_dfs_view ) {
  2311. chomp $dfs_view_info;
  2312. next if ( !$dfs_view_info );
  2313. next if ( $dfs_view_info =~ /^REM BATCH RESTORE SCRIPT/ );
  2314. $dfs_view_info = lc $dfs_view_info;
  2315. # Are we done?
  2316. if ( $dfs_view_info eq 'the command completed successfully.' ) {
  2317. last;
  2318. }
  2319. my ($dfs_link, $share, $comment) = $dfs_view_info =~ /dfscmd \/(?:map|add) "(.*?)" "(.*?)"(?: "(.*)")?$/;
  2320. my $relative_link_name = $dfs_link;
  2321. $relative_link_name =~ s/^\Q$dfs_root\E\\//i;
  2322. if ( !$dfs_link || !$share || !$relative_link_name )
  2323. {
  2324. $Last_Error_Msg = "Unrecognized entry parsing ". ExecuteProgram::GetLastCommand(). "\n";
  2325. $Last_Error_Msg .= " $dfs_view_info";
  2326. return;
  2327. }
  2328. # If it is just the root, then this entry represents a hosting server
  2329. if ( lc $dfs_link eq lc $dfs_root ) {
  2330. push @{$instance->{"DFS Servers"}}, $share;
  2331. }
  2332. # Otherwise associate the link and the share
  2333. else {
  2334. push @{$dfs_map{$relative_link_name}}, $share;
  2335. push @{$dfs_map{$share}}, $relative_link_name;
  2336. }
  2337. }
  2338. # Expect to find at least one DFS server under the root
  2339. if ( !exists $instance->{"DFS Servers"} ) {
  2340. $Last_Error_Msg = "Did not find the root node and specified servers for $dfs_root.";
  2341. return;
  2342. }
  2343. # FUTURE: find a nicer way around this
  2344. # In cases where more than one server is actually hosting DFS,
  2345. # we need to pick one of the hosting machines to perform
  2346. # our commands against so that we don't have to wait for
  2347. # replication (this is to help tests for just created and
  2348. # just removed directories succeed).
  2349. foreach ( @{$instance->{"DFS Servers"}} ) {
  2350. if ( -e $_ ) {
  2351. $instance->{"DFS Work Root"} = $_;
  2352. last;
  2353. }
  2354. }
  2355. # Verify we could see at least one
  2356. if ( ! exists $instance->{"DFS Work Root"} ) {
  2357. $Last_Error_Msg = "Could not find an accessible DFS server.";
  2358. return;
  2359. }
  2360. #print "\n$_:\n ". (join "\n ", @{$dfs_map{$_}}) foreach ( sort keys %dfs_map );
  2361. # Store the map we just created
  2362. # inside of our private hash data
  2363. $instance->{"Map"} = \%dfs_map;
  2364. return bless $instance, $class;
  2365. }
  2366. sub FETCH {
  2367. my $self = shift;
  2368. my $link_or_share = shift;
  2369. my $dfs_map = $self->{"Map"};
  2370. return $dfs_map->{ lc $link_or_share } if (exists $dfs_map->{ lc $link_or_share });
  2371. return;
  2372. }
  2373. sub STORE {
  2374. my $self = shift;
  2375. my $link_or_share = shift;
  2376. my $new_link = shift;
  2377. my $dfs_map = $self->{"Map"};
  2378. my $fshare = $link_or_share =~ /^\\\\/;
  2379. # Remove any trailing backslashes
  2380. $link_or_share =~ s/\\$//;
  2381. $new_link =~ s/\\$//;
  2382. # If mapping already exists, we are done
  2383. my $cur_mappings = $dfs_map->{lc $link_or_share};
  2384. foreach ( @$cur_mappings ) {
  2385. return 1 if ( lc $_ eq lc $new_link );
  2386. }
  2387. # Create new DFS mapping
  2388. if ( !ExecuteProgram::ExecuteProgram(
  2389. "dfscmd.exe ". (@$cur_mappings?"/add ":"/map "). $self->{"DFS Root"}.
  2390. ($fshare?"\\$new_link $link_or_share":"\\$link_or_share $new_link") )
  2391. ) {
  2392. #
  2393. # COMMENTED OUT: If someone else is trying to manipulate the same
  2394. # links as we are while we are doing this then
  2395. # we have a problem -- don't try to correct
  2396. #
  2397. # Might have gotten into a race-condition with another
  2398. # machine attempting to add a new link -- if that is
  2399. # a possibility try a map instead before giving up
  2400. #
  2401. #if ( !@$cur_mappings ||
  2402. # !ExecuteProgram( "dfscmd.exe /map ". $self->{"DFS Root"}.
  2403. # ($fshare?"\\$new_link $link_or_share":"\\$link_or_share $new_link") ) ) {
  2404. # errmsg( "Failed to create link \\$self->{Dfs Root}\\". ($fshare?"\\$new_link => $link_or_share":"\\$link_or_share => $new_link") . "." );
  2405. # return;
  2406. #}
  2407. $Last_Error_Msg = "Problem adding/mapping link ". ($fshare?"$link_or_share":"$new_link"). ":\n";
  2408. $Last_Error_Msg .= " " . ExecuteProgram::GetLastCommand() . "\n";
  2409. $Last_Error_Msg .= join "\n ", ExecuteProgram::GetLastOutput();
  2410. return;
  2411. }
  2412. # Update the hash with the new information
  2413. push @{$dfs_map->{lc $link_or_share}}, $new_link;
  2414. push @{$dfs_map->{lc $new_link}}, $link_or_share;
  2415. return 1;
  2416. }
  2417. sub DELETE {
  2418. my $self = shift;
  2419. my $link_or_share = shift;
  2420. my $dfs_map = $self->{"Map"};
  2421. my $fshare = $link_or_share =~ /^\\\\/;
  2422. # Make sure DFS mapping exists before attempting to delete it
  2423. return 1 if ( !exists $dfs_map->{lc $link_or_share} );
  2424. my $cur_mappings = $dfs_map->{lc $link_or_share};
  2425. return 1 if ( !@$cur_mappings );
  2426. if ( $fshare ) {
  2427. my $next_link;
  2428. # Remove all DFS links pointing to this share
  2429. foreach $next_link ( @$cur_mappings ) {
  2430. if ( !$self->RemoveShareFromLink( $next_link, $link_or_share ) ) {
  2431. return;
  2432. }
  2433. }
  2434. } else {
  2435. if ( !ExecuteProgram::ExecuteProgram(
  2436. "dfscmd.exe /unmap ". $self->{"DFS Root"}. "\\$link_or_share"
  2437. ) ) {
  2438. $Last_Error_Msg = "Failure trying to unmap $link_or_share:\n";
  2439. $Last_Error_Msg .= " " . ExecuteProgram::GetLastCommand() . "\n";
  2440. $Last_Error_Msg .= join "\n ", ExecuteProgram::GetLastOutput();
  2441. return;
  2442. }
  2443. # Remove the hash references
  2444. delete $dfs_map->{lc $link_or_share};
  2445. for ( my $i = 0; $i < scalar(@$cur_mappings); $i++) {
  2446. if ( lc $cur_mappings->[$i] eq lc $link_or_share ) {
  2447. splice @$cur_mappings, $i, 1;
  2448. }
  2449. }
  2450. }
  2451. return 1;
  2452. }
  2453. sub CLEAR {
  2454. my $self = shift;
  2455. # We don't actually want to be able to delete the entire
  2456. # DFS structure, so we will just clear our information
  2457. undef %$self;
  2458. return;
  2459. }
  2460. sub EXISTS {
  2461. my $self = shift;
  2462. my $link_or_share = shift;
  2463. my $dfs_map = $self->{"Map"};
  2464. return exists $dfs_map->{lc $link_or_share};
  2465. }
  2466. sub FIRSTKEY {
  2467. my $self = shift;
  2468. my $dfs_map = $self->{"Map"};
  2469. my $force_to_first_key = keys %$dfs_map;
  2470. return scalar each %$dfs_map;
  2471. }
  2472. sub NEXTKEY {
  2473. my $self = shift;
  2474. my $last_key = shift;
  2475. my $dfs_map = $self->{"Map"};
  2476. return scalar each %$dfs_map;
  2477. }
  2478. sub DESTROY {
  2479. my $self = shift;
  2480. return;
  2481. }
  2482. #
  2483. # Need more methods than provided by TIEHASH defaults
  2484. #
  2485. sub RemoveShareFromLink {
  2486. my $self = shift;
  2487. my $link_root = shift;
  2488. my $share_name = shift;
  2489. my $dfs_map = $self->{"Map"};
  2490. # Get current associated links
  2491. my $cur_link_mappings = $dfs_map->{lc $link_root};
  2492. my $cur_share_mappings = $dfs_map->{lc $share_name};
  2493. my $fshare_linked;
  2494. # If the share is not part of the link, return
  2495. foreach (@$cur_link_mappings) {
  2496. if ( lc $_ eq lc $share_name ) {
  2497. $fshare_linked = 1;
  2498. last;
  2499. }
  2500. }
  2501. return 1 if ( !$fshare_linked );
  2502. my $dfs_command = "dfscmd.exe /remove ".
  2503. $self->{'DFS Root'}.
  2504. "\\$link_root $share_name";
  2505. if ( !ExecuteProgram::ExecuteProgram( $dfs_command ) ) {
  2506. $Last_Error_Msg = "Could not remove $share_name from DFS link $link_root:\n";
  2507. $Last_Error_Msg .= " " . ExecuteProgram::GetLastCommand() . "\n";
  2508. $Last_Error_Msg .= join "\n ", ExecuteProgram::GetLastOutput();
  2509. return;
  2510. }
  2511. # Remove the associated hash entries
  2512. for ( my $i = 0; $i < scalar(@$cur_link_mappings); $i++) {
  2513. if ( lc $cur_link_mappings->[$i] eq lc $share_name ) {
  2514. splice @$cur_link_mappings, $i, 1;
  2515. }
  2516. }
  2517. for ( my $i = 0; $i < scalar(@$cur_share_mappings); $i++) {
  2518. if ( lc $cur_share_mappings->[$i] eq lc $link_root ) {
  2519. splice @$cur_share_mappings, $i, 1;
  2520. }
  2521. }
  2522. return 1;
  2523. }
  2524. sub Flush {
  2525. my $self = shift;
  2526. # Do nothing for now, but here in preparation for batching of commands
  2527. return 1;
  2528. }
  2529. sub GetDfsRoot {
  2530. my $self = shift;
  2531. # return $self->{ "DFS Root" };
  2532. return $self->{ "DFS Root" };
  2533. }
  2534. sub GetDfsHosts {
  2535. my $self = shift;
  2536. return @{$self->{"DFS Servers"}};
  2537. }
  2538. sub VerifyDfsCmdVer {
  2539. my ( @file_spec, $file_ver_string, @file_ver_info );
  2540. # Verify the dfscmd.exe is proper
  2541. if ( !ExecuteProgram::ExecuteProgram("where dfscmd.exe") ) {
  2542. $Last_Error_Msg = "Could not find dfscmd.exe\n";
  2543. $Last_Error_Msg .= " ". ExecuteProgram::GetLastCommand(). "\n";
  2544. $Last_Error_Msg .= " $_\n" foreach ExecuteProgram::GetLastOutput();
  2545. return;
  2546. }
  2547. @file_spec = ExecuteProgram::GetLastOutput();
  2548. if ( !ExecuteProgram::ExecuteProgram("filever $file_spec[0]") ) {
  2549. $Last_Error_Msg = "Failed executing \'filever $file_spec[0]\'.";
  2550. return;
  2551. }
  2552. $file_ver_string = join " ", ExecuteProgram::GetLastOutput();
  2553. @file_ver_info = split /\s+/, $file_ver_string;
  2554. # Fourth field is version number; compare it with known good
  2555. if ($file_ver_info[4] lt "5.0.2203.1") {
  2556. $Last_Error_Msg = "$file_ver_info[0] version is less than 5.0.2203.1. Please install DFSCMD.EXE from a 2203 or later build.";
  2557. return;
  2558. }
  2559. return 1;
  2560. }
  2561. sub GetLastErrorMessage {
  2562. return $Last_Error_Msg;
  2563. }