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.10 2009/04/16 05:56:32 lukem 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
48static struct pid_tab {
49	pid_t	pid;
50	struct cfg_entry *cep;
51} pid_tab[MAX_PIDS];
52
53/*---------------------------------------------------------------------------*
54 *	SIGCHLD signal handler
55 *---------------------------------------------------------------------------*/
56void
57sigchild_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 *---------------------------------------------------------------------------*/
107pid_t
108exec_prog(const char *prog, const 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, __UNCONST(arglist)) < 0 )
156		_exit(127);
157
158	return(-1);
159}
160
161/*---------------------------------------------------------------------------*
162 *	run interface up/down script
163 *---------------------------------------------------------------------------*/
164int
165exec_connect_prog(struct cfg_entry *cep, const char *prog, int link_down)
166{
167	const 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++ = 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(prog, argv);
200}
201
202/*---------------------------------------------------------------------------*
203 *	run answeringmachine application
204 *---------------------------------------------------------------------------*/
205int
206exec_answer(struct cfg_entry *cep)
207{
208	const char *argv[32];
209	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 *---------------------------------------------------------------------------*/
269void
270check_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 *---------------------------------------------------------------------------*/
293void
294upd_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