Skip to content

Commit

Permalink
Merge pull request #21 from R1kaB3rN/steamuser
Browse files Browse the repository at this point in the history
Support symlink of current unix username to steamuser
  • Loading branch information
R1kaB3rN authored Feb 26, 2024
2 parents 8383765 + 2ea4c71 commit 2610564
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 16 deletions.
40 changes: 40 additions & 0 deletions ulwgl_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from ulwgl_consts import Level
from ulwgl_util import msg
from ulwgl_log import log, console_handler, debug_formatter
from ulwgl_util import UnixUser

verbs: Set[str] = {
"waitforexitandrun",
Expand Down Expand Up @@ -86,6 +87,11 @@ def set_log() -> None:
def setup_pfx(path: str) -> None:
"""Create a symlink to the WINE prefix and tracked_files file."""
pfx: Path = Path(path).joinpath("pfx").expanduser()
steam: Path = Path(path).expanduser().joinpath("drive_c/users/steamuser")
user: UnixUser = UnixUser()
wineuser: Path = (
Path(path).expanduser().joinpath(f"drive_c/users/{user.get_user()}")
)

if pfx.is_symlink():
pfx.unlink()
Expand All @@ -95,6 +101,40 @@ def setup_pfx(path: str) -> None:

Path(path).joinpath("tracked_files").expanduser().touch()

# Create a symlink of the current user to the steamuser dir or vice versa
# Default for a new prefix is: unixuser -> steamuser
if (
not wineuser.is_dir()
and not steam.is_dir()
and not (wineuser.is_symlink() or steam.is_symlink())
):
# For new prefixes with our Proton: user -> steamuser
steam.mkdir(parents=True)
wineuser.unlink(missing_ok=True)
wineuser.symlink_to("steamuser")
elif wineuser.is_dir() and not steam.is_dir() and not steam.is_symlink():
# When there's a user dir: steamuser -> user
# Be sure it's relative
steam.unlink(missing_ok=True)
steam.symlink_to(user.get_user())
elif not wineuser.exists() and not wineuser.is_symlink() and steam.is_dir():
wineuser.unlink(missing_ok=True)
wineuser.symlink_to("steamuser")
else:
paths: List[str] = [steam.as_posix(), wineuser.as_posix()]
log.warning(
msg(
f"Skipping link creation for prefix: {pfx}",
Level.WARNING,
)
)
log.warning(
msg(
f"Following paths already exist: {paths}",
Level.WARNING,
)
)


def check_env(
env: Dict[str, str], toml: Dict[str, Any] = None
Expand Down
142 changes: 126 additions & 16 deletions ulwgl_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import ulwgl_plugins
import ulwgl_dl_util
import tarfile
import ulwgl_util


class TestGameLauncher(unittest.TestCase):
Expand Down Expand Up @@ -845,35 +846,132 @@ def test_setup_pfx_mv(self):
Path(self.test_file + "/pfx").is_symlink(), "Expected pfx to be a symlink"
)

# Check if the symlink is in its unexpanded form
self.assertEqual(
Path(self.test_file + "/pfx").readlink().as_posix(),
Path(unexpanded_path).expanduser().as_posix(),
def test_setup_pfx_symlinks_else(self):
"""Test setup_pfx in the case both steamuser and unixuser exist in some form.
Tests the case when they are symlinks
An error should not be raised and we should just do nothing
"""
result = None
pattern = r"^/home/[\w\d]+"
user = ulwgl_util.UnixUser()
unexpanded_path = re.sub(
pattern,
"~",
Path(
Path(self.test_file).cwd().as_posix() + "/" + self.test_file
).as_posix(),
)

# Create only the dir
Path(unexpanded_path).joinpath("drive_c/users").expanduser().mkdir(
parents=True, exist_ok=True
)

old_link = Path(self.test_file + "/pfx").resolve()
# Create the symlink to the test file itself
Path(unexpanded_path).joinpath("drive_c/users").joinpath(
user.get_user()
).expanduser().symlink_to(Path(self.test_file).absolute())
Path(unexpanded_path).joinpath("drive_c/users").joinpath(
"steamuser"
).expanduser().symlink_to(Path(self.test_file).absolute())

result = ulwgl_run.setup_pfx(unexpanded_path)

# Rename the dir and replicate passing a new WINEPREFIX
new_dir = Path(unexpanded_path).expanduser().rename("foo")
new_unexpanded_path = re.sub(
self.assertIsNone(
result,
"Expected None when calling setup_pfx",
)

def test_setup_pfx_symlinks_unixuser(self):
"""Test setup_pfx for symbolic link to steamuser.
Tests the case when the steamuser dir does not exist and user dir exists
In this case, create: steamuser -> user
"""
result = None
pattern = r"^/home/[\w\d]+"
user = ulwgl_util.UnixUser()
unexpanded_path = re.sub(
pattern,
"~",
new_dir.cwd().joinpath("foo").as_posix(),
Path(
Path(self.test_file).cwd().as_posix() + "/" + self.test_file
).as_posix(),
)

ulwgl_run.setup_pfx(new_unexpanded_path)
# Create only the user dir
Path(unexpanded_path).joinpath("drive_c/users").joinpath(
user.get_user()
).expanduser().mkdir(parents=True, exist_ok=True)

result = ulwgl_run.setup_pfx(unexpanded_path)

self.assertIsNone(
result,
"Expected None when creating symbolic link to WINE prefix and tracked_files file",
)

new_link = Path("foo/pfx").resolve()
# Verify steamuser -> unix user
self.assertTrue(
old_link is not new_link,
"Expected the symbolic link to change after moving the WINEPREFIX",
Path(self.test_file).joinpath("drive_c/users/steamuser").is_symlink(),
"Expected steamuser to be a symbolic link",
)
self.assertEqual(
Path(self.test_file).joinpath("drive_c/users/steamuser").readlink(),
Path(user.get_user()),
"Expected steamuser -> user",
)

def test_setup_pfx_symlinks_steamuser(self):
"""Test setup_pfx for symbolic link to wine.
Tests the case when only steamuser exist and the user dir does not exist
"""
result = None
user = ulwgl_util.UnixUser()
pattern = r"^/home/[\w\d]+"
unexpanded_path = re.sub(
pattern,
"~",
Path(
Path(self.test_file).cwd().as_posix() + "/" + self.test_file
).as_posix(),
)

# Create the steamuser dir
Path(unexpanded_path + "/drive_c/users/steamuser").expanduser().mkdir(
parents=True, exist_ok=True
)

result = ulwgl_run.setup_pfx(unexpanded_path)

if new_link.exists():
rmtree(new_link.as_posix())
self.assertIsNone(
result,
"Expected None when creating symbolic link to WINE prefix and tracked_files file",
)

# Verify unixuser -> steamuser
self.assertTrue(
Path(self.test_file + "/drive_c/users/steamuser").is_dir(),
"Expected steamuser to be created",
)
self.assertTrue(
Path(unexpanded_path + "/drive_c/users/" + user.get_user())
.expanduser()
.is_symlink(),
"Expected symbolic link for unixuser",
)
self.assertEqual(
Path(self.test_file)
.joinpath(f"drive_c/users/{user.get_user()}")
.readlink(),
Path("steamuser"),
"Expected unixuser -> steamuser",
)

def test_setup_pfx_symlinks(self):
"""Test _setup_pfx for valid symlinks.
"""Test setup_pfx for valid symlinks.
Ensure that symbolic links to the WINE prefix (pfx) are always in expanded form when passed an unexpanded path.
For example:
Expand Down Expand Up @@ -946,6 +1044,7 @@ def test_setup_pfx_paths(self):
def test_setup_pfx(self):
"""Test setup_pfx."""
result = None
user = ulwgl_util.UnixUser()
result = ulwgl_run.setup_pfx(self.test_file)
self.assertIsNone(
result,
Expand All @@ -958,6 +1057,17 @@ def test_setup_pfx(self):
Path(self.test_file + "/tracked_files").is_file(),
"Expected tracked_files to be a file",
)
# For new prefixes, steamuser should exist and a user symlink
self.assertTrue(
Path(self.test_file + "/drive_c/users/steamuser").is_dir(),
"Expected steamuser to be created",
)
self.assertTrue(
Path(self.test_file + "/drive_c/users/" + user.get_user())
.expanduser()
.is_symlink(),
"Expected symlink of username -> steamuser",
)

def test_parse_args(self):
"""Test parse_args with no options.
Expand Down
33 changes: 33 additions & 0 deletions ulwgl_util.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from ulwgl_consts import Color, Level
from typing import Any
from os import getuid
from pathlib import Path
from pwd import struct_passwd, getpwuid


def msg(msg: Any, level: Level):
Expand All @@ -18,3 +21,33 @@ def msg(msg: Any, level: Level):
log = f"{Color.BOLD.value}{Color.DEBUG.value}{msg}{Color.RESET.value}"

return log


class UnixUser:
"""Represents the User of the system as determined by the password database rather than environment variables or file system paths."""

def __init__(self):
"""Immutable properties of the user determined by the password database that's derived from the real user id."""
uid: int = getuid()
entry: struct_passwd = getpwuid(uid)
# Immutable properties, hence no setters
self.name: str = entry.pw_name
self.puid: str = entry.pw_uid # Should be equivalent to the value from getuid
self.dir: str = entry.pw_dir
self.is_user: bool = self.puid == uid

def get_home_dir(self) -> Path:
"""User home directory as determined by the password database that's derived from the current process's real user id."""
return Path(self.dir).as_posix()

def get_user(self) -> str:
"""User (login name) as determined by the password database that's derived from the current process's real user id."""
return self.name

def get_puid(self) -> int:
"""Numerical user ID as determined by the password database that's derived from the current process's real user id."""
return self.puid

def is_user(self, uid: int) -> bool:
"""Compare the UID passed in to this instance."""
return uid == self.puid

0 comments on commit 2610564

Please sign in to comment.