diff --git a/Makefile.am b/Makefile.am index e690362..abcb4e9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,7 +10,7 @@ AM_CPPFLAGS = ${regular_CPPFLAGS} \ -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 @@ sbin_PROGRAMS = bin/bumblebeed 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 diff --git a/README.markdown b/README.markdown index b534a6c..5c2baa5 100644 --- a/README.markdown +++ b/README.markdown @@ -19,6 +19,7 @@ The following packages are dependencies for the build process: - 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 --git a/configure.ac b/configure.ac index 8dd831a..067c1f8 100644 --- a/configure.ac +++ b/configure.ac @@ -122,6 +122,7 @@ AC_SUBST([regular_CFLAGS]) # 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 --git a/src/bbconfig.c b/src/bbconfig.c index 1dff5e0..af38098 100644 --- a/src/bbconfig.c +++ b/src/bbconfig.c @@ -251,12 +251,6 @@ Bumblebee homepage: \n", out); */ 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 @@ void bbconfig_parse_opts(int argc, char *argv[], int conf_round) { 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; diff --git a/src/bbconfig.h b/src/bbconfig.h index 5596b64..c6ebb28 100644 --- a/src/bbconfig.h +++ b/src/bbconfig.h @@ -26,6 +26,7 @@ #include //for pid_t #include //for CHAR_MAX #include +#include /* Daemon states */ #define BB_DAEMON 1 @@ -118,6 +119,7 @@ struct bb_status_struct { 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. */ diff --git a/src/bblogger.c b/src/bblogger.c index 55aa163..7519487 100644 --- a/src/bblogger.c +++ b/src/bblogger.c @@ -225,7 +225,7 @@ void check_xorg_pipe(void){ /* 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 +254,5 @@ void check_xorg_pipe(void){ memmove(x_output_buffer, next_part, x_buffer_pos); } } - }while(repeat); + }while (repeat); }/* check_xorg_pipe */ diff --git a/src/bumblebeed.c b/src/bumblebeed.c index a911da9..6e0ade5 100644 --- a/src/bumblebeed.c +++ b/src/bumblebeed.c @@ -34,6 +34,7 @@ #include #include #include +#include #ifdef WITH_PIDFILE #ifdef HAVE_LIBBSD_020 #include @@ -488,6 +489,14 @@ int main(int argc, char* argv[]) { 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 @@ int main(int argc, char* argv[]) { /* dump the config after detecting the driver */ config_dump(); + if (config_validate() != 0) { return (EXIT_FAILURE); } @@ -572,5 +582,7 @@ int main(int argc, char* argv[]) { //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 --git a/src/module.c b/src/module.c index f7b99fa..c378bd7 100644 --- a/src/module.c +++ b/src/module.c @@ -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 */ 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 succesfully 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 */ 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 @@ int module_unload(char *driver) { * @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"; + 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; } - char *mod_argv[] = { - modprobe_bin, - "--dry-run", - "--quiet", - module_name, - NULL - }; - return bb_run_fork(mod_argv, 1) == EXIT_SUCCESS; + + available = list != NULL; + + kmod_module_unref_list(list); + + return available; } diff --git a/src/optirun.c b/src/optirun.c index 65c83d4..8010d2f 100644 --- a/src/optirun.c +++ b/src/optirun.c @@ -37,7 +37,6 @@ #include "bbsocketclient.h" #include "bblogger.h" #include "bbrun.h" -#include "driver.h" /**