authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-29 23:27:21-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-30 13:46:09-04:00
log3c8d4e04ea000d087af4e77331340db1c8b1cef3
tree8f6cc5c2e61d2659189f235529e7208b341270e7
parenta870228ab467906ecc663bf89ecd042b49e116f5

std: file system watching for linux


6 files changed, 368 insertions(+), 35 deletions(-)

std/event/fs.zig+206-5
......@@ -20,17 +20,18 @@ pub const Request = struct {
2020 PWriteV: PWriteV,
2121 PReadV: PReadV,
2222 OpenRead: OpenRead,
23 OpenRW: OpenRW,
2324 Close: Close,
2425 WriteFile: WriteFile,
2526 End, // special - means the fs thread should exit
2627
2728 pub const PWriteV = struct {
2829 fd: os.FileHandle,
29 data: []const []const u8,
30 iov: []os.linux.iovec_const,
3031 offset: usize,
3132 result: Error!void,
3233
33 pub const Error = error{};
34 pub const Error = os.File.WriteError;
3435 };
3536
3637 pub const PReadV = struct {
......@@ -50,6 +51,15 @@ pub const Request = struct {
5051 pub const Error = os.File.OpenError;
5152 };
5253
54 pub const OpenRW = struct {
55 /// must be null terminated. TODO https://github.com/ziglang/zig/issues/265
56 path: []const u8,
57 result: Error!os.FileHandle,
58 mode: os.File.Mode,
59
60 pub const Error = os.File.OpenError;
61 };
62
5363 pub const WriteFile = struct {
5464 /// must be null terminated. TODO https://github.com/ziglang/zig/issues/265
5565 path: []const u8,
......@@ -66,7 +76,7 @@ pub const Request = struct {
6676 };
6777};
6878
69/// data - both the outer and inner references - must live until pwritev promise completes.
79/// data - just the inner references - must live until pwritev promise completes.
7080pub async fn pwritev(loop: *event.Loop, fd: os.FileHandle, offset: usize, data: []const []const u8) !void {
7181 //const data_dupe = try mem.dupe(loop.allocator, []const u8, data);
7282 //defer loop.allocator.free(data_dupe);
......@@ -78,13 +88,23 @@ pub async fn pwritev(loop: *event.Loop, fd: os.FileHandle, offset: usize, data:
7888 resume p;
7989 }
8090
91 const iovecs = try loop.allocator.alloc(os.linux.iovec_const, data.len);
92 defer loop.allocator.free(iovecs);
93
94 for (data) |buf, i| {
95 iovecs[i] = os.linux.iovec_const{
96 .iov_base = buf.ptr,
97 .iov_len = buf.len,
98 };
99 }
100
81101 var req_node = RequestNode{
82102 .next = undefined,
83103 .data = Request{
84104 .msg = Request.Msg{
85105 .PWriteV = Request.Msg.PWriteV{
86106 .fd = fd,
87 .data = data,
107 .iov = iovecs,
88108 .offset = offset,
89109 .result = undefined,
90110 },
......@@ -162,12 +182,15 @@ pub async fn openRead(loop: *event.Loop, path: []const u8) os.File.OpenError!os.
162182 resume p;
163183 }
164184
185 const path_with_null = try std.cstr.addNullByte(loop.allocator, path);
186 defer loop.allocator.free(path_with_null);
187
165188 var req_node = RequestNode{
166189 .next = undefined,
167190 .data = Request{
168191 .msg = Request.Msg{
169192 .OpenRead = Request.Msg.OpenRead{
170 .path = path,
193 .path = path_with_null[0..path.len],
171194 .result = undefined,
172195 },
173196 },
......@@ -187,6 +210,48 @@ pub async fn openRead(loop: *event.Loop, path: []const u8) os.File.OpenError!os.
187210 return req_node.data.msg.OpenRead.result;
188211}
189212
213/// Creates if does not exist. Does not truncate.
214pub async fn openReadWrite(
215 loop: *event.Loop,
216 path: []const u8,
217 mode: os.File.Mode,
218) os.File.OpenError!os.FileHandle {
219 // workaround for https://github.com/ziglang/zig/issues/1194
220 var my_handle: promise = undefined;
221 suspend |p| {
222 my_handle = p;
223 resume p;
224 }
225
226 const path_with_null = try std.cstr.addNullByte(loop.allocator, path);
227 defer loop.allocator.free(path_with_null);
228
229 var req_node = RequestNode{
230 .next = undefined,
231 .data = Request{
232 .msg = Request.Msg{
233 .OpenRW = Request.Msg.OpenRW{
234 .path = path_with_null[0..path.len],
235 .mode = mode,
236 .result = undefined,
237 },
238 },
239 .finish = Request.Finish{
240 .TickNode = event.Loop.NextTickNode{
241 .next = undefined,
242 .data = my_handle,
243 },
244 },
245 },
246 };
247
248 suspend |_| {
249 loop.linuxFsRequest(&req_node);
250 }
251
252 return req_node.data.msg.OpenRW.result;
253}
254
190255/// This abstraction helps to close file handles in defer expressions
191256/// without suspending. Start a CloseOperation before opening a file.
192257pub const CloseOperation = struct {
......@@ -302,6 +367,113 @@ pub async fn readFile(loop: *event.Loop, file_path: []const u8, max_size: usize)
302367 }
303368}
304369
370pub const Watch = struct {
371 channel: *event.Channel(Event),
372 putter: promise,
373
374 pub const Event = union(enum) {
375 CloseWrite,
376 Err: Error,
377 };
378
379 pub const Error = error{
380 UserResourceLimitReached,
381 SystemResources,
382 };
383
384 pub fn destroy(self: *Watch) void {
385 // TODO https://github.com/ziglang/zig/issues/1261
386 cancel self.putter;
387 }
388};
389
390pub fn watchFile(loop: *event.Loop, file_path: []const u8) !*Watch {
391 const path_with_null = try std.cstr.addNullByte(loop.allocator, file_path);
392 defer loop.allocator.free(path_with_null);
393
394 const inotify_fd = try os.linuxINotifyInit1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC);
395 errdefer os.close(inotify_fd);
396
397 const wd = try os.linuxINotifyAddWatchC(inotify_fd, path_with_null.ptr, os.linux.IN_CLOSE_WRITE);
398 errdefer os.close(wd);
399
400 const channel = try event.Channel(Watch.Event).create(loop, 0);
401 errdefer channel.destroy();
402
403 var result: *Watch = undefined;
404 _ = try async<loop.allocator> watchEventPutter(inotify_fd, wd, channel, &result);
405 return result;
406}
407
408async fn watchEventPutter(inotify_fd: i32, wd: i32, channel: *event.Channel(Watch.Event), out_watch: **Watch) void {
409 // TODO https://github.com/ziglang/zig/issues/1194
410 var my_handle: promise = undefined;
411 suspend |p| {
412 my_handle = p;
413 resume p;
414 }
415
416 var watch = Watch{
417 .putter = my_handle,
418 .channel = channel,
419 };
420 out_watch.* = &watch;
421
422 const loop = channel.loop;
423 loop.beginOneEvent();
424
425 defer {
426 channel.destroy();
427 os.close(wd);
428 os.close(inotify_fd);
429 loop.finishOneEvent();
430 }
431
432 var event_buf: [4096]u8 align(@alignOf(os.linux.inotify_event)) = undefined;
433
434 while (true) {
435 const rc = os.linux.read(inotify_fd, &event_buf, event_buf.len);
436 const errno = os.linux.getErrno(rc);
437 switch (errno) {
438 0 => {
439 // can't use @bytesToSlice because of the special variable length name field
440 var ptr = event_buf[0..].ptr;
441 const end_ptr = ptr + event_buf.len;
442 var ev: *os.linux.inotify_event = undefined;
443 while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += @sizeOf(os.linux.inotify_event) + ev.len) {
444 ev = @ptrCast(*os.linux.inotify_event, ptr);
445 if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) {
446 await (async channel.put(Watch.Event.CloseWrite) catch unreachable);
447 }
448 }
449 },
450 os.linux.EINTR => continue,
451 os.linux.EINVAL => unreachable,
452 os.linux.EFAULT => unreachable,
453 os.linux.EAGAIN => {
454 (await (async loop.linuxWaitFd(
455 inotify_fd,
456 os.linux.EPOLLET | os.linux.EPOLLIN,
457 ) catch unreachable)) catch |err| {
458 const transformed_err = switch (err) {
459 error.InvalidFileDescriptor => unreachable,
460 error.FileDescriptorAlreadyPresentInSet => unreachable,
461 error.InvalidSyscall => unreachable,
462 error.OperationCausesCircularLoop => unreachable,
463 error.FileDescriptorNotRegistered => unreachable,
464 error.SystemResources => error.SystemResources,
465 error.UserResourceLimitReached => error.UserResourceLimitReached,
466 error.FileDescriptorIncompatibleWithEpoll => unreachable,
467 error.Unexpected => unreachable,
468 };
469 await (async channel.put(Watch.Event{ .Err = transformed_err }) catch unreachable);
470 };
471 },
472 else => unreachable,
473 }
474 }
475}
476
305477const test_tmp_dir = "std_event_fs_test";
306478
307479test "write a file, watch it, write it again" {
......@@ -338,10 +510,39 @@ async fn testFsWatch(loop: *event.Loop) !void {
338510 \\line 1
339511 \\line 2
340512 ;
513 const line2_offset = 7;
341514
342515 // first just write then read the file
343516 try await try async writeFile(loop, file_path, contents);
344517
345518 const read_contents = try await try async readFile(loop, file_path, 1024 * 1024);
346519 assert(mem.eql(u8, read_contents, contents));
520
521 // now watch the file
522 var watch = try watchFile(loop, file_path);
523 defer watch.destroy();
524
525 const ev = try async watch.channel.get();
526 var ev_consumed = false;
527 defer if (!ev_consumed) cancel ev;
528
529 // overwrite line 2
530 const fd = try await try async openReadWrite(loop, file_path, os.File.default_mode);
531 {
532 defer os.close(fd);
533
534 try await try async pwritev(loop, fd, line2_offset, []const []const u8{"lorem ipsum"});
535 }
536
537 ev_consumed = true;
538 switch (await ev) {
539 Watch.Event.CloseWrite => {},
540 Watch.Event.Err => |err| return err,
541 }
542
543 const contents_updated = try await try async readFile(loop, file_path, 1024 * 1024);
544 assert(mem.eql(u8, contents_updated,
545 \\line 1
546 \\lorem ipsum
547 ));
347548}
std/event/loop.zig+36-23
......@@ -318,45 +318,46 @@ pub const Loop = struct {
318318 }
319319
320320 /// resume_node must live longer than the promise that it holds a reference to.
321 pub fn addFd(self: *Loop, fd: i32, resume_node: *ResumeNode) !void {
322 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
323 errdefer {
324 self.finishOneEvent();
325 }
326 try self.modFd(
321 /// flags must contain EPOLLET
322 pub fn linuxAddFd(self: *Loop, fd: i32, resume_node: *ResumeNode, flags: u32) !void {
323 assert(flags & posix.EPOLLET == posix.EPOLLET);
324 self.beginOneEvent();
325 errdefer self.finishOneEvent();
326 try self.linuxModFd(
327327 fd,
328328 posix.EPOLL_CTL_ADD,
329 os.linux.EPOLLIN | os.linux.EPOLLOUT | os.linux.EPOLLET,
329 flags,
330330 resume_node,
331331 );
332332 }
333333
334 pub fn modFd(self: *Loop, fd: i32, op: u32, events: u32, resume_node: *ResumeNode) !void {
334 pub fn linuxModFd(self: *Loop, fd: i32, op: u32, flags: u32, resume_node: *ResumeNode) !void {
335 assert(flags & posix.EPOLLET == posix.EPOLLET);
335336 var ev = os.linux.epoll_event{
336 .events = events,
337 .events = flags,
337338 .data = os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) },
338339 };
339340 try os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev);
340341 }
341342
342 pub fn removeFd(self: *Loop, fd: i32) void {
343 self.removeFdNoCounter(fd);
343 pub fn linuxRemoveFd(self: *Loop, fd: i32) void {
344 self.linuxRemoveFdNoCounter(fd);
344345 self.finishOneEvent();
345346 }
346347
347 fn removeFdNoCounter(self: *Loop, fd: i32) void {
348 fn linuxRemoveFdNoCounter(self: *Loop, fd: i32) void {
348349 os.linuxEpollCtl(self.os_data.epollfd, os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
349350 }
350351
351 pub async fn waitFd(self: *Loop, fd: i32) !void {
352 defer self.removeFd(fd);
352 pub async fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void {
353 defer self.linuxRemoveFd(fd);
353354 suspend |p| {
354355 // TODO explicitly put this memory in the coroutine frame #1194
355356 var resume_node = ResumeNode{
356357 .id = ResumeNode.Id.Basic,
357358 .handle = p,
358359 };
359 try self.addFd(fd, &resume_node);
360 try self.linuxAddFd(fd, &resume_node, flags);
360361 }
361362 }
362363
......@@ -382,7 +383,7 @@ pub const Loop = struct {
382383 // the pending count is already accounted for
383384 const epoll_events = posix.EPOLLONESHOT | os.linux.EPOLLIN | os.linux.EPOLLOUT |
384385 os.linux.EPOLLET;
385 self.modFd(
386 self.linuxModFd(
386387 eventfd_node.eventfd,
387388 eventfd_node.epoll_op,
388389 epoll_events,
......@@ -416,7 +417,7 @@ pub const Loop = struct {
416417
417418 /// Bring your own linked list node. This means it can't fail.
418419 pub fn onNextTick(self: *Loop, node: *NextTickNode) void {
419 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
420 self.beginOneEvent(); // finished in dispatch()
420421 self.next_tick_queue.put(node);
421422 self.dispatch();
422423 }
......@@ -470,8 +471,14 @@ pub const Loop = struct {
470471 }
471472 }
472473
473 fn finishOneEvent(self: *Loop) void {
474 if (@atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst) == 1) {
474 /// call finishOneEvent when done
475 pub fn beginOneEvent(self: *Loop) void {
476 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
477 }
478
479 pub fn finishOneEvent(self: *Loop) void {
480 const prev = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
481 if (prev == 1) {
475482 // cause all the threads to stop
476483 switch (builtin.os) {
477484 builtin.Os.linux => {
......@@ -593,7 +600,7 @@ pub const Loop = struct {
593600 }
594601
595602 fn linuxFsRequest(self: *Loop, request_node: *fs.RequestNode) void {
596 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
603 self.beginOneEvent(); // finished in linuxFsRun after processing the msg
597604 self.os_data.fs_queue.put(request_node);
598605 _ = @atomicRmw(i32, &self.os_data.fs_queue_len, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); // let this wrap
599606 const rc = os.linux.futex_wake(@ptrToInt(&self.os_data.fs_queue_len), os.linux.FUTEX_WAKE, 1);
......@@ -610,14 +617,21 @@ pub const Loop = struct {
610617 while (self.os_data.fs_queue.get()) |node| {
611618 processed_count +%= 1;
612619 switch (node.data.msg) {
613 @TagType(fs.Request.Msg).PWriteV => @panic("TODO"),
620 @TagType(fs.Request.Msg).End => return,
621 @TagType(fs.Request.Msg).PWriteV => |*msg| {
622 msg.result = os.posix_pwritev(msg.fd, msg.iov.ptr, msg.iov.len, msg.offset);
623 },
614624 @TagType(fs.Request.Msg).PReadV => |*msg| {
615625 msg.result = os.posix_preadv(msg.fd, msg.iov.ptr, msg.iov.len, msg.offset);
616626 },
617627 @TagType(fs.Request.Msg).OpenRead => |*msg| {
618 const flags = posix.O_LARGEFILE | posix.O_RDONLY;
628 const flags = posix.O_LARGEFILE | posix.O_RDONLY | posix.O_CLOEXEC;
619629 msg.result = os.posixOpenC(msg.path.ptr, flags, 0);
620630 },
631 @TagType(fs.Request.Msg).OpenRW => |*msg| {
632 const flags = posix.O_LARGEFILE | posix.O_RDWR | posix.O_CREAT | posix.O_CLOEXEC;
633 msg.result = os.posixOpenC(msg.path.ptr, flags, msg.mode);
634 },
621635 @TagType(fs.Request.Msg).Close => |*msg| os.close(msg.fd),
622636 @TagType(fs.Request.Msg).WriteFile => |*msg| blk: {
623637 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT |
......@@ -629,7 +643,6 @@ pub const Loop = struct {
629643 defer os.close(fd);
630644 msg.result = os.posixWrite(fd, msg.contents);
631645 },
632 @TagType(fs.Request.Msg).End => return,
633646 }
634647 switch (node.data.finish) {
635648 @TagType(fs.Request.Finish).TickNode => |*tick_node| self.onNextTick(tick_node),
std/event/tcp.zig+2-3
......@@ -55,7 +55,7 @@ pub const Server = struct {
5555 errdefer cancel self.accept_coro.?;
5656
5757 self.listen_resume_node.handle = self.accept_coro.?;
58 try self.loop.addFd(sockfd, &self.listen_resume_node);
58 try self.loop.linuxAddFd(sockfd, &self.listen_resume_node, posix.EPOLLIN | posix.EPOLLOUT | posix.EPOLLET);
5959 errdefer self.loop.removeFd(sockfd);
6060 }
6161
......@@ -116,7 +116,7 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File
116116 errdefer std.os.close(sockfd);
117117
118118 try std.os.posixConnectAsync(sockfd, &address.os_addr);
119 try await try async loop.waitFd(sockfd);
119 try await try async loop.linuxWaitFd(sockfd, posix.EPOLLIN | posix.EPOLLOUT);
120120 try std.os.posixGetSockOptConnectError(sockfd);
121121
122122 return std.os.File.openHandle(sockfd);
......@@ -181,4 +181,3 @@ async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Serv
181181 assert(mem.eql(u8, msg, "hello from server\n"));
182182 server.close();
183183}
184
std/os/file.zig-4
......@@ -29,7 +29,6 @@ pub const File = struct {
2929
3030 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
3131 /// Call close to clean up.
32 /// TODO deprecated, just use open
3332 pub fn openRead(allocator: *mem.Allocator, path: []const u8) OpenError!File {
3433 if (is_posix) {
3534 const flags = posix.O_LARGEFILE | posix.O_RDONLY;
......@@ -51,7 +50,6 @@ pub const File = struct {
5150 }
5251
5352 /// Calls `openWriteMode` with os.File.default_mode for the mode.
54 /// TODO deprecated, just use open
5553 pub fn openWrite(allocator: *mem.Allocator, path: []const u8) OpenError!File {
5654 return openWriteMode(allocator, path, os.File.default_mode);
5755 }
......@@ -60,7 +58,6 @@ pub const File = struct {
6058 /// If a file already exists in the destination it will be truncated.
6159 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
6260 /// Call close to clean up.
63 /// TODO deprecated, just use open
6461 pub fn openWriteMode(allocator: *mem.Allocator, path: []const u8, file_mode: Mode) OpenError!File {
6562 if (is_posix) {
6663 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;
......@@ -85,7 +82,6 @@ pub const File = struct {
8582 /// If a file already exists in the destination this returns OpenError.PathAlreadyExists
8683 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
8784 /// Call close to clean up.
88 /// TODO deprecated, just use open
8985 pub fn openWriteNoClobber(allocator: *mem.Allocator, path: []const u8, file_mode: Mode) OpenError!File {
9086 if (is_posix) {
9187 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL;
std/os/index.zig+64
......@@ -310,6 +310,29 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void {
310310 }
311311}
312312
313pub fn posix_pwritev(fd: i32, iov: [*]const posix.iovec_const, count: usize, offset: u64) PosixWriteError!void {
314 while (true) {
315 const rc = posix.pwritev(fd, iov, count, offset);
316 const err = posix.getErrno(rc);
317 switch (err) {
318 0 => return,
319 posix.EINTR => continue,
320 posix.EINVAL => unreachable,
321 posix.EFAULT => unreachable,
322 posix.EAGAIN => return PosixWriteError.WouldBlock,
323 posix.EBADF => return PosixWriteError.FileClosed,
324 posix.EDESTADDRREQ => return PosixWriteError.DestinationAddressRequired,
325 posix.EDQUOT => return PosixWriteError.DiskQuota,
326 posix.EFBIG => return PosixWriteError.FileTooBig,
327 posix.EIO => return PosixWriteError.InputOutput,
328 posix.ENOSPC => return PosixWriteError.NoSpaceLeft,
329 posix.EPERM => return PosixWriteError.AccessDenied,
330 posix.EPIPE => return PosixWriteError.BrokenPipe,
331 else => return unexpectedErrorPosix(err),
332 }
333 }
334}
335
313336pub const PosixOpenError = error{
314337 OutOfMemory,
315338 AccessDenied,
......@@ -2913,3 +2936,44 @@ pub fn bsdKEvent(
29132936 }
29142937 }
29152938}
2939
2940pub fn linuxINotifyInit1(flags: u32) !i32 {
2941 const rc = linux.inotify_init1(flags);
2942 const err = posix.getErrno(rc);
2943 switch (err) {
2944 0 => return @intCast(i32, rc),
2945 posix.EINVAL => unreachable,
2946 posix.EMFILE => return error.ProcessFdQuotaExceeded,
2947 posix.ENFILE => return error.SystemFdQuotaExceeded,
2948 posix.ENOMEM => return error.SystemResources,
2949 else => return unexpectedErrorPosix(err),
2950 }
2951}
2952
2953pub fn linuxINotifyAddWatchC(inotify_fd: i32, pathname: [*]const u8, mask: u32) !i32 {
2954 const rc = linux.inotify_add_watch(inotify_fd, pathname, mask);
2955 const err = posix.getErrno(rc);
2956 switch (err) {
2957 0 => return @intCast(i32, rc),
2958 posix.EACCES => return error.AccessDenied,
2959 posix.EBADF => unreachable,
2960 posix.EFAULT => unreachable,
2961 posix.EINVAL => unreachable,
2962 posix.ENAMETOOLONG => return error.NameTooLong,
2963 posix.ENOENT => return error.FileNotFound,
2964 posix.ENOMEM => return error.SystemResources,
2965 posix.ENOSPC => return error.UserResourceLimitReached,
2966 else => return unexpectedErrorPosix(err),
2967 }
2968}
2969
2970pub fn linuxINotifyRmWatch(inotify_fd: i32, wd: i32) !void {
2971 const rc = linux.inotify_rm_watch(inotify_fd, wd);
2972 const err = posix.getErrno(rc);
2973 switch (err) {
2974 0 => return rc,
2975 posix.EBADF => unreachable,
2976 posix.EINVAL => unreachable,
2977 else => unreachable,
2978 }
2979}
std/os/linux/index.zig+60
......@@ -567,6 +567,37 @@ pub const MNT_DETACH = 2;
567567pub const MNT_EXPIRE = 4;
568568pub const UMOUNT_NOFOLLOW = 8;
569569
570pub const IN_CLOEXEC = O_CLOEXEC;
571pub const IN_NONBLOCK = O_NONBLOCK;
572
573pub const IN_ACCESS = 0x00000001;
574pub const IN_MODIFY = 0x00000002;
575pub const IN_ATTRIB = 0x00000004;
576pub const IN_CLOSE_WRITE = 0x00000008;
577pub const IN_CLOSE_NOWRITE = 0x00000010;
578pub const IN_CLOSE = IN_CLOSE_WRITE | IN_CLOSE_NOWRITE;
579pub const IN_OPEN = 0x00000020;
580pub const IN_MOVED_FROM = 0x00000040;
581pub const IN_MOVED_TO = 0x00000080;
582pub const IN_MOVE = IN_MOVED_FROM | IN_MOVED_TO;
583pub const IN_CREATE = 0x00000100;
584pub const IN_DELETE = 0x00000200;
585pub const IN_DELETE_SELF = 0x00000400;
586pub const IN_MOVE_SELF = 0x00000800;
587pub const IN_ALL_EVENTS = 0x00000fff;
588
589pub const IN_UNMOUNT = 0x00002000;
590pub const IN_Q_OVERFLOW = 0x00004000;
591pub const IN_IGNORED = 0x00008000;
592
593pub const IN_ONLYDIR = 0x01000000;
594pub const IN_DONT_FOLLOW = 0x02000000;
595pub const IN_EXCL_UNLINK = 0x04000000;
596pub const IN_MASK_ADD = 0x20000000;
597
598pub const IN_ISDIR = 0x40000000;
599pub const IN_ONESHOT = 0x80000000;
600
570601pub const S_IFMT = 0o170000;
571602
572603pub const S_IFDIR = 0o040000;
......@@ -704,6 +735,18 @@ pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize {
704735 return syscall3(SYS_getdents, @intCast(usize, fd), @ptrToInt(dirp), count);
705736}
706737
738pub fn inotify_init1(flags: u32) usize {
739 return syscall1(SYS_inotify_init1, flags);
740}
741
742pub fn inotify_add_watch(fd: i32, pathname: [*]const u8, mask: u32) usize {
743 return syscall3(SYS_inotify_add_watch, @intCast(usize, fd), @ptrToInt(pathname), mask);
744}
745
746pub fn inotify_rm_watch(fd: i32, wd: i32) usize {
747 return syscall2(SYS_inotify_rm_watch, @intCast(usize, fd), @intCast(usize, wd));
748}
749
707750pub fn isatty(fd: i32) bool {
708751 var wsz: winsize = undefined;
709752 return syscall3(SYS_ioctl, @intCast(usize, fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0;
......@@ -750,6 +793,10 @@ pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: u64) usize {
750793 return syscall4(SYS_preadv, @intCast(usize, fd), @ptrToInt(iov), count, offset);
751794}
752795
796pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64) usize {
797 return syscall4(SYS_pwritev, @intCast(usize, fd), @ptrToInt(iov), count, offset);
798}
799
753800// TODO https://github.com/ziglang/zig/issues/265
754801pub fn rmdir(path: [*]const u8) usize {
755802 return syscall1(SYS_rmdir, @ptrToInt(path));
......@@ -1068,6 +1115,11 @@ pub const iovec = extern struct {
10681115 iov_len: usize,
10691116};
10701117
1118pub const iovec_const = extern struct {
1119 iov_base: [*]const u8,
1120 iov_len: usize,
1121};
1122
10711123pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
10721124 return syscall3(SYS_getsockname, @intCast(usize, fd), @ptrToInt(addr), @ptrToInt(len));
10731125}
......@@ -1376,6 +1428,14 @@ pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize {
13761428 return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));
13771429}
13781430
1431pub const inotify_event = extern struct {
1432 wd: i32,
1433 mask: u32,
1434 cookie: u32,
1435 len: u32,
1436 //name: [?]u8,
1437};
1438
13791439test "import" {
13801440 if (builtin.os == builtin.Os.linux) {
13811441 _ = @import("test.zig");