Project

General

Profile

Feature #840 ยป mod_useronline.c

Module: online user tracking -- Wojciech Hlibowicki <wojtek - Anonymous, 2006-09-07 19:46

 
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	max_age;
17
	size_t	active;
18

    
19
	size_t 	maxips;
20
	size_t 	id;
21
	int 	enable;
22
} plugin_config;
23

    
24
typedef struct {
25
	unsigned int ipl;
26
	int hits;
27
	time_t last;
28
} user_data;
29

    
30
typedef struct {
31
	PLUGIN_DATA;
32
	plugin_config **config_storage;
33
	
34
	plugin_config *conf;
35
	user_data **ip_data;
36
} plugin_data;
37

    
38

    
39
INIT_FUNC(mod_useronline_init) {
40
	plugin_data *p;
41
	
42
	p = calloc(1, sizeof(*p));
43
	return p;
44
}
45

    
46
FREE_FUNC(mod_useronline_free) {
47
	plugin_data *p = p_d;
48
	
49
	UNUSED(srv);
50

    
51
	if (!p) return HANDLER_GO_ON;
52
	
53
	if (p->config_storage) {
54
		size_t i;
55
		for (i = 0; i < srv->config_context->used; i++) {
56
			plugin_config *s = p->config_storage[i];
57
		
58
			free(p->ip_data[i]);
59
			free(s);
60
		}
61
		free(p->config_storage);
62
		free(p->ip_data);
63
	}
64
	
65
	free(p);
66
	
67
	return HANDLER_GO_ON;
68
}
69

    
70
SETDEFAULTS_FUNC(mod_useronline_set_defaults) {
71
	plugin_data *p = p_d;
72
	size_t i = 0;
73
	
74
	config_values_t cv[] = { 
75
		{ "useronline.maxage",  	NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
76
		{ "useronline.active",  	NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
77
		{ "useronline.maxips",  	NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
78
		{ "useronline.enable",  	NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
79
		
80
		{ NULL,                        NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
81
	};
82
	
83
	if (!p) return HANDLER_ERROR;
84
	
85
	/* 0 */
86
	p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
87
	p->ip_data = calloc(1, srv->config_context->used * sizeof(user_data *));
88
	
89
	for (i = 0; i < srv->config_context->used; i++) {
90
		plugin_config *s;
91
		
92
		s = calloc(1, sizeof(plugin_config));
93
		s->max_age	= 300;
94
		s->active	= 0;
95
		s->maxips	= 1024;
96
		s->id		= i;
97
		s->enable	= 0;
98
		
99
		cv[0].destination = &(s->max_age);
100
		cv[1].destination = &(s->active);
101
		cv[2].destination = &(s->maxips);
102
		cv[3].destination = &(s->enable);
103
		
104
		p->config_storage[i] = s;
105
	
106
		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
107
			return HANDLER_ERROR;
108
		}
109

    
110
		if (s->enable) {
111
			if (s->active==0) s->active = s->max_age/3;
112
				
113
			p->ip_data[i] = calloc(s->maxips, sizeof(user_data));
114
			p->ip_data[i][0].hits=-1;
115
		}
116
	}
117
	
118
	return HANDLER_GO_ON;
119
}
120

    
121
#define PATCH() \
122
	p->conf = s;
123
static int mod_useronline_patch_connection(server *srv, connection *con, plugin_data *p) {
124
	size_t i;
125
	plugin_config *s = p->config_storage[0];
126

    
127
	PATCH();
128
	
129
	/* skip the first, the global context */
130
	for (i = 1; i < srv->config_context->used; i++) {
131
		data_config *dc = (data_config *)srv->config_context->data[i];
132
		s = p->config_storage[i];
133
		
134
		/* condition didn't match */
135
		if (!config_check_cond(srv, con, dc)) continue;
136
		
137
		PATCH();
138
	}
139
	
140
	return 0;
141
}
142
#undef PATCH
143

    
144
static void mod_useronline_add_env_int(array  *env, const char *key, int value) {
145
	data_string *ds;
146
	if (NULL == (ds = (data_string *)array_get_unused_element(env, TYPE_STRING))) {
147
		ds = data_string_init();
148
	}
149
	buffer_copy_string(ds->key, key);
150
	buffer_append_long(ds->value, value);
151
	array_insert_unique(env, (data_unset *)ds);
152
}
153

    
154
URIHANDLER_FUNC(mod_useronline_uri_handler) {
155
	plugin_data *p = p_d;
156
	size_t j;
157
	plugin_config *s;
158
	user_data *ipd;
159
	unsigned int ipl;
160
	time_t time_threshold;
161
	time_t active_threshold;
162
	
163
	size_t user_count	= 0;
164
	size_t user_active	= 0;
165
	int free_slot		= -1;
166
	size_t last_item	= 0;
167
	int foundat		= -1;
168
	time_t current_time	= srv->cur_ts;
169

    
170
	mod_useronline_patch_connection(srv, con, p);
171
	
172
	if (!p->conf->enable) return HANDLER_GO_ON;
173

    
174
	s 		= p->conf;
175
	ipd		= p->ip_data[s->id];
176
	ipl		= inet_addr(inet_ntop_cache_get_ip(srv, &(con->dst_addr)));
177
	time_threshold	= current_time - (s->max_age);
178
	active_threshold= current_time - (s->active);
179
		
180
	for (j=0; j<s->maxips; j++) {
181
		if (ipd[j].ipl==0) {
182
			/*empty entry, mark it if first free slot*/
183
			if (free_slot==-1) free_slot=j;
184
			if (ipd[j].hits==-1) {
185
				/* end found */
186
				j=s->maxips;
187
			} 
188
		} else if (ipd[j].ipl==ipl) {
189
			/*found a previous entry...*/
190
			foundat=j;
191
			ipd[j].hits++;
192
			ipd[j].last=current_time;
193
		} else if (ipd[j].last < time_threshold) {
194
			/*expired ip, mark it if first free slot*/
195
			ipd[j].ipl=0;
196
			if (free_slot==-1) free_slot=j;
197
		} 
198
		if (ipd[j].ipl>0) {
199
			/*count user/ip*/
200
			if (ipd[j].last >= active_threshold) user_active++;
201
			user_count++;
202
			last_item=j;
203
		}
204
	}
205
				
206
	if (foundat==-1) {
207
		/*not found, add new*/
208
		if (free_slot!=-1) {
209
			ipd[free_slot].ipl=ipl;
210
			ipd[free_slot].last=current_time;
211
			ipd[free_slot].hits=1;
212
			foundat=free_slot;
213
			if (free_slot>(int)last_item) last_item=free_slot;
214
			
215
			user_active++;
216
			user_count++;
217
		}
218
	}
219
				
220
	/*reduce amount of elements to scan through (mark as end, hits=-1)*/
221
	if (last_item+1 < s->maxips) {
222
		ipd[last_item+1].ipl=0;
223
		ipd[last_item+1].hits=-1;
224
	}
225

    
226
	/*set environment variables*/
227

    
228
	/*users online currently*/
229
	mod_useronline_add_env_int(con->environment, "users_online", user_count);
230
	/*users active currently*/
231
	mod_useronline_add_env_int(con->environment, "users_active", user_active);
232
	/*hits made by current user*/
233
	mod_useronline_add_env_int(con->environment, "user_hits", ipd[foundat].hits);
234
	/*max age setting for current*/
235
	mod_useronline_add_env_int(con->environment, "users_online_age", s->max_age);
236
	/*max active age setting for current*/
237
	mod_useronline_add_env_int(con->environment, "users_active_age", s->active);
238
	/*max ips setting for current*/
239
	mod_useronline_add_env_int(con->environment, "users_online_max_ips", s->maxips);
240
	/*current id for current*/
241
	mod_useronline_add_env_int(con->environment, "users_online_id", s->id);
242
			
243
	return HANDLER_GO_ON;
244
}
245

    
246
int mod_useronline_plugin_init(plugin *p) {
247
	p->version     = LIGHTTPD_VERSION_ID;
248
	p->name        = buffer_init_string("useronline");
249
	
250
	p->init        = mod_useronline_init;
251
	p->handle_uri_clean = mod_useronline_uri_handler;
252
	p->set_defaults   = mod_useronline_set_defaults;
253
	p->cleanup        = mod_useronline_free;
254
	
255
	p->data        = NULL;
256
	
257
	return 0;
258
}
    (1-1/1)