The diff below lets me use "vt = 5" in /etc/greetd/config.toml so that
greetd automatically switches to a free VT (ttyC4, like X11).  Since
libseat itself doesn't implement VT switching, you won't be able to
switch VTs in your wlroots-based compositor.  Still, that should be a
step in the right direction.

Restarting greetd with rcctl doesn't seem to be reliable, with or
without the diff below.

Please make sure you update your config file before testing, to use
"vty = 5" or whatever floats your boat.  "vty = 0" only worked because
the vt number was actually unused and ttyC0 was hardcoded.

Thoughts?


Index: Makefile
===================================================================
RCS file: /cvs/ports/wayland/greetd/Makefile,v
diff -u -p -r1.5 Makefile
--- Makefile    2 Jan 2026 06:43:51 -0000       1.5
+++ Makefile    2 Jan 2026 09:16:43 -0000
@@ -1,7 +1,7 @@
 COMMENT =      wayland login manager daemon
 
 DIST_TUPLE =   srht kennylevinsen greetd 0.10.3 .
-REVISION =     3
+REVISION =     4
 
 CATEGORIES =   wayland
 
Index: patches/patch-config_toml
===================================================================
RCS file: /cvs/ports/wayland/greetd/patches/patch-config_toml,v
diff -u -p -r1.1.1.1 patch-config_toml
--- patches/patch-config_toml   12 Nov 2025 18:43:03 -0000      1.1.1.1
+++ patches/patch-config_toml   2 Jan 2026 12:19:55 -0000
@@ -1,13 +1,16 @@
 Index: config.toml
 --- config.toml.orig
 +++ config.toml
-@@ -1,16 +1,25 @@
+@@ -1,16 +1,28 @@
  [terminal]
  # The VT to run the greeter on. Can be "next", "current" or a number
  # designating the VT.
 -vt = 1
-+# XXX hardcoded to 0 for now on OpenBSD
-+vt = 0
++# XXX On OpenBSD ttyC0 corresponds to vt 1
++# XXX On OpenBSD "next" should work, but "current" is likely broken
++# By default the first tty with no getty(8) should be ttyC4,
++# which maps to vt 5
++vt = 5
  
 +[general]
 +# use /etc/pam.d/system
Index: patches/patch-greetd_src_server_rs
===================================================================
RCS file: /cvs/ports/wayland/greetd/patches/patch-greetd_src_server_rs,v
diff -u -p -r1.1.1.1 patch-greetd_src_server_rs
--- patches/patch-greetd_src_server_rs  12 Nov 2025 18:43:03 -0000      1.1.1.1
+++ patches/patch-greetd_src_server_rs  2 Jan 2026 12:24:30 -0000
@@ -1,5 +1,9 @@
 use /dev/ttyC everywhere
 
+"vt <num>" and "next" should work, but "current" is probably broken.
+
+Fix path to socket, we don't have /run
+
 Index: greetd/src/server.rs
 --- greetd/src/server.rs.orig
 +++ greetd/src/server.rs
@@ -17,7 +21,7 @@ Index: greetd/src/server.rs
                  // We don't have a usable terminal, so we have to jump 
through some hoops
                  _ => {
 -                    let sys_term = Terminal::open("/dev/tty0")
-+                    let sys_term = Terminal::open("/dev/ttyC0")
++                    let sys_term = Terminal::open("/dev/tty")
                          .map_err(|e| format!("unable to open terminal: {}", 
e))?;
                      let vt = sys_term
                          .vt_get_current()
@@ -28,11 +32,12 @@ Index: greetd/src/server.rs
                          vt,
                          switch: false,
                      }
