456 lines
11 KiB
C
456 lines
11 KiB
C
/**********
|
|
Copyright 1990 Regents of the University of California. All rights reserved.
|
|
Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group
|
|
**********/
|
|
|
|
/*
|
|
* Stuff for asynchronous spice runs, and also rspice.
|
|
*/
|
|
|
|
#include "ngspice/ngspice.h"
|
|
#include "ngspice/cpdefs.h"
|
|
#include "ngspice/ftedefs.h"
|
|
|
|
#include "aspice.h"
|
|
#include "variable.h"
|
|
#include "circuits.h"
|
|
|
|
|
|
# ifdef HAVE_SYS_WAIT_H
|
|
/* should be more tests here I think */
|
|
# define OK_ASPICE
|
|
# endif
|
|
|
|
#ifdef OK_ASPICE
|
|
|
|
#ifdef HAVE_PWD_H
|
|
# include <pwd.h>
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include "ngspice/fteinp.h"
|
|
#include "ngspice/dvec.h"
|
|
|
|
#include "../misc/mktemp.h"
|
|
|
|
/*
|
|
This is required for the GCC pre-processor and might be needed for others
|
|
Added to resolve ngspice bug 1293746
|
|
http://sourceforge.net/tracker/index.php?func=detail&aid=1293746&group_id=38962&atid=423915
|
|
*/
|
|
#if !defined(SOLARIS) && defined(__SVR4) && defined(__sun)
|
|
# define SOLARIS
|
|
#endif
|
|
|
|
|
|
#ifndef SEEK_SET
|
|
# define SEEK_SET 0
|
|
#endif
|
|
|
|
static void sigchild(void);
|
|
|
|
struct proc {
|
|
int pr_pid; /* The pid of the spice job. */
|
|
char *pr_rawfile; /* The temporary raw file. */
|
|
char *pr_name; /* The name of the spice run. */
|
|
char *pr_inpfile; /* The name of the input file. */
|
|
char *pr_outfile; /* The name of the (tmp) output file. */
|
|
bool pr_saveout; /* Don't (void) unlink the output file */
|
|
struct proc *pr_next; /* Link. */
|
|
};
|
|
|
|
static struct proc *running = NULL;
|
|
static int numchanged = 0; /* How many children have changed in state. */
|
|
|
|
|
|
void
|
|
com_aspice(wordlist *wl)
|
|
{
|
|
char *deck, *output = NULL, spicepath[BSIZE_SP], s[BSIZE_SP];
|
|
char *raw, *t;
|
|
FILE *inp;
|
|
struct proc *p;
|
|
int pid;
|
|
bool saveout = FALSE;
|
|
|
|
deck = wl->wl_word;
|
|
if (!cp_getvar("spicepath", CP_STRING, spicepath, sizeof(spicepath))) {
|
|
if (!Spice_Path || !*Spice_Path) {
|
|
fprintf(cp_err,
|
|
"No spice-3 binary is available for the aspice command.\n");
|
|
return;
|
|
}
|
|
(void) strcpy(spicepath, Spice_Path);
|
|
}
|
|
|
|
if (wl->wl_next) {
|
|
output = wl->wl_next->wl_word;
|
|
saveout = TRUE;
|
|
} else {
|
|
output = smktemp("spout");
|
|
}
|
|
|
|
if ((inp = fopen(deck, "r")) == NULL) {
|
|
perror(deck);
|
|
return;
|
|
}
|
|
if (!fgets(s, BSIZE_SP, inp)) {
|
|
fprintf(cp_err, "Error: bad deck %s\n", deck);
|
|
(void) fclose(inp);
|
|
return;
|
|
}
|
|
for (t = s; *t && (*t != '\n'); t++)
|
|
;
|
|
*t = '\0';
|
|
fprintf(cp_out, "Starting spice run for:\n%s\n", s);
|
|
(void) fclose(inp);
|
|
raw = smktemp("raw");
|
|
(void) fclose(fopen(raw, "w")); /* So there isn't a race condition. */
|
|
pid = fork();
|
|
if (pid == 0) {
|
|
if (!(freopen(deck, "r", stdin))) {
|
|
perror(deck);
|
|
exit(EXIT_BAD);
|
|
}
|
|
if (!(freopen(output, "w", stdout))) {
|
|
perror(output);
|
|
exit(EXIT_BAD);
|
|
}
|
|
(void) dup2(fileno(stdout), fileno(stderr));
|
|
|
|
(void) execl(spicepath, spicepath, "-r", raw, NULL);
|
|
|
|
/* Screwed up. */
|
|
perror(spicepath);
|
|
exit(EXIT_BAD);
|
|
}
|
|
|
|
/* Add this one to the job list. */
|
|
p = TMALLOC(struct proc, 1);
|
|
p->pr_pid = pid;
|
|
p->pr_name = copy(s);
|
|
p->pr_rawfile = copy(raw);
|
|
p->pr_inpfile = copy(deck);
|
|
p->pr_outfile = copy(output);
|
|
p->pr_saveout = saveout;
|
|
if (running)
|
|
p->pr_next = running;
|
|
running = p;
|
|
# ifdef SIGCHLD
|
|
(void) signal(SIGCHLD, (SIGNAL_FUNCTION) sigchild);
|
|
# else
|
|
# ifdef SIGCLD
|
|
(void) signal(SIGCLD, (SIGNAL_FUNCTION) sigchild);
|
|
# endif
|
|
# endif
|
|
}
|
|
|
|
|
|
void
|
|
com_jobs(wordlist *wl)
|
|
{
|
|
struct proc *p;
|
|
|
|
NG_IGNORE(wl);
|
|
|
|
for (p = running; p; p = p->pr_next)
|
|
fprintf(cp_out, "%d\t%.70s\n", p->pr_pid, p->pr_name);
|
|
}
|
|
|
|
|
|
static void
|
|
sigchild(void)
|
|
{
|
|
numchanged++;
|
|
if (ft_asyncdb)
|
|
fprintf(cp_err, "%d jobs done now\n", numchanged);
|
|
if (cp_cwait)
|
|
ft_checkkids();
|
|
}
|
|
|
|
|
|
/* This gets called every once in a while, and checks to see if any
|
|
* jobs have finished. If they have it gets the data. The problem is
|
|
* that wait(0) is probably more portable, but it can't tell
|
|
* whether the exit was normal or not.
|
|
*/
|
|
|
|
/*
|
|
* On posix systems, wait() is:
|
|
* pid_t wait(int *status);
|
|
*/
|
|
|
|
int status;
|
|
|
|
|
|
void
|
|
ft_checkkids(void)
|
|
{
|
|
struct proc *p = NULL, *lp = NULL;
|
|
char buf[BSIZE_SP];
|
|
FILE *fp;
|
|
pid_t pid = 0;
|
|
static bool here = FALSE; /* Don't want to be re-entrant. */
|
|
|
|
if (!numchanged || here)
|
|
return;
|
|
|
|
here = TRUE;
|
|
|
|
while (numchanged > 0) {
|
|
pid = wait(&status);
|
|
if (pid == -1) {
|
|
fprintf(cp_err,
|
|
"ft_checkkids: Internal Error: should be %d jobs done but there aren't any.\n",
|
|
numchanged);
|
|
numchanged = 0;
|
|
running = NULL;
|
|
here = FALSE;
|
|
return;
|
|
}
|
|
|
|
for (p = running; p; p = p->pr_next) {
|
|
if (p->pr_pid == pid)
|
|
break;
|
|
lp = p;
|
|
}
|
|
|
|
if (p == NULL) {
|
|
fprintf(cp_err,
|
|
"ft_checkkids: Internal Error: Process %d not a job!\n",
|
|
(int) pid);
|
|
here = FALSE;
|
|
return;
|
|
}
|
|
|
|
if (p == running)
|
|
running = p->pr_next;
|
|
else
|
|
lp->pr_next = p->pr_next;
|
|
|
|
fprintf(cp_out, "Job finished: %.60s\n", p->pr_name);
|
|
numchanged--;
|
|
ft_loadfile(p->pr_rawfile);
|
|
(void) unlink(p->pr_rawfile);
|
|
out_init();
|
|
|
|
if (!(fp = fopen(p->pr_outfile, "r"))) {
|
|
perror(p->pr_outfile);
|
|
here = FALSE;
|
|
return;
|
|
}
|
|
|
|
while (fgets(buf, BSIZE_SP, fp))
|
|
out_send(buf);
|
|
|
|
(void) fclose(fp);
|
|
if (!p->pr_saveout)
|
|
(void) unlink(p->pr_outfile);
|
|
printf("\n-----\n");
|
|
}
|
|
|
|
printf("\n");
|
|
#ifdef TIOCSTI
|
|
(void) ioctl(0, TIOCSTI, "\022"); /* Reprint the line. */
|
|
#endif
|
|
here = FALSE;
|
|
}
|
|
|
|
|
|
/* Run a spice job remotely. See the description of the spice daemon for
|
|
* the protocol. This is no longer 4.2 specific.
|
|
*/
|
|
|
|
void
|
|
com_rspice(wordlist *wl)
|
|
{
|
|
char rhost[64], program[128], buf[BSIZE_SP];
|
|
char remote_shell[513];
|
|
char *outfile;
|
|
FILE *inp, *serv, *out, *srv_input, *err_outp;
|
|
struct plot *pl;
|
|
size_t n;
|
|
int to_serv[2], from_serv[2], err_serv[2];
|
|
int pid;
|
|
long pos;
|
|
int num;
|
|
char *p;
|
|
|
|
/* Figure out where the spicedaemon is and connect to it. */
|
|
if (!cp_getvar("rhost", CP_STRING, rhost, sizeof(rhost)))
|
|
(void) strcpy(rhost, Spice_Host);
|
|
if (!cp_getvar("rprogram", CP_STRING, program, sizeof(program)))
|
|
*program = '\0';
|
|
if (!cp_getvar("remote_shell", CP_STRING, remote_shell, sizeof(remote_shell)))
|
|
strcpy(remote_shell, "rsh");
|
|
|
|
if (*rhost == '\0') {
|
|
fprintf(cp_err,
|
|
"Error: there is no remote ngspice.host for this site -- set \"rhost\".\n");
|
|
return;
|
|
}
|
|
if (*program == '\0') {
|
|
fprintf(cp_err,
|
|
"Error: there is no remote spice program for this site -- set \"rprogram\".\n");
|
|
return;
|
|
}
|
|
|
|
if (pipe(to_serv) < 0) {
|
|
perror("pipe to server");
|
|
return;
|
|
}
|
|
if (pipe(from_serv) < 0) {
|
|
perror("pipe from server");
|
|
return;
|
|
}
|
|
if (pipe(err_serv) < 0) {
|
|
perror("2nd pipe from server");
|
|
return;
|
|
}
|
|
|
|
pid = fork();
|
|
if (pid == 0) {
|
|
/* I am the "server" process */
|
|
close(to_serv[1]);
|
|
close(from_serv[0]);
|
|
close(err_serv[0]);
|
|
|
|
fclose(stdin);
|
|
fclose(stdout);
|
|
fclose(stderr);
|
|
|
|
dup2(to_serv[0], 0); /* stdin */
|
|
dup2(from_serv[1], 1); /* stdout */
|
|
dup2(err_serv[1], 2); /* stderr */
|
|
|
|
execlp(remote_shell, remote_shell, rhost, program, "-s", NULL);
|
|
/* system(com_buf); */
|
|
perror(remote_shell);
|
|
exit(-1);
|
|
} else if (pid == -1) {
|
|
perror("fork");
|
|
return;
|
|
}
|
|
|
|
/* I am the "client" side */
|
|
close(to_serv[0]);
|
|
close(from_serv[1]);
|
|
close(err_serv[1]);
|
|
srv_input = fdopen(to_serv[1], "w");
|
|
serv = fdopen(from_serv[0], "r");
|
|
err_outp = fdopen(err_serv[0], "r");
|
|
|
|
/* Send the circuit over. */
|
|
if (wl) {
|
|
while (wl) {
|
|
if (!(inp = fopen(wl->wl_word, "r"))) {
|
|
perror(wl->wl_word);
|
|
wl = wl->wl_next;
|
|
continue; /* Should be careful */
|
|
}
|
|
while ((n = fread(buf, 1, BSIZE_SP, inp)) > 0)
|
|
(void) fwrite(buf, 1, strlen(buf), srv_input);
|
|
/* (void) write(s, buf, n); */
|
|
wl = wl->wl_next;
|
|
fclose(inp);
|
|
}
|
|
/* (void) write(s, "@\n", 3);*/
|
|
} else {
|
|
if (ft_nutmeg || !ft_curckt) {
|
|
fprintf(cp_err, "Error: no circuits loaded\n");
|
|
fclose(srv_input);
|
|
fclose(serv);
|
|
return;
|
|
}
|
|
|
|
inp_list(srv_input, ft_curckt->ci_deck, ft_curckt->ci_options, LS_DECK);
|
|
}
|
|
|
|
fclose(srv_input);
|
|
|
|
/* Now wait for things to come through */
|
|
while ((p = fgets(buf, BSIZE_SP, serv)) != NULL) {
|
|
if (!strncmp(buf, "Title:", 6))
|
|
break;
|
|
fputs(buf, cp_out);
|
|
}
|
|
|
|
outfile = smktemp("rsp");
|
|
if ((out = fopen(outfile, "w+")) == NULL) {
|
|
perror(outfile);
|
|
(void) fclose(serv);
|
|
return;
|
|
}
|
|
|
|
if (p)
|
|
fputs(buf, out);
|
|
|
|
while ((n = fread(buf, 1, BSIZE_SP, serv)) != 0)
|
|
(void) fwrite(buf, 1, n, out);
|
|
|
|
/* We hope that positioning info + error messages < pipe size */
|
|
while (fgets(buf, BSIZE_SP, err_outp))
|
|
if (!strncmp("@@@", buf, 3)) {
|
|
if (sscanf(buf, "@@@ %ld %d", &pos, &num) != 2) {
|
|
fprintf(stderr, "Error reading rawdata: %s\n", buf);
|
|
continue;
|
|
}
|
|
if (fseek(out, pos, SEEK_SET))
|
|
fprintf(stderr,
|
|
"Error adjusting rawfile: write \"%d\" at %ld\n",
|
|
num, pos);
|
|
else
|
|
fprintf(out, "%d", num);
|
|
} else {
|
|
fprintf(stderr, "%s", buf);
|
|
}
|
|
|
|
(void) fclose(out);
|
|
(void) fclose(serv);
|
|
(void) fclose(err_outp);
|
|
|
|
pl = raw_read(outfile);
|
|
if (pl)
|
|
plot_add(pl);
|
|
(void) unlink(outfile);
|
|
fprintf(stderr, "done.\n");
|
|
}
|
|
|
|
#else
|
|
|
|
void
|
|
com_aspice(wordlist *wl)
|
|
{
|
|
NG_IGNORE(wl);
|
|
fprintf(cp_err, "Asynchronous spice jobs are not available.\n");
|
|
}
|
|
|
|
|
|
void
|
|
com_jobs(wordlist *wl)
|
|
{
|
|
NG_IGNORE(wl);
|
|
fprintf(cp_err, "Asynchronous spice jobs are not available.\n");
|
|
}
|
|
|
|
|
|
void
|
|
ft_checkkids(void)
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
com_rspice(wordlist *wl)
|
|
{
|
|
NG_IGNORE(wl);
|
|
fprintf(cp_err, "Remote spice jobs are not available.\n");
|
|
}
|
|
|
|
#endif
|