Jump to content

I need a bash script


fugu

Recommended Posts

so thats what I was thinking too, but it doesnt work. a remote nc instance won't connect up to it. it immediately disconnects and you can't use it. I even tried with

-q      after EOF on stdin, wait the specified number of seconds and then quit. If seconds is negative, wait forever.
but it didn't work either

edit for typo

Edited by fugu
Link to comment
Share on other sites

Does 'netstat -nlp | grep 1025' show that nc is listening on port 1025?

When I test it as Cooper suggested I notice that netstat shows 'nc' is not listening on port 1025.

Interestingly, 'nc -l -p 1025 > output' also doesn't appear to be listening... until you run 'cat output' and then netstat shows that it is listening.

It would seem that nc does not actually start unless the other end of the input and output pipes are actually attached to a process.

What is the usecase for using fifos here?

Link to comment
Share on other sites

I find it next to impossible to connect to a listening netcat. It might be worth noting that the netcat I'm using here is this one. Alternatives are NCat and GNU netcat.

When I start netcat as-is using 'nc -l -p 2222' I see in the netstat output that it's listening on that port, but neither telnet nor another nc instance is able to connect to it. It'll just wait for the connection to be accepted. Some example uses are listed on the website but none of them work, presumably for the same reason which I frankly can't figure out. I always assumed this is how it should work, but apparently it's not. Weird.

Link to comment
Share on other sites

That is strange, because I am able to connect just fine.

I run 'nc -l -p 1025' in one window, and then 'nc localhost 1025' in another window, and I can connect and send data back and forth.

Similarly I can run 'mkfifo output; nc -l -p 1025' in one window, 'cat output' in a second window, and 'nc localhost 1025' in a third window. It works the same except that nc's output is redirected to the second window.

I get the same behavior with 'nc.openbsd' (netcat-openbsd-1.105-x86_64-2_SBo), 'ncat' (nmap-6.40-x86_64-1), and 'netcat' (netcat-0.7.1-x86_64-1_SBo). Well, ncat also listens on IPv6, but otherwise the behavior was identical (including not listening until I cat the output file).

Link to comment
Share on other sites

Does 'netstat -nlp | grep 1025' show that nc is listening on port 1025?

yeah its listening

tcp 0 0 0.0.0.0:1025 0.0.0.0:* LISTEN 31337/nc

What is the usecase for using fifos here?

Im trying to figure out if you can create a simple network service in linux, just using bash, for instances where you don't have direct acces to a real terminal, but you can access fifo pipes that can recreate the inputs and outputs for the service.

Alternatives are NCat and GNU netcat.

I'm just using the netcat-openbsd package

Similarly I can run 'mkfifo output; nc -l -p 1025' in one window, 'cat output' in a second window, and 'nc localhost 1025' in a third window. It works the same except that nc's output is redirected to the second window.

the command "mkfifo output; nc -l -p 1025" doesn't actually link "output" to anything with netcat, the two commands are independent. I also try an stay away from testing netcat using 'localhost' as on a few rare ocassions it's localhost behavior is different from it's remote host behavior.

I've been toying with the idea of something like this

$ mkfifo mystdout
$ mkfifo mystdin
$ tail -f mystdin | nc -l 1025 > mystdout
then in another terminal

$ tail -f mystdout
havn't figured out how to stream into the mystdin stream, I can see that nc is listening, but cannot connect remotely
Link to comment
Share on other sites

Terminal 1

$ tail -f mystdin | nc -v -l 1025 > mystdout

Terminal 2

$ cat mystdout | tail -f

Terminal 3

echo 'Hello World' > mystdin

This allows remote connection, doesn't output anything in terminal 2 (probabley because cat dumps the entire contents of the fifo (just the EOF) all at once and is done with it at that point)

If I try

Terminal 1

$ tail -f mystdin | nc -v -l 1025

Terminal 3

echo 'Hello World' > mystdin

This allows remote connection, but i don't get mystdout redircted. But it hints at the problem which I think might be the output pipe.

Link to comment
Share on other sites

the command "mkfifo output; nc -l -p 1025" doesn't actually link "output" to anything with netcat, the two commands are independent. I also try an stay away from testing netcat using 'localhost' as on a few rare ocassions it's localhost behavior is different from it's remote host behavior.

Sorry, that was a typo. I did actually run it as 'mkfifo output; nc -l -p 1025 > output'.

Link to comment
Share on other sites

Yea, using the 'tail -f input | nc -l -p 1025 > output' with 'cat output' in another terminal certainly works to start the server.

The behavior of netcat on localhost should be identical to the behavior from a remote host, barring any interference from external sources such as a firewall or intermediate network device. As far as netcat is concerned, a socket is a socket is a socket, regardless of if it's coming from the lo device or the eth0 device.


There are patterns out there (use google) for using netcat to build proper servers (such as web servers) in bash. You should probably look at those examples, rather than banging your head against problems that others have already solved (several times over).

