├── .gitignore ├── LICENSE ├── README.md ├── caddy ├── caddy_centos ├── caddy_debian └── caddy_install.sh ├── clients ├── status-client.py └── status-psutil.py ├── server ├── Makefile ├── config.json ├── include │ ├── argparse.h │ ├── detect.h │ ├── json.h │ └── system.h ├── obj │ └── .gitignore └── src │ ├── argparse.c │ ├── json.c │ ├── main.cpp │ ├── main.h │ ├── netban.cpp │ ├── netban.h │ ├── network.cpp │ ├── network.h │ ├── network_client.cpp │ ├── server.cpp │ ├── server.h │ └── system.c ├── service ├── server_status_client_centos ├── server_status_client_debian ├── server_status_server_centos ├── server_status_server_debian ├── status-client.service └── status-server.service └── status.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | *.iml 4 | out 5 | gen 6 | 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright 2019 CokeMine 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ServerStatus-Hotaru 2 | 云探针、多服务器探针、云监控、多服务器云监控 3 | 4 | 基于 ServerStatus-Toyo 最新版本稍作修改。 5 | 6 | ## 特性 7 | 8 | 服务端客户端脚本支持系统:Centos 7、Debian 8、Ubuntu 15.10 及以上、ArchLinux 9 | 10 | Python 客户端:支持 Python 版本:Python 2.7+ 11 | 12 | Go 客户端:如果您的客户端环境无法使用 Python, 可以使用 Go 编写的客户端 13 | 14 | 开源地址:https://github.com/cokemine/ServerStatus-goclient 15 | 16 | 流量计算:客户端可以选择使用 vnStat 按月计算流量,会自动编译安装最新版本vnStat(ArchLinux 会从软件源安装最新版本)。如不使用 vnStat ,则默认计算流量方式为重启后流量清零。请注意 ServerStatus 不会把协议为 GPLv2 的 vnStat 作为必须的依赖。 17 | 18 | 前端基于 Vue 3.0 和 SemanticUI 制作,如需修改前端建议自行修改打包。 19 | 20 | 前端所使用一些静态资源见前端仓库下的声明。 21 | 22 | 前端开源地址:https://github.com/cokemine/hotaru_theme 23 | 24 | ## 其他说明 25 | 26 | ServerStatus-Hotaru 将会停留在轻量级的 ServerStatus,不会再添加新的功能 27 | 28 | 如果你有以下需求: 29 | 30 | 1、服务端更低的 IO 占用 31 | 32 | 2、Websocket 支持 33 | 34 | 3、Docker 支持 35 | 36 | 4、更方便服务器的顺序调整 37 | 38 | 5、客户端掉线 Telegram Bot 通知 39 | 40 | 6、使用 Web 管理、添加、修改客户端信息 41 | 42 | 7、等等 43 | 44 | 欢迎使用 NodeStatus: https://github.com/cokemine/nodestatus 45 | 46 | 本项目仍会继续维护 47 | 48 | ## 安装方法 49 | 50 | 服务端: 51 | 52 | ```bash 53 | wget https://raw.githubusercontent.com/cokemine/ServerStatus-Hotaru/master/status.sh 54 | # wget https://cokemine.coding.net/p/hotarunet/d/ServerStatus-Hotaru/git/raw/master/status.sh 若服务器位于中国大陆建议选择 Coding.net 仓库 55 | bash status.sh s 56 | ``` 57 | 58 | 客户端: 59 | 60 | ``` 61 | bash status.sh c 62 | ``` 63 | 64 | ## 手动安装服务端 65 | 66 | ```bash 67 | mkdir -p /usr/local/ServerStatus/server 68 | apt install wget unzip curl vim build-essential 69 | cd /tmp 70 | wget https://github.com/cokemine/ServerStatus-Hotaru/archive/master.zip 71 | unzip master.zip 72 | cd ./ServerStatus-Hotaru-master/server 73 | make #编译生成二进制文件 74 | chmod +x sergate 75 | mv sergate /usr/local/ServerStatus/server 76 | vim /usr/local/ServerStatus/server/config.json #修改配置文件 77 | #下载前端 78 | cd /tmp && wget https://github.com/cokemine/hotaru_theme/releases/latest/download/hotaru-theme.zip 79 | unzip hotaru-theme.zip 80 | mv ./hotaru-theme /usr/local/ServerStatus/web #此为站点根目录,请自行设置 81 | nohup ./sergate --config=config.json --web-dir=/usr/local/ServerStatus/web --port=35601 > /tmp/serverstatus_server.log 2>&1 & #默认端口35601 82 | ``` 83 | 84 | ## 手动安装客户端 85 | 86 | 使用 Psutil 版客户端即可使 ServerStatus 客户端在 Windows 等其他平台运行 87 | 88 | ```powershell 89 | curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py # 若未安装pip 90 | python get-pip.py 91 | python pip install psutil 92 | # 修改 status-psutil.py 93 | python status-psutil.py 94 | ``` 95 | 96 | Linux 版客户端支持绝大部分 Linux 发行版系统,一般不需要使用 psutil 版客户端。 97 | 98 | ```bash 99 | apt install python3 python3-pip wget 100 | pip3 install psutil 101 | wget https://raw.githubusercontent.com/cokemine/ServerStatus-Hotaru/master/clients/status-psutil.py 102 | vim status-psutil.py #修改客户端配置文件 103 | python3 status-psutil.py 104 | # https://raw.githubusercontent.com/cokemine/ServerStatus-Hotaru/master/clients/status-client.py 默认版本无需 psutil 依赖 105 | ``` 106 | 107 | ## 更新前端 108 | 109 | 默认服务端更新不会更新前端。因为更新前端会导致自己自定义的前端消失。 110 | 111 | ```bash 112 | rm -rf /usr/local/ServerStatus/web/* 113 | wget https://github.com/cokemine/hotaru_theme/releases/latest/download/hotaru-theme.zip 114 | unzip hotaru-theme.zip 115 | mv ./hotaru-theme/* /usr/local/ServerStatus/web/ 116 | service status-server restart 117 | # systemctl restart status-server 118 | ``` 119 | 120 | ## 关于前端旗帜图标 121 | 122 | 目前通过脚本使用旗帜图标仅支援当前国家/地区在 ISO 3166-1 标准里,否则可能会出现无法添加的情况,如欧盟 `EU`,但是前端是具备该旗帜的。你可能需要手动加入。方法是修改`/usr/local/ServerStatus/server/config.json`,将你想修改的服务器的`region`改成你需要的。 123 | 124 | 同时,前端还具备以下特殊旗帜,可供选择使用,启用也是需要上述修改。 125 | 126 | Transgender flag: `trans` 127 | 128 | Rainbow flag: `rainbow` 129 | 130 | Pirate flag: `pirate` 131 | 132 | ## Toyo版本修改方法 133 | 134 | 如果你使用 Toyo 版本或其他版本的 ServerStatus,请备份你的config文件并重新编译安装本版本服务端 135 | 136 | 配置文件: /usr/local/ServerStatus/server/config.json 备份并自行添加`region` 137 | 138 | ```json 139 | { 140 | "username": "Name", 141 | "password": "Password", 142 | "name": "Your Servername", 143 | "type": "KVM", 144 | "host": "None", 145 | "location": "洛杉矶", 146 | "disabled": false, 147 | "region": "US" 148 | }, 149 | ``` 150 | 151 | 替换配置文件,重启 ServerStatus 152 | 153 | ## 效果演示 154 | 155 |  156 | 157 | ## 相关开源项目 : 158 | * ServerStatus-Toyo:https://github.com/ToyoDAdoubiBackup/ServerStatus-Toyo MIT License 159 | * ServerStatus:https://github.com/BotoX/ServerStatus WTFPL License 160 | * mojeda's ServerStatus: https://github.com/mojeda/ServerStatus WTFPL License -> GNU GPLv3 License (ServerStatus is a full rewrite of mojeda's ServerStatus script and not affected by GPL) 161 | * BlueVM's project: http://www.lowendtalk.com/discussion/comment/169690#Comment_169690 WTFPL License 162 | 163 | ## 感谢 164 | 165 | * i18n-iso-countries: https://github.com/michaelwittig/node-i18n-iso-countries MIT License (To convert country name in Chinese to iso 3166-1 and check if the code is valid) 166 | * jq: https://github.com/stedolan/jq CC BY 3.0 License 167 | * caddy: https://github.com/caddyserver/caddy Apache-2.0 License 168 | * twemoji: https://github.com/twitter/twemoji CC-BY 4.0 License (The flag icons are designed by Twitter) 169 | 170 | -------------------------------------------------------------------------------- /caddy/caddy_centos: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # chkconfig: 2345 90 10 3 | # description: The HTTP/2 web server with automatic HTTPS. 4 | 5 | ### BEGIN INIT INFO 6 | # Provides: Caddy 7 | # Required-Start: $network $syslog 8 | # Required-Stop: $network 9 | # Default-Start: 2 3 4 5 10 | # Default-Stop: 0 1 6 11 | # Short-Description: The HTTP/2 web server with automatic HTTPS 12 | # Description: Start or stop the Caddy server 13 | ### END INIT INFO 14 | 15 | NAME="Caddy" 16 | NAME_BIN="caddy" 17 | BIN="/usr/local/caddy/caddy" 18 | if [ -f "/usr/local/caddy/Caddyfile" ]; then 19 | CONF="/usr/local/caddy/Caddyfile" 20 | elif [ -f "/etc/caddy/Caddyfile" ]; then 21 | CONF="/etc/caddy/Caddyfile" 22 | fi 23 | Info_font_prefix="\033[32m" && Error_font_prefix="\033[31m" && Font_suffix="\033[0m" 24 | RETVAL=0 25 | 26 | check_running() { 27 | PID=$(ps -ef | grep "${NAME_BIN}" | grep -v "grep" | grep -v "init.d" | grep -v "service" | awk '{print $2}') 28 | if [[ -n ${PID} ]]; then 29 | return 0 30 | else 31 | return 1 32 | fi 33 | } 34 | do_start() { 35 | if check_running; then 36 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME (PID ${PID}) 正在运行..." && exit 0 37 | else 38 | ulimit -n 51200 39 | nohup "$BIN" run --config="$CONF" >>/tmp/caddy.log 2>&1 & 40 | sleep 2s 41 | if check_running; then 42 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 启动成功 !" 43 | else 44 | echo -e "${Error_font_prefix}[错误]${Font_suffix} $NAME 启动失败 !" 45 | fi 46 | fi 47 | } 48 | do_stop() { 49 | if check_running; then 50 | kill -9 "${PID}" 51 | RETVAL=$? 52 | if [[ $RETVAL -eq 0 ]]; then 53 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 停止成功 !" 54 | else 55 | echo -e "${Error_font_prefix}[错误]${Font_suffix} $NAME 停止失败 !" 56 | fi 57 | else 58 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 未运行" 59 | RETVAL=1 60 | fi 61 | } 62 | do_status() { 63 | if check_running; then 64 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME (PID ${PID}) 正在运行..." 65 | else 66 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 未运行 !" 67 | RETVAL=1 68 | fi 69 | } 70 | do_restart() { 71 | do_stop 72 | do_start 73 | } 74 | case "$1" in 75 | start | stop | restart | status) 76 | do_"$1" 77 | ;; 78 | *) 79 | echo -e "使用方法: $0 { start | stop | restart | status }" 80 | RETVAL=1 81 | ;; 82 | esac 83 | exit $RETVAL 84 | -------------------------------------------------------------------------------- /caddy/caddy_debian: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### BEGIN INIT INFO 4 | # Provides: Caddy 5 | # Required-Start: $network $local_fs $remote_fs 6 | # Required-Stop: $network $local_fs $remote_fs 7 | # Default-Start: 2 3 4 5 8 | # Default-Stop: 0 1 6 9 | # Short-Description: The HTTP/2 web server with automatic HTTPS 10 | # Description: Start or stop the Caddy server 11 | ### END INIT INFO 12 | 13 | NAME="Caddy" 14 | NAME_BIN="caddy" 15 | BIN="/usr/local/caddy/caddy" 16 | if [ -f "/usr/local/caddy/Caddyfile" ]; then 17 | CONF="/usr/local/caddy/Caddyfile" 18 | elif [ -f "/etc/caddy/Caddyfile" ]; then 19 | CONF="/etc/caddy/Caddyfile" 20 | fi 21 | Info_font_prefix="\033[32m" && Error_font_prefix="\033[31m" && Font_suffix="\033[0m" 22 | RETVAL=0 23 | 24 | check_running() { 25 | PID=$(ps -ef | grep "${NAME_BIN}" | grep -v "grep" | grep -v "init.d" | grep -v "service" | awk '{print $2}') 26 | if [[ -n ${PID} ]]; then 27 | return 0 28 | else 29 | return 1 30 | fi 31 | } 32 | do_start() { 33 | if check_running; then 34 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME (PID ${PID}) 正在运行..." && exit 0 35 | else 36 | ulimit -n 51200 37 | nohup "$BIN" run --config="$CONF" >>/tmp/caddy.log 2>&1 & 38 | sleep 2s 39 | if check_running; then 40 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 启动成功 !" 41 | else 42 | echo -e "${Error_font_prefix}[错误]${Font_suffix} $NAME 启动失败 !" 43 | fi 44 | fi 45 | } 46 | do_stop() { 47 | if check_running; then 48 | kill -9 "${PID}" 49 | RETVAL=$? 50 | if [[ $RETVAL -eq 0 ]]; then 51 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 停止成功 !" 52 | else 53 | echo -e "${Error_font_prefix}[错误]${Font_suffix}$NAME 停止失败 !" 54 | fi 55 | else 56 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 未运行 !" 57 | RETVAL=1 58 | fi 59 | } 60 | do_status() { 61 | if check_running; then 62 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME (PID ${PID}) 正在运行..." 63 | else 64 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 未运行 !" 65 | RETVAL=1 66 | fi 67 | } 68 | do_restart() { 69 | do_stop 70 | do_start 71 | } 72 | case "$1" in 73 | start | stop | restart | status) 74 | do_"$1" 75 | ;; 76 | *) 77 | echo "使用方法: $0 { start | stop | restart | status }" 78 | RETVAL=1 79 | ;; 80 | esac 81 | exit $RETVAL 82 | -------------------------------------------------------------------------------- /caddy/caddy_install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin 3 | export PATH 4 | #================================================= 5 | # System Required: CentOS/Debian/Ubuntu 6 | # Description: Caddy Install 7 | # Version: 1.0.8 8 | # Author: Toyo 9 | # Blog: https://doub.io/shell-jc1/ 10 | #================================================= 11 | file="/usr/local/caddy/" 12 | caddy_file="/usr/local/caddy/caddy" 13 | caddy_conf_file="/usr/local/caddy/Caddyfile" 14 | Info_font_prefix="\033[32m" && Error_font_prefix="\033[31m" && Font_suffix="\033[0m" && Red_font_prefix="\033[31m" && Font_color_suffix="\033[0m" && Green_background_prefix="\033[42;37m" 15 | Error="${Red_font_prefix}[错误]${Font_color_suffix}" 16 | check_root() { 17 | [[ $EUID != 0 ]] && echo -e "${Error} 当前非ROOT账号(或没有ROOT权限),无法继续操作,请更换ROOT账号或使用 ${Green_background_prefix}sudo su${Font_color_suffix} 命令获取临时ROOT权限(执行后可能会提示输入当前账号的密码)。" && exit 1 18 | } 19 | check_sys() { 20 | if [[ -f /etc/redhat-release ]]; then 21 | release="centos" 22 | elif grep -q -E -i "debian" /etc/issue; then 23 | release="debian" 24 | elif grep -q -E -i "ubuntu" /etc/issue; then 25 | release="ubuntu" 26 | elif grep -q -E -i "centos|red hat|redhat" /etc/issue; then 27 | release="centos" 28 | elif grep -q -E -i "debian" /proc/version; then 29 | release="debian" 30 | elif grep -q -E -i "ubuntu" /proc/version; then 31 | release="ubuntu" 32 | elif grep -q -E -i "centos|red hat|redhat" /proc/version; then 33 | release="centos" 34 | fi 35 | bit=$(uname -m) 36 | } 37 | check_installed_status() { 38 | [[ ! -e ${caddy_file} ]] && echo -e "${Error_font_prefix}[错误]${Font_suffix} Caddy 没有安装,请检查 !" && exit 1 39 | } 40 | Download_caddy() { 41 | [[ ! -e ${file} ]] && mkdir "${file}" 42 | cd "${file}" || exit 43 | PID=$(ps -ef | grep "caddy" | grep -v "grep" | grep -v "init.d" | grep -v "service" | grep -v "caddy_install" | awk '{print $2}') 44 | [[ -n ${PID} ]] && kill -9 "${PID}" 45 | [[ -e "caddy*" ]] && rm -rf "caddy*" 46 | if [[ ${bit} == "x86_64" ]]; then 47 | wget --no-check-certificate -O "caddy" "https://github.com/CokeMine/Caddy_Linux/releases/latest/download/caddy_v2_linux_amd64" 48 | elif [[ ${bit} == "i386" || ${bit} == "i686" ]]; then 49 | wget --no-check-certificate -O "caddy" "https://github.com/CokeMine/Caddy_Linux/releases/latest/download/caddy_v2_linux_386" 50 | elif [[ ${bit} == "armv7l" ]]; then 51 | wget --no-check-certificate -O "caddy" "https://github.com/CokeMine/Caddy_Linux/releases/latest/download/caddy_v2_linux_armv7" 52 | elif [[ ${bit} == "arm64" || ${bit} == "aarch64" ]]; then 53 | wget --no-check-certificate -O "caddy" "https://github.com/CokeMine/Caddy_Linux/releases/latest/download/caddy_v2_linux_arm64" 54 | else 55 | echo -e "${Error_font_prefix}[错误]${Font_suffix} 不支持 [${bit}] ! 请向本站反馈[]中的名称,我会看看是否可以添加支持。" && exit 1 56 | fi 57 | [[ ! -e "caddy" ]] && echo -e "${Error_font_prefix}[错误]${Font_suffix} Caddy 下载失败 !" && exit 1 58 | chmod +x caddy 59 | } 60 | Service_caddy() { 61 | if [[ ${release} == "centos" ]]; then 62 | if ! wget --no-check-certificate https://raw.githubusercontent.com/CokeMine/ServerStatus-Hotaru/master/caddy/caddy_centos -O /etc/init.d/caddy; then 63 | echo -e "${Error_font_prefix}[错误]${Font_suffix} Caddy服务 管理脚本下载失败 !" && exit 1 64 | fi 65 | chmod +x /etc/init.d/caddy 66 | chkconfig --add caddy 67 | chkconfig caddy on 68 | else 69 | if ! wget --no-check-certificate https://raw.githubusercontent.com/CokeMine/ServerStatus-Hotaru/master/caddy/caddy_debian -O /etc/init.d/caddy; then 70 | echo -e "${Error_font_prefix}[错误]${Font_suffix} Caddy服务 管理脚本下载失败 !" && exit 1 71 | fi 72 | chmod +x /etc/init.d/caddy 73 | update-rc.d -f caddy defaults 74 | fi 75 | } 76 | install_caddy() { 77 | check_root 78 | if [[ -e ${caddy_file} ]]; then 79 | echo && echo -e "${Error_font_prefix}[信息]${Font_suffix} 检测到 Caddy 已安装,是否继续安装(覆盖更新)?[y/N]" 80 | read -rep "(默认: n):" yn 81 | [[ -z ${yn} ]] && yn="n" 82 | if [[ ${yn} == [Nn] ]]; then 83 | echo && echo "已取消..." && exit 1 84 | fi 85 | fi 86 | Download_caddy 87 | Service_caddy 88 | echo && echo -e " Caddy 使用命令:${caddy_conf_file} 89 | 日志文件:cat /tmp/caddy.log 90 | 使用说明:service caddy start | stop | restart | status 91 | 或者使用:/etc/init.d/caddy start | stop | restart | status 92 | ${Info_font_prefix}[信息]${Font_suffix} Caddy 安装完成!" && echo 93 | } 94 | uninstall_caddy() { 95 | check_installed_status 96 | echo && echo "确定要卸载 Caddy ? [y/N]" 97 | read -rep "(默认: n):" unyn 98 | [[ -z ${unyn} ]] && unyn="n" 99 | if [[ ${unyn} == [Yy] ]]; then 100 | PID=$(ps -ef | grep "caddy" | grep -v "grep" | grep -v "init.d" | grep -v "service" | grep -v "caddy_install" | awk '{print $2}') 101 | [[ -n ${PID} ]] && kill -9 "${PID}" 102 | if [[ ${release} == "centos" ]]; then 103 | chkconfig --del caddy 104 | else 105 | update-rc.d -f caddy remove 106 | fi 107 | [[ -s /tmp/caddy.log ]] && rm -rf /tmp/caddy.log 108 | rm -rf ${caddy_file} 109 | rm -rf ${caddy_conf_file} 110 | rm -rf /etc/init.d/caddy 111 | [[ ! -e ${caddy_file} ]] && echo && echo -e "${Info_font_prefix}[信息]${Font_suffix} Caddy 卸载完成 !" && echo && exit 1 112 | echo && echo -e "${Error_font_prefix}[错误]${Font_suffix} Caddy 卸载失败 !" && echo 113 | else 114 | echo && echo "卸载已取消..." && echo 115 | fi 116 | } 117 | check_sys 118 | action=$1 119 | #extension=$2 120 | [[ -z $1 ]] && action=install 121 | case "$action" in 122 | install | uninstall) 123 | ${action}_caddy 124 | ;; 125 | *) 126 | echo "输入错误 !" 127 | echo "用法: {install | uninstall}" 128 | ;; 129 | esac 130 | -------------------------------------------------------------------------------- /clients/status-client.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Support Python Version 2.7 to 3.7 3 | # Update by: https://github.com/CokeMine/ServerStatus-Hotaru 4 | 5 | import socket 6 | import time 7 | import re 8 | import os 9 | import json 10 | import subprocess 11 | from collections import deque 12 | 13 | SERVER = "127.0.0.1" 14 | PORT = 35601 15 | USER = "USER" 16 | PASSWORD = "USER_PASSWORD" 17 | INTERVAL = 1 # 更新间隔,单位:秒 18 | 19 | 20 | def check_interface(net_name): 21 | net_name = net_name.strip() 22 | invalid_name = ['lo', 'tun', 'kube', 'docker', 'vmbr', 'br-', 'vnet', 'veth'] 23 | return not any(name in net_name for name in invalid_name) 24 | 25 | 26 | def get_uptime(): 27 | with open('/proc/uptime', 'r') as f: 28 | uptime = f.readline().split('.') 29 | return int(uptime[0]) 30 | 31 | 32 | def get_memory(): 33 | re_parser = re.compile(r'(\S*):\s*(\d*)\s*kB') 34 | result = dict() 35 | for line in open('/proc/meminfo'): 36 | match = re_parser.match(line) 37 | if match: 38 | result[match.group(1)] = int(match.group(2)) 39 | 40 | mem_total = float(result['MemTotal']) 41 | mem_free = float(result['MemFree']) 42 | buffers = float(result['Buffers']) 43 | cached = float(result['Cached']) 44 | mem_used = mem_total - (mem_free + buffers + cached) 45 | swap_total = float(result['SwapTotal']) 46 | swap_free = float(result['SwapFree']) 47 | return int(mem_total), int(mem_used), int(swap_total), int(swap_free) 48 | 49 | 50 | def get_hdd(): 51 | p = subprocess.check_output( 52 | ['df', '-Tlm', '--total', '-t', 'ext4', '-t', 'ext3', '-t', 'ext2', '-t', 'reiserfs', '-t', 'jfs', '-t', 'ntfs', 53 | '-t', 'fat32', '-t', 'btrfs', '-t', 'fuseblk', '-t', 'zfs', '-t', 'simfs', '-t', 'xfs']).decode('utf-8') 54 | total = p.splitlines()[-1] 55 | used = total.split()[3] 56 | size = total.split()[2] 57 | return int(size), int(used) 58 | 59 | 60 | def get_load(): 61 | return round(os.getloadavg()[0], 1) 62 | 63 | 64 | def get_cpu_time(): 65 | with open('/proc/stat', 'r') as stat_file: 66 | time_list = stat_file.readline().split()[1:] 67 | time_list = list(map(int, time_list)) 68 | return sum(time_list), time_list[3] 69 | 70 | 71 | def get_cpu(): 72 | old_total, old_idle = get_cpu_time() 73 | time.sleep(INTERVAL) 74 | total, idle = get_cpu_time() 75 | return round(100 - float(idle - old_idle) / (total - old_total) * 100.00, 1) 76 | 77 | 78 | def get_traffic_vnstat(): 79 | vnstat = os.popen('vnstat --oneline b').readline() 80 | if "Not enough data available yet" in vnstat: 81 | return 0, 0 82 | v_data = vnstat.split(';') 83 | net_in = int(v_data[8]) 84 | net_out = int(v_data[9]) 85 | return net_in, net_out 86 | 87 | 88 | class Network: 89 | def __init__(self): 90 | self.rx = deque(maxlen=10) 91 | self.tx = deque(maxlen=10) 92 | self._get_traffic() 93 | 94 | def _get_traffic(self): 95 | net_in = 0 96 | net_out = 0 97 | re_parser = re.compile(r'([^\s]+):[\s]*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(' 98 | r'\d+)\s+(\d+)\s+(\d+)') 99 | with open('/proc/net/dev') as f: 100 | for line in f.readlines(): 101 | net_info = re_parser.findall(line) 102 | if net_info: 103 | if check_interface(net_info[0][0]): 104 | net_in += int(net_info[0][1]) 105 | net_out += int(net_info[0][9]) 106 | self.rx.append(net_in) 107 | self.tx.append(net_out) 108 | 109 | def get_speed(self): 110 | self._get_traffic() 111 | avg_rx = 0 112 | avg_tx = 0 113 | queue_len = len(self.rx) 114 | for x in range(queue_len - 1): 115 | avg_rx += self.rx[x + 1] - self.rx[x] 116 | avg_tx += self.tx[x + 1] - self.tx[x] 117 | avg_rx = int(avg_rx / queue_len / INTERVAL) 118 | avg_tx = int(avg_tx / queue_len / INTERVAL) 119 | return avg_rx, avg_tx 120 | 121 | def get_traffic(self): 122 | queue_len = len(self.rx) 123 | return self.rx[queue_len - 1], self.tx[queue_len - 1] 124 | 125 | 126 | def get_network(ip_version): 127 | if ip_version == 4: 128 | host = 'ipv4.google.com' 129 | elif ip_version == 6: 130 | host = 'ipv6.google.com' 131 | else: 132 | return False 133 | try: 134 | socket.create_connection((host, 80), 2).close() 135 | return True 136 | except Exception: 137 | return False 138 | 139 | 140 | if __name__ == '__main__': 141 | socket.setdefaulttimeout(30) 142 | while True: 143 | try: 144 | print('Connecting...') 145 | s = socket.create_connection((SERVER, PORT)) 146 | data = s.recv(1024).decode() 147 | if data.find('Authentication required') > -1: 148 | s.send((USER + ':' + PASSWORD + '\n').encode('utf-8')) 149 | data = s.recv(1024).decode() 150 | if data.find('Authentication successful') < 0: 151 | print(data) 152 | raise socket.error 153 | else: 154 | print(data) 155 | raise socket.error 156 | 157 | print(data) 158 | if data.find('You are connecting via') < 0: 159 | data = s.recv(1024).decode() 160 | print(data) 161 | 162 | timer = 0 163 | check_ip = 0 164 | if data.find('IPv4') > -1: 165 | check_ip = 6 166 | elif data.find('IPv6') > -1: 167 | check_ip = 4 168 | else: 169 | print(data) 170 | raise socket.error 171 | 172 | traffic = Network() 173 | while True: 174 | CPU = get_cpu() 175 | NetRx, NetTx = traffic.get_speed() 176 | NET_IN, NET_OUT = traffic.get_traffic() 177 | Uptime = get_uptime() 178 | Load = get_load() 179 | MemoryTotal, MemoryUsed, SwapTotal, SwapFree = get_memory() 180 | HDDTotal, HDDUsed = get_hdd() 181 | 182 | array = {} 183 | if not timer: 184 | array['online' + str(check_ip)] = get_network(check_ip) 185 | timer = 150 186 | else: 187 | timer -= 1 * INTERVAL 188 | 189 | array['uptime'] = Uptime 190 | array['load'] = Load 191 | array['memory_total'] = MemoryTotal 192 | array['memory_used'] = MemoryUsed 193 | array['swap_total'] = SwapTotal 194 | array['swap_used'] = SwapTotal - SwapFree 195 | array['hdd_total'] = HDDTotal 196 | array['hdd_used'] = HDDUsed 197 | array['cpu'] = CPU 198 | array['network_rx'] = NetRx 199 | array['network_tx'] = NetTx 200 | array['network_in'] = NET_IN 201 | array['network_out'] = NET_OUT 202 | s.send(("update " + json.dumps(array) + '\n').encode('utf-8')) 203 | except KeyboardInterrupt: 204 | raise 205 | except socket.error: 206 | print('Disconnected...') 207 | # keep on trying after a disconnect 208 | if 's' in locals().keys(): 209 | del s 210 | time.sleep(3) 211 | except Exception as e: 212 | print('Caught Exception:', e) 213 | if 's' in locals().keys(): 214 | del s 215 | time.sleep(3) 216 | -------------------------------------------------------------------------------- /clients/status-psutil.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Update by: https://github.com/CokeMine/ServerStatus-Hotaru 3 | # 依赖于psutil跨平台库: 4 | # 支持Python版本:2.6 to 3.7 5 | # 支持操作系统: Linux, Windows, OSX, Sun Solaris, FreeBSD, OpenBSD and NetBSD, both 32-bit and 64-bit architectures 6 | 7 | import socket 8 | import time 9 | import json 10 | import psutil 11 | from collections import deque 12 | 13 | SERVER = "127.0.0.1" 14 | PORT = 35601 15 | USER = "USER" 16 | PASSWORD = "USER_PASSWORD" 17 | INTERVAL = 1 # 更新间隔,单位:秒 18 | 19 | 20 | def check_interface(net_name): 21 | net_name = net_name.strip() 22 | invalid_name = ['lo', 'tun', 'kube', 'docker', 'vmbr', 'br-', 'vnet', 'veth'] 23 | return not any(name in net_name for name in invalid_name) 24 | 25 | 26 | def get_uptime(): 27 | return int(time.time() - psutil.boot_time()) 28 | 29 | 30 | def get_memory(): 31 | mem = psutil.virtual_memory() 32 | swap = psutil.swap_memory() 33 | return int(mem.total / 1024.0), int(mem.used / 1024.0), int(swap.total / 1024.0), int(swap.used / 1024.0) 34 | 35 | 36 | def get_hdd(): 37 | valid_fs = ['ext4', 'ext3', 'ext2', 'reiserfs', 'jfs', 'btrfs', 'fuseblk', 'zfs', 'simfs', 'ntfs', 'fat32', 'exfat', 38 | 'xfs'] 39 | disks = dict() 40 | size = 0 41 | used = 0 42 | for disk in psutil.disk_partitions(): 43 | if disk.device not in disks and disk.fstype.lower() in valid_fs: 44 | disks[disk.device] = disk.mountpoint 45 | for disk in disks.values(): 46 | usage = psutil.disk_usage(disk) 47 | size += usage.total 48 | used += usage.used 49 | return int(size / 1024.0 / 1024.0), int(used / 1024.0 / 1024.0) 50 | 51 | 52 | def get_load(): 53 | try: 54 | return round(psutil.getloadavg()[0], 1) 55 | except Exception: 56 | return -1.0 57 | 58 | 59 | def get_cpu(): 60 | return psutil.cpu_percent(interval=INTERVAL) 61 | 62 | 63 | class Network: 64 | def __init__(self): 65 | self.rx = deque(maxlen=10) 66 | self.tx = deque(maxlen=10) 67 | self._get_traffic() 68 | 69 | def _get_traffic(self): 70 | net_in = 0 71 | net_out = 0 72 | net = psutil.net_io_counters(pernic=True) 73 | for k, v in net.items(): 74 | if check_interface(k): 75 | net_in += v[1] 76 | net_out += v[0] 77 | self.rx.append(net_in) 78 | self.tx.append(net_out) 79 | 80 | def get_speed(self): 81 | self._get_traffic() 82 | avg_rx = 0 83 | avg_tx = 0 84 | queue_len = len(self.rx) 85 | for x in range(queue_len - 1): 86 | avg_rx += self.rx[x + 1] - self.rx[x] 87 | avg_tx += self.tx[x + 1] - self.tx[x] 88 | avg_rx = int(avg_rx / queue_len / INTERVAL) 89 | avg_tx = int(avg_tx / queue_len / INTERVAL) 90 | return avg_rx, avg_tx 91 | 92 | def get_traffic(self): 93 | queue_len = len(self.rx) 94 | return self.rx[queue_len - 1], self.tx[queue_len - 1] 95 | 96 | 97 | def get_network(ip_version): 98 | if ip_version == 4: 99 | host = 'ipv4.google.com' 100 | elif ip_version == 6: 101 | host = 'ipv6.google.com' 102 | else: 103 | return False 104 | try: 105 | socket.create_connection((host, 80), 2).close() 106 | return True 107 | except Exception: 108 | return False 109 | 110 | 111 | if __name__ == '__main__': 112 | socket.setdefaulttimeout(30) 113 | while True: 114 | try: 115 | print("Connecting...") 116 | s = socket.create_connection((SERVER, PORT)) 117 | data = s.recv(1024).decode() 118 | if data.find("Authentication required") > -1: 119 | s.send((USER + ':' + PASSWORD + '\n').encode("utf-8")) 120 | data = s.recv(1024).decode() 121 | if data.find("Authentication successful") < 0: 122 | print(data) 123 | raise socket.error 124 | else: 125 | print(data) 126 | raise socket.error 127 | 128 | print(data) 129 | if data.find('You are connecting via') < 0: 130 | data = s.recv(1024).decode() 131 | print(data) 132 | 133 | timer = 0 134 | check_ip = 0 135 | if data.find("IPv4") > -1: 136 | check_ip = 6 137 | elif data.find("IPv6") > -1: 138 | check_ip = 4 139 | else: 140 | print(data) 141 | raise socket.error 142 | 143 | traffic = Network() 144 | while True: 145 | CPU = get_cpu() 146 | NetRx, NetTx = traffic.get_speed() 147 | NET_IN, NET_OUT = traffic.get_traffic() 148 | Uptime = get_uptime() 149 | Load = get_load() 150 | MemoryTotal, MemoryUsed, SwapTotal, SwapUsed = get_memory() 151 | HDDTotal, HDDUsed = get_hdd() 152 | 153 | array = {} 154 | if not timer: 155 | array['online' + str(check_ip)] = get_network(check_ip) 156 | timer = 150 157 | else: 158 | timer -= 1 * INTERVAL 159 | 160 | array['uptime'] = Uptime 161 | array['load'] = Load 162 | array['memory_total'] = MemoryTotal 163 | array['memory_used'] = MemoryUsed 164 | array['swap_total'] = SwapTotal 165 | array['swap_used'] = SwapUsed 166 | array['hdd_total'] = HDDTotal 167 | array['hdd_used'] = HDDUsed 168 | array['cpu'] = CPU 169 | array['network_rx'] = NetRx 170 | array['network_tx'] = NetTx 171 | array['network_in'] = NET_IN 172 | array['network_out'] = NET_OUT 173 | 174 | s.send(("update " + json.dumps(array) + "\n").encode("utf-8")) 175 | except KeyboardInterrupt: 176 | raise 177 | except socket.error: 178 | print("Disconnected...") 179 | # keep on trying after a disconnect 180 | if 's' in locals().keys(): 181 | del s 182 | time.sleep(3) 183 | except Exception as e: 184 | print("Caught Exception:", e) 185 | if 's' in locals().keys(): 186 | del s 187 | time.sleep(3) 188 | -------------------------------------------------------------------------------- /server/Makefile: -------------------------------------------------------------------------------- 1 | OUT = sergate 2 | 3 | #CC = clang 4 | CC = gcc 5 | CFLAGS = -Wall -O2 6 | 7 | #CXX = clang++ 8 | CXX = g++ 9 | CXXFLAGS = -Wall -O2 10 | 11 | ODIR = obj 12 | SDIR = src 13 | LIBS = -pthread -lm 14 | INC = -Iinclude 15 | 16 | C_SRCS := $(wildcard $(SDIR)/*.c) 17 | CXX_SRCS := $(wildcard $(SDIR)/*.cpp) 18 | C_OBJS := $(patsubst $(SDIR)/%.c,$(ODIR)/%.o,$(C_SRCS)) 19 | CXX_OBJS := $(patsubst $(SDIR)/%.cpp,$(ODIR)/%.o,$(CXX_SRCS)) 20 | OBJS := $(C_OBJS) $(CXX_OBJS) 21 | 22 | $(ODIR)/%.o: $(SDIR)/%.c 23 | $(CC) -c $(INC) $(CFLAGS) lt; -o $@ 24 | 25 | $(ODIR)/%.o: $(SDIR)/%.cpp 26 | $(CXX) -c $(INC) $(CXXFLAGS) lt; -o $@ 27 | 28 | $(OUT): $(OBJS) 29 | $(CXX) $(LIBS) $^ -o $(OUT) 30 | 31 | .PHONY: clean 32 | 33 | clean: 34 | rm -f $(ODIR)/*.o $(OUT) 35 | -------------------------------------------------------------------------------- /server/config.json: -------------------------------------------------------------------------------- 1 | {"servers": 2 | [ 3 | { 4 | "username": "s01", 5 | "password": "password", 6 | "name": "Mainserver 1", 7 | "type": "Dedicated Server", 8 | "host": "No", 9 | "location": "America", 10 | "disabled": false, 11 | "region":"US" 12 | }, 13 | { 14 | "username": "bs01", 15 | "password": "password", 16 | "name": "Backupserver 1", 17 | "type": "Virtual Server", 18 | "host": "No", 19 | "location": "Japan", 20 | "disabled": false, 21 | "region":"JP" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /server/include/argparse.h: -------------------------------------------------------------------------------- 1 | #ifndef ARGPARSE_H 2 | #define ARGPARSE_H 3 | 4 | /** 5 | * Command-line arguments parsing library. 6 | * 7 | * This module is inspired by parse-options.c (git) and python's argparse 8 | * module. 9 | * 10 | * Arguments parsing is common task in cli program, but traditional `getopt` 11 | * libraries are not easy to use. This library provides high-level arguments 12 | * parsing solutions. 13 | * 14 | * The program defines what arguments it requires, and `argparse` will figure 15 | * out how to parse those out of `argc` and `argv`, it also automatically 16 | * generates help and usage messages and issues errors when users give the 17 | * program invalid arguments. 18 | * 19 | * Reserved namespaces: 20 | * argparse 21 | * OPT 22 | * Author: Yecheng Fu <cofyc.jackson@gmail.com> 23 | */ 24 | 25 | #include <assert.h> 26 | #include <math.h> 27 | #include <stdint.h> 28 | #include <stdio.h> 29 | #include <stdlib.h> 30 | #include <string.h> 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | struct argparse; 37 | struct argparse_option; 38 | 39 | typedef int argparse_callback(struct argparse *this_, 40 | const struct argparse_option *option); 41 | 42 | enum argparse_flag { 43 | ARGPARSE_STOP_AT_NON_OPTION = 1, 44 | }; 45 | 46 | enum argparse_option_type { 47 | /* special */ 48 | ARGPARSE_OPT_END, 49 | /* options with no arguments */ 50 | ARGPARSE_OPT_BOOLEAN, 51 | ARGPARSE_OPT_BIT, 52 | /* options with arguments (optional or required) */ 53 | ARGPARSE_OPT_INTEGER, 54 | ARGPARSE_OPT_STRING, 55 | }; 56 | 57 | enum argparse_option_flags { 58 | OPT_NONEG = 1, /* Negation disabled. */ 59 | }; 60 | 61 | /* 62 | * Argparse option struct. 63 | * 64 | * `type`: 65 | * holds the type of the option, you must have an ARGPARSE_OPT_END last in your 66 | * array. 67 | * 68 | * `short_name`: 69 | * the character to use as a short option name, '\0' if none. 70 | * 71 | * `long_name`: 72 | * the long option name, without the leading dash, NULL if none. 73 | * 74 | * `value`: 75 | * stores pointer to the value to be filled. 76 | * 77 | * `help`: 78 | * the short help message associated to what the option does. 79 | * Must never be NULL (except for ARGPARSE_OPT_END). 80 | * 81 | * `callback`: 82 | * function is called when corresponding argument is parsed. 83 | * 84 | * `data`: 85 | * associated data. Callbacks can use it like they want. 86 | * 87 | * `flags`: 88 | * option flags. 89 | * 90 | */ 91 | struct argparse_option { 92 | enum argparse_option_type type; 93 | const char short_name; 94 | const char *long_name; 95 | void *value; 96 | const char *help; 97 | argparse_callback *callback; 98 | intptr_t data; 99 | int flags; 100 | }; 101 | 102 | /* 103 | * argpparse 104 | */ 105 | struct argparse { 106 | // user supplied 107 | const struct argparse_option *options; 108 | const char *usage; 109 | int flags; 110 | // internal context 111 | int argc; 112 | const char **argv; 113 | const char **out; 114 | int cpidx; 115 | const char *optvalue; // current option value 116 | }; 117 | 118 | // builtin callbacks 119 | int argparse_help_cb(struct argparse *this_, 120 | const struct argparse_option *option); 121 | 122 | // builtin option macros 123 | #define OPT_END() { ARGPARSE_OPT_END, 0 } 124 | #define OPT_BOOLEAN(...) { ARGPARSE_OPT_BOOLEAN, __VA_ARGS__ } 125 | #define OPT_BIT(...) { ARGPARSE_OPT_BIT, __VA_ARGS__ } 126 | #define OPT_INTEGER(...) { ARGPARSE_OPT_INTEGER, __VA_ARGS__ } 127 | #define OPT_STRING(...) { ARGPARSE_OPT_STRING, __VA_ARGS__ } 128 | #define OPT_HELP() OPT_BOOLEAN('h', "help", 0, "Show this help message and exit", argparse_help_cb) 129 | 130 | int argparse_init(struct argparse *this_, struct argparse_option *options, 131 | const char *usage, int flags); 132 | int argparse_parse(struct argparse *this_, int argc, const char **argv); 133 | void argparse_usage(struct argparse *this_); 134 | 135 | #ifdef __cplusplus 136 | } 137 | #endif 138 | 139 | #endif 140 | -------------------------------------------------------------------------------- /server/include/detect.h: -------------------------------------------------------------------------------- 1 | /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ 2 | /* If you are missing that file, acquire a complete release at teeworlds.com. */ 3 | #ifndef BASE_DETECT_H 4 | #define BASE_DETECT_H 5 | 6 | /* 7 | this file detected the family, platform and architecture 8 | to compile for. 9 | */ 10 | 11 | /* platforms */ 12 | 13 | /* windows Family */ 14 | #if defined(WIN64) || defined(_WIN64) 15 | /* Hmm, is this IA64 or x86-64? */ 16 | #define CONF_FAMILY_WINDOWS 1 17 | #define CONF_FAMILY_STRING "windows" 18 | #define CONF_PLATFORM_WIN64 1 19 | #define CONF_PLATFORM_STRING "win64" 20 | #elif defined(WIN32) || defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__) 21 | #define CONF_FAMILY_WINDOWS 1 22 | #define CONF_FAMILY_STRING "windows" 23 | #define CONF_PLATFORM_WIN32 1 24 | #define CONF_PLATFORM_STRING "win32" 25 | #endif 26 | 27 | /* unix family */ 28 | #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) 29 | #define CONF_FAMILY_UNIX 1 30 | #define CONF_FAMILY_STRING "unix" 31 | #define CONF_PLATFORM_FREEBSD 1 32 | #define CONF_PLATFORM_STRING "freebsd" 33 | #endif 34 | 35 | #if defined(__OpenBSD__) 36 | #define CONF_FAMILY_UNIX 1 37 | #define CONF_FAMILY_STRING "unix" 38 | #define CONF_PLATFORM_OPENBSD 1 39 | #define CONF_PLATFORM_STRING "openbsd" 40 | #endif 41 | 42 | #if defined(__LINUX__) || defined(__linux__) 43 | #define CONF_FAMILY_UNIX 1 44 | #define CONF_FAMILY_STRING "unix" 45 | #define CONF_PLATFORM_LINUX 1 46 | #define CONF_PLATFORM_STRING "linux" 47 | #endif 48 | 49 | #if defined(__GNU__) || defined(__gnu__) 50 | #define CONF_FAMILY_UNIX 1 51 | #define CONF_FAMILY_STRING "unix" 52 | #define CONF_PLATFORM_HURD 1 53 | #define CONF_PLATFORM_STRING "gnu" 54 | #endif 55 | 56 | #if defined(MACOSX) || defined(__APPLE__) || defined(__DARWIN__) 57 | #define CONF_FAMILY_UNIX 1 58 | #define CONF_FAMILY_STRING "unix" 59 | #define CONF_PLATFORM_MACOSX 1 60 | #define CONF_PLATFORM_STRING "macosx" 61 | #endif 62 | 63 | #if defined(__sun) 64 | #define CONF_FAMILY_UNIX 1 65 | #define CONF_FAMILY_STRING "unix" 66 | #define CONF_PLATFORM_SOLARIS 1 67 | #define CONF_PLATFORM_STRING "solaris" 68 | #endif 69 | 70 | /* beos family */ 71 | #if defined(__BeOS) || defined(__BEOS__) 72 | #define CONF_FAMILY_BEOS 1 73 | #define CONF_FAMILY_STRING "beos" 74 | #define CONF_PLATFORM_BEOS 1 75 | #define CONF_PLATFORM_STRING "beos" 76 | #endif 77 | 78 | 79 | /* use gcc endianness definitions when available */ 80 | #if defined(__GNUC__) && !defined(__APPLE__) && !defined(__MINGW32__) && !defined(__sun) 81 | #if defined(__FreeBSD__) || defined(__OpenBSD__) 82 | #include <sys/endian.h> 83 | #else 84 | #include <endian.h> 85 | #endif 86 | 87 | #if __BYTE_ORDER == __LITTLE_ENDIAN 88 | #define CONF_ARCH_ENDIAN_LITTLE 1 89 | #elif __BYTE_ORDER == __BIG_ENDIAN 90 | #define CONF_ARCH_ENDIAN_BIG 1 91 | #endif 92 | #endif 93 | 94 | 95 | /* architectures */ 96 | #if defined(i386) || defined(__i386__) || defined(__x86__) || defined(CONF_PLATFORM_WIN32) 97 | #define CONF_ARCH_IA32 1 98 | #define CONF_ARCH_STRING "ia32" 99 | #if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG) 100 | #define CONF_ARCH_ENDIAN_LITTLE 1 101 | #endif 102 | #endif 103 | 104 | #if defined(__ia64__) || defined(_M_IA64) 105 | #define CONF_ARCH_IA64 1 106 | #define CONF_ARCH_STRING "ia64" 107 | #if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG) 108 | #define CONF_ARCH_ENDIAN_LITTLE 1 109 | #endif 110 | #endif 111 | 112 | #if defined(__amd64__) || defined(__x86_64__) || defined(_M_X64) 113 | #define CONF_ARCH_AMD64 1 114 | #define CONF_ARCH_STRING "amd64" 115 | #if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG) 116 | #define CONF_ARCH_ENDIAN_LITTLE 1 117 | #endif 118 | #endif 119 | 120 | #if defined(__powerpc__) || defined(__ppc__) 121 | #define CONF_ARCH_PPC 1 122 | #define CONF_ARCH_STRING "ppc" 123 | #if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG) 124 | #define CONF_ARCH_ENDIAN_BIG 1 125 | #endif 126 | #endif 127 | 128 | #if defined(__sparc__) 129 | #define CONF_ARCH_SPARC 1 130 | #define CONF_ARCH_STRING "sparc" 131 | #if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG) 132 | #define CONF_ARCH_ENDIAN_BIG 1 133 | #endif 134 | #endif 135 | 136 | 137 | #ifndef CONF_FAMILY_STRING 138 | #define CONF_FAMILY_STRING "unknown" 139 | #endif 140 | 141 | #ifndef CONF_PLATFORM_STRING 142 | #define CONF_PLATFORM_STRING "unknown" 143 | #endif 144 | 145 | #ifndef CONF_ARCH_STRING 146 | #define CONF_ARCH_STRING "unknown" 147 | #endif 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /server/include/json.h: -------------------------------------------------------------------------------- 1 | 2 | /* vim: set et ts=3 sw=3 sts=3 ft=c: 3 | * 4 | * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved. 5 | * https://github.com/udp/json-parser 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef _JSON_H 32 | #define _JSON_H 33 | 34 | #ifndef json_char 35 | #define json_char char 36 | #endif 37 | 38 | #ifndef json_int_t 39 | #ifndef _MSC_VER 40 | #include <inttypes.h> 41 | #define json_int_t int64_t 42 | #else 43 | #define json_int_t __int64 44 | #endif 45 | #endif 46 | 47 | #include <stdlib.h> 48 | 49 | #ifdef __cplusplus 50 | 51 | #include <string.h> 52 | 53 | extern "C" 54 | { 55 | 56 | #endif 57 | 58 | typedef struct 59 | { 60 | unsigned long max_memory; 61 | int settings; 62 | 63 | /* Custom allocator support (leave null to use malloc/free) 64 | */ 65 | 66 | void * (* mem_alloc) (size_t, int zero, void * user_data); 67 | void (* mem_free) (void *, void * user_data); 68 | 69 | void * user_data; /* will be passed to mem_alloc and mem_free */ 70 | 71 | } json_settings; 72 | 73 | #define json_enable_comments 0x01 74 | 75 | typedef enum 76 | { 77 | json_none, 78 | json_object, 79 | json_array, 80 | json_integer, 81 | json_double, 82 | json_string, 83 | json_boolean, 84 | json_null 85 | 86 | } json_type; 87 | 88 | extern const struct _json_value json_value_none; 89 | 90 | typedef struct _json_value 91 | { 92 | struct _json_value * parent; 93 | 94 | json_type type; 95 | 96 | union 97 | { 98 | int boolean; 99 | json_int_t integer; 100 | double dbl; 101 | 102 | struct 103 | { 104 | unsigned int length; 105 | json_char * ptr; /* null terminated */ 106 | 107 | } string; 108 | 109 | struct 110 | { 111 | unsigned int length; 112 | 113 | struct 114 | { 115 | json_char * name; 116 | unsigned int name_length; 117 | 118 | struct _json_value * value; 119 | 120 | } * values; 121 | 122 | #if defined(__cplusplus) && __cplusplus >= 201103L 123 | decltype(values) begin () const 124 | { return values; 125 | } 126 | decltype(values) end () const 127 | { return values + length; 128 | } 129 | #endif 130 | 131 | } object; 132 | 133 | struct 134 | { 135 | unsigned int length; 136 | struct _json_value ** values; 137 | 138 | #if defined(__cplusplus) && __cplusplus >= 201103L 139 | decltype(values) begin () const 140 | { return values; 141 | } 142 | decltype(values) end () const 143 | { return values + length; 144 | } 145 | #endif 146 | 147 | } array; 148 | 149 | } u; 150 | 151 | union 152 | { 153 | struct _json_value * next_alloc; 154 | void * object_mem; 155 | 156 | } _reserved; 157 | 158 | 159 | /* Some C++ operator sugar */ 160 | 161 | #ifdef __cplusplus 162 | 163 | public: 164 | 165 | inline _json_value () 166 | { memset (this, 0, sizeof (_json_value)); 167 | } 168 | 169 | inline const struct _json_value &operator [] (int index) const 170 | { 171 | if (type != json_array || index < 0 172 | || ((unsigned int) index) >= u.array.length) 173 | { 174 | return json_value_none; 175 | } 176 | 177 | return *u.array.values [index]; 178 | } 179 | 180 | inline const struct _json_value &operator [] (const char * index) const 181 | { 182 | if (type != json_object) 183 | return json_value_none; 184 | 185 | for (unsigned int i = 0; i < u.object.length; ++ i) 186 | if (!strcmp (u.object.values [i].name, index)) 187 | return *u.object.values [i].value; 188 | 189 | return json_value_none; 190 | } 191 | 192 | inline operator const char * () const 193 | { 194 | switch (type) 195 | { 196 | case json_string: 197 | return u.string.ptr; 198 | 199 | default: 200 | return ""; 201 | }; 202 | } 203 | 204 | inline operator json_int_t () const 205 | { 206 | switch (type) 207 | { 208 | case json_integer: 209 | return u.integer; 210 | 211 | case json_double: 212 | return (json_int_t) u.dbl; 213 | 214 | default: 215 | return 0; 216 | }; 217 | } 218 | 219 | inline operator bool () const 220 | { 221 | if (type != json_boolean) 222 | return false; 223 | 224 | return u.boolean != 0; 225 | } 226 | 227 | inline operator double () const 228 | { 229 | switch (type) 230 | { 231 | case json_integer: 232 | return (double) u.integer; 233 | 234 | case json_double: 235 | return u.dbl; 236 | 237 | default: 238 | return 0; 239 | }; 240 | } 241 | 242 | #endif 243 | 244 | } json_value; 245 | 246 | json_value * json_parse (const json_char * json, 247 | size_t length); 248 | 249 | #define json_error_max 128 250 | json_value * json_parse_ex (json_settings * settings, 251 | const json_char * json, 252 | size_t length, 253 | char * error); 254 | 255 | void json_value_free (json_value *); 256 | 257 | 258 | /* Not usually necessary, unless you used a custom mem_alloc and now want to 259 | * use a custom mem_free. 260 | */ 261 | void json_value_free_ex (json_settings * settings, 262 | json_value *); 263 | 264 | 265 | #ifdef __cplusplus 266 | } /* extern "C" */ 267 | #endif 268 | 269 | #endif 270 | -------------------------------------------------------------------------------- /server/include/system.h: -------------------------------------------------------------------------------- 1 | /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ 2 | /* If you are missing that file, acquire a complete release at teeworlds.com. */ 3 | 4 | /* 5 | Title: OS Abstraction 6 | */ 7 | 8 | #ifndef BASE_SYSTEM_H 9 | #define BASE_SYSTEM_H 10 | 11 | #include "detect.h" 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | /* Group: Debug */ 18 | /* 19 | Function: dbg_assert 20 | Breaks into the debugger based on a test. 21 | 22 | Parameters: 23 | test - Result of the test. 24 | msg - Message that should be printed if the test fails. 25 | 26 | Remarks: 27 | Does nothing in release version of the library. 28 | 29 | See Also: 30 | <dbg_break> 31 | */ 32 | void dbg_assert(int test, const char *msg); 33 | #define dbg_assert(test,msg) dbg_assert_imp(__FILE__, __LINE__, test, msg) 34 | void dbg_assert_imp(const char *filename, int line, int test, const char *msg); 35 | 36 | 37 | #ifdef __clang_analyzer__ 38 | #include <assert.h> 39 | #undef dbg_assert 40 | #define dbg_assert(test,msg) assert(test) 41 | #endif 42 | 43 | /* 44 | Function: dbg_break 45 | Breaks into the debugger. 46 | 47 | Remarks: 48 | Does nothing in release version of the library. 49 | 50 | See Also: 51 | <dbg_assert> 52 | */ 53 | void dbg_break(); 54 | 55 | /* 56 | Function: dbg_msg 57 | 58 | Prints a debug message. 59 | 60 | Parameters: 61 | sys - A string that describes what system the message belongs to 62 | fmt - A printf styled format string. 63 | 64 | Remarks: 65 | Does nothing in release version of the library. 66 | 67 | See Also: 68 | <dbg_assert> 69 | */ 70 | void dbg_msg(const char *sys, const char *fmt, ...); 71 | 72 | /* Group: Memory */ 73 | 74 | /* 75 | Function: mem_alloc 76 | Allocates memory. 77 | 78 | Parameters: 79 | size - Size of the needed block. 80 | alignment - Alignment for the block. 81 | 82 | Returns: 83 | Returns a pointer to the newly allocated block. Returns a 84 | null pointer if the memory couldn't be allocated. 85 | 86 | Remarks: 87 | - Passing 0 to size will allocated the smallest amount possible 88 | and return a unique pointer. 89 | 90 | See Also: 91 | <mem_free> 92 | */ 93 | void *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned alignment); 94 | #define mem_alloc(s,a) mem_alloc_debug(__FILE__, __LINE__, (s), (a)) 95 | 96 | /* 97 | Function: mem_free 98 | Frees a block allocated through <mem_alloc>. 99 | 100 | Remarks: 101 | - In the debug version of the library the function will assert if 102 | a non-valid block is passed, like a null pointer or a block that 103 | isn't allocated. 104 | 105 | See Also: 106 | <mem_alloc> 107 | */ 108 | void mem_free(void *block); 109 | 110 | /* 111 | Function: mem_copy 112 | Copies a a memory block. 113 | 114 | Parameters: 115 | dest - Destination. 116 | source - Source to copy. 117 | size - Size of the block to copy. 118 | 119 | Remarks: 120 | - This functions DOES NOT handles cases where source and 121 | destination is overlapping. 122 | 123 | See Also: 124 | <mem_move> 125 | */ 126 | void mem_copy(void *dest, const void *source, unsigned size); 127 | 128 | /* 129 | Function: mem_move 130 | Copies a a memory block 131 | 132 | Parameters: 133 | dest - Destination 134 | source - Source to copy 135 | size - Size of the block to copy 136 | 137 | Remarks: 138 | - This functions handles cases where source and destination 139 | is overlapping 140 | 141 | See Also: 142 | <mem_copy> 143 | */ 144 | void mem_move(void *dest, const void *source, unsigned size); 145 | 146 | /* 147 | Function: mem_zero 148 | Sets a complete memory block to 0 149 | 150 | Parameters: 151 | block - Pointer to the block to zero out 152 | size - Size of the block 153 | */ 154 | void mem_zero(void *block, unsigned size); 155 | 156 | /* 157 | Function: mem_comp 158 | Compares two blocks of memory 159 | 160 | Parameters: 161 | a - First block of data 162 | b - Second block of data 163 | size - Size of the data to compare 164 | 165 | Returns: 166 | <0 - Block a is lesser then block b 167 | 0 - Block a is equal to block b 168 | >0 - Block a is greater then block b 169 | */ 170 | int mem_comp(const void *a, const void *b, int size); 171 | 172 | /* 173 | Function: mem_check 174 | Validates the heap 175 | Will trigger a assert if memory has failed. 176 | */ 177 | int mem_check_imp(); 178 | #define mem_check() dbg_assert_imp(__FILE__, __LINE__, mem_check_imp(), "Memory check failed") 179 | 180 | /* Group: File IO */ 181 | enum { 182 | IOFLAG_READ = 1, 183 | IOFLAG_WRITE = 2, 184 | IOFLAG_RANDOM = 4, 185 | 186 | IOSEEK_START = 0, 187 | IOSEEK_CUR = 1, 188 | IOSEEK_END = 2 189 | }; 190 | 191 | typedef struct IOINTERNAL *IOHANDLE; 192 | 193 | /* 194 | Function: io_open 195 | Opens a file. 196 | 197 | Parameters: 198 | filename - File to open. 199 | flags - A set of flags. IOFLAG_READ, IOFLAG_WRITE, IOFLAG_RANDOM. 200 | 201 | Returns: 202 | Returns a handle to the file on success and 0 on failure. 203 | 204 | */ 205 | IOHANDLE io_open(const char *filename, int flags); 206 | 207 | /* 208 | Function: io_read 209 | Reads data into a buffer from a file. 210 | 211 | Parameters: 212 | io - Handle to the file to read data from. 213 | buffer - Pointer to the buffer that will recive the data. 214 | size - Number of bytes to read from the file. 215 | 216 | Returns: 217 | Number of bytes read. 218 | 219 | */ 220 | unsigned io_read(IOHANDLE io, void *buffer, unsigned size); 221 | 222 | /* 223 | Function: io_skip 224 | Skips data in a file. 225 | 226 | Parameters: 227 | io - Handle to the file. 228 | size - Number of bytes to skip. 229 | 230 | Returns: 231 | Number of bytes skipped. 232 | */ 233 | unsigned io_skip(IOHANDLE io, int size); 234 | 235 | /* 236 | Function: io_write 237 | Writes data from a buffer to file. 238 | 239 | Parameters: 240 | io - Handle to the file. 241 | buffer - Pointer to the data that should be written. 242 | size - Number of bytes to write. 243 | 244 | Returns: 245 | Number of bytes written. 246 | */ 247 | unsigned io_write(IOHANDLE io, const void *buffer, unsigned size); 248 | 249 | /* 250 | Function: io_write_newline 251 | Writes newline to file. 252 | 253 | Parameters: 254 | io - Handle to the file. 255 | 256 | Returns: 257 | Number of bytes written. 258 | */ 259 | unsigned io_write_newline(IOHANDLE io); 260 | 261 | /* 262 | Function: io_seek 263 | Seeks to a specified offset in the file. 264 | 265 | Parameters: 266 | io - Handle to the file. 267 | offset - Offset from pos to stop. 268 | origin - Position to start searching from. 269 | 270 | Returns: 271 | Returns 0 on success. 272 | */ 273 | int io_seek(IOHANDLE io, int offset, int origin); 274 | 275 | /* 276 | Function: io_tell 277 | Gets the current position in the file. 278 | 279 | Parameters: 280 | io - Handle to the file. 281 | 282 | Returns: 283 | Returns the current position. -1L if an error occured. 284 | */ 285 | long int io_tell(IOHANDLE io); 286 | 287 | /* 288 | Function: io_length 289 | Gets the total length of the file. Resetting cursor to the beginning 290 | 291 | Parameters: 292 | io - Handle to the file. 293 | 294 | Returns: 295 | Returns the total size. -1L if an error occured. 296 | */ 297 | long int io_length(IOHANDLE io); 298 | 299 | /* 300 | Function: io_close 301 | Closes a file. 302 | 303 | Parameters: 304 | io - Handle to the file. 305 | 306 | Returns: 307 | Returns 0 on success. 308 | */ 309 | int io_close(IOHANDLE io); 310 | 311 | /* 312 | Function: io_flush 313 | Empties all buffers and writes all pending data. 314 | 315 | Parameters: 316 | io - Handle to the file. 317 | 318 | Returns: 319 | Returns 0 on success. 320 | */ 321 | int io_flush(IOHANDLE io); 322 | 323 | 324 | /* 325 | Function: io_stdin 326 | Returns an <IOHANDLE> to the standard input. 327 | */ 328 | IOHANDLE io_stdin(); 329 | 330 | /* 331 | Function: io_stdout 332 | Returns an <IOHANDLE> to the standard output. 333 | */ 334 | IOHANDLE io_stdout(); 335 | 336 | /* 337 | Function: io_stderr 338 | Returns an <IOHANDLE> to the standard error. 339 | */ 340 | IOHANDLE io_stderr(); 341 | 342 | 343 | /* Group: Threads */ 344 | 345 | /* 346 | Function: thread_sleep 347 | Suspends the current thread for a given period. 348 | 349 | Parameters: 350 | milliseconds - Number of milliseconds to sleep. 351 | */ 352 | void thread_sleep(int milliseconds); 353 | 354 | /* 355 | Function: thread_create 356 | Creates a new thread. 357 | 358 | Parameters: 359 | threadfunc - Entry point for the new thread. 360 | user - Pointer to pass to the thread. 361 | 362 | */ 363 | void *thread_create(void (*threadfunc)(void *), void *user); 364 | 365 | /* 366 | Function: thread_wait 367 | Waits for a thread to be done or destroyed. 368 | 369 | Parameters: 370 | thread - Thread to wait for. 371 | */ 372 | void thread_wait(void *thread); 373 | 374 | /* 375 | Function: thread_destroy 376 | Destroys a thread. 377 | 378 | Parameters: 379 | thread - Thread to destroy. 380 | */ 381 | void thread_destroy(void *thread); 382 | 383 | /* 384 | Function: thread_yeild 385 | Yeild the current threads execution slice. 386 | */ 387 | void thread_yield(); 388 | 389 | /* 390 | Function: thread_detach 391 | Puts the thread in the detached thread, guaranteeing that 392 | resources of the thread will be freed immediately when the 393 | thread terminates. 394 | 395 | Parameters: 396 | thread - Thread to detach 397 | */ 398 | void thread_detach(void *thread); 399 | 400 | /* Group: Locks */ 401 | typedef void* LOCK; 402 | 403 | LOCK lock_create(); 404 | void lock_destroy(LOCK lock); 405 | 406 | int lock_try(LOCK lock); 407 | void lock_wait(LOCK lock); 408 | void lock_release(LOCK lock); 409 | 410 | 411 | /* Group: Semaphores */ 412 | 413 | #if !defined(CONF_PLATFORM_MACOSX) 414 | #if defined(CONF_FAMILY_UNIX) 415 | #include <semaphore.h> 416 | typedef sem_t SEMAPHORE; 417 | #elif defined(CONF_FAMILY_WINDOWS) 418 | typedef void* SEMAPHORE; 419 | #else 420 | #error missing sempahore implementation 421 | #endif 422 | 423 | void semaphore_init(SEMAPHORE *sem); 424 | void semaphore_wait(SEMAPHORE *sem); 425 | void semaphore_signal(SEMAPHORE *sem); 426 | void semaphore_destroy(SEMAPHORE *sem); 427 | #endif 428 | 429 | /* Group: Timer */ 430 | #ifdef __GNUC__ 431 | /* if compiled with -pedantic-errors it will complain about long 432 | not being a C90 thing. 433 | */ 434 | __extension__ typedef long long int64; 435 | #else 436 | typedef long long int64; 437 | #endif 438 | /* 439 | Function: time_get 440 | Fetches a sample from a high resolution timer. 441 | 442 | Returns: 443 | Current value of the timer. 444 | 445 | Remarks: 446 | To know how fast the timer is ticking, see <time_freq>. 447 | */ 448 | int64 time_get(); 449 | 450 | /* 451 | Function: time_freq 452 | Returns the frequency of the high resolution timer. 453 | 454 | Returns: 455 | Returns the frequency of the high resolution timer. 456 | */ 457 | int64 time_freq(); 458 | 459 | /* 460 | Function: time_timestamp 461 | Retrives the current time as a UNIX timestamp 462 | 463 | Returns: 464 | The time as a UNIX timestamp 465 | */ 466 | int time_timestamp(); 467 | 468 | /* Group: Network General */ 469 | typedef struct 470 | { 471 | int type; 472 | int ipv4sock; 473 | int ipv6sock; 474 | } NETSOCKET; 475 | 476 | enum 477 | { 478 | NETADDR_MAXSTRSIZE = 1+(8*4+7)+1+1+5+1, // [XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX]:XXXXX 479 | 480 | NETTYPE_INVALID = 0, 481 | NETTYPE_IPV4 = 1, 482 | NETTYPE_IPV6 = 2, 483 | NETTYPE_LINK_BROADCAST = 4, 484 | NETTYPE_ALL = NETTYPE_IPV4|NETTYPE_IPV6 485 | }; 486 | 487 | typedef struct 488 | { 489 | unsigned int type; 490 | unsigned char ip[16]; 491 | unsigned short port; 492 | } NETADDR; 493 | 494 | /* 495 | Function: net_init 496 | Initiates network functionallity. 497 | 498 | Returns: 499 | Returns 0 on success, 500 | 501 | Remarks: 502 | You must call this function before using any other network 503 | functions. 504 | */ 505 | int net_init(); 506 | 507 | /* 508 | Function: net_host_lookup 509 | Does a hostname lookup by name and fills out the passed 510 | NETADDR struct with the recieved details. 511 | 512 | Returns: 513 | 0 on success. 514 | */ 515 | int net_host_lookup(const char *hostname, NETADDR *addr, int types); 516 | 517 | /* 518 | Function: net_addr_comp 519 | Compares two network addresses. 520 | 521 | Parameters: 522 | a - Address to compare 523 | b - Address to compare to. 524 | 525 | Returns: 526 | <0 - Address a is lesser then address b 527 | 0 - Address a is equal to address b 528 | >0 - Address a is greater then address b 529 | */ 530 | int net_addr_comp(const NETADDR *a, const NETADDR *b); 531 | 532 | /* 533 | Function: net_addr_str 534 | Turns a network address into a representive string. 535 | 536 | Parameters: 537 | addr - Address to turn into a string. 538 | string - Buffer to fill with the string. 539 | max_length - Maximum size of the string. 540 | add_port - add port to string or not 541 | 542 | Remarks: 543 | - The string will always be zero terminated 544 | 545 | */ 546 | void net_addr_str(const NETADDR *addr, char *string, int max_length, int add_port); 547 | 548 | /* 549 | Function: net_addr_from_str 550 | Turns string into a network address. 551 | 552 | Returns: 553 | 0 on success 554 | 555 | Parameters: 556 | addr - Address to fill in. 557 | string - String to parse. 558 | */ 559 | int net_addr_from_str(NETADDR *addr, const char *string); 560 | 561 | /* Group: Network UDP */ 562 | 563 | /* 564 | Function: net_udp_create 565 | Creates a UDP socket and binds it to a port. 566 | 567 | Parameters: 568 | bindaddr - Address to bind the socket to. 569 | 570 | Returns: 571 | On success it returns an handle to the socket. On failure it 572 | returns NETSOCKET_INVALID. 573 | */ 574 | NETSOCKET net_udp_create(NETADDR bindaddr); 575 | 576 | /* 577 | Function: net_udp_send 578 | Sends a packet over an UDP socket. 579 | 580 | Parameters: 581 | sock - Socket to use. 582 | addr - Where to send the packet. 583 | data - Pointer to the packet data to send. 584 | size - Size of the packet. 585 | 586 | Returns: 587 | On success it returns the number of bytes sent. Returns -1 588 | on error. 589 | */ 590 | int net_udp_send(NETSOCKET sock, const NETADDR *addr, const void *data, int size); 591 | 592 | /* 593 | Function: net_udp_recv 594 | Recives a packet over an UDP socket. 595 | 596 | Parameters: 597 | sock - Socket to use. 598 | addr - Pointer to an NETADDR that will recive the address. 599 | data - Pointer to a buffer that will recive the data. 600 | maxsize - Maximum size to recive. 601 | 602 | Returns: 603 | On success it returns the number of bytes recived. Returns -1 604 | on error. 605 | */ 606 | int net_udp_recv(NETSOCKET sock, NETADDR *addr, void *data, int maxsize); 607 | 608 | /* 609 | Function: net_udp_close 610 | Closes an UDP socket. 611 | 612 | Parameters: 613 | sock - Socket to close. 614 | 615 | Returns: 616 | Returns 0 on success. -1 on error. 617 | */ 618 | int net_udp_close(NETSOCKET sock); 619 | 620 | 621 | /* Group: Network TCP */ 622 | 623 | /* 624 | Function: net_tcp_create 625 | Creates a TCP socket. 626 | 627 | Parameters: 628 | bindaddr - Address to bind the socket to. 629 | 630 | Returns: 631 | On success it returns an handle to the socket. On failure it returns NETSOCKET_INVALID. 632 | */ 633 | NETSOCKET net_tcp_create(NETADDR bindaddr); 634 | 635 | /* 636 | Function: net_tcp_listen 637 | Makes the socket start listening for new connections. 638 | 639 | Parameters: 640 | sock - Socket to start listen to. 641 | backlog - Size of the queue of incomming connections to keep. 642 | 643 | Returns: 644 | Returns 0 on success. 645 | */ 646 | int net_tcp_listen(NETSOCKET sock, int backlog); 647 | 648 | /* 649 | Function: net_tcp_accept 650 | Polls a listning socket for a new connection. 651 | 652 | Parameters: 653 | sock - Listning socket to poll. 654 | new_sock - Pointer to a socket to fill in with the new socket. 655 | addr - Pointer to an address that will be filled in the remote address (optional, can be NULL). 656 | 657 | Returns: 658 | Returns a non-negative integer on success. Negative integer on failure. 659 | */ 660 | int net_tcp_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR *addr); 661 | 662 | /* 663 | Function: net_tcp_connect 664 | Connects one socket to another. 665 | 666 | Parameters: 667 | sock - Socket to connect. 668 | addr - Address to connect to. 669 | 670 | Returns: 671 | Returns 0 on success. 672 | 673 | */ 674 | int net_tcp_connect(NETSOCKET sock, const NETADDR *addr); 675 | 676 | /* 677 | Function: net_tcp_send 678 | Sends data to a TCP stream. 679 | 680 | Parameters: 681 | sock - Socket to send data to. 682 | data - Pointer to the data to send. 683 | size - Size of the data to send. 684 | 685 | Returns: 686 | Number of bytes sent. Negative value on failure. 687 | */ 688 | int net_tcp_send(NETSOCKET sock, const void *data, int size); 689 | 690 | /* 691 | Function: net_tcp_recv 692 | Recvives data from a TCP stream. 693 | 694 | Parameters: 695 | sock - Socket to recvive data from. 696 | data - Pointer to a buffer to write the data to 697 | max_size - Maximum of data to write to the buffer. 698 | 699 | Returns: 700 | Number of bytes recvived. Negative value on failure. When in 701 | non-blocking mode, it returns 0 when there is no more data to 702 | be fetched. 703 | */ 704 | int net_tcp_recv(NETSOCKET sock, void *data, int maxsize); 705 | 706 | /* 707 | Function: net_tcp_close 708 | Closes a TCP socket. 709 | 710 | Parameters: 711 | sock - Socket to close. 712 | 713 | Returns: 714 | Returns 0 on success. Negative value on failure. 715 | */ 716 | int net_tcp_close(NETSOCKET sock); 717 | 718 | /* Group: Strings */ 719 | 720 | /* 721 | Function: str_append 722 | Appends a string to another. 723 | 724 | Parameters: 725 | dst - Pointer to a buffer that contains a string. 726 | src - String to append. 727 | dst_size - Size of the buffer of the dst string. 728 | 729 | Remarks: 730 | - The strings are treated as zero-termineted strings. 731 | - Garantees that dst string will contain zero-termination. 732 | */ 733 | void str_append(char *dst, const char *src, int dst_size); 734 | 735 | /* 736 | Function: str_copy 737 | Copies a string to another. 738 | 739 | Parameters: 740 | dst - Pointer to a buffer that shall recive the string. 741 | src - String to be copied. 742 | dst_size - Size of the buffer dst. 743 | 744 | Remarks: 745 | - The strings are treated as zero-termineted strings. 746 | - Garantees that dst string will contain zero-termination. 747 | */ 748 | void str_copy(char *dst, const char *src, int dst_size); 749 | 750 | /* 751 | Function: str_length 752 | Returns the length of a zero terminated string. 753 | 754 | Parameters: 755 | str - Pointer to the string. 756 | 757 | Returns: 758 | Length of string in bytes excluding the zero termination. 759 | */ 760 | int str_length(const char *str); 761 | 762 | /* 763 | Function: str_format 764 | Performs printf formating into a buffer. 765 | 766 | Parameters: 767 | buffer - Pointer to the buffer to recive the formated string. 768 | buffer_size - Size of the buffer. 769 | format - printf formating string. 770 | ... - Parameters for the formating. 771 | 772 | Remarks: 773 | - See the C manual for syntax for the printf formating string. 774 | - The strings are treated as zero-termineted strings. 775 | - Garantees that dst string will contain zero-termination. 776 | */ 777 | void str_format(char *buffer, int buffer_size, const char *format, ...); 778 | 779 | /* 780 | Function: str_sanitize_strong 781 | Replaces all characters below 32 and above 127 with whitespace. 782 | 783 | Parameters: 784 | str - String to sanitize. 785 | 786 | Remarks: 787 | - The strings are treated as zero-termineted strings. 788 | */ 789 | void str_sanitize_strong(char *str); 790 | 791 | /* 792 | Function: str_sanitize_cc 793 | Replaces all characters below 32 with whitespace. 794 | 795 | Parameters: 796 | str - String to sanitize. 797 | 798 | Remarks: 799 | - The strings are treated as zero-termineted strings. 800 | */ 801 | void str_sanitize_cc(char *str); 802 | 803 | /* 804 | Function: str_sanitize 805 | Replaces all characters below 32 with whitespace with 806 | exception to \t, \n and \r. 807 | 808 | Parameters: 809 | str - String to sanitize. 810 | 811 | Remarks: 812 | - The strings are treated as zero-termineted strings. 813 | */ 814 | void str_sanitize(char *str); 815 | 816 | /* 817 | Function: str_skip_to_whitespace 818 | Skips leading non-whitespace characters(all but ' ', '\t', '\n', '\r'). 819 | 820 | Parameters: 821 | str - Pointer to the string. 822 | 823 | Returns: 824 | Pointer to the first whitespace character found 825 | within the string. 826 | 827 | Remarks: 828 | - The strings are treated as zero-termineted strings. 829 | */ 830 | char *str_skip_to_whitespace(char *str); 831 | 832 | /* 833 | Function: str_skip_whitespaces 834 | Skips leading whitespace characters(' ', '\t', '\n', '\r'). 835 | 836 | Parameters: 837 | str - Pointer to the string. 838 | 839 | Returns: 840 | Pointer to the first non-whitespace character found 841 | within the string. 842 | 843 | Remarks: 844 | - The strings are treated as zero-termineted strings. 845 | */ 846 | char *str_skip_whitespaces(char *str); 847 | 848 | /* 849 | Function: str_comp_nocase 850 | Compares to strings case insensitive. 851 | 852 | Parameters: 853 | a - String to compare. 854 | b - String to compare. 855 | 856 | Returns: 857 | <0 - String a is lesser then string b 858 | 0 - String a is equal to string b 859 | >0 - String a is greater then string b 860 | 861 | Remarks: 862 | - Only garanted to work with a-z/A-Z. 863 | - The strings are treated as zero-termineted strings. 864 | */ 865 | int str_comp_nocase(const char *a, const char *b); 866 | 867 | /* 868 | Function: str_comp_nocase_num 869 | Compares up to num characters of two strings case insensitive. 870 | 871 | Parameters: 872 | a - String to compare. 873 | b - String to compare. 874 | num - Maximum characters to compare 875 | 876 | Returns: 877 | <0 - String a is lesser than string b 878 | 0 - String a is equal to string b 879 | >0 - String a is greater than string b 880 | 881 | Remarks: 882 | - Only garanted to work with a-z/A-Z. 883 | - The strings are treated as zero-termineted strings. 884 | */ 885 | int str_comp_nocase_num(const char *a, const char *b, const int num); 886 | 887 | /* 888 | Function: str_comp 889 | Compares to strings case sensitive. 890 | 891 | Parameters: 892 | a - String to compare. 893 | b - String to compare. 894 | 895 | Returns: 896 | <0 - String a is lesser then string b 897 | 0 - String a is equal to string b 898 | >0 - String a is greater then string b 899 | 900 | Remarks: 901 | - The strings are treated as zero-termineted strings. 902 | */ 903 | int str_comp(const char *a, const char *b); 904 | 905 | /* 906 | Function: str_comp_num 907 | Compares up to num characters of two strings case sensitive. 908 | 909 | Parameters: 910 | a - String to compare. 911 | b - String to compare. 912 | num - Maximum characters to compare 913 | 914 | Returns: 915 | <0 - String a is lesser then string b 916 | 0 - String a is equal to string b 917 | >0 - String a is greater then string b 918 | 919 | Remarks: 920 | - The strings are treated as zero-termineted strings. 921 | */ 922 | int str_comp_num(const char *a, const char *b, const int num); 923 | 924 | /* 925 | Function: str_comp_filenames 926 | Compares two strings case sensitive, digit chars will be compared as numbers. 927 | 928 | Parameters: 929 | a - String to compare. 930 | b - String to compare. 931 | 932 | Returns: 933 | <0 - String a is lesser then string b 934 | 0 - String a is equal to string b 935 | >0 - String a is greater then string b 936 | 937 | Remarks: 938 | - The strings are treated as zero-termineted strings. 939 | */ 940 | int str_comp_filenames(const char *a, const char *b); 941 | 942 | /* 943 | Function: str_find_nocase 944 | Finds a string inside another string case insensitive. 945 | 946 | Parameters: 947 | haystack - String to search in 948 | needle - String to search for 949 | 950 | Returns: 951 | A pointer into haystack where the needle was found. 952 | Returns NULL of needle could not be found. 953 | 954 | Remarks: 955 | - Only garanted to work with a-z/A-Z. 956 | - The strings are treated as zero-termineted strings. 957 | */ 958 | const char *str_find_nocase(const char *haystack, const char *needle); 959 | 960 | /* 961 | Function: str_find 962 | Finds a string inside another string case sensitive. 963 | 964 | Parameters: 965 | haystack - String to search in 966 | needle - String to search for 967 | 968 | Returns: 969 | A pointer into haystack where the needle was found. 970 | Returns NULL of needle could not be found. 971 | 972 | Remarks: 973 | - The strings are treated as zero-termineted strings. 974 | */ 975 | const char *str_find(const char *haystack, const char *needle); 976 | 977 | /* 978 | Function: str_hex 979 | Takes a datablock and generates a hexstring of it. 980 | 981 | Parameters: 982 | dst - Buffer to fill with hex data 983 | dst_size - size of the buffer 984 | data - Data to turn into hex 985 | data - Size of the data 986 | 987 | Remarks: 988 | - The desination buffer will be zero-terminated 989 | */ 990 | void str_hex(char *dst, int dst_size, const void *data, int data_size); 991 | 992 | /* 993 | Function: str_timestamp 994 | Copies a time stamp in the format year-month-day_hour-minute-second to the string. 995 | 996 | Parameters: 997 | buffer - Pointer to a buffer that shall receive the time stamp string. 998 | buffer_size - Size of the buffer. 999 | 1000 | Remarks: 1001 | - Guarantees that buffer string will contain zero-termination. 1002 | */ 1003 | void str_timestamp(char *buffer, int buffer_size); 1004 | 1005 | /* Group: Filesystem */ 1006 | 1007 | /* 1008 | Function: fs_listdir 1009 | Lists the files in a directory 1010 | 1011 | Parameters: 1012 | dir - Directory to list 1013 | cb - Callback function to call for each entry 1014 | type - Type of the directory 1015 | user - Pointer to give to the callback 1016 | 1017 | Returns: 1018 | Always returns 0. 1019 | */ 1020 | typedef int (*FS_LISTDIR_CALLBACK)(const char *name, int is_dir, int dir_type, void *user); 1021 | int fs_listdir(const char *dir, FS_LISTDIR_CALLBACK cb, int type, void *user); 1022 | 1023 | /* 1024 | Function: fs_makedir 1025 | Creates a directory 1026 | 1027 | Parameters: 1028 | path - Directory to create 1029 | 1030 | Returns: 1031 | Returns 0 on success. Negative value on failure. 1032 | 1033 | Remarks: 1034 | Does not create several directories if needed. "a/b/c" will result 1035 | in a failure if b or a does not exist. 1036 | */ 1037 | int fs_makedir(const char *path); 1038 | 1039 | /* 1040 | Function: fs_storage_path 1041 | Fetches per user configuration directory. 1042 | 1043 | Returns: 1044 | Returns 0 on success. Negative value on failure. 1045 | 1046 | Remarks: 1047 | - Returns ~/.appname on UNIX based systems 1048 | - Returns ~/Library/Applications Support/appname on Mac OS X 1049 | - Returns %APPDATA%/Appname on Windows based systems 1050 | */ 1051 | int fs_storage_path(const char *appname, char *path, int max); 1052 | 1053 | /* 1054 | Function: fs_is_dir 1055 | Checks if directory exists 1056 | 1057 | Returns: 1058 | Returns 1 on success, 0 on failure. 1059 | */ 1060 | int fs_is_dir(const char *path); 1061 | 1062 | /* 1063 | Function: fs_chdir 1064 | Changes current working directory 1065 | 1066 | Returns: 1067 | Returns 0 on success, 1 on failure. 1068 | */ 1069 | int fs_chdir(const char *path); 1070 | 1071 | /* 1072 | Function: fs_getcwd 1073 | Gets the current working directory. 1074 | 1075 | Returns: 1076 | Returns a pointer to the buffer on success, 0 on failure. 1077 | */ 1078 | char *fs_getcwd(char *buffer, int buffer_size); 1079 | 1080 | /* 1081 | Function: fs_parent_dir 1082 | Get the parent directory of a directory 1083 | 1084 | Parameters: 1085 | path - The directory string 1086 | 1087 | Returns: 1088 | Returns 0 on success, 1 on failure. 1089 | 1090 | Remarks: 1091 | - The string is treated as zero-termineted string. 1092 | */ 1093 | int fs_parent_dir(char *path); 1094 | 1095 | /* 1096 | Function: fs_remove 1097 | Deletes the file with the specified name. 1098 | 1099 | Parameters: 1100 | filename - The file to delete 1101 | 1102 | Returns: 1103 | Returns 0 on success, 1 on failure. 1104 | 1105 | Remarks: 1106 | - The strings are treated as zero-terminated strings. 1107 | */ 1108 | int fs_remove(const char *filename); 1109 | 1110 | /* 1111 | Function: fs_rename 1112 | Renames the file or directory. If the paths differ the file will be moved. 1113 | 1114 | Parameters: 1115 | oldname - The actual name 1116 | newname - The new name 1117 | 1118 | Returns: 1119 | Returns 0 on success, 1 on failure. 1120 | 1121 | Remarks: 1122 | - The strings are treated as zero-terminated strings. 1123 | */ 1124 | int fs_rename(const char *oldname, const char *newname); 1125 | 1126 | /* 1127 | Group: Undocumented 1128 | */ 1129 | 1130 | 1131 | /* 1132 | Function: net_tcp_connect_non_blocking 1133 | 1134 | DOCTODO: serp 1135 | */ 1136 | int net_tcp_connect_non_blocking(NETSOCKET sock, NETADDR bindaddr); 1137 | 1138 | /* 1139 | Function: net_set_non_blocking 1140 | 1141 | DOCTODO: serp 1142 | */ 1143 | int net_set_non_blocking(NETSOCKET sock); 1144 | 1145 | /* 1146 | Function: net_set_non_blocking 1147 | 1148 | DOCTODO: serp 1149 | */ 1150 | int net_set_blocking(NETSOCKET sock); 1151 | 1152 | /* 1153 | Function: net_errno 1154 | 1155 | DOCTODO: serp 1156 | */ 1157 | int net_errno(); 1158 | 1159 | /* 1160 | Function: net_would_block 1161 | 1162 | DOCTODO: serp 1163 | */ 1164 | int net_would_block(); 1165 | 1166 | int net_socket_read_wait(NETSOCKET sock, int time); 1167 | 1168 | void mem_debug_dump(IOHANDLE file); 1169 | 1170 | void swap_endian(void *data, unsigned elem_size, unsigned num); 1171 | 1172 | 1173 | typedef void (*DBG_LOGGER)(const char *line); 1174 | void dbg_logger(DBG_LOGGER logger); 1175 | 1176 | void dbg_logger_stdout(); 1177 | void dbg_logger_debugger(); 1178 | void dbg_logger_file(const char *filename); 1179 | 1180 | typedef struct 1181 | { 1182 | int allocated; 1183 | int active_allocations; 1184 | int total_allocations; 1185 | } MEMSTATS; 1186 | 1187 | const MEMSTATS *mem_stats(); 1188 | 1189 | typedef struct 1190 | { 1191 | int sent_packets; 1192 | int sent_bytes; 1193 | int recv_packets; 1194 | int recv_bytes; 1195 | } NETSTATS; 1196 | 1197 | 1198 | void net_stats(NETSTATS *stats); 1199 | 1200 | int str_toint(const char *str); 1201 | float str_tofloat(const char *str); 1202 | int str_isspace(char c); 1203 | char str_uppercase(char c); 1204 | unsigned str_quickhash(const char *str); 1205 | 1206 | /* 1207 | Function: gui_messagebox 1208 | Display plain OS-dependent message box 1209 | 1210 | Parameters: 1211 | title - title of the message box 1212 | message - text to display 1213 | */ 1214 | void gui_messagebox(const char *title, const char *message); 1215 | 1216 | 1217 | /* 1218 | Function: str_utf8_rewind 1219 | Moves a cursor backwards in an utf8 string 1220 | 1221 | Parameters: 1222 | str - utf8 string 1223 | cursor - position in the string 1224 | 1225 | Returns: 1226 | New cursor position. 1227 | 1228 | Remarks: 1229 | - Won't move the cursor less then 0 1230 | */ 1231 | int str_utf8_rewind(const char *str, int cursor); 1232 | 1233 | /* 1234 | Function: str_utf8_forward 1235 | Moves a cursor forwards in an utf8 string 1236 | 1237 | Parameters: 1238 | str - utf8 string 1239 | cursor - position in the string 1240 | 1241 | Returns: 1242 | New cursor position. 1243 | 1244 | Remarks: 1245 | - Won't move the cursor beyond the zero termination marker 1246 | */ 1247 | int str_utf8_forward(const char *str, int cursor); 1248 | 1249 | /* 1250 | Function: str_utf8_decode 1251 | Decodes an utf8 character 1252 | 1253 | Parameters: 1254 | ptr - pointer to an utf8 string. this pointer will be moved forward 1255 | 1256 | Returns: 1257 | Unicode value for the character. -1 for invalid characters and 0 for end of string. 1258 | 1259 | Remarks: 1260 | - This function will also move the pointer forward. 1261 | */ 1262 | int str_utf8_decode(const char **ptr); 1263 | 1264 | /* 1265 | Function: str_utf8_encode 1266 | Encode an utf8 character 1267 | 1268 | Parameters: 1269 | ptr - Pointer to a buffer that should recive the data. Should be able to hold at least 4 bytes. 1270 | 1271 | Returns: 1272 | Number of bytes put into the buffer. 1273 | 1274 | Remarks: 1275 | - Does not do zero termination of the string. 1276 | */ 1277 | int str_utf8_encode(char *ptr, int chr); 1278 | 1279 | /* 1280 | Function: str_utf8_check 1281 | Checks if a strings contains just valid utf8 characters. 1282 | 1283 | Parameters: 1284 | str - Pointer to a possible utf8 string. 1285 | 1286 | Returns: 1287 | 0 - invalid characters found. 1288 | 1 - only valid characters found. 1289 | 1290 | Remarks: 1291 | - The string is treated as zero-terminated utf8 string. 1292 | */ 1293 | int str_utf8_check(const char *str); 1294 | 1295 | #ifdef __cplusplus 1296 | } 1297 | #endif 1298 | 1299 | #endif 1300 | -------------------------------------------------------------------------------- /server/obj/.gitignore: -------------------------------------------------------------------------------- 1 | *.o -------------------------------------------------------------------------------- /server/src/argparse.c: -------------------------------------------------------------------------------- 1 | #include "argparse.h" 2 | 3 | #if defined(__cplusplus) 4 | extern "C" { 5 | #endif 6 | 7 | #define OPT_UNSET 1 8 | 9 | static const char * 10 | prefix_skip(const char *str, const char *prefix) 11 | { 12 | size_t len = strlen(prefix); 13 | return strncmp(str, prefix, len) ? NULL : str + len; 14 | } 15 | 16 | int 17 | prefix_cmp(const char *str, const char *prefix) 18 | { 19 | for (;; str++, prefix++) 20 | if (!*prefix) 21 | return 0; 22 | else if (*str != *prefix) 23 | return (unsigned char)*prefix - (unsigned char)*str; 24 | } 25 | 26 | static void 27 | argparse_error(struct argparse *this_, const struct argparse_option *opt, 28 | const char *reason) 29 | { 30 | if (!strncmp(this_->argv[0], "--", 2)) { 31 | fprintf(stderr, "error: option `%s` %s\n", opt->long_name, reason); 32 | exit(-1); 33 | } else { 34 | fprintf(stderr, "error: option `%c` %s\n", opt->short_name, reason); 35 | exit(-1); 36 | } 37 | } 38 | 39 | static int 40 | argparse_getvalue(struct argparse *this_, const struct argparse_option *opt, 41 | int flags) 42 | { 43 | const char *s = NULL; 44 | if (!opt->value) 45 | goto skipped; 46 | switch (opt->type) { 47 | case ARGPARSE_OPT_BOOLEAN: 48 | if (flags & OPT_UNSET) { 49 | *(int *)opt->value = *(int *)opt->value - 1; 50 | } else { 51 | *(int *)opt->value = *(int *)opt->value + 1; 52 | } 53 | if (*(int *)opt->value < 0) { 54 | *(int *)opt->value = 0; 55 | } 56 | break; 57 | case ARGPARSE_OPT_BIT: 58 | if (flags & OPT_UNSET) { 59 | *(int *)opt->value &= ~opt->data; 60 | } else { 61 | *(int *)opt->value |= opt->data; 62 | } 63 | break; 64 | case ARGPARSE_OPT_STRING: 65 | if (this_->optvalue) { 66 | *(const char **)opt->value = this_->optvalue; 67 | this_->optvalue = NULL; 68 | } else if (this_->argc > 1) { 69 | this_->argc--; 70 | *(const char **)opt->value = *++this_->argv; 71 | } else { 72 | argparse_error(this_, opt, "requires a value"); 73 | } 74 | break; 75 | case ARGPARSE_OPT_INTEGER: 76 | if (this_->optvalue) { 77 | *(int *)opt->value = strtol(this_->optvalue, (char **)&s, 0); 78 | this_->optvalue = NULL; 79 | } else if (this_->argc > 1) { 80 | this_->argc--; 81 | *(int *)opt->value = strtol(*++this_->argv, (char **)&s, 0); 82 | } else { 83 | argparse_error(this_, opt, "requires a value"); 84 | } 85 | if (*s) 86 | argparse_error(this_, opt, "expects a numerical value"); 87 | break; 88 | default: 89 | assert(0); 90 | } 91 | 92 | skipped: 93 | if (opt->callback) { 94 | return opt->callback(this_, opt); 95 | } 96 | 97 | return 0; 98 | } 99 | 100 | static void 101 | argparse_options_check(const struct argparse_option *options) 102 | { 103 | for (; options->type != ARGPARSE_OPT_END; options++) { 104 | switch (options->type) { 105 | case ARGPARSE_OPT_END: 106 | case ARGPARSE_OPT_BOOLEAN: 107 | case ARGPARSE_OPT_BIT: 108 | case ARGPARSE_OPT_INTEGER: 109 | case ARGPARSE_OPT_STRING: 110 | continue; 111 | default: 112 | fprintf(stderr, "wrong option type: %d", options->type); 113 | break; 114 | } 115 | } 116 | } 117 | 118 | static int 119 | argparse_short_opt(struct argparse *this_, const struct argparse_option *options) 120 | { 121 | for (; options->type != ARGPARSE_OPT_END; options++) { 122 | if (options->short_name == *this_->optvalue) { 123 | this_->optvalue = this_->optvalue[1] ? this_->optvalue + 1 : NULL; 124 | return argparse_getvalue(this_, options, 0); 125 | } 126 | } 127 | return -2; 128 | } 129 | 130 | static int 131 | argparse_long_opt(struct argparse *this_, const struct argparse_option *options) 132 | { 133 | for (; options->type != ARGPARSE_OPT_END; options++) { 134 | const char *rest; 135 | int opt_flags = 0; 136 | if (!options->long_name) 137 | continue; 138 | 139 | rest = prefix_skip(this_->argv[0] + 2, options->long_name); 140 | if (!rest) { 141 | // Negation allowed? 142 | if (options->flags & OPT_NONEG) { 143 | continue; 144 | } 145 | // Only boolean/bit allow negation. 146 | if (options->type != ARGPARSE_OPT_BOOLEAN && options->type != ARGPARSE_OPT_BIT) { 147 | continue; 148 | } 149 | 150 | if (!prefix_cmp(this_->argv[0] + 2, "no-")) { 151 | rest = prefix_skip(this_->argv[0] + 2 + 3, options->long_name); 152 | if (!rest) 153 | continue; 154 | opt_flags |= OPT_UNSET; 155 | } else { 156 | continue; 157 | } 158 | } 159 | if (*rest) { 160 | if (*rest != '=') 161 | continue; 162 | this_->optvalue = rest + 1; 163 | } 164 | return argparse_getvalue(this_, options, opt_flags); 165 | } 166 | return -2; 167 | } 168 | 169 | int 170 | argparse_init(struct argparse *this_, struct argparse_option *options, 171 | const char *usage, int flags) 172 | { 173 | memset(this_, 0, sizeof(*this_)); 174 | this_->options = options; 175 | this_->usage = usage; 176 | this_->flags = flags; 177 | return 0; 178 | } 179 | 180 | int 181 | argparse_parse(struct argparse *this_, int argc, const char **argv) 182 | { 183 | this_->argc = argc - 1; 184 | this_->argv = argv + 1; 185 | this_->out = argv; 186 | 187 | argparse_options_check(this_->options); 188 | 189 | for (; this_->argc; this_->argc--, this_->argv++) { 190 | const char *arg = this_->argv[0]; 191 | if (arg[0] != '-' || !arg[1]) { 192 | if (this_->flags & ARGPARSE_STOP_AT_NON_OPTION) { 193 | goto end; 194 | } 195 | // if it's not option or is a single char '-', copy verbatimly 196 | this_->out[this_->cpidx++] = this_->argv[0]; 197 | continue; 198 | } 199 | // short option 200 | if (arg[1] != '-') { 201 | this_->optvalue = arg + 1; 202 | switch (argparse_short_opt(this_, this_->options)) { 203 | case -1: 204 | break; 205 | case -2: 206 | goto unknown; 207 | } 208 | while (this_->optvalue) { 209 | switch (argparse_short_opt(this_, this_->options)) { 210 | case -1: 211 | break; 212 | case -2: 213 | goto unknown; 214 | } 215 | } 216 | continue; 217 | } 218 | // if '--' presents 219 | if (!arg[2]) { 220 | this_->argc--; 221 | this_->argv++; 222 | break; 223 | } 224 | // long option 225 | switch (argparse_long_opt(this_, this_->options)) { 226 | case -1: 227 | break; 228 | case -2: 229 | goto unknown; 230 | } 231 | continue; 232 | 233 | unknown: 234 | fprintf(stderr, "error: unknown option `%s`\n", this_->argv[0]); 235 | argparse_usage(this_); 236 | exit(0); 237 | } 238 | 239 | end: 240 | memmove(this_->out + this_->cpidx, this_->argv, 241 | this_->argc * sizeof(*this_->out)); 242 | this_->out[this_->cpidx + this_->argc] = NULL; 243 | 244 | return this_->cpidx + this_->argc; 245 | } 246 | 247 | void 248 | argparse_usage(struct argparse *this_) 249 | { 250 | fprintf(stdout, "Usage: %s\n", this_->usage); 251 | fputc('\n', stdout); 252 | 253 | const struct argparse_option *options; 254 | 255 | // figure out best width 256 | size_t usage_opts_width = 0; 257 | size_t len; 258 | options = this_->options; 259 | for (; options->type != ARGPARSE_OPT_END; options++) { 260 | len = 0; 261 | if ((options)->short_name) { 262 | len += 2; 263 | } 264 | if ((options)->short_name && (options)->long_name) { 265 | len += 2; // separator ", " 266 | } 267 | if ((options)->long_name) { 268 | len += strlen((options)->long_name) + 2; 269 | } 270 | if (options->type == ARGPARSE_OPT_INTEGER) { 271 | len += strlen("=<int>"); 272 | } else if (options->type == ARGPARSE_OPT_STRING) { 273 | len += strlen("=<str>"); 274 | } 275 | len = ceil((float)len / 4) * 4; 276 | if (usage_opts_width < len) { 277 | usage_opts_width = len; 278 | } 279 | } 280 | usage_opts_width += 4; // 4 spaces prefix 281 | 282 | options = this_->options; 283 | for (; options->type != ARGPARSE_OPT_END; options++) { 284 | size_t pos; 285 | int pad; 286 | pos = fprintf(stdout, " "); 287 | if (options->short_name) { 288 | pos += fprintf(stdout, "-%c", options->short_name); 289 | } 290 | if (options->long_name && options->short_name) { 291 | pos += fprintf(stdout, ", "); 292 | } 293 | if (options->long_name) { 294 | pos += fprintf(stdout, "--%s", options->long_name); 295 | } 296 | if (options->type == ARGPARSE_OPT_INTEGER) { 297 | pos += fprintf(stdout, "=<int>"); 298 | } else if (options->type == ARGPARSE_OPT_STRING) { 299 | pos += fprintf(stdout, "=<str>"); 300 | } 301 | if (pos <= usage_opts_width) { 302 | pad = usage_opts_width - pos; 303 | } else { 304 | fputc('\n', stdout); 305 | pad = usage_opts_width; 306 | } 307 | fprintf(stdout, "%*s%s\n", pad + 2, "", options->help); 308 | } 309 | } 310 | 311 | int 312 | argparse_help_cb(struct argparse *this_, const struct argparse_option *option) 313 | { 314 | (void)option; 315 | argparse_usage(this_); 316 | exit(0); 317 | return 0; 318 | } 319 | 320 | #if defined(__cplusplus) 321 | } 322 | #endif 323 | -------------------------------------------------------------------------------- /server/src/json.c: -------------------------------------------------------------------------------- 1 | /* vim: set et ts=3 sw=3 sts=3 ft=c: 2 | * 3 | * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved. 4 | * https://github.com/udp/json-parser 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | */ 29 | 30 | #include "json.h" 31 | 32 | #ifdef _MSC_VER 33 | #ifndef _CRT_SECURE_NO_WARNINGS 34 | #define _CRT_SECURE_NO_WARNINGS 35 | #endif 36 | #endif 37 | 38 | #ifdef __cplusplus 39 | const struct _json_value json_value_none; /* zero-d by ctor */ 40 | #else 41 | const struct _json_value json_value_none = { 0 }; 42 | #endif 43 | 44 | #include <stdio.h> 45 | #include <string.h> 46 | #include <ctype.h> 47 | #include <math.h> 48 | 49 | typedef unsigned short json_uchar; 50 | 51 | static unsigned char hex_value (json_char c) 52 | { 53 | if (isdigit(c)) 54 | return c - '0'; 55 | 56 | switch (c) { 57 | case 'a': case 'A': return 0x0A; 58 | case 'b': case 'B': return 0x0B; 59 | case 'c': case 'C': return 0x0C; 60 | case 'd': case 'D': return 0x0D; 61 | case 'e': case 'E': return 0x0E; 62 | case 'f': case 'F': return 0x0F; 63 | default: return 0xFF; 64 | } 65 | } 66 | 67 | typedef struct 68 | { 69 | unsigned long used_memory; 70 | 71 | unsigned int uint_max; 72 | unsigned long ulong_max; 73 | 74 | json_settings settings; 75 | int first_pass; 76 | 77 | } json_state; 78 | 79 | static void * default_alloc (size_t size, int zero, void * user_data) 80 | { 81 | return zero ? calloc (1, size) : malloc (size); 82 | } 83 | 84 | static void default_free (void * ptr, void * user_data) 85 | { 86 | free (ptr); 87 | } 88 | 89 | static void * json_alloc (json_state * state, unsigned long size, int zero) 90 | { 91 | if ((state->ulong_max - state->used_memory) < size) 92 | return 0; 93 | 94 | if (state->settings.max_memory 95 | && (state->used_memory += size) > state->settings.max_memory) 96 | { 97 | return 0; 98 | } 99 | 100 | return state->settings.mem_alloc (size, zero, state->settings.user_data); 101 | } 102 | 103 | static int new_value 104 | (json_state * state, json_value ** top, json_value ** root, json_value ** alloc, json_type type) 105 | { 106 | json_value * value; 107 | int values_size; 108 | 109 | if (!state->first_pass) 110 | { 111 | value = *top = *alloc; 112 | *alloc = (*alloc)->_reserved.next_alloc; 113 | 114 | if (!*root) 115 | *root = value; 116 | 117 | switch (value->type) 118 | { 119 | case json_array: 120 | 121 | if (! (value->u.array.values = (json_value **) json_alloc 122 | (state, value->u.array.length * sizeof (json_value *), 0)) ) 123 | { 124 | return 0; 125 | } 126 | 127 | value->u.array.length = 0; 128 | break; 129 | 130 | case json_object: 131 | 132 | values_size = sizeof (*value->u.object.values) * value->u.object.length; 133 | 134 | if (! ((*(void **) &value->u.object.values) = json_alloc 135 | (state, values_size + ((unsigned long) value->u.object.values), 0)) ) 136 | { 137 | return 0; 138 | } 139 | 140 | value->_reserved.object_mem = (*(char **) &value->u.object.values) + values_size; 141 | 142 | value->u.object.length = 0; 143 | break; 144 | 145 | case json_string: 146 | 147 | if (! (value->u.string.ptr = (json_char *) json_alloc 148 | (state, (value->u.string.length + 1) * sizeof (json_char), 0)) ) 149 | { 150 | return 0; 151 | } 152 | 153 | value->u.string.length = 0; 154 | break; 155 | 156 | default: 157 | break; 158 | }; 159 | 160 | return 1; 161 | } 162 | 163 | value = (json_value *) json_alloc (state, sizeof (json_value), 1); 164 | 165 | if (!value) 166 | return 0; 167 | 168 | if (!*root) 169 | *root = value; 170 | 171 | value->type = type; 172 | value->parent = *top; 173 | 174 | if (*alloc) 175 | (*alloc)->_reserved.next_alloc = value; 176 | 177 | *alloc = *top = value; 178 | 179 | return 1; 180 | } 181 | 182 | #define e_off \ 183 | ((int) (i - cur_line_begin)) 184 | 185 | #define whitespace \ 186 | case '\n': ++ cur_line; cur_line_begin = i; \ 187 | case ' ': case '\t': case '\r' 188 | 189 | #define string_add(b) \ 190 | do { if (!state.first_pass) string [string_length] = b; ++ string_length; } while (0); 191 | 192 | static const long 193 | flag_next = 1 << 0, 194 | flag_reproc = 1 << 1, 195 | flag_need_comma = 1 << 2, 196 | flag_seek_value = 1 << 3, 197 | flag_escaped = 1 << 4, 198 | flag_string = 1 << 5, 199 | flag_need_colon = 1 << 6, 200 | flag_done = 1 << 7, 201 | flag_num_negative = 1 << 8, 202 | flag_num_zero = 1 << 9, 203 | flag_num_e = 1 << 10, 204 | flag_num_e_got_sign = 1 << 11, 205 | flag_num_e_negative = 1 << 12, 206 | flag_line_comment = 1 << 13, 207 | flag_block_comment = 1 << 14; 208 | 209 | json_value * json_parse_ex (json_settings * settings, 210 | const json_char * json, 211 | size_t length, 212 | char * error_buf) 213 | { 214 | json_char error [json_error_max]; 215 | unsigned int cur_line; 216 | const json_char * cur_line_begin, * i, * end; 217 | json_value * top, * root, * alloc = 0; 218 | json_state state = { 0 }; 219 | long flags; 220 | long num_digits = 0, num_e = 0; 221 | json_int_t num_fraction = 0; 222 | 223 | /* Skip UTF-8 BOM 224 | */ 225 | if (length >= 3 && ((unsigned char) json [0]) == 0xEF 226 | && ((unsigned char) json [1]) == 0xBB 227 | && ((unsigned char) json [2]) == 0xBF) 228 | { 229 | json += 3; 230 | length -= 3; 231 | } 232 | 233 | error[0] = '\0'; 234 | end = (json + length); 235 | 236 | memcpy (&state.settings, settings, sizeof (json_settings)); 237 | 238 | if (!state.settings.mem_alloc) 239 | state.settings.mem_alloc = default_alloc; 240 | 241 | if (!state.settings.mem_free) 242 | state.settings.mem_free = default_free; 243 | 244 | memset (&state.uint_max, 0xFF, sizeof (state.uint_max)); 245 | memset (&state.ulong_max, 0xFF, sizeof (state.ulong_max)); 246 | 247 | state.uint_max -= 8; /* limit of how much can be added before next check */ 248 | state.ulong_max -= 8; 249 | 250 | for (state.first_pass = 1; state.first_pass >= 0; -- state.first_pass) 251 | { 252 | json_uchar uchar; 253 | unsigned char uc_b1, uc_b2, uc_b3, uc_b4; 254 | json_char * string = 0; 255 | unsigned int string_length = 0; 256 | 257 | top = root = 0; 258 | flags = flag_seek_value; 259 | 260 | cur_line = 1; 261 | cur_line_begin = json; 262 | 263 | for (i = json ;; ++ i) 264 | { 265 | json_char b = (i == end ? 0 : *i); 266 | 267 | if (flags & flag_string) 268 | { 269 | if (!b) 270 | { sprintf (error, "Unexpected EOF in string (at %d:%d)", cur_line, e_off); 271 | goto e_failed; 272 | } 273 | 274 | if (string_length > state.uint_max) 275 | goto e_overflow; 276 | 277 | if (flags & flag_escaped) 278 | { 279 | flags &= ~ flag_escaped; 280 | 281 | switch (b) 282 | { 283 | case 'b': string_add ('\b'); break; 284 | case 'f': string_add ('\f'); break; 285 | case 'n': string_add ('\n'); break; 286 | case 'r': string_add ('\r'); break; 287 | case 't': string_add ('\t'); break; 288 | case 'u': 289 | 290 | if (end - i < 4 || 291 | (uc_b1 = hex_value (*++ i)) == 0xFF || (uc_b2 = hex_value (*++ i)) == 0xFF 292 | || (uc_b3 = hex_value (*++ i)) == 0xFF || (uc_b4 = hex_value (*++ i)) == 0xFF) 293 | { 294 | sprintf (error, "Invalid character value `%c` (at %d:%d)", b, cur_line, e_off); 295 | goto e_failed; 296 | } 297 | 298 | uc_b1 = uc_b1 * 16 + uc_b2; 299 | uc_b2 = uc_b3 * 16 + uc_b4; 300 | 301 | uchar = ((json_char) uc_b1) * 256 + uc_b2; 302 | 303 | if (sizeof (json_char) >= sizeof (json_uchar) || (uc_b1 == 0 && uc_b2 <= 0x7F)) 304 | { 305 | string_add ((json_char) uchar); 306 | break; 307 | } 308 | 309 | if (uchar <= 0x7FF) 310 | { 311 | if (state.first_pass) 312 | string_length += 2; 313 | else 314 | { string [string_length ++] = 0xC0 | ((uc_b2 & 0xC0) >> 6) | ((uc_b1 & 0x7) << 2); 315 | string [string_length ++] = 0x80 | (uc_b2 & 0x3F); 316 | } 317 | 318 | break; 319 | } 320 | 321 | if (state.first_pass) 322 | string_length += 3; 323 | else 324 | { string [string_length ++] = 0xE0 | ((uc_b1 & 0xF0) >> 4); 325 | string [string_length ++] = 0x80 | ((uc_b1 & 0xF) << 2) | ((uc_b2 & 0xC0) >> 6); 326 | string [string_length ++] = 0x80 | (uc_b2 & 0x3F); 327 | } 328 | 329 | break; 330 | 331 | default: 332 | string_add (b); 333 | }; 334 | 335 | continue; 336 | } 337 | 338 | if (b == '\\') 339 | { 340 | flags |= flag_escaped; 341 | continue; 342 | } 343 | 344 | if (b == '"') 345 | { 346 | if (!state.first_pass) 347 | string [string_length] = 0; 348 | 349 | flags &= ~ flag_string; 350 | string = 0; 351 | 352 | switch (top->type) 353 | { 354 | case json_string: 355 | 356 | top->u.string.length = string_length; 357 | flags |= flag_next; 358 | 359 | break; 360 | 361 | case json_object: 362 | 363 | if (state.first_pass) 364 | (*(json_char **) &top->u.object.values) += string_length + 1; 365 | else 366 | { 367 | top->u.object.values [top->u.object.length].name 368 | = (json_char *) top->_reserved.object_mem; 369 | 370 | top->u.object.values [top->u.object.length].name_length 371 | = string_length; 372 | 373 | (*(json_char **) &top->_reserved.object_mem) += string_length + 1; 374 | } 375 | 376 | flags |= flag_seek_value | flag_need_colon; 377 | continue; 378 | 379 | default: 380 | break; 381 | }; 382 | } 383 | else 384 | { 385 | string_add (b); 386 | continue; 387 | } 388 | } 389 | 390 | if (state.settings.settings & json_enable_comments) 391 | { 392 | if (flags & (flag_line_comment | flag_block_comment)) 393 | { 394 | if (flags & flag_line_comment) 395 | { 396 | if (b == '\r' || b == '\n' || !b) 397 | { 398 | flags &= ~ flag_line_comment; 399 | -- i; /* so null can be reproc'd */ 400 | } 401 | 402 | continue; 403 | } 404 | 405 | if (flags & flag_block_comment) 406 | { 407 | if (!b) 408 | { sprintf (error, "%d:%d: Unexpected EOF in block comment", cur_line, e_off); 409 | goto e_failed; 410 | } 411 | 412 | if (b == '*' && i < (end - 1) && i [1] == '/') 413 | { 414 | flags &= ~ flag_block_comment; 415 | ++ i; /* skip closing sequence */ 416 | } 417 | 418 | continue; 419 | } 420 | } 421 | else if (b == '/') 422 | { 423 | if (! (flags & (flag_seek_value | flag_done)) && top->type != json_object) 424 | { 425 | sprintf (error, "%d:%d: Comment not allowed here", cur_line, e_off); 426 | goto e_failed; 427 | } 428 | 429 | if (++ i == end) 430 | { sprintf (error, "%d:%d: EOF unexpected", cur_line, e_off); 431 | goto e_failed; 432 | } 433 | 434 | switch (b = *i) 435 | { 436 | case '/': 437 | flags |= flag_line_comment; 438 | continue; 439 | 440 | case '*': 441 | flags |= flag_block_comment; 442 | continue; 443 | 444 | default: 445 | sprintf (error, "%d:%d: Unexpected `%c` in comment opening sequence", cur_line, e_off, b); 446 | goto e_failed; 447 | }; 448 | } 449 | } 450 | 451 | if (flags & flag_done) 452 | { 453 | if (!b) 454 | break; 455 | 456 | switch (b) 457 | { 458 | whitespace: 459 | continue; 460 | 461 | default: 462 | sprintf (error, "%d:%d: Trailing garbage: `%c`", cur_line, e_off, b); 463 | goto e_failed; 464 | }; 465 | } 466 | 467 | if (flags & flag_seek_value) 468 | { 469 | switch (b) 470 | { 471 | whitespace: 472 | continue; 473 | 474 | case ']': 475 | 476 | if (top->type == json_array) 477 | flags = (flags & ~ (flag_need_comma | flag_seek_value)) | flag_next; 478 | else 479 | { sprintf (error, "%d:%d: Unexpected ]", cur_line, e_off); 480 | goto e_failed; 481 | } 482 | 483 | break; 484 | 485 | default: 486 | 487 | if (flags & flag_need_comma) 488 | { 489 | if (b == ',') 490 | { flags &= ~ flag_need_comma; 491 | continue; 492 | } 493 | else 494 | { sprintf (error, "%d:%d: Expected , before %c", cur_line, e_off, b); 495 | goto e_failed; 496 | } 497 | } 498 | 499 | if (flags & flag_need_colon) 500 | { 501 | if (b == ':') 502 | { flags &= ~ flag_need_colon; 503 | continue; 504 | } 505 | else 506 | { sprintf (error, "%d:%d: Expected : before %c", cur_line, e_off, b); 507 | goto e_failed; 508 | } 509 | } 510 | 511 | flags &= ~ flag_seek_value; 512 | 513 | switch (b) 514 | { 515 | case '{': 516 | 517 | if (!new_value (&state, &top, &root, &alloc, json_object)) 518 | goto e_alloc_failure; 519 | 520 | continue; 521 | 522 | case '[': 523 | 524 | if (!new_value (&state, &top, &root, &alloc, json_array)) 525 | goto e_alloc_failure; 526 | 527 | flags |= flag_seek_value; 528 | continue; 529 | 530 | case '"': 531 | 532 | if (!new_value (&state, &top, &root, &alloc, json_string)) 533 | goto e_alloc_failure; 534 | 535 | flags |= flag_string; 536 | 537 | string = top->u.string.ptr; 538 | string_length = 0; 539 | 540 | continue; 541 | 542 | case 't': 543 | 544 | if ((end - i) < 3 || *(++ i) != 'r' || *(++ i) != 'u' || *(++ i) != 'e') 545 | goto e_unknown_value; 546 | 547 | if (!new_value (&state, &top, &root, &alloc, json_boolean)) 548 | goto e_alloc_failure; 549 | 550 | top->u.boolean = 1; 551 | 552 | flags |= flag_next; 553 | break; 554 | 555 | case 'f': 556 | 557 | if ((end - i) < 4 || *(++ i) != 'a' || *(++ i) != 'l' || *(++ i) != 's' || *(++ i) != 'e') 558 | goto e_unknown_value; 559 | 560 | if (!new_value (&state, &top, &root, &alloc, json_boolean)) 561 | goto e_alloc_failure; 562 | 563 | flags |= flag_next; 564 | break; 565 | 566 | case 'n': 567 | 568 | if ((end - i) < 3 || *(++ i) != 'u' || *(++ i) != 'l' || *(++ i) != 'l') 569 | goto e_unknown_value; 570 | 571 | if (!new_value (&state, &top, &root, &alloc, json_null)) 572 | goto e_alloc_failure; 573 | 574 | flags |= flag_next; 575 | break; 576 | 577 | default: 578 | 579 | if (isdigit (b) || b == '-') 580 | { 581 | if (!new_value (&state, &top, &root, &alloc, json_integer)) 582 | goto e_alloc_failure; 583 | 584 | if (!state.first_pass) 585 | { 586 | while (isdigit (b) || b == '+' || b == '-' 587 | || b == 'e' || b == 'E' || b == '.') 588 | { 589 | if ( (++ i) == end) 590 | { 591 | b = 0; 592 | break; 593 | } 594 | 595 | b = *i; 596 | } 597 | 598 | flags |= flag_next | flag_reproc; 599 | break; 600 | } 601 | 602 | flags &= ~ (flag_num_negative | flag_num_e | 603 | flag_num_e_got_sign | flag_num_e_negative | 604 | flag_num_zero); 605 | 606 | num_digits = 0; 607 | num_fraction = 0; 608 | num_e = 0; 609 | 610 | if (b != '-') 611 | { 612 | flags |= flag_reproc; 613 | break; 614 | } 615 | 616 | flags |= flag_num_negative; 617 | continue; 618 | } 619 | else 620 | { sprintf (error, "%d:%d: Unexpected %c when seeking value", cur_line, e_off, b); 621 | goto e_failed; 622 | } 623 | }; 624 | }; 625 | } 626 | else 627 | { 628 | switch (top->type) 629 | { 630 | case json_object: 631 | 632 | switch (b) 633 | { 634 | whitespace: 635 | continue; 636 | 637 | case '"': 638 | 639 | if (flags & flag_need_comma) 640 | { 641 | sprintf (error, "%d:%d: Expected , before \"", cur_line, e_off); 642 | goto e_failed; 643 | } 644 | 645 | flags |= flag_string; 646 | 647 | string = (json_char *) top->_reserved.object_mem; 648 | string_length = 0; 649 | 650 | break; 651 | 652 | case '}': 653 | 654 | flags = (flags & ~ flag_need_comma) | flag_next; 655 | break; 656 | 657 | case ',': 658 | 659 | if (flags & flag_need_comma) 660 | { 661 | flags &= ~ flag_need_comma; 662 | break; 663 | } 664 | 665 | default: 666 | 667 | sprintf (error, "%d:%d: Unexpected `%c` in object", cur_line, e_off, b); 668 | goto e_failed; 669 | }; 670 | 671 | break; 672 | 673 | case json_integer: 674 | case json_double: 675 | 676 | if (isdigit (b)) 677 | { 678 | ++ num_digits; 679 | 680 | if (top->type == json_integer || flags & flag_num_e) 681 | { 682 | if (! (flags & flag_num_e)) 683 | { 684 | if (flags & flag_num_zero) 685 | { sprintf (error, "%d:%d: Unexpected `0` before `%c`", cur_line, e_off, b); 686 | goto e_failed; 687 | } 688 | 689 | if (num_digits == 1 && b == '0') 690 | flags |= flag_num_zero; 691 | } 692 | else 693 | { 694 | flags |= flag_num_e_got_sign; 695 | num_e = (num_e * 10) + (b - '0'); 696 | continue; 697 | } 698 | 699 | top->u.integer = (top->u.integer * 10) + (b - '0'); 700 | continue; 701 | } 702 | 703 | num_fraction = (num_fraction * 10) + (b - '0'); 704 | continue; 705 | } 706 | 707 | if (b == '+' || b == '-') 708 | { 709 | if ( (flags & flag_num_e) && !(flags & flag_num_e_got_sign)) 710 | { 711 | flags |= flag_num_e_got_sign; 712 | 713 | if (b == '-') 714 | flags |= flag_num_e_negative; 715 | 716 | continue; 717 | } 718 | } 719 | else if (b == '.' && top->type == json_integer) 720 | { 721 | if (!num_digits) 722 | { sprintf (error, "%d:%d: Expected digit before `.`", cur_line, e_off); 723 | goto e_failed; 724 | } 725 | 726 | top->type = json_double; 727 | top->u.dbl = (double) top->u.integer; 728 | 729 | num_digits = 0; 730 | continue; 731 | } 732 | 733 | if (! (flags & flag_num_e)) 734 | { 735 | if (top->type == json_double) 736 | { 737 | if (!num_digits) 738 | { sprintf (error, "%d:%d: Expected digit after `.`", cur_line, e_off); 739 | goto e_failed; 740 | } 741 | 742 | top->u.dbl += ((double) num_fraction) / (pow (10, (double) num_digits)); 743 | } 744 | 745 | if (b == 'e' || b == 'E') 746 | { 747 | flags |= flag_num_e; 748 | 749 | if (top->type == json_integer) 750 | { 751 | top->type = json_double; 752 | top->u.dbl = (double) top->u.integer; 753 | } 754 | 755 | num_digits = 0; 756 | flags &= ~ flag_num_zero; 757 | 758 | continue; 759 | } 760 | } 761 | else 762 | { 763 | if (!num_digits) 764 | { sprintf (error, "%d:%d: Expected digit after `e`", cur_line, e_off); 765 | goto e_failed; 766 | } 767 | 768 | top->u.dbl *= pow (10, (double) (flags & flag_num_e_negative ? - num_e : num_e)); 769 | } 770 | 771 | if (flags & flag_num_negative) 772 | { 773 | if (top->type == json_integer) 774 | top->u.integer = - top->u.integer; 775 | else 776 | top->u.dbl = - top->u.dbl; 777 | } 778 | 779 | flags |= flag_next | flag_reproc; 780 | break; 781 | 782 | default: 783 | break; 784 | }; 785 | } 786 | 787 | if (flags & flag_reproc) 788 | { 789 | flags &= ~ flag_reproc; 790 | -- i; 791 | } 792 | 793 | if (flags & flag_next) 794 | { 795 | flags = (flags & ~ flag_next) | flag_need_comma; 796 | 797 | if (!top->parent) 798 | { 799 | /* root value done */ 800 | 801 | flags |= flag_done; 802 | continue; 803 | } 804 | 805 | if (top->parent->type == json_array) 806 | flags |= flag_seek_value; 807 | 808 | if (!state.first_pass) 809 | { 810 | json_value * parent = top->parent; 811 | 812 | switch (parent->type) 813 | { 814 | case json_object: 815 | 816 | parent->u.object.values 817 | [parent->u.object.length].value = top; 818 | 819 | break; 820 | 821 | case json_array: 822 | 823 | parent->u.array.values 824 | [parent->u.array.length] = top; 825 | 826 | break; 827 | 828 | default: 829 | break; 830 | }; 831 | } 832 | 833 | if ( (++ top->parent->u.array.length) > state.uint_max) 834 | goto e_overflow; 835 | 836 | top = top->parent; 837 | 838 | continue; 839 | } 840 | } 841 | 842 | alloc = root; 843 | } 844 | 845 | return root; 846 | 847 | e_unknown_value: 848 | 849 | sprintf (error, "%d:%d: Unknown value", cur_line, e_off); 850 | goto e_failed; 851 | 852 | e_alloc_failure: 853 | 854 | strcpy (error, "Memory allocation failure"); 855 | goto e_failed; 856 | 857 | e_overflow: 858 | 859 | sprintf (error, "%d:%d: Too long (caught overflow)", cur_line, e_off); 860 | goto e_failed; 861 | 862 | e_failed: 863 | 864 | if (error_buf) 865 | { 866 | if (*error) 867 | strcpy (error_buf, error); 868 | else 869 | strcpy (error_buf, "Unknown error"); 870 | } 871 | 872 | if (state.first_pass) 873 | alloc = root; 874 | 875 | while (alloc) 876 | { 877 | top = alloc->_reserved.next_alloc; 878 | state.settings.mem_free (alloc, state.settings.user_data); 879 | alloc = top; 880 | } 881 | 882 | if (!state.first_pass) 883 | json_value_free_ex (&state.settings, root); 884 | 885 | return 0; 886 | } 887 | 888 | json_value * json_parse (const json_char * json, size_t length) 889 | { 890 | json_settings settings = { 0 }; 891 | return json_parse_ex (&settings, json, length, 0); 892 | } 893 | 894 | void json_value_free_ex (json_settings * settings, json_value * value) 895 | { 896 | json_value * cur_value; 897 | 898 | if (!value) 899 | return; 900 | 901 | value->parent = 0; 902 | 903 | while (value) 904 | { 905 | switch (value->type) 906 | { 907 | case json_array: 908 | 909 | if (!value->u.array.length) 910 | { 911 | settings->mem_free (value->u.array.values, settings->user_data); 912 | break; 913 | } 914 | 915 | value = value->u.array.values [-- value->u.array.length]; 916 | continue; 917 | 918 | case json_object: 919 | 920 | if (!value->u.object.length) 921 | { 922 | settings->mem_free (value->u.object.values, settings->user_data); 923 | break; 924 | } 925 | 926 | value = value->u.object.values [-- value->u.object.length].value; 927 | continue; 928 | 929 | case json_string: 930 | 931 | settings->mem_free (value->u.string.ptr, settings->user_data); 932 | break; 933 | 934 | default: 935 | break; 936 | }; 937 | 938 | cur_value = value; 939 | value = value->parent; 940 | settings->mem_free (cur_value, settings->user_data); 941 | } 942 | } 943 | 944 | void json_value_free (json_value * value) 945 | { 946 | json_settings settings = { 0 }; 947 | settings.mem_free = default_free; 948 | json_value_free_ex (&settings, value); 949 | } 950 | -------------------------------------------------------------------------------- /server/src/main.cpp: -------------------------------------------------------------------------------- 1 | #define __STDC_FORMAT_MACROS 2 | #include <inttypes.h> 3 | #include <time.h> 4 | #include <detect.h> 5 | #include <system.h> 6 | #include <argparse.h> 7 | #include <json.h> 8 | #include "server.h" 9 | #include "main.h" 10 | 11 | #if defined(CONF_FAMILY_UNIX) 12 | #include <signal.h> 13 | #endif 14 | 15 | #ifndef PRId64 16 | #define PRId64 "I64d" 17 | #endif 18 | static volatile int gs_Running = 1; 19 | static volatile int gs_ReloadConfig = 0; 20 | 21 | static void ExitFunc(int Signal) 22 | { 23 | printf("[EXIT] Caught signal %d\n", Signal); 24 | gs_Running = 0; 25 | } 26 | 27 | static void ReloadFunc(int Signal) 28 | { 29 | printf("[RELOAD] Caught signal %d\n", Signal); 30 | gs_ReloadConfig = 1; 31 | } 32 | 33 | CConfig::CConfig() 34 | { 35 | // Initialize to default values 36 | m_Verbose = false; // -v, --verbose 37 | str_copy(m_aConfigFile, "config.json", sizeof(m_aConfigFile)); // -c, --config 38 | str_copy(m_aWebDir, "../web/", sizeof(m_aJSONFile)); // -d, --web-dir 39 | str_copy(m_aTemplateFile, "template.html", sizeof(m_aTemplateFile)); 40 | str_copy(m_aJSONFile, "json/stats.json", sizeof(m_aJSONFile)); 41 | str_copy(m_aBindAddr, "", sizeof(m_aBindAddr)); // -b, --bind 42 | m_Port = 35601; // -p, --port 43 | } 44 | 45 | CMain::CMain(CConfig Config) : m_Config(Config) 46 | { 47 | mem_zero(m_aClients, sizeof(m_aClients)); 48 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 49 | m_aClients[i].m_ClientNetID = -1; 50 | } 51 | 52 | CMain::CClient *CMain::ClientNet(int ClientNetID) 53 | { 54 | if(ClientNetID < 0 || ClientNetID >= NET_MAX_CLIENTS) 55 | return 0; 56 | 57 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 58 | { 59 | if(Client(i)->m_ClientNetID == ClientNetID) 60 | return Client(i); 61 | } 62 | 63 | return 0; 64 | } 65 | 66 | int CMain::ClientNetToClient(int ClientNetID) 67 | { 68 | if(ClientNetID < 0 || ClientNetID >= NET_MAX_CLIENTS) 69 | return -1; 70 | 71 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 72 | { 73 | if(Client(i)->m_ClientNetID == ClientNetID) 74 | return i; 75 | } 76 | 77 | return -1; 78 | } 79 | 80 | void CMain::OnNewClient(int ClientNetID, int ClientID) 81 | { 82 | dbg_msg("main", "OnNewClient(ncid=%d, cid=%d)", ClientNetID, ClientID); 83 | Client(ClientID)->m_ClientNetID = ClientNetID; 84 | Client(ClientID)->m_ClientNetType = m_Server.Network()->ClientAddr(ClientNetID)->type; 85 | Client(ClientID)->m_TimeConnected = time_get(); 86 | Client(ClientID)->m_Connected = true; 87 | 88 | if(Client(ClientID)->m_ClientNetType == NETTYPE_IPV4) 89 | Client(ClientID)->m_Stats.m_Online4 = true; 90 | else if(Client(ClientID)->m_ClientNetType == NETTYPE_IPV6) 91 | Client(ClientID)->m_Stats.m_Online6 = true; 92 | } 93 | 94 | void CMain::OnDelClient(int ClientNetID) 95 | { 96 | int ClientID = ClientNetToClient(ClientNetID); 97 | dbg_msg("main", "OnDelClient(ncid=%d, cid=%d)", ClientNetID, ClientID); 98 | if(ClientID >= 0 && ClientID < NET_MAX_CLIENTS) 99 | { 100 | Client(ClientID)->m_Connected = false; 101 | Client(ClientID)->m_ClientNetID = -1; 102 | Client(ClientID)->m_ClientNetType = NETTYPE_INVALID; 103 | mem_zero(&Client(ClientID)->m_Stats, sizeof(CClient::CStats)); 104 | } 105 | } 106 | 107 | int CMain::HandleMessage(int ClientNetID, char *pMessage) 108 | { 109 | CClient *pClient = ClientNet(ClientNetID); 110 | if(!pClient) 111 | return true; 112 | 113 | if(str_comp_num(pMessage, "update", sizeof("update")-1) == 0) 114 | { 115 | char *pData = str_skip_whitespaces(&pMessage[sizeof("update")-1]); 116 | 117 | // parse json data 118 | json_settings JsonSettings; 119 | mem_zero(&JsonSettings, sizeof(JsonSettings)); 120 | char aError[256]; 121 | json_value *pJsonData = json_parse_ex(&JsonSettings, pData, strlen(pData), aError); 122 | if(!pJsonData) 123 | { 124 | dbg_msg("main", "JSON Error: %s", aError); 125 | if(pClient->m_Stats.m_Pong) 126 | m_Server.Network()->Send(ClientNetID, "1"); 127 | return 1; 128 | } 129 | 130 | // extract data 131 | const json_value &rStart = (*pJsonData); 132 | if(rStart["uptime"].type) 133 | pClient->m_Stats.m_Uptime = rStart["uptime"].u.integer; 134 | if(rStart["load"].type) 135 | pClient->m_Stats.m_Load = rStart["load"].u.dbl; 136 | if(rStart["network_rx"].type) 137 | pClient->m_Stats.m_NetworkRx = rStart["network_rx"].u.integer; 138 | if(rStart["network_tx"].type) 139 | pClient->m_Stats.m_NetworkTx = rStart["network_tx"].u.integer; 140 | if(rStart["network_in"].type) 141 | pClient->m_Stats.m_NetworkIN = rStart["network_in"].u.integer; 142 | if(rStart["network_out"].type) 143 | pClient->m_Stats.m_NetworkOUT = rStart["network_out"].u.integer; 144 | if(rStart["memory_total"].type) 145 | pClient->m_Stats.m_MemTotal = rStart["memory_total"].u.integer; 146 | if(rStart["memory_used"].type) 147 | pClient->m_Stats.m_MemUsed = rStart["memory_used"].u.integer; 148 | if(rStart["swap_total"].type) 149 | pClient->m_Stats.m_SwapTotal = rStart["swap_total"].u.integer; 150 | if(rStart["swap_used"].type) 151 | pClient->m_Stats.m_SwapUsed = rStart["swap_used"].u.integer; 152 | if(rStart["hdd_total"].type) 153 | pClient->m_Stats.m_HDDTotal = rStart["hdd_total"].u.integer; 154 | if(rStart["hdd_used"].type) 155 | pClient->m_Stats.m_HDDUsed = rStart["hdd_used"].u.integer; 156 | if(rStart["cpu"].type) 157 | pClient->m_Stats.m_CPU = rStart["cpu"].u.dbl; 158 | if(rStart["online4"].type && pClient->m_ClientNetType == NETTYPE_IPV6) 159 | pClient->m_Stats.m_Online4 = rStart["online4"].u.boolean; 160 | if(rStart["online6"].type && pClient->m_ClientNetType == NETTYPE_IPV4) 161 | pClient->m_Stats.m_Online6 = rStart["online6"].u.boolean; 162 | if(rStart["custom"].type == json_string) 163 | str_copy(pClient->m_Stats.m_aCustom, rStart["custom"].u.string.ptr, sizeof(pClient->m_Stats.m_aCustom)); 164 | 165 | if(m_Config.m_Verbose) 166 | { 167 | if(rStart["online4"].type) 168 | dbg_msg("main", "Online4: %s\nUptime: %" PRId64 "\nLoad: %f\nNetworkRx: %" PRId64 "\nNetworkTx: %" PRId64 "\nNetworkIN: %" PRId64 "\nNetworkOUT: %" PRId64 "\nMemTotal: %" PRId64 "\nMemUsed: %" PRId64 "\nSwapTotal: %" PRId64 "\nSwapUsed: %" PRId64 "\nHDDTotal: %" PRId64 "\nHDDUsed: %" PRId64 "\nCPU: %f\n", 169 | rStart["online4"].u.boolean ? "true" : "false", 170 | pClient->m_Stats.m_Uptime, pClient->m_Stats.m_Load, pClient->m_Stats.m_NetworkRx, pClient->m_Stats.m_NetworkTx, pClient->m_Stats.m_NetworkIN, pClient->m_Stats.m_NetworkOUT, pClient->m_Stats.m_MemTotal, pClient->m_Stats.m_MemUsed, pClient->m_Stats.m_SwapTotal, pClient->m_Stats.m_SwapUsed, pClient->m_Stats.m_HDDTotal, pClient->m_Stats.m_HDDUsed, pClient->m_Stats.m_CPU); 171 | else if(rStart["online6"].type) 172 | dbg_msg("main", "Online6: %s\nUptime: %" PRId64 "\nLoad: %f\nNetworkRx: %" PRId64 "\nNetworkTx: %" PRId64 "\nNetworkIN: %" PRId64 "\nNetworkOUT: %" PRId64 "\nMemTotal: %" PRId64 "\nMemUsed: %" PRId64 "\nSwapTotal: %" PRId64 "\nSwapUsed: %" PRId64 "\nHDDTotal: %" PRId64 "\nHDDUsed: %" PRId64 "\nCPU: %f\n", 173 | rStart["online6"].u.boolean ? "true" : "false", 174 | pClient->m_Stats.m_Uptime, pClient->m_Stats.m_Load, pClient->m_Stats.m_NetworkRx, pClient->m_Stats.m_NetworkTx, pClient->m_Stats.m_NetworkIN, pClient->m_Stats.m_NetworkOUT, pClient->m_Stats.m_MemTotal, pClient->m_Stats.m_MemUsed, pClient->m_Stats.m_SwapTotal, pClient->m_Stats.m_SwapUsed, pClient->m_Stats.m_HDDTotal, pClient->m_Stats.m_HDDUsed, pClient->m_Stats.m_CPU); 175 | else 176 | dbg_msg("main", "Uptime: %" PRId64 "\nLoad: %f\nNetworkRx: %" PRId64 "\nNetworkTx: %" PRId64 "\nNetworkIN: %" PRId64 "\nNetworkOUT: %" PRId64 "\nMemTotal: %" PRId64 "\nMemUsed: %" PRId64 "\nSwapTotal: %" PRId64 "\nSwapUsed: %" PRId64 "\nHDDTotal: %" PRId64 "\nHDDUsed: %" PRId64 "\nCPU: %f\n", 177 | pClient->m_Stats.m_Uptime, pClient->m_Stats.m_Load, pClient->m_Stats.m_NetworkRx, pClient->m_Stats.m_NetworkTx, pClient->m_Stats.m_NetworkIN, pClient->m_Stats.m_NetworkOUT, pClient->m_Stats.m_MemTotal, pClient->m_Stats.m_MemUsed, pClient->m_Stats.m_SwapTotal, pClient->m_Stats.m_SwapUsed, pClient->m_Stats.m_HDDTotal, pClient->m_Stats.m_HDDUsed, pClient->m_Stats.m_CPU); 178 | } 179 | 180 | // clean up 181 | json_value_free(pJsonData); 182 | 183 | if(pClient->m_Stats.m_Pong) 184 | m_Server.Network()->Send(ClientNetID, "0"); 185 | return 0; 186 | } 187 | else if(str_comp_num(pMessage, "pong", sizeof("pong")-1) == 0) 188 | { 189 | char *pData = str_skip_whitespaces(&pMessage[sizeof("pong")-1]); 190 | 191 | if(!str_comp(pData, "0") || !str_comp(pData, "off")) 192 | pClient->m_Stats.m_Pong = false; 193 | else if(!str_comp(pData, "1") || !str_comp(pData, "on")) 194 | pClient->m_Stats.m_Pong = true; 195 | 196 | return 0; 197 | } 198 | 199 | if(pClient->m_Stats.m_Pong) 200 | m_Server.Network()->Send(ClientNetID, "1"); 201 | 202 | return 1; 203 | } 204 | 205 | void CMain::JSONUpdateThread(void *pUser) 206 | { 207 | CJSONUpdateThreadData *m_pJSONUpdateThreadData = (CJSONUpdateThreadData *)pUser; 208 | CClient *pClients = m_pJSONUpdateThreadData->pClients; 209 | CConfig *pConfig = m_pJSONUpdateThreadData->pConfig; 210 | 211 | while(gs_Running) 212 | { 213 | char aFileBuf[2048*NET_MAX_CLIENTS]; 214 | char *pBuf = aFileBuf; 215 | 216 | str_format(pBuf, sizeof(aFileBuf), "{\n\"servers\": [\n"); 217 | pBuf += strlen(pBuf); 218 | 219 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 220 | { 221 | if(!pClients[i].m_Active || pClients[i].m_Disabled) 222 | continue; 223 | 224 | if(pClients[i].m_Connected) 225 | { 226 | // Uptime 227 | char aUptime[16]; 228 | int Days = pClients[i].m_Stats.m_Uptime/60.0/60.0/24.0; 229 | if(Days > 0) 230 | { 231 | if(Days > 1) 232 | str_format(aUptime, sizeof(aUptime), "%d 天", Days); 233 | else 234 | str_format(aUptime, sizeof(aUptime), "%d 天", Days); 235 | } 236 | else 237 | str_format(aUptime, sizeof(aUptime), "%02d:%02d:%02d", (int)(pClients[i].m_Stats.m_Uptime/60.0/60.0), (int)((pClients[i].m_Stats.m_Uptime/60)%60), (int)((pClients[i].m_Stats.m_Uptime)%60)); 238 | 239 | str_format(pBuf, sizeof(aFileBuf) - (pBuf - aFileBuf), "{ \"name\": \"%s\", \"type\": \"%s\", \"host\": \"%s\", \"location\": \"%s\", \"online4\": %s, \"online6\": %s, \"uptime\": \"%s\", \"load\": %.2f, \"network_rx\": %" PRId64 ", \"network_tx\": %" PRId64 ", \"network_in\": %" PRId64 ", \"network_out\": %" PRId64 ", \"cpu\": %d, \"memory_total\": %" PRId64 ", \"memory_used\": %" PRId64 ", \"swap_total\": %" PRId64 ", \"swap_used\": %" PRId64 ", \"hdd_total\": %" PRId64 ", \"hdd_used\": %" PRId64 ", \"custom\": \"%s\" ,\"region\": \"%s\"},\n", 240 | pClients[i].m_aName, pClients[i].m_aType, pClients[i].m_aHost, pClients[i].m_aLocation, pClients[i].m_Stats.m_Online4 ? "true" : "false", pClients[i].m_Stats.m_Online6 ? "true" : "false", aUptime, pClients[i].m_Stats.m_Load, pClients[i].m_Stats.m_NetworkRx, pClients[i].m_Stats.m_NetworkTx, pClients[i].m_Stats.m_NetworkIN, pClients[i].m_Stats.m_NetworkOUT, (int)pClients[i].m_Stats.m_CPU, pClients[i].m_Stats.m_MemTotal, pClients[i].m_Stats.m_MemUsed, pClients[i].m_Stats.m_SwapTotal, pClients[i].m_Stats.m_SwapUsed, pClients[i].m_Stats.m_HDDTotal, pClients[i].m_Stats.m_HDDUsed, pClients[i].m_Stats.m_aCustom,pClients[i].m_aRegion); 241 | pBuf += strlen(pBuf); 242 | } 243 | else 244 | { 245 | str_format(pBuf, sizeof(aFileBuf) - (pBuf - aFileBuf), "{ \"name\": \"%s\", \"type\": \"%s\", \"host\": \"%s\", \"location\": \"%s\", \"online4\": false, \"online6\": false ,\"region\": \"%s\"},\n", 246 | pClients[i].m_aName, pClients[i].m_aType, pClients[i].m_aHost, pClients[i].m_aLocation,pClients[i].m_aRegion); 247 | pBuf += strlen(pBuf); 248 | } 249 | } 250 | if(!m_pJSONUpdateThreadData->m_ReloadRequired) 251 | str_format(pBuf - 2, sizeof(aFileBuf) - (pBuf - aFileBuf), "\n],\n\"updated\": \"%lld\"\n}", (long long)time(/*ago*/0)); 252 | else 253 | { 254 | str_format(pBuf - 2, sizeof(aFileBuf) - (pBuf - aFileBuf), "\n],\n\"updated\": \"%lld\",\n\"reload\": true\n}", (long long)time(/*ago*/0)); 255 | m_pJSONUpdateThreadData->m_ReloadRequired--; 256 | } 257 | pBuf += strlen(pBuf); 258 | 259 | char aJSONFileTmp[1024]; 260 | str_format(aJSONFileTmp, sizeof(aJSONFileTmp), "%s~", pConfig->m_aJSONFile); 261 | IOHANDLE File = io_open(aJSONFileTmp, IOFLAG_WRITE); 262 | if(!File) 263 | { 264 | dbg_msg("main", "Couldn't open %s", aJSONFileTmp); 265 | exit(1); 266 | } 267 | io_write(File, aFileBuf, (pBuf - aFileBuf)); 268 | io_flush(File); 269 | io_close(File); 270 | fs_rename(aJSONFileTmp, pConfig->m_aJSONFile); 271 | thread_sleep(1000); 272 | } 273 | fs_remove(pConfig->m_aJSONFile); 274 | } 275 | 276 | int CMain::ReadConfig() 277 | { 278 | // read and parse config 279 | IOHANDLE File = io_open(m_Config.m_aConfigFile, IOFLAG_READ); 280 | if(!File) 281 | { 282 | dbg_msg("main", "Couldn't open %s", m_Config.m_aConfigFile); 283 | return 1; 284 | } 285 | int FileSize = (int)io_length(File); 286 | char *pFileData = (char *)mem_alloc(FileSize + 1, 1); 287 | 288 | io_read(File, pFileData, FileSize); 289 | pFileData[FileSize] = 0; 290 | io_close(File); 291 | 292 | // parse json data 293 | json_settings JsonSettings; 294 | mem_zero(&JsonSettings, sizeof(JsonSettings)); 295 | char aError[256]; 296 | json_value *pJsonData = json_parse_ex(&JsonSettings, pFileData, strlen(pFileData), aError); 297 | if(!pJsonData) 298 | { 299 | dbg_msg("main", "JSON Error in file %s: %s", m_Config.m_aConfigFile, aError); 300 | mem_free(pFileData); 301 | return 1; 302 | } 303 | 304 | // reset clients 305 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 306 | { 307 | if(!Client(i)->m_Active || !Client(i)->m_Connected) 308 | continue; 309 | 310 | m_Server.Network()->Drop(Client(i)->m_ClientNetID, "Server reloading..."); 311 | } 312 | mem_zero(m_aClients, sizeof(m_aClients)); 313 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 314 | m_aClients[i].m_ClientNetID = -1; 315 | 316 | // extract data 317 | int ID = 0; 318 | const json_value &rStart = (*pJsonData)["servers"]; 319 | if(rStart.type == json_array) 320 | { 321 | for(unsigned i = 0; i < rStart.u.array.length; i++) 322 | { 323 | if(ID < 0 || ID >= NET_MAX_CLIENTS) 324 | continue; 325 | 326 | Client(ID)->m_Active = true; 327 | Client(ID)->m_Disabled = rStart[i]["disabled"].u.boolean; 328 | str_copy(Client(ID)->m_aName, rStart[i]["name"].u.string.ptr, sizeof(Client(ID)->m_aName)); 329 | str_copy(Client(ID)->m_aUsername, rStart[i]["username"].u.string.ptr, sizeof(Client(ID)->m_aUsername)); 330 | str_copy(Client(ID)->m_aType, rStart[i]["type"].u.string.ptr, sizeof(Client(ID)->m_aType)); 331 | str_copy(Client(ID)->m_aHost, rStart[i]["host"].u.string.ptr, sizeof(Client(ID)->m_aHost)); 332 | str_copy(Client(ID)->m_aLocation, rStart[i]["location"].u.string.ptr, sizeof(Client(ID)->m_aLocation)); 333 | str_copy(Client(ID)->m_aPassword, rStart[i]["password"].u.string.ptr, sizeof(Client(ID)->m_aPassword)); 334 | str_copy(Client(ID)->m_aRegion, rStart[i]["region"].u.string.ptr, sizeof(Client(ID)->m_aRegion)); 335 | 336 | if(m_Config.m_Verbose) 337 | { 338 | if(Client(ID)->m_Disabled) 339 | dbg_msg("main", "[#%d: Name: \"%s\", Username: \"%s\", Type: \"%s\", Host: \"%s\", Location: \"%s\", Password: \"%s\", Region: \"%s\"]", 340 | ID, Client(ID)->m_aName, Client(ID)->m_aUsername, Client(ID)->m_aType, Client(ID)->m_aHost, Client(ID)->m_aLocation, Client(ID)->m_aPassword, Client(ID)->m_aRegion); 341 | else 342 | dbg_msg("main", "#%d: Name: \"%s\", Username: \"%s\", Type: \"%s\", Host: \"%s\", Location: \"%s\", Password: \"%s\", Region: \"%s\"", 343 | ID, Client(ID)->m_aName, Client(ID)->m_aUsername, Client(ID)->m_aType, Client(ID)->m_aHost, Client(ID)->m_aLocation, Client(ID)->m_aPassword, Client(ID)->m_aRegion); 344 | 345 | } 346 | ID++; 347 | } 348 | } 349 | 350 | // clean up 351 | json_value_free(pJsonData); 352 | mem_free(pFileData); 353 | 354 | // tell clients to reload the page 355 | m_JSONUpdateThreadData.m_ReloadRequired = 2; 356 | 357 | return 0; 358 | } 359 | 360 | int CMain::Run() 361 | { 362 | if(m_Server.Init(this, m_Config.m_aBindAddr, m_Config.m_Port)) 363 | return 1; 364 | 365 | if(ReadConfig()) 366 | return 1; 367 | 368 | // Start JSON Update Thread 369 | m_JSONUpdateThreadData.m_ReloadRequired = 2; 370 | m_JSONUpdateThreadData.pClients = m_aClients; 371 | m_JSONUpdateThreadData.pConfig = &m_Config; 372 | void *LoadThread = thread_create(JSONUpdateThread, &m_JSONUpdateThreadData); 373 | //thread_detach(LoadThread); 374 | 375 | while(gs_Running) 376 | { 377 | if(gs_ReloadConfig) 378 | { 379 | if(ReadConfig()) 380 | return 1; 381 | m_Server.NetBan()->UnbanAll(); 382 | gs_ReloadConfig = 0; 383 | } 384 | 385 | m_Server.Update(); 386 | 387 | // wait for incomming data 388 | net_socket_read_wait(*m_Server.Network()->Socket(), 10); 389 | } 390 | 391 | dbg_msg("server", "Closing."); 392 | m_Server.Network()->Close(); 393 | thread_wait(LoadThread); 394 | 395 | return 0; 396 | } 397 | 398 | int main(int argc, const char *argv[]) 399 | { 400 | int RetVal; 401 | dbg_logger_stdout(); 402 | 403 | #if defined(CONF_FAMILY_UNIX) 404 | signal(SIGINT, ExitFunc); 405 | signal(SIGTERM, ExitFunc); 406 | signal(SIGQUIT, ExitFunc); 407 | signal(SIGHUP, ReloadFunc); 408 | #endif 409 | 410 | char aUsage[128]; 411 | CConfig Config; 412 | str_format(aUsage, sizeof(aUsage), "%s [options]", argv[0]); 413 | const char *pConfigFile = 0; 414 | const char *pWebDir = 0; 415 | const char *pBindAddr = 0; 416 | 417 | struct argparse_option aOptions[] = { 418 | OPT_HELP(), 419 | OPT_BOOLEAN('v', "verbose", &Config.m_Verbose, "Verbose output", 0), 420 | OPT_STRING('c', "config", &pConfigFile, "Config file to use", 0), 421 | OPT_STRING('d', "web-dir", &pWebDir, "Location of the web directory", 0), 422 | OPT_STRING('b', "bind", &pBindAddr, "Bind to address", 0), 423 | OPT_INTEGER('p', "port", &Config.m_Port, "Listen on port", 0), 424 | OPT_END(), 425 | }; 426 | struct argparse Argparse; 427 | argparse_init(&Argparse, aOptions, aUsage, 0); 428 | argc = argparse_parse(&Argparse, argc, argv); 429 | 430 | if(pConfigFile) 431 | str_copy(Config.m_aConfigFile, pConfigFile, sizeof(Config.m_aConfigFile)); 432 | if(pWebDir) 433 | str_copy(Config.m_aWebDir, pWebDir, sizeof(Config.m_aWebDir)); 434 | if(pBindAddr) 435 | str_copy(Config.m_aBindAddr, pBindAddr, sizeof(Config.m_aBindAddr)); 436 | 437 | if(Config.m_aWebDir[strlen(Config.m_aWebDir)-1] != '/') 438 | str_append(Config.m_aWebDir, "/", sizeof(Config.m_aWebDir)); 439 | if(!fs_is_dir(Config.m_aWebDir)) 440 | { 441 | dbg_msg("main", "ERROR: Can't find web directory: %s", Config.m_aWebDir); 442 | return 1; 443 | } 444 | 445 | char aTmp[1024]; 446 | str_format(aTmp, sizeof(aTmp), "%s%s", Config.m_aWebDir, Config.m_aJSONFile); 447 | str_copy(Config.m_aJSONFile, aTmp, sizeof(Config.m_aJSONFile)); 448 | 449 | CMain Main(Config); 450 | RetVal = Main.Run(); 451 | 452 | return RetVal; 453 | } 454 | -------------------------------------------------------------------------------- /server/src/main.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_H 2 | #define MAIN_H 3 | 4 | #include <stdint.h> 5 | #include "server.h" 6 | 7 | class CConfig 8 | { 9 | public: 10 | bool m_Verbose; 11 | char m_aConfigFile[1024]; 12 | char m_aWebDir[1024]; 13 | char m_aTemplateFile[1024]; 14 | char m_aJSONFile[1024]; 15 | char m_aBindAddr[256]; 16 | int m_Port; 17 | 18 | CConfig(); 19 | }; 20 | 21 | class CMain 22 | { 23 | CConfig m_Config; 24 | CServer m_Server; 25 | 26 | struct CClient 27 | { 28 | bool m_Active; 29 | bool m_Disabled; 30 | bool m_Connected; 31 | int m_ClientNetID; 32 | int m_ClientNetType; 33 | char m_aUsername[128]; 34 | char m_aName[128]; 35 | char m_aType[128]; 36 | char m_aHost[128]; 37 | char m_aLocation[128]; 38 | char m_aRegion[128]; 39 | char m_aPassword[128]; 40 | 41 | int64 m_TimeConnected; 42 | int64 m_LastUpdate; 43 | 44 | struct CStats 45 | { 46 | bool m_Online4; 47 | bool m_Online6; 48 | int64_t m_Uptime; 49 | double m_Load; 50 | int64_t m_NetworkRx; 51 | int64_t m_NetworkTx; 52 | int64_t m_NetworkIN; 53 | int64_t m_NetworkOUT; 54 | int64_t m_MemTotal; 55 | int64_t m_MemUsed; 56 | int64_t m_SwapTotal; 57 | int64_t m_SwapUsed; 58 | int64_t m_HDDTotal; 59 | int64_t m_HDDUsed; 60 | double m_CPU; 61 | char m_aCustom[512]; 62 | // Options 63 | bool m_Pong; 64 | } m_Stats; 65 | } m_aClients[NET_MAX_CLIENTS]; 66 | 67 | struct CJSONUpdateThreadData 68 | { 69 | CClient *pClients; 70 | CConfig *pConfig; 71 | volatile short m_ReloadRequired; 72 | } m_JSONUpdateThreadData; 73 | 74 | static void JSONUpdateThread(void *pUser); 75 | public: 76 | CMain(CConfig Config); 77 | 78 | void OnNewClient(int ClienNettID, int ClientID); 79 | void OnDelClient(int ClientNetID); 80 | int HandleMessage(int ClientNetID, char *pMessage); 81 | int ReadConfig(); 82 | int Run(); 83 | 84 | CClient *Client(int ClientID) { return &m_aClients[ClientID]; } 85 | CClient *ClientNet(int ClientNetID); 86 | const CConfig *Config() const { return &m_Config; } 87 | int ClientNetToClient(int ClientNetID); 88 | }; 89 | 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /server/src/netban.cpp: -------------------------------------------------------------------------------- 1 | #include <math.h> 2 | #include "netban.h" 3 | 4 | bool CNetBan::StrAllnum(const char *pStr) 5 | { 6 | while(*pStr) 7 | { 8 | if(!(*pStr >= '0' && *pStr <= '9')) 9 | return false; 10 | pStr++; 11 | } 12 | return true; 13 | } 14 | 15 | 16 | CNetBan::CNetHash::CNetHash(const NETADDR *pAddr) 17 | { 18 | if(pAddr->type==NETTYPE_IPV4) 19 | m_Hash = (pAddr->ip[0]+pAddr->ip[1]+pAddr->ip[2]+pAddr->ip[3])&0xFF; 20 | else 21 | m_Hash = (pAddr->ip[0]+pAddr->ip[1]+pAddr->ip[2]+pAddr->ip[3]+pAddr->ip[4]+pAddr->ip[5]+pAddr->ip[6]+pAddr->ip[7]+ 22 | pAddr->ip[8]+pAddr->ip[9]+pAddr->ip[10]+pAddr->ip[11]+pAddr->ip[12]+pAddr->ip[13]+pAddr->ip[14]+pAddr->ip[15])&0xFF; 23 | m_HashIndex = 0; 24 | } 25 | 26 | CNetBan::CNetHash::CNetHash(const CNetRange *pRange) 27 | { 28 | m_Hash = 0; 29 | m_HashIndex = 0; 30 | for(int i = 0; pRange->m_LB.ip[i] == pRange->m_UB.ip[i]; ++i) 31 | { 32 | m_Hash += pRange->m_LB.ip[i]; 33 | ++m_HashIndex; 34 | } 35 | m_Hash &= 0xFF; 36 | } 37 | 38 | int CNetBan::CNetHash::MakeHashArray(const NETADDR *pAddr, CNetHash aHash[17]) 39 | { 40 | int Length = pAddr->type==NETTYPE_IPV4 ? 4 : 16; 41 | aHash[0].m_Hash = 0; 42 | aHash[0].m_HashIndex = 0; 43 | for(int i = 1, Sum = 0; i <= Length; ++i) 44 | { 45 | Sum += pAddr->ip[i-1]; 46 | aHash[i].m_Hash = Sum&0xFF; 47 | aHash[i].m_HashIndex = i%Length; 48 | } 49 | return Length; 50 | } 51 | 52 | 53 | template<class T, int HashCount> 54 | typename CNetBan::CBan<T> *CNetBan::CBanPool<T, HashCount>::Add(const T *pData, const CBanInfo *pInfo, const CNetHash *pNetHash) 55 | { 56 | if(!m_pFirstFree) 57 | return 0; 58 | 59 | // create new ban 60 | CBan<T> *pBan = m_pFirstFree; 61 | pBan->m_Data = *pData; 62 | pBan->m_Info = *pInfo; 63 | pBan->m_NetHash = *pNetHash; 64 | if(pBan->m_pNext) 65 | pBan->m_pNext->m_pPrev = pBan->m_pPrev; 66 | if(pBan->m_pPrev) 67 | pBan->m_pPrev->m_pNext = pBan->m_pNext; 68 | else 69 | m_pFirstFree = pBan->m_pNext; 70 | 71 | // add it to the hash list 72 | if(m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]) 73 | m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]->m_pHashPrev = pBan; 74 | pBan->m_pHashPrev = 0; 75 | pBan->m_pHashNext = m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]; 76 | m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash] = pBan; 77 | 78 | // insert it into the used list 79 | if(m_pFirstUsed) 80 | { 81 | for(CBan<T> *p = m_pFirstUsed; ; p = p->m_pNext) 82 | { 83 | if(p->m_Info.m_Expires == CBanInfo::EXPIRES_NEVER || (pInfo->m_Expires != CBanInfo::EXPIRES_NEVER && pInfo->m_Expires <= p->m_Info.m_Expires)) 84 | { 85 | // insert before 86 | pBan->m_pNext = p; 87 | pBan->m_pPrev = p->m_pPrev; 88 | if(p->m_pPrev) 89 | p->m_pPrev->m_pNext = pBan; 90 | else 91 | m_pFirstUsed = pBan; 92 | p->m_pPrev = pBan; 93 | break; 94 | } 95 | 96 | if(!p->m_pNext) 97 | { 98 | // last entry 99 | p->m_pNext = pBan; 100 | pBan->m_pPrev = p; 101 | pBan->m_pNext = 0; 102 | break; 103 | } 104 | } 105 | } 106 | else 107 | { 108 | m_pFirstUsed = pBan; 109 | pBan->m_pNext = pBan->m_pPrev = 0; 110 | } 111 | 112 | // update ban count 113 | ++m_CountUsed; 114 | 115 | return pBan; 116 | } 117 | 118 | template<class T, int HashCount> 119 | int CNetBan::CBanPool<T, HashCount>::Remove(CBan<T> *pBan) 120 | { 121 | if(pBan == 0) 122 | return -1; 123 | 124 | // remove from hash list 125 | if(pBan->m_pHashNext) 126 | pBan->m_pHashNext->m_pHashPrev = pBan->m_pHashPrev; 127 | if(pBan->m_pHashPrev) 128 | pBan->m_pHashPrev->m_pHashNext = pBan->m_pHashNext; 129 | else 130 | m_paaHashList[pBan->m_NetHash.m_HashIndex][pBan->m_NetHash.m_Hash] = pBan->m_pHashNext; 131 | pBan->m_pHashNext = pBan->m_pHashPrev = 0; 132 | 133 | // remove from used list 134 | if(pBan->m_pNext) 135 | pBan->m_pNext->m_pPrev = pBan->m_pPrev; 136 | if(pBan->m_pPrev) 137 | pBan->m_pPrev->m_pNext = pBan->m_pNext; 138 | else 139 | m_pFirstUsed = pBan->m_pNext; 140 | 141 | // add to recycle list 142 | if(m_pFirstFree) 143 | m_pFirstFree->m_pPrev = pBan; 144 | pBan->m_pPrev = 0; 145 | pBan->m_pNext = m_pFirstFree; 146 | m_pFirstFree = pBan; 147 | 148 | // update ban count 149 | --m_CountUsed; 150 | 151 | return 0; 152 | } 153 | 154 | template<class T, int HashCount> 155 | void CNetBan::CBanPool<T, HashCount>::Update(CBan<CDataType> *pBan, const CBanInfo *pInfo) 156 | { 157 | pBan->m_Info = *pInfo; 158 | 159 | // remove from used list 160 | if(pBan->m_pNext) 161 | pBan->m_pNext->m_pPrev = pBan->m_pPrev; 162 | if(pBan->m_pPrev) 163 | pBan->m_pPrev->m_pNext = pBan->m_pNext; 164 | else 165 | m_pFirstUsed = pBan->m_pNext; 166 | 167 | // insert it into the used list 168 | if(m_pFirstUsed) 169 | { 170 | for(CBan<T> *p = m_pFirstUsed; ; p = p->m_pNext) 171 | { 172 | if(p->m_Info.m_Expires == CBanInfo::EXPIRES_NEVER || (pInfo->m_Expires != CBanInfo::EXPIRES_NEVER && pInfo->m_Expires <= p->m_Info.m_Expires)) 173 | { 174 | // insert before 175 | pBan->m_pNext = p; 176 | pBan->m_pPrev = p->m_pPrev; 177 | if(p->m_pPrev) 178 | p->m_pPrev->m_pNext = pBan; 179 | else 180 | m_pFirstUsed = pBan; 181 | p->m_pPrev = pBan; 182 | break; 183 | } 184 | 185 | if(!p->m_pNext) 186 | { 187 | // last entry 188 | p->m_pNext = pBan; 189 | pBan->m_pPrev = p; 190 | pBan->m_pNext = 0; 191 | break; 192 | } 193 | } 194 | } 195 | else 196 | { 197 | m_pFirstUsed = pBan; 198 | pBan->m_pNext = pBan->m_pPrev = 0; 199 | } 200 | } 201 | 202 | template<class T, int HashCount> 203 | void CNetBan::CBanPool<T, HashCount>::Reset() 204 | { 205 | mem_zero(m_paaHashList, sizeof(m_paaHashList)); 206 | mem_zero(m_aBans, sizeof(m_aBans)); 207 | m_pFirstUsed = 0; 208 | m_CountUsed = 0; 209 | 210 | for(int i = 1; i < MAX_BANS-1; ++i) 211 | { 212 | m_aBans[i].m_pNext = &m_aBans[i+1]; 213 | m_aBans[i].m_pPrev = &m_aBans[i-1]; 214 | } 215 | 216 | m_aBans[0].m_pNext = &m_aBans[1]; 217 | m_aBans[MAX_BANS-1].m_pPrev = &m_aBans[MAX_BANS-2]; 218 | m_pFirstFree = &m_aBans[0]; 219 | } 220 | 221 | template<class T, int HashCount> 222 | typename CNetBan::CBan<T> *CNetBan::CBanPool<T, HashCount>::Get(int Index) const 223 | { 224 | if(Index < 0 || Index >= Num()) 225 | return 0; 226 | 227 | for(CNetBan::CBan<T> *pBan = m_pFirstUsed; pBan; pBan = pBan->m_pNext, --Index) 228 | { 229 | if(Index == 0) 230 | return pBan; 231 | } 232 | 233 | return 0; 234 | } 235 | 236 | 237 | template<class T> 238 | void CNetBan::MakeBanInfo(const CBan<T> *pBan, char *pBuf, unsigned BuffSize, int Type) const 239 | { 240 | if(pBan == 0 || pBuf == 0) 241 | { 242 | if(BuffSize > 0) 243 | pBuf[0] = 0; 244 | return; 245 | } 246 | 247 | // build type based part 248 | char aBuf[256]; 249 | if(Type == MSGTYPE_PLAYER) 250 | str_copy(aBuf, "You have been banned", sizeof(aBuf)); 251 | else 252 | { 253 | char aTemp[256]; 254 | switch(Type) 255 | { 256 | case MSGTYPE_LIST: 257 | str_format(aBuf, sizeof(aBuf), "%s banned", NetToString(&pBan->m_Data, aTemp, sizeof(aTemp))); break; 258 | case MSGTYPE_BANADD: 259 | str_format(aBuf, sizeof(aBuf), "banned %s", NetToString(&pBan->m_Data, aTemp, sizeof(aTemp))); break; 260 | case MSGTYPE_BANREM: 261 | str_format(aBuf, sizeof(aBuf), "unbanned %s", NetToString(&pBan->m_Data, aTemp, sizeof(aTemp))); break; 262 | default: 263 | aBuf[0] = 0; 264 | } 265 | } 266 | 267 | // add info part 268 | if(pBan->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER) 269 | { 270 | int Mins = ((pBan->m_Info.m_Expires-time_timestamp()) + 59) / 60; 271 | if(Mins <= 1) 272 | str_format(pBuf, BuffSize, "%s for 1 minute (%s)", aBuf, pBan->m_Info.m_aReason); 273 | else 274 | str_format(pBuf, BuffSize, "%s for %d minutes (%s)", aBuf, Mins, pBan->m_Info.m_aReason); 275 | } 276 | else 277 | str_format(pBuf, BuffSize, "%s for life (%s)", aBuf, pBan->m_Info.m_aReason); 278 | } 279 | 280 | template<class T> 281 | int CNetBan::Ban(T *pBanPool, const typename T::CDataType *pData, int Seconds, const char *pReason) 282 | { 283 | // do not ban localhost 284 | if(NetMatch(pData, &m_LocalhostIPV4) || NetMatch(pData, &m_LocalhostIPV6)) 285 | { 286 | dbg_msg("net_ban", "ban failed (localhost)"); 287 | return -1; 288 | } 289 | 290 | int Stamp = Seconds > 0 ? time_timestamp()+Seconds : CBanInfo::EXPIRES_NEVER; 291 | 292 | // set up info 293 | CBanInfo Info = {0}; 294 | Info.m_Expires = Stamp; 295 | str_copy(Info.m_aReason, pReason, sizeof(Info.m_aReason)); 296 | 297 | // check if it already exists 298 | CNetHash NetHash(pData); 299 | CBan<typename T::CDataType> *pBan = pBanPool->Find(pData, &NetHash); 300 | if(pBan) 301 | { 302 | // adjust the ban 303 | pBanPool->Update(pBan, &Info); 304 | char aBuf[128]; 305 | MakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_LIST); 306 | dbg_msg("net_ban", aBuf); 307 | return 1; 308 | } 309 | 310 | // add ban and print result 311 | pBan = pBanPool->Add(pData, &Info, &NetHash); 312 | if(pBan) 313 | { 314 | char aBuf[128]; 315 | MakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_BANADD); 316 | dbg_msg("net_ban", aBuf); 317 | return 0; 318 | } 319 | else 320 | dbg_msg("net_ban", "ban failed (full banlist)"); 321 | return -1; 322 | } 323 | 324 | template<class T> 325 | int CNetBan::Unban(T *pBanPool, const typename T::CDataType *pData) 326 | { 327 | CNetHash NetHash(pData); 328 | CBan<typename T::CDataType> *pBan = pBanPool->Find(pData, &NetHash); 329 | if(pBan) 330 | { 331 | char aBuf[256]; 332 | MakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_BANREM); 333 | pBanPool->Remove(pBan); 334 | dbg_msg("net_ban", aBuf); 335 | return 0; 336 | } 337 | else 338 | dbg_msg("net_ban", "unban failed (invalid entry)"); 339 | return -1; 340 | } 341 | 342 | void CNetBan::Init() 343 | { 344 | m_BanAddrPool.Reset(); 345 | m_BanRangePool.Reset(); 346 | 347 | net_host_lookup("localhost", &m_LocalhostIPV4, NETTYPE_IPV4); 348 | net_host_lookup("localhost", &m_LocalhostIPV6, NETTYPE_IPV6); 349 | } 350 | 351 | void CNetBan::Update() 352 | { 353 | int Now = time_timestamp(); 354 | 355 | // remove expired bans 356 | char aBuf[256], aNetStr[256]; 357 | while(m_BanAddrPool.First() && m_BanAddrPool.First()->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER && m_BanAddrPool.First()->m_Info.m_Expires < Now) 358 | { 359 | str_format(aBuf, sizeof(aBuf), "ban %s expired", NetToString(&m_BanAddrPool.First()->m_Data, aNetStr, sizeof(aNetStr))); 360 | dbg_msg("net_ban", aBuf); 361 | m_BanAddrPool.Remove(m_BanAddrPool.First()); 362 | } 363 | while(m_BanRangePool.First() && m_BanRangePool.First()->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER && m_BanRangePool.First()->m_Info.m_Expires < Now) 364 | { 365 | str_format(aBuf, sizeof(aBuf), "ban %s expired", NetToString(&m_BanRangePool.First()->m_Data, aNetStr, sizeof(aNetStr))); 366 | dbg_msg("net_ban", aBuf); 367 | m_BanRangePool.Remove(m_BanRangePool.First()); 368 | } 369 | } 370 | 371 | int CNetBan::BanAddr(const NETADDR *pAddr, int Seconds, const char *pReason) 372 | { 373 | return Ban(&m_BanAddrPool, pAddr, Seconds, pReason); 374 | } 375 | 376 | int CNetBan::BanRange(const CNetRange *pRange, int Seconds, const char *pReason) 377 | { 378 | if(pRange->IsValid()) 379 | return Ban(&m_BanRangePool, pRange, Seconds, pReason); 380 | 381 | dbg_msg("net_ban", "ban failed (invalid range)"); 382 | return -1; 383 | } 384 | 385 | int CNetBan::UnbanByAddr(const NETADDR *pAddr) 386 | { 387 | return Unban(&m_BanAddrPool, pAddr); 388 | } 389 | 390 | int CNetBan::UnbanByRange(const CNetRange *pRange) 391 | { 392 | if(pRange->IsValid()) 393 | return Unban(&m_BanRangePool, pRange); 394 | 395 | dbg_msg("net_ban", "ban failed (invalid range)"); 396 | return -1; 397 | } 398 | 399 | int CNetBan::UnbanByIndex(int Index) 400 | { 401 | int Result; 402 | char aBuf[256]; 403 | CBanAddr *pBan = m_BanAddrPool.Get(Index); 404 | if(pBan) 405 | { 406 | NetToString(&pBan->m_Data, aBuf, sizeof(aBuf)); 407 | Result = m_BanAddrPool.Remove(pBan); 408 | } 409 | else 410 | { 411 | CBanRange *pBan = m_BanRangePool.Get(Index-m_BanAddrPool.Num()); 412 | if(pBan) 413 | { 414 | NetToString(&pBan->m_Data, aBuf, sizeof(aBuf)); 415 | Result = m_BanRangePool.Remove(pBan); 416 | } 417 | else 418 | { 419 | dbg_msg("net_ban", "unban failed (invalid index)"); 420 | return -1; 421 | } 422 | } 423 | 424 | char aMsg[256]; 425 | str_format(aMsg, sizeof(aMsg), "unbanned index %i (%s)", Index, aBuf); 426 | dbg_msg("net_ban", aMsg); 427 | return Result; 428 | } 429 | 430 | void CNetBan::UnbanAll() 431 | { 432 | m_BanAddrPool.Reset(); 433 | m_BanRangePool.Reset(); 434 | } 435 | 436 | bool CNetBan::IsBanned(const NETADDR *pAddr, char *pBuf, unsigned BufferSize) const 437 | { 438 | CNetHash aHash[17]; 439 | int Length = CNetHash::MakeHashArray(pAddr, aHash); 440 | 441 | // check ban adresses 442 | CBanAddr *pBan = m_BanAddrPool.Find(pAddr, &aHash[Length]); 443 | if(pBan) 444 | { 445 | MakeBanInfo(pBan, pBuf, BufferSize, MSGTYPE_PLAYER); 446 | return true; 447 | } 448 | 449 | // check ban ranges 450 | for(int i = Length-1; i >= 0; --i) 451 | { 452 | for(CBanRange *pBan = m_BanRangePool.First(&aHash[i]); pBan; pBan = pBan->m_pHashNext) 453 | { 454 | if(NetMatch(&pBan->m_Data, pAddr, i, Length)) 455 | { 456 | MakeBanInfo(pBan, pBuf, BufferSize, MSGTYPE_PLAYER); 457 | return true; 458 | } 459 | } 460 | } 461 | 462 | return false; 463 | } 464 | -------------------------------------------------------------------------------- /server/src/netban.h: -------------------------------------------------------------------------------- 1 | #ifndef NETBAN_H 2 | #define NETBAN_H 3 | 4 | #include <system.h> 5 | 6 | inline int NetComp(const NETADDR *pAddr1, const NETADDR *pAddr2) 7 | { 8 | return mem_comp(pAddr1, pAddr2, pAddr1->type==NETTYPE_IPV4 ? 8 : 20); 9 | } 10 | 11 | class CNetRange 12 | { 13 | public: 14 | NETADDR m_LB; 15 | NETADDR m_UB; 16 | 17 | bool IsValid() const { return m_LB.type == m_UB.type && NetComp(&m_LB, &m_UB) < 0; } 18 | }; 19 | 20 | inline int NetComp(const CNetRange *pRange1, const CNetRange *pRange2) 21 | { 22 | return NetComp(&pRange1->m_LB, &pRange2->m_LB) || NetComp(&pRange1->m_UB, &pRange2->m_UB); 23 | } 24 | 25 | 26 | class CNetBan 27 | { 28 | protected: 29 | bool NetMatch(const NETADDR *pAddr1, const NETADDR *pAddr2) const 30 | { 31 | return NetComp(pAddr1, pAddr2) == 0; 32 | } 33 | 34 | bool NetMatch(const CNetRange *pRange, const NETADDR *pAddr, int Start, int Length) const 35 | { 36 | return pRange->m_LB.type == pAddr->type && (Start == 0 || mem_comp(&pRange->m_LB.ip[0], &pAddr->ip[0], Start) == 0) && 37 | mem_comp(&pRange->m_LB.ip[Start], &pAddr->ip[Start], Length-Start) <= 0 && mem_comp(&pRange->m_UB.ip[Start], &pAddr->ip[Start], Length-Start) >= 0; 38 | } 39 | 40 | bool NetMatch(const CNetRange *pRange, const NETADDR *pAddr) const 41 | { 42 | return NetMatch(pRange, pAddr, 0, pRange->m_LB.type==NETTYPE_IPV4 ? 4 : 16); 43 | } 44 | 45 | const char *NetToString(const NETADDR *pData, char *pBuffer, unsigned BufferSize) const 46 | { 47 | char aAddrStr[NETADDR_MAXSTRSIZE]; 48 | net_addr_str(pData, aAddrStr, sizeof(aAddrStr), false); 49 | str_format(pBuffer, BufferSize, "'%s'", aAddrStr); 50 | return pBuffer; 51 | } 52 | 53 | const char *NetToString(const CNetRange *pData, char *pBuffer, unsigned BufferSize) const 54 | { 55 | char aAddrStr1[NETADDR_MAXSTRSIZE], aAddrStr2[NETADDR_MAXSTRSIZE]; 56 | net_addr_str(&pData->m_LB, aAddrStr1, sizeof(aAddrStr1), false); 57 | net_addr_str(&pData->m_UB, aAddrStr2, sizeof(aAddrStr2), false); 58 | str_format(pBuffer, BufferSize, "'%s' - '%s'", aAddrStr1, aAddrStr2); 59 | return pBuffer; 60 | } 61 | 62 | // todo: move? 63 | static bool StrAllnum(const char *pStr); 64 | 65 | class CNetHash 66 | { 67 | public: 68 | int m_Hash; 69 | int m_HashIndex; // matching parts for ranges, 0 for addr 70 | 71 | CNetHash() {} 72 | CNetHash(const NETADDR *pAddr); 73 | CNetHash(const CNetRange *pRange); 74 | 75 | static int MakeHashArray(const NETADDR *pAddr, CNetHash aHash[17]); 76 | }; 77 | 78 | struct CBanInfo 79 | { 80 | enum 81 | { 82 | EXPIRES_NEVER=-1, 83 | REASON_LENGTH=64, 84 | }; 85 | int m_Expires; 86 | char m_aReason[REASON_LENGTH]; 87 | }; 88 | 89 | template<class T> struct CBan 90 | { 91 | T m_Data; 92 | CBanInfo m_Info; 93 | CNetHash m_NetHash; 94 | 95 | // hash list 96 | CBan *m_pHashNext; 97 | CBan *m_pHashPrev; 98 | 99 | // used or free list 100 | CBan *m_pNext; 101 | CBan *m_pPrev; 102 | }; 103 | 104 | template<class T, int HashCount> class CBanPool 105 | { 106 | public: 107 | typedef T CDataType; 108 | 109 | CBan<CDataType> *Add(const CDataType *pData, const CBanInfo *pInfo, const CNetHash *pNetHash); 110 | int Remove(CBan<CDataType> *pBan); 111 | void Update(CBan<CDataType> *pBan, const CBanInfo *pInfo); 112 | void Reset(); 113 | 114 | int Num() const { return m_CountUsed; } 115 | bool IsFull() const { return m_CountUsed == MAX_BANS; } 116 | 117 | CBan<CDataType> *First() const { return m_pFirstUsed; } 118 | CBan<CDataType> *First(const CNetHash *pNetHash) const { return m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]; } 119 | CBan<CDataType> *Find(const CDataType *pData, const CNetHash *pNetHash) const 120 | { 121 | for(CBan<CDataType> *pBan = m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]; pBan; pBan = pBan->m_pHashNext) 122 | { 123 | if(NetComp(&pBan->m_Data, pData) == 0) 124 | return pBan; 125 | } 126 | 127 | return 0; 128 | } 129 | CBan<CDataType> *Get(int Index) const; 130 | 131 | private: 132 | enum 133 | { 134 | MAX_BANS=1024, 135 | }; 136 | 137 | CBan<CDataType> *m_paaHashList[HashCount][256]; 138 | CBan<CDataType> m_aBans[MAX_BANS]; 139 | CBan<CDataType> *m_pFirstFree; 140 | CBan<CDataType> *m_pFirstUsed; 141 | int m_CountUsed; 142 | }; 143 | 144 | typedef CBanPool<NETADDR, 1> CBanAddrPool; 145 | typedef CBanPool<CNetRange, 16> CBanRangePool; 146 | typedef CBan<NETADDR> CBanAddr; 147 | typedef CBan<CNetRange> CBanRange; 148 | 149 | template<class T> void MakeBanInfo(const CBan<T> *pBan, char *pBuf, unsigned BuffSize, int Type) const; 150 | template<class T> int Ban(T *pBanPool, const typename T::CDataType *pData, int Seconds, const char *pReason); 151 | template<class T> int Unban(T *pBanPool, const typename T::CDataType *pData); 152 | 153 | CBanAddrPool m_BanAddrPool; 154 | CBanRangePool m_BanRangePool; 155 | NETADDR m_LocalhostIPV4, m_LocalhostIPV6; 156 | 157 | public: 158 | enum 159 | { 160 | MSGTYPE_PLAYER=0, 161 | MSGTYPE_LIST, 162 | MSGTYPE_BANADD, 163 | MSGTYPE_BANREM, 164 | }; 165 | 166 | virtual ~CNetBan() {} 167 | void Init(); 168 | void Update(); 169 | 170 | virtual int BanAddr(const NETADDR *pAddr, int Seconds, const char *pReason); 171 | virtual int BanRange(const CNetRange *pRange, int Seconds, const char *pReason); 172 | int UnbanByAddr(const NETADDR *pAddr); 173 | int UnbanByRange(const CNetRange *pRange); 174 | int UnbanByIndex(int Index); 175 | void UnbanAll(); 176 | bool IsBanned(const NETADDR *pAddr, char *pBuf, unsigned BufferSize) const; 177 | }; 178 | 179 | #endif 180 | -------------------------------------------------------------------------------- /server/src/network.cpp: -------------------------------------------------------------------------------- 1 | #include <system.h> 2 | #include "netban.h" 3 | #include "network.h" 4 | 5 | bool CNetwork::Open(NETADDR BindAddr, CNetBan *pNetBan) 6 | { 7 | // zero out the whole structure 8 | mem_zero(this, sizeof(*this)); 9 | m_Socket.type = NETTYPE_INVALID; 10 | m_Socket.ipv4sock = -1; 11 | m_Socket.ipv6sock = -1; 12 | m_pNetBan = pNetBan; 13 | 14 | // open socket 15 | m_Socket = net_tcp_create(BindAddr); 16 | if(!m_Socket.type) 17 | return false; 18 | if(net_tcp_listen(m_Socket, NET_MAX_CLIENTS)) 19 | return false; 20 | net_set_non_blocking(m_Socket); 21 | 22 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 23 | m_aSlots[i].m_Connection.Reset(); 24 | 25 | return true; 26 | } 27 | 28 | void CNetwork::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser) 29 | { 30 | m_pfnNewClient = pfnNewClient; 31 | m_pfnDelClient = pfnDelClient; 32 | m_UserPtr = pUser; 33 | } 34 | 35 | int CNetwork::Close() 36 | { 37 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 38 | m_aSlots[i].m_Connection.Disconnect("Closing connection."); 39 | 40 | net_tcp_close(m_Socket); 41 | 42 | return 0; 43 | } 44 | 45 | int CNetwork::Drop(int ClientID, const char *pReason) 46 | { 47 | if(m_pfnDelClient) 48 | m_pfnDelClient(ClientID, pReason, m_UserPtr); 49 | 50 | m_aSlots[ClientID].m_Connection.Disconnect(pReason); 51 | 52 | return 0; 53 | } 54 | 55 | int CNetwork::AcceptClient(NETSOCKET Socket, const NETADDR *pAddr) 56 | { 57 | char aError[256] = { 0 }; 58 | int FreeSlot = -1; 59 | 60 | // look for free slot or multiple client 61 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 62 | { 63 | if(FreeSlot == -1 && m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) 64 | FreeSlot = i; 65 | if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE) 66 | { 67 | if(net_addr_comp(pAddr, m_aSlots[i].m_Connection.PeerAddress()) == 0) 68 | { 69 | str_copy(aError, "Only one client per IP allowed.", sizeof(aError)); 70 | break; 71 | } 72 | } 73 | } 74 | 75 | // accept client 76 | if(!aError[0] && FreeSlot != -1) 77 | { 78 | m_aSlots[FreeSlot].m_Connection.Init(Socket, pAddr); 79 | if(m_pfnNewClient) 80 | m_pfnNewClient(FreeSlot, m_UserPtr); 81 | return 0; 82 | } 83 | 84 | // reject client 85 | if(!aError[0]) 86 | str_copy(aError, "No free slot available.", sizeof(aError)); 87 | 88 | net_tcp_send(Socket, aError, str_length(aError)); 89 | net_tcp_close(Socket); 90 | 91 | return -1; 92 | } 93 | 94 | int CNetwork::Update() 95 | { 96 | NETSOCKET Socket; 97 | NETADDR Addr; 98 | 99 | if(net_tcp_accept(m_Socket, &Socket, &Addr) > 0) 100 | { 101 | // check if we should just drop the packet 102 | char aBuf[128]; 103 | if(NetBan() && NetBan()->IsBanned(&Addr, aBuf, sizeof(aBuf))) 104 | { 105 | // banned, reply with a message and drop 106 | net_tcp_send(Socket, aBuf, str_length(aBuf)); 107 | net_tcp_close(Socket); 108 | } 109 | else 110 | AcceptClient(Socket, &Addr); 111 | } 112 | 113 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 114 | { 115 | if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ONLINE) 116 | m_aSlots[i].m_Connection.Update(); 117 | if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR) 118 | Drop(i, m_aSlots[i].m_Connection.ErrorString()); 119 | } 120 | 121 | return 0; 122 | } 123 | 124 | int CNetwork::Recv(char *pLine, int MaxLength, int *pClientID) 125 | { 126 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 127 | { 128 | if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ONLINE && m_aSlots[i].m_Connection.Recv(pLine, MaxLength)) 129 | { 130 | if(pClientID) 131 | *pClientID = i; 132 | return 1; 133 | } 134 | } 135 | return 0; 136 | } 137 | 138 | int CNetwork::Send(int ClientID, const char *pLine) 139 | { 140 | if(m_aSlots[ClientID].m_Connection.State() == NET_CONNSTATE_ONLINE) 141 | return m_aSlots[ClientID].m_Connection.Send(pLine); 142 | else 143 | return -1; 144 | } 145 | -------------------------------------------------------------------------------- /server/src/network.h: -------------------------------------------------------------------------------- 1 | #ifndef NETWORK_H 2 | #define NETWORK_H 3 | 4 | enum 5 | { 6 | NET_CONNSTATE_OFFLINE=0, 7 | NET_CONNSTATE_CONNECT=1, 8 | NET_CONNSTATE_PENDING=2, 9 | NET_CONNSTATE_ONLINE=3, 10 | NET_CONNSTATE_ERROR=4, 11 | 12 | NET_MAX_PACKETSIZE = 1400, 13 | NET_MAX_CLIENTS = 512 14 | }; 15 | 16 | typedef int (*NETFUNC_DELCLIENT)(int ClientID, const char* pReason, void *pUser); 17 | typedef int (*NETFUNC_NEWCLIENT)(int ClientID, void *pUser); 18 | 19 | class CNetworkClient 20 | { 21 | private: 22 | int m_State; 23 | 24 | NETADDR m_PeerAddr; 25 | NETSOCKET m_Socket; 26 | 27 | char m_aBuffer[NET_MAX_PACKETSIZE]; 28 | int m_BufferOffset; 29 | 30 | char m_aErrorString[256]; 31 | 32 | bool m_LineEndingDetected; 33 | char m_aLineEnding[3]; 34 | 35 | public: 36 | void Init(NETSOCKET Socket, const NETADDR *pAddr); 37 | void Disconnect(const char *pReason); 38 | 39 | int State() const { return m_State; } 40 | const NETADDR *PeerAddress() const { return &m_PeerAddr; } 41 | const char *ErrorString() const { return m_aErrorString; } 42 | 43 | void Reset(); 44 | int Update(); 45 | int Send(const char *pLine); 46 | int Recv(char *pLine, int MaxLength); 47 | }; 48 | 49 | class CNetwork 50 | { 51 | private: 52 | struct CSlot 53 | { 54 | CNetworkClient m_Connection; 55 | }; 56 | 57 | NETSOCKET m_Socket; 58 | class CNetBan *m_pNetBan; 59 | CSlot m_aSlots[NET_MAX_CLIENTS]; 60 | 61 | NETFUNC_NEWCLIENT m_pfnNewClient; 62 | NETFUNC_DELCLIENT m_pfnDelClient; 63 | void *m_UserPtr; 64 | 65 | public: 66 | void SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser); 67 | 68 | // 69 | bool Open(NETADDR BindAddr, CNetBan *pNetBan); 70 | int Close(); 71 | 72 | // 73 | int Recv(char *pLine, int MaxLength, int *pClientID = 0); 74 | int Send(int ClientID, const char *pLine); 75 | int Update(); 76 | 77 | // 78 | int AcceptClient(NETSOCKET Socket, const NETADDR *pAddr); 79 | int Drop(int ClientID, const char *pReason); 80 | 81 | // status requests 82 | const NETADDR *ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); } 83 | const NETSOCKET *Socket() const { return &m_Socket; } 84 | class CNetBan *NetBan() const { return m_pNetBan; } 85 | }; 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /server/src/network_client.cpp: -------------------------------------------------------------------------------- 1 | #include <system.h> 2 | #include "network.h" 3 | 4 | void CNetworkClient::Reset() 5 | { 6 | m_State = NET_CONNSTATE_OFFLINE; 7 | mem_zero(&m_PeerAddr, sizeof(m_PeerAddr)); 8 | m_aErrorString[0] = 0; 9 | 10 | m_Socket.type = NETTYPE_INVALID; 11 | m_Socket.ipv4sock = -1; 12 | m_Socket.ipv6sock = -1; 13 | m_aBuffer[0] = 0; 14 | m_BufferOffset = 0; 15 | 16 | m_LineEndingDetected = false; 17 | #if defined(CONF_FAMILY_WINDOWS) 18 | m_aLineEnding[0] = '\r'; 19 | m_aLineEnding[1] = '\n'; 20 | m_aLineEnding[2] = 0; 21 | #else 22 | m_aLineEnding[0] = '\n'; 23 | m_aLineEnding[1] = 0; 24 | m_aLineEnding[2] = 0; 25 | #endif 26 | } 27 | 28 | void CNetworkClient::Init(NETSOCKET Socket, const NETADDR *pAddr) 29 | { 30 | Reset(); 31 | 32 | m_Socket = Socket; 33 | net_set_non_blocking(m_Socket); 34 | 35 | m_PeerAddr = *pAddr; 36 | m_State = NET_CONNSTATE_ONLINE; 37 | } 38 | 39 | void CNetworkClient::Disconnect(const char *pReason) 40 | { 41 | if(State() == NET_CONNSTATE_OFFLINE) 42 | return; 43 | 44 | if(pReason && pReason[0]) 45 | Send(pReason); 46 | 47 | net_tcp_close(m_Socket); 48 | 49 | Reset(); 50 | } 51 | 52 | int CNetworkClient::Update() 53 | { 54 | if(State() == NET_CONNSTATE_ONLINE) 55 | { 56 | if((int)(sizeof(m_aBuffer)) <= m_BufferOffset) 57 | { 58 | m_State = NET_CONNSTATE_ERROR; 59 | str_copy(m_aErrorString, "too weak connection (out of buffer)", sizeof(m_aErrorString)); 60 | return -1; 61 | } 62 | 63 | int Bytes = net_tcp_recv(m_Socket, m_aBuffer+m_BufferOffset, (int)(sizeof(m_aBuffer))-m_BufferOffset); 64 | 65 | if(Bytes > 0) 66 | { 67 | m_BufferOffset += Bytes; 68 | } 69 | else if(Bytes < 0) 70 | { 71 | if(net_would_block()) // no data received 72 | return 0; 73 | 74 | m_State = NET_CONNSTATE_ERROR; // error 75 | str_copy(m_aErrorString, "connection failure", sizeof(m_aErrorString)); 76 | return -1; 77 | } 78 | else 79 | { 80 | m_State = NET_CONNSTATE_ERROR; 81 | str_copy(m_aErrorString, "remote end closed the connection", sizeof(m_aErrorString)); 82 | return -1; 83 | } 84 | } 85 | 86 | return 0; 87 | } 88 | 89 | int CNetworkClient::Recv(char *pLine, int MaxLength) 90 | { 91 | if(State() == NET_CONNSTATE_ONLINE) 92 | { 93 | if(m_BufferOffset) 94 | { 95 | // find message start 96 | int StartOffset = 0; 97 | while(m_aBuffer[StartOffset] == '\r' || m_aBuffer[StartOffset] == '\n') 98 | { 99 | // detect clients line ending format 100 | if(!m_LineEndingDetected) 101 | { 102 | m_aLineEnding[0] = m_aBuffer[StartOffset]; 103 | if(StartOffset+1 < m_BufferOffset && (m_aBuffer[StartOffset+1] == '\r' || m_aBuffer[StartOffset+1] == '\n') && 104 | m_aBuffer[StartOffset] != m_aBuffer[StartOffset+1]) 105 | m_aLineEnding[1] = m_aBuffer[StartOffset+1]; 106 | m_LineEndingDetected = true; 107 | } 108 | 109 | if(++StartOffset >= m_BufferOffset) 110 | { 111 | m_BufferOffset = 0; 112 | return 0; 113 | } 114 | } 115 | 116 | // find message end 117 | int EndOffset = StartOffset; 118 | while(m_aBuffer[EndOffset] != '\r' && m_aBuffer[EndOffset] != '\n') 119 | { 120 | if(++EndOffset >= m_BufferOffset) 121 | { 122 | if(StartOffset > 0) 123 | { 124 | mem_move(m_aBuffer, m_aBuffer+StartOffset, m_BufferOffset-StartOffset); 125 | m_BufferOffset -= StartOffset; 126 | } 127 | return 0; 128 | } 129 | } 130 | 131 | // extract message and update buffer 132 | if(MaxLength-1 < EndOffset-StartOffset) 133 | { 134 | if(StartOffset > 0) 135 | { 136 | mem_move(m_aBuffer, m_aBuffer+StartOffset, m_BufferOffset-StartOffset); 137 | m_BufferOffset -= StartOffset; 138 | } 139 | return 0; 140 | } 141 | mem_copy(pLine, m_aBuffer+StartOffset, EndOffset-StartOffset); 142 | pLine[EndOffset-StartOffset] = 0; 143 | str_sanitize_cc(pLine); 144 | mem_move(m_aBuffer, m_aBuffer+EndOffset, m_BufferOffset-EndOffset); 145 | m_BufferOffset -= EndOffset; 146 | return 1; 147 | } 148 | } 149 | return 0; 150 | } 151 | 152 | int CNetworkClient::Send(const char *pLine) 153 | { 154 | if(State() != NET_CONNSTATE_ONLINE) 155 | return -1; 156 | 157 | char aBuf[1024]; 158 | str_copy(aBuf, pLine, (int)(sizeof(aBuf))-2); 159 | int Length = str_length(aBuf); 160 | aBuf[Length] = m_aLineEnding[0]; 161 | aBuf[Length+1] = m_aLineEnding[1]; 162 | aBuf[Length+2] = m_aLineEnding[2]; 163 | Length += 3; 164 | const char *pData = aBuf; 165 | 166 | while(1) 167 | { 168 | int Send = net_tcp_send(m_Socket, pData, Length); 169 | if(Send < 0) 170 | { 171 | m_State = NET_CONNSTATE_ERROR; 172 | str_copy(m_aErrorString, "failed to send packet", sizeof(m_aErrorString)); 173 | return -1; 174 | } 175 | 176 | if(Send >= Length) 177 | break; 178 | 179 | pData += Send; 180 | Length -= Send; 181 | } 182 | 183 | return 0; 184 | } 185 | -------------------------------------------------------------------------------- /server/src/server.cpp: -------------------------------------------------------------------------------- 1 | #include <system.h> 2 | #include "netban.h" 3 | #include "network.h" 4 | #include "main.h" 5 | #include "server.h" 6 | 7 | int CServer::NewClientCallback(int ClientID, void *pUser) 8 | { 9 | CServer *pThis = (CServer *)pUser; 10 | 11 | char aAddrStr[NETADDR_MAXSTRSIZE]; 12 | net_addr_str(pThis->m_Network.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true); 13 | if(pThis->Main()->Config()->m_Verbose) 14 | dbg_msg("server", "Connection accepted. ncid=%d addr=%s'", ClientID, aAddrStr); 15 | 16 | pThis->m_aClients[ClientID].m_State = CClient::STATE_CONNECTED; 17 | pThis->m_aClients[ClientID].m_TimeConnected = time_get(); 18 | pThis->m_Network.Send(ClientID, "Authentication required:"); 19 | 20 | return 0; 21 | } 22 | 23 | int CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser) 24 | { 25 | CServer *pThis = (CServer *)pUser; 26 | 27 | char aAddrStr[NETADDR_MAXSTRSIZE]; 28 | net_addr_str(pThis->m_Network.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true); 29 | if(pThis->Main()->Config()->m_Verbose) 30 | dbg_msg("server", "Client dropped. ncid=%d addr=%s reason='%s'", ClientID, aAddrStr, pReason); 31 | 32 | if(pThis->m_aClients[ClientID].m_State == CClient::STATE_AUTHED) 33 | pThis->Main()->OnDelClient(ClientID); 34 | pThis->m_aClients[ClientID].m_State = CClient::STATE_EMPTY; 35 | 36 | return 0; 37 | } 38 | 39 | int CServer::Init(CMain *pMain, const char *Bind, int Port) 40 | { 41 | m_pMain = pMain; 42 | m_NetBan.Init(); 43 | 44 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 45 | m_aClients[i].m_State = CClient::STATE_EMPTY; 46 | 47 | m_Ready = false; 48 | 49 | if(Port == 0) 50 | { 51 | dbg_msg("server", "Will not bind to port 0."); 52 | return 1; 53 | } 54 | 55 | NETADDR BindAddr; 56 | if(Bind[0] && net_host_lookup(Bind, &BindAddr, NETTYPE_ALL) == 0) 57 | { 58 | // got bindaddr 59 | BindAddr.type = NETTYPE_ALL; 60 | BindAddr.port = Port; 61 | } 62 | else 63 | { 64 | mem_zero(&BindAddr, sizeof(BindAddr)); 65 | BindAddr.type = NETTYPE_ALL; 66 | BindAddr.port = Port; 67 | } 68 | 69 | if(m_Network.Open(BindAddr, &m_NetBan)) 70 | { 71 | m_Network.SetCallbacks(NewClientCallback, DelClientCallback, this); 72 | m_Ready = true; 73 | dbg_msg("server", "Bound to %s:%d", Bind, Port); 74 | return 0; 75 | } 76 | else 77 | dbg_msg("server", "Couldn't open socket. Port (%d) might already be in use.", Port); 78 | 79 | return 1; 80 | } 81 | 82 | void CServer::Update() 83 | { 84 | if(!m_Ready) 85 | return; 86 | 87 | m_NetBan.Update(); 88 | m_Network.Update(); 89 | 90 | char aBuf[NET_MAX_PACKETSIZE]; 91 | int ClientID; 92 | 93 | while(m_Network.Recv(aBuf, (int)(sizeof(aBuf))-1, &ClientID)) 94 | { 95 | dbg_assert(m_aClients[ClientID].m_State != CClient::STATE_EMPTY, "Got message from empty slot."); 96 | if(m_aClients[ClientID].m_State == CClient::STATE_CONNECTED) 97 | { 98 | int ID = -1; 99 | char aUsername[128] = {0}; 100 | char aPassword[128] = {0}; 101 | const char *pTmp; 102 | 103 | if(!(pTmp = str_find(aBuf, ":")) 104 | || (unsigned)(pTmp - aBuf) > sizeof(aUsername) || (unsigned)(str_length(pTmp) - 1) > sizeof(aPassword)) 105 | { 106 | m_Network.NetBan()->BanAddr(m_Network.ClientAddr(ClientID), 60, "You're an idiot, go away."); 107 | m_Network.Drop(ClientID, "Fuck off."); 108 | return; 109 | } 110 | 111 | str_copy(aUsername, aBuf, pTmp - aBuf + 1); 112 | str_copy(aPassword, pTmp + 1, sizeof(aPassword)); 113 | if(!*aUsername || !*aPassword) 114 | { 115 | m_Network.NetBan()->BanAddr(m_Network.ClientAddr(ClientID), 60, "You're an idiot, go away."); 116 | m_Network.Drop(ClientID, "Username and password must not be blank."); 117 | return; 118 | } 119 | 120 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 121 | { 122 | if(!Main()->Client(i)->m_Active) 123 | continue; 124 | 125 | if(str_comp(Main()->Client(i)->m_aUsername, aUsername) == 0 && str_comp(Main()->Client(i)->m_aPassword, aPassword) == 0) 126 | ID = i; 127 | } 128 | 129 | if(ID == -1) 130 | { 131 | m_Network.NetBan()->BanAddr(m_Network.ClientAddr(ClientID), 60, "Wrong username and/or password."); 132 | m_Network.Drop(ClientID, "Wrong username and/or password."); 133 | } 134 | else if(Main()->Client(ID)->m_ClientNetID != -1) 135 | { 136 | m_Network.Drop(ClientID, "Only one connection per user allowed."); 137 | } 138 | else 139 | { 140 | m_aClients[ClientID].m_State = CClient::STATE_AUTHED; 141 | m_aClients[ClientID].m_LastReceived = time_get(); 142 | m_Network.Send(ClientID, "Authentication successful. Access granted."); 143 | 144 | if(m_Network.ClientAddr(ClientID)->type == NETTYPE_IPV4) 145 | m_Network.Send(ClientID, "You are connecting via: IPv4"); 146 | else if(m_Network.ClientAddr(ClientID)->type == NETTYPE_IPV6) 147 | m_Network.Send(ClientID, "You are connecting via: IPv6"); 148 | 149 | if(Main()->Config()->m_Verbose) 150 | dbg_msg("server", "ncid=%d authed", ClientID); 151 | Main()->OnNewClient(ClientID, ID); 152 | } 153 | } 154 | else if(m_aClients[ClientID].m_State == CClient::STATE_AUTHED) 155 | { 156 | m_aClients[ClientID].m_LastReceived = time_get(); 157 | if(Main()->Config()->m_Verbose) 158 | dbg_msg("server", "ncid=%d cmd='%s'", ClientID, aBuf); 159 | 160 | if(str_comp(aBuf, "logout") == 0) 161 | m_Network.Drop(ClientID, "Logout. Bye Bye ~"); 162 | else 163 | Main()->HandleMessage(ClientID, aBuf); 164 | } 165 | } 166 | 167 | for(int i = 0; i < NET_MAX_CLIENTS; ++i) 168 | { 169 | if(m_aClients[i].m_State == CClient::STATE_CONNECTED && 170 | time_get() > m_aClients[i].m_TimeConnected + 5 * time_freq()) 171 | { 172 | m_Network.NetBan()->BanAddr(m_Network.ClientAddr(i), 30, "Authentication timeout."); 173 | m_Network.Drop(i, "Authentication timeout."); 174 | } 175 | else if(m_aClients[i].m_State == CClient::STATE_AUTHED && 176 | time_get() > m_aClients[i].m_LastReceived + 15 * time_freq()) 177 | m_Network.Drop(i, "Timeout."); 178 | } 179 | } 180 | 181 | void CServer::Send(int ClientID, const char *pLine) 182 | { 183 | if(!m_Ready) 184 | return; 185 | 186 | if(ClientID == -1) 187 | { 188 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 189 | { 190 | if(m_aClients[i].m_State == CClient::STATE_AUTHED) 191 | m_Network.Send(i, pLine); 192 | } 193 | } 194 | else if(ClientID >= 0 && ClientID < NET_MAX_CLIENTS && m_aClients[ClientID].m_State == CClient::STATE_AUTHED) 195 | m_Network.Send(ClientID, pLine); 196 | } 197 | 198 | void CServer::Shutdown() 199 | { 200 | if(!m_Ready) 201 | return; 202 | 203 | m_Network.Close(); 204 | } 205 | -------------------------------------------------------------------------------- /server/src/server.h: -------------------------------------------------------------------------------- 1 | #ifndef SERVER_H 2 | #define SERVER_H 3 | 4 | #include "netban.h" 5 | #include "network.h" 6 | 7 | class CServer 8 | { 9 | class CClient 10 | { 11 | public: 12 | enum 13 | { 14 | STATE_EMPTY=0, 15 | STATE_CONNECTED, 16 | STATE_AUTHED, 17 | }; 18 | 19 | int m_State; 20 | int64 m_TimeConnected; 21 | int64 m_LastReceived; 22 | }; 23 | CClient m_aClients[NET_MAX_CLIENTS]; 24 | 25 | CNetwork m_Network; 26 | CNetBan m_NetBan; 27 | 28 | class CMain *m_pMain; 29 | 30 | bool m_Ready; 31 | 32 | static int NewClientCallback(int ClientID, void *pUser); 33 | static int DelClientCallback(int ClientID, const char *pReason, void *pUser); 34 | 35 | public: 36 | int Init(CMain *pMain, const char *Bind, int Port); 37 | void Update(); 38 | void Send(int ClientID, const char *pLine); 39 | void Shutdown(); 40 | 41 | CNetwork *Network() { return &m_Network; } 42 | CNetBan *NetBan() { return &m_NetBan; } 43 | CMain *Main() { return m_pMain; } 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /service/server_status_client_centos: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # chkconfig: 2345 90 10 3 | # description: ServerStatus-Client 4 | 5 | ### BEGIN INIT INFO 6 | # Provides: ServerStatus-Client 7 | # Required-Start: $network $syslog 8 | # Required-Stop: $network 9 | # Default-Start: 2 3 4 5 10 | # Default-Stop: 0 1 6 11 | # Short-Description: Server status monitoring 12 | # Description: Start or stop the ServerStatus Client server 13 | ### END INIT INFO 14 | 15 | NAME="ServerStatus Client" 16 | NAME_BIN="status-client.py" 17 | Info_font_prefix="\033[32m" && Error_font_prefix="\033[31m" && Font_suffix="\033[0m" 18 | if [[ -e "/usr/local/ServerStatus/client/status-client.py" ]]; then 19 | BIN="/usr/local/ServerStatus/client/status-client.py" 20 | elif [[ -e "/usr/local/ServerStatus/status-client.py" ]]; then 21 | BIN="/usr/local/ServerStatus/status-client.py" 22 | else 23 | echo -e "${Error_font_prefix}[错误]${Font_suffix} 客户端文件($NAME_BIN)找不到 !" && exit 1 24 | fi 25 | RETVAL=0 26 | 27 | check_running(){ 28 | PID=$(pgrep -f "${NAME_BIN}") 29 | if [[ -n ${PID} ]]; then 30 | return 0 31 | else 32 | return 1 33 | fi 34 | } 35 | do_start(){ 36 | if check_running; then 37 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME (PID ${PID}) 正在运行..." && exit 0 38 | else 39 | ulimit -n 51200 >/dev/null 2>&1 40 | nohup python -u "$BIN" > /tmp/serverstatus_client.log 2>&1 & 41 | sleep 2s 42 | if check_running; then 43 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 启动成功 !" 44 | else 45 | echo -e "${Error_font_prefix}[错误]${Font_suffix} $NAME 启动失败 !" 46 | fi 47 | fi 48 | } 49 | do_stop(){ 50 | if check_running; then 51 | kill -9 "${PID}" 52 | RETVAL=$? 53 | if [[ $RETVAL -eq 0 ]]; then 54 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 停止成功 !" 55 | else 56 | echo -e "${Error_font_prefix}[错误]${Font_suffix} $NAME 停止失败 !" 57 | fi 58 | else 59 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 未运行" 60 | RETVAL=1 61 | fi 62 | } 63 | do_status(){ 64 | if check_running; then 65 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME (PID ${PID}) 正在运行..." 66 | else 67 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 未运行 !" 68 | RETVAL=1 69 | fi 70 | } 71 | do_restart(){ 72 | do_stop 73 | do_start 74 | } 75 | case "$1" in 76 | start|stop|restart|status) 77 | do_"$1" 78 | ;; 79 | *) 80 | echo -e "使用方法: $0 { start | stop | restart | status }" 81 | RETVAL=1 82 | ;; 83 | esac 84 | exit $RETVAL 85 | -------------------------------------------------------------------------------- /service/server_status_client_debian: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### BEGIN INIT INFO 4 | # Provides: ServerStatus-Client 5 | # Required-Start: $network $local_fs $remote_fs 6 | # Required-Stop: $network $local_fs $remote_fs 7 | # Default-Start: 2 3 4 5 8 | # Default-Stop: 0 1 6 9 | # Short-Description: Server status monitoring 10 | # Description: Start or stop the ServerStatus Client server 11 | ### END INIT INFO 12 | 13 | NAME="ServerStatus Client" 14 | NAME_BIN="status-client.py" 15 | Info_font_prefix="\033[32m" && Error_font_prefix="\033[31m" && Font_suffix="\033[0m" 16 | if [[ -e "/usr/local/ServerStatus/client/status-client.py" ]]; then 17 | BIN="/usr/local/ServerStatus/client/status-client.py" 18 | elif [[ -e "/usr/local/ServerStatus/status-client.py" ]]; then 19 | BIN="/usr/local/ServerStatus/status-client.py" 20 | else 21 | echo -e "${Error_font_prefix}[错误]${Font_suffix} 客户端文件($NAME_BIN)找不到 !" && exit 1 22 | fi 23 | RETVAL=0 24 | 25 | check_running(){ 26 | PID=$(pgrep -f "${NAME_BIN}") 27 | if [[ -n ${PID} ]]; then 28 | return 0 29 | else 30 | return 1 31 | fi 32 | } 33 | do_start(){ 34 | if check_running; then 35 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME (PID ${PID}) 正在运行..." && exit 0 36 | else 37 | ulimit -n 51200 >/dev/null 2>&1 38 | nohup python -u "$BIN" > /tmp/serverstatus_client.log 2>&1 & 39 | sleep 2s 40 | if check_running; then 41 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 启动成功 !" 42 | else 43 | echo -e "${Error_font_prefix}[错误]${Font_suffix} $NAME 启动失败 !" 44 | fi 45 | fi 46 | } 47 | do_stop(){ 48 | if check_running; then 49 | kill -9 "${PID}" 50 | RETVAL=$? 51 | if [[ $RETVAL -eq 0 ]]; then 52 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 停止成功 !" 53 | else 54 | echo -e "${Error_font_prefix}[错误]${Font_suffix}$NAME 停止失败 !" 55 | fi 56 | else 57 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 未运行 !" 58 | RETVAL=1 59 | fi 60 | } 61 | do_status(){ 62 | if check_running; then 63 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME (PID ${PID}) 正在运行..." 64 | else 65 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 未运行 !" 66 | RETVAL=1 67 | fi 68 | } 69 | do_restart(){ 70 | do_stop 71 | do_start 72 | } 73 | case "$1" in 74 | start|stop|restart|status) 75 | do_"$1" 76 | ;; 77 | *) 78 | echo "使用方法: $0 { start | stop | restart | status }" 79 | RETVAL=1 80 | ;; 81 | esac 82 | exit $RETVAL 83 | -------------------------------------------------------------------------------- /service/server_status_server_centos: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # chkconfig: 2345 90 10 3 | # description: ServerStatus-Server 4 | 5 | ### BEGIN INIT INFO 6 | # Provides: ServerStatus-Server 7 | # Required-Start: $network $syslog 8 | # Required-Stop: $network 9 | # Default-Start: 2 3 4 5 10 | # Default-Stop: 0 1 6 11 | # Short-Description: Server status monitoring 12 | # Description: Start or stop the ServerStatus Server server 13 | ### END INIT INFO 14 | 15 | NAME="ServerStatus Server" 16 | NAME_BIN="sergate" 17 | SERVER_BIN="/usr/local/ServerStatus/server" 18 | WEB_BIN="/usr/local/ServerStatus/web" 19 | CONF="/usr/local/ServerStatus/server/config.json" 20 | CONF1="/usr/local/ServerStatus/server/config.conf" 21 | Info_font_prefix="\033[32m" && Error_font_prefix="\033[31m" && Font_suffix="\033[0m" 22 | RETVAL=0 23 | 24 | check_running(){ 25 | PID=$(pgrep -f "${NAME_BIN}") 26 | if [[ -n ${PID} ]]; then 27 | return 0 28 | else 29 | return 1 30 | fi 31 | } 32 | Read_config(){ 33 | if [[ -e "${CONF1}" ]]; then 34 | port="$(grep "PORT = " "${CONF1}" | awk '{print $3}')" 35 | else 36 | port="35601" 37 | fi 38 | } 39 | do_start(){ 40 | if check_running; then 41 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME (PID ${PID}) 正在运行..." && exit 0 42 | else 43 | Read_config 44 | cd "${SERVER_BIN}" || return 45 | ulimit -n 51200 >/dev/null 2>&1 46 | nohup "./$NAME_BIN" --config="$CONF" --web-dir="$WEB_BIN" --port=${port} > /tmp/serverstatus_server.log 2>&1 & 47 | sleep 2s 48 | if check_running; then 49 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 启动成功[监听端口:${port}] !" 50 | else 51 | echo -e "${Error_font_prefix}[错误]${Font_suffix} $NAME 启动失败 !" 52 | fi 53 | fi 54 | } 55 | do_stop(){ 56 | if check_running; then 57 | kill -9 "${PID}" 58 | RETVAL=$? 59 | if [[ $RETVAL -eq 0 ]]; then 60 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 停止成功 !" 61 | else 62 | echo -e "${Error_font_prefix}[错误]${Font_suffix} $NAME 停止失败 !" 63 | fi 64 | else 65 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 未运行" 66 | RETVAL=1 67 | fi 68 | } 69 | do_status(){ 70 | if check_running; then 71 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME (PID ${PID}) 正在运行..." 72 | else 73 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 未运行 !" 74 | RETVAL=1 75 | fi 76 | } 77 | do_restart(){ 78 | do_stop 79 | do_start 80 | } 81 | case "$1" in 82 | start|stop|restart|status) 83 | do_"$1" 84 | ;; 85 | *) 86 | echo -e "使用方法: $0 { start | stop | restart | status }" 87 | RETVAL=1 88 | ;; 89 | esac 90 | exit $RETVAL -------------------------------------------------------------------------------- /service/server_status_server_debian: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### BEGIN INIT INFO 4 | # Provides: ServerStatus-Server 5 | # Required-Start: $network $local_fs $remote_fs 6 | # Required-Stop: $network $local_fs $remote_fs 7 | # Default-Start: 2 3 4 5 8 | # Default-Stop: 0 1 6 9 | # Short-Description: Server status monitoring 10 | # Description: Start or stop the ServerStatus Server server 11 | ### END INIT INFO 12 | 13 | NAME="ServerStatus Server" 14 | NAME_BIN="sergate" 15 | SERVER_BIN="/usr/local/ServerStatus/server" 16 | WEB_BIN="/usr/local/ServerStatus/web" 17 | CONF="/usr/local/ServerStatus/server/config.json" 18 | CONF1="/usr/local/ServerStatus/server/config.conf" 19 | Info_font_prefix="\033[32m" && Error_font_prefix="\033[31m" && Font_suffix="\033[0m" 20 | RETVAL=0 21 | 22 | check_running(){ 23 | PID=$(pgrep -f "${NAME_BIN}") 24 | if [[ -n ${PID} ]]; then 25 | return 0 26 | else 27 | return 1 28 | fi 29 | } 30 | Read_config(){ 31 | if [[ -e "${CONF1}" ]]; then 32 | port="$(grep "PORT = " "${CONF1}" | awk '{print $3}')" 33 | else 34 | port="35601" 35 | fi 36 | } 37 | do_start(){ 38 | if check_running; then 39 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME (PID ${PID}) 正在运行..." && exit 0 40 | else 41 | Read_config 42 | cd "${SERVER_BIN}" || return 43 | ulimit -n 51200 >/dev/null 2>&1 44 | nohup "./$NAME_BIN" --config="$CONF" --web-dir="$WEB_BIN" --port=${port} > /tmp/serverstatus_server.log 2>&1 & 45 | sleep 2s 46 | if check_running; then 47 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 启动成功[监听端口:${port}] !" 48 | else 49 | echo -e "${Error_font_prefix}[错误]${Font_suffix} $NAME 启动失败 !" 50 | fi 51 | fi 52 | } 53 | do_stop(){ 54 | if check_running; then 55 | kill -9 "${PID}" 56 | RETVAL=$? 57 | if [[ $RETVAL -eq 0 ]]; then 58 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 停止成功 !" 59 | else 60 | echo -e "${Error_font_prefix}[错误]${Font_suffix}$NAME 停止失败 !" 61 | fi 62 | else 63 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 未运行 !" 64 | RETVAL=1 65 | fi 66 | } 67 | do_status(){ 68 | if check_running; then 69 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME (PID ${PID}) 正在运行..." 70 | else 71 | echo -e "${Info_font_prefix}[信息]${Font_suffix} $NAME 未运行 !" 72 | RETVAL=1 73 | fi 74 | } 75 | do_restart(){ 76 | do_stop 77 | do_start 78 | } 79 | case "$1" in 80 | start|stop|restart|status) 81 | do_"$1" 82 | ;; 83 | *) 84 | echo "使用方法: $0 { start | stop | restart | status }" 85 | RETVAL=1 86 | ;; 87 | esac 88 | exit $RETVAL -------------------------------------------------------------------------------- /service/status-client.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=ServerStatus-Client 3 | Documentation=https://github.com/cokemine/ServerStatus-Hotaru 4 | After=network.target 5 | [Service] 6 | ExecStart=/usr/bin/python3 /usr/local/ServerStatus/client/status-client.py 7 | ExecReload=/bin/kill -HUP $MAINPID 8 | Restart=on-failure 9 | [Install] 10 | WantedBy=multi-user.target 11 | -------------------------------------------------------------------------------- /service/status-server.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=ServerStatus-Server 3 | Documentation=https://github.com/cokemine/ServerStatus-Hotaru 4 | After=network.target 5 | [Service] 6 | EnvironmentFile=/usr/local/ServerStatus/server/config.conf 7 | ExecStart=/usr/local/ServerStatus/server/sergate --config=/usr/local/ServerStatus/server/config.json --web-dir=/usr/local/ServerStatus/web --port $PORT 8 | ExecReload=/bin/kill -HUP $MAINPID 9 | Restart=on-failure 10 | [Install] 11 | WantedBy=multi-user.target 12 | --------------------------------------------------------------------------------