revert "%s" addition in em_main.c, broke %D in log_file.
[exim.git] / src / exim_monitor / em_main.c
... / ...
CommitLineData
1/*************************************************
2* Exim Monitor *
3*************************************************/
4
5/* Copyright (c) University of Cambridge 1995 - 2009 */
6/* See the file NOTICE for conditions of use and distribution. */
7
8
9#include "em_hdr.h"
10
11/* This module contains the main program of the Exim monitor, which
12sets up the world and then lets the XtAppMainLoop function
13run things off X events. */
14
15
16/*************************************************
17* Static variables *
18*************************************************/
19
20/* Fallback resources */
21
22static String fallback_resources[] = {"eximon.geometry: +150+0", NULL};
23
24/* X11 fixed argument lists */
25
26static Arg quit_args[] = {
27 {XtNfromVert, (XtArgVal) NULL}, /* must be first */
28 {XtNlabel, (XtArgVal) " Quit "},
29 {"left", XawChainLeft},
30 {"right", XawChainLeft},
31 {"top", XawChainTop},
32 {"bottom", XawChainTop}
33};
34
35static Arg resize_args[] = {
36 {XtNfromVert, (XtArgVal) NULL}, /* must be first */
37 {XtNfromHoriz,(XtArgVal) NULL}, /* must be second */
38 {XtNlabel, (XtArgVal) " Size "},
39 {"left", XawChainLeft},
40 {"right", XawChainLeft},
41 {"top", XawChainTop},
42 {"bottom", XawChainTop}
43};
44
45static Arg update_args[] = {
46 {XtNfromVert, (XtArgVal) NULL}, /* must be first */
47 {XtNlabel, (XtArgVal) " Update "},
48 {"left", XawChainLeft},
49 {"right", XawChainLeft},
50 {"top", XawChainTop},
51 {"bottom", XawChainTop}
52};
53
54static Arg hide_args[] = {
55 {XtNfromVert, (XtArgVal) NULL}, /* must be first */
56 {XtNfromHoriz,(XtArgVal) NULL}, /* must be second */
57 {XtNlabel, (XtArgVal) " Hide "},
58 {"left", XawChainLeft},
59 {"right", XawChainLeft},
60 {"top", XawChainTop},
61 {"bottom", XawChainTop}
62};
63
64static Arg unhide_args[] = {
65 {XtNfromVert, (XtArgVal) NULL}, /* must be first */
66 {XtNfromHoriz,(XtArgVal) NULL}, /* must be second */
67 {XtNlabel, (XtArgVal) " Unhide "},
68 {"left", XawChainLeft},
69 {"right", XawChainLeft},
70 {"top", XawChainTop},
71 {"bottom", XawChainTop}
72};
73
74static Arg log_args[] = {
75 {XtNfromVert, (XtArgVal) NULL}, /* must be first */
76 {"editType", XawtextEdit},
77 {"useStringInPlace", (XtArgVal)TRUE},
78 {"string", (XtArgVal)""}, /* dummy to get it going */
79 {"scrollVertical", XawtextScrollAlways},
80 {"scrollHorizontal", XawtextScrollAlways},
81 {"right", XawChainRight},
82 {"top", XawChainTop},
83 {"bottom", XawChainTop}
84};
85
86static Arg queue_args[] = {
87 {XtNfromVert, (XtArgVal) NULL}, /* must be first */
88 {"editType", XawtextEdit},
89 {"string", (XtArgVal)""}, /* dummy to get it going */
90 {"scrollVertical", XawtextScrollAlways},
91 {"right", XawChainRight},
92 {"top", XawChainTop},
93 {"bottom", XawChainBottom}
94};
95
96static Arg sizepos_args[] = {
97 {"width", (XtArgVal)NULL},
98 {"height", (XtArgVal)NULL},
99 {"x", (XtArgVal)NULL},
100 {"y", (XtArgVal)NULL}
101};
102
103XtActionsRec menu_action_table[] = {
104 { "menu-create", menu_create } };
105
106/* Types of non-message dialog action */
107
108enum { da_hide };
109
110/* Miscellaneous local variables */
111
112static int dialog_action;
113static int tick_stripchart_accumulator = 999999;
114static int tick_interval = 2;
115static int maxposset = 0;
116static int minposset = 0;
117static int x_adjustment = -1;
118static int y_adjustment = -1;
119static Dimension screenwidth, screenheight;
120static Dimension original_x, original_y;
121static Dimension maxposx, maxposy;
122static Dimension minposx, minposy;
123static Dimension maxwidth, maxheight;
124static Widget outer_form_widget;
125static Widget hide_widget;
126static Widget above_queue_widget;
127
128
129
130
131#ifdef STRERROR_FROM_ERRLIST
132/*************************************************
133* Provide strerror() for non-ANSI libraries *
134*************************************************/
135
136/* Some old-fashioned systems still around (e.g. SunOS4) don't have strerror()
137in their libraries, but can provide the same facility by this simple
138alternative function. */
139
140uschar *
141strerror(int n)
142{
143if (n < 0 || n >= sys_nerr) return "unknown error number";
144return sys_errlist[n];
145}
146#endif /* STRERROR_FROM_ERRLIST */
147
148
149
150/*************************************************
151* Handle attempts to write the log *
152*************************************************/
153
154/* The message gets written to stderr when log_write() is called from a
155utility. The message always gets '\n' added on the end of it. These calls come
156from modules such as store.c when things go drastically wrong (e.g. malloc()
157failing). In normal use they won't get obeyed.
158
159Arguments:
160 selector not relevant when running a utility
161 flags not relevant when running a utility
162 format a printf() format
163 ... arguments for format
164
165Returns: nothing
166*/
167
168void
169log_write(unsigned int selector, int flags, const char *format, ...)
170{
171va_list ap;
172va_start(ap, format);
173vfprintf(stderr, format, ap);
174fprintf(stderr, "\n");
175va_end(ap);
176selector = selector; /* Keep picky compilers happy */
177flags = flags;
178}
179
180
181
182
183/*************************************************
184* Extract port from address string *
185*************************************************/
186
187/* In the spool file, a host plus port is given as an IP address followed by a
188dot and a port number. This function decodes this. It is needed by the
189spool-reading function, and copied here to avoid having to include the whole
190host.c module. One day the interaction between exim and eximon with regard to
191included code MUST be tidied up!
192
193Argument:
194 address points to the string; if there is a port, the '.' in the string
195 is overwritten with zero to terminate the address
196
197Returns: 0 if there is no port, else the port number.
198*/
199
200int
201host_address_extract_port(uschar *address)
202{
203int skip = -3; /* Skip 3 dots in IPv4 addresses */
204address--;
205while (*(++address) != 0)
206 {
207 int ch = *address;
208 if (ch == ':') skip = 0; /* Skip 0 dots in IPv6 addresses */
209 else if (ch == '.' && skip++ >= 0) break;
210 }
211if (*address == 0) return 0;
212*address++ = 0;
213return Uatoi(address);
214}
215
216
217
218
219/*************************************************
220* SIGCHLD handler *
221*************************************************/
222
223/* Operations on messages are done in subprocesses; this handler
224just catches them when they finish. It causes a queue display update
225unless configured not to. */
226
227static void sigchld_handler(int sig)
228{
229while (waitpid(-1, NULL, WNOHANG) > 0);
230signal(sig, sigchld_handler);
231if (action_queue_update) tick_queue_accumulator = 999999;
232}
233
234
235
236/*************************************************
237* Callback routines *
238*************************************************/
239
240
241void updateAction(Widget w, XtPointer client_data, XtPointer call_data)
242{
243w = w; /* Keep picky compilers happy */
244client_data = client_data;
245call_data = call_data;
246scan_spool_input(TRUE);
247queue_display();
248tick_queue_accumulator = 0;
249}
250
251void hideAction(Widget w, XtPointer client_data, XtPointer call_data)
252{
253w = w; /* Keep picky compilers happy */
254client_data = client_data;
255call_data = call_data;
256actioned_message[0] = 0;
257dialog_ref_widget = w;
258dialog_action = da_hide;
259create_dialog(US"Hide addresses ending with", US"");
260}
261
262void unhideAction(Widget w, XtPointer client_data, XtPointer call_data)
263{
264skip_item *sk = queue_skip;
265
266w = w; /* Keep picky compilers happy */
267client_data = client_data;
268call_data = call_data;
269
270while (sk != NULL)
271 {
272 skip_item *next = sk->next;
273 store_free(sk);
274 sk = next;
275 }
276queue_skip = NULL;
277
278XtDestroyWidget(unhide_widget);
279unhide_widget = NULL;
280
281scan_spool_input(TRUE);
282queue_display();
283tick_queue_accumulator = 0;
284}
285
286void quitAction(Widget w, XtPointer client_data, XtPointer call_data)
287{
288w = w; /* Keep picky compilers happy */
289client_data = client_data;
290call_data = call_data;
291exit(0);
292}
293
294
295/* Action when the "Size" button is pressed. This is a kludged up mess
296that I made work after much messing around. Reading the position of the
297toplevel widget gets the absolute position of the data portion of the window,
298excluding the window manager's furniture. However, positioning the toplevel
299widget's window seems to position the top corner of the furniture under the twm
300window manager, but not under fwvm and others. The two cases are distinguished
301by the values of x_adjustment and y_adjustment.
302
303For twm (adjustment >= 0), one has to fudge the miminizing function to ensure
304that we go back to exactly the same position as before.
305
306For fwvm (adjustment < 0), one has to fudge the "top left hand corner"
307positioning to ensure that the window manager's furniture gets displayed on the
308screen. I haven't found a way of discovering the thickness of the furniture, so
309some screwed-in values are used.
310
311This is all ad hoc, developed by floundering around as I haven't found any
312documentation that tells me what I really should do. */
313
314void resizeAction(Widget button, XtPointer client_data, XtPointer call_data)
315{
316Dimension x, y;
317Dimension width, height;
318XWindowAttributes a;
319Window w = XtWindow(toplevel_widget);
320
321button = button; /* Keep picky compilers happy */
322client_data = client_data;
323call_data = call_data;
324
325/* Get the position and size of the top level widget. */
326
327sizepos_args[0].value = (XtArgVal)(&width);
328sizepos_args[1].value = (XtArgVal)(&height);
329sizepos_args[2].value = (XtArgVal)(&x);
330sizepos_args[3].value = (XtArgVal)(&y);
331XtGetValues(toplevel_widget, sizepos_args, 4);
332
333/* Get the position of the widget's window relative to its parent; this
334gives the thickness of the window manager's furniture. At least it does
335in twm. For fwvm it gives zero. The size/movement function uses this data.
336I tried doing this before entering the main loop, but it didn't always
337work properly with twm. Running it every time seems to be OK. */
338
339XGetWindowAttributes(X_display, XtWindow(toplevel_widget), &a);
340if (a.x != 0) x_adjustment = a.x;
341if (a.y != 0) y_adjustment = a.y;
342
343/* If at maximum size, reduce to minimum and move back to where it was
344when maximized, if that value is set, allowing for the furniture in cases
345where the positioning includes the furniture. */
346
347if (width == maxwidth && height == maxheight)
348 {
349 maxposx = x;
350 maxposy = y;
351 maxposset = 1;
352
353 if (minposset)
354 xs_SetValues(toplevel_widget, 4,
355 "width", min_width,
356 "height", min_height,
357 "x", minposx - ((x_adjustment >= 0)? x_adjustment : 0),
358 "y", minposy - ((y_adjustment >= 0)? y_adjustment : 0));
359 else
360 xs_SetValues(toplevel_widget, 2,
361 "width", min_width,
362 "height", min_height);
363 }
364
365/* Else always expand to maximum. If currently at minimum size, remember where
366it was for coming back. If we don't have a value for the thickness of the
367furniture, the implication is that the coordinates position the application
368window, so we can't use (0,0) because that loses the furniture. Use screwed in
369values that seem to work with fvwm. */
370
371else
372 {
373 int xx = x;
374 int yy = y;
375
376 if (width == min_width && height == min_height)
377 {
378 minposx = x;
379 minposy = y;
380 minposset = 1;
381 }
382
383 if ((int)(x + maxwidth) > (int)screenwidth ||
384 (int)(y + maxheight + 10) > (int)screenheight)
385 {
386 if (maxposset)
387 {
388 xx = maxposx - ((x_adjustment >= 0)? x_adjustment : 0);
389 yy = maxposy - ((y_adjustment >= 0)? y_adjustment : 0);
390 }
391 else
392 {
393 if ((int)(x + maxwidth) > (int)screenwidth)
394 xx = (x_adjustment >= 0)? 0 : 4;
395 if ((int)(y + maxheight + 10) > (int)screenheight)
396 yy = (y_adjustment >= 0)? 0 : 21;
397 }
398
399 xs_SetValues(toplevel_widget, 4,
400 "width", maxwidth,
401 "height", maxheight,
402 "x", xx,
403 "y", yy);
404 }
405
406 else xs_SetValues(toplevel_widget, 2,
407 "width", maxwidth,
408 "height", maxheight);
409 }
410
411/* Ensure the window is at the top */
412
413XRaiseWindow(X_display, w);
414}
415
416
417
418
419/*************************************************
420* Handle input from non-msg dialogue *
421*************************************************/
422
423/* The various cases here are: hide domain, (no more yet) */
424
425void NonMessageDialogue(uschar *s)
426{
427skip_item *sk;
428
429switch(dialog_action)
430 {
431 case da_hide:
432
433 /* Create the unhide button if not present */
434
435 if (unhide_widget == NULL)
436 {
437 unhide_args[0].value = (XtArgVal) above_queue_widget;
438 unhide_args[1].value = (XtArgVal) hide_widget;
439 unhide_widget = XtCreateManagedWidget("unhide", commandWidgetClass,
440 outer_form_widget, unhide_args, XtNumber(unhide_args));
441 XtAddCallback(unhide_widget, "callback", unhideAction, NULL);
442 }
443
444 /* Add item to skip queue */
445
446 sk = (skip_item *)store_malloc(sizeof(skip_item) + Ustrlen(s));
447 sk->next = queue_skip;
448 queue_skip = sk;
449 Ustrcpy(sk->text, s);
450 sk->reveal = time(NULL) + 60 * 60;
451 scan_spool_input(TRUE);
452 queue_display();
453 tick_queue_accumulator = 0;
454 break;
455 }
456}
457
458
459
460/*************************************************
461* Ticker function *
462*************************************************/
463
464/* This function is called initially to set up the starting data
465values; it then sets a timeout so that it continues to be called
466every 2 seconds. */
467
468static void ticker(XtPointer pt, XtIntervalId *i)
469{
470pipe_item **pp = &pipe_chain;
471pipe_item *p = pipe_chain;
472tick_queue_accumulator += tick_interval;
473tick_stripchart_accumulator += tick_interval;
474read_log();
475
476pt = pt; /* Keep picky compilers happy */
477i = i;
478
479/* If we have passed the queue update time, we must do a full
480scan of the queue, checking for new arrivals, etc. This will
481as a by-product set the count of items for use by the stripchart
482display. On some systems, SIGCHLD signals can get lost at busy times,
483so just in case, clean up any completed children here. */
484
485if (tick_queue_accumulator >= queue_update)
486 {
487 scan_spool_input(TRUE);
488 queue_display();
489 tick_queue_accumulator = 0;
490 if (tick_stripchart_accumulator >= stripchart_update)
491 tick_stripchart_accumulator = 0;
492 while (waitpid(-1, NULL, WNOHANG) > 0);
493 }
494
495/* Otherwise, if we have exceeded the stripchart interval,
496do a reduced queue scan that simply provides the count for
497the stripchart. */
498
499else if (tick_stripchart_accumulator >= stripchart_update)
500 {
501 scan_spool_input(FALSE);
502 tick_stripchart_accumulator = 0;
503 }
504
505/* Scan any pipes that are set up for listening to delivery processes,
506and display their output if their windows are still open. */
507
508while (p != NULL)
509 {
510 int count;
511 uschar buffer[256];
512
513 while ((count = read(p->fd, buffer, 254)) > 0)
514 {
515 buffer[count] = 0;
516 if (p->widget != NULL) text_show(p->widget, buffer);
517 }
518
519 if (count == 0)
520 {
521 close(p->fd);
522 *pp = p->next;
523 store_free(p);
524 /* If configured, cause display update */
525 if (action_queue_update) tick_queue_accumulator = 999999;
526 }
527
528 else pp = &(p->next);
529
530 p = *pp;
531 }
532
533/* Reset the timer for next time */
534
535XtAppAddTimeOut(X_appcon, tick_interval * 1000, ticker, 0);
536}
537
538
539
540/*************************************************
541* Find Num Lock modifiers *
542*************************************************/
543
544/* Return a string with the modifiers generated by XK_Num_Lock, or return
545NULL if XK_Num_Lock doesn't generate any modifiers. This is needed because Num
546Lock isn't always the same modifier on all servers.
547
548Arguments:
549 display the Display
550 buf a buffer in which to put the answers (long enough to hold 5)
551
552Returns: points to the buffer, or NULL
553*/
554
555static uschar *
556numlock_modifiers(Display *display, uschar *buf)
557{
558XModifierKeymap *m;
559int i, j;
560uschar *ret = NULL;
561
562m = XGetModifierMapping(display);
563if (m == NULL)
564 {
565 printf("Not enough memory\n");
566 exit (EXIT_FAILURE);
567 }
568
569/* Look at Mod1 through Mod5, and fill in the buffer as necessary. */
570
571buf[0] = 0;
572for (i = 3; i < 8; i++)
573 {
574 for (j = 0; j < m->max_keypermod; j++)
575 {
576 if (XKeycodeToKeysym(display, m->modifiermap [i*m->max_keypermod + j], 0)
577 == XK_Num_Lock)
578 {
579 sprintf(CS(buf+Ustrlen(buf)), " Mod%d", i-2);
580 ret = buf;
581 }
582 }
583 }
584
585XFreeModifiermap(m);
586return ret;
587}
588
589
590
591/*************************************************
592* Initialize *
593*************************************************/
594
595int main(int argc, char **argv)
596{
597int i;
598struct stat statdata;
599uschar modbuf[] = " Mod1 Mod2 Mod3 Mod4 Mod5";
600uschar *numlock;
601Widget stripchart_form_widget,
602 update_widget,
603 quit_widget,
604 resize_widget;
605
606/* The exim global message_id needs to get set */
607
608message_id_external = message_id_option + 1;
609message_id = message_id_external + 1;
610message_subdir[1] = 0;
611
612/* Some store needs getting for big_buffer, which is used for
613constructing file names and things. This call will initialize
614the store_get() function. */
615
616big_buffer_size = 1024;
617big_buffer = store_get(big_buffer_size);
618
619/* Set up the version string and date and output them */
620
621version_init();
622printf("\nExim Monitor version %s (compiled %s) initializing\n",
623 version_string, version_date);
624
625/* Initialize various things from the environment and arguments. */
626
627init(argc, USS argv);
628
629/* Set up the SIGCHLD handler */
630
631signal(SIGCHLD, sigchld_handler);
632
633/* Get the buffer for storing the string for the log display. */
634
635log_display_buffer = (uschar *)store_malloc(log_buffer_size);
636log_display_buffer[0] = 0;
637
638/* Initialize the data structures for the stripcharts */
639
640stripchart_init();
641
642/* If log_file contains the empty string, then Exim is running using syslog
643only, and we can't tail the log. If not, open the log file and position to the
644end of it. Before doing so, we have to detect whether the log files are
645datestamped, and if so, sort out the name. The string in log_file already has
646%s replaced by "main"; if datestamping is occurring, %D or %M will be present.
647In fact, we don't need to test explicitly - just process the string with
648string_format.
649
650Once opened, save the file's inode so that we can detect when the file is
651switched to another one for non-datestamped files. However, allow the monitor
652to start up without a log file (can happen if no messages have been sent
653today.) */
654
655if (log_file[0] != 0)
656 {
657 /* Do *not* use "%s" here, we need the %D datestamp in the log_file to
658 be expanded! */
659 (void)string_format(log_file_open, sizeof(log_file_open), CS log_file);
660 log_datestamping = string_datestamp_offset >= 0;
661
662 LOG = fopen(CS log_file_open, "r");
663
664 if (LOG == NULL)
665 {
666 printf("*** eximon warning: can't open log file %s - will try "
667 "periodically\n", log_file_open);
668 }
669 else
670 {
671 fseek(LOG, 0, SEEK_END);
672 log_position = ftell(LOG);
673 fstat(fileno(LOG), &statdata);
674 log_inode = statdata.st_ino;
675 }
676 }
677else
678 {
679 printf("*** eximon warning: no log file available to tail\n");
680 }
681
682/* Now initialize the X world and create the top-level widget */
683
684toplevel_widget = XtAppInitialize(&X_appcon, "Eximon", NULL, 0, &argc, argv,
685 fallback_resources, NULL, 0);
686X_display = XtDisplay(toplevel_widget);
687xs_SetValues(toplevel_widget, 4,
688 "title", window_title,
689 "iconName", window_title,
690 "minWidth", min_width,
691 "minHeight", min_height);
692
693
694/* Create the action for setting up the menu in the queue display
695window, and register the action for positioning the menu. */
696
697XtAppAddActions(X_appcon, menu_action_table, 1);
698XawSimpleMenuAddGlobalActions(X_appcon);
699
700/* Set up translation tables for the text widgets we use. We don't
701want all the generality of editing, etc. that the defaults provide.
702This cannot be done before initializing X - the parser complains
703about unknown events, modifiers, etc. in an unhelpful way... The
704queue text widget has a different table which includes the button
705for popping up the menu. Note that the order of things in these
706tables is significant. Shift<thing> must come before <thing> as
707otherwise it isn't noticed. */
708
709/*
710 <FocusIn>: display-caret(on)\n\
711 <FocusOut>: display-caret(off)\n\
712*/
713
714/* The translation manager sets up passive grabs for the menu popups as a
715result of MenuPopup(), but the grabs match only the exact modifiers listed,
716hence combinations with and without caps-lock and num-lock must be given,
717rather than just one "Shift<Btn1Down>" (or whatever menu_event is set to),
718despite the fact that that notation (without a leading !) should ignore the
719state of other modifiers. Thanks to Kevin Ryde for this information, and for
720the function above that discovers which modifier is Num Lock, because it turns
721out that it varies from server to server. */
722
723sprintf(CS big_buffer,
724 "!%s: menu-create() XawPositionSimpleMenu(menu) MenuPopup(menu)\n\
725 !Lock %s: menu-create() XawPositionSimpleMenu(menu) MenuPopup(menu)\n\
726 ", menu_event, menu_event);
727
728numlock = numlock_modifiers(X_display, modbuf); /* Get Num Lock modifier(s) */
729
730if (numlock != NULL) sprintf(CS big_buffer + Ustrlen(big_buffer),
731 "!%s %s: menu-create() XawPositionSimpleMenu(menu) MenuPopup(menu)\n\
732 !Lock %s %s: menu-create() XawPositionSimpleMenu(menu) MenuPopup(menu)\n\
733 ", numlock, menu_event, numlock, menu_event);
734
735sprintf(CS big_buffer + Ustrlen(big_buffer),
736 "<Btn1Down>: select-start()\n\
737 <Btn1Motion>: extend-adjust()\n\
738 <Btn1Up>: extend-end(PRIMARY,CUT_BUFFER0)\n\
739 <Btn3Down>: extend-start()\n\
740 <Btn3Motion>: extend-adjust()\n\
741 <Btn3Up>: extend-end(PRIMARY,CUT_BUFFER0)\n\
742 <Key>Up: scroll-one-line-down()\n\
743 <Key>Down: scroll-one-line-up()\n\
744 Ctrl<Key>R: search(backward)\n\
745 Ctrl<Key>S: search(forward)\n\
746 ");
747
748queue_trans = XtParseTranslationTable(CS big_buffer);
749
750text_trans = XtParseTranslationTable(
751 "<Btn1Down>: select-start()\n\
752 <Btn1Motion>: extend-adjust()\n\
753 <Btn1Up>: extend-end(PRIMARY,CUT_BUFFER0)\n\
754 <Btn3Down>: extend-start()\n\
755 <Btn3Motion>: extend-adjust()\n\
756 <Btn3Up>: extend-end(PRIMARY,CUT_BUFFER0)\n\
757 <Key>Up: scroll-one-line-down()\n\
758 <Key>Down: scroll-one-line-up()\n\
759 Ctrl<Key>R: search(backward)\n\
760 Ctrl<Key>S: search(forward)\n\
761 ");
762
763
764/* Create a toplevel form widget to hold all the other things */
765
766outer_form_widget = XtCreateManagedWidget("form", formWidgetClass,
767 toplevel_widget, NULL, 0);
768
769/* Now create an inner form to hold the stripcharts */
770
771stripchart_form_widget = XtCreateManagedWidget("form", formWidgetClass,
772 outer_form_widget, NULL, 0);
773xs_SetValues(stripchart_form_widget, 5,
774 "defaultDistance", 8,
775 "left", XawChainLeft,
776 "right", XawChainLeft,
777 "top", XawChainTop,
778 "bottom", XawChainTop);
779
780/* Create the queue count stripchart and its label. */
781
782create_stripchart(stripchart_form_widget, queue_stripchart_name);
783
784/* If configured, create the size monitoring stripchart, but
785only if the OS supports statfs(). */
786
787if (size_stripchart != NULL)
788 {
789#ifdef HAVE_STATFS
790 if (size_stripchart_name == NULL)
791 {
792 size_stripchart_name = size_stripchart + Ustrlen(size_stripchart) - 1;
793 while (size_stripchart_name > size_stripchart &&
794 *size_stripchart_name == '/') size_stripchart_name--;
795 while (size_stripchart_name > size_stripchart &&
796 *size_stripchart_name != '/') size_stripchart_name--;
797 }
798 create_stripchart(stripchart_form_widget, size_stripchart_name);
799#else
800 printf("Can't create size stripchart: statfs() function not available\n");
801#endif
802 }
803
804/* Now create the configured input/output stripcharts; note
805the total number includes the queue stripchart. */
806
807for (i = stripchart_varstart; i < stripchart_number; i++)
808 create_stripchart(stripchart_form_widget, stripchart_title[i]);
809
810/* Next in vertical order come the Resize & Quit buttons */
811
812quit_args[0].value = (XtArgVal) stripchart_form_widget;
813quit_widget = XtCreateManagedWidget("quit", commandWidgetClass,
814 outer_form_widget, quit_args, XtNumber(quit_args));
815XtAddCallback(quit_widget, "callback", quitAction, NULL);
816
817resize_args[0].value = (XtArgVal) stripchart_form_widget;
818resize_args[1].value = (XtArgVal) quit_widget;
819resize_widget = XtCreateManagedWidget("resize", commandWidgetClass,
820 outer_form_widget, resize_args, XtNumber(resize_args));
821XtAddCallback(resize_widget, "callback", resizeAction, NULL);
822
823/* In the absence of log tailing, the quit widget is the one above the
824queue listing. */
825
826above_queue_widget = quit_widget;
827
828/* Create an Ascii text widget for the log tail display if we are tailing a
829log. Skip it if not. */
830
831if (log_file[0] != 0)
832 {
833 log_args[0].value = (XtArgVal) quit_widget;
834 log_widget = XtCreateManagedWidget("log", asciiTextWidgetClass,
835 outer_form_widget, log_args, XtNumber(log_args));
836 XawTextDisplayCaret(log_widget, TRUE);
837 xs_SetValues(log_widget, 6,
838 "editType", XawtextEdit,
839 "translations", text_trans,
840 "string", log_display_buffer,
841 "length", log_buffer_size,
842 "height", log_depth,
843 "width", log_width);
844
845 if (log_font != NULL)
846 {
847 XFontStruct *f = XLoadQueryFont(X_display, CS log_font);
848 if (f != NULL) xs_SetValues(log_widget, 1, "font", f);
849 }
850
851 above_queue_widget = log_widget;
852 }
853
854/* The update button */
855
856update_args[0].value = (XtArgVal) above_queue_widget;
857update_widget = XtCreateManagedWidget("update", commandWidgetClass,
858 outer_form_widget, update_args, XtNumber(update_args));
859XtAddCallback(update_widget, "callback", updateAction, NULL);
860
861/* The hide button */
862
863hide_args[0].value = (XtArgVal) above_queue_widget;
864hide_args[1].value = (XtArgVal) update_widget;
865hide_widget = XtCreateManagedWidget("hide", commandWidgetClass,
866 outer_form_widget, hide_args, XtNumber(hide_args));
867XtAddCallback(hide_widget, "callback", hideAction, NULL);
868
869/* Create an Ascii text widget for the queue display. */
870
871queue_args[0].value = (XtArgVal) update_widget;
872queue_widget = XtCreateManagedWidget("queue", asciiTextWidgetClass,
873 outer_form_widget, queue_args, XtNumber(queue_args));
874XawTextDisplayCaret(queue_widget, TRUE);
875
876xs_SetValues(queue_widget, 4,
877 "editType", XawtextEdit,
878 "height", queue_depth,
879 "width", queue_width,
880 "translations", queue_trans);
881
882if (queue_font != NULL)
883 {
884 XFontStruct *f = XLoadQueryFont(X_display, CS queue_font);
885 if (f != NULL) xs_SetValues(queue_widget, 1, "font", f);
886 }
887
888/* Call the ticker function to get the initial data set up. It
889arranges to have itself recalled every 2 seconds. */
890
891ticker(NULL, NULL);
892
893/* Everything is now set up; this flag is used by the regerror
894function and also by the queue reader. */
895
896eximon_initialized = TRUE;
897printf("\nExim Monitor running\n");
898
899/* Realize the toplevel and thereby get things displayed */
900
901XtRealizeWidget(toplevel_widget);
902
903/* Find out the size of the initial window, and set that as its
904maximum. While we are at it, get the initial position. */
905
906sizepos_args[0].value = (XtArgVal)(&maxwidth);
907sizepos_args[1].value = (XtArgVal)(&maxheight);
908sizepos_args[2].value = (XtArgVal)(&original_x);
909sizepos_args[3].value = (XtArgVal)(&original_y);
910XtGetValues(toplevel_widget, sizepos_args, 4);
911
912xs_SetValues(toplevel_widget, 2,
913 "maxWidth", maxwidth,
914 "maxHeight", maxheight);
915
916/* Set up the size of the screen */
917
918screenwidth = XDisplayWidth(X_display, 0);
919screenheight= XDisplayHeight(X_display,0);
920
921/* Register the action table */
922
923XtAppAddActions(X_appcon, actionTable, actionTableSize);
924
925/* Reduce the window to the small size if this is wanted */
926
927if (start_small) resizeAction(NULL, NULL, NULL);
928
929/* Enter the application loop which handles things from here
930onwards. The return statement is never obeyed, but is needed to
931keep pedantic ANSI compilers happy. */
932
933XtAppMainLoop(X_appcon);
934
935return 0;
936}
937
938/* End of em_main.c */
939