blob: 5ea9b912ea038b8108218a175756cda18dce5ce0 [file] [log] [blame]
/*
* Copyright (c) 2007 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdio.h>
#include <errno.h>
int main(int argc, char *argv[]);
static void *stdout_writer(void *fd);
static void *stderr_writer(void *fd);
static void common_writer(int fd, char *file_suffix);
static char *stdout_prefix;
static char *stderr_prefix;
static FILE *trace_fd;
static int thread_count;
static pthread_t tid;
static pthread_mutex_t count_lock = PTHREAD_MUTEX_INITIALIZER;
int
main(int argc, char *argv[])
{
/*
* Parse command line parameter and redirect stdin, stdout and stderr
* to the specified target file.
* In principle, this code could be part of the main proxy module, in
* which case, the main proxy would just fork a copy of itself and run
* this code in the new child. It turns out this does not work, since
* at the point that the proxy is shut down, there may still be data
* queued to be sent to the front end. Attempts to write this data
* result in I/O errors once the front end has closed the proxy
* connection, and there is not a safe way to shut down the queued I/O.
* A separate process is likely safer anyway, since if the proxy has
* other problems, they could also interfere with the I/O handling.
* Since this miniproxy cannot assume a communication path to the front
* end, the miniproxy will silently fail on errors, unless tracing is
* enabled.
* The command line consists of a single parameter which this function
* must parse. The parameter contains blank delimited tokens as follows
* trace flag (y or n). Tracing is active when flag is 'y'
* Pathname prefix for stdout files, or /dev/null
* Pathname prefix for stderr files, or /dev/null
* Number of stdin file descriptors (fds) following, may be 0
* Set of stdin fds
* Number of stdout file descriptors following, may be 0
* Set of stdout fds
* Number of stderr file descriptors following, may be 0
* Set of stderr fds
*/
char *token;
int num_fds;
int current_fd;
int i;
if (argc != 2) {
exit(1);
}
token = strtok(argv[1], " ");
if (token == NULL) {
exit(1);
}
if (*token == 'y') {
trace_fd = fopen("/tmp/ptp_ibmpe_miniproxy.log", "w");
}
stdout_prefix = strtok(NULL, " ");
if (stdout_prefix == NULL) {
exit(1);
}
stderr_prefix = strtok(NULL, " ");
if (stderr_prefix == NULL) {
exit(1);
}
token = strtok(NULL, " ");
if (token == NULL) {
exit(1);
}
num_fds = atoi(token);
/*
* For stdin, just redirect stdin to /dev/null. The application will
* get an unexpected eof, which it will hopefully handle more
* gracefully than the SIGPIPE it would get without attempting
* redirection. There is really no other alternative here.
*/
for (i = 0; i < num_fds; i++) {
int stdin_fd;
token = strtok(NULL, " ");
if (token == NULL) {
exit(1);
}
current_fd = atoi(token);
stdin_fd - open("/dev/null", O_RDONLY);
if (stdin_fd != -1) {
if (trace_fd != NULL) {
fprintf(trace_fd, "Redirect stdin pipe fd %d to /dev/null\n",
current_fd);
fflush(trace_fd);
}
dup2(current_fd, stdin_fd);
}
}
token = strtok(NULL, " ");
if (token == NULL) {
exit(1);
}
/*
* Start a thread for each stdout file descriptor, where output will be
* redirected to /dev/null or to a user specified target file
*/
num_fds = atoi(token);
for (i = 0; i < num_fds; i++) {
token = strtok(NULL, " ");
if (token == NULL) {
exit(1);
}
current_fd = atoi(token);
if (trace_fd != NULL) {
fprintf(trace_fd, "Starting stdout writer thread for fd %d\n",
current_fd);
fflush(trace_fd);
}
pthread_mutex_lock(&count_lock);
thread_count = thread_count + 1;
pthread_mutex_unlock(&count_lock);
pthread_create(&tid, NULL, stdout_writer, (void *) current_fd);
}
token = strtok(NULL, " ");
if (token == NULL) {
exit(1);
}
/*
* Start a thread for each stdout file descriptor, where output will be
* redirected to /dev/null or to a user specified target file
*/
num_fds = atoi(token);
for (i = 0; i < num_fds; i++) {
token = strtok(NULL, " ");
if (token == NULL) {
exit(1);
}
current_fd = atoi(token);
if (trace_fd != NULL) {
fprintf(trace_fd, "Starting stderr writer thread for fd %d\n",
current_fd);
fflush(trace_fd);
}
pthread_mutex_lock(&count_lock);
thread_count = thread_count + 1;
pthread_mutex_unlock(&count_lock);
pthread_create(&tid, NULL, stderr_writer, (void *) current_fd);
}
/*
* Wait until there are no more I/O threads
*/
while (thread_count > 0) {
sleep(60);
}
exit(0);
}
void *
stdout_writer(void *fd)
{
common_writer((int) fd, "stdout");
return NULL;
}
void *
stderr_writer(void *fd)
{
common_writer((int) fd, "stderr");
return NULL;
}
void
common_writer(int fd, char *file_suffix)
{
/*
* Handle redirection of output to /dev/null or to user-specified
* file. This function first changes the file descriptor from
* non-blocking I/O to blocking I/O then loops, copying data from
* input to output until eof is reached.
*/
int flags;
int status;
int out_fd;
char buf[256];
char path[PATH_MAX];
flags = fcntl((int) fd, F_GETFL);
flags = flags & (0xffffffff ^ O_NONBLOCK);
status = fcntl((int) fd, F_SETFL, flags);
if (status != -1) {
if (strcmp(stdout_prefix, "/dev/null") == 0) {
out_fd = open(stdout_prefix, O_RDWR | O_CREAT | O_TRUNC, 0644);
}
else {
snprintf(path, sizeof path, "%s_fd%d.%s", stdout_prefix, (int) fd,
file_suffix);
path[sizeof path - 1] = '\0';
out_fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
}
if (out_fd != -1) {
status = read((int) fd, buf, sizeof buf);
while (status > 0) {
status = write(out_fd, buf, status);
if (status == -1) {
break;
}
status = read((int) fd, buf, sizeof buf);
}
if ((status == -1) && (trace_fd != NULL)) {
fprintf(trace_fd, "I/O error in writer thread for fd %d: %s\n",
(int) fd, strerror(errno));
fflush(trace_fd);
}
close(out_fd);
}
}
if (trace_fd != NULL) {
fprintf(trace_fd, "Writer thread for fd %d complete\n", (int) fd);
fflush(trace_fd);
}
pthread_mutex_lock(&count_lock);
thread_count = thread_count - 1;
pthread_mutex_unlock(&count_lock);
}