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