WvStreams
wvunixsocket.cc
1/*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4 *
5 * WvStream-based Unix domain socket connection class. See wvunixsocket.h.
6 */
7#include "wvistreamlist.h"
8#include "wvunixlistener.h"
9#include "wvunixsocket.h"
10#include "wvstringmask.h"
11#include "wvmoniker.h"
12#include "wvlinkerhack.h"
13
14#if HAVE_ERRNO_H
15# include <errno.h>
16#endif
17#include <stdio.h>
18#if HAVE_SYS_TYPES_H
19# include <sys/types.h>
20#endif
21#if STDC_HEADERS
22# include <stdlib.h>
23# include <stddef.h>
24#else
25# if HAVE_STDLIB_H
26# include <stdlib.h>
27# endif
28#endif
29#if HAVE_SYS_STAT_H
30# include <sys/stat.h>
31#endif
32#if HAVE_SYS_SOCKET_H
33# include <sys/socket.h>
34#endif
35#if HAVE_NETDB_H
36# include <netdb.h>
37#endif
38#if HAVE_NETINET_IN_H
39# include <netinet/in.h>
40#endif
41#if HAVE_NETINET_IP_H
42# if HAVE_NETINET_IN_SYSTM_H
43# include <netinet/in_systm.h>
44# endif
45# include <netinet/ip.h>
46#endif
47#if HAVE_NETINET_TCP_H
48# include <netinet/tcp.h>
49#endif
50
51#include <fcntl.h>
52#include <sys/un.h>
53
54WV_LINK(WvUnixConn);
55WV_LINK(WvUnixListener);
56
57static IWvStream *creator(WvStringParm s, IObject*)
58{
59 return new WvUnixConn(s);
60}
61
62static WvMoniker<IWvStream> reg("unix", creator);
63
64
65static IWvListener *listener(WvStringParm s, IObject *)
66{
68 WvString path = wvtcl_getword(b);
69 WvString wrapper = b.getstr();
70 IWvListener *l = new WvUnixListener(path, 0777);
71 if (l && !!wrapper)
72 l->addwrap(wv::bind(&IWvStream::create, wrapper, _1));
73 return l;
74}
75
76static IWvListener *modelistener(WvStringParm s, IObject *)
77{
79
80 // strtoul knows how to interpret octal if it starts with '0'
81 int mode = strtoul(wvtcl_getword(b, WvStringMask(":")), NULL, 0);
82 if (b.peekch() == ':')
83 b.get(1);
84 WvString path = wvtcl_getword(b);
85 WvString wrapper = b.getstr();
86 IWvListener *l = new WvUnixListener(path, mode);
87 if (l && !!wrapper)
88 l->addwrap(wv::bind(&IWvStream::create, wrapper, _1));
89 return l;
90}
91
92static WvMoniker<IWvListener> lreg("unix", listener);
93static WvMoniker<IWvListener> lmodereg("unixmode", modelistener);
94
95
96WvUnixConn::WvUnixConn(int _fd, const WvUnixAddr &_addr)
97 : WvFDStream(_fd), addr(_addr)
98{
99 // all is well and we're connected.
100 set_nonblock(true);
101 set_close_on_exec(true);
102}
103
104
106 : addr(_addr)
107{
108 setfd(socket(PF_UNIX, SOCK_STREAM, 0));
109 if (getfd() < 0)
110 {
111 seterr(errno);
112 return;
113 }
114
115 // Make the socket non-blocking and close-on-exec.
116 fcntl(getfd(), F_SETFD, FD_CLOEXEC);
117 fcntl(getfd(), F_SETFL, O_RDWR|O_NONBLOCK);
118
119 sockaddr *sa = addr.sockaddr();
120 if (connect(getfd(), sa, addr.sockaddr_len()) < 0)
121 seterr(errno);
122 delete sa;
123
124 // all is well and we're connected.
125 set_nonblock(true);
126 set_close_on_exec(true);
127}
128
129
130WvUnixConn::~WvUnixConn()
131{
132 // we don't want to delete the socket file here; that's a job for the
133 // listener class.
134
135 // close the socket
136 close();
137}
138
139
141{
142 return &addr;
143}
144
145
146WvUnixListener::WvUnixListener(const WvUnixAddr &_addr, int create_mode)
147 : WvListener(new WvFdStream(socket(PF_UNIX, SOCK_STREAM, 0))),
148 addr(_addr)
149{
150 WvFdStream *fds = (WvFdStream *)cloned;
151
152 mode_t oldmask;
153 bound_okay = false;
154
155 if (getfd() < 0)
156 {
157 // error inherited from substream
158 return;
159 }
160
161 fds->set_close_on_exec(true);
162 fds->set_nonblock(true);
163
164 sockaddr *sa = addr.sockaddr();
165 size_t salen = addr.sockaddr_len();
166
167 if (connect(getfd(), sa, salen) == 0) // successful connect?!
168 seterr(EADDRINUSE); // someone is using this already!
169 else
170 {
171 // unfortunately we have to change the umask here to make the
172 // create_mode work, because bind() doesn't take extra arguments
173 // like open() does. However, we don't actually want to _cancel_
174 // the effects of umask, only add to them; so the original umask is
175 // or'ed into ~create_mode. This way it acts like open().
176 oldmask = umask(0777); // really just reading the old umask here
177 umask(oldmask | ((~create_mode) & 0777));
178
179 ::unlink(WvString(addr));
180
181 if (bind(getfd(), sa, salen) || listen(getfd(), 50))
182 seterr(errno);
183 else
184 bound_okay = true;
185
186 umask(oldmask);
187 }
188
189 delete sa;
190}
191
192
193WvUnixListener::~WvUnixListener()
194{
195 close();
196}
197
198
199void WvUnixListener::close()
200{
201 // delete the socket _before_ closing it. Unix will keep
202 // existing connections around anyway (if any), but if it's idle, then
203 // we never have an existing not-in-use socket inode.
204 if (bound_okay)
205 {
206 WvString filename(addr);
207 ::unlink(filename);
208 }
209
210 WvListener::close();
211}
212
213
215{
216 struct sockaddr_un saun;
217 socklen_t len = sizeof(saun);
218
219 if (!isok()) return NULL;
220
221 int newfd = ::accept(getfd(), (struct sockaddr *)&saun, &len);
222 if (newfd >= 0)
223 return wrap(new WvUnixConn(newfd, addr));
224 else if (errno == EAGAIN || errno == EINTR)
225 return NULL; // this listener is doing weird stuff
226 else
227 {
228 seterr(errno);
229 return NULL;
230 }
231}
232
233
234void WvUnixListener::accept_callback(WvIStreamList *list,
235 wv::function<void(IWvStream*)> cb,
236 IWvStream *_conn)
237{
238 WvStreamClone *conn = new WvStreamClone(_conn);
239 conn->setcallback(wv::bind(cb, conn));
240 list->append(conn, true, "WvUnixConn");
241}
242
243
245{
246 return &addr;
247}
248
The basic interface which is included by all other XPLC interfaces and objects.
Definition IObject.h:65
virtual void addwrap(IWvListenerWrapper _wrapper)=0
Add a wrapper function for this stream: something that accept() will call to possibly wrap the stream...
A raw memory read-only buffer backed by a constant WvString.
Definition wvbuf.h:242
virtual void seterr(int _errnum)
Set the errnum variable – we have an error.
Definition wverror.cc:144
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
Definition wvstring.h:94
Base class for streams built on Unix file descriptors.
Definition wvfdstream.h:21
void setfd(int fd)
Sets the file descriptor for both reading and writing.
Definition wvfdstream.h:36
int getfd() const
Returns the Unix file descriptor for reading and writing.
Definition wvfdstream.h:81
void set_nonblock(bool nonblock)
Make the fds on this stream blocking or non-blocking.
Definition wvfdstream.cc:97
virtual void close()
Closes the file descriptors.
void set_close_on_exec(bool close_on_exec)
Make the fds on this stream close-on-exec or not.
WvStreamList holds a list of WvStream objects – and its select() and callback() functions know how to...
virtual bool isok() const
By default, returns true if geterr() == 0.
Definition wvlistener.h:38
A type-safe version of WvMonikerBase that lets you provide create functions for object types other th...
Definition wvmoniker.h:62
WvStreamClone simply forwards all requests to the "cloned" stream.
void setcallback(IWvStreamCallback _callfunc)
define the callback function for this stream, called whenever the callback() member is run,...
Definition wvstream.cc:1129
virtual void seterr(int _errnum)
Override seterr() from WvError so that it auto-closes the stream.
Definition wvstream.cc:451
A class used to provide a masked lookup for characters in a string.
WvString is an implementation of a simple and efficient printable-string class.
Definition wvstring.h:330
A Unix domain socket address is really just a filename.
Definition wvaddr.h:430
WvStream-based Unix domain socket connection class.
WvUnixConn(int _fd, const WvUnixAddr &_addr)
connect an already-open socket (used by WvUnixListener)
virtual const WvUnixAddr * src() const
return the remote address (source of all incoming packets), which is a constant for any given connect...
Server end of a Unix Sockets stream.
virtual const WvUnixAddr * src() const
src() is a bit of a misnomer, but it returns the socket address.
IWvStream * accept()
return a new WvUnixConn socket corresponding to a newly-accepted connection.
WvString wvtcl_getword(WvBuf &buf, const WvStringMask &splitchars=WVTCL_SPLITCHARS, bool do_unescape=true)
Get a single tcl word from an input buffer, and return the rest of the buffer untouched.