core: allow libusb_set_option on the default context before libusb_init

This commit updates libusb_set_option to save the options if setting them on the default
context. This ensures the options 1) can be set before libusb_init(NULL, ...), and 2)
are honored even if the default context is destroyed and re-created.

Signed-off-by: Nathan Hjelm <hjelmn@google.com>
diff --git a/libusb/core.c b/libusb/core.c
index 6fd0c29..054bc6a 100644
--- a/libusb/core.c
+++ b/libusb/core.c
@@ -43,6 +43,8 @@
 struct libusb_context *usbi_default_context;
 static int default_context_refcnt;
 static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER;
+static struct usbi_option default_context_options[LIBUSB_OPTION_MAX];
+
 
 usbi_mutex_static_t active_contexts_lock = USBI_MUTEX_INITIALIZER;
 struct list_head active_contexts_list;
@@ -2173,40 +2175,63 @@
 int API_EXPORTED libusb_set_option(libusb_context *ctx,
 	enum libusb_option option, ...)
 {
-	int arg, r = LIBUSB_SUCCESS;
+	int arg = 0, r = LIBUSB_SUCCESS;
 	va_list ap;
 
-	ctx = usbi_get_context(ctx);
-
 	va_start(ap, option);
-	switch (option) {
-	case LIBUSB_OPTION_LOG_LEVEL:
+	if (LIBUSB_OPTION_LOG_LEVEL == option) {
 		arg = va_arg(ap, int);
 		if (arg < LIBUSB_LOG_LEVEL_NONE || arg > LIBUSB_LOG_LEVEL_DEBUG) {
 			r = LIBUSB_ERROR_INVALID_PARAM;
-			break;
 		}
+	}
+	va_end(ap);
+
+	if (LIBUSB_SUCCESS != r) {
+		return r;
+	}
+
+	if (option >= LIBUSB_OPTION_MAX) {
+		return LIBUSB_ERROR_INVALID_PARAM;
+	}
+
+	if (NULL == ctx) {
+		usbi_mutex_static_lock(&default_context_lock);
+		default_context_options[option].is_set = 1;
+		if (LIBUSB_OPTION_LOG_LEVEL == option) {
+			default_context_options[option].arg.ival = arg;
+		}
+		usbi_mutex_static_unlock(&default_context_lock);
+	}
+
+	ctx = usbi_get_context(ctx);
+	if (NULL == ctx) {
+		return LIBUSB_SUCCESS;
+	}
+
+	switch (option) {
+	case LIBUSB_OPTION_LOG_LEVEL:
 #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
 		if (!ctx->debug_fixed)
 			ctx->debug = (enum libusb_log_level)arg;
 #endif
 		break;
 
-	/* Handle all backend-specific options here */
+		/* Handle all backend-specific options here */
 	case LIBUSB_OPTION_USE_USBDK:
 	case LIBUSB_OPTION_WEAK_AUTHORITY:
 		if (usbi_backend.set_option)
-			r = usbi_backend.set_option(ctx, option, ap);
-		else
-			r = LIBUSB_ERROR_NOT_SUPPORTED;
+			return usbi_backend.set_option(ctx, option, ap);
+
+		return LIBUSB_ERROR_NOT_SUPPORTED;
 		break;
 
+	case LIBUSB_OPTION_MAX:
 	default:
-		r = LIBUSB_ERROR_INVALID_PARAM;
+		return LIBUSB_ERROR_INVALID_PARAM;
 	}
-	va_end(ap);
 
-	return r;
+	return LIBUSB_SUCCESS;;
 }
 
 #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
@@ -2270,7 +2295,11 @@
 	}
 
 #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
-	_ctx->debug = get_env_debug_level();
+	if (NULL == ctx && default_context_options[LIBUSB_OPTION_LOG_LEVEL].is_set) {
+		_ctx->debug = default_context_options[LIBUSB_OPTION_LOG_LEVEL].arg.ival;
+	} else {
+		_ctx->debug = get_env_debug_level();
+	}
 	if (_ctx->debug != LIBUSB_LOG_LEVEL_NONE)
 		_ctx->debug_fixed = 1;
 #endif
@@ -2308,11 +2337,20 @@
 
 	usbi_hotplug_init(_ctx);
 
-	usbi_mutex_static_unlock(&default_context_lock);
-
-	if (ctx)
+	if (!ctx) {
+		for (enum libusb_option option = 0 ; option < LIBUSB_OPTION_MAX ; option++) {
+			if (LIBUSB_OPTION_LOG_LEVEL == option || !default_context_options[option].is_set) {
+				continue;
+			}
+			r = libusb_set_option(_ctx, option);
+			if (LIBUSB_SUCCESS != r)
+				goto err_io_exit;
+		}
+	} else
 		*ctx = _ctx;
 
+	usbi_mutex_static_unlock(&default_context_lock);
+
 	return 0;
 
 err_io_exit:
diff --git a/libusb/libusb.h b/libusb/libusb.h
index 57ca58f..a50e06d 100644
--- a/libusb/libusb.h
+++ b/libusb/libusb.h
@@ -2107,7 +2107,9 @@
 	 *
 	 * Only valid for Android builds.
 	 */
-	LIBUSB_OPTION_WEAK_AUTHORITY = 2
+	LIBUSB_OPTION_WEAK_AUTHORITY = 2,
+
+	LIBUSB_OPTION_MAX = 3
 };
 
 int LIBUSB_CALL libusb_set_option(libusb_context *ctx, enum libusb_option option, ...);
diff --git a/libusb/libusbi.h b/libusb/libusbi.h
index 97c894e..31d4917 100644
--- a/libusb/libusbi.h
+++ b/libusb/libusbi.h
@@ -791,6 +791,13 @@
 	short poll_events);
 void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle);
 
+struct usbi_option {
+  int is_set;
+  union {
+    int ival;
+  } arg;
+};
+
 /* OS event abstraction */
 
 int usbi_create_event(usbi_event_t *event);
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index 6524fe2..9a19b34 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 11622
+#define LIBUSB_NANO 11623
diff --git a/tests/stress.c b/tests/stress.c
index 6dcb8f3..a5c9d50 100644
--- a/tests/stress.c
+++ b/tests/stress.c
@@ -130,10 +130,13 @@
 			return TEST_STATUS_FAILURE;
 		}
 
-		/* Enable debug output, to be sure to use the context */
-		libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG);
+		/* Enable debug output on new context, to be sure to use the context */
 		libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG);
 
+		/* Enable debug outout on the default context. This should work even before
+		 * the context has been created. */
+		libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG);
+
 		/* Now create a reference to the default context */
 		r = libusb_init(NULL);
 		if (r != LIBUSB_SUCCESS) {