forked from yast/yast-installation
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
763 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
# ------------------------------------------------------------------------------ | ||
# Copyright (c) 2021 SUSE LLC, All Rights Reserved. | ||
# | ||
# This program is free software; you can redistribute it and/or modify it under | ||
# the terms of version 2 of the GNU General Public License as published by the | ||
# Free Software Foundation. | ||
# | ||
# This program is distributed in the hope that it will be useful, but WITHOUT | ||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
# | ||
# ------------------------------------------------------------------------------ | ||
|
||
require "yast" | ||
require "installation/console/commands" | ||
require "installation/console/gui" | ||
require "installation/console/tui" | ||
|
||
Yast.import "UI" | ||
|
||
# Override the IRB to log all executed commands into the y2log so we know | ||
# what exactly happened there (in case user did something wrong or strange...) | ||
module IrbLogger | ||
# wrap the original "evaluate" method, do some logging around | ||
def evaluate(*args) | ||
statements = args[1] | ||
# do not log the internal IRB command for setting the last value variable | ||
if statements.is_a?(::String) && statements.start_with?("_ = ") | ||
super | ||
else | ||
Yast::Y2Logger.instance.info "Executing console command: #{statements.inspect}" | ||
ret = super | ||
Yast::Y2Logger.instance.info "Console command result: #{ret.inspect}" | ||
ret | ||
end | ||
end | ||
end | ||
|
||
# inject the code | ||
require "irb/workspace" | ||
module IRB # :nodoc: | ||
class WorkSpace | ||
prepend IrbLogger | ||
end | ||
end | ||
|
||
module Installation | ||
module Console | ||
class << self | ||
# open a console and run an interactive IRB session in it | ||
# testing in installed system: | ||
# ruby -I src/lib -r installation/console.rb -e ::Installation::Console.run | ||
def run | ||
console = Yast::UI.TextMode ? Console::Tui.new : Console::Gui.new | ||
console.run do | ||
commands = Console::Commands.new(console) | ||
# print the basic help text | ||
commands.welcome | ||
|
||
# start an IRB session in the context of the "commands" object | ||
irb(commands) | ||
end | ||
end | ||
|
||
private | ||
|
||
# configure IRB and start an interactive session | ||
# @param context [Object] context in which the IRB session runs | ||
def irb(context) | ||
# lazy loading | ||
require "irb" | ||
# enable TAB completion | ||
require "irb/completion" | ||
|
||
# see the Binding::irb method in irb.rb in the Ruby stdlib | ||
IRB.setup(eval("__FILE__"), argv: []) | ||
# use a simple prompt with some customizations | ||
IRB.conf[:PROMPT][:YAST] = IRB.conf[:PROMPT][:SIMPLE].dup | ||
IRB.conf[:PROMPT][:YAST][:RETURN] = "" | ||
IRB.conf[:PROMPT][:YAST][:PROMPT_I] = "YaST >> " | ||
IRB.conf[:PROMPT_MODE] = :YAST | ||
workspace = IRB::WorkSpace.new(context) | ||
IRB::Irb.new(workspace).run(IRB.conf) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# ------------------------------------------------------------------------------ | ||
# Copyright (c) 2021 SUSE LLC, All Rights Reserved. | ||
# | ||
# This program is free software; you can redistribute it and/or modify it under | ||
# the terms of version 2 of the GNU General Public License as published by the | ||
# Free Software Foundation. | ||
# | ||
# This program is distributed in the hope that it will be useful, but WITHOUT | ||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
# | ||
# ------------------------------------------------------------------------------ | ||
|
||
require "pp" | ||
require "shellwords" | ||
|
||
require "yast" | ||
require "installation/console/plugins" | ||
|
||
module Installation | ||
module Console | ||
class Commands | ||
include Yast::I18n | ||
|
||
# This class implements the commands in the installer console, | ||
# the actual commands are implemented as plugins loaded from | ||
# lib/installation/console/plugins/*.rb files | ||
# | ||
# All public methods in this class are the commands available in the console, | ||
# that means we cannot include Yast::Logger here because it would define | ||
# "log" command. For logging we have to use the full form here, e.g. | ||
# Yast::Y2Logger.instance.info | ||
def initialize(console) | ||
textdomain "installation" | ||
|
||
@console = console | ||
Plugins.load_plugins | ||
end | ||
|
||
# print the "welcome" message with a basic help text | ||
def welcome | ||
puts "---- This is the YaST Installation Console ----" | ||
puts | ||
# this is the most important message so make it translatable, | ||
# the console is for experts so it is OK have the rest untranslated | ||
# TRANSLATORS: help text displayed in the installer command line console | ||
puts _("Type 'quit' or press Ctrl+D to close the console and go back to the installer") | ||
puts | ||
puts "Type 'commands' to see the available special commands" | ||
puts | ||
puts "This is a Ruby shell, you can also type any Ruby command here" | ||
puts "and inspect or change the YaST installer" | ||
puts | ||
puts "Hints: <Tab> completion is enabled, the command history is kept," | ||
puts "you can use the usual \"readline\" features..." | ||
puts | ||
end | ||
|
||
# print the available commands | ||
def commands | ||
puts "Available commands:" | ||
puts | ||
print_command("quit", "Close the console and return back to the installer") | ||
|
||
private_methods.grep(/_description$/).sort.each do |method| | ||
print_command(method.to_s.sub(/_description$/, ""), send(method)) | ||
end | ||
|
||
puts | ||
end | ||
|
||
# all unknown commands are handled via this "method_missing" callback | ||
def method_missing(method_name, *_args) | ||
Yast::Y2Logger.instance.info "Entered unknown command: #{method_name.inspect}" | ||
puts "Error: Unknown command \"#{method_name}\"" | ||
puts | ||
commands | ||
end | ||
|
||
# helper for running an YaST module | ||
def run_yast_module(*args) | ||
@console.run_yast_module(*args) | ||
end | ||
|
||
private | ||
|
||
# print help text for a command | ||
def print_command(cmd, descr) | ||
# indent multiline descriptions | ||
description = descr.gsub("\n", "\n ") | ||
puts " #{cmd} - #{description}" | ||
puts | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
# ------------------------------------------------------------------------------ | ||
# Copyright (c) 2021 SUSE LLC, All Rights Reserved. | ||
# | ||
# This program is free software; you can redistribute it and/or modify it under | ||
# the terms of version 2 of the GNU General Public License as published by the | ||
# Free Software Foundation. | ||
# | ||
# This program is distributed in the hope that it will be useful, but WITHOUT | ||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||
# | ||
# ------------------------------------------------------------------------------ | ||
|
||
require "yast" | ||
|
||
module Installation | ||
module Console | ||
# the installer console implementation for the graphical (Qt) UI | ||
class Gui | ||
Yast.import "Wizard" | ||
|
||
# open a console and run a block in it | ||
def run(&block) | ||
start | ||
redirect(&block) | ||
ensure | ||
stop | ||
end | ||
|
||
# helper for running an YaST module in console | ||
def run_yast_module(*args) | ||
# we cannot run any YaST module if there is a popup displayed, the module | ||
# would be displayed *below* the popup making it inaccessible :-( | ||
# make sure a wizard dialog is at the top | ||
return unless wizard_dialog? | ||
|
||
begin | ||
# get the window ID of the currently active window (the xterm window) | ||
window = `#{SWITCHER}`.chomp | ||
rescue Errno::ENOENT | ||
# if the switcher is missing display a short help | ||
puts "Starting an YaST configuration module..." | ||
puts | ||
puts "After it is finished (by pressing [Next]/[Back]/[Abort])" | ||
puts "press Alt+Tab to get back to this console." | ||
|
||
# wait a bit so the user can read the message above | ||
sleep(5) | ||
end | ||
|
||
Yast::WFM.call(*args) | ||
|
||
# automatically switch the window focus from YaST back to the xterm window | ||
system("#{SWITCHER} #{Shellwords.escape(window)}") if window | ||
end | ||
|
||
private | ||
|
||
# path to the window switching helper tool (from yast2-x11) | ||
SWITCHER = "/usr/lib/YaST2/bin/active_window".freeze | ||
|
||
# is the toplevel dialog a wizard dialog? We cannot run an YaST module | ||
# if a popup is currently displayed... | ||
def wizard_dialog? | ||
return true if Yast::Wizard.IsWizardDialog | ||
|
||
puts "Error: YaST modules cannot be started if there is a popup dialog" | ||
puts "displayed. First close this console then close the popup in the installer" | ||
puts "and then start the console again." | ||
|
||
false | ||
end | ||
|
||
# start the console, open a new xterm window | ||
def start | ||
# create a pipe for communication with the shell running in the xterm | ||
@read, @write = IO.pipe | ||
# get the /proc path for the reading end of the pipe | ||
read_path = fd_path(Process.pid, @read.fileno) | ||
|
||
# start a new xterm window, run a shell command which: | ||
# 1. opens a watching FD for signaling exit | ||
# 2. "echo" command prints the PID, the watching FD and the terminal | ||
# device to the Ruby pipe above | ||
# 3. "read" command keeps the xterm window open until any input is sent | ||
# to the watching FD | ||
command = "xterm -title \"YaST Installation Console\" -e bash -c \"exec {CLOSEFD}<> <(:); | ||
echo \\$\\$ \\$CLOSEFD \\$(tty) > #{read_path}; | ||
read -u \\$CLOSEFD\" &" | ||
|
||
system(command) | ||
|
||
# read the values printed by the "echo" command above | ||
@pid, @close_fd, @tty = @read.readline.split | ||
end | ||
|
||
# stop the console, close the xterm window | ||
def stop | ||
# send an empty string to the waiting "read" process | ||
File.write(fd_path(@pid, @close_fd), "\n") if @pid && @close_fd | ||
# close the pipes | ||
@read.close if @read | ||
@write.close if @write | ||
end | ||
|
||
# run a block with redirected IO (redirect to the started xterm console) | ||
def redirect(&block) | ||
# remember the initial IO channels | ||
stdout_orig = $stdout.dup | ||
stderr_orig = $stderr.dup | ||
stdin_orig = $stdin.dup | ||
|
||
# redirect all IO to the xterm window (its tty device) | ||
$stdout.reopen(@tty) | ||
$stderr.reopen(@tty) | ||
$stdin.reopen(@tty) | ||
|
||
begin | ||
block.call if block_given? | ||
ensure | ||
# restore the original IO channels | ||
$stdout.reopen(stdout_orig) | ||
$stderr.reopen(stderr_orig) | ||
$stdin.reopen(stdin_orig) | ||
end | ||
end | ||
|
||
# get /proc path for a file descriptor | ||
# @param pid [String, Integer] PID of the process | ||
# @param fd [String, Integer] file descriptor number | ||
def fd_path(pid, fd) | ||
"/proc/#{pid}/fd/#{fd}" | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.