/* @(#)osx.c 17.1.1.1 (ESO-IPG) 01/25/02 17:35:10 */ /*=========================================================================== Copyright (C) 1995 European Southern Observatory (ESO) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning ESO-MIDAS should be addressed as follows: Internet e-mail: midas@eso.org Postal address: European Southern Observatory Data Management Division Karl-Schwarzschild-Strasse 2 D 85748 Garching bei Muenchen GERMANY ===========================================================================*/ /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .TYPE Module .NAME osx.c .LANGUAGE C .AUTHOR IPG-ESO Garching .CATEGORY Host operating system interfaces. Interprocess Communication .COMMENTS Interprocess communication routines. The routines allow the creation of asynchronous communication channels between processes possibly located in different nodes. Three modes of operation are foreseen depending on the communication channel: local communication (fast channel for processes residing in the same node), network communication (medium channel for processes located in processors interconnected with a local area network), The communication channel is open by giving the channel name and the mode of opening. The function returns an integer number used to address the channel. Operations include asynchronous input/output, wait for completion and control operations. The routines return always a non-negative integer number on successful return. Otherwise, a value of -1 is set to indicate an error condition and the variable ``oserror'' contains the symbolic error code. Symbolic error codes for each function to be defined. .HISTORY [1.1] 910828 CG. New implementation. .HISTORY [2.1] 920527 If no S_IFSOCK no local unix sockets. CG. .HISTORY [3.1] 21-Apr-1993 Posix compliant .VERSION 011113 last modif ------------------------------------------------------------*/ /* * Define _POSIX_SOURCE to indicate * that this is a POSIX program * Except for SUN & GNU-SUN & HP-UX & OSF & AIX & ULTRIX & SGI * because the POSIX definition * couses undefines (eg. u_long) in socket includes. */ #if !defined(__hpux) && !defined(sun) && !defined(__sun) && !defined(__osf__) && !defined(_AIX) && !defined(ultrix) && !defined(__sgi) && !defined(__linux__) && !defined(__FreeBSD__) #define _POSIX_SOURCE 1 #endif #include /* ANSI-C prototyping */ #include /* generic definitions */ #include /* sockets definitions */ #include /* stat(2v); */ #ifdef S_IFSOCK /* defined in stat.h if Unix domain sockets */ #include /* struct sockaddr_un (Unix domain) */ #endif /* S_IFSOCK */ #include /* struct sockaddr_in (Internet domain) */ #include /* getservbyname(3n), gethostbyname(3n); */ #include /* struct timeval timeout */ #include /* signal(3v); */ #include /* error definitions */ #include /* error definitions */ #include /* generic definitions */ #include /* function definitions */ #include /* string definitions */ #include /* MIDAS osx definitions */ #ifdef FASTMEM_SET #include #endif #ifndef SUN_LEN #define SUN_LEN(su) (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) #endif static struct ipccstat IPCC[MAX_IPCC]; #define NULL_PTR(x) (x *)0 #define RET_ERROR(x) { oserror=(x); return(-1); } static char msg0[]="OSX: Channel out of table"; static char msg1[]="OSX: Unable to open service"; static char msg2[]="OSX: Unable to open host"; static char msg3[]="OSX: Unknown open mode"; static char msg4[]="OSX: Local sockets not available"; static char msg5[]="OSX: Unable to reuse sockets"; #ifndef FD_SET /* If it is not the Berkeley select */ typedef int osxfds; # define FD_SET(n, p) (*(p) |= (1 << (n)) ) #else typedef fd_set osxfds; #endif static int writen(fd, ptr, nbytes) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Write "n" bytest into a descriptor. Use in place of write() when fd is a stream socket .RETURNS If succesful, the number of bytes actaully written is returned. Otherwise, a -1 is returned. .REMARKS System dependencies: -- UNIX: write(2) ------------------------------------------------------------*/ register int fd; register char *ptr; register int nbytes; { int nleft, nwritten; nleft = nbytes; while (nleft > 0) { nwritten = write(fd, ptr, nleft); if (nwritten <= 0) return(nwritten); /* error */ nleft -= nwritten; ptr += nwritten; } return(nbytes - nleft); } static int readn(fd, ptr, nbytes) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Read "n" bytest from a descriptor. Use in place of read() when fd is a stream socket .RETURNS If succesful, the number of bytes actaully read is returned. Otherwise, a -1 is returned. .REMARKS System dependencies: -- UNIX: read(2) ------------------------------------------------------------*/ register int fd; register char *ptr; register int nbytes; { int nleft, nread; nleft = nbytes; while (nleft > 0) { nread = read(fd, ptr, nleft); if (nread < 0) /* error, return < 0 */ return(nread); else if (nread == 0) /* EOF */ break; nleft -= nread; ptr += nread; } return(nbytes - nleft); /* return >= 0 */ } static int osxstat(fd,sec,usec) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Examines the read status of a file descriptor. The timeout (sec, usec) specifies a maximum interval to wait for data to be available in the descriptor. To effect a poll, the timeout (sec, usec) should be 0. .RETURNS a non-negative value on data available. 0 indicates that the time limit referred by timeout expired. On failure, it returns -1 and errno is set to indicate the error. .REMARKS System dependencies: -- UNIX: select(2) ------------------------------------------------------------*/ register int fd, sec, usec; { int ret; int width; struct timeval timeout; osxfds readfds; memset((char *)&readfds,0,sizeof(readfds)); FD_SET(fd, &readfds); width = fd+1; timeout.tv_sec = sec; timeout.tv_usec = usec; ret = select(width,&readfds,NULL_PTR(osxfds),NULL_PTR(osxfds),&timeout); return(ret); } int osxopen(channame, mode) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Opens communication channel for read or write. The variable mode defines the way of opening the file by constructing the OR of the following flags: - LOCAL (local communication channel) - NETW (communication channel using the network) - IPC_READ (open the channel in READ mode or SERVER) - IPC_WRITE (open the channel in WRITE mode or CLIENT) .RETURNS Upon successful completion a positive number with the channel identification is returned or -1 in case of error. .REMARKS System dependencies: -- UNIX: socket(2), bind(2), listen(2), gethostbyname(3n), getservbyname(3n), mknod(2), open(2) ------------------------------------------------------------*/ register char *channame[2]; /* physical channel name */ register int mode; /* open mode */ { static int cid; static struct stat statbuf; static struct hostent *hp; static struct servent *sp; static struct sockaddr_in pin; struct sigaction act; int sockopt = 1; #ifdef S_IFSOCK /* defined in stat.h if Unix domain sockets */ static struct sockaddr_un pun; #endif /* Ignore 'broken pipes' signal. */ act.sa_handler = SIG_IGN; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (sigaction(SIGPIPE,&act,(struct sigaction *)NULL) != 0) { oserror = errno; return(-1); } /* printf("osxopen: %s mode: %d\n",channame[0],mode); */ switch (mode & (LOCAL|NETW)) { case LOCAL: /* open socket in server mode, locally */ #ifdef S_IFSOCK /* defined in stat.h if Unix domain sockets */ if ( (cid = socket(AF_UNIX, SOCK_STREAM, 0)) < 0 ) RET_ERROR(errno); memset((char *)&pun,0,sizeof(pun)); pun.sun_family = AF_UNIX; (void)strcpy(pun.sun_path, channame[0]); #else oserrmsg = msg4; RET_ERROR(-1); #endif break; case NETW: /* open socket in server mode, locally */ if ( (cid = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) RET_ERROR(errno); memset((char *)&pin,0,sizeof(pin)); pin.sin_family = AF_INET; /* channame[0] could indicate a port number or a service */ if ( (pin.sin_port = atoi(channame[0])) == 0) { /* get service reference */ if ((sp = getservbyname(channame[0],"tcp")) == NULL) { oserrmsg = msg1; (void)close(cid); RET_ERROR(-1); } pin.sin_port = sp->s_port; } /* * allow process to reuse both the IP address and port number */ if (setsockopt(cid, SOL_SOCKET, SO_REUSEADDR, (char *)&sockopt, sizeof(sockopt)) != 0) { oserrmsg = msg5; (void)close(cid); RET_ERROR(-1); } break; default: oserrmsg = msg3; RET_ERROR(-1); } if (cid > MAX_IPCC) { oserrmsg = msg0; (void)close(cid); RET_ERROR(-2); /* make it different from other errors... */ } (void)strcpy(IPCC[cid].chname = malloc((unsigned)strlen(channame[0])+1),channame[0]); IPCC[cid].omode = mode & (IPC_READ|IPC_WRITE); IPCC[cid].type = mode & (LOCAL|NETW); IPCC[cid].accept = 0; switch (mode) { #ifdef S_IFSOCK case LOCAL|IPC_READ: /* open socket in server mode, locally */ /* Unlinking the socket_file if existing */ if(stat(channame[0],&statbuf) == 0) { if ((statbuf.st_mode & S_IFSOCK) == S_IFSOCK || (statbuf.st_mode & S_IFIFO) == S_IFIFO) { if (unlink(channame[0]) == -1) { (void)close(cid); RET_ERROR(errno); } } } if (bind(cid, (struct sockaddr *)&pun, SUN_LEN(&pun)) < 0) { (void)close(cid); RET_ERROR(errno); } /* Start accepting connections */ if (listen(cid, LOCAL_QUEUE_LENGTH) == -1) { (void)close(cid); RET_ERROR(errno); } break; case LOCAL|IPC_WRITE: /* open channel in client mode, locally */ if (connect(cid,(struct sockaddr *)&pun,sizeof(pun)) < 0) { (void)close(cid); RET_ERROR(errno); } IPCC[cid].accept = cid; break; #endif case NETW|IPC_READ: /* open channel in server mode, network */ pin.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(cid,(struct sockaddr *)&pin,sizeof(pin)) < 0) { (void)close(cid); RET_ERROR(errno); } if (listen(cid,NETW_QUEUE_LENGTH) == -1) { (void)close(cid); RET_ERROR(errno) } break; case NETW|IPC_WRITE: /* open channel in client mode, network */ if ((hp = gethostbyname(channame[1])) == NULL) { (void)close(cid); oserrmsg = msg2; RET_ERROR(-1); } memcpy((char *)&pin.sin_addr,hp->h_addr,hp->h_length); if (connect(cid,(struct sockaddr *)&pin,sizeof(pin)) < 0) { (void)close(cid); RET_ERROR(errno); } IPCC[cid].accept = cid; break; default: (void)close(cid); oserrmsg = msg3; RET_ERROR(-1); } /* printf("osxopen: cid: %d\n",cid); */ return(cid); } int osxclose(cid) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Closes interprocess communication channel. The argument cid is the channel identification as obtained from osxopen. .RETURNS Value 0 on normal return. .REMARKS System dependencies: -- UNIX: close(2), unlink(2) ------------------------------------------------------------*/ register int cid; /* channel identification */ { if (cid < 0 || cid > MAX_IPCC ) { oserrmsg = msg0; RET_ERROR(-1); } close(cid); /* close any conection accepted */ if ( IPCC[cid].accept != cid && IPCC[cid].accept != 0) close(IPCC[cid].accept); /* suppress the associated unix socket file */ if ( IPCC[cid].type == LOCAL && IPCC[cid].omode == IPC_READ) if (unlink(IPCC[cid].chname) == -1) RET_ERROR(errno); free(IPCC[cid].chname); return(0); } int osxread(cid, pbuf, nobyt) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Synchronous read from interprocess communication channel. The argument cid is the channel identification as obtained from osxopen. The function reads nobyt bytes from the channel and put the information into the buffer pointed by pbuf. .RETURNS Upon successful completion, osxread return the number of bytes actually read and placed in the buffer. If the returned values is 0, then EOF has been reached. Otherwise, a -1 is returnde and the gloval variable oserror is set to indicate the error. (The function osxwait must be called after the asynchronous read. NOT IMPLEMENTED YET) .REMARKS System dependencies: -- UNIX: accept(2),close(2),read(2) ------------------------------------------------------------*/ int cid; /* channel identification */ char *pbuf; /* pointer to input buffer */ int nobyt; /* number of input bytes */ { int ns, nb; int stat; /* int i, checksum; */ if (cid < 0 || cid > MAX_IPCC ) { oserrmsg = msg0; RET_ERROR(-1); } if (nobyt <= 0) return(0); /* printf("osxread: bytes: %d\n",nobyt); */ if ( (ns = IPCC[cid].accept) == 0) { if ( (stat = osxstat(cid,0,0)) == -1) RET_ERROR(errno); if (stat == 0 ) RET_ERROR(ENOTCONN); if ( (ns = accept(cid, (struct sockaddr *)0, (int *)0)) == -1) RET_ERROR(errno); IPCC[cid].accept=ns; } if ( (nb=readn(ns,pbuf,nobyt)) == -1) RET_ERROR(errno); if ( nb == 0 ) { /* printf("clossing by %d\n",ns); */ if (IPCC[cid].omode == IPC_READ) IPCC[cid].accept = 0; if (close(ns) == -1) RET_ERROR(errno); } /* checksum=0; for (i=0; i MAX_IPCC ) { oserrmsg = msg0; RET_ERROR(-1); } /* Here, the connection is accepted */ /* printf("osxwrite: %d write bytes.\n",nobyt); */ if ( (ns = IPCC[cid].accept) == 0) { if ( (stat = osxstat(cid,0,0)) == -1) RET_ERROR(errno); if (stat == 0 ) RET_ERROR(ENOTCONN); if ( (ns = accept(cid, (struct sockaddr *)0, (int *)0)) == -1) RET_ERROR(errno); IPCC[cid].accept=ns; } if ((nb=writen(ns,pbuf,nobyt)) < 0) { if (IPCC[cid].omode == IPC_READ) { IPCC[cid].accept = 0; if (close(ns) == -1) RET_ERROR(errno); RET_ERROR(ENOTCONN); } } /* checksum=0; for (i=0; i MAX_IPCC ) { oserrmsg = msg0; RET_ERROR(-1); } if ( (ns = IPCC[cid].accept) == 0) { if ( (stat = osxstat(cid,sec,usec)) == -1) RET_ERROR(errno); if (stat == 0 ) return(NOCONN); if ( (ns = accept(cid, (struct sockaddr *)0, (int *)0)) == -1) RET_ERROR(errno); IPCC[cid].accept=ns; } if ( (stat = osxstat(ns,sec,usec)) < 0) return(NOCONN); return(stat ? DATARDY : NODATA ); } int osxgetservbyname(service) /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Interface to the getservbyname(). .RETURNS The port number, or -1 if no port. .REMARKS System dependencies: -- UNIX: getservbyname ------------------------------------------------------------*/ char *service; { struct servent *sp; if ((sp = getservbyname(service,"tcp")) == NULL) return(-1); else return(sp->s_port); }