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