297 lines
11 KiB
Plaintext
297 lines
11 KiB
Plaintext
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Microsoft Research Singularity
|
||
|
//
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//
|
||
|
// File: Binder.cs
|
||
|
//
|
||
|
// Note:
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
using System;
|
||
|
using System.Text;
|
||
|
using System.GCs;
|
||
|
using System.Collections;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using System.Threading;
|
||
|
|
||
|
using Microsoft.SingSharp;
|
||
|
using Microsoft.Singularity.Channels;
|
||
|
using Microsoft.Singularity.Directory;
|
||
|
using Microsoft.Singularity.Io;
|
||
|
using Microsoft.Singularity.Xml;
|
||
|
using Microsoft.Singularity.V1.Types;
|
||
|
using EndpointImplementation = Microsoft.Singularity.Channels.Endpoint;
|
||
|
|
||
|
namespace Microsoft.Singularity.Applications
|
||
|
{
|
||
|
[CLSCompliant(false)]
|
||
|
public class Binder
|
||
|
{
|
||
|
// duplicate of Directory Service consts
|
||
|
public const string ManifestFile = "manifest";
|
||
|
public const string ExecutableExtension = ".x86";
|
||
|
public const string ManifestExtension = ".manifest";
|
||
|
public const string ExecutablesNamespace = "init";
|
||
|
|
||
|
private static StringBuilder sb;
|
||
|
private static bool isInitialized = false;
|
||
|
|
||
|
const string NameXmlTag = "name";
|
||
|
const string StartStatedIdXmlAttribute = "startStateId";
|
||
|
const string EndpointClassName = "Microsoft.Singularity.Channels.Endpoint";
|
||
|
|
||
|
public static void Initialize()
|
||
|
{
|
||
|
if (isInitialized) return;
|
||
|
sb = new StringBuilder(512); // cache away a string form manipulation
|
||
|
isInitialized = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
public static Manifest LoadManifest(DirectoryServiceContract.Imp:Ready! ds, String application)
|
||
|
{
|
||
|
if (application == null) return null;
|
||
|
|
||
|
application = (!)application.ToLower();
|
||
|
string folderName = application;
|
||
|
string appName = application;
|
||
|
|
||
|
|
||
|
// Open the directory containing the manifest and the .x86
|
||
|
// remove the ".x86" extension if present
|
||
|
if (application.EndsWith(ExecutableExtension)) {
|
||
|
folderName =(!)(application.Substring(0, application.Length - ((!)ExecutableExtension).Length));
|
||
|
appName = folderName;
|
||
|
}
|
||
|
|
||
|
if (folderName.Length == 0) {
|
||
|
// invalid application name
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
if (sb == null) {
|
||
|
sb = new StringBuilder(512); // cache away a string form manipulation
|
||
|
isInitialized = true;
|
||
|
}
|
||
|
// if the first character of the folder name is not "/",
|
||
|
// then we will redirect to the init namespace.
|
||
|
if (folderName[0] != '/') {
|
||
|
appName = folderName;
|
||
|
sb.Length = 0; //reset the buffer
|
||
|
sb.Append("/");
|
||
|
sb.Append(ExecutablesNamespace);
|
||
|
sb.Append("/");
|
||
|
sb.Append(appName);
|
||
|
|
||
|
folderName = (!)sb.ToString();
|
||
|
}
|
||
|
else {
|
||
|
// need to strip off all but last part.
|
||
|
appName = folderName.Substring(folderName.LastIndexOf('/')+1);
|
||
|
}
|
||
|
#if DEBUG2
|
||
|
DebugStub.WriteLine("Binder.LoadManifest: application={0},folder={1},appName={2}",
|
||
|
__arglist (application,folderName,appName));
|
||
|
#endif
|
||
|
|
||
|
DirectoryServiceContract.Imp:Ready dirEP = openDir(ds, folderName);
|
||
|
|
||
|
if (dirEP == null) {
|
||
|
#if DEBUG
|
||
|
DebugStub.WriteLine("Binder.LoadManifest: unable to open ,folder={0}",
|
||
|
__arglist (folderName));
|
||
|
#endif
|
||
|
return null;
|
||
|
}
|
||
|
Manifest man = GetManifest(ManifestFile, dirEP);
|
||
|
delete dirEP;
|
||
|
return man;
|
||
|
}
|
||
|
|
||
|
|
||
|
public static byte [] FindAndReadManifest(string! file,
|
||
|
DirectoryServiceContract.Imp:Ready! dirClient)
|
||
|
{
|
||
|
long size = 0;
|
||
|
NodeType nodeType;
|
||
|
ErrorCode errorOut;
|
||
|
|
||
|
long readSize = 4096;
|
||
|
|
||
|
bool ok = SdsUtils.GetAttributes(file,
|
||
|
dirClient,
|
||
|
out size,
|
||
|
out nodeType,
|
||
|
out errorOut );
|
||
|
if (!ok) return null;
|
||
|
|
||
|
|
||
|
// bind to file
|
||
|
|
||
|
FileContract.Imp! fileClient;
|
||
|
FileContract.Exp! fileServer;
|
||
|
FileContract.NewChannel(out fileClient, out fileServer);
|
||
|
|
||
|
ok = SdsUtils.Bind(file,dirClient,fileServer, out errorOut);
|
||
|
if (!ok) {
|
||
|
delete fileClient;
|
||
|
return null;
|
||
|
}
|
||
|
fileClient.RecvSuccess();
|
||
|
|
||
|
// Now read file
|
||
|
|
||
|
if (fileClient != null) {
|
||
|
// allocate memory and read file
|
||
|
byte [] region = new byte[size];
|
||
|
if (region == null) {
|
||
|
delete fileClient;
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
byte[] in ExHeap buf = new[ExHeap] byte[readSize];
|
||
|
long readOffset = 0;
|
||
|
|
||
|
while (true) {
|
||
|
// invariant: we own the non-null buffer buf at the
|
||
|
// loop head
|
||
|
// and we don't own the buffer at the loop exit.
|
||
|
//
|
||
|
Tracing.Log(Tracing.Debug,"FindImage pre SendRead");
|
||
|
fileClient.SendRead(buf, 0, readOffset, readSize);
|
||
|
|
||
|
switch receive {
|
||
|
case fileClient.AckRead(localbuf, bytesRead, error) :
|
||
|
Tracing.Log(Tracing.Debug,"FindImage Post SendRead");
|
||
|
Bitter.ToByteArray(localbuf, 0, (int)bytesRead,
|
||
|
region, (int)readOffset);
|
||
|
if (bytesRead == readSize) {
|
||
|
// see if there is more
|
||
|
readOffset += bytesRead;
|
||
|
buf = localbuf;
|
||
|
continue;
|
||
|
}
|
||
|
delete localbuf;
|
||
|
break;
|
||
|
|
||
|
case fileClient.ChannelClosed() :
|
||
|
break;
|
||
|
case unsatisfiable :
|
||
|
break;
|
||
|
}
|
||
|
// Get out of loop
|
||
|
break;
|
||
|
}
|
||
|
delete fileClient;
|
||
|
return region;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
private static DirectoryServiceContract.Imp:Ready openDir(DirectoryServiceContract.Imp:Ready! ds, string! filePath)
|
||
|
{
|
||
|
assert ds != null;
|
||
|
|
||
|
DirectoryServiceContract.Imp! dirClient;
|
||
|
DirectoryServiceContract.Exp! dirServer;
|
||
|
DirectoryServiceContract.NewChannel(
|
||
|
out dirClient, out dirServer);
|
||
|
|
||
|
ErrorCode errorCode;
|
||
|
bool ok = SdsUtils.Bind(filePath, ds, dirServer, out errorCode);
|
||
|
if (!ok) {
|
||
|
delete dirClient;
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
dirClient.RecvSuccess();
|
||
|
return dirClient;
|
||
|
}
|
||
|
|
||
|
private static Manifest GetManifest(string! name,
|
||
|
DirectoryServiceContract.Imp:Ready! dirEP)
|
||
|
{
|
||
|
Manifest manifest = null;
|
||
|
|
||
|
Tracing.Log(Tracing.Debug, "Binder.GetManifest: path={0}", name);
|
||
|
try {
|
||
|
byte [] manifestMemory = FindAndReadManifest(name,dirEP);
|
||
|
if (manifestMemory != null) {
|
||
|
manifest = new Manifest(manifestMemory);
|
||
|
}
|
||
|
else {
|
||
|
DebugStub.WriteLine(" no manifest! file={0}",__arglist(name));
|
||
|
}
|
||
|
}
|
||
|
catch (Exception e) {
|
||
|
DebugStub.WriteLine("Exception in GetManifest: {0}", __arglist(e.Message));
|
||
|
Tracing.Log(Tracing.Debug, "Exception in GetManifest: {0}", e.Message);
|
||
|
}
|
||
|
return manifest;
|
||
|
}
|
||
|
|
||
|
|
||
|
// utility function for getting SystemTypes from strings in the manifest
|
||
|
private static SystemType GetEndpointType(XmlNode! metadata)
|
||
|
{
|
||
|
|
||
|
// everything must derive from Endpoint
|
||
|
SystemType epSystemType = typeof(Channels.Endpoint).GetSystemType();
|
||
|
|
||
|
// now traverse the metadata to the types, in order:
|
||
|
foreach (XmlNode! child in metadata.Children) {
|
||
|
string! name = (!)child.GetAttribute(NameXmlTag, "");
|
||
|
|
||
|
long lower, upper;
|
||
|
// [TODO] the encoding of types will change, and when
|
||
|
// it does, this will need to change. Right now, we create the
|
||
|
// type name of foo.imp as foo+foo.imp (same for exp)
|
||
|
if (name.EndsWith(".Exp") || name.EndsWith(".Imp")) {
|
||
|
name = name.Remove(name.Length - 4, 4) + "+" + name;
|
||
|
}
|
||
|
|
||
|
// get the hash for this type:
|
||
|
System.RuntimeTypeHash.ComputeHash(name, out lower, out upper);
|
||
|
|
||
|
// If this isn't the base of our derivation, get the type
|
||
|
if (name != EndpointClassName) {
|
||
|
epSystemType = EndpointImplementation.RegisterSystemType(name,lower, upper, epSystemType);
|
||
|
}
|
||
|
}
|
||
|
return epSystemType;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Given information in the application manifest attempt to create and bind a channel to
|
||
|
/// a service conforming to the contract specified. This routine parses the manifest
|
||
|
/// metadata then makes an ABI call to generate the endpoints, connect them, add the
|
||
|
/// imp endpoint to the process StartupEndpoint array at "index", and attempt to bind
|
||
|
/// the exp endpoint to a service provider using system policy.
|
||
|
/// </summary>
|
||
|
public static bool BindServiceUser(Process! process,
|
||
|
int index,
|
||
|
string! contract,
|
||
|
XmlNode! metadata)
|
||
|
{
|
||
|
// get the metadata for both sides of the channel
|
||
|
XmlNode! impNode = (!)metadata.GetChild("imp");
|
||
|
XmlNode! expNode = (!)metadata.GetChild("exp");
|
||
|
|
||
|
// get the initial state as an integer
|
||
|
int initialState = metadata.GetAttribute(StartStatedIdXmlAttribute, 0);
|
||
|
|
||
|
// now get the SystemType of each endpoint's type (we don't actually
|
||
|
// know the type in the kernel, but we can discern it using the
|
||
|
// fullname from the metadata)
|
||
|
SystemType impType = GetEndpointType(impNode);
|
||
|
SystemType expType = GetEndpointType(expNode);
|
||
|
|
||
|
bool ok = process.BindToService(impType, expType, contract, initialState, index);
|
||
|
return ok;
|
||
|
}
|
||
|
}
|
||
|
}
|