Woof.ServiceInstaller 6.0.5

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package Woof.ServiceInstaller --version 6.0.5                
NuGet\Install-Package Woof.ServiceInstaller -Version 6.0.5                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Woof.ServiceInstaller" Version="6.0.5" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Woof.ServiceInstaller --version 6.0.5                
#r "nuget: Woof.ServiceInstaller, 6.0.5"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Woof.ServiceInstaller as a Cake Addin
#addin nuget:?package=Woof.ServiceInstaller&version=6.0.5

// Install Woof.ServiceInstaller as a Cake Tool
#tool nuget:?package=Woof.ServiceInstaller&version=6.0.5                

Woof.ServiceInstaller

.NET extension created by CodeDog

Distributed under MIT License. (c)2021 by CodeDog, All rights reserved.


About

Installs and runs system services on Linux and Windows.

When {command} is your compiled service as one file native executable:

  • For help use {command} --help.
  • To install: sudo {command} --install
  • To uninstall: sudo {command} --uninstall
  • To delete Windows Event Log: {command} --delete-log

Windows

Install your executable with a Microsoft Installer first. For protected configurations use DataProtectionScope.LocalMachine. The service will be registered in the location it was run with the --install option.

On Windows, Windows Installer (MSI) is the default way to install applications, so the service installer does not copy the files elsewhere.

To check if the service is running use either Services panel, or type sc query {serviceName} in cmd.

You can also use Windows Event Log to look for entries from the service.

The service runs in LocalSystem user context.

When the service is uninstalled with --uninstal option it will be automatically stopped, then unregistered. The logs will stay until deleted with --delete-log option.

Linux

When run with sudo and --install option, the service will be copied to the /srv/{serviceName} directory and the permission for the directory will be set to user service (the user account set in configuration).

If the system user service does not exist it will be created. The service will be run in service user context. The service can be configured to use any system user and group. Both user and group will be created if they don't exist.

(See the ServiceMetadata XML documentation.)

Linux system services are meant to be installed from a console as root user.

To check if the service is running type service {serviceName} status.

The logging events from the service will go to the main system log.

When the service is uninstalled with --uninstall option the service installed files will be deleted but the service user will stay.

Also the Linux logs are not cleared.

Common

For service configuration options see the configuration JSON in example, also check the ServiceMetadata class XML documentation.

The configuration for the example service is provided with Woof.Config package.

Installing the service with the same name as an existing one will cause the current one to be automatically stopped and uninstalled first.

Naturally, the service will be uninstalled if it was installed with Woof.ServiceInstaller and its configuration matches.

The described feature is designed for easy software upgrades. Just install the new version, the service will be upgraded, no extra steps needed.

The installed services can be controlled (started, stopped, queried and unregistered) using the target system service controller.

The default configuration makes the service start as soon as it's installed.

The Woof.ServiceInstaller package was made to install self-hosting WebSocket APIs on both Linux and Windows hosts, however additional networking setup is needed for that.

To build a WebSocket API use Woof.Net.WebSockets package.

Operation / Design

Both Windows Service and Linux systemd use the IHostedService interface.

It allows the service to be started and stopped.

The service executable is a normal executable that starts the service if run without parameters. The process never exists unless it receives the stop signal from the host. The services should use the CancellationToken provided with Start() and Stop() methods.

The services will restart after the host is restarted.

The command line interface

The service installer uses the Woof.CommandLine to provide a unified, POSIX type command line interface.

By default the service installer automatically binds the --install|-i, --uninstall|-u and --delete-log|-d options to its internal handlers.

It doesn't bind the --help|-h|-? option, it should be done in user code, because the user code can provide its own additional options. --query|-q and --running|r options are defined in the example to illustrate how it's done.

The automatically generated help for the options can be obtained from CommandLine.Help property.

Similarily, command line validation errors can be obtained from CommandLine.ValidationErrors property after the Parse() method was called.

Usage

Here's example Program.cs:

using System;
using System.Threading.Tasks;

using Woof;
using Woof.Config;
using Woof.ServiceInstaller;

var config = new JsonConfig();
var commandLine = CommandLine.Default;

ServiceInstaller.Configure<TestService.TestService>(config);
if (args?.Length > 0) {
    commandLine.Map<Options>();
    commandLine.Delegates.Add(Options.Help, Help);
    commandLine.Delegates.Add(Options.Query, QueryAsync);
    commandLine.Delegates.Add(Options.Running, IsRunningAsync);
    commandLine.Parse(args);
    var errors = CommandLine.ValidationErrors;
    if (errors is not null) Error(errors);
    else await commandLine.RunDelegatesAsync();
}
else await ServiceInstaller.ServiceMetadata!.RunHostAsync<TestService.TestService>();

static void Help() => Console.WriteLine(CommandLine.Help);

static void Error(string? errorText) {
    Console.WriteLine(errorText);
    Console.WriteLine(CommandLine.Usage);
}

static async ValueTask IsRunningAsync()
    => Console.WriteLine((await ServiceInstaller.ServiceMetadata!.IsRunningAsync()) ? "RUNNING" : "NOT RUNNING");

static async ValueTask QueryAsync()
    => Console.WriteLine(await ServiceInstaller.ServiceMetadata!.QueryAsync());

[Usage("(sudo) {command} [--install|--uninstall|--help]")]
enum Options {
    [Option("q|query", null, "Shows current service status.")]
    Query,
    [Option("r|running", null, "Tests if the service is running.")]
    Running,
    [Option("?|h|help", null, "Displays this help message.")]
    Help
}

To execute code before or after installation / uninstallation simply check if the install/uninstall options are present before and/or after RunDelegatesAsync like this:

if (commandLine.HasOption(ServiceInstaller.Options.Install)) {
    // code to execute
}

The installed service is started with RunHostAsync:

ServiceInstaller.ServiceMetadata!.RunHostAsync<TestService.TestService>();

If executing of some external commands is needed before or after the installation, Shell.ExecAsync method from Woof.LinuxAdmin dependency can be used for that. If the command executed that way fails, the method will throw an InvalidOperationException with the error message obtained from the command output.

For more information see the included example project.

Compatibility

The package is compatible with .NET 5.0 or newer. Tested on Windows 11 and Ubuntu 20.


Disclaimer

Please report any issues on GitHub.

Woof Toolkit is a work in progress in constant development, however it's carefully maintained with production code quality.

Product Compatible and additional computed target framework versions.
.NET net5.0 is compatible.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

FIX: Configuration loading.