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