Imported Upstream version 2.5.2p1
[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,x) do {       \
46         if ((i) <= debug_amrecover) {   \
47             dbprintf(x);                \
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 *config = NULL;
60 char *server_name = NULL;
61 int server_socket;
62 char *server_line = NULL;
63 char *dump_datestamp = NULL;            /* date we are restoring */
64 char *dump_hostname;                    /* which machine we are restoring */
65 char *disk_name = NULL;                 /* disk we are restoring */
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, ("%s: amrecover: get_line size < 0 (%zd)\n", debug_prefix_time(NULL), size));
114             return -1;
115         }
116         else if(size == 0) {
117             amrecover_debug(1, ("%s: amrecover: get_line size == 0 (%zd)\n", debug_prefix_time(NULL), size));
118             return -1;
119         }
120         else if (buf == NULL) {
121             amrecover_debug(1, ("%s: amrecover: get_line buf == NULL\n", debug_prefix_time(NULL)));
122             return -1;
123         }
124         amrecover_debug(1, ("%s: amrecover: get_line size = %zd\n", debug_prefix_time(NULL), 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     return 0;
140 }
141
142
143 /* get reply from server and print to screen */
144 /* handle multi-line reply */
145 /* return -1 if error */
146 /* return code returned by server always occupies first 3 bytes of global
147    variable server_line */
148 int
149 grab_reply(
150     int show)
151 {
152     do {
153         if (get_line() == -1) {
154             return -1;
155         }
156         if(show) puts(server_line);
157     } while (server_line[3] == '-');
158     if(show) fflush(stdout);
159
160     return 0;
161 }
162
163
164 /* get 1 line of reply */
165 /* returns -1 if error, 0 if last (or only) line, 1 if more to follow */
166 int
167 get_reply_line(void)
168 {
169     if (get_line() == -1)
170         return -1;
171     return server_line[3] == '-';
172 }
173
174
175 /* returns pointer to returned line */
176 char *
177 reply_line(void)
178 {
179     return server_line;
180 }
181
182
183
184 /* returns 0 if server returned an error code (ie code starting with 5)
185    and non-zero otherwise */
186 int
187 server_happy(void)
188 {
189     return server_line[0] != '5';
190 }
191
192
193 int
194 send_command(
195     char *      cmd)
196 {
197     /*
198      * NOTE: this routine is called from sigint_handler, so we must be
199      * **very** careful about what we do since there is no way to know
200      * our state at the time the interrupt happened.  For instance,
201      * do not use any stdio or malloc routines here.
202      */
203     char *buffer;
204
205     buffer = alloc(strlen(cmd)+3);
206     strncpy(buffer, cmd, strlen(cmd));
207     buffer[strlen(cmd)] = '\r';
208     buffer[strlen(cmd)+1] = '\n';
209     buffer[strlen(cmd)+2] = '\0';
210
211     if(security_stream_write(streams[MESGFD].fd, buffer, strlen(buffer)) < 0) {
212         return -1;
213     }
214     amfree(buffer);
215     return (0);
216 }
217
218
219 /* send a command to the server, get reply and print to screen */
220 int
221 converse(
222     char *      cmd)
223 {
224     if (send_command(cmd) == -1) return -1;
225     if (grab_reply(1) == -1) return -1;
226     return 0;
227 }
228
229
230 /* same as converse() but reply not echoed to stdout */
231 int
232 exchange(
233     char *      cmd)
234 {
235     if (send_command(cmd) == -1) return -1;
236     if (grab_reply(0) == -1) return -1;
237     return 0;
238 }
239
240
241 /* basic interrupt handler for when user presses ^C */
242 /* Bale out, letting server know before doing so */
243 void
244 sigint_handler(
245     int signum)
246 {
247     /*
248      * NOTE: we must be **very** careful about what we do here since there
249      * is no way to know our state at the time the interrupt happened.
250      * For instance, do not use any stdio routines here or in any called
251      * routines.  Also, use _exit() instead of exit() to make sure stdio
252      * buffer flushing is not attempted.
253      */
254     (void)signum;       /* Quiet unused parameter warning */
255
256     if (extract_restore_child_pid != -1)
257         (void)kill(extract_restore_child_pid, SIGKILL);
258     extract_restore_child_pid = -1;
259
260     if(amindexd_alive) 
261         (void)send_command("QUIT");
262
263     _exit(1);
264 }
265
266
267 void
268 clean_pathname(
269     char *      s)
270 {
271     size_t length;
272     length = strlen(s);
273
274     /* remove "/" at end of path */
275     if(length>1 && s[length-1]=='/')
276         s[length-1]='\0';
277
278     /* change "/." to "/" */
279     if(strcmp(s,"/.")==0)
280         s[1]='\0';
281
282     /* remove "/." at end of path */
283     if(strcmp(&(s[length-2]),"/.")==0)
284         s[length-2]='\0';
285 }
286
287
288 void
289 quit(void)
290 {
291     quit_prog = 1;
292     (void)converse("QUIT");
293     stop_amindexd();
294 }
295
296 char *localhost = NULL;
297
298 #ifdef DEFAULT_TAPE_SERVER
299 # define DEFAULT_TAPE_SERVER_FAILOVER (DEFAULT_TAPE_SERVER)
300 #else
301 # define DEFAULT_TAPE_SERVER_FAILOVER (NULL)
302 #endif
303
304 int
305 main(
306     int         argc,
307     char **     argv)
308 {
309     int i;
310     time_t timer;
311     char *lineread = NULL;
312     struct sigaction act, oact;
313     extern char *optarg;
314     extern int optind;
315     char *line = NULL;
316     char *conffile;
317     const security_driver_t *secdrv;
318     char *req = NULL;
319     int response_error;
320     int new_argc;
321     char **new_argv;
322     struct tm *tm;
323
324     safe_fd(-1, 0);
325
326     set_pname("amrecover");
327
328     /* Don't die when child closes pipe */
329     signal(SIGPIPE, SIG_IGN);
330
331     dbopen(DBG_SUBDIR_CLIENT);
332
333 #ifndef IGNORE_UID_CHECK
334     if (geteuid() != 0) {
335         erroutput_type |= ERR_SYSLOG;
336         error("amrecover must be run by root");
337         /*NOTREACHED*/
338     }
339 #endif
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     parse_conf(argc, argv, &new_argc, &new_argv);
349
350     if (new_argc > 1 && new_argv[1][0] != '-') {
351         /*
352          * If the first argument is not an option flag, then we assume
353          * it is a configuration name to match the syntax of the other
354          * Amanda utilities.
355          */
356         char **new_argv1;
357
358         new_argv1 = (char **) alloc((size_t)((new_argc + 1 + 1) * sizeof(*new_argv1)));
359         new_argv1[0] = new_argv[0];
360         new_argv1[1] = "-C";
361         for (i = 1; i < new_argc; i++) {
362             new_argv1[i + 1] = new_argv[i];
363         }
364         new_argv1[i + 1] = NULL;
365         new_argc++;
366         amfree(new_argv);
367         new_argv = new_argv1;
368     }
369     while ((i = getopt(new_argc, new_argv, "C:s:t:d:U")) != EOF) {
370         switch (i) {
371             case 'C':
372                 add_client_conf(CNF_CONF, optarg);
373                 break;
374
375             case 's':
376                 add_client_conf(CNF_INDEX_SERVER, optarg);
377                 break;
378
379             case 't':
380                 add_client_conf(CNF_TAPE_SERVER, optarg);
381                 break;
382
383             case 'd':
384                 add_client_conf(CNF_TAPEDEV, optarg);
385                 break;
386
387             case 'U':
388             case '?':
389                 (void)printf(USAGE);
390                 return 0;
391         }
392     }
393     if (optind != new_argc) {
394         (void)fprintf(stderr, USAGE);
395         exit(1);
396     }
397
398     our_features = am_init_feature_set();
399     our_features_string = am_feature_to_string(our_features);
400
401     conffile = vstralloc(CONFIG_DIR, "/", "amanda-client.conf", NULL);
402     if (read_clientconf(conffile) > 0) {
403         error("error reading conffile: %s", conffile);
404         /*NOTREACHED*/
405     }
406     amfree(conffile);
407
408     config = stralloc(getconf_str(CNF_CONF));
409
410     conffile = vstralloc(CONFIG_DIR, "/", config, "/", "amanda-client.conf",
411                          NULL);
412     if (read_clientconf(conffile) > 0) {
413         error("error reading conffile: %s", conffile);
414         /*NOTREACHED*/
415     }
416     amfree(conffile);
417
418     dbrename(config, DBG_SUBDIR_CLIENT);
419
420     report_bad_conf_arg();
421
422     server_name = NULL;
423     if (getconf_seen(CNF_INDEX_SERVER) == -2) { /* command line argument */
424         server_name = getconf_str(CNF_INDEX_SERVER);
425     }
426     if (!server_name) {
427         server_name = getenv("AMANDA_SERVER");
428         if (server_name) {
429             printf("Using index server from environment AMANDA_SERVER (%s)\n", server_name);
430         }
431     }
432     if (!server_name) {
433         server_name = getconf_str(CNF_INDEX_SERVER);
434     }
435     if (!server_name) {
436         error("No index server set");
437         /*NOTREACHED*/
438     }
439     server_name = stralloc(server_name);
440
441     tape_server_name = NULL;
442     if (getconf_seen(CNF_TAPE_SERVER) == -2) { /* command line argument */
443         tape_server_name = getconf_str(CNF_TAPE_SERVER);
444     }
445     if (!tape_server_name) {
446         tape_server_name = getenv("AMANDA_TAPE_SERVER");
447         if (!tape_server_name) {
448             tape_server_name = getenv("AMANDA_TAPESERVER");
449             if (tape_server_name) {
450                 printf("Using tape server from environment AMANDA_TAPESERVER (%s)\n", tape_server_name);
451             }
452         } else {
453             printf("Using tape server from environment AMANDA_TAPE_SERVER (%s)\n", tape_server_name);
454         }
455     }
456     if (!tape_server_name) {
457         tape_server_name = getconf_str(CNF_TAPE_SERVER);
458     }
459     if (!tape_server_name) {
460         error("No tape server set");
461         /*NOTREACHED*/
462     }
463     tape_server_name = stralloc(tape_server_name);
464
465     amfree(tape_device_name);
466     tape_device_name = getconf_str(CNF_TAPEDEV);
467     if (tape_device_name)
468         tape_device_name = stralloc(tape_device_name);
469
470     authopt = stralloc(getconf_str(CNF_AUTH));
471
472
473     amfree(disk_name);
474     amfree(mount_point);
475     amfree(disk_path);
476     dump_date[0] = '\0';
477
478     /* Don't die when child closes pipe */
479     signal(SIGPIPE, SIG_IGN);
480
481     /* set up signal handler */
482     act.sa_handler = sigint_handler;
483     sigemptyset(&act.sa_mask);
484     act.sa_flags = 0;
485     if (sigaction(SIGINT, &act, &oact) != 0) {
486         error("error setting signal handler: %s", strerror(errno));
487         /*NOTREACHED*/
488     }
489
490     protocol_init();
491
492     /* We assume that amindexd support fe_amindexd_options_features */
493     /*                             and fe_amindexd_options_auth     */
494     /* We should send a noop to really know                         */
495     req = vstralloc("SERVICE amindexd\n",
496                     "OPTIONS ", "features=", our_features_string, ";",
497                                 "auth=", authopt, ";",
498                     "\n", NULL);
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     printf("AMRECOVER Version %s. Contacting server on %s ...\n",
514            version(), server_name);
515
516     if(response_error != 0) {
517         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 = stralloc2("FEATURES ", 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     printf("Setting restore date to today (%s)\n", dump_date);
567     line = stralloc2("DATE ", dump_date);
568     if (converse(line) == -1) {
569         aclose(server_socket);
570         exit(1);
571     }
572     amfree(line);
573
574     line = stralloc2("SCNF ", config);
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             printf("Use the setdisk command to choose dump disk to recover\n");
587         else
588             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             process_line(lineread);     /* act on line's content */
603         }
604         amfree(lineread);
605     } while (!quit_prog);
606
607     dbclose();
608
609     aclose(server_socket);
610     return 0;
611 }
612
613 static void
614 amindexd_response(
615     void *datap,
616     pkt_t *pkt,
617     security_handle_t *sech)
618 {
619     int ports[NSTREAMS], *response_error = datap, i;
620     char *p;
621     char *tok;
622     char *extra = NULL;
623
624     assert(response_error != NULL);
625     assert(sech != NULL);
626
627     if (pkt == NULL) {
628         errstr = newvstralloc(errstr, "[request failed: ",
629                              security_geterror(sech), "]", NULL);
630         *response_error = 1;
631         return;
632     }
633
634     if (pkt->type == P_NAK) {
635 #if defined(PACKET_DEBUG)
636         fprintf(stderr, "got nak response:\n----\n%s\n----\n\n", pkt->body);
637 #endif
638
639         tok = strtok(pkt->body, " ");
640         if (tok == NULL || strcmp(tok, "ERROR") != 0)
641             goto bad_nak;
642
643         tok = strtok(NULL, "\n");
644         if (tok != NULL) {
645             errstr = newvstralloc(errstr, "NAK: ", tok, NULL);
646             *response_error = 1;
647         } else {
648 bad_nak:
649             errstr = newstralloc(errstr, "request NAK");
650             *response_error = 2;
651         }
652         return;
653     }
654
655     if (pkt->type != P_REP) {
656         errstr = newvstralloc(errstr, "received strange packet type ",
657                               pkt_type2str(pkt->type), ": ", pkt->body, NULL);
658         *response_error = 1;
659         return;
660     }
661
662 #if defined(PACKET_DEBUG)
663     fprintf(stderr, "got response:\n----\n%s\n----\n\n", pkt->body);
664 #endif
665
666     for(i = 0; i < NSTREAMS; i++) {
667         ports[i] = -1;
668         streams[i].fd = NULL;
669     }
670
671     p = pkt->body;
672     while((tok = strtok(p, " \n")) != NULL) {
673         p = NULL;
674
675         /*
676          * Error response packets have "ERROR" followed by the error message
677          * followed by a newline.
678          */
679         if (strcmp(tok, "ERROR") == 0) {
680             tok = strtok(NULL, "\n");
681             if (tok == NULL)
682                 tok = "[bogus error packet]";
683             errstr = newstralloc(errstr, tok);
684             *response_error = 2;
685             return;
686         }
687
688
689         /*
690          * Regular packets have CONNECT followed by three streams
691          */
692         if (strcmp(tok, "CONNECT") == 0) {
693
694             /*
695              * Parse the three stream specifiers out of the packet.
696              */
697             for (i = 0; i < NSTREAMS; i++) {
698                 tok = strtok(NULL, " ");
699                 if (tok == NULL || strcmp(tok, streams[i].name) != 0) {
700                     extra = vstralloc("CONNECT token is \"",
701                                       tok ? tok : "(null)",
702                                       "\": expected \"",
703                                       streams[i].name,
704                                       "\"",
705                                       NULL);
706                     goto parse_error;
707                 }
708                 tok = strtok(NULL, " \n");
709                 if (tok == NULL || sscanf(tok, "%d", &ports[i]) != 1) {
710                     extra = vstralloc("CONNECT ",
711                                       streams[i].name,
712                                       " token is \"",
713                                       tok ? tok : "(null)",
714                                       "\": expected a port number",
715                                       NULL);
716                     goto parse_error;
717                 }
718             }
719             continue;
720         }
721
722         /*
723          * OPTIONS [options string] '\n'
724          */
725         if (strcmp(tok, "OPTIONS") == 0) {
726             tok = strtok(NULL, "\n");
727             if (tok == NULL) {
728                 extra = stralloc("OPTIONS token is missing");
729                 goto parse_error;
730             }
731 /*
732             tok_end = tok + strlen(tok);
733             while((p = strchr(tok, ';')) != NULL) {
734                 *p++ = '\0';
735                 if(strncmp_const(tok, "features=") == 0) {
736                     tok += SIZEOF("features=") - 1;
737                     am_release_feature_set(their_features);
738                     if((their_features = am_string_to_feature(tok)) == NULL) {
739                         errstr = newvstralloc(errstr,
740                                               "OPTIONS: bad features value: ",
741                                               tok,
742                                               NULL);
743                         goto parse_error;
744                     }
745                 }
746                 tok = p;
747             }
748 */
749             continue;
750         }
751 /*
752         extra = vstralloc("next token is \"",
753                           tok ? tok : "(null)",
754                           "\": expected \"CONNECT\", \"ERROR\" or \"OPTIONS\"",
755                           NULL);
756         goto parse_error;
757 */
758     }
759
760     /*
761      * Connect the streams to their remote ports
762      */
763     for (i = 0; i < NSTREAMS; i++) {
764 /*@i@*/ if (ports[i] == -1)
765             continue;
766         streams[i].fd = security_stream_client(sech, ports[i]);
767         if (streams[i].fd == NULL) {
768             errstr = newvstralloc(errstr,
769                         "[could not connect ", streams[i].name, " stream: ",
770                         security_geterror(sech), "]", NULL);
771             goto connect_error;
772         }
773     }
774     /*
775      * Authenticate the streams
776      */
777     for (i = 0; i < NSTREAMS; i++) {
778         if (streams[i].fd == NULL)
779             continue;
780         if (security_stream_auth(streams[i].fd) < 0) {
781             errstr = newvstralloc(errstr,
782                 "[could not authenticate ", streams[i].name, " stream: ",
783                 security_stream_geterror(streams[i].fd), "]", NULL);
784             goto connect_error;
785         }
786     }
787
788     /*
789      * The MESGFD and DATAFD streams are mandatory.  If we didn't get
790      * them, complain.
791      */
792     if (streams[MESGFD].fd == NULL) {
793         errstr = newstralloc(errstr, "[couldn't open MESG streams]");
794         goto connect_error;
795     }
796
797     /* everything worked */
798     *response_error = 0;
799     amindexd_alive = 1;
800     return;
801
802 parse_error:
803     errstr = newvstralloc(errstr,
804                           "[parse of reply message failed: ",
805                           extra ? extra : "(no additional information)",
806                           "]",
807                           NULL);
808     amfree(extra);
809     *response_error = 2;
810     return;
811
812 connect_error:
813     stop_amindexd();
814     *response_error = 1;
815 }
816
817 /*
818  * This is called when everything needs to shut down so event_loop()
819  * will exit.
820  */
821 void
822 stop_amindexd(void)
823 {
824     int i;
825
826     amindexd_alive = 0;
827     for (i = 0; i < NSTREAMS; i++) {
828         if (streams[i].fd != NULL) {
829             security_stream_close(streams[i].fd);
830             streams[i].fd = NULL;
831         }
832     }
833 }
834
835 char *
836 amindexd_client_get_security_conf(
837     char *      string,
838     void *      arg)
839 {
840     (void)arg;  /* Quiet unused parameter warning */
841
842     if(!string || !*string)
843         return(NULL);
844
845     if(strcmp(string, "auth")==0) {
846         return(getconf_str(CNF_AUTH));
847     }
848     else if(strcmp(string, "ssh_keys")==0) {
849         return(getconf_str(CNF_SSH_KEYS));
850     }
851     return(NULL);
852 }