Merge pull request #1388 from schani/fix-23401
[mono.git] / mcs / class / Mono.Posix / Mono.Remoting.Channels.Unix / UnixServerChannel.cs
1 //
2 // Mono.Remoting.Channels.Unix.UnixServerChannel.cs
3 //
4 // Author: Rodrigo Moya (rodrigo@ximian.com)
5 //     Lluis Sanchez Gual (lluis@ideary.com)
6 //
7 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System;
32 using System.Collections;
33 using System.Runtime.Remoting.Messaging;
34 using System.Text.RegularExpressions;
35 using System.Net.Sockets;
36 using System.Net;
37 using System.Threading;
38 using System.IO;
39 using System.Runtime.Remoting.Channels;
40 using Mono.Unix;
41
42 namespace Mono.Remoting.Channels.Unix
43 {
44     public class UnixServerChannel : IChannelReceiver, IChannel
45     {
46         string path = null;
47         string name = "unix";
48
49         int priority = 1;
50         bool supressChannelData = false;
51         
52         Thread server_thread = null;
53         UnixListener listener;
54         UnixServerTransportSink sink;
55         ChannelDataStore channel_data;
56         int _maxConcurrentConnections = 100;
57         ArrayList _activeConnections = new ArrayList();
58         
59         
60         void Init (IServerChannelSinkProvider serverSinkProvider) 
61         {
62             if (serverSinkProvider == null) 
63             {
64                 serverSinkProvider = new UnixBinaryServerFormatterSinkProvider ();
65             }
66             
67             // Gets channel data from the chain of channel providers
68
69             channel_data = new ChannelDataStore (null);
70             IServerChannelSinkProvider provider = serverSinkProvider;
71             while (provider != null)
72             {
73                 provider.GetChannelData(channel_data);
74                 provider = provider.Next;
75             }
76
77             // Creates the sink chain that will process all incoming messages
78
79             IServerChannelSink next_sink = ChannelServices.CreateServerChannelSinkChain (serverSinkProvider, this);
80             sink = new UnixServerTransportSink (next_sink);
81             
82             StartListening (null);
83         }
84         
85         public UnixServerChannel (string path)
86         {
87             this.path = path;
88             Init (null);
89         }
90
91         public UnixServerChannel (IDictionary properties,
92                                   IServerChannelSinkProvider serverSinkProvider)
93         {
94             foreach(DictionaryEntry property in properties)
95             {
96                 switch((string)property.Key)
97                 {
98                 case "path":
99                     path = property.Value as string;
100                     break;
101                 case "priority":
102                     priority = Convert.ToInt32(property.Value);
103                     break;
104                 case "supressChannelData":
105                     supressChannelData = Convert.ToBoolean (property.Value);
106                     break;
107                 }
108             }            
109             Init (serverSinkProvider);
110         }
111
112         public UnixServerChannel (string name, string path,
113                                   IServerChannelSinkProvider serverSinkProvider)
114         {
115             this.name = name;
116             this.path = path;
117             Init (serverSinkProvider);
118         }
119         
120         public UnixServerChannel (string name, string path)
121         {
122             this.name = name;
123             this.path = path;
124             Init (null);
125         }
126         
127         public object ChannelData
128         {
129             get {
130                 if (supressChannelData) return null;
131                 else return channel_data;
132             }
133         }
134
135         public string ChannelName
136         {
137             get {
138                 return name;
139             }
140         }
141
142         public int ChannelPriority
143         {
144             get {
145                 return priority;
146             }
147         }
148
149         public string GetChannelUri ()
150         {
151             return "unix://" + path;
152         }
153         
154         public string[] GetUrlsForUri (string uri)
155         {
156             if (!uri.StartsWith ("/")) uri = "/" + uri;
157
158             string [] chnl_uris = channel_data.ChannelUris;
159             string [] result = new String [chnl_uris.Length];
160
161             for (int i = 0; i < chnl_uris.Length; i++) 
162                 result [i] = chnl_uris [i] + "?" + uri;
163             
164             return result;
165         }
166
167         public string Parse (string url, out string objectURI)
168         {
169             return UnixChannel.ParseUnixURL (url, out objectURI);
170         }
171
172         void WaitForConnections ()
173         {
174             try
175             {
176                 while (true) 
177                 {
178                     Socket client = listener.AcceptSocket ();
179                     CreateListenerConnection (client);
180                 }
181             }
182             catch
183             {}
184         }
185
186         internal void CreateListenerConnection (Socket client)
187         {
188             lock (_activeConnections)
189                 {
190                     if (_activeConnections.Count >= _maxConcurrentConnections)
191                         Monitor.Wait (_activeConnections);
192
193                     if (server_thread == null) return;    // Server was stopped while waiting
194
195                     ClientConnection reader = new ClientConnection (this, client, sink);
196                     Thread thread = new Thread (new ThreadStart (reader.ProcessMessages));
197                     thread.Start();
198                     thread.IsBackground = true;
199                     _activeConnections.Add (thread);
200                 }
201         }
202
203         internal void ReleaseConnection (Thread thread)
204         {
205             lock (_activeConnections)
206                 {
207                     _activeConnections.Remove (thread);
208                     Monitor.Pulse (_activeConnections);
209                 }
210         }
211         
212         public void StartListening (object data)
213         {
214             listener = new UnixListener (path);
215             Mono.Unix.Native.Syscall.chmod (path,
216                                      Mono.Unix.Native.FilePermissions.S_IRUSR |
217                                      Mono.Unix.Native.FilePermissions.S_IWUSR |
218                                      Mono.Unix.Native.FilePermissions.S_IRGRP |
219                                      Mono.Unix.Native.FilePermissions.S_IWGRP |
220                                      Mono.Unix.Native.FilePermissions.S_IROTH |
221                                      Mono.Unix.Native.FilePermissions.S_IWOTH);
222
223             if (server_thread == null) 
224             {
225                 listener.Start ();
226                 
227                 string[] uris = new String [1];
228                 uris = new String [1];
229                 uris [0] = GetChannelUri ();
230                 channel_data.ChannelUris = uris;
231
232                 server_thread = new Thread (new ThreadStart (WaitForConnections));
233                 server_thread.IsBackground = true;
234                 server_thread.Start ();
235             }
236         }
237
238         public void StopListening (object data)
239         {
240             if (server_thread == null) return;
241
242             lock (_activeConnections)
243                 {
244                     server_thread.Abort ();
245                     server_thread = null;
246                     listener.Stop ();
247
248                     foreach (Thread thread in _activeConnections)
249                         thread.Abort();
250
251                     _activeConnections.Clear();
252                     Monitor.PulseAll (_activeConnections);
253                 }
254         }
255     }
256
257     class ClientConnection
258     {
259         Socket _client;
260         UnixServerTransportSink _sink;
261         Stream _stream;
262         UnixServerChannel _serverChannel;
263
264         byte[] _buffer = new byte[UnixMessageIO.DefaultStreamBufferSize];
265
266         public ClientConnection (UnixServerChannel serverChannel, Socket client, UnixServerTransportSink sink)
267         {
268             _serverChannel = serverChannel;
269             _client = client;
270             _sink = sink;
271         }
272
273         public Socket Client {
274             get { return _client; }
275         }
276
277         public byte[] Buffer
278         {
279             get { return _buffer; }
280         }
281
282         public void ProcessMessages()
283         {
284                         byte[] buffer = new byte[256];
285             _stream = new BufferedStream (new NetworkStream (_client));
286
287             try
288             {
289                 bool end = false;
290                 while (!end)
291                 {
292                     MessageStatus type = UnixMessageIO.ReceiveMessageStatus (_stream, buffer);
293
294                     switch (type)
295                     {
296                                                 case MessageStatus.MethodMessage:
297                                                         _sink.InternalProcessMessage (this, _stream);
298                                                         break;
299
300                                                 case MessageStatus.Unknown:
301                                                 case MessageStatus.CancelSignal:
302                                                         end = true;
303                                                         break;
304                     }
305                 }
306             }
307             catch (Exception)
308             {
309                 //                Console.WriteLine (ex);
310             }
311             finally
312             {
313                                 try {
314                         _serverChannel.ReleaseConnection (Thread.CurrentThread);
315                         _stream.Close();
316                                         _client.Close ();
317                                 } catch {
318                                 }
319             }
320         }
321         
322         public bool IsLocal
323         {
324             get
325             {
326                 return true;
327             }
328         }
329     }
330 }