Initial import from FreeBSD RELENG_4:
[games.git] / usr.sbin / i4b / isdnd / exec.c
1 /*
2  * Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  *---------------------------------------------------------------------------
26  *
27  *      exec.h - supplemental program/script execution
28  *      ----------------------------------------------
29  *
30  * $FreeBSD: src/usr.sbin/i4b/isdnd/exec.c,v 1.6.2.4 2001/12/10 09:42:52 hm Exp $
31  *
32  *      last edit-date: [Mon Dec 10 10:39:53 2001]
33  *
34  *---------------------------------------------------------------------------*/
35
36 #include "isdnd.h"
37
38 #include <sys/wait.h>
39 #include <sys/socket.h>
40 #include <net/if.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #include <paths.h>
44
45 #define MAX_PIDS 32
46
47 static struct pid_tab {
48         pid_t   pid;
49         cfg_entry_t *cep;
50 } pid_tab[MAX_PIDS];
51
52 /*---------------------------------------------------------------------------*
53  *      SIGCHLD signal handler
54  *---------------------------------------------------------------------------*/
55 void
56 sigchild_handler(int sig)
57 {
58         int retstat;
59         register int i;
60         pid_t pid;
61         
62         if((pid = waitpid(-1, &retstat, WNOHANG)) <= 0)
63         {
64                 log(LL_ERR, "ERROR, sigchild_handler, waitpid: %s", strerror(errno));
65                 error_exit(1, "ERROR, sigchild_handler, waitpid: %s", strerror(errno));
66         }
67         else
68         {
69                 if(WIFEXITED(retstat))
70                 {
71                         DBGL(DL_PROC, (log(LL_DBG, "normal child (pid=%d) termination, exitstat = %d",
72                                 pid, WEXITSTATUS(retstat))));
73                 }
74                 else if(WIFSIGNALED(retstat))
75                 {
76                         if(WCOREDUMP(retstat))
77                                 log(LL_WRN, "child (pid=%d) termination due to signal %d (coredump)",
78                                         pid, WTERMSIG(retstat));
79                         else
80                                 log(LL_WRN, "child (pid=%d) termination due to signal %d",
81                                         pid, WTERMSIG(retstat));
82                 }
83         }
84
85         /* check if hangup required */
86         
87         for(i=0; i < MAX_PIDS; i++)
88         {
89                 if(pid_tab[i].pid == pid)
90                 {
91                         if(pid_tab[i].cep->cdid != CDID_UNUSED)
92                         {
93                                 DBGL(DL_PROC, (log(LL_DBG, "sigchild_handler: scheduling hangup for cdid %d, pid %d",
94                                         pid_tab[i].cep->cdid, pid_tab[i].pid)));
95                                 pid_tab[i].cep->hangup = 1;
96                         }
97                         pid_tab[i].pid = 0;
98                         break;
99                 }
100         }
101 }
102
103 /*---------------------------------------------------------------------------*
104  *      execute prog as a subprocess and pass an argumentlist
105  *---------------------------------------------------------------------------*/
106 pid_t
107 exec_prog(char *prog, char **arglist)
108 {
109         char tmp[MAXPATHLEN];
110         char path[MAXPATHLEN+1];
111         pid_t pid;
112         int a;
113
114         snprintf(path, sizeof(path), "%s/%s", ETCPATH, prog);
115
116         arglist[0] = path;
117
118         tmp[0] = '\0';
119
120         for(a=1; arglist[a] != NULL; ++a )
121         {
122                 strcat(tmp, " " );
123                 strcat(tmp, arglist[a]);
124         }
125
126         DBGL(DL_PROC, (log(LL_DBG, "exec_prog: %s, args:%s", path, tmp)));
127         
128         switch(pid = fork())
129         {
130                 case -1:                /* error */
131                         log(LL_ERR, "ERROR, exec_prog/fork: %s", strerror(errno));
132                         error_exit(1, "ERROR, exec_prog/fork: %s", strerror(errno));
133                 case 0:                 /* child */
134                         break;
135                 default:                /* parent */
136                         return(pid);
137         }
138
139         /* this is the child now */
140
141         /*
142          * close files used only by isdnd, e.g.
143          * 1. /dev/i4b
144          * 2. /var/log/isdnd.acct (or similar, when used)
145          * 3. /var/log/isdnd.log (or similar, when used)
146          */
147         close(isdnfd);
148
149         if(useacctfile && acctfp)
150                 fclose(acctfp);
151
152         if(uselogfile && logfp)
153                 fclose(logfp);
154
155         if(execvp(path,arglist) < 0 )
156                 _exit(127);
157
158         return(-1);
159 }
160
161 /*---------------------------------------------------------------------------*
162  *      run interface up/down script
163  *---------------------------------------------------------------------------*/
164 int
165 exec_connect_prog(cfg_entry_t *cep, const char *prog, int link_down)
166 {
167         char *argv[32], **av = argv;
168         char devicename[MAXPATHLEN], addr[100];
169         char *device;
170         int s;
171         struct ifreq ifr;
172
173         /* the obvious things */
174         device = bdrivername(cep->usrdevicename);
175         snprintf(devicename, sizeof(devicename), "%s%d", device, cep->usrdeviceunit);
176         *av++ = (char*)prog;
177         *av++ = "-d";
178         *av++ = devicename;
179         *av++ = "-f";
180         *av++ = link_down ? "down" : "up";
181
182         /* try to figure AF_INET address of interface */
183         addr[0] = '\0';
184         memset(&ifr, 0, sizeof ifr);
185         ifr.ifr_addr.sa_family = AF_INET;
186         strncpy(ifr.ifr_name, devicename, sizeof(ifr.ifr_name));
187         s = socket(AF_INET, SOCK_DGRAM, 0);
188         if (s >= 0) {
189                 if (ioctl(s, SIOCGIFADDR, (caddr_t)&ifr) >= 0) {
190                         struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
191                         strcpy(addr, inet_ntoa(sin->sin_addr));
192                         *av++ = "-a";
193                         *av++ = addr;
194                 }
195                 close(s);
196         }
197
198         /* terminate argv */
199         *av++ = NULL;
200
201         return exec_prog((char*)prog, argv);
202 }
203
204 /*---------------------------------------------------------------------------*
205  *      run answeringmachine application
206  *---------------------------------------------------------------------------*/
207 int
208 exec_answer(cfg_entry_t *cep)
209 {
210         char *argv[32];
211         u_char devicename[MAXPATHLEN];  
212         int pid;
213         char *device;
214         
215         device = bdrivername(cep->usrdevicename);
216
217         snprintf(devicename, sizeof(devicename), "%si4b%s%d", _PATH_DEV, device,
218             cep->usrdeviceunit);
219
220         argv[0] = cep->answerprog;
221         argv[1] = "-D";
222         argv[2] = devicename;
223         argv[3] = "-d";
224         argv[4] = "unknown";
225         argv[5] = "-s";
226         argv[6] = "unknown";
227         argv[7] = NULL;
228
229         /* if destination telephone number avail, add it as argument */
230         
231         if(*cep->local_phone_incoming)
232                 argv[4] = cep->local_phone_incoming;
233
234         /* if source telephone number avail, add it as argument */
235         
236         if(*cep->real_phone_incoming)
237                 argv[6] = cep->real_phone_incoming;
238
239         if(*cep->display)
240         {
241                 argv[7] = "-t";
242                 argv[8] = cep->display;
243                 argv[9] = NULL;
244         }
245
246         /* exec program */
247         
248         DBGL(DL_PROC, (log(LL_DBG, "exec_answer: prog=[%s]", cep->answerprog)));
249         
250         pid = exec_prog(cep->answerprog, argv);
251                 
252         /* enter pid and conf ptr entry addr into table */
253         
254         if(pid != -1)
255         {
256                 int i;
257                 
258                 for(i=0; i < MAX_PIDS; i++)
259                 {
260                         if(pid_tab[i].pid == 0)
261                         {
262                                 pid_tab[i].pid = pid;
263                                 pid_tab[i].cep = cep;
264                                 break;
265                         }
266                 }
267                 return(GOOD);
268         }
269         return(ERROR);
270 }
271
272 /*---------------------------------------------------------------------------*
273  *      check if a connection has an outstanding process, if yes, kill it
274  *---------------------------------------------------------------------------*/
275 void
276 check_and_kill(cfg_entry_t *cep)
277 {
278         int i;
279         
280         for(i=0; i < MAX_PIDS; i++)
281         {
282                 if(pid_tab[i].cep == cep)
283                 {
284                         pid_t kp;
285
286                         DBGL(DL_PROC, (log(LL_DBG, "check_and_kill: killing pid %d", pid_tab[i].pid)));
287
288                         kp = pid_tab[i].pid;
289                         pid_tab[i].pid = 0;                     
290                         kill(kp, SIGHUP);
291                         break;
292                 }
293         }
294 }
295
296 /*---------------------------------------------------------------------------*
297  *      update budget callout/callback statistics counter file
298  *---------------------------------------------------------------------------*/
299 void
300 upd_callstat_file(char *filename, int rotateflag)
301 {
302         FILE *fp;
303         time_t s, l, now;
304         int n;
305         int ret;
306
307         now = time(NULL);
308         
309         fp = fopen(filename, "r+");
310
311         if(fp == NULL)
312         {
313                 /* file not there, create it and exit */
314                 
315                 log(LL_WRN, "upd_callstat_file: creating %s", filename);
316
317                 fp = fopen(filename, "w");
318                 if(fp == NULL)
319                 {
320                         log(LL_ERR, "ERROR, upd_callstat_file: cannot create %s, %s", filename, strerror(errno));
321                         return;
322                 }
323
324                 ret = fprintf(fp, "%ld %ld 1", now, now);
325                 if(ret <= 0)
326                         log(LL_ERR, "ERROR, upd_callstat_file: fprintf failed: %s", strerror(errno));
327                 
328                 fclose(fp);
329                 return;
330         }
331
332         /* get contents */
333         
334         ret = fscanf(fp, "%ld %ld %d", &s, &l, &n);
335
336         /* reset fp */
337         
338         rewind(fp);
339                 
340         if(ret != 3)
341         {
342                 /* file corrupt ? anyway, initialize */
343                 
344                 log(LL_WRN, "upd_callstat_file: initializing %s", filename);
345
346                 s = l = now;
347                 n = 0;
348         }
349
350         if(rotateflag)
351         {
352                 struct tm *stmp;
353                 int dom;
354
355                 /* get day of month for last timestamp */
356                 stmp = localtime(&l);
357                 dom = stmp->tm_mday;    
358
359                 /* get day of month for just now */
360                 stmp = localtime(&now);
361                 
362                 if(dom != stmp->tm_mday)
363                 {
364                         FILE *nfp;
365                         char buf[MAXPATHLEN];
366
367                         /* new day, write last days stats */
368
369                         sprintf(buf, "%s-%02d", filename, stmp->tm_mday);
370
371                         nfp = fopen(buf, "w");
372                         if(nfp == NULL)
373                         {
374                                 log(LL_ERR, "ERROR, upd_callstat_file: cannot open for write %s, %s", buf, strerror(errno));
375                                 return;
376                         }
377
378                         ret = fprintf(nfp, "%ld %ld %d", s, l, n);
379                         if(ret <= 0)
380                                 log(LL_ERR, "ERROR, upd_callstat_file: fprintf failed: %s", strerror(errno));
381                         
382                         fclose(nfp);
383
384                         /* init new days stats */
385                         n = 0;
386                         s = now;
387
388                         log(LL_WRN, "upd_callstat_file: rotate %s, new s=%ld l=%ld n=%d", filename, s, l, n);
389                 }                               
390         }
391
392         n++;    /* increment call count */
393
394         /*
395          * the "%-3d" is necessary to overwrite any
396          * leftovers from previous contents!
397          */
398
399         ret = fprintf(fp, "%ld %ld %-3d", s, now, n);   
400
401         if(ret <= 0)
402                 log(LL_ERR, "ERROR, upd_callstat_file: fprintf failed: %s", strerror(errno));
403         
404         fclose(fp);
405 }
406         
407 /* EOF */