1 /*----------------------------------------------------------------------------*/
2 /* Xymon demonstration tool. */
3 /* */
4 /* This tool fakes several hosts that can be tested by Xymon, both with */
5 /* fake network services and fake client data. It is used to demonstrate */
6 /* features in Xymon. */
7 /* */
8 /* Copyright (C) 2005-2011 Henrik Storner <henrik@hswn.dk> */
9 /* */
10 /* This program is released under the GNU General Public License (GPL), */
11 /* version 2. See the file "COPYING" for details. */
12 /* */
13 /*----------------------------------------------------------------------------*/
14
15 static char rcsid[] = "$Id: demotool.c 6712 2011-07-31 21:01:52Z storner $";
16
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <sys/socket.h>
20 #include <limits.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <dirent.h>
26 #include <fcntl.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <sys/time.h>
30 #include <time.h>
31 #include <signal.h>
32 #include <errno.h>
33 #ifdef HAVE_SYS_SELECT_H
34 #include <sys/select.h>
35 #endif
36
37 char *CONFIGDIR = "/etc/hdemo";
38 struct sockaddr_in srvaddr;
39 volatile int reconfig = 1;
40
41 typedef struct netsvc_t {
42 int listenfd;
43 int delay;
44 char *response;
45 int respsize;
46 struct netsvc_t *next;
47 } netsvc_t;
48 netsvc_t *nethead = NULL;
49
50 typedef struct active_t {
51 int fd;
52 netsvc_t *svc;
53 struct timeval rbegin;
54 char *respbuf, *respptr;
55 int readdone;
56 int bytesleft;
57 struct active_t *next;
58 } active_t;
59 active_t *acthead = NULL;
60
61 typedef struct client_t {
62 time_t lastupd;
63 char *hostname;
64 char *ostype;
65 time_t bootup;
66 double minload, maxload;
67 char *msg;
68 struct client_t *next;
69 } client_t;
70 client_t *clihead = NULL;
71
72 static DIR *confdir = NULL;
73 struct dirent *dent = NULL;
74 static char *path = NULL;
75
nextservice(char * dirname,char * svc)76 char *nextservice(char *dirname, char *svc)
77 {
78 struct stat st;
79 char fn[PATH_MAX];
80 FILE *fd;
81 char *result;
82
83 if (dirname) {
84 if (confdir) closedir(confdir);
85 if (path) free(path);
86 confdir = opendir(dirname);
87 path = strdup(dirname);
88 }
89
90 do {
91 do { dent = readdir(confdir); } while (dent && (*(dent->d_name) == '.'));
92
93 if (!dent) {
94 closedir(confdir);
95 free(path);
96 path = NULL;
97 confdir = NULL;
98 dent = NULL;
99 return NULL;
100 }
101
102 sprintf(fn, "%s/%s/%s", path, dent->d_name, svc);
103 } while ( (stat(fn, &st) == -1) || ((fd = fopen(fn, "r")) == NULL) );
104
105 result = (char *)malloc(st.st_size+1);
106 fread(result, 1, st.st_size, fd);
107 *(result + st.st_size) = '\0';
108 fclose(fd);
109
110 return result;
111 }
112
svcattrib(char * attr)113 char *svcattrib(char *attr)
114 {
115 struct stat st;
116 char fn[PATH_MAX];
117 FILE *fd;
118 char *result;
119
120 if (!dent) return NULL;
121
122 if (!attr) {
123 sprintf(fn, "%s/%s", path, dent->d_name);
124 return strdup(fn);
125 }
126
127 sprintf(fn, "%s/%s/%s", path, dent->d_name, attr);
128 if (stat(fn, &st) == -1) return NULL;
129 fd = fopen(fn, "r"); if (!fd) return NULL;
130
131 result = (char *)calloc(1, st.st_size+1);
132 fread(result, 1, st.st_size, fd);
133 fclose(fd);
134
135 return result;
136 }
137
addtobuffer(char ** buf,int * bufsz,char * newtext)138 void addtobuffer(char **buf, int *bufsz, char *newtext)
139 {
140 if (*buf == NULL) {
141 *bufsz = strlen(newtext) + 4096;
142 *buf = (char *) malloc(*bufsz);
143 **buf = '\0';
144 }
145 else if ((strlen(*buf) + strlen(newtext) + 1) > *bufsz) {
146 *bufsz += strlen(newtext) + 4096;
147 *buf = (char *) realloc(*buf, *bufsz);
148 }
149
150 strcat(*buf, newtext);
151 }
152
clientdata(char * cpath)153 char *clientdata(char *cpath)
154 {
155 char *res = NULL;
156 int ressz = 0;
157 DIR *cdir;
158 struct dirent *d;
159 char fn[PATH_MAX];
160 struct stat st;
161 int n;
162 FILE *fd;
163 char buf[4096];
164
165 cdir = opendir(cpath);
166 while ((d = readdir(cdir)) != NULL) {
167 if (strncmp(d->d_name, "client_", 7) != 0) continue;
168
169 sprintf(fn, "%s/%s", cpath, d->d_name);
170 if (stat(fn, &st) == -1) continue;
171 fd = fopen(fn, "r"); if (fd == NULL) continue;
172
173 sprintf(buf, "[%s]\n", d->d_name+7);
174 addtobuffer(&res, &ressz, buf);
175
176 while ((n = fread(buf, 1, sizeof(buf)-1, fd)) > 0) {
177 *(buf+n) = '\0';
178 addtobuffer(&res, &ressz, buf);
179 }
180
181 fclose(fd);
182 }
183 closedir(cdir);
184
185 if (!res) res = strdup("");
186
187 return res;
188 }
189
timeafter(struct timeval * lim,struct timeval * now)190 int timeafter(struct timeval *lim, struct timeval *now)
191 {
192 if (now->tv_sec > lim->tv_sec) return 1;
193 if (now->tv_sec < lim->tv_sec) return 0;
194 return (now->tv_usec >= lim->tv_usec);
195 }
196
setuplisteners(void)197 void setuplisteners(void)
198 {
199 netsvc_t *nwalk;
200 char *lspec;
201 struct sockaddr_in laddr;
202
203 nwalk = nethead;
204 while (nwalk) {
205 netsvc_t *tmp = nwalk;
206 nwalk = nwalk->next;
207
208 if (tmp->listenfd > 0) close(tmp->listenfd);
209 if (tmp->response) free(tmp->response);
210 free(tmp);
211 }
212 nethead = NULL;
213
214 lspec = nextservice(CONFIGDIR, "listen");
215 while (lspec) {
216 char *p, *listenip = NULL;
217 int listenport = -1;
218 int lsocket, opt;
219
220 p = strchr(lspec, ':'); if (p) { *p = '\0'; p++; listenip = lspec; listenport = atoi(p); }
221
222 if (listenip && (listenport > 0)) {
223 memset(&laddr, 0, sizeof(laddr));
224 inet_aton(listenip, (struct in_addr *) &laddr.sin_addr.s_addr);
225 laddr.sin_port = htons(listenport);
226 laddr.sin_family = AF_INET;
227 lsocket = socket(AF_INET, SOCK_STREAM, 0);
228
229 if (lsocket != -1) {
230 opt = 1;
231 setsockopt(lsocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
232 fcntl(lsocket, F_SETFL, O_NONBLOCK);
233
234 if ( (bind(lsocket, (struct sockaddr *)&laddr, sizeof(laddr)) != -1) &&
235 (listen(lsocket, 10) != -1) ) {
236 netsvc_t *newitem = malloc(sizeof(netsvc_t));
237
238 newitem->listenfd = lsocket;
239 newitem->response = svcattrib("response");
240 newitem->respsize = (newitem->response ? strlen(newitem->response) : 0);
241 p = svcattrib("delay");
242 newitem->delay = (p ? atoi(p) : 0);
243 newitem->next = nethead;
244 nethead = newitem;
245 }
246 }
247 }
248
249 free(lspec);
250 lspec = nextservice(NULL, "listen");
251 }
252 }
253
setupclients(void)254 void setupclients(void)
255 {
256 client_t *cwalk;
257 char *cspec;
258
259 cwalk = clihead;
260 while (cwalk) {
261 client_t *tmp = cwalk;
262 cwalk = cwalk->next;
263
264 if (tmp->hostname) free(tmp->hostname);
265 if (tmp->ostype) free(tmp->ostype);
266 if (tmp->msg) free(tmp->msg);
267 free(tmp);
268 }
269 clihead = NULL;
270
271 cspec = nextservice(CONFIGDIR, "client");
272 while (cspec) {
273 char *p;
274
275 p = strchr(cspec, ':');
276 if (p) {
277 client_t *newitem = (client_t *)malloc(sizeof(client_t));
278
279 *p = '\0';
280 newitem->lastupd = 0;
281 newitem->hostname = strdup(cspec);
282 newitem->ostype = strdup(p+1);
283 while ((p = strchr(newitem->hostname, '.')) != NULL) *p = ',';
284 p = svcattrib("uptime");
285 if (p) newitem->bootup = time(NULL) - 60*atoi(p);
286 else newitem->bootup = time(NULL);
287 p = svcattrib("minload");
288 if (p) newitem->minload = atof(p); else newitem->minload = 0.2;
289 p = svcattrib("maxload");
290 if (p) newitem->maxload = atof(p); else newitem->maxload = 1.0;
291 newitem->msg = clientdata(svcattrib(NULL));
292 newitem->next = clihead;
293 clihead = newitem;
294 }
295
296 free(cspec);
297 cspec = nextservice(NULL, "client");
298 }
299 }
300
do_select(void)301 void do_select(void)
302 {
303 fd_set readfds, writefds;
304 int maxfd, n;
305 netsvc_t *nwalk;
306 active_t *awalk, *aprev;
307 struct timeval now, start;
308 struct timezone tz;
309 struct timeval tmo;
310 char rbuf[4096];
311
312 gettimeofday(&start, &tz);
313 do {
314 gettimeofday(&now, &tz);
315 FD_ZERO(&readfds); FD_ZERO(&writefds); maxfd = -1;
316
317 nwalk = nethead;
318 while (nwalk) {
319 if (nwalk->listenfd) {
320 FD_SET(nwalk->listenfd, &readfds);
321 if (nwalk->listenfd > maxfd) maxfd = nwalk->listenfd;
322 }
323 nwalk = nwalk->next;
324 }
325
326 awalk = acthead;
327 while (awalk) {
328 if (awalk->fd) {
329 if (!awalk->readdone) {
330 FD_SET(awalk->fd, &readfds);
331 if (awalk->fd > maxfd) maxfd = awalk->fd;
332 }
333 if (timeafter(&awalk->rbegin, &now)) {
334 FD_SET(awalk->fd, &writefds);
335 if (awalk->fd > maxfd) maxfd = awalk->fd;
336 }
337 }
338 awalk = awalk->next;
339 }
340
341 tmo.tv_sec = 0; tmo.tv_usec = 100000;
342 n = select(maxfd+1, &readfds, &writefds, NULL, &tmo);
343 } while ((n == 0) && ((now.tv_sec - start.tv_sec) < 10));
344
345 if (n <= 0) return;
346
347 gettimeofday(&now, &tz);
348
349 awalk = acthead; aprev = NULL;
350 while (awalk) {
351 if (awalk->fd && !awalk->readdone && FD_ISSET(awalk->fd, &readfds)) {
352 n = read(awalk->fd, rbuf, sizeof(rbuf));
353 if (n <= 0) awalk->readdone = 1;
354 }
355
356 if (awalk->fd && awalk->respptr && FD_ISSET(awalk->fd, &writefds)) {
357 n = write(awalk->fd, awalk->respptr, awalk->bytesleft);
358 if (n > 0) { awalk->respptr += n; awalk->bytesleft -= n; }
359 }
360 else n = 0;
361
362 if ((awalk->bytesleft == 0) || (n < 0)) {
363 if (awalk->respbuf) free(awalk->respbuf);
364 close(awalk->fd);
365 awalk->fd = 0;
366 }
367
368 if (awalk->fd) {
369 aprev = awalk; awalk = awalk->next;
370 }
371 else {
372 active_t *tmp = awalk;
373
374 awalk = awalk->next;
375
376 if (aprev) aprev->next = awalk;
377 else acthead = awalk;
378
379 free(tmp);
380 }
381 }
382
383 nwalk = nethead;
384 while (nwalk) {
385 int newfd;
386
387 if (nwalk->listenfd && FD_ISSET(nwalk->listenfd, &readfds)) {
388 while ((newfd = accept(nwalk->listenfd, NULL, 0)) > 0) {
389 /* Pick up a new connection */
390 fcntl(newfd, F_SETFL, O_NONBLOCK);
391 active_t *newitem = (active_t *)malloc(sizeof(active_t));
392 newitem->fd = newfd;
393 newitem->rbegin.tv_sec = now.tv_sec + nwalk->delay;
394 newitem->rbegin.tv_usec = now.tv_usec + (random() % 1000000);
395 if (newitem->rbegin.tv_usec > 1000000) {
396 newitem->rbegin.tv_usec -= 1000000;
397 newitem->rbegin.tv_sec++;
398 }
399 newitem->svc = nwalk;
400 if (nwalk->response) {
401 newitem->respbuf = strdup(nwalk->response);
402 newitem->respptr = newitem->respbuf;
403 newitem->bytesleft = nwalk->respsize;
404 }
405 else {
406 newitem->respbuf = NULL;
407 newitem->respptr = NULL;
408 newitem->bytesleft = 0;
409 }
410 newitem->readdone = 0;
411 newitem->next = acthead;
412 acthead = newitem;
413 }
414 }
415 nwalk = nwalk->next;
416 }
417 }
418
do_clients(void)419 void do_clients(void)
420 {
421 client_t *cwalk;
422 active_t *conn;
423 time_t now = time(NULL);
424 time_t uptime;
425 int updays, uphours, upmins;
426 int n;
427 struct tm *tm;
428 double curload;
429
430 cwalk = clihead;
431 while (cwalk && ((cwalk->lastupd + 60) > now)) cwalk = cwalk->next;
432 if (!cwalk) return;
433
434 cwalk->lastupd = now;
435 tm = localtime(&now);
436 uptime = now - cwalk->bootup;
437 updays = uptime / 86400;
438 uphours = (uptime % 86400) / 3600;
439 upmins = (uptime % 3600) / 60;
440 curload = cwalk->minload + ((random() % 1000) / 1000.0) * (cwalk->maxload - cwalk->minload);
441
442 conn = malloc(sizeof(active_t));
443 conn->fd = socket(AF_INET, SOCK_STREAM, 0);
444 fcntl(conn->fd, F_SETFL, O_NONBLOCK);
445 conn->rbegin.tv_sec = now;
446 conn->rbegin.tv_usec = 0;
447 conn->svc = NULL;
448 conn->respbuf = (char *)malloc(strlen(cwalk->msg) + 4096);
449 sprintf(conn->respbuf, "client %s.%s\n[date]\n%s[uptime]\n %02d:%02d:%02d up %d days, %d:%02d, 1 users, load average: 0.21, %5.2f, 0.25\n\n%s\n",
450 cwalk->hostname, cwalk->ostype,
451 ctime(&now),
452 tm->tm_hour, tm->tm_min, tm->tm_sec,
453 updays, uphours, upmins,
454 curload, cwalk->msg);
455 conn->respptr = conn->respbuf;
456 conn->bytesleft = strlen(conn->respbuf);
457 conn->readdone = 0;
458
459 n = connect(conn->fd, (struct sockaddr *)&srvaddr, sizeof(srvaddr));
460
461 if ((n == 0) || ((n == -1) && (errno == EINPROGRESS))) {
462 conn->next = acthead;
463 acthead = conn;
464 }
465 else {
466 close(conn->fd);
467 free(conn->respbuf);
468 free(conn);
469 }
470 }
471
sigmisc_handler(int signum)472 void sigmisc_handler(int signum)
473 {
474 if (signum == SIGHUP) reconfig = 1;
475 }
476
main(int argc,char * argv[])477 int main(int argc, char *argv[])
478 {
479 int argi;
480 struct sigaction sa;
481 time_t lastconf = 0;
482
483 memset(&srvaddr, 0, sizeof(srvaddr));
484 srvaddr.sin_port = htons(1984);
485 srvaddr.sin_family = AF_INET;
486 inet_aton("127.0.0.1", (struct in_addr *) &srvaddr.sin_addr.s_addr);
487
488 for (argi = 1; (argi < argc); argi++) {
489 if (strncmp(argv[argi], "--confdir=", 10) == 0) {
490 char *p = strchr(argv[argi], '=');
491 CONFIGDIR = strdup(p+1);
492 }
493 else if (strncmp(argv[argi], "--srvip=", 8) == 0) {
494 char *p = strchr(argv[argi], '=');
495 inet_aton(p+1, (struct in_addr *) &srvaddr.sin_addr.s_addr);
496 }
497 }
498
499 memset(&sa, 0, sizeof(sa));
500 sa.sa_handler = sigmisc_handler;
501 sigaction(SIGHUP, &sa, NULL);
502
503 while (1) {
504 if (reconfig || (time(NULL) > (lastconf+60))) {
505 printf("(Re)loading config\n");
506 setuplisteners();
507 setupclients();
508 reconfig = 0;
509 lastconf = time(NULL);
510 }
511
512 do_clients();
513 do_select();
514 }
515
516 return 0;
517 }
518
519