491 lines
17 KiB
Plaintext
491 lines
17 KiB
Plaintext
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// 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
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Service process counterpart.
|
||
|
/// </summary>
|
||
|
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;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Management channel to the service
|
||
|
/// </summary>
|
||
|
protected TRef<ManagedServiceContract.Imp:Ready> managementEndpoint;
|
||
|
|
||
|
/// <summary>
|
||
|
/// User-Service Communication Relay Point
|
||
|
/// </summary>
|
||
|
private TRef<ServiceControlContract.Exp:Ready> controlProxy;
|
||
|
|
||
|
protected TRef<DirectoryServiceContract.Imp:Ready> directory;
|
||
|
private TRef<ThreadTerminationContract.Imp:Start> senderRef;
|
||
|
private TRef<ThreadTerminationContract.Exp:Start> receiverRef;
|
||
|
|
||
|
private TRef<ServiceProxyContract.Exp:Start> 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<ThreadTerminationContract.Imp:Start>(imp);
|
||
|
receiverRef = new TRef<ThreadTerminationContract.Exp:Start>(exp);
|
||
|
directory = new TRef<DirectoryServiceContract.Imp:Ready>(ds);
|
||
|
}
|
||
|
|
||
|
~Service()
|
||
|
{
|
||
|
delete directory.Acquire();
|
||
|
delete senderRef.Acquire();
|
||
|
delete receiverRef.Acquire();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Starts this service counter part. Doesn't start any service process.
|
||
|
/// </summary>
|
||
|
internal void Start([Claims]ServiceControlContract.Exp:Start! ep)
|
||
|
{
|
||
|
ep.SendSuccess();
|
||
|
controlProxy = new TRef<ServiceControlContract.Exp:Ready>(ep);
|
||
|
thread = new Thread(new ThreadStart(Run));
|
||
|
thread.Start();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Terminates the service counter part.
|
||
|
/// </summary>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Brings up the service that this object represents. Throws
|
||
|
/// ProcessCreateException.
|
||
|
/// </summary>
|
||
|
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<ServiceProxyContract.Exp:Start>(exp);
|
||
|
|
||
|
process.Start();
|
||
|
|
||
|
// Make sure the process has been started
|
||
|
switch receive {
|
||
|
case msClient.RecvSuccess():
|
||
|
managementEndpoint = new TRef<ManagedServiceContract.Imp:Ready>(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;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Shuts down the service process.
|
||
|
/// </summary>
|
||
|
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<ManagedServiceContract.Imp:Ready>! 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);
|
||
|
}
|
||
|
}
|
||
|
}
|