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 SIGQUIT is hokey, but the child might not have 68 * re-established its signal handler for SIGTERM yet. */ 69 if ((child = server_child_add(serv_children, CHILD_DSIFORK, pid, ipc_fds)) == NULL) { 70 LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno)); 71 dsi->header.dsi_flags = DSIFL_REPLY; 72 dsi->header.dsi_code = DSIERR_SERVBUSY; 73 dsi_send(dsi); 74 dsi->header.dsi_code = DSIERR_OK; 75 kill(pid, SIGQUIT); 76 } 77 dsi->proto_close(dsi); 78 return child; 79 } 80 81 /* child: check number of open connections. this is one off the 82 * actual count. */ 83 if ((serv_children->count >= serv_children->nsessions) && 84 (dsi->header.dsi_command == DSIFUNC_OPEN)) { 85 LOG(log_info, logtype_dsi, "dsi_getsess: too many connections"); 86 dsi->header.dsi_flags = DSIFL_REPLY; 87 dsi->header.dsi_code = DSIERR_TOOMANY; 88 dsi_send(dsi); 89 exit(EXITERR_CLNT); 90 } 91 92 /* get rid of some stuff */ 93 close(dsi->serversock); 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 signal(SIGPIPE, SIG_IGN); /* we catch these ourselves */ 124 dsi_opensession(dsi); 125 if ((child = calloc(1, sizeof(afp_child_t))) == NULL) 126 exit(EXITERR_SYS); 127 child->ipc_fds[1] = ipc_fds[1]; 128 return child; 129 break; 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