1
// Copyright 2006 Alp Toker <alp@atoker.com>
2
// This software is made available under the MIT License
3
// See COPYING for details
6
using System.Collections.Generic;
8
using System.Threading;
9
using System.Reflection;
16
public partial class Connection
19
internal Transport Transport {
24
transport.Connection = this;
28
protected Connection () {}
30
internal Connection (Transport transport)
32
this.transport = transport;
33
transport.Connection = this;
36
//should this be public?
37
internal Connection (string address)
39
OpenPrivate (address);
43
internal bool isConnected = false;
44
public bool IsConnected
51
// TODO: Complete disconnection support
52
internal bool isShared = false;
56
throw new Exception ("Cannot disconnect a shared Connection");
61
transport.Disconnect ();
65
//should we do connection sharing here?
66
public static Connection Open (string address)
68
Connection conn = new Connection ();
69
conn.OpenPrivate (address);
75
internal void OpenPrivate (string address)
78
throw new ArgumentNullException ("address");
80
AddressEntry[] entries = Address.Parse (address);
81
if (entries.Length == 0)
82
throw new Exception ("No addresses were found");
84
//TODO: try alternative addresses if needed
85
AddressEntry entry = entries[0];
88
Transport = Transport.Create (entry);
92
internal UUID Id = UUID.Zero;
96
if (transport != null)
97
transport.WriteCred ();
99
SaslClient auth = new SaslClient ();
100
auth.Identity = transport.AuthString ();
101
auth.stream = transport.Stream;
102
auth.Peer = new SaslPeer ();
103
auth.Peer.Peer = auth;
104
auth.Peer.stream = transport.Stream;
106
if (!auth.Authenticate ())
107
throw new Exception ("Authentication failure");
110
if (auth.ActualId != Id)
111
throw new Exception ("Authentication failure: Unexpected GUID");
116
isAuthenticated = true;
118
//(((SocketTransport)Transport).socket).Blocking = false;
121
internal bool isAuthenticated = false;
122
internal bool IsAuthenticated
125
return isAuthenticated;
129
//Interlocked.Increment() handles the overflow condition for uint correctly, so it's ok to store the value as an int but cast it to uint
131
internal uint GenerateSerial ()
134
return (uint)Interlocked.Increment (ref serial);
137
internal Message SendWithReplyAndBlock (Message msg)
139
PendingCall pending = SendWithReply (msg);
140
return pending.Reply;
143
internal PendingCall SendWithReply (Message msg)
145
msg.ReplyExpected = true;
147
if (msg.Header.Serial == 0)
148
msg.Header.Serial = GenerateSerial ();
150
// Should we throttle the maximum number of concurrent PendingCalls?
151
// Should we support timeouts?
152
PendingCall pending = new PendingCall (this);
153
pendingCalls[msg.Header.Serial] = pending;
156
//WriteMessage (msg);
161
internal virtual uint Send (Message msg)
163
if (msg.Header.Serial == 0)
164
msg.Header.Serial = GenerateSerial ();
166
transport.WriteMessage (msg);
168
//Outbound.Enqueue (msg);
172
return msg.Header.Serial;
175
Queue<Message> Inbound = new Queue<Message> ();
177
Queue<Message> Outbound = new Queue<Message> ();
181
//should just iterate the enumerator here
182
while (Outbound.Count != 0) {
183
Message msg = Outbound.Dequeue ();
188
public bool ReadWrite (int timeout_milliseconds)
195
public bool ReadWrite ()
197
return ReadWrite (-1);
200
public bool Dispatch ()
203
Message msg = Inbound.Dequeue ();
204
//HandleMessage (msg);
209
public bool ReadWriteDispatch (int timeout_milliseconds)
215
public bool ReadWriteDispatch ()
217
return ReadWriteDispatch (-1);
222
internal void DispatchSignals ()
225
while (Inbound.Count != 0) {
226
Message msg = Inbound.Dequeue ();
232
internal Thread mainThread = Thread.CurrentThread;
235
public void Iterate ()
237
mainThread = Thread.CurrentThread;
239
//Message msg = Inbound.Dequeue ();
240
Message msg = transport.ReadMessage ();
246
internal void Dispatch ()
248
while (transport.Inbound.Count != 0) {
249
Message msg = transport.Inbound.Dequeue ();
255
internal virtual void HandleMessage (Message msg)
260
//TODO: support disconnection situations properly and move this check elsewhere
262
throw new ArgumentNullException ("msg", "Cannot handle a null message; maybe the bus was disconnected");
264
//TODO: Restrict messages to Local ObjectPath?
267
object field_value = msg.Header[FieldCode.ReplySerial];
268
if (field_value != null) {
269
uint reply_serial = (uint)field_value;
272
if (pendingCalls.TryGetValue (reply_serial, out pending)) {
273
if (pendingCalls.Remove (reply_serial))
279
//we discard reply messages with no corresponding PendingCall
280
if (Protocol.Verbose)
281
Console.Error.WriteLine ("Unexpected reply message received: MessageType='" + msg.Header.MessageType + "', ReplySerial=" + reply_serial);
287
switch (msg.Header.MessageType) {
288
case MessageType.MethodCall:
289
MethodCall method_call = new MethodCall (msg);
290
HandleMethodCall (method_call);
292
case MessageType.Signal:
293
//HandleSignal (msg);
295
Inbound.Enqueue (msg);
297
case MessageType.Error:
298
//TODO: better exception handling
299
Error error = new Error (msg);
300
string errMsg = String.Empty;
301
if (msg.Signature.Value.StartsWith ("s")) {
302
MessageReader reader = new MessageReader (msg);
303
errMsg = reader.ReadString ();
305
//throw new Exception ("Remote Error: Signature='" + msg.Signature.Value + "' " + error.ErrorName + ": " + errMsg);
306
//if (Protocol.Verbose)
307
Console.Error.WriteLine ("Remote Error: Signature='" + msg.Signature.Value + "' " + error.ErrorName + ": " + errMsg);
309
case MessageType.Invalid:
311
throw new Exception ("Invalid message received: MessageType='" + msg.Header.MessageType + "'");
315
Dictionary<uint,PendingCall> pendingCalls = new Dictionary<uint,PendingCall> ();
317
//this might need reworking with MulticastDelegate
318
internal void HandleSignal (Message msg)
320
Signal signal = new Signal (msg);
322
//TODO: this is a hack, not necessary when MatchRule is complete
323
MatchRule rule = new MatchRule ();
324
rule.MessageType = MessageType.Signal;
325
rule.Fields.Add (FieldCode.Interface, new MatchTest (signal.Interface));
326
rule.Fields.Add (FieldCode.Member, new MatchTest (signal.Member));
327
rule.Fields.Add (FieldCode.Path, new MatchTest (signal.Path));
330
if (Handlers.TryGetValue (rule, out dlg)) {
331
MethodInfo mi = dlg.GetType ().GetMethod ("Invoke");
333
bool compatible = false;
334
Signature inSig, outSig;
336
if (TypeImplementer.SigsForMethod(mi, out inSig, out outSig))
337
if (outSig == Signature.Empty && inSig == msg.Signature)
341
if (Protocol.Verbose)
342
Console.Error.WriteLine ("Signal argument mismatch: " + signal.Interface + '.' + signal.Member);
346
//signals have no return value
347
dlg.DynamicInvoke (MessageHelper.GetDynamicValues (msg, mi.GetParameters ()));
349
//TODO: how should we handle this condition? sending an Error may not be appropriate in this case
350
if (Protocol.Verbose)
351
Console.Error.WriteLine ("Warning: No signal handler for " + signal.Member);
355
internal Dictionary<MatchRule,Delegate> Handlers = new Dictionary<MatchRule,Delegate> ();
358
internal void MaybeSendUnknownMethodError (MethodCall method_call)
360
Message msg = MessageHelper.CreateUnknownMethodError (method_call);
365
//not particularly efficient and needs to be generalized
366
internal void HandleMethodCall (MethodCall method_call)
368
//TODO: Ping and Introspect need to be abstracted and moved somewhere more appropriate once message filter infrastructure is complete
370
//FIXME: these special cases are slightly broken for the case where the member but not the interface is specified in the message
371
if (method_call.Interface == "org.freedesktop.DBus.Peer") {
372
switch (method_call.Member) {
374
Send (MessageHelper.ConstructReply (method_call));
377
if (MachineId != UUID.Zero) {
378
Send (MessageHelper.ConstructReply (method_call, MachineId.ToString ()));
381
// Might want to send back an error here?
387
if (method_call.Interface == "org.freedesktop.DBus.Introspectable" && method_call.Member == "Introspect") {
388
Introspector intro = new Introspector ();
389
intro.root_path = method_call.Path;
392
//FIXME: do this properly
393
//this is messy and inefficient
394
List<string> linkNodes = new List<string> ();
395
int depth = method_call.Path.Decomposed.Length;
396
foreach (ObjectPath pth in RegisteredObjects.Keys) {
397
if (pth.Value == (method_call.Path.Value)) {
398
ExportObject exo = (ExportObject)RegisteredObjects[pth];
399
exo.WriteIntrospect (intro);
401
for (ObjectPath cur = pth ; cur != null ; cur = cur.Parent) {
402
if (cur.Value == method_call.Path.Value) {
403
string linkNode = pth.Decomposed[depth];
404
if (!linkNodes.Contains (linkNode)) {
405
intro.WriteNode (linkNode);
406
linkNodes.Add (linkNode);
415
Message reply = MessageHelper.ConstructReply (method_call, intro.xml);
421
if (RegisteredObjects.TryGetValue (method_call.Path, out bo)) {
422
ExportObject eo = (ExportObject)bo;
423
eo.HandleMethodCall (method_call);
425
MaybeSendUnknownMethodError (method_call);
429
Dictionary<ObjectPath,BusObject> RegisteredObjects = new Dictionary<ObjectPath,BusObject> ();
431
//FIXME: this shouldn't be part of the core API
432
//that also applies to much of the other object mapping code
434
public object GetObject (Type type, string bus_name, ObjectPath path)
437
// return GetObject (bus_name, path);
439
//if the requested type is an interface, we can implement it efficiently
440
//otherwise we fall back to using a transparent proxy
441
if (type.IsInterface || type.IsAbstract) {
442
return BusObject.GetObject (this, bus_name, path, type);
444
if (Protocol.Verbose)
445
Console.Error.WriteLine ("Warning: Note that MarshalByRefObject use is not recommended; for best performance, define interfaces");
447
BusObject busObject = new BusObject (this, bus_name, path);
448
DProxy prox = new DProxy (busObject, type);
449
return prox.GetTransparentProxy ();
453
public T GetObject<T> (string bus_name, ObjectPath path)
455
return (T)GetObject (typeof (T), bus_name, path);
458
[Obsolete ("Use the overload of Register() which does not take a bus_name parameter")]
459
public void Register (string bus_name, ObjectPath path, object obj)
461
Register (path, obj);
464
[Obsolete ("Use the overload of Unregister() which does not take a bus_name parameter")]
465
public object Unregister (string bus_name, ObjectPath path)
467
return Unregister (path);
470
public void Register (ObjectPath path, object obj)
472
ExportObject eo = ExportObject.CreateExportObject (this, path, obj);
473
eo.Registered = true;
475
//TODO: implement some kind of tree data structure or internal object hierarchy. right now we are ignoring the name and putting all object paths in one namespace, which is bad
476
RegisteredObjects[path] = eo;
479
public object Unregister (ObjectPath path)
483
if (!RegisteredObjects.TryGetValue (path, out bo))
484
throw new Exception ("Cannot unregister " + path + " as it isn't registered");
486
RegisteredObjects.Remove (path);
488
ExportObject eo = (ExportObject)bo;
489
eo.Registered = false;
494
//these look out of place, but are useful
495
internal protected virtual void AddMatch (string rule)
499
internal protected virtual void RemoveMatch (string rule)
503
// Maybe we should use XDG/basedir or check an env var for this?
504
const string machineUuidFilename = @"/var/lib/dbus/machine-id";
505
static UUID? machineId = null;
506
private static object idReadLock = new object ();
507
internal static UUID MachineId
511
if (machineId != null)
512
return (UUID)machineId;
514
machineId = ReadMachineId (machineUuidFilename);
516
machineId = UUID.Zero;
518
return (UUID)machineId;
523
static UUID ReadMachineId (string fname)
525
using (FileStream fs = File.OpenRead (fname)) {
526
// Length is typically 33 (32 for the UUID, plus a linefeed)
527
//if (fs.Length < 32)
530
byte[] data = new byte[32];
533
while (pos < data.Length) {
534
int read = fs.Read (data, pos, data.Length - pos);
540
if (pos != data.Length)
542
throw new Exception ("Insufficient data while reading GUID string");
544
return UUID.Parse (System.Text.Encoding.ASCII.GetString (data));
550
[DllImport ("advapi32.dll", SetLastError = true)]
551
static extern bool GetCurrentHwProfile (IntPtr fProfile);
553
[StructLayout (LayoutKind.Sequential)]
554
private class HWProfile
556
public Int32 dwDockInfo;
557
[MarshalAs (UnmanagedType.ByValTStr, SizeConst = 39)]
558
public string szHwProfileGuid;
559
[MarshalAs (UnmanagedType.ByValTStr, SizeConst = 80)]
560
public string szHwProfileName;
563
static UUID ReadMachineIdWin32 ()
565
// Completely untested
568
IntPtr lHWInfoPtr = Marshal.AllocHGlobal (123);
569
HWProfile lProfile = new HWProfile ();
570
Marshal.StructureToPtr (lProfile, lHWInfoPtr, false);
571
if (GetCurrentHwProfile (lHWInfoPtr)) {
572
Marshal.PtrToStructure (lHWInfoPtr, lProfile);
573
lText = lProfile.szHwProfileGuid.ToString ();
575
Marshal.FreeHGlobal (lHWInfoPtr);
577
string uuidString = lText.Replace ("-", String.Empty).Substring (1, 32);
578
return UUID.Parse (uuidString);
584
if (BitConverter.IsLittleEndian)
585
NativeEndianness = EndianFlag.Little;
587
NativeEndianness = EndianFlag.Big;
590
internal static readonly EndianFlag NativeEndianness;