diff --git a/README.md b/README.md index a26218b..1966f2a 100644 --- a/README.md +++ b/README.md @@ -136,8 +136,9 @@ The following lists the options: Specify multiple times for more than one path to mount. -n, --hostname-pattern - Specify a hostname for the system. The pattern is a printf(3) - pattern. It is passed a unique ID for the board. E.g., "nerves-%.4s" + Specify a hostname for the system. The pattern supports a "%[-][.len]s" + where len is the length of the unique ID to use and the "-" controls + whether the ID is trimmed from the right or left. E.g., "nerves-%.4s" --pre-run-exec Run the specified command before Erlang starts @@ -201,7 +202,7 @@ The following lists the options: ``` If you're using this in combination with `Nerves` you can customize configration -via the application env as well. For details see the +via the application env as well. For details see the [related documentation](https://hexdocs.pm/nerves/advanced-configuration.html#overriding-erlinit-config-from-mix-config). ## Rebooting or hanging when the Erlang VM exits diff --git a/src/hostname.c b/src/hostname.c index a5efc09..3ecf2bb 100644 --- a/src/hostname.c +++ b/src/hostname.c @@ -83,6 +83,63 @@ static void make_rfc1123_compatible(char *s) *d = '\0'; } +static inline int digit2int(char digit) +{ + return digit - '0'; +} + +static inline int min(int a, int b) +{ + if (a < b) + return a; + else + return b; +} + +static void hostname_sprintf(char *output, const char *pattern, const char *serial) +{ + // Simple version of sprintf that supports a subset of patterns: + // %s - Include the whole string + // %.s - Include len characters of the string starting at the left + // %-.s - Include len characters of the string starting at the right + while (*pattern) { + if (*pattern == '%') { + pattern++; + const char *start = serial; + int max_len = strlen(serial); + int len; + if (pattern[0] == 's') { + pattern++; + len = max_len; + } else if (pattern[0] == '.' && isdigit(pattern[1]) && pattern[2] == 's') { + len = min(max_len, digit2int(pattern[1])); + pattern += 3; + } else if (pattern[0] == '.' && isdigit(pattern[1]) && isdigit(pattern[2]) && pattern[3] == 's') { + len = min(max_len, digit2int(pattern[1]) * 10 + digit2int(pattern[2])); + pattern += 4; + } else if (pattern[0] == '-' && pattern[1] == '.' && isdigit(pattern[2]) && pattern[3] == 's') { + len = min(max_len, digit2int(pattern[2])); + start += max_len - len; + pattern += 4; + } else if (pattern[0] == '-' && pattern[1] == '.' && isdigit(pattern[2]) && isdigit(pattern[3]) && pattern[4] == 's') { + len = min(max_len, digit2int(pattern[2]) * 10 + digit2int(pattern[3])); + start += max_len - len; + pattern += 5; + } else { + // Just give up on bad percent escapes. The config file is wrong and + // hopefully it will be obvious that the pattern was broke. Don't crash + // or error, since this shouldn't mess up booting. + break; + } + memcpy(output, start, len); + output += len; + } else { + *output++ = *pattern++; + } + } + *output = '\0'; +} + void configure_hostname() { debug("configure_hostname"); @@ -101,7 +158,7 @@ void configure_hostname() warn("`%s` failed. Using default ID: '%s'", options.uniqueid_exec, unique_id); } } - sprintf(hostname, options.hostname_pattern, unique_id); + hostname_sprintf(hostname, options.hostname_pattern, unique_id); kill_whitespace(hostname); make_rfc1123_compatible(hostname); } else { diff --git a/tests/010_uniqueid b/tests/010_uniqueid index 3671abd..aed3999 100644 --- a/tests/010_uniqueid +++ b/tests/010_uniqueid @@ -17,7 +17,7 @@ EOF cat >"$WORK/usr/bin/make-unique-id" <"$CONFIG" <"$WORK/usr/bin/make-unique-id" <"$EXPECTED" <"$CONFIG" <"$WORK/usr/bin/make-unique-id" <"$EXPECTED" <"$CONFIG" <"$WORK/usr/bin/make-unique-id" <"$EXPECTED" <"$CONFIG" <"$WORK/usr/bin/make-unique-id" <"$EXPECTED" <"$CONFIG" <"$WORK/usr/bin/make-unique-id" <"$EXPECTED" <