[SLOF] [PATCH v2 06/11] libnet: Add functions for downloading and parsing pxelinux.cfg files
Alexey Kardashevskiy
aik at ozlabs.ru
Fri May 25 16:45:50 AEST 2018
On 19/5/18 1:45 am, Thomas Huth wrote:
> Booting a kernel via pxelinux.cfg files is common on x86 and also with
> ppc64 bootloaders like petitboot, so it would be nice to support this
> in SLOF, too. This patch adds functions for downloading and parsing
> such pxelinux.cfg files. See this URL for more details on pxelinux.cfg:
> https://www.syslinux.org/wiki/index.php?title=PXELINUX
>
> Signed-off-by: Thomas Huth <thuth at redhat.com>
> ---
> lib/libnet/Makefile | 2 +-
> lib/libnet/pxelinux.c | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++
> lib/libnet/pxelinux.h | 33 ++++++++
> 3 files changed, 243 insertions(+), 1 deletion(-)
> create mode 100644 lib/libnet/pxelinux.c
> create mode 100644 lib/libnet/pxelinux.h
>
> diff --git a/lib/libnet/Makefile b/lib/libnet/Makefile
> index dfefea9..a2a6570 100644
> --- a/lib/libnet/Makefile
> +++ b/lib/libnet/Makefile
> @@ -19,7 +19,7 @@ include $(TOP)/make.rules
> CFLAGS += -I. -I.. -I../libc/include -I$(TOP)/include $(FLAG)
>
> SRCS = ethernet.c ipv4.c udp.c tcp.c dns.c bootp.c dhcp.c tftp.c \
> - ipv6.c dhcpv6.c icmpv6.c ndp.c netload.c ping.c args.c
> + ipv6.c dhcpv6.c icmpv6.c ndp.c netload.c ping.c args.c pxelinux.c
>
> OBJS = $(SRCS:%.c=%.o)
>
> diff --git a/lib/libnet/pxelinux.c b/lib/libnet/pxelinux.c
> new file mode 100644
> index 0000000..0c0b42e
> --- /dev/null
> +++ b/lib/libnet/pxelinux.c
> @@ -0,0 +1,209 @@
> +/*****************************************************************************
> + * pxelinux.cfg-style config file support.
> + *
> + * See https://www.syslinux.org/wiki/index.php?title=PXELINUX for information
> + * about the pxelinux config file layout.
> + *
> + * Copyright 2018 Red Hat, Inc.
> + *
> + * This program and the accompanying materials are made available under the
> + * terms of the BSD License which accompanies this distribution, and is
> + * available at http://www.opensource.org/licenses/bsd-license.php
> + *
> + * Contributors:
> + * Thomas Huth, Red Hat Inc. - initial implementation
> + *****************************************************************************/
> +
> +#include <stdio.h>
> +#include <string.h>
> +#include "tftp.h"
> +#include "pxelinux.h"
> +
> +/**
> + * Call tftp() and report errors (excet "file-not-found" errors)
> + */
> +static int pxelinux_tftp_load(filename_ip_t *fnip, void *buffer, int len)
> +{
> + tftp_err_t tftp_err;
> + int rc;
> +
> + rc = tftp(fnip, buffer, len, &tftp_err);
> +
> + if (rc > 0) {
> + printf("\r TFTP: Received %s (%d bytes)\n",
> + fnip->filename, rc);
> + } else if (rc == -3) {
> + /* Ignore file-not-found (since we are probing the files)
> + * and simply erase the "Receiving data: 0 KBytes" string */
> + printf("\r \r");
> + } else {
> + const char *errstr = NULL;
> + rc = tftp_get_error_info(fnip, &tftp_err, rc, &errstr, NULL);
> + if (errstr)
> + printf("\r TFTP error: %s\n", errstr);
> + }
> +
> + return rc;
> +}
> +
> +/**
> + * Try to load a pxelinux.cfg file by probing the possible file names.
> + */
> +static int pxelinux_load_cfg(filename_ip_t *fn_ip, uint8_t *mac, uint8_t *uuid,
> + char *cfgbuf, int cfgbufsize)
> +{
> + int rc, idx;
> + char basedir[sizeof(fn_ip->filename) - 40];
> + char *slash;
> +
> + cfgbuf[cfgbufsize - 1] = 0; /* Make sure it is NUL-terminated */
> +
> + /* Did we get a usable base directory via DHCP? */
> + slash = strrchr(fn_ip->filename, '/');
> + if (slash && slash - fn_ip->filename < sizeof(basedir) - 1) {
> + slash[1] = 0;
> + strcpy(basedir, fn_ip->filename);
> + } else {
> + strcpy(basedir, "pxelinux.cfg/");
> + }
> +
> + printf("Trying pxelinux.cfg files...\n");
> +
> + /* Try to load config file with name based on the VM UUID */
> + if (uuid) {
> + sprintf(fn_ip->filename, "%s%02x%02x%02x%02x-%02x%02x-"
> + "%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", basedir,
> + uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5],
> + uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11],
> + uuid[12], uuid[13], uuid[14], uuid[15]);
> + rc = pxelinux_tftp_load(fn_ip, cfgbuf, cfgbufsize - 1);
> + if (rc > 0) {
> + return rc;
> + }
> + }
This belongs to 11/11. Also, this UUID comes from QEMU's system-id DT
property which is formatted as xxxx-xx-xx-xx-xxxxxx (UUID_FMT in QEMU)
which is exactly the same as above, cannot you just use the plain string
from the property and avoid parsing/sprintf?
> +
> + /* Look for config file with MAC address in its name */
> + sprintf(fn_ip->filename, "%s%02x-%02x-%02x-%02x-%02x-%02x", basedir,
> + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
> + rc = pxelinux_tftp_load(fn_ip, cfgbuf, cfgbufsize - 1);
> + if (rc > 0) {
> + return rc;
> + }
> +
> + /* Look for config file with IP address in its name */
> + if (fn_ip->ip_version == 4) {
> + for (idx = 0; idx <= 7; idx++) {
> + sprintf(fn_ip->filename, "%s%02X%02X%02X%02X", basedir,
> + (fn_ip->own_ip >> 24) & 0xff,
> + (fn_ip->own_ip >> 16) & 0xff,
> + (fn_ip->own_ip >> 8) & 0xff,
> + fn_ip->own_ip & 0xff);
> + fn_ip->filename[strlen(fn_ip->filename) - idx] = 0;
> + rc = pxelinux_tftp_load(fn_ip, cfgbuf, cfgbufsize - 1);
> + if (rc > 0) {
> + return rc;
> + }
> + }
> + }
> +
> + /* Try "default" config file */
> + sprintf(fn_ip->filename, "%sdefault", basedir);
> + rc = pxelinux_tftp_load(fn_ip, cfgbuf, cfgbufsize - 1);
> +
> + return rc;
> +}
> +
> +/**
> + * Parse a pxelinux-style configuration file.
> + * @param cfg Pointer to the buffer with contents of the config file
> + * @param cfgsize Size of the cfg buffer
> + * @param entries Pointer to array where the results should be put into
> + * @param max_entries Number of available slots in the entries array
> + * @param def_ent Used to return the index of the default entry
> + * @return Number of valid entries
> + */
> +int pxelinux_parse_cfg(char *cfg, int cfgsize, struct lkia *entries,
> + int max_entries, int *def_ent)
> +{
> + int num_entries = 0;
> + char *ptr = cfg, *nextptr, *eol, *arg;
> + char *defaultlabel = NULL;
> +
> + *def_ent = 0;
> +
> + while (ptr < cfg + cfgsize && num_entries < max_entries) {
> + eol = strchr(ptr, '\n');
> + if (!eol) {
> + eol = cfg + cfgsize;
> + }
> + nextptr = eol + 1;
> + do {
> + *eol-- = '\0'; /* Remove spaces, tabs and returns */
> + } while (eol >= ptr &&
> + (*eol == '\r' || *eol == ' ' || *eol == '\t'));
> + while (*ptr == ' ' || *ptr == '\t') {
> + ptr++;
> + }
> + if (*ptr == 0 || *ptr == '#') {
> + goto nextline; /* Ignore comments and empty lines */
> + }
> + arg = strchr(ptr, ' '); /* Search space between cmnd and arg */
> + if (!arg) {
> + arg = strchr(ptr, '\t');
> + }
> + if (!arg) {
> + printf("Failed to parse this line:\n %s\n", ptr);
> + goto nextline;
> + }
> + *arg++ = 0;
> + while (*arg == ' ' || *arg == '\t') {
> + arg++;
> + }
> + if (!strcasecmp("default", ptr)) {
> + defaultlabel = arg;
> + } else if (!strcasecmp("label", ptr)) {
> + entries[num_entries].label = arg;
> + if (defaultlabel && !strcmp(arg, defaultlabel)) {
> + *def_ent = num_entries;
> + }
> + num_entries++;
> + } else if (!strcasecmp("kernel", ptr) && num_entries > 0) {
> + entries[num_entries - 1].kernel = arg;
> + } else if (!strcasecmp("initrd", ptr) && num_entries > 0) {
> + entries[num_entries - 1].initrd = arg;
> + } else if (!strcasecmp("append", ptr) && num_entries > 0) {
> + entries[num_entries - 1].append = arg;
> + } else {
> + printf("Command '%s' is not supported.\n", ptr);
> + }
> +nextline:
> + ptr = nextptr;
> + }
> +
> + return num_entries;
> +}
> +
> +/**
> + * Try to load and parse a pxelinux-style configuration file.
> + * @param fn_ip must contain server and client IP information
> + * @param mac MAC address which should be used for probing
> + * @param uuid UUID which should be used for probing (can be NULL)
> + * @param cfgbuf Pointer to the buffer where config file should be loaded
> + * @param cfgsize Size of the cfgbuf buffer
> + * @param entries Pointer to array where the results should be put into
> + * @param max_entries Number of available slots in the entries array
> + * @param def_ent Used to return the index of the default entry
> + * @return Number of valid entries
> + */
> +int pxelinux_load_parse_cfg(filename_ip_t *fn_ip, uint8_t *mac, uint8_t *uuid,
> + char *cfgbuf, int cfgsize, struct lkia *entries,
> + int max_entries, int *def_ent)
> +{
> + int rc;
> +
> + rc = pxelinux_load_cfg(fn_ip, mac, uuid, cfgbuf, cfgsize);
> + if (rc < 0)
> + return rc;
> +
> + return pxelinux_parse_cfg(cfgbuf, rc, entries, max_entries, def_ent);
> +}
> diff --git a/lib/libnet/pxelinux.h b/lib/libnet/pxelinux.h
> new file mode 100644
> index 0000000..9fd1b2f
> --- /dev/null
> +++ b/lib/libnet/pxelinux.h
> @@ -0,0 +1,33 @@
> +/*****************************************************************************
> + * Definitions for pxelinux-style config file support
> + *
> + * Copyright 2018 Red Hat, Inc.
> + *
> + * This program and the accompanying materials
> + * are made available under the terms of the BSD License
> + * which accompanies this distribution, and is available at
> + * http://www.opensource.org/licenses/bsd-license.php
> + *
> + * Contributors:
> + * Thomas Huth, Red Hat Inc. - initial implementation
> + *****************************************************************************/
> +
> +#ifndef LIBNET_PXELINUX_H
> +#define LIBNET_PXELINUX_H
> +
> +/* This structure holds the data from one pxelinux.cfg file entry */
> +struct lkia {
> + const char *label;
> + const char *kernel;
> + const char *initrd;
> + const char *append;
> +};
> +
> +int pxelinux_parse_cfg(char *cfg, int cfgsize, struct lkia *entries,
> + int max_entries, int *def_ent);
> +int pxelinux_load_parse_cfg(filename_ip_t *fn_ip, uint8_t *mac, uint8_t *uuid,
> + char *cfgbuf, int cfgsize, struct lkia *entries,
> + int max_entries, int *def_ent);
> +
> +
> +#endif
>
--
Alexey
More information about the SLOF
mailing list