Link to comment
Share on other sites

Okay, I got things to work using 4 terminals. Start by making the 2 fifos as I did initially, naming one 'input' and the other 'output'

Terminal 1
cat /path/to/some/file > input

This will just sit there, waiting to push the file over.

Terminal 2
nc -l -p 1025 < input > output

If your input file was small, chances are the process at Terminal 1 terminates because the data to deliver managed to fit inside the input buffer of the nc command.

Terminal 3
cat output

To display what the client will be sending us.

Terminal 4
cat /path/to/a/different/file | nc localhost 1025

This one will set things in motion. It sends its data which will be made visible in Terminal 3 and it will display the data from the file being cat in Terminal 1. Feel free to redirect somewhere else as you see fit.

No idea at all why it works at home but not on my laptop, but there you have it.

Edited by Cooper
Link to comment
Share on other sites

there is a package called ucspi-tcp (apt-get install ucspi-tcp) that i found to be really neat. The tcpserver program works very simillar to netcat -l with the exception that it is multi-threaded. The problem is that it can only execute 1 program for each thread, and that thread is not associated with the stdin/stdout of any terminal, so you can't really use it as a netcat in the ways that you normally do. I managed to find some code which is listed below that does what I couldn't figure out how to do in bash. it basiclly sets up 2 fifo's for each thread that connects to the tcpserver. what would be nice is if it also executed am xterm or somthing also with those pipes connected as well.
Cheers

#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char *argv[]){
    struct pollfd poll_file_descriptor[2];
    char buffer[2048];
    char *myfdin = "";
    char *myfdout = "";
    int fdin, fdout, index = 0, read_bytes, ret, written_bytes;
    int quit = 0, file1exists = 0, file2exists = 0;

    if(argc > 2){
        myfdin = argv[1];
        myfdout = argv[2];
    }else{
        index = 0;
        do{
            index++;
            if(index>=99999){
                return -1;
            }
            sprintf(buffer, "/tmp/std_in_%05d", index);
            if(access(buffer , F_OK ) != -1) {
                file1exists = 1;
            } else {
                file1exists = 0;
            }
            myfdin = (char *) malloc(sizeof(char)*(strlen(buffer)));
            memcpy(myfdin, &buffer, strlen(buffer)+1);

            sprintf(buffer,"/tmp/std_out_%05d", index);
            if(access(buffer , F_OK ) != -1) {
                file2exists = 1;
            } else {
                file2exists = 0;
            }
            myfdout = (char *) malloc(sizeof(char)*(strlen(buffer)));
            memcpy(myfdout, &buffer, strlen(buffer)+1);

        }while(file1exists == 1 || file2exists == 1);
    }

    mkfifo(myfdin, 0666);
    mkfifo(myfdout, 0666);
    
    fdin = open(myfdin, O_RDONLY);
    fdout = open(myfdout, O_WRONLY);

    while (quit == 0) {
        poll_file_descriptor[0].fd = fdin;
        poll_file_descriptor[0].events = 1;
        poll_file_descriptor[0].revents = 0;
        poll_file_descriptor[1].fd = STDIN_FILENO;
        poll_file_descriptor[1].events = 1;
        poll_file_descriptor[1].revents = 0;

        poll(poll_file_descriptor,2,1200000); //20 minutes

        if (poll_file_descriptor[0].revents != 0) {
            read_bytes = read(fdin, buffer, sizeof(buffer));
            if(read_bytes < 0){
                close(fdout);
                close(fdin);
                unlink(myfdin);
                unlink(myfdout);
                return -1;
            }  
            if(read_bytes == 0)
                quit = 1;
            while (read_bytes != 0) {
                written_bytes = write(STDOUT_FILENO,buffer,read_bytes);
                if (written_bytes == -1) {
                    if (errno == EINTR){
                        continue;
                    }
                    ret = -1;
                    read_bytes = 0;
                }else{
                    if (written_bytes == 0) ;
                    *buffer += written_bytes;
                    read_bytes -= written_bytes;
                }
            }

            if(ret == -1){
                close(fdout);
                close(fdin);
                unlink(myfdin);
                unlink(myfdout);
                return -1;
            }  
        }
        if (poll_file_descriptor[1].revents != 0) {
            read_bytes = read(STDIN_FILENO, buffer, sizeof(buffer));
            if(read_bytes < 0){
                close(fdout);
                close(fdin);
                unlink(myfdin);
                unlink(myfdout);
                return -1;
            }  
            if(read_bytes == 0)
                quit = 1;
            while (read_bytes != 0) {
                written_bytes = write(fdout, buffer, read_bytes);
                if (written_bytes == -1) {
                    if (errno == EINTR){
                        continue;
                    }
                    ret = -1;
                    read_bytes = 0;
                }else{
                    if (written_bytes == 0) ;
                    *buffer += written_bytes;
                    read_bytes -= written_bytes;
                }
            }

            if(ret == -1){
                close(fdout);
                close(fdin);
                unlink(myfdin);
                unlink(myfdout);
                return -1;
            }  
        }
        if (poll_file_descriptor[0].revents == 0 && poll_file_descriptor[1].revents == 0){
            close(fdout);
            close(fdin);
            unlink(myfdin);
            unlink(myfdout);
            return -1;
        }  
    }
    
    close(fdout);
    close(fdin);
    unlink(myfdin);
    unlink(myfdout);
    
    return 0;
}

