CMSDK 2.0.1
Cross-platform C++ base library and SDK for the Psyclone AIOS platform
Loading...
Searching...
No Matches
Utils.cpp
Go to the documentation of this file.
1
7#ifdef _WIN32
8 // To avoid complaints about fopen and _open
9 #define _CRT_SECURE_NO_WARNINGS
10#endif
11
12#include "Utils.h"
13#include "ObjectIDs.h"
14#include "HTML.h"
15#include "ThreadManager.h"
16#include "UnitTestFramework.h"
17
18#ifdef __APPLE__
19#include <mach/vm_statistics.h>
20#include <libproc.h>
21#include <mach/host_info.h>
22#include <mach/task_info.h>
23#include <mach/thread_act.h>
24#include <sys/sysctl.h>
25#endif
26
27#ifndef _WIN32
28#include <termios.h>
29#include <sys/ioctl.h>
30#include <mutex>
31#include <condition_variable>
32#endif
33
34#ifdef __APPLE__
35#include <sys/event.h>
36#include <sys/types.h>
37#endif
38
39namespace cmlabs {
40
42// INIT/EXIT //
44
47
51
61
63
66 std::map<std::string, utils::Semaphore*>::iterator si;
67 while (utils::SharedMemorySemaphoreMap->size()) {
69 delete(si->second);
70 utils::SharedMemorySemaphoreMap->erase(si->first);
71 }
77 }
78
81 std::map<std::string, utils::Event*>::iterator ei;
82 while (utils::SharedMemoryEventMap->size()) {
83 ei = utils::SharedMemoryEventMap->begin();
84 delete(ei->second);
85 utils::SharedMemoryEventMap->erase(ei->first);
86 }
92 }
93
96 std::map<std::string, utils::Mutex*>::iterator mi;
97 while (utils::SharedMemoryMutexMap->size()) {
98 mi = utils::SharedMemoryMutexMap->begin();
99 delete(mi->second);
100 utils::SharedMemoryMutexMap->erase(mi->first);
101 }
107 }
108
109 delete(utils::DrumBeatMap);
110 utils::DrumBeatMap = NULL;
111
114
115 delete(utils::Timer::timers);
117
120
121 return true;
122}
123
124utils::CommandLineInfo* utils::CommandLineInfo::CommandLineInfoSingleton = NULL;
125
127// Objects //
129
130std::string GetDataTypeName(uint32 datatype) {
131 switch (datatype) {
132 case CONSTCHARID:
133 return "String";
134 case TIMEID:
135 return "Time";
136 case INTID:
137 return "Integer";
138 case DOUBLEID:
139 return "Float";
140 case CHARDATAID:
141 return "Binary";
142 case DATAMESSAGEID:
143 return "Message";
144 case CONSTCHARINFOID:
145 return "String info";
146 case CHARDATAINFOID:
147 return "Binary info";
149 return "Message info";
150 default:
151 return "Unknown type";
152 }
153}
154
155uint32 GetDataTypeID(const char* typeName) {
156 if ((stricmp(typeName, "string") == 0) || (stricmp(typeName, "text") == 0))
157 return CONSTCHARID;
158 else if (stricmp(typeName, "time") == 0)
159 return CONSTCHARID;
160 else if ((stricmp(typeName, "integer") == 0) || (stricmp(typeName, "int") == 0))
161 return INTID;
162 else if ((stricmp(typeName, "float") == 0) || (stricmp(typeName, "double") == 0))
163 return DOUBLEID;
164 else if (stricmp(typeName, "binary") == 0)
165 return CHARDATAID;
166 else if (stricmp(typeName, "message") == 0)
167 return DATAMESSAGEID;
168 else if (stricmp(typeName, "string info") == 0)
169 return CONSTCHARINFOID;
170 else if (stricmp(typeName, "binary info") == 0)
171 return CHARDATAINFOID;
172 else if (stricmp(typeName, "message info") == 0)
173 return DATAMESSAGEINFOID;
174 else
175 return 0;
176}
177
179// Logging //
181
183 if (!LogSingleton) {
184 LogSingleton = new LogSystem();
185 memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
186 memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
187 LogSingleton->logReceiverObj = NULL;
188 }
189 LogSingleton->logReceiverObj = rec;
190 return true;
191}
192
194 if (!LogSingleton) {
195 LogSingleton = new LogSystem();
196 memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
197 memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
198 LogSingleton->logReceiverObj = NULL;
199 }
200 memset(LogSingleton->logLevelDebug, level, LOG_MAXCOUNT);
201 return true;
202}
203
204bool LogSystem::SetLogLevelDebug(uint8 subject, uint8 level) {
205 if (subject >= LOG_MAXCOUNT)
206 return false;
207 if (!LogSingleton) {
208 LogSingleton = new LogSystem();
209 memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
210 memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
211 LogSingleton->logReceiverObj = NULL;
212 }
213 LogSingleton->logLevelDebug[subject] = level;
214 return true;
215}
216
218 if (!LogSingleton) {
219 LogSingleton = new LogSystem();
220 memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
221 memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
222 LogSingleton->logReceiverObj = NULL;
223 }
224 memset(LogSingleton->logLevelVerbose, level, LOG_MAXCOUNT);
225 return true;
226}
227
228bool LogSystem::SetLogLevelVerbose(uint8 subject, uint8 level) {
229 if (subject >= LOG_MAXCOUNT)
230 return false;
231 if (!LogSingleton) {
232 LogSingleton = new LogSystem();
233 memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
234 memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
235 LogSingleton->logReceiverObj = NULL;
236 }
237 LogSingleton->logLevelVerbose[subject] = level;
238 return true;
239}
240
241
243 logfile = NULL;
244 printToStdOut = true;
245}
247 if (logfile)
248 delete [] logfile;
249 logfile = NULL;
250 printToStdOut = true;
251}
252
253const char* LogEntry::getText(uint32& len) {
254 if (len = this->size - sizeof(LogEntry))
255 return (char*)this+sizeof(LogEntry);
256 else
257 return NULL;
258}
259
260bool LogEntry::setText(char* text, uint32 len) {
261 if (!text || !len)
262 return false;
263 int32 avail = (int32)size - sizeof(LogEntry) - 1;
264 if (avail < (int32)len)
265 return false;
266 memcpy((char*)this+sizeof(LogEntry), text, len);
267 *((char*)this+sizeof(LogEntry)+len) = 0;
268 return true;
269}
270
271std::string LogEntry::toJSON() {
272 uint32 len;
273 const char* text = getText(len);
274 return utils::StringFormat(
275 "{ \"time\": %llu, \"level\": %u, \"source\": %u, \"subject\": %u, \"type\": %u, \"text\": \"%s\" }",
276 time, level, source, subject, type, utils::EncodeJSON(text).c_str());
277}
278
279std::string LogEntry::toXML() {
280 uint32 len;
281 const char* text = getText(len);
282 return utils::StringFormat(
283 "<entry time=\"%llu\" level=\"%u\" source=\"%u\" subject=\"%u\" type=\"%u\" text=\"%s\" />\n",
284 time, level, source, subject, type, html::EncodeHTML(text).c_str());
285}
286
287
288bool LogSystem::SetLogFileOutput(const char* logfile, bool printOut) {
289
290 if (!LogSingleton) {
291 LogSingleton = new LogSystem();
292 memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
293 memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
294 LogSingleton->logReceiverObj = NULL;
295 }
296
297 uint32 len;
298 if (!logfile || !(len = (uint32)strlen(logfile))) {
299 if (LogSingleton->logfile) {
300 delete [] LogSingleton->logfile;
301 LogSingleton->logfile = NULL;
302 }
303 }
304 else {
305 if (LogSingleton->logfile)
306 delete [] LogSingleton->logfile;
307 LogSingleton->logfile = new char[len+1];
308 utils::strcpyavail(LogSingleton->logfile, logfile, len+1, true);
309 }
310 LogSingleton->printToStdOut = printOut;
311 return true;
312}
313
314//#pragma data_seg(".shared") // Begin the shared data segment.
316//#pragma data_seg() // End the shared data segment
317//#pragma comment(linker, "/section:.shared,RWS")
318
319bool LogSystem::LogSystemPrint(uint32 source, uint8 subject, uint8 level, const char *formatstring, ... ) {
320
321 if (!LogSingleton) {
322 LogSingleton = new LogSystem();
323 memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
324 memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
325 LogSingleton->logReceiverObj = NULL;
326 }
327
328 LogEntry* entry;
329 char* str;
330 uint32 len;
331 bool res = true;
332
333 if (LogSingleton->logLevelVerbose[subject] >= level) {
334 va_list args;
335 va_start(args, formatstring);
336 str = utils::StringFormatVA(len, formatstring, args);
337 va_end(args);
338 if (!str || !len)
339 res = false;
340 else {
341 if (LogSingleton->logReceiverObj) {
342 entry = (LogEntry*) malloc(sizeof(LogEntry)+len+1);
343 entry->size = sizeof(LogEntry)+len+1;
344 entry->cid = LOGENTRYID;
345 entry->time = GetTimeNow();
346 entry->source = source;
347 entry->subject = subject;
348 entry->level = level;
349 entry->type = LOGPRINT;
350 entry->setText(str, len);
351 free(str);
352 return LogSingleton->logReceiverObj->logEntry(entry);
353 }
354 if (LogSingleton->logfile || LogSingleton->printToStdOut) {
355 std::string strline = utils::StringFormat("%s %s\n", PrintTimeNowString().c_str(), str);
356 if (LogSingleton->logfile && strlen(LogSingleton->logfile))
357 utils::AppendToAFile(LogSingleton->logfile, strline.c_str(), (uint32)strline.length());
358 if (LogSingleton->printToStdOut)
359 std::cout << strline;
360 }
361 }
362 free(str);
363 }
364 return res;
365}
366
367bool LogSystem::LogSystemDebug(uint32 source, uint8 subject, uint8 level, const char *formatstring, ... ) {
368
369 if (!LogSingleton) {
370 LogSingleton = new LogSystem();
371 memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
372 memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
373 LogSingleton->logReceiverObj = NULL;
374 }
375
376 LogEntry* entry;
377 char* str;
378 uint32 len;
379 bool res = true;
380
381 if (LogSingleton->logLevelDebug[subject] >= level) {
382 va_list args;
383 va_start(args, formatstring);
384 str = utils::StringFormatVA(len, formatstring, args);
385 va_end(args);
386 if (!str || !len)
387 res = false;
388 else {
389 if (LogSingleton->logReceiverObj) {
390 entry = (LogEntry*) malloc(sizeof(LogEntry)+len+1);
391 entry->size = sizeof(LogEntry)+len+1;
392 entry->cid = LOGENTRYID;
393 entry->time = GetTimeNow();
394 entry->source = source;
395 entry->subject = subject;
396 entry->level = level;
397 entry->type = LOGDEBUG;
398 entry->setText(str, len);
399 free(str);
400 return LogSingleton->logReceiverObj->logEntry(entry);
401 }
402 else
403 std::cout << PrintTimeNowString() << " [DEBUG] " << str << std::endl;
404 }
405 free(str);
406 }
407 return res;
408}
409
410#ifdef __APPLE__
411 /*
412 * A pthread_mutex_timedlock() impl for OSX/macOS, which lacks the
413 * real thing.
414 * NOTE: Unlike the real McCoy, won't return EOWNERDEAD, EDEADLK
415 * or EOWNERDEAD
416 */
417 static int macos_pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abs_timeout)
418 {
419 // abs_timeout is an ABSOLUTE deadline (CalcTimeout builds it from
420 // gettimeofday()). The previous implementation mistook it for a relative
421 // "remaining" duration and initialised its countdown to the absolute
422 // epoch time (~1.78e9 s), so the deadline check effectively never fired
423 // and mutex.enter(timeoutMS) would wait FOREVER whenever the lock was
424 // actually held by another thread - a process-wide latent hang on macOS.
425 // Compare the real clock against the deadline and poll trylock instead.
426 int rv;
427 while ((rv = pthread_mutex_trylock(mutex)) == EBUSY) {
428 struct timeval now;
429 gettimeofday(&now, NULL);
430 if (now.tv_sec > abs_timeout->tv_sec ||
431 (now.tv_sec == abs_timeout->tv_sec &&
432 (long)(now.tv_usec) * 1000L >= abs_timeout->tv_nsec))
433 return ETIMEDOUT;
434 struct timespec nap = { 0, 1000000 }; // poll every 1ms
435 nanosleep(&nap, NULL);
436 }
437 return rv;
438 }
439#endif
440
441
442namespace utils {
443
444#ifndef WINDOWS
445//#if defined LINUX
446 bool CalcTimeout(struct timespec &timeout, uint32 ms) {
447 struct timeval now;
448 if (gettimeofday(&now, NULL) != 0)
449 return false;
450
451 timeout.tv_sec = now.tv_sec + (ms / 1000);
452 int64 us = (int64)(now.tv_usec) + ((ms % 1000)*1000);
453 while (us >= 1000000) {
454 timeout.tv_sec++;
455 us -= 1000000;
456 }
457 timeout.tv_nsec = (long)(us * 1000); // usec -> nsec
458 return true;
459 }
460
461 uint64 GetTime() {
462 struct timeval tv;
463 if ( gettimeofday(&tv, NULL))
464 return 0;
465 return (tv.tv_usec + tv.tv_sec * 1000000LL);
466 }
467#endif
468
469// https://stackoverflow.com/questions/44807302/create-c-timer-in-macos/52905687#52905687
470
471#ifdef WINDOWS
472 VOID CALLBACK DrumBeatCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired) {
473 DrumBeatInfo* info = (DrumBeatInfo*) lpParameter;
474 DrumBeatFunc func = info->func;
475 if (!info->id || !info->createdTime || !info->started || !func)
476 return;
477 info->count++;
478 if (!info->func(info->id, info->count))
479 StopDrumBeat(info->id);
480 }
481#else
482 #ifndef __APPLE__
483 static void DrumBeatCallback(union sigval p) {
484 DrumBeatInfo* info = (DrumBeatInfo*) p.sival_ptr;
485 DrumBeatFunc func = info->func;
486 if (!info->id || !info->createdTime || !info->started || !func)
487 return;
488 info->count++;
489 if (!info->func(info->id, info->count))
490 StopDrumBeat(info->id);
491 }
492 #endif
493#endif
494
495bool CreateDrumBeat(uint32 id, uint32 interval, DrumBeatFunc func, bool autostart) {
496
497 if (!DrumBeatMap)
498 DrumBeatMap = new std::map<uint32, DrumBeatInfo>;
499 DrumBeatInfo* info = &(*DrumBeatMap)[id];
500
501 // check if it already exists
502 if (info->id && info->createdTime)
503 return false;
504
505 // Windows threadpool size limit is 500, we probably shouldn't create more than that anyway
506 // Could change the limit with WT_SET_MAX_THREADPOOL_THREAD
507 if (DrumBeatMap->size() > 450)
508 return false;
509
510 info->id = id;
511 info->createdTime = GetTimeNow();
512 info->func = func;
513 info->count = 0;
514 info->interval = interval;
515
516 #ifdef WINDOWS
517 #else
518 #ifdef __APPLE__
519 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
520 dispatch_source_t timer_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
521 if (timer_source)
522 info->handle = timer_source;
523 #else
524 // struct sigaction sa;
525 struct sigevent timer_event;
526
527 //sigemptyset(&sa.sa_mask);
528 //sa.sa_flags = SA_SIGINFO; /* Real-Time signal */
529 //sa.sa_sigaction = timer_signal_handler;
530 //sigaction(SIGRTMIN, &sa, NULL);
531
532 timer_event.sigev_notify = SIGEV_THREAD;
533 timer_event.sigev_notify_attributes = NULL;
534 timer_event.sigev_notify_function = DrumBeatCallback;
535 timer_event.sigev_value.sival_ptr = (void *)info;
536 int ret = timer_create(CLOCK_REALTIME, &timer_event, &info->handle);
537 if (ret != 0) {
538 LogPrint(0,LOG_SYSTEM,0,"Error creating timer: %d", ret);
539 }
540 #endif
541 #endif
542
543 if (autostart)
544 StartDrumBeat(id);
545 return true;
546}
547
548bool StartDrumBeat(uint32 id) {
549 if (!DrumBeatMap)
550 return false;
551 DrumBeatInfo* info = &(*DrumBeatMap)[id];
552 if (!info->id || !info->createdTime)
553 return false;
554
555 if (info->started)
556 return true;
557
558 #ifdef WINDOWS
559 // To compile an application that uses this function, define _WIN32_WINNT as 0x0500 or later
560 CreateTimerQueueTimer(&info->handle, NULL, DrumBeatCallback, info, info->interval, info->interval, WT_EXECUTEINTIMERTHREAD);
561 info->started = info->createdTime;
562 #else
563 #ifdef __APPLE__
564 if (info->handle) {
565 uint64 intervalNs = (uint64)info->interval * 1000000;
566 dispatch_source_set_timer(info->handle, dispatch_time(DISPATCH_TIME_NOW, (int64_t)intervalNs), intervalNs, 0);
567 DrumBeatInfo* inf = info;
568 dispatch_source_set_event_handler(info->handle, ^{
569 inf->count++;
570 if (!inf->func(inf->id, inf->count))
571 StopDrumBeat(inf->id);
572 });
573 dispatch_resume(info->handle);
574 info->started = info->createdTime;
575 }
576 #else
577 struct itimerspec newtv;
578 uint64 p = info->interval * 1000; // convert to nanosec
579 newtv.it_interval.tv_sec = p / 1000000;
580 newtv.it_interval.tv_nsec = (p % 1000000)*1000;
581 newtv.it_value.tv_sec = p / 1000000;
582 newtv.it_value.tv_nsec = (p % 1000000)*1000;
583
584 int ret = timer_settime(info->handle, 0, &newtv, NULL);
585 if (ret != 0) {
586 LogPrint(0,LOG_SYSTEM,0,"Error arming timer: %d", ret);
587 }
588 info->started = info->createdTime;
589 #endif
590 #endif
591 return true;
592}
593
594bool StopDrumBeat(uint32 id) {
595 if (!DrumBeatMap)
596 return false;
597 DrumBeatInfo* info = &(*DrumBeatMap)[id];
598 if (!info->id || !info->createdTime)
599 return false;
600
601 if (!info->started)
602 return true;
603
604 #ifdef WINDOWS
605 DeleteTimerQueueTimer(NULL, info->handle, NULL);
606 info->started = 0;
607 info->handle = NULL;
608 #else
609 #ifdef __APPLE__
610 if (info->handle) {
611 dispatch_suspend(info->handle);
612 info->started = 0;
613 }
614 #else
615 struct itimerspec newtv;
616 memset(&newtv, 0, sizeof(itimerspec));
617 int ret = timer_settime(info->handle, 0, &newtv, NULL);
618 if (ret != 0) {
619 LogPrint(0,LOG_SYSTEM,0,"Error arming timer: %d", ret);
620 }
621 info->started = 0;
622 #endif
623 #endif
624 return true;
625}
626
627bool EndDrumBeat(uint32 id) {
628 if (!DrumBeatMap)
629 return false;
630 DrumBeatInfo* info = &(*DrumBeatMap)[id];
631 if (!info->id || !info->createdTime)
632 return false;
633
634 #ifdef WINDOWS
635 if (info->started)
636 StopDrumBeat(id);
637 #else
638 #ifdef __APPLE__
639 if (info->handle) {
640 // libdispatch contract: a dispatch source must NOT be released while
641 // suspended — releasing a suspended source raises EXC_BREAKPOINT
642 // (SIGTRAP). StopDrumBeat leaves the source SUSPENDED (started==0), so a
643 // stopped-then-ended drumbeat crashed here on the old code path.
644 // Correct teardown: cancel FIRST (prevents any future handler firing),
645 // THEN, if suspended, resume to rebalance the suspend count to zero so
646 // the release is legal. Resuming an already-cancelled source does not
647 // re-run the timer handler, so there is no teardown race on the global
648 // concurrent queue the source runs on.
649 dispatch_source_cancel(info->handle);
650 if (!info->started)
651 dispatch_resume(info->handle);
652 dispatch_release(info->handle);
653 info->handle = NULL;
654 info->started = 0;
655 }
656 #else
657 timer_delete(info->handle);
658 info->started = 0;
659 info->handle = 0;
660 #endif
661 #endif
662
663 info->createdTime = 0;
664 info->func = NULL;
665 info->count = 0;
666 info->interval = 0;
667 return true;
668}
669
670
671bool EnterMutex(const char* name, uint32 ms, bool autocreate) {
673 SharedMemoryMutexMap = new std::map<std::string, Mutex*>;
676
677 Mutex* mutex;
678 std::map<std::string, Mutex*>::iterator i = SharedMemoryMutexMap->find(name);
679 std::map<std::string, Mutex*>::iterator e = SharedMemoryMutexMap->end();
680 if (i == e) {
682 i = SharedMemoryMutexMap->find(name);
683 if (i == e) {
684 if (!autocreate) {
686 return false;
687 }
688 mutex = new Mutex(name);
690 }
691 else {
692 // try again
693 if (i->second)
694 mutex = i->second;
695 else {
696 if (!autocreate) {
698 return false;
699 }
700 mutex = new Mutex(name);
702 }
703 }
705 }
706 else
707 mutex = i->second;
708 return (mutex->enter(ms));
709}
710
711bool LeaveMutex(const char* name) {
713 SharedMemoryMutexMap = new std::map<std::string, Mutex*>;
716
717 std::map<std::string, Mutex*>::iterator i = SharedMemoryMutexMap->find(name);
718 std::map<std::string, Mutex*>::iterator e = SharedMemoryMutexMap->end();
719 if (i == e)
720 return false;
721 return (i->second->leave());
722}
723
724bool DestroyMutex(const char* name) {
726 SharedMemoryMutexMap = new std::map<std::string, Mutex*>;
730
731 std::map<std::string, Mutex*>::iterator i = SharedMemoryMutexMap->find(name);
732 std::map<std::string, Mutex*>::iterator e = SharedMemoryMutexMap->end();
733 if (i == e)
734 return false;
735 delete(i->second);
736 SharedMemoryMutexMap->erase(i);
738 return true;
739}
740
741
742
743
744
745
746
747
748Semaphore* GetSemaphore(const char* name, bool autocreate) {
750 SharedMemorySemaphoreMap = new std::map<std::string, Semaphore*>;
753 Semaphore* semaphore;
754 std::map<std::string, Semaphore*>::iterator i = SharedMemorySemaphoreMap->find(name);
755 std::map<std::string, Semaphore*>::iterator e = SharedMemorySemaphoreMap->end();
756 if (i == e) {
758 i = SharedMemorySemaphoreMap->find(name);
759 if (i == e) {
760 if (!autocreate) {
762 return NULL;
763 }
764 semaphore = new Semaphore(name);
766 }
767 else {
768 // try again
769 if (i->second)
770 semaphore = i->second;
771 else {
772 if (!autocreate) {
774 return NULL;
775 }
776 semaphore = new Semaphore(name);
778 }
779 }
781 }
782 else
783 semaphore = i->second;
784 return semaphore;
785}
786
787bool WaitForSemaphore(const char* name, uint32 ms, bool autocreate) {
789 SharedMemorySemaphoreMap = new std::map<std::string, Semaphore*>;
792 Semaphore* semaphore;
793 std::map<std::string, Semaphore*>::iterator i = SharedMemorySemaphoreMap->find(name);
794 std::map<std::string, Semaphore*>::iterator e = SharedMemorySemaphoreMap->end();
795 if (i == e) {
797 i = SharedMemorySemaphoreMap->find(name);
798 if (i == e) {
799 if (!autocreate) {
801 return false;
802 }
803 semaphore = new Semaphore(name);
805 }
806 else {
807 // try again
808 if (i->second)
809 semaphore = i->second;
810 else {
811 if (!autocreate) {
813 return false;
814 }
815 semaphore = new Semaphore(name);
817 }
818 }
820 }
821 else
822 semaphore = i->second;
823 return (semaphore->wait(ms));
824}
825
826bool SignalSemaphore(const char* name) {
828 SharedMemorySemaphoreMap = new std::map<std::string, Semaphore*>;
831
832 Semaphore* semaphore;
833 std::map<std::string, Semaphore*>::iterator i = SharedMemorySemaphoreMap->find(name);
834 std::map<std::string, Semaphore*>::iterator e = SharedMemorySemaphoreMap->end();
835 if (i == e) {
837 i = SharedMemorySemaphoreMap->find(name);
838 if (i == e) {
839 semaphore = new Semaphore(name);
841 }
842 else {
843 // try again
844 if (i->second)
845 semaphore = i->second;
846 else {
847 semaphore = new Semaphore(name);
849 }
850 }
852 }
853 else
854 semaphore = i->second;
855 return (semaphore->signal());
856}
857
858bool DestroySemaphore(const char* name) {
860 SharedMemorySemaphoreMap = new std::map<std::string, Semaphore*>;
864
865 std::map<std::string, Semaphore*>::iterator i = SharedMemorySemaphoreMap->find(name);
866 std::map<std::string, Semaphore*>::iterator e = SharedMemorySemaphoreMap->end();
867 if (i == e) {
869 return false;
870 }
871 delete(i->second);
872 SharedMemorySemaphoreMap->erase(i);
874 return true;
875}
876
877
878
879
880
881
882
883
884
885
886
887bool WaitForNextEvent(const char* name, uint32 ms, bool autocreate) {
889 SharedMemoryEventMap = new std::map<std::string, Event*>;
892 Event* event;
893 std::map<std::string, Event*>::iterator i = SharedMemoryEventMap->find(name);
894 std::map<std::string, Event*>::iterator e = SharedMemoryEventMap->end();
895 if (i == e) {
897 i = SharedMemoryEventMap->find(name);
898 if (i == e) {
899 if (!autocreate) {
901 return false;
902 }
903 event = new Event(name);
905 }
906 else {
907 // try again
908 if (i->second)
909 event = i->second;
910 else {
911 if (!autocreate) {
913 return false;
914 }
915 event = new Event(name);
917 }
918 }
920 }
921 else
922 event = i->second;
923 return (event->waitNext(ms));
924}
925
926bool SignalEvent(const char* name) {
928 SharedMemoryEventMap = new std::map<std::string, Event*>;
931 std::map<std::string, Event*>::iterator i = SharedMemoryEventMap->find(name);
932 std::map<std::string, Event*>::iterator e = SharedMemoryEventMap->end();
933 if (i == e)
934 return false;
935 else
936 return (i->second->signal());
937}
938
939bool DestroyEvent(const char* name) {
941 SharedMemoryEventMap = new std::map<std::string, Event*>;
945
946 std::map<std::string, Event*>::iterator i = SharedMemoryEventMap->find(name);
947 std::map<std::string, Event*>::iterator e = SharedMemoryEventMap->end();
948 if (i == e)
949 return false;
950 delete(i->second);
951 SharedMemoryEventMap->erase(i);
953 return true;
954}
955
956
957
958
959
960bool SetSharedSystemInstance(uint32 inst) {
962 return true;
963}
964
968
969
970// ---------------------------------------------------------------------------
971// Cancel-safe mutex support, stored OUT-OF-LINE so sizeof(Mutex) and its member
972// layout stay byte-identical to the original (Mutex is embedded by value in
973// size/layout-sensitive structs across shared objects; growing it corrupted the
974// Linux libPsySystem.so build). The "is this mutex cancel-safe?" bit lives in a
975// small pointer-keyed set; the saved cancel state is per-thread (thread_local),
976// which is where cancel state conceptually belongs anyway.
977#ifndef WINDOWS
978static pthread_mutex_t g_cancelSafeSetLock = PTHREAD_MUTEX_INITIALIZER;
979static std::map<const void*, bool>* g_cancelSafeSet = NULL; // present+true => cancel-safe
980static __thread int g_savedCancelState = 0;
981
982// Lock-free fast-path guard: an exact count of how many mutexes are currently
983// registered cancel-safe. MutexIsCancelSafe() runs on EVERY Mutex::enter() and
984// every outermost Mutex::leave() of EVERY mutex in the process, so taking
985// g_cancelSafeSetLock unconditionally made one global pthread mutex plus a
986// red-black-tree lookup into a process-wide serialisation point sitting directly
987// on the hot network/messaging path. Almost always nothing is registered, and
988// this counter answers "no" without touching the lock or the map.
989//
990// Safety of the unlocked read: the counter is only mutated under
991// g_cancelSafeSetLock and is kept an exact count of map entries, so it can never
992// read 0 while an entry exists. A stale read is therefore only possible against
993// a concurrent registration - and registration happens during object setup
994// (ThreadManager construction, connection creation) before that object can be a
995// cancel target, so reading 0 cannot drop a guarantee already established.
996static volatile int g_cancelSafeCount = 0;
997
998static inline bool AnyCancelSafeMutexes() {
999 #if defined(__GNUC__) || defined(__clang__)
1000 return __atomic_load_n(&g_cancelSafeCount, __ATOMIC_ACQUIRE) != 0;
1001 #else
1002 return g_cancelSafeCount != 0;
1003 #endif
1004}
1005
1006static bool MutexIsCancelSafe(const void* m) {
1007 if (!AnyCancelSafeMutexes())
1008 return false; // fast path: no global lock, no map lookup
1009 pthread_mutex_lock(&g_cancelSafeSetLock);
1010 bool r = (g_cancelSafeSet && g_cancelSafeSet->find(m) != g_cancelSafeSet->end());
1011 pthread_mutex_unlock(&g_cancelSafeSetLock);
1012 return r;
1013}
1014#endif
1015
1017 #ifndef WINDOWS
1018 pthread_mutex_lock(&g_cancelSafeSetLock);
1019 if (!g_cancelSafeSet)
1020 g_cancelSafeSet = new std::map<const void*, bool>();
1021 if (on) {
1022 // Only count real insertions/erasures, so the counter stays an exact
1023 // entry count (re-registering an already-registered mutex must not
1024 // inflate it, or it could never fall back to 0).
1025 if (g_cancelSafeSet->find(this) == g_cancelSafeSet->end()) {
1026 (*g_cancelSafeSet)[this] = true;
1028 }
1029 }
1030 else {
1031 if (g_cancelSafeSet->erase(this) > 0 && g_cancelSafeCount > 0)
1033 }
1034 pthread_mutex_unlock(&g_cancelSafeSetLock);
1035 #else
1036 (void)on;
1037 #endif
1038}
1039
1041 //uint32 tt;
1042 //utils::GetCurrentThreadOSID(tt);
1043 //printf("Mutex[%p] create by thread: %u\n", this, tt);
1044 name = NULL;
1045 osid = 0;
1046 count = 0;
1047 total = 0;
1048 created = true;
1049 #ifdef WINDOWS
1050 mutex = ::CreateMutex(NULL, FALSE, NULL);
1051 #else
1052 mutex = new pthread_mutex_t;
1053 pthread_mutexattr_t attr;
1054 pthread_mutexattr_init(&attr);
1055 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
1056 pthread_mutex_init(mutex, &attr);
1057
1058 //semaphore = new sem_t;
1059 //if (sem_init(semaphore, 0, 1) != 0) {
1060 // delete(semaphore);
1061 // semaphore = NULL;
1062 //}
1063
1064 pthread_mutexattr_destroy(&attr);
1065 #endif
1066}
1067
1068Mutex::Mutex(const char* name, bool force) {
1069 this->name = new char[MAXSHMEMNAMELEN];
1070 osid = 0;
1071 count = 0;
1072 total = 0;
1073 created = true;
1074 #ifdef WINDOWS
1075 //sprintf(this->name, "Global\\Mutex_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
1076 snprintf(this->name, MAXSHMEMNAMELEN, "%s\\Mutex_%s", WINSHMEMSPACE, name);
1077 mutex = ::CreateMutex(NULL, FALSE, this->name);
1078 #else
1079 mutex = NULL;
1080// sprintf(this->name, "/Cond_%s", name);
1081// semaphore = sem_open(name, O_CREAT, 0666, 1);
1082
1083 snprintf(this->name, MAXSHMEMNAMELEN, "Mutex_%s", name);
1084
1085 // test if shared memory exists
1086 char* data = NULL;
1087
1088 if (!force)
1089 data = OpenSharedMemorySegment(this->name, MUTEXMEMSIZE);
1090 if (data) {
1091 mutex = (pthread_mutex_t*) data;
1092 created = false;
1093 }
1094 else {
1095 // create shared mutex
1096 if (data = CreateSharedMemorySegment(this->name, MUTEXMEMSIZE, force)) {
1097 // create mutex
1098 mutex = (pthread_mutex_t*) data;
1099 pthread_mutexattr_t attr;
1100 pthread_mutexattr_init(&attr);
1101 pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
1102 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
1103 pthread_mutex_init(mutex, &attr);
1104 pthread_mutexattr_destroy(&attr);
1105 }
1106 }
1107 #endif
1108}
1109
1110#ifdef WINDOWS
1111bool CloseMutexHandle(HANDLE mutex) {
1112 // had to do this to catch exceptions in 32-bit Windows
1113 // as using __try+__except in C++ objects gave linker error
1114 __try {
1115 CloseHandle(mutex);
1116 }
1117 __except (EXCEPTION_EXECUTE_HANDLER) {
1118 }
1119 return true;
1120}
1121#endif
1122
1124 //uint32 tt;
1125 //utils::GetCurrentThreadOSID(tt);
1126 //printf("Mutex[%p] DELETE by thread: %u\n", this, tt);
1127 #ifndef WINDOWS
1128 // Unregister from the out-of-line cancel-safe side map, else a stale
1129 // entry keyed on this address would alias onto a future Mutex allocated
1130 // at the same address (harmless but wrong) and the map would grow
1131 // unboundedly for short-lived cancel-safe mutexes (per-connection ones).
1132 setCancelSafe(false);
1133 #endif
1134 #ifdef WINDOWS
1135 if (mutex == NULL)
1136 return;
1137
1138 CloseMutexHandle(mutex);
1139 mutex = NULL;
1140 delete [] name;
1141 #else
1142 if (!name) {
1143 pthread_mutex_destroy(mutex);
1144 // sem_destroy(semaphore);
1145 // delete(semaphore);
1146 }
1147 else {
1148 if (created)
1149 pthread_mutex_destroy(mutex);
1151 mutex = NULL;
1152 // sem_close(semaphore);
1153 delete [] name;
1154 }
1155 #endif
1156}
1157
1159 #ifdef WINDOWS
1160 DWORD reply;
1161 reply = WaitForSingleObject(mutex, INFINITE);
1162 // WAIT_ABANDONED means the previous owner died while holding the mutex;
1163 // ownership IS granted to us, so treat it as a successful (if suspect) lock.
1164 // Returning false here while secretly owning the mutex would deadlock all
1165 // future waiters, which is exactly what intermittently broke the CMSDK tests.
1166 if (reply == WAIT_OBJECT_0 || reply == WAIT_ABANDONED) {
1168// if (count)
1169// int n=0;
1170 count++;
1171 total++;
1172 return true;
1173 }
1174 else if (reply == WAIT_FAILED && GetLastError() == ERROR_INVALID_HANDLE) {
1175 // The mutex handle is invalid (closed elsewhere, e.g. during teardown
1176 // storms where a stale/double CloseHandle invalidated it). Mirror the
1177 // timeout variant: treat as acquired so callers (typically shutdown
1178 // paths) proceed instead of failing; hanging here forever was the
1179 // alternative when the handle value got recycled into another object.
1180 LogPrint(0, LOG_SYSTEM, 1, "Mutex::enter on invalid handle [%p] - treating as acquired", mutex);
1181 return true;
1182 }
1183 else {
1184 return false;
1185 }
1186 #else
1187 // Cancel-safe mutexes disable thread cancellation BEFORE blocking on the
1188 // lock, so a pthread_cancel (e.g. during teardown) can never fire while we
1189 // hold it and orphan it. State kept out-of-line (see setCancelSafe): the
1190 // "is cancel-safe" bit in a side map, the saved cancel state in a thread-local.
1191 bool cs = MutexIsCancelSafe(this);
1192 int prevCancelState = 0;
1193 if (cs)
1194 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &prevCancelState);
1195 int error = pthread_mutex_lock(mutex);
1196 if (error == 0) {
1198 // Capture the caller's real cancel state only on the OUTERMOST acquire
1199 // (recursive re-entry just re-disables an already-disabled state).
1200 if (cs && count == 0)
1201 g_savedCancelState = prevCancelState;
1202 count++;
1203 return true;
1204 }
1205 else {
1206 if (cs)
1207 pthread_setcancelstate(prevCancelState, NULL);
1208 LogPrint(0, LOG_SYSTEM, 0, "Lock of mutex failed: %s (%d)", name ? name : "-", error);
1209 return false;
1210 }
1211 #endif
1212}
1213
1214bool Mutex::enter(uint32 timeout, const char* errorMsg) {
1215// printf("*** %s ***\n", errorMsg);
1216// uint32 tt;
1217// utils::GetCurrentThreadOSID(tt);
1218// printf("Mutex[%p] locking by thread: %u\n", this, tt);
1219
1220 #ifdef WINDOWS
1221 DWORD reply;
1222 reply = WaitForSingleObject(mutex, timeout);
1223 // See Mutex::enter(): WAIT_ABANDONED grants ownership; treat as success.
1224 if (reply == WAIT_OBJECT_0 || reply == WAIT_ABANDONED) {
1226 //if (count)
1227 // printf("*** [%u]Mutex[%p](%s) locked more than once: %u\n", osid, mutex, errorMsg, count);
1228 //else
1229 // printf("--- [%u]Mutex[%p](%s) locked\n", osid, mutex, errorMsg);
1230 // if (count)
1231 // printf("Mutex[%p] double locking by thread: %u\n", this, tt);
1232
1233 count++;
1234 total++;
1235 return true;
1236 }
1237 //if (errorMsg) {
1238 uint32 osid2;
1240 if (reply == WAIT_TIMEOUT) {
1241 LogPrint(0, LOG_SYSTEM, 0, "[%u]Mutex enter timeout[%p](%s) - currently held by %u, count %u, total %u",
1242 osid2, mutex, errorMsg ? errorMsg : "-", osid, count, total);
1243 //if (!errorMsg)
1244 // int n = 0;
1245 }
1246 else if (reply == WAIT_FAILED) {
1247 DWORD mtxErr = GetLastError();
1248 if (mtxErr == ERROR_INVALID_HANDLE)
1249 return true;
1250 char* msg = new char[2048];
1251 int length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
1252 NULL, mtxErr, 0, msg, 2048, NULL);
1253 msg[length] = '\0';
1254 LogPrint(0, LOG_SYSTEM, 0, "[%u]Mutex enter failed[%p](%s): %s - currently held by %u, count %u, total %u",
1255 osid2, mutex, errorMsg ? errorMsg : "-", msg, osid, count, total);
1256 delete[] msg;
1257 }
1258 else
1259 //[36136]Mutex enter error[00000000000016E0](-): 4294967295 - currently held by 0, count 0, total 7
1260 LogPrint(0,LOG_SYSTEM,0,"[%u]Mutex enter error[%p](%s): %d - currently held by %u, count %u, total %u",
1261 osid2, mutex, errorMsg ? errorMsg : "-", reply, osid, count, total);
1262 //}
1263 return false;
1264 #else
1265 struct timespec ts;
1266 CalcTimeout(ts, timeout);
1267 // See Mutex::enter(): disable cancellation before blocking on a cancel-safe
1268 // lock so a cancel can never orphan it. State is out-of-line (setCancelSafe).
1269 bool cs = MutexIsCancelSafe(this);
1270 int prevCancelState = 0;
1271 if (cs)
1272 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &prevCancelState);
1273 #ifdef __APPLE__
1274 int error = macos_pthread_mutex_timedlock(mutex, &ts);
1275 #else
1276 int error = pthread_mutex_timedlock(mutex, &ts);
1277 #endif
1278 if (error == 0) {
1280 if (cs && count == 0)
1281 g_savedCancelState = prevCancelState;
1282 count++;
1283 // if (count > 1)
1284 // printf("Lock count now %u...\n", count);
1285 // LogPrint(0,LOG_SYSTEM,0,"Mutex enter success: '%s' (%d) [%p] - I am %u", errorMsg, error, this, osid);
1286 return true;
1287 }
1288 else if (cs) {
1289 // Timed out / failed without acquiring: undo the cancel-disable so we
1290 // don't leave the caller permanently non-cancellable.
1291 pthread_setcancelstate(prevCancelState, NULL);
1292 }
1293 if (error == EINVAL) {
1294 LogPrint(0,LOG_SYSTEM,0,"Mutex enter error: '%s' (%d) [%u = %llu.%lld]",
1295 errorMsg ? errorMsg : "-", error, timeout, (uint64)(ts.tv_sec), (int64)(ts.tv_nsec));
1296 return false;
1297 }
1298// if (errorMsg)
1299 LogPrint(0,LOG_SYSTEM,0,"Mutex enter timeout %u: '%s' (%d) [%p] - held by %u", timeout, errorMsg ? errorMsg : "-", error, this, osid);
1300 return false;
1301
1302 //if (pthread_mutex_trylock(mutex) == 0)
1303 // return true;
1304
1305 //printf("[w-%s]", errorMsg);
1306 //fflush(stdout);
1308 //uint64 start = GetTimeNow();
1309 //struct timespec ts;
1310 //CalcTimeout(ts, timeout);
1311
1312 //int res = 1;
1313 //do {
1314 // if (sem_timedwait(semaphore, &ts) != 0)
1315 // break;
1316 // res = pthread_mutex_trylock(mutex);
1317 //} while ( !res && (GetTimeAgeMS(start) < (int32)timeout) );
1318
1319 //if (res) {
1320 // printf("s");
1321 // fflush(stdout);
1322 // return true;
1323 //}
1324 //if (errorMsg)
1325 // LogPrint(0,LOG_SYSTEM,0,"Mutex enter timeout: %s", errorMsg);
1326 //return false;
1327 #endif
1328}
1329
1331// uint32 tt;
1332// utils::GetCurrentThreadOSID(tt);
1333// printf("Mutex[%p] leaving by thread: %u\n", this, tt);
1334 #ifdef WINDOWS
1335 uint32 osid2 = osid;
1336 //utils::GetCurrentThreadOSID(osid2);
1337 //else
1338 // return true;
1339 //if (osid != osid2)
1340 // printf("*** [%u]Mutex[%p] unlocked by someone else: %u\n", osid, mutex, osid2);
1341 //if (!count)
1342 // osid = 0;
1343 if (ReleaseMutex(mutex) != 0) {
1344 if (count)
1345 count--;
1346 if (!count)
1347 osid = 0;
1348 //else
1349 // printf("Mutex[%p] double release by thread: %u\n", this, tt);
1350 //osid = 0;
1351 return true;
1352 }
1353 else {
1354 // reinstate values
1355 //count++;
1356 osid = osid2;
1357 return false;
1358 }
1359 #else
1360 // Capture whether this is the outermost release BEFORE unlocking; if it is,
1361 // restore the caller's cancel state AFTER the unlock so any cancel that was
1362 // pending during the (now-complete) critical section can finally land -- but
1363 // only once we no longer hold the lock, so it can never orphan it. State is
1364 // out-of-line (see setCancelSafe): cancel-safe bit in a side map, saved
1365 // cancel state in a thread-local.
1366 bool outermost = (count == 1 && MutexIsCancelSafe(this));
1367 int restoreState = g_savedCancelState;
1368 if (pthread_mutex_unlock(mutex) != 0)
1369 return false;
1370 else {
1371 osid = 0;
1372 count--;
1373 if (outermost)
1374 pthread_setcancelstate(restoreState, NULL);
1375 return true;
1376 }
1377
1378 // Signal semaphore
1379 // return (sem_post(semaphore) == 0);
1380 #endif
1381}
1382
1383//bool Mutex::destroy() {
1384// #ifdef WINDOWS
1385// #else
1386// pthread_mutex_destroy(mutex);
1387// DestroySharedMemorySegment((char*)mutex, MUTEXMEMSIZE);
1388// mutex = NULL;
1389// sem_unlink(name);
1390// #endif
1391// return true;
1392//}
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406Semaphore::Semaphore(uint32 maxCount) {
1407 name = NULL;
1408 #ifdef WINDOWS
1409 semaphore = CreateSemaphore(
1410 NULL, // no security attributes
1411 0, // initial count
1412 10, // maximum count
1413 NULL); // unnamed semaphore
1414 #else
1415 #ifdef __APPLE__
1416 semaphore_impl = dispatch_semaphore_create(0);
1417 #else
1418 semaphore = new sem_t;
1419 if (sem_init(semaphore, 0, 0) != 0) {
1420 delete(semaphore);
1421 semaphore = NULL;
1422 }
1423 #endif
1424 #endif
1425}
1426
1427Semaphore::Semaphore(const char* name, uint32 maxCount) {
1428 this->name = new char[MAXSHMEMNAMELEN];
1429 #ifdef WINDOWS
1430 //sprintf(this->name, "Global\\Semaphore_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
1431 snprintf(this->name, MAXSHMEMNAMELEN, "%s\\Semaphore_%s", WINSHMEMSPACE, name);
1432 // Creates or gets an existing Semaphore
1433 semaphore = CreateSemaphore(
1434 NULL, // no security attributes
1435 0, // initial count
1436 maxCount, // maximum count
1437 this->name); // unnamed semaphore
1438 #else
1439 #ifdef __APPLE__
1440 char shmemname[MAXSHMEMNAMELEN];
1441 snprintf(shmemname, sizeof(shmemname), "Semaphore_%s", name);
1442 char* data = OpenSharedMemorySegment(shmemname, SEMAPHOREMEMSIZE);
1443 if (!data) {
1444 data = CreateSharedMemorySegment(shmemname, SEMAPHOREMEMSIZE, true);
1445 if (data) {
1446 pthread_mutex_t* m = (pthread_mutex_t*)data;
1447 pthread_mutexattr_t attr;
1448 pthread_mutexattr_init(&attr);
1449 pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
1450 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
1451 pthread_mutex_init(m, &attr);
1452 pthread_mutexattr_destroy(&attr);
1453 pthread_cond_t* c = (pthread_cond_t*)(data + sizeof(pthread_mutex_t));
1454 pthread_condattr_t cattr;
1455 pthread_condattr_init(&cattr);
1456 pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
1457 pthread_cond_init(c, &cattr);
1458 pthread_condattr_destroy(&cattr);
1459 uint32* cnt = (uint32*)(data + sizeof(pthread_mutex_t) + sizeof(pthread_cond_t));
1460 *cnt = 1;
1461 }
1462 }
1463 semaphore_impl = data;
1464 #else
1465 snprintf(this->name, MAXSHMEMNAMELEN, "/Semaphore_%s", name);
1466// sprintf(this->name, "/Semaphore_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
1467 semaphore = sem_open(this->name, O_CREAT, 0666, 1);
1468 if (semaphore == SEM_FAILED)
1469 semaphore = NULL;
1470 #endif
1471 #endif
1472 // Rewrite the original name
1473 utils::strcpyavail(this->name, name, MAXKEYNAMELEN, true);
1474}
1475
1477 #ifdef WINDOWS
1478 if (semaphore) {
1479 CloseHandle(semaphore);
1480 semaphore = NULL;
1481 }
1482 delete [] name;
1483 #else
1484 #ifdef __APPLE__
1485 if (!name) {
1486 if (semaphore_impl)
1487 dispatch_release((dispatch_semaphore_t)semaphore_impl);
1488 semaphore_impl = NULL;
1489 }
1490 else {
1491 if (semaphore_impl) {
1492 CloseSharedMemorySegment((char*)semaphore_impl, SEMAPHOREMEMSIZE);
1493 semaphore_impl = NULL;
1494 }
1495 delete [] name;
1496 }
1497 #else
1498 // printf("Deleting semaphore '%s'...\n", name);
1499 if (!name) {
1500 sem_destroy(semaphore);
1501 delete(semaphore);
1502 }
1503 else {
1504 sem_close(semaphore);
1505 // Remove the /dev/shm/sem.Semaphore_<name> entry; ctor rewrote this->name
1506 // back to the bare name, so re-prefix before unlinking. Safe even if other
1507 // holders exist: their open handles remain valid until closed.
1508 char semname[MAXSHMEMNAMELEN];
1509 snprintf(semname, sizeof(semname), "/Semaphore_%s", name);
1510 sem_unlink(semname);
1511 delete [] name;
1512 }
1513 #endif
1514 #endif
1515}
1516
1518 #ifdef WINDOWS
1519 return (WaitForSingleObject(semaphore, INFINITE) == WAIT_OBJECT_0);
1520 #else
1521 #ifdef __APPLE__
1522 if (!name)
1523 return dispatch_semaphore_wait((dispatch_semaphore_t)semaphore_impl, DISPATCH_TIME_FOREVER) == 0;
1524 else {
1525 char* data = (char*)semaphore_impl;
1526 if (!data) return false;
1527 pthread_mutex_t* m = (pthread_mutex_t*)data;
1528 pthread_cond_t* c = (pthread_cond_t*)(data + sizeof(pthread_mutex_t));
1529 uint32* cnt = (uint32*)(data + sizeof(pthread_mutex_t) + sizeof(pthread_cond_t));
1530 pthread_mutex_lock(m);
1531 while (*cnt == 0)
1532 pthread_cond_wait(c, m);
1533 (*cnt)--;
1534 pthread_mutex_unlock(m);
1535 return true;
1536 }
1537 #else
1538 int r = sem_wait(semaphore);
1539 return r == 0;
1540 #endif
1541 #endif
1542}
1543
1544bool Semaphore::wait(uint32 timeout) {
1545 #ifdef WINDOWS
1546 return (WaitForSingleObject(semaphore, timeout) == WAIT_OBJECT_0);
1547 #else
1548 #ifdef __APPLE__
1549 if (!name) {
1550 dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)timeout * 1000000);
1551 return dispatch_semaphore_wait((dispatch_semaphore_t)semaphore_impl, when) == 0;
1552 }
1553 else {
1554 char* data = (char*)semaphore_impl;
1555 if (!data) return false;
1556 pthread_mutex_t* m = (pthread_mutex_t*)data;
1557 pthread_cond_t* c = (pthread_cond_t*)(data + sizeof(pthread_mutex_t));
1558 uint32* cnt = (uint32*)(data + sizeof(pthread_mutex_t) + sizeof(pthread_cond_t));
1559 pthread_mutex_lock(m);
1560 struct timespec t;
1561 CalcTimeout(t, timeout);
1562 while (*cnt == 0) {
1563 int r = pthread_cond_timedwait(c, m, &t);
1564 if (r == ETIMEDOUT) {
1565 pthread_mutex_unlock(m);
1566 return false;
1567 }
1568 if (r != 0) {
1569 pthread_mutex_unlock(m);
1570 return false;
1571 }
1572 }
1573 (*cnt)--;
1574 pthread_mutex_unlock(m);
1575 return true;
1576 }
1577 #else
1578 struct timespec t;
1579 CalcTimeout(t, timeout);
1580 int r = sem_timedwait(semaphore, &t);
1581 return r == 0;
1582 #endif
1583 #endif
1584}
1585
1587 #ifdef WINDOWS
1588 return (ReleaseSemaphore(
1589 semaphore, // handle to semaphore
1590 1, // increase count by one
1591 NULL) != 0); // not interested in previous count
1592 #else
1593 #ifdef __APPLE__
1594 if (!name)
1595 return dispatch_semaphore_signal((dispatch_semaphore_t)semaphore_impl) != 0;
1596 else {
1597 char* data = (char*)semaphore_impl;
1598 if (!data) return false;
1599 pthread_mutex_t* m = (pthread_mutex_t*)data;
1600 pthread_cond_t* c = (pthread_cond_t*)(data + sizeof(pthread_mutex_t));
1601 uint32* cnt = (uint32*)(data + sizeof(pthread_mutex_t) + sizeof(pthread_cond_t));
1602 pthread_mutex_lock(m);
1603 (*cnt)++;
1604 pthread_cond_signal(c);
1605 pthread_mutex_unlock(m);
1606 return true;
1607 }
1608 #else
1609 int r = sem_post(semaphore);
1610 return (r == 0);
1611 #endif
1612 #endif
1613}
1614
1615//bool Semaphore::destroy() {
1616// #ifdef WINDOWS
1617// return true;
1618// #else
1619// if (name)
1620// sem_unlink(name);
1621// return true;
1622// #endif
1623//}
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1638 name = NULL;
1639 #ifdef WINDOWS
1640 event = CreateEvent(
1641 NULL, // default security attributes
1642 TRUE, // manual-reset event
1643 FALSE, // initial state is nonsignaled
1644 NULL); // object name
1645 #else
1646 event = new pthread_cond_t;
1647 pthread_cond_init(event, NULL);
1648
1649 mutex = new pthread_mutex_t;
1650 pthread_mutex_init(mutex, NULL);
1651 #endif
1652}
1653
1654Event::Event(const char* name) {
1655 this->name = new char[MAXSHMEMNAMELEN];
1656 #ifdef WINDOWS
1657 //sprintf(this->name, "Global\\Event_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
1658 snprintf(this->name, MAXSHMEMNAMELEN, "%s\\Event_%s", WINSHMEMSPACE, name);
1659 // Creates or gets an existing Event
1660 event = CreateEvent(
1661 NULL, // default security attributes
1662 TRUE, // manual-reset event
1663 FALSE, // initial state is nonsignaled
1664 this->name); // object name
1665 #else
1666 char* data = OpenSharedMemorySegment(this->name, EVENTMEMSIZE);
1667 if (data) {
1668 mutex = (pthread_mutex_t*) data;
1669 event = (pthread_cond_t*) (data + sizeof(pthread_mutex_t));
1670 }
1671 else {
1672 // create shared event
1673 if (data = CreateSharedMemorySegment(this->name, EVENTMEMSIZE, true)) {
1674 // create mutex
1675 mutex = (pthread_mutex_t*) data;
1676 pthread_mutexattr_t attr;
1677 pthread_mutexattr_init(&attr);
1678 pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
1679 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
1680 pthread_mutex_init(mutex, &attr);
1681 pthread_mutexattr_destroy(&attr);
1682
1683 event = (pthread_cond_t*) (data + sizeof(pthread_mutex_t));
1684 pthread_condattr_t cattr;
1685 pthread_condattr_init(&cattr);
1686 pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
1687 // pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
1688 pthread_cond_init(event, &cattr);
1689 pthread_mutexattr_destroy(&attr);
1690 }
1691 }
1692 #endif
1693}
1694
1696 #ifdef WINDOWS
1697 if (event) {
1698 CloseHandle(event);
1699 event = NULL;
1700 }
1701 delete [] name;
1702 #else
1703 if (!name) {
1704 pthread_cond_destroy(event);
1705 delete(event);
1706 pthread_mutex_destroy(mutex);
1707 delete(mutex);
1708 }
1709 else {
1710 pthread_cond_destroy(event);
1711 pthread_mutex_destroy(mutex);
1713 mutex = NULL;
1714 event = NULL;
1715 delete [] name;
1716 }
1717 #endif
1718}
1719
1721 #ifdef WINDOWS
1722 return (WaitForSingleObject(event, INFINITE) == WAIT_OBJECT_0);
1723 #else
1724 pthread_mutex_lock(mutex);
1725 int r = pthread_cond_wait(event, mutex);
1726 pthread_mutex_unlock(mutex);
1727 return r == 0;
1728 #endif
1729}
1730
1731bool Event::waitNext(uint32 timeout) {
1732 #ifdef WINDOWS
1733 return (WaitForSingleObject(event, timeout) == WAIT_OBJECT_0);
1734 #else
1735 pthread_mutex_lock(mutex);
1736 struct timespec t;
1737 CalcTimeout(t, timeout);
1738 int r = pthread_cond_timedwait(event, mutex, &t);
1739 pthread_mutex_unlock(mutex);
1740 return r == 0;
1741 #endif
1742}
1743
1745 #ifdef WINDOWS
1746 if (!SetEvent(event))
1747 return false;
1748// Sleep(1);
1749 ResetEvent(event);
1750 return true;
1751 #else
1752 pthread_mutex_lock(mutex);
1753 int r = pthread_cond_broadcast(event);
1754 pthread_mutex_unlock(mutex);
1755 return (r == 0);
1756 #endif
1757}
1758
1759//bool Event::destroy() {
1760// #ifdef WINDOWS
1761// return true;
1762// #else
1763// if (name)
1764// sem_unlink(name);
1765// return true;
1766// #endif
1767//}
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782std::map<uint32, Timer*>* Timer::timers = NULL;
1783
1785 if (!timers)
1786 timers = new std::map<uint32, Timer*>;
1787 globalID = 0;
1788 while (timers->find(globalID) != timers->end())
1789 globalID++;
1790 (*timers)[globalID] = this;
1791}
1792
1794 if (!timers)
1795 return;
1796 timers->erase(globalID);
1797 mutex.enter();
1798 std::map<uint32, TimerSchedule*>::iterator it = schedules.begin();
1799 std::map<uint32, TimerSchedule*>::iterator itEnd = schedules.end();
1800 while (it != itEnd)
1801 removeTimer((it++)->first);
1802
1803 // Now delete the remaining old schedules that were kept
1804 // in case a timer returned the same time as it was removed
1805 std::list<TimerSchedule*>::iterator it2 = oldSchedules.begin();
1806 std::list<TimerSchedule*>::iterator it2End = oldSchedules.end();
1807 while (it2 != it2End)
1808 delete(*it2++);
1809 oldSchedules.clear();
1810
1811 mutex.leave();
1812}
1813
1814bool Timer::addTimer(uint32 id, uint32 interval, uint64 start, uint64 end) {
1815 mutex.enter();
1816 if (schedules[id]) {
1817 mutex.leave();
1818 return false;
1819 }
1820
1821 TimerSchedule* schedule = new TimerSchedule;
1822 schedule->globalID = globalID;
1823 schedule->id = id;
1824 schedule->handle = 0;
1825 schedule->start = start;
1826 schedule->end = end;
1827 schedule->interval = interval;
1828 schedules[id] = schedule;
1829
1830 uint64 now = GetTimeNow();
1831 uint32 firstDelay = 0;
1832 if (start && (start > now))
1833 firstDelay = (uint32)(start - now)/1000;
1834
1835 // Create and set timer
1836 #ifdef WINDOWS
1837 if (!CreateTimerQueueTimer(&schedule->handle, NULL, TimerCallback, schedule, firstDelay, interval, WT_EXECUTEINTIMERTHREAD)) {
1838 schedules[id] = NULL;
1839 delete(schedule);
1840 mutex.leave();
1841 return false;
1842 }
1843 #else
1844 #ifdef __APPLE__
1845 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
1846 dispatch_source_t timer_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
1847 if (!timer_source) {
1848 schedules[id] = NULL;
1849 delete(schedule);
1850 mutex.leave();
1851 return false;
1852 }
1853 schedule->handle = timer_source;
1854 uint64 firstDelayNs = (uint64)firstDelay * 1000000;
1855 uint64 intervalNs = (uint64)interval * 1000000;
1856 dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (int64_t)firstDelayNs), intervalNs, 0);
1857 dispatch_source_set_event_handler(timer_source, ^{
1858 if (!Timer::timers)
1859 return;
1860 TimerSchedule* sched = schedule;
1861 if (!sched)
1862 return;
1863 Timer* timer = (*Timer::timers)[sched->globalID];
1864 if (timer)
1865 timer->triggerTimer(sched->id);
1866 });
1867 dispatch_resume(timer_source);
1868 #else
1869 struct sigaction sa;
1870 struct sigevent timer_event;
1871
1872 sigemptyset(&sa.sa_mask);
1873 sa.sa_flags = SA_SIGINFO; /* Real-Time signal */
1874 sa.sa_sigaction = TimerCallback;
1875 sigaction(SIGRTMIN, &sa, NULL);
1876
1877 timer_event.sigev_notify = SIGEV_SIGNAL;
1878 timer_event.sigev_signo = SIGRTMIN;
1879 timer_event.sigev_value.sival_ptr = (void *)schedule;
1880 if (timer_create(CLOCK_REALTIME, &timer_event, &schedule->handle) != 0) {
1881 schedules[id] = NULL;
1882 delete(schedule);
1883 mutex.leave();
1884 return false;
1885 }
1886
1887 struct itimerspec newtv;
1888 sigset_t allsigs;
1889
1890 uint64 period = interval * 1000;
1891 newtv.it_value.tv_sec = firstDelay / 1000000;
1892 newtv.it_value.tv_nsec = (firstDelay % 1000000)*1000;
1893 newtv.it_interval.tv_sec = period / 1000000;
1894 newtv.it_interval.tv_nsec = (period % 1000000)*1000;
1895
1896 if (timer_settime(schedule->handle, 0, &newtv, NULL) != 0) {
1897 timer_delete(schedule->handle);
1898 schedules[id] = NULL;
1899 delete(schedule);
1900 mutex.leave();
1901 return false;
1902 }
1903 sigemptyset(&allsigs);
1904 #endif
1905 #endif
1906
1907 mutex.leave();
1908 return true;
1909}
1910
1911bool Timer::removeTimer(uint32 id) {
1912 mutex.enter();
1913
1914 TimerSchedule* schedule = schedules[id];
1915 if (!schedule) {
1916 mutex.leave();
1917 return true;
1918 }
1919
1920 schedules[id] = NULL;
1921
1922 #ifdef WINDOWS
1923 DeleteTimerQueueTimer(NULL, schedule->handle, NULL);
1924 #else
1925 #ifdef __APPLE__
1926 dispatch_source_cancel(schedule->handle);
1927 dispatch_release(schedule->handle);
1928 #else
1929 timer_delete(schedule->handle);
1930 #endif
1931 #endif
1932
1933 oldSchedules.push_back(schedule);
1934 mutex.leave();
1935 return true;
1936}
1937
1938bool Timer::triggerTimer(uint32 id) {
1939 mutex.enter();
1940 TimerSchedule* schedule = schedules[id];
1941 if (!schedule) {
1942 mutex.leave();
1943 return false;
1944 }
1945
1946 TimerTrigger* trigger = new TimerTrigger;
1947 trigger->id = schedule->id;
1948 trigger->time = GetTimeNow();
1949
1950 triggers.push(trigger);
1951 semaphore.signal();
1952
1953 if ( schedule->end && ((int32)(schedule->end - trigger->time) < (int32)schedule->interval))
1954 removeTimer(schedule->id);
1955
1956 mutex.leave();
1957 return true;
1958}
1959
1960bool Timer::waitForTimer(uint32 timeout, uint32& id, uint64& time) {
1961 TimerTrigger* trigger;
1962 uint64 start = GetTimeNow();
1963 int64 timeleft;
1964
1965 mutex.enter();
1966 while (!triggers.size()) {
1967 mutex.leave();
1968 if ( (timeleft = (int32)timeout - GetTimeAgeMS(start)) <= 0)
1969 return false;
1970 semaphore.wait((uint32)timeleft);
1971 mutex.enter();
1972 }
1973
1974 trigger = triggers.front();
1975 triggers.pop();
1976 id = trigger->id;
1977 time = trigger->time;
1978 delete(trigger);
1979 mutex.leave();
1980 return true;
1981}
1982
1983#ifdef WINDOWS
1984 void CALLBACK TimerCallback(PVOID arg, BOOLEAN TimerOrWaitFired) {
1985 if (!Timer::timers)
1986 return;
1987 TimerSchedule* schedule = (TimerSchedule*)arg;
1988 if (!schedule)
1989 return;
1990 Timer* timer = (*Timer::timers)[schedule->globalID];
1991 if (timer)
1992 timer->triggerTimer(schedule->id);
1993 }
1994#else
1995 #ifndef __APPLE__
1996 void TimerCallback(int sig, siginfo_t *siginfo, void *context) {
1997 if (!Timer::timers)
1998 return;
1999 TimerSchedule* schedule = (TimerSchedule*) siginfo->si_value.sival_ptr;
2000 if (!schedule)
2001 return;
2002 Timer* timer = (*Timer::timers)[schedule->globalID];
2003 if (timer)
2004 timer->triggerTimer(schedule->id);
2005 }
2006 #endif
2007#endif
2008
2010 unittest::progress(5, "create timer");
2011 uint32 id;
2012 uint64 time, now = GetTimeNow();
2013
2014 Timer* timer = new Timer();
2015 if (!timer) {
2016 unittest::fail("Timer test: failed to create Timer");
2017 return false;
2018 }
2019
2020 unittest::progress(20, "schedule timers");
2021 if (!timer->addTimer(1, 1000, now+50000, now+10000000)) {
2022 unittest::fail("Timer test: addTimer 1 failed");
2023 delete(timer);
2024 return false;
2025 }
2026 if (!timer->addTimer(2, 2000, now+55000, now+15000000)) {
2027 unittest::fail("Timer test: addTimer 2 failed");
2028 delete(timer);
2029 return false;
2030 }
2031 if (!timer->addTimer(3, 3000, now+60000, now+20000000)) {
2032 unittest::fail("Timer test: addTimer 3 failed");
2033 delete(timer);
2034 return false;
2035 }
2036
2037 unittest::progress(40, "wait for triggers");
2038 uint32 triggers = 0;
2039 double totalLatencyUs = 0;
2040 uint64 t0 = GetTimeNow();
2041 // Bounded loop: timers fire every 1-3ms after ~50ms, so this returns quickly.
2042 // Stop once we have collected a stable sample or the wall-clock budget elapses.
2043 for (uint32 n = 0; n < 60; n++) {
2044 if (timer->waitForTimer(100, id, time)) {
2045 now = GetTimeNow();
2046 triggers++;
2047 totalLatencyUs += (double)(now - time);
2048 unittest::detail("Timer %u triggered %.3f ms ago", id, (now - time) / 1000.0);
2049 }
2050 else {
2051 unittest::detail("(Timeout)");
2052 }
2053 // Keep the test well under a second of wall time.
2054 if ((GetTimeNow() - t0) > 2000000ULL)
2055 break;
2056 }
2057
2058 unittest::progress(85, "verify triggers");
2059 if (triggers == 0) {
2060 unittest::fail("Timer test: no timer triggers received");
2061 delete(timer);
2062 return false;
2063 }
2064
2065 unittest::metric("timer_triggers", (double)triggers, "count", true);
2066 unittest::metric("avg_trigger_latency", totalLatencyUs / (double)triggers, "us", false);
2067
2068 delete(timer);
2069 unittest::progress(100, "done");
2070 return true;
2071}
2072
2073// utils_probe_wait (2.7b-i): ProbeProcessWait detects a child blocked reading stdin
2074// and reports growing CPU time for a busy child. POSIX only; trivially passes on
2075// Windows (Windows probe is a stub until 2.7c).
2077#if defined(WINDOWS)
2078 unittest::progress(100, "Windows stub (full detection in 2.7c)");
2079 return true;
2080#else
2081 unittest::progress(10, "spawn stdin-blocked child: sh -c 'read x' on a pipe");
2082 int inPipe[2];
2083 if (pipe(inPipe) != 0) { unittest::fail("pipe() failed"); return false; }
2084 pid_t child = fork();
2085 if (child < 0) { unittest::fail("fork() failed"); close(inPipe[0]); close(inPipe[1]); return false; }
2086 if (child == 0) {
2087 dup2(inPipe[0], STDIN_FILENO);
2088 close(inPipe[0]); close(inPipe[1]);
2089 execl("/bin/sh", "sh", "-c", "read x", (char*)NULL);
2090 _exit(127);
2091 }
2092 close(inPipe[0]); // keep write end open so the child's read(0) blocks
2093 uint32 pid = (uint32)child;
2094 ProcessWaitInfo info; info.alive = false; info.waitingStdin = false; info.waitExact = false; info.cpuTimeMs = 0;
2095 bool detected = false;
2096 for (int i = 0; i < 40 && !detected; i++) { // up to 2s
2097 if (!ProbeProcessWait(pid, info)) { unittest::fail("ProbeProcessWait returned false"); kill(child, SIGKILL); close(inPipe[1]); waitpid(child, NULL, 0); return false; }
2098 if (info.alive && info.waitingStdin) detected = true;
2099 else Sleep(50);
2100 }
2101 if (!detected) {
2102 unittest::fail("stdin-blocked child not detected (alive=%d waitingStdin=%d waitExact=%d)",
2103 (int)info.alive, (int)info.waitingStdin, (int)info.waitExact);
2104 kill(child, SIGKILL); close(inPipe[1]); waitpid(child, NULL, 0); return false;
2105 }
2106#if defined(__linux__)
2107 if (!info.waitExact) { unittest::fail("Linux should report waitExact"); kill(child, SIGKILL); close(inPipe[1]); waitpid(child, NULL, 0); return false; }
2108#endif
2109 kill(child, SIGKILL);
2110 close(inPipe[1]);
2111 waitpid(child, NULL, 0);
2112 int rc;
2113 unittest::progress(55, "spawn CPU-busy child, expect no stdin-wait + growing cpuTimeMs");
2114 uint32 pid2 = NewProcessEx("i=0; while [ $i -lt 100000000 ]; do i=$((i+1)); done",
2115 NULL, NULL, NULL, true, false, true, SIGKILL, true);
2116 if (!pid2) { unittest::fail("NewProcessEx(busy loop) failed"); return false; }
2117 Sleep(200);
2118 ProcessWaitInfo a, b;
2119 if (!ProbeProcessWait(pid2, a) || !a.alive) { unittest::fail("busy child probe 1 failed"); EndProcess(pid2); return false; }
2120 Sleep(400);
2121 if (!ProbeProcessWait(pid2, b) || !b.alive) { unittest::fail("busy child probe 2 failed"); EndProcess(pid2); return false; }
2122 bool busyFlagged = a.waitingStdin || b.waitingStdin;
2123 EndProcess(pid2);
2124 WaitForProcess(pid2, 2000, rc);
2125 if (busyFlagged) { unittest::fail("busy child wrongly flagged waitingStdin"); return false; }
2126 if (b.cpuTimeMs <= a.cpuTimeMs) { unittest::fail("cpuTimeMs did not grow (%llu -> %llu)",
2127 (unsigned long long)a.cpuTimeMs, (unsigned long long)b.cpuTimeMs); return false; }
2128 unittest::detail("cpu delta %llu ms over 400 ms window", (unsigned long long)(b.cpuTimeMs - a.cpuTimeMs));
2129 return true;
2130#endif
2131}
2132
2134 unittest::progress(5, "allocate bitfield");
2135
2136 uint32 slotCount = 350;
2137 uint32 bitFieldSize = Calc32BitFieldSize(slotCount);
2138 char* bitField = new char[bitFieldSize];
2139 uint32 loc;
2140
2141 // First set the full new bitfield to unoccupied
2142 Reset32BitField(bitField, bitFieldSize);
2143 unittest::detail("%s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str());
2144
2145 unittest::progress(15, "empty bitfield queries");
2146 if (GetLastOccupiedBitLoc(bitField, bitFieldSize, loc)) {
2147 unittest::fail("GetLastOccupiedBitLoc: loc %u, expected: false", loc);
2148 delete[] bitField;
2149 return false;
2150 }
2151
2152 if (!GetFirstFreeBitLoc(bitField, bitFieldSize, loc) || (loc != 0)) {
2153 unittest::fail("GetFirstFreeBitLoc failed: %u, expected: 0", loc);
2154 delete[] bitField;
2155 return false;
2156 }
2157 if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 10, loc) || (loc != 0)) {
2158 unittest::fail("GetFirstFreeBitLocN failed: %u, expected: 0", loc);
2159 delete[] bitField;
2160 return false;
2161 }
2162
2163 unittest::progress(30, "single SetBit");
2164 if (!SetBit(10, BITOCCUPIED, bitField, bitFieldSize)) {
2165 unittest::fail("SetBit failed");
2166 delete[] bitField;
2167 return false;
2168 }
2169 unittest::detail("Bitfield: %s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str());
2170
2171 if (!GetFirstFreeBitLoc(bitField, bitFieldSize, loc) || (loc != 0)) {
2172 unittest::fail("GetFirstFreeBitLoc 2 failed: %u, expected: 0", loc);
2173 delete[] bitField;
2174 return false;
2175 }
2176 if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 10, loc) || (loc != 0)) {
2177 unittest::fail("GetFirstFreeBitLocN 2 failed: %u, expected: 0", loc);
2178 delete[] bitField;
2179 return false;
2180 }
2181
2182 if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 11, loc) || (loc != 11)) {
2183 unittest::detail("Bitfield: %s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str());
2184 unittest::fail("GetFirstFreeBitLocN 3 failed: %u, expected: 11", loc);
2185 delete[] bitField;
2186 return false;
2187 }
2188 if (!GetLastOccupiedBitLoc(bitField, bitFieldSize, loc) || (loc != 10)) {
2189 unittest::fail("GetLastOccupiedBitLoc: loc %u, expected: 10", loc);
2190 delete[] bitField;
2191 return false;
2192 }
2193
2194 unittest::progress(50, "SetBitN occupied ranges");
2195 if (!SetBit(0, BITOCCUPIED, bitField, bitFieldSize)) {
2196 unittest::fail("SetBit 2 failed");
2197 delete[] bitField;
2198 return false;
2199 }
2200 if (!SetBitN(100, 50, BITOCCUPIED, bitField, bitFieldSize)) {
2201 unittest::fail("SetBitN 100 failed");
2202 delete[] bitField;
2203 return false;
2204 }
2205 unittest::detail("%s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str());
2206
2207 if (!GetFirstFreeBitLoc(bitField, bitFieldSize, loc) || (loc != 1)) {
2208 unittest::fail("GetFirstFreeBitLoc 4 failed: %u, expected: 1", loc);
2209 delete[] bitField;
2210 return false;
2211 }
2212 if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 10, loc) || (loc != 11)) {
2213 unittest::fail("GetFirstFreeBitLocN 5 failed: %u, expected: 11", loc);
2214 delete[] bitField;
2215 return false;
2216 }
2217 if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 11, loc) || (loc != 11)) {
2218 unittest::fail("GetFirstFreeBitLocN 6 failed: %u, expected: 11", loc);
2219 delete[] bitField;
2220 return false;
2221 }
2222 if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 100, loc) || (loc != 150)) {
2223 unittest::fail("GetFirstFreeBitLocN 7 failed: %u, expected: 150", loc);
2224 delete[] bitField;
2225 return false;
2226 }
2227 if (!GetLastOccupiedBitLoc(bitField, bitFieldSize, loc) || (loc != 149)) {
2228 unittest::fail("GetLastOccupiedBitLoc: loc %u, expected: 149", loc);
2229 delete[] bitField;
2230 return false;
2231 }
2232
2233 if (!SetBitN(200, 50, BITOCCUPIED, bitField, bitFieldSize)) {
2234 unittest::fail("SetBitN 200 failed");
2235 delete[] bitField;
2236 return false;
2237 }
2238 unittest::detail("%s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str());
2239
2240 unittest::progress(70, "free-loc searches");
2241 if (!GetFirstFreeBitLoc(bitField, bitFieldSize, loc) || (loc != 1)) {
2242 unittest::fail("GetFirstFreeBitLoc 8 failed: %u, expected: 1", loc);
2243 delete[] bitField;
2244 return false;
2245 }
2246 if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 10, loc) || (loc != 11)) {
2247 unittest::fail("GetFirstFreeBitLocN 9 failed: %u, expected: 11", loc);
2248 delete[] bitField;
2249 return false;
2250 }
2251 if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 11, loc) || (loc != 11)) {
2252 unittest::fail("GetFirstFreeBitLocN 10 failed: %u, expected: 11", loc);
2253 delete[] bitField;
2254 return false;
2255 }
2256 if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 100, loc) || (loc != 250)) {
2257 unittest::fail("GetFirstFreeBitLocN 11 failed: %u, expected: 250", loc);
2258 delete[] bitField;
2259 return false;
2260 }
2261 if (GetFirstFreeBitLocN(bitField, bitFieldSize, 200, loc)) {
2262 unittest::fail("GetFirstFreeBitLocN 12 failed: %u, expected: false", loc);
2263 delete[] bitField;
2264 return false;
2265 }
2266
2267 if (!GetLastOccupiedBitLoc(bitField, bitFieldSize, loc) || (loc != 249)) {
2268 unittest::fail("GetLastOccupiedBitLoc: loc %u, expected: 249", loc);
2269 delete[] bitField;
2270 return false;
2271 }
2272
2273 unittest::progress(85, "clear range and re-search");
2274 if (!SetBitN(210, 30, BITFREE, bitField, bitFieldSize)) {
2275 unittest::fail("SetBitN 210 failed");
2276 delete[] bitField;
2277 return false;
2278 }
2279 unittest::detail("%s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str());
2280
2281 if (!GetFirstFreeBitLoc(bitField, bitFieldSize, loc) || (loc != 1)) {
2282 unittest::fail("GetFirstFreeBitLoc 13 failed: %u, expected: 1", loc);
2283 delete[] bitField;
2284 return false;
2285 }
2286 if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 10, loc) || (loc != 11)) {
2287 unittest::fail("GetFirstFreeBitLocN 14 failed: %u, expected: 11", loc);
2288 delete[] bitField;
2289 return false;
2290 }
2291 if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 11, loc) || (loc != 11)) {
2292 unittest::fail("GetFirstFreeBitLocN 15 failed: %u, expected: 11", loc);
2293 delete[] bitField;
2294 return false;
2295 }
2296 if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 52, loc) || (loc != 11)) {
2297 unittest::fail("GetFirstFreeBitLocN 16 failed: %u, expected: 11", loc);
2298 delete[] bitField;
2299 return false;
2300 }
2301 if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 100, loc) || (loc != 250)) {
2302 unittest::fail("GetFirstFreeBitLocN 17 failed: %u, expected: 250", loc);
2303 delete[] bitField;
2304 return false;
2305 }
2306 if (!GetLastOccupiedBitLoc(bitField, bitFieldSize, loc) || (loc != 249)) {
2307 unittest::fail("GetLastOccupiedBitLoc: loc %u, expected: 249", loc);
2308 delete[] bitField;
2309 return false;
2310 }
2311
2312 if (!SetBitN(50, 30, BITOCCUPIED, bitField, bitFieldSize)) {
2313 unittest::fail("SetBitN 50 failed");
2314 delete[] bitField;
2315 return false;
2316 }
2317 unittest::detail("%s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str());
2318
2319 if (!GetFirstFreeBitLoc(bitField, bitFieldSize, loc) || (loc != 1)) {
2320 unittest::fail("GetFirstFreeBitLoc 18 failed: %u, expected: 1", loc);
2321 delete[] bitField;
2322 return false;
2323 }
2324 if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 10, loc) || (loc != 11)) {
2325 unittest::fail("GetFirstFreeBitLocN 19 failed: %u, expected: 11", loc);
2326 delete[] bitField;
2327 return false;
2328 }
2329 if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 11, loc) || (loc != 11)) {
2330 unittest::fail("GetFirstFreeBitLocN 20 failed: %u, expected: 11", loc);
2331 delete[] bitField;
2332 return false;
2333 }
2334 if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 52, loc) || (loc != 250)) {
2335 unittest::fail("GetFirstFreeBitLocN 21 failed: %u, expected: 250", loc);
2336 delete[] bitField;
2337 return false;
2338 }
2339 if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 100, loc) || (loc != 250)) {
2340 unittest::fail("GetFirstFreeBitLocN 22 failed: %u, expected: 250", loc);
2341 delete[] bitField;
2342 return false;
2343 }
2344 if (GetFirstFreeBitLocN(bitField, bitFieldSize, 200, loc)) {
2345 unittest::fail("GetFirstFreeBitLocN 23 failed: %u, expected: false", loc);
2346 delete[] bitField;
2347 return false;
2348 }
2349 if (!GetLastOccupiedBitLoc(bitField, bitFieldSize, loc) || (loc != 249)) {
2350 unittest::fail("GetLastOccupiedBitLoc: loc %u, expected: 249", loc);
2351 delete[] bitField;
2352 return false;
2353 }
2354
2355 delete[] bitField;
2356 unittest::progress(100, "done");
2357 return true;
2358}
2359
2360
2361
2362
2363
2364
2365
2366
2367// Create named shared memory segment
2368char* CreateSharedMemorySegment(const char* name, uint64 size, bool force) {
2369
2370 char* memname = new char[MAXSHMEMNAMELEN];
2371 #ifdef WINDOWS
2372 //sprintf(memname, "Global\\Shmem_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
2373 snprintf(memname, MAXSHMEMNAMELEN, "%s\\Shmem_%s", WINSHMEMSPACE, name);
2374 HANDLE hMapFile;
2375 char* pBuf;
2376
2377 hMapFile = CreateFileMapping(
2378 INVALID_HANDLE_VALUE, // use paging file
2379 NULL, // default security
2380 PAGE_READWRITE, // read/write access
2381 (uint32)((size >> 32) & 0xffffffff), // maximum object size (high-order DWORD)
2382 (uint32)(size & 0xffffffff), // maximum object size (low-order DWORD)
2383 memname); // name of mapping object
2384
2385 // Capture immediately: CreateFileMapping succeeds for an already-existing
2386 // named mapping and sets ERROR_ALREADY_EXISTS. On Windows a named mapping
2387 // lives until the last handle closes, so a segment left behind by a prior
2388 // in-process user (e.g. a previous unit test reusing system id 0) is handed
2389 // back with its OLD contents rather than fresh zeroed memory. Unlike the
2390 // POSIX/macOS branches - which honour `force` by unlinking and recreating -
2391 // this branch previously ignored `force`, so a forced create silently
2392 // reused stale state. Record whether it pre-existed so we can re-init below.
2393 bool alreadyExisted = (GetLastError() == ERROR_ALREADY_EXISTS);
2394
2395 if (hMapFile == NULL) {
2396 LogPrint(0,LOG_SYSTEM,0, "Could not create shared memory '%s': %s", memname, GetLastOSErrorMessage().c_str());
2397 delete [] memname;
2398 return NULL;
2399 }
2400 delete [] memname;
2401 pBuf = (char*) MapViewOfFile(hMapFile, // handle to map object
2402 FILE_MAP_ALL_ACCESS, // read/write permission
2403 0, 0, 0); // from beginning to end
2404
2405 if (pBuf == NULL) {
2406 int error = GetLastError();
2407 CloseHandle(hMapFile);
2408 return NULL;
2409 }
2410
2411 // When the caller asked for a fresh segment (force) but Windows returned an
2412 // existing named mapping, zero the view so the caller gets clean memory -
2413 // matching the force semantics on POSIX/macOS. Without this, a forced
2414 // create over a leaked segment inherits stale occupancy/state, which is
2415 // exactly what made the CMSDK ProcessMemory tests fail on every other run.
2416 if (force && alreadyExisted)
2417 memset(pBuf, 0, (size_t)size);
2418
2421 // Save the handle so we can close it later
2422 (*SharedMemoryFileHandleMap)[pBuf] = hMapFile;
2423
2424 return pBuf;
2425 #else
2426 #ifdef __APPLE__
2427 /* macOS: use file-based shared memory (shm_open is unreliable, errno 63) */
2428 snprintf(memname, MAXSHMEMNAMELEN, "/tmp/Psyclone_Shmem_%s", name);
2429 if (force)
2430 unlink(memname);
2431 int fd = open(memname, O_CREAT | O_RDWR, 0666);
2432 if (fd == -1) {
2433 LogPrint(0,LOG_SYSTEM,0,"Couldn't create shared memory: '%s' (err: %d)...", memname, errno);
2434 delete [] memname;
2435 return NULL;
2436 }
2437 if (ftruncate(fd, size) == -1) {
2438 LogPrint(0,LOG_SYSTEM,0,"Couldn't truncate shared memory: '%s' size: %u (%d) (err: %d)...", memname, size, fd, errno);
2439 delete [] memname;
2440 close(fd);
2441 return NULL;
2442 }
2443 char* pBuf = (char*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
2444 if (pBuf == MAP_FAILED) {
2445 LogPrint(0,LOG_SYSTEM,0,"Couldn't map created shared memory: '%s' (err: %d)...", memname, errno);
2446 delete [] memname;
2447 close(fd);
2448 return NULL;
2449 }
2452 (*SharedMemoryFileHandleMap)[pBuf] = std::string(memname);
2453 close(fd);
2454 delete [] memname;
2455 return pBuf;
2456 #else
2457 /* Linux: POSIX shm_open */
2458 snprintf(memname, MAXSHMEMNAMELEN, "/Shmem_%s", name);
2459 int fd = shm_open(memname, O_CREAT | O_RDWR, 0666);
2460 if (fd == -1) {
2461 LogPrint(0,LOG_SYSTEM,0,"Couldn't create shared memory: '%s' (err: %d)...", memname, errno);
2462 delete [] memname;
2463 return NULL;
2464 }
2465 if (ftruncate(fd, size) == -1) {
2466 LogPrint(0,LOG_SYSTEM,0,"Couldn't truncate shared memory: '%s' size: %u (%d) (err: %d)...", memname, size, fd, errno);
2467 delete [] memname;
2468 close(fd);
2469 return NULL;
2470 }
2471 char* pBuf = (char*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
2472 if (pBuf == MAP_FAILED) {
2473 LogPrint(0,LOG_SYSTEM,0,"Couldn't map created shared memory: '%s' (err: %d)...", memname, errno);
2474 delete [] memname;
2475 close(fd);
2476 return NULL;
2477 }
2480 (*SharedMemoryFileHandleMap)[pBuf] = memname;
2481 close(fd);
2482 delete [] memname;
2483 return pBuf;
2484 #endif
2485 #endif
2486}
2487
2488// Get named shared memory segment, size = 0 means autodetect size
2489char* OpenSharedMemorySegment(const char* name, uint64 size) {
2490
2491 char* memname = new char[MAXSHMEMNAMELEN];
2492 #ifdef WINDOWS
2493 // sprintf(memname, "Global\\Shmem_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
2494 snprintf(memname, MAXSHMEMNAMELEN, "%s\\Shmem_%s", WINSHMEMSPACE, name);
2495 HANDLE hMapFile;
2496 char* pBuf;
2497
2498 hMapFile = OpenFileMapping(
2499 FILE_MAP_ALL_ACCESS, // read/write access
2500 FALSE, // do not inherit the name
2501 memname); // name of mapping object
2502
2503 if (hMapFile == NULL) {
2504 // LogPrint(0,LOG_SYSTEM,0, "Could not open shared memory '%s': %s", memname, GetLastOSErrorMessage().c_str());
2505 delete [] memname;
2506 return NULL;
2507 }
2508
2509 pBuf = (char*) MapViewOfFile(hMapFile, // handle to map object
2510 FILE_MAP_ALL_ACCESS, // read/write permission
2511 0, 0, 0); // from beginning to end
2512
2513 if (pBuf == NULL) {
2514 LogPrint(0,LOG_SYSTEM,0, "Could not map shared memory '%s': %s", memname, GetLastOSErrorMessage().c_str());
2515 delete [] memname;
2516 GetLastError();
2517 CloseHandle(hMapFile);
2518 return NULL;
2519 }
2520 delete [] memname;
2521
2522 // Save the handle so we can close it later
2525 (*SharedMemoryFileHandleMap)[pBuf] = hMapFile;
2526
2527 return pBuf;
2528 #else
2529 #ifdef __APPLE__
2530 /* macOS: open file-based shared memory */
2531 snprintf(memname, MAXSHMEMNAMELEN, "/tmp/Psyclone_Shmem_%s", name);
2532 int fd = open(memname, O_RDWR);
2533 if (fd == -1) {
2534 delete [] memname;
2535 return NULL;
2536 }
2537 char* pBuf;
2538 uint64 memSize = size;
2539 if (!size) {
2540 /* Autodetect: the segment file was ftruncate'd to its full size at
2541 creation, so the on-disk file size IS the segment size. (Reading a
2542 size prefix from offset 0 is wrong - callers store their own header
2543 there, e.g. RequestServerHeader.) */
2544 struct stat st;
2545 if (fstat(fd, &st) == -1 || st.st_size <= 0) {
2546 delete [] memname;
2547 close(fd);
2548 return NULL;
2549 }
2550 memSize = (uint64) st.st_size;
2551 }
2552 pBuf = (char*)mmap(NULL, memSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
2553 if (pBuf == MAP_FAILED) {
2554 delete [] memname;
2555 close(fd);
2556 return NULL;
2557 }
2560 (*SharedMemoryFileHandleMap)[pBuf] = std::string(memname);
2561 close(fd);
2562 delete [] memname;
2563 return pBuf;
2564 #else
2565 /* Linux: POSIX shm_open */
2566 snprintf(memname, MAXSHMEMNAMELEN, "/Shmem_%s", name);
2567 int fd = shm_open(memname, O_RDWR, 0666);
2568 if (fd == -1) {
2569 delete [] memname;
2570 return NULL;
2571 }
2572 char* pBuf;
2573 uint64 memSize = size;
2574 if (!size) {
2575 /* Autodetect from the actual segment size (ftruncate'd at creation),
2576 not from a size prefix at offset 0 - callers store their own header
2577 there. */
2578 struct stat st;
2579 if (fstat(fd, &st) == -1 || st.st_size <= 0) {
2580 LogPrint(0,LOG_SYSTEM,0,"Couldn't stat opened shared memory: '%s' (err: %d)...", memname, errno);
2581 delete [] memname;
2582 close(fd);
2583 return NULL;
2584 }
2585 memSize = (uint64) st.st_size;
2586 }
2587 pBuf = (char*)mmap(NULL, memSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
2588 if (pBuf == MAP_FAILED) {
2589 delete [] memname;
2590 close(fd);
2591 return NULL;
2592 }
2595 (*SharedMemoryFileHandleMap)[pBuf] = memname;
2596 close(fd);
2597 delete [] memname;
2598 return pBuf;
2599 #endif
2600 #endif
2601}
2602
2603// Close named shared memory segment
2604bool CloseSharedMemorySegment(char* data, uint64 size) {
2605 if (!data || !size)
2606 return true;
2607 #ifdef WINDOWS
2608 UnmapViewOfFile(data);
2609 CloseHandle((*SharedMemoryFileHandleMap)[data]);
2610 SharedMemoryFileHandleMap->erase(data);
2611 return true;
2612 #else
2613 munmap(data, size);
2614 #ifdef __APPLE__
2615 unlink((*SharedMemoryFileHandleMap)[data].c_str());
2616 #else
2617 shm_unlink((*SharedMemoryFileHandleMap)[data].c_str());
2618 #endif
2619 SharedMemoryFileHandleMap->erase(data);
2620 return true;
2621 #endif
2622}
2623
2624// Destroy named shared memory segment
2625//bool DestroySharedMemorySegment(char* data, uint32 size) {
2626// #ifdef WINDOWS
2627// UnmapViewOfFile(data);
2628// CloseHandle(SharedMemoryFileHandleMap[data]);
2629// SharedMemoryFileHandleMap[data] = NULL;
2630// return true;
2631// #else
2632// munmap(data, size);
2633// close(SharedMemoryFileHandleMap[data]);
2634// SharedMemoryFileHandleMap[data] = NULL;
2635// return (shm_unlink(data) == 0);
2636// #endif
2637//}
2638
2639#ifndef WINDOWS
2640// True if 'name' contains the port digits as a delimited token:
2641// preceding char non-digit, following char non-digit or end.
2642// Prevents port 10000 matching 100001 or 10001.
2643static bool ContainsPortToken(const char* name, const char* portstr, size_t plen) {
2644 const char* p = name;
2645 while ((p = strstr(p, portstr)) != NULL) {
2646 bool leftOK = (p == name) || !isdigit((unsigned char)*(p - 1));
2647 char after = *(p + plen);
2648 bool rightOK = (after == '\0') || !isdigit((unsigned char)after);
2649 if (leftOK && rightOK)
2650 return true;
2651 p++;
2652 }
2653 return false;
2654}
2655
2656static uint32 ClearSegmentsIn(const char* dir, const char* prefix, const char* portstr, size_t plen) {
2657 uint32 count = 0;
2658 DIR* d = opendir(dir);
2659 if (!d) return 0;
2660 size_t preflen = strlen(prefix);
2661 struct dirent* e;
2662 while ((e = readdir(d)) != NULL) {
2663 if (strncmp(e->d_name, prefix, preflen) != 0)
2664 continue;
2665 if (!ContainsPortToken(e->d_name, portstr, plen))
2666 continue;
2667 std::string path = std::string(dir) + "/" + e->d_name;
2668 if (unlink(path.c_str()) == 0)
2669 count++;
2670 }
2671 closedir(d);
2672 return count;
2673}
2674#endif
2675
2676// Remove stale shared-memory/semaphore objects for this port (caller must have
2677// verified no live node on the port). Windows sections auto-clean: no-op.
2678void ClearStaleSharedSegments(uint16 port) {
2679 #ifdef WINDOWS
2680 (void)port;
2681 #else
2682 char portstr[8];
2683 snprintf(portstr, sizeof(portstr), "%u", port);
2684 size_t plen = strlen(portstr);
2685 uint32 count = 0;
2686 #ifdef __APPLE__
2687 count += ClearSegmentsIn("/tmp", "Psyclone_Shmem_", portstr, plen);
2688 #else
2689 count += ClearSegmentsIn("/dev/shm", "Shmem_", portstr, plen);
2690 count += ClearSegmentsIn("/dev/shm", "sem.Semaphore_", portstr, plen);
2691 #endif
2692 if (count)
2693 LogPrint(0, LOG_SYSTEM, 1, "Cleared %u stale shared segment(s) for port %u...", count, port);
2694 #endif
2695}
2696
2697
2698
2699
2700
2701
2702uint64 ntoh64(const uint64 *input) {
2703 uint64 rval;
2704 uint8 *data = (uint8*)&rval;
2705 data[0] = (uint8)(*input >> 56);
2706 data[1] = (uint8)(*input >> 48);
2707 data[2] = (uint8)(*input >> 40);
2708 data[3] = (uint8)(*input >> 32);
2709 data[4] = (uint8)(*input >> 24);
2710 data[5] = (uint8)(*input >> 16);
2711 data[6] = (uint8)(*input >> 8);
2712 data[7] = (uint8)(*input >> 0);
2713 return rval;
2714}
2715
2716uint64 hton64(const uint64 *input) {
2717 return (ntoh64(input));
2718}
2719
2720
2721
2722
2723uint32 Calc32BitFieldSize(uint32 bitsize) {
2724 return ((bitsize + 31) >> 5) << 2;
2725}
2726
2727bool Reset32BitField(char* bitfield, uint32 bytesize) {
2728 memset(bitfield, 255, bytesize);
2729 return true;
2730}
2731
2732// Get first zero bit in field of size bits, starting from data loc
2733bool GetFirstFreeBitLoc(const char* bitfield, uint32 bytesize, uint32& loc) {
2734 uint32* data = (uint32*)bitfield;
2735 uint32* src = (uint32*)data;
2736 int32 index;
2737
2738 // Bound in WORDS, and check BEFORE dereferencing. The original loop compared
2739 // a word delta (src - data) against a BYTE count and only tested after having
2740 // already read *src, so a fully-occupied field (all-zero words) over-read by
2741 // ~4x the buffer - up to ~384 bytes past a 128-byte bitfield. It only ever
2742 // looked harmless because callers allocate the bitfield with the payload
2743 // (ThreadStats / map entries / bin blocks) immediately behind it, so the wild
2744 // read landed in owned memory and returned a garbage loc that downstream
2745 // range guards happened to reject. In a shared-memory segment whose bitfield
2746 // sits near the tail of the mapping that is a SIGSEGV/SIGBUS instead.
2747 const int32 words = (int32)(bytesize >> 2);
2748 while (src - data < words && *src == 0)
2749 src++;
2750 if (src - data >= words)
2751 return false; // field is fully occupied
2752 #if defined WINDOWS
2753 _BitScanForward((DWORD*)&index, *src);
2754 #elif defined LINUX
2755 index = ffsl(*src) - 1;
2756 #elif defined OSX
2757 index = ffsl(*src) - 1;
2758 #endif
2759
2760 loc = ((uint32)(src-data)*32) + index;
2761 return true;
2762}
2763
2764// Get first block of num zero bit in field of size bits, starting from data loc
2765bool GetFirstFreeBitLocN(const char* bitfield, uint32 bytesize, uint32 num, uint32& loc) {
2766 uint32* data = (uint32*)bitfield;
2767 uint32* src = (uint32*)data;
2768 int32 index;
2769
2770 uint32 l;
2771 bit val;
2772
2773 uint32 foundBlock = 0;
2774 // Bound in WORDS (see GetFirstFreeBitLoc): the original compared a word delta
2775 // against a byte count and tested only after dereferencing.
2776 const int32 words = (int32)(bytesize >> 2);
2777 while (true) {
2778 while (src - data < words && *src == 0)
2779 src++;
2780 if (src - data >= words)
2781 return false; // no free bit left at all
2782 #if defined WINDOWS
2783 _BitScanForward((DWORD*)&index, *src);
2784 #elif defined LINUX
2785 index = ffsl(*src) - 1;
2786 #elif defined OSX
2787 index = ffsl(*src) - 1;
2788 #endif
2789 loc = ((uint32)(src - data) * 32) + index;
2790 l = loc + 1;
2791 foundBlock = 1;
2792 // Check that bit `l` is inside the buffer -- in WORDS, because the read below
2793 // dereferences a whole uint32 at (l >> 5). The old guard tested the BYTE
2794 // containing bit l ((l + 7) >> 3 > bytesize), which passes for any bit in the
2795 // final 1-3 bytes of a bitfield whose size is not a multiple of 4 and then
2796 // reads up to 3 bytes past the end. ASan caught this as a 4-byte
2797 // heap-buffer-overflow READ against a 44-byte bitfield; it is NOT test-only --
2798 // TemporalMemory::addMessage (TemporalMemory.cpp:327,342) calls this on a bin
2799 // bitfield inside a shared-memory segment, laid out header|bitfield|blockdata,
2800 // so the over-read silently mixes message payload into the free-block
2801 // decision, and for the last bin in a mapping it can run off the mapping.
2802 if ((l >> 5) >= (bytesize >> 2))
2803 return false;
2804 {
2805 uint32* word = (uint32*)bitfield + (l >> 5);
2806 uint32 n = l & 31;
2807 #if defined WINDOWS
2808 val = (_bittest((long*)word, n) != 0);
2809 #elif defined LINUX
2810 val = (bit)((*word & (static_cast<uint32>(1) << n)) != 0);
2811 #elif defined OSX
2812 val = (bit)((*word & (static_cast<uint32>(1) << n)) != 0);
2813 #endif
2814 }
2815 while (true) {
2816 l++;
2817 index++;
2818 if (val == BITFREE) {
2819 foundBlock++;
2820 if (foundBlock >= num)
2821 return true;
2822 if (index % 32 == 0) {
2823 // Fast-forward whole free words. This run had NO bound at all: with a
2824 // large `num` and a long tail of all-free words it walked src past
2825 // the end of the bitfield reading MAXVALUINT32 out of whatever
2826 // followed. Bound every step in words.
2827 while ((num - foundBlock > 31) && (src - data < words) && (*src == MAXVALUINT32)) {
2828 foundBlock += 32;
2829 l += 32;
2830 src++;
2831 }
2832 if (src - data >= words)
2833 return false;
2834 if ((num - foundBlock > 15) && (*(uint16*)src == MAXVALUINT16)) {
2835 foundBlock += 16;
2836 l += 16;
2837 index += 16;
2838 if ((num - foundBlock > 7) && (*((unsigned char*)src+2) == 255)) {
2839 foundBlock += 8;
2840 l += 8;
2841 index += 8;
2842 }
2843 }
2844 else if ((num - foundBlock > 7) && (*(unsigned char*)src == 255)) {
2845 foundBlock += 8;
2846 l += 8;
2847 index += 8;
2848 }
2849 }
2850 if (foundBlock >= num)
2851 return true;
2852 }
2853 else {
2854 foundBlock = 0;
2855 loc = l;
2856 // are we on a boundary
2857 if (index % 32 == 0)
2858 break;
2859 }
2860 // Same word-granular bound as the first inline read above: this
2861 // dereferences a full uint32 at (l >> 5), so a byte-granular guard lets it
2862 // read past the end of a bitfield whose size is not a multiple of 4.
2863 if ((l >> 5) >= (bytesize >> 2))
2864 return false;
2865 {
2866 uint32* word = (uint32*)bitfield + (l >> 5);
2867 uint32 n = l & 31;
2868 #if defined WINDOWS
2869 val = (_bittest((long*)word, n) != 0);
2870 #elif defined LINUX
2871 val = (bit)((*word & (static_cast<uint32>(1) << n)) != 0);
2872 #elif defined OSX
2873 val = (bit)((*word & (static_cast<uint32>(1) << n)) != 0);
2874 #endif
2875 }
2876 }
2877 // we are now on a boundary, jump back and use the bitscan search
2878 src += (uint32)(index / 32);
2879 if (src - data >= words)
2880 return false; // walked off the end looking for a big enough block
2881 index = 0;
2882 // if we get here, the block found wasn't big enough, continue
2883 }
2884 return true;
2885}
2886
2887// Set the nth bit to value in field of size bits, starting from data loc
2888bool SetBit(uint32 loc, bit value, char* bitfield, uint32 bytesize) {
2889 if ((loc + 7) >> 3 > bytesize)
2890 return false;
2891 uint32* data = (uint32*)bitfield;
2892 uint32* src = (uint32*)data;
2893 uint32 n = loc;
2894 while (n > 31) {
2895 n -= 32;
2896 src++;
2897 }
2898
2899 #if defined WINDOWS
2900 if (value != 0)
2901 _bittestandset((long*)src, n);
2902 else
2903 _bittestandreset((long*)src, n);
2904 #elif defined LINUX
2905 if (value != 0)
2906 *src |= 1<<n;
2907 else
2908 *src &= ~(1<<n);
2909 //*src &= ((1<<n)^0xFFFFFFFF);
2910 #elif defined OSX
2911 if (value != 0)
2912 *src |= 1<<n;
2913 else
2914 *src &= ~(1<<n);
2915 #endif
2916 return true;
2917}
2918
2919// Set the block of num bits starting with nth bit to value in field of size bits, starting from data loc
2920bool SetBitN(uint32 loc, uint32 num, bit value, char* bitfield, uint32 bytesize) {
2921 if ((loc + 7) >> 3 > bytesize)
2922 return false;
2923 uint32* data = (uint32*)bitfield;
2924 uint32* src = (uint32*)data;
2925 uint32 n = loc;
2926 while (n > 31) {
2927 n -= 32;
2928 src++;
2929 }
2930
2931 for (uint32 i = 0; i < num; i++) {
2932 if (!n) {
2933 while (num - i > 31) {
2934 *src = (value ? MAXVALUINT32 : 0);
2935 i += 32;
2936 src++;
2937 }
2938 if (num - i > 15) {
2939 *(uint16*)src = (value ? MAXVALUINT16 : 0);
2940 i += 16;
2941 n += 16;
2942 if (num - i > 7) {
2943 *((unsigned char*)src + 2) = (value ? 255 : 0);
2944 i += 8;
2945 n += 8;
2946 }
2947 }
2948 else if (num - i > 7) {
2949 *(unsigned char*)src = (value ? 255 : 0);
2950 i += 8;
2951 n += 8;
2952 }
2953 if (i == num)
2954 return true;
2955 }
2956 #if defined WINDOWS
2957 if (value != 0)
2958 _bittestandset((long*)src, n);
2959 else
2960 _bittestandreset((long*)src, n);
2961 #elif defined LINUX
2962 if (value != 0)
2963 *src |= 1 << n;
2964 else
2965 *src &= ~(1 << n);
2966 //*src &= ((1<<n)^0xFFFFFFFF);
2967 #elif defined OSX
2968 if (value != 0)
2969 *src |= 1 << n;
2970 else
2971 *src &= ~(1 << n);
2972 #endif
2973 n++;
2974 if (n > 31) {
2975 n = 0;
2976 src++;
2977 }
2978 }
2979 return true;
2980}
2981
2982// Get the nth bit in field of size bits, starting from data loc
2983bool GetBit(uint32 loc, const char* bitfield, uint32 bytesize, bit& val) {
2984 if ((loc + 7) >> 3 > bytesize)
2985 return false;
2986 uint32* data = (uint32*)bitfield;
2987 uint32* src = (uint32*)data;
2988 uint32 n = loc;
2989 while (n > 31) {
2990 n -= 32;
2991 src++;
2992 }
2993
2994 #if defined WINDOWS
2995 val = (_bittest((long*)src, n) != 0);
2996 #elif defined LINUX
2997 uint32 mask = static_cast<uint32>( 1 << n ) ;
2998 val = (bit)(mask & *src);
2999 #elif defined OSX
3000 uint32 mask = static_cast<uint32>(1 << n);
3001 val = (bit)(mask & *src);
3002 #endif
3003// printf("GetBit returned %u...\n", val);
3004 return true;
3005}
3006
3007// Get location of last bit in use, starting from data loc
3008bool GetLastOccupiedBitLoc(const char* bitfield, uint32 bytesize, uint32& loc) {
3009 uint32* data = (uint32*)bitfield;
3010 uint32* src = (uint32*)(bitfield + bytesize - 4);
3011 int32 index;
3012
3013 loc = 0;
3014 while ((*src == 0xFFFFFFFF)) {
3015 if (src <= data)
3016 return false;
3017 src--;
3018 }
3019
3020 #if defined WINDOWS
3021 _BitScanReverse((DWORD*)&index, ~(*src));
3022 #elif defined LINUX
3023 index = 31 - __builtin_clz(~(*src));
3024 #elif defined OSX
3025 index = 31 - __builtin_clz(~(*src));
3026 #endif
3027 loc = ((uint32)(src - data) * 32) + index;
3028 return true;
3029}
3030
3031
3032// Return string representing the bitfield
3033std::string GetBitFieldAsString(const char* bitfield, uint32 bytesize, uint32 size) {
3034 std::string str;
3035 str.reserve(bytesize * 9);
3036
3037 uint32 i,j,n=0;
3038 for(i = 0; i < bytesize; i++) {
3039 for(j = 0; j < 8; j++) {
3040 if (++n <= size)
3041 str.push_back((bitfield[i] & (1 << j)) ? '_' : '0');
3042 else
3043 str.push_back('*');
3044 }
3045 str.push_back(' ');
3046 };
3047 return str;
3048}
3049
3050
3051// Print string representing the bitfield
3052std::string PrintBitFieldString(const char* bitfield, uint32 bytesize, uint32 size, const char* title) {
3053 std::string str = GetBitFieldAsString(bitfield, bytesize, size);
3054 uint32 strSize = (uint32)str.size();
3055 if (!strSize)
3056 return "";
3057 if (strSize < 73) {
3058 if (title != NULL)
3059 return StringFormat("%s [%u]: %s\n", title, size, str.c_str());
3060 else
3061 return StringFormat("%s\n", str.c_str());
3062 }
3063 else {
3064 std::string str2;
3065 if (title != NULL)
3066 str2 = StringFormat("%s [%u]:\n", title, size);
3067 uint32 loc = 0;
3068 while (loc < strSize-1) {
3069 str2 += str.substr(loc, 36) + "\n";
3070 loc += 36;
3071 }
3072 if (loc < strSize)
3073 str2 += str.substr(loc) + "\n";
3074 return str2;
3075 }
3076}
3077
3078
3079
3080
3081int32 AtomicIncrement32(int32 volatile &v) {
3082 #if defined WINDOWS
3083 return InterlockedIncrement((LONG*)&v);
3084 // #elif defined LINUX
3085 #else
3086 __sync_add_and_fetch(&v, 1);
3087 return v;
3088 #endif
3089};
3090
3091int64 AtomicIncrement64(int64 volatile &v) {
3092 #if defined WINDOWS
3093 return InterlockedIncrement64((LONGLONG*)&v);
3094 // #elif defined LINUX
3095 #else
3096 __sync_add_and_fetch(&v, 1);
3097 return v;
3098 #endif
3099};
3100
3101int32 AtomicDecrement32(int32 volatile &v) {
3102 #if defined WINDOWS
3103 return InterlockedDecrement((LONG*)&v);
3104 // #elif defined LINUX
3105 #else
3106 __sync_add_and_fetch(&v, -1);
3107 return v;
3108 #endif
3109};
3110
3111int64 AtomicDecrement64(int64 volatile &v) {
3112 #if defined WINDOWS
3113 return InterlockedDecrement64((LONGLONG*)&v);
3114 // #elif defined LINUX
3115 #else
3116 __sync_add_and_fetch(&v, -1);
3117 return v;
3118 #endif
3119};
3120
3121bool Sleep(uint32 ms) {
3122 #if defined WINDOWS
3123 // we are actually being passed millisecond, so multiply up
3124 ::Sleep((uint32)ms);
3125 #else
3126 usleep(ms*1000);
3127 #endif
3128 return true;
3129}
3130
3131std::string PrintProgramTrace(uint32 startLine, uint32 endLine) {
3132 std::string tracePrint;
3133 std::list<std::string> trace = GetProgramTrace();
3134
3135 std::list<std::string>::iterator i = trace.begin(), e = trace.end();
3136
3137 uint32 line = 0;
3138 while (i != e) {
3139 if ((++line > startLine) && (!endLine || (line <= endLine)))
3140 tracePrint += " -" + (*i) + "\n";
3141 i++;
3142 }
3143 return tracePrint;
3144}
3145
3146std::list<std::string> GetProgramTrace() {
3147 std::list<std::string> trace;
3148 #ifdef WINDOWS
3149 // this needs Dbghelp.h included and link with Dbghelp.lib
3150 //unsigned int i;
3151 //void * stack[ 100 ];
3152 //unsigned short frames;
3153 //SYMBOL_INFO * symbol;
3154 //HANDLE process;
3155
3156 //process = GetCurrentProcess();
3157
3158 //SymInitialize( process, NULL, TRUE );
3159
3160 //frames = CaptureStackBackTrace( 0, 100, stack, NULL );
3161 //symbol = ( SYMBOL_INFO * )calloc( sizeof( SYMBOL_INFO ) + 256 * sizeof( char ), 1 );
3162 //symbol->MaxNameLen = 255;
3163 //symbol->SizeOfStruct = sizeof( SYMBOL_INFO );
3164
3165 //for( i = 0; i < frames; i++ ) {
3166 // SymFromAddr( process, ( DWORD64 )( stack[ i ] ), 0, symbol );
3167 // trace.push_back(StringFormat("%i: %s (0x%0X)", frames - i - 1, symbol->Name, symbol->Address));
3168 //}
3169 //free( symbol );
3170 #else
3171 #ifdef _DEBUG
3172 void *array[200];
3173 size_t size;
3174 char **strings;
3175 size_t i;
3176 size = backtrace(array, 200);
3177 strings = backtrace_symbols(array, size);
3178 for (i = 0; i < size; i++)
3179 trace.push_back(strings[i]);
3180 free (strings);
3181 #endif // _DEBUG
3182 #endif
3183 return trace;
3184}
3185
3186
3187bool CreateThread(THREAD_FUNCTION func, void* args, ThreadHandle& thread, uint32 &osID) {
3188
3189 #if defined WINDOWS
3190 DWORD dwThreadId;
3191 thread = ::CreateThread(
3192 NULL, // no security attributes
3193 0, // use default stack size
3194 (LPTHREAD_START_ROUTINE) func, // thread function
3195 args, // argument to thread function
3196 0, // use default creation flags
3197 &dwThreadId); // returns the thread identifier
3198
3199 if (thread != NULL) {
3200 osID = ::GetThreadId(thread);
3201 Sleep(0);
3202 return true;
3203 }
3204 else
3205 return false;
3206 #else
3207 pthread_attr_t attr;
3208 pthread_attr_init(&attr); /* initialize attr with default attributes */
3209 // pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
3210 pthread_attr_setschedpolicy(&attr, SCHED_RR);
3211 pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
3212 // pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
3213
3214 int res;
3215 if ((res=pthread_create(&thread, &attr, func, args)) != 0)
3216 return false;
3217 int oldstate;
3218 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
3219 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldstate);
3220 #ifdef __APPLE__
3221 uint64 tid;
3222 pthread_threadid_np(thread, &tid);
3223 osID = (uint32)tid;
3224 #else
3225 osID = (uint32) thread;
3226 #endif
3227 // printf("create hThread: %u\n", thread);
3228 Sleep(0);
3229 return true;
3230 #endif
3231}
3232
3234 #if defined WINDOWS
3235 int res = WaitForSingleObject(hThread, 0);
3236 if (res != WAIT_OBJECT_0)
3237 return false;
3238 return true;
3239 //return (CloseHandle(hThread) != 0);
3240 #else
3241 #ifdef __APPLE__
3242 // Non-destructive probe -- see IsThreadRunning. Must not pthread_join here,
3243 // or the later authoritative join double-joins a reaped thread and corrupts
3244 // the heap. pthread_kill(h, 0): ESRCH (non-zero) => thread has finished.
3245 // A null handle (empty slot) is "finished"; guard it since pthread_kill,
3246 // unlike pthread_join, dereferences the handle and would crash on null.
3247 if (!hThread)
3248 return true;
3249 return (pthread_kill(hThread, 0) != 0);
3250 #else
3251 // Non-destructive liveness probe (same rationale as the __APPLE__ branch).
3252 // The previous pthread_tryjoin_np() REAPS the thread on success, so any
3253 // second probe of the same handle (e.g. ThreadManager::shutdown()'s wait
3254 // loop followed by terminateThread()'s grace loop) operated on an
3255 // already-joined pthread_t -- undefined behaviour that segfaulted inside
3256 // pthread_tryjoin_np (SIGSEGV in the CMSDK threadmanager/network tests).
3257 // pthread_kill(h, 0) sends no signal: 0 => alive, ESRCH => finished.
3258 // A null handle (empty/reaped slot) counts as finished. Reaping is done
3259 // exactly once via utils::ReapThread / WaitForThreadToFinish / TerminateThread.
3260 if (!hThread)
3261 return true;
3262 return (pthread_kill(hThread, 0) != 0);
3263 #endif
3264 #endif
3265}
3266
3267bool ReapThread(ThreadHandle& hThread) {
3268 // Release the OS resources of a thread that has ALREADY finished, exactly
3269 // once, and zero the handle so no later probe/join can touch it again.
3270 // POSIX: a joinable thread that exited keeps its stack until pthread_join.
3271 // Windows: the thread object lives until its handle is closed.
3272 if (!hThread)
3273 return true;
3274 #if defined WINDOWS
3275 CloseHandle(hThread);
3276 #else
3277 pthread_join(hThread, NULL);
3278 #endif
3279 hThread = 0;
3280 return true;
3281}
3282
3284 // Atomic probe-and-reap: returns true iff the thread has finished, and in
3285 // that case releases its OS resources exactly once and ZEROES the handle so
3286 // no later probe, join or cancel can ever touch a dead pthread_t again
3287 // (double pthread_tryjoin_np/pthread_join on one handle is undefined
3288 // behaviour and segfaulted the CMSDK threadmanager/network tests on Linux).
3289 // Note: pthread_kill(h, 0) cannot be used as the probe on glibc >= 2.34 --
3290 // it returns 0 ("alive") for a thread that exited but was not yet joined.
3291 if (!hThread)
3292 return true;
3293 #if defined WINDOWS
3294 if (WaitForSingleObject(hThread, 0) != WAIT_OBJECT_0)
3295 return false;
3296 CloseHandle(hThread);
3297 #elif defined __APPLE__
3298 if (pthread_kill(hThread, 0) == 0)
3299 return false; // still alive
3300 pthread_join(hThread, NULL);
3301 #else
3302 if (pthread_tryjoin_np(hThread, NULL) != 0)
3303 return false; // still alive (EBUSY)
3304 #endif
3305 hThread = 0;
3306 return true;
3307}
3308
3309bool WaitForThreadToFinish(ThreadHandle hThread, uint32 timeoutMS) {
3310 // Sadly, the Linux implementation of join doesn't allow a timeout, so to be compatible
3311 // we cannot allow the Windows to have this either, although it could (and should) have
3312 #if defined WINDOWS
3313 int res = WaitForSingleObject(hThread, timeoutMS ? timeoutMS : INFINITE);
3314 if(res != WAIT_OBJECT_0)
3315 return false;
3316 return true;
3317 //return (CloseHandle(hThread) != 0);
3318 #else
3319 #ifdef __APPLE__
3320 if (!hThread)
3321 return true; // nothing to wait for
3322 // macOS has no pthread_timedjoin_np. A timeout==0 means "block until done".
3323 if (timeoutMS == 0)
3324 return (pthread_join(hThread, NULL) == 0);
3325 // For a bounded wait, poll liveness non-destructively (pthread_kill, no
3326 // signal) until the thread exits or the deadline passes, then perform a
3327 // single authoritative join to reap it. This avoids spawning a detached
3328 // joiner thread that would race a later join on the same handle (the
3329 // previous approach could leave two pthread_join calls on one target --
3330 // undefined behaviour that corrupted the heap).
3331 uint32 waited = 0;
3332 while (pthread_kill(hThread, 0) == 0) {
3333 if (waited >= timeoutMS)
3334 return false; // still running at deadline; do NOT reap
3335 Sleep(2);
3336 waited += 2;
3337 }
3338 // Thread has exited but is still joinable; reap it exactly once.
3339 pthread_join(hThread, NULL);
3340 return true;
3341 #else
3342 struct timespec ts;
3343 CalcTimeout(ts, timeoutMS);
3344 if (timeoutMS) {
3345 if (pthread_timedjoin_np(hThread, NULL, &ts) != 0)
3346 return false;
3347 else
3348 return true;
3349 }
3350 else {
3351 if (pthread_join(hThread, NULL) != 0)
3352 return false;
3353 }
3354 return true;
3355 #endif
3356 #endif
3357}
3358
3360 if (!hThread)
3361 return true;
3362 #if defined WINDOWS
3363 // printf("cancel hThread: %u\n", hThread);
3364 bool res = false;
3365 try {
3366 res = (::TerminateThread(hThread, 0) != 0);
3367 if (!res) {
3368 char* msg = new char[2048];
3369 int length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
3370 NULL, GetLastError(), 0, msg, sizeof(msg), NULL);
3371 msg[length] = '\0';
3372 LogPrint(0, LOG_SYSTEM, 0, "Error terminating thread: %s", msg);
3373 delete[] msg;
3374 }
3375 else
3376 CloseHandle(hThread);
3377 }
3378 catch (...) {}
3379 return res;
3380 #else
3381 // printf("cancel hThread: %u\n", hThread);
3382 pthread_cancel(hThread);
3383 if (pthread_join(hThread, NULL) != 0)
3384 return false;
3385 return true;
3386 #endif
3387}
3388
3389// ------------------------------------------------------------------
3390// Graceful-shutdown signal handling: block the shutdown signals on all
3391// threads and dequeue them SYNCHRONOUSLY on one dedicated thread via
3392// sigwait(). This keeps signal handling out of async-signal context and
3393// off arbitrary worker threads, so a signal can never drive exit()/teardown
3394// on a random thread and deadlock against the main thread's own teardown.
3395// ------------------------------------------------------------------
3396#ifndef WINDOWS
3397// The set of signals treated as "please shut down gracefully". SIGUSR2 is
3398// added purely as the wakeup used by StopSignalThread() to unblock sigwait().
3399static void SignalShutdownSigset(sigset_t* set) {
3400 sigemptyset(set);
3401 sigaddset(set, SIGINT);
3402 sigaddset(set, SIGTERM);
3403 sigaddset(set, SIGHUP);
3404 sigaddset(set, SIGUSR2); // internal wakeup only (see StopSignalThread)
3405}
3406
3407static pthread_t g_signalThread;
3408static bool g_signalThreadRunning = false;
3409static volatile sig_atomic_t g_signalThreadStop = 0;
3411static uint32 g_shutdownSignalCount = 0;
3412
3414 sigset_t set;
3416 int sig = 0;
3417 for (;;) {
3418 if (sigwait(&set, &sig) != 0)
3419 continue; // interrupted; wait again
3420 // SIGUSR2 is ONLY ever sent by StopSignalThread() as the stop wakeup, so
3421 // treating it as an unconditional break is race-free (no dependence on the
3422 // visibility ordering of g_signalThreadStop).
3423 if (sig == SIGUSR2 || g_signalThreadStop)
3424 break;
3428 }
3429 thread_ret_val(0);
3430}
3431#endif
3432
3434 #if defined WINDOWS
3435 return true; // no-op: Windows uses SetConsoleCtrlHandler / signal()
3436 #else
3437 sigset_t set;
3439 // Block on the calling (main) thread BEFORE any worker is spawned, so the
3440 // mask is inherited by every thread created afterwards.
3441 return (pthread_sigmask(SIG_BLOCK, &set, NULL) == 0);
3442 #endif
3443}
3444
3446 #if defined WINDOWS
3447 (void)cb;
3448 return true; // no-op on Windows
3449 #else
3451 return true; // idempotent
3452 g_shutdownCallback = cb;
3455 // Ensure the shutdown signals are blocked on THIS thread too, so if the
3456 // creator forgot BlockShutdownSignals() the new thread still inherits a
3457 // blocked mask and sigwait() works deterministically.
3458 sigset_t set;
3460 pthread_sigmask(SIG_BLOCK, &set, NULL);
3461 if (pthread_create(&g_signalThread, NULL, SignalThreadMain, NULL) != 0)
3462 return false;
3463 g_signalThreadRunning = true;
3464 return true;
3465 #endif
3466}
3467
3469 #if defined WINDOWS
3470 return true;
3471 #else
3473 return true;
3475 // Wake the sigwait() with the dedicated internal signal, then join.
3476 pthread_kill(g_signalThread, SIGUSR2);
3477 pthread_join(g_signalThread, NULL);
3478 g_signalThreadRunning = false;
3479 g_shutdownCallback = NULL;
3480 return true;
3481 #endif
3482}
3483
3485 #if defined WINDOWS
3486 return (SuspendThread(hThread) >= 0);
3487 #else
3488 return (pthread_kill(hThread, SIGSTOP) == 0);
3489 #endif
3490 return false;
3491}
3492
3494 #if defined WINDOWS
3495 DWORD count;
3496 while ( (count = ResumeThread(hThread)) > 1);
3497 return (count == 1);
3498 #else
3499 return (pthread_kill(hThread, SIGCONT) == 0);
3500 #endif
3501 return false;
3502}
3503
3504bool GetCurrentThreadUniqueID(uint32 &tid) {
3505 // This function returns an ID unique to the thread on a given OS
3506 // On Windows this is the Thread OS ID
3507 // On Linux we cannot read other thread OS IDs so it has to be the
3508 // thread handle converted to a 32bit uint.
3509 #if defined WINDOWS
3510 tid = ::GetCurrentThreadId();
3511 return true;
3512 #else
3513 #ifdef __APPLE__
3514 uint64 id;
3515 pthread_threadid_np(pthread_self(), &id);
3516 tid = (uint32)id;
3517 #else
3518 tid = (uint32) pthread_self();
3519 #endif
3520 return true;
3521 #endif
3522}
3523
3524bool GetCurrentThreadOSID(uint32 &tid) {
3525 #if defined WINDOWS
3526 tid = ::GetCurrentThreadId();
3527 return true;
3528 #else
3529 #ifdef __APPLE__
3530 uint64 id;
3531 if (pthread_threadid_np(pthread_self(), &id) != 0)
3532 return false;
3533 tid = (uint32)id;
3534 #else
3535 int id = syscall(SYS_gettid);
3536 if (id <= 0)
3537 return false;
3538 tid = (uint32)id;
3539 #endif
3540 return true;
3541 #endif
3542}
3543
3545 #if defined WINDOWS
3546 thread = ::GetCurrentThread();
3547 return true;
3548 #else
3549 thread = pthread_self();
3550 return true;
3551 #endif
3552}
3553
3555 #if defined WINDOWS
3556 DWORD code;
3557 if (!GetExitCodeThread(hThread, &code))
3558 return false;
3559 return (code == STILL_ACTIVE);
3560 #else
3561 #ifdef __APPLE__
3562 // Non-destructive liveness probe. macOS has no pthread_tryjoin_np, and we
3563 // must NOT use pthread_join here: this is polled repeatedly (e.g. by the
3564 // ThreadManager monitoring loop) and a join reaps the thread, after which
3565 // the authoritative TerminateThread/WaitForThreadToFinish join would operate
3566 // on an already-joined pthread_t -- undefined behaviour that corrupts the
3567 // heap. pthread_kill(h, 0) sends no signal and just reports existence:
3568 // 0 => still alive, ESRCH => already terminated. Unlike pthread_join, it
3569 // dereferences the handle, so a null handle (empty slot) must be guarded.
3570 if (!hThread)
3571 return false;
3572 return (pthread_kill(hThread, 0) == 0);
3573 #else
3574 // Non-destructive probe -- see CheckForThreadFinished/__APPLE__ comments.
3575 // pthread_tryjoin_np reaped the thread on success, making any later join
3576 // or probe of the same handle undefined behaviour (heap corruption/SIGSEGV).
3577 if (!hThread)
3578 return false;
3579 return (pthread_kill(hThread, 0) == 0);
3580 #endif
3581 #endif
3582}
3583
3585 #if defined WINDOWS
3586 return THREAD_STATS_AUTO;
3587 #else
3588 #if defined RUSAGE_THREAD
3589 return THREAD_STATS_ADHOC;
3590 #else
3591 return THREAD_STATS_OFF;
3592 #endif
3593 #endif
3594}
3595
3596bool GetCPUTicks(ThreadHandle hThread, uint64& ticks) {
3597 ticks = 0;
3598
3599 #if defined WINDOWS
3600 // if (!QueryThreadCycleTime(hThread, &ticks))
3601 // return false;
3602 uint64 cpuTicks = 0;
3603 LARGE_INTEGER perfFreq;
3604 if (!QueryPerformanceFrequency(&perfFreq) || !perfFreq.QuadPart)
3605 return false;
3606 if (!QueryThreadCycleTime(hThread, &cpuTicks))
3607 return false;
3608 ticks = cpuTicks / (perfFreq.QuadPart / 1000000); // cpu ticks / (number of ticks per us)
3609 //FILETIME creationTime;
3610 //FILETIME exitTime;
3611 //FILETIME kernelTime;
3612 //FILETIME userTime;
3613 //if (!GetThreadTimes(hThread, &creationTime, &exitTime, &kernelTime, &userTime))
3614 // return false;
3615 //userCPU = ((uint64)userTime.dwLowDateTime | (((uint64)userTime.dwHighDateTime) << 32)) / 10;
3616 //kernelCPU = ((uint64)kernelTime.dwLowDateTime | (((uint64)kernelTime.dwHighDateTime) << 32)) / 10;
3617 return true;
3618 #else
3619 #ifdef __APPLE__
3620 // macOS: use Mach thread_info to get per-thread CPU time
3621 mach_port_t machThread = pthread_mach_thread_np(hThread);
3622 if (machThread == MACH_PORT_NULL)
3623 return false;
3624 thread_basic_info_data_t info;
3625 mach_msg_type_number_t infoCount = THREAD_BASIC_INFO_COUNT;
3626 if (thread_info(machThread, THREAD_BASIC_INFO, (thread_info_t)&info, &infoCount) != KERN_SUCCESS)
3627 return false;
3628 ticks = (uint64)info.user_time.seconds * 1000000 + info.user_time.microseconds
3629 + (uint64)info.system_time.seconds * 1000000 + info.system_time.microseconds;
3630 return true;
3631 #else
3632 clockid_t cid;
3633 if (pthread_getcpuclockid(hThread, &cid) != 0)
3634 return false;
3635 timespec ts;
3636 if (clock_gettime(cid, &ts) != -1 ) {
3637 ticks = ((uint64)ts.tv_sec * 1000000) + (ts.tv_nsec/1000);
3638 return true;
3639 }
3640 else
3641 return false;
3642 #endif
3643 #endif
3644 return true;
3645}
3646
3647bool GetCPUTicks(uint64& ticks) {
3648 ticks = 0;
3649
3650 #if defined WINDOWS
3651 // #### QueryPerformanceFrequency
3652 uint64 cpuTicks = 0;
3653 LARGE_INTEGER perfFreq;
3654 if (!QueryPerformanceFrequency(&perfFreq) || !perfFreq.QuadPart)
3655 return false;
3656 if (!QueryThreadCycleTime(::GetCurrentThread(), &cpuTicks))
3657 return false;
3658 ticks = cpuTicks / (perfFreq.QuadPart / 1000000); // cpu ticks / (number of ticks per us)
3659 //FILETIME creationTime;
3660 //FILETIME exitTime;
3661 //FILETIME kernelTime;
3662 //FILETIME userTime;
3663 //if (!GetThreadTimes(::GetCurrentThread(), &creationTime, &exitTime, &kernelTime, &userTime))
3664 // return false;
3665 //userCPU = ((uint64)userTime.dwLowDateTime | (((uint64)userTime.dwHighDateTime) << 32)) / 10;
3666 //kernelCPU = ((uint64)kernelTime.dwLowDateTime | (((uint64)kernelTime.dwHighDateTime) << 32)) / 10;
3667 return true;
3668 #else
3669 timespec ts;
3670 if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) != -1 ) {
3671 ticks = ((uint64)ts.tv_sec * 1000000) + (ts.tv_nsec/1000);
3672 return true;
3673 }
3674 else
3675 return false;
3676 //#if defined RUSAGE_THREAD
3677 // struct rusage us;
3678 // if (getrusage(RUSAGE_THREAD, &us) == 0) { // RUSAGE_THREAD // accumulate_thread_rusage
3679 // userCPU = (us.ru_utime.tv_sec * 1000000) + us.ru_utime.tv_usec;
3680 // kernelCPU = (us.ru_stime.tv_sec * 1000000) + us.ru_stime.tv_usec;
3681 // }
3682 // else
3683 // return false;
3684 //#else
3685 // return false;
3686 //#endif
3687 #endif
3688}
3689
3690bool GetProcessCPUTicks(uint32 osProcID, uint64& ticks) {
3691 ticks = 0;
3692
3693 #if defined WINDOWS
3694 HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, osProcID);
3695 if (!hProcess)
3696 return false;
3697 // if (!QueryProcessCycleTime(hProcess, &ticks))
3698 // return false;
3699
3700 uint64 cpuTicks = 0;
3701 LARGE_INTEGER perfFreq;
3702 if (!QueryPerformanceFrequency(&perfFreq) || !perfFreq.QuadPart)
3703 return false;
3704 if (!QueryProcessCycleTime(hProcess, &cpuTicks))
3705 return false;
3706 ticks = cpuTicks / (perfFreq.QuadPart / 1000000); // cpu ticks / (number of ticks per us)
3707
3708 //FILETIME creationTime;
3709 //FILETIME exitTime;
3710 //FILETIME kernelTime;
3711 //FILETIME userTime;
3712 //if (!GetProcessTimes(::GetCurrentProcess(), &creationTime, &exitTime, &kernelTime, &userTime))
3713 // return false;
3714 //userCPU = ((uint64)userTime.dwLowDateTime | (((uint64)userTime.dwHighDateTime) << 32)) / 10;
3715 //kernelCPU = ((uint64)kernelTime.dwLowDateTime | (((uint64)kernelTime.dwHighDateTime) << 32)) / 10;
3716 return true;
3717 #else
3718 #ifdef __APPLE__
3719 if ((pid_t)osProcID != getpid())
3720 return false;
3721 struct rusage ru;
3722 if (getrusage(RUSAGE_SELF, &ru) != 0)
3723 return false;
3724 ticks = ((uint64)ru.ru_utime.tv_sec * 1000000) + (uint64)ru.ru_utime.tv_usec
3725 + ((uint64)ru.ru_stime.tv_sec * 1000000) + (uint64)ru.ru_stime.tv_usec;
3726 return true;
3727 #else
3728 clockid_t clockid;
3729 if (clock_getcpuclockid(osProcID, &clockid) != 0)
3730 return false;
3731 timespec ts;
3732 if (clock_gettime(clockid, &ts) != -1 ) {
3733 ticks = ((uint64)ts.tv_sec * 1000000) + (ts.tv_nsec/1000);
3734 return true;
3735 }
3736 else
3737 return false;
3738 //#if defined RUSAGE_THREAD
3739 // struct rusage us;
3740 // if (getrusage(RUSAGE_SELF, &us) == 0) { // RUSAGE_THREAD // accumulate_thread_rusage
3741 // userCPU = (us.ru_utime.tv_sec * 1000000) + us.ru_utime.tv_usec;
3742 // kernelCPU = (us.ru_stime.tv_sec * 1000000) + us.ru_stime.tv_usec;
3743 // }
3744 // else
3745 // return false;
3746 //#else
3747 // return false;
3748 //#endif
3749 #endif
3750 #endif
3751 return true;
3752}
3753
3754bool GetProcessCPUTicks(uint64& ticks) {
3755 ticks = 0;
3756
3757 #if defined WINDOWS
3758 // if (!QueryProcessCycleTime(::GetCurrentProcess(), &ticks))
3759 // return false;
3760
3761 uint64 cpuTicks = 0;
3762 LARGE_INTEGER perfFreq;
3763 if (!QueryPerformanceFrequency(&perfFreq) || !perfFreq.QuadPart)
3764 return false;
3765 if (!QueryProcessCycleTime(::GetCurrentProcess(), &cpuTicks))
3766 return false;
3767 ticks = cpuTicks / (perfFreq.QuadPart / 1000000); // cpu ticks / (number of ticks per us)
3768
3769 //FILETIME creationTime;
3770 //FILETIME exitTime;
3771 //FILETIME kernelTime;
3772 //FILETIME userTime;
3773 //if (!GetProcessTimes(::GetCurrentProcess(), &creationTime, &exitTime, &kernelTime, &userTime))
3774 // return false;
3775 //userCPU = ((uint64)userTime.dwLowDateTime | (((uint64)userTime.dwHighDateTime) << 32)) / 10;
3776 //kernelCPU = ((uint64)kernelTime.dwLowDateTime | (((uint64)kernelTime.dwHighDateTime) << 32)) / 10;
3777 return true;
3778 #else
3779 timespec ts;
3780 if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) != -1 ) {
3781 ticks = ((uint64)ts.tv_sec * 1000000) + (ts.tv_nsec/1000);
3782 return true;
3783 }
3784 else
3785 return false;
3786 //#if defined RUSAGE_THREAD
3787 // struct rusage us;
3788 // if (getrusage(RUSAGE_SELF, &us) == 0) { // RUSAGE_THREAD // accumulate_thread_rusage
3789 // userCPU = (us.ru_utime.tv_sec * 1000000) + us.ru_utime.tv_usec;
3790 // kernelCPU = (us.ru_stime.tv_sec * 1000000) + us.ru_stime.tv_usec;
3791 // }
3792 // else
3793 // return false;
3794 //#else
3795 // return false;
3796 //#endif
3797 #endif
3798 return true;
3799}
3800
3801bool GetProcAndOSCPUUsage(double& procPercentUse, double& osPercentUse, uint64 &prevOSTicks, uint64 &prevOSIdleTicks, uint64 &prevProcTicks) {
3802 #if defined WINDOWS
3803 return GetProcAndOSCPUUsage(0, procPercentUse, osPercentUse, prevOSTicks, prevOSIdleTicks, prevProcTicks);
3804 #else
3805 return GetProcAndOSCPUUsage(getpid(), procPercentUse, osPercentUse, prevOSTicks, prevOSIdleTicks, prevProcTicks);
3806 #endif
3807 return true;
3808}
3809
3810bool GetProcAndOSCPUUsage(uint32 osProcID, double& procPercentUse, double& osPercentUse, uint64 &prevOSTicks, uint64 &prevOSIdleTicks, uint64 &prevProcTicks) {
3811 procPercentUse = osPercentUse = 0;
3812
3813 uint64 totalTicks, kernelTicks, userTicks, idleTicks, procTotalTicks, procUserTicks, procKernelTicks;
3814
3815 #if defined WINDOWS
3816 FILETIME idleTime, kernelTime, userTime;
3817 if (!GetSystemTimes(&idleTime, &kernelTime, &userTime))
3818 return false;
3819
3820 idleTicks = FileTimeToUint64(idleTime);
3821 kernelTicks = FileTimeToUint64(kernelTime);
3822 userTicks = FileTimeToUint64(userTime);
3823 totalTicks = kernelTicks + userTicks;
3824
3825 FILETIME procCreationTime, procExitTime, procKernelTime, procUserTime;
3826
3827 if (!osProcID) {
3828 if (!GetProcessTimes(GetCurrentProcess(), &procCreationTime, &procExitTime, &procKernelTime, &procUserTime))
3829 return false;
3830 }
3831 else {
3832 HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, osProcID);
3833 if (!hProcess)
3834 return false;
3835
3836 if (!GetProcessTimes(hProcess, &procCreationTime, &procExitTime, &procKernelTime, &procUserTime))
3837 return false;
3838 }
3839
3840 procKernelTicks = FileTimeToUint64(procKernelTime);
3841 procUserTicks = FileTimeToUint64(procUserTime);
3842 procTotalTicks = procKernelTicks + procUserTicks;
3843
3844 // printf("Proc User: %llu Kernel: %llu\n", procUserTicks, procKernelTicks);
3845
3846 #elif defined OSX
3847 host_cpu_load_info_data_t cpuInfo;
3848 mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
3849 if (host_statistics64(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info64_t)&cpuInfo, &count) != KERN_SUCCESS)
3850 return false;
3851 // cpu_ticks are in Mach clock ticks (typically 100/s via CLK_TCK)
3852 // Convert to microseconds to match procTotalTicks units
3853 long ticksPerSec = sysconf(_SC_CLK_TCK);
3854 uint64 usPerTick = (ticksPerSec > 0) ? (1000000 / ticksPerSec) : 10000;
3855 totalTicks = ((uint64)cpuInfo.cpu_ticks[CPU_STATE_USER] + cpuInfo.cpu_ticks[CPU_STATE_SYSTEM] + cpuInfo.cpu_ticks[CPU_STATE_IDLE] + cpuInfo.cpu_ticks[CPU_STATE_NICE]) * usPerTick;
3856 idleTicks = (uint64)cpuInfo.cpu_ticks[CPU_STATE_IDLE] * usPerTick;
3857 if (osProcID == 0 || osProcID == (uint32)getpid()) {
3858 task_thread_times_info_data_t threadTimes;
3859 count = TASK_THREAD_TIMES_INFO_COUNT;
3860 if (task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t)&threadTimes, &count) == KERN_SUCCESS)
3861 procTotalTicks = (uint64)threadTimes.user_time.seconds * 1000000 + threadTimes.user_time.microseconds + (uint64)threadTimes.system_time.seconds * 1000000 + threadTimes.system_time.microseconds;
3862 else
3863 procTotalTicks = 0;
3864 } else {
3865 return false;
3866 }
3867
3868 #else
3869 std::string statLine, procStatLine;
3870 std::ifstream statFile ("/proc/stat");
3871 std::ifstream procStatFile (StringFormat("/proc/%d/stat", osProcID).c_str());
3872 if (statFile.is_open())
3873 getline (statFile, statLine);
3874 else
3875 return false;
3876
3877 if (procStatFile.is_open())
3878 getline (procStatFile, procStatLine);
3879 else
3880 return false;
3881
3882 if (!statLine.length() || !procStatLine.length())
3883 return false;
3884
3885 //cpu 56344 20038 65629 5095979 55549 9083 10717 0 0 0
3886 uint64 fields[10];
3887 int retval = sscanf(statLine.c_str(), "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
3888 &fields[0], &fields[1], &fields[2], &fields[3], &fields[4],
3889 &fields[5], &fields[6], &fields[7], &fields[8], &fields[9]);
3890 totalTicks = 0;
3891 for (int i=0; i<10; i++)
3892 totalTicks += fields[i];
3893 idleTicks = fields[3];
3894
3895 uint64 utime_ticks, stime_ticks, cutime_ticks, cstime_ticks;
3896 retval = sscanf(procStatLine.c_str(), "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %llu %llu %llu %llu",
3897 &utime_ticks, &stime_ticks, &cutime_ticks, &cstime_ticks);
3898
3899 procTotalTicks = utime_ticks + stime_ticks + cutime_ticks + cstime_ticks;
3900 #endif
3901
3902 uint64 totalTicksSinceLastTime = totalTicks - prevOSTicks;
3903 uint64 idleTicksSinceLastTime = idleTicks - prevOSIdleTicks;
3904 uint64 procTicksSinceLastTime = procTotalTicks - prevProcTicks;
3905
3906 osPercentUse = 1.0f-((totalTicksSinceLastTime > 0) ? ((double)idleTicksSinceLastTime)/totalTicksSinceLastTime : 0);
3907 procPercentUse = ((totalTicksSinceLastTime > 0) ? ((double)procTicksSinceLastTime)/totalTicksSinceLastTime : 0);
3908
3909// printf("Idle: %llu Total: %llu %.2f%%\n",
3910// idleTicksSinceLastTime, totalTicksSinceLastTime, percentUse*100);
3911
3912 prevOSTicks = totalTicks;
3913 prevOSIdleTicks = idleTicks;
3914 prevProcTicks = procTotalTicks;
3915
3916 return true;
3917}
3918
3919bool GetOSCPUUsage(double& percentUse) {
3920 percentUse = 0;
3921
3922 static uint64 PreviousTotalTicks = 0;
3923 static uint64 PreviousIdleTicks = 0;
3924
3925 uint64 totalTicks, kernelTicks, userTicks, idleTicks;
3926
3927 #if defined WINDOWS
3928
3929 FILETIME idleTime, kernelTime, userTime;
3930 if (!GetSystemTimes(&idleTime, &kernelTime, &userTime))
3931 return false;
3932
3933 idleTicks = FileTimeToUint64(idleTime);
3934 kernelTicks = FileTimeToUint64(kernelTime);
3935 userTicks = FileTimeToUint64(userTime);
3936 totalTicks = kernelTicks + userTicks;
3937
3938 #elif defined OSX
3939 host_cpu_load_info_data_t cpuInfo;
3940 mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
3941 if (host_statistics64(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info64_t)&cpuInfo, &count) != KERN_SUCCESS)
3942 return false;
3943 totalTicks = (uint64)cpuInfo.cpu_ticks[CPU_STATE_USER] + cpuInfo.cpu_ticks[CPU_STATE_SYSTEM] + cpuInfo.cpu_ticks[CPU_STATE_IDLE] + cpuInfo.cpu_ticks[CPU_STATE_NICE];
3944 idleTicks = (uint64)cpuInfo.cpu_ticks[CPU_STATE_IDLE];
3945 #else
3946 std::string statLine;
3947 std::ifstream statFile("/proc/stat");
3948 if (!statFile.is_open() || !getline(statFile, statLine) || !statLine.length())
3949 return false;
3950
3951 uint64 fields[10];
3952 if (sscanf(statLine.c_str(), "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
3953 &fields[0], &fields[1], &fields[2], &fields[3], &fields[4],
3954 &fields[5], &fields[6], &fields[7], &fields[8], &fields[9]) < 10)
3955 return false;
3956 totalTicks = 0;
3957 for (int i = 0; i < 10; i++)
3958 totalTicks += fields[i];
3959 idleTicks = fields[3];
3960 #endif
3961
3962 uint64 totalTicksSinceLastTime = totalTicks - PreviousTotalTicks;
3963 uint64 idleTicksSinceLastTime = idleTicks - PreviousIdleTicks;
3964
3965 percentUse = 1.0f-((totalTicksSinceLastTime > 0) ? ((double)idleTicksSinceLastTime)/totalTicksSinceLastTime : 0);
3966
3967// printf("Idle: %llu Total: %llu %.2f%%\n",
3968// idleTicksSinceLastTime, totalTicksSinceLastTime, percentUse*100);
3969
3970 PreviousTotalTicks = totalTicks;
3971 PreviousIdleTicks = idleTicks;
3972
3973 return true;
3974}
3975
3976bool GetThreadPriority(ThreadHandle hThread, uint16 priority) {
3977
3978 #if defined WINDOWS
3979 uint16 prio = ::GetThreadPriority(hThread);
3980 priority = FromOSPriority(prio);
3981 #else
3982 struct sched_param sched;
3983 int policy;
3984 #ifdef __APPLE__
3985 policy = SCHED_OTHER;
3986 #else
3987 policy = sched_getscheduler(0);
3988 if(policy < 0)
3989 return false;
3990 #endif
3991 if (pthread_getschedparam(hThread, &policy, &sched) != 0)
3992 return false;
3993
3994 priority = FromOSPriority(sched.sched_priority);
3995 #endif
3996 return true;
3997}
3998
3999bool SetThreadPriority(ThreadHandle hThread, uint16 priority) {
4000 #if defined WINDOWS
4001 return (::SetThreadPriority(hThread, ToOSPriority(priority)) != 0);
4002 #else
4003 int policy;
4004 struct sched_param sched;
4005 memset(&sched, 0, sizeof(struct sched_param));
4006
4007 policy = SCHED_FIFO;
4008 // policy = SCHED_RR;
4009 sched.sched_priority = ToOSPriority(priority);
4010
4011 int res = pthread_setschedparam(hThread, policy, &sched);
4012 if (res != 0)
4013 return false;
4014 #endif
4015 return true;
4016}
4017
4018int ToOSPriority(int pri) {
4019 int val;
4020
4021 #if defined WINDOWS
4022 if (pri == 0)
4023 val = THREAD_PRIORITY_NORMAL;
4024 else if (pri >= 90)
4025 val = THREAD_PRIORITY_TIME_CRITICAL;
4026 else if (pri >= 80)
4027 val = THREAD_PRIORITY_HIGHEST;
4028 else if (pri >= 70)
4029 val = THREAD_PRIORITY_ABOVE_NORMAL;
4030 else if (pri >= 60)
4031 val = THREAD_PRIORITY_NORMAL;
4032 else if (pri >= 50)
4033 val = THREAD_PRIORITY_BELOW_NORMAL;
4034 else if (pri >= 40)
4035 val = THREAD_PRIORITY_LOWEST;
4036 else
4037 val = THREAD_PRIORITY_IDLE;
4038 #else
4039 if (pri == 0)
4040 val = 40;
4041 else if (pri >= 90)
4042 val = 60;
4043 else if (pri >= 80)
4044 val = 55;
4045 else if (pri >= 70)
4046 val = 40;
4047 else if (pri >= 60)
4048 val = 35;
4049 else if (pri >= 50)
4050 val = 30;
4051 else if (pri >= 40)
4052 val = 25;
4053 else if (pri >= 30)
4054 val = 20;
4055 else if (pri >= 20)
4056 val = 15;
4057 else if (pri >= 10)
4058 val = 10;
4059 else
4060 val = 5;
4061 #endif
4062
4063 return val;
4064}
4065
4066int FromOSPriority(int pri) {
4067
4068 #if defined WINDOWS
4069 switch (pri) {
4070 case THREAD_PRIORITY_TIME_CRITICAL:
4071 return 80;
4072 case THREAD_PRIORITY_HIGHEST:
4073 return 60;
4074 case THREAD_PRIORITY_ABOVE_NORMAL:
4075 return 55;
4076 case THREAD_PRIORITY_NORMAL:
4077 return 50;
4078 case THREAD_PRIORITY_BELOW_NORMAL:
4079 return 40;
4080 case THREAD_PRIORITY_LOWEST:
4081 return 30;
4082 default:
4083 return 20;
4084 }
4085 #else
4086 return pri;
4087 #endif
4088}
4089
4090bool SignalThread(ThreadHandle hThread, int32 signal) {
4091 #if defined WINDOWS
4092 // ######################################
4093 #else
4094 // ######################################
4095 #endif
4096 return false;
4097}
4098
4099
4100
4101
4103// Desktop //
4105
4106bool GetDesktopSize(uint32& width, uint32& height) {
4107 width = height = 0;
4108 #if defined WINDOWS
4109 RECT desktop;
4110 // Get a handle to the desktop window
4111 const HWND hDesktop = GetDesktopWindow();
4112 // Get the size of screen to the variable desktop
4113 if (!GetWindowRect(hDesktop, &desktop))
4114 return false;
4115 // The top left corner will have coordinates (0,0)
4116 // and the bottom right corner will have coordinates
4117 // (horizontal, vertical)
4118 width = (uint32)desktop.right;
4119 height = (uint32)desktop.bottom;
4120 return true;
4121 #else
4122 return false;
4123 #endif
4124}
4125
4126bool RenameConsoleWindow(const char* name, bool prepend) {
4127 #if defined WINDOWS
4128 if (prepend) {
4129 uint32 size;
4130 char* oldName = new char[1024];
4131 if (!GetConsoleTitle(oldName, 1024)) {
4132 delete [] oldName;
4133 return false;
4134 }
4135 char* newName = StringFormat(size, "%s - %s", name, oldName);
4136 bool res = (SetConsoleTitle(name) != 0);
4137 delete [] newName;
4138 delete [] oldName;
4139 return res;
4140 }
4141 else
4142 return (SetConsoleTitle(name) != 0);
4143 #else
4144 return false;
4145 #endif
4146}
4147
4148bool MoveConsoleWindow(int32 x, int32 y, int32 w, int32 h) {
4149 #if defined WINDOWS
4150 char* consoleName = new char[1024];
4151 if (!GetConsoleTitle(consoleName, 1024)) {
4152 delete [] consoleName;
4153 return false;
4154 }
4155 HWND handle = FindWindow(NULL, consoleName);
4156 if (!handle) {
4157 delete [] consoleName;
4158 return false;
4159 }
4160 RECT wRect;
4161 if (!GetWindowRect(handle, &wRect)) {
4162 delete [] consoleName;
4163 return false;
4164 }
4165
4166 int conX = (x<0) ? wRect.left : x;
4167 int conY = (y<0) ? wRect.top : y;
4168 int conW = (w<0) ? wRect.right - wRect.left : w;
4169 int conH = (h<0) ? wRect.bottom - wRect.top : h;
4170
4171 bool res = (MoveWindow(handle, conX, conY, conW, conH, true) != 0);
4172 delete [] consoleName;
4173 return res;
4174
4175 #else
4176 return false;
4177 #endif
4178}
4179
4180
4182// Processes //
4184
4186 #if defined WINDOWS
4187 return (uint32)GetCurrentProcessId();
4188 #else
4189 return (uint32)getpid();
4190 #endif
4191}
4192
4193
4194int RunOSCommand(const char* cmdline, const char* initdir, uint32 timeout, std::string& stdoutString, std::string& stderrString) {
4195 if (!cmdline || !strlen(cmdline))
4196 return -1;
4197
4198 #if defined WINDOWS
4199 HANDLE g_hChildStd_OUT_Rd = NULL;
4200 HANDLE g_hChildStd_OUT_Wr = NULL;
4201 HANDLE g_hChildStd_ERR_Rd = NULL;
4202 HANDLE g_hChildStd_ERR_Wr = NULL;
4203
4204 SECURITY_ATTRIBUTES sa;
4205 // Set the bInheritHandle flag so pipe handles are inherited.
4206 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
4207 sa.bInheritHandle = TRUE;
4208 sa.lpSecurityDescriptor = NULL;
4209 // Create a pipe for the child process's STDERR.
4210 if ( ! CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &sa, 0) ) {
4211 return -1;
4212 }
4213 // Ensure the read handle to the pipe for STDERR is not inherited.
4214 if ( ! SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0) ){
4215 return -1;
4216 }
4217 // Create a pipe for the child process's STDOUT.
4218 if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0) ) {
4219 return -1;
4220 }
4221 // Ensure the read handle to the pipe for STDOUT is not inherited
4222 if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) ){
4223 return -1;
4224 }
4225 PROCESS_INFORMATION piProcInfo;
4226 STARTUPINFO siStartInfo;
4227 bool bSuccess = FALSE;
4228
4229 // Set up members of the PROCESS_INFORMATION structure.
4230 ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
4231
4232 // Set up members of the STARTUPINFO structure.
4233 // This structure specifies the STDERR and STDOUT handles for redirection.
4234 ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
4235 siStartInfo.cb = sizeof(STARTUPINFO);
4236 siStartInfo.hStdError = g_hChildStd_ERR_Wr;
4237 siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
4238 siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
4239
4240 uint32 size = 0;
4241 char* cmd = utils::StringFormat(size, cmdline);
4242
4243 // Create the child process.
4244 bSuccess = (CreateProcess(NULL,
4245 cmd, // command line
4246 NULL, // process security attributes
4247 NULL, // primary thread security attributes
4248 TRUE, // handles are inherited
4249 0, // creation flags
4250 NULL, // use parent's environment
4251 initdir, // use parent's current directory
4252 &siStartInfo, // STARTUPINFO pointer
4253 &piProcInfo) != 0); // receives PROCESS_INFORMATION
4254 CloseHandle(g_hChildStd_ERR_Wr);
4255 CloseHandle(g_hChildStd_OUT_Wr);
4256 // If an error occurs, exit the application.
4257 if ( ! bSuccess ) {
4258 return -1;
4259 }
4260
4261 uint64 startTime = GetTimeNow();
4262
4263 DWORD dwRead;
4264 char* chBuf = new char[PROCBUFSIZE];
4265 bSuccess = FALSE;
4266 for (;;) {
4267 bSuccess=(ReadFile( g_hChildStd_OUT_Rd, chBuf, PROCBUFSIZE, &dwRead, NULL) != 0);
4268 if( ! bSuccess || dwRead == 0 ) break;
4269 std::string s(chBuf, dwRead);
4270 stdoutString += s;
4271 if(GetTimeAgeMS(startTime) > (int32)timeout) break;
4272 }
4273 dwRead = 0;
4274 for (;;) {
4275 bSuccess=(ReadFile( g_hChildStd_ERR_Rd, chBuf, PROCBUFSIZE, &dwRead, NULL) != 0);
4276 if( ! bSuccess || dwRead == 0 ) break;
4277 std::string s(chBuf, dwRead);
4278 stderrString += s;
4279 if(GetTimeAgeMS(startTime) > (int32)timeout) break;
4280 }
4281 delete [] chBuf;
4282
4283 DWORD exitcode;
4284 if (GetExitCodeProcess(piProcInfo.hProcess, &exitcode) && (exitcode == STILL_ACTIVE)) {
4285 TerminateProcess(piProcInfo.hProcess, 1);
4286 }
4287 CloseHandle(g_hChildStd_ERR_Rd);
4288 CloseHandle(g_hChildStd_OUT_Rd);
4289 CloseHandle(piProcInfo.hProcess);
4290 CloseHandle(piProcInfo.hThread);
4291 return exitcode;
4292 #else
4293 fflush(stdin);
4294 fflush(stdout);
4295 FILE* runfile = popen(cmdline, "r");
4296 if (runfile == NULL)
4297 return -1;
4298
4299 int exitcode = -1;
4300 int status;
4301 int res;
4302 int size = 4096;
4303 char* buffer = new char[size+1];
4304
4305 res = fread(buffer, 1, size, runfile);
4306 if (res <= 0) {
4307 status = pclose(runfile);
4308 exitcode = WEXITSTATUS(status);
4309 delete [] buffer;
4310 return exitcode;
4311 }
4312 do {
4313 buffer[res] = 0;
4314 stdoutString += buffer;
4315 } while (res = fread(buffer, 1, size, runfile));
4316
4317 status = pclose(runfile);
4318 exitcode = WEXITSTATUS(status);
4319 delete [] buffer;
4320 return exitcode;
4321
4322 //
4323 //int size = 1024;
4324 // char name[1024];
4325 // if (!RunOSTextCommand("uname -n", name, size))
4326 // return "";
4327 //
4328 //
4329 //int argc = 0;
4330 // char **argv=SplitCommandline(cmdline, argc);
4331 // if (!argc || !argv)
4332 // return -1;
4333
4334 // /* since pipes are unidirectional, we need two pipes.
4335 // one for data to flow from parent's stdout to child's
4336 // stdin and the other for child's stdout to flow to
4337 // parent's stdin */
4338
4339 // int* pipes = new int[NUM_PIPES][2];
4340 // int outfd[2];
4341 // int infd[2];
4342
4343 // // pipes for parent to write and read
4344 // pipe((*pipes)[PARENT_READ_PIPE]);
4345 // pipe((*pipes)[PARENT_WRITE_PIPE]);
4346
4347 // if(!fork()) {
4348
4349 // dup2(CHILD_READ_FD, STDIN_FILENO);
4350 // dup2(CHILD_WRITE_FD, STDOUT_FILENO);
4351
4352 // /* Close fds not required by child. Also, we don't
4353 // want the exec'ed program to know these existed */
4354 // close(CHILD_READ_FD);
4355 // close(CHILD_WRITE_FD);
4356 // close(PARENT_READ_FD);
4357 // close(PARENT_WRITE_FD);
4358
4359 // execv(argv[0], argv);
4360 // } else {
4361 // char buffer[100];
4362 // int count;
4363
4364 // /* close fds not required by parent */
4365 // close(CHILD_READ_FD);
4366 // close(CHILD_WRITE_FD);
4367
4368 // // Write to child�s stdin
4369 // //write(PARENT_WRITE_FD, "2^32\n", 5);
4370
4371 // // Read from child�s stdout
4372 // count = read(PARENT_READ_FD, buffer, sizeof(buffer)-1);
4373 // if (count >= 0) {
4374 // buffer[count] = 0;
4375 // printf("%s", buffer);
4376 // } else {
4377 // printf("IO Error\n");
4378 // }
4379 // // ################
4380 // // Not yet complete!!!
4381 // }
4382 #endif
4383
4384}
4385
4386
4387uint32 NewProcess(const char* cmdline, const char* initdir, const char* title, int16 x, int16 y, int16 w, int16 h) {
4388
4389 if (!cmdline || !strlen(cmdline))
4390 return 0;
4391
4392 #if defined WINDOWS
4393
4394 if (!ProcessInformationMap) {
4395 ProcessInformationMap = new std::map<uint32, ProcessData*>;
4396 ghJob = CreateJobObject(NULL, NULL); // GLOBAL
4397 if (ghJob) {
4398 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };
4399 // Configure all child processes associated with the job to terminate when the
4400 jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
4401 SetInformationJobObject(ghJob, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli));
4402 }
4403 }
4404
4405 ProcessData* pData = new ProcessData;
4406
4407 ZeroMemory(&(pData->procInfo), sizeof(pData->procInfo));
4408 ZeroMemory(&(pData->si), sizeof(pData->si));
4409 pData->si.cb = sizeof(pData->si);
4410 pData->si.wShowWindow = SW_SHOWNOACTIVATE; //SW_SHOW; // SW_HIDE;
4411
4412 if ( (x>=0) && (y>=0) && (w>=0) && (h>=0) ) {
4413 pData->si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USEPOSITION | STARTF_USESIZE;
4414 pData->si.dwX = x;
4415 pData->si.dwY = y;
4416 pData->si.dwXSize = w;
4417 pData->si.dwYSize = h;
4418 }
4419 else {
4420 pData->si.dwFlags = STARTF_USESHOWWINDOW;
4421 }
4422
4423 // cmdline/title are plain strings, not printf formats: copy verbatim into
4424 // mutable new[] buffers (see NewProcessEx note re: '%' fast-fail).
4425 char* titleCopy = NULL;
4426 if (title) {
4427 size_t tlen = strlen(title);
4428 titleCopy = new char[tlen + 1];
4429 memcpy(titleCopy, title, tlen + 1);
4430 pData->si.lpTitle = titleCopy;
4431 }
4432
4433 size_t clen = strlen(cmdline);
4434 char* cmd = new char[clen + 1];
4435 memcpy(cmd, cmdline, clen + 1);
4436 //utils::strcpyavail(cmd, cmdline, (uint32)strlen(cmdline)+1, true);
4437
4438 // DETACHED_PROCESS
4439 int res = CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, initdir, &pData->si, &pData->procInfo);
4440 delete [] titleCopy;
4441 delete [] cmd;
4442 if (res == 0) {
4443 char* msg = new char[2048];
4444 int length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
4445 NULL, GetLastError(), 0, msg, sizeof(msg), NULL);
4446 msg[length] = '\0';
4447 LogPrint(0, LOG_SYSTEM, 0, "Error creating new process: %s\n%s", msg, cmd);
4448 delete [] msg;
4449 delete pData;
4450 return 0;
4451 }
4452
4453 if (ghJob)
4454 AssignProcessToJobObject(ghJob, pData->procInfo.hProcess);
4455
4456 ProcessInformationMap->insert(Proc_Pair((uint32)pData->procInfo.dwProcessId, pData));
4457 return pData->procInfo.dwProcessId;
4458
4459 #else
4460
4461 //std::vector<std::string> args = utils::TextListSplit(cmdline, " ");
4462 //int argc = args.size();
4464 //char** argv = new char*[argc+1];
4465 //int len;
4466 //for (int n=0; n<argc; n++) {
4467 // argv[n] = new char[args[n].size() + 1];
4468 // utils::strcpyavail(argv[n], args[n].c_str(), args[n].size()+1, true);
4469 //}
4470 //argv[argc] = NULL;
4471
4472 int argc = 0;
4473 char** argv = SplitCommandline(cmdline, argc);
4474 if (!argc)
4475 return -1;
4476
4477 pid_t ppid_before_fork = getpid();
4478 int pid = fork();
4479
4480 if (pid < 0) {
4481 DeleteCommandline(argv, argc);
4482 return 0;
4483 }
4484 // We are the child process
4485 else if (pid == 0) {
4486
4487 if ( initdir && (chdir(initdir) != 0) ) {
4488 LogPrint(0,LOG_SYSTEM,0,"Could not run process '%s' from starup dir '%s'\n", cmdline, initdir);
4489 exit(0);
4490 }
4491
4492 #ifndef __APPLE__
4493 int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
4494 if (r == -1) { perror(0); exit(1); }
4495 // test in case the original parent exited just
4496 // before the prctl() call
4497 if (getppid() != ppid_before_fork)
4498 exit(1);
4499 #endif
4500
4501 execvp(argv[0],argv);
4502 // here we exit the program, if we continue the call failed...
4503
4504 int res = errno;
4505 // If program didn't take over the exec call failed.
4506 LogPrint(0,LOG_SYSTEM,0,"Could not create new process: %s (%d)!\n", cmdline, res);
4507 exit(0);
4508
4509 }
4510 // we are in the calling process
4511 else {
4512 DeleteCommandline(argv, argc);
4513 return pid;
4514 }
4515
4516 #endif
4517}
4518
4519uint8 GetProcessStatus(uint32 proc, int &returncode) {
4520 returncode = 0;
4521 #if defined WINDOWS
4522 DWORD exitcode;
4523 std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
4524 if (i != ProcessInformationMap->end()) {
4525 ProcessData* pData = i->second;
4526 if (GetExitCodeProcess(pData->procInfo.hProcess, &exitcode) && (exitcode == STILL_ACTIVE)) {
4527 return PROC_RUNNING;
4528 }
4529 else {
4530 ProcessInformationMap->erase(i);
4531 CloseHandle(pData->procInfo.hProcess);
4532 CloseHandle(pData->procInfo.hThread);
4533 if (pData->hOutRead) CloseHandle(pData->hOutRead);
4534 if (pData->hErrRead) CloseHandle(pData->hErrRead);
4535 if (pData->hInWrite) CloseHandle(pData->hInWrite); // 2.7c stdin pipe
4536 if (pData->hJob) CloseHandle(pData->hJob); // 2.7c per-child job
4537 delete pData;
4538 returncode = exitcode;
4539 return PROC_TERMINATED;
4540 }
4541 }
4542 else {
4543 // We didn't create this process, look it up by id
4544 HANDLE hproc = OpenProcess(NULL, false, (DWORD)proc);
4545 if (!hproc)
4546 return PROC_ERROR;
4547 if (GetExitCodeProcess(hproc, &exitcode) && (exitcode == STILL_ACTIVE)) {
4548 CloseHandle(hproc);
4549 return PROC_RUNNING;
4550 }
4551 else {
4552 CloseHandle(hproc);
4553 return PROC_TERMINATED;
4554 }
4555 }
4556 #else
4557 int status;
4558 int res = waitpid(proc, &status, WNOHANG);
4559
4560 if (res == proc) {
4561 //if (WIFEXITED(status)) {
4562 // printf("exited, status=%d\n", WEXITSTATUS(status));
4563 // } else if (WIFSIGNALED(status)) {
4564 // printf("killed by signal %d\n", WTERMSIG(status));
4565 // } else if (WIFSTOPPED(status)) {
4566 // printf("stopped by signal %d\n", WSTOPSIG(status));
4567 // } else if (WIFCONTINUED(status)) {
4568 // printf("continued\n");
4569 // }
4570 returncode = WEXITSTATUS(status);
4571 //printf("Return code: %d\n", returncode);
4572 return PROC_TERMINATED;
4573 }
4574 else if (res == 0) {
4575 return PROC_RUNNING;
4576 }
4577 return PROC_TERMINATED;
4578 #endif
4579}
4580
4581#if !defined(WINDOWS)
4582// POSIX: release any capture pipe read-fds recorded for this pid by NewProcessEx.
4583// Safe to call for pids that were not captured (no-op). Does NOT reap/kill.
4584void ReleaseProcessCapture(uint32 proc) {
4585 if (!ProcessInformationMap) return;
4586 std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
4587 if (i == ProcessInformationMap->end()) return;
4588 ProcessData* pData = i->second;
4589 if (pData->outFD >= 0) close(pData->outFD);
4590 if (pData->errFD >= 0) close(pData->errFD);
4591 ProcessInformationMap->erase(i);
4592 delete pData;
4593}
4594#else
4595// Windows: capture pipes are Win32 HANDLEs owned by the ProcessData entry and are
4596// closed by EndProcess; there is no separate read-fd to release here. Provided so
4597// the symbol resolves for the SWIG language bindings, which wrap it unconditionally.
4598void ReleaseProcessCapture(uint32 proc) {
4599}
4600#endif
4601
4602bool EndProcess(uint32 proc) {
4603 #if defined WINDOWS
4605 return true;
4606 std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
4607 if (i != ProcessInformationMap->end()) {
4608 ProcessData* pData = i->second;
4609 TerminateProcess(pData->procInfo.hProcess, 1);
4610 CloseHandle(pData->procInfo.hProcess);
4611 CloseHandle(pData->procInfo.hThread);
4612 if (pData->hOutRead) CloseHandle(pData->hOutRead);
4613 if (pData->hErrRead) CloseHandle(pData->hErrRead);
4614 if (pData->hInWrite) CloseHandle(pData->hInWrite); // 2.7c stdin pipe
4615 if (pData->hJob) CloseHandle(pData->hJob); // 2.7c per-child job
4616 ProcessInformationMap->erase(i);
4617 delete pData;
4618 return true;
4619 }
4620 else {
4621 HANDLE hproc = OpenProcess(NULL, NULL, (DWORD)proc);
4622 if (!hproc)
4623 return true;
4624 if (TerminateProcess(hproc, 1) == 0) {
4625 CloseHandle(hproc);
4626 return false;
4627 }
4628 else {
4629 CloseHandle(hproc);
4630 return true;
4631 }
4632 }
4633 #else
4634 bool ok = (kill(proc, SIGKILL) == 0);
4635 ReleaseProcessCapture(proc); // close+erase any capture pipes for this pid
4636 return ok;
4637 #endif
4638}
4639
4640// ---- ProbeProcessWait (step 2.7b-i): stall-on-stdin probe primitive ----
4641#if defined(WINDOWS)
4642// Windows (step 2.7c): composite heuristic, NEVER exact.
4643//
4644// Windows cannot name the syscall a thread is blocked in, and Thor accepted the
4645// consequence explicitly ("Input detection - well, I think that's the best we
4646// have", 2026-08-05). Every alternative was rejected on inspection:
4647// - no public "blocked in ReadFile on handle X" API exists;
4648// - NtQueryInformationThread(ThreadLastSystemCall) is undocumented and unstable
4649// across builds;
4650// - GetThreadContext yields the instruction pointer, not the wait target;
4651// - NtQuerySystemInformation(SystemProcessInformation) gives wait REASONS but
4652// not the waited-on handle;
4653// - PeekNamedPipe answers the opposite question (is there unread child OUTPUT);
4654// - writing a probe byte to see whether it is consumed is destructive.
4655// So waitExact is hard-wired false here and waitingStdin is a composite:
4656// (1) the process is alive, AND
4657// (2) every thread is in a wait state (GetThreadWaitReason via the toolhelp
4658// snapshot is unavailable, so we use the job's active-process count plus
4659// thread wait counters we CAN read - see below), AND
4660// (3) the whole child TREE has burned ~no CPU since the previous sample.
4661// The CALLER (CLISession::probeStall) additionally requires a quiet output window
4662// before grading "likely", so a slow-but-working step is not flagged.
4663//
4664// ⚠️ Documented false positives, preserved deliberately: a socket/network wait or
4665// a plain `sleep` is indistinguishable from an stdin block - both are quiet and
4666// idle. The signal means "probably awaiting input or otherwise idle-blocked"; the
4667// decision to terminate stays with the sender.
4668//
4669// CPU accounting uses the PER-CHILD job object (ProcessData::hJob) so a
4670// GRANDCHILD's CPU counts: the real reader is usually a grandchild (`python` under
4671// `cmd.exe`), and per-process GetProcessTimes would report ~0 for the shell and
4672// mis-flag a busy tree as idle. Falls back to GetProcessTimes when the job is
4673// unavailable (pre-Win8 nested-job failure, or a pid we did not start).
4674bool ProbeProcessWait(uint32 pid, ProcessWaitInfo& out) {
4675 out.alive = false; out.waitExact = false; out.waitingStdin = false; out.cpuTimeMs = 0;
4676 if (!pid) return false;
4677 HANDLE h = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, (DWORD)pid);
4678 if (!h) return true; // gone (or inaccessible): degraded but valid probe
4679 DWORD code = 0;
4680 if (GetExitCodeProcess(h, &code) && code == STILL_ACTIVE)
4681 out.alive = true;
4682
4683 // --- (3) whole-tree CPU, job first, per-process as a fallback ---------------
4684 bool haveTreeCpu = false;
4685 ProcessData* pd = NULL;
4687 std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(pid);
4688 if (i != ProcessInformationMap->end()) pd = i->second;
4689 }
4690 if (pd && pd->hJob) {
4691 JOBOBJECT_BASIC_ACCOUNTING_INFORMATION jbai;
4692 ZeroMemory(&jbai, sizeof(jbai));
4693 DWORD retLen = 0;
4694 if (QueryInformationJobObject(pd->hJob, JobObjectBasicAccountingInformation,
4695 &jbai, sizeof(jbai), &retLen)) {
4696 // TotalUserTime/TotalKernelTime are LARGE_INTEGER in 100ns units and
4697 // accumulate over the job's whole lifetime, including exited members -
4698 // which is what we want: a monotonic tree-wide counter the caller diffs.
4699 out.cpuTimeMs = (uint64)((jbai.TotalUserTime.QuadPart +
4700 jbai.TotalKernelTime.QuadPart) / 10000ULL);
4701 haveTreeCpu = true;
4702 }
4703 }
4704 if (!haveTreeCpu) {
4705 FILETIME ct, et, kt, ut;
4706 if (GetProcessTimes(h, &ct, &et, &kt, &ut)) {
4707 ULARGE_INTEGER k, u;
4708 k.LowPart = kt.dwLowDateTime; k.HighPart = kt.dwHighDateTime;
4709 u.LowPart = ut.dwLowDateTime; u.HighPart = ut.dwHighDateTime;
4710 out.cpuTimeMs = (uint64)((k.QuadPart + u.QuadPart) / 10000ULL); // 100ns -> ms
4711 }
4712 }
4713
4714 // --- (2) is the tree quiescent? --------------------------------------------
4715 // A child blocked on stdin has no thread making progress. Windows will not tell
4716 // us WHICH handle a thread waits on, but it will tell us how much CPU each
4717 // thread has consumed, and a thread parked in a blocking read consumes none.
4718 // So sample the tree's CPU twice a short interval apart: zero delta across the
4719 // whole tree means nothing in it is running.
4720 //
4721 // ⚠️ waitingStdin must NOT simply mean "alive". A first draft set it true for
4722 // any live process, which made the composite meaningless - the busy-child test
4723 // would then rest entirely on the caller's CPU-delta term, and one unlucky
4724 // sample under tolerance would mis-flag a working child as awaiting input.
4725 // Distinguishing "parked" from "running but quiet" is the whole job of this
4726 // flag, so it is decided here on its own evidence.
4727 //
4728 // The double-sample is deliberately short (12 ms): long enough that a running
4729 // tree accrues measurable time on any modern scheduler quantum, short enough
4730 // that a caller polling every ~150 ms does not notice the cost.
4731 if (out.alive) {
4732 uint64 firstCpu = out.cpuTimeMs;
4733 Sleep(12);
4734 uint64 secondCpu = firstCpu;
4735 if (pd && pd->hJob) {
4736 JOBOBJECT_BASIC_ACCOUNTING_INFORMATION j2;
4737 ZeroMemory(&j2, sizeof(j2));
4738 DWORD rl = 0;
4739 if (QueryInformationJobObject(pd->hJob, JobObjectBasicAccountingInformation,
4740 &j2, sizeof(j2), &rl))
4741 secondCpu = (uint64)((j2.TotalUserTime.QuadPart +
4742 j2.TotalKernelTime.QuadPart) / 10000ULL);
4743 } else {
4744 FILETIME c2, e2, k2, u2;
4745 if (GetProcessTimes(h, &c2, &e2, &k2, &u2)) {
4746 ULARGE_INTEGER k, u;
4747 k.LowPart = k2.dwLowDateTime; k.HighPart = k2.dwHighDateTime;
4748 u.LowPart = u2.dwLowDateTime; u.HighPart = u2.dwHighDateTime;
4749 secondCpu = (uint64)((k.QuadPart + u.QuadPart) / 10000ULL);
4750 }
4751 }
4752 // Report the LATER sample so the caller's own delta stays monotonic.
4753 out.cpuTimeMs = secondCpu;
4754 // Quiescent candidate: the tree burned no measurable CPU across the window.
4755 // The caller (CLISession::probeStall) still requires a quiet OUTPUT window
4756 // of stallProbeMs and a near-zero delta between ITS samples before grading
4757 // "likely" - so a socket wait or a plain sleep will legitimately look the
4758 // same here, which is the documented and accepted false positive.
4759 out.waitingStdin = (secondCpu == firstCpu);
4760 }
4761 CloseHandle(h);
4762 return true;
4763}
4764#elif defined(__APPLE__)
4765// macOS: libproc composite. Cannot name the blocking syscall, so waitExact is
4766// ALWAYS false; waitingStdin = all threads TH_STATE_WAITING and fd 0 is a pipe.
4767// Same-uid children only; no entitlements required.
4768bool ProbeProcessWait(uint32 pid, ProcessWaitInfo& out) {
4769 out.alive = false; out.waitExact = false; out.waitingStdin = false; out.cpuTimeMs = 0;
4770 if (!pid) return false;
4771 if (kill((pid_t)pid, 0) != 0 && errno == ESRCH) return true; // gone
4772 out.alive = true;
4773 struct proc_taskinfo ti;
4774 if (proc_pidinfo((int)pid, PROC_PIDTASKINFO, 0, &ti, sizeof(ti)) == (int)sizeof(ti))
4775 out.cpuTimeMs = (uint64)((ti.pti_total_user + ti.pti_total_system) / 1000000ULL); // ns -> ms
4776 uint64_t tids[256];
4777 int n = proc_pidinfo((int)pid, PROC_PIDLISTTHREADS, 0, tids, sizeof(tids));
4778 if (n <= 0) return true; // degraded: cannot inspect threads
4779 int count = n / (int)sizeof(uint64_t);
4780 bool allWaiting = (count > 0);
4781 for (int i = 0; i < count; i++) {
4782 struct proc_threadinfo th;
4783 if (proc_pidinfo((int)pid, PROC_PIDTHREADINFO, tids[i], &th, sizeof(th)) != (int)sizeof(th)
4784 || th.pth_run_state != TH_STATE_WAITING) {
4785 allWaiting = false; break;
4786 }
4787 }
4788 if (allWaiting) {
4789 struct pipe_fdinfo pfd;
4790 if (proc_pidfdinfo((int)pid, 0, PROC_PIDFDPIPEINFO, &pfd, sizeof(pfd)) == (int)sizeof(pfd))
4791 out.waitingStdin = true; // best-effort composite; never exact on macOS
4792 }
4793 return true;
4794}
4795#else
4796// Linux: /proc-based, exact. State 'S' pre-filter from /proc/<pid>/stat, then
4797// /proc/<pid>/task/*/syscall: blocked in read(2) with arg0==0 => waitingStdin.
4798// Walks the whole process group (the real stdin reader is usually a child of sh).
4799namespace { // ProbeProcessWait internals
4800#if defined(__x86_64__)
4801 static const long PPW_SYS_read = 0;
4802#elif defined(__aarch64__)
4803 static const long PPW_SYS_read = 63;
4804#else
4805 static const long PPW_SYS_read = -1; // unknown arch: degrade to waitExact=false
4806#endif
4807 // Parse /proc/<pid>/stat (or task stat): state char, pgrp, utime+stime in ticks.
4808 static bool ppwReadStat(const char* path, char& state, long& pgrp, uint64& cpuTicks) {
4809 FILE* f = fopen(path, "r");
4810 if (!f) return false;
4811 char buf[1024];
4812 size_t len = fread(buf, 1, sizeof(buf) - 1, f);
4813 fclose(f);
4814 if (!len) return false;
4815 buf[len] = 0;
4816 const char* p = strrchr(buf, ')'); // comm may contain spaces/parens
4817 if (!p) return false;
4818 p++; // now at " S ppid pgrp ..." (fields 3..)
4819 long ppid = 0; unsigned long ut = 0, st = 0;
4820 char st_c = 0;
4821 // fields: 3=state 4=ppid 5=pgrp ... 14=utime 15=stime
4822 if (sscanf(p, " %c %ld %ld %*d %*d %*d %*u %*u %*u %*u %*u %lu %lu",
4823 &st_c, &ppid, &pgrp, &ut, &st) != 5)
4824 return false;
4825 state = st_c;
4826 cpuTicks = (uint64)ut + (uint64)st;
4827 return true;
4828 }
4829 // Check /proc/<pid>[/task/<tid>]/syscall: blocked in read(0,...)?
4830 // Returns: 1 = yes, 0 = no, -1 = unreadable (degrade).
4831 static int ppwSyscallIsStdinRead(const char* path) {
4832 FILE* f = fopen(path, "r");
4833 if (!f) return -1;
4834 char buf[256];
4835 size_t len = fread(buf, 1, sizeof(buf) - 1, f);
4836 fclose(f);
4837 if (!len) return -1;
4838 buf[len] = 0;
4839 long nr = 0; unsigned long arg0 = 0;
4840 if (sscanf(buf, "%ld %lx", &nr, &arg0) < 1)
4841 return -1; // "running" or unparsable
4842 if (PPW_SYS_read < 0) return -1;
4843 return (nr == PPW_SYS_read && arg0 == 0) ? 1 : 0;
4844 }
4845} // anonymous namespace
4846
4847bool ProbeProcessWait(uint32 pid, ProcessWaitInfo& out) {
4848 out.alive = false; out.waitExact = false; out.waitingStdin = false; out.cpuTimeMs = 0;
4849 if (!pid) return false;
4850 char path[64];
4851 snprintf(path, sizeof(path), "/proc/%u/stat", pid);
4852 char state = 0; long pgid = 0; uint64 ticks = 0;
4853 if (!ppwReadStat(path, state, pgid, ticks)) return true; // gone
4854 if (state == 'Z' || state == 'X') return true; // zombie/dead: not alive for wait purposes
4855 out.alive = true;
4856 long tck = sysconf(_SC_CLK_TCK);
4857 if (tck <= 0) tck = 100;
4858 uint64 totalTicks = 0;
4859 bool sawUnreadable = false, sawStdinRead = false;
4860 // Collect the process group: target + any /proc/<p> whose pgrp matches its pgid
4861 // (the real stdin reader is typically a child of /bin/sh, not sh itself).
4862 DIR* proc = opendir("/proc");
4863 if (proc) {
4864 struct dirent* de;
4865 while ((de = readdir(proc)) != NULL) {
4866 if (de->d_name[0] < '0' || de->d_name[0] > '9') continue;
4867 unsigned long p = strtoul(de->d_name, NULL, 10);
4868 char pstate = 0; long ppgrp = 0; uint64 pticks = 0;
4869 snprintf(path, sizeof(path), "/proc/%lu/stat", p);
4870 if (!ppwReadStat(path, pstate, ppgrp, pticks)) continue;
4871 if (ppgrp != pgid && (uint32)p != pid) continue;
4872 totalTicks += pticks;
4873 // Walk this member's threads; only sleeping ones can be blocked in read(0).
4874 char tdir[64];
4875 snprintf(tdir, sizeof(tdir), "/proc/%lu/task", p);
4876 DIR* tasks = opendir(tdir);
4877 if (!tasks) { sawUnreadable = true; continue; }
4878 struct dirent* te;
4879 while ((te = readdir(tasks)) != NULL) {
4880 if (te->d_name[0] < '0' || te->d_name[0] > '9') continue;
4881 char tpath[352]; char tstate = 0; long tpgrp = 0; uint64 tt = 0;
4882 snprintf(tpath, sizeof(tpath), "%s/%s/stat", tdir, te->d_name);
4883 if (ppwReadStat(tpath, tstate, tpgrp, tt) && tstate != 'S')
4884 continue; // cheap pre-filter: only sleeping threads
4885 snprintf(tpath, sizeof(tpath), "%s/%s/syscall", tdir, te->d_name);
4886 int r = ppwSyscallIsStdinRead(tpath);
4887 if (r < 0) sawUnreadable = true;
4888 else if (r == 1) sawStdinRead = true;
4889 }
4890 closedir(tasks);
4891 }
4892 closedir(proc);
4893 } else sawUnreadable = true;
4894 out.cpuTimeMs = totalTicks * 1000ULL / (uint64)tck;
4895 if (sawStdinRead) { out.waitingStdin = true; out.waitExact = true; }
4896 else if (!sawUnreadable) out.waitExact = true; // exact negative
4897 // else: /proc/*/syscall unreadable (perms/kernel config) -> degrade gracefully
4898 return true;
4899}
4900#endif // ProbeProcessWait per-OS
4901
4902#ifndef WINDOWS
4903// --- Persistent POSIX process launcher -------------------------------------
4904// prctl(PR_SET_PDEATHSIG) binds the death signal to the LAUNCHING THREAD, not
4905// the process. If NewProcessEx is called from a transient thread, the child is
4906// killed as soon as that thread exits. Die-with-parent launches are therefore
4907// routed through a single persistent launcher thread (lazy-created, lives for
4908// the process lifetime) so the signal is armed against a long-lived thread.
4909// On macOS the same effect is emulated with a kqueue nanny (see DoPosixForkExec).
4911 // inputs (borrowed pointers, valid for the duration of the call)
4912 const char* cmdline;
4913 const char* initdir;
4914 const std::map<std::string, std::string>* env;
4920 char* const* argv; // pre-split argv (NULL in shell mode)
4921 int outPipe0, outPipe1, errPipe0, errPipe1; // pre-created pipes (or -1s)
4922 // outputs
4923 pid_t pid; // <0 fork failed, else child pid
4924 bool done;
4925};
4926
4927// The fork+child-setup body shared by the direct path and the launcher thread.
4929 pid_t ppid_before_fork = getpid();
4930 (void)ppid_before_fork;
4931 // Shell-mode argv: run "/bin/sh -c <cmdline>" so shell semantics apply.
4932 const char* shArgv[4] = { "sh", "-c", r.cmdline, NULL };
4933 pid_t pid = fork();
4934 if (pid < 0)
4935 return pid;
4936 if (pid == 0) {
4937 // Child
4938 if (r.captureOutput) {
4939 dup2(r.outPipe1, STDOUT_FILENO);
4940 dup2(r.errPipe1, STDERR_FILENO);
4941 close(r.outPipe0); close(r.outPipe1);
4942 close(r.errPipe0); close(r.errPipe1);
4943 }
4944 else if (r.ignoreOutput) {
4945 int devnull = open("/dev/null", O_WRONLY);
4946 if (devnull >= 0) {
4947 dup2(devnull, STDOUT_FILENO);
4948 dup2(devnull, STDERR_FILENO);
4949 close(devnull);
4950 }
4951 }
4952 // else: inherit stdout/stderr as-is (console mode).
4953
4954 if (r.newProcessGroup)
4955 setpgid(0, 0); // child leads its own group (== its pid)
4956
4957 if (r.initdir && chdir(r.initdir) != 0) {
4958 LogPrint(0, LOG_SYSTEM, 0, "NewProcessEx: could not run '%s' from startup dir '%s'\n", r.cmdline, r.initdir);
4959 _exit(127);
4960 }
4961
4962 if (r.env) {
4963 std::map<std::string, std::string>::const_iterator ei = r.env->begin();
4964 while (ei != r.env->end()) { setenv(ei->first.c_str(), ei->second.c_str(), 1); ei++; }
4965 }
4966
4967 #ifdef __APPLE__
4968 if (r.posixParentDeathSig) {
4969 // macOS has no PR_SET_PDEATHSIG: emulate with a detached kqueue nanny.
4970 // Force our own process group (even if newProcessGroup was false) so
4971 // the nanny can SIGKILL the whole tree via kill(-pgid).
4972 setpgid(0, 0);
4973 if (getppid() != ppid_before_fork)
4974 _exit(1); // parent already died before we could arm
4975 pid_t self = getpid();
4976 pid_t nanny = fork();
4977 if (nanny == 0) {
4978 // Grandchild nanny: wait for the launching process to exit, then
4979 // kill the child's whole process group.
4980 int kq = kqueue();
4981 if (kq >= 0) {
4982 struct kevent ev;
4983 EV_SET(&ev, ppid_before_fork, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT, 0, NULL);
4984 if (kevent(kq, &ev, 1, NULL, 0, NULL) != -1) {
4985 struct kevent out;
4986 kevent(kq, NULL, 0, &out, 1, NULL);
4987 }
4988 }
4989 kill(-self, SIGKILL);
4990 _exit(0);
4991 }
4992 }
4993 #else
4994 if (r.posixParentDeathSig) {
4995 int pres = prctl(PR_SET_PDEATHSIG, r.posixParentDeathSig);
4996 if (pres == -1) { perror(0); _exit(1); }
4997 // Parent may have died between fork() and prctl(): check.
4998 if (getppid() != ppid_before_fork)
4999 _exit(1);
5000 }
5001 #endif
5002
5003 execvp(r.posixUseShell ? "/bin/sh" : r.argv[0],
5004 r.posixUseShell ? (char* const*)shArgv : r.argv);
5005 LogPrint(0, LOG_SYSTEM, 0, "NewProcessEx: could not create new process: %s (%d)!\n", r.cmdline, errno);
5006 _exit(127);
5007 }
5008 return pid;
5009}
5010
5011// Single-slot handoff to the persistent launcher thread. Launches are rare, so
5012// callers are serialized under gLaunchSerialMx.
5013static std::mutex gLaunchMx;
5014static std::condition_variable gLaunchCv;
5016static bool gLauncherStarted = false;
5017static std::mutex gLaunchSerialMx;
5018
5020 std::unique_lock<std::mutex> lock(gLaunchMx);
5021 for (;;) {
5022 while (!gLaunchReq)
5023 gLaunchCv.wait(lock);
5025 req->pid = DoPosixForkExec(*req);
5026 req->done = true;
5027 gLaunchReq = NULL;
5028 gLaunchCv.notify_all();
5029 }
5030 thread_ret_val(0);
5031}
5032
5034 std::lock_guard<std::mutex> serial(gLaunchSerialMx);
5035 std::unique_lock<std::mutex> lock(gLaunchMx);
5036 if (!gLauncherStarted) {
5037 ThreadHandle th;
5038 uint32 osID = 0;
5039 if (!CreateThread((THREAD_FUNCTION)ProcessLauncherRun, NULL, th, osID))
5040 return DoPosixForkExec(req); // degraded: launch inline (old behavior)
5041 gLauncherStarted = true;
5042 }
5043 req.done = false;
5044 gLaunchReq = &req;
5045 gLaunchCv.notify_all();
5046 while (!req.done)
5047 gLaunchCv.wait(lock);
5048 return req.pid;
5049}
5050#endif // !WINDOWS
5051
5052uint32 NewProcessEx(const char* cmdline, const char* initdir, const char* title,
5053 const std::map<std::string, std::string>* env,
5054 bool captureOutput, bool ignoreOutput, bool newProcessGroup,
5055 int posixParentDeathSig, bool posixUseShell) {
5056 if (!cmdline || !strlen(cmdline))
5057 return 0;
5058
5059 #if defined WINDOWS
5060
5061 (void)posixParentDeathSig; (void)posixUseShell;
5062 if (!ProcessInformationMap) {
5063 ProcessInformationMap = new std::map<uint32, ProcessData*>;
5064 ghJob = CreateJobObject(NULL, NULL); // GLOBAL
5065 if (ghJob) {
5066 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };
5067 jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
5068 SetInformationJobObject(ghJob, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli));
5069 }
5070 }
5071
5072 ProcessData* pData = new ProcessData;
5073 ZeroMemory(&(pData->procInfo), sizeof(pData->procInfo));
5074 ZeroMemory(&(pData->si), sizeof(pData->si));
5075 pData->si.cb = sizeof(pData->si);
5076 pData->si.wShowWindow = SW_SHOWNOACTIVATE;
5077 pData->si.dwFlags = STARTF_USESHOWWINDOW;
5078
5079 // hInRead is the CHILD's end of the new stdin pipe (2.7c) - inheritable, and
5080 // closed in the parent right after CreateProcess like the other child ends.
5081 HANDLE hOutWrite = NULL, hErrWrite = NULL, hNull = NULL, hInRead = NULL;
5082 SECURITY_ATTRIBUTES sa;
5083 ZeroMemory(&sa, sizeof(sa));
5084 sa.nLength = sizeof(sa);
5085 sa.bInheritHandle = TRUE;
5086 sa.lpSecurityDescriptor = NULL;
5087
5088 if (captureOutput) {
5089 // Create stdout/stderr pipes; make only the WRITE ends inheritable.
5090 if ( !CreatePipe(&pData->hOutRead, &hOutWrite, &sa, 0) ||
5091 !CreatePipe(&pData->hErrRead, &hErrWrite, &sa, 0) ) {
5092 LogPrint(0, LOG_SYSTEM, 0, "NewProcessEx: could not create output pipes for '%s'", cmdline);
5093 if (pData->hOutRead) CloseHandle(pData->hOutRead);
5094 if (hOutWrite) CloseHandle(hOutWrite);
5095 if (pData->hErrRead) CloseHandle(pData->hErrRead);
5096 delete pData;
5097 return 0;
5098 }
5099 SetHandleInformation(pData->hOutRead, HANDLE_FLAG_INHERIT, 0);
5100 SetHandleInformation(pData->hErrRead, HANDLE_FLAG_INHERIT, 0);
5101 // T1.1 step 2.7c: give the child its OWN stdin pipe. Previously it
5102 // inherited the parent's STD_INPUT_HANDLE, which has two consequences:
5103 // a child that reads stdin gets whatever the parent's console has (an
5104 // instant EOF when there is no console at all, e.g. under SSH or as a
5105 // service), so it CANNOT block awaiting input - making a stall-on-stdin
5106 // probe impossible to demonstrate; and there was no handle to write to,
5107 // so WriteProcessInput()/CLISession::writeStdin() had nothing to use.
5108 // Only the child's READ end is inheritable; we keep the write end.
5109 if (!CreatePipe(&hInRead, &pData->hInWrite, &sa, 0)) {
5110 LogPrint(0, LOG_SYSTEM, 0, "NewProcessEx: could not create stdin pipe for '%s'", cmdline);
5111 CloseHandle(pData->hOutRead); CloseHandle(hOutWrite);
5112 CloseHandle(pData->hErrRead); CloseHandle(hErrWrite);
5113 delete pData;
5114 return 0;
5115 }
5116 SetHandleInformation(pData->hInWrite, HANDLE_FLAG_INHERIT, 0);
5117 pData->si.dwFlags |= STARTF_USESTDHANDLES;
5118 pData->si.hStdOutput = hOutWrite;
5119 pData->si.hStdError = hErrWrite;
5120 pData->si.hStdInput = hInRead;
5121 }
5122 else if (ignoreOutput) {
5123 hNull = CreateFile("NUL", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
5124 &sa, OPEN_EXISTING, 0, NULL);
5125 if (hNull != INVALID_HANDLE_VALUE) {
5126 pData->si.dwFlags |= STARTF_USESTDHANDLES;
5127 pData->si.hStdOutput = hNull;
5128 pData->si.hStdError = hNull;
5129 pData->si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
5130 }
5131 else hNull = NULL;
5132 }
5133
5134 // Build the environment block (parent env + overrides) if any overrides given.
5135 char* envBlock = NULL;
5136 if (env && env->size()) {
5137 std::map<std::string, std::string> merged;
5138 LPCH parentEnv = GetEnvironmentStrings();
5139 if (parentEnv) {
5140 for (LPCH p = parentEnv; *p; ) {
5141 std::string entry = p;
5142 size_t eq = entry.find('=');
5143 if (eq != std::string::npos && eq > 0)
5144 merged[entry.substr(0, eq)] = entry.substr(eq + 1);
5145 p += entry.size() + 1;
5146 }
5147 FreeEnvironmentStrings(parentEnv);
5148 }
5149 std::map<std::string, std::string>::const_iterator ei = env->begin();
5150 while (ei != env->end()) { merged[ei->first] = ei->second; ei++; }
5151 size_t total = 1;
5152 std::map<std::string, std::string>::iterator mi = merged.begin();
5153 while (mi != merged.end()) { total += mi->first.size() + 1 + mi->second.size() + 1; mi++; }
5154 envBlock = new char[total];
5155 size_t off = 0;
5156 for (mi = merged.begin(); mi != merged.end(); mi++) {
5157 std::string kv = mi->first + "=" + mi->second;
5158 memcpy(envBlock + off, kv.c_str(), kv.size() + 1);
5159 off += kv.size() + 1;
5160 }
5161 envBlock[off] = 0;
5162 }
5163
5164 DWORD flags = CREATE_NEW_CONSOLE;
5165 if (newProcessGroup)
5166 flags |= CREATE_NEW_PROCESS_GROUP;
5167
5168 // NOTE: cmdline/title are plain strings, NOT printf formats. Copy them
5169 // verbatim into mutable new[] buffers. (Previously StringFormat(size, x)
5170 // was used, which interpreted any '%' in the command line as a printf
5171 // conversion -> invalid-parameter fast-fail (c0000409) for commands like
5172 // `cmd /c echo %VAR%`, and mismatched the malloc alloc with delete[].)
5173 char* titleCopy = NULL;
5174 if (title) {
5175 size_t tlen = strlen(title);
5176 titleCopy = new char[tlen + 1];
5177 memcpy(titleCopy, title, tlen + 1);
5178 pData->si.lpTitle = titleCopy;
5179 }
5180 size_t clen = strlen(cmdline);
5181 char* cmd = new char[clen + 1];
5182 memcpy(cmd, cmdline, clen + 1);
5183
5184 int res = CreateProcess(NULL, cmd, NULL, NULL, TRUE, flags, envBlock, initdir, &pData->si, &pData->procInfo);
5185
5186 delete [] titleCopy;
5187 delete [] cmd;
5188 if (envBlock) delete [] envBlock;
5189 // Parent closes the child's ends and the null handle; keeps its own ends.
5190 // hInRead MUST be closed here: while the parent holds a copy of the child's
5191 // read end, closing our WRITE end would not deliver EOF to the child, so a
5192 // child waiting on stdin would hang forever instead of seeing our close.
5193 if (hOutWrite) CloseHandle(hOutWrite);
5194 if (hErrWrite) CloseHandle(hErrWrite);
5195 if (hInRead) CloseHandle(hInRead);
5196 if (hNull) CloseHandle(hNull);
5197
5198 if (res == 0) {
5199 char msg[2048];
5200 int length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, msg, sizeof(msg), NULL);
5201 msg[length] = '\0';
5202 LogPrint(0, LOG_SYSTEM, 0, "NewProcessEx: error creating process: %s\n%s", msg, cmdline);
5203 if (pData->hOutRead) CloseHandle(pData->hOutRead);
5204 if (pData->hErrRead) CloseHandle(pData->hErrRead);
5205 if (pData->hInWrite) CloseHandle(pData->hInWrite); // 2.7c stdin pipe
5206 // NOTE: hJob is not yet created at this point (it is assigned only after
5207 // a successful CreateProcess below), so there is nothing to close here.
5208 delete pData;
5209 return 0;
5210 }
5211 if (ghJob)
5212 AssignProcessToJobObject(ghJob, pData->procInfo.hProcess);
5213 // T1.1 step 2.7c: additionally place the child in its OWN job, so
5214 // ProbeProcessWait can read whole-TREE CPU for THIS child. ghJob above is
5215 // process-wide (kill-on-close) and aggregates every child we ever started,
5216 // so its accounting cannot answer "is this child's tree idle?".
5217 // Nested jobs require Windows 8+; on failure we simply leave hJob NULL and
5218 // the probe degrades to per-process times rather than refusing to work.
5219 pData->hJob = CreateJobObject(NULL, NULL);
5220 if (pData->hJob) {
5221 if (!AssignProcessToJobObject(pData->hJob, pData->procInfo.hProcess)) {
5222 CloseHandle(pData->hJob);
5223 pData->hJob = NULL; // degraded, not fatal
5224 }
5225 }
5226 ProcessInformationMap->insert(Proc_Pair((uint32)pData->procInfo.dwProcessId, pData));
5227 return pData->procInfo.dwProcessId;
5228
5229 #else
5230 // POSIX: real implementation. Fork/execvp with optional stdout/stderr
5231 // capture pipes, per-child env overrides (merged over the parent's),
5232 // working directory, and (optionally) the child leading its own process
5233 // group so a break signal can be delivered to the whole group via
5234 // SendProcessBreak(). Lifecycle (reap/kill) is via GetProcessStatus() /
5235 // WaitForProcess() / EndProcess(), which operate on the bare pid; the
5236 // only bookkeeping kept here is the capture read-fds for ReadProcessOutput().
5237 (void)title; (void)ignoreOutput;
5238
5240 ProcessInformationMap = new std::map<uint32, ProcessData*>;
5241
5242 int outPipe[2] = { -1, -1 }, errPipe[2] = { -1, -1 };
5243 if (captureOutput) {
5244 if (pipe(outPipe) != 0) {
5245 LogPrint(0, LOG_SYSTEM, 0, "NewProcessEx: could not create output pipes for '%s'", cmdline);
5246 return 0;
5247 }
5248 if (pipe(errPipe) != 0) {
5249 LogPrint(0, LOG_SYSTEM, 0, "NewProcessEx: could not create output pipes for '%s'", cmdline);
5250 close(outPipe[0]); close(outPipe[1]);
5251 return 0;
5252 }
5253 }
5254
5255 int argc = 0;
5256 char** argv = NULL;
5257 if (!posixUseShell) {
5258 argv = SplitCommandline(cmdline, argc);
5259 if (!argc) {
5260 if (captureOutput) { close(outPipe[0]); close(outPipe[1]); close(errPipe[0]); close(errPipe[1]); }
5261 if (argv) DeleteCommandline(argv, argc);
5262 return 0;
5263 }
5264 }
5265
5267 req.cmdline = cmdline; req.initdir = initdir; req.env = env;
5268 req.captureOutput = captureOutput; req.ignoreOutput = ignoreOutput;
5269 req.newProcessGroup = newProcessGroup; req.posixUseShell = posixUseShell;
5270 req.posixParentDeathSig = posixParentDeathSig;
5271 req.argv = argv;
5272 req.outPipe0 = outPipe[0]; req.outPipe1 = outPipe[1];
5273 req.errPipe0 = errPipe[0]; req.errPipe1 = errPipe[1];
5274 req.pid = -1; req.done = false;
5275 // Die-with-parent launches go through the persistent launcher thread so
5276 // PR_SET_PDEATHSIG (Linux) is armed against a thread that never exits;
5277 // all other launches fork inline on the caller thread exactly as before.
5278 int pid = (int)(posixParentDeathSig ? SubmitLaunchRequest(req) : DoPosixForkExec(req));
5279 if (pid < 0) {
5280 if (argv) DeleteCommandline(argv, argc);
5281 if (captureOutput) { close(outPipe[0]); close(outPipe[1]); close(errPipe[0]); close(errPipe[1]); }
5282 return 0;
5283 }
5284 // Parent (all child-side setup lives in DoPosixForkExec above)
5285 if (argv) DeleteCommandline(argv, argc);
5286 if (captureOutput) {
5287 close(outPipe[1]);
5288 close(errPipe[1]);
5289 fcntl(outPipe[0], F_SETFL, fcntl(outPipe[0], F_GETFL, 0) | O_NONBLOCK);
5290 fcntl(errPipe[0], F_SETFL, fcntl(errPipe[0], F_GETFL, 0) | O_NONBLOCK);
5291 ProcessData* pData = new ProcessData;
5292 pData->outFD = outPipe[0];
5293 pData->errFD = errPipe[0];
5294 ProcessInformationMap->insert(Proc_Pair((uint32)pid, pData));
5295 }
5296 return (uint32)pid;
5297 #endif
5298}
5299
5300// T1.1 step 2.7c: write to a captured child's stdin. Windows only - see the header.
5301bool WriteProcessInput(uint32 proc, const char* data, uint32 len) {
5302 #if defined WINDOWS
5303 if (!data || !len) return false;
5304 if (!ProcessInformationMap) return false;
5305 std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
5306 if (i == ProcessInformationMap->end()) return false;
5307 ProcessData* pData = i->second;
5308 if (!pData->hInWrite) return false; // child inherited our stdin; nothing to write
5309 uint32 written = 0;
5310 while (written < len) {
5311 DWORD n = 0;
5312 if (!WriteFile(pData->hInWrite, data + written, (DWORD)(len - written), &n, NULL) || !n) {
5313 // ERROR_BROKEN_PIPE / ERROR_NO_DATA: the child closed its read end.
5314 // Report failure rather than spinning; callers treat it as "child gone".
5315 return false;
5316 }
5317 written += (uint32)n;
5318 }
5319 return true;
5320 #else
5321 // POSIX: CLISession owns the child's stdin fd directly on the fork/pipe path
5322 // and writes to it there. Routing writes through this map as well would make
5323 // two owners of one descriptor, which is the class of bug that produced the
5324 // flake-1 double-free, so it is deliberately not offered here.
5325 (void)proc; (void)data; (void)len;
5326 return false;
5327 #endif
5328}
5329
5330// T1.1 step 2.7c: close a captured child's stdin, delivering EOF. Idempotent.
5331bool CloseProcessInput(uint32 proc) {
5332 #if defined WINDOWS
5333 if (!ProcessInformationMap) return false;
5334 std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
5335 if (i == ProcessInformationMap->end()) return false;
5336 ProcessData* pData = i->second;
5337 if (!pData->hInWrite) return false;
5338 CloseHandle(pData->hInWrite);
5339 pData->hInWrite = NULL; // so the teardown paths do not double-close
5340 return true;
5341 #else
5342 (void)proc;
5343 return false;
5344 #endif
5345}
5346
5347bool ReadProcessOutput(uint32 proc, std::string& out, std::string& err) {
5348 #if defined WINDOWS
5349 if (!ProcessInformationMap) return false;
5350 std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
5351 if (i == ProcessInformationMap->end()) return false;
5352 ProcessData* pData = i->second;
5353 HANDLE handles[2] = { pData->hOutRead, pData->hErrRead };
5354 std::string* targets[2] = { &out, &err };
5355 char buf[4096];
5356 for (int n = 0; n < 2; n++) {
5357 if (!handles[n]) continue;
5358 DWORD avail = 0;
5359 // PeekNamedPipe works on anonymous pipes to avoid blocking on ReadFile.
5360 while (PeekNamedPipe(handles[n], NULL, 0, NULL, &avail, NULL) && avail) {
5361 DWORD toRead = avail < sizeof(buf) ? avail : (DWORD)sizeof(buf);
5362 DWORD got = 0;
5363 if (!ReadFile(handles[n], buf, toRead, &got, NULL) || !got) break;
5364 targets[n]->append(buf, got);
5365 }
5366 }
5367 return true;
5368 #else
5369 // POSIX: drain the non-blocking capture pipes recorded by NewProcessEx.
5370 // Appends any newly available bytes (matching the Windows path); returns
5371 // true if this pid was launched with captureOutput.
5372 if (!ProcessInformationMap) return false;
5373 std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
5374 if (i == ProcessInformationMap->end()) return false;
5375 ProcessData* pData = i->second;
5376 int fds[2] = { pData->outFD, pData->errFD };
5377 std::string* targets[2] = { &out, &err };
5378 char buf[4096];
5379 for (int n = 0; n < 2; n++) {
5380 if (fds[n] < 0) continue;
5381 ssize_t got;
5382 while ((got = read(fds[n], buf, sizeof(buf))) > 0)
5383 targets[n]->append(buf, (size_t)got);
5384 }
5385 return true;
5386 #endif
5387}
5388
5389// ------------------------------------------------------------------ 2.7d
5390// Terminal-mode child launch (POSIX PTY). See Utils.h for semantics. The
5391// master-fd bookkeeping lives here (file-local), NOT in the header ProcessData
5392// map: terminal children share the pid-based lifecycle helpers but only need
5393// one extra fd, and a separate map avoids touching the capture-path struct.
5394#if !defined(WINDOWS)
5395static std::map<uint32, int>* TerminalMasterMap = NULL;
5396static bool TerminalMasterFind(uint32 proc, int& fd) {
5397 if (!TerminalMasterMap) return false;
5398 std::map<uint32, int>::iterator i = TerminalMasterMap->find(proc);
5399 if (i == TerminalMasterMap->end()) return false;
5400 fd = i->second;
5401 return true;
5402}
5403#endif
5404
5405uint32 NewProcessTerminal(const char* cmdline, const char* initdir,
5406 const std::map<std::string, std::string>* env,
5407 uint16 cols, uint16 rows) {
5408#if defined(WINDOWS)
5409 // Stub until ConPTY lands in step 2.7e.
5410 (void)cmdline; (void)initdir; (void)env; (void)cols; (void)rows;
5411 LogPrint(0, LOG_SYSTEM, 1, "NewProcessTerminal: unsupported until ConPTY (2.7e)");
5412 return 0;
5413#else
5414 if (!cmdline || !*cmdline) return 0;
5415 // posix_openpt (not openpty) so no -lutil link dependency on older glibc.
5416 int master = posix_openpt(O_RDWR | O_NOCTTY);
5417 if (master < 0) {
5418 LogPrint(0, LOG_SYSTEM, 0, "NewProcessTerminal: posix_openpt failed (%d) for '%s'", errno, cmdline);
5419 return 0;
5420 }
5421 if (grantpt(master) != 0 || unlockpt(master) != 0) {
5422 LogPrint(0, LOG_SYSTEM, 0, "NewProcessTerminal: grantpt/unlockpt failed (%d) for '%s'", errno, cmdline);
5423 close(master);
5424 return 0;
5425 }
5426 char slaveName[256];
5427 #if defined(__APPLE__)
5428 const char* sn = ptsname(master);
5429 if (!sn) { close(master); return 0; }
5430 strncpy(slaveName, sn, sizeof(slaveName) - 1);
5431 slaveName[sizeof(slaveName) - 1] = 0;
5432 #else
5433 if (ptsname_r(master, slaveName, sizeof(slaveName)) != 0) { close(master); return 0; }
5434 #endif
5435 struct winsize ws;
5436 memset(&ws, 0, sizeof(ws));
5437 ws.ws_col = cols ? cols : 80;
5438 ws.ws_row = rows ? rows : 25;
5439 ioctl(master, TIOCSWINSZ, &ws);
5440
5441 pid_t pid = fork();
5442 if (pid < 0) { close(master); return 0; }
5443 if (pid == 0) {
5444 // Child: new session so the PTY slave becomes its CONTROLLING terminal
5445 // (setsid also makes it a process-group leader => group kill via -pid).
5446 close(master);
5447 setsid();
5448 int slave = open(slaveName, O_RDWR); // first tty open after setsid acquires it
5449 if (slave < 0) _exit(127);
5450 #ifdef TIOCSCTTY
5451 ioctl(slave, TIOCSCTTY, 0); // explicit, for platforms that need it
5452 #endif
5453 dup2(slave, STDIN_FILENO);
5454 dup2(slave, STDOUT_FILENO);
5455 dup2(slave, STDERR_FILENO);
5456 if (slave > STDERR_FILENO) close(slave);
5457 if (initdir && *initdir && chdir(initdir) != 0) _exit(126);
5458 if (env) {
5459 std::map<std::string, std::string>::const_iterator ei = env->begin();
5460 while (ei != env->end()) { setenv(ei->first.c_str(), ei->second.c_str(), 1); ei++; }
5461 }
5462 execl("/bin/sh", "sh", "-c", cmdline, (char*)NULL);
5463 _exit(127);
5464 }
5465 // Parent: keep the master, non-blocking reads.
5466 fcntl(master, F_SETFL, fcntl(master, F_GETFL, 0) | O_NONBLOCK);
5467 if (!TerminalMasterMap)
5468 TerminalMasterMap = new std::map<uint32, int>;
5469 (*TerminalMasterMap)[(uint32)pid] = master;
5470 return (uint32)pid;
5471#endif
5472}
5473
5474bool ReadProcessTerminal(uint32 proc, std::string& out, bool& eofOut) {
5475 eofOut = false;
5476#if defined(WINDOWS)
5477 (void)proc; (void)out;
5478 return false; // stub until ConPTY (2.7e)
5479#else
5480 int fd = -1;
5481 if (!TerminalMasterFind(proc, fd)) return false;
5482 char buf[4096];
5483 for (;;) {
5484 ssize_t got = read(fd, buf, sizeof(buf));
5485 if (got > 0) { out.append(buf, (size_t)got); continue; }
5486 if (got == 0) { eofOut = true; break; } // clean EOF
5487 if (errno == EINTR) continue;
5488 if (errno == EAGAIN || errno == EWOULDBLOCK) break; // no data right now
5489 // EIO: the slave side is gone (child exited/hung up). On a PTY master
5490 // this is the NORMAL end-of-stream, not an error (Thor rc requirement:
5491 // callers still reap the real exit code via waitpid on the pid).
5492 eofOut = true;
5493 break;
5494 }
5495 return true;
5496#endif
5497}
5498
5499bool WriteProcessTerminal(uint32 proc, const char* data, uint32 len) {
5500#if defined(WINDOWS)
5501 (void)proc; (void)data; (void)len;
5502 return false; // stub until ConPTY (2.7e)
5503#else
5504 if (!data || !len) return false;
5505 int fd = -1;
5506 if (!TerminalMasterFind(proc, fd)) return false;
5507 uint32 written = 0;
5508 while (written < len) {
5509 ssize_t n = write(fd, data + written, len - written);
5510 if (n < 0) {
5511 if (errno == EINTR) continue;
5512 if (errno == EAGAIN || errno == EWOULDBLOCK) { Sleep(2); continue; }
5513 return false; // EIO (child gone) or other hard error
5514 }
5515 written += (uint32)n;
5516 }
5517 return true;
5518#endif
5519}
5520
5521bool ResizeProcessTerminal(uint32 proc, uint16 cols, uint16 rows) {
5522#if defined(WINDOWS)
5523 (void)proc; (void)cols; (void)rows;
5524 return false; // stub until ConPTY (2.7e)
5525#else
5526 int fd = -1;
5527 if (!TerminalMasterFind(proc, fd)) return false;
5528 struct winsize ws;
5529 memset(&ws, 0, sizeof(ws));
5530 ws.ws_col = cols ? cols : 80;
5531 ws.ws_row = rows ? rows : 25;
5532 if (ioctl(fd, TIOCSWINSZ, &ws) != 0) return false;
5533 kill(-(pid_t)proc, SIGWINCH); // child leads its own group (setsid)
5534 return true;
5535#endif
5536}
5537
5538void CloseProcessTerminal(uint32 proc) {
5539#if defined(WINDOWS)
5540 (void)proc; // stub until ConPTY (2.7e)
5541#else
5542 if (!TerminalMasterMap) return;
5543 std::map<uint32, int>::iterator i = TerminalMasterMap->find(proc);
5544 if (i == TerminalMasterMap->end()) return;
5545 close(i->second); // hangup: child's foreground group gets SIGHUP
5546 TerminalMasterMap->erase(i);
5547#endif
5548}
5549
5550bool SendProcessBreak(uint32 proc, int sig) {
5551 #if defined WINDOWS
5552 (void)sig;
5553 // Requires the child to have been created with CREATE_NEW_PROCESS_GROUP.
5554 // The group id equals the child's process id.
5555 return (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, (DWORD)proc) != 0);
5556 #else
5557 return (kill((pid_t)proc, sig) == 0);
5558 #endif
5559}
5560
5561uint8 WaitForProcess(uint32 proc, uint32 timeout, int &returncode) {
5562 #if defined WINDOWS
5563 DWORD exitcode;
5564 std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
5565 if (i != ProcessInformationMap->end()) {
5566 ProcessData* pData = i->second;
5567 if (WaitForSingleObject(pData->procInfo.hProcess, timeout) == WAIT_OBJECT_0) {
5568 if (GetExitCodeProcess(pData->procInfo.hProcess, &exitcode) && (exitcode == STILL_ACTIVE)) {
5569 return PROC_RUNNING;
5570 }
5571 else {
5572 ProcessInformationMap->erase(i);
5573 CloseHandle(pData->procInfo.hProcess);
5574 CloseHandle(pData->procInfo.hThread);
5575 if (pData->hOutRead) CloseHandle(pData->hOutRead);
5576 if (pData->hErrRead) CloseHandle(pData->hErrRead);
5577 if (pData->hInWrite) CloseHandle(pData->hInWrite); // 2.7c stdin pipe
5578 if (pData->hJob) CloseHandle(pData->hJob); // 2.7c per-child job
5579 delete pData;
5580 returncode = exitcode;
5581 return PROC_TERMINATED;
5582 }
5583 }
5584 return PROC_TIMEOUT;
5585 }
5586 else {
5587 HANDLE hproc = OpenProcess(NULL, NULL, (DWORD)proc);
5588 if (!hproc) {
5589 return PROC_ERROR;
5590 }
5591 if (WaitForSingleObject(hproc, timeout) == WAIT_OBJECT_0) {
5592 if (GetExitCodeProcess(hproc, &exitcode) && (exitcode == STILL_ACTIVE)) {
5593 CloseHandle(hproc);
5594 return PROC_RUNNING;
5595 }
5596 else {
5597 returncode = exitcode;
5598 CloseHandle(hproc);
5599 return PROC_TERMINATED;
5600 }
5601 }
5602 else {
5603 CloseHandle(hproc);
5604 return PROC_RUNNING;
5605 }
5606 }
5607 #else
5608 uint64 start = GetTimeNow();
5609 do {
5610 if (GetProcessStatus(proc, returncode) < PROC_RUNNING)
5611 return PROC_TERMINATED;
5612 utils::Sleep(10);
5613 } while (GetTimeAgeMS(start) < timeout);
5614 return PROC_RUNNING;
5615
5616 #endif
5617}
5618
5619
5620
5621
5622
5623
5624
5625
5626
5628// DLL Libraries //
5630
5631bool SetCommandLine(int argc, char* argv[]) {
5632
5635
5636 std::string key;
5637 const char* val;
5638 int32 n;
5639 CommandLineInfo::CommandLineInfoSingleton->CommandLineString = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec = argv[0];
5640 CommandLineInfo::CommandLineInfoSingleton->CommandLineItems.push_back(argv[0]);
5641 for (n=1; n<argc; n++) {
5642 CommandLineInfo::CommandLineInfoSingleton->CommandLineItems.push_back(argv[n]);
5643 key = argv[n];
5644 val = strchr(argv[n], '=');
5645 if (val)
5646 CommandLineInfo::CommandLineInfoSingleton->CommandLineArgs[key.substr(0, val-argv[n])] = val+1;
5647 CommandLineInfo::CommandLineInfoSingleton->CommandLineString += ' ';
5648 CommandLineInfo::CommandLineInfoSingleton->CommandLineString += argv[n];
5649 }
5650
5651 //printf("Full: '%s'\n", CommandLineString.c_str());
5652 //printf("Base: '%s'\n", CommandLineExec.c_str());
5653
5654 //n = 0;
5655 //std::vector<std::string>::iterator it = CommandLineItems.begin(), itEnd = CommandLineItems.end();
5656 //while (it != itEnd) {
5657 // printf("[%u] '%s'\n", n++, (*it).c_str());
5658 // it++;
5659 //}
5660
5661 //n = 0;
5662 //std::map<std::string, std::string>::iterator mit = CommandLineArgs.begin(), mitEnd = CommandLineArgs.end();
5663 //while (mit != mitEnd) {
5664 // printf("[%u] '%s' = '%s'\n", n++, mit->first.c_str(), mit->second.c_str());
5665 // mit++;
5666 //}
5667
5668 //printf("Executable: '%s' '%s'\n", GetCommandLinePath().c_str(), GetCommandLineExecutableOnly().c_str());
5669 return true;
5670}
5671
5672std::string GetCommandLine() {
5674 return "";
5675 return CommandLineInfo::CommandLineInfoSingleton->CommandLineString;
5676}
5677
5678std::string GetCommandLinePath() {
5680 return "";
5681 std::string::size_type p1 = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.find_last_of('/');
5682 std::string::size_type p2 = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.find_last_of('\\');
5683 if (p1 == std::string::npos) {
5684 if (p2 == std::string::npos)
5685 return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec;
5686 else
5687 return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(0, p2+1);
5688 }
5689 else if (p2 == std::string::npos)
5690 return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(0, p1+1);
5691 else
5692 return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(0, (p1 > p2) ? p1+1 : p2+1);
5693}
5694
5697 return "";
5698 return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec;
5699}
5700
5703 return "";
5704 std::string::size_type p1 = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.find_last_of('/');
5705 std::string::size_type p2 = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.find_last_of('\\');
5706 if (p1 == std::string::npos) {
5707 if (p2 == std::string::npos)
5708 return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec;
5709 else
5710 return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(p2+1);
5711 }
5712 else if (p2 == std::string::npos)
5713 return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(p1+1);
5714 else
5715 return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr((p1 > p2) ? p1+1 : p2+1);
5716}
5717
5720 return 0;
5721 return (uint32)CommandLineInfo::CommandLineInfoSingleton->CommandLineItems.size();
5722}
5723
5724std::string GetCommandLineArg(uint16 n) {
5726 return "";
5727 return CommandLineInfo::CommandLineInfoSingleton->CommandLineItems[n];
5728}
5729
5730std::string GetCommandLineArg(const char* key) {
5732 return "";
5733 return CommandLineInfo::CommandLineInfoSingleton->CommandLineArgs[key];
5734}
5735
5736bool HasCommandLineArg(const char* key, std::string& value) {
5738 return false;
5739 std::map<std::string, std::string>::const_iterator it =
5740 CommandLineInfo::CommandLineInfoSingleton->CommandLineArgs.find(key);
5741 if (it == CommandLineInfo::CommandLineInfoSingleton->CommandLineArgs.end())
5742 return false;
5743 value = it->second;
5744 return true;
5745}
5746
5747
5748
5750 handle = NULL;
5751}
5753 if (handle) {
5754 #ifdef WINDOWS
5755 FreeLibrary(handle);
5756 #else // WINDOWS
5757 // dlclose() bug: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=28625
5758 // __dso_handle unreferenced
5759 // if there are any globally desclared objects in the library!!!
5760 dlclose(handle);
5761 #endif // WINDOWS
5762 }
5763}
5764
5765std::string Library::patchLibraryFilename(const char* filename, const char* path) {
5766 #ifdef WINDOWS
5767 return filename;
5768 #else
5769 std::string realLibName;
5770 if (utils::TextEndsWith(filename, ".so"))
5771 realLibName = filename;
5772 else
5773 realLibName = utils::StringFormat("%s.so", filename);
5774
5775 int32 filenameStart;
5776 if ( (filenameStart = realLibName.find_last_of('/') == std::string::npos ))
5777 filenameStart = 0;
5778 else
5779 filenameStart++;
5780
5781 if (!utils::TextStartsWith(filename + filenameStart, "lib", true))
5782 realLibName.insert(filenameStart, "lib");
5783
5784 if (!filenameStart) {
5785 if (!path)
5786 realLibName.insert(0, "./");
5787 else
5788 realLibName.insert(0, path);
5789 }
5790
5791 return realLibName;
5792 #endif
5793}
5794
5795bool Library::load(const char* filename) {
5796 if (!filename)
5797 return false;
5798 std::string errorText;
5799 //printf("Trying to find library: '%s'...\n", filename);
5800
5801 #ifdef WINDOWS
5802 LPVOID lpMsgBuf; //Message Buffer
5803 #ifdef _DEBUG
5804 handle = LoadLibrary(utils::StringFormat("%sDebug", filename).c_str());
5805 if (handle == NULL) {
5806 FormatMessage(
5807 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
5808 NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
5809 (LPTSTR)&lpMsgBuf, 0, NULL);
5810 LogPrint(0, LOG_SYSTEM, 0, "Could not find or load debug library '%sDebug': %s", filename, lpMsgBuf);
5811 LocalFree(lpMsgBuf);
5812 return false;
5813 }
5814 #else
5815 handle = LoadLibrary(filename);
5816 if (handle == NULL) {
5817 FormatMessage(
5818 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
5819 NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
5820 (LPTSTR) &lpMsgBuf, 0, NULL);
5821 LogPrint(0, LOG_SYSTEM, 0, "Could not find or load library '%s': %s", filename, lpMsgBuf);
5822 LocalFree(lpMsgBuf);
5823 return false;
5824 }
5825 #endif
5826 #else // WINDOWS
5827 handle = 0;
5828 const char* dlErrorText;
5829 std::string realLibName = patchLibraryFilename(filename);
5830 //printf("Trying patched library: '%s'...\n", realLibName.c_str());
5831
5832 // clear errors
5833 dlerror();
5834 #ifdef _DEBUG
5835 std::string debugName = realLibName;
5836 utils::StringSingleReplace(debugName, ".so", "Debug.so", true);
5837 // printf("Trying debug library: '%s'...\n", debugName.c_str());
5838 handle = dlopen(debugName.c_str(), RTLD_NOW | RTLD_GLOBAL);
5839 if (!handle) {
5840 dlErrorText = dlerror();
5841 // printf("Error 1: '%s'...\n", dlErrorText);
5842 if (dlErrorText && strlen(dlErrorText) &&
5843 !(strstr(dlErrorText, "No such file") && strstr(dlErrorText, debugName.c_str())))
5844 errorText = dlErrorText;
5845 }
5846 #endif
5847
5848 if (!handle && !errorText.size()) {
5849 // handle = dlopen(realLibName.c_str(), RTLD_NOW | RTLD_GLOBAL);
5850 if (!handle) {
5851 dlErrorText = dlerror();
5852 // printf("Error 2: '%s'...\n", dlErrorText);
5853 if (dlErrorText && strlen(dlErrorText) &&
5854 !(strstr(dlErrorText, "No such file") && strstr(dlErrorText, realLibName.c_str())))
5855 errorText = dlErrorText;
5856 }
5857 }
5858
5859 if (!handle && !errorText.size() && !strchr(filename, '/')) {
5860 realLibName = patchLibraryFilename(filename, utils::GetCommandLinePath().c_str());
5861 // printf("Trying library: '%s'...\n", realLibName.c_str());
5862 handle = dlopen(realLibName.c_str(), RTLD_NOW | RTLD_GLOBAL);
5863 if (!handle) {
5864 dlErrorText = dlerror();
5865 // printf("Error 3: '%s'...\n", dlErrorText);
5866 if (dlErrorText && strlen(dlErrorText) &&
5867 !(strstr(dlErrorText, "No such file") && strstr(dlErrorText, realLibName.c_str())))
5868 errorText = dlErrorText;
5869 }
5870 }
5871
5872 if (handle == NULL) {
5873 if (errorText.size())
5874 LogPrint(0, LOG_SYSTEM, 0, "Could not load library file '%s': %s", realLibName.c_str(), errorText.c_str());
5875 else
5876 LogPrint(0, LOG_SYSTEM, 0, "Could not find library file '%s'", filename);
5877 return false;
5878 }
5879 #endif // WINDOWS
5880 return true;
5881}
5882
5884 if (handle == NULL)
5885 return NULL;
5886 if (!strlen(funcName))
5887 return NULL;
5888
5889 LibraryFunction func;
5890
5891 #ifdef WINDOWS
5892 func = (LibraryFunction)GetProcAddress(handle, funcName);
5893 if (func == NULL) {
5894 LPVOID lpMsgBuf; //Message Buffer
5895 //Generate Error Message from GetLastError
5896 FormatMessage(
5897 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
5898 NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
5899 (LPTSTR) &lpMsgBuf, 0, NULL);
5900 LogPrint(0, LOG_SYSTEM, 0, "Could not find library function: %s: %s", funcName, lpMsgBuf);
5901 // Free the buffer.
5902 LocalFree(lpMsgBuf);
5903 return NULL;
5904 }
5905 #else // WINDOWS
5906 const char* errmsg;
5907 dlerror();
5908 func = (LibraryFunction)dlsym(handle, funcName);
5909 errmsg = dlerror();
5910 if (errmsg != NULL) {
5911 LogPrint(0, LOG_SYSTEM, 0, "Could not find library function: %s: %s", (char*) funcName, (char*) errmsg);
5912 return NULL;
5913 }
5914 #endif // WINDOWS
5915 return func;
5916}
5917
5918Library* OpenLibrary(const char* libName) {
5919 Library* lib = new Library();
5920 if (!lib->load(libName)) {
5921 delete(lib);
5922 return NULL;
5923 }
5924 return lib;
5925}
5926
5927
5929// OS System //
5931
5932char OSLocalHostName[1024] = {0};
5933char OSArchitectureName[1024] = {0};
5934char OSName[1024] = {0};
5935
5936const char* GetComputerName() {
5937 //char name[1024];
5938 char* name = new char[1024];
5939 if (strlen(OSLocalHostName) == 0) {
5940 #ifdef WINDOWS
5941
5942 DWORD size = 1024;
5943 #ifdef POCKETPC
5944 delete [] name;
5945 return "PocketPC";
5946 #else
5947 if (::GetComputerName(name, &size) == 0) {
5948 delete [] name;
5949 return "";
5950 }
5951 else
5952 utils::strcpyavail(OSLocalHostName, name, 1024, true);
5953 #endif
5954
5955 //if (gethostname(name, 1024) == 0)
5956 // strcpy(OSLocalHostName, name);
5957 //else {
5958 // int er = WSAGetLastError();
5959 // if (er == WSANOTINITIALISED) {
5960 // WSADATA info;
5961 // if (WSAStartup(MAKEWORD(1,1), &info) == 0) {
5962 // // Now we can retry the socket() function
5963 // if (gethostname(hname, 1000) == 0)
5964 // strcpy(OSLocalHostName, name);
5965 // }
5966 // }
5967 //}
5968 #else
5969 std::string outString, errString;
5970 if (!RunOSCommand("uname -n", NULL, 1000, outString, errString)) {
5971 //if (!RunOSTextCommand("uname -n", name, size))
5972 delete [] name;
5973 return "";
5974 }
5975 else
5976 utils::strcpyavail(OSLocalHostName, outString.c_str(), 1024, true);
5977 #endif // WINDOWS
5978 }
5979 delete [] name;
5980 return OSLocalHostName;
5981}
5982
5984
5985 #if defined WINDOWS
5986 HANDLE hProcess = GetCurrentProcess();
5987 PROCESS_MEMORY_COUNTERS pmc;
5988 if (hProcess == NULL)
5989 return 0;
5990 if ( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)) != 0)
5991 return (uint64)pmc.WorkingSetSize;
5992 else
5993 return 0;
5994 #elif defined OSX
5995 struct task_basic_info_64 info;
5996 mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT;
5997 if (task_info(mach_task_self(), TASK_BASIC_INFO_64, (task_info_t)&info, &count) == KERN_SUCCESS)
5998 return (uint64)info.resident_size;
5999 return 0;
6000 #else
6001 /* Linux ---------------------------------------------------- */
6002 long rss = 0L;
6003 FILE* fp = NULL;
6004 if ( (fp = fopen( "/proc/self/statm", "r" )) == NULL )
6005 return (uint64)0L; /* Can't open? */
6006 if ( fscanf( fp, "%*s%ld", &rss ) != 1 ) {
6007 fclose( fp );
6008 return (uint64)0L; /* Can't read? */
6009 }
6010 fclose( fp );
6011 return (uint64)rss * (uint64)sysconf( _SC_PAGESIZE);
6012 #endif
6013}
6014
6016
6017 #if defined WINDOWS
6018 HANDLE hProcess = GetCurrentProcess();
6019 PROCESS_MEMORY_COUNTERS pmc;
6020 if (hProcess == NULL)
6021 return 0;
6022 if ( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)) != 0)
6023 return (uint64)pmc.PeakWorkingSetSize;
6024 else
6025 return 0;
6026 #elif defined OSX
6027 // macOS/BSD: ru_maxrss is in bytes (Linux uses KB)
6028 struct rusage usage;
6029 getrusage(RUSAGE_SELF, &usage);
6030 return (uint64)usage.ru_maxrss;
6031 #else
6032 // Linux: ru_maxrss is in KB
6033 struct rusage usage;
6034 getrusage( RUSAGE_SELF, &usage );
6035 return ((uint64)usage.ru_maxrss) * 1024L;
6036
6037
6039 //struct psinfo psinfo;
6040 //int fd = -1;
6041 //if ( (fd = open( "/proc/self/psinfo", O_RDONLY )) == -1 )
6042 // return (size_t)0L; /* Can't open? */
6043 //if ( read( fd, &psinfo, sizeof(psinfo) ) != sizeof(psinfo) )
6044 //{
6045 // close( fd );
6046 // return (size_t)0L; /* Can't read? */
6047 //}
6048 //close( fd );
6049 //return (size_t)(psinfo.pr_rssize * 1024L);
6050
6051 #endif
6052}
6053
6054
6055uint16 GetCPUCount() {
6056 #if defined WINDOWS
6057 SYSTEM_INFO siSysInfo;
6058 GetSystemInfo(&siSysInfo);
6059 return (uint16) siSysInfo.dwNumberOfProcessors;
6060 #elif defined OSX
6061 int ncpu = 0;
6062 size_t len = sizeof(ncpu);
6063 if (sysctlbyname("hw.ncpu", &ncpu, &len, NULL, 0) == 0 && ncpu > 0)
6064 return (uint16)(ncpu > 65535 ? 65535 : ncpu);
6065 return 0;
6066 #else
6067 long n = sysconf(_SC_NPROCESSORS_ONLN);
6068 if (n > 0)
6069 return (uint16)(n > 65535 ? 65535 : (uint16)n);
6070 return 0;
6071 #endif
6072}
6073
6075 #if defined WINDOWS
6076 SYSTEM_INFO siSysInfo;
6077 GetSystemInfo(&siSysInfo);
6078 switch(siSysInfo.wProcessorArchitecture) {
6079 case PROCESSOR_ARCHITECTURE_AMD64:
6080 return OSCPU_AMD64;
6081 case PROCESSOR_ARCHITECTURE_IA64:
6082 return OSCPU_IA64;
6083 case PROCESSOR_ARCHITECTURE_INTEL:
6084 return OSCPU_X86;
6085 default:
6086 return OSCPU_UNKNOWN;
6087 }
6088 #elif defined OSX
6089 #if defined(__x86_64__)
6090 return OSCPU_AMD64;
6091 #elif defined(__i386__)
6092 return OSCPU_X86;
6093 #elif defined(__aarch64__)
6094 return OSCPU_UNKNOWN;
6095 #else
6096 return OSCPU_UNKNOWN;
6097 #endif
6098 #else
6099 #if defined(__x86_64__)
6100 return OSCPU_AMD64;
6101 #elif defined(__i386__)
6102 return OSCPU_X86;
6103 #elif defined(__aarch64__)
6104 return OSCPU_UNKNOWN;
6105 #else
6106 return OSCPU_UNKNOWN;
6107 #endif
6108 #endif
6109}
6110
6111uint64 GetCPUSpeed() {
6112 #if defined WINDOWS
6113 //HKEY hKey;
6114 //LONG lRet;
6115
6116 // Test for SP6 versus SP6a.
6117 //JString key = "HARDWARE\\DESCRIPTION\\SYSTEM\\CentralProcessor\\0";
6118 //lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE, key, 0, KEY_QUERY_VALUE, &hKey );
6119 //if( lRet == ERROR_SUCCESS ) {
6120 // DWORD len = sizeof(int);
6121 // BYTE data[sizeof(int)];
6122 // key = "~MHz";
6123 // lRet = RegQueryValueEx(hKey, key, NULL, NULL, data, &len);
6124 // if (lRet == ERROR_SUCCESS) {
6125 // RegCloseKey(hKey);
6126 // return (uint64) (data[3] << 24) + (data[2] << 16) + (data[1] << 8) + (data[0] << 0);
6127 // }
6128 // else {
6129 // RegCloseKey(hKey);
6130 // return 0;
6131 // }
6132 //}
6133 #elif defined OSX
6134 uint64 freq = 0;
6135 size_t len = sizeof(freq);
6136 if (sysctlbyname("hw.cpufrequency", &freq, &len, NULL, 0) == 0)
6137 return freq;
6138 return 0;
6139 #else
6140 // Linux: optional /proc/cpuinfo cpu MHz
6141 std::ifstream cpuinfo("/proc/cpuinfo");
6142 std::string line;
6143 while (cpuinfo && getline(cpuinfo, line)) {
6144 if (line.compare(0, 7, "cpu MHz") == 0) {
6145 size_t colon = line.find(':');
6146 if (colon != std::string::npos) {
6147 double mhz = 0;
6148 if (sscanf(line.c_str() + colon + 1, "%lf", &mhz) == 1 && mhz > 0)
6149 return (uint64)(mhz * 1000000.0);
6150 }
6151 break;
6152 }
6153 }
6154 #endif
6155 return 0;
6156}
6157
6159 #if defined WINDOWS
6160 MEMORYSTATUS memstat;
6161 GlobalMemoryStatus(&memstat);
6162 return (uint64)memstat.dwTotalPhys;
6163 #else
6164 #ifdef __APPLE__
6165 uint64 memsize = 0;
6166 size_t len = sizeof(memsize);
6167 if (sysctlbyname("hw.memsize", &memsize, &len, NULL, 0) == 0)
6168 return memsize;
6169 return 0;
6170 #else
6171 struct sysinfo info;
6172 if (sysinfo(&info) == 0)
6173 return info.totalram;
6174 else
6175 return 0;
6176 #endif
6177 #endif
6178 return 0;
6179}
6180
6181bool GetSystemMemoryUsage(uint64 &totalRAM, uint64 &freeRAM) {
6182 #if defined WINDOWS
6183 MEMORYSTATUSEX memstat;
6184 memset(&memstat, 0, sizeof(MEMORYSTATUSEX));
6185 memstat.dwLength = sizeof(MEMORYSTATUSEX);
6186 if (!GlobalMemoryStatusEx(&memstat)) {
6187 int a = GetLastError();
6188 return false;
6189 }
6190 totalRAM = (uint64)memstat.ullTotalPhys;
6191 freeRAM = (uint64)memstat.ullAvailPhys;
6192 return true;
6193 #else
6194 #ifdef __APPLE__
6195 uint64 memsize = 0;
6196 size_t len = sizeof(memsize);
6197 if (sysctlbyname("hw.memsize", &memsize, &len, NULL, 0) != 0)
6198 return false;
6199 totalRAM = memsize;
6200 vm_statistics64_data_t vm_stat;
6201 mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
6202 if (host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t)&vm_stat, &count) != KERN_SUCCESS)
6203 return false;
6204 vm_size_t page_size;
6205 if (host_page_size(mach_host_self(), &page_size) != KERN_SUCCESS)
6206 return false;
6207 freeRAM = (uint64)vm_stat.free_count * (uint64)page_size;
6208 return true;
6209 #else
6210 struct sysinfo info;
6211 if (sysinfo(&info) == 0) {
6212 totalRAM = info.totalram;
6213 freeRAM = info.freeram;
6214 return true;
6215 }
6216 else
6217 return false;
6218 #endif
6219 #endif
6220}
6221
6223 if (strlen(OSArchitectureName) == 0) {
6224 #if defined WINDOWS
6225 SYSTEM_INFO siSysInfo;
6226 GetSystemInfo(&siSysInfo);
6227
6228 switch(siSysInfo.wProcessorArchitecture) {
6229 case PROCESSOR_ARCHITECTURE_UNKNOWN:
6230 utils::strcpyavail(OSArchitectureName, "i386", 1024, true);
6231 case PROCESSOR_ARCHITECTURE_INTEL:
6232 switch(siSysInfo.wProcessorLevel) {
6233 case 3:
6234 utils::strcpyavail(OSArchitectureName, "i386", 1024, true);
6235 break;
6236 case 4:
6237 utils::strcpyavail(OSArchitectureName, "i486", 1024, true);
6238 break;
6239 case 5:
6240 utils::strcpyavail(OSArchitectureName, "i586", 1024, true);
6241 break;
6242 case 6:
6243 utils::strcpyavail(OSArchitectureName, "i686", 1024, true);
6244 break;
6245 case 7:
6246 utils::strcpyavail(OSArchitectureName, "i786", 1024, true);
6247 break;
6248 case 8:
6249 utils::strcpyavail(OSArchitectureName, "i886", 1024, true);
6250 break;
6251 case 9:
6252 utils::strcpyavail(OSArchitectureName, "i986", 1024, true);
6253 break;
6254 default:
6255 utils::strcpyavail(OSArchitectureName, "i686", 1024, true);
6256 break;
6257 }
6258 case PROCESSOR_ARCHITECTURE_MIPS:
6259 utils::strcpyavail(OSArchitectureName, "mips", 1024, true);
6260 break;
6261 case PROCESSOR_ARCHITECTURE_ALPHA:
6262 utils::strcpyavail(OSArchitectureName, "alpha", 1024, true);
6263 break;
6264 case PROCESSOR_ARCHITECTURE_PPC:
6265 utils::strcpyavail(OSArchitectureName, "ppc", 1024, true);
6266 break;
6267 case PROCESSOR_ARCHITECTURE_IA64:
6268 utils::strcpyavail(OSArchitectureName, "ia64", 1024, true);
6269 break;
6270 case PROCESSOR_ARCHITECTURE_AMD64:
6271 utils::strcpyavail(OSArchitectureName, "amd64", 1024, true);
6272 break;
6273 default:
6274 break;
6275 }
6276 #else
6277 // ######################################
6278 #endif
6279 }
6280 return OSArchitectureName;
6281}
6282
6283const char* GetSystemOSName() {
6284 if (strlen(OSName) == 0) {
6285 #if defined WINDOWS
6286 #ifdef POCKETPC
6287 utils::strcpyavail(OSName, "Microsoft PocketPC", 1024, true);
6288 #else
6289 switch(GetCPUArchitecture()) {
6290 case OSCPU_AMD64:
6291 utils::strcpyavail(OSName, "Microsoft Windows X64", 1024, true);
6292 break;
6293 case OSCPU_IA64:
6294 utils::strcpyavail(OSName, "Microsoft Windows IA64", 1024, true);
6295 break;
6296 case OSCPU_X86:
6297 utils::strcpyavail(OSName, "Microsoft Windows X86", 1024, true);
6298 break;
6299 default:
6300 utils::strcpyavail(OSName, "Microsoft Windows", 1024, true);
6301 break;
6302 }
6303 #endif
6304 #else
6305 // ######################################
6306 #endif
6307 }
6308 return OSName;
6309}
6310
6311bool GetSystemOSVersion(uint16& major, uint16& minor, uint16& build, char* text, uint16 textSize) {
6312 major = 0;
6313 minor = 0;
6314 build = 0;
6315 text[0] = 0;
6316 #if defined WINDOWS
6317 #ifdef POCKETPC
6318
6319 OSVERSIONINFO osvi;
6320 BOOL bOsVersionInfoEx;
6321
6322 // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
6323 // If that fails, try using the OSVERSIONINFO structure.
6324
6325 ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
6326 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
6327
6328 if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) )
6329 {
6330 osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
6331 if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) )
6332 return false;
6333 }
6334
6335 major = osvi.dwMajorVersion;
6336 minor = osvi.dwMinorVersion;
6337 if (strlen((char*)osvi.szCSDVersion) < textSize)
6338 utils::strcpyavail(text, (char*)osvi.szCSDVersion, textSize, true);
6339 #else
6340
6341 OSVERSIONINFOEX osvi;
6342 BOOL bOsVersionInfoEx;
6343
6344 // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
6345 // If that fails, try using the OSVERSIONINFO structure.
6346
6347 ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
6348 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
6349
6350 if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) )
6351 {
6352 osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
6353 if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) )
6354 return false;
6355 }
6356
6357 switch (osvi.dwPlatformId)
6358 {
6359 // Test for the Windows NT product family.
6360 case VER_PLATFORM_WIN32_NT:
6361
6362 // Display service pack (if any) and build number.
6363
6364 if( osvi.dwMajorVersion == 4 &&
6365 lstrcmpi( osvi.szCSDVersion, "Service Pack 6" ) == 0 )
6366 {
6367 HKEY hKey;
6368 LONG lRet;
6369
6370 // Test for SP6 versus SP6a.
6371 lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
6372 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009",
6373 0, KEY_QUERY_VALUE, &hKey );
6374 if( lRet == ERROR_SUCCESS ) {
6375 if (textSize > 16)
6376 utils::strcpyavail(text, "Service Pack 6a", textSize, true);
6377 build = osvi.dwBuildNumber & 0xFFFF;
6378 }
6379 else // Windows NT 4.0 prior to SP6a
6380 {
6381 major = (uint16)osvi.dwMajorVersion;
6382 minor = (uint16)osvi.dwMinorVersion;
6383 build = (uint16)osvi.dwBuildNumber & 0xFFFF;
6384 if (strlen((char*)osvi.szCSDVersion) < textSize)
6385 utils::strcpyavail(text, (char*)osvi.szCSDVersion, textSize, true);
6386 }
6387 RegCloseKey( hKey );
6388 }
6389 else // Windows NT 3.51 and earlier or Windows 2000 and later
6390 {
6391 major = (uint16)osvi.dwMajorVersion;
6392 minor = (uint16)osvi.dwMinorVersion;
6393 build = (uint16)osvi.dwBuildNumber & 0xFFFF;
6394
6395 if (strlen((char*)osvi.szCSDVersion) < textSize)
6396 utils::strcpyavail(text, (char*)osvi.szCSDVersion, textSize, true);
6397 }
6398 break;
6399 }
6400
6401 #endif
6402 #else
6403 // ######################################
6404 #endif
6405 return true;
6406}
6407
6408//bool RunOSTextCommand(const char* cmd, char** result, uint32 size) {
6409//
6410// #ifdef WINDOWS
6411// // Use RunOSCommand instead
6412// return false;
6413// #else
6414// fflush(stdin);
6415// fflush(stdout);
6416// FILE* runfile = popen(cmd, "r");
6417// if (runfile == NULL)
6418// return false;
6419//
6420// int res;
6421// int size = 4096;
6422// char buffer[size+1];
6423// std::string output;
6424//
6425// res = fread(buffer, 1, size, runfile);
6426// if (res <= 0) {
6427// pclose(runfile);
6428// return false;
6429// }
6430// do {
6431// buffer[res] = 0;
6432// output += buffer;
6433// } while (res = fread(buffer, 1, size, runfile));
6434//
6435// pclose(runfile);
6436// delete [] buffer;
6437// *result = new char[output.length()+1];
6438// memcpy(*result, output.c_str(), output.length());
6439// result[output.length()] = 0;
6440// return true;
6441// #endif
6442//}
6443
6444//bool GetSystemName(uint32 id, const char* title, char* name, uint32 size) {
6445// if ( (name == NULL) || (size < 64) )
6446// return false;
6447// memset(name, 0, 64);
6448// uint32 pos = 0;
6449// #ifdef WINDOWS
6450// strcpy_s(name, size, "Global\\");
6451// strcpy_s(name+(pos = (uint32)strlen(name)), size-pos, title);
6452// // _itoa_s(id, name+(pos = (uint32)strlen(name)), size-pos, 16);
6453// #else
6454// strncpy(name, "/", size);
6455// pos = (uint32)strlen(name);
6456// strncpy(name+pos, title, size-pos);
6457// #endif
6458// Int2Ascii((int32)id, name+(pos = (uint32)strlen(name)), size-pos, 16);
6459// return true;
6460//}
6461
6462char* Int2Ascii(int64 value, char* result, uint16 size, uint8 base) {
6463 if (base < 2 || base > 36) {
6464 result[0] = 0;
6465 return result;
6466 }
6467
6468 char* ptr = result, *ptr1 = result, tmp_char;
6469 int64 tmp_value;
6470
6471 do {
6472 tmp_value = value;
6473 value /= base;
6474 *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)];
6475 } while ( value );
6476
6477 // Apply negative sign
6478 if (tmp_value < 0)
6479 *ptr++ = '-';
6480 *ptr-- = '\0';
6481 while(ptr1 < ptr) {
6482 tmp_char = *ptr;
6483 *ptr--= *ptr1;
6484 *ptr1++ = tmp_char;
6485 }
6486 return result;
6487}
6488
6489char* Uint2Ascii(uint64 value, char* result, uint16 size, uint8 base) {
6490 if (base < 2 || base > 36) {
6491 result[0] = 0;
6492 return result;
6493 }
6494
6495 char* ptr = result, *ptr1 = result, tmp_char;
6496 int64 tmp_value;
6497
6498 do {
6499 tmp_value = value;
6500 value /= base;
6501 *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)];
6502 } while ( value );
6503
6504 *ptr-- = '\0';
6505 while(ptr1 < ptr) {
6506 tmp_char = *ptr;
6507 *ptr--= *ptr1;
6508 *ptr1++ = tmp_char;
6509 }
6510 return result;
6511}
6512
6513unsigned char* Ascii2UTF16LE(const char* ascii, uint32 len, uint32& size) {
6514 size = (len * 2) + 4;
6515 unsigned char* result = new unsigned char[size];
6516 result[0] = 0xff;
6517 result[1] = 0xfe;
6518 uint32 i = 2;
6519 const char* src = ascii;
6520 for (uint32 n = 0; n < len; n++) {
6521 result[i++] = *src;
6522 result[i++] = 0;
6523 src++;
6524 }
6525 result[i++] = 0;
6526 result[i++] = 0;
6527 size = (len * 2) + 2;
6528 return result;
6529}
6530
6531
6532bool GetSocketError(int error, char* errorString, uint16 errorStringMaxSize, bool* isRecoverable) {
6533
6534 if (errorStringMaxSize < 128)
6535 return false;
6536
6537 #ifdef WINDOWS
6538
6539 if (error == WSANOTINITIALISED) {
6540 utils::strcpyavail(errorString, "Cannot initialize WinSock!", errorStringMaxSize, true);
6541 *isRecoverable = false;
6542 }
6543 else if (error == WSAENETDOWN) {
6544 utils::strcpyavail(errorString, "The network subsystem or the associated service provider has failed", errorStringMaxSize, true);
6545 *isRecoverable = false;
6546 }
6547 else if (error == WSAEAFNOSUPPORT) {
6548 utils::strcpyavail(errorString, "The specified address family is not supported", errorStringMaxSize, true);
6549 *isRecoverable = false;
6550 }
6551 else if (error == WSAEINPROGRESS) {
6552 utils::strcpyavail(errorString, "A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function", errorStringMaxSize, true);
6553 *isRecoverable = true;
6554 }
6555 else if (error == WSAEMFILE) {
6556 utils::strcpyavail(errorString, "No more socket descriptors are available", errorStringMaxSize, true);
6557 *isRecoverable = false;
6558 }
6559 else if (error == WSAENOBUFS) {
6560 utils::strcpyavail(errorString, "No buffer space is available. The socket cannot be created", errorStringMaxSize, true);
6561 *isRecoverable = false;
6562 }
6563 else if (error == WSAEPROTONOSUPPORT) {
6564 utils::strcpyavail(errorString, "The specified protocol is not supported", errorStringMaxSize, true);
6565 *isRecoverable = false;
6566 }
6567 else if (error == WSAEPROTOTYPE) {
6568 utils::strcpyavail(errorString, "The specified protocol is the wrong type for this socket", errorStringMaxSize, true);
6569 *isRecoverable = false;
6570 }
6571 else if (error == WSAESOCKTNOSUPPORT) {
6572 utils::strcpyavail(errorString, "The specified socket type is not supported in this address family", errorStringMaxSize, true);
6573 *isRecoverable = false;
6574 }
6575 else if (error == WSAEADDRINUSE) {
6576 utils::strcpyavail(errorString, "The socket's local address is already in use and the socket was not marked to allow address reuse with SO_REUSEADDR. This error usually occurs during execution of the bind function, but could be delayed until this function if the bind was to a partially wildcard address (involving ADDR_ANY) and if a specific address needs to be committed at the time of this function", errorStringMaxSize, true);
6577 *isRecoverable = false;
6578 }
6579 else if (error == WSAEINVAL) {
6580 utils::strcpyavail(errorString, "The socket has not been bound with bind", errorStringMaxSize, true);
6581 *isRecoverable = false;
6582 }
6583 else if (error == WSAEISCONN) {
6584 utils::strcpyavail(errorString, "The socket is already connected", errorStringMaxSize, true);
6585 *isRecoverable = false;
6586 }
6587 else if (error == WSAENOTSOCK) {
6588 utils::strcpyavail(errorString, "The descriptor is not a socket", errorStringMaxSize, true);
6589 *isRecoverable = false;
6590 }
6591 else if (error == WSAEOPNOTSUPP) {
6592 utils::strcpyavail(errorString, "The referenced socket is not of a type that supports the listen operation", errorStringMaxSize, true);
6593 *isRecoverable = false;
6594 }
6595 else if (error == WSAEADDRNOTAVAIL) {
6596 utils::strcpyavail(errorString, "The specified address is not a valid address for this machine", errorStringMaxSize, true);
6597 *isRecoverable = false;
6598 }
6599 else if (error == WSAEFAULT) {
6600 utils::strcpyavail(errorString, "The name or namelen parameter is not a valid part of the user address space", errorStringMaxSize, true);
6601 *isRecoverable = false;
6602 }
6603 else if (error == WSAEMFILE) {
6604 utils::strcpyavail(errorString, "The queue is nonempty upon entry to accept and there are no descriptors available", errorStringMaxSize, true);
6605 *isRecoverable = false;
6606 }
6607 else if (error == WSAEWOULDBLOCK) {
6608 utils::strcpyavail(errorString, "The socket is marked as nonblocking and no connections are present to be accepted", errorStringMaxSize, true);
6609 *isRecoverable = false;
6610 }
6611 else if (error == WSAETIMEDOUT) {
6612 utils::strcpyavail(errorString, "Attempt to connect timed out without establishing a connection", errorStringMaxSize, true);
6613 *isRecoverable = false;
6614 }
6615 else if (error == WSAENETUNREACH) {
6616 utils::strcpyavail(errorString, "The network cannot be reached from this host at this time", errorStringMaxSize, true);
6617 *isRecoverable = false;
6618 }
6619 else if (error == WSAEISCONN) {
6620 utils::strcpyavail(errorString, "The socket is already connected (connection-oriented sockets only)", errorStringMaxSize, true);
6621 *isRecoverable = false;
6622 }
6623 else if (error == WSAECONNREFUSED) {
6624 utils::strcpyavail(errorString, "The attempt to connect was forcefully rejected", errorStringMaxSize, true);
6625 *isRecoverable = false;
6626 }
6627 else if (error == WSAEAFNOSUPPORT) {
6628 utils::strcpyavail(errorString, "Addresses in the specified family cannot be used with this socket", errorStringMaxSize, true);
6629 *isRecoverable = false;
6630 }
6631 else if (error == WSAEADDRNOTAVAIL) {
6632 utils::strcpyavail(errorString, "The remote address is not a valid address (such as ADDR_ANY)", errorStringMaxSize, true);
6633 *isRecoverable = false;
6634 }
6635 else if (error == WSAEALREADY) {
6636 utils::strcpyavail(errorString, "A nonblocking connect call is in progress on the specified socket", errorStringMaxSize, true);
6637 *isRecoverable = false;
6638 }
6639 else if (error == WSAECONNRESET) {
6640 utils::strcpyavail(errorString, "Connection was reset", errorStringMaxSize, true);
6641 *isRecoverable = false;
6642 }
6643 else if (error == WSAECONNABORTED) {
6644 utils::strcpyavail(errorString, "Software caused connection abort", errorStringMaxSize, true);
6645 *isRecoverable = false;
6646 }
6647 else {
6648 snprintf(errorString, errorStringMaxSize, "TCP error with no description: %d", error);
6649 *isRecoverable = false;
6650 }
6651
6652 return true;
6653 #else
6654 if (error == 100) {
6655 utils::strcpyavail(errorString, "Cannot initialise socket", errorStringMaxSize, true);
6656 *isRecoverable = false;
6657 }
6658 else {
6659 utils::strcpyavail(errorString, strerror(error), errorStringMaxSize, true);
6660 *isRecoverable = false;
6661 }
6662
6663 return true;
6664 #endif
6665}
6666
6667
6669 #ifdef WINDOWS
6670 int err = WSAGetLastError();
6671 WSASetLastError(0);
6672 return err;
6673 #else
6674 return errno;
6675 #endif
6676}
6677
6679 #ifdef WINDOWS
6680 LPVOID lpMsgBuf; //Message Buffer
6681 DWORD error = GetLastError();
6682 uint32 len = FormatMessage(
6683 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
6684 NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
6685 (LPTSTR) &lpMsgBuf, 0, NULL);
6686 std::string str = (char*)lpMsgBuf;
6687 LocalFree(lpMsgBuf);
6688 return str;
6689 #else
6690 return utils::StringFormat("Error number: %u", errno);
6691 #endif
6692}
6693
6694bool WaitForSocketWriteability(SOCKET s, int32 timeout) {
6695
6696 int maxfd = 0;
6697
6698 // If socket is not valid return true so the next read will report the error
6699 if (s == INVALID_SOCKET) return true;
6700
6701 struct timeval tv;
6702 tv.tv_sec = 0;
6703 tv.tv_usec = 0;
6704
6705 fd_set wds;
6706 // create a list of sockets to check for activity
6707 FD_ZERO(&wds);
6708 // specify mySocket
6709 FD_SET(s, &wds);
6710
6711 #ifdef WINDOWS
6712 #else
6713 maxfd = s + 1;
6714 #endif
6715
6716 if (timeout > 0) {
6717 ldiv_t d = ldiv(timeout*1000, 1000000);
6718 tv.tv_sec = d.quot;
6719 tv.tv_usec = d.rem;
6720 }
6721
6722 // Check for readability
6723 return( select(maxfd, NULL, &wds, NULL, &tv) > 0);
6724}
6725
6726bool WaitForSocketReadability(SOCKET s, int32 timeout) {
6727
6728 int maxfd = 0;
6729
6730 // If socket is not valid return true so the next read will report the error
6731 if (s == INVALID_SOCKET) return true;
6732
6733 struct timeval tv;
6734 tv.tv_sec = 0;
6735 tv.tv_usec = 0;
6736
6737 fd_set rdds;
6738 // create a list of sockets to check for activity
6739 FD_ZERO(&rdds);
6740 // specify mySocket
6741 FD_SET(s, &rdds);
6742
6743 #ifdef WINDOWS
6744 #else
6745 maxfd = s + 1;
6746 #endif
6747
6748 if (timeout > 0) {
6749 ldiv_t d = ldiv(timeout*1000, 1000000);
6750 tv.tv_sec = d.quot;
6751 tv.tv_usec = d.rem;
6752 }
6753
6754 // Check for readability
6755// uint64 t2 = GetTimeNow();
6756 int ret = select(maxfd, &rdds, NULL, NULL, &tv);
6757 //if (GetTimeAge(t2) > 1000) {
6758// LogPrint(0,LOG_NETWORK,0,"************** WaitForSocketReadability(%d) took %s ******************", s, PrintTimeDifString(GetTimeAge(t2)).c_str());
6759 //}
6760 return(ret > 0);
6761}
6762
6764 #if defined(WINDOWS)
6765 unsigned long parm = 1; // 1 = Non-blocking, 0 = Blocking
6766 ioctlsocket(s, FIONBIO, &parm);
6767 #else
6768 long parm = fcntl(s, F_GETFL);
6769 parm |= O_NONBLOCK;
6770 fcntl(s, F_SETFL, parm);
6771 #endif
6772 return true;
6773}
6774
6776 #if defined(WINDOWS)
6777 unsigned long parm = 0; // 1 = Non-blocking, 0 = Blocking
6778 ioctlsocket(s, FIONBIO, &parm);
6779 #else
6780 long parm = fcntl(s, F_GETFL);
6781 parm &= ~O_NONBLOCK;
6782 fcntl(s, F_SETFL, parm);
6783 #endif
6784 return true;
6785}
6786
6787bool LookupIPAddress(const char* name, uint32& address) {
6789 if ( (name == NULL) || (strlen(name) == 0) )
6790 return false;
6791
6792 // struct hostent* hent;
6793
6794 #ifdef WINDOWS
6795 //hent = gethostbyname(name);
6796 //if (hent == NULL)
6797 // return false;
6798 //else {
6799 // memcpy(&address, hent->h_addr_list[0], sizeof(address));
6800 // printf("IP1 of %s = %u.%u.%u.%u (%u)\n", name, GETIPADDRESSQUAD(address), address);
6801 // //return true;
6802 //}
6803
6804 //uint32 address2 = 0;
6805 struct addrinfo hints, *res;
6806 int error = -1;
6807 const char *cause = NULL;
6808
6809 memset(&hints, 0, sizeof(hints));
6810 //do not set to AI_NUMERICHOST, causes issues with connections...
6811 //hints.ai_flags = AI_NUMERICHOST; // AI_PASSIVE
6812 hints.ai_family = AF_INET;
6813 hints.ai_socktype = SOCK_STREAM;
6814 hints.ai_protocol = IPPROTO_TCP;
6815 __try {
6816 error = getaddrinfo(name, NULL, &hints, &res);
6817 }
6818 __except (EXCEPTION_CONTINUE_EXECUTION) {
6819 error = -1;
6820 }
6821
6822 if (!error) {
6823 memcpy(&address, res->ai_addr->sa_data + 2, sizeof(address));
6824 //printf("IP2 of %s = %u.%u.%u.%u (%u)\n", name, GETIPADDRESSQUAD(address), address);
6825 freeaddrinfo(res);
6826 return true;
6827 }
6828 else
6829 return false;
6830
6831
6832 #else
6833 struct addrinfo hints, *res;
6834 int error;
6835 const char *cause = NULL;
6836
6837 memset(&hints, 0, sizeof(hints));
6838 // hints.ai_flags = AI_NUMERICHOST; // AI_PASSIVE
6839 hints.ai_family = PF_UNSPEC;
6840 hints.ai_socktype = SOCK_STREAM;
6841 error = getaddrinfo(name, NULL, &hints, &res);
6842 if (!error) {
6843 memcpy(&address, res->ai_addr->sa_data+2, 4);
6844 freeaddrinfo(res);
6845 return true;
6846 }
6847 else
6848 return false;
6849 #endif // WINDOWS
6850}
6851
6852#ifdef WINDOWS
6853 bool IsNetworkInitialised = false;
6854 bool CheckNetworkInit() {
6855 if (IsNetworkInitialised) return true;
6856 WSADATA info;
6857 if (WSAStartup(MAKEWORD(1,1), &info) != 0) {
6858 LogPrint(0, LOG_NETWORK, 0, "Could not start Windows Networking...");
6859 return false;
6860 }
6861 IsNetworkInitialised = true;
6862 return true;
6863 }
6864#endif
6865
6866bool LookupHostname(uint32 address, char* name, uint32 maxSize) {
6868 struct hostent *pHost;
6869
6870 unsigned char* addrChars = (unsigned char*)&address;
6871 //char addrString[20];
6872 char* addrString = new char[20];
6873 snprintf(addrString, 20, "%u.%u.%u.%u", addrChars[0], addrChars[1], addrChars[2], addrChars[3]);
6874
6875 #ifdef WINDOWS
6876 pHost = gethostbyname(addrString);
6877 delete [] addrString;
6878 if (pHost == NULL) {
6879 return false;
6880 }
6881 if (strlen(pHost->h_name) > maxSize-1) {
6882 return false;
6883 }
6884 utils::strcpyavail(name, pHost->h_name, maxSize, true);
6885 return true;
6886 #else // WINDOWS
6887
6888 #ifdef Darwin
6889 pHost = gethostbyname(addrString);
6890 delete [] addrString;
6891 if (pHost == NULL)
6892 return false;
6893 else {
6894 if (strlen(pHost->h_name) > maxSize-1)
6895 return false;
6896 utils::strcpyavail(name, pHost->h_name, maxSize, true);
6897 return true;
6898 }
6899 #else // Darwin
6900
6901 pHost = new struct hostent;
6902 char bf[2000];
6903 int er;
6904 gethostbyname_r(addrString, pHost, bf, 2000, &pHost, &er);
6905 delete [] addrString;
6906 if (er != 0) {
6907 delete pHost;
6908 return false;
6909 }
6910 else {
6911 if (strlen(pHost->h_name) > maxSize-1)
6912 return false;
6913 utils::strcpyavail(name, pHost->h_name, maxSize, true);
6914 delete pHost;
6915 return true;
6916 }
6917
6918 #endif // Darwin
6919 #endif // WINDOWS
6920}
6921
6923 char* name = new char[256];
6924 if (!GetLocalHostname(name, 255)) {
6925 delete [] name;
6926 return "";
6927 }
6928
6929 std::string sname = name;
6930 delete [] name;
6931 return sname;
6932}
6933
6934bool GetLocalHostname(char* name, uint32 maxSize) {
6936
6937 if (gethostname(name, maxSize) == 0)
6938 return true;
6939 else
6940 return false;
6941
6942 //JString ipaddress = GetLocalIPAddress();
6943 //JString hostname = ipaddress;
6944 //struct hostent* hent;
6945 //if ( (ipaddress.equals("127.0.0.1")) || (ipaddress.equals("localhost")) ) {
6946 // localhostName = "127.0.0.1";
6947 // return localhostName;
6948 //}
6949
6950 //#ifdef WINDOWS
6951
6952 // hent = gethostbyname((char*) ipaddress);
6953 // if (hent != NULL)
6954 // localhostName = JString(hent->h_name);
6955 // else {
6956 // int err = WSAGetLastError();
6957 // if (DEBUGLEVEL(KITCHENSINK)) {
6958 // printf("getLocalHostname(): gethostbyaddr error %d...\n", err);
6959 // }
6960 // localhostName = ipaddress;
6961 // return ipaddress;
6962 // }
6963
6964 //#else // WINDOWS
6965
6966 // struct addrinfo hints, *res;
6967 // int error;
6968 // const char *cause = NULL;
6969
6970 // memset(&hints, 0, sizeof(hints));
6971 // hints.ai_flags = AI_NUMERICHOST;
6972 // hints.ai_family = PF_UNSPEC;
6973 // hints.ai_socktype = SOCK_STREAM;
6974 // error = getaddrinfo(ipaddress, NULL, &hints, &res);
6975 // if (error != 0) {
6976 // localhostName = ipaddress;
6977 // return ipaddress;
6978 // }
6979 // else {
6980 // if (res->ai_canonname != NULL) {
6981 // localhostName = res->ai_canonname;
6982 // }
6983 // else
6984 // localhostName = ipaddress;
6985 // }
6986 // freeaddrinfo(res);
6987
6988 //#endif // WINDOWS
6989
6990 //return localhostName;
6991
6992}
6993
6994bool GetLocalMACAddress(uint64& address) {
6995 address = 0;
6996 uint32 c;
6997 NetworkInterfaces* interfaces = GetLocalInterfaces(c);
6998 for (uint32 i=0; i<c; i++) {
6999 if ( (interfaces[i].address != 0) && (interfaces[i].address != LOCALHOSTIP) )
7000 address = interfaces[i].mac;
7001 }
7002 delete [] interfaces;
7003 return (address != 0);
7004}
7005
7006uint64* GetLocalMACAddresses(uint32& count) {
7007 uint32 c;
7008 NetworkInterfaces* interfaces = GetLocalInterfaces(c);
7009 if (!c)
7010 return NULL;
7011 uint64* addresses = new uint64[c];
7012 uint32 p=0;
7013 for (uint32 i=0; i<c; i++) {
7014 if ( (interfaces[i].address != 0) && (interfaces[i].address != LOCALHOSTIP) )
7015 addresses[p++] = interfaces[i].mac;
7016 }
7017 count = p;
7018 return addresses;
7019}
7020
7021bool IsLocalIPAddress(const char* addr) {
7022 uint32 address;
7023 if (!LookupIPAddress(addr, address))
7024 return false;
7025 return IsLocalIPAddress(address);
7026}
7027
7028bool IsLocalIPAddress(uint32& address) {
7030 uint32 count;
7031 uint32* addresses = GetLocalIPAddresses(count);
7032 if ((addresses == NULL) || (count == 0)) {
7033 delete [] addresses;
7034 return false;
7035 }
7036
7037 unsigned int n;
7038 for (n=0; n<count; n++) {
7039 if (addresses[n] == address) {
7040 delete [] addresses;
7041 return true;
7042 }
7043 }
7044 delete [] addresses;
7045 return false;
7046}
7047
7048bool GetLocalIPAddress(uint32& address) {
7050 uint32 count;
7051 uint32* addresses = GetLocalIPAddresses(count);
7052 if ((addresses == NULL) || (count == 0)) {
7053 delete [] addresses;
7054 return false;
7055 }
7056 // First find first non-localhost address
7057 unsigned int n;
7058 bool localhostPresent = false;
7059 for (n=0; n<count; n++) {
7060 if (addresses[n] != LOCALHOSTIP) {
7061 if (addresses[n] != 0) {
7062 address = addresses[n];
7063 delete [] addresses;
7064 return true;
7065 }
7066 }
7067 else
7068 localhostPresent = true;
7069 }
7070 delete [] addresses;
7071 // OK, then accept localhost, if present
7072 if (localhostPresent) {
7073 address = LOCALHOSTIP;
7074 return true;
7075 }
7076 else
7077 return false;
7078}
7079
7080uint32* GetLocalIPAddresses(uint32& count) {
7082
7083 uint32 maxSize = 128;
7084 uint32* addresses = new uint32[maxSize];
7085
7086 addresses[0] = LOCALHOSTIP;
7087 count = 1;
7088
7089 char* szHostName = new char[255];
7090
7091 bool canAskHost = true;
7092 if( gethostname(szHostName, 255) != 0 ) {
7093 #ifdef WINDOWS
7094 int err = GetLastOSErrorNumber();
7095 if (err == WSANOTINITIALISED) {
7096 WSADATA info;
7097 if (WSAStartup(MAKEWORD(1,1), &info) != 0) {
7098 delete [] szHostName;
7099 return addresses;
7100 }
7101 else {
7102 if( gethostname(szHostName, 255) != 0 )
7103 canAskHost = false;
7104 }
7105 }
7106 else
7107 canAskHost = false;
7108 #else // WINDOWS
7109 canAskHost = false;
7110 #endif // WINDOWS
7111 }
7112
7113 struct hostent * pHost;
7114 uint32 addr, j, i;
7115 bool exists;
7116 #if !defined(WINDOWS) && !defined(Darwin)
7117 // gethostbyname_r() writes its result INTO this caller-supplied buffer,
7118 // and the returned hostent's h_addr_list points INTO that buffer - so it
7119 // must outlive every read of pHost below. Freeing it before the
7120 // consumption loop was a real heap-use-after-free (ASan: 30/30 runs of
7121 // network_http reported it at the h_addr_list read). Freed after the loop.
7122 char* hostBuf = NULL;
7123 struct hostent* hostAlloc = NULL;
7124 #endif
7125
7126 if (canAskHost) {
7127 // Get host addresses
7128
7129 #ifdef WINDOWS
7130 pHost = gethostbyname(szHostName);
7131 #else // WINDOWS
7132
7133 #ifdef Darwin
7134 pHost = gethostbyname(szHostName);
7135 //if (pHost == NULL)
7136 // return addresses;
7137 #else // Darwin
7138
7139 hostAlloc = new struct hostent;
7140 hostBuf = new char[2000];
7141 int er = 0;
7142 struct hostent* hres = NULL;
7143 int res = gethostbyname_r(szHostName, hostAlloc, hostBuf, 2000, &hres, &er);
7144 // LogPrint(0, LOG_NETWORK, 0, "gethostbyname_r '%s' [%d] [%d]...\n", szHostName, res, er);
7145 // Success = res==0 AND non-NULL result. Keep the result in its own
7146 // pointer: glibc sets *result = NULL on failure, and the old code
7147 // passed &pHost, so that NULL overwrote our allocation - leaking the
7148 // hostent and leaving the trailing "delete pHost" deleting NULL.
7149 // Do NOT free hostBuf here: h_addr_list points into it.
7150 if ((res != 0) || (hres == NULL))
7151 pHost = NULL;
7152 else
7153 pHost = hres;
7154 #endif // Darwin
7155 #endif // WINDOWS
7156
7157 for( i = 0; pHost!= NULL && pHost->h_addr_list[i]!= NULL; i++ )
7158 {
7159 exists = false;
7160 memcpy(&addr, ((unsigned char*)pHost->h_addr_list[i]), sizeof(uint32));
7161 if ( (addr != 0) && (addr != LOCALHOSTIP) ) {
7162 for (j=0; j<count; j++) {
7163 if (addresses[j] == addr) {
7164 exists = true;
7165 break;
7166 }
7167 }
7168 if (!exists)
7169 addresses[count++] = addr;
7170 }
7171 }
7172
7173 #ifdef WINDOWS
7174 #else // WINDOWS
7175 #ifdef Darwin
7176 endhostent();
7177 #else
7178 // Safe now: the h_addr_list reads above are done.
7179 delete hostAlloc;
7180 delete [] hostBuf;
7181 #endif // Darwin
7182 #endif // WINDOWS
7183 }
7184
7185 uint32 c;
7186 NetworkInterfaces* interfaces = GetLocalInterfaces(c);
7187 for (i=0; i<c; i++) {
7188 exists = false;
7189 if ( (interfaces[i].address != 0) && (interfaces[i].address != LOCALHOSTIP) ) {
7190 for (j=0; j<count; j++) {
7191 if (addresses[j] == interfaces[i].address) {
7192 exists = true;
7193 break;
7194 }
7195 }
7196 if (!exists)
7197 addresses[count++] = interfaces[i].address;
7198 }
7199 }
7200 delete [] interfaces;
7201 delete [] szHostName;
7202 return addresses;
7203}
7204
7207
7208 uint32 maxSize = 128;
7209 uint32 addr;
7210 NetworkInterfaces* interfaces = new NetworkInterfaces[maxSize];
7211 count = 0;
7212
7213 #ifdef WINDOWS
7214
7215 DWORD dwSize = 0;
7216 DWORD dwRetVal = 0;
7217
7218 unsigned int i = 0;
7219
7220 // Set the flags to pass to GetAdaptersAddresses
7221 ULONG flags = GAA_FLAG_INCLUDE_PREFIX;
7222
7223 // default to unspecified address family (both)
7224 ULONG family = AF_INET;
7225
7226 LPVOID lpMsgBuf = NULL;
7227
7228 PIP_ADAPTER_ADDRESSES pAddresses = NULL;
7229 ULONG outBufLen = 0;
7230 ULONG Iterations = 0;
7231
7232 PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
7233 PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
7234 PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL;
7235 PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL;
7236 IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = NULL;
7237 IP_ADAPTER_PREFIX *pPrefix = NULL;
7238
7239 // Allocate a 15 KB buffer to start with.
7240 outBufLen = 15000;
7241
7242 do {
7243
7244 pAddresses = (IP_ADAPTER_ADDRESSES *) MALLOC(outBufLen);
7245 if (pAddresses == NULL)
7246 return interfaces;
7247
7248 dwRetVal =
7249 GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen);
7250
7251 if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
7252 FREE(pAddresses);
7253 pAddresses = NULL;
7254 }
7255 else
7256 break;
7257
7258 Iterations++;
7259
7260 } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < 10));
7261
7262
7263 if (dwRetVal == NO_ERROR) {
7264 // If successful, output some information from the data we received
7265 pCurrAddresses = pAddresses;
7266 while (pCurrAddresses) {
7267 if ( (pCurrAddresses->OperStatus == IfOperStatusUp) && (pCurrAddresses->IfIndex != 0)) {
7268
7269 // First check the IP address
7270 pUnicast = pCurrAddresses->FirstUnicastAddress;
7271 if (pUnicast != NULL) {
7272 for (i = 0; pUnicast != NULL; i++) {
7273 addr = *(uint32*)(((unsigned char*) ((SOCKADDR*)(pUnicast->Address.lpSockaddr))->sa_data) + 2);
7274 if ( (addr != 0) && (addr != LOCALHOSTIP) ) {
7275 interfaces[count].address = addr;
7276 interfaces[count].mac = 0;
7277 if (pCurrAddresses->PhysicalAddressLength == 6)
7278 memcpy(&(interfaces[count].mac), &(pCurrAddresses->PhysicalAddress), 6);
7279 WideCharToMultiByte( CP_ACP, 0, pCurrAddresses->Description, -1, interfaces[count].name, MAXKEYNAMELEN, NULL, NULL );
7280 WideCharToMultiByte( CP_ACP, 0, pCurrAddresses->FriendlyName, -1, interfaces[count].friendlyName, MAXKEYNAMELEN, NULL, NULL );
7281 count++;
7282 }
7283 pUnicast = pUnicast->Next;
7284 }
7285 }
7286 }
7287
7288 pCurrAddresses = pCurrAddresses->Next;
7289 }
7290 }
7291 if (pAddresses)
7292 FREE(pAddresses);
7293
7294 return interfaces;
7295
7296 #else
7297 int sock = 0;
7298 struct ifreq ifreq;
7299 struct sockaddr_in *saptr = NULL;
7300 struct if_nameindex *iflist = NULL, *listsave = NULL;
7301
7302 //need a socket for ioctl()
7303 if( (sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
7304 return interfaces;
7305 }
7306
7307 //returns pointer to dynamically allocated list of structs
7308 iflist = listsave = if_nameindex();
7309
7310 if (iflist == NULL) {
7311 close(sock);
7312 return interfaces;
7313 }
7314
7315 //walk thru the array returned and query for each
7316 //interface's address
7317 //for(iflist; iflist->if_index != 0; iflist++) {
7318 for(; iflist->if_index != 0; iflist++) {
7319 //copy in the interface name to look up address of
7320 strncpy(ifreq.ifr_name, iflist->if_name, IF_NAMESIZE);
7321 //get the address for this interface
7322 if(ioctl(sock, SIOCGIFADDR, &ifreq) != 0) {
7323 // ignore;
7324 continue;
7325 }
7326 //print out the address
7327 saptr = (struct sockaddr_in *)&ifreq.ifr_addr;
7328 //unsigned char* ip = (unsigned char*)&(saptr->sin_addr.s_addr);
7329 //printf("**** %u.%u.%u.%u ***\n", ip[0], ip[1], ip[2], ip[3]);
7330 memcpy(&(interfaces[count].address), &(saptr->sin_addr.s_addr), sizeof(uint32));
7331 utils::strcpyavail(interfaces[count].name, ifreq.ifr_name, MAXKEYNAMELEN, true);
7332 #ifdef __APPLE__
7333 interfaces[count].mac = 0;
7334 #else
7335 // get the MAC address (Linux only)
7336 interfaces[count].mac = 0;
7337 ioctl(sock, SIOCGIFHWADDR, &ifreq);
7338 memcpy(&(interfaces[count].mac), &(ifreq.ifr_hwaddr.sa_data), 6);
7339 #endif
7340 count++;
7341 }
7342 //free the dynamic memory kernel allocated for us
7343 if_freenameindex(listsave);
7344 close(sock);
7345
7346 #endif
7347
7348 return interfaces;
7349}
7350
7351bool GetNextAvailableLocalPort(uint16 lastPort, uint16 &nextPort) {
7353
7354 SOCKET socket;
7355 struct sockaddr_in addr;
7356 addr.sin_family= AF_INET;
7357 addr.sin_addr.s_addr=INADDR_ANY;
7358 bool result = false;
7359
7360 nextPort = lastPort + 1;
7361 while (nextPort < lastPort + 1000) {
7362 if((socket=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET)
7363 return false;
7364
7365 #ifdef WINDOWS
7366 // Set the exclusive address option, preventing other software binding to
7367 // non-INADDR_ANY (i.e. interface addresses such as localhost directly)
7368 int one = 1;
7369 setsockopt(socket, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *) &one, sizeof(one));
7370 #else
7371 /*
7372 This socket option tells the kernel that even if this port is busy (in
7373 the TIME_WAIT state), go ahead and reuse it anyway. If it is busy,
7374 but with another state, you will still get an address already in use
7375 error. It is useful if your server has been shut down, and then
7376 restarted right away while sockets are still active on its port. You
7377 should be aware that if any unexpected data comes in, it may confuse
7378 your server, but while this is possible, it is not likely.
7379 */
7380 int one = 1;
7381 setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&one,sizeof(one));
7382 #endif
7383
7384 addr.sin_port=htons(nextPort);
7385
7386 result = (bind(socket,(SOCKADDR*)&addr,sizeof(struct sockaddr_in))!=SOCKET_ERROR);
7387 shutdown(socket, SD_BOTH);
7388 closesocket(socket);
7389
7390 if (result)
7391 return true;
7392
7393 nextPort++;
7394 }
7395 return false;
7396
7397}
7398
7400 printf("Testing Utils...\n\n");
7401
7402 char hostname[1024];
7403 if (!GetLocalHostname(hostname, 1023)) {
7404 printf("Error GetLocalHostname\n");
7405 return false;
7406 }
7407 printf("Local Hostname: '%s'\n\n", hostname);
7408
7409 uint32 addr;
7410 unsigned char* addrChars = (unsigned char*) &addr;
7411 if (!GetLocalIPAddress(addr)) {
7412 printf("Error GetLocalIPAddress\n");
7413 return false;
7414 }
7415 printf("Local IP Address: %u.%u.%u.%u\n\n", addrChars[0], addrChars[1], addrChars[2], addrChars[3]);
7416
7417 uint32 count;
7418 uint32* addresses = GetLocalIPAddresses(count);
7419 if (addresses == NULL) {
7420 printf("Error GetLocalIPAddresses\n");
7421 return false;
7422 }
7423 uint32 n;
7424 for (n=0; n<count; n++) {
7425 addrChars = (unsigned char*) &addresses[n];
7426 printf("Local IP Address[%u]: %u.%u.%u.%u (%u,%u)\n\n", n, addrChars[0], addrChars[1], addrChars[2], addrChars[3], addresses[n], LOCALHOSTIP);
7427 }
7428 delete [] addresses;
7429
7430 NetworkInterfaces* interfaces = GetLocalInterfaces(count);
7431 for (n=0; n<count; n++) {
7432 addrChars = (unsigned char*) &(interfaces[n]);
7433 printf("Local Interface[%u] %s: %u.%u.%u.%u (%llX)\n\n", n, interfaces[n].name,
7434 addrChars[0], addrChars[1], addrChars[2], addrChars[3], interfaces[n].mac);
7435 }
7436 delete [] interfaces;
7437
7438 return true;
7439}
7440
7441void PrintBinary(void* p, uint32 size, bool asInt, const char* title) {
7442 if (title != NULL)
7443 printf("--- %s %u ---\n", title, size);
7444 unsigned char c;
7445 for (uint32 n=0; n<size; n++) {
7446 c = *(((unsigned char*)p)+n);
7447 if (asInt)
7448 printf("[%u] ", (unsigned int)c);
7449 else
7450 printf("[%c] ", c);
7451 if ( (n > 0) && ((n+1)%10 == 0) )
7452 printf("\n");
7453 }
7454 printf("\n");
7455}
7456
7457// const char* stristr(const char *str, const char *substr, uint32 len) {
7458// if (!str || !substr) return NULL;
7459// const char *a, *b;
7460// uint32 n=0, m, sslen = (uint32)strlen(substr);
7461// for(;*str;*str++,n++) {
7462// a = str;
7463// b = substr;
7464// m = n+sslen;
7465// while((*a++ | 32) == (*b++ | 32)) {
7466// if(!*b)
7467// return str;
7468// if (len && (++m > len))
7469// return NULL;
7470// }
7471// }
7472// return NULL;
7473// }
7474
7475
7476const char* stristr(const char *str, const char *substr, uint32 len) {
7477 if (!str || !substr) return NULL;
7478 uint32 sslen = (uint32)strlen(substr);
7479 if (sslen == 0) return (const char*)str;
7480 const char *a, *b;
7481 uint32 n=0, m;
7482 for (; *str; str++, n++) {
7483 a = str;
7484 b = substr;
7485 m = n+sslen;
7486 /* Case-insensitive compare via |32 (ASCII A–Z only). */
7487 while ((*a++ | 32) == (*b++ | 32)) {
7488 if (!*b)
7489 return str;
7490 if (len && (++m > len))
7491 return NULL;
7492 }
7493 }
7494 return NULL;
7495}
7496
7497uint32 strcpyavail(char* dst, const char* src, uint32 maxlen, bool copyAvailable) {
7498 if ( !dst || ! src || !maxlen )
7499 return 0;
7500 uint32 len = (uint32)strlen(src);
7501 if (len > maxlen-1) {
7502 if (!copyAvailable)
7503 return 0;
7504 else {
7505 memcpy(dst, src, maxlen-1);
7506 dst[maxlen] = 0;
7507 return maxlen-1;
7508 }
7509 }
7510 else
7511 memcpy(dst, src, len+1);
7512 return len;
7513}
7514
7515bool GetNextLineEnd(const char *str, uint32 size, uint32& len, uint32& crSize) {
7516 if (!str || !size)
7517 return false;
7518
7519 for (len = 0; len < size; len++) {
7520 // Checking for CR and CRLF
7521 if (str[len] == 13) {
7522 crSize = ( (len < size-1) && (str[len+1] == 10 ) ) ? 2 : 1;
7523 return true;
7524 }
7525 // Checking for LF only without CR
7526 if (str[len] == 10) {
7527 crSize = 1;
7528 return true;
7529 }
7530 }
7531 return false;
7532}
7533
7534std::string TextCapitalise(const char* text) {
7535 if (!text)
7536 return "";
7537 std::string str = text;
7538 if (!str.length())
7539 return "";
7540 std::string::iterator i = str.begin(), e = str.end();
7541 *i = ::toupper(*i);
7542 i++;
7543 if (i != e)
7544 std::transform(i, e, i, ::tolower);
7545 return str;
7546}
7547
7548std::string TextUppercase(const char* text) {
7549 std::string str = text;
7550 std::transform(str.begin(), str.end(), str.begin(), ::toupper);
7551 return str;
7552}
7553
7554std::string TextLowercase(const char* text) {
7555 std::string str = text;
7556 std::transform(str.begin(), str.end(), str.begin(), ::tolower);
7557 return str;
7558}
7559
7560std::string TextIndent(const char* text, const char* indent) {
7561 std::vector<std::string> lines = utils::TextListSplitLines(text, true, false);
7562 std::vector<std::string>::iterator i = lines.begin(), e = lines.end();
7563 std::string output;
7564 while (i != e) {
7565 output += utils::StringFormat("%s%s\n", indent, (*i).c_str());
7566 i++;
7567 }
7568 return output;
7569}
7570
7571std::string TextUnindent(const char* text) {
7572 uint32 len = (uint32)strlen(text);
7573 if (!len) return "";
7574 if (text[0] > 32)
7575 return text;
7576
7577 // Find initial spacing char sequence
7578 uint32 s = 0, p = 0;
7579 // Find initial line endings
7580 while ( ((text[s] == '\n') || (text[s] == '\r')) && (s < len))
7581 s++;
7582 p = s;
7583 while ((text[p] <= 32) && (p < len))
7584 p++;
7585 std::string indentString = std::string(text+s, p-s);
7586 std::string str = text;
7587 StringSingleReplace(str, indentString, "", false);
7588 return str;
7589}
7590
7591
7592std::string TextTrimQuotes(const char* text) {
7593 if (text) {
7594 uint32 begin = 0, end = (uint32)strlen(text);
7595 if (begin == end)
7596 return "";
7597 while ((begin < end) && (text[begin] <= 32) || (text[begin] == '\"') || (text[begin] == '\''))
7598 begin++;
7599 if (begin == end)
7600 return "";
7601 while ((end > begin) && (text[end-1] <= 32) || (text[end-1] == '\"') || (text[end-1] == '\''))
7602 end--;
7603 return std::string(text+begin, end-begin);
7604 }
7605 else
7606 return "";
7607}
7608
7609
7610std::string TextTrim(const char* text) {
7611 if (text) {
7612 uint32 begin = 0, end = (uint32)strlen(text);
7613 if (begin == end)
7614 return "";
7615 while ((text[begin] <= 32) && (begin < end))
7616 begin++;
7617 while ((end > begin) && (text[end-1] <= 32))
7618 end--;
7619 return std::string(text+begin, end-begin);
7620 }
7621 else
7622 return "";
7623}
7624
7625std::multimap<std::string, std::string> TextMultiMapSplit(const char* text, const char* outersplit, const char* innersplit) {
7626 std::multimap<std::string, std::string> result;
7627 typedef std::pair <std::string, std::string> Map_String_Pair;
7628
7629 if (!text || !outersplit || !innersplit)
7630 return result;
7631
7632 const char* pstr = text, *lstr = text;
7633 const char* src = text;
7634
7635 while (pstr = strstr(src, outersplit)) {
7636 if ((lstr = strstr(src, innersplit)) && (lstr < pstr)) {
7637 // result[TextTrim(std::string(src, lstr-src).c_str())] = TextTrim(std::string(lstr+1, pstr-lstr-1).c_str());
7638 result.insert(Map_String_Pair(TextTrim(std::string(src, lstr-src).c_str()), TextTrim(std::string(lstr+1, pstr-lstr-1).c_str())));
7639 }
7640 src = pstr + 1;
7641 }
7642 pstr = src + strlen(src);
7643 if ((lstr = strstr(src, innersplit)) && (lstr < pstr)) {
7644 // result[TextTrim(std::string(src, lstr-src).c_str())] = TextTrim(std::string(lstr+1, pstr-lstr-1).c_str());
7645 result.insert(Map_String_Pair(TextTrim(std::string(src, lstr-src).c_str()), TextTrim(std::string(lstr+1, pstr-lstr-1).c_str())));
7646 }
7647
7648 return result;
7649}
7650
7651
7652std::map<std::string, std::string> TextMapSplit(const char* text, const char* outersplit, const char* innersplit) {
7653 std::map<std::string, std::string> result;
7654 typedef std::pair <std::string, std::string> Map_String_Pair;
7655
7656 if (!text || !outersplit || !innersplit)
7657 return result;
7658
7659 const char* pstr = text, *lstr = text;
7660 const char* src = text;
7661
7662 while (pstr = strstr(src, outersplit)) {
7663 if ((lstr = strstr(src, innersplit)) && (lstr < pstr)) {
7664 // result[TextTrim(std::string(src, lstr-src).c_str())] = TextTrim(std::string(lstr+1, pstr-lstr-1).c_str());
7665 result.insert(Map_String_Pair(TextTrim(std::string(src, lstr - src).c_str()), TextTrim(std::string(lstr + 1, pstr - lstr - 1).c_str())));
7666 }
7667 src = pstr + 1;
7668 }
7669 pstr = src + strlen(src);
7670 if ((lstr = strstr(src, innersplit)) && (lstr < pstr)) {
7671 // result[TextTrim(std::string(src, lstr-src).c_str())] = TextTrim(std::string(lstr+1, pstr-lstr-1).c_str());
7672 result.insert(Map_String_Pair(TextTrim(std::string(src, lstr - src).c_str()), TextTrim(std::string(lstr + 1, pstr - lstr - 1).c_str())));
7673 }
7674
7675 return result;
7676}
7677
7678char** SplitCommandline(const char* cmdline, int& argc) {
7679 std::vector<std::string> args = TextCommandlineSplit(cmdline);
7680 uint32 size = (uint32)args.size();
7681 if (!size)
7682 return NULL;
7683
7684 char** argv = new char*[size+1];
7685 uint32 c = 0;
7686 std::vector<std::string>::iterator i = args.begin(), e = args.end();
7687 while (i != e) {
7688 argv[c] = new char[(*i).length()+1];
7689 strcpy(argv[c], (*i).c_str());
7690 c++;
7691 i++;
7692 }
7693 argv[c] = NULL;
7694 argc = (int)c;
7695 return argv;
7696}
7697
7698wchar_t** SplitCommandlineW(const char* cmdline, int& argc) {
7699 std::vector<std::string> args = TextCommandlineSplit(cmdline);
7700 uint32 size = (uint32)args.size();
7701 if (!size)
7702 return NULL;
7703
7704 wchar_t** argv = new wchar_t*[size+1];
7705 uint32 c = 0;
7706 std::vector<std::string>::iterator i = args.begin(), e = args.end();
7707 while (i != e) {
7708 argv[c] = new wchar_t[(*i).length()+1];
7709 mbstowcs(argv[c], (*i).c_str(), (*i).length() + 1);
7710 c++;
7711 i++;
7712 }
7713 argv[c] = NULL;
7714 argc = (int)c;
7715 return argv;
7716}
7717
7718bool DeleteCommandline(char** argv, int argc) {
7719 for (uint32 n=0; n<(uint32)argc; n++)
7720 delete [] argv[n];
7721 delete [] argv;
7722 return true;
7723}
7724
7725std::vector<std::string> TextCommandlineSplit(const char* cmdline) {
7726
7727 bool insideQuotes = false;
7728 bool lastWasWhiteSpace = false;
7729 std::vector<std::string> vect;
7730 std::string arg, val, str;
7731 unsigned char ch;
7732
7733 uint32 len = (uint32)strlen(cmdline);
7734
7735 bool keepQuotes = false;
7736
7737 for (uint32 n=0; n<len; n++) {
7738 ch = cmdline[n];
7739 if (ch <= 32) {
7740 if (!insideQuotes) {
7741 if (!lastWasWhiteSpace) {
7742 // An arg is ended, parse it...
7743 vect.push_back(str);
7744 str = "";
7745 }
7746 else {
7747 // Ignore this whitespace then...
7748 }
7749 lastWasWhiteSpace = true;
7750 }
7751 else {
7752 str += ch;
7753 }
7754 }
7755 else if (ch == '"') {
7756 insideQuotes = !insideQuotes;
7757 if (keepQuotes)
7758 str += ch;
7759 lastWasWhiteSpace = false;
7760 }
7761 else {
7762 str += ch;
7763 lastWasWhiteSpace = false;
7764 }
7765 }
7766
7767 if (str.length() > 0) {
7768 vect.push_back(str);
7769 str = "";
7770 }
7771
7772 return vect;
7773}
7774
7775std::vector<std::string> TextListSplitLines(const char* text, bool keepEmpty, bool autoTrim) {
7776 if (!text || !strlen(text))
7777 return std::vector<std::string>();
7778
7779 if (strstr(text, "\r\n"))
7780 return TextListSplit(text, "\r\n", keepEmpty, autoTrim);
7781 else if (strstr(text, "\n\r"))
7782 return TextListSplit(text, "\n\r", keepEmpty, autoTrim);
7783 else if (strstr(text, "\n"))
7784 return TextListSplit(text, "\n", keepEmpty, autoTrim);
7785 else if (strstr(text, "\r"))
7786 return TextListSplit(text, "\n", keepEmpty, autoTrim);
7787
7788 std::vector<std::string> result;
7789 result.push_back(text);
7790 return result;
7791}
7792
7793std::vector<std::string> TextListBreakLines(const char* text, uint32 maxLineLength) {
7794 if (!text || !strlen(text))
7795 return std::vector<std::string>();
7796
7797 std::string str;
7798 std::vector<std::string> result;
7799 uint32 s = 0, n = 0, len = (uint32)strlen(text);
7800 int p = -1;
7801 while (n < len) {
7802 if (text[n] == 13) {
7803 if (n == s)
7804 result.push_back("");
7805 else {
7806 str = std::string(text + s, n - s);
7807 result.push_back(TextTrim(str.c_str()));
7808 }
7809 if ((n < len - 1) && (text[n + 1] == 10))
7810 s = n + 2;
7811 else
7812 s = n + 1;
7813 p = -1;
7814 }
7815 else if (text[n] == 10) {
7816 if (n == s)
7817 result.push_back("");
7818 else {
7819 str = std::string(text + s, n - s);
7820 result.push_back(TextTrim(str.c_str()));
7821 }
7822 if ((n < len - 1) && (text[n + 1] == 13))
7823 s = n + 2;
7824 else
7825 s = n + 1;
7826 p = -1;
7827 }
7828 else if ((n-s < maxLineLength) && (text[n] == 32)) {
7829 p = n - s;
7830 }
7831 else if (n - s >= maxLineLength) {
7832 if (p == 0) {
7833 result.push_back("");
7834 s += p + 1;
7835 p = -1;
7836 n = s - 1;
7837 }
7838 else if (p > 0) {
7839 str = std::string(text + s, p);
7840 result.push_back(TextTrim(str.c_str()));
7841 s += p + 1;
7842 p = -1;
7843 n = s - 1;
7844 }
7845 else {
7846 str = std::string(text + s, maxLineLength);
7847 result.push_back(TextTrim(str.c_str()));
7848 s += maxLineLength;
7849 p = -1;
7850 n = s;
7851 }
7852 }
7853 n++;
7854 }
7855 if (s < n) {
7856 str = std::string(text + s, n - s);
7857 result.push_back(TextTrim(str.c_str()));
7858 }
7859
7860 return result;
7861}
7862
7863std::string TextListBreakLines(const char* text, uint32 maxLineLength, const char* breakstr) {
7864 std::vector<std::string> lines = TextListBreakLines(text, maxLineLength);
7865 if (!breakstr)
7866 return TextJoin(lines, "\n");
7867 else
7868 return TextJoin(lines, breakstr);
7869}
7870
7871std::string TextJoin(std::vector<std::string> &list, const char* split, uint32 start, uint32 count) {
7872 std::ostringstream o;
7873 std::vector<std::string>::iterator i = list.begin(), e = list.end();
7874 uint32 l = 0, c = 0;
7875 while (i != e) {
7876 if (!count || ((l >= start) && (c < count))) {
7877 if (c++) o << split;
7878 o << *i;
7879 }
7880 l++;
7881 i++;
7882 }
7883 return o.str();
7884}
7885
7886std::string TextJoin(std::list<std::string> &list, const char* split, uint32 start, uint32 count) {
7887 std::ostringstream o;
7888 std::list<std::string>::iterator i = list.begin(), e = list.end();
7889 uint32 l = 0, c = 0;
7890 while (i != e) {
7891 if (!count || ((l >= start) && (c < count))) {
7892 if (c++) o << split;
7893 o << *i;
7894 }
7895 l++;
7896 i++;
7897 }
7898 return o.str();
7899}
7900
7901std::string TextJoin(std::map<std::string, std::string> &map, const char* innersplit, const char* outersplit, bool keyquotes, bool valquotes) {
7902 std::ostringstream o;
7903 std::map<std::string, std::string>::iterator i = map.begin(), e = map.end();
7904 uint32 c = 0;
7905 while (i != e) {
7906 if (c++) o << outersplit;
7907 if (keyquotes && valquotes)
7908 o << "\"" << i->first << "\"" << innersplit << "\"" << i->second << "\"";
7909 else if (valquotes)
7910 o << i->first << innersplit << "\"" << i->second << "\"";
7911 else
7912 o << i->first << innersplit << i->second;
7913 i++;
7914 }
7915 return o.str();
7916}
7917
7918std::string TextJoinJSON(std::map<std::string, std::string> &map) {
7919 std::ostringstream o;
7920 std::map<std::string, std::string>::iterator i = map.begin(), e = map.end();
7921 uint32 c = 0;
7922 o << "{ ";
7923 while (i != e) {
7924 if (c++)
7925 o << ", \"" << utils::EncodeJSON(i->first) << "\": \"" << utils::EncodeJSON(i->second) << "\"";
7926 else
7927 o << " \"" << utils::EncodeJSON(i->first) << "\": \"" << utils::EncodeJSON(i->second) << "\"";
7928 i++;
7929 }
7930 o << " }";
7931 return o.str();
7932}
7933
7934std::string TextJoinXML(std::map<std::string, std::string> &map, const char* innernodeName, const char* outernodeName) {
7935 if (!innernodeName || !strlen(innernodeName))
7936 return "";
7937 std::ostringstream o;
7938 std::map<std::string, std::string>::iterator i = map.begin(), e = map.end();
7939 if (outernodeName && strlen(outernodeName))
7940 o << "<" << outernodeName << ">\n";
7941 while (i != e) {
7942 o << "<" << innernodeName << " " << html::EncodeHTML(i->first) << "=\"" << html::EncodeHTML(i->second) << "\" />\n";
7943 i++;
7944 }
7945 if (outernodeName && strlen(outernodeName))
7946 o << "</" << outernodeName << ">\n";
7947 return o.str();
7948}
7949
7950
7951
7952std::vector<std::string> TextListSplit(const char* text, const char* split, bool keepEmpty, bool autoTrim) {
7953 std::vector<std::string> result;
7954 if (!text || !split)
7955 return result;
7956
7957 uint32 splitLen = (uint32)strlen(split);
7958 const char* pstr = text;
7959 const char* src = text;
7960
7961 while (pstr = strstr(src, split)) {
7962 if (pstr - src || keepEmpty) {
7963 if (autoTrim)
7964 result.push_back(TextTrim(std::string(src, pstr - src).c_str()));
7965 else
7966 result.push_back(std::string(src, pstr - src).c_str());
7967 }
7968 src = pstr + splitLen;
7969 }
7970 pstr = src + strlen(src);
7971 if (pstr - src) {
7972 if (autoTrim)
7973 result.push_back(TextTrim(std::string(src, pstr - src).c_str()));
7974 else
7975 result.push_back(std::string(src, pstr - src).c_str());
7976 }
7977 return result;
7978}
7979
7980std::string TextVectorConcat(std::vector<std::string> vect, const char* sep, bool allowEmpty) {
7981 std::string result;
7982 std::vector<std::string>::iterator i, e;
7983 for (i=vect.begin(), e=vect.end(); i!=e; i++) {
7984 if (allowEmpty || (*i).size()) {
7985 if (result.size())
7986 result += sep;
7987 result += *i;
7988 }
7989 }
7990 return result;
7991}
7992
7993char* StringFormat(uint32& size, const char *format, ...) {
7994 if (strlen(format) == 0) {
7995 size = 0;
7996 return 0;
7997 }
7998
7999 va_list args;
8000 va_start(args, format);
8001 char* res = StringFormatVA(size, format, args);
8002 va_end(args);
8003 return res;
8004}
8005
8006
8007char* StringFormatVA(uint32& size, const char *format, va_list orig_args) {
8008 char* str = NULL;
8009
8010 #ifdef WINDOWS
8011 int len = _vscprintf(format, orig_args) + 10;
8012 str = (char*)malloc(len+1);
8013 #ifdef CYGWIN
8014 len = vsnprintf(str, len, format, orig_args);
8015 #else
8016 len = _vsnprintf_s(str, len, len+1, format, orig_args);
8017 #endif
8018
8019 if (len)
8020 size = (uint32)strlen(str);
8021 else {
8022 size = 0;
8023 free((char*)str);
8024 str = NULL;
8025 }
8026 #else // WINDOWS
8027 int len = vasprintf(&str, format, orig_args);
8028 if(len >= 0)
8029 size = strlen(str);
8030 else
8031 size = 0;
8032 #endif //HAVE_VASPRINTF
8033
8034 return str;
8035}
8036
8037bool StringFormatInto(char* dst, uint32 maxsize, const char *format, ...) {
8038 if (!dst || !maxsize)
8039 return false;
8040 if (strlen(format) == 0) {
8041 dst[0] = 0;
8042 return true;
8043 }
8044
8045 uint32 len = 0;
8046 va_list args;
8047 va_start(args, format);
8048 char* str = StringFormatVA(len, format, args);
8049 va_end(args);
8050 if (str && len) {
8051 if (len > maxsize) {
8052 free(str);
8053 return false;
8054 }
8055 else {
8056 utils::strcpyavail(dst, str, maxsize, true);
8057 free(str);
8058 return true;
8059 }
8060 }
8061 else {
8062 free(str);
8063 return false;
8064 }
8065}
8066
8067std::string StringFormat(const char *format, ...) {
8068 if (strlen(format) == 0)
8069 return "";
8070
8071 std::string res;
8072 uint32 len = 0;
8073 va_list args;
8074 va_start(args, format);
8075 char* str = StringFormatVA(len, format, args);
8076 va_end(args);
8077 if (str && len)
8078 res = str;
8079 else
8080 res = "";
8081 free(str);
8082 return res;
8083}
8084
8085uint32 StringSingleReplace(std::string& text, std::string key, std::string value, bool onlyFirst) {
8086 if (!text.size() || !key.size())
8087 return 0;
8088
8089 uint32 c = 0;
8090 size_t p = 0;
8091 uint32 keylen = (uint32)key.size();
8092
8093 while ( (p = text.find(key, p)) != std::string::npos) {
8094 text.replace(p, keylen, value);
8095 p++;
8096 c++;
8097 if (onlyFirst)
8098 break;
8099 }
8100 return c;
8101}
8102
8103uint32 StringMultiReplace(std::string& text, std::map<std::string, std::string>& map, bool onlyFirst) {
8104 if (!text.size() || !map.size())
8105 return 0;
8106
8107 uint32 c = 0;
8108
8109 std::map<std::string, std::string>::iterator it, itEnd;
8110 for (it = map.begin(), itEnd = map.end(); it != itEnd; ++it)
8111 c += StringSingleReplace(text, it->first, it->second, onlyFirst);
8112
8113 return c;
8114}
8115
8116
8117// Example use
8118 //uint32 len;
8119 //char* file = utils::ReadAFile("test.html", len);
8120 //std::string sfile = file;
8121
8122 //std::list<std::map<std::string, std::string> > list;
8123
8124 //char tmp[64];
8125 //std::map<std::string, std::string> map;
8126 //for (uint32 n=0; n<10; n++) {
8127 // map["%1%"] = utils::Int2Ascii(n, tmp, 64, 10);
8128 // map["%2%"] = "Hello2";
8129 // map["%3%"] = "Hello3";
8130 // map["%4%"] = "Hello4";
8131 // map["%5%"] = "Hello5";
8132 // list.push_back(map);
8133 //}
8134
8135 //utils::ScriptReplace(sfile, "test", list);
8136
8137uint32 StringScriptReplace(std::string& text, std::string name, std::list<std::map<std::string, std::string> >& list) {
8138 if (!text.size() || !name.size())
8139 return 0;
8140
8141 uint32 c = 0;
8142
8143 char* start = new char[name.size() + 50];
8144 snprintf(start, name.size()+50, "<!-- start:%s -->", name.c_str());
8145 char* end = new char[name.size() + 50];
8146 snprintf(end, name.size()+50, "<!-- end:%s -->", name.c_str());
8147
8148 // Find the section boundaries
8149 size_t a1 = text.find(start);
8150 if (a1 == std::string::npos) {
8151 delete [] start;
8152 delete [] end;
8153 return 0;
8154 }
8155 size_t b1 = text.find(end);
8156 if (b1 == std::string::npos) {
8157 delete [] start;
8158 delete [] end;
8159 return 0;
8160 }
8161
8162 uint32 a2 = (uint32)(a1 + strlen(start));
8163 uint32 b2 = (uint32)(b1 + strlen(end));
8164
8165 // Copy it
8166 std::string section = text.substr(a2, b1-a2);
8167 std::string tmp;
8168 std::string result;
8169
8170 std::list<std::map<std::string, std::string> >::iterator it, itEnd;
8171 for (it = list.begin(), itEnd = list.end(); it != itEnd; ++it) {
8172 tmp = section;
8173 c += StringMultiReplace(tmp, *it, false);
8174 result += tmp;
8175 }
8176
8177 text.replace(a1, b2-a1, result);
8178 delete [] start;
8179 delete [] end;
8180 return c;
8181}
8182
8183
8184
8185unsigned char Hex2Char(const char* str) {
8186 unsigned char c = (unsigned char) strtol(str, NULL, 16);
8187 if (c == 0)
8188 return ' ';
8189 else
8190 return c;
8191}
8192
8193unsigned char Dec2Char(const char* str) {
8194 unsigned char c = (unsigned char) strtol(str, NULL, 10);
8195 if (c == 0)
8196 return ' ';
8197 else
8198 return c;
8199}
8200
8201int8 CompareFloats(float64 a, float64 b) {
8202 float64 diff = a - b;
8203 if ((diff < std::numeric_limits<float64>::epsilon()) && (-diff < std::numeric_limits<float64>::epsilon()))
8204 return 0;
8205 else if (diff > 0)
8206 return 1;
8207 else
8208 return -1;
8209}
8210
8211
8212#define poly 0xEDB88320
8213/* Some compilers need
8214 #define poly 0xEDB88320uL
8215 */
8216
8217/* On entry, addr=>start of data
8218 num = length of data
8219 crc = incoming CRC */
8220int CRC32(const char *addr, uint32 length, int32 crc) {
8221 uint32 i;
8222 for (; length>0; length--) { /* Step through bytes in memory */
8223 crc = crc ^ *addr++; /* Fetch byte from memory, XOR into CRC */
8224 for (i=0; i<8; i++) { /* Prepare to rotate 8 bits */
8225 if (crc & 1) /* b0 is set... */
8226 crc = (crc >> 1) ^ poly; /* rotate and XOR with ZIP polynomic */
8227 else /* b0 is clear... */
8228 crc >>= 1; /* just rotate */
8229 /* Some compilers need:
8230 crc &= 0xFFFFFFFF;
8231 */
8232 } /* Loop for 8 bits */
8233 } /* Loop until num=0 */
8234 return(crc); /* Return updated CRC */
8235}
8236
8237
8238std::string ReadAFileString(std::string filename) {
8239 uint32 length;
8240 char* data = ReadAFile(filename.c_str(), length, false);
8241 if (!data || !length)
8242 return "";
8243 std::string dataString = data;
8244 delete [] data;
8245 return dataString;
8246}
8247
8248char* ReadAFile(const char* dir, const char* filename, uint32& length, bool binary) {
8249 if (!dir || !filename || strlen(filename) == 0)
8250 return NULL;
8251
8252 char* fullname = new char[strlen(dir) + strlen(filename) + 5];
8253 snprintf(fullname, strlen(dir) + strlen(filename) + 5, "%s/%s", dir, filename);
8254 char* res = ReadAFile(fullname, length, binary);
8255 delete [] fullname;
8256 return res;
8257}
8258
8259char* ReadAFile(const char* filename, uint32& length, bool binary) {
8260 if (!filename || strlen(filename) == 0)
8261 return NULL;
8262
8263 length = 0;
8264
8265 FILE* file;
8266 if (binary)
8267 file = fopen(filename, "rb");
8268 else
8269 file = fopen(filename, "r");
8270 if (file == NULL) {
8271 //LogPrint(0,0,0,"Couldn't open file %s", filename);
8272 return NULL;
8273 }
8274
8275 fseek(file, 0, SEEK_END);
8276 length = ftell(file);
8277 fseek(file, 0, SEEK_SET);
8278
8279 if (length <= 0) {
8280 fclose(file);
8281 //LogPrint(0,0,0,"File %s has no size", filename);
8282 return NULL;
8283 }
8284
8285 char* data = new char[length+1];
8286
8287 int res = (int)fread(data, 1, length, file);
8288
8289 if ((res <= 0) || (res != length)) {
8290 int error = ferror(file);
8291 int eof = feof(file);
8292 if (eof == 0) {
8293 delete [] data;
8294 fclose(file);
8295 LogPrint(0,0,0,"File %s has no data", filename);
8296 return NULL;
8297 }
8298 }
8299
8300 fclose(file);
8301
8302 length = (uint32)res;
8303 data[length] = 0;
8304 return data;
8305}
8306
8307bool WriteAFile(const char* dir, const char* filename, const char* data, uint32 length, bool binary) {
8308 if (!dir || !filename || strlen(filename) == 0)
8309 return false;
8310
8311 char* fullname = new char[strlen(dir) + strlen(filename) + 5];
8312 snprintf(fullname, strlen(dir) + strlen(filename) + 5, "%s/%s", dir, filename);
8313 bool res = WriteAFile(fullname, data, length, binary);
8314 delete [] fullname;
8315 return res;
8316}
8317
8318bool WriteAFile(const char* filename, const char* data, uint32 length, bool binary) {
8319 if (!filename || strlen(filename) == 0)
8320 return false;
8321
8322 FILE* file;
8323 if (binary)
8324 file = fopen(filename, "wb");
8325 else
8326 file = fopen(filename, "w");
8327 if (file == NULL)
8328 return false;
8329
8330 int res = (int)fwrite(data, 1, length, file);
8331 fclose(file);
8332 return (res == length);
8333}
8334
8335bool AppendToAFile(const char* dir, const char* filename, const char* data, uint32 length, bool binary) {
8336 if (!dir || !filename || strlen(filename) == 0)
8337 return false;
8338
8339 char* fullname = new char[strlen(dir) + strlen(filename) + 5];
8340 snprintf(fullname, strlen(dir) + strlen(filename) + 5, "%s/%s", dir, filename);
8341 bool res = AppendToAFile(fullname, data, length, binary);
8342 delete [] fullname;
8343 return res;
8344}
8345
8346bool AppendToAFile(const char* filename, const char* data, uint32 length, bool binary) {
8347 if (!filename || strlen(filename) == 0)
8348 return false;
8349
8350 FILE* file;
8351 if (binary)
8352 file = fopen(filename, "ab");
8353 else
8354 file = fopen(filename, "a");
8355 if (file == NULL)
8356 return false;
8357
8358 int res = (int)fwrite(data, 1, length, file);
8359 fclose(file);
8360 return (res == length);
8361}
8362
8363bool DeleteAFile(const char* dir, const char* filename, bool force) {
8364 if (!dir || !filename || strlen(filename) == 0)
8365 return false;
8366
8367 char* fullname = new char[strlen(dir) + strlen(filename) + 5];
8368 snprintf(fullname, strlen(dir) + strlen(filename) + 5, "%s/%s", dir, filename);
8369 bool res = DeleteADir(fullname, force);
8370 delete [] fullname;
8371 return res;
8372}
8373
8374bool DeleteAFile(const char* filename, bool force) {
8375 return DeleteADir(filename, force);
8376}
8377
8378bool ChangeAFileAttr(const char* filename, bool read, bool write) {
8379 if (!filename || strlen(filename) == 0)
8380 return false;
8381 #ifdef WINDOWS
8382 if (!write)
8383 return (SetFileAttributes(filename, FILE_ATTRIBUTE_READONLY) != 0);
8384 else
8385 return (SetFileAttributes(filename, FILE_ATTRIBUTE_NORMAL) != 0);
8386 #else
8387 if (read && write)
8388 return (chmod(filename,S_IREAD | S_IWRITE) != 0);
8389 else if (read)
8390 return (chmod(filename,S_IREAD) == 0);
8391 else if (write)
8392 return (chmod(filename,S_IWRITE) == 0);
8393 else
8394 return (chmod(filename,0) == 0);
8395 #endif
8396}
8397
8398bool MoveAFile(const char* oldfilename, const char* newfilename, bool force) {
8399 #ifdef WINDOWS
8400 bool result;
8401 if (force)
8402 result = (MoveFileEx(oldfilename, newfilename, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0);
8403 else
8404 result = (MoveFileEx(oldfilename, newfilename, MOVEFILE_COPY_ALLOWED) != 0);
8405 if (result)
8406 return true;
8407 LogPrint(0,LOG_SYSTEM,0,"MoveAFile '%s' to '%s' failed with: %s", oldfilename, newfilename, GetLastOSErrorMessage().c_str());
8408 return false;
8409 #else
8410 if (rename(oldfilename, newfilename) == 0)
8411 return true;
8412 int error = errno;
8413 if (force) {
8414 FileDetails info = GetFileDetails(newfilename);
8415 if (info.doesExist) {
8416 delete(newfilename);
8417 if (rename(oldfilename, newfilename) == 0)
8418 return true;
8419 }
8420 }
8421 if (error == EXDEV) {
8422 if (CopyAFile(newfilename, oldfilename)) {
8423 DeleteAFile(oldfilename, true);
8424 return true;
8425 }
8426 }
8427 return false;
8428 #endif
8429}
8430
8431bool MoveAFile(const char* olddir, const char* oldfilename, const char* newdir, const char* newfilename, bool force) {
8432 if (!newdir || !newfilename || !strlen(newfilename) ||
8433 !olddir || !oldfilename || !strlen(oldfilename) )
8434 return false;
8435
8436 char* oldname = new char[strlen(olddir) + strlen(oldfilename) + 5];
8437 snprintf(oldname, strlen(olddir) + strlen(oldfilename) + 5, "%s/%s", olddir, oldfilename);
8438 char* newname = new char[strlen(newdir) + strlen(newfilename) + 5];
8439 snprintf(newname, strlen(newdir) + strlen(newfilename) + 5, "%s/%s", newdir, newfilename);
8440 bool res = MoveAFile(newname, oldname, force);
8441 delete [] oldname;
8442 delete [] newname;
8443 return res;
8444}
8445
8446bool CopyAFile(const char* oldfilename, const char* newfilename, bool force) {
8447 #ifdef WINDOWS
8448 if (force)
8449 return (CopyFile(oldfilename, newfilename, FALSE) != 0);
8450 else
8451 return (CopyFile(oldfilename, newfilename, TRUE) != 0);
8452 #else
8453 FileDetails target = GetFileDetails(newfilename);
8454 if (target.doesExist && !force)
8455 return false;
8456 if (target.isDirectory)
8457 return false;
8458 uint32 size;
8459 char* data = ReadAFile(oldfilename, size, true);
8460 if (!data || !size) {
8461 delete [] data;
8462 return false;
8463 }
8464
8465 if ((target.doesExist) && !DeleteAFile(newfilename, true)) {
8466 delete [] data;
8467 return false;
8468 }
8469
8470 if (!WriteAFile(newfilename, data, size, true)) {
8471 delete [] data;
8472 return false;
8473 }
8474 delete [] data;
8475 return true;
8476 #endif
8477}
8478
8479bool CopyAFile(const char* olddir, const char* oldfilename, const char* newdir, const char* newfilename, bool force) {
8480 if (!newdir || !newfilename || !strlen(newfilename) ||
8481 !olddir || !oldfilename || !strlen(oldfilename) )
8482 return false;
8483
8484 char* oldname = new char[strlen(olddir) + strlen(oldfilename) + 5];
8485 snprintf(oldname, strlen(olddir) + strlen(oldfilename) + 5, "%s/%s", olddir, oldfilename);
8486 char* newname = new char[strlen(newdir) + strlen(newfilename) + 5];
8487 snprintf(newname, strlen(newdir) + strlen(newfilename) + 5, "%s/%s", newdir, newfilename);
8488 bool res = CopyAFile(newname, oldname, force);
8489 delete [] oldname;
8490 delete [] newname;
8491 return res;
8492}
8493
8494bool CreateADir(const char* dirname) {
8495 if (!dirname || strlen(dirname) == 0)
8496 return false;
8497
8498 FileDetails info = GetFileDetails(dirname);
8499
8500 if (info.doesExist) {
8501 if (info.isDirectory)
8502 return true;
8503 else
8504 return false;
8505 }
8506
8507 #ifdef WINDOWS
8508 if (CreateDirectory(dirname, NULL) != 0)
8509 return true;
8510 else
8511 return false;
8512 #else
8513 if (mkdir(dirname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
8514 return true;
8515 else
8516 return false;
8517 #endif
8518}
8519
8520bool DeleteADir(const char* dirname, bool force) {
8521 if (!dirname || strlen(dirname) == 0)
8522 return false;
8523
8524 FileDetails info = GetFileDetails(dirname);
8525
8526 if (!info.doesExist)
8527 return true;
8528
8529 // If it is a file
8530 if (!info.isDirectory) {
8531 #ifdef WINDOWS
8532 if (DeleteFile(dirname) != 0)
8533 return true;
8534 else if (!force)
8535 return false;
8536 else {
8537 ChangeAFileAttr(dirname, true, true);
8538 if (DeleteFile(dirname) != 0)
8539 return true;
8540 else
8541 return false;
8542 }
8543 #else
8544 if (unlink(dirname) == 0)
8545 return true;
8546 else if (!force)
8547 return false;
8548 else {
8549 ChangeAFileAttr(dirname, true, true);
8550 if (unlink(dirname) == 0)
8551 return true;
8552 else
8553 return false;
8554 }
8555 #endif
8556 }
8557
8558 // Now we know it is a dir
8559 #ifdef WINDOWS
8560 if (RemoveDirectory(dirname) != 0)
8561 return true;
8562 else if (!force)
8563 return false;
8564 else {
8565 DeleteFilesInADir(dirname, true);
8566 if (RemoveDirectory(dirname) != 0)
8567 return true;
8568 else
8569 return false;
8570 }
8571 #else
8572 if (rmdir(dirname) == 0)
8573 return true;
8574 else if (!force)
8575 return false;
8576 else {
8577 DeleteFilesInADir(dirname, true);
8578 if (rmdir(dirname) == 0)
8579 return true;
8580 else
8581 return false;
8582 }
8583 #endif
8584
8585}
8586
8587bool DeleteFilesInADir(const char* dirname, bool force) {
8588 if (!dirname || strlen(dirname) == 0)
8589 return false;
8590
8591 uint32 count;
8592 char* files = utils::GetFileList(dirname, NULL, count, true);
8593 if (!files)
8594 return true;
8595
8596 const char* name;
8597 for (uint32 n = 0; n<count; n++) {
8598 name = files + (n*(MAXFILENAMELEN+1));
8599 if (!DeleteADir(name, force)) {
8600 delete[] files;
8601 return false;
8602 }
8603 }
8604
8605 delete [] files;
8606 return true;
8607}
8608
8609
8610
8611
8612
8613uint32 TextReplaceCharsInPlace(char* str, uint32 size, char find, char replace) {
8614 uint32 c = 0;
8615 for (uint32 n=0; n<size; n++) {
8616 if (str[n] == find) {
8617 str[n] = replace;
8618 c++;
8619 }
8620 }
8621 return c;
8622}
8623
8624const char* laststrstr(const char* str1, const char* str2) {
8625 const char* strp;
8626 int len1, len2;
8627
8628 len2 = (int)strlen(str2);
8629 if(len2==0)
8630 return (char*)str1;
8631
8632 len1 = (int)strlen(str1);
8633 if (len1 == len2)
8634 return (strcmp(str1, str2) == 0) ? str1 : 0;
8635 else if (len1 - len2 < 0)
8636 return 0;
8637
8638 strp = (char*)(str1 + len1 - len2);
8639 while(strp != str1) {
8640 if(*strp == *str2) {
8641 if(strncmp(strp,str2,len2)==0)
8642 return strp;
8643 }
8644 strp--;
8645 }
8646 return 0;
8647}
8648
8649const char* laststristr(const char* str1, const char* str2) {
8650 const char* strp;
8651 int len1, len2;
8652
8653 len2 = (int)strlen(str2);
8654 if(len2==0)
8655 return (char*)str1;
8656
8657 len1 = (int)strlen(str1);
8658 if (len1 == len2)
8659 return (stricmp(str1, str2) == 0) ? str1 : 0;
8660 else if (len1 - len2 < 0)
8661 return 0;
8662
8663 strp = (char*)(str1 + len1 - len2);
8664 while(strp != str1) {
8665 if(*strp == *str2) {
8666 if(strnicmp(strp,str2,len2)==0)
8667 return strp;
8668 }
8669 strp--;
8670 }
8671 return 0;
8672}
8673
8674bool TextEndsWith(const char* str, const char* end, bool caseSensitive) {
8675 if (!caseSensitive)
8676 return (laststristr(str, end) == str + strlen(str) - strlen(end));
8677 else
8678 return (laststrstr(str, end) == str + strlen(str) - strlen(end));
8679}
8680
8681bool TextStartsWith(const char* str, const char* start, bool caseSensitive) {
8682 if (!caseSensitive)
8683 return (stristr(str, start) == str);
8684 else
8685 return (strstr(str, start) == str);
8686}
8687
8688
8689std::string GetFilePath(const char* filename) {
8690 std::string name = filename;
8691 std::string::size_type p1 = name.find_last_of('/');
8692 std::string::size_type p2 = name.find_last_of('\\');
8693 if (p1 == std::string::npos) {
8694 if (p2 == std::string::npos)
8695 return "";
8696 else
8697 return name.substr(0, p2 + 1);
8698 }
8699 else if (p2 == std::string::npos)
8700 return name.substr(0, p1 + 1);
8701 else
8702 return name.substr(0, (p1 > p2) ? p1 + 1 : p2 + 1);
8703}
8704
8705const char* GetFileBasename(const char* filename) {
8706 std::string name = filename;
8707 std::string::size_type p1 = name.find_last_of('/');
8708 std::string::size_type p2 = name.find_last_of('\\');
8709 if (p1 == std::string::npos) {
8710 if (p2 == std::string::npos)
8711 return filename;
8712 else
8713 return filename + p2 + 1;
8714 }
8715 else if (p2 == std::string::npos)
8716 return filename + p1 + 1;
8717 else
8718 return (p1 > p2) ? filename + p1 + 1 : filename + p2 + 1;
8719}
8720
8721std::string GetCurrentDir() {
8722
8723 char cCurrentPath[FILENAME_MAX];
8724
8725 if (!GetCurrentDirEx(cCurrentPath, sizeof(cCurrentPath)))
8726 {
8727 return "";
8728 }
8729 cCurrentPath[sizeof(cCurrentPath) - 1] = '\0'; /* not really required */
8730 //printf("The current working directory is %s\n", cCurrentPath);
8731 return cCurrentPath;
8732}
8733
8734char* GetFileList(const char* dirname, const char* ext, uint32& count, bool fullpath, uint32 maxNameLen) {
8735 DIR* dir = opendir(dirname);
8736 if (dir == NULL)
8737 return NULL;
8738
8739 uint32 extlen = (ext == NULL) ? 0 : (uint32)strlen(ext);
8740 struct dirent* direntry = NULL;
8741
8742 uint32 tempMaxCount = 50;
8743 uint32 len = (maxNameLen+1)*tempMaxCount;
8744 char* result = new char[len];
8745 char* dst = result;
8746 char* tmp;
8747 uint32 dirnamelen = (uint32)strlen(dirname);
8748 bool dirslash = (*(dirname + dirnamelen - 1) == '/') || (*(dirname + dirnamelen - 1) == '\\');
8749
8750 count = 0;
8751 direntry = readdir(dir);
8752 while (direntry) {
8753 #ifdef WINDOWS
8754 if (direntry->d_name && direntry->d_name[0] != '\0') {
8755 #else
8756 if (direntry->d_name[0] != '\0') {
8757 #endif
8758 if ( !strcmp(direntry->d_name, ".") || !strcmp(direntry->d_name, "..") ) {
8759 direntry = readdir(dir);
8760 continue;
8761 }
8762 if (ext && extlen && !TextEndsWith(direntry->d_name, ext)) {
8763 direntry = readdir(dir);
8764 continue;
8765 }
8766 if (fullpath) {
8767 utils::strcpyavail(dst, dirname, maxNameLen, true);
8768 if (!dirslash) {
8769 memset(dst+dirnamelen, '/', 1);
8770 utils::strcpyavail(dst+dirnamelen+1, direntry->d_name, maxNameLen, true);
8771 }
8772 else
8773 utils::strcpyavail(dst+dirnamelen, direntry->d_name, maxNameLen, true);
8774 }
8775 else
8776 utils::strcpyavail(dst, direntry->d_name, maxNameLen, true);
8777 dst+= maxNameLen+1;
8778 count++;
8779 if (count >= tempMaxCount) {
8780 tempMaxCount *= 2;
8781 tmp = new char[len*2];
8782 memcpy(tmp, result, len);
8783 delete [] result;
8784 result = tmp;
8785 dst = result + len;
8786 len *= 2;
8787 }
8788 }
8789 direntry = readdir(dir);
8790 }
8791 closedir(dir);
8792 return result;
8793}
8794
8795bool DoesADirExist(const char* dirname) {
8796 FileDetails details = GetFileDetails(dirname);
8797 return (details.doesExist && details.isDirectory);
8798}
8799
8800bool DoesAFileExist(const char* filename) {
8801 FileDetails details = GetFileDetails(filename);
8802 return (details.doesExist && !details.isDirectory);
8803}
8804
8805FileDetails GetFileDetails(const char* filename) {
8806 struct stat statbuf;
8807 int fd, result;
8808
8809 FileDetails info;
8810
8811 info.doesExist = false;
8812 info.isDirectory = false;
8813 info.isReadable = false;
8814 info.isWritable = false;
8815 info.isExecutable = false;
8816 info.size = 0;
8817 info.lastAccessTime = 0;
8818 info.lastModifyTime = 0;
8819 info.creationTime = 0;
8820
8821 // If file cannot be opened
8822 if ((fd = _open(filename, O_RDONLY)) == -1) {
8823 // On Windows, this might be a dir
8824 DIR* dir = opendir(filename);
8825 if (dir == NULL)
8826 return info;
8827 struct dirent* direntry = readdir(dir);
8828
8829 info.doesExist = true;
8830 info.isDirectory = true;
8832 info.isReadable = true;
8833 info.isWritable = true;
8834 info.isExecutable = true;
8835
8836 closedir(dir);
8837 return info;
8838 }
8839
8840 // Get data associated with "fd":
8841 result = fstat(fd, &statbuf);
8842
8843 // Check if statistics are valid:
8844 if( result != 0 ) {
8845 _close(fd);
8846 return info;
8847 }
8848
8849 _close(fd);
8850 info.doesExist = true;
8851
8852 info.size = statbuf.st_size;
8853 info.isDirectory = ((statbuf.st_mode & S_IFDIR) != 0);
8854 info.lastAccessTime = FTime2PsyTime(statbuf.st_atime);
8855 info.lastModifyTime = FTime2PsyTime(statbuf.st_ctime);
8856 info.creationTime = FTime2PsyTime(statbuf.st_ctime);
8857
8858 #ifdef WINDOWS
8859 info.isReadable = ((statbuf.st_mode & _S_IREAD) != 0);
8860 info.isWritable = ((statbuf.st_mode & _S_IWRITE) != 0);
8861 info.isExecutable = ((statbuf.st_mode & _S_IEXEC) != 0);
8862 #else
8863 info.isReadable = ((statbuf.st_mode & S_IRUSR) != 0);
8864 info.isWritable = ((statbuf.st_mode & S_IWUSR) != 0);
8865 info.isExecutable = ((statbuf.st_mode & S_IXUSR) != 0);
8866 #endif
8867
8868 return info;
8869
8870}
8871
8872char* TextSubstringCopy(const char* ascii, uint32 start, uint32 end) {
8873 uint32 len;
8874 char* copy;
8875 if (!ascii || (start >= end) || (!(len = (uint32)strlen(ascii))) ) {
8876 copy = new char[1];
8877 copy[0] = 0;
8878 }
8879 else {
8880 if (end > len - 1)
8881 end = len - 1;
8882 copy = new char[end - start + 2];
8883 memcpy(copy, ascii + start,
8884 end - start + 1);
8885 copy[end - start + 1] = 0;
8886 }
8887 return copy;
8888}
8889
8890
8891bool IsTextNumeric(const char* ascii, uint32 start, uint32 end) {
8892 if (!ascii || (start > end)) return false;
8893 uint32 len = (uint32)strlen(ascii);
8894 uint32 stop = end ? end+1 : len;
8895 if (stop > len) stop = len;
8896 for (uint32 n=start; n<stop; n++) {
8897 if (( ascii[n] < '0' || ascii[n] > '9') && ascii[n] != '.')
8898 return false;
8899 }
8900 return true;
8901}
8902
8903int64 Ascii2Int64(const char* ascii, uint32 start, uint32 end) {
8904 if (!ascii || (start > end)) return false;
8905 if (!end)
8906 #ifdef WINDOWS
8907 return _strtoi64(ascii + start, NULL, 10);
8908 #else
8909 return strtoll(ascii + start, NULL, 10);
8910 #endif
8911 char* copy = TextSubstringCopy(ascii, start, end);
8912 #ifdef WINDOWS
8913 int64 val = _strtoi64(copy, NULL, 10);
8914 #else
8915 int64 val = strtoll(copy, NULL, 10);
8916 #endif
8917 delete[] copy;
8918 return val;
8919}
8920
8921uint64 Ascii2Uint64(const char* ascii, uint32 start, uint32 end) {
8922 if (!ascii || (start > end)) return false;
8923 if (!end)
8924 #ifdef WINDOWS
8925 return _strtoui64(ascii + start, NULL, 10);
8926 #else
8927 return strtoull(ascii + start, NULL, 10);
8928 #endif
8929 char* copy = TextSubstringCopy(ascii, start, end);
8930 #ifdef WINDOWS
8931 uint64 val = _strtoui64(copy, NULL, 10);
8932 #else
8933 uint64 val = strtoull(copy, NULL, 10);
8934 #endif
8935 delete[] copy;
8936 return val;
8937}
8938
8939uint64 AsciiHex2Uint64(const char* ascii, uint32 start, uint32 end) {
8940 if (!ascii || (start > end)) return false;
8941 if (!end)
8942 #ifdef WINDOWS
8943 return _strtoui64(ascii + start, NULL, 16);
8944 #else
8945 return strtoull(ascii + start, NULL, 16);
8946 #endif
8947 char* copy = TextSubstringCopy(ascii, start, end);
8948 #ifdef WINDOWS
8949 uint64 val = _strtoui64(copy, NULL, 16);
8950 #else
8951 uint64 val = strtoull(copy, NULL, 16);
8952 #endif
8953 delete[] copy;
8954 return val;
8955}
8956
8957int32 Ascii2Int32(const char* ascii, uint32 start, uint32 end) {
8958 if (!ascii || (start > end)) return false;
8959 if (!end)
8960 return (int32)strtol(ascii + start, NULL, 10);
8961 char* copy = TextSubstringCopy(ascii, start, end);
8962 int32 val = (int32)strtol(copy, NULL, 10);
8963 delete[] copy;
8964 return val;
8965}
8966
8967uint32 Ascii2Uint32(const char* ascii, uint32 start, uint32 end) {
8968 if (!ascii || (start > end)) return false;
8969 if (!end)
8970 return (uint32)strtoul(ascii + start, NULL, 10);
8971 char* copy = TextSubstringCopy(ascii, start, end);
8972 uint32 val = (uint32)strtoul(copy, NULL, 10);
8973 delete[] copy;
8974 return val;
8975}
8976
8977uint32 AsciiHex2Uint32(const char* ascii, uint32 start, uint32 end) {
8978 if (!ascii || (start > end)) return false;
8979 if (!end)
8980 return (uint32)strtoul(ascii + start, NULL, 16);
8981 char* copy = TextSubstringCopy(ascii, start, end);
8982 uint32 val = (uint32)strtoul(copy, NULL, 16);
8983 delete[] copy;
8984 return val;
8985}
8986
8987float64 Ascii2Float64(const char* ascii, uint32 start, uint32 end) {
8988 if (!ascii || (start > end)) return false;
8989 if (!end)
8990 return strtod(ascii + start, NULL);
8991 char* copy = TextSubstringCopy(ascii, start, end);
8992 float64 val = strtod(copy, NULL);
8993 delete[] copy;
8994 return val;
8995}
8996
8997
8998std::string DecodeJSON(std::string str) {
8999 bool isEscaped = false;
9000 char* tmp = new char[5];
9001 tmp[4] = 0;
9002 std::ostringstream o;
9003 std::string::iterator c;
9004 for (c = str.begin(); c != str.end(); c++) {
9005 if (isEscaped) {
9006 switch (*c) {
9007 case '"': o << "\""; break;
9008 case '\\': o << "\\"; break;
9009 case 'b': o << "\b"; break;
9010 case 'f': o << "\f"; break;
9011 case 'n': o << "\n"; break;
9012 case 'r': o << "\r"; break;
9013 case 't': o << "\t"; break;
9014 case 'u':
9015 c++;
9016 tmp[0] = (uint8)(*c++);
9017 tmp[1] = (uint8)(*c++);
9018 tmp[2] = (uint8)(*c++);
9019 tmp[3] = (uint8)(*c);
9020 o << (char) strtol(tmp, NULL, 16);
9021 break;
9022 default: o << *c;
9023 }
9024 isEscaped = false;
9025 }
9026 else {
9027 switch (*c) {
9028 case '\\': isEscaped = true; break;
9029 default: o << *c;
9030 }
9031 }
9032 }
9033 delete[] tmp;
9034 return o.str();
9035}
9036
9037std::string EncodeJSON(std::string str) {
9038 std::ostringstream o;
9039 std::string::iterator c;
9040 for (c = str.begin(); c != str.end(); c++) {
9041 switch (*c) {
9042 case '"': o << "\\\""; break;
9043 case '\\': o << "\\\\"; break;
9044 case '\b': o << "\\b"; break;
9045 case '\f': o << "\\f"; break;
9046 case '\n': o << "\\n"; break;
9047 case '\r': o << "\\r"; break;
9048 case '\t': o << "\\t"; break;
9049 default:
9050 if ('\x00' <= *c && *c <= '\x1f') {
9051 o << "\\u"
9052 << std::hex << std::setw(4) << std::setfill('0') << (int)*c;
9053 } else {
9054 o << *c;
9055 }
9056 }
9057 }
9058 return o.str();
9059
9060 //if (str.size() == 0)
9061 // return str;
9062 //std::string result = str;
9063 //char c;
9064 //char* tmp = (char*)malloc(20);
9065
9066 //for (uint32 n=0; n<result.size(); n++) {
9067 // switch(c = result[n]) {
9068 // case '\n':
9069 // result.replace(n, 1, "\\n");
9070 // n+=1;
9071 // break;
9072 // case '\r':
9073 // result.replace(n, 1, "\\r");
9074 // n+=1;
9075 // break;
9076 // case '\t':
9077 // result.replace(n, 1, "\\t");
9078 // n+=2;
9079 // break;
9080 // case '"':
9081 // result.replace(n, 1, "\\\"");
9082 // n+=2;
9083 // break;
9084 // case '\\':
9085 // result.replace(n, 1, "\\\\");
9086 // n+=2;
9087 // break;
9088 // case '\b':
9089 // result.replace(n, 1, "\\b");
9090 // n+=2;
9091 // break;
9092 // case '\f':
9093 // result.replace(n, 1, "\\f");
9094 // n+=2;
9095 // break;
9096 // default:
9097 // if ('\x00' <= c && c <= '\x1f') {
9098 // snprintf(tmp, 20, "\\u%.4x;", (int) c & 0xFF);
9099 // result.replace(n, 1, tmp);
9100 // n+=(uint32)strlen(tmp)-1;
9101 // }
9102 // break;
9103 // }
9104 //}
9105
9106 //free((char*)tmp);
9107
9108 //return result;
9109}
9110
9111
9112// Seeds the rand() function
9113bool SeedRandomValues(uint32 seedvalue) {
9114 srand( seedvalue ? seedvalue : (uint32)GetTimeNow());
9115 return true;
9116}
9117
9118// Returns a random value between 0.0 and 1.0
9119double RandomValue() {
9120 double d = 0;
9121 double t = (1.0/((RAND_MAX + 1.0)*(RAND_MAX + 1.0)*(RAND_MAX + 1.0)));
9122 do {
9123 d = (rand () * ((RAND_MAX + 1.0) * (RAND_MAX + 1.0))
9124 + rand () * (RAND_MAX + 1.0)
9125 + rand ()) * t;
9126 } while (d >= 1); /* Round off */
9127 return d;
9128}
9129
9130// Returns a random value between 0.0 and max
9131double RandomValue(double max) {
9132 return RandomValue() * max;
9133}
9134
9135// Returns a random value between from and to
9136double RandomValue(double from, double to) {
9137 if (to < from)
9138 return (RandomValue()*(from-to))+to;
9139 else
9140 return (RandomValue()*(to-from))+from;
9141}
9142
9143// Returns a random value between from and to, rounded to nearest interval
9144double RandomValue(double from, double to, double interval) {
9145 if (to < from)
9146 return RandomValue(to, from, interval);
9147 double r = RandomValue(0, to-from);
9148 uint64 n = (uint64)(r/interval);
9149 double l = r - (n*interval);
9150 if (l >= (0.5*interval))
9151 return from + (n*interval);
9152 else
9153 return from + ((n+1)*interval);
9154}
9155
9156int64 RandomInt(int64 from, int64 to) {
9157 return (int64)roundl(RandomValue((double)from, (double)to));
9158}
9159
9160
9161//std::string BytifySize(int32 val) {
9162// return BytifySize((double)val);
9163//}
9164
9165std::string BytifySize(double val) {
9166
9167 double kb = 1024;
9168 double mb = kb*1024;
9169 double gb = mb*1024;
9170 double tb = gb*1024;
9171
9172 char *tmp = new char[64];
9173
9174 if (val < 512)
9175 snprintf(tmp, 64, "%.2f B", val);
9176 else if (val < 512*kb) {
9177 val = val / kb;
9178 snprintf(tmp, 64, "%.2f KB", val);
9179 }
9180 else if (val < 512*mb) {
9181 val = val / mb;
9182 snprintf(tmp, 64, "%.2f MB", val);
9183 }
9184 else if (val < 512*gb) {
9185 val = val / gb;
9186 snprintf(tmp, 64, "%.2f GB", val);
9187 }
9188 else {
9189 val = val / tb;
9190 snprintf(tmp, 64, "%.2f TB", val);
9191 }
9192
9193 std::string result = tmp;
9194 delete [] tmp;
9195 return result;
9196}
9197
9198//std::string BytifySizes(int32 val1, int32 val2) {
9199// return BytifySizes((double)val1, (double) val2);
9200//}
9201
9202std::string BytifySizes(double val1, double val2) {
9203
9204 double kb = 1024;
9205 double mb = kb*1024;
9206 double gb = mb*1024;
9207 double tb = gb*1024;
9208
9209 char *tmp = new char[64];
9210 char *end = new char[64];
9211
9212 double val;
9213 double maximum = ((val1 > val2) ? val1 : val2);
9214
9215 if (maximum < 512) {
9216 val = 1;
9217 utils::strcpyavail(end, "B", 32, true);
9218 }
9219 else if (maximum < 512*kb) {
9220 val = kb;
9221 utils::strcpyavail(end, "KB", 32, true);
9222 }
9223 else if (maximum < 512*mb) {
9224 val = mb;
9225 utils::strcpyavail(end, "MB", 32, true);
9226 }
9227 else if (maximum < 512*gb) {
9228 val = gb;
9229 utils::strcpyavail(end, "GB", 32, true);
9230 }
9231 else {
9232 val = gb;
9233 utils::strcpyavail(end, "TB", 32, true);
9234 }
9235 snprintf(tmp, 64, "%.2f / %.2f %s", ((double)val1)/val, ((double)val2)/val, (char*) end);
9236
9237 std::string result = tmp;
9238 delete [] tmp;
9239 delete [] end;
9240 return result;
9241
9242}
9243
9244//std::string BytifyRate(int32 val) {
9245// return BytifyRate((double)val);
9246//}
9247
9248std::string BytifyRate(double val) {
9249 char *tmp = new char[64];
9250 snprintf(tmp, 64, "%s/sec", (char*) BytifySize(val).c_str());
9251 std::string result = tmp;
9252 delete [] tmp;
9253 return result;
9254}
9255
9256//std::string BytifyRates(int32 val1, int32 val2) {
9257// return BytifyRates((double)val1, (double)val2);
9258//}
9259
9260std::string BytifyRates(double val1, double val2) {
9261 char *tmp = new char[64];
9262 snprintf(tmp, 64, "%s/sec", (char*) BytifySizes(val1, val2).c_str());
9263 std::string result = tmp;
9264 delete [] tmp;
9265 return result;
9266}
9267
9268
9269
9270
9271
9272
9273
9274#ifdef WINDOWS
9275
9276uint32 ReadRegistryDWORD(const char* key, const char* entry) {
9277 HKEY hKeyRoot, hKey;
9278 std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);
9279
9280 uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
9281 if (err != ERROR_SUCCESS)
9282 return 0;
9283
9284 DWORD dwType = REG_DWORD;
9285 DWORD numVal = 0;
9286 DWORD dwSize = sizeof(numVal);
9287
9288 err = RegQueryValueEx(hKey, entry, NULL, &dwType, (LPBYTE)&numVal, &dwSize);
9289 RegCloseKey(hKey);
9290 if (err != ERROR_SUCCESS)
9291 return 0;
9292 return numVal;
9293}
9294
9295uint64 ReadRegistryQWORD(const char* key, const char* entry) {
9296 HKEY hKeyRoot, hKey;
9297 std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);
9298
9299 uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
9300 if (err != ERROR_SUCCESS)
9301 return 0;
9302
9303 DWORD dwType = REG_QWORD;
9304 uint64 numVal = 0;
9305 DWORD dwSize = sizeof(numVal);
9306
9307 err = RegQueryValueEx(hKey, entry, NULL, &dwType, (LPBYTE)&numVal, &dwSize);
9308 RegCloseKey(hKey);
9309 if (err != ERROR_SUCCESS)
9310 return 0;
9311 return numVal;
9312}
9313
9314std::string ReadRegistryString(const char* key, const char* entry) {
9315 HKEY hKeyRoot, hKey;
9316 std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);
9317
9318 uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
9319 if (err != ERROR_SUCCESS) {
9320 return "";
9321 }
9322
9323 DWORD dwType = REG_SZ;
9324 DWORD dwSize = 4096;
9325 char* lszValue = new char[dwSize];
9326
9327 err = RegQueryValueEx(hKey, entry, NULL, &dwType, (LPBYTE)lszValue, &dwSize);
9328 RegCloseKey(hKey);
9329 if (err != ERROR_SUCCESS) {
9330 delete[] lszValue;
9331 return "";
9332 }
9333
9334 std::string val = lszValue;
9335 delete[] lszValue;
9336 return val;
9337}
9338
9339bool WriteRegistryDWORD(const char* key, const char* entry, uint32 value) {
9340 DWORD dwDisposition;
9341 HKEY hKeyRoot, hKey;
9342 std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);
9343
9344 uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_WRITE | KEY_WOW64_64KEY, &hKey);
9345 if (err != ERROR_SUCCESS) {
9346 err = RegCreateKeyEx(hKeyRoot, keySubName.c_str(), 0, NULL, 0, KEY_WRITE | KEY_WOW64_64KEY, NULL, &hKey, &dwDisposition);
9347 if (err != ERROR_SUCCESS)
9348 return false;
9349 }
9350
9351 DWORD dwType = REG_DWORD;
9352 err = RegSetValueEx(hKey, entry, 0, dwType, (const BYTE*)&value, sizeof(value));
9353 RegCloseKey(hKey);
9354 return (err == ERROR_SUCCESS);
9355}
9356
9357bool WriteRegistryQWORD(const char* key, const char* entry, uint64 value) {
9358 DWORD dwDisposition;
9359 HKEY hKeyRoot, hKey;
9360 std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);
9361
9362 uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_WRITE | KEY_WOW64_64KEY, &hKey);
9363 if (err != ERROR_SUCCESS) {
9364 err = RegCreateKeyEx(hKeyRoot, keySubName.c_str(), 0, NULL, 0, KEY_WRITE | KEY_WOW64_64KEY, NULL, &hKey, &dwDisposition);
9365 if (err != ERROR_SUCCESS)
9366 return false;
9367 }
9368
9369 DWORD dwType = REG_QWORD;
9370 err = RegSetValueEx(hKey, entry, 0, dwType, (const BYTE*)&value, sizeof(value));
9371 RegCloseKey(hKey);
9372 return (err == ERROR_SUCCESS);
9373}
9374
9375bool WriteRegistryString(const char* key, const char* entry, const char* value) {
9376 DWORD dwDisposition;
9377 HKEY hKeyRoot, hKey;
9378 std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);
9379
9380 uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_WRITE | KEY_WOW64_64KEY, &hKey);
9381 if (err != ERROR_SUCCESS) {
9382 err = RegCreateKeyEx(hKeyRoot, keySubName.c_str(), 0, NULL, 0, KEY_WRITE | KEY_WOW64_64KEY, NULL, &hKey, &dwDisposition);
9383 if (err != ERROR_SUCCESS)
9384 return false;
9385 }
9386
9387 DWORD dwType = REG_SZ;
9388 DWORD dwSize = 4096;
9389
9390 err = RegSetValueEx(hKey, entry, 0, dwType, (LPBYTE)value, (DWORD)(strlen(value) + 1));
9391 RegCloseKey(hKey);
9392 return (err == ERROR_SUCCESS);
9393}
9394
9395bool DeleteRegistryEntry(const char* key, const char* entry) {
9396 HKEY hKeyRoot, hKey;
9397 std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);
9398
9399 uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_SET_VALUE | KEY_WOW64_64KEY, &hKey);
9400 if (err != ERROR_SUCCESS)
9401 return false;
9402
9403 //std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);
9404 err = RegDeleteValue(hKey, entry);
9405 return (err == ERROR_SUCCESS);
9406}
9407
9408bool DeleteRegistryKey(const char* entry) {
9409 // https://msdn.microsoft.com/en-us/library/aa379776(VS.85).aspx
9410
9411 HKEY hKeyRoot;
9412 std::string keySubName = GetRegistryKeyInfo(entry, hKeyRoot);
9413 uint32 err = RegDeleteKeyEx(hKeyRoot, keySubName.c_str(), KEY_WOW64_64KEY, 0);
9414 return (err == ERROR_SUCCESS);
9415}
9416
9417bool DeleteRegistryTree(const char* root, const char* key) {
9418 if (!key || !strlen(key))
9419 return false;
9420
9421 HKEY hKey;
9422 HKEY hKeyRoot;
9423 std::string keySubName = GetRegistryKeyInfo(root, hKeyRoot);
9424 uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, DELETE | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_WOW64_64KEY, &hKey);
9425 if (err != ERROR_SUCCESS)
9426 return false;
9427
9428 err = RegDeleteTree(hKey, key);
9429 return (err == ERROR_SUCCESS);
9430}
9431
9432//bool CopyRegistryKey(const char* key, const char* newName) {
9433//}
9434
9435bool CopyRegistryTree(const char* entry, const char* newName) {
9436 // https://msdn.microsoft.com/en-us/library/aa379768(VS.85).aspx
9437
9438 // access 64 or 32-bit registry: https://msdn.microsoft.com/en-us/library/aa384129(v=vs.85).aspx
9439 // we always use the 64-bit registry here
9440
9441 DWORD dwDisposition;
9442 HKEY hKey;
9443 HKEY hKeyRoot;
9444 HKEY hKeyNew;
9445 HKEY hKeyNewRoot;
9446 uint32 err;
9447
9448 std::string keySubName = GetRegistryKeyInfo(entry, hKeyRoot);
9449 std::string keySubNewName = GetRegistryKeyInfo(newName, hKeyNewRoot);
9450
9451 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
9452 if (err != ERROR_SUCCESS)
9453 return false;
9454
9455 err = RegCreateKeyEx(hKeyNewRoot, keySubNewName.c_str(), 0, NULL, 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, NULL, &hKeyNew, &dwDisposition);
9456 if (err != ERROR_SUCCESS) {
9457 RegCloseKey(hKey);
9458 return false;
9459 }
9460
9461 err = RegCopyTree(hKey, NULL, hKeyNew);
9462
9463 RegCloseKey(hKey);
9464 RegCloseKey(hKeyNew);
9465
9466 if (err != ERROR_SUCCESS) {
9467 RegDeleteKeyEx(hKeyNewRoot, keySubNewName.c_str(), KEY_WOW64_64KEY, 0);
9468 return false;
9469 }
9470 return true;
9471}
9472
9473//bool RenameRegistryKey(const char* key, const char* newName) {
9474// if (!CopyRegistryKey(key, newName))
9475// return false;
9476// if (!DeleteRegistryKey(key)) {
9477// CopyRegistryKey(newName, key);
9478// return false;
9479// }
9480// return true;
9481//}
9482
9483//bool RenameRegistryTree(const char* entry, const char* newName) {
9484 //if (!CopyRegistryTree(entry, newName))
9485 // return false;
9486 //if (!DeleteRegistryTree(entry)) {
9487 // CopyRegistryTree(newName, entry);
9488 // return false;
9489 //}
9490// return true;
9491//}
9492
9493std::string GetRegistryKeyInfo(const char* key, HKEY &hKeyRoot) {
9494 std::vector<std::string> path = utils::TextListSplit(key, "\\");
9495 if (!path.size()) path = utils::TextListSplit(key, "/");
9496 if (path.size() < 2) return false;
9497 if (stristr(path.at(0).c_str(), "HKEY_CLASSES_ROOT"))
9498 hKeyRoot = HKEY_CLASSES_ROOT;
9499 else if (stristr(path.at(0).c_str(), "HKEY_CURRENT_USER"))
9500 hKeyRoot = HKEY_CURRENT_USER;
9501 else if (stristr(path.at(0).c_str(), "HKEY_LOCAL_MACHINE"))
9502 hKeyRoot = HKEY_LOCAL_MACHINE;
9503 else if (stristr(path.at(0).c_str(), "HKEY_USERS"))
9504 hKeyRoot = HKEY_USERS;
9505 return utils::TextJoin(path, "\\", 1, (DWORD)(path.size() - 1));
9506}
9507
9508std::string GetRegistryParentKeyInfo(const char* key, HKEY &hKeyRoot) {
9509 std::vector<std::string> path = utils::TextListSplit(key, "\\");
9510 if (!path.size()) path = utils::TextListSplit(key, "/");
9511 if (path.size() < 2) return false;
9512 if (stristr(path.at(0).c_str(), "HKEY_CLASSES_ROOT"))
9513 hKeyRoot = HKEY_CLASSES_ROOT;
9514 else if (stristr(path.at(0).c_str(), "HKEY_CURRENT_USER"))
9515 hKeyRoot = HKEY_CURRENT_USER;
9516 else if (stristr(path.at(0).c_str(), "HKEY_LOCAL_MACHINE"))
9517 hKeyRoot = HKEY_LOCAL_MACHINE;
9518 else if (stristr(path.at(0).c_str(), "HKEY_USERS"))
9519 hKeyRoot = HKEY_USERS;
9520 return utils::TextJoin(path, "\\", 1, (DWORD)(path.size() - 2));
9521}
9522
9523
9524#else // WINDOWS
9525 int32 ReadRegistryDWORD(const char* key) { return 0; }
9526 int64 ReadRegistryQWORD(const char* key) { return 0; }
9527 std::string ReadRegistryString(const char* key) { return ""; }
9528 bool WriteRegistryDWORD(const char* key, int32 value) { return false; }
9529 bool WriteRegistryQWORD(const char* key, int64 value) { return false; }
9530 bool WriteRegistryString(const char* key, const char* value) { return false; }
9531 bool DeleteRegistryKey(const char* key) { return false; }
9532 bool DeleteRegistryTree(const char* key) { return false; }
9533 //bool CopyRegistryKey(const char* key, const char* newName) { return false; }
9534 bool CopyRegistryTree(const char* entry, const char* newName) { return false; }
9535 //bool RenameRegistryKey(const char* key, const char* newName) { return false; }
9536 bool RenameRegistryTree(const char* entry, const char* newName) { return false; }
9537#endif // WINDOWS
9538
9539
9540
9541} // namespace utils
9542
9543// Adapters so the utils:: free-function tests can be registered as bool() pointers.
9544static bool Test_Utils() { return utils::UnitTest_Utils(); }
9546static bool Test_Timer() { return utils::UnitTest_Timer(); }
9547
9550 "Bitfield slot allocation: SetBit/SetBitN, first-free and last-occupied searches", "util");
9552 "Timer scheduling and trigger delivery via waitForTimer", "util");
9554 "ProbeProcessWait: stdin-blocked child detected, busy child not flagged, cpuTimeMs grows", "util");
9555}
9556
9557} // namespace cmlabs
9558
9559
HTML/URL helper utilities: entity encoding/decoding, MIME type lookup and URL component parsing.
#define PROC_ERROR
Error state.
Definition MemoryMaps.h:153
Object type ids used to tag and verify every binary structure in CMSDK memory.
#define CONSTCHARID
Definition ObjectIDs.h:38
#define CHARDATAID
Definition ObjectIDs.h:40
#define CONSTCHARINFOID
Definition ObjectIDs.h:43
#define DOUBLEID
Definition ObjectIDs.h:42
#define DATAMESSAGEINFOID
Definition ObjectIDs.h:69
#define LOGENTRYID
Definition ObjectIDs.h:55
#define DATAMESSAGEID
Definition ObjectIDs.h:75
#define INTID
Definition ObjectIDs.h:41
#define CHARDATAINFOID
Definition ObjectIDs.h:44
#define TIMEID
Definition ObjectIDs.h:39
#define strnicmp
Definition Standard.h:188
Process-wide thread registry and lifecycle manager: the concurrency core of CMSDK.
#define BITOCCUPIED
Definition Types.h:34
#define BITFREE
Definition Types.h:33
#define MAXVALUINT16
Definition Types.h:85
#define MAXVALUINT32
Definition Types.h:87
Small, dependency-free unit test harness used by all CMSDK object tests.
#define poly
Definition Utils.cpp:8212
Cross-platform utility toolbox for CMSDK: threading, synchronization, shared memory,...
#define LOGPRINT
Definition Utils.h:210
#define WINSHMEMSPACE
Definition Utils.h:82
#define SD_BOTH
Definition Utils.h:151
#define MAXKEYNAMELEN
Definition Utils.h:85
#define OSCPU_IA64
Definition Utils.h:157
#define LOG_SYSTEM
Definition Utils.h:197
#define OSCPU_X86
Definition Utils.h:155
#define OSCPU_UNKNOWN
Definition Utils.h:154
#define LOGDEBUG
Definition Utils.h:211
#define thread_ret_val(ret)
Definition Utils.h:131
#define EVENTMEMSIZE
Definition Utils.h:143
#define INVALID_SOCKET
Definition Utils.h:137
#define GetCurrentDirEx
Definition Utils.h:79
#define CHECKNETWORKINIT
Definition Utils.h:1583
#define THREAD_STATS_ADHOC
Definition Utils.h:102
#define closesocket(X)
Definition Utils.h:138
#define THREAD_STATS_OFF
Definition Utils.h:100
#define SEMAPHOREMEMSIZE
Definition Utils.h:144
#define WSAEWOULDBLOCK
Definition Utils.h:1584
#define THREAD_RET
Definition Utils.h:127
#define stricmp
Definition Utils.h:132
#define LOCALHOSTIP
Definition Utils.h:1642
#define THREAD_FUNCTION_CALL
Definition Utils.h:129
#define SOCKET_ERROR
Definition Utils.h:136
#define LogPrint
Definition Utils.h:313
#define MAXFILENAMELEN
Definition Utils.h:89
#define MALLOC(x)
Definition Utils.h:1643
THREAD_RET(* THREAD_FUNCTION)(void *)
Definition Utils.h:128
#define LOG_NETWORK
Definition Utils.h:198
#define THREAD_STATS_AUTO
Definition Utils.h:101
#define MUTEXMEMSIZE
Definition Utils.h:142
#define ThreadHandle
Definition Utils.h:125
#define OSCPU_AMD64
Definition Utils.h:156
#define MAXSHMEMNAMELEN
Definition Utils.h:90
struct sockaddr SOCKADDR
Definition Utils.h:135
#define LOG_MAXCOUNT
Definition Utils.h:208
#define THREAD_ARG
Definition Utils.h:130
#define SOCKET
Definition Utils.h:134
RAII guard whose single static instance triggers global utility cleanup at program exit.
Definition Utils.h:178
CleanShutdown()
Default constructor; no side effects.
Definition Utils.cpp:48
~CleanShutdown()
Destroys all lazily-created global utility maps via ClearUtilsMaps().
Definition Utils.cpp:52
Callback interface for receiving log entries produced through LogSystem.
Definition Utils.h:256
Process-wide singleton logging hub with per-topic verbosity/debug levels.
Definition Utils.h:272
char * logfile
Definition Utils.h:309
static bool SetLogFileOutput(const char *logfile, bool printOut=true)
Direct log output to a file.
Definition Utils.cpp:288
static bool SetLogReceiver(LogReceiver *rec)
Register a receiver that gets every accepted LogEntry.
Definition Utils.cpp:182
bool printToStdOut
Definition Utils.h:310
static bool LogSystemPrint(uint32 source, uint8 subject, uint8 level, const char *formatstring,...)
printf-style informational logging (usually invoked via the LogPrint macro).
Definition Utils.cpp:319
static bool LogSystemDebug(uint32 source, uint8 subject, uint8 level, const char *formatstring,...)
printf-style debug logging (usually invoked via the LogDebug macro); same parameters as LogSystemPrin...
Definition Utils.cpp:367
static LogSystem * LogSingleton
Lazily-created global instance used by all static functions.
Definition Utils.h:278
static bool SetLogLevelDebug(uint8 level)
Set the debug level threshold for all topics.
Definition Utils.cpp:193
static bool SetLogLevelVerbose(uint8 level)
Set the verbose (print) level threshold for all topics.
Definition Utils.cpp:217
static bool Shutdown()
Terminate all managed threads, then destroy the singleton.
static UnitTestRunner & instance()
Access the singleton (created on first use).
void registerTest(const char *name, UnitTestFunc func, const char *description="", const char *category="", bool inDefaultRun=true)
Register a test with the runner.
Singleton snapshot of the parsed process command line (set via SetCommandLine()).
Definition Utils.h:1460
static CommandLineInfo * CommandLineInfoSingleton
Definition Utils.h:1462
Auto-reset notification event (condition variable style), optionally named for cross-process use.
Definition Utils.h:550
pthread_cond_t * event
Definition Utils.h:569
bool waitNext()
Block until the next signal() occurs.
Definition Utils.cpp:1720
Event()
Create an anonymous, process-local event.
Definition Utils.cpp:1637
bool signal()
Wake all threads currently waiting on this event.
Definition Utils.cpp:1744
pthread_mutex_t * mutex
Definition Utils.h:570
Thin RAII wrapper around a dynamically loaded library (LoadLibrary / dlopen).
Definition Utils.h:1500
LibraryFunction getFunction(const char *funcName)
Resolve an exported function.
Definition Utils.cpp:5883
static std::string patchLibraryFilename(const char *filename, const char *path=NULL)
Normalize a library name into a platform filename (adds lib prefix / .dll, .so, .dylib suffix and opt...
Definition Utils.cpp:5765
bool load(const char *filename)
Load a library.
Definition Utils.cpp:5795
Recursive mutual-exclusion lock, optionally named for cross-process use.
Definition Utils.h:463
Mutex()
Create an anonymous, process-local mutex.
Definition Utils.cpp:1040
void setCancelSafe(bool on)
Make this mutex cancellation-safe (POSIX only; no-op on Windows).
Definition Utils.cpp:1016
bool leave()
Release the mutex.
Definition Utils.cpp:1330
bool enter()
Block until the mutex is acquired.
Definition Utils.cpp:1158
pthread_mutex_t * mutex
Definition Utils.h:509
Counting semaphore, optionally named for cross-process use.
Definition Utils.h:519
Semaphore(uint32 maxCount=100)
Create an anonymous semaphore.
Definition Utils.cpp:1406
bool signal()
Increment (release) the semaphore, waking one waiter.
Definition Utils.cpp:1586
bool wait()
Block until the semaphore can be decremented.
Definition Utils.cpp:1517
Multiplexing timer: schedule many periodic timers and consume their expiries from one queue.
Definition Utils.h:686
bool addTimer(uint32 id, uint32 interval, uint64 start=0, uint64 end=0)
Register a periodic timer.
Definition Utils.cpp:1814
static std::map< uint32, Timer * > * timers
Global registry mapping globalID to live Timer instances, used by the OS callbacks.
Definition Utils.h:689
bool triggerTimer(uint32 id)
Manually inject an expiry for timer id, as if it had fired now.
Definition Utils.cpp:1938
bool waitForTimer(uint32 timeout, uint32 &id, uint64 &time)
Wait for the next expiry of any registered timer.
Definition Utils.cpp:1960
bool removeTimer(uint32 id)
Unregister a timer and cancel its OS timer.
Definition Utils.cpp:1911
uint64 FTime2PsyTime(uint64 t)
Convert an ftime-style value (ms since Unix epoch) to a PsyTime µs timestamp.
Definition PsyTime.cpp:758
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
Definition PsyTime.cpp:69
std::string PrintTimeNowString(bool local=true, bool us=true, bool ms=true)
Format GetTimeNow().
Definition PsyTime.cpp:672
int32 GetTimeAgeMS(uint64 t)
Age of a timestamp relative to now, in milliseconds.
Definition PsyTime.cpp:35
bool UnitTest_Utils()
Aggregate self test for miscellaneous utils functionality.
Definition Utils.cpp:2133
int64 AtomicDecrement64(int64 volatile &v)
Atomically decrement a 64-bit value.
Definition Utils.cpp:3111
bool ResizeProcessTerminal(uint32 proc, uint16 cols, uint16 rows)
Resize the pseudo-terminal (TIOCSWINSZ + SIGWINCH to the child's group).
Definition Utils.cpp:5521
uint32 NewProcess(const char *cmdline, const char *initdir=NULL, const char *title=NULL, int16 x=-1, int16 y=-1, int16 w=-1, int16 h=-1)
Launch a detached child process.
Definition Utils.cpp:4387
bool SetSharedSystemInstance(uint32 inst)
Set the instance number used to namespace shared OS objects, allowing several independent CMSDK syste...
Definition Utils.cpp:960
static std::map< uint32, ProcessData * > * ProcessInformationMap
Definition Utils.h:649
uint32 GetThreadStatColAbility()
Report this platform's capability for per-thread CPU statistics collection.
Definition Utils.cpp:3584
void ReleaseProcessCapture(uint32 proc)
POSIX only: release the captured stdout/stderr read-pipes recorded for a child by NewProcessEx(captur...
Definition Utils.cpp:4584
bool WaitForThreadToFinish(ThreadHandle hThread, uint32 timeoutMS=0)
Join a thread.
Definition Utils.cpp:3309
bool SignalSemaphore(const char *name)
Signal a named global semaphore.
Definition Utils.cpp:826
bool GetThreadPriority(ThreadHandle hThread, uint16 priority)
Query a thread's scheduling priority.
Definition Utils.cpp:3976
std::pair< std::string, Mutex * > SharedMemoryMutexMapPair
Definition Utils.h:606
int FromOSPriority(int pri)
Map a native OS priority back to the CMSDK priority scale.
Definition Utils.cpp:4066
bool CloseProcessInput(uint32 proc)
Close a child's stdin, delivering EOF (T1.1 step 2.7c).
Definition Utils.cpp:5331
bool EndProcess(uint32 proc)
Forcibly terminate a child process.
Definition Utils.cpp:4602
#define PROC_RUNNING
Definition Utils.h:1278
std::pair< std::string, Semaphore * > SharedMemorySemaphoreMapPair
Definition Utils.h:609
static Mutex * SharedMemorySemaphoreMapMutex
Definition Utils.h:607
bool GetLastOccupiedBitLoc(const char *bitfield, uint32 bytesize, uint32 &loc)
Find the highest set (occupied) bit.
Definition Utils.cpp:3008
const char * laststrstr(const char *str1, const char *str2)
Find the last occurrence of str2 in str1.
Definition Utils.cpp:8624
std::pair< std::string, Event * > SharedMemoryEventMapPair
Definition Utils.h:612
bool DestroyEvent(const char *name)
Destroy a named global event.
Definition Utils.cpp:939
bool WaitForSemaphore(const char *name, uint32 ms, bool autocreate=true)
Wait on a named global semaphore.
Definition Utils.cpp:787
static SharedMemoryFileHandleMapType * SharedMemoryFileHandleMap
Definition Utils.h:395
bool TextStartsWith(const char *str, const char *start, bool caseSensitive=true)
Test whether str starts with start.
Definition Utils.cpp:8681
bool Sleep(uint32 ms)
Suspend the calling thread.
Definition Utils.cpp:3121
bool SetThreadPriority(ThreadHandle hThread, uint16 priority)
Set a thread's scheduling priority.
Definition Utils.cpp:3999
bool ReadProcessTerminal(uint32 proc, std::string &out, bool &eofOut)
Non-blocking read of the merged terminal stream.
Definition Utils.cpp:5474
bool GetProcAndOSCPUUsage(double &procPercentUse, double &osPercentUse, uint64 &prevOSTicks, uint64 &prevOSIdleTicks, uint64 &prevProcTicks)
Sample CPU usage of this process and of the whole OS since the previous call.
Definition Utils.cpp:3801
bool PauseThread(ThreadHandle hThread)
Suspend a thread's execution.
Definition Utils.cpp:3484
uint32 NewProcessTerminal(const char *cmdline, const char *initdir, const std::map< std::string, std::string > *env, uint16 cols=0, uint16 rows=0)
Launch a child on a new pseudo-terminal (POSIX: posix_openpt + fork; child setsid() + TIOCSCTTY so it...
Definition Utils.cpp:5405
static std::map< std::string, Event * > * SharedMemoryEventMap
Definition Utils.h:611
bool MoveConsoleWindow(int32 x=-1, int32 y=-1, int32 w=-1, int32 h=-1)
Move/resize the console window.
Definition Utils.cpp:4148
bool WriteProcessTerminal(uint32 proc, const char *data, uint32 len)
Write bytes to the child's terminal input (its stdin).
Definition Utils.cpp:5499
bool IsThreadRunning(ThreadHandle hThread)
Check whether a thread is still alive.
Definition Utils.cpp:3554
bool CreateThread(THREAD_FUNCTION func, void *args, ThreadHandle &thread, uint32 &osID)
Start a new OS thread.
Definition Utils.cpp:3187
int32 AtomicDecrement32(int32 volatile &v)
Atomically decrement a 32-bit value.
Definition Utils.cpp:3101
uint32 NewProcessEx(const char *cmdline, const char *initdir, const char *title, const std::map< std::string, std::string > *env, bool captureOutput, bool ignoreOutput, bool newProcessGroup, int posixParentDeathSig=0, bool posixUseShell=false)
Launch a child with environment overrides and optional output capture.
Definition Utils.cpp:5052
static std::map< uint32, DrumBeatInfo > * DrumBeatMap
Definition Utils.h:441
bool SignalThread(ThreadHandle hThread, int32 signal)
Send a signal to a thread (POSIX pthread_kill; limited emulation on Windows).
Definition Utils.cpp:4090
bool BlockShutdownSignals()
Block the graceful-shutdown signals (SIGINT, SIGTERM, SIGHUP) on the CALLING thread.
Definition Utils.cpp:3433
bool UnitTest_ProbeProcessWait()
Definition Utils.cpp:2076
char * OpenSharedMemorySegment(const char *name, uint64 size)
Open and map an existing named shared memory segment.
Definition Utils.cpp:2489
int64 AtomicIncrement64(int64 volatile &v)
Atomically increment a 64-bit value.
Definition Utils.cpp:3091
uint32 GetSharedSystemInstance()
Get the current shared-system instance number.
Definition Utils.cpp:965
bool StopDrumBeat(uint32 id)
Stop a running drum beat without destroying it.
Definition Utils.cpp:594
bool EnterMutex(const char *name, uint32 ms, bool autocreate=true)
Acquire a named global mutex, creating it on first use.
Definition Utils.cpp:671
const char * laststristr(const char *str1, const char *str2)
Case-insensitive laststrstr().
Definition Utils.cpp:8649
bool ProbeProcessWait(uint32 pid, ProcessWaitInfo &out)
Probe whether a child process (group) appears blocked reading stdin (fd 0).
Definition Utils.cpp:4847
static Mutex * SharedMemoryMutexMapMutex
Definition Utils.h:604
#define SharedMemoryFileHandleMapType
Definition Utils.h:393
bool CreateDrumBeat(uint32 id, uint32 interval, DrumBeatFunc func, bool autostart=false)
Create a periodic timer ("drum beat") that calls func every interval ms.
Definition Utils.cpp:495
bool ReapThread(ThreadHandle &hThread)
Definition Utils.cpp:3267
std::string PrintProgramTrace(uint32 startLine=0, uint32 endLine=0)
Format the current call stack as printable text.
Definition Utils.cpp:3131
uint8 GetProcessStatus(uint32 proc, int &returncode)
Poll a child started with NewProcess().
Definition Utils.cpp:4519
bool(* DrumBeatFunc)(uint32 id, uint32 count)
Callback invoked on every drum-beat tick.
Definition Utils.h:418
static Mutex * SharedMemoryEventMapMutex
Definition Utils.h:610
Semaphore * GetSemaphore(const char *name, bool autocreate=true)
Look up (and optionally create) a named semaphore in the global registry.
Definition Utils.cpp:748
bool LeaveMutex(const char *name)
Release a named global mutex previously acquired with EnterMutex().
Definition Utils.cpp:711
int RunOSCommand(const char *cmdline, const char *initdir, uint32 timeout, std::string &stdoutString, std::string &stderrString)
Run a command synchronously and capture its output.
Definition Utils.cpp:4194
char * CreateSharedMemorySegment(const char *name, uint64 size, bool force=false)
Create a named shared memory segment and map it into this process.
Definition Utils.cpp:2368
std::string PrintBitFieldString(const char *bitfield, uint32 bytesize, uint32 size, const char *title)
Like GetBitFieldAsString() but prefixed with title and formatted for printing.
Definition Utils.cpp:3052
std::string GetBitFieldAsString(const char *bitfield, uint32 bytesize, uint32 size)
Render the first size bits as a '0'/'1' string, for debugging.
Definition Utils.cpp:3033
uint8 WaitForProcess(uint32 proc, uint32 timeout, int &returncode)
Wait for a child to exit.
Definition Utils.cpp:5561
uint32 GetLocalProcessID()
Get the OS process id of the current process.
Definition Utils.cpp:4185
bool EndDrumBeat(uint32 id)
Destroy a drum beat and release its OS timer.
Definition Utils.cpp:627
bool DestroySemaphore(const char *name)
Destroy a named global semaphore.
Definition Utils.cpp:858
bool CloseSharedMemorySegment(char *data, uint64 size)
Unmap a segment previously created/opened here.
Definition Utils.cpp:2604
bool TerminateThread(ThreadHandle hThread)
Forcibly kill a thread.
Definition Utils.cpp:3359
bool GetFirstFreeBitLocN(const char *bitfield, uint32 bytesize, uint32 num, uint32 &loc)
Find the first run of num consecutive 0 bits.
Definition Utils.cpp:2765
bool StartSignalThread(ShutdownSignalCallback cb)
Start the dedicated signal-handling thread (POSIX only).
Definition Utils.cpp:3445
bool GetCurrentThreadUniqueID(uint32 &tid)
Get a process-unique id for the calling thread.
Definition Utils.cpp:3504
int32 AtomicIncrement32(int32 volatile &v)
Atomically increment a 32-bit value.
Definition Utils.cpp:3081
bool GetDesktopSize(uint32 &width, uint32 &height)
Get the primary desktop resolution.
Definition Utils.cpp:4106
bool CheckForThreadFinished(ThreadHandle hThread)
Non-blocking check whether a thread has terminated.
Definition Utils.cpp:3233
void(* ShutdownSignalCallback)(int sig, int count)
Callback invoked (on the dedicated signal thread) when a graceful-shutdown signal is caught.
Definition Utils.h:1147
bool DestroyMutex(const char *name)
Destroy a named global mutex and remove it from the registry.
Definition Utils.cpp:724
bool ContinueThread(ThreadHandle hThread)
Resume a thread paused with PauseThread().
Definition Utils.cpp:3493
#define PROC_TERMINATED
Definition Utils.h:1276
const char * stristr(const char *str, const char *substr, uint32 len=0)
Case-insensitive strstr.
Definition Utils.cpp:7476
void CloseProcessTerminal(uint32 proc)
Close the master side (terminal hangup: the child's foreground group gets SIGHUP and any blocked term...
Definition Utils.cpp:5538
std::list< std::string > GetProgramTrace()
Capture the current call stack.
Definition Utils.cpp:3146
uint64 hton64(const uint64_t *input)
Convert a 64-bit value from host to network byte order.
static uint16 SharedSystemInstance
Definition Utils.h:397
static void TimerCallback(int sig, siginfo_t *siginfo, void *context)
POSIX signal-based timer callback trampoline (SIGEV_SIGNAL); routes the expiry into the owning Timer'...
Definition Utils.cpp:1996
void PrintBinary(void *p, uint32 size, bool asInt, const char *title)
Hex/decimal dump a memory region to stdout for debugging.
Definition Utils.cpp:7441
static std::map< std::string, Mutex * > * SharedMemoryMutexMap
Definition Utils.h:605
bool GetFirstFreeBitLoc(const char *bitfield, uint32 bytesize, uint32 &loc)
Find the first 0 (free) bit.
Definition Utils.cpp:2733
bool SignalEvent(const char *name)
Signal a named global event.
Definition Utils.cpp:926
bool WriteProcessInput(uint32 proc, const char *data, uint32 len)
Write to the stdin of a child launched with captureOutput=true (T1.1 step 2.7c).
Definition Utils.cpp:5301
bool GetCPUTicks(ThreadHandle hThread, uint64 &ticks)
Get accumulated CPU time of a specific thread.
Definition Utils.cpp:3596
bool GetNextLineEnd(const char *str, uint32 size, uint32 &len, uint32 &crSize)
Find the end of the current line.
Definition Utils.cpp:7515
bool GetBit(uint32 loc, const char *bitfield, uint32 bytesize, bit &val)
Read bit loc.
Definition Utils.cpp:2983
uint64 ntoh64(const uint64 *input)
Convert a 64-bit value from network to host byte order.
Definition Utils.cpp:2702
bool GetProcessCPUTicks(uint64 &ticks)
Get accumulated CPU time of the current process.
Definition Utils.cpp:3754
bool ReadProcessOutput(uint32 proc, std::string &out, std::string &err)
Read any pending captured stdout/stderr from a child launched with captureOutput=true.
Definition Utils.cpp:5347
int ToOSPriority(int pri)
Map a CMSDK priority value to the platform's native priority scale.
Definition Utils.cpp:4018
static std::map< std::string, Semaphore * > * SharedMemorySemaphoreMap
Definition Utils.h:608
bool StopSignalThread()
Stop the dedicated signal thread and join it.
Definition Utils.cpp:3468
bool SendProcessBreak(uint32 proc, int sig)
Attempt a graceful stop: POSIX sends the given signal; Windows posts a CTRL_BREAK to the child's proc...
Definition Utils.cpp:5550
bool SetBitN(uint32 loc, uint32 num, bit value, char *bitfield, uint32 bytesize)
Set num consecutive bits starting at loc to value.
Definition Utils.cpp:2920
bool Reset32BitField(char *bitfield, uint32 bytesize)
Zero the whole bitfield, marking every bit as free.
Definition Utils.cpp:2727
bool StartDrumBeat(uint32 id)
Start (or resume) a previously created drum beat.
Definition Utils.cpp:548
bool TextEndsWith(const char *str, const char *end, bool caseSensitive=true)
Test whether str ends with end.
Definition Utils.cpp:8674
bool UnitTest_Timer()
Self test for the Timer class.
Definition Utils.cpp:2009
#define PROC_TIMEOUT
Definition Utils.h:1279
bool SetBit(uint32 loc, bit value, char *bitfield, uint32 bytesize)
Set bit loc to value.
Definition Utils.cpp:2888
bool GetCurrentThread(ThreadHandle &thread)
Get the handle of the calling thread.
Definition Utils.cpp:3544
bool RenameConsoleWindow(const char *name, bool prepend=false)
Set the console window title.
Definition Utils.cpp:4126
bool TryReapThread(ThreadHandle &hThread)
Definition Utils.cpp:3283
bool UtilsTest()
Run the built-in self test of the utils module.
Definition Utils.cpp:7399
#define PROCBUFSIZE
Definition Utils.h:1293
uint32 Calc32BitFieldSize(uint32 bitsize)
Compute the byte size needed for a bitfield of bitsize bits, rounded up to a 32-bit boundary.
Definition Utils.cpp:2723
uint32 TextReplaceCharsInPlace(char *str, uint32 size, char find, char replace)
Replace every occurrence of a character, in place.
Definition Utils.cpp:8613
uint32 strcpyavail(char *dst, const char *src, uint32 maxlen, bool copyAvailable)
Bounded strcpy that always NUL-terminates.
Definition Utils.cpp:7497
void ClearStaleSharedSegments(uint16 port)
Remove stale OS shared-memory/semaphore objects left by a crashed node on this port.
Definition Utils.cpp:2678
std::pair< uint32, ProcessData * > Proc_Pair
Definition Utils.h:650
bool GetOSCPUUsage(double &percentUse)
Get the instantaneous total OS CPU usage.
Definition Utils.cpp:3919
bool GetCurrentThreadOSID(uint32 &tid)
Get the OS-level id of the calling thread (gettid / GetCurrentThreadId).
Definition Utils.cpp:3524
std::string EncodeHTML(std::string str)
Encode a plain string for safe embedding in HTML (e.g.
Definition HTML.cpp:51
void fail(const char *fmt,...)
Set an explanatory reason shown on the FAIL line.
void metric(const char *name, double value, const char *unit="", bool higherIsBetter=true)
Record a performance metric.
void detail(const char *fmt,...)
Verbose-only indented diagnostic line (shown only when verbose=1).
void progress(int percent, const char *action)
Report progress with a short description of the current action.
bool SeedRandomValues(uint32 seedvalue=0)
Seed the pseudo-random generator.
Definition Utils.cpp:9113
int GetLastOSErrorNumber()
Get the last OS error number (errno / GetLastError()).
Definition Utils.cpp:6668
static THREAD_RET THREAD_FUNCTION_CALL SignalThreadMain(THREAD_ARG arg)
Definition Utils.cpp:3413
std::string GetLastOSErrorMessage()
Get the last OS error as human-readable text.
Definition Utils.cpp:6678
static bool AnyCancelSafeMutexes()
Definition Utils.cpp:998
std::vector< std::string > TextListBreakLines(const char *text, uint32 maxLineLength=80)
Word-wrap text into lines no longer than maxLineLength.
Definition Utils.cpp:7793
int8 CompareFloats(float64 a, float64 b)
Compare two doubles with epsilon tolerance.
Definition Utils.cpp:8201
uint32 * GetLocalIPAddresses(uint32 &count)
List all local IPv4 addresses.
Definition Utils.cpp:7080
std::string EncodeJSON(std::string str)
Escape text for embedding as a JSON string value.
Definition Utils.cpp:9037
unsigned char * Ascii2UTF16LE(const char *ascii, uint32 len, uint32 &size)
Convert 8-bit ASCII text to UTF-16LE (used for Windows wide APIs and some protocols).
Definition Utils.cpp:6513
uint64 GetProcessMemoryUsage()
Current resident memory usage of this process.
Definition Utils.cpp:5983
char ** SplitCommandline(const char *cmdline, int &argc)
Split a command line into a malloc'ed argv array.
Definition Utils.cpp:7678
uint64 ReadRegistryQWORD(const char *key, const char *entry)
Read a 64-bit registry value.
static std::map< uint32, int > * TerminalMasterMap
Definition Utils.cpp:5395
std::string GetCommandLineArg(uint16 n)
Get the n-th positional command line item.
Definition Utils.cpp:5724
bool CreateADir(const char *dirname)
Create a directory (parents included where supported).
Definition Utils.cpp:8494
std::string BytifyRates(double val1, double val2)
Format two byte rates as "x / y" with matching units.
Definition Utils.cpp:9260
bool CopyAFile(const char *oldfilename, const char *newfilename, bool force=false)
Copy a file.
Definition Utils.cpp:8446
bool DeleteFilesInADir(const char *dirname, bool force)
Delete all files inside a directory, leaving the directory itself.
Definition Utils.cpp:8587
uint32 GetCommandLineArgCount()
Number of positional command line items (excluding the executable).
Definition Utils.cpp:5718
uint16 GetCPUArchitecture()
CPU architecture id.
Definition Utils.cpp:6074
char OSLocalHostName[1024]
Definition Utils.cpp:5932
char OSArchitectureName[1024]
Definition Utils.cpp:5933
std::string ReadAFileString(std::string filename)
Read an entire file into a std::string.
Definition Utils.cpp:8238
static uint32 g_shutdownSignalCount
Definition Utils.cpp:3411
char * StringFormatVA(uint32 &size, const char *format, va_list args)
va_list core used by the other StringFormat overloads.
Definition Utils.cpp:8007
std::string TextUppercase(const char *text)
Convert to upper case (ASCII/current locale).
Definition Utils.cpp:7548
int CRC32(const char *addr, uint32 length, int32 crc=0)
Compute (or continue) a CRC-32 checksum.
Definition Utils.cpp:8220
static pthread_mutex_t g_cancelSafeSetLock
Definition Utils.cpp:978
bool WaitForSocketReadability(SOCKET s, int32 timeout)
Wait until a socket has data to read.
Definition Utils.cpp:6726
uint64 * GetLocalMACAddresses(uint32 &count)
List all local MAC addresses.
Definition Utils.cpp:7006
bool WaitForNextEvent(const char *name, uint32 ms, bool autocreate)
Definition Utils.cpp:887
static volatile int g_cancelSafeCount
Definition Utils.cpp:996
const char * GetSystemArchitecture()
Architecture name string, e.g.
Definition Utils.cpp:6222
std::string TextTrim(const char *text)
Strip leading and trailing whitespace.
Definition Utils.cpp:7610
std::string GetCommandLinePath()
Get the directory portion of the executable path.
Definition Utils.cpp:5678
bool HasCommandLineArg(const char *key, std::string &value)
Test for a 'key=value' argument without mutating the argument map.
Definition Utils.cpp:5736
bool SetSocketBlockingMode(SOCKET s)
Put a socket into blocking mode.
Definition Utils.cpp:6775
uint32 Ascii2Uint32(const char *ascii, uint32 start=0, uint32 end=0)
Parse an unsigned 32-bit decimal integer from a substring.
Definition Utils.cpp:8967
uint64 GetCPUSpeed()
Nominal CPU clock speed.
Definition Utils.cpp:6111
wchar_t ** SplitCommandlineW(const char *cmdline, int &argc)
Wide-character variant of SplitCommandline() (for Windows APIs).
Definition Utils.cpp:7698
std::string TextVectorConcat(std::vector< std::string >, const char *sep, bool allowEmpty=true)
Concatenate vector entries with sep.
Definition Utils.cpp:7980
bool AppendToAFile(const char *filename, const char *data, uint32 length, bool binary=false)
Append to a file, creating it if missing.
Definition Utils.cpp:8346
std::string TextIndent(const char *text, const char *indent)
Prefix every line of text with indent.
Definition Utils.cpp:7560
unsigned char Dec2Char(const char *str)
Parse up to three decimal digits into a byte.
Definition Utils.cpp:8193
char * GetFileList(const char *dirname, const char *ext, uint32 &count, bool fullpath, uint32 maxNameLen=256)
List files in a directory matching an extension.
Definition Utils.cpp:8734
bool GetNextAvailableLocalPort(uint16 lastPort, uint16 &nextPort)
Find the next free TCP port above lastPort.
Definition Utils.cpp:7351
static std::map< const void *, bool > * g_cancelSafeSet
Definition Utils.cpp:979
bool MoveAFile(const char *oldfilename, const char *newfilename, bool force=false)
Move/rename a file.
Definition Utils.cpp:8398
uint64 GetTime()
Definition Utils.cpp:461
uint32 StringMultiReplace(std::string &text, std::map< std::string, std::string > &map, bool onlyFirst)
Apply many key→value replacements in one pass.
Definition Utils.cpp:8103
std::vector< std::string > TextListSplit(const char *text, const char *split, bool keepEmpty=true, bool autoTrim=false)
Split text on a separator.
Definition Utils.cpp:7952
bool IsTextNumeric(const char *ascii, uint32 start=0, uint32 end=0)
Test whether a (sub)string is a valid number.
Definition Utils.cpp:8891
uint16 GetCPUCount()
Number of logical CPU cores.
Definition Utils.cpp:6055
char * Uint2Ascii(uint64 value, char *result, uint16 size, uint8 base)
Render an unsigned integer as text in an arbitrary base.
Definition Utils.cpp:6489
std::map< std::string, std::string > TextMapSplit(const char *text, const char *outersplit, const char *innersplit)
Split "k=v<sep>k=v..." text into a map (later duplicates overwrite earlier ones).
Definition Utils.cpp:7652
uint64 GetPeakProcessMemoryUsage()
Peak resident memory usage of this process.
Definition Utils.cpp:6015
bool DeleteCommandline(char **argv, int argc)
Free an argv array produced by SplitCommandline().
Definition Utils.cpp:7718
uint32 AsciiHex2Uint32(const char *ascii, uint32 start=0, uint32 end=0)
Parse an unsigned 32-bit hexadecimal integer from a substring.
Definition Utils.cpp:8977
bool DeleteRegistryKey(const char *key)
Delete a (leaf) registry key.
Definition Utils.cpp:9531
bool GetLocalHostname(char *name, uint32 maxSize)
Local hostname into a caller buffer.
Definition Utils.cpp:6934
std::string TextUnindent(const char *text)
Remove one level of leading indentation from every line.
Definition Utils.cpp:7571
bool GetSystemOSVersion(uint16 &major, uint16 &minor, uint16 &build, char *text, uint16 textSize)
Detailed OS version.
Definition Utils.cpp:6311
uint32 StringScriptReplace(std::string &text, std::string name, std::list< std::map< std::string, std::string > > &list)
Expand a named repeating template block in text once per map in list, substituting each map's keys in...
Definition Utils.cpp:8137
std::string TextJoinXML(std::map< std::string, std::string > &map, const char *innernodeName, const char *outernodeName)
Serialize a map as XML, one innernodeName element per pair inside an outernodeName element.
Definition Utils.cpp:7934
bool SetCommandLine(int argc, char *argv[])
Parse and store the process command line for later retrieval by the Get/HasCommandLine* functions.
Definition Utils.cpp:5631
uint32 StringSingleReplace(std::string &text, std::string key, std::string value, bool onlyFirst)
Replace occurrences of key with value in text.
Definition Utils.cpp:8085
uint64 GetSystemMemorySize()
Total physical RAM installed.
Definition Utils.cpp:6158
uint32 ReadRegistryDWORD(const char *key, const char *entry)
Read a 32-bit registry value.
static bool TerminalMasterFind(uint32 proc, int &fd)
Definition Utils.cpp:5396
static ShutdownSignalCallback g_shutdownCallback
Definition Utils.cpp:3410
bool GetLocalMACAddress(uint64 &address)
Get the primary local MAC address.
Definition Utils.cpp:6994
bool WaitForSocketWriteability(SOCKET s, int32 timeout)
Wait until a socket can be written without blocking.
Definition Utils.cpp:6694
FileDetails GetFileDetails(const char *filename)
Stat a file.
Definition Utils.cpp:8805
double RandomValue()
Uniform random double in [0,1).
Definition Utils.cpp:9119
bool GetSocketError(int wsaError, char *errorString, uint16 errorStringMaxSize, bool *isRecoverable)
Translate a socket error code into text and classify it.
Definition Utils.cpp:6532
std::string GetCommandLineExecutableOnly()
Get just the executable filename without path.
Definition Utils.cpp:5701
static volatile sig_atomic_t g_signalThreadStop
Definition Utils.cpp:3409
std::multimap< std::string, std::string > TextMultiMapSplit(const char *text, const char *outersplit, const char *innersplit)
Split "k=v<sep>k=v..." text into a multimap (duplicate keys preserved).
Definition Utils.cpp:7625
bool RenameRegistryTree(const char *entry, const char *newName)
Definition Utils.cpp:9536
static bool ContainsPortToken(const char *name, const char *portstr, size_t plen)
Definition Utils.cpp:2643
bool GetLocalIPAddress(uint32 &address)
Get the primary local IPv4 address.
Definition Utils.cpp:7048
bool LookupHostname(uint32 address, char *name, uint32 maxSize)
Reverse-resolve an IPv4 address to a hostname.
Definition Utils.cpp:6866
char * TextSubstringCopy(const char *ascii, uint32 start, uint32 end)
Copy the substring [start,end) into a new NUL-terminated buffer.
Definition Utils.cpp:8872
bool LookupIPAddress(const char *name, uint32 &address)
Resolve a hostname to an IPv4 address.
Definition Utils.cpp:6787
static uint32 ClearSegmentsIn(const char *dir, const char *prefix, const char *portstr, size_t plen)
Definition Utils.cpp:2656
char * Int2Ascii(int64 value, char *result, uint16 size, uint8 base)
Render a signed integer as text in an arbitrary base.
Definition Utils.cpp:6462
std::string GetLocalHostnameString()
Local hostname as std::string.
Definition Utils.cpp:6922
static pid_t DoPosixForkExec(PosixLaunchRequest &r)
Definition Utils.cpp:4928
int32 Ascii2Int32(const char *ascii, uint32 start=0, uint32 end=0)
Parse a signed 32-bit decimal integer from a substring.
Definition Utils.cpp:8957
bool DoesAFileExist(const char *filename)
Test whether a regular file exists.
Definition Utils.cpp:8800
NetworkInterfaces * GetLocalInterfaces(uint32 &count)
Enumerate local network interfaces with address, MAC and names.
Definition Utils.cpp:7205
std::string TextTrimQuotes(const char *text)
Strip a single pair of surrounding quotes if present.
Definition Utils.cpp:7592
unsigned char Hex2Char(const char *str)
Parse two hex digits into a byte.
Definition Utils.cpp:8185
bool DeleteAFile(const char *filename, bool force)
Delete a file.
Definition Utils.cpp:8374
bool ChangeAFileAttr(const char *filename, bool read, bool write)
Change read/write permission attributes of a file.
Definition Utils.cpp:8378
static bool MutexIsCancelSafe(const void *m)
Definition Utils.cpp:1006
std::string BytifySize(double val)
Format a byte count with binary units, e.g.
Definition Utils.cpp:9165
std::string GetCommandLineExecutable()
Get the full executable path as invoked.
Definition Utils.cpp:5695
bool StringFormatInto(char *dst, uint32 maxsize, const char *format,...)
printf into a caller-supplied buffer with truncation.
Definition Utils.cpp:8037
std::vector< std::string > TextListSplitLines(const char *text, bool keepEmpty=true, bool autoTrim=false)
Split text into lines, handling both \n and \r\n.
Definition Utils.cpp:7775
bool WriteAFile(const char *filename, const char *data, uint32 length, bool binary=false)
Write (create/overwrite) a file.
Definition Utils.cpp:8318
std::string GetCurrentDir()
Current working directory.
Definition Utils.cpp:8721
std::string GetCommandLine()
Get the full original command line as one string.
Definition Utils.cpp:5672
static std::mutex gLaunchMx
Definition Utils.cpp:5013
bool CalcTimeout(struct timespec &timeout, uint32 ms)
Definition Utils.cpp:446
static std::mutex gLaunchSerialMx
Definition Utils.cpp:5017
std::string ReadRegistryString(const char *key, const char *entry)
Read a string registry value.
char OSName[1024]
Definition Utils.cpp:5934
std::vector< std::string > TextCommandlineSplit(const char *cmdline)
Split a command line into arguments, honouring quoting rules.
Definition Utils.cpp:7725
bool DoesADirExist(const char *dirname)
Test whether a directory exists.
Definition Utils.cpp:8795
static std::condition_variable gLaunchCv
Definition Utils.cpp:5014
bool DeleteRegistryEntry(const char *key, const char *entry)
Delete a single value from a key.
static bool gLauncherStarted
Definition Utils.cpp:5016
std::string StringFormat(const char *format,...)
printf into a std::string.
Definition Utils.cpp:8067
bool SetSocketNonBlockingMode(SOCKET s)
Put a socket into non-blocking mode.
Definition Utils.cpp:6763
bool DeleteADir(const char *dirname, bool force)
Delete a directory (recursively when force).
Definition Utils.cpp:8520
bool WriteRegistryQWORD(const char *key, const char *entry, uint64 value)
Write a 64-bit registry value.
char * ReadAFile(const char *filename, uint32 &length, bool binary=false)
Read an entire file into a new buffer.
Definition Utils.cpp:8259
static __thread int g_savedCancelState
Definition Utils.cpp:980
uint64 Ascii2Uint64(const char *ascii, uint32 start=0, uint32 end=0)
Parse an unsigned 64-bit decimal integer from a substring.
Definition Utils.cpp:8921
uint64 AsciiHex2Uint64(const char *ascii, uint32 start=0, uint32 end=0)
Parse an unsigned 64-bit hexadecimal integer (with or without 0x) from a substring.
Definition Utils.cpp:8939
const char * GetFileBasename(const char *filename)
Filename portion of a path.
Definition Utils.cpp:8705
static PosixLaunchRequest * gLaunchReq
Definition Utils.cpp:5015
Library * OpenLibrary(const char *libName)
Load a library by name, applying platform filename conventions.
Definition Utils.cpp:5918
bool WriteRegistryString(const char *key, const char *entry, const char *value)
Write a string registry value.
static void DrumBeatCallback(union sigval p)
Definition Utils.cpp:483
bool GetSystemMemoryUsage(uint64 &totalRAM, uint64 &freeRAM)
Query total and free physical memory.
Definition Utils.cpp:6181
static THREAD_RET THREAD_FUNCTION_CALL ProcessLauncherRun(THREAD_ARG)
Definition Utils.cpp:5019
int64 Ascii2Int64(const char *ascii, uint32 start=0, uint32 end=0)
Parse a signed 64-bit decimal integer from a substring.
Definition Utils.cpp:8903
static pid_t SubmitLaunchRequest(PosixLaunchRequest &req)
Definition Utils.cpp:5033
bool WriteRegistryDWORD(const char *key, const char *entry, uint32 value)
Write a 32-bit registry value (creates the key when needed).
std::string TextJoinJSON(std::map< std::string, std::string > &map)
Serialize a map as a JSON object (keys/values escaped).
Definition Utils.cpp:7918
bool CopyRegistryTree(const char *entry, const char *newName)
Recursively copy a registry subtree.
Definition Utils.cpp:9534
static bool g_signalThreadRunning
Definition Utils.cpp:3408
int(* LibraryFunction)()
Signature of a plain function exported from a dynamically loaded library.
Definition Utils.h:1455
bool DeleteRegistryTree(const char *root, const char *key)
Recursively delete a key and all subkeys.
std::string TextLowercase(const char *text)
Convert to lower case (ASCII/current locale).
Definition Utils.cpp:7554
std::string DecodeJSON(std::string str)
Unescape a JSON string value (\" \\ \n \t \uXXXX etc.).
Definition Utils.cpp:8998
std::string TextCapitalise(const char *text)
Capitalise the first letter of text.
Definition Utils.cpp:7534
const char * GetComputerName()
Get this machine's hostname.
Definition Utils.cpp:5936
std::string GetFilePath(const char *filename)
Directory portion of a path.
Definition Utils.cpp:8689
static void SignalShutdownSigset(sigset_t *set)
Definition Utils.cpp:3399
const char * GetSystemOSName()
OS name string, e.g.
Definition Utils.cpp:6283
std::string BytifySizes(double val1, double val2)
Format two byte counts as "x / y" with matching units.
Definition Utils.cpp:9202
bool IsLocalIPAddress(const char *addr)
Test whether a textual address refers to this machine (loopback or a local interface).
Definition Utils.cpp:7021
float64 Ascii2Float64(const char *ascii, uint32 start=0, uint32 end=0)
Parse a 64-bit float from a substring (decimal point, not locale dependent).
Definition Utils.cpp:8987
std::string TextJoin(std::vector< std::string > &list, const char *split, uint32 start=0, uint32 count=0)
Join vector elements with split.
Definition Utils.cpp:7871
static pthread_t g_signalThread
Definition Utils.cpp:3407
int64 RandomInt(int64 from=0, int64 to=100)
Uniform random integer in [from,to].
Definition Utils.cpp:9156
std::string BytifyRate(double val)
Format a byte rate, e.g.
Definition Utils.cpp:9248
static bool Test_Utils()
Definition Utils.cpp:9544
CleanShutdown CleanShutdownInstance
Definition Utils.cpp:46
struct dirent * readdir(DIR *)
DIR * opendir(const char *)
Definition direntwin.cpp:53
bool ClearUtilsMaps()
Free all lazily-allocated global registries (mutex/semaphore/event/shared-memory maps).
Definition Utils.cpp:62
static bool Test_ProbeProcessWait()
Definition Utils.cpp:9545
uint32 CleanShutdownInstanceCount
Definition Utils.cpp:45
void Register_Utils_Tests()
Definition Utils.cpp:9548
int closedir(DIR *)
Definition direntwin.cpp:95
uint32 GetDataTypeID(const char *typeName)
Inverse of GetDataTypeName(): look up the numeric datatype id for a type name.
Definition Utils.cpp:155
static bool Test_Timer()
Definition Utils.cpp:9546
std::string GetDataTypeName(uint32 datatype)
Translate a CMSDK datatype id (e.g.
Definition Utils.cpp:130
Wire/storage layout of one log record: fixed header immediately followed by the message text.
Definition Utils.h:228
uint32 source
Definition Utils.h:232
uint32 size
Definition Utils.h:229
std::string toJSON()
Serialize the entry (header fields + text) as a JSON object string.
Definition Utils.cpp:271
uint8 level
Definition Utils.h:234
uint64 time
Definition Utils.h:231
bool setText(char *text, uint32 len)
Copy len bytes of text into the payload area and update size.
Definition Utils.cpp:260
uint8 subject
Definition Utils.h:233
uint8 type
Definition Utils.h:235
uint32 cid
Definition Utils.h:230
const char * getText(uint32 &len)
Access the text payload stored after the header.
Definition Utils.cpp:253
std::string toXML()
Serialize the entry as an XML fragment.
Definition Utils.cpp:279
char * d_name
Definition direntwin.h:35
Bookkeeping record for one periodic "drum beat" timer.
Definition Utils.h:423
Existence, type, permission and timestamp information for one file, as returned by GetFileDetails().
Definition Utils.h:1869
Description of one local network interface (IPv4 address, MAC and names).
Definition Utils.h:1610
char friendlyName[MAXKEYNAMELEN+1]
Definition Utils.h:1614
char name[MAXKEYNAMELEN+1]
Definition Utils.h:1613
const std::map< std::string, std::string > * env
Definition Utils.cpp:4914
POSIX capture bookkeeping for children started via NewProcessEx(captureOutput).
Definition Utils.h:644
int errFD
Parent read-end of the child's stderr (-1 = not captured).
Definition Utils.h:647
int outFD
Parent read-end of the child's stdout (-1 = not captured).
Definition Utils.h:646
Snapshot from ProbeProcessWait(): is the child (group) alive, is it blocked reading stdin,...
Definition Utils.h:1379
uint64 cpuTimeMs
cumulative user+sys CPU across the process group, in ms
Definition Utils.h:1383
bool waitingStdin
blocked reading fd 0 (exact on Linux; best-effort composite elsewhere)
Definition Utils.h:1382
bool alive
process (or group) still exists
Definition Utils.h:1380
bool waitExact
true only when the OS reported the exact blocking syscall (Linux /proc/<pid>/syscall)
Definition Utils.h:1381
One scheduled entry inside a Timer: id, active window and platform timer handle.
Definition Utils.h:657
A single timer expiry: which timer fired and when.
Definition Utils.h:675
#define TRUE
Definition xml_parser.h:124
#define FALSE
Definition xml_parser.h:121