Added FileDescriptor support

Some DBus methods return file descriptors, such as
"org.freedesktop.login1.Manager.Inhibit". This commit adds support for
these.

I followed the same enum type defining pattern I've seen in the code,
such as with InterfaceNames and BusNames, to prevent integers and
fileDescriptors implicitly casting to each other.

Note that it is kinda hard to test with reading and writing
fileDescriptors, as DBus will duplicate the file descriptor
when reading, leading to relatively unpredicatable behaviour.

I also must admit I focussed most of my attention to reading file
descriptors and less to writing file descriptors, as I don't know a good
way to test those.
This commit is contained in:
Chris Josten 2021-01-22 00:20:43 +01:00
parent ab994acdb3
commit 3b1cc9b453
3 changed files with 31 additions and 5 deletions

View file

@ -172,7 +172,7 @@ void buildIter(TS...)(DBusMessageIter* iter, TS args)
} }
T readIter(T)(DBusMessageIter* iter) T readIter(T)(DBusMessageIter* iter)
if (is(T == enum) && !is(T == InterfaceName) && !is(T == BusName)) { if (is(T == enum) && !is(T == InterfaceName) && !is(T == BusName) && !is(T == FileDescriptor)) {
import std.algorithm.searching : canFind; import std.algorithm.searching : canFind;
alias OriginalType!T B; alias OriginalType!T B;
@ -198,7 +198,8 @@ T readIter(T)(DBusMessageIter* iter)
} }
T readIter(T)(DBusMessageIter* iter) T readIter(T)(DBusMessageIter* iter)
if (!(is(T == enum) && !is(T == InterfaceName) && !is(T == BusName)) && !isInstanceOf!(BitFlags, T) && canDBus!T) { if (!(is(T == enum) && !is(T == InterfaceName) && !is(T == BusName) && !is(T == FileDescriptor))
&& !isInstanceOf!(BitFlags, T) && canDBus!T) {
auto argType = dbus_message_iter_get_arg_type(iter); auto argType = dbus_message_iter_get_arg_type(iter);
T ret; T ret;

View file

@ -171,6 +171,17 @@ InterfaceName interfaceName(string path) pure @nogc nothrow @safe {
return cast(InterfaceName) path; return cast(InterfaceName) path;
} }
/// Serving as a typesafe alias for a FileDescriptor.
enum FileDescriptor : int {
none = -1,
max = int.max
}
/// Casts an integer to a FileDescriptor.
FileDescriptor fileDescriptor(int fileNo) pure @nogc nothrow @safe {
return cast(FileDescriptor) fileNo;
}
unittest { unittest {
import dunit.toolkit; import dunit.toolkit;
@ -284,6 +295,9 @@ struct DBusAny {
} else static if (is(T == int)) { } else static if (is(T == int)) {
this(typeCode!int, null, false); this(typeCode!int, null, false);
int32 = cast(int) value; int32 = cast(int) value;
} else static if (is(T == FileDescriptor)) {
this(typeCode!FileDescriptor, null, false);
int32 = cast(int) value;
} else static if (is(T == uint)) { } else static if (is(T == uint)) {
this(typeCode!uint, null, false); this(typeCode!uint, null, false);
uint32 = cast(uint) value; uint32 = cast(uint) value;
@ -459,6 +473,9 @@ struct DBusAny {
case 'e': case 'e':
valueStr = entry.key.toString ~ ": " ~ entry.value.toString; valueStr = entry.key.toString ~ ": " ~ entry.value.toString;
break; break;
case 'h':
valueStr = int32.to!string;
break;
default: default:
valueStr = "unknown"; valueStr = "unknown";
break; break;
@ -488,7 +505,9 @@ struct DBusAny {
"Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ typeSig ~ "'.", "Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ typeSig ~ "'.",
typeCode!T, type)); typeCode!T, type));
static if (isIntegral!T) { static if (is(T == FileDescriptor)) {
return fileDescriptor(int32);
} else static if (isIntegral!T) {
enum memberName = (isUnsigned!T ? "uint" : "int") ~ (T.sizeof * 8).to!string; enum memberName = (isUnsigned!T ? "uint" : "int") ~ (T.sizeof * 8).to!string;
return __traits(getMember, this, memberName); return __traits(getMember, this, memberName);
} else static if (is(T == double)) { } else static if (is(T == double)) {
@ -730,6 +749,7 @@ unittest {
test(variant(cast(short) 184), set!"int16"(DBusAny('n', null, true), cast(short) 184)); test(variant(cast(short) 184), set!"int16"(DBusAny('n', null, true), cast(short) 184));
test(variant(cast(ushort) 184), set!"uint16"(DBusAny('q', null, true), cast(ushort) 184)); test(variant(cast(ushort) 184), set!"uint16"(DBusAny('q', null, true), cast(ushort) 184));
test(variant(cast(int) 184), set!"int32"(DBusAny('i', null, true), cast(int) 184)); test(variant(cast(int) 184), set!"int32"(DBusAny('i', null, true), cast(int) 184));
test(variant(cast(FileDescriptor) 184), set!"int32"(DBusAny('h', null, true), cast(FileDescriptor) 184));
test(variant(cast(uint) 184), set!"uint32"(DBusAny('u', null, true), cast(uint) 184)); test(variant(cast(uint) 184), set!"uint32"(DBusAny('u', null, true), cast(uint) 184));
test(variant(cast(long) 184), set!"int64"(DBusAny('x', null, true), cast(long) 184)); test(variant(cast(long) 184), set!"int64"(DBusAny('x', null, true), cast(long) 184));
test(variant(cast(ulong) 184), set!"uint64"(DBusAny('t', null, true), cast(ulong) 184)); test(variant(cast(ulong) 184), set!"uint64"(DBusAny('t', null, true), cast(ulong) 184));

View file

@ -55,7 +55,7 @@ template allCanDBus(TS...) {
+/ +/
package // Don't add to the API yet, 'cause I intend to move it later package // Don't add to the API yet, 'cause I intend to move it later
alias BasicTypes = AliasSeq!(bool, byte, short, ushort, int, uint, long, ulong, alias BasicTypes = AliasSeq!(bool, byte, short, ushort, int, uint, long, ulong,
double, string, ObjectPath, InterfaceName, BusName); double, string, ObjectPath, InterfaceName, BusName, FileDescriptor);
template basicDBus(T) { template basicDBus(T) {
static if (staticIndexOf!(T, BasicTypes) >= 0) { static if (staticIndexOf!(T, BasicTypes) >= 0) {
@ -104,6 +104,7 @@ unittest {
(canDBus!(Tuple!(int[], bool, Variant!short))).assertTrue(); (canDBus!(Tuple!(int[], bool, Variant!short))).assertTrue();
(canDBus!(Tuple!(int[], int[string]))).assertTrue(); (canDBus!(Tuple!(int[], int[string]))).assertTrue();
(canDBus!(int[string])).assertTrue(); (canDBus!(int[string])).assertTrue();
(canDBus!FileDescriptor).assertTrue();
} }
string typeSig(T)() string typeSig(T)()
@ -116,6 +117,8 @@ string typeSig(T)()
return "n"; return "n";
} else static if (is(T == ushort)) { } else static if (is(T == ushort)) {
return "q"; return "q";
} else static if (is(T == FileDescriptor)) {
return "h";
} else static if (is(T == int)) { } else static if (is(T == int)) {
return "i"; return "i";
} else static if (is(T == uint)) { } else static if (is(T == uint)) {
@ -217,6 +220,7 @@ unittest {
typeSig!bool().assertEqual("b"); typeSig!bool().assertEqual("b");
typeSig!string().assertEqual("s"); typeSig!string().assertEqual("s");
typeSig!(Variant!int)().assertEqual("v"); typeSig!(Variant!int)().assertEqual("v");
typeSig!FileDescriptor().assertEqual("h");
// enums // enums
enum E : byte { enum E : byte {
a, a,
@ -256,9 +260,10 @@ unittest {
string d; string d;
S1 e; S1 e;
uint f; uint f;
FileDescriptor g;
} }
typeSig!S2.assertEqual("(vs(ids)u)"); typeSig!S2.assertEqual("(vs(ids)uh)");
// arrays // arrays
typeSig!(int[]).assertEqual("ai"); typeSig!(int[]).assertEqual("ai");
typeSig!(Variant!int[]).assertEqual("av"); typeSig!(Variant!int[]).assertEqual("av");