$ gcc -o juggle juggle.c

$ tcpserver 192.168.1.100 4444 ./juggle

$ while read cmd; do echo "$cmd"; done > /tmp/std_in_00001
$ cat /tmp/std_out_00001

Edit: formatting

Edited by fugu
Link to comment
Share on other sites

    mkfifo(myfdin, 0666);
    mkfifo(myfdout, 0666);
You might want to clamp down on that a bit...

        poll_file_descriptor[0].events = 1;
POLLIN is the appropriate constant for that 1.

                close(fdout);
                close(fdin);
                unlink(myfdin);
                unlink(myfdout);
                return -1;
All instances of this can be replaced with a simple

break;
This code looks extremely suspect to me:

                    if (written_bytes == 0) ;
                    *buffer += written_bytes;
The if does nothing and adding something to that buffer pointer corrupts the data (increments the first char in buffer with the value of written_bytes).

I would highly recommend against running this code in its current form.

Edited by Cooper
Link to comment
Share on other sites


I would highly recommend against running this code in its current form.

So i agree, those changes you recommended seem really good.

and the

//if (written_bytes == 0) ;
//*buffer += written_bytes;

doesnt seem to do anything. I thought it might look like is was moving the position of the buffer if the write operation had to handle more bytes then where inside the char buffer[] variable, but i tested the program with a very small buffer [20], and I just removed the above 2 lines of extra code, moved much more bytes then coulda fit into the buffer, and it appears to run fine without it.

    mkfifo(myfdin, 0600);
    mkfifo(myfdout, 0600);

just the user level needs to be able to read and write from each pipe, but the others don't need any permissions.

close(fdout);
close(fdin);
unlink(myfdin);
unlink(myfdout);

those were all really redundant, replacing them with breaks simplifies this code.

programming in C is not an area I know a lot about, so thank you for your help. Ill keep working at it to see if I can make it better

Link to comment
Share on other sites

Oh. I thought you plucked that code snippet off the internet somewhere and figured it might be useful. Didn't expect you to have been the author.

That at least changes the intent of the code from something we would hope to be safe but might be deviously, maliciously written to look benign but isn't really to a well-intended suggestion on how to achieve something.

What you really should do is make a separate function that simply reads from one file descriptor and deposits this data into the other file descriptor. There's 2 instances of that in your code and it would clean things up nicely if you could replace that with a function and 2 invocations of that function.

For the polling, I believe you can initialize the array outside of the loop. The revents attribute of the struct is the only 'output' attribute to the poll function so it's safe to reuse as-is.

To continue writing when the write was incomplete, you need to keep track of how far you'd gotten with writing the read buffer so you're just going to have to include another int to keep track. It'll look more obvious once the function is in place.

It's not a problem here, but since we're learning, note that the errno variable is a global to your program. If you were to write a multi-threaded program, it would be impossible for you to be certain that this global was set by your function rather than something from the other thread.

Finally, never test if something is 'error code' unless you want to do something specifically for that error. Defensive coding is to test for the non-succesful situation. In other words, not

if (ret == -1)

But rather

if (ret < 0)
Link to comment
Share on other sites

Ok just to be clear, i'm not the orginal author of this code, i did look through the code before I tested it, and it seemed ok, but I don't know enough C to tell good code from bad code. It might have had ill intent; that little bit that you picked out look benigne to me but that coulda been a big no-no. I probably could even tell a buffer overflow if it was staring me in the face. I was actually more worried about the malloc's memcpy's cause I've seen demos of buffer overflows using those functions. Should there be a myfdin[strlen(buffer)+1] = '\0'; thrown in there too, to be safe? I have no problem modifing this to get it to work for this task, and hoefully learn a little bit more C while Im at it.

I'm working on making a function out of those redirects

int bytes_redirect(struct pollfd poll_file_descriptor, int fdout){

int read_bytes, written_bytes;

char buffer[2048];

if (poll_file_descriptor.revents != 0) {

read_bytes = read(poll_file_descriptor.fd, buffer, sizeof(buffer));

if(read_bytes < 0){

return read_bytes;

}

if(read_bytes == 0){

return = 1;

}

while (read_bytes > 0) {

written_bytes = write(fdout,buffer,read_bytes);

if (written_bytes < 0) {

return written_bytes;

}else{

read_bytes -= written_bytes;

}

}

}

return 0;

}

