include <string.h> if necessary, too
[freeradius.git] / src / modules / rlm_radutmp / rlm_radutmp2.c
1 /*
2  * rlm_radutmp.c
3  *
4  * Version:     $Id$
5  *
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.
10  *
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.
15  *
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000,2001,2002,2003,2004,2006  The FreeRADIUS server project
21  */
22
23 #include        <freeradius-devel/ident.h>
24 RCSID("$Id$")
25
26 #include        <freeradius-devel/autoconf.h>
27
28 #include        <sys/types.h>
29 #include        <stdio.h>
30 #include        <string.h>
31 #include        <stdlib.h>
32 #include        <unistd.h>
33 #include        <fcntl.h>
34 #include        <time.h>
35 #include        <errno.h>
36 #include        <limits.h>
37
38 #include "config.h"
39
40 #include        <freeradius-devel/radiusd.h>
41 #include        <freeradius-devel/radutmp.h>
42 #include        <freeradius-devel/modules.h>
43 #include        <freeradius-devel/rad_assert.h>
44
45 #define LOCK_LEN sizeof(struct radutmp)
46
47 static const char porttypes[] = "ASITX";
48
49 /*
50  *      Used for caching radutmp lookups in the accounting
51  *      component. The session (checksimul) component doesn't use it,
52  *      but probably should, though we're not sure how...
53  *
54  *      The intent here is to keep this structure as small as
55  *      possible, so that it doesn't take up too much memory.
56  */
57 typedef struct nas_port {
58         uint32_t                nas_address;
59         unsigned int            nas_port;
60         off_t                   offset;
61
62         struct nas_port         *next; /* for the free list */
63 } NAS_PORT;
64
65
66 /*
67  *      Per-file information.
68  *
69  *      Hmm... having multiple filenames managed by one instance
70  *      of the module makes it difficult for the module to do
71  *      simultaneous-use checking, without more code edits.
72  */
73 typedef struct radutmp_cache_t {
74         const char      *filename; /* for future reference */
75         time_t          last_used; /* for future reference */
76
77         rbtree_t        *nas_ports;
78         NAS_PORT        *free_offsets;
79         off_t           max_offset;
80         int             cached_file;
81         int             permission;
82 #ifdef HAVE_PTHREAD_H
83         pthread_mutex_t mutex;
84 #endif
85 } radutmp_cache_t;
86
87
88 /*
89  *      We cache the users, too, so that we only have to read radutmp
90  *      once.
91  */
92 typedef struct radutmp_simul_t {
93         char            login[sizeof(((struct radutmp *) NULL)->login) + 1];
94         int             simul_count;
95 } radutmp_simul_t;
96
97
98 /*
99  *      Data we store per module.
100  */
101 typedef struct rlm_radutmp_t {
102         char            *filename;
103         char            *username;
104         int             case_sensitive;
105         int             check_nas;
106         int             permission;
107         int             callerid_ok;
108
109         rbtree_t        *user_tree; /* for simultaneous-use */
110
111         /*
112          *      As the filenames can be dynamically translated,
113          *      we want to keep track of them in a separate data
114          *      structure, so that we can have per-file caches.
115          */
116         radutmp_cache_t cache;
117 } rlm_radutmp_t;
118
119 #ifndef HAVE_PTHREAD_H
120 /*
121  *      This is easier than ifdef's throughout the code.
122  */
123 #define pthread_mutex_init(_x, _y)
124 #define pthread_mutex_destroy(_x)
125 #define pthread_mutex_lock(_x)
126 #define pthread_mutex_unlock(_x)
127 #endif
128
129 static const CONF_PARSER module_config[] = {
130         { "filename", PW_TYPE_STRING_PTR,
131           offsetof(rlm_radutmp_t,filename), NULL,  RADUTMP },
132         { "username", PW_TYPE_STRING_PTR,
133           offsetof(rlm_radutmp_t,username), NULL,  "%{User-Name}"},
134         { "case_sensitive", PW_TYPE_BOOLEAN,
135           offsetof(rlm_radutmp_t,case_sensitive), NULL,  "yes"},
136         { "check_with_nas", PW_TYPE_BOOLEAN,
137           offsetof(rlm_radutmp_t,check_nas), NULL,  "yes"},
138         { "perm",     PW_TYPE_INTEGER,
139           offsetof(rlm_radutmp_t,permission), NULL,  "0644" },
140         { "callerid", PW_TYPE_BOOLEAN,
141           offsetof(rlm_radutmp_t,callerid_ok), NULL, "no" },
142         { NULL, -1, 0, NULL, NULL }             /* end the list */
143 };
144
145
146 /*
147  *      NAS PORT cmp
148  */
149 static int nas_port_cmp(const void *a, const void *b)
150 {
151         const NAS_PORT *one = a;
152         const NAS_PORT *two = b;
153
154         if (one->nas_address < two->nas_address) return -1;
155         if (one->nas_address > two->nas_address) return +1;
156
157         if (one->nas_port < two->nas_port) return -1;
158         if (one->nas_port > two->nas_port) return +1;
159
160         return 0;
161 }
162
163
164 /*
165  *      Compare two user names.
166  */
167 static int user_cmp(const void *a, const void *b)
168 {
169         const radutmp_simul_t *one = a;
170         const radutmp_simul_t *two = b;
171
172         return strcmp(one->login, two->login);
173 }
174
175
176 /*
177  *      Compare two user names, case insensitive.
178  */
179 static int user_case_cmp(const void *a, const void *b)
180 {
181         const radutmp_simul_t *one = a;
182         const radutmp_simul_t *two = b;
183
184         return strcasecmp(one->login, two->login);
185 }
186
187
188 /*
189  *      Detach.
190  */
191 static int radutmp_detach(void *instance)
192 {
193         NAS_PORT        *this, *next;
194         rlm_radutmp_t *inst = instance;
195
196         rbtree_free(inst->cache.nas_ports);
197
198         for (this = inst->cache.free_offsets;
199              this != NULL;
200              this = next) {
201                 next = this->next;
202                 free(this);
203         }
204
205         if (inst->cache.filename) free(inst->cache.filename);
206
207         pthread_mutex_destroy(&(inst->cache.mutex));
208
209
210         rbtree_free(inst->user_tree);
211
212         free(inst);
213         return 0;
214 }
215
216
217 /*
218  *      Instantiate.
219  */
220 static int radutmp_instantiate(CONF_SECTION *conf, void **instance)
221 {
222         rlm_radutmp_t *inst;
223
224         inst = rad_malloc(sizeof(*inst));
225         if (!inst) {
226                 return -1;
227         }
228         memset(inst, 0, sizeof(*inst));
229
230         if (cf_section_parse(conf, inst, module_config)) {
231                 radutmp_detach(inst);
232                 return -1;
233         }
234
235         inst->cache.nas_ports = rbtree_create(nas_port_cmp, free, 0);
236         if (!inst->cache.nas_ports) {
237                 radlog(L_ERR, "rlm_radutmp: Failed to create nas tree");
238                 radutmp_detach(inst);
239                 return -1;
240         }
241
242         pthread_mutex_init(&(inst->cache.mutex), NULL);
243         inst->cache.permission = inst->permission;
244
245         if (inst->case_sensitive) {
246                 inst->user_tree = rbtree_create(user_cmp, free, 0);
247         } else {
248                 inst->user_tree = rbtree_create(user_case_cmp, free, 0);
249         }
250         if (!inst->user_tree) {
251                 radlog(L_ERR, "rlm_radutmp: Failed to create user tree");
252                 radutmp_detach(inst);
253                 return -1;
254         }
255
256         *instance = inst;
257         return 0;
258 }
259
260
261 /*
262  *      Reset the cached entries.
263  */
264 static int cache_reset(rlm_radutmp_t *inst, radutmp_cache_t *cache)
265 {
266         NAS_PORT *this, *next;
267         
268         /*
269          *      Cache is already reset, do nothing.
270          */
271         if ((rbtree_num_elements(cache->nas_ports) == 0) &&
272             (cache->free_offsets == NULL)) {
273                 DEBUG2("  rlm_radutmp: Not resetting the cache");
274                 return 1;
275         }
276         DEBUG2("  rlm_radutmp: Resetting the cache");
277
278         pthread_mutex_lock(&cache->mutex);
279
280         rbtree_free(inst->user_tree);
281
282         rbtree_free(cache->nas_ports);
283         
284         for (this = cache->free_offsets;
285              this != NULL;
286              this = next) {
287                 next = this->next;
288                 free(this);
289         }
290         cache->free_offsets = NULL;
291
292         /*
293          *      Re-create the caches.
294          */
295         cache->nas_ports = rbtree_create(nas_port_cmp, free, 0);
296         if (!cache->nas_ports) {
297                 pthread_mutex_unlock(&cache->mutex);
298                 radlog(L_ERR, "rlm_radutmp: No memory");
299                 return 0;
300         }
301         
302         cache->max_offset = 0;
303         
304         cache->cached_file = 1;
305
306         if (inst->case_sensitive) {
307                 inst->user_tree = rbtree_create(user_cmp, free, 0);
308         } else {
309                 inst->user_tree = rbtree_create(user_case_cmp, free, 0);
310         }
311         if (!inst->user_tree) {
312                 pthread_mutex_unlock(&cache->mutex);
313                 radlog(L_ERR, "rlm_radutmp: No memory");
314                 return 0;
315         }
316
317         pthread_mutex_unlock(&cache->mutex);
318
319         return 1;
320 }
321
322
323 /*
324  *      Compare two offsets in a tree.
325  */
326 static int offset_cmp(const void *a, const void *b)
327 {
328         const NAS_PORT *one = a;
329         const NAS_PORT *two = b;
330
331         if (one->offset < two->offset) return -1;
332         if (one->offset > two->offset) return +1;
333
334         return 0;
335 }
336
337
338 /*
339  *      Data structure to use when walking the trees, for zap.
340  */
341 typedef struct offset_walk_t {
342         rlm_radutmp_t   *inst;
343         radutmp_cache_t *cache;
344         rbtree_t        *offset_tree;
345         uint32_t        nas_address;
346         int             fd;
347         time_t          now;
348 } offset_walk_t;
349
350
351 /*
352  *      Walk over the cache, finding entries with the matching NAS IP address.
353  */
354 static int nas_port_walk(void *context, void *data)
355 {
356         offset_walk_t   *walk = context;
357         NAS_PORT        *nas_port = data;
358
359         /*
360          *      Doesn't match, keep going.
361          */
362         if (walk->nas_address != nas_port->nas_address) return 0;
363
364         /*
365          *      Insert it into the offset tree, for later deletion.
366          */
367         if (rbtree_insert(walk->offset_tree, nas_port) != 1) {
368                 DEBUG2("  rlm_radumtp: Insertion failed in nas port walk.");
369                 return 1;
370         }
371
372         return 0;
373 }
374
375
376 /*
377  *      Walk through the offset tree, operating on the cache
378  */
379 static int offset_walk(void *context, void *data)
380 {
381         offset_walk_t   *walk = context;
382         NAS_PORT        *nas_port = data;
383         struct radutmp  utmp;
384         radutmp_simul_t *user, myUser;
385
386         /*
387          *      Seek to the entry, and possibly re-write it.
388          */
389         if (lseek(walk->fd, nas_port->offset, SEEK_SET) < 0) {
390                 rad_assert(0 == 1);
391         }
392
393         if (read(walk->fd, &utmp, sizeof(utmp)) != sizeof(utmp)) {
394                 rad_assert(0 == 1);
395         }
396         
397         /*
398          *      If the entry in the file is NEWER than the reboot
399          *      packet, don't re-write it, and don't delete it.
400          */
401         if (utmp.time > walk->now) {
402                 return 0;
403         }
404
405         utmp.type = P_IDLE;
406         utmp.time = walk->now;
407
408         if (lseek(walk->fd, -(off_t)sizeof(utmp), SEEK_CUR) < 0) {
409                 radlog(L_ERR, "rlm_radutmp: offset_walk: failed in lseek: %s",
410                        strerror(errno));
411                 return 1;
412         }
413
414         write(walk->fd, &utmp, sizeof(utmp));
415
416         strlcpy(myUser.login, utmp.login, sizeof(myUser.login));
417         user = rbtree_finddata(walk->inst->user_tree, &myUser);
418         rad_assert(user != NULL);
419         rad_assert(user->simul_count > 0);
420         user->simul_count--;
421         if (user->simul_count == 0) {
422                 rbtree_deletebydata(walk->inst->user_tree, user);
423         }
424
425         if (rbtree_deletebydata(walk->cache->nas_ports, nas_port) == 0) {
426                 radlog(L_ERR, "rlm_radutmp: Failed to delete entry from cache");
427                 return 1;
428         }
429
430         /*
431          *      Insert the entry into the free list.
432          */
433         nas_port->next = walk->cache->free_offsets;
434         walk->cache->free_offsets = nas_port;
435
436         return 0;
437 }
438
439
440 /*
441  *      Zap all users on a NAS from the radutmp file.
442  */
443 static int radutmp_zap(rlm_radutmp_t *inst,
444                        radutmp_cache_t *cache,
445                        uint32_t nas_address,
446                        time_t now)
447 {
448         int             rcode;
449         rbtree_t        *offset_tree;
450         offset_walk_t   walk;
451
452         rad_assert(now != 0);
453
454         /*
455          *      If there's nothing in the file, do nothing,
456          *      but truncate the file, just to be safe.
457          */
458         if (rbtree_num_elements(cache->nas_ports) == 0) {
459                 truncate(cache->filename, (off_t) 0);
460                 DEBUG2("  rlm_radutmp: No entries in file.  Quenching zap.");
461                 return 1;
462         }
463
464         /*
465          *      Create the offset tree, as we want to delete utmp
466          *      entries starting from the start of the file, and we
467          *      can't delete nodes from an rbtree while we're walking
468          *      it.
469          */
470         offset_tree = rbtree_create(offset_cmp, NULL, 0);
471         if (!offset_tree) {
472                 radlog(L_ERR, "rlm_radutmp: Out of memory");
473                 return 0;
474         }
475         
476         pthread_mutex_lock(&cache->mutex);
477
478         /*
479          *      Walk through the cache, finding entries for this NAS,
480          *      and add those entries to the offset tree.
481          */
482         memset(&walk, 0, sizeof(walk));
483         walk.inst = inst;
484         walk.offset_tree = offset_tree;
485         walk.nas_address = nas_address;
486         rcode = rbtree_walk(cache->nas_ports, PreOrder, nas_port_walk, &walk);
487         if (rcode != 0) {
488                 pthread_mutex_unlock(&cache->mutex);
489                 rbtree_free(offset_tree);
490                 radlog(L_ERR, "rlm_radutmp: Failed walking the cache.");
491                 return 0;
492         }
493
494         /*
495          *      If both trees have the same number of elements, then
496          *      don't do anything special, as UDP packets may be
497          *      received out of order, by several seconds.  The
498          *      "offset_walk" routine MAY NOT delete the entries, if
499          *      it sees that the entries in the file are newer than
500          *      the reboot packet.
501          */
502
503         /*
504          *      If there's nothing to do, don't do anything.
505          */
506         if (rbtree_num_elements(offset_tree) == 0) {
507                 DEBUG2("  rlm_radutmp: NAS IP %08x has no users recorded in file %s.",
508                        htonl(nas_address), cache->filename);
509                 pthread_mutex_unlock(&cache->mutex);
510                 rbtree_free(offset_tree);
511                 return 1;
512         }
513
514         /*
515          *      Open the file, to re-write only a few of the entries.
516          */
517         walk.fd = open(cache->filename, O_RDWR);
518         if (walk.fd < 0) {
519                 pthread_mutex_unlock(&cache->mutex);
520                 rbtree_free(offset_tree);
521                 radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
522                        cache->filename, strerror(errno));
523                 return 0;
524         }
525
526         /*
527          *      Lock the utmp file, prefer lockf() over flock().
528          *
529          *      FIXME: maybe we want to lock per-record?
530          */
531         rad_lockfd(walk.fd, LOCK_LEN);
532
533         /*
534          *      Walk through the offset tree, from start to finish,
535          *      deleting entries from the NAS tree, adding them to
536          *      the "free offset" cache, and lseek'ing to that offset
537          *      in the file, and clearing out the data.
538          */
539         walk.cache = cache;
540         walk.now = now;
541         rcode = rbtree_walk(offset_tree, InOrder, offset_walk, &walk);
542         rbtree_free(offset_tree);
543         if (rcode != 0) {
544                 radlog(L_ERR, "rlm_radutmp: Failed walking the offsets.");
545                 return 0;
546         }
547
548         close(walk.fd); /* and implicitly release the locks */
549
550         /*
551          *      Just to clean up the file.  If it's empty,
552          *      nuke everything.
553          */
554         if (rbtree_num_elements(cache->nas_ports) == 0) {
555                 NAS_PORT        *this, *next; /* too many copies of code */
556                 
557                 for (this = inst->cache.free_offsets;
558                      this != NULL;
559                      this = next) {
560                         next = this->next;
561                         free(this);
562                 }
563                 
564                 truncate(cache->filename, 0);
565                 rad_assert(rbtree_num_elements(inst->user_tree) == 0);
566         }
567
568         pthread_mutex_unlock(&cache->mutex);
569         
570         return 1;
571 }
572
573
574 /*
575  *      Read a file, to cache all of its entries.
576  */
577 static int cache_file(rlm_radutmp_t *inst, radutmp_cache_t *cache)
578 {
579         int             fd;
580         int             read_size;
581         struct          stat buf;
582         struct          radutmp utmp;
583         NAS_PORT        **tail;
584
585         rad_assert(cache->max_offset == 0);
586         rad_assert(cache->free_offsets == NULL);
587
588         /*
589          *      Doesn't exist, we're fine.
590          */
591         if (stat(cache->filename, &buf) < 0) {
592                 if (errno == ENOENT) {
593                         cache->cached_file = 1;
594                         return 0;
595                 }
596                 radlog(L_ERR, "rlm_radutmp: Cannot stat %s: %s",
597                        cache->filename, strerror(errno));
598                 return 1;
599         }
600
601         /*
602          *      Nothing's there, we're OK.
603          */
604         if (buf.st_size == 0) {
605                 cache->cached_file = 1;
606                 return 0;
607         }
608
609         /*
610          *      Don't let others much around with our data.
611          */
612         pthread_mutex_lock(&cache->mutex);
613
614         /*
615          *      Read the file and cache it's entries.
616          */
617         fd = open(cache->filename, O_RDONLY, cache->permission);
618         if (fd < 0) {
619                 pthread_mutex_unlock(&cache->mutex);
620                 radlog(L_ERR, "rlm_radutmp: Error opening %s: %s",
621                        cache->filename, strerror(errno));
622                 return 1;
623         }
624
625         /*
626          *      Insert free entries into the tail, so that entries
627          *      get used from the start.
628          */
629         tail = &(cache->free_offsets);
630
631         /*
632          *      Don't lock the file, as we're only reading it.
633          */
634         do {
635                 read_size = read(fd, &utmp, sizeof(utmp));
636
637                 /*
638                  *      Read one record.
639                  */
640                 if (read_size == sizeof(utmp)) {
641                         radutmp_simul_t *user, myUser;
642                         NAS_PORT *nas_port = rad_malloc(sizeof(*nas_port));
643
644                         memset(nas_port, 0, sizeof(nas_port));
645                         nas_port->offset = cache->max_offset;
646                         cache->max_offset += sizeof(utmp);
647
648                         /*
649                          *      Idle.  Add it to the list of free
650                          *      offsets.
651                          */
652                         if (utmp.type == P_IDLE) {
653                                 *tail = nas_port;
654                                 tail = &(nas_port->next);
655                                 continue;
656                         }
657
658                         /*
659                          *      It's a login record, 
660                          */
661                         nas_port->nas_address = utmp.nas_address;
662                         nas_port->nas_port = utmp.nas_port;
663
664                         if (!rbtree_insert(cache->nas_ports, nas_port)) {
665                                 rad_assert(0 == 1);
666                         }
667
668                         /*
669                          *      Adds a trailing \0, so myUser.login has
670                          *      an extra char allocated..
671                          */
672                         strlcpy(myUser.login, utmp.login, sizeof(myUser.login));
673                         user = rbtree_finddata(inst->user_tree, &myUser);
674                         if (user) {
675                                 user->simul_count++;
676                         } else {
677                                 /*
678                                  *      Allocate new entry, and add it
679                                  *      to the tree.
680                                  */
681                                 user = rad_malloc(sizeof(user));
682                                 strlcpy(user->login, utmp.login,
683                                         sizeof(user->login));
684                                 user->simul_count = 1;
685
686                                 if (!rbtree_insert(inst->user_tree, user)) {
687                                         rad_assert(0 == 1);
688                                 }
689                         }
690                         continue;
691                 }
692                 
693                 /*
694                  *      We've read a partial record.  WTF?
695                  */
696                 if (read_size != 0) {
697                         pthread_mutex_unlock(&cache->mutex);
698                         close(fd);
699                         radlog(L_ERR, "rlm_radutmp: Badly formed file %s",
700                                cache->filename);
701                         return 1;
702                 }
703
704                 /*
705                  *      Read nothing, stop.
706                  */
707         } while (read_size != 0);
708
709         pthread_mutex_unlock(&cache->mutex);
710         close(fd);              /* and release the lock. */
711         cache->cached_file = 1;
712
713         return 0;
714 }
715
716
717 /*
718  *      Store logins in the RADIUS utmp file.
719  */
720 static int radutmp_accounting(void *instance, REQUEST *request)
721 {
722         rlm_radutmp_t   *inst = instance;
723         struct radutmp  utmp, u;
724         VALUE_PAIR      *vp;
725         int             status = -1;
726         uint32_t        nas_address = 0;
727         uint32_t        framed_address = 0;
728         int             protocol = -1;
729         int             fd;
730         int             port_seen = 0;
731         char            buffer[256];
732         char            filename[1024];
733         char            ip_name[32]; /* 255.255.255.255 */
734         const char      *nas;
735         NAS_PORT        *nas_port, myPort;
736         radutmp_cache_t *cache;
737         int             read_size;
738         rbnode_t        *node;
739
740         /*
741          *      Which type is this.
742          */
743         if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) == NULL) {
744                 radlog(L_ERR, "rlm_radutmp: No Accounting-Status-Type record.");
745                 return RLM_MODULE_NOOP;
746         }
747         status = vp->lvalue;
748
749         /*
750          *      Look for weird reboot packets.
751          *
752          *      ComOS (up to and including 3.5.1b20) does not send
753          *      standard PW_STATUS_ACCOUNTING_* messages.
754          *
755          *      Check for:  o no Acct-Session-Time, or time of 0
756          *                  o Acct-Session-Id of "00000000".
757          *
758          *      We could also check for NAS-Port, that attribute
759          *      should NOT be present (but we don't right now).
760          */
761         if ((status != PW_STATUS_ACCOUNTING_ON) &&
762             (status != PW_STATUS_ACCOUNTING_OFF)) do {
763                 int check1 = 0;
764                 int check2 = 0;
765
766                 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME))
767                      == NULL || vp->lvalue == 0)
768                         check1 = 1;
769                 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID))
770                      != NULL && vp->length == 8 &&
771                      memcmp(vp->vp_strvalue, "00000000", 8) == 0)
772                         check2 = 1;
773                 if (check1 == 0 || check2 == 0) {
774 #if 0 /* Cisco sometimes sends START records without username. */
775                         radlog(L_ERR, "rlm_radutmp: no username in record");
776                         return RLM_MODULE_FAIL;
777 #else
778                         break;
779 #endif
780                 }
781                 radlog(L_INFO, "rlm_radutmp: converting reboot records.");
782                 if (status == PW_STATUS_STOP)
783                         status = PW_STATUS_ACCOUNTING_OFF;
784                 if (status == PW_STATUS_START)
785                         status = PW_STATUS_ACCOUNTING_ON;
786         } while(0);
787
788         memset(&utmp, 0, sizeof(utmp));
789         utmp.porttype = 'A';
790
791         /*
792          *      First, find the interesting attributes.
793          */
794         for (vp = request->packet->vps; vp; vp = vp->next) {
795                 switch (vp->attribute) {
796                         case PW_LOGIN_IP_HOST:
797                         case PW_FRAMED_IP_ADDRESS:
798                                 framed_address = vp->lvalue;
799                                 utmp.framed_address = vp->lvalue;
800                                 break;
801                         case PW_FRAMED_PROTOCOL:
802                                 protocol = vp->lvalue;
803                                 break;
804                         case PW_NAS_IP_ADDRESS:
805                                 nas_address = vp->lvalue;
806                                 utmp.nas_address = vp->lvalue;
807                                 break;
808                         case PW_NAS_PORT:
809                                 utmp.nas_port = vp->lvalue;
810                                 port_seen = 1;
811                                 break;
812                         case PW_ACCT_DELAY_TIME:
813                                 utmp.delay = vp->lvalue;
814                                 break;
815                         case PW_ACCT_SESSION_ID:
816                                 /*
817                                  *      If it's too big, only use the
818                                  *      last bit.
819                                  */
820                                 if (vp->length > sizeof(utmp.session_id)) {
821                                         int length = vp->length - sizeof(utmp.session_id);
822
823                                         /*
824                                          *      Ascend is br0ken - it
825                                          *      adds a \0 to the end
826                                          *      of any string.
827                                          *      Compensate.
828                                          */
829                                         if (vp->vp_strvalue[vp->length - 1] == 0) {
830                                                 length--;
831                                         }
832
833                                         memcpy(utmp.session_id,
834                                               vp->vp_strvalue + length,
835                                               sizeof(utmp.session_id));
836                                 } else {
837                                         memset(utmp.session_id, 0,
838                                                sizeof(utmp.session_id));
839                                         memcpy(utmp.session_id,
840                                                vp->vp_strvalue,
841                                                vp->length);
842                                 }
843                                 break;
844                         case PW_NAS_PORT_TYPE:
845                                 if (vp->lvalue <= 4)
846                                         utmp.porttype = porttypes[vp->lvalue];
847                                 break;
848                         case PW_CALLING_STATION_ID:
849                                 if(inst->callerid_ok)
850                                         strlcpy(utmp.caller_id,
851                                                 (char *)vp->vp_strvalue,
852                                                 sizeof(utmp.caller_id));
853                                 break;
854                 }
855         }
856
857         /*
858          *      If we didn't find out the NAS address, use the
859          *      originator's IP address.
860          */
861         if (nas_address == 0) {
862                 nas_address = request->packet->src_ipaddr;
863                 utmp.nas_address = nas_address;
864                 nas = client_name(nas_address); /* MUST be a valid client */
865
866         } else {                /* might be a client, might not be. */
867                 RADCLIENT *cl;
868
869                 /*
870                  *      Hack like 'client_name()', but with sane
871                  *      fall-back.
872                  */
873                 cl = client_find(nas_address);
874                 if (cl) {
875                         if (cl->shortname && cl->shortname[0]) {
876                                 nas = cl->shortname;
877                         } else {
878                                 nas = cl->longname;
879                         }
880                 } else {
881                         /*
882                          *      The NAS isn't a client, it's behind
883                          *      a proxy server.  In that case, just
884                          *      get the IP address.
885                          */
886                         nas = ip_ntoa(ip_name, nas_address);
887                 }
888         }
889
890         /*
891          *      Set the protocol field.
892          */
893         if (protocol == PW_PPP)
894                 utmp.proto = 'P';
895         else if (protocol == PW_SLIP)
896                 utmp.proto = 'S';
897         else
898                 utmp.proto = 'T';
899
900         utmp.time = request->timestamp - utmp.delay;
901
902         /*
903          *      Get the utmp filename, via xlat.
904          */
905         radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
906
907         /*
908          *      Future: look up filename in filename tree, to get
909          *      radutmp_cache_t pointer
910          */
911         cache = &inst->cache;
912
913         /*
914          *      For now, double-check the filename, to be sure it isn't
915          *      changing.
916          */
917         if (!cache->filename) {
918                 cache->filename = strdup(filename);
919                 rad_assert(cache->filename != NULL);
920
921         } else if (strcmp(cache->filename, filename) != 0) {
922                 radlog(L_ERR, "rlm_radutmp: We do not support dynamically named files.");
923                 return RLM_MODULE_FAIL;
924         }
925
926         /*
927          *      If the lookup failed, create a new one, and add it
928          *      to the filename tree, and cache the file, as below.
929          */
930
931         /*
932          *      For aging, in the future.
933          */
934         cache->last_used = request->timestamp;
935
936         /*
937          *      If we haven't already read the file, then read the
938          *      entire file, in order to cache its entries.
939          */
940         if (!cache->cached_file) {
941                 cache_file(inst, cache);
942         }
943
944         /*
945          *      See if this was a reboot.
946          *
947          *      Hmm... we may not want to zap all of the users when
948          *      the NAS comes up, because of issues with receiving
949          *      UDP packets out of order.
950          */
951         if (status == PW_STATUS_ACCOUNTING_ON && nas_address) {
952                 radlog(L_INFO, "rlm_radutmp: NAS %s restarted (Accounting-On packet seen)",
953                        nas);
954                 if (!radutmp_zap(inst, cache, nas_address, utmp.time)) {
955                         rad_assert(0 == 1);
956                 }
957                 return RLM_MODULE_OK;
958         }
959
960         if (status == PW_STATUS_ACCOUNTING_OFF && nas_address) {
961                 radlog(L_INFO, "rlm_radutmp: NAS %s rebooted (Accounting-Off packet seen)",
962                        nas);
963                 if (!radutmp_zap(inst, cache, nas_address, utmp.time)) {
964                         rad_assert(0 == 1);
965                 }
966                 return RLM_MODULE_OK;
967         }
968
969         /*
970          *      If we don't know this type of entry, then pretend we
971          *      succeeded.
972          */
973         if (status != PW_STATUS_START &&
974             status != PW_STATUS_STOP &&
975             status != PW_STATUS_ALIVE) {
976                 radlog(L_ERR, "rlm_radutmp: NAS %s port %u unknown packet type %d, ignoring it.",
977                        nas, utmp.nas_port, status);
978                 return RLM_MODULE_NOOP;
979         }
980
981         /*
982          *      Perhaps we don't want to store this record into
983          *      radutmp. We skip records:
984          *
985          *      - without a NAS-Port (telnet / tcp access)
986          *      - with the username "!root" (console admin login)
987          */
988         if (!port_seen) {
989                 DEBUG2("  rlm_radutmp: No NAS-Port in the packet.  Cannot do anything.");
990                 DEBUG2("  rlm_radumtp: WARNING: checkrad will probably not work!");
991                 return RLM_MODULE_NOOP;
992         }
993
994         /*
995          *      Translate the User-Name attribute, or whatever else
996          *      they told us to use.
997          */
998         *buffer = '\0';
999         radius_xlat(buffer, sizeof(buffer), inst->username, request, NULL);
1000
1001         /*
1002          *      Don't log certain things...
1003          */
1004         if (strcmp(buffer, "!root") == 0) {
1005                 DEBUG2("  rlm_radutmp: Not recording administrative user");
1006
1007                 return RLM_MODULE_NOOP;
1008         }
1009         strlcpy(utmp.login, buffer, RUT_NAMESIZE);
1010
1011         /*
1012          *      First, try to open the file.  If it doesn't exist,
1013          *      nuke the existing caches, and try to create it.
1014          *
1015          *      FIXME: Create any intermediate directories, as
1016          *      appropriate.  See rlm_detail.
1017          */
1018         fd = open(cache->filename, O_RDWR, inst->permission);
1019         if (fd < 0) {
1020                 if (errno == ENOENT) {
1021                         DEBUG2("  rlm_radutmp: File %s doesn't exist, creating it.", cache->filename);
1022                         if (!cache_reset(inst, cache)) return RLM_MODULE_FAIL;
1023                         
1024                         /*
1025                          *      Try to create the file.
1026                          */
1027                         fd = open(cache->filename, O_RDWR | O_CREAT,
1028                                   inst->permission);
1029                 }
1030         } else {                /* exists, but may be empty */
1031                 struct stat buf;
1032
1033                 /*
1034                  *      If the file is empty, reset the cache.
1035                  */
1036                 if ((stat(cache->filename, &buf) == 0) &&
1037                     (buf.st_size == 0) &&
1038                     (!cache_reset(inst, cache))) {
1039                         return RLM_MODULE_FAIL;
1040                 }
1041                 DEBUG2("  rlm_radutmp: File %s was truncated.  Resetting cache.",
1042                        cache->filename);
1043         }
1044
1045         /*
1046          *      Error from creation, or error other than ENOENT: die.
1047          */
1048         if (fd < 0) {
1049                 radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
1050                        cache->filename, strerror(errno));
1051                 return RLM_MODULE_FAIL;
1052         }
1053
1054         /*
1055          *      OK.  Now that we've prepared everything we want to do,
1056          *      let's see if we've cached the entry.
1057          */
1058         myPort.nas_address = utmp.nas_address;
1059         myPort.nas_port = utmp.nas_port;
1060
1061         pthread_mutex_lock(&cache->mutex);
1062         node = rbtree_find(cache->nas_ports, &myPort);
1063         pthread_mutex_unlock(&cache->mutex);
1064
1065         if (node) {
1066                 nas_port = rbtree_node2data(cache->nas_ports, node);
1067 #if 0
1068
1069                 /*
1070                  *      stat the file, and get excited if it's been
1071                  *      truncated.
1072                  *
1073                  *      i.e wipe out the cache, and re-read the file.
1074                  */
1075
1076                 /*
1077                  *      Now find the new entry.
1078                  */
1079                 pthread_mutex_lock(&cache->mutex);
1080                 node = rbtree_find(cache->nas_ports, &myPort);
1081                 pthread_mutex_unlock(&cache->mutex);
1082 #endif
1083         }
1084
1085         if (!node) {
1086                 radutmp_simul_t *user;
1087
1088                 /*
1089                  *      Not found in the cache, and we're trying to
1090                  *      delete an existing record: ignore it.
1091                  */
1092                 if (status == PW_STATUS_STOP) {
1093                         DEBUG2("  rlm_radumtp: Logout entry for NAS %s port %u with no Login: ignoring it.",
1094                                nas, utmp.nas_port);
1095                         return RLM_MODULE_NOOP;
1096                 }
1097
1098                 pthread_mutex_lock(&cache->mutex);
1099
1100                 /*
1101                  *      It's a START or ALIVE.  Try to find a free
1102                  *      offset where we can store the new entry, or
1103                  *      create one, if one doesn't already exist.
1104                  */
1105                 if (!cache->free_offsets) {
1106                         cache->free_offsets = rad_malloc(sizeof(NAS_PORT));
1107                         memset(cache->free_offsets, 0,
1108                                sizeof(*(cache->free_offsets)));
1109                         cache->free_offsets->offset = cache->max_offset;
1110                         cache->max_offset += sizeof(u);
1111                 }
1112
1113                 /*
1114                  *      Grab the offset, and put it into the various
1115                  *      caches.
1116                  */
1117                 nas_port = cache->free_offsets;
1118                 cache->free_offsets = nas_port->next;
1119
1120                 nas_port->nas_address = nas_address;
1121                 nas_port->nas_port = utmp.nas_port;
1122
1123                 if (!rbtree_insert(cache->nas_ports, nas_port)) {
1124                         rad_assert(0 == 1);
1125                 }
1126
1127                 /*
1128                  *      Allocate new entry, and add it
1129                  *      to the tree.
1130                  */
1131                 user = rad_malloc(sizeof(user));
1132                 strlcpy(user->login, utmp.login,
1133                         sizeof(user->login));
1134                 user->simul_count = 1;
1135                 
1136                 if (!rbtree_insert(inst->user_tree, user)) {
1137                         rad_assert(0 == 1);
1138                 }
1139
1140                 pthread_mutex_unlock(&cache->mutex);
1141
1142         }
1143                 
1144         /*
1145          *      Entry was found, or newly created in the cache.
1146          *      Seek to the place in the file.
1147          */
1148         lseek(fd, nas_port->offset, SEEK_SET);
1149
1150         /*
1151          *      Lock the utmp file, prefer lockf() over flock().
1152          */
1153         rad_lockfd(fd, LOCK_LEN);
1154
1155         /*
1156          *      If it WAS found in the cache, double-check it against
1157          *      what is in the file.
1158          */
1159         if (node) {
1160                 /*
1161                  *      If we didn't read anything, then this entry
1162                  *      doesn't exist.
1163                  *
1164                  *      Similarly, if the entry in the file doesn't
1165                  *      match what we recall, then nuke the cache
1166                  *      entry.
1167                  */
1168                 read_size = read(fd, &u, sizeof(u));
1169                 if ((read_size < 0) ||
1170                     ((read_size > 0) && (read_size  != sizeof(u)))) {
1171                         /*
1172                          *      Bad read, or bad record.
1173                          */
1174                         radlog(L_ERR, "rlm_radutmp: Badly formed file %s",
1175                                cache->filename);
1176                         close(fd);
1177                         return RLM_MODULE_FAIL;
1178                 }
1179
1180                 rad_assert(read_size != 0);
1181
1182                 /*
1183                  *      We've read a record, go poke at it.
1184                  */
1185                 if (read_size > 0) {
1186                         /*
1187                          *      If these aren't true, then
1188                          *
1189                          *      a) we have cached a "logout" entry,
1190                          *         which we don't do.
1191                          *
1192                          *      b) we have cached the wrong NAS address
1193                          *
1194                          *      c) we have cached the wrong NAS port.
1195                          */
1196                         rad_assert(u.type == P_LOGIN);
1197                         rad_assert(u.nas_address == utmp.nas_address);
1198                         rad_assert(u.nas_port == utmp.nas_port);
1199                         
1200                         /*
1201                          *      An update for the same session.
1202                          */
1203                         if (strncmp(utmp.session_id, u.session_id,
1204                                     sizeof(u.session_id)) == 0) {
1205
1206                                 /*
1207                                  *      It's a duplicate start, so we
1208                                  *      don't bother writing it.
1209                                  */
1210                                 if (status == PW_STATUS_START) {
1211                                         DEBUG2("  rlm_radutmp: Login entry for NAS %s port %u duplicate, ignoring it.",
1212                                                nas, u.nas_port);
1213                                         close(fd);
1214                                         return RLM_MODULE_OK;
1215
1216
1217                                 /*
1218                                  *      ALIVE for this session, keep the
1219                                  *      original login time.
1220                                  */
1221                                 } else if (status == PW_STATUS_ALIVE) {
1222                                         utmp.time = u.time;
1223
1224                                 /*
1225                                  *      Stop: delete it from our cache.
1226                                  */
1227                                 } else if (status == PW_STATUS_STOP) {
1228                                         radutmp_simul_t *user, myUser;
1229
1230                                         pthread_mutex_lock(&cache->mutex);
1231                                         rbtree_deletebydata(cache->nas_ports,
1232                                                             nas_port);
1233
1234                                         strlcpy(myUser.login,
1235                                                 u.login, sizeof(myUser.login));
1236                                         user = rbtree_finddata(inst->user_tree,
1237                                                                &myUser);
1238                                         rad_assert(user != NULL);
1239                                         rad_assert(user->simul_count > 0);
1240
1241                                         user->simul_count--;
1242                                         if (user->simul_count == 0) {
1243                                                 rbtree_deletebydata(inst->user_tree, user);
1244                                         }
1245
1246                                         pthread_mutex_unlock(&cache->mutex);
1247
1248                                 } else {
1249                                         /*
1250                                          *      We don't know how to
1251                                          *      handle this.
1252                                          */
1253                                         rad_assert(0 == 1);
1254                                 }
1255
1256                         } else { /* session ID doesn't match */
1257                                 /*
1258                                  *      STOP for the right NAS & port,
1259                                  *      but the Acct-Session-Id is
1260                                  *      different.  This means that
1261                                  *      we missed the original "stop",
1262                                  *      and a new "start".
1263                                  */
1264                                 if (status == PW_STATUS_STOP) {
1265                                         radlog(L_ERR, "rlm_radutmp: Logout entry for NAS %s port %u has old Acct-Session-ID, ignoring it.",
1266                                                nas, u.nas_port);
1267                                         close(fd);
1268                                         return RLM_MODULE_OK;
1269                                 }
1270                         } /* checked session ID's */
1271                 }  /* else we haven't read anything from the file. */
1272         } /* else the entry wasn't cached, but could have been inserted */
1273
1274         /*
1275          *      Hmm... we may have received a start or alive packet
1276          *      AFTER a stop or nas-down, in that case, we want to
1277          *      discard the new packet.  However, the original code
1278          *      could over-write an idle record with a new login
1279          *      record for another NAS && port, so we won't worry
1280          *      about this case too much.
1281          */
1282
1283         /*
1284          *      Seek to where the entry is, and write it blindly.
1285          */
1286         lseek(fd, nas_port->offset, SEEK_SET); /* FIXME: err */
1287
1288         if (status != PW_STATUS_STOP) {
1289                 utmp.type = P_LOGIN;
1290                 rad_assert(nas_port != NULL); /* it WAS cached */
1291         } else {
1292                 /* FIXME: maybe assert that the entry was deleted... */
1293                 memcpy(&utmp, &u, sizeof(utmp));
1294                 utmp.type = P_IDLE;
1295         }
1296
1297         write(fd, &utmp, sizeof(utmp)); /* FIXME: err */
1298
1299         close(fd);      /* and implicitly release the locks */
1300
1301         return RLM_MODULE_OK;
1302 }
1303
1304 /*
1305  *      See if a user is already logged in. Sets request->simul_count
1306  *      to the current session count for this user and sets
1307  *      request->simul_mpp to 2 if it looks like a multilink attempt
1308  *      based on the requested IP address, otherwise leaves
1309  *      request->simul_mpp alone.
1310  *
1311  *      Check twice. If on the first pass the user exceeds his
1312  *      max. number of logins, do a second pass and validate all
1313  *      logins by querying the terminal server (using eg. SNMP).
1314  */
1315 static int radutmp_checksimul(void *instance, REQUEST *request)
1316 {
1317         struct radutmp  u;
1318         int             fd;
1319         VALUE_PAIR      *vp;
1320         uint32_t        ipno = 0;
1321         char            *call_num = NULL;
1322         int             rcode;
1323         rlm_radutmp_t   *inst = instance;
1324         char            login[256];
1325         char            filename[1024];
1326         radutmp_cache_t *cache;
1327         radutmp_simul_t *user, myUser;
1328
1329         /*
1330          *      Get the filename, via xlat.
1331          */
1332         radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
1333
1334         /*
1335          *      Future: look up filename in filename tree, to get
1336          *      radutmp_cache_t pointer
1337          */
1338         cache = &inst->cache;
1339
1340         /*
1341          *      For now, double-check the filename, to be sure it isn't
1342          *      changing.
1343          */
1344         if (!cache->filename) {
1345                 cache->filename = strdup(filename);
1346                 rad_assert(cache->filename != NULL);
1347
1348         } else if (strcmp(cache->filename, filename) != 0) {
1349                 radlog(L_ERR, "rlm_radutmp: We do not support dynamically named files.");
1350                 return RLM_MODULE_FAIL;
1351         }
1352
1353         *login = '\0';
1354         radius_xlat(login, sizeof(login), inst->username, request, NULL);
1355         if (!*login) {
1356                 return RLM_MODULE_NOOP;
1357         }
1358
1359         /*
1360          *      WTF?  This is probably wrong... we probably want to
1361          *      be able to check users across multiple session accounting
1362          *      methods.
1363          */
1364         request->simul_count = 0;
1365
1366         strlcpy(myUser.login, login, sizeof(myUser.login));
1367         pthread_mutex_lock(&inst->cache.mutex);
1368         user = rbtree_finddata(inst->user_tree, &myUser);
1369         if (user) request->simul_count = user->simul_count;
1370         user = NULL;            /* someone else may delete it */
1371         pthread_mutex_unlock(&inst->cache.mutex);
1372
1373         /*
1374          *      The number of users logged in is OK,
1375          *      OR, we've been told to not check the NAS.
1376          */
1377         if ((request->simul_count < request->simul_max) ||
1378             !inst->check_nas) {
1379                 return RLM_MODULE_OK;
1380         }
1381
1382         /*
1383          *      The user is logged in at least N times, and
1384          *      we're told to check the NAS.  In that case,
1385          *      we've got to read the file, and check each
1386          *      NAS port by hand.
1387          */
1388         if ((fd = open(cache->filename, O_RDWR)) < 0) {
1389                 /*
1390                  *      If the file doesn't exist, then no users
1391                  *      are logged in.
1392                  */
1393                 if (errno == ENOENT) {
1394                         request->simul_count = 0;
1395                         return RLM_MODULE_OK;
1396                 }
1397
1398                 /*
1399                  *      Error accessing the file.
1400                  */
1401                 radlog(L_ERR, "rlm_radumtp: Error accessing file %s: %s",
1402                        cache->filename, strerror(errno));
1403                 return RLM_MODULE_FAIL;
1404         }
1405
1406         /*
1407          *      Setup some stuff, like for MPP detection.
1408          */
1409         if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
1410                 ipno = vp->lvalue;
1411         if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
1412                 call_num = vp->vp_strvalue;
1413
1414         /*
1415          *      lock the file while reading/writing.
1416          */
1417         rad_lockfd(fd, LOCK_LEN);
1418
1419         /*
1420          *      FIXME: If we get a 'Start' for a user/nas/port which is
1421          *      listed, but for which we did NOT get a 'Stop', then
1422          *      it's not a duplicate session.  This happens with
1423          *      static IP's like DSL.
1424          */
1425         request->simul_count = 0;
1426         while (read(fd, &u, sizeof(u)) == sizeof(u)) {
1427                 if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
1428                      (!inst->case_sensitive &&
1429                       (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
1430                     (u.type == P_LOGIN)) {
1431                         char session_id[sizeof(u.session_id) + 1];
1432                         char utmp_login[sizeof(u.login) + 1];
1433
1434                         strlcpy(session_id, u.session_id, sizeof(session_id));
1435
1436                         /*
1437                          *      The login name MAY fill the whole field,
1438                          *      and thus won't be zero-filled.
1439                          *
1440                          *      Note that we take the user name from
1441                          *      the utmp file, as that's the canonical
1442                          *      form.  The 'login' variable may contain
1443                          *      a string which is an upper/lowercase
1444                          *      version of u.login.  When we call the
1445                          *      routine to check the terminal server,
1446                          *      the NAS may be case sensitive.
1447                          *
1448                          *      e.g. We ask if "bob" is using a port,
1449                          *      and the NAS says "no", because "BOB"
1450                          *      is using the port.
1451                          */
1452                         strlcpy(utmp_login, u.login, sizeof(u.login));
1453
1454                         /*
1455                          *      rad_check_ts may take seconds
1456                          *      to return, and we don't want
1457                          *      to block everyone else while
1458                          *      that's happening.  */
1459                         rad_unlockfd(fd, LOCK_LEN);
1460                         rcode = rad_check_ts(u.nas_address, u.nas_port,
1461                                              utmp_login, session_id);
1462                         rad_lockfd(fd, LOCK_LEN);
1463
1464                         if (rcode == 0) {
1465                                 /*
1466                                  *      Stale record - zap it.
1467                                  *
1468                                  *      Hmm... this ends up calling
1469                                  *      the accounting section
1470                                  *      recursively...
1471                                  */
1472                                 session_zap(request, u.nas_address,
1473                                             u.nas_port, login, session_id,
1474                                             u.framed_address, u.proto,0);
1475                         }
1476                         else if (rcode == 1) {
1477                                 /*
1478                                  *      User is still logged in.
1479                                  */
1480                                 ++request->simul_count;
1481
1482                                 /*
1483                                  *      Does it look like a MPP attempt?
1484                                  */
1485                                 if (strchr("SCPA", u.proto) &&
1486                                     ipno && u.framed_address == ipno)
1487                                         request->simul_mpp = 2;
1488                                 else if (strchr("SCPA", u.proto) && call_num &&
1489                                         !strncmp(u.caller_id,call_num,16))
1490                                         request->simul_mpp = 2;
1491                         }
1492                         else {
1493                                 /*
1494                                  *      Failed to check the terminal
1495                                  *      server for duplicate logins:
1496                                  *      Return an error.
1497                                  */
1498                                 close(fd);
1499                                 radlog(L_ERR, "rlm_radutmp: Failed to check the terminal server for user '%s'.", utmp_login);
1500                                 return RLM_MODULE_FAIL;
1501                         }
1502                 }
1503         }
1504         close(fd);              /* and implicitly release the locks */
1505
1506         return RLM_MODULE_OK;
1507 }
1508
1509 /* globally exported name */
1510 module_t rlm_radutmp = {
1511   "radutmp",
1512   0,                            /* type: reserved */
1513   NULL,                         /* initialization */
1514   radutmp_instantiate,          /* instantiation */
1515   {
1516           NULL,                 /* authentication */
1517           NULL,                 /* authorization */
1518           NULL,                 /* preaccounting */
1519           radutmp_accounting,   /* accounting */
1520           radutmp_checksimul,   /* checksimul */
1521           NULL,                 /* pre-proxy */
1522           NULL,                 /* post-proxy */
1523           NULL                  /* post-auth */
1524   },
1525   radutmp_detach,               /* detach */
1526   NULL,                         /* destroy */
1527 };
1528