The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .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 | ![](https://i.imgur.com/utfcHPV.png)
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 | 


--------------------------------------------------------------------------------