but it kinda mixes the errors if there is a problem reading or writing, but i don't know if that matters. It should probably just stop if theres an error. Also is there a normal convention for sizing a buffer? I've seen some programs define a MAX_STR_LEN of somthing for the buffer size but i don't know if there is a perferred way to do that.

Link to comment
Share on other sites

I'm more thinking something like this:

#define BUF_SIZE 2048
boolean copy(int fdin, int fdout){
  int read_bytes;
  char buffer[BUF_SIZE];
  do {
    read_bytes = read(infd, buffer, BUF_SIZE);
    if(read_bytes < 0){
      // Failed to read.
      return false;
    } else if (read_bytes != write(fdout,buffer,read_bytes)) {
      // Failed to write the complete buffer.
       return false;
    }
  } while (read_bytes == BUF_SIZE);
  return true;
}
It's all blocking calls so unless there is a problem the calls should just complete. If this function returns true, all data was succesfully transfered. You potentially perform a write of 0 bytes when there was no data to begin with or when the amount of data to write is an exact multitude of BUF_SIZE. No harm - the call will simply return without having done anything.

Is there a normal convention for sizing a buffer? I've seen some programs define a MAX_STR_LEN of somthing for the buffer size but i don't know if there is a perferred way to do that. I was actually more worried about the malloc's memcpy's cause I've seen demos of buffer overflows using those functions. Should there be a myfdin[strlen(buffer)+1] = '\0'; thrown in there too, to be safe?

Not really. The point about the read call is that it will fill up the buffer as much as it can. If you get the complete buffer, that typically means there's more data for you to work with. If you have a chat-like program then the bits of data that need to be transferred will be very small and you can't really buffer it as that would tank responsiveness so you use a small buffer and just read/write often. If you have an ftp-like program then the transferred data is likely to be large chunks. In that case you're best served with a large buffer so that you only need to do a relatively small amount of read/write calls and the kernel has a chance to optimize things around it. So it's up to your program what size buffer you make. Choosing between malloc or a fixed buffer I think is more a matter of taste. Malloc is for the perfectly sized buffer that you shouldn't forget to free (only once!) after use and a sized array is for when you don't know the size up-front and don't want to worry about freeing that data. But you can use either for both situations with sufficient checks. Just make sure you don't go beyond the end of your buffer so only use functions that allow you to specify the length of the data put in or taken from the buffer. That way you don't have to include a '\0' in the buffer yourself as that is only necessary when you treat your _data_ buffer as a _string_ buffer. These are not the same and so should not be treated equally.

Malloc'd buffers are a willing target for exploits because you _have_ to keep track of the size yourself and sometimes programmers get these calculations wrong. Getting it wrong by 1 may be sufficient to get exploited so you need to be very precise. Another interesting coding error is this one:

printf( buffer );
Looks benign, no? Problem is, that buffer is allowed/expected to be a format string to the printf function, so if buffer contains '%s' somewhere suddenly this printf function will go and look for parameters that aren't there. The correct version of that bit is

printf( "%s", buffer );
It's been *years* since I last programmed anything in C, so thanks for this. It's a refreshing experience. :smile: Edited by Cooper
Link to comment
Share on other sites

I like your version a lot better. smaller and just does what I want it too. no extra anything, and it works. I even added one more function for finding an available name for the fifo because I use it twice. this is the function for the name part

char *find_open_named_pipe(const char *file_name_format, int index, int *file_exists){
    char buffer[50];
    char *myfd_str;
    sprintf(buffer,file_name_format, index);
    if(access(buffer , F_OK ) != -1) {
        *file_exists = 1;
    } else {
        *file_exists = 0;
    }
    myfd_str = (char *) malloc(sizeof(char)*(strlen(buffer)));
    memcpy(myfd_str, &buffer, strlen(buffer)+1);
    myfd_str[sizeof(char)*(strlen(buffer))] = '\0';
    return myfd_str;
}

and it's implimented like

        index = 0;
        do{
            index++;
            if(index>=99999){
                return -1;
            }
            myfdin = find_open_named_pipe((const char *)"/tmp/std_in_%05d", index, &file1exists);
            myfdout = find_open_named_pipe((const char *)"/tmp/std_out_%05d", index, &file2exists);
        }while(file1exists == 1 || file2exists == 1);

and when I'm done with it ill post it completed too, in case anyone wants to see/use it

Edit: i can see now, i need to change some of those int's to bools

Edited by fugu
Link to comment
Share on other sites

#include <math.h>

#define FIFO_FOLDER_NAME "/tmp"

static int number_of_chars_to_write_int(int number) {

int sign_length = 0;

if (number < 0) {

sign_length = 1;

number *= -1;

}

return sign_length + (int)log10((double)number);

}

