2 * rlm_detail.c accounting: Write the "detail" files.
6 * This program is is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License, version 2 if the
8 * License as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 * Copyright 2000 The FreeRADIUS server project
22 static const char rcsid[] = "$Id$";
25 #include "libradius.h"
28 #include <sys/select.h>
39 static const char *packet_codes[] = {
45 "Accounting-Response",
55 struct detail_instance {
59 /* detail file permissions */
62 /* directory permissions */
65 /* last made directory */
66 char *last_made_directory;
68 /* if we want file locking */
72 static CONF_PARSER module_config[] = {
73 { "detailfile", PW_TYPE_STRING_PTR,
74 offsetof(struct detail_instance,detailfile), NULL, "%A/%{Client-IP-Address}/detail" },
75 { "detailperm", PW_TYPE_INTEGER,
76 offsetof(struct detail_instance,detailperm), NULL, "0600" },
77 { "dirperm", PW_TYPE_INTEGER,
78 offsetof(struct detail_instance,dirperm), NULL, "0755" },
79 { "locking", PW_TYPE_BOOLEAN,
80 offsetof(struct detail_instance,locking), NULL, "no" },
81 { NULL, -1, 0, NULL, NULL }
85 * (Re-)read radiusd.conf into memory.
87 static int detail_instantiate(CONF_SECTION *conf, void **instance)
89 struct detail_instance *inst;
91 inst = rad_malloc(sizeof(*inst));
95 memset(inst, 0, sizeof(*inst));
97 if (cf_section_parse(conf, inst, module_config) < 0) {
102 inst->last_made_directory = NULL;
109 * Do detail, compatible with old accounting
111 static int do_detail(void *instance, REQUEST *request, RADIUS_PACKET *packet,
123 char proxy_buffer[16];
124 VALUE_PAIR *pair = packet->vps;
126 struct detail_instance *inst = instance;
129 * Nothing to log: don't do anything.
132 return RLM_MODULE_NOOP;
136 * Create a directory for this nas.
138 * Generate the path for the detail file. Use the
139 * same format, but truncate at the last /. Then
140 * feed it through radius_xlat() to expand the
143 radius_xlat(buffer, sizeof(buffer), inst->detailfile, request, NULL);
144 DEBUG2("rlm_detail: %s expands to %s", inst->detailfile, buffer);
147 * Grab the last directory delimiter.
149 p = strrchr(buffer,'/');
152 * There WAS a directory delimiter there, and
153 * the file doesn't exist, so
154 * we prolly must create it the dir(s)
156 if ((p) && (stat(buffer, &st) < 0)) {
159 * NO previously cached directory name, so we've
160 * got to create a new one.
162 * OR the new directory name is different than the old,
163 * so we've got to create a new one.
165 * OR the cached directory has somehow gotten removed,
166 * so we've got to create a new one.
168 if ((inst->last_made_directory == NULL) ||
169 (strcmp(inst->last_made_directory, buffer) != 0)) {
172 * Free any previously cached name.
174 if (inst->last_made_directory != NULL) {
175 free((char *) inst->last_made_directory);
176 inst->last_made_directory = NULL;
180 * Go create possibly multiple directories.
182 if (rad_mkdir(buffer, inst->dirperm) < 0) {
183 radlog(L_ERR, "rlm_detail: Failed to create directory %s: %s", buffer, strerror(errno));
184 return RLM_MODULE_FAIL;
186 inst->last_made_directory = strdup(buffer);
190 } /* else there was no directory delimiter. */
193 * Open & create the file, with the given permissions.
195 if ((outfd = open(buffer, O_WRONLY | O_APPEND | O_CREAT,
196 inst->detailperm)) < 0) {
197 radlog(L_ERR, "rlm_detail: Couldn't open file %s: %s",
198 buffer, strerror(errno));
199 return RLM_MODULE_FAIL;
203 * If we're not using locking, we'll just pass straight though
205 * If we fail to aquire the filelock in 80 tries (approximately
206 * two seconds) we bail out.
212 lseek(outfd, 0L, SEEK_SET);
213 if (rad_lockfd_nonblock(outfd, 0) < 0) {
217 select(0, NULL, NULL, NULL, &tv);
220 DEBUG("rlm_detail: Acquired filelock, tried %d time(s)",
225 } while (!locked && inst->locking && lock_count < 80);
227 if (!locked && inst->locking && lock_count >= 80) {
228 radlog(L_ERR, "rlm_detail: Failed to aquire filelock for %s, giving up",
230 return RLM_MODULE_FAIL;
234 * Convert the FD to FP. The FD is no longer valid
235 * after this operation.
237 if ((outfp = fdopen(outfd, "a")) == NULL) {
238 radlog(L_ERR, "rlm_detail: Couldn't open file %s: %s",
239 buffer, strerror(errno));
241 lseek(outfd, 0L, SEEK_SET);
242 rad_unlockfd(outfd, 0);
243 DEBUG("rlm_detail: Released filelock");
247 return RLM_MODULE_FAIL;
251 * Write the information to the file.
255 * Print out names, if they're OK.
258 if ((packet->code > 0) &&
259 (packet->code <= PW_ACCESS_CHALLENGE)) {
260 fprintf(outfp, "Packet-Type = %s\n",
261 packet_codes[packet->code]);
263 fprintf(outfp, "Packet-Type = %d\n", packet->code);
270 fseek(outfp, 0L, SEEK_END);
271 fputs(ctime_r(&request->timestamp, buffer), outfp);
273 /* Write each attribute/value to the log file */
276 * Don't print passwords in old format...
278 if (compat && (pair->attribute == PW_PASSWORD)) continue;
281 * Print all of the attributes.
284 vp_print(outfp, pair);
290 * Add non-protocol attibutes.
293 if ((pair = pairfind(request->config_items,
294 PW_PROXY_TO_REALM)) != NULL) {
295 proxy_realm = realm_find(pair->strvalue, TRUE);
297 memset((char *) proxy_buffer, 0, 16);
298 ip_ntoa(proxy_buffer, proxy_realm->acct_ipaddr);
299 fprintf(outfp, "\tFreeradius-Proxied-To = %s\n",
301 DEBUG("rlm_detail: Freeradius-Proxied-To set to %s",
305 fprintf(outfp, "\tTimestamp = %ld\n", request->timestamp);
307 if (request->packet->verified == 2)
308 fputs("\tRequest-Authenticator = Verified\n", outfp);
309 else if (request->packet->verified == 1)
310 fputs("\tRequest-Authenticator = None\n", outfp);
317 lseek(outfd, 0L, SEEK_SET);
318 rad_unlockfd(outfd, 0);
319 DEBUG("rlm_detail: Released filelock");
325 * And everything is fine.
327 return RLM_MODULE_OK;
331 * Accounting - write the detail files.
333 static int detail_accounting(void *instance, REQUEST *request)
336 return do_detail(instance,request,request->packet, TRUE);
340 * Incoming Access Request - write the detail files.
342 static int detail_authorize(void *instance, REQUEST *request)
344 return do_detail(instance,request,request->packet, FALSE);
348 * Outgoing Access-Request Reply - write the detail files.
350 static int detail_postauth(void *instance, REQUEST *request)
352 return do_detail(instance,request,request->reply, FALSE);
357 * Outgoing Access-Request to home server - write the detail files.
359 static int detail_pre_proxy(void *instance, REQUEST *request)
361 if (request->proxy &&
362 request->proxy->vps) {
363 return do_detail(instance,request,request->proxy, FALSE);
366 return RLM_MODULE_NOOP;
371 * Outgoing Access-Request Reply - write the detail files.
373 static int detail_post_proxy(void *instance, REQUEST *request)
375 if (request->proxy_reply &&
376 request->proxy_reply->vps) {
377 return do_detail(instance,request,request->proxy_reply, FALSE);
380 return RLM_MODULE_NOOP;
387 static int detail_detach(void *instance)
389 struct detail_instance *inst = instance;
390 free((char *) inst->detailfile);
392 if (inst->last_made_directory)
393 free((char*) inst->last_made_directory);
399 /* globally exported name */
400 module_t rlm_detail = {
402 0, /* type: reserved */
403 NULL, /* initialization */
404 detail_instantiate, /* instantiation */
406 NULL, /* authentication */
407 detail_authorize, /* authorization */
408 NULL, /* preaccounting */
409 detail_accounting, /* accounting */
410 NULL, /* checksimul */
411 detail_pre_proxy, /* pre-proxy */
412 detail_post_proxy, /* post-proxy */
413 detail_postauth /* post-auth */
415 detail_detach, /* detach */