├── .gitattributes ├── test ├── Cargo.toml └── src │ └── main.rs ├── .gitignore ├── README.md ├── Cargo.toml ├── c ├── test.c ├── info.h ├── haiku.c ├── freebsd.c ├── netbsd.c ├── openbsd.c ├── windows.c ├── linux.c └── darwin.c ├── LICENSE ├── .travis.yml ├── CHANGELOG.md ├── kstat.rs └── lib.rs /.gitattributes: -------------------------------------------------------------------------------- 1 | c/* linguist-vendored 2 | -------------------------------------------------------------------------------- /test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "test" 4 | version = "0.0.1" 5 | authors = ["FillZpp "] 6 | 7 | [dependencies.sys-info] 8 | path = ".." 9 | version = "*" 10 | 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.so 4 | *.rlib 5 | *.dll 6 | 7 | # Executables 8 | *.out 9 | *.exe 10 | 11 | # Generated by Cargo 12 | target/ 13 | *.lock 14 | 15 | .idea/ 16 | *.code-workspace 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sys-info-rs 2 | 3 | [![Build Status](https://travis-ci.org/FillZpp/sys-info-rs.svg?branch=master)](https://travis-ci.org/FillZpp/sys-info-rs) 4 | 5 | Get system information in Rust. 6 | 7 | For now it supports Linux, Mac OS X, illumos, Solaris, FreeBSD, OpenBSD, NetBSD and Windows. 8 | And now it can get information of kernel/cpu/memory/disk/load/hostname and so on. 9 | 10 | [Documentation](https://docs.rs/sys-info) 11 | 12 | ### Usage 13 | Add this to `Cargo.toml`: 14 | 15 | ``` 16 | [dependencies] 17 | sys-info = "0.9" 18 | ``` 19 | 20 | and add this to crate root: 21 | 22 | ``` 23 | extern crate sys_info; 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "sys-info" 4 | version = "0.9.1" 5 | authors = ["Siyu Wang "] 6 | license = "MIT" 7 | readme = "README.md" 8 | keywords = ["system", "cpu", "disk", "memory", "information"] 9 | repository = "https://github.com/FillZpp/sys-info-rs" 10 | documentation = "https://docs.rs/sys-info" 11 | description = """ 12 | Get system information in Rust. 13 | 14 | For now it supports Linux, Mac OS X, illumos, Solaris, FreeBSD, OpenBSD, and Windows. 15 | """ 16 | 17 | links = "info" 18 | build = "build.rs" 19 | 20 | [lib] 21 | name = "sys_info" 22 | path = "lib.rs" 23 | 24 | [build-dependencies] 25 | cc = "1" 26 | 27 | [dependencies] 28 | libc = "0.2.29" 29 | -------------------------------------------------------------------------------- /c/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "info.h" 3 | 4 | int main(void) { 5 | printf("os type: %s\n", get_os_type()); 6 | printf("os release: %s\n", get_os_release()); 7 | 8 | printf("\ncpu num: %u\n", get_cpu_num()); 9 | printf("cpu speed: %lu\n", get_cpu_speed()); 10 | 11 | LoadAvg la = get_loadavg(); 12 | printf("\nloadavg: %f %f %f\n", la.one, la.five, la.fifteen); 13 | 14 | printf("proc total: %lu\n", get_proc_total()); 15 | 16 | MemInfo mi = get_mem_info(); 17 | printf("\nmem:\ntotal %llu, avail %llu, free %llu\n", 18 | mi.total, mi.avail, mi.free); 19 | printf("buffers %llu, cached %llu\n", mi.buffers, mi.cached); 20 | printf("swap: total %llu, free %llu\n", mi.swap_total, mi.swap_free); 21 | 22 | DiskInfo di = get_disk_info(); 23 | printf("\ndisk: total %llu, free %llu\n", di.total, di.free); 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /c/info.h: -------------------------------------------------------------------------------- 1 | #ifndef INFO_H_ 2 | #define INFO_H_ 3 | 4 | typedef struct LoadAvg { 5 | double one; 6 | double five; 7 | double fifteen; 8 | } LoadAvg; 9 | 10 | typedef struct MemInfo { 11 | unsigned long long total; 12 | unsigned long long free; 13 | unsigned long long avail; 14 | 15 | unsigned long long buffers; 16 | unsigned long long cached; 17 | 18 | unsigned long long swap_total; 19 | unsigned long long swap_free; 20 | } MemInfo; 21 | 22 | typedef struct DiskInfo { 23 | unsigned long long total; 24 | unsigned long long free; 25 | } DiskInfo; 26 | 27 | const char *get_os_type(void); 28 | const char *get_os_release(void); 29 | 30 | unsigned int get_cpu_num(void); 31 | unsigned long get_cpu_speed(void); 32 | 33 | LoadAvg get_loadavg(void); 34 | unsigned long get_proc_total(void); 35 | 36 | MemInfo get_mem_info(void); 37 | DiskInfo get_disk_info(void); 38 | 39 | #endif 40 | 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Siyu Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /test/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | extern crate sys_info; 3 | 4 | use sys_info::*; 5 | 6 | fn main() { 7 | 8 | println!("os: {} {}", os_type().unwrap(), os_release().unwrap()); 9 | println!("cpu: {} cores, {} MHz", cpu_num().unwrap(), cpu_speed().unwrap()); 10 | println!("proc total: {}", proc_total().unwrap()); 11 | let load = loadavg().unwrap(); 12 | println!("load: {} {} {}", load.one, load.five, load.fifteen); 13 | let mem = mem_info().unwrap(); 14 | println!("mem: total {} KB, free {} KB, avail {} KB, buffers {} KB, cached {} KB", 15 | mem.total, mem.free, mem.avail, mem.buffers, mem.cached); 16 | println!("swap: total {} KB, free {} KB", mem.swap_total, mem.swap_free); 17 | #[cfg(not(target_os = "solaris"))] { 18 | let disk = disk_info().unwrap(); 19 | println!("disk: total {} KB, free {} KB", disk.total, disk.free); 20 | } 21 | println!("hostname: {}", hostname().unwrap()); 22 | #[cfg(not(target_os = "windows"))] { 23 | let t = boottime().unwrap(); 24 | println!("boottime {} sec, {} usec", t.tv_sec, t.tv_usec); 25 | } 26 | #[cfg(target_os = "linux")] 27 | println!("/etc/os-release: {:?}", linux_os_release().unwrap()); 28 | } 29 | 30 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - linux 3 | - osx 4 | - windows 5 | 6 | language: rust 7 | rust: 8 | - stable 9 | - beta 10 | - nightly 11 | 12 | # Apparently (according to VSCode), 13 | # `cache: rust` is invalid 14 | # Should be `cache: cargo`, 15 | # but this seems to hang the build on windows 16 | # Uncomment the below line and comment out the current line to enable cargo caching: 17 | # cache: cargo 18 | cache: rust 19 | 20 | matrix: 21 | allow_failures: 22 | - rust: nightly 23 | 24 | # Use the msvc toolchain of rust on windows 25 | # The Travis default is _-x86_64-pc-windows-gnu 26 | # But since we need windows libraries for this crate, 27 | # _-x86_64-pc-windows-msvc needs to be used instead. 28 | # See https://github.com/rust-lang/rustup/blob/master/README.md#working-with-rust-on-windows 29 | before_script: 30 | - if [ "$TRAVIS_OS_NAME" = "windows" ]; then rustup toolchain add $TRAVIS_RUST_VERSION-x86_64-pc-windows-msvc && rustup default $TRAVIS_RUST_VERSION-x86_64-pc-windows-msvc ; fi 31 | script: 32 | - cargo test --all --verbose 33 | # Note: Whilst Travis says to install these in the before_script, 34 | # on nightly the install command fails 35 | # so the install happens after tests so nightly tests are still ran 36 | - rustup component add clippy rustfmt 37 | - cargo clippy 38 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ChangeLog 2 | 3 | ## 0.9.1 4 | 5 | - Fix iOS Support and CPU speed doesn't work on ARM64 Macs either. 6 | - Rust Nightly fix 7 | - Add a cast to allow building on ILP32 systems 8 | - Prevent free swap from getting larger than total swap 9 | - Fix compiler errors/warnings for NetBSD/arm 10 | 11 | ## 0.9.0 12 | 13 | - Typo fixes & test fixup 14 | - macOS: On failure copy the unknown value and null-terminate it correctly 15 | - Fix windows-gnu build 16 | - Support for NetBSD platform 17 | 18 | ## 0.8.0 19 | 20 | - Fix build for target android 21 | - add OpenBSD support 22 | - Make get_cpu_speed arch-independent on windows 23 | - improve get_mem_info on macos 24 | - Make Disk Info Thread-Safe on Linux 25 | - Loop to find max CPU speed in Windows get_cpu_speed 26 | 27 | ## 0.7.0 28 | 29 | - FreeBSD port. 30 | 31 | ## 0.6.1 32 | 33 | - Restore `Send` trait to `Error` for wrapping with `error-chain` 34 | - Use cfg attribute instead of cfg! macro, which fixes Windows build errors in v0.6.0 35 | 36 | ## 0.6.0 37 | 38 | - Support illumos and Solaris systems 39 | 40 | ## 0.5.10 41 | 42 | - Cast gethostname() arguments to a proper type. 43 | 44 | ## 0.5.9 45 | 46 | - Optimize getHostname for hostname command might not be installed. 47 | 48 | ## 0.5.8 49 | 50 | - Support get os-release information for Linux #38 51 | -------------------------------------------------------------------------------- /c/haiku.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "info.h" 10 | 11 | static const char *os_type = "Haiku"; 12 | 13 | 14 | /* Get information */ 15 | 16 | const char *get_os_type(void) { 17 | return os_type; 18 | } 19 | 20 | const char *get_os_release(void) { 21 | return ""; 22 | } 23 | 24 | unsigned int get_cpu_num(void) { 25 | system_info sysInfo; 26 | 27 | if (get_system_info(&sysInfo) == B_OK) { 28 | return sysInfo.cpu_count; 29 | } 30 | 31 | return 1; 32 | } 33 | 34 | 35 | /* 36 | get_cpu_speed 37 | */ 38 | 39 | unsigned long get_cpu_speed(void) { 40 | uint32 topologyNodeCount = 0; 41 | cpu_topology_node_info* topology = NULL; 42 | get_cpu_topology_info(NULL, &topologyNodeCount); 43 | if (topologyNodeCount != 0) 44 | topology = (cpu_topology_node_info*)calloc(topologyNodeCount, sizeof(cpu_topology_node_info)); 45 | get_cpu_topology_info(topology, &topologyNodeCount); 46 | 47 | uint64 cpuFrequency = 0; 48 | for (uint32 i = 0; i < topologyNodeCount; i++) { 49 | if (topology[i].type == B_TOPOLOGY_CORE) { 50 | cpuFrequency = topology[i].data.core.default_frequency; 51 | break; 52 | } 53 | } 54 | free(topology); 55 | 56 | return cpuFrequency / 1000000; 57 | } 58 | 59 | /* 60 | get_loadavg & get_proc_total 61 | /proc/loadavg 62 | */ 63 | 64 | LoadAvg get_loadavg(void) { 65 | static LoadAvg avg; 66 | return avg; 67 | } 68 | 69 | unsigned long get_proc_total(void) { 70 | system_info sysInfo; 71 | 72 | if (get_system_info(&sysInfo) == B_OK) { 73 | return sysInfo.used_teams; 74 | } 75 | 76 | return 1; 77 | } 78 | 79 | /* 80 | get_mem_info 81 | */ 82 | 83 | MemInfo get_mem_info(void) { 84 | MemInfo mi; 85 | 86 | system_info sysInfo; 87 | 88 | int factor = B_PAGE_SIZE / 1024; 89 | 90 | if (get_system_info(&sysInfo) == B_OK) { 91 | mi.total = (uint64)(sysInfo.max_pages) * factor; 92 | mi.free = (uint64)(sysInfo.max_pages - sysInfo.used_pages) * factor; 93 | mi.avail = 0; 94 | mi.cached = (uint64)(sysInfo.cached_pages) * factor; 95 | mi.buffers = 0; 96 | mi.swap_total = (uint64)(sysInfo.max_swap_pages) * factor; 97 | mi.swap_free = (uint64)(sysInfo.free_swap_pages) * factor; 98 | } else { 99 | memset(&mi, 0, sizeof(mi)); 100 | } 101 | 102 | return mi; 103 | } 104 | 105 | DiskInfo get_disk_info(void) { 106 | DiskInfo di; 107 | 108 | fs_info info; 109 | int32 cookie = 0; 110 | 111 | dev_t handle = next_dev(&cookie); 112 | 113 | while (fs_stat_dev(handle, &info) >= 0) { 114 | // just count the native FS 115 | if (strcmp(info.fsh_name, "bfs") == 0) { 116 | unsigned long long free = info.free_blocks; 117 | unsigned long long total = info.total_blocks; 118 | free *= info.block_size; 119 | free /= 1024; 120 | total *= info.block_size; 121 | total /= 1024; 122 | di.free += free; 123 | di.total += total; 124 | } 125 | 126 | handle = next_dev(&cookie); 127 | } 128 | 129 | return di; 130 | } 131 | -------------------------------------------------------------------------------- /c/freebsd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "info.h" 14 | 15 | static const char *os_release; 16 | 17 | static pthread_once_t once_init_freebsd; 18 | static void init_freebsd(void) { 19 | struct utsname un; 20 | 21 | if (uname(&un) == -1) 22 | return; 23 | os_release = strdup(un.release); 24 | } 25 | 26 | const char *get_os_release(void) { 27 | pthread_once(&once_init_freebsd, init_freebsd); 28 | return (os_release); 29 | } 30 | 31 | uint64_t get_cpu_speed(void) { 32 | uint64_t tsc_freq; 33 | size_t len; 34 | int error; 35 | 36 | #if defined(__i386__) || defined(__amd64__) 37 | len = sizeof(tsc_freq); 38 | error = sysctlbyname("machdep.tsc_freq", &tsc_freq, &len, NULL, 0); 39 | if (error == -1) 40 | return (0); 41 | #else 42 | tsc_freq = 1000 * 1000 * 1000; 43 | #endif 44 | return (tsc_freq / 1000 / 1000); 45 | } 46 | 47 | uint64_t get_proc_total(void) { 48 | struct kinfo_proc *kp, *kpp; 49 | int mib[3], count, error; 50 | size_t len; 51 | 52 | mib[0] = CTL_KERN; 53 | mib[1] = KERN_PROC; 54 | mib[2] = KERN_PROC_PROC; 55 | 56 | error = sysctl(mib, nitems(mib), NULL, &len, NULL, 0); 57 | if (error == -1) 58 | return (0); 59 | 60 | kp = malloc(len); 61 | if (kp == NULL) 62 | return (0); 63 | memset(kp, 0, len); 64 | 65 | error = sysctl(mib, nitems(mib), kp, &len, NULL, 0); 66 | if (error == -1) { 67 | free(kp); 68 | return (0); 69 | } 70 | 71 | for (count = 0, kpp = kp; (char *)kpp < (char *)kp + len; kpp++) { 72 | if (kpp->ki_pid == 0) 73 | continue; 74 | count++; 75 | } 76 | free(kp); 77 | return (count); 78 | } 79 | 80 | int32_t get_mem_info_bsd(struct MemInfo *mi) { 81 | struct vmtotal vmt; 82 | struct xswdev xs; 83 | int mib[3], error, i; 84 | unsigned long res; 85 | size_t len; 86 | 87 | len = sizeof(res); 88 | error = sysctlbyname("hw.realmem", &res, &len, NULL, 0); 89 | if (error == -1) 90 | goto fail; 91 | mi->total = res / 1024; 92 | 93 | len = sizeof(res); 94 | error = sysctlbyname("hw.physmem", &res, &len, NULL, 0); 95 | if (error == -1) 96 | goto fail; 97 | mi->avail = res / 1024; 98 | 99 | mib[0] = CTL_VM; 100 | mib[1] = VM_TOTAL; 101 | len = sizeof(vmt); 102 | error = sysctl(mib, 2, &vmt, &len, NULL, 0); 103 | if (error == -1) 104 | goto fail; 105 | mi->free = vmt.t_free * PAGE_SIZE / 1024; 106 | 107 | len = nitems(mib); 108 | if (sysctlnametomib("vm.swap_info", mib, &len) == -1) 109 | goto fail; 110 | for (i = 0; ; i++) { 111 | mib[2] = i; 112 | len = sizeof(xs); 113 | error = sysctl(mib, 3, &xs, &len, NULL, 0); 114 | if (error == -1) 115 | break; 116 | mi->swap_total += (uint64_t)xs.xsw_nblks * PAGE_SIZE / 1024; 117 | mi->swap_free += ((uint64_t)xs.xsw_nblks - xs.xsw_used) * 118 | PAGE_SIZE / 1024; 119 | } 120 | return (0); 121 | 122 | fail: 123 | return (-1); 124 | } 125 | 126 | int32_t get_disk_info_bsd(DiskInfo *di) { 127 | struct statfs *sfs, *sf; 128 | int i, nmounts; 129 | uint64_t dtotal, dfree; 130 | int32_t res; 131 | 132 | dtotal = 0; 133 | dfree = 0; 134 | sfs = NULL; 135 | res = -1; 136 | 137 | nmounts = getfsstat(NULL, 0, MNT_WAIT); 138 | if (nmounts == -1) 139 | goto fail; 140 | sfs = calloc(nmounts, sizeof(*sfs)); 141 | if (sfs == NULL) 142 | goto fail; 143 | nmounts = getfsstat(sfs, nmounts * sizeof(*sfs), MNT_WAIT); 144 | if (nmounts == -1) 145 | goto fail; 146 | 147 | for (i = 0; i < nmounts; i++) { 148 | sf = &sfs[i]; 149 | if ((sf->f_flags & (MNT_LOCAL | MNT_IGNORE)) != MNT_LOCAL) 150 | continue; 151 | dtotal += sf->f_blocks * sf->f_bsize; 152 | dfree += sf->f_bfree * sf->f_bsize; 153 | } 154 | 155 | di->total = dtotal / 1000; 156 | di->free = dfree / 1000; 157 | res = 0; 158 | 159 | fail: 160 | free(sfs); 161 | return (res); 162 | } 163 | -------------------------------------------------------------------------------- /c/netbsd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | #include "info.h" 19 | 20 | #define ONE_K 1024L 21 | #define ONE_DECIMAL_K 1000L 22 | 23 | #ifndef PAGE_SIZE 24 | #define PAGE_SIZE ( sysconf(_SC_PAGESIZE) ) 25 | #endif 26 | #define PAGE_SIZE_KB ( PAGE_SIZE / ONE_K ) 27 | 28 | static const char *os_release; 29 | 30 | static pthread_once_t once_init_netbsd; 31 | static void init_netbsd(void) { 32 | struct utsname un; 33 | 34 | if (uname(&un) == -1) 35 | return; 36 | os_release = strdup(un.release); 37 | } 38 | 39 | const char *get_os_release(void) { 40 | pthread_once(&once_init_netbsd, init_netbsd); 41 | return (os_release); 42 | } 43 | 44 | unsigned long get_cpu_speed(void) { 45 | #if defined(__i386__) || defined(__amd64__) 46 | uint64_t tsc_freq; 47 | size_t len; 48 | int error; 49 | 50 | len = sizeof(tsc_freq); 51 | error = sysctlbyname("machdep.tsc_freq", &tsc_freq, &len, NULL, 0); 52 | if (error == -1) 53 | return (0); 54 | return (unsigned long) (tsc_freq / ONE_DECIMAL_K / ONE_DECIMAL_K); 55 | #elif defined(__arm__) || defined(__aarch64__) 56 | uint32_t tsc_freq; 57 | size_t len = sizeof(tsc_freq); 58 | int const result = sysctlbyname("machdep.cpu.frequency.current", 59 | &tsc_freq, &len, NULL, 0); 60 | 61 | return result == -1 ? ONE_DECIMAL_K : tsc_freq; 62 | #else 63 | return ONE_DECIMAL_K; 64 | #endif 65 | } 66 | 67 | unsigned long get_proc_total(void) { 68 | char errbuf[_POSIX2_LINE_MAX]; 69 | int count; 70 | kvm_t *kd; 71 | struct kinfo_proc *kp; 72 | 73 | if ((kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf)) == NULL) 74 | return (0); 75 | 76 | if ((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &count)) == NULL) { 77 | (void) kvm_close(kd); 78 | return(0); 79 | } 80 | 81 | kvm_close(kd); 82 | free(kp); 83 | return (unsigned long) (count); 84 | } 85 | 86 | int32_t get_mem_info_bsd(struct MemInfo *mi) { 87 | static int uvmexp_mib[] = {CTL_VM, VM_UVMEXP2}; 88 | struct uvmexp_sysctl uvmexp; 89 | size_t size_uvmexp = sizeof(uvmexp); 90 | int error; 91 | 92 | error = sysctl(uvmexp_mib, 2, &uvmexp, &size_uvmexp, NULL, 0); 93 | if (error == -1) 94 | goto fail; 95 | 96 | // These calculations have been taken from sys/miscfs/procfs 97 | // They need review for testing the correctness 98 | mi->total = (uint64_t)uvmexp.npages * PAGE_SIZE_KB / ONE_K; 99 | mi->avail = 0; 100 | mi->free = (uint64_t)uvmexp.free * PAGE_SIZE_KB / ONE_K; 101 | mi->cached = (uvmexp.anonpages + uvmexp.filepages + uvmexp.execpages) * PAGE_SIZE_KB / ONE_K; 102 | mi->buffers = uvmexp.filepages * PAGE_SIZE_KB / ONE_K; 103 | mi->swap_total = uvmexp.swpages * PAGE_SIZE_KB / ONE_K; 104 | mi->swap_free = (uvmexp.swpages - uvmexp.swpginuse) * PAGE_SIZE_KB / ONE_K; 105 | return (0); 106 | 107 | fail: 108 | return (-1); 109 | } 110 | 111 | int32_t get_disk_info_bsd(DiskInfo *di) { 112 | struct statvfs *svfs, *svf; 113 | int i, nmounts; 114 | uint64_t dtotal, dfree; 115 | int32_t res = 0; 116 | 117 | dtotal = 0; 118 | dfree = 0; 119 | svfs = NULL; 120 | res = -1; 121 | 122 | nmounts = getvfsstat(NULL, 0, MNT_WAIT); 123 | if (nmounts == -1) 124 | goto fail; 125 | svfs = calloc(nmounts, sizeof(*svfs)); 126 | if (svfs == NULL) 127 | goto fail; 128 | nmounts = getvfsstat(svfs, nmounts * sizeof(*svfs), MNT_WAIT); 129 | if (nmounts == -1) 130 | goto fail; 131 | 132 | for (i = 0; i < nmounts; i++) { 133 | svf = &svfs[i]; 134 | if ((svf->f_flag & MNT_LOCAL) != MNT_LOCAL) 135 | continue; 136 | dtotal += svf->f_blocks * svf->f_bsize; 137 | dfree += svf->f_bfree * svf->f_bsize; 138 | } 139 | 140 | di->total = dtotal / 1000; 141 | di->free = dfree / 1000; 142 | res = 0; 143 | 144 | fail: 145 | free(svfs); 146 | return (res); 147 | } 148 | -------------------------------------------------------------------------------- /c/openbsd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "info.h" 14 | 15 | static const char *os_release; 16 | 17 | static pthread_once_t once_init_openbsd; 18 | static void init_openbsd(void) { 19 | struct utsname un; 20 | 21 | if (uname(&un) == -1) 22 | return; 23 | os_release = strdup(un.release); 24 | } 25 | 26 | const char *get_os_release(void) { 27 | pthread_once(&once_init_openbsd, init_openbsd); 28 | return (os_release); 29 | } 30 | 31 | unsigned long get_cpu_speed(void) { 32 | unsigned int mhz; 33 | int mib[2], error; 34 | size_t len; 35 | 36 | mib[0] = CTL_HW; 37 | mib[1] = HW_CPUSPEED; 38 | len = sizeof(mhz); 39 | error = sysctl(mib, 2, &mhz, &len, NULL, 0); 40 | if (error == -1) 41 | return 0; 42 | 43 | return mhz; 44 | } 45 | 46 | unsigned long get_proc_total(void) { 47 | struct kinfo_proc *kp, *kpp; 48 | int mib[6], count, error; 49 | size_t len; 50 | 51 | mib[0] = CTL_KERN; 52 | mib[1] = KERN_NPROCS; 53 | len = sizeof(count); 54 | error = sysctl(mib, 2, &count, &len, NULL, 0); 55 | if (error == -1) 56 | return (0); 57 | 58 | kp = calloc(count, sizeof(*kp)); 59 | if (kp == NULL) 60 | return (0); 61 | 62 | mib[0] = CTL_KERN; 63 | mib[1] = KERN_PROC; 64 | mib[2] = KERN_PROC_ALL; 65 | mib[3] = 0; 66 | mib[4] = sizeof(*kp); 67 | mib[5] = count; 68 | len = count * sizeof(*kp); 69 | error = sysctl(mib, 6, kp, &len, NULL, 0); 70 | if (error == -1) { 71 | free(kp); 72 | return (0); 73 | } 74 | 75 | for (count = 0, kpp = kp; (char *)kpp < (char *)kp + len; kpp++) { 76 | if (kpp->p_pid == 0) 77 | continue; 78 | count++; 79 | } 80 | free(kp); 81 | return (count); 82 | } 83 | 84 | int32_t get_mem_info_bsd(struct MemInfo *mi) { 85 | struct uvmexp uv; 86 | struct bcachestats bc; 87 | struct swapent *sw; 88 | int mib[3], error, i, ns; 89 | size_t len; 90 | 91 | mib[0] = CTL_VM; 92 | mib[1] = VM_UVMEXP; 93 | len = sizeof(uv); 94 | error = sysctl(mib, 2, &uv, &len, NULL, 0); 95 | if (error == -1) 96 | goto fail; 97 | 98 | mib[0] = CTL_VFS; 99 | mib[1] = VFS_GENERIC; 100 | mib[2] = VFS_BCACHESTAT; 101 | len = sizeof(bc); 102 | error = sysctl(mib, 3, &bc, &len, NULL, 0); 103 | if (error == -1) 104 | goto fail; 105 | 106 | mi->total = (uint64_t)uv.npages * uv.pagesize / 1024; 107 | mi->avail = 0; 108 | mi->free = (uint64_t)uv.free * uv.pagesize / 1024; 109 | mi->cached = bc.numbufpages * uv.pagesize / 1024; 110 | mi->buffers = 0; 111 | mi->swap_total = 0; 112 | mi->swap_free = 0; 113 | 114 | ns = swapctl(SWAP_NSWAP, 0, 0); 115 | sw = (ns > 0) ? calloc(ns, sizeof(*sw)) : NULL; 116 | if (sw != NULL && (swapctl(SWAP_STATS, sw, ns) == ns)) { 117 | for (i = 0; i < ns; i++) { 118 | if (!(sw[i].se_flags & SWF_ENABLE)) 119 | continue; 120 | mi->swap_total += sw[i].se_nblks / (1024 / DEV_BSIZE); 121 | mi->swap_free += (sw[i].se_nblks - sw[i].se_inuse) / 122 | (1024 / DEV_BSIZE); 123 | } 124 | } 125 | free(sw); 126 | return (0); 127 | 128 | fail: 129 | return (-1); 130 | } 131 | 132 | int32_t get_disk_info_bsd(DiskInfo *di) { 133 | struct statfs *sfs, *sf; 134 | int i, nmounts; 135 | uint64_t dtotal, dfree; 136 | int32_t res; 137 | 138 | dtotal = 0; 139 | dfree = 0; 140 | sfs = NULL; 141 | res = -1; 142 | 143 | nmounts = getfsstat(NULL, 0, MNT_WAIT); 144 | if (nmounts == -1) 145 | goto fail; 146 | sfs = calloc(nmounts, sizeof(*sfs)); 147 | if (sfs == NULL) 148 | goto fail; 149 | nmounts = getfsstat(sfs, nmounts * sizeof(*sfs), MNT_WAIT); 150 | if (nmounts == -1) 151 | goto fail; 152 | 153 | for (i = 0; i < nmounts; i++) { 154 | sf = &sfs[i]; 155 | if ((sf->f_flags & MNT_LOCAL) != MNT_LOCAL) 156 | continue; 157 | dtotal += sf->f_blocks * sf->f_bsize; 158 | dfree += sf->f_bfree * sf->f_bsize; 159 | } 160 | 161 | di->total = dtotal / 1000; 162 | di->free = dfree / 1000; 163 | res = 0; 164 | 165 | fail: 166 | free(sfs); 167 | return (res); 168 | } 169 | -------------------------------------------------------------------------------- /c/windows.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "info.h" 9 | 10 | #define LEN 20 11 | #define MAXPROCESSES 1024 12 | static const char *os_type = "Windows"; 13 | 14 | /* Internal Declarations */ 15 | static double calculate_cpu_load(unsigned long long, unsigned long long); 16 | static unsigned long long file_time_to_ull(const FILETIME); 17 | 18 | /* Get information */ 19 | const char *get_os_type(void) { 20 | return os_type; 21 | } 22 | 23 | const char *get_os_release(void) { 24 | OSVERSIONINFO osvi; 25 | char *s = malloc(LEN); 26 | 27 | ZeroMemory(&osvi, sizeof(osvi)); 28 | osvi.dwOSVersionInfoSize = sizeof(osvi); 29 | 30 | if (GetVersionEx(&osvi)) 31 | snprintf(s, LEN, "%ld.%ld.%ld", 32 | osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber); 33 | else 34 | strncpy(s, "unknown", LEN); 35 | s[LEN - 1] = '\0'; 36 | 37 | return s; 38 | } 39 | 40 | unsigned int get_cpu_num(void) { 41 | unsigned int num; 42 | SYSTEM_INFO sys_info; 43 | 44 | GetSystemInfo(&sys_info); 45 | num = sys_info.dwNumberOfProcessors; 46 | 47 | return num; 48 | } 49 | 50 | // https://docs.microsoft.com/en-us/windows/win32/power/processor-power-information-str#requirements 51 | // Note that this structure definition was accidentally omitted from WinNT.h. This error will be corrected in the future. 52 | // In the meantime, to compile your application, include the structure definition contained in this topic in your source code. 53 | typedef struct _PROCESSOR_POWER_INFORMATION 54 | { 55 | ULONG Number; 56 | ULONG MaxMhz; 57 | ULONG CurrentMhz; 58 | ULONG MhzLimit; 59 | ULONG MaxIdleState; 60 | ULONG CurrentIdleState; 61 | } PROCESSOR_POWER_INFORMATION; 62 | 63 | unsigned long get_cpu_speed(void) { 64 | unsigned int num = get_cpu_num(); 65 | unsigned int power_info_len = num * sizeof(PROCESSOR_POWER_INFORMATION); 66 | PROCESSOR_POWER_INFORMATION *power_info = malloc(power_info_len); 67 | 68 | CallNtPowerInformation(ProcessorInformation, NULL, 0, power_info, power_info_len); 69 | 70 | unsigned int speed = 0; 71 | for (unsigned int i = 0; i < num; i++) { 72 | if (speed < power_info[i].MaxMhz) { 73 | speed = power_info[i].MaxMhz; 74 | } 75 | } 76 | 77 | free(power_info); 78 | return speed; 79 | } 80 | 81 | LoadAvg get_loadavg(void) { 82 | FILETIME idle_time, kernel_time, user_time; 83 | LoadAvg la; 84 | double load = GetSystemTimes(&idle_time, &kernel_time, &user_time) ? 85 | calculate_cpu_load(file_time_to_ull(idle_time), 86 | file_time_to_ull(kernel_time) + 87 | file_time_to_ull(user_time)) : 88 | -1.0; 89 | 90 | la.one = load; 91 | la.five = load; 92 | la.fifteen = load; 93 | return la; 94 | } 95 | 96 | unsigned long get_proc_total(void) { 97 | DWORD aprocesses[MAXPROCESSES], cb_needed, cprocesses; 98 | 99 | if (!EnumProcesses(aprocesses, sizeof(aprocesses), &cb_needed)) 100 | cprocesses = 0; 101 | else 102 | cprocesses = cb_needed / sizeof(unsigned long); 103 | return cprocesses; 104 | } 105 | 106 | MemInfo get_mem_info(void) { 107 | MEMORYSTATUSEX stat; 108 | /* DWORDLONG size; */ 109 | MemInfo mi; 110 | 111 | stat.dwLength = sizeof(stat); 112 | if (GlobalMemoryStatusEx(&stat)) { 113 | mi.total = stat.ullTotalPhys / 1024; 114 | mi.avail = 0; 115 | mi.free = stat.ullAvailPhys / 1024; 116 | mi.cached = 0; 117 | mi.buffers = 0; 118 | mi.swap_total = (stat.ullTotalPageFile - stat.ullTotalPhys) / 1024; 119 | mi.swap_free = (stat.ullAvailPageFile - stat.ullAvailPhys) / 1024; 120 | if (mi.swap_free > mi.swap_total) { 121 | mi.swap_free = mi.swap_total; 122 | } 123 | } else { 124 | memset(&mi, 0, sizeof(mi)); 125 | } 126 | return mi; 127 | } 128 | 129 | DiskInfo get_disk_info(void) { 130 | DWORD cluser, sector, free, total; 131 | double tmp; 132 | DiskInfo di; 133 | 134 | if (GetDiskFreeSpace(NULL, &cluser, §or, &free, &total)) { 135 | tmp = cluser * sector; 136 | di.total = tmp * total / 1024; 137 | di.free = tmp * free / 1024; 138 | } else { 139 | di.total = 0; 140 | di.free = 0; 141 | } 142 | return di; 143 | } 144 | 145 | 146 | /* Internal definitions */ 147 | double calculate_cpu_load(unsigned long long idle_ticks, 148 | unsigned long long total_ticks) { 149 | static unsigned long long _prev_total_ticks = 0; 150 | static unsigned long long _prev_idle_ticks = 0; 151 | unsigned long long total_ticks_since_last_time = total_ticks - 152 | _prev_total_ticks; 153 | unsigned long long idle_ticks_since_last_time = idle_ticks - 154 | _prev_idle_ticks; 155 | double ret = 1.0 - ((total_ticks_since_last_time > 0) ? 156 | ((double)idle_ticks_since_last_time) / 157 | total_ticks_since_last_time : 158 | 0); 159 | _prev_total_ticks = total_ticks; 160 | _prev_idle_ticks = idle_ticks; 161 | return ret; 162 | } 163 | 164 | unsigned long long file_time_to_ull(const FILETIME ft) { 165 | return (((unsigned long long)(ft.dwHighDateTime)) << 32) | 166 | ((unsigned long long)ft.dwLowDateTime); 167 | } 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /c/linux.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "info.h" 8 | 9 | #define DFHASHSIZE 101 10 | #define MOUNTS "/proc/mounts" 11 | static const char *os_type = "Linux"; 12 | 13 | /* Internal Declarations */ 14 | struct nlist { 15 | struct nlist *next; 16 | char *name; 17 | }; 18 | 19 | unsigned int DFhash(const char*); 20 | struct nlist *seen_before(struct nlist**, const char*); 21 | void DFcleanup(struct nlist**); 22 | int remote_mount(const char*, const char*); 23 | float device_space(struct nlist**, char*, char*, double*, double*); 24 | 25 | 26 | /* Get information */ 27 | /* 28 | get_os_type & get_os_release 29 | /proc/sys/kernel 30 | */ 31 | 32 | const char *get_os_type(void) { 33 | return os_type; 34 | } 35 | 36 | const char *get_os_release(void) { 37 | return ""; 38 | } 39 | 40 | unsigned int get_cpu_num(void) { 41 | return get_nprocs(); 42 | } 43 | 44 | 45 | /* 46 | get_cpu_speed 47 | /sys/devices/system/cpu/cpu0 48 | */ 49 | 50 | unsigned long get_cpu_speed(void) { 51 | return 0; 52 | } 53 | 54 | /* 55 | get_loadavg & get_proc_total 56 | /proc/loadavg 57 | */ 58 | 59 | LoadAvg get_loadavg(void) { 60 | static LoadAvg avg; 61 | return avg; 62 | } 63 | 64 | unsigned long get_proc_total(void) { 65 | return 0; 66 | } 67 | 68 | /* 69 | get_mem_info 70 | /proc/meminfo 71 | */ 72 | 73 | MemInfo get_mem_info(void) { 74 | static MemInfo info; 75 | return info; 76 | } 77 | 78 | DiskInfo get_disk_info(void) { 79 | FILE *mounts; 80 | char procline[1024]; 81 | struct nlist *DFhashvector[DFHASHSIZE] = {0}; 82 | char *mount, *device, *type, *mode, *other; 83 | float thispct, max=0.0; 84 | double dtotal=0.0, dfree=0.0; 85 | DiskInfo di; 86 | 87 | di.total = 0; 88 | di.free = 0; 89 | 90 | mounts = fopen(MOUNTS,"r"); 91 | if (!mounts) { 92 | return di; 93 | } 94 | while ( fgets(procline, sizeof(procline), mounts) ) { 95 | device = procline; 96 | mount = index(procline, ' '); 97 | if (mount == NULL) continue; 98 | *mount++ = '\0'; 99 | type = index(mount, ' '); 100 | if (type == NULL) continue; 101 | *type++ = '\0'; 102 | mode = index(type, ' '); 103 | if (mode == NULL) continue; 104 | *mode++ = '\0'; 105 | other = index(mode, ' '); 106 | if (other != NULL) *other = '\0'; 107 | if (!strncmp(mode, "ro", 2)) continue; 108 | if (remote_mount(device, type)) continue; 109 | if (strncmp(device, "/dev/", 5) != 0 && 110 | strncmp(device, "/dev2/", 6) != 0) continue; 111 | thispct = device_space(DFhashvector, mount, device, &dtotal, &dfree); 112 | if (!max || maxnext) { 142 | if (!strcmp(name,np->name)) { 143 | found=np; 144 | break; 145 | } 146 | } 147 | if (!found) { /* not found */ 148 | np = (struct nlist *) malloc(sizeof(*np)); 149 | if (!np || !(np->name = (char *) strdup(name))) 150 | return NULL; 151 | np->next = DFhashvector[hashval]; 152 | DFhashvector[hashval] = np; 153 | return NULL; 154 | } 155 | else /* found name */ 156 | return found; 157 | } 158 | 159 | void DFcleanup(struct nlist **DFhashvector) 160 | { 161 | struct nlist *np, *next; 162 | int i; 163 | for (i=0; inext; 167 | free(np->name); 168 | free(np); 169 | } 170 | DFhashvector[i] = 0; 171 | } 172 | } 173 | 174 | int remote_mount(const char *device, const char *type) 175 | { 176 | /* From ME_REMOTE macro in mountlist.h: 177 | A file system is `remote' if its Fs_name contains a `:' 178 | or if (it is of type smbfs and its Fs_name starts with `//'). */ 179 | return ((strchr(device,':') != 0) 180 | || (!strcmp(type, "smbfs") && device[0]=='/' && device[1]=='/') 181 | || (!strncmp(type, "nfs", 3)) || (!strcmp(type, "autofs")) 182 | || (!strcmp(type,"gfs")) || (!strcmp(type,"none")) ); 183 | } 184 | 185 | float device_space(struct nlist **DFhashvector, char *mount, char *device, double *total_size, double *total_free) 186 | { 187 | struct statvfs svfs; 188 | double blocksize; 189 | double free; 190 | double size; 191 | /* The percent used: used/total * 100 */ 192 | float pct=0.0; 193 | 194 | /* Avoid multiply-mounted disks - not done in df. */ 195 | if (seen_before(DFhashvector, device)) return pct; 196 | 197 | if (statvfs(mount, &svfs)) { 198 | /* Ignore funky devices... */ 199 | return pct; 200 | } 201 | 202 | free = svfs.f_bavail; 203 | size = svfs.f_blocks; 204 | blocksize = svfs.f_bsize; 205 | /* Keep running sum of total used, free local disk space. */ 206 | *total_size += size * blocksize; 207 | *total_free += free * blocksize; 208 | /* The percentage of space used on this partition. */ 209 | pct = size ? ((size - free) / (float) size) * 100 : 0.0; 210 | return pct; 211 | } 212 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /c/darwin.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "info.h" 9 | 10 | #define LEN 20 11 | #define MNT_IGNORE 0 12 | 13 | /* Internal Declarations */ 14 | static size_t regetmntinfo(struct statfs **mntbufp, 15 | long mntsize, const char **vfslist); 16 | static const char **makevfslist(char *fslist); 17 | static int checkvfsname(const char *vfsname, const char **vfslist); 18 | static char *makenetvfslist(void); 19 | 20 | static int skipvfs; 21 | 22 | /* Get information */ 23 | const char *get_os_type(void) { 24 | char *buf; 25 | size_t len; 26 | int mib[2]; 27 | 28 | mib[0] = CTL_KERN; 29 | mib[1] = KERN_OSTYPE; 30 | buf = malloc(LEN); 31 | len = LEN; 32 | 33 | if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) { 34 | // At this point `len` is _most likely_ set to `0` (e.g. in case of ENOMEM). 35 | // We copy our string and reset the length. 36 | strncpy(buf, "Darwin", LEN); 37 | len = LEN; 38 | } 39 | 40 | // Ensure null termination at the end. 41 | buf[len-1] = '\0'; 42 | 43 | return buf; 44 | } 45 | 46 | const char *get_os_release(void) { 47 | char *buf; 48 | size_t len; 49 | int mib[2]; 50 | 51 | mib[0] = CTL_KERN; 52 | mib[1] = KERN_OSRELEASE; 53 | buf = malloc(LEN); 54 | len = LEN; 55 | 56 | if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) { 57 | // At this point `len` is _most likely_ set to `0` (e.g. in case of ENOMEM). 58 | // We copy our string and reset the length. 59 | strncpy(buf, "Unknown", LEN); 60 | len = LEN; 61 | } 62 | 63 | // Ensure null termination at the end. 64 | buf[len-1] = '\0'; 65 | 66 | return buf; 67 | } 68 | 69 | unsigned int get_cpu_num(void) { 70 | unsigned int num; 71 | int mib[2]; 72 | size_t len; 73 | 74 | mib[0] = CTL_HW; 75 | mib[1] = HW_NCPU; 76 | len = sizeof(num); 77 | 78 | if (sysctl(mib, 2, &num, &len, NULL, 0) == -1 || !len) 79 | num = 1; 80 | 81 | return num; 82 | } 83 | 84 | unsigned long get_cpu_speed(void) { 85 | unsigned long speed; 86 | size_t len; 87 | 88 | len = sizeof(speed); 89 | sysctlbyname("hw.cpufrequency", &speed, &len, NULL, 0); 90 | speed /= 1000000; 91 | 92 | return speed; 93 | } 94 | 95 | unsigned long get_proc_total(void) { 96 | int mib[3]; 97 | size_t len; 98 | 99 | mib[0] = CTL_KERN; 100 | mib[1] = KERN_PROC; 101 | mib[2] = KERN_PROC_ALL; 102 | 103 | sysctl(mib, 3, NULL, &len, NULL, 0); 104 | 105 | return len / sizeof(struct kinfo_proc); 106 | } 107 | 108 | MemInfo get_mem_info(void) { 109 | static unsigned long long size = 0; 110 | size_t len; 111 | int mib[2]; 112 | vm_statistics_data_t vm_stat; 113 | struct xsw_usage swap_info; 114 | mach_msg_type_number_t count = HOST_VM_INFO_COUNT; 115 | MemInfo mi; 116 | 117 | if (size == 0) { 118 | mib[0] = CTL_HW; 119 | mib[1] = HW_MEMSIZE; 120 | len = sizeof(size); 121 | sysctl(mib, 2, &size, &len, NULL, 0); 122 | size /= 1024; 123 | } 124 | 125 | mib[0] = CTL_VM; 126 | mib[1] = VM_SWAPUSAGE; 127 | len = sizeof(swap_info); 128 | sysctl(mib, 2 , &swap_info, &len, NULL, 0); 129 | 130 | host_statistics(mach_host_self(), HOST_VM_INFO, 131 | (host_info_t)&vm_stat, &count); 132 | 133 | mi.total = size; 134 | mi.avail = (vm_stat.free_count + vm_stat.inactive_count) * PAGE_SIZE / 1024; 135 | mi.free = (vm_stat.free_count - vm_stat.speculative_count) * PAGE_SIZE / 1024; 136 | mi.buffers = 0; 137 | mi.cached = 0; 138 | mi.swap_total = swap_info.xsu_total / 1024; 139 | mi.swap_free = swap_info.xsu_avail / 1024; 140 | 141 | return mi; 142 | } 143 | 144 | DiskInfo get_disk_info(void) { 145 | DiskInfo di; 146 | struct statfs *mntbuf; 147 | const char **vfslist; 148 | char *str; 149 | size_t i, mntsize; 150 | size_t used, availblks; 151 | const double reported_units = 1e9; 152 | float pct; 153 | float most_full = 0.0; 154 | double toru, dtotal = 0.0, dfree = 0.0; 155 | 156 | str = makenetvfslist(); 157 | vfslist = makevfslist(str); 158 | free(str); 159 | 160 | mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 161 | mntsize = regetmntinfo(&mntbuf, mntsize, vfslist); 162 | 163 | for (i = 0; i < mntsize; i++) { 164 | if ((mntbuf[i].f_flags & MNT_IGNORE) == 0) { 165 | used = mntbuf[i].f_blocks - mntbuf[i].f_bfree; 166 | availblks = mntbuf[i].f_bavail + used; 167 | pct = (availblks == 0 ? 100.0 : 168 | (double)used / (double)availblks * 100.0); 169 | if (pct > most_full) 170 | most_full = pct; 171 | toru = reported_units / mntbuf[i].f_bsize; 172 | dtotal += mntbuf[i].f_blocks / toru; 173 | dfree += mntbuf[i].f_bavail / toru; 174 | } 175 | } 176 | 177 | free(vfslist); 178 | di.total = dtotal * 1000000; 179 | di.free = dfree * 1000000; 180 | return di; 181 | } 182 | 183 | /* Internal definitions */ 184 | const char **makevfslist(char *fslist) { 185 | const char **av; 186 | int i; 187 | char *nextcp; 188 | 189 | if (fslist == NULL) 190 | return (NULL); 191 | if (fslist[0] == 'n' && fslist[1] == 'o') { 192 | fslist += 2; 193 | skipvfs = 1; 194 | } 195 | for (i = 0, nextcp = fslist; *nextcp; nextcp++) 196 | if (*nextcp == ',') 197 | i++; 198 | if ((av = (const char**)malloc((size_t)(i + 2) * sizeof(char *))) == NULL) { 199 | return (NULL); 200 | } 201 | nextcp = fslist; 202 | i = 0; 203 | av[i++] = nextcp; 204 | while ((nextcp = strchr(nextcp, ',')) != NULL) { 205 | *nextcp++ = '\0'; 206 | av[i++] = nextcp; 207 | } 208 | av[i++] = NULL; 209 | 210 | return (av); 211 | 212 | } 213 | 214 | size_t regetmntinfo(struct statfs **mntbufp, long mntsize, 215 | const char **vfslist) { 216 | int i, j; 217 | struct statfs *mntbuf; 218 | 219 | if (vfslist == NULL) 220 | return (getmntinfo(mntbufp, MNT_WAIT)); 221 | 222 | mntbuf = *mntbufp; 223 | for (j = 0, i = 0; i < mntsize; i++) { 224 | if (checkvfsname(mntbuf[i].f_fstypename, vfslist)) 225 | continue; 226 | (void)statfs(mntbuf[i].f_mntonname,&mntbuf[j]); 227 | j++; 228 | } 229 | return (j); 230 | } 231 | 232 | int checkvfsname(const char *vfsname, const char **vfslist) { 233 | 234 | if (vfslist == NULL) 235 | return (0); 236 | while (*vfslist != NULL) { 237 | if (strcmp(vfsname, *vfslist) == 0) 238 | return (skipvfs); 239 | ++vfslist; 240 | } 241 | return (!skipvfs); 242 | } 243 | 244 | char *makenetvfslist(void){ 245 | char *str, *strptr, **listptr; 246 | int mib[4], maxvfsconf, cnt=0, i; 247 | size_t miblen; 248 | struct vfsconf vfc; 249 | 250 | mib[0] = CTL_VFS; 251 | mib[1] = VFS_GENERIC; 252 | mib[2] = VFS_MAXTYPENUM; 253 | miblen=sizeof(maxvfsconf); 254 | if (sysctl(mib, 3, &maxvfsconf, &miblen, NULL, 0)) { 255 | return (NULL); 256 | } 257 | 258 | if ((listptr = (char**)malloc(sizeof(char*)*maxvfsconf)) == NULL) { 259 | return (NULL); 260 | } 261 | 262 | miblen = sizeof (struct vfsconf); 263 | mib[2] = VFS_CONF; 264 | for (i = 0; i < maxvfsconf; i++) { 265 | mib[3] = i; 266 | if (sysctl(mib, 4, &vfc, &miblen, NULL, 0) == 0) { 267 | if (!(vfc.vfc_flags & MNT_LOCAL)) { 268 | listptr[cnt++] = strdup(vfc.vfc_name); 269 | if (listptr[cnt-1] == NULL) { 270 | return (NULL); 271 | } 272 | } 273 | } 274 | } 275 | 276 | if (cnt == 0 || 277 | (str = (char*)malloc(sizeof(char) * (32 * cnt + cnt + 2))) == NULL) { 278 | free(listptr); 279 | return (NULL); 280 | } 281 | 282 | *str = 'n'; 283 | *(str + 1) = 'o'; 284 | for (i = 0, strptr = str + 2; i < cnt; i++, strptr++) { 285 | strncpy(strptr, listptr[i], 32); 286 | strptr += strlen(listptr[i]); 287 | *strptr = ','; 288 | free(listptr[i]); 289 | } 290 | *(--strptr) = '\0'; 291 | free(listptr); 292 | return (str); 293 | } 294 | 295 | 296 | 297 | -------------------------------------------------------------------------------- /kstat.rs: -------------------------------------------------------------------------------- 1 | #![cfg(any(target_os = "solaris", target_os = "illumos"))] 2 | 3 | type Result = std::result::Result>; 4 | 5 | // There is presently no static CStr constructor, so use these constants with 6 | // the c() wrapper below: 7 | const MODULE_CPU_INFO: &[u8] = b"cpu_info\0"; 8 | 9 | const STAT_CLOCK_MHZ: &[u8] = b"clock_MHz\0"; 10 | 11 | const MODULE_UNIX: &[u8] = b"unix\0"; 12 | 13 | const NAME_SYSTEM_MISC: &[u8] = b"system_misc\0"; 14 | const STAT_BOOT_TIME: &[u8] = b"boot_time\0"; 15 | const STAT_NPROC: &[u8] = b"nproc\0"; 16 | 17 | const NAME_SYSTEM_PAGES: &[u8] = b"system_pages\0"; 18 | const STAT_FREEMEM: &[u8] = b"freemem\0"; 19 | const STAT_PHYSMEM: &[u8] = b"physmem\0"; 20 | 21 | fn c(buf: &[u8]) -> &std::ffi::CStr { 22 | std::ffi::CStr::from_bytes_with_nul(buf).expect("invalid string constant") 23 | } 24 | 25 | 26 | mod wrapper { 27 | use std::os::raw::c_int; 28 | use std::os::raw::c_uint; 29 | use std::os::raw::c_char; 30 | use std::os::raw::c_uchar; 31 | use std::os::raw::c_void; 32 | use std::os::raw::c_long; 33 | use std::os::raw::c_ulong; 34 | use std::os::raw::c_longlong; 35 | use std::ptr::{null, null_mut, NonNull}; 36 | use std::ffi::CStr; 37 | 38 | const KSTAT_TYPE_NAMED: c_uchar = 1; 39 | 40 | const KSTAT_STRLEN: usize = 31; 41 | 42 | #[repr(C)] 43 | struct Kstat { 44 | ks_crtime: c_longlong, 45 | ks_next: *mut Kstat, 46 | ks_kid: c_uint, 47 | ks_module: [c_char; KSTAT_STRLEN], 48 | ks_resv: c_uchar, 49 | ks_instance: c_int, 50 | ks_name: [c_char; KSTAT_STRLEN], 51 | ks_type: c_uchar, 52 | ks_class: [c_char; KSTAT_STRLEN], 53 | ks_flags: c_uchar, 54 | ks_data: *mut c_void, 55 | ks_ndata: c_uint, 56 | ks_data_size: usize, 57 | ks_snaptime: c_longlong, 58 | } 59 | 60 | impl Kstat { 61 | fn name(&self) -> &CStr { 62 | unsafe { CStr::from_ptr(self.ks_name.as_ptr()) } 63 | } 64 | 65 | fn module(&self) -> &CStr { 66 | unsafe { CStr::from_ptr(self.ks_module.as_ptr()) } 67 | } 68 | } 69 | 70 | #[repr(C)] 71 | struct KstatCtl { 72 | kc_chain_id: c_uint, 73 | kc_chain: *mut Kstat, 74 | kc_kd: c_int, 75 | } 76 | 77 | #[repr(C)] 78 | #[derive(Copy, Clone)] 79 | union KstatValue { 80 | c: [c_char; 16], 81 | l: c_long, 82 | ul: c_ulong, 83 | ui32: u32, 84 | } 85 | 86 | #[repr(C)] 87 | struct KstatNamed { 88 | name: [c_char; KSTAT_STRLEN], 89 | data_type: c_uchar, 90 | value: KstatValue, 91 | } 92 | 93 | extern "C" { 94 | fn kstat_open() -> *mut KstatCtl; 95 | fn kstat_close(kc: *mut KstatCtl) -> c_int; 96 | fn kstat_lookup(kc: *mut KstatCtl, module: *const c_char, 97 | instance: c_int, name: *const c_char) -> *mut Kstat; 98 | fn kstat_read(kc: *mut KstatCtl, ksp: *mut Kstat, buf: *mut c_void) 99 | -> c_int; 100 | fn kstat_data_lookup(ksp: *mut Kstat, name: *const c_char) 101 | -> *mut c_void; 102 | } 103 | 104 | /// Minimal wrapper around libkstat(3LIB) on illumos and Solaris systems. 105 | pub struct KstatWrapper { 106 | kc: NonNull, 107 | ks: Option>, 108 | stepping: bool, 109 | } 110 | 111 | /// Turn an optional CStr into a (const char *) for Some, or NULL for None. 112 | fn cp(p: &Option<&CStr>) -> *const c_char { 113 | p.map_or_else(|| null(), |p| p.as_ptr()) 114 | } 115 | 116 | impl KstatWrapper { 117 | pub fn open() -> super::Result { 118 | let kc = NonNull::new(unsafe { kstat_open() }); 119 | if let Some(kc) = kc { 120 | Ok(KstatWrapper { 121 | kc: kc, 122 | ks: None, 123 | stepping: false, 124 | }) 125 | } else { 126 | let e = std::io::Error::last_os_error(); 127 | Err(format!("kstat_open(3KSTAT) failed: {}", e).into()) 128 | } 129 | } 130 | 131 | /// Call kstat_lookup(3KSTAT) and store the result, if there is a match. 132 | pub fn lookup(&mut self, module: Option<&CStr>, name: Option<&CStr>) { 133 | self.ks = NonNull::new(unsafe { 134 | kstat_lookup(self.kc.as_ptr(), cp(&module), -1, cp(&name)) 135 | }); 136 | 137 | self.stepping = false; 138 | } 139 | 140 | /// Call once to start iterating, and then repeatedly for each 141 | /// additional kstat in the chain. Returns false once there are no more 142 | /// kstat entries. 143 | pub fn step(&mut self) -> bool { 144 | if !self.stepping { 145 | self.stepping = true; 146 | } else { 147 | self.ks = self.ks.map_or(None, 148 | |ks| NonNull::new(unsafe { ks.as_ref() }.ks_next)); 149 | } 150 | 151 | if self.ks.is_none() { 152 | self.stepping = false; 153 | false 154 | } else { 155 | true 156 | } 157 | } 158 | 159 | /// Return the module name of the current kstat. This routine will 160 | /// panic if step() has not returned true. 161 | pub fn module(&self) -> &CStr { 162 | let ks = self.ks.as_ref().expect("step() must return true first"); 163 | unsafe { ks.as_ref() }.module() 164 | } 165 | 166 | /// Return the name of the current kstat. This routine will panic if 167 | /// step() has not returned true. 168 | pub fn name(&self) -> &CStr { 169 | let ks = self.ks.as_ref().expect("step() must return true first"); 170 | unsafe { ks.as_ref() }.name() 171 | } 172 | 173 | /// Look up a named kstat value. For internal use by typed accessors. 174 | fn data_value(&self, statistic: &CStr) -> Option> { 175 | let (ks, ksp) = if let Some(ks) = &self.ks { 176 | (unsafe { ks.as_ref() }, ks.as_ptr()) 177 | } else { 178 | return None; 179 | }; 180 | 181 | if unsafe { kstat_read(self.kc.as_ptr(), ksp, null_mut()) } == -1 { 182 | return None; 183 | } 184 | 185 | if ks.ks_type != KSTAT_TYPE_NAMED || ks.ks_ndata < 1 { 186 | // This is not a named kstat, or it has no data payload. 187 | return None; 188 | } 189 | 190 | NonNull::new(unsafe { 191 | kstat_data_lookup(ksp, cp(&Some(statistic))) 192 | }).map(|voidp| voidp.cast()) 193 | } 194 | 195 | /// Look up a named kstat value and interpret it as a "long_t". 196 | pub fn data_long(&self, statistic: &CStr) -> Option { 197 | self.data_value(statistic).map(|kn| unsafe { 198 | kn.as_ref().value.l 199 | } as i64) 200 | } 201 | 202 | /// Look up a named kstat value and interpret it as a "ulong_t". 203 | pub fn data_ulong(&self, statistic: &CStr) -> Option { 204 | self.data_value(statistic).map(|kn| unsafe { 205 | kn.as_ref().value.ul 206 | } as u64) 207 | } 208 | 209 | /// Look up a named kstat value and interpret it as a "uint32_t". 210 | pub fn data_u32(&self, statistic: &CStr) -> Option { 211 | self.data_value(statistic).map(|kn| unsafe { 212 | kn.as_ref().value.ui32 213 | }) 214 | } 215 | } 216 | 217 | impl Drop for KstatWrapper { 218 | fn drop(&mut self) { 219 | unsafe { kstat_close(self.kc.as_ptr()) }; 220 | } 221 | } 222 | } 223 | 224 | pub fn cpu_mhz() -> Result { 225 | let mut k = wrapper::KstatWrapper::open()?; 226 | 227 | k.lookup(Some(c(MODULE_CPU_INFO)), None); 228 | while k.step() { 229 | if k.module() != c(MODULE_CPU_INFO) { 230 | continue; 231 | } 232 | 233 | if let Some(mhz) = k.data_long(c(STAT_CLOCK_MHZ)) { 234 | return Ok(mhz as u64); 235 | } 236 | } 237 | 238 | return Err("cpu speed kstat not found".into()); 239 | } 240 | 241 | pub fn boot_time() -> Result { 242 | let mut k = wrapper::KstatWrapper::open()?; 243 | 244 | k.lookup(Some(c(MODULE_UNIX)), Some(c(NAME_SYSTEM_MISC))); 245 | while k.step() { 246 | if k.module() != c(MODULE_UNIX) || k.name() != c(NAME_SYSTEM_MISC) { 247 | continue; 248 | } 249 | 250 | if let Some(boot_time) = k.data_u32(c(STAT_BOOT_TIME)) { 251 | return Ok(boot_time as u64); 252 | } 253 | } 254 | 255 | return Err("boot time kstat not found".into()); 256 | } 257 | 258 | pub fn nproc() -> Result { 259 | let mut k = wrapper::KstatWrapper::open()?; 260 | 261 | k.lookup(Some(c(MODULE_UNIX)), Some(c(NAME_SYSTEM_MISC))); 262 | while k.step() { 263 | if k.module() != c(MODULE_UNIX) || k.name() != c(NAME_SYSTEM_MISC) { 264 | continue; 265 | } 266 | 267 | if let Some(nproc) = k.data_u32(c(STAT_NPROC)) { 268 | return Ok(nproc as u64); 269 | } 270 | } 271 | 272 | return Err("process count kstat not found".into()); 273 | } 274 | 275 | pub struct Pages { 276 | pub freemem: u64, 277 | pub physmem: u64, 278 | } 279 | 280 | pub fn pages() -> Result { 281 | let mut k = wrapper::KstatWrapper::open()?; 282 | 283 | k.lookup(Some(c(MODULE_UNIX)), Some(c(NAME_SYSTEM_PAGES))); 284 | while k.step() { 285 | if k.module() != c(MODULE_UNIX) || k.name() != c(NAME_SYSTEM_PAGES) { 286 | continue; 287 | } 288 | 289 | let freemem = k.data_ulong(c(STAT_FREEMEM)); 290 | let physmem = k.data_ulong(c(STAT_PHYSMEM)); 291 | 292 | if freemem.is_some() && physmem.is_some() { 293 | return Ok(Pages { 294 | freemem: freemem.unwrap(), 295 | physmem: physmem.unwrap(), 296 | }); 297 | } 298 | } 299 | 300 | return Err("system pages kstat not available".into()); 301 | } 302 | -------------------------------------------------------------------------------- /lib.rs: -------------------------------------------------------------------------------- 1 | //! #Introduction 2 | //! This crate focuses on geting system information. 3 | //! 4 | //! For now it supports Linux, Mac OS X and Windows. 5 | //! And now it can get information of kernel/cpu/memory/disk/load/hostname and so on. 6 | //! 7 | 8 | extern crate libc; 9 | 10 | use std::ffi; 11 | use std::fmt; 12 | use std::io::{self, Read}; 13 | use std::fs::File; 14 | #[cfg(any(target_os = "windows", target_vendor = "apple", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))] 15 | use std::os::raw::c_char; 16 | #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "haiku")))] 17 | use std::os::raw::{c_int, c_double}; 18 | 19 | #[cfg(any(target_vendor = "apple", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))] 20 | use libc::sysctl; 21 | #[cfg(any(target_vendor = "apple", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))] 22 | use std::mem::size_of_val; 23 | #[cfg(any(target_vendor = "apple", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))] 24 | use std::ptr::null_mut; 25 | #[cfg(not(target_os = "windows"))] 26 | use libc::timeval; 27 | #[cfg(any(target_os = "solaris", target_os = "illumos"))] 28 | use std::time::SystemTime; 29 | #[cfg(target_os = "linux")] 30 | use std::collections::HashMap; 31 | 32 | #[cfg(any(target_os = "solaris", target_os = "illumos"))] 33 | mod kstat; 34 | 35 | #[cfg(any(target_vendor = "apple", target_os="freebsd", target_os = "openbsd", target_os = "netbsd"))] 36 | static OS_CTL_KERN: libc::c_int = 1; 37 | #[cfg(any(target_vendor = "apple", target_os="freebsd", target_os = "openbsd", target_os = "netbsd"))] 38 | static OS_KERN_BOOTTIME: libc::c_int = 21; 39 | 40 | /// System load average value. 41 | #[repr(C)] 42 | #[derive(Debug)] 43 | pub struct LoadAvg { 44 | /// Average load within one minutes. 45 | pub one: f64, 46 | /// Average load within five minutes. 47 | pub five: f64, 48 | /// Average load within fifteen minutes. 49 | pub fifteen: f64, 50 | } 51 | 52 | /// System memory information. 53 | #[repr(C)] 54 | #[derive(Debug)] 55 | pub struct MemInfo { 56 | /// Total physical memory. 57 | pub total: u64, 58 | pub free: u64, 59 | pub avail: u64, 60 | 61 | pub buffers: u64, 62 | pub cached: u64, 63 | 64 | /// Total swap memory. 65 | pub swap_total: u64, 66 | pub swap_free: u64, 67 | } 68 | 69 | /// The os release info of Linux. 70 | /// 71 | /// See [man os-release](https://www.freedesktop.org/software/systemd/man/os-release.html). 72 | #[derive(Debug)] 73 | #[derive(Default)] 74 | pub struct LinuxOSReleaseInfo { 75 | /// A lower-case string (no spaces or other characters outside of 0–9, a–z, ".", "_" and "-") 76 | /// identifying the operating system, excluding any version information and suitable for 77 | /// processing by scripts or usage in generated filenames. 78 | /// 79 | /// Note that we don't verify that the string is lower-case and can be used in file-names. If 80 | /// the /etc/os-release file has an invalid value, you will get this value. 81 | /// 82 | /// If not set, defaults to "ID=linux". Use `self.id()` to fallback to the default. 83 | /// 84 | /// Example: "fedora" or "debian". 85 | pub id: Option, 86 | 87 | /// A space-separated list of operating system identifiers in the same syntax as the ID= 88 | /// setting. It should list identifiers of operating systems that are closely related to the 89 | /// local operating system in regards to packaging and programming interfaces, for example 90 | /// listing one or more OS identifiers the local OS is a derivative from. An OS should 91 | /// generally only list other OS identifiers it itself is a derivative of, and not any OSes 92 | /// that are derived from it, though symmetric relationships are possible. Build scripts and 93 | /// similar should check this variable if they need to identify the local operating system and 94 | /// the value of ID= is not recognized. Operating systems should be listed in order of how 95 | /// closely the local operating system relates to the listed ones, starting with the closest. 96 | /// 97 | /// This field is optional. 98 | /// 99 | /// Example: for an operating system with `ID=centos`, an assignment of `ID_LIKE="rhel fedora"` 100 | /// would be appropriate. For an operating system with `ID=ubuntu`, an assignment of 101 | /// `ID_LIKE=debian` is appropriate. 102 | pub id_like: Option, 103 | 104 | /// A string identifying the operating system, without a version component, and suitable for 105 | /// presentation to the user. 106 | /// 107 | /// If not set, defaults to "NAME=Linux".Use `self.id()` to fallback to the default. 108 | /// 109 | /// Example: "Fedora" or "Debian GNU/Linux". 110 | pub name: Option, 111 | 112 | /// A pretty operating system name in a format suitable for presentation to the user. May or 113 | /// may not contain a release code name or OS version of some kind, as suitable. 114 | /// 115 | /// If not set, defaults to "Linux". Use `self.id()` to fallback to the default. 116 | /// 117 | /// Example: "Fedora 17 (Beefy Miracle)". 118 | pub pretty_name: Option, 119 | 120 | /// A string identifying the operating system version, excluding any OS name information, 121 | /// possibly including a release code name, and suitable for presentation to the user. 122 | /// 123 | /// This field is optional. 124 | /// 125 | /// Example: "17" or "17 (Beefy Miracle)" 126 | pub version: Option, 127 | 128 | /// A lower-case string (mostly numeric, no spaces or other characters outside of 0–9, a–z, 129 | /// ".", "_" and "-") identifying the operating system version, excluding any OS name 130 | /// information or release code name, and suitable for processing by scripts or usage in 131 | /// generated filenames. 132 | /// 133 | /// This field is optional. 134 | /// 135 | /// Example: "17" or "11.04". 136 | pub version_id: Option, 137 | 138 | /// A lower-case string (no spaces or other characters outside of 0–9, a–z, ".", "_" and "-") 139 | /// identifying the operating system release code name, excluding any OS name information or 140 | /// release version, and suitable for processing by scripts or usage in generated filenames. 141 | /// 142 | /// This field is optional and may not be implemented on all systems. 143 | /// 144 | /// Examples: "buster", "xenial". 145 | pub version_codename: Option, 146 | 147 | /// A suggested presentation color when showing the OS name on the console. This should be 148 | /// specified as string suitable for inclusion in the ESC [ m ANSI/ECMA-48 escape code for 149 | /// setting graphical rendition. 150 | /// 151 | /// This field is optional. 152 | /// 153 | /// Example: "0;31" for red, "1;34" for light blue, or "0;38;2;60;110;180" for Fedora blue. 154 | pub ansi_color: Option, 155 | 156 | /// A string, specifying the name of an icon as defined by freedesktop.org Icon Theme 157 | /// Specification. This can be used by graphical applications to display an operating 158 | /// system's or distributor's logo. 159 | /// 160 | /// This field is optional and may not necessarily be implemented on all systems. 161 | /// 162 | /// Examples: "LOGO=fedora-logo", "LOGO=distributor-logo-opensuse". 163 | pub logo: Option, 164 | 165 | /// A CPE name for the operating system, in URI binding syntax, following the Common Platform 166 | /// Enumeration Specification as proposed by the NIST. 167 | /// 168 | /// This field is optional. 169 | /// 170 | /// Example: "cpe:/o:fedoraproject:fedora:17". 171 | pub cpe_name: Option, 172 | 173 | /// A string uniquely identifying the system image used as the origin for a distribution (it is 174 | /// not updated with system updates). The field can be identical between different VERSION_IDs 175 | /// as BUILD_ID is an only a unique identifier to a specific version. Distributions that 176 | /// release each update as a new version would only need to use VERSION_ID as each build is 177 | /// already distinct based on the VERSION_ID. 178 | /// 179 | /// This field is optional. 180 | /// 181 | /// Example: "2013-03-20.3" or "BUILD_ID=201303203". 182 | pub build_id: Option, 183 | 184 | /// A string identifying a specific variant or edition of the operating system suitable for 185 | /// presentation to the user. This field may be used to inform the user that the configuration 186 | /// of this system is subject to a specific divergent set of rules or default configuration 187 | /// settings. 188 | /// 189 | /// This field is optional and may not be implemented on all systems. 190 | /// 191 | /// Examples: "Server Edition", "Smart Refrigerator Edition". 192 | /// 193 | /// Note: this field is for display purposes only. The VARIANT_ID field should be used for 194 | /// making programmatic decisions. 195 | pub variant: Option, 196 | 197 | /// A lower-case string (no spaces or other characters outside of 0–9, a–z, ".", "_" and "-"), 198 | /// identifying a specific variant or edition of the operating system. This may be interpreted 199 | /// by other packages in order to determine a divergent default configuration. 200 | /// 201 | /// This field is optional and may not be implemented on all systems. 202 | /// 203 | /// Examples: "server", "embedded". 204 | pub variant_id: Option, 205 | 206 | /// HOME_URL= should refer to the homepage of the operating system, or alternatively some homepage of 207 | /// the specific version of the operating system. 208 | /// 209 | /// These URLs are intended to be exposed in "About this system" UIs behind links with captions 210 | /// such as "About this Operating System", "Obtain Support", "Report a Bug", or "Privacy 211 | /// Policy". The values should be in RFC3986 format, and should be "http:" or "https:" URLs, 212 | /// and possibly "mailto:" or "tel:". Only one URL shall be listed in each setting. If multiple 213 | /// resources need to be referenced, it is recommended to provide an online landing page 214 | /// linking all available resources. 215 | /// 216 | /// Example: "https://fedoraproject.org/". 217 | pub home_url: Option, 218 | 219 | /// DOCUMENTATION_URL= should refer to the main documentation page for this operating system. 220 | /// 221 | /// See also `home_url`. 222 | pub documentation_url: Option, 223 | 224 | /// SUPPORT_URL= should refer to the main support page for the operating system, if there is 225 | /// any. This is primarily intended for operating systems which vendors provide support for. 226 | /// 227 | /// See also `home_url`. 228 | pub support_url: Option, 229 | 230 | /// BUG_REPORT_URL= should refer to the main bug reporting page for the operating system, if 231 | /// there is any. This is primarily intended for operating systems that rely on community QA. 232 | /// 233 | /// Example: "https://bugzilla.redhat.com/". 234 | /// 235 | /// See also `home_url`. 236 | pub bug_report_url: Option, 237 | 238 | /// PRIVACY_POLICY_URL= should refer to the main privacy policy page for the operating system, 239 | /// if there is any. These settings are optional, and providing only some of these settings is 240 | /// common. 241 | /// 242 | /// See also `home_url`. 243 | pub privacy_policy_url: Option, 244 | } 245 | 246 | macro_rules! os_release_defaults { 247 | ( 248 | $( 249 | $(#[$meta:meta])* 250 | $vis:vis fn $field:ident => $default:literal 251 | )* 252 | ) => { 253 | $( 254 | $(#[$meta])* 255 | $vis fn $field(&self) -> &str { 256 | match self.$field.as_ref() { 257 | Some(value) => value, 258 | None => $default, 259 | } 260 | } 261 | )* 262 | } 263 | } 264 | 265 | impl LinuxOSReleaseInfo { 266 | os_release_defaults!( 267 | /// Returns the value of `self.id` or, if `None`, "linux" (the default value). 268 | pub fn id => "linux" 269 | /// Returns the value of `self.name` or, if `None`, "Linux" (the default value). 270 | pub fn name => "Linux" 271 | /// Returns the value of `self.pretty_name` or, if `None`, "Linux" (the default value). 272 | pub fn pretty_name => "Linux" 273 | ); 274 | } 275 | 276 | /// Disk information. 277 | #[repr(C)] 278 | #[derive(Debug)] 279 | pub struct DiskInfo { 280 | pub total: u64, 281 | pub free: u64, 282 | } 283 | 284 | /// Error types 285 | #[derive(Debug)] 286 | pub enum Error { 287 | UnsupportedSystem, 288 | ExecFailed(io::Error), 289 | IO(io::Error), 290 | SystemTime(std::time::SystemTimeError), 291 | General(String), 292 | Unknown, 293 | } 294 | 295 | impl fmt::Display for Error { 296 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 297 | use self::Error::*; 298 | match *self { 299 | UnsupportedSystem => write!(fmt, "System is not supported"), 300 | ExecFailed(ref e) => write!(fmt, "Execution failed: {}", e), 301 | IO(ref e) => write!(fmt, "IO error: {}", e), 302 | SystemTime(ref e) => write!(fmt, "System time error: {}", e), 303 | General(ref e) => write!(fmt, "Error: {}", e), 304 | Unknown => write!(fmt, "An unknown error occurred"), 305 | } 306 | } 307 | } 308 | 309 | impl std::error::Error for Error { 310 | fn description(&self) -> &str { 311 | use self::Error::*; 312 | match *self { 313 | UnsupportedSystem => "unsupported system", 314 | ExecFailed(_) => "execution failed", 315 | IO(_) => "io error", 316 | SystemTime(_) => "system time", 317 | General(_) => "general error", 318 | Unknown => "unknown error", 319 | } 320 | } 321 | 322 | fn cause(&self) -> Option<&dyn std::error::Error> { 323 | use self::Error::*; 324 | match *self { 325 | UnsupportedSystem => None, 326 | ExecFailed(ref e) => Some(e), 327 | IO(ref e) => Some(e), 328 | SystemTime(ref e) => Some(e), 329 | General(_) => None, 330 | Unknown => None, 331 | } 332 | } 333 | } 334 | 335 | impl From for Error { 336 | fn from(e: io::Error) -> Error { 337 | Error::IO(e) 338 | } 339 | } 340 | 341 | impl From for Error { 342 | fn from(e: std::time::SystemTimeError) -> Error { 343 | Error::SystemTime(e) 344 | } 345 | } 346 | 347 | impl From> for Error { 348 | fn from(e: Box) -> Error { 349 | Error::General(e.to_string()) 350 | } 351 | } 352 | 353 | extern "C" { 354 | #[cfg(any(target_vendor = "apple", target_os = "windows"))] 355 | fn get_os_type() -> *const i8; 356 | #[cfg(any(target_vendor = "apple", target_os = "windows", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))] 357 | fn get_os_release() -> *const i8; 358 | 359 | #[cfg(all(not(any(target_os = "solaris", target_os = "illumos", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd")), any(unix, windows)))] 360 | fn get_cpu_num() -> u32; 361 | #[cfg(any(all(target_vendor = "apple", not(any(target_arch = "aarch64", target_arch = "arm"))), target_os = "windows", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "haiku"))] 362 | fn get_cpu_speed() -> u64; 363 | 364 | #[cfg(target_os = "windows")] 365 | fn get_loadavg() -> LoadAvg; 366 | #[cfg(any(target_vendor = "apple", target_os = "windows", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "haiku"))] 367 | fn get_proc_total() -> u64; 368 | 369 | #[cfg(any(target_vendor = "apple", target_os = "windows", target_os = "haiku"))] 370 | fn get_mem_info() -> MemInfo; 371 | #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))] 372 | fn get_mem_info_bsd(mi: &mut MemInfo) ->i32; 373 | 374 | #[cfg(any(target_os = "linux", target_vendor = "apple", target_os = "windows", target_os = "haiku"))] 375 | fn get_disk_info() -> DiskInfo; 376 | #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))] 377 | fn get_disk_info_bsd(di: &mut DiskInfo) -> i32; 378 | } 379 | 380 | 381 | /// Get operation system type. 382 | /// 383 | /// Such as "Linux", "Darwin", "Windows". 384 | pub fn os_type() -> Result { 385 | #[cfg(target_os = "linux")] 386 | { 387 | let mut s = String::new(); 388 | File::open("/proc/sys/kernel/ostype")?.read_to_string(&mut s)?; 389 | s.pop(); // pop '\n' 390 | Ok(s) 391 | } 392 | #[cfg(any(target_vendor = "apple", target_os = "windows"))] 393 | { 394 | let typ = unsafe { ffi::CStr::from_ptr(get_os_type() as *const c_char).to_bytes() }; 395 | Ok(String::from_utf8_lossy(typ).into_owned()) 396 | } 397 | #[cfg(target_os = "solaris")] 398 | { 399 | Ok("solaris".to_string()) 400 | } 401 | #[cfg(target_os = "illumos")] 402 | { 403 | Ok("illumos".to_string()) 404 | } 405 | #[cfg(target_os = "freebsd")] 406 | { 407 | Ok("freebsd".to_string()) 408 | } 409 | #[cfg(target_os = "openbsd")] 410 | { 411 | Ok("openbsd".to_string()) 412 | } 413 | #[cfg(target_os = "netbsd")] 414 | { 415 | Ok("netbsd".to_string()) 416 | } 417 | #[cfg(target_os = "haiku")] 418 | { 419 | Ok("haiku".to_string()) 420 | } 421 | #[cfg(not(any(target_os = "linux", target_vendor = "apple", target_os = "windows", target_os = "solaris", target_os = "illumos", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "haiku")))] 422 | { 423 | Err(Error::UnsupportedSystem) 424 | } 425 | } 426 | 427 | /// Get operation system release version. 428 | /// 429 | /// Such as "3.19.0-gentoo" 430 | pub fn os_release() -> Result { 431 | #[cfg(target_os = "linux")] 432 | { 433 | let mut s = String::new(); 434 | File::open("/proc/sys/kernel/osrelease")?.read_to_string(&mut s)?; 435 | s.pop(); // pop '\n' 436 | Ok(s) 437 | } 438 | #[cfg(any(target_vendor = "apple", target_os = "windows", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))] 439 | { 440 | unsafe { 441 | let rp = get_os_release() as *const c_char; 442 | if rp == std::ptr::null() { 443 | Err(Error::Unknown) 444 | } else { 445 | let typ = ffi::CStr::from_ptr(rp).to_bytes(); 446 | Ok(String::from_utf8_lossy(typ).into_owned()) 447 | } 448 | } 449 | } 450 | #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "haiku"))] 451 | { 452 | let release: Option = unsafe { 453 | let mut name: libc::utsname = std::mem::zeroed(); 454 | if libc::uname(&mut name) < 0 { 455 | None 456 | } else { 457 | let cstr = std::ffi::CStr::from_ptr(name.release.as_mut_ptr()); 458 | Some(cstr.to_string_lossy().to_string()) 459 | } 460 | }; 461 | match release { 462 | None => Err(Error::Unknown), 463 | Some(release) => Ok(release), 464 | } 465 | } 466 | #[cfg(not(any(target_os = "linux", target_vendor = "apple", target_os = "windows", target_os = "solaris", target_os = "illumos", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "haiku")))] 467 | { 468 | Err(Error::UnsupportedSystem) 469 | } 470 | } 471 | 472 | /// Get the os release note of Linux 473 | /// 474 | /// Information in /etc/os-release, such as name and version of distribution. 475 | /// 476 | /// See `LinuxOSReleaseInfo` for more documentation. 477 | pub fn linux_os_release() -> Result { 478 | if !cfg!(target_os = "linux") { 479 | return Err(Error::UnsupportedSystem); 480 | } 481 | 482 | let mut s = String::new(); 483 | File::open("/etc/os-release")?.read_to_string(&mut s)?; 484 | 485 | let mut info: LinuxOSReleaseInfo = Default::default(); 486 | for l in s.split('\n') { 487 | match parse_line_for_linux_os_release(l.trim().to_string()) { 488 | Some((key, value)) => 489 | match (key.as_ref(), value) { 490 | ("ID", val) => info.id = Some(val), 491 | ("ID_LIKE", val) => info.id_like = Some(val), 492 | ("NAME", val) => info.name = Some(val), 493 | ("PRETTY_NAME", val) => info.pretty_name = Some(val), 494 | 495 | ("VERSION", val) => info.version = Some(val), 496 | ("VERSION_ID", val) => info.version_id = Some(val), 497 | ("VERSION_CODENAME", val) => info.version_codename = Some(val), 498 | 499 | ("ANSI_COLOR", val) => info.ansi_color = Some(val), 500 | ("LOGO", val) => info.logo = Some(val), 501 | 502 | ("CPE_NAME", val) => info.cpe_name = Some(val), 503 | ("BUILD_ID", val) => info.build_id = Some(val), 504 | ("VARIANT", val) => info.variant = Some(val), 505 | ("VARIANT_ID", val) => info.variant_id = Some(val), 506 | 507 | ("HOME_URL", val) => info.home_url = Some(val), 508 | ("BUG_REPORT_URL", val) => info.bug_report_url = Some(val), 509 | ("SUPPORT_URL", val) => info.support_url = Some(val), 510 | ("DOCUMENTATION_URL", val) => info.documentation_url = Some(val), 511 | ("PRIVACY_POLICY_URL", val) => info.privacy_policy_url = Some(val), 512 | _ => {} 513 | } 514 | None => {} 515 | } 516 | } 517 | 518 | Ok(info) 519 | } 520 | 521 | fn parse_line_for_linux_os_release(l: String) -> Option<(String, String)> { 522 | let words: Vec<&str> = l.splitn(2, '=').collect(); 523 | if words.len() < 2 { 524 | return None 525 | } 526 | let mut trim_value = String::from(words[1]); 527 | 528 | if trim_value.starts_with('"') { 529 | trim_value.remove(0); 530 | } 531 | if trim_value.ends_with('"') { 532 | let len = trim_value.len(); 533 | trim_value.remove(len - 1); 534 | } 535 | 536 | return Some((String::from(words[0]), trim_value)) 537 | } 538 | 539 | /// Get cpu num quantity. 540 | /// 541 | /// Notice, it returns the logical cpu quantity. 542 | pub fn cpu_num() -> Result { 543 | #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))] 544 | { 545 | let ret = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) }; 546 | if ret < 1 || ret as i64 > std::u32::MAX as i64 { 547 | Err(Error::IO(io::Error::last_os_error())) 548 | } else { 549 | Ok(ret as u32) 550 | } 551 | } 552 | #[cfg(all(not(any(target_os = "solaris", target_os = "illumos", target_os="freebsd", target_os = "openbsd", target_os = "netbsd")), any(unix, windows)))] 553 | { 554 | unsafe { Ok(get_cpu_num()) } 555 | } 556 | #[cfg(not(any(target_os = "solaris", target_os = "illumos", unix, windows)))] 557 | { 558 | Err(Error::UnsupportedSystem) 559 | } 560 | } 561 | 562 | /// Get cpu speed. 563 | /// 564 | /// Such as 2500, that is 2500 MHz. 565 | pub fn cpu_speed() -> Result { 566 | #[cfg(any(target_os = "solaris", target_os = "illumos"))] 567 | { 568 | Ok(kstat::cpu_mhz()?) 569 | } 570 | #[cfg(target_os = "linux")] 571 | { 572 | // /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq 573 | let mut s = String::new(); 574 | File::open("/proc/cpuinfo")?.read_to_string(&mut s)?; 575 | 576 | let find_cpu_mhz = s.split('\n').find(|line| 577 | line.starts_with("cpu MHz\t") || 578 | line.starts_with("BogoMIPS") || 579 | line.starts_with("clock\t") || 580 | line.starts_with("bogomips per cpu") 581 | ); 582 | 583 | find_cpu_mhz.and_then(|line| line.split(':').last()) 584 | .and_then(|val| val.replace("MHz", "").trim().parse::().ok()) 585 | .map(|speed| speed as u64) 586 | .ok_or(Error::Unknown) 587 | } 588 | #[cfg(any(all(target_vendor = "apple", not(any(target_arch = "aarch64", target_arch = "arm"))), target_os = "windows", target_os = "haiku"))] 589 | { 590 | unsafe { Ok(get_cpu_speed()) } 591 | } 592 | #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))] 593 | { 594 | let res: u64 = unsafe { get_cpu_speed() }; 595 | match res { 596 | 0 => Err(Error::IO(io::Error::last_os_error())), 597 | _ => Ok(res), 598 | } 599 | } 600 | #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "linux", all(target_vendor = "apple", not(any(target_arch = "aarch64", target_arch = "arm"))), target_os = "windows", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "haiku")))] 601 | { 602 | Err(Error::UnsupportedSystem) 603 | } 604 | } 605 | 606 | /// Get system load average value. 607 | /// 608 | /// Notice, on windows, one/five/fifteen of the LoadAvg returned are the current load. 609 | pub fn loadavg() -> Result { 610 | #[cfg(target_os = "linux")] 611 | { 612 | let mut s = String::new(); 613 | File::open("/proc/loadavg")?.read_to_string(&mut s)?; 614 | let loads = s.trim().split(' ') 615 | .take(3) 616 | .map(|val| val.parse::().unwrap()) 617 | .collect::>(); 618 | Ok(LoadAvg { 619 | one: loads[0], 620 | five: loads[1], 621 | fifteen: loads[2], 622 | }) 623 | } 624 | #[cfg(any(target_os = "solaris", target_os = "illumos", target_vendor = "apple", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))] 625 | { 626 | let mut l: [c_double; 3] = [0f64; 3]; 627 | if unsafe { libc::getloadavg(l.as_mut_ptr(), l.len() as c_int) } < 3 { 628 | Err(Error::Unknown) 629 | } else { 630 | Ok(LoadAvg { 631 | one: l[0], 632 | five: l[1], 633 | fifteen: l[2], 634 | }) 635 | } 636 | } 637 | #[cfg(any(target_os = "windows"))] 638 | { 639 | Ok(unsafe { get_loadavg() }) 640 | } 641 | #[cfg(not(any(target_os = "linux", target_os = "solaris", target_os = "illumos", target_vendor = "apple", target_os = "windows", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd")))] 642 | { 643 | Err(Error::UnsupportedSystem) 644 | } 645 | } 646 | 647 | /// Get current processes quantity. 648 | pub fn proc_total() -> Result { 649 | #[cfg(any(target_os = "solaris", target_os = "illumos"))] 650 | { 651 | Ok(kstat::nproc()?) 652 | } 653 | #[cfg(target_os = "linux")] 654 | { 655 | let mut s = String::new(); 656 | File::open("/proc/loadavg")?.read_to_string(&mut s)?; 657 | s.split(' ') 658 | .nth(3) 659 | .and_then(|val| val.split('/').last()) 660 | .and_then(|val| val.parse::().ok()) 661 | .ok_or(Error::Unknown) 662 | } 663 | #[cfg(any(target_vendor = "apple", target_os = "windows", target_os = "haiku"))] 664 | { 665 | Ok(unsafe { get_proc_total() }) 666 | } 667 | #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))] 668 | { 669 | let res: u64 = unsafe { get_proc_total() }; 670 | match res { 671 | 0 => Err(Error::IO(io::Error::last_os_error())), 672 | _ => Ok(res), 673 | } 674 | } 675 | #[cfg(not(any(target_os = "linux", target_os = "solaris", target_os = "illumos", target_vendor = "apple", target_os = "windows", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "haiku")))] 676 | { 677 | Err(Error::UnsupportedSystem) 678 | } 679 | } 680 | 681 | #[cfg(any(target_os = "solaris", target_os = "illumos"))] 682 | fn pagesize() -> Result { 683 | let ret = unsafe { libc::sysconf(libc::_SC_PAGESIZE) }; 684 | if ret < 1 || ret > std::u32::MAX as i64 { 685 | Err(Error::Unknown) 686 | } else { 687 | Ok(ret as u32) 688 | } 689 | } 690 | 691 | /// Get memory information. 692 | /// 693 | /// On Mac OS X and Windows, the buffers and cached variables of the MemInfo returned are zero. 694 | pub fn mem_info() -> Result { 695 | #[cfg(target_os = "linux")] 696 | { 697 | let mut s = String::new(); 698 | File::open("/proc/meminfo")?.read_to_string(&mut s)?; 699 | let mut meminfo_hashmap = HashMap::new(); 700 | for line in s.lines() { 701 | let mut split_line = line.split_whitespace(); 702 | let label = split_line.next(); 703 | let value = split_line.next(); 704 | if value.is_some() && label.is_some() { 705 | let label = label.unwrap().split(':').nth(0).ok_or(Error::Unknown)?; 706 | let value = value.unwrap().parse::().ok().ok_or(Error::Unknown)?; 707 | meminfo_hashmap.insert(label, value); 708 | } 709 | } 710 | let total = *meminfo_hashmap.get("MemTotal").ok_or(Error::Unknown)?; 711 | let free = *meminfo_hashmap.get("MemFree").ok_or(Error::Unknown)?; 712 | let buffers = *meminfo_hashmap.get("Buffers").ok_or(Error::Unknown)?; 713 | let cached = *meminfo_hashmap.get("Cached").ok_or(Error::Unknown)?; 714 | let avail = meminfo_hashmap.get("MemAvailable").map(|v| v.clone()).or_else(|| { 715 | let sreclaimable = *meminfo_hashmap.get("SReclaimable")?; 716 | let shmem = *meminfo_hashmap.get("Shmem")?; 717 | Some(free + buffers + cached + sreclaimable - shmem) 718 | }).ok_or(Error::Unknown)?; 719 | let swap_total = *meminfo_hashmap.get("SwapTotal").ok_or(Error::Unknown)?; 720 | let swap_free = *meminfo_hashmap.get("SwapFree").ok_or(Error::Unknown)?; 721 | Ok(MemInfo { 722 | total, 723 | free, 724 | avail, 725 | buffers, 726 | cached, 727 | swap_total, 728 | swap_free, 729 | }) 730 | } 731 | #[cfg(any(target_os = "solaris", target_os = "illumos"))] 732 | { 733 | let pagesize = pagesize()? as u64; 734 | let pages = kstat::pages()?; 735 | return Ok(MemInfo { 736 | total: pages.physmem * pagesize / 1024, 737 | avail: 0, 738 | free: pages.freemem * pagesize / 1024, 739 | cached: 0, 740 | buffers: 0, 741 | swap_total: 0, 742 | swap_free: 0, 743 | }); 744 | } 745 | #[cfg(any(target_vendor = "apple", target_os = "windows", target_os = "haiku"))] 746 | { 747 | Ok(unsafe { get_mem_info() }) 748 | } 749 | #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))] 750 | { 751 | let mut mi:MemInfo = MemInfo{total: 0, free: 0, avail: 0, buffers: 0, 752 | cached: 0, swap_total: 0, swap_free: 0}; 753 | let res: i32 = unsafe { get_mem_info_bsd(&mut mi) }; 754 | match res { 755 | -1 => Err(Error::IO(io::Error::last_os_error())), 756 | 0 => Ok(mi), 757 | _ => Err(Error::Unknown), 758 | } 759 | } 760 | #[cfg(not(any(target_os = "linux", target_os = "solaris", target_os = "illumos", target_vendor = "apple", target_os = "windows", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "haiku")))] 761 | { 762 | Err(Error::UnsupportedSystem) 763 | } 764 | } 765 | 766 | /// Get disk information. 767 | /// 768 | /// Notice, it just calculate current disk on Windows. 769 | pub fn disk_info() -> Result { 770 | #[cfg(any(target_os = "linux", target_vendor = "apple", target_os = "windows", target_os = "haiku"))] 771 | { 772 | Ok(unsafe { get_disk_info() }) 773 | } 774 | #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))] 775 | { 776 | let mut di:DiskInfo = DiskInfo{total: 0, free: 0}; 777 | let res: i32 = unsafe { get_disk_info_bsd(&mut di) }; 778 | match res { 779 | -1 => Err(Error::IO(io::Error::last_os_error())), 780 | 0 => Ok(di), 781 | _ => Err(Error::Unknown), 782 | } 783 | } 784 | #[cfg(not(any(target_os = "linux", target_vendor = "apple", target_os = "windows", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "haiku")))] 785 | { 786 | Err(Error::UnsupportedSystem) 787 | } 788 | } 789 | 790 | /// Get hostname. 791 | #[cfg(target_family = "unix")] 792 | pub fn hostname() -> Result { 793 | unsafe { 794 | let buf_size = libc::sysconf(libc::_SC_HOST_NAME_MAX) as usize; 795 | let mut buf = Vec::::with_capacity(buf_size + 1); 796 | if libc::gethostname(buf.as_mut_ptr() as *mut libc::c_char, buf_size) < 0 { 797 | return Err(Error::IO(io::Error::last_os_error())); 798 | } 799 | let hostname_len = libc::strnlen(buf.as_ptr() as *const libc::c_char, buf_size); 800 | buf.set_len(hostname_len); 801 | Ok(ffi::CString::new(buf).unwrap().into_string().unwrap()) 802 | } 803 | } 804 | 805 | #[cfg(target_family = "windows")] 806 | pub fn hostname() -> Result { 807 | use std::process::Command; 808 | Command::new("hostname") 809 | .output() 810 | .map_err(Error::ExecFailed) 811 | .map(|output| String::from_utf8(output.stdout).unwrap().trim().to_string()) 812 | } 813 | 814 | /// Get system boottime 815 | #[cfg(not(windows))] 816 | pub fn boottime() -> Result { 817 | let mut bt = timeval { 818 | tv_sec: 0, 819 | tv_usec: 0 820 | }; 821 | 822 | #[cfg(any(target_os = "linux", target_os="android"))] 823 | { 824 | let mut s = String::new(); 825 | File::open("/proc/uptime")?.read_to_string(&mut s)?; 826 | let secs = s.trim().split(' ') 827 | .take(2) 828 | .map(|val| val.parse::().unwrap()) 829 | .collect::>(); 830 | bt.tv_sec = secs[0] as libc::time_t; 831 | bt.tv_usec = secs[1] as libc::suseconds_t; 832 | return Ok(bt); 833 | } 834 | #[cfg(any(target_vendor = "apple", target_os="freebsd", target_os = "openbsd", target_os = "netbsd"))] 835 | { 836 | let mut mib = [OS_CTL_KERN, OS_KERN_BOOTTIME]; 837 | let mut size: libc::size_t = size_of_val(&bt) as libc::size_t; 838 | unsafe { 839 | if sysctl(&mut mib[0], 2, 840 | &mut bt as *mut timeval as *mut libc::c_void, 841 | &mut size, null_mut(), 0) == -1 { 842 | return Err(Error::IO(io::Error::last_os_error())); 843 | } else { 844 | return Ok(bt); 845 | } 846 | } 847 | } 848 | #[cfg(any(target_os = "solaris", target_os = "illumos"))] 849 | { 850 | let start = kstat::boot_time()?; 851 | let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?; 852 | let now = now.as_secs(); 853 | if now < start { 854 | return Err(Error::General("time went backwards".into())); 855 | } 856 | bt.tv_sec = (now - start) as i64; 857 | return Ok(bt); 858 | } 859 | 860 | #[cfg(target_os = "haiku")] 861 | { 862 | unsafe { 863 | let mut sysinfo: libc::system_info = std::mem::zeroed(); 864 | if libc::get_system_info(&mut sysinfo) == libc::B_OK { 865 | let mut now: libc::time_t = 0; 866 | libc::time(&mut now); 867 | bt.tv_sec = now - (sysinfo.boot_time / 1000000); 868 | bt.tv_usec = (sysinfo.boot_time % 1000000) as libc::suseconds_t; 869 | return Ok(bt); 870 | } 871 | } 872 | return Err(Error::IO(io::Error::last_os_error())); 873 | } 874 | 875 | #[warn(unreachable_code)] 876 | Err(Error::UnsupportedSystem) 877 | } 878 | 879 | #[cfg(test)] 880 | mod test { 881 | use super::*; 882 | 883 | #[test] 884 | pub fn test_os_type() { 885 | let typ = os_type().unwrap(); 886 | assert!(typ.len() > 0); 887 | println!("os_type(): {}", typ); 888 | } 889 | 890 | #[test] 891 | pub fn test_os_release() { 892 | let release = os_release().unwrap(); 893 | assert!(release.len() > 0); 894 | println!("os_release(): {}", release); 895 | } 896 | 897 | #[test] 898 | pub fn test_cpu_num() { 899 | let num = cpu_num().unwrap(); 900 | assert!(num > 0); 901 | println!("cpu_num(): {}", num); 902 | } 903 | 904 | #[test] 905 | #[cfg(not(all(target_vendor = "apple", target_arch = "aarch64")))] 906 | pub fn test_cpu_speed() { 907 | let speed = cpu_speed().unwrap(); 908 | assert!(speed > 0); 909 | println!("cpu_speed(): {}", speed); 910 | } 911 | 912 | #[test] 913 | pub fn test_loadavg() { 914 | let load = loadavg().unwrap(); 915 | println!("loadavg(): {:?}", load); 916 | } 917 | 918 | #[test] 919 | pub fn test_proc_total() { 920 | let procs = proc_total().unwrap(); 921 | assert!(procs > 0); 922 | println!("proc_total(): {}", procs); 923 | } 924 | 925 | #[test] 926 | pub fn test_mem_info() { 927 | let mem = mem_info().unwrap(); 928 | assert!(mem.total > 0); 929 | println!("mem_info(): {:?}", mem); 930 | } 931 | 932 | #[test] 933 | #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] 934 | pub fn test_disk_info() { 935 | let info = disk_info().unwrap(); 936 | println!("disk_info(): {:?}", info); 937 | } 938 | 939 | #[test] 940 | pub fn test_hostname() { 941 | let host = hostname().unwrap(); 942 | assert!(host.len() > 0); 943 | println!("hostname(): {}", host); 944 | } 945 | 946 | #[test] 947 | #[cfg(not(windows))] 948 | pub fn test_boottime() { 949 | let bt = boottime().unwrap(); 950 | println!("boottime(): {} {}", bt.tv_sec, bt.tv_usec); 951 | assert!(bt.tv_sec > 0 || bt.tv_usec > 0); 952 | } 953 | 954 | #[test] 955 | #[cfg(target_os = "linux")] 956 | pub fn test_linux_os_release() { 957 | let os_release = linux_os_release().unwrap(); 958 | println!("linux_os_release(): {:?}", os_release.name) 959 | } 960 | } 961 | --------------------------------------------------------------------------------