I'm new to this kind of programming so i'm sorry in advance if it's a dump question.I'm trying to do a very simple task but i don't seem to get what goes wrong.
I have a parent process that creates a number of children processes, and with the usage of FIFOs i want to send a message to all of the children (e.g "hi"), the message get's received by the processes but there is an error coming up no matter what i do and can't seem to find what's wrong.
Here is the parent's main function:
int main(int argc, char *argv[])
{
int num_monitors, buf_size; //command line arguments
num_monitors = stoi(argv[1]);
buf_size = stoi(argv[2]);
// Structures to store monitor info
Monitors *m_info = new Monitors[num_monitors]; // Stores monitor pid & pipe ends
create_n_monitors(m_info,num_monitors,buf_size,input_dir_path);
sleep(1); // making sure all pipes get created
for(int i=0; i<num_monitors; i++) // opening the write end now that the monitors have been created
{
m_info[i].write_fd = open(m_info[i].write_p, O_WRONLY | O_NONBLOCK);
if (m_info[i].write_fd == -1){perror("open @ 27 main parent");exit(1);}
}
for(int i=0; i<num_monitors; i++)
send_message(m_info[i].write_fd, (char*)"hi", buf_size);
delete [] m_info;
return 0;
}
Here's the class where i keep every process information stored:
class Monitors
{
public:
pid_t m_pid; // monitors's PID
char read_p[32]; // write monitor - read parent pipe name
char write_p[32]; // read monitor - write parent pipe name
int read_fd; // file descriptor for read fifo of monitor
int write_fd; // file descriptor for write fifo of monitor
Monitors();
~Monitors();
};
Here's how i create the processes and the pipes(FIFOs):
void create_n_monitors(Monitors *m_info, int num_monitors, int buf_size, char *input_dir)
{
create_unique_fifo(true, NULL, 0); // creates a fifo file
for (int i = 0; i < num_monitors; ++i) // create num monitors
create_monitor(m_info, i, buf_size, input_dir);
}
/* ========================================================================= */
// Create a monitor and it's named fifos. Store it's info in <m_info[index]>.
void create_monitor(Monitors *m_info, int index, int buf_size, char *input_dir)
{
create_unique_fifo(false, m_info, index); // Create fifos
pid_t pid = fork();
if(pid == -1) {
perror("fork");
exit(1);
}
else if ( pid == 0) { // we are in the child monitor, read_p(read parent) : monitor's write end of the fifo
// write_p(write parent): monitor's read end
char buf_size_str[15];
sprintf(buf_size_str, "%d", buf_size); // buf_size must be a char*
execl("./Monitor","Monitor", buf_size_str, m_info[index].read_p, m_info[index].write_p, (char * )NULL);
perror("execl");
exit(1);
}
//else
m_info[index].m_pid = pid; // Store it's pid
}
/* ========================================================================= */
// If <setup> is true, create a directory to store fifos.
void create_unique_fifo(bool setup, Monitors *m_info, int index)
{
static char fifo_name[32];
static int counter = 0;
if (setup == true)
{
char dir_path[] = "named_fifos";
if (access(dir_path, F_OK) == 0) // If dir already exists (due to abnormal previous termination, eg: SIGKILL)
delete_flat_dir(dir_path); // completely remove it
if (mkdir(dir_path, 0777) == -1){perror("mkdir @ unique_fifo");exit(1);}
sprintf(fifo_name, "named_fifos/f");
return;
}
struct stat stat_temp;
// Create a unique name (e.g named_fifos1R , named_fifos6W )
sprintf(m_info[index].read_p, "%s%d%c", fifo_name, counter, 'R');
// Create fifos
if(stat(m_info[index].read_p, &stat_temp) == -1){
if (mkfifo(m_info[index].read_p,0666) < 0 ){perror("mkfifo @ unique_fifo");exit(1);}
}
m_info[index].read_fd = open(m_info[index].read_p, O_RDONLY | O_NONBLOCK);
if (m_info[index].read_fd == -1) {perror("open @ 73 setup_monitors");exit(1);}
sprintf(m_info[index].write_p, "%s%d%c", fifo_name, counter, 'W');
++counter; // counter used for pipe names
}
/* ========================================================================= */
// Remove a flat directory and its contents.
void delete_flat_dir(char *init_flat_path)
{
char flat_path[32];
strcpy(flat_path, init_flat_path);
DIR *dir = opendir(flat_path);
if (dir == NULL){perror("opendir @ delete_flat_dir"); exit(1);}
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) // Delete contents/files
{
char *f_name = entry->d_name;
if (!strcmp(f_name, ".") || !strcmp(f_name, ".."))
continue;
char f_path[32];
snprintf(f_path, 32, "%s/%s", flat_path, f_name); // Remove file
if (remove(f_path) == -1){perror("remove @ delete_flat_dir"); exit(1);}
}
// Remove dir
if (closedir(dir) == -1){perror("closedir 2 @ delete_flat_dir"); exit(1);}
if (rmdir(flat_path) == -1){perror("rmdir @ delete_flat_dir"); exit(1);}
}
Here are the functions used for the communication of the processes and parent:
// Sends <message> to file descriptor <fd>
void send_message(int fd, char *message, int buf_size)
{
int length = strlen(message);
char buffer[10];
sprintf(buffer, "%d@", length);
write(fd, buffer, 9); // sending the number of bytes reader is about to read
write(fd, message, length); // sending the message itself
}
char *read_message(int read_end_fd, int buf_size)
{
char buffer[10];
int fifo_buffer_size = buf_size;
read(read_end_fd, buffer, 9);
char * tok = strtok(buffer, "@");
int length = atoi(tok); // how many characters will be received
char * input_read = new char[length + 1];
char * str = input_read;
int bytes_read = 0, total_bytes = 0; // We might need to read less or more bytes
fifo_buffer_size = length < fifo_buffer_size ? length : fifo_buffer_size; // // than <buf_size>
while(total_bytes < length)
{
str += bytes_read; // move str pointer
bytes_read = read(read_end_fd, str, fifo_buffer_size); //and read the next <buf_size> characters
total_bytes += bytes_read; // adding them to the total amount of bytes read altogether
if((total_bytes + fifo_buffer_size) > length)
fifo_buffer_size = length - total_bytes; // reading exactly the amount that's left
}
input_read[length] = '\0';
return input_read;
}
And lastly the processes main function:
int create_unique_fifo(char* read_fifo)
{
int read_fd;
struct stat stat_temp;
// Create fifos
if(stat(read_fifo, &stat_temp) == -1){
if (mkfifo(read_fifo,0666) < 0 ){perror("mkfifo @ unique_fifo 37");exit(1);}
}
read_fd = open(read_fifo, O_RDONLY); // Open named pipe for reading
if (read_fd == -1){perror("open @ monitor.cpp 1"); exit(1);}
return read_fd;
}
int main(int argc, char* argv[])
{
int buf_size = stoi(argv[1]); // Process command line args
char read_fifo[100], write_fifo[100];
strcpy(write_fifo, argv[2]); // parent read - monitor write
strcpy(read_fifo, argv[3]); // parent write - monitor read
int read_fd, write_fd;
read_fd = create_unique_fifo(read_fifo);
write_fd = open(write_fifo, O_WRONLY | O_NONBLOCK); // Open named pipe for writing
if (write_fd == -1){perror("open @ monitor.cpp 2"); exit(1);}
char* message;
message = read_message(read_fd, buf_size);
cout << "2:" << message << endl;
return 0;
}
I'm using the valgrind debugger and get the following result when num_monitors=5 and buf_size=2:
==29783== Syscall param write(buf) points to uninitialised byte(s)
==29783== at 0x4B691E7: write (write.c:26)
==29783== by 0x10B85B: send_message(int, char*, int) (in /home/sofia/Desktop/syspro-2/travelMonitor)
==29783== by 0x10A80F: main (in /home/sofia/Desktop/syspro-2/travelMonitor)
==29783== Address 0x1ffefffc81 is on thread 1's stack
==29783== in frame #1, created by send_message(int, char*, int) (???:)
==29783==
2:hi
2:hi
2:hi
2:hi
2:hi
==29783==
==29783== HEAP SUMMARY:
==29783== in use at exit: 0 bytes in 0 blocks
==29783== total heap usage: 4 allocs, 4 frees, 138,864 bytes allocated
==29783==
==29783== All heap blocks were freed -- no leaks are possible
==29783==
==29783== Use --track-origins=yes to see where uninitialised values come from
==29783== For lists of detected and suppressed errors, rerun with: -s
==29783== ERROR SUMMARY: 5 errors from 1 contexts (suppressed: 0 from 0)
Any idea why this could be happening? I added sleep(1) after the creation of the processes to make sure all the fifos get created in time but still get this error... Any help would be much appreciated
In send_message
, you always write 9 bytes from buffer
, but not all of those bytes have had a value written to them. This is because the sprintf
that populates buffer
only writes to the first few locations, possibly as few as 3.
The solution is to either initialize them all to 0
char buffer[10] = 0;
or only write the number of bytes that are in the string. This is easily known from the value returned by sprintf
.
auto buf_len = sprintf(buffer, "%d@", length);
write(fd, buffer, buf_len);