1 | /* $Id: mrtest.c 3956 2010-01-05 20:56:56Z zacheiss $ |
---|
2 | * |
---|
3 | * Bare-bones Moira client |
---|
4 | * |
---|
5 | * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology |
---|
6 | * For copying and distribution information, please see the file |
---|
7 | * <mit-copyright.h>. |
---|
8 | */ |
---|
9 | |
---|
10 | #include <mit-copyright.h> |
---|
11 | #include <moira.h> |
---|
12 | |
---|
13 | #include <errno.h> |
---|
14 | #include <fcntl.h> |
---|
15 | #include <setjmp.h> |
---|
16 | #include <signal.h> |
---|
17 | #include <stdio.h> |
---|
18 | #include <stdlib.h> |
---|
19 | #include <string.h> |
---|
20 | #ifdef HAVE_UNISTD_H |
---|
21 | #include <unistd.h> |
---|
22 | #endif |
---|
23 | |
---|
24 | #ifdef HAVE_GETOPT_H |
---|
25 | #include <getopt.h> |
---|
26 | #endif |
---|
27 | |
---|
28 | #ifdef _WIN32 |
---|
29 | #include <windows.h> |
---|
30 | #include <io.h> |
---|
31 | #define dup _dup |
---|
32 | #define dup2 _dup2 |
---|
33 | #define isatty _isatty |
---|
34 | #define close _close |
---|
35 | #define open _open |
---|
36 | #define sigjmp_buf jmp_buf |
---|
37 | #define siglongjmp longjmp |
---|
38 | #define sigsetjmp(env, save) setjmp(env) |
---|
39 | #endif /* _WIN32 */ |
---|
40 | |
---|
41 | #ifdef HAVE_READLINE |
---|
42 | #include "readline/readline.h" |
---|
43 | #include "readline/history.h" |
---|
44 | #endif |
---|
45 | |
---|
46 | RCSID("$HeadURL: svn+ssh://svn.mit.edu/moira/trunk/moira/clients/mrtest/mrtest.c $ $Id: mrtest.c 3956 2010-01-05 20:56:56Z zacheiss $"); |
---|
47 | |
---|
48 | int recursion = 0, quote_output = 0, interactive; |
---|
49 | int count, quit = 0, cancel = 0; |
---|
50 | char *whoami; |
---|
51 | |
---|
52 | sigjmp_buf jb; |
---|
53 | |
---|
54 | #define MAXARGS 20 |
---|
55 | |
---|
56 | void discard_input(int sig); |
---|
57 | char *mr_gets(char *prompt, char *buf, size_t len); |
---|
58 | void execute_line(char *cmdbuf); |
---|
59 | int parse(char *buf, char *argv[MAXARGS]); |
---|
60 | int print_reply(int argc, char **argv, void *help); |
---|
61 | void test_noop(void); |
---|
62 | void test_connect(int argc, char **argv); |
---|
63 | void test_disconnect(void); |
---|
64 | void test_host(void); |
---|
65 | void test_motd(void); |
---|
66 | void test_query(int argc, char **argv); |
---|
67 | void test_auth(void); |
---|
68 | void test_proxy(int argc, char **argv); |
---|
69 | void test_access(int argc, char **argv); |
---|
70 | void test_dcm(void); |
---|
71 | void test_script(int argc, char **argv); |
---|
72 | void test_list_requests(void); |
---|
73 | void test_version(int argc, char **argv); |
---|
74 | void test_krb5_auth(void); |
---|
75 | void set_signal_handler(int, void (*handler)(int)); |
---|
76 | void set_signal_blocking(int, int); |
---|
77 | |
---|
78 | int main(int argc, char **argv) |
---|
79 | { |
---|
80 | char cmdbuf[BUFSIZ]; |
---|
81 | int c; |
---|
82 | |
---|
83 | whoami = argv[0]; |
---|
84 | interactive = (isatty(0) && isatty(1)); |
---|
85 | |
---|
86 | while ((c = getopt(argc, argv, "q")) != -1) |
---|
87 | { |
---|
88 | switch (c) |
---|
89 | { |
---|
90 | case 'q': |
---|
91 | quote_output = 1; |
---|
92 | break; |
---|
93 | |
---|
94 | default: |
---|
95 | fprintf(stderr, "Usage: mrtest [-q]\n"); |
---|
96 | exit (1); |
---|
97 | } |
---|
98 | } |
---|
99 | |
---|
100 | #if defined(__APPLE__) && defined(__MACH__) |
---|
101 | add_error_table(&et_sms_error_table); |
---|
102 | add_error_table(&et_krb_error_table); |
---|
103 | #else |
---|
104 | initialize_sms_error_table(); |
---|
105 | initialize_krb_error_table(); |
---|
106 | #endif |
---|
107 | |
---|
108 | #ifdef HAVE_READLINE |
---|
109 | /* we don't want filename completion */ |
---|
110 | rl_bind_key('\t', rl_insert); |
---|
111 | #endif |
---|
112 | |
---|
113 | set_signal_handler(SIGINT, discard_input); |
---|
114 | sigsetjmp(jb, 1); |
---|
115 | |
---|
116 | while (!quit) |
---|
117 | { |
---|
118 | if (!mr_gets("moira: ", cmdbuf, BUFSIZ)) |
---|
119 | break; |
---|
120 | execute_line(cmdbuf); |
---|
121 | } |
---|
122 | mr_disconnect(); |
---|
123 | exit(0); |
---|
124 | } |
---|
125 | |
---|
126 | void discard_input(int sig) |
---|
127 | { |
---|
128 | putc('\n', stdout); |
---|
129 | |
---|
130 | /* if we're inside a script, we have to clean up file descriptors, |
---|
131 | so don't jump out yet */ |
---|
132 | if (recursion) |
---|
133 | cancel = 1; |
---|
134 | else |
---|
135 | siglongjmp(jb, 1); |
---|
136 | } |
---|
137 | |
---|
138 | char *mr_gets(char *prompt, char *buf, size_t len) |
---|
139 | { |
---|
140 | char *in; |
---|
141 | #ifdef HAVE_READLINE |
---|
142 | if (interactive) |
---|
143 | { |
---|
144 | in = readline(prompt); |
---|
145 | |
---|
146 | if (!in) |
---|
147 | return NULL; |
---|
148 | if (*in) |
---|
149 | add_history(in); |
---|
150 | strncpy(buf, in, len - 1); |
---|
151 | buf[len] = 0; |
---|
152 | free(in); |
---|
153 | |
---|
154 | return buf; |
---|
155 | } |
---|
156 | #endif |
---|
157 | printf("%s", prompt); |
---|
158 | fflush(stdout); |
---|
159 | in = fgets(buf, len, stdin); |
---|
160 | if (!in) |
---|
161 | return in; |
---|
162 | if (strchr(buf, '\n')) |
---|
163 | *(strchr(buf, '\n')) = '\0'; |
---|
164 | return buf; |
---|
165 | } |
---|
166 | |
---|
167 | void execute_line(char *cmdbuf) |
---|
168 | { |
---|
169 | int argc; |
---|
170 | char *argv[MAXARGS]; |
---|
171 | |
---|
172 | argc = parse(cmdbuf, argv); |
---|
173 | if (argc == 0) |
---|
174 | return; |
---|
175 | if (!strcmp(argv[0], "noop")) |
---|
176 | test_noop(); |
---|
177 | else if (!strcmp(argv[0], "connect") || !strcmp(argv[0], "c")) |
---|
178 | test_connect(argc, argv); |
---|
179 | else if (!strcmp(argv[0], "disconnect") || !strcmp(argv[0], "d")) |
---|
180 | test_disconnect(); |
---|
181 | else if (!strcmp(argv[0], "host")) |
---|
182 | test_host(); |
---|
183 | else if (!strcmp(argv[0], "motd") || !strcmp(argv[0], "m")) |
---|
184 | test_motd(); |
---|
185 | else if (!strcmp(argv[0], "query") || !strcmp(argv[0], "qy")) |
---|
186 | test_query(argc, argv); |
---|
187 | else if (!strcmp(argv[0], "auth") || !strcmp(argv[0], "a")) |
---|
188 | test_krb5_auth(); |
---|
189 | else if (!strcmp(argv[0], "proxy") || !strcmp(argv[0], "p")) |
---|
190 | test_proxy(argc, argv); |
---|
191 | else if (!strcmp(argv[0], "access")) |
---|
192 | test_access(argc, argv); |
---|
193 | else if (!strcmp(argv[0], "dcm")) |
---|
194 | test_dcm(); |
---|
195 | else if (!strcmp(argv[0], "script") || !strcmp(argv[0], "s")) |
---|
196 | test_script(argc, argv); |
---|
197 | else if (!strcmp(argv[0], "list_requests") || |
---|
198 | !strcmp(argv[0], "lr") || !strcmp(argv[0], "?")) |
---|
199 | test_list_requests(); |
---|
200 | else if (!strcmp(argv[0], "quit") || !strcmp(argv[0], "Q")) |
---|
201 | quit = 1; |
---|
202 | else if (!strcmp(argv[0], "version") || !strcmp(argv[0], "v")) |
---|
203 | test_version(argc, argv); |
---|
204 | else if (!strcmp(argv[0], "krb4_auth") || !strcmp(argv[0], "4")) |
---|
205 | test_auth(); |
---|
206 | else |
---|
207 | { |
---|
208 | fprintf(stderr, "moira: Unknown request \"%s\". " |
---|
209 | "Type \"?\" for a request list.\n", argv[0]); |
---|
210 | } |
---|
211 | } |
---|
212 | |
---|
213 | int parse(char *buf, char *argv[MAXARGS]) |
---|
214 | { |
---|
215 | char *p; |
---|
216 | int argc, num; |
---|
217 | |
---|
218 | if (!*buf) |
---|
219 | return 0; |
---|
220 | |
---|
221 | for (p = buf, argc = 0, argv[0] = buf; *p && *p != '\n'; p++) |
---|
222 | { |
---|
223 | if (*p == '"') |
---|
224 | { |
---|
225 | char *d = p++; |
---|
226 | /* skip to close-quote, copying back over open-quote */ |
---|
227 | while (*p != '"') |
---|
228 | { |
---|
229 | if (!*p || *p == '\n') |
---|
230 | { |
---|
231 | fprintf(stderr, |
---|
232 | "moira: Unbalanced quotes in command line\n"); |
---|
233 | return 0; |
---|
234 | } |
---|
235 | /* deal with \### or \\ */ |
---|
236 | if (*p == '\\') |
---|
237 | { |
---|
238 | if (*++p != '"' && (*p < '0' || *p > '9') && (*p != '\\')) |
---|
239 | { |
---|
240 | fprintf(stderr, "moira: Bad use of \\\n"); |
---|
241 | return 0; |
---|
242 | } |
---|
243 | else if (*p >= '0' && *p <= '9') |
---|
244 | { |
---|
245 | num = (*p - '0') * 64 + (*++p - '0') * 8 + (*++p - '0'); |
---|
246 | *p = num; |
---|
247 | } |
---|
248 | } |
---|
249 | *d++ = *p++; |
---|
250 | } |
---|
251 | if (p == d + 1) |
---|
252 | { |
---|
253 | *d = '\0'; |
---|
254 | p++; |
---|
255 | } |
---|
256 | else |
---|
257 | { |
---|
258 | while (p >= d) |
---|
259 | *p-- = ' '; |
---|
260 | } |
---|
261 | } |
---|
262 | if (*p == ' ' || *p == '\t') |
---|
263 | { |
---|
264 | /* skip whitespace */ |
---|
265 | for (*p++ = '\0'; *p == ' ' || *p == '\t'; p++) |
---|
266 | ; |
---|
267 | if (*p && *p != '\n') { |
---|
268 | if (++argc >= MAXARGS) { |
---|
269 | fprintf(stderr, |
---|
270 | "moira: Too many command line arguments\n"); |
---|
271 | return 0; |
---|
272 | } |
---|
273 | argv[argc] = p--; |
---|
274 | } |
---|
275 | } |
---|
276 | } |
---|
277 | if (*p == '\n') |
---|
278 | *p = '\0'; |
---|
279 | return argc + 1; |
---|
280 | } |
---|
281 | |
---|
282 | void test_noop(void) |
---|
283 | { |
---|
284 | int status = mr_noop(); |
---|
285 | if (status) |
---|
286 | com_err("moira (noop)", status, ""); |
---|
287 | } |
---|
288 | |
---|
289 | void test_connect(int argc, char *argv[]) |
---|
290 | { |
---|
291 | char *server = ""; |
---|
292 | int status; |
---|
293 | |
---|
294 | if (argc > 1) |
---|
295 | server = argv[1]; |
---|
296 | status = mr_connect(server); |
---|
297 | if (status) |
---|
298 | com_err("moira (connect)", status, ""); |
---|
299 | mr_version(-1); |
---|
300 | } |
---|
301 | |
---|
302 | void test_disconnect(void) |
---|
303 | { |
---|
304 | int status = mr_disconnect(); |
---|
305 | if (status) |
---|
306 | com_err("moira (disconnect)", status, ""); |
---|
307 | } |
---|
308 | |
---|
309 | void test_host(void) |
---|
310 | { |
---|
311 | char host[BUFSIZ]; |
---|
312 | int status; |
---|
313 | |
---|
314 | memset(host, 0, sizeof(host)); |
---|
315 | |
---|
316 | if ((status = mr_host(host, sizeof(host) - 1))) |
---|
317 | com_err("moira (host)", status, ""); |
---|
318 | else |
---|
319 | printf("You are connected to host %s\n", host); |
---|
320 | } |
---|
321 | |
---|
322 | void test_auth(void) |
---|
323 | { |
---|
324 | int status; |
---|
325 | |
---|
326 | status = mr_auth("mrtest"); |
---|
327 | if (status) |
---|
328 | com_err("moira (auth)", status, ""); |
---|
329 | } |
---|
330 | |
---|
331 | void test_krb5_auth(void) |
---|
332 | { |
---|
333 | int status; |
---|
334 | |
---|
335 | status = mr_krb5_auth("mrtest"); |
---|
336 | if (status) |
---|
337 | com_err("moira (krb5_auth)", status, ""); |
---|
338 | } |
---|
339 | |
---|
340 | void test_proxy(int argc, char *argv[]) |
---|
341 | { |
---|
342 | int status; |
---|
343 | |
---|
344 | if (argc != 3) |
---|
345 | { |
---|
346 | com_err("moira (proxy)", 0, "Usage: proxy principal authtype"); |
---|
347 | return; |
---|
348 | } |
---|
349 | status = mr_proxy(argv[1], argv[2]); |
---|
350 | if (status) |
---|
351 | com_err("moira (proxy)", status, ""); |
---|
352 | } |
---|
353 | |
---|
354 | void test_script(int argc, char *argv[]) |
---|
355 | { |
---|
356 | FILE *inp; |
---|
357 | char input[BUFSIZ], *cp; |
---|
358 | int status, oldstdout, oldstderr; |
---|
359 | |
---|
360 | if (recursion > 8) |
---|
361 | { |
---|
362 | com_err("moira (script)", 0, "too many levels deep in script files\n"); |
---|
363 | return; |
---|
364 | } |
---|
365 | |
---|
366 | if (argc < 2) |
---|
367 | { |
---|
368 | com_err("moira (script)", 0, "Usage: script input_file [ output_file ]"); |
---|
369 | return; |
---|
370 | } |
---|
371 | |
---|
372 | inp = fopen(argv[1], "r"); |
---|
373 | if (!inp) |
---|
374 | { |
---|
375 | sprintf(input, "Cannot open input file %s", argv[1]); |
---|
376 | com_err("moira (script)", 0, input); |
---|
377 | return; |
---|
378 | } |
---|
379 | |
---|
380 | if (argc == 3) |
---|
381 | { |
---|
382 | printf("Redirecting output to %s\n", argv[2]); |
---|
383 | fflush(stdout); |
---|
384 | oldstdout = dup(1); |
---|
385 | close(1); |
---|
386 | status = open(argv[2], O_CREAT|O_WRONLY|O_APPEND, 0664); |
---|
387 | if (status != 1) |
---|
388 | { |
---|
389 | close(status); |
---|
390 | dup2(oldstdout, 1); |
---|
391 | argc = 2; |
---|
392 | sprintf(input, "Unable to redirect output to %s\n", argv[2]); |
---|
393 | com_err("moira (script)", errno, input); |
---|
394 | } |
---|
395 | else |
---|
396 | { |
---|
397 | fflush(stderr); |
---|
398 | oldstderr = dup(2); |
---|
399 | close(2); |
---|
400 | dup2(1, 2); |
---|
401 | } |
---|
402 | } |
---|
403 | |
---|
404 | recursion++; |
---|
405 | |
---|
406 | while (!cancel) |
---|
407 | { |
---|
408 | if (!fgets(input, BUFSIZ, inp)) |
---|
409 | break; |
---|
410 | if ((cp = strchr(input, '\n'))) |
---|
411 | *cp = '\0'; |
---|
412 | if (input[0] == 0) |
---|
413 | { |
---|
414 | printf("\n"); |
---|
415 | continue; |
---|
416 | } |
---|
417 | if (input[0] == '%') |
---|
418 | { |
---|
419 | for (cp = &input[1]; *cp && isspace(*cp); cp++) |
---|
420 | ; |
---|
421 | printf("Comment: %s\n", cp); |
---|
422 | continue; |
---|
423 | } |
---|
424 | printf("Executing: %s\n", input); |
---|
425 | execute_line(input); |
---|
426 | } |
---|
427 | |
---|
428 | recursion--; |
---|
429 | if (!recursion) |
---|
430 | cancel = 0; |
---|
431 | |
---|
432 | fclose(inp); |
---|
433 | if (argc == 3) |
---|
434 | { |
---|
435 | fflush(stdout); |
---|
436 | close(1); |
---|
437 | dup2(oldstdout, 1); |
---|
438 | close(oldstdout); |
---|
439 | fflush(stderr); |
---|
440 | close(2); |
---|
441 | dup2(oldstderr, 2); |
---|
442 | close(oldstderr); |
---|
443 | } |
---|
444 | } |
---|
445 | |
---|
446 | int print_reply(int argc, char **argv, void *help) |
---|
447 | { |
---|
448 | int i; |
---|
449 | for (i = 0; i < argc; i++) |
---|
450 | { |
---|
451 | if (i != 0) |
---|
452 | printf(", "); |
---|
453 | if (quote_output && !*(int *)help) |
---|
454 | { |
---|
455 | unsigned char *p; |
---|
456 | |
---|
457 | for (p = (unsigned char *)argv[i]; *p; p++) |
---|
458 | { |
---|
459 | if (isprint(*p) && *p != '\\' && *p != ',') |
---|
460 | putc(*p, stdout); |
---|
461 | else |
---|
462 | printf("\\%03o", *p); |
---|
463 | } |
---|
464 | } |
---|
465 | else |
---|
466 | printf("%s", argv[i]); |
---|
467 | } |
---|
468 | printf("\n"); |
---|
469 | count++; |
---|
470 | return MR_CONT; |
---|
471 | } |
---|
472 | |
---|
473 | void test_query(int argc, char **argv) |
---|
474 | { |
---|
475 | int status, help; |
---|
476 | |
---|
477 | if (argc < 2) |
---|
478 | { |
---|
479 | com_err("moira (query)", 0, "Usage: query handle [ args ... ]"); |
---|
480 | return; |
---|
481 | } |
---|
482 | help = !strcmp(argv[1], "_help"); |
---|
483 | |
---|
484 | count = 0; |
---|
485 | /* Don't allow ^C during the query: it will confuse libmoira's |
---|
486 | internal state. (Yay static variables) */ |
---|
487 | set_signal_blocking(SIGINT, 1); |
---|
488 | status = mr_query(argv[1], argc - 2, argv + 2, print_reply, &help); |
---|
489 | set_signal_blocking(SIGINT, 0); |
---|
490 | printf("%d tuple%s\n", count, ((count == 1) ? "" : "s")); |
---|
491 | if (status) |
---|
492 | com_err("moira (query)", status, ""); |
---|
493 | } |
---|
494 | |
---|
495 | void test_access(int argc, char **argv) |
---|
496 | { |
---|
497 | int status; |
---|
498 | if (argc < 2) |
---|
499 | { |
---|
500 | com_err("moira (access)", 0, "Usage: access handle [ args ... ]"); |
---|
501 | return; |
---|
502 | } |
---|
503 | status = mr_access(argv[1], argc - 2, argv + 2); |
---|
504 | if (status) |
---|
505 | com_err("moira (access)", status, ""); |
---|
506 | } |
---|
507 | |
---|
508 | void test_dcm() |
---|
509 | { |
---|
510 | int status; |
---|
511 | |
---|
512 | if ((status = mr_do_update())) |
---|
513 | com_err("moira (dcm)", status, " while triggering dcm"); |
---|
514 | } |
---|
515 | |
---|
516 | void test_motd() |
---|
517 | { |
---|
518 | int status; |
---|
519 | char *motd; |
---|
520 | |
---|
521 | if ((status = mr_motd(&motd))) |
---|
522 | com_err("moira (motd)", status, " while getting motd"); |
---|
523 | if (motd) |
---|
524 | printf("%s\n", motd); |
---|
525 | else |
---|
526 | printf("No message of the day.\n"); |
---|
527 | } |
---|
528 | |
---|
529 | void test_list_requests(void) |
---|
530 | { |
---|
531 | printf("Available moira requests:\n"); |
---|
532 | printf("\n"); |
---|
533 | printf("noop\t\t\tAsk Moira to do nothing\n"); |
---|
534 | printf("connect, c\t\tConnect to Moira server\n"); |
---|
535 | printf("disconnect, d\t\tDisconnect from server\n"); |
---|
536 | printf("host\t\t\tIdentify the server host\n"); |
---|
537 | printf("motd, m\t\t\tGet the Message of the Day\n"); |
---|
538 | printf("query, qy\t\tMake a query.\n"); |
---|
539 | printf("auth, a\t\t\tAuthenticate to Moira via krb5.\n"); |
---|
540 | printf("krb4_auth, 4\t\tAuthenticate to Moira via krb4.\n"); |
---|
541 | printf("proxy, p\t\tProxy authenticate to Moira.\n"); |
---|
542 | printf("access\t\t\tCheck access to a Moira query.\n"); |
---|
543 | printf("dcm\t\t\tTrigger the DCM\n"); |
---|
544 | printf("script, s\t\tRead commands from a script.\n"); |
---|
545 | printf("list_requests, lr, ?\tList available commands.\n"); |
---|
546 | printf("quit, Q\t\t\tLeave the subsystem.\n"); |
---|
547 | } |
---|
548 | |
---|
549 | void test_version(int argc, char **argv) |
---|
550 | { |
---|
551 | int status; |
---|
552 | |
---|
553 | if (argc != 2) |
---|
554 | { |
---|
555 | com_err("moira (version)", 0, "Usage: version versionnumber"); |
---|
556 | return; |
---|
557 | } |
---|
558 | status = mr_version(atoi(argv[1])); |
---|
559 | if (status) |
---|
560 | com_err("moira (version)", status, ""); |
---|
561 | } |
---|
562 | |
---|
563 | #ifdef HAVE_POSIX_SIGNALS |
---|
564 | |
---|
565 | void set_signal_handler(int sig, void (*handler)(int)) |
---|
566 | { |
---|
567 | struct sigaction action; |
---|
568 | |
---|
569 | sigemptyset(&action.sa_mask); |
---|
570 | action.sa_flags = 0; |
---|
571 | action.sa_handler = handler; |
---|
572 | sigaction(sig, &action, NULL); |
---|
573 | } |
---|
574 | |
---|
575 | void set_signal_blocking(int sig, int block) |
---|
576 | { |
---|
577 | sigset_t sigs; |
---|
578 | sigemptyset(&sigs); |
---|
579 | sigaddset(&sigs, sig); |
---|
580 | sigprocmask(block ? SIG_BLOCK : SIG_UNBLOCK, &sigs, NULL); |
---|
581 | } |
---|
582 | |
---|
583 | #else |
---|
584 | |
---|
585 | void set_signal_handler(int sig, void (*handler)(int)) |
---|
586 | { |
---|
587 | signal(sig, handler); |
---|
588 | } |
---|
589 | |
---|
590 | #ifdef _WIN32 |
---|
591 | BOOL WINAPI blocking_handler(DWORD dwCtrlType) |
---|
592 | { |
---|
593 | return(TRUE); |
---|
594 | } |
---|
595 | |
---|
596 | void set_signal_blocking(int sig, int block) |
---|
597 | { |
---|
598 | SetConsoleCtrlHandler(blocking_handler, block ? TRUE : FALSE); |
---|
599 | } |
---|
600 | #endif /* _WIN32 */ |
---|
601 | |
---|
602 | #endif /* HAVE_POSIX_SIGNALS */ |
---|