Tag Archives: pipe
C++ || Multi-Hash Interprocess Communication Using Fork, Popen, & Pipes
The following is another homework assignment which was presented in an Operating Systems Concepts class. Using two pipes, the following is a program which implements the computing of hash values on a file using the MD5, SHA1, SHA224, SHA256, SHA384, and SHA512 hashing algorithms provided on Unix based systems.
REQUIRED KNOWLEDGE FOR THIS PROGRAM
==== 1. OVERVIEW ====
Hash algorithms map large data sets of variable length (e.g. files), to data sets of a fixed length. For example, the contents of a 1GB file may be hashed into a single 128-bit integer. Many hash algorithms exhibit an important property called an avalanche effect – slight changes in the input data trigger significant changes in the hash value.
Hash algorithms are often used for verifying the integrity of files downloaded from the WEB. For example, websites hosting a file usually post the hash value of the file using the MD5 hash algorithm. By doing this, the user can then verify the integrity of the downloaded file by computing the MD5 algorithm on their own, and compare their hash value against the hash value posted on the website. The user will know if the download was valid only if the two hash values match.
==== 2. TECHNICAL DETAILS ====
The following implements a program for computing the hash value of a file using the MD5, SHA1, SHA224, SHA256, SHA384, and SHA512 hashing algorithms provided on Unix based systems.
This program takes the name of the target file being analyzed as a command line argument, and does the following:
1. Check to make sure the file exists.
2. Create two pipes.
3. Create a child process.
4. The parent transmits the name of the file to the child (over the first pipe).
5. The child receives the name of the file and computes the hash of the file using the MD5 algorithm (using Linux program md5sum).
6. The child transmits the computed hash to the parent (over the second pipe) and terminates.
7. The parent receives the hash, prints it, and calls wait().
8. Repeat the same process starting with step 3, but using algorithms SHA1...SHA512.
9. The parent terminates after all hashes have been computed.
The use of the popen function is used in order to launch the above programs and capture their output into a character array buffer.
This program also uses two pipes. The two pipes created are the following:
(1) Parent to child pipe: Used by the parent to transfer the name of the file to the child. The parent writes to this pipe and the child reads it.
(2) Child to parent pipe: Used by the child to transfer the computed hashes to the parent. The child writes to this pipe and the parent reads it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
// ============================================================================= // Author: K Perkins // Date: Aug 20, 2013 // Taken From: http://programmingnotes.org/ // File: Multihash.cpp // Description: Hash algorithms map large data sets of variable length (e.g. // files), to data sets of a fixed length. For example, contents of a // 1GB file may be hashed into a single 128-bit integer. Using 2 pipes, // this program implements the computing of hash values on a file using // the MD5, SHA1, SHA224, SHA256, SHA384, and SHA512 hashing algorithms. // // The two pipes created are the following: // (1) Parent to child pipe: Used by the parent to transfer the name of // the file to the child. The parent writes to this pipe and the // child reads it. // (2) Child to parent pipe: Used by the child to transfer the computed // hashes to the parent. The child writes to this pipe and the // parent reads it. // ============================================================================= #include <iostream> #include <fstream> #include <cstdlib> #include <cstring> #include <unistd.h> #include <sys/wait.h> using namespace std; // compile: g++ Multihash.cpp -o Multihash // run: ./Multihash <file name> // parentToChild = pipe for parent to child communication // childToParent = pipe for child to parent communication int parentToChild[2]; int childToParent[2]; // names of the hash programs const char hashAlgs[6][10] = {"md5sum", "sha1sum", "sha224sum", "sha256sum", "sha384sum", "sha512sum"}; // read end of pipe const int READ_END = 0; // write end of pipe const int WRITE_END = 1; // number of hash programs const int NUM_HASH_ALGS = sizeof(hashAlgs)/sizeof(hashAlgs[0]); // maximum length of hash value const int HASH_LENGTH = 1000; // maximum length of file name const int FILENAME_LENGTH = 100; // child function which computes the hash of a file and // returns the value back to the parent // @param 'hashAlg' == the name of the hash program void ComputeHash(const char hashAlg[]); int main(int argc, char* argv[]) { // declare variables ifstream infile; // used to see if file exists char hashValue[HASH_LENGTH]; // hash value which child passes to parent // check to see if theres enough commandline args if(argc < 2) { cerr<<"n** ERROR - NOT ENOUGH ARGUMENTS!n" <<"nUSAGE: "<<argv[0]<<" <FILE NAME>n"; exit(1); } // try to open file infile.open(argv[1]); // if file doesnt exist, then exit if(infile.fail()) { cerr<<"n** ERROR!n" <<"Cant find the file ""<<argv[1]<<""!nn"; exit(1); } infile.close(); // start the piping process for(int currentAlg = 0; currentAlg < NUM_HASH_ALGS; ++currentAlg) { // create pipes if((pipe(parentToChild) < 0) || (pipe(childToParent) < 0)) { cerr << "npipe failedn"; exit(1); } // create & fork a child pid_t pid = fork(); // make sure fork succeeded if(pid < 0) { cerr << "nfork failedn"; exit(1); } // child process else if(pid == 0) { ComputeHash(hashAlgs[currentAlg]); } // parent process else { // close selected pipes ends close(parentToChild[READ_END]); close(childToParent[WRITE_END]); // pass the filename to the child write(parentToChild[WRITE_END], argv[1], strlen(argv[1])+1); close(parentToChild[WRITE_END]); // read the incoming hash value from the child read(childToParent[READ_END], hashValue, sizeof(hashValue)); close(childToParent[READ_END]); // wait for child to complete wait(NULL); // display current hash to the screen cout<<"Hash Algorithm #"<<currentAlg+1<<":n"<<hashAlgs[currentAlg] <<" - HASH VALUE: "<<hashValue<<endl; // reset hash buffer memset(hashValue, (char)NULL, sizeof(hashValue)); } } cerr<<"The parent process is now exiting...n"; return 0; }// end of main void ComputeHash(const char hashAlg[]) { // declare variables char recievedFileName[FILENAME_LENGTH]; // saves recieved filename char cmdLine[FILENAME_LENGTH]; // saves command line for popen char hashOutput[HASH_LENGTH]; // saves final hash output FILE* popenOutput; // popen file pointer // close selected pipes ends close(parentToChild[WRITE_END]); close(childToParent[READ_END]); // get filename from parent read(parentToChild[READ_END], recievedFileName, sizeof(recievedFileName)); close(parentToChild[READ_END]); // construct command line argument to pass to popen strncpy(cmdLine, hashAlg, sizeof(cmdLine)); strncat(cmdLine, " ", sizeof(cmdLine)); strncat(cmdLine, recievedFileName, sizeof(cmdLine)); // get has value for current hash prog popenOutput = popen(cmdLine, "r"); // make sure that popen succeeded if(!popenOutput) { cerr<<"npopen failedn"; exit(1); } // set hash buffer to all NULLS memset(hashOutput, (char)NULL, sizeof(hashOutput)); // read program output into buffer fread(hashOutput, sizeof(char), sizeof(char)*sizeof(hashOutput), popenOutput); // close popen buffer pclose(popenOutput); // pass hash value back to parent write(childToParent[WRITE_END], hashOutput, strlen(hashOutput)+1); close(childToParent[WRITE_END]); // exit 'AKA' kill child exit(0); }// http://programmingnotes.org/ |
QUICK NOTES:
The highlighted lines are sections of interest to look out for.
The code is heavily commented, so no further insight is necessary. If you have any questions, feel free to leave a comment below.
Using the following example input file located here, the following is sample output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Hash Algorithm #1: md5sum - HASH VALUE: cd2f02cc5e50bd08d83cef630a32d7d6 INPUT_Dictionary_programmingnotes_freeweq_com.txt Hash Algorithm #2: sha1sum - HASH VALUE: 1e1d2fd77b331fc2b2f24822fdf2133f3678d662 INPUT_Dictionary_programmingnotes_freeweq_com.txt Hash Algorithm #3: sha224sum - HASH VALUE: bf69da0f9f9990cece8dd6800a25a9faca5381c0c62e0667c258e8d5 INPUT_Dictionary_programmingnotes_freeweq_com.txt Hash Algorithm #4: sha256sum - HASH VALUE: a8cd7a082ce571f7e66bc4d5eea8f71e7e455735922c90c060580b51d43593ec INPUT_Dictionary_programmingnotes_freeweq_com.txt Hash Algorithm #5: sha384sum - HASH VALUE: a564df501470a43eebb51c28f83ab07090e39c434fc40bf496622f536c525140301633d6513dec83ea512bcb38c4e2e6 INPUT_Dictionary_programmingnotes_freeweq_com.txt Hash Algorithm #6: sha512sum - HASH VALUE: e686a709b3025f8b21fd6d40ce92f741dd8644335e935baced86098ff3e7278b5443e28f02fa886e7c0cd391ce6e8aa91842a6de29535a40453d41862a0bc1c7 INPUT_Dictionary_programmingnotes_freeweq_com.txt The parent process is now exiting... |
C++ || Snippet – How To Use Fork & Pipe For Interprocess Communication
The following is sample code which demonstrates the use of the fork, read, and write function calls for use with pipes on Unix based systems.
A pipe is a mechanism for interprocess communication. Data written to a pipe by one process can be read by another process. Creating a pipe is achieved by using the pipe function, which creates both the reading and writing ends of the pipe file descriptor.
In typical use, a parent process creates a pipe just before it forks one or more child processes. The pipe is then used for communication between either the parent or child processes, or between two sibling processes.
A real world example of this kind of communication can be seen in all operating system terminal shells. When you type a command in a shell, it will spawn the executable represented by that command with a call to fork. A pipe is opened to the new child process, and its output is read and printed by the terminal.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
// ============================================================================ // Author: Kenneth Perkins // Date: Aug 19, 2013 // Taken From: http://programmingnotes.org/ // File: ForkPipeExample.cpp // Description: Demonstrate the use of the fork() and pipe() command for // parent and child interprocess communication. // ============================================================================ #include <iostream> #include <cstdlib> #include <cstring> #include <sys/wait.h> #include <unistd.h> using namespace std; // global variables const int BUFFER_SIZE = 256; const int READ_END = 0; const int WRITE_END = 1; int main() { // declare variables int fd[2]; // pipe file descriptor pid_t pid = -1; char writeMsg[BUFFER_SIZE] = "Greetings From Your Parent!"; char readMsg[BUFFER_SIZE]; // create a pipe if(pipe(fd) < 0) { perror("pipe"); exit(1); } cout <<"\nParent is forking a child." << endl; // create a duplicate process of this current program pid = fork(); // exit if something went wrong with the fork if(pid < 0) { perror("fork"); exit(1); } // this code only gets executed by the child process else if(pid == 0) { cout <<"\nStarting the child process..\n\n"; // close the "write" end b/c we arent using it close(fd[WRITE_END]); // get data from the pipe using the read() function read(fd[READ_END], readMsg, sizeof(readMsg)); // close the "read" end close(fd[READ_END]); cout <<"Message from the parent via the pipe: "<<readMsg<<endl; } // the parent process executes here else { cout <<"\nParent is now waiting for child id #"<<pid<<" to complete..\n"; // close the "read" end b/c we arent using it close(fd[READ_END]); // write data on pipe write(fd[WRITE_END], writeMsg, strlen(writeMsg)+1); // close the "write" end close(fd[WRITE_END]); // use the "wait" function to wait for the child to complete wait(NULL); cout <<"\nThe child process is complete and has terminated!\n"; } // NOTE: this message gets displayed twice - Why?! cout <<"\nProgram is now exiting...\n"; return 0; }// http://programmingnotes.org/ |
QUICK NOTES:
The highlighted lines are sections of interest to look out for.
The code is heavily commented, so no further insight is necessary. If you have any questions, feel free to leave a comment below.
The following is sample output:
Parent is forking a child.
Parent is now waiting for child id #12776 to complete..Starting the child process..
Message from the parent via the pipe: Greetings From Your Parent!
Program is now exiting...
The child process is complete and has terminated!
Program is now exiting...