diff --git a/.monadid b/.monadid index 41492f066ad069d49dd2c8fc21987dc78f3aa429..33d5feb7e57d186ba5814eb41d32d4d23015e104 100644 --- a/.monadid +++ b/.monadid @@ -1,4 +1,4 @@ monad identification file This file is used for monad directory identification Built by toole1 on linux1.ews.illinois.edu -Build Date: Mon Jan 23 00:57:08 CST 2012 +Build Date: Tue Jan 24 17:14:38 CST 2012 diff --git a/Makefile b/Makefile index 73b7aa492c4b3729e4e965ee795d6d67c5375fc0..8262302865c3d4b6e170666ae369c5bb3083179c 100644 --- a/Makefile +++ b/Makefile @@ -16,11 +16,14 @@ $(warning Invalid value specified for OPTIMIZE. Should be on or off) CFLAGS += -g -O0 endif -IS_LIBRT:=$(shell locate -l 1 -c librt.so) +LIBS:= +IS_LIBRT:=$(shell echo "int main(){}" | g++ -o /dev/null -x c++ - -lrt &>/dev/null ; echo $$?) +IS_LIBPNG:=$(shell echo "int main(){}" | g++ -o /dev/null -x c++ - -lpng &>/dev/null ; echo $$?) ifeq ($(IS_LIBRT),0) -LIBRT= -else -LIBRT=-lrt +LIBS+= -lrt +endif +ifeq ($(IS_LIBPNG),0) +LIBS+= -lpng endif all: $(EXENAME) $(CCMONAD) $(IDFILE) @@ -32,10 +35,10 @@ $(IDFILE): $(OBJS) 'Build Date:' `date`> $(IDFILE) $(EXENAME): $(OBJS) $(IDFILE) - $(CC) $(CFLAGS) $(LIBRT) $(OBJS) -o $@ + $(CC) $(CFLAGS) $(LIBS) $(OBJS) -o $@ $(CCMONAD): $(CCOBJS) $(IDFILE) - $(CC) $(CFLAGS) $(LIBRT) $(CCOBJS) -o $@ + $(CC) $(CFLAGS) $(LIBS) $(CCOBJS) -o $@ help.o: help.cpp $(wildcard *.h) README.cgo README_config.cgo README_tests.cgo LICENSE.cgo quotes.cga diff --git a/ccmonad b/ccmonad index 0c9b9a7ef64a12268e50c5226fc826fbb0049e32..826a50bc4e32599060921c8261714a6901aa4858 100755 Binary files a/ccmonad and b/ccmonad differ diff --git a/monad b/monad index ff7a2666e0d03621ba47cb20ac712eff064c85c2..65f14673f74969effb85a8d44a08119c90f44949 100755 Binary files a/monad and b/monad differ diff --git a/monad.cpp b/monad.cpp index 8587fbf94d8433a7e03a8cd17f5e3c06bd99faab..26b33756c1975afef01268596101a8c775400577 100644 --- a/monad.cpp +++ b/monad.cpp @@ -12,7 +12,8 @@ void processArgs(int argc, const char * const * argv); void copyRequiredFiles(); void getLibs(const vector<string> & libs); string updateFolder(const string & folder, bool link); -string getFolder(const string & folder, const string & searchFirstDir = "", bool link = false); +string tryGetFolder(string dirAndFolder, const string & target, bool link); +string getFolder(const string & folder, const string & searchDir = "", bool link = false); void importFiles(const string & preservedFolder, const string & theSourceFolder, const string & destFolder, const vector<string> & files); void exec_command(const string & command); @@ -54,19 +55,6 @@ bool optimize = false; } -string kyleGetDate() { - string dateAndTime = "Current Date and Time: "; - - time_t currentTime; - struct tm * timeStructure; - time(¤tTime); - timeStructure = localtime(¤tTime); - dateAndTime += asctime(timeStructure); - - return dateAndTime; -} - - int main(int argc, const char * const * argv) { @@ -333,7 +321,7 @@ void monad::copyRequiredFiles() EXIT_IF_ERROR(rename(gradeFolder.c_str(), tempFolder.c_str())); } - exec("/bin/mkdir",gradeFolder.c_str()); + exec("/bin/mkdir", gradeFolder); // Copy and link appropriate files - parsed from config.ini importFiles(tempFolder, "./", gradeFolder, config["Monad Files"]); @@ -405,62 +393,65 @@ string monad::updateFolder(const string & folder, bool link) { string get = getFolder(folder, "", link); if (opts::update) - exec(/*-1,*/ "svn","up", get.c_str()); + exec("svn", "up", get /*, NULL*/); return get; } -string monad::getFolder(const string & folder, const string & searchFirstDir /* = "" */, bool link /* = false */) +string monad::tryGetFolder(string dirAndFolder, const string & target, bool link) { - // TODO (toole1): All this stuff is ugly! - string target = "./" + folder; + if (dirAndFolder[dirAndFolder.length() - 1] == '/') + dirAndFolder = dirAndFolder.substr(0, dirAndFolder.size() - 1); - // Look in the searchFirstDir folder - if (searchFirstDir != "") + if (exists(dirAndFolder + "/")) { - string searchfirst = "./" + searchFirstDir + folder; - if (exists(searchfirst + "/")) - { - if (!link) return searchfirst + "/"; - EXIT_IF_ERROR(symlink(searchfirst.c_str(), target.c_str()) != 0); - return target + "/"; - } + if (!link) return dirAndFolder + "/"; + EXIT_IF_ERROR(symlink(dirAndFolder.c_str(), target.c_str()) != 0); + return target + "/"; } - // Look in the current folder - if (exists(target + "/")) - return target + "/"; + return ""; +} - // Look in the parent folder - string source = "../" + folder; - if (exists(source + "/")) +string monad::getFolder(const string & folder, const string & searchDir /* = "" */, bool link /* = false */) +{ + // TODO (toole1): All this stuff is ugly! + string target = "./" + folder; + string result; + + // Look in the searchDir folder + if (searchDir != "") { - if (!link) return source + "/"; - EXIT_IF_ERROR(symlink(source.c_str(), target.c_str()) != 0); - return target + "/"; + result = tryGetFolder(searchDir + "/" + folder, target, link); + if (result != "") return result; + + // Try SVN + // Clear out our cache first + if (exists(target + "/")) + exec("rm", "-rf", target + "/"); + + string svndir = config["SVN Root"][0] + "/" + searchDir + "/" + folder; + + int svnstatus = exec("svn", "co", svndir /*, NULL*/); + if (svnstatus == 0) return target + "/"; + + cerr << "Error: " << folder << " not found." << endl; + exit(-1); } - // Maybe it actually *is* the parent folder - string cwd = getcwdstr(); - size_t cwd_i = findNthLast(cwd, '/', 2); - if (cwd_i != string::npos && - cwd.length() - cwd_i > folder.length() && - cwd.compare(cwd_i, folder.length(), folder) == 0) - { - if (!link) return "../"; - EXIT_IF_ERROR(symlink("../", target.c_str()) != 0); + // Look in the parent folder + result = tryGetFolder("../" + folder, target, link); + if (result != "") return result; + + // Look in the current folder + if (exists(target + "/")) return target + "/"; - } // Look two directories up and over - why not? If the parent folder is // the target source folder for the mp/lab, then the tests or libs // may be two up and over - source = "../../" + folder; - if (exists(source + "/")) - { - if (!link) return source + "/"; - EXIT_IF_ERROR(symlink(source.c_str(), target.c_str()) != 0); - return target + "/"; - } + // This also covers the case where it is our parent folder + result = tryGetFolder("../../" + folder, target, link); + if (result != "") return result; // Check Subversion @@ -481,13 +472,12 @@ string monad::getFolder(const string & folder, const string & searchFirstDir /* string svndir = config[svn_config_name][0] + "/" + config[svn_config_subdir][0] + "/" + folder; // TODO (toole1): Won't work if user needs to type password - int svnstatus = exec(/*-1,*/ "svn","co",svndir.c_str()); + int svnstatus = exec("svn", "co", svndir /*, NULL*/); if (svnstatus == 0) return target + "/"; } cerr << "Error: " << folder << " not found." << endl; exit(-1); - return ""; } diff --git a/monad_shared.cpp b/monad_shared.cpp index 98284f3bc3d5bc1954b642345fbe35d4c9dd93c3..229b5207c2be0247da0bb81c5b2ad096ff6313e0 100644 --- a/monad_shared.cpp +++ b/monad_shared.cpp @@ -15,7 +15,7 @@ namespace versioninfo { const char * official_name = "Monad Autograder"; const char * version_name = "confession"; -const Version version_num = Version(2, 1, 3, 3); +const Version version_num = Version(2, 1, 4, 0); const char * date = "18 Jan 2012"; } diff --git a/proxy.cpp b/proxy.cpp index a5c47306b6879496da5f4d076754b032a86f3dce..ab8896082fa5a6109e71419cfb0c490a1bc0038f 100644 --- a/proxy.cpp +++ b/proxy.cpp @@ -502,7 +502,7 @@ void test_execution::child_valgrind() nums_pipe.close_write(); start_timeout(); - exec("valgrind", "--dsymutil=yes", "--trace-children=yes", /*"--log-fd=-1",*/ "-q", "./proxy", test.name(), NULL); + exec("valgrind", "--dsymutil=yes", "--trace-children=yes", /*"--log-fd=-1",*/ "-q", "./proxy", test.name()); } diff --git a/util.cpp b/util.cpp index 1f17813a01ed544567d6e7971aac317e730b81dc..e730cd7df003f9d0f8c995c6643aca1c12bc42fe 100644 --- a/util.cpp +++ b/util.cpp @@ -43,6 +43,11 @@ void SET_ERROR_MESSAGE(const char * message) internal::error_prefix = message; } +namespace internal +{ +namespace exec +{ + char * allocateCstr(const string & str) { char * cstr = new char[str.size() + 1]; @@ -51,9 +56,217 @@ char * allocateCstr(const string & str) return cstr; } +pair<int, int> getFdsFromStream(const ostream * stream) +{ + int redirect_fd; + int outpipe_fd = -1; + + if (stream == NULL) + redirect_fd = -1; + else if (stream == &cout || stream == &cerr) // we don't handle cerr right now; it is just left unpiped + redirect_fd = STDOUT_FILENO; + else + { + int pipe_fds[2]; + EXIT_IF_ERROR(pipe(pipe_fds) != 0); + redirect_fd = pipe_fds[1]; + outpipe_fd = pipe_fds[0]; + } + + return make_pair(redirect_fd, outpipe_fd); +} + +struct exec_timers +{ + struct itimerval remaining_real; + struct itimerval remaining_virtual; + struct itimerval remaining_prof; + bool supports_virtual; + bool supports_prof; + exec_timers() : supports_virtual(true), supports_prof(true) { } +}; + +exec_timers get_exec_timers() +{ + exec_timers result; + + EXIT_IF_ERROR(getitimer(ITIMER_REAL, &result.remaining_real)); + + if (getitimer(ITIMER_VIRTUAL, &result.remaining_virtual) != 0) + { + if (errno == EINVAL) + { + result.supports_virtual = false; + errno = 0; + } + else + exit_if_error_output(__FILE__, __LINE__, "getitimer(ITIMER_VIRTUAL) failed"); + } + + if (getitimer(ITIMER_PROF, &result.remaining_prof) != 0) + { + if (errno == EINVAL) + { + result.supports_prof = false; + errno = 0; + } + else + exit_if_error_output(__FILE__, __LINE__, "getitimer(ITIMER_PROF) failed"); + } + + return result; +} + +void set_exec_timers(const exec_timers & timers) +{ + EXIT_IF_ERROR(setitimer(ITIMER_REAL, &timers.remaining_real, NULL)); + if (timers.supports_virtual) EXIT_IF_ERROR(setitimer(ITIMER_VIRTUAL, &timers.remaining_virtual, NULL)); + if (timers.supports_prof) EXIT_IF_ERROR(setitimer(ITIMER_PROF, &timers.remaining_prof, NULL)); +} + +void redirect_to_fd(const int redirect_fd) +{ + if (redirect_fd == -1) + { + int devnull_fd = open("/dev/null", O_WRONLY | O_NONBLOCK); + close(STDOUT_FILENO); + close(STDERR_FILENO); + dup2(devnull_fd, STDOUT_FILENO); + dup2(devnull_fd, STDERR_FILENO); + close(devnull_fd); + } + else if (redirect_fd != STDOUT_FILENO) + { + close(STDOUT_FILENO); + close(STDERR_FILENO); + dup2(redirect_fd, STDOUT_FILENO); + dup2(redirect_fd, STDERR_FILENO); + close(redirect_fd); + } +} + +char ** cstr_array_from_vector_string(const string & command, const vector<string> & args) +{ + // TODO (toole1): check this doesn't cause a memleak + char ** args_cstr = new char*[args.size() + 2]; + args_cstr[0] = allocateCstr(command); + for (size_t i = 0; i < args.size(); i++) + args_cstr[i + 1] = allocateCstr(args[i]); + args_cstr[args.size() + 1] = NULL; + return args_cstr; +} + +} // namespace exec +} // namespace internal + +// heavily refactored, but originally from: +// http://stackoverflow.com/questions/2180079/how-can-i-copy-a-file-on-unix-using-c +int8_t exec(const string & command, const vector<string> & args, ostream * output) +{ + using namespace internal; + using namespace internal::exec; + + pair<int, int> fds = getFdsFromStream(output); + int redirect_fd = fds.first; + int outpipe_fd = fds.second; + + exec_timers timers = get_exec_timers(); + + pid_t pid = fork(); + EXIT_IF_ERROR(pid < 0); + if (pid == 0) + { + set_exec_timers(timers); + redirect_to_fd(redirect_fd); + if (outpipe_fd != -1) + close(outpipe_fd); + + // Sanitize environment + char path[] = "PATH=/bin/:/usr/bin:/usr/local/bin"; + // Turn off glibc errors default write-to-terminal behaviour, because + // it does not get caught by stderr. This instead results in an abort. + char redirect_glibc[] = "LIBC_FATAL_STDERR_=1"; + char * newenv[] = { path, redirect_glibc, NULL }; + environ = newenv; + + char ** args_cstr = cstr_array_from_vector_string(command, args); + + // Swap out child process image with the command, searching + // in the specified path + execvp(command.c_str(), args_cstr); + + // An error occured + cerr << "exec(" << '\"' << command << '\"'; + for (size_t i = 0; i < args.size(); i++) + cerr << ", \"" << args[i] << "\""; + cerr << ") failed: " << strerror(errno) << endl; + exit(-1); + } + else + { + // Take the output of the pipe and put it into the *output stream + if (outpipe_fd != -1) + { + EXIT_IF_ERROR(output == NULL); + close(redirect_fd); + + vector<char> buffer(1024); + ssize_t char_read_count; + do + { + errno = 0; + cerr << "START READ" << endl; + char_read_count = ::read(outpipe_fd, &buffer[0], buffer.size() - 1); + cerr << "END READ " << char_read_count << endl; + + if (char_read_count < 0 && errno == EINTR) continue; + EXIT_IF_ERROR(char_read_count < 0); + EXIT_IF_ERROR(static_cast<size_t>(char_read_count) + 1 >= buffer.size()); + + buffer[char_read_count] = '\0'; + *output << &buffer[0]; + + } while (char_read_count > 0); + } + + int childExitStatus; + /* parent - wait for child - this has all error handling, you + * could just call wait() as long as you are only expecting to + * have one child process at a time. + */ + pid_t ws = waitpid( pid, &childExitStatus, 0); + EXIT_IF_ERROR(ws == -1); + + if (WIFEXITED(childExitStatus)) /* exit code in childExitStatus */ + { + int8_t status = WEXITSTATUS(childExitStatus); /* zero is normal exit */ + /* handle non-zero as you wish */ + return status; + } + else if (WIFSIGNALED(childExitStatus)) /* killed */ + { + // No idea why I'm doing this anymore... if it segfaults, you do too?? + kill(getpid(), WTERMSIG(childExitStatus)); + return -1; + } + else if (WIFSTOPPED(childExitStatus)) /* stopped */ + { + //cout << "exec error: " << __LINE__ << endl; + return -1; + } + else + EXIT_IF_ERROR(true); + exit(-1); + } +} + // TODO (toole1) this is a total hack, should use execvp like exec() below +// originally from: +// http://stackoverflow.com/questions/2180079/how-can-i-copy-a-file-on-unix-using-c int8_t exec(int redirect_fd, const string & command, const vector<string> & args) { + using namespace internal::exec; + // For debugging: #if 0 cerr << "exec(" << '\"' << command << '\"'; diff --git a/util.h b/util.h index 1439a0082dcbae844a635e8aa73a81ec36d565bc..9da2b7218b8280e5a7d07854bd5c095412b82a2b 100644 --- a/util.h +++ b/util.h @@ -138,7 +138,8 @@ inline int8_t exec(int redirect_fd, const string & command, const string & arg1, inline int8_t exec(int redirect_fd, const string & command, const string & arg1, const string & arg2, const string & arg3, const string & arg4, const string & arg5) { return exec(redirect_fd, command.c_str(), arg1.c_str(), arg2.c_str(), arg3.c_str(), arg4.c_str(), arg5.c_str()); } inline int8_t exec(int redirect_fd, const string & command, const string & arg1, const string & arg2, const string & arg3, const string & arg4, const string & arg5, const string & arg6) { return exec(redirect_fd, command.c_str(), arg1.c_str(), arg2.c_str(), arg3.c_str(), arg4.c_str(), arg5.c_str(), arg6.c_str()); } int8_t exec(int redirect_fd, const string & command, const vector<string> & args); -inline int8_t exec(const string & command, const vector<string> & args) { return exec(STDOUT_FILENO, command, args); } +int8_t exec(const string & command, const vector<string> & args, ostream * output); +inline int8_t exec(const string & command, const vector<string> & args) { return exec(command, args, &std::cout); } // FILESYSTEM FUNCTIONS void assertExists(const string & path, int exit_code = -1);