2 * rlm_detail.c accounting: Write the "detail" files.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Copyright 2000 The FreeRADIUS server project
23 static const char rcsid[] = "$Id$";
28 #include <sys/select.h>
37 #include "rad_assert.h"
41 static const char *packet_codes[] = {
47 "Accounting-Response",
57 struct detail_instance {
61 /* detail file permissions */
64 /* directory permissions */
67 /* last made directory */
68 char *last_made_directory;
70 /* timestamp & stuff */
73 /* if we want file locking */
77 static const CONF_PARSER module_config[] = {
78 { "detailfile", PW_TYPE_STRING_PTR,
79 offsetof(struct detail_instance,detailfile), NULL, "%A/%{Client-IP-Address}/detail" },
80 { "header", PW_TYPE_STRING_PTR,
81 offsetof(struct detail_instance,header), NULL, "%t" },
82 { "detailperm", PW_TYPE_INTEGER,
83 offsetof(struct detail_instance,detailperm), NULL, "0600" },
84 { "dirperm", PW_TYPE_INTEGER,
85 offsetof(struct detail_instance,dirperm), NULL, "0755" },
86 { "locking", PW_TYPE_BOOLEAN,
87 offsetof(struct detail_instance,locking), NULL, "no" },
88 { NULL, -1, 0, NULL, NULL }
92 * (Re-)read radiusd.conf into memory.
94 static int detail_instantiate(CONF_SECTION *conf, void **instance)
96 struct detail_instance *inst;
98 inst = rad_malloc(sizeof(*inst));
102 memset(inst, 0, sizeof(*inst));
104 if (cf_section_parse(conf, inst, module_config) < 0) {
109 inst->last_made_directory = NULL;
116 * Do detail, compatible with old accounting
118 static int do_detail(void *instance, REQUEST *request, RADIUS_PACKET *packet,
131 char proxy_buffer[16];
132 VALUE_PAIR *pair = packet->vps;
134 struct detail_instance *inst = instance;
137 * Nothing to log: don't do anything.
140 return RLM_MODULE_NOOP;
144 * Create a directory for this nas.
146 * Generate the path for the detail file. Use the
147 * same format, but truncate at the last /. Then
148 * feed it through radius_xlat() to expand the
151 radius_xlat(buffer, sizeof(buffer), inst->detailfile, request, NULL);
152 DEBUG2("rlm_detail: %s expands to %s", inst->detailfile, buffer);
155 * Grab the last directory delimiter.
157 p = strrchr(buffer,'/');
160 * There WAS a directory delimiter there, and
161 * the file doesn't exist, so
162 * we prolly must create it the dir(s)
164 if ((p) && (stat(buffer, &st) < 0)) {
167 * NO previously cached directory name, so we've
168 * got to create a new one.
170 * OR the new directory name is different than the old,
171 * so we've got to create a new one.
173 * OR the cached directory has somehow gotten removed,
174 * so we've got to create a new one.
176 if ((inst->last_made_directory == NULL) ||
177 (strcmp(inst->last_made_directory, buffer) != 0)) {
180 * Free any previously cached name.
182 if (inst->last_made_directory != NULL) {
183 free((char *) inst->last_made_directory);
184 inst->last_made_directory = NULL;
187 inst->last_made_directory = strdup(buffer);
191 * stat the directory, and don't do anything if
192 * it exists. If it doesn't exist, create it.
194 * This also catches the case where some idiot
195 * deleted a directory that the server was using.
197 if (rad_mkdir(inst->last_made_directory, inst->dirperm) < 0) {
198 radlog(L_ERR, "rlm_detail: Failed to create directory %s: %s", inst->last_made_directory, strerror(errno));
199 return RLM_MODULE_FAIL;
203 } /* else there was no directory delimiter. */
209 * Open & create the file, with the given
212 if ((outfd = open(buffer, O_WRONLY | O_APPEND | O_CREAT,
213 inst->detailperm)) < 0) {
214 radlog(L_ERR, "rlm_detail: Couldn't open file %s: %s",
215 buffer, strerror(errno));
216 return RLM_MODULE_FAIL;
220 * If we fail to aquire the filelock in 80 tries
221 * (approximately two seconds) we bail out.
224 lseek(outfd, 0L, SEEK_SET);
225 if (rad_lockfd_nonblock(outfd, 0) < 0) {
229 select(0, NULL, NULL, NULL, &tv);
235 * The file might have been deleted by
236 * radrelay while we tried to acquire
237 * the lock (race condition)
239 if (fstat(outfd, &st) != 0) {
240 radlog(L_ERR, "rlm_detail: Couldn't stat file %s: %s",
241 buffer, strerror(errno));
243 return RLM_MODULE_FAIL;
245 if (st.st_nlink == 0) {
246 DEBUG("rlm_detail: File %s removed by another program, retrying",
253 DEBUG("rlm_detail: Acquired filelock, tried %d time(s)",
257 } while (inst->locking && !locked && lock_count < 80);
259 if (inst->locking && !locked) {
261 radlog(L_ERR, "rlm_detail: Failed to aquire filelock for %s, giving up",
263 return RLM_MODULE_FAIL;
267 * Convert the FD to FP. The FD is no longer valid
268 * after this operation.
270 if ((outfp = fdopen(outfd, "a")) == NULL) {
271 radlog(L_ERR, "rlm_detail: Couldn't open file %s: %s",
272 buffer, strerror(errno));
274 lseek(outfd, 0L, SEEK_SET);
275 rad_unlockfd(outfd, 0);
276 DEBUG("rlm_detail: Released filelock");
278 close(outfd); /* automatically releases the lock */
280 return RLM_MODULE_FAIL;
284 * Write the information to the file.
288 * Print out names, if they're OK.
291 if ((packet->code > 0) &&
292 (packet->code <= PW_ACCESS_CHALLENGE)) {
293 fprintf(outfp, "Packet-Type = %s\n",
294 packet_codes[packet->code]);
296 fprintf(outfp, "Packet-Type = %d\n", packet->code);
303 fseek(outfp, 0L, SEEK_END);
304 radius_xlat(timestamp, sizeof(timestamp), inst->header, request, NULL);
305 fprintf(outfp, "%s\n", timestamp);
307 /* Write each attribute/value to the log file */
310 * Don't print passwords in old format...
312 if (compat && (pair->attribute == PW_PASSWORD)) {
318 * Print all of the attributes.
321 vp_print(outfp, pair);
327 * Add non-protocol attibutes.
330 if ((pair = pairfind(request->config_items,
331 PW_PROXY_TO_REALM)) != NULL) {
332 proxy_realm = realm_find(pair->strvalue, TRUE);
334 memset((char *) proxy_buffer, 0, 16);
336 rad_assert(proxy_realm->acct_ipaddr.af == AF_INET);
338 inet_ntop(proxy_realm->acct_ipaddr.af,
339 &proxy_realm->acct_ipaddr.ipaddr,
340 proxy_buffer, sizeof(proxy_buffer));
341 fprintf(outfp, "\tFreeradius-Proxied-To = %s\n",
343 DEBUG("rlm_detail: Freeradius-Proxied-To set to %s",
347 fprintf(outfp, "\tTimestamp = %ld\n",
348 (unsigned long) request->timestamp);
350 if (request->packet->verified == 2)
351 fputs("\tRequest-Authenticator = Verified\n", outfp);
352 else if (request->packet->verified == 1)
353 fputs("\tRequest-Authenticator = None\n", outfp);
360 lseek(outfd, 0L, SEEK_SET);
361 rad_unlockfd(outfd, 0);
362 DEBUG("rlm_detail: Released filelock");
368 * And everything is fine.
370 return RLM_MODULE_OK;
374 * Accounting - write the detail files.
376 static int detail_accounting(void *instance, REQUEST *request)
379 return do_detail(instance,request,request->packet, TRUE);
383 * Incoming Access Request - write the detail files.
385 static int detail_authorize(void *instance, REQUEST *request)
387 return do_detail(instance,request,request->packet, FALSE);
391 * Outgoing Access-Request Reply - write the detail files.
393 static int detail_postauth(void *instance, REQUEST *request)
395 return do_detail(instance,request,request->reply, FALSE);
400 * Outgoing Access-Request to home server - write the detail files.
402 static int detail_pre_proxy(void *instance, REQUEST *request)
404 if (request->proxy &&
405 request->proxy->vps) {
406 return do_detail(instance,request,request->proxy, FALSE);
409 return RLM_MODULE_NOOP;
414 * Outgoing Access-Request Reply - write the detail files.
416 static int detail_post_proxy(void *instance, REQUEST *request)
418 if (request->proxy_reply &&
419 request->proxy_reply->vps) {
420 return do_detail(instance,request,request->proxy_reply, FALSE);
423 return RLM_MODULE_NOOP;
430 static int detail_detach(void *instance)
432 struct detail_instance *inst = instance;
433 free((char *) inst->detailfile);
435 if (inst->last_made_directory)
436 free((char*) inst->last_made_directory);
442 /* globally exported name */
443 module_t rlm_detail = {
445 RLM_TYPE_THREAD_UNSAFE, /* type: reserved */
446 NULL, /* initialization */
447 detail_instantiate, /* instantiation */
449 NULL, /* authentication */
450 detail_authorize, /* authorization */
451 NULL, /* preaccounting */
452 detail_accounting, /* accounting */
453 NULL, /* checksimul */
454 detail_pre_proxy, /* pre-proxy */
455 detail_post_proxy, /* post-proxy */
456 detail_postauth /* post-auth */
458 detail_detach, /* detach */