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 * 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, 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 49#include "fixlib.h" 50#include "server.h" 51 52STATIC volatile enum t_bool read_pipe_timeout; 53STATIC pid_t server_master_pid = NOPROCESS; 54 55tSCC* def_args[] = 56{ (char *) NULL, (char *) NULL }; 57STATIC t_pf_pair server_pair = 58{ (FILE *) NULL, (FILE *) NULL }; 59STATIC pid_t server_id = NULLPROCESS; 60/* 61 * Arbitrary text that should not be found in the shell output. 62 * It must be a single line and appear verbatim at the start of 63 * the terminating output line. 64 */ 65tSCC z_done[] = "ShElL-OuTpUt-HaS-bEeN-cOmPlEtEd"; 66tSCC* p_cur_dir = (char *) NULL; 67 68/* 69 * load_data 70 * 71 * Read data from a file pointer (a pipe to a process in this context) 72 * until we either get EOF or we get a marker line back. 73 * The read data are stored in a malloc-ed string that is truncated 74 * to size at the end. Input is assumed to be an ASCII string. 75 */ 76static char * 77load_data (FILE* fp) 78{ 79 char *pz_text; 80 size_t text_size; 81 char *pz_scan; 82 char z_line[1024]; 83 t_bool got_done = BOOL_FALSE; 84 85 text_size = sizeof (z_line) * 2; 86 pz_scan = pz_text = XNEWVEC (char, text_size); 87 88 for (;;) 89 { 90 size_t used_ct; 91 92 alarm (10); 93 read_pipe_timeout = BOOL_FALSE; 94 if (fgets (z_line, sizeof (z_line), fp) == (char *) NULL) 95 break; 96 97 if (strncmp (z_line, z_done, sizeof (z_done) - 1) == 0) 98 { 99 got_done = BOOL_TRUE; 100 break; 101 } 102 103 strcpy (pz_scan, z_line); 104 pz_scan += strlen (z_line); 105 used_ct = (size_t) (pz_scan - pz_text); 106 107 if (text_size - used_ct < sizeof (z_line)) 108 { 109 size_t off = (size_t) (pz_scan - pz_text); 110 111 text_size += 4096; 112 pz_text = XRESIZEVEC (char, pz_text, text_size); 113 pz_scan = pz_text + off; 114 } 115 } 116 117 alarm (0); 118 if (read_pipe_timeout || ! got_done) 119 { 120 free ((void *) pz_text); 121 return (char *) NULL; 122 } 123 124 while ((pz_scan > pz_text) && ISSPACE (pz_scan[-1])) 125 pz_scan--; 126 *pz_scan = NUL; 127 return XRESIZEVEC (char, pz_text, strlen (pz_text) + 1); 128} 129 130 131/* 132 * close_server 133 * 134 * Make certain the server process is dead, close the 135 * pipes to it and from it, finally NULL out the file pointers 136 */ 137void 138close_server (void) 139{ 140 if ( (server_id != NULLPROCESS) 141 && (server_master_pid == getpid ())) 142 { 143 kill ((pid_t) server_id, SIGKILL); 144 server_id = NULLPROCESS; 145 server_master_pid = NOPROCESS; 146 fclose (server_pair.pf_read); 147 fclose (server_pair.pf_write); 148 server_pair.pf_read = server_pair.pf_write = (FILE *) NULL; 149 } 150} 151 152/* 153 * sig_handler really only handles the timeout and pipe signals. 154 * This ensures that we do not wait forever on a request 155 * to our server, and also that if the server dies, we do not 156 * die from a sigpipe problem. 157 */ 158static void 159sig_handler (int signo ATTRIBUTE_UNUSED) 160{ 161#ifdef DEBUG 162 /* FIXME: this is illegal to do in a signal handler. */ 163 fprintf (stderr, 164 "fixincl ERROR: sig_handler: killed pid %ld due to %s\n", 165 (long) server_id, signo == SIGPIPE ? "SIGPIPE" : "SIGALRM"); 166#endif 167 close_server (); 168 read_pipe_timeout = BOOL_TRUE; 169} 170 171 172/* 173 * server_setup Establish the signal handler for PIPE and ALARM. 174 * Also establishes the current directory to give to the 175 * server process at the start of every server command. 176 */ 177static void 178server_setup (void) 179{ 180 static int atexit_done = 0; 181 char buff [MAXPATHLEN + 1]; 182 183 if (atexit_done++ == 0) 184 atexit (close_server); 185 else 186 fputs ("NOTE: server restarted\n", stderr); 187 188 server_master_pid = getpid (); 189 190 signal (SIGPIPE, sig_handler); 191 signal (SIGALRM, sig_handler); 192 193 fputs ("trap : 1\n", server_pair.pf_write); 194 fflush (server_pair.pf_write); 195 if (getcwd (buff, MAXPATHLEN + 1) == NULL) 196 buff[0] = 0; 197 p_cur_dir = xstrdup (buff); 198} 199 200/* 201 * find_shell 202 * 203 * Locate a shell suitable for use. For various reasons 204 * (like the use of "trap" in server_setup(), it must be a 205 * Bourne-like shell. 206 * 207 * Most of the time, /bin/sh is preferred, but sometimes 208 * it's quite broken (like on Ultrix). autoconf lets you 209 * override with $CONFIG_SHELL, so we do the same. 210 */ 211 212static const char * 213find_shell (void) 214{ 215 char * shell = getenv ("CONFIG_SHELL"); 216 if (shell) 217 return shell; 218 219 return "/bin/sh"; 220} 221 222 223/* 224 * run_shell 225 * 226 * Run a shell command on the server. The command string 227 * passed in is wrapped inside the sequence: 228 * 229 * cd <original directory> 230 * <command string> 231 * echo 232 * echo <end-of-command-marker> 233 * 234 * This ensures that all commands start at a known place in 235 * the directory structure, that any incomplete output lines 236 * are completed and that our special marker sequence appears on 237 * a line by itself. We have chosen a marker that is 238 * excessively unlikely to be reproduced in normal output: 239 * 240 * "ShElL-OuTpUt-HaS-bEeN-cOmPlEtEd" 241 */ 242char * 243run_shell (const char* pz_cmd) 244{ 245 tSCC zNoServer[] = "Server not running, cannot run:\n%s\n\n"; 246 t_bool retry = BOOL_TRUE; 247 248 do_retry: 249 /* IF the shell server process is not running yet, 250 THEN try to start it. */ 251 if (server_id == NULLPROCESS) 252 { 253 def_args[0] = find_shell (); 254 255 server_id = proc2_fopen (&server_pair, def_args); 256 if (server_id > 0) 257 server_setup (); 258 } 259 260 /* IF it is still not running, THEN return the nil string. */ 261 if (server_id <= 0) 262 { 263 fprintf (stderr, zNoServer, pz_cmd); 264 return XCNEW (char); 265 } 266 267 /* Make sure the process will pay attention to us, send the 268 supplied command, and then have it output a special marker that 269 we can find. */ 270 fprintf (server_pair.pf_write, "cd \"%s\"\n%s\n\necho\necho %s\n", 271 p_cur_dir, pz_cmd, z_done); 272 fflush (server_pair.pf_write); 273 274 /* IF the server died and we received a SIGPIPE, 275 THEN return an empty string. */ 276 if (server_id == NULLPROCESS) 277 { 278 fprintf (stderr, zNoServer, pz_cmd); 279 return XCNEW (char); 280 } 281 282 /* Now try to read back all the data. If we fail due to either a 283 sigpipe or sigalrm (timeout), we will return the nil string. */ 284 { 285 char *pz = load_data (server_pair.pf_read); 286 287 if (pz == (char *) NULL) 288 { 289 close_server (); 290 291 if (retry) 292 { 293 retry = BOOL_FALSE; 294 goto do_retry; 295 } 296 297 fprintf (stderr, "CLOSING SHELL SERVER - command failure:\n\t%s\n", 298 pz_cmd); 299 pz = XCNEW (char); 300 } 301#ifdef DEBUG 302 fprintf( stderr, "run_shell command success: %s\n", pz ); 303#endif 304 return pz; 305 } 306} 307