/* $Xorg: server.c,v 1.5 2000/08/17 19:54:01 cpqbld Exp $ */ /************************************************************************/ /* Copyright (c) 1993 Quarterdeck Office Systems */ /* */ /* Permission to use, copy, modify, distribute, and sell this software */ /* and software and its documentation for any purpose is hereby granted */ /* without fee, provided that the above copyright notice appear in all */ /* copies and that both that copyright notice and this permission */ /* notice appear in supporting documentation, and that the name */ /* Quarterdeck Office Systems, Inc. not be used in advertising or */ /* publicity pertaining to distribution of this software without */ /* specific, written prior permission. */ /* */ /* THIS SOFTWARE IS PROVIDED `AS-IS'. QUARTERDECK OFFICE SYSTEMS, */ /* INC., DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, */ /* INCLUDING WITHOUT LIMITATION ALL IMPLIED WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR */ /* NONINFRINGEMENT. IN NO EVENT SHALL QUARTERDECK OFFICE SYSTEMS, */ /* INC., BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING SPECIAL, */ /* INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA, OR */ /* PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS */ /* OF WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT */ /* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /************************************************************************/ /* $XFree86: xc/programs/rstart/server.c,v 1.5tsi Exp $ */ /* Extended rsh "helper" program */ #include #include #include #include #include #include #ifdef ODT1_DISPLAY_HACK #include #endif /* ODT1_DISPLAY_HACK */ #include "server.h" #define TRUE 1 #define FALSE 0 static void squish_out_escapes(char *s); static int get_a_line(FILE *f, int *pargc, char ***pargv); static char *Strlwr(char *s0); static void do_it(void); static void process(FILE *f, int is_the_real_thing); static void detach(void); static void putenv_with_prefix(char *prefix, char *name, char *value); struct key { char *name; void (*func)(int ac, char **av); }; static void key_cmd(int ac, char **av); static void key_exec(int ac, char **av); static void key_context(int ac, char **av); static void key_misc(int ac, char **av); static void key_generic_cmd(int ac, char **av); static void key_dir(int ac, char **av); static void key_detach(int ac, char **av); static void key_nodetach(int ac, char **av); static void key_posix_umask(int ac, char **av); static void key_internal_registries(int ac, char **av); static void key_internal_local_default(int ac, char **av); static void key_internal_global_contexts(int ac, char **av); static void key_internal_local_contexts(int ac, char **av); static void key_internal_global_commands(int ac, char **av); static void key_internal_local_commands(int ac, char **av); static void key_internal_variable_prefix(int ac, char **av); static void key_internal_print(int ac, char **av); static struct key keys[] = { { "cmd", key_cmd }, { "exec", key_exec }, { "context", key_context }, { "misc", key_misc }, { "generic-cmd", key_generic_cmd }, { "dir", key_dir }, { "detach", key_detach }, { "nodetach", key_nodetach }, { "posix-umask", key_posix_umask }, { "auth", key_auth }, { "internal-registries", key_internal_registries }, { "internal-local-default", key_internal_local_default }, { "internal-global-contexts", key_internal_global_contexts }, { "internal-local-contexts", key_internal_local_contexts }, { "internal-global-commands", key_internal_global_commands }, { "internal-local-commands", key_internal_local_commands }, { "internal-variable-prefix", key_internal_variable_prefix }, { "internal-print", key_internal_print }, { "internal-auth-program", key_internal_auth_program }, { "internal-auth-input", key_internal_auth_input }, { NULL, NULL } }; static char **parm_cmd = NULL; static char **parm_exec = NULL; static char **parm_generic_cmd = NULL; static char *parm_dir = NULL; static char *parm_context = NULL; static char **parm_internal_registries = NULL; static char *parm_internal_local_default = NULL; static char *parm_internal_global_contexts = NULL; static char *parm_internal_local_contexts = NULL; static char *parm_internal_global_commands = NULL; static char *parm_internal_local_commands = NULL; static char *parm_internal_variable_prefix = NULL; static int parm_detach = FALSE; static char *parm_global_default = DEFAULT_CONFIG; char myname[]=SERVERNAME; int main(int argc, char *argv[]) { FILE *f; if(argc == 3 && !strcmp(argv[1], "-c")) parm_global_default = argv[2]; setbuf(stdin, NULL); printf( "%s: Ready: version 1.0, May 02 1994 X11R6.3\n", myname); fflush(stdout); f = fopen(parm_global_default, "r"); if(f) process(f, FALSE); if(parm_internal_local_default) { /* We start in $HOME */ f = fopen(parm_internal_local_default, "r"); if(f) process(f, FALSE); } process(stdin, TRUE); do_it(); exit(0); } static void squish_out_escapes(char *s) { char *p; char *o; o = s; for(p = s; *p; p++) { if(*p == '\\') { if(p[1] >= '0' && p[1] <= '3' && p[2] >= '0' && p[2] <= '7' && p[3] >= '0' && p[3] <= '7') { *o++ = ((p[1]-'0') << 6) + ((p[2]-'0') << 3) + (p[3]-'0'); p += 3; } else { printf( "%s: Failure: Improper \\ escape\n",myname); exit(255); } } else *o++ = *p; } *o = '\0'; } static int get_a_line(FILE *f, int *pargc, char ***pargv) { char buf[2048]; char *p; int c; char **pa; int was_space; char *saved; while(1) { p = buf; while((c = getc(f)) != '\n') { switch(c) { case '\0': case '\r': /* Ignored, per spec */ break; case EOF: return FALSE; default: if (p >= (buf + sizeof(buf) - 2)) { printf("%s: Failure: line too long\n", myname); exit(255); } *p++ = c; break; } } *p = '\0'; saved = strdup(buf); if(!saved) nomem(); *pargc = 0; was_space = TRUE; for(p = saved; *p; p++) { if(was_space && !isspace(*p)) (*pargc)++; was_space = isspace(*p); } *pargv = (char **)malloc((*pargc+1)*sizeof(char *)); if(!*pargv) nomem(); pa = *pargv; was_space = TRUE; for(p = saved; *p; p++) { if(was_space && !isspace(*p)) *pa++ = p; was_space = isspace(*p); if(isspace(*p)) *p = '\0'; } *pa = NULL; if(*pargc > 0 && (*pargv)[0][0] == '#') { /* How embarrassing. All that work for a comment. */ free(saved); free(*pargv); continue; } break; } for(pa = *pargv; *pa; pa++) { squish_out_escapes(*pa); } return TRUE; } void nomem(void) { printf("%s: Failure: Out of memory\n",myname); exit(255); } static void key_internal_registries(int ac, char **av) { parm_internal_registries = av+1; } static void key_internal_variable_prefix(int ac, char **av) { if(ac != 2) { printf( "%s: Failure: Malformed INTERNAL-VARIABLE-PREFIX\n",myname); exit(255); } parm_internal_variable_prefix = av[1]; } static void key_internal_local_default(int ac, char **av) { if(ac != 2) { printf("%s: Failure: Malformed INTERNAL-LOCAL-DEFAULT\n",myname); exit(255); } parm_internal_local_default = av[1]; } static void key_internal_global_commands(int ac, char **av) { if(ac != 2) { printf("%s: Failure: Malformed INTERNAL-GLOBAL-COMMANDS\n",myname); exit(255); } parm_internal_global_commands = av[1]; } static void key_internal_local_commands(int ac, char **av) { if(ac != 2) { printf("%s: Failure: Malformed INTERNAL-LOCAL-COMMANDS\n",myname); exit(255); } parm_internal_local_commands = av[1]; } static void key_internal_global_contexts(int ac, char **av) { if(ac != 2) { printf("%s: Failure: Malformed INTERNAL-GLOBAL-CONTEXTS\n",myname); exit(255); } parm_internal_global_contexts = av[1]; } static void key_internal_local_contexts(int ac, char **av) { if(ac != 2) { printf("%s: Failure: Malformed INTERNAL-LOCAL-CONTEXTS\n",myname); exit(255); } parm_internal_local_contexts = av[1]; } static void key_cmd(int ac, char **av) { if(parm_cmd || parm_exec || parm_generic_cmd) { printf( "%s: Failure: more than one of CMD, EXEC, GENERIC-CMD\n",myname); exit(255); } parm_cmd = av; } static void key_exec(int ac, char **av) { if(parm_cmd || parm_exec || parm_generic_cmd) { printf( "%s: Failure: more than one of CMD, EXEC, GENERIC-CMD\n",myname); exit(255); } parm_exec = av; } static char * Strlwr(char *s0) { char *s; for(s = s0; *s; s++) { if(isupper(*s)) *s = tolower(*s); } return s0; } static void key_context(int ac, char **av) { char buf[1024]; int ok; FILE *f; if(ac != 2) { printf("%s: Failure: Malformed CONTEXT\n",myname); exit(255); } Strlwr(av[1]); parm_context = av[1]; ok = FALSE; if(parm_internal_global_contexts) { strcpy(buf, parm_internal_global_contexts); strcat(buf, "/"); strcat(buf, parm_context); if((f = fopen(buf, "r"))) { process(f, FALSE); ok = TRUE; } } if(parm_internal_local_contexts) { strcpy(buf, parm_internal_local_contexts); strcat(buf, "/"); strcat(buf, parm_context); if((f = fopen(buf, "r"))) { process(f, FALSE); ok = TRUE; } } if(!ok) { printf("%s: Error: Unknown context '%s'\n",myname, parm_context); exit(255); } } static void key_dir(int ac, char **av) { if(ac != 2) { printf("%s: Failure: malformed DIR line\n",myname); exit(255); } parm_dir = av[1]; } static void key_misc(int ac, char **av) { char **pp; if(ac != 3) { printf("%s: Failure: malformed MISC line\n",myname); exit(255); } Strlwr(av[1]); if(parm_internal_registries) { for(pp = parm_internal_registries; *pp; pp++) { if(!strcmp(av[1], *pp)) goto ok; } } printf("%s: Warning: registry %s not recognized\n",myname, av[1]); fflush(stdout); ok: ; #ifdef ODT1_DISPLAY_HACK /* The X apps in ODT version 1 don't know how to look up */ /* host names using DNS. Do it for them. */ if(!strcmp(av[1], "x") && !strncmp(av[2], "DISPLAY=", 8) && odt1_display_hack(av[2]+8)) return; #endif /* ODT1_DISPLAY_HACK */ putenv(av[2]); } #ifdef ODT1_DISPLAY_HACK odt1_display_hack(char *s) { char buf[80]; struct hostent *he; char *p; int ok; ok = FALSE; for(p = s; *p; p++) { if(*p == ':') { if(!ok) break; *p = '\0'; he = gethostbyname(s); *p = ':'; if(!he) break; sprintf(buf, "DISPLAY=%u.%u.%u.%u%s", he->h_addr_list[0][0] & 0xff, he->h_addr_list[0][1] & 0xff, he->h_addr_list[0][2] & 0xff, he->h_addr_list[0][3] & 0xff, p); s = strdup(buf); if(!s) nomem(); putenv(s); return TRUE; } if(!isdigit(*p) && *p != '.') ok = TRUE; } return FALSE; } #endif /* ODT1_DISPLAY_HACK */ static void key_generic_cmd(int ac, char **av) { if(parm_cmd || parm_exec || parm_generic_cmd) { printf( "%s: Failure: more than one of CMD, EXEC, GENERIC-CMD\n",myname); exit(255); } parm_generic_cmd = av; } static void do_it(void) { if(parm_dir) { if(chdir(parm_dir)) { printf("%s: Error: %s - %s\n",myname, parm_dir, strerror(errno)); exit(255); } } if(parm_internal_variable_prefix) { putenv_with_prefix(parm_internal_variable_prefix, "CONTEXT", parm_context); putenv_with_prefix(parm_internal_variable_prefix, "LOCAL_COMMANDS", parm_internal_local_commands); putenv_with_prefix(parm_internal_variable_prefix, "GLOBAL_COMMANDS", parm_internal_global_commands); putenv_with_prefix(parm_internal_variable_prefix, "LOCAL_CONTEXTS", parm_internal_local_contexts); putenv_with_prefix(parm_internal_variable_prefix, "GLOBAL_CONTEXTS", parm_internal_global_contexts); } do_auth(); if(parm_cmd) { char *shell; char *buf; int len; char **pa; if(!(shell = getenv("SHELL"))) shell = "/bin/sh"; len = 0; for(pa = parm_cmd+1; *pa; pa++) { len += strlen(*pa) + 1; } buf = malloc(len+1); if(!buf) nomem(); buf[0] = '\0'; for(pa = parm_cmd+1; *pa; pa++) { strcat(buf, " "); strcat(buf, *pa); } printf("%s: Success: about to exec %s -c %s\n",myname, shell, buf+1); fflush(stdout); if(parm_detach) detach(); execl(shell, shell, "-c", buf+1, (char *)NULL); printf("%s: Error: %s - %s\n",myname, shell, strerror(errno)); exit(255); } if(parm_exec) { printf("%s: Success: about to exec %s with args\n",myname, parm_exec[1]); fflush(stdout); if(parm_detach) detach(); execvp(parm_exec[1], parm_exec+2); printf("%s: Error: %s - %s\n",myname, parm_exec[0], strerror(errno)); exit(255); } if(parm_generic_cmd) { char buf[1024]; if(!parm_internal_local_commands && !parm_internal_global_commands) { printf( "%s: Failure: No generic command directory!\n",myname); exit(255); } printf("%s: Success: about to exec generic command\n",myname); fflush(stdout); if(parm_detach) detach(); /* Try context-specific generic commands first */ if(parm_context) { if(parm_internal_local_commands) { strcpy(buf, parm_internal_local_commands); strcat(buf, "/"); strcat(buf, parm_context); strcat(buf, "/"); strcat(buf, parm_generic_cmd[1]); execv(buf, parm_generic_cmd+1); } if(parm_internal_global_commands) { strcpy(buf, parm_internal_global_commands); strcat(buf, "/"); strcat(buf, parm_context); strcat(buf, "/"); strcat(buf, parm_generic_cmd[1]); execv(buf, parm_generic_cmd+1); } } /* Failing that, try non-context-specific */ if(parm_internal_local_commands) { strcpy(buf, parm_internal_local_commands); strcat(buf, "/"); strcat(buf, parm_generic_cmd[1]); execv(buf, parm_generic_cmd+1); } if(parm_internal_global_commands) { strcpy(buf, parm_internal_global_commands); strcat(buf, "/"); strcat(buf, parm_generic_cmd[1]); execv(buf, parm_generic_cmd+1); } printf("%s: Error: %s - %s\n",myname, buf, strerror(errno)); exit(255); } printf("%s: Failure: No CMD, EXEC, or GENERIC-CMD.\n",myname); exit(255); } static void process(FILE *f, int is_the_real_thing) { int line_argc; char **line_argv; struct key *pk; while(1) { if(!get_a_line(f, &line_argc, &line_argv)) { if(is_the_real_thing) { printf( "%s: Failure: No blank line after request\n",myname); exit(255); } return; } if(line_argc == 0) { if(is_the_real_thing) return; continue; } Strlwr(line_argv[0]); for(pk = keys; pk->name; pk++) { if(!strcmp(line_argv[0], pk->name)) { (*pk->func)(line_argc, line_argv); goto ok; } } printf("%s: Failure: %s not recognized\n",myname, line_argv[0]); exit(255); ok: ; } } static void key_internal_print(int ac, char **av) { printf("%s: Debug:",myname); while(*++av) printf(" %s", *av); printf("\n"); } static void key_detach(int ac, char **av) { parm_detach = TRUE; } static void key_nodetach(int ac, char **av) { parm_detach = FALSE; } static void detach(void) { /* I'm not exactly sure how you're supposed to handle stdio here */ switch(fork()) { case -1: printf("%s: Error: fork - %s\n",myname, strerror(errno)); exit(255); case 0: /* Child */ close(0); close(1); close(2); dup(dup(open("/dev/null", O_RDWR))); return; default: /* Parent */ _exit(0); } } static void putenv_with_prefix(char *prefix, char *name, char *value) { char *s; if(!value) return; s = malloc(strlen(prefix) + strlen(name) + strlen(value) + 3); if(!s) nomem(); sprintf(s, "%s_%s=%s", prefix, name, value); putenv(s); } static void key_posix_umask(int ac, char **av) { int i; char *s; if(ac != 2) { printf( "%s: Failure: Malformed POSIX-UMASK - wrong number of args\n",myname); exit(255); } i = strtol(av[1], &s, 8); if(*s || i < 0 || i > 0777) { printf( "%s: Failure: Malformed POSIX-UMASK - bad arg\n",myname); exit(255); } umask(i); } #ifdef NOPUTENV /* * define our own putenv() if the system doesn't have one. * putenv(s): place s (a string of the form "NAME=value") in * the environment; replacing any existing NAME. s is placed in * environment, so if you change s, the environment changes (like * putenv on a sun). Binding removed if you putenv something else * called NAME. */ int putenv(char *s) { char *v; int varlen, idx; extern char **environ; char **newenv; static int virgin = 1; /* true while "environ" is a virgin */ v = index(s, '='); if(v == 0) return 0; /* punt if it's not of the right form */ varlen = (v + 1) - s; for (idx = 0; environ[idx] != 0; idx++) { if (strncmp(environ[idx], s, varlen) == 0) { if(v[1] != 0) { /* true if there's a value */ environ[idx] = s; return 0; } else { do { environ[idx] = environ[idx+1]; } while(environ[++idx] != 0); return 0; } } } /* add to environment (unless no value; then just return) */ if(v[1] == 0) return 0; if(virgin) { register i; newenv = (char **) malloc((unsigned) ((idx + 2) * sizeof(char*))); if(newenv == 0) return -1; for(i = idx-1; i >= 0; --i) newenv[i] = environ[i]; virgin = 0; /* you're not a virgin anymore, sweety */ } else { newenv = (char **) realloc((char *) environ, (unsigned) ((idx + 2) * sizeof(char*))); if (newenv == 0) return -1; } environ = newenv; environ[idx] = s; environ[idx+1] = 0; return 0; } #endif /* NOPUTENV */