/*
* grace_np - a library for interfacing with Grace using pipes
*
* Copyright (c) 1997-1998 Henrik Seidel
* Copyright (c) 1999-2003 Grace Development Team
*
*
* All Rights Reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#include <limits.h>
#ifndef OPEN_MAX
# define OPEN_MAX 256
#endif
#include "grace_np.h"
/* static global variables */
static char* buf = NULL; /* global write buffer */
static int bufsize; /* size of the global write buffer */
static int bufsizeforce; /* threshold for forcing a flush */
static int fd_pipe = -1; /* file descriptor of the pipe */
static pid_t pid = (pid_t) -1; /* pid of grace */
/*
* notify grace when finished
*/
static void
#ifdef HAVE_ON_EXIT
notify_grace_on_exit(int status, void* arg)
#else
notify_grace_on_exit(void)
#endif
{
if (fd_pipe != -1) {
GraceClosePipe();
}
}
/*
* default function for reporting errors
*/
static void
GraceDefaultError(const char *msg)
{
fprintf(stderr, "%s\n", msg);
}
/*
* variable holding user function for reporting errors
*/
static GraceErrorFunctionType error_function = GraceDefaultError;
/*
* function for reporting system errors
*/
static void
GracePerror(const char *prefix)
{
char msg[1024];
#ifdef HAVE_STRERROR
sprintf(msg, "%s: %s", prefix, strerror(errno));
#else
# ifdef HAVE_SYS_ERRLIST_IN_STDIO_H
sprintf(msg, "%s: %s", prefix, sys_errlist[errno]);
# else
if (errno == EPIPE) {
/* this one deserve special attention here */
sprintf(msg, "%s: Broken pipe", prefix);
} else {
sprintf(msg, "%s: System error (errno = %d)", prefix, errno);
}
# endif
#endif
error_function(msg);
}
/* Close the pipe and free the buffer */
static void
GraceCleanup(void)
{
if (fd_pipe != -1) {
if (close(fd_pipe) != 0) {
GracePerror("GraceCleanup");
}
fd_pipe = -1;
}
free(buf);
buf = NULL;
}
/*
* try to send data to grace (one pass only)
*/
static int
GraceOneWrite(int left)
{
int written;
written = write(fd_pipe, buf, left);
if (written > 0) {
left -= written;
if (left > 0) {
/* move the remaining characters (and the final '\0') */
#ifdef HAVE_MEMMOVE
memmove(buf, buf + written, left + 1);
#else
bcopy(buf + written, buf, left + 1);
#endif
} else {
/* clear the buffer */
*buf = '\0';
}
} else if (written < 0) {
if (errno == EPIPE) {
/* Grace has closed the pipe : we cannot write anymore */
GraceCleanup();
} else {
GracePerror("GraceOneWrite");
}
return (-1);
}
return (left);
}
/*
* register a user function to report errors
*/
GraceErrorFunctionType
GraceRegisterErrorFunction(GraceErrorFunctionType f)
{
GraceErrorFunctionType old = error_function;
if (f != (GraceErrorFunctionType) NULL) {
error_function = f;
}
return old;
}
static void
handle_sigchld(int signum)
{
int status;
pid_t retval;
if (fd_pipe != -1) {
retval = waitpid(pid, &status, WNOHANG);
if (retval == pid) {
/* Grace just died */
pid = (pid_t) -1;
}
}
}
int
GraceOpenVA(char* exe, int bs, ...)
{
int i, fd[2];
char fd_number[4];
va_list ap;
char **arglist;
char *s;
int numarg;
if (fd_pipe != -1) {
error_function("Grace subprocess already running");
return (-1);
}
/* Make sure the buffer is not too small */
if (bs < 64) {
error_function("The buffer size in GraceOpenVA should be >= 64");
return (-1);
}
bufsize = bs;
bufsizeforce = bs / 2;
/* make sure the grace subprocess is notified at the end */
#ifdef HAVE_ON_EXIT
on_exit(notify_grace_on_exit, NULL);
#else
atexit(notify_grace_on_exit);
#endif
/* Don't exit on SIGPIPE */
signal(SIGPIPE, SIG_IGN);
/* Clean up zombie prcesses */
signal(SIGCHLD, handle_sigchld);
/* Make the pipe */
if (pipe(fd)) {
GracePerror("GraceOpenVA");
return (-1);
}
/* Fork a subprocess for starting grace */
pid = fork();
if (pid == (pid_t) (-1)) {
GracePerror("GraceOpenVA");
close(fd[0]);
close(fd[1]);
return (-1);
}
/* If we are the child, replace ourselves with grace */
if (pid == (pid_t) 0) {
for (i = 0; i < OPEN_MAX; i++) {
/* we close everything except stdin, stdout, stderr
and the read part of the pipe */
if (i != fd[0] &&
i != STDIN_FILENO &&
i != STDOUT_FILENO &&
i != STDERR_FILENO) {
close(i);
}
}
/* build the argument list */
va_start(ap, bs);
numarg = 3;
arglist = malloc((numarg + 1)*SIZEOF_VOID_P);
arglist[0] = exe;
arglist[1] = "-dpipe";
sprintf(fd_number, "%d", fd[0]);
arglist[2] = fd_number;
while ((s = va_arg(ap, char *)) != NULL) {
numarg++;
arglist = realloc(arglist, (numarg + 1)*SIZEOF_VOID_P);
arglist[numarg - 1] = s;
}
arglist[numarg] = NULL;
va_end(ap);
execvp(exe, arglist);
/* if we get here execvp failed */
fprintf(stderr, "GraceOpenVA: Could not start %s\n", exe);
_exit(EXIT_FAILURE);
}
/* We are the parent -> keep the write part of the pipe
and allocate the write buffer */
buf = malloc(bufsize);
if (buf == NULL) {
error_function("GraceOpenVA: Not enough memory");
close(fd[0]);
close(fd[1]);
return (-1);
}
*buf = '\0';
close(fd[0]);
fd_pipe = fd[1];
return (0);
}
int
GraceOpen(int bs)
{
return GraceOpenVA("xmgrace", bs, "-nosafe", "-noask", NULL);
}
int
GraceIsOpen(void)
{
return (fd_pipe >= 0) ? 1 : 0;
}
int
GraceClose(void)
{
if (fd_pipe == -1) {
error_function("No grace subprocess");
return (-1);
}
/* Tell grace to exit */
if (GraceCommand ("exit") == -1 || GraceFlush() == -1){
kill(pid, SIGTERM);
}
GraceCleanup();
return (0);
}
int
GraceClosePipe(void)
{
if (fd_pipe == -1) {
error_function("No grace subprocess");
return (-1);
}
/* Tell grace to close the pipe */
if (GraceCommand ("close") == -1 || GraceFlush() == -1){
GraceCleanup();
return (-1);
}
GraceCleanup();
return (0);
}
int
GraceFlush(void)
{
int loop, left;
if (fd_pipe == -1) {
error_function("No grace subprocess");
return (-1);
}
left = strlen(buf);
for (loop = 0; loop < 30; loop++) {
left = GraceOneWrite(left);
if (left < 0) {
return (-1);
} else if (left == 0) {
return (0);
}
}
error_function("GraceFlush: ran into eternal loop");
return (-1);
}
int
GracePrintf(const char* fmt, ...)
{
va_list ap;
char* str;
int nchar;
if (fd_pipe == -1) {
error_function("No grace subprocess");
return (0);
}
/* Allocate a new string buffer for the function arguments */
str = (char *) malloc ((size_t) bufsize);
if (str == (char *) NULL) {
error_function("GracePrintf: Not enough memory");
return (0);
}
/* Print to the string buffer according to the function arguments */
va_start (ap, fmt);
#if defined(HAVE_VSNPRINTF)
nchar = vsnprintf (str, bufsize - 2, fmt, ap);
#else
nchar = vsprintf (str, fmt, ap);
#endif
va_end (ap);
nchar++; /* This is for the appended "\n" */
if (GraceCommand (str) == -1) {
nchar = 0;
}
free (str);
return (nchar);
}
int
GraceCommand(const char* cmd)
{
int left;
if (fd_pipe == -1) {
error_function("No grace subprocess");
return (-1);
}
/* Append the new string to the global write buffer */
if (strlen(buf) + strlen(cmd) + 2 > bufsize) {
error_function("GraceCommand: Buffer full");
return (-1);
}
strcat(buf, cmd);
strcat(buf, "\n");
left = strlen(buf);
/* Try to send the global write buffer to grace */
left = GraceOneWrite(left);
if (left >= bufsizeforce) {
if (GraceFlush() != 0) {
return (-1);
}
} else if (left < 0) {
return (-1);
}
return (0);
}
syntax highlighted by Code2HTML, v. 0.9.1