Question: Purpose: To go over multiprocessing on a single machine: pipes/socket pairs fork()/execl() Overview: Cryptographic hash functions are hash functions that are designed to be relatively
Purpose:
To go over multiprocessing on a single machine:
- pipes/socket pairs
- fork()/execl()
Overview:
Cryptographic hash functions are hash functions that are designed to be relatively easy to compute but hard to figure out which sequences of bytes yields a given hash value. (Computation in reverse.)
On Linux systems the command-line tool openssl does several things, including computing some common hash functions. For example, if we make a little file:
$ cat > print.txt Greetings, how are you? (press Enter) (press Ctrl-D) $ cat print.txt Greetings, how are you? $
We can use openssl to compute the SHA-224 hash of it:
$ openssl sha224 -hex < print.txt (stdin)= 2bf96f28acc7c224180c36f241abb9f36a1ab95fe8f3737d312abcd4 $ We will finish a program that acts like a simple word-processor and that sends its text to openssl to compute the hashes SHA-224 and SHA-256. It then prints them for the user.
'Esc' to quit. Ctrl-P to compute hashes. Greetings, how are you? Hashes: sha224= 2bf96f28acc7c224180c36f241abb9f36a1ab95fe8f3737d312abcd4 sha256= 77a710abf0b87330026b1530fd062049fd923f0170fdc2dd786004601659218e
//--- ---// //--- Header file inclusions: ---// //--- ---// #include #include #include #include // For alarm() #include #include // For creat() #include // For creat(), wait() #include // For creat() #include // For creat() #include #include //--- ---// //--- Definitions of constants: ---// //--- ---// #define HASH_PROGRAM "/usr/bin/openssl" #define HASH_ARG "-hex" #define STOP_CHARS "*+-#&" const char* HASH_TYPE_ARRAY[] = {"sha224","sha256"}; const int NUM_HASH_TYPES = sizeof(HASH_TYPE_ARRAY)/sizeof(char*); const int HEIGHT = 5; const int WIDTH = 72; const int INIT_TEXT_LEN = 1024; const int MAX_TEXT_LEN = 65536; const char STOP_CHAR = (char)27; const char PRINT_CHAR = (char)0x10; const int BUFFER_LEN = 64 * 16; const int TYPING_WINDOW_BAR_Y = 0; const int CHECKING_WINDOW_BAR_Y = TYPING_WINDOW_BAR_Y + HEIGHT + 1; const int MESSAGE_WINDOW_BAR_Y = CHECKING_WINDOW_BAR_Y + 1; const int NUM_MESSAGE_SECS = 6; //--- ---// //--- Definitions of global vars: ---// //--- ---// WINDOW* typingWindow; WINDOW* checkingWindow; WINDOW* messageWindow; pid_t childPid; int shouldRun = 1; //--- ---// //--- Definitions of global fncs: ---// //--- ---// // PURPOSE: To turn 'ncurses' on. No parameters. No return value. void onNCurses () { // I. Application validity check: // II. Turn 'ncurses' on: initscr(); cbreak(); noecho(); nonl(); //intrflush(stdscr, FALSE); //keypad(stdscr, TRUE); typingWindow = newwin(HEIGHT,WIDTH,TYPING_WINDOW_BAR_Y+1,1); checkingWindow = newwin(HEIGHT,WIDTH,CHECKING_WINDOW_BAR_Y+1,1); messageWindow = newwin( 2,WIDTH,MESSAGE_WINDOW_BAR_Y,1); scrollok(typingWindow,TRUE); scrollok(checkingWindow,TRUE); mvaddstr(TYPING_WINDOW_BAR_Y,0,"'Esc' to quit. Ctrl-P to compute hashes."); mvaddstr(CHECKING_WINDOW_BAR_Y,0,"Hashes:"); refresh(); wrefresh(typingWindow); // moves cursor back to 'typingWindow': // III. Finished: } // PURPOSE: To handle 'SIGALRM' signals. Ignores 'sig' (which will be // 'SIGALRM'). No return value. void sigAlarmHandler (int sig ) { mvwaddstr (messageWindow,0,0, " " " " ); mvwaddstr (messageWindow,1,0, " " " " ); wrefresh(messageWindow); wrefresh(typingWindow); // moves cursor back to 'typingWindow': } // PURPOSE: To save the 'lineIndex' chars at the beginning of 'line' to // to position '*endTextPtrPtr' in buffer '*bufferPtrPtr' of length // '*bufferLenPtr' and with end '*endBufferPtrPtr'. If there is not // enough space in '*bufferPtrPtr' then '*bufferLenPtr' will be doubled, // and '*bufferPtrPtr' will be 'realloc()'-ed to this new length. // No return value. void saveLine (size_t* bufferLenPtr, char** bufferPtrPtr, char** endTextPtrPtr, char** endBufferPtrPtr, const char* line, int lineIndex ) { // I. Application validity check: // II. Save 'line' to '*bufferPtrPtr': // II.A. Allocate more space if needed: if (lineIndex >= (*endBufferPtrPtr - *endTextPtrPtr + 1) ) { size_t textLen = *endTextPtrPtr - *bufferPtrPtr; (*bufferLenPtr) *= 2; (*bufferPtrPtr) = (char*)realloc(*bufferPtrPtr,*bufferLenPtr); (*endTextPtrPtr) = *bufferPtrPtr + textLen; (*endBufferPtrPtr) = *bufferPtrPtr + *bufferLenPtr; } // II.B. Save 'line' to '*bufferPtrPtr': memcpy(*endTextPtrPtr,line,lineIndex); (*endTextPtrPtr) += lineIndex; // III. Finished: } // PURPOSE: To attempt to compute the hashes of the text pointed to by // 'bufferPtr' using the hash functions in 'HASH_TYPE_ARRAY[]'. // 'endTextPtr' points to one char beyond the end of the text to print // in 'bufferPtr'. No return value. // // SIDE EFFECT: Prints to 'messageWindow' and sets process to receive // 'SIGALRM' 'NUM_MESSAGE_SECS' seconds in the future. // This will invoke 'sigAlarmHandler()', which erases the // text in 'messageWindow'. void computeHashes (const char* bufferPtr, const char* endTextPtr ) { // I. Application validity check: // II. Compute hashes: // II.A. Each iteration computes one hash: int hashIndex; for (hashIndex = 0; hashIndex < NUM_HASH_TYPES; hashIndex++) { // II.A.1. Create pipes: int toChild[2]; int fromChild[2]; // YOUR CODE HERE to make 2 pipes // II.A.2. Do 'openssl' process work: if (/* YOUR CODE HERE to make a child */ == 0) { // YOUR CODE HERE to handle child case exit(EXIT_FAILURE); } // II.A.3. Do parent work: int numBytes; int status; char text[INIT_TEXT_LEN]; const char* msgCPtr; // YOUR CODE HERE to handle parent case mvwaddstr(messageWindow,hashIndex,0,HASH_TYPE_ARRAY[hashIndex]); waddstr(messageWindow,msgCPtr); wrefresh(messageWindow); } wrefresh(typingWindow); // moves cursor back to 'typingWindow': // YOUR CODE HERE to do one last thing // III. Finished: } // PURPOSE: To allow the user to type, display what they type in // &apos'typingWindow', and to compute the hash upon pressing 'PRINT_CHAR'. // 'vPtr' comes in, perhaps pointing to something. Returns 'NULL'. void* type (void* vPtr ) { // I. Application validity check: // II. Handle user typing: unsigned int c; char line[WIDTH+1]; int index = 0; size_t bufferLen = INIT_TEXT_LEN; char* bufferPtr = (char*)malloc(bufferLen); char* endTextPtr = bufferPtr; char* endBufferPtr = bufferPtr + bufferLen; // II.A. Each iteration handles another typed char: while ( (c = getch()) != STOP_CHAR ) { // II.A.1. Handle special chars: if (c == ' ') { // II.A.1.a. Treat carriage return like newline: c = ' '; } else if ( (c == 0x7) || (c == 127) ) { // II.A.1.b. Handle backspace: int col = getcurx(typingWindow); if (col > 0) { index--; wmove(typingWindow,getcury(typingWindow),col-1); wrefresh(typingWindow); } continue; } else if (c == PRINT_CHAR) { size_t textLen = endTextPtr - bufferPtr; saveLine(&bufferLen,&bufferPtr,&endTextPtr,&endBufferPtr,line,index); computeHashes(bufferPtr,endTextPtr); endTextPtr = bufferPtr + textLen; continue; } else if (c == ERR) { continue; } // II.A.2. Print and record the char: waddch(typingWindow,c); wrefresh(typingWindow); line[index++] = c; // II.A.3. Handle when save 'line': if (c == ' ') { // II.A.3.a. Save 'line' when user types newline: saveLine(&bufferLen,&bufferPtr,&endTextPtr,&endBufferPtr,line,index); index = 0; } else if (index == WIDTH-1) { // II.A.3.b. Save 'line' when at last column: line[index] = ' '; index++; saveLine(&bufferLen,&bufferPtr,&endTextPtr,&endBufferPtr,line,index); index = 0; waddch(typingWindow,' '); wrefresh(typingWindow); } } // III. Finished: saveLine(&bufferLen,&bufferPtr,&endTextPtr,&endBufferPtr,line,index); //int inFd = creat(TEXT_FILENAME,0640); //write(inFd,bufferPtr,endTextPtr-bufferPtr); //close(inFd); free(bufferPtr); return(NULL); } // PURPOSE: To turn off 'ncurses'. No parameters. No return value. void offNCurses () { sleep(1); nl(); echo(); refresh(); delwin(messageWindow); delwin(typingWindow); delwin(checkingWindow); endwin(); } // PURPOSE: To do the spell-checking word-processor. Ignores command line // arguments. Return 'EXIT_SUCCESS' to OS. int main () { struct sigaction act; // (2) YOUR CODE HERE to install sigAlarmHandler as the handler for SIGALRM onNCurses(); type(NULL); offNCurses(); return(EXIT_SUCCESS); }
-
- The action starts, of course, in main(). The first thing that main() should do is to install a simple signal handler:
| Signal: | Handler to run: |
| SIGALRM | sigAlarmHandler() |
After that, mainy-main: - turns the windowing on (onNCurses(), already done)
- lets the user type expressions (type() is done, but it calls computeHashes() which you must finish)
- turns the windowing off (offNCurses(), already done)
-
You must finish computeHashes(). It has a loop because it calls openssl twice, once for SHA-224 and once for SHA-256. Both iterations use pipes to send the endTextPtr-outputBufferPtr bytes pointed to by outputBufferPtr to a child process running openssl.
- (Section II.A.1.) First create two pipes. We will use one for the parent to talk to the child and the other for the child to talk to the parent.
- (Section II.A.2.) Now make a child process. If we are the child process then be sure to:
- close() unnecessary pipe file descriptors
- Redirect STDIN_FILENO to the input end of the appropriate pipe, and redirect STDOUT_FILENO to the output end of the appropriate pipe.
- Run the hash program. The execl() program must specify the string constants
- HASH_PROGRAM: The path of openssl
- HASH_TYPE_ARRAY[hashIndex]: Tells which hash to compute (SHA-224 or SHA-256)
- HASH_ARG: tells to give output in hexadecimal
Remember: - How many times do we specify the program name?
- What should the last argument to execl() be?
- Have exit(EXIT_FAILURE) after the execl() line. Why?
- (Section II.A.3.) Now handle the parent. If we are the parent process then be sure to:
- close() unnecessary pipe file descriptors
- Send the endTextPtr-bufferPtr bytes of text pointed to by bufferPtr to the child. Then close() that file descriptor.
- Get the response from the child into text and wait() for the child to end (getting its return status).
- If the child successfully finished and read()ing its response was successful then If the child did not successfully finish or read()ing the response was not successful then set msgCPtr = "Compute hash failed"
- The very last thing the function should do is alarm(NUM_MESSAGE_SECS) This tells it run sigAlarmHandler() NUM_MESSAGE_SECS seconds in the future. That function erases the hash-values.
- Add whatever variables you want (within reason).
-
Now run the bad boy! Type something and press Ctrl-P to compute the hash. Press Esc to quit.
Sample output:
'Esc' to quit. Ctrl-P to compute hashes. Greetings, how are you? Hashes: sha224= 2bf96f28acc7c224180c36f241abb9f36a1ab95fe8f3737d312abcd4 sha256= 77a710abf0b87330026b1530fd062049fd923f0170fdc2dd786004601659218e
Sequence diagram:
parent process | | | fork() | /execl() openssl +--------------------------------------->| | | | | | | | | |write("Greetings, how are you? ") | +--------------------------------------->|read() | | | | |<---------------------------------------+ write("(stdin)= 2bf96f28acc7c224180c36f241abb9f36a1ab95fe8f3737d312abcd4") | stops | | | | | fork() | /execl() openssl +--------------------------------------->| | | | | | | | | | write("Greetings, how are you? ") | +--------------------------------------->|read() | | | | |<---------------------------------------+ write("(stdin)= 77a710abf0b87330026b1530fd062049fd923f0170fdc2dd786004601659218e") | | | stops |