rust-battery icon indicating copy to clipboard operation
rust-battery copied to clipboard

NetBSD support

Open svartalf opened this issue 6 years ago • 14 comments
trafficstars

This is a tracking issue for NetBSD support.

svartalf avatar Mar 11 '19 17:03 svartalf

Blocked by the https://github.com/ebarnard/rust-plist/pull/34

svartalf avatar Jun 06 '19 17:06 svartalf

FWIW the 0.4.2 release of rust-plist has the change referenced above

rtyler avatar Jan 10 '21 21:01 rtyler

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.

svartalf avatar Jan 17 '21 14:01 svartalf

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 :smile_cat:

rtyler avatar Jan 17 '21 20:01 rtyler

@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).

svartalf avatar Feb 21 '21 10:02 svartalf

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

0-wiz-0 avatar Jun 04 '21 17:06 0-wiz-0

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!

0-wiz-0 avatar Jun 04 '21 17:06 0-wiz-0

@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?).

bsiegert avatar Jun 23 '21 14:06 bsiegert

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.

he32 avatar Jun 23 '21 23:06 he32

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.

0-wiz-0 avatar Jun 27 '21 12:06 0-wiz-0

Cargo dependencies:

[dependencies]
nix = "*"
plist = "*"

0-wiz-0 avatar Jun 27 '21 12:06 0-wiz-0

I put the code here for easier testing. Pull requests welcome!

0-wiz-0 avatar Jun 27 '21 13:06 0-wiz-0

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?

0-wiz-0 avatar Jul 04 '21 13:07 0-wiz-0