static int ulapi_load(rtapi_switch_t **ulapi_switch) { int retval; const char *errmsg; rtapi_get_handle_t rtapi_get_handle; char ulapi_lib_fname[PATH_MAX]; char *instance = getenv("INSTANCE"); char *debug_env = getenv("ULAPI_DEBUG"); int size = 0; int globalkey; // set the rtapi_instance global for this hal library instance if (instance != NULL) rtapi_instance = atoi(instance); if (debug_env) ulapi_debug = atoi(debug_env); rtapi_set_msg_level(ulapi_debug); // tag message origin field rtapi_set_logtag("ulapi"); // first thing is to attach the global segment, based on // the RTAPI instance id. This will contain the flavor // this ULAPI HAL instance is to run with. // Also, it's the prerequisite for common error message // handling through the message ringbuffer; unless then // error messages will go to stderr. // the global segment is attached once here per ULAPI instance; // it's address is passed to the ulapi-<flavor>.so module once loaded. // init the common shared memory driver APU shm_common_init(); globalkey = OS_KEY(GLOBAL_KEY, rtapi_instance); retval = shm_common_new(globalkey, &size, rtapi_instance, (void **) &global_data, 0); if (retval == -ENOENT) { // the global_data segment does not exist. Happens if the realtime // script was not started rtapi_print_msg(RTAPI_MSG_ERR, "ULAPI:%d ERROR: realtime not started\n", rtapi_instance); return retval; } if (retval < 0) { // some other error attaching global rtapi_print_msg(RTAPI_MSG_ERR, "ULAPI:%d ERROR: shm_common_new() failed key=0x%x %s\n", rtapi_instance, globalkey, strerror(-retval)); return retval; } if (size != sizeof(global_data_t)) { rtapi_print_msg(RTAPI_MSG_ERR, "ULAPI:%d ERROR: global segment size mismatch," " expected: %zd, actual:%d\n", rtapi_instance, sizeof(global_data_t), size); return -EINVAL; } if (global_data->magic != GLOBAL_READY) { rtapi_print_msg(RTAPI_MSG_ERR, "ULAPI:%d ERROR: global segment invalid magic:" " expected: 0x%x, actual: 0x%x\n", rtapi_instance, GLOBAL_READY, global_data->magic); return -EINVAL; } // global data set up ok // obtain handle on flavor descriptor as detected by rtapi_msgd flavor = flavor_byid(global_data->rtapi_thread_flavor); if (flavor == NULL) { rtapi_print_msg(RTAPI_MSG_ERR, "HAL_LIB:%d BUG - invalid flavor id: %d\n", rtapi_instance, global_data->rtapi_thread_flavor); return -EINVAL; } snprintf(ulapi_lib_fname,PATH_MAX,"%s/%s-%s%s", EMC2_RTLIB_DIR, ulapi_lib, flavor->name, flavor->so_ext); // dynload the proper ulapi.so: if ((ulapi_so = dlopen(ulapi_lib_fname, RTLD_GLOBAL|RTLD_NOW)) == NULL) { errmsg = dlerror(); rtapi_print_msg(RTAPI_MSG_ERR, "HAL_LIB:%d FATAL - dlopen(%s) failed: %s\n", rtapi_instance, ulapi_lib_fname, errmsg ? errmsg : "NULL"); return -ENOENT; } // resolve rtapi_switch getter function dlerror(); if ((rtapi_get_handle = (rtapi_get_handle_t) dlsym(ulapi_so, "rtapi_get_handle")) == NULL) { errmsg = dlerror(); rtapi_print_msg(RTAPI_MSG_ERR, "HAL_LIB:%d FATAL - resolving %s: cant" " dlsym(rtapi_get_handle): %s\n", rtapi_instance, ulapi_lib, errmsg ? errmsg : "NULL"); return -ENOENT; } assert(rtapi_get_handle != NULL); // this redirects calls to rtapi through the just-loaded ulapi *ulapi_switch = rtapi_get_handle(); // from here on it is safe to call all RTAPI functions (i.e. including those // which go through rtapi_switch) // resolve main function dlerror(); if ((ulapi_main_ref = (ulapi_main_t) dlsym(ulapi_so, "ulapi_main")) == NULL) { errmsg = dlerror(); rtapi_print_msg(RTAPI_MSG_ERR, "HAL_LIB:%d FATAL - resolving %s: " "cant dlsym(ulapi_main): %s\n", rtapi_instance, ulapi_lib, errmsg ? errmsg : "NULL"); return -ENOENT; } // resolve exit function dlerror(); if ((ulapi_exit_ref = (ulapi_exit_t) dlsym(ulapi_so, "ulapi_exit")) == NULL) { errmsg = dlerror(); rtapi_print_msg(RTAPI_MSG_ERR, "HAL_LIB: FATAL - resolving %s:" " cant dlsym(ulapi_exit): %s\n", ulapi_lib, errmsg ? errmsg : "NULL"); return -ENOENT; } assert(ulapi_main_ref != NULL); assert(ulapi_exit_ref != NULL); // call the ulapi init method, passing in the global segment if ((retval = ulapi_main_ref(rtapi_instance, flavor->id, global_data)) < 0) { // check shmdrv, permissions rtapi_print_msg(RTAPI_MSG_ERR, "HAL_LIB: FATAL - cannot attach to instance %d" " - realtime not started?\n", rtapi_instance); return -ENOENT; } // pretty bad - we loaded the wrong ulapi.so if (flavor->id != rtapi_switch->thread_flavor_id) { rtapi_print_msg(RTAPI_MSG_ERR, "HAL_LIB: BUG: thread flavors disagree:" " hal_lib.c=%d rtapi=%d\n", flavor->id, rtapi_switch->thread_flavor_id); } // sanity check - may be harmless if (strcmp(GIT_VERSION, rtapi_switch->git_version)) { rtapi_print_msg(RTAPI_MSG_WARN, "HAL_LIB: UP API warning - git versions disagree:" " hal_lib.c=%s %s=%s\n", GIT_VERSION, ulapi_lib, rtapi_switch->git_version); } // declare victory return 0; }
int main(int argc, char **argv) { int c, i, retval; int option = LOG_NDELAY; pid_t pid, sid; size_t argv0_len, procname_len, max_procname_len; progname = argv[0]; shm_common_init(); while (1) { int option_index = 0; int curind = optind; c = getopt_long (argc, argv, "hI:sFf:i:S", long_options, &option_index); if (c == -1) break; switch (c) { case 'F': foreground++; break; case 'I': rtapi_instance = atoi(optarg); break; case 'R': hal_thread_stack_size = atoi(optarg); break; case 'i': instance_name = optarg; break; case 'f': if ((flavor = flavor_byname(optarg)) == NULL) { fprintf(stderr, "no such flavor: '%s' -- valid flavors are:\n", optarg); flavor_ptr f = flavors; while (f->name) { fprintf(stderr, "\t%s\n", f->name); f++; } exit(1); } break; case 'u': usr_msglevel = atoi(optarg); break; case 'r': rt_msglevel = atoi(optarg); break; case 'H': halsize = atoi(optarg); break; case 'S': use_shmdrv++; break; case 's': log_stderr++; option |= LOG_PERROR; break; case '?': if (optopt) fprintf(stderr, "bad short opt '%c'\n", optopt); else fprintf(stderr, "bad long opt \"%s\"\n", argv[curind]); exit(1); break; default: usage(argc, argv); exit(0); } } // sanity if (getuid() == 0) { fprintf(stderr, "%s: FATAL - will not run as root\n", progname); exit(EXIT_FAILURE); } if (geteuid() == 0) { fprintf(stderr, "%s: FATAL - will not run as setuid root\n", progname); exit(EXIT_FAILURE); } if (flavor == NULL) flavor = default_flavor(); if (flavor == NULL) { fprintf(stderr, "%s: FATAL - failed to detect thread flavor\n", progname); exit(EXIT_FAILURE); } // can we actually run what's being suggested? if (!flavor_and_kernel_compatible(flavor)) { fprintf(stderr, "%s: FATAL - cant run the %s flavor on this kernel\n", progname, flavor->name); exit(EXIT_FAILURE); } // catch installation error: user not in xenomai group if (flavor->id == RTAPI_XENOMAI_ID) { int retval = user_in_xenomai_group(); switch (retval) { case 1: // yes break; case 0: fprintf(stderr, "this user is not member of group xenomai\n"); fprintf(stderr, "please 'sudo adduser <username> xenomai'," " logout and login again\n"); exit(EXIT_FAILURE); default: fprintf(stderr, "cannot determine if this user " "is a member of group xenomai: %s\n", strerror(-retval)); exit(EXIT_FAILURE); } } // do we need the shmdrv module? if (((flavor->flags & FLAVOR_KERNEL_BUILD) || use_shmdrv) && !shmdrv_available()) { fprintf(stderr, "%s: FATAL - %s requires the shmdrv module loaded\n", progname, use_shmdrv ? "--shmdrv" : flavor->name); exit(EXIT_FAILURE); } // the global segment every entity in HAL/RTAPI land attaches to if ((retval = create_global_segment()) != 1) // must be a new shm segment exit(retval); // good to go if (!foreground) { pid = fork(); if (pid < 0) { exit(EXIT_FAILURE); } if (pid > 0) { exit(EXIT_SUCCESS); } umask(0); sid = setsid(); if (sid < 0) { exit(EXIT_FAILURE); } #if 0 if ((chdir("/")) < 0) { exit(EXIT_FAILURE); } #endif } snprintf(proctitle, sizeof(proctitle), "msgd:%d",rtapi_instance); openlog_async(proctitle, option , SYSLOG_FACILITY); // set new process name argv0_len = strlen(argv[0]); procname_len = strlen(proctitle); max_procname_len = (argv0_len > procname_len) ? (procname_len) : (argv0_len); strncpy(argv[0], proctitle, max_procname_len); memset(&argv[0][max_procname_len], '\0', argv0_len - max_procname_len); for (i = 1; i < argc; i++) memset(argv[i], '\0', strlen(argv[i])); // this is the single place in all of linuxCNC where the global segment // gets initialized - no reinitialization from elsewhere init_global_data(global_data, flavor->id, rtapi_instance, halsize, rt_msglevel, usr_msglevel, instance_name,hal_thread_stack_size); syslog_async(LOG_INFO, "startup instance=%s pid=%d flavor=%s " "rtlevel=%d usrlevel=%d halsize=%d shm=%s gcc=%s version=%s", global_data->instance_name, getpid(), flavor->name, global_data->rt_msg_level, global_data->user_msg_level, global_data->hal_size, shmdrv_loaded ? "shmdrv" : "Posix", __VERSION__, GIT_VERSION); syslog_async(LOG_INFO,"configured: %s sha=%s", CONFIG_DATE, GIT_CONFIG_SHA); syslog_async(LOG_INFO,"built: %s %s sha=%s", __DATE__, __TIME__, GIT_BUILD_SHA); if (strcmp(GIT_CONFIG_SHA,GIT_BUILD_SHA)) syslog_async(LOG_WARNING, "WARNING: git SHA's for configure and build do not match!"); if ((global_data->rtapi_msgd_pid != 0) && kill(global_data->rtapi_msgd_pid, 0) == 0) { fprintf(stderr,"%s: another rtapi_msgd is already running (pid %d), exiting\n", progname, global_data->rtapi_msgd_pid); exit(EXIT_FAILURE); } else { global_data->rtapi_msgd_pid = getpid(); } close(STDIN_FILENO); close(STDOUT_FILENO); if (!log_stderr) close(STDERR_FILENO); message_thread(); // signal received - check if rtapi_app running, and shut it down cleanup_actions(); closelog_async(); exit(exit_code); }