]>
Commit | Line | Data |
---|---|---|
189935b1 | 1 | /* |
2 | * Userload module by Michael L. VanLoon (mlv) <michaelv@iastate.edu> | |
3 | * Written 2/93. Originally grafted into irc2.7.2g 4/93. | |
4 | * | |
5 | * Rewritten 9/97 by Carlo Wood (Run) <carlo@runaway.xs4all.nl> | |
6 | * because previous version used ridiculous amounts of memory | |
7 | * (stored all loads of the passed three days ~ 8 megs). | |
8 | * | |
9 | * IRC - Internet Relay Chat, ircd/userload.c | |
10 | * Copyright (C) 1990 University of Oulu, Computing Center | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or modify | |
13 | * it under the terms of the GNU General Public License as published by | |
14 | * the Free Software Foundation; either version 1, or (at your option) | |
15 | * any later version. | |
16 | * | |
17 | * This program is distributed in the hope that it will be useful, | |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | * GNU General Public License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License | |
23 | * along with this program; if not, write to the Free Software | |
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
25 | */ | |
26 | /** @file | |
27 | * @brief Userload tracking and statistics. | |
28 | * @version $Id: userload.c,v 1.11 2004/10/05 04:21:36 entrope Exp $ | |
29 | */ | |
30 | #include "config.h" | |
31 | ||
32 | #include "userload.h" | |
33 | #include "client.h" | |
34 | #include "ircd.h" | |
35 | #include "msg.h" | |
36 | #include "numnicks.h" | |
37 | #include "querycmds.h" | |
38 | #include "s_misc.h" | |
39 | #include "s_stats.h" | |
40 | #include "send.h" | |
41 | #include "struct.h" | |
42 | #include "sys.h" | |
43 | ||
44 | #include <stdio.h> | |
45 | #include <string.h> | |
46 | #include <time.h> | |
47 | ||
48 | struct current_load_st current_load; /**< The current load */ | |
49 | ||
50 | static struct current_load_st cspm_sum; /**< Number of connections times number | |
51 | of seconds per minute. */ | |
52 | static struct current_load_st csph_sum; /**< Number of connections times number | |
53 | of seconds per hour. */ | |
54 | static struct current_load_st cspm[60]; /**< Last 60 minutes */ | |
55 | static struct current_load_st csph[72]; /**< Last 72 hours */ | |
56 | ||
57 | static int m_index; /**< Next entry to use in #cspm. */ | |
58 | static int h_index; /**< Next entry to use in #csph. */ | |
59 | ||
60 | /** Update load average to reflect a change in the local client count. | |
61 | */ | |
62 | void update_load(void) | |
63 | { | |
64 | static struct tm tm_now; /* Current time. */ | |
65 | static time_t last_sec; /* Seconds of last time that | |
66 | update_load() called. */ | |
67 | static time_t last_min; | |
68 | static time_t last; /* Last time that update_load() was called. */ | |
69 | static struct current_load_st last_load; /* The load last time that | |
70 | update_load() was called. */ | |
71 | static int initialized; /* Boolean, set when initialized. */ | |
72 | int diff_time; /* Temp. variable used to hold time intervals | |
73 | in seconds, minutes or hours. */ | |
74 | ||
75 | /* Update `current_load' */ | |
76 | current_load.client_count = UserStats.local_clients; | |
77 | current_load.conn_count = UserStats.local_clients + UserStats.local_servers; | |
78 | ||
79 | /* Nothing needed when still in the same second */ | |
80 | if (!(diff_time = CurrentTime - last)) | |
81 | { | |
82 | last_load = current_load; /* Update last_load to be the load last | |
83 | time that update_load() was called. */ | |
84 | return; | |
85 | } | |
86 | ||
87 | /* If we get here we entered a new second */ | |
88 | ||
89 | /* | |
90 | * Make sure we keep the accurate time in 'tm_now' | |
91 | */ | |
92 | if ((tm_now.tm_sec += diff_time) > 59) | |
93 | { | |
94 | /* This is done once every minute */ | |
95 | diff_time = tm_now.tm_sec / 60; | |
96 | tm_now.tm_sec -= 60 * diff_time; | |
97 | if ((tm_now.tm_min += diff_time) > 59) | |
98 | { | |
99 | /* This is done once every hour */ | |
100 | diff_time = tm_now.tm_min / 60; | |
101 | tm_now.tm_min -= 60 * diff_time; | |
102 | if ((tm_now.tm_hour += diff_time) > 23) | |
103 | { | |
104 | tm_now = *localtime(&CurrentTime); /* Only called once a day */ | |
105 | if (!initialized) | |
106 | { | |
107 | initialized = 1; | |
108 | last_sec = 60; | |
109 | last_min = tm_now.tm_min; | |
110 | } | |
111 | } | |
112 | } | |
113 | ||
114 | /* If we get here we entered a new minute */ | |
115 | ||
116 | /* Finish the calculation of cspm of the last minute first: */ | |
117 | diff_time = 60 - last_sec; | |
118 | cspm_sum.conn_count += last_load.conn_count * diff_time; | |
119 | cspm_sum.client_count += last_load.client_count * diff_time; | |
120 | cspm_sum.local_count += last_load.local_count * diff_time; | |
121 | ||
122 | /* Add the completed minute to the Connections*Seconds/Hour sum */ | |
123 | csph_sum.conn_count += cspm_sum.conn_count - cspm[m_index].conn_count; | |
124 | csph_sum.client_count += cspm_sum.client_count - cspm[m_index].client_count; | |
125 | csph_sum.local_count += cspm_sum.local_count - cspm[m_index].local_count; | |
126 | ||
127 | /* Store the completed minute in an array */ | |
128 | cspm[m_index] = cspm_sum; | |
129 | ||
130 | /* How long did last_cspm last ? */ | |
131 | diff_time = tm_now.tm_min - last_min; | |
132 | last_min = tm_now.tm_min; | |
133 | ||
134 | if (diff_time < 0) | |
135 | diff_time += 60; /* update_load() must be called at | |
136 | _least_ once an hour */ | |
137 | ||
138 | if (diff_time > 1) /* Did more then one minute pass ? */ | |
139 | { | |
140 | /* Calculate the constant load during those extra minutes */ | |
141 | cspm_sum.conn_count = last_load.conn_count * 60; | |
142 | cspm_sum.client_count = last_load.client_count * 60; | |
143 | cspm_sum.local_count = last_load.local_count * 60; | |
144 | } | |
145 | ||
146 | for (;;) | |
147 | { | |
148 | /* Increase minute index */ | |
149 | if (++m_index == 60) | |
150 | { | |
151 | m_index = 0; | |
152 | /* Keep a list of the last 72 hours */ | |
153 | csph[h_index] = csph_sum; | |
154 | if (++h_index == 72) | |
155 | h_index = 0; | |
156 | } | |
157 | ||
158 | if (--diff_time <= 0) /* '<' to prevent endless loop if update_load() | |
159 | was not called once an hour :/ */ | |
160 | break; | |
161 | ||
162 | /* Add extra minutes to the Connections*Seconds/Hour sum */ | |
163 | csph_sum.conn_count += cspm_sum.conn_count - cspm[m_index].conn_count; | |
164 | csph_sum.client_count += | |
165 | cspm_sum.client_count - cspm[m_index].client_count; | |
166 | csph_sum.local_count += cspm_sum.local_count - cspm[m_index].local_count; | |
167 | ||
168 | /* Store extra minutes in the array */ | |
169 | cspm[m_index] = cspm_sum; | |
170 | } | |
171 | ||
172 | /* Now start the calculation of the new minute: */ | |
173 | last_sec = tm_now.tm_sec; | |
174 | cspm_sum.conn_count = last_load.conn_count * last_sec; | |
175 | cspm_sum.client_count = last_load.client_count * last_sec; | |
176 | cspm_sum.local_count = last_load.local_count * last_sec; | |
177 | } | |
178 | else | |
179 | { | |
180 | /* A new second, but the same minute as last time */ | |
181 | /* How long did last_load last ? */ | |
182 | diff_time = tm_now.tm_sec - last_sec; | |
183 | last_sec = tm_now.tm_sec; | |
184 | if (diff_time == 1) /* Just one second ? */ | |
185 | { | |
186 | cspm_sum.conn_count += last_load.conn_count; | |
187 | cspm_sum.client_count += last_load.client_count; | |
188 | cspm_sum.local_count += last_load.local_count; | |
189 | } | |
190 | else | |
191 | { | |
192 | /* More then one second */ | |
193 | /* At most 3 integer multiplication per second */ | |
194 | cspm_sum.conn_count += last_load.conn_count * diff_time; | |
195 | cspm_sum.client_count += last_load.client_count * diff_time; | |
196 | cspm_sum.local_count += last_load.local_count * diff_time; | |
197 | } | |
198 | } | |
199 | last_load = current_load; /* Update last_load to be the load last | |
200 | time that update_load() was called. */ | |
201 | last = CurrentTime; | |
202 | } | |
203 | ||
204 | /** Statistics callback to display userload. | |
205 | * @param[in] sptr Client requesting statistics. | |
206 | * @param[in] sd Stats descriptor for request (ignored). | |
207 | * @param[in] param Extra parameter from user (ignored). | |
208 | */ | |
209 | void | |
210 | calc_load(struct Client *sptr, const struct StatDesc *sd, char *param) | |
211 | { | |
212 | /* *INDENT-OFF* */ | |
213 | static const char *header = | |
214 | /* ----.- ----.- ---- ---- ---- ------------ */ | |
215 | "Minute Hour Day Yest. YYest. Userload for:"; | |
216 | /* *INDENT-ON* */ | |
217 | static const char *what[3] = { | |
218 | "local clients", | |
219 | "total clients", | |
220 | "total connections" | |
221 | }; | |
222 | int i, j, times[5][3]; /* [min,hour,day,Yest,YYest] | |
223 | [local,client,conn] */ | |
224 | int last_m_index = m_index, last_h_index = h_index; | |
225 | ||
226 | update_load(); /* We want stats accurate as of *now* */ | |
227 | ||
228 | if (--last_m_index < 0) | |
229 | last_m_index = 59; | |
230 | times[0][0] = (cspm[last_m_index].local_count + 3) / 6; | |
231 | times[0][1] = (cspm[last_m_index].client_count + 3) / 6; | |
232 | times[0][2] = (cspm[last_m_index].conn_count + 3) / 6; | |
233 | ||
234 | times[1][0] = (csph_sum.local_count + 180) / 360; | |
235 | times[1][1] = (csph_sum.client_count + 180) / 360; | |
236 | times[1][2] = (csph_sum.conn_count + 180) / 360; | |
237 | ||
238 | for (i = 2; i < 5; ++i) | |
239 | { | |
240 | times[i][0] = 43200; | |
241 | times[i][1] = 43200; | |
242 | times[i][2] = 43200; | |
243 | for (j = 0; j < 24; ++j) | |
244 | { | |
245 | if (--last_h_index < 0) | |
246 | last_h_index = 71; | |
247 | times[i][0] += csph[last_h_index].local_count; | |
248 | times[i][1] += csph[last_h_index].client_count; | |
249 | times[i][2] += csph[last_h_index].conn_count; | |
250 | } | |
251 | times[i][0] /= 86400; | |
252 | times[i][1] /= 86400; | |
253 | times[i][2] /= 86400; | |
254 | } | |
255 | ||
256 | sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, header); | |
257 | for (i = 0; i < 3; ++i) | |
258 | sendcmdto_one(&me, CMD_NOTICE, sptr, | |
259 | "%C :%4d.%1d %4d.%1d %4d %4d %4d %s", sptr, | |
260 | times[0][i] / 10, times[0][i] % 10, | |
261 | times[1][i] / 10, times[1][i] % 10, | |
262 | times[2][i], times[3][i], times[4][i], what[i]); | |
263 | } | |
264 | ||
265 | /** Initialize the userload statistics. */ | |
266 | void initload(void) | |
267 | { | |
268 | memset(¤t_load, 0, sizeof(current_load)); | |
269 | update_load(); /* Initialize the load list */ | |
270 | } |