Reading and writing(at back) simultaneously in a fifo - c

I'm trying to copy the contents of a file1 into other file2 through fifo. The first four characters I want to write back in the fifo (during reading, not earlier when writing contents from file1 to fifo) and then copy it in the file2 also. But the first four characters don't get appended at back but they get inserted randomly in the middle. My code is
int main( int argc, char* argv[] ) {
int fdes,fdes1;
pid_t pid;
ssize_t numRead;
char readBuff[1];
char writeBuff[1];
int readCounter;
int c=0;
umask(0);
if (mkfifo("ajjp.e",0666) == -1 /*make the fifo*/
&& errno != EEXIST)
{}
if( argc < 3 ) {
printf( "Atleast need 2 params " );
exit(1);
}
int to_copy = open( argv[1], 0 );/* file from which contents are to be copied */
int oo = creat(argv[2], 0666);/* file created where we've to write contents*/
if ( to_copy == -1 ) {
printf( "Opening file failed " );
exit(1);
}
if ( (pid = fork()) < 0) /* child process is made*/
perror("fork error");
/* in parent process,I'm cwriting contents of file1 to fifo character by character */
else if(pid>0)
{
fdes = open("ajjp.e", O_WRONLY);
while( (readCounter = read( to_copy, readBuff, sizeof( readBuff ) ) > 0 ) ) {
write( fdes, readBuff, sizeof( readBuff ) );
}
close(to_copy);
}
/* now, in child process, I opened its read end then I'm reading contents from fifo and writing it to file2(i.e copy_to here) but for first four characters( c< 5 here), I'm writing them to fifo also by opening its write end. */
else
{
fdes1 = open("ajjp.e", O_RDONLY);
fdes = open("ajjp.e", O_WRONLY);
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
printf("signal");
int copy_to = open( argv[2], 0666);/* opened the file where we've to write*/
if ( copy_to == -1 ) {
printf( "Opening file failed " );
exit(1);
}
for(;;) {
c++;
numRead = read(fdes1, readBuff, sizeof(readBuff));/* reading from read end of fifo*/
if (numRead == 0)
break;
/* write to the file2*/
if (write(copy_to, readBuff, numRead) != numRead)
{}
/* for first 4 characters, I am rewriting to the back of fifo*/
if(c<5)
{
write(fdes,readBuff,sizeof(readBuff));
}
/*after writing those 4 characters, write end I've closed*/
if(c==5)
close(fdes);
}
close(fdes);
close(fdes1);
}//end else
return 0;
}
Now, if on terminal, I run
$ ./a.out a.txt b.txt
I want to copy from a.txt to b.txt, b.txt contains a.txt plus first 4 characters inserted randomly between characters.

You've got some logic problems and synchronization problems. Your goal isn't too clear, but it seems like you want to copy a file, say "Hello World" and in the copy, it should have "Hello World" but also have "Hell" sprinkled in. So, maybe "HelHleo llWorld"?
Computers are fast and can buffer and do quite a lot at once. Your child process may not even execute until after the parent is completely done because it takes a bit of time to start a new process. As such you are probably getting "Hello WorldHell". That is, the parent totally copies the file to the FIFO before the child even starts reading. You need to look into some synchronization methods. For example, using the tools you have, you could make another fifo. Have the parent wait until it can read from it. Have the child write to it when it is loaded as a way to tell the parent it is ready to go. Making your file really really large might also give the child time to start or adding sleep statements after each character.
It would be easier to speculate if you had provided your input file as well as your output(the wrong output). But, basically, you have a multi-process/multi-threading problem where you want them to operate together but they really end up running one at a time. Slow down the parent or make it wait for the child.

Related

Named pipes for client-server simulation in 2 terminals

