Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
fn main() {
let lib_path = "./assets/libs/arm64-v8a";
println!("cargo::rustc-link-search={}", lib_path);
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("android") {
println!("cargo::rustc-link-search=./assets/libs/arm64-v8a");
}
}
15 changes: 10 additions & 5 deletions gh-pages/docs/user/2-creating-a-non-root-user.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,17 @@ _(Replace `teddy` with the username you created [previously](#create-your-user))

Save and exit.

You can test if your new user can use `sudo` by temporarily logging in:
You can test whether the account is resolvable and can use `sudo` by temporarily logging in:

```bash
su teddy # Change to your username
getent passwd teddy # Change to your username
su - teddy
sudo ls /root # Make sure it does not output something like: "teddy is not in the sudoers file"
exit
```

If `su` displays `I have no name`, close and reopen Local Desktop, then repeat the test. Local Desktop repairs the account database permissions during startup so non-root processes can resolve users and groups.

## [Important] Tell Local Desktop

You must tell Local Desktop who to log in as, or it will log in as root. To (create and) edit the config file:
Expand All @@ -76,13 +80,14 @@ You must tell Local Desktop who to log in as, or it will log in as root. To (cre
nano /etc/localdesktop/localdesktop.toml
```

Add the following content:
Test the new account for one launch while keeping root as the persistent fallback:

```toml title="/etc/localdesktop/localdesktop.toml"
[user]
username = "teddy"
username = "root"
try_username = "teddy"
```

_(Replace `teddy` with the username you created [previously](#create-your-user))_

The changes will take effect the next time you launch Local Desktop. If something goes wrong, you can always delete this config file and restart as root.
The changes will take effect the next time you launch Local Desktop. After that launch, `try_username` is commented out automatically. If the desktop works, change `username = "root"` to `username = "teddy"` and remove the commented `try_username` line. If the configured account is missing, Local Desktop falls back to root so you can repair the account or config without clearing the app's storage.
31 changes: 20 additions & 11 deletions src/android/proot/process.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use crate::android::utils::application_context::get_application_context;
use crate::core::config;
use crate::core::{
config,
guest_user::{repair_account_database_permissions, GuestUser, ROOT_USERNAME},
};
use std::ffi::CString;
use std::fs;
use std::io::{BufRead, BufReader, Read};
Expand Down Expand Up @@ -101,7 +104,17 @@ impl ArchProcess {

pub fn run(self) -> Output {
let context = get_application_context();
let user = self.user.as_deref().unwrap_or("root");
if let Err(error) = repair_account_database_permissions(Path::new(config::ARCH_FS_ROOT)) {
log::warn!("Failed to repair guest account database permissions: {error}");
}

let requested_username = self.user.as_deref().unwrap_or(ROOT_USERNAME);
let user = GuestUser::resolve(Path::new(config::ARCH_FS_ROOT), requested_username);
if self.user.is_some() && user.username() != requested_username {
log::warn!(
"Configured guest user '{requested_username}' could not be resolved; falling back to root"
);
}

let mut process = Command::new(context.native_library_dir.join("libproot.so"));
process
Expand Down Expand Up @@ -153,28 +166,24 @@ impl ArchProcess {

// env vars
process.arg("/usr/bin/env").arg("-i");
if user == "root" {
process.arg("HOME=/root");
} else {
process.arg(format!("HOME=/home/{}", user));
}
process
.arg(format!("HOME={}", user.guest_home_dir().display()))
.arg("LANG=C.UTF-8")
.arg("TERM=xterm-256color")
.arg("PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games:/system/bin:/system/xbin")
.arg("TMPDIR=/tmp")
.arg(format!("USER={}", user))
.arg(format!("LOGNAME={}", user));
.arg(format!("USER={}", user.username()))
.arg(format!("LOGNAME={}", user.username()));

// user shell
if user == "root" {
if user.username() == ROOT_USERNAME {
process.arg("sh");
} else {
process
.arg("runuser")
.arg("--pty")
.arg("-u")
.arg(user)
.arg(user.username())
.arg("--")
.arg("sh");
}
Expand Down
22 changes: 9 additions & 13 deletions src/android/proot/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use crate::{
utils::application_context::get_application_context,
utils::ndk::run_in_jvm,
},
core::config::{CommandConfig, ARCH_FS_ARCHIVE, ARCH_FS_ROOT, DOCS_HOME_URL, PULSE_GUEST_SERVER},
core::{
config::{CommandConfig, ARCH_FS_ARCHIVE, ARCH_FS_ROOT, DOCS_HOME_URL, PULSE_GUEST_SERVER},
guest_user::GuestUser,
},
};
use jni::objects::JObject;
use jni::sys::_jobject;
Expand All @@ -19,7 +22,7 @@ use std::{
fs::{self, File},
io::{Read, Write},
os::unix::fs::{symlink, PermissionsExt},
path::{Path, PathBuf},
path::Path,
sync::{
mpsc::{self, Sender},
Arc, Mutex,
Expand Down Expand Up @@ -501,14 +504,6 @@ runpy.run_path('/usr/sbin/onboard', run_name='__main__')
None
}

fn chroot_home_dir(fs_root: &Path, username: &str) -> PathBuf {
if username == "root" {
fs_root.join("root")
} else {
fs_root.join(format!("home/{username}"))
}
}

fn write_executable(path: &Path, contents: &str) {
if let Some(parent) = path.parent() {
let _ = fs::create_dir_all(parent);
Expand Down Expand Up @@ -561,8 +556,9 @@ fn android_ui_scale(density_dpi: i32) -> i32 {

fn setup_xfce_wayland(options: &SetupOptions) -> StageOutput {
let fs_root = Path::new(ARCH_FS_ROOT);
let username = get_application_context().local_config.user.username;
let home_dir = chroot_home_dir(fs_root, &username);
let configured_username = get_application_context().local_config.user.username;
let guest_user = GuestUser::resolve(fs_root, &configured_username);
let home_dir = guest_user.host_home_dir(fs_root);
let labwc_dir = home_dir.join(".config/xfce4/labwc");

let density_dpi = read_android_density_dpi(options.android_app.clone());
Expand Down Expand Up @@ -926,7 +922,7 @@ pub fn setup(android_app: AndroidApp) -> PolarBearBackend {
Box::new(setup_onboard_signal_fix), // Step 6. Wrap Onboard to survive proot fstat/signal.set_wakeup_fd failure
Box::new(setup_xfce_wayland), // Step 7. Setup Xfce Wayland launch and HiDPI scaling
Box::new(fix_xkb_symlink), // Step 8. Fix xkb symlink
Box::new(setup_pulse_client_conf), // Step 9. Write PulseAudio conf (last)
Box::new(setup_pulse_client_conf), // Step 9. Write PulseAudio conf (last)
];

let handle_stage_error = |e: Box<dyn std::any::Any + Send>, sender: &Sender<SetupMessage>| {
Expand Down
47 changes: 43 additions & 4 deletions src/core/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub const CONFIG_FILE: &str = "/etc/localdesktop/localdesktop.toml";

#[derive(Debug, Serialize, Deserialize, Default, Clone)]
pub struct LocalConfig {
#[serde(default)]
#[serde(default, alias = "users")]
pub user: UserConfig,

/// What happens if we don't assign this `#[serde(default)]` attribute?
Expand All @@ -54,13 +54,18 @@ pub struct LocalConfig {

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UserConfig {
#[serde(default = "default_username")]
pub username: String,
}

fn default_username() -> String {
"root".to_string()
}

impl Default for UserConfig {
fn default() -> Self {
Self {
username: "root".to_string(),
username: default_username(),
}
}
}
Expand Down Expand Up @@ -179,8 +184,7 @@ pub fn parse_config(full_config_path: String) -> LocalConfig {
return config;
}
// Config malformed, use the default config and the user can modify it again
let default_config = LocalConfig::default();
default_config
LocalConfig::default()
}

#[cfg(test)]
Expand Down Expand Up @@ -244,6 +248,41 @@ mod tests {
);
}

#[test]
fn should_keep_other_sections_when_try_username_has_been_consumed() {
with_config_file(
r#"
[user]
try_username = "alice"

[command]
check = "custom-check"
"#,
|full_config_path| {
let first_launch = parse_config(full_config_path.clone());
assert_eq!(first_launch.user.username, "alice");

let second_launch = parse_config(full_config_path);
assert_eq!(second_launch.user.username, "root");
assert_eq!(second_launch.command.check, "custom-check");
},
);
}

#[test]
fn should_accept_the_legacy_users_section_name() {
with_config_file(
r#"
[users]
username = "alice"
"#,
|full_config_path| {
let config = parse_config(full_config_path);
assert_eq!(config.user.username, "alice");
},
);
}

#[test]
fn should_comment_out_try_configs() {
with_config_file(
Expand Down
Loading
Loading