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.
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.
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...
Base class for streams built on Unix file descriptors.
void setfd(int fd)
Sets the file descriptor for both reading and writing.
int getfd() const
Returns the Unix file descriptor for reading and writing.
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.
A type-safe version of WvMonikerBase that lets you provide create functions for object types other th...
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.
A Unix domain socket address is really just a filename.
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...
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.