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