diff options
author | Paweł Redman <pawel.redman@gmail.com> | 2017-12-26 19:36:21 +0000 |
---|---|---|
committer | Paweł Redman <pawel.redman@gmail.com> | 2017-12-26 19:40:50 +0000 |
commit | 4fbda5a9af95345504636d178e26b248791a2262 (patch) | |
tree | aa20acbf33686b6843273b175e487ff397abee12 | |
parent | 26d2028b4773aed05707faa7f525075b07da899c (diff) |
Initial commit.
No history was lost as the scripts had no history in the first place.
-rw-r--r-- | gdb-wrapper2/Makefile | 10 | ||||
-rwxr-xr-x | gdb-wrapper2/gdb-wrapper2.sh | 92 | ||||
-rwxr-xr-x | gdb-wrapper2/test.sh | 32 | ||||
-rw-r--r-- | gdb-wrapper2/test_program.c | 154 | ||||
-rwxr-xr-x | rconsole/rconsole | 40 | ||||
-rwxr-xr-x | rconsole/rconsole-input | 9 | ||||
-rwxr-xr-x | rconsole/rconsole-output | 12 | ||||
-rwxr-xr-x | run-instance.sh | 111 |
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" |