e195fab43eeed5fc4b37122579f561cce29bf9f5
[ircu2.10.12-pk.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
27 #include "sys.h"
28 #include <stdio.h>
29 #include <signal.h>
30 #include <sys/resource.h>
31 #include "h.h"
32 #include "struct.h"
33 #include "send.h"
34 #include "s_misc.h"
35 #include "userload.h"
36 #include "ircd.h"
37 #include "numnicks.h"
38 #include "s_serv.h"
39 #include "querycmds.h"
40
41 RCSTAG_CC("$Id$");
42
43 struct current_load_st current_load;    /* The current load */
44
45 static struct current_load_st cspm_sum; /* Number of connections times number
46                                            of seconds per minute. */
47 static struct current_load_st csph_sum; /* Number of connections times number
48                                            of seconds per hour. */
49 static struct current_load_st cspm[60]; /* Last 60 minutes */
50 static struct current_load_st csph[72]; /* Last 72 hours */
51
52 static int m_index, h_index;    /* Array indexes */
53
54 /*
55  * update_load
56  *
57  * A new connection was added or removed.
58  */
59 void update_load(void)
60 {
61   static struct tm tm_now;      /* Current time. */
62   static time_t last_sec;       /* Seconds of last time that
63                                    update_load() called. */
64   static time_t last_min;
65   static time_t last;           /* Last time that update_load() was called. */
66   static struct current_load_st last_load;      /* The load last time that
67                                                    update_load() was called. */
68   static int initialized;       /* Boolean, set when initialized. */
69   register int diff_time;       /* Temp. variable used to hold time intervals
70                                    in seconds, minutes or hours. */
71
72   /* Update `current_load' */
73   current_load.client_count = nrof.local_clients;
74   current_load.conn_count = nrof.local_clients + nrof.local_servers;
75
76   /* Nothing needed when still in the same second */
77   if (!(diff_time = now - last))
78   {
79     last_load = current_load;   /* Update last_load to be the load last
80                                    time that update_load() was called. */
81     return;
82   }
83
84   /* If we get here we entered a new second */
85
86   /*
87    * Make sure we keep the accurate time in 'tm_now'
88    */
89   if ((tm_now.tm_sec += diff_time) > 59)
90   {
91     /* This is done once every minute */
92     diff_time = tm_now.tm_sec / 60;
93     tm_now.tm_sec -= 60 * diff_time;
94     if ((tm_now.tm_min += diff_time) > 59)
95     {
96       /* This is done once every hour */
97       diff_time = tm_now.tm_min / 60;
98       tm_now.tm_min -= 60 * diff_time;
99       if ((tm_now.tm_hour += diff_time) > 23)
100       {
101         tm_now = *localtime(&now);      /* Only called once a day */
102         if (!initialized)
103         {
104           initialized = 1;
105           last_sec = 60;
106           last_min = tm_now.tm_min;
107         }
108       }
109     }
110
111     /* If we get here we entered a new minute */
112
113     /* Finish the calculation of cspm of the last minute first: */
114     diff_time = 60 - last_sec;
115     cspm_sum.conn_count += last_load.conn_count * diff_time;
116     cspm_sum.client_count += last_load.client_count * diff_time;
117     cspm_sum.local_count += last_load.local_count * diff_time;
118
119     /* Add the completed minute to the Connections*Seconds/Hour sum */
120     csph_sum.conn_count += cspm_sum.conn_count - cspm[m_index].conn_count;
121     csph_sum.client_count += cspm_sum.client_count - cspm[m_index].client_count;
122     csph_sum.local_count += cspm_sum.local_count - cspm[m_index].local_count;
123
124     /* Store the completed minute in an array */
125     cspm[m_index] = cspm_sum;
126
127     /* How long did last_cspm last ? */
128     diff_time = tm_now.tm_min - last_min;
129     last_min = tm_now.tm_min;
130
131     if (diff_time < 0)
132       diff_time += 60;          /* update_load() must be called at
133                                    _least_ once an hour */
134
135     if (diff_time > 1)          /* Did more then one minute pass ? */
136     {
137       /* Calculate the constant load during those extra minutes */
138       cspm_sum.conn_count = last_load.conn_count * 60;
139       cspm_sum.client_count = last_load.client_count * 60;
140       cspm_sum.local_count = last_load.local_count * 60;
141     }
142
143     for (;;)
144     {
145       /* Increase minute index */
146       if (++m_index == 60)
147       {
148         m_index = 0;
149         /* Keep a list of the last 72 hours */
150         csph[h_index] = csph_sum;
151         if (++h_index == 72)
152           h_index = 0;
153       }
154
155       if (--diff_time <= 0)     /* '<' to prevent endless loop if update_load()
156                                    was not called once an hour :/ */
157         break;
158
159       /* Add extra minutes to the Connections*Seconds/Hour sum */
160       csph_sum.conn_count += cspm_sum.conn_count - cspm[m_index].conn_count;
161       csph_sum.client_count +=
162           cspm_sum.client_count - cspm[m_index].client_count;
163       csph_sum.local_count += cspm_sum.local_count - cspm[m_index].local_count;
164
165       /* Store extra minutes in the array */
166       cspm[m_index] = cspm_sum;
167     }
168
169     /* Now start the calculation of the new minute: */
170     last_sec = tm_now.tm_sec;
171     cspm_sum.conn_count = last_load.conn_count * last_sec;
172     cspm_sum.client_count = last_load.client_count * last_sec;
173     cspm_sum.local_count = last_load.local_count * last_sec;
174   }
175   else
176   {
177     /* A new second, but the same minute as last time */
178     /* How long did last_load last ? */
179     diff_time = tm_now.tm_sec - last_sec;
180     last_sec = tm_now.tm_sec;
181     if (diff_time == 1)         /* Just one second ? */
182     {
183       cspm_sum.conn_count += last_load.conn_count;
184       cspm_sum.client_count += last_load.client_count;
185       cspm_sum.local_count += last_load.local_count;
186     }
187     else
188     {
189       /* More then one second */
190       /* At most 3 integer multiplication per second */
191       cspm_sum.conn_count += last_load.conn_count * diff_time;
192       cspm_sum.client_count += last_load.client_count * diff_time;
193       cspm_sum.local_count += last_load.local_count * diff_time;
194     }
195   }
196   last_load = current_load;     /* Update last_load to be the load last
197                                    time that update_load() was called. */
198   last = now;
199 }
200
201 void calc_load(aClient *sptr)
202 {
203   /* *INDENT-OFF* */
204   static const char *header =
205   /*   ----.-  ----.-  ----  ----  ----   ------------ */
206       "Minute  Hour    Day   Yest. YYest. Userload for:";
207   /* *INDENT-ON* */
208   static const char *what[3] = {
209     "local clients",
210     "total clients",
211     "total connections"
212   };
213   int i, j, times[5][3];        /* [min,hour,day,Yest,YYest]
214                                    [local,client,conn] */
215   int last_m_index = m_index, last_h_index = h_index;
216
217   update_load();                /* We want stats accurate as of *now* */
218
219   if (--last_m_index < 0)
220     last_m_index = 59;
221   times[0][0] = (cspm[last_m_index].local_count + 3) / 6;
222   times[0][1] = (cspm[last_m_index].client_count + 3) / 6;
223   times[0][2] = (cspm[last_m_index].conn_count + 3) / 6;
224
225   times[1][0] = (csph_sum.local_count + 180) / 360;
226   times[1][1] = (csph_sum.client_count + 180) / 360;
227   times[1][2] = (csph_sum.conn_count + 180) / 360;
228
229   for (i = 2; i < 5; ++i)
230   {
231     times[i][0] = 43200;
232     times[i][1] = 43200;
233     times[i][2] = 43200;
234     for (j = 0; j < 24; ++j)
235     {
236       if (--last_h_index < 0)
237         last_h_index = 71;
238       times[i][0] += csph[last_h_index].local_count;
239       times[i][1] += csph[last_h_index].client_count;
240       times[i][2] += csph[last_h_index].conn_count;
241     }
242     times[i][0] /= 86400;
243     times[i][1] /= 86400;
244     times[i][2] /= 86400;
245   }
246
247   if (MyUser(sptr) || Protocol(sptr->from) < 10)
248   {
249     sendto_one(sptr, ":%s NOTICE %s :%s", me.name, sptr->name, header);
250     for (i = 0; i < 3; ++i)
251       sendto_one(sptr,
252           ":%s NOTICE %s :%4d.%1d  %4d.%1d  %4d  %4d  %4d   %s",
253           me.name, sptr->name,
254           times[0][i] / 10, times[0][i] % 10,
255           times[1][i] / 10, times[1][i] % 10,
256           times[2][i], times[3][i], times[4][i], what[i]);
257   }
258   else
259   {
260     sendto_one(sptr, "%s NOTICE %s%s :%s", NumServ(&me), NumNick(sptr), header);
261     for (i = 0; i < 3; ++i)
262       sendto_one(sptr,
263           "%s NOTICE %s%s :%4d.%1d  %4d.%1d  %4d  %4d  %4d   %s",
264           NumServ(&me), NumNick(sptr),
265           times[0][i] / 10, times[0][i] % 10,
266           times[1][i] / 10, times[1][i] % 10,
267           times[2][i], times[3][i], times[4][i], what[i]);
268   }
269 }
270
271 void initload(void)
272 {
273   memset(&current_load, 0, sizeof(current_load));
274   update_load();                /* Initialize the load list */
275 }