lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <87bjsmsy4o.fsf@trenco.lwn.net>
Date: Wed, 23 Apr 2025 09:20:23 -0600
From: Jonathan Corbet <corbet@....net>
To: saivishnu725@...il.com, mchehab@...nel.org
Cc: linux-doc@...r.kernel.org, linux-kernel@...r.kernel.org, Sai Vishnu M
 <saivishnu725@...il.com>
Subject: Re: [PATCH] add --interactive feature

OK, we are getting somewhere, but there are various things to point
out/fix here. 

saivishnu725@...il.com writes:

> From: Sai Vishnu M <saivishnu725@...il.com>
>
> This patch introduces an interactive mode to the sphinx-pre-install script
> that guides users through missing dependency installation for convenience.

Some maintainers will react strongly to "this patch", insisting that
changelogs be written in the imperative mode.  I am less fussy about
such things, but that's a good practice to follow in general.

> - Adds `--interactive` flag to trigger prompt-based guidance
> - Handles cases where stdin is not available
> - Implements default behavior for invalid or no input
> - Improves messages for unknown distros and errors

A list like this is a clear sign that a patch needs to be broken up into
a series.  Remember, each patch should do one clearly verifiable thing.
When you mix changes like this, you make things much harder to review.

> RFC: https://lore.kernel.org/linux-doc/20250410155414.47114-1-saivishnu725@gmail.com/T/#u

This is not a normal patch tag.  Putting in a link to the RFC is fine,
but it should go below the "---" line.  It also *really* helps to add a
summary of what has changed since the previous revision.

> Signed-off-by: Sai Vishnu M <saivishnu725@...il.com>
> ---
>  scripts/sphinx-pre-install | 185 ++++++++++++++++++++++++++++++++-----
>  1 file changed, 160 insertions(+), 25 deletions(-)
>
> diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install
> index ad9945ccb0cf..a3fbe920bb44 100755
> --- a/scripts/sphinx-pre-install
> +++ b/scripts/sphinx-pre-install
> @@ -42,6 +42,7 @@ my $latest_avail_ver;
>  my $pdf = 1;
>  my $virtualenv = 1;
>  my $version_check = 0;
> +my $interactive = 0;
>  
>  #
>  # List of required texlive packages on Fedora and OpenSuse
> @@ -338,12 +339,96 @@ sub which($)
>  	return undef;
>  }
>  
> +sub run_if_interactive($)
> +{
> +	my $command = shift;
> +	printf("\n\t$command\n");
> +
> +	if($interactive) {

Please try to stick with something close to the kernel coding style
(space after "if")

> +		printf("Run the command now? [Y/n, default:Y]: ");
> +		my $user_input = <STDIN>;
> +		chomp $user_input;
> +		# Default = Y

Given that the code just above makes the default explicit, this comment
is not too helpful.

> +		if ($user_input eq '' or $user_input =~ /^y(es)?$/i) {
> +			system($command) == 0 or warn "Failed to run the command";
> +		}

It seems that, if a command fails, everything should come to a stop
immediately? 

> +	}
> +}
> +
> +sub fallback_unknown_distro()
> +{
> +	# Fall-back to generic hint code for other distros
> +	# That's far from ideal, specially for LaTeX dependencies.
> +	my %map = (
> +		"sphinx-build" => "sphinx"
> +	);
> +	check_missing_tex(2) if ($pdf);
> +	check_missing(\%map);
> +	print "I don't know distro $system_release.\n";
> +	print "So, I can't provide you a hint with the install procedure.\n";
> +	print "There are likely missing dependencies.\n";
> +}
> +
> +# checks if a package exists in path
> +sub is_in_path($)
> +{
> +    my $cmd = shift;
> +    for my $dir (split /:/, $ENV{PATH}) {
> +        return 1 if -x "$dir/$cmd";
> +    }
> +    return 0;
> +}
> +
> +# adding a check in --interactive
> +# Reason: Selecting an incorrect distribution in cases where the user's distribution is unrecognized may lead to unexpected behavior.

Please keep lines within 80 columns

> +sub check_user_choice($)
> +{
> +	my $package_manager = shift;
> +	if ($interactive) {
> +		# checks if the package manager exists. hence, confirming the distribution
> +		if (!is_in_path($package_manager)) {
> +			print "$package_manager not found\n";
> +			fallback_unknown_distro();
> +			return 0;
> +		}
> +		return 1; # package_manager found
> +	}
> +	return 1; # non-interactive
> +}

...and the case where the tool isn't in the user's path, but running
under sudo will find it...?

