├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── mkrrd.sh ├── rrdupd.c ├── serverstat-linux.service └── stat.pl /.gitignore: -------------------------------------------------------------------------------- 1 | *.rrd 2 | *.png 3 | rrdupd 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014, 2016 Alexander Graf and André Koch-Kramer. 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS+=-O2 -march=native -pipe -Wall -Wextra 2 | LDFLAGS+=-lrrd 3 | 4 | rrdupd: rrdupd.c 5 | $(CC) -o $@ $(CFLAGS) $< $(LDFLAGS) 6 | 7 | clean: 8 | rm -f rrdupd 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This is a tool providing a CGI page **plotting system usage statistics** using 4 | [rrdtool](http://oss.oetiker.ch/rrdtool/). With only about 400 lines of code, 5 | it is very **simple** and therefore **easy to understand and extend**. 6 | 7 | Currently it collects **Load Average**, **CPU usage**, **Temperature**, 8 | **Disk I/O**, **Network Traffic** and **Memory and Swap Usage**, which are plotted over a 9 | period chosen by the user at runtime. 10 | 11 | It is build up of two parts: 12 | - stat.pl - **Perl CGI script** to be called by a webserver. 13 | - rrdupd.c - **lightweight** daemon collecting system usage information. 14 | 15 | # Installation 16 | 17 | You need to have [rrdtool](http://oss.oetiker.ch/rrdtool/) with plotting 18 | capability installed, as well as the common tools for building C programs (i.e. 19 | make and gcc). This program should be installed in a path where your webserver 20 | is able to execute the stat.pl script via its CGI. 21 | 22 | When these prerequisites are met, installation is done with following steps: 23 | 24 | 1. Download serverstat-linux, e.g. by doing `git clone https://github.com/aandergr/serverstat-linux` 25 | 2. You might adjust the files to fit your needs 26 | 3. Build rrdupd using `make` 27 | 4. Run `./mkrrd.sh` to initalize RRD databases 28 | 5. Adjust permissions such that your webserver is able to execute stat.pl as 29 | CGI script, able to read .rrd files and able to write/create .png files in 30 | that directory 31 | 6. Run `./rrdupd` 32 | 33 | `./rrdupd` should be started automatically on system startup. On 34 | debian-alike systems, this can be achieved using: 35 | 36 | ``` 37 | echo "start-stop-daemon -S -x /var/www/html/stat/rrdupd -c stat -b" >> /etc/rc.local 38 | chmod +x /etc/rc.local 39 | ``` 40 | 41 | (Where stat is the name of the user and /var/www/html/stat the path of this 42 | software) 43 | 44 | Also, there is a `serverstat-linux.service` systemctl script template in this repository. 45 | 46 | Consult the documentation of your operating system for more information. 47 | 48 | Now you can point your browser to the direction of your stat.pl and (hopefully) 49 | everything works. Of course it will take a few minutes until you see some data. 50 | 51 | # Screenshots 52 | 53 | It might look like this: 54 | ![Screenshot of stat.pl page](https://raw.github.com/wiki/aandergr/serverstat-linux/screenshot-odroid.png) 55 | -------------------------------------------------------------------------------- /mkrrd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright (c) 2011, 2014 Alexander Graf. All rights reserved. 4 | 5 | rrdtool create loadavg.rrd -s 120\ 6 | DS:la5:GAUGE:240:0:U \ 7 | DS:la15:GAUGE:240:0:U \ 8 | RRA:AVERAGE:0.5:1:720 \ 9 | RRA:AVERAGE:0.5:15:336 \ 10 | RRA:AVERAGE:0.5:180:224 \ 11 | RRA:AVERAGE:0.5:720:365 12 | 13 | rrdtool create traffic.rrd -s 120\ 14 | DS:rx:GAUGE:240:0:U \ 15 | DS:tx:GAUGE:240:0:U \ 16 | RRA:AVERAGE:0.5:1:720 \ 17 | RRA:AVERAGE:0.5:15:336 \ 18 | RRA:AVERAGE:0.5:180:224 \ 19 | RRA:AVERAGE:0.5:720:365 20 | 21 | rrdtool create mem.rrd -s 120 \ 22 | DS:user:GAUGE:240:0:U \ 23 | DS:kernel:GAUGE:240:0:U \ 24 | DS:kernel-cache:GAUGE:240:0:U \ 25 | DS:buffers:GAUGE:240:0:U \ 26 | DS:cached:GAUGE:240:0:U \ 27 | DS:shared:GAUGE:240:0:U \ 28 | DS:other:GAUGE:240:0:U\ 29 | RRA:AVERAGE:0.5:1:720 \ 30 | RRA:AVERAGE:0.5:15:336 \ 31 | RRA:AVERAGE:0.5:180:224 \ 32 | RRA:AVERAGE:0.5:720:365 33 | 34 | rrdtool create swap.rrd -s 120 \ 35 | DS:used:GAUGE:240:0:U \ 36 | RRA:AVERAGE:0.5:1:720 \ 37 | RRA:AVERAGE:0.5:15:336 \ 38 | RRA:AVERAGE:0.5:180:224 \ 39 | RRA:AVERAGE:0.5:720:365 40 | 41 | rrdtool create temp.rrd -s 120 \ 42 | DS:core:GAUGE:240:0:120 \ 43 | RRA:AVERAGE:0.5:1:720 \ 44 | RRA:AVERAGE:0.5:15:336 \ 45 | RRA:AVERAGE:0.5:180:224 \ 46 | RRA:AVERAGE:0.5:720:365 47 | # 48 | rrdtool create cpu.rrd -s 120 \ 49 | DS:user:GAUGE:240:0:1\ 50 | DS:nice:GAUGE:240:0:1\ 51 | DS:system:GAUGE:240:0:1 \ 52 | DS:iowait:GAUGE:240:0:1 \ 53 | DS:irq:GAUGE:240:0:1 \ 54 | DS:softirq:GAUGE:240:0:1 \ 55 | RRA:AVERAGE:0.5:1:720 \ 56 | RRA:AVERAGE:0.5:15:336 \ 57 | RRA:AVERAGE:0.5:180:224 \ 58 | RRA:AVERAGE:0.5:720:365 59 | 60 | rrdtool create disk.rrd -s 120 \ 61 | DS:r:GAUGE:240:0:U \ 62 | DS:w:GAUGE:240:0:U \ 63 | RRA:AVERAGE:0.5:1:720 \ 64 | RRA:AVERAGE:0.5:15:336 \ 65 | RRA:AVERAGE:0.5:180:224 \ 66 | RRA:AVERAGE:0.5:720:365 67 | -------------------------------------------------------------------------------- /rrdupd.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014, 2016 Alexander Graf and André Koch-Kramer. 2 | * All rights reserved. */ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | 16 | #define HEARTBEAT 120 17 | 18 | /* uncomment this if you want to measure temperature. */ 19 | //#define HAVE_TEMP 20 | #define TEMPSENSOR "/sys/devices/virtual/thermal/thermal_zone0/temp" 21 | #define TEMPSAMPLES 8 22 | 23 | 24 | #define prefmatch(str, prefix) !strncmp(str, prefix, strlen(prefix)) 25 | 26 | #ifdef HAVE_TEMP 27 | static int temp_sum; 28 | #endif 29 | 30 | #if !defined(LVM_BLK_MAJOR) 31 | #define LVM_BLK_MAJOR 58 32 | #endif 33 | 34 | #define DM_MAJOR_RAID 253 35 | #define DM_MAJOR_CRYPT 254 36 | 37 | typedef struct list { 38 | char *devname; 39 | char is_device; 40 | struct list *next; 41 | } *devicelist; 42 | 43 | static devicelist devices = NULL; 44 | 45 | static devicelist insert(devicelist list, char *device, char is_device) { 46 | devicelist tmplist = (devicelist) malloc(sizeof(struct list)); 47 | if (list == NULL) { 48 | tmplist->next = NULL; 49 | } else { 50 | tmplist->next = list; 51 | } 52 | int size = strlen(device) + 1; 53 | tmplist->devname = (char*) malloc(size); 54 | strncpy(tmplist->devname, device, size); 55 | tmplist->is_device = is_device; 56 | return tmplist; 57 | } 58 | 59 | static char is_disk(char *dev) { 60 | char devpath[27] = "/sys/block/"; 61 | strncat(devpath, dev, 27); 62 | devicelist tmplist = devices; 63 | while (tmplist != NULL) { 64 | if (!strcmp(tmplist->devname, dev)) 65 | break; 66 | tmplist = tmplist->next; 67 | } 68 | if (tmplist != NULL) 69 | return tmplist->is_device; 70 | else { 71 | char is_device = !access(devpath, F_OK); 72 | devices = insert(devices, dev, is_device); 73 | return is_device; 74 | } 75 | } 76 | 77 | static void update() 78 | { 79 | FILE *proc; 80 | unsigned long vm_MemTotal = 0, vm_MemFree = 0, vm_Buffers = 0, 81 | vm_Cached = 0, vm_SwapCached = 0, vm_Shmem = 0, 82 | vm_PageTables = 0, vm_KernelStack = 0, vm_Slab = 0, 83 | vm_AnonPages = 0, vm_SwapTotal = 0, vm_SwapFree = 0; 84 | int i, j; 85 | char buf[256]; 86 | char *argv[4]; 87 | unsigned long long cp_times[7], cp_times_diff[7], cp_times_dsum; 88 | static unsigned long long last_cp_times[7]; 89 | static int last_valid; 90 | float cp_times_dn[7]; 91 | float loadavg5, loadavg15; 92 | char devname[16]; 93 | unsigned long long ibytes, obytes; 94 | unsigned long long ibytes_d, obytes_d; 95 | static unsigned long long last_ibytes, last_obytes; 96 | unsigned int major, minor; 97 | unsigned long long reads, writes, reads_d, writes_d; 98 | static unsigned long long last_reads, last_writes; 99 | 100 | 101 | /* CPU usage */ 102 | proc = fopen("/proc/stat", "r"); 103 | i = fscanf(proc, "%*s %Lu %Lu %Lu %Lu %Lu %Lu %Lu", 104 | &cp_times[0], &cp_times[1], &cp_times[2], &cp_times[3], 105 | &cp_times[4], &cp_times[5], &cp_times[6]); 106 | assert(i == 7); 107 | fclose(proc); 108 | if (last_valid) { 109 | for (i = 0; i < 7; i++) { 110 | if (last_cp_times[i] > cp_times[i]) 111 | goto cpuovfl; 112 | cp_times_diff[i] = cp_times[i] - last_cp_times[i]; 113 | } 114 | cp_times_dsum = 0; 115 | for (i = 0; i < 7; i++) { 116 | cp_times_dsum += cp_times_diff[i]; 117 | } 118 | for (i = 0; i < 7; i++) { 119 | cp_times_dn[i] = (float) cp_times_diff[i] / cp_times_dsum; 120 | } 121 | snprintf(buf, sizeof(buf), "N:%.6f:%.6f:%.6f:%.6f:%.6f:%.6f", 122 | cp_times_dn[0], cp_times_dn[1], cp_times_dn[2], 123 | cp_times_dn[4], cp_times_dn[5], cp_times_dn[6]); 124 | argv[0] = "update"; 125 | argv[1] = "cpu.rrd"; 126 | argv[2] = buf; 127 | argv[3] = 0; 128 | rrd_update(3, argv); 129 | } 130 | cpuovfl: 131 | memcpy(last_cp_times, cp_times, sizeof(cp_times)); 132 | 133 | /* VM stats */ 134 | proc = fopen("/proc/meminfo", "r"); 135 | i = 0; 136 | while (fgets(buf, sizeof(buf), proc)) { 137 | if (prefmatch(buf, "MemTotal:")) { 138 | i++; 139 | vm_MemTotal = atoi(buf + sizeof("MemTotal:")); 140 | } else if (prefmatch(buf, "MemFree:")) { 141 | i++; 142 | vm_MemFree = atoi(buf + sizeof("MemFree:")); 143 | } else if (prefmatch(buf, "Buffers:")) { 144 | i++; 145 | vm_Buffers = atoi(buf + sizeof("Buffers:")); 146 | } else if (prefmatch(buf, "Cached:")) { 147 | i++; 148 | vm_Cached = atoi(buf + sizeof("Cached:")); 149 | } else if (prefmatch(buf, "SwapCached:")) { 150 | i++; 151 | vm_SwapCached = atoi(buf + sizeof("SwapCached:")); 152 | } else if (prefmatch(buf, "Shmem:")) { 153 | i++; 154 | vm_Shmem = atoi(buf + sizeof("Shmem:")); 155 | } else if (prefmatch(buf, "PageTables:")) { 156 | i++; 157 | vm_PageTables = atoi(buf + sizeof("PageTables:")); 158 | } else if (prefmatch(buf, "KernelStack:")) { 159 | i++; 160 | vm_KernelStack = atoi(buf + sizeof("KernelStack:")); 161 | } else if (prefmatch(buf, "Slab:")) { 162 | i++; 163 | vm_Slab = atoi(buf + sizeof("Slab:")); 164 | } else if (prefmatch(buf, "AnonPages:")) { 165 | i++; 166 | vm_AnonPages = atoi(buf + sizeof("AnonPages:")); 167 | } else if (prefmatch(buf, "SwapTotal:")) { 168 | i++; 169 | vm_SwapTotal = atoi(buf + sizeof("SwapTotal:")); 170 | } else if (prefmatch(buf, "SwapFree:")) { 171 | i++; 172 | vm_SwapFree = atoi(buf + sizeof("SwapFree:")); 173 | } 174 | if (i == 12) 175 | break; 176 | } 177 | fclose(proc); 178 | assert(i == 12); 179 | // tautological assert: 180 | /*assert((vm_MemTotal - (vm_MemFree + vm_Buffers + vm_Cached + vm_Shmem)) + 181 | (vm_MemFree - vm_Shmem) + vm_Shmem + vm_Buffers + 182 | (vm_Cached + vm_Shmem) == vm_MemTotal); 183 | */ 184 | snprintf(buf, sizeof(buf), "N:%lu:%lu:%lu:%lu:%lu:%lu:%lu", 185 | 1024 * vm_AnonPages, 186 | 1024 * (vm_PageTables + vm_KernelStack), 187 | 1024 * vm_Slab, 188 | 1024 * vm_Buffers, 189 | 1024 * (vm_Cached + vm_SwapCached - vm_Shmem), 190 | 1024 * vm_Shmem, 191 | 1024 * (vm_MemTotal - (vm_MemFree + vm_AnonPages + 192 | vm_PageTables + vm_KernelStack + vm_Slab + 193 | vm_Buffers + vm_Cached + vm_SwapCached))); 194 | argv[0] = "update"; 195 | argv[1] = "mem.rrd"; 196 | argv[2] = buf; 197 | argv[3] = 0; 198 | rrd_update(3, argv); 199 | snprintf(buf, sizeof(buf), "N:%li", vm_SwapTotal - vm_SwapFree); 200 | argv[0] = "update"; 201 | argv[1] = "swap.rrd"; 202 | argv[2] = buf; 203 | argv[3] = 0; 204 | rrd_update(3, argv); 205 | 206 | /* load average */ 207 | proc = fopen("/proc/loadavg", "r"); 208 | i = fscanf(proc, "%*f %f %f", &loadavg5, &loadavg15); 209 | assert(i == 2); 210 | fclose(proc); 211 | snprintf(buf, sizeof(buf), "N:%.6f:%.6f", loadavg5, loadavg15); 212 | argv[0] = "update"; 213 | argv[1] = "loadavg.rrd"; 214 | argv[2] = buf; 215 | argv[3] = 0; 216 | rrd_update(3, argv); 217 | 218 | #ifdef HAVE_TEMP 219 | /* cpu temperature */ 220 | snprintf(buf, sizeof(buf), "N:%.3f", 221 | temp_sum / 1000.0 / (float) TEMPSAMPLES); 222 | temp_sum = 0; 223 | argv[0] = "update"; 224 | argv[1] = "temp.rrd"; 225 | argv[2] = buf; 226 | argv[3] = 0; 227 | rrd_update(3, argv); 228 | #endif 229 | 230 | /* network usage */ 231 | proc = fopen("/proc/net/dev", "r"); 232 | i = 0; 233 | ibytes = obytes = 0; 234 | while (fgets(buf, sizeof(buf), proc)) { 235 | if (i++ < 2) 236 | /* skip first two lines */ 237 | continue; 238 | j = sscanf(buf, "%15s %Lu %*d %*d %*d %*d %*d %*d %*d %Lu", 239 | devname, &ibytes_d, &obytes_d); 240 | assert(j == 3); 241 | if (!strcmp(devname, "lo:")) 242 | /* skip loopback interface */ 243 | continue; 244 | ibytes += ibytes_d; 245 | obytes += obytes_d; 246 | } 247 | fclose(proc); 248 | if (last_valid && 249 | ibytes >= last_ibytes && 250 | obytes >= last_obytes) { 251 | snprintf(buf, sizeof(buf), "N:%Lu:%Lu", 252 | (ibytes-last_ibytes)/HEARTBEAT, 253 | (obytes-last_obytes)/HEARTBEAT); 254 | argv[0] = "update"; 255 | argv[1] = "traffic.rrd"; 256 | argv[2] = buf; 257 | argv[3] = 0; 258 | rrd_update(3, argv); 259 | } 260 | last_ibytes = ibytes; 261 | last_obytes = obytes; 262 | 263 | /* disk I/O */ 264 | proc = fopen("/proc/diskstats", "r"); 265 | reads = writes = 0; 266 | while (fgets(buf, sizeof(buf), proc)) { 267 | j = sscanf(buf, "%u %u %s %*u %*u %Lu %*u %*u %*u %Lu", &major, 268 | &minor, devname, &reads_d, &writes_d); 269 | if (j == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR 270 | && major != RAMDISK_MAJOR && major != LOOP_MAJOR 271 | && major != DM_MAJOR_CRYPT && major != DM_MAJOR_RAID) { 272 | if (is_disk(devname)) { 273 | reads += reads_d; 274 | writes += writes_d; 275 | } 276 | } 277 | } 278 | fclose(proc); 279 | if (last_valid && 280 | reads >= last_reads && 281 | writes >= last_writes) { 282 | snprintf(buf, sizeof(buf), "N:%Lu:%Lu", 283 | 4096*(reads-last_reads)/HEARTBEAT, 284 | 4096*(writes-last_writes)/HEARTBEAT); 285 | argv[0] = "update"; 286 | argv[1] = "disk.rrd"; 287 | argv[2] = buf; 288 | argv[3] = 0; 289 | rrd_update(3, argv); 290 | } 291 | last_reads = reads; 292 | last_writes = writes; 293 | 294 | last_valid = 1; 295 | } 296 | 297 | #ifdef HAVE_TEMP 298 | static void measure_temp() 299 | { 300 | FILE *proc; 301 | int i, temp; 302 | 303 | proc = fopen(TEMPSENSOR, "r"); 304 | if (proc == NULL) { 305 | fprintf(stderr, "Adjust TEMPSENSOR define in rrdupd.c!\n"); 306 | exit(1); 307 | } 308 | i = fscanf(proc, "%d", &temp); 309 | assert(i == 1); 310 | fclose(proc); 311 | 312 | temp_sum += temp; 313 | } 314 | #endif 315 | 316 | int main(int argc, char **argv) 317 | { 318 | struct timespec tp; 319 | time_t nextrun; 320 | #ifdef HAVE_TEMP 321 | int cnt = 0; 322 | #endif 323 | 324 | /* go to directory where binary resides, as we expect rrd files there */ 325 | assert(argc == 1); 326 | if (chdir(dirname(argv[0])) != 0) 327 | perror("chdir"); 328 | 329 | clock_gettime(CLOCK_MONOTONIC, &tp); 330 | nextrun = tp.tv_sec; 331 | 332 | while (1) { 333 | #ifdef HAVE_TEMP 334 | nextrun += HEARTBEAT / TEMPSAMPLES; 335 | measure_temp(); 336 | 337 | if (++cnt == TEMPSAMPLES) { 338 | update(); 339 | cnt = 0; 340 | } 341 | #else 342 | nextrun += HEARTBEAT; 343 | update(); 344 | #endif 345 | 346 | clock_gettime(CLOCK_MONOTONIC, &tp); 347 | if (nextrun - tp.tv_sec > 0) 348 | sleep(nextrun - tp.tv_sec); 349 | } 350 | } 351 | -------------------------------------------------------------------------------- /serverstat-linux.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=serverstat-linux rrdupd daemon 3 | 4 | [Service] 5 | Type=simple 6 | WorkingDirectory=/srv/http/serverstat-linux 7 | ExecStart=/srv/http/serverstat-linux/rrdupd 8 | Restart=on-failure 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /stat.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # Copyright (c) 2011, 2014 Alexander Graf. All rights reserved. 4 | 5 | use strict; 6 | use warnings; 7 | 8 | use CGI 'param','header'; 9 | use File::Basename; 10 | 11 | my $rrdtool_exe = "/usr/bin/rrdtool"; 12 | 13 | my @ranges = ( 14 | { 15 | 'name' => "day", 16 | 'sec' => 86400 17 | }, 18 | { 19 | 'name' => "week", 20 | 'sec' => 604800 21 | }, 22 | { 23 | 'name' => "eight weeks", 24 | 'sec' => 4838400 25 | }, 26 | { 27 | 'name' => "year", 28 | 'sec' => 31536000 29 | } 30 | ); 31 | 32 | # get total amount of RAM for appropriate scaling 33 | my $MemTotal; 34 | open INFILE, "< /proc/meminfo"; 35 | while () { 36 | if (/^MemTotal: *(\d*)/) { 37 | $MemTotal = $1*1024; 38 | last; 39 | } 40 | } 41 | close INFILE; 42 | 43 | my @graphs = ( 44 | { 45 | 'name' => "loadavg", 46 | 'title' => "Load Average", 47 | 'create' => "-v \"Running Threads\" -l 0 DEF:la5=loadavg.rrd:la5:AVERAGE DEF:la15=loadavg.rrd:la15:AVERAGE LINE:la5\#ff0000:\"Five Minutes Average\" LINE:la15\#00ffff:\"15 Minutes Average\"" 48 | }, 49 | 50 | { 51 | 'name' => "cpu", 52 | 'title' => "CPU Usage", 53 | 'create' => "-v \"CPU Usage\" -r -l 0 -u 1 DEF:user=cpu.rrd:user:AVERAGE DEF:nice=cpu.rrd:nice:AVERAGE DEF:system=cpu.rrd:system:AVERAGE DEF:iowait=cpu.rrd:iowait:AVERAGE DEF:irq=cpu.rrd:irq:AVERAGE DEF:softirq=cpu.rrd:softirq:AVERAGE AREA:user\#ff0000:\"user\" AREA:nice\#ffff00:\"nice\":STACK AREA:system\#00ff00:\"system\":STACK AREA:iowait\#00ffff:\"iowait\":STACK AREA:irq\#0000ff:\"irq\":STACK AREA:softirq\#ff00ff:\"softirq\":STACK" 54 | }, 55 | 56 | { 57 | 'name' => "temp", 58 | 'title' => "CPU Temperature", 59 | 'create' => "-v \"Degree Celsius\" -g -A DEF:c0=temp.rrd:core:AVERAGE LINE:c0\#ff0000:\"CPU\"" 60 | }, 61 | 62 | { 63 | 'name' => "disk", 64 | 'title' => "Disk R/W", 65 | 'create' => "-v \"Bits/Second\" DEF:r=disk.rrd:r:AVERAGE DEF:w=disk.rrd:w:AVERAGE CDEF:wb=w,-1,* AREA:r\#ff0000:\"Read\" HRULE:0#000000 AREA:wb\#00ffff:\"Write\"" 66 | }, 67 | 68 | { 69 | 'name' => "traffic", 70 | 'title' => "Network RX/TX", 71 | 'create' => "-v \"Bits/Second\" DEF:rx=traffic.rrd:rx:AVERAGE DEF:tx=traffic.rrd:tx:AVERAGE CDEF:rxb=rx,-8,* CDEF:txb=tx,8,* AREA:txb\#ff0000:\"Upload\" AREA:rxb\#00ffff:\"Download\" HRULE:0#000000" 72 | }, 73 | 74 | { 75 | 'name' => "mem", 76 | 'title' => "RAM Usage", 77 | 'create' => "-v \"Bytes\" -b 1024 -r -l 0 -u ${MemTotal} DEF:user=mem.rrd:user:AVERAGE DEF:kernel=mem.rrd:kernel:AVERAGE DEF:kernel-cache=mem.rrd:kernel-cache:AVERAGE DEF:buffers=mem.rrd:buffers:AVERAGE DEF:cached=mem.rrd:cached:AVERAGE DEF:shared=mem.rrd:shared:AVERAGE DEF:other=mem.rrd:other:AVERAGE AREA:user\#ff0000:\"user\" AREA:kernel\#48ff00:\"kernel\":STACK AREA:kernel-cache\#00ff91:\"kernel-cache\":STACK AREA:buffers\#0091ff:\"buffers\":STACK AREA:cached\#4800ff:\"cached\":STACK AREA:shared\#ffda00:\"shared\":STACK AREA:other\#ff00da:\"other\":STACK" 78 | }, 79 | 80 | { 81 | 'name' => "swap", 82 | 'title' => "Swap", 83 | 'create' => "-v \"Bytes\" -b 1024 -g -l 0 DEF:usedb=swap.rrd:used:AVERAGE CDEF:used=usedb,1024,* LINE:used\#ff0000:\"Used\"" 84 | }, 85 | 86 | 87 | 88 | ); 89 | 90 | my $r; 91 | 92 | $r = int(param('r')); 93 | $r = 0 unless $r; 94 | $r = 0 if $r < 0; 95 | $r = 0 unless $ranges[$r]; 96 | 97 | chdir(dirname(__FILE__)); 98 | 99 | for my $i ( 0 .. $#graphs ) { 100 | system "$rrdtool_exe graph --lazy $graphs[$i]{'name'}$r.png --start -$ranges[$r]{'sec'} -w 720 -h 200 $graphs[$i]{'create'} >/dev/null"; 101 | } 102 | 103 | print header; 104 | 105 | print "\n"; 106 | print "\n"; 107 | print ""; 108 | 109 | print ""; 110 | print "system usage statistics"; 111 | print ""; 112 | print ""; 113 | 114 | print ""; 115 | #print "

Serverstatistik

"; 116 | #print "

Copyright (c) 2011, 2014 Alexander Graf.

"; 117 | 118 | #print "
"; system("uptime; echo; echo -n Temp:; cat /sys/devices/virtual/thermal/thermal_zone0/temp; echo; df -h; echo; free -tm"); print "
"; 119 | 120 | print "

Show period of last: "; 121 | 122 | 123 | for (my $range = 0; $range < @ranges; $range++) { 124 | print " - " unless $range == 0; 125 | if ($range == $r) { 126 | print "$ranges[$range]{'name'}"; 127 | } else { 128 | print "$ranges[$range]{'name'}"; 129 | } 130 | } 131 | 132 | print "

"; 133 | 134 | for my $i ( 0 .. $#graphs ) { 135 | print "

$graphs[$i]{'title'}

"; 136 | print "\"$graphs[$i]{'title'}\""; 137 | } 138 | 139 | print ""; 140 | print ""; 141 | --------------------------------------------------------------------------------