WvStreams
|
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