Ok, it works now with the new connection pool. Helps to initalize our mutexes first.
[freeradius.git] / src / modules / rlm_sql / sql.c
1 /*
2  *  sql.c               rlm_sql - FreeRADIUS SQL Module
3  *              Main code directly taken from ICRADIUS
4  *
5  * Version:     $Id$
6  *
7  *   This program is free software; you can redistribute it and/or modify
8  *   it under the terms of the GNU General Public License as published by
9  *   the Free Software Foundation; either version 2 of the License, or
10  *   (at your option) any later version.
11  *
12  *   This program is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with this program; if not, write to the Free Software
19  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * Copyright 2000  The FreeRADIUS server project
22  * Copyright 2000  Mike Machado <mike@innercite.com>
23  * Copyright 2000  Alan DeKok <aland@ox.org>
24  */
25
26
27 #include        <sys/types.h>
28 #include        <sys/socket.h>
29 #include        <sys/time.h>
30 #include        <sys/file.h>
31 #include        <string.h>
32 #include        <sys/stat.h>
33 #include        <netinet/in.h>
34
35 #include        <stdio.h>
36 #include        <stdlib.h>
37 #include        <netdb.h>
38 #include        <pwd.h>
39 #include        <time.h>
40 #include        <ctype.h>
41 #include        <unistd.h>
42 #include        <signal.h>
43 #include        <errno.h>
44 #include        <sys/wait.h>
45
46 #if HAVE_PTHREAD_H
47 #include        <pthread.h>
48 #endif
49
50 #include        "radiusd.h"
51 #include        "conffile.h"
52 #include        "rlm_sql.h"
53
54 /*************************************************************************
55  *
56  *      Function: sql_init_socket
57  *
58  *      Purpose: Connect to the sql server
59  *
60  *************************************************************************/
61 int sql_init_socketpool(SQL_INST *inst) {
62
63         SQLSOCK *sqlsocket;
64         int     i;
65
66         inst->used = 0;
67         inst->sqlpool = NULL;
68
69         for (i = 0; i < inst->config->num_sql_socks; i++) {
70                 if ((sqlsocket = sql_create_socket(inst)) == NULL) {
71                         radlog(L_CONS | L_ERR, "rlm_sql:  Failed to connect sqlsocket %d", i);
72                         return -1;
73                 } else {
74                         sqlsocket->id = i;
75 #if HAVE_PTHREAD_H
76                         sqlsocket->semaphore = (sem_t *)malloc(sizeof(sem_t));
77                         sem_init(sqlsocket->semaphore, 0, SQLSOCK_UNLOCKED);
78 #else
79                         sqlsocket->in_use = 0;
80 #endif
81                         sqlsocket->next = inst->sqlpool;
82                         inst->sqlpool = sqlsocket;
83                 }
84         }
85
86         return 1;
87 }
88
89 /*************************************************************************
90  *
91  *     Function: sql_poolfree
92  *
93  *     Purpose: Clean up and free sql pool
94  *
95  *************************************************************************/
96 void sql_poolfree(SQL_INST *inst) {
97
98         SQLSOCK *cur;
99
100         for (cur = inst->sqlpool; cur; cur = cur->next) {
101                 sql_close_socket(cur);
102         }
103 }
104
105
106 /*************************************************************************
107  *
108  *      Function: sql_close_socket
109  *
110  *      Purpose: Close and free a sql sqlsocket
111  *
112  *************************************************************************/
113 int sql_close_socket(SQLSOCK *sqlsocket) {
114
115         radlog(L_DBG,"rlm_sql: Closing sqlsocket %d", sqlsocket->id);
116         sql_close(sqlsocket);
117         free(sqlsocket);
118         return 1;
119 }
120
121
122 /*************************************************************************
123  *
124  *      Function: sql_get_socket
125  *
126  *      Purpose: Return a SQL sqlsocket from the connection pool           
127  *
128  *************************************************************************/
129 SQLSOCK *sql_get_socket(SQL_INST *inst) {
130
131
132         SQLSOCK *cur;
133
134 #if HAVE_PTHREAD_H
135         pthread_mutex_lock(inst->lock);
136 #endif
137         while (inst->used == inst->config->num_sql_socks) {
138                 radlog(L_DBG, "rlm_sql: Waiting for open sql socket");
139 #if HAVE_PTHREAD_H
140                 pthread_cond_wait(inst->notfull, inst->lock);
141 #else
142                 /* FIXME: Subsecond sleep needed here */
143                 sleep(1);
144 #endif
145         }
146
147         for (cur = inst->sqlpool; cur; cur = cur->next) {
148 #if HAVE_PTHREAD_H
149                 if (sem_trywait(cur->semaphore) == 0) {
150 #else
151                 if (cur->in_use == SQLSOCK_UNLOCKED) {
152 #endif
153                         (inst->used)++;
154 #if HAVE_PTHREAD_H
155                         pthread_mutex_unlock(inst->lock);
156 #else
157                         cur->in_use = SQLSOCK_LOCKED;
158 #endif
159                         radlog(L_DBG,"rlm_sql: Reserved sql socket id: %d", cur->id);
160                         return cur;
161                 }
162         }
163
164 #if HAVE_PTHREAD_H
165         pthread_mutex_unlock(inst->lock);
166 #endif
167
168         /* Should never get here, but what the hey */
169         return NULL;
170 }
171
172 /*************************************************************************
173  *
174  *      Function: sql_release_socket
175  *
176  *      Purpose: Frees a SQL sqlsocket back to the connection pool           
177  *
178  *************************************************************************/
179 int sql_release_socket(SQL_INST *inst, SQLSOCK *sqlsocket) {
180
181         struct timeval tv;
182         double  start, end;
183         char    buff[24];
184
185         gettimeofday(&tv, NULL);
186         sprintf(buff, "%ld.%2ld", tv.tv_sec, tv.tv_usec);
187         end = strtod(buff, NULL);
188         sprintf(buff, "%ld %2.0ld", sqlsocket->tv.tv_sec, sqlsocket->tv.tv_usec);
189         start = strtod(buff, NULL);
190         radlog(L_DBG,"rlm_sql: Socket %d used for %.2f seconds", sqlsocket->id, end - start);
191
192         sqlsocket->tv.tv_sec = tv.tv_sec;
193         sqlsocket->tv.tv_usec = tv.tv_usec;
194
195 #if HAVE_PTHREAD_H
196         pthread_mutex_lock(inst->lock);
197 #endif
198         (inst->used)--;
199 #if HAVE_PTHREAD_H
200         sem_post(sqlsocket->semaphore);
201 #else
202         sqlsocket->in_use = 0;
203 #endif
204
205         radlog(L_DBG,"rlm_sql: Released sql socket id: %d", sqlsocket->id);
206
207 #if HAVE_PTHREAD_H
208         pthread_mutex_unlock(inst->lock);
209         pthread_cond_signal(inst->notfull);
210 #endif
211
212         return 1;
213 }
214
215
216 /*************************************************************************
217  *
218  *      Function: sql_save_acct
219  *
220  *      Purpose: Write data from the sqlrecord structure to the database
221  *
222  *************************************************************************/
223
224 int sql_save_acct(SQL_INST *inst, SQLSOCK *sqlsocket, SQLACCTREC *sqlrecord) {
225
226         char    querystr[2048];
227         FILE   *sqlfile=0;
228         int     num = 0;
229         int                     acctunique = 0;
230
231 #ifdef NT_DOMAIN_HACK
232         char   *ptr;
233         char    newname[AUTH_STRING_LEN];
234 #endif
235
236         acctunique = strlen(sqlrecord->AcctUniqueId);
237
238         if(inst->config->sqltrace) {
239                 if ((sqlfile = fopen(inst->config->tracefile, "a")) == (FILE *) NULL) {
240                         radlog(L_ERR, "rlm_sql: Couldn't open file %s", inst->config->tracefile);
241                 } else {
242 #if defined(F_LOCK) && !defined(BSD)
243                         (void) lockf((int) sqlfile, (int) F_LOCK, (off_t) SQL_LOCK_LEN);
244 #else
245                         (void) flock(sqlfile, SQL_LOCK_EX);
246 #endif
247                 }
248         }
249
250 #ifdef NT_DOMAIN_HACK
251         /*
252          *      Windows NT machines often authenticate themselves as
253          *      NT_DOMAIN\username. Try to be smart about this.
254          *
255          *      FIXME: should we handle this as a REALM ?
256          */
257         if ((ptr = strchr(sqlrecord->UserName, '\\')) != NULL) {
258                 strncpy(newname, ptr + 1, sizeof(newname));
259                 newname[sizeof(newname) - 1] = 0;
260                 strcpy(sqlrecord->UserName, newname);
261         }
262 #endif /*
263                           * NT_DOMAIN_HACK 
264                           */
265
266         if (sqlrecord->AcctStatusTypeId == PW_STATUS_ACCOUNTING_ON ||
267                         sqlrecord->AcctStatusTypeId == PW_STATUS_ACCOUNTING_OFF) {
268                 radlog(L_INFO, "rlm_sql:  Portmaster %s rebooted at %s", sqlrecord->NASIPAddress,
269                                          sqlrecord->AcctTimeStamp);
270
271                 /*
272                  * The Terminal server informed us that it was rebooted
273                  * * STOP all records from this NAS 
274                  */
275
276                 sprintf(querystr,
277                                                 "UPDATE %s SET AcctStopTime='%s', AcctSessionTime=unix_timestamp('%s') - unix_timestamp(AcctStartTime), AcctTerminateCause='%s', AcctStopDelay = %ld WHERE AcctSessionTime=0 AND AcctStopTime=0 AND NASIPAddress= '%s' AND AcctStartTime <= '%s'",
278                                                 inst->config->sql_acct_table, sqlrecord->AcctTimeStamp,
279                                                 sqlrecord->AcctTimeStamp, sqlrecord->AcctTerminateCause,
280                                                 sqlrecord->AcctDelayTime, sqlrecord->NASIPAddress,
281                                                 sqlrecord->AcctTimeStamp);
282
283                 if (sql_query(inst, sqlsocket, querystr) < 0)
284                         radlog(L_ERR, "rlm_sql: Couldn't update SQL accounting after NAS reboot - %s",
285                                                  sql_error(sqlsocket));
286                 sql_finish_query(sqlsocket);
287
288                 if (sqlfile) {
289                         fputs(querystr, sqlfile);
290                         fputs(";\n", sqlfile);
291                         fclose(sqlfile);
292                 }
293                 return 0;
294         }
295
296         if (sqlrecord->AcctStatusTypeId == PW_STATUS_ALIVE) {
297                 /* 
298                  * Use acct unique session identifier if present
299                  */
300                 if(acctunique) { 
301                         sprintf(querystr, "UPDATE %s SET FramedIPAddress = '%s' WHERE AcctUniqueId = '%s'",
302                                                         inst->config->sql_acct_table, sqlrecord->FramedIPAddress,
303                                                         sqlrecord->AcctUniqueId);
304
305                 } else {
306                         sprintf(querystr, "UPDATE %s SET FramedIPAddress = '%s' WHERE AcctSessionId = '%s' AND UserName = '%s' AND NASIPAddress= '%s'",
307                                                         inst->config->sql_acct_table, sqlrecord->FramedIPAddress,
308                                                         sqlrecord->AcctSessionId, sqlrecord->UserName,
309                                                         sqlrecord->NASIPAddress);
310                 }
311
312                 if (sql_query(inst, sqlsocket, querystr) < 0)
313                         radlog(L_ERR, "rlm_sql: Couldn't update SQL accounting for ALIVE packet - %s",
314                                                  sql_error(sqlsocket));
315                 sql_finish_query(sqlsocket);
316
317                 if (sqlfile) {
318                         fputs(querystr, sqlfile);
319                         fputs(";\n", sqlfile);
320                         fclose(sqlfile);
321                 }
322                 return 0;
323         }
324
325         /*
326          * Got start record 
327          */
328         if (sqlrecord->AcctStatusTypeId == PW_STATUS_START) {
329
330                 /*
331                  * Insert new record with blank stop time until stop record is got 
332                  */
333                 snprintf(querystr, 2048,
334                                                  "INSERT INTO %s VALUES (0, '%s', '%s', '%s', '%s', '%s', %ld, '%s', '%s', 0, 0, '%s', '%s', '', 0, 0, '%s', '%s', '', '%s', '%s', '%s', %ld, 0)",
335                                                  inst->config->sql_acct_table, sqlrecord->AcctSessionId, 
336                                                  sqlrecord->AcctUniqueId, sqlrecord->UserName, 
337                                                  sqlrecord->Realm, sqlrecord->NASIPAddress,
338                                                  sqlrecord->NASPortId, sqlrecord->NASPortType,
339                                                  sqlrecord->AcctTimeStamp, sqlrecord->AcctAuthentic,
340                                                  sqlrecord->ConnectInfo, sqlrecord->CalledStationId,
341                                                  sqlrecord->CallingStationId, sqlrecord->ServiceType,
342                                                  sqlrecord->FramedProtocol, sqlrecord->FramedIPAddress,
343                                                  sqlrecord->AcctDelayTime);
344
345                 if (sql_query(inst, sqlsocket, querystr) < 0) {
346                         radlog(L_ERR, "rlm_sql: Couldn't insert SQL accounting START record - %s",
347                                                          sql_error(sqlsocket));
348
349                         /*
350                          * We failed the insert above.  It's probably because 
351                          * the stop record came before the start.  We try an
352                          * update here to be sure
353                          */
354                         if(acctunique) {
355                                 snprintf(querystr, 2048, "UPDATE %s SET AcctStartTime = '%s', AcctStartDelay = %ld, ConnectInfo_start = '%s' WHERE AcctUniqueId = '%s'",
356                                                                  inst->config->sql_acct_table, sqlrecord->AcctTimeStamp,
357                                                                  sqlrecord->AcctDelayTime, sqlrecord->ConnectInfo,
358                                                                  sqlrecord->AcctUniqueId);
359                         } else {
360                                 snprintf(querystr, 2048, "UPDATE %s SET AcctStartTime = '%s', AcctStartDelay = %ld, ConnectInfo_start = '%s' WHERE AcctSessionId = '%s' AND UserName = '%s' AND NASIPAddress = '%s'",
361                                                                  inst->config->sql_acct_table, sqlrecord->AcctTimeStamp,
362                                                                  sqlrecord->AcctDelayTime, sqlrecord->ConnectInfo,
363                                                                  sqlrecord->AcctSessionId, sqlrecord->UserName, 
364                                                                  sqlrecord->NASIPAddress);
365                         }
366                         if (sql_query(inst, sqlsocket, querystr) < 0)
367                                 radlog(L_ERR, "rlm_sql: Couldn't update SQL accounting START record - %s",
368                                                          sql_error(sqlsocket));
369
370                 } 
371                 sql_finish_query(sqlsocket);
372
373                 /*
374                  * Got stop record 
375                  */
376         } else {
377
378                 /*
379                  * Set stop time on matching record with start time 
380                  */
381                 if(acctunique) {
382                         snprintf(querystr, 2048,
383                                                          "UPDATE %s SET AcctStopTime = '%s', AcctSessionTime = '%lu', AcctInputOctets = '%lu', AcctOutputOctets = '%lu', AcctTerminateCause = '%s', AcctStopDelay = %ld, ConnectInfo_stop = '%s' WHERE AcctUniqueId = '%s'",
384                                                          inst->config->sql_acct_table, sqlrecord->AcctTimeStamp,
385                                                          sqlrecord->AcctSessionTime, sqlrecord->AcctInputOctets,
386                                                          sqlrecord->AcctOutputOctets, sqlrecord->AcctTerminateCause,
387                                                          sqlrecord->AcctDelayTime, sqlrecord->ConnectInfo,
388                                                          sqlrecord->AcctUniqueId);
389
390                 } else {
391                         snprintf(querystr, 2048,
392                                                          "UPDATE %s SET AcctStopTime = '%s', AcctSessionTime = '%lu', AcctInputOctets = '%lu', AcctOutputOctets = '%lu', AcctTerminateCause = '%s', AcctStopDelay = %ld, ConnectInfo_stop = '%s' WHERE AcctSessionId = '%s' AND UserName = '%s' AND NASIPAddress = '%s'",
393                                                          inst->config->sql_acct_table, sqlrecord->AcctTimeStamp,
394                                                          sqlrecord->AcctSessionTime, sqlrecord->AcctInputOctets,
395                                                          sqlrecord->AcctOutputOctets, sqlrecord->AcctTerminateCause,
396                                                          sqlrecord->AcctDelayTime, sqlrecord->ConnectInfo,
397                                                          sqlrecord->AcctSessionId, sqlrecord->UserName, 
398                                                          sqlrecord->NASIPAddress);
399                 }
400
401
402                 if (sql_query(inst, sqlsocket, querystr) < 0)
403                         radlog(L_ERR, "rlm_sql: Couldn't update SQL accounting STOP record - %s",
404                                                  sql_error(sqlsocket));
405                 sql_finish_query(sqlsocket);
406
407
408                 /* 
409                  * If our update above didn't match anything
410                  * we assume it's because we haven't seen a 
411                  * matching Start record.  So we have to
412                  * insert this stop rather than do an update
413                  */
414                 num = sql_affected_rows(sqlsocket);
415                 if(num < 1) {
416
417 #ifdef CISCO_ACCOUNTING_HACK
418                         /*
419                          * If stop but zero session length AND no previous 
420                          * session found, drop it as in invalid packet 
421                          * This is to fix CISCO's aaa from filling our 
422                          * table with bogus crap 
423                          */
424                         if (sqlrecord->AcctSessionTime <= 0) {
425                                 radlog(L_ERR, "rlm_sql: Invalid STOP record. [%s] STOP record but zero session length? (nas %s)",
426                                                          sqlrecord->UserName, sqlrecord->NASIPAddress);
427                                 return 0;
428                         }
429 #endif
430
431                         /*
432                          * Insert record with no start time until matching start record comes 
433                          */
434                         snprintf(querystr, 2048,
435                                                          "INSERT INTO %s VALUES (0, '%s', '%s', '%s', '%s', '%s', %ld, '%s', 0, '%s', '%lu', '%s', '', '%s', '%lu', '%lu', '%s', '%s', '%s', '%s', '%s', '%s', 0, %ld)",
436                                                          inst->config->sql_acct_table, sqlrecord->AcctSessionId,
437                                                          sqlrecord->AcctUniqueId, sqlrecord->UserName, 
438                                                          sqlrecord->Realm, sqlrecord->NASIPAddress,
439                                                          sqlrecord->NASPortId, sqlrecord->NASPortType,
440                                                          sqlrecord->AcctTimeStamp, sqlrecord->AcctSessionTime,
441                                                          sqlrecord->AcctAuthentic, sqlrecord->ConnectInfo,
442                                                          sqlrecord->AcctInputOctets, sqlrecord->AcctOutputOctets,
443                                                          sqlrecord->CalledStationId, sqlrecord->CallingStationId,
444                                                          sqlrecord->AcctTerminateCause, sqlrecord->ServiceType,
445                                                          sqlrecord->FramedProtocol, sqlrecord->FramedIPAddress,
446                                                          sqlrecord->AcctDelayTime);
447
448                         if (sql_query(inst, sqlsocket, querystr) < 0)
449                                 radlog(L_ERR, "rlm_sql: Couldn't insert SQL accounting STOP record - %s",
450                                                          sql_error(sqlsocket));
451                         sql_finish_query(sqlsocket);
452                 }
453
454         }
455         if (sqlfile) {
456                 fputs(querystr, sqlfile);
457                 fputs(";\n", sqlfile);
458                 fflush(sqlfile);
459                 fclose(sqlfile);
460         }
461
462         return 0;
463
464 }
465
466
467 /*************************************************************************
468  *
469  *      Function: sql_userparse
470  *
471  *      Purpose: Read entries from the database and fill VALUE_PAIR structures
472  *
473  *************************************************************************/
474 int sql_userparse(VALUE_PAIR ** first_pair, SQL_ROW row, int mode) {
475
476         DICT_ATTR *attr;
477         VALUE_PAIR *pair, *check;
478
479
480         if ((attr = dict_attrbyname(row[2])) == (DICT_ATTR *) NULL) {
481                 radlog(L_ERR | L_CONS, "rlm_sql: unknown attribute %s", row[2]);
482                 return (-1);
483         }
484
485         /*
486          * If attribute is already there, skip it because we checked usercheck first 
487          * and we want user settings to over ride group settings 
488          */
489         if ((check = pairfind(*first_pair, attr->attr)) != NULL &&
490 #if defined( BINARY_FILTERS )
491                         attr->type != PW_TYPE_ABINARY &&
492 #endif
493                         mode == PW_VP_GROUPDATA) return 0;
494
495         pair = pairmake(row[2], row[3], T_OP_CMP_EQ);
496         pairadd(first_pair, pair);
497
498         return 0;
499 }
500
501
502 /*************************************************************************
503  *
504  *      Function: sql_getvpdata
505  *
506  *      Purpose: Get any group check or reply pairs
507  *
508  *************************************************************************/
509 int sql_getvpdata(SQL_INST *inst, SQLSOCK *sqlsocket, char *table, VALUE_PAIR ** vp, char *user, int mode)
510 {
511
512         char    querystr[256];
513         char    authstr[256];
514         char    username[AUTH_STRING_LEN * 2 + 1];
515         SQL_ROW row;
516         int     rows;
517         int     length;
518
519         if (strlen(user) > AUTH_STRING_LEN)
520                 length = AUTH_STRING_LEN;
521         else
522                 length = strlen(user);
523
524         /*
525          * FIXME CHECK user for weird charactors!! 
526          */
527         sql_escape_string(username, user, length);
528
529         if (mode == PW_VP_USERDATA) {
530                 if (inst->config->sensitiveusername)
531                         sprintf(authstr, "STRCMP(Username, '%s') = 0", username);
532                 else
533                         sprintf(authstr, "UserName = '%s'", username);
534                 sprintf(querystr, "SELECT * FROM %s WHERE %s ORDER BY id", table,
535                                                 authstr);
536         } else if (mode == PW_VP_GROUPDATA) {
537                 if (inst->config->sensitiveusername)
538                         sprintf(authstr, "STRCMP(%s.Username, '%s') = 0",
539                                                         inst->config->sql_usergroup_table, username);
540                 else
541                         sprintf(authstr, "%s.UserName = '%s'", inst->config->sql_usergroup_table,
542                                                         username);
543                 sprintf(querystr,
544                                                 "SELECT %s.* FROM %s, %s WHERE %s AND %s.GroupName = %s.GroupName ORDER BY %s.id",
545                                                 table, table, inst->config->sql_usergroup_table, authstr,
546                                                 inst->config->sql_usergroup_table, table, table);
547         } else if (mode == PW_VP_REALMDATA)
548                 sprintf(querystr,
549                                                 "SELECT %s.* FROM %s, %s WHERE %s.RealmName = '%s' AND %s.GroupName = %s.GroupName ORDER BY %s.id",
550                                                 table, table, inst->config->sql_realmgroup_table,
551                                                 inst->config->sql_realmgroup_table, username,
552                                                 inst->config->sql_realmgroup_table, table, table);
553         sql_select_query(inst, sqlsocket, querystr);
554         rows = sql_num_rows(sqlsocket);
555         while ((row = sql_fetch_row(sqlsocket))) {
556
557                 if (sql_userparse(vp, row, mode) != 0) {
558                         radlog(L_ERR | L_CONS, "rlm_sql:  Error getting data from database");
559                         sql_finish_select_query(sqlsocket);
560                         return -1;
561                 }
562         }
563         sql_finish_select_query(sqlsocket);
564
565         return rows;
566
567 }
568
569
570 static int got_alrm;
571 static void
572 alrm_handler()
573 {
574         got_alrm = 1;
575 }
576
577 /*************************************************************************
578  *
579  *      Function: sql_check_ts
580  *
581  *      Purpose: Checks the terminal server for a spacific login entry
582  *
583  *************************************************************************/
584 static int sql_check_ts(SQL_ROW row) {
585
586         int     pid, st, e;
587         int     n;
588         NAS    *nas;
589         char    session_id[12];
590         char   *s;
591         void    (*handler) (int);
592
593         /*
594          *      Find NAS type.
595          */
596         if ((nas = nas_find(ip_addr(row[4]))) == NULL) {
597                 radlog(L_ERR, "rlm_sql:  unknown NAS [%s]", row[4]);
598                 return -1;
599         }
600
601         /*
602          *      Fork.
603          */
604         handler = signal(SIGCHLD, SIG_DFL);
605         if ((pid = fork()) < 0) {
606                 radlog(L_ERR, "rlm_sql: fork: %s", strerror(errno));
607                 signal(SIGCHLD, handler);
608                 return -1;
609         }
610
611         if (pid > 0) {
612                 /*
613                  *      Parent - Wait for checkrad to terminate.
614                  *      We timeout in 10 seconds.
615                  */
616                 got_alrm = 0;
617                 signal(SIGALRM, alrm_handler);
618                 alarm(10);
619                 while ((e = waitpid(pid, &st, 0)) != pid)
620                         if (e < 0 && (errno != EINTR || got_alrm))
621                                 break;
622                 alarm(0);
623                 signal(SIGCHLD, handler);
624                 if (got_alrm) {
625                         kill(pid, SIGTERM);
626                         sleep(1);
627                         kill(pid, SIGKILL);
628                         radlog(L_ERR, "rlm_sql:  Check-TS: timeout waiting for checkrad");
629                         return 2;
630                 }
631                 if (e < 0) {
632                         radlog(L_ERR, "rlm_sql:  Check-TS: unknown error in waitpid()");
633                         return 2;
634                 }
635                 return WEXITSTATUS(st);
636         }
637
638         /*
639          *      Child - exec checklogin with the right parameters.
640          */
641         for (n = 32; n >= 3; n--)
642                 close(n);
643
644         sprintf(session_id, "%.8s", row[1]);
645
646         s = CHECKRAD2;
647         execl(CHECKRAD2, "checkrad", nas->nastype, row[4], row[5],
648                                 row[2], session_id, NULL);
649         if (errno == ENOENT) {
650                 s = CHECKRAD1;
651                 execl(CHECKRAD1, "checklogin", nas->nastype, row[4], row[5],
652                                         row[2], session_id, NULL);
653         }
654         radlog(L_ERR, "rlm_sql:  Check-TS: exec %s: %s", s, strerror(errno));
655
656         /*
657          *      Exit - 2 means "some error occured".
658          */
659         exit(2);
660
661 }
662
663
664 /*************************************************************************
665  *
666  *      Function: sql_check_multi
667  *
668  *      Purpose: Check radius accounting for duplicate logins
669  *
670  *************************************************************************/
671 int sql_check_multi(SQL_INST *inst, SQLSOCK *sqlsocket, char *name, VALUE_PAIR * request, int maxsimul) {
672
673         char    querystr[256];
674         char    authstr[256];
675         VALUE_PAIR *fra;
676         SQL_ROW row;
677         int     count = 0;
678         uint32_t ipno = 0;
679         int     mpp = 1;
680
681         if (inst->config->sensitiveusername)
682                 sprintf(authstr, "STRCMP(UserName, '%s') = 0", name);
683         else
684                 sprintf(authstr, "UserName = '%s'", name);
685         sprintf(querystr, "SELECT COUNT(*) FROM %s WHERE %s AND AcctStopTime = 0",
686                                         inst->config->sql_acct_table, authstr);
687         sql_select_query(inst, sqlsocket, querystr);
688         row = sql_fetch_row(sqlsocket);
689         count = atoi(row[0]);
690         sql_finish_select_query(sqlsocket);
691
692         if (count < maxsimul)
693                 return 0;
694
695         /*
696          * *      Setup some stuff, like for MPP detection.
697          */
698         if ((fra = pairfind(request, PW_FRAMED_IP_ADDRESS)) != NULL)
699                 ipno = htonl(fra->lvalue);
700
701         count = 0;
702         sprintf(querystr, "SELECT * FROM %s WHERE %s AND AcctStopTime = 0",
703                                         inst->config->sql_acct_table, authstr);
704         sql_select_query(inst, sqlsocket, querystr);
705         while ((row = sql_fetch_row(sqlsocket))) {
706                 int     check = sql_check_ts(row);
707
708                 if (check == 1) {
709                         count++;
710
711                         if (ipno && atoi(row[19]) == ipno)
712                                 mpp = 2;
713
714                 } else if (check == 2)
715                         radlog(L_ERR, "rlm_sql:  Problem with checkrad [%s] (from nas %s)", name, row[4]);
716                 else {
717                         /*
718                          *      False record - zap it
719                          */
720
721                         if (inst->config->deletestalesessions) {
722                                 SQLSOCK *sqlsocket1;
723
724                                 radlog(L_ERR, "rlm_sql:  Deleteing stale session [%s] (from nas %s/%s)", row[2],
725                                                          row[4], row[5]);
726                                 sqlsocket1 = sql_get_socket(inst);
727                                 sprintf(querystr, "DELETE FROM %s WHERE RadAcctId = '%s'",
728                                                                 inst->config->sql_acct_table, row[0]);
729                                 sql_query(inst, sqlsocket1, querystr);
730                                 sql_finish_query(sqlsocket1);
731                                 sql_release_socket(inst, sqlsocket1);
732                         }
733                 }
734         }
735         sql_finish_select_query(sqlsocket);
736
737         return (count < maxsimul) ? 0 : mpp;
738
739 }