How to exit with Ctrl+D like bash with execl? - c

I'm doing :
execl("/bin/bash", "/bin/bash", NULL);
When I do a Ctrl+D, it directly exit. How can I do the same as bash and write exit before exiting ?
Do I have to add a flag or something to execl?

When I compile execl(...), it prints exit on Ctrl-D just fine
#include <unistd.h>
int main(int argc, char **argv)
{
execl("/bin/bash", "/bin/bash", 0);
return 0;
}
maybe, you do a fork() or detach from the terminal or do something else, which lets bash assume it is non-interactive.
Ctrl-D is usually interpreted by the terminal. If you want to do this yourself, you must reset VEOF in the termios structure see c_cc for details.
This is a simplified example for handling Ctrl-D yourself. It still reads a whole line before processing anything, but you get the idea
#include <sys/wait.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
int main(int argc, char **argv)
{
char buf[100];
int fd;
struct termios tio;
fd = open("/dev/tty", O_RDWR);
if (fd < 0) {
perror("open tty");
exit(1);
}
memset(&tio, 0, sizeof(tio));
tcgetattr(fd, &tio);
tio.c_cc[VEOF] = 0;
tcflush(fd, TCIFLUSH);
tcsetattr(fd, TCSANOW, &tio);
while (fgets(buf, sizeof(buf), stdin)) {
if (buf[0] == 4) {
printf("Got Ctrl-D\n");
break;
}
}
return 0;
}
This program reads a line from the terminal, until it receives a line starting with Ctrl-D.
For more examples, see the Serial Programming HOWTO.

Related

C: popen reading from stdout blocks

The manpage for popen says "reading from a "popened" stream reads the command's standard output".
However, I can't seem to get the subprocess output in the trivial program below. The "reader" parent process blocks on the read (whether using fgets or fread)
What am I missing?
Attaching to the pinger program with gdb shows it is looping and calling printf to output text. Just nothing detected by fgets on the parent's side...
PINGER.C
#include <string.h>
#include <stdio.h>
#include <unistd.h>
int
main(int argc, char **argv)
{
int i = 0;
while (1)
{
printf("stdout %d\n", i++);
sleep(1);
}
}
POPENTEST.C
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
int
main(int argc, char **argv)
{
char *cmd = "./pinger";
printf("Running '%s'\n", cmd);
FILE *fp = popen(cmd, "r");
if (!fp)
{
perror("popen failed:");
exit(1);
}
printf("fp open\n");
char inLine[1024];
while (fgets(inLine, sizeof(inLine), fp) != NULL)
{
printf("Received: '%s'\n", inLine);
}
printf("feof=%d ferror=%d: %s\n", feof(fp), ferror(fp), strerror(errno));
pclose(fp);
}
OUTPUT
$ ./popenTest
fp open
By default, C buffers writes to the stdout when stdout is not connected to a tty. This means that from the OS' perspective, the program has not written anything to stdout until either the buffer is full or you manually flushed the output:
#include <string.h>
#include <stdio.h>
#include <unistd.h>
int
main(int argc, char **argv)
{
int i = 0;
while (1)
{
printf("stdout %d\n", i++);
fflush(stdout);
sleep(1);
}
}
When connected to a tty, the stdout is automatically flushed on every newline. But this automatic flushing does not happen when the stdout is connected to a pipe.

use dup2 to redirect printf failed

The code is following.
Q1:
If dup2(fd3, STDOUT_FILENO), string2 will be in log.txt.
If dup2(g_ctl[0], STDOUT_FILENO), string2 won't be received by g_ctl[1].
string1 and ls -al output will be received, Why ?
Q2:
The third library have some stdout/stderr log, if using dup2(socket_fd, STDOUT_FILENO), all logs will be collected by socket. But I also want to print all logs to screen at the same time, how to do it?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
static pthread_t g_pid;
static int g_ctl[2] = {-1, -1};
void *_run_loop(void *args) {
char buf[1024];
int n;
while (1) {
n = recv(g_ctl[1], buf, 1024, 0);
if (n > 0) {
fprintf(stderr, "%.*s\n", n, buf);
}
}
}
int main(int argc, char const *argv[])
{
int fd3 = open("./log.txt", O_CREAT | O_RDWR | O_APPEND, 0666);
int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, g_ctl);
assert(ret == 0);
ret = dup2(g_ctl[0], STDOUT_FILENO);
assert(ret > 0);
pthread_create(&g_pid, NULL, _run_loop, NULL);
send(STDOUT_FILENO, "string1", 5, 0);
system("ls -al");
printf("string2\n");
sleep(5);
return 0;
}
Q1: You need to fflush(stdout); after your printf. Otherwise printf may buffer your output. It will be written when your program exits if it hasn't already, but by that time your reading thread has already been canceled, so you don't get to read it.
Q2: As far as I know, the only way to get your output written to two files is to actually write it to both file descriptors. There is no way in Unix to "double-dup" a file descriptor. Even a command like tee is really just calling write() twice for each chunk of data read. You can do it manually, or inside a function, or in a thread, but you have to do it.

Why I can't receive SIGPOLL signal from ioctl function?