char * construct_filename(char *path, char *file_prefix, int index) {

int filename_length = strlen(path) + 1 + strlen(file_prefix) + number_of_chars_to_write_int(index) + 1;

char *filename = (char *) malloc ( filename_length );

if (filename == NULL) {

return NULL;

}

if (snprintf(filename, filename_length, "%s/%s%d", path, file_prefix, index)<0) {

free( filename );

return NULL;

}

return filename;

}

boolean is_directory(const char *name) {

struct stat statbuf;

return (stat(folder_name, &statbuf)==0) && (S_ISDIR(statbuf.st_mode));

}

char ** find_available_filenames(const char *directory_name, char **filename_prefix, int count) {

int index;

int file_counter;

char ** filenames;

if (count < 1) {

return NULL;

} else if (!is_directory(directory_name)) {

return NULL;

}

filenames = (char**)malloc( count * sizeof(char *) );

if (filenames == NULL) {

return NULL;

}

for (index = 1; index < 100; index++) {

for (file_counter = 0 ; file_counter < count ; file_counter++) {

filenames[file_counter] = construct_filename( directory_name, filename_prefix[file_counter], index );

if (access( filenames[file_counter], F_OK ) == 0) {

// File exists already, so bad index...

// Free all the filenames accumulated so far.

do {

free( filenames[file_counter] );

filenames[file_counter] = NULL;

} while (file_counter-- > 0);

// Break this loop to try a new index.

break;

}

}

if (filenames[0] != NULL) {

// We have our result.

break;

}

}

if (filenames[0] == NULL) {

// No appropriate index found.

free( filenames );

return NULL;

}

return filenames;

}

...

char ** filenames = find_available_filenames( FIFO_FOLDER_NAME, {"std_in_","std_out_"}, 2 );

if (filenames == NULL) {

return -1;

}

...

for (index = 0 ; index < 2 ; index++) {

free( filenames[index] );

}

free( filenames );

Didn't compile so there's probably a few errors in there. Idea here is that you don't want a file, you want a matching pair. There's some extra input validation because if the folder didn't exist for sure the files will not exist either, but you'll be hard-pressed to create them once you've determined them to be available. An additional test could be included that the filename prefixes don't contain '/' characters, but since it's an internal function and the prefixes are hard-coded I figured what the hell. Edited by Cooper
Link to comment
Share on other sites

<erased due to user error>

boolean is_directory(const char *name) {
    struct stat statbuf;
    return (stat(folder_name, &statbuf)==0) && (S_ISDIR(statbuf.st_mode));
}

has a typo,

boolean is_directory(const char *name) {
    struct stat statbuf;
    return (stat(name, &statbuf)==0) && (S_ISDIR(statbuf.st_mode));
}

I'm still having trouble getting it to compile too, but ill keep trying

Edited by fugu
Link to comment
Share on other sites

This compiles.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#define FIFO_FOLDER_NAME "/tmp"
static int number_of_chars_to_write_int(int number) {
    int sign_length = 0;
    if (number < 0) {
        sign_length = 1;
        number *= -1;
    }
    return sign_length + (int)log10((double)number);
}
char * construct_filename(const char *path, char *file_prefix, int index) {
    int filename_length = strlen(path) + 1 + strlen(file_prefix) + number_of_chars_to_write_int(index) + 1;
    char *filename = (char *) malloc ( filename_length );
    if (filename == NULL) {
        return NULL;
    }
    if (snprintf(filename, filename_length, "%s/%s%d", path, file_prefix, index)<0) {
        free( filename );
        return NULL;
    }
    return filename;
}
int is_directory(const char *name) {
    struct stat statbuf;
    return (stat(name, &statbuf)==0) && (S_ISDIR(statbuf.st_mode));
}
char ** find_available_filenames(const char *directory_name, char **filename_prefix, int count) {
    int index;
    int file_counter;
    char ** filenames;
    if (count < 1) {
        return NULL;
    } else if (!is_directory(directory_name)) {
        return NULL;
    }
    filenames = (char**)malloc( count * sizeof(char *) );
    if (filenames == NULL) {
        return NULL;
    }
    for (index = 1; index < 100; index++) {
        for (file_counter = 0 ; file_counter < count ; file_counter++) {
            filenames[file_counter] = construct_filename( directory_name, filename_prefix[file_counter], index );
            if (access( filenames[file_counter], F_OK ) == 0) {
                // File exists already, so bad index...
                // Free all the filenames accumulated so far.
                do {
                    free( filenames[file_counter] );
                    filenames[file_counter] = NULL;
                } while (file_counter-- > 0);
                // Break this loop to try a new index.
                break;
            }
        }
        if (filenames[0] != NULL) {
            // We have our result.
            break;
        }
    }
    if (filenames[0] == NULL) {
        // No appropriate index found.
        free( filenames );
        return NULL;
    }
    return filenames;
}
Link to comment
Share on other sites

