Skip to content

Commit

Permalink
Merge pull request #1571 from tgstation/MoreDebGotchas
Browse files Browse the repository at this point in the history
.Deb Finalization
  • Loading branch information
Cyberboss authored Jun 24, 2023
2 parents 39f1409 + 2102a83 commit e147256
Show file tree
Hide file tree
Showing 20 changed files with 214 additions and 114 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci-suite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,7 @@ jobs:
dotnet publish -c ${{ matrix.configuration }}NoService --no-build -o ../../Artifacts/Console/lib/Default
mv ../../Artifacts/Console/lib/Default/appsettings.yml ../../Artifacts/Console/appsettings.yml
rm ../../Artifacts/Console/lib/Default/Tgstation.Server.Host
rm ../../Artifacts/Console/Tgstation.Server.Host.Console
- name: Package Server Update Package
if: ${{ matrix.configuration == 'Release' && matrix.watchdog-type == 'System' && matrix.database-type == 'PostgresSql' }}
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ Older server versions can be found in the V# branches of this repository. Note t

### Pre-Requisites

- [ASP .NET Core Runtime (>= v6.0)](https://dotnet.microsoft.com/download/dotnet/6.0) (Choose the option to `Run Server Apps` for your system) If you plan to install tgstation-server as a Windows service, you should also ensure that your .NET Framework runtime version is >= v4.7.2 (Download can be found on same page). Ensure that the `dotnet` executable file is in your system's `PATH` variable (or that of the user's that will be running the server).
- A [MariaDB](https://downloads.mariadb.org/), MySQL, [PostgresSQL](https://www.postgresql.org/download/), or [Microsoft SQL Server](https://www.microsoft.com/en-us/download/details.aspx?id=55994) database engine is required

### Installation
Expand All @@ -27,6 +26,8 @@ Follow the instructions for your OS below.

#### Windows

Download and install the [ASP .NET Core Runtime (>= v6.0)](https://dotnet.microsoft.com/download/dotnet/6.0) (Choose the option to `Run Server Apps` for your system). If you plan to install tgstation-server as a Windows service, you should also ensure that your .NET Framework runtime version is >= v4.7.2 (Most modern systems have it by default. Download can be found on same page). Ensure that the `dotnet` executable file is in your system's `PATH` variable (or that of the user's that will be running the server), you can test this by opening a command prompt and running `dotnet --list-runtimes`.

[Download the latest release .zip](https://github.com/tgstation/tgstation-server/releases/latest). You probably want the `ServerService` package. Choose `ServerConsole` if you prefer not to use the Windows service.

Extract the .zip file to where you want the server to run from. Note the account running the server must have write and delete access to the `lib` subdirectory.
Expand All @@ -43,7 +44,7 @@ Installing natively is the recommended way to run tgstation-server on Linux.

##### Ubuntu

Install TGS and all it's dependencies via our apt repository with this one-liner:
Install TGS and all it's dependencies via our apt repository, interactively configure it, and start the service with this one-liner:

```sh
sudo dpkg --add-architecture i386 \
Expand All @@ -52,7 +53,9 @@ sudo dpkg --add-architecture i386 \
&& sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv B6FD15EE7ED77676EAEAF910EEEDC8280A307527 \
&& sudo add-apt-repository -y "deb https://tgstation.github.io/tgstation-ppa/debian unstable main" \
&& sudo apt update \
&& sudo apt install -y tgstation-server
&& sudo apt install -y tgstation-server \
&& sudo tgs-configure \
&& sudo systemctl start tgstation-server
```

##### Debian
Expand Down
16 changes: 4 additions & 12 deletions build/package/deb/MakeInstall
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
#!/usr/bin/make -f

install:
install -d $(DESTDIR)/opt/tgstation-server
cp -r artifacts/* $(DESTDIR)/opt/tgstation-server
install -d $(DESTDIR)/etc/tgstation-server
cp artifacts/appsettings.yml $(DESTDIR)/etc/tgstation-server/appsettings.ex.yml
echo -e "# tgstation-server configuration file\n# See /etc/tgstation-server/appsettings.ex.yml for details on individual configuration options" > $(DESTDIR)/etc/tgstation-server/appsettings.Production.yml
dh_link $(DESTDIR)/etc/tgstation-server/appsettings.Production.yml $(DESTDIR)/opt/tgstation-server/appsettings.Production.yml
install -d $(DESTDIR)/usr/bin
install build/package/deb/tgs-configure $(DESTDIR)/usr/bin/

uninstall:
mkdir -p $(DESTDIR)/opt/tgstation-server
cp -r artifacts/* $(DESTDIR)/opt/tgstation-server/
build/package/deb/install_artifacts.sh "$(DESTDIR)"
install -D build/package/deb/appsettings.Initial.yml "$(DESTDIR)/etc/tgstation-server/appsettings.Production.yml"
install src/Tgstation.Server.Host/appsettings.yml "$(DESTDIR)/etc/tgstation-server/appsettings.ex.yml"
install -D build/package/deb/tgs-configure "$(DESTDIR)/usr/bin/tgs-configure"
2 changes: 2 additions & 0 deletions build/package/deb/appsettings.Initial.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# tgstation-server configuration file
# See /opt/tgstation-server/appsettings.yml for details on individual configuration options
1 change: 1 addition & 0 deletions build/package/deb/debian/links
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/etc/tgstation-server/appsettings.Production.yml /opt/tgstation-server/appsettings.Production.yml
16 changes: 7 additions & 9 deletions build/package/deb/debian/postinst
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
#!/bin/sh -e

if [[ "$1" = "configure" ]]; then
systemctl mask tgstation-server
fi

#DEBHELPER#

if [[ "$1" = "configure" ]]; then
if [ "$1" = "configure" ]; then
chmod 600 /etc/tgstation-server
deb-systemd-helper stop 'tgstation-server.service' >/dev/null || true

echo " _ _ _ _ "
echo " | |_ __ _ ___| |_ __ _| |_(_) ___ _ __ ___ ___ _ ____ _____ _ __ "
echo " | __/ _` / __| __/ _` | __| |/ _ \| '_ \ _____/ __|/ _ \ '__\ \ / / _ \ '__|"
echo " | || (_| \__ \ || (_| | |_| | (_) | | | |_____\__ \ __/ | \ V / __/ | "
echo " \__\__, |___/\__\__,_|\__|_|\___/|_| |_| |___/\___|_| \_/ \___|_| "
echo " | __/ _\` / __| __/ _\` | __| |/ _ \\| '_ \\ _____/ __|/ _ \\ '__\\ \\ / / _ \\ '__|"
echo " | || (_| \\__ \\ || (_| | |_| | (_) | | | |_____\\__ \\ __/ | \\ V / __/ | "
echo " \\__\\__, |___/\\__\\__,_|\\__|_|\\___/|_| |_| |___/\\___|_| \\_/ \\___|_| "
echo " |___/ "
echo "tgstation-server is now installed but must first be configured"
echo "Run 'sudo tgs-configure' to interactively configure your server"
echo "Alternatively, edit '/etc/tgstation-server/appsettings.Production.yml' to your desired specifications"
echo "Once complete, run 'sudo systemctl start tgstation-server' to start the service"
echo "You should do this now to prevent the service from starting with invalid configuration on the next system reboot"
fi
1 change: 1 addition & 0 deletions build/package/deb/debian/rules
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ override_dh_auto_build:
cd src/Tgstation.Server.Host && dotnet publish -c Release -o ../../artifacts/lib/Default
mv artifacts/lib/Default/appsettings.yml artifacts/appsettings.yml
rm artifacts/lib/Default/Tgstation.Server.Host
rm artifacts/Tgstation.Server.Host.Console

override_dh_auto_install:
cp build/package/deb/MakeInstall ./Makefile
Expand Down
3 changes: 3 additions & 0 deletions build/package/deb/install_artifacts.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/sh -e

cd artifacts && for f in $(find * -type f); do install -D "$f" "$1/opt/tgstation-server/$f"; done
6 changes: 3 additions & 3 deletions build/package/deb/tgs-configure
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/sh

pushd /opt/tgstation-server
dotnet /opt/tgstation-server/lib/Default/Tgstation.Server.Host.dll General:SetupWizardMode=Only
popd
cd /opt/tgstation-server
export General__SetupWizardMode=Only
exec /usr/bin/dotnet /opt/tgstation-server/lib/Default/Tgstation.Server.Host.dll
7 changes: 5 additions & 2 deletions build/tgstation-server.service
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,20 @@ After=postgresql.service
After=mssql-server.service

[Service]
Type=notify
Type=notify-reload
NotifyAccess=all
ExecStart=/bin/bash /opt/tgstation-server/tgs.sh General:SetupWizardMode=Never
WorkingDirectory=/opt/tgstation-server
ExecStart=/usr/bin/dotnet Tgstation.Server.Host.Console.dll --General:SetupWizardMode=Never
TimeoutStartSec=600
Restart=Always
KillMode=process
ReloadSignal=SIGUSR2
RestartKillSignal=SIGUSR2
AmbientCapabilities=CAP_SYS_NICE
StandardOutput=null
StandardError=null
WatchdogSec=60
WatchdogSignal=SIGTERM

[Install]
WantedBy=multi-user.target
1 change: 1 addition & 0 deletions src/Tgstation.Server.Host.Console/tgs.bat
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
@echo off

dotnet %~dp0Tgstation.Server.Host.Console.dll %*
2 changes: 1 addition & 1 deletion src/Tgstation.Server.Host.Console/tgs.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/sh -e

script_full_path=$(dirname "$0")
exec dotnet "$script_full_path/Tgstation.Server.Host.Console.dll" "$@"
92 changes: 92 additions & 0 deletions src/Tgstation.Server.Host.Watchdog/SignalChecker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using System;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.Extensions.Logging;

using Mono.Unix;
using Mono.Unix.Native;

namespace Tgstation.Server.Host.Watchdog
{
/// <summary>
/// Helper for checking POSIX signals.
/// </summary>
static class SignalChecker
{
/// <summary>
/// Forwards certain signals to a given <paramref name="childPid"/>.
/// </summary>
/// <param name="logger">The <see cref="ILogger"/> to write to.</param>
/// <param name="childPid">The <see cref="System.Diagnostics.Process.Id"/> of the process to forward signals to.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
public static async Task CheckSignals(ILogger logger, int childPid, CancellationToken cancellationToken)
{
var signalTcs = new TaskCompletionSource<Signum>();
async Task<Signum?> CheckSignal(Signum signum)
{
try
{
using var unixSignal = new UnixSignal(signum);
if (!unixSignal.IsSet)
{
logger.LogTrace("Waiting for {signum}...", signum);
while (!unixSignal.IsSet)
await Task.Delay(TimeSpan.FromMilliseconds(250), cancellationToken);

logger.LogTrace("{signum} received!", signum);
}
else
logger.LogDebug("{signum} has already been sent", signum);

signalTcs.TrySetResult(signum);
}
catch (OperationCanceledException)
{
}

return signum;
}

var tasks = new[]
{
CheckSignal(Signum.SIGUSR1),
CheckSignal(Signum.SIGUSR2),
};
var completedTask = await Task.WhenAny(tasks);
if (cancellationToken.IsCancellationRequested)
{
await Task.WhenAll(tasks);
return;
}

var signalReceived = await completedTask;
logger.LogInformation("Received {signalReceived}, forwarding to main TGS process!", signalReceived);
var result = Syscall.kill(childPid, signalReceived.Value);
if (result != 0)
logger.LogWarning(
new UnixIOException(Stdlib.GetLastError()),
"Failed to forward {signalReceived}!",
signalReceived);

// forward the other signal if necessary
await Task.WhenAll(tasks);
if (cancellationToken.IsCancellationRequested)
return;

var otherTask = tasks[0] == completedTask
? tasks[1]
: tasks[0];

signalReceived = await otherTask;
logger.LogInformation("Received {signalReceived}, forwarding to main TGS process!", signalReceived);
result = Syscall.kill(childPid, signalReceived.Value);
if (result != 0)
logger.LogWarning(
new UnixIOException(Stdlib.GetLastError()),
"Failed to forward {signalReceived}!",
signalReceived);
}
}
}
89 changes: 28 additions & 61 deletions src/Tgstation.Server.Host.Watchdog/Watchdog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@

using Microsoft.Extensions.Logging;

using Mono.Unix;
using Mono.Unix.Native;

using Tgstation.Server.Host.Common;

namespace Tgstation.Server.Host.Watchdog
Expand Down Expand Up @@ -71,7 +68,8 @@ public async Task RunAsync(bool runConfigure, string[] args, CancellationToken c
{
// VS special tactics
// just copy the shit where it belongs
Directory.Delete(assemblyStoragePath, true);
if (Directory.Exists(assemblyStoragePath))
Directory.Delete(assemblyStoragePath, true);
Directory.CreateDirectory(defaultAssemblyPath);

var sourcePath = "../../../../Tgstation.Server.Host/bin/Debug/net6.0";
Expand Down Expand Up @@ -128,8 +126,8 @@ public async Task RunAsync(bool runConfigure, string[] args, CancellationToken c

if (runConfigure)
{
logger.LogInformation("Running configuration check and wizard if necessary...");
arguments.Add("General:SetupWizardMode=Only");
logger.LogInformation("Running configuration check and wizard...");
arguments.Add("--General:SetupWizardMode=Only");
}

arguments.AddRange(args);
Expand Down Expand Up @@ -179,61 +177,19 @@ public async Task RunAsync(bool runConfigure, string[] args, CancellationToken c
}))
{
var processTask = tcs.Task;
while (!processTask.IsCompleted)
using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);

var checkerTask = isWindows
? Task.CompletedTask
: SignalChecker.CheckSignals(logger, childPid, cts.Token);
try
{
var signalTcs = new TaskCompletionSource<Signum>();
using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
async Task CheckSignal(Signum signum)
{
if (isWindows)
return;

try
{
using var unixSignal = new UnixSignal(signum);
if (!unixSignal.IsSet)
{
logger.LogTrace("Waiting for {signum}...", signum);
while (!unixSignal.IsSet)
await Task.Delay(TimeSpan.FromMilliseconds(250), cts.Token);

logger.LogTrace("{signum} received!", signum);
}
else
logger.LogDebug("{signum} has already been sent", signum);

signalTcs.TrySetResult(signum);
}
catch (OperationCanceledException)
{
}
}

var checkerTask = Task.WhenAll(
CheckSignal(Signum.SIGUSR1),
CheckSignal(Signum.SIGUSR2));
try
{
var signalTask = signalTcs.Task;

var completedTask = await Task.WhenAny(processTask, signalTask);
if (completedTask == signalTask)
{
var signalReceived = await signalTask;
logger.LogInformation("Received {signalReceived}, forwarding to main TGS process!", signalReceived);
var result = Syscall.kill(childPid, signalReceived);
if (result != 0)
logger.LogWarning(
new UnixIOException(Stdlib.GetLastError()),
"Failed to forward {signalReceived}!",
signalReceived);
}
}
finally
{
cts.Cancel();
await checkerTask;
}
await processTask;
}
finally
{
cts.Cancel();
await checkerTask;
}
}
}
Expand All @@ -252,8 +208,19 @@ async Task CheckSignal(Signum signum)
process.WaitForExit();
}
}
catch (InvalidOperationException)
catch (InvalidOperationException ex2)
{
logger.LogWarning(ex2, "Error killing host process!");
}

try
{
if (File.Exists(updateDirectory))
File.Delete(updateDirectory);
}
catch (Exception ex2)
{
logger.LogWarning(ex2, "Error deleting comms file!");
}

logger.LogInformation("Host exited!");
Expand Down
5 changes: 4 additions & 1 deletion src/Tgstation.Server.Host/Setup/SetupWizard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1050,7 +1050,10 @@ async Task HandleSetupCancel()
{
var bytes = await ioManager.ReadAllBytes(userConfigFileName, cancellationToken);
var contents = Encoding.UTF8.GetString(bytes);
var existingConfigIsEmpty = String.IsNullOrWhiteSpace(contents) || contents.Trim() == "{}";
var lines = contents.Split('\n', StringSplitOptions.RemoveEmptyEntries);
var existingConfigIsEmpty = lines
.Select(line => line.Trim())
.All(line => line[0] == '#' || line == "{}" || line.Length == 0);
shouldRunBasedOnAutodetect = existingConfigIsEmpty;
}
else
Expand Down
Loading

0 comments on commit e147256

Please sign in to comment.