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