summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb-wrapper2/Makefile10
-rwxr-xr-xgdb-wrapper2/gdb-wrapper2.sh92
-rwxr-xr-xgdb-wrapper2/test.sh32
-rw-r--r--gdb-wrapper2/test_program.c154
-rwxr-xr-xrconsole/rconsole40
-rwxr-xr-xrconsole/rconsole-input9
-rwxr-xr-xrconsole/rconsole-output12
-rwxr-xr-xrun-instance.sh111
8 files changed, 460 insertions, 0 deletions
diff --git a/gdb-wrapper2/Makefile b/gdb-wrapper2/Makefile
new file mode 100644
index 0000000..bba6e44
--- /dev/null
+++ b/gdb-wrapper2/Makefile
@@ -0,0 +1,10 @@
+test_program: test_program.c
+ cc test_program.c -o test_program $(CFLAGS) $(LDFLAGS)
+
+test: test.sh test_program
+ ./test.sh
+
+clean:
+ rm test_program
+
+.PHONY: clean
diff --git a/gdb-wrapper2/gdb-wrapper2.sh b/gdb-wrapper2/gdb-wrapper2.sh
new file mode 100755
index 0000000..c60b649
--- /dev/null
+++ b/gdb-wrapper2/gdb-wrapper2.sh
@@ -0,0 +1,92 @@
+#!/bin/bash
+
+# gdb-wrapper2
+# Copyright (C) 2017 Paweł Redman <pawel.redman@gmail.com>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+SELF="$0"
+PROGRAM="$1"
+shift 1
+
+if [ -z "$PROGRAM" ]; then
+ echo "$SELF [program] (args...)"
+ exit 1
+fi
+
+# Check if the program exists before calling GDB so the errors are clearer.
+if [ ! -f "$PROGRAM" ]; then
+ stat "$PROGRAM"
+ exit 1
+fi
+
+# 'catch' and 'break' can be followed by a 'commands' block to tell GDB what
+# to do in case of a breakpoint/signal. This is where the stack is printed
+# and the core is dumped.
+function write_commands {
+ echo "commands"
+ echo "thread apply all backtrace"
+ echo "gcore"
+ echo "echo \n"
+
+ # Terminate if this isn't the first fault to avoid infinite
+ # recursion.
+ echo "set \$faults = \$faults + 1"
+ echo "if \$faults >= 2"
+ echo "echo Double fault, terminating the program.\n"
+ echo "kill"
+ echo "quit"
+ echo "else"
+ # Resume the program's execution.
+ # If the program can handle signals and exit gracefully
+ # then this script should let it (after dumping a core
+ # for debugging).
+ echo "continue"
+ echo "end"
+ echo "end"
+}
+
+function write_config {
+ # Make sure gdb won't ask for interactive input. This is supposed
+ # to never happen when using -batch but I don't trust them.
+ echo "set confirm off"
+ echo "set width unlimited"
+ echo "set height unlimited"
+ echo "set pagination off"
+ echo "set backtrace limit unlimited"
+
+ echo "set \$faults = 0"
+
+ # This catches all signals except SIGINT and SIGTRAP.
+ echo "catch signal"
+ write_commands
+
+ # Catch SIGINT too, just in case.
+ echo "catch signal SIGINT"
+ write_commands
+
+ # Catch calls to Com_Error. You'll want something else here if you're
+ # using this script for another program.
+ echo "break Com_Error"
+ write_commands
+
+ # Finally start the program.
+ echo "run"
+}
+
+# Disable automatic coredumps.
+ulimit -c 0
+
+# For some reason gdb's '-x' option won't accept a pipe as an argument. The
+# workaround is to create a temporary file for the configuration.
+config_path=$(mktemp /tmp/gdb-wrapper2.XXXXXX)
+write_config > "$config_path"
+
+gdb -batch -x "$config_path" --args "$PROGRAM" "$@"
+exit_status="$?"
+
+rm "$config_path"
+exit $exit_status
diff --git a/gdb-wrapper2/test.sh b/gdb-wrapper2/test.sh
new file mode 100755
index 0000000..f5fea63
--- /dev/null
+++ b/gdb-wrapper2/test.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+function do_test {
+ ../gdb-wrapper2.sh ./test_program --verbose "$@"
+}
+
+function title {
+ tput bold
+ tput setaf 3
+ echo "$@"
+ echo
+ tput sgr0
+}
+
+title "Test: Raise 11."
+do_test --raise 11
+
+title "Test: Raise and catch 11."
+do_test --raise 11 --catch 11
+
+title "Test: Raise then keep recursively catching 11."
+do_test --raise 11 --catch 11 --recursive
+
+title "Test: Call Com_Error."
+do_test --Com_Error
+
+title "Test: Call Com_Error then keep recursively calling it."
+do_test --Com_Error --recursive
+
+title "Test: Raise but ignore 2."
+do_test --raise 2 --ignore 2
+
diff --git a/gdb-wrapper2/test_program.c b/gdb-wrapper2/test_program.c
new file mode 100644
index 0000000..e332d5f
--- /dev/null
+++ b/gdb-wrapper2/test_program.c
@@ -0,0 +1,154 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <signal.h>
+
+#define MAX_SIGNALS 32
+
+#define print(fmt, ...) fprintf(stderr, "test_program: "fmt, ##__VA_ARGS__)
+
+_Bool do_raise = 0;
+int raise_signal;
+_Bool do_Com_Error = 0;
+_Bool recursive = 0;
+
+enum { DEFAULT = 0, CATCH, IGNORE };
+int handle[MAX_SIGNALS] = {0};
+_Bool handle_any = 0;
+
+_Bool verbose = 0;
+
+static int read_args(int argc, char **argv)
+{
+ for (int i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "--raise")) {
+ if (i + 1 >= argc)
+ goto needs_argument;
+
+ do_raise = 1;
+ raise_signal = atoi(argv[i + 1]);
+ i++;
+ } else if (!strcmp(argv[i], "--Com_Error")) {
+ do_Com_Error = 1;
+ } else if (!strcmp(argv[i], "--recursive")) {
+ recursive = 1;
+ } else if (!strcmp(argv[i], "--catch") ||
+ !strcmp(argv[i], "--ignore")) {
+ int sig;
+
+ if (i + 1 >= argc)
+ goto needs_argument;
+
+ sig = atoi(argv[i + 1]);
+ if (sig < 0 || sig >= MAX_SIGNALS) {
+ print("invalid argument for --catch/--ignore: %i\n",
+ sig);
+ return 1;
+ }
+
+ if (!strcmp(argv[i], "--catch"))
+ handle[sig] = CATCH;
+ else
+ handle[sig] = IGNORE;
+
+ handle_any = 1;
+ i++;
+ } else if (!strcmp(argv[i], "--ignore")) {
+
+ } else if (!strcmp(argv[i], "--verbose")) {
+ verbose = 1;
+ } else {
+ print("invalid option '%s'\n", argv[i]);
+ return 1;
+ }
+
+ continue;
+ needs_argument:
+ print("option '%s' needs an argument\n", argv[i]);
+ return 1;
+ }
+
+ if (do_Com_Error && do_raise) {
+ print("--raise and --Com_Error are mutually exclusive.\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+_Noreturn void graceful_exit(void)
+{
+ print("Graceful exit.\n");
+ exit(0);
+}
+
+void signal_catcher(int sig)
+{
+ print("Caught signal %i.\n", sig);
+
+ if (recursive) {
+ if (verbose)
+ print("Raising signal %i from within the handler.\n",
+ raise_signal);
+ raise(raise_signal);
+ }
+
+ graceful_exit();
+}
+
+_Noreturn void Com_Error(int type, const char *str)
+{
+ print("Com_Error: %s\n", str);
+
+ if (recursive)
+ Com_Error(666, "Error while erroring");
+
+ graceful_exit();
+}
+
+int main(int argc, char **argv)
+{
+ int rv;
+
+ rv = read_args(argc, argv);
+ if (rv)
+ return rv;
+
+ for (int i = 0; i < MAX_SIGNALS; i++) {
+ void (*fun)(int);
+
+ switch (handle[i]) {
+ case DEFAULT:
+ continue;
+
+ case CATCH:
+ fun = signal_catcher;
+ if (verbose)
+ print("Signal %i will be caught.\n", i);
+ break;
+
+ case IGNORE:
+ fun = SIG_IGN;
+ if (verbose)
+ print("Signal %i will be ignored.\n", i);
+ break;
+ }
+
+ if (signal(i, fun) == SIG_ERR) {
+ print("For signal %i: ", i);
+ perror("signal");
+ return 1;
+ }
+ }
+
+ if (do_raise) {
+ print("Raising signal %i.\n", raise_signal);
+ raise(raise_signal);
+ }
+
+ if (do_Com_Error)
+ Com_Error(123, "Some error string");
+
+ print("Reached the end of main().\n");
+ graceful_exit();
+}
diff --git a/rconsole/rconsole b/rconsole/rconsole
new file mode 100755
index 0000000..835952a
--- /dev/null
+++ b/rconsole/rconsole
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+if [ "$#" -lt 1 ]; then
+ echo "usage: $0 [instance]"
+ exit 1
+fi
+
+export RCONSOLE_INSTANCE="$1"
+export RCONSOLE_SCREEN="rconsole"
+export RCONSOLE_PIPE="/home/zittrig/tremulous/server/@$RCONSOLE_INSTANCE/home/slacker/pipe"
+
+if [ ! -p "$RCONSOLE_PIPE" ]; then
+ printf "\"%s\" is not a pipe\n" "$RCONSOLE_PIPE"
+ exit 1
+fi
+
+SESSION="$RCONSOLE_SCREEN"
+
+screen -S $SESSION -X quit
+
+screen -S $SESSION -a -O -d -m -T xterm -s rconsole-output || exit
+screen -S $SESSION -X screen rconsole-input || exit
+
+(
+ while ! screen -list | grep "$SESSION.*Attached" > /dev/null; do
+ sleep 0.01s
+ done
+
+ screen -S $SESSION -X select 0
+ screen -S $SESSION -X title "Server logs"
+ screen -S $SESSION -X split
+ screen -S $SESSION -X focus
+ screen -S $SESSION -X select 1
+ screen -S $SESSION -X title "Rcon"
+ screen -S $SESSION -X resize 10%
+) &
+
+screen -d -r $SESSION
+clear
+echo "[rconsole has finished]"
diff --git a/rconsole/rconsole-input b/rconsole/rconsole-input
new file mode 100755
index 0000000..3fe076e
--- /dev/null
+++ b/rconsole/rconsole-input
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+trap 'screen -S "$RCONSOLE_SCREEN" -X quit; exit' SIGINT
+
+PROMPT="(rcon@$RCONSOLE_INSTANCE) "
+
+while read -e -p "$PROMPT" line; do
+ printf "$line\n" > "$RCONSOLE_PIPE"
+done
diff --git a/rconsole/rconsole-output b/rconsole/rconsole-output
new file mode 100755
index 0000000..a6eba89
--- /dev/null
+++ b/rconsole/rconsole-output
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+trap 'screen -S "$RCONSOLE_SCREEN" -X quit; exit' SIGINT
+
+IDENTIFIER="tremded@$RCONSOLE_INSTANCE"
+
+journalctl --user -f -o cat SYSLOG_IDENTIFIER="$IDENTIFIER" \
+|| (
+ echo "journalctl didn't work"
+ read
+ exit
+)
diff --git a/run-instance.sh b/run-instance.sh
new file mode 100755
index 0000000..cdb8342
--- /dev/null
+++ b/run-instance.sh
@@ -0,0 +1,111 @@
+#!/bin/bash
+
+#################
+# Configuration #
+#################
+
+DEBUG=1
+
+# The root directory.
+PREFIX="/home/tremded"
+
+# fs_basepath, fs_homepath and fs_game.
+COMMON="$PREFIX/common"
+HOME_PREFIX="$PREFIX/home@"
+FS_GAME="slacker"
+
+# Config directory: as seen by tremded's vfs and the absolute path, respectively.
+CONFIG_LOCAL="config"
+CONFIG_GLOBAL="$PREFIX/$CONFIG_LOCAL"
+
+# Executables.
+GDB_WRAPPER="$PREFIX/bin/gdb-wrapper2.sh"
+TREMDED_PREFIX="$PREFIX/bin/tremded@"
+
+####################
+# Helper functions #
+####################
+
+SELF="$0"
+
+function error {
+ tput setaf 9
+ tput bold
+ printf "%s: ERROR: " "$0"
+ printf "$@"
+ tput sgr0
+ exit 1
+}
+
+function debug {
+ if [ "$DEBUG" -ne 1 ]; then
+ return
+ fi
+
+ tput setaf 12
+ printf "[DEBUG] "
+ printf "$@"
+ tput sgr0
+}
+
+function check_file {
+ [ -f "$1" ] || error "%s '%s' is not a file\n" "$2" "$1"
+}
+
+function check_dir {
+ [ -d "$1" ] || error "%s '%s' is not a directory\n" "$2" "$1"
+}
+
+function ensure_link {
+ if [ -L "$2" ]; then
+ debug "ensure_link: '%s' already exists\n" "$2"
+ if [ `readlink "$2"` == "$1" ]; then
+ return
+ fi
+
+ debug "ensure_link: ...and points at '%s' instead of '%s'\n" `readlink "$2"` "$1"
+ unlink "$2" || error "couldn't unlink '%s'\n" "$2"
+ fi
+
+ mkdir -p `dirname "$2"` || error
+ ln -s "$1" "$2" || error "linking failed\n"
+ debug "ensure_link: linked '%s' -> '%s'\n" "$2" "$1"
+}
+
+###################
+# The entry point #
+###################
+
+# Check the argument.
+[ -z "$1" ] && error "instance name is missing\nusage: %s [INSTANCE]\n" "$SELF"
+
+# Figure out all the instance-specific paths.
+I="$1"
+TREMDED="$TREMDED_PREFIX$I"
+HOME="$HOME_PREFIX$I"
+CONFIG_LOCAL_COMMON="$CONFIG_LOCAL/common.cfg"
+CONFIG_LOCAL_INST="$CONFIG_LOCAL/$I.cfg"
+CONFIG_GLOBAL_COMMON="$CONFIG_GLOBAL/common.cfg"
+CONFIG_GLOBAL_INST="$CONFIG_GLOBAL/$I.cfg"
+
+# Do some integrity tests and fix missing links.
+check_file "$TREMDED" "the tremded"
+check_dir "$HOME" "the home directory"
+check_file "$CONFIG_GLOBAL_COMMON" "the common config"
+check_file "$CONFIG_GLOBAL_INST" "the instance-specific config"
+ensure_link "${CONFIG_GLOBAL}" "$HOME/$FS_GAME/$CONFIG_LOCAL"
+
+# cd to HOME (that's where cores will appear) and start the server.
+cd "$HOME" || error "cannot chdir to %s\n" "$HOME"
+#"$GDB_WRAPPER" "$TREMDED" \
+"$TREMDED" \
+ +exec "$CONFIG_LOCAL_COMMON" \
+ +set fs_basepath "$COMMON" \
+ +set fs_homepath "$HOME" \
+ +set fs_game "$FS_GAME" \
+ +set com_pipefile "pipe" \
+ +set dedicated 2 \
+ +exec "$CONFIG_LOCAL_INST" \
+ +map "atcs" \
+ +nocurses \
+ "$@" || error "couldn't start the server\n"