End of initial drop of statistics console infrastructure. Data stream
[privoxy.git] / src / stats.c
1 const char stats_rcs[] = "$Id: stats.c,v 2.2 2002/12/28 04:17:58 david__schmidt Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/src/stats.c,v $
5  *
6  * Purpose     :  Functions and definitions for accumulating and
7  *                sending statistics to an "external" stats console
8  *
9  * Copyright   :  Written by and Copyright (C) 2002, 2003 the SourceForge
10  *                Privoxy team. http://www.privoxy.org/
11  *
12  *                Based on the Internet Junkbuster originally written
13  *                by and Copyright (C) 1997 Anonymous Coders and
14  *                Junkbusters Corporation.  http://www.junkbusters.com
15  *
16  *                This program is free software; you can redistribute it
17  *                and/or modify it under the terms of the GNU General
18  *                Public License as published by the Free Software
19  *                Foundation; either version 2 of the License, or (at
20  *                your option) any later version.
21  *
22  *                This program is distributed in the hope that it will
23  *                be useful, but WITHOUT ANY WARRANTY; without even the
24  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
25  *                PARTICULAR PURPOSE.  See the GNU General Public
26  *                License for more details.
27  *
28  *                The GNU General Public License should be included with
29  *                this file.  If not, you can view it at
30  *                http://www.gnu.org/copyleft/gpl.html
31  *                or write to the Free Software Foundation, Inc., 59
32  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
33  *
34  *********************************************************************/
35 \f
36
37 #include <string.h>
38 #ifdef unix
39 #include <sys/signal.h>
40 #endif
41 #include "project.h"
42 #include "errlog.h"
43 #include "miscutil.h"
44 #include "stats.h"
45 #include "ipc.h"
46 #include "jbsockets.h"
47
48 const char stats_h_rcs[] = STATS_H_VERSION;
49 const char ipc_h_rcs[] = IPC_H_VERSION;
50 static IPC_MUTEX_LOCK stats_lock;
51
52 struct configuration_spec *latest_config;
53 int changed = 0,
54     stats_array[STATS_MAX_KEYS];
55
56 /*********************************************************************
57  *
58  * Function    :  init_stats_config
59  *
60  * Description :  Initializes the statistics array and spawns a thread
61  *                to transmit statistics to the listening party.
62  *
63  * Parameters  :
64  *          1  :  config = Privoxy configuration.
65  *
66  * Returns     :  N/A
67  *
68  *********************************************************************/
69 void init_stats_config(struct configuration_spec * config)
70 {
71   int rc, i, child_id;
72 #if defined(FEATURE_PTHREAD)
73   pthread_attr_t attr;
74   pthread_t thread;
75 #endif /* def FEATURE_PTHREAD */
76
77   IPC_CREATE_MUTEX(stats_lock);
78   for (i=0; i < STATS_MAX_KEYS; i++)
79   {
80     stats_array[i] = 0;
81   }
82   latest_config = config;
83
84   /*
85    * Start the timing/sending thread - I stole this from jcc.c. 
86    * The idea is to get a mutiplatform thread started.
87    * YMMV - please tweak for your platform!
88    */
89
90 /* this is a switch () statment in the C preprocessor - ugh */
91 #undef SELECTED_ONE_OPTION
92
93 /* Use pthreads in preference to any native code */
94 #if defined(FEATURE_PTHREAD) && !defined(SELECTED_ONE_OPTION)
95 #define SELECTED_ONE_OPTION
96   pthread_t the_thread;
97   pthread_attr_t attrs;
98
99   signal(SIGALRM, null_routine);  /* Ignore the SIGALRM signal */
100   pthread_attr_init(&attrs);
101   pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
102   child_id = (pthread_create(&the_thread, &attrs,
103     (void*)forward_stats, NULL) ? -1 : 0);
104   pthread_attr_destroy(&attrs);
105 #endif
106
107 #if defined(_WIN32) && !defined(_CYGWIN) && !defined(SELECTED_ONE_OPTION)
108 #define SELECTED_ONE_OPTION
109   child_id = _beginthread(
110     (void (*)(void *))forward_stats,
111     64 * 1024,
112     NULL);
113 #endif
114
115 #if defined(__OS2__) && !defined(SELECTED_ONE_OPTION)
116 #define SELECTED_ONE_OPTION
117   child_id = _beginthread(
118     (void(* _Optlink)(void*))forward_stats,
119     NULL,
120     64 * 1024,
121     NULL);
122 #endif
123
124 #if defined(__BEOS__) && !defined(SELECTED_ONE_OPTION)
125 #define SELECTED_ONE_OPTION
126   thread_id tid = spawn_thread
127     (server_thread, "forward_stats", B_NORMAL_PRIORITY, NULL);
128
129   if ((tid >= 0) && (resume_thread(tid) == B_OK))
130   {
131     child_id = (int) tid;
132   }
133   else
134   {
135     child_id = -1;
136   }
137 #endif
138
139 #if defined(AMIGA) && !defined(SELECTED_ONE_OPTION)
140 #define SELECTED_ONE_OPTION
141   if((child_id = (int)CreateNewProcTags(
142      NP_Entry, (ULONG)server_thread,
143      NP_Output, Output(),
144      NP_CloseOutput, FALSE,
145      NP_Name, (ULONG)"privoxy child",
146      NP_StackSize, 200*1024,
147      TAG_DONE)))
148   {
149      childs++;
150      Signal((struct Task *)child_id, SIGF_SINGLE);
151      Wait(SIGF_SINGLE);
152   }
153 #endif
154
155 #if !defined(SELECTED_ONE_OPTION)
156   /* I don't think the IPC will really work in a fork()'d environment,
157    * so proceed with caution.  FIXME.
158    */
159   child_id = fork();
160
161   if (child_id == 0)   /* child */
162   {
163      forward_stats();
164      _exit(0);
165   }
166   else if (child_id > 0) /* parent */
167   {
168   }
169 #endif
170
171 #undef SELECTED_ONE_OPTION
172 /* end of c preprocessor switch () */
173
174 }
175
176 /*********************************************************************
177  *
178  * Function    :  update_stats_config
179  *
180  * Description :  Updates the pointer to the most recent Privoxy
181  *                configuration changes.
182  *
183  * Parameters  :
184  *          1  :  config = Privoxy configuration.
185  *
186  * Returns     :  N/A
187  *
188  *********************************************************************/
189 void update_stats_config(struct configuration_spec * config)
190 {
191   latest_config = config;
192 }
193
194 /*********************************************************************
195  *
196  * Function    :  accumulate_stats
197  *
198  * Description :  Updates one element of the statistics array with a
199  *                single integer value.
200  *
201  * Parameters  :
202  *          1  :  key = the key into the stats array
203  *          2  :  value = the value to add to the current stats key
204  *
205  * Returns     :  N/A
206  *
207  *********************************************************************/
208 void accumulate_stats(int key, int value)
209 {
210   if (key < STATS_MAX_KEYS)
211   {
212     IPC_LOCK_MUTEX(stats_lock);
213     stats_array[key] += value;
214     changed = 1;
215     IPC_UNLOCK_MUTEX(stats_lock);
216   }
217 }
218
219 /*********************************************************************
220  *
221  * Function    :  forward_stats
222  *
223  * Description :  Main routine for the statistics thread; loops and 
224  *                periodically checks if there's anything to send.  If
225  *                so, call send_stats() to do the work.
226  *
227  * Parameters  :  N/A
228  *
229  * Returns     :  N/A
230  *
231  *********************************************************************/
232 void *forward_stats()
233 {
234   int local_stats_array[STATS_MAX_KEYS];
235   
236   for (;;)
237   {
238     IPC_SLEEP_SECONDS(latest_config->activity_freq);
239     if (changed == 1)
240     {
241       IPC_LOCK_MUTEX(stats_lock);
242       memcpy(local_stats_array,stats_array,sizeof(stats_array));
243       changed = 0;
244       IPC_UNLOCK_MUTEX(stats_lock);
245       send_stats(local_stats_array);
246     }
247   }
248 }
249
250 /*********************************************************************
251  *
252  * Function    :  send_stats
253  *
254  * Description :  Attempt to send statistics to the listening console.
255  *                Stats are formatted as a clear-text string for now -
256  *                no need for any encoding fanciness just yet.
257  *
258  * Parameters  :
259  *          1  :  local_stats_array, a pointer to a local copy of the
260  *                statistics array.
261  *
262  * Returns     :  N/A
263  *
264  *********************************************************************/
265 void send_stats(int local_stats_array[])
266 {
267   jb_socket sk;
268   char *msg = NULL, tmp_msg[64];
269   int i;
270
271   /* Here, we initiate the socket send to the console */
272   sk = connect_to(latest_config->activity_address,latest_config->activity_port,NULL);
273   if (sk > 0)
274   {
275     /* max size of a key looks like this: xxxxx:xxxxxb */
276     msg = zalloc(
277       64 + /* space for socket key ("serving:") and value and stuff */
278       STATS_MAX_KEYS * 64  /* Space for keys - much bigger than necessary for safety */
279       );
280     if (msg)
281     {
282       sprintf(msg,"serving:%d ",latest_config->hport);
283       for (i = 0; i < STATS_MAX_KEYS; i++)
284       {
285         sprintf(tmp_msg,"%d:%d ",i,local_stats_array[i]);
286         strcat(msg,tmp_msg);
287       }
288       write_socket(sk,msg,strlen(msg));
289       freez(msg);
290     }
291     close_socket(sk);
292   }
293 }
294
295 /*********************************************************************
296  *
297  * Function    :  null_routine
298  *
299  * Description :  Called when hit by a signal in unix; do nothing.
300  *
301  * Parameters  :
302  *          1  :  sig - the integer signal
303  *
304  * Returns     :  N/A
305  *
306  *********************************************************************/
307 #ifdef unix
308 void null_routine(int sig)
309 {
310   /* sigignore(sig); */
311 }
312 #endif /* def unix */