//////////////////////////////////////////////////////////////////////////////// // // 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; } /// /// 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. /// 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; } } }