Skip to content

Commit

Permalink
Fixed #115, added option to save last successfully logged in user for…
Browse files Browse the repository at this point in the history
… next login screen.
  • Loading branch information
tvrzna committed Jun 11, 2024
1 parent b6fcf7c commit c1e6d65
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 54 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ Path to directory, where Xorg sessions' desktop files are stored. Default value
`WAYLAND_SESSIONS_PATH`
Path to directory, where Wayland sessions' desktop files are stored. Default value is "/usr/share/wayland-sessions/".

`SELECT_LAST_USER`
Enables funtionality of saving last successfully logged in user for next login. Possible values are "false", "per-tty" or "global". Default value is false.

#### Dynamic MOTD
If `DYNAMIC_MOTD` is set to `true`, this file exists and is executable for its owner, the result is printed as your own MOTD. Be very careful with this script!

Expand Down
3 changes: 3 additions & 0 deletions res/emptty.1
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ Path to directory, where Xorg sessions' desktop files are stored. Default value
.IP WAYLAND_SESSIONS_PATH
Path to directory, where Wayland sessions' desktop files are stored. Default value is "/usr/share/wayland-sessions/".

.IP SELECT_LAST_USER
Enables funtionality of saving last successfully logged in user for next login. Possible values are "false", "per-tty" or "global". Default value is false.

.SH DYNAMIC MOTD
Optional file stored by default as /etc/emptty/motd-gen.sh. Could be overridden.

Expand Down
3 changes: 2 additions & 1 deletion res/testing/conf
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ AUTOLOGIN_MAX_RETRY=-1
HIDE_ENTER_LOGIN=true
ALWAYS_DBUS_LAUNCH=true
XORG_SESSIONS_PATH=/dev/null
WAYLAND_SESSIONS_PATH=/dev/zero
WAYLAND_SESSIONS_PATH=/dev/zero
SELECT_LAST_USER=per-tty
97 changes: 97 additions & 0 deletions src/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package src

import (
"bufio"
"fmt"
"os"
"strings"
)

const (
pathLastLoggedInUser = "/var/cache/emptty/lastuser"
)

type enSelectLastUser byte

const (
// Do not preselect any last user
False enSelectLastUser = iota

// Preselect last successfully logged in user per tty
PerTty

// Preselect last successfully logged in user per system
Global
)

type authBase struct {
}

// Performs input selection user. If saving last user is enabled (PerTty/Global), user is read from defined path and used as predefined value.
func (a *authBase) selectUser(c *config) (string, error) {
lastUser := a.getLastSelectedUser(c)
if !c.HideEnterLogin {
hostname, _ := os.Hostname()
lastUserDisplay := ""
if lastUser != "" {
lastUserDisplay = " [" + lastUser + "]"
}
fmt.Printf("%s login%s: ", hostname, lastUserDisplay)
}
input, err := bufio.NewReader(os.Stdin).ReadString('\n')
if err != nil {
return "", err
}
username := input[:len(input)-1]

if lastUser != "" && username == "" {
username = lastUser
}
return username, nil
}

// Gets last selected user with respect to configuration.
func (a *authBase) getLastSelectedUser(c *config) string {
switch c.SelectLastUser {
case PerTty:
return a.readLastUser(pathLastLoggedInUser + "-" + c.strTTY())
case Global:
return a.readLastUser(pathLastLoggedInUser)
}
return ""
}

// Reads last user from file on path.
func (a *authBase) readLastUser(path string) string {
b, err := os.ReadFile(path)
if err != nil {
if !os.IsNotExist(err) {
logPrint(err)
}
return ""
}
lastUser := strings.TrimSpace(string(b))
if strings.Contains(lastUser, "\n") {
return ""
}
return lastUser
}

// Saves last selected user with respect to configuration.
func (a *authBase) saveLastSelectedUser(c *config, username string) {
if c.SelectLastUser == False {
return
}

path := pathLastLoggedInUser
if c.SelectLastUser == PerTty {
path += "-" + c.strTTY()
}

if err := mkDirsForFile(path, 0700); err != nil {
logPrint(err)
}
if err := os.WriteFile(path, []byte(username), 0600); err != nil {
logPrint(err)
}
}
10 changes: 4 additions & 6 deletions src/auth_nopam.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
package src

