Browse code

refactor, minor fixes

git-svn: trunk@4386

Török Edvin authored on 2008/11/13 01:53:27
Showing 4 changed files
... ...
@@ -1,3 +1,7 @@
1
+Wed Nov 12 17:52:12 EET 2008 (edwin)
2
+------------------------------------
3
+ * contrib/clamdtop/TODO, contrib/clamdtop/clamdtop.c: refactor, minor fixes
4
+
1 5
 Wed Nov 12 16:31:34 CET 2008 (tk)
2 6
 ---------------------------------
3 7
  * libclamav: introduce new API calls for engine management (THIS REVISION IS
... ...
@@ -2,7 +2,6 @@ CFLAGS=-g -Wall -W -Wshadow -Wformat=2 -O2
2 2
 CC=gcc
3 3
 OBJS=clamdtop.o
4 4
 CPPFLAGS=-D_FORTIFY_SOURCE=2
5
-
6 5
 ifeq ($(MSYSTEM),MINGW32)
7 6
 LIBS=-lpdcurses -lws2_32
8 7
 else
... ...
@@ -1,6 +1,9 @@
1 1
 select one clamd when monitoring more, and show details for that only
2
-better handling for connection errors (inform user within an ncurses window)
2
+parse pool states, and show those that are != VALID
3
+highlight problems in version/dbversion/dbtime (old db version, old dbtime,
4
+different db versions).
3 5
 write a manpage
6
+check that it works for clamd w/o STATS support (version should still work).
4 7
 consider writing a GUI using Tk 8.5 (version 8.5 has much nicer widgets than 8.4)
5 8
 integrate into toplevel clamav, and build if ncurses is available (optional)
6 9
 figure out minimum version of ncurses required, I used 5.5 during development.
... ...
@@ -56,6 +56,7 @@ typedef struct connection {
56 56
 	int tcp;
57 57
 	struct timeval tv_conn;
58 58
 	char *version;
59
+	int line;
59 60
 } conn_t;
60 61
 
61 62
 struct global_stats {
... ...
@@ -72,6 +73,7 @@ struct stats {
72 72
 	char *db_version;
73 73
 	struct tm db_time;
74 74
 	const char *version;
75
+	int stats_unsupp;
75 76
 	uint8_t conn_hr, conn_min, conn_sec;
76 77
 	/* threads - primary */
77 78
 	unsigned prim_live, prim_idle, prim_max;
... ...
@@ -89,6 +91,18 @@ static int send_string_noreconn(conn_t *conn, const char *cmd);
89 89
 static void send_string(conn_t *conn, const char *cmd);
90 90
 static void read_version(conn_t *conn);
91 91
 
92
+enum exit_reason {
93
+	FAIL_INITIAL_CONN=1,
94
+	OUT_OF_MEMORY,
95
+	RECONNECT_FAIL,
96
+	SIGINT_REASON
97
+};
98
+
99
+static void exit_program(enum exit_reason reason, const char *func, unsigned line);
100
+#define EXIT_PROGRAM(r) exit_program(r, __PRETTY_FUNCTION__, __LINE__);
101
+#define OOM_CHECK(p) do { if (!p) EXIT_PROGRAM(OUT_OF_MEMORY); } while (0)
102
+
103
+
92 104
 static struct global_stats global;
93 105
 static int curses_inited = 1;
94 106
 static int maxystats=0;
... ...
@@ -96,6 +110,7 @@ static int maxystats=0;
96 96
 enum colors {
97 97
 	header_color=1,
98 98
 	version_color,
99
+	error_color,
99 100
 	value_color,
100 101
 	descr_color,
101 102
 	queue_header_color,
... ...
@@ -112,6 +127,7 @@ enum colors {
112 112
 
113 113
 #define VALUE_ATTR A_BOLD | COLOR_PAIR(value_color)
114 114
 #define DESCR_ATTR COLOR_PAIR(descr_color)
115
+#define ERROR_ATTR A_BOLD | COLOR_PAIR(error_color)
115 116
 
116 117
 static WINDOW *header_window = NULL;
117 118
 static WINDOW *stats_head_window = NULL;
... ...
@@ -125,7 +141,7 @@ static char *queue_header = NULL;
125 125
 static char *clamd_header = NULL;
126 126
 
127 127
 #define CMDHEAD " COMMAND        TIME QUEUED   FILE"
128
-#define CMDHEAD2 "NO COMMAND     TIME QUEUED   FILE"
128
+#define CMDHEAD2 " # COMMAND     TIME QUEUED   FILE"
129 129
 
130 130
 /*
131 131
  * CLAMD - which local/remote clamd this is
... ...
@@ -149,11 +165,9 @@ static void resize(void)
149 149
 	free(queue_header);
150 150
 	free(clamd_header);
151 151
 	queue_header = malloc(maxx + 1);
152
+	OOM_CHECK(queue_header);
152 153
 	clamd_header = malloc(maxx + 1);
153
-	if(!queue_header || !clamd_header) {
154
-		fprintf(stderr,"Out of memory\n");
155
-		exit(1);
156
-	}
154
+	OOM_CHECK(clamd_header);
157 155
 	strncpy(queue_header, global.num_clamd>1 ? CMDHEAD2 : CMDHEAD, maxx);
158 156
 	strncpy(clamd_header, SUMHEAD, maxx);
159 157
 	queue_header[maxx] = '\0';
... ...
@@ -203,7 +217,7 @@ static void init_windows(int num_clamd)
203 203
 	stats_window = subwin(stdscr, maxystats, maxx, num_clamd+2, 0);
204 204
 	status_bar_window = subwin(stdscr, 1, maxx, maxy-1, 0);
205 205
 	/* memwindow overlaps, used only in details mode */
206
-	mem_window = derwin(stats_window, 6, 48, 0, maxx-48);
206
+	mem_window = derwin(stats_window, 6, 41, 0, maxx-41);
207 207
 	touchwin(stdscr);
208 208
 	werase(stdscr);
209 209
 	refresh();
... ...
@@ -227,6 +241,7 @@ static void init_ncurses(int num_clamd)
227 227
 
228 228
 	init_pair(header_color, COLOR_BLACK, COLOR_WHITE);
229 229
 	init_pair(version_color, DEFAULT_COLOR, DEFAULT_COLOR);
230
+	init_pair(error_color, COLOR_WHITE, COLOR_RED);
230 231
 	init_pair(value_color, COLOR_GREEN, DEFAULT_COLOR);
231 232
 	init_pair(descr_color, COLOR_CYAN, DEFAULT_COLOR);
232 233
 	init_pair(queue_header_color, COLOR_BLACK, COLOR_GREEN);
... ...
@@ -297,7 +312,7 @@ static void show_bar(WINDOW *win, size_t i, unsigned live, unsigned idle,
297 297
 		unsigned max, int blink)
298 298
 {
299 299
 	int y,x;
300
-	unsigned len  = 47;
300
+	unsigned len  = 39;
301 301
 	unsigned start = 1;
302 302
 	unsigned activ = max ? ((live-idle)*(len - start - 2) + (max/2)) / max : 0;
303 303
 	unsigned dim   = max ? idle*(len - start - 2) / max : 0;
... ...
@@ -333,8 +348,10 @@ static void cleanup(void)
333 333
 {
334 334
 	unsigned i;
335 335
 	if (curses_inited) {
336
-		werase(status_bar_window);
337
-		wrefresh(status_bar_window);
336
+		if (status_bar_window) {
337
+			werase(status_bar_window);
338
+			wrefresh(status_bar_window);
339
+		}
338 340
 		rm_windows();
339 341
 		endwin();
340 342
 	}
... ...
@@ -358,11 +375,6 @@ static void cleanup(void)
358 358
 	}
359 359
 }
360 360
 
361
-enum exit_reason {
362
-	FAIL_INITIAL_CONN=1,
363
-	OUT_OF_MEMORY,
364
-	RECONNECT_FAIL
365
-};
366 361
 
367 362
 static void exit_program(enum exit_reason reason, const char *func, unsigned line)
368 363
 {
... ...
@@ -376,6 +388,9 @@ static void exit_program(enum exit_reason reason, const char *func, unsigned lin
376 376
 		case RECONNECT_FAIL:
377 377
 			exit_reason = "Failed to reconnect to clamd after connection was lost";
378 378
 			break;
379
+		case SIGINT_REASON:
380
+			exit_reason = "User interrupt";
381
+			break;
379 382
 		default:
380 383
 			exit_reason = "Unknown";
381 384
 			break;
... ...
@@ -385,9 +400,6 @@ static void exit_program(enum exit_reason reason, const char *func, unsigned lin
385 385
 	exit(reason);
386 386
 }
387 387
 
388
-#define EXIT_PROGRAM(r) exit_program(r, __PRETTY_FUNCTION__, __LINE__);
389
-#define OOM_CHECK(p) do { if (!p) EXIT_PROGRAM(OUT_OF_MEMORY); } while (0)
390
-
391 388
 struct task {
392 389
 	char *line;
393 390
 	double tim;
... ...
@@ -406,17 +418,23 @@ static int tasks_compare(const void *a, const void *b)
406 406
 }
407 407
 
408 408
 /* ----------- Socket routines ----------------------- */
409
-static void print_con_error(const char *fmt, ...)
409
+static void print_con_info(conn_t *conn, const char *fmt, ...)
410 410
 {
411 411
 	va_list ap;
412 412
 	va_start(ap, fmt);
413
-/*	if (version_window) {
414
-		werase(version_window);
415
-		wmove(version_window, 0, 0);
416
-		vwprintw(version_window, fmt, ap);
417
-		wrefresh(version_window);
418
-	} else*/
419
-		vfprintf(stderr, fmt, ap);
413
+	if (stats_head_window) {
414
+		char *buf = malloc(maxx+1);
415
+		memset(buf, ' ', maxx);
416
+		OOM_CHECK(buf);
417
+		vsnprintf(buf, maxx, fmt, ap);
418
+		buf[strlen(buf)] = ' ';
419
+		buf[maxx] = '\0';
420
+		wattron(stats_head_window, ERROR_ATTR);
421
+		mvwprintw(stats_head_window, conn->line, 0, "%s", buf);
422
+		wattroff(stats_head_window, ERROR_ATTR);
423
+		wrefresh(stats_head_window);
424
+	} else
425
+		vfprintf(stdout, fmt, ap);
420 426
 	va_end(ap);
421 427
 }
422 428
 
... ...
@@ -424,6 +442,7 @@ static void print_con_error(const char *fmt, ...)
424 424
 static int make_connection(const char *soname, conn_t *conn)
425 425
 {
426 426
 	int s;
427
+	struct timeval tv;
427 428
 	conn->tcp = 0;
428 429
 #ifdef _WIN32
429 430
     {
... ...
@@ -438,7 +457,7 @@ static int make_connection(const char *soname, conn_t *conn)
438 438
 		memset(&addr, 0, sizeof(addr));
439 439
 		addr.sun_family = AF_UNIX;
440 440
 		strncpy(addr.sun_path, soname, sizeof(addr.sun_path));
441
-		printf("Connecting to: %s\n", soname);
441
+		print_con_info(conn, "Connecting to: %s\n", soname);
442 442
 		if (connect(s, (struct sockaddr *)&addr, sizeof(addr))) {
443 443
 			perror("connect");
444 444
 			return -1;
... ...
@@ -458,7 +477,7 @@ static int make_connection(const char *soname, conn_t *conn)
458 458
 		}
459 459
 		if(!port)
460 460
 			port = 3310;
461
-		printf("Looking up: %s\n", host);
461
+		print_con_info(conn, "Looking up: %s\n", host);
462 462
 		if((hp = gethostbyname(host)) == NULL) {
463 463
 			herror("Cannot find host");
464 464
 			return -1;
... ...
@@ -472,7 +491,7 @@ static int make_connection(const char *soname, conn_t *conn)
472 472
 		server.sin_family = AF_INET;
473 473
 		server.sin_port = htons(port);
474 474
 		server.sin_addr.s_addr = ((struct in_addr*)(hp->h_addr))->s_addr;
475
-		printf("Connecting to: %s:%u\n", inet_ntoa(server.sin_addr), port);
475
+		print_con_info(conn, "Connecting to: %s:%u\n", inet_ntoa(server.sin_addr), port);
476 476
 		if (connect(s, (struct sockaddr *)&server, sizeof(server))) {
477 477
 			perror("connect");
478 478
 			return -1;
... ...
@@ -481,6 +500,9 @@ static int make_connection(const char *soname, conn_t *conn)
481 481
 	conn->remote = soname;
482 482
 	conn->sd = s;
483 483
 	gettimeofday(&conn->tv_conn, NULL);
484
+	tv.tv_sec = 4;
485
+	tv.tv_usec = 0;
486
+	setsockopt(conn->sd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
484 487
 	send_string(conn, "SESSION\nVERSION\n");
485 488
 	read_version(conn);
486 489
 	return 0;
... ...
@@ -508,9 +530,8 @@ static void reconnect(conn_t *conn)
508 508
 	if(++tries > 3) {
509 509
 		EXIT_PROGRAM(RECONNECT_FAIL);
510 510
 	}
511
-	print_con_error("%s: %s", conn->remote, strerror(errno));
512 511
 	if (make_connection(conn->remote, conn) < 0) {
513
-		print_con_error("Unable to reconnect to %s: %s", conn->remote, strerror(errno));
512
+		print_con_info(conn, "Unable to reconnect to %s: %s", conn->remote, strerror(errno));
514 513
 		exit(3);
515 514
 	}
516 515
 	free(conn->version);
... ...
@@ -520,17 +541,23 @@ static void reconnect(conn_t *conn)
520 520
 static int recv_line(conn_t *conn, char *buf, size_t len)
521 521
 {
522 522
 	assert(len > 0);
523
-	assert(conn && conn->sd > 0);
523
+	assert(conn);
524 524
 	assert(buf);
525 525
 
526 526
 	len--;
527
-	if (!len)
527
+	if (!len || conn->sd == -1)
528 528
 		return 0;
529
+	assert(conn->sd > 0);
529 530
 	while (len > 0) {
530 531
 		ssize_t nread = recv(conn->sd, buf, len, MSG_PEEK);
531
-		if (nread == -1)
532
-			reconnect(conn);
533
-		else {
532
+		if (nread  <= 0) {
533
+			print_con_info(conn, "%s: %s", conn->remote, strerror(errno));
534
+			/* it could be a timeout, be nice and send an END */
535
+			send_string_noreconn(conn, "END\n");
536
+			close(conn->sd);
537
+			conn->sd = -1;
538
+			return 0;
539
+		} else {
534 540
 			char *p = memchr(buf, '\n', nread);
535 541
 			if (p) {
536 542
 				len = p - buf + 1;
... ...
@@ -557,7 +584,6 @@ static void output_queue(size_t line, ssize_t max)
557 557
 {
558 558
 	ssize_t i;
559 559
 	struct task *tasks = global.tasks;
560
-	assert(tasks);
561 560
 	wattron(stats_window, COLOR_PAIR(queue_header_color));
562 561
 	mvwprintw(stats_window, line++, 0, "%s", queue_header);
563 562
 	wattroff(stats_window, COLOR_PAIR(queue_header_color));
... ...
@@ -567,6 +593,7 @@ static void output_queue(size_t line, ssize_t max)
567 567
 		--max;
568 568
 	if (max < 0) max = 0;
569 569
 	for(i=0;i<max;i++) {
570
+		assert(tasks);
570 571
 		char *cmde = strchr(tasks[i].line, ' ');
571 572
 		if(cmde) {
572 573
 			char cmd[16];
... ...
@@ -608,18 +635,11 @@ static void parse_queue(conn_t *conn, char* buf, size_t len, unsigned idx)
608 608
 			continue;
609 609
 		++global.n;
610 610
 		global.tasks = realloc(global.tasks, sizeof(*global.tasks)*global.n);
611
-		if(!global.tasks) {
612
-			fprintf(stderr, "Out of memory\n");
613
-			/* OOM */
614
-			exit(1);
615
-		}
611
+		OOM_CHECK(global.tasks);
616 612
 		global.tasks[global.n-1].line = strdup(buf);
617
-		if(!global.tasks[global.n-1].line) {
618
-			fprintf(stderr, "Out of memory\n");
619
-			exit(1);
620
-		}
613
+		OOM_CHECK(global.tasks[global.n-1].line);
621 614
 		global.tasks[global.n-1].tim  = tim;
622
-		global.tasks[global.n-1].clamd_no = idx;
615
+		global.tasks[global.n-1].clamd_no = idx + 1;
623 616
 	} while (recv_line(conn, buf, len) && buf[0] == '\t' && strcmp("END\n", buf) != 0);
624 617
 }
625 618
 
... ...
@@ -634,18 +654,18 @@ static void output_memstats(struct stats *stats)
634 634
 	werase(mem_window);
635 635
 	box(mem_window, 0, 0);
636 636
 
637
-	snprintf(buf, sizeof(buf),"heap %4luM mmap %4luM releasable %3luM",
637
+	snprintf(buf, sizeof(buf),"heap %4luM mmap %4luM unused %3luM",
638 638
 			stats->lheapu/1024, stats->lmmapu/1024, stats->lreleasable/1024);
639
-	mvwprintw(mem_window, 1, 1, "Memory: ");
639
+	mvwprintw(mem_window, 1, 1, "Mem:  ");
640 640
 	print_colored(mem_window, buf);
641 641
 
642
-	mvwprintw(mem_window, 2, 1, "Malloc: ");
643
-	snprintf(buf, sizeof(buf),"used %4luM free %4luM total     %4luM",
642
+	mvwprintw(mem_window, 2, 1, "Libc: ");
643
+	snprintf(buf, sizeof(buf),"used %4luM free %4luM total %4luM",
644 644
 			stats->ltotalu/1024, stats->ltotalf/1024, (stats->ltotalu+stats->ltotalf)/1024);
645 645
 	print_colored(mem_window, buf);
646 646
 
647
-	mvwprintw(mem_window, 3, 1, "Mempool: ");
648
-	snprintf(buf, sizeof(buf), "count  %u  used %4luM total     %4luM",
647
+	mvwprintw(mem_window, 3, 1, "Pool: ");
648
+	snprintf(buf, sizeof(buf), "count   %u  used %4luM total %4luM",
649 649
 			stats->pools_cnt, stats->lpoolu/1024, stats->lpoolt/1024);
650 650
 	print_colored(mem_window, buf);
651 651
 
... ...
@@ -699,8 +719,8 @@ static int output_stats(struct stats *stats, unsigned idx)
699 699
 	char mem[6];
700 700
 	WINDOW *win = stats_head_window;
701 701
 
702
-	if (stats->mem == -1)
703
-		strcpy(mem, "N/A");
702
+	if (stats->mem == -1 || stats->stats_unsupp)
703
+		strncpy(mem, "N/A", sizeof(mem));
704 704
 	else {
705 705
 		char c;
706 706
 		double s;
... ...
@@ -720,7 +740,7 @@ static int output_stats(struct stats *stats, unsigned idx)
720 720
 	i = idx+1;
721 721
 
722 722
 	if (!stats->db_time.tm_year)
723
-		strcpy(timbuf,"N/A");
723
+		strncpy(timbuf,"N/A",sizeof(timbuf));
724 724
 	else
725 725
 		snprintf(timbuf, sizeof(timbuf), "%04u-%02u-%02u %02uh",
726 726
 				1900 + stats->db_time.tm_year,
... ...
@@ -728,14 +748,19 @@ static int output_stats(struct stats *stats, unsigned idx)
728 728
 				stats->db_time.tm_mday,
729 729
 				stats->db_time.tm_hour);
730 730
 
731
-	mvwprintw(win, i++, 0,"%2u %02u:%02u:%02u %3u %3u %5u %5u %5s %-14s %-6s %5s %s", idx+1,  stats->conn_hr, stats->conn_min, stats->conn_sec,
731
+	if (!stats->stats_unsupp) {
732
+		mvwprintw(win, i++, 0,"%2u %02u:%02u:%02u %3u %3u %5u %5u %5s %-14s %-6s %5s %s", idx+1,  stats->conn_hr, stats->conn_min, stats->conn_sec,
732 733
 			stats->live, stats->idle,
733 734
 			stats->current_q, stats->biggest_queue,
734 735
 			mem,
735 736
 			stats->remote, stats->engine_version, stats->db_version, timbuf);
737
+	} else {
738
+		mvwprintw(win, i++, 0,"%2u %02u:%02u:%02u N/A N/A   N/A   N/A   N/A %-14s %-6s %5s %s", idx+1,  stats->conn_hr, stats->conn_min, stats->conn_sec,
739
+			stats->remote, stats->engine_version, stats->db_version, timbuf);
740
+	}
736 741
 	win = stats_window;
737 742
 	i = 0;
738
-	if (show_detail(idx)) {
743
+	if (show_detail(idx) && !stats->stats_unsupp) {
739 744
 		mvwprintw(win, i++, 0, "Primary threads: ");
740 745
 		snprintf(buf, sizeof(buf), "live %3u idle %3u max %3u", stats->prim_live, stats->prim_idle, stats->prim_max);
741 746
 		print_colored(win, buf);
... ...
@@ -757,7 +782,7 @@ static int output_stats(struct stats *stats, unsigned idx)
757 757
 			stats->biggest_queue = stats->current_q;
758 758
 			blink = 1;
759 759
 	}
760
-	if (show_detail(idx)) {
760
+	if (show_detail(idx) && !stats->stats_unsupp) {
761 761
 		show_bar(win, i++, stats->current_q, 0, biggest_queue, blink);
762 762
 	}
763 763
 	return i;
... ...
@@ -810,10 +835,7 @@ static void parse_stats(conn_t *conn, struct stats *stats, unsigned idx)
810 810
 		p++;
811 811
 
812 812
 	stats->engine_version = malloc(p - pstart+1);
813
-	if (!stats->engine_version) {
814
-		fprintf(stderr,"Out of memory!\n");
815
-		exit(1);
816
-	}
813
+	OOM_CHECK(stats->engine_version);
817 814
 
818 815
 	memcpy(stats->engine_version, pstart, p-pstart);
819 816
 	stats->engine_version[p-pstart] = '\0';
... ...
@@ -827,10 +849,7 @@ static void parse_stats(conn_t *conn, struct stats *stats, unsigned idx)
827 827
 		if (!p)
828 828
 			p = pstart + strlen(pstart);
829 829
 		stats->db_version = malloc(p - pstart + 1);
830
-		if (!stats->db_version) {
831
-			fprintf(stderr, "Out of memory!\n");
832
-			exit(1);
833
-		}
830
+		OOM_CHECK(stats->db_version);
834 831
 		memcpy(stats->db_version, pstart, p-pstart);
835 832
 		stats->db_version[p-pstart] = '\0';
836 833
 		if(*p) p++;
... ...
@@ -865,6 +884,10 @@ static void parse_stats(conn_t *conn, struct stats *stats, unsigned idx)
865 865
 			parse_memstats(val, stats);
866 866
 			continue;
867 867
 		}
868
+		if(!strncmp("UNKNOWN COMMAND", buf, 15)) {
869
+			stats->stats_unsupp = 1;
870
+			break;
871
+		}
868 872
 		for(j=1;j<strlen(buf);j++)
869 873
 			buf[j] = tolower(buf[j]);
870 874
 	/*	mvwprintw(win, i, 0, "%s", buf);
... ...
@@ -914,6 +937,11 @@ static void read_version(conn_t *conn)
914 914
 	}
915 915
 }
916 916
 
917
+static void sigint(int a)
918
+{
919
+	EXIT_PROGRAM(SIGINT_REASON);
920
+}
921
+
917 922
 /* -------------------------- Initialization ---------------- */
918 923
 static void setup_connections(int argc, char *argv[])
919 924
 {
... ...
@@ -941,6 +969,7 @@ static void setup_connections(int argc, char *argv[])
941 941
 	global.conn = calloc(global.num_clamd, sizeof(*global.conn));
942 942
 	OOM_CHECK(global.conn);
943 943
 	for (i=0;i<global.num_clamd;i++) {
944
+		global.conn[i].line = i+1;
944 945
 		if (make_connection(argv[i+1], &global.conn[i]) < 0) {
945 946
 			EXIT_PROGRAM(FAIL_INITIAL_CONN);
946 947
 		}
... ...
@@ -948,13 +977,14 @@ static void setup_connections(int argc, char *argv[])
948 948
 	}
949 949
 #ifndef _WIN32
950 950
 	signal(SIGPIPE, SIG_IGN);
951
+	signal(SIGINT, sigint);
951 952
 #endif
952 953
 }
953 954
 
954 955
 static void free_global_stats(void)
955 956
 {
956 957
 	unsigned i;
957
-	for (i=0;i<global.n;i++) {
958
+	for (i=0;i<(unsigned)global.n;i++) {
958 959
 		free(global.tasks[i].line);
959 960
 	}
960 961
 	for (i=0;i<global.num_clamd;i++) {
... ...
@@ -999,12 +1029,16 @@ int main(int argc, char *argv[])
999 999
 				memset(stats, 0, sizeof(*stats));
1000 1000
 				parse_stats(&global.conn[i], stats, i);
1001 1001
 			}
1002
-			assert(global.tasks);
1003
-			qsort(global.tasks, global.n, sizeof(*global.tasks), tasks_compare);
1002
+			if (global.tasks)
1003
+				qsort(global.tasks, global.n, sizeof(*global.tasks), tasks_compare);
1004 1004
 			tv_last = tv;
1005 1005
 		}
1006 1006
 		/* always show, so that screen resizes take effect instantly*/
1007 1007
 		output_all();
1008
+		for(i=0;i<global.num_clamd;i++) {
1009
+			if (global.conn[i].sd == -1)
1010
+				reconnect(&global.conn[i]);
1011
+		}
1008 1012
 	} while(toupper(ch = getch()) != 'Q');
1009 1013
 	free_global_stats();
1010 1014
 	normal_exit = 1;