Commit | Line | Data |
---|---|---|
059ec3d9 PH |
1 | /************************************************* |
2 | * Exim Monitor * | |
3 | *************************************************/ | |
4 | ||
0a49a7a4 | 5 | /* Copyright (c) University of Cambridge 1995 - 2009 */ |
059ec3d9 PH |
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 */ |