]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/comm.c
cnid dbd don't fall back to 1 sec timeout after an idle timer if active connections
[netatalk.git] / etc / cnid_dbd / comm.c
1 /*
2  * $Id: comm.c,v 1.5 2009-10-18 17:50:13 didg Exp $
3  *
4  * Copyright (C) Joerg Lenneis 2003
5  * All Rights Reserved.  See COPYING.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <errno.h>
16
17 #ifdef HAVE_UNISTD_H
18 #include <unistd.h>
19 #endif
20
21 #include <sys/param.h>
22 #define _XPG4_2 1
23 #include <sys/socket.h>
24
25 #ifdef HAVE_SYS_TYPES_H
26 #include <sys/types.h>
27 #endif
28
29 #ifdef HAVE_SYS_TIME_H
30 #include <sys/time.h>
31 #endif
32
33 #ifdef HAVE_SYS_UIO_H
34 #include <sys/uio.h>
35 #endif
36
37 #ifdef HAVE_SYS_SOCKET_H
38 #include <sys/socket.h>
39 #endif
40
41 #include <sys/select.h>
42
43 #include <assert.h>
44 #include <time.h>
45
46 #include <atalk/logger.h>
47 #include <atalk/cnid_dbd_private.h>
48
49 #include "db_param.h"
50 #include "usockfd.h"
51 #include "comm.h"
52
53 /* Length of the space taken up by a padded control message of length len */
54 #ifndef CMSG_SPACE
55 #define CMSG_SPACE(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + __CMSG_ALIGN(len))
56 #endif
57
58
59 struct connection {
60     time_t tm;                    /* When respawned last */
61     int    fd;
62 };
63
64 static int   control_fd;
65 static int   cur_fd;
66 static struct connection *fd_table;
67 static int  fd_table_size;
68 static int  fds_in_use = 0;
69
70
71 static void invalidate_fd(int fd)
72 {
73     int i;
74
75     if (fd == control_fd)
76         return;
77     for (i = 0; i != fds_in_use; i++)
78         if (fd_table[i].fd == fd)
79             break;
80
81     assert(i < fds_in_use);
82
83     fds_in_use--;
84     fd_table[i] = fd_table[fds_in_use];
85     fd_table[fds_in_use].fd = -1;
86     close(fd);
87     return;
88 }
89
90 static int recv_cred(int fd)
91 {
92     int ret;
93     struct msghdr msgh;
94     struct iovec iov[1];
95     struct cmsghdr *cmsgp = NULL;
96     char buf[CMSG_SPACE(sizeof(int))];
97     char dbuf[80];
98
99     memset(&msgh,0,sizeof(msgh));
100     memset(buf,0,sizeof(buf));
101
102     msgh.msg_name = NULL;
103     msgh.msg_namelen = 0;
104
105     msgh.msg_iov = iov;
106     msgh.msg_iovlen = 1;
107
108     iov[0].iov_base = dbuf;
109     iov[0].iov_len = sizeof(dbuf);
110
111     msgh.msg_control = buf;
112     msgh.msg_controllen = sizeof(buf);
113
114     do  {
115         ret = recvmsg(fd ,&msgh,0);
116     } while ( ret == -1 && errno == EINTR );
117
118     if ( ret == -1 ) {
119         return -1;
120     }
121
122     for ( cmsgp = CMSG_FIRSTHDR(&msgh); cmsgp != NULL; cmsgp = CMSG_NXTHDR(&msgh,cmsgp) ) {
123         if ( cmsgp->cmsg_level == SOL_SOCKET && cmsgp->cmsg_type == SCM_RIGHTS ) {
124             return *(int *) CMSG_DATA(cmsgp);
125         }
126     }
127
128     if ( ret == sizeof (int) )
129         errno = *(int *)dbuf; /* Rcvd errno */
130     else
131         errno = ENOENT;    /* Default errno */
132
133     return -1;
134 }
135
136 /*
137  *  Check for client requests. We keep up to fd_table_size open descriptors in
138  *  fd_table. If the table is full and we get a new descriptor via
139  *  control_fd, we close a random decriptor in the table to make space. The
140  *  affected client will automatically reconnect. For an EOF (descriptor is
141  *  closed by the client, so a read here returns 0) comm_rcv will take care of
142  *  things and clean up fd_table. The same happens for any read/write errors.
143  */
144
145 static int check_fd(time_t timeout, const sigset_t *sigmask)
146 {
147     int fd;
148     fd_set readfds;
149     struct timespec tv;
150     int ret;
151     int i;
152     int maxfd = control_fd;
153     time_t t;
154
155     FD_ZERO(&readfds);
156     FD_SET(control_fd, &readfds);
157
158     for (i = 0; i != fds_in_use; i++) {
159         FD_SET(fd_table[i].fd, &readfds);
160         if (maxfd < fd_table[i].fd)
161             maxfd = fd_table[i].fd;
162     }
163
164     tv.tv_nsec = 0;
165     tv.tv_sec  = timeout;
166     if ((ret = pselect(maxfd + 1, &readfds, NULL, NULL, &tv, sigmask)) < 0) {
167         if (errno == EINTR)
168             return 0;
169         LOG(log_error, logtype_cnid, "error in select: %s",strerror(errno));
170         return -1;
171     }
172
173     if (!ret)
174         return 0;
175
176     time(&t);
177
178     if (FD_ISSET(control_fd, &readfds)) {
179         int    l = 0;
180
181         fd = recv_cred(control_fd);
182         if (fd < 0) {
183             return -1;
184         }
185         if (fds_in_use < fd_table_size) {
186             fd_table[fds_in_use].fd = fd;
187             fd_table[fds_in_use].tm = t;
188             fds_in_use++;
189         } else {
190             time_t older = t;
191
192             for (i = 0; i != fds_in_use; i++) {
193                 if (older <= fd_table[i].tm) {
194                     older = fd_table[i].tm;
195                     l = i;
196                 }
197             }
198             close(fd_table[l].fd);
199             fd_table[l].fd = fd;
200             fd_table[l].tm = t;
201         }
202         return 0;
203     }
204
205     for (i = 0; i != fds_in_use; i++) {
206         if (FD_ISSET(fd_table[i].fd, &readfds)) {
207             fd_table[i].tm = t;
208             return fd_table[i].fd;
209         }
210     }
211     /* We should never get here */
212     return 0;
213 }
214
215 int comm_init(struct db_param *dbp, int ctrlfd, int clntfd)
216 {
217     int i;
218
219     fds_in_use = 0;
220     fd_table_size = dbp->fd_table_size;
221
222     if ((fd_table = malloc(fd_table_size * sizeof(struct connection))) == NULL) {
223         LOG(log_error, logtype_cnid, "Out of memory");
224         return -1;
225     }
226     for (i = 0; i != fd_table_size; i++)
227         fd_table[i].fd = -1;
228     /* from dup2 */
229     control_fd = ctrlfd;
230 #if 0
231     int b = 1;
232     /* this one dump core in recvmsg, great */
233     if ( setsockopt(control_fd, SOL_SOCKET, SO_PASSCRED, &b, sizeof (b)) < 0) {
234         LOG(log_error, logtype_cnid, "setsockopt SO_PASSCRED %s",  strerror(errno));
235         return -1;
236     }
237 #endif
238     /* push the first client fd */
239     fd_table[fds_in_use].fd = clntfd;
240     fds_in_use++;
241
242     return 0;
243 }
244
245 /* ------------
246    nbe of clients
247 */
248 int comm_nbe(void)
249 {
250     return fds_in_use;
251 }
252
253 /* ------------ */
254 int comm_rcv(struct cnid_dbd_rqst *rqst, time_t timeout, const sigset_t *sigmask)
255 {
256     char *nametmp;
257     int b;
258
259     if ((cur_fd = check_fd(timeout, sigmask)) < 0)
260         return -1;
261
262     if (!cur_fd)
263         return 0;
264     nametmp = rqst->name;
265     if ((b = read(cur_fd, rqst, sizeof(struct cnid_dbd_rqst))) != sizeof(struct cnid_dbd_rqst)) {
266         if (b)
267             LOG(log_error, logtype_cnid, "error reading message header: %s", strerror(errno));
268         invalidate_fd(cur_fd);
269         rqst->name = nametmp;
270         return 0;
271     }
272     rqst->name = nametmp;
273     if (rqst->namelen && read(cur_fd, rqst->name, rqst->namelen) != rqst->namelen) {
274         LOG(log_error, logtype_cnid, "error reading message name: %s", strerror(errno));
275         invalidate_fd(cur_fd);
276         return 0;
277     }
278     /* We set this to make life easier for logging. None of the other stuff
279        needs zero terminated strings. */
280     rqst->name[rqst->namelen] = '\0';
281
282     return 1;
283 }
284
285 /* ------------ */
286 #define USE_WRITEV
287 int comm_snd(struct cnid_dbd_rply *rply)
288 {
289 #ifdef USE_WRITEV
290     struct iovec iov[2];
291     size_t towrite;
292 #endif
293
294     if (!rply->namelen) {
295         if (write(cur_fd, rply, sizeof(struct cnid_dbd_rply)) != sizeof(struct cnid_dbd_rply)) {
296             LOG(log_error, logtype_cnid, "error writing message header: %s", strerror(errno));
297             invalidate_fd(cur_fd);
298             return 0;
299         }
300         return 1;
301     }
302 #ifdef USE_WRITEV
303
304     iov[0].iov_base = rply;
305     iov[0].iov_len = sizeof(struct cnid_dbd_rply);
306     iov[1].iov_base = rply->name;
307     iov[1].iov_len = rply->namelen;
308     towrite = sizeof(struct cnid_dbd_rply) +rply->namelen;
309
310     if (writev(cur_fd, iov, 2) != towrite) {
311         LOG(log_error, logtype_cnid, "error writing message : %s", strerror(errno));
312         invalidate_fd(cur_fd);
313         return 0;
314     }
315 #else
316     if (write(cur_fd, rply, sizeof(struct cnid_dbd_rply)) != sizeof(struct cnid_dbd_rply)) {
317         LOG(log_error, logtype_cnid, "error writing message header: %s", strerror(errno));
318         invalidate_fd(cur_fd);
319         return 0;
320     }
321     if (write(cur_fd, rply->name, rply->namelen) != rply->namelen) {
322         LOG(log_error, logtype_cnid, "error writing message name: %s", strerror(errno));
323         invalidate_fd(cur_fd);
324         return 0;
325     }
326 #endif
327     return 1;
328 }
329
330