ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/cms/source/idar/idar.c
Revision: 1.1
Committed: Fri Mar 28 15:37:05 2003 UTC (21 years, 2 months ago) by pajs
Content type: text/plain
Branch: MAIN
Log Message:
idar is a "top like" client which displays data collected by ihost.
It currently displays cpu, load, pageing, mem, swap, net and disk io.
There is infastructure to display much more in the future, and to be able
to sort on more than just CPU.

genmergesort is macro written by mtr to sort a linked list.

Inital version 1.0 This compiles and works. It requires libxml2 (but it
may work against libxml, but this has not been checked). It also requires
ukcprog. This will not currently compile on linux (as it lacks strlcpy).

Usage is

idar <-d number_lines_to_display> machine_name port <"fqdn1;fqdn2;fqdn3">

File Contents

# Content
1 #include <stdio.h>
2 #include <string.h>
3 #include <sys/types.h>
4 #include <sys/socket.h>
5 #include <unistd.h>
6 #include <stdlib.h>
7 #include <ukcprog.h>
8
9 #include <libxml/xmlmemory.h>
10 #include <libxml/parser.h>
11 #include "genmergesort.h"
12
13 struct host_line_t{
14 char *hostname;
15 int line;
16
17 struct host_line_t *next;
18 };
19 typedef struct host_line_t host_line_list_t;
20
21 struct diskio_data_t{
22 char *name;
23
24 long long read;
25 long long write;
26
27 struct diskio_data_t *next;
28 };
29 typedef struct diskio_data_t diskio_data_list_t;
30
31 struct network_data_t{
32 char *name;
33
34 long long rx;
35 long long tx;
36
37 struct network_data_t *next;
38 };
39 typedef struct network_data_t network_data_list_t;
40
41 /*
42 struct disk_data_t{
43 char *name;
44 char *mount_pnt;
45
46 long long total_space;
47 long long total_used;
48 long long total_avail;
49 // Other data we are less intrested in are not stored
50
51 struct disk_data_t *next;
52 };
53 typedef struct disk_data_t disk_data_list_t;
54 */
55 #define MAXHOSTNAMESIZE 10
56 struct machine_data_t{
57
58 char *name;
59
60 char sysname[MAXHOSTNAMESIZE+1];
61
62 double cpu_user;
63 double cpu_idle;
64 double cpu_iowait;
65 double cpu_kernel;
66 double cpu_swap;
67 double cpu_used; /* 100 - idle */
68
69 long long memory_total;
70 long long memory_free;
71 long long memory_used;
72 double memory_used_pecent;
73
74 long long swap_total;
75 long long swap_free;
76 long long swap_used;
77 double swap_used_pecent;
78
79 int pages_in;
80 int pages_out;
81
82 double load_1;
83 double load_5;
84 double load_15;
85
86 int processes_total;
87 int processes_sleeping;
88 int processes_cpu;
89 int processes_zombie;
90 int processes_stopped;
91
92 network_data_list_t *network_data_list;
93 long long network_io_total_tx;
94 long long network_io_total_rx;
95
96 diskio_data_list_t *diskio_data_list;
97 long long disk_io_total_write;
98 long long disk_io_total_read;
99
100 /* Maybe in the future */
101 /*
102 disk_data_list_t disk_data_list;
103 double disk_total_used;
104 */
105
106 struct machine_data_t *next;
107 };
108
109 typedef struct machine_data_t machine_data_list_t;
110
111 typedef struct{
112 int cpu_user;
113 int cpu_idle;
114 int cpu_iowait;
115 int cpu_kernel;
116 int cpu_swap;
117 int cpu_used;
118
119 int memory_total;
120 int memory_free;
121 int memory_used;
122 int memory_used_pecent;
123
124 int swap_total;
125 int swap_free;
126 int swap_used;
127 int swap_used_pecent;
128
129 int load_1;
130 int load_5;
131 int load_15;
132
133 int pages_in;
134 int pages_out;
135
136 int processes_total;
137 int processes_sleeping;
138 int processes_cpu;
139 int processes_zombie;
140 int processes_stopped;
141
142 int network_io_total_tx;
143 int network_io_total_rx;
144 int network_all_stats;
145
146 int disk_io_total_write;
147 int disk_io_total_read;
148 int disk_io_all_stats;
149
150 int disk_total_used;
151 int disk_all_stats;
152 }display_config_t;
153
154 GENERIC_MERGE_SORT(static, sort_machine_stats, machine_data_list_t, next)
155
156 int cmp_cpu(machine_data_list_t *a, machine_data_list_t *b){
157
158 if(a->cpu_used == b->cpu_used){
159 if(a->load_1 == b->load_1) return 0;
160 if(a->load_1 > b->load_1){
161 return -1;
162 }else{
163 return 1;
164 }
165 }
166
167 if((a->cpu_used) > (b->cpu_used)){
168 return -1;
169 }else{
170 return 1;
171 }
172 }
173
174 FILE *create_tcp_connection(char *hostname, int port){
175 int sock;
176 struct sockaddr_in addr;
177 struct in_addr haddr;
178 FILE *f;
179
180 if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))<0){
181 return NULL;
182 }
183
184 if((get_host_addr(hostname, &haddr))!=0){
185 close(sock);
186 return NULL;
187 }
188
189 memset(&addr, 0, sizeof(addr));
190 addr.sin_family = AF_INET;
191 memcpy(&addr.sin_addr, &haddr, sizeof(haddr));
192 addr.sin_port = htons(port);
193
194 if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) !=0){
195 close(sock);
196 return NULL;
197 }
198
199 if((f=fdopen(sock, "r+"))==NULL){
200 close(sock);
201 return NULL;
202 }
203
204 return f;
205 }
206
207 int tcp_comm(FILE *f, char *send, char **response, char *expected){
208
209 if(send!=NULL){
210 fprintf(f, "%s\n", send);
211 }
212 fflush(f);
213 *response=fpgetline(f);
214 fseek(f, 0, SEEK_CUR);
215
216 if( (*response==NULL) || (strcmp(*response, "ERROR")==0) ) return -1;
217
218 if(expected==NULL) return 0;
219
220 if((strcmp(expected, *response))==0) return 0;
221
222 return -1;
223 }
224
225 /* Takes a xml char * and a machine_data_list_t. This will parse
226 * the xml and put it into the correct entry of the machine_data
227 * linked list. This will return the number of entries in the linked
228 * list, or -1 on a error.
229 */
230 int parse_xml(char *xml, machine_data_list_t **md){
231 xmlDocPtr doc;
232 xmlNodePtr cur;
233 xmlNodePtr ele;
234 xmlAttr *list;
235
236 static int num_hosts=0;
237
238 machine_data_list_t *machine_data_list=*md;
239
240 int found_host=0;
241
242 char *hostname=NULL;
243 network_data_list_t *network_data_list=NULL;
244 network_data_list_t *ndl_ptr=NULL;
245 diskio_data_list_t *diskio_data_list=NULL;
246 diskio_data_list_t *didl_ptr=NULL;
247 char *tmp;
248
249 doc=xmlParseDoc(xml);
250 if(doc==NULL) return -1;
251
252 cur = xmlDocGetRootElement(doc);
253 if(cur==NULL){
254 xmlFreeDoc(doc);
255 return -1;
256 }
257
258
259 /* Get the hostname */
260 list=cur->properties;
261 while(list!=NULL){
262 if((xmlStrcmp(list->name, (const xmlChar *) "machine_name"))==0){
263 hostname = xmlNodeGetContent(list->children);
264 if(hostname==NULL){
265 return -1;
266 }
267 }
268 list=list->next;
269 }
270 if(hostname==NULL){
271 return -1;
272 }
273
274 /* Get machine_data for host.. Or create if it doesn't have one */
275 while(machine_data_list!=NULL){
276 if((strncmp(machine_data_list->name, hostname, strlen(hostname)))==0){
277 found_host=1;
278 break;
279 }
280 machine_data_list=machine_data_list->next;
281 }
282
283 if(!found_host){
284 /* We didn't find this host, but we should be at the end of the list */
285 machine_data_list=malloc(sizeof(machine_data_list_t));
286 if(machine_data_list==NULL) return -1;
287 machine_data_list->next=(*md);
288 machine_data_list->name=hostname;
289 machine_data_list->network_data_list=NULL;
290 machine_data_list->diskio_data_list=NULL;
291 *md=machine_data_list;
292 num_hosts++;
293 }
294
295 /* Now we want to pull out the data */
296
297 cur = cur->xmlChildrenNode;
298 while(cur != NULL) {
299 ele=cur->xmlChildrenNode;
300 while(ele != NULL){
301
302 /* CPU Stats */
303 if(!xmlStrcmp(cur->name, (const xmlChar *) "cpu")){
304 tmp=xmlNodeGetContent(ele);
305 if(!xmlStrcmp(ele->name, (const xmlChar *) "user")){
306 machine_data_list->cpu_user=atof(tmp);
307 }
308 if(!xmlStrcmp(ele->name, (const xmlChar *) "kernel")){
309 machine_data_list->cpu_kernel=atof(tmp);
310 }
311 if(!xmlStrcmp(ele->name, (const xmlChar *) "idle")){
312 machine_data_list->cpu_idle=atof(tmp);
313 }
314 if(!xmlStrcmp(ele->name, (const xmlChar *) "iowait")){
315 machine_data_list->cpu_iowait=atof(tmp);
316 }
317 if(!xmlStrcmp(ele->name, (const xmlChar *) "swap")){
318 machine_data_list->cpu_iowait=atof(tmp);
319 }
320
321 if(tmp!=NULL) xmlFree(tmp);
322 }
323
324 /* Memory Stats */
325 if(!xmlStrcmp(cur->name, (const xmlChar *) "memory")){
326 tmp=xmlNodeGetContent(ele);
327 if(!xmlStrcmp(ele->name, (const xmlChar *) "total")){
328 machine_data_list->memory_total=atoll(tmp);
329 }
330 if(!xmlStrcmp(ele->name, (const xmlChar *) "free")){
331 machine_data_list->memory_free=atoll(tmp);
332 }
333 if(!xmlStrcmp(ele->name, (const xmlChar *) "used")){
334 machine_data_list->memory_used=atoll(tmp);
335 }
336
337 if(tmp!=NULL) xmlFree(tmp);
338 }
339
340 /* Load Stats */
341 if(!xmlStrcmp(cur->name, (const xmlChar *) "load")){
342 tmp=xmlNodeGetContent(ele);
343 if(!xmlStrcmp(ele->name, (const xmlChar *) "load1")){
344 machine_data_list->load_1=atof(tmp);
345 }
346 if(!xmlStrcmp(ele->name, (const xmlChar *) "load5")){
347 machine_data_list->load_5=atof(tmp);
348 }
349 if(!xmlStrcmp(ele->name, (const xmlChar *) "load15")){
350 machine_data_list->load_15=atof(tmp);
351 }
352
353 if(tmp!=NULL) xmlFree(tmp);
354 }
355
356 /* swap stats */
357 if(!xmlStrcmp(cur->name, (const xmlChar *) "swap")){
358 tmp=xmlNodeGetContent(ele);
359 if(!xmlStrcmp(ele->name, (const xmlChar *) "total")){
360 machine_data_list->swap_total=atoll(tmp);
361 }
362 if(!xmlStrcmp(ele->name, (const xmlChar *) "free")){
363 machine_data_list->swap_free=atoll(tmp);
364 }
365 if(!xmlStrcmp(ele->name, (const xmlChar *) "used")){
366 machine_data_list->swap_used=atoll(tmp);
367 }
368
369 if(tmp!=NULL) xmlFree(tmp);
370 }
371
372 /* Process stat */
373 if(!xmlStrcmp(cur->name, (const xmlChar *) "processes")){
374 tmp=xmlNodeGetContent(ele);
375 if(!xmlStrcmp(ele->name, (const xmlChar *) "sleeping")){
376 machine_data_list->processes_sleeping=atoi(tmp);
377 }
378 if(!xmlStrcmp(ele->name, (const xmlChar *) "cpu")){
379 machine_data_list->processes_cpu=atoi(tmp);
380 }
381 if(!xmlStrcmp(ele->name, (const xmlChar *) "zombie")){
382 machine_data_list->processes_zombie=atoi(tmp);
383 }
384 if(!xmlStrcmp(ele->name, (const xmlChar *) "total")){
385 machine_data_list->processes_total=atoi(tmp);
386 }
387
388 if(tmp!=NULL) xmlFree(tmp);
389 }
390
391 /* paging stats */
392 if(!xmlStrcmp(cur->name, (const xmlChar *) "pages")){
393 tmp=xmlNodeGetContent(ele);
394 if(!xmlStrcmp(ele->name, (const xmlChar *) "pageins")){
395 machine_data_list->pages_in=atoi(tmp);
396 }
397 if(!xmlStrcmp(ele->name, (const xmlChar *) "pageouts")){
398 machine_data_list->pages_out=atoi(tmp);
399 }
400
401 if(tmp!=NULL) xmlFree(tmp);
402 }
403
404 /* OS stats */
405 if(!xmlStrcmp(cur->name, (const xmlChar *) "os")){
406 tmp=xmlNodeGetContent(ele);
407 if(!xmlStrcmp(ele->name, (const xmlChar *) "sysname")){
408 strlcpy(machine_data_list->sysname, tmp, MAXHOSTNAMESIZE+1);
409 }
410 if(tmp!=NULL) xmlFree(tmp);
411 }
412
413 /* Network stats */
414 /* Needs to connect current stat to a previous one. Or create it if new */
415 /* Get name.. Walk list. If match, copy rx/tx values. Else malloc, add to list */
416 if(!xmlStrcmp(cur->name, (const xmlChar *) "net")){
417
418 list=ele->properties;
419 if(list==NULL) continue;
420 network_data_list=malloc(sizeof(network_data_list_t));
421 if(network_data_list==NULL) return -1;
422 while(list!=NULL){
423 tmp=xmlNodeGetContent(list->children);
424 if(!xmlStrcmp(list->name, (const xmlChar *) "name")){
425 network_data_list->name=strdup(tmp);
426 }
427 if(!xmlStrcmp(list->name, (const xmlChar *) "rx")){
428 network_data_list->rx=atoll(tmp);
429 }
430 if(!xmlStrcmp(list->name, (const xmlChar *) "tx")){
431 network_data_list->tx=atoll(tmp);
432 }
433
434 xmlFree(tmp);
435 list=list->next;
436 }
437 if(network_data_list->name==NULL) continue;
438 found_host=0;
439 ndl_ptr=machine_data_list->network_data_list;
440 while(ndl_ptr!=NULL){
441 if(!strcmp(ndl_ptr->name, network_data_list->name)){
442 found_host=1;
443 break;
444 }
445 ndl_ptr=ndl_ptr->next;
446 }
447 if(found_host){
448 ndl_ptr->rx=network_data_list->rx;
449 ndl_ptr->tx=network_data_list->tx;
450 free(network_data_list->name);
451 free(network_data_list);
452 }else{
453 network_data_list->next=machine_data_list->network_data_list;
454 machine_data_list->network_data_list=network_data_list;
455 }
456 }
457
458 /* Disk IO stats */
459 if(!xmlStrcmp(cur->name, (const xmlChar *) "diskio")){
460
461 list=ele->properties;
462 if(list==NULL) continue;
463 diskio_data_list=malloc(sizeof(diskio_data_list_t));
464 if(diskio_data_list==NULL) return -1;
465 while(list!=NULL){
466 tmp=xmlNodeGetContent(list->children);
467 if(!xmlStrcmp(list->name, (const xmlChar *) "name")){
468 diskio_data_list->name=strdup(tmp);
469 }
470 if(!xmlStrcmp(list->name, (const xmlChar *) "rbytes")){
471 diskio_data_list->read=atoll(tmp);
472 }
473 if(!xmlStrcmp(list->name, (const xmlChar *) "wbytes")){
474 diskio_data_list->write=atoll(tmp);
475 }
476
477 xmlFree(tmp);
478 list=list->next;
479 }
480 if(diskio_data_list->name==NULL) continue;
481 found_host=0;
482 didl_ptr=machine_data_list->diskio_data_list;
483 while(didl_ptr!=NULL){
484 if(!strcmp(didl_ptr->name, diskio_data_list->name)){
485 found_host=1;
486 break;
487 }
488 didl_ptr=didl_ptr->next;
489 }
490 if(found_host){
491 didl_ptr->read=diskio_data_list->read;
492 didl_ptr->write=diskio_data_list->write;
493 free(diskio_data_list->name);
494 free(diskio_data_list);
495 }else{
496 diskio_data_list->next=machine_data_list->diskio_data_list;
497 machine_data_list->diskio_data_list=diskio_data_list;
498 }
499 }
500
501
502
503 ele=ele->next;
504 }
505 cur=cur->next;
506 }
507
508 /* Append data we want thats not stored in the server */
509 machine_data_list->cpu_used=100.00-machine_data_list->cpu_idle;
510 machine_data_list->memory_used_pecent=((double)machine_data_list->memory_used / (double)machine_data_list->memory_total) * 100.00;
511 machine_data_list->swap_used_pecent=((double)machine_data_list->swap_used / (double)machine_data_list->swap_total) * 100.00;
512
513 ndl_ptr=machine_data_list->network_data_list;
514 machine_data_list->network_io_total_tx=0;
515 machine_data_list->network_io_total_rx=0;
516 while(ndl_ptr!=NULL){
517 machine_data_list->network_io_total_tx+=ndl_ptr->tx;
518 machine_data_list->network_io_total_rx+=ndl_ptr->rx;
519 ndl_ptr=ndl_ptr->next;
520 }
521
522 didl_ptr=machine_data_list->diskio_data_list;
523 machine_data_list->disk_io_total_read=0;
524 machine_data_list->disk_io_total_write=0;
525 while(didl_ptr!=NULL){
526 machine_data_list->disk_io_total_read+=didl_ptr->read;
527 machine_data_list->disk_io_total_write+=didl_ptr->write;
528 didl_ptr=didl_ptr->next;
529 }
530
531 xmlFreeDoc(doc);
532
533 return num_hosts;
534
535
536 }
537
538 void display(machine_data_list_t *machine_data_list, int num_lines){
539 int line_num=4;
540
541 for(;num_lines;num_lines--){
542 if(machine_data_list==NULL) break;
543 printf("\033[%d;%dH%-11s %5.1f %5.1f %5d %5d %5.1f %5.1f %8lld %8lld %9lld %9lld", \
544 line_num++, 1, \
545 machine_data_list->sysname,
546 machine_data_list->cpu_used,
547 machine_data_list->load_1,
548 machine_data_list->pages_in,
549 machine_data_list->pages_out,
550 machine_data_list->memory_used_pecent,
551 machine_data_list->swap_used_pecent,
552 machine_data_list->network_io_total_rx,
553 machine_data_list->network_io_total_tx,
554 machine_data_list->disk_io_total_read,
555 machine_data_list->disk_io_total_write);
556 machine_data_list=machine_data_list->next;
557 }
558
559 fflush(stdout);
560
561 }
562
563 int main(int argc, char **argv){
564 FILE *control;
565 FILE *data;
566
567 char *machine_list=NULL;
568 char *response=NULL;
569
570 char *servername;
571 int server_control_port;
572 int server_data_port;
573
574 machine_data_list_t *machine_data_list=NULL;
575
576 int num_hosts;
577 int max_display=0;
578
579 int cmdopt;
580 extern int optind;
581 extern char *optarg;
582
583 display_config_t display_config;
584
585 /* What to display defaults */
586 display_config.cpu_user=0;
587 display_config.cpu_idle=0;
588 display_config.cpu_iowait=0;
589 display_config.cpu_kernel=0;
590 display_config.cpu_swap=0;
591 display_config.cpu_used=1;
592
593 display_config.memory_total=0;
594 display_config.memory_free=0;
595 display_config.memory_used=0;
596 display_config.memory_used_pecent=1;
597
598 display_config.swap_total=0;
599 display_config.swap_free=0;
600 display_config.swap_used=0;
601 display_config.swap_used_pecent=1;
602
603 display_config.load_1=1;
604 display_config.load_5=0;
605 display_config.load_15=0;
606
607 display_config.pages_in=1;
608 display_config.pages_out=1;
609
610 display_config.processes_total=0;
611 display_config.processes_sleeping=0;
612 display_config.processes_cpu=0;
613 display_config.processes_zombie=0;
614 display_config.processes_stopped=0;
615
616 display_config.network_io_total_tx=0;
617 display_config.network_io_total_rx=0;
618 display_config.network_all_stats=1;
619
620 display_config.disk_io_total_write=0;
621 display_config.disk_io_total_read=0;
622 display_config.disk_io_all_stats=1;
623
624 display_config.disk_total_used=0;
625 display_config.disk_all_stats=0;
626
627 while((cmdopt=getopt(argc, argv, "d:")) != -1){
628 switch(cmdopt){
629 case 'd':
630 max_display=atoi(optarg);
631 break;
632 }
633 }
634
635
636 if(argc<(optind+2)){
637 printf("Usage is %s <-d lines> hostname port <machine list>\n", argv[0]);
638 exit(1);
639 }
640
641 servername=argv[optind];
642 server_control_port=atoi(argv[optind+1]);
643
644 control=create_tcp_connection(servername, server_control_port);
645 if(control==NULL){
646 errf("Failed to connect (%m)");
647 }
648
649 if(argc==4){
650 /* We've been passed a machine list */
651 /* list currently needs to be ; seperated */
652 machine_list=strdup(argv[3]);
653 }
654
655 if((tcp_comm(control, NULL, &response, "PROTOCOL 1.1"))!=0){
656 errf("Incorrect version number (%s)", response);
657 exit(1);
658 }
659
660 if((tcp_comm(control, "stattop", &response, "OK"))!=0){
661 errf("Unexpected response %s", response);
662 exit(1);
663 }
664
665 if(machine_list!=NULL){
666 if((tcp_comm(control, "SETHOSTLIST", &response, "OK"))!=0){
667 errf("Unexpected response %s", response);
668 exit(1);
669 }
670 if((tcp_comm(control, machine_list, &response, "OK"))!=0){
671 errf("Unexpected response %s", response);
672 exit(1);
673 }
674 }
675
676 if((tcp_comm(control, "STARTDATA", &response, NULL))!=0){
677 errf("Unexpected response %s", response);
678 exit(1);
679 }
680
681 server_data_port=atoi(response);
682 if(server_data_port==0){
683 errf("Unexpected response %s", response);
684 exit(1);
685 }
686
687 data=create_tcp_connection(servername, server_data_port);
688 if(data==NULL){
689 errf("Failed to connect to host %s on port %d (%m)",servername, server_data_port);
690 }
691
692
693 printf("\033[2J");
694 printf("\033[1;1HHostname CPU Load Page Page Mem Swap Net Net Disk Disk");
695 printf("\033[2;1H used%% (1m) ins outs used used rx tx read write");
696 fflush(stdout);
697 for(;;){
698 response=fpgetline(data);
699 if (response==NULL){
700 errf("Failed to read data (%m)");
701 exit(1);
702 }
703
704 num_hosts=parse_xml(response, &machine_data_list);
705 if(num_hosts==-1) continue;
706 machine_data_list=sort_machine_stats(machine_data_list, num_hosts, cmp_cpu);
707 if(max_display==0){
708 display(machine_data_list, num_hosts);
709 }else{
710 display(machine_data_list, max_display);
711 }
712
713 }
714 exit(0);
715 }