-@@ -148,20 +148,20 @@ fn get_tty(config: &Config) -> Result<TerminalMode, Er
+@@ -148,20 +148,21 @@ fn get_tty(config: &Config) -> Result<TerminalMode, Er
              }
          }
          VtSelection::Next => {
 -            let term = Terminal::open("/dev/tty0")
++            // hardcoding ttyC0 is ok here, we have no equivalent to Linux 
tty0
 +            let term = Terminal::open("/dev/ttyC0")
                  .map_err(|e| format!("unable to open terminal: {}", e))?;
              let vt = term
@@ -40,7 +45,7 @@ Index: greetd/src/server.rs
                  .map_err(|e| format!("unable to get next VT: {}", e))?;
              TerminalMode::Terminal {
 -                path: format!("/dev/tty{}", vt),
-+                path: format!("/dev/ttyC{}", vt),
++                path: format!("/dev/ttyC{}", vt - 1),
                  vt,
                  switch: config.file.terminal.switch,
              }
@@ -48,11 +53,11 @@ Index: greetd/src/server.rs
          VtSelection::None => TerminalMode::Stdin,
          VtSelection::Specific(vt) => TerminalMode::Terminal {
 -            path: format!("/dev/tty{}", vt),
-+            path: format!("/dev/ttyC{}", vt),
++            path: format!("/dev/ttyC{}", vt - 1),
              vt,
              switch: config.file.terminal.switch,
          },
-@@ -175,7 +175,7 @@ struct Listener(UnixListener);
+@@ -175,7 +176,7 @@ struct Listener(UnixListener);
  
  impl Listener {
      fn create(uid: Uid, gid: Gid) -> Result<(String, Listener), Error> {
Index: patches/patch-greetd_src_session_worker_rs
===================================================================
RCS file: /cvs/ports/wayland/greetd/patches/patch-greetd_src_session_worker_rs,v
diff -u -p -r1.3 patch-greetd_src_session_worker_rs
--- patches/patch-greetd_src_session_worker_rs  24 Nov 2025 13:15:12 -0000      
1.3
+++ patches/patch-greetd_src_session_worker_rs  2 Jan 2026 12:16:44 -0000
@@ -27,11 +27,13 @@ Index: greetd/src/session/worker.rs
  use nix::{
      sys::wait::waitpid,
      unistd::{execve, fork, initgroups, setgid, setsid, setuid, ForkResult},
-@@ -163,6 +175,11 @@ fn worker(sock: &UnixDatagram) -> Result<(), Error> {
+@@ -163,6 +175,13 @@ 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
++    // hardcode ttyC0 here, by default fbtab has no ttyC4/etc entry
++    // one may wonder how xenodm somehow ends up with the correct value
 +    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()) };
@@ -39,16 +41,16 @@ Index: greetd/src/session/worker.rs
      // 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> {
+@@ -170,7 +189,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.set_item(PamItemType::TTY, &format!("ttyC{}", vt))?; // FIXME 
On OpenBSD ttyC0 == vt 1
              pam.putenv(&format!("XDG_VTNR={}", vt))?;
  
              // Opening our target terminal.
-@@ -204,6 +221,8 @@ fn worker(sock: &UnixDatagram) -> Result<(), Error> {
+@@ -204,6 +223,8 @@ fn worker(sock: &UnixDatagram) -> Result<(), Error> {
      // and set all environment variables later.
      let prepared_env = [
          "XDG_SEAT=seat0".to_string(),
@@ -57,7 +59,7 @@ Index: greetd/src/session/worker.rs
          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> {
+@@ -225,7 +246,7 @@ fn worker(sock: &UnixDatagram) -> Result<(), Error> {
      _ = pam.putenv(&"XDG_SESSION_CLASS");
  
      // Prepare some strings in C format that we'll need.
@@ -66,7 +68,7 @@ Index: greetd/src/session/worker.rs
      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> {
+@@ -250,10 +271,35 @@ fn worker(sock: &UnixDatagram) -> Result<(), Error> {
              // this match arm.
  
              // Drop privileges to target user
@@ -105,7 +107,7 @@ Index: greetd/src/session/worker.rs
              // 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> {
+@@ -308,6 +354,9 @@ fn worker(sock: &UnixDatagram) -> Result<(), Error> {
      pam.close_session(PamFlag::NONE)?;
      pam.setcred(PamFlag::DELETE_CRED)?;
      pam.end()?;
Index: patches/patch-greetd_src_terminal_ioctl_rs
===================================================================
RCS file: patches/patch-greetd_src_terminal_ioctl_rs
diff -N patches/patch-greetd_src_terminal_ioctl_rs
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ patches/patch-greetd_src_terminal_ioctl_rs  2 Jan 2026 12:42:39 -0000
@@ -0,0 +1,46 @@
+Replace Linux ioctl values (except TIOCSCTTY) with hardcoded OpenBSD
+ioctl values.
+
+Use TIOCSCTTY from libc crate, which actually knows about OpenBSD. This
+lets agreety and other child sessions run with a proper controlling
+terminal.
+
+Index: greetd/src/terminal/ioctl.rs
+--- greetd/src/terminal/ioctl.rs.orig
++++ greetd/src/terminal/ioctl.rs
+@@ -1,16 +1,15 @@
+ use nix::{ioctl_read_bad, ioctl_write_int_bad, ioctl_write_ptr_bad};
+ 
+-pub const KDSETMODE: u16 = 0x4B3A;
+-pub const KDTEXT: i32 = 0x00;
+-pub const KDGRAPHICS: i32 = 0x01;
+-pub const VT_OPENQRY: u16 = 0x5600;
+-pub const VT_SETMODE: u16 = 0x5602;
+-pub const VT_GETSTATE: u16 = 0x5603;
+-pub const VT_ACTIVATE: u16 = 0x5606;
+-pub const VT_WAITACTIVE: u16 = 0x5607;
+-pub const VT_SETACTIVATE: u16 = 0x560F;
+-pub const VT_AUTO: u8 = 0;
+-pub const TIOCSCTTY: u16 = 0x540E;
++pub const KDSETMODE: u32 = 0x20004b0a;
++pub const KDTEXT: i32 = 0x00; // compatible
++pub const KDGRAPHICS: i32 = 0x01; // compatible
++pub const VT_OPENQRY: u32 = 0x40047601;
++pub const VT_SETMODE: u32 = 0x80087602;
++pub const VT_GETSTATE: u32 = 0x40067664;
++pub const VT_ACTIVATE: u32 = 0x20007605;
++pub const VT_WAITACTIVE: u32 = 0x20007606;
++pub const VT_SETACTIVATE: u32 = 0x40047607;
++pub const VT_AUTO: u8 = 0; // compatible
+ 
+ ioctl_write_int_bad!(kd_setmode, KDSETMODE);
+ ioctl_write_int_bad!(vt_activate, VT_ACTIVATE);
+@@ -19,7 +18,7 @@ ioctl_write_ptr_bad!(vt_setmode, VT_SETMODE, vt_mode);
+ ioctl_write_ptr_bad!(vt_setactivate, VT_SETACTIVATE, vt_setactivate);
+ ioctl_read_bad!(vt_openqry, VT_OPENQRY, i64);
+ ioctl_read_bad!(vt_getstate, VT_GETSTATE, vt_state);
+-ioctl_write_int_bad!(term_tiocsctty, TIOCSCTTY);
++ioctl_write_int_bad!(term_tiocsctty, libc::TIOCSCTTY);
+ 
+ #[allow(dead_code)]
+ #[repr(C)]
Index: patches/patch-greetd_src_terminal_mod_rs
===================================================================
RCS file: patches/patch-greetd_src_terminal_mod_rs
diff -N patches/patch-greetd_src_terminal_mod_rs
--- patches/patch-greetd_src_terminal_mod_rs    2 Jan 2026 06:43:51 -0000       
1.3
+++ /dev/null   1 Jan 1970 00:00:00 -0000
@@ -1,138 +0,0 @@
-neuter all ioctls/vt switches, no support yet in wayland
-
-(eg it needs more than just what's in wsconsctl display.focus)
-Index: greetd/src/terminal/mod.rs
---- greetd/src/terminal/mod.rs.orig
-+++ greetd/src/terminal/mod.rs
-@@ -90,51 +90,22 @@ impl Terminal {
-     /// disables the kernel console on this VT, and also disables blanking
-     /// between VT switches if both source and target VT is in graphics mode.
-     pub fn kd_setmode(&self, mode: KdMode) -> Result<(), Error> {
--        let mode = mode.to_const();
--        let ret = unsafe { ioctl::kd_setmode(self.fd, mode) };
--
--        if let Err(v) = ret {
--            Err(format!("terminal: unable to set kernel display mode: {}", 
v).into())
--        } else {
--            Ok(())
--        }
-+        Ok(())
-     }
- 
-     /// Switches to the specified VT and waits for completion of switch.
-     fn vt_activate(&self, target_vt: usize) -> Result<(), Error> {
--        if let Err(v) = unsafe { ioctl::vt_activate(self.fd, target_vt as 
i32) } {
--            return Err(format!("terminal: unable to activate: {}", v).into());
--        }
--        if let Err(v) = unsafe { ioctl::vt_waitactive(self.fd, target_vt as 
i32) } {
--            return Err(format!("terminal: unable to wait for activation: {}", 
v).into());
--        }
-         Ok(())
-     }
- 
-     /// Waits for specified VT to become active.
-     pub fn vt_waitactive(&self, target_vt: usize) -> Result<(), Error> {
--        if let Err(v) = unsafe { ioctl::vt_waitactive(self.fd, target_vt as 
i32) } {
--            return Err(format!("terminal: unable to wait for activation: {}", 
v).into());
--        }
-         Ok(())
-     }
- 
-     /// Set the VT mode to VT_AUTO with everything cleared.
-     fn vt_mode_clean(&self) -> Result<(), Error> {
--        let mode = ioctl::vt_mode {
--            mode: ioctl::VT_AUTO,
--            waitv: 0,
--            relsig: 0,
--            acqsig: 0,
--            frsig: 0,
--        };
--        let res = unsafe { ioctl::vt_setmode(self.fd, &mode) };
--
--        if let Err(v) = res {
--            Err(format!("terminal: unable to set vt mode: {}", v).into())
--        } else {
--            Ok(())
--        }
-+        Ok(())
-     }
- 
-     /// Set a VT mode, switch to the VT and wait for its activation. On Linux,
-@@ -143,62 +114,19 @@ impl Terminal {
-     /// VT_SETMODE followed by VT_ACTIVATE is used. For all platforms,
-     /// VT_WAITACTIVE is used to wait for shell activation.
-     pub fn vt_setactivate(&self, target_vt: usize) -> Result<(), Error> {
--        if cfg!(target_os = "linux") {
--            let arg = ioctl::vt_setactivate {
--                console: target_vt as u64,
--                mode: ioctl::vt_mode {
--                    mode: ioctl::VT_AUTO,
--                    waitv: 0,
--                    relsig: 0,
--                    acqsig: 0,
--                    frsig: 0,
--                },
--            };
--            if let Err(v) = unsafe { ioctl::vt_setactivate(self.fd, &arg) } {
--                return Err(format!("terminal: unable to setactivate: {}", 
v).into());
--            }
--            if let Err(v) = unsafe { ioctl::vt_waitactive(self.fd, target_vt 
as i32) } {
--                return Err(format!("terminal: unable to wait for activation: 
{}", v).into());
--            }
--        } else {
--            self.vt_mode_clean()?;
--            self.vt_activate(target_vt)?;
--        }
-         Ok(())
-     }
- 
-     /// Retrieves the current VT number.
-     pub fn vt_get_current(&self) -> Result<usize, Error> {
--        let mut state = ioctl::vt_state {
--            v_active: 0,
--            v_signal: 0,
--            v_state: 0,
--        };
--        let res = unsafe { ioctl::vt_getstate(self.fd, &mut state as *mut 
ioctl::vt_state) };
--
--        if let Err(v) = res {
--            Err(format!("terminal: unable to get current vt: {}", v).into())
--        } else if state.v_active < 1 {
--            Err(format!("terminal: current vt invalid: {}", 
state.v_active).into())
--        } else {
--            Ok(state.v_active as usize)
--        }
-+        Ok(0 as usize)
-     }
- 
-     /// Find the next unallocated VT, allocate it and return the number. Note
-     /// that allocation does not mean exclusivity, and another process may 
take
-     /// and use the VT before you get to it.
-     pub fn vt_get_next(&self) -> Result<usize, Error> {
--        let mut next_vt: i64 = 0;
--        let res = unsafe { ioctl::vt_openqry(self.fd, &mut next_vt as *mut 
i64) };
--
--        if let Err(v) = res {
--            Err(format!("terminal: unable to get next vt: {}", v).into())
--        } else if next_vt < 1 {
--            Err(format!("terminal: next vt invalid: {}", next_vt).into())
--        } else {
--            Ok(next_vt as usize)
--        }
-+        Ok(4 as usize)
-     }
- 
-     /// Hook up stdin, stdout and stderr of the current process ot this
-@@ -228,11 +156,6 @@ impl Terminal {
- 
-     // Forcibly take control of the terminal referred to by this fd.
-     pub fn term_take_ctty(&self) -> Result<(), Error> {
--        let res = unsafe { ioctl::term_tiocsctty(self.fd, 1) };
--
--        match res {
--            Err(e) => Err(format!("terminal: unable to take controlling 
terminal: {}", e).into()),
--            Ok(_) => Ok(()),
--        }
-+        Ok(())
-     }
- }

-- 
jca

Reply via email to