// When you run this example, you MUST use ./ on the plugin name
// passed as the first argument, like:
//
//     ./12_dload ./plugin1.so 5
//                ^
//                |
//                need this!
//
// If you don't, it won't work. (This is actually a security feature
// to prevent malicious shared objects from inserting themselves
// into programs.)
#include <stdio.h>
#include <stdlib.h>

// this gives us dlopen() and dlsym()
#include <dlfcn.h>

// Our "plugin API" is very simple: just a single function named
// "plugin_func" of type plugin_func_t. More complex APIs are
// implemented as either a collection of functions, or as a single
// function which returns a struct *containing* a bunch of function
// pointers to the plugin's various operations.
#define PLUGIN_FUNC_NAME "plugin_func"

// Typedef for our "plugin API" - a single function that takes an int
// and returns an int. dlsym() returns pointers to functions, so this
// is a function pointer type.
typedef int (*plugin_func_t)(int);

// Prototype for load_plugin below
plugin_func_t load_plugin(const char* filename);

int main(int argc, char** argv) {
	if(argc < 3) {
		fprintf(stderr, "run this like:\n    ./12_dload ./<libraryname.so> <integer>\n");
		return 1;
	}

	int val;
	if(sscanf(argv[2], "%d", &val) == 0) {
		fprintf(stderr, "gimme an integer dammit\n");
		return 1;
	}
	
	// Load the function from the plugin and call it!
	plugin_func_t plugin_func = load_plugin(argv[1]);
	printf("in main: plugin_func(%d) returned %d.\n", val, plugin_func(val));
	return 0;
}

// The actual plugin loading
plugin_func_t load_plugin(const char* filename) {
	// Open the dynamic library. The second argument is used to control
	// when and how it is loaded; RTLD_NOW forces the library and all its
	// dependencies to be loaded immediately.
	void* lib = dlopen(filename, RTLD_NOW);
	if(lib == NULL) {
		fprintf(stderr, "could not open %s: %s\n", filename, dlerror());
		exit(1);
	}

	// You extract symbols from the library by name.
	plugin_func_t plugin_func = dlsym(lib, PLUGIN_FUNC_NAME);
	if(plugin_func == NULL) {
		fprintf(stderr, "could not extract plugin_func: %s\n", dlerror());
		exit(1);
	}

	return plugin_func;
}