Can't write to pseudo terminal master - linux

I'm able to open a new pseudo terminal, and start a shell on the slave, but writing to the master doesn't seem to do anything, and trying to read after the shell has started ends in failure (-1). What am i doing wrong:
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/ioctl.h>
int posix_openpt(int flags);
int grantpt(int fd);
int unlockpt(int fd);
char *ptsname(int fd);
static void execsh(void);
int main(int argc, char *argv[]) {
printf("Hiya\n");
// Open the Master Clone Device /dev/ptmx, return the fd
int master_fd = posix_openpt(O_RDWR);
printf("PTMaster = %d\n", master_fd);
// Change the permissions and ownership of the slave device
int grant_success = grantpt(master_fd);
printf("Grant success = %d\n", grant_success);
// Unlock the slave pseudoterminal device corresponding to master_fd
int unlock_success = unlockpt(master_fd);
printf("Unlock success = %d\n", unlock_success);
// Grab the name of the slave device
char *slave_name = ptsname(master_fd);
printf("Slave name = %s\n", slave_name);
// Open the slave pseudoterminal device
int slave_fd = open(slave_name, O_WRONLY);
printf("Slave fd = %d\n", slave_fd);
// Exec shell
pid_t pid;
switch (pid = fork()) {
case -1:
printf("Failed to fork\n");
break;
case 0:
// Child
setsid(); /* create a new process group */
dup2(slave_fd, STDIN_FILENO);
dup2(slave_fd, STDOUT_FILENO);
dup2(slave_fd, STDERR_FILENO);
ioctl(slave_fd, TIOCSCTTY, NULL); /* make this the controlling terminal for this process */
close(slave_fd);
close(master_fd);
// Start Shell
execsh();
break;
default:
// Parent
close(slave_fd);
}
// Read from master
sleep(1);
char buffer[200];
ssize_t read_bytes = read(master_fd, buffer, 200);
printf("read %ld from master\n", read_bytes);
printf("buffer = %s\n", buffer);
// ls
ssize_t written = write(master_fd, "ls\n", 3);
printf("wrote %ld to master\n", written);
// Read from master
read_bytes = read(master_fd, buffer, 200);
printf("read %ld from master\n", read_bytes);
printf("buffer = %s\n", buffer);
close(master_fd);
kill(pid, SIGKILL); // Kill the child, biblical
return 0;
}
void
execsh(void) {
char **args;
char *envshell = getenv("SHELL");
unsetenv("COLUMNS");
unsetenv("LINES");
unsetenv("TERMCAP");
signal(SIGCHLD, SIG_DFL);
signal(SIGHUP, SIG_DFL);
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGALRM, SIG_DFL);
args = (char *[]){envshell, "-i", NULL};
printf("\nforked child starting terminal\n");
execvp(args[0], args);
printf("\nExited the shell\n");
exit(EXIT_FAILURE);
}
output looks like this:
Hiya
PTMaster = 3
Grant success = 0
Unlock success = 0
Slave name = /dev/pts/19
Slave fd = 4
read 130 from master
buffer =
forked child starting terminal
eric#vbox:~/Desktop/terminal$ exit
wrote 3 to master
read -1 from master
buffer =
forked child starting terminal
eric#vbox:~/Desktop/terminal$ exit
I'm not sure why it has the word exit there either. Thanks in advance for any pointers you might have!

ninjalj was right. I had the slave opened for writing only.
Thank you very much!

Related

epoll: difference between level triggered and edge triggered when EPOLLONESHOT specified

