├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── .scalafmt.conf ├── DEVELOPING.md ├── LICENSE.txt ├── README.md ├── build.sbt ├── examples └── echo-server │ ├── README.md │ ├── dependency-reduced-pom.xml │ ├── nodejs-client │ ├── .gitignore │ ├── index.js │ ├── package-lock.json │ └── package.json │ ├── pom.xml │ └── src │ └── main │ └── java │ └── ipcsocketexample │ ├── EchoServer.java │ └── ExampleApp.java ├── jni ├── org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary.h ├── org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider.c ├── org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider.h ├── org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider.c ├── org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider.h └── org_scalasbt_ipcsocket_UnixDomainSocketLibrary.h ├── project ├── build.properties └── plugins.sbt └── src ├── main ├── java │ └── org │ │ └── scalasbt │ │ └── ipcsocket │ │ ├── JNAWin32NamedPipeLibraryProvider.java │ │ ├── JNIUnixDomainSocketLibraryProvider.java │ │ ├── JNIWin32NamedPipeLibraryProvider.java │ │ ├── NativeErrorException.java │ │ ├── NativeLoader.java │ │ ├── ReferenceCountedFileDescriptor.java │ │ ├── ServerSocketWrapper.java │ │ ├── SocketWrapper.java │ │ ├── UnixDomainServerSocket.java │ │ ├── UnixDomainSocket.java │ │ ├── UnixDomainSocketLibrary.java │ │ ├── UnixDomainSocketLibraryProvider.java │ │ ├── Win32NamedPipeLibrary.java │ │ ├── Win32NamedPipeLibraryProvider.java │ │ ├── Win32NamedPipeServerSocket.java │ │ ├── Win32NamedPipeSocket.java │ │ ├── Win32SecurityLevel.java │ │ └── Win32SecurityLibrary.java └── resources │ ├── darwin │ └── x86_64 │ │ └── libsbtipcsocket.dylib │ ├── linux │ ├── aarch64 │ │ └── libsbtipcsocket.so │ └── x86_64 │ │ └── libsbtipcsocket.so │ └── win32 │ └── x86_64 │ └── sbtipcsocket.dll └── test ├── java-17 └── org │ └── scalasbt │ └── ipcsocket │ └── ServerSocketWrapperTest.java └── java └── org └── scalasbt └── ipcsocket ├── BaseSocketSetup.java ├── EchoServer.java ├── SocketTest.java ├── SocketTestJNI.java ├── UnixSocketLengthTest.java └── duplex ├── DuplexClient.java ├── DuplexServer.java ├── DuplexTest.java ├── Receiver.java └── Sender.java /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [pull_request, push] 3 | 4 | jobs: 5 | test: 6 | timeout-minutes: 20 7 | strategy: 8 | fail-fast: false 9 | matrix: 10 | include: 11 | - os: ubuntu-22.04 12 | java: 8 13 | jobtype: 1 14 | - os: ubuntu-22.04 15 | java: 25 16 | jobtype: 2 17 | - os: macos-13 18 | java: 8 19 | jobtype: 1 20 | - os: macos-14 21 | java: 8 22 | jobtype: 1 23 | - os: windows-latest 24 | java: 8 25 | jobtype: 1 26 | - os: windows-latest 27 | java: 25 28 | jobtype: 2 29 | runs-on: ${{ matrix.os }} 30 | env: 31 | JAVA_OPTS: -Xms800M -Xmx2G -Xss6M -XX:ReservedCodeCacheSize=128M -server -Dsbt.io.virtual=false -Dfile.encoding=UTF-8 32 | JVM_OPTS: -Xms800M -Xmx2G -Xss6M -XX:ReservedCodeCacheSize=128M -server -Dsbt.io.virtual=false -Dfile.encoding=UTF-8 33 | steps: 34 | - name: Checkout 35 | uses: actions/checkout@v5 36 | - name: Setup JDK 37 | uses: actions/setup-java@v5 38 | with: 39 | distribution: zulu 40 | java-version: "${{ matrix.java }}" 41 | cache: sbt 42 | - uses: sbt/setup-sbt@v1 43 | - name: Set up MinGW 44 | if: ${{ runner.os == 'Linux' }} 45 | uses: egor-tensin/setup-mingw@v2 46 | with: 47 | platform: x64 48 | - name: Set up gcc-aarch64-linux-gnu 49 | if: ${{ runner.os == 'Linux' }} 50 | shell: bash 51 | run: sudo apt-get -y install gcc-aarch64-linux-gnu 52 | - name: Build and test (Linux) 53 | if: ${{ runner.os == 'Linux' }} 54 | shell: bash 55 | run: | 56 | sbt "jvmfmtCheckAll; buildNativeArtifacts; test" 57 | - name: Build and test (macOS) 58 | if: ${{ runner.os == 'macOS' }} 59 | shell: bash 60 | run: sbt "buildNativeArtifacts; test" 61 | - name: Build and test (Windows) 62 | if: ${{ runner.os == 'Windows' }} 63 | shell: bash 64 | run: sbt "buildNativeArtifacts; test" 65 | - name: Archive native artifacts (Linux) 66 | if: ${{ runner.os == 'Linux' && matrix.jobtype == '1' }} 67 | uses: actions/upload-artifact@v4 68 | with: 69 | name: dist-${{ runner.os }} 70 | path: | 71 | src/main/resources/linux/x86_64/libsbtipcsocket.so 72 | src/main/resources/linux/aarch64/libsbtipcsocket.so 73 | src/main/resources/win32/x86_64/sbtipcsocket.dll 74 | - name: Archive native artifacts (macOS) 75 | if: ${{ matrix.os == 'macos-13' }} 76 | uses: actions/upload-artifact@v4 77 | with: 78 | name: dist-${{ runner.os }} 79 | path: src/main/resources/darwin/x86_64/libsbtipcsocket.dylib 80 | - name: Test examples compilation 81 | shell: bash 82 | run: | 83 | cd examples/echo-server 84 | mvn package 85 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/target 2 | 3 | # Eclipse/VsCode 4 | .apt_generated 5 | .classpath 6 | .factorypath 7 | .project 8 | .settings 9 | 10 | # VsCode 11 | .vscode 12 | 13 | # Metals 14 | .metals 15 | 16 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = 2.3.2 2 | edition = 2019-10 3 | maxColumn = 100 4 | project.git = true 5 | project.excludeFilters = [ "\\Wsbt-test\\W", "\\Winput_sources\\W", "\\Wcontraband-scala\\W" ] 6 | 7 | # http://docs.scala-lang.org/style/scaladoc.html recommends the JavaDoc style. 8 | # scala/scala is written that way too https://github.com/scala/scala/blob/v2.12.2/src/library/scala/Predef.scala 9 | docstrings = JavaDoc 10 | 11 | # This also seems more idiomatic to include whitespace in import x.{ yyy } 12 | spaces.inImportCurlyBraces = true 13 | 14 | # This is more idiomatic Scala. 15 | # http://docs.scala-lang.org/style/indentation.html#methods-with-numerous-arguments 16 | align.openParenCallSite = false 17 | align.openParenDefnSite = false 18 | 19 | # For better code clarity 20 | danglingParentheses = true 21 | 22 | trailingCommas = preserve 23 | -------------------------------------------------------------------------------- /DEVELOPING.md: -------------------------------------------------------------------------------- 1 | Developing ipcsocket requires the use of the 2 | [sbt](https://www.scala-sbt.org/1.x/docs/index.html) build tool. To generate a 3 | new version of the library for testing, run 4 | ``` 5 | sbt publishLocal 6 | ``` 7 | (or run `publishLocal` in the sbt command shell). This will generate a snapshot 8 | version that you can add to another sbt project with: 9 | ``` 10 | libraryDependencies += "org.scalasbt" % "ipcsocket" % "1.0.1-SNAPSHOT" 11 | ``` 12 | Project tests can be run with the `test` command. 13 | 14 | #### JNI library 15 | 16 | There are two implementations of both the `UnixDomain*Socket*` and the 17 | `Win32Named*Socket*` classes. One implementation uses 18 | [jna](https://en.wikipedia.org/wiki/Java_Native_Access#External_links) while the 19 | other uses [jni](https://en.wikipedia.org/wiki/Java_Native_Interface). In order 20 | to test the latter implementation, it is necessary to compile native code. This 21 | requires a working installation of [gcc](https://gcc.gnu.org) (for posix 22 | systems) and/or [mingw-w64](http://mingw-w64.org/doku.php) (for windows) in the 23 | sbt library path. MingW is used to build windows native libraries and is 24 | available for mac and linux as part of the standard package management systems. 25 | Install on mac with homebrew using: 26 | ``` 27 | brew install mingw-w64 28 | ``` 29 | Install on ubuntu/debian with: 30 | ``` 31 | sudo apt install mingw-w64 32 | ``` 33 | There is a [chocolatey](https://chocolatey.org) package available for windows, 34 | but it requires more work to get the toolchain to work correctly with the sbt 35 | build. 36 | 37 | Once mingw-w64 is set up, run: 38 | 39 | ``` 40 | sbt buildNativeArtifacts 41 | ``` 42 | 43 | to build the native artifacts. This is done automatically for the CI on Github Actions. 44 | 45 | #### Releasing 46 | 47 | Each release should include the latest binaries. The binaries are built during 48 | each CI run on Github Actions. To include them, go to the latest passing Github 49 | Actions at . The required 50 | artifacts can be found under the Artifacts section as 51 | "dist-Linux.zip" and "dist-macOS.zip" links. 52 | 53 | The dist-Linux.zip provides: 54 | * src/main/resources/linux/x86_64/libsbtipcsocket.so 55 | * src/main/resources/linux/aarch64/libsbtipcsocket.so 56 | * src/main/resources/win32/x86_64/sbtipcsocket.dll 57 | 58 | while the dist-macOS.zip provides: 59 | * src/main/resources/darwin/x86_64/libsbtipcsocket.dylib 60 | 61 | Extract each of these files and overwrite the existing library in the project's 62 | `src/main/resources` directory. Check in the overwritten libraries and push the 63 | changes in a new PR that updates the binaries. Once that PR is merged, it is 64 | safe to make a release off of that commit. 65 | 66 | ```sh 67 | mv $HOME/Downloads/dist-Linux/win32/x86_64/sbtipcsocket.dll src/main/resources/win32/x86_64/sbtipcsocket.dll 68 | mv $HOME/Downloads/dist-Linux/linux/x86_64/libsbtipcsocket.so src/main/resources/linux/x86_64/libsbtipcsocket.so 69 | mv $HOME/Downloads/dist-Linux/linux/aarch64/libsbtipcsocket.so src/main/resources/linux/aarch64/libsbtipcsocket.so 70 | mv $HOME/Downloads/libsbtipcsocket.dylib src/main/resources/darwin/x86_64/libsbtipcsocket.dylib 71 | ``` 72 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Nailgun Copyright © 2004-2012, Martian Software, Inc. 2 | 3 | Apache License 4 | Version 2.0, January 2004 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, 12 | and distribution as defined by Sections 1 through 9 of this document. 13 | 14 | "Licensor" shall mean the copyright owner or entity authorized by 15 | the copyright owner that is granting the License. 16 | 17 | "Legal Entity" shall mean the union of the acting entity and all 18 | other entities that control, are controlled by, or are under common 19 | control with that entity. For the purposes of this definition, 20 | "control" means (i) the power, direct or indirect, to cause the 21 | direction or management of such entity, whether by contract or 22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 23 | outstanding shares, or (iii) beneficial ownership of such entity. 24 | 25 | "You" (or "Your") shall mean an individual or Legal Entity 26 | exercising permissions granted by this License. 27 | 28 | "Source" form shall mean the preferred form for making modifications, 29 | including but not limited to software source code, documentation 30 | source, and configuration files. 31 | 32 | "Object" form shall mean any form resulting from mechanical 33 | transformation or translation of a Source form, including but 34 | not limited to compiled object code, generated documentation, 35 | and conversions to other media types. 36 | 37 | "Work" shall mean the work of authorship, whether in Source or 38 | Object form, made available under the License, as indicated by a 39 | copyright notice that is included in or attached to the work 40 | (an example is provided in the Appendix below). 41 | 42 | "Derivative Works" shall mean any work, whether in Source or Object 43 | form, that is based on (or derived from) the Work and for which the 44 | editorial revisions, annotations, elaborations, or other modifications 45 | represent, as a whole, an original work of authorship. For the purposes 46 | of this License, Derivative Works shall not include works that remain 47 | separable from, or merely link (or bind by name) to the interfaces of, 48 | the Work and Derivative Works thereof. 49 | 50 | "Contribution" shall mean any work of authorship, including 51 | the original version of the Work and any modifications or additions 52 | to that Work or Derivative Works thereof, that is intentionally 53 | submitted to Licensor for inclusion in the Work by the copyright owner 54 | or by an individual or Legal Entity authorized to submit on behalf of 55 | the copyright owner. For the purposes of this definition, "submitted" 56 | means any form of electronic, verbal, or written communication sent 57 | to the Licensor or its representatives, including but not limited to 58 | communication on electronic mailing lists, source code control systems, 59 | and issue tracking systems that are managed by, or on behalf of, the 60 | Licensor for the purpose of discussing and improving the Work, but 61 | excluding communication that is conspicuously marked or otherwise 62 | designated in writing by the copyright owner as "Not a Contribution." 63 | 64 | "Contributor" shall mean Licensor and any individual or Legal Entity 65 | on behalf of whom a Contribution has been received by Licensor and 66 | subsequently incorporated within the Work. 67 | 68 | 2. Grant of Copyright License. Subject to the terms and conditions of 69 | this License, each Contributor hereby grants to You a perpetual, 70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 71 | copyright license to reproduce, prepare Derivative Works of, 72 | publicly display, publicly perform, sublicense, and distribute the 73 | Work and such Derivative Works in Source or Object form. 74 | 75 | 3. Grant of Patent License. Subject to the terms and conditions of 76 | this License, each Contributor hereby grants to You a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 78 | (except as stated in this section) patent license to make, have made, 79 | use, offer to sell, sell, import, and otherwise transfer the Work, 80 | where such license applies only to those patent claims licensable 81 | by such Contributor that are necessarily infringed by their 82 | Contribution(s) alone or by combination of their Contribution(s) 83 | with the Work to which such Contribution(s) was submitted. If You 84 | institute patent litigation against any entity (including a 85 | cross-claim or counterclaim in a lawsuit) alleging that the Work 86 | or a Contribution incorporated within the Work constitutes direct 87 | or contributory patent infringement, then any patent licenses 88 | granted to You under this License for that Work shall terminate 89 | as of the date such litigation is filed. 90 | 91 | 4. Redistribution. You may reproduce and distribute copies of the 92 | Work or Derivative Works thereof in any medium, with or without 93 | modifications, and in Source or Object form, provided that You 94 | meet the following conditions: 95 | 96 | (a) You must give any other recipients of the Work or 97 | Derivative Works a copy of this License; and 98 | 99 | (b) You must cause any modified files to carry prominent notices 100 | stating that You changed the files; and 101 | 102 | (c) You must retain, in the Source form of any Derivative Works 103 | that You distribute, all copyright, patent, trademark, and 104 | attribution notices from the Source form of the Work, 105 | excluding those notices that do not pertain to any part of 106 | the Derivative Works; and 107 | 108 | (d) If the Work includes a "NOTICE" text file as part of its 109 | distribution, then any Derivative Works that You distribute must 110 | include a readable copy of the attribution notices contained 111 | within such NOTICE file, excluding those notices that do not 112 | pertain to any part of the Derivative Works, in at least one 113 | of the following places: within a NOTICE text file distributed 114 | as part of the Derivative Works; within the Source form or 115 | documentation, if provided along with the Derivative Works; or, 116 | within a display generated by the Derivative Works, if and 117 | wherever such third-party notices normally appear. The contents 118 | of the NOTICE file are for informational purposes only and 119 | do not modify the License. You may add Your own attribution 120 | notices within Derivative Works that You distribute, alongside 121 | or as an addendum to the NOTICE text from the Work, provided 122 | that such additional attribution notices cannot be construed 123 | as modifying the License. 124 | 125 | You may add Your own copyright statement to Your modifications and 126 | may provide additional or different license terms and conditions 127 | for use, reproduction, or distribution of Your modifications, or 128 | for any such Derivative Works as a whole, provided Your use, 129 | reproduction, and distribution of the Work otherwise complies with 130 | the conditions stated in this License. 131 | 132 | 5. Submission of Contributions. Unless You explicitly state otherwise, 133 | any Contribution intentionally submitted for inclusion in the Work 134 | by You to the Licensor shall be under the terms and conditions of 135 | this License, without any additional terms or conditions. 136 | Notwithstanding the above, nothing herein shall supersede or modify 137 | the terms of any separate license agreement you may have executed 138 | with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. This License does not grant permission to use the trade 141 | names, trademarks, service marks, or product names of the Licensor, 142 | except as required for reasonable and customary use in describing the 143 | origin of the Work and reproducing the content of the NOTICE file. 144 | 145 | 7. Disclaimer of Warranty. Unless required by applicable law or 146 | agreed to in writing, Licensor provides the Work (and each 147 | Contributor provides its Contributions) on an "AS IS" BASIS, 148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 149 | implied, including, without limitation, any warranties or conditions 150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any 153 | risks associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, 156 | whether in tort (including negligence), contract, or otherwise, 157 | unless required by applicable law (such as deliberate and grossly 158 | negligent acts) or agreed to in writing, shall any Contributor be 159 | liable to You for damages, including any direct, indirect, special, 160 | incidental, or consequential damages of any character arising as a 161 | result of this License or out of the use or inability to use the 162 | Work (including but not limited to damages for loss of goodwill, 163 | work stoppage, computer failure or malfunction, or any and all 164 | other commercial damages or losses), even if such Contributor 165 | has been advised of the possibility of such damages. 166 | 167 | 9. Accepting Warranty or Additional Liability. While redistributing 168 | the Work or Derivative Works thereof, You may choose to offer, 169 | and charge a fee for, acceptance of support, warranty, indemnity, 170 | or other liability obligations and/or rights consistent with this 171 | License. However, in accepting such obligations, You may act only 172 | on Your own behalf and on Your sole responsibility, not on behalf 173 | of any other Contributor, and only if You agree to indemnify, 174 | defend, and hold each Contributor harmless for any liability 175 | incurred by, or claims asserted against, such Contributor by reason 176 | of your accepting any such warranty or additional liability. 177 | 178 | END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following 183 | boilerplate notice, with the fields enclosed by brackets "{}" 184 | replaced with your own identifying information. (Don't include 185 | the brackets!) The text should be enclosed in the appropriate 186 | comment syntax for the file format. We also recommend that a 187 | file or class name and description of purpose be included on the 188 | same "printed page" as the copyright notice for easier 189 | identification within third-party archives. 190 | 191 | Copyright {yyyy} {name of copyright owner} 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | IPC Socket 2 | ========== 3 | 4 | IPC Socket is a Java wrapper around interprocess communication (IPC) using `java.net.ServerSocket` and `java.net.Socket` as the API. 5 | 6 | On Unix-like systems, it uses Unix Domain Socket. The path is a filesystem path name. 7 | 8 | On Windows, IPC is implemented using Named Pipe. The path must refer to an entry in `\\?\pipe\` or `\\.\pipe\`. 9 | 10 | See unit tests for the details. 11 | 12 | ### Module ID 13 | 14 | ```scala 15 | libraryDependencies += "org.scala-sbt.ipcsocket" % "ipcsocket" % "1.6.1" 16 | ``` 17 | 18 | ### Examples 19 | 20 | Check out the [examples directory](./examples). 21 | 22 | ### Why not just use TCP/IP? 23 | 24 | TCP/IP is open to everyone on the network, if someone can get hold of your machine, the person could connect to it. 25 | This raises security concerns for some usages (like build tools) since it could lead to arbitrary code execution if used without authentication. 26 | 27 | ### License 28 | 29 | Apache v2 30 | 31 | The server socket code was originally taken from Nailgun project, and client-side was added. 32 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | import java.nio.file.{ Files, Path, Paths } 2 | import java.io.InputStream 3 | import java.util.concurrent.TimeUnit 4 | import scala.collection.mutable.ArrayBuffer 5 | import scala.util.Try 6 | import scala.sys.process._ 7 | 8 | val jnaVersion = "5.12.0" 9 | val jna = "net.java.dev.jna" % "jna" % jnaVersion 10 | 11 | val jnaPlatform = "net.java.dev.jna" % "jna-platform" % jnaVersion 12 | val junitInterface = "com.github.sbt" % "junit-interface" % "0.13.3" 13 | val nativePlatform = settingKey[String]("The target platform") 14 | val nativeArch = settingKey[String]("The target architecture") 15 | val nativeArtifact = settingKey[Path]("The target artifact location") 16 | val nativeBuild = taskKey[Path]("Build the native artifact") 17 | val nativeCompiler = settingKey[String]("The compiler for native compilation") 18 | val nativeCompileOptions = settingKey[Seq[String]]("The native compilation options") 19 | val nativeIncludes = settingKey[Seq[String]]("The native include paths") 20 | val buildDarwin = taskKey[Path]("Build fat binary for x86_64 and arm64 on mac os") 21 | val buildDarwinX86_64 = taskKey[Path]("Build mac native library for x86_64") 22 | val buildDarwinArm64 = taskKey[Path]("Build mac native library for arm64") 23 | val buildLinuxX86_64 = taskKey[Path]("Build Linux native library for x86_64") 24 | val buildLinuxAarch64 = taskKey[Path]("Build Linux native library for Aarch64") 25 | val buildWin32X86_64 = taskKey[Path]("Build windows native library for x86_64") 26 | 27 | val isMac = scala.util.Properties.isMac 28 | val isWin = scala.util.Properties.isWin 29 | val libShortName = "sbtipcsocket" 30 | val platforms = Map( 31 | "win32" -> s"$libShortName.dll", 32 | "darwin" -> s"lib$libShortName.dylib", 33 | "linux" -> s"lib$libShortName.so" 34 | ) 35 | 36 | buildDarwin := { 37 | if (!(buildDarwin / skip).value) { 38 | val fatBinary = 39 | (Compile / resourceDirectory).value.toPath / "darwin" / "x86_64" / platforms("darwin") 40 | val x86 = buildDarwinX86_64.value.toString 41 | val arm = buildDarwinArm64.value.toString 42 | val logger = streams.value.log 43 | scala.util.Try(eval(Seq("lipo", "-create", "-o", s"$fatBinary", x86, arm), logger)) 44 | fatBinary 45 | } else (Compile / resourceDirectory).value.toPath / "darwin" / "x86_64" / platforms("darwin") 46 | } 47 | 48 | inThisBuild( 49 | List( 50 | organization := "org.scala-sbt.ipcsocket", 51 | organizationName := "sbt", 52 | organizationHomepage := Some(url("http://scala-sbt.org/")), 53 | homepage := scmInfo.value map (_.browseUrl), 54 | scmInfo := Some( 55 | ScmInfo(url("https://github.com/sbt/ipcsocket"), "git@github.com:sbt/ipcsocket.git") 56 | ), 57 | Compile / javacOptions ++= Seq("-h", (baseDirectory.value / "jni").toString), 58 | Compile / doc / javacOptions := Nil, 59 | developers := List( 60 | Developer("eed3si9n", "Eugene Yokota", "@eed3si9n", url("https://github.com/eed3si9n")) 61 | ), 62 | isSnapshot := (isSnapshot or version(_ endsWith "-SNAPSHOT")).value, 63 | description := "IPC: Unix Domain Socket and Windows Named Pipes for Java", 64 | licenses := Seq("Apache 2" -> url("http://www.apache.org/licenses/LICENSE-2.0.txt")), 65 | publishTo := { 66 | val nexus = "https://oss.sonatype.org/" 67 | if (isSnapshot.value) Some("snapshots" at nexus + "content/repositories/snapshots") 68 | else Some("releases" at nexus + "service/local/staging/deploy/maven2") 69 | }, 70 | nativeArch := "x86_64", 71 | nativeCompiler := "gcc", 72 | nativeCompileOptions := "-shared" :: "-O2" :: "-Wall" :: "-Wextra" :: Nil, 73 | nativePlatform := (System.getProperty("os.name").head.toLower match { 74 | case 'm' => "darwin" 75 | case 'w' => "win32" 76 | case 'l' => "linux" 77 | case _ => "unknown" 78 | }), 79 | nativeIncludes := { 80 | val home = 81 | javaHome.value.getOrElse(throw new IllegalStateException("No java home defined")) 82 | s"-I${home / "include"}" :: s"-I${home / "include" / nativePlatform.value}" :: Nil 83 | }, 84 | ) 85 | ) 86 | name := "ipcsocket" 87 | libraryDependencies ++= Seq(jna, jnaPlatform, junitInterface % Test) 88 | crossPaths := false 89 | autoScalaLibrary := false 90 | nativeLibrarySettings("darwinX86_64") 91 | nativeLibrarySettings("darwinArm64") 92 | nativeLibrarySettings("linuxX86_64") 93 | nativeLibrarySettings("linuxAarch64") 94 | nativeLibrarySettings("win32X86_64") 95 | if (!isWin) (buildWin32X86_64 / nativeCompiler := "x86_64-w64-mingw32-gcc") :: Nil else Nil 96 | buildLinuxAarch64 / nativeCompiler := "aarch64-linux-gnu-gcc" 97 | buildWin32X86_64 / skip := { 98 | val s = streams.value 99 | Try(s"which ${(buildWin32X86_64 / nativeCompiler).value}".!!).fold(_ => { 100 | s.log.warn( 101 | s"skipping buildWin32X86_64 because ${(buildWin32X86_64 / nativeCompiler).value} was not found" 102 | ) 103 | true 104 | }, _.isEmpty) 105 | } 106 | Test / fork := true 107 | clangfmt / fileInputs += baseDirectory.value.toGlob / "jni" / "*.c" 108 | commands += Command.command("buildNativeArtifacts") { state => 109 | "buildLinuxX86_64" :: "buildLinuxAarch64" :: "buildDarwin" :: "buildWin32X86_64" :: state 110 | } 111 | 112 | Global / javaHome := { 113 | System.getProperty("java.home") match { 114 | case null => None 115 | case h if h.endsWith("jre") => Some(Paths.get(h).getParent.toFile) 116 | case h => Some(file(h)) 117 | } 118 | } 119 | 120 | def nativeLibrarySettings(platform: String): Seq[Setting[_]] = { 121 | val key = TaskKey[Path](s"build${platform.head.toUpper}${platform.tail}") 122 | val shortPlatform = 123 | platform match { 124 | case p if p.startsWith("darwin") => "darwin" 125 | case p if p.startsWith("linux") => "linux" 126 | case p if p.startsWith("win32") => "win32" 127 | } 128 | Def.settings( 129 | key / nativeArch := { 130 | val orig = (key / nativeArch).value 131 | if (platform.contains("Arm64")) "arm64" 132 | else if (platform.contains("Aarch64")) "aarch64" 133 | else orig 134 | }, 135 | key / nativeCompileOptions ++= (shortPlatform match { 136 | case "win32" => 137 | Seq("-D__WIN__", "-lkernel32", "-ladvapi32", "-ffreestanding", "-fdiagnostics-color=always") 138 | case "darwin" => Seq("-arch", (key / nativeArch).value) 139 | case _ => Nil 140 | }), 141 | key / nativeArtifact := { 142 | val name = platforms.get(shortPlatform).getOrElse(s"lib$libShortName.so") 143 | val resourceDir = (Compile / resourceDirectory).value.toPath 144 | val targetDir = (Compile / target).value.toPath 145 | val arch = (key / nativeArch).value 146 | if (shortPlatform == "darwin") targetDir / platform / arch / name 147 | else resourceDir / shortPlatform / arch / name 148 | }, 149 | key / fileInputs += { 150 | val glob = if (shortPlatform == "win32") "*Win*.{c,h}" else "*Unix*.{c,h}" 151 | baseDirectory.value.toGlob / "jni" / glob, 152 | }, 153 | key / skip := ((ThisBuild / nativePlatform).value match { 154 | case `platform` => false 155 | case p if shortPlatform == "win32" => false 156 | case p => !platform.startsWith(p) 157 | }), 158 | key / nativeBuild := { 159 | val artifact = (key / nativeArtifact).value 160 | val inputs = key.inputFiles.collect { 161 | case i if i.getFileName.toString.endsWith(".c") => i.toString 162 | } 163 | val options = (key / nativeCompileOptions).value 164 | val compiler = (key / nativeCompiler).value 165 | val logger = streams.value.log 166 | val includes = (key / nativeIncludes).value 167 | val s = streams.value 168 | s.log.info(s"""compiling ${inputs.mkString(", ")}""") 169 | if (key.inputFileChanges.hasChanges || !artifact.toFile.exists) { 170 | Files.createDirectories(artifact.getParent) 171 | eval(Seq(compiler, "-o", artifact.toString) ++ includes ++ options ++ inputs, logger) 172 | } 173 | s.log.info(s"""done compiling $artifact""") 174 | artifact 175 | }, 176 | key := { 177 | if ((key / skip).value) (key / nativeArtifact).value 178 | else (key / nativeBuild).value 179 | }, 180 | key := key.dependsOn(Compile / compile).value, 181 | ) 182 | } 183 | 184 | def eval(cmd: Seq[String], logger: Logger): Unit = { 185 | logger.debug(s"Running compilation: ${cmd mkString " "}") 186 | val proc = new java.lang.ProcessBuilder(cmd: _*).start() 187 | val thread = new Thread() { 188 | setDaemon(true) 189 | start() 190 | val is = proc.getInputStream 191 | val es = proc.getErrorStream 192 | val isOutput = new ArrayBuffer[Int] 193 | val esOutput = new ArrayBuffer[Int] 194 | def drain(stream: InputStream, buffer: ArrayBuffer[Int], isError: Boolean): Unit = { 195 | while (stream.available > 0) { 196 | stream.read match { 197 | case 10 => 198 | val msg = new String(buffer.map(_.toByte).toArray) 199 | buffer.clear() 200 | if (isError) logger.error(msg) else logger.info(msg) 201 | case c => buffer += c 202 | } 203 | } 204 | } 205 | def drain(): Unit = { 206 | drain(is, isOutput, false) 207 | drain(es, esOutput, true) 208 | } 209 | override def run(): Unit = { 210 | while (proc.isAlive) { 211 | drain() 212 | Thread.sleep(10) 213 | } 214 | drain() 215 | } 216 | } 217 | proc.waitFor(1, TimeUnit.MINUTES) 218 | thread.join() 219 | if (proc.exitValue != 0) 220 | throw new IllegalStateException( 221 | s"'${cmd mkString " "}' exited with ${proc.exitValue}" 222 | ) 223 | } 224 | 225 | Test / unmanagedSourceDirectories ++= { 226 | if (scala.util.Properties.isJavaAtLeast("17")) { 227 | Seq((Test / sourceDirectory).value / "java-17") 228 | } else { 229 | Nil 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /examples/echo-server/README.md: -------------------------------------------------------------------------------- 1 | # Echo Server Example 2 | 3 | This example shows how to create an Echo Server using Java and Maven. 4 | Along with the server, there's a simple Node.js client you can use for 5 | testing purposes. 6 | 7 | ## Running the server 8 | 9 | First you'll have to generate a JAR file: 10 | 11 | ``` 12 | cd examples/echo-server 13 | mvn package 14 | ``` 15 | 16 | And then execute it: 17 | 18 | ``` 19 | java -jar target/ipcsocketexample-0.1.0.jar 20 | ``` 21 | 22 | This should get your server running. 23 | 24 | ## Running the Node.js client 25 | 26 | > If you don't have Node.js installed, please [see how to install it](https://nodejs.org/en/download/) first. 27 | 28 | Install dependencies 29 | 30 | ``` 31 | cd examples/echo-server/nodejs-client 32 | npm install 33 | ``` 34 | 35 | And run it: 36 | 37 | ``` 38 | npm start 39 | ``` 40 | -------------------------------------------------------------------------------- /examples/echo-server/dependency-reduced-pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | org.example 5 | ipcsocketexample 6 | 0.1.0 7 | 8 | 9 | 10 | maven-shade-plugin 11 | 3.2.4 12 | 13 | 14 | package 15 | 16 | shade 17 | 18 | 19 | 20 | 21 | ipcsocketexample.ExampleApp 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 1.8 32 | 1.8 33 | 34 | 35 | -------------------------------------------------------------------------------- /examples/echo-server/nodejs-client/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /examples/echo-server/nodejs-client/index.js: -------------------------------------------------------------------------------- 1 | const ipc = require('socket-ipc') 2 | 3 | const client = new ipc.MessageClient('/tmp/socket-loc.sock') 4 | 5 | client.on('connection', function (connection) { 6 | console.log('connected. sending greetings...') 7 | client.send('greetings\n') 8 | }) 9 | 10 | client.on('message', function (message) { 11 | console.log('got message:', message.data) 12 | }) 13 | 14 | client.start() -------------------------------------------------------------------------------- /examples/echo-server/nodejs-client/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "assert-plus": { 8 | "version": "1.0.0", 9 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 10 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 11 | }, 12 | "core-util-is": { 13 | "version": "1.0.2", 14 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 15 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 16 | }, 17 | "extsprintf": { 18 | "version": "1.4.0", 19 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz", 20 | "integrity": "sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=" 21 | }, 22 | "p-timeout": { 23 | "version": "4.1.0", 24 | "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-4.1.0.tgz", 25 | "integrity": "sha512-+/wmHtzJuWii1sXn3HCuH/FTwGhrp4tmJTxSKJbfS+vkipci6osxXM5mY0jUiRzWKMTgUT8l7HFbeSwZAynqHw==" 26 | }, 27 | "socket-ipc": { 28 | "version": "2.2.0", 29 | "resolved": "https://registry.npmjs.org/socket-ipc/-/socket-ipc-2.2.0.tgz", 30 | "integrity": "sha512-WrmT/hFzlQYRyDX5Yac4OoGXornNHmJ719SowphvsV5bEpukgnOQYlgYfutGKcRcxI7f+D8fRVUOkH9ON9YO5w==", 31 | "requires": { 32 | "p-timeout": "^4.1.0", 33 | "strict-event-emitter-types": "^2.0.0", 34 | "verror": "^1.10.0" 35 | } 36 | }, 37 | "strict-event-emitter-types": { 38 | "version": "2.0.0", 39 | "resolved": "https://registry.npmjs.org/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz", 40 | "integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==" 41 | }, 42 | "verror": { 43 | "version": "1.10.0", 44 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 45 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 46 | "requires": { 47 | "assert-plus": "^1.0.0", 48 | "core-util-is": "1.0.2", 49 | "extsprintf": "^1.2.0" 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/echo-server/nodejs-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "start": "node index.js" 6 | }, 7 | "author": "Fran Mendez", 8 | "dependencies": { 9 | "socket-ipc": "^2.2.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/echo-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.example 7 | ipcsocketexample 8 | jar 9 | 0.1.0 10 | 11 | 12 | 1.8 13 | 1.8 14 | 15 | 16 | 17 | 18 | org.scala-sbt.ipcsocket 19 | ipcsocket 20 | 1.4.0 21 | 22 | 23 | 24 | 25 | 26 | 27 | org.apache.maven.plugins 28 | maven-shade-plugin 29 | 3.2.4 30 | 31 | 32 | package 33 | 34 | shade 35 | 36 | 37 | 38 | 40 | ipcsocketexample.ExampleApp 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /examples/echo-server/src/main/java/ipcsocketexample/EchoServer.java: -------------------------------------------------------------------------------- 1 | package ipcsocketexample; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.io.PrintWriter; 6 | import java.io.InputStreamReader; 7 | import java.io.BufferedReader; 8 | 9 | import java.net.ServerSocket; 10 | import java.net.Socket; 11 | 12 | import java.util.concurrent.CompletableFuture; 13 | 14 | public class EchoServer { 15 | private final ServerSocket serverSocket; 16 | 17 | public EchoServer(ServerSocket serverSocket) { 18 | this.serverSocket = serverSocket; 19 | } 20 | 21 | public void run() throws IOException { 22 | while (true) { 23 | Socket clientSocket = serverSocket.accept(); 24 | CompletableFuture.supplyAsync( 25 | () -> { 26 | try { 27 | PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); 28 | BufferedReader in = 29 | new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 30 | String line; 31 | do { 32 | line = in.readLine(); 33 | if (line != null) { 34 | System.out.println("server: " + line); 35 | out.print(line + "\n"); 36 | out.flush(); 37 | } 38 | } while (!line.trim().equals("bye")); 39 | } catch (IOException e) { 40 | } 41 | return true; 42 | }); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/echo-server/src/main/java/ipcsocketexample/ExampleApp.java: -------------------------------------------------------------------------------- 1 | package ipcsocketexample; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.io.IOException; 8 | import java.io.OutputStream; 9 | import java.io.PrintWriter; 10 | 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | import java.nio.file.Paths; 14 | import java.net.ServerSocket; 15 | import java.net.Socket; 16 | import java.util.concurrent.CompletableFuture; 17 | import java.util.function.Consumer; 18 | import org.scalasbt.ipcsocket.*; 19 | 20 | public class ExampleApp 21 | { 22 | static final boolean isWin = System.getProperty("os.name", "").toLowerCase().startsWith("win"); 23 | 24 | static ServerSocket newServerSocket(String socketName) throws IOException { 25 | return isWin 26 | ? new Win32NamedPipeServerSocket(socketName, false, Win32SecurityLevel.LOGON_DACL) 27 | : new UnixDomainServerSocket(socketName, false); 28 | } 29 | 30 | static Socket newClientSocket(String socketName) throws IOException { 31 | return isWin 32 | ? new Win32NamedPipeSocket(socketName, false) 33 | : new UnixDomainSocket(socketName, false); 34 | } 35 | 36 | public static void main( String[] args ) 37 | { 38 | Path socketPath = null; 39 | Path tempDir = null; 40 | 41 | try { 42 | tempDir = isWin ? null : Files.createTempDirectory("ipcsocket"); 43 | socketPath = tempDir != null ? tempDir.resolve("/tmp/socket-loc.sock") : null; 44 | String socketName = 45 | socketPath != null ? socketPath.toString() : "\\\\.\\pipe\\ipcsockettest"; 46 | ServerSocket serverSocket = newServerSocket(socketName); 47 | 48 | EchoServer echo = new EchoServer(serverSocket); 49 | echo.run(); 50 | } catch (IOException exception) { 51 | exception.printStackTrace(); 52 | } finally { 53 | try { 54 | if (socketPath != null) Files.deleteIfExists(socketPath); 55 | if (tempDir != null) Files.deleteIfExists(socketPath); 56 | } catch (IOException exception) { 57 | exception.printStackTrace(); 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /jni/org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary */ 4 | 5 | #ifndef _Included_org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary 6 | #define _Included_org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary 12 | * Method: socket 13 | * Signature: (III)I 14 | */ 15 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary_socket 16 | (JNIEnv *, jobject, jint, jint, jint); 17 | 18 | /* 19 | * Class: org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary 20 | * Method: bind 21 | * Signature: (ILorg/scalasbt/ipcsocket/UnixDomainSocketLibrary/SockaddrUn;I)I 22 | */ 23 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary_bind 24 | (JNIEnv *, jobject, jint, jobject, jint); 25 | 26 | /* 27 | * Class: org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary 28 | * Method: listen 29 | * Signature: (II)I 30 | */ 31 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary_listen 32 | (JNIEnv *, jobject, jint, jint); 33 | 34 | /* 35 | * Class: org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary 36 | * Method: accept 37 | * Signature: (ILorg/scalasbt/ipcsocket/UnixDomainSocketLibrary/SockaddrUn;Lcom/sun/jna/ptr/IntByReference;)I 38 | */ 39 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary_accept 40 | (JNIEnv *, jobject, jint, jobject, jobject); 41 | 42 | /* 43 | * Class: org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary 44 | * Method: connect 45 | * Signature: (ILorg/scalasbt/ipcsocket/UnixDomainSocketLibrary/SockaddrUn;I)I 46 | */ 47 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary_connect 48 | (JNIEnv *, jobject, jint, jobject, jint); 49 | 50 | /* 51 | * Class: org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary 52 | * Method: read 53 | * Signature: (I[BI)I 54 | */ 55 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary_read 56 | (JNIEnv *, jobject, jint, jbyteArray, jint); 57 | 58 | /* 59 | * Class: org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary 60 | * Method: write 61 | * Signature: (I[BI)I 62 | */ 63 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary_write 64 | (JNIEnv *, jobject, jint, jbyteArray, jint); 65 | 66 | /* 67 | * Class: org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary 68 | * Method: close 69 | * Signature: (I)I 70 | */ 71 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary_close 72 | (JNIEnv *, jobject, jint); 73 | 74 | /* 75 | * Class: org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary 76 | * Method: shutdown 77 | * Signature: (II)I 78 | */ 79 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNAUnixDomainSocketLibrary_shutdown 80 | (JNIEnv *, jobject, jint, jint); 81 | 82 | #ifdef __cplusplus 83 | } 84 | #endif 85 | #endif 86 | -------------------------------------------------------------------------------- /jni/org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider.c: -------------------------------------------------------------------------------- 1 | #include "errno.h" 2 | #include "jni.h" 3 | #include "stdio.h" 4 | #include "stdlib.h" 5 | #include "string.h" 6 | #include "sys/socket.h" 7 | #include "sys/types.h" 8 | #include "sys/un.h" 9 | #include "unistd.h" 10 | 11 | #include "org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider.h" 12 | #define UNUSED __attribute__((unused)) 13 | #define THROW_ON_ERROR(res) \ 14 | do { \ 15 | int err = errno; \ 16 | errno = 0; \ 17 | return err ? -err : (int)res; \ 18 | } while (0); 19 | 20 | static void native_sockaddr(JNIEnv *env, struct sockaddr_un *native, 21 | jobject array, jint len) { 22 | memset(native, 0, sizeof(struct sockaddr_un)); 23 | if (array) { 24 | jbyte *bytes = (*env)->GetByteArrayElements(env, (jbyteArray)array, 0); 25 | memcpy(native->sun_path, bytes, len); 26 | (*env)->ReleaseByteArrayElements(env, (jbyteArray)array, bytes, JNI_ABORT); 27 | } 28 | native->sun_family = 1; 29 | #ifdef __APPLE__ 30 | native->sun_len = len + 2; 31 | #endif 32 | } 33 | 34 | jint JNICALL 35 | Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_socketNative( 36 | UNUSED JNIEnv *env, UNUSED jclass clazz, jint domain, jint type, 37 | jint protocol) { 38 | errno = 0; 39 | int res = socket(domain, type, protocol); 40 | THROW_ON_ERROR(res) 41 | } 42 | 43 | jint JNICALL 44 | Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_bindNative( 45 | UNUSED JNIEnv *env, UNUSED jclass clazz, jint fd, jbyteArray path, 46 | jint len) { 47 | errno = 0; 48 | struct sockaddr_un addr; 49 | const struct sockaddr *sa = (struct sockaddr *)&addr; 50 | native_sockaddr(env, &addr, path, len); 51 | int res = bind(fd, sa, sizeof(struct sockaddr_un)); 52 | THROW_ON_ERROR(res) 53 | } 54 | 55 | jint JNICALL 56 | Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_listenNative( 57 | UNUSED JNIEnv *env, UNUSED jclass clazz, jint fd, jint backlog) { 58 | errno = 0; 59 | int res = listen(fd, backlog); 60 | THROW_ON_ERROR(res) 61 | } 62 | 63 | jint JNICALL 64 | Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_acceptNative( 65 | UNUSED JNIEnv *env, UNUSED jclass clazz, jint fd) { 66 | errno = 0; 67 | struct sockaddr_un addr; 68 | native_sockaddr(env, &addr, NULL, 0); 69 | socklen_t l = 0; 70 | int res = accept(fd, (struct sockaddr *)&addr, &l); 71 | THROW_ON_ERROR(res); 72 | } 73 | 74 | jint JNICALL 75 | Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_connectNative( 76 | UNUSED JNIEnv *env, UNUSED jclass clazz, jint fd, jbyteArray path, 77 | jint len) { 78 | struct sockaddr_un addr; 79 | native_sockaddr(env, &addr, path, len); 80 | errno = 0; 81 | int res = connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)); 82 | THROW_ON_ERROR(res); 83 | } 84 | 85 | jint JNICALL 86 | Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_readNative( 87 | UNUSED JNIEnv *env, UNUSED jclass clazz, jint fd, jbyteArray buffer, 88 | jint offset, jint len) { 89 | errno = 0; 90 | jbyte *bytes = malloc(len); 91 | int bytes_read = read(fd, bytes, len); 92 | (*env)->SetByteArrayRegion(env, buffer, offset, bytes_read, bytes); 93 | free(bytes); 94 | THROW_ON_ERROR(bytes_read); 95 | } 96 | 97 | jint JNICALL 98 | Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_writeNative( 99 | UNUSED JNIEnv *env, UNUSED jclass clazz, jint fd, jbyteArray buffer, 100 | jint offset, jint len) { 101 | errno = 0; 102 | jbyte *bytes = (*env)->GetByteArrayElements(env, buffer, 0); 103 | int bytes_written = write(fd, bytes + offset, len); 104 | (*env)->ReleaseByteArrayElements(env, buffer, bytes, JNI_ABORT); 105 | THROW_ON_ERROR(bytes_written); 106 | } 107 | 108 | jint JNICALL 109 | Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_closeNative( 110 | UNUSED JNIEnv *env, UNUSED jclass clazz, jint fd) { 111 | errno = 0; 112 | int res = close(fd); 113 | THROW_ON_ERROR(res); 114 | } 115 | 116 | jint JNICALL 117 | Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_shutdownNative( 118 | UNUSED JNIEnv *env, UNUSED jclass clazz, jint fd, jint how) { 119 | errno = 0; 120 | int res = shutdown(fd, how); 121 | THROW_ON_ERROR(res); 122 | } 123 | 124 | jstring JNICALL 125 | Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_errString( 126 | JNIEnv *env, UNUSED jobject object, jint code) { 127 | const char *err = strerror(code); 128 | return (*env)->NewStringUTF(env, err); 129 | } 130 | 131 | JNIEXPORT jint JNICALL 132 | Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_maxSocketLength( 133 | UNUSED JNIEnv *env, UNUSED jobject object) { 134 | struct sockaddr_un un; 135 | return sizeof(un.sun_path); 136 | } 137 | -------------------------------------------------------------------------------- /jni/org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider */ 4 | 5 | #ifndef _Included_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider 6 | #define _Included_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider 12 | * Method: socketNative 13 | * Signature: (III)I 14 | */ 15 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_socketNative 16 | (JNIEnv *, jobject, jint, jint, jint); 17 | 18 | /* 19 | * Class: org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider 20 | * Method: bindNative 21 | * Signature: (I[BI)I 22 | */ 23 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_bindNative 24 | (JNIEnv *, jobject, jint, jbyteArray, jint); 25 | 26 | /* 27 | * Class: org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider 28 | * Method: listenNative 29 | * Signature: (II)I 30 | */ 31 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_listenNative 32 | (JNIEnv *, jobject, jint, jint); 33 | 34 | /* 35 | * Class: org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider 36 | * Method: acceptNative 37 | * Signature: (I)I 38 | */ 39 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_acceptNative 40 | (JNIEnv *, jobject, jint); 41 | 42 | /* 43 | * Class: org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider 44 | * Method: connectNative 45 | * Signature: (I[BI)I 46 | */ 47 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_connectNative 48 | (JNIEnv *, jobject, jint, jbyteArray, jint); 49 | 50 | /* 51 | * Class: org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider 52 | * Method: readNative 53 | * Signature: (I[BII)I 54 | */ 55 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_readNative 56 | (JNIEnv *, jobject, jint, jbyteArray, jint, jint); 57 | 58 | /* 59 | * Class: org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider 60 | * Method: writeNative 61 | * Signature: (I[BII)I 62 | */ 63 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_writeNative 64 | (JNIEnv *, jobject, jint, jbyteArray, jint, jint); 65 | 66 | /* 67 | * Class: org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider 68 | * Method: closeNative 69 | * Signature: (I)I 70 | */ 71 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_closeNative 72 | (JNIEnv *, jobject, jint); 73 | 74 | /* 75 | * Class: org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider 76 | * Method: shutdownNative 77 | * Signature: (II)I 78 | */ 79 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_shutdownNative 80 | (JNIEnv *, jobject, jint, jint); 81 | 82 | /* 83 | * Class: org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider 84 | * Method: maxSocketLength 85 | * Signature: ()I 86 | */ 87 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_maxSocketLength 88 | (JNIEnv *, jobject); 89 | 90 | /* 91 | * Class: org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider 92 | * Method: errString 93 | * Signature: (I)Ljava/lang/String; 94 | */ 95 | JNIEXPORT jstring JNICALL Java_org_scalasbt_ipcsocket_JNIUnixDomainSocketLibraryProvider_errString 96 | (JNIEnv *, jobject, jint); 97 | 98 | #ifdef __cplusplus 99 | } 100 | #endif 101 | #endif 102 | -------------------------------------------------------------------------------- /jni/org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider.c: -------------------------------------------------------------------------------- 1 | #include "org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define UNUSED __attribute__((unused)) 8 | #define SECURITY_DESCRIPTOR_SIZE 64 * 1024 9 | #define HANDLE_OR_ERROR(h) \ 10 | (h == (jlong)INVALID_HANDLE_VALUE) ? -((jlong)GetLastError()) : (jlong)h 11 | #define DEBUG 0 12 | #define THROW_IO(prefix, ...) \ 13 | do { \ 14 | char _buf[1024]; \ 15 | snprintf(_buf, 1024, prefix ? prefix : "%s", __VA_ARGS__); \ 16 | jclass exClass = (*env)->FindClass(env, "java/io/IOException"); \ 17 | if (exClass != NULL) { \ 18 | (*env)->ThrowNew(env, exClass, _buf); \ 19 | } \ 20 | } while (0); 21 | 22 | #define FILL_ERROR(prefix, buf) \ 23 | do { \ 24 | char err[sizeof(buf)]; \ 25 | FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, \ 26 | NULL, GetLastError(), \ 27 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err, sizeof(buf), \ 28 | NULL); \ 29 | size_t len = strnlen(err, sizeof(err)); \ 30 | if (err[len - 2] == '\r') \ 31 | err[len - 2] = '\0'; \ 32 | snprintf(buf, sizeof(buf), prefix ? prefix : "%s (error code %ld)", err, \ 33 | GetLastError()); \ 34 | } while (0); 35 | 36 | #define LOGON_DACL 2 // must match the value in Win32SecurityLevel.java 37 | 38 | static int createSecurityWithDacl(PSECURITY_ATTRIBUTES pSA, DWORD accessMask, 39 | BOOL logon); 40 | 41 | BOOL WINAPI CancelIoEx(_In_ HANDLE hFile, _In_opt_ LPOVERLAPPED lpOverlapped); 42 | 43 | jlong JNICALL 44 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_CreateNamedPipeNative( 45 | JNIEnv *env, UNUSED jobject object, jstring lpName, jint dwOpenMode, 46 | jint dwPipeMode, jint nMaxInstances, jint nOutBufferSize, 47 | jint nIntBufferSize, jint nDefaultTimeout, jint lpSecurityAttributes, 48 | jint security) { 49 | PSECURITY_ATTRIBUTES pSA = security 50 | ? HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 51 | sizeof(SECURITY_ATTRIBUTES)) 52 | : NULL; 53 | int err = security ? createSecurityWithDacl(pSA, lpSecurityAttributes, 54 | security == LOGON_DACL) 55 | : 0; 56 | if (!err || !security) { 57 | LPCWSTR name = (LPCWSTR)(*env)->GetStringChars(env, lpName, 0); 58 | 59 | jlong handle = (jlong)(CreateNamedPipeW( 60 | name, dwOpenMode, dwPipeMode, nMaxInstances, nOutBufferSize, 61 | nIntBufferSize, nDefaultTimeout, pSA)); 62 | if (handle == (jlong)INVALID_HANDLE_VALUE) { 63 | char buf[512]; 64 | FILL_ERROR(NULL, buf); 65 | THROW_IO("Couldn't create named pipe for %s (%s)", 66 | (*env)->GetStringUTFChars(env, lpName, 0), buf); 67 | } 68 | if (pSA) 69 | HeapFree(GetProcessHeap(), 0, pSA); 70 | return handle; 71 | } else { 72 | char buf[512]; 73 | FILL_ERROR("Couldn't create security acl -- %s (error code %ld)", buf); 74 | jclass exClass = (*env)->FindClass(env, "java/io/IOException"); 75 | if (exClass != NULL) { 76 | if (pSA) 77 | HeapFree(GetProcessHeap(), 0, pSA); 78 | return (*env)->ThrowNew(env, exClass, buf); 79 | } 80 | if (pSA) 81 | HeapFree(GetProcessHeap(), 0, pSA); 82 | return -1; 83 | } 84 | } 85 | 86 | jlong JNICALL 87 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_CreateFileNative( 88 | JNIEnv *env, UNUSED jobject object, jstring lpName) { 89 | LPCWSTR name = (LPCWSTR)(*env)->GetStringChars(env, lpName, 0); 90 | 91 | HANDLE handle = 92 | CreateFileW(name, GENERIC_READ | GENERIC_WRITE, 93 | 0, // no sharing 94 | NULL, // default security attributes 95 | OPEN_EXISTING, 96 | FILE_FLAG_OVERLAPPED, // need overlapped for true 97 | // asynchronous read/write access 98 | NULL); // no template file 99 | if (handle == INVALID_HANDLE_VALUE) { 100 | char buf[512]; 101 | FILL_ERROR(NULL, buf); 102 | THROW_IO("Couldn't open file %s (%s)", 103 | (*env)->GetStringUTFChars(env, lpName, 0), buf); 104 | } 105 | return (jlong)handle; 106 | } 107 | jint JNICALL 108 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_ConnectNamedPipeNative( 109 | UNUSED JNIEnv *env, UNUSED jobject object, jlong handlePointer, 110 | jlong overlappedPointer) { 111 | jboolean result = 112 | ConnectNamedPipe((HANDLE)handlePointer, (LPOVERLAPPED)overlappedPointer); 113 | return result ? -1 : (jint)GetLastError(); 114 | } 115 | 116 | jboolean JNICALL 117 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_DisconnectNamedPipe( 118 | UNUSED JNIEnv *env, UNUSED jobject object, jlong handlePointer) { 119 | return DisconnectNamedPipe((HANDLE)handlePointer); 120 | } 121 | 122 | jint JNICALL 123 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_readNative( 124 | JNIEnv *env, UNUSED jobject object, jlong waitable, jlong hFile, 125 | jbyteArray buffer, jint offset, jint length, jboolean strict) { 126 | HANDLE handle = (HANDLE)hFile; 127 | OVERLAPPED olap = {0}; 128 | olap.hEvent = (HANDLE)waitable; 129 | 130 | DWORD bytes_read = 0; 131 | LPVOID read_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, length); 132 | BOOL immediate = ReadFile(handle, read_buffer, length, &bytes_read, &olap); 133 | if (!immediate) { 134 | if (GetLastError() != ERROR_IO_PENDING) { 135 | char buf[256]; 136 | FILL_ERROR("ReadFile() failed: %s (error code %ld)", buf); 137 | THROW_IO(NULL, buf); 138 | } 139 | } 140 | 141 | if (!GetOverlappedResult(handle, &olap, &bytes_read, TRUE)) { 142 | char buf[256]; 143 | FILL_ERROR( 144 | "GetOverlappedResult() failed for read operation: %s (error code %ld)", 145 | buf); 146 | THROW_IO(NULL, buf); 147 | } 148 | if (strict && (bytes_read != (DWORD)length)) { 149 | char buf[256]; 150 | snprintf(buf, 256, 151 | "ReadFile() read less bytes than requested: expected %d bytes, " 152 | "but read %ld bytes", 153 | length, bytes_read); 154 | THROW_IO(NULL, buf); 155 | } 156 | (*env)->SetByteArrayRegion(env, buffer, offset, bytes_read, read_buffer); 157 | HeapFree(GetProcessHeap(), 0, read_buffer); 158 | return bytes_read; 159 | return -1; 160 | } 161 | 162 | /* 163 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 164 | * Method: writeNative 165 | * Signature: (JJ[BII)V 166 | */ 167 | void JNICALL 168 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_writeNative( 169 | JNIEnv *env, UNUSED jobject object, jlong waitable, jlong hHandle, 170 | jbyteArray buffer, jint offset, jint length) { 171 | HANDLE handle = (HANDLE)hHandle; 172 | OVERLAPPED olap; 173 | olap.hEvent = (HANDLE)waitable; 174 | 175 | jbyte *bytes = (*env)->GetByteArrayElements(env, buffer, 0); 176 | BOOL immediate = 177 | WriteFile(handle, bytes + (DWORD)offset, (DWORD)length, NULL, &olap); 178 | if (!immediate) { 179 | if (GetLastError() != ERROR_IO_PENDING) { 180 | char buf[256]; 181 | FILL_ERROR("ReadFile() failed: %s (error code %ld)", buf); 182 | THROW_IO(NULL, buf); 183 | } 184 | } 185 | DWORD bytes_written = 0; 186 | if (!GetOverlappedResult(handle, &olap, &bytes_written, TRUE)) { 187 | char buf[256]; 188 | FILL_ERROR( 189 | "GetOverlappedResult() failed for write operation: %s (error code %ld)", 190 | buf); 191 | THROW_IO(NULL, buf); 192 | } 193 | if (bytes_written != (DWORD)length) { 194 | char buf[256]; 195 | snprintf(buf, 256, 196 | "WriteFile() wrote less bytes than requested: expected %d bytes, " 197 | "but wrote %ld bytes", 198 | length, bytes_written); 199 | THROW_IO(NULL, buf); 200 | } 201 | (*env)->ReleaseByteArrayElements(env, buffer, bytes, JNI_ABORT); 202 | } 203 | 204 | JNIEXPORT jboolean JNICALL 205 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_CloseHandleNative( 206 | UNUSED JNIEnv *env, UNUSED jobject object, jlong handlePointer) { 207 | return CloseHandle((HANDLE)handlePointer); 208 | } 209 | 210 | JNIEXPORT jboolean JNICALL 211 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_GetOverlappedResultNative( 212 | UNUSED JNIEnv *env, UNUSED jobject object, jlong handlePointer, 213 | jlong overlappedPointer) { 214 | DWORD len = 0; 215 | return GetOverlappedResult((HANDLE)handlePointer, 216 | (LPOVERLAPPED)overlappedPointer, &len, TRUE); 217 | } 218 | 219 | JNIEXPORT jboolean JNICALL 220 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_CancelIoEx( 221 | UNUSED JNIEnv *env, UNUSED jobject object, jlong handlePointer) { 222 | return CancelIoEx((HANDLE)handlePointer, NULL); 223 | } 224 | 225 | JNIEXPORT jlong JNICALL 226 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_CreateEventNative( 227 | JNIEnv *env, UNUSED jobject object, jboolean manualReset, 228 | jboolean initialState, jstring lpName) { 229 | LPCWSTR name = 230 | lpName ? (LPCWSTR)(*env)->GetStringChars(env, lpName, 0) : NULL; 231 | HANDLE handle = CreateEventW(NULL, manualReset, initialState, name); 232 | if (handle == INVALID_HANDLE_VALUE) { 233 | char buf[512]; 234 | FILL_ERROR(NULL, buf); 235 | THROW_IO("Couldn't create event %s (%s)", 236 | (*env)->GetStringUTFChars(env, lpName, 0), buf); 237 | } 238 | return (jlong)handle; 239 | } 240 | 241 | jint JNICALL 242 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_GetLastError( 243 | UNUSED JNIEnv *env, UNUSED jobject object) { 244 | return GetLastError(); 245 | } 246 | 247 | jboolean JNICALL 248 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_FlushFileBuffersNative( 249 | UNUSED JNIEnv *env, UNUSED jobject object, jlong handlePointer) { 250 | return FlushFileBuffers((HANDLE)handlePointer); 251 | } 252 | 253 | jlong JNICALL 254 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_NewOverlappedNative( 255 | UNUSED JNIEnv *env, UNUSED jobject object, jlong handlePointer) { 256 | HANDLE handle = (HANDLE)handlePointer; 257 | UNUSED LPOVERLAPPED op = 258 | HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(OVERLAPPED)); 259 | op->hEvent = handle; 260 | return (jlong)op; 261 | } 262 | 263 | void JNICALL 264 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_DeleteOverlappedNative( 265 | UNUSED JNIEnv *env, UNUSED jobject object, jlong overlappedPointer) { 266 | HeapFree(GetProcessHeap(), 0, (void *)overlappedPointer); 267 | } 268 | 269 | // Constants follow: 270 | jint JNICALL 271 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_ERROR_1IO_1PENDING( 272 | UNUSED JNIEnv *env, UNUSED jobject object) { 273 | return ERROR_IO_PENDING; 274 | }; 275 | 276 | jint JNICALL 277 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_ERROR_1NO_1DATA( 278 | UNUSED JNIEnv *env, UNUSED jobject object) { 279 | return ERROR_NO_DATA; 280 | } 281 | 282 | jint JNICALL 283 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_ERROR_1PIPE_1CONNECTED( 284 | UNUSED JNIEnv *env, UNUSED jobject object) { 285 | return ERROR_PIPE_CONNECTED; 286 | } 287 | 288 | jint JNICALL 289 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_FILE_1ALL_1ACCESS( 290 | UNUSED JNIEnv *env, UNUSED jobject object) { 291 | return FILE_ALL_ACCESS; 292 | } 293 | 294 | jint JNICALL 295 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_FILE_1FLAG_1FIRST_1PIPE_1INSTANCE( 296 | UNUSED JNIEnv *env, UNUSED jobject object) { 297 | return FILE_FLAG_FIRST_PIPE_INSTANCE; 298 | } 299 | 300 | jint JNICALL 301 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_FILE_1FLAG_1OVERLAPPED( 302 | UNUSED JNIEnv *env, UNUSED jobject object) { 303 | return FILE_FLAG_OVERLAPPED; 304 | } 305 | 306 | jint JNICALL 307 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_FILE_1GENERIC_1READ( 308 | UNUSED JNIEnv *env, UNUSED jobject object) { 309 | return FILE_GENERIC_READ; 310 | } 311 | 312 | jint JNICALL 313 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_GENERIC_1READ( 314 | UNUSED JNIEnv *env, UNUSED jobject object) { 315 | return GENERIC_READ; 316 | } 317 | 318 | jint JNICALL 319 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_GENERIC_1WRITE( 320 | UNUSED JNIEnv *env, UNUSED jobject object) { 321 | return GENERIC_WRITE; 322 | } 323 | 324 | jint JNICALL 325 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_PIPE_1ACCESS_1DUPLEX( 326 | UNUSED JNIEnv *env, UNUSED jobject object) { 327 | return PIPE_ACCESS_DUPLEX; 328 | } 329 | 330 | jstring JNICALL 331 | Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_getErrorMessage( 332 | JNIEnv *env, UNUSED jobject object, jint errorCode) { 333 | wchar_t buf[256]; 334 | FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 335 | NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 336 | buf, (sizeof(buf) / sizeof(wchar_t)), NULL); 337 | size_t len = wcsnlen(buf, 256); 338 | if (len > 0 && buf[len - 1] == '\n') { 339 | buf[len - 1] = '0'; 340 | len--; 341 | } 342 | return (*env)->NewString(env, buf, len - 1); 343 | } 344 | 345 | static int createSecurityWithDacl(PSECURITY_ATTRIBUTES pSA, DWORD accessMask, 346 | BOOL logon) { 347 | int result = 0; 348 | PSECURITY_DESCRIPTOR pSD = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 349 | SECURITY_DESCRIPTOR_MIN_LENGTH); 350 | if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) { 351 | wchar_t buf[256]; 352 | FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 353 | NULL, GetLastError(), 354 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 355 | (sizeof(buf) / sizeof(wchar_t)), NULL); 356 | if (DEBUG) 357 | wprintf(L"Failed to initialize security descriptor %d %s\n", 358 | GetLastError(), buf); 359 | result = 1; 360 | goto psdfail; 361 | } 362 | GetLastError(); 363 | 364 | PSID psid = NULL; 365 | HANDLE phToken; 366 | OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &phToken); 367 | long unsigned int tokenInformationLength; 368 | PTOKEN_GROUPS groups = NULL; 369 | PTOKEN_OWNER owner = NULL; 370 | if (logon) { 371 | GetTokenInformation(phToken, TokenGroups, NULL, 0, &tokenInformationLength); 372 | groups = 373 | (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 374 | tokenInformationLength * sizeof(TOKEN_GROUPS)); 375 | GetTokenInformation(phToken, TokenOwner, groups, 0, 376 | &tokenInformationLength); 377 | GetLastError(); 378 | for (DWORD i = 0; i < groups->GroupCount; ++i) { 379 | SID_AND_ATTRIBUTES a = groups->Groups[i]; 380 | if ((a.Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID) { 381 | psid = a.Sid; 382 | break; 383 | } 384 | } 385 | } else { 386 | GetTokenInformation(phToken, TokenOwner, NULL, 0, &tokenInformationLength); 387 | owner = 388 | (PTOKEN_OWNER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 389 | tokenInformationLength * sizeof(TOKEN_OWNER)); 390 | GetTokenInformation(phToken, TokenOwner, owner, tokenInformationLength, 391 | &tokenInformationLength); 392 | psid = owner->Owner; 393 | } 394 | if (psid == NULL) 395 | goto psidfail; 396 | 397 | DWORD cbAcl = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psid); 398 | cbAcl = (cbAcl + (sizeof(DWORD) - 1)) & 0xfffffffc; 399 | 400 | PACL pAcl = HeapAlloc(GetProcessHeap(), 0, cbAcl); 401 | if (!InitializeAcl(pAcl, cbAcl, ACL_REVISION)) { 402 | wchar_t buf[256]; 403 | FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 404 | NULL, GetLastError(), 405 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 406 | (sizeof(buf) / sizeof(wchar_t)), NULL); 407 | if (DEBUG) 408 | wprintf(L"Failed to initialize acl %d %s\n", GetLastError(), buf); 409 | result = 1; 410 | goto exit; 411 | } 412 | if (!AddAccessAllowedAce(pAcl, ACL_REVISION, accessMask, psid)) { 413 | wchar_t buf[256]; 414 | FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 415 | NULL, GetLastError(), 416 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 417 | (sizeof(buf) / sizeof(wchar_t)), NULL); 418 | if (DEBUG) 419 | wprintf(L"Failed to add access allowed ace %d %s\n", GetLastError(), buf); 420 | result = 1; 421 | goto exit; 422 | } 423 | if (!SetSecurityDescriptorDacl(pSD, TRUE, pAcl, FALSE)) { 424 | wchar_t buf[256]; 425 | FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 426 | NULL, GetLastError(), 427 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 428 | (sizeof(buf) / sizeof(wchar_t)), NULL); 429 | if (DEBUG) 430 | wprintf(L"Failed to set security despcriptor %d %s\n", GetLastError(), 431 | buf); 432 | result = 1; 433 | pSA->nLength = 0; 434 | pSA->lpSecurityDescriptor = NULL; 435 | pSA->bInheritHandle = FALSE; 436 | } else { 437 | pSA->nLength = SECURITY_DESCRIPTOR_SIZE; 438 | pSA->lpSecurityDescriptor = pSD; 439 | pSA->bInheritHandle = FALSE; 440 | } 441 | 442 | exit: 443 | HeapFree(GetProcessHeap(), 0, pAcl); 444 | psidfail: 445 | if (groups) 446 | HeapFree(GetProcessHeap(), 0, groups); 447 | if (owner) 448 | HeapFree(GetProcessHeap(), 0, owner); 449 | CloseHandle(phToken); 450 | psdfail: 451 | HeapFree(GetProcessHeap(), 0, pSD); 452 | 453 | return result; 454 | } 455 | -------------------------------------------------------------------------------- /jni/org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider */ 4 | 5 | #ifndef _Included_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 6 | #define _Included_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 12 | * Method: CreateNamedPipeNative 13 | * Signature: (Ljava/lang/String;IIIIIIII)J 14 | */ 15 | JNIEXPORT jlong JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_CreateNamedPipeNative 16 | (JNIEnv *, jobject, jstring, jint, jint, jint, jint, jint, jint, jint, jint); 17 | 18 | /* 19 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 20 | * Method: CreateFileNative 21 | * Signature: (Ljava/lang/String;)J 22 | */ 23 | JNIEXPORT jlong JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_CreateFileNative 24 | (JNIEnv *, jobject, jstring); 25 | 26 | /* 27 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 28 | * Method: ConnectNamedPipeNative 29 | * Signature: (JJ)I 30 | */ 31 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_ConnectNamedPipeNative 32 | (JNIEnv *, jobject, jlong, jlong); 33 | 34 | /* 35 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 36 | * Method: DisconnectNamedPipe 37 | * Signature: (J)Z 38 | */ 39 | JNIEXPORT jboolean JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_DisconnectNamedPipe 40 | (JNIEnv *, jobject, jlong); 41 | 42 | /* 43 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 44 | * Method: readNative 45 | * Signature: (JJ[BIIZ)I 46 | */ 47 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_readNative 48 | (JNIEnv *, jobject, jlong, jlong, jbyteArray, jint, jint, jboolean); 49 | 50 | /* 51 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 52 | * Method: writeNative 53 | * Signature: (JJ[BII)V 54 | */ 55 | JNIEXPORT void JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_writeNative 56 | (JNIEnv *, jobject, jlong, jlong, jbyteArray, jint, jint); 57 | 58 | /* 59 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 60 | * Method: CloseHandleNative 61 | * Signature: (J)Z 62 | */ 63 | JNIEXPORT jboolean JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_CloseHandleNative 64 | (JNIEnv *, jobject, jlong); 65 | 66 | /* 67 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 68 | * Method: GetOverlappedResultNative 69 | * Signature: (JJ)Z 70 | */ 71 | JNIEXPORT jboolean JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_GetOverlappedResultNative 72 | (JNIEnv *, jobject, jlong, jlong); 73 | 74 | /* 75 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 76 | * Method: CancelIoEx 77 | * Signature: (J)Z 78 | */ 79 | JNIEXPORT jboolean JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_CancelIoEx 80 | (JNIEnv *, jobject, jlong); 81 | 82 | /* 83 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 84 | * Method: CreateEventNative 85 | * Signature: (ZZLjava/lang/String;)J 86 | */ 87 | JNIEXPORT jlong JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_CreateEventNative 88 | (JNIEnv *, jobject, jboolean, jboolean, jstring); 89 | 90 | /* 91 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 92 | * Method: GetLastError 93 | * Signature: ()I 94 | */ 95 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_GetLastError 96 | (JNIEnv *, jobject); 97 | 98 | /* 99 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 100 | * Method: NewOverlappedNative 101 | * Signature: (J)J 102 | */ 103 | JNIEXPORT jlong JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_NewOverlappedNative 104 | (JNIEnv *, jobject, jlong); 105 | 106 | /* 107 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 108 | * Method: FlushFileBuffersNative 109 | * Signature: (J)Z 110 | */ 111 | JNIEXPORT jboolean JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_FlushFileBuffersNative 112 | (JNIEnv *, jobject, jlong); 113 | 114 | /* 115 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 116 | * Method: DeleteOverlappedNative 117 | * Signature: (J)V 118 | */ 119 | JNIEXPORT void JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_DeleteOverlappedNative 120 | (JNIEnv *, jobject, jlong); 121 | 122 | /* 123 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 124 | * Method: getErrorMessage 125 | * Signature: (I)Ljava/lang/String; 126 | */ 127 | JNIEXPORT jstring JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_getErrorMessage 128 | (JNIEnv *, jobject, jint); 129 | 130 | /* 131 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 132 | * Method: ERROR_IO_PENDING 133 | * Signature: ()I 134 | */ 135 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_ERROR_1IO_1PENDING 136 | (JNIEnv *, jobject); 137 | 138 | /* 139 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 140 | * Method: ERROR_NO_DATA 141 | * Signature: ()I 142 | */ 143 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_ERROR_1NO_1DATA 144 | (JNIEnv *, jobject); 145 | 146 | /* 147 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 148 | * Method: ERROR_PIPE_CONNECTED 149 | * Signature: ()I 150 | */ 151 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_ERROR_1PIPE_1CONNECTED 152 | (JNIEnv *, jobject); 153 | 154 | /* 155 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 156 | * Method: FILE_ALL_ACCESS 157 | * Signature: ()I 158 | */ 159 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_FILE_1ALL_1ACCESS 160 | (JNIEnv *, jobject); 161 | 162 | /* 163 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 164 | * Method: FILE_FLAG_FIRST_PIPE_INSTANCE 165 | * Signature: ()I 166 | */ 167 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_FILE_1FLAG_1FIRST_1PIPE_1INSTANCE 168 | (JNIEnv *, jobject); 169 | 170 | /* 171 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 172 | * Method: FILE_FLAG_OVERLAPPED 173 | * Signature: ()I 174 | */ 175 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_FILE_1FLAG_1OVERLAPPED 176 | (JNIEnv *, jobject); 177 | 178 | /* 179 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 180 | * Method: FILE_GENERIC_READ 181 | * Signature: ()I 182 | */ 183 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_FILE_1GENERIC_1READ 184 | (JNIEnv *, jobject); 185 | 186 | /* 187 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 188 | * Method: GENERIC_READ 189 | * Signature: ()I 190 | */ 191 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_GENERIC_1READ 192 | (JNIEnv *, jobject); 193 | 194 | /* 195 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 196 | * Method: GENERIC_WRITE 197 | * Signature: ()I 198 | */ 199 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_GENERIC_1WRITE 200 | (JNIEnv *, jobject); 201 | 202 | /* 203 | * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider 204 | * Method: PIPE_ACCESS_DUPLEX 205 | * Signature: ()I 206 | */ 207 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_PIPE_1ACCESS_1DUPLEX 208 | (JNIEnv *, jobject); 209 | 210 | #ifdef __cplusplus 211 | } 212 | #endif 213 | #endif 214 | -------------------------------------------------------------------------------- /jni/org_scalasbt_ipcsocket_UnixDomainSocketLibrary.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class org_scalasbt_ipcsocket_UnixDomainSocketLibrary */ 4 | 5 | #ifndef _Included_org_scalasbt_ipcsocket_UnixDomainSocketLibrary 6 | #define _Included_org_scalasbt_ipcsocket_UnixDomainSocketLibrary 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | #undef org_scalasbt_ipcsocket_UnixDomainSocketLibrary_PF_LOCAL 11 | #define org_scalasbt_ipcsocket_UnixDomainSocketLibrary_PF_LOCAL 1L 12 | #undef org_scalasbt_ipcsocket_UnixDomainSocketLibrary_AF_LOCAL 13 | #define org_scalasbt_ipcsocket_UnixDomainSocketLibrary_AF_LOCAL 1L 14 | #undef org_scalasbt_ipcsocket_UnixDomainSocketLibrary_SOCK_STREAM 15 | #define org_scalasbt_ipcsocket_UnixDomainSocketLibrary_SOCK_STREAM 1L 16 | #undef org_scalasbt_ipcsocket_UnixDomainSocketLibrary_SHUT_RD 17 | #define org_scalasbt_ipcsocket_UnixDomainSocketLibrary_SHUT_RD 0L 18 | #undef org_scalasbt_ipcsocket_UnixDomainSocketLibrary_SHUT_WR 19 | #define org_scalasbt_ipcsocket_UnixDomainSocketLibrary_SHUT_WR 1L 20 | /* 21 | * Class: org_scalasbt_ipcsocket_UnixDomainSocketLibrary 22 | * Method: socket 23 | * Signature: (III)I 24 | */ 25 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_UnixDomainSocketLibrary_socket 26 | (JNIEnv *, jclass, jint, jint, jint); 27 | 28 | /* 29 | * Class: org_scalasbt_ipcsocket_UnixDomainSocketLibrary 30 | * Method: bind 31 | * Signature: (ILorg/scalasbt/ipcsocket/UnixDomainSocketLibrary/SockaddrUn;I)I 32 | */ 33 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_UnixDomainSocketLibrary_bind 34 | (JNIEnv *, jclass, jint, jobject, jint); 35 | 36 | /* 37 | * Class: org_scalasbt_ipcsocket_UnixDomainSocketLibrary 38 | * Method: listen 39 | * Signature: (II)I 40 | */ 41 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_UnixDomainSocketLibrary_listen 42 | (JNIEnv *, jclass, jint, jint); 43 | 44 | /* 45 | * Class: org_scalasbt_ipcsocket_UnixDomainSocketLibrary 46 | * Method: accept 47 | * Signature: (ILorg/scalasbt/ipcsocket/UnixDomainSocketLibrary/SockaddrUn;Lcom/sun/jna/ptr/IntByReference;)I 48 | */ 49 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_UnixDomainSocketLibrary_accept 50 | (JNIEnv *, jclass, jint, jobject, jobject); 51 | 52 | /* 53 | * Class: org_scalasbt_ipcsocket_UnixDomainSocketLibrary 54 | * Method: connect 55 | * Signature: (ILorg/scalasbt/ipcsocket/UnixDomainSocketLibrary/SockaddrUn;I)I 56 | */ 57 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_UnixDomainSocketLibrary_connect 58 | (JNIEnv *, jclass, jint, jobject, jint); 59 | 60 | /* 61 | * Class: org_scalasbt_ipcsocket_UnixDomainSocketLibrary 62 | * Method: read 63 | * Signature: (ILjava/nio/ByteBuffer;I)I 64 | */ 65 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_UnixDomainSocketLibrary_read 66 | (JNIEnv *, jclass, jint, jobject, jint); 67 | 68 | /* 69 | * Class: org_scalasbt_ipcsocket_UnixDomainSocketLibrary 70 | * Method: write 71 | * Signature: (ILjava/nio/ByteBuffer;I)I 72 | */ 73 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_UnixDomainSocketLibrary_write 74 | (JNIEnv *, jclass, jint, jobject, jint); 75 | 76 | /* 77 | * Class: org_scalasbt_ipcsocket_UnixDomainSocketLibrary 78 | * Method: close 79 | * Signature: (I)I 80 | */ 81 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_UnixDomainSocketLibrary_close 82 | (JNIEnv *, jclass, jint); 83 | 84 | /* 85 | * Class: org_scalasbt_ipcsocket_UnixDomainSocketLibrary 86 | * Method: shutdown 87 | * Signature: (II)I 88 | */ 89 | JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_UnixDomainSocketLibrary_shutdown 90 | (JNIEnv *, jclass, jint, jint); 91 | 92 | #ifdef __cplusplus 93 | } 94 | #endif 95 | #endif 96 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.11.6 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.github.sbt" % "sbt-dynver" % "5.1.1") 2 | addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.1") 3 | addSbtPlugin("com.swoval" % "sbt-source-format" % "0.3.1") 4 | -------------------------------------------------------------------------------- /src/main/java/org/scalasbt/ipcsocket/JNAWin32NamedPipeLibraryProvider.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket; 2 | 3 | import java.io.IOException; 4 | import java.nio.ByteBuffer; 5 | 6 | import com.sun.jna.*; 7 | import com.sun.jna.platform.win32.WinError; 8 | import com.sun.jna.ptr.IntByReference; 9 | import com.sun.jna.platform.win32.WinNT; 10 | import com.sun.jna.platform.win32.WinNT.HANDLE; 11 | import com.sun.jna.platform.win32.WinBase.SECURITY_ATTRIBUTES; 12 | import com.sun.jna.platform.win32.WinBase.OVERLAPPED; 13 | 14 | class JNAWin32NamedPipeLibraryProvider implements Win32NamedPipeLibraryProvider { 15 | static final Win32NamedPipeLibrary delegate = Win32NamedPipeLibrary.INSTANCE; 16 | static final Win32NamedPipeLibraryProvider instance = new JNAWin32NamedPipeLibraryProvider(); 17 | 18 | static Win32NamedPipeLibraryProvider instance() { 19 | return instance; 20 | } 21 | 22 | private JNAWin32NamedPipeLibraryProvider() {}; 23 | 24 | private static class JNAHandle implements Handle { 25 | final HANDLE handle; 26 | 27 | private JNAHandle(final HANDLE handle) { 28 | this.handle = handle; 29 | } 30 | 31 | static Handle make(final HANDLE handle) throws IOException { 32 | if (handle == Win32NamedPipeLibrary.INVALID_HANDLE_VALUE) { 33 | throw new IOException("Invalid handle"); 34 | } 35 | return new JNAHandle(handle); 36 | } 37 | } 38 | 39 | private static class JNAOverlapped implements Overlapped { 40 | final OVERLAPPED overlapped; 41 | 42 | JNAOverlapped(final OVERLAPPED overlapped) { 43 | this.overlapped = overlapped; 44 | } 45 | } 46 | 47 | @Override 48 | public Handle CreateNamedPipe( 49 | String lpName, 50 | int dwOpenMode, 51 | int dwPipeMode, 52 | int nMaxInstances, 53 | int nOutBufferSize, 54 | int nInBufferSize, 55 | int nDefaultTimeOut, 56 | int lpSecurityAttributes, 57 | int securityLevel) 58 | throws IOException { 59 | SECURITY_ATTRIBUTES sa = 60 | Win32SecurityLibrary.createSecurityWithDacl(lpSecurityAttributes, securityLevel); 61 | return JNAHandle.make( 62 | delegate.CreateNamedPipe( 63 | lpName, 64 | dwOpenMode, 65 | dwPipeMode, 66 | nMaxInstances, 67 | nOutBufferSize, 68 | nInBufferSize, 69 | nDefaultTimeOut, 70 | sa)); 71 | } 72 | 73 | @Override 74 | public Handle CreateFile(String pipeName) throws IOException { 75 | HANDLE handle = 76 | delegate.CreateFile( 77 | pipeName, 78 | WinNT.GENERIC_READ | WinNT.GENERIC_WRITE, 79 | 0, // no sharing 80 | null, // default security attributes 81 | WinNT.OPEN_EXISTING, 82 | WinNT.FILE_FLAG_OVERLAPPED, // need overlapped for true asynchronous read/write access 83 | null); // no template file 84 | if (handle == Win32NamedPipeLibrary.INVALID_HANDLE_VALUE) 85 | throw new IOException("Couldn't open pipe for " + pipeName + " (" + GetLastError() + ")"); 86 | return JNAHandle.make(handle); 87 | } 88 | 89 | @Override 90 | public int ConnectNamedPipe(Handle hNamedPipe, Overlapped lpOverlapped) { 91 | HANDLE pipe = getHandle(hNamedPipe); 92 | OVERLAPPED op = getOverlap(lpOverlapped); 93 | if (delegate.ConnectNamedPipe(pipe, op.getPointer())) { 94 | return -1; 95 | } else { 96 | return GetLastError(); 97 | } 98 | } 99 | 100 | @Override 101 | public boolean DisconnectNamedPipe(Handle handle) { 102 | HANDLE pipe = getHandle(handle); 103 | return delegate.DisconnectNamedPipe(pipe); 104 | } 105 | 106 | @Override 107 | public int read( 108 | Handle waitable, 109 | Handle hFile, 110 | byte[] buffer, 111 | int offset, 112 | int len, 113 | boolean requireStrictLength) 114 | throws IOException { 115 | HANDLE readerWaitable = getHandle(waitable); 116 | HANDLE handle = getHandle(hFile); 117 | Memory readBuffer = new Memory(len); 118 | 119 | OVERLAPPED olap = new OVERLAPPED(); 120 | olap.hEvent = readerWaitable; 121 | olap.write(); 122 | 123 | boolean immediate = delegate.ReadFile(handle, readBuffer, len, null, olap.getPointer()); 124 | if (!immediate) { 125 | int lastError = delegate.GetLastError(); 126 | if (lastError != WinError.ERROR_IO_PENDING) { 127 | throw new IOException("ReadFile() failed: " + lastError); 128 | } 129 | } 130 | 131 | IntByReference r = new IntByReference(); 132 | if (!delegate.GetOverlappedResult(handle, olap.getPointer(), r, true)) { 133 | int lastError = delegate.GetLastError(); 134 | throw new IOException("GetOverlappedResult() failed for read operation: " + lastError); 135 | } 136 | int actualLen = r.getValue(); 137 | if (requireStrictLength && (actualLen != len)) { 138 | throw new IOException( 139 | "ReadFile() read less bytes than requested: expected " 140 | + len 141 | + " bytes, but read " 142 | + actualLen 143 | + " bytes"); 144 | } 145 | byte[] byteArray = readBuffer.getByteArray(0, actualLen); 146 | System.arraycopy(byteArray, 0, buffer, offset, actualLen); 147 | return actualLen; 148 | } 149 | 150 | @Override 151 | public void write(Handle waitable, Handle hHandle, byte[] b, int off, int len) 152 | throws IOException { 153 | HANDLE writerWaitable = getHandle(waitable); 154 | HANDLE handle = getHandle(hHandle); 155 | ByteBuffer data = ByteBuffer.wrap(b, off, len); 156 | 157 | OVERLAPPED olap = new OVERLAPPED(); 158 | olap.hEvent = writerWaitable; 159 | olap.write(); 160 | 161 | boolean immediate = delegate.WriteFile(handle, data, len, null, olap.getPointer()); 162 | if (!immediate) { 163 | int lastError = delegate.GetLastError(); 164 | if (lastError != WinError.ERROR_IO_PENDING) { 165 | throw new IOException("WriteFile() failed: " + lastError); 166 | } 167 | } 168 | IntByReference written = new IntByReference(); 169 | if (!delegate.GetOverlappedResult(handle, olap.getPointer(), written, true)) { 170 | int lastError = delegate.GetLastError(); 171 | throw new IOException("GetOverlappedResult() failed for write operation: " + lastError); 172 | } 173 | if (written.getValue() != len) { 174 | throw new IOException("WriteFile() wrote less bytes than requested"); 175 | } 176 | } 177 | 178 | @Override 179 | public boolean CloseHandle(Handle handle) { 180 | return delegate.CloseHandle(getHandle(handle)); 181 | } 182 | 183 | @Override 184 | public boolean GetOverlappedResult(Handle hFile, Overlapped lpOverlapped) { 185 | HANDLE handle = getHandle(hFile); 186 | OVERLAPPED op = getOverlap(lpOverlapped); 187 | return delegate.GetOverlappedResult(handle, op.getPointer(), new IntByReference(), true); 188 | } 189 | 190 | @Override 191 | public boolean CancelIoEx(Handle hHandle) { 192 | return delegate.CancelIoEx(getHandle(hHandle), null); 193 | } 194 | 195 | @Override 196 | public Handle CreateEvent(boolean bManualReset, boolean bInitialState, String lpName) 197 | throws IOException { 198 | return JNAHandle.make(delegate.CreateEvent(null, bManualReset, bInitialState, lpName)); 199 | } 200 | 201 | @Override 202 | public int GetLastError() { 203 | return delegate.GetLastError(); 204 | } 205 | 206 | @Override 207 | public Overlapped NewOverlapped(Handle hEvent) { 208 | HANDLE handle = getHandle(hEvent); 209 | OVERLAPPED op = new OVERLAPPED(); 210 | op.hEvent = handle; 211 | op.write(); 212 | return new JNAOverlapped(op); 213 | } 214 | 215 | @Override 216 | public void DeleteOverlapped(Overlapped op) {} 217 | 218 | public boolean FlushFileBuffers(Handle handle) { 219 | return delegate.FlushFileBuffers(getHandle(handle)); 220 | } 221 | 222 | @Override 223 | public int ERROR_IO_PENDING() { 224 | return WinError.ERROR_IO_PENDING; 225 | } 226 | 227 | @Override 228 | public int ERROR_NO_DATA() { 229 | return WinError.ERROR_NO_DATA; 230 | } 231 | 232 | @Override 233 | public int ERROR_PIPE_CONNECTED() { 234 | return WinError.ERROR_PIPE_CONNECTED; 235 | } 236 | 237 | @Override 238 | public int FILE_ALL_ACCESS() { 239 | return WinNT.FILE_ALL_ACCESS; 240 | } 241 | 242 | @Override 243 | public int FILE_FLAG_FIRST_PIPE_INSTANCE() { 244 | return delegate.FILE_FLAG_FIRST_PIPE_INSTANCE; 245 | } 246 | 247 | @Override 248 | public int FILE_FLAG_OVERLAPPED() { 249 | return WinNT.FILE_FLAG_OVERLAPPED; 250 | } 251 | 252 | @Override 253 | public int FILE_GENERIC_READ() { 254 | return WinNT.FILE_GENERIC_READ; 255 | } 256 | 257 | @Override 258 | public int GENERIC_READ() { 259 | return WinNT.GENERIC_READ; 260 | } 261 | 262 | @Override 263 | public int GENERIC_WRITE() { 264 | return WinNT.GENERIC_WRITE; 265 | } 266 | 267 | @Override 268 | public int PIPE_ACCESS_DUPLEX() { 269 | return delegate.PIPE_ACCESS_DUPLEX; 270 | } 271 | 272 | private HANDLE getHandle(Handle handle) { 273 | if (handle instanceof JNAHandle) return ((JNAHandle) handle).handle; 274 | else 275 | throw new IllegalStateException( 276 | "Incompatible handle " + handle + " of type: " + handle.getClass()); 277 | } 278 | 279 | private OVERLAPPED getOverlap(Overlapped overlapped) { 280 | if (overlapped instanceof JNAOverlapped) return ((JNAOverlapped) overlapped).overlapped; 281 | else 282 | throw new IllegalStateException("Incompatible overlapped of type: " + overlapped.getClass()); 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /src/main/java/org/scalasbt/ipcsocket/JNIUnixDomainSocketLibraryProvider.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket; 2 | 3 | class JNIUnixDomainSocketLibraryProvider implements UnixDomainSocketLibraryProvider { 4 | private static final JNIUnixDomainSocketLibraryProvider instance = 5 | new JNIUnixDomainSocketLibraryProvider(); 6 | 7 | static final JNIUnixDomainSocketLibraryProvider instance() { 8 | return instance; 9 | } 10 | 11 | public int socket(int domain, int type, int protocol) throws NativeErrorException { 12 | return returnOrThrow(socketNative(domain, type, protocol), 0); 13 | } 14 | 15 | public int bind(int fd, byte[] address, int addressLen) throws NativeErrorException { 16 | return returnOrThrow(bindNative(fd, address, addressLen), 0); 17 | } 18 | 19 | public int listen(int fd, int backlog) throws NativeErrorException { 20 | return returnOrThrow(listenNative(fd, backlog), 0); 21 | } 22 | 23 | public int accept(int fd) throws NativeErrorException { 24 | return returnOrThrow(acceptNative(fd), 0); 25 | } 26 | 27 | public int connect(int fd, byte[] address, int len) throws NativeErrorException { 28 | return returnOrThrow(connectNative(fd, address, len), 0); 29 | } 30 | 31 | public int read(int fd, byte[] buffer, int offset, int len) throws NativeErrorException { 32 | return returnOrThrow(readNative(fd, buffer, offset, len), -1); 33 | } 34 | 35 | public int write(int fd, byte[] buffer, int offset, int len) throws NativeErrorException { 36 | return returnOrThrow(writeNative(fd, buffer, offset, len), 0); 37 | } 38 | 39 | public int close(int fd) throws NativeErrorException { 40 | return returnOrThrow(closeNative(fd), 0); 41 | } 42 | 43 | public int shutdown(int fd, int how) throws NativeErrorException { 44 | return returnOrThrow(shutdownNative(fd, how), 0); 45 | } 46 | 47 | private int returnOrThrow(int result, int threshold) throws NativeErrorException { 48 | if (result < threshold) { 49 | final String message = "Error " + (-result) + ": " + errString(-result); 50 | throw new NativeErrorException(-result, message); 51 | } 52 | return result; 53 | } 54 | 55 | native int socketNative(int domain, int type, int protocol); 56 | 57 | native int bindNative(int fd, byte[] address, int addressLen); 58 | 59 | native int listenNative(int fd, int backlog); 60 | 61 | native int acceptNative(int fd); 62 | 63 | native int connectNative(int fd, byte[] address, int len); 64 | 65 | native int readNative(int fd, byte[] buffer, int offset, int len); 66 | 67 | native int writeNative(int fd, byte[] buffer, int offset, int len); 68 | 69 | native int closeNative(int fd); 70 | 71 | native int shutdownNative(int fd, int how); 72 | 73 | public native int maxSocketLength(); 74 | 75 | native String errString(int error); 76 | 77 | static { 78 | NativeLoader.load(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/org/scalasbt/ipcsocket/JNIWin32NamedPipeLibraryProvider.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket; 2 | 3 | import java.io.IOException; 4 | 5 | class JNIWin32NamedPipeLibraryProvider implements Win32NamedPipeLibraryProvider { 6 | static { 7 | NativeLoader.load(); 8 | } 9 | 10 | private static final JNIWin32NamedPipeLibraryProvider instance = 11 | new JNIWin32NamedPipeLibraryProvider(); 12 | 13 | static JNIWin32NamedPipeLibraryProvider instance() { 14 | return instance; 15 | } 16 | 17 | static class JNIHandle implements Handle { 18 | final long pointer; 19 | 20 | JNIHandle(long pointer) throws IOException { 21 | if (pointer < 0) throw new IOException("Bad pointer: " + pointer); 22 | this.pointer = pointer; 23 | } 24 | } 25 | 26 | static class JNIOverlapped implements Overlapped { 27 | final long pointer; 28 | 29 | JNIOverlapped(long pointer) { 30 | if (pointer < 0) throw new IllegalStateException("Bad pointer"); 31 | this.pointer = pointer; 32 | } 33 | } 34 | 35 | @Override 36 | public Handle CreateNamedPipe( 37 | String lpName, 38 | int dwOpenMode, 39 | int dwPipeMode, 40 | int nMaxInstances, 41 | int nOutBufferSize, 42 | int nInBufferSize, 43 | int nDefaultTimeOut, 44 | int lpSecurityAttributes, 45 | int securityLevel) 46 | throws IOException { 47 | return new JNIHandle( 48 | CreateNamedPipeNative( 49 | lpName, 50 | dwOpenMode, 51 | dwPipeMode, 52 | nMaxInstances, 53 | nOutBufferSize, 54 | nInBufferSize, 55 | nDefaultTimeOut, 56 | lpSecurityAttributes, 57 | securityLevel)); 58 | } 59 | 60 | native long CreateNamedPipeNative( 61 | String lpName, 62 | int dwOpenMode, 63 | int dwPipeMode, 64 | int nMaxInstances, 65 | int nOutBufferSize, 66 | int nInBufferSize, 67 | int nDefaultTimeOut, 68 | int lpSecurityAttributes, 69 | int securityLevel) 70 | throws IOException; 71 | 72 | @Override 73 | public Handle CreateFile(String pipeName) throws IOException { 74 | return new JNIHandle(CreateFileNative(pipeName)); 75 | } 76 | 77 | native long CreateFileNative(String pipeName) throws IOException; 78 | 79 | @Override 80 | public int ConnectNamedPipe(Handle hNamedPipe, Overlapped lpOverlapped) { 81 | return ConnectNamedPipeNative(getHandlePointer(hNamedPipe), getOverlappedPointer(lpOverlapped)); 82 | } 83 | 84 | native int ConnectNamedPipeNative(long handlePointer, long overlappedPointer); 85 | 86 | @Override 87 | public boolean DisconnectNamedPipe(Handle handle) { 88 | return DisconnectNamedPipe(getHandlePointer(handle)); 89 | } 90 | 91 | native boolean DisconnectNamedPipe(long handlePointer); 92 | 93 | @Override 94 | public int read( 95 | Handle waitable, 96 | Handle hFile, 97 | byte[] buffer, 98 | int offset, 99 | int len, 100 | boolean requireStrictLength) 101 | throws IOException { 102 | return readNative( 103 | getHandlePointer(waitable), 104 | getHandlePointer(hFile), 105 | buffer, 106 | offset, 107 | len, 108 | requireStrictLength); 109 | } 110 | 111 | native int readNative( 112 | long waitable, long hFile, byte[] buffer, int offset, int len, boolean requireStrictLength) 113 | throws IOException; 114 | 115 | @Override 116 | public void write(Handle waitable, Handle hFile, byte[] lpBuffer, int offset, int len) 117 | throws IOException { 118 | writeNative(getHandlePointer(waitable), getHandlePointer(hFile), lpBuffer, offset, len); 119 | } 120 | 121 | native void writeNative( 122 | long waitablePointer, long hFilePointer, byte[] lpBuffer, int offset, int len) 123 | throws IOException; 124 | 125 | @Override 126 | public boolean CloseHandle(Handle handle) { 127 | return CloseHandleNative(getHandlePointer(handle)); 128 | } 129 | 130 | native boolean CloseHandleNative(long pointer); 131 | 132 | @Override 133 | public boolean GetOverlappedResult(Handle hFile, Overlapped lpOverlapped) { 134 | return GetOverlappedResultNative(getHandlePointer(hFile), getOverlappedPointer(lpOverlapped)); 135 | } 136 | 137 | native boolean GetOverlappedResultNative(long handlePointer, long overlappedPointer); 138 | 139 | @Override 140 | public boolean CancelIoEx(Handle handle) { 141 | return CancelIoEx(getHandlePointer(handle)); 142 | } 143 | 144 | native boolean CancelIoEx(long pointer); 145 | 146 | @Override 147 | public Handle CreateEvent(boolean bManualReset, boolean bInitialState, String lpName) 148 | throws IOException { 149 | return new JNIHandle(CreateEventNative(bManualReset, bInitialState, lpName)); 150 | } 151 | 152 | native long CreateEventNative(boolean bManualReset, boolean bInitialState, String lpName); 153 | 154 | @Override 155 | public native int GetLastError(); 156 | 157 | @Override 158 | public Overlapped NewOverlapped(Handle hEvent) { 159 | return new JNIOverlapped(NewOverlappedNative(getHandlePointer(hEvent))); 160 | } 161 | 162 | native long NewOverlappedNative(long handle); 163 | 164 | @Override 165 | public void DeleteOverlapped(Overlapped overlapped) { 166 | DeleteOverlappedNative(getOverlappedPointer(overlapped)); 167 | } 168 | 169 | native boolean FlushFileBuffersNative(long handle); 170 | 171 | @Override 172 | public boolean FlushFileBuffers(Handle handle) { 173 | return FlushFileBuffersNative(getHandlePointer(handle)); 174 | } 175 | 176 | native void DeleteOverlappedNative(long overlapped); 177 | 178 | native String getErrorMessage(int errorCode); 179 | 180 | // Constants: 181 | @Override 182 | public native int ERROR_IO_PENDING(); 183 | 184 | @Override 185 | public native int ERROR_NO_DATA(); 186 | 187 | @Override 188 | public native int ERROR_PIPE_CONNECTED(); 189 | 190 | @Override 191 | public native int FILE_ALL_ACCESS(); 192 | 193 | @Override 194 | public native int FILE_FLAG_FIRST_PIPE_INSTANCE(); 195 | 196 | @Override 197 | public native int FILE_FLAG_OVERLAPPED(); 198 | 199 | @Override 200 | public native int FILE_GENERIC_READ(); 201 | 202 | @Override 203 | public native int GENERIC_READ(); 204 | 205 | @Override 206 | public native int GENERIC_WRITE(); 207 | 208 | @Override 209 | public native int PIPE_ACCESS_DUPLEX(); 210 | 211 | private long getHandlePointer(Handle handle) { 212 | if (handle instanceof JNIHandle) { 213 | return ((JNIHandle) handle).pointer; 214 | } else { 215 | throw new IllegalStateException("Invalid handle " + handle + " of type " + handle.getClass()); 216 | } 217 | } 218 | 219 | private long getOverlappedPointer(Overlapped overlapped) { 220 | if (overlapped instanceof JNIOverlapped) return ((JNIOverlapped) overlapped).pointer; 221 | else 222 | throw new IllegalStateException( 223 | "Invalid overlapped " + overlapped + " of type " + overlapped.getClass()); 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/main/java/org/scalasbt/ipcsocket/NativeErrorException.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket; 2 | 3 | public class NativeErrorException extends Exception { 4 | private final int code; 5 | private final String message; 6 | 7 | public NativeErrorException(final int code) { 8 | this(code, "Native code returned error " + code); 9 | } 10 | 11 | public NativeErrorException(final int code, final String message) { 12 | this.code = code; 13 | this.message = message; 14 | } 15 | 16 | public int returnCode() { 17 | return code; 18 | } 19 | 20 | @Override 21 | public String getMessage() { 22 | return message; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/scalasbt/ipcsocket/NativeLoader.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket; 2 | 3 | import java.util.concurrent.atomic.AtomicBoolean; 4 | import java.io.BufferedReader; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.io.IOException; 8 | import java.lang.management.ManagementFactory; 9 | import java.net.URL; 10 | import java.nio.ByteBuffer; 11 | import java.nio.channels.FileChannel; 12 | import java.nio.file.DirectoryNotEmptyException; 13 | import java.nio.file.Files; 14 | import java.nio.file.FileVisitor; 15 | import java.nio.file.FileVisitResult; 16 | import java.nio.file.Path; 17 | import java.nio.file.Paths; 18 | import java.nio.file.StandardOpenOption; 19 | import java.nio.file.attribute.BasicFileAttributes; 20 | 21 | class NativeLoader { 22 | private static final AtomicBoolean loaded = new AtomicBoolean(false); 23 | private static final boolean isMac; 24 | private static final boolean isLinux; 25 | private static final boolean isWindows; 26 | 27 | static { 28 | final String os = System.getProperty("os.name", "").toLowerCase(); 29 | isMac = os.startsWith("mac"); 30 | isLinux = os.startsWith("linux"); 31 | isWindows = os.startsWith("windows"); 32 | } 33 | 34 | private static final String pid = 35 | isWindows ? "" : ManagementFactory.getRuntimeMXBean().getName().replaceAll("@.*", ""); 36 | 37 | private static Path runtimeDir() { 38 | String prop = System.getProperty("sbt.ipcsocket.tmpdir"); 39 | if (prop != null) { 40 | return Paths.get(prop); 41 | } else { 42 | String runtimeDir = System.getenv("XDG_RUNTIME_DIR"); 43 | if (runtimeDir == null) { 44 | runtimeDir = System.getProperty("java.io.tmpdir"); 45 | } 46 | String home = System.getProperty("user.home"); 47 | if (home == null) { 48 | home = "unknown_home"; 49 | } 50 | return Paths.get(runtimeDir) 51 | .resolve(".sbt" + Integer.toString(home.hashCode())) 52 | .resolve("ipcsocket"); 53 | } 54 | } 55 | 56 | private static final String tempFilePrefix = "libsbtipcsocket"; 57 | 58 | static void load() throws UnsatisfiedLinkError { 59 | if (!loaded.get()) { 60 | final String os = System.getProperty("os.name", "").toLowerCase(); 61 | final boolean isMac = os.startsWith("mac"); 62 | final boolean isLinux = os.startsWith("linux"); 63 | final boolean isWindows = os.startsWith("windows"); 64 | final boolean is64bit = System.getProperty("sun.arch.data.model", "64").equals("64"); 65 | String arch = System.getProperty("os.arch", "").toLowerCase(); 66 | if (arch.equals("amd64")) { 67 | arch = "x86_64"; 68 | } 69 | // https://github.com/sbt/sbt/issues/7117 70 | // Currently only Linux has ARM-specific binary. The macOS binary is a universal binary, 71 | // and Windows can potentially emulate x86. 72 | if (isMac || isWindows) { 73 | arch = "x86_64"; 74 | } 75 | if (is64bit && (isMac || isLinux || isWindows)) { 76 | final String extension = "." + (isMac ? "dylib" : isWindows ? "dll" : "so"); 77 | final String libName = (isWindows ? "" : "lib") + "sbtipcsocket" + extension; 78 | final String prefix = isMac ? "darwin" : isLinux ? "linux" : "win32"; 79 | 80 | final String resource = prefix + "/" + arch + "/" + libName; 81 | final URL url = NativeLoader.class.getClassLoader().getResource(resource); 82 | if (url == null) throw new UnsatisfiedLinkError(resource + " not found on classpath"); 83 | try { 84 | final Path base = Files.createDirectories(runtimeDir()); 85 | final Path output = Files.createTempFile(base, tempFilePrefix, extension); 86 | try (final InputStream in = url.openStream(); 87 | final FileChannel channel = FileChannel.open(output, StandardOpenOption.WRITE)) { 88 | int total = 0; 89 | int read = 0; 90 | byte[] buffer = new byte[1024]; 91 | do { 92 | read = in.read(buffer); 93 | if (read > 0) channel.write(ByteBuffer.wrap(buffer, 0, read)); 94 | } while (read > 0); 95 | channel.close(); 96 | } catch (final IOException ex) { 97 | throw new UnsatisfiedLinkError(); 98 | } 99 | output.toFile().deleteOnExit(); 100 | if (!pid.isEmpty()) { 101 | final Path pidFile = Paths.get(output.toString() + ".pid"); 102 | Files.write(pidFile, pid.getBytes()); 103 | pidFile.toFile().deleteOnExit(); 104 | } 105 | try { 106 | System.load(output.toString()); 107 | } catch (final UnsatisfiedLinkError e) { 108 | Files.deleteIfExists(output); 109 | throw e; 110 | } 111 | loaded.set(true); 112 | final Thread thread = new Thread(new CleanupRunnable(), "ipcsocket-jni-cleanup"); 113 | thread.setDaemon(true); 114 | thread.start(); 115 | return; 116 | } catch (final IOException e) { 117 | throw new UnsatisfiedLinkError(e.getMessage()); 118 | } 119 | } 120 | throw new UnsatisfiedLinkError(); 121 | } 122 | } 123 | 124 | /** 125 | * This cleans up the temporary shared libraries that are created by NativeLoader. The 126 | * deleteOnExit calls don't work on windows because the classloader has open handles to the shared 127 | * libraries. If the process abruptly exits on posix systems, the deleteOnExit calls also aren't 128 | * run so it is necessary to manually clean up these files to avoid leaking disk space. This is 129 | * done on a background thread to avoid blocking the application. On windows, we can just try and 130 | * delete the file and if there is an open handle to the file, the delete will fail, which is what 131 | * we want. On posix systems, we add a pid file and check if there is a process with that pid 132 | * running so that we don't accidentally delete an active library from a running process. In some 133 | * cases, there might be a pid collision that prevents a deletion that could actually be safely 134 | * performed but this is fairly unlikely and, even if it does happen, is unlikely to lead to an 135 | * accumulation of temp files because there is unlikely to be another pid collision the next time 136 | * the collector runs (except in pathological cases). 137 | */ 138 | private static class CleanupRunnable implements Runnable { 139 | @Override 140 | public void run() { 141 | try { 142 | Files.walkFileTree( 143 | runtimeDir(), 144 | new FileVisitor() { 145 | @Override 146 | public FileVisitResult preVisitDirectory( 147 | final Path dir, final BasicFileAttributes attrs) { 148 | return FileVisitResult.CONTINUE; 149 | } 150 | 151 | @Override 152 | public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) 153 | throws IOException { 154 | if (!file.getFileName().toString().startsWith(tempFilePrefix)) { 155 | // do nothing 156 | return FileVisitResult.CONTINUE; 157 | } else if (isWindows) { 158 | try { 159 | Files.deleteIfExists(file); 160 | } catch (final IOException e) { 161 | } 162 | } else if (file.toString().endsWith(".pid")) { 163 | final String pid = new String(Files.readAllBytes(file)); 164 | boolean pidExists = true; 165 | final Process process = new ProcessBuilder("ps", "-p", pid).start(); 166 | try { 167 | process.waitFor(); 168 | final InputStreamReader is = new InputStreamReader(process.getInputStream()); 169 | final BufferedReader reader = new BufferedReader(is); 170 | String line = reader.readLine(); 171 | while (line != null) { 172 | pidExists = line.contains(pid + " "); 173 | line = reader.readLine(); 174 | } 175 | } catch (final InterruptedException e) { 176 | } 177 | if (!pidExists) { 178 | Files.deleteIfExists(Paths.get(file.toString().replaceAll(".pid", ""))); 179 | Files.deleteIfExists(file); 180 | } 181 | } 182 | return FileVisitResult.CONTINUE; 183 | } 184 | 185 | @Override 186 | public FileVisitResult visitFileFailed(Path file, IOException exc) { 187 | return FileVisitResult.CONTINUE; 188 | } 189 | 190 | @Override 191 | public FileVisitResult postVisitDirectory(Path dir, IOException exc) 192 | throws IOException { 193 | try { 194 | Files.deleteIfExists(dir); 195 | } catch (final DirectoryNotEmptyException e) { 196 | } catch (final IOException e) { 197 | throw e; 198 | } 199 | return FileVisitResult.CONTINUE; 200 | } 201 | }); 202 | } catch (final IOException e) { 203 | } 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/main/java/org/scalasbt/ipcsocket/ReferenceCountedFileDescriptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2004-2015, Martian Software, Inc. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | */ 18 | package org.scalasbt.ipcsocket; 19 | 20 | import java.io.IOException; 21 | 22 | /** 23 | * Encapsulates a file descriptor plus a reference count to ensure close requests only close the 24 | * file descriptor once the last reference to the file descriptor is released. 25 | * 26 | *

