aboutsummaryrefslogtreecommitdiff
path: root/nxcomp/Pipe.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'nxcomp/Pipe.cpp')
-rw-r--r--nxcomp/Pipe.cpp412
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);
+}