i also had a thought. if this can create the stdin and stdout as files, could it not also read from those 2 files turning your command prompt into an interface for those fifos? i think it might be able to work, but it is not working for me. i don't know if it has anything to do with the blocking nature of the 'open' commands inside the program. I was thinking about adding the '| O_NONBLOCKING' flags to the open commands, then adding my own little wait so that the fifos can connect in any order they want to (i.e. not needing to connect the fdin's first then the fdout's second the way it is doing it right now).

$ gcc -o juggle ./juggle_fifos.c -lm
#include <fcntl.h>
#include <math.h>
#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

#define FIFO_FOLDER_NAME "/tmp"
#define BUF_SIZE 2048
bool bytes_redirect(int fdin, int fdout){
    int read_bytes;
    char buffer[BUF_SIZE];
    do {
        read_bytes = read(fdin, buffer, BUF_SIZE);
        if(read_bytes < 0){
            // Failed to read.
            return false;
        } else if (read_bytes != write(fdout,buffer,read_bytes)) {
            // Failed to write the complete buffer.
            return false;
        }
    } while (read_bytes == BUF_SIZE);
    return true;
}

static int number_of_chars_to_write_int(int number) {
    int sign_length = 0;
    if (number < 0) {
        sign_length = 1;
        number *= -1;
    }
    return sign_length + (int)log10((double)number);
}
char * construct_filename(const char *path, char *file_prefix, int index) {
    int filename_length = strlen(path) + 1 + strlen(file_prefix) + number_of_chars_to_write_int(index) + 1;
    char *filename = (char *) malloc ( filename_length );
    if (filename == NULL) {
        return NULL;
    }
    if (snprintf(filename, filename_length, "%s/%s%d", path, file_prefix, index)<0) {
        free( filename );
        return NULL;
    }
    return filename;
}
int is_directory(const char *name) {
    struct stat statbuf;
    return (stat(name, &statbuf)==0) && (S_ISDIR(statbuf.st_mode));
}
char ** find_available_filenames(const char *directory_name, char **filename_prefix, int count) {
    int index;
    int file_counter;
    char ** filenames;
    if (count < 1) {
        return NULL;
    } else if (!is_directory(directory_name)) {
        return NULL;
    }
    filenames = (char**)malloc( count * sizeof(char *) );
    if (filenames == NULL) {
        return NULL;
    }
    for (index = 1; index < 100; index++) {
        for (file_counter = 0 ; file_counter < count ; file_counter++) {
            filenames[file_counter] = construct_filename( directory_name, filename_prefix[file_counter], index );
            if (access( filenames[file_counter], F_OK ) == 0) {
                // File exists already, so bad index...
                // Free all the filenames accumulated so far.
                do {
                    free( filenames[file_counter] );
                    filenames[file_counter] = NULL;
                } while (file_counter-- > 0);
                // Break this loop to try a new index.
                break;
            }
        }
        if (filenames[0] != NULL) {
            // We have our result.
            break;
        }
    }
    if (filenames[0] == NULL) {
        // No appropriate index found.
        free( filenames );
        return NULL;
    }
    return filenames;
}

int main(int argc, char *argv[]){
    struct pollfd poll_file_descriptor[2];
    char *myfdin = "";
    char *myfdout = "";
    char *inputargs[] = {"std_in_","std_out_"};
    char **filenames;
    int fdin, fdout, index = 0;
    bool file1exists, file2exists;
    bool continue_loop = true;

    if(argc > 2){
        myfdin = argv[1];
        myfdout = argv[2];
    }else{
        filenames = find_available_filenames(FIFO_FOLDER_NAME, inputargs, 2);

        myfdin = filenames[0];
        myfdout = filenames[1];

        mkfifo(myfdin, 0600);
        mkfifo(myfdout, 0600);
    }
    
    fdin = open(myfdin, O_RDONLY);
    if(fdin < 0){
        return -1;
    }else{
        free(myfdin);
    }
    fdout = open(myfdout, O_WRONLY);
    if(fdout < 0){
        return -1;
    }else{
        free(myfdout);
    }

    poll_file_descriptor[0].fd = fdin;
    poll_file_descriptor[0].events = POLLIN;
    poll_file_descriptor[0].revents = 0;
    poll_file_descriptor[1].fd = STDIN_FILENO;
    poll_file_descriptor[1].events = POLLIN;
    poll_file_descriptor[1].revents = 0;

    while (continue_loop) {
        poll(poll_file_descriptor,2,1200000);
        if (poll_file_descriptor[0].revents != 0) {
            continue_loop = bytes_redirect(fdin, STDOUT_FILENO);
            if(!continue_loop){
                break;
            }
        }
        if (poll_file_descriptor[1].revents != 0) {
            continue_loop = bytes_redirect(STDIN_FILENO, fdout);
            if(!continue_loop){
                break;
            }
        }
        if (poll_file_descriptor[0].revents == 0 && poll_file_descriptor[1].revents == 0){
                break;
        }  
    }
    close(fdout);
    close(fdin);
    unlink(myfdin);
    unlink(myfdout);
    return 0;
}
Edited by fugu
Link to comment
Share on other sites

