12 Commits

Author SHA1 Message Date
7e7a297e2e feat: support Xorg & better auth logic (#80)
Co-authored-by: grialion <48643945+grialion@users.noreply.github.com>
2026-01-19 21:17:45 +01:00
Martin Bogdanov
d65bd7a8ee feat: get sessions from /usr/local/share/{x,wayland-}sessions (#99)
Co-authored-by: javalsai <jvssxxi@gmail.com>
2026-01-19 20:35:44 +01:00
Martin Bogdanov
728611aa19 fix: systemd service install recipe & systemd service restart (#96) 2026-01-16 16:18:13 +01:00
Martin Bogdanov
4eca2b056f docs,makefile: put conventional systemd service path (#94)
`/etc/systemd` -> `/usr/local/lib/systemd`
2026-01-15 23:52:46 +01:00
creations
a7a1f42f0a feat: add support for fido yubikeys (#89)
* add support for fido keybind

* add to themes

* fix clang format

* Update ui.c

* docs: add misc stuff about the yubikey

---------

Co-authored-by: javalsai <jvssxxi@gmail.com>
2025-12-13 21:37:58 +01:00
dariuskl
feeba5c41b feat: don't trunc long hostnames if there is space (#87)
Instead of restricting the length of the hostname field to the size of
the column, the hostname is allowed to utilize all space that is not
taken up by the time string.

Co-authored-by: Darius Kellermann <kellermann@pm.me>
2025-11-16 17:29:59 +01:00
ddc38ef27f fix: stupid logic bug
https://github.com/javalsai/lidm/issues/77#issuecomment-3225642519
2025-08-26 22:33:13 +02:00
grialion
b3c8d3cbea feat: exec parser for desktop entries (#78)
Feature-rich Exec= parser implementation for .desktop files
* backslash stripping (e.g '\ ' -> ' ')
* percentage stripping (e.g '%u' -> '', '%%' -> '%')
* quote string handling (e.g 'arg1 "arg 2"' -> "arg1", "arg 2")
The current implementation strips all "percentage codes", instead
of handling them.
Argument count is limited at 100.

---------

Co-authored-by: javalsai <jvssxxi@gmail.com>
2025-08-26 21:19:25 +02:00
5761838112 redo of #69 (#75)
* fix(pkg,aur): sources now are version unique

* build: allow to override git-rev and timestamp build info

* chore: organize and parallelize some checks

* chore: make standard PKGBUIDLs

* pkg(aur): `-bin` only has to depend on the `.so` file

* pkg(feat,aur): multiarch `-bin` pkg

* ci(void): update xbps first, add warning

* pkg(aur): bump pkgrels

* pkg(aur): pkgrel again AND `.SRCINFO` finally

---------

Co-authored-by: grialion <48643945+grialion@users.noreply.github.com>
2025-08-23 00:43:33 +02:00
363deeab28 docs: update dead links 2025-07-20 22:09:28 +02:00
github-actions[bot]
98b898fc08 Update AUR pkgs to v1.2.3 (#67)
Co-authored-by: GitHub Actions <actions@github.com>
Co-authored-by: javalsai <javalsai@proton.me>
2025-07-20 22:03:51 +02:00
github-actions[bot]
6b4b64d3fc Update NixOS module to v1.2.3 (#66)
Co-authored-by: GitHub Actions <actions@github.com>
2025-07-20 22:01:56 +02:00
47 changed files with 1312 additions and 428 deletions

View File

@@ -16,15 +16,13 @@ Checks: >
readability-*,
-readability-braces-around-statements,
WarningsAsErrors: ''
HeaderFilterRegex: '.*'
WarningsAsErrors: ""
HeaderFilterRegex: ".*"
FormatStyle: file
CheckOptions:
- key: readability-magic-numbers.IgnoredIntegerValues
value: '0;1;2;3;10;255'
# - key: readability-magic-numbers.IgnoredValues
# value: '0;1;2;3;10;255'
value: "0;1;2;3;10;255"
- key: readability-identifier-naming.VariableCase
value: lower_case
- key: readability-identifier-naming.ConstantParameterCase
@@ -32,16 +30,20 @@ CheckOptions:
- key: readability-identifier-naming.ConstantCase
value: "UPPER_CASE"
- key: readability-identifier-naming.EnumCase
value: "CamelCase"
- key: readability-identifier-naming.FunctionCase
value: "lower_case"
- key: readability-identifier-length.VariableThreshold
value: '2'
value: "2"
- key: readability-identifier-length.ParameterThreshold
value: '2'
value: "2"
- key: readability-identifier-length.LocalConstantThreshold
value: '2'
value: "2"
- key: readability-identifier-length.MemberThreshold
value: '2'
value: "2"
- key: readability-identifier-length.MinimumParameterNameLength
value: '2'
value: "2"
- key: readability-identifier-length.MinimumVariableNameLength
value: '2'
value: "2"

View File

@@ -132,6 +132,7 @@ jobs:
path: lidm-riscv64
retention-days: 1
# This build job is only symbolic, the behavior of the packaged Void Linux version may differ.
build-linux-amd64-musl:
name: amd64-musl
runs-on: ubuntu-24.04
@@ -147,7 +148,7 @@ jobs:
run: |
cd /workspace
xbps-install -Sy
xbps-install -Syu xbps && xbps-install -yu
xbps-install -y git pam-devel make gcc bash git
ARCH=amd64-musl GITHUB_STEP_SUMMARY=gss.out GITHUB_OUTPUT=go.out assets/github_scripts/build.sh

View File

@@ -26,7 +26,7 @@ jobs:
with:
packages: "shellcheck"
version: 1.0
- run: find . -type f -name '*.sh' -not -path './assets/pkg/aur/*/src/*' | xargs shellcheck
- run: git ls-files "*.sh" "*/PKGBUILD" | xargs shellcheck --shell=bash
clangcheck:
name: Clang

View File

@@ -9,27 +9,29 @@ ODIR=dist
PREFIX=/usr
CC?=gcc
CFLAGS?=-O3 -Wall
CFLAGS?=-O3 -Wall -Wextra -fdata-sections -ffunction-sections
# C PreProcessor flags, not C Plus Plus
CPPFLAGS?=
ALLFLAGS=$(CFLAGS) $(CPPFLAGS) -I$(IDIR)
LDFLAGS?=-Wl,--gc-sections
LIBS=-lpam
_DEPS = version.h log.h util.h ui.h ui_state.h config.h desktop.h auth.h ofield.h efield.h keys.h users.h sessions.h chvt.h macros.h launch_state.h
_DEPS = version.h log.h util.h ui.h ui_state.h config.h pam.h desktop.h desktop_exec.h auth.h ofield.h efield.h keys.h users.h sessions.h chvt.h macros.h launch_state.h signal_handler.h
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
_OBJ = main.o log.o util.o ui.o ui_state.o config.o desktop.o auth.o ofield.o efield.o users.o sessions.o chvt.o launch_state.o
_OBJ = main.o log.o util.o ui.o ui_state.o config.o pam.o desktop.o desktop_exec.o auth.o ofield.o efield.o users.o sessions.o chvt.o launch_state.o signal_handler.o
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
.git/HEAD:
INFO_GIT_REV?=$$(git describe --long --tags --always || echo '?')
INFO_BUILD_TS?=$$(date +%s)
$(IDIR)/version.h: Makefile .git/HEAD
$(IDIR)/version.h: Makefile
@tmp=$$(mktemp); \
printf '' > $$tmp; \
echo '#define LIDM_VERSION "'$(VERSION)'"' >> $$tmp; \
echo '#define LIDM_GIT_REV "'$$(git describe --long --tags --always || echo '?')'"' >> $$tmp; \
echo '#define LIDM_BUILD_TS '$$(date +%s) >> $$tmp; \
echo '#define LIDM_GIT_REV "'$(INFO_GIT_REV)'"' >> $$tmp; \
echo '#define LIDM_BUILD_TS '$(INFO_BUILD_TS) >> $$tmp; \
if ! cmp -s $$tmp $@; then \
mv $$tmp $@; \
fi; \
@@ -40,7 +42,7 @@ $(ODIR)/%.o: $(CDIR)/%.c $(DEPS)
$(CC) -c -o $@ $< $(ALLFLAGS)
lidm: $(OBJ)
$(CC) -o $@ $^ $(ALLFLAGS) $(LIBS)
$(CC) -o $@ $^ $(ALLFLAGS) $(LIBS) $(LDFLAGS)
clean:
rm -f $(ODIR)/*.o lidm
@@ -56,7 +58,7 @@ install: lidm
uninstall:
rm -rf ${DESTDIR}${PREFIX}/bin/lidm ${DESTDIR}/etc/lidm.ini
rm -rf ${DESTDIR}/usr/share/man/man{1/lidm.1,5/lidm-config.5}.gz
rm -rf ${DESTDIR}/etc/systemd/system/lidm.service ${DESTDIR}/etc/dinit.d/lidm ${DESTDIR}/etc/runit/sv/lidm
rm -rf ${DESTDIR}/usr/local/lib/systemd/system/lidm.service ${DESTDIR}/etc/dinit.d/lidm ${DESTDIR}/etc/runit/sv/lidm
install-service:
@if command -v systemctl &> /dev/null; then \
@@ -86,7 +88,7 @@ install-service:
fi
install-service-systemd:
install -m644 ./assets/services/systemd.service ${DESTDIR}/etc/systemd/system/lidm.service
install -Dm644 ./assets/services/systemd.service ${DESTDIR}/usr/local/lib/systemd/system/lidm.service
@printf '\033[1m%s\033[0m\n\n' " don't forget to run 'systemctl enable lidm'"
install-service-dinit:
install -m644 ./assets/services/dinit ${DESTDIR}/etc/dinit.d/lidm
@@ -133,10 +135,12 @@ install-service-s6-etc:
pre-commit:
codespell
prettier --write "**/*.md"
find . -type f -name '*.sh' -not -path './assets/pkg/aur/*/src/*' | xargs shellcheck
prettier -c "**/*.md"
git ls-files "*.sh" "*/PKGBUILD" | xargs shellcheck --shell=bash
clang-format -i $$(git ls-files "*.c" "*.h")
clang-tidy -p . $$(git ls-files "*.c" "*.h")
git ls-files -z "*.c" "*.h" | \
parallel -j$$(nproc) -q0 --no-notice --will-cite --tty clang-tidy -warnings-as-errors=\* --quiet |& \
grep -v "warnings generated." || true
print-version:
@echo $(VERSION)

View File

@@ -27,10 +27,11 @@ kmscon -l --vt /dev/tty7 --font-name "Cascadia Code" -- /usr/bin/lidm
## Features
- Simple as C, you only need a C compiler and standard unix libraries to build this.
- Fully customizable, from strings, including action keys, to colors (I hope you know ansi escape codes)
- Automatically detects xorg and wayland sessions, plus allowing to launch the default user shell (if enabled in config)
- Starts with many init systems (systemd, dinit, runit, openrc and s6).
- Simple as C, meant to depend only on standard libc and basic unix system headers.
- Fully customizable: ALL strings, colors (with its ANSI keys) and most behavior.
- Experimental Xorg support[\*](https://github.com/javalsai/lidm/pull/80?#issuecomment-3764073217) and wayland sessions, while supporting the default user shell (if enabled in config)
- Init agnostinc (systemd, dinit, runit, openrc and s6).
- Supports [fido yubikeys](./docs/yubikey.md) (via pam_u2f).
# Table of Contents

View File

@@ -69,13 +69,13 @@ Characters for the corners of the box (ctl = corner top left, cbr = corner botto
.SS functions
All these are of type \fBKEY\fP.
.TP
\fBpoweroff, reboot, refresh\fP
\fBpoweroff, reboot, fido, refresh\fP
Function key to use for such action.
.SS strings
Display strings to use for some elements.
.TP
\fBf_poweroff, f_reboot, f_refresh\fP
\fBf_poweroff, f_reboot, f_fido, f_refresh\fP
Text displayed to name such functions at the bottom of the screen.
.TP
\fBe_user, e_passwd\fP

View File

@@ -1,20 +1,32 @@
pkgbase = lidm-bin
pkgdesc = A fully colorful customizable TUI display manager made in C. (release binary)
pkgver = 1.2.0
pkgrel = 1
pkgver = 1.2.3
pkgrel = 3
url = https://github.com/javalsai/lidm
arch = x86_64
arch = i686
arch = aarch64
arch = armv7h
arch = riscv64
license = GPL
depends = pam
depends = libpam.so
provides = lidm
conflicts = lidm
source = lidm::https://github.com/javalsai/lidm/releases/download/v1.2.0/lidm-amd64
source = default-theme.ini::https://raw.githubusercontent.com/javalsai/lidm/v1.2.0/themes/default.ini
source = lidm.1::https://raw.githubusercontent.com/javalsai/lidm/v1.2.0/assets/man/lidm.1
source = lidm-config.5::https://raw.githubusercontent.com/javalsai/lidm/v1.2.0/assets/man/lidm-config.5
sha256sums = 6bf4403c21bd26607302d99d7bd1a129822e7d2506e949fb483ea445e022eb6d
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,29 +1,39 @@
# shellcheck disable=SC2034,SC2148,SC2128,SC2154,SC2164
# shellcheck disable=SC2034,SC2154,SC2164
# Maintainer: javalsai <javalsai@proton.me>
pkgname=lidm-bin
pkgver=1.2.0
pkgrel=1
depends=('pam')
pkgver=1.2.3
pkgrel=3
depends=('libpam.so')
pkgdesc="A fully colorful customizable TUI display manager made in C. (release binary)"
arch=('x86_64')
arch=('x86_64' 'i686' 'aarch64' 'armv7h' 'riscv64')
url="https://github.com/javalsai/lidm"
license=('GPL')
provides=('lidm')
conflicts=('lidm')
source=(
"lidm::$url/releases/download/v$pkgver/lidm-amd64"
"default-theme.ini::https://raw.githubusercontent.com/javalsai/lidm/v$pkgver/themes/default.ini"
"lidm.1::https://raw.githubusercontent.com/javalsai/lidm/v$pkgver/assets/man/lidm.1"
"lidm-config.5::https://raw.githubusercontent.com/javalsai/lidm/v$pkgver/assets/man/lidm-config.5"
"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"
)
sha256sums=('6bf4403c21bd26607302d99d7bd1a129822e7d2506e949fb483ea445e022eb6d'
'ffaa5fe2cf5011bf53c90f81bfec8585158d35f72c0666db0bd4d3866ae041ca'
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() {
install -Dm755 lidm "${pkgdir}/usr/bin/lidm"
install -Dm644 default-theme.ini "${pkgdir}/etc/lidm.ini"
install -Dm644 lidm.1 "${pkgdir}/usr/share/man/man1/lidm.1"
install -Dm644 lidm-config.5 "${pkgdir}/usr/share/man/man5/lidm-config.5"
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,13 +1,11 @@
pkgbase = lidm-git
pkgdesc = A fully colorful customizable TUI display manager made in C. (last git commit)
pkgver = 0.1.0.r0.g8071694
pkgrel = 1
pkgver = 1.2.3.r3.g363deea
pkgrel = 3
url = https://github.com/javalsai/lidm
arch = any
license = GPL
makedepends = git
makedepends = make
makedepends = gcc
depends = pam
provides = lidm
conflicts = lidm

View File

@@ -1,10 +1,10 @@
# shellcheck disable=SC2034,SC2148,SC2128,SC2154,SC2164
# shellcheck disable=SC2034,SC2154,SC2164
# Maintainer: javalsai <javalsai@proton.me>
pkgname=lidm-git
pkgver=1.2.0.r0.g7f75b8e
pkgrel=1
pkgver=1.2.3.r3.g363deea
pkgrel=3
depends=('pam')
makedepends=('git' 'make' 'gcc')
makedepends=('git')
pkgdesc="A fully colorful customizable TUI display manager made in C. (last git commit)"
arch=('any')
url="https://github.com/javalsai/lidm"
@@ -15,17 +15,17 @@ source=("lidm::git+https://github.com/javalsai/lidm")
sha256sums=('SKIP')
pkgver() {
cd "lidm"
cd "$srcdir/lidm"
git describe --long --abbrev=7 --tags | \
sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g'
}
build() {
cd "lidm"
make CFLAGS="-O3"
cd "$srcdir/lidm"
make
}
package() {
cd "lidm"
make install DESTDIR="${pkgdir}"
cd "$srcdir/lidm"
make DESTDIR="${pkgdir}" install
}

View File

@@ -1,14 +1,12 @@
pkgbase = lidm
pkgdesc = A fully colorful customizable TUI display manager made in C. (build latest tag)
pkgver = 1.2.0
pkgrel = 1
pkgver = 1.2.3
pkgrel = 3
url = https://github.com/javalsai/lidm
arch = any
license = GPL
makedepends = git
makedepends = gcc
depends = pam
source = tarball.tar.gz::https://github.com/javalsai/lidm/archive/refs/tags/v1.2.0.tar.gz
sha256sums = 0dffded5fcef45cb45fe88358b0cba8de04f614e323a9c6e4162f84b6e3a50b6
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,25 +1,21 @@
# shellcheck disable=SC2034,SC2148,SC2128,SC2154,SC2164
# shellcheck disable=SC2034,SC2154,SC2164
# Maintainer: javalsai <javalsai@proton.me>
pkgname=lidm
pkgver=1.2.0
pkgrel=1
pkgver=1.2.3
pkgrel=3
depends=('pam')
makedepends=('git' 'gcc')
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')
source=("tarball.tar.gz::https://github.com/javalsai/lidm/archive/refs/tags/v$pkgver.tar.gz")
sha256sums=('0dffded5fcef45cb45fe88358b0cba8de04f614e323a9c6e4162f84b6e3a50b6')
source=("${pkgname}-${pkgver}.tar.gz::https://github.com/javalsai/lidm/archive/refs/tags/v$pkgver.tar.gz")
sha256sums=('1ce414b510c5bbc3e32ea882f915b4d3958cb82eb1fbb5cf33e62f69c844bf93')
build() {
tar -xzf "tarball.tar.gz"
cd "lidm-$pkgver"
make CFLAGS="-O3"
make -C "$srcdir/lidm-$pkgver"
}
package() {
cd "lidm-$pkgver"
make install DESTDIR="${pkgdir}"
make -C "$srcdir/lidm-$pkgver" DESTDIR="$pkgdir" install
}

22
assets/pkg/aur/makepkg-clean.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -e
MYSELF=$(realpath "$0")
MYDIR=$(dirname "$MYSELF")
cd "$MYDIR"
typeset -a pkgs=(lidm{,-git,-bin})
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

@@ -4,18 +4,19 @@ set -e
MYSELF=$(realpath "$0")
MYDIR=$(dirname "$MYSELF")
for pkg in "$MYDIR"/*/; do
printf "\x1b[1mEntering '%s'\x1b[0m\n" "$pkg"
cd "$MYDIR"
typeset -a pkgs=(lidm{,-git,-bin})
for pkg in "${pkgs[@]}"; do
printf "\x1b[mEntering '%s'\x1b[0m\n" "$pkg"
cd "$pkg"
# shellcheck disable=SC1091
source "PKGBUILD"
for source in "${source[@]}"; do
awk -F'::' '{print $1}' <<<"$source" | xargs rm -rf
done
rm -rf ./*.{gz,zst} src pkg
makepkg -f .
makepkg -Cf
cd ..
echo
done
if [[ -n "${PRINT_TREE:-}" ]]; then
for pkg in "${pkgs[@]}"; do
eza --tree "$pkg/pkg/"*
done
fi

View File

@@ -17,12 +17,6 @@ for pkg in "$MYDIR"/lidm{,-bin}/; do
sed -i "s/pkgver=.*/pkgver=$1/" PKGBUILD
sed -i "s/pkgrel=.*/pkgrel=1/" PKGBUILD
grep 'source = ' <.SRCINFO | awk -F'= |::' '{print $2}' | \
while read -r srcfile; do
printf "\x1b[31mDeleting '%s'\x1b[0m\n" "$srcfile"
rm -f "$srcfile"
done
updpkgsums
makepkg --printsrcinfo | tee .SRCINFO
echo

View File

@@ -11,7 +11,7 @@ let
dmcfg = config.services.displayManager;
desktops = dmcfg.sessionData.desktops;
version = "1.2.2";
version = "1.2.3";
lidmPkg = pkgs.callPackage ./lidm.nix {
inherit pkgs;
config = {
@@ -21,7 +21,7 @@ let
owner = "javalsai";
repo = "lidm";
rev = "v${version}";
sha256 = "sha256-rvxSNv/F+ksy68D7zP7NwbCBV9/6OfEZSfGxKsGiVYc=";
sha256 = "sha256-eKgBoh+bRcl2Y7oOxW77Kjtb9Ws2Xln1SenknIsGxD4=";
};
xsessions = "${desktops}/share/xsessions";

View File

@@ -13,7 +13,7 @@ The manual steps for installation are:
## Systemd
- Copy `systemd.service` to `/etc/systemd/system/lidm.service`
- Copy `systemd.service` to `/usr/local/lib/systemd/system/lidm.service` (if the directory doesn't exist, create it first)
- To enable it you can run `systemctl enable lidm`
## Dinit

View File

@@ -11,6 +11,7 @@ StandardError=tty
TTYPath=/dev/tty7
TTYReset=yes
TTYVHangup=yes
Restart=always
[Install]
Alias=display-manager.service

View File

@@ -29,7 +29,7 @@ Then you can install the files onto your filesystem with:
make install
```
And additionally, to install service files (start-up behavior). <sup>[more docs](./assets/services/README.md)</sup>
And additionally, to install service files (start-up behavior). <sup>[more docs](../assets/services/README.md)</sup>
```sh
# automatically detects your init system
@@ -55,7 +55,7 @@ make install-service-s6-etc # s6 (/etc/s6/sv)
[AUR packages](https://aur.archlinux.org/packages?K=lidm&SeB=n) will automatically install most files.
> [!CAUTION]
> [service files](./assets/pkg/aur#services) have to be manually installed by now.
> [service files](../assets/pkg/aur#services) have to be manually installed by now.
# Nix Flake
@@ -72,7 +72,7 @@ nix run github:javalsai/lidm
```
> [!CAUTION]
> This doesn't include [service files](./assets/pkg/aur#services) neither
> This doesn't include [service files](../assets/pkg/aur#services) neither
# Nix Module
@@ -125,4 +125,4 @@ 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_
> [!NOTE]
> [service files](./assets/pkg/aur#services) **are** included and enabled
> [service files](../assets/pkg/aur#services) **are** included and enabled

View File

@@ -22,10 +22,12 @@ make \
The list of possible `#define`s is:
| Name | Default | Description | Env Override? |
| ---------------------- | ------------------------------- | -------------------------------------------------------------------------- | ------------------------ |
| -------------------------- | ------------------------------------- | -------------------------------------------------------------------------- | ------------------------ |
| `PAM_SERVICE_FALLBACK` | `"login"` | Name of the default PAM module to use. Defaults to the distro's `"login"`. | Yes (`LIDM_PAM_SERVICE`) |
| `SESSIONS_XSESSIONS` | `"/usr/share/xsessions"` | | No |
| `SESSIONS_XSESSIONS_LOCAL` | `"/usr/local/share/xsessions"` | | No |
| `SESSIONS_WAYLAND` | `"/usr/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`) |
# Other Build Settings
@@ -38,6 +40,17 @@ Lidm attempts to support being built by `gcc` and `clang` with preference over t
Compiler flags should be passed with `CFLAGS`, its `-O3 -Wall` by default so adding anything will overwrite this.
## Build Metadata
`lidm -v` outputs some information about the build version, this can be weaked with `INFO_GIT_REV` and `INFO_BUILD_TS`, by default they are:
```make
INFO_GIT_REV?=$$(git describe --long --tags --always || echo '?')
INFO_BUILD_TS?=$$(date +%s)
```
But this can be changed by just passing those env variables, for example, in the case git is not applicable in the build environment of the package.
## Target Directory
`DESTDIR` can be used to for installation recipes to install on alternative root directories. Along with `PREFIX` (defaults to `/usr`) for systems which don't use the common `/usr` structure. e.g. `make install DESTDIR=$out PREFIX=`

30
docs/yubikey.md Normal file
View File

@@ -0,0 +1,30 @@
# Yubikeys
Quick reference explaining how yubikeys work for now.
# Enable
Yubikeys are disabled by default, to enable them activate a keybinding for it (`[functions] fido`) in the config file.
Note that pressing this configured keybinding has no difference from trying to log in with an empty password, there's virtually no difference.
`pam_u2f` must be configured with a registered key (`pamu2fcfg`).
# Extra
All my yubikey knowledge comes from the [pr that implemented this](https://github.com/javalsai/lidm/pull/89), please refer to it for extra details. Contributions to this documentation are welcome (explaining more in detail, potential issues... really anything that improves this).
Allegedly this pam module configuration should work:
```pam
#%PAM-1.0
auth sufficient pam_u2f.so cue
auth requisite pam_nologin.so
auth include system-local-login
account include system-local-login
session include system-local-login
password include system-local-login
```
Also, I recommend giving the [arch wiki](https://wiki.archlinux.org/title/YubiKey) a read anyways.

View File

@@ -6,7 +6,8 @@
#include "config.h"
#include "sessions.h"
bool launch(char* user, char* passwd, struct session session, void (*cb)(void),
struct config* config);
bool launch(char* NNULLABLE user, char* NNULLABLE passwd,
struct session session, void (*NULLABLE cb)(void),
struct config* NNULLABLE config);
#endif

View File

@@ -8,7 +8,7 @@
#include "macros.h"
#include "util.h"
enum introspection_type {
enum IntrospectionType {
STRING,
BOOL,
NUMBER,
@@ -26,7 +26,7 @@ static const char* NNULLABLE const INTROS_TYS_NAMES[] = {
struct introspection_item {
char* NNULLABLE name;
size_t offset;
enum introspection_type typ;
enum IntrospectionType typ;
};
#define INTROS_ITEM(key, table, ty) \
@@ -90,15 +90,17 @@ BUILD(colors, COLORS, TABLE_COLORS);
BUILD(chars, CHARS, TABLE_CHARS);
#define TABLE_FUNCTIONS(F, name) \
F(enum keys, poweroff, KEY, F1, name) \
F(enum keys, reboot, KEY, F2, name) \
F(enum keys, refresh, KEY, F5, name)
F(enum Keys, poweroff, KEY, F1, name) \
F(enum Keys, reboot, KEY, F2, name) \
F(enum Keys, fido, KEY, NONE, name) \
F(enum Keys, refresh, KEY, F5, name)
BUILD(functions, FUNCTIONS, TABLE_FUNCTIONS);
#define TABLE_STRINGS(F, name) \
F(char* NNULLABLE, f_poweroff, STRING, "poweroff", name) \
F(char* NNULLABLE, f_reboot, STRING, "reboot", name) \
F(char* NNULLABLE, f_fido, STRING, "fido", name) \
F(char* NNULLABLE, f_refresh, STRING, "refresh", name) \
F(char* NNULLABLE, e_user, STRING, "user", name) \
F(char* NNULLABLE, e_passwd, STRING, "password", name) \
@@ -150,15 +152,15 @@ struct introspection_table {
static const struct introspection_table CONFIG_INSTROSPECTION[] = {
{"colors", offsetof(struct config, colors), INTROS_TABLE_COLORS,
sizeof(INTROS_TABLE_COLORS) / sizeof(INTROS_TABLE_COLORS[0])},
LEN(INTROS_TABLE_COLORS)},
{"chars", offsetof(struct config, chars), INTROS_TABLE_CHARS,
sizeof(INTROS_TABLE_CHARS) / sizeof(INTROS_TABLE_CHARS[0])},
LEN(INTROS_TABLE_CHARS)},
{"functions", offsetof(struct config, functions), INTROS_TABLE_FUNCTIONS,
sizeof(INTROS_TABLE_FUNCTIONS) / sizeof(INTROS_TABLE_FUNCTIONS[0])},
LEN(INTROS_TABLE_FUNCTIONS)},
{"strings", offsetof(struct config, strings), INTROS_TABLE_STRINGS,
sizeof(INTROS_TABLE_STRINGS) / sizeof(INTROS_TABLE_STRINGS[0])},
LEN(INTROS_TABLE_STRINGS)},
{"behavior", offsetof(struct config, behavior), INTROS_TABLE_BEHAVIOR,
sizeof(INTROS_TABLE_BEHAVIOR) / sizeof(INTROS_TABLE_BEHAVIOR[0])},
LEN(INTROS_TABLE_BEHAVIOR)},
};
//// FUNCTIONS

15
include/desktop_exec.h Normal file
View File

@@ -0,0 +1,15 @@
// TODO: rewrite properly
// NOLINTBEGIN(clang-diagnostic-nullability-completeness)
#ifndef DESKTOP_EXEC_H_
#define DESKTOP_EXEC_H_
#include "macros.h"
char* NULLABLE search_path(const char* NNULLABLE for_binary);
int execvpe_desktop(char** args, char* NNULLABLE* NNULLABLE envlist);
int parse_exec_string(const char* exec_s, int* arg_count, char*** args);
void free_parsed_args(int arg_count, char** args);
#endif
// NOLINTEND(clang-diagnostic-nullability-completeness)

View File

@@ -3,7 +3,7 @@
#include <stdlib.h>
enum keys {
enum Keys {
ESC,
F1,
F2,
@@ -32,9 +32,11 @@ enum keys {
END,
PAGE_UP,
PAGE_DOWN,
NONE,
};
static const char* const KEY_NAMES[] = {
[NONE] = "NONE",
[ESC] = "ESC",
[F1] = "F1",
[F2] = "F2",
@@ -66,7 +68,7 @@ static const char* const KEY_NAMES[] = {
};
struct key_mapping {
enum keys key;
enum Keys key;
const char* sequences[3];
};

View File

@@ -7,25 +7,25 @@
#endif
// Do we just replace the compiler with clang??
#if defined(__clang__)
#ifdef __clang__
#define NULLABLE _Nullable
#else
#define NULLABLE
#endif
#if defined(__clang__)
#ifdef __clang__
#define NNULLABLE _Nonnull
#else
#define NNULLABLE
#endif
#if defined(__clang__)
#ifdef __clang__
#define UNULLABLE _Null_unspecified
#else
#define UNULLABLE
#endif
#if defined(__clang__)
#ifdef __clang__
#define COMPILER_VERSION __VERSION__
#elif defined(__GNUC__)
#define xstr(s) str(s)
@@ -36,5 +36,6 @@
#endif
#define LEN(X) (sizeof(X) / sizeof((X)[0]))
#define UNUSED(x) ((void)(x))
#endif

33
include/pam.h Normal file
View File

@@ -0,0 +1,33 @@
#ifndef PAM_H
#define PAM_H
#include <pwd.h>
#include <security/_pam_types.h>
#include <security/pam_appl.h>
#include <stdbool.h>
#include "macros.h"
#include "sessions.h"
#define PAMH_ERR_NOERR 0
#define PAMH_ERR_ALLOC 1
#define PAMH_ERR_ERRNO 2
#define PAMH_ERR_NOERRNO 3
struct pamh_getenv_status {
char error_flag;
union {
char* NULLABLE* NNULLABLE envlist;
const char* NNULLABLE errfn;
};
};
// Doesn't include `source`s
struct pamh_getenv_status pamh_get_complete_env(pam_handle_t* NNULLABLE handle,
struct passwd* NNULLABLE pw,
enum SessionType session_typ);
void free_envlist(char* NULLABLE* NNULLABLE envlist);
pam_handle_t* NULLABLE get_pamh(char* NNULLABLE user, char* NNULLABLE passwd);
#endif /* PAM_H */

View File

@@ -2,21 +2,71 @@
#define SESSIONSH_
#include <sys/types.h>
#include <unistd.h>
#include "desktop_exec.h"
#include "macros.h"
#include "util.h"
enum session_type {
enum SessionType {
XORG,
WAYLAND,
SHELL,
};
enum ExecType {
EXEC_SHELL,
EXEC_DESKTOP,
};
struct desktop_exec {
char* NULLABLE* NNULLABLE args;
int arg_count;
};
struct session_exec {
enum ExecType typ;
union {
char* NNULLABLE shell;
struct desktop_exec desktop;
};
};
static inline struct session_exec session_exec_shell(char* NNULLABLE shell) {
return (struct session_exec){
.typ = EXEC_SHELL,
.shell = shell,
};
}
static inline struct session_exec session_exec_desktop(
int arg_count, char* NULLABLE* NNULLABLE args) {
return (struct session_exec){
.typ = EXEC_DESKTOP,
.desktop =
{
.args = args,
.arg_count = arg_count,
},
};
}
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 {
char* NNULLABLE name;
char* NNULLABLE exec;
char* NULLABLE tryexec;
enum session_type type;
struct session_exec exec;
enum SessionType type;
};
struct Vector get_avaliable_sessions();

8
include/signal_handler.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef SIGNALHANDLERH_
#define SIGNALHANDLERH_
// handle SIGTERM by sending SIGTERM to all children, resulting
// in a graceful graphical shutdown
void setup_sigterm();
#endif /* SIGNALHANDLERH_ */

View File

@@ -39,15 +39,17 @@
#define VALUES_SEPR 3
#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;
void setup(struct config* config);
int load(struct Vector* users, struct Vector* sessions);
void print_err(const char* /*msg*/);
void print_errno(const char* /*descr*/);
void print_pam_msg(const char* msg, int msg_style);
void clear_pam_msg(void);
void ui_update_field(enum input focused_input);
void ui_update_field(enum Input focused_input);
void ui_update_ffield();
void ui_update_ofield(struct opts_field* self);
void ui_update_cursor_focus();

View File

@@ -6,7 +6,7 @@
#include "macros.h"
#include "ui.h"
extern enum input focused_input;
extern enum Input focused_input;
extern struct opts_field of_session;
extern struct opts_field of_user;
@@ -15,7 +15,7 @@ extern struct opts_field of_passwd;
extern struct Vector* UNULLABLE gusers;
extern struct Vector* UNULLABLE gsessions;
struct opts_field* NNULLABLE get_opts_field(enum input from);
struct opts_field* NNULLABLE get_opts_field(enum Input from);
struct opts_field* NNULLABLE get_opts_ffield();
struct user st_user();

View File

@@ -10,12 +10,18 @@
#include "keys.h"
int find_keyname(enum keys* at, const char* name);
enum keys find_ansi(const char* seq);
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);
@@ -24,12 +30,16 @@ 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);

View File

@@ -1,83 +1,38 @@
// TODO: handle `fork() == -1`// TODO: handle `fork() == -1`s
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <security/pam_misc.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include "auth.h"
#include "config.h"
#include "log.h"
#include "macros.h"
#include "pam.h"
#include "sessions.h"
#include "ui.h"
#include "unistd.h"
#include "util.h"
int pam_conversation(int num_msg, const struct pam_message** msg,
struct pam_response** resp, void* appdata_ptr) {
struct pam_response* reply =
(struct pam_response*)malloc(sizeof(struct pam_response) * num_msg);
if (!reply) {
return PAM_BUF_ERR;
#define XORG_MESSAGE_LENGTH 16
static void try_source_file(struct Vector* NNULLABLE vec_envlist,
char* NNULLABLE filepath) {
log_printf("sourcing %s\n", filepath);
FILE* file2source = fopen(filepath, "r");
if (file2source == NULL) {
log_printf("error sourcing %s\n", filepath);
return;
}
for (size_t i = 0; i < num_msg; i++) {
reply[i].resp = NULL;
reply[i].resp_retcode = 0;
if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF ||
msg[i]->msg_style == PAM_PROMPT_ECHO_ON) {
char* input = (char*)appdata_ptr;
reply[i].resp = strdup(input);
}
}
*resp = reply;
return PAM_SUCCESS;
}
#ifndef PAM_SERVICE_FALLBACK
#define PAM_SERVICE_FALLBACK "login"
#endif
#define CHECK_PAM_RET(call) \
ret = (call); \
if (ret != PAM_SUCCESS) { \
pam_end(pamh, ret); \
return NULL; \
}
void clear_screen() {
printf("\x1b[H\x1b[J");
}
pam_handle_t* get_pamh(char* user, char* passwd) {
pam_handle_t* pamh = NULL;
struct pam_conv pamc = {pam_conversation, (void*)passwd};
int ret;
char* pam_service_override = getenv("LIDM_PAM_SERVICE");
char* pam_service_name =
pam_service_override ? pam_service_override : PAM_SERVICE_FALLBACK;
CHECK_PAM_RET(pam_start(pam_service_name, user, &pamc, &pamh))
CHECK_PAM_RET(pam_authenticate(pamh, 0))
CHECK_PAM_RET(pam_acct_mgmt(pamh, 0))
CHECK_PAM_RET(pam_setcred(pamh, PAM_ESTABLISH_CRED))
CHECK_PAM_RET(pam_open_session(pamh, 0))
CHECK_PAM_RET(pam_setcred(pamh, PAM_REINITIALIZE_CRED))
return pamh;
}
#undef CHECK_PAM_RET
void* shmalloc(size_t size) {
return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,
-1, 0);
}
void sourceFileTry(char* file) {
FILE* file2source = fopen(file, "r");
if (file2source == NULL) return;
char* line = NULL;
size_t len = 0;
@@ -87,13 +42,10 @@ void sourceFileTry(char* file) {
if (read == 0 || (read > 0 && *line == '#')) continue;
if (line[read - 1] == '\n') line[read - 1] = '\0';
/* printf("Retrieved line of length %zu:\n", read); */
/* printf("%s\n", line); */
for (size_t i = 1; i < read; i++) {
for (ssize_t i = 1; i < read; i++) {
if (line[i] == '=') {
/* printf("FOUND '='!\n"); */
line[i] = '\0';
setenv(line, &line[i + 1], 1);
vec_push(vec_envlist, (void*)line);
line = NULL;
break;
}
}
@@ -103,53 +55,29 @@ void sourceFileTry(char* file) {
(void)fclose(file2source);
}
void moarEnv(char* user, struct session session, struct passwd* pw,
struct config* config) {
if (chdir(pw->pw_dir) == -1) print_errno("can't chdir to user home");
setenv("HOME", pw->pw_dir, true);
setenv("USER", pw->pw_name, true);
setenv("SHELL", pw->pw_shell, true);
// TERM
setenv("LOGNAME", pw->pw_name, true);
// MAIL?
// PATH?
char* xdg_session_type = "unknown";
if (session.type == SHELL) xdg_session_type = "tty";
if (session.type == XORG) xdg_session_type = "x11";
if (session.type == WAYLAND) xdg_session_type = "wayland";
setenv("XDG_SESSION_TYPE", xdg_session_type, true);
printf("\n\n\n\n\x1b[1m");
for (size_t i = 0; i < config->behavior.source.length; i++) {
/* printf("DEBUG(source)!!!! %d %s\n", i, (char*)vec_get(&behavior->source,
* i)); */
sourceFileTry((char*)vec_get(&config->behavior.source, i));
static void source_paths(struct Vector* NNULLABLE vec_envlist,
struct Vector* NNULLABLE abs_source,
const char* NULLABLE user_home,
struct Vector* NNULLABLE user_source) {
for (size_t i = 0; i < abs_source->length; i++) {
char* path = vec_get(abs_source, i);
try_source_file(vec_envlist, path);
}
/* printf("\n"); */
if (pw->pw_dir) {
const char* home = pw->pw_dir;
size_t home_len = strlen(home);
for (size_t i = 0; i < config->behavior.user_source.length; i++) {
const char* filename = (char*)vec_get(&config->behavior.user_source, i);
size_t filename_len = strlen(filename);
size_t path_len = home_len + 1 + filename_len + 1; // nullbyte and slash
char* path = malloc(path_len);
if (!path) continue; // can't bother
memcpy(path, home, home_len);
path[home_len] = '/'; // assume pw_dir doesn't start with '/' :P
memcpy(&path[home_len + 1], filename, filename_len);
path[path_len - 1] = '\0';
sourceFileTry(path);
if (user_home)
for (size_t i = 0; i < user_source->length; i++) {
char* path = NULL;
asprintf(&path, "%s/%s", user_home, (char*)vec_get(user_source, i));
if (!path) {
log_puts("alloc failure on user source\n");
continue;
}
try_source_file(vec_envlist, path);
free(path);
}
else {
log_puts("user has no home\n");
}
}
/*char *buf;*/
@@ -161,7 +89,197 @@ void moarEnv(char* user, struct session session, struct passwd* pw,
/*setenv("XDG_SESSION_ID", "1", true);*/
/*setenv("XDG_SESSION_DESKTOP", , true);*/
/*setenv("XDG_SEAT", "seat0", true);*/
struct child_msg {
char* msg;
int _errno;
bool err;
};
/// block until X returns the display number or an error occurs
static bool x_get_display(const int xorg_pipefd[2], int* display) {
char buffer[XORG_MESSAGE_LENGTH];
bool status;
close(xorg_pipefd[1]);
ssize_t bytes_read = read(xorg_pipefd[0], buffer, sizeof(buffer) - 1);
buffer[bytes_read] = '\0';
if (bytes_read > 0) {
char* endptr;
int val = (int)strtol(buffer, &endptr, 10);
if (endptr == buffer) {
(void)fputs("failed to parse Xorg display response\n", stderr);
status = false;
} else {
*display = val;
status = true;
}
} else if (bytes_read == 0) {
(void)fputs("Xorg pipe closed\n", stderr);
status = false;
} else {
perror("read");
status = false;
}
close(xorg_pipefd[0]);
return status;
}
/// small helper to push dyn arr
static void push_dyn_arr(void*** arr, void* item) {
struct Vector vec = vec_from_raw(*arr);
vec_push(&vec, item);
*arr = vec_as_raw(vec);
}
// TODO: properly pass this down
extern int vt;
static void start_xorg_server(struct passwd* pw, char** NNULLABLE envlist,
int xorg_pipefd[2]) {
close(xorg_pipefd[0]);
if (!pw->pw_dir) _exit(EXIT_FAILURE);
// !!!!!!!!!! ATTENTION: this fails silently, of course add failure msgs but
// for now I can't so be careful
if (vt == -1) _exit(EXIT_FAILURE);
// pass the pipe so Xorg can write the DISPLAY value in there
char* fd_str;
asprintf(&fd_str, "%d", xorg_pipefd[1]);
if (!fd_str) _exit(EXIT_FAILURE);
char* vt_path;
asprintf(&vt_path, "vt%d", vt);
if (!vt_path) {
free(fd_str);
_exit(EXIT_FAILURE);
}
char* xorg_path = search_path("Xorg");
if (!xorg_path) {
(void)fputs("couldn't find Xorg binary in PATH, sure it's installed?\n",
stderr);
_exit(EXIT_FAILURE);
}
int exit = execle(xorg_path, xorg_path, "-displayfd", fd_str, vt_path, NULL,
envlist);
perror("exec");
free(vt_path);
free(fd_str);
free(xorg_path);
_exit(exit);
}
// TODO: add error msgs
/// returns on completion
static void launch_with_xorg_server(struct session_exec* NNULLABLE exec,
struct passwd* pw,
char** NNULLABLE envlist) {
int xorg_pipefd[2];
if (pipe(xorg_pipefd) == -1) _exit(EXIT_FAILURE);
(void)fflush(NULL);
pid_t xorg_pid = fork();
if (xorg_pid == 0) {
start_xorg_server(pw, envlist, xorg_pipefd);
}
int display = 0;
if (!x_get_display(xorg_pipefd, &display)) {
(void)fputs("failed to get X display, aborting\n", stderr);
int status;
waitpid(xorg_pid, &status, 0);
_exit(EXIT_FAILURE);
}
char* display_env;
asprintf(&display_env, "DISPLAY=:%d", display);
if (!display_env) {
(void)fputs("failure allocating memory for DISPLAY string\n", stderr);
_exit(EXIT_FAILURE);
}
// convert back for convenient push-ing
push_dyn_arr((void***)&envlist, display_env);
if (!envlist) {
(void)fputs("failure allocating memory for DISPLAY env\n", stderr);
_exit(EXIT_FAILURE);
}
(void)fflush(NULL);
pid_t xorg_session_pid = fork();
if (xorg_session_pid == 0) {
int exit = session_exec_exec(exec, envlist);
perror("exec error");
(void)fputs("failure calling session\n", stderr);
_exit(exit);
}
// looks weird, waiting on -1 should wait on any child and then just check if
// its xorg server or the session and kill the other waiting on it
pid_t pid;
int status; // not even read for now
while ((pid = waitpid(-1, &status, 0)) > 0) {
if (pid == xorg_pid || pid == xorg_session_pid) {
pid_t pid_to_kill = pid ^ xorg_pid ^ xorg_session_pid;
if (pid == xorg_pid) printf("Xorg server died\n");
if (pid == xorg_session_pid) printf("Xorg session died\n");
kill(pid_to_kill, SIGTERM);
waitpid(pid_to_kill, &status, 0);
break;
}
}
}
#define SEND_MSG(MSG) \
{ \
write(pipefd[1], &(MSG), sizeof(struct child_msg)); \
close(pipefd[1]); \
}
#define SEND_ERR(MSG) \
{ \
write(pipefd[1], \
&(struct child_msg){.msg = (MSG), ._errno = errno, .err = true}, \
sizeof(struct child_msg)); \
close(pipefd[1]); \
_exit(EXIT_FAILURE); \
}
#define DUMMY_READ() \
{ \
char _dummy; \
read(pipefd[0], &_dummy, sizeof(_dummy)); \
}
inline static void forked(int pipefd[2], struct passwd* pw,
char* NNULLABLE user,
struct session* NNULLABLE session,
char** NNULLABLE envlist) {
if (chdir(pw->pw_dir) == -1) SEND_ERR("chdir");
if (setgid(pw->pw_gid) == -1) SEND_ERR("setgid");
if (initgroups(user, pw->pw_gid) == -1) SEND_ERR("initgroups");
if (setuid(pw->pw_uid) == -1) SEND_ERR("setuid");
SEND_MSG((struct child_msg){.err = false});
DUMMY_READ();
close(pipefd[0]);
if (session->type == XORG) {
launch_with_xorg_server(&session->exec, pw, envlist);
_exit(EXIT_SUCCESS);
} else {
int exit = session_exec_exec(&session->exec, envlist);
perror("exec error");
(void)fputs("failure calling session\n", stderr);
_exit(exit);
}
}
#undef SEND_MSG
#undef SEND_ERR
#undef DUMMY_READ
// NOLINTBEGIN(readability-function-cognitive-complexity)
bool launch(char* user, char* passwd, struct session session, void (*cb)(void),
@@ -174,93 +292,63 @@ bool launch(char* user, char* passwd, struct session session, void (*cb)(void),
pam_handle_t* pamh = get_pamh(user, passwd);
if (pamh == NULL) {
print_err("error on pam authentication");
print_pam_msg("authentication failed", PAM_ERROR_MSG);
return false;
}
clear_pam_msg();
struct pamh_getenv_status env_ret =
pamh_get_complete_env(pamh, pw, session.type);
if (env_ret.error_flag != PAMH_ERR_NOERR) {
if (env_ret.error_flag == PAMH_ERR_ALLOC) {
print_err("allocator error");
} else if (env_ret.error_flag == PAMH_ERR_ERRNO) {
print_errno(env_ret.errfn);
} else if (env_ret.error_flag == PAMH_ERR_NOERRNO) {
print_err(env_ret.errfn);
}
return false;
}
bool* reach_session = shmalloc(sizeof(bool));
if (reach_session == NULL) {
perror("error allocating shared memory");
struct Vector envlist_vec = vec_from_raw((void**)env_ret.envlist);
source_paths(&envlist_vec, &config->behavior.source, pw->pw_dir,
&config->behavior.user_source);
char** envlist = (char**)vec_as_raw(envlist_vec);
if (!envlist) {
print_err("vec alloc error");
return false;
}
*reach_session = false;
int pipefd[2];
pipe(pipefd);
uint pid = fork();
if (pid == 0) { // child
char* term = NULL;
char* getterm = getenv("TERM");
// TODO: handle malloc error
if (getterm != NULL) term = strdup(getterm);
if (clearenv() != 0) {
print_errno("clearenv");
_exit(EXIT_FAILURE);
if (pid == 0)
forked(pipefd, pw, user, &session, envlist);
else {
struct child_msg msg;
read(pipefd[0], &msg, sizeof(struct child_msg));
close(pipefd[0]);
if (msg.err) {
errno = msg._errno;
print_errno(msg.msg);
return false;
}
char** envlist = pam_getenvlist(pamh);
if (envlist == NULL) {
print_errno("pam_getenvlist");
_exit(EXIT_FAILURE);
}
for (size_t i = 0; envlist[i] != NULL; i++) {
putenv(envlist[i]);
}
// FIXME: path hotfix
putenv("PATH=/bin:/usr/bin");
if (term != NULL) {
setenv("TERM", term, true);
free(term);
}
free((void*)envlist);
moarEnv(user, session, pw, config);
// TODO: chown stdin to user
// does it inherit stdin from parent and
// does parent need to reclaim it after
// this dies?
if (setgid(pw->pw_gid) == -1) {
print_errno("setgid");
_exit(EXIT_FAILURE);
}
if (initgroups(user, pw->pw_gid) == -1) {
print_errno("initgroups");
_exit(EXIT_FAILURE);
}
if (setuid(pw->pw_uid) == -1) {
perror("setuid");
_exit(EXIT_FAILURE);
}
if (cb != NULL) cb();
*reach_session = true;
// TODO: these will be different due to TryExec
// and, Exec/TryExec might contain spaces as args
printf("\x1b[0m");
// NOLINTNEXTLINE(bugprone-branch-clone)
if (session.type == SHELL) {
clear_screen();
if (cb) cb();
printf("\x1b[0m\x1b[H\x1b[J");
(void)fflush(stdout);
execlp(session.exec, session.exec, NULL);
} else if (session.type == XORG || session.type == WAYLAND) {
clear_screen();
(void)fflush(stdout);
execlp(session.exec, session.exec, NULL);
}
perror("execl error");
(void)fputs("failure calling session\n", stderr);
} else {
pid_t child_pid = (pid_t)pid;
waitpid(child_pid, NULL, 0);
close(pipefd[1]);
int exit_code;
waitpid((pid_t)pid, &exit_code, 0);
pam_setcred(pamh, PAM_DELETE_CRED);
pam_close_session(pamh, 0);
pam_end(pamh, PAM_SUCCESS);
if (*reach_session == false) return false;
if (exit_code != 0) return false;
exit(0);
}

View File

@@ -4,6 +4,7 @@
#include <stdlib.h>
#include "chvt.h"
#include "macros.h"
static char* vterms[] = {"/dev/tty", "/dev/tty0", "/dev/vc/0", "/dev/systty",
"/dev/console"};
@@ -23,16 +24,16 @@ int chvt_str(char* str) {
return chvt((int)i);
}
int vt = -1;
int chvt(int n) {
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
(void)fprintf(stderr, "activating vt %d\n", n);
vt = n;
// NOLINTNEXTLINE(readability-identifier-length)
char c = 0;
for (size_t i = 0; i < sizeof(vterms) / sizeof(vterms[0]); i++) {
for (size_t i = 0; i < LEN(vterms); i++) {
int fd = open(vterms[i], O_RDWR);
if (fd >= 0 && isatty(fd) && ioctl(fd, KDGKBTYPE, &c) == 0 && c < 3) {
if (ioctl(fd, VT_ACTIVATE, n) < 0 || ioctl(fd, VT_WAITACTIVE, n) < 0) {
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
(void)fprintf(stderr, "Couldn't activate vt %d\n", n);
return -1;
}

View File

@@ -117,12 +117,12 @@ union typ_ptr {
char** string;
long long* number;
bool* boolean;
enum keys* key;
enum Keys* key;
struct Vector* vec;
uintptr_t ptr;
};
struct parser_error parse_key(enum introspection_type typ, union typ_ptr at,
char* key, size_t offset) {
struct parser_error parse_key(enum IntrospectionType typ, union typ_ptr at,
char* key) {
char* aux_str = NULL;
struct parser_error aux_err;
@@ -219,7 +219,7 @@ struct status config_line_handler(void* _config, char* table, char* k,
log_printf("[I] parsing [%s.%s] as %s\n", table, k,
INTROS_TYS_NAMES[this_intros_key->typ]);
struct parser_error err = parse_key(this_intros_key->typ, k_addr, v, offset);
struct parser_error err = parse_key(this_intros_key->typ, k_addr, v);
if (err.msg != NULL) {
log_printf("[E] cfg parser, failed to parse [%s.%s] (%s): %s\n", table, k,
INTROS_TYS_NAMES[this_intros_key->typ], err.msg);

View File

@@ -1,4 +1,3 @@
// NOLINTBEGIN(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,readability-function-cognitive-complexity)
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -20,6 +19,7 @@ char* trim_str(char* str) {
return str;
}
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
int read_desktop(FILE* fd, void* ctx,
struct status (*cb)(void* ctx, char* table, char* key,
char* value)) {
@@ -82,4 +82,3 @@ int read_desktop(FILE* fd, void* ctx,
if (buf != NULL) free(buf);
return ret;
}
// NOLINTEND(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,readability-function-cognitive-complexity)

242
src/desktop_exec.c Normal file
View File

@@ -0,0 +1,242 @@
// TODO: rewrite properly
// NOLINTBEGIN(clang-diagnostic-nullability-completeness)
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "desktop_exec.h"
#include "macros.h"
// constants for exec string parsing
#define MAX_ARGS 100
// ARG_LENGTH is the initial length of a parsed argument
#define ARG_LENGTH 64
// returns NULL on any error
// otherwise it returns the absolute path of the program that MUST BE FREED
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) {
// TODO: check exec bit ig
// if(stat_buf.) {}
free(path);
return bin_path;
}
free(bin_path);
tok = strtok(NULL, ":");
}
free(path);
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
void free_parsed_args(int arg_count, char** args) {
if (!args) return;
for (int i = 0; i < arg_count; i++) {
free(args[i]);
}
free((void*)args);
}
/* small closure-like struct to pass state to helper functions */
struct ctx {
char** pcur;
size_t* pcur_len;
size_t* pcur_cap;
char*** pargv;
int* pargc;
};
/* append_char(state, ch) -> 0 on error, 1 on success */
int append_char(struct ctx* st, char ch) {
char** pcur = st->pcur;
size_t* plen = st->pcur_len;
size_t* pcap = st->pcur_cap;
if (*plen + 1 >= *pcap) {
size_t newcap = *pcap ? (*pcap) * 2 : ARG_LENGTH;
char* cur = (char*)realloc(*pcur, newcap);
if (!cur) return 0;
*pcur = cur;
*pcap = newcap;
}
(*pcur)[(*plen)++] = ch;
return 1;
}
/* push_arg(state) -> 0 on error, 1 on success */
int push_arg(struct ctx* st) {
char** pcur = st->pcur;
size_t* plen = st->pcur_len;
size_t* pcap = st->pcur_cap;
char*** pargv = st->pargv;
int* pargc = st->pargc;
if (*pargc > MAX_ARGS) {
return 1;
}
if (!*pcur) {
char* empty = strdup("");
if (!empty) return 0;
char** na = (char**)realloc((void*)*pargv, sizeof(char*) * ((*pargc) + 1));
if (!na) {
free(empty);
return 0;
}
*pargv = na;
(*pargv)[(*pargc)++] = empty;
return 1;
}
if (!append_char(st, '\0')) return 0;
char* final = (char*)realloc(*pcur, *plen);
if (!final) final = *pcur;
*pcur = NULL;
*plen = 0;
*pcap = 0;
char** na = (char**)realloc((void*)*pargv, sizeof(char*) * ((*pargc) + 1));
if (!na) {
free(final);
return 0;
}
*pargv = na;
(*pargv)[(*pargc)++] = final;
return 1;
}
/* Return codes:
0 = success
1 = bad args
2 = memory
3 = syntax
Important: call free_parsed_args afterwards to free the passed ***args
*/
// NOLINTBEGIN(readability-function-cognitive-complexity)
int parse_exec_string(const char* exec_s, int* arg_count, char*** args) {
if (!exec_s || !args || !arg_count) return 1;
*args = NULL;
*arg_count = 0;
size_t len = strlen(exec_s);
size_t idx = 0;
char* cur = NULL;
size_t cur_len = 0;
size_t cur_cap = 0;
char** argv = NULL;
int argc = 0;
int in_quote = 0;
struct ctx ctx;
ctx.pcur = &cur;
ctx.pcur_len = &cur_len;
ctx.pcur_cap = &cur_cap;
ctx.pargv = &argv;
ctx.pargc = &argc;
while (idx < len) {
char cur_c = exec_s[idx];
if (!in_quote && (cur_c == ' ' || cur_c == '\t' || cur_c == '\n')) {
if (cur_cap) {
if (!push_arg(&ctx)) goto nomem;
}
idx++;
continue;
}
if (!in_quote && cur_c == '"') {
in_quote = 1;
idx++;
continue;
}
if (in_quote && cur_c == '"') {
in_quote = 0;
idx++;
continue;
}
if (cur_c == '\\') {
if (idx + 1 >= len) goto syntax_err;
if (!append_char(&ctx, exec_s[idx + 1])) goto nomem;
idx += 2;
continue;
}
if (cur_c == '%') {
if (idx + 1 >= len) goto syntax_err;
if (exec_s[idx + 1] == '%') {
if (!append_char(&ctx, '%')) goto nomem;
idx += 2;
continue;
}
/* drop any %X */
idx += 2;
continue;
}
if (!append_char(&ctx, cur_c)) goto nomem;
idx++;
}
if (in_quote) goto syntax_err;
if (cur_cap) {
if (!push_arg(&ctx)) goto nomem;
}
char** na = (char**)realloc((void*)argv, sizeof(char*) * (argc + 1));
if (!na) goto nomem;
argv = na;
argv[argc] = NULL;
*args = argv;
*arg_count = argc;
return 0;
nomem:
if (cur) free(cur);
free_parsed_args(argc, argv);
*args = NULL;
*arg_count = 0;
return 2;
syntax_err:
if (cur) free(cur);
free_parsed_args(argc, argv);
*args = NULL;
*arg_count = 0;
return 3;
}
// NOLINTEND(readability-function-cognitive-complexity)
// NOLINTEND(clang-diagnostic-nullability-completeness)

View File

@@ -4,8 +4,6 @@
#include "ui.h"
#include "util.h"
// NOLINTBEGIN(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
struct editable_field efield_new(char* content) {
struct editable_field efield;
if (content != NULL) {
@@ -80,5 +78,3 @@ bool efield_seek(struct editable_field* self, char seek) {
self->pos = (u_char)(ptr - self->content);
return ptr != start;
}
// NOLINTEND(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)

View File

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

View File

@@ -12,12 +12,14 @@
#include "log.h"
#include "macros.h"
#include "sessions.h"
#include "signal_handler.h"
#include "ui.h"
#include "users.h"
#include "util.h"
#include "version.h"
#define DATESTR_MAXBUFSIZE 0x20
int main(int argc, char* argv[]) {
// Logger
char* log_output = getenv("LIDM_LOG");
@@ -85,6 +87,8 @@ int main(int argc, char* argv[]) {
struct Vector users = get_human_users();
struct Vector sessions = get_avaliable_sessions();
setup_sigterm();
int ret = load(&users, &sessions);
if (ret == 0) execl(argv[0], argv[0], NULL);
}

192
src/pam.c Normal file
View File

@@ -0,0 +1,192 @@
#include <pwd.h>
#include <security/_pam_types.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "macros.h"
#include "pam.h"
#include "sessions.h"
#include "ui.h"
struct envpair {
const char* NNULLABLE name;
char* NULLABLE value;
};
char* NULLABLE make_env_kv(const char* NNULLABLE key, char* NNULLABLE value) {
char* buf = NULL;
asprintf(&buf, "%s=%s", key, value);
return buf;
}
void free_envlist(char** NNULLABLE envlist) {
for (char** ptr = envlist; *ptr; ptr++)
free(*ptr);
free((void*)envlist);
}
// NULL when allocation failure
// in any case, envlist would be freed after this function
char** NULLABLE merge_envlist(char** NNULLABLE envlist, struct envpair extra[],
size_t extra_len) {
size_t envlist_len = 0;
while (envlist[envlist_len])
envlist_len++;
size_t nonnullelems = 0;
for (size_t i = 0; i < extra_len; i++) {
if (extra[i].value) nonnullelems++;
}
size_t new_envlist_len = envlist_len + nonnullelems + 1;
char** new_envlist =
(char**)realloc((void*)envlist, sizeof(char*) * new_envlist_len);
if (!new_envlist) {
free_envlist(envlist);
return NULL;
}
// NOLINTNEXTLINE(readability-identifier-length)
size_t k = 0;
for (size_t i = 0; i < extra_len; i++) {
if (!extra[i].value) continue;
char* env_kv = make_env_kv(extra[i].name, extra[i].value);
if (!env_kv) goto free_new_envlist_extra;
new_envlist[envlist_len + k++] = env_kv;
}
new_envlist[envlist_len + nonnullelems] = NULL;
return new_envlist;
free_new_envlist_extra:
for (size_t j = 0; j < envlist_len + k; j++) {
free(new_envlist[envlist_len + j]);
}
free((void*)new_envlist);
return NULL;
}
char* NULLABLE xdg_ssession_type_str(enum SessionType typ) {
char* xdg_session_type = NULL;
if (typ == SHELL) xdg_session_type = "tty";
if (typ == XORG) xdg_session_type = "x11";
if (typ == WAYLAND) xdg_session_type = "wayland";
return xdg_session_type;
}
#define FAIL_ALLOC() \
{ \
return (struct pamh_getenv_status){.error_flag = PAMH_ERR_ALLOC}; \
}
#define FAIL(ERR, ERRFN) \
{ \
return (struct pamh_getenv_status){.error_flag = (ERR), .errfn = (ERRFN)}; \
}
struct pamh_getenv_status pamh_get_complete_env(pam_handle_t* handle,
struct passwd* NNULLABLE pw,
enum SessionType session_typ) {
char** raw_envlist = pam_getenvlist(handle);
if (!raw_envlist) FAIL(PAMH_ERR_ERRNO, "pam_getenvlist");
struct envpair extra_env[] = {
{"TERM", getenv("TERM")},
{"PATH", getenv("PATH")},
{"HOME", pw->pw_dir},
{"USER", pw->pw_name},
{"SHELL", pw->pw_shell},
{"LOGNAME", pw->pw_name},
{"XDG_SESSION_TYPE", xdg_ssession_type_str(session_typ)}};
char** envlist = merge_envlist(raw_envlist, extra_env, LEN(extra_env));
if (!envlist) FAIL_ALLOC();
return (struct pamh_getenv_status){
.error_flag = PAMH_ERR_NOERR,
.envlist = envlist,
};
}
#undef FAIL
#undef FAIL_ALLOC
///////////////
struct pam_conv_data {
char* password;
void (*display_pam_msg)(const char* msg, int msg_style);
};
int pam_conversation(int num_msg, const struct pam_message** msg,
struct pam_response** resp, void* appdata_ptr) {
struct pam_response* reply = malloc(sizeof(struct pam_response) * num_msg);
if (!reply) {
return PAM_BUF_ERR;
}
struct pam_conv_data* conv_data = (struct pam_conv_data*)appdata_ptr;
for (int i = 0; i < num_msg; i++) {
reply[i].resp = NULL;
reply[i].resp_retcode = 0;
switch (msg[i]->msg_style) {
case PAM_PROMPT_ECHO_OFF:
case PAM_PROMPT_ECHO_ON:
reply[i].resp = strdup(conv_data->password);
if (!reply[i].resp) {
for (int j = 0; j < i; j++)
free(reply[j].resp);
free(reply);
return PAM_BUF_ERR;
}
break;
case PAM_TEXT_INFO:
case PAM_ERROR_MSG:
if (conv_data->display_pam_msg && msg[i]->msg) {
conv_data->display_pam_msg(msg[i]->msg, msg[i]->msg_style);
}
break;
default:
break;
}
}
*resp = reply;
return PAM_SUCCESS;
}
#ifndef PAM_SERVICE_FALLBACK
#define PAM_SERVICE_FALLBACK "login"
#endif
#define CHECK_PAM_RET(call) \
ret = (call); \
if (ret != PAM_SUCCESS) { \
pam_end(pamh, ret); \
return NULL; \
}
pam_handle_t* get_pamh(char* user, char* passwd) {
pam_handle_t* pamh = NULL;
struct pam_conv_data conv_data = {.password = passwd,
.display_pam_msg = print_pam_msg};
struct pam_conv pamc = {pam_conversation, (void*)&conv_data};
int ret;
char* pam_service_override = getenv("LIDM_PAM_SERVICE");
char* pam_service_name =
pam_service_override ? pam_service_override : PAM_SERVICE_FALLBACK;
CHECK_PAM_RET(pam_start(pam_service_name, user, &pamc, &pamh))
CHECK_PAM_RET(pam_authenticate(pamh, 0))
CHECK_PAM_RET(pam_acct_mgmt(pamh, 0))
CHECK_PAM_RET(pam_setcred(pamh, PAM_ESTABLISH_CRED))
CHECK_PAM_RET(pam_open_session(pamh, 0))
CHECK_PAM_RET(pam_setcred(pamh, PAM_REINITIALIZE_CRED))
return pamh;
}
#undef CHECK_PAM_RET

View File

@@ -8,25 +8,33 @@
#include <sys/types.h>
#include "desktop.h"
#include "desktop_exec.h"
#include "log.h"
#include "macros.h"
#include "sessions.h"
#include "util.h"
struct source_dir {
enum session_type type;
enum SessionType type;
char* dir;
};
#ifndef SESSIONS_XSESSIONS
#define SESSIONS_XSESSIONS "/usr/share/xsessions"
#endif
#ifndef SESSIONS_XSESSIONS_LOCAL
#define SESSIONS_XSESSIONS_LOCAL "/usr/local/share/xsessions"
#endif
#ifndef SESSIONS_WAYLAND
#define SESSIONS_WAYLAND "/usr/share/wayland-sessions"
#endif
static const struct source_dir SOURCES[] = {
{XORG, SESSIONS_XSESSIONS},
#ifndef SESSIONS_WAYLAND_LOCAL
#define SESSIONS_WAYLAND_LOCAL "/usr/local/share/wayland-sessions"
#endif
static const struct source_dir SOURCES[] = {{XORG, SESSIONS_XSESSIONS},
{XORG, SESSIONS_XSESSIONS_LOCAL},
{WAYLAND, SESSIONS_WAYLAND},
};
{WAYLAND, SESSIONS_WAYLAND_LOCAL}};
static struct Vector* cb_sessions = NULL;
@@ -72,9 +80,11 @@ struct status cb(void* _ctx, char* NULLABLE table, char* key, char* value) {
// also, always return 0 or we will break parsing and we don't want a bad
// desktop file to break all possible sessions
static enum session_type session_type;
static enum SessionType session_type;
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
static int fn(const char* fpath, const struct stat* sb, int typeflag) {
UNUSED(sb);
// guessing symlink behavior
// - FTW_PHYS if set doesn't follow symlinks, so ftw() has no flags and it
// follows symlinks, we should never get to handle that
@@ -95,22 +105,33 @@ static int fn(const char* fpath, const struct stat* sb, int typeflag) {
}
int ret = read_desktop(fd, &ctx, &cb);
if (ret < 0) { // any error
log_printf("[E] format error parsing %s", fpath);
return 0;
}
// any error
if (ret < 0) goto err_close;
(void)fclose(fd);
// TODO: filter based on tryexec
// https://specifications.freedesktop.org/desktop-entry-spec/latest/recognized-keys.html
free(ctx.tryexec);
// just add this to the list
if (ctx.name != NULL && ctx.exec != NULL) {
struct session* this_session = malloc(sizeof(struct session));
if (this_session == NULL) return 0;
int arg_count;
char** args;
int parse_status = parse_exec_string(ctx.exec, &arg_count, &args);
if (parse_status != 0 || arg_count == 0 || !args[0]) {
log_printf("[E] parsing exec string '%s': %d\n", ctx.exec, parse_status);
free(this_session);
goto err_parsing;
}
free(ctx.exec);
*this_session = (struct session){
.name = ctx.name,
.exec = ctx.exec,
.tryexec = ctx.tryexec,
.exec = session_exec_desktop(arg_count, args),
.type = session_type,
};
@@ -118,6 +139,12 @@ static int fn(const char* fpath, const struct stat* sb, int typeflag) {
}
return 0;
err_close:
(void)fclose(fd);
err_parsing:
log_printf("[E] format error parsing %s", fpath);
return 0;
}
// This code is designed to be run purely single threaded
@@ -127,7 +154,7 @@ struct Vector get_avaliable_sessions() {
vec_reserve(&sessions, LIKELY_BOUND_SESSIONS);
cb_sessions = &sessions;
for (size_t i = 0; i < (sizeof(SOURCES) / sizeof(SOURCES[0])); i++) {
for (size_t i = 0; i < LEN(SOURCES); i++) {
log_printf("[I] parsing into %s\n", SOURCES[i].dir);
session_type = SOURCES[i].type;
ftw(SOURCES[i].dir, &fn, 1);

29
src/signal_handler.c Normal file
View File

@@ -0,0 +1,29 @@
#include <errno.h>
#include <stddef.h>
#include <sys/wait.h>
#include <unistd.h>
#include "signal_handler.h"
static void handle_sigterm(int sig) {
(void)sig;
(void)signal(SIGTERM, SIG_IGN);
kill(-getpgrp(), SIGTERM);
int status;
while (waitpid(-1, &status, 0) > 0 || errno == EINTR) {
}
_exit(0);
}
void setup_sigterm() {
setpgid(0, 0);
struct sigaction sa;
sa.sa_handler = handle_sigterm;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGTERM, &sa, NULL);
}

163
src/ui.c
View File

@@ -5,6 +5,7 @@
#include <errno.h>
#include <limits.h>
#include <pwd.h>
#include <security/pam_appl.h>
#include <signal.h>
#include <stdbool.h>
#include <stddef.h>
@@ -25,6 +26,7 @@
#include "keys.h"
#include "launch_state.h"
#include "log.h"
#include "macros.h"
#include "ofield.h"
#include "sessions.h"
#include "ui.h"
@@ -61,9 +63,15 @@ struct config* g_config = NULL;
static volatile sig_atomic_t need_resize = 0;
static void process_sigwinch(int signal) {
UNUSED(signal);
need_resize = 1;
}
inline void draw_bg() {
// apply bg color to all screen
printf("\x1b[%sm\x1b[2J", g_config->colors.bg);
}
void setup(struct config* config) {
g_config = config;
@@ -73,10 +81,9 @@ void setup(struct config* config) {
term.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDOUT_FILENO, TCSANOW, &term);
// save cursor pos, save screen, set color and reset screen
// (applying color to all screen)
printf("\x1b[s\x1b[?47h\x1b[%s;%sm\x1b[2J", g_config->colors.bg,
g_config->colors.fg);
// save cursor pos, save screen
printf("\x1b[s\x1b[?47h");
draw_bg();
(void)atexit(restore_all);
(void)signal(SIGINT, signal_handler);
@@ -109,8 +116,8 @@ static char* fmt_time(const char* fmt) {
}
}
char* trunc_gethostname(const size_t MAXLEN, const char* const ELLIPSIS) {
if (utf8len(ELLIPSIS) > MAXLEN) return NULL;
char* trunc_gethostname(size_t maxlen, const char* const ELLIPSIS) {
if (utf8len(ELLIPSIS) > maxlen) return NULL;
size_t alloc_size = HOST_NAME_MAX + strlen(ELLIPSIS) + 1;
char* buf = malloc(alloc_size);
if (!buf) return NULL;
@@ -120,8 +127,8 @@ char* trunc_gethostname(const size_t MAXLEN, const char* const ELLIPSIS) {
return NULL;
}
if (utf8len(buf) > MAXLEN) {
size_t end = utf8trunc(buf, MAXLEN - utf8len(ELLIPSIS));
if (utf8len(buf) > maxlen) {
size_t end = utf8trunc(buf, maxlen - utf8len(ELLIPSIS));
strcpy(&buf[end], ELLIPSIS);
}
return buf;
@@ -151,7 +158,7 @@ void ui_update_cursor_focus() {
(void)printf("\x1b[%d;%dH", line, col);
}
void ui_update_field(enum input focused_input) {
void ui_update_field(enum Input focused_input) {
if (focused_input == PASSWD) {
print_passwd(utf8len(of_passwd.efield.content), false);
} else if (focused_input == SESSION) {
@@ -170,7 +177,7 @@ void ui_update_ffield() {
}
void ui_update_ofield(struct opts_field* NNULLABLE self) {
enum input input;
enum Input input;
if (self == &of_session)
input = SESSION;
else if (self == &of_user)
@@ -183,6 +190,7 @@ void ui_update_ofield(struct opts_field* NNULLABLE self) {
ui_update_field(input);
}
/// draw everything
void scratch_print_ui() {
ioctl(STDOUT_FILENO, TIOCGWINSZ, &window);
box_start = (struct uint_point){
@@ -190,15 +198,16 @@ void scratch_print_ui() {
.y = ((window.ws_row - BOX_HEIGHT) / 2), // leave more space under
};
if (window.ws_row < BOX_HEIGHT + INNER_BOX_OUT_MARGIN * 2 ||
window.ws_col < BOX_WIDTH + INNER_BOX_OUT_MARGIN * 2) {
if (window.ws_row < BOX_HEIGHT + (INNER_BOX_OUT_MARGIN * 2) ||
window.ws_col < BOX_WIDTH + (INNER_BOX_OUT_MARGIN * 2)) {
printf("\033[2J\033[H"); // Clear screen
printf("\x1b[1;31mScreen too small\x1b[0m\n");
printf("\x1b[%s;%sm\x1b[2J", g_config->colors.bg, g_config->colors.fg);
return;
}
printf("\033[2J\033[H"); // Clear screen
printf("\033[2J\033[H\033c"); // Clear screen
draw_bg();
/// PRINTING
// printf box
@@ -280,28 +289,46 @@ int load(struct Vector* users, struct Vector* sessions) {
(void)fflush(stdout);
if (!read_press_nb(&len, seq, &tv)) continue;
if (*seq == '\x1b') {
enum keys ansi_code = find_ansi(seq);
if (ansi_code != -1) {
if (ansi_code == ESC) {
struct option_keys ansi_code = find_ansi(seq);
if (ansi_code.is_some) {
enum Keys ansi_key = ansi_code.key;
if (ansi_key == ESC) {
esc = 2;
} else if (ansi_code == g_config->functions.refresh) {
} else if (ansi_key == g_config->functions.refresh) {
restore_all();
return 0;
} else if (ansi_code == g_config->functions.reboot) {
} else if (ansi_key == g_config->functions.reboot) {
restore_all();
reboot(RB_AUTOBOOT);
exit(0);
} else if (ansi_code == g_config->functions.poweroff) {
} else if (ansi_key == g_config->functions.poweroff) {
restore_all();
reboot(RB_POWER_OFF);
exit(0);
} else if (ansi_code == A_UP || ansi_code == A_DOWN) {
st_ch_focus(ansi_code == A_DOWN ? 1 : -1);
} else if (ansi_code == A_RIGHT || ansi_code == A_LEFT) {
} else if (g_config->functions.fido != NONE &&
ansi_key == g_config->functions.fido) {
bool successful_write = write_launch_state((struct LaunchState){
.username = st_user().username,
.session_opt =
st_session(g_config->behavior.include_defshell).name,
});
if (!successful_write) log_puts("[E] failed to write launch state");
if (!launch(st_user().username, "",
st_session(g_config->behavior.include_defshell),
&restore_all, g_config)) {
print_passwd(utf8len(of_passwd.efield.content), true);
ui_update_cursor_focus();
} else {
scratch_print_ui();
}
} else if (ansi_key == A_UP || ansi_key == A_DOWN) {
st_ch_focus(ansi_key == A_DOWN ? 1 : -1);
} else if (ansi_key == A_RIGHT || ansi_key == A_LEFT) {
if (esc)
st_ch_of_opts(ansi_code == A_RIGHT ? 1 : -1);
st_ch_of_opts(ansi_key == A_RIGHT ? 1 : -1);
else
st_ch_ef_col(ansi_code == A_RIGHT ? 1 : -1);
st_ch_ef_col(ansi_key == A_RIGHT ? 1 : -1);
}
}
} else {
@@ -317,6 +344,8 @@ int load(struct Vector* users, struct Vector* sessions) {
&restore_all, g_config)) {
print_passwd(utf8len(of_passwd.efield.content), true);
ui_update_cursor_focus();
} else {
scratch_print_ui();
}
} else
st_kbd_type(seq, g_config->behavior.include_defshell);
@@ -341,25 +370,40 @@ u_char get_render_pos_offset(struct opts_field* self, u_char maxlen) {
return pos - ofield_display_cursor_col(self, maxlen);
}
#define HOSTNAME_SIZE (VALUES_COL - VALUES_SEPR - BOX_HMARGIN - 1)
void print_head() {
// hostname doesn't just change on runtime
static char* hostname = NULL;
if (!hostname)
hostname = trunc_gethostname(HOSTNAME_SIZE, g_config->strings.ellipsis);
if (!hostname) hostname = "unknown";
char* fmtd_time = fmt_time(g_config->behavior.timefmt);
size_t len_tm = utf8len(fmtd_time);
// calculate the space available for the host name
ssize_t hostname_size = BOX_WIDTH - (BOX_HMARGIN * 2) - len_tm - VALUES_SEPR;
if (hostname_size < 0) hostname_size = 0;
// hostname doesn't just change on runtime,
// but the length of the time string might
static char* NULLABLE hostname = NULL;
static ssize_t hostname_calcd_size;
// save the truncated hostname and the length it truncated to,
// if said length changes recalculate this (and free previous str)
if (!hostname || hostname_calcd_size != hostname_size) {
if (hostname) free(hostname);
hostname = trunc_gethostname(hostname_size, g_config->strings.ellipsis);
hostname_calcd_size = hostname_size;
}
clean_line(box_start, HEAD_ROW);
// put hostname
printf("\x1b[%dG\x1b[%sm%s\x1b[%sm",
box_start.x + VALUES_COL - VALUES_SEPR - (uint)utf8len(hostname),
g_config->colors.e_hostname, hostname, g_config->colors.fg);
if (hostname_size)
printf("\x1b[%dG\x1b[%sm%s\x1b[%sm", box_start.x + 1 + BOX_HMARGIN,
g_config->colors.e_hostname, hostname ? hostname : "unknown",
g_config->colors.fg);
// put date
char* fmtd_time = fmt_time(g_config->behavior.timefmt);
printf("\x1b[%dG\x1b[%sm%s\x1b[%sm",
box_start.x + BOX_WIDTH - 1 - BOX_HMARGIN - (uint)utf8len(fmtd_time),
box_start.x + BOX_WIDTH - 1 - BOX_HMARGIN - (uint)len_tm,
g_config->colors.e_date, fmtd_time, g_config->colors.fg);
free(fmtd_time);
}
@@ -494,6 +538,8 @@ static void print_box() {
}
static void print_footer() {
bool fido_enabled = g_config->functions.fido != NONE;
size_t bsize = utf8len(g_config->strings.f_poweroff) +
utf8len(KEY_NAMES[g_config->functions.poweroff]) +
utf8len(g_config->strings.f_reboot) +
@@ -501,19 +547,31 @@ static void print_footer() {
utf8len(g_config->strings.f_refresh) +
utf8len(KEY_NAMES[g_config->functions.refresh]);
bsize += 2 * 2 + // 2 wide separators between 3 items
3 * 1; // 3 thin separators inside every item
bsize += (2 * 2) + (3 * 1);
if (fido_enabled) {
bsize += utf8len(g_config->strings.f_fido) +
utf8len(KEY_NAMES[g_config->functions.fido]) + 2 + 1;
}
uint row = window.ws_row - 1;
uint col = window.ws_col - 2 - bsize;
printf(
"\x1b[%3$d;%4$dH%8$s \x1b[%1$sm%5$s\x1b[%2$sm %9$s "
"\x1b[%1$sm%6$s\x1b[%2$sm %10$s \x1b[%1$sm%7$s\x1b[%2$sm",
g_config->colors.e_key, g_config->colors.fg, row, col,
KEY_NAMES[g_config->functions.poweroff],
KEY_NAMES[g_config->functions.reboot],
KEY_NAMES[g_config->functions.refresh], g_config->strings.f_poweroff,
g_config->strings.f_reboot, g_config->strings.f_refresh);
printf("\x1b[%d;%dH%s \x1b[%sm%s\x1b[%sm %s \x1b[%sm%s\x1b[%sm ", row, col,
g_config->strings.f_poweroff, g_config->colors.e_key,
KEY_NAMES[g_config->functions.poweroff], g_config->colors.fg,
g_config->strings.f_reboot, g_config->colors.e_key,
KEY_NAMES[g_config->functions.reboot], g_config->colors.fg);
if (fido_enabled) {
printf("%s \x1b[%sm%s\x1b[%sm ", g_config->strings.f_fido,
g_config->colors.e_key, KEY_NAMES[g_config->functions.fido],
g_config->colors.fg);
}
printf("%s \x1b[%sm%s\x1b[%sm", g_config->strings.f_refresh,
g_config->colors.e_key, KEY_NAMES[g_config->functions.refresh],
g_config->colors.fg);
}
void print_err(const char* msg) {
@@ -521,6 +579,21 @@ void print_err(const char* msg) {
msg, errno, strerror(errno));
}
void print_pam_msg(const char* msg, int msg_style) {
uint row = box_start.y + BOX_HEIGHT + 1;
const char* color =
(msg_style == PAM_ERROR_MSG) ? g_config->colors.err : g_config->colors.fg;
printf("\x1b[%d;%dH\x1b[K\x1b[%sm%.*s\x1b[%sm", row, box_start.x, color,
BOX_WIDTH, msg, g_config->colors.fg);
(void)fflush(stdout);
}
void clear_pam_msg(void) {
uint row = box_start.y + BOX_HEIGHT + 1;
printf("\x1b[%d;%dH\x1b[K", row, box_start.x);
(void)fflush(stdout);
}
void print_errno(const char* descr) {
if (descr == NULL)
(void)fprintf(stderr, "\x1b[%d;%dH\x1b[%smunknown error(%d): %s",

View File

@@ -4,7 +4,7 @@
#include "ui.h"
#include "users.h"
enum input focused_input = PASSWD;
enum Input focused_input = PASSWD;
struct Vector* gusers;
struct Vector* gsessions;
@@ -13,7 +13,7 @@ struct opts_field of_session;
struct opts_field of_user;
struct opts_field of_passwd;
struct opts_field* NNULLABLE get_opts_field(enum input from) {
struct opts_field* NNULLABLE get_opts_field(enum Input from) {
if (from == SESSION) return &of_session;
if (from == USER) return &of_user;
if (from == PASSWD) return &of_passwd;
@@ -40,7 +40,8 @@ struct session st_session(bool include_defshell) {
if (include_defshell && of_session.current_opt == gsessions->length + 1) {
struct session shell_session;
shell_session.type = SHELL;
shell_session.exec = shell_session.name = st_user().shell;
shell_session.exec =
session_exec_shell(shell_session.name = st_user().shell);
return shell_session;
}
@@ -49,7 +50,8 @@ struct session st_session(bool include_defshell) {
struct session custom_session;
custom_session.type = SHELL;
custom_session.name = custom_session.exec = of_session.efield.content;
custom_session.exec =
session_exec_shell(custom_session.name = of_session.efield.content);
return custom_session;
}
@@ -86,7 +88,7 @@ void st_kbd_type(char* text, bool cfg_include_defshell) {
start = st_user().username;
if (focused_input == SESSION && of_session.current_opt != 0 &&
st_session(cfg_include_defshell).type == SHELL)
start = st_session(cfg_include_defshell).exec;
start = st_session(cfg_include_defshell).exec.shell;
ofield_kbd_type(field, text, start);
ui_update_ffield();

View File

@@ -13,10 +13,10 @@
static int selret_magic();
int find_keyname(enum keys* at, const char* name) {
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;
*at = (enum Keys)i;
return 0;
}
}
@@ -24,16 +24,19 @@ int find_keyname(enum keys* at, const char* name) {
return -1;
}
enum keys find_ansi(const char* seq) {
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 (enum keys)i;
return (struct option_keys){
.is_some = true,
.key = (enum Keys)i,
};
}
}
}
return -1;
return (struct option_keys){.is_some = false};
}
void read_press(u_char* length, char* out) {
@@ -147,6 +150,23 @@ const struct Vector VEC_NEW = {
.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*));

View File

@@ -25,11 +25,13 @@
[functions]
# poweroff = F1
# reboot = F2
# fido = NONE
# refresh = F5
[strings]
# f_poweroff = "poweroff"
# f_reboot = "reboot"
# f_fido = "fido"
# f_refresh = "refresh"
# e_user = "user"
# e_passwd = "password"