diff -uNr Bumblebee.org/conf/bumblebee.conf.in Bumblebee/conf/bumblebee.conf.in --- Bumblebee.org/conf/bumblebee.conf.in 2018-03-20 13:39:09.000000000 +0800 +++ Bumblebee/conf/bumblebee.conf.in 2018-03-20 13:48:07.102769296 +0800 @@ -22,6 +22,8 @@ Driver=@CONF_DRIVER@ # Directory with a dummy config file to pass as a -configdir to secondary X XorgConfDir=@XCONFDDIR@ +# Xorg binary to run +XorgBinary=@CONF_XORG_BINARY@ ## Client options. Will take effect on the next optirun executed. [optirun] diff -uNr Bumblebee.org/configure.ac Bumblebee/configure.ac --- Bumblebee.org/configure.ac 2018-03-20 13:39:09.000000000 +0800 +++ Bumblebee/configure.ac 2018-03-20 13:48:07.110769310 +0800 @@ -32,6 +32,16 @@ AC_DEFINE_SUBST(CONF_VGLCOMPRESS, "proxy", [vglclient transport method]) AC_DEFINE_SUBST(CONF_TURNOFFATEXIT, "false", [state of card when shutting off daemon]) +# OpenSUSE: /usr/bin/X -> /var/lib/X11/X -> /usr/bin/Xorg +# Fedora, Arch Linux: /usr/bin/X -> /usr/bin/Xorg +# Ubuntu: /usr/bin/X is a custom binary doing authorization and then executes +# /etc/X11/X -> /usr/bin/Xorg +AC_DEFINE_CONF(CONF_XORG_BINARY, [Xorg binary to run], [ +if test "x" = "x$CONF_XORG_BINARY"; then + CONF_XORG_BINARY=Xorg +fi +]) + AC_DEFINE_CONF(CONF_BRIDGE, [optirun display/render bridge, valid values are auto (default), primus and virtualgl], [ case $CONF_BRIDGE in auto|primus|virtualgl) ;; @@ -122,6 +132,7 @@ # Checks for header files. PKG_CHECK_MODULES([x11], [x11]) PKG_CHECK_MODULES([glib], [glib-2.0]) +PKG_CHECK_MODULES([kmod], [libkmod]) AS_IF([test "x$with_pidfile" != xno], [ PKG_CHECK_MODULES([libbsd], [libbsd >= 0.2.0]) PKG_CHECK_EXISTS([libbsd = 0.2.0], [AC_DEFINE(HAVE_LIBBSD_020)]) diff -uNr Bumblebee.org/Makefile.am Bumblebee/Makefile.am --- Bumblebee.org/Makefile.am 2018-03-20 13:39:09.000000000 +0800 +++ Bumblebee/Makefile.am 2018-03-20 13:48:07.110769310 +0800 @@ -10,7 +10,7 @@ -DCONF_XORG='"$(bumblebeedconfdir)/xorg.conf.DRIVER"' \ -DCONF_XORG_DIR='"$(bumblebeedconfdir)/xorg.conf.d"' AM_CFLAGS = ${regular_CFLAGS} \ - ${x11_CFLAGS} ${libbsd_CFLAGS} ${glib_CFLAGS} \ + ${x11_CFLAGS} ${libbsd_CFLAGS} ${glib_CFLAGS} ${kmod_CFLAGS} \ -Wextra -funsigned-char -DGITVERSION='"${GITVERSION}"' noinst_SCRIPTS = scripts/systemd/bumblebeed.service \ @@ -49,13 +49,13 @@ bin_PROGRAMS = bin/optirun bin_optirun_SOURCES = src/module.c src/bbconfig.c src/bblogger.c src/bbrun.c \ - src/bbsocket.c src/driver.c src/optirun.c src/bbsocketclient.c -bin_optirun_LDADD = ${glib_LIBS} -lrt + src/bbsocket.c src/optirun.c src/bbsocketclient.c +bin_optirun_LDADD = ${glib_LIBS} ${kmod_LIBS} -lrt bin_bumblebeed_SOURCES = src/pci.c src/bbconfig.c src/bblogger.c src/bbrun.c \ src/bbsocket.c src/module.c src/bbsecondary.c src/switch/switching.c \ src/switch/sw_bbswitch.c src/switch/sw_switcheroo.c \ src/driver.c src/bumblebeed.c -bin_bumblebeed_LDADD = ${x11_LIBS} ${libbsd_LIBS} ${glib_LIBS} -lrt +bin_bumblebeed_LDADD = ${x11_LIBS} ${libbsd_LIBS} ${glib_LIBS} ${kmod_LIBS} -lrt dist_doc_DATA = $(relnotes) README.markdown bumblebeedconf_DATA = conf/bumblebee.conf conf/xorg.conf.nouveau conf/xorg.conf.nvidia @@ -70,6 +70,7 @@ do_subst = sed -e 's|[@]GITVERSION[@]|$(GITVERSION)|g' \ -e 's|[@]CONF_XDISP[@]|$(CONF_XDISP)|g' \ + -e 's|[@]CONF_XORG_BINARY[@]|$(CONF_XORG_BINARY)|g' \ -e 's|[@]CONF_SOCKPATH[@]|$(CONF_SOCKPATH)|g' \ -e 's|[@]CONF_GID[@]|$(CONF_GID)|g' \ -e 's|[@]CONF_PM_METHOD[@]|$(CONF_PM_METHOD)|g' \ diff -uNr Bumblebee.org/README.markdown Bumblebee/README.markdown --- Bumblebee.org/README.markdown 2018-03-20 13:39:09.000000000 +0800 +++ Bumblebee/README.markdown 2018-03-20 13:48:07.110769310 +0800 @@ -19,6 +19,7 @@ - pkg-config - glib-2.0 and development headers - libx11 and development headers +- libkmod2 and development headers - libbsd and development headers (if pidfile support is enabled, default yes) - help2man (optional, it is needed for building manual pages) diff -uNr Bumblebee.org/src/bbconfig.c Bumblebee/src/bbconfig.c --- Bumblebee.org/src/bbconfig.c 2018-03-20 13:39:09.000000000 +0800 +++ Bumblebee/src/bbconfig.c 2018-03-20 13:48:07.110769310 +0800 @@ -201,11 +201,11 @@ } /* common options */ fputs("\ - -q, --quiet, --silent supresses all logging messages\n\ + -q, --quiet, --silent suppresses all logging messages\n\ -v, --verbose increase the verbosity level of log messages. It\n\ can be specified up to two times (or five if\n\ --quiet is used)\n\ - --debug show all logging messsages by setting the verbosity\n\ + --debug show all logging messages by setting the verbosity\n\ level to the maximum\n\ -C, --config FILE retrieve settings for Bumblebee from FILE\n", out); if (is_optirun) { @@ -251,12 +251,6 @@ */ static int bbconfig_parse_common(int opt, char *value) { switch (opt) { - case 'q'://quiet mode - bb_status.verbosity = VERB_NONE; - break; - case OPT_DEBUG://debug mode - bb_status.verbosity = VERB_ALL; - break; case 'd'://X display number set_string_value(&bb_config.x_display, value); break; @@ -307,6 +301,12 @@ bb_status.verbosity++; } break; + case 'q'://quiet mode + bb_status.verbosity = VERB_NONE; + break; + case OPT_DEBUG://debug mode + bb_status.verbosity = VERB_ALL; + break; case 's': /* Unix socket to use for communication */ set_string_value(&bb_config.socket_path, optarg); break; @@ -425,6 +425,10 @@ if (g_key_file_has_key(bbcfg, section, key, NULL)) { free_and_set_value(&bb_config.x_conf_dir, g_key_file_get_string(bbcfg, section, key, NULL)); } + key = "XorgBinary"; + if (g_key_file_has_key(bbcfg, section, key, NULL)) { + free_and_set_value(&bb_config.xorg_binary, g_key_file_get_string(bbcfg, section, key, NULL)); + } return bbcfg; } @@ -517,6 +521,7 @@ set_string_value(&bb_config.gid_name, CONF_GID); set_string_value(&bb_config.x_conf_file, CONF_XORG); set_string_value(&bb_config.x_conf_dir, CONF_XORG_DIR); + set_string_value(&bb_config.xorg_binary, CONF_XORG_BINARY); set_string_value(&bb_config.optirun_bridge, CONF_BRIDGE); set_string_value(&bb_config.primus_ld_path, CONF_PRIMUS_LD_PATH); set_string_value(&bb_config.vgl_compress, CONF_VGLCOMPRESS); @@ -550,6 +555,7 @@ #endif bb_log(LOG_DEBUG, " xorg.conf file: %s\n", bb_config.x_conf_file); bb_log(LOG_DEBUG, " xorg.conf.d dir: %s\n", bb_config.x_conf_dir); + bb_log(LOG_DEBUG, " Xorg binary: %s\n", bb_config.xorg_binary); bb_log(LOG_DEBUG, " ModulePath: %s\n", bb_config.mod_path); bb_log(LOG_DEBUG, " GID name: %s\n", bb_config.gid_name); bb_log(LOG_DEBUG, " Power method: %s\n", diff -uNr Bumblebee.org/src/bbconfig.h Bumblebee/src/bbconfig.h --- Bumblebee.org/src/bbconfig.h 2018-03-20 13:39:09.000000000 +0800 +++ Bumblebee/src/bbconfig.h 2018-03-20 13:48:07.110769310 +0800 @@ -26,6 +26,7 @@ #include //for pid_t #include //for CHAR_MAX #include +#include /* Daemon states */ #define BB_DAEMON 1 @@ -118,10 +119,12 @@ int x_pipe[2];//pipes for reading/writing output from X's stdout/stderr gboolean use_syslog; char *program_name; + struct kmod_ctx *kmod_ctx; }; /* Structure containing the configuration. */ struct bb_config_struct { + char * xorg_binary; /// Xorg binary to run. char * x_display; /// X display number to use. char * x_conf_file; /// Path to the X configuration file. char * x_conf_dir; /// Path to the dummy X configuration directory. diff -uNr Bumblebee.org/src/bblogger.c Bumblebee/src/bblogger.c --- Bumblebee.org/src/bblogger.c 2018-03-20 13:39:09.000000000 +0800 +++ Bumblebee/src/bblogger.c 2018-03-20 13:48:07.110769310 +0800 @@ -144,7 +144,10 @@ /* Error lines are errors. */ if (strncmp(string, "(EE)", 4) == 0){ if (strstr(string, "Failed to load module \"kbd\"") || - strstr(string, "No input driver matching")) { + strstr(string, "No input driver matching") || + strstr(string, "systemd-logind: failed to get session:") || + strstr(string, "failed to set DRM interface version 1.4:") || + strstr(string, "Server terminated successfully")) { /* non-fatal errors */ prio = LOG_DEBUG; } else { @@ -200,7 +203,7 @@ } } } - + /* do the actual logging */ bb_log(prio, "[XORG] %s\n", string); } @@ -225,7 +228,7 @@ /* line / buffer is full, process the remaining buffer the next round */ repeat = 1; } - }else{ + } else { if (r == 0 || (errno != EAGAIN && r == -1)){ /* the pipe is closed/invalid. Clean up. */ if (bb_status.x_pipe[0] != -1){close(bb_status.x_pipe[0]); bb_status.x_pipe[0] = -1;} @@ -254,5 +257,5 @@ memmove(x_output_buffer, next_part, x_buffer_pos); } } - }while(repeat); + }while (repeat); }/* check_xorg_pipe */ diff -uNr Bumblebee.org/src/bbrun.c Bumblebee/src/bbrun.c --- Bumblebee.org/src/bbrun.c 2018-03-20 13:39:09.000000000 +0800 +++ Bumblebee/src/bbrun.c 2018-03-20 13:48:07.098769289 +0800 @@ -170,7 +170,7 @@ exitcode = 128 + WTERMSIG(status); } } else { - bb_log(LOG_ERR, "waitpid(%i) faild with %s\n", pid, strerror(errno)); + bb_log(LOG_ERR, "waitpid(%i) failed with %s\n", pid, strerror(errno)); } pidlist_remove(pid); } else { diff -uNr Bumblebee.org/src/bbsecondary.c Bumblebee/src/bbsecondary.c --- Bumblebee.org/src/bbsecondary.c 2018-03-20 13:39:09.000000000 +0800 +++ Bumblebee/src/bbsecondary.c 2018-03-20 13:48:07.102769296 +0800 @@ -138,7 +138,7 @@ if (!bb_is_running(bb_status.x_pid)) { char pci_id[12]; static char *x_conf_file; - snprintf(pci_id, 12, "PCI:%02x:%02x:%o", pci_bus_id_discrete->bus, + snprintf(pci_id, 12, "PCI:%02d:%02d:%o", pci_bus_id_discrete->bus, pci_bus_id_discrete->slot, pci_bus_id_discrete->func); if (!x_conf_file) { x_conf_file = xorg_path_w_driver(bb_config.x_conf_file, bb_config.driver); @@ -146,7 +146,7 @@ bb_log(LOG_INFO, "Starting X server on display %s.\n", bb_config.x_display); char *x_argv[] = { - XORG_BINARY, + bb_config.xorg_binary, bb_config.x_display, "-config", x_conf_file, "-configdir", bb_config.x_conf_dir, @@ -158,6 +158,12 @@ "-modulepath", bb_config.mod_path, // keep last NULL }; + char **argvp; + bb_log(LOG_DEBUG, "X server command line:"); + for (argvp = x_argv; argvp && *argvp; argvp++) { + bb_log(LOG_DEBUG, " %s", *argvp); + } + bb_log(LOG_DEBUG, "\n"); enum {n_x_args = sizeof(x_argv) / sizeof(x_argv[0])}; if (!*bb_config.mod_path) { x_argv[n_x_args - 3] = 0; //remove -modulepath if not set diff -uNr Bumblebee.org/src/bbsecondary.h Bumblebee/src/bbsecondary.h --- Bumblebee.org/src/bbsecondary.h 2018-03-20 13:39:09.000000000 +0800 +++ Bumblebee/src/bbsecondary.h 2018-03-20 13:48:07.102769296 +0800 @@ -20,14 +20,6 @@ */ #pragma once -/** - * OpenSUSE: /usr/bin/X -> /var/lib/X11/X -> /usr/bin/Xorg - * Fedora, Arch Linux: /usr/bin/X -> /usr/bin/Xorg - * Ubuntu: /usr/bin/X is a custom binary doing authorization and then executes - * /etc/X11/X -> /usr/bin/Xorg - */ -#define XORG_BINARY "Xorg" - /* PCI Bus ID of the discrete video card */ struct pci_bus_id *pci_bus_id_discrete; diff -uNr Bumblebee.org/src/bumblebeed.c Bumblebee/src/bumblebeed.c --- Bumblebee.org/src/bumblebeed.c 2018-03-20 13:39:09.000000000 +0800 +++ Bumblebee/src/bumblebeed.c 2018-03-20 13:48:07.110769310 +0800 @@ -34,6 +34,7 @@ #include #include #include +#include #ifdef WITH_PIDFILE #ifdef HAVE_LIBBSD_020 #include @@ -488,6 +489,14 @@ free(pci_id_igd); + // kmod context have to be available for driver detection + bb_status.kmod_ctx = kmod_new(NULL, NULL); + if (bb_status.kmod_ctx == NULL) { + bb_log(LOG_ERR, "kmod_new() failed!\n"); + bb_closelog(); + exit(EXIT_FAILURE); + } + GKeyFile *bbcfg = bbconfig_parse_conf(); bbconfig_parse_opts(argc, argv, PARSE_STAGE_DRIVER); driver_detect(); @@ -500,6 +509,7 @@ /* dump the config after detecting the driver */ config_dump(); + if (config_validate() != 0) { return (EXIT_FAILURE); } @@ -572,5 +582,7 @@ //close X pipe, if any parts of it are open still if (bb_status.x_pipe[0] != -1){close(bb_status.x_pipe[0]); bb_status.x_pipe[0] = -1;} if (bb_status.x_pipe[1] != -1){close(bb_status.x_pipe[1]); bb_status.x_pipe[1] = -1;} + //cleanup kmod context + kmod_unref(bb_status.kmod_ctx); return (EXIT_SUCCESS); } diff -uNr Bumblebee.org/src/module.c Bumblebee/src/module.c --- Bumblebee.org/src/module.c 2018-03-20 13:39:09.000000000 +0800 +++ Bumblebee/src/module.c 2018-03-20 13:48:07.110769310 +0800 @@ -24,90 +24,152 @@ #include #include #include +#include +#include #include "module.h" #include "bblogger.h" #include "bbrun.h" +#include "bbconfig.h" + +int module_unload_recursive(struct kmod_module *mod); /** - * Checks in /proc/modules whether a kernel module is loaded + * Checks whether a kernel module is loaded * * @param driver The name of the driver (not a filename) * @return 1 if the module is loaded, 0 otherwise */ int module_is_loaded(char *driver) { - // use the same buffer length as lsmod - char buffer[4096]; - FILE * bbs = fopen("/proc/modules", "r"); - int ret = 0; - /* assume mod_len <= sizeof(buffer) */ - int mod_len = strlen(driver); - - if (bbs == 0) {//error opening, return -1 - bb_log(LOG_DEBUG, "Couldn't open /proc/modules"); - return -1; - } - while (fgets(buffer, sizeof(buffer), bbs)) { - /* match "module" with "module " and not "module-blah" */ - if (!strncmp(buffer, driver, mod_len) && isspace(buffer[mod_len])) { - /* module is found */ - ret = 1; - break; - } + int err, state; + struct kmod_module *mod; + + err = kmod_module_new_from_name(bb_status.kmod_ctx, driver, &mod); + if (err < 0) { + bb_log(LOG_DEBUG, "kmod_module_new_from_name(%s) failed (err: %d).\n", + driver, err); + return 0; } - fclose(bbs); - return ret; + + state = kmod_module_get_initstate(mod); + kmod_module_unref(mod); + + return state == KMOD_MODULE_LIVE; } /** - * Attempts to load a module. If the module has not been loaded after ten - * seconds, give up + * Attempts to load a module. * * @param module_name The filename of the module to be loaded * @param driver The name of the driver to be loaded - * @return 1 if the driver is succesfully loaded, 0 otherwise + * @return 1 if the driver is successfully loaded, 0 otherwise */ int module_load(char *module_name, char *driver) { + int err = 0; + int flags = KMOD_PROBE_IGNORE_LOADED; + struct kmod_list *l, *list = NULL; + if (module_is_loaded(driver) == 0) { /* the module has not loaded yet, try to load it */ - bb_log(LOG_INFO, "Loading driver %s (module %s)\n", driver, module_name); - char *mod_argv[] = { - "modprobe", - module_name, - NULL - }; - bb_run_fork_wait(mod_argv, 10); - if (module_is_loaded(driver) == 0) { - bb_log(LOG_ERR, "Module %s could not be loaded (timeout?)\n", module_name); + + bb_log(LOG_INFO, "Loading driver '%s' (module '%s')\n", driver, module_name); + err = kmod_module_new_from_lookup(bb_status.kmod_ctx, module_name, &list); + + if (err < 0) { + bb_log(LOG_DEBUG, "kmod_module_new_from_lookup(%s) failed (err: %d).\n", + module_name, err); + return 0; + } + + if (list == NULL) { + bb_log(LOG_ERR, "Module '%s' not found.\n", module_name); return 0; } + + kmod_list_foreach(l, list) { + struct kmod_module *mod = kmod_module_get_module(l); + + bb_log(LOG_DEBUG, "Loading module '%s'.\n", kmod_module_get_name(mod)); + err = kmod_module_probe_insert_module(mod, flags, NULL, NULL, NULL, 0); + + if (err < 0) { + bb_log(LOG_DEBUG, "kmod_module_probe_insert_module(%s) failed (err: %d).\n", + kmod_module_get_name(mod), err); + } + + kmod_module_unref(mod); + + if (err < 0) { + break; + } + } + + kmod_module_unref_list(list); } - return 1; + + return err >= 0; } /** - * Attempts to unload a module if loaded, for ten seconds before - * giving up + * Unloads module and modules that are depending on this module. + * + * @param mod Reference to libkmod module + * @return 1 if the module is successfully unloaded, 0 otherwise + */ +int module_unload_recursive(struct kmod_module *mod) { + int err = 0, flags = 0, refcnt; + struct kmod_list *holders; + + holders = kmod_module_get_holders(mod); + if (holders != NULL) { + struct kmod_list *itr; + + kmod_list_foreach(itr, holders) { + struct kmod_module *hm = kmod_module_get_module(itr); + err = module_unload_recursive(hm); + kmod_module_unref(hm); + + if (err < 0) { + break; + } + } + kmod_module_unref_list(holders); + } + + refcnt = kmod_module_get_refcnt(mod); + if (refcnt == 0) { + bb_log(LOG_INFO, "Unloading module %s\n", kmod_module_get_name(mod)); + err = kmod_module_remove_module(mod, flags); + } else { + bb_log(LOG_ERR, "Failed to unload module '%s' (ref count: %d).\n", + kmod_module_get_name(mod), refcnt); + err = 1; + } + + return err == 0; +} + +/** + * Attempts to unload a module if loaded. * * @param driver The name of the driver (not a filename) - * @return 1 if the driver is succesfully unloaded, 0 otherwise + * @return 1 if the driver is successfully unloaded, 0 otherwise */ int module_unload(char *driver) { + int err; + struct kmod_module *mod; if (module_is_loaded(driver) == 1) { - int retries = 30; - bb_log(LOG_INFO, "Unloading %s driver\n", driver); - char *mod_argv[] = { - "rmmod", - driver, - NULL - }; - bb_run_fork_wait(mod_argv, 10); - while (retries-- > 0 && module_is_loaded(driver) == 1) { - usleep(100000); - } - if (module_is_loaded(driver) == 1) { - bb_log(LOG_ERR, "Unloading %s driver timed out.\n", driver); + err = kmod_module_new_from_name(bb_status.kmod_ctx, driver, &mod); + + if (err < 0) { + bb_log(LOG_DEBUG, "kmod_module_new_from_name(%s) failed (err: %d).\n", + driver, err); return 0; } + + err = module_unload_recursive(mod); + kmod_module_unref(mod); + + return err; } return 1; } @@ -119,18 +181,20 @@ * @return 1 if the module is available for loading, 0 otherwise */ int module_is_available(char *module_name) { - /* HACK to support call from optirun */ - char *modprobe_bin = "/sbin/modprobe"; - if (access(modprobe_bin, X_OK)) { - /* if /sbin/modprobe is not found, pray that PATH contains it */ - modprobe_bin = "modprobe"; - } - char *mod_argv[] = { - modprobe_bin, - "--dry-run", - "--quiet", - module_name, - NULL - }; - return bb_run_fork(mod_argv, 1) == EXIT_SUCCESS; + int err, available; + struct kmod_list *list = NULL; + + err = kmod_module_new_from_lookup(bb_status.kmod_ctx, module_name, &list); + + if (err < 0) { + bb_log(LOG_DEBUG, "kmod_module_new_from_lookup(%s) failed (err: %d).\n", + module_name, err); + return 0; + } + + available = list != NULL; + + kmod_module_unref_list(list); + + return available; } diff -uNr Bumblebee.org/src/optirun.c Bumblebee/src/optirun.c --- Bumblebee.org/src/optirun.c 2018-03-20 13:39:09.000000000 +0800 +++ Bumblebee/src/optirun.c 2018-03-20 13:48:07.110769310 +0800 @@ -37,7 +37,6 @@ #include "bbsocketclient.h" #include "bblogger.h" #include "bbrun.h" -#include "driver.h" /** @@ -62,7 +61,7 @@ /** * Prints the status of the Bumblebee server if available - * @return EXIT_SUCCESS if the status is succesfully retrieved, + * @return EXIT_SUCCESS if the status is successfully retrieved, * EXIT_FAILURE otherwise */ static int report_daemon_status(void) { diff -uNr Bumblebee.org/src/switch/sw_bbswitch.c Bumblebee/src/switch/sw_bbswitch.c --- Bumblebee.org/src/switch/sw_bbswitch.c 2018-03-20 13:39:09.000000000 +0800 +++ Bumblebee/src/switch/sw_bbswitch.c 2018-03-20 13:48:07.098769289 +0800 @@ -98,7 +98,7 @@ * recognized by bbswitch. Assuming that vga_switcheroo was not told to OFF * the device */ if (module_load("bbswitch", "bbswitch")) { - bb_log(LOG_DEBUG, "succesfully loaded bbswitch\n"); + bb_log(LOG_DEBUG, "successfully loaded bbswitch\n"); /* hurrah, bbswitch could be loaded which means that the module is * available and that the card is supported */ return 1;