Skip to content
This repository was archived by the owner on Mar 7, 2021. It is now read-only.

Commit cf3212c

Browse files
authored
Fixes #13 -- added initial sysctl API (#17)
1 parent 68b0982 commit cf3212c

File tree

9 files changed

+382
-0
lines changed

9 files changed

+382
-0
lines changed

.travis.yml

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ cache:
99
- directories:
1010
- static-filesystem/target/
1111
- hello-world/target/
12+
- example-sysctl/target/
1213

1314
branches:
1415
only:
@@ -18,6 +19,7 @@ matrix:
1819
include:
1920
- env: MODULE_DIR=hello-world MODULE=helloworld
2021
- env: MODULE_DIR=static-filesystem MODULE=staticfilesystem
22+
- env: MODULE_DIR=example-sysctl MODULE=examplesysctl
2123

2224
install:
2325
- sudo apt-get install -y "linux-headers-$(uname -r)"

example-sysctl/Cargo.toml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "example-sysctl"
3+
version = "0.1.0"
4+
authors = ["Alex Gaynor <[email protected]>", "Geoffrey Thomas <[email protected]>"]
5+
6+
[lib]
7+
crate-type = ["staticlib"]
8+
9+
[dependencies]
10+
linux-kernel-module = { path = ".." }

example-sysctl/Makefile

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
obj-m := examplesysctl.o
2+
examplesysctl-objs := target/x86_64-linux-kernel-module/debug/libexample_sysctl.a
3+
EXTRA_LDFLAGS += --entry=init_module
4+
5+
all:
6+
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(CURDIR)
7+
8+
clean:
9+
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(CURDIR) clean

example-sysctl/src/lib.rs

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#![no_std]
2+
3+
use core::sync::atomic::{AtomicBool, Ordering};
4+
5+
#[macro_use]
6+
extern crate linux_kernel_module;
7+
8+
use linux_kernel_module::sysctl::Sysctl;
9+
use linux_kernel_module::Mode;
10+
11+
struct ExampleSysctlModule {
12+
a: Sysctl<AtomicBool>,
13+
}
14+
15+
impl linux_kernel_module::KernelModule for ExampleSysctlModule {
16+
fn init() -> linux_kernel_module::KernelResult<Self> {
17+
let a = Sysctl::register(
18+
"rust/example\x00",
19+
"a\x00",
20+
AtomicBool::new(false),
21+
Mode::from_int(0o644),
22+
)?;
23+
24+
Ok(ExampleSysctlModule { a })
25+
}
26+
}
27+
28+
impl Drop for ExampleSysctlModule {
29+
fn drop(&mut self) {
30+
println!(
31+
"Current sysctl value: {}",
32+
self.a.get().load(Ordering::Relaxed)
33+
);
34+
}
35+
}
36+
kernel_module!(
37+
ExampleSysctlModule,
38+
author: "Alex Gaynor and Geoffrey Thomas",
39+
description: "A kernel module that offers a sysctl",
40+
license: "GPL"
41+
);

src/helpers.c

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include <linux/bug.h>
22
#include <linux/printk.h>
3+
#include <linux/uaccess.h>
4+
35

