Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

331 lines
9.9 KiB
C#

//
// Mono.Remoting.Channels.Unix.UnixServerChannel.cs
//
// Author: Rodrigo Moya (rodrigo@ximian.com)
// Lluis Sanchez Gual (lluis@ideary.com)
//
// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections;
using System.Runtime.Remoting.Messaging;
using System.Text.RegularExpressions;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;
using System.Runtime.Remoting.Channels;
using Mono.Unix;
namespace Mono.Remoting.Channels.Unix
{
public class UnixServerChannel : IChannelReceiver, IChannel
{
string path = null;
string name = "unix";
int priority = 1;
bool supressChannelData = false;
Thread server_thread = null;
UnixListener listener;
UnixServerTransportSink sink;
ChannelDataStore channel_data;
int _maxConcurrentConnections = 100;
ArrayList _activeConnections = new ArrayList();
void Init (IServerChannelSinkProvider serverSinkProvider)
{
if (serverSinkProvider == null)
{
serverSinkProvider = new UnixBinaryServerFormatterSinkProvider ();
}
// Gets channel data from the chain of channel providers
channel_data = new ChannelDataStore (null);
IServerChannelSinkProvider provider = serverSinkProvider;
while (provider != null)
{
provider.GetChannelData(channel_data);
provider = provider.Next;
}
// Creates the sink chain that will process all incoming messages
IServerChannelSink next_sink = ChannelServices.CreateServerChannelSinkChain (serverSinkProvider, this);
sink = new UnixServerTransportSink (next_sink);
StartListening (null);
}
public UnixServerChannel (string path)
{
this.path = path;
Init (null);
}
public UnixServerChannel (IDictionary properties,
IServerChannelSinkProvider serverSinkProvider)
{
foreach(DictionaryEntry property in properties)
{
switch((string)property.Key)
{
case "path":
path = property.Value as string;
break;
case "priority":
priority = Convert.ToInt32(property.Value);
break;
case "supressChannelData":
supressChannelData = Convert.ToBoolean (property.Value);
break;
}
}
Init (serverSinkProvider);
}
public UnixServerChannel (string name, string path,
IServerChannelSinkProvider serverSinkProvider)
{
this.name = name;
this.path = path;
Init (serverSinkProvider);
}
public UnixServerChannel (string name, string path)
{
this.name = name;
this.path = path;
Init (null);
}
public object ChannelData
{
get {
if (supressChannelData) return null;
else return channel_data;
}
}
public string ChannelName
{
get {
return name;
}
}
public int ChannelPriority
{
get {
return priority;
}
}
public string GetChannelUri ()
{
return "unix://" + path;
}
public string[] GetUrlsForUri (string uri)
{
if (!uri.StartsWith ("/")) uri = "/" + uri;
string [] chnl_uris = channel_data.ChannelUris;
string [] result = new String [chnl_uris.Length];
for (int i = 0; i < chnl_uris.Length; i++)
result [i] = chnl_uris [i] + "?" + uri;
return result;
}
public string Parse (string url, out string objectURI)
{
return UnixChannel.ParseUnixURL (url, out objectURI);
}
void WaitForConnections ()
{
try
{
while (true)
{
Socket client = listener.AcceptSocket ();
CreateListenerConnection (client);
}
}
catch
{}
}
internal void CreateListenerConnection (Socket client)
{
lock (_activeConnections)
{
if (_activeConnections.Count >= _maxConcurrentConnections)
Monitor.Wait (_activeConnections);
if (server_thread == null) return; // Server was stopped while waiting
ClientConnection reader = new ClientConnection (this, client, sink);
Thread thread = new Thread (new ThreadStart (reader.ProcessMessages));
thread.Start();
thread.IsBackground = true;
_activeConnections.Add (thread);
}
}
internal void ReleaseConnection (Thread thread)
{
lock (_activeConnections)
{
_activeConnections.Remove (thread);
Monitor.Pulse (_activeConnections);
}
}
public void StartListening (object data)
{
listener = new UnixListener (path);
Mono.Unix.Native.Syscall.chmod (path,
Mono.Unix.Native.FilePermissions.S_IRUSR |
Mono.Unix.Native.FilePermissions.S_IWUSR |
Mono.Unix.Native.FilePermissions.S_IRGRP |
Mono.Unix.Native.FilePermissions.S_IWGRP |
Mono.Unix.Native.FilePermissions.S_IROTH |
Mono.Unix.Native.FilePermissions.S_IWOTH);
if (server_thread == null)
{
listener.Start ();
string[] uris = new String [1];
uris = new String [1];
uris [0] = GetChannelUri ();
channel_data.ChannelUris = uris;
server_thread = new Thread (new ThreadStart (WaitForConnections));
server_thread.IsBackground = true;
server_thread.Start ();
}
}
public void StopListening (object data)
{
if (server_thread == null) return;
lock (_activeConnections)
{
server_thread.Abort ();
server_thread = null;
listener.Stop ();
foreach (Thread thread in _activeConnections)
thread.Abort();
_activeConnections.Clear();
Monitor.PulseAll (_activeConnections);
}
}
}
class ClientConnection
{
Socket _client;
UnixServerTransportSink _sink;
Stream _stream;
UnixServerChannel _serverChannel;
byte[] _buffer = new byte[UnixMessageIO.DefaultStreamBufferSize];
public ClientConnection (UnixServerChannel serverChannel, Socket client, UnixServerTransportSink sink)
{
_serverChannel = serverChannel;
_client = client;
_sink = sink;
}
public Socket Client {
get { return _client; }
}
public byte[] Buffer
{
get { return _buffer; }
}
public void ProcessMessages()
{
byte[] buffer = new byte[256];
_stream = new BufferedStream (new NetworkStream (_client));
try
{
bool end = false;
while (!end)
{
MessageStatus type = UnixMessageIO.ReceiveMessageStatus (_stream, buffer);
switch (type)
{
case MessageStatus.MethodMessage:
_sink.InternalProcessMessage (this, _stream);
break;
case MessageStatus.Unknown:
case MessageStatus.CancelSignal:
end = true;
break;
}
}
}
catch (Exception)
{
// Console.WriteLine (ex);
}
finally
{
try {
_serverChannel.ReleaseConnection (Thread.CurrentThread);
_stream.Close();
_client.Close ();
} catch {
}
}
}
public bool IsLocal
{
get
{
return true;
}
}
}
}