Compare commits
3 Commits
linux-kotl
...
main
Author | SHA1 | Date | |
---|---|---|---|
f985f31c0a | |||
ea983cf990 | |||
3ec3a3a4e1 |
@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<configuration>
|
|
||||||
<root level="OFF" />
|
|
||||||
</configuration>
|
|
@ -16,6 +16,8 @@ plugins {
|
|||||||
kotlin("multiplatform") version Versions.kotlin
|
kotlin("multiplatform") version Versions.kotlin
|
||||||
kotlin("plugin.serialization") version Versions.kotlin
|
kotlin("plugin.serialization") version Versions.kotlin
|
||||||
id("app.cash.sqldelight") version Versions.sqlDelight
|
id("app.cash.sqldelight") version Versions.sqlDelight
|
||||||
|
`java-library`
|
||||||
|
id("org.bytedeco.gradle-javacpp-platform").version("1.5.10")
|
||||||
application
|
application
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,15 +75,12 @@ val buildVersionsTask by tasks.registering(Sync::class) {
|
|||||||
kotlin {
|
kotlin {
|
||||||
jvm {
|
jvm {
|
||||||
withJava()
|
withJava()
|
||||||
compilations["main"].defaultSourceSet {
|
|
||||||
resources.srcDirs("src/jvmMain/kotlin/fr/acinq/lightning/vsock/")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KotlinNativeTargetWithHostTests.phoenixBinaries() {
|
fun KotlinNativeTargetWithHostTests.phoenixBinaries() {
|
||||||
binaries {
|
binaries {
|
||||||
executable("phoenixd") {
|
executable("phoenixd") {
|
||||||
entryPoint = "fr.acinq.lightning.bin.main"
|
entryPoint = "fr.acinq.lightning.bin.MainKt"
|
||||||
optimized = false // without this, release mode throws 'Index 0 out of bounds for length 0' in StaticInitializersOptimization.kt
|
optimized = false // without this, release mode throws 'Index 0 out of bounds for length 0' in StaticInitializersOptimization.kt
|
||||||
}
|
}
|
||||||
/*executable("phoenix-cli") {
|
/*executable("phoenix-cli") {
|
||||||
@ -117,12 +116,12 @@ kotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation("com.github.raymond98.lightning-kmp:lightning-kmp:v1.6.2-FEECREDIT-8")
|
implementation("com.github.raymond98.lightning-kmp:lightning-kmp:v1.6.2-FEECREDIT-8")
|
||||||
|
|
||||||
implementation("org.jetbrains.kotlinx:atomicfu:0.25.0")
|
//implementation("org.bytedeco:javacpp:1.5.10")
|
||||||
|
//implementation("com.github.maven-nar:nar-maven-plugin:3.10.1")
|
||||||
|
|
||||||
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.8.20")
|
//api("fr.acinq.bitcoin:bitcoin-kmp:${Versions.bitcoinKmpVersion}")
|
||||||
api("fr.acinq.bitcoin:bitcoin-kmp:${Versions.bitcoinKmpVersion}")
|
//api("co.touchlab:kermit:${Versions.kermitLoggerVersion}")
|
||||||
api("co.touchlab:kermit:${Versions.kermitLoggerVersion}")
|
//api("org.jetbrains.kotlinx:kotlinx-datetime:${Versions.datetimeVersion}")
|
||||||
api("org.jetbrains.kotlinx:kotlinx-datetime:${Versions.datetimeVersion}")
|
|
||||||
api(ktor("network"))
|
api(ktor("network"))
|
||||||
api(ktor("network-tls"))
|
api(ktor("network-tls"))
|
||||||
|
|
||||||
@ -149,11 +148,9 @@ kotlin {
|
|||||||
jvmMain {
|
jvmMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("app.cash.sqldelight:sqlite-driver:${Versions.sqlDelight}")
|
implementation("app.cash.sqldelight:sqlite-driver:${Versions.sqlDelight}")
|
||||||
implementation("fr.acinq.secp256k1:secp256k1-kmp-jni-jvm:${Versions.secpJniJvmVersion}")
|
//implementation("fr.acinq.secp256k1:secp256k1-kmp-jni-jvm:${Versions.secpJniJvmVersion}")
|
||||||
implementation(ktor("client-okhttp"))
|
implementation(ktor("client-okhttp"))
|
||||||
implementation("ch.qos.logback:logback-classic:1.2.3")
|
implementation("ch.qos.logback:logback-classic:1.2.3")
|
||||||
implementation("org.bytedeco:javacpp:1.5.10")
|
|
||||||
implementation("org.bytedeco:javacpp-presets:1.5.10")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nativeMain {
|
nativeMain {
|
||||||
@ -187,10 +184,10 @@ kotlin {
|
|||||||
include("*.kexe")
|
include("*.kexe")
|
||||||
rename("phoenixd.kexe", "phoenixd")
|
rename("phoenixd.kexe", "phoenixd")
|
||||||
}
|
}
|
||||||
from("$projectDir/build/bin/$dir/phoenix-cliReleaseExecutable") {
|
/*from("$projectDir/build/bin/$dir/phoenix-cliReleaseExecutable") {
|
||||||
include("*.kexe")
|
include("*.kexe")
|
||||||
rename("phoenix-cli.kexe", "phoenix-cli")
|
rename("phoenix-cli.kexe", "phoenix-cli")
|
||||||
}
|
}*/
|
||||||
into("${archiveBaseName.get()}-${archiveVersion.get()}-${archiveClassifier.get()}")
|
into("${archiveBaseName.get()}-${archiveVersion.get()}-${archiveClassifier.get()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,11 +214,10 @@ application {
|
|||||||
mainClass = "fr.acinq.lightning.bin.MainKt"
|
mainClass = "fr.acinq.lightning.bin.MainKt"
|
||||||
|
|
||||||
// Set java.library.path to include the directory where the shared library is generated
|
// Set java.library.path to include the directory where the shared library is generated
|
||||||
applicationDefaultJvmArgs = listOf("-Djava.library.path=${layout.buildDirectory.dir("libs").get().asFile}")
|
applicationDefaultJvmArgs = listOf("-Djava.library.path=${layout.buildDirectory.dir("libs").get().asFile.absolutePath}")
|
||||||
applicationDefaultJvmArgs = listOf("-DLIBS_PATH=${layout.buildDirectory.dir("libs").get().asFile.absolutePath.replace("\\", "/")}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val cliScripts by tasks.register("cliScripts", CreateStartScripts::class) {
|
/*val cliScripts by tasks.register("cliScripts", CreateStartScripts::class) {
|
||||||
mainClass.set("fr.acinq.lightning.cli.PhoenixCliKt")
|
mainClass.set("fr.acinq.lightning.cli.PhoenixCliKt")
|
||||||
outputDir = tasks.startScripts.get().outputDir
|
outputDir = tasks.startScripts.get().outputDir
|
||||||
classpath = tasks.startScripts.get().classpath
|
classpath = tasks.startScripts.get().classpath
|
||||||
@ -230,27 +226,11 @@ val cliScripts by tasks.register("cliScripts", CreateStartScripts::class) {
|
|||||||
|
|
||||||
tasks.startScripts {
|
tasks.startScripts {
|
||||||
dependsOn(cliScripts)
|
dependsOn(cliScripts)
|
||||||
}
|
}*/
|
||||||
|
|
||||||
val setLibPath by tasks.register<Exec>("runLinuxExecutable") {
|
|
||||||
val libsDir = layout.buildDirectory.dir("libs").get().asFile
|
|
||||||
val linuxLibsPath = libsDir.absolutePath.replace("\\", "/")
|
|
||||||
|
|
||||||
// Set the environment variable
|
|
||||||
environment("LIBS_PATH", linuxLibsPath)
|
|
||||||
|
|
||||||
// Mark the task as always out-of-date
|
|
||||||
outputs.upToDateWhen { false }
|
|
||||||
|
|
||||||
// Execute the command
|
|
||||||
commandLine("sh", "-c", "LIBS_PATH=$linuxLibsPath; export LIBS_PATH=$linuxLibsPath")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val compileNative by tasks.register<Exec>("compileNative") {
|
val compileNative by tasks.register<Exec>("compileNative") {
|
||||||
group = "build"
|
group = "build"
|
||||||
description = "Compile the native C++ code into a shared library"
|
description = "Compile the native C++ code into a shared library and package it into a .nar file"
|
||||||
|
|
||||||
val outputDir = layout.buildDirectory.dir("libs").get().asFile
|
val outputDir = layout.buildDirectory.dir("libs").get().asFile
|
||||||
val nativeSourceDir = file("src/commonMain/kotlin/fr/acinq/lightning/vsock/native")
|
val nativeSourceDir = file("src/commonMain/kotlin/fr/acinq/lightning/vsock/native")
|
||||||
@ -263,17 +243,18 @@ val compileNative by tasks.register<Exec>("compileNative") {
|
|||||||
inputs.dir(nativeSourceDir)
|
inputs.dir(nativeSourceDir)
|
||||||
outputs.dir(outputDir)
|
outputs.dir(outputDir)
|
||||||
|
|
||||||
|
// Compile the shared library
|
||||||
commandLine("g++", "-I$jniIncludeDir", "-I$jniPlatformIncludeDir", "-shared", "-o", outputDir.resolve("libjniVSockImpl.so"), nativeSourceDir.resolve("VSockImpl.cpp"), "-fPIC")
|
commandLine("g++", "-I$jniIncludeDir", "-I$jniPlatformIncludeDir", "-shared", "-o", outputDir.resolve("libjniVSockImpl.so"), nativeSourceDir.resolve("VSockImpl.cpp"), "-fPIC")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Ensure the native library is compiled before creating the distribution
|
||||||
tasks.withType<Tar> {
|
tasks.withType<Tar> {
|
||||||
dependsOn(compileNative)
|
dependsOn(compileNative)
|
||||||
dependsOn(setLibPath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<Zip> {
|
tasks.withType<Zip> {
|
||||||
dependsOn(compileNative)
|
dependsOn(compileNative)
|
||||||
dependsOn(setLibPath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
distributions {
|
distributions {
|
||||||
@ -286,6 +267,7 @@ distributions {
|
|||||||
// forward std input when app is run via gradle (otherwise keyboard input will return EOF)
|
// forward std input when app is run via gradle (otherwise keyboard input will return EOF)
|
||||||
tasks.withType<JavaExec> {
|
tasks.withType<JavaExec> {
|
||||||
standardInput = System.`in`
|
standardInput = System.`in`
|
||||||
|
dependsOn(compileNative) //This should not be the case for all platforms
|
||||||
}
|
}
|
||||||
|
|
||||||
sqldelight {
|
sqldelight {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
object Versions {
|
object Versions {
|
||||||
val kotlin = "1.9.23"
|
val kotlin = "1.9.23"
|
||||||
val lightningKmp = "1.7.0-FEECREDIT-8"
|
val lightningKmp = "1.7.0-FEECREDIT-8"
|
||||||
|
val lightningKmpTag = "v1.6.2-FEECREDIT-8"
|
||||||
val sqlDelight = "2.0.1"
|
val sqlDelight = "2.0.1"
|
||||||
val okio = "3.8.0"
|
val okio = "3.8.0"
|
||||||
val clikt = "4.2.2"
|
val clikt = "4.2.2"
|
||||||
|
@ -372,7 +372,7 @@ class Api(private val nodeParams: NodeParams, private val peer: Peer, private va
|
|||||||
else -> call.respondText("Splice-in failed: unexpected response type", status = HttpStatusCode.InternalServerError)
|
else -> call.respondText("Splice-in failed: unexpected response type", status = HttpStatusCode.InternalServerError)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
call.respond(HttpStatusCode.InternalServerError, "Failed to process splice-in: ${e.message}")
|
call.respond(HttpStatusCode.InternalServerError, "Failed to process splice-in: ${e.localizedMessage}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
post("closechannel") {
|
post("closechannel") {
|
||||||
|
@ -133,8 +133,8 @@ class Phoenixd : CliktCommand() {
|
|||||||
private val electrumServerPort by option("--electrum-server-port", help = "Port for the electrum server").int().default(50002)
|
private val electrumServerPort by option("--electrum-server-port", help = "Port for the electrum server").int().default(50002)
|
||||||
|
|
||||||
private val startVsock by option("--start-vsock-server", help = "Start the vsock server for API calls").boolean().default(true)
|
private val startVsock by option("--start-vsock-server", help = "Start the vsock server for API calls").boolean().default(true)
|
||||||
private val vsockCID by option("--vsock-server-cid", help = "CID for the Vsock server").int().default(4)
|
private val vsockCID by option("--vsock-server-cid", help = "CID for the Vsock server").int().default(4) //enclave cid is 4 i think
|
||||||
private val vsockPort by option("--vsock-server-port", help = "Port for the Vsock server").int().default(9001)
|
private val vsockPort by option("--vsock-server-port", help = "Port for the Vsock server").int().default(9002)
|
||||||
|
|
||||||
class LiquidityOptions : OptionGroup(name = "Liquidity Options") {
|
class LiquidityOptions : OptionGroup(name = "Liquidity Options") {
|
||||||
val autoLiquidity by option("--auto-liquidity", help = "Amount automatically requested when inbound liquidity is needed").choice(
|
val autoLiquidity by option("--auto-liquidity", help = "Amount automatically requested when inbound liquidity is needed").choice(
|
||||||
@ -393,9 +393,12 @@ class Phoenixd : CliktCommand() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var vsockServer: VsockServer? = null
|
var vsockServer: VsockServer? = null
|
||||||
if(startVsock){
|
if (startVsock) {
|
||||||
vsockServer = VsockServer(vsockCID, vsockPort, httpBindPort, httpBindIp, loggerFactory)
|
vsockServer = VsockServer(vsockCID, vsockPort, httpBindPort, httpBindIp, loggerFactory)
|
||||||
vsockServer.start()
|
GlobalScope.launch {
|
||||||
|
consoleLog(yellow("Vsock Server Binding to Port: $vsockPort"))
|
||||||
|
vsockServer.start()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val server = embeddedServer(CIO, port = httpBindPort, host = httpBindIp,
|
val server = embeddedServer(CIO, port = httpBindPort, host = httpBindIp,
|
||||||
|
@ -1,64 +1,67 @@
|
|||||||
package fr.acinq.lightning.vsock
|
package fr.acinq.lightning.vsock
|
||||||
|
|
||||||
import fr.acinq.lightning.logging.LoggerFactory
|
|
||||||
import fr.acinq.lightning.logging.error
|
|
||||||
import fr.acinq.lightning.logging.warning
|
|
||||||
import fr.acinq.lightning.vsock.native.VSockImpl
|
import fr.acinq.lightning.vsock.native.VSockImpl
|
||||||
import kotlinx.atomicfu.locks.SynchronizedObject
|
import java.io.Closeable
|
||||||
import kotlinx.atomicfu.locks.synchronized
|
import java.io.IOException
|
||||||
import kotlin.jvm.Synchronized
|
import java.net.SocketException
|
||||||
|
|
||||||
abstract class BaseVSock(loggerfactory: LoggerFactory) : Closeable {
|
abstract class BaseVSock : Closeable {
|
||||||
private val closeLock = SynchronizedObject()
|
protected val closeLock: Any = Any()
|
||||||
val logger = loggerfactory.newLogger(this::class)
|
|
||||||
|
|
||||||
protected var isClosed: Boolean = false
|
protected var isClosed: Boolean = false
|
||||||
protected var created: Boolean = false
|
protected var created: Boolean = false
|
||||||
protected var bound: Boolean = false
|
protected var bound: Boolean = false
|
||||||
private lateinit var implementation: VSockImpl
|
private var implementation: VSockImpl? = null
|
||||||
|
|
||||||
@Throws(SocketException::class)
|
@Throws(SocketException::class)
|
||||||
private fun createImplementation() {
|
private fun createImplementation() {
|
||||||
implementation = VSockImpl()
|
implementation = VSockImpl()
|
||||||
implementation.create()
|
implementation!!.create()
|
||||||
created = true
|
created = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(SocketException::class)
|
@Throws(SocketException::class)
|
||||||
fun getImplementation(): VSockImpl {
|
fun getImplementation(): VSockImpl? {
|
||||||
if (!created) createImplementation()
|
if (!created) createImplementation()
|
||||||
return implementation
|
return implementation
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(SocketException::class)
|
@Throws(SocketException::class)
|
||||||
fun setImplementation(): VSockImpl {
|
fun setImplementation(): VSockImpl {
|
||||||
return implementation
|
if (implementation == null) {
|
||||||
|
implementation = VSockImpl()
|
||||||
|
}
|
||||||
|
return implementation!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@get:Throws(IOException::class)
|
||||||
|
val localCid: Int
|
||||||
|
get() = getImplementation()!!.getLocalCid()
|
||||||
|
|
||||||
|
@JvmOverloads
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun bind(address: VSockAddress?, backlog: Int = DEFAULT_BACKLOG) {
|
fun bind(address: VSockAddress?, backlog: Int = DEFAULT_BACKLOG) {
|
||||||
var backlogs = backlog
|
var backlog = backlog
|
||||||
if (isClosed) {
|
if (isClosed) {
|
||||||
logger.warning { "Socket closed" }
|
throw SocketException("Socket closed thrown in Base Vsock")
|
||||||
}
|
}
|
||||||
if (bound) {
|
if (bound) {
|
||||||
logger.warning { "Socket already bound" }
|
throw SocketException("Socket already bound")
|
||||||
}
|
}
|
||||||
if (backlogs <= 0) {
|
if (backlog <= 0) {
|
||||||
backlogs = DEFAULT_BACKLOG
|
backlog = DEFAULT_BACKLOG
|
||||||
}
|
}
|
||||||
if (address != null) {
|
getImplementation()!!.bind(address)
|
||||||
getImplementation().bind(address)
|
getImplementation()!!.listen(backlog)
|
||||||
}
|
|
||||||
getImplementation().listen(backlogs)
|
|
||||||
bound = true
|
bound = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
|
@Throws(IOException::class)
|
||||||
override fun close() {
|
override fun close() {
|
||||||
synchronized(closeLock) {
|
synchronized(closeLock) {
|
||||||
if (isClosed) return
|
if (isClosed) return
|
||||||
if (created) getImplementation().close()
|
if (created) getImplementation()!!.close()
|
||||||
isClosed = true
|
isClosed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,9 +70,3 @@ abstract class BaseVSock(loggerfactory: LoggerFactory) : Closeable {
|
|||||||
private const val DEFAULT_BACKLOG = 42
|
private const val DEFAULT_BACKLOG = 42
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom Closeable interface for Kotlin/Native
|
|
||||||
interface Closeable {
|
|
||||||
fun close()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
package fr.acinq.lightning.vsock
|
package fr.acinq.lightning.vsock
|
||||||
|
|
||||||
import fr.acinq.lightning.logging.LoggerFactory
|
import java.io.IOException
|
||||||
import fr.acinq.lightning.logging.debug
|
import java.net.SocketException
|
||||||
|
|
||||||
class ServerVSock(private val loggerfactory: LoggerFactory) : BaseVSock(loggerfactory) {
|
class ServerVSock : BaseVSock() {
|
||||||
|
@Throws(IOException::class)
|
||||||
@Throws(SocketException::class)
|
|
||||||
fun accept(): VSock {
|
fun accept(): VSock {
|
||||||
if (isClosed) logger.debug { "Socket closed" }
|
if (isClosed) {
|
||||||
if (!bound) logger.debug { "Socket not bound"}
|
throw SocketException("Socket closed")
|
||||||
|
}
|
||||||
val socket = VSock(logger = loggerfactory)
|
if (!bound) throw SocketException("Socket not bound")
|
||||||
|
val socket = VSock()
|
||||||
socket.setImplementation()
|
socket.setImplementation()
|
||||||
socket.getImplementation().let { getImplementation().accept(it) }
|
socket.getImplementation()?.let { getImplementation()!!.accept(it) }
|
||||||
socket.postAccept()
|
socket.postAccept()
|
||||||
|
|
||||||
return socket
|
return socket
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,81 +1,73 @@
|
|||||||
package fr.acinq.lightning.vsock
|
package fr.acinq.lightning.vsock
|
||||||
|
|
||||||
import fr.acinq.lightning.logging.LoggerFactory
|
import java.io.Closeable
|
||||||
import kotlin.jvm.Synchronized
|
import java.io.IOException
|
||||||
|
import java.net.SocketException
|
||||||
class VSock(address: VSockAddress? = null, logger: LoggerFactory) : BaseVSock(logger) {
|
|
||||||
private val loggerInstance = logger.newLogger(this::class)
|
|
||||||
|
|
||||||
|
class VSock : BaseVSock, Closeable {
|
||||||
private var connected = false
|
private var connected = false
|
||||||
|
|
||||||
|
@get:Throws(IOException::class)
|
||||||
|
@get:Synchronized
|
||||||
var outputStream: VSockOutputStream? = null
|
var outputStream: VSockOutputStream? = null
|
||||||
private set
|
get() {
|
||||||
|
if (isClosed) {
|
||||||
var inputStream: VSockInputStream? = null
|
throw SocketException("VSock is closed thrown in Vsock")
|
||||||
private set
|
|
||||||
|
|
||||||
init {
|
|
||||||
if (address != null) {
|
|
||||||
try {
|
|
||||||
getImplementation().connect(address)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
try {
|
|
||||||
close()
|
|
||||||
} catch (ce: Exception) {
|
|
||||||
e.addSuppressed(ce)
|
|
||||||
}
|
|
||||||
throw IllegalStateException(e.message, e)
|
|
||||||
}
|
}
|
||||||
|
if (field == null) {
|
||||||
|
field = getImplementation()?.let { VSockOutputStream(it) }
|
||||||
|
}
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
private set
|
||||||
|
|
||||||
|
@get:Throws(IOException::class)
|
||||||
|
@get:Synchronized
|
||||||
|
var inputStream: VSockInputStream? = null
|
||||||
|
get() {
|
||||||
|
if (isClosed) {
|
||||||
|
getImplementation()!!.create()
|
||||||
|
isClosed = false;
|
||||||
|
//throw SocketException("VSock is closed thrown in Vsock")
|
||||||
|
}
|
||||||
|
if (field == null) {
|
||||||
|
field = getImplementation()?.let { VSockInputStream(it) }
|
||||||
|
}
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
private set
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
|
||||||
|
constructor(address: VSockAddress?) {
|
||||||
|
try {
|
||||||
|
getImplementation()!!.connect(address)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
try {
|
||||||
|
close()
|
||||||
|
} catch (ce: Exception) {
|
||||||
|
e.addSuppressed(ce)
|
||||||
|
}
|
||||||
|
throw IllegalStateException(e.message, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(SocketException::class)
|
@Throws(SocketException::class)
|
||||||
fun connect(address: VSockAddress) {
|
fun connect(address: VSockAddress?) {
|
||||||
if (isClosed) {
|
if (isClosed) {
|
||||||
throw SocketException("Socket closed")
|
throw SocketException("Socket closed")
|
||||||
}
|
}
|
||||||
if (connected) {
|
if (connected) {
|
||||||
throw SocketException("Socket already connected")
|
throw SocketException("Socket already connected")
|
||||||
}
|
}
|
||||||
getImplementation().connect(address)
|
getImplementation()!!.connect(address)
|
||||||
connected = true
|
connected = true
|
||||||
bound = true
|
bound = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
@Throws(IOException::class)
|
|
||||||
fun initializeOutputStream(): VSockOutputStream? {
|
|
||||||
if (isClosed) {
|
|
||||||
throw SocketException("VSock is closed")
|
|
||||||
}
|
|
||||||
if (outputStream == null) {
|
|
||||||
outputStream = VSockOutputStream(getImplementation())
|
|
||||||
}
|
|
||||||
return outputStream
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
@Throws(IOException::class)
|
|
||||||
fun initializeInputStream(): VSockInputStream? {
|
|
||||||
if (isClosed) {
|
|
||||||
throw SocketException("VSock is closed")
|
|
||||||
}
|
|
||||||
if (inputStream == null) {
|
|
||||||
inputStream = VSockInputStream(getImplementation())
|
|
||||||
}
|
|
||||||
return inputStream
|
|
||||||
}
|
|
||||||
|
|
||||||
fun postAccept() {
|
fun postAccept() {
|
||||||
created = true
|
created = true
|
||||||
bound = true
|
bound = true
|
||||||
connected = true
|
connected = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom exception classes for Kotlin/Native
|
|
||||||
class SocketException(message: String) : Exception(message)
|
|
||||||
class IOException(message: String) : Exception(message)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,36 +1,35 @@
|
|||||||
package fr.acinq.lightning.vsock
|
package fr.acinq.lightning.vsock
|
||||||
|
|
||||||
class VSockAddress(val cid: Int, val port: Int) {
|
import java.net.SocketAddress
|
||||||
|
import java.util.Objects
|
||||||
|
|
||||||
companion object {
|
class VSockAddress(val cid: Int, val port: Int) : SocketAddress() {
|
||||||
const val VMADDR_CID_ANY = -1
|
override fun equals(o: Any?): Boolean {
|
||||||
const val VMADDR_CID_HYPERVISOR = 0
|
if (this === o) return true
|
||||||
const val VMADDR_CID_RESERVED = 1
|
if (o == null || javaClass != o.javaClass) return false
|
||||||
const val VMADDR_CID_HOST = 2
|
val that = o as VSockAddress
|
||||||
const val VMADDR_CID_PARENT = 3
|
return cid == that.cid &&
|
||||||
|
port == that.port
|
||||||
const val VMADDR_PORT_ANY = -1
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (other == null || this::class != other::class) return false
|
|
||||||
|
|
||||||
other as VSockAddress
|
|
||||||
|
|
||||||
if (cid != other.cid) return false
|
|
||||||
if (port != other.port) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = cid
|
return Objects.hash(cid, port)
|
||||||
result = 31 * result + port
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "VSockAddress(cid=$cid, port=$port)"
|
return "VSockAddress{" +
|
||||||
|
"cid=" + cid +
|
||||||
|
", port=" + port +
|
||||||
|
'}'
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val VMADDR_CID_ANY: Int = -1
|
||||||
|
const val VMADDR_CID_HYPERVISOR: Int = 0
|
||||||
|
const val VMADDR_CID_RESERVED: Int = 1
|
||||||
|
const val VMADDR_CID_HOST: Int = 2
|
||||||
|
const val VMADDR_CID_PARENT: Int = 3
|
||||||
|
|
||||||
|
const val VMADDR_PORT_ANY: Int = -1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,30 @@
|
|||||||
package fr.acinq.lightning.vsock
|
package fr.acinq.lightning.vsock
|
||||||
|
|
||||||
import fr.acinq.lightning.vsock.native.VSockImpl
|
import fr.acinq.lightning.vsock.native.VSockImpl
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
|
||||||
class VSockInputStream(private val vSock: VSockImpl) : Closeable {
|
class VSockInputStream(private val vSock: VSockImpl) : InputStream() {
|
||||||
|
private lateinit var temp: ByteArray
|
||||||
private var temp: ByteArray? = null
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun read(b: ByteArray, off: Int, len: Int): Int {
|
override fun read(b: ByteArray, off: Int, len: Int): Int {
|
||||||
return vSock.read(b, off, len)
|
return vSock.read(b, off, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun read(): Int {
|
override fun read(): Int {
|
||||||
temp = ByteArray(1)
|
temp = ByteArray(1)
|
||||||
val n = read(temp!!, 0, 1)
|
val n = read(temp, 0, 1)
|
||||||
return if (n <= 0) {
|
if (n <= 0) {
|
||||||
-1
|
return -1
|
||||||
} else {
|
|
||||||
temp!![0].toInt()
|
|
||||||
}
|
}
|
||||||
|
return temp[0].toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
override fun close() {
|
override fun close() {
|
||||||
vSock.close()
|
vSock.close()
|
||||||
|
super.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
package fr.acinq.lightning.vsock
|
|
||||||
|
|
||||||
import fr.acinq.lightning.vsock.native.VSockImpl
|
|
||||||
|
|
||||||
expect object VSockImplLib {
|
|
||||||
|
|
||||||
fun loadLibrary()
|
|
||||||
|
|
||||||
fun socketCreate()
|
|
||||||
|
|
||||||
fun connect(address: VSockAddress)
|
|
||||||
|
|
||||||
fun close()
|
|
||||||
|
|
||||||
fun write(b: ByteArray, off: Int, len: Int)
|
|
||||||
|
|
||||||
fun read(b: ByteArray, off: Int, len: Int): Int
|
|
||||||
|
|
||||||
fun bind(address: VSockAddress)
|
|
||||||
|
|
||||||
fun listen(backlog: Int)
|
|
||||||
|
|
||||||
fun accept(peerVSock: VSockImplLib)
|
|
||||||
}
|
|
@ -1,23 +1,26 @@
|
|||||||
package fr.acinq.lightning.vsock
|
package fr.acinq.lightning.vsock
|
||||||
|
|
||||||
import fr.acinq.lightning.vsock.native.VSockImpl
|
import fr.acinq.lightning.vsock.native.VSockImpl
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
class VSockOutputStream(private val vSock: VSockImpl) : Closeable {
|
class VSockOutputStream internal constructor(private val vSock: VSockImpl) : OutputStream() {
|
||||||
|
|
||||||
private val temp = ByteArray(1)
|
private val temp = ByteArray(1)
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun write(b: ByteArray) {
|
override fun write(b: Int) {
|
||||||
temp[0] = b[0]
|
temp[0] = b.toByte()
|
||||||
this.write(temp, 0, 1)
|
this.write(temp, 0, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun write(b: ByteArray, off: Int, len: Int) {
|
override fun write(b: ByteArray, off: Int, len: Int) {
|
||||||
vSock.write(b, off, len)
|
vSock.write(b, off, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
override fun close() {
|
override fun close() {
|
||||||
vSock.close()
|
vSock.close()
|
||||||
|
super.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,80 +1,75 @@
|
|||||||
package fr.acinq.lightning.vsock
|
package fr.acinq.lightning.vsock
|
||||||
|
|
||||||
import fr.acinq.lightning.bin.json.ApiType
|
import fr.acinq.lightning.bin.json.ApiType.*
|
||||||
import fr.acinq.lightning.logging.LoggerFactory
|
import fr.acinq.lightning.logging.LoggerFactory
|
||||||
import fr.acinq.lightning.logging.debug
|
import fr.acinq.lightning.logging.info
|
||||||
import fr.acinq.lightning.logging.error
|
import fr.acinq.lightning.logging.error
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
import io.ktor.client.statement.*
|
import io.ktor.client.statement.*
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.utils.io.charsets.Charsets
|
|
||||||
import io.ktor.utils.io.core.toByteArray
|
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlin.io.encoding.Base64
|
import java.io.IOException
|
||||||
import kotlin.io.encoding.ExperimentalEncodingApi
|
import java.nio.charset.StandardCharsets
|
||||||
|
import java.util.Base64
|
||||||
|
|
||||||
class VsockServer(private val CID: Int, private val port: Int, httpBindPort: Int, host: String,
|
class VsockServer(private val cid: Int, private val port: Int, httpBindPort: Int, host: String, loggerFactory: LoggerFactory) {
|
||||||
private val loggerFactory: LoggerFactory
|
|
||||||
) {
|
|
||||||
private var server: ServerVSock? = null
|
private var server: ServerVSock? = null
|
||||||
private val logger = loggerFactory.newLogger(this::class)
|
private val logger = loggerFactory.newLogger(this::class)
|
||||||
private val client = HttpClient()
|
private val client = HttpClient()
|
||||||
private val apiBaseUrl: String = "$host:$httpBindPort"
|
private val apiBaseUrl: String = "${host}:${httpBindPort}"
|
||||||
private val bufferSize: Int = 4096
|
private val bufferSize: Int = 4096
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
fun start() {
|
fun start() {
|
||||||
server = ServerVSock(loggerFactory)
|
server = ServerVSock()
|
||||||
var peerVSock: VSock? = null
|
|
||||||
try {
|
try {
|
||||||
server?.bind(VSockAddress(CID, port)) // For any CID, use VSockAddress.VMADDR_CID_ANY
|
server?.bind(VSockAddress(VSockAddress.VMADDR_CID_ANY, port)) //For any CID use VSockAddress.VMADDR_CID_ANY
|
||||||
logger.debug { "Vsock Bound on CID: ${CID}" }
|
logger.info { "Vsock Bound on Cid: ${server?.localCid}" }
|
||||||
|
|
||||||
peerVSock = server?.accept()
|
server?.accept()?.use { peerVSock ->
|
||||||
|
|
||||||
if (peerVSock != null) {
|
|
||||||
logger.debug { "Vsock start did do it" }
|
|
||||||
val buffer = ByteArray(bufferSize)
|
val buffer = ByteArray(bufferSize)
|
||||||
val bytesRead = peerVSock.inputStream?.read(buffer, 0, bufferSize)
|
val bytesRead = peerVSock.inputStream?.read(buffer, 0, bufferSize)
|
||||||
if (bytesRead != null && bytesRead > 0) {
|
if (bytesRead != null) {
|
||||||
val receivedData = buffer.decodeToString(0, bytesRead).trim()
|
if (bytesRead > 0) {
|
||||||
logger.debug { "Received Data: $receivedData" }
|
val receivedData = String(buffer, 0, bytesRead, StandardCharsets.UTF_8).trim()
|
||||||
|
logger.info { "Received Data: $receivedData" }
|
||||||
|
|
||||||
// Parse the received data into an API request
|
// Parse the received data into a http request
|
||||||
val apiRequest = try {
|
val apiRequest = try {
|
||||||
Json.decodeFromString<ApiType.VsockApiRequest>(receivedData)
|
Json.decodeFromString<VsockApiRequest>(receivedData)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error { "Failed to parse JSON: ${e.message}" }
|
logger.error { "Failed to parse JSON: ${e.message}" }
|
||||||
peerVSock.outputStream?.write("Invalid JSON format".toByteArray(Charsets.UTF_8))
|
val errorMessage = "{\"error\":\"Invalid JSON format\", \"received\":\"${receivedData}\"}"
|
||||||
return
|
peerVSock.outputStream?.write(errorMessage.toByteArray(StandardCharsets.UTF_8))
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Handle the API call
|
// Handle the API call
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
val response = handleApiCall(apiRequest)
|
val response = handleApiCall(apiRequest)
|
||||||
peerVSock.outputStream?.write(response.toByteArray(Charsets.UTF_8))
|
peerVSock.outputStream?.write(response.toByteArray(StandardCharsets.UTF_8))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (ex: IOException) {
|
} catch (ex: IOException) {
|
||||||
logger.error { "Error starting Vsock: ${ex.message}" }
|
logger.error { "Error starting Vsock: ${ex.message}" }
|
||||||
} finally {
|
|
||||||
peerVSock?.close()
|
|
||||||
stop()
|
|
||||||
}
|
}
|
||||||
|
/*finally { // We have to keep the server running
|
||||||
|
stop()
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalEncodingApi::class)
|
private suspend fun handleApiCall(request: VsockApiRequest): String {
|
||||||
private suspend fun handleApiCall(request: ApiType.VsockApiRequest): String {
|
|
||||||
return try {
|
return try {
|
||||||
val url = "$apiBaseUrl/${request.method}"
|
val url = "$apiBaseUrl/${request.method}"
|
||||||
val response: HttpResponse = client.get(url) {
|
val response: HttpResponse = client.get(url) {
|
||||||
headers {
|
headers {
|
||||||
append(HttpHeaders.Authorization, "Basic ${Base64.encode("user:${request.httpPassword}".toByteArray(Charsets.UTF_8))}")
|
append(HttpHeaders.Authorization, "Basic ${Base64.getEncoder().encodeToString("user:${request.httpPassword}".toByteArray())}")
|
||||||
}
|
}
|
||||||
url {
|
url {
|
||||||
parameters.appendAll(Parameters.build {
|
parameters.appendAll(Parameters.build {
|
||||||
@ -97,7 +92,7 @@ class VsockServer(private val CID: Int, private val port: Int, httpBindPort: Int
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun stop() {
|
fun stop() {
|
||||||
logger.debug { "Stopping Vsock Server" }
|
logger.info { "Stopping Vsock Server" }
|
||||||
server?.close()
|
server?.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,20 +12,28 @@
|
|||||||
#define BUFFER_LEN 65536
|
#define BUFFER_LEN 65536
|
||||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
|
||||||
// Make sure to wrap all your JNI functions with extern "C" to avoid name mangling
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
// Native method implementations matching the JNI header
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_fr_acinq_lightning_vsock_native_VSockImpl_socketCreate(JNIEnv *env, jobject thisObj) {
|
Java_fr_acinq_lightning_vsock_native_VSockImpl_socketCreate(JNIEnv *env, jobject thisObj) {
|
||||||
int fd = socket(AF_VSOCK, SOCK_STREAM, 0);
|
int fd = socket(AF_VSOCK, SOCK_STREAM, 0);
|
||||||
// Optionally store the socket descriptor in the Java object's field
|
if (fd < 0) {
|
||||||
|
env->ThrowNew(env->FindClass("java/net/SocketException"), "Failed to create socket");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the socket descriptor in the Java object's field
|
||||||
|
jclass thisClass = env->GetObjectClass(thisObj);
|
||||||
|
jfieldID fdField = env->GetFieldID(thisClass, "fd", "I");
|
||||||
|
env->SetIntField(thisObj, fdField, fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_fr_acinq_lightning_vsock_native_VSockImpl_connect(JNIEnv *env, jobject thisObj, jobject addr) {
|
Java_fr_acinq_lightning_vsock_native_VSockImpl_connect(JNIEnv *env, jobject thisObj, jobject addr) {
|
||||||
int fd = -1; // Assuming you have stored the fd somewhere accessible
|
jclass thisClass = env->GetObjectClass(thisObj);
|
||||||
|
jfieldID fdField = env->GetFieldID(thisClass, "fd", "I");
|
||||||
|
int fd = env->GetIntField(thisObj, fdField);
|
||||||
|
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed");
|
env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed");
|
||||||
return;
|
return;
|
||||||
@ -41,6 +49,7 @@ std::memset(&sock_addr, 0, sizeof(struct sockaddr_vm));
|
|||||||
sock_addr.svm_family = AF_VSOCK;
|
sock_addr.svm_family = AF_VSOCK;
|
||||||
sock_addr.svm_port = env->GetIntField(addr, portField);
|
sock_addr.svm_port = env->GetIntField(addr, portField);
|
||||||
sock_addr.svm_cid = env->GetIntField(addr, cidField);
|
sock_addr.svm_cid = env->GetIntField(addr, cidField);
|
||||||
|
|
||||||
int status = ::connect(fd, (struct sockaddr *)&sock_addr, sizeof(struct sockaddr_vm));
|
int status = ::connect(fd, (struct sockaddr *)&sock_addr, sizeof(struct sockaddr_vm));
|
||||||
|
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
@ -51,23 +60,30 @@ env->ThrowNew(env->FindClass("java/net/ConnectException"),
|
|||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_fr_acinq_lightning_vsock_native_VSockImpl_close(JNIEnv *env, jobject thisObj) {
|
Java_fr_acinq_lightning_vsock_native_VSockImpl_close(JNIEnv *env, jobject thisObj) {
|
||||||
int fd = -1; // Assuming you have stored the fd somewhere accessible
|
jclass thisClass = env->GetObjectClass(thisObj);
|
||||||
|
jfieldID fdField = env->GetFieldID(thisClass, "fd", "I");
|
||||||
|
int fd = env->GetIntField(thisObj, fdField);
|
||||||
|
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
return;
|
return; // Socket is already closed
|
||||||
}
|
}
|
||||||
|
|
||||||
int status = ::close(fd);
|
int status = ::close(fd);
|
||||||
fd = -1;
|
|
||||||
|
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
env->ThrowNew(env->FindClass("java/net/SocketException"),
|
env->ThrowNew(env->FindClass("java/net/SocketException"),
|
||||||
("Close failed with error no: " + std::to_string(errno)).c_str());
|
("Close failed with error no: " + std::to_string(errno)).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark the socket as closed
|
||||||
|
env->SetIntField(thisObj, fdField, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_fr_acinq_lightning_vsock_native_VSockImpl_write(JNIEnv *env, jobject thisObj, jbyteArray b, jint offset, jint len) {
|
Java_fr_acinq_lightning_vsock_native_VSockImpl_write(JNIEnv *env, jobject thisObj, jbyteArray b, jint offset, jint len) {
|
||||||
int fd = -1; // Assuming you have stored the fd somewhere accessible
|
jclass thisClass = env->GetObjectClass(thisObj);
|
||||||
|
jfieldID fdField = env->GetFieldID(thisClass, "fd", "I");
|
||||||
|
int fd = env->GetIntField(thisObj, fdField);
|
||||||
|
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed");
|
env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed");
|
||||||
return;
|
return;
|
||||||
@ -91,7 +107,10 @@ offset += chunkLen;
|
|||||||
|
|
||||||
JNIEXPORT jint JNICALL
|
JNIEXPORT jint JNICALL
|
||||||
Java_fr_acinq_lightning_vsock_native_VSockImpl_read(JNIEnv *env, jobject thisObj, jbyteArray b, jint offset, jint len) {
|
Java_fr_acinq_lightning_vsock_native_VSockImpl_read(JNIEnv *env, jobject thisObj, jbyteArray b, jint offset, jint len) {
|
||||||
int fd = -1; // Assuming you have stored the fd somewhere accessible
|
jclass thisClass = env->GetObjectClass(thisObj);
|
||||||
|
jfieldID fdField = env->GetFieldID(thisClass, "fd", "I");
|
||||||
|
int fd = env->GetIntField(thisObj, fdField);
|
||||||
|
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed");
|
env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed");
|
||||||
return -1;
|
return -1;
|
||||||
@ -114,7 +133,10 @@ return nread;
|
|||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_fr_acinq_lightning_vsock_native_VSockImpl_bind(JNIEnv *env, jobject thisObj, jobject addr) {
|
Java_fr_acinq_lightning_vsock_native_VSockImpl_bind(JNIEnv *env, jobject thisObj, jobject addr) {
|
||||||
int fd = -1; // Assuming you have stored the fd somewhere accessible
|
jclass thisClass = env->GetObjectClass(thisObj);
|
||||||
|
jfieldID fdField = env->GetFieldID(thisClass, "fd", "I");
|
||||||
|
int fd = env->GetIntField(thisObj, fdField);
|
||||||
|
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed");
|
env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed");
|
||||||
return;
|
return;
|
||||||
@ -141,7 +163,10 @@ env->ThrowNew(env->FindClass("java/net/BindException"),
|
|||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_fr_acinq_lightning_vsock_native_VSockImpl_listen(JNIEnv *env, jobject thisObj, jint backlog) {
|
Java_fr_acinq_lightning_vsock_native_VSockImpl_listen(JNIEnv *env, jobject thisObj, jint backlog) {
|
||||||
int fd = -1; // Assuming you have stored the fd somewhere accessible
|
jclass thisClass = env->GetObjectClass(thisObj);
|
||||||
|
jfieldID fdField = env->GetFieldID(thisClass, "fd", "I");
|
||||||
|
int fd = env->GetIntField(thisObj, fdField);
|
||||||
|
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed");
|
env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed");
|
||||||
return;
|
return;
|
||||||
@ -157,7 +182,10 @@ env->ThrowNew(env->FindClass("java/net/SocketException"),
|
|||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_fr_acinq_lightning_vsock_native_VSockImpl_accept(JNIEnv *env, jobject thisObj, jobject connectionVSock) {
|
Java_fr_acinq_lightning_vsock_native_VSockImpl_accept(JNIEnv *env, jobject thisObj, jobject connectionVSock) {
|
||||||
int fd = -1; // Assuming you have stored the fd somewhere accessible
|
jclass thisClass = env->GetObjectClass(thisObj);
|
||||||
|
jfieldID fdField = env->GetFieldID(thisClass, "fd", "I");
|
||||||
|
int fd = env->GetIntField(thisObj, fdField);
|
||||||
|
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed");
|
env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed");
|
||||||
return;
|
return;
|
||||||
@ -175,13 +203,16 @@ return;
|
|||||||
|
|
||||||
// Set the peer_fd in the Java connectionVSock object
|
// Set the peer_fd in the Java connectionVSock object
|
||||||
jclass VSockImplClass = env->GetObjectClass(connectionVSock);
|
jclass VSockImplClass = env->GetObjectClass(connectionVSock);
|
||||||
jfieldID fdField = env->GetFieldID(VSockImplClass, "fd", "I");
|
jfieldID peerFdField = env->GetFieldID(VSockImplClass, "fd", "I");
|
||||||
env->SetIntField(connectionVSock, fdField, peer_fd);
|
env->SetIntField(connectionVSock, peerFdField, peer_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL
|
JNIEXPORT jint JNICALL
|
||||||
Java_fr_acinq_lightning_vsock_native_VSockImpl_getLocalCid(JNIEnv *env, jobject thisObj) {
|
Java_fr_acinq_lightning_vsock_native_VSockImpl_getLocalCid(JNIEnv *env, jobject thisObj) {
|
||||||
int fd = -1; // Assuming you have stored the fd somewhere accessible
|
jclass thisClass = env->GetObjectClass(thisObj);
|
||||||
|
jfieldID fdField = env->GetFieldID(thisClass, "fd", "I");
|
||||||
|
int fd = env->GetIntField(thisObj, fdField);
|
||||||
|
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed");
|
env->ThrowNew(env->FindClass("java/net/SocketException"), "Socket is closed");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -1,38 +1,47 @@
|
|||||||
package fr.acinq.lightning.vsock.native
|
package fr.acinq.lightning.vsock.native
|
||||||
|
|
||||||
import fr.acinq.lightning.vsock.VSockAddress
|
import fr.acinq.lightning.vsock.VSockAddress
|
||||||
import fr.acinq.lightning.vsock.VSockImplLib
|
import java.net.SocketException
|
||||||
|
|
||||||
class VSockImpl {
|
class VSockImpl() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
VSockImplLib.loadLibrary()
|
System.load("${System.getProperty("user.dir")}/build/libs/libjniVSockImpl.so")
|
||||||
|
//NarSystem.loadLibrary() // Load the native library from the .nar file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var fd: Int = -1
|
||||||
|
|
||||||
|
@Throws(SocketException::class)
|
||||||
fun create() {
|
fun create() {
|
||||||
VSockImplLib.socketCreate()
|
socketCreate()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun connect(address: VSockAddress){
|
private external fun allocate()
|
||||||
VSockImplLib.connect(address)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun close(){
|
private external fun socketCreate()
|
||||||
VSockImplLib.close()
|
|
||||||
}
|
@Throws(Exception::class)
|
||||||
fun write(b: ByteArray, off: Int, len: Int){
|
external fun connect(address: VSockAddress?)
|
||||||
VSockImplLib.write(b, off, len)
|
|
||||||
}
|
@Throws(Exception::class)
|
||||||
fun read(b: ByteArray, off: Int, len: Int): Int{
|
external fun close()
|
||||||
return VSockImplLib.read(b, off, len)
|
|
||||||
}
|
@Throws(Exception::class)
|
||||||
fun bind(address: VSockAddress){
|
external fun write(b: ByteArray, offset: Int, len: Int)
|
||||||
VSockImplLib.bind(address)
|
|
||||||
}
|
@Throws(Exception::class)
|
||||||
fun listen(backlog: Int){
|
external fun read(b: ByteArray, offset: Int, len: Int): Int
|
||||||
VSockImplLib.listen(backlog)
|
|
||||||
}
|
@Throws(Exception::class)
|
||||||
fun accept(peerVSock: VSockImpl){
|
external fun bind(addr: fr.acinq.lightning.vsock.VSockAddress?)
|
||||||
VSockImplLib.accept(peerVSock)
|
|
||||||
}
|
@Throws(Exception::class)
|
||||||
}
|
external fun listen(backlog: Int)
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
external fun accept(peerVSock: VSockImpl)
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
external fun getLocalCid(): Int
|
||||||
|
}
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
package fr.acinq.lightning.vsock
|
|
||||||
|
|
||||||
import fr.acinq.lightning.vsock.native.VSockImpl
|
|
||||||
|
|
||||||
actual object VSockImplLib {
|
|
||||||
actual fun loadLibrary() {
|
|
||||||
val libsPath = System.getenv("LIBS_PATH")
|
|
||||||
?: throw IllegalStateException("LIBS_PATH environment variable not set")
|
|
||||||
|
|
||||||
val libPath = "$libsPath/libjniVSockImpl.so"
|
|
||||||
System.load(libPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
actual external fun socketCreate()
|
|
||||||
actual external fun connect(address: VSockAddress)
|
|
||||||
actual external fun close()
|
|
||||||
actual external fun write(b: ByteArray, off: Int, len: Int)
|
|
||||||
actual external fun read(b: ByteArray, off: Int, len: Int): Int
|
|
||||||
actual external fun bind(address: VSockAddress)
|
|
||||||
actual external fun listen(backlog: Int)
|
|
||||||
actual external fun accept(peerVSock: VSockImplLib)
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
package fr.acinq.lightning.vsock
|
|
||||||
|
|
||||||
import kotlinx.cinterop.*
|
|
||||||
import platform.posix.*
|
|
||||||
import kotlin.experimental.ExperimentalNativeApi
|
|
||||||
|
|
||||||
@OptIn(ExperimentalForeignApi::class)
|
|
||||||
fun getEnvVariable(name: String): String? {
|
|
||||||
return getenv(name)?.toKString()
|
|
||||||
}
|
|
||||||
|
|
||||||
actual object VSockImplLib {
|
|
||||||
@OptIn(ExperimentalForeignApi::class)
|
|
||||||
val handle: COpaquePointer? by lazy {
|
|
||||||
val libsPath = getEnvVariable("LIBS_PATH")
|
|
||||||
?: throw IllegalStateException("LIBS_PATH environment variable not set")
|
|
||||||
|
|
||||||
val libPath = "$libsPath/libjniVSockImpl.so"
|
|
||||||
dlopen(libPath, RTLD_LAZY)?.also {
|
|
||||||
println("Library loaded successfully")
|
|
||||||
} ?: run {
|
|
||||||
val error = dlerror() ?: "Unknown error"
|
|
||||||
throw IllegalStateException("Failed to load library: $error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalForeignApi::class)
|
|
||||||
actual fun loadLibrary() {
|
|
||||||
handle
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to retrieve the symbol (function pointer) and cast it
|
|
||||||
@OptIn(ExperimentalNativeApi::class)
|
|
||||||
inline fun <reified T : CPointer<*>> getSymbol(name: String): T {
|
|
||||||
val symbol = dlsym(handle, name)
|
|
||||||
?: throw IllegalStateException("Symbol $name not found: ${dlerror()}")
|
|
||||||
return symbol.reinterpret()
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalNativeApi::class)
|
|
||||||
actual fun socketCreate(){
|
|
||||||
val socketCreatePtr = getSymbol<CFunction<() -> Int>>("socketCreate")
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalNativeApi::class)
|
|
||||||
actual fun connect(address: VSockAddress) {
|
|
||||||
val connectPtr = getSymbol<CFunction<(VSockAddress) -> Unit>>("connect")
|
|
||||||
connectPtr(address)
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalNativeApi::class)
|
|
||||||
actual fun close() {
|
|
||||||
val closePtr = getSymbol<CFunction<() -> Unit>>("close")
|
|
||||||
closePtr()
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalNativeApi::class)
|
|
||||||
actual fun write(b: ByteArray, off: Int, len: Int) {
|
|
||||||
val writePtr = getSymbol<CFunction<(CPointer<ByteVar>, Int, Int) -> Unit>>("write")
|
|
||||||
b.usePinned {
|
|
||||||
writePtr(it.addressOf(off), off, len)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalNativeApi::class)
|
|
||||||
actual fun read(b: ByteArray, off: Int, len: Int): Int {
|
|
||||||
val readPtr = getSymbol<CFunction<(CPointer<ByteVar>, Int, Int) -> Int>>("read")
|
|
||||||
return b.usePinned {
|
|
||||||
readPtr(it.addressOf(off), off, len)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalNativeApi::class)
|
|
||||||
actual fun bind(address: VSockAddress) {
|
|
||||||
val bindPtr = getSymbol<CFunction<(VSockAddress) -> Unit>>("bind")
|
|
||||||
bindPtr(address)
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalNativeApi::class)
|
|
||||||
actual fun listen(backlog: Int) {
|
|
||||||
val listenPtr = getSymbol<CFunction<(Int) -> Unit>>("listen")
|
|
||||||
listenPtr(backlog)
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalNativeApi::class)
|
|
||||||
actual fun accept(peerVSock: VSockImplLib) {
|
|
||||||
@OptIn(ExperimentalNativeApi::class)
|
|
||||||
val acceptPtr = getSymbol<CFunction<(VSockImplLib) -> Unit>>("accept")
|
|
||||||
acceptPtr(peerVSock)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user