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 struct detail_instance {
43 /* detail file permissions */
46 /* directory permissions */
49 /* last made directory */
50 char *last_made_directory;
52 /* if we want file locking */
56 static CONF_PARSER module_config[] = {
57 { "detailfile", PW_TYPE_STRING_PTR,
58 offsetof(struct detail_instance,detailfile), NULL, "%A/%{Client-IP-Address}/detail" },
59 { "detailperm", PW_TYPE_INTEGER,
60 offsetof(struct detail_instance,detailperm), NULL, "0600" },
61 { "dirperm", PW_TYPE_INTEGER,
62 offsetof(struct detail_instance,dirperm), NULL, "0755" },
63 { "locking", PW_TYPE_BOOLEAN,
64 offsetof(struct detail_instance,locking), NULL, "no" },
65 { NULL, -1, 0, NULL, NULL }
69 * (Re-)read radiusd.conf into memory.
71 static int detail_instantiate(CONF_SECTION *conf, void **instance)
73 struct detail_instance *inst;
75 inst = rad_malloc(sizeof(*inst));
79 memset(inst, 0, sizeof(*inst));
81 if (cf_section_parse(conf, inst, module_config) < 0) {
86 inst->last_made_directory = NULL;
92 static int do_detail(void *instance, REQUEST *request, VALUE_PAIR *pair)
98 int ret = RLM_MODULE_OK;
104 char proxy_buffer[16];
106 struct detail_instance *inst = instance;
109 * Create a directory for this nas.
111 * Generate the path for the detail file. Use the
112 * same format, but truncate at the last /. Then
113 * feed it through radius_xlat() to expand the
116 radius_xlat(buffer, sizeof(buffer), inst->detailfile, request, NULL);
117 DEBUG2("rlm_detail: %s expands to %s", inst->detailfile, buffer);
120 * Grab the last directory delimiter.
122 p = strrchr(buffer,'/');
125 * There WAS a directory delimiter there, and
126 * the file doesn't exist, so
127 * we prolly must create it the dir(s)
129 if ((p) && (stat(buffer, &st) < 0)) {
132 * NO previously cached directory name, so we've
133 * got to create a new one.
135 * OR the new directory name is different than the old,
136 * so we've got to create a new one.
138 * OR the cached directory has somehow gotten removed,
139 * so we've got to create a new one.
141 if ((inst->last_made_directory == NULL) ||
142 (strcmp(inst->last_made_directory, buffer) != 0)) {
145 * Free any previously cached name.
147 if (inst->last_made_directory != NULL) {
148 free((char *) inst->last_made_directory);
149 inst->last_made_directory = NULL;
153 * Go create possibly multiple directories.
155 if (rad_mkdir(buffer, inst->dirperm) < 0) {
156 radlog(L_ERR, "rlm_detail: Failed to create directory %s: %s", buffer, strerror(errno));
157 return RLM_MODULE_FAIL;
159 inst->last_made_directory = strdup(buffer);
163 } /* else there was no directory delimiter. */
166 * Open & create the file, with the given permissions.
168 * If we're not using locking, we'll just pass straight though
170 * If we fail to aquire the filelock in 80 tries (approximately
171 * two seconds) we bail out.
176 if ((outfd = open(buffer, O_WRONLY | O_APPEND | O_CREAT,
177 inst->detailperm)) < 0) {
178 radlog(L_ERR, "rlm_detail: Couldn't open file %s: %s",
179 buffer, strerror(errno));
180 ret = RLM_MODULE_FAIL;
184 lseek(outfd, 0L, SEEK_SET);
185 if (rad_lockfd_nonblock(outfd, 0) < 0) {
189 select(0, NULL, NULL, NULL, &tv);
192 DEBUG("rlm_detail: Aquired filelock, tried %d time(s)",
197 } while (!locked && inst->locking && lock_count < 80);
199 if (!locked && inst->locking && lock_count >= 80) {
200 radlog(L_ERR, "rlm_detail: Failed to aquire filelock for %s, giving up",
203 ret = RLM_MODULE_FAIL;
208 if ((outfp = fdopen(outfd, "a")) == NULL) {
209 radlog(L_ERR, "rlm_detail: Couldn't open file %s: %s",
210 buffer, strerror(errno));
211 ret = RLM_MODULE_FAIL;
213 lseek(outfd, 0L, SEEK_SET);
214 rad_unlockfd(outfd, 0);
215 DEBUG("rlm_detail: Released filelock");
221 if (outfd > -1 && outfp) {
222 /* Post a timestamp */
223 fseek(outfp, 0L, SEEK_END);
224 fputs(ctime_r(&request->timestamp, buffer), outfp);
226 /* Write each attribute/value to the log file */
228 if (pair->attribute != PW_PASSWORD) {
230 vp_print(outfp, pair);
237 * Add non-protocol attibutes.
239 if ((pair = pairfind(request->config_items, PW_PROXY_TO_REALM))
241 proxy_realm = realm_find(pair->strvalue, TRUE);
243 memset((char *) proxy_buffer, 0, 16);
244 ip_ntoa(proxy_buffer, proxy_realm->acct_ipaddr);
245 fprintf(outfp, "\tFreeradius-Proxied-To = %s\n",
247 DEBUG("rlm_detail: Freeradius-Proxied-To set to %s",
251 fprintf(outfp, "\tTimestamp = %ld\n", request->timestamp);
252 if (request->packet->verified == 2)
253 fputs("\tRequest-Authenticator = Verified\n", outfp);
254 else if (request->packet->verified == 1)
255 fputs("\tRequest-Authenticator = None\n", outfp);
262 lseek(outfd, 0L, SEEK_SET);
263 rad_unlockfd(outfd, 0);
264 DEBUG("rlm_detail: Released filelock");
274 * Accounting - write the detail files.
276 static int detail_accounting(void *instance, REQUEST *request)
279 return do_detail(instance,request,request->packet->vps);
283 * Incoming Access Request - write the detail files.
285 static int detail_authorize(void *instance, REQUEST *request)
287 return do_detail(instance,request,request->packet->vps);
291 * Outgoing Access-Request Reply - write the detail files.
293 static int detail_postauth(void *instance, REQUEST *request)
295 return do_detail(instance,request,request->reply->vps);
302 static int detail_detach(void *instance)
304 struct detail_instance *inst = instance;
305 free((char *) inst->detailfile);
307 if (inst->last_made_directory)
308 free((char*) inst->last_made_directory);
314 /* globally exported name */
315 module_t rlm_detail = {
317 0, /* type: reserved */
318 NULL, /* initialization */
319 detail_instantiate, /* instantiation */
321 NULL, /* authentication */
322 detail_authorize, /* authorization */
323 NULL, /* preaccounting */
324 detail_accounting, /* accounting */
325 NULL, /* checksimul */
326 NULL, /* pre-proxy */
327 NULL, /* post-proxy */
328 detail_postauth /* post-auth */
330 detail_detach, /* detach */