I got a strange problem that I can't solve. This is my code.
#include <stdio.h>
#include <stropts.h>
#include <signal.h>
#include <sys/types.h>
void handle_signal(int s)
{
char c = getchar();
printf("got char '%c'\n");
if(c == 'q')
{
exit(0);
}
}
int main(int argc, char** argv)
{
sigset(SIGPOLL, handle_signal);
ioctl(0, I_SETSIG, S_RDNORM);
printf("type q to exit");
while(1);
return 0;
}
When I run this program, I type character in terminal but it did not work!!! I can not receive SIGPOLL signal. Have someone can give me some advice? By the way, my operating system is ubuntu 12.04.
On Linux it needs to set O_ASYNC flag and F_SETOWN property on the file descriptor to get SIGIO signal (a synonym of SIGPOLL). And the signal handler can only call async-signal safe functions:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <ctype.h>
void handle_signal(int) { // Can only use async-signal safe functions here.
char msg[] = "got char c\n";
char* c = msg + (sizeof msg - 3);
if(1 != read(STDIN_FILENO, c, 1) || !isprint(*c))
return;
write(STDOUT_FILENO, msg, sizeof msg - 1);
if(*c == 'q')
exit(EXIT_SUCCESS);
}
int main() {
printf("type q to exit\n");
signal(SIGIO, handle_signal);
fcntl(STDIN_FILENO, F_SETFL, O_ASYNC | fcntl(STDIN_FILENO, F_GETFL));
fcntl(STDIN_FILENO, F_SETOWN, getpid());
sigset_t mask;
sigemptyset(&mask);
for(;;)
sigsuspend(&mask);
return EXIT_SUCCESS;
}
You may also like to have a look at F_SETSIG that allows receiving a signal of your choosing and extra information into the signal handler.

Attach process to new Terminal (Mac OS)

I write a program, which should create new process (I use fork(), and next in child process call execl()) and communicate with it. Here is my server:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
int main(int argc, char *argv[]) {
pid_t process;
process = fork();
if (process == 0) {
printf("The program will be executed %s...\n\n", argv[0]);
printf("Executing %s", argv[0]);
execl("hello", "Hello, World!", NULL);
return EXIT_SUCCESS;
}
else if (process < 0) {
fprintf (stderr, "Fork failed.\n");
return EXIT_FAILURE;
}
waitpid(process, NULL, NULL);
return 0;
}
And here is my client:
#include <stdio.h>
int main(int argc, char *argv[])
{
int i=0;
printf("%s\n",argv[0]);
printf("The program was executed and got a string : ");
while(argv[++i] != NULL)
printf("%s ",argv[i]);
return 0;
}
The problem is the next: my client and server show output in the same terminal. I want them to show output in separate terminals. So, how can I do it?
You need to have two open terminals. The idea is to run your program in the first terminal and see the output of the client in the second terminal.
First, you need to know what is the ID of the second terminal. So in the second terminal do:
$ tty
/dev/pts/1
(note your output will be probably different because mine is a SSH connection and hence pts, yours will be /dev/tty)
And then in your child process, you tell it to use this other terminal for its output. Like this:
#include <stdio.h>
#include <fcntl.h>
int main(int argc, char *argv[]) {
int fd = open("/dev/pts/1",O_RDWR) ; // note that in your case you need to update this based on your terminal name
// duplicate the fd and overwrite the stdout value
if (fd < 0){
perror("could not open fd");
exit(0);
}
if (dup2(fd, 0) < 0 ){
perror("dup2 on stdin failed");
exit(0);
}
if (dup2(fd, 1) < 0 ){
perror("dup2 on stdout failed");
exit(0);
}
// from now on all your outputs are directed to the other terminal.
// and inputs are also come from other terminal.
}

Can't open() second file C

For some reason if I do a second open, it compiles but when I try to run it, it does nothing like it's locked. It's missing a lot of other functions, because it's a work in progress for a school project. If I remove one of the open(), the program runs just fine.
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUFFER_SIZE 100
#define INPUT "/tmp/father"
int main(int argc, char **argv)
{
int fds;
int fd;
char mode[BUFFER_SIZE];
char buffer[BUFFER_SIZE];
unlink(INPUT);
mkfifo(INPUT, S_IRUSR | S_IWUSR);
if(argc != 2)
{
fputs("Argumentos invalidos\n", stderr);
exit(EXIT_FAILURE);
}
fd = open(argv[1], O_WRONLY);
if(fd == -1)
{
fprintf(stderr, "\nCan't open pipe\n");
exit(EXIT_FAILURE);
}
fds = open(INPUT, O_RDONLY);
if(fds == -1)
{
fprintf(stderr, "\nCan't open pipe\n");
exit(EXIT_FAILURE);
}
while(1)
{
fgets(buffer,BUFFER_SIZE,stdin);
sscanf(buffer,"%s", mode);
write(fd,buffer,strlen(buffer));
}
}
Are you sure there's a problem? You are reading from stdin (the fgets at the bottom), and writing to the pipe. What you're missing is something reading from the pipe. So if in another terminal you type:
$ cat /tmp/father
then anything you type into your prog will appear there.
So, in one terminal I do:
$ ./test /tmp/father
line one
line two
And in the second terminal:
$ cat /tmp/father
and I see:
line one
line two
No?
P.S. You are doing sscanf to read from buffer and write to mode, then writing out the buffer string. Not that it matters, but you're not using mode.

Resources