1 /*************************************************
3 *************************************************/
5 /* Copyright (c) University of Cambridge 1995 - 2018 */
6 /* See the file NOTICE for conditions of use and distribution. */
11 /* This module contains the main program of the Exim monitor, which
12 sets up the world and then lets the XtAppMainLoop function
13 run things off X events. */
16 /*************************************************
18 *************************************************/
20 /* Fallback resources */
22 static String fallback_resources[] = {"eximon.geometry: +150+0", NULL};
24 /* X11 fixed argument lists */
26 static Arg quit_args[] = {
27 {XtNfromVert, (XtArgVal) NULL}, /* must be first */
28 {XtNlabel, (XtArgVal) " Quit "},
29 {"left", XawChainLeft},
30 {"right", XawChainLeft},
32 {"bottom", XawChainTop}
35 static 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},
42 {"bottom", XawChainTop}
45 static Arg update_args[] = {
46 {XtNfromVert, (XtArgVal) NULL}, /* must be first */
47 {XtNlabel, (XtArgVal) " Update "},
48 {"left", XawChainLeft},
49 {"right", XawChainLeft},
51 {"bottom", XawChainTop}
54 static 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},
61 {"bottom", XawChainTop}
64 static 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},
71 {"bottom", XawChainTop}
74 static 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},
83 {"bottom", XawChainTop}
86 static 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},
93 {"bottom", XawChainBottom}
96 static Arg sizepos_args[] = {
97 {"width", (XtArgVal)NULL},
98 {"height", (XtArgVal)NULL},
99 {"x", (XtArgVal)NULL},
100 {"y", (XtArgVal)NULL}
103 XtActionsRec menu_action_table[] = {
104 { "menu-create", menu_create } };
106 /* Types of non-message dialog action */
110 /* Miscellaneous local variables */
112 static int dialog_action;
113 static int tick_stripchart_accumulator = 999999;
114 static int tick_interval = 2;
115 static int maxposset = 0;
116 static int minposset = 0;
117 static int x_adjustment = -1;
118 static int y_adjustment = -1;
119 static Dimension screenwidth, screenheight;
120 static Dimension original_x, original_y;
121 static Dimension maxposx, maxposy;
122 static Dimension minposx, minposy;
123 static Dimension maxwidth, maxheight;
124 static Widget outer_form_widget;
125 static Widget hide_widget;
126 static Widget above_queue_widget;
131 #ifdef STRERROR_FROM_ERRLIST
132 /*************************************************
133 * Provide strerror() for non-ANSI libraries *
134 *************************************************/
136 /* Some old-fashioned systems still around (e.g. SunOS4) don't have strerror()
137 in their libraries, but can provide the same facility by this simple
138 alternative function. */
143 if (n < 0 || n >= sys_nerr) return "unknown error number";
144 return sys_errlist[n];
146 #endif /* STRERROR_FROM_ERRLIST */
150 /*************************************************
151 * Handle attempts to write the log *
152 *************************************************/
154 /* The message gets written to stderr when log_write() is called from a
155 utility. The message always gets '\n' added on the end of it. These calls come
156 from modules such as store.c when things go drastically wrong (e.g. malloc()
157 failing). In normal use they won't get obeyed.
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
169 log_write(unsigned int selector, int flags, const char *format, ...)
172 va_start(ap, format);
173 vfprintf(stderr, format, ap);
174 fprintf(stderr, "\n");
181 /*************************************************
182 * Extract port from address string *
183 *************************************************/
185 /* In the spool file, a host plus port is given as an IP address followed by a
186 dot and a port number. This function decodes this. It is needed by the
187 spool-reading function, and copied here to avoid having to include the whole
188 host.c module. One day the interaction between exim and eximon with regard to
189 included code MUST be tidied up!
192 address points to the string; if there is a port, the '.' in the string
193 is overwritten with zero to terminate the address
195 Returns: 0 if there is no port, else the port number.
199 host_address_extract_port(uschar *address)
201 int skip = -3; /* Skip 3 dots in IPv4 addresses */
203 while (*(++address) != 0)
206 if (ch == ':') skip = 0; /* Skip 0 dots in IPv6 addresses */
207 else if (ch == '.' && skip++ >= 0) break;
209 if (*address == 0) return 0;
211 return Uatoi(address);
217 /*************************************************
219 *************************************************/
221 /* Operations on messages are done in subprocesses; this handler
222 just catches them when they finish. It causes a queue display update
223 unless configured not to. */
225 static void sigchld_handler(int sig)
227 while (waitpid(-1, NULL, WNOHANG) > 0);
228 signal(sig, sigchld_handler);
229 if (action_queue_update) tick_queue_accumulator = 999999;
234 /*************************************************
235 * Callback routines *
236 *************************************************/
239 void updateAction(Widget w, XtPointer client_data, XtPointer call_data)
241 scan_spool_input(TRUE);
243 tick_queue_accumulator = 0;
246 void hideAction(Widget w, XtPointer client_data, XtPointer call_data)
248 actioned_message[0] = 0;
249 dialog_ref_widget = w;
250 dialog_action = da_hide;
251 create_dialog(US"Hide addresses ending with", US"");
254 void unhideAction(Widget w, XtPointer client_data, XtPointer call_data)
256 skip_item *sk = queue_skip;
260 skip_item *next = sk->next;
266 XtDestroyWidget(unhide_widget);
267 unhide_widget = NULL;
269 scan_spool_input(TRUE);
271 tick_queue_accumulator = 0;
274 void quitAction(Widget w, XtPointer client_data, XtPointer call_data)
280 /* Action when the "Size" button is pressed. This is a kludged up mess
281 that I made work after much messing around. Reading the position of the
282 toplevel widget gets the absolute position of the data portion of the window,
283 excluding the window manager's furniture. However, positioning the toplevel
284 widget's window seems to position the top corner of the furniture under the twm
285 window manager, but not under fwvm and others. The two cases are distinguished
286 by the values of x_adjustment and y_adjustment.
288 For twm (adjustment >= 0), one has to fudge the miminizing function to ensure
289 that we go back to exactly the same position as before.
291 For fwvm (adjustment < 0), one has to fudge the "top left hand corner"
292 positioning to ensure that the window manager's furniture gets displayed on the
293 screen. I haven't found a way of discovering the thickness of the furniture, so
294 some screwed-in values are used.
296 This is all ad hoc, developed by floundering around as I haven't found any
297 documentation that tells me what I really should do. */
299 void resizeAction(Widget button, XtPointer client_data, XtPointer call_data)
302 Dimension width, height;
304 Window w = XtWindow(toplevel_widget);
306 /* Get the position and size of the top level widget. */
308 sizepos_args[0].value = (XtArgVal)(&width);
309 sizepos_args[1].value = (XtArgVal)(&height);
310 sizepos_args[2].value = (XtArgVal)(&x);
311 sizepos_args[3].value = (XtArgVal)(&y);
312 XtGetValues(toplevel_widget, sizepos_args, 4);
314 /* Get the position of the widget's window relative to its parent; this
315 gives the thickness of the window manager's furniture. At least it does
316 in twm. For fwvm it gives zero. The size/movement function uses this data.
317 I tried doing this before entering the main loop, but it didn't always
318 work properly with twm. Running it every time seems to be OK. */
320 XGetWindowAttributes(X_display, XtWindow(toplevel_widget), &a);
321 if (a.x != 0) x_adjustment = a.x;
322 if (a.y != 0) y_adjustment = a.y;
324 /* If at maximum size, reduce to minimum and move back to where it was
325 when maximized, if that value is set, allowing for the furniture in cases
326 where the positioning includes the furniture. */
328 if (width == maxwidth && height == maxheight)
335 xs_SetValues(toplevel_widget, 4,
337 "height", min_height,
338 "x", minposx - ((x_adjustment >= 0)? x_adjustment : 0),
339 "y", minposy - ((y_adjustment >= 0)? y_adjustment : 0));
341 xs_SetValues(toplevel_widget, 2,
343 "height", min_height);
346 /* Else always expand to maximum. If currently at minimum size, remember where
347 it was for coming back. If we don't have a value for the thickness of the
348 furniture, the implication is that the coordinates position the application
349 window, so we can't use (0,0) because that loses the furniture. Use screwed in
350 values that seem to work with fvwm. */
357 if (width == min_width && height == min_height)
364 if ((int)(x + maxwidth) > (int)screenwidth ||
365 (int)(y + maxheight + 10) > (int)screenheight)
369 xx = maxposx - ((x_adjustment >= 0)? x_adjustment : 0);
370 yy = maxposy - ((y_adjustment >= 0)? y_adjustment : 0);
374 if ((int)(x + maxwidth) > (int)screenwidth)
375 xx = (x_adjustment >= 0)? 0 : 4;
376 if ((int)(y + maxheight + 10) > (int)screenheight)
377 yy = (y_adjustment >= 0)? 0 : 21;
380 xs_SetValues(toplevel_widget, 4,
387 else xs_SetValues(toplevel_widget, 2,
389 "height", maxheight);
392 /* Ensure the window is at the top */
394 XRaiseWindow(X_display, w);
400 /*************************************************
401 * Handle input from non-msg dialogue *
402 *************************************************/
404 /* The various cases here are: hide domain, (no more yet) */
406 void NonMessageDialogue(uschar *s)
410 switch(dialog_action)
414 /* Create the unhide button if not present */
416 if (unhide_widget == NULL)
418 unhide_args[0].value = (XtArgVal) above_queue_widget;
419 unhide_args[1].value = (XtArgVal) hide_widget;
420 unhide_widget = XtCreateManagedWidget("unhide", commandWidgetClass,
421 outer_form_widget, unhide_args, XtNumber(unhide_args));
422 XtAddCallback(unhide_widget, "callback", unhideAction, NULL);
425 /* Add item to skip queue */
427 sk = (skip_item *)store_malloc(sizeof(skip_item) + Ustrlen(s));
428 sk->next = queue_skip;
430 Ustrcpy(sk->text, s);
431 sk->reveal = time(NULL) + 60 * 60;
432 scan_spool_input(TRUE);
434 tick_queue_accumulator = 0;
441 /*************************************************
443 *************************************************/
445 /* This function is called initially to set up the starting data
446 values; it then sets a timeout so that it continues to be called
449 static void ticker(XtPointer pt, XtIntervalId *i)
451 pipe_item **pp = &pipe_chain;
452 pipe_item *p = pipe_chain;
453 tick_queue_accumulator += tick_interval;
454 tick_stripchart_accumulator += tick_interval;
457 /* If we have passed the queue update time, we must do a full
458 scan of the queue, checking for new arrivals, etc. This will
459 as a by-product set the count of items for use by the stripchart
460 display. On some systems, SIGCHLD signals can get lost at busy times,
461 so just in case, clean up any completed children here. */
463 if (tick_queue_accumulator >= queue_update)
465 scan_spool_input(TRUE);
467 tick_queue_accumulator = 0;
468 if (tick_stripchart_accumulator >= stripchart_update)
469 tick_stripchart_accumulator = 0;
470 while (waitpid(-1, NULL, WNOHANG) > 0);
473 /* Otherwise, if we have exceeded the stripchart interval,
474 do a reduced queue scan that simply provides the count for
477 else if (tick_stripchart_accumulator >= stripchart_update)
479 scan_spool_input(FALSE);
480 tick_stripchart_accumulator = 0;
483 /* Scan any pipes that are set up for listening to delivery processes,
484 and display their output if their windows are still open. */
491 while ((count = read(p->fd, buffer, 254)) > 0)
494 if (p->widget != NULL) text_show(p->widget, buffer);
502 /* If configured, cause display update */
503 if (action_queue_update) tick_queue_accumulator = 999999;
506 else pp = &(p->next);
511 /* Reset the timer for next time */
513 XtAppAddTimeOut(X_appcon, tick_interval * 1000, ticker, 0);
518 /*************************************************
519 * Find Num Lock modifiers *
520 *************************************************/
522 /* Return a string with the modifiers generated by XK_Num_Lock, or return
523 NULL if XK_Num_Lock doesn't generate any modifiers. This is needed because Num
524 Lock isn't always the same modifier on all servers.
528 buf a buffer in which to put the answers (long enough to hold 5)
530 Returns: points to the buffer, or NULL
534 numlock_modifiers(Display *display, uschar *buf)
540 m = XGetModifierMapping(display);
543 printf("Not enough memory\n");
547 /* Look at Mod1 through Mod5, and fill in the buffer as necessary. */
550 for (i = 3; i < 8; i++)
552 for (j = 0; j < m->max_keypermod; j++)
554 if (XKeycodeToKeysym(display, m->modifiermap [i*m->max_keypermod + j], 0)
557 sprintf(CS(buf+Ustrlen(buf)), " Mod%d", i-2);
569 /*************************************************
571 *************************************************/
573 int main(int argc, char **argv)
576 struct stat statdata;
577 uschar modbuf[] = " Mod1 Mod2 Mod3 Mod4 Mod5";
579 Widget stripchart_form_widget,
584 /* The exim global message_id needs to get set */
586 message_id_external = message_id_option + 1;
587 message_id = message_id_external + 1;
588 message_subdir[1] = 0;
590 /* Some store needs getting for big_buffer, which is used for
591 constructing file names and things. This call will initialize
592 the store_get() function. */
594 big_buffer = store_get(big_buffer_size, FALSE);
596 /* Set up the version string and date and output them */
599 printf("\nExim Monitor version %s (compiled %s) initializing\n",
600 version_string, version_date);
602 /* Initialize various things from the environment and arguments. */
604 init(argc, USS argv);
606 /* Set up the SIGCHLD handler */
608 signal(SIGCHLD, sigchld_handler);
610 /* Get the buffer for storing the string for the log display. */
612 log_display_buffer = US store_malloc(log_buffer_size);
613 log_display_buffer[0] = 0;
615 /* Initialize the data structures for the stripcharts */
619 /* If log_file contains the empty string, then Exim is running using syslog
620 only, and we can't tail the log. If not, open the log file and position to the
621 end of it. Before doing so, we have to detect whether the log files are
622 datestamped, and if so, sort out the name. The string in log_file already has
623 %s replaced by "main"; if datestamping is occurring, %D or %M will be present.
624 In fact, we don't need to test explicitly - just process the string with
627 Once opened, save the file's inode so that we can detect when the file is
628 switched to another one for non-datestamped files. However, allow the monitor
629 to start up without a log file (can happen if no messages have been sent
632 if (log_file[0] != 0)
634 /* Do *not* use "%s" here, we need the %D datestamp in the log_file to
636 (void)string_format(log_file_open, sizeof(log_file_open), CS log_file, NULL);
637 log_datestamping = string_datestamp_offset >= 0;
639 LOG = fopen(CS log_file_open, "r");
643 printf("*** eximon warning: can't open log file %s - will try "
644 "periodically\n", log_file_open);
648 fseek(LOG, 0, SEEK_END);
649 log_position = ftell(LOG);
650 if (fstat(fileno(LOG), &statdata))
652 perror("log file fstat");
657 log_inode = statdata.st_ino;
662 printf("*** eximon warning: no log file available to tail\n");
665 /* Now initialize the X world and create the top-level widget */
667 toplevel_widget = XtAppInitialize(&X_appcon, "Eximon", NULL, 0, &argc, argv,
668 fallback_resources, NULL, 0);
669 X_display = XtDisplay(toplevel_widget);
670 xs_SetValues(toplevel_widget, 4,
671 "title", window_title,
672 "iconName", window_title,
673 "minWidth", min_width,
674 "minHeight", min_height);
677 /* Create the action for setting up the menu in the queue display
678 window, and register the action for positioning the menu. */
680 XtAppAddActions(X_appcon, menu_action_table, 1);
681 XawSimpleMenuAddGlobalActions(X_appcon);
683 /* Set up translation tables for the text widgets we use. We don't
684 want all the generality of editing, etc. that the defaults provide.
685 This cannot be done before initializing X - the parser complains
686 about unknown events, modifiers, etc. in an unhelpful way... The
687 queue text widget has a different table which includes the button
688 for popping up the menu. Note that the order of things in these
689 tables is significant. Shift<thing> must come before <thing> as
690 otherwise it isn't noticed. */
693 <FocusIn>: display-caret(on)\n\
694 <FocusOut>: display-caret(off)\n\
697 /* The translation manager sets up passive grabs for the menu popups as a
698 result of MenuPopup(), but the grabs match only the exact modifiers listed,
699 hence combinations with and without caps-lock and num-lock must be given,
700 rather than just one "Shift<Btn1Down>" (or whatever menu_event is set to),
701 despite the fact that that notation (without a leading !) should ignore the
702 state of other modifiers. Thanks to Kevin Ryde for this information, and for
703 the function above that discovers which modifier is Num Lock, because it turns
704 out that it varies from server to server. */
706 sprintf(CS big_buffer,
707 "!%s: menu-create() XawPositionSimpleMenu(menu) MenuPopup(menu)\n\
708 !Lock %s: menu-create() XawPositionSimpleMenu(menu) MenuPopup(menu)\n\
709 ", menu_event, menu_event);
711 numlock = numlock_modifiers(X_display, modbuf); /* Get Num Lock modifier(s) */
713 if (numlock != NULL) sprintf(CS big_buffer + Ustrlen(big_buffer),
714 "!%s %s: menu-create() XawPositionSimpleMenu(menu) MenuPopup(menu)\n\
715 !Lock %s %s: menu-create() XawPositionSimpleMenu(menu) MenuPopup(menu)\n\
716 ", numlock, menu_event, numlock, menu_event);
718 sprintf(CS big_buffer + Ustrlen(big_buffer),
719 "<Btn1Down>: select-start()\n\
720 <Btn1Motion>: extend-adjust()\n\
721 <Btn1Up>: extend-end(PRIMARY,CUT_BUFFER0)\n\
722 <Btn3Down>: extend-start()\n\
723 <Btn3Motion>: extend-adjust()\n\
724 <Btn3Up>: extend-end(PRIMARY,CUT_BUFFER0)\n\
725 <Key>Up: scroll-one-line-down()\n\
726 <Key>Down: scroll-one-line-up()\n\
727 Ctrl<Key>R: search(backward)\n\
728 Ctrl<Key>S: search(forward)\n\
731 queue_trans = XtParseTranslationTable(CS big_buffer);
733 text_trans = XtParseTranslationTable(
734 "<Btn1Down>: select-start()\n\
735 <Btn1Motion>: extend-adjust()\n\
736 <Btn1Up>: extend-end(PRIMARY,CUT_BUFFER0)\n\
737 <Btn3Down>: extend-start()\n\
738 <Btn3Motion>: extend-adjust()\n\
739 <Btn3Up>: extend-end(PRIMARY,CUT_BUFFER0)\n\
740 <Key>Up: scroll-one-line-down()\n\
741 <Key>Down: scroll-one-line-up()\n\
742 Ctrl<Key>R: search(backward)\n\
743 Ctrl<Key>S: search(forward)\n\
747 /* Create a toplevel form widget to hold all the other things */
749 outer_form_widget = XtCreateManagedWidget("form", formWidgetClass,
750 toplevel_widget, NULL, 0);
752 /* Now create an inner form to hold the stripcharts */
754 stripchart_form_widget = XtCreateManagedWidget("form", formWidgetClass,
755 outer_form_widget, NULL, 0);
756 xs_SetValues(stripchart_form_widget, 5,
757 "defaultDistance", 8,
758 "left", XawChainLeft,
759 "right", XawChainLeft,
761 "bottom", XawChainTop);
763 /* Create the queue count stripchart and its label. */
765 create_stripchart(stripchart_form_widget, queue_stripchart_name);
767 /* If configured, create the size monitoring stripchart, but
768 only if the OS supports statfs(). */
770 if (size_stripchart != NULL)
773 if (size_stripchart_name == NULL)
775 size_stripchart_name = size_stripchart + Ustrlen(size_stripchart) - 1;
776 while (size_stripchart_name > size_stripchart &&
777 *size_stripchart_name == '/') size_stripchart_name--;
778 while (size_stripchart_name > size_stripchart &&
779 *size_stripchart_name != '/') size_stripchart_name--;
781 create_stripchart(stripchart_form_widget, size_stripchart_name);
783 printf("Can't create size stripchart: statfs() function not available\n");
787 /* Now create the configured input/output stripcharts; note
788 the total number includes the queue stripchart. */
790 for (i = stripchart_varstart; i < stripchart_number; i++)
791 create_stripchart(stripchart_form_widget, stripchart_title[i]);
793 /* Next in vertical order come the Resize & Quit buttons */
795 quit_args[0].value = (XtArgVal) stripchart_form_widget;
796 quit_widget = XtCreateManagedWidget("quit", commandWidgetClass,
797 outer_form_widget, quit_args, XtNumber(quit_args));
798 XtAddCallback(quit_widget, "callback", quitAction, NULL);
800 resize_args[0].value = (XtArgVal) stripchart_form_widget;
801 resize_args[1].value = (XtArgVal) quit_widget;
802 resize_widget = XtCreateManagedWidget("resize", commandWidgetClass,
803 outer_form_widget, resize_args, XtNumber(resize_args));
804 XtAddCallback(resize_widget, "callback", resizeAction, NULL);
806 /* In the absence of log tailing, the quit widget is the one above the
809 above_queue_widget = quit_widget;
811 /* Create an Ascii text widget for the log tail display if we are tailing a
812 log. Skip it if not. */
814 if (log_file[0] != 0)
816 log_args[0].value = (XtArgVal) quit_widget;
817 log_widget = XtCreateManagedWidget("log", asciiTextWidgetClass,
818 outer_form_widget, log_args, XtNumber(log_args));
819 XawTextDisplayCaret(log_widget, TRUE);
820 xs_SetValues(log_widget, 6,
821 "editType", XawtextEdit,
822 "translations", text_trans,
823 "string", log_display_buffer,
824 "length", log_buffer_size,
828 if (log_font != NULL)
830 XFontStruct *f = XLoadQueryFont(X_display, CS log_font);
831 if (f != NULL) xs_SetValues(log_widget, 1, "font", f);
834 above_queue_widget = log_widget;
837 /* The update button */
839 update_args[0].value = (XtArgVal) above_queue_widget;
840 update_widget = XtCreateManagedWidget("update", commandWidgetClass,
841 outer_form_widget, update_args, XtNumber(update_args));
842 XtAddCallback(update_widget, "callback", updateAction, NULL);
844 /* The hide button */
846 hide_args[0].value = (XtArgVal) above_queue_widget;
847 hide_args[1].value = (XtArgVal) update_widget;
848 hide_widget = XtCreateManagedWidget("hide", commandWidgetClass,
849 outer_form_widget, hide_args, XtNumber(hide_args));
850 XtAddCallback(hide_widget, "callback", hideAction, NULL);
852 /* Create an Ascii text widget for the queue display. */
854 queue_args[0].value = (XtArgVal) update_widget;
855 queue_widget = XtCreateManagedWidget("queue", asciiTextWidgetClass,
856 outer_form_widget, queue_args, XtNumber(queue_args));
857 XawTextDisplayCaret(queue_widget, TRUE);
859 xs_SetValues(queue_widget, 4,
860 "editType", XawtextEdit,
861 "height", queue_depth,
862 "width", queue_width,
863 "translations", queue_trans);
865 if (queue_font != NULL)
867 XFontStruct *f = XLoadQueryFont(X_display, CS queue_font);
868 if (f != NULL) xs_SetValues(queue_widget, 1, "font", f);
871 /* Call the ticker function to get the initial data set up. It
872 arranges to have itself recalled every 2 seconds. */
876 /* Everything is now set up; this flag is used by the regerror
877 function and also by the queue reader. */
879 eximon_initialized = TRUE;
880 printf("\nExim Monitor running\n");
882 /* Realize the toplevel and thereby get things displayed */
884 XtRealizeWidget(toplevel_widget);
886 /* Find out the size of the initial window, and set that as its
887 maximum. While we are at it, get the initial position. */
889 sizepos_args[0].value = (XtArgVal)(&maxwidth);
890 sizepos_args[1].value = (XtArgVal)(&maxheight);
891 sizepos_args[2].value = (XtArgVal)(&original_x);
892 sizepos_args[3].value = (XtArgVal)(&original_y);
893 XtGetValues(toplevel_widget, sizepos_args, 4);
895 xs_SetValues(toplevel_widget, 2,
896 "maxWidth", maxwidth,
897 "maxHeight", maxheight);
899 /* Set up the size of the screen */
901 screenwidth = XDisplayWidth(X_display, 0);
902 screenheight= XDisplayHeight(X_display,0);
904 /* Register the action table */
906 XtAppAddActions(X_appcon, actionTable, actionTableSize);
908 /* Reduce the window to the small size if this is wanted */
910 if (start_small) resizeAction(NULL, NULL, NULL);
912 /* Enter the application loop which handles things from here
913 onwards. The return statement is never obeyed, but is needed to
914 keep pedantic ANSI compilers happy. */
916 XtAppMainLoop(X_appcon);
921 /* End of em_main.c */