From ae4d5fe03d623e721db9331d2332999bbfc10fcd Mon Sep 17 00:00:00 2001 From: Hannes Diethelm Date: Sun, 7 Jun 2026 01:11:28 +0200 Subject: [PATCH 1/5] rtapi_app: start command / do not autostart This gives rtapi_app a deterministic behaivour. No other changes needed exept implementing start / stop in realtime due to all apps run: realtime start at startup realtime stop at exit --- scripts/realtime.in | 22 +++++++++----- src/rtapi/uspace_rtapi_main.cc | 54 +++++++++++++++++++--------------- 2 files changed, 44 insertions(+), 32 deletions(-) diff --git a/scripts/realtime.in b/scripts/realtime.in index 286d57e0aee..036fde1353f 100644 --- a/scripts/realtime.in +++ b/scripts/realtime.in @@ -170,14 +170,20 @@ CheckMem(){ Load(){ CheckKernel - for MOD in $MODULES_LOAD ; do - if ! [ -d "/sys/module/$(basename "$MOD" .ko)" ]; then - $INSMOD "$MOD" || return $? - fi - done - if [ "$DEBUG" != "" ] && [ -w /proc/rtapi/debug ] ; then - echo "$DEBUG" > /proc/rtapi/debug - fi + case $RTPREFIX in + uspace) + rtapi_app start + ;; + (*rtai*) + for MOD in $MODULES_LOAD ; do + if ! [ -d "/sys/module/$(basename "$MOD" .ko)" ]; then + $INSMOD "$MOD" || return $? + fi + done + if [ "$DEBUG" != "" ] && [ -w /proc/rtapi/debug ] ; then + echo "$DEBUG" > /proc/rtapi/debug + fi + esac } CheckLoaded(){ diff --git a/src/rtapi/uspace_rtapi_main.cc b/src/rtapi/uspace_rtapi_main.cc index 3f9bda20ecb..aa761b5a977 100644 --- a/src/rtapi/uspace_rtapi_main.cc +++ b/src/rtapi/uspace_rtapi_main.cc @@ -772,7 +772,10 @@ static int handle_command(const std::vector &args) { if (args.size() == 0) { return 0; } - if (args.size() == 1 && args[0] == "exit") { + if (args.size() == 1 && args[0] == "start") { + rtapi_print_msg(RTAPI_MSG_ERR, "rtapi_app: start received while running\n"); + return 0; + } else if (args.size() == 1 && args[0] == "exit") { force_exit = 1; return 0; } else if (args.size() >= 2 && args[0] == "load") { @@ -790,7 +793,7 @@ static int handle_command(const std::vector &args) { } else if (args.size() == 1 && args[0] == "check_rt") { return do_check_rt_cmd(); } else { - rtapi_print_msg(RTAPI_MSG_ERR, "Unrecognized command starting with %s\n", args[0].c_str()); + rtapi_print_msg(RTAPI_MSG_ERR, "rtapi_app: unrecognized command starting with %s\n", args[0].c_str()); return -1; } } @@ -849,12 +852,12 @@ static bool master_process_socket_command(int fd) { } close(fd1); } - return !force_exit && instance_count > 0; + return !force_exit; } static pthread_t main_thread{}; -static int master(int fd, const std::vector &args) { +static int master(int fd) { is_master = true; main_thread = pthread_self(); int result; @@ -864,18 +867,9 @@ static int master(int fd, const std::vector &args) { return -1; } do_load_cmd("hal_lib", std::vector()); - instance_count = 0; App(); // force rtapi_app to be created - if (args.size()) { - result = handle_command(args); - if (result != 0) - goto out; - if (force_exit || instance_count == 0) - goto out; - } //Process commands as long as master should not exit while(master_process_socket_command(fd)); -out: do_unload_cmd("hal_lib"); pthread_cancel(queue_thread); pthread_join(queue_thread, nullptr); @@ -966,7 +960,6 @@ int main(int argc, char **argv) { args.push_back(std::string(argv[i])); } -become_master: int fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd == -1) { perror("socket"); @@ -986,9 +979,9 @@ int main(int argc, char **argv) { int result = bind(fd, (sockaddr *)&addr, sizeof(addr)); if (result == 0) { - //If exit is called and master is not running, do not start master - //and exit again + //If exit is called and master is not running, just give a warning if (args.size() == 1 && args[0] == "exit") { + rtapi_print_msg(RTAPI_MSG_ERR, "rtapi_app: exit received while not running\n"); return 0; } //If check_rt is called and master is not running, do not start master @@ -999,14 +992,27 @@ int main(int argc, char **argv) { if (args.size() == 1 && args[0] == "check_rt") { return do_check_rt_cmd(); } - int result = listen(fd, 10); - if (result != 0) { - perror("listen"); - exit(1); + //Start a master on start command + if (args.size() == 1 && args[0] == "start") { + int result = listen(fd, 10); + if (result != 0) { + perror("listen"); + exit(1); + } + //Demonize + pid_t pid = fork(); + if (pid < 0){ + perror("fork"); + exit(1); + } + if(pid == 0){ + setsid(); // create a new session if we can... + result = master(fd); + } + return result; } - setsid(); // create a new session if we can... - result = master(fd, args); - return result; + fprintf(stderr, "error: No master found. Use realtime start to start one.\n"); + exit(1); } else if (errno == EADDRINUSE) { struct timespec start, now; clock_gettime(CLOCK_MONOTONIC, &start); @@ -1023,7 +1029,7 @@ int main(int argc, char **argv) { if (result < 0 && errno == ECONNREFUSED) { fprintf(stderr, "Waited 3 seconds for master. giving up.\n"); close(fd); - goto become_master; + exit(1); } if (result < 0) { fprintf(stderr, "connect %s: %s", addr.sun_path, strerror(errno)); From 8ee1cb281f363ac04ac49015153fa164eedea78c Mon Sep 17 00:00:00 2001 From: Hannes Diethelm Date: Mon, 15 Jun 2026 22:51:46 +0200 Subject: [PATCH 2/5] rtapi_app no autostart: Fix tests --- tests/hal-show/expected | 24 ++++++++++++------------ tests/raster/test | 3 ++- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/hal-show/expected b/tests/hal-show/expected index e4bce169e35..7ecb4551e21 100644 --- a/tests/hal-show/expected +++ b/tests/hal-show/expected @@ -1,11 +1,11 @@ Component Pins: Owner Type Dir Value Name - 8 bit IN FALSE conv-bit-u32.0.in <== net-conv-bit-u32.0.in - 14 float IN 0 conv-float-s32.0.in <== net-conv-float-s32.0.in - 17 s32 IN 0 conv-s32-float.0.in <== net-conv-s32-float.0.in - 20 s64 IN 0 conv-s64-u64.0.in <== net-conv-s64-u64.0.in - 11 u32 IN 0x00000000 conv-u32-bit.0.in <== net-conv-u32-bit.0.in - 23 u64 IN 0x0000000000000000 conv-u64-s64.0.in <== net-conv-u64-s64.0.in + 10 bit IN FALSE conv-bit-u32.0.in <== net-conv-bit-u32.0.in + 16 float IN 0 conv-float-s32.0.in <== net-conv-float-s32.0.in + 19 s32 IN 0 conv-s32-float.0.in <== net-conv-s32-float.0.in + 22 s64 IN 0 conv-s64-u64.0.in <== net-conv-s64-u64.0.in + 13 u32 IN 0x00000000 conv-u32-bit.0.in <== net-conv-u32-bit.0.in + 25 u64 IN 0x0000000000000000 conv-u64-s64.0.in <== net-conv-u64-s64.0.in Signals: Type Value Name (linked to) @@ -36,12 +36,12 @@ s64 0 net-conv-u64-s64.0.out Component Pins: Owner Type Dir Value Name - 8 bit IN TRUE conv-bit-u32.0.in <== net-conv-bit-u32.0.in - 14 float IN 2.147484e+09 conv-float-s32.0.in <== net-conv-float-s32.0.in - 17 s32 IN -2147483648 conv-s32-float.0.in <== net-conv-s32-float.0.in - 20 s64 IN -9223372036854775808 conv-s64-u64.0.in <== net-conv-s64-u64.0.in - 11 u32 IN 0xFFFFFFFF conv-u32-bit.0.in <== net-conv-u32-bit.0.in - 23 u64 IN 0xFFFFFFFFFFFFFFFF conv-u64-s64.0.in <== net-conv-u64-s64.0.in + 10 bit IN TRUE conv-bit-u32.0.in <== net-conv-bit-u32.0.in + 16 float IN 2.147484e+09 conv-float-s32.0.in <== net-conv-float-s32.0.in + 19 s32 IN -2147483648 conv-s32-float.0.in <== net-conv-s32-float.0.in + 22 s64 IN -9223372036854775808 conv-s64-u64.0.in <== net-conv-s64-u64.0.in + 13 u32 IN 0xFFFFFFFF conv-u32-bit.0.in <== net-conv-u32-bit.0.in + 25 u64 IN 0xFFFFFFFFFFFFFFFF conv-u64-s64.0.in <== net-conv-u64-s64.0.in Signals: Type Value Name (linked to) diff --git a/tests/raster/test b/tests/raster/test index af44c73404c..26d54b2b4a4 100755 --- a/tests/raster/test +++ b/tests/raster/test @@ -169,6 +169,7 @@ def testProgram(prog, pin, pos, offset, bpp, dpu, program, data): def main(): try: + os.system(os.environ['REALTIME'] + " start"); c = hal.component("test") pin = { @@ -252,7 +253,7 @@ def main(): finally: c.exit() prog.exit() - os.system('halrun -U') + os.system('halrun -U') #Calls also realtime stop return 0 if __name__ == "__main__": From 78137415754f47f2ba4bd888b09800c1ddf3ed39 Mon Sep 17 00:00:00 2001 From: Hannes Diethelm Date: Sun, 28 Jun 2026 16:11:16 +0200 Subject: [PATCH 3/5] rtapi_app no autostart: Fix raster/test the proper way Use subprocess.Popen to start halrun and exit at the end. --- tests/raster/test | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/tests/raster/test b/tests/raster/test index 26d54b2b4a4..fad3f74d7bd 100755 --- a/tests/raster/test +++ b/tests/raster/test @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import hal import time -import os +import subprocess from raster import * theta = 0.005 @@ -169,7 +169,6 @@ def testProgram(prog, pin, pos, offset, bpp, dpu, program, data): def main(): try: - os.system(os.environ['REALTIME'] + " start"); c = hal.component("test") pin = { @@ -194,10 +193,11 @@ def main(): prog = RasterProgrammer("programmer") - #instantiate the raster component, - #add it to a thread and link all signals from - #the test component here - assert os.system('halcmd -f raster.hal') == 0, "raster.hal script failed" + #instantiate the raster component + #use interactive mode to be have the hal running + #while needed and exit with writing "exit" at the end + halrun = subprocess.Popen(["halrun", "-Is", "raster.hal"], stdin=subprocess.PIPE) + time.sleep(0.5) #Needs a short delay until halrun is up and running testInvalidOffset(prog, pin) testInvalidBPP(prog, pin) @@ -253,7 +253,21 @@ def main(): finally: c.exit() prog.exit() - os.system('halrun -U') #Calls also realtime stop + try: + try: + halrun.communicate(input=b"exit", timeout=15) + except TimeoutExpired: + halrun.kill() + halrun.communicate() + exit_status = halrun.returncode + if exit_status == 0: + return 0 + else: + print("Halrun failed, exit status " + str(exit_status)) + return 1 + except Exception as e: + print("error: Test failed: {}".format(traceback.format_exc())) + return 1 return 0 if __name__ == "__main__": From 9fab450a436a313e19fdbf9fa43ecd6de17852f8 Mon Sep 17 00:00:00 2001 From: Hannes Diethelm Date: Sun, 28 Jun 2026 19:09:58 +0200 Subject: [PATCH 4/5] rtapi_app: Add deprecated master autostart --- src/rtapi/uspace_rtapi_main.cc | 120 +++++++++++++++++++++------------ 1 file changed, 76 insertions(+), 44 deletions(-) diff --git a/src/rtapi/uspace_rtapi_main.cc b/src/rtapi/uspace_rtapi_main.cc index aa761b5a977..24a89109e4d 100644 --- a/src/rtapi/uspace_rtapi_main.cc +++ b/src/rtapi/uspace_rtapi_main.cc @@ -922,6 +922,65 @@ static double diff_timespec(const struct timespec *time1, const struct timespec static void raise_net_admin_ambient(void); #endif +static int create_socket(){ + int fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (fd == -1) { + perror("socket"); + return fd; + } + + int enable = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)); + return fd; +} + +static int start_master(int fd){ + int result = listen(fd, 10); + if (result != 0) { + perror("listen"); + return 1; + } + //Demonize + pid_t pid = fork(); + if (pid < 0){ + perror("fork"); + return 1; + } + if(pid == 0){ + setsid(); // create a new session if we can... + result = master(fd); + exit(result); + }else{ + return 0; + } +} + +static int run_slave_cmd(struct sockaddr_un *addr, int fd, const std::vector &args){ + int result = -1; + struct timespec start, now; + clock_gettime(CLOCK_MONOTONIC, &start); + clock_gettime(CLOCK_MONOTONIC, &now); + srand48(start.tv_sec ^ start.tv_nsec); + while (diff_timespec(&now, &start) < 3.0) { + result = connect(fd, (sockaddr *)addr, sizeof(*addr)); + if (result == 0) + break; + + usleep((useconds_t)(lrand48() % 100000) + 100); //Random sleep min 100us max 100100us + clock_gettime(CLOCK_MONOTONIC, &now); + } + if (result < 0 && errno == ECONNREFUSED) { + fprintf(stderr, "Waited 3 seconds for master. giving up.\n"); + close(fd); + return 1; + } + if (result < 0) { + fprintf(stderr, "connect %s: %s", addr->sun_path, strerror(errno)); + return 1; + } + return slave(fd, args); +} + int main(int argc, char **argv) { if (getuid() == 0) { char *fallback_uid_str = getenv("RTAPI_UID"); @@ -960,20 +1019,17 @@ int main(int argc, char **argv) { args.push_back(std::string(argv[i])); } - int fd = socket(PF_UNIX, SOCK_STREAM, 0); - if (fd == -1) { - perror("socket"); - exit(1); - } - - int enable = 1; - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)); struct sockaddr_un addr; memset(&addr, 0x0, sizeof(addr)); addr.sun_family = AF_UNIX; if (!get_fifo_path_to_addr(&addr)) exit(1); + int fd = create_socket(); + if (fd < 0) { + exit(1); + } + // plus one because we use the abstract namespace, it will show up in // /proc/net/unix prefixed with an @ int result = bind(fd, (sockaddr *)&addr, sizeof(addr)); @@ -994,48 +1050,24 @@ int main(int argc, char **argv) { } //Start a master on start command if (args.size() == 1 && args[0] == "start") { - int result = listen(fd, 10); + result = start_master(fd); + exit(result); + }else{ + fprintf(stderr, "WARNING: Deprecated: No master found. Use realtime start to start one.\n" + " A master is started automaticaly.\n"); + result = start_master(fd); if (result != 0) { - perror("listen"); - exit(1); + exit(result); } - //Demonize - pid_t pid = fork(); - if (pid < 0){ - perror("fork"); + close(fd); //Need to close the socket, it is already bound and master is using it + int fd = create_socket(); + if (fd < 0) { exit(1); } - if(pid == 0){ - setsid(); // create a new session if we can... - result = master(fd); - } - return result; + return run_slave_cmd(&addr, fd, args); } - fprintf(stderr, "error: No master found. Use realtime start to start one.\n"); - exit(1); } else if (errno == EADDRINUSE) { - struct timespec start, now; - clock_gettime(CLOCK_MONOTONIC, &start); - clock_gettime(CLOCK_MONOTONIC, &now); - srand48(start.tv_sec ^ start.tv_nsec); - while (diff_timespec(&now, &start) < 3.0) { - result = connect(fd, (sockaddr *)&addr, sizeof(addr)); - if (result == 0) - break; - - usleep((useconds_t)(lrand48() % 100000) + 100); //Random sleep min 100us max 100100us - clock_gettime(CLOCK_MONOTONIC, &now); - } - if (result < 0 && errno == ECONNREFUSED) { - fprintf(stderr, "Waited 3 seconds for master. giving up.\n"); - close(fd); - exit(1); - } - if (result < 0) { - fprintf(stderr, "connect %s: %s", addr.sun_path, strerror(errno)); - exit(1); - } - return slave(fd, args); + return run_slave_cmd(&addr, fd, args); } else { perror("bind"); exit(1); From 3e1db6838b5514515220475858c803fc847bf7fe Mon Sep 17 00:00:00 2001 From: Hannes Diethelm Date: Mon, 29 Jun 2026 21:47:17 +0200 Subject: [PATCH 5/5] rtapi_app no autostart: Fix raster/test the proper way part 2 --- tests/raster/test | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/raster/test b/tests/raster/test index fad3f74d7bd..07eedac263b 100755 --- a/tests/raster/test +++ b/tests/raster/test @@ -197,7 +197,15 @@ def main(): #use interactive mode to be have the hal running #while needed and exit with writing "exit" at the end halrun = subprocess.Popen(["halrun", "-Is", "raster.hal"], stdin=subprocess.PIPE) - time.sleep(0.5) #Needs a short delay until halrun is up and running + #Test the last connection added in raster.hal + #to determine if hal is up and running + deadline = time.time() + 5 + while time.time() < deadline: + if hal.pin_has_writer("test.fraction"): + break + time.sleep(0.01) + else: + raise RuntimeError("raster.hal did not come up within 5s") testInvalidOffset(prog, pin) testInvalidBPP(prog, pin) @@ -255,8 +263,8 @@ def main(): prog.exit() try: try: - halrun.communicate(input=b"exit", timeout=15) - except TimeoutExpired: + halrun.communicate(input=b"exit\n", timeout=5) + except subprocess.TimeoutExpired: halrun.kill() halrun.communicate() exit_status = halrun.returncode