Skip to content

Commit

Permalink
Extend the printf pattern for hostnames for left trimming
Browse files Browse the repository at this point in the history
This provides more flexibility in specifying hostname patterns that
include all or part of the board's serial number. Before this change,
"%s" and "%.4s" (4 is the number of characters to use of the serial
number) were supported. After this change, the printf pattern also
supports "%-.4s" to use the rightmost characters in the board's serial
number. This is an extension to printf. I found one reference to someone
else doing this based on web searches and it was the only hit, so it
doesn't look like there's any precedence to follow.

While making this change, the custom sprintf was dumbed down to only
support these escapes and nothing else. This might prevent a crash.
  • Loading branch information
fhunleth committed Jul 7, 2024
1 parent b85e4af commit 214e5d6
Show file tree
Hide file tree
Showing 8 changed files with 545 additions and 7 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,9 @@ The following lists the options:
Specify multiple times for more than one path to mount.
-n, --hostname-pattern <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 <program and arguments>
Run the specified command before Erlang starts
Expand Down Expand Up @@ -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
Expand Down
59 changes: 58 additions & 1 deletion src/hostname.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
// %.<len>s - Include len characters of the string starting at the left
// %-.<len>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");
Expand All @@ -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 {
Expand Down
6 changes: 3 additions & 3 deletions tests/010_uniqueid
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ EOF
cat >"$WORK/usr/bin/make-unique-id" <<EOF
#!/usr/bin/env bash
echo "0042"
echo "12345678"
EOF
chmod +x "$WORK/usr/bin/make-unique-id"

Expand Down Expand Up @@ -54,8 +54,8 @@ fixture: ioctl(SIOCSIFFLAGS)
fixture: ioctl(SIOCGIFINDEX)
erlinit: configure_hostname
erlinit: system_cmd '/usr/bin/make-unique-id'
erlinit: Hostname: nerves-0042
fixture: sethostname("nerves-0042", 11)
erlinit: Hostname: nerves-1234
fixture: sethostname("nerves-1234", 11)
fixture: mkdir("/root/seedrng", 700)
erlinit: Saving 256 bits of creditable seed for next boot
erlinit: Env: 'HOME=/home/user0'
Expand Down
96 changes: 96 additions & 0 deletions tests/061_uniqueid_full
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env bash

#
# Test that calling out to a unique id generator does the right thing
#

cat >"$CONFIG" <<EOF
-v
# Specify a hostname pattern that has a unique id part
--hostname-pattern nerves-%s-
# Call out to a dummy unique id generator
--uniqueid-exec "/usr/bin/make-unique-id"
EOF

cat >"$WORK/usr/bin/make-unique-id" <<EOF
#!/usr/bin/env bash
echo "12345678"
EOF
chmod +x "$WORK/usr/bin/make-unique-id"

cat >"$EXPECTED" <<EOF
erlinit: cmdline argc=1, merged argc=6
erlinit: merged argv[0]=/sbin/init
erlinit: merged argv[1]=-v
erlinit: merged argv[2]=--hostname-pattern
erlinit: merged argv[3]=nerves-%s-
erlinit: merged argv[4]=--uniqueid-exec
erlinit: merged argv[5]=/usr/bin/make-unique-id
fixture: mount("proc", "/proc", "proc", 14, data)
fixture: mount("sysfs", "/sys", "sysfs", 14, data)
fixture: mount("devtmpfs", "/dev", "devtmpfs", 42, data)
fixture: mkdir("/dev/pts", 755)
fixture: mkdir("/dev/shm", 1777)
fixture: mount("devpts", "/dev/pts", "devpts", 10, data)
fixture: symlink("/dev/mmcblk0","/dev/rootdisk0")
fixture: symlink("/dev/mmcblk0p4","/dev/rootdisk0p4")
fixture: symlink("/dev/mmcblk0p3","/dev/rootdisk0p3")
fixture: symlink("/dev/mmcblk0p2","/dev/rootdisk0p2")
fixture: symlink("/dev/mmcblk0p1","/dev/rootdisk0p1")
erlinit: set_ctty
fixture: setsid()
fixture: mount("tmpfs", "/tmp", "tmpfs", 14, data)
fixture: mount("tmpfs", "/run", "tmpfs", 14, data)
erlinit: find_release
erlinit: No release found in /srv/erlang.
erlinit: find_erts_directory
erlinit: setup_environment
erlinit: setup_networking
fixture: ioctl(SIOCGIFFLAGS)
fixture: ioctl(SIOCSIFFLAGS)
fixture: ioctl(SIOCGIFINDEX)
erlinit: configure_hostname
erlinit: system_cmd '/usr/bin/make-unique-id'
erlinit: Hostname: nerves-12345678-
fixture: sethostname("nerves-12345678-", 16)
fixture: mkdir("/root/seedrng", 700)
erlinit: Saving 256 bits of creditable seed for next boot
erlinit: Env: 'HOME=/home/user0'
erlinit: Env: 'PATH=/usr/sbin:/usr/bin:/sbin:/bin'
erlinit: Env: 'TERM=xterm-256color'
erlinit: Env: 'ROOTDIR=/usr/lib/erlang'
erlinit: Env: 'BINDIR=/usr/lib/erlang/erts-6.0/bin'
erlinit: Env: 'EMU=beam'
erlinit: Env: 'PROGNAME=erlexec'
erlinit: Arg: 'erlexec'
erlinit: Arg: '-boot_var'
erlinit: Arg: 'RELEASE_LIB'
erlinit: Arg: '/srv/erlang/lib'
erlinit: Launching erl...
Hello from erlexec
erlinit: Erlang VM exited
erlinit: kill_all
erlinit: Sending SIGTERM to all processes
fixture: kill(-1, 15)
fixture: sleep(1)
erlinit: Sending SIGKILL to all processes
fixture: kill(-1, 9)
fixture: mkdir("/root/seedrng", 700)
erlinit: Seeding 256 bits and crediting
fixture: ioctl(RNDADDENTROPY)
erlinit: Saving 256 bits of creditable seed for next boot
erlinit: unmount_all
erlinit: unmounting tmpfs at /sys/fs/cgroup...
fixture: umount("/sys/fs/cgroup")
erlinit: unmounting tmpfs at /dev/shm...
fixture: umount("/dev/shm")
erlinit: unmounting devpts at /dev/pts...
fixture: umount("/dev/pts")
erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
EOF
96 changes: 96 additions & 0 deletions tests/062_uniqueid_right
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env bash

#
# Test that calling out to a unique id generator does the right thing
#

cat >"$CONFIG" <<EOF
-v
# Specify a hostname pattern that has a unique id part
--hostname-pattern nerves-%-.4s-
# Call out to a dummy unique id generator
--uniqueid-exec "/usr/bin/make-unique-id"
EOF

cat >"$WORK/usr/bin/make-unique-id" <<EOF
#!/usr/bin/env bash
echo "12345678"
EOF
chmod +x "$WORK/usr/bin/make-unique-id"

cat >"$EXPECTED" <<EOF
erlinit: cmdline argc=1, merged argc=6
erlinit: merged argv[0]=/sbin/init
erlinit: merged argv[1]=-v
erlinit: merged argv[2]=--hostname-pattern
erlinit: merged argv[3]=nerves-%-.4s-
erlinit: merged argv[4]=--uniqueid-exec
erlinit: merged argv[5]=/usr/bin/make-unique-id
fixture: mount("proc", "/proc", "proc", 14, data)
fixture: mount("sysfs", "/sys", "sysfs", 14, data)
fixture: mount("devtmpfs", "/dev", "devtmpfs", 42, data)
fixture: mkdir("/dev/pts", 755)
fixture: mkdir("/dev/shm", 1777)
fixture: mount("devpts", "/dev/pts", "devpts", 10, data)
fixture: symlink("/dev/mmcblk0","/dev/rootdisk0")
fixture: symlink("/dev/mmcblk0p4","/dev/rootdisk0p4")
fixture: symlink("/dev/mmcblk0p3","/dev/rootdisk0p3")
fixture: symlink("/dev/mmcblk0p2","/dev/rootdisk0p2")
fixture: symlink("/dev/mmcblk0p1","/dev/rootdisk0p1")
erlinit: set_ctty
fixture: setsid()
fixture: mount("tmpfs", "/tmp", "tmpfs", 14, data)
fixture: mount("tmpfs", "/run", "tmpfs", 14, data)
erlinit: find_release
erlinit: No release found in /srv/erlang.
erlinit: find_erts_directory
erlinit: setup_environment
erlinit: setup_networking
fixture: ioctl(SIOCGIFFLAGS)
fixture: ioctl(SIOCSIFFLAGS)
fixture: ioctl(SIOCGIFINDEX)
erlinit: configure_hostname
erlinit: system_cmd '/usr/bin/make-unique-id'
erlinit: Hostname: nerves-5678-
fixture: sethostname("nerves-5678-", 12)
fixture: mkdir("/root/seedrng", 700)
erlinit: Saving 256 bits of creditable seed for next boot
erlinit: Env: 'HOME=/home/user0'
erlinit: Env: 'PATH=/usr/sbin:/usr/bin:/sbin:/bin'
erlinit: Env: 'TERM=xterm-256color'
erlinit: Env: 'ROOTDIR=/usr/lib/erlang'
erlinit: Env: 'BINDIR=/usr/lib/erlang/erts-6.0/bin'
erlinit: Env: 'EMU=beam'
erlinit: Env: 'PROGNAME=erlexec'
erlinit: Arg: 'erlexec'
erlinit: Arg: '-boot_var'
erlinit: Arg: 'RELEASE_LIB'
erlinit: Arg: '/srv/erlang/lib'
erlinit: Launching erl...
Hello from erlexec
erlinit: Erlang VM exited
erlinit: kill_all
erlinit: Sending SIGTERM to all processes
fixture: kill(-1, 15)
fixture: sleep(1)
erlinit: Sending SIGKILL to all processes
fixture: kill(-1, 9)
fixture: mkdir("/root/seedrng", 700)
erlinit: Seeding 256 bits and crediting
fixture: ioctl(RNDADDENTROPY)
erlinit: Saving 256 bits of creditable seed for next boot
erlinit: unmount_all
erlinit: unmounting tmpfs at /sys/fs/cgroup...
fixture: umount("/sys/fs/cgroup")
erlinit: unmounting tmpfs at /dev/shm...
fixture: umount("/dev/shm")
erlinit: unmounting devpts at /dev/pts...
fixture: umount("/dev/pts")
erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
EOF
Loading

0 comments on commit 214e5d6

Please sign in to comment.