0a53719b1a9469ee43d61831b98d862375b3d69c
[privoxy.git] / src / stats.c
1 const char stats_rcs[] = "$Id: stats.c,v 2.3 2002/12/30 19:56:16 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  * Revisions   :
35  *    $Log: stats.c,v $
36  *
37  *********************************************************************/
38 \f
39
40 #include <string.h>
41 #ifdef unix
42 #include <sys/signal.h>
43 #include <pthread.h>
44 #endif
45 #include "project.h"
46 #include "errlog.h"
47 #include "miscutil.h"
48 #include "stats.h"
49 #include "ipc.h"
50 #include "jbsockets.h"
51
52 const char stats_h_rcs[] = STATS_H_VERSION;
53 const char ipc_h_rcs[] = IPC_H_VERSION;
54 static IPC_MUTEX_LOCK stats_lock;
55
56 stats_struct *main_stats;
57
58 /*********************************************************************
59  *
60  * Function    :  init_stats_config
61  *
62  * Description :  Initializes the statistics array and spawns a thread
63  *                to transmit statistics to the listening party.
64  *
65  * Parameters  :
66  *          1  :  config = Privoxy configuration.
67  *
68  * Returns     :  N/A
69  *
70  *********************************************************************/
71 void init_stats_config(struct configuration_spec * config)
72 {
73   int i, child_id;
74 #ifdef unix
75   pthread_attr_t attr;
76   pthread_t thread;
77 #endif /* def unix */
78
79   main_stats = zalloc(sizeof(stats_struct));
80   IPC_CREATE_MUTEX(stats_lock);
81   for (i=0; i < STATS_MAX_KEYS; i++)
82   {
83     main_stats->stats_array[i] = 0;
84   }
85   main_stats->config = config;
86
87   accumulate_stats(STATS_PRIVOXY_PORT, config->hport);
88
89   /*
90    * Start the timing/sending thread - I stole this from jcc.c. 
91    * The idea is to get a mutiplatform thread started.
92    * YMMV - please tweak for your platform!
93    */
94
95 /* this is a switch () statment in the C preprocessor - ugh */
96 #undef SELECTED_ONE_OPTION
97
98 /* Use pthreads in preference to any native code */
99 #if defined(FEATURE_PTHREAD) && !defined(SELECTED_ONE_OPTION)
100 #define SELECTED_ONE_OPTION
101   signal(SIGALRM, null_routine);  /* Ignore the SIGALRM signal */
102   pthread_attr_init(&attr);
103   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
104   child_id = (pthread_create(&thread, &attr,
105     (void*)forward_stats, main_stats) ? -1 : 0);
106   pthread_attr_destroy(&attr);
107 #endif
108
109 #if defined(_WIN32) && !defined(_CYGWIN) && !defined(SELECTED_ONE_OPTION)
110 #define SELECTED_ONE_OPTION
111   child_id = _beginthread(
112     (void (*)(void *))forward_stats,
113     64 * 1024,
114     main_stats);
115 #endif
116
117 #if defined(__OS2__) && !defined(SELECTED_ONE_OPTION)
118 #define SELECTED_ONE_OPTION
119   child_id = _beginthread(
120     (void(* _Optlink)(void*))forward_stats,
121     NULL,
122     64 * 1024,
123     main_stats);
124 #endif
125
126 #if defined(__BEOS__) && !defined(SELECTED_ONE_OPTION)
127 #define SELECTED_ONE_OPTION
128   thread_id tid = spawn_thread
129     (server_thread, "forward_stats", B_NORMAL_PRIORITY, NULL);
130   if ((tid >= 0) && (resume_thread(tid) == B_OK))
131   {
132     child_id = (int) tid;
133   }
134   else
135   {
136     child_id = -1;
137   }
138 #endif
139
140 #if defined(AMIGA) && !defined(SELECTED_ONE_OPTION)
141 #define SELECTED_ONE_OPTION
142   if((child_id = (int)CreateNewProcTags(
143      NP_Entry, (ULONG)server_thread,
144      NP_Output, Output(),
145      NP_CloseOutput, FALSE,
146      NP_Name, (ULONG)"privoxy child",
147      NP_StackSize, 200*1024,
148      TAG_DONE)))
149   {
150      childs++;
151      Signal((struct Task *)child_id, SIGF_SINGLE);
152      Wait(SIGF_SINGLE);
153   }
154 #endif
155
156 #if !defined(SELECTED_ONE_OPTION)
157   /* I don't think the IPC will really work in a fork()'d environment,
158    * so proceed with caution.  FIXME.
159    */
160 #error FIXME - stats won't work without pthreads!
161   child_id = fork();
162
163   if (child_id == 0)   /* child */
164   {
165      forward_stats(main_stats);
166      _exit(0);
167   }
168   else if (child_id > 0) /* parent */
169   {
170   }
171 #endif
172
173 #undef SELECTED_ONE_OPTION
174 /* end of c preprocessor switch () */
175
176 }
177
178 /*********************************************************************
179  *
180  * Function    :  update_stats_config
181  *
182  * Description :  Updates the pointer to the most recent Privoxy
183  *                configuration changes.
184  *
185  * Parameters  :
186  *          1  :  config = Privoxy configuration.
187  *
188  * Returns     :  N/A
189  *
190  *********************************************************************/
191 void update_stats_config(struct configuration_spec * config)
192 {
193   main_stats->config = config;
194 }
195
196 /*********************************************************************
197  *
198  * Function    :  accumulate_stats
199  *
200  * Description :  Updates one element of the statistics array with a
201  *                single integer value.
202  *
203  * Parameters  :
204  *          1  :  key = the key into the stats array
205  *          2  :  value = the value to add to the current stats key
206  *
207  * Returns     :  N/A
208  *
209  *********************************************************************/
210 void accumulate_stats(int key, int value)
211 {
212   if (key < STATS_MAX_KEYS)
213   {
214     IPC_LOCK_MUTEX(stats_lock);
215     main_stats->stats_array[key] += value;
216     main_stats->changed = 1;
217     IPC_UNLOCK_MUTEX(stats_lock);
218   }
219 }
220
221 /*********************************************************************
222  *
223  * Function    :  forward_stats
224  *
225  * Description :  Main routine for the statistics thread; loops and 
226  *                periodically checks if there's anything to send.  If
227  *                so, call send_stats() to do the work.
228  *
229  * Parameters  :  N/A
230  *
231  * Returns     :  N/A
232  *
233  *********************************************************************/
234 void *forward_stats(stats_struct *pstats)
235 {
236   int local_stats_array[STATS_MAX_KEYS];
237  
238   for (;;)
239   {
240     IPC_SLEEP_SECONDS(pstats->config->activity_freq);
241     if (pstats->changed == 1)
242     {
243       IPC_LOCK_MUTEX(stats_lock);
244       memcpy(local_stats_array,pstats->stats_array,sizeof(pstats->stats_array));
245       pstats->changed = 0;
246       IPC_UNLOCK_MUTEX(stats_lock);
247       send_stats(local_stats_array);
248     }
249   }
250 }
251
252 /*********************************************************************
253  *
254  * Function    :  send_stats
255  *
256  * Description :  Attempt to send statistics to the listening console.
257  *                Stats are formatted as a clear-text string for now -
258  *                no need for any encoding fanciness just yet.
259  *
260  * Parameters  :
261  *          1  :  local_stats_array, a pointer to a local copy of the
262  *                statistics array.
263  *
264  * Returns     :  N/A
265  *
266  *********************************************************************/
267 void send_stats(int local_stats_array[])
268 {
269   jb_socket sk;
270   char *msg = NULL, tmp_msg[64];
271   int i;
272
273   /* Here, we initiate the socket send to the console */
274   sk = connect_to(main_stats->config->activity_address,main_stats->config->activity_port,NULL);
275   if (sk > 0)
276   {
277     /* max size of a key looks like this: xxxxx:xxxxxb */
278     msg = zalloc(
279       STATS_MAX_KEYS * 64  /* Space for keys - much bigger than necessary for safety */
280       );
281     if (msg)
282     {
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 */