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