1 /*
2 * Copyright (c) 1997, 2000 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 * $Id: exec.c,v 1.8 2003/10/06 09:43:27 itojun Exp $
31 *
32 * $FreeBSD$
33 *
34 * last edit-date: [Wed Sep 27 09:39:22 2000]
35 *
36 *---------------------------------------------------------------------------*/
37
38 #include "isdnd.h"
39
40 #include <sys/wait.h>
41 #include <sys/socket.h>
42 #include <net/if.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45
46 #define MAX_PIDS 32
47
48 static struct pid_tab {
49 pid_t pid;
50 struct cfg_entry *cep;
51 } pid_tab[MAX_PIDS];
52
53 /*---------------------------------------------------------------------------*
54 * SIGCHLD signal handler
55 *---------------------------------------------------------------------------*/
56 void
sigchild_handler(int sig)57 sigchild_handler(int sig)
58 {
59 int retstat;
60 register int i;
61 pid_t pid;
62
63 if ((pid = waitpid(-1, &retstat, WNOHANG)) <= 0)
64 {
65 logit(LL_ERR, "ERROR, sigchild_handler, waitpid: %s", strerror(errno));
66 error_exit(1, "ERROR, sigchild_handler, waitpid: %s", strerror(errno));
67 }
68 else
69 {
70 if (WIFEXITED(retstat))
71 {
72 DBGL(DL_PROC, (logit(LL_DBG, "normal child (pid=%d) termination, exitstat = %d",
73 pid, WEXITSTATUS(retstat))));
74 }
75 else if (WIFSIGNALED(retstat))
76 {
77 if (WCOREDUMP(retstat))
78 logit(LL_WRN, "child (pid=%d) termination due to signal %d (coredump)",
79 pid, WTERMSIG(retstat));
80 else
81 logit(LL_WRN, "child (pid=%d) termination due to signal %d",
82 pid, WTERMSIG(retstat));
83 }
84 }
85
86 /* check if hangup required */
87
88 for (i=0; i < MAX_PIDS; i++)
89 {
90 if (pid_tab[i].pid == pid)
91 {
92 if (pid_tab[i].cep->cdid != CDID_UNUSED)
93 {
94 DBGL(DL_PROC, (logit(LL_DBG, "sigchild_handler: scheduling hangup for cdid %d, pid %d",
95 pid_tab[i].cep->cdid, pid_tab[i].pid)));
96 pid_tab[i].cep->hangup = 1;
97 }
98 pid_tab[i].pid = 0;
99 break;
100 }
101 }
102 }
103
104 /*---------------------------------------------------------------------------*
105 * execute prog as a subprocess and pass an argumentlist
106 *---------------------------------------------------------------------------*/
107 pid_t
exec_prog(char * prog,char ** arglist)108 exec_prog(char *prog, char **arglist)
109 {
110 char tmp[MAXPATHLEN];
111 char path[MAXPATHLEN+1];
112 pid_t pid;
113 int a;
114
115 snprintf(path, sizeof(path), "%s/%s", ETCPATH, prog);
116
117 arglist[0] = path;
118
119 tmp[0] = '\0';
120
121 for (a=1; arglist[a] != NULL; ++a )
122 {
123 strlcat(tmp, " ", sizeof(tmp));
124 strlcat(tmp, arglist[a], sizeof(tmp));
125 }
126
127 DBGL(DL_PROC, (logit(LL_DBG, "exec_prog: %s, args:%s", path, tmp)));
128
129 switch (pid = fork())
130 {
131 case -1: /* error */
132 logit(LL_ERR, "ERROR, exec_prog/fork: %s", strerror(errno));
133 error_exit(1, "ERROR, exec_prog/fork: %s", strerror(errno));
134 case 0: /* child */
135 break;
136 default: /* parent */
137 return(pid);
138 }
139
140 /* this is the child now */
141
142 /*
143 * close files used only by isdnd, e.g.
144 * 1. /dev/isdn
145 * 2. /var/log/isdnd.acct (or similar, when used)
146 * 3. /var/log/isdnd.log (or similar, when used)
147 */
148 close(isdnfd);
149 if (useacctfile)
150 fclose(acctfp);
151 if (uselogfile)
152 fclose(logfp);
153
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
exec_connect_prog(struct cfg_entry * cep,const char * prog,int link_down)165 exec_connect_prog(struct cfg_entry *cep, const char *prog, int link_down)
166 {
167 char *argv[32], **av = argv;
168 char devicename[MAXPATHLEN], addr[100];
169 int s;
170 struct ifreq ifr;
171
172 /* the obvious things */
173 snprintf(devicename, sizeof(devicename), "%s%d", cep->usrdevicename, cep->usrdeviceunit);
174 *av++ = (char*)prog;
175 *av++ = "-d";
176 *av++ = devicename;
177 *av++ = "-f";
178 *av++ = link_down ? "down" : "up";
179
180 /* try to figure AF_INET address of interface */
181 addr[0] = '\0';
182 memset(&ifr, 0, sizeof ifr);
183 ifr.ifr_addr.sa_family = AF_INET;
184 strncpy(ifr.ifr_name, devicename, sizeof(ifr.ifr_name));
185 s = socket(AF_INET, SOCK_DGRAM, 0);
186 if (s >= 0) {
187 if (ioctl(s, SIOCGIFADDR, (caddr_t)&ifr) >= 0) {
188 struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
189 strlcpy(addr, inet_ntoa(sin->sin_addr), sizeof(addr));
190 *av++ = "-a";
191 *av++ = addr;
192 }
193 close(s);
194 }
195
196 /* terminate argv */
197 *av++ = NULL;
198
199 return exec_prog((char*)prog, argv);
200 }
201
202 /*---------------------------------------------------------------------------*
203 * run answeringmachine application
204 *---------------------------------------------------------------------------*/
205 int
exec_answer(struct cfg_entry * cep)206 exec_answer(struct cfg_entry *cep)
207 {
208 char *argv[32];
209 u_char devicename[MAXPATHLEN];
210 int pid;
211
212 snprintf(devicename, sizeof(devicename), "/dev/%s%d", cep->usrdevicename, cep->usrdeviceunit);
213
214 argv[0] = cep->answerprog;
215 argv[1] = "-D";
216 argv[2] = devicename;
217 argv[3] = "-d";
218 argv[4] = "unknown";
219 argv[5] = "-s";
220 argv[6] = "unknown";
221 argv[7] = NULL;
222
223 /* if destination telephone number avail, add it as argument */
224
225 if (*cep->local_phone_incoming)
226 argv[4] = cep->local_phone_incoming;
227
228 /* if source telephone number avail, add it as argument */
229
230 if (*cep->real_phone_incoming)
231 argv[6] = cep->real_phone_incoming;
232
233 if (*cep->display)
234 {
235 argv[7] = "-t";
236 argv[8] = cep->display;
237 argv[9] = NULL;
238 }
239
240 /* exec program */
241
242 DBGL(DL_PROC, (logit(LL_DBG, "exec_answer: prog=[%s]", cep->answerprog)));
243
244 pid = exec_prog(cep->answerprog, argv);
245
246 /* enter pid and conf ptr entry addr into table */
247
248 if (pid != -1)
249 {
250 int i;
251
252 for (i=0; i < MAX_PIDS; i++)
253 {
254 if (pid_tab[i].pid == 0)
255 {
256 pid_tab[i].pid = pid;
257 pid_tab[i].cep = cep;
258 break;
259 }
260 }
261 return(GOOD);
262 }
263 return(ERROR);
264 }
265
266 /*---------------------------------------------------------------------------*
267 * check if a connection has an outstanding process, if yes, kill it
268 *---------------------------------------------------------------------------*/
269 void
check_and_kill(struct cfg_entry * cep)270 check_and_kill(struct cfg_entry *cep)
271 {
272 int i;
273
274 for (i=0; i < MAX_PIDS; i++)
275 {
276 if (pid_tab[i].cep == cep)
277 {
278 pid_t kp;
279
280 DBGL(DL_PROC, (logit(LL_DBG, "check_and_kill: killing pid %d", pid_tab[i].pid)));
281
282 kp = pid_tab[i].pid;
283 pid_tab[i].pid = 0;
284 kill(kp, SIGHUP);
285 break;
286 }
287 }
288 }
289
290 /*---------------------------------------------------------------------------*
291 * update budget callout/callback statistics counter file
292 *---------------------------------------------------------------------------*/
293 void
upd_callstat_file(char * filename,int rotateflag)294 upd_callstat_file(char *filename, int rotateflag)
295 {
296 FILE *fp;
297 time_t s, l, now;
298 long s_in, l_in;
299 int n;
300 int ret;
301
302 now = time(NULL);
303
304 fp = fopen(filename, "r+");
305
306 if (fp == NULL)
307 {
308 /* file not there, create it and exit */
309
310 logit(LL_WRN, "upd_callstat_file: creating %s", filename);
311
312 fp = fopen(filename, "w");
313 if (fp == NULL)
314 {
315 logit(LL_ERR, "ERROR, upd_callstat_file: cannot create %s, %s", filename, strerror(errno));
316 return;
317 }
318
319 ret = fprintf(fp, "%ld %ld 1", (long)now, (long)now);
320 if (ret <= 0)
321 logit(LL_ERR, "ERROR, upd_callstat_file: fprintf failed: %s", strerror(errno));
322
323 fclose(fp);
324 return;
325 }
326
327 /* get contents */
328
329 ret = fscanf(fp, "%ld %ld %d", &s_in, &l_in, &n);
330 s = s_in; l = l_in;
331
332 /* reset fp */
333
334 rewind(fp);
335
336 if (ret != 3)
337 {
338 /* file corrupt ? anyway, initialize */
339
340 logit(LL_WRN, "upd_callstat_file: initializing %s", filename);
341
342 s = l = now;
343 n = 0;
344 }
345
346 if (rotateflag)
347 {
348 struct tm *stmp;
349 int dom;
350
351 /* get day of month for last timestamp */
352 stmp = localtime(&l);
353 dom = stmp->tm_mday;
354
355 /* get day of month for just now */
356 stmp = localtime(&now);
357
358 if (dom != stmp->tm_mday)
359 {
360 FILE *nfp;
361 char buf[MAXPATHLEN];
362
363 /* new day, write last days stats */
364
365 snprintf(buf, sizeof(buf), "%s-%02d", filename,
366 stmp->tm_mday);
367
368 nfp = fopen(buf, "w");
369 if (nfp == NULL)
370 {
371 logit(LL_ERR, "ERROR, upd_callstat_file: cannot open for write %s, %s", buf, strerror(errno));
372 return;
373 }
374
375 ret = fprintf(nfp, "%ld %ld %d", (long)s, (long)l, n);
376 if (ret <= 0)
377 logit(LL_ERR, "ERROR, upd_callstat_file: fprintf failed: %s", strerror(errno));
378
379 fclose(nfp);
380
381 /* init new days stats */
382 n = 0;
383 s = now;
384
385 logit(LL_WRN, "upd_callstat_file: rotate %s, new s=%ld l=%ld n=%d", filename, s, l, n);
386 }
387 }
388
389 n++; /* increment call count */
390
391 /*
392 * the "%-3d" is necessary to overwrite any
393 * leftovers from previous contents!
394 */
395
396 ret = fprintf(fp, "%ld %ld %-3d", (long)s, (long)now, n);
397
398 if (ret <= 0)
399 logit(LL_ERR, "ERROR, upd_callstat_file: fprintf failed: %s", strerror(errno));
400
401 fclose(fp);
402 }
403
404 /* EOF */
405