Question
Need help with a Distributed Systems problem in C please! Will upvote Purpose: To go over multiprocessing on a single machine: pipes/socket pairs fork()/execl() Overview:
Need help with a Distributed Systems problem in C please! Will upvote
Purpose:
To go over multiprocessing on a single machine:
- pipes/socket pairs
- fork()/execl()
Overview:
bc is a command line calculator program. We will give it a simple text window interface.
Computing
Please ssh into one of the following: or use your own Unix machine.
- 140.192.36.185
- 140.192.36.186
- 140.192.36.187
-
Please copy-and-paste the following files (0 Points):
interactiveCalc.c
/*-------------------------------------------------------------------------* *--- ---* *--- interactiveCalc.c ---* *--- ---* *--- An interactive calculator. ---* *--- ---* *--- ---- ---- ---- ---- ---- ---- ---- ---- ---* *--- ---* *--- Version 1a 2020 January 8 Joseph Phillips ---* *--- ---* *-------------------------------------------------------------------------*/ // // Compile with: // $ g++ interactiveCalc.c -o interactiveCalc -lncurses -g // // NOTE: // If this program crashes then you may not see what you type. // If that happens, just type: // // stty sane // // to your terminal, even though you cannot even see it. //--- ---// //--- Header file inclusions: ---// //--- ---// #include
#include #include #include // For alarm() #include #include #include // For creat() #include // For creat(), wait() #include // For creat() #include // For creat() #include #include //--- ---// //--- Definitions of constants: ---// //--- ---// #define DEFAULT_CALC_PROGRAM "/usr/bin/bc" #define CALC_ARG1 "-l" #define BC_QUIT_CMD "quit" const int HEIGHT = 5; const int WIDTH = 64; const int INIT_TEXT_LEN = 1024; const int MAX_TEXT_LEN = 65536; const char STOP_CHAR = (char)27; const char CALC_CHAR = (char)' '; const int BUFFER_LEN = 4096; 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 + HEIGHT + 1; const int NUM_MESSAGE_SECS = 4; //--- ---// //--- Definitions of global vars: ---// //--- ---// WINDOW* typingWindow; WINDOW* answerWindow; WINDOW* messageWindow; int shouldRun = 1; int hasChildStopped = 0; // YOUR CODE HERE: add some global variable(s) //--- ---// //--- Definitions of global fncs: ---// //--- ---// // PURPOSE: To turn 'ncurses' on. No parameters. No return value. void onNCurses () { // I. Application validity check: // II. Turn 'ncurses' on: const int LINE_LEN = 80; char line[LINE_LEN]; initscr(); cbreak(); noecho(); nonl(); //intrflush(stdscr, FALSE); //keypad(stdscr, TRUE); typingWindow = newwin(HEIGHT,WIDTH,TYPING_WINDOW_BAR_Y+1,1); answerWindow = newwin(HEIGHT,WIDTH,CHECKING_WINDOW_BAR_Y+1,1); messageWindow = newwin( 1,WIDTH,MESSAGE_WINDOW_BAR_Y,1); scrollok(typingWindow,TRUE); scrollok(answerWindow,TRUE); snprintf(line,LINE_LEN,"'Esc' to quit. Enter to calculate"); mvaddstr(TYPING_WINDOW_BAR_Y,0,line); mvaddstr(CHECKING_WINDOW_BAR_Y,0,"Answers:"); refresh(); wrefresh(typingWindow); // moves cursor back to 'typingWindow': // III. Finished: } // PURPOSE: To handle the 'SIGCHLD' signal. 'sig' will be 'SIGCHLD' and // will be ignored. No return value. void sigChildHandler (int sig ) { int status; wait(&status); hasChildStopped = 1; } // PURPOSE: To handle the 'SIGALRM' signal. 'sig' will be 'SIGALRM' and // will be ignored. No return value. void sigAlarmHandler (int sig ) { // Do not worry, literally does nothing } // PURPOSE: To launch the 'bc' calculator. If 'argc' is at least 2 then // 'argv[1]' will be run. If 'argc' is less then 2 then // 'DEFAULT_CALC_PROGRAM' will be run. No return value. void launchBc (int argc, char* argv[] ) { // I. Application validity check: // II. Attempt to launch 'bc' // YOUR CODE HERE (see (3)) // III. Finished: } // 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 send the text pointed to by 'bufferPtr' to the // bc-running child process. 'endTextPtr' points to one char beyond // the end of the text to send in 'bufferPtr'. No return value. // // SIDE EFFECT: Prints to 'answerWindow' void calculate (const char* outputBufferPtr, const char* endTextPtr ) { // I. Application validity check: // II. Send 'outputBufferPtr' to calculator: int numBytes; char inputBuffer[BUFFER_LEN]; // See (4) // YOUR CODE HERE TO SEND endTextPtr-outputBufferPtr BYTES POINTED TO BY outputBufferPtr alarm(1); numBytes = 0; // CHANGE THAT 0. // YOUR CODE HERE TO RECEIVE RESPONSE INTO inputBuffer[] alarm(0); if (numBytes > 0) { if ( (endTextPtr[-1] == ' ') || (endTextPtr[-1] == ' ') ) { endTextPtr--; } waddnstr(answerWindow,outputBufferPtr,endTextPtr-outputBufferPtr); waddstr (answerWindow," = "); inputBuffer[numBytes] = '\0'; waddstr(answerWindow,inputBuffer); wrefresh(answerWindow); wrefresh(typingWindow); // moves cursor back to 'typingWindow': } // III. Finished: } // PURPOSE: To allow the user to type, display what they type in // 'typingWindow', and to send what they type to the spell checking // process upon pressing 'Enter'. '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 == 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: size_t textLen = endTextPtr - bufferPtr; saveLine(&bufferLen,&bufferPtr,&endTextPtr,&endBufferPtr,line,index); index = 0; calculate(bufferPtr,endTextPtr); endTextPtr = bufferPtr + textLen; continue; } 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); free(bufferPtr); // YOUR CODE HERE TO TELL THE bc PROCESS TO END ITSELF, see (5) return(NULL); } // PURPOSE: To turn off 'ncurses'. No parameters. No return value. void offNCurses () { sleep(1); nl(); echo(); refresh(); delwin(messageWindow); delwin(typingWindow); delwin(answerWindow); endwin(); } // PURPOSE: To do the spell-checking word-processor. Ignores command line // arguments. Return 'EXIT_SUCCESS' to OS. int main (int argc, char* argv[] ) { // YOUR CODE HERE (see (2)) onNCurses(); launchBc(argc,argv); type(NULL); offNCurses(); while (!hasChildStopped) { sleep(1); } return(EXIT_SUCCESS); } - The action starts, of course, in main(). The first thing that main() should do is to install two simple signal handlers:
Signal: Handler to run: SIGCHLD sigChildHandler() SIGALRM sigAlarmHandler() - turns the windowing on (onNCurses(), already done)
- starts bc (launchBc(), you must finish)
- lets the user type expressions (type() is done, but it calls calculcate() which you must finish)
- turns the windowing off (offNCurses(), already done)
- hangs out until bc process has finished (already done)
- launchBc() takes the same argc and argv arguments as mainy-main because the user can give the path to bc on the command line. launchBc() should make the pipes or socket pairs for a child process to talk to its mama process. Then it should make the child process.
The child process should close() unnecessary file descriptors and redirect both STDIN_FILENO and STDOUT_FILENO to use the pipes/socket pairs. It should then, of course, try to run argv[1] if there is an argument on the command line, or DEFAULT_CALC_PROGRAM if there is none. Either way, it should give CALC_ARG1 as the sole command line argument.
The mama process should also close() unnecessary file descriptors, but it should save necessary file descriptor(s) in global variables. They will be used by calculate().
-
You must finish calculate(), though. Send the endTextPtr-outputBufferPtr bytes pointed to by outputBufferPtr to the bc process via your file descriptor.
Then do:
- alarm(1)
- Get the response from the bc process, putting its bytes in inputBuffer[] and setting numBytes equal to the number of bytes received.
- alarm(0)
Keep the other code in calculate(). It displays the answer.
- Most of the work is done by type(), which gets what the user types and saves it into ever expanding char buffer bufferPtr. (Already done.) However, just before the return() at the end you must send BC_QUIT_CMD to the bc process. This politely tells the bc process to end itself, without getting all rude with kill -9 or anything that extreme.
- Add whatever variables you want (within reason).
-
Now run the bad boy! Type an expression, and press Enter. Press Esc to quit.
Sample output:
'Esc' to quit. Enter to calculate s(3.141592) s(3.141292/4) j=3 j^j Answers: 5+6 = 11 s(3.141592) = .00000065358979323841 s(3.141292/4) = .70705363064115213028 j^j = 27
Sequence diagram:
parent process | | | fork() | /execl() bc +--------------->| | | | | | | | | | write("2+2") | +--------------->|read() | | | | |<---------------+ write("4") | | | | | | | | | SIGCHLD | |<---------------+ | stops | ends
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started