I know the title doesn't explain the problem precisely and I apologize.
I've been writing a program in C of named pipes and the goal of it is to open 2 windows of the terminal, each running either the "Server" or the "Client" file.
After the terminals connected to each other via one named pipe, the Client can then send a string to the Server, the server would create a thread that prints the string it had received, reverses it, and then send it back to the Client via another named pipe. Finally, the Client should print the message it had received back from the Server. The client should be able to keep sending strings until the file is exited or the string "exit" is sent.
What my issue is and what I think causes it:
Everything works fine when the user enters a single word in the string, but when it sends a sentence with spaces in it, the Client's fscanf, that is meant to read from "toClient" named pipe, slices the sentence and it can only receive one word at a time.
So then the next time the client sends a message, it will read the second word of the previous message because it was sliced out and stayed in the named pipe.
I tried to use a while loop to keep reading from "toClient" until all the individual words have been taken out and it works like I expected it to, but after the first message the connection hangs and the client can't send new messages. I think it hangs because the while loop doesn't reach EOF for some reason, maybe because both the Client and Server still have the named pipe open.
Server:
/*function that reverses a string*/
char* revstr(char *str1)
{
int i, len, temp;
len = strlen(str1);
for (i = 0; i < len/2; i++)
{
temp = str1[i];
str1[i] = str1[len - i - 1];
str1[len - i - 1] = temp;
}
return str1;
}
/*function that is run by a thread to handle a client's message*/
void *threadFunc(void *arg){
char *string = (char*)arg;
printf("Received from client: %s\n", string);//print message received to terminal
/*make a connection to "toClient" named pipe that in order to send back the reversed string*/
FILE *fdw;
if (!(fdw = fopen("toClient", "w"))) {
perror("cannot open fifo file for w") ;
exit(EXIT_FAILURE) ;
}
/*irrelevant*/
if(strcmp(string,"exit")==0)
{
fprintf(fdw, " Done\n") ;
fflush(fdw) ;
printf("Shutting down...\n");
exit(0) ;
}
char *string2 = revstr(string);//string2 is the reversed string
fprintf(fdw, " %s\n", string2) ;//send string2 into the named pipe labeled "toClient"
fflush(fdw) ;
printf("Sent message back to client...\n");
}
int main()
{
char s[STR_LEN];
FILE *fdr;
if (mkfifo("toServer", 0777) == -1 && errno != EEXIST) {
perror("cannot create fifo1 file") ;
exit(EXIT_FAILURE) ;
}
if (mkfifo("toClient", 0777) == -1 && errno != EEXIST) {
perror("cannot create fifo2 file") ;
exit(EXIT_FAILURE) ;
}
printf("Waiting for client...\n");
if (!(fdr = fopen("toServer", "r"))) {
perror("cannot open fifo file for r") ;
exit(EXIT_FAILURE) ;
}
printf("Client found, waiting for message...\n");
/*this block waits for a message from the client, then creates a thread to handle it*/
while ( fscanf(fdr, " %s", s) != EOF){
int retcode;
pthread_t t1;
retcode = pthread_create(&t1,NULL,&threadFunc,(void *)(s));
if(retcode!=0)
printf("Create thread failed with error %d\n", retcode);
pthread_join(t1,NULL);
}
printf("Client disconnected\n") ;
return EXIT_SUCCESS ;
}
Client:
{
char s[STR_LEN];
FILE *fdw;
FILE *fdr;
if (mkfifo("toServer", 0777) == -1 && errno != EEXIST) {
perror("cannot create fifo file") ;
exit(EXIT_FAILURE) ;
}
if (mknod("toClient", 0777,0) == -1 && errno != EEXIST) {
perror("cannot create fifo file") ;
exit(EXIT_FAILURE) ;
}
if (!(fdw = fopen("toServer", "w"))) {
perror("cannot open fifo file for w") ;
exit(EXIT_FAILURE) ;
}
puts("Connected to server, enter a message:\n");
/*the user now enters a message into the terminal*/
while ( fgets(s, STR_LEN, stdin) != NULL) {
printf("Sent: %s",s);//print the message to terminal
fprintf(fdw, " %s\n", s) ;//send the message into the named pipe labeled "toServer"
fflush(fdw) ;
/*connect to the server to receive it's response using the named pipe labeled "toClient"*/
if (!(fdr = fopen("toClient", "r"))) {
perror("cannot open fifo file for r") ;
exit(EXIT_FAILURE) ;
}
/*this is where my problem is - this block is essentially meant to read from the named pipe "toClient"*/
if/*while*/ ( fscanf(fdr, " %s", s) != EOF )
{
printf("Received from server: %s\n", s);//print the response received to the terminal
/*Irrelevant*/
if(strcmp(s,"Done")==0)
exit(0);
}
}
return EXIT_SUCCESS ;
}
Terminals when using if on fscanf:
Terminals when using while on fscanf:
I know this thread is really long and the problem is probably due to bad code design but I have no idea what to do after almost 8 hours of trying and being relatively inexperienced with the subject .(It's part of an OS course I'm taking in uni)
Well, this is how scanf with %s specifier works. If you want to read whole line using scanf then use %[^\n]s, which means - read until newline character. Anyways it would be better to use fgets.

how to get the output of a terminal command into a C string [duplicate]

