add proper support for OpenBSD specificities:

- call login_fbtab with the logged in user so that devices have the right mode/ownership
- call it again at session teardown to reset perms to root
- call setusercontext(LOGIN_SETALL) so that limits from login.conf are applied
- push the XDG_RUNTIME_DIR env var to the env passed to the child
- the LOGIN_SETALL magic number comes from the define in login_cap.h

Index: greetd/src/session/worker.rs
--- greetd/src/session/worker.rs.orig
+++ greetd/src/session/worker.rs
@@ -1,5 +1,17 @@
 use std::{env, ffi::CString, os::unix::net::UnixDatagram};
 
+use libc::{ c_int, c_char, uid_t, gid_t, c_uint, c_void, getpwnam, passwd };
+// $grep -E 'LOGIN_SET(ALL|XDGENV)' /usr/include/login_cap.h | bindgen /dev/stdin -- -x c
+const LOGIN_SETALL : u32 = 511 ;
+const LOGIN_SETXDGENV : u32 = 512 ;
+
+#[link(name = "util")]
+extern "C" {
+    fn login_fbtab(tty: *const c_char, uid: uid_t, gid: gid_t) -> c_void;
+    fn setusercontext(lc: *mut c_void, pwd: *mut passwd, uid: uid_t, flags: c_uint) -> c_int;
+}
+use std::ptr;
+
 use nix::{
     sys::wait::waitpid,
     unistd::{execve, fork, initgroups, setgid, setsid, setuid, ForkResult},
@@ -163,6 +175,11 @@ fn worker(sock: &UnixDatagram) -> Result<(), Error> {
 
     let user = nix::unistd::User::from_name(&pam_username)?.ok_or("unable to get user info")?;
 
+    // call login_fbtab with the user logging in for login.conf limits
+    let ttyc_str = CString::new("ttyC0").unwrap();
+    let ttyptr: *const c_char = ttyc_str.as_ptr() as *const c_char;
+    unsafe { login_fbtab(ttyptr, user.uid.into(), user.gid.into()) };
+
     // Make this process a session leader.
     setsid().map_err(|e| format!("unable to become session leader: {}", e))?;
 
@@ -170,7 +187,7 @@ fn worker(sock: &UnixDatagram) -> Result<(), Error> {
         TerminalMode::Stdin => (),
         TerminalMode::Terminal { path, vt, switch } => {
             // Tell PAM what TTY we're targetting, which is used by logind.
-            pam.set_item(PamItemType::TTY, &format!("tty{}", vt))?;
+            pam.set_item(PamItemType::TTY, &format!("ttyC{}", vt))?;
             pam.putenv(&format!("XDG_VTNR={}", vt))?;
 
             // Opening our target terminal.
@@ -204,6 +221,8 @@ fn worker(sock: &UnixDatagram) -> Result<(), Error> {
     // and set all environment variables later.
     let prepared_env = [
         "XDG_SEAT=seat0".to_string(),
+        "WLR_DRM_DEVICES=/dev/dri/card0".to_string(),
+        "QT_QPA_PLATFORM=wayland".to_string(),
         format!("XDG_SESSION_CLASS={}", class.as_str()),
         format!("USER={}", user.name),
         format!("LOGNAME={}", user.name),
@@ -225,7 +244,7 @@ fn worker(sock: &UnixDatagram) -> Result<(), Error> {
     _ = pam.putenv(&"XDG_SESSION_CLASS");
 
     // Prepare some strings in C format that we'll need.
-    let cusername = CString::new(user.name)?;
+    let cusername = CString::new(user.name.clone())?;
     let command = if source_profile {
         format!(
             "[ -f /etc/profile ] && . /etc/profile; [ -f $HOME/.profile ] && . $HOME/.profile; exec {}",
@@ -250,10 +269,35 @@ fn worker(sock: &UnixDatagram) -> Result<(), Error> {
             // this match arm.
 
             // Drop privileges to target user
-            initgroups(&cusername, user.gid).expect("unable to init groups");
-            setgid(user.gid).expect("unable to set GID");
-            setuid(user.uid).expect("unable to set UID");
+            // setusercontext(LOGIN_SETALL) takes care of calling initgroups/setuid/setgid
+            // setusercontext(LOGIN_SETXDGENV) creates XDG_RUNTIME_DIR
+            let unameptr: *const c_char = cusername.as_ptr() as *const c_char;
+            let pwd = unsafe { getpwnam(unameptr) };
+            if pwd.is_null() {
+                eprintln!("failed getpwnam() for user {}", user.name);
+                std::process::exit(1);
+            }
+            let ret = unsafe { setusercontext(ptr::null_mut(), pwd, user.uid.into(), LOGIN_SETALL|LOGIN_SETXDGENV) };
+            if ret != 0 {
+                eprintln!("failed setusercontext for user {}", user.name);
+                std::process::exit(1);
+            }
 
+            // shadows the one from the parent because we want to push to it
+            let mut envvec = envvec.clone();
+            // set a failsafe hardcoded value, required by various things
+            let mut xrd = format!("XDG_RUNTIME_DIR={}/.local/run", user.dir.to_string_lossy());
+            // Check whether setusercontext successfully set XDG_RUNTIME_DIR
+            if let Ok(dir) = env::var("XDG_RUNTIME_DIR") {
+                xrd = format!("XDG_RUNTIME_DIR={}", dir);
+            }
+            // Push XDG_RUNTIME_DIR to the child, we can't use '?' here.
+            let cdir = match CString::new(xrd) {
+                Ok(cdir) => cdir,
+                Err(e) => panic!("invalid XDG_RUNTIME_DIR for user {}: {}", user.name, e)
+            };
+            envvec.push(cdir.as_c_str());
+
             // Set our parent death signal. setuid/setgid above resets the
             // death signal, which is why we do this here.
             prctl(PrctlOption::SET_PDEATHSIG(libc::SIGTERM)).expect("unable to set death signal");
@@ -308,6 +352,9 @@ fn worker(sock: &UnixDatagram) -> Result<(), Error> {
     pam.close_session(PamFlag::NONE)?;
     pam.setcred(PamFlag::DELETE_CRED)?;
     pam.end()?;
+
+    // reset perms to root via fbtab (xenodm does the same)
+    unsafe { login_fbtab(ttyptr, 0, 0) };
 
     Ok(())
 }
