1 /* 2 Copyright (c) 2023-2024 Andrea Fontana 3 4 Permission is hereby granted, free of charge, to any person 5 obtaining a copy of this software and associated documentation 6 files (the "Software"), to deal in the Software without 7 restriction, including without limitation the rights to use, 8 copy, modify, merge, publish, distribute, sublicense, and/or sell 9 copies of the Software, and to permit persons to whom the 10 Software is furnished to do so, subject to the following 11 conditions: 12 13 The above copyright notice and this permission notice shall be 14 included in all copies or substantial portions of the Software. 15 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 20 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 OTHER DEALINGS IN THE SOFTWARE. 24 */ 25 26 module serverino.common; 27 28 import std.datetime : MonoTimeImpl, ClockType; 29 import std.stdio : File; 30 31 alias CoarseTime = MonoTimeImpl!(ClockType.coarse); 32 33 // Serverino version 34 public static int SERVERINO_MAJOR = 0; 35 public static int SERVERINO_MINOR = 6; 36 public static int SERVERINO_REVISION = 3; 37 38 // Struct WorkerPayload is used to pass data from the worker to the daemon 39 // It is prepended to the actual response payload 40 package struct WorkerPayload 41 { 42 bool isKeepAlive = false; 43 size_t contentLength = 0; 44 } 45 46 // An implementation of unix domain sockets for Windows 47 version(Windows) 48 { 49 import core.sys.windows.winsock2; 50 import std.socket; 51 52 struct sockaddr_un 53 { 54 ushort sun_family; /* AF_UNIX */ 55 byte[108] sun_path; /* pathname */ 56 } 57 58 class UnixAddress: Address 59 { 60 protected: 61 socklen_t _nameLen; 62 63 struct 64 { 65 align (1): 66 sockaddr_un sun; 67 char unused = '\0'; // placeholder for a terminating '\0' 68 } 69 70 this() pure nothrow @nogc 71 { 72 sun.sun_family = 1; 73 sun.sun_path = '?'; 74 _nameLen = sun.sizeof; 75 } 76 77 override void setNameLen(socklen_t len) @trusted 78 { 79 if (len > sun.sizeof) 80 throw new SocketParameterException("Not enough socket address storage"); 81 _nameLen = len; 82 } 83 84 public: 85 override @property sockaddr* name() return 86 { 87 return cast(sockaddr*)&sun; 88 } 89 90 override @property const(sockaddr)* name() const return 91 { 92 return cast(const(sockaddr)*)&sun; 93 } 94 95 override @property socklen_t nameLen() @trusted const 96 { 97 return _nameLen; 98 } 99 100 this(scope const(char)[] path) @trusted pure 101 { 102 import std.exception : enforce; 103 enforce(path.length <= sun.sun_path.sizeof, new SocketParameterException("Path too long")); 104 sun.sun_family = 1; 105 sun.sun_path.ptr[0 .. path.length] = (cast(byte[]) path)[]; 106 _nameLen = cast(socklen_t) 107 { 108 auto len = sockaddr_un.init.sun_path.offsetof + path.length; 109 // Pathname socket address must be terminated with '\0' 110 // which must be included in the address length. 111 if (sun.sun_path.ptr[0]) 112 { 113 sun.sun_path.ptr[path.length] = 0; 114 ++len; 115 } 116 return len; 117 }(); 118 } 119 120 this(sockaddr_un addr) pure nothrow @nogc 121 { 122 assert(addr.sun_family == 1); 123 sun = addr; 124 } 125 126 @property string path() @trusted const pure 127 { 128 auto len = _nameLen - sockaddr_un.init.sun_path.offsetof; 129 if (len == 0) 130 return null; // An empty path may be returned from getpeername 131 // For pathname socket address we need to strip off the terminating '\0' 132 if (sun.sun_path.ptr[0]) 133 --len; 134 return (cast(const(char)*) sun.sun_path.ptr)[0 .. len].idup; 135 } 136 137 override string toString() const pure 138 { 139 return path; 140 } 141 } 142 } 143 144 // A simple list implementation 145 package struct SimpleList 146 { 147 private struct SLElement 148 { 149 size_t v; 150 size_t prev = size_t.max; 151 size_t next = size_t.max; 152 } 153 154 auto asRange() 155 { 156 struct Range 157 { 158 bool empty() { 159 return tail == size_t.max || elements[tail].next == head; 160 } 161 162 void popFront() { head = elements[head].next; if (head == size_t.max) tail = size_t.max; } 163 size_t front() { return elements[head].v; } 164 165 void popBack() { tail = elements[tail].prev; if (tail == size_t.max) head = size_t.max; } 166 size_t back() { return elements[tail].v; } 167 168 private: 169 size_t head; 170 size_t tail; 171 SLElement[] elements; 172 } 173 174 return Range(head, tail, elements); 175 } 176 177 size_t insert(size_t e, bool prepend) 178 { 179 count++; 180 181 enum EOL = size_t.max; 182 183 size_t selected = EOL; 184 185 if (free == EOL) 186 { 187 elements ~= SLElement(e, EOL, EOL); 188 selected = elements.length - 1; 189 } 190 else { 191 selected = free; 192 elements[selected].v = e; 193 free = elements[selected].next; 194 195 if (free != EOL) 196 elements[free].prev = EOL; 197 } 198 199 200 if (head == EOL) 201 { 202 head = selected; 203 tail = selected; 204 elements[selected].next = EOL; 205 elements[selected].prev = EOL; 206 } 207 else 208 { 209 if (prepend) 210 { 211 size_t oldHead = head; 212 head = selected; 213 elements[selected].next = oldHead; 214 elements[selected].prev = EOL; 215 elements[oldHead].prev = selected; 216 } 217 else 218 { 219 size_t oldTail = tail; 220 tail = selected; 221 elements[selected].prev = oldTail; 222 elements[selected].next = EOL; 223 elements[oldTail].next = selected; 224 } 225 } 226 227 return selected; 228 } 229 230 size_t insertBack(size_t e) { return insert(e, false); } 231 size_t insertFront(size_t e) { return insert(e, true); } 232 233 size_t remove(size_t e) 234 { 235 enum EOL = size_t.max; 236 237 auto t = head; 238 while(t != EOL) 239 { 240 if (elements[t].v == e) 241 { 242 count--; 243 244 if (elements[t].prev == EOL) head = elements[t].next; 245 else elements[elements[t].prev].next = elements[t].next; 246 247 if (elements[t].next == EOL) tail = elements[t].prev; 248 else elements[elements[t].next].prev = elements[t].prev; 249 250 elements[t].prev = EOL; 251 elements[t].next = free; 252 253 if (free != EOL) 254 elements[free].prev = t; 255 256 free = t; 257 return t; 258 } 259 260 t = elements[t].next; 261 } 262 263 return EOL; 264 } 265 266 pragma(inline, true) size_t length() { return count; } 267 pragma(inline, true) bool empty() { return head == size_t.max; } 268 269 private: 270 271 272 SLElement[] elements; 273 size_t head = size_t.max; 274 size_t tail = size_t.max; 275 size_t free = size_t.max; 276 size_t count = 0; 277 } 278 279 // ProcessInfo is a simple class to manage processes in a cross-platform way 280 // It is used to check if a worker is running, to kill it and so on... 281 class ProcessInfo 282 { 283 this(int pid) { this.pid = pid; } 284 285 int id() { return this.pid; } 286 287 bool isValid() { return pid >= 0; } 288 bool isRunning() { return !isTerminated; } 289 bool isTerminated() 290 { 291 version(Posix) 292 { 293 import core.sys.posix.signal : kill; 294 import core.stdc.errno : errno, ESRCH; 295 296 kill(pid, 0); 297 298 return (errno == ESRCH); 299 } 300 else 301 { 302 import core.sys.windows.winbase : STILL_ACTIVE, GetExitCodeProcess; 303 import core.sys.windows.windef : LPDWORD; 304 305 if (processHandle) 306 { 307 int ec; 308 GetExitCodeProcess(processHandle, cast(LPDWORD)&ec); 309 return(ec != STILL_ACTIVE); 310 } 311 else return false; 312 } 313 } 314 315 void kill() 316 { 317 version (Posix) 318 { 319 import core.sys.posix.signal : kill, SIGTERM; 320 kill(pid, SIGTERM); 321 } 322 else 323 { 324 import core.sys.windows.winbase : TerminateProcess; 325 import core.sys.windows.windef : HANDLE; 326 327 TerminateProcess(processHandle, 1); 328 } 329 } 330 331 ~this() 332 { 333 version(Windows) 334 { 335 if (processHandle != null) 336 { 337 import core.sys.windows.winbase : CloseHandle; 338 CloseHandle(processHandle); 339 } 340 } 341 } 342 343 @disable this(); 344 345 346 version(Windows) 347 { 348 import core.sys.windows.windef : HANDLE; 349 350 HANDLE processHandle() 351 { 352 import core.sys.windows.winbase : OpenProcess; 353 import core.sys.windows.windef : PROCESS_QUERY_INFORMATION, PROCESS_ALL_ACCESS, FALSE; 354 355 if (_processHandle == null) 356 _processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); 357 358 return _processHandle; 359 } 360 361 HANDLE _processHandle = null; 362 } 363 364 private: 365 int pid = -1; 366 }