/* * @(#)Process.cpp * * This file is part of webCDwriter - Network CD/DVD Writing. * * Copyright (C) 1999-2006 Jörg P. M. Haeger * * webCDwriter is free software. See CDWserver.cpp for details. * * Jörg Haeger, 02.11.1999 */ #include #include #include #include #include #include #include #include #include "Config.h" #include "Support.h" #include "LineReader.h" #include "Log.h" #include "Process.h" Process::Process(const char *fileName) { init(fileName); } Process::~Process() { if (pid > 0 && result < 0) { kill(pid, SIGKILL); getResult(); } log.debug("Process", "~Process() %s %d result = %d", args[0], pid, result); if (protoStream != NULL && protoStream != stdout) fclose(protoStream); if (inStream != NULL) delete inStream; for (int i = 0; i < envsNum; i++) delete[] envs[i]; for (int i = 0; i < argsNum; i++) delete[] args[i]; closePipes(); } void Process::addArg(const char *format, ...) { if (argsNum == 256 - 1) throw new Exception("Process::addArg: to many args"); char str[1024]; va_list ap; va_start(ap, format); vsnprintf(str, sizeof str, format, ap); va_end(ap); args[argsNum++] = strDup(str); args[argsNum] = NULL; } void Process::addArg(String &str) { try { addArg("%s", str.getBytes()); } catch (Exception *e) { String::free(&str); throw e; } String::free(&str); } void Process::addArgs(const char *args) { char str[strlen(args) + 1]; for (int i = 0; ; i++, args++) { str[i] = *args; if (str[i] == 0 || str[i] == ' ' || str[i] == '\t') { str[i] = 0; if (i > 0) addArg(str); i = -1; } if (*args == 0) break; } } void Process::closePipes() { for (int i = 0; i < 2; i++) if (outPipe[i] >= 0) { close(outPipe[i]); outPipe[i] = -1; } for (int i = 0; i < 2; i++) if (inPipe[i] >= 0) { close(inPipe[i]); inPipe[i] = -1; } for (int i = 0; i < 2; i++) if (errPipe[i] >= 0) { close(errPipe[i]); errPipe[i] = -1; } } void Process::closePipeToChild() { close(outPipe[STDOUT_FILENO]); outPipe[STDOUT_FILENO] = -1; } int Process::getErrorStream() { return errPipe[STDIN_FILENO]; } int Process::getInputStream() { return inPipe[STDIN_FILENO]; } int Process::getOutputStream() { return outPipe[STDOUT_FILENO]; } int Process::getResult() { log.debug("Process", "getResult() %d %d", pid, result); if (pid <= 0 || result >= 0) return result; if (inStream != NULL) delete inStream; inStream = NULL; int res, status; // TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); while ((res = waitpid(pid, &status, 0)) == -1 && errno == EINTR); if (res == -1) result = 256; else if (WIFEXITED(status)) result = WEXITSTATUS(status); else if (WIFSIGNALED(status)) result = 1000 + WTERMSIG(status); else result = 257; return result; } void Process::init(const char *fileName) { argsNum = envsNum = 0; args[argsNum++] = strDup(fileName); args[argsNum] = NULL; envs[envsNum] = NULL; pid = -1; result = -1; outPipe[0] = outPipe[1] = -1; inPipe[0] = inPipe[1] = -1; errPipe[0] = errPipe[1] = -1; inStream = NULL; protoStream = NULL; } int Process::isLineAvailable() { return inStream != NULL && inStream->available(); } int Process::read(char *buf, int count) { fcntl(inPipe[STDIN_FILENO], F_SETFL, O_NONBLOCK); return ::read(inPipe[STDIN_FILENO], buf, count); } /** * A log filter for readcd. */ inline bool filter(const char *line) { int addr; return sscanf(line, "addr: %d", &addr) == 1 && addr % 5120 != 0; } const char *Process::readLine(int timeout) { const char *lineStr = inStream->next(timeout); if (lineStr != NULL && !filter(lineStr)) if (protoStream != NULL) if (protoStream != stdout) { fprintf(protoStream, "%s\n", lineStr); fflush(protoStream); } else if (!filter(lineStr)) log.put(5, S.e + "S" + (pid < 10000? "0": "") + (pid < 1000? "0": "") + (pid < 100? "0": "") + (pid < 10? "0": "") + pid + " " + lineStr); return lineStr; } int Process::run() { start(); while (1) { const char *lineStr = readLine(); if (lineStr == NULL) break; } return getResult(); } void Process::select() { fd_set fdSet; FD_ZERO(&fdSet); if (inPipe[STDIN_FILENO] >= 0) FD_SET(inPipe[STDIN_FILENO], &fdSet); fd_set fdWriteSet; FD_ZERO(&fdWriteSet); if (outPipe[STDOUT_FILENO] >= 0) FD_SET(outPipe[STDOUT_FILENO], &fdWriteSet); ::select(FD_SETSIZE, &fdSet, &fdWriteSet, NULL, NULL); } void Process::sendTerminateSignal() { if (pid > 0 && result < 0) { log.put(5, S.e + "kill " + pid); int res = kill(pid, SIGTERM); log.put(5, S.e + "kill " + pid + " = " + res); if (res != 0) { log.put(5, S.e + "kill " + errno + " " + strerror(errno)); String name = "/tmp/CDWcancel-"; name = name + pid; FILE *out = fopen(name.getBytes(), "w"); if (out != NULL) fclose(out); } } } void Process::setInputStream(int fd) { outPipe[STDIN_FILENO] = fd; } void Process::setProtoFile(const char *fileName) { if (fileName != NULL && config.getLogLevel() > 9) { makeDirs(fileName); protoStream = fopen(fileName, "w"); if (protoStream == NULL) protoStream = stdout; } else protoStream = stdout; } void Process::show() { log.put(4, S.e + "Process: \"" + args[0] + "\""); for (int i = 1; i < argsNum; i++) log.put(4, S.e + " " + (i < 10? " ": "") + i + ": \"" + args[i] + "\""); } /** *@see glibc-2.1.1/sysdeps/posix/pipestream.c */ void Process::start(const char *workDirName, int splitOut) { if (outPipe[STDIN_FILENO] < 0) if (pipe(outPipe) != 0) throw new Exception("outPipe failed"); if (pipe(inPipe) != 0) throw new Exception("inPipe failed"); if (splitOut) if (pipe(errPipe) != 0) throw new Exception("errPipe failed"); bool debugProcessStart = config.getDebug().indexOf("Process::start") >= 0; char logDir[1024]; strncpy(logDir, config.getLogDir(), sizeof logDir - 1); logDir[sizeof logDir - 1] = 0; pid = fork(); if (pid == (pid_t)-1) throw new Exception("fork failed"); if (pid == (pid_t)0) { // We are the child side. FILE *logFile = NULL; if (debugProcessStart) { char logFileName[1024]; sprintf(logFileName, "%s/childs/%d", logDir, getpid()); makeDirs(logFileName); logFile = fopen(logFileName, "w"); if (logFile != NULL) { fprintf(logFile, "1\n"); fflush(logFile); } } dup2(outPipe[STDIN_FILENO], STDIN_FILENO); dup2(inPipe[STDOUT_FILENO], STDOUT_FILENO); if (splitOut) dup2(errPipe[STDOUT_FILENO], STDERR_FILENO); else dup2(inPipe[STDOUT_FILENO], STDERR_FILENO); if (logFile != NULL) { fprintf(logFile, "2\n"); fflush(logFile); } closePipes(); if (logFile != NULL) { fprintf(logFile, "3\n"); fflush(logFile); } if (workDirName != NULL) if (chdir(workDirName) != 0) exit(129); if (logFile != NULL) { fprintf(logFile, "4\n"); fflush(logFile); } execvp(args[0], args); if (logFile != NULL) { fprintf(logFile, "5\n"); fclose(logFile); } exit(128); } log.debug("Process", "start() %s %d", args[0], pid); close(outPipe[STDIN_FILENO]); outPipe[STDIN_FILENO] = -1; close(inPipe[STDOUT_FILENO]); inPipe[STDOUT_FILENO] = -1; if (errPipe[STDOUT_FILENO] >= 0) { close(errPipe[STDOUT_FILENO]); errPipe[STDOUT_FILENO] = -1; } if (splitOut) { inStream = new LineReader(errPipe[STDIN_FILENO]); // errPipe[STDIN_FILENO] = -1; } else { inStream = new LineReader(inPipe[STDIN_FILENO]); // inPipe[STDIN_FILENO] = -1; } if (inStream == NULL) throw new Exception("open inStream failed"); } void Process::terminate() { if (pid > 0 && result < 0) { kill(pid, SIGTERM); getResult(); } } const char *Process::toString(char *str) { sprintf(str, "Process: <%s", args[0]); for (int i = 1; i < argsNum; i++) { strcat(str, " "); strcat(str, args[i]); } strcat(str, ">"); return str; } int Process::write(const char *buf, int count) { fcntl(outPipe[STDOUT_FILENO], F_SETFL, O_NONBLOCK); return ::write(outPipe[STDOUT_FILENO], buf, count); }