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.
5013// The launcher thread lives for the whole process and is never joined, so the mutex
5014// and condvar it blocks on must never be destroyed either: as plain statics, exit()
5015// ran ~condition_variable while the thread was still waiting on it, and glibc's
5016// pthread_cond_destroy then waits for that waiter forever - any process that had
5017// launched a child hung at exit after main() returned (Examples/psytest.xml: 7/7
5018// SUCCESS, then hung in __run_exit_handlers -> pthread_cond_destroy(gLaunchCv)). Heap
5019// objects that are deliberately leaked have process lifetime, like the thread.
5020static std::mutex& gLaunchMx = *new std::mutex;
5021static std::condition_variable& gLaunchCv = *new std::condition_variable;
5023static bool gLauncherStarted = false;
5024static std::mutex gLaunchSerialMx;
5025
5027 std::unique_lock<std::mutex> lock(gLaunchMx);
5028 for (;;) {
5029 while (!gLaunchReq)
5030 gLaunchCv.wait(lock);
5032 req->pid = DoPosixForkExec(*req);
5033 req->done = true;
5034 gLaunchReq = NULL;
5035 gLaunchCv.notify_all();
5036 }
5037 thread_ret_val(0);
5038}
5039
5041 std::lock_guard<std::mutex> serial(gLaunchSerialMx);
5042 std::unique_lock<std::mutex> lock(gLaunchMx);
5043 if (!gLauncherStarted) {
5044 ThreadHandle th;
5045 uint32 osID = 0;
5046 if (!CreateThread((THREAD_FUNCTION)ProcessLauncherRun, NULL, th, osID))
5047 return DoPosixForkExec(req); // degraded: launch inline (old behavior)
5048 gLauncherStarted = true;
5049 }
5050 req.done = false;
5051 gLaunchReq = &req;
5052 gLaunchCv.notify_all();
5053 while (!req.done)
5054 gLaunchCv.wait(lock);
5055 return req.pid;
5056}
5057#endif // !WINDOWS
5058
5059uint32 NewProcessEx(const char* cmdline, const char* initdir, const char* title,
5060 const std::map<std::string, std::string>* env,
5061 bool captureOutput, bool ignoreOutput, bool newProcessGroup,
5062 int posixParentDeathSig, bool posixUseShell) {
5063 if (!cmdline || !strlen(cmdline))
5064 return 0;
5065
5066 #if defined WINDOWS
5067
5068 (void)posixParentDeathSig; (void)posixUseShell;
5069 if (!ProcessInformationMap) {
5070 ProcessInformationMap = new std::map<uint32, ProcessData*>;
5071 ghJob = CreateJobObject(NULL, NULL); // GLOBAL
5072 if (ghJob) {
5073 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };
5074 jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
5075 SetInformationJobObject(ghJob, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli));
5076 }
5077 }
5078
5079 ProcessData* pData = new ProcessData;
5080 ZeroMemory(&(pData->procInfo), sizeof(pData->procInfo));
5081 ZeroMemory(&(pData->si), sizeof(pData->si));
5082 pData->si.cb = sizeof(pData->si);
5083 pData->si.wShowWindow = SW_SHOWNOACTIVATE;
5084 pData->si.dwFlags = STARTF_USESHOWWINDOW;
5085
5086 // hInRead is the CHILD's end of the new stdin pipe (2.7c) - inheritable, and
5087 // closed in the parent right after CreateProcess like the other child ends.
5088 HANDLE hOutWrite = NULL, hErrWrite = NULL, hNull = NULL, hInRead = NULL;
5089 SECURITY_ATTRIBUTES sa;
5090 ZeroMemory(&sa, sizeof(sa));
5091 sa.nLength = sizeof(sa);
5092 sa.bInheritHandle = TRUE;
5093 sa.lpSecurityDescriptor = NULL;
5094
5095 if (captureOutput) {
5096 // Create stdout/stderr pipes; make only the WRITE ends inheritable.
5097 if ( !CreatePipe(&pData->hOutRead, &hOutWrite, &sa, 0) ||
5098 !CreatePipe(&pData->hErrRead, &hErrWrite, &sa, 0) ) {
5099 LogPrint(0, LOG_SYSTEM, 0, "NewProcessEx: could not create output pipes for '%s'", cmdline);
5100 if (pData->hOutRead) CloseHandle(pData->hOutRead);
5101 if (hOutWrite) CloseHandle(hOutWrite);
5102 if (pData->hErrRead) CloseHandle(pData->hErrRead);
5103 delete pData;
5104 return 0;
5105 }
5106 SetHandleInformation(pData->hOutRead, HANDLE_FLAG_INHERIT, 0);
5107 SetHandleInformation(pData->hErrRead, HANDLE_FLAG_INHERIT, 0);
5108 // T1.1 step 2.7c: give the child its OWN stdin pipe. Previously it
5109 // inherited the parent's STD_INPUT_HANDLE, which has two consequences:
5110 // a child that reads stdin gets whatever the parent's console has (an
5111 // instant EOF when there is no console at all, e.g. under SSH or as a
5112 // service), so it CANNOT block awaiting input - making a stall-on-stdin
5113 // probe impossible to demonstrate; and there was no handle to write to,
5114 // so WriteProcessInput()/CLISession::writeStdin() had nothing to use.
5115 // Only the child's READ end is inheritable; we keep the write end.
5116 if (!CreatePipe(&hInRead, &pData->hInWrite, &sa, 0)) {
5117 LogPrint(0, LOG_SYSTEM, 0, "NewProcessEx: could not create stdin pipe for '%s'", cmdline);
5118 CloseHandle(pData->hOutRead); CloseHandle(hOutWrite);
5119 CloseHandle(pData->hErrRead); CloseHandle(hErrWrite);
5120 delete pData;
5121 return 0;
5122 }
5123 SetHandleInformation(pData->hInWrite, HANDLE_FLAG_INHERIT, 0);
5124 pData->si.dwFlags |= STARTF_USESTDHANDLES;
5125 pData->si.hStdOutput = hOutWrite;
5126 pData->si.hStdError = hErrWrite;
5127 pData->si.hStdInput = hInRead;
5128 }
5129 else if (ignoreOutput) {
5130 hNull = CreateFile("NUL", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
5131 &sa, OPEN_EXISTING, 0, NULL);
5132 if (hNull != INVALID_HANDLE_VALUE) {
5133 pData->si.dwFlags |= STARTF_USESTDHANDLES;
5134 pData->si.hStdOutput = hNull;
5135 pData->si.hStdError = hNull;
5136 pData->si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
5137 }
5138 else hNull = NULL;
5139 }
5140
5141 // Build the environment block (parent env + overrides) if any overrides given.
5142 char* envBlock = NULL;
5143 if (env && env->size()) {
5144 std::map<std::string, std::string> merged;
5145 LPCH parentEnv = GetEnvironmentStrings();
5146 if (parentEnv) {
5147 for (LPCH p = parentEnv; *p; ) {
5148 std::string entry = p;
5149 size_t eq = entry.find('=');
5150 if (eq != std::string::npos && eq > 0)
5151 merged[entry.substr(0, eq)] = entry.substr(eq + 1);
5152 p += entry.size() + 1;
5153 }
5154 FreeEnvironmentStrings(parentEnv);
5155 }
5156 std::map<std::string, std::string>::const_iterator ei = env->begin();
5157 while (ei != env->end()) { merged[ei->first] = ei->second; ei++; }
5158 size_t total = 1;
5159 std::map<std::string, std::string>::iterator mi = merged.begin();
5160 while (mi != merged.end()) { total += mi->first.size() + 1 + mi->second.size() + 1; mi++; }
5161 envBlock = new char[total];
5162 size_t off = 0;
5163 for (mi = merged.begin(); mi != merged.end(); mi++) {
5164 std::string kv = mi->first + "=" + mi->second;
5165 memcpy(envBlock + off, kv.c_str(), kv.size() + 1);
5166 off += kv.size() + 1;
5167 }
5168 envBlock[off] = 0;
5169 }
5170
5171 DWORD flags = CREATE_NEW_CONSOLE;
5172 if (newProcessGroup)
5173 flags |= CREATE_NEW_PROCESS_GROUP;
5174
5175 // NOTE: cmdline/title are plain strings, NOT printf formats. Copy them
5176 // verbatim into mutable new[] buffers. (Previously StringFormat(size, x)
5177 // was used, which interpreted any '%' in the command line as a printf
5178 // conversion -> invalid-parameter fast-fail (c0000409) for commands like
5179 // `cmd /c echo %VAR%`, and mismatched the malloc alloc with delete[].)
5180 char* titleCopy = NULL;
5181 if (title) {
5182 size_t tlen = strlen(title);
5183 titleCopy = new char[tlen + 1];
5184 memcpy(titleCopy, title, tlen + 1);
5185 pData->si.lpTitle = titleCopy;
5186 }
5187 size_t clen = strlen(cmdline);
5188 char* cmd = new char[clen + 1];
5189 memcpy(cmd, cmdline, clen + 1);
5190
5191 int res = CreateProcess(NULL, cmd, NULL, NULL, TRUE, flags, envBlock, initdir, &pData->si, &pData->procInfo);
5192
5193 delete [] titleCopy;
5194 delete [] cmd;
5195 if (envBlock) delete [] envBlock;
5196 // Parent closes the child's ends and the null handle; keeps its own ends.
5197 // hInRead MUST be closed here: while the parent holds a copy of the child's
5198 // read end, closing our WRITE end would not deliver EOF to the child, so a
5199 // child waiting on stdin would hang forever instead of seeing our close.
5200 if (hOutWrite) CloseHandle(hOutWrite);
5201 if (hErrWrite) CloseHandle(hErrWrite);
5202 if (hInRead) CloseHandle(hInRead);
5203 if (hNull) CloseHandle(hNull);
5204
5205 if (res == 0) {
5206 char msg[2048];
5207 int length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, msg, sizeof(msg), NULL);
5208 msg[length] = '\0';
5209 LogPrint(0, LOG_SYSTEM, 0, "NewProcessEx: error creating process: %s\n%s", msg, cmdline);
5210 if (pData->hOutRead) CloseHandle(pData->hOutRead);
5211 if (pData->hErrRead) CloseHandle(pData->hErrRead);
5212 if (pData->hInWrite) CloseHandle(pData->hInWrite); // 2.7c stdin pipe
5213 // NOTE: hJob is not yet created at this point (it is assigned only after
5214 // a successful CreateProcess below), so there is nothing to close here.
5215 delete pData;
5216 return 0;
5217 }
5218 if (ghJob)
5219 AssignProcessToJobObject(ghJob, pData->procInfo.hProcess);
5220 // T1.1 step 2.7c: additionally place the child in its OWN job, so
5221 // ProbeProcessWait can read whole-TREE CPU for THIS child. ghJob above is
5222 // process-wide (kill-on-close) and aggregates every child we ever started,
5223 // so its accounting cannot answer "is this child's tree idle?".
5224 // Nested jobs require Windows 8+; on failure we simply leave hJob NULL and
5225 // the probe degrades to per-process times rather than refusing to work.
5226 pData->hJob = CreateJobObject(NULL, NULL);
5227 if (pData->hJob) {
5228 if (!AssignProcessToJobObject(pData->hJob, pData->procInfo.hProcess)) {
5229 CloseHandle(pData->hJob);
5230 pData->hJob = NULL; // degraded, not fatal
5231 }
5232 }
5233 ProcessInformationMap->insert(Proc_Pair((uint32)pData->procInfo.dwProcessId, pData));
5234 return pData->procInfo.dwProcessId;
5235
5236 #else
5237 // POSIX: real implementation. Fork/execvp with optional stdout/stderr
5238 // capture pipes, per-child env overrides (merged over the parent's),
5239 // working directory, and (optionally) the child leading its own process
5240 // group so a break signal can be delivered to the whole group via
5241 // SendProcessBreak(). Lifecycle (reap/kill) is via GetProcessStatus() /
5242 // WaitForProcess() / EndProcess(), which operate on the bare pid; the
5243 // only bookkeeping kept here is the capture read-fds for ReadProcessOutput().
5244 (void)title; (void)ignoreOutput;
5245
5247 ProcessInformationMap = new std::map<uint32, ProcessData*>;
5248
5249 int outPipe[2] = { -1, -1 }, errPipe[2] = { -1, -1 };
5250 if (captureOutput) {
5251 if (pipe(outPipe) != 0) {
5252 LogPrint(0, LOG_SYSTEM, 0, "NewProcessEx: could not create output pipes for '%s'", cmdline);
5253 return 0;
5254 }
5255 if (pipe(errPipe) != 0) {
5256 LogPrint(0, LOG_SYSTEM, 0, "NewProcessEx: could not create output pipes for '%s'", cmdline);
5257 close(outPipe[0]); close(outPipe[1]);
5258 return 0;
5259 }
5260 }
5261
5262 int argc = 0;
5263 char** argv = NULL;
5264 if (!posixUseShell) {
5265 argv = SplitCommandline(cmdline, argc);
5266 if (!argc) {
5267 if (captureOutput) { close(outPipe[0]); close(outPipe[1]); close(errPipe[0]); close(errPipe[1]); }
5268 if (argv) DeleteCommandline(argv, argc);
5269 return 0;
5270 }
5271 }
5272
5274 req.cmdline = cmdline; req.initdir = initdir; req.env = env;
5275 req.captureOutput = captureOutput; req.ignoreOutput = ignoreOutput;
5276 req.newProcessGroup = newProcessGroup; req.posixUseShell = posixUseShell;
5277 req.posixParentDeathSig = posixParentDeathSig;
5278 req.argv = argv;
5279 req.outPipe0 = outPipe[0]; req.outPipe1 = outPipe[1];
5280 req.errPipe0 = errPipe[0]; req.errPipe1 = errPipe[1];
5281 req.pid = -1; req.done = false;
5282 // Die-with-parent launches go through the persistent launcher thread so
5283 // PR_SET_PDEATHSIG (Linux) is armed against a thread that never exits;
5284 // all other launches fork inline on the caller thread exactly as before.
5285 int pid = (int)(posixParentDeathSig ? SubmitLaunchRequest(req) : DoPosixForkExec(req));
5286 if (pid < 0) {
5287 if (argv) DeleteCommandline(argv, argc);
5288 if (captureOutput) { close(outPipe[0]); close(outPipe[1]); close(errPipe[0]); close(errPipe[1]); }
5289 return 0;
5290 }
5291 // Parent (all child-side setup lives in DoPosixForkExec above)
5292 if (argv) DeleteCommandline(argv, argc);
5293 if (captureOutput) {
5294 close(outPipe[1]);
5295 close(errPipe[1]);
5296 fcntl(outPipe[0], F_SETFL, fcntl(outPipe[0], F_GETFL, 0) | O_NONBLOCK);
5297 fcntl(errPipe[0], F_SETFL, fcntl(errPipe[0], F_GETFL, 0) | O_NONBLOCK);
5298 ProcessData* pData = new ProcessData;
5299 pData->outFD = outPipe[0];
5300 pData->errFD = errPipe[0];
5301 ProcessInformationMap->insert(Proc_Pair((uint32)pid, pData));
5302 }
5303 return (uint32)pid;
5304 #endif
5305}
5306
5307// T1.1 step 2.7c: write to a captured child's stdin. Windows only - see the header.
5308bool WriteProcessInput(uint32 proc, const char* data, uint32 len) {
5309 #if defined WINDOWS
5310 if (!data || !len) return false;
5311 if (!ProcessInformationMap) return false;
5312 std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
5313 if (i == ProcessInformationMap->end()) return false;
5314 ProcessData* pData = i->second;
5315 if (!pData->hInWrite) return false; // child inherited our stdin; nothing to write
5316 uint32 written = 0;
5317 while (written < len) {
5318 DWORD n = 0;
5319 if (!WriteFile(pData->hInWrite, data + written, (DWORD)(len - written), &n, NULL) || !n) {
5320 // ERROR_BROKEN_PIPE / ERROR_NO_DATA: the child closed its read end.
5321 // Report failure rather than spinning; callers treat it as "child gone".
5322 return false;
5323 }
5324 written += (uint32)n;
5325 }
5326 return true;
5327 #else
5328 // POSIX: CLISession owns the child's stdin fd directly on the fork/pipe path
5329 // and writes to it there. Routing writes through this map as well would make
5330 // two owners of one descriptor, which is the class of bug that produced the
5331 // flake-1 double-free, so it is deliberately not offered here.
5332 (void)proc; (void)data; (void)len;
5333 return false;
5334 #endif
5335}
5336
5337// T1.1 step 2.7c: close a captured child's stdin, delivering EOF. Idempotent.
5338bool CloseProcessInput(uint32 proc) {
5339 #if defined WINDOWS
5340 if (!ProcessInformationMap) return false;
5341 std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
5342 if (i == ProcessInformationMap->end()) return false;
5343 ProcessData* pData = i->second;
5344 if (!pData->hInWrite) return false;
5345 CloseHandle(pData->hInWrite);
5346 pData->hInWrite = NULL; // so the teardown paths do not double-close
5347 return true;
5348 #else
5349 (void)proc;
5350 return false;
5351 #endif
5352}
5353
5354bool ReadProcessOutput(uint32 proc, std::string& out, std::string& err) {
5355 #if defined WINDOWS
5356 if (!ProcessInformationMap) return false;
5357 std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
5358 if (i == ProcessInformationMap->end()) return false;
5359 ProcessData* pData = i->second;
5360 HANDLE handles[2] = { pData->hOutRead, pData->hErrRead };
5361 std::string* targets[2] = { &out, &err };
5362 char buf[4096];
5363 for (int n = 0; n < 2; n++) {
5364 if (!handles[n]) continue;
5365 DWORD avail = 0;
5366 // PeekNamedPipe works on anonymous pipes to avoid blocking on ReadFile.
5367 while (PeekNamedPipe(handles[n], NULL, 0, NULL, &avail, NULL) && avail) {
5368 DWORD toRead = avail < sizeof(buf) ? avail : (DWORD)sizeof(buf);
5369 DWORD got = 0;
5370 if (!ReadFile(handles[n], buf, toRead, &got, NULL) || !got) break;
5371 targets[n]->append(buf, got);
5372 }
5373 }
5374 return true;
5375 #else
5376 // POSIX: drain the non-blocking capture pipes recorded by NewProcessEx.
5377 // Appends any newly available bytes (matching the Windows path); returns
5378 // true if this pid was launched with captureOutput.
5379 if (!ProcessInformationMap) return false;
5380 std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
5381 if (i == ProcessInformationMap->end()) return false;
5382 ProcessData* pData = i->second;
5383 int fds[2] = { pData->outFD, pData->errFD };
5384 std::string* targets[2] = { &out, &err };
5385 char buf[4096];
5386 for (int n = 0; n < 2; n++) {
5387 if (fds[n] < 0) continue;
5388 ssize_t got;
5389 while ((got = read(fds[n], buf, sizeof(buf))) > 0)
5390 targets[n]->append(buf, (size_t)got);
5391 }
5392 return true;
5393 #endif
5394}
5395
5396// ------------------------------------------------------------------ 2.7d
5397// Terminal-mode child launch (POSIX PTY). See Utils.h for semantics. The
5398// master-fd bookkeeping lives here (file-local), NOT in the header ProcessData
5399// map: terminal children share the pid-based lifecycle helpers but only need
5400// one extra fd, and a separate map avoids touching the capture-path struct.
5401#if !defined(WINDOWS)
5402static std::map<uint32, int>* TerminalMasterMap = NULL;
5403static bool TerminalMasterFind(uint32 proc, int& fd) {
5404 if (!TerminalMasterMap) return false;
5405 std::map<uint32, int>::iterator i = TerminalMasterMap->find(proc);
5406 if (i == TerminalMasterMap->end()) return false;
5407 fd = i->second;
5408 return true;
5409}
5410#endif
5411
5412uint32 NewProcessTerminal(const char* cmdline, const char* initdir,
5413 const std::map<std::string, std::string>* env,
5414 uint16 cols, uint16 rows) {
5415#if defined(WINDOWS)
5416 // Stub until ConPTY lands in step 2.7e.
5417 (void)cmdline; (void)initdir; (void)env; (void)cols; (void)rows;
5418 LogPrint(0, LOG_SYSTEM, 1, "NewProcessTerminal: unsupported until ConPTY (2.7e)");
5419 return 0;
5420#else
5421 if (!cmdline || !*cmdline) return 0;
5422 // posix_openpt (not openpty) so no -lutil link dependency on older glibc.
5423 int master = posix_openpt(O_RDWR | O_NOCTTY);
5424 if (master < 0) {
5425 LogPrint(0, LOG_SYSTEM, 0, "NewProcessTerminal: posix_openpt failed (%d) for '%s'", errno, cmdline);
5426 return 0;
5427 }
5428 if (grantpt(master) != 0 || unlockpt(master) != 0) {
5429 LogPrint(0, LOG_SYSTEM, 0, "NewProcessTerminal: grantpt/unlockpt failed (%d) for '%s'", errno, cmdline);
5430 close(master);
5431 return 0;
5432 }
5433 char slaveName[256];
5434 #if defined(__APPLE__)
5435 const char* sn = ptsname(master);
5436 if (!sn) { close(master); return 0; }
5437 strncpy(slaveName, sn, sizeof(slaveName) - 1);
5438 slaveName[sizeof(slaveName) - 1] = 0;
5439 #else
5440 if (ptsname_r(master, slaveName, sizeof(slaveName)) != 0) { close(master); return 0; }
5441 #endif
5442 struct winsize ws;
5443 memset(&ws, 0, sizeof(ws));
5444 ws.ws_col = cols ? cols : 80;
5445 ws.ws_row = rows ? rows : 25;
5446 ioctl(master, TIOCSWINSZ, &ws);
5447
5448 pid_t pid = fork();
5449 if (pid < 0) { close(master); return 0; }
5450 if (pid == 0) {
5451 // Child: new session so the PTY slave becomes its CONTROLLING terminal
5452 // (setsid also makes it a process-group leader => group kill via -pid).
5453 close(master);
5454 setsid();
5455 int slave = open(slaveName, O_RDWR); // first tty open after setsid acquires it
5456 if (slave < 0) _exit(127);
5457 #ifdef TIOCSCTTY
5458 ioctl(slave, TIOCSCTTY, 0); // explicit, for platforms that need it
5459 #endif
5460 dup2(slave, STDIN_FILENO);
5461 dup2(slave, STDOUT_FILENO);
5462 dup2(slave, STDERR_FILENO);
5463 if (slave > STDERR_FILENO) close(slave);
5464 if (initdir && *initdir && chdir(initdir) != 0) _exit(126);
5465 if (env) {
5466 std::map<std::string, std::string>::const_iterator ei = env->begin();
5467 while (ei != env->end()) { setenv(ei->first.c_str(), ei->second.c_str(), 1); ei++; }
5468 }
5469 execl("/bin/sh", "sh", "-c", cmdline, (char*)NULL);
5470 _exit(127);
5471 }
5472 // Parent: keep the master, non-blocking reads.
5473 fcntl(master, F_SETFL, fcntl(master, F_GETFL, 0) | O_NONBLOCK);
5474 if (!TerminalMasterMap)
5475 TerminalMasterMap = new std::map<uint32, int>;
5476 (*TerminalMasterMap)[(uint32)pid] = master;
5477 return (uint32)pid;
5478#endif
5479}
5480
5481bool ReadProcessTerminal(uint32 proc, std::string& out, bool& eofOut) {
5482 eofOut = false;
5483#if defined(WINDOWS)
5484 (void)proc; (void)out;
5485 return false; // stub until ConPTY (2.7e)
5486#else
5487 int fd = -1;
5488 if (!TerminalMasterFind(proc, fd)) return false;
5489 char buf[4096];
5490 for (;;) {
5491 ssize_t got = read(fd, buf, sizeof(buf));
5492 if (got > 0) { out.append(buf, (size_t)got); continue; }
5493 if (got == 0) { eofOut = true; break; } // clean EOF
5494 if (errno == EINTR) continue;
5495 if (errno == EAGAIN || errno == EWOULDBLOCK) break; // no data right now
5496 // EIO: the slave side is gone (child exited/hung up). On a PTY master
5497 // this is the NORMAL end-of-stream, not an error (Thor rc requirement:
5498 // callers still reap the real exit code via waitpid on the pid).
5499 eofOut = true;
5500 break;
5501 }
5502 return true;
5503#endif
5504}
5505
5506bool WriteProcessTerminal(uint32 proc, const char* data, uint32 len) {
5507#if defined(WINDOWS)
5508 (void)proc; (void)data; (void)len;
5509 return false; // stub until ConPTY (2.7e)
5510#else
5511 if (!data || !len) return false;
5512 int fd = -1;
5513 if (!TerminalMasterFind(proc, fd)) return false;
5514 uint32 written = 0;
5515 while (written < len) {
5516 ssize_t n = write(fd, data + written, len - written);
5517 if (n < 0) {
5518 if (errno == EINTR) continue;
5519 if (errno == EAGAIN || errno == EWOULDBLOCK) { Sleep(2); continue; }
5520 return false; // EIO (child gone) or other hard error
5521 }
5522 written += (uint32)n;
5523 }
5524 return true;
5525#endif
5526}
5527
5528bool ResizeProcessTerminal(uint32 proc, uint16 cols, uint16 rows) {
5529#if defined(WINDOWS)
5530 (void)proc; (void)cols; (void)rows;
5531 return false; // stub until ConPTY (2.7e)
5532#else
5533 int fd = -1;
5534 if (!TerminalMasterFind(proc, fd)) return false;
5535 struct winsize ws;
5536 memset(&ws, 0, sizeof(ws));
5537 ws.ws_col = cols ? cols : 80;
5538 ws.ws_row = rows ? rows : 25;
5539 if (ioctl(fd, TIOCSWINSZ, &ws) != 0) return false;
5540 kill(-(pid_t)proc, SIGWINCH); // child leads its own group (setsid)
5541 return true;
5542#endif
5543}
5544
5545void CloseProcessTerminal(uint32 proc) {
5546#if defined(WINDOWS)
5547 (void)proc; // stub until ConPTY (2.7e)
5548#else
5549 if (!TerminalMasterMap) return;
5550 std::map<uint32, int>::iterator i = TerminalMasterMap->find(proc);
5551 if (i == TerminalMasterMap->end()) return;
5552 close(i->second); // hangup: child's foreground group gets SIGHUP
5553 TerminalMasterMap->erase(i);
5554#endif
5555}
5556
5557bool SendProcessBreak(uint32 proc, int sig) {
5558 #if defined WINDOWS
5559 (void)sig;
5560 // Requires the child to have been created with CREATE_NEW_PROCESS_GROUP.
5561 // The group id equals the child's process id.
5562 return (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, (DWORD)proc) != 0);
5563 #else
5564 return (kill((pid_t)proc, sig) == 0);
5565 #endif
5566}
5567
5568uint8 WaitForProcess(uint32 proc, uint32 timeout, int &returncode) {
5569 #if defined WINDOWS
5570 DWORD exitcode;
5571 std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
5572 if (i != ProcessInformationMap->end()) {
5573 ProcessData* pData = i->second;
5574 if (WaitForSingleObject(pData->procInfo.hProcess, timeout) == WAIT_OBJECT_0) {
5575 if (GetExitCodeProcess(pData->procInfo.hProcess, &exitcode) && (exitcode == STILL_ACTIVE)) {
5576 return PROC_RUNNING;
5577 }
5578 else {
5579 ProcessInformationMap->erase(i);
5580 CloseHandle(pData->procInfo.hProcess);
5581 CloseHandle(pData->procInfo.hThread);
5582 if (pData->hOutRead) CloseHandle(pData->hOutRead);
5583 if (pData->hErrRead) CloseHandle(pData->hErrRead);
5584 if (pData->hInWrite) CloseHandle(pData->hInWrite); // 2.7c stdin pipe
5585 if (pData->hJob) CloseHandle(pData->hJob); // 2.7c per-child job
5586 delete pData;
5587 returncode = exitcode;
5588 return PROC_TERMINATED;
5589 }
5590 }
5591 return PROC_TIMEOUT;
5592 }
5593 else {
5594 HANDLE hproc = OpenProcess(NULL, NULL, (DWORD)proc);
5595 if (!hproc) {
5596 return PROC_ERROR;
5597 }
5598 if (WaitForSingleObject(hproc, timeout) == WAIT_OBJECT_0) {
5599 if (GetExitCodeProcess(hproc, &exitcode) && (exitcode == STILL_ACTIVE)) {
5600 CloseHandle(hproc);
5601 return PROC_RUNNING;
5602 }
5603 else {
5604 returncode = exitcode;
5605 CloseHandle(hproc);
5606 return PROC_TERMINATED;
5607 }
5608 }
5609 else {
5610 CloseHandle(hproc);
5611 return PROC_RUNNING;
5612 }
5613 }
5614 #else
5615 uint64 start = GetTimeNow();
5616 do {
5617 if (GetProcessStatus(proc, returncode) < PROC_RUNNING)
5618 return PROC_TERMINATED;
5619 utils::Sleep(10);
5620 } while (GetTimeAgeMS(start) < timeout);
5621 return PROC_RUNNING;
5622
5623 #endif
5624}
5625
5626
5627
5628
5629
5630
5631
5632
5633
5635// DLL Libraries //
5637
5638bool SetCommandLine(int argc, char* argv[]) {
5639
5642
5643 std::string key;
5644 const char* val;
5645 int32 n;
5646 CommandLineInfo::CommandLineInfoSingleton->CommandLineString = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec = argv[0];
5647 CommandLineInfo::CommandLineInfoSingleton->CommandLineItems.push_back(argv[0]);
5648 for (n=1; n<argc; n++) {
5649 CommandLineInfo::CommandLineInfoSingleton->CommandLineItems.push_back(argv[n]);
5650 key = argv[n];
5651 val = strchr(argv[n], '=');
5652 if (val)
5653 CommandLineInfo::CommandLineInfoSingleton->CommandLineArgs[key.substr(0, val-argv[n])] = val+1;
5654 CommandLineInfo::CommandLineInfoSingleton->CommandLineString += ' ';
5655 CommandLineInfo::CommandLineInfoSingleton->CommandLineString += argv[n];
5656 }
5657
5658 //printf("Full: '%s'\n", CommandLineString.c_str());
5659 //printf("Base: '%s'\n", CommandLineExec.c_str());
5660
5661 //n = 0;
5662 //std::vector<std::string>::iterator it = CommandLineItems.begin(), itEnd = CommandLineItems.end();
5663 //while (it != itEnd) {
5664 // printf("[%u] '%s'\n", n++, (*it).c_str());
5665 // it++;
5666 //}
5667
5668 //n = 0;
5669 //std::map<std::string, std::string>::iterator mit = CommandLineArgs.begin(), mitEnd = CommandLineArgs.end();
5670 //while (mit != mitEnd) {
5671 // printf("[%u] '%s' = '%s'\n", n++, mit->first.c_str(), mit->second.c_str());
5672 // mit++;
5673 //}
5674
5675 //printf("Executable: '%s' '%s'\n", GetCommandLinePath().c_str(), GetCommandLineExecutableOnly().c_str());
5676 return true;
5677}
5678
5679std::string GetCommandLine() {
5681 return "";
5682 return CommandLineInfo::CommandLineInfoSingleton->CommandLineString;
5683}
5684
5685std::string GetCommandLinePath() {
5687 return "";
5688 std::string::size_type p1 = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.find_last_of('/');
5689 std::string::size_type p2 = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.find_last_of('\\');
5690 if (p1 == std::string::npos) {
5691 if (p2 == std::string::npos)
5692 return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec;
5693 else
5694 return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(0, p2+1);
5695 }
5696 else if (p2 == std::string::npos)
5697 return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(0, p1+1);
5698 else
5699 return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(0, (p1 > p2) ? p1+1 : p2+1);
5700}
5701
5704 return "";
5705 return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec;
5706}
5707
5710 return "";
5711 std::string::size_type p1 = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.find_last_of('/');
5712 std::string::size_type p2 = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.find_last_of('\\');
5713 if (p1 == std::string::npos) {
5714 if (p2 == std::string::npos)
5715 return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec;
5716 else
5717 return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(p2+1);
5718 }
5719 else if (p2 == std::string::npos)
5720 return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(p1+1);
5721 else
5722 return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr((p1 > p2) ? p1+1 : p2+1);
5723}
5724
5727 return 0;
5728 return (uint32)CommandLineInfo::CommandLineInfoSingleton->CommandLineItems.size();
5729}
5730
5731std::string GetCommandLineArg(uint16 n) {
5733 return "";
5734 return CommandLineInfo::CommandLineInfoSingleton->CommandLineItems[n];
5735}
5736
5737std::string GetCommandLineArg(const char* key) {
5739 return "";
5740 return CommandLineInfo::CommandLineInfoSingleton->CommandLineArgs[key];
5741}
5742
5743bool HasCommandLineArg(const char* key, std::string& value) {
5745 return false;
5746 std::map<std::string, std::string>::const_iterator it =
5747 CommandLineInfo::CommandLineInfoSingleton->CommandLineArgs.find(key);
5748 if (it == CommandLineInfo::CommandLineInfoSingleton->CommandLineArgs.end())
5749 return false;
5750 value = it->second;
5751 return true;
5752}
5753
5754
5755
5757 handle = NULL;
5758}
5760 if (handle) {
5761 #ifdef WINDOWS
5762 FreeLibrary(handle);
5763 #else // WINDOWS
5764 // dlclose() bug: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=28625
5765 // __dso_handle unreferenced
5766 // if there are any globally desclared objects in the library!!!
5767 dlclose(handle);
5768 #endif // WINDOWS
5769 }
5770}
5771
5772std::string Library::patchLibraryFilename(const char* filename, const char* path) {
5773 #ifdef WINDOWS
5774 return filename;
5775 #else
5776 std::string realLibName;
5777 if (utils::TextEndsWith(filename, ".so"))
5778 realLibName = filename;
5779 else
5780 realLibName = utils::StringFormat("%s.so", filename);
5781
5782 // Start of the file name within the path. (This used to read
5783 // `(filenameStart = find_last_of('/') == npos)`, which assigns the comparison, so any
5784 // filename with a directory got start 1 and became "/libhome/.../Name.so": a
5785 // <library path=".."> could never load.)
5786 size_t slash = realLibName.find_last_of('/');
5787 int32 filenameStart = (slash == std::string::npos) ? 0 : (int32)(slash + 1);
5788
5789 if (!utils::TextStartsWith(realLibName.c_str() + filenameStart, "lib", true))
5790 realLibName.insert(filenameStart, "lib");
5791
5792 if (!filenameStart) {
5793 if (!path)
5794 realLibName.insert(0, "./");
5795 else
5796 realLibName.insert(0, path);
5797 }
5798
5799 return realLibName;
5800 #endif
5801}
5802
5803bool Library::load(const char* filename) {
5804 if (!filename)
5805 return false;
5806 std::string errorText;
5807 //printf("Trying to find library: '%s'...\n", filename);
5808
5809 #ifdef WINDOWS
5810 LPVOID lpMsgBuf; //Message Buffer
5811 #ifdef _DEBUG
5812 handle = LoadLibrary(utils::StringFormat("%sDebug", filename).c_str());
5813 if (handle == NULL) {
5814 FormatMessage(
5815 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
5816 NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
5817 (LPTSTR)&lpMsgBuf, 0, NULL);
5818 LogPrint(0, LOG_SYSTEM, 0, "Could not find or load debug library '%sDebug': %s", filename, lpMsgBuf);
5819 LocalFree(lpMsgBuf);
5820 return false;
5821 }
5822 #else
5823 handle = LoadLibrary(filename);
5824 if (handle == NULL) {
5825 FormatMessage(
5826 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
5827 NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
5828 (LPTSTR) &lpMsgBuf, 0, NULL);
5829 LogPrint(0, LOG_SYSTEM, 0, "Could not find or load library '%s': %s", filename, lpMsgBuf);
5830 LocalFree(lpMsgBuf);
5831 return false;
5832 }
5833 #endif
5834 #else // WINDOWS
5835 handle = 0;
5836 const char* dlErrorText;
5837 std::string realLibName = patchLibraryFilename(filename);
5838 //printf("Trying patched library: '%s'...\n", realLibName.c_str());
5839
5840 // clear errors
5841 dlerror();
5842 #ifdef _DEBUG
5843 std::string debugName = realLibName;
5844 utils::StringSingleReplace(debugName, ".so", "Debug.so", true);
5845 // printf("Trying debug library: '%s'...\n", debugName.c_str());
5846 handle = dlopen(debugName.c_str(), RTLD_NOW | RTLD_GLOBAL);
5847 if (!handle) {
5848 dlErrorText = dlerror();
5849 // printf("Error 1: '%s'...\n", dlErrorText);
5850 if (dlErrorText && strlen(dlErrorText) &&
5851 !(strstr(dlErrorText, "No such file") && strstr(dlErrorText, debugName.c_str())))
5852 errorText = dlErrorText;
5853 }
5854 #endif
5855
5856 if (!handle && !errorText.size()) {
5857 // A filename with a directory (<library path="..">) is opened exactly where it says.
5858 // This dlopen had been commented out, so such libraries were never even tried in a
5859 // release build. Path-less names keep their old behaviour: the executable-directory
5860 // lookup below.
5861 if (strchr(filename, '/'))
5862 handle = dlopen(realLibName.c_str(), RTLD_NOW | RTLD_GLOBAL);
5863 if (!handle) {
5864 dlErrorText = dlerror();
5865 // printf("Error 2: '%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 && !errorText.size() && !strchr(filename, '/')) {
5873 realLibName = patchLibraryFilename(filename, utils::GetCommandLinePath().c_str());
5874 // printf("Trying library: '%s'...\n", realLibName.c_str());
5875 handle = dlopen(realLibName.c_str(), RTLD_NOW | RTLD_GLOBAL);
5876 if (!handle) {
5877 dlErrorText = dlerror();
5878 // printf("Error 3: '%s'...\n", dlErrorText);
5879 if (dlErrorText && strlen(dlErrorText) &&
5880 !(strstr(dlErrorText, "No such file") && strstr(dlErrorText, realLibName.c_str())))
5881 errorText = dlErrorText;
5882 }
5883 }
5884
5885 if (handle == NULL) {
5886 if (errorText.size())
5887 LogPrint(0, LOG_SYSTEM, 0, "Could not load library file '%s': %s", realLibName.c_str(), errorText.c_str());
5888 else
5889 LogPrint(0, LOG_SYSTEM, 0, "Could not find library file '%s'", filename);
5890 return false;
5891 }
5892 #endif // WINDOWS
5893 return true;
5894}
5895
5897 if (handle == NULL)
5898 return NULL;
5899 if (!strlen(funcName))
5900 return NULL;
5901
5902 LibraryFunction func;
5903
5904 #ifdef WINDOWS
5905 func = (LibraryFunction)GetProcAddress(handle, funcName);
5906 if (func == NULL) {
5907 LPVOID lpMsgBuf; //Message Buffer
5908 //Generate Error Message from GetLastError
5909 FormatMessage(
5910 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
5911 NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
5912 (LPTSTR) &lpMsgBuf, 0, NULL);
5913 LogPrint(0, LOG_SYSTEM, 0, "Could not find library function: %s: %s", funcName, lpMsgBuf);
5914 // Free the buffer.
5915 LocalFree(lpMsgBuf);
5916 return NULL;
5917 }
5918 #else // WINDOWS
5919 const char* errmsg;
5920 dlerror();
5921 func = (LibraryFunction)dlsym(handle, funcName);
5922 errmsg = dlerror();
5923 if (errmsg != NULL) {
5924 LogPrint(0, LOG_SYSTEM, 0, "Could not find library function: %s: %s", (char*) funcName, (char*) errmsg);
5925 return NULL;
5926 }
5927 #endif // WINDOWS
5928 return func;
5929}
5930
5931Library* OpenLibrary(const char* libName) {
5932 Library* lib = new Library();
5933 if (!lib->load(libName)) {
5934 delete(lib);
5935 return NULL;
5936 }
5937 return lib;
5938}
5939
5940
5942// OS System //
5944
5945char OSLocalHostName[1024] = {0};
5946char OSArchitectureName[1024] = {0};
5947char OSName[1024] = {0};
5948
5949const char* GetComputerName() {
5950 //char name[1024];
5951 char* name = new char[1024];
5952 if (strlen(OSLocalHostName) == 0) {
5953 #ifdef WINDOWS
5954
5955 DWORD size = 1024;
5956 #ifdef POCKETPC
5957 delete [] name;
5958 return "PocketPC";
5959 #else
5960 if (::GetComputerName(name, &size) == 0) {
5961 delete [] name;
5962 return "";
5963 }
5964 else
5965 utils::strcpyavail(OSLocalHostName, name, 1024, true);
5966 #endif
5967
5968 //if (gethostname(name, 1024) == 0)
5969 // strcpy(OSLocalHostName, name);
5970 //else {
5971 // int er = WSAGetLastError();
5972 // if (er == WSANOTINITIALISED) {
5973 // WSADATA info;
5974 // if (WSAStartup(MAKEWORD(1,1), &info) == 0) {
5975 // // Now we can retry the socket() function
5976 // if (gethostname(hname, 1000) == 0)
5977 // strcpy(OSLocalHostName, name);
5978 // }
5979 // }
5980 //}
5981 #else
5982 std::string outString, errString;
5983 if (!RunOSCommand("uname -n", NULL, 1000, outString, errString)) {
5984 //if (!RunOSTextCommand("uname -n", name, size))
5985 delete [] name;
5986 return "";
5987 }
5988 else
5989 utils::strcpyavail(OSLocalHostName, outString.c_str(), 1024, true);
5990 #endif // WINDOWS
5991 }
5992 delete [] name;
5993 return OSLocalHostName;
5994}
5995
5997
5998 #if defined WINDOWS
5999 HANDLE hProcess = GetCurrentProcess();
6000 PROCESS_MEMORY_COUNTERS pmc;
6001 if (hProcess == NULL)
6002 return 0;
6003 if ( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)) != 0)
6004 return (uint64)pmc.WorkingSetSize;
6005 else
6006 return 0;
6007 #elif defined OSX
6008 struct task_basic_info_64 info;
6009 mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT;
6010 if (task_info(mach_task_self(), TASK_BASIC_INFO_64, (task_info_t)&info, &count) == KERN_SUCCESS)
6011 return (uint64)info.resident_size;
6012 return 0;
6013 #else
6014 /* Linux ---------------------------------------------------- */
6015 long rss = 0L;
6016 FILE* fp = NULL;
6017 if ( (fp = fopen( "/proc/self/statm", "r" )) == NULL )
6018 return (uint64)0L; /* Can't open? */
6019 if ( fscanf( fp, "%*s%ld", &rss ) != 1 ) {
6020 fclose( fp );
6021 return (uint64)0L; /* Can't read? */
6022 }
6023 fclose( fp );
6024 return (uint64)rss * (uint64)sysconf( _SC_PAGESIZE);
6025 #endif
6026}
6027
6029
6030 #if defined WINDOWS
6031 HANDLE hProcess = GetCurrentProcess();
6032 PROCESS_MEMORY_COUNTERS pmc;
6033 if (hProcess == NULL)
6034 return 0;
6035 if ( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)) != 0)
6036 return (uint64)pmc.PeakWorkingSetSize;
6037 else
6038 return 0;
6039 #elif defined OSX
6040 // macOS/BSD: ru_maxrss is in bytes (Linux uses KB)
6041 struct rusage usage;
6042 getrusage(RUSAGE_SELF, &usage);
6043 return (uint64)usage.ru_maxrss;
6044 #else
6045 // Linux: ru_maxrss is in KB
6046 struct rusage usage;
6047 getrusage( RUSAGE_SELF, &usage );
6048 return ((uint64)usage.ru_maxrss) * 1024L;
6049
6050
6052 //struct psinfo psinfo;
6053 //int fd = -1;
6054 //if ( (fd = open( "/proc/self/psinfo", O_RDONLY )) == -1 )
6055 // return (size_t)0L; /* Can't open? */
6056 //if ( read( fd, &psinfo, sizeof(psinfo) ) != sizeof(psinfo) )
6057 //{
6058 // close( fd );
6059 // return (size_t)0L; /* Can't read? */
6060 //}
6061 //close( fd );
6062 //return (size_t)(psinfo.pr_rssize * 1024L);
6063
6064 #endif
6065}
6066
6067
6068uint16 GetCPUCount() {
6069 #if defined WINDOWS
6070 SYSTEM_INFO siSysInfo;
6071 GetSystemInfo(&siSysInfo);
6072 return (uint16) siSysInfo.dwNumberOfProcessors;
6073 #elif defined OSX
6074 int ncpu = 0;
6075 size_t len = sizeof(ncpu);
6076 if (sysctlbyname("hw.ncpu", &ncpu, &len, NULL, 0) == 0 && ncpu > 0)
6077 return (uint16)(ncpu > 65535 ? 65535 : ncpu);
6078 return 0;
6079 #else
6080 long n = sysconf(_SC_NPROCESSORS_ONLN);
6081 if (n > 0)
6082 return (uint16)(n > 65535 ? 65535 : (uint16)n);
6083 return 0;
6084 #endif
6085}
6086
6088 #if defined WINDOWS
6089 SYSTEM_INFO siSysInfo;
6090 GetSystemInfo(&siSysInfo);
6091 switch(siSysInfo.wProcessorArchitecture) {
6092 case PROCESSOR_ARCHITECTURE_AMD64:
6093 return OSCPU_AMD64;
6094 case PROCESSOR_ARCHITECTURE_IA64:
6095 return OSCPU_IA64;
6096 case PROCESSOR_ARCHITECTURE_INTEL:
6097 return OSCPU_X86;
6098 default:
6099 return OSCPU_UNKNOWN;
6100 }
6101 #elif defined OSX
6102 #if defined(__x86_64__)
6103 return OSCPU_AMD64;
6104 #elif defined(__i386__)
6105 return OSCPU_X86;
6106 #elif defined(__aarch64__)
6107 return OSCPU_UNKNOWN;
6108 #else
6109 return OSCPU_UNKNOWN;
6110 #endif
6111 #else
6112 #if defined(__x86_64__)
6113 return OSCPU_AMD64;
6114 #elif defined(__i386__)
6115 return OSCPU_X86;
6116 #elif defined(__aarch64__)
6117 return OSCPU_UNKNOWN;
6118 #else
6119 return OSCPU_UNKNOWN;
6120 #endif
6121 #endif
6122}
6123
6124uint64 GetCPUSpeed() {
6125 #if defined WINDOWS
6126 //HKEY hKey;
6127 //LONG lRet;
6128
6129 // Test for SP6 versus SP6a.
6130 //JString key = "HARDWARE\\DESCRIPTION\\SYSTEM\\CentralProcessor\\0";
6131 //lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE, key, 0, KEY_QUERY_VALUE, &hKey );
6132 //if( lRet == ERROR_SUCCESS ) {
6133 // DWORD len = sizeof(int);
6134 // BYTE data[sizeof(int)];
6135 // key = "~MHz";
6136 // lRet = RegQueryValueEx(hKey, key, NULL, NULL, data, &len);
6137 // if (lRet == ERROR_SUCCESS) {
6138 // RegCloseKey(hKey);
6139 // return (uint64) (data[3] << 24) + (data[2] << 16) + (data[1] << 8) + (data[0] << 0);
6140 // }
6141 // else {
6142 // RegCloseKey(hKey);
6143 // return 0;
6144 // }
6145 //}
6146 #elif defined OSX
6147 uint64 freq = 0;
6148 size_t len = sizeof(freq);
6149 if (sysctlbyname("hw.cpufrequency", &freq, &len, NULL, 0) == 0)
6150 return freq;
6151 return 0;
6152 #else
6153 // Linux: optional /proc/cpuinfo cpu MHz
6154 std::ifstream cpuinfo("/proc/cpuinfo");
6155 std::string line;
6156 while (cpuinfo && getline(cpuinfo, line)) {
6157 if (line.compare(0, 7, "cpu MHz") == 0) {
6158 size_t colon = line.find(':');
6159 if (colon != std::string::npos) {
6160 double mhz = 0;
6161 if (sscanf(line.c_str() + colon + 1, "%lf", &mhz) == 1 && mhz > 0)
6162 return (uint64)(mhz * 1000000.0);
6163 }
6164 break;
6165 }
6166 }
6167 #endif
6168 return 0;
6169}
6170
6172 #if defined WINDOWS
6173 MEMORYSTATUS memstat;
6174 GlobalMemoryStatus(&memstat);
6175 return (uint64)memstat.dwTotalPhys;
6176 #else
6177 #ifdef __APPLE__
6178 uint64 memsize = 0;
6179 size_t len = sizeof(memsize);
6180 if (sysctlbyname("hw.memsize", &memsize, &len, NULL, 0) == 0)
6181 return memsize;
6182 return 0;
6183 #else
6184 struct sysinfo info;
6185 if (sysinfo(&info) == 0)
6186 return info.totalram;
6187 else
6188 return 0;
6189 #endif
6190 #endif
6191 return 0;
6192}
6193
6194bool GetSystemMemoryUsage(uint64 &totalRAM, uint64 &freeRAM) {
6195 #if defined WINDOWS
6196 MEMORYSTATUSEX memstat;
6197 memset(&memstat, 0, sizeof(MEMORYSTATUSEX));
6198 memstat.dwLength = sizeof(MEMORYSTATUSEX);
6199 if (!GlobalMemoryStatusEx(&memstat)) {
6200 int a = GetLastError();
6201 return false;
6202 }
6203 totalRAM = (uint64)memstat.ullTotalPhys;
6204 freeRAM = (uint64)memstat.ullAvailPhys;
6205 return true;
6206 #else
6207 #ifdef __APPLE__
6208 uint64 memsize = 0;
6209 size_t len = sizeof(memsize);
6210 if (sysctlbyname("hw.memsize", &memsize, &len, NULL, 0) != 0)
6211 return false;
6212 totalRAM = memsize;
6213 vm_statistics64_data_t vm_stat;
6214 mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
6215 if (host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t)&vm_stat, &count) != KERN_SUCCESS)
6216 return false;
6217 vm_size_t page_size;
6218 if (host_page_size(mach_host_self(), &page_size) != KERN_SUCCESS)
6219 return false;
6220 freeRAM = (uint64)vm_stat.free_count * (uint64)page_size;
6221 return true;
6222 #else
6223 struct sysinfo info;
6224 if (sysinfo(&info) == 0) {
6225 totalRAM = info.totalram;
6226 freeRAM = info.freeram;
6227 return true;
6228 }
6229 else
6230 return false;
6231 #endif
6232 #endif
6233}
6234
6236 if (strlen(OSArchitectureName) == 0) {
6237 #if defined WINDOWS
6238 SYSTEM_INFO siSysInfo;
6239 GetSystemInfo(&siSysInfo);
6240
6241 switch(siSysInfo.wProcessorArchitecture) {
6242 case PROCESSOR_ARCHITECTURE_UNKNOWN:
6243 utils::strcpyavail(OSArchitectureName, "i386", 1024, true);
6244 case PROCESSOR_ARCHITECTURE_INTEL:
6245 switch(siSysInfo.wProcessorLevel) {
6246 case 3:
6247 utils::strcpyavail(OSArchitectureName, "i386", 1024, true);
6248 break;
6249 case 4:
6250 utils::strcpyavail(OSArchitectureName, "i486", 1024, true);
6251 break;
6252 case 5:
6253 utils::strcpyavail(OSArchitectureName, "i586", 1024, true);
6254 break;
6255 case 6:
6256 utils::strcpyavail(OSArchitectureName, "i686", 1024, true);
6257 break;
6258 case 7:
6259 utils::strcpyavail(OSArchitectureName, "i786", 1024, true);
6260 break;
6261 case 8:
6262 utils::strcpyavail(OSArchitectureName, "i886", 1024, true);
6263 break;
6264 case 9:
6265 utils::strcpyavail(OSArchitectureName, "i986", 1024, true);
6266 break;
6267 default:
6268 utils::strcpyavail(OSArchitectureName, "i686", 1024, true);
6269 break;
6270 }
6271 case PROCESSOR_ARCHITECTURE_MIPS:
6272 utils::strcpyavail(OSArchitectureName, "mips", 1024, true);
6273 break;
6274 case PROCESSOR_ARCHITECTURE_ALPHA:
6275 utils::strcpyavail(OSArchitectureName, "alpha", 1024, true);
6276 break;
6277 case PROCESSOR_ARCHITECTURE_PPC:
6278 utils::strcpyavail(OSArchitectureName, "ppc", 1024, true);
6279 break;
6280 case PROCESSOR_ARCHITECTURE_IA64:
6281 utils::strcpyavail(OSArchitectureName, "ia64", 1024, true);
6282 break;
6283 case PROCESSOR_ARCHITECTURE_AMD64:
6284 utils::strcpyavail(OSArchitectureName, "amd64", 1024, true);
6285 break;
6286 default:
6287 break;
6288 }
6289 #else
6290 // ######################################
6291 #endif
6292 }
6293 return OSArchitectureName;
6294}
6295
6296const char* GetSystemOSName() {
6297 if (strlen(OSName) == 0) {
6298 #if defined WINDOWS
6299 #ifdef POCKETPC
6300 utils::strcpyavail(OSName, "Microsoft PocketPC", 1024, true);
6301 #else
6302 switch(GetCPUArchitecture()) {
6303 case OSCPU_AMD64:
6304 utils::strcpyavail(OSName, "Microsoft Windows X64", 1024, true);
6305 break;
6306 case OSCPU_IA64:
6307 utils::strcpyavail(OSName, "Microsoft Windows IA64", 1024, true);
6308 break;
6309 case OSCPU_X86:
6310 utils::strcpyavail(OSName, "Microsoft Windows X86", 1024, true);
6311 break;
6312 default:
6313 utils::strcpyavail(OSName, "Microsoft Windows", 1024, true);
6314 break;
6315 }
6316 #endif
6317 #else
6318 // ######################################
6319 #endif
6320 }
6321 return OSName;
6322}
6323
6324bool GetSystemOSVersion(uint16& major, uint16& minor, uint16& build, char* text, uint16 textSize) {
6325 major = 0;
6326 minor = 0;
6327 build = 0;
6328 text[0] = 0;
6329 #if defined WINDOWS
6330 #ifdef POCKETPC
6331
6332 OSVERSIONINFO osvi;
6333 BOOL bOsVersionInfoEx;
6334
6335 // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
6336 // If that fails, try using the OSVERSIONINFO structure.
6337
6338 ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
6339 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
6340
6341 if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) )
6342 {
6343 osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
6344 if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) )
6345 return false;
6346 }
6347
6348 major = osvi.dwMajorVersion;
6349 minor = osvi.dwMinorVersion;
6350 if (strlen((char*)osvi.szCSDVersion) < textSize)
6351 utils::strcpyavail(text, (char*)osvi.szCSDVersion, textSize, true);
6352 #else
6353
6354 OSVERSIONINFOEX osvi;
6355 BOOL bOsVersionInfoEx;
6356
6357 // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
6358 // If that fails, try using the OSVERSIONINFO structure.
6359
6360 ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
6361 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
6362
6363 if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) )
6364 {
6365 osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
6366 if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) )
6367 return false;
6368 }
6369
6370 switch (osvi.dwPlatformId)
6371 {
6372 // Test for the Windows NT product family.
6373 case VER_PLATFORM_WIN32_NT:
6374
6375 // Display service pack (if any) and build number.
6376
6377 if( osvi.dwMajorVersion == 4 &&
6378 lstrcmpi( osvi.szCSDVersion, "Service Pack 6" ) == 0 )
6379 {
6380 HKEY hKey;
6381 LONG lRet;
6382
6383 // Test for SP6 versus SP6a.
6384 lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
6385 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009",
6386 0, KEY_QUERY_VALUE, &hKey );
6387 if( lRet == ERROR_SUCCESS ) {
6388 if (textSize > 16)
6389 utils::strcpyavail(text, "Service Pack 6a", textSize, true);
6390 build = osvi.dwBuildNumber & 0xFFFF;
6391 }
6392 else // Windows NT 4.0 prior to SP6a
6393 {
6394 major = (uint16)osvi.dwMajorVersion;
6395 minor = (uint16)osvi.dwMinorVersion;
6396 build = (uint16)osvi.dwBuildNumber & 0xFFFF;
6397 if (strlen((char*)osvi.szCSDVersion) < textSize)
6398 utils::strcpyavail(text, (char*)osvi.szCSDVersion, textSize, true);
6399 }
6400 RegCloseKey( hKey );
6401 }
6402 else // Windows NT 3.51 and earlier or Windows 2000 and later
6403 {
6404 major = (uint16)osvi.dwMajorVersion;
6405 minor = (uint16)osvi.dwMinorVersion;
6406 build = (uint16)osvi.dwBuildNumber & 0xFFFF;
6407
6408 if (strlen((char*)osvi.szCSDVersion) < textSize)
6409 utils::strcpyavail(text, (char*)osvi.szCSDVersion, textSize, true);
6410 }
6411 break;
6412 }
6413
6414 #endif
6415 #else
6416 // ######################################
6417 #endif
6418 return true;
6419}
6420
6421//bool RunOSTextCommand(const char* cmd, char** result, uint32 size) {
6422//
6423// #ifdef WINDOWS
6424// // Use RunOSCommand instead
6425// return false;
6426// #else
6427// fflush(stdin);
6428// fflush(stdout);
6429// FILE* runfile = popen(cmd, "r");
6430// if (runfile == NULL)
6431// return false;
6432//
6433// int res;
6434// int size = 4096;
6435// char buffer[size+1];
6436// std::string output;
6437//
6438// res = fread(buffer, 1, size, runfile);
6439// if (res <= 0) {
6440// pclose(runfile);
6441// return false;
6442// }
6443// do {
6444// buffer[res] = 0;
6445// output += buffer;
6446// } while (res = fread(buffer, 1, size, runfile));
6447//
6448// pclose(runfile);
6449// delete [] buffer;
6450// *result = new char[output.length()+1];
6451// memcpy(*result, output.c_str(), output.length());
6452// result[output.length()] = 0;
6453// return true;
6454// #endif
6455//}
6456
6457//bool GetSystemName(uint32 id, const char* title, char* name, uint32 size) {
6458// if ( (name == NULL) || (size < 64) )
6459// return false;
6460// memset(name, 0, 64);
6461// uint32 pos = 0;
6462// #ifdef WINDOWS
6463// strcpy_s(name, size, "Global\\");
6464// strcpy_s(name+(pos = (uint32)strlen(name)), size-pos, title);
6465// // _itoa_s(id, name+(pos = (uint32)strlen(name)), size-pos, 16);
6466// #else
6467// strncpy(name, "/", size);
6468// pos = (uint32)strlen(name);
6469// strncpy(name+pos, title, size-pos);
6470// #endif
6471// Int2Ascii((int32)id, name+(pos = (uint32)strlen(name)), size-pos, 16);
6472// return true;
6473//}
6474
6475char* Int2Ascii(int64 value, char* result, uint16 size, uint8 base) {
6476 if (base < 2 || base > 36) {
6477 result[0] = 0;
6478 return result;
6479 }
6480
6481 char* ptr = result, *ptr1 = result, tmp_char;
6482 int64 tmp_value;
6483
6484 do {
6485 tmp_value = value;
6486 value /= base;
6487 *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)];
6488 } while ( value );
6489
6490 // Apply negative sign
6491 if (tmp_value < 0)
6492 *ptr++ = '-';
6493 *ptr-- = '\0';
6494 while(ptr1 < ptr) {
6495 tmp_char = *ptr;
6496 *ptr--= *ptr1;
6497 *ptr1++ = tmp_char;
6498 }
6499 return result;
6500}
6501
6502char* Uint2Ascii(uint64 value, char* result, uint16 size, uint8 base) {
6503 if (base < 2 || base > 36) {
6504 result[0] = 0;
6505 return result;
6506 }
6507
6508 char* ptr = result, *ptr1 = result, tmp_char;
6509 int64 tmp_value;
6510
6511 do {
6512 tmp_value = value;
6513 value /= base;
6514 *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)];
6515 } while ( value );
6516
6517 *ptr-- = '\0';
6518 while(ptr1 < ptr) {
6519 tmp_char = *ptr;
6520 *ptr--= *ptr1;
6521 *ptr1++ = tmp_char;
6522 }
6523 return result;
6524}
6525
6526unsigned char* Ascii2UTF16LE(const char* ascii, uint32 len, uint32& size) {
6527 size = (len * 2) + 4;
6528 unsigned char* result = new unsigned char[size];
6529 result[0] = 0xff;
6530 result[1] = 0xfe;
6531 uint32 i = 2;
6532 const char* src = ascii;
6533 for (uint32 n = 0; n < len; n++) {
6534 result[i++] = *src;
6535 result[i++] = 0;
6536 src++;
6537 }
6538 result[i++] = 0;
6539 result[i++] = 0;
6540 size = (len * 2) + 2;
6541 return result;
6542}
6543
6544
6545bool GetSocketError(int error, char* errorString, uint16 errorStringMaxSize, bool* isRecoverable) {
6546
6547 if (errorStringMaxSize < 128)
6548 return false;
6549
6550 #ifdef WINDOWS
6551
6552 if (error == WSANOTINITIALISED) {
6553 utils::strcpyavail(errorString, "Cannot initialize WinSock!", errorStringMaxSize, true);
6554 *isRecoverable = false;
6555 }
6556 else if (error == WSAENETDOWN) {
6557 utils::strcpyavail(errorString, "The network subsystem or the associated service provider has failed", errorStringMaxSize, true);
6558 *isRecoverable = false;
6559 }
6560 else if (error == WSAEAFNOSUPPORT) {
6561 utils::strcpyavail(errorString, "The specified address family is not supported", errorStringMaxSize, true);
6562 *isRecoverable = false;
6563 }
6564 else if (error == WSAEINPROGRESS) {
6565 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);
6566 *isRecoverable = true;
6567 }
6568 else if (error == WSAEMFILE) {
6569 utils::strcpyavail(errorString, "No more socket descriptors are available", errorStringMaxSize, true);
6570 *isRecoverable = false;
6571 }
6572 else if (error == WSAENOBUFS) {
6573 utils::strcpyavail(errorString, "No buffer space is available. The socket cannot be created", errorStringMaxSize, true);
6574 *isRecoverable = false;
6575 }
6576 else if (error == WSAEPROTONOSUPPORT) {
6577 utils::strcpyavail(errorString, "The specified protocol is not supported", errorStringMaxSize, true);
6578 *isRecoverable = false;
6579 }
6580 else if (error == WSAEPROTOTYPE) {
6581 utils::strcpyavail(errorString, "The specified protocol is the wrong type for this socket", errorStringMaxSize, true);
6582 *isRecoverable = false;
6583 }
6584 else if (error == WSAESOCKTNOSUPPORT) {
6585 utils::strcpyavail(errorString, "The specified socket type is not supported in this address family", errorStringMaxSize, true);
6586 *isRecoverable = false;
6587 }
6588 else if (error == WSAEADDRINUSE) {
6589 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);
6590 *isRecoverable = false;
6591 }
6592 else if (error == WSAEINVAL) {
6593 utils::strcpyavail(errorString, "The socket has not been bound with bind", errorStringMaxSize, true);
6594 *isRecoverable = false;
6595 }
6596 else if (error == WSAEISCONN) {
6597 utils::strcpyavail(errorString, "The socket is already connected", errorStringMaxSize, true);
6598 *isRecoverable = false;
6599 }
6600 else if (error == WSAENOTSOCK) {
6601 utils::strcpyavail(errorString, "The descriptor is not a socket", errorStringMaxSize, true);
6602 *isRecoverable = false;
6603 }
6604 else if (error == WSAEOPNOTSUPP) {
6605 utils::strcpyavail(errorString, "The referenced socket is not of a type that supports the listen operation", errorStringMaxSize, true);
6606 *isRecoverable = false;
6607 }
6608 else if (error == WSAEADDRNOTAVAIL) {
6609 utils::strcpyavail(errorString, "The specified address is not a valid address for this machine", errorStringMaxSize, true);
6610 *isRecoverable = false;
6611 }
6612 else if (error == WSAEFAULT) {
6613 utils::strcpyavail(errorString, "The name or namelen parameter is not a valid part of the user address space", errorStringMaxSize, true);
6614 *isRecoverable = false;
6615 }
6616 else if (error == WSAEMFILE) {
6617 utils::strcpyavail(errorString, "The queue is nonempty upon entry to accept and there are no descriptors available", errorStringMaxSize, true);
6618 *isRecoverable = false;
6619 }
6620 else if (error == WSAEWOULDBLOCK) {
6621 utils::strcpyavail(errorString, "The socket is marked as nonblocking and no connections are present to be accepted", errorStringMaxSize, true);
6622 *isRecoverable = false;
6623 }
6624 else if (error == WSAETIMEDOUT) {
6625 utils::strcpyavail(errorString, "Attempt to connect timed out without establishing a connection", errorStringMaxSize, true);
6626 *isRecoverable = false;
6627 }
6628 else if (error == WSAENETUNREACH) {
6629 utils::strcpyavail(errorString, "The network cannot be reached from this host at this time", errorStringMaxSize, true);
6630 *isRecoverable = false;
6631 }
6632 else if (error == WSAEISCONN) {
6633 utils::strcpyavail(errorString, "The socket is already connected (connection-oriented sockets only)", errorStringMaxSize, true);
6634 *isRecoverable = false;
6635 }
6636 else if (error == WSAECONNREFUSED) {
6637 utils::strcpyavail(errorString, "The attempt to connect was forcefully rejected", errorStringMaxSize, true);
6638 *isRecoverable = false;
6639 }
6640 else if (error == WSAEAFNOSUPPORT) {
6641 utils::strcpyavail(errorString, "Addresses in the specified family cannot be used with this socket", errorStringMaxSize, true);
6642 *isRecoverable = false;
6643 }
6644 else if (error == WSAEADDRNOTAVAIL) {
6645 utils::strcpyavail(errorString, "The remote address is not a valid address (such as ADDR_ANY)", errorStringMaxSize, true);
6646 *isRecoverable = false;
6647 }
6648 else if (error == WSAEALREADY) {
6649 utils::strcpyavail(errorString, "A nonblocking connect call is in progress on the specified socket", errorStringMaxSize, true);
6650 *isRecoverable = false;
6651 }
6652 else if (error == WSAECONNRESET) {
6653 utils::strcpyavail(errorString, "Connection was reset", errorStringMaxSize, true);
6654 *isRecoverable = false;
6655 }
6656 else if (error == WSAECONNABORTED) {
6657 utils::strcpyavail(errorString, "Software caused connection abort", errorStringMaxSize, true);
6658 *isRecoverable = false;
6659 }
6660 else {
6661 snprintf(errorString, errorStringMaxSize, "TCP error with no description: %d", error);
6662 *isRecoverable = false;
6663 }
6664
6665 return true;
6666 #else
6667 if (error == 100) {
6668 utils::strcpyavail(errorString, "Cannot initialise socket", errorStringMaxSize, true);
6669 *isRecoverable = false;
6670 }
6671 else {
6672 utils::strcpyavail(errorString, strerror(error), errorStringMaxSize, true);
6673 *isRecoverable = false;
6674 }
6675
6676 return true;
6677 #endif
6678}
6679
6680
6682 #ifdef WINDOWS
6683 int err = WSAGetLastError();
6684 WSASetLastError(0);
6685 return err;
6686 #else
6687 return errno;
6688 #endif
6689}
6690
6692 #ifdef WINDOWS
6693 LPVOID lpMsgBuf; //Message Buffer
6694 DWORD error = GetLastError();
6695 uint32 len = FormatMessage(
6696 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
6697 NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
6698 (LPTSTR) &lpMsgBuf, 0, NULL);
6699 std::string str = (char*)lpMsgBuf;
6700 LocalFree(lpMsgBuf);
6701 return str;
6702 #else
6703 return utils::StringFormat("Error number: %u", errno);
6704 #endif
6705}
6706
6707bool WaitForSocketWriteability(SOCKET s, int32 timeout) {
6708
6709 int maxfd = 0;
6710
6711 // If socket is not valid return true so the next read will report the error
6712 if (s == INVALID_SOCKET) return true;
6713
6714 struct timeval tv;
6715 tv.tv_sec = 0;
6716 tv.tv_usec = 0;
6717
6718 fd_set wds;
6719 // create a list of sockets to check for activity
6720 FD_ZERO(&wds);
6721 // specify mySocket
6722 FD_SET(s, &wds);
6723
6724 #ifdef WINDOWS
6725 #else
6726 maxfd = s + 1;
6727 #endif
6728
6729 if (timeout > 0) {
6730 ldiv_t d = ldiv(timeout*1000, 1000000);
6731 tv.tv_sec = d.quot;
6732 tv.tv_usec = d.rem;
6733 }
6734
6735 // Check for readability
6736 return( select(maxfd, NULL, &wds, NULL, &tv) > 0);
6737}
6738
6739bool WaitForSocketReadability(SOCKET s, int32 timeout) {
6740
6741 int maxfd = 0;
6742
6743 // If socket is not valid return true so the next read will report the error
6744 if (s == INVALID_SOCKET) return true;
6745
6746 struct timeval tv;
6747 tv.tv_sec = 0;
6748 tv.tv_usec = 0;
6749
6750 fd_set rdds;
6751 // create a list of sockets to check for activity
6752 FD_ZERO(&rdds);
6753 // specify mySocket
6754 FD_SET(s, &rdds);
6755
6756 #ifdef WINDOWS
6757 #else
6758 maxfd = s + 1;
6759 #endif
6760
6761 if (timeout > 0) {
6762 ldiv_t d = ldiv(timeout*1000, 1000000);
6763 tv.tv_sec = d.quot;
6764 tv.tv_usec = d.rem;
6765 }
6766
6767 // Check for readability
6768// uint64 t2 = GetTimeNow();
6769 int ret = select(maxfd, &rdds, NULL, NULL, &tv);
6770 //if (GetTimeAge(t2) > 1000) {
6771// LogPrint(0,LOG_NETWORK,0,"************** WaitForSocketReadability(%d) took %s ******************", s, PrintTimeDifString(GetTimeAge(t2)).c_str());
6772 //}
6773 return(ret > 0);
6774}
6775
6777 #if defined(WINDOWS)
6778 unsigned long parm = 1; // 1 = Non-blocking, 0 = Blocking
6779 ioctlsocket(s, FIONBIO, &parm);
6780 #else
6781 long parm = fcntl(s, F_GETFL);
6782 parm |= O_NONBLOCK;
6783 fcntl(s, F_SETFL, parm);
6784 #endif
6785 return true;
6786}
6787
6789 #if defined(WINDOWS)
6790 unsigned long parm = 0; // 1 = Non-blocking, 0 = Blocking
6791 ioctlsocket(s, FIONBIO, &parm);
6792 #else
6793 long parm = fcntl(s, F_GETFL);
6794 parm &= ~O_NONBLOCK;
6795 fcntl(s, F_SETFL, parm);
6796 #endif
6797 return true;
6798}
6799
6800bool LookupIPAddress(const char* name, uint32& address) {
6802 if ( (name == NULL) || (strlen(name) == 0) )
6803 return false;
6804
6805 // struct hostent* hent;
6806
6807 #ifdef WINDOWS
6808 //hent = gethostbyname(name);
6809 //if (hent == NULL)
6810 // return false;
6811 //else {
6812 // memcpy(&address, hent->h_addr_list[0], sizeof(address));
6813 // printf("IP1 of %s = %u.%u.%u.%u (%u)\n", name, GETIPADDRESSQUAD(address), address);
6814 // //return true;
6815 //}
6816
6817 //uint32 address2 = 0;
6818 struct addrinfo hints, *res;
6819 int error = -1;
6820 const char *cause = NULL;
6821
6822 memset(&hints, 0, sizeof(hints));
6823 //do not set to AI_NUMERICHOST, causes issues with connections...
6824 //hints.ai_flags = AI_NUMERICHOST; // AI_PASSIVE
6825 hints.ai_family = AF_INET;
6826 hints.ai_socktype = SOCK_STREAM;
6827 hints.ai_protocol = IPPROTO_TCP;
6828 __try {
6829 error = getaddrinfo(name, NULL, &hints, &res);
6830 }
6831 __except (EXCEPTION_CONTINUE_EXECUTION) {
6832 error = -1;
6833 }
6834
6835 if (!error) {
6836 memcpy(&address, res->ai_addr->sa_data + 2, sizeof(address));
6837 //printf("IP2 of %s = %u.%u.%u.%u (%u)\n", name, GETIPADDRESSQUAD(address), address);
6838 freeaddrinfo(res);
6839 return true;
6840 }
6841 else
6842 return false;
6843
6844
6845 #else
6846 struct addrinfo hints, *res;
6847 int error;
6848 const char *cause = NULL;
6849
6850 memset(&hints, 0, sizeof(hints));
6851 // hints.ai_flags = AI_NUMERICHOST; // AI_PASSIVE
6852 hints.ai_family = PF_UNSPEC;
6853 hints.ai_socktype = SOCK_STREAM;
6854 error = getaddrinfo(name, NULL, &hints, &res);
6855 if (!error) {
6856 memcpy(&address, res->ai_addr->sa_data+2, 4);
6857 freeaddrinfo(res);
6858 return true;
6859 }
6860 else
6861 return false;
6862 #endif // WINDOWS
6863}
6864
6865#ifdef WINDOWS
6866 bool IsNetworkInitialised = false;
6867 bool CheckNetworkInit() {
6868 if (IsNetworkInitialised) return true;
6869 WSADATA info;
6870 if (WSAStartup(MAKEWORD(1,1), &info) != 0) {
6871 LogPrint(0, LOG_NETWORK, 0, "Could not start Windows Networking...");
6872 return false;
6873 }
6874 IsNetworkInitialised = true;
6875 return true;
6876 }
6877#endif
6878
6879bool LookupHostname(uint32 address, char* name, uint32 maxSize) {
6881 struct hostent *pHost;
6882
6883 unsigned char* addrChars = (unsigned char*)&address;
6884 //char addrString[20];
6885 char* addrString = new char[20];
6886 snprintf(addrString, 20, "%u.%u.%u.%u", addrChars[0], addrChars[1], addrChars[2], addrChars[3]);
6887
6888 #ifdef WINDOWS
6889 pHost = gethostbyname(addrString);
6890 delete [] addrString;
6891 if (pHost == NULL) {
6892 return false;
6893 }
6894 if (strlen(pHost->h_name) > maxSize-1) {
6895 return false;
6896 }
6897 utils::strcpyavail(name, pHost->h_name, maxSize, true);
6898 return true;
6899 #else // WINDOWS
6900
6901 #ifdef Darwin
6902 pHost = gethostbyname(addrString);
6903 delete [] addrString;
6904 if (pHost == NULL)
6905 return false;
6906 else {
6907 if (strlen(pHost->h_name) > maxSize-1)
6908 return false;
6909 utils::strcpyavail(name, pHost->h_name, maxSize, true);
6910 return true;
6911 }
6912 #else // Darwin
6913
6914 pHost = new struct hostent;
6915 char bf[2000];
6916 int er;
6917 gethostbyname_r(addrString, pHost, bf, 2000, &pHost, &er);
6918 delete [] addrString;
6919 if (er != 0) {
6920 delete pHost;
6921 return false;
6922 }
6923 else {
6924 if (strlen(pHost->h_name) > maxSize-1)
6925 return false;
6926 utils::strcpyavail(name, pHost->h_name, maxSize, true);
6927 delete pHost;
6928 return true;
6929 }
6930
6931 #endif // Darwin
6932 #endif // WINDOWS
6933}
6934
6936 char* name = new char[256];
6937 if (!GetLocalHostname(name, 255)) {
6938 delete [] name;
6939 return "";
6940 }
6941
6942 std::string sname = name;
6943 delete [] name;
6944 return sname;
6945}
6946
6947bool GetLocalHostname(char* name, uint32 maxSize) {
6949
6950 if (gethostname(name, maxSize) == 0)
6951 return true;
6952 else
6953 return false;
6954
6955 //JString ipaddress = GetLocalIPAddress();
6956 //JString hostname = ipaddress;
6957 //struct hostent* hent;
6958 //if ( (ipaddress.equals("127.0.0.1")) || (ipaddress.equals("localhost")) ) {
6959 // localhostName = "127.0.0.1";
6960 // return localhostName;
6961 //}
6962
6963 //#ifdef WINDOWS
6964
6965 // hent = gethostbyname((char*) ipaddress);
6966 // if (hent != NULL)
6967 // localhostName = JString(hent->h_name);
6968 // else {
6969 // int err = WSAGetLastError();
6970 // if (DEBUGLEVEL(KITCHENSINK)) {
6971 // printf("getLocalHostname(): gethostbyaddr error %d...\n", err);
6972 // }
6973 // localhostName = ipaddress;
6974 // return ipaddress;
6975 // }
6976
6977 //#else // WINDOWS
6978
6979 // struct addrinfo hints, *res;
6980 // int error;
6981 // const char *cause = NULL;
6982
6983 // memset(&hints, 0, sizeof(hints));
6984 // hints.ai_flags = AI_NUMERICHOST;
6985 // hints.ai_family = PF_UNSPEC;
6986 // hints.ai_socktype = SOCK_STREAM;
6987 // error = getaddrinfo(ipaddress, NULL, &hints, &res);
6988 // if (error != 0) {
6989 // localhostName = ipaddress;
6990 // return ipaddress;
6991 // }
6992 // else {
6993 // if (res->ai_canonname != NULL) {
6994 // localhostName = res->ai_canonname;
6995 // }
6996 // else
6997 // localhostName = ipaddress;
6998 // }
6999 // freeaddrinfo(res);
7000
7001 //#endif // WINDOWS
7002
7003 //return localhostName;
7004
7005}
7006
7007bool GetLocalMACAddress(uint64& address) {
7008 address = 0;
7009 uint32 c;
7010 NetworkInterfaces* interfaces = GetLocalInterfaces(c);
7011 for (uint32 i=0; i<c; i++) {
7012 if ( (interfaces[i].address != 0) && (interfaces[i].address != LOCALHOSTIP) )
7013 address = interfaces[i].mac;
7014 }
7015 delete [] interfaces;
7016 return (address != 0);
7017}
7018
7019uint64* GetLocalMACAddresses(uint32& count) {
7020 uint32 c;
7021 NetworkInterfaces* interfaces = GetLocalInterfaces(c);
7022 if (!c)
7023 return NULL;
7024 uint64* addresses = new uint64[c];
7025 uint32 p=0;
7026 for (uint32 i=0; i<c; i++) {
7027 if ( (interfaces[i].address != 0) && (interfaces[i].address != LOCALHOSTIP) )
7028 addresses[p++] = interfaces[i].mac;
7029 }
7030 count = p;
7031 return addresses;
7032}
7033
7034bool IsLocalIPAddress(const char* addr) {
7035 uint32 address;
7036 if (!LookupIPAddress(addr, address))
7037 return false;
7038 return IsLocalIPAddress(address);
7039}
7040
7041bool IsLocalIPAddress(uint32& address) {
7043 uint32 count;
7044 uint32* addresses = GetLocalIPAddresses(count);
7045 if ((addresses == NULL) || (count == 0)) {
7046 delete [] addresses;
7047 return false;
7048 }
7049
7050 unsigned int n;
7051 for (n=0; n<count; n++) {
7052 if (addresses[n] == address) {
7053 delete [] addresses;
7054 return true;
7055 }
7056 }
7057 delete [] addresses;
7058 return false;
7059}
7060
7061bool GetLocalIPAddress(uint32& address) {
7063 uint32 count;
7064 uint32* addresses = GetLocalIPAddresses(count);
7065 if ((addresses == NULL) || (count == 0)) {
7066 delete [] addresses;
7067 return false;
7068 }
7069 // First find first non-localhost address
7070 unsigned int n;
7071 bool localhostPresent = false;
7072 for (n=0; n<count; n++) {
7073 if (addresses[n] != LOCALHOSTIP) {
7074 if (addresses[n] != 0) {
7075 address = addresses[n];
7076 delete [] addresses;
7077 return true;
7078 }
7079 }
7080 else
7081 localhostPresent = true;
7082 }
7083 delete [] addresses;
7084 // OK, then accept localhost, if present
7085 if (localhostPresent) {
7086 address = LOCALHOSTIP;
7087 return true;
7088 }
7089 else
7090 return false;
7091}
7092
7093uint32* GetLocalIPAddresses(uint32& count) {
7095
7096 uint32 maxSize = 128;
7097 uint32* addresses = new uint32[maxSize];
7098
7099 addresses[0] = LOCALHOSTIP;
7100 count = 1;
7101
7102 char* szHostName = new char[255];
7103
7104 bool canAskHost = true;
7105 if( gethostname(szHostName, 255) != 0 ) {
7106 #ifdef WINDOWS
7107 int err = GetLastOSErrorNumber();
7108 if (err == WSANOTINITIALISED) {
7109 WSADATA info;
7110 if (WSAStartup(MAKEWORD(1,1), &info) != 0) {
7111 delete [] szHostName;
7112 return addresses;
7113 }
7114 else {
7115 if( gethostname(szHostName, 255) != 0 )
7116 canAskHost = false;
7117 }
7118 }
7119 else
7120 canAskHost = false;
7121 #else // WINDOWS
7122 canAskHost = false;
7123 #endif // WINDOWS
7124 }
7125
7126 struct hostent * pHost;
7127 uint32 addr, j, i;
7128 bool exists;
7129 #if !defined(WINDOWS) && !defined(Darwin)
7130 // gethostbyname_r() writes its result INTO this caller-supplied buffer,
7131 // and the returned hostent's h_addr_list points INTO that buffer - so it
7132 // must outlive every read of pHost below. Freeing it before the
7133 // consumption loop was a real heap-use-after-free (ASan: 30/30 runs of
7134 // network_http reported it at the h_addr_list read). Freed after the loop.
7135 char* hostBuf = NULL;
7136 struct hostent* hostAlloc = NULL;
7137 #endif
7138
7139 if (canAskHost) {
7140 // Get host addresses
7141
7142 #ifdef WINDOWS
7143 pHost = gethostbyname(szHostName);
7144 #else // WINDOWS
7145
7146 #ifdef Darwin
7147 pHost = gethostbyname(szHostName);
7148 //if (pHost == NULL)
7149 // return addresses;
7150 #else // Darwin
7151
7152 hostAlloc = new struct hostent;
7153 hostBuf = new char[2000];
7154 int er = 0;
7155 struct hostent* hres = NULL;
7156 int res = gethostbyname_r(szHostName, hostAlloc, hostBuf, 2000, &hres, &er);
7157 // LogPrint(0, LOG_NETWORK, 0, "gethostbyname_r '%s' [%d] [%d]...\n", szHostName, res, er);
7158 // Success = res==0 AND non-NULL result. Keep the result in its own
7159 // pointer: glibc sets *result = NULL on failure, and the old code
7160 // passed &pHost, so that NULL overwrote our allocation - leaking the
7161 // hostent and leaving the trailing "delete pHost" deleting NULL.
7162 // Do NOT free hostBuf here: h_addr_list points into it.
7163 if ((res != 0) || (hres == NULL))
7164 pHost = NULL;
7165 else
7166 pHost = hres;
7167 #endif // Darwin
7168 #endif // WINDOWS
7169
7170 for( i = 0; pHost!= NULL && pHost->h_addr_list[i]!= NULL; i++ )
7171 {
7172 exists = false;
7173 memcpy(&addr, ((unsigned char*)pHost->h_addr_list[i]), sizeof(uint32));
7174 if ( (addr != 0) && (addr != LOCALHOSTIP) ) {
7175 for (j=0; j<count; j++) {
7176 if (addresses[j] == addr) {
7177 exists = true;
7178 break;
7179 }
7180 }
7181 if (!exists)
7182 addresses[count++] = addr;
7183 }
7184 }
7185
7186 #ifdef WINDOWS
7187 #else // WINDOWS
7188 #ifdef Darwin
7189 endhostent();
7190 #else
7191 // Safe now: the h_addr_list reads above are done.
7192 delete hostAlloc;
7193 delete [] hostBuf;
7194 #endif // Darwin
7195 #endif // WINDOWS
7196 }
7197
7198 uint32 c;
7199 NetworkInterfaces* interfaces = GetLocalInterfaces(c);
7200 for (i=0; i<c; i++) {
7201 exists = false;
7202 if ( (interfaces[i].address != 0) && (interfaces[i].address != LOCALHOSTIP) ) {
7203 for (j=0; j<count; j++) {
7204 if (addresses[j] == interfaces[i].address) {
7205 exists = true;
7206 break;
7207 }
7208 }
7209 if (!exists)
7210 addresses[count++] = interfaces[i].address;
7211 }
7212 }
7213 delete [] interfaces;
7214 delete [] szHostName;
7215 return addresses;
7216}
7217
7220
7221 uint32 maxSize = 128;
7222 uint32 addr;
7223 NetworkInterfaces* interfaces = new NetworkInterfaces[maxSize];
7224 count = 0;
7225
7226 #ifdef WINDOWS
7227
7228 DWORD dwSize = 0;
7229 DWORD dwRetVal = 0;
7230
7231 unsigned int i = 0;
7232
7233 // Set the flags to pass to GetAdaptersAddresses
7234 ULONG flags = GAA_FLAG_INCLUDE_PREFIX;
7235
7236 // default to unspecified address family (both)
7237 ULONG family = AF_INET;
7238
7239 LPVOID lpMsgBuf = NULL;
7240
7241 PIP_ADAPTER_ADDRESSES pAddresses = NULL;
7242 ULONG outBufLen = 0;
7243 ULONG Iterations = 0;
7244
7245 PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
7246 PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
7247 PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL;
7248 PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL;
7249 IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = NULL;
7250 IP_ADAPTER_PREFIX *pPrefix = NULL;
7251
7252 // Allocate a 15 KB buffer to start with.
7253 outBufLen = 15000;
7254
7255 do {
7256
7257 pAddresses = (IP_ADAPTER_ADDRESSES *) MALLOC(outBufLen);
7258 if (pAddresses == NULL)
7259 return interfaces;
7260
7261 dwRetVal =
7262 GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen);
7263
7264 if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
7265 FREE(pAddresses);
7266 pAddresses = NULL;
7267 }
7268 else
7269 break;
7270
7271 Iterations++;
7272
7273 } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < 10));
7274
7275
7276 if (dwRetVal == NO_ERROR) {
7277 // If successful, output some information from the data we received
7278 pCurrAddresses = pAddresses;
7279 while (pCurrAddresses) {
7280 if ( (pCurrAddresses->OperStatus == IfOperStatusUp) && (pCurrAddresses->IfIndex != 0)) {
7281
7282 // First check the IP address
7283 pUnicast = pCurrAddresses->FirstUnicastAddress;
7284 if (pUnicast != NULL) {
7285 for (i = 0; pUnicast != NULL; i++) {
7286 addr = *(uint32*)(((unsigned char*) ((SOCKADDR*)(pUnicast->Address.lpSockaddr))->sa_data) + 2);
7287 if ( (addr != 0) && (addr != LOCALHOSTIP) ) {
7288 interfaces[count].address = addr;
7289 interfaces[count].mac = 0;
7290 if (pCurrAddresses->PhysicalAddressLength == 6)
7291 memcpy(&(interfaces[count].mac), &(pCurrAddresses->PhysicalAddress), 6);
7292 WideCharToMultiByte( CP_ACP, 0, pCurrAddresses->Description, -1, interfaces[count].name, MAXKEYNAMELEN, NULL, NULL );
7293 WideCharToMultiByte( CP_ACP, 0, pCurrAddresses->FriendlyName, -1, interfaces[count].friendlyName, MAXKEYNAMELEN, NULL, NULL );
7294 count++;
7295 }
7296 pUnicast = pUnicast->Next;
7297 }
7298 }
7299 }
7300
7301 pCurrAddresses = pCurrAddresses->Next;
7302 }
7303 }
7304 if (pAddresses)
7305 FREE(pAddresses);
7306
7307 return interfaces;
7308
7309 #else
7310 int sock = 0;
7311 struct ifreq ifreq;
7312 struct sockaddr_in *saptr = NULL;
7313 struct if_nameindex *iflist = NULL, *listsave = NULL;
7314
7315 //need a socket for ioctl()
7316 if( (sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
7317 return interfaces;
7318 }
7319
7320 //returns pointer to dynamically allocated list of structs
7321 iflist = listsave = if_nameindex();
7322
7323 if (iflist == NULL) {
7324 close(sock);
7325 return interfaces;
7326 }
7327
7328 //walk thru the array returned and query for each
7329 //interface's address
7330 //for(iflist; iflist->if_index != 0; iflist++) {
7331 for(; iflist->if_index != 0; iflist++) {
7332 //copy in the interface name to look up address of
7333 strncpy(ifreq.ifr_name, iflist->if_name, IF_NAMESIZE);
7334 //get the address for this interface
7335 if(ioctl(sock, SIOCGIFADDR, &ifreq) != 0) {
7336 // ignore;
7337 continue;
7338 }
7339 //print out the address
7340 saptr = (struct sockaddr_in *)&ifreq.ifr_addr;
7341 //unsigned char* ip = (unsigned char*)&(saptr->sin_addr.s_addr);
7342 //printf("**** %u.%u.%u.%u ***\n", ip[0], ip[1], ip[2], ip[3]);
7343 memcpy(&(interfaces[count].address), &(saptr->sin_addr.s_addr), sizeof(uint32));
7344 utils::strcpyavail(interfaces[count].name, ifreq.ifr_name, MAXKEYNAMELEN, true);
7345 #ifdef __APPLE__
7346 interfaces[count].mac = 0;
7347 #else
7348 // get the MAC address (Linux only)
7349 interfaces[count].mac = 0;
7350 ioctl(sock, SIOCGIFHWADDR, &ifreq);
7351 memcpy(&(interfaces[count].mac), &(ifreq.ifr_hwaddr.sa_data), 6);
7352 #endif
7353 count++;
7354 }
7355 //free the dynamic memory kernel allocated for us
7356 if_freenameindex(listsave);
7357 close(sock);
7358
7359 #endif
7360
7361 return interfaces;
7362}
7363
7364bool GetNextAvailableLocalPort(uint16 lastPort, uint16 &nextPort) {
7366
7367 SOCKET socket;
7368 struct sockaddr_in addr;
7369 addr.sin_family= AF_INET;
7370 addr.sin_addr.s_addr=INADDR_ANY;
7371 bool result = false;
7372
7373 nextPort = lastPort + 1;
7374 while (nextPort < lastPort + 1000) {
7375 if((socket=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET)
7376 return false;
7377
7378 #ifdef WINDOWS
7379 // Set the exclusive address option, preventing other software binding to
7380 // non-INADDR_ANY (i.e. interface addresses such as localhost directly)
7381 int one = 1;
7382 setsockopt(socket, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *) &one, sizeof(one));
7383 #else
7384 /*
7385 This socket option tells the kernel that even if this port is busy (in
7386 the TIME_WAIT state), go ahead and reuse it anyway. If it is busy,
7387 but with another state, you will still get an address already in use
7388 error. It is useful if your server has been shut down, and then
7389 restarted right away while sockets are still active on its port. You
7390 should be aware that if any unexpected data comes in, it may confuse
7391 your server, but while this is possible, it is not likely.
7392 */
7393 int one = 1;
7394 setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&one,sizeof(one));
7395 #endif
7396
7397 addr.sin_port=htons(nextPort);
7398
7399 result = (bind(socket,(SOCKADDR*)&addr,sizeof(struct sockaddr_in))!=SOCKET_ERROR);
7400 shutdown(socket, SD_BOTH);
7401 closesocket(socket);
7402
7403 if (result)
7404 return true;
7405
7406 nextPort++;
7407 }
7408 return false;
7409
7410}
7411
7413 printf("Testing Utils...\n\n");
7414
7415 char hostname[1024];
7416 if (!GetLocalHostname(hostname, 1023)) {
7417 printf("Error GetLocalHostname\n");
7418 return false;
7419 }
7420 printf("Local Hostname: '%s'\n\n", hostname);
7421
7422 uint32 addr;
7423 unsigned char* addrChars = (unsigned char*) &addr;
7424 if (!GetLocalIPAddress(addr)) {
7425 printf("Error GetLocalIPAddress\n");
7426 return false;
7427 }
7428 printf("Local IP Address: %u.%u.%u.%u\n\n", addrChars[0], addrChars[1], addrChars[2], addrChars[3]);
7429
7430 uint32 count;
7431 uint32* addresses = GetLocalIPAddresses(count);
7432 if (addresses == NULL) {
7433 printf("Error GetLocalIPAddresses\n");
7434 return false;
7435 }
7436 uint32 n;
7437 for (n=0; n<count; n++) {
7438 addrChars = (unsigned char*) &addresses[n];
7439 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);
7440 }
7441 delete [] addresses;
7442
7443 NetworkInterfaces* interfaces = GetLocalInterfaces(count);
7444 for (n=0; n<count; n++) {
7445 addrChars = (unsigned char*) &(interfaces[n]);
7446 printf("Local Interface[%u] %s: %u.%u.%u.%u (%llX)\n\n", n, interfaces[n].name,
7447 addrChars[0], addrChars[1], addrChars[2], addrChars[3], interfaces[n].mac);
7448 }
7449 delete [] interfaces;
7450
7451 return true;
7452}
7453
7454void PrintBinary(void* p, uint32 size, bool asInt, const char* title) {
7455 if (title != NULL)
7456 printf("--- %s %u ---\n", title, size);
7457 unsigned char c;
7458 for (uint32 n=0; n<size; n++) {
7459 c = *(((unsigned char*)p)+n);
7460 if (asInt)
7461 printf("[%u] ", (unsigned int)c);
7462 else
7463 printf("[%c] ", c);
7464 if ( (n > 0) && ((n+1)%10 == 0) )
7465 printf("\n");
7466 }
7467 printf("\n");
7468}
7469
7470// const char* stristr(const char *str, const char *substr, uint32 len) {
7471// if (!str || !substr) return NULL;
7472// const char *a, *b;
7473// uint32 n=0, m, sslen = (uint32)strlen(substr);
7474// for(;*str;*str++,n++) {
7475// a = str;
7476// b = substr;
7477// m = n+sslen;
7478// while((*a++ | 32) == (*b++ | 32)) {
7479// if(!*b)
7480// return str;
7481// if (len && (++m > len))
7482// return NULL;
7483// }
7484// }
7485// return NULL;
7486// }
7487
7488
7489const char* stristr(const char *str, const char *substr, uint32 len) {
7490 if (!str || !substr) return NULL;
7491 uint32 sslen = (uint32)strlen(substr);
7492 if (sslen == 0) return (const char*)str;
7493 const char *a, *b;
7494 uint32 n=0, m;
7495 for (; *str; str++, n++) {
7496 a = str;
7497 b = substr;
7498 m = n+sslen;
7499 /* Case-insensitive compare via |32 (ASCII A–Z only). */
7500 while ((*a++ | 32) == (*b++ | 32)) {
7501 if (!*b)
7502 return str;
7503 if (len && (++m > len))
7504 return NULL;
7505 }
7506 }
7507 return NULL;
7508}
7509
7510uint32 strcpyavail(char* dst, const char* src, uint32 maxlen, bool copyAvailable) {
7511 if ( !dst || ! src || !maxlen )
7512 return 0;
7513 uint32 len = (uint32)strlen(src);
7514 if (len > maxlen-1) {
7515 if (!copyAvailable)
7516 return 0;
7517 else {
7518 memcpy(dst, src, maxlen-1);
7519 dst[maxlen] = 0;
7520 return maxlen-1;
7521 }
7522 }
7523 else
7524 memcpy(dst, src, len+1);
7525 return len;
7526}
7527
7528bool GetNextLineEnd(const char *str, uint32 size, uint32& len, uint32& crSize) {
7529 if (!str || !size)
7530 return false;
7531
7532 for (len = 0; len < size; len++) {
7533 // Checking for CR and CRLF
7534 if (str[len] == 13) {
7535 crSize = ( (len < size-1) && (str[len+1] == 10 ) ) ? 2 : 1;
7536 return true;
7537 }
7538 // Checking for LF only without CR
7539 if (str[len] == 10) {
7540 crSize = 1;
7541 return true;
7542 }
7543 }
7544 return false;
7545}
7546
7547std::string TextCapitalise(const char* text) {
7548 if (!text)
7549 return "";
7550 std::string str = text;
7551 if (!str.length())
7552 return "";
7553 std::string::iterator i = str.begin(), e = str.end();
7554 *i = ::toupper(*i);
7555 i++;
7556 if (i != e)
7557 std::transform(i, e, i, ::tolower);
7558 return str;
7559}
7560
7561std::string TextUppercase(const char* text) {
7562 std::string str = text;
7563 std::transform(str.begin(), str.end(), str.begin(), ::toupper);
7564 return str;
7565}
7566
7567std::string TextLowercase(const char* text) {
7568 std::string str = text;
7569 std::transform(str.begin(), str.end(), str.begin(), ::tolower);
7570 return str;
7571}
7572
7573std::string TextIndent(const char* text, const char* indent) {
7574 std::vector<std::string> lines = utils::TextListSplitLines(text, true, false);
7575 std::vector<std::string>::iterator i = lines.begin(), e = lines.end();
7576 std::string output;
7577 while (i != e) {
7578 output += utils::StringFormat("%s%s\n", indent, (*i).c_str());
7579 i++;
7580 }
7581 return output;
7582}
7583
7584std::string TextUnindent(const char* text) {
7585 uint32 len = (uint32)strlen(text);
7586 if (!len) return "";
7587 if (text[0] > 32)
7588 return text;
7589
7590 // Find initial spacing char sequence
7591 uint32 s = 0, p = 0;
7592 // Find initial line endings
7593 while ( ((text[s] == '\n') || (text[s] == '\r')) && (s < len))
7594 s++;
7595 p = s;
7596 while ((text[p] <= 32) && (p < len))
7597 p++;
7598 std::string indentString = std::string(text+s, p-s);
7599 std::string str = text;
7600 StringSingleReplace(str, indentString, "", false);
7601 return str;
7602}
7603
7604
7605std::string TextTrimQuotes(const char* text) {
7606 if (text) {
7607 uint32 begin = 0, end = (uint32)strlen(text);
7608 if (begin == end)
7609 return "";
7610 while ((begin < end) && (text[begin] <= 32) || (text[begin] == '\"') || (text[begin] == '\''))
7611 begin++;
7612 if (begin == end)
7613 return "";
7614 while ((end > begin) && (text[end-1] <= 32) || (text[end-1] == '\"') || (text[end-1] == '\''))
7615 end--;
7616 return std::string(text+begin, end-begin);
7617 }
7618 else
7619 return "";
7620}
7621
7622
7623std::string TextTrim(const char* text) {
7624 if (text) {
7625 uint32 begin = 0, end = (uint32)strlen(text);
7626 if (begin == end)
7627 return "";
7628 while ((text[begin] <= 32) && (begin < end))
7629 begin++;
7630 while ((end > begin) && (text[end-1] <= 32))
7631 end--;
7632 return std::string(text+begin, end-begin);
7633 }
7634 else
7635 return "";
7636}
7637
7638std::multimap<std::string, std::string> TextMultiMapSplit(const char* text, const char* outersplit, const char* innersplit) {
7639 std::multimap<std::string, std::string> result;
7640 typedef std::pair <std::string, std::string> Map_String_Pair;
7641
7642 if (!text || !outersplit || !innersplit)
7643 return result;
7644
7645 const char* pstr = text, *lstr = text;
7646 const char* src = text;
7647
7648 while (pstr = strstr(src, outersplit)) {
7649 if ((lstr = strstr(src, innersplit)) && (lstr < pstr)) {
7650 // result[TextTrim(std::string(src, lstr-src).c_str())] = TextTrim(std::string(lstr+1, pstr-lstr-1).c_str());
7651 result.insert(Map_String_Pair(TextTrim(std::string(src, lstr-src).c_str()), TextTrim(std::string(lstr+1, pstr-lstr-1).c_str())));
7652 }
7653 src = pstr + 1;
7654 }
7655 pstr = src + strlen(src);
7656 if ((lstr = strstr(src, innersplit)) && (lstr < pstr)) {
7657 // result[TextTrim(std::string(src, lstr-src).c_str())] = TextTrim(std::string(lstr+1, pstr-lstr-1).c_str());
7658 result.insert(Map_String_Pair(TextTrim(std::string(src, lstr-src).c_str()), TextTrim(std::string(lstr+1, pstr-lstr-1).c_str())));
7659 }
7660
7661 return result;
7662}
7663
7664
7665std::map<std::string, std::string> TextMapSplit(const char* text, const char* outersplit, const char* innersplit) {
7666 std::map<std::string, std::string> result;
7667 typedef std::pair <std::string, std::string> Map_String_Pair;
7668
7669 if (!text || !outersplit || !innersplit)
7670 return result;
7671
7672 const char* pstr = text, *lstr = text;
7673 const char* src = text;
7674
7675 while (pstr = strstr(src, outersplit)) {
7676 if ((lstr = strstr(src, innersplit)) && (lstr < pstr)) {
7677 // result[TextTrim(std::string(src, lstr-src).c_str())] = TextTrim(std::string(lstr+1, pstr-lstr-1).c_str());
7678 result.insert(Map_String_Pair(TextTrim(std::string(src, lstr - src).c_str()), TextTrim(std::string(lstr + 1, pstr - lstr - 1).c_str())));
7679 }
7680 src = pstr + 1;
7681 }
7682 pstr = src + strlen(src);
7683 if ((lstr = strstr(src, innersplit)) && (lstr < pstr)) {
7684 // result[TextTrim(std::string(src, lstr-src).c_str())] = TextTrim(std::string(lstr+1, pstr-lstr-1).c_str());
7685 result.insert(Map_String_Pair(TextTrim(std::string(src, lstr - src).c_str()), TextTrim(std::string(lstr + 1, pstr - lstr - 1).c_str())));
7686 }
7687
7688 return result;
7689}
7690
7691char** SplitCommandline(const char* cmdline, int& argc) {
7692 std::vector<std::string> args = TextCommandlineSplit(cmdline);
7693 uint32 size = (uint32)args.size();
7694 if (!size)
7695 return NULL;
7696
7697 char** argv = new char*[size+1];
7698 uint32 c = 0;
7699 std::vector<std::string>::iterator i = args.begin(), e = args.end();
7700 while (i != e) {
7701 argv[c] = new char[(*i).length()+1];
7702 strcpy(argv[c], (*i).c_str());
7703 c++;
7704 i++;
7705 }
7706 argv[c] = NULL;
7707 argc = (int)c;
7708 return argv;
7709}
7710
7711wchar_t** SplitCommandlineW(const char* cmdline, int& argc) {
7712 std::vector<std::string> args = TextCommandlineSplit(cmdline);
7713 uint32 size = (uint32)args.size();
7714 if (!size)
7715 return NULL;
7716
7717 wchar_t** argv = new wchar_t*[size+1];
7718 uint32 c = 0;
7719 std::vector<std::string>::iterator i = args.begin(), e = args.end();
7720 while (i != e) {
7721 argv[c] = new wchar_t[(*i).length()+1];
7722 mbstowcs(argv[c], (*i).c_str(), (*i).length() + 1);
7723 c++;
7724 i++;
7725 }
7726 argv[c] = NULL;
7727 argc = (int)c;
7728 return argv;
7729}
7730
7731bool DeleteCommandline(char** argv, int argc) {
7732 for (uint32 n=0; n<(uint32)argc; n++)
7733 delete [] argv[n];
7734 delete [] argv;
7735 return true;
7736}
7737
7738std::vector<std::string> TextCommandlineSplit(const char* cmdline) {
7739
7740 bool insideQuotes = false;
7741 bool lastWasWhiteSpace = false;
7742 std::vector<std::string> vect;
7743 std::string arg, val, str;
7744 unsigned char ch;
7745
7746 uint32 len = (uint32)strlen(cmdline);
7747
7748 bool keepQuotes = false;
7749
7750 for (uint32 n=0; n<len; n++) {
7751 ch = cmdline[n];
7752 if (ch <= 32) {
7753 if (!insideQuotes) {
7754 if (!lastWasWhiteSpace) {
7755 // An arg is ended, parse it...
7756 vect.push_back(str);
7757 str = "";
7758 }
7759 else {
7760 // Ignore this whitespace then...
7761 }
7762 lastWasWhiteSpace = true;
7763 }
7764 else {
7765 str += ch;
7766 }
7767 }
7768 else if (ch == '"') {
7769 insideQuotes = !insideQuotes;
7770 if (keepQuotes)
7771 str += ch;
7772 lastWasWhiteSpace = false;
7773 }
7774 else {
7775 str += ch;
7776 lastWasWhiteSpace = false;
7777 }
7778 }
7779
7780 if (str.length() > 0) {
7781 vect.push_back(str);
7782 str = "";
7783 }
7784
7785 return vect;
7786}
7787
7788std::vector<std::string> TextListSplitLines(const char* text, bool keepEmpty, bool autoTrim) {
7789 if (!text || !strlen(text))
7790 return std::vector<std::string>();
7791
7792 if (strstr(text, "\r\n"))
7793 return TextListSplit(text, "\r\n", keepEmpty, autoTrim);
7794 else if (strstr(text, "\n\r"))
7795 return TextListSplit(text, "\n\r", keepEmpty, autoTrim);
7796 else if (strstr(text, "\n"))
7797 return TextListSplit(text, "\n", keepEmpty, autoTrim);
7798 else if (strstr(text, "\r"))
7799 return TextListSplit(text, "\n", keepEmpty, autoTrim);
7800
7801 std::vector<std::string> result;
7802 result.push_back(text);
7803 return result;
7804}
7805
7806std::vector<std::string> TextListBreakLines(const char* text, uint32 maxLineLength) {
7807 if (!text || !strlen(text))
7808 return std::vector<std::string>();
7809
7810 std::string str;
7811 std::vector<std::string> result;
7812 uint32 s = 0, n = 0, len = (uint32)strlen(text);
7813 int p = -1;
7814 while (n < len) {
7815 if (text[n] == 13) {
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] == 10))
7823 s = n + 2;
7824 else
7825 s = n + 1;
7826 p = -1;
7827 }
7828 else if (text[n] == 10) {
7829 if (n == s)
7830 result.push_back("");
7831 else {
7832 str = std::string(text + s, n - s);
7833 result.push_back(TextTrim(str.c_str()));
7834 }
7835 if ((n < len - 1) && (text[n + 1] == 13))
7836 s = n + 2;
7837 else
7838 s = n + 1;
7839 p = -1;
7840 }
7841 else if ((n-s < maxLineLength) && (text[n] == 32)) {
7842 p = n - s;
7843 }
7844 else if (n - s >= maxLineLength) {
7845 if (p == 0) {
7846 result.push_back("");
7847 s += p + 1;
7848 p = -1;
7849 n = s - 1;
7850 }
7851 else if (p > 0) {
7852 str = std::string(text + s, p);
7853 result.push_back(TextTrim(str.c_str()));
7854 s += p + 1;
7855 p = -1;
7856 n = s - 1;
7857 }
7858 else {
7859 str = std::string(text + s, maxLineLength);
7860 result.push_back(TextTrim(str.c_str()));
7861 s += maxLineLength;
7862 p = -1;
7863 n = s;
7864 }
7865 }
7866 n++;
7867 }
7868 if (s < n) {
7869 str = std::string(text + s, n - s);
7870 result.push_back(TextTrim(str.c_str()));
7871 }
7872
7873 return result;
7874}
7875
7876std::string TextListBreakLines(const char* text, uint32 maxLineLength, const char* breakstr) {
7877 std::vector<std::string> lines = TextListBreakLines(text, maxLineLength);
7878 if (!breakstr)
7879 return TextJoin(lines, "\n");
7880 else
7881 return TextJoin(lines, breakstr);
7882}
7883
7884std::string TextJoin(std::vector<std::string> &list, const char* split, uint32 start, uint32 count) {
7885 std::ostringstream o;
7886 std::vector<std::string>::iterator i = list.begin(), e = list.end();
7887 uint32 l = 0, c = 0;
7888 while (i != e) {
7889 if (!count || ((l >= start) && (c < count))) {
7890 if (c++) o << split;
7891 o << *i;
7892 }
7893 l++;
7894 i++;
7895 }
7896 return o.str();
7897}
7898
7899std::string TextJoin(std::list<std::string> &list, const char* split, uint32 start, uint32 count) {
7900 std::ostringstream o;
7901 std::list<std::string>::iterator i = list.begin(), e = list.end();
7902 uint32 l = 0, c = 0;
7903 while (i != e) {
7904 if (!count || ((l >= start) && (c < count))) {
7905 if (c++) o << split;
7906 o << *i;
7907 }
7908 l++;
7909 i++;
7910 }
7911 return o.str();
7912}
7913
7914std::string TextJoin(std::map<std::string, std::string> &map, const char* innersplit, const char* outersplit, bool keyquotes, bool valquotes) {
7915 std::ostringstream o;
7916 std::map<std::string, std::string>::iterator i = map.begin(), e = map.end();
7917 uint32 c = 0;
7918 while (i != e) {
7919 if (c++) o << outersplit;
7920 if (keyquotes && valquotes)
7921 o << "\"" << i->first << "\"" << innersplit << "\"" << i->second << "\"";
7922 else if (valquotes)
7923 o << i->first << innersplit << "\"" << i->second << "\"";
7924 else
7925 o << i->first << innersplit << i->second;
7926 i++;
7927 }
7928 return o.str();
7929}
7930
7931std::string TextJoinJSON(std::map<std::string, std::string> &map) {
7932 std::ostringstream o;
7933 std::map<std::string, std::string>::iterator i = map.begin(), e = map.end();
7934 uint32 c = 0;
7935 o << "{ ";
7936 while (i != e) {
7937 if (c++)
7938 o << ", \"" << utils::EncodeJSON(i->first) << "\": \"" << utils::EncodeJSON(i->second) << "\"";
7939 else
7940 o << " \"" << utils::EncodeJSON(i->first) << "\": \"" << utils::EncodeJSON(i->second) << "\"";
7941 i++;
7942 }
7943 o << " }";
7944 return o.str();
7945}
7946
7947std::string TextJoinXML(std::map<std::string, std::string> &map, const char* innernodeName, const char* outernodeName) {
7948 if (!innernodeName || !strlen(innernodeName))
7949 return "";
7950 std::ostringstream o;
7951 std::map<std::string, std::string>::iterator i = map.begin(), e = map.end();
7952 if (outernodeName && strlen(outernodeName))
7953 o << "<" << outernodeName << ">\n";
7954 while (i != e) {
7955 o << "<" << innernodeName << " " << html::EncodeHTML(i->first) << "=\"" << html::EncodeHTML(i->second) << "\" />\n";
7956 i++;
7957 }
7958 if (outernodeName && strlen(outernodeName))
7959 o << "</" << outernodeName << ">\n";
7960 return o.str();
7961}
7962
7963
7964
7965std::vector<std::string> TextListSplit(const char* text, const char* split, bool keepEmpty, bool autoTrim) {
7966 std::vector<std::string> result;
7967 if (!text || !split)
7968 return result;
7969
7970 uint32 splitLen = (uint32)strlen(split);
7971 const char* pstr = text;
7972 const char* src = text;
7973
7974 while (pstr = strstr(src, split)) {
7975 if (pstr - src || keepEmpty) {
7976 if (autoTrim)
7977 result.push_back(TextTrim(std::string(src, pstr - src).c_str()));
7978 else
7979 result.push_back(std::string(src, pstr - src).c_str());
7980 }
7981 src = pstr + splitLen;
7982 }
7983 pstr = src + strlen(src);
7984 if (pstr - src) {
7985 if (autoTrim)
7986 result.push_back(TextTrim(std::string(src, pstr - src).c_str()));
7987 else
7988 result.push_back(std::string(src, pstr - src).c_str());
7989 }
7990 return result;
7991}
7992
7993std::string TextVectorConcat(std::vector<std::string> vect, const char* sep, bool allowEmpty) {
7994 std::string result;
7995 std::vector<std::string>::iterator i, e;
7996 for (i=vect.begin(), e=vect.end(); i!=e; i++) {
7997 if (allowEmpty || (*i).size()) {
7998 if (result.size())
7999 result += sep;
8000 result += *i;
8001 }
8002 }
8003 return result;
8004}
8005
8006char* StringFormat(uint32& size, const char *format, ...) {
8007 if (strlen(format) == 0) {
8008 size = 0;
8009 return 0;
8010 }
8011
8012 va_list args;
8013 va_start(args, format);
8014 char* res = StringFormatVA(size, format, args);
8015 va_end(args);
8016 return res;
8017}
8018
8019
8020char* StringFormatVA(uint32& size, const char *format, va_list orig_args) {
8021 char* str = NULL;
8022
8023 #ifdef WINDOWS
8024 int len = _vscprintf(format, orig_args) + 10;
8025 str = (char*)malloc(len+1);
8026 #ifdef CYGWIN
8027 len = vsnprintf(str, len, format, orig_args);
8028 #else
8029 len = _vsnprintf_s(str, len, len+1, format, orig_args);
8030 #endif
8031
8032 if (len)
8033 size = (uint32)strlen(str);
8034 else {
8035 size = 0;
8036 free((char*)str);
8037 str = NULL;
8038 }
8039 #else // WINDOWS
8040 int len = vasprintf(&str, format, orig_args);
8041 if(len >= 0)
8042 size = strlen(str);
8043 else
8044 size = 0;
8045 #endif //HAVE_VASPRINTF
8046
8047 return str;
8048}
8049
8050bool StringFormatInto(char* dst, uint32 maxsize, const char *format, ...) {
8051 if (!dst || !maxsize)
8052 return false;
8053 if (strlen(format) == 0) {
8054 dst[0] = 0;
8055 return true;
8056 }
8057
8058 uint32 len = 0;
8059 va_list args;
8060 va_start(args, format);
8061 char* str = StringFormatVA(len, format, args);
8062 va_end(args);
8063 if (str && len) {
8064 if (len > maxsize) {
8065 free(str);
8066 return false;
8067 }
8068 else {
8069 utils::strcpyavail(dst, str, maxsize, true);
8070 free(str);
8071 return true;
8072 }
8073 }
8074 else {
8075 free(str);
8076 return false;
8077 }
8078}
8079
8080std::string StringFormat(const char *format, ...) {
8081 if (strlen(format) == 0)
8082 return "";
8083
8084 std::string res;
8085 uint32 len = 0;
8086 va_list args;
8087 va_start(args, format);
8088 char* str = StringFormatVA(len, format, args);
8089 va_end(args);
8090 if (str && len)
8091 res = str;
8092 else
8093 res = "";
8094 free(str);
8095 return res;
8096}
8097
8098uint32 StringSingleReplace(std::string& text, std::string key, std::string value, bool onlyFirst) {
8099 if (!text.size() || !key.size())
8100 return 0;
8101
8102 uint32 c = 0;
8103 size_t p = 0;
8104 uint32 keylen = (uint32)key.size();
8105
8106 while ( (p = text.find(key, p)) != std::string::npos) {
8107 text.replace(p, keylen, value);
8108 p++;
8109 c++;
8110 if (onlyFirst)
8111 break;
8112 }
8113 return c;
8114}
8115
8116uint32 StringMultiReplace(std::string& text, std::map<std::string, std::string>& map, bool onlyFirst) {
8117 if (!text.size() || !map.size())
8118 return 0;
8119
8120 uint32 c = 0;
8121
8122 std::map<std::string, std::string>::iterator it, itEnd;
8123 for (it = map.begin(), itEnd = map.end(); it != itEnd; ++it)
8124 c += StringSingleReplace(text, it->first, it->second, onlyFirst);
8125
8126 return c;
8127}
8128
8129
8130// Example use
8131 //uint32 len;
8132 //char* file = utils::ReadAFile("test.html", len);
8133 //std::string sfile = file;
8134
8135 //std::list<std::map<std::string, std::string> > list;
8136
8137 //char tmp[64];
8138 //std::map<std::string, std::string> map;
8139 //for (uint32 n=0; n<10; n++) {
8140 // map["%1%"] = utils::Int2Ascii(n, tmp, 64, 10);
8141 // map["%2%"] = "Hello2";
8142 // map["%3%"] = "Hello3";
8143 // map["%4%"] = "Hello4";
8144 // map["%5%"] = "Hello5";
8145 // list.push_back(map);
8146 //}
8147
8148 //utils::ScriptReplace(sfile, "test", list);
8149
8150uint32 StringScriptReplace(std::string& text, std::string name, std::list<std::map<std::string, std::string> >& list) {
8151 if (!text.size() || !name.size())
8152 return 0;
8153
8154 uint32 c = 0;
8155
8156 char* start = new char[name.size() + 50];
8157 snprintf(start, name.size()+50, "<!-- start:%s -->", name.c_str());
8158 char* end = new char[name.size() + 50];
8159 snprintf(end, name.size()+50, "<!-- end:%s -->", name.c_str());
8160
8161 // Find the section boundaries
8162 size_t a1 = text.find(start);
8163 if (a1 == std::string::npos) {
8164 delete [] start;
8165 delete [] end;
8166 return 0;
8167 }
8168 size_t b1 = text.find(end);
8169 if (b1 == std::string::npos) {
8170 delete [] start;
8171 delete [] end;
8172 return 0;
8173 }
8174
8175 uint32 a2 = (uint32)(a1 + strlen(start));
8176 uint32 b2 = (uint32)(b1 + strlen(end));
8177
8178 // Copy it
8179 std::string section = text.substr(a2, b1-a2);
8180 std::string tmp;
8181 std::string result;
8182
8183 std::list<std::map<std::string, std::string> >::iterator it, itEnd;
8184 for (it = list.begin(), itEnd = list.end(); it != itEnd; ++it) {
8185 tmp = section;
8186 c += StringMultiReplace(tmp, *it, false);
8187 result += tmp;
8188 }
8189
8190 text.replace(a1, b2-a1, result);
8191 delete [] start;
8192 delete [] end;
8193 return c;
8194}
8195
8196
8197
8198unsigned char Hex2Char(const char* str) {
8199 unsigned char c = (unsigned char) strtol(str, NULL, 16);
8200 if (c == 0)
8201 return ' ';
8202 else
8203 return c;
8204}
8205
8206unsigned char Dec2Char(const char* str) {
8207 unsigned char c = (unsigned char) strtol(str, NULL, 10);
8208 if (c == 0)
8209 return ' ';
8210 else
8211 return c;
8212}
8213
8214int8 CompareFloats(float64 a, float64 b) {
8215 float64 diff = a - b;
8216 if ((diff < std::numeric_limits<float64>::epsilon()) && (-diff < std::numeric_limits<float64>::epsilon()))
8217 return 0;
8218 else if (diff > 0)
8219 return 1;
8220 else
8221 return -1;
8222}
8223
8224
8225#define poly 0xEDB88320
8226/* Some compilers need
8227 #define poly 0xEDB88320uL
8228 */
8229
8230/* On entry, addr=>start of data
8231 num = length of data
8232 crc = incoming CRC */
8233int CRC32(const char *addr, uint32 length, int32 crc) {
8234 uint32 i;
8235 for (; length>0; length--) { /* Step through bytes in memory */
8236 crc = crc ^ *addr++; /* Fetch byte from memory, XOR into CRC */
8237 for (i=0; i<8; i++) { /* Prepare to rotate 8 bits */
8238 if (crc & 1) /* b0 is set... */
8239 crc = (crc >> 1) ^ poly; /* rotate and XOR with ZIP polynomic */
8240 else /* b0 is clear... */
8241 crc >>= 1; /* just rotate */
8242 /* Some compilers need:
8243 crc &= 0xFFFFFFFF;
8244 */
8245 } /* Loop for 8 bits */
8246 } /* Loop until num=0 */
8247 return(crc); /* Return updated CRC */
8248}
8249
8250
8251std::string ReadAFileString(std::string filename) {
8252 uint32 length;
8253 char* data = ReadAFile(filename.c_str(), length, false);
8254 if (!data || !length)
8255 return "";
8256 std::string dataString = data;
8257 delete [] data;
8258 return dataString;
8259}
8260
8261char* ReadAFile(const char* dir, const char* filename, uint32& length, bool binary) {
8262 if (!dir || !filename || strlen(filename) == 0)
8263 return NULL;
8264
8265 char* fullname = new char[strlen(dir) + strlen(filename) + 5];
8266 snprintf(fullname, strlen(dir) + strlen(filename) + 5, "%s/%s", dir, filename);
8267 char* res = ReadAFile(fullname, length, binary);
8268 delete [] fullname;
8269 return res;
8270}
8271
8272char* ReadAFile(const char* filename, uint32& length, bool binary) {
8273 if (!filename || strlen(filename) == 0)
8274 return NULL;
8275
8276 length = 0;
8277
8278 FILE* file;
8279 if (binary)
8280 file = fopen(filename, "rb");
8281 else
8282 file = fopen(filename, "r");
8283 if (file == NULL) {
8284 //LogPrint(0,0,0,"Couldn't open file %s", filename);
8285 return NULL;
8286 }
8287
8288 fseek(file, 0, SEEK_END);
8289 length = ftell(file);
8290 fseek(file, 0, SEEK_SET);
8291
8292 if (length <= 0) {
8293 fclose(file);
8294 //LogPrint(0,0,0,"File %s has no size", filename);
8295 return NULL;
8296 }
8297
8298 char* data = new char[length+1];
8299
8300 int res = (int)fread(data, 1, length, file);
8301
8302 if ((res <= 0) || (res != length)) {
8303 int error = ferror(file);
8304 int eof = feof(file);
8305 if (eof == 0) {
8306 delete [] data;
8307 fclose(file);
8308 LogPrint(0,0,0,"File %s has no data", filename);
8309 return NULL;
8310 }
8311 }
8312
8313 fclose(file);
8314
8315 length = (uint32)res;
8316 data[length] = 0;
8317 return data;
8318}
8319
8320bool WriteAFile(const char* dir, const char* filename, const char* data, uint32 length, bool binary) {
8321 if (!dir || !filename || strlen(filename) == 0)
8322 return false;
8323
8324 char* fullname = new char[strlen(dir) + strlen(filename) + 5];
8325 snprintf(fullname, strlen(dir) + strlen(filename) + 5, "%s/%s", dir, filename);
8326 bool res = WriteAFile(fullname, data, length, binary);
8327 delete [] fullname;
8328 return res;
8329}
8330
8331bool WriteAFile(const char* filename, const char* data, uint32 length, bool binary) {
8332 if (!filename || strlen(filename) == 0)
8333 return false;
8334
8335 FILE* file;
8336 if (binary)
8337 file = fopen(filename, "wb");
8338 else
8339 file = fopen(filename, "w");
8340 if (file == NULL)
8341 return false;
8342
8343 int res = (int)fwrite(data, 1, length, file);
8344 fclose(file);
8345 return (res == length);
8346}
8347
8348bool AppendToAFile(const char* dir, const char* filename, const char* data, uint32 length, bool binary) {
8349 if (!dir || !filename || strlen(filename) == 0)
8350 return false;
8351
8352 char* fullname = new char[strlen(dir) + strlen(filename) + 5];
8353 snprintf(fullname, strlen(dir) + strlen(filename) + 5, "%s/%s", dir, filename);
8354 bool res = AppendToAFile(fullname, data, length, binary);
8355 delete [] fullname;
8356 return res;
8357}
8358
8359bool AppendToAFile(const char* filename, const char* data, uint32 length, bool binary) {
8360 if (!filename || strlen(filename) == 0)
8361 return false;
8362
8363 FILE* file;
8364 if (binary)
8365 file = fopen(filename, "ab");
8366 else
8367 file = fopen(filename, "a");
8368 if (file == NULL)
8369 return false;
8370
8371 int res = (int)fwrite(data, 1, length, file);
8372 fclose(file);
8373 return (res == length);
8374}
8375
8376bool DeleteAFile(const char* dir, const char* filename, bool force) {
8377 if (!dir || !filename || strlen(filename) == 0)
8378 return false;
8379
8380 char* fullname = new char[strlen(dir) + strlen(filename) + 5];
8381 snprintf(fullname, strlen(dir) + strlen(filename) + 5, "%s/%s", dir, filename);
8382 bool res = DeleteADir(fullname, force);
8383 delete [] fullname;
8384 return res;
8385}
8386
8387bool DeleteAFile(const char* filename, bool force) {
8388 return DeleteADir(filename, force);
8389}
8390
8391bool ChangeAFileAttr(const char* filename, bool read, bool write) {
8392 if (!filename || strlen(filename) == 0)
8393 return false;
8394 #ifdef WINDOWS
8395 if (!write)
8396 return (SetFileAttributes(filename, FILE_ATTRIBUTE_READONLY) != 0);
8397 else
8398 return (SetFileAttributes(filename, FILE_ATTRIBUTE_NORMAL) != 0);
8399 #else
8400 if (read && write)
8401 return (chmod(filename,S_IREAD | S_IWRITE) != 0);
8402 else if (read)
8403 return (chmod(filename,S_IREAD) == 0);
8404 else if (write)
8405 return (chmod(filename,S_IWRITE) == 0);
8406 else
8407 return (chmod(filename,0) == 0);
8408 #endif
8409}
8410
8411bool MoveAFile(const char* oldfilename, const char* newfilename, bool force) {
8412 #ifdef WINDOWS
8413 bool result;
8414 if (force)
8415 result = (MoveFileEx(oldfilename, newfilename, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0);
8416 else
8417 result = (MoveFileEx(oldfilename, newfilename, MOVEFILE_COPY_ALLOWED) != 0);
8418 if (result)
8419 return true;
8420 LogPrint(0,LOG_SYSTEM,0,"MoveAFile '%s' to '%s' failed with: %s", oldfilename, newfilename, GetLastOSErrorMessage().c_str());
8421 return false;
8422 #else
8423 if (rename(oldfilename, newfilename) == 0)
8424 return true;
8425 int error = errno;
8426 if (force) {
8427 FileDetails info = GetFileDetails(newfilename);
8428 if (info.doesExist) {
8429 delete(newfilename);
8430 if (rename(oldfilename, newfilename) == 0)
8431 return true;
8432 }
8433 }
8434 if (error == EXDEV) {
8435 if (CopyAFile(newfilename, oldfilename)) {
8436 DeleteAFile(oldfilename, true);
8437 return true;
8438 }
8439 }
8440 return false;
8441 #endif
8442}
8443
8444bool MoveAFile(const char* olddir, const char* oldfilename, const char* newdir, const char* newfilename, bool force) {
8445 if (!newdir || !newfilename || !strlen(newfilename) ||
8446 !olddir || !oldfilename || !strlen(oldfilename) )
8447 return false;
8448
8449 char* oldname = new char[strlen(olddir) + strlen(oldfilename) + 5];
8450 snprintf(oldname, strlen(olddir) + strlen(oldfilename) + 5, "%s/%s", olddir, oldfilename);
8451 char* newname = new char[strlen(newdir) + strlen(newfilename) + 5];
8452 snprintf(newname, strlen(newdir) + strlen(newfilename) + 5, "%s/%s", newdir, newfilename);
8453 bool res = MoveAFile(newname, oldname, force);
8454 delete [] oldname;
8455 delete [] newname;
8456 return res;
8457}
8458
8459bool CopyAFile(const char* oldfilename, const char* newfilename, bool force) {
8460 #ifdef WINDOWS
8461 if (force)
8462 return (CopyFile(oldfilename, newfilename, FALSE) != 0);
8463 else
8464 return (CopyFile(oldfilename, newfilename, TRUE) != 0);
8465 #else
8466 FileDetails target = GetFileDetails(newfilename);
8467 if (target.doesExist && !force)
8468 return false;
8469 if (target.isDirectory)
8470 return false;
8471 uint32 size;
8472 char* data = ReadAFile(oldfilename, size, true);
8473 if (!data || !size) {
8474 delete [] data;
8475 return false;
8476 }
8477
8478 if ((target.doesExist) && !DeleteAFile(newfilename, true)) {
8479 delete [] data;
8480 return false;
8481 }
8482
8483 if (!WriteAFile(newfilename, data, size, true)) {
8484 delete [] data;
8485 return false;
8486 }
8487 delete [] data;
8488 return true;
8489 #endif
8490}
8491
8492bool CopyAFile(const char* olddir, const char* oldfilename, const char* newdir, const char* newfilename, bool force) {
8493 if (!newdir || !newfilename || !strlen(newfilename) ||
8494 !olddir || !oldfilename || !strlen(oldfilename) )
8495 return false;
8496
8497 char* oldname = new char[strlen(olddir) + strlen(oldfilename) + 5];
8498 snprintf(oldname, strlen(olddir) + strlen(oldfilename) + 5, "%s/%s", olddir, oldfilename);
8499 char* newname = new char[strlen(newdir) + strlen(newfilename) + 5];
8500 snprintf(newname, strlen(newdir) + strlen(newfilename) + 5, "%s/%s", newdir, newfilename);
8501 bool res = CopyAFile(newname, oldname, force);
8502 delete [] oldname;
8503 delete [] newname;
8504 return res;
8505}
8506
8507bool CreateADir(const char* dirname) {
8508 if (!dirname || strlen(dirname) == 0)
8509 return false;
8510
8511 FileDetails info = GetFileDetails(dirname);
8512
8513 if (info.doesExist) {
8514 if (info.isDirectory)
8515 return true;
8516 else
8517 return false;
8518 }
8519
8520 #ifdef WINDOWS
8521 if (CreateDirectory(dirname, NULL) != 0)
8522 return true;
8523 else
8524 return false;
8525 #else
8526 if (mkdir(dirname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
8527 return true;
8528 else
8529 return false;
8530 #endif
8531}
8532
8533bool DeleteADir(const char* dirname, bool force) {
8534 if (!dirname || strlen(dirname) == 0)
8535 return false;
8536
8537 FileDetails info = GetFileDetails(dirname);
8538
8539 if (!info.doesExist)
8540 return true;
8541
8542 // If it is a file
8543 if (!info.isDirectory) {
8544 #ifdef WINDOWS
8545 if (DeleteFile(dirname) != 0)
8546 return true;
8547 else if (!force)
8548 return false;
8549 else {
8550 ChangeAFileAttr(dirname, true, true);
8551 if (DeleteFile(dirname) != 0)
8552 return true;
8553 else
8554 return false;
8555 }
8556 #else
8557 if (unlink(dirname) == 0)
8558 return true;
8559 else if (!force)
8560 return false;
8561 else {
8562 ChangeAFileAttr(dirname, true, true);
8563 if (unlink(dirname) == 0)
8564 return true;
8565 else
8566 return false;
8567 }
8568 #endif
8569 }
8570
8571 // Now we know it is a dir
8572 #ifdef WINDOWS
8573 if (RemoveDirectory(dirname) != 0)
8574 return true;
8575 else if (!force)
8576 return false;
8577 else {
8578 DeleteFilesInADir(dirname, true);
8579 if (RemoveDirectory(dirname) != 0)
8580 return true;
8581 else
8582 return false;
8583 }
8584 #else
8585 if (rmdir(dirname) == 0)
8586 return true;
8587 else if (!force)
8588 return false;
8589 else {
8590 DeleteFilesInADir(dirname, true);
8591 if (rmdir(dirname) == 0)
8592 return true;
8593 else
8594 return false;
8595 }
8596 #endif
8597
8598}
8599
8600bool DeleteFilesInADir(const char* dirname, bool force) {
8601 if (!dirname || strlen(dirname) == 0)
8602 return false;
8603
8604 uint32 count;
8605 char* files = utils::GetFileList(dirname, NULL, count, true);
8606 if (!files)
8607 return true;
8608
8609 const char* name;
8610 for (uint32 n = 0; n<count; n++) {
8611 name = files + (n*(MAXFILENAMELEN+1));
8612 if (!DeleteADir(name, force)) {
8613 delete[] files;
8614 return false;
8615 }
8616 }
8617
8618 delete [] files;
8619 return true;
8620}
8621
8622
8623
8624
8625
8626uint32 TextReplaceCharsInPlace(char* str, uint32 size, char find, char replace) {
8627 uint32 c = 0;
8628 for (uint32 n=0; n<size; n++) {
8629 if (str[n] == find) {
8630 str[n] = replace;
8631 c++;
8632 }
8633 }
8634 return c;
8635}
8636
8637const char* laststrstr(const char* str1, const char* str2) {
8638 const char* strp;
8639 int len1, len2;
8640
8641 len2 = (int)strlen(str2);
8642 if(len2==0)
8643 return (char*)str1;
8644
8645 len1 = (int)strlen(str1);
8646 if (len1 == len2)
8647 return (strcmp(str1, str2) == 0) ? str1 : 0;
8648 else if (len1 - len2 < 0)
8649 return 0;
8650
8651 strp = (char*)(str1 + len1 - len2);
8652 while(strp != str1) {
8653 if(*strp == *str2) {
8654 if(strncmp(strp,str2,len2)==0)
8655 return strp;
8656 }
8657 strp--;
8658 }
8659 return 0;
8660}
8661
8662const char* laststristr(const char* str1, const char* str2) {
8663 const char* strp;
8664 int len1, len2;
8665
8666 len2 = (int)strlen(str2);
8667 if(len2==0)
8668 return (char*)str1;
8669
8670 len1 = (int)strlen(str1);
8671 if (len1 == len2)
8672 return (stricmp(str1, str2) == 0) ? str1 : 0;
8673 else if (len1 - len2 < 0)
8674 return 0;
8675
8676 strp = (char*)(str1 + len1 - len2);
8677 while(strp != str1) {
8678 if(*strp == *str2) {
8679 if(strnicmp(strp,str2,len2)==0)
8680 return strp;
8681 }
8682 strp--;
8683 }
8684 return 0;
8685}
8686
8687bool TextEndsWith(const char* str, const char* end, bool caseSensitive) {
8688 if (!caseSensitive)
8689 return (laststristr(str, end) == str + strlen(str) - strlen(end));
8690 else
8691 return (laststrstr(str, end) == str + strlen(str) - strlen(end));
8692}
8693
8694bool TextStartsWith(const char* str, const char* start, bool caseSensitive) {
8695 if (!caseSensitive)
8696 return (stristr(str, start) == str);
8697 else
8698 return (strstr(str, start) == str);
8699}
8700
8701
8702std::string GetFilePath(const char* filename) {
8703 std::string name = filename;
8704 std::string::size_type p1 = name.find_last_of('/');
8705 std::string::size_type p2 = name.find_last_of('\\');
8706 if (p1 == std::string::npos) {
8707 if (p2 == std::string::npos)
8708 return "";
8709 else
8710 return name.substr(0, p2 + 1);
8711 }
8712 else if (p2 == std::string::npos)
8713 return name.substr(0, p1 + 1);
8714 else
8715 return name.substr(0, (p1 > p2) ? p1 + 1 : p2 + 1);
8716}
8717
8718const char* GetFileBasename(const char* filename) {
8719 std::string name = filename;
8720 std::string::size_type p1 = name.find_last_of('/');
8721 std::string::size_type p2 = name.find_last_of('\\');
8722 if (p1 == std::string::npos) {
8723 if (p2 == std::string::npos)
8724 return filename;
8725 else
8726 return filename + p2 + 1;
8727 }
8728 else if (p2 == std::string::npos)
8729 return filename + p1 + 1;
8730 else
8731 return (p1 > p2) ? filename + p1 + 1 : filename + p2 + 1;
8732}
8733
8734std::string GetCurrentDir() {
8735
8736 char cCurrentPath[FILENAME_MAX];
8737
8738 if (!GetCurrentDirEx(cCurrentPath, sizeof(cCurrentPath)))
8739 {
8740 return "";
8741 }
8742 cCurrentPath[sizeof(cCurrentPath) - 1] = '\0'; /* not really required */
8743 //printf("The current working directory is %s\n", cCurrentPath);
8744 return cCurrentPath;
8745}
8746
8747char* GetFileList(const char* dirname, const char* ext, uint32& count, bool fullpath, uint32 maxNameLen) {
8748 DIR* dir = opendir(dirname);
8749 if (dir == NULL)
8750 return NULL;
8751
8752 uint32 extlen = (ext == NULL) ? 0 : (uint32)strlen(ext);
8753 struct dirent* direntry = NULL;
8754
8755 uint32 tempMaxCount = 50;
8756 uint32 len = (maxNameLen+1)*tempMaxCount;
8757 char* result = new char[len];
8758 char* dst = result;
8759 char* tmp;
8760 uint32 dirnamelen = (uint32)strlen(dirname);
8761 bool dirslash = (*(dirname + dirnamelen - 1) == '/') || (*(dirname + dirnamelen - 1) == '\\');
8762
8763 count = 0;
8764 direntry = readdir(dir);
8765 while (direntry) {
8766 #ifdef WINDOWS
8767 if (direntry->d_name && direntry->d_name[0] != '\0') {
8768 #else
8769 if (direntry->d_name[0] != '\0') {
8770 #endif
8771 if ( !strcmp(direntry->d_name, ".") || !strcmp(direntry->d_name, "..") ) {
8772 direntry = readdir(dir);
8773 continue;
8774 }
8775 if (ext && extlen && !TextEndsWith(direntry->d_name, ext)) {
8776 direntry = readdir(dir);
8777 continue;
8778 }
8779 if (fullpath) {
8780 utils::strcpyavail(dst, dirname, maxNameLen, true);
8781 if (!dirslash) {
8782 memset(dst+dirnamelen, '/', 1);
8783 utils::strcpyavail(dst+dirnamelen+1, direntry->d_name, maxNameLen, true);
8784 }
8785 else
8786 utils::strcpyavail(dst+dirnamelen, direntry->d_name, maxNameLen, true);
8787 }
8788 else
8789 utils::strcpyavail(dst, direntry->d_name, maxNameLen, true);
8790 dst+= maxNameLen+1;
8791 count++;
8792 if (count >= tempMaxCount) {
8793 tempMaxCount *= 2;
8794 tmp = new char[len*2];
8795 memcpy(tmp, result, len);
8796 delete [] result;
8797 result = tmp;
8798 dst = result + len;
8799 len *= 2;
8800 }
8801 }
8802 direntry = readdir(dir);
8803 }
8804 closedir(dir);
8805 return result;
8806}
8807
8808bool DoesADirExist(const char* dirname) {
8809 FileDetails details = GetFileDetails(dirname);
8810 return (details.doesExist && details.isDirectory);
8811}
8812
8813bool DoesAFileExist(const char* filename) {
8814 FileDetails details = GetFileDetails(filename);
8815 return (details.doesExist && !details.isDirectory);
8816}
8817
8818FileDetails GetFileDetails(const char* filename) {
8819 struct stat statbuf;
8820 int fd, result;
8821
8822 FileDetails info;
8823
8824 info.doesExist = false;
8825 info.isDirectory = false;
8826 info.isReadable = false;
8827 info.isWritable = false;
8828 info.isExecutable = false;
8829 info.size = 0;
8830 info.lastAccessTime = 0;
8831 info.lastModifyTime = 0;
8832 info.creationTime = 0;
8833
8834 // If file cannot be opened
8835 if ((fd = _open(filename, O_RDONLY)) == -1) {
8836 // On Windows, this might be a dir
8837 DIR* dir = opendir(filename);
8838 if (dir == NULL)
8839 return info;
8840 struct dirent* direntry = readdir(dir);
8841
8842 info.doesExist = true;
8843 info.isDirectory = true;
8845 info.isReadable = true;
8846 info.isWritable = true;
8847 info.isExecutable = true;
8848
8849 closedir(dir);
8850 return info;
8851 }
8852
8853 // Get data associated with "fd":
8854 result = fstat(fd, &statbuf);
8855
8856 // Check if statistics are valid:
8857 if( result != 0 ) {
8858 _close(fd);
8859 return info;
8860 }
8861
8862 _close(fd);
8863 info.doesExist = true;
8864
8865 info.size = statbuf.st_size;
8866 info.isDirectory = ((statbuf.st_mode & S_IFDIR) != 0);
8867 info.lastAccessTime = FTime2PsyTime(statbuf.st_atime);
8868 info.lastModifyTime = FTime2PsyTime(statbuf.st_ctime);
8869 info.creationTime = FTime2PsyTime(statbuf.st_ctime);
8870
8871 #ifdef WINDOWS
8872 info.isReadable = ((statbuf.st_mode & _S_IREAD) != 0);
8873 info.isWritable = ((statbuf.st_mode & _S_IWRITE) != 0);
8874 info.isExecutable = ((statbuf.st_mode & _S_IEXEC) != 0);
8875 #else
8876 info.isReadable = ((statbuf.st_mode & S_IRUSR) != 0);
8877 info.isWritable = ((statbuf.st_mode & S_IWUSR) != 0);
8878 info.isExecutable = ((statbuf.st_mode & S_IXUSR) != 0);
8879 #endif
8880
8881 return info;
8882
8883}
8884
8885char* TextSubstringCopy(const char* ascii, uint32 start, uint32 end) {
8886 uint32 len;
8887 char* copy;
8888 if (!ascii || (start >= end) || (!(len = (uint32)strlen(ascii))) ) {
8889 copy = new char[1];
8890 copy[0] = 0;
8891 }
8892 else {
8893 if (end > len - 1)
8894 end = len - 1;
8895 copy = new char[end - start + 2];
8896 memcpy(copy, ascii + start,
8897 end - start + 1);
8898 copy[end - start + 1] = 0;
8899 }
8900 return copy;
8901}
8902
8903
8904bool IsTextNumeric(const char* ascii, uint32 start, uint32 end) {
8905 if (!ascii || (start > end)) return false;
8906 uint32 len = (uint32)strlen(ascii);
8907 uint32 stop = end ? end+1 : len;
8908 if (stop > len) stop = len;
8909 for (uint32 n=start; n<stop; n++) {
8910 if (( ascii[n] < '0' || ascii[n] > '9') && ascii[n] != '.')
8911 return false;
8912 }
8913 return true;
8914}
8915
8916int64 Ascii2Int64(const char* ascii, uint32 start, uint32 end) {
8917 if (!ascii || (start > end)) return false;
8918 if (!end)
8919 #ifdef WINDOWS
8920 return _strtoi64(ascii + start, NULL, 10);
8921 #else
8922 return strtoll(ascii + start, NULL, 10);
8923 #endif
8924 char* copy = TextSubstringCopy(ascii, start, end);
8925 #ifdef WINDOWS
8926 int64 val = _strtoi64(copy, NULL, 10);
8927 #else
8928 int64 val = strtoll(copy, NULL, 10);
8929 #endif
8930 delete[] copy;
8931 return val;
8932}
8933
8934uint64 Ascii2Uint64(const char* ascii, uint32 start, uint32 end) {
8935 if (!ascii || (start > end)) return false;
8936 if (!end)
8937 #ifdef WINDOWS
8938 return _strtoui64(ascii + start, NULL, 10);
8939 #else
8940 return strtoull(ascii + start, NULL, 10);
8941 #endif
8942 char* copy = TextSubstringCopy(ascii, start, end);
8943 #ifdef WINDOWS
8944 uint64 val = _strtoui64(copy, NULL, 10);
8945 #else
8946 uint64 val = strtoull(copy, NULL, 10);
8947 #endif
8948 delete[] copy;
8949 return val;
8950}
8951
8952uint64 AsciiHex2Uint64(const char* ascii, uint32 start, uint32 end) {
8953 if (!ascii || (start > end)) return false;
8954 if (!end)
8955 #ifdef WINDOWS
8956 return _strtoui64(ascii + start, NULL, 16);
8957 #else
8958 return strtoull(ascii + start, NULL, 16);
8959 #endif
8960 char* copy = TextSubstringCopy(ascii, start, end);
8961 #ifdef WINDOWS
8962 uint64 val = _strtoui64(copy, NULL, 16);
8963 #else
8964 uint64 val = strtoull(copy, NULL, 16);
8965 #endif
8966 delete[] copy;
8967 return val;
8968}
8969
8970int32 Ascii2Int32(const char* ascii, uint32 start, uint32 end) {
8971 if (!ascii || (start > end)) return false;
8972 if (!end)
8973 return (int32)strtol(ascii + start, NULL, 10);
8974 char* copy = TextSubstringCopy(ascii, start, end);
8975 int32 val = (int32)strtol(copy, NULL, 10);
8976 delete[] copy;
8977 return val;
8978}
8979
8980uint32 Ascii2Uint32(const char* ascii, uint32 start, uint32 end) {
8981 if (!ascii || (start > end)) return false;
8982 if (!end)
8983 return (uint32)strtoul(ascii + start, NULL, 10);
8984 char* copy = TextSubstringCopy(ascii, start, end);
8985 uint32 val = (uint32)strtoul(copy, NULL, 10);
8986 delete[] copy;
8987 return val;
8988}
8989
8990uint32 AsciiHex2Uint32(const char* ascii, uint32 start, uint32 end) {
8991 if (!ascii || (start > end)) return false;
8992 if (!end)
8993 return (uint32)strtoul(ascii + start, NULL, 16);
8994 char* copy = TextSubstringCopy(ascii, start, end);
8995 uint32 val = (uint32)strtoul(copy, NULL, 16);
8996 delete[] copy;
8997 return val;
8998}
8999
9000float64 Ascii2Float64(const char* ascii, uint32 start, uint32 end) {
9001 if (!ascii || (start > end)) return false;
9002 if (!end)
9003 return strtod(ascii + start, NULL);
9004 char* copy = TextSubstringCopy(ascii, start, end);
9005 float64 val = strtod(copy, NULL);
9006 delete[] copy;
9007 return val;
9008}
9009
9010
9011std::string DecodeJSON(std::string str) {
9012 bool isEscaped = false;
9013 char* tmp = new char[5];
9014 tmp[4] = 0;
9015 std::ostringstream o;
9016 std::string::iterator c;
9017 for (c = str.begin(); c != str.end(); c++) {
9018 if (isEscaped) {
9019 switch (*c) {
9020 case '"': o << "\""; break;
9021 case '\\': o << "\\"; break;
9022 case 'b': o << "\b"; break;
9023 case 'f': o << "\f"; break;
9024 case 'n': o << "\n"; break;
9025 case 'r': o << "\r"; break;
9026 case 't': o << "\t"; break;
9027 case 'u':
9028 c++;
9029 tmp[0] = (uint8)(*c++);
9030 tmp[1] = (uint8)(*c++);
9031 tmp[2] = (uint8)(*c++);
9032 tmp[3] = (uint8)(*c);
9033 o << (char) strtol(tmp, NULL, 16);
9034 break;
9035 default: o << *c;
9036 }
9037 isEscaped = false;
9038 }
9039 else {
9040 switch (*c) {
9041 case '\\': isEscaped = true; break;
9042 default: o << *c;
9043 }
9044 }
9045 }
9046 delete[] tmp;
9047 return o.str();
9048}
9049
9050std::string EncodeJSON(std::string str) {
9051 std::ostringstream o;
9052 std::string::iterator c;
9053 for (c = str.begin(); c != str.end(); c++) {
9054 switch (*c) {
9055 case '"': o << "\\\""; break;
9056 case '\\': o << "\\\\"; break;
9057 case '\b': o << "\\b"; break;
9058 case '\f': o << "\\f"; break;
9059 case '\n': o << "\\n"; break;
9060 case '\r': o << "\\r"; break;
9061 case '\t': o << "\\t"; break;
9062 default:
9063 if ('\x00' <= *c && *c <= '\x1f') {
9064 o << "\\u"
9065 << std::hex << std::setw(4) << std::setfill('0') << (int)*c;
9066 } else {
9067 o << *c;
9068 }
9069 }
9070 }
9071 return o.str();
9072
9073 //if (str.size() == 0)
9074 // return str;
9075 //std::string result = str;
9076 //char c;
9077 //char* tmp = (char*)malloc(20);
9078
9079 //for (uint32 n=0; n<result.size(); n++) {
9080 // switch(c = result[n]) {
9081 // case '\n':
9082 // result.replace(n, 1, "\\n");
9083 // n+=1;
9084 // break;
9085 // case '\r':
9086 // result.replace(n, 1, "\\r");
9087 // n+=1;
9088 // break;
9089 // case '\t':
9090 // result.replace(n, 1, "\\t");
9091 // n+=2;
9092 // break;
9093 // case '"':
9094 // result.replace(n, 1, "\\\"");
9095 // n+=2;
9096 // break;
9097 // case '\\':
9098 // result.replace(n, 1, "\\\\");
9099 // n+=2;
9100 // break;
9101 // case '\b':
9102 // result.replace(n, 1, "\\b");
9103 // n+=2;
9104 // break;
9105 // case '\f':
9106 // result.replace(n, 1, "\\f");
9107 // n+=2;
9108 // break;
9109 // default:
9110 // if ('\x00' <= c && c <= '\x1f') {
9111 // snprintf(tmp, 20, "\\u%.4x;", (int) c & 0xFF);
9112 // result.replace(n, 1, tmp);
9113 // n+=(uint32)strlen(tmp)-1;
9114 // }
9115 // break;
9116 // }
9117 //}
9118
9119 //free((char*)tmp);
9120
9121 //return result;
9122}
9123
9124
9125// Seeds the rand() function
9126bool SeedRandomValues(uint32 seedvalue) {
9127 srand( seedvalue ? seedvalue : (uint32)GetTimeNow());
9128 return true;
9129}
9130
9131// Returns a random value between 0.0 and 1.0
9132double RandomValue() {
9133 double d = 0;
9134 double t = (1.0/((RAND_MAX + 1.0)*(RAND_MAX + 1.0)*(RAND_MAX + 1.0)));
9135 do {
9136 d = (rand () * ((RAND_MAX + 1.0) * (RAND_MAX + 1.0))
9137 + rand () * (RAND_MAX + 1.0)
9138 + rand ()) * t;
9139 } while (d >= 1); /* Round off */
9140 return d;
9141}
9142
9143// Returns a random value between 0.0 and max
9144double RandomValue(double max) {
9145 return RandomValue() * max;
9146}
9147
9148// Returns a random value between from and to
9149double RandomValue(double from, double to) {
9150 if (to < from)
9151 return (RandomValue()*(from-to))+to;
9152 else
9153 return (RandomValue()*(to-from))+from;
9154}
9155
9156// Returns a random value between from and to, rounded to nearest interval
9157double RandomValue(double from, double to, double interval) {
9158 if (to < from)
9159 return RandomValue(to, from, interval);
9160 double r = RandomValue(0, to-from);
9161 uint64 n = (uint64)(r/interval);
9162 double l = r - (n*interval);
9163 if (l >= (0.5*interval))
9164 return from + (n*interval);
9165 else
9166 return from + ((n+1)*interval);
9167}
9168
9169int64 RandomInt(int64 from, int64 to) {
9170 return (int64)roundl(RandomValue((double)from, (double)to));
9171}
9172
9173
9174//std::string BytifySize(int32 val) {
9175// return BytifySize((double)val);
9176//}
9177
9178std::string BytifySize(double val) {
9179
9180 double kb = 1024;
9181 double mb = kb*1024;
9182 double gb = mb*1024;
9183 double tb = gb*1024;
9184
9185 char *tmp = new char[64];
9186
9187 if (val < 512)
9188 snprintf(tmp, 64, "%.2f B", val);
9189 else if (val < 512*kb) {
9190 val = val / kb;
9191 snprintf(tmp, 64, "%.2f KB", val);
9192 }
9193 else if (val < 512*mb) {
9194 val = val / mb;
9195 snprintf(tmp, 64, "%.2f MB", val);
9196 }
9197 else if (val < 512*gb) {
9198 val = val / gb;
9199 snprintf(tmp, 64, "%.2f GB", val);
9200 }
9201 else {
9202 val = val / tb;
9203 snprintf(tmp, 64, "%.2f TB", val);
9204 }
9205
9206 std::string result = tmp;
9207 delete [] tmp;
9208 return result;
9209}
9210
9211//std::string BytifySizes(int32 val1, int32 val2) {
9212// return BytifySizes((double)val1, (double) val2);
9213//}
9214
9215std::string BytifySizes(double val1, double val2) {
9216
9217 double kb = 1024;
9218 double mb = kb*1024;
9219 double gb = mb*1024;
9220 double tb = gb*1024;
9221
9222 char *tmp = new char[64];
9223 char *end = new char[64];
9224
9225 double val;
9226 double maximum = ((val1 > val2) ? val1 : val2);
9227
9228 if (maximum < 512) {
9229 val = 1;
9230 utils::strcpyavail(end, "B", 32, true);
9231 }
9232 else if (maximum < 512*kb) {
9233 val = kb;
9234 utils::strcpyavail(end, "KB", 32, true);
9235 }
9236 else if (maximum < 512*mb) {
9237 val = mb;
9238 utils::strcpyavail(end, "MB", 32, true);
9239 }
9240 else if (maximum < 512*gb) {
9241 val = gb;
9242 utils::strcpyavail(end, "GB", 32, true);
9243 }
9244 else {
9245 val = gb;
9246 utils::strcpyavail(end, "TB", 32, true);
9247 }
9248 snprintf(tmp, 64, "%.2f / %.2f %s", ((double)val1)/val, ((double)val2)/val, (char*) end);
9249
9250 std::string result = tmp;
9251 delete [] tmp;
9252 delete [] end;
9253 return result;
9254
9255}
9256
9257//std::string BytifyRate(int32 val) {
9258// return BytifyRate((double)val);
9259//}
9260
9261std::string BytifyRate(double val) {
9262 char *tmp = new char[64];
9263 snprintf(tmp, 64, "%s/sec", (char*) BytifySize(val).c_str());
9264 std::string result = tmp;
9265 delete [] tmp;
9266 return result;
9267}
9268
9269//std::string BytifyRates(int32 val1, int32 val2) {
9270// return BytifyRates((double)val1, (double)val2);
9271//}
9272
9273std::string BytifyRates(double val1, double val2) {
9274 char *tmp = new char[64];
9275 snprintf(tmp, 64, "%s/sec", (char*) BytifySizes(val1, val2).c_str());
9276 std::string result = tmp;
9277 delete [] tmp;
9278 return result;
9279}
9280
9281
9282
9283
9284
9285
9286
9287#ifdef WINDOWS
9288
9289uint32 ReadRegistryDWORD(const char* key, const char* entry) {
9290 HKEY hKeyRoot, hKey;
9291 std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);
9292
9293 uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
9294 if (err != ERROR_SUCCESS)
9295 return 0;
9296
9297 DWORD dwType = REG_DWORD;
9298 DWORD numVal = 0;
9299 DWORD dwSize = sizeof(numVal);
9300
9301 err = RegQueryValueEx(hKey, entry, NULL, &dwType, (LPBYTE)&numVal, &dwSize);
9302 RegCloseKey(hKey);
9303 if (err != ERROR_SUCCESS)
9304 return 0;
9305 return numVal;
9306}
9307
9308uint64 ReadRegistryQWORD(const char* key, const char* entry) {
9309 HKEY hKeyRoot, hKey;
9310 std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);
9311
9312 uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
9313 if (err != ERROR_SUCCESS)
9314 return 0;
9315
9316 DWORD dwType = REG_QWORD;
9317 uint64 numVal = 0;
9318 DWORD dwSize = sizeof(numVal);
9319
9320 err = RegQueryValueEx(hKey, entry, NULL, &dwType, (LPBYTE)&numVal, &dwSize);
9321 RegCloseKey(hKey);
9322 if (err != ERROR_SUCCESS)
9323 return 0;
9324 return numVal;
9325}
9326
9327std::string ReadRegistryString(const char* key, const char* entry) {
9328 HKEY hKeyRoot, hKey;
9329 std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);
9330
9331 uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
9332 if (err != ERROR_SUCCESS) {
9333 return "";
9334 }
9335
9336 DWORD dwType = REG_SZ;
9337 DWORD dwSize = 4096;
9338 char* lszValue = new char[dwSize];
9339
9340 err = RegQueryValueEx(hKey, entry, NULL, &dwType, (LPBYTE)lszValue, &dwSize);
9341 RegCloseKey(hKey);
9342 if (err != ERROR_SUCCESS) {
9343 delete[] lszValue;
9344 return "";
9345 }
9346
9347 std::string val = lszValue;
9348 delete[] lszValue;
9349 return val;
9350}
9351
9352bool WriteRegistryDWORD(const char* key, const char* entry, uint32 value) {
9353 DWORD dwDisposition;
9354 HKEY hKeyRoot, hKey;
9355 std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);
9356
9357 uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_WRITE | KEY_WOW64_64KEY, &hKey);
9358 if (err != ERROR_SUCCESS) {
9359 err = RegCreateKeyEx(hKeyRoot, keySubName.c_str(), 0, NULL, 0, KEY_WRITE | KEY_WOW64_64KEY, NULL, &hKey, &dwDisposition);
9360 if (err != ERROR_SUCCESS)
9361 return false;
9362 }
9363
9364 DWORD dwType = REG_DWORD;
9365 err = RegSetValueEx(hKey, entry, 0, dwType, (const BYTE*)&value, sizeof(value));
9366 RegCloseKey(hKey);
9367 return (err == ERROR_SUCCESS);
9368}
9369
9370bool WriteRegistryQWORD(const char* key, const char* entry, uint64 value) {
9371 DWORD dwDisposition;
9372 HKEY hKeyRoot, hKey;
9373 std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);
9374
9375 uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_WRITE | KEY_WOW64_64KEY, &hKey);
9376 if (err != ERROR_SUCCESS) {
9377 err = RegCreateKeyEx(hKeyRoot, keySubName.c_str(), 0, NULL, 0, KEY_WRITE | KEY_WOW64_64KEY, NULL, &hKey, &dwDisposition);
9378 if (err != ERROR_SUCCESS)
9379 return false;
9380 }
9381
9382 DWORD dwType = REG_QWORD;
9383 err = RegSetValueEx(hKey, entry, 0, dwType, (const BYTE*)&value, sizeof(value));
9384 RegCloseKey(hKey);
9385 return (err == ERROR_SUCCESS);
9386}
9387
9388bool WriteRegistryString(const char* key, const char* entry, const char* value) {
9389 DWORD dwDisposition;
9390 HKEY hKeyRoot, hKey;
9391 std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);
9392
9393 uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_WRITE | KEY_WOW64_64KEY, &hKey);
9394 if (err != ERROR_SUCCESS) {
9395 err = RegCreateKeyEx(hKeyRoot, keySubName.c_str(), 0, NULL, 0, KEY_WRITE | KEY_WOW64_64KEY, NULL, &hKey, &dwDisposition);
9396 if (err != ERROR_SUCCESS)
9397 return false;
9398 }
9399
9400 DWORD dwType = REG_SZ;
9401 DWORD dwSize = 4096;
9402
9403 err = RegSetValueEx(hKey, entry, 0, dwType, (LPBYTE)value, (DWORD)(strlen(value) + 1));
9404 RegCloseKey(hKey);
9405 return (err == ERROR_SUCCESS);
9406}
9407
9408bool DeleteRegistryEntry(const char* key, const char* entry) {
9409 HKEY hKeyRoot, hKey;
9410 std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);
9411
9412 uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_SET_VALUE | KEY_WOW64_64KEY, &hKey);
9413 if (err != ERROR_SUCCESS)
9414 return false;
9415
9416 //std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);
9417 err = RegDeleteValue(hKey, entry);
9418 return (err == ERROR_SUCCESS);
9419}
9420
9421bool DeleteRegistryKey(const char* entry) {
9422 // https://msdn.microsoft.com/en-us/library/aa379776(VS.85).aspx
9423
9424 HKEY hKeyRoot;
9425 std::string keySubName = GetRegistryKeyInfo(entry, hKeyRoot);
9426 uint32 err = RegDeleteKeyEx(hKeyRoot, keySubName.c_str(), KEY_WOW64_64KEY, 0);
9427 return (err == ERROR_SUCCESS);
9428}
9429
9430bool DeleteRegistryTree(const char* root, const char* key) {
9431 if (!key || !strlen(key))
9432 return false;
9433
9434 HKEY hKey;
9435 HKEY hKeyRoot;
9436 std::string keySubName = GetRegistryKeyInfo(root, hKeyRoot);
9437 uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, DELETE | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_WOW64_64KEY, &hKey);
9438 if (err != ERROR_SUCCESS)
9439 return false;
9440
9441 err = RegDeleteTree(hKey, key);
9442 return (err == ERROR_SUCCESS);
9443}
9444
9445//bool CopyRegistryKey(const char* key, const char* newName) {
9446//}
9447
9448bool CopyRegistryTree(const char* entry, const char* newName) {
9449 // https://msdn.microsoft.com/en-us/library/aa379768(VS.85).aspx
9450
9451 // access 64 or 32-bit registry: https://msdn.microsoft.com/en-us/library/aa384129(v=vs.85).aspx
9452 // we always use the 64-bit registry here
9453
9454 DWORD dwDisposition;
9455 HKEY hKey;
9456 HKEY hKeyRoot;
9457 HKEY hKeyNew;
9458 HKEY hKeyNewRoot;
9459 uint32 err;
9460
9461 std::string keySubName = GetRegistryKeyInfo(entry, hKeyRoot);
9462 std::string keySubNewName = GetRegistryKeyInfo(newName, hKeyNewRoot);
9463
9464 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
9465 if (err != ERROR_SUCCESS)
9466 return false;
9467
9468 err = RegCreateKeyEx(hKeyNewRoot, keySubNewName.c_str(), 0, NULL, 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, NULL, &hKeyNew, &dwDisposition);
9469 if (err != ERROR_SUCCESS) {
9470 RegCloseKey(hKey);
9471 return false;
9472 }
9473
9474 err = RegCopyTree(hKey, NULL, hKeyNew);
9475
9476 RegCloseKey(hKey);
9477 RegCloseKey(hKeyNew);
9478
9479 if (err != ERROR_SUCCESS) {
9480 RegDeleteKeyEx(hKeyNewRoot, keySubNewName.c_str(), KEY_WOW64_64KEY, 0);
9481 return false;
9482 }
9483 return true;
9484}
9485
9486//bool RenameRegistryKey(const char* key, const char* newName) {
9487// if (!CopyRegistryKey(key, newName))
9488// return false;
9489// if (!DeleteRegistryKey(key)) {
9490// CopyRegistryKey(newName, key);
9491// return false;
9492// }
9493// return true;
9494//}
9495
9496//bool RenameRegistryTree(const char* entry, const char* newName) {
9497 //if (!CopyRegistryTree(entry, newName))
9498 // return false;
9499 //if (!DeleteRegistryTree(entry)) {
9500 // CopyRegistryTree(newName, entry);
9501 // return false;
9502 //}
9503// return true;
9504//}
9505
9506std::string GetRegistryKeyInfo(const char* key, HKEY &hKeyRoot) {
9507 std::vector<std::string> path = utils::TextListSplit(key, "\\");
9508 if (!path.size()) path = utils::TextListSplit(key, "/");
9509 if (path.size() < 2) return false;
9510 if (stristr(path.at(0).c_str(), "HKEY_CLASSES_ROOT"))
9511 hKeyRoot = HKEY_CLASSES_ROOT;
9512 else if (stristr(path.at(0).c_str(), "HKEY_CURRENT_USER"))
9513 hKeyRoot = HKEY_CURRENT_USER;
9514 else if (stristr(path.at(0).c_str(), "HKEY_LOCAL_MACHINE"))
9515 hKeyRoot = HKEY_LOCAL_MACHINE;
9516 else if (stristr(path.at(0).c_str(), "HKEY_USERS"))
9517 hKeyRoot = HKEY_USERS;
9518 return utils::TextJoin(path, "\\", 1, (DWORD)(path.size() - 1));
9519}
9520
9521std::string GetRegistryParentKeyInfo(const char* key, HKEY &hKeyRoot) {
9522 std::vector<std::string> path = utils::TextListSplit(key, "\\");
9523 if (!path.size()) path = utils::TextListSplit(key, "/");
9524 if (path.size() < 2) return false;
9525 if (stristr(path.at(0).c_str(), "HKEY_CLASSES_ROOT"))
9526 hKeyRoot = HKEY_CLASSES_ROOT;
9527 else if (stristr(path.at(0).c_str(), "HKEY_CURRENT_USER"))
9528 hKeyRoot = HKEY_CURRENT_USER;
9529 else if (stristr(path.at(0).c_str(), "HKEY_LOCAL_MACHINE"))
9530 hKeyRoot = HKEY_LOCAL_MACHINE;
9531 else if (stristr(path.at(0).c_str(), "HKEY_USERS"))
9532 hKeyRoot = HKEY_USERS;
9533 return utils::TextJoin(path, "\\", 1, (DWORD)(path.size() - 2));
9534}
9535
9536
9537#else // WINDOWS
9538 int32 ReadRegistryDWORD(const char* key) { return 0; }
9539 int64 ReadRegistryQWORD(const char* key) { return 0; }
9540 std::string ReadRegistryString(const char* key) { return ""; }
9541 bool WriteRegistryDWORD(const char* key, int32 value) { return false; }
9542 bool WriteRegistryQWORD(const char* key, int64 value) { return false; }
9543 bool WriteRegistryString(const char* key, const char* value) { return false; }
9544 bool DeleteRegistryKey(const char* key) { return false; }
9545 bool DeleteRegistryTree(const char* key) { return false; }
9546 //bool CopyRegistryKey(const char* key, const char* newName) { return false; }
9547 bool CopyRegistryTree(const char* entry, const char* newName) { return false; }
9548 //bool RenameRegistryKey(const char* key, const char* newName) { return false; }
9549 bool RenameRegistryTree(const char* entry, const char* newName) { return false; }
9550#endif // WINDOWS
9551
9552
9553
9554} // namespace utils
9555
9556// Adapters so the utils:: free-function tests can be registered as bool() pointers.
9557static bool Test_Utils() { return utils::UnitTest_Utils(); }
9559static bool Test_Timer() { return utils::UnitTest_Timer(); }
9560
9563 "Bitfield slot allocation: SetBit/SetBitN, first-free and last-occupied searches", "util");
9565 "Timer scheduling and trigger delivery via waitForTimer", "util");
9567 "ProbeProcessWait: stdin-blocked child detected, busy child not flagged, cpuTimeMs grows", "util");
9568}
9569
9570} // namespace cmlabs
9571
9572
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:8225
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:5896
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:5772
bool load(const char *filename)
Load a library.
Definition Utils.cpp:5803
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:5528
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:5338
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:8637
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:8694
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:5481
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:5412
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:5506
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:5059
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:8662
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:5568
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:7489
void CloseProcessTerminal(uint32 proc)
Close the master side (terminal hangup: the child's foreground group gets SIGHUP and any blocked term...
Definition Utils.cpp:5545
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:7454
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:5308
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:7528
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:5354
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:5557
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:8687
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:7412
#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:8626
uint32 strcpyavail(char *dst, const char *src, uint32 maxlen, bool copyAvailable)
Bounded strcpy that always NUL-terminates.
Definition Utils.cpp:7510
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:9126
int GetLastOSErrorNumber()
Get the last OS error number (errno / GetLastError()).
Definition Utils.cpp:6681
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:6691
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:7806
int8 CompareFloats(float64 a, float64 b)
Compare two doubles with epsilon tolerance.
Definition Utils.cpp:8214
uint32 * GetLocalIPAddresses(uint32 &count)
List all local IPv4 addresses.
Definition Utils.cpp:7093
std::string EncodeJSON(std::string str)
Escape text for embedding as a JSON string value.
Definition Utils.cpp:9050
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:6526
uint64 GetProcessMemoryUsage()
Current resident memory usage of this process.
Definition Utils.cpp:5996
char ** SplitCommandline(const char *cmdline, int &argc)
Split a command line into a malloc'ed argv array.
Definition Utils.cpp:7691
uint64 ReadRegistryQWORD(const char *key, const char *entry)
Read a 64-bit registry value.
static std::map< uint32, int > * TerminalMasterMap
Definition Utils.cpp:5402
std::string GetCommandLineArg(uint16 n)
Get the n-th positional command line item.
Definition Utils.cpp:5731
bool CreateADir(const char *dirname)
Create a directory (parents included where supported).
Definition Utils.cpp:8507
std::string BytifyRates(double val1, double val2)
Format two byte rates as "x / y" with matching units.
Definition Utils.cpp:9273
bool CopyAFile(const char *oldfilename, const char *newfilename, bool force=false)
Copy a file.
Definition Utils.cpp:8459
bool DeleteFilesInADir(const char *dirname, bool force)
Delete all files inside a directory, leaving the directory itself.
Definition Utils.cpp:8600
uint32 GetCommandLineArgCount()
Number of positional command line items (excluding the executable).
Definition Utils.cpp:5725
uint16 GetCPUArchitecture()
CPU architecture id.
Definition Utils.cpp:6087
char OSLocalHostName[1024]
Definition Utils.cpp:5945
char OSArchitectureName[1024]
Definition Utils.cpp:5946
std::string ReadAFileString(std::string filename)
Read an entire file into a std::string.
Definition Utils.cpp:8251
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:8020
std::string TextUppercase(const char *text)
Convert to upper case (ASCII/current locale).
Definition Utils.cpp:7561
static std::condition_variable & gLaunchCv
Definition Utils.cpp:5021
int CRC32(const char *addr, uint32 length, int32 crc=0)
Compute (or continue) a CRC-32 checksum.
Definition Utils.cpp:8233
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:6739
uint64 * GetLocalMACAddresses(uint32 &count)
List all local MAC addresses.
Definition Utils.cpp:7019
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:6235
std::string TextTrim(const char *text)
Strip leading and trailing whitespace.
Definition Utils.cpp:7623
std::string GetCommandLinePath()
Get the directory portion of the executable path.
Definition Utils.cpp:5685
bool HasCommandLineArg(const char *key, std::string &value)
Test for a 'key=value' argument without mutating the argument map.
Definition Utils.cpp:5743
bool SetSocketBlockingMode(SOCKET s)
Put a socket into blocking mode.
Definition Utils.cpp:6788
uint32 Ascii2Uint32(const char *ascii, uint32 start=0, uint32 end=0)
Parse an unsigned 32-bit decimal integer from a substring.
Definition Utils.cpp:8980
uint64 GetCPUSpeed()
Nominal CPU clock speed.
Definition Utils.cpp:6124
wchar_t ** SplitCommandlineW(const char *cmdline, int &argc)
Wide-character variant of SplitCommandline() (for Windows APIs).
Definition Utils.cpp:7711
std::string TextVectorConcat(std::vector< std::string >, const char *sep, bool allowEmpty=true)
Concatenate vector entries with sep.
Definition Utils.cpp:7993
bool AppendToAFile(const char *filename, const char *data, uint32 length, bool binary=false)
Append to a file, creating it if missing.
Definition Utils.cpp:8359
std::string TextIndent(const char *text, const char *indent)
Prefix every line of text with indent.
Definition Utils.cpp:7573
unsigned char Dec2Char(const char *str)
Parse up to three decimal digits into a byte.
Definition Utils.cpp:8206
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:8747
bool GetNextAvailableLocalPort(uint16 lastPort, uint16 &nextPort)
Find the next free TCP port above lastPort.
Definition Utils.cpp:7364
static std::map< const void *, bool > * g_cancelSafeSet
Definition Utils.cpp:979
static std::mutex & gLaunchMx
Definition Utils.cpp:5020
bool MoveAFile(const char *oldfilename, const char *newfilename, bool force=false)
Move/rename a file.
Definition Utils.cpp:8411
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:8116
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:7965
bool IsTextNumeric(const char *ascii, uint32 start=0, uint32 end=0)
Test whether a (sub)string is a valid number.
Definition Utils.cpp:8904
uint16 GetCPUCount()
Number of logical CPU cores.
Definition Utils.cpp:6068
char * Uint2Ascii(uint64 value, char *result, uint16 size, uint8 base)
Render an unsigned integer as text in an arbitrary base.
Definition Utils.cpp:6502
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:7665
uint64 GetPeakProcessMemoryUsage()
Peak resident memory usage of this process.
Definition Utils.cpp:6028
bool DeleteCommandline(char **argv, int argc)
Free an argv array produced by SplitCommandline().
Definition Utils.cpp:7731
uint32 AsciiHex2Uint32(const char *ascii, uint32 start=0, uint32 end=0)
Parse an unsigned 32-bit hexadecimal integer from a substring.
Definition Utils.cpp:8990
bool DeleteRegistryKey(const char *key)
Delete a (leaf) registry key.
Definition Utils.cpp:9544
bool GetLocalHostname(char *name, uint32 maxSize)
Local hostname into a caller buffer.
Definition Utils.cpp:6947
std::string TextUnindent(const char *text)
Remove one level of leading indentation from every line.
Definition Utils.cpp:7584
bool GetSystemOSVersion(uint16 &major, uint16 &minor, uint16 &build, char *text, uint16 textSize)
Detailed OS version.
Definition Utils.cpp:6324
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:8150
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:7947
bool SetCommandLine(int argc, char *argv[])
Parse and store the process command line for later retrieval by the Get/HasCommandLine* functions.
Definition Utils.cpp:5638
uint32 StringSingleReplace(std::string &text, std::string key, std::string value, bool onlyFirst)
Replace occurrences of key with value in text.
Definition Utils.cpp:8098
uint64 GetSystemMemorySize()
Total physical RAM installed.
Definition Utils.cpp:6171
uint32 ReadRegistryDWORD(const char *key, const char *entry)
Read a 32-bit registry value.
static bool TerminalMasterFind(uint32 proc, int &fd)
Definition Utils.cpp:5403
static ShutdownSignalCallback g_shutdownCallback
Definition Utils.cpp:3410
bool GetLocalMACAddress(uint64 &address)
Get the primary local MAC address.
Definition Utils.cpp:7007
bool WaitForSocketWriteability(SOCKET s, int32 timeout)
Wait until a socket can be written without blocking.
Definition Utils.cpp:6707
FileDetails GetFileDetails(const char *filename)
Stat a file.
Definition Utils.cpp:8818
double RandomValue()
Uniform random double in [0,1).
Definition Utils.cpp:9132
bool GetSocketError(int wsaError, char *errorString, uint16 errorStringMaxSize, bool *isRecoverable)
Translate a socket error code into text and classify it.
Definition Utils.cpp:6545
std::string GetCommandLineExecutableOnly()
Get just the executable filename without path.
Definition Utils.cpp:5708
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:7638
bool RenameRegistryTree(const char *entry, const char *newName)
Definition Utils.cpp:9549
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:7061
bool LookupHostname(uint32 address, char *name, uint32 maxSize)
Reverse-resolve an IPv4 address to a hostname.
Definition Utils.cpp:6879
char * TextSubstringCopy(const char *ascii, uint32 start, uint32 end)
Copy the substring [start,end) into a new NUL-terminated buffer.
Definition Utils.cpp:8885
bool LookupIPAddress(const char *name, uint32 &address)
Resolve a hostname to an IPv4 address.
Definition Utils.cpp:6800
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:6475
std::string GetLocalHostnameString()
Local hostname as std::string.
Definition Utils.cpp:6935
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:8970
bool DoesAFileExist(const char *filename)
Test whether a regular file exists.
Definition Utils.cpp:8813
NetworkInterfaces * GetLocalInterfaces(uint32 &count)
Enumerate local network interfaces with address, MAC and names.
Definition Utils.cpp:7218
std::string TextTrimQuotes(const char *text)
Strip a single pair of surrounding quotes if present.
Definition Utils.cpp:7605
unsigned char Hex2Char(const char *str)
Parse two hex digits into a byte.
Definition Utils.cpp:8198
bool DeleteAFile(const char *filename, bool force)
Delete a file.
Definition Utils.cpp:8387
bool ChangeAFileAttr(const char *filename, bool read, bool write)
Change read/write permission attributes of a file.
Definition Utils.cpp:8391
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:9178
std::string GetCommandLineExecutable()
Get the full executable path as invoked.
Definition Utils.cpp:5702
bool StringFormatInto(char *dst, uint32 maxsize, const char *format,...)
printf into a caller-supplied buffer with truncation.
Definition Utils.cpp:8050
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:7788
bool WriteAFile(const char *filename, const char *data, uint32 length, bool binary=false)
Write (create/overwrite) a file.
Definition Utils.cpp:8331
std::string GetCurrentDir()
Current working directory.
Definition Utils.cpp:8734
std::string GetCommandLine()
Get the full original command line as one string.
Definition Utils.cpp:5679
bool CalcTimeout(struct timespec &timeout, uint32 ms)
Definition Utils.cpp:446
static std::mutex gLaunchSerialMx
Definition Utils.cpp:5024
std::string ReadRegistryString(const char *key, const char *entry)
Read a string registry value.
char OSName[1024]
Definition Utils.cpp:5947
std::vector< std::string > TextCommandlineSplit(const char *cmdline)
Split a command line into arguments, honouring quoting rules.
Definition Utils.cpp:7738
bool DoesADirExist(const char *dirname)
Test whether a directory exists.
Definition Utils.cpp:8808
bool DeleteRegistryEntry(const char *key, const char *entry)
Delete a single value from a key.
static bool gLauncherStarted
Definition Utils.cpp:5023
std::string StringFormat(const char *format,...)
printf into a std::string.
Definition Utils.cpp:8080
bool SetSocketNonBlockingMode(SOCKET s)
Put a socket into non-blocking mode.
Definition Utils.cpp:6776
bool DeleteADir(const char *dirname, bool force)
Delete a directory (recursively when force).
Definition Utils.cpp:8533
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:8272
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:8934
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:8952
const char * GetFileBasename(const char *filename)
Filename portion of a path.
Definition Utils.cpp:8718
static PosixLaunchRequest * gLaunchReq
Definition Utils.cpp:5022
Library * OpenLibrary(const char *libName)
Load a library by name, applying platform filename conventions.
Definition Utils.cpp:5931
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:6194
static THREAD_RET THREAD_FUNCTION_CALL ProcessLauncherRun(THREAD_ARG)
Definition Utils.cpp:5026
int64 Ascii2Int64(const char *ascii, uint32 start=0, uint32 end=0)
Parse a signed 64-bit decimal integer from a substring.
Definition Utils.cpp:8916
static pid_t SubmitLaunchRequest(PosixLaunchRequest &req)
Definition Utils.cpp:5040
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:7931
bool CopyRegistryTree(const char *entry, const char *newName)
Recursively copy a registry subtree.
Definition Utils.cpp:9547
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:7567
std::string DecodeJSON(std::string str)
Unescape a JSON string value (\" \\ \n \t \uXXXX etc.).
Definition Utils.cpp:9011
std::string TextCapitalise(const char *text)
Capitalise the first letter of text.
Definition Utils.cpp:7547
const char * GetComputerName()
Get this machine's hostname.
Definition Utils.cpp:5949
std::string GetFilePath(const char *filename)
Directory portion of a path.
Definition Utils.cpp:8702
static void SignalShutdownSigset(sigset_t *set)
Definition Utils.cpp:3399
const char * GetSystemOSName()
OS name string, e.g.
Definition Utils.cpp:6296
std::string BytifySizes(double val1, double val2)
Format two byte counts as "x / y" with matching units.
Definition Utils.cpp:9215
bool IsLocalIPAddress(const char *addr)
Test whether a textual address refers to this machine (loopback or a local interface).
Definition Utils.cpp:7034
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:9000
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:7884
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:9169
std::string BytifyRate(double val)
Format a byte rate, e.g.
Definition Utils.cpp:9261
static bool Test_Utils()
Definition Utils.cpp:9557
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:9558
uint32 CleanShutdownInstanceCount
Definition Utils.cpp:45
void Register_Utils_Tests()
Definition Utils.cpp:9561
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:9559
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