Merge branch 'master' into transp_logging_1031
[users/jgh/exim.git] / src / exim_monitor / em_strip.c
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 functions for handling stripcharts */
12
13
14 /*************************************************
15 *               Static variables                 *
16 *************************************************/
17
18 static int     queue_first_time = 1;         /* flag for resetting time */
19 static int     size_first_time = 1;          /* and another */
20
21 static int     stripchart_count = 0;         /* count stripcharts created */
22 static int    *stripchart_delay;             /* vector of delay counts */
23 static Widget *stripchart_label;             /* vector of label widgets */
24 static int    *stripchart_last_total;        /* vector of prevous values */
25 static int    *stripchart_max;               /* vector of maxima */
26 static int    *stripchart_middelay;          /* vector of */
27 static int    *stripchart_midmax;            /* vector of */
28 static uschar  **stripchart_name;              /* vector of name strings */
29 static Widget  stripchart_prev_chart = NULL; /* previously created chart */
30 static Widget  stripchart_prev_label = NULL; /* previously created label */
31
32
33
34 /*************************************************
35 *               Initialize                       *
36 *************************************************/
37
38 void stripchart_init(void)
39 {
40 stripchart_delay =      (int *)store_malloc(stripchart_number * sizeof(int));
41 stripchart_label =   (Widget *)store_malloc(stripchart_number * sizeof(Widget));
42 stripchart_last_total = (int *)store_malloc(stripchart_number * sizeof(int));
43 stripchart_max =        (int *)store_malloc(stripchart_number * sizeof(int));
44 stripchart_middelay =   (int *)store_malloc(stripchart_number * sizeof(int));
45 stripchart_midmax =     (int *)store_malloc(stripchart_number * sizeof(int));
46 stripchart_name =     (uschar **)store_malloc(stripchart_number * sizeof(uschar *));
47 stripchart_total =      (int *)store_malloc(stripchart_number * sizeof(int));
48 }
49
50
51
52 /*************************************************
53 *           Stripchart callback function         *
54 *************************************************/
55
56 /* The client data is the index of the stripchart. We have to play
57 a little game in order to ensure that the double value is correctly
58 passed back via the value pointer without the compiler doing an
59 unwanted cast. */
60
61 static void stripchartAction(Widget w, XtPointer client_data, XtPointer value)
62 {
63 double *ptr = (double *)value;
64 static int thresholds[] =
65   {10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 0};
66 int num = (int)client_data;
67 int oldmax = 0;
68 int newmax = 0;
69 int newvalue = 0;
70 int i = 0;
71
72 /* For the queue stripchart, the value is the current vector value.
73 We reset the initial delay of 1 second to the normal value. */
74
75 if (num == 0)
76   {
77   newvalue = stripchart_total[0];
78   if (queue_first_time)
79     {
80     xs_SetValues(w, 1, "update", stripchart_update);
81     queue_first_time = 0;
82     }
83   }
84
85 /* For the size monitoring stripchart, the value is the percentage
86 fullness of the partition. A similar fudge to the above is implemented
87 for the first time. Not all OS have statvfs(); for those that don't this
88 code is omitted. In fact it should never be obeyed, as we don't allow
89 size_stripchart to get set in that case. For some OS the old function
90 and struct name statfs is used; that is handled by a macro. */
91
92 else if (size_stripchart != NULL && num == 1)
93   {
94 #ifdef HAVE_STATFS
95   struct statvfs statbuf;
96   if (statvfs(CS size_stripchart, &statbuf) == 0)
97     {
98     int used = statbuf.f_blocks - statbuf.f_bfree;
99     int max = used + statbuf.f_bavail;
100     double fraction = ((double)used) / ((double)max);
101     newvalue = (int)((fraction + 0.005) * 100.0);
102     }
103 #endif
104   if (size_first_time)
105     {
106     xs_SetValues(w, 1, "update", stripchart_update);
107     size_first_time = 0;
108     }
109   }
110
111 /* For the configured stripcharts, the value to be set is
112 the difference from last time; save the current total for
113 next time. */
114
115 else
116   {
117   newvalue = stripchart_total[num] - stripchart_last_total[num];
118   stripchart_last_total[num] = stripchart_total[num];
119   }
120
121 /* Adjust the scale of the stripchart according to the value;
122 we delay enlarging the scale for a while after the values
123 reduce. Keep the maximum value while delaying, and reset
124 down to that. For the size stripchart, the threshold is always
125 forced to be at least 100. */
126
127 while (thresholds[i] > 0)
128   {
129   int thresh = (size_stripchart != NULL && num == 1)? 100 : thresholds[i++];
130   if (newvalue < (double)thresh)
131     {
132     /* If the current maximum is less than required, or if it is
133     greater and we have delayed long enough, adjust the scale. */
134
135     if (stripchart_max[num] < thresh ||
136        (stripchart_max[num] > thresh && stripchart_delay[num]++ > 20))
137       {
138       uschar buffer[128];
139       newmax = (thresh > stripchart_midmax[num])?
140         thresh : stripchart_midmax[num];
141       if (newmax == 10) sprintf(CS buffer, "%s", stripchart_name[num]);
142         else sprintf(CS buffer, "%s x%d", stripchart_name[num], newmax/10);
143       if (size_stripchart != NULL && num == 1) Ustrcat(buffer, "%");
144       xs_SetValues(stripchart_label[num], 1, "label", buffer);
145       oldmax = stripchart_max[num];
146       stripchart_max[num] = newmax;
147       stripchart_midmax[num] = 0;
148       stripchart_delay[num] -= stripchart_middelay[num];
149       }
150
151     /* Otherwise, if the current maximum is greater than required,
152     keep the highest value encountered during the delay, and its
153     position so we can adjust the delay when re-scaling. */
154
155     else if (stripchart_max[num] > thresh)
156       {
157       if (thresh > stripchart_midmax[num])
158         {
159         stripchart_midmax[num] = thresh;
160         stripchart_middelay[num] = stripchart_delay[num];
161         }
162       }
163
164     /* If the maximum is exactly what we need, reset the delay. */
165
166     if (stripchart_max[num] == thresh) stripchart_delay[num] = 0;
167     break;
168     }
169   }
170
171 /* The vanilla Athena stripchart widget does not support change of
172 scale - it just draws scale lines closer and closer together, which
173 doesn't work when the number gets very large. However, we can cause
174 it to change scale quite simply by recomputing all the values and
175 then calling its repaint routine. I had to nobble the repaint routine
176 too, to stop it changing scale to anything other than 10. There's
177 probably a better way to do this, like adding some new resource, but
178 I'm not a widget programmer and want to get on with the rest of
179 eximon... */
180
181 if (oldmax > 0)
182   {
183   int i;
184   StripChartWidget ww = (StripChartWidget)w;
185   ww->strip_chart.max_value = 0;
186   for (i = 0; i < (int)ww->strip_chart.interval; i++)
187     {
188     ww->strip_chart.valuedata[i] =
189       (ww->strip_chart.valuedata[i] * oldmax)/newmax;
190     if (ww->strip_chart.valuedata[i] > ww->strip_chart.max_value)
191       ww->strip_chart.max_value = ww->strip_chart.valuedata[i];
192     }
193   XClearWindow( XtDisplay(w), XtWindow(w));
194   ww->strip_chart.interval = repaint_window(ww, 0, (int)w->core.width);
195   }
196
197 /* Pass back the new value at the new scale */
198
199 *ptr = ((double)newvalue * 10.0)/(double)(stripchart_max[num]);
200 }
201
202
203
204 /*************************************************
205 *            Create one stripchart               *
206 *************************************************/
207
208 /* This function creates two widgets, one being the title and the other being
209 the stripchart. The client_data values for each stripchart are index into the
210 stripchart_values vector; each new stripchart just gets the next number. There
211 is a fudge for the very first stripchart, which is the queue length display,
212 and for the second if it is a partition size display; its update time is
213 initially set to 1 second so that it gives an immediate display of the queue.
214 The first time its callback function is obeyed, the update time gets reset. */
215
216 void create_stripchart(Widget parent, uschar *title)
217 {
218 Widget chart;
219
220 Widget label = XtCreateManagedWidget("label",
221   labelWidgetClass, parent, NULL, 0);
222
223 xs_SetValues(label, 10,
224   "label",          title,
225   "width",          stripchart_width + 2,
226   "borderWidth",    0,
227   "internalHeight", 0,
228   "internalWidth",  0,
229   "left",           XawChainLeft,
230   "right",          XawChainLeft,
231   "top",            XawChainTop,
232   "bottom",         XawChainTop,
233   XtNfromHoriz,     stripchart_prev_label);
234
235 chart = XtCreateManagedWidget("stripchart",
236   mystripChartWidgetClass, parent, NULL, 0);
237
238 xs_SetValues(chart, 11,
239   "jumpScroll", 1,
240   "update",     (stripchart_count < stripchart_varstart)? 1:stripchart_update,
241   "minScale",   10,
242   "width",      stripchart_width,
243   "height",     stripchart_height,
244   "left",       XawChainLeft,
245   "right",      XawChainLeft,
246   "top",        XawChainTop,
247   "bottom",     XawChainTop,
248   XtNfromHoriz, stripchart_prev_chart,
249   XtNfromVert,  label);
250
251 XtAddCallback(chart, "getValue", stripchartAction,
252   (XtPointer)stripchart_count);
253
254 stripchart_last_total[stripchart_count] = 0;
255 stripchart_max[stripchart_count] = 10;
256 stripchart_midmax[stripchart_count] = 0;
257 stripchart_name[stripchart_count] = title;
258 stripchart_prev_label = stripchart_label[stripchart_count] = label;
259 stripchart_prev_chart = chart;
260 stripchart_total[stripchart_count] = 0;
261 stripchart_count++;
262 }
263
264 /* End of em_strip.c */