What's the difference between level triggered and edge triggered mode, when EPOLLONESHOT specified?
There's a similar question already here. The answer by "Crouching Kitten" doesn't seem to be right (and as I understand, the other answer doesn't answer my question).
I've tried the following:
server sends 2 bytes to a client, while client waits in epoll_wait
client returns from epoll_wait, then reads 1 byte.
client re-arms the event (because of EPOLLONESHOT)
client calls epoll_wait again. Here, for both cases (LT & ET), epoll_wait doesn't wait, but returns immediately (contrary to the answer by "Crouching Kitten")
client can read the second byte
Is there any difference between LT & ET, when EPOLLONESHOT specified?
I think the bottom line answer is "there is not difference".
Looking at the code, it seems that the fd remembers the last set bits before being disabled by the one-shot. It remembers it was one shot, and it remembers whether it was ET or not.
Which is futile, because the fd is disabled until modified, and the next call to EPOLL_CTL_MOD will erase all of that, and replace with whatever the new MOD says.
Having said that, I do not understand why anyone would want both EPOLLET and EPOLLONESHOT. To me, the whole point of EPOLLET is that, unders certain programming models (namely, microthreads), it follows the semantics perfcetly. This means that I can add the fd to the epoll at the very start, and then never have to perform another epoll related system call.
EPOLLONESHOT, on the other hand, is used by people who want to keep a very strict control over when the fd is watched and when it isn't. That, by definition, is the opposite of what EPOLLET is used for. I just don't think the two are conceptually compatible.
The other poster said "I do not understand why anyone would want both EPOLLET and EPOLLONESHOT." Actually, according to epoll(7), there is a use case for that:
Since even with edge-triggered epoll, multiple events can be generated upon receipt of multiple chunks of data, the caller has the option to specify the EPOLLONESHOT flag, to tell epoll to disable the associated file descriptor after the receipt of an event with epoll_wait(2).
The key point is that whether EPOLL will treat the combination of EPOLLET | EPOLLONESHOT and EPOLLLT | EPOLLONESHOT as special case. As I known, it is not. EPOLL just care them seperately. To EPOLLET and EPOLLLT, the different kindly only is in function ep_send_events, if the EPOLLET is set, then the function will call list_add_tail to add the epitem into the ready list in epoll_fd/eventepoll object.
To the EPOLLONESHOT, the role is to disable the fd. So I think the different between them is the different between ET and LT. You can check the result using below codes I think
// server.cc
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <pthread.h>
#define MAX_EVENT_NUMBER 1024
int setnonblocking(int fd)
{
int old_option = fcntl(fd, F_GETFL);
int new_option = old_option | O_NONBLOCK;
fcntl(fd, F_SETFL, new_option);
return old_option;
}
void addfd(int epollfd, int fd, bool oneshot)
{
epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN | EPOLLET;
if(oneshot)
event.events |= EPOLLONESHOT;
epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
setnonblocking(fd);
}
// reset the fd with EPOLLONESHOT
void reset_oneshot(int epollfd, int fd)
{
epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
}
int main(int argc, char** argv)
{
if(argc <= 2)
{
printf("usage: %s ip_address port_number\n", basename(argv[0]));
return 1;
}
const char* ip = argv[1];
int port = atoi(argv[2]);
int ret = 0;
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
inet_pton(AF_INET, ip, &address.sin_addr);
address.sin_port = htons(port);
int listenfd = socket(PF_INET, SOCK_STREAM, 0);
assert(listenfd >= 0);
ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
assert(ret != -1);
ret = listen(listenfd, 5);
assert(ret != -1);
epoll_event events[MAX_EVENT_NUMBER];
int epollfd = epoll_create(5);
addfd(epollfd, listenfd, false);
while(1)
{
printf("next loop: -----------------------------");
int ret = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
if(ret < 0)
{
printf("epoll failure\n");
break;
}
for(int i = 0; i < ret; i++)
{
int sockfd = events[i].data.fd;
if(sockfd == listenfd)
{
printf("into listenfd part\n");
struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof(client_address);
int connfd = accept(listenfd, (struct sockaddr*)&client_address,
&client_addrlength);
printf("receive connfd: %d\n", connfd);
addfd(epollfd, connfd, true);
// reset_oneshot(epollfd, listenfd);
}
else if(events[i].events & EPOLLIN)
{
printf("into linkedfd part\n");
printf("start new thread to receive data on fd: %d\n", sockfd);
char buf[2];
memset(buf, '\0', 2);
// just read one byte, and reset the fd with EPOLLONESHOT, check whether still EPOLLIN event
int ret = recv(sockfd, buf, 2 - 1, 0);
if(ret == 0)
{
close(sockfd);
printf("foreigner closed the connection\n");
break;
}
else if(ret < 0)
{
if(errno == EAGAIN)
{
printf("wait to the client send the new data, check the oneshot memchnism\n");
sleep(10);
reset_oneshot(epollfd, sockfd);
printf("read later\n");
break;
}
}
else {
printf("receive the content: %s\n", buf);
reset_oneshot(epollfd, sockfd);
printf("reset the oneshot successfully\n");
}
}
else
printf("something unknown happend\n");
}
sleep(1);
}
close(listenfd);
return 0;
}
the Client is
from socket import *
import sys
import time
long_string = b"this is a long content which need two time to fetch"
def sendOneTimeThenSleepAndClose(ip, port):
s = socket(AF_INET, SOCK_STREAM);
a = s.connect((ip, int(port)));
print("connect success: {}".format(a));
data = s.send(b"this is test");
print("send successfuly");
time.sleep(50);
s.close();
sendOneTimeThenSleepAndClose('127.0.0.1', 9999)

Is this a bug in linux kernel concerning write to /proc/self/loginuid?

There is a possibility that i found a bug in linux kernel. Let's consider application that write to /proc/self/loginuid from main thread and one auxiliary thread. The code is below:
#include <stdio.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void write_loginuid(char *str)
{
int fd;
printf("%s\n", str);
fd = open("/proc/self/loginuid", O_RDWR);
if (fd < 0) {
perror(str);
return;
}
if (write(fd, "0", 2) != 2) {
printf("write\n");
perror(str);
}
close(fd);
}
void *thread_function(void *arg)
{
fprintf(stderr, "Hello from thread! my pid = %u, tid = %u, parent pid = %u\n", getpid(), syscall(SYS_gettid), getppid());
write_loginuid("thread");
return NULL;
}
int main()
{
pthread_t thread;
pthread_create(&thread, NULL, thread_function, NULL);
write_loginuid("main process");
fprintf(stderr, "test my pid = %u, tid = %u, parent pid = %u\n", getpid(), syscall(SYS_gettid), getppid());
pthread_join(thread, NULL);
return 0;
}
After executing this application we get:
main process
test my pid = 3487, tid = 3487, parent pid = 3283
Hello from thread! my pid = 3487, tid = 3488, parent pid = 3283
thread
write
thread: Operation not permitted
That tells us the thread write failed by -EPERM.
Looking at the kernel file fs/proc/base.c and function proc_loginuid_write() we see at the beginning check:
static ssize_t proc_loginuid_write(struct file * file, const char __user * buf,
size_t count, loff_t *ppos)
{
struct inode * inode = file_inode(file);
uid_t loginuid;
kuid_t kloginuid;
int rv;
/* this is the probably buggy check */
rcu_read_lock();
if (current != pid_task(proc_pid(inode), PIDTYPE_PID)) {
rcu_read_unlock();
return -EPERM;
}
rcu_read_unlock();
So, looking at the code above we see that only for exact PID (checked by me with printks) we pass through.Thread doesn't satisfy the condition, because compared pids differs.
So my question is: is this a bug ? Why to not allow thread's of particular process to change the loginuid? I encountered this in login application that spawned another thread for PAM login.
Whether this is bug or not i written a fix that extends writing permission to this file by threads:
rcu_read_lock();
/*
* I changed the condition that it checks now the tgid as returned in sys_getpid()
* rather than task_struct pointers
*/
if (task_tgid_vnr(current) != task_tgid_vnr(pid_task(proc_pid(inode), PIDTYPE_PID))) {
rcu_read_unlock();
return -EPERM;
}
rcu_read_unlock();
What do you think about it? Does it affects security?

IPC - How to redirect a command output to a shared memory segment in child

I tried to redirect (write) a Unix command output to a shared memory segment in the child,
and then have the parent read the output back out from the same shared memory segment in the parent process. I don't have a lot of success after few futile attempts. Can anyone show me a way?
thanks in advance.
My code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/shm.h>
#define SHM_SIZE 1024
int main()
{
key_t key; int shmid; char* data;
pid_t cpid=fork();
if (cpid<0)
{
fprintf(stderr,"Fork error!\n");
exit (-1);
}
else if (cpid==0) // child process
{
if ((key = ftok("mysh.c", 'R')) == -1)
{
perror("ftok");
exit(1);
}
// Connect to shared memory
if ((shmid = shmget(key, SHM_SIZE, 0644 | IPC_CREAT)) == -1)
{
perror("shmget");
exit(1);
}
// Attach to the segment
data = shmat(shmid, (void *) 0, 0);
if (data == (char *) (-1))
{
perror("shmat");
exit(1);
}
system("ls -l");
// Stuck: How to redirect the output of "ls -l"
// to a shared memmory segment "data", so that parent process
// can retrieve it later?? Tried to
// do pipe and dup2 but none worked.
// Attempt via read?, but only garbage
read(STDIN_FILENO, data, SHM_SIZE);
}
else
{ // parent process
int st;
wait(&st);
printf("Output read from the child:\n");
if ((write(STDOUT_FILENO, data, SHM_SIZE)) < 0 )
{
perror("write 2");
exit(1);
}
}
}
======================
system("ls -l");
// Stuck: How to redirect the output of "ls -l"
// to a shared memmory segment "data", so that parent process
// can retrieve it later?? Tried to
// do pipe and dup2 but none worked.
For test purpose, I suggest you read from stdin, then write them to data.
Here is an example using POSIX shared memory (POSIX IPC API is better than SYSV IPC API), which child read from stdin to a shared memory region, and parent write the content of this shared memory region to stdout:
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char *argv[])
{
const char *shm_name = "/dummy_cat_shm";
int shm_fd;
off_t shm_length;
const char *read_sem_name = "/dummy_cat_read";
const char *write_sem_name = "/dummy_cat_write";
sem_t *read_sem, *write_sem;
pid_t pid;
int buf_length;
char *write_ptr, *read_ptr;
buf_length = 1024;
shm_length = sizeof(buf_length) + buf_length;
/* Create semaphore */
read_sem = sem_open(read_sem_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR, 0);
if (read_sem == SEM_FAILED) {
perror("sem_open");
goto clean_up3;
}
write_sem = sem_open(write_sem_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR, 1);
if (write_sem == SEM_FAILED) {
perror("sem_open");
goto clean_up2;
}
/* Create shared memory segment */
shm_fd = shm_open(shm_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (shm_fd < 0) {
perror("shm_open");
goto clean_up1;
}
if (ftruncate(shm_fd, shm_length) < 0) {
perror("ftruncate");
goto clean_up0;
}
if ((pid = fork()) < 0) {
perror("fork");
goto clean_up0;
}
else if (pid == 0) {
write_ptr = mmap(NULL, shm_length, PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (write_ptr == MAP_FAILED) {
perror("mmap");
goto clean_up0;
}
char *buf = write_ptr+sizeof(buf_length);
while (sem_wait(write_sem) == 0) {
if (fgets(buf, buf_length, stdin) != NULL) {
*(int *)write_ptr = 1;
sem_post(read_sem);
}
else {
*(int *)write_ptr = 0;
sem_post(read_sem);
break;
}
}
munmap(write_ptr, shm_length);
}
else {
read_ptr = mmap(NULL, shm_length, PROT_READ, MAP_SHARED, shm_fd, 0);
if (read_ptr == MAP_FAILED) {
perror("mmap");
goto clean_up0;
}
char *buf = read_ptr + sizeof(buf_length);
while (sem_wait(read_sem) == 0) {
if (*(int *)read_ptr > 0) {
printf("%s", buf);
sem_post(write_sem);
}
else {
break;
}
}
munmap(read_ptr, shm_length);
}
clean_up0:
shm_unlink(shm_name);
clean_up1:
sem_unlink(write_sem_name);
clean_up2:
sem_unlink(read_sem_name);
clean_up3:
exit(EXIT_FAILURE);
}
Note: these two mmap() could be put before fork() in this case.
Compiling:
gcc shm_exp.c -pthread -lrt
Running:
$ ls / | ./a.out
bin/ home/ lib32/ mnt/ run/ sys/ vmlinuz#
boot/ initrd.img# lib64/ opt/ sbin/ tmp/ vmlinuz.old#
dev/ initrd.img.old# lost+found/ proc/ selinux/ usr#
etc/ lib/ media/ root/ srv/ var/
How to redirect stdout of the ls -l
We must shed more light on the processes (parent and children) involved into this code.
How many processes your program creates during its run?
The correct answer is - three.
Two processes are the parent and the explicitly forked child.
The third one is created by the system("ls -l") call.
This function implicitly forks another process that executes (by calling an exec family function) the "ls -l" sell command. What you need to redirect is the output of the child process created by the system() function. It is sad, but the system() does not establish IPC between the participators. If you need to manipulate with the output, do not use system().
I agree with #leeduhem, popen() could be the best approach.
It works exactly as the system(), i.e. forks a new process and executes "ls -l".
In addition, it also establishes a pipe IPC between the participators, so it is easy to catch the child output and to do with it whatever you want:
char buff[1024];
FILE *fd;
// instead of system("ls -l")
fd = popen("ls -l", "r");
// check for errors
while(fgets(buff, sizeof(buff), fd) != NULL)
{
// write to the shared memory
}
pclose(fd);
If you do not want to use the popen() function, you may write a similar one.
The general approach is
open a pipe()
fork() a new process
redirect stdout using dup2
call a suitable exec() function (probably execl()) executing "ls -l"
read from the descriptor you are duplicating by dup2.

Fail to wake up from epoll_wait when other process closes fifo

I'm seeing different epoll and select behavior in two different binaries and was hoping for some debugging help. In the following, epoll_wait and select will be used interchangeably.
I have two processes, one writer and one reader, that communicate over a fifo. The reader performs an epoll_wait to be notified of writes. I would also like to know when the writer closes the fifo, and it appears that epoll_wait should notify me of this as well. The following toy program, which behaves as expected, illustrates what I'm trying to accomplish:
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <unistd.h>
int
main(int argc, char** argv)
{
const char* filename = "tempfile";
char buf[1024];
memset(buf, 0, sizeof(buf));
struct stat statbuf;
if (!stat(filename, &statbuf))
unlink(filename);
mkfifo(filename, S_IRUSR | S_IWUSR);
pid_t pid = fork();
if (!pid) {
int fd = open(filename, O_WRONLY);
printf("Opened %d for writing\n", fd);
sleep(3);
close(fd);
} else {
int fd = open(filename, O_RDONLY);
printf("Opened %d for reading\n", fd);
static const int MAX_LENGTH = 1;
struct epoll_event init;
struct epoll_event evs[MAX_LENGTH];
int efd = epoll_create(MAX_LENGTH);
int i;
for (i = 0; i < MAX_LENGTH; ++i) {
init.data.u64 = 0;
init.data.fd = fd;
init.events |= EPOLLIN | EPOLLPRI | EPOLLHUP;
epoll_ctl(efd, EPOLL_CTL_ADD, fd, &init);
}
while (1) {
int nfds = epoll_wait(efd, evs, MAX_LENGTH, -1);
printf("%d fds ready\n", nfds);
int nread = read(fd, buf, sizeof(buf));
if (nread < 0) {
perror("read");
exit(1);
} else if (!nread) {
printf("Child %d closed the pipe\n", pid);
break;
}
printf("Reading: %s\n", buf);
}
}
return 0;
}
However, when I do this with another reader (whose code I'm not privileged to post, but which makes the exact same calls--the toy program is modeled on it), the process does not wake when the writer closes the fifo. The toy reader also gives the desired semantics with select. The real reader configured to use select also fails.
What might account for the different behavior of the two? For any provided hypotheses, how can I verify them? I'm running Linux 2.6.38.8.
strace is a great tool to confirm that the system calls are invoked correctly (i.e. parameters are passed correctly and they don't return any unexpected errors).
In addition to that I would recommend using lsof to check that no other process has that FIFO still opened.

Read timeout on pty file descriptor failing

I am trying to set a read timeout on a file descriptor representing a PTY. I have set VMIN = 0 and VTIME = 10 in termios, which I expect to return when a character is available, or after a second if no characters are available. However, my program sits forever in the read call.
Is there something special about PTY that makes this not work? Are there other TERMIOS settings that cause this to work? I tried this same configuration on the stdin file descriptor and it worked as expected.
#define _XOPEN_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <fcntl.h>
#define debug(...) fprintf (stderr, __VA_ARGS__)
static void set_term (int fd)
{
struct termios termios;
int res;
res = tcgetattr (fd, &termios);
if (res) {
debug ("TERM get error\n");
return;
}
cfmakeraw (&termios);
termios.c_lflag &= ~(ICANON);
termios.c_cc[VMIN] = 0;
termios.c_cc[VTIME] = 10; /* One second */
res = tcsetattr (fd, TCSANOW, &termios);
if (res) {
debug ("TERM set error\n");
return;
}
}
int get_term (void)
{
int fd;
int res;
char *name;
fd = posix_openpt (O_RDWR);
if (fd < 0) {
debug ("Error opening PTY\n");
exit (1);
}
res = grantpt (fd);
if (res) {
debug ("Error granting PTY\n");
exit (1);
}
res = unlockpt (fd);
if (res) {
debug ("Error unlocking PTY\n");
exit (1);
}
name = ptsname (fd);
debug ("Attach terminal on %s\n", name);
return fd;
}
int main (int argc, char **argv)
{
int read_fd;
int bytes;
char c;
read_fd = get_term ();
set_term (read_fd);
bytes = read (read_fd, &c, 1);
debug ("Read returned\n");
return 0;
}
From the linux pty (7) manpage (italics are mine):
A pseudoterminal (sometimes abbreviated "pty") is a pair of virtual character devices that
provide a bidirectional communication channel. One end of the channel is called the
master; the other end is called the slave. The slave end of the pseudoterminal provides
an interface that behaves exactly like a classical terminal
Your program, however, is reading from the master, which cannot be expected to behave exactly like a terminal device
If you change/expand the last few lines of get_term thusly ...
int slave_fd = open (name, O_RDWR); /* now open the slave end..*/
if (slave_fd < 0) {
debug ("Error opening slave PTY\n");
exit (1);
}
return slave_fd; /* ... and use it instead of the master..*/
... your example program will work as expected.

Resources