diff options
Diffstat (limited to 'nxcomp/Pipe.cpp')
-rw-r--r-- | nxcomp/Pipe.cpp | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/nxcomp/Pipe.cpp b/nxcomp/Pipe.cpp new file mode 100644 index 000000000..7238d0c73 --- /dev/null +++ b/nxcomp/Pipe.cpp @@ -0,0 +1,412 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <pwd.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include "Pipe.h" +#include "Misc.h" +#include "Fork.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +extern void RegisterChild(int child); + +static int Psplit(const char *command, char *parameters[], int limit); + +// +// These are slightly modified versions of popen(3) and pclose(3) +// that don't rely on a shell to be available on the system, so +// that they can also work on Windows. As an additional benefit, +// these functions give up all privileges before running the com- +// mand. Code is taken from the X distribution and, in turn, is +// based on libc from FreeBSD 2.2. +// + +static struct pid +{ + struct pid *next; + FILE *fp; + int self; + +} *pidlist; + +// +// A very unsofisticated attempt to parse the command line and +// split each parameter in distinct strings. This is not going +// to work when dealing with parameters containing spaces, even +// if they are enclosed in quotes. +// + +int Psplit(const char *command, char *parameters[], int limit) +{ + char *line; + char *value; + + int number; + + // + // Preapare the list of parameters. + // + + for (number = 0; number < limit; number++) + { + parameters[number] = NULL; + } + + // + // Copy the command to get rid of the + // const qualifier. + // + + line = new char[strlen(command) + 1]; + + if (line == NULL) + { + goto PsplitError; + } + + strcpy(line, command); + + number = 0; + + value = strtok(line, " "); + + while (value != NULL && number < limit) + { + #ifdef DEBUG + *logofs << "Psplit: Got parameter '" << value + << "'.\n" << logofs_flush; + #endif + + parameters[number] = new char[strlen(value) + 1]; + + if (parameters[number] == NULL) + { + goto PsplitError; + } + + strcpy(parameters[number], value); + + number++; + + // + // If this is the first parameter, then + // copy it in the second position and + // use it as the name of the command. + // + + if (number == 1) + { + parameters[number] = new char[strlen(value) + 1]; + + if (parameters[number] == NULL) + { + goto PsplitError; + } + + strcpy(parameters[number], value); + + number++; + } + + value = strtok(NULL, " "); + } + + // + // Needs at least to have the command itself and + // the first argument, being again the name of + // the command. + // + + if (number < 2) + { + goto PsplitError; + } + + return number; + +PsplitError: + + #ifdef PANIC + *logofs << "Psplit: PANIC! Can't split command line '" + << command << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't split command line '" + << command << "'.\n"; + + delete [] line; + + return -1; +} + +FILE *Popen(char * const parameters[], const char *type) +{ + FILE *iop; + + struct pid *cur; + int pdes[2], pid; + + if (parameters == NULL || type == NULL) + { + return NULL; + } + + if ((*type != 'r' && *type != 'w') || type[1]) + { + return NULL; + } + + if ((cur = (struct pid *) malloc(sizeof(struct pid))) == NULL) + { + return NULL; + } + + if (pipe(pdes) < 0) + { + free(cur); + + return NULL; + } + + // + // Block all signals until command is exited. + // We need to gather information about the + // child in Pclose(). + // + + DisableSignals(); + + switch (pid = Fork()) + { + case -1: + { + // + // Error. + // + + #ifdef PANIC + *logofs << "Popen: PANIC! Function fork failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Function fork failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n"; + + close(pdes[0]); + close(pdes[1]); + + free(cur); + + return NULL; + } + case 0: + { + // + // Child. + // + + setgid(getgid()); + setuid(getuid()); + + if (*type == 'r') + { + if (pdes[1] != 1) + { + // + // Set up stdout. + // + + dup2(pdes[1], 1); + close(pdes[1]); + } + + close(pdes[0]); + } + else + { + if (pdes[0] != 0) + { + // + // Set up stdin. + // + + dup2(pdes[0], 0); + close(pdes[0]); + } + + close(pdes[1]); + } + + execvp(parameters[0], parameters + 1); + + exit(127); + } + } + + // + // Parent. Save data about the child. + // + + RegisterChild(pid); + + if (*type == 'r') + { + iop = fdopen(pdes[0], type); + + close(pdes[1]); + } + else + { + iop = fdopen(pdes[1], type); + + close(pdes[0]); + } + + cur -> fp = iop; + cur -> self = pid; + cur -> next = pidlist; + + pidlist = cur; + + #ifdef TEST + *logofs << "Popen: Executing "; + + for (int i = 0; i < 256 && parameters[i] != NULL; i++) + { + *logofs << "[" << parameters[i] << "]"; + } + + *logofs << " with descriptor " << fileno(iop) + << ".\n" << logofs_flush; + #endif + + return iop; +} + +FILE *Popen(const char *command, const char *type) +{ + char *parameters[256]; + + if (Psplit(command, parameters, 256) > 0) + { + FILE *file = Popen(parameters, type); + + for (int i = 0; i < 256; i++) + { + delete [] parameters[i]; + } + + return file; + } + else + { + #ifdef PANIC + *logofs << "Popen: PANIC! Failed to parse command '" + << command << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Failed to parse command '" + << command << "'.\n"; + + return NULL; + } +} + +int Pclose(FILE *iop) +{ + struct pid *cur, *last; + + int pstat; + int pid; + + #ifdef TEST + *logofs << "Pclose: Closing command with output " + << "on descriptor " << fileno(iop) << ".\n" + << logofs_flush; + #endif + + fclose((FILE *) iop); + + for (last = NULL, cur = pidlist; cur; last = cur, cur = cur -> next) + { + if (cur -> fp == iop) + { + break; + } + } + + if (cur == NULL) + { + #ifdef PANIC + *logofs << "Pclose: PANIC! Failed to find the process " + << "for descriptor " << fileno(iop) << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failed to find the process " + << "for descriptor " << fileno(iop) << ".\n"; + + return -1; + } + + do + { + #ifdef TEST + *logofs << "Pclose: Going to wait for process " + << "with pid '" << cur -> self << "'.\n" + << logofs_flush; + #endif + + pid = waitpid(cur -> self, &pstat, 0); + } + while (pid == -1 && errno == EINTR); + + if (last == NULL) + { + pidlist = cur -> next; + } + else + { + last -> next = cur -> next; + } + + free(cur); + + // + // Child has finished and we called the + // waitpid(). We can enable signals again. + // + + EnableSignals(); + + return (pid == -1 ? -1 : pstat); +} |