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