* Replace the call to malloc() with a call to rad_malloc() and
[freeradius.git] / src / modules / rlm_detail / rlm_detail.c
1 /*
2  * rlm_detail.c accounting:    Write the "detail" files.
3  *
4  * Version:     $Id$
5  *
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.
9  * 
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.
14  *  
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.
18  *
19  * Copyright 2000  The FreeRADIUS server project
20  */
21
22 static const char rcsid[] = "$Id$";
23
24 #include        "autoconf.h"
25 #include        "libradius.h"
26
27 #include        <sys/stat.h>
28
29 #include        <stdlib.h>
30 #include        <string.h>
31 #include        <ctype.h>
32 #include        <fcntl.h>
33
34 #include        "radiusd.h"
35 #include        "modules.h"
36 #define         DIRLEN  8192
37
38 struct detail_instance {
39         /* detail file */
40         char *detailfile;
41
42         /* detail file permissions */
43         int detailperm;
44
45         /* directory permissions */
46         int dirperm;
47
48         /* last made directory */
49         char *last_made_directory;
50 };
51
52 /*
53  *      A temporary holding area for config values to be extracted
54  *      into, before they are copied into the instance data
55  */
56 static struct detail_instance config;
57
58 static CONF_PARSER module_config[] = {
59         { "detailfile",    PW_TYPE_STRING_PTR, &config.detailfile, "%A/%{Client-IP-Address}/detail" },
60         { "detailperm",    PW_TYPE_INTEGER,    &config.detailperm, "0600" },
61         { "dirperm",       PW_TYPE_INTEGER,    &config.dirperm,    "0755" },
62         { NULL, -1, NULL, NULL }
63 };
64
65 /*
66  *      (Re-)read radiusd.conf into memory.
67  */
68 static int detail_instantiate(CONF_SECTION *conf, void **instance)
69 {
70         struct detail_instance *inst;
71
72         inst = rad_malloc(sizeof *inst);
73
74         if (cf_section_parse(conf, module_config) < 0) {
75                 free(inst);
76                 return -1;
77         }
78
79         inst->detailfile = config.detailfile;
80         inst->detailperm = config.detailperm;
81         inst->dirperm = config.dirperm;
82         inst->last_made_directory = NULL;
83         config.detailfile = NULL;
84
85         *instance = inst;
86         return 0;
87 }
88
89 /*
90  *      Accounting - write the detail files.
91  */
92 static int detail_accounting(void *instance, REQUEST *request)
93 {
94         int             outfd;
95         FILE            *outfp;
96         char            buffer[DIRLEN];
97         char            *p;
98         VALUE_PAIR      *pair;
99         int             ret = RLM_MODULE_OK;
100         struct stat     st;
101
102         struct detail_instance *inst = instance;
103
104         /*
105          *      Create a directory for this nas.
106          *
107          *      Generate the path for the detail file.  Use the
108          *      same format, but truncate at the last /.  Then
109          *      feed it through radius_xlat2() to expand the
110          *      variables.
111          */
112         radius_xlat2(buffer, sizeof(buffer), inst->detailfile, request);
113         DEBUG2("rlm_detail: %s expands to %s", inst->detailfile, buffer);
114
115         /*
116          *      Grab the last directory delimiter.
117          */
118         p = strrchr(buffer,'/');
119
120         /*
121          *      There WAS a directory delimiter there, and
122          *      the file doesn't exist, so
123          *      we prolly must create it the dir(s)
124          */
125         if ((p) && (stat(buffer, &st) < 0)) {
126                 *p = '\0';      
127                 /*
128                  *      NO previously cached directory name, so we've
129                  *      got to create a new one.
130                  *
131                  *      OR the new directory name is different than the old,
132                  *      so we've got to create a new one.
133                  *
134                  *      OR the cached directory has somehow gotten removed,
135                  *      so we've got to create a new one.
136                  */
137                 if ((inst->last_made_directory == NULL) ||
138                     (strcmp(inst->last_made_directory, buffer) != 0)) { 
139                         
140                         /*
141                          *      Free any previously cached name.
142                          */
143                         if (inst->last_made_directory != NULL) {
144                                 free((char *) inst->last_made_directory);
145                                 inst->last_made_directory = NULL;
146                         }
147                         
148                         /*
149                          *      Go create possibly multiple directories.
150                          */
151                         if (rad_mkdir(buffer, inst->dirperm) < 0) {
152                                 radlog(L_ERR, "rlm_detail: Failed to create directory %s: %s", buffer, strerror(errno));
153                                 return RLM_MODULE_FAIL;
154                         }
155                         inst->last_made_directory = strdup(buffer);
156                 }
157                 
158                 *p = '/';       
159         } /* else there was no directory delimiter. */
160
161         /*
162          *      Open & create the file, with the given permissions.
163          */
164         if ((outfd = open(buffer, O_WRONLY|O_APPEND|O_CREAT,
165                           inst->detailperm)) < 0) {
166                 radlog(L_ERR, "rlm_detail: Couldn't open file %s: %s",
167                        buffer, strerror(errno));
168                 ret = RLM_MODULE_FAIL;
169
170         } else if ((outfp = fdopen(outfd, "a")) == NULL) {
171                 radlog(L_ERR, "rlm_detail: Couldn't open file %s: %s",
172                        buffer, strerror(errno));
173                 ret = RLM_MODULE_FAIL;
174                 close(outfd);
175         } else {
176                 /* Post a timestamp */
177                 fputs(ctime(&request->timestamp), outfp);
178
179                 /* Write each attribute/value to the log file */
180                 pair = request->packet->vps;
181                 while (pair) {
182                         if (pair->attribute != PW_PASSWORD) {
183                                 fputs("\t", outfp);
184                                 vp_print(outfp, pair);
185                                 fputs("\n", outfp);
186                         }
187                         pair = pair->next;
188                 }
189
190                 /*
191                  *      Add non-protocol attibutes.
192                  */
193                 fprintf(outfp, "\tTimestamp = %ld\n", request->timestamp);
194                 if (request->packet->verified)
195                         fputs("\tRequest-Authenticator = Verified\n", outfp);
196                 else
197                         fputs("\tRequest-Authenticator = None\n", outfp);
198                 fputs("\n", outfp);
199                 fclose(outfp);
200         }
201
202         return ret;
203 }
204
205
206 /*
207  *      Clean up.
208  */
209 static int detail_detach(void *instance)
210 {
211         struct detail_instance *inst = instance;
212         free((char *) inst->detailfile);
213
214         if (inst->last_made_directory)
215                 free((char*) inst->last_made_directory);
216         free(inst);
217         return 0;
218 }
219
220
221 /* globally exported name */
222 module_t rlm_detail = {
223         "detail",
224         0,                              /* type: reserved */
225         NULL,                           /* initialization */
226         detail_instantiate,             /* instantiation */
227         NULL,                           /* authorization */
228         NULL,                           /* authentication */
229         NULL,                           /* preaccounting */
230         detail_accounting,              /* accounting */
231         NULL,                           /* checksimul */
232         detail_detach,                  /* detach */
233         NULL                            /* destroy */
234 };
235