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