I don't quite understand what it is you're after. Do you want this to operate as a terminal or something? Trust me when I say that that's a very bad idea. Those things are rather more complicated than you'd guess.

NONBLOCK is something you use when you can't be arsed to wait for something because you've got something better to do. You don't (well, at least with the program in it's current form).

Regarding the code, your "myfdin/out" variables are now very poorly named since they're filenames, not descriptors. You're assuming that giving in 2 filenames means those files exist and they are fifos, plus you're removing them when done. I think you want this option to simply specify the filenames to use, the program should always use mkfifo to make the fifos and fail if either of the files already existed (note that mkfifo can return something to indicate an error). Instead of using argv[1] and argv[2] as filenames, you should use them for the inputargs variable to find_available_filenames. Currently the unlink calls at the end use memory which you freed directly after opening the file named by that variable. You should open both files, have a single if that tests if fdin and fdout are >=0 and if so start polling. After polling or when either fd was <0, close any fd that's still >=0, unlink the named files, free the memory containing those filenames and then return 0.

Edited by Cooper
Link to comment
Share on other sites

Ok I realize i couldn't get your code to work for the filenames. For some reason I just couldn't get it to compile. My goal for whole project this was to make an interface for the program 'tcpserver' from the package "ucspi-tcp". It basically creates a a multithreaded server that can host 1 program over and over again, one time for each thread. This is the program that I would like to have it host, and I want it to generate a pair of fifo pipes that I can interacte with, when I chose to do so (by running another instance of it maybe). I know terminals are much more complicated but I'm looking at this as kind of a simple multithreaded netcat server application, reading from the pipes can really be done by any program, but i was thinking this same program might be able to do it too, having all the tools needed to do this already in place. I think checking the fd's before and after polling is a great idea, but can't we still close those connections at the end of the program, just before we return 0?
I reverted back to my orginal filename generating system, so that I could get it to work. And it's now functioning:) I hope that I'm explaining well enough what I was hoping to accomplish with this.

Term1
server$ tcpserver 192.168.1.100 4444 juggle
client1$ nc 192.168.1.100 4444
hello world 1
client2$ nc 192.168.1.100 4444
hello world 2
client3$ nc 192.168.1.100 4444
...
Term2
server$ juggle /tmp/std_out_00001 /tmp/std_in_00001
hello world 1
Term3
server$ juggle /tmp/std_out_00002 /tmp/std_in_00002
hello world 2
here is my current working version, with the filenames fixed, only delete fifos that we create, only close fd's that are still open, there is a problem with shuting down the program and getting it to cleanup the fifo's that are made. When you SIGINT on any of the parts, it doesn't delete the fifo's like I'd think it should. In bash there is the "trap" command which could clean this up, but afaik c doesn't impliment. Also, because it's not running to the end of the program, I'm having trouble testing what actually happens when the program gets there.
#include <fcntl.h>
#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define BUF_SIZE 2048
bool bytes_redirect(int fdin, int fdout){
    int read_bytes;
    char buffer[BUF_SIZE];
    do {
        read_bytes = read(fdin, buffer, BUF_SIZE);
        if(read_bytes < 0){
            // Failed to read.
            return false;
        } else if (read_bytes != write(fdout,buffer,read_bytes)) {
            // Failed to write the complete buffer.
            return false;
        }
    } while (read_bytes == BUF_SIZE);
    return true;
}

char *find_open_named_pipe(const char *file_name_format, int index, bool *file_exists){
    char buffer[50];
    char *myfd_str;
    sprintf(buffer,file_name_format, index);
    if(access(buffer , F_OK ) != -1) {
        *file_exists = true;
    } else {
        *file_exists = false;
    }
    myfd_str = (char *) malloc(sizeof(char)*(strlen(buffer)));
    memcpy(myfd_str, &buffer, strlen(buffer)+1);
    return myfd_str;
}

