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/* POSIX.1 sys/wait.h check */ 22#include <sys/types.h> 23#ifdef HAVE_SYS_WAIT_H 24#include <sys/wait.h> 25#endif /* HAVE_SYS_WAIT_H */ 26#ifndef WEXITSTATUS 27#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) 28#endif /* ! WEXITSTATUS */ 29#ifndef WIFEXITED 30#define WIFEXITED(stat_val) (((stat_val) & 255) == 0) 31#endif /* ! WIFEXITED */ 32 33#include <sys/time.h> 34#include <atalk/logger.h> 35#include <atalk/util.h> 36 37#include <atalk/dsi.h> 38#include <atalk/server_child.h> 39 40/* hand off the command. return child connection to the main program */ 41afp_child_t *dsi_getsession(DSI *dsi, server_child *serv_children, int tickleval) 42{ 43 pid_t pid; 44 unsigned int ipc_fds[2]; 45 afp_child_t *child; 46 47 if (socketpair(PF_UNIX, SOCK_STREAM, 0, ipc_fds) < 0) { 48 LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno)); 49 exit( EXITERR_CLNT ); 50 } 51 52 if (setnonblock(ipc_fds[0], 1) != 0 || setnonblock(ipc_fds[1], 1) != 0) { 53 LOG(log_error, logtype_dsi, "dsi_getsess: setnonblock: %s", strerror(errno)); 54 exit(EXITERR_CLNT); 55 } 56 57 switch (pid = dsi->proto_open(dsi)) { /* in libatalk/dsi/dsi_tcp.c */ 58 case -1: 59 /* if we fail, just return. it might work later */ 60 LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno)); 61 return NULL; 62 63 case 0: /* child. mostly handled below. */ 64 break; 65 66 default: /* parent */ 67 /* using SIGKILL is hokey, but the child might not have 68 * re-established its signal handler for SIGTERM yet. */ 69 close(ipc_fds[1]); 70 if ((child = server_child_add(serv_children, CHILD_DSIFORK, pid, ipc_fds[0])) == NULL) { 71 LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno)); 72 close(ipc_fds[0]); 73 dsi->header.dsi_flags = DSIFL_REPLY; 74 dsi->header.dsi_code = DSIERR_SERVBUSY; 75 dsi_send(dsi); 76 dsi->header.dsi_code = DSIERR_OK; 77 kill(pid, SIGKILL); 78 } 79 dsi->proto_close(dsi); 80 return child; 81 } 82 83 /* child: check number of open connections. this is one off the 84 * actual count. */ 85 if ((serv_children->count >= serv_children->nsessions) && 86 (dsi->header.dsi_command == DSIFUNC_OPEN)) { 87 LOG(log_info, logtype_dsi, "dsi_getsess: too many connections"); 88 dsi->header.dsi_flags = DSIFL_REPLY; 89 dsi->header.dsi_code = DSIERR_TOOMANY; 90 dsi_send(dsi); 91 exit(EXITERR_CLNT); 92 } 93 94 /* get rid of some stuff */ 95 dsi->AFPobj->ipc_fd = ipc_fds[1]; 96 close(ipc_fds[0]); 97 close(dsi->serversock); 98 server_child_free(serv_children); 99 100 switch (dsi->header.dsi_command) { 101 case DSIFUNC_STAT: /* send off status and return */ 102 { 103 /* OpenTransport 1.1.2 bug workaround: 104 * 105 * OT code doesn't currently handle close sockets well. urk. 106 * the workaround: wait for the client to close its 107 * side. timeouts prevent indefinite resource use. 108 */ 109 110 static struct timeval timeout = {120, 0}; 111 fd_set readfds; 112 113 dsi_getstatus(dsi); 114 115 FD_ZERO(&readfds); 116 FD_SET(dsi->socket, &readfds); 117 free(dsi); 118 select(FD_SETSIZE, &readfds, NULL, NULL, &timeout); 119 exit(0); 120 } 121 break; 122 123 case DSIFUNC_OPEN: /* setup session */ 124 /* set up the tickle timer */ 125 dsi->timer.it_interval.tv_sec = dsi->timer.it_value.tv_sec = tickleval; 126 dsi->timer.it_interval.tv_usec = dsi->timer.it_value.tv_usec = 0; 127 signal(SIGPIPE, SIG_IGN); /* we catch these ourselves */ 128 dsi_opensession(dsi); 129 return NULL; 130 131 default: /* just close */ 132 LOG(log_info, logtype_dsi, "DSIUnknown %d", dsi->header.dsi_command); 133 dsi->proto_close(dsi); 134 exit(EXITERR_CLNT); 135 } 136} 137