Skip to content

Commit

Permalink
Tool: add monitor command.
Browse files Browse the repository at this point in the history
  • Loading branch information
tmds committed Jan 3, 2024
1 parent c018ff9 commit f9b4853
Show file tree
Hide file tree
Showing 6 changed files with 326 additions and 34 deletions.
15 changes: 7 additions & 8 deletions docs/tool.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,9 @@ The `Tmds.DBus.Tool` packages extends the dotnet cli to facilitate developing .N
The tool supports:
- [codegen](#codegen): generate C# code for consuming D-Bus services.
- [list](#list): list the available objects, interfaces and services.
- [monitor](#monitor): prints message bus messages.

The tool can be added by using a `DotNetCliToolReference` in the project file.

```xml
<ItemGroup>
<DotNetCliToolReference Include="Tmds.DBus.Tool" Version="0.5.0" />
</ItemGroup>
```
The tool can be installed using `dotnet tool install -g Tmds.DBus.Tool`

Now it can be invoked from the command line:
```
Expand Down Expand Up @@ -77,4 +72,8 @@ The `list` command can also be used to list interfaces from XML files.

```
$ dotnet dbus list interfaces /usr/share/dbus-1/interfaces/*.xml
```
```

## monitor

The `monitor` command registers the tool as a bus monitor and prints out the message bus messages.
14 changes: 0 additions & 14 deletions samples/Monitor/Monitor.csproj

This file was deleted.

11 changes: 0 additions & 11 deletions samples/Monitor/Program.cs

This file was deleted.

6 changes: 6 additions & 0 deletions src/Tmds.DBus.Protocol/Reader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ public bool HasNext(ArrayEnd iterator)
}
return true;
}

public void SkipTo(ArrayEnd end)
{
int advance = end.EndOfArray - (int)_reader.Consumed;
_reader.Advance(advance);
}
}

public ref struct ArrayEnd
Expand Down
310 changes: 310 additions & 0 deletions src/Tmds.DBus.Tool/MonitorCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
using System;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.CommandLineUtils;
using Tmds.DBus.Protocol;

namespace Tmds.DBus.Tool;

class MonitorCommand : Command
{
CommandOption _busOption;

public MonitorCommand(CommandLineApplication parent) :
base("monitor", parent)
{ }

public override void Configure()
{
_busOption = AddBusOption();
}

public override void Execute()
{
var address = ParseBusAddress(_busOption);
MonitorBusAsync(address).Wait();
}

private async Task MonitorBusAsync(string address)
{
StringBuilder sb = new StringBuilder();

await foreach (DisposableMessage dmsg in Tmds.DBus.Protocol.Connection.MonitorBusAsync(address))
{
using var _ = dmsg;
Message msg = dmsg.Message;

FormatMessage(sb, msg);

Print(sb);

sb.Clear();
}
}

static void Print(StringBuilder sb)
{
foreach (var chunk in sb.GetChunks())
{
Console.Write(chunk);
}
}

private const string ItemPrefix = "- ";

static void FormatMessage(StringBuilder sb, Message msg)
{
switch (msg.MessageType)
{
case MessageType.MethodCall:
sb.Append("CAL ");
break;
case MessageType.MethodReturn:
sb.Append("RET ");
break;
case MessageType.Error:
sb.Append("ERR ");
break;
case MessageType.Signal:
sb.Append("SIG ");
break;
default:
throw new ArgumentOutOfRangeException(msg.MessageType.ToString());
}

sb.Append(msg.SenderAsString);
sb.Append($"({msg.Serial})");
sb.Append("->");
if (msg.Destination.Length > 0)
{
sb.Append(msg.DestinationAsString);
}
if (msg.ReplySerial.HasValue)
{
sb.Append($"({msg.ReplySerial.Value})");
}
sb.Append(' ');

if (msg.Path.Length > 0)
{
sb.Append(msg.PathAsString);
sb.Append(' ');
}

if (msg.Interface.Length > 0)
{
sb.Append(msg.InterfaceAsString);
sb.Append('.');
}
if (msg.Member.Length > 0)
{
sb.Append(msg.MemberAsString);
sb.Append(' ');
}

if (msg.Signature.Length > 0)
{
sb.Append(msg.SignatureAsString);
}
sb.AppendLine();

SignatureReader sigReader = new(msg.Signature);
Reader reader = msg.GetBodyReader();
while (sigReader.TryRead(out DBusType type, out ReadOnlySpan<byte> innerSignature))
{
sb.Append(" ");
sb.Append(ItemPrefix);
AppendValue(sb, indent: 4, ref reader, type, innerSignature);
}
}

static void AppendValue(StringBuilder sb, int indent, ref Reader reader, DBusType type, ReadOnlySpan<byte> innerSignature, bool addNewLine = true, bool isDictEntryValue = false)
{
SignatureReader sigReader;
switch (type)
{
case DBusType.Byte:
sb.Append(reader.ReadByte());
break;
case DBusType.Bool:
sb.Append(reader.ReadBool());
break;
case DBusType.Int16:
sb.Append(reader.ReadInt16());
break;
case DBusType.UInt16:
sb.Append(reader.ReadUInt16());
break;
case DBusType.Int32:
sb.Append(reader.ReadInt32());
break;
case DBusType.UInt32:
sb.Append(reader.ReadUInt32());
break;
case DBusType.Int64:
sb.Append(reader.ReadInt64());
break;
case DBusType.UInt64:
sb.Append(reader.ReadUInt64());
break;
case DBusType.Double:
sb.Append(reader.ReadDouble());
break;
case DBusType.String:
sb.Append(reader.ReadString());
break;
case DBusType.ObjectPath:
sb.Append(reader.ReadObjectPath());
break;
case DBusType.Signature:
sb.Append(reader.ReadSignatureAsString());
break;
case DBusType.UnixFd:
sb.Append(reader.ReadHandleRaw());
break;
case DBusType.Array:
sigReader = new(innerSignature);
sigReader.TryRead(out type, out innerSignature);
bool isDictionary = type == DBusType.DictEntry;

// Print these types on a single line.
bool printSingleLine = type is DBusType.Byte or
DBusType.Bool or
DBusType.Int16 or
DBusType.UInt16 or
DBusType.Int32 or
DBusType.UInt32 or
DBusType.Int64 or
DBusType.UInt64 or
DBusType.Double or
DBusType.UnixFd;

// Only print first 16 elements of an array.
int remaining = isDictionary ? int.MaxValue : 16;

ArrayEnd arrayEnd = reader.ReadArrayStart(type);
bool isEmpty = true;
while (reader.HasNext(arrayEnd))
{
if (printSingleLine)
{
if (isEmpty) // first
{
sb.Append("[");
}
else
{
sb.Append(", ");
}
}
else
{
if (isEmpty) // first
{
if (isDictEntryValue)
{
sb.AppendLine();
Indent(sb, indent);
}
}
else
{
Indent(sb, indent);
}

if (!isDictionary)
{
sb.Append(ItemPrefix);
}
}
isEmpty = false;

if (remaining-- == 0)
{
if (printSingleLine)
{
sb.Append("...");
}
else
{
sb.AppendLine("...");
}
reader.SkipTo(arrayEnd);
break;
}

AppendValue(sb, isDictionary ? indent : indent + ItemPrefix.Length, ref reader, type, innerSignature, addNewLine: !printSingleLine);
}
if (isEmpty)
{
sb.AppendLine(isDictionary ? "{}" : "[]");
}
else if (printSingleLine)
{
sb.AppendLine("]");
}

addNewLine = false;
break;
case DBusType.Struct:
if (isDictEntryValue)
{
sb.AppendLine();
Indent(sb, indent);
}

reader.AlignStruct();
sigReader = new(innerSignature);

bool isFirst = true;
while (sigReader.TryRead(out type, out innerSignature))
{
if (!isFirst)
{
Indent(sb, indent);
}
isFirst = false;

sb.Append(ItemPrefix);
AppendValue(sb, indent + ItemPrefix.Length, ref reader, type, innerSignature);
}

addNewLine = false;
break;
case DBusType.Variant:
innerSignature = reader.ReadSignature().Span;
sigReader = new(innerSignature);
sigReader.TryRead(out type, out innerSignature);
AppendValue(sb, indent, ref reader, type, innerSignature, isDictEntryValue: isDictEntryValue);

addNewLine = false;
break;
case DBusType.DictEntry:
reader.AlignStruct();
sigReader = new(innerSignature);

sigReader.TryRead(out type, out innerSignature);
AppendValue(sb, indent, ref reader, type, innerSignature, addNewLine: false);

sb.Append(": ");

sigReader.TryRead(out type, out innerSignature);
AppendValue(sb, indent + 2, ref reader, type, innerSignature, isDictEntryValue: true);

addNewLine = false;
break;
default:
throw new InvalidOperationException();
}

if (addNewLine)
{
sb.AppendLine();
}

static void Indent(StringBuilder sb, int indent)
{
sb.Append(' ', indent);
}
}
}
4 changes: 3 additions & 1 deletion src/Tmds.DBus.Tool/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.Extensions.CommandLineUtils;
using System.Threading;
using Microsoft.Extensions.CommandLineUtils;

namespace Tmds.DBus.Tool
{
Expand All @@ -11,6 +12,7 @@ public static void Main(string[] args)
commandLineApp.HelpOption(Command.HelpTemplate);
new CodeGenCommand(commandLineApp);
new ListCommand(commandLineApp);
new MonitorCommand(commandLineApp);
commandLineApp.OnExecute(() => { commandLineApp.ShowHelp(); return 0; });
commandLineApp.Execute(args);
}
Expand Down

0 comments on commit f9b4853

Please sign in to comment.