diff --git a/bin/main/logback.xml b/bin/main/logback.xml
new file mode 100644
index 0000000..c8b7060
--- /dev/null
+++ b/bin/main/logback.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 0330431..27034c9 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -16,8 +16,6 @@ plugins {
kotlin("multiplatform") version Versions.kotlin
kotlin("plugin.serialization") version Versions.kotlin
id("app.cash.sqldelight") version Versions.sqlDelight
- `java-library`
- id("org.bytedeco.gradle-javacpp-platform").version("1.5.10")
application
}
@@ -75,6 +73,9 @@ val buildVersionsTask by tasks.registering(Sync::class) {
kotlin {
jvm {
withJava()
+ compilations["main"].defaultSourceSet {
+ resources.srcDirs("src/jvmMain/kotlin/fr/acinq/lightning/vsock/")
+ }
}
fun KotlinNativeTargetWithHostTests.phoenixBinaries() {
@@ -83,10 +84,10 @@ kotlin {
entryPoint = "fr.acinq.lightning.bin.main"
optimized = false // without this, release mode throws 'Index 0 out of bounds for length 0' in StaticInitializersOptimization.kt
}
- executable("phoenix-cli") {
+ /*executable("phoenix-cli") {
entryPoint = "fr.acinq.lightning.cli.main"
optimized = false // without this, release mode throws 'Index 0 out of bounds for length 0' in StaticInitializersOptimization.kt
- }
+ }*/
}
}
@@ -111,20 +112,14 @@ kotlin {
}
sourceSets {
- val jvmMain by getting {
- // Include the native source directory if needed
- resources.srcDirs("src/commonMain/kotlin/fr/acinq/lightning/vsock/native")
- }
commonMain {
kotlin.srcDir(buildVersionsTask.map { it.destinationDir })
dependencies {
implementation("com.github.raymond98.lightning-kmp:lightning-kmp:v1.6.2-FEECREDIT-8")
- implementation("org.bytedeco:javacpp:1.5.10")
- implementation(kotlin("stdlib-jdk8"))
- implementation("org.bytedeco:javacv-platform:1.5.10")
- implementation("org.bytedeco:javacpp-presets:1.5.10")
+ implementation("org.jetbrains.kotlinx:atomicfu:0.25.0")
+ implementation("org.jetbrains.kotlin:kotlin-stdlib:1.8.20")
api("fr.acinq.bitcoin:bitcoin-kmp:${Versions.bitcoinKmpVersion}")
api("co.touchlab:kermit:${Versions.kermitLoggerVersion}")
api("org.jetbrains.kotlinx:kotlinx-datetime:${Versions.datetimeVersion}")
@@ -157,6 +152,8 @@ kotlin {
implementation("fr.acinq.secp256k1:secp256k1-kmp-jni-jvm:${Versions.secpJniJvmVersion}")
implementation(ktor("client-okhttp"))
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 {
@@ -220,7 +217,8 @@ application {
mainClass = "fr.acinq.lightning.bin.MainKt"
// Set java.library.path to include the directory where the shared library is generated
- applicationDefaultJvmArgs = listOf("-Djava.library.path=${project.buildDir}/libs")
+ applicationDefaultJvmArgs = listOf("-Djava.library.path=${layout.buildDirectory.dir("libs").get().asFile}")
+ applicationDefaultJvmArgs = listOf("-DLIBS_PATH=${layout.buildDirectory.dir("libs").get().asFile.absolutePath.replace("\\", "/")}")
}
val cliScripts by tasks.register("cliScripts", CreateStartScripts::class) {
@@ -234,11 +232,27 @@ tasks.startScripts {
dependsOn(cliScripts)
}
+val setLibPath by tasks.register("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("compileNative") {
group = "build"
description = "Compile the native C++ code into a shared library"
- val outputDir = file("${project.buildDir}/libs")
+ val outputDir = layout.buildDirectory.dir("libs").get().asFile
val nativeSourceDir = file("src/commonMain/kotlin/fr/acinq/lightning/vsock/native")
// Locate the JNI headers - adjust these paths based on your actual JDK location
@@ -252,13 +266,14 @@ val compileNative by tasks.register("compileNative") {
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 {
dependsOn(compileNative)
+ dependsOn(setLibPath)
}
tasks.withType {
dependsOn(compileNative)
+ dependsOn(setLibPath)
}
distributions {
@@ -271,7 +286,6 @@ distributions {
// forward std input when app is run via gradle (otherwise keyboard input will return EOF)
tasks.withType {
standardInput = System.`in`
- dependsOn(compileNative) //This should not be the case for all platforms
}
sqldelight {
diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt
index 7a499ef..6c8fd8e 100644
--- a/buildSrc/src/main/kotlin/Versions.kt
+++ b/buildSrc/src/main/kotlin/Versions.kt
@@ -1,7 +1,6 @@
object Versions {
val kotlin = "1.9.23"
val lightningKmp = "1.7.0-FEECREDIT-8"
- val lightningKmpTag = "v1.6.2-FEECREDIT-8"
val sqlDelight = "2.0.1"
val okio = "3.8.0"
val clikt = "4.2.2"
diff --git a/src/commonMain/kotlin/fr/acinq/lightning/bin/Api.kt b/src/commonMain/kotlin/fr/acinq/lightning/bin/Api.kt
index b2bb8e1..8cc9100 100644
--- a/src/commonMain/kotlin/fr/acinq/lightning/bin/Api.kt
+++ b/src/commonMain/kotlin/fr/acinq/lightning/bin/Api.kt
@@ -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)
}
} catch (e: Exception) {
- call.respond(HttpStatusCode.InternalServerError, "Failed to process splice-in: ${e.localizedMessage}")
+ call.respond(HttpStatusCode.InternalServerError, "Failed to process splice-in: ${e.message}")
}
}
post("closechannel") {
diff --git a/src/commonMain/kotlin/fr/acinq/lightning/vsock/BaseVSock.kt b/src/commonMain/kotlin/fr/acinq/lightning/vsock/BaseVSock.kt
index da725ad..cad6fc2 100644
--- a/src/commonMain/kotlin/fr/acinq/lightning/vsock/BaseVSock.kt
+++ b/src/commonMain/kotlin/fr/acinq/lightning/vsock/BaseVSock.kt
@@ -1,67 +1,64 @@
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 java.io.Closeable
-import java.io.IOException
-import java.net.SocketException
+import kotlinx.atomicfu.locks.SynchronizedObject
+import kotlinx.atomicfu.locks.synchronized
+import kotlin.jvm.Synchronized
-abstract class BaseVSock : Closeable {
- protected val closeLock: Any = Any()
+abstract class BaseVSock(loggerfactory: LoggerFactory) : Closeable {
+ private val closeLock = SynchronizedObject()
+ val logger = loggerfactory.newLogger(this::class)
protected var isClosed: Boolean = false
protected var created: Boolean = false
protected var bound: Boolean = false
- private var implementation: VSockImpl? = null
+ private lateinit var implementation: VSockImpl
@Throws(SocketException::class)
private fun createImplementation() {
implementation = VSockImpl()
- implementation!!.create()
+ implementation.create()
created = true
}
@Throws(SocketException::class)
- fun getImplementation(): VSockImpl? {
+ fun getImplementation(): VSockImpl {
if (!created) createImplementation()
return implementation
}
@Throws(SocketException::class)
fun setImplementation(): VSockImpl {
- if (implementation == null) {
- implementation = VSockImpl()
- }
- return implementation!!
+ return implementation
}
- @get:Throws(IOException::class)
- val localCid: Int
- get() = getImplementation()!!.getLocalCid()
-
- @JvmOverloads
@Throws(IOException::class)
fun bind(address: VSockAddress?, backlog: Int = DEFAULT_BACKLOG) {
- var backlog = backlog
+ var backlogs = backlog
if (isClosed) {
- throw SocketException("Socket closed")
+ logger.warning { "Socket closed" }
}
if (bound) {
- throw SocketException("Socket already bound")
+ logger.warning { "Socket already bound" }
}
- if (backlog <= 0) {
- backlog = DEFAULT_BACKLOG
+ if (backlogs <= 0) {
+ backlogs = DEFAULT_BACKLOG
}
- getImplementation()!!.bind(address)
- getImplementation()!!.listen(backlog)
+ if (address != null) {
+ getImplementation().bind(address)
+ }
+ getImplementation().listen(backlogs)
bound = true
}
@Synchronized
- @Throws(IOException::class)
override fun close() {
synchronized(closeLock) {
if (isClosed) return
- if (created) getImplementation()!!.close()
+ if (created) getImplementation().close()
isClosed = true
}
}
@@ -70,3 +67,9 @@ abstract class BaseVSock : Closeable {
private const val DEFAULT_BACKLOG = 42
}
}
+
+// Custom Closeable interface for Kotlin/Native
+interface Closeable {
+ fun close()
+}
+
diff --git a/src/commonMain/kotlin/fr/acinq/lightning/vsock/ServerVSock.kt b/src/commonMain/kotlin/fr/acinq/lightning/vsock/ServerVSock.kt
index b6ec846..2cdebad 100644
--- a/src/commonMain/kotlin/fr/acinq/lightning/vsock/ServerVSock.kt
+++ b/src/commonMain/kotlin/fr/acinq/lightning/vsock/ServerVSock.kt
@@ -1,17 +1,20 @@
package fr.acinq.lightning.vsock
-import java.io.IOException
-import java.net.SocketException
+import fr.acinq.lightning.logging.LoggerFactory
+import fr.acinq.lightning.logging.debug
-class ServerVSock : BaseVSock() {
- @Throws(IOException::class)
+class ServerVSock(private val loggerfactory: LoggerFactory) : BaseVSock(loggerfactory) {
+
+ @Throws(SocketException::class)
fun accept(): VSock {
- if (isClosed) throw SocketException("Socket closed")
- if (!bound) throw SocketException("Socket not bound")
- val socket = VSock()
+ if (isClosed) logger.debug { "Socket closed" }
+ if (!bound) logger.debug { "Socket not bound"}
+
+ val socket = VSock(logger = loggerfactory)
socket.setImplementation()
- socket.getImplementation()?.let { getImplementation()!!.accept(it) }
+ socket.getImplementation().let { getImplementation().accept(it) }
socket.postAccept()
+
return socket
}
}
diff --git a/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSock.kt b/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSock.kt
index b35e36f..d5c88cc 100644
--- a/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSock.kt
+++ b/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSock.kt
@@ -1,71 +1,81 @@
package fr.acinq.lightning.vsock
-import java.io.Closeable
-import java.io.IOException
-import java.net.SocketException
+import fr.acinq.lightning.logging.LoggerFactory
+import kotlin.jvm.Synchronized
+
+class VSock(address: VSockAddress? = null, logger: LoggerFactory) : BaseVSock(logger) {
+ private val loggerInstance = logger.newLogger(this::class)
-class VSock : BaseVSock, Closeable {
private var connected = false
-
- @get:Throws(IOException::class)
- @get:Synchronized
var outputStream: VSockOutputStream? = null
- get() {
- if (isClosed) {
- throw SocketException("VSock is closed")
- }
- 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) {
- throw SocketException("VSock is closed")
- }
- if (field == null) {
- field = getImplementation()?.let { VSockInputStream(it) }
- }
- return field
- }
private set
- constructor()
-
- constructor(address: VSockAddress?) {
- try {
- getImplementation()!!.connect(address)
- } catch (e: Exception) {
+ init {
+ if (address != null) {
try {
- close()
- } catch (ce: Exception) {
- e.addSuppressed(ce)
+ getImplementation().connect(address)
+ } catch (e: Exception) {
+ try {
+ close()
+ } catch (ce: Exception) {
+ e.addSuppressed(ce)
+ }
+ throw IllegalStateException(e.message, e)
}
- throw IllegalStateException(e.message, e)
}
}
@Throws(SocketException::class)
- fun connect(address: VSockAddress?) {
+ fun connect(address: VSockAddress) {
if (isClosed) {
throw SocketException("Socket closed")
}
if (connected) {
throw SocketException("Socket already connected")
}
- getImplementation()!!.connect(address)
+ getImplementation().connect(address)
connected = 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() {
created = true
bound = true
connected = true
}
}
+
+// Custom exception classes for Kotlin/Native
+class SocketException(message: String) : Exception(message)
+class IOException(message: String) : Exception(message)
+
+
+
+
diff --git a/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSockAddress.kt b/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSockAddress.kt
index 80829a1..a75dfcd 100644
--- a/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSockAddress.kt
+++ b/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSockAddress.kt
@@ -1,35 +1,36 @@
package fr.acinq.lightning.vsock
-import java.net.SocketAddress
-import java.util.Objects
+class VSockAddress(val cid: Int, val port: Int) {
-class VSockAddress(val cid: Int, val port: Int) : SocketAddress() {
- override fun equals(o: Any?): Boolean {
- if (this === o) return true
- if (o == null || javaClass != o.javaClass) return false
- val that = o as VSockAddress
- return cid == that.cid &&
- port == that.port
+ companion object {
+ const val VMADDR_CID_ANY = -1
+ const val VMADDR_CID_HYPERVISOR = 0
+ const val VMADDR_CID_RESERVED = 1
+ const val VMADDR_CID_HOST = 2
+ const val VMADDR_CID_PARENT = 3
+
+ 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 {
- return Objects.hash(cid, port)
+ var result = cid
+ result = 31 * result + port
+ return result
}
override fun toString(): String {
- 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
+ return "VSockAddress(cid=$cid, port=$port)"
}
}
diff --git a/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSockInputStream.kt b/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSockInputStream.kt
index b07275b..4717ab8 100644
--- a/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSockInputStream.kt
+++ b/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSockInputStream.kt
@@ -1,30 +1,29 @@
package fr.acinq.lightning.vsock
import fr.acinq.lightning.vsock.native.VSockImpl
-import java.io.IOException
-import java.io.InputStream
-class VSockInputStream(private val vSock: VSockImpl) : InputStream() {
- private lateinit var temp: ByteArray
+class VSockInputStream(private val vSock: VSockImpl) : Closeable {
+
+ private var temp: ByteArray? = null
@Throws(IOException::class)
- override fun read(b: ByteArray, off: Int, len: Int): Int {
+ fun read(b: ByteArray, off: Int, len: Int): Int {
return vSock.read(b, off, len)
}
@Throws(IOException::class)
- override fun read(): Int {
+ fun read(): Int {
temp = ByteArray(1)
- val n = read(temp, 0, 1)
- if (n <= 0) {
- return -1
+ val n = read(temp!!, 0, 1)
+ return if (n <= 0) {
+ -1
+ } else {
+ temp!![0].toInt()
}
- return temp[0].toInt()
}
- @Throws(IOException::class)
override fun close() {
vSock.close()
- super.close()
}
}
+
diff --git a/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSockLibraryLoader.kt b/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSockLibraryLoader.kt
new file mode 100644
index 0000000..0b2c050
--- /dev/null
+++ b/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSockLibraryLoader.kt
@@ -0,0 +1,24 @@
+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)
+}
\ No newline at end of file
diff --git a/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSockOutputStream.kt b/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSockOutputStream.kt
index 83c9690..817bef4 100644
--- a/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSockOutputStream.kt
+++ b/src/commonMain/kotlin/fr/acinq/lightning/vsock/VSockOutputStream.kt
@@ -1,26 +1,23 @@
package fr.acinq.lightning.vsock
import fr.acinq.lightning.vsock.native.VSockImpl
-import java.io.IOException
-import java.io.OutputStream
-class VSockOutputStream internal constructor(private val vSock: VSockImpl) : OutputStream() {
+class VSockOutputStream(private val vSock: VSockImpl) : Closeable {
+
private val temp = ByteArray(1)
@Throws(IOException::class)
- override fun write(b: Int) {
- temp[0] = b.toByte()
+ fun write(b: ByteArray) {
+ temp[0] = b[0]
this.write(temp, 0, 1)
}
@Throws(IOException::class)
- override fun write(b: ByteArray, off: Int, len: Int) {
+ fun write(b: ByteArray, off: Int, len: Int) {
vSock.write(b, off, len)
}
- @Throws(IOException::class)
override fun close() {
vSock.close()
- super.close()
}
}
diff --git a/src/commonMain/kotlin/fr/acinq/lightning/vsock/VsockMain.kt b/src/commonMain/kotlin/fr/acinq/lightning/vsock/VsockMain.kt
index 893ecd9..73b0974 100644
--- a/src/commonMain/kotlin/fr/acinq/lightning/vsock/VsockMain.kt
+++ b/src/commonMain/kotlin/fr/acinq/lightning/vsock/VsockMain.kt
@@ -1,6 +1,6 @@
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.debug
import fr.acinq.lightning.logging.error
@@ -8,66 +8,73 @@ import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
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.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
-import java.io.IOException
-import java.nio.charset.StandardCharsets
-import java.util.Base64
+import kotlin.io.encoding.Base64
+import kotlin.io.encoding.ExperimentalEncodingApi
-class VsockServer(private val CID: Int, private val port: Int, httpBindPort: Int, host: String, loggerFactory: LoggerFactory) {
+class VsockServer(private val CID: Int, private val port: Int, httpBindPort: Int, host: String,
+ private val loggerFactory: LoggerFactory
+) {
private var server: ServerVSock? = null
private val logger = loggerFactory.newLogger(this::class)
private val client = HttpClient()
- private val apiBaseUrl: String = "${host}:${httpBindPort}"
+ private val apiBaseUrl: String = "$host:$httpBindPort"
private val bufferSize: Int = 4096
@OptIn(DelicateCoroutinesApi::class)
fun start() {
- server = ServerVSock()
+ server = ServerVSock(loggerFactory)
+ var peerVSock: VSock? = null
try {
- server?.bind(VSockAddress(CID, port)) //For any CID use VSockAddress.VMADDR_CID_ANY
- logger.debug { "Vsock Bound on Cid: ${server?.localCid}" }
+ server?.bind(VSockAddress(CID, port)) // For any CID, use VSockAddress.VMADDR_CID_ANY
+ logger.debug { "Vsock Bound on CID: ${CID}" }
- server?.accept()?.use { peerVSock ->
+ peerVSock = server?.accept()
+
+ if (peerVSock != null) {
+ logger.debug { "Vsock start did do it" }
val buffer = ByteArray(bufferSize)
val bytesRead = peerVSock.inputStream?.read(buffer, 0, bufferSize)
- if (bytesRead != null) {
- if (bytesRead > 0) {
- val receivedData = String(buffer, 0, bytesRead, StandardCharsets.UTF_8).trim()
- logger.debug { "Received Data: $receivedData" }
+ if (bytesRead != null && bytesRead > 0) {
+ val receivedData = buffer.decodeToString(0, bytesRead).trim()
+ logger.debug { "Received Data: $receivedData" }
- // Parse the received data into a http request
- val apiRequest = try {
- Json.decodeFromString(receivedData)
- } catch (e: Exception) {
- logger.error { "Failed to parse JSON: ${e.message}" }
- peerVSock.outputStream?.write("Invalid JSON format".toByteArray(StandardCharsets.UTF_8))
- return
- }
+ // Parse the received data into an API request
+ val apiRequest = try {
+ Json.decodeFromString(receivedData)
+ } catch (e: Exception) {
+ logger.error { "Failed to parse JSON: ${e.message}" }
+ peerVSock.outputStream?.write("Invalid JSON format".toByteArray(Charsets.UTF_8))
+ return
+ }
- // Handle the API call
- GlobalScope.launch {
- val response = handleApiCall(apiRequest)
- peerVSock.outputStream?.write(response.toByteArray(StandardCharsets.UTF_8))
- }
+ // Handle the API call
+ GlobalScope.launch {
+ val response = handleApiCall(apiRequest)
+ peerVSock.outputStream?.write(response.toByteArray(Charsets.UTF_8))
}
}
}
} catch (ex: IOException) {
logger.error { "Error starting Vsock: ${ex.message}" }
} finally {
+ peerVSock?.close()
stop()
}
}
- private suspend fun handleApiCall(request: VsockApiRequest): String {
+ @OptIn(ExperimentalEncodingApi::class)
+ private suspend fun handleApiCall(request: ApiType.VsockApiRequest): String {
return try {
val url = "$apiBaseUrl/${request.method}"
val response: HttpResponse = client.get(url) {
headers {
- append(HttpHeaders.Authorization, "Basic ${Base64.getEncoder().encodeToString("user:${request.httpPassword}".toByteArray())}")
+ append(HttpHeaders.Authorization, "Basic ${Base64.encode("user:${request.httpPassword}".toByteArray(Charsets.UTF_8))}")
}
url {
parameters.appendAll(Parameters.build {
diff --git a/src/commonMain/kotlin/fr/acinq/lightning/vsock/native/VSockImpl.kt b/src/commonMain/kotlin/fr/acinq/lightning/vsock/native/VSockImpl.kt
index fa60369..3be1184 100644
--- a/src/commonMain/kotlin/fr/acinq/lightning/vsock/native/VSockImpl.kt
+++ b/src/commonMain/kotlin/fr/acinq/lightning/vsock/native/VSockImpl.kt
@@ -1,59 +1,38 @@
package fr.acinq.lightning.vsock.native
import fr.acinq.lightning.vsock.VSockAddress
-import org.bytedeco.javacpp.Loader
-import org.bytedeco.javacpp.Pointer
-import org.bytedeco.javacpp.annotation.Namespace
-import org.bytedeco.javacpp.annotation.Platform
-import java.net.SocketException
+import fr.acinq.lightning.vsock.VSockImplLib
-
-@Platform(include = [
- "",
- "",
- "",
- "",
- ""
-])
-@Namespace("vsock")
-class VSockImpl() : Pointer() {
+class VSockImpl {
init {
- Loader.load() //load the native library
+ VSockImplLib.loadLibrary()
}
- var fd: Int = -1
-
- @Throws(SocketException::class)
fun create() {
- socketCreate()
+ VSockImplLib.socketCreate()
}
- private external fun allocate()
+ fun connect(address: VSockAddress){
+ VSockImplLib.connect(address)
+ }
- private external fun socketCreate()
-
- @Throws(Exception::class)
- external fun connect(address: VSockAddress?)
-
- @Throws(Exception::class)
- external override fun close()
-
- @Throws(Exception::class)
- external fun write(b: ByteArray, offset: Int, len: Int)
-
- @Throws(Exception::class)
- external fun read(b: ByteArray, offset: Int, len: Int): Int
-
- @Throws(Exception::class)
- external fun bind(addr: fr.acinq.lightning.vsock.VSockAddress?)
-
- @Throws(Exception::class)
- external fun listen(backlog: Int)
-
- @Throws(Exception::class)
- external fun accept(peerVSock: VSockImpl)
-
- @Throws(Exception::class)
- external fun getLocalCid(): Int
-}
+ fun close(){
+ VSockImplLib.close()
+ }
+ fun write(b: ByteArray, off: Int, len: Int){
+ VSockImplLib.write(b, off, len)
+ }
+ fun read(b: ByteArray, off: Int, len: Int): Int{
+ return VSockImplLib.read(b, off, len)
+ }
+ fun bind(address: VSockAddress){
+ VSockImplLib.bind(address)
+ }
+ fun listen(backlog: Int){
+ VSockImplLib.listen(backlog)
+ }
+ fun accept(peerVSock: VSockImpl){
+ VSockImplLib.accept(peerVSock)
+ }
+}
\ No newline at end of file
diff --git a/src/jvmMain/kotlin/fr/acinq/lightning/vsock/VSockLibraryLoader.jvm.kt b/src/jvmMain/kotlin/fr/acinq/lightning/vsock/VSockLibraryLoader.jvm.kt
new file mode 100644
index 0000000..7777ba4
--- /dev/null
+++ b/src/jvmMain/kotlin/fr/acinq/lightning/vsock/VSockLibraryLoader.jvm.kt
@@ -0,0 +1,22 @@
+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)
+}
diff --git a/src/linuxX64Main/kotlin/fr/acinq/lightning/vsock/VSockLibraryLoader.linuxX64.kt b/src/linuxX64Main/kotlin/fr/acinq/lightning/vsock/VSockLibraryLoader.linuxX64.kt
new file mode 100644
index 0000000..e4e2074
--- /dev/null
+++ b/src/linuxX64Main/kotlin/fr/acinq/lightning/vsock/VSockLibraryLoader.linuxX64.kt
@@ -0,0 +1,91 @@
+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 > 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 Int>>("socketCreate")
+ }
+
+ @OptIn(ExperimentalNativeApi::class)
+ actual fun connect(address: VSockAddress) {
+ val connectPtr = getSymbol Unit>>("connect")
+ connectPtr(address)
+ }
+
+ @OptIn(ExperimentalNativeApi::class)
+ actual fun close() {
+ val closePtr = getSymbol Unit>>("close")
+ closePtr()
+ }
+
+ @OptIn(ExperimentalNativeApi::class)
+ actual fun write(b: ByteArray, off: Int, len: Int) {
+ val writePtr = getSymbol, 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, Int, Int) -> Int>>("read")
+ return b.usePinned {
+ readPtr(it.addressOf(off), off, len)
+ }
+ }
+
+ @OptIn(ExperimentalNativeApi::class)
+ actual fun bind(address: VSockAddress) {
+ val bindPtr = getSymbol Unit>>("bind")
+ bindPtr(address)
+ }
+
+ @OptIn(ExperimentalNativeApi::class)
+ actual fun listen(backlog: Int) {
+ val listenPtr = getSymbol Unit>>("listen")
+ listenPtr(backlog)
+ }
+
+ @OptIn(ExperimentalNativeApi::class)
+ actual fun accept(peerVSock: VSockImplLib) {
+ @OptIn(ExperimentalNativeApi::class)
+ val acceptPtr = getSymbol Unit>>("accept")
+ acceptPtr(peerVSock)
+ }
+}