Skip to content

shims: add a shim for gethostname#5145

Open
faukah wants to merge 1 commit into
rust-lang:masterfrom
faukah:faukah/push-toxnyonrrnys
Open

shims: add a shim for gethostname#5145
faukah wants to merge 1 commit into
rust-lang:masterfrom
faukah:faukah/push-toxnyonrrnys

Conversation

@faukah

@faukah faukah commented Jun 28, 2026

Copy link
Copy Markdown

Add a shim for gethostname().
Closes #5138.

Behavior differs between targets, so I had to match on Os and on Glibc/Musl. I don't know whether throw_unsup_format!() is the correct way of handling all other cases.

I'm not quite sure how Solaris behaves, but I assume it matches the behavior of Illumos. Also not quite sure if MacOS behavior is correct. It can in theory return ENAMETOOLONG, but docs and source clash a bit here:

Manpage:

[ENAMETOOLONG] The current host name is longer than namelen. (For gethostname() only.)

Source code:

int
gethostname(char *name, size_t namelen)
{
	int mib[2];

	mib[0] = CTL_KERN;
	mib[1] = KERN_HOSTNAME;
	if (namelen < MAXHOSTNAMELEN + 1) {
		char local_buf[MAXHOSTNAMELEN + 1];
		size_t local_len = sizeof(local_buf);
		if (sysctl(mib, 2, local_buf, &local_len, NULL, 0) == -1) {
			if (errno == ENOMEM)
				errno = ENAMETOOLONG;
			return (-1);
		}
		strncpy(name, local_buf, namelen);
		name[namelen - 1] = 0;
	} else {
	if (sysctl(mib, 2, name, &namelen, NULL, 0) == -1) {
		if (errno == ENOMEM)
			errno = ENAMETOOLONG;
		return (-1);
		}
	}
	return (0);
}

As far as I can see, if sysctl() succeeds, i.e. the hostname is copied into local_buf, and name is too small, it still would put a NULL-terminated part of the hostname into name and return successfully.

A few notes on the match statement:

@ Android

(Os::Android, _) => this.set_errno_and_return_neg1_i32(LibcError("ENAMETOOLONG")),

Android returns error code ENAMETOOLONG without writing anything to name, see here:

int gethostname(char* buf, size_t n) {
  utsname name = {};
  uname(&name);

  size_t name_length = static_cast<size_t>(strlen(name.nodename) + 1);
  if (name_length > n) {
    errno = ENAMETOOLONG;
    return -1;
  }

  memcpy(buf, name.nodename, name_length);
  return 0;
}

@ GNU libc, FreeBSD

GNU libc and FreeBSD behave the same, see libc source and FreeBSD source. Both GNU libc and FreeBSD write a trunctuated, non-NULL-terminated prefix of the hostname before returning:

(Os::Linux, Env::Gnu) | (Os::FreeBsd, _) => {
    let len: usize = len.try_into().unwrap();
    this.write_bytes_ptr(name, hostname[..len].iter().copied())?;
    this.set_errno_and_return_neg1_i32(LibcError("ENAMETOOLONG"))
}

GNU libc:

node_len = strlen (buf.nodename) + 1;
memcpy (name, buf.nodename, len < node_len ? len : node_len);

if (node_len > len)
  {
    __set_errno (ENAMETOOLONG);
    return -1;
  }

FreeBSD:

int mib[2];

mib[0] = CTL_KERN;
mib[1] = KERN_HOSTNAME;
if (sysctl(mib, 2, name, &namelen, NULL, 0) == -1) {
	if (errno == ENOMEM)
		errno = ENAMETOOLONG;
	return (-1);
}

@ Musl, MacOS, Solaris, Illumos

(Os::Linux, _) | (Os::MacOs, _) | (Os::Solaris, _) | (Os::Illumos, _) => {
    if len > 0 {
        let len: usize = len.try_into().unwrap();
        this.write_bytes_ptr(
            name,
            hostname[..len - 1].iter().copied().chain(std::iter::once(0)),
        )?;
    }
    interp_ok(Scalar::from_i32(0))
}

Musl libc's implementation is actually different from GNU libc, it adds a NULL byte at the end:

if (len > sizeof uts.nodename) len = sizeof uts.nodename;
for (i=0; i<len && (name[i] = uts.nodename[i]); i++);
if (i && i==len) name[i-1] = 0;
}

In cases where hostname length == buffer length, we get different behavior:

$ ./gethostname_musl.o
ret=0 errno=0 (No error information)
bytes=74 65 6d 00
chars=tem\0

$ ./gethostname_glibc.o
ret=-1 errno=36 (File name too long)
bytes=74 65 6d 70
chars=temp

MacOS also NULL-terminates the name:

char local_buf[MAXHOSTNAMELEN + 1];
size_t local_len = sizeof(local_buf);
if (sysctl(mib, 2, local_buf, &local_len, NULL, 0) == -1) {
	if (errno == ENOMEM)
		errno = ENAMETOOLONG;
	return (-1);
}
strncpy(name, local_buf, namelen);
name[namelen - 1] = 0;

Illumos uses the systeminfo syscall:

return (sysinfo(SI_HOSTNAME, name, namelen) == -1 ? -1 : 0);

And systeminfo also NULL-terminates the buffer when it's too small:

if (kstr != NULL) {
		strcnt = strlen(kstr);
		if (count > 0) {
			if (count <= strcnt) {
				getcnt = count - 1;
				if (subyte(buf + getcnt, 0) < 0)
					return (set_errno(EFAULT));
			} else {
				getcnt = strcnt + 1;
			}
			if (copyout(kstr, buf, getcnt))
				return (set_errno(EFAULT));
		}
		return (strcnt + 1);
	}

Relevant manpages:

@rustbot rustbot added the S-waiting-on-review Status: Waiting for a review to complete label Jun 28, 2026
@faukah faukah changed the title shims: add a shim for gethostname systemcall shims: add a shim for gethostname Jun 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: Waiting for a review to complete

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missing support for gethostname

2 participants