├── .github └── workflows │ └── dart.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── dart_test.yaml ├── example └── example.dart ├── lib ├── file_system.dart ├── loading.dart ├── src │ ├── file_system │ │ ├── misc.dart │ │ └── named_pipe.dart │ ├── libc │ │ ├── all.dart │ │ ├── error_codes.dart │ │ └── functions.dart │ ├── loading │ │ └── loading.dart │ ├── virtual_memory │ │ ├── virtual_memory.dart │ │ ├── virtual_memory_impl_posix.dart │ │ └── virtual_memory_impl_windows.dart │ └── windows │ │ └── windows.dart └── virtual_memory.dart ├── pubspec.yaml └── test └── src ├── file ├── misc_test.dart └── named_pipe_test.dart └── virtual_memory ├── virtual_memory_error_script.dart └── virtual_memory_test.dart /.github/workflows/dart.yml: -------------------------------------------------------------------------------- 1 | name: Dart CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | container: 9 | image: google/dart:latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Install dependencies 13 | run: pub get 14 | - name: Run tests 15 | run: pub run test 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://www.dartlang.org/guides/libraries/private-files 2 | 3 | # Files and directories created by pub 4 | .dart_tool/ 5 | .packages 6 | .pub/ 7 | build/ 8 | # If you're building an application, you may want to check-in your pubspec.lock 9 | pubspec.lock 10 | 11 | # Directory created by dartdoc 12 | # If you don't generate documentation locally you can remove this line. 13 | doc/api/ 14 | 15 | .idea/ 16 | .VSCode/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: dart 2 | 3 | os: 4 | - osx 5 | 6 | dart: 7 | - dev 8 | 9 | branches: 10 | only: [master] -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | ## 0.1.3 3 | * Improved documentation. 4 | 5 | ## 0.1.2 6 | * Updated to SDK 2.7. 7 | 8 | ## 0.1.1 9 | * Small fixes suggested by the package analyzer. 10 | 11 | ## 0.1.0 12 | * Initial release. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Pub Package](https://img.shields.io/pub/v/os.svg)](https://pub.dartlang.org/packages/os) 2 | [![Github Actions CI](https://github.com/dart-interop/os/workflows/Dart%20CI/badge.svg)](https://github.com/dart-interop/os/actions?query=workflow%3A%22Dart+CI%22) 3 | [![Build Status](https://travis-ci.org/dart-interop/os.svg?branch=master)](https://travis-ci.org/dart-interop/os) 4 | 5 | # Overview 6 | This package provides access to various low-level APIs of operating systems. The package binds with 7 | _libc_ (Linux, Mac OS X) and _kernel32.dll_ (Windows) using [dart:ffi](https://dart.dev/guides/libraries/c-interop). 8 | 9 | The project is licensed under the [Apache License 2.0](LICENSE). 10 | 11 | ## Status 12 | Passes tests in: 13 | * Darwin (OS X) 14 | 15 | Fails tests in: 16 | * Linux 17 | * Windows 18 | 19 | ## Issues? 20 | * [Create an issue](https://github.com/terrier989/os/issues). 21 | 22 | # APIs 23 | ## os.file_system 24 | Library 'package:os/file_system.dart' provides access to various functions not supported by 25 | 'dart:io'. 26 | 27 | Examples: 28 | ```dart 29 | chmodSync(Directory("some/path"), 0x1FF); // Octal: 777 30 | ``` 31 | 32 | Named pipes (also known as "POSIX pipes") are sometimes used for inter-process communication in 33 | operating systems such as Linux and Mac OS X. 34 | 35 | ```dart 36 | void main() async { 37 | // Create the pipe 38 | final namedPipe = NamedPipe("some/path"); 39 | namedPipe.createSync(); 40 | 41 | // Write something 42 | final writer = namedPipe.openWrite() 43 | writer.add([1,2,3]) 44 | await writer.close(); 45 | 46 | // Delete the pipe 47 | namedPipe.deleteSync(); 48 | } 49 | ``` 50 | 51 | ## os.virtual_memory 52 | Library 'package:os/memory.dart' enables you to control virtual memory tables. 53 | 54 | ```dart 55 | void main() async { 56 | // Allocate memory 57 | final memory = VirtualMemory.allocate(1024); 58 | 59 | // Set protection bits 60 | memory.setProtection(VirtualMemory.protectionReadWrite); 61 | 62 | // Write to the memory 63 | memory.asUint8List[23] = 29; 64 | 65 | // Free the memory 66 | memory.free(); 67 | } 68 | ``` -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.yaml -------------------------------------------------------------------------------- /dart_test.yaml: -------------------------------------------------------------------------------- 1 | platforms: [vm] -------------------------------------------------------------------------------- /example/example.dart: -------------------------------------------------------------------------------- 1 | import 'package:os/virtual_memory.dart'; 2 | 3 | void main() { 4 | // Allocate virtual memory 5 | final memory = VirtualMemory.allocate(1024); 6 | memory.asUint8List[0] = 42; 7 | 8 | // Make the memory read-only 9 | memory.setProtection(VirtualMemory.protectionWrite); 10 | 11 | // Any mutation will result in a crash now 12 | memory.asUint8List[0] = 99; 13 | } 14 | -------------------------------------------------------------------------------- /lib/file_system.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 terrier989 . 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// Provides access to named pipes and other file system functions not provided 16 | /// by 'dart:io'. 17 | library os.file_system; 18 | 19 | export 'src/file_system/misc.dart'; 20 | export 'src/file_system/named_pipe.dart'; 21 | -------------------------------------------------------------------------------- /lib/loading.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 terrier989 . 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// Helpers for loading dynamic libraries. 16 | library os.loading; 17 | 18 | export 'src/loading/loading.dart'; 19 | -------------------------------------------------------------------------------- /lib/src/file_system/misc.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 terrier989 . 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import 'dart:async'; 16 | import 'dart:ffi' as ffi; 17 | import 'dart:io'; 18 | 19 | import 'package:ffi/ffi.dart' as ffi; 20 | 21 | import '../libc/all.dart' as libc; 22 | 23 | /// Changes permissions of a file. 24 | /// 25 | /// In windows, this function doesn't do anything. 26 | Future chmod(FileSystemEntity entity, int mode) async { 27 | chmodSync(entity, mode); 28 | } 29 | 30 | /// Changes permissions of a file. 31 | /// 32 | /// In windows, this function doesn't do anything. 33 | void chmodSync(FileSystemEntity entity, int mode) { 34 | if (Platform.isWindows) { 35 | // TODO: Implement in Windows? 36 | throw UnsupportedError('Not supported in Windows'); 37 | } 38 | final pathAddr = ffi.Utf8.toUtf8(entity.path); 39 | try { 40 | final result = libc.chmod( 41 | pathAddr, 42 | mode, 43 | ); 44 | if (result < 0) { 45 | throw StateError('Error code: ${libc.errorDescription}'); 46 | } 47 | } finally { 48 | ffi.free(pathAddr); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/src/file_system/named_pipe.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 terrier989 . 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the 'License'); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an 'AS IS' BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import 'dart:async'; 16 | import 'dart:ffi' as ffi; 17 | import 'dart:io'; 18 | import 'dart:typed_data'; 19 | 20 | import 'package:ffi/ffi.dart' as ffi; 21 | 22 | import '../libc/all.dart' as libc; 23 | 24 | /// Duration before polling again. 25 | const _shortDuration = Duration(microseconds: 100); 26 | 27 | /// Opens a file and returns file descriptor, which can be used with other 28 | /// 'libc' functions. 29 | int _openFd(String path, int flags) { 30 | final pathAddr = ffi.Utf8.toUtf8(path); 31 | try { 32 | return libc.open( 33 | pathAddr, 34 | flags, 35 | 0, 36 | ); 37 | } finally { 38 | ffi.free(pathAddr); 39 | } 40 | } 41 | 42 | class NamedPipe { 43 | final String path; 44 | 45 | NamedPipe(this.path) { 46 | if (path == null) { 47 | throw ArgumentError.notNull(); 48 | } 49 | } 50 | 51 | /// Creates the named pipe. 52 | /// 53 | /// Parameter [mode] can be used to specify Posix file permissions. 54 | void createSync({int mode = 0x1FF}) { 55 | final pathAddr = ffi.Utf8.toUtf8(path); 56 | try { 57 | final result = libc.mkfifo( 58 | pathAddr, 59 | mode, 60 | ); 61 | if (result < 0) { 62 | throw _NamedPipeException('create', path, libc.errorCode); 63 | } 64 | } finally { 65 | ffi.free(pathAddr); 66 | } 67 | } 68 | 69 | /// Deletes the named pipe. 70 | /// 71 | /// Note that usually named pipes can't be deleted with normal file 72 | /// operations. 73 | void deleteSync() { 74 | if (!existsSync()) { 75 | return; 76 | } 77 | final pathAddr = ffi.Utf8.toUtf8(path); 78 | try { 79 | final result = libc.unlink( 80 | pathAddr, 81 | ); 82 | if (result < 0) { 83 | throw _NamedPipeException( 84 | 'delete', 85 | path, 86 | libc.errorCode, 87 | ); 88 | } 89 | } finally { 90 | ffi.free(pathAddr); 91 | } 92 | } 93 | 94 | bool existsSync() { 95 | return File(path).existsSync(); 96 | } 97 | 98 | /// Opens the named pipe for reading. 99 | Stream> openRead() { 100 | final streamController = StreamController>(); 101 | streamController.onListen = () { 102 | // Open the file 103 | final fd = _openFd(path, libc.O_RDONLY | libc.O_NONBLOCK); 104 | if (fd < 0) { 105 | final error = _NamedPipeException( 106 | 'open', 107 | path, 108 | libc.errorCode, 109 | ); 110 | streamController.addError(error); 111 | streamController.close(); 112 | return; 113 | } 114 | 115 | // Allocate a buffer that can be filled by 'libc' 116 | final bufferLength = 512; 117 | final buffer = ffi.allocate(count: bufferLength); 118 | final bufferData = buffer.asTypedList(bufferLength); 119 | 120 | var libcClosed = false; 121 | 122 | // Periodically check whether any data has arrived 123 | Timer.periodic(_shortDuration, (timer) { 124 | if (libcClosed) { 125 | return; 126 | } 127 | 128 | // If the stream is closed 129 | if (streamController.isClosed) { 130 | // Cancel timer 131 | timer.cancel(); 132 | libcClosed = true; 133 | 134 | // Free memory 135 | ffi.free(buffer); 136 | 137 | // Close file handle 138 | libc.close(fd); 139 | return; 140 | } 141 | 142 | // Read from 'libc' 143 | final result = libc.read(fd, buffer, bufferLength); 144 | 145 | // Error? 146 | if (result < 0) { 147 | final errorCode = libc.errorCode; 148 | 149 | // Because writer hasn't closed? 150 | if (errorCode == libc.EAGAIN) { 151 | // Wait a bit more 152 | return; 153 | } 154 | 155 | final error = _NamedPipeException( 156 | 'read', 157 | path, 158 | errorCode, 159 | ); 160 | streamController.addError(error); 161 | streamController.close(); 162 | return; 163 | } 164 | 165 | // Empty? 166 | if (result == 0) { 167 | return; 168 | } 169 | 170 | // We received data. 171 | // We need to allocate a new Uint8List that we can pass to the listener. 172 | final readData = Uint8List(result); 173 | readData.setAll(0, bufferData.take(result)); 174 | streamController.add(readData); 175 | }); 176 | //libc.write(pollFd.fd, 0, 0); 177 | }; 178 | return streamController.stream; 179 | } 180 | 181 | /// Opens the named pipe for writing. 182 | /// 183 | /// Optional parameter [timeout] defines how long to wait for readers to open 184 | /// the pipe. 185 | _NamedPipeWriter openWrite({Duration timeout}) { 186 | return _NamedPipeWriter(path, timeout: timeout); 187 | } 188 | } 189 | 190 | class _NamedPipeException implements Exception { 191 | final String op; 192 | final String path; 193 | final int errorCode; 194 | 195 | _NamedPipeException(this.op, this.path, this.errorCode); 196 | 197 | @override 198 | String toString() { 199 | final errorName = libc.errorNames[errorCode]; 200 | return 'Operation "$op" on a named pipe failed.\n path = "$path"\n error = $errorCode ($errorName)'; 201 | } 202 | } 203 | 204 | class _NamedPipeWriter implements Sink>, StreamConsumer> { 205 | final String path; 206 | int _fd; 207 | Future _future; 208 | bool _isClosed = false; 209 | final Duration _timeout; 210 | 211 | _NamedPipeWriter(this.path, {Duration timeout}) : _timeout = timeout { 212 | if (!File(path).existsSync()) { 213 | throw StateError('File "$path" does not exist'); 214 | } 215 | } 216 | 217 | @override 218 | Future add(List data) { 219 | if (_isClosed) { 220 | throw StateError('The sink is closed'); 221 | } 222 | var oldFuture = _future; 223 | if (oldFuture == null) { 224 | oldFuture = _waitForOpen(); 225 | _future = oldFuture; 226 | } 227 | final newFuture = _add(oldFuture, data); 228 | _future = newFuture; 229 | return newFuture; 230 | } 231 | 232 | @override 233 | Future addStream(Stream> stream) async { 234 | await for (var chunk in stream) { 235 | await add(chunk); 236 | } 237 | } 238 | 239 | @override 240 | Future close() { 241 | _isClosed = true; 242 | final future = _future ?? Future.value(); 243 | return future.whenComplete(() { 244 | if (_fd != null) { 245 | final result = libc.close(_fd); 246 | _fd = null; 247 | if (result < 0) { 248 | throw _NamedPipeException( 249 | 'close', 250 | path, 251 | libc.errorCode, 252 | ); 253 | } 254 | } 255 | }); 256 | } 257 | 258 | Future _add(Future waitedFuture, List data) async { 259 | // Wait for previous future 260 | await waitedFuture; 261 | 262 | // If empty, we can skip allocation 263 | if (data.isEmpty) { 264 | return; 265 | } 266 | 267 | // Allocate buffer than 'libc' can use 268 | final length = data.length; 269 | final pointer = ffi.allocate(count: length); 270 | 271 | try { 272 | // Declare remaining pointer/length 273 | var remainingPointer = pointer; 274 | var remainingLength = length; 275 | 276 | // Copy data 277 | final pointerData = pointer.asTypedList(length); 278 | pointerData.setAll(0, data); 279 | 280 | // While we have remaining bytes 281 | while (remainingLength > 0) { 282 | if (_isClosed) { 283 | throw StateError('The sink is closed'); 284 | } 285 | 286 | // Write 287 | final n = libc.write(_fd, remainingPointer, remainingLength); 288 | 289 | // An error? 290 | if (n < 0) { 291 | throw _NamedPipeException('add', path, libc.errorCode); 292 | } 293 | 294 | // Wrote something? 295 | if (n > 0) { 296 | remainingPointer = remainingPointer.elementAt(n); 297 | remainingLength -= n; 298 | if (remainingLength == 0) { 299 | return; 300 | } 301 | } 302 | 303 | // Wait a bit before trying again 304 | await Future.delayed(_shortDuration); 305 | } 306 | } finally { 307 | ffi.free(pointer); 308 | } 309 | } 310 | 311 | Future _waitForOpen() async { 312 | final startedAt = DateTime.now(); 313 | while (true) { 314 | if (_isClosed) { 315 | throw StateError('The sink is closed'); 316 | } 317 | final fd = _openFd(path, libc.O_WRONLY | libc.O_NONBLOCK); 318 | if (fd <= 0) { 319 | // Failed because nobody is reading the stream? 320 | if (libc.errorCode == libc.ENXIO) { 321 | // Wait a bit more 322 | await Future.delayed(_shortDuration); 323 | if (_timeout != null && 324 | DateTime.now().isAfter(startedAt.add(_timeout))) { 325 | throw TimeoutException('Timeout before reader was attached'); 326 | } 327 | continue; 328 | } 329 | throw _NamedPipeException('open', path, libc.errorCode); 330 | } 331 | _fd = fd; 332 | return; 333 | } 334 | } 335 | } 336 | -------------------------------------------------------------------------------- /lib/src/libc/all.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 terrier989 . 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | export 'error_codes.dart'; 16 | export 'functions.dart'; 17 | -------------------------------------------------------------------------------- /lib/src/libc/error_codes.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 terrier989 . 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the 'License'); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an 'AS IS' BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import 'dart:ffi' as ffi; 16 | 17 | import 'functions.dart' show libraryLoader; 18 | 19 | final _errno = libraryLoader.open().lookup('errno'); 20 | 21 | int get errorCode => _errno.value; 22 | 23 | String get errorDescription { 24 | final c = errorCode; 25 | return '${errorNames[c]} ($c)'; 26 | } 27 | 28 | const EPERM = 1; 29 | const ENOENT = 2; 30 | const ESRCH = 3; 31 | const EINTR = 4; 32 | const EIO = 5; 33 | const ENXIO = 6; 34 | const BIG = 7; 35 | const ENOEXEC = 8; 36 | const EBADF = 9; 37 | const ECHILD = 10; 38 | const EDEADLK = 11; 39 | const ENOMEM = 12; 40 | const EACCES = 13; 41 | const EFAULT = 14; 42 | const ENOTBLK = 15; 43 | const EBUSY = 16; 44 | const EEXIST = 17; 45 | const EXDEV = 18; 46 | const ENODEV = 19; 47 | const ENOTDIR = 20; 48 | const EISDIR = 21; 49 | const EINVAL = 22; 50 | const ENFILE = 23; 51 | const EMFILE = 24; 52 | const ENOTTY = 25; 53 | const ETXTBSY = 26; 54 | const EFBIG = 27; 55 | const ENOSPC = 28; 56 | const ESPIPE = 29; 57 | const EROFS = 30; 58 | const EMLINK = 31; 59 | const EPIPE = 32; 60 | const EDOM = 33; 61 | const ERANGE = 34; 62 | const EAGAIN = 35; 63 | const EINPROGRESS = 36; 64 | const EALREADY = 37; 65 | const ENOTSOCK = 38; 66 | const EDESTADDRREQ = 39; 67 | const EMSGSIZE = 40; 68 | const EPROTOTYPE = 41; 69 | const ENOPROTOOPT = 42; 70 | const EPROTONOSUPPORT = 43; 71 | const ESOCKTNOSUPPORT = 44; 72 | const ENOTSUP = 45; 73 | const EPFNOSUPPORT = 46; 74 | const EAFNOSUPPORT = 47; 75 | const EADDRINUSE = 48; 76 | const EADDRNOTAVAIL = 49; 77 | const ENETDOWN = 50; 78 | const ENETUNREACH = 51; 79 | const ENETRESET = 52; 80 | const ECONNABORTED = 53; 81 | const ECONNRESET = 54; 82 | const ENOBUFS = 55; 83 | const EISCONN = 56; 84 | const ENOTCONN = 57; 85 | const ESHUTDOWN = 58; 86 | const ETOOMANYREFS = 59; 87 | const ETIMEDOUT = 60; 88 | const ECONNREFUSED = 61; 89 | const ELOOP = 62; 90 | const ENAMETOOLONG = 63; 91 | const EHOSTDOWN = 64; 92 | const EHOSTUNREACH = 65; 93 | const ENOTEMPTY = 66; 94 | const EPROCLIM = 67; 95 | const EUSERS = 68; 96 | const EDQUOT = 69; 97 | const ENOLCK = 77; 98 | const ENOSYS = 78; 99 | const EFTYPE = 79; 100 | const EAUTH = 80; 101 | const ENEEDAUTH = 81; 102 | const EPWROFF = 82; 103 | const EDEVERR = 83; 104 | const EOVERFLOW = 84; 105 | const EBADEXEC = 85; 106 | const EBADARCH = 86; 107 | const ESHLIBVERS = 87; 108 | 109 | const errorNames = { 110 | 1: 'EPERM', 111 | 2: 'ENOENT', 112 | 3: 'ESRCH', 113 | 4: 'EINTR', 114 | 5: 'EIO', 115 | 6: 'ENXIO', 116 | 7: 'BIG', 117 | 8: 'ENOEXEC', 118 | 9: 'EBADF', 119 | 10: 'ECHILD', 120 | 11: 'EDEADLK', 121 | 12: 'ENOMEM', 122 | 13: 'EACCES', 123 | 14: 'EFAULT', 124 | 15: 'ENOTBLK', 125 | 16: 'EBUSY', 126 | 17: 'EEXIST', 127 | 18: 'EXDEV', 128 | 19: 'ENODEV', 129 | 20: 'ENOTDIR', 130 | 21: 'EISDIR', 131 | 22: 'EINVAL', 132 | 23: 'ENFILE', 133 | 24: 'EMFILE', 134 | 25: 'ENOTTY', 135 | 26: 'ETXTBSY', 136 | 27: 'EFBIG', 137 | 28: 'ENOSPC', 138 | 29: 'ESPIPE', 139 | 30: 'EROFS', 140 | 31: 'EMLINK', 141 | 32: 'EPIPE', 142 | 33: 'EDOM', 143 | 34: 'ERANGE', 144 | 35: 'EAGAIN', 145 | 36: 'EINPROGRESS', 146 | 37: 'EALREADY', 147 | 38: 'ENOTSOCK', 148 | 39: 'EDESTADDRREQ', 149 | 40: 'EMSGSIZE', 150 | 41: 'EPROTOTYPE', 151 | 42: 'ENOPROTOOPT', 152 | 43: 'EPROTONOSUPPORT', 153 | 44: 'ESOCKTNOSUPPORT', 154 | 45: 'ENOTSUP', 155 | 46: 'EPFNOSUPPORT', 156 | 47: 'EAFNOSUPPORT', 157 | 48: 'EADDRINUSE', 158 | 49: 'EADDRNOTAVAIL', 159 | 50: 'ENETDOWN', 160 | 51: 'ENETUNREACH', 161 | 52: 'ENETRESET', 162 | 53: 'ECONNABORTED', 163 | 54: 'ECONNRESET', 164 | 55: 'ENOBUFS', 165 | 56: 'EISCONN', 166 | 57: 'ENOTCONN', 167 | 58: 'ESHUTDOWN', 168 | 59: 'ETOOMANYREFS', 169 | 60: 'ETIMEDOUT', 170 | 61: 'ECONNREFUSED', 171 | 62: 'ELOOP', 172 | 63: 'ENAMETOOLONG', 173 | 64: 'EHOSTDOWN', 174 | 65: 'EHOSTUNREACH', 175 | 66: 'ENOTEMPTY', 176 | 67: 'EPROCLIM', 177 | 68: 'EUSERS', 178 | 69: 'EDQUOT', 179 | 77: 'ENOLCK', 180 | 78: 'ENOSYS', 181 | 79: 'EFTYPE', 182 | 80: 'EAUTH', 183 | 81: 'ENEEDAUTH', 184 | 82: 'EPWROFF', 185 | 83: 'EDEVERR', 186 | 84: 'EOVERFLOW', 187 | 85: 'EBADEXEC', 188 | 86: 'EBADARCH', 189 | 87: 'ESHLIBVERS', 190 | }; 191 | -------------------------------------------------------------------------------- /lib/src/libc/functions.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 terrier989 . 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the 'License'); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an 'AS IS' BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// Access to 'libc', a library available in Posix systems. 16 | library os.libc; 17 | 18 | import 'dart:ffi' as ffi; 19 | 20 | import 'package:ffi/ffi.dart' as ffi; 21 | import 'package:os/loading.dart'; 22 | 23 | const MAP_ANONYMOUS = 0x1000; 24 | const MAP_FIXED = 0x0010; 25 | const MAP_JIT = 0x0800; 26 | const MAP_NOCACHE = 0x0400; 27 | const MAP_PRIVATE = 0x0002; 28 | const MAP_SHARED = 0x0001; 29 | 30 | const O_ACCMODE = 0x0003; 31 | const O_APPEND = 0x0008; 32 | const O_ASYNC = 0x0040; 33 | const O_CREAT = 0x0200; 34 | const O_EXCL = 0x0800; 35 | const O_EXLOCK = 0x0020; 36 | const O_FSYNC = 0x0080; 37 | const O_NOFOLLOW = 0x0100; 38 | const O_NONBLOCK = 0x0004; 39 | const O_RDONLY = 0x0000; 40 | const O_RDWR = 0x0002; 41 | const O_SHLOCK = 0x0010; 42 | const O_TRUNC = 0x0400; 43 | const O_WRONLY = 0x0001; 44 | 45 | const POLLERR = 0x08; 46 | const POLLHUP = 0x10; 47 | const POLLIN = 0x01; 48 | const POLLNVAL = 0x20; 49 | const POLLOUT = 0x04; 50 | const POLLPRI = 0x02; 51 | 52 | const PROT_EXEC = 0x04; 53 | const PROT_NONE = 0x00; 54 | const PROT_READ = 0x01; 55 | const PROT_WRITE = 0x02; 56 | 57 | final chmod = libraryLoader.open().lookupFunction<_chmod_C, _chmod_Dart>( 58 | 'chmod', 59 | ); 60 | 61 | final close = libraryLoader.open().lookupFunction<_close_C, _close_Dart>( 62 | 'close', 63 | ); 64 | 65 | final libraryLoader = DynamicLibraryProvider( 66 | darwinNames: const ['libc.dylib', '/usr/lib/libc.dylib'], 67 | linuxNames: const ['libc.so.6', 'libc.so'], 68 | ); 69 | 70 | final mkfifo = libraryLoader.open().lookupFunction<_mkfifo_C, _mkfifo_Dart>( 71 | 'mkfifo', 72 | ); 73 | 74 | final mkdir = libraryLoader.open().lookupFunction<_mkdir_C, _mkdir_Dart>( 75 | 'mkdir', 76 | ); 77 | 78 | final mmap = libraryLoader.open().lookupFunction<_mmap_C, _mmap_Dart>( 79 | 'mmap', 80 | ); 81 | 82 | final mprotect = 83 | libraryLoader.open().lookupFunction<_mprotect_C, _mprotect_Dart>( 84 | 'mprotect', 85 | ); 86 | 87 | final munmap = libraryLoader.open().lookupFunction<_munmap_C, _munmap_Dart>( 88 | 'munmap', 89 | ); 90 | 91 | final open = libraryLoader.open().lookupFunction<_open_C, _open_Dart>( 92 | 'open', 93 | ); 94 | 95 | final poll = libraryLoader.open().lookupFunction<_poll_C, _poll_Dart>( 96 | 'poll', 97 | ); 98 | 99 | final read = libraryLoader.open().lookupFunction<_read_C, _read_Dart>( 100 | 'read', 101 | ); 102 | 103 | final unlink = libraryLoader.open().lookupFunction<_unlink_C, _unlink_Dart>( 104 | 'unlink', 105 | ); 106 | 107 | final write = libraryLoader.open().lookupFunction<_write_C, _write_Dart>( 108 | 'write', 109 | ); 110 | 111 | typedef _chmod_C = ffi.Int32 Function( 112 | ffi.Pointer name, 113 | ffi.Int32 mode, 114 | ); 115 | 116 | typedef _chmod_Dart = int Function( 117 | ffi.Pointer name, 118 | int mode, 119 | ); 120 | 121 | typedef _close_C = ffi.Int32 Function( 122 | ffi.Int32 fd, 123 | ); 124 | 125 | typedef _close_Dart = int Function( 126 | int fd, 127 | ); 128 | 129 | typedef _mkfifo_C = ffi.Int32 Function( 130 | ffi.Pointer name, 131 | ffi.Int32 mode, 132 | ); 133 | 134 | typedef _mkfifo_Dart = int Function( 135 | ffi.Pointer name, 136 | int mode, 137 | ); 138 | 139 | typedef _mmap_C = ffi.Pointer Function( 140 | ffi.Pointer pointer, 141 | ffi.IntPtr size, // size_t 142 | ffi.Int32 protection, 143 | ffi.Int32 flags, 144 | ffi.Int32 fileDescriptor, 145 | ffi.IntPtr offset, // off_t 146 | ); 147 | 148 | typedef _mmap_Dart = ffi.Pointer Function( 149 | ffi.Pointer pointer, 150 | int size, // size_t 151 | int protection, 152 | int flags, 153 | int fileDescriptor, 154 | int offset, // off_t 155 | ); 156 | 157 | typedef _mprotect_C = ffi.Void Function( 158 | ffi.Pointer pointer, 159 | ffi.IntPtr size, 160 | ffi.IntPtr protection, 161 | ); 162 | 163 | typedef _mprotect_Dart = void Function( 164 | ffi.Pointer pointer, 165 | int size, 166 | int protection, 167 | ); 168 | 169 | typedef _mkdir_C = ffi.Int32 Function( 170 | ffi.Pointer name, 171 | ffi.Int32 mode, 172 | ); 173 | 174 | typedef _mkdir_Dart = int Function( 175 | ffi.Pointer name, 176 | int mode, 177 | ); 178 | 179 | typedef _munmap_C = ffi.Void Function( 180 | ffi.Pointer pointer, 181 | ffi.IntPtr size, 182 | ); 183 | 184 | typedef _munmap_Dart = void Function( 185 | ffi.Pointer pointer, 186 | int size, 187 | ); 188 | 189 | typedef _open_C = ffi.Int32 Function( 190 | ffi.Pointer name, 191 | ffi.Int32 flags, 192 | ffi.Int32 mode, 193 | ); 194 | 195 | typedef _open_Dart = int Function( 196 | ffi.Pointer name, 197 | int flags, 198 | int mode, 199 | ); 200 | 201 | typedef _poll_C = ffi.Int32 Function( 202 | ffi.Pointer pollfd, 203 | ffi.Int32 pollfdLength, 204 | ffi.Int32 timeout, 205 | ); 206 | 207 | typedef _poll_Dart = int Function( 208 | ffi.Pointer pollfd, 209 | int pollfdLength, 210 | int timeout, 211 | ); 212 | 213 | typedef _read_C = ffi.IntPtr Function( 214 | ffi.Int32 fd, 215 | ffi.Pointer buffer, 216 | ffi.IntPtr length, 217 | ); 218 | 219 | typedef _read_Dart = int Function( 220 | int fd, 221 | ffi.Pointer buffer, 222 | int length, 223 | ); 224 | 225 | typedef _unlink_C = ffi.Int32 Function( 226 | ffi.Pointer name, 227 | ); 228 | 229 | typedef _unlink_Dart = int Function( 230 | ffi.Pointer name, 231 | ); 232 | 233 | typedef _write_C = ffi.Int32 Function( 234 | ffi.Int32 fd, 235 | ffi.Pointer buffer, 236 | ffi.IntPtr length, 237 | ); 238 | 239 | typedef _write_Dart = int Function( 240 | int fd, 241 | ffi.Pointer buffer, 242 | int length, 243 | ); 244 | 245 | abstract class PollFd extends ffi.Struct { 246 | @ffi.Int32() 247 | int fd; 248 | 249 | @ffi.Int16() 250 | int events; 251 | 252 | @ffi.Int16() 253 | int revents; 254 | } 255 | -------------------------------------------------------------------------------- /lib/src/loading/loading.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 terrier989 . 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the 'License'); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an 'AS IS' BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import 'dart:ffi' as ffi; 16 | import 'dart:io'; 17 | 18 | /// Returns true when [Platform.isIOS] or [Platform.isMacOS] is true. 19 | bool get isDarwin { 20 | return Platform.isIOS || Platform.isMacOS; 21 | } 22 | 23 | /// Loads a dynamic library by trying different names. 24 | class DynamicLibraryProvider { 25 | final List linuxNames; 26 | final List darwinNames; 27 | final List windowsNames; 28 | final List otherNames; 29 | 30 | ffi.DynamicLibrary _dynamicLibrary; 31 | 32 | DynamicLibraryProvider({ 33 | this.linuxNames = const [], 34 | this.darwinNames = const [], 35 | this.windowsNames = const [], 36 | this.otherNames = const [], 37 | }); 38 | 39 | List get currentNames { 40 | if (isDarwin) { 41 | return darwinNames; 42 | } 43 | if (Platform.isLinux) { 44 | return linuxNames; 45 | } 46 | if (Platform.isWindows) { 47 | return windowsNames; 48 | } 49 | return otherNames; 50 | } 51 | 52 | List get supportedPlatforms { 53 | final result = []; 54 | if (darwinNames.isNotEmpty) { 55 | result.add('darwin'); 56 | } 57 | if (linuxNames.isNotEmpty) { 58 | result.add('linux'); 59 | } 60 | if (windowsNames.isNotEmpty) { 61 | result.add('windows'); 62 | } 63 | return result; 64 | } 65 | 66 | /// Opens the dynamic library. 67 | /// 68 | /// If the dynamic library is not found, throws [UnsupportedError]. 69 | ffi.DynamicLibrary open() { 70 | var result = _dynamicLibrary; 71 | if (result != null) { 72 | return result; 73 | } 74 | final names = currentNames; 75 | if (names.isEmpty) { 76 | final platformList = supportedPlatforms.toList()..sort(); 77 | throw UnsupportedError( 78 | 'The library is only supported in: ${platformList.join(', ')}', 79 | ); 80 | } 81 | Object firstError; 82 | for (var name in currentNames) { 83 | try { 84 | final result = ffi.DynamicLibrary.open(name); 85 | _dynamicLibrary = result; 86 | return result; 87 | } catch (e) { 88 | firstError ??= e; 89 | } 90 | } 91 | if (firstError != null) { 92 | throw firstError; 93 | } 94 | final joined = names.join(''); 95 | throw UnsupportedError( 96 | 'Could not find "$joined"', 97 | ); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /lib/src/virtual_memory/virtual_memory.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 terrier989 . 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import 'dart:ffi' as ffi; 16 | import 'dart:io'; 17 | import 'dart:typed_data'; 18 | 19 | import 'package:ffi/ffi.dart' as ffi; 20 | 21 | import '../libc/all.dart' as libc; 22 | import 'virtual_memory_impl_posix.dart'; 23 | import 'virtual_memory_impl_windows.dart'; 24 | 25 | abstract class VirtualMemory { 26 | static const int protectionNoAccess = libc.PROT_NONE; 27 | static const int protectionRead = libc.PROT_READ; 28 | static const int protectionWrite = libc.PROT_WRITE; 29 | static const int protectionReadWrite = libc.PROT_READ | libc.PROT_WRITE; 30 | static const int protectionExecute = libc.PROT_EXEC; 31 | static const int flagsShared = libc.MAP_SHARED; 32 | static const int flagsPrivate = libc.MAP_PRIVATE; 33 | static const int flagsAnonymous = libc.MAP_ANONYMOUS; 34 | 35 | factory VirtualMemory.allocate( 36 | int size, { 37 | int protection, 38 | int flags, 39 | }) { 40 | if (size < 0) { 41 | throw ArgumentError.value(size, 'size'); 42 | } 43 | protection ??= protectionRead | protectionWrite; 44 | flags ??= flagsPrivate | flagsAnonymous; 45 | if (Platform.isWindows) { 46 | return VirtualMemoryImplWindows.allocate( 47 | size, 48 | protection: protection, 49 | flags: flags, 50 | ); 51 | } 52 | return VirtualMemoryImplPosix.allocate( 53 | size, 54 | protection: protection, 55 | flags: flags, 56 | ); 57 | } 58 | 59 | factory VirtualMemory.fromAddress(int address, int size) { 60 | if (Platform.isWindows) { 61 | return VirtualMemoryImplWindows.fromPointer( 62 | ffi.Pointer.fromAddress(address), 63 | size, 64 | ); 65 | } 66 | return VirtualMemoryImplPosix.fromPointer( 67 | ffi.Pointer.fromAddress(address), 68 | size, 69 | ); 70 | } 71 | 72 | int get address; 73 | Uint8List get asUint8List; 74 | int get size; 75 | void free(); 76 | void setProtection(int protection); 77 | } 78 | -------------------------------------------------------------------------------- /lib/src/virtual_memory/virtual_memory_impl_posix.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 terrier989 . 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import 'dart:ffi' as ffi; 16 | import 'dart:typed_data'; 17 | 18 | import 'package:ffi/ffi.dart' as ffi; 19 | 20 | import '../libc/all.dart' as libc; 21 | import 'virtual_memory.dart'; 22 | 23 | class VirtualMemoryImplPosix implements VirtualMemory { 24 | final ffi.Pointer _pointer; 25 | 26 | @override 27 | final int size; 28 | 29 | @override 30 | final Uint8List asUint8List; 31 | 32 | factory VirtualMemoryImplPosix.fromPointer( 33 | ffi.Pointer pointer, int size) { 34 | final data = pointer.cast().asTypedList(size); 35 | return VirtualMemoryImplPosix._(pointer, size, data); 36 | } 37 | 38 | VirtualMemoryImplPosix._(this._pointer, this.size, this.asUint8List); 39 | 40 | @override 41 | int get address => _pointer.address; 42 | 43 | @override 44 | void free() { 45 | libc.munmap( 46 | _pointer, 47 | size, 48 | ); 49 | } 50 | 51 | @override 52 | void setProtection(int protection) { 53 | libc.mprotect(_pointer, size, protection); 54 | } 55 | 56 | static VirtualMemory allocate( 57 | int size, { 58 | int protection, 59 | int flags, 60 | }) { 61 | final pointer = libc.mmap( 62 | ffi.Pointer.fromAddress(0), 63 | size, 64 | protection, 65 | flags, 66 | -1, 67 | 0, 68 | ); 69 | if (pointer.address < 0) { 70 | throw StateError( 71 | 'Allocating $size bytes of memory failed: ${libc.errorDescription}', 72 | ); 73 | } 74 | return VirtualMemoryImplPosix.fromPointer(pointer, size); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/src/virtual_memory/virtual_memory_impl_windows.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 terrier989 . 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import 'dart:ffi' as ffi; 16 | import 'dart:typed_data'; 17 | 18 | import 'package:ffi/ffi.dart' as ffi; 19 | 20 | import '../libc/all.dart' as libc; 21 | import '../windows/windows.dart' as windows; 22 | import 'virtual_memory.dart'; 23 | 24 | class VirtualMemoryImplWindows implements VirtualMemory { 25 | final ffi.Pointer _pointer; 26 | 27 | @override 28 | final int size; 29 | 30 | VirtualMemoryImplWindows.fromPointer(this._pointer, this.size); 31 | 32 | @override 33 | int get address => _pointer.address; 34 | 35 | @override 36 | Uint8List get asUint8List { 37 | return _pointer.asTypedList(size); 38 | } 39 | 40 | @override 41 | void free() { 42 | windows.virtualFree( 43 | _pointer, 44 | size, 45 | windows.MEM_DECOMMIT | windows.MEM_RELEASE, 46 | ); 47 | } 48 | 49 | @override 50 | void setProtection(int protection) { 51 | final oldPtr = ffi.allocate(); 52 | try { 53 | windows.virtualProtect( 54 | _pointer, 55 | size, 56 | _toWindowsProtection(protection), 57 | oldPtr, 58 | ); 59 | } finally { 60 | ffi.free(oldPtr); 61 | } 62 | } 63 | 64 | static VirtualMemory allocate( 65 | int size, { 66 | int protection, 67 | int flags, 68 | }) { 69 | final nullPointer = ffi.Pointer.fromAddress(0); 70 | final resultPointer = windows.virtualAlloc( 71 | nullPointer, 72 | size, 73 | _toWindowsFlags(flags), 74 | _toWindowsProtection(protection), 75 | ); 76 | return VirtualMemoryImplWindows.fromPointer(resultPointer, size); 77 | } 78 | 79 | static int _toWindowsFlags(int flags) { 80 | var result = windows.MEM_COMMIT | windows.MEM_RESERVE; 81 | return result; 82 | } 83 | 84 | static int _toWindowsProtection(int protection) { 85 | final isExecute = protection & libc.PROT_EXEC != 0; 86 | final isRead = protection & libc.PROT_READ != 0; 87 | final isWrite = protection & libc.PROT_WRITE != 0; 88 | if (isExecute) { 89 | if (isRead) { 90 | if (isWrite) { 91 | return windows.PAGE_EXECUTE_READWRITE; 92 | } 93 | return windows.PAGE_EXECUTE_READ; 94 | } 95 | return windows.PAGE_EXECUTE; 96 | } 97 | if (isWrite) { 98 | return windows.PAGE_READWRITE; 99 | } 100 | if (isRead) { 101 | return windows.PAGE_READONLY; 102 | } 103 | return windows.PAGE_NOACCESS; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /lib/src/windows/windows.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 terrier989 . 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the 'License'); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an 'AS IS' BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// Access to Windows kernel functions. 16 | library os.windows.kernel; 17 | 18 | import 'dart:ffi' as ffi; 19 | 20 | import 'package:ffi/ffi.dart' as ffi; 21 | import 'package:os/loading.dart'; 22 | 23 | const MEM_COALESCE_PLACEHOLDERS = 0x1; 24 | const MEM_COMMIT = 0x1000; 25 | const MEM_DECOMMIT = 0x4000; 26 | const MEM_LARGE_PAGES = 0x20000000; 27 | const MEM_PRESERVE_PLACEHOLDER = 0x2; 28 | const MEM_RELEASE = 0x8000; 29 | const MEM_RESERVE = 0x2000; 30 | const MEM_RESET = 0x80000; 31 | const PAGE_EXECUTE = 0x10; 32 | 33 | const PAGE_EXECUTE_READ = 0x20; 34 | const PAGE_EXECUTE_READWRITE = 0x40; 35 | const PAGE_EXECUTE_WRITECOPY = 0x80; 36 | const PAGE_NOACCESS = 0x1; 37 | const PAGE_READONLY = 0x2; 38 | const PAGE_READWRITE = 0x4; 39 | const PAGE_WRITECOPY = 0x8; 40 | 41 | final libraryLoader = DynamicLibraryProvider( 42 | windowsNames: ['kernel32.dll'], 43 | ); 44 | 45 | final virtualAlloc = 46 | libraryLoader.open().lookupFunction<_virtualAlloc_C, _virtualAlloc_Dart>( 47 | 'VirtualAlloc', 48 | ); 49 | 50 | final virtualFree = 51 | libraryLoader.open().lookupFunction<_virtualFree_C, _virtualFree_Dart>( 52 | 'VirtualFree', 53 | ); 54 | 55 | final virtualProtect = libraryLoader 56 | .open() 57 | .lookupFunction<_virtualProtect_C, _virtualProtect_Dart>( 58 | 'VirtualProtect', 59 | ); 60 | 61 | typedef _virtualAlloc_C = ffi.Pointer Function( 62 | ffi.Pointer pointer, 63 | ffi.IntPtr size, 64 | ffi.Uint32 type, 65 | ffi.Uint32 protection, 66 | ); 67 | 68 | typedef _virtualAlloc_Dart = ffi.Pointer Function( 69 | ffi.Pointer pointer, 70 | int size, 71 | int type, 72 | int protection, 73 | ); 74 | 75 | typedef _virtualFree_C = ffi.Pointer Function( 76 | ffi.Pointer pointer, 77 | ffi.IntPtr size, 78 | ffi.Uint32 freeType, 79 | ); 80 | 81 | typedef _virtualFree_Dart = ffi.Pointer Function( 82 | ffi.Pointer pointer, 83 | int size, 84 | int freeType, 85 | ); 86 | 87 | typedef _virtualProtect_C = ffi.Pointer Function( 88 | ffi.Pointer pointer, 89 | ffi.IntPtr size, 90 | ffi.Uint32 protection, 91 | ffi.Pointer oldProtection, 92 | ); 93 | 94 | typedef _virtualProtect_Dart = ffi.Pointer Function( 95 | ffi.Pointer pointer, 96 | int size, 97 | int protection, 98 | ffi.Pointer oldProtection, 99 | ); 100 | -------------------------------------------------------------------------------- /lib/virtual_memory.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2019 terrier989 . 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// Provides access to virtual memory functions. 16 | library os.virtual_memory; 17 | 18 | export 'src/virtual_memory/virtual_memory.dart'; 19 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: os 2 | version: 0.1.2 3 | description: 4 | Access to operating system APIs (using 'dart:ffi'). Virtual memory management, IPC pipes, file 5 | permissions, etc. 6 | homepage: https://github.com/dart-interop/os 7 | 8 | environment: 9 | sdk: '>=2.7.0 <3.0.0' 10 | 11 | dependencies: 12 | ffi: ^0.1.3 13 | meta: ^1.1.0 14 | 15 | dev_dependencies: 16 | pedantic: ^1.9.0 17 | test: ^1.8.0 -------------------------------------------------------------------------------- /test/src/file/misc_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:os/file_system.dart'; 4 | import 'package:test/test.dart'; 5 | 6 | void main() { 7 | group('Misc file system functions', () { 8 | test('chmod', () async { 9 | final directory = Directory.systemTemp.createTempSync(); 10 | addTearDown(() { 11 | directory.deleteSync(); 12 | }); 13 | expect(_octal(directory.statSync().mode & 0x1FF), isNot('777')); 14 | await chmod(directory, 0x1FF); 15 | expect(_octal(directory.statSync().mode & 0x1FF), '777'); 16 | }); 17 | 18 | test('chmodSync', () { 19 | final directory = Directory.systemTemp.createTempSync(); 20 | addTearDown(() { 21 | directory.deleteSync(); 22 | }); 23 | expect(_octal(directory.statSync().mode & 0x1FF), isNot('777')); 24 | chmodSync(directory, 0x1FF); 25 | expect(_octal(directory.statSync().mode & 0x1FF), '777'); 26 | }); 27 | }, testOn: 'mac-os'); 28 | } 29 | 30 | String _octal(int v) => v.toRadixString(8); 31 | -------------------------------------------------------------------------------- /test/src/file/named_pipe_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:os/file_system.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | void main() { 8 | test( 9 | 'NamedPipe', 10 | () async { 11 | // Construct File 12 | final file = File.fromUri(Directory.systemTemp.uri.resolve( 13 | 'named_pipe_test', 14 | )); 15 | 16 | // Construct NamedPipe 17 | final fifo = NamedPipe(file.path); 18 | 19 | if (fifo.existsSync()) { 20 | fifo.deleteSync(); 21 | } 22 | 23 | // Create a named pipe in the system 24 | fifo.createSync(); 25 | expect(fifo.existsSync(), isTrue); 26 | addTearDown(() { 27 | if (fifo.existsSync()) { 28 | fifo.deleteSync(); 29 | } 30 | }); 31 | 32 | // Open writer and reader 33 | final receivedChunks = >[]; 34 | final readerSubscription = fifo.openRead().listen((item) { 35 | receivedChunks.add(item); 36 | }, onError: (error) { 37 | fail(error); 38 | }); 39 | addTearDown(() { 40 | readerSubscription.cancel(); 41 | }); 42 | final writer = fifo.openWrite(); 43 | 44 | // Write 45 | await writer.add(Uint8List.fromList([1, 2])); 46 | await writer.add(Uint8List.fromList([3, 4, 5])); 47 | await Future.delayed(const Duration(milliseconds: 50)); 48 | 49 | // Stop 50 | await writer.close(); 51 | await readerSubscription.cancel(); 52 | 53 | // Check that we received the data 54 | expect( 55 | receivedChunks, 56 | [ 57 | [1, 2, 3, 4, 5], 58 | ], 59 | ); 60 | 61 | // Delete 62 | fifo.deleteSync(); 63 | expect(file.existsSync(), isFalse); 64 | }, 65 | testOn: 'mac-os', 66 | timeout: const Timeout(Duration(milliseconds: 200)), 67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /test/src/virtual_memory/virtual_memory_error_script.dart: -------------------------------------------------------------------------------- 1 | import 'package:os/virtual_memory.dart'; 2 | 3 | /// PURPOSE: 4 | /// Testing that a virtual memory protection violation leads to a crash. 5 | 6 | void main(List args) { 7 | mutateMemory(args[0], int.parse(args[1]), args[2]); 8 | } 9 | 10 | void mutateMemory(String when, int protection, String op) { 11 | VirtualMemory virtualMemory; 12 | switch (when) { 13 | case 'allocate': 14 | // Allocate 15 | virtualMemory = VirtualMemory.allocate( 16 | 1024, 17 | protection: protection, 18 | ); 19 | break; 20 | 21 | case 'setProtection': 22 | // Allocate with default protection 23 | virtualMemory = VirtualMemory.allocate(1024); 24 | 25 | // Change protection 26 | virtualMemory.setProtection(protection); 27 | break; 28 | 29 | default: 30 | throw ArgumentError.value(when, 'when'); 31 | } 32 | switch (op) { 33 | case 'read': 34 | final value = virtualMemory.asUint8List[0]; 35 | print('Read: $value'); 36 | break; 37 | 38 | case 'write': 39 | virtualMemory.asUint8List[0] = 3; 40 | break; 41 | 42 | default: 43 | throw ArgumentError.value(op, 'op'); 44 | } 45 | print('SUCCESS'); 46 | } 47 | -------------------------------------------------------------------------------- /test/src/virtual_memory/virtual_memory_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:os/virtual_memory.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | void main() { 8 | group('VirtualMemory:', () { 9 | const pageSize = 4096; 10 | 11 | test('allocate(...)', () { 12 | final memory = VirtualMemory.allocate(pageSize); 13 | expect(memory, isNotNull); 14 | }); 15 | 16 | Future runVirtualMemoryScript( 17 | String when, int protection, String op) async { 18 | final future = Process.run( 19 | 'dart', 20 | [ 21 | 'test/src/virtual_memory/virtual_memory_error_script.dart' 22 | .replaceAll('/', Platform.pathSeparator), 23 | when, 24 | protection.toString(), 25 | op, 26 | ], 27 | runInShell: true, 28 | ); 29 | return future.timeout(const Duration(seconds: 5)); 30 | } 31 | 32 | Future testProtection(String when, int protection) async { 33 | final read = protection & VirtualMemory.protectionRead != 0; 34 | final write = protection & VirtualMemory.protectionWrite != 0; 35 | 36 | // Try read 37 | { 38 | final result = await runVirtualMemoryScript(when, protection, 'read'); 39 | if (read) { 40 | expect(result.stderr, ''); 41 | expect(result.stdout, contains('SUCCESS')); 42 | } else { 43 | expect(result.stderr, contains('CRASH')); 44 | } 45 | } 46 | 47 | // Try write 48 | { 49 | final result = await runVirtualMemoryScript(when, protection, 'write'); 50 | if (write) { 51 | expect(result.stderr, ''); 52 | expect(result.stdout, contains('SUCCESS')); 53 | } else { 54 | expect(result.stderr, contains('CRASH')); 55 | } 56 | } 57 | } 58 | 59 | test('allocate(..., protection:VirtualMemory.protectionNoAccess)', 60 | () async { 61 | await testProtection('allocate', VirtualMemory.protectionNoAccess); 62 | }); 63 | 64 | test('allocate(..., protection:VirtualMemory.protectionRead)', () async { 65 | await testProtection('allocate', VirtualMemory.protectionRead); 66 | }); 67 | 68 | test('allocate(..., protection:VirtualMemory.protectionReadWrite)', 69 | () async { 70 | await testProtection('allocate', VirtualMemory.protectionReadWrite); 71 | }); 72 | 73 | test('setProtection(..., VirtualMemory.protectionNoAccess)', () async { 74 | await testProtection('setProtection', VirtualMemory.protectionNoAccess); 75 | }); 76 | 77 | test('setProtection(..., VirtualMemory.protectionRead)', () async { 78 | await testProtection('setProtection', VirtualMemory.protectionRead); 79 | }); 80 | 81 | test('setProtection(..., VirtualMemory.protectionReadWrite)', () async { 82 | await testProtection('setProtection', VirtualMemory.protectionReadWrite); 83 | }); 84 | 85 | test('mutate the first element: OK', () { 86 | final memory = VirtualMemory.allocate( 87 | pageSize, 88 | protection: VirtualMemory.protectionReadWrite, 89 | ); 90 | expect(memory.asUint8List[0], 0); 91 | memory.asUint8List[0] = 3; 92 | expect(memory.asUint8List[0], 3); 93 | }); 94 | 95 | test('mutate the last element', () { 96 | final memory = VirtualMemory.allocate(pageSize); 97 | final last = pageSize - 1; 98 | expect(memory.asUint8List[last], 0); 99 | memory.asUint8List[last] = 3; 100 | expect(memory.asUint8List[last], 3); 101 | expect(() => memory.asUint8List[last + 1], throwsArgumentError); 102 | }); 103 | 104 | test('free', () { 105 | final memory = VirtualMemory.allocate(pageSize); 106 | 107 | // Free the memory 108 | // TODO: How to test this? 109 | memory.free(); 110 | }); 111 | }, testOn: 'mac-os'); 112 | } 113 | --------------------------------------------------------------------------------