diff --git a/libc/bionic/pty.cpp b/libc/bionic/pty.cpp index 995e00608..2c8618047 100644 --- a/libc/bionic/pty.cpp +++ b/libc/bionic/pty.cpp @@ -28,13 +28,15 @@ #include #include +#include #include #include #include #include #include +#include -int getpt(void) { +int getpt() { return posix_openpt(O_RDWR|O_NOCTTY); } @@ -47,7 +49,7 @@ int posix_openpt(int flags) { } char* ptsname(int fd) { - static char buf[64]; + static char buf[32]; return ptsname_r(fd, buf, sizeof(buf)) == 0 ? buf : NULL; } @@ -105,3 +107,83 @@ int unlockpt(int fd) { int unlock = 0; return ioctl(fd, TIOCSPTLCK, &unlock); } + +int openpty(int* master, int* slave, char* name, const termios* t, const winsize* ws) { + *master = getpt(); + if (*master == -1) { + return -1; + } + + if (grantpt(*master) == -1 || unlockpt(*master) == -1) { + close(*master); + return -1; + } + + char buf[32]; + if (name == NULL) { + name = buf; + } + if (ptsname_r(*master, name, sizeof(buf)) != 0) { + close(*master); + return -1; + } + + *slave = open(name, O_RDWR|O_NOCTTY); + if (*slave == -1) { + close(*master); + return -1; + } + + if (t != NULL) { + tcsetattr(*slave, TCSAFLUSH, t); + } + if (ws != NULL) { + ioctl(*slave, TIOCSWINSZ, ws); + } + + return 0; +} + +int forkpty(int* master, char* name, const termios* t, const winsize* ws) { + int slave; + if (openpty(master, &slave, name, t, ws) == -1) { + return -1; + } + + pid_t pid = fork(); + if (pid == -1) { + close(*master); + close(slave); + return -1; + } + + if (pid == 0) { + // Child. + close(*master); + if (login_tty(slave) == -1) { + _exit(1); + } + return 0; + } + + // Parent. + close(slave); + return pid; +} + +int login_tty(int fd) { + setsid(); + + if (ioctl(fd, TIOCSCTTY, NULL) == -1) { + return -1; + } + + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > STDERR_FILENO) { + close(fd); + } + + return 0; +} diff --git a/libc/include/pty.h b/libc/include/pty.h new file mode 100644 index 000000000..bca113752 --- /dev/null +++ b/libc/include/pty.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef _PTY_H +#define _PTY_H + +#include + +#include +#include + +__BEGIN_DECLS + +int openpty(int*, int*, char*, const struct termios*, const struct winsize*); +int forkpty(int*, char*, const struct termios*, const struct winsize*); + +__END_DECLS + +#endif /* _PTY_H */ diff --git a/libc/include/utmp.h b/libc/include/utmp.h index d76422781..ebf23726a 100644 --- a/libc/include/utmp.h +++ b/libc/include/utmp.h @@ -91,6 +91,8 @@ int utmpname(const char*); void setutent(); struct utmp* getutent(); +int login_tty(int); + __END_DECLS #endif /* _UTMP_H_ */ diff --git a/tests/Android.mk b/tests/Android.mk index 93ab9bde6..3f5a11260 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -72,6 +72,7 @@ libBionicStandardTests_src_files := \ mntent_test.cpp \ netdb_test.cpp \ pthread_test.cpp \ + pty_test.cpp \ regex_test.cpp \ sched_test.cpp \ search_test.cpp \ @@ -108,6 +109,7 @@ libBionicStandardTests_src_files := \ uchar_test.cpp \ uniqueptr_test.cpp \ unistd_test.cpp \ + utmp_test.cpp \ wchar_test.cpp \ libBionicStandardTests_cflags := \ @@ -309,7 +311,7 @@ bionic-unit-tests-glibc_whole_static_libraries := \ libBionicStandardTests \ bionic-unit-tests-glibc_ldlibs := \ - -lrt -ldl \ + -lrt -ldl -lutil \ bionic-unit-tests-glibc_c_includes := \ bionic/libc \ diff --git a/tests/pty_test.cpp b/tests/pty_test.cpp new file mode 100644 index 000000000..7fe97e9af --- /dev/null +++ b/tests/pty_test.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +TEST(pty, openpty) { + int master, slave; + char name[32]; + struct winsize w = { 123, 456, 9999, 999 }; + ASSERT_EQ(0, openpty(&master, &slave, name, NULL, &w)); + ASSERT_NE(-1, master); + ASSERT_NE(-1, slave); + ASSERT_NE(master, slave); + + char tty_name[32]; + ASSERT_EQ(0, ttyname_r(slave, tty_name, sizeof(tty_name))); + ASSERT_STREQ(tty_name, name); + + struct winsize w_actual; + ASSERT_EQ(0, ioctl(slave, TIOCGWINSZ, &w_actual)); + ASSERT_EQ(w_actual.ws_row, w.ws_row); + ASSERT_EQ(w_actual.ws_col, w.ws_col); + ASSERT_EQ(w_actual.ws_xpixel, w.ws_xpixel); + ASSERT_EQ(w_actual.ws_ypixel, w.ws_ypixel); + + close(master); + close(slave); +} + +TEST(pty, forkpty) { + pid_t sid = getsid(0); + + int master; + pid_t pid = forkpty(&master, NULL, NULL, NULL); + ASSERT_NE(-1, pid); + + if (pid == 0) { + // We're the child. + ASSERT_NE(sid, getsid(0)); + _exit(0); + } + + ASSERT_EQ(sid, getsid(0)); + + int status; + ASSERT_EQ(pid, waitpid(pid, &status, 0)); + ASSERT_TRUE(WIFEXITED(status)); + ASSERT_EQ(0, WEXITSTATUS(status)); + + close(master); +} diff --git a/tests/utmp_test.cpp b/tests/utmp_test.cpp new file mode 100644 index 000000000..b61110d68 --- /dev/null +++ b/tests/utmp_test.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +TEST(utmp, login_tty) { + // login_tty is tested indirectly by the openpty and forkpty tests. + // This test just checks that we're exporting the symbol independently. + ASSERT_EQ(-1, login_tty(-1)); +}