]> git.meshlink.io Git - catta/blob - avahi-sharp/Client.cs
fix avahi_netlink_new to allow multiple netlinks per process
[catta] / avahi-sharp / Client.cs
1 /***
2   This file is part of avahi.
3
4   avahi is free software; you can redistribute it and/or modify it
5   under the terms of the GNU Lesser General Public License as
6   published by the Free Software Foundation; either version 2.1 of the
7   License, or (at your option) any later version.
8
9   avahi is distributed in the hope that it will be useful, but WITHOUT
10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12   Public License for more details.
13
14   You should have received a copy of the GNU Lesser General Public
15   License along with avahi; if not, write to the Free Software
16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17   USA.
18 ***/
19
20
21 using System;
22 using System.Threading;
23 using System.Collections;
24 using System.Runtime.InteropServices;
25 using Mono.Unix;
26 using Mono.Unix.Native;
27
28 using Stdlib = Mono.Unix.Native.Stdlib;
29
30 namespace Avahi
31 {
32     internal enum ResolverEvent {
33         Found,
34         Failure
35     }
36
37     internal enum BrowserEvent {
38         Added,
39         Removed,
40         CacheExhausted,
41         AllForNow,
42         Failure
43     }
44
45     internal delegate int PollCallback (IntPtr ufds, uint nfds, int timeout);
46     internal delegate void ClientCallback (IntPtr client, ClientState state, IntPtr userData);
47
48     public delegate void ClientStateHandler (object o, ClientStateArgs state);
49
50     public class ClientStateArgs : EventArgs
51     {
52         private ClientState state;
53         private ErrorCode error;
54
55         public ClientState State
56         {
57             get { return state; }
58         }
59
60         public ErrorCode Error
61         {
62             get { return error; }
63         }
64
65         public ClientStateArgs (ClientState state, ErrorCode error)
66         {
67             this.state = state;
68             this.error = error;
69         }
70     }
71
72     public enum Protocol {
73         Unspecified = -1,
74         IPv4 = 0,
75         IPv6 = 1
76     }
77
78     internal enum ServerState {
79         Invalid,
80         Registering,
81         Running,
82         Collision
83     }
84
85     public enum ClientState {
86         Registering = ServerState.Registering,
87         Running = ServerState.Running,
88         Collision = ServerState.Collision,
89         Failure = 100,
90         Connecting = 101
91     }
92
93     [Flags]
94     public enum LookupFlags {
95         None = 0,
96         UseWideArea = 1,
97         UseMulticast = 2,
98         NoTxt = 4,
99         NoAddress = 8
100     }
101
102     [Flags]
103     public enum LookupResultFlags {
104         None = 0,
105         Cached = 1,
106         WideArea = 2,
107         Multicast = 4,
108         Local = 8,
109         OurOwn = 16,
110     }
111
112     [Flags]
113     public enum ClientFlags {
114         None = 0,
115         IgnoreUserConfig = 1,
116         NoFail = 2
117     }
118
119     public class Client : IDisposable
120     {
121         private IntPtr handle;
122         private ClientCallback cb;
123         private PollCallback pollcb;
124         private IntPtr spoll;
125
126         private Thread thread;
127
128         [DllImport ("avahi-client")]
129         private static extern IntPtr avahi_client_new (IntPtr poll, ClientFlags flags, ClientCallback handler,
130                                                        IntPtr userData, out int error);
131
132         [DllImport ("avahi-client")]
133         private static extern void avahi_client_free (IntPtr handle);
134
135         [DllImport ("avahi-client")]
136         private static extern IntPtr avahi_client_get_version_string (IntPtr handle);
137
138         [DllImport ("avahi-client")]
139         private static extern IntPtr avahi_client_get_host_name (IntPtr handle);
140
141         [DllImport ("avahi-client")]
142         private static extern IntPtr avahi_client_get_domain_name (IntPtr handle);
143
144         [DllImport ("avahi-client")]
145         private static extern IntPtr avahi_client_get_host_name_fqdn (IntPtr handle);
146
147         [DllImport ("avahi-client")]
148         private static extern ClientState avahi_client_get_state (IntPtr handle);
149
150         [DllImport ("avahi-client")]
151         private static extern int avahi_client_errno (IntPtr handle);
152
153         [DllImport ("avahi-common")]
154         private static extern IntPtr avahi_simple_poll_new ();
155
156         [DllImport ("avahi-common")]
157         private static extern IntPtr avahi_simple_poll_get (IntPtr spoll);
158
159         [DllImport ("avahi-common")]
160         private static extern void avahi_simple_poll_free (IntPtr spoll);
161
162         [DllImport ("avahi-common")]
163         private static extern int avahi_simple_poll_loop (IntPtr spoll);
164
165         [DllImport ("avahi-common")]
166         private static extern void avahi_simple_poll_set_func (IntPtr spoll, PollCallback cb);
167
168         [DllImport ("avahi-common")]
169         private static extern void avahi_simple_poll_quit (IntPtr spoll);
170
171         [DllImport ("avahi-client")]
172         private static extern uint avahi_client_get_local_service_cookie (IntPtr client);
173
174         [DllImport ("avahi-common")]
175         private static extern int avahi_service_name_join (IntPtr buf, int len, byte[] name, byte[] type,
176                                                            byte[] domain);
177
178         [DllImport ("avahi-common")]
179         private static extern int avahi_service_name_split (byte[] service, IntPtr name, int name_len,
180                                                             IntPtr type, int type_len,
181                                                             IntPtr domain, int domain_len);
182
183
184         [DllImport ("libc")]
185         private static extern int poll(IntPtr ufds, uint nfds, int timeout);
186
187         public event ClientStateHandler StateChanged;
188
189         internal IntPtr Handle
190         {
191             get { return handle; }
192         }
193
194         public string Version
195         {
196             get {
197                 lock (this) {
198                     return Utility.PtrToString (avahi_client_get_version_string (handle));
199                 }
200             }
201         }
202
203         public string HostName
204         {
205             get {
206                 lock (this) {
207                     return Utility.PtrToString (avahi_client_get_host_name (handle));
208                 }
209             }
210         }
211
212         public string DomainName
213         {
214             get {
215                 lock (this) {
216                     return Utility.PtrToString (avahi_client_get_domain_name (handle));
217                 }
218             }
219         }
220
221         public string HostNameFqdn
222         {
223             get {
224                 lock (this) {
225                     return Utility.PtrToString (avahi_client_get_host_name_fqdn (handle));
226                 }
227             }
228         }
229
230         public ClientState State
231         {
232             get {
233                 lock (this) {
234                     return (ClientState) avahi_client_get_state (handle);
235                 }
236             }
237         }
238
239         public uint LocalServiceCookie
240         {
241             get {
242                 lock (this) {
243                     return avahi_client_get_local_service_cookie (handle);
244                 }
245             }
246         }
247
248         internal ErrorCode LastError
249         {
250             get {
251                 lock (this) {
252                     return (ErrorCode) avahi_client_errno (handle);
253                 }
254             }
255         }
256
257         public Client (ClientFlags flags)
258         {
259             spoll = avahi_simple_poll_new ();
260
261             pollcb = OnPollCallback;
262             avahi_simple_poll_set_func (spoll, pollcb);
263             IntPtr poll = avahi_simple_poll_get (spoll);
264             cb = OnClientCallback;
265
266             int error;
267             handle = avahi_client_new (poll, flags, cb, IntPtr.Zero, out error);
268             if (error != 0)
269                 throw new ClientException (error);
270
271             thread = new Thread (PollLoop);
272             thread.IsBackground = true;
273             thread.Start ();
274         }
275
276         public Client () : this (ClientFlags.None) {
277         }
278
279         ~Client ()
280         {
281             Dispose ();
282         }
283
284         public void Dispose ()
285         {
286             if (handle != IntPtr.Zero) {
287                 lock (this) {
288                     avahi_client_free (handle);
289                     handle = IntPtr.Zero;
290
291                     avahi_simple_poll_quit (spoll);
292                     Monitor.Wait (this);
293
294                     avahi_simple_poll_free (spoll);
295                 }
296             }
297         }
298
299         public static string JoinServiceName (string name, string type, string domain)
300         {
301             int len = 4 * (name.Length + type.Length + domain.Length) + 4;
302             IntPtr buf = Stdlib.malloc ((ulong) len);
303
304             int ret = avahi_service_name_join (buf, len,
305                                                Utility.StringToBytes (name),
306                                                Utility.StringToBytes (type),
307                                                Utility.StringToBytes (domain));
308
309             if (ret < 0) {
310                 Utility.Free (buf);
311                 return null; // FIXME, should throw exception
312             }
313
314             string service = Utility.PtrToString (buf);
315             Utility.Free (buf);
316
317             return service;
318         }
319
320         public static void SplitServiceName (string service, out string name, out string type, out string domain)
321         {
322             int len = 1024;
323
324             IntPtr namePtr = Stdlib.malloc ((ulong) len);
325             IntPtr typePtr = Stdlib.malloc ((ulong) len);
326             IntPtr domainPtr = Stdlib.malloc ((ulong) len);
327
328             int ret = avahi_service_name_split (Utility.StringToBytes (service), namePtr, len, typePtr, len,
329                                                 domainPtr, len);
330
331             if (ret < 0) {
332                 Utility.Free (namePtr);
333                 Utility.Free (typePtr);
334                 Utility.Free (domainPtr);
335
336                 name = null;
337                 type = null;
338                 domain = null;
339                 return;
340             }
341
342             name = Utility.PtrToString (namePtr);
343             type = Utility.PtrToString (typePtr);
344             domain = Utility.PtrToString (domainPtr);
345
346             Utility.Free (namePtr);
347             Utility.Free (typePtr);
348             Utility.Free (domainPtr);
349         }
350
351         internal void ThrowError ()
352         {
353             ErrorCode error = LastError;
354
355             if (error != ErrorCode.Ok)
356                 throw new ClientException (error);
357         }
358
359         private void OnClientCallback (IntPtr client, ClientState state, IntPtr userData)
360         {
361             if (StateChanged != null)
362                 StateChanged (this, new ClientStateArgs (state, LastError));
363         }
364
365         private int OnPollCallback (IntPtr ufds, uint nfds, int timeout) {
366             Monitor.Exit (this);
367             int result = poll (ufds, nfds, timeout);
368             Monitor.Enter (this);
369             return result;
370         }
371
372         private void PollLoop () {
373             try {
374                 lock (this) {
375                     avahi_simple_poll_loop (spoll);
376                     Monitor.Pulse (this);
377                 }
378             } catch (Exception e) {
379                 Console.Error.WriteLine ("Error in avahi-sharp event loop: " + e);
380             }
381         }
382     }
383 }