singrdk/base/Libraries/Manifest/Binder.sg

509 lines
20 KiB
Plaintext
Raw Permalink Normal View History

2008-03-05 09:52:00 -05:00
////////////////////////////////////////////////////////////////////////////////
//
// 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
2008-11-17 18:29:00 -05:00
#if ISA_ARM
public const string ExecutableExtension = ".arm";
public const string ManifestExtension = ".arm.manifest";
#elif ISA_IX64
public const string ExecutableExtension = ".x64";
public const string ManifestExtension = ".x64.manifest";
#elif ISA_IX86
2008-03-05 09:52:00 -05:00
public const string ExecutableExtension = ".x86";
2008-11-17 18:29:00 -05:00
public const string ManifestExtension = ".x86.manifest";
#endif
public const int ExecutableExtensionLength = 4; // Should be (ExecutableExtension.Length);
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
public const string ExecutablesNamespace = "init";
2008-03-05 09:52:00 -05:00
const string NameXmlTag = "name";
const string StartStatedIdXmlAttribute = "startStateId";
const string EndpointClassName = "Microsoft.Singularity.Channels.Endpoint";
public static void Initialize()
{
}
public static Manifest LoadManifest(DirectoryServiceContract.Imp:Ready! ds, String application)
{
if (application == null) return null;
application = (!)application.ToLower();
string folderName = application;
string appName = application;
2008-11-17 18:29:00 -05:00
// Open the directory containing the manifest and the ExecutableExtension
// remove the ExecutableExtension (e.g. ".x86") extension if present
2008-03-05 09:52:00 -05:00
if (application.EndsWith(ExecutableExtension)) {
2008-11-17 18:29:00 -05:00
folderName =(!)application.Substring(0, application.Length - ExecutableExtensionLength);
2008-03-05 09:52:00 -05:00
appName = folderName;
}
if (folderName.Length == 0) {
// invalid application name
return null;
}
// if the first character of the folder name is not "/",
// then we will redirect to the init namespace.
if (folderName[0] != '/') {
appName = folderName;
2008-11-17 18:29:00 -05:00
folderName = "/" + ExecutablesNamespace + "/" + appName;
2008-03-05 09:52:00 -05:00
}
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) {
2008-11-17 18:29:00 -05:00
Tracing.Log(Tracing.Debug, "Unable to open folder {0}",
folderName);
2008-03-05 09:52:00 -05:00
return null;
}
2008-11-17 18:29:00 -05:00
Manifest man = GetManifest(appName + ManifestExtension, dirEP);
2008-03-05 09:52:00 -05:00
delete dirEP;
return man;
}
2008-11-17 18:29:00 -05:00
/// <summary>
/// Wrap up all the details of creating a process in one rountine.
/// deals with old and new style processes. Performs manifest parsing
/// argument parsing and setting and manages stdin/out
/// </summary>
public static Process CreateProcess(DirectoryServiceContract.Imp! ds,
string[] args,
PipeMultiplexer! outputMux)
{
Manifest manifest;
return CreateProcess(ds,args,outputMux, out manifest);
}
public static Process CreateProcess(DirectoryServiceContract.Imp! ds,
string[] args,
PipeMultiplexer! outputMux,
out Manifest outManifest)
{
outManifest = null;
if (args == null || args.Length == 0) return null;
UnicodePipeContract.Imp! childStdInImp;
UnicodePipeContract.Exp! childStdInExp;
UnicodePipeContract.NewChannel(out childStdInImp, out childStdInExp);
// Splice a new output pipe into our output MUX.
UnicodePipeContract.Imp childStdOutImp = outputMux.FreshClient();
if (childStdOutImp == null) {
delete childStdInImp;
delete childStdInExp;
return null;
}
Process p = CreateProcess(ds,args,childStdInExp,childStdOutImp, out outManifest);
delete childStdInImp;
return p;
}
public static Process CreateProcess(DirectoryServiceContract.Imp! ds,
string[] args,
[Claims] UnicodePipeContract.Exp:READY? childStdInExp,
[Claims] UnicodePipeContract.Imp:READY! childStdOutImp)
{
Manifest manifest;
return CreateProcess(ds,args,childStdInExp, childStdOutImp, out manifest);
}
public static Process CreateProcess(DirectoryServiceContract.Imp! ds,
string[] args,
[Claims] UnicodePipeContract.Exp:READY? childStdInExp,
[Claims] UnicodePipeContract.Imp:READY! childStdOutImp,
out Manifest outManifest)
{
return CreateProcess(null, ds, args, childStdInExp, childStdOutImp, out outManifest);
}
public static Process CreateProcess( string category,
DirectoryServiceContract.Imp! ds,
string[] args,
out Manifest outManifest)
{
string action;
return CreateProcess(category, ds, args, out outManifest, out action);
}
public static Process CreateProcess( string category,
DirectoryServiceContract.Imp! ds,
string[] args,
[Claims] UnicodePipeContract.Exp:READY? childStdInExp,
[Claims] UnicodePipeContract.Imp:READY! childStdOutImp,
out Manifest outManifest)
{
string action;
Process p = CreateProcess(category, ds, args, out outManifest, out action);
if (p == null) {
delete childStdInExp;
delete childStdOutImp;
return null;
}
bool ok = ProcessIo(p, outManifest, action, childStdInExp,childStdOutImp);
if (!ok) {
DebugStub.Break();
return null;
}
return p;
}
///<summary>
/// Create a new process and set parameters using the manifest
///</summary>
///<return>
/// The process created. If there are errors return null.
///</return>
public static Process CreateProcess(string category,
DirectoryServiceContract.Imp! ds,
string[] args,
out Manifest manifest,
out string action)
{
manifest = null;
action = null;
if (args == null || args.Length == 0) {
DebugStub.Break();
return null;
}
// Figure out if this process needs any special endpoints.
manifest = Binder.LoadManifest(ds, args[0]);
if (manifest == null) {
Console.WriteLine("'{0}' is not a command or has no manifest", args[0]);
DebugStub.Break();
return null;
}
if (manifest.HasParameters()) {
Process child;
XmlNode categoryNode = null;
if (category != null ) {
// the caller expects the category name passed in
// to match the one found in the manifest. If it does
// not throw exception
categoryNode = manifest.GetCategoryNodeByName(category);
if (categoryNode == null) {
DebugStub.WriteLine("Category node not present " +
"in manifest for {0}", __arglist(args[0]));
throw new Exception("no category found in manifest");
}
}
if ((category != null) && (category != "console")) {
child = new Process((!)args[0], null, null);
}
else {
ParameterProcessor! parameters = new ParameterProcessor();
bool ok = parameters.ProcessParameters(args, manifest,
out child, out action);
if(ok && child != null) {
categoryNode = manifest.GetCategoryNode(action);
if (categoryNode != null) {
}
}
else {
return null;
}
}
assert child != null;
int result = manifest.SetEndpoints(child, action, false);
if (result < 0) {
Console.WriteLine("Unable to set all endpoints for this process.");
DebugStub.Break();
return null;
}
return child;
}
else {
Console.WriteLine(" old style");
// Old-style app.
return new Process(args, null, 2);
}
}
///<summary>
/// Try to parse an integer, return true if successful.
///</summary>
private static bool TryParseInt(string input, out int value)
{
try {
value = int.Parse(input);
return true;
}
catch(FormatException) {
value = 0;
return false;
}
catch(OverflowException) {
value = 0;
return false;
}
}
private static bool ProcessIo( Process! child,
Manifest manifest,
string action,
[Claims] UnicodePipeContract.Exp:READY? childStdInExp,
[Claims] UnicodePipeContract.Imp:READY! childStdOutImp
)
{
try {
if (manifest != null && manifest.HasParameters()) {
int stdinIndex = manifest.GetInputPipeIndex(action, "data");
if (stdinIndex == -1) {
Console.WriteLine(" no stdin data pipe specified in manifest");
delete childStdInExp;
}
else child.SetStartupEndpoint(stdinIndex, (Endpoint * in ExHeap) childStdInExp);
int stdoutIndex = manifest.GetOutputPipeIndex(action, "data");
if (stdoutIndex == -1) {
Console.WriteLine(" no stdout data pipe specified in manifest");
delete childStdOutImp;
}
else child.SetStartupEndpoint(stdoutIndex, (Endpoint * in ExHeap) childStdOutImp);
}
else {
child.SetStartupEndpoint(0, (Endpoint * in ExHeap) childStdInExp);
child.SetStartupEndpoint(1, (Endpoint * in ExHeap) childStdOutImp);
}
}
catch (Exception ex) {
Console.WriteLine("Exception: " + ex.Message);
}
return true;
}
2008-03-05 09:52:00 -05:00
public static byte [] FindAndReadManifest(string! file,
DirectoryServiceContract.Imp:Ready! dirClient)
{
ErrorCode errorOut;
2008-11-17 18:29:00 -05:00
long readSize = 0x1000;
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
FileAttributesRecord fileAttributes;
2008-03-05 09:52:00 -05:00
bool ok = SdsUtils.GetAttributes(file,
dirClient,
2008-11-17 18:29:00 -05:00
out fileAttributes,
2008-03-05 09:52:00 -05:00
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
2008-11-17 18:29:00 -05:00
byte [] region = new byte[fileAttributes.FileSize];
2008-03-05 09:52:00 -05:00
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;
}
2008-11-17 18:29:00 -05:00
public static Manifest GetManifest(string! name,
DirectoryServiceContract.Imp:Ready! dirEP)
2008-03-05 09:52:00 -05:00
{
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);
2008-11-17 18:29:00 -05:00
Tracing.Log(Tracing.Debug, "Success: {0}", name);
2008-03-05 09:52:00 -05:00
}
else {
2008-11-17 18:29:00 -05:00
Tracing.Log(Tracing.Debug, "Failed: no manifest {0}", name);
2008-03-05 09:52:00 -05:00
}
}
catch (Exception e) {
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;
2008-11-17 18:29:00 -05:00
// TODO: the encoding of types will change, and when
2008-03-05 09:52:00 -05:00
// it does, this will need to change. Right now, we create the
// type name of foo.imp as foo+foo.imp (same for exp)
2008-11-17 18:29:00 -05:00
// XXX now types are in line with CLR -- foo+exp
if (name.EndsWith(".Exp")) {
name = name.Remove(name.Length - 4, 4) + "+Exp";
}
else if (name.EndsWith(".Imp")) {
name = name.Remove(name.Length - 4, 4) + "+Imp";
2008-03-05 09:52:00 -05:00
}
// 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;
}
}
}