Add {get,set}domainname(2)
{get,set}domainname aren't in POSIX but are widely-implemented
extensions.
The Linux kernel provides a setdomainname syscall but not a symmetric
getdomainname syscall, since it expects userspace to get the domain name
from uname(2).
Change-Id: I96726c242f4bb646c130b361688328b0b97269a0
Signed-off-by: Greg Hackmann <ghackmann@google.com>
diff --git a/libc/Android.mk b/libc/Android.mk
index 828a227..ffa8a27 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -109,6 +109,7 @@
bionic/ftruncate.cpp \
bionic/futimens.cpp \
bionic/getcwd.cpp \
+ bionic/getdomainname.cpp \
bionic/gethostname.cpp \
bionic/getpgrp.cpp \
bionic/getpid.cpp \
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index d5dd206..b774dbc 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -327,6 +327,7 @@
int setfsgid(gid_t) all
int setfsuid(uid_t) all
+int setdomainname(const char*, size_t) all
int sethostname(const char*, size_t) all
pid_t wait4(pid_t, int*, int, struct rusage*) all
diff --git a/libc/arch-arm/syscalls/setdomainname.S b/libc/arch-arm/syscalls/setdomainname.S
new file mode 100644
index 0000000..20f1f6f
--- /dev/null
+++ b/libc/arch-arm/syscalls/setdomainname.S
@@ -0,0 +1,14 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(setdomainname)
+ mov ip, r7
+ ldr r7, =__NR_setdomainname
+ swi #0
+ mov r7, ip
+ cmn r0, #(MAX_ERRNO + 1)
+ bxls lr
+ neg r0, r0
+ b __set_errno_internal
+END(setdomainname)
diff --git a/libc/arch-arm64/syscalls/setdomainname.S b/libc/arch-arm64/syscalls/setdomainname.S
new file mode 100644
index 0000000..18f8d4b
--- /dev/null
+++ b/libc/arch-arm64/syscalls/setdomainname.S
@@ -0,0 +1,14 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(setdomainname)
+ mov x8, __NR_setdomainname
+ svc #0
+
+ cmn x0, #(MAX_ERRNO + 1)
+ cneg x0, x0, hi
+ b.hi __set_errno_internal
+
+ ret
+END(setdomainname)
diff --git a/libc/arch-mips/syscalls/setdomainname.S b/libc/arch-mips/syscalls/setdomainname.S
new file mode 100644
index 0000000..ec0504c
--- /dev/null
+++ b/libc/arch-mips/syscalls/setdomainname.S
@@ -0,0 +1,19 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(setdomainname)
+ .set noreorder
+ .cpload t9
+ li v0, __NR_setdomainname
+ syscall
+ bnez a3, 1f
+ move a0, v0
+ j ra
+ nop
+1:
+ la t9,__set_errno_internal
+ j t9
+ nop
+ .set reorder
+END(setdomainname)
diff --git a/libc/arch-mips64/syscalls/setdomainname.S b/libc/arch-mips64/syscalls/setdomainname.S
new file mode 100644
index 0000000..6cee88e
--- /dev/null
+++ b/libc/arch-mips64/syscalls/setdomainname.S
@@ -0,0 +1,25 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(setdomainname)
+ .set push
+ .set noreorder
+ li v0, __NR_setdomainname
+ syscall
+ bnez a3, 1f
+ move a0, v0
+ j ra
+ nop
+1:
+ move t0, ra
+ bal 2f
+ nop
+2:
+ .cpsetup ra, t1, 2b
+ LA t9,__set_errno_internal
+ .cpreturn
+ j t9
+ move ra, t0
+ .set pop
+END(setdomainname)
diff --git a/libc/arch-x86/syscalls/setdomainname.S b/libc/arch-x86/syscalls/setdomainname.S
new file mode 100644
index 0000000..57189fe
--- /dev/null
+++ b/libc/arch-x86/syscalls/setdomainname.S
@@ -0,0 +1,26 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(setdomainname)
+ pushl %ebx
+ .cfi_def_cfa_offset 8
+ .cfi_rel_offset ebx, 0
+ pushl %ecx
+ .cfi_adjust_cfa_offset 4
+ .cfi_rel_offset ecx, 0
+ mov 12(%esp), %ebx
+ mov 16(%esp), %ecx
+ movl $__NR_setdomainname, %eax
+ int $0x80
+ cmpl $-MAX_ERRNO, %eax
+ jb 1f
+ negl %eax
+ pushl %eax
+ call __set_errno_internal
+ addl $4, %esp
+1:
+ popl %ecx
+ popl %ebx
+ ret
+END(setdomainname)
diff --git a/libc/arch-x86_64/syscalls/setdomainname.S b/libc/arch-x86_64/syscalls/setdomainname.S
new file mode 100644
index 0000000..40d1a33
--- /dev/null
+++ b/libc/arch-x86_64/syscalls/setdomainname.S
@@ -0,0 +1,15 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(setdomainname)
+ movl $__NR_setdomainname, %eax
+ syscall
+ cmpq $-MAX_ERRNO, %rax
+ jb 1f
+ negl %eax
+ movl %eax, %edi
+ call __set_errno_internal
+1:
+ ret
+END(setdomainname)
diff --git a/libc/bionic/getdomainname.cpp b/libc/bionic/getdomainname.cpp
new file mode 100644
index 0000000..761a999
--- /dev/null
+++ b/libc/bionic/getdomainname.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+int getdomainname(char* name, size_t len) {
+ utsname uts;
+ if (uname(&uts) == -1) return -1;
+
+ // Note: getdomainname()'s behavior varies across implementations when len is
+ // too small. bionic follows the historical libc policy of returning EINVAL,
+ // instead of glibc's policy of copying the first len bytes without a NULL
+ // terminator.
+ if (strlen(uts.domainname) >= len) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ strncpy(name, uts.domainname, len);
+ return 0;
+}
diff --git a/libc/include/unistd.h b/libc/include/unistd.h
index 5045267..2d62c1f 100644
--- a/libc/include/unistd.h
+++ b/libc/include/unistd.h
@@ -292,6 +292,9 @@
__errordecl(__readlinkat_size_toobig_error, "readlinkat called with size > SSIZE_MAX");
extern ssize_t __readlinkat_real(int dirfd, const char*, char*, size_t) __RENAME(readlinkat);
+extern int getdomainname(char*, size_t);
+extern int setdomainname(const char*, size_t);
+
#if defined(__BIONIC_FORTIFY)
__BIONIC_FORTIFY_INLINE
diff --git a/libc/libc.arm.brillo.map b/libc/libc.arm.brillo.map
index bb5aaa0..2776b91 100644
--- a/libc/libc.arm.brillo.map
+++ b/libc/libc.arm.brillo.map
@@ -1273,7 +1273,9 @@
LIBC_O {
global:
+ getdomainname;
pthread_getname_np;
+ setdomainname;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index e77471e..5f7ae2d 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1273,7 +1273,9 @@
LIBC_O {
global:
+ getdomainname;
pthread_getname_np;
+ setdomainname;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index cef6d4a..0890b59 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1195,7 +1195,9 @@
LIBC_O {
global:
+ getdomainname;
pthread_getname_np;
+ setdomainname;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 1f523d7..38edff0 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1299,7 +1299,9 @@
LIBC_O {
global:
+ getdomainname;
pthread_getname_np;
+ setdomainname;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/libc/libc.mips.brillo.map b/libc/libc.mips.brillo.map
index bdd9faa..c5b1bd3 100644
--- a/libc/libc.mips.brillo.map
+++ b/libc/libc.mips.brillo.map
@@ -1257,7 +1257,9 @@
LIBC_O {
global:
+ getdomainname;
pthread_getname_np;
+ setdomainname;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index 08a0a1c..dde4cb4 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1257,7 +1257,9 @@
LIBC_O {
global:
+ getdomainname;
pthread_getname_np;
+ setdomainname;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index cef6d4a..0890b59 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1195,7 +1195,9 @@
LIBC_O {
global:
+ getdomainname;
pthread_getname_np;
+ setdomainname;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/libc/libc.x86.brillo.map b/libc/libc.x86.brillo.map
index d50fd48..eb99079 100644
--- a/libc/libc.x86.brillo.map
+++ b/libc/libc.x86.brillo.map
@@ -1256,7 +1256,9 @@
LIBC_O {
global:
+ getdomainname;
pthread_getname_np;
+ setdomainname;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index 667ee63..8f6c64e 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1256,7 +1256,9 @@
LIBC_O {
global:
+ getdomainname;
pthread_getname_np;
+ setdomainname;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index cef6d4a..0890b59 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1195,7 +1195,9 @@
LIBC_O {
global:
+ getdomainname;
pthread_getname_np;
+ setdomainname;
} LIBC_N;
LIBC_PRIVATE {
diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp
index 62e39fd..79b0fa5 100644
--- a/tests/unistd_test.cpp
+++ b/tests/unistd_test.cpp
@@ -25,6 +25,7 @@
#include <fcntl.h>
#include <limits.h>
#include <stdint.h>
+#include <sys/capability.h>
#include <sys/param.h>
#include <sys/syscall.h>
#include <sys/types.h>
@@ -986,3 +987,46 @@
ASSERT_EQ(file_size/2, lseek64(tf.fd, file_size/2, SEEK_SET));
ASSERT_EQ(0, lockf64(tf.fd, F_TLOCK, file_size/2));
}
+
+TEST(UNISTD_TEST, getdomainname) {
+ struct utsname u;
+ ASSERT_EQ(0, uname(&u));
+
+ char buf[sizeof(u.domainname)];
+ ASSERT_EQ(0, getdomainname(buf, sizeof(buf)));
+ EXPECT_STREQ(u.domainname, buf);
+
+#if defined(__BIONIC__)
+ // bionic and glibc have different behaviors when len is too small
+ ASSERT_EQ(-1, getdomainname(buf, strlen(u.domainname)));
+ EXPECT_EQ(EINVAL, errno);
+#endif
+}
+
+TEST(UNISTD_TEST, setdomainname) {
+ __user_cap_header_struct header;
+ memset(&header, 0, sizeof(header));
+ header.version = _LINUX_CAPABILITY_VERSION_3;
+
+ __user_cap_data_struct old_caps[_LINUX_CAPABILITY_U32S_3];
+ ASSERT_EQ(0, capget(&header, &old_caps[0]));
+
+ auto admin_idx = CAP_TO_INDEX(CAP_SYS_ADMIN);
+ auto admin_mask = CAP_TO_MASK(CAP_SYS_ADMIN);
+ bool has_admin = old_caps[admin_idx].effective & admin_mask;
+ if (has_admin) {
+ __user_cap_data_struct new_caps[_LINUX_CAPABILITY_U32S_3];
+ memcpy(new_caps, old_caps, sizeof(new_caps));
+ new_caps[admin_idx].effective &= ~admin_mask;
+
+ ASSERT_EQ(0, capset(&header, &new_caps[0])) << "failed to drop admin privileges";
+ }
+
+ const char* name = "newdomainname";
+ ASSERT_EQ(-1, setdomainname(name, strlen(name)));
+ ASSERT_EQ(EPERM, errno);
+
+ if (has_admin) {
+ ASSERT_EQ(0, capset(&header, &old_caps[0])) << "failed to restore admin privileges";
+ }
+}