// ============================================================== // Name : Good Student // Email : ?@ou.edu // ID # : 123-456-7890 // Class : CS 3113 // Instructor : Dr. Dean Hougen // T/A : Eric Diamond // Project : #2 (fork, exec, and wait) // Date : 10/08/2001 // File Name : PicShare.c // Due : 10/16/2001 (Tuesday) // ============================================================== // Description: // // This is a picture sharing application, that utilizes other // existing programs to do most of the actual work. These helper // applications are created on new processes by using the fork // and exec statements. The main program then uses the wait // statement to detect when the child process has finished, and // it can continue on. // ============================================================== #include #include #include #include #include #include #include #include #include #include "string.h" // Method Prototypes void create(); void edit(); void place(); void view(); void quit(); void trimInput(char *oldString); int fileExists(const char *name); void copyString(char *newString, const char *oldString); int file_copy(char *oldname, char *newname); void showDirectory(char *directory); char* getEditor(); void printViewers(); int updateViewers(); void removeViewer(); void addViewer(); void closeViewers(); // Constants int MAX_INPUT_SIZE; int MAX_PATH_SIZE; int MAX_PICS; // Global variables // User input char *input; // User home directory char *homeDir; // Directory separator char slash[2]; // Temp space for path building and string utilities char *temp; char *temp1; char *temp2; char *temp3; char nullString[2]; // Currently active viewers int *viewers; /** * Method name: main(...) * Method description: Main control loop of the program. * Date Created: 10/08/2001 - Good Student */ int main(int argc,char *argv[], char *envp[]) { int intVal; int x,y; // Set constants MAX_INPUT_SIZE = 100; MAX_PATH_SIZE = 100; // Allocate memory for globals input = malloc(MAX_INPUT_SIZE); // Allocate memory for string handlers temp = malloc(MAX_PATH_SIZE); temp1 = malloc(MAX_PATH_SIZE); temp2 = malloc(MAX_PATH_SIZE); nullString[0] = '\0'; // File separator character. slash[0] = '/'; slash[1] = '\0'; // Get the user's home directory homeDir = getenv("HOME"); // Make sure the string contains something, to eliminate // handling special cases later on. if (homeDir == NULL) { homeDir = ""; } // Get the maximum number of pictures // that may be displayed at one time temp3 = getenv("MAX_PICS"); if (temp3 == NULL) { // No MAX_PICS environment variable set. // Use the default value MAX_PICS = 10; } else { // MAX_PICS is set. Get the value intVal = atoi(temp3); // Test to see if we have a valid number if (intVal < 1) { // Invalid value for MAX_PICS printf("Invalid MAX_PICS value detected!\n"); // Use the default value MAX_PICS = 10; } else { MAX_PICS = intVal; } } // Allocate array space to hold the pids of child processes // being used to view pictures. if ( ( viewers = (int *)malloc(MAX_PICS*sizeof(int)) ) == NULL ) { printf("Error allocating memory.\n"); printf("Program Halted.\n"); exit(99); } // Fill up the array with 0. This value indicates an // available "slot" for another picture viewer process for (x=0; x 0) { // This code is executed by the parent process // wait here until the child process has finished waitpid(childpid, &status, 0); // That's it! We're done. Return to the main menu. return; } } /** * Method name: edit() * Method description: Edit an existing picture. This * method calls the picture editing program * specified by the environment variable * PIC_EDIT (or gimp, if none is set) with * an existing picture specified by the user. * Date Created: 10/10/2001 - Good Student */ void edit() { // user-specified file name char fileName[MAX_PATH_SIZE]; // user picture directory char *picDirectory; char path[MAX_PATH_SIZE]; // pid of the child process int childpid; // picture editor to use char *editor; // return code of child process int status; // error code of child process int code; // Get the user's picture directory picDirectory = getenv("PIC_DIR"); // If it doesn't exist, use the default directory if (picDirectory == NULL) { copyString(temp1, homeDir); strcat(temp1, "/PicDir"); picDirectory = malloc(MAX_PATH_SIZE); copyString(picDirectory,temp1); } do { // Prompt user for the picture name printf("Picture name : "); // Get the user input fgets(input, MAX_INPUT_SIZE, stdin); // Remove the trailing ASCII 10 (Line Feed) character trimInput(input); copyString(fileName, input); // Create the full path for this file copyString(temp1, nullString); strcat(temp1,picDirectory); strcat(temp1, slash); strcat(temp1, fileName); copyString(path, temp1); // Return to main menu if user just hit enter if (strlen(fileName)<1) return; // Determine if this file exists if (!fileExists(path)) { // The file does not exist printf("%s does not exist.\n\n",path); // show a listing of the user's picture directory showDirectory(picDirectory); } } while (!fileExists(path)); // We have a valid file selected. // Fork off a child process to edit the picture. childpid = fork(); if (childpid < 0) { // Error in fork printf("Error spawning child process!\n"); printf("Unable to edit picture.\n"); return; } if (childpid == 0) { // This code is executed by the child process editor = getEditor(); code = execlp(editor, editor, path, NULL); // If we got to this point, some problem happened with execl printf("Error executing picture editor!\n"); printf("Error code : %d\n",code); return; } if (childpid > 0) { // This code is executed by the parent process // wait here until the child process has finished waitpid(childpid, &status, 0); // That's it! We're done. Return to the main menu. return; } } /** * Method name: place() * Method description: Place picture for viewing. This method * copies a picture specified by the user to * the /tmp directory on the machine on which * the program is running. It changes the name * of the copy, and sets permissions on the new * file so that anyone can access it. * Date Created: 10/10/2001 - Good Student */ void place() { // user-specified file name char fileName[MAX_PATH_SIZE]; // user picture directory char *picDirectory; char path[MAX_PATH_SIZE]; // pid of the child process int childpid; // picture editor to use char *editor; // return code int status; // error code int code; // path to the file on /tmp for viewing char targetFile[MAX_PATH_SIZE]; // login name of this user char *logName; // Get the user's picture directory picDirectory = getenv("PIC_DIR"); // If it doesn't exist, use the default directory if (picDirectory == NULL) { copyString(temp1, nullString); strcat(temp1,homeDir); strcat(temp1,"/PicDir"); picDirectory = malloc(MAX_PATH_SIZE); copyString(picDirectory, temp1); } do { // Prompt user for the picture name printf("Picture to place : "); // Get the user input fgets(input, MAX_INPUT_SIZE, stdin); // Remove the trailing ASCII 10 (Line Feed) character trimInput(input); copyString(fileName, input); // Create the full path for this file copyString(temp1, nullString); strcat(temp1,picDirectory); strcat(temp1,slash); strcat(temp1,fileName); copyString(path, temp1); // Return to main menu if user just hit enter if (strlen(fileName)<1) return; // Determine if this file exists if (!fileExists(path)) { // The file does not exist printf("%s does not exist.\n\n",path); // show a listing of the user's picture directory showDirectory(picDirectory); } } while (!fileExists(path)); // We have a valid file selected. // Copy the file to the temporary directory logName = getenv("LOGNAME"); if (logName == NULL) { // The LOGNAME environment variable doesn't exist. // place a value to avoid dealing with special // cases later on. logName = "default"; } // Build the path to the temporary file. copyString(temp1, nullString); strcat(temp1,"/tmp/"); strcat(temp1,logName); strcat(temp1,"-picshare.jpg"); copyString(targetFile, temp1); status = file_copy(path, targetFile); if (status) { // some error copying the file printf("Error copying file! Picture not placed for viewing.\n"); printf("Error code %d\n",status); } else { // picture copied o.k. printf("Picture copied to : %s\n",targetFile); // Now set rights so others can view, but not modify the file. umask(0000); chmod(targetFile, 0664); } } /** * Method name: view() * Method description: View pictures in the viewing area. this method * invokes xv to show the picture specified by the * user. The maximum number of pictures that may * be displayed at any single time is determined * from the MAX_PICS environment variable. * Date Created: 10/10/2001 - Good Student */ void view() { char fileName[MAX_PATH_SIZE]; char path[MAX_PATH_SIZE]; int kids; int childpid; int code; // Update the viewer child process list // Check to make sure we should open a new viewer. kids = updateViewers(); if (kids >= MAX_PICS) { // The user has the maximum number of pictures open // already. Show a message and return. printf("Maximum number of pictures (%d) already open\n",MAX_PICS); printf("Close a picture viewer then try again.\n"); return; } do { // Show a listing of the /tmp directory showDirectory("/tmp"); // Prompt user for the picture name printf("Picture to view : "); // Get the user input fgets(input, MAX_INPUT_SIZE, stdin); // Remove the trailing ASCII 10 (Line Feed) character trimInput(input); copyString(fileName, input); // Create the full path for this file copyString(temp1, nullString); strcat(temp1,"/tmp/"); strcat(temp1,fileName); copyString(path, temp1); // Return to main menu if user just hit enter if (strlen(fileName)<1) return; // Determine if this file exists if (!fileExists(path)) { // The file does not exist printf("%s does not exist.\n\n",path); } } while (!fileExists(path)); // We have a valid file. // Spawn a new child process to view this picture childpid = fork(); if (childpid < 0) { // Error in fork printf("Error spawning child process!\n"); printf("Unable to view picture.\n"); return; } if (childpid == 0) { // This code is executed by the child process code = execlp("xv", "xv", path, NULL); // If we got to this point, some problem happened with execl printf("Error executing picture editor!\n"); printf("Error code : %d\n",code); return; } if (childpid > 0) { // This code is executed by the parent process // Update our list of child processes addViewer(childpid); // That's it! We're done. Return to the main menu. return; } } /** * Method name: quit() * Method description: Quit program. This method tidies up the * system environment by shutting down all * instances of xv that are currently displaying * pictures. It then exits the program. * Date Created: 10/10/2001 - Good Student */ void quit() { // First, terminate any viewers that we have open. closeViewers(); // We now return you to your regularly scheduled operating system exit(0); } /** * Method name: trimInput() * Method description: This method truncates the trailing * ASCII 10 (line feed) character from * a character array. This allows user * input to be tested without including * the "\n" every time. * Date Created: 10/10/2001 - Good Student */ void trimInput(char *oldString) { int x,y; int size; char newString[100]; // Make sure there is data to work with if (oldString == NULL) return; if (strlen(oldString) < 1) return; size = strlen(oldString) + 1; // Copy the string contents y = 0; for (x=0; xd_name); } closedir(dirStructP); printf("\n"); } else { printf("Unable to open directory: %s\n", directory); } } /** * Method name: getEditor() * Method description: Returns the name of the picture editor to use. * If the environment variable PIC_EDIT exists, * that value is returned. Otherwise "gimp" is * returned. * Date Created: 10/13/2001 - Good Student */ char* getEditor() { char* editor; // Get the user specified picture editor editor = getenv("PIC_EDIT"); // If it doesn't exist, use the default editor if (editor == NULL) { editor = malloc(MAX_PATH_SIZE); copyString(editor, nullString); strcat(editor, "gimp"); } return editor; } /** * Method name: printViewers() * Method description: For debugging only. This method prints out * a count of the picture viewer processes, and * the pid of each one. * Date Created: 10/13/2001 - Good Student */ void printViewers() { int x; int count; count = 0; printf("\n"); for (x=0; x 0) { count = count + 1; printf("pid : %d\n",viewers[x]); } } printf("===============\n"); printf("count : %d\n",count); printf("\n"); } /** * Method name: updateViewers() * Method description: Updates the list of picture viewer child * processes by polling the system to see if * any have terminated since the last call of * this method. The number of currently active * viewer processes is returned. * Date Created: 10/13/2001 - Good Student */ int updateViewers() { int status; int x; int childpid; int count; childpid = waitpid(-1, &status, WNOHANG); // Loop until every terminated child process has been handled. while (childpid > 0) { // Remove the terminated child process from the list removeViewer(childpid); childpid = childpid = waitpid(-1, &status, WNOHANG); } // Now build a count of the active viewers to return count = 0; for (x=0; x 0) { // This is an active process count = count + 1; } } return count; } /** * Method name: removeViewer() * Method description: Removes a specified childpid from the list * of currently active viewer child processes. * Date Created: 10/13/2001 - Good Student */ void removeViewer(int pid) { int x; for (x=0; x 0) { // Open child process found status = kill(viewers[x],SIGKILL); if (status) { // Couldn't term the child process for some reason. printf("Error terminating viewer! code = %d\n",status); } } } }