I am currently working on a ssh program and I want to be able to have full control over the terminal via networking. My question is, if I send a command to the server to run in the terminal, how do I get the output that the terminal prints? I have seen many posts saying to use the popen() command but from what I have tried I can't change directories and do other commands using this, only simple things such as ls. Is there any other way to get output from terminal besides sending it to a file like command > filetoholdcommand. Thanks in advance!
I would put this as a comment, but I dont have enough rep as I'm new. cd is a built in shell command so you want to use system(). But cd will have no effect on your process (you have to use chdir(), for that),so what you really want to do is start a shell as a subprocess via fork/exec, connect pipes to it stdin and stdout,then pipe it commands for the duration of the user session or connection.
Following code give the general idea. Basic, and flawed - use select() not usleep() for one.
int argc2;
printf( "Server started - %d\n", getpid() );
char buf[1024] = {0};
int pid;
int pipe_fd_1[2];
int pipe_fd_2[2];
pipe( pipe_fd_1 );
pipe( pipe_fd_2 );
switch ( pid = fork() )
{
case -1:
exit(1);
case 0: /* child */
close(pipe_fd_1[1]);
close(pipe_fd_2[0]);
dup2( pipe_fd_1[0], STDIN_FILENO );
dup2( pipe_fd_2[1], STDOUT_FILENO );
execlp("/bin/bash", "bash", NULL);
default: /* parent */
close(pipe_fd_1[0]);
close(pipe_fd_2[1]);
fcntl(pipe_fd_2[0], F_SETFL, fcntl(pipe_fd_2[0], F_GETFL, NULL ) | O_NONBLOCK );
while(true)
{
int r = 0;
printf( "Enter cmd:\n" );
r = read( STDIN_FILENO, &buf, 1024 );
if( r > 1 )
{
buf[r] = '\0';
write(pipe_fd_1[1], &buf, r);
}
usleep(100000);
while( ( r = read( pipe_fd_2[0], &buf, 1024 ) ) > 0 )
{
buf[r-1] = '\0';
printf("%s", buf );
}
printf("\n");
}
}
You want the "popen" function. Here's an example of running the command ls /etc and outputting to the console.
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char *argv[] )
{
FILE *fp;
int status;
char path[1035];
/* Open the command for reading. */
fp = popen("/bin/ls /etc/", "r");
if (fp == NULL) {
printf("Failed to run command\n" );
exit;
}
/* Read the output a line at a time - output it. */
while (fgets(path, sizeof(path), fp) != NULL) {
printf("%s", path);
}
/* close */
pclose(fp);
return 0;
}

c - Unable to write data to a file from inside a child process

