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
print_prompt()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
listen_int_handler(signo)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 */
get_commands(ss_commands * commands)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 */
CPL_MATCH_FN(cmdmatch)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
ss_listen(sci_idx)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
ss_abort_subsystem(sci_idx,code)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
ss_quit(argc,argv,sci_idx,infop)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