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 */ | |
4c04137d | 24 | static int *stripchart_last_total; /* vector of previous values */ |
059ec3d9 PH |
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 | ||
4dc2379a JH |
61 | static void |
62 | stripchartAction(Widget w, XtPointer client_data, XtPointer value) | |
059ec3d9 | 63 | { |
4dc2379a | 64 | double * ptr = (double *)value; |
059ec3d9 PH |
65 | static int thresholds[] = |
66 | {10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 0}; | |
4dc2379a | 67 | int num = (long)client_data; |
059ec3d9 PH |
68 | int oldmax = 0; |
69 | int newmax = 0; | |
70 | int newvalue = 0; | |
71 | int i = 0; | |
72 | ||
73 | /* For the queue stripchart, the value is the current vector value. | |
74 | We reset the initial delay of 1 second to the normal value. */ | |
75 | ||
76 | if (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 | |
87 | fullness of the partition. A similar fudge to the above is implemented | |
88 | for the first time. Not all OS have statvfs(); for those that don't this | |
89 | code is omitted. In fact it should never be obeyed, as we don't allow | |
90 | size_stripchart to get set in that case. For some OS the old function | |
91 | and struct name statfs is used; that is handled by a macro. */ | |
92 | ||
93 | else 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 | |
113 | the difference from last time; save the current total for | |
114 | next time. */ | |
115 | ||
116 | else | |
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; | |
123 | we delay enlarging the scale for a while after the values | |
124 | reduce. Keep the maximum value while delaying, and reset | |
125 | down to that. For the size stripchart, the threshold is always | |
126 | forced to be at least 100. */ | |
127 | ||
128 | while (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); | |
f3ebb786 | 144 | if (size_stripchart != NULL && num == 1) Ustrcat(buffer, US"%"); |
059ec3d9 PH |
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 | |
173 | scale - it just draws scale lines closer and closer together, which | |
174 | doesn't work when the number gets very large. However, we can cause | |
175 | it to change scale quite simply by recomputing all the values and | |
176 | then calling its repaint routine. I had to nobble the repaint routine | |
177 | too, to stop it changing scale to anything other than 10. There's | |
178 | probably a better way to do this, like adding some new resource, but | |
179 | I'm not a widget programmer and want to get on with the rest of | |
180 | eximon... */ | |
181 | ||
182 | if (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 | |
210 | the stripchart. The client_data values for each stripchart are index into the | |
211 | stripchart_values vector; each new stripchart just gets the next number. There | |
212 | is a fudge for the very first stripchart, which is the queue length display, | |
213 | and for the second if it is a partition size display; its update time is | |
214 | initially set to 1 second so that it gives an immediate display of the queue. | |
215 | The first time its callback function is obeyed, the update time gets reset. */ | |
216 | ||
4dc2379a JH |
217 | void |
218 | create_stripchart(Widget parent, uschar *title) | |
059ec3d9 PH |
219 | { |
220 | Widget chart; | |
221 | ||
222 | Widget label = XtCreateManagedWidget("label", | |
223 | labelWidgetClass, parent, NULL, 0); | |
224 | ||
225 | xs_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 | ||
237 | chart = XtCreateManagedWidget("stripchart", | |
238 | mystripChartWidgetClass, parent, NULL, 0); | |
239 | ||
240 | xs_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 | ||
253 | XtAddCallback(chart, "getValue", stripchartAction, | |
4dc2379a | 254 | (XtPointer)(long)stripchart_count); |
059ec3d9 PH |
255 | |
256 | stripchart_last_total[stripchart_count] = 0; | |
257 | stripchart_max[stripchart_count] = 10; | |
258 | stripchart_midmax[stripchart_count] = 0; | |
259 | stripchart_name[stripchart_count] = title; | |
260 | stripchart_prev_label = stripchart_label[stripchart_count] = label; | |
261 | stripchart_prev_chart = chart; | |
262 | stripchart_total[stripchart_count] = 0; | |
263 | stripchart_count++; | |
264 | } | |
265 | ||
266 | /* End of em_strip.c */ |