8 Commits

Author SHA1 Message Date
GitHub Actions
f14eff273e Update NixOS module to v2.0.2 2026-02-25 22:02:45 +00:00
15b8089e37 version bump: v2.0.2 2026-02-25 22:59:02 +01:00
grialion
dd6760127c fix: launch state off by one bug (#116)
Previously it couldn't find the currect user/session because the last
newline character was present in the field
2026-02-08 17:39:09 +01:00
github-actions[bot]
8e9a222908 Update NixOS module to v2.0.1 (#115)
Co-authored-by: GitHub Actions <actions@github.com>
2026-02-07 18:46:44 +01:00
502d749df6 chore(release): v2.0.1 2026-02-07 18:40:10 +01:00
0b2d0bdf03 fixes: improve login environment handling (#114)
* chore(organization): allow nested header and c files

* feat: add shell login "middleware" & etc:
  * lidm now calls `bash` (or other shells, depends on PACKAGE cfg) in
  login mode as a session wrapper to source most env (can be disabled)
  * this fixes a lot of env problems with all `/etc/profile` and more

Extra:
* implemented a musl compatible version of `execvpe` and now lidm should
  search for PATH everywhere it needs to
* `search_path` now also checks if the found binary is properly
  executable
* lidm now uses `confstr` for a decent PATH default if none is found
* logs are unbuffered for cases where debug logs appear empty (exit
  without handlers moment)

* chore: one-time evaluate certain makefile vars

---------

Co-authored-by: grialion <48643945+grialion@users.noreply.github.com>
2026-02-07 17:12:03 +01:00
c009c6c31c remove: all aur stuff (#113)
there are more contributors now and given the rare releases its much better to manually prepare a release than to automate it

also, the packages now could be updated by any maintainer so not worth it to keep it centralized

and now releases are much less dependant on the scripts, so its not such a hassle
2026-02-07 15:23:30 +01:00
6de3996cf0 git-lfs,media: add lfs for media files and new catpuccin imgs (#111) 2026-02-06 14:57:42 +01:00
58 changed files with 568 additions and 791 deletions

View File

@@ -65,40 +65,6 @@ jobs:
artifactErrorsFailBuild: true artifactErrorsFailBuild: true
body: Release notes not generated yet. body: Release notes not generated yet.
aur-update:
name: Update AUR pkgs
runs-on: ubuntu-24.04
container: archlinux:latest
permissions: write-all
needs: [ release-checks, release ]
steps:
- run: pacman -Sy --noconfirm git github-cli base-devel pacman-contrib
- uses: actions/checkout@v4
- run: |
chage -E -1 nobody
passwd -u nobody
cd "assets/pkg/aur"
chown nobody:nobody . -R
su - -s /bin/bash nobody -c "$PWD/update-pkgs.sh ${{ needs.release-checks.outputs.VERSION }}"
su - -s /bin/bash nobody -c "$PWD/test-makepkg.sh" # This will also update -git pkgver
chown $UID:$(id -g) . -R
- run: |
BRANCH=actions/update-aur-${{ needs.release-checks.outputs.VERSION }}
git config --global --add safe.directory $GITHUB_WORKSPACE
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git checkout -b $BRANCH
git commit -am "Update AUR pkgs to v${{ needs.release-checks.outputs.VERSION }}"
git push -u origin $BRANCH
gh pr create --head $BRANCH \
--title "[AUR update]: Bump to ${{ needs.release-checks.outputs.VERSION }}" \
--body "*This PR was created automatically*"
env:
GH_TOKEN: ${{ github.token }}
nix-update: nix-update:
name: Update NixOS module name: Update NixOS module
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04

View File

@@ -4,6 +4,19 @@
<!-- By "very relevant" I mean big features or something manual packagers should know, like leftover files --> <!-- By "very relevant" I mean big features or something manual packagers should know, like leftover files -->
<!-- Once a release would be opened, group the last bunch of dangling changes, add release version as header and its date --> <!-- Once a release would be opened, group the last bunch of dangling changes, add release version as header and its date -->
# 2.0.2
- fix off-by-one error with launch state save
# 2.0.1
- source and header files can be nested in `src/` and `include/`
- lidm now calls bash for logging in and sourcing profile files, other shells can be configured
- a good PATH default is fetched from `confstr`
- debug logs are now unbuffered
# 2.0.0
- Most stuff (most of `/etc` being a notable exception) now installs to `/usr/local` by default, check [`docs/PACKAGERS.md`](./docs/PACKAGERS.md). - Most stuff (most of `/etc` being a notable exception) now installs to `/usr/local` by default, check [`docs/PACKAGERS.md`](./docs/PACKAGERS.md).
- Added a changelog. - Added a changelog.
- Finally add proper (experimental) xorg support. - Finally add proper (experimental) xorg support.

View File

@@ -1,4 +1,4 @@
VERSION = 2.0.0 VERSION := 2.0.2
.DEFAULT_GOAL := lidm .DEFAULT_GOAL := lidm
CDIR = src CDIR = src
@@ -9,7 +9,9 @@ ODIR = dist
PREFIX = /usr/local PREFIX = /usr/local
INFO_GIT_REV ?= $(shell git describe --long --tags --always || echo '?') INFO_GIT_REV ?= $(shell git describe --long --tags --always || echo '?')
INFO_GIT_REV := $(INFO_GIT_REV)
INFO_BUILD_TS ?= $(shell date +%s) INFO_BUILD_TS ?= $(shell date +%s)
INFO_BUILD_TS := $(INFO_BUILD_TS)
CFLAGS ?= -O3 -Wall -Wextra -fdata-sections -ffunction-sections CFLAGS ?= -O3 -Wall -Wextra -fdata-sections -ffunction-sections
# C PreProcessor flags, not C Plus Plus # C PreProcessor flags, not C Plus Plus
@@ -24,21 +26,19 @@ LDFLAGS ?= -Wl,--gc-sections
LIBS = -lpam LIBS = -lpam
# includes all headers in `$(IDIR)` and compiles everything in `$(CDIR)` # includes all headers in `$(IDIR)` and compiles everything in `$(CDIR)`
DEPS = $(wildcard $(IDIR)/*.h) DEPS = $(wildcard $(IDIR)/*.h $(IDIR)/**/*.h)
_SOURCES = $(notdir $(wildcard $(CDIR)/*.c)) _SOURCES = $(wildcard $(CDIR)/*.c) $(wildcard $(CDIR)/**/*.c)
OBJ = $(patsubst %.c,$(ODIR)/%.o,$(_SOURCES)) OBJ = $(patsubst $(CDIR)/%.c,$(ODIR)/%.o,$(_SOURCES))
$(ODIR)/%.o: $(CDIR)/%.c $(DEPS) $(ODIR)/%.o: $(CDIR)/%.c $(DEPS)
@mkdir -p $(ODIR) @mkdir -p $(dir $@)
$(CC) -c -o $@ $< $(ALLFLAGS) $(CC) -c -o $@ $< $(ALLFLAGS)
lidm: $(OBJ) lidm: $(OBJ)
$(CC) -o $@ $^ $(ALLFLAGS) $(LIBS) $(LDFLAGS) $(CC) -o $@ $^ $(ALLFLAGS) $(LIBS) $(LDFLAGS)
clean: clean:
rm -f \ rm -rf $(ODIR) lidm
$(ODIR)/* \
lidm
install: lidm install: lidm
mkdir -p ${DESTDIR}${PREFIX}/bin ${DESTDIR}${PREFIX}/share/man/man{1,5} mkdir -p ${DESTDIR}${PREFIX}/bin ${DESTDIR}${PREFIX}/share/man/man{1,5}

View File

@@ -104,6 +104,9 @@ Specify the time format string to be displayed. Check \fBstrftime (3)\fP to know
.TP .TP
\fBrefresh_rate\fP \fBrefresh_rate\fP
Rate (in milliseconds) at which the UI should refresh, affects clock and resize behavior. Rate (in milliseconds) at which the UI should refresh, affects clock and resize behavior.
.TP
\fBbypass_shell_login\fP
Type \fBBOOL\fP, lidm logins through a login shell (e.g. `bash`) in login mode (e.g. `arg0` being `-bash`) to load `/etc/profile/, `/etc/profile.d/` and more. If this option is enabled that's bypassed entirely.
.SH "SEE ALSO" .SH "SEE ALSO"

View File

@@ -1,4 +0,0 @@
*/*
!*/.gitignore
!*/PKGBUILD
!*/.SRCINFO

View File

@@ -1,12 +0,0 @@
# AUR Packages
These files are just for reference, I'll manually edit and publish them, at least until I automate it with github actions (like updating version automatically on a release).
There are three packages that follow standard conventions:
- [`lidm`](https://aur.archlinux.org/packages/lidm): Builds latest release (manually updated per release basis)
- [`lidm-bin`](https://aur.archlinux.org/packages/lidm-bin): Fetches latest release binary (compiled by GitHub Actions, also updated per release)
- [`lidm-git`](https://aur.archlinux.org/packages/lidm-git): Fetches latest commit and builds it (should be updated automatically)
> \[!IMPORTANT]
> None of those packages include the service files. [You have to do this yourself](../../services/README.md).

View File

@@ -1,33 +0,0 @@
pkgbase = lidm-bin
pkgdesc = A fully colorful customizable TUI display manager made in C. (release binary)
pkgver = 1.2.3
pkgrel = 4
url = https://github.com/javalsai/lidm
arch = x86_64
arch = i686
arch = aarch64
arch = armv7h
arch = riscv64
license = GPL-3.0-only
depends = libpam.so
depends = lidm-service
provides = lidm
conflicts = lidm
source = lidm-default-theme-1.2.3.ini::https://raw.githubusercontent.com/javalsai/lidm/v1.2.3/themes/default.ini
source = lidm-1.2.3.1::https://raw.githubusercontent.com/javalsai/lidm/v1.2.3/assets/man/lidm.1
source = lidm-config-1.2.3.5::https://raw.githubusercontent.com/javalsai/lidm/v1.2.3/assets/man/lidm-config.5
sha256sums = ffaa5fe2cf5011bf53c90f81bfec8585158d35f72c0666db0bd4d3866ae041ca
sha256sums = 7f2fb91f55088be1a9b1c93ecf5d6c1e437f369b56df2eacc9d10b00c93c39f8
sha256sums = 0aa5755bdcc60ea80cd9ee0f89233ffaf22c6cee9db9da277274a62c6ed477d9
source_x86_64 = lidm-1.2.3-x86_64::https://github.com/javalsai/lidm/releases/download/v1.2.3/lidm-amd64
sha256sums_x86_64 = a533b5aee3ffe04268f8d3ff8d7eb87f09d31fbe25e1b8b1ed29c42ef465bd4b
source_i686 = lidm-1.2.3-i686::https://github.com/javalsai/lidm/releases/download/v1.2.3/lidm-i386
sha256sums_i686 = 75018578e68bffda9807de8a65e16eaed8a16c6cf2417a0b58c5d5bcfa603e45
source_aarch64 = lidm-1.2.3-aarch64::https://github.com/javalsai/lidm/releases/download/v1.2.3/lidm-aarch64
sha256sums_aarch64 = 1b81a1537a1e31ca1902cbc3b60add4ac712aa64fd4d266685f53372cc365882
source_armv7h = lidm-1.2.3-armv7h::https://github.com/javalsai/lidm/releases/download/v1.2.3/lidm-armv7
sha256sums_armv7h = e86f59509fe2366d6312b9bc9e8d89c14e9c049fd713a04c41dab49a848b1ada
source_riscv64 = lidm-1.2.3-riscv64::https://github.com/javalsai/lidm/releases/download/v1.2.3/lidm-riscv64
sha256sums_riscv64 = 3f0eb0315c523d367bac332641e5cd3c86cfd9aa4e7c14b2efc036937b97a598
pkgname = lidm-bin

View File

@@ -1,3 +0,0 @@
*
!PKGBUILD
!.SRCINFO

View File

@@ -1,39 +0,0 @@
# shellcheck disable=SC2034,SC2154,SC2164
# Maintainer: javalsai <javalsai@proton.me>
pkgname=lidm-bin
pkgver=1.2.3
pkgrel=4
depends=('libpam.so' 'lidm-service')
pkgdesc="A fully colorful customizable TUI display manager made in C. (release binary)"
arch=('x86_64' 'i686' 'aarch64' 'armv7h' 'riscv64')
url="https://github.com/javalsai/lidm"
license=('GPL-3.0-only')
provides=('lidm')
conflicts=('lidm')
source=(
"lidm-default-theme-${pkgver}.ini::https://raw.githubusercontent.com/javalsai/lidm/v$pkgver/themes/default.ini"
"lidm-${pkgver}.1::https://raw.githubusercontent.com/javalsai/lidm/v$pkgver/assets/man/lidm.1"
"lidm-config-${pkgver}.5::https://raw.githubusercontent.com/javalsai/lidm/v$pkgver/assets/man/lidm-config.5"
)
source_x86_64=("lidm-${pkgver}-x86_64::$url/releases/download/v$pkgver/lidm-amd64")
source_i686=("lidm-${pkgver}-i686::$url/releases/download/v$pkgver/lidm-i386")
source_aarch64=("lidm-${pkgver}-aarch64::$url/releases/download/v$pkgver/lidm-aarch64")
source_armv7h=("lidm-${pkgver}-armv7h::$url/releases/download/v$pkgver/lidm-armv7")
source_riscv64=("lidm-${pkgver}-riscv64::$url/releases/download/v$pkgver/lidm-riscv64")
sha256sums=('ffaa5fe2cf5011bf53c90f81bfec8585158d35f72c0666db0bd4d3866ae041ca'
'7f2fb91f55088be1a9b1c93ecf5d6c1e437f369b56df2eacc9d10b00c93c39f8'
'0aa5755bdcc60ea80cd9ee0f89233ffaf22c6cee9db9da277274a62c6ed477d9')
sha256sums_x86_64=('a533b5aee3ffe04268f8d3ff8d7eb87f09d31fbe25e1b8b1ed29c42ef465bd4b')
sha256sums_i686=('75018578e68bffda9807de8a65e16eaed8a16c6cf2417a0b58c5d5bcfa603e45')
sha256sums_aarch64=('1b81a1537a1e31ca1902cbc3b60add4ac712aa64fd4d266685f53372cc365882')
sha256sums_armv7h=('e86f59509fe2366d6312b9bc9e8d89c14e9c049fd713a04c41dab49a848b1ada')
sha256sums_riscv64=('3f0eb0315c523d367bac332641e5cd3c86cfd9aa4e7c14b2efc036937b97a598')
package() {
cd "$srcdir"
install -Dm755 "lidm-${pkgver}-$CARCH" "${pkgdir}/usr/bin/lidm"
install -Dm644 "lidm-default-theme-${pkgver}.ini" "${pkgdir}/etc/lidm.ini"
install -Dm644 "lidm-${pkgver}.1" "${pkgdir}/usr/share/man/man1/lidm.1"
install -Dm644 "lidm-config-${pkgver}.5" "${pkgdir}/usr/share/man/man5/lidm-config.5"
}

View File

@@ -1,12 +0,0 @@
pkgbase = lidm-dinit
pkgdesc = A fully colorful customizable TUI display manager made in C. (dinit service files)
pkgver = 1.2.3
pkgrel = 1
url = https://github.com/javalsai/lidm
arch = any
license = GPL-3.0-only
provides = lidm-service
source = lidm-dinit-1.2.3-dinit::https://raw.githubusercontent.com/javalsai/lidm/v1.2.3/assets/services/dinit
sha256sums = 185cd76c1d29ddc73cf96be999f13278a427bb80aa9169f25f30673ad2de3770
pkgname = lidm-dinit

View File

@@ -1,3 +0,0 @@
*
!PKGBUILD
!.SRCINFO

View File

@@ -1,17 +0,0 @@
# shellcheck disable=SC2034,SC2154,SC2164
# Maintainer: javalsai <javalsai@proton.me>
pkgname=lidm-dinit
pkgver=1.2.3
pkgrel=1
pkgdesc="A fully colorful customizable TUI display manager made in C. (dinit service files)"
provides=('lidm-service')
arch=('any')
url="https://github.com/javalsai/lidm"
license=('GPL-3.0-only')
source=("${pkgname}-${pkgver}-dinit::https://raw.githubusercontent.com/javalsai/lidm/v1.2.3/assets/services/dinit")
sha256sums=('185cd76c1d29ddc73cf96be999f13278a427bb80aa9169f25f30673ad2de3770')
package() {
cd "$srcdir"
install -Dm0644 "${pkgname}-${pkgver}-dinit" "$pkgdir/etc/dinit.d/lidm"
}

View File

@@ -1,16 +0,0 @@
pkgbase = lidm-git
pkgdesc = A fully colorful customizable TUI display manager made in C. (last git commit)
pkgver = 1.2.3.r12.g7e7a297
pkgrel = 1
url = https://github.com/javalsai/lidm
arch = any
license = GPL-3.0-only
makedepends = git
depends = pam
depends = lidm-service
provides = lidm
conflicts = lidm
source = lidm::git+https://github.com/javalsai/lidm
sha256sums = SKIP
pkgname = lidm-git

View File

@@ -1,3 +0,0 @@
*
!PKGBUILD
!.SRCINFO

View File

@@ -1,31 +0,0 @@
# shellcheck disable=SC2034,SC2154,SC2164
# Maintainer: javalsai <javalsai@proton.me>
pkgname=lidm-git
pkgver=1.2.3.r13.g9cf5297
pkgrel=1
depends=('pam' 'lidm-service')
makedepends=('git')
pkgdesc="A fully colorful customizable TUI display manager made in C. (last git commit)"
arch=('any')
url="https://github.com/javalsai/lidm"
license=('GPL-3.0-only')
provides=('lidm')
conflicts=('lidm')
source=("lidm::git+https://github.com/javalsai/lidm")
sha256sums=('SKIP')
pkgver() {
cd "$srcdir/lidm"
git describe --long --abbrev=7 --tags | \
sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g'
}
build() {
cd "$srcdir/lidm"
make
}
package() {
cd "$srcdir/lidm"
make DESTDIR="${pkgdir}" install
}

View File

@@ -1,12 +0,0 @@
pkgbase = lidm-systemd
pkgdesc = A fully colorful customizable TUI display manager made in C. (systemd service files)
pkgver = 1.2.3
pkgrel = 1
url = https://github.com/javalsai/lidm
arch = any
license = GPL-3.0-only
provides = lidm-service
source = lidm-systemd-1.2.3-systemd::https://raw.githubusercontent.com/javalsai/lidm/v1.2.3/assets/services/systemd.service
sha256sums = e63464441a97102194994adc82159871ccd3f23d5e97224f21a460e4296364cc
pkgname = lidm-systemd

View File

@@ -1,3 +0,0 @@
*
!PKGBUILD
!.SRCINFO

View File

@@ -1,17 +0,0 @@
# shellcheck disable=SC2034,SC2154,SC2164
# Maintainer: javalsai <javalsai@proton.me>
pkgname=lidm-systemd
pkgver=1.2.3
pkgrel=1
pkgdesc="A fully colorful customizable TUI display manager made in C. (systemd service files)"
provides=('lidm-service')
arch=('any')
url="https://github.com/javalsai/lidm"
license=('GPL-3.0-only')
source=("${pkgname}-${pkgver}-systemd::https://raw.githubusercontent.com/javalsai/lidm/v1.2.3/assets/services/systemd.service")
sha256sums=('e63464441a97102194994adc82159871ccd3f23d5e97224f21a460e4296364cc')
package() {
cd "$srcdir"
install -Dm0644 "${pkgname}-${pkgver}-systemd" "$pkgdir/usr/lib/systemd/system/lidm.service"
}

View File

@@ -1,13 +0,0 @@
pkgbase = lidm
pkgdesc = A fully colorful customizable TUI display manager made in C. (build latest tag)
pkgver = 1.2.3
pkgrel = 5
url = https://github.com/javalsai/lidm
arch = any
license = GPL-3.0-only
depends = pam
depends = lidm-service
source = lidm-1.2.3.tar.gz::https://github.com/javalsai/lidm/archive/refs/tags/v1.2.3.tar.gz
sha256sums = 1ce414b510c5bbc3e32ea882f915b4d3958cb82eb1fbb5cf33e62f69c844bf93
pkgname = lidm

View File

@@ -1,3 +0,0 @@
*
!PKGBUILD
!.SRCINFO

View File

@@ -1,21 +0,0 @@
# shellcheck disable=SC2034,SC2154,SC2164
# Maintainer: javalsai <javalsai@proton.me>
pkgname=lidm
pkgver=1.2.3
pkgrel=5
depends=('pam' 'lidm-service')
makedepends=()
pkgdesc="A fully colorful customizable TUI display manager made in C. (build latest tag)"
arch=('any')
url="https://github.com/javalsai/lidm"
license=('GPL-3.0-only')
source=("${pkgname}-${pkgver}.tar.gz::https://github.com/javalsai/lidm/archive/refs/tags/v$pkgver.tar.gz")
sha256sums=('1ce414b510c5bbc3e32ea882f915b4d3958cb82eb1fbb5cf33e62f69c844bf93')
build() {
make -C "$srcdir/lidm-$pkgver"
}
package() {
make -C "$srcdir/lidm-$pkgver" DESTDIR="$pkgdir" install
}

View File

@@ -1,12 +0,0 @@
#!/usr/bin/env bash
set -e
MYSELF=$(realpath "$0")
MYDIR=$(dirname "$MYSELF")
for pkg in "$MYDIR"/*/; do
cd "$pkg"
printf "\x1b[1mEntering '%s'\x1b[0m\n" "$pkg"
makepkg --printsrcinfo | tee .SRCINFO
echo
done

View File

@@ -1,22 +0,0 @@
#!/usr/bin/env bash
set -e
MYSELF=$(realpath "$0")
MYDIR=$(dirname "$MYSELF")
cd "$MYDIR"
typeset -a pkgs=(lidm{,-git,-bin,-systemd,-dinit})
for pkg in "${pkgs[@]}"; do
printf "\x1b[mEntering '%s'\x1b[0m\n" "$pkg"
cd "$pkg"
# shellcheck disable=SC1091
source PKGBUILD
# shellcheck disable=SC2154
for f in "${source[@]}"; do
echo "$f"
awk -F:: '{print $1}' <<<"$f" | xargs rm -rf
done
cd ..
echo
done

View File

@@ -1,22 +0,0 @@
#!/usr/bin/env bash
set -e
MYSELF=$(realpath "$0")
MYDIR=$(dirname "$MYSELF")
cd "$MYDIR"
typeset -a pkgs=(lidm{,-git,-bin,-systemd,-dinit})
for pkg in "${pkgs[@]}"; do
printf "\x1b[mEntering '%s'\x1b[0m\n" "$pkg"
cd "$pkg"
makepkg -Cf
cd ..
echo
done
if [[ -n "${PRINT_TREE:-}" ]]; then
for pkg in "${pkgs[@]}"; do
eza --tree "$pkg/pkg/"*
done
fi

View File

@@ -1,23 +0,0 @@
#!/usr/bin/env bash
set -e
MYSELF=$(realpath "$0")
MYDIR=$(dirname "$MYSELF")
if [ -z "$1" ]; then
printf "\x1b[1;31mERR: No version to update provided\x1b[0m\n" >&2
exit 1;
fi
version="$1"
printf "\x1b[34mINF: Using '%s' version\x1b[0m\n" "$version"
for pkg in "$MYDIR"/lidm{,-bin,-systemd,-dinit}/; do
cd "$pkg"
printf "\x1b[1mEntering '%s'\x1b[0m\n" "$pkg"
sed -i "s/pkgver=.*/pkgver=$1/" PKGBUILD
sed -i "s/pkgrel=.*/pkgrel=1/" PKGBUILD
updpkgsums
makepkg --printsrcinfo | tee .SRCINFO
echo
done

View File

@@ -11,7 +11,7 @@ let
dmcfg = config.services.displayManager; dmcfg = config.services.displayManager;
desktops = dmcfg.sessionData.desktops; desktops = dmcfg.sessionData.desktops;
version = "2.0.0"; version = "2.0.2";
lidmPkg = pkgs.callPackage ./lidm.nix { lidmPkg = pkgs.callPackage ./lidm.nix {
inherit pkgs; inherit pkgs;
config = { config = {
@@ -21,7 +21,7 @@ let
owner = "javalsai"; owner = "javalsai";
repo = "lidm"; repo = "lidm";
rev = "v${version}"; rev = "v${version}";
sha256 = "sha256-dI1OGndbT6wFAhuGmsPZPqLFvtPREfO/3HqhmlSMpN4="; sha256 = "sha256-P0thtKNVae1oBuJq400fjZMMgd69iY4YDNed5RfQH4I=";
}; };
xsessions = "${desktops}/share/xsessions"; xsessions = "${desktops}/share/xsessions";

9
debug/lidm-debug Normal file
View File

@@ -0,0 +1,9 @@
type = process
command = /bin/sh -c "LIDM_LOG=/tmp/lidm.debug /sbin/agetty tty8 linux-c -n -l /usr/local/bin/lidm -o 8"
restart = true
depends-on = login.target
termsignal = HUP
smooth-recovery = true
inittab-id = 8
inittab-line = tty8
log-type = buffer

View File

@@ -50,7 +50,6 @@ It is recommended to disable any other display managers that are still enabled:
touch /etc/sv/lightdm/down # replace lightdm with your previous display manager (e.g., sddm, gdm) touch /etc/sv/lightdm/down # replace lightdm with your previous display manager (e.g., sddm, gdm)
``` ```
## Fedora ## Fedora
Thanks to @KernelFreeze there's a COPR repo available at <https://copr.fedorainfracloud.org/coprs/celestelove/lidm/> to install lidm. (<https://github.com/javalsai/lidm/discussions/92>) Thanks to @KernelFreeze there's a COPR repo available at <https://copr.fedorainfracloud.org/coprs/celestelove/lidm/> to install lidm. (<https://github.com/javalsai/lidm/discussions/92>)
@@ -59,9 +58,9 @@ Thanks to @KernelFreeze there's a COPR repo available at <https://copr.fedorainf
There are several [AUR packages](https://aur.archlinux.org/packages?K=lidm&SeB=n) for most things. You only need to install one of the following: There are several [AUR packages](https://aur.archlinux.org/packages?K=lidm&SeB=n) for most things. You only need to install one of the following:
* `lidm` - `lidm`
* `lidm-bin` - `lidm-bin`
* `lidm-git` - `lidm-git`
Each of those depend on `lidm-service` which is provided by `lidm-systemd` and `lidm-dinit` (you'll have to pick one). I maintain both but I'm open to anyone that actually uses systemd to take the first one. Each of those depend on `lidm-service` which is provided by `lidm-systemd` and `lidm-dinit` (you'll have to pick one). I maintain both but I'm open to anyone that actually uses systemd to take the first one.
@@ -81,9 +80,6 @@ or try it out without installing by:
nix run github:javalsai/lidm nix run github:javalsai/lidm
``` ```
> [!CAUTION]
> This doesn't include [service files](../assets/pkg/aur#services)
## Nix Module ## Nix Module
<details> <details>
@@ -134,9 +130,6 @@ with config.lidm.keysEnum; {
> _it's not necessary to cover all keys and anything can be put there, even if it's not valid config_ > _it's not necessary to cover all keys and anything can be put there, even if it's not valid config_
> [!NOTE]
> [service files](../assets/pkg/aur#services) **are** included and enabled
# Installing from Source # Installing from Source
Firstly, you'll need to build the package, this also includes man pages, default config, themes and other files you might need. Firstly, you'll need to build the package, this also includes man pages, default config, themes and other files you might need.

View File

@@ -29,6 +29,7 @@ The list of possible `#define`s is:
| `SESSIONS_WAYLAND` | `"/usr/share/wayland-sessions"` | | No | | `SESSIONS_WAYLAND` | `"/usr/share/wayland-sessions"` | | No |
| `SESSIONS_WAYLAND_LOCAL` | `"/usr/local/share/wayland-sessions"` | | No | | `SESSIONS_WAYLAND_LOCAL` | `"/usr/local/share/wayland-sessions"` | | No |
| `LIDM_CONF_PATH` | `"/etc/lidm.ini"` | Path of the default configuration. | Yes (`LIDM_CONF`) | | `LIDM_CONF_PATH` | `"/etc/lidm.ini"` | Path of the default configuration. | Yes (`LIDM_CONF`) |
| `LOGIN_SHELL` | `"bash"` | Login shell name. | No |
# Other Build Settings # Other Build Settings

View File

@@ -6,7 +6,7 @@
#include "keys.h" #include "keys.h"
#include "macros.h" #include "macros.h"
#include "util.h" #include "util/vec.h"
enum IntrospectionType { enum IntrospectionType {
STRING, STRING,
@@ -122,7 +122,8 @@ BUILD(strings, STRINGS, TABLE_STRINGS);
F(struct Vector, source, STRING_ARRAY, NULL_VEC, name) \ F(struct Vector, source, STRING_ARRAY, NULL_VEC, name) \
F(struct Vector, user_source, STRING_ARRAY, NULL_VEC, name) \ F(struct Vector, user_source, STRING_ARRAY, NULL_VEC, name) \
F(char* NNULLABLE, timefmt, STRING, "%X %x", name) \ F(char* NNULLABLE, timefmt, STRING, "%X %x", name) \
F(long long, refresh_rate, NUMBER, 100, name) F(long long, refresh_rate, NUMBER, 100, name) \
F(bool, bypass_shell_login, BOOL, false, name)
BUILD(behavior, BEHAVIOR, TABLE_BEHAVIOR); BUILD(behavior, BEHAVIOR, TABLE_BEHAVIOR);

View File

@@ -6,8 +6,7 @@
#include "macros.h" #include "macros.h"
char* NULLABLE search_path(const char* NNULLABLE for_binary); char* NULLABLE desktop_as_cmdline(char** args);
int execvpe_desktop(char** args, char* NNULLABLE* NNULLABLE envlist);
int parse_exec_string(const char* exec_s, int* arg_count, char*** args); int parse_exec_string(const char* exec_s, int* arg_count, char*** args);
void free_parsed_args(int arg_count, char** args); void free_parsed_args(int arg_count, char** args);

View File

@@ -2,16 +2,17 @@
#define EFIELDH_ #define EFIELDH_
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <sys/types.h> #include <sys/types.h>
// holds also the max string buffer in itself, not dynamic // holds also the max string buffer in itself, not dynamic
struct editable_field { struct editable_field {
u_char pos; uint8_t pos;
char content[255]; char content[255];
}; };
struct editable_field efield_new(char* content); struct editable_field efield_new(char* content);
void efield_trim(struct editable_field* self, u_char pos); void efield_trim(struct editable_field* self, uint8_t pos);
void efield_update(struct editable_field* self, char* update); void efield_update(struct editable_field* self, char* update);
bool efield_seek(struct editable_field* self, char seek); bool efield_seek(struct editable_field* self, char seek);

View File

@@ -1,7 +1,10 @@
#ifndef KEYSH_ #ifndef KEYSH_
#define KEYSH_ #define KEYSH_
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/time.h>
enum Keys { enum Keys {
ESC, ESC,
@@ -103,4 +106,15 @@ static const struct key_mapping KEY_MAPPINGS[] = {
{PAGE_DOWN, {"\x1b[6~", NULL}}, {PAGE_DOWN, {"\x1b[6~", NULL}},
}; };
struct option_keys {
bool is_some;
enum Keys key;
};
int find_keyname(enum Keys* at, const char* name);
struct option_keys find_ansi(const char* seq);
void read_press(uint8_t* length, char* out);
// non blocking, waits up to tv or interrupt, returns true if actually read
bool read_press_nb(uint8_t* length, char* out, struct timeval* tv);
#endif #endif

View File

@@ -18,6 +18,6 @@ void ofield_kbd_type(struct opts_field* self, char* typed, char* empty_default);
bool ofield_opts_seek(struct opts_field* self, char seek); bool ofield_opts_seek(struct opts_field* self, char seek);
bool ofield_seek(struct opts_field* self, char seek); bool ofield_seek(struct opts_field* self, char seek);
u_char ofield_display_cursor_col(struct opts_field* self, u_char maxlen); uint8_t ofield_display_cursor_col(struct opts_field* self, uint8_t maxlen);
#endif #endif

View File

@@ -4,9 +4,8 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include "desktop_exec.h"
#include "macros.h" #include "macros.h"
#include "util.h" #include "util/vec.h"
enum SessionType { enum SessionType {
XORG, XORG,
@@ -51,18 +50,6 @@ static inline struct session_exec session_exec_desktop(
}; };
} }
static inline int session_exec_exec(struct session_exec* NNULLABLE exec,
char* NULLABLE* NNULLABLE envlist) {
switch (exec->typ) {
case EXEC_SHELL:
return execle(exec->shell, exec->shell, NULL, envlist);
case EXEC_DESKTOP:
return execvpe_desktop(exec->desktop.args, envlist);
default:
__builtin_unreachable();
}
}
struct session { struct session {
char* NNULLABLE name; char* NNULLABLE name;
struct session_exec exec; struct session_exec exec;
@@ -70,5 +57,9 @@ struct session {
}; };
struct Vector get_avaliable_sessions(); struct Vector get_avaliable_sessions();
int session_exec_exec(struct session_exec* NNULLABLE exec,
char* NULLABLE* NNULLABLE envp);
int session_exec_login_through_shell(struct session_exec* NNULLABLE exec,
char* NULLABLE* NNULLABLE envp);
#endif #endif

View File

@@ -3,7 +3,7 @@
#include "config.h" #include "config.h"
#include "ofield.h" #include "ofield.h"
#include "util.h" #include "util/vec.h"
// [box_start] // [box_start]
// ↓ // ↓
@@ -40,7 +40,7 @@
#define VALUE_MAXLEN (BOX_WIDTH - VALUES_COL + 1 - BOX_HMARGIN - 2) #define VALUE_MAXLEN (BOX_WIDTH - VALUES_COL + 1 - BOX_HMARGIN - 2)
enum Input { SESSION, USER, PASSWD }; enum Input { SESSION, USER, PASSWD };
extern const u_char INPUTS_N; extern const uint8_t INPUTS_N;
void setup(struct config* config); void setup(struct config* config);
int load(struct Vector* users, struct Vector* sessions); int load(struct Vector* users, struct Vector* sessions);

View File

@@ -3,7 +3,7 @@
#include <sys/types.h> #include <sys/types.h>
#include "util.h" #include "util/vec.h"
struct user { struct user {
char* shell; char* shell;

View File

@@ -1,54 +0,0 @@
#ifndef UTILH_
#define UTILH_
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include "keys.h"
int find_keyname(enum Keys* at, const char* name);
struct option_keys {
bool is_some;
enum Keys key;
};
struct option_keys find_ansi(const char* seq);
void read_press(u_char* length, char* out);
// non blocking, waits up to tv or interrupt, returns true if actually read
bool read_press_nb(u_char* length, char* out, struct timeval* tv);
// UTF8
//
bool utf8_iscont(char byte);
size_t utf8len(const char* str);
size_t utf8len_until(const char* str, const char* until);
size_t utf8trunc(char* str, size_t n);
const char* utf8back(const char* str);
const char* utf8seek(const char* str);
const char* utf8seekn(const char* str, size_t n);
// Vector
//
struct Vector {
uint32_t length;
uint32_t capacity;
void** pages;
};
struct Vector vec_from_raw(void** raw);
void** vec_as_raw(struct Vector self);
extern const struct Vector VEC_NEW;
int vec_resize(struct Vector* self, size_t size);
int vec_reserve(struct Vector* self, size_t size);
int vec_reserve_exact(struct Vector* self, size_t size);
int vec_push(struct Vector* self, void* item);
void vec_free(struct Vector* self);
void vec_clear(struct Vector* self);
void vec_reset(struct Vector* self);
void* vec_pop(struct Vector* self); // won't free it, nor shrink vec list space
void* vec_get(struct Vector* self, size_t index);
#endif

10
include/util/path.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef UTIL_PATH_H
#define UTIL_PATH_H
#include "macros.h"
char* NULLABLE search_path(const char* NNULLABLE for_binary);
int execvpe(const char* NNULLABLE file, char* NULLABLE const argv[NNULLABLE],
char* NULLABLE const envp[NNULLABLE]);
#endif /* UTIL_PATH_H */

15
include/util/utf8.h Normal file
View File

@@ -0,0 +1,15 @@
#ifndef UTIL_UTF8_H
#define UTIL_UTF8_H
#include <stdbool.h>
#include <stddef.h>
bool utf8_iscont(char byte);
size_t utf8len(const char* str);
size_t utf8len_until(const char* str, const char* until);
size_t utf8trunc(char* str, size_t n);
const char* utf8back(const char* str);
const char* utf8seek(const char* str);
const char* utf8seekn(const char* str, size_t n);
#endif /* UTIL_UTF8_H */

26
include/util/vec.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef UTIL_VEC_H
#define UTIL_VEC_H
#include <stddef.h>
#include <stdint.h>
struct Vector {
uint32_t length;
uint32_t capacity;
void** pages;
};
struct Vector vec_from_raw(void** raw);
void** vec_as_raw(struct Vector self);
extern const struct Vector VEC_NEW;
int vec_resize(struct Vector* self, size_t size);
int vec_reserve(struct Vector* self, size_t size);
int vec_reserve_exact(struct Vector* self, size_t size);
int vec_push(struct Vector* self, void* item);
void vec_free(struct Vector* self);
void vec_clear(struct Vector* self);
void vec_reset(struct Vector* self);
void* vec_pop(struct Vector* self); // won't free it, nor shrink vec list space
void* vec_get(struct Vector* self, size_t index);
#endif /* UTIL_VEC_H */

View File

@@ -1,10 +1,11 @@
// TODO: handle `fork() == -1`// TODO: handle `fork() == -1`s // TODO: handle `fork() == -1`
#include <errno.h> #include <errno.h>
#include <grp.h> #include <grp.h>
#include <pwd.h> #include <pwd.h>
#include <security/pam_misc.h> #include <security/pam_misc.h>
#include <signal.h> #include <signal.h>
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -21,7 +22,8 @@
#include "pam.h" #include "pam.h"
#include "sessions.h" #include "sessions.h"
#include "ui.h" #include "ui.h"
#include "util.h" #include "util/path.h"
#include "util/vec.h"
#define XORG_MESSAGE_LENGTH 16 #define XORG_MESSAGE_LENGTH 16
@@ -80,16 +82,6 @@ static void source_paths(struct Vector* NNULLABLE vec_envlist,
} }
} }
/*char *buf;*/
/*size_t bsize = snprintf(NULL, 0, "/run/user/%d", pw->pw_uid) + 1;*/
/*buf = malloc(bsize);*/
/*snprintf(buf, bsize, "/run/user/%d", pw->pw_uid);*/
/*setenv("XDG_RUNTIME_DIR", buf, true);*/
/*setenv("XDG_SESSION_CLASS", "user", true);*/
/*setenv("XDG_SESSION_ID", "1", true);*/
/*setenv("XDG_SESSION_DESKTOP", , true);*/
/*setenv("XDG_SEAT", "seat0", true);*/
struct child_msg { struct child_msg {
char* msg; char* msg;
int _errno; int _errno;
@@ -176,7 +168,8 @@ static void start_xorg_server(struct passwd* pw, char** NNULLABLE envlist,
// TODO: add error msgs // TODO: add error msgs
/// returns on completion /// returns on completion
static void launch_with_xorg_server(struct session_exec* NNULLABLE exec, static void launch_with_xorg_server(struct config* config,
struct session_exec* NNULLABLE exec,
struct passwd* pw, struct passwd* pw,
char** NNULLABLE envlist) { char** NNULLABLE envlist) {
int xorg_pipefd[2]; int xorg_pipefd[2];
@@ -212,10 +205,14 @@ static void launch_with_xorg_server(struct session_exec* NNULLABLE exec,
(void)fflush(NULL); (void)fflush(NULL);
pid_t xorg_session_pid = fork(); pid_t xorg_session_pid = fork();
if (xorg_session_pid == 0) { if (xorg_session_pid == 0) {
int exit = session_exec_exec(exec, envlist); if (config->behavior.bypass_shell_login)
perror("exec error"); session_exec_exec(exec, envlist);
else
session_exec_login_through_shell(exec, envlist);
perror("session exec error");
(void)fputs("failure calling session\n", stderr); (void)fputs("failure calling session\n", stderr);
_exit(exit); _exit(EXIT_FAILURE);
} }
// looks weird, waiting on -1 should wait on any child and then just check if // looks weird, waiting on -1 should wait on any child and then just check if
@@ -254,8 +251,8 @@ static void launch_with_xorg_server(struct session_exec* NNULLABLE exec,
char _dummy; \ char _dummy; \
read(pipefd[0], &_dummy, sizeof(_dummy)); \ read(pipefd[0], &_dummy, sizeof(_dummy)); \
} }
inline static void forked(int pipefd[2], struct passwd* pw, inline static void forked(struct config* config, int pipefd[2],
char* NNULLABLE user, struct passwd* pw, char* NNULLABLE user,
struct session* NNULLABLE session, struct session* NNULLABLE session,
char** NNULLABLE envlist) { char** NNULLABLE envlist) {
if (chdir(pw->pw_dir) == -1) SEND_ERR("chdir"); if (chdir(pw->pw_dir) == -1) SEND_ERR("chdir");
@@ -267,14 +264,26 @@ inline static void forked(int pipefd[2], struct passwd* pw,
DUMMY_READ(); DUMMY_READ();
close(pipefd[0]); close(pipefd[0]);
if (!getenv("PATH")) {
size_t path_size = confstr(_CS_PATH, NULL, 0);
char* path_env = malloc(path_size);
confstr(_CS_PATH, path_env, path_size);
setenv("PATH", path_env, 1);
free(path_env);
}
log_printf(" [I] using shell login?: %s",
config->behavior.bypass_shell_login ? "true" : "false");
if (session->type == XORG) { if (session->type == XORG) {
launch_with_xorg_server(&session->exec, pw, envlist); launch_with_xorg_server(config, &session->exec, pw, envlist);
_exit(EXIT_SUCCESS); _exit(EXIT_SUCCESS);
} else { } else {
int exit = session_exec_exec(&session->exec, envlist); if (config->behavior.bypass_shell_login)
perror("exec error"); session_exec_exec(&session->exec, envlist);
(void)fputs("failure calling session\n", stderr); else
_exit(exit); session_exec_login_through_shell(&session->exec, envlist);
perror("session exec error");
_exit(EXIT_FAILURE);
} }
} }
#undef SEND_MSG #undef SEND_MSG
@@ -325,7 +334,7 @@ bool launch(char* user, char* passwd, struct session session, void (*cb)(void),
uint pid = fork(); uint pid = fork();
if (pid == 0) if (pid == 0)
forked(pipefd, pw, user, &session, envlist); forked(config, pipefd, pw, user, &session, envlist);
else { else {
struct child_msg msg; struct child_msg msg;
read(pipefd[0], &msg, sizeof(struct child_msg)); read(pipefd[0], &msg, sizeof(struct child_msg));

View File

@@ -11,14 +11,15 @@
#include "config.h" #include "config.h"
#include "desktop.h" #include "desktop.h"
#include "keys.h"
#include "log.h" #include "log.h"
#include "macros.h" #include "macros.h"
#include "util.h" #include "util/vec.h"
#define UPPER_HALF_BYTE 4 #define UPPER_HALF_BYTE 4
int parse_hex(char* _at, char x1, char x2) { int parse_hex(char* _at, char x1, char x2) {
// make linter happy // make linter happy
u_char* at = (u_char*)_at; uint8_t* at = (uint8_t*)_at;
*at = 0; *at = 0;

View File

@@ -9,61 +9,39 @@
#include "desktop_exec.h" #include "desktop_exec.h"
#include "macros.h" #include "macros.h"
#include "util/path.h"
// constants for exec string parsing // constants for exec string parsing
#define MAX_ARGS 100 #define MAX_ARGS 100
// ARG_LENGTH is the initial length of a parsed argument // ARG_LENGTH is the initial length of a parsed argument
#define ARG_LENGTH 64 #define ARG_LENGTH 64
// returns NULL on any error char* NULLABLE desktop_as_cmdline(char** args) {
// otherwise it returns the absolute path of the program that MUST BE FREED if (args[0] == NULL) return NULL;
char* NULLABLE search_path(const char* NNULLABLE for_binary) { size_t fmtd_len = 0;
if (strchr(for_binary, '/') != NULL) {
// skip absolute paths char** ptr = args;
return strdup(for_binary); while (*ptr) {
fmtd_len += strlen(*ptr) + 1;
ptr++;
} }
char* path_env = getenv("PATH"); fmtd_len -= 1;
if (!path_env) return NULL;
char* path = strdup(path_env);
if (!path) return NULL;
char* tok = strtok(path, ":"); char* fmt_cmdline = malloc(fmtd_len + 1);
while (tok) { if (!fmt_cmdline) return NULL;
char* bin_path;
asprintf(&bin_path, "%s/%s", tok, for_binary);
if (!bin_path) {
free(path);
return NULL;
}
struct stat stat_buf; size_t fmting_len = 0;
if (stat(bin_path, &stat_buf) == 0) { ptr = args;
// TODO: check exec bit ig while (*ptr) {
// if(stat_buf.) {} char* nbyte = stpcpy(&fmt_cmdline[fmting_len], *ptr);
free(path); *nbyte = ' ';
return bin_path;
}
free(bin_path); fmting_len += nbyte - &fmt_cmdline[fmting_len] + 1;
tok = strtok(NULL, ":"); ptr++;
} }
fmt_cmdline[fmting_len - 1] = '\0';
free(path); return fmt_cmdline;
return NULL;
}
// returns -1 on exec failure and -2 on search failure
int execvpe_desktop(char** args, char* NNULLABLE* NNULLABLE envlist) {
char* new_arg = search_path(args[0]);
if (!new_arg) return -2;
free(args[0]);
args[0] = new_arg;
int status = execve(args[0], args, envlist);
free(new_arg);
return status;
} }
// parse Exec=/bin/prog arg1 arg2\ with\ spaces // parse Exec=/bin/prog arg1 arg2\ with\ spaces

View File

@@ -2,7 +2,7 @@
#include "efield.h" #include "efield.h"
#include "ui.h" #include "ui.h"
#include "util.h" #include "util/utf8.h"
struct editable_field efield_new(char* content) { struct editable_field efield_new(char* content) {
struct editable_field efield; struct editable_field efield;
@@ -16,14 +16,14 @@ struct editable_field efield_new(char* content) {
return efield; return efield;
} }
void efield_trim(struct editable_field* self, u_char pos) { void efield_trim(struct editable_field* self, uint8_t pos) {
self->pos = pos; self->pos = pos;
self->content[pos + 1] = '\0'; self->content[pos + 1] = '\0';
} }
#define BACKSPACE_CODE 127 #define BACKSPACE_CODE 127
void efield_update(struct editable_field* self, char* update) { void efield_update(struct editable_field* self, char* update) {
u_char insert_len = strlen(update); uint8_t insert_len = strlen(update);
if (insert_len == 0) return; if (insert_len == 0) return;
if (self->pos > strlen(self->content)) if (self->pos > strlen(self->content))
@@ -61,7 +61,7 @@ bool efield_seek(struct editable_field* self, char seek) {
if (*self->content == '\0') return false; if (*self->content == '\0') return false;
if (seek == 0) return false; if (seek == 0) return false;
u_char count = seek < 0 ? -seek : seek; uint8_t count = seek < 0 ? -seek : seek;
char* ptr = &self->content[self->pos]; char* ptr = &self->content[self->pos];
char* start = ptr; char* start = ptr;
@@ -75,6 +75,6 @@ bool efield_seek(struct editable_field* self, char seek) {
} }
} }
self->pos = (u_char)(ptr - self->content); self->pos = (uint8_t)(ptr - self->content);
return ptr != start; return ptr != start;
} }

77
src/keys.c Normal file
View File

@@ -0,0 +1,77 @@
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include "keys.h"
#include "macros.h"
#include "ui.h"
static int selret_magic();
int find_keyname(enum Keys* at, const char* name) {
for (size_t i = 0; i < LEN(KEY_MAPPINGS); i++) {
if (strcmp(KEY_NAMES[i], name) == 0) {
*at = (enum Keys)i;
return 0;
}
}
return -1;
}
struct option_keys find_ansi(const char* seq) {
for (size_t i = 0; i < LEN(KEY_MAPPINGS); i++) {
struct key_mapping mapping = KEY_MAPPINGS[i];
for (size_t j = 0; mapping.sequences[j] != NULL; j++) {
if (strcmp(mapping.sequences[j], seq) == 0) {
return (struct option_keys){
.is_some = true,
.key = (enum Keys)i,
};
}
}
}
return (struct option_keys){.is_some = false};
}
void read_press(uint8_t* length, char* out) {
*length = 0;
while (true) {
if (read(STDIN_FILENO, &out[(*length)++], 1) != 1) {
print_errno("read error");
sleep(3);
exit(1);
}
int selret = selret_magic();
if (selret == -1) {
print_errno("selret error");
} else if (selret != 1) {
out[*length] = '\0';
return;
}
}
}
bool read_press_nb(uint8_t* length, char* out, struct timeval* tv) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
errno = 0;
int ret = select(STDIN_FILENO + 1, &fds, NULL, NULL, tv);
if (errno || ret <= 0) return false;
read_press(length, out);
return true;
}
// https://stackoverflow.com/a/48040042
static int selret_magic() {
fd_set set;
struct timeval timeout;
FD_ZERO(&set);
FD_SET(STDIN_FILENO, &set);
timeout.tv_sec = 0;
timeout.tv_usec = 0;
return select(1, &set, NULL, NULL, &timeout);
}

View File

@@ -22,16 +22,16 @@ int read_launch_state(struct LaunchState* NNULLABLE state) {
size_t num = 0; size_t num = 0;
ssize_t chars = getline(&state->username, &num, state_fd); ssize_t chars = getline(&state->username, &num, state_fd);
if (chars < 0) goto fail; if (chars <= 0) goto fail;
if (state->username[chars] == '\n') state->username[chars] = 0; if (state->username[chars - 1] == '\n') state->username[chars - 1] = 0;
num = 0; num = 0;
chars = getline(&state->session_opt, &num, state_fd); chars = getline(&state->session_opt, &num, state_fd);
if (chars < 0) { if (chars <= 0) {
free(state->session_opt); free(state->session_opt);
goto fail; goto fail;
} }
if (state->session_opt[chars] == '\n') state->session_opt[chars] = 0; if (state->session_opt[chars - 1] == '\n') state->session_opt[chars - 1] = 0;
(void)fclose(state_fd); (void)fclose(state_fd);
return 0; return 0;

View File

@@ -8,6 +8,7 @@ static FILE* logger_out = NULL;
void log_init(FILE* fd) { void log_init(FILE* fd) {
if (logger_out) (void)fclose(logger_out); if (logger_out) (void)fclose(logger_out);
(void)setvbuf(fd, NULL, _IONBF, 0);
logger_out = fd; logger_out = fd;
} }

View File

@@ -15,7 +15,7 @@
#include "signal_handler.h" #include "signal_handler.h"
#include "ui.h" #include "ui.h"
#include "users.h" #include "users.h"
#include "util.h" #include "util/vec.h"
#define DATESTR_MAXBUFSIZE 0x20 #define DATESTR_MAXBUFSIZE 0x20
@@ -31,6 +31,7 @@ int main(int argc, char* argv[]) {
} }
log_init(log_fd); log_init(log_fd);
log_puts("Starting log:");
} }
if (argc == 2) { if (argc == 2) {

View File

@@ -1,10 +1,9 @@
#include <stddef.h> #include <stddef.h>
#include "config.h"
#include "efield.h" #include "efield.h"
#include "ofield.h" #include "ofield.h"
#include "ui.h" #include "ui.h"
#include "util.h" #include "util/utf8.h"
struct opts_field ofield_new(size_t opts) { struct opts_field ofield_new(size_t opts) {
if (opts == 0) { if (opts == 0) {
@@ -55,11 +54,11 @@ bool ofield_seek(struct opts_field* self, char seek) {
return true; return true;
} }
u_char ofield_display_cursor_col(struct opts_field* self, u_char maxlen) { uint8_t ofield_display_cursor_col(struct opts_field* self, uint8_t maxlen) {
if (self->current_opt == 0) { if (self->current_opt == 0) {
u_char display_len = utf8len(self->efield.content); uint8_t display_len = utf8len(self->efield.content);
u_char pos = utf8len_until(self->efield.content, uint8_t pos = utf8len_until(self->efield.content,
&self->efield.content[self->efield.pos]); &self->efield.content[self->efield.pos]);
if (display_len > maxlen) { if (display_len > maxlen) {
if (pos < maxlen / 2) { if (pos < maxlen / 2) {

View File

@@ -6,6 +6,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "log.h"
#include "macros.h" #include "macros.h"
#include "pam.h" #include "pam.h"
#include "sessions.h" #include "sessions.h"
@@ -92,6 +93,10 @@ struct pamh_getenv_status pamh_get_complete_env(pam_handle_t* handle,
char** raw_envlist = pam_getenvlist(handle); char** raw_envlist = pam_getenvlist(handle);
if (!raw_envlist) FAIL(PAMH_ERR_ERRNO, "pam_getenvlist"); if (!raw_envlist) FAIL(PAMH_ERR_ERRNO, "pam_getenvlist");
char** ptr = raw_envlist;
while (*ptr)
log_printf("[I] got pam env %s\n", *(ptr++));
struct envpair extra_env[] = { struct envpair extra_env[] = {
{"TERM", getenv("TERM")}, {"TERM", getenv("TERM")},
{"PATH", getenv("PATH")}, {"PATH", getenv("PATH")},

View File

@@ -1,3 +1,4 @@
#include <errno.h>
#include <ftw.h> #include <ftw.h>
#include <limits.h> #include <limits.h>
#include <stdbool.h> #include <stdbool.h>
@@ -6,13 +7,16 @@
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h>
#include "config.h"
#include "desktop.h" #include "desktop.h"
#include "desktop_exec.h" #include "desktop_exec.h"
#include "log.h" #include "log.h"
#include "macros.h" #include "macros.h"
#include "sessions.h" #include "sessions.h"
#include "util.h" #include "util/path.h"
#include "util/vec.h"
struct source_dir { struct source_dir {
enum SessionType type; enum SessionType type;
@@ -163,3 +167,54 @@ struct Vector get_avaliable_sessions() {
return sessions; return sessions;
} }
int session_exec_exec(struct session_exec* NNULLABLE exec,
char* NULLABLE* NNULLABLE envp) {
switch (exec->typ) {
case EXEC_SHELL: {
char* argv[] = {exec->shell, NULL};
return execvpe(exec->shell, argv, envp);
}
case EXEC_DESKTOP:
return execvpe(exec->desktop.args[0], exec->desktop.args, envp);
default:
__builtin_unreachable();
}
}
/// So the reason this doesn't use the user shell is because that can really be
/// anything, also assuming it were fish for example, it can't execute posix
/// shell files and that leaves it out of most `/etc/profile.d/` scripts.
///
/// I'll just default to bash for now as it's able to source almost everything
/// and takes most flags. Maybe will try to make this more sophisticated in the
/// future.
///
/// This respects errno. Even also works as any exec family function.
#ifndef LOGIN_SHELL
#define LOGIN_SHELL "bash"
#endif
// This triggers login behavior
#define LOGIN_SHELL_ARG0 "-" LOGIN_SHELL
int session_exec_login_through_shell(struct session_exec* NNULLABLE exec,
char* NULLABLE* NNULLABLE envp) {
switch (exec->typ) {
case EXEC_SHELL: {
char* argv[] = {LOGIN_SHELL_ARG0, "-c", exec->shell, NULL};
return execvpe(LOGIN_SHELL, argv, envp);
}
case EXEC_DESKTOP: {
char* name = desktop_as_cmdline(exec->desktop.args);
if (!name) {
errno = EINVAL;
return -1;
}
char* argv[] = {LOGIN_SHELL_ARG0, "-c", name, NULL};
int ret = execvpe(LOGIN_SHELL, argv, envp);
free(name);
return ret;
}
default:
__builtin_unreachable();
}
}

View File

@@ -32,9 +32,9 @@
#include "ui.h" #include "ui.h"
#include "ui_state.h" #include "ui_state.h"
#include "users.h" #include "users.h"
#include "util.h" #include "util/utf8.h"
const u_char INPUTS_N = 3; const uint8_t INPUTS_N = 3;
struct uint_point { struct uint_point {
uint x; uint x;
@@ -135,11 +135,11 @@ char* trunc_gethostname(size_t maxlen, const char* const ELLIPSIS) {
} }
void ui_update_cursor_focus() { void ui_update_cursor_focus() {
u_char line = box_start.y; uint8_t line = box_start.y;
u_char col = box_start.x + VALUES_COL; uint8_t col = box_start.x + VALUES_COL;
struct opts_field* ofield = get_opts_ffield(); struct opts_field* ofield = get_opts_ffield();
u_char maxlen = VALUE_MAXLEN; uint8_t maxlen = VALUE_MAXLEN;
if (ofield->opts > 1) { if (ofield->opts > 1) {
maxlen -= utf8len(g_config->strings.opts_pre) + maxlen -= utf8len(g_config->strings.opts_pre) +
utf8len(g_config->strings.opts_post); utf8len(g_config->strings.opts_post);
@@ -269,7 +269,7 @@ int load(struct Vector* users, struct Vector* sessions) {
scratch_print_ui(); scratch_print_ui();
/// INTERACTIVE /// INTERACTIVE
u_char len; uint8_t len;
char seq[0xff]; char seq[0xff];
uint esc = 0; uint esc = 0;
while (true) { while (true) {
@@ -362,11 +362,11 @@ void clean_line(struct uint_point origin, uint line) {
printf("\x1b[%d;%dH%s", origin.y + line, origin.x + 1, line_cleaner); printf("\x1b[%d;%dH%s", origin.y + line, origin.x + 1, line_cleaner);
} }
u_char get_render_pos_offset(struct opts_field* self, u_char maxlen) { uint8_t get_render_pos_offset(struct opts_field* self, uint8_t maxlen) {
if (self->current_opt != 0) return 0; if (self->current_opt != 0) return 0;
u_char pos = utf8len_until(self->efield.content, uint8_t pos = utf8len_until(self->efield.content,
&self->efield.content[self->efield.pos]); &self->efield.content[self->efield.pos]);
return pos - ofield_display_cursor_col(self, maxlen); return pos - ofield_display_cursor_col(self, maxlen);
} }
@@ -439,8 +439,8 @@ void print_session(struct session session, bool multiple) {
char* toprint = session.name; char* toprint = session.name;
if (multiple) { if (multiple) {
u_char maxlen = VALUE_MAXLEN - utf8len(g_config->strings.opts_pre) - uint8_t maxlen = VALUE_MAXLEN - utf8len(g_config->strings.opts_pre) -
utf8len(g_config->strings.opts_post); utf8len(g_config->strings.opts_post);
toprint += get_render_pos_offset(&of_session, maxlen); toprint += get_render_pos_offset(&of_session, maxlen);
size_t printlen = utf8seekn(toprint, maxlen) - toprint; size_t printlen = utf8seekn(toprint, maxlen) - toprint;
@@ -467,8 +467,8 @@ void print_user(struct user user, bool multiple) {
char* toprint = user.display_name; char* toprint = user.display_name;
if (multiple) { if (multiple) {
u_char maxlen = VALUE_MAXLEN - utf8len(g_config->strings.opts_pre) - uint8_t maxlen = VALUE_MAXLEN - utf8len(g_config->strings.opts_pre) -
utf8len(g_config->strings.opts_post); utf8len(g_config->strings.opts_post);
toprint += get_render_pos_offset(&of_session, maxlen); toprint += get_render_pos_offset(&of_session, maxlen);
size_t printlen = utf8seekn(toprint, maxlen) - toprint; size_t printlen = utf8seekn(toprint, maxlen) - toprint;

View File

@@ -7,7 +7,7 @@
#include "log.h" #include "log.h"
#include "macros.h" #include "macros.h"
#include "users.h" #include "users.h"
#include "util.h" #include "util/vec.h"
// NOLINTNEXTLINE(readability-identifier-length) // NOLINTNEXTLINE(readability-identifier-length)
int build_user(struct user* NNULLABLE user, struct passwd* p) { int build_user(struct user* NNULLABLE user, struct passwd* p) {

View File

@@ -1,238 +0,0 @@
#include <asm-generic/errno-base.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include "keys.h"
#include "ui.h"
#include "util.h"
static int selret_magic();
int find_keyname(enum Keys* at, const char* name) {
for (size_t i = 0; i < LEN(KEY_MAPPINGS); i++) {
if (strcmp(KEY_NAMES[i], name) == 0) {
*at = (enum Keys)i;
return 0;
}
}
return -1;
}
struct option_keys find_ansi(const char* seq) {
for (size_t i = 0; i < LEN(KEY_MAPPINGS); i++) {
struct key_mapping mapping = KEY_MAPPINGS[i];
for (size_t j = 0; mapping.sequences[j] != NULL; j++) {
if (strcmp(mapping.sequences[j], seq) == 0) {
return (struct option_keys){
.is_some = true,
.key = (enum Keys)i,
};
}
}
}
return (struct option_keys){.is_some = false};
}
void read_press(u_char* length, char* out) {
*length = 0;
while (true) {
if (read(STDIN_FILENO, &out[(*length)++], 1) != 1) {
print_errno("read error");
sleep(3);
exit(1);
}
int selret = selret_magic();
if (selret == -1) {
print_errno("selret error");
} else if (selret != 1) {
out[*length] = '\0';
return;
}
}
}
bool read_press_nb(u_char* length, char* out, struct timeval* tv) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
errno = 0;
int ret = select(STDIN_FILENO + 1, &fds, NULL, NULL, tv);
if (errno || ret <= 0) return false;
read_press(length, out);
return true;
}
// https://stackoverflow.com/a/48040042
static int selret_magic() {
fd_set set;
struct timeval timeout;
FD_ZERO(&set);
FD_SET(STDIN_FILENO, &set);
timeout.tv_sec = 0;
timeout.tv_usec = 0;
return select(1, &set, NULL, NULL, &timeout);
}
// UTF-8 shii
#define UTF8_CONT_MSK 0b11000000
#define UTF8_CONT_VAL 0b10000000
bool utf8_iscont(char byte) {
return (byte & UTF8_CONT_MSK) == UTF8_CONT_VAL;
}
size_t utf8len(const char* str) {
size_t len = 0;
while (*str != '\0') {
if (!utf8_iscont(*(str++))) len++;
}
return len;
}
size_t utf8len_until(const char* str, const char* until) {
size_t len = 0;
while (str < until) {
if (!utf8_iscont(*(str++))) len++;
}
return len;
}
size_t utf8trunc(char* str, size_t n) {
size_t bytes = 0;
while (true) {
if (str[bytes] == '\0') break;
if (utf8_iscont(str[bytes])) {
bytes++;
continue;
}
if (n == 0) {
str[bytes] = '\0';
break;
}
bytes++;
n--;
}
return bytes;
}
const char* utf8back(const char* str) {
while (utf8_iscont(*(--str))) {
}
return str;
}
const char* utf8seek(const char* str) {
while (utf8_iscont(*(++str))) {
}
return str;
}
const char* utf8seekn(const char* str, size_t n) {
while (n > 0 && *str != '\0') {
str = utf8seek(str);
n--;
}
return str;
}
// Vector shii
const struct Vector VEC_NEW = {
.length = 0,
.capacity = 0,
.pages = NULL,
};
struct Vector vec_from_raw(void** raw) {
size_t len = 0;
while (raw[len])
len++;
return (struct Vector){
.length = len,
.capacity = len,
.pages = raw,
};
}
void** vec_as_raw(struct Vector self) {
if (vec_push(&self, NULL) != 0) return NULL;
return self.pages;
}
int vec_resize(struct Vector* self, size_t size) {
void** new_location =
(void**)realloc((void*)self->pages, size * sizeof(void*));
if (new_location != NULL) {
if (self->length > size) self->length = size;
self->capacity = size;
self->pages = new_location;
} else {
return -1;
}
return 0;
}
int vec_reserve(struct Vector* self, size_t size) {
uint32_t new_capacity = self->capacity;
while (self->length + size > new_capacity) {
new_capacity = new_capacity + (new_capacity >> 1) +
1; // cap * 1.5 + 1; 0 1 2 4 7 11...
}
return vec_resize(self, new_capacity);
}
int vec_reserve_exact(struct Vector* self, size_t size) {
uint32_t needed_capacity = self->length + size;
if (self->capacity < needed_capacity) {
return vec_resize(self, needed_capacity);
}
return 0;
}
int vec_push(struct Vector* self, void* item) {
int res_ret = vec_reserve(self, 1);
if (res_ret != 0) return res_ret;
self->pages[self->length++] = item;
return 0;
}
void vec_free(struct Vector* self) {
while (self->length > 0)
free(self->pages[--self->length]);
vec_clear(self);
}
void vec_clear(struct Vector* self) {
free((void*)self->pages);
vec_reset(self);
}
void vec_reset(struct Vector* self) {
*self = (struct Vector){
.length = 0,
.capacity = 0,
.pages = NULL,
};
}
void* vec_pop(struct Vector* self) {
if (self->length == 0) return NULL;
return self->pages[--self->length];
}
void* vec_get(struct Vector* self, size_t index) {
if (index >= self->length) return NULL;
return self->pages[index];
}

64
src/util/path.c Normal file
View File

@@ -0,0 +1,64 @@
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "util/path.h"
// returns NULL on any error
// otherwise it returns the absolute path of the program that MUST BE FREED
//
// Should be almost completely posix compliant, except it won't resolve empty
// PATH entries relative to the cwd
char* NULLABLE search_path(const char* NNULLABLE for_binary) {
if (strchr(for_binary, '/') != NULL) {
// skip absolute paths
return strdup(for_binary);
}
char* path_env = getenv("PATH");
if (!path_env) return NULL;
char* path = strdup(path_env);
if (!path) return NULL;
char* tok = strtok(path, ":");
while (tok) {
char* bin_path;
asprintf(&bin_path, "%s/%s", tok, for_binary);
if (!bin_path) {
free(path);
return NULL;
}
struct stat stat_buf;
if (stat(bin_path, &stat_buf) == 0 && access(bin_path, X_OK) == 0) {
free(path);
return bin_path;
}
free(bin_path);
tok = strtok(NULL, ":");
}
free(path);
return NULL;
}
// This is present in glibc ONLY with GNU extensions, this aims to provide a
// musl compatible variant.
//
// Respects errno of exec functions family.
int execvpe(const char* NNULLABLE file, char* NULLABLE const argv[NNULLABLE],
char* NULLABLE const envp[NNULLABLE]) {
char* path = search_path(file);
if (!path) {
errno = ENOENT;
return -1;
}
int ret = execve(path, argv, envp);
free(path);
return ret;
}

62
src/util/utf8.c Normal file
View File

@@ -0,0 +1,62 @@
#include "util/utf8.h"
#define UTF8_CONT_MSK 0b11000000
#define UTF8_CONT_VAL 0b10000000
bool utf8_iscont(char byte) {
return (byte & UTF8_CONT_MSK) == UTF8_CONT_VAL;
}
size_t utf8len(const char* str) {
size_t len = 0;
while (*str != '\0') {
if (!utf8_iscont(*(str++))) len++;
}
return len;
}
size_t utf8len_until(const char* str, const char* until) {
size_t len = 0;
while (str < until) {
if (!utf8_iscont(*(str++))) len++;
}
return len;
}
size_t utf8trunc(char* str, size_t n) {
size_t bytes = 0;
while (true) {
if (str[bytes] == '\0') break;
if (utf8_iscont(str[bytes])) {
bytes++;
continue;
}
if (n == 0) {
str[bytes] = '\0';
break;
}
bytes++;
n--;
}
return bytes;
}
const char* utf8back(const char* str) {
while (utf8_iscont(*(--str))) {
}
return str;
}
const char* utf8seek(const char* str) {
while (utf8_iscont(*(++str))) {
}
return str;
}
const char* utf8seekn(const char* str, size_t n) {
while (n > 0 && *str != '\0') {
str = utf8seek(str);
n--;
}
return str;
}

96
src/util/vec.c Normal file
View File

@@ -0,0 +1,96 @@
#include <stdlib.h>
#include "util/vec.h"
const struct Vector VEC_NEW = {
.length = 0,
.capacity = 0,
.pages = NULL,
};
struct Vector vec_from_raw(void** raw) {
size_t len = 0;
while (raw[len])
len++;
return (struct Vector){
.length = len,
.capacity = len,
.pages = raw,
};
}
void** vec_as_raw(struct Vector self) {
if (vec_push(&self, NULL) != 0) return NULL;
return self.pages;
}
int vec_resize(struct Vector* self, size_t size) {
void** new_location =
(void**)realloc((void*)self->pages, size * sizeof(void*));
if (new_location != NULL) {
if (self->length > size) self->length = size;
self->capacity = size;
self->pages = new_location;
} else {
return -1;
}
return 0;
}
int vec_reserve(struct Vector* self, size_t size) {
uint32_t new_capacity = self->capacity;
while (self->length + size > new_capacity) {
new_capacity = new_capacity + (new_capacity >> 1) +
1; // cap * 1.5 + 1; 0 1 2 4 7 11...
}
return vec_resize(self, new_capacity);
}
int vec_reserve_exact(struct Vector* self, size_t size) {
uint32_t needed_capacity = self->length + size;
if (self->capacity < needed_capacity) {
return vec_resize(self, needed_capacity);
}
return 0;
}
int vec_push(struct Vector* self, void* item) {
int res_ret = vec_reserve(self, 1);
if (res_ret != 0) return res_ret;
self->pages[self->length++] = item;
return 0;
}
void vec_free(struct Vector* self) {
while (self->length > 0)
free(self->pages[--self->length]);
vec_clear(self);
}
void vec_clear(struct Vector* self) {
free((void*)self->pages);
vec_reset(self);
}
void vec_reset(struct Vector* self) {
*self = (struct Vector){
.length = 0,
.capacity = 0,
.pages = NULL,
};
}
void* vec_pop(struct Vector* self) {
if (self->length == 0) return NULL;
return self->pages[--self->length];
}
void* vec_get(struct Vector* self, size_t index) {
if (index >= self->length) return NULL;
return self->pages[index];
}