int main(int argc, char *argv[]){
    struct pollfd poll_file_descriptor[2];
    char *myfdin = "";
    char *myfdout = "";
    int fdin, fdout, index = 0;
    bool file1exists, file2exists;
    bool continue_loop = true;
    bool i_created_fifos;

    if(argc > 2){
        myfdin = argv[1];
        myfdout = argv[2];
        fdout = open(myfdout, O_WRONLY); //different order, out first
        fdin = open(myfdin, O_RDONLY);
        i_created_fifos = false;
    }else{
        index = 0;
        do{
            index++;
            if(index>=99){
                return -1;
            }
            myfdin = find_open_named_pipe((const char *)"/tmp/std_in_%05d", index, &file1exists);
            myfdout = find_open_named_pipe((const char *)"/tmp/std_out_%05d", index, &file2exists);
        }while(file1exists || file2exists );
        mkfifo(myfdin, 0600);
        mkfifo(myfdout, 0600);
        fdin = open(myfdin, O_RDONLY); //different order, in first
        fdout = open(myfdout, O_WRONLY);
        i_created_fifos = true;
    }

    poll_file_descriptor[0].fd = fdin;
    poll_file_descriptor[0].events = POLLIN;
    poll_file_descriptor[0].revents = 0;
    poll_file_descriptor[1].fd = STDIN_FILENO;
    poll_file_descriptor[1].events = POLLIN;
    poll_file_descriptor[1].revents = 0;

    while (continue_loop) {
        if(fdin < 0 || fdout < 0){
            break;
        }
        poll(poll_file_descriptor,2,1200000);
        if(fdin < 0 || fdout < 0){
            break;
        }
        if (poll_file_descriptor[0].revents != 0) {
            continue_loop = bytes_redirect(fdin, STDOUT_FILENO);
            if(!continue_loop){
                break;
            }
        }
        if (poll_file_descriptor[1].revents != 0) {
            continue_loop = bytes_redirect(STDIN_FILENO, fdout);
            if(!continue_loop){
                break;
            }
        }
        if (poll_file_descriptor[0].revents == 0 && poll_file_descriptor[1].revents == 0){
                break;
        }  
    }
    if(fdout >=0)
        close(fdout);
    if(fdin >=0)
        close(fdin);
    if(i_created_fifos){
        unlink(myfdin);
        unlink(myfdout);
        free(myfdin);
        free(myfdout);
    }
    return 0;
}
Edited by fugu
Link to comment
Share on other sites

Ok I realize i couldn't get your code to work for the filenames. For some reason I just couldn't get it to compile. My goal for whole project this was to make an interface for the program 'tcpserver' from the package "ucspi-tcp". It basically creates a a multithreaded server that can host 1 program over and over again, one time for each thread. This is the program that I would like to have it host, and I want it to generate a pair of fifo pipes that I can interacte with, when I chose to do so (by running another instance of it maybe). I know terminals are much more complicated but I'm looking at this as kind of a simple multithreaded netcat server application, reading from the pipes can really be done by any program, but i was thinking this same program might be able to do it too, having all the tools needed to do this already in place. I think checking the fd's before and after polling is a great idea, but can't we still close those connections at the end of the program, just before we return 0?

I reverted back to my orginal filename generating system, so that I could get it to work. And it's now functioning:) I hope that I'm explaining well enough what I was hoping to accomplish with this.

The issue with that idea is ucspi-tcp is that I think that's a TCP superserver - It listens on behalf of server programs on the designated port and when a connection is made it starts the server program and sets itself up as the gateway to the program exactly as you're trying to do now, except that tcpd is using STDIN and STDOUT whereas you're using fifos. Using tcpd has the added benefit that using hosts.deny and hosts.allow files you can specify which services are or aren't available to which (group of) IPs/hostnames. The issue within the context of your program is that it will create a fifo on demand, so when someone connects to port 4444, tcpd will start your program, it in turn creates the fifos and then.... nothing. Someone has to manually start something to work those fifo's exact. Unless you specify the fifo's in your tcpd configuration as fixed values, which would make your service behave erratically once multiple clients connect simultaneously there's no way to automate something picking up the other side of the FIFOs unless your program starts that up itself... which is kinda what tcpd was already doing so you've just added a secondary conduit on the way to the service, whatever that may be, without really adding something beneficial.

We're allowed to close the FDs, but tcpd is likely to detect this and possibly have some behaviour attached to it (like, say, kill the program since it can't interact with it anymore).

here is my current working version, with the filenames fixed, only delete fifos that we create, only close fd's that are still open, there is a problem with shuting down the program and getting it to cleanup the fifo's that are made. When you SIGINT on any of the parts, it doesn't delete the fifo's like I'd think it should. In bash there is the "trap" command which could clean this up, but afaik c doesn't impliment. Also, because it's not running to the end of the program, I'm having trouble testing what actually happens when the program gets there.

C doesn't do anything you didn't specifically tell it to. The problem you have with SIGINT is that the standard behaviour of a C program in response to ANY signal is to terminate immediately. The only way to prevent that is to attach a signal handler to your program for the signal you're expecting. Use the sigaction function for this (example). Your signal handler doesn't have to _do_ anything, it just has to be there to be called when the signal arrives. The behaviour of interrupting the blocked call will have occurred and can be handled by your regular code but the signal itself simply needs to be acknowledged. Note that it's impossible to attach a signal handler to the SIGKILL signal for obvious reasons and a number of other signals can be handled but typically imply that your program's memory space is fucked to the point of no return and you really shouldn't be trying to recover from it (SIGBUS comes to mind).

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...