Docs/ModUserOnline: mod_useronline.c

File mod_useronline.c, 8.5 kB (added by jan, 2 years ago)

online tracker module from Wojjie (moved)

Line 
1 #include <ctype.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 #include "base.h"
6 #include "buffer.h"
7
8 #include "plugin.h"
9 #include "inet_ntop_cache.h"
10
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif
14
15 typedef struct {
16         size_t  online_age;
17         size_t  active_age;
18
19         size_t  max_ips;
20         size_t  id;
21         int     enable;
22         buffer **status_keynames;
23 } plugin_config;
24
25 typedef struct {
26         unsigned int ipl;
27         int hits;
28         time_t last;
29 } user_data;
30
31 typedef struct {
32         PLUGIN_DATA;
33         plugin_config **config_storage;
34        
35         plugin_config *conf;
36         user_data **ip_data;
37 } plugin_data;
38
39
40 INIT_FUNC(mod_useronline_init) {
41         plugin_data *p;
42        
43         p = calloc(1, sizeof(*p));
44         return p;
45 }
46
47 #define NUM_STATUS_NAMES 6
48 FREE_FUNC(mod_useronline_free) {
49         plugin_data *p = p_d;
50        
51         UNUSED(srv);
52
53         if (!p) return HANDLER_GO_ON;
54        
55         if (p->config_storage) {
56                 size_t i, j;
57                 for (i = 0; i < srv->config_context->used; i++) {
58                         plugin_config *s = p->config_storage[i];
59                
60                         free(p->ip_data[i]);
61                         if (s->status_keynames) {
62                                 for (j=0; j<NUM_STATUS_NAMES; j++) {
63                                         buffer_free(s->status_keynames[j]);
64                                 }
65                         }
66                         free(s);
67                 }
68                 free(p->config_storage);
69                 free(p->ip_data);
70         }
71        
72         free(p);
73        
74         return HANDLER_GO_ON;
75 }
76
77 void status_counter_set_counter(server *srv, const char *s, int value) {
78         data_integer *di;
79
80         if (NULL == (di = (data_integer *)array_get_element(srv->status, s))) {
81                 /* not found, create it */
82
83                 if (NULL == (di = (data_integer *)array_get_unused_element(srv->status, TYPE_INTEGER))) {
84                         di = data_integer_init();
85                 }
86                 buffer_copy_string(di->key, s);
87                 di->value = value;
88
89                 array_insert_unique(srv->status, (data_unset *)di);
90         }
91         di->value = value;
92 }
93
94 SETDEFAULTS_FUNC(mod_useronline_set_defaults) {
95         plugin_data *p = p_d;
96         size_t i = 0, j=0;
97        
98         config_values_t cv[] = {
99                 { "useronline.online-age",      NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
100                 { "useronline.active-age",      NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
101                 { "useronline.max-ips",         NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
102                 { "useronline.enable",          NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
103                 { "useronline.status-name",     NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
104                
105                 { NULL,                        NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
106         };
107        
108         if (!p) return HANDLER_ERROR;
109        
110         /* 0 */
111         p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
112         p->ip_data = calloc(1, srv->config_context->used * sizeof(user_data *));
113        
114         for (i = 0; i < srv->config_context->used; i++) {
115                 plugin_config *s;
116                 buffer *status_name;
117
118                 s = calloc(1, sizeof(plugin_config));
119                 s->online_age   = 300;
120                 s->active_age   = 0;
121                 s->max_ips      = 1024;
122                 s->id           = i;
123                 s->enable       = 0;
124                 status_name=buffer_init();             
125                
126                 cv[0].destination = &(s->online_age);
127                 cv[1].destination = &(s->active_age);
128                 cv[2].destination = &(s->max_ips);
129                 cv[3].destination = &(s->enable);
130                 cv[4].destination = status_name;
131                
132                 p->config_storage[i] = s;
133        
134                 if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
135                         return HANDLER_ERROR;
136                 }
137
138                 if (s->enable) {
139                         if (s->active_age==0) s->active_age = s->online_age/3;
140                                
141                         p->ip_data[i] = calloc(s->max_ips, sizeof(user_data));
142                         p->ip_data[i][0].hits=-1;
143                 }
144                 /*not used? free the memory*/
145                 if (status_name->used==0 || !s->enable) {
146                         buffer_free(status_name);
147                 } else {
148                         s->status_keynames = calloc(NUM_STATUS_NAMES, sizeof(buffer*));
149                         for (j=0; j<NUM_STATUS_NAMES; j++) {
150                                 s->status_keynames[j]=buffer_init_string("useronline.");
151                                 buffer_append_string    (s->status_keynames[j], status_name->ptr);
152                                 buffer_append_string    (s->status_keynames[j], ".");
153                         }
154                         buffer_free(status_name);
155                        
156                         buffer_append_string    (s->status_keynames[0], "id");
157                         buffer_append_string    (s->status_keynames[1], "users-online");
158                         buffer_append_string    (s->status_keynames[2], "users-active");
159                         buffer_append_string    (s->status_keynames[3], "online-age");
160                         buffer_append_string    (s->status_keynames[4], "active-age");
161                         buffer_append_string    (s->status_keynames[5], "max-ips");
162                         status_counter_set_counter(srv, s->status_keynames[0]->ptr, i);
163                         status_counter_set_counter(srv, s->status_keynames[3]->ptr, s->online_age);
164                         status_counter_set_counter(srv, s->status_keynames[4]->ptr, s->active_age);
165                         status_counter_set_counter(srv, s->status_keynames[5]->ptr, s->max_ips);
166                 }
167         }
168        
169         return HANDLER_GO_ON;
170 }
171 #undef NUM_STATUS_NAMES
172
173 #define PATCH() \
174         p->conf = s;
175 static int mod_useronline_patch_connection(server *srv, connection *con, plugin_data *p) {
176         size_t i;
177         plugin_config *s = p->config_storage[0];
178
179         PATCH();
180        
181         /* skip the first, the global context */
182         for (i = 1; i < srv->config_context->used; i++) {
183                 data_config *dc = (data_config *)srv->config_context->data[i];
184                 s = p->config_storage[i];
185                
186                 /* condition didn't match */
187                 if (!config_check_cond(srv, con, dc)) continue;
188                
189                 PATCH();
190         }
191        
192         return 0;
193 }
194 #undef PATCH
195
196 static void mod_useronline_add_env_int(array  *env, const char *key, int value) {
197         data_string *ds;
198         if (NULL == (ds = (data_string *)array_get_unused_element(env, TYPE_STRING))) {
199                 ds = data_string_init();
200         }
201         buffer_copy_string(ds->key, key);
202         buffer_copy_long(ds->value, value);
203         array_insert_unique(env, (data_unset *)ds);
204 }
205
206 URIHANDLER_FUNC(mod_useronline_uri_handler) {
207         plugin_data *p = p_d;
208         size_t j;
209         plugin_config *s;
210         user_data *ipd;
211         unsigned int ipl;
212         time_t time_threshold;
213         time_t active_threshold;
214        
215         size_t user_count       = 0;
216         size_t user_active      = 0;
217         int free_slot           = -1;
218         size_t last_item        = 0;
219         int foundat             = -1;
220         time_t current_time     = srv->cur_ts;
221
222         mod_useronline_patch_connection(srv, con, p);
223        
224         if (!p->conf->enable) return HANDLER_GO_ON;
225
226         s               = p->conf;
227         ipd             = p->ip_data[s->id];
228         ipl             = inet_addr(inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
229         time_threshold  = current_time - (s->online_age);
230         active_threshold= current_time - (s->active_age);
231                
232         for (j=0; j<s->max_ips; j++) {
233                 if (ipd[j].ipl==0) {
234                         /*empty entry, mark it if first free slot*/
235                         if (free_slot==-1) free_slot=j;
236                         if (ipd[j].hits==-1) {
237                                 /* end found */
238                                 j=s->max_ips;
239                         }
240                 } else if (ipd[j].ipl==ipl) {
241                         /*found a previous entry...*/
242                         foundat=j;
243                         ipd[j].hits++;
244                         ipd[j].last=current_time;
245                 } else if (ipd[j].last < time_threshold) {
246                         /*expired ip, mark it if first free slot*/
247                         ipd[j].ipl=0;
248                         if (free_slot==-1) free_slot=j;
249                 }
250                 if (ipd[j].ipl>0) {
251                         /*count user/ip*/
252                         if (ipd[j].last >= active_threshold) user_active++;
253                         user_count++;
254                         last_item=j;
255                 }
256         }
257                                
258         if (foundat==-1) {
259                 /*not found, add new*/
260                 if (free_slot!=-1) {
261                         ipd[free_slot].ipl=ipl;
262                         ipd[free_slot].last=current_time;
263                         ipd[free_slot].hits=1;
264                         foundat=free_slot;
265                         if (free_slot>(int)last_item) last_item=free_slot;
266                        
267                         user_active++;
268                         user_count++;
269                 }
270         }
271                                
272         /*reduce amount of elements to scan through (mark as end, hits=-1)*/
273         if (last_item+1 < s->max_ips) {
274                 ipd[last_item+1].ipl=0;
275                 ipd[last_item+1].hits=-1;
276         }
277
278         /*set environment variables*/
279
280         /*users online currently*/
281         mod_useronline_add_env_int(con->environment, "users_online", user_count);
282         /*users active currently*/
283         mod_useronline_add_env_int(con->environment, "users_active", user_active);
284         /*hits made by current user*/
285         mod_useronline_add_env_int(con->environment, "user_hits", ipd[foundat].hits);
286         /*max age setting for current*/
287         mod_useronline_add_env_int(con->environment, "users_online_age", s->online_age);
288         /*max active age setting for current*/
289         mod_useronline_add_env_int(con->environment, "users_active_age", s->active_age);
290         /*max ips setting for current*/
291         mod_useronline_add_env_int(con->environment, "users_online_max_ips", s->max_ips);
292         /*current id for current*/
293         mod_useronline_add_env_int(con->environment, "users_online_id", s->id);
294        
295         if (s->status_keynames) {
296                 /*users online currently*/
297                 status_counter_set_counter(srv, s->status_keynames[1]->ptr, user_count);
298                 /*users active currently*/
299                 status_counter_set_counter(srv, s->status_keynames[2]->ptr, user_active);
300         }
301         return HANDLER_GO_ON;
302 }
303
304 int mod_useronline_plugin_init(plugin *p) {
305         p->version     = LIGHTTPD_VERSION_ID;
306         p->name        = buffer_init_string("useronline");
307        
308         p->init        = mod_useronline_init;
309         p->handle_uri_clean = mod_useronline_uri_handler;
310         p->set_defaults   = mod_useronline_set_defaults;
311         p->cleanup        = mod_useronline_free;
312        
313         p->data        = NULL;
314        
315         return 0;
316 }