1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * Listener loop for subsystem library libss.a. 8 * 9 * util/ss/listen.c 10 * 11 * Copyright 1987, 1988 by MIT Student Information Processing Board 12 * 13 * For copyright information, see copyright.h. 14 */ 15 16 #include "copyright.h" 17 #include "ss_internal.h" 18 #include <stdio.h> 19 #include <setjmp.h> 20 #include <signal.h> 21 #include <termios.h> 22 #include <libintl.h> 23 #include <sys/param.h> 24 /* Solaris Kerberos */ 25 #include <libtecla.h> 26 27 #define MAX_LINE_LEN BUFSIZ 28 #define MAX_HIST_LEN 8192 29 30 static ss_data *current_info; 31 static jmp_buf listen_jmpb; 32 33 static RETSIGTYPE print_prompt() 34 { 35 struct termios termbuf; 36 37 if (tcgetattr(STDIN_FILENO, &termbuf) == 0) { 38 termbuf.c_lflag |= ICANON|ISIG|ECHO; 39 tcsetattr(STDIN_FILENO, TCSANOW, &termbuf); 40 } 41 (void) fputs(current_info->prompt, stdout); 42 (void) fflush(stdout); 43 } 44 45 static RETSIGTYPE listen_int_handler(signo) 46 int signo; 47 { 48 putc('\n', stdout); 49 longjmp(listen_jmpb, 1); 50 } 51 /* Solaris Kerberos */ 52 typedef struct _ss_commands { 53 int sci_idx; 54 const char **cmd; 55 unsigned int count; 56 } ss_commands; 57 58 /* 59 * Solaris Kerberos 60 * get_commands fills out a ss_commands structure with pointers 61 * to the top-level commands (char*) that a program supports. 62 * count reflects the number of commands cmd holds. Memory must 63 * be allocated by the caller. 64 */ 65 void get_commands(ss_commands *commands) { 66 const char * const *cmd; 67 ss_request_table **table; 68 ss_request_entry *request; 69 ss_data *info; 70 71 commands->count = 0; 72 73 info = ss_info(commands->sci_idx); 74 for (table = info->rqt_tables; *table; table++) { 75 for (request = (*table)->requests; 76 request->command_names != NULL; request++) { 77 for (cmd = request->command_names; 78 cmd != NULL && *cmd != NULL; cmd++) { 79 if (commands->cmd != NULL) 80 commands->cmd[commands->count] = *cmd; 81 commands->count++; 82 } 83 } 84 } 85 } 86 87 /* 88 * Solaris Kerberos 89 * Match function used by libtecla for tab-completion. 90 */ 91 CPL_MATCH_FN(cmdmatch) { 92 int argc, len, ws, i; 93 char **argv, *l; 94 ss_commands *commands = data; 95 int ret = 0; 96 97 /* Dup the line as ss_parse will modify the string */ 98 l = strdup(line); 99 if (l == NULL) 100 return (ret); 101 102 /* Tab-completion may happen in the middle of a line */ 103 if (word_end != strlen(l)) 104 l[word_end] = '\0'; 105 106 if (ss_parse(commands->sci_idx, l, &argc, &argv, 1)) { 107 free (l); 108 return (ret); 109 } 110 111 /* Don't bother if the arg count is not 1 or 0 */ 112 if (argc < 2) { 113 len = argc ? strlen(argv[0]) : 0; 114 ws = word_end - len; 115 116 for (i = 0; i < commands->count; i++) { 117 if (strncmp(commands->cmd[i], line + ws, len) == 0) { 118 ret = cpl_add_completion(cpl, line, ws, 119 word_end, commands->cmd[i] + len, "", " "); 120 if (ret) 121 break; 122 } 123 } 124 } 125 126 free(argv); 127 free(l); 128 return (ret); 129 } 130 131 int ss_listen (sci_idx) 132 int sci_idx; 133 { 134 register char *cp; 135 register ss_data *info; 136 char buffer[BUFSIZ]; 137 char *volatile end = buffer; 138 int code; 139 140 /* Solaris Kerberos */ 141 char *input; 142 GetLine *gl; 143 GlReturnStatus ret; 144 ss_commands commands; 145 146 jmp_buf old_jmpb; 147 ss_data *old_info = current_info; 148 #ifdef POSIX_SIGNALS 149 struct sigaction isig, csig, nsig, osig; 150 sigset_t nmask, omask; 151 #else 152 register RETSIGTYPE (*sig_cont)(); 153 RETSIGTYPE (*sig_int)(), (*old_sig_cont)(); 154 int mask; 155 #endif 156 157 current_info = info = ss_info(sci_idx); 158 info->abort = 0; 159 160 /* Solaris Kerberos */ 161 gl = new_GetLine(MAX_LINE_LEN, MAX_HIST_LEN); 162 if (gl == NULL) { 163 ss_error(sci_idx, 0, dgettext(TEXT_DOMAIN, 164 "new_GetLine() failed.\n")); 165 current_info = old_info; 166 return (SS_ET_TECLA_ERR); 167 } 168 169 commands.sci_idx = sci_idx; 170 commands.cmd = NULL; 171 172 /* Find out how many commands there are */ 173 get_commands(&commands); 174 175 /* Alloc space for them */ 176 commands.cmd = malloc(sizeof (char *) * commands.count); 177 if (commands.cmd == NULL) { 178 current_info = old_info; 179 gl = del_GetLine(gl); 180 return (ENOMEM); 181 } 182 183 /* Fill-in commands.cmd */ 184 get_commands(&commands); 185 186 if (gl_customize_completion(gl, &commands, cmdmatch) != 0 ) { 187 ss_error(sci_idx, 0, dgettext(TEXT_DOMAIN, 188 "failed to register completion function.\n")); 189 free(commands.cmd); 190 current_info = old_info; 191 gl = del_GetLine(gl); 192 return (SS_ET_TECLA_ERR); 193 } 194 195 #ifdef POSIX_SIGNALS 196 csig.sa_handler = (RETSIGTYPE (*)())0; 197 sigemptyset(&nmask); 198 sigaddset(&nmask, SIGINT); 199 sigprocmask(SIG_BLOCK, &nmask, &omask); 200 #else 201 sig_cont = (RETSIGTYPE (*)())0; 202 mask = sigblock(sigmask(SIGINT)); 203 #endif 204 205 memcpy(old_jmpb, listen_jmpb, sizeof(jmp_buf)); 206 207 #ifdef POSIX_SIGNALS 208 nsig.sa_handler = listen_int_handler; 209 sigemptyset(&nsig.sa_mask); 210 nsig.sa_flags = 0; 211 sigaction(SIGINT, &nsig, &isig); 212 #else 213 sig_int = signal(SIGINT, listen_int_handler); 214 #endif 215 216 setjmp(listen_jmpb); 217 218 #ifdef POSIX_SIGNALS 219 sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0); 220 #else 221 (void) sigsetmask(mask); 222 #endif 223 224 /* 225 * Solaris Kerberos: 226 * Let libtecla deal with SIGINT when it's doing its own processing 227 * otherwise the input line won't be cleared on SIGINT. 228 */ 229 if (gl_trap_signal(gl, SIGINT, GLS_DONT_FORWARD, GLS_ABORT, NULL)) { 230 ss_error(sci_idx, 0, dgettext(TEXT_DOMAIN, 231 "Failed to trap SIGINT.\n")); 232 code = SS_ET_TECLA_ERR; 233 goto egress; 234 } 235 236 while(!info->abort) { 237 print_prompt(); 238 *end = '\0'; 239 #ifdef POSIX_SIGNALS 240 nsig.sa_handler = listen_int_handler; /* fgets is not signal-safe */ 241 osig = csig; 242 sigaction(SIGCONT, &nsig, &csig); 243 if ((RETSIGTYPE (*)())csig.sa_handler==(RETSIGTYPE (*)())listen_int_handler) 244 csig = osig; 245 #else 246 old_sig_cont = sig_cont; 247 sig_cont = signal(SIGCONT, print_prompt); 248 if (sig_cont == print_prompt) 249 sig_cont = old_sig_cont; 250 #endif 251 252 /* Solaris Kerberos */ 253 input = gl_get_line(gl, info->prompt, NULL, -1); 254 ret = gl_return_status(gl); 255 256 switch (ret) { 257 case (GLR_SIGNAL): 258 gl_abandon_line(gl); 259 continue; 260 case (GLR_EOF): 261 info->abort = 1; 262 continue; 263 case (GLR_ERROR): 264 ss_error(sci_idx, 0, dgettext(TEXT_DOMAIN, 265 "Failed to read line: %s\n"), gl_error_message(gl, NULL, 0)); 266 info->abort = 1; 267 code = SS_ET_TECLA_ERR; 268 goto egress; 269 } 270 cp = strchr(input, '\n'); 271 if (cp) { 272 *cp = '\0'; 273 if (cp == input) 274 continue; 275 } 276 #ifdef POSIX_SIGNALS 277 sigaction(SIGCONT, &csig, (struct sigaction *)0); 278 #else 279 (void) signal(SIGCONT, sig_cont); 280 #endif 281 for (end = input; *end; end++) 282 ; 283 284 code = ss_execute_line (sci_idx, input); 285 if (code == SS_ET_COMMAND_NOT_FOUND) { 286 register char *c = input; 287 while (*c == ' ' || *c == '\t') 288 c++; 289 cp = strchr (c, ' '); 290 if (cp) 291 *cp = '\0'; 292 cp = strchr (c, '\t'); 293 if (cp) 294 *cp = '\0'; 295 ss_error (sci_idx, 0, dgettext(TEXT_DOMAIN, 296 "Unknown request \"%s\". Type \"?\" for a request list."), 297 c); 298 } 299 } 300 code = 0; 301 egress: 302 303 /* Solaris Kerberos */ 304 free(commands.cmd); 305 gl = del_GetLine(gl); 306 307 #ifdef POSIX_SIGNALS 308 sigaction(SIGINT, &isig, (struct sigaction *)0); 309 #else 310 (void) signal(SIGINT, sig_int); 311 #endif 312 memcpy(listen_jmpb, old_jmpb, sizeof(jmp_buf)); 313 current_info = old_info; 314 return code; 315 } 316 317 void ss_abort_subsystem(sci_idx, code) 318 int sci_idx; 319 int code; 320 { 321 ss_info(sci_idx)->abort = 1; 322 ss_info(sci_idx)->exit_status = code; 323 324 } 325 326 void ss_quit(argc, argv, sci_idx, infop) 327 int argc; 328 char const * const *argv; 329 int sci_idx; 330 pointer infop; 331 { 332 ss_abort_subsystem(sci_idx, 0); 333 } 334