Skip to content

Commit

Permalink
Merge pull request #106 from amosproj/config-rework
Browse files Browse the repository at this point in the history
Configuration rework
  • Loading branch information
ffranzgitHub authored Nov 26, 2024
2 parents cb7741e + 62ed97a commit e341301
Show file tree
Hide file tree
Showing 20 changed files with 360 additions and 304 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// SPDX-FileCopyrightText: 2024 Felix Hilgers <[email protected]>
// SPDX-FileCopyrightText: 2024 Luca Bretting <[email protected]>
//
// SPDX-License-Identifier: MIT
Expand Down Expand Up @@ -56,7 +57,7 @@ class ConfigurationManager(val clientFactory: ClientFactory) :
client!!.getConfiguration()
} catch (e: ClientException) {
// TODO this should be handled on the backend
client!!.setConfiguration(Configuration(listOf()))
client!!.setConfiguration(Configuration(vfsWrite = null, uprobes = listOf()))
client!!.getConfiguration()
}
configuration.update { ConfigurationUpdate.Valid(initializedConfiguration) }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// SPDX-FileCopyrightText: 2024 Felix Hilgers <[email protected]>
// SPDX-FileCopyrightText: 2024 Luca Bretting <[email protected]>
//
// SPDX-License-Identifier: MIT
Expand Down Expand Up @@ -40,9 +41,7 @@ fun ConfigurationScreen(
// Render list of options
EbpfOptions(
options = state.options,
onCheckedChanged = { option, newValue ->
viewModel.optionChanged(option, newValue)
},
onVfsWriteChanged = { newValue -> viewModel.vfsWriteChanged(pids, newValue) },
)

// Show the submit button if the user changed settings
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// SPDX-FileCopyrightText: 2024 Felix Hilgers <[email protected]>
// SPDX-FileCopyrightText: 2024 Luca Bretting <[email protected]>
//
// SPDX-License-Identifier: MIT
Expand All @@ -9,7 +10,8 @@ import androidx.lifecycle.viewModelScope
import de.amosproj3.ziofa.api.ConfigurationAccess
import de.amosproj3.ziofa.api.ConfigurationUpdate
import de.amosproj3.ziofa.ui.configuration.data.ConfigurationScreenState
import de.amosproj3.ziofa.ui.configuration.data.EBpfProgramOption
import de.amosproj3.ziofa.ui.configuration.data.EbpfProgramOptions
import de.amosproj3.ziofa.ui.configuration.data.VfsWriteOption
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
Expand All @@ -19,14 +21,17 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import timber.log.Timber
import uniffi.shared.Configuration
import uniffi.shared.VfsWriteConfig

class ConfigurationViewModel(val configurationAccess: ConfigurationAccess) : ViewModel() {

private val _changed = MutableStateFlow(false)
val changed = _changed.stateIn(viewModelScope, SharingStarted.Lazily, false)

private val checkedOptions =
MutableStateFlow<MutableMap<String, EBpfProgramOption>>(mutableMapOf())
MutableStateFlow(
EbpfProgramOptions(vfsWriteOption = VfsWriteOption(enabled = false, pids = listOf()))
)

private val _configurationScreenState =
MutableStateFlow<ConfigurationScreenState>(ConfigurationScreenState.Loading)
Expand All @@ -45,16 +50,17 @@ class ConfigurationViewModel(val configurationAccess: ConfigurationAccess) : Vie
}
}

