Making stdin writable in a safe and portable way - c

I was trying to run two programs.
Case 1
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
int n;
int k = 10;
int ret_val = 0;
ret_val = write (0, &k, sizeof(int));
if (-1 == ret_val)
{
printf ("Failed to write");
exit (EXIT_FAILURE);
}
scanf ("%d", &n);
printf ("Integer read is %d \n", n);
return 0;
}
Then I tried the next one.
Case 2
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int n;
int k = 10;
int ret_val = 0;
/* Open file from which content shall be inserted to stdin_buffer */
int source_fd = open ("file_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == source_fd)
{
printf ("Failed to open file for reading");
exit (EXIT_FAILURE);
}
int stdin_fd;
/* Close STDIN_FILENO */
close(0);
/* dup the source */
stdin_fd = dup (source_fd);
if (-1 == stdin_fd)
{
printf ("Failed to dup");
exit (EXIT_FAILURE);
}
/* write to stdin_buffer (content will be taken from file_in.txt) */
ret_val = write (stdin_fd, &k, sizeof(int));
if (-1 == ret_val)
{
printf ("Failed to write to stdin_buffer");
exit (EXIT_FAILURE);
}
scanf ("%d", &n);
printf ("Integer read is %d \n", n);
close(source_fd);
return 0;
}
Now in the first case, I was not able to write to stdin. In the second case, I was able to take the input from a file, "file_in.txt", and send the content to the standard input buffer.
I couldn't get a good explanation for why my first case didn't work out. Can someone explain?
stdin should be like any other file right? If it is write protected, fine. But then when I redirected the input (in the second case), there was no "permission denied" problem. This code seems to be non-portable. Is there a portable and safe way to redirect stdin from a file?
After going through the comments, I have come up with a better working code. I would like some feedback on this code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define LEN 100
int main()
{
int n;
char buffer[LEN];
memset (buffer, '\0', LEN);
int ret_val = 0;
/* Open file from which content shall be inserted to stdin_buffer */
int source_fd = open ("file_in.txt", O_CREAT | O_RDONLY, S_IRWXU);
if (-1 == source_fd)
{
perror ("Failed to open file for reading");
exit (EXIT_FAILURE);
}
/* Temp stdin_buffer */
int temp_fd = open ("temp_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == temp_fd)
{
perror ("Failed to open temp stdin");
exit (EXIT_FAILURE);
}
int stdin_fd;
/* Close STDIN_FILENO */
close(0);
/* dup the source */
stdin_fd = dup (temp_fd);
if (-1 == stdin_fd)
{
perror ("Failed to dup");
exit (EXIT_FAILURE);
}
ret_val = read (source_fd, buffer, LEN);
if (-1 == ret_val)
{
perror ("Failed to read from source");
exit (EXIT_FAILURE);
}
else
{
printf ("%s read from Source file\n", buffer);
}
/* write to stdin_buffer (content taken from file_in.txt) */
ret_val = write (stdin_fd, buffer, LEN);
if (-1 == ret_val)
{
perror ("Failed to write to stdin_buffer");
exit (EXIT_FAILURE);
}
ret_val = lseek (stdin_fd, 0, SEEK_SET);
if (-1 == ret_val)
{
perror ("Failed lseek");
exit (EXIT_FAILURE);
}
ret_val = scanf ("%d", &n);
if (-1 == ret_val)
{
perror ("Failed to read stdin_buffer");
exit (EXIT_FAILURE);
}
printf ("Integer read is %d \n", n);
close(source_fd);
return 0;
}

Before the updates
In the first program, 3 null bytes and a newline were (probably) written to the screen (not necessarily in that order); the program then tries to read from the keyboard (assuming that there's no I/O redirection on the command line). Writing to standard input does not load the input buffer. You very often can write to standard input (and read from standard output and standard error) because the classic technique opens a file descriptor with O_RDWR and then connects that to the standard I/O channels. However, there is no guarantee that you can do so. (The first program needs <unistd.h>, incidentally.)
The second program has so much undefined behaviour it is difficult to analyze. The open() call needs three arguments because it includes O_CREAT; the third argument is the mode for the file (e.g. 0644). You don't check that the open() succeeds. You don't check that the write succeeds; it won't, because the file descriptor is opened O_RDONLY (or, rather, the source_fd is opened O_RDONLY, and the dup() will copy that mode to file descriptor 0), which means the write() will fail. The input operation is not checked (you don't ensure that scanf() succeeds). (The second program doesn't really need <sys/types.h> or <sys/stat.h>.)
Basically, you don't know anything about what is going on because you've not checked any of the critical function calls.
After update 1
Note that error messages should be written to standard error and should be terminated with newlines.
I get the first program working as stated (Mac OS X 10.10 Yosemite, GCC 4.8.1), though it is hard to prove that the null bytes got written to standard input (but a newline was written there). I could then type 10 (or 20, or 100, or …) plus Return and that integer would then be printed.
The second program fails on the scanf() because the file pointer is at the end of the file when you try to read. You can see with this variant of your program:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
/* Open file from which content shall be inserted to stdin_buffer */
int source_fd = open ("file_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == source_fd)
{
printf ("Failed to open file for reading\n");
exit (EXIT_FAILURE);
}
close(0);
int stdin_fd = dup (source_fd);
if (-1 == stdin_fd)
{
printf("Failed to dup\n");
exit(EXIT_FAILURE);
}
int k = 10;
int ret_val = write(stdin_fd, &k, sizeof(int));
if (-1 == ret_val)
{
printf("Failed to write to stdin_buffer\n");
exit(EXIT_FAILURE);
}
int rc;
int n;
if ((rc = scanf("%d", &n)) != 1)
printf("Failed to read from standard input: rc = %d\n", rc);
else
printf("Integer read is %d (0x%08x)\n", n, n);
close(source_fd);
return 0;
}
It produces:
Failed to read from standard input: rc = -1
If you rewind the file before reading, you will get 0 returned; the binary data written to the file is not a valid string representation of an integer.
After update 2
I've written a small function err_exit() because it allows the code to be smaller on the page. I've modified your code in a couple of places to report on the return value from a previous function. The absence of input is not an error. When you get 0 bytes read, that isn't an error; it is EOF. When there is data to be read but it isn't the text format for an integer value, no conversions take place, but that isn't an error.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#define LEN 100
static void err_exit(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
int main(void)
{
int n = 99;
char buffer[LEN];
memset(buffer, '\0', LEN);
int ret_val = 0;
/* Open file from which content shall be inserted to stdin_buffer */
int source_fd = open("file_in.txt", O_CREAT | O_RDONLY, S_IRWXU);
if (-1 == source_fd)
err_exit("Failed to open file for reading");
/* Temp stdin_buffer */
int temp_fd = open("temp_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == temp_fd)
err_exit("Failed to open temp stdin");
/* Close STDIN_FILENO */
close(0);
/* dup the source */
int stdin_fd = dup(temp_fd);
if (-1 == stdin_fd)
err_exit("Failed to dup");
ret_val = read(source_fd, buffer, LEN);
if (-1 == ret_val)
err_exit("Failed to read from source");
else
printf("(%d bytes) <<%s>> read from Source file\n", ret_val, buffer);
/* write to stdin_buffer (content taken from file_in.txt) */
ret_val = write(stdin_fd, buffer, LEN);
if (-1 == ret_val)
err_exit("Failed to write to stdin_buffer");
ret_val = lseek(stdin_fd, 0, SEEK_SET);
if (-1 == ret_val)
err_exit("Failed lseek");
ret_val = scanf("%d", &n);
if (-1 == ret_val)
err_exit("Failed to read stdin_buffer");
printf("Integer read is %d (ret_val = %d)\n", n, ret_val);
close(source_fd);
return 0;
}
Output:
(0 bytes) <<>> read from Source file
Integer read is 99 (ret_val = 0)
When scanf() fails to read a value, it (usually) doesn't write anything into the corresponding variable. That's why the 99 survives. If you want data that can be read by scanf() as an integer, you need:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#define LEN 100
static void err_exit(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
int main(void)
{
int n = 99;
char buffer[LEN] = "";
int ret_val = 0;
/* Open file from which content shall be inserted to stdin */
int source_fd = open("file_in.txt", O_CREAT | O_RDONLY, S_IRWXU);
if (-1 == source_fd)
err_exit("Failed to open file for reading");
/* Temp stdin */
int temp_fd = open("temp_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == temp_fd)
err_exit("Failed to open temp stdin");
/* Close STDIN_FILENO */
close(0);
/* dup the source */
int stdin_fd = dup(temp_fd);
if (-1 == stdin_fd)
err_exit("Failed to dup");
ret_val = read(source_fd, buffer, LEN);
if (-1 == ret_val)
err_exit("Failed to read from source");
else
printf("(%d bytes) <<%s>> read from Source file\n", ret_val, buffer);
/* write to stdin (content taken from file_in.txt) */
ret_val = write(stdin_fd, "10\n", sizeof("10\n")-1);
if (-1 == ret_val)
err_exit("Failed to write to stdin");
ret_val = lseek(stdin_fd, 0, SEEK_SET);
if (-1 == ret_val)
err_exit("Failed lseek");
ret_val = scanf("%d", &n);
if (-1 == ret_val)
err_exit("Failed to read stdin");
printf("Integer read is %d (ret_val = %d)\n", n, ret_val);
close(source_fd);
return 0;
}
Output:
(0 bytes) <<>> read from Source file
Integer read is 10 (ret_val = 1)

Let's see what your three programs are doing...
Program 1
The first program writes to the filedescriptor 0. By default, this is stdout, hence the macro STDOUT_FILENO has that value. Since stdout is a unidirectional stream that runs into the process, writing fails.
Program 2
In this program, you open a file, which probably gets FD (filedescriptor) 3 (after 0-2 for the standard streams). Then, you close stdout with FD 0. Then, you dup() FD 3 and since the first open spot is at index 0, that's the new FD to the file. Since scanf() just uses the (unchanged) macro STDIN_FILENO, it will pick up the content of that file there.
Program 3
In program 3, you do pretty much the same as in program 2, only that you open the file twice. The explanation of what is going on pretty much follows the two above.
Question now is what you mean when you say "a better working code". The point is that "better" is impossible to say unless you specify what you actually want. Guessing from your topic and your comment, you want to remote-control a second process, just like using the pipe symbol in bash.
In order to do that, you will have to resort to OS-specific means, just search for "input output redirection" and you should be able to find some info. Since you already mentioned execl(), portability beyond POSIX (e.g. to win32) seems not be an issue though, and for that you should find lots of example code out there, much better that what I can write up here.
In any case, it boils down to roughly these steps:
pipe() to create a pair of FDs
fork() to create a new process
child: dup2() to reassign the input FD from pipe() to stdin
child: execl() the new process
parent: write to output FD to generate input for the child process
In addition, you should close unused streams or configure them with fcntl(FD_CLOEXEC) to have them closed automatically for you. The important point is that the two FDs remain connected to the same (!) channel, although you have both end in both processes. Closing one end in each leaves a unidirectional channel between the two processes.

Related

Why when I press CTRL+C the program reads zero bytes? (C-Posix)

My program has to do this:
The user has to pass N absolute pathname for files by command line. Then the i-th thread, with 0<=i<= N, has to write in the i-th file a string passed by user with scanf (or fgets). If CTRL+C is pressed, the program has to print all strings that user has passed with scanf.
When I run this and I insert a string for 1 of the N files and I press CTRL+C, in the function onPress the function read returns 0 (I think that in this case not indicates that the file pointer is in the end of file) and it prints only the string "Strings:"
Code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <signal.h>
pthread_mutex_t mutex;
int fdGlobal;
void* writer (int* arg) {
int fd_in = *(arg);
char buffer[100];
pthread_mutex_lock(&mutex);
printf("Write the string that you want to insert in the file\n");
scanf("%s", &buffer);
write(fd_in, &buffer, strlen(buffer));
write(fdGlobal, &buffer, strlen(buffer));
printf("Finished\n");
pthread_mutex_unlock(&mutex);
}
void onPress(int sig) {
char buff[100];
printf("I'm going to print all strings passed in files...\n");
int rd = read(fdGlobal, &buff, sizeof(buff));
if (rd == -1) perror("Error in the read of global file\n");
printf("I read %d bytes\n", rd);
printf("Strings: %s\n", buff);
exit(0);
}
void main (int argc, char* argv[]) {
int fds[argc-1];
pthread_t tid[argc-1];
int i, mu;
if (argc<=1) {
printf("Insert a number >=1 of pathname/s\n");
}
for ( i = 1 ; i<argc; i++) {
if (argv[i][0] != '/') {
printf("Insert a pathname\n");
}
}
signal(SIGINT, onPress);
fdGlobal = open("globalFile.txt", O_CREAT|O_RDWR, 0666);
if (fdGlobal == -1) perror("Error in the open of global file\n");
mu = pthread_mutex_init(&mutex, NULL);
if (mu < 0) perror("Error in the creation of mutex\n");
for (i=0; i<argc-1; i++) {
fds[i] = open(argv[i+1], O_CREAT|O_WRONLY, 0666);
if (fds[i] < 0 ) perror("Error in the open of the file\n");
pthread_create ( &tid[i], NULL, (void*) writer, &(fds[i]) );
}
for (i=0; i<argc-1; i++) {
pthread_join(tid[i], NULL);
}
}
Your code has numerous problems revolving around async-signal-safety, buffer sizes, and (non-)concurrency, but by far the most likely reason for the symptom you describe:
the function read returns 0
is that your belief that the file pointer is not at the end of the file is misplaced.
Indeed, read() returning 0 is a positive indicator that the file offset is currently at (or past) the end of the file. If the file was newly created then I don't see any reason to think that the offset would be anywhere else. Even when the file already exists, you need to move the file offset back to the beginning to read the data written in the current run of the program. You could do this with an appropriate call to lseek(), for example.

C -> Shell - Block on a Write Until Read

I want to send a character from a c program to a shell program. I am using a named pipe to send the letter 'a' whenever it is requsted. I should only have to open the pipe once. Here's an example:
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main(){
int fd;
mkfifo("/tmp/test", 0666);
fd = open("/tmp/test", O_WRONLY);
printf("Opened\n");
char * a = "a";
while(1){
printf("Writing to pipe...\n");
write(fd,a,1);
sleep(1);
}
}
And the shell executes this command as many times as it wants...
head -c 1 /tmp/test
The issue is after one head, the c will endlessly stream into the pipe, even if nobody's there.
I noticed that open() blocks until someone is on the other end. How to I tell write() to block until somebody is reading?
I would rather have this feature on write() than read(), as I think there's lots of overhead on opening the file for each request.
Thanks!
UPDATE
This is how I'm handling it in Java, it waits until I have somebody listening on this pipe before it continues on. Maybe just because it's a higher level language.
public static void writeToPipe(int val, String pipename){
try{
pipe_out = new PrintStream("/tmp/" + pipename);
}catch(Exception e){
System.out.println("Could not open a pipe for output!");
e.printStackTrace();
}
try{
pipe_out.println(val);
}catch(Exception e){
System.out.println("Could not write to pipe!");
e.printStackTrace();
}
try{
pipe_out.close();
}catch(Exception e){
System.out.println("Could not close the output pipe!");
e.printStackTrace();
}
}
UPDATE #2 - THIS IS THE SOLUTION
Here is my code based on David's idea, it's rough, but it works. I'm not checking if the named pipe exists and just supressing it from quitting.
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char **argv){
mkfifo("/tmp/test", 0666);
while(1){
int fd, status;
if ((fd = open ("/tmp/test", O_WRONLY)) == -1) {
perror ("open failed");
return 1;
}
printf("Opened Pipe\n");
char a = 'a';
int f = fork();
if(f == -1){
perror("fork");
exit(1);
}else if(f == 0){
//This is by the child process
if (write (fd, &a, 1) == -1) {
close(fd);
perror ("open failed");
return 1;
}
}else{
//This is the parent process
int w = waitpid(f, &status, WUNTRACED | WCONTINUED);
if (w == -1){
perror("waitpid");
exit(EXIT_FAILURE);
}
}
}
}
You can do what you are attempting, but understand you must limit your read to one-char on the shell side since there will be no '\n' written to the pipe. Also, you may write many more times than the shell reads. For example, you can add validations as Mr. Pursell suggests to insure your C-program is functioning and blocking on write with something similar to:
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
int main (int argc, char **argv) {
int fd;
errno = 0;
if (mkfifo (argc > 1 ? argv[1] : "/tmp/test", 0666)) {
perror ("mkfifo failed");
return 1;
}
if ((fd = open ("/tmp/test", O_WRONLY)) == -1) {
perror ("open failed");
return 1;
}
printf ("Opened\n");
char a = 'a';
while (1) {
printf ("Writing to pipe...\n");
if (write (fd, &a, 1) == -1) {
perror ("open failed");
return 1;
}
}
return 0;
}
You can test with a simple:
$ declare -i c=0; while test "$c" -lt 10 && read -n 1 ch; do
echo "read: $ch"
((c++))
done </tmp/test
Example Shell Output
read: a
read: a
read: a
read: a
read: a
read: a
read: a
read: a
read: a
read: a
You will write until the fifo buffer is full resulting in more Writing to pipe... than you have read: a.
Rough Example with fork
Continuing from the comments here is a rough example of using fork to spawn child processes to insure your C-program is always blocking on write for the shell. This example is limited to 3 repetitions, but you could just use while (1) for a continual cycle. I also added a quick counter for the write (just for my curiosity) e.g.
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int crt_fifo_write (char *fname);
int file_exists (char *f);
int main (int argc, char **argv) {
int n = 0;
errno = 0;
while (n < 3) { /* limit example to 3 child processes */
pid_t cpid, w;
int status;
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Code executed by child */
if (!crt_fifo_write (argc > 1 ? argv[1] : "/tmp/test"))
fprintf (stderr, "crt_fifo_write() failed.\n");
}
else { /* Code executed by parent */
do {
w = waitpid (cpid, &status, WUNTRACED | WCONTINUED);
if (w == -1) {
perror("waitpid");
exit(EXIT_FAILURE);
}
if (WIFSIGNALED(status)) /* signal on close of read end */
printf("shell read complete. %s\n",
n < 2 ? "restarting" : "done");
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
n++;
}
return 0;
}
/** your write 'a' to the fifo with check for existence & unlink */
int crt_fifo_write (char *fname)
{
int fd, n = 0;
errno = 0;
if (!fname || !*fname) return 0;
if (file_exists (fname))
if (unlink (fname) == -1) {
perror ("fifo exists unlink failed");
return 0;
}
if (mkfifo (fname, 0666)) {
perror ("mkfifo failed");
return 1;
}
if ((fd = open (fname, O_WRONLY)) == -1) {
perror ("open failed");
return 1;
}
printf ("Opened\n");
char a = 'a';
while (write (fd, &a, 1) != -1) {
printf ("%3d - Writing to pipe...\n", n++);
}
return 0;
}
/** atomic test that file exists (1 success, 0 otherwise) */
int file_exists (char *f)
{
errno = 0;
int flags = O_CREAT | O_WRONLY | O_EXCL;
int mode = S_IRUSR | S_IWUSR;
int fd = open (f, flags, mode);
if (fd < 0 && errno == EEXIST)
return 1;
else if (fd) { /* created, like bash touch */
close (fd);
unlink (f);
}
return 0;
}
Example Program Use/Output
$ ./bin/pipe_write_shell_fork
Opened
0 - Writing to pipe...
1 - Writing to pipe...
2 - Writing to pipe...
3 - Writing to pipe...
4 - Writing to pipe...
...
138 - Writing to pipe...
139 - Writing to pipe...
140 - Writing to pipe...
shell read complete. restarting
Opened
0 - Writing to pipe...
1 - Writing to pipe...
2 - Writing to pipe...
3 - Writing to pipe...
4 - Writing to pipe...
...
130 - Writing to pipe...
131 - Writing to pipe...
shell read complete. restarting
Opened
0 - Writing to pipe...
1 - Writing to pipe...
2 - Writing to pipe...
3 - Writing to pipe...
4 - Writing to pipe...
...
144 - Writing to pipe...
145 - Writing to pipe...
shell read complete. done
Example Shell Read/Output
$ declare -i c=0; while test "$c" -lt 10 && read -n 8 ch; do \
echo "read: $ch"; ((c++)); done </tmp/test
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
(repeated 2 more times)
Since you don't bother to check the return of write, you don't even notice that it is failing. (That is, it's not endlessly streaming into the pipe; it's just lying to you and printing "Writing to pipe..." while failing to write to the pipe.)
The only way to block on the write is to fill the pipe. But that's not your problem. The problem is that the pipe has been terminated. If you want to keep that pipe open, you'll need to have some process still alive reading from it. For example, you could do sleep 500 < /tmp/test in some other shell. Or just open the fifo for reading in the program doing the writing. (eg, add the line open("/tmp/fifo", O_RDONLY);)
The more curious issue is; why isn't your program terminating from the SIGPIPE?

Redirecting stdin with FIFO (names pipe)

I'm creating a C program with a server-client bases.
I've been trying to redirect the stdin to a named pipe I created and I've managed to put a client writing to the pipe. On the server side I opened the same pipe, closed stdin and redirected the stdin, using dup (tried with dup2 as well), to the pipe.
I have to read the input with the function getline. The problem is it reads the first input correctly, but recieves only nulls after it. I'll add a sample to the question.
server:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
main () {
char* str;
size_t size=0;
int pshell_in;
unlink("/tmp/par-shell-in");
if(mkfifo("/tmp/par-shell-in", 0777) < 0){
fprintf(stderr, "Error: Could not create pipe\n");
exit(-1);
}
if((pshell_in = open("/tmp/par-shell-in", O_CREAT | O_RDONLY, S_IRUSR)) < 0){
fprintf(stderr, "Error: Failed to open file\n");
exit(-1);
}
dup2(pshell_in, 0);
close(pshell_in);
while(1) {
if (getline(&str, &size, stdin)<0) {
printf("Oh dear, something went wrong with getline()! %s\n", strerror(errno));
return -1;
}
printf("%s", str);
}
}
* I know its null cause I've printed it with read (instead of redirecting) and it prints (null).
client:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#define VECTORSIZE 7
int main() {
char* buf;
int pshell_in;
size_t size=0;
if((pshell_in = open("/tmp/par-shell-in", O_WRONLY, S_IWUSR)) < 0){
fprintf(stderr, "Error: Failed to open file\n");
exit(-1);
}
printf("%d\n", pshell_in);
while(1) {
if (getline(&buf, &size, stdin) < 0) {
return -1;
}
write(pshell_in, buf, 256);
}
}
I suspect its right because if I use read on the client side (replacing O_WRONLY with O_RDWR) it prints the string as I typed it.
Can anyone help me with this one?
FIFOs are funny things. If a process tries to open one for reading, it will block until there's a process that opens it for writing. Conversely, if a process tries to open one for writingt, it will block until there's a process that opens it for reading. However, multiple processes can open it for reading or writing. When there are no more processes with it open for reading, writes will fail; when there are no more processes with it open for writing, reads will fail. And when the operations fail, you have to close and reopen the FIFO to continue processing data afresh.
I strongly suspect you're running into problems because of these behaviours.
Additionally, your client write code is dubious; you aren't paying any attention to how much data was read. You have:
while(1) {
if (getline(&buf, &size, stdin) < 0) {
return -1;
}
write(pshell_in, buf, 256);
}
If, as is probable, you read less than 256 characters of input in the line, then it's quite possible that you go writing beyond the bounds of the array that was allocated by getline(). It's also distinctly possible that some or even most of that data is null bytes. However, the (null) you're seeing in the server typically indicates that you're trying to print a string but passed printf() a null pointer. Whatever's going on, most of it is undefined behaviour which is a Bad Thing™ and should be avoided at all costs.
You should have something more like:
ssize_t nbytes;
while ((nbytes = getline(&buf, &size, stdin)) > 0)
{
if (write(pshell_in, buf, nbytes) != nbytes)
{
fprintf(stderr, "Short write to FIFO\n");
break;
}
}
free(buf);
Note how this only writes as much data as was read and doesn't assume that 256 bytes were available to be written.

How to send a simple string between two programs using pipes?

I tried searching on the net, but there are hardly any resources. A small example would suffice.
EDIT
I mean, two different C programs communicating with each other. One program should send "Hi" and the other should receive it. Something like that.
A regular pipe can only connect two related processes. It is created by a process and will vanish when the last process closes it.
A named pipe, also called a FIFO for its behavior, can be used to connect two unrelated processes and exists independently of the processes; meaning it can exist even if no one is using it. A FIFO is created using the mkfifo() library function.
Example
writer.c
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int fd;
char * myfifo = "/tmp/myfifo";
/* create the FIFO (named pipe) */
mkfifo(myfifo, 0666);
/* write "Hi" to the FIFO */
fd = open(myfifo, O_WRONLY);
write(fd, "Hi", sizeof("Hi"));
close(fd);
/* remove the FIFO */
unlink(myfifo);
return 0;
}
reader.c
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#define MAX_BUF 1024
int main()
{
int fd;
char * myfifo = "/tmp/myfifo";
char buf[MAX_BUF];
/* open, read, and display the message from the FIFO */
fd = open(myfifo, O_RDONLY);
read(fd, buf, MAX_BUF);
printf("Received: %s\n", buf);
close(fd);
return 0;
}
Note: Error checking was omitted from the above code for simplicity.
From Creating Pipes in C, this shows you how to fork a program to use a pipe. If you don't want to fork(), you can use named pipes.
In addition, you can get the effect of prog1 | prog2 by sending output of prog1 to stdout and reading from stdin in prog2. You can also read stdin by opening a file named /dev/stdin (but not sure of the portability of that).
/*****************************************************************************
Excerpt from "Linux Programmer's Guide - Chapter 6"
(C)opyright 1994-1995, Scott Burkett
*****************************************************************************
MODULE: pipe.c
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
int main(void)
{
int fd[2], nbytes;
pid_t childpid;
char string[] = "Hello, world!\n";
char readbuffer[80];
pipe(fd);
if((childpid = fork()) == -1)
{
perror("fork");
exit(1);
}
if(childpid == 0)
{
/* Child process closes up input side of pipe */
close(fd[0]);
/* Send "string" through the output side of pipe */
write(fd[1], string, (strlen(string)+1));
exit(0);
}
else
{
/* Parent process closes up output side of pipe */
close(fd[1]);
/* Read in a string from the pipe */
nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
printf("Received string: %s", readbuffer);
}
return(0);
}
dup2( STDIN_FILENO, newfd )
And read:
char reading[ 1025 ];
int fdin = 0, r_control;
if( dup2( STDIN_FILENO, fdin ) < 0 ){
perror( "dup2( )" );
exit( errno );
}
memset( reading, '\0', 1025 );
while( ( r_control = read( fdin, reading, 1024 ) ) > 0 ){
printf( "<%s>", reading );
memset( reading, '\0', 1025 );
}
if( r_control < 0 )
perror( "read( )" );
close( fdin );
But, I think that fcntl can be a better solution
echo "salut" | code
What one program writes to stdout can be read by another via stdin. So simply, using c, write prog1 to print something using printf() and prog2 to read something using scanf(). Then just run
./prog1 | ./prog2
Here's a sample:
int main()
{
char buff[1024] = {0};
FILE* cvt;
int status;
/* Launch converter and open a pipe through which the parent will write to it */
cvt = popen("converter", "w");
if (!cvt)
{
printf("couldn't open a pipe; quitting\n");
exit(1)
}
printf("enter Fahrenheit degrees: " );
fgets(buff, sizeof (buff), stdin); /*read user's input */
/* Send expression to converter for evaluation */
fprintf(cvt, "%s\n", buff);
fflush(cvt);
/* Close pipe to converter and wait for it to exit */
status=pclose(cvt);
/* Check the exit status of pclose() */
if (!WIFEXITED(status))
printf("error on closing the pipe\n");
return 0;
}
The important steps in this program are:
The popen() call which establishes the association between a child process and a pipe in the parent.
The fprintf() call that uses the pipe as an ordinary file to write to the child process's stdin or read from its stdout.
The pclose() call that closes the pipe and causes the child process to terminate.
This answer might be helpful for a future Googler.
#include <stdio.h>
#include <unistd.h>
int main(){
int p, f;
int rw_setup[2];
char message[20];
p = pipe(rw_setup);
if(p < 0){
printf("An error occured. Could not create the pipe.");
_exit(1);
}
f = fork();
if(f > 0){
write(rw_setup[1], "Hi from Parent", 15);
}
else if(f == 0){
read(rw_setup[0],message,15);
printf("%s %d\n", message, r_return);
}
else{
printf("Could not create the child process");
}
return 0;
}
You can find an advanced two-way pipe call example here.
First, have program 1 write the string to stdout (as if you'd like it to appear in screen). Then the second program should read a string from stdin, as if a user was typing from a keyboard. then you run:
$ program_1 | program_2

writing stdin into a file with fwrite()

I have to capture the stdout in a program and write that into a file...so I created a pipe. In the parent process, I captured the stdout in the pipe using dup() and I need to get this into a file...so I did a dup() in the child to get the captured file descriptor into the stdin. Now, how do I write this stdin into a file using fwrite()?
Isn't that doing things the hard way? All you need to do in the parent is use freopen() to connect stdout to the file of your choosing.
FILE *fp = freopen("/tmp/mylogfile", "w", stdout);
if (fp == 0)
error("...something went wrong opening the log file...\n");
The direct answer to your question is:
char buffer[32768];
ssize_t nbytes;
FILE *fp = fopen("/tmp/mylogfile", "w");
if (fp == 0)
error("....something went wrong opening my log file...\n");
while ((nbytes = fread(buffer, sizeof(char), sizeof(buffer), stdin)) > 0)
if (fwrite(buffer, sizeof(char), nbytes, fp) != nbytes)
error("...something went wrong writing to standard output...\n");
However, this is hardly necessary. You can improve the error handling in all sorts of ways; I'm simply assuming that 'error()' reports a message and does not return.
The easiest way is just to open the file and provide that as the child's stdout:
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid = fork();
switch (pid) {
case -1:
perror("fork");
return 1;
case 0:;
int new_out = open("output.txt", O_WRONLY | O_CREAT, 0666);
if (new_out == -1) {
perror("open");
return 1;
}
if (dup2(new_out, 1) == -1) {
perror("dup2");
return 1;
}
char* args[] = {"/bin/echo", "test output", 0};
execv(args[0], args);
perror("exec");
return 1;
default:;
int s;
if (waitpid(pid, &s, 0) == -1) {
perror("waitpid");
return 1;
}
if (WIFEXITED(s)) {
return WEXITSTATUS(s);
}
return 1;
}
}
You should capture into a byte or char buffer and the send that ot the fwrite.
When I say a buffer I mean an array or dynamically allocated block of bytes/chars.

Resources