I have two furnctions in a c program, create_open_log_file() and write_to_log_file() and a global file pointer.
When these functions get called, the log file is created as expected (I can see it in the dir). Then write_to_log_file() is called and a child process is created. At this point I would have expected that the string test test test would be written to this file in a loop. The string child process is printed on the terminal o I know the code is being excuted. However, the log file has no content?
I'd appreicate if somebody could tell me if I am doing something obvious wrong.
FILE *log_file;
static void create_open_log_file(void) {
char filename[40];
time_t t = time(NULL);
struct tm *tm = localtime(&t);
char s[64];
strftime(s, sizeof(s), "%a%b%d%T", tm);
sprintf(filename, "dut1_serial_log_%s", s);
log_file = fopen(filename,"w");
if (log_file == NULL) {
perror("Error creating log file");
}
}
static write_to_log_file() {
// Prevent killed child-processes remaining as "defunct"
struct sigaction sigchld_action = {
.sa_handler = SIG_DFL,
.sa_flags = SA_NOCLDWAIT
};
sigaction( SIGCHLD, &sigchld_action, NULL ) ;
// Duplicate ("fork") the process. Will return zero in the child
// process, and the child's PID in the parent (or negative on error).
int pid = fork();
global_pid = pid;
if( pid < 0 ) {
printf( "Fork failed\n" ) ;
return 1 ;
}
// ------------ Child process
if( pid == 0 ) {
// ------------ Child process
// Open log file and write to it from /dev/USB1
create_open_log_file();
while( 1 ) {
printf( "child process\n" ) ;
char str[] = "test test test";
fwrite(str , 1 , sizeof(str) , log_file);
sleep(1) ;
}
return 0 ; //never reached
}
}
From a quick code review, it looks like the child process never closes the file, hence the data may or may not reach the file.
Ah. Since it is an infinite loop you really don't intend a close. Yes. Flushing the buffer will generally get the data all the way to the disk, which is what I am guessing is what you are really after.
Flushing a FILE is needed; otherwise your output just sits in memory (the file's buffer block) till the block is filled up, or you fclose the file pointer. That's part of the difference between buffered stdio, and bare file handles.

How to use read and write past BUFSIZ in C

For an assignment, I'm supposed to create two methods: Method one will read() and write() the input file to an empty output file, one byte at a time (slowly).
The other method will instead use char buf[BUFSIZ]; where BUFSIZ is from <stdio.h>. We are supposed to read() and write() with the BUFSIZ which will make things a lot faster.
The input file we test each method on is just a linux dictionary (/dict/linux.words).
I've correctly implemented method one, where I call read() and write() on one character at a time, copying the input file to the output file. Although it's very slow, it at least copies everything over.
My code for this looks like this:
// assume we have a valid, opened fd_in and fd_out file.
char buf;
while(read(fd_in, buf, 1) != 0)
write(fd_out, buf, 1);
For method two however, where I use BUFSIZ, I am not able to transfer every single entry into the output file. It fails in the z entries, and doesn't write anymore.
So, my first try:
// assume we have a valid, opened fd_in and fd_out file
char buf[BUFSIZ];
while(read(fd_in, buf, BUFSIZ) != 0)
write(fd_out, buf, BUFSIZ);
doesn't work.
I understand that read() will return either the number of bytes read or 0 if it is at the end of a file. The problem I'm having is understanding how I can compare read() to BUFSIZ, and then loop around and start read() at where it left off until I reach the real end of file.
Since your file will most likely not be an exact multiple of BUFSIZ you need to check for the actual number of bytes read, so that the last block will be written correctly, e.g.
char buf[BUFSIZ];
ssize_t n;
while((n = read(fd_in, buf, BUFSIZ)) > 0)
write(fd_out, buf, n);
this code:
// assume we have a valid, opened fd_in and fd_out file
char buf[BUFSIZ];
while(read(fd_in, buf, BUFSIZ) != 0)
write(fd_out, buf, BUFSIZ);
leaves much to be desired,
does not handle a short remaining char count at the end of the file,
does not handle errors, etc.
a much better code block would be:
// assume we have a valid, opened fd_in and fd_out file
char buf[BUFSIZ];
int readCount; // number of bytes read
int writeCount; // number of bytes written
while(1)
{
if( 0 > (readCount = read(fd_in, buf, BUFSIZ) ) )
{ // then, read failed
perror( "read failed" );
exit( EXIT_FAILURE );
}
// implied else, read successful
if( 0 == readCount )
{ // then assume end of file
break; // exit while loop
}
// implied else, readCount > 0
if( readCount != (writeCount = write( fd_out, buf, readCount ) ) )
{ // then, error occurred
perror( "write failed" );
exit( EXIT_FAILURE );
}
// implied else, write successful
} // end while
Note: I did not include the closing of input/output files statements
before each call to exit() however, that does need to be added

Getting all output from terminal in C

I am currently working on a ssh program and I want to be able to have full control over the terminal via networking. My question is, if I send a command to the server to run in the terminal, how do I get the output that the terminal prints? I have seen many posts saying to use the popen() command but from what I have tried I can't change directories and do other commands using this, only simple things such as ls. Is there any other way to get output from terminal besides sending it to a file like command > filetoholdcommand. Thanks in advance!
I would put this as a comment, but I dont have enough rep as I'm new. cd is a built in shell command so you want to use system(). But cd will have no effect on your process (you have to use chdir(), for that),so what you really want to do is start a shell as a subprocess via fork/exec, connect pipes to it stdin and stdout,then pipe it commands for the duration of the user session or connection.
Following code give the general idea. Basic, and flawed - use select() not usleep() for one.
int argc2;
printf( "Server started - %d\n", getpid() );
char buf[1024] = {0};
int pid;
int pipe_fd_1[2];
int pipe_fd_2[2];
pipe( pipe_fd_1 );
pipe( pipe_fd_2 );
switch ( pid = fork() )
{
case -1:
exit(1);
case 0: /* child */
close(pipe_fd_1[1]);
close(pipe_fd_2[0]);
dup2( pipe_fd_1[0], STDIN_FILENO );
dup2( pipe_fd_2[1], STDOUT_FILENO );
execlp("/bin/bash", "bash", NULL);
default: /* parent */
close(pipe_fd_1[0]);
close(pipe_fd_2[1]);
fcntl(pipe_fd_2[0], F_SETFL, fcntl(pipe_fd_2[0], F_GETFL, NULL ) | O_NONBLOCK );
while(true)
{
int r = 0;
printf( "Enter cmd:\n" );
r = read( STDIN_FILENO, &buf, 1024 );
if( r > 1 )
{
buf[r] = '\0';
write(pipe_fd_1[1], &buf, r);
}
usleep(100000);
while( ( r = read( pipe_fd_2[0], &buf, 1024 ) ) > 0 )
{
buf[r-1] = '\0';
printf("%s", buf );
}
printf("\n");
}
}
You want the "popen" function. Here's an example of running the command ls /etc and outputting to the console.
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char *argv[] )
{
FILE *fp;
int status;
char path[1035];
/* Open the command for reading. */
fp = popen("/bin/ls /etc/", "r");
if (fp == NULL) {
printf("Failed to run command\n" );
exit;
}
/* Read the output a line at a time - output it. */
while (fgets(path, sizeof(path), fp) != NULL) {
printf("%s", path);
}
/* close */
pclose(fp);
return 0;
}

Resources