authuserdb: Fix treatment of integers as strings in my previous patch.
[hcoop/debian/courier-authlib.git] / authpipelib.c
1 /*
2 ** Copyright 1998 - 2000 Double Precision, Inc. See COPYING for
3 ** distribution information.
4 */
5
6 /* Based on code by Christian Loitsch <courier-imap@abc.fgecko.com> */
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <errno.h>
12
13 #include <unistd.h>
14 /* for fork */
15 #include <sys/types.h>
16 /* used to avoid zombies */
17 #include <signal.h>
18 #include <sys/wait.h>
19 #include <sys/time.h>
20 #include <sys/select.h>
21
22 #include "auth.h"
23 #include "authcustom.h"
24 #include "courierauthdebug.h"
25
26 #include "courierauthdebug.h"
27
28 #include "authpipelib.h"
29 #include "authpiperc.h"
30
31 static int lastIn = -1;
32 static int lastOut = -1;
33 static pid_t childPID = -1;
34
35 static void eliminatePipe(pid_t child);
36
37 static void execChild(int to[], int from[])
38 {
39 DPRINTF("executing %s", PIPE_PROGRAM);
40
41 close(STDIN_FILENO); dup2(to[0], STDIN_FILENO);
42 close(STDOUT_FILENO); dup2(from[1], STDOUT_FILENO);
43 close(to[0]); close(to[1]); close(from[0]); close(from[1]);
44
45 execl(PIPE_PROGRAM, PIPE_PROGRAM, NULL);
46
47 DPRINTF("pipe: failed to execute %s: %s",PIPE_PROGRAM, strerror(errno));
48 exit(1);
49 }
50
51 void closePipe(void)
52 {
53 DPRINTF("closing pipe");
54 if (lastIn >= 0) { close(lastIn); lastIn = -1; }
55 if (lastOut >= 0) { close (lastOut); lastOut = -1; }
56 if (childPID > 1) { eliminatePipe(childPID); childPID = -1; }
57 }
58
59 static int forkPipe(int *dataIn, int *dataOut, pid_t *child)
60 {
61 int to[2], from[2];
62
63 /* let's create 2 pipes */
64 if(pipe(to) < 0) {
65 DPRINTF("pipe: failed to create pipe: %s", strerror(errno));
66 return 1;
67 }
68
69 if(pipe(from) < 0) {
70 DPRINTF("pipe: failed to create pipe: %s", strerror(errno));
71 close(to[0]); close(to[1]);
72 return 1;
73 }
74
75 DPRINTF("attempting to fork");
76 *child = fork();
77 if(*child < 0) {
78 DPRINTF("pipe: failed to fork: %s", strerror(errno));
79 close(to[0]); close(to[1]); close(from[0]); close(from[1]);
80 return 1;
81 }
82
83 /* child */
84 if(*child == 0) execChild(to, from);
85
86 /* parent */
87 DPRINTF("Pipe auth. started Pipe-program (pid %d)", *child);
88
89 close(to[0]); close(from[1]);
90 *dataIn = from[0]; *dataOut = to[1];
91 return 0;
92 }
93
94 /* kills and waits for child
95 * in a quite inefficient way, but this shouldn't happen very often */
96 static void eliminatePipe(pid_t child)
97 {
98 unsigned int seconds;
99
100 /* let's first look, if child is already terminated */
101 DPRINTF("trying to wait for child (WNOHANG) (pid %d)", child);
102 if (waitpid(child, NULL, WNOHANG) > 0) return;
103
104 DPRINTF("sleep 2 seconds and try again to wait for pid %d", child);
105 /* let's give the pipe-program a few seconds to terminate */
106 sleep(2); /* don't care if interrupted earlier */
107 if (waitpid(child, NULL, WNOHANG) > 0) return;
108
109 /* let's TERM it */
110 DPRINTF("killing (SIGTERM) child pid %d", child);
111 kill(child, SIGTERM);
112
113 /* give it a few seconds */
114 for (seconds = 10; seconds > 0; sleep(1), seconds--)
115 if (waitpid(child, NULL, WNOHANG) > 0) return;
116
117 /* ok, let's KILL it */
118 DPRINTF("killing (SIGKILL) child pid %d", child);
119 if (kill(child, SIGKILL) == 0)
120 {
121 /* and wait, unless we have a kernel bug, it MUST terminate */
122 DPRINTF("waitpiding for child pid (blocking!) %d)", child);
123 waitpid(child, NULL, 0);
124 }
125 else
126 {
127
128 DPRINTF("error when sending sigkill to %d", child);
129 if (errno != ESRCH) return;
130 /* strange, we can not kill our own child with SIGKILL*/
131
132 /* errno indicates process does not exist, maybe it's dead
133 * by now, let's try 1 final time, else, ignore it */
134 DPRINTF("maybe because already dead (pid: %d)", child);
135 waitpid(child, NULL, WNOHANG);
136 }
137 }
138
139 int getPipe(int *dataIn, int *dataOut)
140 {
141 int rv;
142
143 if (childPID > 1)
144 {
145 /* Simple test if the child is still usable: do a read
146 ** poll on dataIn. If the child has closed the pipe,
147 ** or there is spurious data, the fd will be ready. */
148 fd_set fdr;
149 struct timeval tv;
150 FD_ZERO(&fdr);
151 FD_SET(lastIn, &fdr);
152 tv.tv_sec=0;
153 tv.tv_usec=0;
154 rv = select(lastIn+1, &fdr, 0, 0, &tv);
155 if (rv == 0)
156 {
157 DPRINTF("reusing pipe, with in: %d out: %d", lastIn, lastOut);
158 *dataIn = lastIn;
159 *dataOut = lastOut;
160 return 0;
161 }
162 if (rv < 0)
163 perror("authpipe: getPipe: select");
164 else
165 {
166 DPRINTF("child died or sent spurious data (pid: %d)", childPID);
167 }
168 }
169
170 /* ok pipe was not usable; either this is the first call, or
171 * the pipe broke the connection.
172 * We have to clean up and start a new one */
173
174 closePipe();
175 DPRINTF("forking new one");
176 rv = forkPipe(&lastIn, &lastOut, &childPID);
177 if (rv)
178 {
179 DPRINTF("couldn't fork new pipe");
180 lastIn = -1;
181 lastOut = -1;
182 childPID = -1;
183 }
184 else
185 {
186 DPRINTF("new pipe has in: %d, out: %d", lastIn, lastOut);
187 *dataIn = lastIn;
188 *dataOut = lastOut;
189 }
190 return rv;
191 }
192