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 }