diff --git a/package/harvester-os/files/usr/bin/setup-installer.sh b/package/harvester-os/files/usr/bin/setup-installer.sh index 83a09cd0e..6d52ca613 100755 --- a/package/harvester-os/files/usr/bin/setup-installer.sh +++ b/package/harvester-os/files/usr/bin/setup-installer.sh @@ -36,6 +36,22 @@ EOF } +is_virtual_tty() +{ + local tty_name=$1 + local tty_num=${tty_name#tty} + [[ ${tty_num} =~ ^[1-9][0-9]*$ ]] +} + +is_serial_tty() +{ + local tty_name=$1 + local tty_type + + tty_type=$(cat "/sys/class/tty/${tty_name}/type") + [ "x${tty_type}" != "x0" ] +} + echo "Remove the getty service..." rm -rf "/etc/systemd/system/getty*" @@ -43,40 +59,29 @@ rm -rf "/etc/systemd/system/getty*" echo "Remove the serial-getty service..." rm -rf "/etc/systemd/system/serial-getty*" -# reverse the ttys to start from the last one read -r -a tty_list < /sys/class/tty/console/active +preferred_virtual_tty="" for TTY in "${tty_list[@]}"; do - tty_num=${TTY#tty} - - #for arm64 the terminals are named /dev/ttyAMA* - #skip /dev/ttyAMA* if an additional tty is present - #on equinix metal /dev/ttyAMA is the only terminal available - #so needs to be used as default option - PLATFORM=$(uname -m) - if [[ $PLATFORM == "aarch64" && ${#tty_list[@]} > 1 ]] - then - if [[ $tty_num =~ ^AMA[0-9]+$ ]]; then - continue - fi - fi - - # tty1 ~ tty64 - if [[ $tty_num =~ ^[0-9]+$ ]]; then + if [[ ${TTY} == "tty1" ]]; then create_drop_in "/etc/systemd/system/getty@${TTY}.service.d" - - break + exit 0 + fi + if [[ -z ${preferred_virtual_tty} ]] && is_virtual_tty "${TTY}"; then + preferred_virtual_tty=${TTY} fi +done - - # might be serial console +if [[ -n ${preferred_virtual_tty} ]]; then + create_drop_in "/etc/systemd/system/getty@${preferred_virtual_tty}.service.d" + exit 0 +fi - # check type is not 0 - tty_type=$(cat "/sys/class/tty/${TTY}/type") - if [ "x${tty_type}" = "x0" ]; then +for TTY in "${tty_list[@]}"; do + if ! is_serial_tty "${TTY}"; then continue fi - + create_drop_in "/etc/systemd/system/serial-getty@${TTY}.service.d" - break + exit 0 done diff --git a/package/harvester-os/files/usr/sbin/harv-restart-services b/package/harvester-os/files/usr/sbin/harv-restart-services index cee642d86..a7236ab42 100755 --- a/package/harvester-os/files/usr/sbin/harv-restart-services +++ b/package/harvester-os/files/usr/sbin/harv-restart-services @@ -1,3 +1,35 @@ #!/bin/bash -ex + +restart_console_getty() +{ + read -r -a tty_list < /sys/class/tty/console/active + local preferred_virtual_tty="" + + for TTY in "${tty_list[@]}"; do + if [[ ${TTY} == "tty1" ]]; then + systemctl restart "getty@${TTY}.service" + return 0 + fi + tty_num=${TTY#tty} + if [[ -z ${preferred_virtual_tty} ]] && [[ ${tty_num} =~ ^[1-9][0-9]*$ ]]; then + preferred_virtual_tty=${TTY} + fi + done + + if [[ -n ${preferred_virtual_tty} ]]; then + systemctl restart "getty@${preferred_virtual_tty}.service" + return 0 + fi + + for TTY in "${tty_list[@]}"; do + tty_type=$(cat "/sys/class/tty/${TTY}/type") + if [ "x${tty_type}" = "x0" ]; then + continue + fi + systemctl restart "serial-getty@${TTY}.service" + return 0 + done +} + systemctl restart --no-block rancherd -systemctl restart getty@tty1.service \ No newline at end of file +restart_console_getty diff --git a/pkg/console/tty.go b/pkg/console/tty.go index a841b7e69..e20e08155 100644 --- a/pkg/console/tty.go +++ b/pkg/console/tty.go @@ -2,12 +2,45 @@ package console import ( "os" - goruntime "runtime" + "strconv" "strings" "github.com/sirupsen/logrus" ) +func isVirtualTTY(tty string) bool { + if !strings.HasPrefix(tty, "tty") { + return false + } + + ttyNum, err := strconv.Atoi(strings.TrimPrefix(tty, "tty")) + return err == nil && ttyNum > 0 +} + +func getPreferredConsoleTTY(ttys []string) string { + preferredVirtualTTY := "" + + for _, tty := range ttys { + if tty == "tty1" { + return tty + } + if preferredVirtualTTY == "" && isVirtualTTY(tty) { + preferredVirtualTTY = tty + } + } + + if preferredVirtualTTY != "" { + return preferredVirtualTTY + } + + for _, tty := range ttys { + if tty != "tty0" { + return tty + } + } + return ttys[0] +} + func getFirstConsoleTTY() string { b, err := os.ReadFile("/sys/class/tty/console/active") if err != nil { @@ -17,16 +50,7 @@ func getFirstConsoleTTY() string { ttys := strings.Split(strings.TrimRight(string(b), "\n"), " ") if len(ttys) > 0 { - // arm devices generally have first console as /dev/ttyAMA0 - // this console is skipped in iso based installs due to display resolution issues - // as a result of this automatic install via ipxe fails since installer runs in - // say /dev/tty1 but first console returned by this method is ttyAMA0 - // we are currently adding a check to skip AMA0 if it is the first console and return - // the second item in the list - if goruntime.GOARCH == "arm64" && strings.Contains(ttys[0], "AMA0") && len(ttys) > 1 { - return ttys[1] - } - return ttys[0] + return getPreferredConsoleTTY(ttys) } return "" } diff --git a/pkg/console/tty_test.go b/pkg/console/tty_test.go new file mode 100644 index 000000000..80ea800d6 --- /dev/null +++ b/pkg/console/tty_test.go @@ -0,0 +1,46 @@ +package console + +import "testing" + +func TestGetPreferredConsoleTTY(t *testing.T) { + tests := []struct { + name string + ttys []string + want string + }{ + { + name: "prefer tty1 over serial", + ttys: []string{"ttyS0", "tty1"}, + want: "tty1", + }, + { + name: "prefer tty1 over earlier virtual tty", + ttys: []string{"tty2", "tty1"}, + want: "tty1", + }, + { + name: "skip tty0 when serial is available", + ttys: []string{"tty0", "ttyS0"}, + want: "ttyS0", + }, + { + name: "prefer first usable virtual tty", + ttys: []string{"ttyS0", "tty2"}, + want: "tty2", + }, + { + name: "keep ama console when it is the only usable option", + ttys: []string{"ttyAMA0"}, + want: "ttyAMA0", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := getPreferredConsoleTTY(tt.ttys) + if got != tt.want { + t.Fatalf("getPreferredConsoleTTY(%v) = %q, want %q", tt.ttys, got, tt.want) + } + }) + } +} diff --git a/pkg/console/util_test.go b/pkg/console/util_test.go index 55573ef61..15f3e89b8 100644 --- a/pkg/console/util_test.go +++ b/pkg/console/util_test.go @@ -1120,56 +1120,56 @@ func Test_getAllValidDiskOptions(t *testing.T) { expectedAllValidDiskOptions []widgets.Option }{ { - name: "Disks with serial number", + name: "Disks with serial number", mockedRunCommandOutput: []byte(sampleSerialDiskOutput), expectedAllValidDiskOptions: []widgets.Option{ { Value: "/dev/sda", - Text: "sda 250G", + Text: "sda 250G", }, }, }, { - name: "Disks with existing data", + name: "Disks with existing data", mockedRunCommandOutput: []byte(reinstallDisks), expectedAllValidDiskOptions: []widgets.Option{ { Value: "/dev/sda", - Text: "sda 10G", + Text: "sda 10G", }, { Value: "/dev/vda", - Text: "vda 250G", + Text: "vda 250G", }, }, }, { - name: "Disks on existing installs", + name: "Disks on existing installs", mockedRunCommandOutput: []byte(preInstalledMultiPath), expectedAllValidDiskOptions: []widgets.Option{ { Value: "/dev/sda", - Text: "sda 250G", + Text: "sda 250G", }, }, }, { - name: "RAID disks", + name: "RAID disks", mockedRunCommandOutput: []byte(raidDisks), expectedAllValidDiskOptions: []widgets.Option{ { Value: "/dev/sda", - Text: "sda 447.1G", + Text: "sda 447.1G", }, { Value: "/dev/sdb", - Text: "sdb 447.1G", + Text: "sdb 447.1G", }, }, }, { - name: "No Valid Disks", - mockedRunCommandOutput: []byte(noValidDisks), + name: "No Valid Disks", + mockedRunCommandOutput: []byte(noValidDisks), expectedAllValidDiskOptions: []widgets.Option(nil), }, } @@ -1213,7 +1213,7 @@ func Test_getDataDisksOptions(t *testing.T) { widgets.Option{Value: "/dev/sdb", Text: "sdb 447.1G"}, }, doc.getDataDiskOptions(hvstConfig), - ) + ) // Change the installation disk to the second disk option hvstConfig.Install.Device = doc.getAllValidDiskOptions()[1].Value @@ -1223,7 +1223,7 @@ func Test_getDataDisksOptions(t *testing.T) { widgets.Option{Value: "/dev/sda", Text: "sda 447.1G"}, }, doc.getDataDiskOptions(hvstConfig), - ) + ) } func Test_getWipeDisksOptions(t *testing.T) { @@ -1244,7 +1244,7 @@ func Test_getWipeDisksOptions(t *testing.T) { widgets.Option{Value: "/dev/sdc", Text: "sdc 250G"}, }, doc.getWipeDisksOptions(hvstConfig), - ) + ) hvstConfig.Install.Device = "/dev/sdc" hvstConfig.Install.DataDisk = ""