| 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 previous 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 |
| 62 | stripchartAction(Widget w, XtPointer client_data, XtPointer value) |
| 63 | { |
| 64 | double * ptr = (double *)value; |
| 65 | static int thresholds[] = |
| 66 | {10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 0}; |
| 67 | int num = (long)client_data; |
| 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); |
| 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 |
| 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 | |
| 217 | void |
| 218 | 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)(long)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 */ |