xref: /illumos-gate/usr/src/lib/krb5/ss/listen.c (revision 6d084746)
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