> +# checks if either of the package manager exists
> +sub check_user_choice_two($$)
> +{
> +	my ($pm1, $pm2) = @_;
> +	if ($interactive) {
> +		my $found = 0;
> +		# checks if either of the package managers exists. hence, confirming the distribution
> +		if(is_in_path($pm1)) {

Again, watch coding style

> +			$found = 1;
> +		}
> +		if(is_in_path($pm2)) {
> +			$found = 1;
> +		}

this whole series could be something like:

  $found = is_in_path($pm1) or is_in_path($pm2)

right?

> +		if(!$found) {
> +			print "both $pm1 and $pm2 not found\n";
> +			fallback_unknown_distro();
> +			return 0; # package_manager not found
> +		}
> +		return 1; # package_manager found
> +	}
> +	return 1; # non-interactive
> +}
> +
>  #
>  # Subroutines that check distro-specific hints
>  #
>  
>  sub give_debian_hints()
>  {
> +	if (!check_user_choice("apt-get")) {
> +		return;
> +	}

I guess I don't understand why we have to do these checks.  We know it's
Debian, right?

>  	my %map = (
>  		"python-sphinx"		=> "python3-sphinx",
>  		"yaml"			=> "python3-yaml",
> @@ -374,11 +459,16 @@ sub give_debian_hints()
>  
>  	return if (!$need && !$optional);
>  	printf("You should run:\n") if ($verbose_warn_install);
> -	printf("\n\tsudo apt-get install $install\n");
> +	my $command = "sudo apt-get install $install";
> +	run_if_interactive($command);
>  }
>  
>  sub give_redhat_hints()
>  {
> +	if (!check_user_choice_two("dnf", "yum")) {
> +		return;
> +	}

Do we support any RH versions that still have yum at this point?

>  	my %map = (
>  		"python-sphinx"		=> "python3-sphinx",
>  		"yaml"			=> "python3-pyyaml",
> @@ -452,16 +542,21 @@ sub give_redhat_hints()
>  	if (!$old) {
>  		# dnf, for Fedora 18+
>  		printf("You should run:\n") if ($verbose_warn_install);
> -		printf("\n\tsudo dnf install -y $install\n");
> +		my $command = "sudo dnf install -y $install";
> +		run_if_interactive($command);
>  	} else {
>  		# yum, for RHEL (and clones) or Fedora version < 18
>  		printf("You should run:\n") if ($verbose_warn_install);
> -		printf("\n\tsudo yum install -y $install\n");
> +		my $command = "sudo yum install -y $install";
> +		run_if_interactive($command);
>  	}
>  }
>  
>  sub give_opensuse_hints()
>  {
> +	if (!check_user_choice("zypper")) {
> +		return;
> +	}
>  	my %map = (
>  		"python-sphinx"		=> "python3-sphinx",
>  		"yaml"			=> "python3-pyyaml",
> @@ -505,11 +600,16 @@ sub give_opensuse_hints()
>  
>  	return if (!$need && !$optional);
>  	printf("You should run:\n") if ($verbose_warn_install);
> -	printf("\n\tsudo zypper install --no-recommends $install\n");
> +	my $command = "sudo zypper install --no-recommends $install";
> +	run_if_interactive($command);
> +
>  }
>  
>  sub give_mageia_hints()
>  {
> +	if (!check_user_choice_two("dnf", "urpmi")) {
> +		return;
> +	}
>  	my %map = (
>  		"python-sphinx"		=> "python3-sphinx",
>  		"yaml"			=> "python3-yaml",
> @@ -538,7 +638,6 @@ sub give_mageia_hints()
>  		$noto_sans = "google-noto-sans-cjk-ttc-fonts";
>  	}
>  
> -
>  	if ($pdf) {
>  		check_missing_file(["/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc",
>  				    "/usr/share/fonts/TTF/NotoSans-Regular.ttf"],
> @@ -550,11 +649,17 @@ sub give_mageia_hints()
>  
>  	return if (!$need && !$optional);
>  	printf("You should run:\n") if ($verbose_warn_install);
> -	printf("\n\tsudo $packager_cmd $install\n");
> +	my $command = "sudo $packager_cmd $install";
> +	run_if_interactive($command);
> +
>  }
>  
>  sub give_arch_linux_hints()
>  {
> +	if (!check_user_choice("pacman")) {
> +		return;
> +	}
> +
>  	my %map = (
>  		"yaml"			=> "python-yaml",
>  		"virtualenv"		=> "python-virtualenv",
> @@ -581,11 +686,16 @@ sub give_arch_linux_hints()
>  
>  	return if (!$need && !$optional);
>  	printf("You should run:\n") if ($verbose_warn_install);
> -	printf("\n\tsudo pacman -S $install\n");
> +	my $command = "sudo pacman -S $install";
> +	run_if_interactive($command);
>  }
>  
>  sub give_gentoo_hints()
>  {
> +	if (!check_user_choice("emerge")) {
> +		return;
> +	}
> +
>  	my %map = (
>  		"yaml"			=> "dev-python/pyyaml",
>  		"virtualenv"		=> "dev-python/virtualenv",
> @@ -617,14 +727,15 @@ sub give_gentoo_hints()
>  	my $portage_cairo = "/etc/portage/package.use/graphviz";
>  
>  	if (qx(grep imagemagick $portage_imagemagick 2>/dev/null) eq "") {
> -		printf("\tsudo su -c 'echo \"$imagemagick\" > $portage_imagemagick'\n")
> +		my $imagemagick_command = "sudo su -c 'echo \"$imagemagick\" > $portage_imagemagick'";
> +		run_if_interactive($imagemagick_command);
>  	}
>  	if (qx(grep graphviz $portage_cairo 2>/dev/null) eq  "") {
> -		printf("\tsudo su -c 'echo \"$cairo\" > $portage_cairo'\n");
> +		my $portage_command = "sudo su -c 'echo \"$cairo\" > $portage_cairo'";
> +		run_if_interactive($portage_command);
>  	}
> -
> -	printf("\tsudo emerge --ask $install\n");
> -
> +	my $command = "sudo emerge --ask $install";
> +	run_if_interactive($command);
>  }
>  
>  sub check_distros()
> @@ -678,19 +789,35 @@ sub check_distros()
>  		give_gentoo_hints;
>  		return;
>  	}
> +	# if the distro is not recognised
> +	# but it is derived from the available options
> +	if ($interactive) {
> +		my @distros = (
> +			{ name => "Debian/Ubuntu", func => \&give_debian_hints },
> +			{ name => "RedHat/CentOS/Fedora", func => \&give_redhat_hints },
> +			{ name => "OpenSUSE", func => \&give_opensuse_hints },
> +			{ name => "Mageia", func => \&give_mageia_hints },
> +			{ name => "Arch Linux", func => \&give_arch_linux_hints },
> +			{ name => "Gentoo", func => \&give_gentoo_hints },
> +		);
> +		print "Which distro is your OS based on?\n";
> +		for my $i (0 .. $#distros) {
> +			printf("[%d] %s\n", $i + 1, $distros[$i]->{name});
> +		}
> +		print "[0] Others\n";

Are there really any cases where we will not detect the distribution,
but where our canned commands will work anyway?  Which distributions
have you tested this on?

> -	#
> -	# Fall-back to generic hint code for other distros
> -	# That's far from ideal, specially for LaTeX dependencies.
> -	#
> -	my %map = (
> -		"sphinx-build" => "sphinx"
> -	);
> -	check_missing_tex(2) if ($pdf);
> -	check_missing(\%map);
> -	print "I don't know distro $system_release.\n";
> -	print "So, I can't provide you a hint with the install procedure.\n";
> -	print "There are likely missing dependencies.\n";
> +		print "Select a number: ";
> +		my $choice = <STDIN>;
> +		chomp $choice;
> +
> +		if ($choice =~ /^\d+$/ && $choice >= 1 && $choice <= scalar(@distros)) {
> +			$distros[$choice - 1]->{func}->();
> +		} else {
> +			fallback_unknown_distro();
> +		}
> +	} else {
> +		fallback_unknown_distro();
> +	}
>  }
>  
>  #
> @@ -1002,12 +1129,20 @@ while (@ARGV) {
>  		$pdf = 0;
>  	} elsif ($arg eq "--version-check"){
>  		$version_check = 1;
> +	} elsif ($arg eq "--interactive") {
> +		# check if the user can interact with the script
> +		if (-t STDIN) {
> +			$interactive = 1;
> +		} else {
> +    		print "Non-interactive environment\n";
> +		}
>  	} else {
>  		print "Usage:\n\t$0 <--no-virtualenv> <--no-pdf> <--version-check>\n\n";
>  		print "Where:\n";
>  		print "\t--no-virtualenv\t- Recommend installing Sphinx instead of using a virtualenv\n";

Installing sphinx is happening either way, so this is not quite right.

>  		print "\t--version-check\t- if version is compatible, don't check for missing dependencies\n";
> -		print "\t--no-pdf\t- don't check for dependencies required to build PDF docs\n\n";
> +		print "\t--no-pdf\t- don't check for dependencies required to build PDF docs\n";
> +		print "\t--interactuve\t- Ask to intsall missing dependencies\n\n";

I don't think that's how you spell "interactive"

>  		exit -1;
>  	}
>  }

Thanks,

jon

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