fun optionChanged(eBpfProgramOption: EBpfProgramOption, newState: Boolean) {
checkedOptions.update { currentMap ->
currentMap.computeIfPresent(eBpfProgramOption.name) { _, oldValue ->
oldValue.copy(active = newState)
}
currentMap
}
_configurationScreenState.update {
ConfigurationScreenState.Valid(checkedOptions.value.values.toList())
fun vfsWriteChanged(pids: IntArray?, newState: Boolean) {
checkedOptions.update {
it.copy(
vfsWriteOption =
VfsWriteOption(
enabled = newState,
pids = pids?.let { it.map { it.toUInt() } } ?: listOf(),
)
)
}
_configurationScreenState.update { ConfigurationScreenState.Valid(checkedOptions.value) }
_changed.update { true }
}

Expand All @@ -73,8 +79,8 @@ class ConfigurationViewModel(val configurationAccess: ConfigurationAccess) : Vie
private fun ConfigurationUpdate.toUIUpdate(): ConfigurationScreenState {
return when (this) {
is ConfigurationUpdate.Valid -> {
checkedOptions.update { this.toUIOptions().associateBy { it.name }.toMutableMap() }
ConfigurationScreenState.Valid(checkedOptions.value.values.toList())
checkedOptions.update { this.toUIOptions() }
ConfigurationScreenState.Valid(checkedOptions.value)
}

is ConfigurationUpdate.Invalid ->
Expand All @@ -84,13 +90,20 @@ class ConfigurationViewModel(val configurationAccess: ConfigurationAccess) : Vie
}
}

private fun ConfigurationUpdate.Valid.toUIOptions(): List<EBpfProgramOption> {
return this.configuration.entries.map {
EBpfProgramOption(it.hrName, active = it.attach, true, it)
}
private fun ConfigurationUpdate.Valid.toUIOptions(): EbpfProgramOptions {
val vfsOption =
this.configuration.vfsWrite?.let { VfsWriteOption(enabled = true, pids = it.pids) }
?: VfsWriteOption(enabled = false, pids = listOf())

return EbpfProgramOptions(vfsWriteOption = vfsOption)
}

private fun MutableMap<String, EBpfProgramOption>.toConfiguration(): Configuration {
return Configuration(this.values.map { it.ebpfEntry })
private fun EbpfProgramOptions.toConfiguration(): Configuration {
val vfsConfig =
if (this.vfsWriteOption.enabled) {
VfsWriteConfig(this.vfsWriteOption.pids)
} else null

return Configuration(vfsWrite = vfsConfig, uprobes = listOf())
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// SPDX-FileCopyrightText: 2024 Felix Hilgers <[email protected]>
// SPDX-FileCopyrightText: 2024 Luca Bretting <[email protected]>
//
// SPDX-License-Identifier: MIT
Expand All @@ -8,36 +9,33 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Checkbox
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import de.amosproj3.ziofa.ui.configuration.data.EBpfProgramOption
import de.amosproj3.ziofa.ui.configuration.data.EbpfProgramOptions

@Composable
fun EbpfOptions(
options: List<EBpfProgramOption>,
onCheckedChanged: (EBpfProgramOption, Boolean) -> Unit,
) {
fun EbpfOptions(options: EbpfProgramOptions, onVfsWriteChanged: (Boolean) -> Unit) {
LazyColumn(modifier = Modifier.padding(horizontal = 20.dp).fillMaxSize()) {
item { Spacer(Modifier.height(15.dp)) }

items(options) { option ->
item {
Row(
modifier = Modifier.fillMaxSize(),
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(option.name)
Text("Vfs Write Analysis")
Checkbox(
checked = option.active,
onCheckedChange = { onCheckedChanged(option, it) },
checked = options.vfsWriteOption.enabled,
onCheckedChange = onVfsWriteChanged,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// SPDX-FileCopyrightText: 2024 Felix Hilgers <[email protected]>
// SPDX-FileCopyrightText: 2024 Luca Bretting <[email protected]>
//
// SPDX-License-Identifier: MIT

package de.amosproj3.ziofa.ui.configuration.data

sealed class ConfigurationScreenState {
data class Valid(val options: List<EBpfProgramOption>) : ConfigurationScreenState()
data class Valid(val options: EbpfProgramOptions) : ConfigurationScreenState()

data class Invalid(val errorMessage: String) : ConfigurationScreenState()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
// SPDX-FileCopyrightText: 2024 Felix Hilgers <[email protected]>
// SPDX-FileCopyrightText: 2024 Luca Bretting <[email protected]>
//
// SPDX-License-Identifier: MIT

package de.amosproj3.ziofa.ui.configuration.data

import uniffi.shared.EbpfEntry
data class EbpfProgramOptions(val vfsWriteOption: VfsWriteOption)

data class EBpfProgramOption(
val name: String,
val active: Boolean,
val confirmed: Boolean, // TODO show diff
val ebpfEntry: EbpfEntry,
)
data class VfsWriteOption(val enabled: Boolean, val pids: List<UInt>)
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package de.amosproj3.ziofa.client

import kotlinx.coroutines.flow.Flow
import uniffi.shared.Configuration
import uniffi.shared.Event
import uniffi.shared.Process

interface Client {
Expand All @@ -30,6 +31,8 @@ interface Client {
suspend fun getConfiguration(): Configuration

suspend fun setConfiguration(configuration: Configuration)

suspend fun initStream(): Flow<Event>
}

interface ClientFactory {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// SPDX-FileCopyrightText: 2024 Felix Hilgers <[email protected]>
// SPDX-FileCopyrightText: 2024 Luca Bretting <[email protected]>
//
// SPDX-License-Identifier: MIT
Expand All @@ -11,27 +12,17 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import uniffi.shared.Cmd
import uniffi.shared.Configuration
import uniffi.shared.EbpfEntry
import uniffi.shared.Event
import uniffi.shared.EventData
import uniffi.shared.Process
import uniffi.shared.UprobeConfig
import uniffi.shared.VfsWriteConfig
import uniffi.shared.VfsWriteEvent

const val alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

object RustClient : Client {
private var configuration: Configuration =
Configuration(
listOf(
EbpfEntry(
"Test HR name",
"this is a test",
"ebpf_name",
12345u,
UprobeConfig(0u, "target", 54321),
"hook",
false,
)
)
)
Configuration(vfsWrite = VfsWriteConfig(listOf(1234u, 43124u)), uprobes = listOf())

override suspend fun serverCount(): Flow<UInt> = flow {
var ctr = 0u
Expand Down Expand Up @@ -88,6 +79,25 @@ object RustClient : Client {
override suspend fun setConfiguration(configuration: Configuration) {
this.configuration = configuration
}

override suspend fun initStream(): Flow<Event> = flow {
while (true) {
delay(Random.nextUInt(500u).toLong())
emit(
Event(
EventData.VfsWrite(
VfsWriteEvent(
pid = 12415u,
tid = 1234u,
fp = 125123123u,
bytesWritten = 123121u,
beginTimeStamp = 12312412u,
)
)
)
)
}
}
}

class RustClientFactory(val url: String) : ClientFactory {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import uniffi.shared.Configuration
import uniffi.shared.Event
import uniffi.shared.Process

class RustClient(private val inner: uniffi.client.Client) : Client {
Expand All @@ -35,6 +36,8 @@ class RustClient(private val inner: uniffi.client.Client) : Client {

override suspend fun setConfiguration(configuration: Configuration) =
inner.setConfiguration(configuration)

override suspend fun initStream(): Flow<Event> = inner.initStreamFlow()
}

class RustClientFactory(val url: String) : ClientFactory {
Expand Down Expand Up @@ -65,3 +68,11 @@ fun uniffi.client.Client.serverCountFlow() = flow {
}
}
}

fun uniffi.client.Client.initStreamFlow() = flow {
initStream().use { stream ->
while (true) {
stream.next()?.also { event -> emit(event) } ?: break
}
}
}
27 changes: 12 additions & 15 deletions rust/backend/daemon/src/bin/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

use clap::Parser;
use shared::{
config::{Configuration, EbpfEntry},
config::{Configuration, VfsWriteConfig},
ziofa::ziofa_client::ZiofaClient,
};
use tonic::transport::Channel;
Expand All @@ -30,37 +30,34 @@ async fn test_check_server(client: &mut ZiofaClient<Channel>) {
println!();
}

async fn test_get_configuration(
client: &mut ZiofaClient<Channel>,
verbose: bool,
) -> Vec<EbpfEntry> {
async fn test_get_configuration(client: &mut ZiofaClient<Channel>, verbose: bool) -> Configuration {
println!("TEST get_configuration");
let config = match client.get_configuration(()).await {
Ok(t) => {
let res = t.into_inner().entries;
let res = t.into_inner();
println!("SUCCESS");

if verbose {
for (i, e) in res.iter().enumerate() {
println!("Entry {}: {:?}", i, e);
}
println!("{:?}", res);
}

res
}
Err(e) => {
println!("ERROR: {:?}", e);
Vec::new()
Configuration {
uprobes: vec![],
vfs_write: Some(VfsWriteConfig { pids: vec![] }),
}
}
};
println!();
config
}

async fn test_set_configuration(client: &mut ZiofaClient<Channel>, config: Vec<EbpfEntry>) {
async fn test_set_configuration(client: &mut ZiofaClient<Channel>, config: Configuration) {
println!("TEST set_configuration");
match client
.set_configuration(Configuration { entries: config })
.await
{
match client.set_configuration(config).await {
Ok(t) => {
let res = t.into_inner().response_type;
println!("SUCCESS: {}", res);
Expand Down
Loading

0 comments on commit e341301

Please sign in to comment.