listen.c revision 8093:d2fc11f7c4e7
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
30static ss_data *current_info;
31static jmp_buf listen_jmpb;
32
33static 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
45static RETSIGTYPE listen_int_handler(signo)
46    int signo;
47{
48    putc('\n', stdout);
49    longjmp(listen_jmpb, 1);
50}
51/* Solaris Kerberos */
52typedef 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 */
65void 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 */
91CPL_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
131int 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;
301egress:
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
317void 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
326void 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