RustForge

foundry

Foundry OS: the operating system built on the Rustbelt kernel

foundry / rust / sys / src / bin / random.rs
2119 bytes ยท raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//! random -- print random bytes from the kernel's entropy source.
//!
//!   random            16 random bytes as hex (a fresh 128-bit id)
//!   random -n 32      32 bytes as hex
//!   random -d 6 [N]   roll N dice with 6 sides (default 1 die)
//!
//! The bytes come from the `getrandom` system call (Linux number 318),
//! which the kernel fills from the virtio-rng device, i.e. from the host's
//! /dev/urandom. /dev/random and /dev/urandom read the same source.
#![no_std]
#![no_main]

#[macro_use]
#[path = "../../../rbrt/ulib.rs"]
mod ulib;

use ulib::*;

fn main() -> i64 {
    match (arg(1), arg(2), arg(3)) {
        (None, ..) => hex(16),
        (Some("-n"), Some(n), None) => match n.parse::<usize>() {
            Ok(n) if n <= 4096 => hex(n),
            _ => usage(),
        },
        (Some("-d"), Some(sides), count) => {
            let sides = sides.parse::<u64>().unwrap_or(0);
            let count = count.map_or(Some(1), |c| c.parse::<u64>().ok()).unwrap_or(0);
            if sides < 2 || count == 0 || count > 100 {
                return usage();
            }
            for i in 0..count {
                print!("{}{}", if i > 0 { " " } else { "" }, roll(sides));
            }
            println!();
            0
        }
        _ => usage(),
    }
}

/// Print `n` random bytes as hex digits.
fn hex(n: usize) -> i64 {
    let mut buf = [0u8; 4096];
    let r = getrandom(&mut buf[..n]);
    if r < 0 {
        eprintln!("random: getrandom: {}", strerror(r));
        return 1;
    }
    for b in &buf[..n] {
        print!("{:02x}", b);
    }
    println!();
    0
}

/// A fair number in 1..=sides. Plain `x % sides` would favour small
/// numbers slightly, so throw away the values from the uneven top part.
fn roll(sides: u64) -> u64 {
    let limit = u64::MAX - u64::MAX % sides;
    loop {
        let mut b = [0u8; 8];
        getrandom(&mut b);
        let x = u64::from_le_bytes(b);
        if x < limit {
            return x % sides + 1;
        }
    }
}

fn usage() -> i64 {
    eprintln!("usage: random | random -n BYTES (max 4096) | random -d SIDES [COUNT]");
    2
}