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