@@ -340,131 +340,15 @@ int rtapi_exit(int module_id)
340340
341341int rtapi_is_kernelspace () { return 0 ; }
342342
343- #ifdef __linux__
344- // detect_preempt_rt() inspects uname for the PREEMPT_RT marker. Used only
345- // for diagnostic warning at startup; callers must not gate behavior on
346- // the kernel string, since SCHED_FIFO on a PREEMPT_DYNAMIC kernel is still
347- // useful (better than SCHED_OTHER, worse than PREEMPT_RT).
348- static inline int detect_preempt_rt () {
349- struct utsname u ;
350- if (uname (& u ) < 0 ) return 0 ;
351- return strcasestr (u .version , "PREEMPT RT" ) != 0
352- || strcasestr (u .version , "PREEMPT_RT" ) != 0 ;
353- }
354- #else
355- static inline int detect_preempt_rt () {
356- return 0 ;
357- }
358- #endif
359-
360- // FIXME: detect_rtai/detect_xenomai/detect_xenomai_evl currently gate on
361- // setuid because the RTAI/Xenomai backends still need root for iopl()
362- // (RTAI) or RTDM device access (Xenomai/EVL). Long-term these should
363- // probe the actual capability the way can_set_sched_fifo() does, paired
364- // with udev rules + a 'xenomai'/'evl' group; @hdiethelm has a follow-up
365- // planned. Until then, an unprivileged user on a Xenomai kernel cannot
366- // claim the Xenomai backend, and falls back to the SCHED_FIFO probe.
367- static inline int has_setuid_root () {
368- return geteuid () == 0 ;
369- }
370-
371- #ifdef USPACE_RTAI
372- static int detect_rtai () {
373- if (!has_setuid_root ()) return 0 ;
374- struct utsname u ;
375- uname (& u );
376- return strcasestr (u .release , "-rtai" ) != 0 ;
377- }
378- #else
379- static int detect_rtai () {
380- return 0 ;
381- }
382- #endif
383- #ifdef USPACE_XENOMAI
384- static int detect_xenomai () {
385- if (!has_setuid_root ()) return 0 ;
386- struct stat sb ;
387- //Running xenomai has /proc/xenomai
388- return stat ("/proc/xenomai" , & sb ) == 0 ;
389- }
390- #else
391- static int detect_xenomai () {
392- return 0 ;
393- }
394- #endif
395- #ifdef USPACE_XENOMAI_EVL
396- static int detect_xenomai_evl () {
397- if (!has_setuid_root ()) return 0 ;
398- struct stat sb ;
399- //Running xenomai evl has /dev/evl but no /proc/xenomai
400- return stat ("/dev/evl" , & sb ) == 0 ;
401- }
402- #else
403- static int detect_xenomai_evl () {
343+ #ifndef RTAPI
344+ //For RTAPI, this function is implemented in uspace_rtapi_main.cc
345+ //For user components, keep it in for now with a warning to avoid link issues
346+ int rtapi_is_realtime () {
347+ rtapi_print_msg (RTAPI_MSG_ERR , "rtapi_is_realtime() only allowed in real time context" );
404348 return 0 ;
405349}
406350#endif
407351
408- // errno from the most recent sched_setscheduler(SCHED_FIFO) probe. Zero
409- // when the probe succeeded or has not run yet. Read via
410- // rtapi_sched_fifo_errno() from diagnostic code.
411- static int rtapi_sched_fifo_last_errno = 0 ;
412-
413- // Success-probe for realtime scheduling: briefly try to set SCHED_FIFO on
414- // the calling thread and restore the previous policy. Succeeds when the
415- // process holds CAP_SYS_NICE (file caps or setuid root) or has a matching
416- // RLIMIT_RTPRIO. Works on any kernel, so the probe also covers the
417- // PREEMPT_RT-vs-stock distinction implicitly: if we can actually get
418- // SCHED_FIFO, the platform can deliver realtime, regardless of how.
419- static int can_set_sched_fifo (void ) {
420- struct sched_param old_param , probe_param ;
421- int old_policy = sched_getscheduler (0 );
422- if (old_policy < 0 ) {
423- rtapi_sched_fifo_last_errno = errno ;
424- return 0 ;
425- }
426- if (sched_getparam (0 , & old_param ) < 0 ) {
427- rtapi_sched_fifo_last_errno = errno ;
428- return 0 ;
429- }
430-
431- memset (& probe_param , 0 , sizeof (probe_param ));
432- probe_param .sched_priority = sched_get_priority_min (SCHED_FIFO );
433- if (sched_setscheduler (0 , SCHED_FIFO , & probe_param ) < 0 ) {
434- rtapi_sched_fifo_last_errno = errno ;
435- return 0 ;
436- }
437-
438- // Best-effort restore; if this fails we are still on SCHED_FIFO at
439- // minimum priority, which is no worse than where we started.
440- sched_setscheduler (0 , old_policy , & old_param );
441- rtapi_sched_fifo_last_errno = 0 ;
442- return 1 ;
443- }
444-
445- static inline int rtapi_sched_fifo_errno (void ) { return rtapi_sched_fifo_last_errno ; }
446-
447- // rtapi_is_realtime() reports whether this process can actually run
448- // realtime code. This matches the convention used by JACK, PipeWire,
449- // rtkit, Xenomai, and Klipper: surface the observed capability, not
450- // kernel metadata. The old setuid-root stat check has been removed; it
451- // stat()ed EMC2_BIN_DIR/rtapi_app rather than the running binary (breaking
452- // wrapper-based installs like NixOS /run/wrappers) and silently masked
453- // LINUXCNC_FORCE_REALTIME (see issue #3928).
454- int rtapi_is_realtime () {
455- static int cached = -1 ;
456- if (cached != -1 ) return cached ;
457-
458- const char * force = getenv ("LINUXCNC_FORCE_REALTIME" );
459- if (force != NULL && atoi (force ) != 0 )
460- return (cached = 1 );
461-
462- if (detect_rtai () || detect_xenomai () || detect_xenomai_evl ())
463- return (cached = 1 );
464-
465- return (cached = can_set_sched_fifo ());
466- }
467-
468352/* Like clock_nanosleep, except that an optional 'estimate of now' parameter may
469353 * optionally be passed in. This is a very slight optimization for platforms
470354 * where rtapi_clock_nanosleep is implemented in terms of nanosleep, because it
0 commit comments