singrdk/base/Applications/ServiceManager/SMSClient/SMSClient.cs

893 lines
32 KiB
C#

///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Note: Service Manager client program
//
using System;
using System.Threading;
using Microsoft.SingSharp;
using Microsoft.SingSharp.Reflection;
using Microsoft.Singularity;
using Microsoft.Singularity.Applications;
using Microsoft.Singularity.Channels;
using Microsoft.Singularity.Configuration;
using Microsoft.Singularity.Contracts;
using Microsoft.Singularity.Directory;
using Microsoft.Singularity.Io;
using Microsoft.Singularity.ServiceManager;
[assembly: Transform(typeof(ApplicationResourceTransform))]
namespace Microsoft.Singularity.Applications.ServiceManager
{
[ConsoleCategory(HelpMessage="Service management client", DefaultAction=true)]
internal class DefaultConfig
{
[InputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Exp:READY> Stdin;
[OutputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Imp:READY> Stdout;
reflective internal DefaultConfig();
internal int AppMain()
{
Console.WriteLine("Use -? for help.");
return 0;
}
}
[ConsoleCategory(Action="start", HelpMessage="Start a service")]
internal class StartConfig
{
[InputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Exp:READY> Stdin;
[OutputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Imp:READY> Stdout;
[StringParameter("service", Mandatory=true, Position=0)]
internal string serviceName;
// [BoolParameter("w", Mandatory=false, HelpMessage="Wait for service to start.")]
public bool wait;
reflective internal StartConfig();
internal int AppMain()
{
return SMSClient.StartService((!)serviceName, wait);
}
}
[ConsoleCategory(Action="stop", HelpMessage="Stop a service")]
internal class StopServiceCommand
{
[InputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Exp:READY> Stdin;
[OutputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Imp:READY> Stdout;
[StringParameter("service", Mandatory=true, Position=0)]
internal string serviceName;
// [BoolParameter("w", Mandatory=false, HelpMessage="Wait for service to stop.")]
public bool wait;
reflective internal StopServiceCommand();
internal int AppMain()
{
return SMSClient.StopService((!)serviceName, wait);
}
}
[ConsoleCategory(Action="list", HelpMessage="Show a list of services")]
internal class ListConfig
{
[InputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Exp:READY> Stdin;
[OutputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Imp:READY> Stdout;
reflective internal ListConfig();
internal int AppMain()
{
return SMSClient.ListServices(this);
}
}
[ConsoleCategory(Action="show", HelpMessage="Show details about a specific service.")]
internal class ShowServiceParameters
{
[InputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Exp:READY> Stdin;
[OutputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Imp:READY> Stdout;
[StringParameter("service", Position=0, Mandatory=true, HelpMessage="The service to examine.")]
public string ServiceName;
reflective internal ShowServiceParameters();
internal int AppMain()
{
if (ServiceName == null) {
Console.WriteLine("The 'service' parameter is required, but has not been provided.");
return -1;
}
ServiceManagerContract.Imp! svmanager = SMSClient.ConnectServiceManager();
try {
ServiceError error = SMSClient.SelectService(svmanager, this.ServiceName);
if (error != ServiceError.None)
return -1;
bool errors = false;
svmanager.SendQueryServiceConfig();
switch receive {
case svmanager.CurrentServiceConfig(config):
SMSClient.ShowConfigDetailed(config);
config.Dispose();
break;
case svmanager.RequestFailed(err):
if (err == ServiceError.ServiceNotFound) {
Console.WriteLine("There is no service with name '{0}'.", this.ServiceName);
return -1;
}
Console.WriteLine("Failed to query service configuration.");
SMSClient.ShowServiceError(err);
errors = true;
break;
case svmanager.ChannelClosed():
Console.WriteLine("The Service Manager closed its channel unexpectedly.");
return -1;
}
svmanager.SendQueryServiceStatus();
switch receive {
case svmanager.CurrentServiceStatus(status):
Console.WriteLine();
SMSClient.ShowStatusDetailed(status);
status.Dispose();
break;
case svmanager.RequestFailed(err):
Console.WriteLine("Failed to query service status.");
SMSClient.ShowServiceError(err);
errors = true;
break;
case svmanager.ChannelClosed():
Console.WriteLine("The Service Manager closed its channel unexpectedly.");
return -1;
}
if (errors)
return -1;
else
return 0;
}
finally {
delete svmanager;
}
}
}
[ConsoleCategory(Action="watch", HelpMessage="Watch the status of a service")]
internal class WatchConfig
{
[InputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Exp:READY> Stdin;
[OutputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Imp:READY> Stdout;
[StringParameter("service", Mandatory=true, Position=0)]
internal string serviceName;
reflective internal WatchConfig();
internal int AppMain()
{
return SMSClient.WatchService((!)serviceName);
}
}
[ConsoleCategory(Action="watchall", HelpMessage="Watch the status of the Service Manager")]
internal class WatchAllConfig
{
[InputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Exp:READY> Stdin;
[OutputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Imp:READY> Stdout;
#if false
[BoolParameter("c", Mandatory=false, HelpMessage="Watch all service configuration changes")]
public bool watchConfigChanges;
[BoolParameter("s", Mandatory=false, HelpMessage="Watch all service status changes")]
public bool watchStatusChanges;
#endif
reflective internal WatchAllConfig();
internal int AppMain()
{
#if false
ServiceManagerEventMask desiredEvents = 0;
if (watchConfigChanges)
desiredEvents |= ServiceManagerEventMask.AnyServiceConfig;
if (watchStatusChanges)
desiredEvents |= ServiceManagerEventMask.AnyServiceStatus;
if (desiredEvents == 0)
desiredEvents = ServiceManagerEventMask.AnyServiceConfig | ServiceManagerEventMask.AnyServiceStatus;
#else
ServiceManagerEventMask desiredEvents = ServiceManagerEventMask.AnyServiceConfig | ServiceManagerEventMask.AnyServiceStatus;
#endif
ServiceManagerContract.Imp! svmanager = SMSClient.ConnectServiceManager();
svmanager.SendWatchServiceManager(desiredEvents);
switch receive {
case svmanager.Ok():
break;
case svmanager.RequestFailed(error):
Console.WriteLine("The Service Manager rejected the subscription request.");
SMSClient.ShowServiceError(error);
delete svmanager;
return -1;
}
Console.WriteLine("Watching Service Manager...");
for (;;) {
Console.WriteLine("Sending WaitNextServiceManagerChange");
svmanager.SendWaitNextServiceManagerChange();
Console.WriteLine("switch-receive");
switch receive {
case svmanager.ServiceManagerChanged(events):
Console.WriteLine("Service Manager events fired: {0:x8}", ((uint)events));
break;
}
}
}
}
[ConsoleCategory(Action="create", HelpMessage="Create a new service entry")]
internal class CreateServiceCommand
{
[InputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Exp:READY> Stdin;
[OutputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Imp:READY> Stdout;
[StringParameter("service", Mandatory=true, Position=0)]
internal string serviceName;
[StringParameter("exe", Mandatory=false, HelpMessage="Executable to use; if omitted, uses service name.")]
public string executableName;
[StringParameter("display", Mandatory=false, HelpMessage="Display name to use; if omitted, uses service name.")]
public string displayName;
[BoolParameter("disabled", Mandatory=false, HelpMessage="Create in a disabled state.")]
public bool isAdministrativelyDisabled;
reflective internal CreateServiceCommand();
internal int AppMain()
{
assert serviceName != null;
if (serviceName.Length == 0) {
Console.WriteLine("Invalid service name.");
return -1;
}
if (executableName == null || executableName.Length == 0)
executableName = serviceName;
if (displayName == null || displayName.Length == 0)
displayName = serviceName;
ServiceManagerContract.Imp! svmanager = SMSClient.ConnectServiceManager();
try {
ServiceConfig config = new ServiceConfig();
config.ServiceName = Bitter.FromString2(serviceName);
config.ExecutableName = Bitter.FromString2(executableName);
config.DisplayName = Bitter.FromString2(displayName);
config.IsAdministrativelyDisabled = isAdministrativelyDisabled;
config.MinProcesses = 0;
config.MaxProcesses = 1;
config.MaxClientsPerProcess = ServiceConfig.UnlimitedClientsPerProcess;
config.MaxProcessAgeInSeconds = ServiceConfig.UnlimitedProcessAge;
svmanager.SendCreateService(config);
switch receive {
case svmanager.Ok():
Console.WriteLine("Service was successfully created.");
return 0;
case svmanager.RequestFailed(error):
Console.WriteLine("Failed to create service.");
SMSClient.ShowServiceError(error);
return -1;
}
}
finally {
delete svmanager;
}
}
}
[ConsoleCategory(Action="delete", HelpMessage="Delete an existing service")]
internal class DeleteServiceCommand
{
[InputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Exp:READY> Stdin;
[OutputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Imp:READY> Stdout;
[StringParameter("service", Mandatory=true, Position=0)]
internal string serviceName;
reflective internal DeleteServiceCommand();
internal int AppMain()
{
assert serviceName != null;
if (serviceName.Length == 0) {
Console.WriteLine("Invalid service name.");
return -1;
}
ServiceManagerContract.Imp! svmanager = SMSClient.ConnectServiceManager();
try {
ServiceError error = SMSClient.SelectService(svmanager, serviceName);
if (error != ServiceError.None)
return -1;
svmanager.SendDeleteService();
switch receive {
case svmanager.Ok():
Console.WriteLine("Service was successfully deleted.");
return 0;
case svmanager.RequestFailed(err):
Console.WriteLine("Failed to delete service.");
SMSClient.ShowServiceError(err);
return -1;
}
}
finally {
delete svmanager;
}
}
}
[ConsoleCategory(Action="enable", HelpMessage="Enable a service")]
internal class EnableServiceCommand
{
[InputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Exp:READY> Stdin;
[OutputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Imp:READY> Stdout;
[StringParameter("service", Mandatory=true, Position=0)]
internal string serviceName;
reflective internal EnableServiceCommand();
internal int AppMain()
{
assert serviceName != null;
if (serviceName.Length == 0) {
Console.WriteLine("Invalid service name.");
return -1;
}
ServiceManagerContract.Imp! svmanager = SMSClient.ConnectServiceManager();
try {
ServiceError error = SMSClient.SelectService(svmanager, serviceName);
if (error != ServiceError.None)
return -1;
svmanager.SendEnableService(true);
switch receive {
case svmanager.Ok():
Console.WriteLine("Service was successfully enabled.");
return 0;
case svmanager.RequestFailed(err):
Console.WriteLine("Failed to enable service.");
SMSClient.ShowServiceError(err);
return -1;
}
}
finally {
delete svmanager;
}
}
}
[ConsoleCategory(Action="disable", HelpMessage="Disable a service")]
internal class DisableServiceCommand
{
[InputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Exp:READY> Stdin;
[OutputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Imp:READY> Stdout;
[StringParameter("service", Mandatory=true, Position=0)]
internal string serviceName;
reflective internal DisableServiceCommand();
internal int AppMain()
{
assert serviceName != null;
if (serviceName.Length == 0) {
Console.WriteLine("Invalid service name.");
return -1;
}
ServiceManagerContract.Imp! svmanager = SMSClient.ConnectServiceManager();
try {
ServiceError error = SMSClient.SelectService(svmanager, serviceName);
if (error != ServiceError.None)
return -1;
svmanager.SendEnableService(false);
switch receive {
case svmanager.Ok():
Console.WriteLine("Service was successfully disabled.");
return 0;
case svmanager.RequestFailed(err):
Console.WriteLine("Failed to disable service.");
SMSClient.ShowServiceError(err);
return -1;
}
}
finally {
delete svmanager;
}
}
}
[ConsoleCategory(Action="kill", HelpMessage="Terminate the process(es) of a service.")]
internal class TerminateServiceCommand
{
[InputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Exp:READY> Stdin;
[OutputEndpoint("data")]
public readonly TRef<UnicodePipeContract.Imp:READY> Stdout;
[StringParameter("service", Mandatory=true, Position=0)]
internal string serviceName;
[LongParameter("pid", Mandatory=false, Position=1, Default=-1)]
internal long processId;
reflective internal TerminateServiceCommand();
internal int AppMain()
{
assert serviceName != null;
if (serviceName.Length == 0) {
Console.WriteLine("Invalid service name.");
return -1;
}
ServiceManagerContract.Imp! svmanager = SMSClient.ConnectServiceManager();
try {
ServiceError error = SMSClient.SelectService(svmanager, serviceName);
if (error != ServiceError.None)
return -1;
if (processId != -1)
svmanager.SendTerminateServiceProcess((int)processId);
else
svmanager.SendTerminateServiceAllProcesses();
switch receive {
case svmanager.Ok():
return 0;
case svmanager.RequestFailed(err):
SMSClient.ShowServiceError(err);
return -1;
}
}
finally {
delete svmanager;
}
}
}
public class SMSClient
{
internal static ServiceManagerContract.Imp! ConnectServiceManager()
{
using (ImpatientWatcher watcher = new ImpatientWatcher("ConnectServiceManager", "create channel", 250)) {
ErrorCode error;
ServiceManagerContract.Imp! manager_imp;
ServiceManagerContract.Exp! manager_exp;
ServiceManagerContract.NewChannel(out manager_imp, out manager_exp);
watcher.NextStep("NewClientEndpoint", 250);
DirectoryServiceContract.Imp! rootds = DirectoryService.NewClientEndpoint();
try {
watcher.NextStep("SdsUtils.Bind", 1000);
if (SdsUtils.Bind(ServiceManagerContract.ModuleName, rootds, manager_exp, out error)) {
watcher.NextStep("RecvSuccess", 250);
manager_imp.RecvSuccess();
return manager_imp;
}
else {
delete manager_imp;
Console.WriteLine("Failed to contact the Service Manager. Error: " + SdsUtils.ErrorCodeToString(error));
throw new Exception("Failed to connect to Service Manager.");
}
}
finally {
delete rootds;
}
}
}
internal static ServiceError SelectService(ServiceManagerContract.Imp! svmanager, string! serviceName)
{
svmanager.SendSelectService(Bitter.FromString2(serviceName));
switch receive {
case svmanager.Ok():
// Console.WriteLine("Successfully selected service '{0}'.", serviceName);
return ServiceError.None;
case svmanager.RequestFailed(error):
Console.WriteLine("Failed to select service '{0}'.", serviceName);
ShowServiceError(error);
return error;
case svmanager.ChannelClosed():
throw new Exception("Service Manager closed channel before responding.");
}
}
internal static void ShowServiceError(ServiceError error)
{
Console.WriteLine("ServiceError: " + ServiceEnums.ToString(error));
}
static internal int StartService(string! serviceName, bool wait)
{
ServiceManagerContract.Imp! svmanager = ConnectServiceManager();
try {
ServiceError error = SelectService(svmanager, serviceName);
if (error != ServiceError.None) {
return -1;
}
if (wait) {
svmanager.SendStartServiceWait();
for (;;) {
switch receive {
case svmanager.RequestFailed(err):
ShowServiceError(err);
return -1;
case svmanager.ServiceStarting():
Console.WriteLine("Service Manager accepted request to start service '{0}'.", serviceName);
return 0;
case timeout(TimeSpan.FromSeconds(10)):
Console.WriteLine("waiting...");
break;
}
}
}
else {
svmanager.SendStartServiceNoWait();
switch receive {
case svmanager.RequestFailed(err):
ShowServiceError(err);
return -1;
case svmanager.ServiceStarting():
Console.WriteLine("Service Manager accepted request to start service '{0}'.", serviceName);
return 0;
}
}
}
finally {
delete svmanager;
}
}
internal static int StopService(string! serviceName, bool wait)
{
ServiceManagerContract.Imp! svmanager = ConnectServiceManager();
try {
ServiceError error = SelectService(svmanager, serviceName);
if (error != ServiceError.None)
return -1;
if (wait) {
svmanager.SendStopServiceWait();
for (;;) {
switch receive {
case svmanager.RequestFailed(err):
ShowServiceError(err);
if (err == ServiceError.CannotStopService) {
Console.WriteLine("If this service is an 'always active' service, then the stop command cannot be used.");
Console.WriteLine("Instead, use the 'disable' command.");
}
return -1;
case svmanager.ServiceStopping():
Console.WriteLine("Service Manager accepted request to stop service '{0}'.", serviceName);
return 0;
case timeout(TimeSpan.FromSeconds(10)):
Console.WriteLine("waiting...");
break;
}
}
}
else {
svmanager.SendStopServiceNoWait();
switch receive {
case svmanager.RequestFailed(err):
ShowServiceError(err);
return -1;
case svmanager.ServiceStopping():
Console.WriteLine("Service Manager accepted request to stop service '{0}'.", serviceName);
return 0;
}
}
}
finally {
delete svmanager;
}
}
internal static int WatchService(string! serviceName)
{
ServiceManagerContract.Imp! svmanager = ConnectServiceManager();
try {
ServiceError error = SelectService(svmanager, serviceName);
if (error != ServiceError.None) {
return -1;
}
svmanager.SendWatchServiceStatus();
switch receive {
case svmanager.RequestFailed(err):
ShowServiceError(err);
return -1;
case svmanager.Ok():
Console.WriteLine("Service Manager accepted request to watch service '{0}'.", serviceName);
break;
}
for (;;) {
svmanager.SendWaitServiceChange();
switch receive {
case svmanager.ServiceStatusChanged(ServiceStatus status, bool missedChange):
Console.WriteLine("Status changed: ");
Console.WriteLine(" State: " + ServiceEnums.ToString(status.State));
if (missedChange) {
Console.WriteLine(" Note: At least one status change was missed.");
}
break;
case svmanager.RequestFailed(err):
Console.WriteLine("Request failed.");
ShowServiceError(err);
return -1;
case svmanager.ChannelClosed():
Console.WriteLine("Service Manager closed channel!");
return -1;
}
}
}
finally {
delete svmanager;
}
}
const string ListServiceFormat = "{0,-20} {1,-10} {2,-6} {3}";
internal static int ListServices(ListConfig! config)
{
ServiceManagerContract.Imp! svmanager = ConnectServiceManager();
try {
Console.WriteLine(ListServiceFormat, "Name", "State", "PID", "Display Name");
Console.WriteLine(ListServiceFormat,
new String('-', 20),
new String('-', 10),
new String('-', 6),
new String('-', 30));
ServiceInfo[]! in ExHeap first_entries = new[ExHeap] ServiceInfo[40];
svmanager.SendEnumerateServices(first_entries);
for (;;) {
switch receive {
case svmanager.EnumerationTerminated(entries, count):
ListServices(entries, count);
delete entries;
return 0;
case svmanager.NextServiceInfo(entries, count):
ListServices(entries, count);
svmanager.SendEnumerateServices(entries);
break;
case svmanager.ChannelClosed():
Console.WriteLine("Service Manager channel closed");
return -1;
}
}
}
finally {
delete svmanager;
}
}
static void ListServices(ServiceInfo[]! in ExHeap entries, int count)
{
for (int i = 0; i < count; i++) {
expose(entries[i])
{
string! serviceName = ToString(entries[i].Config.ServiceName);
string! displayName = ToString(entries[i].Config.DisplayName);
string! stateString = ServiceEnums.ToString(entries[i].Status.State);
string! processIdString = entries[i].Status.ProcessId != -1 ?
(!)entries[i].Status.ProcessId.ToString() : "";
Console.WriteLine(ListServiceFormat, serviceName, stateString, processIdString, displayName);
}
}
}
public static string! ToString(char[] in ExHeap str)
{
if (str != null)
return Bitter.ToString2(str);
else
return "";
}
public static void ShowConfigDetailed(ServiceConfig config)
{
if (config.ServiceName == null) {
Console.WriteLine("Error: A ServiceConfig structure had a null ServiceName field.");
return;
}
string! serviceName = Bitter.ToString2(config.ServiceName);
string! executableName = config.ExecutableName != null ? Bitter.ToString2(config.ExecutableName) : "";
string! displayName = config.DisplayName != null ? Bitter.ToString2(config.DisplayName) : "";
Console.WriteLine("Service Configuration");
Console.WriteLine("---------------------");
Console.WriteLine();
Console.WriteLine(DetailFormat, "Service Name", serviceName);
Console.WriteLine(DetailFormat, "Executable", executableName);
Console.WriteLine(DetailFormat, "Display Name", displayName);
Console.WriteLine(DetailFormat, "Activation Mode", ServiceEnums.ToString(config.ActivationMode));
Console.WriteLine(DetailFormat, "Is Disabled?", config.IsAdministrativelyDisabled.ToString());
Console.WriteLine(DetailFormat, "Min/Max Processes", String.Format("min {0} / {1}",
config.MinProcesses,
config.MaxProcesses == ServiceConfig.UnlimitedProcesses ? "no max" : "max " + config.MaxProcesses));
string! max_age_text;
if (config.MaxProcessAgeInSeconds == ServiceConfig.UnlimitedProcessAge)
max_age_text = "unlimited";
else {
TimeSpan limit = TimeSpan.FromSeconds(config.MaxProcessAgeInSeconds);
max_age_text = (!)limit.ToString();
}
Console.WriteLine(DetailFormat, "Max Process Age", max_age_text);
string clients_per_process_text;
if (config.MaxClientsPerProcess == ServiceConfig.UnlimitedClientsPerProcess) {
clients_per_process_text = "unlimited";
}
else {
clients_per_process_text = config.MaxClientsPerProcess.ToString();
}
Console.WriteLine(DetailFormat, "Max Clients per Process", clients_per_process_text);
}
const string DetailFormat = "{0,-25}: {1}";
public static void ShowStatusDetailed(ServiceStatus status)
{
Console.WriteLine("Service Status");
Console.WriteLine("--------------");
Console.WriteLine();
Console.WriteLine(DetailFormat, "State", ServiceEnums.ToString(status.State));
if (status.State != ServiceState.Stopped) {
Console.WriteLine(DetailFormat, "Process ID", status.ProcessId);
}
// This is not yet accurate.
// Console.WriteLine(DetailFormat, "Total Active Clients", status.TotalActiveClients);
Console.WriteLine(DetailFormat, "Total Active Processes", status.TotalActiveProcesses);
Console.WriteLine(DetailFormat, "Connect Queue Length", status.ConnectQueueLength);
Console.WriteLine(DetailFormat, "Last Process Start", status.LastStartFailed ? "FAILED" : "Succeeded");
if (status.LastStartFailed) {
Console.WriteLine(DetailFormat, "Last Start Error", ServiceEnums.ToString(status.LastStartError));
}
}
}
}