#define _POSIX_C_SOURCE 200809L #define _DEFAULT_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #define BUF_BLOCK_SIZE 4096 char stdinName[2 * PATH_MAX]; char stdoutName[2 * PATH_MAX]; char stderrName[2 * PATH_MAX]; char ctrlinName[2 * PATH_MAX]; char ctrloutName[2 * PATH_MAX]; bool progRunning = false; bool progQuit = false; sig_atomic_t progStatus = 0; pid_t child = 0; char * normalizePath(const char * src, char * res) { size_t src_len = strlen(src); size_t res_len; const char * ptr = src; const char * end = &src[src_len]; const char * next; if (src_len == 0 || src[0] != '/') { // relative path char pwd[PATH_MAX]; size_t pwd_len; if (getcwd(pwd, sizeof(pwd)) == NULL) { return NULL; } pwd_len = strlen(pwd); memcpy(res, pwd, pwd_len); res_len = pwd_len; } else { res_len = 0; } for (ptr = src; ptr < end; ptr=next+1) { size_t len; next = memchr(ptr, '/', end-ptr); if (next == NULL) { next = end; } len = next-ptr; switch(len) { case 2: if (ptr[0] == '.' && ptr[1] == '.') { const char * slash = memrchr(res, '/', res_len); if (slash != NULL) { res_len = slash - res; } continue; } break; case 1: if (ptr[0] == '.') { continue; } break; case 0: continue; } res[res_len++] = '/'; memcpy(&res[res_len], ptr, len); res_len += len; } if (res_len == 0) { res[res_len++] = '/'; } res[res_len] = '\0'; return res; } void cleanup(void) { remove(stdinName); remove(stdoutName); remove(stderrName); remove(ctrlinName); remove(ctrloutName); } void sigchildHandler(int sig) { (void)sig; wait(&progStatus); if(WIFEXITED(progStatus) || WIFSIGNALED(progStatus) || WTERMSIG(progStatus)) { progQuit = true; } } void exitHandler(int sig) { (void)sig; cleanup(); exit(0); } void signalHandler(int sig, void(*func)(int)) { struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = func; if(sigaction(sig, &sa, NULL) == -1) { perror("sigaction"); cleanup(); exit(1); } } void makeFifo(const char* prefix, const char* name, char* nameBuf) { strncpy(nameBuf, prefix, PATH_MAX); strncat(nameBuf, name, PATH_MAX); if(access(nameBuf, F_OK) == 0) { if(remove(nameBuf) != 0) { perror("remove"); cleanup(); exit(3); } } if(mkfifo(nameBuf, 0666) == -1) { perror("mkfifo"); cleanup(); exit(2); } } void reopenCtrlinFifo(const char* name, int* fd) { // fifo "connection" was lost (e.g. the controlling application quit). reopen it close(*fd); if((*fd = open(name, O_RDONLY)) < 0) { perror("reopen ctrlinFifo"); cleanup(); exit(6); } } int main(int argc, char *argv[]) { if(argc != 2) { fprintf(stderr, "Usage: %s FIFONAME_PREFIX\n", argv[0]); return 1; } signalHandler(SIGCHLD, sigchildHandler); signalHandler(SIGPIPE, SIG_IGN); signalHandler(SIGUSR1, SIG_IGN); signalHandler(SIGUSR2, SIG_IGN); signalHandler(SIGINT, exitHandler); signalHandler(SIGHUP, exitHandler); signalHandler(SIGQUIT, exitHandler); signalHandler(SIGTERM, exitHandler); mode_t origMask = umask(0000); char fifonamePrefix[PATH_MAX]; realpath(argv[1], fifonamePrefix); makeFifo(fifonamePrefix, "stdin", stdinName); makeFifo(fifonamePrefix, "stdout", stdoutName); makeFifo(fifonamePrefix, "stderr", stderrName); makeFifo(fifonamePrefix, "ctrlin", ctrlinName); makeFifo(fifonamePrefix, "ctrlout", ctrloutName); umask(origMask); int stdinFifo = -1, stdoutFifo = -1, stderrFifo = -1; int ctrlinFifo = open(ctrlinName, O_RDONLY); if(ctrlinFifo == -1) { perror("open"); cleanup(); return 3; } FILE* ctrloutFile = fopen(ctrloutName, "w"); if(ctrloutFile == NULL) { perror("open"); cleanup(); return 4; } setlinebuf(ctrloutFile); // avoid blocking and/or not open errors open(ctrloutName, O_RDONLY); size_t bufSize = BUF_BLOCK_SIZE; char* buf = malloc(bufSize* sizeof(char)); if(buf == NULL) { perror("malloc"); cleanup(); return 5; } for(;;) { char* newlinePos = 0; if(progQuit && progRunning) { progRunning = true; progQuit = false; fprintf(ctrloutFile, "QUIT: %d\n", WEXITSTATUS(progStatus)); close(stdinFifo); close(stdoutFifo); close(stderrFifo); } ssize_t count = read(ctrlinFifo, buf, BUF_BLOCK_SIZE - 1); if(count <= 0) { reopenCtrlinFifo(ctrlinName, &ctrlinFifo); } else { buf[count] = '\0'; do { if(strcmp("HELLO\n", buf) == 0) { fputs("HELLO\n", ctrloutFile); } else if(strcmp("STATUS\n", buf) == 0) { if(progRunning) { fprintf(ctrloutFile, "RUNNING: %d\n", child); } else { fputs("NOTRUNNING\n", ctrloutFile); } } else if(strncmp("START ", buf, 6) == 0) { if(progRunning && ctrloutFile != NULL) { fputs("ALREADY\n", ctrloutFile); } else { int curPos = 0; char* prog = buf + 6; while((newlinePos = strchr(prog, '\n')) == NULL) { bufSize += count; buf = realloc(buf, bufSize); if(buf == NULL) { perror("realloc"); cleanup(); return 7; } count = read(ctrlinFifo, buf + curPos, BUF_BLOCK_SIZE - 1); if(count == 0) { reopenCtrlinFifo(ctrlinName, &ctrlinFifo); break; } curPos += count; } *newlinePos = '\0'; char** nargv = malloc(2 * sizeof(char*)); if(nargv == NULL) { perror("malloc"); cleanup(); return 8; } nargv[0] = prog; int nargc = 1; unsigned int len = strlen(prog); for(unsigned int i = 0; i < len; ++i) { if(prog[i] == '\\' && (prog[i+1] == '\\' || prog[i+1] == ' ')) { memmove(prog + i, prog + i + 1, strlen(prog + i)); --len; } else if(prog[i] == ' ') { prog[i] = '\0'; nargv = realloc(nargv, (++nargc + 1) * sizeof(char*)); if(nargv == NULL) { perror("realloc"); cleanup(); return 9; } nargv[nargc - 1] = prog + i + 1; } } nargv[nargc] = NULL; char absPath[PATH_MAX]; nargv[0] = normalizePath(nargv[0], absPath); if(nargv[0] == NULL || access(nargv[0], X_OK) != 0) { fprintf(ctrloutFile, "STARTFAIL: %s\n", strerror(errno)); } else { progRunning = true; child = fork(); if(child == -1) { perror("fork"); cleanup(); return 10; } if(child == 0) { stdinFifo = open(stdinName, O_RDONLY); if(stdinFifo == -1) { perror("open"); } stdoutFifo = open(stdoutName, O_WRONLY); if(stdoutFifo == -1) { perror("open"); } stderrFifo = open(stderrName, O_WRONLY); if(stderrFifo == -1) { perror("open"); } dup2(stdinFifo, STDIN_FILENO); dup2(stdoutFifo, STDOUT_FILENO); dup2(stderrFifo, STDERR_FILENO); if(execvp(nargv[0], nargv) == -1) { perror("execvp"); exit(11); } } else { stdinFifo = open(stdinName, O_WRONLY); stdoutFifo = open(stdoutName, O_RDONLY); stderrFifo = open(stderrName, O_RDONLY); fprintf(ctrloutFile, "RUNNING: %d\n", child); } } bufSize = BUF_BLOCK_SIZE; buf = realloc(buf, bufSize); if(buf == NULL) { perror("realloc"); cleanup(); return 12; } buf[0] = '\0'; } } else if(strncmp(buf, "CD ", 3) == 0) { char* dir = buf + 3; if((newlinePos = strchr(dir, '\n')) != 0) { *newlinePos = '\0'; } char tempDir[PATH_MAX]; realpath(dir, tempDir); if(chdir(tempDir) == 0) { fputs("CHANGED\n", ctrloutFile); } else { fprintf(ctrloutFile, "CD FAILED: %s\n", strerror(errno)); } } else if(strcmp(buf, "EXIT\n") == 0) { cleanup(); return 0; } else if(strcmp(buf, "CLOSE\n") == 0) { close(stdinFifo); close(stdoutFifo); close(stderrFifo); fputs("CLOSED\n", ctrloutFile); } else if(strcmp(buf, "KILL\n") == 0) { if(!progRunning) { fputs("NOTRUNNING\n", ctrloutFile); } else { kill(child, SIGTERM); fputs("KILLED\n", ctrloutFile); } } if(newlinePos != 0) { memmove(buf, newlinePos + 1, strlen(newlinePos + 1)); } else { buf[0] = '\0'; } } while(buf[0] != '\0'); } } }