Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/c-api/src/wasi.rs
3068 views
1
//! The WASI embedding API definitions for Wasmtime.
2
3
use crate::wasm_byte_vec_t;
4
use bytes::Bytes;
5
use std::ffi::{CStr, c_char, c_void};
6
use std::fs::File;
7
use std::path::Path;
8
use std::pin::Pin;
9
use std::slice;
10
use std::task::{Context, Poll};
11
use tokio::io::{self, AsyncWrite};
12
use wasmtime::Result;
13
use wasmtime_wasi::WasiCtxBuilder;
14
use wasmtime_wasi::p1::WasiP1Ctx;
15
use wasmtime_wasi_io::streams::StreamError;
16
17
unsafe fn cstr_to_path<'a>(path: *const c_char) -> Option<&'a Path> {
18
CStr::from_ptr(path).to_str().map(Path::new).ok()
19
}
20
21
unsafe fn cstr_to_str<'a>(s: *const c_char) -> Option<&'a str> {
22
CStr::from_ptr(s).to_str().ok()
23
}
24
25
unsafe fn open_file(path: *const c_char) -> Option<File> {
26
File::open(cstr_to_path(path)?).ok()
27
}
28
29
unsafe fn create_file(path: *const c_char) -> Option<File> {
30
File::create(cstr_to_path(path)?).ok()
31
}
32
33
#[repr(C)]
34
pub struct wasi_config_t {
35
builder: WasiCtxBuilder,
36
}
37
38
wasmtime_c_api_macros::declare_own!(wasi_config_t);
39
40
impl wasi_config_t {
41
pub fn into_wasi_ctx(mut self) -> Result<WasiP1Ctx> {
42
Ok(self.builder.build_p1())
43
}
44
}
45
46
#[unsafe(no_mangle)]
47
pub extern "C" fn wasi_config_new() -> Box<wasi_config_t> {
48
Box::new(wasi_config_t {
49
builder: WasiCtxBuilder::new(),
50
})
51
}
52
53
#[unsafe(no_mangle)]
54
pub unsafe extern "C" fn wasi_config_set_argv(
55
config: &mut wasi_config_t,
56
argc: usize,
57
argv: *const *const c_char,
58
) -> bool {
59
for arg in slice::from_raw_parts(argv, argc) {
60
let arg = match CStr::from_ptr(*arg).to_str() {
61
Ok(s) => s,
62
Err(_) => return false,
63
};
64
config.builder.arg(arg);
65
}
66
true
67
}
68
69
#[unsafe(no_mangle)]
70
pub extern "C" fn wasi_config_inherit_argv(config: &mut wasi_config_t) {
71
config.builder.inherit_args();
72
}
73
74
#[unsafe(no_mangle)]
75
pub unsafe extern "C" fn wasi_config_set_env(
76
config: &mut wasi_config_t,
77
envc: usize,
78
names: *const *const c_char,
79
values: *const *const c_char,
80
) -> bool {
81
let names = slice::from_raw_parts(names, envc);
82
let values = slice::from_raw_parts(values, envc);
83
84
for (k, v) in names.iter().zip(values) {
85
let k = match cstr_to_str(*k) {
86
Some(s) => s,
87
None => return false,
88
};
89
let v = match cstr_to_str(*v) {
90
Some(s) => s,
91
None => return false,
92
};
93
config.builder.env(k, v);
94
}
95
true
96
}
97
98
#[unsafe(no_mangle)]
99
pub extern "C" fn wasi_config_inherit_env(config: &mut wasi_config_t) {
100
config.builder.inherit_env();
101
}
102
103
#[unsafe(no_mangle)]
104
pub unsafe extern "C" fn wasi_config_set_stdin_file(
105
config: &mut wasi_config_t,
106
path: *const c_char,
107
) -> bool {
108
let file = match open_file(path) {
109
Some(f) => f,
110
None => return false,
111
};
112
113
let file = tokio::fs::File::from_std(file);
114
let stdin_stream = wasmtime_wasi::cli::AsyncStdinStream::new(file);
115
config.builder.stdin(stdin_stream);
116
117
true
118
}
119
120
#[unsafe(no_mangle)]
121
pub unsafe extern "C" fn wasi_config_set_stdin_bytes(
122
config: &mut wasi_config_t,
123
binary: &mut wasm_byte_vec_t,
124
) {
125
let binary = binary.take();
126
let binary = wasmtime_wasi::p2::pipe::MemoryInputPipe::new(binary);
127
config.builder.stdin(binary);
128
}
129
130
#[unsafe(no_mangle)]
131
pub extern "C" fn wasi_config_inherit_stdin(config: &mut wasi_config_t) {
132
config.builder.inherit_stdin();
133
}
134
135
#[unsafe(no_mangle)]
136
pub unsafe extern "C" fn wasi_config_set_stdout_file(
137
config: &mut wasi_config_t,
138
path: *const c_char,
139
) -> bool {
140
let file = match create_file(path) {
141
Some(f) => f,
142
None => return false,
143
};
144
145
config
146
.builder
147
.stdout(wasmtime_wasi::cli::OutputFile::new(file));
148
149
true
150
}
151
152
#[unsafe(no_mangle)]
153
pub extern "C" fn wasi_config_inherit_stdout(config: &mut wasi_config_t) {
154
config.builder.inherit_stdout();
155
}
156
157
struct CustomOutputStreamInner {
158
foreign_data: crate::ForeignData,
159
callback: extern "C" fn(*mut c_void, *const u8, usize) -> isize,
160
}
161
162
impl CustomOutputStreamInner {
163
pub fn raw_write(&self, buf: &[u8]) -> io::Result<usize> {
164
let wrote = (self.callback)(self.foreign_data.data, buf.as_ptr(), buf.len());
165
166
if wrote >= 0 {
167
Ok(wrote as _)
168
} else {
169
Err(io::Error::from_raw_os_error(wrote.abs() as _))
170
}
171
}
172
}
173
174
#[derive(Clone)]
175
pub struct CustomOutputStream {
176
inner: std::sync::Arc<CustomOutputStreamInner>,
177
}
178
179
impl CustomOutputStream {
180
pub fn new(
181
foreign_data: crate::ForeignData,
182
callback: extern "C" fn(*mut c_void, *const u8, usize) -> isize,
183
) -> Self {
184
Self {
185
inner: std::sync::Arc::new(CustomOutputStreamInner {
186
foreign_data,
187
callback,
188
}),
189
}
190
}
191
}
192
193
#[async_trait::async_trait]
194
impl wasmtime_wasi::p2::Pollable for CustomOutputStream {
195
async fn ready(&mut self) {}
196
}
197
198
#[async_trait::async_trait]
199
impl wasmtime_wasi::p2::OutputStream for CustomOutputStream {
200
fn write(&mut self, bytes: Bytes) -> Result<(), StreamError> {
201
let wrote = self
202
.inner
203
.raw_write(&bytes)
204
.map_err(|e| StreamError::LastOperationFailed(e.into()))?;
205
206
if wrote != bytes.len() {
207
return Err(StreamError::LastOperationFailed(wasmtime::format_err!(
208
"Partial writes in wasip2 implementation are not allowed"
209
)));
210
}
211
212
Ok(())
213
}
214
fn flush(&mut self) -> Result<(), StreamError> {
215
Ok(())
216
}
217
fn check_write(&mut self) -> Result<usize, StreamError> {
218
Ok(usize::MAX)
219
}
220
}
221
222
impl AsyncWrite for CustomOutputStream {
223
fn poll_write(
224
self: Pin<&mut Self>,
225
_cx: &mut Context<'_>,
226
buf: &[u8],
227
) -> Poll<io::Result<usize>> {
228
Poll::Ready(self.inner.raw_write(buf))
229
}
230
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
231
Poll::Ready(Ok(()))
232
}
233
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
234
Poll::Ready(Ok(()))
235
}
236
}
237
238
impl wasmtime_wasi::cli::IsTerminal for CustomOutputStream {
239
fn is_terminal(&self) -> bool {
240
false
241
}
242
}
243
244
impl wasmtime_wasi::cli::StdoutStream for CustomOutputStream {
245
fn async_stream(&self) -> Box<dyn AsyncWrite + Send + Sync> {
246
Box::new(self.clone())
247
}
248
}
249
250
#[unsafe(no_mangle)]
251
pub extern "C" fn wasi_config_set_stdout_custom(
252
config: &mut wasi_config_t,
253
callback: extern "C" fn(*mut c_void, *const u8, usize) -> isize,
254
data: *mut c_void,
255
finalizer: Option<extern "C" fn(*mut c_void)>,
256
) {
257
config.builder.stdout(CustomOutputStream::new(
258
crate::ForeignData { data, finalizer },
259
callback,
260
));
261
}
262
263
#[unsafe(no_mangle)]
264
pub unsafe extern "C" fn wasi_config_set_stderr_file(
265
config: &mut wasi_config_t,
266
path: *const c_char,
267
) -> bool {
268
let file = match create_file(path) {
269
Some(f) => f,
270
None => return false,
271
};
272
273
config
274
.builder
275
.stderr(wasmtime_wasi::cli::OutputFile::new(file));
276
277
true
278
}
279
280
#[unsafe(no_mangle)]
281
pub extern "C" fn wasi_config_inherit_stderr(config: &mut wasi_config_t) {
282
config.builder.inherit_stderr();
283
}
284
285
#[unsafe(no_mangle)]
286
pub extern "C" fn wasi_config_set_stderr_custom(
287
config: &mut wasi_config_t,
288
callback: extern "C" fn(*mut c_void, *const u8, usize) -> isize,
289
data: *mut c_void,
290
finalizer: Option<extern "C" fn(*mut c_void)>,
291
) {
292
config.builder.stderr(CustomOutputStream::new(
293
crate::ForeignData { data, finalizer },
294
callback,
295
));
296
}
297
298
#[unsafe(no_mangle)]
299
pub unsafe extern "C" fn wasi_config_preopen_dir(
300
config: &mut wasi_config_t,
301
path: *const c_char,
302
guest_path: *const c_char,
303
dir_perms: usize,
304
file_perms: usize,
305
) -> bool {
306
let guest_path = match cstr_to_str(guest_path) {
307
Some(p) => p,
308
None => return false,
309
};
310
311
let host_path = match cstr_to_path(path) {
312
Some(p) => p,
313
None => return false,
314
};
315
316
let dir_perms = match wasmtime_wasi::DirPerms::from_bits(dir_perms) {
317
Some(p) => p,
318
None => return false,
319
};
320
321
let file_perms = match wasmtime_wasi::FilePerms::from_bits(file_perms) {
322
Some(p) => p,
323
None => return false,
324
};
325
326
config
327
.builder
328
.preopened_dir(host_path, guest_path, dir_perms, file_perms)
329
.is_ok()
330
}
331
332