From 1c6c25606510e149ce6e0566d8acae81c8241c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Ferr=C3=A3o?= <2031761+viniciusferrao@users.noreply.github.com> Date: Sat, 11 Jul 2026 20:29:53 -0300 Subject: [PATCH] fix(xcat-client): make profile probe safe under errexit --- xCAT-client/xCAT-client.spec | 3 +- xCAT-test/unit/xcat_profile_errexit.t | 100 ++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 xCAT-test/unit/xcat_profile_errexit.t diff --git a/xCAT-client/xCAT-client.spec b/xCAT-client/xCAT-client.spec index b35349324..bedf31a89 100644 --- a/xCAT-client/xCAT-client.spec +++ b/xCAT-client/xCAT-client.spec @@ -318,8 +318,7 @@ MANPATH=\$XCATROOT/share/man:\$MANPATH export XCATROOT PATH MANPATH export PERL_BADLANG=0 # If /usr/local/share/perl5 is not already in @INC, add it to PERL5LIB -perl -e "print \"@INC\"" | grep -E "(^|\W)/usr/local/share/perl5($| )" > /dev/null -if [ \$? = 1 ]; then +if ! perl -e "print \"@INC\"" | grep -qE "(^|\W)/usr/local/share/perl5($| )"; then export PERL5LIB=/usr/local/share/perl5:\$PERL5LIB fi EOF diff --git a/xCAT-test/unit/xcat_profile_errexit.t b/xCAT-test/unit/xcat_profile_errexit.t new file mode 100644 index 000000000..2822f0fef --- /dev/null +++ b/xCAT-test/unit/xcat_profile_errexit.t @@ -0,0 +1,100 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use File::Spec; +use File::Temp qw(tempdir); +use FindBin; +use Test::More; + +my $repo_root = File::Spec->rel2abs( File::Spec->catdir( $FindBin::Bin, '..', '..' ) ); +my $spec_path = File::Spec->catfile( $repo_root, 'xCAT-client/xCAT-client.spec' ); + +open( my $spec_fh, '<', $spec_path ) or die "Unable to read $spec_path: $!"; +my $spec = do { local $/; <$spec_fh> }; +close($spec_fh); + +my ($profile_body) = $spec =~ m{^cat << EOF > /etc/profile\.d/xcat\.sh\n(.*?)^EOF\n}ms; +ok( defined($profile_body), 'found the RPM-generated xcat.sh profile' ) + or BAIL_OUT('Unable to extract xcat.sh from xCAT-client.spec'); + +my $tempdir = tempdir( CLEANUP => 1 ); +my $profile_path = File::Spec->catfile( $tempdir, 'xcat.sh' ); +my $install_prefix = File::Spec->catdir( $tempdir, 'opt', 'xcat' ); +my $render_script = <<"RENDER"; +cat << EOF > "\$1" +$profile_body +EOF +RENDER + +{ + local $ENV{RPM_INSTALL_PREFIX0} = $install_prefix; + my $render_status = system( + 'bash', '--noprofile', '--norc', '-c', + $render_script, 'bash', $profile_path + ); + is( $render_status, 0, 'rendered xcat.sh using the RPM heredoc' ) + or BAIL_OUT('Unable to render xcat.sh from xCAT-client.spec'); +} + +my @cases = ( + { + name => 'missing local Perl path under errexit', + inc => '/usr/lib/perl5 /usr/share/perl5', + expected => "reached\nPERL5LIB=/usr/local/share/perl5:sentinel\n", + }, + { + name => 'existing local Perl path under errexit', + inc => '/usr/lib/perl5 /usr/local/share/perl5 /usr/share/perl5', + expected => "reached\nPERL5LIB=sentinel\n", + }, + { + name => 'missing local Perl path under errexit and pipefail', + inc => '/usr/lib/perl5 /usr/share/perl5', + pipefail => 1, + expected => "reached\nPERL5LIB=/usr/local/share/perl5:sentinel\n", + }, +); + +for my $case (@cases) { + subtest $case->{name} => sub { + my ( $status, $output ) = run_profile( $profile_path, $case ); + + is( $status, 0, 'sourcing continues' ); + is( $output, $case->{expected}, 'PERL5LIB has the expected value' ); + }; +} + +done_testing; + +sub run_profile { + my ( $path, $case ) = @_; + my $runner = <<'BASH'; +perl() { + printf '%s' "$FAKE_PERL_INC" +} +export PERL5LIB=sentinel +. "$1" +printf 'reached\nPERL5LIB=%s\n' "$PERL5LIB" +BASH + + local $ENV{FAKE_PERL_INC} = $case->{inc}; + my @command = ( 'bash', '--noprofile', '--norc', '-e' ); + push @command, ( '-o', 'pipefail' ) if $case->{pipefail}; + push @command, ( '-c', $runner, 'bash', $path ); + + open( my $output_fh, '-|', @command ) + or die "Unable to run generated xcat.sh: $!"; + my $output = do { local $/; <$output_fh> }; + close($output_fh); + + my $raw_status = $?; + my $status = $raw_status == -1 + ? -1 + : ( $raw_status & 127 ) + ? 128 + ( $raw_status & 127 ) + : $raw_status >> 8; + + return ( $status, $output ); +}