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