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