]> jfr.im git - irc/quakenet/snircd.git/blob - ircd/userload.c
Initial import of 2.10.12.01
[irc/quakenet/snircd.git] / ircd / userload.c
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(&current_load, 0, sizeof(current_load));
269 update_load(); /* Initialize the load list */
270 }