X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=server-src%2Famindexd.c;h=493a8089c2c7138f641d07b28a5a5478ffa5b943;hb=34197d9f46a5f4e944378cbb65fca32ee0eec7b9;hp=e35d2709fe2ed89c4108cec033778f2e464f4fc3;hpb=0de2ad0a86685398621fb8ffa6990c029681bb3a;p=debian%2Famanda diff --git a/server-src/amindexd.c b/server-src/amindexd.c index e35d270..493a808 100644 --- a/server-src/amindexd.c +++ b/server-src/amindexd.c @@ -24,7 +24,7 @@ * file named AUTHORS, in the root directory of this distribution. */ /* - * $Id: amindexd.c,v 1.39.2.11.4.4.2.13.2.1 2004/04/05 17:22:59 martinea Exp $ + * $Id: amindexd.c,v 1.106.2.9 2007/02/07 15:23:45 martinea Exp $ * * This is the server daemon part of the index client/server system. * It is assumed that this is launched from inetd instead of being @@ -45,9 +45,7 @@ #include "diskfile.h" #include "arglist.h" #include "clock.h" -#include "dgram.h" #include "version.h" -#include "protocol.h" #include "amindex.h" #include "disk_history.h" #include "list_dir.h" @@ -55,14 +53,9 @@ #include "token.h" #include "find.h" #include "tapefile.h" - -#ifdef HAVE_NETINET_IN_SYSTM_H -#include -#endif - -#ifdef HAVE_NETINET_IP_H -#include -#endif +#include "util.h" +#include "amandad.h" +#include "pipespawn.h" #include @@ -73,13 +66,17 @@ typedef struct REMOVE_ITEM } REMOVE_ITEM; /* state */ -char local_hostname[MAX_HOSTNAME_LENGTH+1]; /* me! */ -char *remote_hostname = NULL; /* the client */ -char *dump_hostname = NULL; /* machine we are restoring */ -char *disk_name; /* disk we are restoring */ -char *target_date = NULL; -disklist_t *disk_list; /* all disks in cur config */ -find_result_t *output_find = NULL; +static int from_amandad; +static char local_hostname[MAX_HOSTNAME_LENGTH+1]; /* me! */ +static char *remote_hostname = NULL; /* the client */ +static char *dump_hostname = NULL; /* machine we are restoring */ +static char *disk_name; /* disk we are restoring */ +char *qdisk_name = NULL; /* disk we are restoring */ +static char *target_date = NULL; +static disklist_t disk_list; /* all disks in cur config */ +static find_result_t *output_find = NULL; +static g_option_t *g_options = NULL; +static int cmdfdin, cmdfdout; static int amindexd_debug = 0; @@ -89,15 +86,41 @@ static REMOVE_ITEM *uncompress_remove = NULL; static am_feature_t *our_features = NULL; static am_feature_t *their_features = NULL; -static void reply P((int n, char * fmt, ...)) +static REMOVE_ITEM *remove_files(REMOVE_ITEM *); +static char *uncompress_file(char *, char **); +static int process_ls_dump(char *, DUMP_ITEM *, int, char **); + +static size_t reply_buffer_size = 1; +static char *reply_buffer = NULL; +static char *amandad_auth = NULL; +static FILE *cmdin; +static FILE *cmdout; + +static void reply(int, char *, ...) __attribute__ ((format (printf, 2, 3))); -static void lreply P((int n, char * fmt, ...)) +static void lreply(int, char *, ...) __attribute__ ((format (printf, 2, 3))); -static void fast_lreply P((int n, char * fmt, ...)) +static void fast_lreply(int, char *, ...) __attribute__ ((format (printf, 2, 3))); - -REMOVE_ITEM *remove_files(remove) -REMOVE_ITEM *remove; +static int is_dump_host_valid(char *); +static int is_disk_valid(char *); +static int is_config_valid(char *); +static int build_disk_table(void); +static int disk_history_list(void); +static int is_dir_valid_opaque(char *); +static int opaque_ls(char *, int); +static void opaque_ls_one (DIR_ITEM *dir_item, am_feature_e marshall_feature, + int recursive); +static int tapedev_is(void); +static int are_dumps_compressed(void); +static char *amindexd_nicedate (char *datestamp); +static int cmp_date (const char *date1, const char *date2); +static char *clean_backslash(char *line); +int main(int, char **); + +static REMOVE_ITEM * +remove_files( + REMOVE_ITEM *remove) { REMOVE_ITEM *prev; @@ -113,15 +136,28 @@ REMOVE_ITEM *remove; return remove; } -char *uncompress_file(filename_gz, emsg) -char *filename_gz; -char **emsg; +static char * +uncompress_file( + char * filename_gz, + char ** emsg) { char *cmd = NULL; char *filename = NULL; struct stat stat_filename; int result; - int len; + size_t len; + int pipe_from_gzip; + int pipe_to_sort; + int indexfd; + int nullfd; + int debugfd; + int debugnullfd; + char line[STR_SIZE]; + FILE *pipe_stream; + pid_t pid_gzip; + pid_t pid_sort; + amwait_t wait_status; + filename = stralloc(filename_gz); len = strlen(filename); @@ -134,30 +170,114 @@ char **emsg; /* uncompress the file */ result=stat(filename,&stat_filename); if(result==-1 && errno==ENOENT) { /* file does not exist */ + struct stat statbuf; REMOVE_ITEM *remove_file; - cmd = vstralloc(UNCOMPRESS_PATH, + + /* + * Check that compressed file exists manually. + */ + if (stat(filename_gz, &statbuf) < 0) { + *emsg = newvstralloc(*emsg, "Compressed file '", + filename_gz, + "' is inaccessable: ", + strerror(errno), + NULL); + dbprintf(("%s\n",*emsg)); + amfree(filename); + return NULL; + } + #ifdef UNCOMPRESS_OPT - " ", UNCOMPRESS_OPT, +# define PARAM_UNCOMPRESS_OPT UNCOMPRESS_OPT +#else +# define PARAM_UNCOMPRESS_OPT skip_argument #endif - " \'", filename_gz, "\'", - " 2>/dev/null", - " | sort", - " > ", "\'", filename, "\'", - NULL); - dbprintf(("%s: uncompress command: %s\n", - debug_prefix_time(NULL), cmd)); - if (system(cmd)!=0) { - amfree(*emsg); - *emsg = vstralloc("\"", cmd, "\" failed", NULL); - unlink(filename); - errno = -1; + + debugfd = dbfd(); + debugnullfd = 0; + if(debugfd < 0) { + debugfd = open("/dev/null", O_WRONLY); + debugnullfd = 1; + } + + nullfd = open("/dev/null", O_RDONLY); + indexfd = open(filename,O_WRONLY|O_CREAT, 0600); + if (indexfd == -1) { + *emsg = newvstralloc(*emsg, "Can't open '", + filename, "' for writting: ", + strerror(errno), + NULL); + dbprintf(("%s\n",*emsg)); amfree(filename); - amfree(cmd); return NULL; } + /* start the uncompress process */ + putenv(stralloc("LC_ALL=C")); + pid_gzip = pipespawn(UNCOMPRESS_PATH, STDOUT_PIPE, + &nullfd, &pipe_from_gzip, &debugfd, + UNCOMPRESS_PATH, PARAM_UNCOMPRESS_OPT, + filename_gz, NULL); + aclose(nullfd); + + pipe_stream = fdopen(pipe_from_gzip,"r"); + if(pipe_stream == NULL) { + *emsg = newvstralloc(*emsg, "Can't fdopen pipe from gzip: ", + strerror(errno), + NULL); + dbprintf(("%s\n",*emsg)); + amfree(filename); + return NULL; + } + + /* start the sort process */ + pid_sort = pipespawn(SORT_PATH, STDIN_PIPE, + &pipe_to_sort, &indexfd, &debugfd, + SORT_PATH, NULL); + if (debugnullfd == 1) + aclose(debugfd); + aclose(indexfd); + + /* send all ouput from uncompress process to sort process */ + /* clean the data with clean_backslash */ + while (fgets(line, STR_SIZE, pipe_stream) != NULL) { + if (line[0] != '\0') { + if (index(line,'/')) { + clean_backslash(line); + fullwrite(pipe_to_sort,line,strlen(line)); + } + } + } + + fclose(pipe_stream); + aclose(pipe_to_sort); + if (waitpid(pid_gzip, &wait_status, 0) < 0) { + if (!WIFEXITED(wait_status)) { + dbprintf(("Uncompress exited with signal %d", + WTERMSIG(wait_status))); + } else if (WEXITSTATUS(wait_status) != 0) { + dbprintf(("Uncompress exited with status %d", + WEXITSTATUS(wait_status))); + } else { + dbprintf(("Uncompres returned negative value: %s", + strerror(errno))); + } + } + if (waitpid(pid_sort, &wait_status, 0)) { + if (!WIFEXITED(wait_status)) { + dbprintf(("Sort exited with signal %d", + WTERMSIG(wait_status))); + } else if (WEXITSTATUS(wait_status) != 0) { + dbprintf(("Sort exited with status %d", + WEXITSTATUS(wait_status))); + } else { + dbprintf(("Sort returned negative value: %s", + strerror(errno))); + } + } + /* add at beginning */ - remove_file = (REMOVE_ITEM *)alloc(sizeof(REMOVE_ITEM)); + remove_file = (REMOVE_ITEM *)alloc(SIZEOF(REMOVE_ITEM)); remove_file->filename = stralloc(filename); remove_file->next = uncompress_remove; uncompress_remove = remove_file; @@ -168,8 +288,6 @@ char **emsg; amfree(filename); amfree(cmd); return NULL; - } else { - /* already uncompressed */ } amfree(cmd); return filename; @@ -177,13 +295,14 @@ char **emsg; /* find all matching entries in a dump listing */ /* return -1 if error */ -static int process_ls_dump(dir, dump_item, recursive, emsg) -char *dir; -DUMP_ITEM *dump_item; -int recursive; -char **emsg; +static int +process_ls_dump( + char * dir, + DUMP_ITEM * dump_item, + int recursive, + char ** emsg) { - char *line = NULL; + char line[STR_SIZE]; char *old_line = NULL; char *filename = NULL; char *filename_gz; @@ -191,7 +310,8 @@ char **emsg; FILE *fp; char *s; int ch; - int len_dir_slash; + size_t len_dir_slash; + struct stat statbuf; if (strcmp(dir, "/") == 0) { dir_slash = stralloc(dir); @@ -201,6 +321,11 @@ char **emsg; filename_gz = getindexfname(dump_hostname, disk_name, dump_item->date, dump_item->level); + if (stat(filename_gz, &statbuf) < 0 && errno == ENOENT) { + amfree(filename_gz); + filename_gz = getoldindexfname(dump_hostname, disk_name, + dump_item->date, dump_item->level); + } if((filename = uncompress_file(filename_gz, emsg)) == NULL) { amfree(filename_gz); amfree(dir_slash); @@ -217,28 +342,31 @@ char **emsg; len_dir_slash=strlen(dir_slash); - for(; (line = agets(fp)) != NULL; free(line)) { - if(strncmp(dir_slash, line, len_dir_slash) == 0) { - if(!recursive) { - s = line + len_dir_slash; - ch = *s++; - while(ch && ch != '/') ch = *s++;/* find end of the file name */ - if(ch == '/') { + while (fgets(line, STR_SIZE, fp) != NULL) { + if (line[0] != '\0') { + if(line[strlen(line)-1] == '\n') + line[strlen(line)-1] = '\0'; + if(strncmp(dir_slash, line, len_dir_slash) == 0) { + if(!recursive) { + s = line + len_dir_slash; ch = *s++; + while(ch && ch != '/') + ch = *s++;/* find end of the file name */ + if(ch == '/') { + ch = *s++; + } + s[-1] = '\0'; + } + if(old_line == NULL || strcmp(line, old_line) != 0) { + add_dir_list_item(dump_item, line); + amfree(old_line); + old_line = stralloc(line); } - s[-1] = '\0'; - } - if(old_line == NULL || strcmp(line, old_line) != 0) { - add_dir_list_item(dump_item, line); - amfree(old_line); - old_line = line; - line = NULL; } } } afclose(fp); - amfree(old_line); - amfree(line); + /*@i@*/ amfree(old_line); amfree(filename); amfree(dir_slash); return 0; @@ -248,49 +376,71 @@ char **emsg; printf_arglist_function1(static void reply, int, n, char *, fmt) { va_list args; - char buf[STR_SIZE]; + int len; + + if(!reply_buffer) + reply_buffer = alloc(reply_buffer_size); + + while(1) { + arglist_start(args, fmt); + len = vsnprintf(reply_buffer, reply_buffer_size, fmt, args); + arglist_end(args); - arglist_start(args, fmt); - ap_snprintf(buf, sizeof(buf), "%03d ", n); - ap_vsnprintf(buf+4, sizeof(buf)-4, fmt, args); - arglist_end(args); + if (len > -1 && (size_t)len < reply_buffer_size-1) + break; + + reply_buffer_size *= 2; + amfree(reply_buffer); + reply_buffer = alloc(reply_buffer_size); + } - if (printf("%s\r\n", buf) < 0) + if (fprintf(cmdout,"%03d %s\r\n", n, reply_buffer) < 0) { dbprintf(("%s: ! error %d (%s) in printf\n", debug_prefix_time(NULL), errno, strerror(errno))); uncompress_remove = remove_files(uncompress_remove); exit(1); } - if (fflush(stdout) != 0) + if (fflush(cmdout) != 0) { dbprintf(("%s: ! error %d (%s) in fflush\n", debug_prefix_time(NULL), errno, strerror(errno))); uncompress_remove = remove_files(uncompress_remove); exit(1); } - dbprintf(("%s: < %s\n", debug_prefix_time(NULL), buf)); + dbprintf(("%s: < %03d %s\n", debug_prefix_time(NULL), n, reply_buffer)); } /* send one line of a multi-line response */ printf_arglist_function1(static void lreply, int, n, char *, fmt) { va_list args; - char buf[STR_SIZE]; + int len; - arglist_start(args, fmt); - ap_snprintf(buf, sizeof(buf), "%03d-", n); - ap_vsnprintf(buf+4, sizeof(buf)-4, fmt, args); - arglist_end(args); + if(!reply_buffer) + reply_buffer = alloc(reply_buffer_size); - if (printf("%s\r\n", buf) < 0) + while(1) { + arglist_start(args, fmt); + len = vsnprintf(reply_buffer, reply_buffer_size, fmt, args); + arglist_end(args); + + if (len > -1 && (size_t)len < reply_buffer_size-1) + break; + + reply_buffer_size *= 2; + amfree(reply_buffer); + reply_buffer = alloc(reply_buffer_size); + } + + if (fprintf(cmdout,"%03d-%s\r\n", n, reply_buffer) < 0) { dbprintf(("%s: ! error %d (%s) in printf\n", debug_prefix_time(NULL), errno, strerror(errno))); uncompress_remove = remove_files(uncompress_remove); exit(1); } - if (fflush(stdout) != 0) + if (fflush(cmdout) != 0) { dbprintf(("%s: ! error %d (%s) in fflush\n", debug_prefix_time(NULL), errno, strerror(errno))); @@ -298,27 +448,41 @@ printf_arglist_function1(static void lreply, int, n, char *, fmt) exit(1); } - dbprintf(("%s: < %s\n", debug_prefix_time(NULL), buf)); + dbprintf(("%s: < %03d-%s\n", debug_prefix_time(NULL), n, reply_buffer)); + } /* send one line of a multi-line response */ printf_arglist_function1(static void fast_lreply, int, n, char *, fmt) { va_list args; - char buf[STR_SIZE]; + int len; + + if(!reply_buffer) + reply_buffer = alloc(reply_buffer_size); - arglist_start(args, fmt); - ap_snprintf(buf, sizeof(buf), "%03d-", n); - ap_vsnprintf(buf+4, sizeof(buf)-4, fmt, args); - arglist_end(args); + while(1) { + arglist_start(args, fmt); + len = vsnprintf(reply_buffer, reply_buffer_size, fmt, args); + arglist_end(args); - if (printf("%s\r\n", buf) < 0) + if (len > -1 && (size_t)len < reply_buffer_size-1) + break; + + reply_buffer_size *= 2; + amfree(reply_buffer); + reply_buffer = alloc(reply_buffer_size); + } + + if (fprintf(cmdout,"%03d-%s\r\n", n, reply_buffer) < 0) { dbprintf(("%s: ! error %d (%s) in printf\n", debug_prefix_time(NULL), errno, strerror(errno))); uncompress_remove = remove_files(uncompress_remove); exit(1); } + + dbprintf(("%s: < %03d-%s\n", debug_prefix_time(NULL), n, reply_buffer)); } /* see if hostname is valid */ @@ -326,8 +490,9 @@ printf_arglist_function1(static void fast_lreply, int, n, char *, fmt) /* also do a security check on the requested dump hostname */ /* to restrict access to index records if required */ /* return -1 if not okay */ -int is_dump_host_valid(host) -char *host; +static int +is_dump_host_valid( + char * host) { struct stat dir_stat; char *fn; @@ -371,30 +536,40 @@ char *host; } -int is_disk_valid(disk) -char *disk; +static int +is_disk_valid( + char *disk) { char *fn; struct stat dir_stat; disk_t *idisk; + char *qdisk; - if (config_name == NULL || dump_hostname == NULL) { + if (config_name == NULL) { reply(501, "Must set config,host before setting disk."); return -1; } + else if (dump_hostname == NULL) { + reply(501, "Must set host before setting disk."); + return -1; + } /* check that the config actually handles that disk */ idisk = lookup_disk(dump_hostname, disk); if(idisk == NULL) { - reply(501, "Disk %s:%s is not in your disklist.", dump_hostname, disk); + qdisk = quote_string(disk); + reply(501, "Disk %s:%s is not in your disklist.", dump_hostname, qdisk); + amfree(qdisk); return -1; } /* assume an index dir already */ fn = getindexfname(dump_hostname, disk, NULL, 0); if (stat (fn, &dir_stat) != 0 || !S_ISDIR(dir_stat.st_mode)) { - reply(501, "No index records for disk: %s. Invalid?", disk); + qdisk = quote_string(disk); + reply(501, "No index records for disk: %s. Invalid?", qdisk); amfree(fn); + amfree(qdisk); return -1; } @@ -403,8 +578,9 @@ char *disk; } -int is_config_valid(config) -char *config; +static int +is_config_valid( + char * config) { char *conffile; char *conf_diskfile; @@ -426,18 +602,20 @@ char *config; return -1; } amfree(conffile); + conf_diskfile = getconf_str(CNF_DISKFILE); if (*conf_diskfile == '/') { conf_diskfile = stralloc(conf_diskfile); } else { conf_diskfile = stralloc2(config_dir, conf_diskfile); } - if ((disk_list = read_diskfile(conf_diskfile)) == NULL) { + if (read_diskfile(conf_diskfile, &disk_list) < 0) { reply(501, "Could not read disk file %s!", conf_diskfile); amfree(conf_diskfile); return -1; } amfree(conf_diskfile); + conf_tapelist = getconf_str(CNF_TAPELIST); if (*conf_tapelist == '/') { conf_tapelist = stralloc(conf_tapelist); @@ -451,10 +629,11 @@ char *config; } amfree(conf_tapelist); - output_find = find_dump(1, disk_list); - sort_find_result("DLKHB", &output_find); + dbrename(config, DBG_SUBDIR_SERVER); + + output_find = find_dump(1, &disk_list); + sort_find_result("DLKHpB", &output_find); - /* okay, now look for the index directory */ conf_indexdir = getconf_str(CNF_INDEXDIR); if(*conf_indexdir == '/') { conf_indexdir = stralloc(conf_indexdir); @@ -472,108 +651,160 @@ char *config; } -int build_disk_table P((void)) +static int +build_disk_table(void) { - char date[3 * NUM_STR_SIZE + 2 + 1]; - long last_datestamp; - int last_filenum; + char *date; + char *last_timestamp; + off_t last_filenum; int last_level; + int last_partnum; find_result_t *find_output; - if (config_name == NULL || dump_hostname == NULL || disk_name == NULL) { + if (config_name == NULL) { reply(590, "Must set config,host,disk before building disk table"); return -1; } + else if (dump_hostname == NULL) { + reply(590, "Must set host,disk before building disk table"); + return -1; + } + else if (disk_name == NULL) { + reply(590, "Must set disk before building disk table"); + return -1; + } clear_list(); - last_datestamp = -1; - last_filenum = -1; + last_timestamp = NULL; + last_filenum = (off_t)-1; last_level = -1; + last_partnum = -1; for(find_output = output_find; find_output != NULL; find_output = find_output->next) { if(strcasecmp(dump_hostname, find_output->hostname) == 0 && strcmp(disk_name , find_output->diskname) == 0 && strcmp("OK" , find_output->status) == 0) { + int partnum = -1; + if(strcmp("--", find_output->partnum)){ + partnum = atoi(find_output->partnum); + } /* - * The sort order puts holding disk entries first. We want to + * The sort order puts holding disk entries first. We want to * use them if at all possible, so ignore any other entries * for the same datestamp after we see a holding disk entry * (as indicated by a filenum of zero). */ - if(find_output->datestamp == last_datestamp && - find_output->level == last_level && last_filenum == 0) { + if(last_timestamp && + strcmp(find_output->timestamp, last_timestamp) == 0 && + find_output->level == last_level && + partnum == last_partnum && last_filenum == 0) { continue; } - last_datestamp = find_output->datestamp; + last_timestamp = find_output->timestamp; last_filenum = find_output->filenum; last_level = find_output->level; - ap_snprintf(date, sizeof(date), "%04d-%02d-%02d", - find_output->datestamp/10000, - (find_output->datestamp/100) %100, - find_output->datestamp %100); + last_partnum = partnum; + date = amindexd_nicedate(find_output->timestamp); add_dump(date, find_output->level, find_output->label, - find_output->filenum); - dbprintf(("%s: - %s %d %s %d\n", - debug_prefix_time(NULL), date, find_output->level, - find_output->label, find_output->filenum)); + find_output->filenum, partnum); + dbprintf(("%s: - %s %d %s " OFF_T_FMT " %d\n", + debug_prefix_time(NULL), date, find_output->level, + find_output->label, + (OFF_T_FMT_TYPE)find_output->filenum, + partnum)); } } return 0; } -int disk_history_list P((void)) +static int +disk_history_list(void) { DUMP_ITEM *item; + char date[20]; - if (config_name == NULL || dump_hostname == NULL || disk_name == NULL) { + if (config_name == NULL) { reply(502, "Must set config,host,disk before listing history"); return -1; } + else if (dump_hostname == NULL) { + reply(502, "Must set host,disk before listing history"); + return -1; + } + else if (disk_name == NULL) { + reply(502, "Must set disk before listing history"); + return -1; + } - lreply(200, " Dump history for config \"%s\" host \"%s\" disk \"%s\"", - config_name, dump_hostname, disk_name); + lreply(200, " Dump history for config \"%s\" host \"%s\" disk %s", + config_name, dump_hostname, qdisk_name); - for (item=first_dump(); item!=NULL; item=next_dump(item)) - lreply(201, " %s %d %s %d", item->date, item->level, item->tape, - item->file); + for (item=first_dump(); item!=NULL; item=next_dump(item)){ + char *tapelist_str = marshal_tapelist(item->tapes, 1); - reply(200, "Dump history for config \"%s\" host \"%s\" disk \"%s\"", - config_name, dump_hostname, disk_name); + strncpy(date, item->date, 20); + date[19] = '\0'; + if(!am_has_feature(their_features,fe_amrecover_timestamp)) + date[10] = '\0'; + + if(am_has_feature(their_features, fe_amindexd_marshall_in_DHST)){ + lreply(201, " %s %d %s", date, item->level, tapelist_str); + } + else{ + lreply(201, " %s %d %s " OFF_T_FMT, date, item->level, + tapelist_str, (OFF_T_FMT_TYPE)item->file); + } + amfree(tapelist_str); + } + + reply(200, "Dump history for config \"%s\" host \"%s\" disk %s", + config_name, dump_hostname, qdisk_name); return 0; } -/* is the directory dir backed up - dir assumed complete relative to - disk mount point */ +/* + * is the directory dir backed up - dir assumed complete relative to + * disk mount point + */ /* opaque version of command */ -int is_dir_valid_opaque(dir) -char *dir; +static int +is_dir_valid_opaque( + char *dir) { DUMP_ITEM *item; - char *line = NULL; + char line[STR_SIZE]; FILE *fp; int last_level; char *ldir = NULL; char *filename_gz = NULL; char *filename = NULL; - int ldir_len; + size_t ldir_len; static char *emsg = NULL; if (config_name == NULL || dump_hostname == NULL || disk_name == NULL) { reply(502, "Must set config,host,disk before asking about directories"); return -1; } - if (target_date == NULL) { + else if (dump_hostname == NULL) { + reply(502, "Must set host,disk before asking about directories"); + return -1; + } + else if (disk_name == NULL) { + reply(502, "Must set disk before asking about directories"); + return -1; + } + else if (target_date == NULL) { reply(502, "Must set date before asking about directories"); return -1; } /* scan through till we find first dump on or before date */ for (item=first_dump(); item!=NULL; item=next_dump(item)) - if (strcmp(item->date, target_date) <= 0) + if (cmp_date(item->date, target_date) <= 0) break; if (item == NULL) @@ -611,13 +842,16 @@ char *dir; amfree(ldir); return -1; } - for(; (line = agets(fp)) != NULL; free(line)) { + while (fgets(line, STR_SIZE, fp) != NULL) { + if (line[0] == '\0') + continue; + if(line[strlen(line)-1] == '\n') + line[strlen(line)-1] = '\0'; if (strncmp(line, ldir, ldir_len) != 0) { continue; /* not found yet */ } amfree(filename); amfree(ldir); - amfree(line); afclose(fp); return 0; } @@ -636,29 +870,45 @@ char *dir; return -1; } -int opaque_ls(dir,recursive) -char *dir; -int recursive; +static int +opaque_ls( + char * dir, + int recursive) { DUMP_ITEM *dump_item; DIR_ITEM *dir_item; - int last_level; + int level, last_level; static char *emsg = NULL; + am_feature_e marshall_feature; + + if (recursive) { + marshall_feature = fe_amindexd_marshall_in_ORLD; + } else { + marshall_feature = fe_amindexd_marshall_in_OLSD; + } clear_dir_list(); - if (config_name == NULL || dump_hostname == NULL || disk_name == NULL) { + if (config_name == NULL) { reply(502, "Must set config,host,disk before listing a directory"); return -1; } - if (target_date == NULL) { + else if (dump_hostname == NULL) { + reply(502, "Must set host,disk before listing a directory"); + return -1; + } + else if (disk_name == NULL) { + reply(502, "Must set disk before listing a directory"); + return -1; + } + else if (target_date == NULL) { reply(502, "Must set date before listing a directory"); return -1; } /* scan through till we find first dump on or before date */ for (dump_item=first_dump(); dump_item!=NULL; dump_item=next_dump(dump_item)) - if (strcmp(dump_item->date, target_date) <= 0) + if (cmp_date(dump_item->date, target_date) <= 0) break; if (dump_item == NULL) @@ -691,52 +941,82 @@ int recursive; } /* return the information to the caller */ - if(recursive) - { - lreply(200, " Opaque recursive list of %s", dir); - for (dir_item = get_dir_list(); dir_item != NULL; - dir_item = dir_item->next) { - if(am_has_feature(their_features, fe_amindexd_fileno_in_ORLD)){ - fast_lreply(201, " %s %d %-16s %d %s", - dir_item->dump->date, dir_item->dump->level, - dir_item->dump->tape, dir_item->dump->file, - dir_item->path); - } - else { - fast_lreply(201, " %s %d %-16s %s", - dir_item->dump->date, dir_item->dump->level, - dir_item->dump->tape, dir_item->path); - } - } - reply(200, " Opaque recursive list of %s", dir); - } - else - { - lreply(200, " Opaque list of %s", dir); + lreply(200, " Opaque list of %s", dir); + for(level=0; level<=9; level++) { for (dir_item = get_dir_list(); dir_item != NULL; dir_item = dir_item->next) { - if(am_has_feature(their_features, fe_amindexd_fileno_in_OLSD)){ - lreply(201, " %s %d %-16s %d %s", - dir_item->dump->date, dir_item->dump->level, - dir_item->dump->tape, dir_item->dump->file, - dir_item->path); - } - else { - lreply(201, " %s %d %-16s %s", - dir_item->dump->date, dir_item->dump->level, - dir_item->dump->tape, dir_item->path); + + if(dir_item->dump->level == level) { + if (!am_has_feature(their_features, marshall_feature) && + (num_entries(dir_item->dump->tapes) > 1 || + dir_item->dump->tapes->numfiles > 1)) { + fast_lreply(501, " ERROR: Split dumps not supported" + " with old version of amrecover."); + break; + } + else { + opaque_ls_one(dir_item, marshall_feature, recursive); + } } } - reply(200, " Opaque list of %s", dir); } + reply(200, " Opaque list of %s", dir); + clear_dir_list(); return 0; } +void opaque_ls_one( + DIR_ITEM * dir_item, + am_feature_e marshall_feature, + int recursive) +{ + char date[20]; + char *tapelist_str; + char *qpath; + + if (am_has_feature(their_features, marshall_feature)) { + tapelist_str = marshal_tapelist(dir_item->dump->tapes, 1); + } else { + tapelist_str = dir_item->dump->tapes->label; + } + + strncpy(date, dir_item->dump->date, 20); + date[19] = '\0'; + if(!am_has_feature(their_features,fe_amrecover_timestamp)) + date[10] = '\0'; + + qpath = quote_string(dir_item->path); + if((!recursive && am_has_feature(their_features, + fe_amindexd_fileno_in_OLSD)) || + (recursive && am_has_feature(their_features, + fe_amindexd_fileno_in_ORLD))) { + fast_lreply(201, " %s %d %s " OFF_T_FMT " %s", + date, + dir_item->dump->level, + tapelist_str, + (OFF_T_FMT_TYPE)dir_item->dump->file, + qpath); + } + else { + + fast_lreply(201, " %s %d %s %s", + date, dir_item->dump->level, + tapelist_str, qpath); + } + amfree(qpath); + if(am_has_feature(their_features, marshall_feature)) { + amfree(tapelist_str); + } +} + +/* + * returns the value of changer or tapedev from the amanda.conf file if set, + * otherwise reports an error + */ -/* returns the value of tapedev from the amanda.conf file if set, - otherwise reports an error */ -int tapedev_is P((void)) +static int +tapedev_is(void) { char *result; @@ -746,37 +1026,67 @@ int tapedev_is P((void)) return -1; } + /* use amrecover_changer if possible */ + if ((result = getconf_str(CNF_AMRECOVER_CHANGER)) != NULL && + *result != '\0') { + dbprintf(("%s: tapedev_is amrecover_changer: %s\n", + debug_prefix_time(NULL), result)); + reply(200, result); + return 0; + } + + /* use changer if possible */ + if ((result = getconf_str(CNF_TPCHANGER)) != NULL && *result != '\0') { + dbprintf(("%s: tapedev_is tpchanger: %s\n", + debug_prefix_time(NULL), result)); + reply(200, result); + return 0; + } + /* get tapedev value */ - if ((result = getconf_str(CNF_TAPEDEV)) == NULL) - { - reply(501, "Tapedev not set in config file."); - return -1; + if ((result = getconf_str(CNF_TAPEDEV)) != NULL && *result != '\0') { + dbprintf(("%s: tapedev_is tapedev: %s\n", + debug_prefix_time(NULL), result)); + reply(200, result); + return 0; } - reply(200, result); - return 0; + dbprintf(("%s: No tapedev or tpchanger in config site.\n", + debug_prefix_time(NULL))); + reply(501, "Tapedev or tpchanger not set in config file."); + return -1; } /* returns YES if dumps for disk are compressed, NO if not */ -int are_dumps_compressed P((void)) +static int +are_dumps_compressed(void) { disk_t *diskp; /* check state okay to do this */ - if (config_name == NULL || dump_hostname == NULL || disk_name == NULL) { + if (config_name == NULL) { reply(501, "Must set config,host,disk name before asking about dumps."); return -1; } + else if (dump_hostname == NULL) { + reply(501, "Must set host,disk name before asking about dumps."); + return -1; + } + else if (disk_name == NULL) { + reply(501, "Must set disk name before asking about dumps."); + return -1; + } /* now go through the list of disks and find which have indexes */ - for (diskp = disk_list->head; diskp != NULL; diskp = diskp->next) + for (diskp = disk_list.head; diskp != NULL; diskp = diskp->next) { if ((strcasecmp(diskp->host->hostname, dump_hostname) == 0) - && (strcmp(diskp->name, disk_name) == 0)) + && (strcmp(diskp->name, disk_name) == 0)) { break; + } + } - if (diskp == NULL) - { + if (diskp == NULL) { reply(501, "Couldn't find host/disk in disk file."); return -1; } @@ -790,35 +1100,26 @@ int are_dumps_compressed P((void)) return 0; } -int main(argc, argv) -int argc; -char **argv; +int +main( + int argc, + char ** argv) { char *line = NULL, *part = NULL; char *s, *fp; int ch; char *cmd_undo, cmd_undo_ch; - int i; + socklen_t socklen; struct sockaddr_in his_addr; struct hostent *his_name; - char *arg; + char *arg = NULL; char *cmd; - int len; - int fd; + size_t len; int user_validated = 0; char *errstr = NULL; - char *pgm = "amindexd"; /* in case argv[0] is not set */ - - for(fd = 3; fd < FD_SETSIZE; fd++) { - /* - * Make sure nobody spoofs us with a lot of extra open files - * that would cause an open we do to get a very high file - * descriptor, which in turn might be used as an index into - * an array (e.g. an fd_set). - */ - close(fd); - } + char *pgm = "amindexd"; /* in case argv[0] is not set */ + safe_fd(DATA_FD_OFFSET, 2); safe_cd(); /* @@ -837,6 +1138,9 @@ char **argv; set_pname(pgm); + /* Don't die when child closes pipe */ + signal(SIGPIPE, SIG_IGN); + #ifdef FORCE_USERID /* we'd rather not run as root */ @@ -844,20 +1148,26 @@ char **argv; if(geteuid() == 0) { if(client_uid == (uid_t) -1) { error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN); + /*NOTREACHED*/ } + /*@ignore@*/ initgroups(CLIENT_LOGIN, client_gid); + /*@end@*/ setgid(client_gid); setuid(client_uid); } #endif /* FORCE_USERID */ - dbopen(); - startclock(); + dbopen(DBG_SUBDIR_SERVER); dbprintf(("%s: version %s\n", get_pname(), version())); - if (! (argc >= 1 && argv != NULL && argv[0] != NULL)) { + if(argv == NULL) { + error("argv == NULL\n"); + } + + if (! (argc >= 1 && argv[0] != NULL)) { dbprintf(("%s: WARNING: argv[0] not defined: check inetd.conf\n", debug_prefix_time(NULL))); } @@ -880,6 +1190,21 @@ char **argv; argv++; } + if(argc > 0 && strcmp(*argv, "amandad") == 0) { + from_amandad = 1; + argc--; + argv++; + if(argc > 0) { + amandad_auth = *argv; + argc--; + argv++; + } + } + else { + from_amandad = 0; + safe_fd(-1, 0); + } + if (argc > 0) { config_name = stralloc(*argv); config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL); @@ -887,9 +1212,11 @@ char **argv; argv++; } - if(gethostname(local_hostname, sizeof(local_hostname)-1) == -1) + if(gethostname(local_hostname, SIZEOF(local_hostname)-1) == -1) { error("gethostname: %s", strerror(errno)); - local_hostname[sizeof(local_hostname)-1] = '\0'; + /*NOTREACHED*/ + } + local_hostname[SIZEOF(local_hostname)-1] = '\0'; /* now trim domain off name */ s = local_hostname; @@ -897,47 +1224,106 @@ char **argv; while(ch && ch != '.') ch = *s++; s[-1] = '\0'; - if(amindexd_debug) { - /* - * Fake the remote address as the local address enough to get - * through the security check. - */ - his_name = gethostbyname(local_hostname); - if(his_name == NULL) { - error("gethostbyname(%s) failed\n", local_hostname); + + if(from_amandad == 0) { + if(amindexd_debug) { + /* + * Fake the remote address as the local address enough to get + * through the security check. + */ + his_name = gethostbyname(local_hostname); + if(his_name == NULL) { + error("gethostbyname(%s) failed\n", local_hostname); + /*NOTREACHED*/ + } + assert((sa_family_t)his_name->h_addrtype == (sa_family_t)AF_INET); + his_addr.sin_family = (sa_family_t)his_name->h_addrtype; + his_addr.sin_port = (in_port_t)htons(0); + memcpy((void *)&his_addr.sin_addr.s_addr, + (void *)his_name->h_addr_list[0], + (size_t)his_name->h_length); + } else { + /* who are we talking to? */ + socklen = sizeof (his_addr); + if (getpeername(0, (struct sockaddr *)&his_addr, &socklen) == -1) + error("getpeername: %s", strerror(errno)); } - assert(his_name->h_addrtype == AF_INET); - his_addr.sin_family = his_name->h_addrtype; - his_addr.sin_port = htons(0); - memcpy((char *)&his_addr.sin_addr.s_addr, - (char *)his_name->h_addr_list[0], his_name->h_length); - } else { - /* who are we talking to? */ - i = sizeof (his_addr); - if (getpeername(0, (struct sockaddr *)&his_addr, &i) == -1) - error("getpeername: %s", strerror(errno)); - } - if (his_addr.sin_family != AF_INET || ntohs(his_addr.sin_port) == 20) - { - error("connection rejected from %s family %d port %d", - inet_ntoa(his_addr.sin_addr), his_addr.sin_family, - htons(his_addr.sin_port)); + if ((his_addr.sin_family != (sa_family_t)AF_INET) + || (ntohs(his_addr.sin_port) == 20)) { + error("connection rejected from %s family %d port %d", + inet_ntoa(his_addr.sin_addr), his_addr.sin_family, + htons(his_addr.sin_port)); + /*NOTREACHED*/ + } + if ((his_name = gethostbyaddr((char *)&(his_addr.sin_addr), + sizeof(his_addr.sin_addr), + AF_INET)) == NULL) { + error("gethostbyaddr(%s): hostname lookup failed", + inet_ntoa(his_addr.sin_addr)); + /*NOTREACHED*/ + } + fp = s = stralloc(his_name->h_name); + ch = *s++; + while(ch && ch != '.') ch = *s++; + s[-1] = '\0'; + remote_hostname = newstralloc(remote_hostname, fp); + s[-1] = (char)ch; + amfree(fp); + cmdout = stdout; + cmdin = stdin; } - if ((his_name = gethostbyaddr((char *)&(his_addr.sin_addr), - sizeof(his_addr.sin_addr), - AF_INET)) == NULL) { - error("gethostbyaddr(%s): hostname lookup failed", - inet_ntoa(his_addr.sin_addr)); + else { + cmdfdout = DATA_FD_OFFSET + 0; + cmdfdin = DATA_FD_OFFSET + 1; + + /* read the REQ packet */ + for(; (line = agets(stdin)) != NULL; free(line)) { +#define sc "OPTIONS " + if(strncmp(line, sc, sizeof(sc)-1) == 0) { +#undef sc + g_options = parse_g_options(line+8, 1); + if(!g_options->hostname) { + g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1); + gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH); + g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0'; + } + } + } + amfree(line); + + if(amandad_auth && g_options->auth) { + if(strcasecmp(amandad_auth, g_options->auth) != 0) { + printf("ERROR recover program ask for auth=%s while amindexd is configured for '%s'\n", + g_options->auth, amandad_auth); + error("amindexd: ERROR recover program ask for auth=%s while amindexd is configured for '%s'", + g_options->auth, amandad_auth); + /*NOTREACHED*/ + } + } + /* send the REP packet */ + printf("CONNECT MESG %d\n", DATA_FD_OFFSET); + printf("\n"); + fflush(stdin); + fflush(stdout); + fclose(stdin); + fclose(stdout); + + cmdout = fdopen(cmdfdout, "a"); + if (!cmdout) { + error("amindexd: Can't fdopen(cmdfdout): %s", strerror(errno)); + /*NOTREACHED*/ + } + + cmdin = fdopen(cmdfdin, "r"); + if (!cmdin) { + error("amindexd: Can't fdopen(cmdfdin): %s", strerror(errno)); + /*NOTREACHED*/ + } } - fp = s = his_name->h_name; - ch = *s++; - while(ch && ch != '.') ch = *s++; - s[-1] = '\0'; - remote_hostname = newstralloc(remote_hostname, fp); - s[-1] = ch; /* clear these so we can detect when the have not been set by the client */ amfree(dump_hostname); + amfree(qdisk_name); amfree(disk_name); amfree(target_date); @@ -951,13 +1337,14 @@ char **argv; reply(220, "%s AMANDA index server (%s) ready.", local_hostname, version()); + user_validated = from_amandad; + /* a real simple parser since there are only a few commands */ while (1) { /* get a line from the client */ - amfree(line); while(1) { - if((part = agets(stdin)) == NULL) { + if((part = agets(cmdin)) == NULL) { if(errno != 0) { dbprintf(("%s: ? read error: %s\n", debug_prefix_time(NULL), strerror(errno))); @@ -969,7 +1356,7 @@ char **argv; dbprintf(("%s: ? unprocessed input:\n", debug_prefix_time(NULL))); dbprintf(("-----\n")); - dbprintf(("%s\n", line)); + dbprintf(("? %s\n", line)); dbprintf(("-----\n")); } amfree(line); @@ -978,13 +1365,9 @@ char **argv; dbclose(); return 1; /* they hung up? */ } - if(line) { - strappend(line, part); - amfree(part); - } else { - line = part; - part = NULL; - } + strappend(line, part); /* Macro: line can be null */ + amfree(part); + if(amindexd_debug) { break; /* we have a whole line */ } @@ -1002,13 +1385,15 @@ char **argv; dbprintf(("%s: > %s\n", debug_prefix_time(NULL), line)); - arg = NULL; + if (arg != NULL) + amfree(arg); s = line; ch = *s++; skip_whitespace(s, ch); if(ch == '\0') { reply(500, "Command not recognised/incorrect: %s", line); + amfree(line); continue; } cmd = s - 1; @@ -1021,28 +1406,30 @@ char **argv; skip_whitespace(s, ch); /* find the argument */ if (ch) { arg = s-1; - skip_non_whitespace(s, ch); + skip_quoted_string(s, ch); + arg = unquote_string(arg); } } amfree(errstr); if (!user_validated && strcmp(cmd, "SECURITY") == 0 && arg) { - user_validated = security_ok(&his_addr, arg, 0, &errstr); + user_validated = check_security(&his_addr, arg, 0, &errstr); if(user_validated) { reply(200, "Access OK"); + amfree(line); continue; } } - if (!user_validated) { - if (errstr) { - reply(500, "Access not allowed: %s", errstr); - } else { - reply(500, "Access not allowed"); + if (!user_validated) { /* don't tell client the reason, just log it to debug log */ + reply(500, "Access not allowed"); + if (errstr) { + dbprintf(("%s: %s\n", debug_prefix_time(NULL), errstr)); } break; } if (strcmp(cmd, "QUIT") == 0) { + amfree(line); break; } else if (strcmp(cmd, "HOST") == 0 && arg) { /* set host we are restoring */ @@ -1051,33 +1438,74 @@ char **argv; { dump_hostname = newstralloc(dump_hostname, arg); reply(200, "Dump host set to %s.", dump_hostname); + amfree(qdisk_name); /* invalidate any value */ amfree(disk_name); /* invalidate any value */ } - s[-1] = ch; + s[-1] = (char)ch; + } else if (strcmp(cmd, "LISTHOST") == 0) { + disk_t *disk, + *diskdup; + int nbhost = 0, + found = 0; + s[-1] = '\0'; + if (config_name == NULL) { + reply(501, "Must set config before listhost"); + } + else { + lreply(200, " List hosts for config %s", config_name); + for (disk = disk_list.head; disk!=NULL; disk = disk->next) { + found = 0; + for (diskdup = disk_list.head; diskdup!=disk; diskdup = diskdup->next) { + if(strcmp(diskdup->host->hostname, disk->host->hostname) == 0) { + found = 1; + break; + } + } + if(!found){ + fast_lreply(201, " %s", disk->host->hostname); + nbhost++; + } + } + if(nbhost > 0) { + reply(200, " List hosts for config %s", config_name); + } + else { + reply(200, "No hosts for config %s", config_name); + } + } + s[-1] = (char)ch; } else if (strcmp(cmd, "DISK") == 0 && arg) { s[-1] = '\0'; if (is_disk_valid(arg) != -1) { disk_name = newstralloc(disk_name, arg); + qdisk_name = quote_string(disk_name); if (build_disk_table() != -1) { - reply(200, "Disk set to %s.", disk_name); + reply(200, "Disk set to %s.", qdisk_name); } } - s[-1] = ch; + s[-1] = (char)ch; } else if (strcmp(cmd, "LISTDISK") == 0) { + char *qname; disk_t *disk; int nbdisk = 0; s[-1] = '\0'; - if (config_name == NULL || dump_hostname == NULL) { + if (config_name == NULL) { reply(501, "Must set config, host before listdisk"); } + else if (dump_hostname == NULL) { + reply(501, "Must set host before listdisk"); + } else if(arg) { lreply(200, " List of disk for device %s on host %s", arg, dump_hostname); - for (disk = disk_list->head; disk!=NULL; disk = disk->next) { - if(strcmp(disk->host->hostname, dump_hostname) == 0 && - ((disk->device && strcmp(disk->device, arg) == 0) || - (!disk->device && strcmp(disk->name, arg) == 0))) { - fast_lreply(201, " %s", disk->name); + for (disk = disk_list.head; disk!=NULL; disk = disk->next) { + + if (strcmp(disk->host->hostname, dump_hostname) == 0 && + ((disk->device && strcmp(disk->device, arg) == 0) || + (!disk->device && strcmp(disk->name, arg) == 0))) { + qname = quote_string(disk->name); + fast_lreply(201, " %s", qname); + amfree(qname); nbdisk++; } } @@ -1092,9 +1520,11 @@ char **argv; } else { lreply(200, " List of disk for host %s", dump_hostname); - for (disk = disk_list->head; disk!=NULL; disk = disk->next) { + for (disk = disk_list.head; disk!=NULL; disk = disk->next) { if(strcmp(disk->host->hostname, dump_hostname) == 0) { - fast_lreply(201, " %s", disk->name); + qname = quote_string(disk->name); + fast_lreply(201, " %s", qname); + amfree(qname); nbdisk++; } } @@ -1105,7 +1535,7 @@ char **argv; reply(200, "No disk for host %s", dump_hostname); } } - s[-1] = ch; + s[-1] = (char)ch; } else if (strcmp(cmd, "SCNF") == 0 && arg) { s[-1] = '\0'; amfree(config_name); @@ -1114,13 +1544,14 @@ char **argv; config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL); if (is_config_valid(arg) != -1) { amfree(dump_hostname); /* invalidate any value */ + amfree(qdisk_name); /* invalidate any value */ amfree(disk_name); /* invalidate any value */ reply(200, "Config set to %s.", config_name); } else { amfree(config_name); amfree(config_dir); } - s[-1] = ch; + s[-1] = (char)ch; } else if (strcmp(cmd, "FEATURES") == 0 && arg) { char *our_feature_string = NULL; char *their_feature_string = NULL; @@ -1129,17 +1560,17 @@ char **argv; am_release_feature_set(their_features); our_features = am_init_feature_set(); our_feature_string = am_feature_to_string(our_features); - their_feature_string = newstralloc(target_date, arg); + their_feature_string = newstralloc(their_feature_string, arg); their_features = am_string_to_feature(their_feature_string); reply(200, "FEATURES %s", our_feature_string); amfree(our_feature_string); amfree(their_feature_string); - s[-1] = ch; + s[-1] = (char)ch; } else if (strcmp(cmd, "DATE") == 0 && arg) { s[-1] = '\0'; target_date = newstralloc(target_date, arg); reply(200, "Working date set to %s.", target_date); - s[-1] = ch; + s[-1] = (char)ch; } else if (strcmp(cmd, "DHST") == 0) { (void)disk_history_list(); } else if (strcmp(cmd, "OISD") == 0 && arg) { @@ -1149,7 +1580,7 @@ char **argv; } else if (strcmp(cmd, "OLSD") == 0 && arg) { (void)opaque_ls(arg,0); } else if (strcmp(cmd, "ORLD") == 0 && arg) { - (void)opaque_ls(arg,1); + (void)opaque_ls(arg, 1); } else if (strcmp(cmd, "TAPE") == 0) { (void)tapedev_is(); } else if (strcmp(cmd, "DCMP") == 0) { @@ -1158,12 +1589,92 @@ char **argv; *cmd_undo = cmd_undo_ch; /* restore the command line */ reply(500, "Command not recognised/incorrect: %s", cmd); } + amfree(line); } - amfree(line); - + amfree(arg); + uncompress_remove = remove_files(uncompress_remove); free_find_result(&output_find); reply(200, "Good bye."); dbclose(); return 0; } + +static char * +amindexd_nicedate( + char * datestamp) +{ + static char nice[20]; + int year, month, day; + int hours, minutes, seconds; + char date[9], atime[7]; + int numdate, numtime; + + strncpy(date, datestamp, 8); + date[8] = '\0'; + numdate = atoi(date); + year = numdate / 10000; + month = (numdate / 100) % 100; + day = numdate % 100; + + if(strlen(datestamp) <= 8) { + snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d", + year, month, day); + } + else { + strncpy(atime, &(datestamp[8]), 6); + atime[6] = '\0'; + numtime = atoi(atime); + hours = numtime / 10000; + minutes = (numtime / 100) % 100; + seconds = numtime % 100; + + snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d-%02d-%02d-%02d", + year, month, day, hours, minutes, seconds); + } + + return nice; +} + +static int +cmp_date( + const char * date1, + const char * date2) +{ + return strncmp(date1, date2, strlen(date2)); +} + +static char * +clean_backslash( + char *line) +{ + char *s = line, *s1, *s2; + char *p = line; + int i; + + while(*s != '\0') { + if (*s == '\\') { + s++; + s1 = s+1; + s2 = s+2; + if (*s != '\0' && isdigit(*s) && + *s1 != '\0' && isdigit(*s1) && + *s2 != '\0' && isdigit(*s2)) { + /* this is \000, an octal value */ + i = ((*s)-'0')*64 + ((*s1)-'0')*8 + ((*s2)-'0'); + *p++ = i; + s += 3; + } else if (*s == '\\') { /* we remove one / */ + *p++ = *s++; + } else { /* we keep the / */ + *p++ = '\\'; + *p++ = *s++; + } + } else { + *p++ = *s++; + } + } + *p = '\0'; + + return line; +}