c7dbc7e01f0483f8a0841ced4d5c303858bbc047
[debian/amanda] / recover-src / amrecover.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998, 2000 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: amrecover.c,v 1.73 2006/07/25 18:27:57 martinea Exp $
28  *
29  * an interactive program for recovering backed-up files
30  */
31
32 #include "amanda.h"
33 #include "version.h"
34 #include "stream.h"
35 #include "amfeatures.h"
36 #include "amrecover.h"
37 #include "getfsent.h"
38 #include "dgram.h"
39 #include "util.h"
40 #include "conffile.h"
41 #include "protocol.h"
42 #include "event.h"
43 #include "security.h"
44
45 #define amrecover_debug(i, ...) do {    \
46         if ((i) <= debug_amrecover) {   \
47             dbprintf(__VA_ARGS__);      \
48         }                               \
49 } while (0)
50
51 extern int process_line(char *line);
52 int get_line(void);
53 int grab_reply(int show);
54 void sigint_handler(int signum);
55 int main(int argc, char **argv);
56
57 #define USAGE _("Usage: amrecover [[-C] <config>] [-s <index-server>] [-t <tape-server>] [-d <tape-device>] [-o <clientconfigoption>]*\n")
58
59 char *server_name = NULL;
60 int server_socket;
61 char *server_line = NULL;
62 char *dump_datestamp = NULL;            /* date we are restoring */
63 char *dump_hostname;                    /* which machine we are restoring */
64 char *disk_name = NULL;                 /* disk we are restoring */
65 dle_t *dump_dle = NULL;
66 char *mount_point = NULL;               /* where disk was mounted */
67 char *disk_path = NULL;                 /* path relative to mount point */
68 char dump_date[STR_SIZE];               /* date on which we are restoring */
69 int quit_prog;                          /* set when time to exit parser */
70 char *tape_server_name = NULL;
71 int tape_server_socket;
72 char *tape_device_name = NULL;
73 am_feature_t *our_features = NULL;
74 char *our_features_string = NULL;
75 am_feature_t *indexsrv_features = NULL;
76 am_feature_t *tapesrv_features = NULL;
77 static char *errstr = NULL;
78 char *authopt;
79 int amindexd_alive = 0;
80
81 static struct {
82     const char *name;
83     security_stream_t *fd;
84 } streams[] = {
85 #define MESGFD  0
86     { "MESG", NULL },
87 };
88 #define NSTREAMS        (int)(sizeof(streams) / sizeof(streams[0]))
89
90 static void amindexd_response(void *, pkt_t *, security_handle_t *);
91 void stop_amindexd(void);
92 char *amindexd_client_get_security_conf(char *, void *);
93
94 static char* mesg_buffer = NULL;
95 /* gets a "line" from server and put in server_line */
96 /* server_line is terminated with \0, \r\n is striped */
97 /* returns -1 if error */
98
99 int
100 get_line(void)
101 {
102     ssize_t size;
103     char *newbuf, *s;
104     void *buf;
105
106     if (!mesg_buffer)
107         mesg_buffer = stralloc("");
108  
109     while (!strstr(mesg_buffer,"\r\n")) {
110         buf = NULL;
111         size = security_stream_read_sync(streams[MESGFD].fd, &buf);
112         if(size < 0) {
113             amrecover_debug(1, "amrecover: get_line size < 0 (%zd)\n", size);
114             return -1;
115         }
116         else if(size == 0) {
117             amrecover_debug(1, "amrecover: get_line size == 0 (%zd)\n", size);
118             return -1;
119         }
120         else if (buf == NULL) {
121             amrecover_debug(1, "amrecover: get_line buf == NULL\n");
122             return -1;
123         }
124         amrecover_debug(1, "amrecover: get_line size = %zd\n", size);
125         newbuf = alloc(strlen(mesg_buffer)+size+1);
126         strncpy(newbuf, mesg_buffer, (size_t)(strlen(mesg_buffer) + size));
127         memcpy(newbuf+strlen(mesg_buffer), buf, (size_t)size);
128         newbuf[strlen(mesg_buffer)+size] = '\0';
129         amfree(mesg_buffer);
130         mesg_buffer = newbuf;
131     }
132
133     s = strstr(mesg_buffer,"\r\n");
134     *s = '\0';
135     newbuf = stralloc(s+2);
136     server_line = newstralloc(server_line, mesg_buffer);
137     amfree(mesg_buffer);
138     mesg_buffer = newbuf;
139     amrecover_debug(1, "get: %s\n", mesg_buffer);
140     return 0;
141 }
142
143
144 /* get reply from server and print to screen */
145 /* handle multi-line reply */
146 /* return -1 if error */
147 /* return code returned by server always occupies first 3 bytes of global
148    variable server_line */
149 int
150 grab_reply(
151     int show)
152 {
153     do {
154         if (get_line() == -1) {
155             return -1;
156         }
157         if(show) puts(server_line);
158     } while (server_line[3] == '-');
159     if(show) fflush(stdout);
160
161     return 0;
162 }
163
164
165 /* get 1 line of reply */
166 /* returns -1 if error, 0 if last (or only) line, 1 if more to follow */
167 int
168 get_reply_line(void)
169 {
170     if (get_line() == -1)
171         return -1;
172     return server_line[3] == '-';
173 }
174
175
176 /* returns pointer to returned line */
177 char *
178 reply_line(void)
179 {
180     return server_line;
181 }
182
183
184
185 /* returns 0 if server returned an error code (ie code starting with 5)
186    and non-zero otherwise */
187 int
188 server_happy(void)
189 {
190     return server_line[0] != '5';
191 }
192
193
194 int
195 send_command(
196     char *      cmd)
197 {
198     /*
199      * NOTE: this routine is called from sigint_handler, so we must be
200      * **very** careful about what we do since there is no way to know
201      * our state at the time the interrupt happened.  For instance,
202      * do not use any stdio or malloc routines here.
203      */
204     char *buffer;
205
206     buffer = alloc(strlen(cmd)+3);
207     strncpy(buffer, cmd, strlen(cmd));
208     buffer[strlen(cmd)] = '\r';
209     buffer[strlen(cmd)+1] = '\n';
210     buffer[strlen(cmd)+2] = '\0';
211
212     if(security_stream_write(streams[MESGFD].fd, buffer, strlen(buffer)) < 0) {
213         return -1;
214     }
215     amfree(buffer);
216     return (0);
217 }
218
219
220 /* send a command to the server, get reply and print to screen */
221 int
222 converse(
223     char *      cmd)
224 {
225     if (send_command(cmd) == -1) return -1;
226     if (grab_reply(1) == -1) return -1;
227     return 0;
228 }
229
230
231 /* same as converse() but reply not echoed to stdout */
232 int
233 exchange(
234     char *      cmd)
235 {
236     if (send_command(cmd) == -1) return -1;
237     if (grab_reply(0) == -1) return -1;
238     return 0;
239 }
240
241
242 /* basic interrupt handler for when user presses ^C */
243 /* Bale out, letting server know before doing so */
244 void
245 sigint_handler(
246     int signum)
247 {
248     /*
249      * NOTE: we must be **very** careful about what we do here since there
250      * is no way to know our state at the time the interrupt happened.
251      * For instance, do not use any stdio routines here or in any called
252      * routines.  Also, use _exit() instead of exit() to make sure stdio
253      * buffer flushing is not attempted.
254      */
255     (void)signum;       /* Quiet unused parameter warning */
256
257     if (extract_restore_child_pid != -1)
258         (void)kill(extract_restore_child_pid, SIGKILL);
259     extract_restore_child_pid = -1;
260
261     if(amindexd_alive) 
262         (void)send_command("QUIT");
263
264     _exit(1);
265 }
266
267
268 void
269 clean_pathname(
270     char *      s)
271 {
272     size_t length;
273     length = strlen(s);
274
275     /* remove "/" at end of path */
276     if(length>1 && s[length-1]=='/')
277         s[length-1]='\0';
278
279     /* change "/." to "/" */
280     if(strcmp(s,"/.")==0)
281         s[1]='\0';
282
283     /* remove "/." at end of path */
284     if(strcmp(&(s[length-2]),"/.")==0)
285         s[length-2]='\0';
286 }
287
288
289 void
290 quit(void)
291 {
292     quit_prog = 1;
293     (void)converse("QUIT");
294     stop_amindexd();
295 }
296
297 char *localhost = NULL;
298
299 #ifdef DEFAULT_TAPE_SERVER
300 # define DEFAULT_TAPE_SERVER_FAILOVER (DEFAULT_TAPE_SERVER)
301 #else
302 # define DEFAULT_TAPE_SERVER_FAILOVER (NULL)
303 #endif
304
305 int
306 main(
307     int         argc,
308     char **     argv)
309 {
310     int i;
311     time_t timer;
312     char *lineread = NULL;
313     struct sigaction act, oact;
314     extern char *optarg;
315     extern int optind;
316     char *line = NULL;
317     const security_driver_t *secdrv;
318     char *req = NULL;
319     int response_error;
320     struct tm *tm;
321     config_overwrites_t *cfg_ovr;
322
323     /*
324      * Configure program for internationalization:
325      *   1) Only set the message locale for now.
326      *   2) Set textdomain for all amanda related programs to "amanda"
327      *      We don't want to be forced to support dozens of message catalogs.
328      */  
329     setlocale(LC_MESSAGES, "C");
330     textdomain("amanda"); 
331
332     safe_fd(-1, 0);
333
334     set_pname("amrecover");
335
336     /* Don't die when child closes pipe */
337     signal(SIGPIPE, SIG_IGN);
338
339     dbopen(DBG_SUBDIR_CLIENT);
340
341     localhost = alloc(MAX_HOSTNAME_LENGTH+1);
342     if (gethostname(localhost, MAX_HOSTNAME_LENGTH) != 0) {
343         error(_("cannot determine local host name\n"));
344         /*NOTREACHED*/
345     }
346     localhost[MAX_HOSTNAME_LENGTH] = '\0';
347
348     /* load the base client configuration */
349     config_init(CONFIG_INIT_CLIENT, NULL);
350
351     if (config_errors(NULL) >= CFGERR_WARNINGS) {
352         config_print_errors();
353         if (config_errors(NULL) >= CFGERR_ERRORS) {
354             g_critical(_("errors processing config file"));
355         }
356     }
357
358     /* treat amrecover-specific command line options as the equivalent
359      * -o command-line options to set configuration values */
360     cfg_ovr = new_config_overwrites(argc/2);
361
362     /* If the first argument is not an option flag, then we assume
363      * it is a configuration name to match the syntax of the other
364      * Amanda utilities. */
365     if (argc > 1 && argv[1][0] != '-') {
366         add_config_overwrite(cfg_ovr, "conf", argv[1]);
367
368         /* remove that option from the command line */
369         argv[1] = argv[0];
370         argv++; argc--;
371     }
372
373     /* now parse regular command-line '-' options */
374     while ((i = getopt(argc, argv, "o:C:s:t:d:U")) != EOF) {
375         switch (i) {
376             case 'C':
377                 add_config_overwrite(cfg_ovr, "conf", optarg);
378                 break;
379
380             case 's':
381                 add_config_overwrite(cfg_ovr, "index_server", optarg);
382                 break;
383
384             case 't':
385                 add_config_overwrite(cfg_ovr, "tape_server", optarg);
386                 break;
387
388             case 'd':
389                 add_config_overwrite(cfg_ovr, "tapedev", optarg);
390                 break;
391
392             case 'o':
393                 add_config_overwrite_opt(cfg_ovr, optarg);
394                 break;
395
396             case 'U':
397             case '?':
398                 (void)g_printf(USAGE);
399                 return 0;
400         }
401     }
402     if (optind != argc) {
403         (void)g_fprintf(stderr, USAGE);
404         exit(1);
405     }
406
407     /* and now try to load the configuration named in that file */
408     apply_config_overwrites(cfg_ovr);
409     config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
410                 getconf_str(CNF_CONF));
411
412     check_running_as(RUNNING_AS_ROOT);
413
414     dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
415
416     our_features = am_init_feature_set();
417     our_features_string = am_feature_to_string(our_features);
418
419     server_name = NULL;
420     if (getconf_seen(CNF_INDEX_SERVER) == -2) { /* command line argument */
421         server_name = getconf_str(CNF_INDEX_SERVER);
422     }
423     if (!server_name) {
424         server_name = getenv("AMANDA_SERVER");
425         if (server_name) {
426             g_printf(_("Using index server from environment AMANDA_SERVER (%s)\n"), server_name);
427         }
428     }
429     if (!server_name) {
430         server_name = getconf_str(CNF_INDEX_SERVER);
431     }
432     if (!server_name) {
433         error(_("No index server set"));
434         /*NOTREACHED*/
435     }
436     server_name = stralloc(server_name);
437
438     tape_server_name = NULL;
439     if (getconf_seen(CNF_TAPE_SERVER) == -2) { /* command line argument */
440         tape_server_name = getconf_str(CNF_TAPE_SERVER);
441     }
442     if (!tape_server_name) {
443         tape_server_name = getenv("AMANDA_TAPE_SERVER");
444         if (!tape_server_name) {
445             tape_server_name = getenv("AMANDA_TAPESERVER");
446             if (tape_server_name) {
447                 g_printf(_("Using tape server from environment AMANDA_TAPESERVER (%s)\n"), tape_server_name);
448             }
449         } else {
450             g_printf(_("Using tape server from environment AMANDA_TAPE_SERVER (%s)\n"), tape_server_name);
451         }
452     }
453     if (!tape_server_name) {
454         tape_server_name = getconf_str(CNF_TAPE_SERVER);
455     }
456     if (!tape_server_name) {
457         error(_("No tape server set"));
458         /*NOTREACHED*/
459     }
460     tape_server_name = stralloc(tape_server_name);
461
462     amfree(tape_device_name);
463     tape_device_name = getconf_str(CNF_TAPEDEV);
464     if (!tape_device_name ||
465         strlen(tape_device_name) == 0 ||
466         !getconf_seen(CNF_TAPEDEV)) {
467         tape_device_name = NULL;
468     } else {
469         tape_device_name = stralloc(tape_device_name);
470     }
471
472     authopt = stralloc(getconf_str(CNF_AUTH));
473
474
475     amfree(disk_name);
476     amfree(mount_point);
477     amfree(disk_path);
478     dump_date[0] = '\0';
479
480     /* Don't die when child closes pipe */
481     signal(SIGPIPE, SIG_IGN);
482
483     /* set up signal handler */
484     act.sa_handler = sigint_handler;
485     sigemptyset(&act.sa_mask);
486     act.sa_flags = 0;
487     if (sigaction(SIGINT, &act, &oact) != 0) {
488         error(_("error setting signal handler: %s"), strerror(errno));
489         /*NOTREACHED*/
490     }
491
492     protocol_init();
493
494     /* We assume that amindexd support fe_amindexd_options_features */
495     /*                             and fe_amindexd_options_auth     */
496     /* We should send a noop to really know                         */
497     req = vstrallocf("SERVICE amindexd\n"
498                     "OPTIONS features=%s;auth=%s;\n",
499                     our_features_string, authopt);
500
501     secdrv = security_getdriver(authopt);
502     if (secdrv == NULL) {
503         error(_("no '%s' security driver available for host '%s'"),
504             authopt, server_name);
505         /*NOTREACHED*/
506     }
507
508     protocol_sendreq(server_name, secdrv, generic_client_get_security_conf,
509                      req, STARTUP_TIMEOUT, amindexd_response, &response_error);
510
511     amfree(req);
512     protocol_run();
513
514     g_printf(_("AMRECOVER Version %s. Contacting server on %s ...\n"),
515            version(), server_name);
516
517     if(response_error != 0) {
518         g_fprintf(stderr,"%s\n",errstr);
519         exit(1);
520     }
521
522     /* get server's banner */
523     if (grab_reply(1) == -1) {
524         aclose(server_socket);
525         exit(1);
526     }
527     if (!server_happy()) {
528         dbclose();
529         aclose(server_socket);
530         exit(1);
531     }
532
533     /* try to get the features from the server */
534     {
535         char *their_feature_string = NULL;
536
537         indexsrv_features = NULL;
538
539         line = vstrallocf("FEATURES %s", our_features_string);
540         if(exchange(line) == 0) {
541             their_feature_string = stralloc(server_line+13);
542             indexsrv_features = am_string_to_feature(their_feature_string);
543             if (!indexsrv_features)
544                 g_printf(_("Bad feature string from server: %s"), their_feature_string);
545         }
546         if (!indexsrv_features)
547             indexsrv_features = am_set_default_feature_set();
548
549         amfree(their_feature_string);
550         amfree(line);
551     }
552
553     /* set the date of extraction to be today */
554     (void)time(&timer);
555     tm = localtime(&timer);
556     if (tm) 
557         strftime(dump_date, sizeof(dump_date), "%Y-%m-%d", tm);
558     else
559         error(_("BAD DATE"));
560
561     g_printf(_("Setting restore date to today (%s)\n"), dump_date);
562     line = vstrallocf("DATE %s", dump_date);
563     if (converse(line) == -1) {
564         aclose(server_socket);
565         exit(1);
566     }
567     amfree(line);
568
569     line = vstrallocf("SCNF %s", get_config_name());
570     if (converse(line) == -1) {
571         aclose(server_socket);
572         exit(1);
573     }
574     amfree(line);
575
576     if (server_happy()) {
577         /* set host we are restoring to this host by default */
578         amfree(dump_hostname);
579         set_host(localhost);
580         if (dump_hostname)
581             g_printf(_("Use the setdisk command to choose dump disk to recover\n"));
582         else
583             g_printf(_("Use the sethost command to choose a host to recover\n"));
584
585     }
586
587     quit_prog = 0;
588     do {
589         if ((lineread = readline("amrecover> ")) == NULL) {
590             clearerr(stdin);
591             putchar('\n');
592             break;
593         }
594         if (lineread[0] != '\0') 
595         {
596             add_history(lineread);
597             dbprintf(_("user command: '%s'\n"), lineread);
598             process_line(lineread);     /* act on line's content */
599         }
600         amfree(lineread);
601     } while (!quit_prog);
602
603     dbclose();
604
605     aclose(server_socket);
606     return 0;
607 }
608
609 static void
610 amindexd_response(
611     void *datap,
612     pkt_t *pkt,
613     security_handle_t *sech)
614 {
615     int ports[NSTREAMS], *response_error = datap, i;
616     char *p;
617     char *tok;
618     char *extra = NULL;
619
620     assert(response_error != NULL);
621     assert(sech != NULL);
622
623     if (pkt == NULL) {
624         errstr = newvstrallocf(errstr, _("[request failed: %s]"),
625                              security_geterror(sech));
626         *response_error = 1;
627         return;
628     }
629
630     if (pkt->type == P_NAK) {
631 #if defined(PACKET_DEBUG)
632         dbprintf(_("got nak response:\n----\n%s\n----\n\n"), pkt->body);
633 #endif
634
635         tok = strtok(pkt->body, " ");
636         if (tok == NULL || strcmp(tok, "ERROR") != 0)
637             goto bad_nak;
638
639         tok = strtok(NULL, "\n");
640         if (tok != NULL) {
641             errstr = newvstrallocf(errstr, "NAK: %s", tok);
642             *response_error = 1;
643         } else {
644 bad_nak:
645             errstr = newvstrallocf(errstr, _("request NAK"));
646             *response_error = 2;
647         }
648         return;
649     }
650
651     if (pkt->type != P_REP) {
652         errstr = newvstrallocf(errstr, _("received strange packet type %s: %s"),
653                               pkt_type2str(pkt->type), pkt->body);
654         *response_error = 1;
655         return;
656     }
657
658 #if defined(PACKET_DEBUG)
659     g_fprintf(stderr, _("got response:\n----\n%s\n----\n\n"), pkt->body);
660 #endif
661
662     for(i = 0; i < NSTREAMS; i++) {
663         ports[i] = -1;
664         streams[i].fd = NULL;
665     }
666
667     p = pkt->body;
668     while((tok = strtok(p, " \n")) != NULL) {
669         p = NULL;
670
671         /*
672          * Error response packets have "ERROR" followed by the error message
673          * followed by a newline.
674          */
675         if (strcmp(tok, "ERROR") == 0) {
676             tok = strtok(NULL, "\n");
677             if (tok == NULL) {
678                 errstr = newvstrallocf(errstr, _("[bogus error packet]"));
679             } else {
680                 errstr = newvstrallocf(errstr, "%s", tok);
681             }
682             *response_error = 2;
683             return;
684         }
685
686
687         /*
688          * Regular packets have CONNECT followed by three streams
689          */
690         if (strcmp(tok, "CONNECT") == 0) {
691
692             /*
693              * Parse the three stream specifiers out of the packet.
694              */
695             for (i = 0; i < NSTREAMS; i++) {
696                 tok = strtok(NULL, " ");
697                 if (tok == NULL || strcmp(tok, streams[i].name) != 0) {
698                     extra = vstrallocf(
699                            _("CONNECT token is \"%s\": expected \"%s\""),
700                            tok ? tok : _("(null)"), streams[i].name);
701                     goto parse_error;
702                 }
703                 tok = strtok(NULL, " \n");
704                 if (tok == NULL || sscanf(tok, "%d", &ports[i]) != 1) {
705                     extra = vstrallocf(
706                            _("CONNECT %s token is \"%s\" expected a port number"),
707                            streams[i].name, tok ? tok : _("(null)"));
708                     goto parse_error;
709                 }
710             }
711             continue;
712         }
713
714         /*
715          * OPTIONS [options string] '\n'
716          */
717         if (strcmp(tok, "OPTIONS") == 0) {
718             tok = strtok(NULL, "\n");
719             if (tok == NULL) {
720                 extra = vstrallocf(_("OPTIONS token is missing"));
721                 goto parse_error;
722             }
723 #if 0
724             tok_end = tok + strlen(tok);
725             while((p = strchr(tok, ';')) != NULL) {
726                 *p++ = '\0';
727                 if(strncmp_const(tok, "features=") == 0) {
728                     tok += SIZEOF("features=") - 1;
729                     am_release_feature_set(their_features);
730                     if((their_features = am_string_to_feature(tok)) == NULL) {
731                         errstr = newvstrallocf(errstr,
732                                       _("OPTIONS: bad features value: %s"),
733                                       tok);
734                         goto parse_error;
735                     }
736                 }
737                 tok = p;
738             }
739 #endif
740             continue;
741         }
742 #if 0
743         extra = vstrallocf(_("next token is \"%s\": expected \"CONNECT\", \"ERROR\" or \"OPTIONS\""), tok ? tok : _("(null)"));
744         goto parse_error;
745 #endif
746     }
747
748     /*
749      * Connect the streams to their remote ports
750      */
751     for (i = 0; i < NSTREAMS; i++) {
752 /*@i@*/ if (ports[i] == -1)
753             continue;
754         streams[i].fd = security_stream_client(sech, ports[i]);
755         if (streams[i].fd == NULL) {
756             errstr = newvstrallocf(errstr,
757                         _("[could not connect %s stream: %s]"),
758                         streams[i].name, security_geterror(sech));
759             goto connect_error;
760         }
761     }
762     /*
763      * Authenticate the streams
764      */
765     for (i = 0; i < NSTREAMS; i++) {
766         if (streams[i].fd == NULL)
767             continue;
768         if (security_stream_auth(streams[i].fd) < 0) {
769             errstr = newvstrallocf(errstr,
770                 _("[could not authenticate %s stream: %s]"),
771                 streams[i].name, security_stream_geterror(streams[i].fd));
772             goto connect_error;
773         }
774     }
775
776     /*
777      * The MESGFD and DATAFD streams are mandatory.  If we didn't get
778      * them, complain.
779      */
780     if (streams[MESGFD].fd == NULL) {
781         errstr = newvstrallocf(errstr, _("[couldn't open MESG streams]"));
782         goto connect_error;
783     }
784
785     /* everything worked */
786     *response_error = 0;
787     amindexd_alive = 1;
788     return;
789
790 parse_error:
791     errstr = newvstrallocf(errstr,
792                           _("[parse of reply message failed: %s]"),
793                           extra ? extra : _("(no additional information)"));
794     amfree(extra);
795     *response_error = 2;
796     return;
797
798 connect_error:
799     stop_amindexd();
800     *response_error = 1;
801 }
802
803 /*
804  * This is called when everything needs to shut down so event_loop()
805  * will exit.
806  */
807 void
808 stop_amindexd(void)
809 {
810     int i;
811
812     amindexd_alive = 0;
813     for (i = 0; i < NSTREAMS; i++) {
814         if (streams[i].fd != NULL) {
815             security_stream_close(streams[i].fd);
816             streams[i].fd = NULL;
817         }
818     }
819 }
820
821 char *
822 amindexd_client_get_security_conf(
823     char *      string,
824     void *      arg)
825 {
826     (void)arg;  /* Quiet unused parameter warning */
827
828     if(!string || !*string)
829         return(NULL);
830
831     if(strcmp(string, "auth")==0) {
832         return(getconf_str(CNF_AUTH));
833     }
834     else if(strcmp(string, "ssh_keys")==0) {
835         return(getconf_str(CNF_SSH_KEYS));
836     }
837     return(NULL);
838 }