├── LICENSE ├── README.md ├── clients ├── client-linux.py └── status-client.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 └── web ├── css ├── bootstrap-theme.min.css ├── bootstrap.min.css └── light.css ├── favicon.ico ├── img └── light.png ├── index.html ├── js ├── bootstrap.min.js ├── jquery-1.10.2.min.js └── serverstatus.js ├── json └── .gitignore └── robots.txt /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Toyo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ServerStatus-Toyo: 2 | 3 | ![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg) 4 | 5 | * ServerStatus-Toyo版是一个酷炫高逼格的云探针、云监控、服务器云监控、多服务器探针~,该云监控(云探针)是ServerStatus( https://github.com/tenyue/ServerStatus )项目的优化/修改版。 6 | * 在线演示:https://tz.toyoo.pw 7 | * 我的博客:https://doub.io/shell-jc3/ 8 | 9 | # 目录介绍: 10 | 11 | * clients 客户端文件 12 | * server 服务端文件 13 | * web 网站文件 14 | 15 | # 更新说明: 16 | 17 | * 2018.08.21, 修改新样式,效果见 https://tz.toyoo.pw 18 | * 2017.10.12, 负载Load 优化,并且支持CentOS6系统 19 | * 2017.10.10, 修改负载 Load 的值为:当前服务器上链接SSR等软件的IP总数(只要软件监听IPv6那么就能统计,例如SSH) 20 | * 2017.04.30, 优化手机显示式样 21 | * 2017.04.29, 去除主机名设定 22 | * 2017.04.27, 增加一键部署脚本 23 | 24 | # 安装教程: 25 | 26 | 执行下面的代码下载并运行脚本。 27 | ``` bash 28 | wget -N --no-check-certificate https://raw.githubusercontent.com/ToyoDAdoubiBackup/doubi/master/status.sh && chmod +x status.sh 29 | ``` 30 | 下载脚本后,根据需要安装客户端或者服务端: 31 | ``` bash 32 | # 显示客户端管理菜单 33 | bash status.sh c 34 | 35 | # 显示服务端管理菜单 36 | bash status.sh s 37 | ``` 38 | 运行脚本后会出现脚本操作菜单,选择并输入` 1 `就会开始安装。 39 | 40 | 一开始会提示你输入 网站服务器的域名和端口,如果没有域名可以直接回车代表使用` 本机IP:8888` 41 | 42 | ## 简单步骤: 43 | 44 | 首先安装服务端,安装过程中会提示: 45 | 46 | ``` bash 47 | 是否由脚本自动配置HTTP服务(服务端的在线监控网站)[Y/n] 48 | 49 | # 如果你不懂,那就直接回车,如果你想用其他的HTTP服务自己配置,那么请输入 n 并回车。 50 | # 注意,当你曾经安装过 服务端,同时没有卸载Caddy(HTTP服务),那么重新安装服务端的时候,请输入 n 并回车。 51 | ``` 52 | 53 | 然后 添加或修改 初始示例的节点配置,注意用户名每个节点配置都不能重复,其他的参数都无所谓了。 54 | 55 | 然后安装客户端,根据提示填写 服务端的IP 和前面添加/修改 对应的 节点用户名和密码(用于和服务端验证),然后启动就好了,有问题请贴出 详细步骤+日志(如果有)联系我。 56 | 57 | # 使用说明: 58 | 59 | 进入下载脚本的目录并运行脚本: 60 | 61 | ``` bash 62 | # 客户端管理菜单 63 | ./status.sh c 64 | # 服务端管理菜单 65 | ./status.sh s 66 | ``` 67 | 68 | 然后选择你要执行的选项即可。 69 | 70 | ``` bash 71 | ServerStatus 一键安装管理脚本 [vx.x.x] 72 | -- Toyo | doub.io/shell-jc3 -- 73 | 74 | 0. 升级脚本 75 | ———————————— 76 | 1. 安装 服务端 77 | 2. 卸载 服务端 78 | ———————————— 79 | 3. 启动 服务端 80 | 4. 停止 服务端 81 | 5. 重启 服务端 82 | ———————————— 83 | 6. 设置 服务端配置 84 | 7. 查看 服务端信息 85 | 8. 查看 服务端日志 86 | ———————————— 87 | 9. 切换为 客户端菜单 88 | 89 | 当前状态: 服务端 已安装 并 已启动 90 | 91 | 请输入数字 [0-9]: 92 | ``` 93 | # 其他操作 94 | 95 | ### 客户端: 96 | 97 | 启动:service status-client start 98 | 99 | 停止:service status-client stop 100 | 101 | 重启:service status-client restart 102 | 103 | 查看状态:service status-client status 104 | 105 | ### 服务端: 106 | 107 | 启动:service status-server start 108 | 109 | 停止:service status-server stop 110 | 111 | 重启:service status-server restart 112 | 113 | 查看状态:service status-server status 114 | 115 | ### Caddy(HTTP服务): 116 | 117 | 启动:service caddy start 118 | 119 | 停止:service caddy stop 120 | 121 | 重启:service caddy restart 122 | 123 | 查看状态:service caddy status 124 | 125 | Caddy配置文件:/usr/local/caddy/caddy 126 | 127 | 默认脚本只能一开始安装的时候设置配置文件,更多的Caddy使用方法,可以参考这些教程:https://doub.io/search/caddy 128 | 129 | —————————————————————————————————————— 130 | 131 | 安装目录:/usr/local/ServerStatus 132 | 133 | 网页文件:/usr/local/ServerStatus/web 134 | 135 | 配置文件:/usr/local/ServerStatus/server/config.json 136 | 137 | 客户端查看日志:tail -f tmp/serverstatus_client.log 138 | 139 | 服务端查看日志:tail -f /tmp/serverstatus_server.log 140 | 141 | # 其他说明 142 | 143 | 网络实时流量单位为:G=GB/s,M=MB/s,K=KB/s 144 | 145 | 服务器总流量单位为:T=TB,G=GB,M=MB,K=KB 146 | 147 | ### CentOS7系统 负载显示异常的问题 148 | 149 | CentOS7系统 默认可能没有安装 netstat 依赖,所以会造成IP检测(负载)出错,手动安装即可: 150 | `yum install net-tools -y ` 151 | 152 | # 相关开源项目,感谢: 153 | 154 | * ServerStatus:https://github.com/BotoX/ServerStatus 155 | * mojeda: https://github.com/mojeda 156 | * mojeda's ServerStatus: https://github.com/mojeda/ServerStatus 157 | * BlueVM's project: http://www.lowendtalk.com/discussion/comment/169690#Comment_169690 158 | -------------------------------------------------------------------------------- /clients/client-linux.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Update by : https://github.com/tenyue/ServerStatus 3 | # 支持Python版本:2.6 to 3.5 4 | # 支持操作系统: Linux, OSX, FreeBSD, OpenBSD and NetBSD, both 32-bit and 64-bit architectures 5 | 6 | 7 | SERVER = "127.0.0.1" 8 | PORT = 35601 9 | USER = "USER" 10 | PASSWORD = "USER_PASSWORD" 11 | INTERVAL = 1 #更新间隔 12 | 13 | 14 | import socket 15 | import time 16 | import string 17 | import math 18 | import re 19 | import os 20 | import json 21 | import subprocess 22 | import collections 23 | import platform 24 | 25 | def get_uptime(): 26 | f = open('/proc/uptime', 'r') 27 | uptime = f.readline() 28 | f.close() 29 | uptime = uptime.split('.', 2) 30 | time = int(uptime[0]) 31 | return int(time) 32 | 33 | def get_memory(): 34 | re_parser = re.compile(r'^(?P\S*):\s*(?P\d*)\s*kB') 35 | result = dict() 36 | for line in open('/proc/meminfo'): 37 | match = re_parser.match(line) 38 | if not match: 39 | continue; 40 | key, value = match.groups(['key', 'value']) 41 | result[key] = int(value) 42 | 43 | MemTotal = float(result['MemTotal']) 44 | MemFree = float(result['MemFree']) 45 | Cached = float(result['Cached']) 46 | MemUsed = MemTotal - (Cached + MemFree) 47 | SwapTotal = float(result['SwapTotal']) 48 | SwapFree = float(result['SwapFree']) 49 | return int(MemTotal), int(MemUsed), int(SwapTotal), int(SwapFree) 50 | 51 | def get_hdd(): 52 | p = subprocess.check_output(['df', '-Tlm', '--total', '-t', 'ext4', '-t', 'ext3', '-t', 'ext2', '-t', 'reiserfs', '-t', 'jfs', '-t', 'ntfs', '-t', 'fat32', '-t', 'btrfs', '-t', 'fuseblk', '-t', 'zfs', '-t', 'simfs', '-t', 'xfs']).decode("Utf-8") 53 | total = p.splitlines()[-1] 54 | used = total.split()[3] 55 | size = total.split()[2] 56 | return int(size), int(used) 57 | 58 | def get_load(): 59 | system = platform.linux_distribution() 60 | if system[0][:6] == "CentOS": 61 | if system[1][0] == "6": 62 | tmp_load = os.popen("netstat -anp |grep ESTABLISHED |grep tcp |grep '::ffff:' |awk '{print $5}' |awk -F ':' '{print $4}' |sort -u |grep -E -o '([0-9]{1,3}[\.]){3}[0-9]{1,3}' |wc -l").read() 63 | else: 64 | tmp_load = os.popen("netstat -anp |grep ESTABLISHED |grep tcp6 |awk '{print $5}' |awk -F ':' '{print $1}' |sort -u |grep -E -o '([0-9]{1,3}[\.]){3}[0-9]{1,3}' |wc -l").read() 65 | else: 66 | tmp_load = os.popen("netstat -anp |grep ESTABLISHED |grep tcp6 |awk '{print $5}' |awk -F ':' '{print $1}' |sort -u |grep -E -o '([0-9]{1,3}[\.]){3}[0-9]{1,3}' |wc -l").read() 67 | 68 | return float(tmp_load) 69 | #return os.getloadavg()[0] 70 | 71 | def get_time(): 72 | stat_file = file("/proc/stat", "r") 73 | time_list = stat_file.readline().split(' ')[2:6] 74 | stat_file.close() 75 | for i in range(len(time_list)) : 76 | time_list[i] = int(time_list[i]) 77 | return time_list 78 | def delta_time(): 79 | x = get_time() 80 | time.sleep(INTERVAL) 81 | y = get_time() 82 | for i in range(len(x)): 83 | y[i]-=x[i] 84 | return y 85 | def get_cpu(): 86 | t = delta_time() 87 | st = sum(t) 88 | if st == 0: 89 | st = 1 90 | result = 100-(t[len(t)-1]*100.00/st) 91 | return round(result) 92 | 93 | class Traffic: 94 | def __init__(self): 95 | self.rx = collections.deque(maxlen=10) 96 | self.tx = collections.deque(maxlen=10) 97 | def get(self): 98 | f = open('/proc/net/dev', 'r') 99 | net_dev = f.readlines() 100 | f.close() 101 | avgrx = 0; avgtx = 0 102 | 103 | for dev in net_dev[2:]: 104 | dev = dev.split(':') 105 | if dev[0].strip() == "lo" or dev[0].find("tun") > -1: 106 | continue 107 | dev = dev[1].split() 108 | avgrx += int(dev[0]) 109 | avgtx += int(dev[8]) 110 | 111 | self.rx.append(avgrx) 112 | self.tx.append(avgtx) 113 | avgrx = 0; avgtx = 0 114 | 115 | l = len(self.rx) 116 | for x in range(l - 1): 117 | avgrx += self.rx[x+1] - self.rx[x] 118 | avgtx += self.tx[x+1] - self.tx[x] 119 | 120 | avgrx = int(avgrx / l / INTERVAL) 121 | avgtx = int(avgtx / l / INTERVAL) 122 | 123 | return avgrx, avgtx 124 | 125 | def liuliang(): 126 | NET_IN = 0 127 | NET_OUT = 0 128 | with open('/proc/net/dev') as f: 129 | for line in f.readlines(): 130 | netinfo = re.findall('([^\s]+):[\s]{0,}(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)', line) 131 | if netinfo: 132 | if netinfo[0][0] == 'lo' or 'tun' in netinfo[0][0] or netinfo[0][1]=='0' or netinfo[0][9]=='0': 133 | continue 134 | else: 135 | NET_IN += int(netinfo[0][1]) 136 | NET_OUT += int(netinfo[0][9]) 137 | return NET_IN, NET_OUT 138 | 139 | def get_network(ip_version): 140 | if(ip_version == 4): 141 | HOST = "ipv4.google.com" 142 | elif(ip_version == 6): 143 | HOST = "ipv6.google.com" 144 | try: 145 | s = socket.create_connection((HOST, 80), 2) 146 | return True 147 | except: 148 | pass 149 | return False 150 | 151 | if __name__ == '__main__': 152 | socket.setdefaulttimeout(30) 153 | while 1: 154 | try: 155 | print("Connecting...") 156 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 157 | s.connect((SERVER, PORT)) 158 | data = s.recv(1024) 159 | if data.find("Authentication required") > -1: 160 | s.send(USER + ':' + PASSWORD + '\n') 161 | data = s.recv(1024) 162 | if data.find("Authentication successful") < 0: 163 | print(data) 164 | raise socket.error 165 | else: 166 | print(data) 167 | raise socket.error 168 | 169 | print(data) 170 | data = s.recv(1024) 171 | print(data) 172 | 173 | timer = 0 174 | check_ip = 0 175 | if data.find("IPv4") > -1: 176 | check_ip = 6 177 | elif data.find("IPv6") > -1: 178 | check_ip = 4 179 | else: 180 | print(data) 181 | raise socket.error 182 | 183 | traffic = Traffic() 184 | traffic.get() 185 | while 1: 186 | CPU = get_cpu() 187 | NetRx, NetTx = traffic.get() 188 | NET_IN, NET_OUT = liuliang() 189 | Uptime = get_uptime() 190 | Load = get_load() 191 | MemoryTotal, MemoryUsed, SwapTotal, SwapFree = get_memory() 192 | HDDTotal, HDDUsed = get_hdd() 193 | 194 | array = {} 195 | if not timer: 196 | array['online' + str(check_ip)] = get_network(check_ip) 197 | timer = 10 198 | else: 199 | timer -= 1*INTERVAL 200 | 201 | array['uptime'] = Uptime 202 | array['load'] = Load 203 | array['memory_total'] = MemoryTotal 204 | array['memory_used'] = MemoryUsed 205 | array['swap_total'] = SwapTotal 206 | array['swap_used'] = SwapTotal - SwapFree 207 | array['hdd_total'] = HDDTotal 208 | array['hdd_used'] = HDDUsed 209 | array['cpu'] = CPU 210 | array['network_rx'] = NetRx 211 | array['network_tx'] = NetTx 212 | array['network_in'] = NET_IN 213 | array['network_out'] = NET_OUT 214 | 215 | s.send("update " + json.dumps(array) + "\n") 216 | except KeyboardInterrupt: 217 | raise 218 | except socket.error: 219 | print("Disconnected...") 220 | # keep on trying after a disconnect 221 | s.close() 222 | time.sleep(3) 223 | except Exception as e: 224 | print("Caught Exception:", e) 225 | s.close() 226 | time.sleep(3) 227 | -------------------------------------------------------------------------------- /clients/status-client.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | SERVER = "127.0.0.1" 4 | PORT = PORT 5 | USER = "USER" 6 | PASSWORD = "USER_PASSWORD" 7 | INTERVAL = 1 #更新间隔,单位:秒 8 | 9 | 10 | import socket 11 | import time 12 | import string 13 | import math 14 | import re 15 | import os 16 | import json 17 | import subprocess 18 | import collections 19 | import platform 20 | 21 | def get_uptime(): 22 | f = open('/proc/uptime', 'r') 23 | uptime = f.readline() 24 | f.close() 25 | uptime = uptime.split('.', 2) 26 | time = int(uptime[0]) 27 | return int(time) 28 | 29 | def get_memory(): 30 | re_parser = re.compile(r'^(?P\S*):\s*(?P\d*)\s*kB') 31 | result = dict() 32 | for line in open('/proc/meminfo'): 33 | match = re_parser.match(line) 34 | if not match: 35 | continue; 36 | key, value = match.groups(['key', 'value']) 37 | result[key] = int(value) 38 | 39 | MemTotal = float(result['MemTotal']) 40 | MemFree = float(result['MemFree']) 41 | Cached = float(result['Cached']) 42 | MemUsed = MemTotal - (Cached + MemFree) 43 | SwapTotal = float(result['SwapTotal']) 44 | SwapFree = float(result['SwapFree']) 45 | return int(MemTotal), int(MemUsed), int(SwapTotal), int(SwapFree) 46 | 47 | def get_hdd(): 48 | p = subprocess.check_output(['df', '-Tlm', '--total', '-t', 'ext4', '-t', 'ext3', '-t', 'ext2', '-t', 'reiserfs', '-t', 'jfs', '-t', 'ntfs', '-t', 'fat32', '-t', 'btrfs', '-t', 'fuseblk', '-t', 'zfs', '-t', 'simfs', '-t', 'xfs']).decode("Utf-8") 49 | total = p.splitlines()[-1] 50 | used = total.split()[3] 51 | size = total.split()[2] 52 | return int(size), int(used) 53 | 54 | def get_load(): 55 | system = platform.linux_distribution() 56 | if system[0][:6] == "CentOS": 57 | if system[1][0] == "6": 58 | tmp_load = os.popen("netstat -anp |grep ESTABLISHED |grep tcp |grep '::ffff:' |awk '{print $5}' |awk -F ':' '{print $4}' |sort -u |grep -E -o '([0-9]{1,3}[\.]){3}[0-9]{1,3}' |wc -l").read() 59 | else: 60 | tmp_load = os.popen("netstat -anp |grep ESTABLISHED |grep tcp6 |awk '{print $5}' |awk -F ':' '{print $1}' |sort -u |grep -E -o '([0-9]{1,3}[\.]){3}[0-9]{1,3}' |wc -l").read() 61 | else: 62 | tmp_load = os.popen("netstat -anp |grep ESTABLISHED |grep tcp6 |awk '{print $5}' |awk -F ':' '{print $1}' |sort -u |grep -E -o '([0-9]{1,3}[\.]){3}[0-9]{1,3}' |wc -l").read() 63 | 64 | return float(tmp_load) 65 | #return os.getloadavg()[0] 66 | 67 | def get_time(): 68 | stat_file = file("/proc/stat", "r") 69 | time_list = stat_file.readline().split(' ')[2:6] 70 | stat_file.close() 71 | for i in range(len(time_list)) : 72 | time_list[i] = int(time_list[i]) 73 | return time_list 74 | def delta_time(): 75 | x = get_time() 76 | time.sleep(INTERVAL) 77 | y = get_time() 78 | for i in range(len(x)): 79 | y[i]-=x[i] 80 | return y 81 | def get_cpu(): 82 | t = delta_time() 83 | st = sum(t) 84 | if st == 0: 85 | st = 1 86 | result = 100-(t[len(t)-1]*100.00/st) 87 | return round(result) 88 | 89 | class Traffic: 90 | def __init__(self): 91 | self.rx = collections.deque(maxlen=10) 92 | self.tx = collections.deque(maxlen=10) 93 | def get(self): 94 | f = open('/proc/net/dev', 'r') 95 | net_dev = f.readlines() 96 | f.close() 97 | avgrx = 0; avgtx = 0 98 | 99 | for dev in net_dev[2:]: 100 | dev = dev.split(':') 101 | if dev[0].strip() == "lo" or dev[0].find("tun") > -1: 102 | continue 103 | dev = dev[1].split() 104 | avgrx += int(dev[0]) 105 | avgtx += int(dev[8]) 106 | 107 | self.rx.append(avgrx) 108 | self.tx.append(avgtx) 109 | avgrx = 0; avgtx = 0 110 | 111 | l = len(self.rx) 112 | for x in range(l - 1): 113 | avgrx += self.rx[x+1] - self.rx[x] 114 | avgtx += self.tx[x+1] - self.tx[x] 115 | 116 | avgrx = int(avgrx / l / INTERVAL) 117 | avgtx = int(avgtx / l / INTERVAL) 118 | 119 | return avgrx, avgtx 120 | 121 | def liuliang(): 122 | NET_IN = 0 123 | NET_OUT = 0 124 | with open('/proc/net/dev') as f: 125 | for line in f.readlines(): 126 | netinfo = re.findall('([^\s]+):[\s]{0,}(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)', line) 127 | if netinfo: 128 | if netinfo[0][0] == 'lo' or 'tun' in netinfo[0][0] or netinfo[0][1]=='0' or netinfo[0][9]=='0': 129 | continue 130 | else: 131 | NET_IN += int(netinfo[0][1]) 132 | NET_OUT += int(netinfo[0][9]) 133 | return NET_IN, NET_OUT 134 | 135 | def get_network(ip_version): 136 | if(ip_version == 4): 137 | HOST = "ipv4.google.com" 138 | elif(ip_version == 6): 139 | HOST = "ipv6.google.com" 140 | try: 141 | s = socket.create_connection((HOST, 80), 2) 142 | return True 143 | except: 144 | pass 145 | return False 146 | 147 | if __name__ == '__main__': 148 | socket.setdefaulttimeout(30) 149 | while 1: 150 | try: 151 | print("Connecting...") 152 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 153 | s.connect((SERVER, PORT)) 154 | data = s.recv(1024) 155 | if data.find("Authentication required") > -1: 156 | s.send(USER + ':' + PASSWORD + '\n') 157 | data = s.recv(1024) 158 | if data.find("Authentication successful") < 0: 159 | print(data) 160 | raise socket.error 161 | else: 162 | print(data) 163 | raise socket.error 164 | 165 | print(data) 166 | data = s.recv(1024) 167 | print(data) 168 | 169 | timer = 0 170 | check_ip = 0 171 | if data.find("IPv4") > -1: 172 | check_ip = 6 173 | elif data.find("IPv6") > -1: 174 | check_ip = 4 175 | else: 176 | print(data) 177 | raise socket.error 178 | 179 | traffic = Traffic() 180 | traffic.get() 181 | while 1: 182 | CPU = get_cpu() 183 | NetRx, NetTx = traffic.get() 184 | NET_IN, NET_OUT = liuliang() 185 | Uptime = get_uptime() 186 | Load = get_load() 187 | MemoryTotal, MemoryUsed, SwapTotal, SwapFree = get_memory() 188 | HDDTotal, HDDUsed = get_hdd() 189 | 190 | array = {} 191 | if not timer: 192 | array['online' + str(check_ip)] = get_network(check_ip) 193 | timer = 10 194 | else: 195 | timer -= 1*INTERVAL 196 | 197 | array['uptime'] = Uptime 198 | array['load'] = Load 199 | array['memory_total'] = MemoryTotal 200 | array['memory_used'] = MemoryUsed 201 | array['swap_total'] = SwapTotal 202 | array['swap_used'] = SwapTotal - SwapFree 203 | array['hdd_total'] = HDDTotal 204 | array['hdd_used'] = HDDUsed 205 | array['cpu'] = CPU 206 | array['network_rx'] = NetRx 207 | array['network_tx'] = NetTx 208 | array['network_in'] = NET_IN 209 | array['network_out'] = NET_OUT 210 | 211 | s.send("update " + json.dumps(array) + "\n") 212 | except KeyboardInterrupt: 213 | raise 214 | except socket.error: 215 | print("Disconnected...") 216 | # keep on trying after a disconnect 217 | s.close() 218 | time.sleep(3) 219 | except Exception as e: 220 | print("Caught Exception:", e) 221 | s.close() 222 | time.sleep(3) 223 | -------------------------------------------------------------------------------- /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) $< -o $@ 24 | 25 | $(ODIR)/%.o: $(SDIR)/%.cpp 26 | $(CXX) -c $(INC) $(CXXFLAGS) $< -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": "Austria", 10 | "disabled": false 11 | }, 12 | { 13 | "username": "bs01", 14 | "password": "password", 15 | "name": "Backupserver 1", 16 | "type": "Virtual Server", 17 | "host": "No", 18 | "location": "Switzerland", 19 | "disabled": false 20 | }, 21 | { 22 | "username": "hidden", 23 | "password": "password", 24 | "name": "Secret", 25 | "type": "Nothing", 26 | "host": "No", 27 | "location": "Nowhere", 28 | "disabled": true 29 | }, 30 | { 31 | "username": "butt", 32 | "password": "password", 33 | "name": "Butt", 34 | "type": "Cloud Server", 35 | "host": "No", 36 | "location": "Heaven??!", 37 | "disabled": false 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /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 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 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 83 | #else 84 | #include 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 41 | #define json_int_t int64_t 42 | #else 43 | #define json_int_t __int64 44 | #endif 45 | #endif 46 | 47 | #include 48 | 49 | #ifdef __cplusplus 50 | 51 | #include 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/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("="); 272 | } else if (options->type == ARGPARSE_OPT_STRING) { 273 | len += strlen("="); 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, "="); 298 | } else if (options->type == ARGPARSE_OPT_STRING) { 299 | pos += fprintf(stdout, "="); 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 45 | #include 46 | #include 47 | #include 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 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "server.h" 9 | #include "main.h" 10 | 11 | #if defined(CONF_FAMILY_UNIX) 12 | #include 13 | #endif 14 | 15 | #ifndef PRId64 16 | #define PRId64 "I64d" 17 | #endif 18 | 19 | static volatile int gs_Running = 1; 20 | static volatile int gs_ReloadConfig = 0; 21 | 22 | static void ExitFunc(int Signal) 23 | { 24 | printf("[EXIT] Caught signal %d\n", Signal); 25 | gs_Running = 0; 26 | } 27 | 28 | static void ReloadFunc(int Signal) 29 | { 30 | printf("[RELOAD] Caught signal %d\n", Signal); 31 | gs_ReloadConfig = 1; 32 | } 33 | 34 | CConfig::CConfig() 35 | { 36 | // Initialize to default values 37 | m_Verbose = false; // -v, --verbose 38 | str_copy(m_aConfigFile, "config.json", sizeof(m_aConfigFile)); // -c, --config 39 | str_copy(m_aWebDir, "../web/", sizeof(m_aJSONFile)); // -d, --web-dir 40 | str_copy(m_aTemplateFile, "template.html", sizeof(m_aTemplateFile)); 41 | str_copy(m_aJSONFile, "json/stats.json", sizeof(m_aJSONFile)); 42 | str_copy(m_aBindAddr, "", sizeof(m_aBindAddr)); // -b, --bind 43 | m_Port = 35601; // -p, --port 44 | } 45 | 46 | CMain::CMain(CConfig Config) : m_Config(Config) 47 | { 48 | mem_zero(m_aClients, sizeof(m_aClients)); 49 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 50 | m_aClients[i].m_ClientNetID = -1; 51 | } 52 | 53 | CMain::CClient *CMain::ClientNet(int ClientNetID) 54 | { 55 | if(ClientNetID < 0 || ClientNetID >= NET_MAX_CLIENTS) 56 | return 0; 57 | 58 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 59 | { 60 | if(Client(i)->m_ClientNetID == ClientNetID) 61 | return Client(i); 62 | } 63 | 64 | return 0; 65 | } 66 | 67 | int CMain::ClientNetToClient(int ClientNetID) 68 | { 69 | if(ClientNetID < 0 || ClientNetID >= NET_MAX_CLIENTS) 70 | return -1; 71 | 72 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 73 | { 74 | if(Client(i)->m_ClientNetID == ClientNetID) 75 | return i; 76 | } 77 | 78 | return -1; 79 | } 80 | 81 | void CMain::OnNewClient(int ClientNetID, int ClientID) 82 | { 83 | dbg_msg("main", "OnNewClient(ncid=%d, cid=%d)", ClientNetID, ClientID); 84 | Client(ClientID)->m_ClientNetID = ClientNetID; 85 | Client(ClientID)->m_ClientNetType = m_Server.Network()->ClientAddr(ClientNetID)->type; 86 | Client(ClientID)->m_TimeConnected = time_get(); 87 | Client(ClientID)->m_Connected = true; 88 | 89 | if(Client(ClientID)->m_ClientNetType == NETTYPE_IPV4) 90 | Client(ClientID)->m_Stats.m_Online4 = true; 91 | else if(Client(ClientID)->m_ClientNetType == NETTYPE_IPV6) 92 | Client(ClientID)->m_Stats.m_Online6 = true; 93 | } 94 | 95 | void CMain::OnDelClient(int ClientNetID) 96 | { 97 | int ClientID = ClientNetToClient(ClientNetID); 98 | dbg_msg("main", "OnDelClient(ncid=%d, cid=%d)", ClientNetID, ClientID); 99 | if(ClientID >= 0 && ClientID < NET_MAX_CLIENTS) 100 | { 101 | Client(ClientID)->m_Connected = false; 102 | Client(ClientID)->m_ClientNetID = -1; 103 | Client(ClientID)->m_ClientNetType = NETTYPE_INVALID; 104 | mem_zero(&Client(ClientID)->m_Stats, sizeof(CClient::CStats)); 105 | } 106 | } 107 | 108 | int CMain::HandleMessage(int ClientNetID, char *pMessage) 109 | { 110 | CClient *pClient = ClientNet(ClientNetID); 111 | if(!pClient) 112 | return true; 113 | 114 | if(str_comp_num(pMessage, "update", sizeof("update")-1) == 0) 115 | { 116 | char *pData = str_skip_whitespaces(&pMessage[sizeof("update")-1]); 117 | 118 | // parse json data 119 | json_settings JsonSettings; 120 | mem_zero(&JsonSettings, sizeof(JsonSettings)); 121 | char aError[256]; 122 | json_value *pJsonData = json_parse_ex(&JsonSettings, pData, strlen(pData), aError); 123 | if(!pJsonData) 124 | { 125 | dbg_msg("main", "JSON Error: %s", aError); 126 | if(pClient->m_Stats.m_Pong) 127 | m_Server.Network()->Send(ClientNetID, "1"); 128 | return 1; 129 | } 130 | 131 | // extract data 132 | const json_value &rStart = (*pJsonData); 133 | if(rStart["uptime"].type) 134 | pClient->m_Stats.m_Uptime = rStart["uptime"].u.integer; 135 | if(rStart["load"].type) 136 | pClient->m_Stats.m_Load = rStart["load"].u.dbl; 137 | if(rStart["network_rx"].type) 138 | pClient->m_Stats.m_NetworkRx = rStart["network_rx"].u.integer; 139 | if(rStart["network_tx"].type) 140 | pClient->m_Stats.m_NetworkTx = rStart["network_tx"].u.integer; 141 | if(rStart["network_in"].type) 142 | pClient->m_Stats.m_NetworkIN = rStart["network_in"].u.integer; 143 | if(rStart["network_out"].type) 144 | pClient->m_Stats.m_NetworkOUT = rStart["network_out"].u.integer; 145 | if(rStart["memory_total"].type) 146 | pClient->m_Stats.m_MemTotal = rStart["memory_total"].u.integer; 147 | if(rStart["memory_used"].type) 148 | pClient->m_Stats.m_MemUsed = rStart["memory_used"].u.integer; 149 | if(rStart["swap_total"].type) 150 | pClient->m_Stats.m_SwapTotal = rStart["swap_total"].u.integer; 151 | if(rStart["swap_used"].type) 152 | pClient->m_Stats.m_SwapUsed = rStart["swap_used"].u.integer; 153 | if(rStart["hdd_total"].type) 154 | pClient->m_Stats.m_HDDTotal = rStart["hdd_total"].u.integer; 155 | if(rStart["hdd_used"].type) 156 | pClient->m_Stats.m_HDDUsed = rStart["hdd_used"].u.integer; 157 | if(rStart["cpu"].type) 158 | pClient->m_Stats.m_CPU = rStart["cpu"].u.dbl; 159 | if(rStart["online4"].type && pClient->m_ClientNetType == NETTYPE_IPV6) 160 | pClient->m_Stats.m_Online4 = rStart["online4"].u.boolean; 161 | if(rStart["online6"].type && pClient->m_ClientNetType == NETTYPE_IPV4) 162 | pClient->m_Stats.m_Online6 = rStart["online6"].u.boolean; 163 | if(rStart["custom"].type == json_string) 164 | str_copy(pClient->m_Stats.m_aCustom, rStart["custom"].u.string.ptr, sizeof(pClient->m_Stats.m_aCustom)); 165 | 166 | if(m_Config.m_Verbose) 167 | { 168 | if(rStart["online4"].type) 169 | 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", 170 | rStart["online4"].u.boolean ? "true" : "false", 171 | 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); 172 | else if(rStart["online6"].type) 173 | 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", 174 | rStart["online6"].u.boolean ? "true" : "false", 175 | 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); 176 | else 177 | 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", 178 | 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); 179 | } 180 | 181 | // clean up 182 | json_value_free(pJsonData); 183 | 184 | if(pClient->m_Stats.m_Pong) 185 | m_Server.Network()->Send(ClientNetID, "0"); 186 | return 0; 187 | } 188 | else if(str_comp_num(pMessage, "pong", sizeof("pong")-1) == 0) 189 | { 190 | char *pData = str_skip_whitespaces(&pMessage[sizeof("pong")-1]); 191 | 192 | if(!str_comp(pData, "0") || !str_comp(pData, "off")) 193 | pClient->m_Stats.m_Pong = false; 194 | else if(!str_comp(pData, "1") || !str_comp(pData, "on")) 195 | pClient->m_Stats.m_Pong = true; 196 | 197 | return 0; 198 | } 199 | 200 | if(pClient->m_Stats.m_Pong) 201 | m_Server.Network()->Send(ClientNetID, "1"); 202 | 203 | return 1; 204 | } 205 | 206 | void CMain::JSONUpdateThread(void *pUser) 207 | { 208 | CJSONUpdateThreadData *m_pJSONUpdateThreadData = (CJSONUpdateThreadData *)pUser; 209 | CClient *pClients = m_pJSONUpdateThreadData->pClients; 210 | CConfig *pConfig = m_pJSONUpdateThreadData->pConfig; 211 | 212 | while(gs_Running) 213 | { 214 | char aFileBuf[2048*NET_MAX_CLIENTS]; 215 | char *pBuf = aFileBuf; 216 | 217 | str_format(pBuf, sizeof(aFileBuf), "{\n\"servers\": [\n"); 218 | pBuf += strlen(pBuf); 219 | 220 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 221 | { 222 | if(!pClients[i].m_Active || pClients[i].m_Disabled) 223 | continue; 224 | 225 | if(pClients[i].m_Connected) 226 | { 227 | // Uptime 228 | char aUptime[16]; 229 | int Days = pClients[i].m_Stats.m_Uptime/60.0/60.0/24.0; 230 | if(Days > 0) 231 | { 232 | if(Days > 1) 233 | str_format(aUptime, sizeof(aUptime), "%d 天", Days); 234 | else 235 | str_format(aUptime, sizeof(aUptime), "%d 天", Days); 236 | } 237 | else 238 | 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)); 239 | 240 | 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\" },\n", 241 | 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); 242 | pBuf += strlen(pBuf); 243 | } 244 | else 245 | { 246 | str_format(pBuf, sizeof(aFileBuf) - (pBuf - aFileBuf), "{ \"name\": \"%s\", \"type\": \"%s\", \"host\": \"%s\", \"location\": \"%s\", \"online4\": false, \"online6\": false },\n", 247 | pClients[i].m_aName, pClients[i].m_aType, pClients[i].m_aHost, pClients[i].m_aLocation); 248 | pBuf += strlen(pBuf); 249 | } 250 | } 251 | if(!m_pJSONUpdateThreadData->m_ReloadRequired) 252 | str_format(pBuf - 2, sizeof(aFileBuf) - (pBuf - aFileBuf), "\n],\n\"updated\": \"%lld\"\n}", (long long)time(/*ago*/0)); 253 | else 254 | { 255 | str_format(pBuf - 2, sizeof(aFileBuf) - (pBuf - aFileBuf), "\n],\n\"updated\": \"%lld\",\n\"reload\": true\n}", (long long)time(/*ago*/0)); 256 | m_pJSONUpdateThreadData->m_ReloadRequired--; 257 | } 258 | pBuf += strlen(pBuf); 259 | 260 | char aJSONFileTmp[1024]; 261 | str_format(aJSONFileTmp, sizeof(aJSONFileTmp), "%s~", pConfig->m_aJSONFile); 262 | IOHANDLE File = io_open(aJSONFileTmp, IOFLAG_WRITE); 263 | if(!File) 264 | { 265 | dbg_msg("main", "Couldn't open %s", aJSONFileTmp); 266 | exit(1); 267 | } 268 | io_write(File, aFileBuf, (pBuf - aFileBuf)); 269 | io_flush(File); 270 | io_close(File); 271 | fs_rename(aJSONFileTmp, pConfig->m_aJSONFile); 272 | thread_sleep(1000); 273 | } 274 | fs_remove(pConfig->m_aJSONFile); 275 | } 276 | 277 | int CMain::ReadConfig() 278 | { 279 | // read and parse config 280 | IOHANDLE File = io_open(m_Config.m_aConfigFile, IOFLAG_READ); 281 | if(!File) 282 | { 283 | dbg_msg("main", "Couldn't open %s", m_Config.m_aConfigFile); 284 | return 1; 285 | } 286 | int FileSize = (int)io_length(File); 287 | char *pFileData = (char *)mem_alloc(FileSize + 1, 1); 288 | 289 | io_read(File, pFileData, FileSize); 290 | pFileData[FileSize] = 0; 291 | io_close(File); 292 | 293 | // parse json data 294 | json_settings JsonSettings; 295 | mem_zero(&JsonSettings, sizeof(JsonSettings)); 296 | char aError[256]; 297 | json_value *pJsonData = json_parse_ex(&JsonSettings, pFileData, strlen(pFileData), aError); 298 | if(!pJsonData) 299 | { 300 | dbg_msg("main", "JSON Error in file %s: %s", m_Config.m_aConfigFile, aError); 301 | mem_free(pFileData); 302 | return 1; 303 | } 304 | 305 | // reset clients 306 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 307 | { 308 | if(!Client(i)->m_Active || !Client(i)->m_Connected) 309 | continue; 310 | 311 | m_Server.Network()->Drop(Client(i)->m_ClientNetID, "Server reloading..."); 312 | } 313 | mem_zero(m_aClients, sizeof(m_aClients)); 314 | for(int i = 0; i < NET_MAX_CLIENTS; i++) 315 | m_aClients[i].m_ClientNetID = -1; 316 | 317 | // extract data 318 | int ID = 0; 319 | const json_value &rStart = (*pJsonData)["servers"]; 320 | if(rStart.type == json_array) 321 | { 322 | for(unsigned i = 0; i < rStart.u.array.length; i++) 323 | { 324 | if(ID < 0 || ID >= NET_MAX_CLIENTS) 325 | continue; 326 | 327 | Client(ID)->m_Active = true; 328 | Client(ID)->m_Disabled = rStart[i]["disabled"].u.boolean; 329 | str_copy(Client(ID)->m_aName, rStart[i]["name"].u.string.ptr, sizeof(Client(ID)->m_aName)); 330 | str_copy(Client(ID)->m_aUsername, rStart[i]["username"].u.string.ptr, sizeof(Client(ID)->m_aUsername)); 331 | str_copy(Client(ID)->m_aType, rStart[i]["type"].u.string.ptr, sizeof(Client(ID)->m_aType)); 332 | str_copy(Client(ID)->m_aHost, rStart[i]["host"].u.string.ptr, sizeof(Client(ID)->m_aHost)); 333 | str_copy(Client(ID)->m_aLocation, rStart[i]["location"].u.string.ptr, sizeof(Client(ID)->m_aLocation)); 334 | str_copy(Client(ID)->m_aPassword, rStart[i]["password"].u.string.ptr, sizeof(Client(ID)->m_aPassword)); 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\"]", 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); 341 | else 342 | dbg_msg("main", "#%d: Name: \"%s\", Username: \"%s\", Type: \"%s\", Host: \"%s\", Location: \"%s\", Password: \"%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); 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 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_aPassword[128]; 39 | 40 | int64 m_TimeConnected; 41 | int64 m_LastUpdate; 42 | 43 | struct CStats 44 | { 45 | bool m_Online4; 46 | bool m_Online6; 47 | int64_t m_Uptime; 48 | double m_Load; 49 | int64_t m_NetworkRx; 50 | int64_t m_NetworkTx; 51 | int64_t m_NetworkIN; 52 | int64_t m_NetworkOUT; 53 | int64_t m_MemTotal; 54 | int64_t m_MemUsed; 55 | int64_t m_SwapTotal; 56 | int64_t m_SwapUsed; 57 | int64_t m_HDDTotal; 58 | int64_t m_HDDUsed; 59 | double m_CPU; 60 | char m_aCustom[512]; 61 | // Options 62 | bool m_Pong; 63 | } m_Stats; 64 | } m_aClients[NET_MAX_CLIENTS]; 65 | 66 | struct CJSONUpdateThreadData 67 | { 68 | CClient *pClients; 69 | CConfig *pConfig; 70 | volatile short m_ReloadRequired; 71 | } m_JSONUpdateThreadData; 72 | 73 | static void JSONUpdateThread(void *pUser); 74 | public: 75 | CMain(CConfig Config); 76 | 77 | void OnNewClient(int ClienNettID, int ClientID); 78 | void OnDelClient(int ClientNetID); 79 | int HandleMessage(int ClientNetID, char *pMessage); 80 | int ReadConfig(); 81 | int Run(); 82 | 83 | CClient *Client(int ClientID) { return &m_aClients[ClientID]; } 84 | CClient *ClientNet(int ClientNetID); 85 | const CConfig *Config() const { return &m_Config; } 86 | int ClientNetToClient(int ClientNetID); 87 | }; 88 | 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /server/src/netban.cpp: -------------------------------------------------------------------------------- 1 | #include 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 54 | typename CNetBan::CBan *CNetBan::CBanPool::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 *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 *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 119 | int CNetBan::CBanPool::Remove(CBan *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 155 | void CNetBan::CBanPool::Update(CBan *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 *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 203 | void CNetBan::CBanPool::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 222 | typename CNetBan::CBan *CNetBan::CBanPool::Get(int Index) const 223 | { 224 | if(Index < 0 || Index >= Num()) 225 | return 0; 226 | 227 | for(CNetBan::CBan *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 238 | void CNetBan::MakeBanInfo(const CBan *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 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 *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 325 | int CNetBan::Unban(T *pBanPool, const typename T::CDataType *pData) 326 | { 327 | CNetHash NetHash(pData); 328 | CBan *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 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 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 CBanPool 105 | { 106 | public: 107 | typedef T CDataType; 108 | 109 | CBan *Add(const CDataType *pData, const CBanInfo *pInfo, const CNetHash *pNetHash); 110 | int Remove(CBan *pBan); 111 | void Update(CBan *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 *First() const { return m_pFirstUsed; } 118 | CBan *First(const CNetHash *pNetHash) const { return m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]; } 119 | CBan *Find(const CDataType *pData, const CNetHash *pNetHash) const 120 | { 121 | for(CBan *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 *Get(int Index) const; 130 | 131 | private: 132 | enum 133 | { 134 | MAX_BANS=1024, 135 | }; 136 | 137 | CBan *m_paaHashList[HashCount][256]; 138 | CBan m_aBans[MAX_BANS]; 139 | CBan *m_pFirstFree; 140 | CBan *m_pFirstUsed; 141 | int m_CountUsed; 142 | }; 143 | 144 | typedef CBanPool CBanAddrPool; 145 | typedef CBanPool CBanRangePool; 146 | typedef CBan CBanAddr; 147 | typedef CBan CBanRange; 148 | 149 | template void MakeBanInfo(const CBan *pBan, char *pBuf, unsigned BuffSize, int Type) const; 150 | template int Ban(T *pBanPool, const typename T::CDataType *pData, int Seconds, const char *pReason); 151 | template 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 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 = 64 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 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 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 | -------------------------------------------------------------------------------- /web/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.0.3 (http://getbootstrap.com) 3 | * Copyright 2013 Twitter, Inc. 4 | * Licensed under http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | 7 | .btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn:active,.btn.active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe0e0e0',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-primary{background-image:-webkit-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:linear-gradient(to bottom,#428bca 0,#2d6ca2 100%);background-repeat:repeat-x;border-color:#2b669a;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff2d6ca2',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:focus{background-color:#2d6ca2;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#2d6ca2;border-color:#2b669a}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);background-repeat:repeat-x;border-color:#3e8f3e;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff419641',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);background-repeat:repeat-x;border-color:#e38d13;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffeb9316',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);background-repeat:repeat-x;border-color:#b92c28;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc12e2a',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);background-repeat:repeat-x;border-color:#28a4c9;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2aabd2',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#ffe8e8e8',GradientType=0)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-color:#357ebd;background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);background-repeat:repeat-x;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff8f8f8',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075)}.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f3f3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb',endColorstr='#fff3f3f3',GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.075);box-shadow:inset 0 3px 9px rgba(0,0,0,0.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,0.25)}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#222 0,#282828 100%);background-image:linear-gradient(to bottom,#222 0,#282828 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff282828',GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.25);box-shadow:inset 0 3px 9px rgba(0,0,0,0.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);background-repeat:repeat-x;border-color:#b2dba1;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffc8e5bc',GradientType=0)}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);background-repeat:repeat-x;border-color:#9acfea;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffb9def0',GradientType=0)}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);background-repeat:repeat-x;border-color:#f5e79e;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fff8efc0',GradientType=0)}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);background-repeat:repeat-x;border-color:#dca7a7;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffe7c3c3',GradientType=0)}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb',endColorstr='#fff5f5f5',GradientType=0)}.progress-bar{background-image:-webkit-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3071a9',GradientType=0)}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff449d44',GradientType=0)}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff31b0d5',GradientType=0)}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffec971f',GradientType=0)}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc9302c',GradientType=0)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);background-repeat:repeat-x;border-color:#3278b3;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3278b3',GradientType=0)}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#ffe8e8e8',GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffd0e9c6',GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffc4e3f3',GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fffaf2cc',GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffebcccc',GradientType=0)}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);background-repeat:repeat-x;border-color:#dcdcdc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8',endColorstr='#fff5f5f5',GradientType=0);-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1)} -------------------------------------------------------------------------------- /web/css/light.css: -------------------------------------------------------------------------------- 1 | body { font-family:Molengo,"Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; background: #ebebeb url('../img/light.png');padding-top: 70px;padding-bottom: 30px;} 2 | .announcement { color: #777; border-bottom: solid 3px #d0d0d0; background-color: #fff; padding: 10px 10px; text-align: center; transition: 0.3s; } 3 | .announcement p { display: inline-block;font-size: 15px;margin: 0;line-height: 1;text-indent: 5px; } 4 | .announcement i { display: inline-block;font-size: 15px;margin: 0;line-height: 1;color: #444; } 5 | 6 | .path-announcement2 { 7 | overflow: hidden; 8 | text-overflow: ellipsis; 9 | white-space: nowrap; 10 | } 11 | .path-announcement { 12 | margin-top: 50px; 13 | background-color: #fafbfc; 14 | border-bottom: 1px solid #e1e4e8; 15 | } 16 | .path-announcement p { 17 | color: #444444; 18 | line-height: 3.5; 19 | margin-left: 20px; 20 | } 21 | .path-announcement a { 22 | color: #666; 23 | box-shadow: 0px 1px 0px 0px #999; 24 | text-decoration: none; 25 | } 26 | .path-announcement a:focus,.path-announcement a:hover { 27 | color: #337ab7; 28 | box-shadow: 0px 1px 0px 0px #555; 29 | } 30 | .path-announcement i { 31 | margin-right: 10px; 32 | } 33 | .path-announcement i,.path-announcement p { 34 | display: inline; 35 | } 36 | 37 | .navbar { min-height: 50px; } 38 | .navbar-top { background-color: #444 !important; } 39 | .navbar-brand { color: #fff; padding: 10px; font-size: 20px; } 40 | .dropdown .dropdown-toggle { padding-bottom: 10px; padding-top: 10px; } 41 | .navbar-inverse .navbar-brand { color: #fff; padding: 15px 20px 10px; font-size: 16px; font-weight: 600; } 42 | .content { background: #ffffff; padding: 20px; border: 1px #eee solid; -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, .1); -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, .1); box-shadow: 0 1px 10px rgba(0, 0, 0, .1); margin-bottom: 20px; margin-top: 50px; } 43 | .table { background: #ffffff; margin-bottom: 0; border-collapse: collapse; border-radius: 3px; } 44 | .table th, .table td { text-align: center; } 45 | .table-striped tbody > tr.even > td, .table-striped tbody > tr.even > th { background-color: #F9F9F9; } 46 | .table-striped tbody > tr.odd > td, .table-striped tbody > tr.odd > th { background-color: #FFF; } 47 | .progress { margin-bottom: 0; } 48 | .progress-bar { color: #000; } 49 | .table-hover > tbody > tr:hover > td { background: #E6E6E6; } 50 | tr.even.expandRow > :hover { background: #F9F9F9 !important; } 51 | tr.odd.expandRow > :hover { background: #FFF !important; } 52 | .expandRow > td { padding: 0 !important; border-top: 0px !important; } 53 | #cpu, #ram, #hdd, #network, #traffic { min-width: 55px; max-width: 100px; } 54 | 55 | @media only screen and (max-width: 992px) { 56 | #location, tr td:nth-child(4) { display:none; visibility:hidden; } 57 | } 58 | @media only screen and (max-width: 720px) { 59 | #type, tr td:nth-child(3) { display:none; visibility:hidden; } 60 | #location, tr td:nth-child(4) { display:none; visibility:hidden; } 61 | #uptime, tr td:nth-child(5) { display:none; visibility:hidden; } 62 | } 63 | @media only screen and (max-width: 600px) { 64 | #type, tr td:nth-child(3) { display:none; visibility:hidden; } 65 | #location, tr td:nth-child(4) { display:none; visibility:hidden; } 66 | #uptime, tr td:nth-child(5) { display:none; visibility:hidden; } 67 | #load, tr td:nth-child(6) { display:none; visibility:hidden; } 68 | } 69 | @media only screen and (max-width: 533px) { 70 | #type, tr td:nth-child(3) { display:none; visibility:hidden; } 71 | #location, tr td:nth-child(4) { display:none; visibility:hidden; } 72 | #uptime, tr td:nth-child(5) { display:none; visibility:hidden; } 73 | #traffic, tr td:nth-child(8) { display:none; visibility:hidden; } 74 | #load, tr td:nth-child(6) { display:none; visibility:hidden; } 75 | } 76 | @media only screen and (max-width: 450px) { 77 | body { font-size: 10px; } 78 | .content { padding: 0; } 79 | #name, tr td:nth-child(2) { min-width: 20px; max-width: 60px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } 80 | #type, tr td:nth-child(3) { display:none; visibility:hidden; } 81 | #location, tr td:nth-child(4) { display:none; visibility:hidden; } 82 | #uptime, tr td:nth-child(5) { display:none; visibility:hidden; } 83 | #traffic, tr td:nth-child(8) { display:none; visibility:hidden; } 84 | #hdd, tr td:nth-child(11) { display:none; visibility:hidden; } 85 | #cpu, #ram { min-width: 20px; max-width: 40px; } 86 | } 87 | -------------------------------------------------------------------------------- /web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ToyoDAdoubiBackup/ServerStatus-Toyo/a653403f14e7b1eccd3991a92654d382462f6c82/web/favicon.ico -------------------------------------------------------------------------------- /web/img/light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ToyoDAdoubiBackup/ServerStatus-Toyo/a653403f14e7b1eccd3991a92654d382462f6c82/web/img/light.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 逗比云监控 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 26 | 33 | 34 |
35 |
36 | 41 |
42 |
加载中...
43 |
44 |
45 | 如果出现此消息,请确保您已启用Javascript!
否则云监控主服务(服务端) 未启动或已关闭. 46 |
47 |

48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
IPv4节点名虚拟化位置在线时间负载网络(B/s) ↓|↑流量(B) ↓|↑CPU内存硬盘
69 |
70 |
Updating...
71 |
72 | 73 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /web/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.0.3 (http://getbootstrap.com) 3 | * Copyright 2013 Twitter, Inc. 4 | * Licensed under http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | 7 | if("undefined"==typeof jQuery)throw new Error("Bootstrap requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]}}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d)};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.is("input")?"val":"html",e=c.data();a+="Text",e.resetText||c.data("resetText",c[d]()),c[d](e[a]||this.options[a]),setTimeout(function(){"loadingText"==a?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons"]'),b=!0;if(a.length){var c=this.$element.find("input");"radio"===c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?b=!1:a.find(".active").removeClass("active")),b&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}b&&this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}this.sliding=!0,f&&this.pause();var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});if(!e.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid.bs.carousel",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(j),j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid.bs.carousel")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(j),j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid.bs.carousel")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?(this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350),void 0):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(jQuery),+function(a){"use strict";function b(){a(d).remove(),a(e).each(function(b){var d=c(a(this));d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown")),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown"))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){if("ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(c).is("body")?a(window):a(c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#\w/.test(e)&&a(e);return f&&f.length&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parents(".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top()),"function"==typeof h&&(h=f.bottom());var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;this.affixed!==i&&(this.unpin&&this.$element.css("top",""),this.affixed=i,this.unpin="bottom"==i?e.top-d:null,this.$element.removeClass(b.RESET).addClass("affix"+(i?"-"+i:"")),"bottom"==i&&this.$element.offset({top:document.body.offsetHeight-h-this.$element.height()}))}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(jQuery); -------------------------------------------------------------------------------- /web/js/serverstatus.js: -------------------------------------------------------------------------------- 1 | // serverstatus.js 2 | var error = 0; 3 | var d = 0; 4 | var server_status = new Array(); 5 | 6 | function timeSince(date) { 7 | if(date == 0) 8 | return "从未."; 9 | 10 | var seconds = Math.floor((new Date() - date) / 1000); 11 | var interval = Math.floor(seconds / 31536000); 12 | 13 | if (interval > 1) 14 | return interval + " 年前."; 15 | interval = Math.floor(seconds / 2592000); 16 | if (interval > 1) 17 | return interval + " 月前."; 18 | interval = Math.floor(seconds / 86400); 19 | if (interval > 1) 20 | return interval + " 日前."; 21 | interval = Math.floor(seconds / 3600); 22 | if (interval > 1) 23 | return interval + " 小时前."; 24 | interval = Math.floor(seconds / 60); 25 | if (interval > 1) 26 | return interval + " 分钟前."; 27 | /*if(Math.floor(seconds) >= 5) 28 | return Math.floor(seconds) + " seconds";*/ 29 | else 30 | return "几秒前."; 31 | } 32 | 33 | function bytesToSize(bytes, precision, si) 34 | { 35 | var ret; 36 | si = typeof si !== 'undefined' ? si : 0; 37 | if(si != 0) { 38 | var kilobyte = 1000; 39 | var megabyte = kilobyte * 1000; 40 | var gigabyte = megabyte * 1000; 41 | var terabyte = gigabyte * 1000; 42 | } else { 43 | var kilobyte = 1024; 44 | var megabyte = kilobyte * 1024; 45 | var gigabyte = megabyte * 1024; 46 | var terabyte = gigabyte * 1024; 47 | } 48 | 49 | if ((bytes >= 0) && (bytes < kilobyte)) { 50 | return bytes + ' B'; 51 | 52 | } else if ((bytes >= kilobyte) && (bytes < megabyte)) { 53 | ret = (bytes / kilobyte).toFixed(precision) + ' K'; 54 | 55 | } else if ((bytes >= megabyte) && (bytes < gigabyte)) { 56 | ret = (bytes / megabyte).toFixed(precision) + ' M'; 57 | 58 | } else if ((bytes >= gigabyte) && (bytes < terabyte)) { 59 | ret = (bytes / gigabyte).toFixed(precision) + ' G'; 60 | 61 | } else if (bytes >= terabyte) { 62 | ret = (bytes / terabyte).toFixed(precision) + ' T'; 63 | 64 | } else { 65 | return bytes + ' B'; 66 | } 67 | if(si != 0) { 68 | return ret + 'B'; 69 | } else { 70 | return ret + 'iB'; 71 | } 72 | } 73 | 74 | function uptime() { 75 | $.getJSON("json/stats.json", function(result) { 76 | $("#loading-notice").remove(); 77 | if(result.reload) 78 | setTimeout(function() { location.reload(true) }, 1000); 79 | 80 | for (var i = 0; i < result.servers.length; i++) { 81 | var TableRow = $("#servers tr#r" + i); 82 | var ExpandRow = $("#servers #rt" + i); 83 | var hack; // fuck CSS for making me do this 84 | if(i%2) hack="odd"; else hack="even"; 85 | if (!TableRow.length) { 86 | $("#servers").append( 87 | "" + 88 | "
加载中
" + 89 | "加载中" + 90 | "加载中" + 91 | "" + 92 | "加载中" + 93 | "加载中" + 94 | "加载中" + 95 | "加载中" + 96 | "加载中" + 97 | "
加载中
" + 98 | "
加载中
" + 99 | "
加载中
" + 100 | "" + 101 | "
" + 102 | "
加载中
" + 103 | "
加载中
" + 104 | "
加载中
" + 105 | "
加载中
" + 106 | "
" 107 | ); 108 | TableRow = $("#servers tr#r" + i); 109 | ExpandRow = $("#servers #rt" + i); 110 | server_status[i] = true; 111 | } 112 | TableRow = TableRow[0]; 113 | if(error) { 114 | TableRow.setAttribute("data-target", "#rt" + i); 115 | server_status[i] = true; 116 | } 117 | 118 | // Online4 119 | if (result.servers[i].online4) { 120 | TableRow.children["online4"].children[0].children[0].className = "progress-bar progress-bar-success"; 121 | TableRow.children["online4"].children[0].children[0].innerHTML = "开启"; 122 | } else { 123 | TableRow.children["online4"].children[0].children[0].className = "progress-bar progress-bar-danger"; 124 | TableRow.children["online4"].children[0].children[0].innerHTML = "关闭"; 125 | } 126 | 127 | // Online6 128 | //if (result.servers[i].online6) { 129 | // TableRow.children["online6"].children[0].children[0].className = "progress-bar progress-bar-success"; 130 | // TableRow.children["online6"].children[0].children[0].innerHTML = "开启"; 131 | //} else { 132 | // TableRow.children["online6"].children[0].children[0].className = "progress-bar progress-bar-danger"; 133 | // TableRow.children["online6"].children[0].children[0].innerHTML = "关闭"; 134 | //} 135 | 136 | // Name 137 | TableRow.children["name"].innerHTML = result.servers[i].name; 138 | 139 | // Type 140 | TableRow.children["type"].innerHTML = result.servers[i].type; 141 | 142 | // Host 143 | //TableRow.children["host"].innerHTML = result.servers[i].host; 144 | 145 | // Location 146 | TableRow.children["location"].innerHTML = result.servers[i].location; 147 | if (!result.servers[i].online4 && !result.servers[i].online6) { 148 | if (server_status[i]) { 149 | TableRow.children["uptime"].innerHTML = "–"; 150 | TableRow.children["load"].innerHTML = "–"; 151 | TableRow.children["network"].innerHTML = "–"; 152 | TableRow.children["traffic"].innerHTML = "–"; 153 | TableRow.children["cpu"].children[0].children[0].className = "progress-bar progress-bar-danger"; 154 | TableRow.children["cpu"].children[0].children[0].style.width = "100%"; 155 | TableRow.children["cpu"].children[0].children[0].innerHTML = "关闭"; 156 | TableRow.children["memory"].children[0].children[0].className = "progress-bar progress-bar-danger"; 157 | TableRow.children["memory"].children[0].children[0].style.width = "100%"; 158 | TableRow.children["memory"].children[0].children[0].innerHTML = "关闭"; 159 | TableRow.children["hdd"].children[0].children[0].className = "progress-bar progress-bar-danger"; 160 | TableRow.children["hdd"].children[0].children[0].style.width = "100%"; 161 | TableRow.children["hdd"].children[0].children[0].innerHTML = "关闭"; 162 | if(ExpandRow.hasClass("in")) { 163 | ExpandRow.collapse("hide"); 164 | } 165 | TableRow.setAttribute("data-target", ""); 166 | server_status[i] = false; 167 | } 168 | } else { 169 | if (!server_status[i]) { 170 | TableRow.setAttribute("data-target", "#rt" + i); 171 | server_status[i] = true; 172 | } 173 | 174 | // Uptime 175 | TableRow.children["uptime"].innerHTML = result.servers[i].uptime; 176 | 177 | // Load 178 | if(result.servers[i].load == -1) { 179 | TableRow.children["load"].innerHTML = "–"; 180 | } else { 181 | TableRow.children["load"].innerHTML = result.servers[i].load; 182 | } 183 | 184 | // Network 185 | var netstr = ""; 186 | if(result.servers[i].network_rx < 1000) 187 | netstr += result.servers[i].network_rx.toFixed(0) + "B"; 188 | else if(result.servers[i].network_rx < 1000*1000) 189 | netstr += (result.servers[i].network_rx/1000).toFixed(0) + "K"; 190 | else 191 | netstr += (result.servers[i].network_rx/1000/1000).toFixed(1) + "M"; 192 | netstr += " | " 193 | if(result.servers[i].network_tx < 1000) 194 | netstr += result.servers[i].network_tx.toFixed(0) + "B"; 195 | else if(result.servers[i].network_tx < 1000*1000) 196 | netstr += (result.servers[i].network_tx/1000).toFixed(0) + "K"; 197 | else 198 | netstr += (result.servers[i].network_tx/1000/1000).toFixed(1) + "M"; 199 | TableRow.children["network"].innerHTML = netstr; 200 | 201 | //Traffic 202 | var trafficstr = ""; 203 | if(result.servers[i].network_in < 1024) 204 | trafficstr += result.servers[i].network_in.toFixed(0) + "B"; 205 | else if(result.servers[i].network_in < 1024*1024) 206 | trafficstr += (result.servers[i].network_in/1024).toFixed(0) + "K"; 207 | else if(result.servers[i].network_in < 1024*1024*1024) 208 | trafficstr += (result.servers[i].network_in/1024/1024).toFixed(1) + "M"; 209 | else if(result.servers[i].network_in < 1024*1024*1024*1024) 210 | trafficstr += (result.servers[i].network_in/1024/1024/1024).toFixed(2) + "G"; 211 | else 212 | trafficstr += (result.servers[i].network_in/1024/1024/1024/1024).toFixed(2) + "T"; 213 | trafficstr += " | " 214 | if(result.servers[i].network_out < 1024) 215 | trafficstr += result.servers[i].network_out.toFixed(0) + "B"; 216 | else if(result.servers[i].network_out < 1024*1024) 217 | trafficstr += (result.servers[i].network_out/1024).toFixed(0) + "K"; 218 | else if(result.servers[i].network_out < 1024*1024*1024) 219 | trafficstr += (result.servers[i].network_out/1024/1024).toFixed(1) + "M"; 220 | else if(result.servers[i].network_out < 1024*1024*1024*1024) 221 | trafficstr += (result.servers[i].network_out/1024/1024/1024).toFixed(2) + "G"; 222 | else 223 | trafficstr += (result.servers[i].network_out/1024/1024/1024/1024).toFixed(2) + "T"; 224 | TableRow.children["traffic"].innerHTML = trafficstr; 225 | 226 | // CPU 227 | if (result.servers[i].cpu >= 90) 228 | TableRow.children["cpu"].children[0].children[0].className = "progress-bar progress-bar-danger"; 229 | else if (result.servers[i].cpu >= 80) 230 | TableRow.children["cpu"].children[0].children[0].className = "progress-bar progress-bar-warning"; 231 | else 232 | TableRow.children["cpu"].children[0].children[0].className = "progress-bar progress-bar-success"; 233 | TableRow.children["cpu"].children[0].children[0].style.width = result.servers[i].cpu + "%"; 234 | TableRow.children["cpu"].children[0].children[0].innerHTML = result.servers[i].cpu + "%"; 235 | 236 | // Memory 237 | var Mem = ((result.servers[i].memory_used/result.servers[i].memory_total)*100.0).toFixed(0); 238 | if (Mem >= 90) 239 | TableRow.children["memory"].children[0].children[0].className = "progress-bar progress-bar-danger"; 240 | else if (Mem >= 80) 241 | TableRow.children["memory"].children[0].children[0].className = "progress-bar progress-bar-warning"; 242 | else 243 | TableRow.children["memory"].children[0].children[0].className = "progress-bar progress-bar-success"; 244 | TableRow.children["memory"].children[0].children[0].style.width = Mem + "%"; 245 | TableRow.children["memory"].children[0].children[0].innerHTML = Mem + "%"; 246 | ExpandRow[0].children["expand_mem"].innerHTML = "内存信息: " + bytesToSize(result.servers[i].memory_used*1024, 2) + " / " + bytesToSize(result.servers[i].memory_total*1024, 2); 247 | // Swap 248 | ExpandRow[0].children["expand_swap"].innerHTML = "交换分区: " + bytesToSize(result.servers[i].swap_used*1024, 2) + " / " + bytesToSize(result.servers[i].swap_total*1024, 2); 249 | 250 | // HDD 251 | var HDD = ((result.servers[i].hdd_used/result.servers[i].hdd_total)*100.0).toFixed(0); 252 | if (HDD >= 90) 253 | TableRow.children["hdd"].children[0].children[0].className = "progress-bar progress-bar-danger"; 254 | else if (HDD >= 80) 255 | TableRow.children["hdd"].children[0].children[0].className = "progress-bar progress-bar-warning"; 256 | else 257 | TableRow.children["hdd"].children[0].children[0].className = "progress-bar progress-bar-success"; 258 | TableRow.children["hdd"].children[0].children[0].style.width = HDD + "%"; 259 | TableRow.children["hdd"].children[0].children[0].innerHTML = HDD + "%"; 260 | ExpandRow[0].children["expand_hdd"].innerHTML = "硬盘信息: " + bytesToSize(result.servers[i].hdd_used*1024*1024, 2) + " / " + bytesToSize(result.servers[i].hdd_total*1024*1024, 2); 261 | 262 | // Custom 263 | if (result.servers[i].custom) { 264 | ExpandRow[0].children["expand_custom"].innerHTML = result.servers[i].custom 265 | } else { 266 | ExpandRow[0].children["expand_custom"].innerHTML = "" 267 | } 268 | } 269 | }; 270 | 271 | d = new Date(result.updated*1000); 272 | error = 0; 273 | }).fail(function(update_error) { 274 | if (!error) { 275 | $("#servers > tr.accordion-toggle").each(function(i) { 276 | var TableRow = $("#servers tr#r" + i)[0]; 277 | var ExpandRow = $("#servers #rt" + i); 278 | TableRow.children["online4"].children[0].children[0].className = "progress-bar progress-bar-error"; 279 | TableRow.children["online4"].children[0].children[0].innerHTML = "错误"; 280 | //TableRow.children["online6"].children[0].children[0].className = "progress-bar progress-bar-error"; 281 | //TableRow.children["online6"].children[0].children[0].innerHTML = "错误"; 282 | TableRow.children["uptime"].innerHTML = "
错误
"; 283 | TableRow.children["load"].innerHTML = "
错误
"; 284 | TableRow.children["network"].innerHTML = "
错误
"; 285 | TableRow.children["traffic"].innerHTML = "
错误
"; 286 | TableRow.children["cpu"].children[0].children[0].className = "progress-bar progress-bar-error"; 287 | TableRow.children["cpu"].children[0].children[0].style.width = "100%"; 288 | TableRow.children["cpu"].children[0].children[0].innerHTML = "错误"; 289 | TableRow.children["memory"].children[0].children[0].className = "progress-bar progress-bar-error"; 290 | TableRow.children["memory"].children[0].children[0].style.width = "100%"; 291 | TableRow.children["memory"].children[0].children[0].innerHTML = "错误"; 292 | TableRow.children["hdd"].children[0].children[0].className = "progress-bar progress-bar-error"; 293 | TableRow.children["hdd"].children[0].children[0].style.width = "100%"; 294 | TableRow.children["hdd"].children[0].children[0].innerHTML = "错误"; 295 | if(ExpandRow.hasClass("in")) { 296 | ExpandRow.collapse("hide"); 297 | } 298 | TableRow.setAttribute("data-target", ""); 299 | server_status[i] = false; 300 | }); 301 | } 302 | error = 1; 303 | $("#updated").html("更新错误."); 304 | }); 305 | } 306 | 307 | function updateTime() { 308 | if (!error) 309 | $("#updated").html("最后更新: " + timeSince(d)); 310 | } 311 | 312 | uptime(); 313 | updateTime(); 314 | setInterval(uptime, 2000); 315 | setInterval(updateTime, 500); 316 | 317 | 318 | // styleswitcher.js 319 | function setActiveStyleSheet(title) { 320 | var i, a, main; 321 | for(i=0; (a = document.getElementsByTagName("link")[i]); i++) { 322 | if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")) { 323 | a.disabled = true; 324 | if(a.getAttribute("title") == title) a.disabled = false; 325 | } 326 | } 327 | } 328 | 329 | function getActiveStyleSheet() { 330 | var i, a; 331 | for(i=0; (a = document.getElementsByTagName("link")[i]); i++) { 332 | if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title") && !a.disabled) 333 | return a.getAttribute("title"); 334 | } 335 | return null; 336 | } 337 | 338 | function getPreferredStyleSheet() { 339 | var i, a; 340 | for(i=0; (a = document.getElementsByTagName("link")[i]); i++) { 341 | if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("rel").indexOf("alt") == -1 && a.getAttribute("title")) 342 | return a.getAttribute("title"); 343 | } 344 | return null; 345 | } 346 | 347 | function createCookie(name,value,days) { 348 | if (days) { 349 | var date = new Date(); 350 | date.setTime(date.getTime()+(days*24*60*60*1000)); 351 | var expires = "; expires="+date.toGMTString(); 352 | } 353 | else expires = ""; 354 | document.cookie = name+"="+value+expires+"; path=/"; 355 | } 356 | 357 | function readCookie(name) { 358 | var nameEQ = name + "="; 359 | var ca = document.cookie.split(';'); 360 | for(var i=0;i < ca.length;i++) { 361 | var c = ca[i]; 362 | while (c.charAt(0)==' ') 363 | c = c.substring(1,c.length); 364 | if (c.indexOf(nameEQ) == 0) 365 | return c.substring(nameEQ.length,c.length); 366 | } 367 | return null; 368 | } 369 | 370 | window.onload = function(e) { 371 | var cookie = readCookie("style"); 372 | var title = cookie ? cookie : getPreferredStyleSheet(); 373 | setActiveStyleSheet(title); 374 | } 375 | 376 | window.onunload = function(e) { 377 | var title = getActiveStyleSheet(); 378 | createCookie("style", title, 365); 379 | } 380 | 381 | var cookie = readCookie("style"); 382 | var title = cookie ? cookie : getPreferredStyleSheet(); 383 | setActiveStyleSheet(title); 384 | -------------------------------------------------------------------------------- /web/json/.gitignore: -------------------------------------------------------------------------------- 1 | stats.json 2 | stats.json~ -------------------------------------------------------------------------------- /web/robots.txt: -------------------------------------------------------------------------------- 1 | # robots.txt generated at http://tool.chinaz.com/robots/ 2 | User-agent: Baiduspider 3 | Disallow: / 4 | User-agent: Sosospider 5 | Disallow: / 6 | User-agent: sogou spider 7 | Disallow: / 8 | User-agent: YodaoBot 9 | Disallow: / 10 | User-agent: Googlebot 11 | Disallow: / 12 | User-agent: Bingbot 13 | Disallow: / 14 | User-agent: Slurp 15 | Disallow: / 16 | User-agent: Teoma 17 | Disallow: / 18 | User-agent: ia_archiver 19 | Disallow: / 20 | User-agent: twiceler 21 | Disallow: / 22 | User-agent: MSNBot 23 | Disallow: / 24 | User-agent: Scrubby 25 | Disallow: / 26 | User-agent: Robozilla 27 | Disallow: / 28 | User-agent: Gigabot 29 | Disallow: / 30 | User-agent: googlebot-image 31 | Disallow: / 32 | User-agent: googlebot-mobile 33 | Disallow: / 34 | User-agent: yahoo-mmcrawler 35 | Disallow: / 36 | User-agent: yahoo-blogs/v3.9 37 | Disallow: / 38 | User-agent: psbot 39 | Disallow: / 40 | Disallow: /ip/ 41 | Disallow: /qr/ 42 | Disallow: / --------------------------------------------------------------------------------