singrdk/base/Services/RamDisk/ClientManager/RamDiskClientManager.sg

422 lines
16 KiB
Plaintext
Raw Permalink Normal View History

2008-11-17 18:29:00 -05:00
///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: RamDisk/ClientManager/RamDiskClientManager.sg
//
// Note:
//
using Microsoft.SingSharp;
using Microsoft.Singularity.Channels;
using Microsoft.Singularity.Directory;
using Microsoft.Singularity.Configuration;
using Microsoft.Singularity.ServiceManager;
using Microsoft.Singularity.Services.RamDisk.Contracts;
using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using FileSystem.Utils;
using DirectoryErrorCode = Microsoft.Singularity.Directory.ErrorCode;
[assembly: Microsoft.Singularity.Security.ApplicationPublisherAttribute("singularity.microsoft.com")]
[assembly: Microsoft.Singularity.Security.AssertPrivilegeAttribute("$register-privilege.localhost")]
namespace Microsoft.Singularity.Services.RamDisk.ClientManager
{
[Category("Service")]
internal class ServiceParameters
{
[CustomEndpoint]
public readonly TRef<ServiceProcessContract.Exp:Starting> ControlEndpointRef;
[CustomEndpoint]
public readonly TRef<ServiceEventContract.Imp:Ready> EventEndpointRef;
reflective private ServiceParameters();
}
public class ClientManager
{
private static SortedList! clientsByDiskName = new SortedList();
private class RamDiskErrorException : Exception
{
private RamDiskContractErrorCode errorCode;
internal RamDiskErrorException(RamDiskContractErrorCode errorCode) {
this.errorCode = errorCode;
}
internal RamDiskContractErrorCode ErrorCode {
get {
return errorCode;
}
}
}
internal class Client
{
TRef<RamDiskClientContract.Imp:Running> tref;
string! diskName;
ulong sizeBytes;
internal Client(
[Claims] RamDiskClientContract.Imp! imp,
string! diskName,
ulong sizeBytes
)
{
this.tref = new TRef<RamDiskClientContract.Imp:Running>(imp);
this.diskName = diskName;
this.sizeBytes = sizeBytes;
}
internal RamDiskClientContract.Imp! Acquire()
{
return tref.Acquire();
}
internal void Release([Claims] RamDiskClientContract.Imp! imp)
{
tref.Release(imp);
}
internal string! DiskName { get { return this.diskName; } }
internal ulong SizeBytes { get { return this.sizeBytes; } }
}
internal static void
AddClient(Client! newClient)
{
clientsByDiskName.Add(newClient.DiskName, newClient);
}
internal static Client GetClientByDiskName(string! diskName)
{
if (clientsByDiskName.Contains(diskName)) {
return (Client)clientsByDiskName[diskName];
}
else {
return null;
}
}
internal static void DeleteClient(Client! closedClient)
{
clientsByDiskName.Remove(closedClient.DiskName);
}
internal static bool ClientIsClosed(Client! client)
{
RamDiskClientContract.Imp:Running! clientImp = client.Acquire();
bool closed = false;
switch receive {
case clientImp.ChannelClosed():
closed = true;
break;
case timeout:
break;
}
client.Release(clientImp);
return closed;
}
private static RamDiskContractErrorCode DoCreate(ulong sizeBytes, out string! outDiskName)
{
outDiskName = "";
string! diskName;
ServiceProviderContract.Exp! serviceExp;
try {
GetNextDiskPath(out diskName, out serviceExp);
}
catch (RamDiskErrorException ex) {
Dbg("FAILED to get next disk path.");
return ex.ErrorCode;
}
RamDiskClientContract.Imp! clientImp;
RamDiskClientContract.Exp! clientExp;
RamDiskClientContract.NewChannel(out clientImp, out clientExp);
string[] args = { "RamDisk" };
Process process;
try {
process = new Process(args);
}
catch (Exception ex) {
Dbg("FAILED to create worker process: " + ex.Message);
return RamDiskContractErrorCode.InternalError;
}
try {
process.SetStartupEndpoint(0, serviceExp);
process.SetStartupEndpoint(1, clientExp);
process.Start();
}
catch (Exception ex) {
Dbg("FAILED to start worker process: " + ex.Message);
process.Dispose(true);
return RamDiskContractErrorCode.InternalError;
}
clientImp.SendCreate(Bitter.FromString2(diskName), sizeBytes);
switch receive {
case clientImp.Success():
AddClient(new Client(clientImp, diskName, sizeBytes));
outDiskName = diskName;
return RamDiskContractErrorCode.NoError;
case clientImp.ChannelClosed():
Dbg("FAILED to create ramdisk. Worker process closed channel.");
delete clientImp;
return RamDiskContractErrorCode.InternalError;
case clientImp.Fail(RamDiskContractErrorCode reason):
Dbg("FAILED to create ramdisk. Worker process sent 'Fail' message, error = " + reason);
delete clientImp;
return reason;
}
}
private static void
DoDestroy(
RamDiskControlContract.Exp:Ready! controller,
[Claims] char[]! in ExHeap diskNameHeap,
bool force
)
{
string! diskName = PathUtils.ToPath(Bitter.ToString2(diskNameHeap));
delete diskNameHeap;
Client client = GetClientByDiskName(diskName);
if (client == null) {
controller.SendFail(RamDiskContractErrorCode.DoesNotExist);
return;
}
else if (ClientIsClosed(client)) {
controller.SendSuccess();
return;
}
RamDiskClientContract.Imp! clientImp = client.Acquire();
clientImp.SendDestroy(force);
switch receive {
case clientImp.Success():
client.Release(clientImp);
DeleteClient(client);
try {
DeregisterDevice(diskName);
}
catch (RamDiskErrorException ex) {
controller.SendFail(ex.ErrorCode);
return;
}
controller.SendSuccess();
break;
case clientImp.Fail(reason):
controller.SendFail(reason);
client.Release(clientImp);
break;
case clientImp.ChannelClosed():
controller.SendFail(RamDiskContractErrorCode.InternalError);
client.Release(clientImp);
DeleteClient(client);
break;
}
}
private static void ServiceClients(ServiceProcessContract.Exp! svcontrol)
{
ESet<RamDiskControlContract.Exp:Ready> controllers =
new ESet<RamDiskControlContract.Exp:Ready>();
svcontrol.SendStartSucceeded();
bool run = true;
while (run) {
switch receive {
case svcontrol.Connect(expath, candidate):
if (expath != null) {
// This service does not use subpaths. Reject request.
delete expath;
delete candidate;
svcontrol.SendNakConnect(ErrorCode.NotFound, null);
break;
}
RamDiskControlContract.Exp controller =
candidate as RamDiskControlContract.Exp;
if (controller != null) {
controller.SendSuccess();
controllers.Add(controller);
svcontrol.SendAckConnect();
}
else {
delete candidate;
svcontrol.SendNakConnect(ErrorCode.ContractNotSupported, null);
}
break;
case svcontrol.Knock():
svcontrol.SendAlive();
break;
case svcontrol.Stop():
// -XXX- Need to deal with this. Either send AckStop and exit, or send Busy
// -XXX- because you can't cleanly stop right now.
DebugStub.WriteLine("RamDiskService: Clean shutdown is not supported -- bailing!");
run = false;
svcontrol.SendAckStop();
break;
case svcontrol.ChannelClosed():
run = false;
break;
case controller.Create(diskSizeBytes) in controllers:
{
string! diskName;
RamDiskContractErrorCode result = DoCreate(diskSizeBytes, out diskName);
if (result == RamDiskContractErrorCode.NoError) {
char[]! in ExHeap exdiskName = Bitter.FromString2(diskName);
controller.SendCreateSuccess(exdiskName);
}
else {
controller.SendFail(result);
}
controllers.Add(controller);
break;
}
case controller.Destroy(diskName, force) in controllers:
DoDestroy(controller, diskName, force);
controllers.Add(controller);
break;
case controller.ChannelClosed() in controllers:
delete controller;
break;
}
}
controllers.Dispose();
}
internal static int AppMain(ServiceParameters! parameters)
{
if (parameters.ControlEndpointRef == null)
throw new Exception("The parent process did not provide a ServiceProcessContract channel.");
ServiceProcessContract.Exp! svcontrol = parameters.ControlEndpointRef.Acquire();
try {
ServiceClients(svcontrol);
return 0;
}
finally {
delete svcontrol;
}
}
static int nextDiskNumber = 0;
static string ramDiskPathPrefix = "/dev/ramdisk";
private static void GetNextDiskPath(out string! diskName, out ServiceProviderContract.Exp! expService)
{
// Create a new directory entry for the disk
DirectoryServiceContract.Imp ns = DirectoryService.NewClientEndpoint();
try {
while (true) {
ServiceProviderContract.Imp! imp;
ServiceProviderContract.Exp! exp;
ServiceProviderContract.NewChannel(out imp, out exp);
diskName = ramDiskPathPrefix + nextDiskNumber.ToString();
ns.SendRegister(Bitter.FromString2(diskName), imp);
switch receive {
case ns.AckRegister():
nextDiskNumber++;
expService = exp;
return;
case ns.NakRegister(nakImp, error):
// If the error is AlreadyExists, recover by trying next disk number, else fail
if (error == Microsoft.Singularity.Directory.ErrorCode.AlreadyExists) {
nextDiskNumber++;
}
else {
throw new RamDiskErrorException(DirectoryErrorToRamDiskError(error));
}
delete exp;
delete nakImp;
break;
//case unsatisfiable:
// resources.service.Release(exp);
// throw new Exception("unsatisfiable registering RAM disk");
}
}
}
finally {
delete ns;
}
}
private static void DeregisterDevice(string! diskName)
{
// Deregister the directory entry
DirectoryServiceContract.Imp ns = DirectoryService.NewClientEndpoint();
ns.SendDeregister(Bitter.FromString2(diskName));
try {
switch receive {
case ns.AckDeregister(serviceImp):
delete serviceImp;
break;
case ns.NakDeregister(error):
throw new RamDiskErrorException(DirectoryErrorToRamDiskError(error));
case unsatisfiable:
throw new Exception("unsatisfiable deregistering RAM disk");
}
}
finally {
delete ns;
}
}
private static RamDiskContractErrorCode DirectoryErrorToRamDiskError(DirectoryErrorCode error) {
switch (error) {
case DirectoryErrorCode.NoError: return RamDiskContractErrorCode.NoError;
case DirectoryErrorCode.AccessDenied: return RamDiskContractErrorCode.AccessDenied;
case DirectoryErrorCode.CapacityReached: return RamDiskContractErrorCode.CapacityReached;
case DirectoryErrorCode.InsufficientResources: return RamDiskContractErrorCode.InsufficientResources;
case DirectoryErrorCode.DirectoryFull: return RamDiskContractErrorCode.DirectoryFull;
case DirectoryErrorCode.IsOpen: return RamDiskContractErrorCode.IsOpen;
default: return RamDiskContractErrorCode.InternalError;
}
}
const string dbgprefix = "RamDiskService: ";
static void Dbg(string! line)
{
DebugStub.WriteLine(dbgprefix + line);
}
static void Dbg(string! format, params object[]! args)
{
Dbg(String.Format(format, args));
}
}
}