config: Overhaul config format and implementation
- Split the config system into multiple files in its own folder. - Use a manual Deserialize implementation for VtSelection. - Change config format to use sections for better organization. - Add temporary fallback to parse old format.
This commit is contained in:
@@ -1,141 +0,0 @@
|
|||||||
use std::{env, fs::read_to_string};
|
|
||||||
|
|
||||||
use getopts::Options;
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
fn default_vt() -> toml::Value {
|
|
||||||
toml::Value::String("next".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_greeter_user() -> String {
|
|
||||||
"greeter".to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_socket_path() -> String {
|
|
||||||
"/run/greetd.sock".to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct Config {
|
|
||||||
#[serde(default = "default_vt")]
|
|
||||||
pub vt: toml::Value,
|
|
||||||
pub greeter: String,
|
|
||||||
#[serde(default = "default_greeter_user")]
|
|
||||||
pub greeter_user: String,
|
|
||||||
|
|
||||||
#[serde(default = "default_socket_path")]
|
|
||||||
pub socket_path: String,
|
|
||||||
|
|
||||||
#[serde(skip_deserializing)]
|
|
||||||
pub session_worker: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum VtSelection {
|
|
||||||
Next,
|
|
||||||
Current,
|
|
||||||
None,
|
|
||||||
Specific(usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Config {
|
|
||||||
pub fn vt(&self) -> VtSelection {
|
|
||||||
match &self.vt {
|
|
||||||
toml::Value::String(s) => match s.as_str() {
|
|
||||||
"next" => VtSelection::Next,
|
|
||||||
"current" => VtSelection::Current,
|
|
||||||
"none" => VtSelection::None,
|
|
||||||
_ => panic!("unknown value of vt, expect next, current, none, or vt number"),
|
|
||||||
},
|
|
||||||
toml::Value::Integer(u) => VtSelection::Specific(*u as usize),
|
|
||||||
_ => panic!("unknown value of vt, expect next, current, or vt number"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_usage(program: &str, opts: Options) {
|
|
||||||
let brief = format!("Usage: {} [options]", program);
|
|
||||||
print!("{}", opts.usage(&brief));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_config() -> Config {
|
|
||||||
let args: Vec<String> = env::args().collect();
|
|
||||||
let program = args[0].clone();
|
|
||||||
let mut opts = Options::new();
|
|
||||||
opts.optflag("h", "help", "print this help menu");
|
|
||||||
opts.optopt("t", "vt", "VT to run on", "VT");
|
|
||||||
opts.optopt("s", "socket-path", "socket path to use", "SOCKET_PATH");
|
|
||||||
opts.optopt("g", "greeter", "greeter to run", "GREETER");
|
|
||||||
opts.optopt("u", "greeter-user", "user to run greeter as", "USER");
|
|
||||||
opts.optopt("c", "config", "config file to use", "CONFIG_FILE");
|
|
||||||
opts.optopt(
|
|
||||||
"w",
|
|
||||||
"session-worker",
|
|
||||||
"start a session worker (internal)",
|
|
||||||
"FD",
|
|
||||||
);
|
|
||||||
let matches = match opts.parse(&args[1..]) {
|
|
||||||
Ok(m) => m,
|
|
||||||
Err(f) => panic!(f.to_string()),
|
|
||||||
};
|
|
||||||
if matches.opt_present("h") {
|
|
||||||
print_usage(&program, opts);
|
|
||||||
std::process::exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut config = match read_to_string(
|
|
||||||
matches
|
|
||||||
.opt_str("config")
|
|
||||||
.unwrap_or_else(|| "/etc/greetd/config.toml".to_string()),
|
|
||||||
) {
|
|
||||||
Ok(s) => match toml::from_str(&s) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Unable to parse configuration file: {:?}", e);
|
|
||||||
eprintln!("Please fix the configuration file and try again.");
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(_) => Config {
|
|
||||||
vt: default_vt(),
|
|
||||||
greeter: "".to_string(),
|
|
||||||
greeter_user: "".to_string(),
|
|
||||||
socket_path: default_socket_path(),
|
|
||||||
session_worker: 0,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(vt) = matches.opt_str("vt") {
|
|
||||||
config.vt = match vt.as_str() {
|
|
||||||
"next" => toml::Value::String("next".to_string()),
|
|
||||||
"current" => toml::Value::String("current".to_string()),
|
|
||||||
"none" => toml::Value::String("none".to_string()),
|
|
||||||
v => toml::Value::Integer(v.parse().expect("could not parse vt number")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(greeter) = matches.opt_str("greeter") {
|
|
||||||
config.greeter = greeter;
|
|
||||||
}
|
|
||||||
if let Some(user) = matches.opt_str("greeter-user") {
|
|
||||||
config.greeter_user = user;
|
|
||||||
}
|
|
||||||
if let Some(socket_path) = matches.opt_str("socket-path") {
|
|
||||||
config.socket_path = socket_path;
|
|
||||||
}
|
|
||||||
if let Some(session_worker) = matches
|
|
||||||
.opt_get("session-worker")
|
|
||||||
.expect("unable to parse session-worker")
|
|
||||||
{
|
|
||||||
config.session_worker = session_worker
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.greeter.is_empty() {
|
|
||||||
eprintln!("No greeter specified. Run with --help for more information.");
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
if config.greeter_user.is_empty() {
|
|
||||||
eprintln!("No greeter user specified. Run with --help for more information.");
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
config
|
|
||||||
}
|
|
9
greetd/src/config/defaults.rs
Normal file
9
greetd/src/config/defaults.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
use super::vtselection::VtSelection;
|
||||||
|
|
||||||
|
pub fn default_vt() -> VtSelection {
|
||||||
|
VtSelection::Next
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default_greeter_user() -> String {
|
||||||
|
"greeter".to_string()
|
||||||
|
}
|
93
greetd/src/config/mod.rs
Normal file
93
greetd/src/config/mod.rs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
mod defaults;
|
||||||
|
mod old_config;
|
||||||
|
mod types;
|
||||||
|
mod vtselection;
|
||||||
|
|
||||||
|
use std::{env, fs::read_to_string};
|
||||||
|
|
||||||
|
use getopts::Options;
|
||||||
|
|
||||||
|
use super::error::Error;
|
||||||
|
use old_config::*;
|
||||||
|
|
||||||
|
pub use types::*;
|
||||||
|
pub use vtselection::VtSelection;
|
||||||
|
|
||||||
|
fn print_usage(program: &str, opts: Options) {
|
||||||
|
let brief = format!("Usage: {} [options]", program);
|
||||||
|
print!("{}", opts.usage(&brief));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_config() -> Result<Config, Error> {
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
let program = args[0].clone();
|
||||||
|
let mut opts = Options::new();
|
||||||
|
opts.optflag("h", "help", "print this help menu");
|
||||||
|
opts.optopt("s", "socket-path", "socket path to use", "SOCKET_PATH");
|
||||||
|
opts.optopt("c", "config", "config file to use", "CONFIG_FILE");
|
||||||
|
opts.optopt(
|
||||||
|
"w",
|
||||||
|
"session-worker",
|
||||||
|
"start a session worker (internal)",
|
||||||
|
"FD",
|
||||||
|
);
|
||||||
|
let matches = match opts.parse(&args[1..]) {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(f) => return Err(format!("could not parse arguments: {}", f).into()),
|
||||||
|
};
|
||||||
|
if matches.opt_present("h") {
|
||||||
|
print_usage(&program, opts);
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let internal = ConfigInternal {
|
||||||
|
socket_path: matches
|
||||||
|
.opt_str("socket-path")
|
||||||
|
.unwrap_or_else(|| "/run/greetd.sock".to_string()),
|
||||||
|
session_worker: matches
|
||||||
|
.opt_get("session-worker")
|
||||||
|
.expect("unable to parse session-worker")
|
||||||
|
.unwrap_or(0),
|
||||||
|
};
|
||||||
|
|
||||||
|
let file = match read_to_string(
|
||||||
|
matches
|
||||||
|
.opt_str("config")
|
||||||
|
.unwrap_or_else(|| "/etc/greetd/config.toml".to_string()),
|
||||||
|
) {
|
||||||
|
Ok(s) => match toml::from_str(&s) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => match try_read_old_config(&s) {
|
||||||
|
Ok(v) => {
|
||||||
|
eprintln!("warning: Fallback to old config format, caused by : {}", e);
|
||||||
|
v
|
||||||
|
}
|
||||||
|
Err(_e) => {
|
||||||
|
return Err(Error::ConfigError(
|
||||||
|
format!("unable to parse configuration file: {}", e).to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Err(_) => ConfigFile {
|
||||||
|
default_session: ConfigDefaultSession {
|
||||||
|
user: "greeter".to_string(),
|
||||||
|
command: "".to_string(),
|
||||||
|
},
|
||||||
|
terminal: Default::default(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if file.default_session.command.is_empty() {
|
||||||
|
return Err(Error::ConfigError(
|
||||||
|
"no default session user specified".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if file.default_session.user.is_empty() {
|
||||||
|
return Err(Error::ConfigError(
|
||||||
|
"no default session user specified".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Config { file, internal })
|
||||||
|
}
|
35
greetd/src/config/old_config.rs
Normal file
35
greetd/src/config/old_config.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::error::Error;
|
||||||
|
|
||||||
|
use super::vtselection::VtSelection;
|
||||||
|
|
||||||
|
use super::{defaults::*, types::*};
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct OldConfig {
|
||||||
|
#[serde(default = "default_vt")]
|
||||||
|
pub vt: VtSelection,
|
||||||
|
pub greeter: String,
|
||||||
|
#[serde(default = "default_greeter_user")]
|
||||||
|
pub greeter_user: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_read_old_config(s: &str) -> Result<super::ConfigFile, Error> {
|
||||||
|
let oldconfig: OldConfig = match toml::from_str(&s) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
return Err(Error::ConfigError(
|
||||||
|
format!("unable to parse configuration file: {}", e).to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ConfigFile {
|
||||||
|
terminal: ConfigTerminal { vt: oldconfig.vt },
|
||||||
|
default_session: ConfigDefaultSession {
|
||||||
|
user: oldconfig.greeter_user,
|
||||||
|
command: oldconfig.greeter,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
37
greetd/src/config/types.rs
Normal file
37
greetd/src/config/types.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
use super::{defaults::*, vtselection::VtSelection};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct ConfigDefaultSession {
|
||||||
|
pub command: String,
|
||||||
|
#[serde(default = "default_greeter_user")]
|
||||||
|
pub user: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ConfigInternal {
|
||||||
|
pub socket_path: String,
|
||||||
|
pub session_worker: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct ConfigTerminal {
|
||||||
|
#[serde(default = "default_vt")]
|
||||||
|
pub vt: VtSelection,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ConfigTerminal {
|
||||||
|
fn default() -> ConfigTerminal {
|
||||||
|
ConfigTerminal { vt: default_vt() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct ConfigFile {
|
||||||
|
pub terminal: ConfigTerminal,
|
||||||
|
pub default_session: ConfigDefaultSession,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Config {
|
||||||
|
pub file: ConfigFile,
|
||||||
|
pub internal: ConfigInternal,
|
||||||
|
}
|
62
greetd/src/config/vtselection.rs
Normal file
62
greetd/src/config/vtselection.rs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use serde::{
|
||||||
|
de::{self, Deserializer, Visitor},
|
||||||
|
Deserialize,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VtSelectionVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for VtSelectionVisitor {
|
||||||
|
type Value = VtSelection;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("next, currrent, none or a positive vt number")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
if value < 1 {
|
||||||
|
Err(de::Error::invalid_value(
|
||||||
|
de::Unexpected::Signed(value),
|
||||||
|
&"next, current, none or a positive vt number",
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(VtSelection::Specific(value as usize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
match value {
|
||||||
|
"next" => Ok(VtSelection::Next),
|
||||||
|
"current" => Ok(VtSelection::Current),
|
||||||
|
"none" => Ok(VtSelection::None),
|
||||||
|
_ => Err(de::Error::invalid_value(
|
||||||
|
de::Unexpected::Str(value),
|
||||||
|
&"next, current, none or a postive vt number",
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum VtSelection {
|
||||||
|
Next,
|
||||||
|
Current,
|
||||||
|
None,
|
||||||
|
Specific(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for VtSelection {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<VtSelection, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
deserializer.deserialize_any(VtSelectionVisitor)
|
||||||
|
}
|
||||||
|
}
|
@@ -16,6 +16,9 @@ pub enum Error {
|
|||||||
|
|
||||||
#[error("i/o error: {0}")]
|
#[error("i/o error: {0}")]
|
||||||
Io(String),
|
Io(String),
|
||||||
|
|
||||||
|
#[error("configuration error: {0}")]
|
||||||
|
ConfigError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Box<dyn std::error::Error>> for Error {
|
impl From<Box<dyn std::error::Error>> for Error {
|
||||||
|
@@ -21,7 +21,7 @@ use tokio::task;
|
|||||||
use crate::{error::Error, session::worker};
|
use crate::{error::Error, session::worker};
|
||||||
|
|
||||||
async fn session_worker_main(config: config::Config) -> Result<(), Error> {
|
async fn session_worker_main(config: config::Config) -> Result<(), Error> {
|
||||||
let raw_fd = config.session_worker as RawFd;
|
let raw_fd = config.internal.session_worker as RawFd;
|
||||||
let mut cur_flags = unsafe { FdFlag::from_bits_unchecked(fcntl(raw_fd, FcntlArg::F_GETFD)?) };
|
let mut cur_flags = unsafe { FdFlag::from_bits_unchecked(fcntl(raw_fd, FcntlArg::F_GETFD)?) };
|
||||||
cur_flags.insert(FdFlag::FD_CLOEXEC);
|
cur_flags.insert(FdFlag::FD_CLOEXEC);
|
||||||
fcntl(raw_fd, FcntlArg::F_SETFD(cur_flags))?;
|
fcntl(raw_fd, FcntlArg::F_SETFD(cur_flags))?;
|
||||||
@@ -31,11 +31,17 @@ async fn session_worker_main(config: config::Config) -> Result<(), Error> {
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let config = config::read_config();
|
let config = match config::read_config() {
|
||||||
|
Ok(config) => config,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
mlockall(MlockAllFlags::all()).expect("unable to lock pages");
|
mlockall(MlockAllFlags::all()).expect("unable to lock pages");
|
||||||
let res = task::LocalSet::new()
|
let res = task::LocalSet::new()
|
||||||
.run_until(async move {
|
.run_until(async move {
|
||||||
if config.session_worker > 0 {
|
if config.internal.session_worker > 0 {
|
||||||
session_worker_main(config).await
|
session_worker_main(config).await
|
||||||
} else {
|
} else {
|
||||||
server::main(config).await
|
server::main(config).await
|
||||||
|
@@ -77,17 +77,12 @@ async fn client_handler(ctx: &Context, mut s: UnixStream) -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn main(config: Config) -> Result<(), Error> {
|
pub async fn main(config: Config) -> Result<(), Error> {
|
||||||
std::env::set_var("GREETD_SOCK", &config.socket_path);
|
std::env::set_var("GREETD_SOCK", &config.internal.socket_path);
|
||||||
|
|
||||||
let _ = std::fs::remove_file(config.socket_path.clone());
|
let _ = std::fs::remove_file(&config.internal.socket_path);
|
||||||
let mut listener = UnixListener::bind(&config.socket_path)
|
let mut listener = UnixListener::bind(&config.internal.socket_path)
|
||||||
.map_err(|e| format!("unable to open listener: {}", e))?;
|
.map_err(|e| format!("unable to open listener: {}", e))?;
|
||||||
|
|
||||||
let u = users::get_user_by_name(&config.greeter_user).ok_or(format!(
|
|
||||||
"configured greeter user '{}' not found",
|
|
||||||
&config.greeter_user
|
|
||||||
))?;
|
|
||||||
|
|
||||||
let service = if Path::new("/etc/pam.d/greetd").exists() {
|
let service = if Path::new("/etc/pam.d/greetd").exists() {
|
||||||
"greetd"
|
"greetd"
|
||||||
} else if Path::new("/etc/pam.d/login").exists() {
|
} else if Path::new("/etc/pam.d/login").exists() {
|
||||||
@@ -97,13 +92,22 @@ pub async fn main(config: Config) -> Result<(), Error> {
|
|||||||
return Err("PAM 'greetd' service missing".into());
|
return Err("PAM 'greetd' service missing".into());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let u = users::get_user_by_name(&config.file.default_session.user).ok_or(format!(
|
||||||
|
"configured default session user '{}' not found",
|
||||||
|
&config.file.default_session.user
|
||||||
|
))?;
|
||||||
|
|
||||||
let uid = Uid::from_raw(u.uid());
|
let uid = Uid::from_raw(u.uid());
|
||||||
let gid = Gid::from_raw(u.primary_group_id());
|
let gid = Gid::from_raw(u.primary_group_id());
|
||||||
chown(config.socket_path.as_str(), Some(uid), Some(gid))
|
chown(config.internal.socket_path.as_str(), Some(uid), Some(gid)).map_err(|e| {
|
||||||
.map_err(|e| format!("unable to chown greetd socket: {}", e))?;
|
format!(
|
||||||
|
"unable to chown greetd socket at {}: {}",
|
||||||
|
&config.internal.socket_path, e
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
let term = Terminal::open(0).map_err(|e| format!("unable to open terminal: {}", e))?;
|
let term = Terminal::open(0).map_err(|e| format!("unable to open terminal: {}", e))?;
|
||||||
let vt = match config.vt() {
|
let vt = match config.file.terminal.vt {
|
||||||
VtSelection::Current => term
|
VtSelection::Current => term
|
||||||
.vt_get_current()
|
.vt_get_current()
|
||||||
.map_err(|e| format!("unable to get current VT: {}", e))?,
|
.map_err(|e| format!("unable to get current VT: {}", e))?,
|
||||||
@@ -116,11 +120,12 @@ pub async fn main(config: Config) -> Result<(), Error> {
|
|||||||
drop(term);
|
drop(term);
|
||||||
|
|
||||||
let ctx = Rc::new(Context::new(
|
let ctx = Rc::new(Context::new(
|
||||||
config.greeter,
|
config.file.default_session.command,
|
||||||
config.greeter_user,
|
config.file.default_session.user,
|
||||||
vt,
|
vt,
|
||||||
service.to_string(),
|
service.to_string(),
|
||||||
));
|
));
|
||||||
|
|
||||||
if let Err(e) = ctx.greet().await {
|
if let Err(e) = ctx.greet().await {
|
||||||
eprintln!("unable to start greeter: {}", e);
|
eprintln!("unable to start greeter: {}", e);
|
||||||
reset_vt(vt).map_err(|e| format!("unable to reset VT: {}", e))?;
|
reset_vt(vt).map_err(|e| format!("unable to reset VT: {}", e))?;
|
||||||
|
@@ -10,6 +10,10 @@ greetd uses a simple TOML configuration file to define its behavior.
|
|||||||
|
|
||||||
# CONFIGURATION
|
# CONFIGURATION
|
||||||
|
|
||||||
|
## terminal
|
||||||
|
|
||||||
|
This section contains terminal configuration.
|
||||||
|
|
||||||
*vt* = num|"next"|"current"
|
*vt* = num|"next"|"current"
|
||||||
The VT to run on. Can be the number of a specific VT, "next" to select the
|
The VT to run on. Can be the number of a specific VT, "next" to select the
|
||||||
next available VT, or "current" to stay wherever greetd was started. The
|
next available VT, or "current" to stay wherever greetd was started. The
|
||||||
@@ -23,16 +27,44 @@ greetd uses a simple TOML configuration file to define its behavior.
|
|||||||
|
|
||||||
Use of a specific VT with appropriate conflict avoidance is recommended.
|
Use of a specific VT with appropriate conflict avoidance is recommended.
|
||||||
|
|
||||||
*greeter* = command-line
|
## default_session
|
||||||
The command-line to run to start the greeter, e.g. "agreety -c sway". The
|
|
||||||
greeter will be run when greetd is initially launched, and every time a
|
This section describes the default session, also referred to as the "greeter".
|
||||||
created session terminates.
|
|
||||||
|
*command* = command-line
|
||||||
|
The command-line to run to start the default session, e.g. "agreety -c sway".
|
||||||
|
The default session is automatically started when no other session is
|
||||||
|
running, such as when user session terminate, and when greetd is initially
|
||||||
|
started with no initial session configured.
|
||||||
|
|
||||||
See *greetd-ipc*(7) for information on how a greeter can create sessions.
|
See *greetd-ipc*(7) for information on how a greeter can create sessions.
|
||||||
|
|
||||||
*greeter_user* = user
|
*user* = user
|
||||||
The user to use for running the greeter. Defaults to "greeter".
|
The user to use for running the greeter. Defaults to "greeter".
|
||||||
|
|
||||||
|
## initial_session
|
||||||
|
|
||||||
|
This optional section describes the initial session, commonly referred to as
|
||||||
|
"auto-login".
|
||||||
|
|
||||||
|
*command* = command-line
|
||||||
|
The command-line to run to start the initial session, e.g. "sway". The
|
||||||
|
initial session will be run when exactly once when greetd is initially
|
||||||
|
launched.
|
||||||
|
|
||||||
|
*user* = user
|
||||||
|
The user to use for running the initial session.
|
||||||
|
|
||||||
|
# EXAMPLE
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[terminal]
|
||||||
|
vt = 1
|
||||||
|
|
||||||
|
[default_session]
|
||||||
|
command = "agreety -c sway"
|
||||||
|
```
|
||||||
|
|
||||||
# AUTHORS
|
# AUTHORS
|
||||||
|
|
||||||
Maintained by Kenny Levinsen <contact@kl.wtf>. For more information about
|
Maintained by Kenny Levinsen <contact@kl.wtf>. For more information about
|
||||||
|
Reference in New Issue
Block a user