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