]> arthur.barton.de Git - netatalk.git/blob - libatalk/dsi/dsi_getsess.c
d5b855a8c359ae904f7ad6338fe7f2b5008d3d6b
[netatalk.git] / libatalk / dsi / dsi_getsess.c
1 /*
2  * $Id: dsi_getsess.c,v 1.7 2005-04-28 20:50:02 bfernhomberg Exp $
3  *
4  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
5  * All rights reserved. See COPYRIGHT.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif /* HAVE_CONFIG_H */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <errno.h>
16 #include <unistd.h>
17 #include <signal.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20
21 #include <sys/types.h>
22 #include <sys/wait.h>
23 #include <sys/time.h>
24 #include <atalk/logger.h>
25 #include <atalk/util.h>
26
27 #include <atalk/dsi.h>
28 #include <atalk/server_child.h>
29
30 /*!
31  * Start a DSI session, fork an afpd process
32  *
33  * @param childp    (w) after fork: parent return pointer to child, child returns NULL
34  * @returns             0 on sucess, any other value denotes failure
35  */
36 int dsi_getsession(DSI *dsi, server_child *serv_children, int tickleval, afp_child_t **childp)
37 {
38   pid_t pid;
39   unsigned int ipc_fds[2];  
40   afp_child_t *child;
41
42   if (socketpair(PF_UNIX, SOCK_STREAM, 0, ipc_fds) < 0) {
43       LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno));
44       return -1;
45   }
46
47   if (setnonblock(ipc_fds[0], 1) != 0 || setnonblock(ipc_fds[1], 1) != 0) {
48       LOG(log_error, logtype_dsi, "dsi_getsess: setnonblock: %s", strerror(errno));
49       return -1;
50   }
51
52   switch (pid = dsi->proto_open(dsi)) { /* in libatalk/dsi/dsi_tcp.c */
53   case -1:
54     /* if we fail, just return. it might work later */
55     LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno));
56     return -1;
57
58   case 0: /* child. mostly handled below. */
59     break;
60
61   default: /* parent */
62     /* using SIGKILL is hokey, but the child might not have
63      * re-established its signal handler for SIGTERM yet. */
64     close(ipc_fds[1]);
65     if ((child = server_child_add(serv_children, CHILD_DSIFORK, pid, ipc_fds[0])) ==  NULL) {
66       LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno));
67       close(ipc_fds[0]);
68       dsi->header.dsi_flags = DSIFL_REPLY;
69       dsi->header.dsi_code = DSIERR_SERVBUSY;
70       dsi_send(dsi);
71       dsi->header.dsi_code = DSIERR_OK;
72       kill(pid, SIGKILL);
73     }
74     dsi->proto_close(dsi);
75     *childp = child;
76     return 0;
77   }
78   
79   /* child: check number of open connections. this is one off the
80    * actual count. */
81   if ((serv_children->count >= serv_children->nsessions) &&
82       (dsi->header.dsi_command == DSIFUNC_OPEN)) {
83     LOG(log_info, logtype_dsi, "dsi_getsess: too many connections");
84     dsi->header.dsi_flags = DSIFL_REPLY;
85     dsi->header.dsi_code = DSIERR_TOOMANY;
86     dsi_send(dsi);
87     exit(EXITERR_CLNT);
88   }
89
90   /* get rid of some stuff */
91   dsi->AFPobj->ipc_fd = ipc_fds[1];
92   close(ipc_fds[0]);
93   close(dsi->serversock);
94   dsi->serversock = -1;
95   server_child_free(serv_children); 
96
97   switch (dsi->header.dsi_command) {
98   case DSIFUNC_STAT: /* send off status and return */
99     {
100       /* OpenTransport 1.1.2 bug workaround: 
101        *
102        * OT code doesn't currently handle close sockets well. urk.
103        * the workaround: wait for the client to close its
104        * side. timeouts prevent indefinite resource use. 
105        */
106       
107       static struct timeval timeout = {120, 0};
108       fd_set readfds;
109       
110       dsi_getstatus(dsi);
111
112       FD_ZERO(&readfds);
113       FD_SET(dsi->socket, &readfds);
114       free(dsi);
115       select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);    
116       exit(0);
117     }
118     break;
119     
120   case DSIFUNC_OPEN: /* setup session */
121     /* set up the tickle timer */
122     dsi->timer.it_interval.tv_sec = dsi->timer.it_value.tv_sec = tickleval;
123     dsi->timer.it_interval.tv_usec = dsi->timer.it_value.tv_usec = 0;
124     dsi_opensession(dsi);
125     *childp = NULL;
126     return 0;
127
128   default: /* just close */
129     LOG(log_info, logtype_dsi, "DSIUnknown %d", dsi->header.dsi_command);
130     dsi->proto_close(dsi);
131     exit(EXITERR_CLNT);
132   }
133 }