46
int printk_helper(const unsigned char *s, int len)
57
{
@@ -10,3 +12,8 @@ void bug_helper(void)
1012
{
1113
BUG();
1214
}
15+
16+
int access_ok_helper(unsigned int mode, const void __user *addr, unsigned long n)
17+
{
18+
return access_ok(mode, addr, n);
19+
}

src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![no_std]
22
#![feature(alloc, global_allocator, allocator_api, const_fn, lang_items, panic_implementation)]
33

4+
#[macro_use]
45
extern crate alloc;
56
#[macro_use]
67
extern crate bitflags;
@@ -14,8 +15,12 @@ mod error;
1415
pub mod filesystem;
1516
#[macro_use]
1617
pub mod printk;
18+
pub mod sysctl;
19+
mod types;
20+
pub mod user_ptr;
1721

1822
pub use error::{Error, KernelResult};
23+
pub use types::Mode;
1924

2025
pub type _InitResult = c_types::c_int;
2126

src/sysctl.rs

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
use alloc::boxed::Box;
2+
use core::mem;
3+
use core::ptr;
4+
use core::sync::atomic;
5+
6+
use bindings;
7+
use c_types;
8+
use error;
9+
use types;
10+
use user_ptr::{UserSlicePtr, UserSlicePtrWriter};
11+
12+
pub struct Sysctl<T: SysctlStorage> {
13+
inner: Box<T>,
14+
table: Box<[bindings::ctl_table]>,
15+
header: *mut bindings::ctl_table_header,
16+
}
17+
18+
pub trait SysctlStorage: Sync {
19+
fn store_value(&self, data: &[u8]) -> (usize, error::KernelResult<()>);
20+
fn read_value(&self, data: &mut UserSlicePtrWriter) -> (usize, error::KernelResult<()>);
21+
}
22+
23+
fn trim_whitespace(mut data: &[u8]) -> &[u8] {
24+
while !data.is_empty() && (data[0] == b' ' || data[0] == b'\t' || data[0] == b'\n') {
25+
data = &data[1..];
26+
}
27+
while !data.is_empty()
28+
&& (data[data.len() - 1] == b' '
29+
|| data[data.len() - 1] == b'\t'
30+
|| data[data.len() - 1] == b'\n')
31+
{
32+
data = &data[..data.len() - 1];
33+
}
34+
return data;
35+
}
36+
37+
impl SysctlStorage for atomic::AtomicBool {
38+
fn store_value(&self, data: &[u8]) -> (usize, error::KernelResult<()>) {
39+
let result = match trim_whitespace(data) {
40+
b"0" => {
41+
self.store(false, atomic::Ordering::Relaxed);
42+
Ok(())
43+
}
44+
b"1" => {
45+
self.store(true, atomic::Ordering::Relaxed);
46+
Ok(())
47+
}
48+
_ => Err(error::Error::EINVAL),
49+
};
50+
return (data.len(), result);
51+
}
52+
53+
fn read_value(&self, data: &mut UserSlicePtrWriter) -> (usize, error::KernelResult<()>) {
54+
let value = if self.load(atomic::Ordering::Relaxed) {
55+
b"1\n"
56+
} else {
57+
b"0\n"
58+
};
59+
(value.len(), data.write(value))
60+
}
61+
}
62+
63+
unsafe extern "C" fn proc_handler<T: SysctlStorage>(
64+
ctl: *mut bindings::ctl_table,
65+
write: c_types::c_int,
66+
buffer: *mut c_types::c_void,
67+
len: *mut usize,
68+
ppos: *mut bindings::loff_t,
69+
) -> c_types::c_int {
70+
// If we're reading from some offset other than the beginning of the file,
71+
// return an empty read to signal EOF.
72+
if *ppos != 0 && write == 0 {
73+
*len = 0;
74+
return 0;
75+
}
76+
77+
let data = match UserSlicePtr::new(buffer, *len) {
78+
Ok(ptr) => ptr,
79+
Err(e) => return e.to_kernel_errno(),
80+
};
81+
let storage = &*((*ctl).data as *const T);
82+
let (bytes_processed, result) = if write != 0 {
83+
let data = match data.read_all() {
84+
Ok(r) => r,
85+
Err(e) => return e.to_kernel_errno(),
86+
};
87+
storage.store_value(&data)
88+
} else {
89+
let mut writer = data.writer();
90+
storage.read_value(&mut writer)
91+
};
92+
*len = bytes_processed;
93+
*ppos += *len as bindings::loff_t;
94+
match result {
95+
Ok(()) => 0,
96+
Err(e) => e.to_kernel_errno(),
97+
}
98+
}
99+
100+
impl<T: SysctlStorage> Sysctl<T> {
101+
pub fn register(
102+
path: &'static str,
103+
name: &'static str,
104+
storage: T,
105+
mode: types::Mode,
106+
) -> error::KernelResult<Sysctl<T>> {
107+
if !path.ends_with('\x00') || !name.ends_with('\x00') || name.contains('/') {
108+
return Err(error::Error::EINVAL);
109+
}
110+
111+
let storage = Box::new(storage);
112+
let mut table = vec![
113+
bindings::ctl_table {
114+
procname: name.as_ptr() as *const i8,
115+
mode: mode.as_int(),
116+
data: &*storage as *const T as *mut c_types::c_void,
117+
proc_handler: Some(proc_handler::<T>),
118+
119+
maxlen: 0,
120+
child: ptr::null_mut(),
121+
poll: ptr::null_mut(),
122+
extra1: ptr::null_mut(),
123+
extra2: ptr::null_mut(),
124+
},
125+
unsafe { mem::zeroed() },
126+
].into_boxed_slice();
127+
128+
let result =
129+
unsafe { bindings::register_sysctl(path.as_ptr() as *const i8, table.as_mut_ptr()) };
130+
if result.is_null() {
131+
return Err(error::Error::ENOMEM);
132+
}
133+
134+
return Ok(Sysctl {
135+
inner: storage,
136+
table: table,
137+
header: result,
138+
});
139+
}
140+
141+
pub fn get(&self) -> &T {
142+
return &self.inner;
143+
}
144+
}
145+
146+
impl<T: SysctlStorage> Drop for Sysctl<T> {
147+
fn drop(&mut self) {
148+
unsafe {
149+
bindings::unregister_sysctl_table(self.header);
150+
}
151+
self.header = ptr::null_mut();
152+
}
153+
}

src/types.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use bindings;
2+
3+
pub struct Mode(bindings::umode_t);
4+
5+
impl Mode {
6+
pub fn from_int(m: u16) -> Mode {
7+
Mode(m)
8+
}
9+
10+
pub fn as_int(&self) -> u16 {
11+
return self.0;
12+
}
13+
}

0 commit comments

Comments
 (0)