NetBSD support
svartalf opened this issue · 14 comments
This is a tracking issue for NetBSD support.
Blocked by the ebarnard/rust-plist#34
FWIW the 0.4.2 release of rust-plist has the change referenced above
Thanks for heads up, @rtyler! I tried to tackle this issue with NetBSD installed on a VirtualBox, but it was not able to emulate battery for some reasons, so I postponed it for a while.
I have a NetBSD-based Pinebook Pro, if you have some pointers, I could take a look at wiring this up in my spurious free time 😸
@rtyler that would be really great, thanks!
I know nothing about NetBSD, so it would be great to start with figuring out what it can provide to us, looks like envstat
tool should help us to do that?
Seems like Golang implementation uses ioctl
to read the same information from /dev/sysmon
fd with ENVSYS_GETDICTIONARY
ioctl type (?) and it returns back proplist with whatever data there is.
When we will know what data we can operate with, we will need to check if it will be enough to fill Battery
struct with it; if it is the case - everything is great, otherwise we might need to change public API first to suit NetBSD changes (I can work on that).
A friend complained that this crate does not support NetBSD, so I took a quick look at it. Adding 'target_os = "netbsd"' to all "freebsd" cases makes it compile, but the 5 tests fail. So I guess that is not sufficient :)
(NetBSD also uses acpica (same as FreeBSD) that's why I thought this might be enough.)
Then I found this issue.
NetBSD's envstat source code is here:
https://github.com/NetBSD/src/tree/trunk/usr.sbin/envstat
The general framework for system monitoring and power management on NetBSD is sysmon (https://man.netbsd.org/sysmon.4) and in particular for environmental sensors, sysmon_envsys (kernel side documented here: https://man.netbsd.org/sysmon_envsys.9, but that does not really apply here). I hope this is sufficient information!
@svartalf What you describe (read /dev/sysmon) is exactly what envstat does too. Caveat: by default, only root may read from the sysmon device, so there should be some fallback (e.g. pretend there is no battery?).
Another piece to take a look at could be e.g. https://github.com/NetBSD/pkgsrc/tree/trunk/sysutils/xbattbar which in the patches/ sub-directory contains a patch for adapting this to NetBSD. It is considerably smaller than the envstat program. And as far as I can see, neither envstat nor xbattbar needs to be set-uid-root to work.
Here's some sample code that gives a plist:
use std::error::Error;
use std::fs::File;
use std::os::unix::io::AsRawFd;
use nix::ioctl_readwrite;
use std::ffi::c_void;
use std::mem::MaybeUninit;
use plist::Value;
use std::slice::from_raw_parts;
use std::io::Cursor;
#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Debug)]
pub struct plistref {
pref_plist: *mut c_void, /* plist data */
pref_len: usize, /* total length of plist data */
}
ioctl_readwrite!(envsys_getdictionary, b'E', 0, plistref);
fn main() {
match detect_sensors() {
Ok(_) => (),
Err(err) => println!("error: {}", err),
}
}
fn detect_sensors() -> Result <(), Box<dyn Error>> {
let envsys = File::open("/dev/sysmon")?;
let mut dict = MaybeUninit::<plistref>::uninit();
let _res = unsafe { envsys_getdictionary(envsys.as_raw_fd(), dict.as_mut_ptr()) };
let dict = unsafe { dict.assume_init() };
let u8slice: &[u8] = unsafe { from_raw_parts(dict.pref_plist as *const u8, dict.pref_len) };
let cursor = Cursor::new(u8slice);
let value = Value::from_reader(cursor)?;
println!("{:?}", value);
Ok(())
}
On one of my systems that prints:
Dictionary({"amdzentemp0": Array([Dictionary({"cur-value": Integer(314900000), "description": String("cpu0 temperature"), "index": String("sensor0"), "monitoring-state-refresh-event": Boolean(true), "monitoring-supported": Boolean(true), "state": String("valid"), "type": String("Temperature")}), Dictionary({"cur-value": Integer(313150000), "description": String("cpu0 ccd0 temperature"), "index": String("sensor1"), "monitoring-state-refresh-event": Boolean(true), "monitoring-supported": Boolean(true), "state": String("valid"), "type": String("Temperature")}), Dictionary({"device-properties": Dictionary({"device-class": String("other"), "refresh-timeout": Integer(30)})})]), "amdzentemp1": Array([Dictionary({"cur-value": Integer(311775000), "description": String("cpu1 temperature"), "index": String("sensor0"), "monitoring-state-refresh-event": Boolean(true), "monitoring-supported": Boolean(true), "state": String("valid"), "type": String("Temperature")}), Dictionary({"device-properties": Dictionary({"device-class": String("other"), "refresh-timeout": Integer(30)})})])})
Sorry, I don't currently have a laptop with NetBSD i.e. no battery to test with.
Cargo dependencies:
[dependencies]
nix = "*"
plist = "*"
I've extended the example code to include basic parsing of the structure and adding some example outputs, including one from a laptop with a battery. I think this now contains enough information that you should be able to extract the information you need rather easily. Can you take over from here?
A lot of project are still depending on this library instead of using Starship's fork.
I just made the full port for NetBSD.
Are you (and especially the maintainer) interested by a backport ?
I don't think it should be too hard.
The interest might be about the ffi that the Starship's fork removed.