import (
"bufio"
"fmt"
"os"
"os/user"
Expand All @@ -13,6 +12,7 @@ const tagPam = "nopam"

// PamHandle defines structure of handle specifically designed for not using PAM authorization
type nopamHandle struct {
*authBase
u *sysuser
}

Expand Down Expand Up @@ -42,12 +42,9 @@ func (n *nopamHandle) authUser(conf *config) {
}
username = conf.DefaultUser
} else {
if !conf.HideEnterLogin {
fmt.Printf("%s login: ", hostname)
}
input, err := bufio.NewReader(os.Stdin).ReadString('\n')
var err error
username, err = n.selectUser(conf)
handleErr(err)
username = input[:len(input)-1]
}
if !conf.HideEnterPassword {
fmt.Print("Password: ")
Expand All @@ -56,6 +53,7 @@ func (n *nopamHandle) authUser(conf *config) {
handleErr(err)

if n.authPassword(username, password) {
n.saveLastSelectedUser(conf, username)
usr, err := user.Lookup(username)
username = ""

Expand Down
13 changes: 3 additions & 10 deletions src/auth_pam.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
package src

import (
"bufio"
"errors"
"fmt"
"os"
Expand All @@ -16,6 +15,7 @@ const tagPam = ""

// PamHandle defines structure of handle specifically designed for using PAM authorization
type pamHandle struct {
*authBase
trans *pam.Transaction
u *sysuser
}
Expand Down Expand Up @@ -50,15 +50,7 @@ func (h *pamHandle) authUser(conf *config) {
if conf.Autologin {
break
}
if !conf.HideEnterLogin {
hostname, _ := os.Hostname()
fmt.Printf("%s login: ", hostname)
}
input, err := bufio.NewReader(os.Stdin).ReadString('\n')
if err != nil {
return "", err
}
return input[:len(input)-1], nil
return h.selectUser(conf)
case pam.ErrorMsg:
logPrint(msg)
return "", nil
Expand All @@ -85,6 +77,7 @@ func (h *pamHandle) authUser(conf *config) {
usr, _ := user.Lookup(pamUsr)

h.u = getSysuser(usr)
h.saveLastSelectedUser(conf, pamUsr)
}

func (h *pamHandle) handleErr(err error) {
Expand Down
88 changes: 51 additions & 37 deletions src/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"os"
"reflect"
"strconv"
"strings"
)

const (
Expand All @@ -13,43 +14,44 @@ const (
// config defines structure of application configuration.
type config struct {
DaemonMode bool
Autologin bool `config:"AUTOLOGIN" parser:"ParseBool" default:"false"`
SwitchTTY bool `config:"SWITCH_TTY" parser:"ParseBool" default:"true"`
PrintIssue bool `config:"PRINT_ISSUE" parser:"ParseBool" default:"true"`
PrintMotd bool `config:"PRINT_MOTD" parser:"ParseBool" default:"true"`
DbusLaunch bool `config:"DBUS_LAUNCH" parser:"ParseBool" default:"true"`
AlwaysDbusLaunch bool `config:"ALWAYS_DBUS_LAUNCH" parser:"ParseBool" default:"false"`
XinitrcLaunch bool `config:"XINITRC_LAUNCH" parser:"ParseBool" default:"false"`
VerticalSelection bool `config:"VERTICAL_SELECTION" parser:"ParseBool" default:"false"`
DynamicMotd bool `config:"DYNAMIC_MOTD" parser:"ParseBool" default:"false"`
EnableNumlock bool `config:"ENABLE_NUMLOCK" parser:"ParseBool" default:"false"`
NoXdgFallback bool `config:"NO_XDG_FALLBACK" parser:"ParseBool" default:"false"`
DefaultXauthority bool `config:"DEFAULT_XAUTHORITY" parser:"ParseBool" default:"false"`
RootlessXorg bool `config:"ROOTLESS_XORG" parser:"ParseBool" default:"false"`
IdentifyEnvs bool `config:"IDENTIFY_ENVS" parser:"ParseBool" default:"false"`
HideEnterLogin bool `config:"HIDE_ENTER_LOGIN" parser:"ParseBool" default:"false"`
HideEnterPassword bool `config:"HIDE_ENTER_PASSWORD" parser:"ParseBool" default:"false"`
DefaultSessionEnv enEnvironment `config:"DEFAULT_SESSION_ENV" parser:"ParseEnv" default:""`
AutologinSessionEnv enEnvironment `config:"AUTOLOGIN_SESSION_ENV" parser:"ParseEnv" default:""`
Logging enLogging `config:"LOGGING" parser:"ParseLogging" default:"rotate"`
SessionErrLog enLogging `config:"SESSION_ERROR_LOGGING" parser:"ParseLogging" default:"disabled"`
AutologinMaxRetry int `config:"AUTOLOGIN_MAX_RETRY" parser:"ParseInt" default:"2"`
Tty int `config:"TTY_NUMBER" parser:"ParseTTY" default:"0"`
DefaultUser string `config:"DEFAULT_USER" parser:"SanitizeValue" default:""`
DefaultSession string `config:"DEFAULT_SESSION" parser:"SanitizeValue" default:""`
AutologinSession string `config:"AUTOLOGIN_SESSION" parser:"SanitizeValue" default:""`
Lang string `config:"LANG" parser:"SanitizeValue" default:""`
LoggingFile string `config:"LOGGING_FILE" parser:"SanitizeValue" default:"/var/log/emptty/[TTY_NUMBER].log"`
XorgArgs string `config:"XORG_ARGS" parser:"SanitizeValue" default:""`
DynamicMotdPath string `config:"DYNAMIC_MOTD_PATH" parser:"SanitizeValue" default:"/etc/emptty/motd-gen.sh"`
MotdPath string `config:"MOTD_PATH" parser:"SanitizeValue" default:"/etc/emptty/motd"`
FgColor string `config:"FG_COLOR" parser:"ConvertFgColor" default:""`
BgColor string `config:"BG_COLOR" parser:"ConvertBgColor" default:""`
DisplayStartScript string `config:"DISPLAY_START_SCRIPT" parser:"SanitizeValue" default:""`
DisplayStopScript string `config:"DISPLAY_STOP_SCRIPT" parser:"SanitizeValue" default:""`
SessionErrLogFile string `config:"SESSION_ERROR_LOGGING_FILE" parser:"SanitizeValue" default:"/var/log/emptty/session-errors.[TTY_NUMBER].log"`
XorgSessionsPath string `config:"XORG_SESSIONS_PATH" parser:"SanitizeValue" default:"/usr/share/xsessions/"`
WaylandSessionsPath string `config:"WAYLAND_SESSIONS_PATH" parser:"SanitizeValue" default:"/usr/share/wayland-sessions/"`
Autologin bool `config:"AUTOLOGIN" parser:"ParseBool" default:"false"`
SwitchTTY bool `config:"SWITCH_TTY" parser:"ParseBool" default:"true"`
PrintIssue bool `config:"PRINT_ISSUE" parser:"ParseBool" default:"true"`
PrintMotd bool `config:"PRINT_MOTD" parser:"ParseBool" default:"true"`
DbusLaunch bool `config:"DBUS_LAUNCH" parser:"ParseBool" default:"true"`
AlwaysDbusLaunch bool `config:"ALWAYS_DBUS_LAUNCH" parser:"ParseBool" default:"false"`
XinitrcLaunch bool `config:"XINITRC_LAUNCH" parser:"ParseBool" default:"false"`
VerticalSelection bool `config:"VERTICAL_SELECTION" parser:"ParseBool" default:"false"`
DynamicMotd bool `config:"DYNAMIC_MOTD" parser:"ParseBool" default:"false"`
EnableNumlock bool `config:"ENABLE_NUMLOCK" parser:"ParseBool" default:"false"`
NoXdgFallback bool `config:"NO_XDG_FALLBACK" parser:"ParseBool" default:"false"`
DefaultXauthority bool `config:"DEFAULT_XAUTHORITY" parser:"ParseBool" default:"false"`
RootlessXorg bool `config:"ROOTLESS_XORG" parser:"ParseBool" default:"false"`
IdentifyEnvs bool `config:"IDENTIFY_ENVS" parser:"ParseBool" default:"false"`
HideEnterLogin bool `config:"HIDE_ENTER_LOGIN" parser:"ParseBool" default:"false"`
HideEnterPassword bool `config:"HIDE_ENTER_PASSWORD" parser:"ParseBool" default:"false"`
DefaultSessionEnv enEnvironment `config:"DEFAULT_SESSION_ENV" parser:"ParseEnv" default:""`
AutologinSessionEnv enEnvironment `config:"AUTOLOGIN_SESSION_ENV" parser:"ParseEnv" default:""`
Logging enLogging `config:"LOGGING" parser:"ParseLogging" default:"rotate"`
SessionErrLog enLogging `config:"SESSION_ERROR_LOGGING" parser:"ParseLogging" default:"disabled"`
AutologinMaxRetry int `config:"AUTOLOGIN_MAX_RETRY" parser:"ParseInt" default:"2"`
Tty int `config:"TTY_NUMBER" parser:"ParseTTY" default:"0"`
DefaultUser string `config:"DEFAULT_USER" parser:"SanitizeValue" default:""`
DefaultSession string `config:"DEFAULT_SESSION" parser:"SanitizeValue" default:""`
AutologinSession string `config:"AUTOLOGIN_SESSION" parser:"SanitizeValue" default:""`
Lang string `config:"LANG" parser:"SanitizeValue" default:""`
LoggingFile string `config:"LOGGING_FILE" parser:"SanitizeValue" default:"/var/log/emptty/[TTY_NUMBER].log"`
XorgArgs string `config:"XORG_ARGS" parser:"SanitizeValue" default:""`
DynamicMotdPath string `config:"DYNAMIC_MOTD_PATH" parser:"SanitizeValue" default:"/etc/emptty/motd-gen.sh"`
MotdPath string `config:"MOTD_PATH" parser:"SanitizeValue" default:"/etc/emptty/motd"`
FgColor string `config:"FG_COLOR" parser:"ConvertFgColor" default:""`
BgColor string `config:"BG_COLOR" parser:"ConvertBgColor" default:""`
DisplayStartScript string `config:"DISPLAY_START_SCRIPT" parser:"SanitizeValue" default:""`
DisplayStopScript string `config:"DISPLAY_STOP_SCRIPT" parser:"SanitizeValue" default:""`
SessionErrLogFile string `config:"SESSION_ERROR_LOGGING_FILE" parser:"SanitizeValue" default:"/var/log/emptty/session-errors.[TTY_NUMBER].log"`
XorgSessionsPath string `config:"XORG_SESSIONS_PATH" parser:"SanitizeValue" default:"/usr/share/xsessions/"`
WaylandSessionsPath string `config:"WAYLAND_SESSIONS_PATH" parser:"SanitizeValue" default:"/usr/share/wayland-sessions/"`
SelectLastUser enSelectLastUser `config:"SELECT_LAST_USER" parser:"ParseSelectLastUser" default:"false"`
}

// LoadConfig handles loading of application configuration.
Expand Down Expand Up @@ -162,3 +164,15 @@ func (c *config) strTTY() string {
func (c *config) ttyPath() string {
return "/dev/tty" + c.strTTY()
}

// Parses select last user config option.
func (c *config) ParseSelectLastUser(value, defaultValue string) enSelectLastUser {
switch strings.ToLower(sanitizeValue(value, defaultValue)) {
case "per-tty":
return PerTty
case "global":
return Global
}
return False

}
3 changes: 3 additions & 0 deletions src/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ func TestLoadConfig(t *testing.T) {
t.Error("TestLoadConfig: WAYLAND_SESSIONS_PATH value is not correct")
}

if conf.SelectLastUser != PerTty {
t.Error("TestLoadConfig: SELECT_LAST_USER value is not correct")
}
}

func TestLangLoadConfig(t *testing.T) {
Expand Down

0 comments on commit c1e6d65

Please sign in to comment.