summaryrefslogtreecommitdiffstats
path: root/common/binfmt.c
diff options
context:
space:
mode:
authorJean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>2012-04-13 14:02:47 +0800
committerJean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>2012-04-18 20:14:12 +0800
commit071ceba1e38f93ba2d937ae7fad037c5a9493655 (patch)
tree82b4377286c1c701e5a27e4d41b2ad06e1356e5c /common/binfmt.c
parent733d85510c74d9638dc88e001f45412f30a2f3d0 (diff)
downloadbarebox-071ceba1e38f93ba2d937ae7fad037c5a9493655.tar.gz
Introduce binfmt support
This will allow to execute any file and detect it's type to handle it. This will allow to use shell for bootp bootfile or dfu. You can register multiple hook for the same filetype. They will be execute in the invert order of register. If a hook does not handle the file you just return -ERESTARTNOHAND; This is only available with hush parser. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Diffstat (limited to 'common/binfmt.c')
-rw-r--r--common/binfmt.c95
1 files changed, 95 insertions, 0 deletions
diff --git a/common/binfmt.c b/common/binfmt.c
new file mode 100644
index 0000000..7dcf5d7
--- /dev/null
+++ b/common/binfmt.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+ *
+ * GPL v2
+ */
+
+#include <common.h>
+#include <binfmt.h>
+#include <libbb.h>
+#include <malloc.h>
+#include <command.h>
+#include <errno.h>
+
+static LIST_HEAD(binfmt_hooks);
+
+static int binfmt_run(char *file, int argc, char **argv)
+{
+ struct binfmt_hook *b;
+ enum filetype type = file_name_detect_type(file);
+ int ret;
+
+ list_for_each_entry(b, &binfmt_hooks, list) {
+ if (b->type != type)
+ continue;
+
+ ret = b->hook(b, file, argc, argv);
+ if (ret != -ERESTARTNOHAND)
+ return ret;
+ }
+ return -ENOENT;
+}
+
+/*
+ * This converts the original '/executable <args>' into
+ * 'barebox_cmd <args> /executable'
+ */
+static int binfmt_exec_excute(struct binfmt_hook *b, char *file, int argc, char **argv)
+{
+ char **newargv = xzalloc(sizeof(char*) * (argc + 1));
+ int ret, i;
+
+ newargv[0] = b->exec;
+
+ for (i = 1 ; i < argc; i++)
+ newargv[i] = argv[i];
+ newargv[i] = file;
+
+ ret = execute_binfmt(argc + 1, newargv);
+
+ free(newargv);
+
+ return ret;
+}
+
+int execute_binfmt(int argc, char **argv)
+{
+ int ret;
+ char *path;
+
+ if (strchr(argv[0], '/'))
+ return binfmt_run(argv[0], argc, argv);
+
+ path = find_execable(argv[0]);
+ if (path) {
+ ret = binfmt_run(path, argc, argv);
+ free(path);
+ return ret;
+ }
+
+ return execute_command(argc, &argv[0]);
+}
+
+int binfmt_register(struct binfmt_hook *b)
+{
+ if (!b || !b->type)
+ return -EIO;
+
+ if (!b->hook && !b->exec)
+ return -EIO;
+
+ if (b->exec)
+ b->hook = binfmt_exec_excute;
+
+ list_add_tail(&b->list, &binfmt_hooks);
+
+ return 0;
+}
+
+void binfmt_unregister(struct binfmt_hook *b)
+{
+ if (!b)
+ return;
+
+ list_del(&b->list);
+}