/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: Services\ServiceManager\Service.sg // // Note: Service counterpart // using System; using System.Collections; using System.Collections.Specialized; using System.Threading; using Microsoft.SingSharp; using Microsoft.Singularity; using Microsoft.Singularity.Directory; using Microsoft.Singularity.Channels; using Microsoft.Singularity.ServiceManager; using Microsoft.Singularity.V1.Processes; namespace Microsoft.Singularity.Services.ServiceManager { /// /// Service process counterpart. /// internal abstract class Service { protected readonly string! serviceName; protected readonly string! binaryName; protected readonly ServiceType serviceType; protected int id; protected Process process; private Thread thread; /// /// Management channel to the service /// protected TRef managementEndpoint; /// /// User-Service Communication Relay Point /// private TRef controlProxy; protected TRef directory; private TRef senderRef; private TRef receiverRef; private TRef dummyRef; internal Service(string! serviceName, string! binaryName, ServiceType serviceType, [Claims]DirectoryServiceContract.Imp:Ready! ds) { this.serviceName = serviceName; this.binaryName = binaryName; this.serviceType = serviceType; ThreadTerminationContract.Imp! imp; ThreadTerminationContract.Exp! exp; ThreadTerminationContract.NewChannel(out imp, out exp); senderRef = new TRef(imp); receiverRef = new TRef(exp); directory = new TRef(ds); } ~Service() { delete directory.Acquire(); delete senderRef.Acquire(); delete receiverRef.Acquire(); } /// /// Starts this service counter part. Doesn't start any service process. /// internal void Start([Claims]ServiceControlContract.Exp:Start! ep) { ep.SendSuccess(); controlProxy = new TRef(ep); thread = new Thread(new ThreadStart(Run)); thread.Start(); } /// /// Terminates the service counter part. /// internal void Stop(out ServiceControlContract.Exp:Ready! ep) { ThreadTerminationContract.Imp:Start! leader; // Terminates Run() leader = senderRef.Acquire(); try { leader.SendStop(); leader.RecvAckStop(); thread.Join(); } catch (Exception) { DebugStub.Print("Service: already stopped.\n"); } // Give EP back ep = controlProxy.Acquire(); controlProxy = null; // Reuse for the next connection senderRef.Release(leader); } /// /// Brings up the service that this object represents. Throws /// ProcessCreateException. /// internal virtual ServiceError StartServiceProcess() { ServiceError report; ManagedServiceContract.Imp! msClient; ManagedServiceContract.Exp! msServer; DirectoryServiceContract.Imp! dsClient; DebugStub.Print("Service: Create process '{0}'\n", __arglist(binaryName)); process = new Process(new String[1]{binaryName}, null, 3); // Create a channel to the service ManagedServiceContract.NewChannel(out msClient, out msServer); if (!process.SetStartupEndpoint(0, (Endpoint * in ExHeap)msServer)) { DebugStub.Print("Endpoint cannot be set. (ManagedService)\n"); delete msClient; return ServiceError.TryAgain; } dsClient = directory.Acquire(); if (!process.SetStartupEndpoint(1, (Endpoint * in ExHeap)dsClient)) { DebugStub.Print("Endpoint cannot be set. " + "(DirectoryService)\n"); delete msClient; return ServiceError.TryAgain; } // // ServiceProxyContract Dummy //NOTE: this is necessary even though the default service never // be able to use. ServiceProxyContract.Imp! imp; ServiceProxyContract.Exp! exp; ServiceProxyContract.NewChannel(out imp, out exp); process.SetStartupEndpoint(2, (Endpoint * in ExHeap)imp); dummyRef = new TRef(exp); process.Start(); // Make sure the process has been started switch receive { case msClient.RecvSuccess(): managementEndpoint = new TRef(msClient); report = ServiceError.None; DebugStub.Print("-- Process ID: {0}\n", __arglist(process.Id)); break; case msClient.ChannelClosed(): DebugStub.Print("Service process's channel is closed.\n"); delete msClient; report = ServiceError.ChannelClosed; break; } //DebugStub.Print("Service.sg: StartServiceProcess EXIT\n"); return report; } /// /// Shuts down the service process. /// internal virtual ServiceError StopServiceProcess() { ServiceError report = ServiceError.Unknown; ManagedServiceContract.Imp:Ready ep; //DebugStub.Print("Service: ENTER StopServiceProcess\n"); StopPoll(); if (managementEndpoint == null) { return ServiceError.NotFound; } ep = managementEndpoint.Acquire(); if (process.State != ProcessState.Stopped) { try { ep.SendStop(); switch receive { case ep.AckStop(): report = ServiceError.None; break; case ep.ChannelClosed(): report = ServiceError.None; break; } } catch (Exception) { DebugStub.Print("already stopped\n"); } } process.Stop(); delete ep; managementEndpoint = null; report = ServiceError.None; //DebugStub.Print("Service: EXIT StopServiceProcess\n"); return report; } internal virtual ServiceError RestartServiceProcess() { ServiceError report; report = StopServiceProcess(); if (report == ServiceError.None) { report = StartServiceProcess(); } return report; } internal virtual ServiceError StartService() { ServiceError report; ManagedServiceContract.Imp:Ready! client; //DebugStub.Print("Service: StartService ENTER\n"); client = managementEndpoint.Acquire(); client.SendStartService(); switch receive { case client.AckStartService(): report = ServiceError.None; break; case client.NakStartService(): report = ServiceError.TryAgain; break; case client.ChannelClosed(): report = ServiceError.ChannelClosed; break; } managementEndpoint.Release(client); //DebugStub.Print("Service: StartService EXIT\n"); return report; } internal virtual ServiceError StopService() { ServiceError report; ManagedServiceContract.Imp:Ready! client; DebugStub.Print("Service: StopService ENTER\n"); client = managementEndpoint.Acquire(); client.SendStopService(); switch receive { case client.AckStopService(): report = ServiceError.None; break; case client.Busy(): report = ServiceError.TryAgain; break; case client.ChannelClosed(): DebugStub.Print("Service: client channel closed\n"); report = ServiceError.ChannelClosed; break; } managementEndpoint.Release(client); DebugStub.Print("Service: StopService EXIT\n"); return report; } internal virtual ServiceError RestartService() { ServiceError report; report = StopService(); if (report == ServiceError.None) { report = StartService(); } return report; } internal virtual void StartPoll(long interval) {} internal virtual void StopPoll() {} internal bool IsBound() { if (controlProxy != null) { return true; } else { return false; } } internal bool IsReachable() { bool result = false; ServiceError report; ManagedServiceContract.Imp:Ready! ep; ep = managementEndpoint.Acquire(); ep.SendKnock(); switch receive { case ep.RecvAlive(): result = true; break; case ep.ChannelClosed(): result = false; break; } managementEndpoint.Release(ep); return result; } protected virtual void BeforeLoop() {} protected virtual void AfterLoop() {} protected void Run() { ServiceError report; ServiceControlContract.Exp:Ready! ep; ThreadTerminationContract.Exp:Start! signal; BeforeLoop(); ep = controlProxy.Acquire(); signal = receiverRef.Acquire(); for (;;) { switch receive { case ep.StartService(): { DebugStub.Print("Service: Start service\n"); try { report = StartServiceProcess(); if (report != ServiceError.None) { ep.SendNakStartService(report); break; } report = StartService(); if (report != ServiceError.None) { ep.SendNakStartService(report); break; } id = process.Id; ServiceManager.Register(this); ep.SendAckStartService(); DebugStub.Print("Service: started\n"); } catch (ProcessCreateException) { DebugStub.Print("Service: " + "Process creation failed\n"); ep.SendNakStartService(ServiceError.NotFound); } catch (ProcessStateException) { ep.SendNakStartService(ServiceError.TryAgain); } break; } case ep.StopService(): { report = StopService(); if (report != ServiceError.None) { ep.SendNakStopService(report); break; } report = StopServiceProcess(); if (report != ServiceError.None) { ep.SendNakStopService(report); break; } ServiceManager.Deregister(this); ep.SendAckStopService(); break; } case ep.RestartService(): { DebugStub.Print("Service: Restarting '{0}'...\n", __arglist(serviceName)); report = RestartService(); if (report != ServiceError.None) { ep.SendNakRestartService(report); break; } DebugStub.Print("Service: '{0}' restarted\n", __arglist(serviceName)); ep.SendAckRestartService(); break; } case ep.StartPoll(interval): { StartPoll(interval); ep.SendAckStartPoll(); break; } case ep.StopPoll(): { StopPoll(); ep.SendAckStopPoll(); break; } case ep.ChannelClosed(): { controlProxy = null; goto exit; break; } case signal.Stop(): { signal.SendAckStop(); goto exit; break; } case signal.ChannelClosed(): { goto exit; break; } } } exit: if (controlProxy != null) { controlProxy.Release(ep); } else { delete ep; } DebugStub.Print("service counterpart quit.\n"); receiverRef.Release(signal); AfterLoop(); } internal virtual string! Name { get { return serviceName; } private set {} } internal virtual string! Binary { get { return binaryName; } private set{} } internal virtual ServiceType Type { get { return serviceType; } private set{} } internal virtual int Id { get { return id; } private set{} } internal virtual TRef! Endpoint { get { return managementEndpoint; } private set{} } public override bool Equals(object o) { Service s = o as Service; if (s == null) { return false; } return (this.process.Id == s.process.Id && this.managementEndpoint == s.managementEndpoint); } } }