set should work again
[nyatools.git] / sockchan.d
blob4aa2f15c9f0bef1937f3bf1220265f1f777103de
1 /* Written by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module sockchan is aliced;
19 import iv.vfs;
22 // ////////////////////////////////////////////////////////////////////////// //
23 struct UDSocket {
24 private:
25 static struct UDSData {
26 uint rc;
27 int fd;
28 uint bytesSent;
29 uint bytesReceived;
30 bool didlisten;
31 bool dontclose;
32 @disable this (this);
35 private:
36 usize udsp;
38 void decRef () nothrow @nogc {
39 if (!udsp) return;
40 auto uds = cast(UDSData*)udsp;
41 if (--uds.rc == 0) {
42 import core.stdc.stdlib : free;
43 import core.sys.posix.unistd : close;
44 if (!uds.dontclose) close(uds.fd);
45 free(uds);
47 udsp = 0;
50 public:
51 this (this) nothrow @nogc { pragma(inline, true); if (udsp) ++(cast(UDSData*)udsp).rc; }
52 ~this () nothrow @nogc { pragma(inline, true); if (udsp) close(); }
54 void opAssign (UDSocket sk) {
55 pragma(inline, true);
56 if (sk.udsp) ++(cast(UDSData*)sk.udsp).rc;
57 close();
58 udsp = sk.udsp;
61 @property bool isOpen () const nothrow @trusted @nogc { pragma(inline, true); return (udsp != 0); }
62 @property int fd () const nothrow @trusted @nogc { pragma(inline, true); return (udsp != 0 ? (cast(UDSData*)udsp).fd : -1); }
64 void close () nothrow @nogc { pragma(inline, true); if (udsp) decRef(); }
65 void create (const(char)[] name) { doCC!"server"(name); }
66 void connect (const(char)[] name) { doCC!"client"(name); }
68 @property uint bytesSent () const nothrow @trusted @nogc { pragma(inline, true); return (udsp != 0 ? (cast(UDSData*)udsp).bytesSent : 0); }
69 @property uint bytesReceived () const nothrow @trusted @nogc { pragma(inline, true); return (udsp != 0 ? (cast(UDSData*)udsp).bytesReceived : 0); }
71 @property void resetBytesSent () nothrow @trusted @nogc { pragma(inline, true); if (udsp != 0) (cast(UDSData*)udsp).bytesSent = 0; }
72 @property void resetBytesReceived () nothrow @trusted @nogc { pragma(inline, true); if (udsp != 0) (cast(UDSData*)udsp).bytesReceived = 0; }
74 void listen () {
75 if (!udsp) throw new Exception("can't listen on closed socket");
76 auto uds = cast(UDSData*)udsp;
77 if (!uds.didlisten) {
78 import core.sys.posix.sys.socket : listen;
79 if (listen(uds.fd, 1) != 0) throw new Exception("listen failed");
80 uds.didlisten = true;
84 UDSocket accept () {
85 listen();
86 auto uds = cast(UDSData*)udsp;
87 assert(uds.didlisten);
88 import core.sys.posix.sys.socket : accept;
89 int cfd = accept(uds.fd, null, null);
90 if (cfd == -1) throw new Exception("accept failed");
91 UDSocket res;
92 res.assignFD(cfd);
93 return res;
96 // detach fd
97 int detach () {
98 if (!udsp) throw new Exception("can't detach closed socket");
99 auto uds = cast(UDSData*)udsp;
100 int rfd = uds.fd;
101 uds.dontclose = true;
102 close();
103 return rfd;
106 void[] rawRead (void[] buf) {
107 import core.sys.posix.sys.socket : recv;
108 if (!udsp) throw new Exception("can't read from closed socket");
109 auto uds = cast(UDSData*)udsp;
110 if (buf.length == 0) return buf[];
111 auto rd = recv(uds.fd, buf.ptr, buf.length, 0);
112 if (rd < 0) throw new Exception("socket read error");
113 uds.bytesReceived += rd;
114 return buf[0..rd];
117 void rawWrite (const(void)[] buf) {
118 import core.sys.posix.sys.socket : send, MSG_NOSIGNAL;
119 if (!udsp) throw new Exception("can't write to closed socket");
120 auto uds = cast(UDSData*)udsp;
121 auto dp = cast(const(ubyte)*)buf.ptr;
122 auto left = buf.length;
123 while (left > 0) {
124 auto wr = send(uds.fd, dp, left, 0);
125 if (wr <= 0) throw new Exception("socket write error");
126 uds.bytesSent += wr;
127 dp += wr;
128 left -= wr;
132 private:
133 void assignFD (int fd) {
134 import core.stdc.stdlib : malloc;
135 import core.stdc.string : memset;
136 close();
137 if (fd >= 0) {
138 auto uds = cast(UDSData*)malloc(UDSData.sizeof);
139 if (uds is null) {
140 import core.sys.posix.unistd : close;
141 close(fd);
142 throw new Exception("out of memory"); // let's hope that we can do it
144 memset(uds, 0, (*uds).sizeof);
145 uds.rc = 1;
146 uds.fd = fd;
147 udsp = cast(usize)uds;
151 void doCC(string mode) (const(char)[] name) {
152 static assert(mode == "client" || mode == "server", "invalid mode");
153 import core.stdc.stdlib : malloc;
154 import core.stdc.string : memset;
155 close();
156 int fd = makeUADS!mode(name);
157 auto uds = cast(UDSData*)malloc(UDSData.sizeof);
158 if (uds is null) {
159 import core.sys.posix.unistd : close;
160 close(fd);
161 throw new Exception("out of memory"); // let's hope that we can do it
163 memset(uds, 0, (*uds).sizeof);
164 uds.rc = 1;
165 uds.fd = fd;
166 udsp = cast(usize)uds;
169 static int makeUADS(string mode) (const(char)[] name) {
170 static assert(mode == "client" || mode == "server", "invalid mode");
171 import core.stdc.string : memset;
172 import core.sys.posix.sys.socket;
173 import core.sys.posix.sys.un : sockaddr_un;
174 import core.sys.posix.unistd : close;
175 // max name length is 108, so be safe here
176 if (name.length == 0 || name.length > 100) throw new Exception("invalid name");
177 sockaddr_un sun = void;
178 memset(&sun, 0, sun.sizeof);
179 sun.sun_family = AF_UNIX;
180 // create domain socket without FS inode (first byte of name buffer should be zero)
181 sun.sun_path[1..1+name.length] = cast(byte[])name[];
182 int fd = socket(AF_UNIX, SOCK_STREAM, 0);
183 if (fd < 0) throw new Exception("can't create unix domain socket");
184 static if (mode == "server") {
185 import core.sys.posix.sys.socket : bind;
186 if (bind(fd, cast(sockaddr*)&sun, sun.sizeof) != 0) { close(fd); throw new Exception("can't bind unix domain socket"); }
187 } else {
188 import core.sys.posix.sys.socket : connect;
189 if (connect(fd, cast(sockaddr*)&sun, sun.sizeof) != 0) {
190 import core.stdc.errno;
191 auto err = errno;
192 close(fd);
193 //{ import std.stdio; writeln("ERRNO: ", err); }
194 throw new Exception("can't connect to unix domain socket");
197 return fd;