/* * Copyright (c) 1996 Gunther Schadow. All rights reserved. * * 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 Mass Ave, Cambridge, MA 02139, USA. */ #pragma implementation #include "UNSSocket.h" #include #include #include #include #include #ifdef PROTOGEN # include "exception.h" #else # include # include # define ERROR(fmt, args...) ({fprintf(stderr, fmt "\n" , ## args); exit(1);}) # define ERROS(fmt, args...) ({fprintf(stderr, fmt ": " , ## args); \ perror(""); exit(1);}) # define FATAL(fmt, args...) ({fprintf(stderr, fmt "\n" , ## args); abort();}) # define WARNING(fmt, args...) ({ fprintf(stderr, fmt "\n" , ## args); }) #endif #include /********************************************************************** * address resolving **********************************************************************/ void UNSSocket::parse_args(const char *s, int l) { if(l == -1) l = strlen(s); const char *addr_p = NULL; int addr_l = 0; if(l == 0) ERROR("must specify an unix socket address"); while(l > 0 && isspace(*s)) { s++; l--; } addr_p = s; while(l > 0) { s++; l--; addr_l++; } for(const char *p = &addr_p[addr_l - 1]; isspace(*p); p--) { addr_l--; } char addr[addr_l + 1]; memcpy(addr, addr_p, addr_l); addr[addr_l] = 0; bzero(&_saddr, sizeof(_saddr)); strncpy(_saddr.sun_path, addr, sizeof(_saddr.sun_path)); _saddr.sun_len = addr_l + sizeof(_saddr.sun_family); _saddr.sun_family = AF_UNIX; } /********************************************************************** * ctor, dtor, and copy methods **********************************************************************/ UNSSocket::UNSSocket(const char *, const char *param, size_t parlen) : Socket(), _sigpipe(SIGPIPE) { _sd = -1; _saddr_bound = false; _sigpipe.ignore(); _state_plus |= accept_able; parse_args(param, parlen); // sets saddr; bzero(&_paddr, sizeof(_paddr)); } UNSSocket::UNSSocket() : Socket(), _sigpipe(SIGPIPE) { _sd = -1; _saddr_bound = false; bzero(&_saddr, sizeof(_saddr)); bzero(&_paddr, sizeof(_paddr)); } UNSSocket::~UNSSocket() { if(_sd != -1) ::close(_sd); if(_saddr_bound) unlink(_saddr.sun_path); } Socket *UNSSocket::ctor(const char *address, const char *param, size_t parlen) { return new UNSSocket(address, param, parlen); } Socket *UNSSocket::ctor() { return new UNSSocket(); } /********************************************************************** * socket methods **********************************************************************/ bool UNSSocket::connect(flags_t mode) { if(_sd != -1) { ::close(_sd); } _sd = ::socket(AF_UNIX, SOCK_STREAM, 0); if(_sd == -1) ERROS("socket"); if(::connect(_sd, (struct sockaddr*)&_saddr, sizeof(_saddr)) == -1) ERROS("connect"); _state_plus |= connected | send_recv & ~eox_any; if(!(mode & send_mode)) shutdown(send_mode); if(!(mode & recv_mode)) shutdown(recv_mode); return true; } Socket *UNSSocket::accept(flags_t mode) { if(! (_state_plus & accept_able)) ERROR("illegal operation"); if(_sd == -1) { _sd = socket(AF_UNIX, SOCK_STREAM, 0); if(_sd == -1) ERROS("socket"); if(::bind(_sd, (struct sockaddr *)&_saddr, sizeof(_saddr)) == -1) ERROS("bind"); _saddr_bound = true; if(::listen(_sd, 5) == -1) ERROS("listen"); } int sd; struct sockaddr_un paddr; int len = sizeof(paddr); bzero(&paddr, len); sd = ::accept(_sd, (struct sockaddr *)&paddr, &len); if(sd == -1) ERROS("accept"); UNSSocket *ps = new UNSSocket(); ps->_sd = sd; ps->_paddr = paddr; ps->_saddr =_saddr; ps->_state_plus = _state_plus & ~accept_able | connected | send_recv & ~eox_any; if(!(mode & send_mode)) ps->shutdown(send_mode); if(!(mode & recv_mode)) ps->shutdown(recv_mode); return ps; } size_t UNSSocket::send(const char *buf, size_t len, int msgf) { size_t n = 0; // filter out the cases where SIGPIPE wouldn't be appropriate if(! ((len == 0) && (msgf & (msg_eot | msg_eom)))) n = ::send(_sd, buf, len, msgf & ~(msg_eof | msg_eot)); if(n == (size_t)-1) ERROS("send failed"); if( msgf & ( msg_eom | msg_eom ) ) { if( msgf & msg_eom ) { // never shut down write end twice, since this can close the read end! if(! ( _state_plus & ( eom_placed | eot_placed ) ) ) shutdown(send_mode); _state_plus |= eom_placed; } if( msgf & msg_eot ) { // never shut down write end twice, since this can close the read end! if(! ( _state_plus & ( eom_placed | eot_placed ) ) ) shutdown(send_mode); _state_plus |= eot_placed; } } return n; } size_t UNSSocket::recv(char *buf, size_t max, int msgf) { size_t n = ::recv(_sd, buf, max, msgf); if(n == (size_t)-1) ERROS("recv failed"); if( n == 0 ) _state_plus |= eom_seen | eot_seen; return n; } void UNSSocket::shutdown(flags_t mode) { if(mode & recv_mode) ::shutdown(_sd, 0); else if(mode & send_mode) ::shutdown(_sd, 1); else ::shutdown(_sd, 2); _state_plus = _state_plus & ~( mode & send_recv ); } void UNSSocket::close(flags_t) { if(_sd != -1) { ::close(_sd); _sd = -1; } _state_plus &= ~( connected | send_recv ); } bool UNSSocket::async(bool on, pid_t pid) { bool old = _state_plus & async_mode; int flags; if(fcntl(_sd, F_GETFL, &flags) == -1) ERROS("fcntl:F_GETFL:"); if(on) { if(pid != 0) if(fcntl(_sd, F_SETOWN, &pid) == -1) ERROS("fcntl:F_SETOWN:"); flags |= O_ASYNC; _state_plus |= async_mode; if(fcntl(_sd, F_SETFL, &flags) == -1) ERROS("fcntl:F_SETFL:"); } else // off { flags &= ~O_ASYNC; if(fcntl(_sd, F_SETFL, &flags) == -1) ERROS("fcntl:F_SETFL:"); _state_plus &= ~async_mode; } return old; } bool UNSSocket::noblock(bool on) { bool old = _state_plus & noblock_mode; int flags; if(fcntl(_sd, F_GETFL, &flags) == -1) ERROS("fcntl:F_GETFL:"); if(on) { flags |= O_NONBLOCK; _state_plus |= noblock_mode; if(fcntl(_sd, F_SETFL, &flags) == -1) ERROS("fcntl:F_SETOWN:"); } else // off { flags &= ~O_NONBLOCK; if(fcntl(_sd, F_SETFL, &flags) == -1) ERROS("fcntl:F_SETFL:"); _state_plus &= ~noblock_mode; } return old; } bool UNSSocket::is_sendready(int sec, int usec) const { FdSet rfds; FdSet tfds(_sd, -1); select_again: int n = tfds.select(NULL, &rfds, NULL, sec, usec); if(n == -1) if(errno == EINTR) goto select_again; else ERROS("select"); return n != 0; } bool UNSSocket::is_recvready(int sec, int usec) const { FdSet rfds; FdSet tfds(_sd, -1); select_again: int n = tfds.select(&rfds, NULL, NULL, sec, usec); if(n == -1) if(errno == EINTR) goto select_again; else ERROS("select"); return n != 0; } bool UNSSocket::is_exception(int sec, int usec) const { FdSet rfds; FdSet tfds(_sd, -1); select_again: int n = tfds.select(NULL, NULL, &rfds, sec, usec); if(n == -1) if(errno == EINTR) goto select_again; else ERROS("select"); return n != 0; }