If not explicitly closed, the file descriptor will be closed when this object is finalized. 27 | */ 28 | public class ReferenceCountedFileDescriptor { 29 | private int fd; 30 | private int fdRefCount; 31 | private boolean closePending; 32 | private final UnixDomainSocketLibraryProvider provider; 33 | 34 | public ReferenceCountedFileDescriptor(int fd, UnixDomainSocketLibraryProvider provider) { 35 | this.provider = provider; 36 | this.fd = fd; 37 | this.fdRefCount = 0; 38 | this.closePending = false; 39 | } 40 | 41 | protected void finalize() throws IOException { 42 | close(); 43 | } 44 | 45 | public synchronized int acquire() { 46 | fdRefCount++; 47 | return fd; 48 | } 49 | 50 | public synchronized void release() throws IOException { 51 | fdRefCount--; 52 | if (fdRefCount == 0 && closePending && fd != -1) { 53 | doClose(); 54 | } 55 | } 56 | 57 | public synchronized void close() throws IOException { 58 | if (fd == -1 || closePending) { 59 | return; 60 | } 61 | 62 | if (fdRefCount == 0) { 63 | doClose(); 64 | } else { 65 | // Another thread has the FD. We'll close it when they release the reference. 66 | closePending = true; 67 | } 68 | } 69 | 70 | private void doClose() throws IOException { 71 | try { 72 | provider.close(fd); 73 | fd = -1; 74 | } catch (NativeErrorException e) { 75 | throw new IOException(e); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/org/scalasbt/ipcsocket/ServerSocketWrapper.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket; 2 | 3 | import java.io.IOException; 4 | import java.lang.reflect.Method; 5 | import java.net.ProtocolFamily; 6 | import java.net.ServerSocket; 7 | import java.net.SocketAddress; 8 | import java.net.SocketException; 9 | import java.net.StandardProtocolFamily; 10 | import java.nio.channels.ServerSocketChannel; 11 | import java.util.Arrays; 12 | 13 | public abstract class ServerSocketWrapper { 14 | private ServerSocketWrapper() {} 15 | 16 | private static SocketAddress unixDomainSocketAddress(final String pathName) 17 | throws ReflectiveOperationException { 18 | final Class clazz = Class.forName("java.net.UnixDomainSocketAddress"); 19 | final Method method = clazz.getMethod("of", String.class); 20 | return (SocketAddress) method.invoke(null, pathName); 21 | } 22 | 23 | private static ProtocolFamily unixProtocolFamily() { 24 | return Arrays.stream(StandardProtocolFamily.class.getEnumConstants()) 25 | .filter(a -> "UNIX".equals(a.name())) 26 | .findFirst() 27 | .orElseThrow(() -> new RuntimeException("not found UNIX value in StandardProtocolFamily")); 28 | } 29 | 30 | static ServerSocketWrapper newJdkUnixDomainSocket(final String pathName) 31 | throws ReflectiveOperationException, IOException { 32 | final SocketAddress address = unixDomainSocketAddress(pathName); 33 | final ProtocolFamily protocolFamily = unixProtocolFamily(); 34 | final Method openMethod = 35 | ServerSocketChannel.class.getMethod("open", java.net.ProtocolFamily.class); 36 | final ServerSocketChannel serverSocketChannel = 37 | (ServerSocketChannel) openMethod.invoke(null, protocolFamily); 38 | serverSocketChannel.bind(address); 39 | return ServerSocketWrapper.fromServerSocketChannel(serverSocketChannel); 40 | } 41 | 42 | public static ServerSocketWrapper newUnixDomainSocket( 43 | final String pathName, final boolean jni, final boolean jdk) throws IOException { 44 | ServerSocketWrapper result; 45 | if (jdk) { 46 | try { 47 | result = newJdkUnixDomainSocket(pathName); 48 | } catch (ReflectiveOperationException e) { 49 | result = ServerSocketWrapper.fromServerSocket(new UnixDomainServerSocket(pathName, jni)); 50 | } 51 | } else { 52 | result = ServerSocketWrapper.fromServerSocket(new UnixDomainServerSocket(pathName, jni)); 53 | } 54 | return result; 55 | } 56 | 57 | public abstract void setSoTimeout(int timeout) throws SocketException; 58 | 59 | public abstract SocketWrapper accept() throws IOException; 60 | 61 | public abstract void close() throws IOException; 62 | 63 | public static ServerSocketWrapper fromServerSocket(final ServerSocket socket) { 64 | return new ServerSocketImpl(socket); 65 | } 66 | 67 | public static ServerSocketWrapper fromServerSocketChannel(final ServerSocketChannel channel) { 68 | return new ServerSocketChannelImpl(channel); 69 | } 70 | 71 | private static final class ServerSocketImpl extends ServerSocketWrapper { 72 | private final ServerSocket socket; 73 | 74 | ServerSocketImpl(ServerSocket socket) { 75 | this.socket = socket; 76 | } 77 | 78 | @Override 79 | public void setSoTimeout(int timeout) throws SocketException { 80 | socket.setSoTimeout(timeout); 81 | } 82 | 83 | @Override 84 | public SocketWrapper accept() throws IOException { 85 | return SocketWrapper.fromSocket(socket.accept()); 86 | } 87 | 88 | @Override 89 | public void close() throws IOException { 90 | socket.close(); 91 | } 92 | } 93 | 94 | private static final class ServerSocketChannelImpl extends ServerSocketWrapper { 95 | private final ServerSocketChannel channel; 96 | 97 | ServerSocketChannelImpl(ServerSocketChannel channel) { 98 | this.channel = channel; 99 | } 100 | 101 | @Override 102 | public void setSoTimeout(int timeout) throws SocketException {} 103 | 104 | @Override 105 | public SocketWrapper accept() throws IOException { 106 | return SocketWrapper.fromByteChannel(channel.accept()); 107 | } 108 | 109 | @Override 110 | public void close() throws IOException { 111 | channel.close(); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/org/scalasbt/ipcsocket/SocketWrapper.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket; 2 | 3 | import java.io.IOException; 4 | import java.lang.reflect.Method; 5 | import java.net.ProtocolFamily; 6 | import java.net.Socket; 7 | import java.net.SocketAddress; 8 | import java.net.StandardProtocolFamily; 9 | import java.nio.ByteBuffer; 10 | import java.nio.channels.ByteChannel; 11 | import java.nio.channels.ServerSocketChannel; 12 | import java.util.Arrays; 13 | 14 | public abstract class SocketWrapper { 15 | private SocketWrapper() {} 16 | 17 | public abstract void write(int value) throws IOException; 18 | 19 | public abstract void write(byte[] value) throws IOException; 20 | 21 | public abstract void write(byte[] b, int offset, int len) throws IOException; 22 | 23 | public abstract void close() throws IOException; 24 | 25 | public abstract int read() throws IOException; 26 | 27 | public abstract void flush() throws IOException; 28 | 29 | public static SocketWrapper fromSocket(Socket socket) { 30 | return new SocketImpl(socket); 31 | } 32 | 33 | public static SocketWrapper fromByteChannel(ByteChannel channel) { 34 | return new ByteChannelImpl(channel); 35 | } 36 | 37 | private static final class ByteChannelImpl extends SocketWrapper { 38 | private final ByteChannel channel; 39 | 40 | private ByteChannelImpl(ByteChannel channel) { 41 | this.channel = channel; 42 | } 43 | 44 | @Override 45 | public void write(int value) throws IOException { 46 | channel.write(ByteBuffer.wrap(new byte[] {(byte) value})); 47 | } 48 | 49 | @Override 50 | public void write(byte[] value) throws IOException { 51 | channel.write(ByteBuffer.wrap(value)); 52 | } 53 | 54 | @Override 55 | public void write(byte[] b, int offset, int len) throws IOException { 56 | channel.write(ByteBuffer.wrap(b, offset, len)); 57 | } 58 | 59 | @Override 60 | public void close() throws IOException { 61 | channel.close(); 62 | } 63 | 64 | @Override 65 | public int read() throws IOException { 66 | final ByteBuffer buf = ByteBuffer.allocate(1); 67 | int n; 68 | do { 69 | n = channel.read(buf); 70 | } while (n == 0); 71 | 72 | if (-1 == n) { 73 | return -1; 74 | } else { 75 | return buf.get(0) & 0xff; 76 | } 77 | } 78 | 79 | @Override 80 | public void flush() {} 81 | } 82 | 83 | private static final class SocketImpl extends SocketWrapper { 84 | private final Socket socket; 85 | 86 | private SocketImpl(Socket socket) { 87 | this.socket = socket; 88 | } 89 | 90 | @Override 91 | public void write(int value) throws IOException { 92 | socket.getOutputStream().write(value); 93 | } 94 | 95 | @Override 96 | public void write(byte[] value) throws IOException { 97 | socket.getOutputStream().write(value); 98 | } 99 | 100 | @Override 101 | public void write(byte[] b, int offset, int len) throws IOException { 102 | socket.getOutputStream().write(b, offset, len); 103 | } 104 | 105 | @Override 106 | public void close() throws IOException { 107 | socket.getOutputStream().close(); 108 | socket.getInputStream().close(); 109 | } 110 | 111 | @Override 112 | public int read() throws IOException { 113 | return socket.getInputStream().read(); 114 | } 115 | 116 | @Override 117 | public void flush() throws IOException { 118 | socket.getOutputStream().flush(); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/org/scalasbt/ipcsocket/UnixDomainServerSocket.java: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2004-2015, Martian Software, Inc. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | */ 18 | package org.scalasbt.ipcsocket; 19 | 20 | import java.io.IOException; 21 | import java.net.ServerSocket; 22 | import java.net.Socket; 23 | import java.net.SocketAddress; 24 | import java.util.concurrent.atomic.AtomicInteger; 25 | 26 | /** 27 | * Implements a {@link ServerSocket} which binds to a local Unix domain socket and returns instances 28 | * of {@link UnixDomainSocket} from {@link #accept()}. 29 | */ 30 | public class UnixDomainServerSocket extends ServerSocket { 31 | private static final int DEFAULT_BACKLOG = 50; 32 | private static final int PF_LOCAL = 1; 33 | private static final int AF_LOCAL = 1; 34 | private static final int SOCK_STREAM = 1; 35 | 36 | // We use an AtomicInteger to prevent a race in this situation which 37 | // could happen if fd were just an int: 38 | // 39 | // Thread 1 -> UnixDomainServerSocket.accept() 40 | // -> lock this 41 | // -> check isBound and isClosed 42 | // -> unlock this 43 | // -> descheduled while still in method 44 | // Thread 2 -> UnixDomainServerSocket.close() 45 | // -> lock this 46 | // -> check isClosed 47 | // -> UnixDomainSocketLibrary.close(fd) 48 | // -> now fd is invalid 49 | // -> unlock this 50 | // Thread 1 -> re-scheduled while still in method 51 | // -> UnixDomainSocketLibrary.accept(fd, which is invalid and maybe re-used) 52 | // 53 | // By using an AtomicInteger, we'll set this to -1 after it's closed, which 54 | // will cause the accept() call above to cleanly fail instead of possibly 55 | // being called on an unrelated fd (which may or may not fail). 56 | private final AtomicInteger fd; 57 | 58 | private final int backlog; 59 | private boolean isBound; 60 | private boolean isClosed; 61 | private final UnixDomainSocketLibraryProvider provider; 62 | private final boolean useJNI; 63 | 64 | public static class UnixDomainServerSocketAddress extends SocketAddress { 65 | private final String path; 66 | 67 | public UnixDomainServerSocketAddress(String path) { 68 | this.path = path; 69 | } 70 | 71 | public String getPath() { 72 | return path; 73 | } 74 | } 75 | 76 | /** Constructs an unbound Unix domain server socket. */ 77 | public UnixDomainServerSocket() throws IOException { 78 | this(DEFAULT_BACKLOG, null, false); 79 | } 80 | 81 | /** Constructs an unbound Unix domain server socket with the specified listen backlog. */ 82 | public UnixDomainServerSocket(int backlog) throws IOException { 83 | this(backlog, null, false); 84 | } 85 | 86 | /** Constructs and binds a Unix domain server socket to the specified path. */ 87 | public UnixDomainServerSocket(String path) throws IOException { 88 | this(DEFAULT_BACKLOG, path, false); 89 | } 90 | 91 | /** Constructs and binds a Unix domain server socket to the specified path. */ 92 | public UnixDomainServerSocket(String path, boolean useJNI) throws IOException { 93 | this(DEFAULT_BACKLOG, path, useJNI); 94 | } 95 | 96 | /** 97 | * Constructs and binds a Unix domain server socket to the specified path with the specified 98 | * listen backlog. 99 | */ 100 | public UnixDomainServerSocket(int backlog, String path) throws IOException { 101 | this(backlog, path, false); 102 | } 103 | /** 104 | * Constructs and binds a Unix domain server socket to the specified path with the specified 105 | * listen backlog. 106 | */ 107 | public UnixDomainServerSocket(int backlog, String path, boolean useJNI) throws IOException { 108 | try { 109 | this.useJNI = useJNI; 110 | provider = UnixDomainSocketLibraryProvider.get(useJNI); 111 | fd = new AtomicInteger(provider.socket(PF_LOCAL, SOCK_STREAM, 0)); 112 | this.backlog = backlog; 113 | if (path != null) { 114 | bind(new UnixDomainServerSocketAddress(path)); 115 | } 116 | } catch (NativeErrorException e) { 117 | throw new IOException(e); 118 | } 119 | } 120 | 121 | public synchronized void bind(SocketAddress endpoint) throws IOException { 122 | if (!(endpoint instanceof UnixDomainServerSocketAddress)) { 123 | throw new IllegalArgumentException( 124 | "endpoint must be an instance of UnixDomainServerSocketAddress"); 125 | } 126 | if (isBound) { 127 | throw new IllegalStateException("Socket is already bound"); 128 | } 129 | if (isClosed) { 130 | throw new IllegalStateException("Socket is already closed"); 131 | } 132 | UnixDomainServerSocketAddress unEndpoint = (UnixDomainServerSocketAddress) endpoint; 133 | byte[] address = unEndpoint.getPath().getBytes(); 134 | try { 135 | int socketFd = fd.get(); 136 | provider.bind(socketFd, address, address.length); 137 | provider.listen(socketFd, backlog); 138 | isBound = true; 139 | } catch (NativeErrorException e) { 140 | throw new IOException(e); 141 | } 142 | } 143 | 144 | public Socket accept() throws IOException { 145 | // We explicitly do not make this method synchronized, since the 146 | // call to UnixDomainSocketLibrary.accept() will block 147 | // indefinitely, causing another thread's call to close() to deadlock. 148 | synchronized (this) { 149 | if (!isBound) { 150 | throw new IllegalStateException("Socket is not bound"); 151 | } 152 | if (isClosed) { 153 | throw new IllegalStateException("Socket is already closed"); 154 | } 155 | } 156 | try { 157 | int clientFd = provider.accept(fd.get()); 158 | return new UnixDomainSocket(clientFd, useJNI); 159 | } catch (NativeErrorException e) { 160 | throw new IOException(e); 161 | } 162 | } 163 | 164 | public synchronized void close() throws IOException { 165 | if (isClosed) { 166 | throw new IllegalStateException("Socket is already closed"); 167 | } 168 | try { 169 | 170 | // Ensure any pending call to accept() fails. 171 | provider.close(fd.getAndSet(-1)); 172 | isClosed = true; 173 | } catch (NativeErrorException e) { 174 | throw new IOException(e); 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/main/java/org/scalasbt/ipcsocket/UnixDomainSocket.java: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2004-2015, Martian Software, Inc. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | */ 18 | package org.scalasbt.ipcsocket; 19 | 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.io.OutputStream; 23 | 24 | import java.nio.ByteBuffer; 25 | 26 | import java.net.Socket; 27 | 28 | import java.util.concurrent.atomic.AtomicInteger; 29 | 30 | /** 31 | * Implements a {@link Socket} backed by a native Unix domain socket. 32 | * 33 | *

Instances of this class always return {@code null} for {@link Socket#getInetAddress()}, {@link 34 | * Socket#getLocalAddress()}, {@link Socket#getLocalSocketAddress()}, {@link 35 | * Socket#getRemoteSocketAddress()}. 36 | */ 37 | public class UnixDomainSocket extends Socket { 38 | private final UnixDomainSocketLibraryProvider provider; 39 | private final ReferenceCountedFileDescriptor fd; 40 | private final InputStream is; 41 | private final OutputStream os; 42 | private final String path; 43 | private static final int SHUT_RD = 0; 44 | private static final int SHUT_WR = 1; 45 | 46 | /** Creates a Unix domain socket backed by a file path. */ 47 | public UnixDomainSocket(String path, boolean useJNI) throws IOException { 48 | try { 49 | this.path = path; 50 | provider = UnixDomainSocketLibraryProvider.get(useJNI); 51 | AtomicInteger fd = 52 | new AtomicInteger( 53 | provider.socket( 54 | UnixDomainSocketLibrary.PF_LOCAL, UnixDomainSocketLibrary.SOCK_STREAM, 0)); 55 | int socketFd = fd.get(); 56 | provider.connect(socketFd, path.getBytes(), path.length()); 57 | this.fd = new ReferenceCountedFileDescriptor(socketFd, provider); 58 | this.is = new UnixDomainSocketInputStream(); 59 | this.os = new UnixDomainSocketOutputStream(); 60 | } catch (NativeErrorException e) { 61 | throw new IOException(e); 62 | } 63 | } 64 | 65 | public UnixDomainSocket(String path) throws IOException { 66 | this(path, false); 67 | } 68 | 69 | /** Creates a Unix domain socket backed by a native file descriptor. */ 70 | public UnixDomainSocket(int fd, boolean useJNI) { 71 | provider = UnixDomainSocketLibraryProvider.get(useJNI); 72 | this.path = null; 73 | this.fd = new ReferenceCountedFileDescriptor(fd, provider); 74 | this.is = new UnixDomainSocketInputStream(); 75 | this.os = new UnixDomainSocketOutputStream(); 76 | } 77 | 78 | public UnixDomainSocket(int fd) { 79 | this(fd, false); 80 | } 81 | 82 | public String toString() { 83 | return "UnixDomainSocket(path = " + this.path + ")"; 84 | } 85 | 86 | public InputStream getInputStream() { 87 | return is; 88 | } 89 | 90 | public OutputStream getOutputStream() { 91 | return os; 92 | } 93 | 94 | public void shutdownInput() throws IOException { 95 | doShutdown(SHUT_RD); 96 | } 97 | 98 | public void shutdownOutput() throws IOException { 99 | doShutdown(SHUT_WR); 100 | } 101 | 102 | private void doShutdown(int how) throws IOException { 103 | try { 104 | int socketFd = fd.acquire(); 105 | if (socketFd != -1) { 106 | provider.shutdown(socketFd, how); 107 | } 108 | } catch (NativeErrorException e) { 109 | throw new IOException(e); 110 | } finally { 111 | fd.release(); 112 | } 113 | } 114 | 115 | public void close() throws IOException { 116 | super.close(); 117 | // This might not close the FD right away. In case we are about 118 | // to read or write on another thread, it will delay the close 119 | // until the read or write completes, to prevent the FD from 120 | // being re-used for a different purpose and the other thread 121 | // reading from a different FD. 122 | fd.close(); 123 | } 124 | 125 | private class UnixDomainSocketInputStream extends InputStream { 126 | public int read() throws IOException { 127 | byte[] buf = new byte[1]; 128 | int result; 129 | if (doRead(buf, 0, 1) == 0) { 130 | result = -1; 131 | } else { 132 | // Make sure to & with 0xFF to avoid sign extension 133 | result = 0xFF & buf[0]; 134 | } 135 | return result; 136 | } 137 | 138 | public int read(byte[] b, int off, int len) throws IOException { 139 | if (len == 0) { 140 | return 0; 141 | } 142 | int result = doRead(b, off, len); 143 | if (result == 0) { 144 | try { 145 | provider.close(fd.acquire()); 146 | } catch (final NativeErrorException e) { 147 | throw new IOException( 148 | "Error closing " + fd.acquire() + (path == null ? "" : " for " + path)); 149 | } 150 | result = -1; 151 | } 152 | return result; 153 | } 154 | 155 | private int doRead(byte[] buf, int offset, int len) throws IOException { 156 | try { 157 | int fdToRead = fd.acquire(); 158 | if (fdToRead == -1) { 159 | return -1; 160 | } 161 | return provider.read(fdToRead, buf, offset, len); 162 | } catch (NativeErrorException e) { 163 | throw new IOException(e); 164 | } finally { 165 | fd.release(); 166 | } 167 | } 168 | } 169 | 170 | private class UnixDomainSocketOutputStream extends OutputStream { 171 | 172 | public void write(int b) throws IOException { 173 | doWrite(new byte[] {(byte) (0xFF & b)}, 0, 1); 174 | } 175 | 176 | public void write(byte[] b, int off, int len) throws IOException { 177 | if (len == 0) { 178 | return; 179 | } 180 | doWrite(b, off, len); 181 | } 182 | 183 | private void doWrite(byte[] b, int off, int len) throws IOException { 184 | try { 185 | int fdToWrite = fd.acquire(); 186 | if (fdToWrite == -1) { 187 | return; 188 | } 189 | int ret = provider.write(fdToWrite, b, off, len); 190 | if (ret != len) { 191 | // This shouldn't happen with standard blocking Unix domain sockets. 192 | throw new IOException( 193 | "Could not write " 194 | + len 195 | + " bytes as requested " 196 | + "(wrote " 197 | + ret 198 | + " bytes instead)"); 199 | } 200 | } catch (NativeErrorException e) { 201 | throw new IOException(e); 202 | } finally { 203 | fd.release(); 204 | } 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/main/java/org/scalasbt/ipcsocket/UnixDomainSocketLibrary.java: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2004-2015, Martian Software, Inc. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | */ 18 | package org.scalasbt.ipcsocket; 19 | 20 | import com.sun.jna.LastErrorException; 21 | import com.sun.jna.Native; 22 | import com.sun.jna.Platform; 23 | import com.sun.jna.Structure; 24 | import com.sun.jna.Union; 25 | import com.sun.jna.ptr.IntByReference; 26 | 27 | import java.io.IOException; 28 | import java.io.InputStream; 29 | import java.net.URL; 30 | import java.nio.ByteBuffer; 31 | import java.nio.channels.FileChannel; 32 | import java.nio.file.Files; 33 | import java.nio.file.Path; 34 | import java.nio.file.StandardOpenOption; 35 | import java.util.Arrays; 36 | import java.util.List; 37 | import java.util.concurrent.atomic.AtomicBoolean; 38 | import java.util.concurrent.atomic.AtomicReference; 39 | 40 | /** Utility class to bridge native Unix domain socket calls to Java using JNA. */ 41 | public class UnixDomainSocketLibrary { 42 | public static final int PF_LOCAL = 1; 43 | public static final int AF_LOCAL = 1; 44 | public static final int SOCK_STREAM = 1; 45 | 46 | public static final int SHUT_RD = 0; 47 | public static final int SHUT_WR = 1; 48 | 49 | // Utility class, do not instantiate. 50 | private UnixDomainSocketLibrary() {} 51 | 52 | // BSD platforms write a length byte at the start of struct sockaddr_un. 53 | private static final boolean HAS_SUN_LEN = 54 | Platform.isMac() 55 | || Platform.isFreeBSD() 56 | || Platform.isNetBSD() 57 | || Platform.isOpenBSD() 58 | || Platform.iskFreeBSD(); 59 | 60 | /** Bridges {@code struct sockaddr_un} to and from native code. */ 61 | public static class SockaddrUn extends Structure implements Structure.ByReference { 62 | /** 63 | * On BSD platforms, the {@code sun_len} and {@code sun_family} values in {@code struct 64 | * sockaddr_un}. 65 | */ 66 | public static class SunLenAndFamily extends Structure { 67 | public byte sunLen; 68 | public byte sunFamily; 69 | 70 | protected List getFieldOrder() { 71 | return Arrays.asList(new String[] {"sunLen", "sunFamily"}); 72 | } 73 | } 74 | 75 | /** 76 | * On BSD platforms, {@code sunLenAndFamily} will be present. On other platforms, only {@code 77 | * sunFamily} will be present. 78 | */ 79 | public static class SunFamily extends Union { 80 | public SunLenAndFamily sunLenAndFamily; 81 | public short sunFamily; 82 | } 83 | 84 | public SunFamily sunFamily = new SunFamily(); 85 | public byte[] sunPath = new byte[104]; 86 | 87 | /** Constructs an empty {@code struct sockaddr_un}. */ 88 | public SockaddrUn() { 89 | if (HAS_SUN_LEN) { 90 | sunFamily.sunLenAndFamily = new SunLenAndFamily(); 91 | sunFamily.setType(SunLenAndFamily.class); 92 | } else { 93 | sunFamily.setType(Short.TYPE); 94 | } 95 | allocateMemory(); 96 | } 97 | 98 | /** 99 | * Constructs a {@code struct sockaddr_un} with a path whose bytes are encoded using the default 100 | * encoding of the platform. 101 | */ 102 | public SockaddrUn(String path) throws IOException { 103 | byte[] pathBytes = path.getBytes(); 104 | if (pathBytes.length > sunPath.length - 1) { 105 | throw new IOException( 106 | "Cannot fit name [" + path + "] in maximum unix domain socket length"); 107 | } 108 | System.arraycopy(pathBytes, 0, sunPath, 0, pathBytes.length); 109 | sunPath[pathBytes.length] = (byte) 0; 110 | if (HAS_SUN_LEN) { 111 | int len = fieldOffset("sunPath") + pathBytes.length; 112 | sunFamily.sunLenAndFamily = new SunLenAndFamily(); 113 | sunFamily.sunLenAndFamily.sunLen = (byte) len; 114 | sunFamily.sunLenAndFamily.sunFamily = AF_LOCAL; 115 | sunFamily.setType(SunLenAndFamily.class); 116 | } else { 117 | sunFamily.sunFamily = AF_LOCAL; 118 | sunFamily.setType(Short.TYPE); 119 | } 120 | allocateMemory(); 121 | } 122 | 123 | protected List getFieldOrder() { 124 | return Arrays.asList(new String[] {"sunFamily", "sunPath"}); 125 | } 126 | } 127 | 128 | static { 129 | Native.register(Platform.C_LIBRARY_NAME); 130 | } 131 | 132 | public static native int socket(int domain, int type, int protocol) throws LastErrorException; 133 | 134 | public static native int bind(int fd, SockaddrUn address, int addressLen) 135 | throws LastErrorException; 136 | 137 | public static native int listen(int fd, int backlog) throws LastErrorException; 138 | 139 | public static native int accept(int fd, SockaddrUn address, IntByReference addressLen) 140 | throws LastErrorException; 141 | 142 | public static native int connect(int fd, SockaddrUn address, int addressLen) 143 | throws LastErrorException; 144 | 145 | public static native int read(int fd, ByteBuffer buffer, int count) throws LastErrorException; 146 | 147 | public static native int write(int fd, ByteBuffer buffer, int count) throws LastErrorException; 148 | 149 | public static native int close(int fd) throws LastErrorException; 150 | 151 | public static native int shutdown(int fd, int how) throws LastErrorException; 152 | } 153 | 154 | class JNAUnixDomainSocketLibraryProvider implements UnixDomainSocketLibraryProvider { 155 | private static final JNAUnixDomainSocketLibraryProvider instance = 156 | new JNAUnixDomainSocketLibraryProvider(); 157 | 158 | static final JNAUnixDomainSocketLibraryProvider instance() { 159 | return instance; 160 | } 161 | 162 | @Override 163 | public int socket(int domain, int type, int protocol) throws NativeErrorException { 164 | try { 165 | return UnixDomainSocketLibrary.socket(domain, type, protocol); 166 | } catch (final LastErrorException e) { 167 | throw new NativeErrorException(e.getErrorCode(), e.getMessage()); 168 | } 169 | } 170 | 171 | @Override 172 | public int bind(int fd, byte[] address, int addressLen) throws NativeErrorException { 173 | try { 174 | final UnixDomainSocketLibrary.SockaddrUn sockaddrUn = 175 | new UnixDomainSocketLibrary.SockaddrUn(new String(address)); 176 | return UnixDomainSocketLibrary.bind(fd, sockaddrUn, sockaddrUn.size()); 177 | } catch (final LastErrorException e) { 178 | throw new NativeErrorException(e.getErrorCode(), e.getMessage()); 179 | } catch (final IOException e) { 180 | throw new NativeErrorException(0, e.getMessage()); 181 | } 182 | } 183 | 184 | @Override 185 | public int listen(int fd, int backlog) throws NativeErrorException { 186 | try { 187 | final UnixDomainSocketLibrary.SockaddrUn sockaddrUn = 188 | new UnixDomainSocketLibrary.SockaddrUn(); 189 | return UnixDomainSocketLibrary.listen(fd, backlog); 190 | } catch (final LastErrorException e) { 191 | throw new NativeErrorException(e.getErrorCode(), e.getMessage()); 192 | } 193 | } 194 | 195 | @Override 196 | public int accept(int fd) throws NativeErrorException { 197 | try { 198 | final UnixDomainSocketLibrary.SockaddrUn sockaddrUn = 199 | new UnixDomainSocketLibrary.SockaddrUn(); 200 | IntByReference addressLen = new IntByReference(); 201 | addressLen.setValue(sockaddrUn.size()); 202 | return UnixDomainSocketLibrary.accept(fd, sockaddrUn, addressLen); 203 | } catch (final LastErrorException e) { 204 | throw new NativeErrorException(e.getErrorCode(), e.getMessage()); 205 | } 206 | } 207 | 208 | @Override 209 | public int connect(int fd, byte[] address, int len) throws NativeErrorException { 210 | try { 211 | final UnixDomainSocketLibrary.SockaddrUn sockaddrUn = 212 | new UnixDomainSocketLibrary.SockaddrUn(new String(address)); 213 | return UnixDomainSocketLibrary.connect(fd, sockaddrUn, sockaddrUn.size()); 214 | } catch (final LastErrorException e) { 215 | throw new NativeErrorException(e.getErrorCode(), e.getMessage()); 216 | } catch (final IOException e) { 217 | throw new NativeErrorException(-1, e.getMessage()); 218 | } 219 | } 220 | 221 | @Override 222 | public int read(int fd, byte[] buffer, int offset, int len) throws NativeErrorException { 223 | try { 224 | if (offset > buffer.length - 1) { 225 | String message = "offset: " + offset + " greater than buffer size " + buffer.length; 226 | throw new IllegalArgumentException(message); 227 | } 228 | if (offset + len > buffer.length) { 229 | String message = 230 | "Tried to read more bytes " 231 | + len 232 | + " than available from position " 233 | + offset 234 | + " in buffer of size " 235 | + buffer.length; 236 | throw new IllegalArgumentException(message); 237 | } 238 | return UnixDomainSocketLibrary.read(fd, ByteBuffer.wrap(buffer, offset, len), len); 239 | } catch (final LastErrorException e) { 240 | throw new NativeErrorException(e.getErrorCode(), e.getMessage()); 241 | } 242 | } 243 | 244 | @Override 245 | public int write(int fd, byte[] buffer, int offset, int len) throws NativeErrorException { 246 | try { 247 | return UnixDomainSocketLibrary.write(fd, ByteBuffer.wrap(buffer, offset, len), len); 248 | } catch (final LastErrorException e) { 249 | throw new NativeErrorException(e.getErrorCode(), e.getMessage()); 250 | } 251 | } 252 | 253 | @Override 254 | public int close(int fd) throws NativeErrorException { 255 | try { 256 | return UnixDomainSocketLibrary.close(fd); 257 | } catch (final LastErrorException e) { 258 | throw new NativeErrorException(e.getErrorCode(), e.getMessage()); 259 | } 260 | } 261 | 262 | @Override 263 | public int shutdown(int fd, int how) throws NativeErrorException { 264 | try { 265 | return UnixDomainSocketLibrary.shutdown(fd, how); 266 | } catch (final LastErrorException e) { 267 | throw new NativeErrorException(e.getErrorCode(), e.getMessage()); 268 | } 269 | } 270 | 271 | @Override 272 | public int maxSocketLength() { 273 | return new UnixDomainSocketLibrary.SockaddrUn().sunPath.length; 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /src/main/java/org/scalasbt/ipcsocket/UnixDomainSocketLibraryProvider.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket; 2 | 3 | public interface UnixDomainSocketLibraryProvider { 4 | int socket(int domain, int type, int protocol) throws NativeErrorException; 5 | 6 | int bind(int fd, byte[] address, int addressLen) throws NativeErrorException; 7 | 8 | int listen(int fd, int backlog) throws NativeErrorException; 9 | 10 | int accept(int fd) throws NativeErrorException; 11 | 12 | int connect(int fd, byte[] address, int len) throws NativeErrorException; 13 | 14 | int read(int fd, byte[] buffer, int offset, int len) throws NativeErrorException; 15 | 16 | int write(int fd, byte[] buffer, int offset, int len) throws NativeErrorException; 17 | 18 | int close(int fd) throws NativeErrorException; 19 | 20 | int shutdown(int fd, int how) throws NativeErrorException; 21 | 22 | int maxSocketLength(); 23 | 24 | static UnixDomainSocketLibraryProvider get(boolean useJNI) { 25 | return useJNI 26 | ? JNIUnixDomainSocketLibraryProvider.instance() 27 | : JNAUnixDomainSocketLibraryProvider.instance(); 28 | } 29 | 30 | static int maxSocketLength(boolean useJNI) { 31 | return get(useJNI).maxSocketLength(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/scalasbt/ipcsocket/Win32NamedPipeLibrary.java: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2004-2017, Martian Software, Inc. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | */ 18 | package org.scalasbt.ipcsocket; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import java.util.concurrent.LinkedBlockingQueue; 23 | import java.io.IOException; 24 | import java.nio.ByteBuffer; 25 | 26 | import com.sun.jna.*; 27 | import com.sun.jna.platform.win32.WinNT; 28 | import com.sun.jna.ptr.IntByReference; 29 | 30 | import com.sun.jna.win32.W32APIOptions; 31 | 32 | import com.sun.jna.platform.win32.WinBase; 33 | import com.sun.jna.platform.win32.WinError; 34 | import com.sun.jna.platform.win32.WinNT; 35 | import com.sun.jna.platform.win32.WinNT.HANDLE; 36 | import com.sun.jna.platform.win32.WinBase.SECURITY_ATTRIBUTES; 37 | 38 | public interface Win32NamedPipeLibrary extends Library, WinNT { 39 | int PIPE_ACCESS_DUPLEX = 3; 40 | int PIPE_UNLIMITED_INSTANCES = 255; 41 | int FILE_FLAG_FIRST_PIPE_INSTANCE = 524288; 42 | 43 | Win32NamedPipeLibrary INSTANCE = 44 | (Win32NamedPipeLibrary) 45 | Native.loadLibrary( 46 | "kernel32", Win32NamedPipeLibrary.class, W32APIOptions.UNICODE_OPTIONS); 47 | 48 | HANDLE CreateNamedPipe( 49 | String lpName, 50 | int dwOpenMode, 51 | int dwPipeMode, 52 | int nMaxInstances, 53 | int nOutBufferSize, 54 | int nInBufferSize, 55 | int nDefaultTimeOut, 56 | SECURITY_ATTRIBUTES lpSecurityAttributes); 57 | 58 | HANDLE CreateFile( 59 | String lpFileName, 60 | int dwDesiredAccess, 61 | int dwSharedMode, 62 | SECURITY_ATTRIBUTES lpSecurityAttributes, 63 | int dwCreationDisposition, 64 | int dwFlagsAndAttributes, 65 | HANDLE hTemplateFile); 66 | 67 | boolean ConnectNamedPipe(HANDLE hNamedPipe, Pointer lpOverlapped); 68 | 69 | boolean DisconnectNamedPipe(HANDLE hObject); 70 | 71 | boolean ReadFile( 72 | HANDLE hFile, 73 | Memory lpBuffer, 74 | int nNumberOfBytesToRead, 75 | IntByReference lpNumberOfBytesRead, 76 | Pointer lpOverlapped); 77 | 78 | boolean WriteFile( 79 | HANDLE hFile, 80 | ByteBuffer lpBuffer, 81 | int nNumberOfBytesToWrite, 82 | IntByReference lpNumberOfBytesWritten, 83 | Pointer lpOverlapped); 84 | 85 | boolean CloseHandle(HANDLE hObject); 86 | 87 | boolean GetOverlappedResult( 88 | HANDLE hFile, Pointer lpOverlapped, IntByReference lpNumberOfBytesTransferred, boolean wait); 89 | 90 | boolean CancelIoEx(HANDLE hObject, Pointer lpOverlapped); 91 | 92 | HANDLE CreateEvent( 93 | SECURITY_ATTRIBUTES lpEventAttributes, 94 | boolean bManualReset, 95 | boolean bInitialState, 96 | String lpName); 97 | 98 | boolean FlushFileBuffers(HANDLE hObject); 99 | 100 | int GetLastError(); 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/org/scalasbt/ipcsocket/Win32NamedPipeLibraryProvider.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket; 2 | 3 | import java.io.IOException; 4 | 5 | interface Win32NamedPipeLibraryProvider { 6 | Handle CreateNamedPipe( 7 | String lpName, 8 | int dwOpenMode, 9 | int dwPipeMode, 10 | int nMaxInstances, 11 | int nOutBufferSize, 12 | int nInBufferSize, 13 | int nDefaultTimeOut, 14 | int lpSecurityAttributes, 15 | int logonDacl) 16 | throws IOException; 17 | 18 | Handle CreateFile(String pipeName) throws IOException; 19 | 20 | int ConnectNamedPipe(Handle hNamedPipe, Overlapped lpOverlapped); 21 | 22 | boolean DisconnectNamedPipe(Handle handle); 23 | 24 | int read( 25 | Handle waitable, 26 | Handle hFile, 27 | byte[] buffer, 28 | int offset, 29 | int len, 30 | boolean requireStrictLength) 31 | throws IOException; 32 | 33 | void write(Handle waitable, Handle hFile, byte[] lpBuffer, int offset, int len) 34 | throws IOException; 35 | 36 | boolean CloseHandle(Handle handle); 37 | 38 | boolean GetOverlappedResult(Handle hFile, Overlapped lpOverlapped); 39 | 40 | boolean CancelIoEx(Handle handle); 41 | 42 | Handle CreateEvent(boolean bManualReset, boolean bInitialState, String lpName) throws IOException; 43 | 44 | int GetLastError(); 45 | 46 | Overlapped NewOverlapped(Handle hEvent); 47 | 48 | void DeleteOverlapped(Overlapped overlapped); 49 | 50 | boolean FlushFileBuffers(Handle handle); 51 | 52 | // Constants: 53 | int ERROR_IO_PENDING(); 54 | 55 | int ERROR_NO_DATA(); 56 | 57 | int ERROR_PIPE_CONNECTED(); 58 | 59 | int FILE_ALL_ACCESS(); 60 | 61 | int FILE_FLAG_FIRST_PIPE_INSTANCE(); 62 | 63 | int FILE_FLAG_OVERLAPPED(); 64 | 65 | int FILE_GENERIC_READ(); 66 | 67 | int GENERIC_READ(); 68 | 69 | int GENERIC_WRITE(); 70 | 71 | int PIPE_ACCESS_DUPLEX(); 72 | 73 | static Win32NamedPipeLibraryProvider get(boolean useJNI) { 74 | return useJNI 75 | ? JNIWin32NamedPipeLibraryProvider.instance() 76 | : JNAWin32NamedPipeLibraryProvider.instance(); 77 | } 78 | } 79 | 80 | interface Handle {} 81 | 82 | interface Overlapped {} 83 | -------------------------------------------------------------------------------- /src/main/java/org/scalasbt/ipcsocket/Win32NamedPipeServerSocket.java: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2004-2017, Martian Software, Inc. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | */ 18 | package org.scalasbt.ipcsocket; 19 | 20 | import java.io.IOException; 21 | import java.net.ServerSocket; 22 | import java.net.Socket; 23 | import java.net.SocketAddress; 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | import java.util.concurrent.LinkedBlockingQueue; 27 | 28 | public class Win32NamedPipeServerSocket extends ServerSocket { 29 | private static final String WIN32_PIPE_PREFIX = "\\\\.\\pipe\\"; 30 | private static final int BUFFER_SIZE = 65535; 31 | private final LinkedBlockingQueue openHandles; 32 | private final LinkedBlockingQueue connectedHandles; 33 | private final Win32NamedPipeSocket.CloseCallback closeCallback; 34 | private final String path; 35 | private final int maxInstances; 36 | private final Handle lockHandle; 37 | private final boolean requireStrictLength; 38 | private final Win32NamedPipeLibraryProvider provider; 39 | private final boolean useJNI; 40 | private final int securityLevel; 41 | 42 | public Win32NamedPipeServerSocket(String path) throws IOException { 43 | this(Win32NamedPipeLibrary.PIPE_UNLIMITED_INSTANCES, path); 44 | } 45 | 46 | public Win32NamedPipeServerSocket(String path, boolean useJNI, int securityLevel) 47 | throws IOException { 48 | this( 49 | Win32NamedPipeLibrary.PIPE_UNLIMITED_INSTANCES, 50 | path, 51 | Win32NamedPipeSocket.DEFAULT_REQUIRE_STRICT_LENGTH, 52 | useJNI, 53 | securityLevel); 54 | } 55 | 56 | /** 57 | * The doc for InputStream#read(byte[] b, int off, int len) states that "An attempt is made to 58 | * read as many as len bytes, but a smaller number may be read." However, using 59 | * requireStrictLength, Win32NamedPipeSocketInputStream can require that len matches up exactly 60 | * the number of bytes to read. 61 | */ 62 | @Deprecated 63 | public Win32NamedPipeServerSocket(String path, boolean requireStrictLength) throws IOException { 64 | this(Win32NamedPipeLibrary.PIPE_UNLIMITED_INSTANCES, path, requireStrictLength); 65 | } 66 | 67 | public Win32NamedPipeServerSocket(int maxInstances, String path) throws IOException { 68 | this(maxInstances, path, Win32NamedPipeSocket.DEFAULT_REQUIRE_STRICT_LENGTH); 69 | } 70 | 71 | /** 72 | * The doc for InputStream#read(byte[] b, int off, int len) states that "An attempt is made to 73 | * read as many as len bytes, but a smaller number may be read." However, using 74 | * requireStrictLength, NGWin32NamedPipeSocketInputStream can require that len matches up exactly 75 | * the number of bytes to read. 76 | */ 77 | public Win32NamedPipeServerSocket(int maxInstances, String path, boolean requireStrictLength) 78 | throws IOException { 79 | this(maxInstances, path, requireStrictLength, false, Win32SecurityLevel.LOGON_DACL); 80 | } 81 | /** 82 | * The doc for InputStream#read(byte[] b, int off, int len) states that "An attempt is made to 83 | * read as many as len bytes, but a smaller number may be read." However, using 84 | * requireStrictLength, NGWin32NamedPipeSocketInputStream can require that len matches up exactly 85 | * the number of bytes to read. 86 | */ 87 | public Win32NamedPipeServerSocket( 88 | int maxInstances, String path, boolean requireStrictLength, boolean useJNI, int securityLevel) 89 | throws IOException { 90 | this.securityLevel = securityLevel; 91 | this.useJNI = useJNI; 92 | this.provider = 93 | useJNI 94 | ? JNIWin32NamedPipeLibraryProvider.instance() 95 | : JNAWin32NamedPipeLibraryProvider.instance(); 96 | this.openHandles = new LinkedBlockingQueue<>(); 97 | this.connectedHandles = new LinkedBlockingQueue<>(); 98 | this.closeCallback = 99 | handle -> { 100 | if (connectedHandles.remove(handle)) { 101 | closeConnectedPipe(handle); 102 | } 103 | if (openHandles.remove(handle)) { 104 | closeOpenPipe(handle); 105 | } 106 | }; 107 | this.maxInstances = maxInstances; 108 | this.requireStrictLength = requireStrictLength; 109 | if (!path.startsWith(WIN32_PIPE_PREFIX)) { 110 | this.path = WIN32_PIPE_PREFIX + path; 111 | } else { 112 | this.path = path; 113 | } 114 | String lockPath = this.path + "_lock"; 115 | try { 116 | lockHandle = 117 | provider.CreateNamedPipe( 118 | lockPath, 119 | provider.FILE_FLAG_FIRST_PIPE_INSTANCE() | provider.PIPE_ACCESS_DUPLEX(), 120 | 0, 121 | 1, 122 | BUFFER_SIZE, 123 | BUFFER_SIZE, 124 | 0, 125 | provider.FILE_GENERIC_READ(), 126 | securityLevel); 127 | } catch (final IOException e) { 128 | throw new IOException( 129 | String.format( 130 | "Could not create lock for %s, error %d", lockPath, provider.GetLastError())); 131 | } 132 | if (!provider.DisconnectNamedPipe(lockHandle)) { 133 | throw new IOException(String.format("Could not disconnect lock %d", provider.GetLastError())); 134 | } 135 | } 136 | 137 | public void bind(SocketAddress endpoint) throws IOException { 138 | throw new IOException("Win32 named pipes do not support bind(), pass path to constructor"); 139 | } 140 | 141 | public Socket accept() throws IOException { 142 | Handle handle; 143 | try { 144 | handle = 145 | provider.CreateNamedPipe( 146 | path, 147 | provider.PIPE_ACCESS_DUPLEX() | provider.FILE_FLAG_OVERLAPPED(), 148 | 0, 149 | maxInstances, 150 | BUFFER_SIZE, 151 | BUFFER_SIZE, 152 | 0, 153 | provider.FILE_ALL_ACCESS(), 154 | securityLevel); 155 | } catch (final IOException e) { 156 | throw new IOException( 157 | String.format("Could not create named pipe, error %d", provider.GetLastError())); 158 | } 159 | openHandles.add(handle); 160 | 161 | Handle connWaitable = provider.CreateEvent(true, false, null); 162 | Overlapped overlapped = provider.NewOverlapped(connWaitable); 163 | try { 164 | 165 | int connectError = provider.ConnectNamedPipe(handle, overlapped); 166 | if (connectError == -1) { 167 | openHandles.remove(handle); 168 | connectedHandles.add(handle); 169 | return new Win32NamedPipeSocket(handle, closeCallback, requireStrictLength, useJNI); 170 | } 171 | 172 | if (connectError == provider.ERROR_PIPE_CONNECTED()) { 173 | openHandles.remove(handle); 174 | connectedHandles.add(handle); 175 | return new Win32NamedPipeSocket(handle, closeCallback, requireStrictLength, useJNI); 176 | } else if (connectError == provider.ERROR_NO_DATA()) { 177 | // Client has connected and disconnected between CreateNamedPipe() and ConnectNamedPipe() 178 | // connection is broken, but it is returned it avoid loop here. 179 | // Actual error will happen for NGSession when it will try to read/write from/to pipe 180 | return new Win32NamedPipeSocket(handle, closeCallback, requireStrictLength, useJNI); 181 | } else if (connectError == provider.ERROR_IO_PENDING()) { 182 | if (!provider.GetOverlappedResult(handle, overlapped)) { 183 | openHandles.remove(handle); 184 | closeOpenPipe(handle); 185 | throw new IOException( 186 | "GetOverlappedResult() failed for connect operation: " + provider.GetLastError()); 187 | } 188 | openHandles.remove(handle); 189 | connectedHandles.add(handle); 190 | return new Win32NamedPipeSocket(handle, closeCallback, requireStrictLength, useJNI); 191 | } else { 192 | throw new IOException("ConnectNamedPipe() failed with: " + connectError); 193 | } 194 | } finally { 195 | provider.DeleteOverlapped(overlapped); 196 | provider.CloseHandle(connWaitable); 197 | } 198 | } 199 | 200 | public void close() throws IOException { 201 | try { 202 | List handlesToClose = new ArrayList<>(); 203 | openHandles.drainTo(handlesToClose); 204 | for (Handle handle : handlesToClose) { 205 | closeOpenPipe(handle); 206 | } 207 | 208 | List handlesToDisconnect = new ArrayList<>(); 209 | connectedHandles.drainTo(handlesToDisconnect); 210 | for (Handle handle : handlesToDisconnect) { 211 | closeConnectedPipe(handle); 212 | } 213 | } finally { 214 | provider.CloseHandle(lockHandle); 215 | } 216 | } 217 | 218 | private void closeOpenPipe(Handle handle) throws IOException { 219 | provider.CancelIoEx(handle); 220 | provider.CloseHandle(handle); 221 | } 222 | 223 | private void closeConnectedPipe(Handle handle) throws IOException { 224 | provider.FlushFileBuffers(handle); 225 | provider.DisconnectNamedPipe(handle); 226 | provider.CloseHandle(handle); 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /src/main/java/org/scalasbt/ipcsocket/Win32NamedPipeSocket.java: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2004-2017, Martian Software, Inc. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | */ 18 | package org.scalasbt.ipcsocket; 19 | 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.io.OutputStream; 23 | import java.net.Socket; 24 | import java.nio.ByteBuffer; 25 | 26 | public class Win32NamedPipeSocket extends Socket { 27 | private static Handle createFile(String pipeName, boolean useJNI) throws IOException { 28 | final Win32NamedPipeLibraryProvider provider = Win32NamedPipeLibraryProvider.get(useJNI); 29 | return provider.CreateFile(pipeName); 30 | } 31 | 32 | private static CloseCallback emptyCallback() { 33 | return new CloseCallback() { 34 | public void onNamedPipeSocketClose(Handle handle) throws IOException {} 35 | }; 36 | } 37 | 38 | static final boolean DEFAULT_REQUIRE_STRICT_LENGTH = false; 39 | private final Handle handle; 40 | private final CloseCallback closeCallback; 41 | private final boolean requireStrictLength; 42 | private final InputStream is; 43 | private final OutputStream os; 44 | private final Handle readerWaitable; 45 | private final Handle writerWaitable; 46 | private final Win32NamedPipeLibraryProvider provider; 47 | 48 | interface CloseCallback { 49 | void onNamedPipeSocketClose(Handle handle) throws IOException; 50 | } 51 | 52 | /** 53 | * The doc for InputStream#read(byte[] b, int off, int len) states that "An attempt is made to 54 | * read as many as len bytes, but a smaller number may be read." However, using 55 | * requireStrictLength, NGWin32NamedPipeSocketInputStream can require that len matches up exactly 56 | * the number of bytes to read. 57 | */ 58 | public Win32NamedPipeSocket( 59 | Handle handle, CloseCallback closeCallback, boolean requireStrictLength) throws IOException { 60 | this(handle, closeCallback, requireStrictLength, false); 61 | } 62 | /** 63 | * The doc for InputStream#read(byte[] b, int off, int len) states that "An attempt is made to 64 | * read as many as len bytes, but a smaller number may be read." However, using 65 | * requireStrictLength, NGWin32NamedPipeSocketInputStream can require that len matches up exactly 66 | * the number of bytes to read. 67 | */ 68 | public Win32NamedPipeSocket( 69 | Handle handle, CloseCallback closeCallback, boolean requireStrictLength, boolean useJNI) 70 | throws IOException { 71 | this.provider = 72 | useJNI 73 | ? JNIWin32NamedPipeLibraryProvider.instance() 74 | : JNAWin32NamedPipeLibraryProvider.instance(); 75 | this.handle = handle; 76 | this.closeCallback = closeCallback; 77 | this.requireStrictLength = requireStrictLength; 78 | this.readerWaitable = provider.CreateEvent(true, false, null); 79 | writerWaitable = provider.CreateEvent(true, false, null); 80 | this.is = new Win32NamedPipeSocketInputStream(handle); 81 | this.os = new Win32NamedPipeSocketOutputStream(handle); 82 | } 83 | 84 | Win32NamedPipeSocket(Handle handle, CloseCallback closeCallback) throws IOException { 85 | this(handle, closeCallback, DEFAULT_REQUIRE_STRICT_LENGTH, false); 86 | } 87 | 88 | public Win32NamedPipeSocket(String pipeName) throws IOException { 89 | this(createFile(pipeName, false), emptyCallback(), DEFAULT_REQUIRE_STRICT_LENGTH, false); 90 | } 91 | 92 | public Win32NamedPipeSocket(String pipeName, boolean useJNI) throws IOException { 93 | this(createFile(pipeName, useJNI), emptyCallback(), DEFAULT_REQUIRE_STRICT_LENGTH, useJNI); 94 | } 95 | 96 | @Override 97 | public InputStream getInputStream() { 98 | return is; 99 | } 100 | 101 | @Override 102 | public OutputStream getOutputStream() { 103 | return os; 104 | } 105 | 106 | @Override 107 | public void close() throws IOException { 108 | closeCallback.onNamedPipeSocketClose(handle); 109 | } 110 | 111 | @Override 112 | public void shutdownInput() throws IOException {} 113 | 114 | @Override 115 | public void shutdownOutput() throws IOException {} 116 | 117 | private class Win32NamedPipeSocketInputStream extends InputStream { 118 | private final Handle handle; 119 | 120 | Win32NamedPipeSocketInputStream(Handle handle) { 121 | this.handle = handle; 122 | } 123 | 124 | @Override 125 | public int read() throws IOException { 126 | int result; 127 | byte[] b = new byte[1]; 128 | if (read(b) == 0) { 129 | result = -1; 130 | } else { 131 | result = 0xFF & b[0]; 132 | } 133 | return result; 134 | } 135 | 136 | @Override 137 | public int read(byte[] b, int off, int len) throws IOException { 138 | return provider.read(readerWaitable, handle, b, off, len, requireStrictLength); 139 | } 140 | } 141 | 142 | private class Win32NamedPipeSocketOutputStream extends OutputStream { 143 | private final Handle handle; 144 | 145 | Win32NamedPipeSocketOutputStream(Handle handle) { 146 | this.handle = handle; 147 | } 148 | 149 | @Override 150 | public void write(int b) throws IOException { 151 | write(new byte[] {(byte) (0xFF & b)}); 152 | } 153 | 154 | @Override 155 | public void write(byte[] b, int off, int len) throws IOException { 156 | provider.write(writerWaitable, handle, b, off, len); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/org/scalasbt/ipcsocket/Win32SecurityLevel.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket; 2 | 3 | public class Win32SecurityLevel { 4 | /* 5 | * Order security levels in increasing strictness. 6 | */ 7 | public static int NO_SECURITY = 0; 8 | public static int OWNER_DACL = 1; 9 | // LOGON_DACL must match the value in JNIWin32NamedPipeLibraryProvider.c 10 | public static int LOGON_DACL = 2; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/scalasbt/ipcsocket/Win32SecurityLibrary.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket; 2 | 3 | import com.sun.jna.platform.win32.WinNT; 4 | import com.sun.jna.platform.win32.WinNT.SECURITY_DESCRIPTOR; 5 | import com.sun.jna.platform.win32.WinNT.PSID; 6 | import com.sun.jna.platform.win32.WinNT.PSIDByReference; 7 | import com.sun.jna.platform.win32.WinNT.HANDLEByReference; 8 | import com.sun.jna.platform.win32.WinNT.HANDLE; 9 | import com.sun.jna.platform.win32.WinNT.SID_AND_ATTRIBUTES; 10 | import com.sun.jna.platform.win32.WinNT.ACL; 11 | import com.sun.jna.platform.win32.WinNT.ACCESS_ALLOWED_ACE; 12 | import com.sun.jna.platform.win32.WinDef.DWORD; 13 | import com.sun.jna.platform.win32.WinBase.SECURITY_ATTRIBUTES; 14 | import com.sun.jna.platform.win32.Advapi32; 15 | import com.sun.jna.platform.win32.Advapi32Util; 16 | import com.sun.jna.platform.win32.Kernel32; 17 | import com.sun.jna.platform.win32.Kernel32Util; 18 | import com.sun.jna.platform.win32.W32Errors; 19 | import com.sun.jna.ptr.IntByReference; 20 | import com.sun.jna.Native; 21 | 22 | public class Win32SecurityLibrary { 23 | 24 | private static final long SE_GROUP_LOGON_ID = 0xC0000000L; 25 | 26 | public static SECURITY_ATTRIBUTES createSecurityWithLogonDacl(int accessMask) { 27 | return createSecurityWithDacl(accessMask, Win32SecurityLevel.LOGON_DACL); 28 | } 29 | 30 | public static SECURITY_ATTRIBUTES createSecurityWithDacl(int accessMask, int securityLevel) { 31 | if (securityLevel == Win32SecurityLevel.NO_SECURITY) return null; 32 | SECURITY_DESCRIPTOR sd = new SECURITY_DESCRIPTOR(64 * 1024); 33 | Advapi32.INSTANCE.InitializeSecurityDescriptor(sd, WinNT.SECURITY_DESCRIPTOR_REVISION); 34 | Native.getLastError(); 35 | 36 | ACL pAcl; 37 | int cbAcl = 0; 38 | PSIDByReference psid = new PSIDByReference(); 39 | if (securityLevel == Win32SecurityLevel.LOGON_DACL) getLogonSID(psid); 40 | else getOwnerSID(psid); 41 | int sidLength = Advapi32.INSTANCE.GetLengthSid(psid.getValue()); 42 | cbAcl = Native.getNativeSize(ACL.class, null); 43 | cbAcl += Native.getNativeSize(ACCESS_ALLOWED_ACE.class, null); 44 | cbAcl += (sidLength - DWORD.SIZE); 45 | cbAcl = Advapi32Util.alignOnDWORD(cbAcl); 46 | pAcl = new ACL(cbAcl); 47 | Advapi32.INSTANCE.InitializeAcl(pAcl, cbAcl, WinNT.ACL_REVISION); 48 | Native.getLastError(); 49 | Advapi32.INSTANCE.AddAccessAllowedAce(pAcl, WinNT.ACL_REVISION, accessMask, psid.getValue()); 50 | Native.getLastError(); 51 | Advapi32.INSTANCE.SetSecurityDescriptorDacl(sd, true, pAcl, false); 52 | Native.getLastError(); 53 | 54 | SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES(); 55 | sa.dwLength = new DWORD(sd.size()); 56 | sa.lpSecurityDescriptor = sd.getPointer(); 57 | sa.bInheritHandle = false; 58 | 59 | return sa; 60 | } 61 | 62 | public static void getOwnerSID(PSIDByReference psid) { 63 | HANDLEByReference phToken = new HANDLEByReference(); 64 | try { 65 | Advapi32.INSTANCE.OpenProcessToken( 66 | Kernel32.INSTANCE.GetCurrentProcess(), WinNT.TOKEN_QUERY, phToken); 67 | Native.getLastError(); 68 | IntByReference tokenInformationLength = new IntByReference(); 69 | Advapi32.INSTANCE.GetTokenInformation( 70 | phToken.getValue(), 71 | WinNT.TOKEN_INFORMATION_CLASS.TokenOwner, 72 | null, 73 | 0, 74 | tokenInformationLength); 75 | WinNT.TOKEN_OWNER owner = new WinNT.TOKEN_OWNER(tokenInformationLength.getValue()); 76 | Advapi32.INSTANCE.GetTokenInformation( 77 | phToken.getValue(), 78 | WinNT.TOKEN_INFORMATION_CLASS.TokenOwner, 79 | owner, 80 | tokenInformationLength.getValue(), 81 | tokenInformationLength); 82 | Native.getLastError(); 83 | psid.setValue(owner.Owner); 84 | } finally { 85 | Kernel32Util.closeHandleRef(phToken); 86 | } 87 | } 88 | 89 | public static void getLogonSID(PSIDByReference psid) { 90 | HANDLEByReference phToken = new HANDLEByReference(); 91 | try { 92 | Advapi32.INSTANCE.OpenProcessToken( 93 | Kernel32.INSTANCE.GetCurrentProcess(), WinNT.TOKEN_QUERY, phToken); 94 | Native.getLastError(); 95 | IntByReference tokenInformationLength = new IntByReference(); 96 | Advapi32.INSTANCE.GetTokenInformation( 97 | phToken.getValue(), 98 | WinNT.TOKEN_INFORMATION_CLASS.TokenGroups, 99 | null, 100 | 0, 101 | tokenInformationLength); 102 | WinNT.TOKEN_GROUPS groups = new WinNT.TOKEN_GROUPS(tokenInformationLength.getValue()); 103 | Advapi32.INSTANCE.GetTokenInformation( 104 | phToken.getValue(), 105 | WinNT.TOKEN_INFORMATION_CLASS.TokenGroups, 106 | groups, 107 | tokenInformationLength.getValue(), 108 | tokenInformationLength); 109 | Native.getLastError(); 110 | 111 | for (SID_AND_ATTRIBUTES sidAndAttribute : groups.getGroups()) { 112 | if ((sidAndAttribute.Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID) { 113 | psid.setValue(sidAndAttribute.Sid); 114 | return; 115 | } 116 | } 117 | throw new RuntimeException("LogonSID was not found."); 118 | } finally { 119 | Kernel32Util.closeHandleRef(phToken); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/resources/darwin/x86_64/libsbtipcsocket.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbt/ipcsocket/b767e37ad3e97ea225f762266e8e6d09dadb42f0/src/main/resources/darwin/x86_64/libsbtipcsocket.dylib -------------------------------------------------------------------------------- /src/main/resources/linux/aarch64/libsbtipcsocket.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbt/ipcsocket/b767e37ad3e97ea225f762266e8e6d09dadb42f0/src/main/resources/linux/aarch64/libsbtipcsocket.so -------------------------------------------------------------------------------- /src/main/resources/linux/x86_64/libsbtipcsocket.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbt/ipcsocket/b767e37ad3e97ea225f762266e8e6d09dadb42f0/src/main/resources/linux/x86_64/libsbtipcsocket.so -------------------------------------------------------------------------------- /src/main/resources/win32/x86_64/sbtipcsocket.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sbt/ipcsocket/b767e37ad3e97ea225f762266e8e6d09dadb42f0/src/main/resources/win32/x86_64/sbtipcsocket.dll -------------------------------------------------------------------------------- /src/test/java-17/org/scalasbt/ipcsocket/ServerSocketWrapperTest.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import java.io.IOException; 8 | import java.net.ServerSocket; 9 | import java.net.StandardProtocolFamily; 10 | import java.net.UnixDomainSocketAddress; 11 | import java.nio.ByteBuffer; 12 | import java.nio.channels.SocketChannel; 13 | import java.nio.file.Files; 14 | import java.nio.file.Path; 15 | import java.util.ArrayList; 16 | import java.util.Collections; 17 | import java.util.List; 18 | import java.util.stream.Collectors; 19 | import java.util.stream.Stream; 20 | 21 | import static org.junit.Assert.assertEquals; 22 | 23 | public class ServerSocketWrapperTest { 24 | private ServerSocketWrapper serverSocket; 25 | private SocketWrapper server; 26 | private SocketChannel client; 27 | private Path dir; 28 | private Path socketPath; 29 | 30 | @Before 31 | public void before() throws IOException, ReflectiveOperationException { 32 | dir = Files.createTempDirectory(ServerSocketWrapperTest.class.getSimpleName()); 33 | socketPath = dir.resolve("socket"); 34 | Files.deleteIfExists(socketPath); 35 | if (!Files.isDirectory(dir)) { 36 | Files.createDirectories(dir); 37 | } 38 | serverSocket = 39 | ServerSocketWrapper.newJdkUnixDomainSocket(socketPath.toFile().getAbsolutePath()); 40 | client = SocketChannel.open(StandardProtocolFamily.UNIX); 41 | client.connect(UnixDomainSocketAddress.of(socketPath.toFile().getAbsolutePath())); 42 | server = serverSocket.accept(); 43 | } 44 | 45 | @After 46 | public void after() throws IOException { 47 | serverSocket.close(); 48 | Files.deleteIfExists(socketPath); 49 | Files.deleteIfExists(dir); 50 | } 51 | 52 | private List readAll(SocketChannel client) throws IOException { 53 | int res; 54 | final List values = new ArrayList<>(); 55 | do { 56 | final ByteBuffer buf = ByteBuffer.allocate(1); 57 | res = client.read(buf); 58 | if (res != -1) { 59 | values.add(buf.get(0)); 60 | } 61 | } while (res != -1); 62 | return values; 63 | } 64 | 65 | private static final List intValues; 66 | private static final List byteValues; 67 | 68 | private static final byte[] byteArray() { 69 | final byte[] array = new byte[byteValues.size()]; 70 | for (int i = 0; i < array.length; i++) { 71 | array[i] = byteValues.get(i); 72 | } 73 | return array; 74 | } 75 | 76 | static { 77 | final List list = 78 | Stream.iterate((int) Byte.MIN_VALUE, x -> x + 3) 79 | .limit(128) 80 | .collect(Collectors.toList()); 81 | Collections.shuffle(list); 82 | intValues = Collections.unmodifiableList(list); 83 | byteValues = 84 | Collections.unmodifiableList( 85 | intValues.stream().map(Integer::byteValue).collect(Collectors.toList())); 86 | } 87 | 88 | @Test 89 | public void writeInt() throws Throwable { 90 | try { 91 | intValues.forEach( 92 | x -> { 93 | try { 94 | server.write(x); 95 | } catch (IOException e) { 96 | throw new RuntimeException(e); 97 | } 98 | }); 99 | } finally { 100 | server.close(); 101 | } 102 | final List actual = readAll(client); 103 | assertEquals(byteValues, actual); 104 | } 105 | 106 | @Test 107 | public void writeByteArray() throws Throwable { 108 | try { 109 | server.write(byteArray()); 110 | } finally { 111 | server.close(); 112 | } 113 | final List actual = readAll(client); 114 | assertEquals(byteValues, actual); 115 | } 116 | 117 | @Test 118 | public void writeByteArrayOffsetLength() throws Throwable { 119 | final int offset = 20; 120 | final int length = 30; 121 | final byte[] array = byteArray(); 122 | try { 123 | server.write(array, offset, length); 124 | } finally { 125 | server.close(); 126 | } 127 | final List actual = readAll(client); 128 | final List expect = new ArrayList<>(); 129 | for (int i = offset; i < (offset + length); i++) { 130 | expect.add(array[i]); 131 | } 132 | assertEquals(expect, actual); 133 | } 134 | 135 | @Test 136 | public void read() throws Throwable { 137 | try { 138 | client.write(ByteBuffer.wrap(byteArray())); 139 | } finally { 140 | client.close(); 141 | } 142 | 143 | final List actual = new ArrayList<>(); 144 | int res; 145 | do { 146 | res = server.read(); 147 | if (res != -1) { 148 | actual.add(res); 149 | } 150 | } while (res != -1); 151 | final List expect = intValues.stream().map(i -> i & 0xff).collect(Collectors.toList()); 152 | assertEquals(expect, actual); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/test/java/org/scalasbt/ipcsocket/BaseSocketSetup.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.nio.file.Paths; 7 | import java.net.ServerSocket; 8 | import java.net.Socket; 9 | import java.util.Random; 10 | 11 | public abstract class BaseSocketSetup { 12 | 13 | final boolean isWin = System.getProperty("os.name", "").toLowerCase().startsWith("win"); 14 | 15 | Random rand = new Random(); 16 | 17 | boolean useJNI() { 18 | return false; 19 | } 20 | 21 | public static interface MayThrow { 22 | void accept(String string) throws IOException, InterruptedException; 23 | } 24 | 25 | protected void withSocket(final MayThrow consumer) throws IOException, InterruptedException { 26 | Path tempDir = isWin ? null : Files.createTempDirectory("ipcsocket"); 27 | Path socketPath = tempDir != null ? tempDir.resolve("foo" + rand.nextInt() + ".sock") : null; 28 | String socket = 29 | socketPath != null ? socketPath.toString() : "\\\\.\\pipe\\ipcsockettest" + rand.nextInt(); 30 | try { 31 | consumer.accept(socket); 32 | } finally { 33 | if (socketPath != null) Files.deleteIfExists(socketPath); 34 | if (tempDir != null) Files.deleteIfExists(socketPath); 35 | } 36 | } 37 | 38 | protected ServerSocket newServerSocket(String socketName) throws IOException { 39 | return isWin 40 | ? new Win32NamedPipeServerSocket(socketName, useJNI(), Win32SecurityLevel.LOGON_DACL) 41 | : new UnixDomainServerSocket(socketName, useJNI()); 42 | } 43 | 44 | protected Socket newClientSocket(String socketName) throws IOException { 45 | return isWin 46 | ? new Win32NamedPipeSocket(socketName, useJNI()) 47 | : new UnixDomainSocket(socketName, useJNI()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/org/scalasbt/ipcsocket/EchoServer.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.io.PrintWriter; 6 | import java.io.InputStreamReader; 7 | import java.io.BufferedReader; 8 | 9 | import java.net.ServerSocket; 10 | import java.net.Socket; 11 | 12 | import java.util.concurrent.CompletableFuture; 13 | 14 | public class EchoServer { 15 | private final ServerSocket serverSocket; 16 | 17 | public EchoServer(ServerSocket serverSocket) { 18 | this.serverSocket = serverSocket; 19 | } 20 | 21 | public void run() throws IOException { 22 | while (true) { 23 | Socket clientSocket = serverSocket.accept(); 24 | CompletableFuture.supplyAsync( 25 | () -> { 26 | try { 27 | PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); 28 | BufferedReader in = 29 | new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 30 | String line; 31 | do { 32 | line = in.readLine(); 33 | if (line != null) { 34 | System.out.println("server: " + line); 35 | out.print(line + "\n"); 36 | out.flush(); 37 | } 38 | } while (!line.trim().equals("bye")); 39 | } catch (IOException e) { 40 | } 41 | return true; 42 | }); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/org/scalasbt/ipcsocket/SocketTest.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.io.IOException; 8 | import java.io.OutputStream; 9 | import java.io.PrintWriter; 10 | 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | import java.nio.file.Paths; 14 | import org.junit.Test; 15 | import static org.junit.Assert.*; 16 | import java.net.ServerSocket; 17 | import java.net.Socket; 18 | import java.util.concurrent.CompletableFuture; 19 | import java.util.function.Consumer; 20 | 21 | public class SocketTest extends BaseSocketSetup { 22 | 23 | /* Uncomment when it works on Linux with useJNI true 24 | @Test 25 | public void testAssertEquals() throws IOException, InterruptedException { 26 | withSocket( 27 | sock -> { 28 | System.out.println("SocketTest#testAssertEquals(" + Boolean.toString(useJNI()) + ")"); 29 | 30 | ServerSocket serverSocket = newServerSocket(sock); 31 | 32 | CompletableFuture server = 33 | CompletableFuture.supplyAsync( 34 | () -> { 35 | try { 36 | EchoServer echo = new EchoServer(serverSocket); 37 | echo.run(); 38 | } catch (IOException e) { 39 | } 40 | return true; 41 | }); 42 | Thread.sleep(100); 43 | 44 | Socket client = newClientSocket(sock.toString()); 45 | PrintWriter out = new PrintWriter(client.getOutputStream(), true); 46 | BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream())); 47 | out.println("hello"); 48 | String line = in.readLine(); 49 | client.close(); 50 | server.cancel(true); 51 | serverSocket.close(); 52 | assertEquals("echo did not return the content", line, "hello"); 53 | }); 54 | } 55 | */ 56 | 57 | @Test 58 | public void throwIOExceptionOnMissingFile() throws IOException, InterruptedException { 59 | withSocket( 60 | sock -> { 61 | System.out.println( 62 | "SocketTest#throwIOExceptionOnMissingFile(" + Boolean.toString(useJNI()) + ")"); 63 | 64 | boolean caughtIOException = false; 65 | Files.deleteIfExists(Paths.get(sock)); 66 | try { 67 | Socket client = newClientSocket(sock); 68 | client.getInputStream().read(); 69 | } catch (final IOException e) { 70 | caughtIOException = true; 71 | } 72 | assertTrue("No io exception was caught", caughtIOException); 73 | }); 74 | } 75 | 76 | /* Uncomment when it works on Windows with useJNI true 77 | @Test 78 | public void shortReadWrite() throws IOException, InterruptedException { 79 | withSocket( 80 | sock -> { 81 | System.out.println("SocketTest#shortReadWrite(" + Boolean.toString(useJNI()) + ")"); 82 | 83 | ServerSocket serverSocket = newServerSocket(sock); 84 | 85 | CompletableFuture server = 86 | CompletableFuture.supplyAsync( 87 | () -> { 88 | try { 89 | EchoServer echo = new EchoServer(serverSocket); 90 | echo.run(); 91 | } catch (IOException e) { 92 | } 93 | return true; 94 | }); 95 | Thread.sleep(100); 96 | 97 | Socket client = newClientSocket(sock.toString()); 98 | OutputStream out = client.getOutputStream(); 99 | InputStream in = client.getInputStream(); 100 | String printed = "hellofoo\n"; 101 | byte[] printedBytes = printed.getBytes(); 102 | out.write(printedBytes, 0, 4); 103 | out.write(printedBytes, 4, 5); 104 | out.flush(); 105 | byte[] buf = new byte[16]; 106 | assertEquals("Did not read 4 bytes", in.read(buf, 0, 4), 4); 107 | assertEquals("Did not read 5 bytes", in.read(buf, 4, 6), 5); 108 | String line = new String(buf, 0, printed.length()); 109 | client.close(); 110 | server.cancel(true); 111 | serverSocket.close(); 112 | assertEquals("echo did not return the content", line, printed); 113 | }); 114 | } 115 | */ 116 | 117 | @Test 118 | public void testToString() throws IOException, InterruptedException { 119 | if (!isWin) { 120 | withSocket( 121 | sock -> { 122 | System.out.println("SocketTest#testToString(" + Boolean.toString(useJNI()) + ")"); 123 | ServerSocket serverSocket = newServerSocket(sock); 124 | Socket client = newClientSocket(sock); 125 | try { 126 | assertTrue(client.toString().startsWith("UnixDomainSocket(path =")); 127 | } finally { 128 | client.close(); 129 | serverSocket.close(); 130 | } 131 | }); 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/test/java/org/scalasbt/ipcsocket/SocketTestJNI.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket; 2 | 3 | public class SocketTestJNI extends SocketTest { 4 | @Override 5 | boolean useJNI() { 6 | return true; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/test/java/org/scalasbt/ipcsocket/UnixSocketLengthTest.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket; 2 | 3 | import org.junit.Test; 4 | import static org.junit.Assert.*; 5 | 6 | public class UnixSocketLengthTest { 7 | final boolean isWin = System.getProperty("os.name", "").toLowerCase().startsWith("win"); 8 | final boolean isLinux = System.getProperty("os.name", "").toLowerCase().startsWith("linux"); 9 | 10 | @Test 11 | public void testJNIMaxSocketLength() { 12 | if (!isWin) { 13 | int length = UnixDomainSocketLibraryProvider.maxSocketLength(true); 14 | int expectedLength = isLinux ? 108 : 104; 15 | assert (length == expectedLength); 16 | } 17 | } 18 | 19 | @Test 20 | public void testJNAMaxSocketLength() { 21 | if (!isWin) { 22 | int length = UnixDomainSocketLibraryProvider.maxSocketLength(false); 23 | assert (length == 104); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/org/scalasbt/ipcsocket/duplex/DuplexClient.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket.duplex; 2 | 3 | import java.io.IOException; 4 | import java.net.Socket; 5 | import java.util.concurrent.Executors; 6 | import java.util.concurrent.ExecutorService; 7 | import org.scalasbt.ipcsocket.BaseSocketSetup; 8 | 9 | public class DuplexClient extends BaseSocketSetup { 10 | 11 | private final String pipeName; 12 | private final int sendMessages; 13 | 14 | public Sender sender; 15 | public Receiver receiver; 16 | 17 | public DuplexClient(String pipeName, int sendMessages) { 18 | this.pipeName = pipeName; 19 | this.sendMessages = sendMessages; 20 | } 21 | 22 | public void startAndAwait() { 23 | ExecutorService pool = Executors.newFixedThreadPool(2); 24 | 25 | try (Socket socket = newClientSocket(pipeName)) { 26 | sender = new Sender("client", socket, sendMessages); 27 | receiver = new Receiver("client", socket); 28 | pool.execute(sender); 29 | pool.execute(receiver); 30 | 31 | Thread.sleep(30 * 1000); 32 | pool.shutdownNow(); 33 | } catch (InterruptedException | IOException e) { 34 | e.printStackTrace(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/org/scalasbt/ipcsocket/duplex/DuplexServer.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket.duplex; 2 | 3 | import java.io.IOException; 4 | import java.net.ServerSocket; 5 | import java.net.Socket; 6 | import java.util.concurrent.Executors; 7 | import java.util.concurrent.ExecutorService; 8 | import org.scalasbt.ipcsocket.BaseSocketSetup; 9 | 10 | public class DuplexServer extends BaseSocketSetup { 11 | 12 | private final String pipeName; 13 | private final int sendMessages; 14 | 15 | public Sender sender; 16 | public Receiver receiver; 17 | 18 | public DuplexServer(String pipeName, int sendMessages) { 19 | this.pipeName = pipeName; 20 | this.sendMessages = sendMessages; 21 | } 22 | 23 | public void startAndAwait() { 24 | ExecutorService pool = Executors.newFixedThreadPool(10); 25 | 26 | System.out.println("DuplexServer started. Waiting for client..."); 27 | try (ServerSocket serverSocket = newServerSocket(pipeName); 28 | Socket socket = serverSocket.accept()) { 29 | 30 | sender = new Sender("server", socket, sendMessages); 31 | receiver = new Receiver("server", socket); 32 | pool.execute(sender); 33 | pool.execute(receiver); 34 | 35 | Thread.sleep(30 * 1000); 36 | pool.shutdownNow(); 37 | } catch (InterruptedException | IOException e) { 38 | e.printStackTrace(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/org/scalasbt/ipcsocket/duplex/DuplexTest.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket.duplex; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import java.util.concurrent.ExecutorService; 5 | import java.util.concurrent.Executors; 6 | import org.junit.Test; 7 | import org.scalasbt.ipcsocket.BaseSocketSetup; 8 | 9 | public class DuplexTest extends BaseSocketSetup { 10 | 11 | @Test 12 | public void testDuplexCommunication() throws Exception { 13 | withSocket( 14 | socketName -> { 15 | ExecutorService pool = Executors.newFixedThreadPool(2); 16 | 17 | // start server 18 | int serverSendMessages = 15; 19 | DuplexServer server = new DuplexServer(socketName, serverSendMessages); 20 | pool.execute(() -> server.startAndAwait()); 21 | 22 | // wait for pipe to be instantiated 23 | Thread.sleep(2000); 24 | 25 | // start client 26 | int clientSendMessages = 7; 27 | DuplexClient client = new DuplexClient(socketName, clientSendMessages); 28 | pool.execute(() -> client.startAndAwait()); 29 | 30 | // wait client and server to terminate 31 | Thread.sleep((Math.max(serverSendMessages, clientSendMessages) + 3) * 1000); 32 | pool.shutdown(); 33 | 34 | assertEquals(serverSendMessages, client.receiver.receivedMessages); 35 | assertEquals(clientSendMessages, server.receiver.receivedMessages); 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/org/scalasbt/ipcsocket/duplex/Receiver.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket.duplex; 2 | 3 | import java.io.IOException; 4 | import java.net.Socket; 5 | import java.util.Random; 6 | import java.util.Scanner; 7 | 8 | public class Receiver implements Runnable { 9 | 10 | private final String name; 11 | private final Socket socket; 12 | public volatile int receivedMessages; 13 | private final Random random = new Random(); 14 | 15 | public Receiver(String name, Socket socket) { 16 | this.name = name; 17 | this.socket = socket; 18 | } 19 | 20 | @Override 21 | public void run() { 22 | try (Scanner in = new Scanner(socket.getInputStream())) { 23 | while (true) { 24 | while (in.hasNextLine()) { 25 | System.out.println("[" + name + "] got a message: " + in.nextLine()); 26 | receivedMessages++; 27 | } 28 | Thread.sleep(Math.abs(random.nextInt(1000))); 29 | } 30 | } catch (IOException | InterruptedException e) { 31 | e.printStackTrace(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/org/scalasbt/ipcsocket/duplex/Sender.java: -------------------------------------------------------------------------------- 1 | package org.scalasbt.ipcsocket.duplex; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintWriter; 5 | import java.net.Socket; 6 | import java.util.Random; 7 | 8 | public class Sender implements Runnable { 9 | 10 | private final String name; 11 | private final Socket socket; 12 | private final int sendMessages; 13 | private final Random random = new Random(); 14 | 15 | public Sender(String name, Socket socket, int sendMessages) { 16 | this.name = name; 17 | this.socket = socket; 18 | this.sendMessages = sendMessages; 19 | } 20 | 21 | @Override 22 | public void run() { 23 | try (PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) { 24 | for (int i = 0; i < sendMessages; i++) { 25 | System.out.println("[" + name + "] sending msg: " + i); 26 | out.println("hello" + i); 27 | out.flush(); 28 | Thread.sleep(Math.abs(random.nextInt(1000))); 29 | } 30 | } catch (IOException | InterruptedException e) { 31 | e.printStackTrace(); 32 | } 33 | } 34 | } 35 | --------------------------------------------------------------------------------