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