1 2/* 3 * server.c Set up and handle communications with a server process. 4 * 5 * Server Handling copyright 1992-1999, 2001 The Free Software Foundation 6 * 7 * Server Handling is free software. 8 * You may redistribute it and/or modify it under the terms of the 9 * GNU General Public License, as published by the Free Software 10 * Foundation; either version 2, or (at your option) any later version. 11 * 12 * Server Handling is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with Server Handling. See the file "COPYING". If not, 19 * write to: The Free Software Foundation, Inc., 20 * 59 Temple Place - Suite 330, 21 * Boston, MA 02111-1307, USA. 22 * 23 * As a special exception, The Free Software Foundation gives 24 * permission for additional uses of the text contained in his release 25 * of ServerHandler. 26 * 27 * The exception is that, if you link the ServerHandler library with other 28 * files to produce an executable, this does not by itself cause the 29 * resulting executable to be covered by the GNU General Public License. 30 * Your use of that executable is in no way restricted on account of 31 * linking the ServerHandler library code into it. 32 * 33 * This exception does not however invalidate any other reasons why 34 * the executable file might be covered by the GNU General Public License. 35 * 36 * This exception applies only to the code released by The Free 37 * Software Foundation under the name ServerHandler. If you copy code 38 * from other sources under the General Public License into a copy of 39 * ServerHandler, as the General Public License permits, the exception 40 * does not apply to the code that you add in this way. To avoid 41 * misleading anyone as to the status of such modified files, you must 42 * delete this exception notice from them. 43 * 44 * If you write modifications of your own for ServerHandler, it is your 45 * choice whether to permit this exception to apply to your modifications. 46 * If you do not wish that, delete this exception notice. 47 */ 48#include "auto-host.h" 49 50#include "ansidecl.h" 51#include "system.h" 52#include <signal.h> 53 54#include "server.h" 55 56#if !defined(volatile) && !defined(HAVE_VOLATILE) 57# define volatile 58#endif 59 60STATIC volatile enum t_bool read_pipe_timeout; 61STATIC pid_t server_master_pid = NOPROCESS; 62 63tSCC* def_args[] = 64{ (char *) NULL, (char *) NULL }; 65STATIC t_pf_pair server_pair = 66{ (FILE *) NULL, (FILE *) NULL }; 67STATIC pid_t server_id = NULLPROCESS; 68/* 69 * Arbitrary text that should not be found in the shell output. 70 * It must be a single line and appear verbatim at the start of 71 * the terminating output line. 72 */ 73tSCC z_done[] = "ShElL-OuTpUt-HaS-bEeN-cOmPlEtEd"; 74tSCC* p_cur_dir = (char *) NULL; 75 76/* 77 * load_data 78 * 79 * Read data from a file pointer (a pipe to a process in this context) 80 * until we either get EOF or we get a marker line back. 81 * The read data are stored in a malloc-ed string that is truncated 82 * to size at the end. Input is assumed to be an ASCII string. 83 */ 84static char *load_data PARAMS ((FILE *)); 85static char * 86load_data (fp) 87 FILE *fp; 88{ 89 char *pz_text; 90 size_t text_size; 91 char *pz_scan; 92 char z_line[1024]; 93 t_bool got_done = BOOL_FALSE; 94 95 text_size = sizeof (z_line) * 2; 96 pz_scan = pz_text = xmalloc (text_size); 97 98 for (;;) 99 { 100 size_t used_ct; 101 102 alarm (10); 103 read_pipe_timeout = BOOL_FALSE; 104 if (fgets (z_line, sizeof (z_line), fp) == (char *) NULL) 105 break; 106 107 if (strncmp (z_line, z_done, sizeof (z_done) - 1) == 0) 108 { 109 got_done = BOOL_TRUE; 110 break; 111 } 112 113 strcpy (pz_scan, z_line); 114 pz_scan += strlen (z_line); 115 used_ct = (size_t) (pz_scan - pz_text); 116 117 if (text_size - used_ct < sizeof (z_line)) 118 { 119 size_t off = (size_t) (pz_scan - pz_text); 120 121 text_size += 4096; 122 pz_text = xrealloc ((void *) pz_text, text_size); 123 pz_scan = pz_text + off; 124 } 125 } 126 127 alarm (0); 128 if (read_pipe_timeout || ! got_done) 129 { 130 free ((void *) pz_text); 131 return (char *) NULL; 132 } 133 134 while ((pz_scan > pz_text) && ISSPACE (pz_scan[-1])) 135 pz_scan--; 136 *pz_scan = NUL; 137 return xrealloc ((void *) pz_text, strlen (pz_text) + 1); 138} 139 140 141/* 142 * close_server 143 * 144 * Make certain the server process is dead, close the 145 * pipes to it and from it, finally NULL out the file pointers 146 */ 147void 148close_server () 149{ 150 if ( (server_id != NULLPROCESS) 151 && (server_master_pid == getpid ())) 152 { 153 kill ((pid_t) server_id, SIGKILL); 154 server_id = NULLPROCESS; 155 server_master_pid = NOPROCESS; 156 fclose (server_pair.pf_read); 157 fclose (server_pair.pf_write); 158 server_pair.pf_read = server_pair.pf_write = (FILE *) NULL; 159 } 160} 161 162/* 163 * sig_handler really only handles the timeout and pipe signals. 164 * This ensures that we do not wait forever on a request 165 * to our server, and also that if the server dies, we do not 166 * die from a sigpipe problem. 167 */ 168static void sig_handler PARAMS ((int)); 169static void 170sig_handler (signo) 171 int signo ATTRIBUTE_UNUSED; 172{ 173#ifdef DEBUG 174 /* FIXME: this is illegal to do in a signal handler. */ 175 fprintf (stderr, 176 "fixincl ERROR: sig_handler: killed pid %ld due to %s\n", 177 (long) server_id, signo == SIGPIPE ? "SIGPIPE" : "SIGALRM"); 178#endif 179 close_server (); 180 read_pipe_timeout = BOOL_TRUE; 181} 182 183 184/* 185 * server_setup Establish the signal handler for PIPE and ALARM. 186 * Also establishes the current directory to give to the 187 * server process at the start of every server command. 188 */ 189static void server_setup PARAMS ((void)); 190static void 191server_setup () 192{ 193 static int atexit_done = 0; 194 195 if (atexit_done++ == 0) 196 atexit (close_server); 197 else 198 fputs ("NOTE: server restarted\n", stderr); 199 200 server_master_pid = getpid (); 201 202 signal (SIGPIPE, sig_handler); 203 signal (SIGALRM, sig_handler); 204 205 fputs ("trap : 1\n", server_pair.pf_write); 206 fflush (server_pair.pf_write); 207 p_cur_dir = getcwd ((char *) NULL, MAXPATHLEN + 1); 208} 209 210/* 211 * find_shell 212 * 213 * Locate a shell suitable for use. For various reasons 214 * (like the use of "trap" in server_setup(), it must be a 215 * Bourne-like shell. 216 * 217 * Most of the time, /bin/sh is preferred, but sometimes 218 * it's quite broken (like on Ultrix). autoconf lets you 219 * override with $CONFIG_SHELL, so we do the same. 220 */ 221 222static const char *find_shell PARAMS ((void)); 223static const char * 224find_shell () 225{ 226 char * shell = getenv ("CONFIG_SHELL"); 227 if (shell) 228 return shell; 229 230 return "/bin/sh"; 231} 232 233 234/* 235 * run_shell 236 * 237 * Run a shell command on the server. The command string 238 * passed in is wrapped inside the sequence: 239 * 240 * cd <original directory> 241 * <command string> 242 * echo 243 * echo <end-of-command-marker> 244 * 245 * This ensures that all commands start at a known place in 246 * the directory structure, that any incomplete output lines 247 * are completed and that our special marker sequence appears on 248 * a line by itself. We have chosen a marker that is 249 * excessively unlikely to be reproduced in normal output: 250 * 251 * "ShElL-OuTpUt-HaS-bEeN-cOmPlEtEd" 252 */ 253char * 254run_shell (pz_cmd) 255 const char *pz_cmd; 256{ 257 tSCC zNoServer[] = "Server not running, cannot run:\n%s\n\n"; 258 t_bool retry = BOOL_TRUE; 259 260 do_retry: 261 /* IF the shell server process is not running yet, 262 THEN try to start it. */ 263 if (server_id == NULLPROCESS) 264 { 265 def_args[0] = find_shell (); 266 267 server_id = proc2_fopen (&server_pair, def_args); 268 if (server_id > 0) 269 server_setup (); 270 } 271 272 /* IF it is still not running, THEN return the nil string. */ 273 if (server_id <= 0) 274 { 275 fprintf (stderr, zNoServer, pz_cmd); 276 return xcalloc (1, 1); 277 } 278 279 /* Make sure the process will pay attention to us, send the 280 supplied command, and then have it output a special marker that 281 we can find. */ 282 fprintf (server_pair.pf_write, "cd %s\n%s\n\necho\necho %s\n", 283 p_cur_dir, pz_cmd, z_done); 284 fflush (server_pair.pf_write); 285 286 /* IF the server died and we received a SIGPIPE, 287 THEN return an empty string. */ 288 if (server_id == NULLPROCESS) 289 { 290 fprintf (stderr, zNoServer, pz_cmd); 291 return xcalloc (1, 1); 292 } 293 294 /* Now try to read back all the data. If we fail due to either a 295 sigpipe or sigalrm (timeout), we will return the nil string. */ 296 { 297 char *pz = load_data (server_pair.pf_read); 298 299 if (pz == (char *) NULL) 300 { 301 close_server (); 302 303 if (retry) 304 { 305 retry = BOOL_FALSE; 306 goto do_retry; 307 } 308 309 fprintf (stderr, "CLOSING SHELL SERVER - command failure:\n\t%s\n", 310 pz_cmd); 311 pz = xcalloc (1, 1); 312 } 313#ifdef DEBUG 314 fprintf( stderr, "run_shell command success: %s\n", pz ); 315#endif 316 return pz; 317 } 318} 319