├── .gitignore ├── tests └── lib.rs ├── bin └── main.rs ├── Cargo.toml ├── Makefile ├── README.md ├── LICENSE ├── src └── lib.rs └── .github └── workflows └── ci.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /tests/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate uptime_lib; 2 | 3 | #[test] 4 | fn test_uptime_get() { 5 | assert!(uptime_lib::get().is_ok()); 6 | } 7 | -------------------------------------------------------------------------------- /bin/main.rs: -------------------------------------------------------------------------------- 1 | extern crate uptime_lib; 2 | 3 | fn main() { 4 | match uptime_lib::get() { 5 | Ok(uptime) => { 6 | println!("uptime: {} seconds", uptime.as_secs_f64()); 7 | } 8 | Err(err) => { 9 | eprintln!("uptime: {}", err); 10 | std::process::exit(1); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uptime_lib" 3 | version = "0.3.1" 4 | authors = ["itchyny "] 5 | description = "Multi-platform uptime library" 6 | repository = "https://github.com/itchyny/uptime-rs" 7 | readme = "README.md" 8 | license = "MIT" 9 | keywords = ["uptime"] 10 | categories = ["os"] 11 | edition = "2021" 12 | 13 | [profile.release] 14 | lto = true 15 | strip = true 16 | codegen-units = 1 17 | 18 | [dependencies] 19 | thiserror = "1.0.61" 20 | 21 | [target.'cfg(not(windows))'.dependencies] 22 | libc = "0.2.155" 23 | 24 | [target.'cfg(windows)'.dependencies] 25 | windows = { version = "0.57.0", features = ["Win32_System_SystemInformation"] } 26 | 27 | [[bin]] 28 | name = "uptime-rs" 29 | path = "bin/main.rs" 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: build 3 | 4 | .PHONY: build 5 | build: 6 | cargo build 7 | 8 | .PHONY: install 9 | install: 10 | cargo install --path . 11 | 12 | .PHONY: test 13 | test: build 14 | cargo test 15 | 16 | .PHONY: clean 17 | clean: 18 | cargo clean 19 | 20 | VERSION := $$(sed -n '/^version =/s/^.* *"\([0-9][0-9.]*\)".*$$/\1/p' Cargo.toml) 21 | GIT_DIFF := $$(git diff --name-only) 22 | 23 | .PHONY: bump 24 | bump: 25 | test -z "$$(git status --porcelain || echo .)" 26 | test "$$(git branch --show-current)" = "main" 27 | @printf "Bump up version in Cargo.toml. Press Enter to proceed: " 28 | @read -n1 29 | @[ "$(GIT_DIFF)" == "Cargo.toml" ] || { \ 30 | echo "Version is not updated or unrelated file is updated:"; \ 31 | [ -z "$(GIT_DIFF)" ] || printf " %s\n" $(GIT_DIFF); \ 32 | exit 1; \ 33 | } 34 | git commit -am "bump up version to $(VERSION)" 35 | git tag "v$(VERSION)" 36 | git push --atomic origin main tag "v$(VERSION)" 37 | 38 | .PHONY: publish 39 | publish: 40 | cargo publish 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multi-platform uptime library for Rust 2 | [![CI Status](https://github.com/itchyny/uptime-rs/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/itchyny/uptime-rs/actions?query=branch:main) 3 | [![crates.io](https://img.shields.io/crates/v/uptime_lib.svg)](https://crates.io/crates/uptime_lib) 4 | [![release](https://img.shields.io/github/release/itchyny/uptime-rs/all.svg)](https://github.com/itchyny/uptime-rs/releases) 5 | [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/itchyny/uptime-rs/blob/main/LICENSE) 6 | 7 | ## Example 8 | 9 | ```rust 10 | extern crate uptime_lib; 11 | 12 | fn main() { 13 | match uptime_lib::get() { 14 | Ok(uptime) => { 15 | println!("uptime: {} seconds", uptime.as_secs_f64()); 16 | } 17 | Err(err) => { 18 | eprintln!("uptime: {}", err); 19 | std::process::exit(1); 20 | } 21 | } 22 | } 23 | ``` 24 | 25 | ## Author 26 | itchyny (https://github.com/itchyny) 27 | 28 | ## License 29 | This software is released under the MIT License, see LICENSE. 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017-2023 itchyny 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 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | #[derive(Debug, thiserror::Error)] 4 | #[cfg_attr(target_os = "windows", allow(unused))] 5 | pub enum Error { 6 | #[cfg(target_os = "linux")] 7 | #[error("sysinfo failed")] 8 | Sysinfo, 9 | 10 | #[cfg(any( 11 | target_os = "macos", 12 | target_os = "freebsd", 13 | target_os = "openbsd", 14 | target_os = "netbsd" 15 | ))] 16 | #[error("sysctl failed")] 17 | Sysctl, 18 | 19 | #[cfg(any( 20 | target_os = "macos", 21 | target_os = "freebsd", 22 | target_os = "openbsd", 23 | target_os = "netbsd" 24 | ))] 25 | #[error(transparent)] 26 | SystemTime(#[from] std::time::SystemTimeError), 27 | } 28 | 29 | #[cfg(target_os = "linux")] 30 | pub fn get() -> Result { 31 | let mut info: libc::sysinfo = unsafe { std::mem::zeroed() }; 32 | let ret = unsafe { libc::sysinfo(&mut info) }; 33 | if ret == 0 { 34 | Ok(Duration::from_secs(info.uptime as u64)) 35 | } else { 36 | Err(Error::Sysinfo) 37 | } 38 | } 39 | 40 | #[cfg(any( 41 | target_os = "macos", 42 | target_os = "freebsd", 43 | target_os = "openbsd", 44 | target_os = "netbsd" 45 | ))] 46 | pub fn get() -> Result { 47 | use std::time::SystemTime; 48 | let mut request = [libc::CTL_KERN, libc::KERN_BOOTTIME]; 49 | let mut boottime: libc::timeval = unsafe { std::mem::zeroed() }; 50 | let mut size: libc::size_t = std::mem::size_of_val(&boottime) as libc::size_t; 51 | let ret = unsafe { 52 | libc::sysctl( 53 | &mut request[0], 54 | 2, 55 | &mut boottime as *mut libc::timeval as *mut libc::c_void, 56 | &mut size, 57 | std::ptr::null_mut(), 58 | 0, 59 | ) 60 | }; 61 | if ret == 0 { 62 | Ok(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)? 63 | - Duration::new(boottime.tv_sec as u64, boottime.tv_usec as u32 * 1000)) 64 | } else { 65 | Err(Error::Sysctl) 66 | } 67 | } 68 | 69 | #[cfg(target_os = "windows")] 70 | // `Result` is needed to match the signature on other platforms 71 | #[allow(clippy::unnecessary_wraps)] 72 | pub fn get() -> Result { 73 | let ret: u64 = unsafe { windows::Win32::System::SystemInformation::GetTickCount64() }; 74 | Ok(Duration::from_millis(ret)) 75 | } 76 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - v* 9 | pull_request: 10 | 11 | permissions: 12 | contents: read 13 | 14 | defaults: 15 | run: 16 | shell: bash --noprofile --norc -euxo pipefail {0} 17 | 18 | jobs: 19 | build: 20 | name: Build 21 | runs-on: ${{ matrix.runs-on }} 22 | strategy: 23 | matrix: 24 | os: [linux, darwin, windows] 25 | arch: [amd64, arm64] 26 | include: 27 | - os: linux 28 | arch: amd64 29 | runs-on: ubuntu-latest 30 | target: x86_64-unknown-linux-gnu 31 | - os: linux 32 | arch: arm64 33 | runs-on: ubuntu-latest 34 | target: aarch64-unknown-linux-gnu 35 | - os: darwin 36 | arch: amd64 37 | runs-on: macos-latest 38 | target: x86_64-apple-darwin 39 | - os: darwin 40 | arch: arm64 41 | runs-on: macos-latest 42 | target: aarch64-apple-darwin 43 | - os: windows 44 | arch: amd64 45 | runs-on: windows-latest 46 | target: x86_64-pc-windows-msvc 47 | - os: windows 48 | arch: arm64 49 | runs-on: windows-latest 50 | target: aarch64-pc-windows-msvc 51 | fail-fast: false 52 | steps: 53 | - name: Checkout code 54 | uses: actions/checkout@v4 55 | - name: Install target toolchain 56 | run: rustup target add ${{ matrix.target }} 57 | - name: Install cross-compiler for arm64 on Linux 58 | if: matrix.os == 'linux' && matrix.arch == 'arm64' 59 | run: | 60 | sudo apt-get install gcc-aarch64-linux-gnu 61 | echo "RUSTFLAGS=-C linker=aarch64-linux-gnu-gcc" >> "$GITHUB_ENV" 62 | - name: Cache dependencies 63 | uses: Swatinem/rust-cache@v2 64 | - name: Build 65 | run: cargo build --release --target=${{ matrix.target }} 66 | - name: Set executable name 67 | id: executable 68 | run: echo name=${{ github.event.repository.name }} >> "$GITHUB_OUTPUT" 69 | - name: Run 70 | if: matrix.os == 'darwin' || matrix.arch == 'amd64' 71 | run: target/${{ matrix.target }}/release/${{ steps.executable.outputs.name }} 72 | - name: Set artifact name 73 | id: artifact 74 | run: echo name=${{ steps.executable.outputs.name }}_${{ startsWith(github.ref, 'refs/tags/v') && github.ref_name || 75 | github.event.pull_request.head.sha || github.sha }}_${{ matrix.os }}_${{ matrix.arch }} >> "$GITHUB_OUTPUT" 76 | - name: Build artifact 77 | run: | 78 | mkdir ${{ steps.artifact.outputs.name }} 79 | cp README.md LICENSE target/${{ matrix.target }}/release/${{ steps.executable.outputs.name }} ${{ steps.artifact.outputs.name }} 80 | if [[ ${{ matrix.os }} == windows ]]; then 81 | powershell Compress-Archive -Path ${{ steps.artifact.outputs.name }} -DestinationPath ${{ steps.artifact.outputs.name }}.zip 82 | else 83 | zip -r ${{ steps.artifact.outputs.name }}.zip ${{ steps.artifact.outputs.name }} 84 | fi 85 | - name: Upload artifact 86 | uses: actions/upload-artifact@v4 87 | with: 88 | name: ${{ steps.artifact.outputs.name }} 89 | path: ${{ steps.artifact.outputs.name }}.zip 90 | 91 | test: 92 | name: Test 93 | runs-on: ${{ matrix.runs-on }} 94 | strategy: 95 | matrix: 96 | runs-on: [ubuntu-latest, macos-latest, windows-latest] 97 | fail-fast: false 98 | steps: 99 | - name: Checkout code 100 | uses: actions/checkout@v4 101 | - name: Cache dependencies 102 | uses: Swatinem/rust-cache@v2 103 | - name: Build 104 | run: cargo build 105 | - name: Clippy 106 | run: cargo clippy 107 | - name: Test 108 | run: cargo test 109 | - name: Run 110 | run: cargo run 111 | 112 | release: 113 | name: Release 114 | needs: [build, test] 115 | if: startsWith(github.ref, 'refs/tags/v') 116 | permissions: 117 | contents: write 118 | runs-on: ubuntu-latest 119 | steps: 120 | - name: Download artifacts 121 | uses: actions/download-artifact@v4 122 | - name: Create release 123 | uses: ncipollo/release-action@v1 124 | with: 125 | name: Release ${{ github.ref_name }} 126 | artifacts: '*/*.zip' 127 | --------------------------------------------------------------------------------