PIPS
wrapper.c
Go to the documentation of this file.
1 /*
2 
3  $Id: wrapper.c 23065 2016-03-02 09:05:50Z coelho $
4 
5  Copyright 1989-2016 MINES ParisTech
6 
7  This file is part of PIPS.
8 
9  PIPS is free software: you can redistribute it and/or modify it
10  under the terms of the GNU General Public License as published by
11  the Free Software Foundation, either version 3 of the License, or
12  any later version.
13 
14  PIPS is distributed in the hope that it will be useful, but WITHOUT ANY
15  WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  FITNESS FOR A PARTICULAR PURPOSE.
17 
18  See the GNU General Public License for more details.
19 
20  You should have received a copy of the GNU General Public License
21  along with PIPS. If not, see <http://www.gnu.org/licenses/>.
22 
23 */
24 #ifdef HAVE_CONFIG_H
25  #include "pips_config.h"
26 #endif
27 /*
28  * UNIX wrapper around tpips for jpips, for signal management.
29  * Under special comments on the pipe, signals are sent to tpips.
30  * I could not use popen() because I found no way to access the pid
31  * of the child process. Basically this is a re-implementation of popen().
32  *
33  * stdin TPIPS -w [stdout -> stdin] TPIPS stdout
34  *
35  * The full picture with jpips:
36  *
37  * -> kill/signal ->
38  * -> OutputStream/stdin -> TPIPS -w -> out/stdin ->
39  * JPIPS TPIPS
40  * <- InputStream/stdout <----------------------------
41  *
42  *
43  * What we might have some day:
44  *
45  * tty <-> EMACS <-> JPIPS > TPIPS -W s> TPIPS <-> XTERM
46  * <---------------+
47  *
48  * @author Fabien Coelho (with some advises from Ronan Keryell)
49  */
50 
51 /* UNIX headers.
52  */
53 #include <unistd.h>
54 #include <sys/types.h>
55 #include <signal.h>
56 #include <errno.h>
57 
58 /* Standard C headers.
59  */
60 #include <stdlib.h>
61 #include <stdio.h>
62 #include <string.h>
63 
64 #define SIZE 1024 /* buffer size */
65 
66 #define SIGNAL_TAG "# jpips send signal to tpips "
67 
68 /* available tags
69  */
70 #define CHECKPOINT "CHECKPOINT"
71 #define INTERRUPT "INTERRUPT"
72 #define KILLNOW "EXIT"
73 #define ABORTIT "ABORT"
74 
75 #define WRAPPER "[tpips_wrapper] "
76 
77 #define starts_with(s1, s2) (strncmp(s1, s2, strlen(s2))==0) /* a la java */
78 
79 /* global because needed in tpw_fatal_error.
80  */
81 static pid_t tpips_pid = 0;
82 
83 /* Enable log with -DLOG for debug.
84  */
85 static void tpw_log(
86 #ifndef LOG
87  __attribute__((__unused__))
88 #endif
89  char * message)
90 {
91 #if defined(LOG)
92  fprintf(stderr, WRAPPER "%s\n", message);
93  fflush(stderr);
94 #endif
95 }
96 
97 static void tpw_fatal_error(char * message)
98 {
99  fprintf(stderr, WRAPPER "fatal error: %s\n", message);
100  fflush(stderr);
101  kill(tpips_pid, SIGKILL);
102  exit(1);
103 }
104 
105 /* checks returned value for most system commands (-1 and errno set).
106  */
107 static void tpw_check_perror(int en, char * message)
108 {
109  if (en==-1)
110  {
111  perror(message);
113  }
114 }
115 
116 #define tpw_check(what, comment) \
117  tpw_log(comment), tpw_check_perror(what, comment)
118 #define KILL(pid,sig) if (pid!=0) { tpw_check(kill(pid,sig),"kill()"); }
119 
120 typedef void (*sig_handler_t)(int);
121 
122 /* signal handler for tpips_wrapper.
123  * basically forwards signals to tpips.
124  * tries to stop tpips before exiting.
125  * stops if tpips is stopped.
126  */
127 static void tpw_sig_handler(int sn /* signal number */)
128 {
129  /* these signals are traced.
130  */
131  fprintf(stderr, WRAPPER "signal %d caught!\n", sn);
132  fflush(stderr);
133 
134  switch (sn)
135  {
136  case SIGHUP:
137  case SIGINT:
138  case SIGTERM:
139  case SIGUSR1:
140  case SIGUSR2:
141  /* forward signals to tpips.
142  */
143  KILL(tpips_pid, sn);
144  break;
145  case SIGQUIT:
146  case SIGABRT:
147  case SIGCHLD: /* tpips stopped. */
148  case SIGPIPE: /* idem? */
149  fprintf(stderr, WRAPPER "killing tpips...\n");
150  fflush(stderr);
151  KILL(tpips_pid, SIGKILL);
152  exit(2);
153  break;
154  default:
155  fprintf(stderr, WRAPPER "unexpected signal (%d)\n", sn);
156  tpw_fatal_error("unexpected signal");
157  }
158 
159  /* reset signal handler.
160  */
161  (void) signal(sn, tpw_sig_handler);
162 }
163 
164 /* special handlers for tpips wrapper.
165  */
166 static void tpw_set_sig_handlers(void)
167 {
168  (void) signal(SIGHUP, tpw_sig_handler);
169  (void) signal(SIGINT, tpw_sig_handler);
170  (void) signal(SIGTERM, tpw_sig_handler);
171 
172  (void) signal(SIGUSR1, tpw_sig_handler);
173  (void) signal(SIGUSR2, tpw_sig_handler);
174 
175  (void) signal(SIGQUIT, tpw_sig_handler);
176  (void) signal(SIGABRT, tpw_sig_handler);
177  (void) signal(SIGCHLD, tpw_sig_handler);
178  (void) signal(SIGPIPE, tpw_sig_handler);
179 }
180 
181 static FILE * in_from_wrapper = NULL;
182 static FILE * out_to_tpips = NULL;
183 
184 /* fork, with stdout-stdin link kept
185  * @return the created pid.
186  * should be in_from_jpips instead of stdin?
187  * what about out_to_jpips and the wrapper???
188  * fdopen() might be of some help.
189  */
190 static pid_t tpw_fork_inout(void)
191 {
192  int filedes[2];
193  pid_t process;
194 
195  /* create pipe.
196  */
197  tpw_check(pipe(filedes), "pipe()");
198 
199  /* fork
200  */
201  process = fork();
202  tpw_check(process, "fork()");
203 
204  if (process==0)
205  {
206  /* CHILD
207  */
208  in_from_wrapper = fdopen(filedes[0], "r");
209  if (!in_from_wrapper) tpw_check(-1, "fdopen()");
210 
211  tpw_check(dup2(filedes[0], 0), "dup2()"); /* stdin = in_from_wrapper; */
212  }
213  else
214  {
215  /* PARENT
216  */
217  out_to_tpips = fdopen(filedes[1], "w");
218  if (!out_to_tpips) tpw_check(-1, "fdopen()");
219  }
220 
221  if (process)
222  {
223  /* the output might be interleaved with tpips output...
224  * maybe I should lock stderr.
225  */
226  fprintf(stderr, WRAPPER "started, tpips pid is %d\n", (int) process);
227  fflush(stderr);
228  }
229 
230  tpw_log("tpw_fork_inout() done");
231 
232  return process;
233 }
234 
235 /* @return a line from "in", or NULL at EOF.
236  * @caution a pointer to a static buffer is returned!
237  */
238 static char * tpw_read_line(FILE * in)
239 {
240  static char buffer[SIZE];
241  int c=0, i=0;
242 
243  while (c != '\n' && i<SIZE-1 && (c = getc(in))!=EOF)
244  buffer[i++] = (char) c;
245 
246  if (i==0) return NULL;
247 
248  buffer[i++] = '\0';
249  return buffer;
250 }
251 
252 /* send line to tpips.
253  */
254 static void tpw_send_string_to_tpips(char * line)
255 {
256  if (!out_to_tpips)
257  tpw_fatal_error("no out_to_tpips");
258 
259  fputs(line, out_to_tpips);
260  fflush(out_to_tpips);
261 
262  /* could send a signal to tpips to warn about the incoming command.
263  * it would be read from in_from_wrapper only.
264  */
265 }
266 
267 /* fork a tpips to goes on, while the current process acts as a wrapper,
268  * which forwards orders, and perform some special signal handling.
269  */
270 void tpips_wrapper(void)
271 {
272  char * line;
273 
275 
276  /* the child is a new tpips, it keeps on executing...
277  * the parent just acts as a wrapper, it never returns from here.
278  */
279  if (tpips_pid==0) return; /* CHILD is tpips and returns. */
280 
281  /* code for the WRAPPER starts here. the PARENT.
282  */
283 
284  /* special handlers.
285  */
287 
288  while ((line = tpw_read_line(stdin)))
289  {
290  /* forward to tpips.
291  * how to ensure that tpips is alive?
292  * SIGCHLD and SIGPIPE are caught.
293  */
295 
296  /* handle signals.
297  */
299  {
300  line += strlen(SIGNAL_TAG);
301 
302  if (starts_with(line, CHECKPOINT)) {
303  KILL(tpips_pid, SIGUSR1);
304  } else if (starts_with(line, INTERRUPT)) {
305  KILL(tpips_pid, SIGINT);
306  } else if (starts_with(line, ABORTIT)) {
307  KILL(tpips_pid, SIGABRT);
308  } else if (starts_with(line, KILLNOW)) {
309  KILL(tpips_pid, SIGUSR2);
310  } else {
312  }
313  }
314  }
315 
316  /* stop tpips on EOF.
317  */
318  tpw_log("exiting...");
319  fputs("exit\n", stdout);
320  fclose(stdout);
321  KILL(tpips_pid, SIGKILL);
322 
323  exit(0); /* wrapper mission done. */
324 }
float a2sf[2] __attribute__((aligned(16)))
USER generates a user error (i.e., non fatal) by printing the given MSG according to the FMT.
Definition: 3dnow.h:3
void const char const char const int
#define exit(code)
Definition: misc-local.h:54
int fprintf()
test sc_min : ce test s'appelle par : programme fichier1.data fichier2.data ...
static int line
FLEX_SCANNER.
Definition: scanner.c:852
static string buffer
Definition: string.c:113
static char * tpw_read_line(FILE *in)
Definition: wrapper.c:238
static void tpw_fatal_error(char *message)
Definition: wrapper.c:97
static void tpw_sig_handler(int sn)
signal handler for tpips_wrapper.
Definition: wrapper.c:127
#define starts_with(s1, s2)
Definition: wrapper.c:77
static void tpw_set_sig_handlers(void)
special handlers for tpips wrapper.
Definition: wrapper.c:166
#define KILL(pid, sig)
Definition: wrapper.c:118
#define ABORTIT
Definition: wrapper.c:73
static void tpw_log(__attribute__((__unused__)) char *message)
Enable log with -DLOG for debug.
Definition: wrapper.c:85
void tpips_wrapper(void)
fork a tpips to goes on, while the current process acts as a wrapper, which forwards orders,...
Definition: wrapper.c:270
#define SIZE
UNIX headers.
Definition: wrapper.c:64
static pid_t tpw_fork_inout(void)
fork, with stdout-stdin link kept
Definition: wrapper.c:190
static void tpw_check_perror(int en, char *message)
checks returned value for most system commands (-1 and errno set).
Definition: wrapper.c:107
static FILE * out_to_tpips
Definition: wrapper.c:182
void(* sig_handler_t)(int)
Definition: wrapper.c:120
#define INTERRUPT
Definition: wrapper.c:71
static void tpw_send_string_to_tpips(char *line)
send line to tpips.
Definition: wrapper.c:254
#define tpw_check(what, comment)
Definition: wrapper.c:116
#define KILLNOW
Definition: wrapper.c:72
#define CHECKPOINT
available tags
Definition: wrapper.c:70
#define SIGNAL_TAG
Definition: wrapper.c:66
static FILE * in_from_wrapper
Definition: wrapper.c:181
#define WRAPPER
Definition: wrapper.c:75
static pid_t tpips_pid
global because needed in tpw_fatal_error.
Definition: wrapper.c:81