WvStreams
wvunixsocket.cc
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  * 
00005  * WvStream-based Unix domain socket connection class.  See wvunixsocket.h.
00006  */
00007 #include "wvistreamlist.h"
00008 #include "wvunixlistener.h"
00009 #include "wvunixsocket.h"
00010 #include "wvstringmask.h"
00011 #include "wvmoniker.h"
00012 #include "wvlinkerhack.h"
00013 
00014 #if HAVE_ERRNO_H
00015 # include <errno.h>
00016 #endif
00017 #include <stdio.h>
00018 #if HAVE_SYS_TYPES_H
00019 # include <sys/types.h>
00020 #endif
00021 #if STDC_HEADERS
00022 # include <stdlib.h>
00023 # include <stddef.h>
00024 #else
00025 # if HAVE_STDLIB_H
00026 #  include <stdlib.h>
00027 # endif
00028 #endif
00029 #if HAVE_SYS_STAT_H
00030 # include <sys/stat.h>
00031 #endif
00032 #if HAVE_SYS_SOCKET_H
00033 # include <sys/socket.h>
00034 #endif
00035 #if HAVE_NETDB_H
00036 # include <netdb.h>
00037 #endif
00038 #if HAVE_NETINET_IN_H
00039 # include <netinet/in.h>
00040 #endif
00041 #if HAVE_NETINET_IP_H
00042 # if HAVE_NETINET_IN_SYSTM_H
00043 #  include <netinet/in_systm.h>
00044 # endif
00045 # include <netinet/ip.h>
00046 #endif
00047 #if HAVE_NETINET_TCP_H
00048 # include <netinet/tcp.h>
00049 #endif
00050 
00051 #include <fcntl.h>
00052 #include <sys/un.h>
00053 
00054 WV_LINK(WvUnixConn);
00055 WV_LINK(WvUnixListener);
00056 
00057 static IWvStream *creator(WvStringParm s, IObject*)
00058 {
00059     return new WvUnixConn(s);
00060 }
00061 
00062 static WvMoniker<IWvStream> reg("unix", creator);
00063 
00064 
00065 static IWvListener *listener(WvStringParm s, IObject *)
00066 {
00067     WvConstStringBuffer b(s);
00068     WvString path = wvtcl_getword(b);
00069     WvString wrapper = b.getstr();
00070     IWvListener *l = new WvUnixListener(path, 0777);
00071     if (l && !!wrapper)
00072         l->addwrap(wv::bind(&IWvStream::create, wrapper, _1));
00073     return l;
00074 }
00075 
00076 static IWvListener *modelistener(WvStringParm s, IObject *)
00077 {
00078     WvConstStringBuffer b(s);
00079     
00080     // strtoul knows how to interpret octal if it starts with '0'
00081     int mode = strtoul(wvtcl_getword(b, WvStringMask(":")), NULL, 0);
00082     if (b.peekch() == ':')
00083         b.get(1);
00084     WvString path = wvtcl_getword(b);
00085     WvString wrapper = b.getstr();
00086     IWvListener *l = new WvUnixListener(path, mode);
00087     if (l && !!wrapper)
00088         l->addwrap(wv::bind(&IWvStream::create, wrapper, _1));
00089     return l;
00090 }
00091 
00092 static WvMoniker<IWvListener> lreg("unix", listener);
00093 static WvMoniker<IWvListener> lmodereg("unixmode", modelistener);
00094 
00095 
00096 WvUnixConn::WvUnixConn(int _fd, const WvUnixAddr &_addr)
00097     : WvFDStream(_fd), addr(_addr)
00098 {
00099     // all is well and we're connected.
00100     set_nonblock(true);
00101     set_close_on_exec(true);
00102 }
00103 
00104 
00105 WvUnixConn::WvUnixConn(const WvUnixAddr &_addr)
00106     : addr(_addr)
00107 {
00108     setfd(socket(PF_UNIX, SOCK_STREAM, 0));
00109     if (getfd() < 0)
00110     {
00111         seterr(errno);
00112         return;
00113     }
00114     
00115     // Make the socket non-blocking and close-on-exec.
00116     fcntl(getfd(), F_SETFD, FD_CLOEXEC);
00117     fcntl(getfd(), F_SETFL, O_RDWR|O_NONBLOCK);
00118     
00119     sockaddr *sa = addr.sockaddr();
00120     if (connect(getfd(), sa, addr.sockaddr_len()) < 0)
00121         seterr(errno);
00122     delete sa;
00123     
00124     // all is well and we're connected.
00125     set_nonblock(true);
00126     set_close_on_exec(true);
00127 }
00128 
00129 
00130 WvUnixConn::~WvUnixConn()
00131 {
00132     // we don't want to delete the socket file here; that's a job for the
00133     // listener class.
00134     
00135     // close the socket
00136     close();
00137 }
00138 
00139 
00140 const WvUnixAddr *WvUnixConn::src() const
00141 {
00142     return &addr;
00143 }
00144 
00145 
00146 WvUnixListener::WvUnixListener(const WvUnixAddr &_addr, int create_mode)
00147         : WvListener(new WvFdStream(socket(PF_UNIX, SOCK_STREAM, 0))),
00148           addr(_addr)
00149 {
00150     WvFdStream *fds = (WvFdStream *)cloned;
00151     
00152     mode_t oldmask;
00153     bound_okay = false;
00154     
00155     if (getfd() < 0)
00156     {
00157         // error inherited from substream
00158         return;
00159     }
00160     
00161     fds->set_close_on_exec(true);
00162     fds->set_nonblock(true);
00163 
00164     sockaddr *sa = addr.sockaddr();
00165     size_t salen = addr.sockaddr_len();
00166     
00167     if (connect(getfd(), sa, salen) == 0) // successful connect?!
00168         seterr(EADDRINUSE); // someone is using this already!
00169     else
00170     {
00171         // unfortunately we have to change the umask here to make the
00172         // create_mode work, because bind() doesn't take extra arguments
00173         // like open() does. However, we don't actually want to _cancel_
00174         // the effects of umask, only add to them; so the original umask is
00175         // or'ed into ~create_mode.  This way it acts like open().
00176         oldmask = umask(0777); // really just reading the old umask here
00177         umask(oldmask | ((~create_mode) & 0777));
00178         
00179         ::unlink(WvString(addr));
00180         
00181         if (bind(getfd(), sa, salen) || listen(getfd(), 50))
00182             seterr(errno);
00183         else
00184             bound_okay = true;
00185 
00186         umask(oldmask);
00187     }
00188     
00189     delete sa;
00190 }
00191 
00192 
00193 WvUnixListener::~WvUnixListener()
00194 {
00195     close();
00196 }
00197 
00198 
00199 void WvUnixListener::close()
00200 {
00201     // delete the socket _before_ closing it.  Unix will keep
00202     // existing connections around anyway (if any), but if it's idle, then
00203     // we never have an existing not-in-use socket inode.
00204     if (bound_okay)
00205     {
00206         WvString filename(addr);
00207         ::unlink(filename);
00208     }
00209     
00210     WvListener::close();
00211 }
00212 
00213 
00214 IWvStream *WvUnixListener::accept()
00215 {
00216     struct sockaddr_un saun;
00217     socklen_t len = sizeof(saun);
00218     
00219     if (!isok()) return NULL;
00220     
00221     int newfd = ::accept(getfd(), (struct sockaddr *)&saun, &len);
00222     if (newfd >= 0)
00223         return wrap(new WvUnixConn(newfd, addr));
00224     else if (errno == EAGAIN || errno == EINTR)
00225         return NULL; // this listener is doing weird stuff
00226     else
00227     {
00228         seterr(errno);
00229         return NULL;
00230     }
00231 }
00232 
00233 
00234 void WvUnixListener::auto_accept(WvIStreamList *list,
00235                                 wv::function<void(IWvStream*)> cb)
00236 {
00237     onaccept(wv::bind(&WvUnixListener::accept_callback, this, list,
00238                          cb, _1));
00239 }
00240 
00241 void WvUnixListener::auto_accept(wv::function<void(IWvStream*)> cb)
00242 {
00243     auto_accept(&WvIStreamList::globallist, cb);
00244 }
00245 
00246 
00247 void WvUnixListener::accept_callback(WvIStreamList *list,
00248                                     wv::function<void(IWvStream*)> cb,
00249                                     IWvStream *_conn)
00250 {
00251     WvStreamClone *conn = new WvStreamClone(_conn);
00252     conn->setcallback(wv::bind(cb, conn));
00253     list->append(conn, true, "WvUnixConn");
00254 }
00255 
00256 
00257 const WvUnixAddr *WvUnixListener::src() const
00258 {
00259     return &addr;
00260 }
00261