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