]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/comm.c
ms, not us stupid
[netatalk.git] / etc / cnid_dbd / comm.c
1 /*
2  * $Id: comm.c,v 1.6 2009-10-19 08:09:07 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, time_t *now)
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     time(&t);
174     if (now)
175         *now = t;
176
177     if (!ret)
178         return 0;
179
180
181     if (FD_ISSET(control_fd, &readfds)) {
182         int    l = 0;
183
184         fd = recv_cred(control_fd);
185         if (fd < 0) {
186             return -1;
187         }
188         if (fds_in_use < fd_table_size) {
189             fd_table[fds_in_use].fd = fd;
190             fd_table[fds_in_use].tm = t;
191             fds_in_use++;
192         } else {
193             time_t older = t;
194
195             for (i = 0; i != fds_in_use; i++) {
196                 if (older <= fd_table[i].tm) {
197                     older = fd_table[i].tm;
198                     l = i;
199                 }
200             }
201             close(fd_table[l].fd);
202             fd_table[l].fd = fd;
203             fd_table[l].tm = t;
204         }
205         return 0;
206     }
207
208     for (i = 0; i != fds_in_use; i++) {
209         if (FD_ISSET(fd_table[i].fd, &readfds)) {
210             fd_table[i].tm = t;
211             return fd_table[i].fd;
212         }
213     }
214     /* We should never get here */
215     return 0;
216 }
217
218 int comm_init(struct db_param *dbp, int ctrlfd, int clntfd)
219 {
220     int i;
221
222     fds_in_use = 0;
223     fd_table_size = dbp->fd_table_size;
224
225     if ((fd_table = malloc(fd_table_size * sizeof(struct connection))) == NULL) {
226         LOG(log_error, logtype_cnid, "Out of memory");
227         return -1;
228     }
229     for (i = 0; i != fd_table_size; i++)
230         fd_table[i].fd = -1;
231     /* from dup2 */
232     control_fd = ctrlfd;
233 #if 0
234     int b = 1;
235     /* this one dump core in recvmsg, great */
236     if ( setsockopt(control_fd, SOL_SOCKET, SO_PASSCRED, &b, sizeof (b)) < 0) {
237         LOG(log_error, logtype_cnid, "setsockopt SO_PASSCRED %s",  strerror(errno));
238         return -1;
239     }
240 #endif
241     /* push the first client fd */
242     fd_table[fds_in_use].fd = clntfd;
243     fds_in_use++;
244
245     return 0;
246 }
247
248 /* ------------
249    nbe of clients
250 */
251 int comm_nbe(void)
252 {
253     return fds_in_use;
254 }
255
256 /* ------------ */
257 int comm_rcv(struct cnid_dbd_rqst *rqst, time_t timeout, const sigset_t *sigmask, time_t *now)
258 {
259     char *nametmp;
260     int b;
261
262     if ((cur_fd = check_fd(timeout, sigmask, now)) < 0)
263         return -1;
264
265     if (!cur_fd)
266         return 0;
267     nametmp = rqst->name;
268     if ((b = readt(cur_fd, rqst, sizeof(struct cnid_dbd_rqst), 1, 5))
269         != sizeof(struct cnid_dbd_rqst)) {
270         if (b)
271             LOG(log_error, logtype_cnid, "error reading message header: %s", strerror(errno));
272         invalidate_fd(cur_fd);
273         rqst->name = nametmp;
274         return 0;
275     }
276     rqst->name = nametmp;
277     if (rqst->namelen && readt(cur_fd, rqst->name, rqst->namelen, 1, 5) != rqst->namelen) {
278         LOG(log_error, logtype_cnid, "error reading message name: %s", strerror(errno));
279         invalidate_fd(cur_fd);
280         return 0;
281     }
282     /* We set this to make life easier for logging. None of the other stuff
283        needs zero terminated strings. */
284     rqst->name[rqst->namelen] = '\0';
285
286     return 1;
287 }
288
289 /* ------------ */
290 #define USE_WRITEV
291 int comm_snd(struct cnid_dbd_rply *rply)
292 {
293 #ifdef USE_WRITEV
294     struct iovec iov[2];
295     size_t towrite;
296 #endif
297
298     if (!rply->namelen) {
299         if (write(cur_fd, rply, sizeof(struct cnid_dbd_rply)) != sizeof(struct cnid_dbd_rply)) {
300             LOG(log_error, logtype_cnid, "error writing message header: %s", strerror(errno));
301             invalidate_fd(cur_fd);
302             return 0;
303         }
304         return 1;
305     }
306 #ifdef USE_WRITEV
307
308     iov[0].iov_base = rply;
309     iov[0].iov_len = sizeof(struct cnid_dbd_rply);
310     iov[1].iov_base = rply->name;
311     iov[1].iov_len = rply->namelen;
312     towrite = sizeof(struct cnid_dbd_rply) +rply->namelen;
313
314     if (writev(cur_fd, iov, 2) != towrite) {
315         LOG(log_error, logtype_cnid, "error writing message : %s", strerror(errno));
316         invalidate_fd(cur_fd);
317         return 0;
318     }
319 #else
320     if (write(cur_fd, rply, sizeof(struct cnid_dbd_rply)) != sizeof(struct cnid_dbd_rply)) {
321         LOG(log_error, logtype_cnid, "error writing message header: %s", strerror(errno));
322         invalidate_fd(cur_fd);
323         return 0;
324     }
325     if (write(cur_fd, rply->name, rply->namelen) != rply->namelen) {
326         LOG(log_error, logtype_cnid, "error writing message name: %s", strerror(errno));
327         invalidate_fd(cur_fd);
328         return 0;
329     }
330 #endif
331     return 1;
332 }
333
334