[syslinux] [PATCH] Add support for DHCP-Options
Maurice Massar
massar at unix-ag.uni-kl.de
Tue Apr 10 11:17:00 PDT 2007
hi,
I've written a start of an c32-module to do basic substition:
add subst.c32
this is a start of a generic substition module
---
commit 9a1f41a7e6599fe6a162197cd9ddc6610185e780
tree cfb799a0be9844926afe44e21b2eb96630666c44
parent 89478bdbfa7167bc1b627a478d042c99e46f06b7
author Maurice Massar <massar at unix-ag.uni-kl.de> Tue, 10 Apr 2007 20:13:39 +0200
committer Maurice Massar <massar at unix-ag.uni-kl.de> Tue, 10 Apr 2007 20:13:39 +0200
com32/modules/Makefile | 3 -
com32/modules/subst.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 231 insertions(+), 1 deletions(-)
diff --git a/com32/modules/Makefile b/com32/modules/Makefile
index bef45a8..a7c2de9 100644
--- a/com32/modules/Makefile
+++ b/com32/modules/Makefile
@@ -45,7 +45,8 @@ INCDIR = /usr/include
COM32DIR = $(AUXDIR)/com32
MODULES = chain.c32 menu.c32 vesamenu.c32 ethersel.c32 mboot.c32 \
- dmitest.c32 cpuidtest.c32 pcitest.c32 elf.c32 linux.c32
+ dmitest.c32 cpuidtest.c32 pcitest.c32 elf.c32 linux.c32 \
+ subst.c32
TESTFILES =
all: $(MODULES) $(TESTFILES)
diff --git a/com32/modules/subst.c b/com32/modules/subst.c
new file mode 100644
index 0000000..60241c9
--- /dev/null
+++ b/com32/modules/subst.c
@@ -0,0 +1,229 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2007 Maurice Massar <massar at unix-ag.uni-kl.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * subst.c
+ *
+ * this is a start of a generic substitution module
+ *
+ * Currently only DHCP string options are supported.
+ *
+ * To use, set up a syslinux config file like this:
+ *
+ * LABEL something
+ * KERNEL subst.c32
+ * APPEND vmlinuz-${dhcp/option-123} initrd=initrd.img-${dhcp/option-123}.gz
+ *
+ * further: ".localboot ###" is supported (like menu.c32 does)
+ *
+ * TODO:
+ * - add support for concatenating options (current len limit: 255 bytes)
+ * - add support for options overflowing into filename and server-name area
+ * - add support to recode binary data to text
+ * - add support for non-option dhcp data
+ * - support to declare variables inside config-file
+ *
+ * - if the comboot-api gets a methode to pass a configfile
+ * (contents, not filename) back to syslinux, use it!
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <console.h>
+#include <netinet/in.h>
+#include <syslinux/boot.h>
+#include <syslinux/pxe.h>
+
+typedef struct {
+ uint8_t opcode;
+ uint8_t hardware;
+ uint8_t hardware_len;
+ uint8_t gatehops;
+ uint32_t ident;
+ uint16_t seconds;
+ uint16_t flags;
+
+ in_addr_t cip;
+ in_addr_t yip;
+ in_addr_t sip;
+ in_addr_t gip;
+ uint8_t caddr[16];
+ uint8_t sname[64];
+ uint8_t bootfile[128];
+ uint8_t options[];
+} __packed bootp_t;
+
+bootp_t *dhcpack;
+size_t dhcpack_len;
+
+int find_dhcp_option(uint8_t search, const uint8_t **str, int *len)
+{
+ uint8_t *p;
+ int l;
+
+ p = dhcpack->options;
+ l = dhcpack_len - 236;
+
+ p += 4; l -= 4; /* skip magic */
+
+ while (l > 0) {
+ if (*p == 0) {
+ p++; l--;
+ continue;
+ }
+ if (*p == 255)
+ return 0;
+ if (*p == search)
+ break;
+ l -= 2 + p[1];
+ p += 2 + p[1];
+ }
+
+ l -= 2;
+ l = (p[1] <= l) ? p[1] : l;
+ p += 2;
+
+ *str = p;
+ *len = l;
+ return 1;
+}
+
+char *getvar_dhcp(const char *str, size_t len)
+{
+ const uint8_t *opt_str;
+ char *out = NULL;
+ int num, opt_len;
+
+ if (len >= 7 && memcmp(str, "option-", 7) == 0) {
+ len -= 7; str += 7;
+ num = 0;
+ while (len-- > 0)
+ num = num*10 + *str++ - '0';
+ if (find_dhcp_option(num, &opt_str, &opt_len)) {
+ out = malloc(opt_len + 1);
+ memcpy(out, opt_str, opt_len);
+ out[opt_len] = '\0';
+ }
+ }
+
+ return out;
+}
+
+char *getvar(const char *str, size_t len)
+{
+ char *out = NULL;
+ const char test[] = "UNKNOWN";
+
+ if (len >= 5 && memcmp(str, "dhcp/", 5) == 0) {
+ out = getvar_dhcp(str+5, len-5);
+ }
+
+ if (out == NULL) {
+ out = malloc(sizeof(test));
+ strcpy(out, test);
+ }
+
+ return out;
+}
+
+void append_output(char **out, size_t *outlen, const char *str, size_t len)
+{
+ size_t used;
+
+ used = (*out != NULL) ? strlen(*out) : 0;
+ if (used + len + 1 > *outlen) {
+ *outlen += 256 + (len & ~255);
+ *out = realloc(*out, *outlen);
+ }
+ memcpy(*out + used, str, len);
+ (*out)[used+len] = '\0';
+}
+
+char *do_subst(const char *str)
+{
+ int i;
+ enum {NORMAL, LITERAL, QUOTE} state = NORMAL;
+ char *out = NULL, *replacement;
+ const char *start, *stop;
+ size_t outlen = 0;
+
+ for (i = 0; str[i] != '\0'; i++) {
+ switch (state) {
+ case LITERAL:
+ append_output(&out, &outlen, str+i, 1);
+ state = NORMAL;
+ break;
+ case QUOTE:
+ if (str[i] == '\'') {
+ state = NORMAL;
+ break;
+ }
+ append_output(&out, &outlen, str+i, 1);
+ break;
+ case NORMAL:
+ if (str[i] == '\\') {
+ state = LITERAL;
+ break;
+ } else if (str[i] == '\'') {
+ state = QUOTE;
+ break;
+ } else if (str[i] != '$') {
+ append_output(&out, &outlen, str+i, 1);
+ break;
+ }
+ start = (str[i+1] == '{') ? str+i+1 : NULL;
+ stop = strchr(str+i+2, '}');
+ if (start == NULL || stop == NULL) {
+ append_output(&out, &outlen, str+i, 1);
+ break;
+ }
+ replacement = getvar(start + 1, stop-start-1);
+ append_output(&out, &outlen, replacement, strlen(replacement));
+ free(replacement);
+ i += stop-start+1;
+ break;
+ }
+ }
+
+ return out;
+}
+
+int main(int argc, char *argv[])
+{
+ int i;
+ char *out, *cmdline = NULL;
+ void *buf;
+ size_t cmd_len = 0;
+
+ openconsole(&dev_stdcon_r, &dev_stdcon_w);
+ if (pxe_get_cached_info(PXENV_PACKET_TYPE_DHCP_ACK, &buf, &dhcpack_len) != 0) {
+ fprintf(stderr, "failed to get dhcp infos\n");
+ return 1;
+ }
+ dhcpack = (bootp_t *)buf;
+
+ for (i = 1; i < argc; i++) {
+ out = do_subst(argv[i]);
+ append_output(&cmdline, &cmd_len, out, strlen(out));
+ free(out);
+ if (i+1 < argc)
+ append_output(&cmdline, &cmd_len, " ", 1);
+ }
+ printf("subst: cmdline=%s\n", cmdline);
+ if (memcmp(cmdline, ".localboot ", 11) == 0) {
+ syslinux_local_boot(strtol(cmdline+11, NULL, 0));
+ }
+ syslinux_run_command(cmdline);
+
+ return 0;
+}
More information about the Syslinux
mailing list