Imported Upstream version 3.3.0
[debian/amanda] / server-src / server_util.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1999 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: server_util.c,v 1.17 2006/05/25 01:47:20 johnfranks Exp $
28  *
29  */
30
31 #include "amanda.h"
32 #include "server_util.h"
33 #include "arglist.h"
34 #include "logfile.h"
35 #include "util.h"
36 #include "conffile.h"
37 #include "diskfile.h"
38 #include "pipespawn.h"
39 #include "conffile.h"
40 #include "infofile.h"
41 #include "sys/wait.h"
42
43 const char *cmdstr[] = {
44     "BOGUS", "QUIT", "QUITTING", "DONE", "PARTIAL", 
45     "START", "FILE-DUMP", "PORT-DUMP", "CONTINUE", "ABORT",/* dumper cmds */
46     "FAILED", "TRY-AGAIN", "NO-ROOM", "RQ-MORE-DISK",   /* dumper results */
47     "ABORT-FINISHED", "BAD-COMMAND",                    /* dumper results */
48     "START-TAPER", "FILE-WRITE", "NEW-TAPE", "NO-NEW-TAPE",
49      
50     "PARTDONE", "PORT-WRITE", "DUMPER-STATUS",              /* taper cmds */
51     "PORT", "TAPE-ERROR", "TAPER-OK",                    /* taper results */
52     "REQUEST-NEW-TAPE", "DIRECTTCP-PORT", "TAKE-SCRIBE-FROM",
53     "START-SCAN", "LAST_TOK",
54     NULL
55 };
56
57
58 struct cmdargs *
59 getcmd(void)
60 {
61     char *line;
62     cmd_t cmd_i;
63     struct cmdargs *cmdargs = g_new0(struct cmdargs, 1);
64
65     if (isatty(0)) {
66         g_printf("%s> ", get_pname());
67         fflush(stdout);
68         line = agets(stdin);
69     } else {
70         line = agets(stdin);
71     }
72     if (line == NULL) {
73         line = stralloc("QUIT");
74     }
75
76     dbprintf(_("getcmd: %s\n"), line);
77
78     cmdargs->argv = split_quoted_strings(line);
79     cmdargs->argc = g_strv_length(cmdargs->argv);
80     cmdargs->cmd = BOGUS;
81
82     amfree(line);
83
84     if (cmdargs->argc < 1) {
85         return cmdargs;
86     }
87
88     for(cmd_i=BOGUS; cmdstr[cmd_i] != NULL; cmd_i++)
89         if(strcmp(cmdargs->argv[0], cmdstr[cmd_i]) == 0) {
90             cmdargs->cmd = cmd_i;
91             return cmdargs;
92         }
93     return cmdargs;
94 }
95
96 struct cmdargs *
97 get_pending_cmd(void)
98 {
99     SELECT_ARG_TYPE ready;
100     struct timeval  to;
101     int             nfound;
102
103     FD_ZERO(&ready);
104     FD_SET(0, &ready);
105     to.tv_sec = 0;
106     to.tv_usec = 0;
107
108     nfound = select(1, &ready, NULL, NULL, &to);
109     if (nfound && FD_ISSET(0, &ready)) {
110         return getcmd();
111     } else {
112         return NULL;
113     }
114 }
115
116 void
117 free_cmdargs(
118     struct cmdargs *cmdargs)
119 {
120     if (!cmdargs)
121         return;
122     if (cmdargs->argv)
123         g_strfreev(cmdargs->argv);
124     g_free(cmdargs);
125 }
126
127 printf_arglist_function1(void putresult, cmd_t, result, const char *, format)
128 {
129     va_list argp;
130
131     arglist_start(argp, format);
132     dbprintf(_("putresult: %d %s\n"), result, cmdstr[result]);
133     g_printf("%s ", cmdstr[result]);
134     g_vprintf(format, argp);
135     fflush(stdout);
136     arglist_end(argp);
137 }
138
139 char *
140 amhost_get_security_conf(
141     char *      string,
142     void *      arg)
143 {
144     if(!string || !*string)
145         return(NULL);
146
147     if(strcmp(string, "krb5principal")==0)
148         return(getconf_str(CNF_KRB5PRINCIPAL));
149     else if(strcmp(string, "krb5keytab")==0)
150         return(getconf_str(CNF_KRB5KEYTAB));
151
152     if(!arg || !((am_host_t *)arg)->disks) return(NULL);
153
154     if(strcmp(string, "amandad_path")==0)
155         return ((am_host_t *)arg)->disks->amandad_path;
156     else if(strcmp(string, "client_username")==0)
157         return ((am_host_t *)arg)->disks->client_username;
158     else if(strcmp(string, "client_port")==0)
159         return ((am_host_t *)arg)->disks->client_port;
160     else if(strcmp(string, "ssh_keys")==0)
161         return ((am_host_t *)arg)->disks->ssh_keys;
162
163     return(NULL);
164 }
165
166 int check_infofile(
167     char        *infodir,
168     disklist_t  *dl,
169     char       **errmsg)
170 {
171     disk_t      *dp, *diskp;
172     char        *hostinfodir, *old_hostinfodir, *Xhostinfodir;
173     char        *diskdir,     *old_diskdir,     *Xdiskdir;
174     char        *infofile,    *old_infofile,    *Xinfofile;
175     struct stat  statbuf;
176     int other_dle_match;
177
178     if (stat(infodir, &statbuf) != 0) {
179         return 0;
180     }
181
182     for (dp = dl->head; dp != NULL; dp = dp->next) {
183         hostinfodir = sanitise_filename(dp->host->hostname);
184         diskdir     = sanitise_filename(dp->name);
185         infofile = vstralloc(infodir, "/", hostinfodir, "/", diskdir,
186                              "/info", NULL);
187         if (stat(infofile, &statbuf) == -1 && errno == ENOENT) {
188             old_hostinfodir = old_sanitise_filename(dp->host->hostname);
189             old_diskdir     = old_sanitise_filename(dp->name);
190             old_infofile    = vstralloc(infodir, old_hostinfodir, "/",
191                                         old_diskdir, "/info", NULL);
192             if (stat(old_infofile, &statbuf) == 0) {
193                 other_dle_match = 0;
194                 diskp = dl->head;
195                 while (diskp != NULL) {
196                     Xhostinfodir = sanitise_filename(diskp->host->hostname);
197                     Xdiskdir     = sanitise_filename(diskp->name);
198                     Xinfofile = vstralloc(infodir, "/", Xhostinfodir, "/",
199                                           Xdiskdir, "/info", NULL);
200                     if (strcmp(old_infofile, Xinfofile) == 0) {
201                         other_dle_match = 1;
202                         diskp = NULL;
203                     }
204                     else {
205                         diskp = diskp->next;
206                     }
207                 }
208                 if (other_dle_match == 0) {
209                     if(mkpdir(infofile, (mode_t)0755, (uid_t)-1,
210                               (gid_t)-1) == -1) {
211                         *errmsg = vstralloc("Can't create directory for ",
212                                             infofile, NULL);
213                         return -1;
214                     }
215                     if(copy_file(infofile, old_infofile, errmsg) == -1) 
216                         return -1;
217                 }
218             }
219             amfree(old_hostinfodir);
220             amfree(old_diskdir);
221             amfree(old_infofile);
222         }
223         amfree(diskdir);
224         amfree(hostinfodir);
225         amfree(infofile);
226     }
227     return 0;
228 }
229
230 void
231 run_server_script(
232     pp_script_t  *pp_script,
233     execute_on_t  execute_on,
234     char         *config,
235     disk_t       *dp,
236     int           level)
237 {
238     pid_t      scriptpid;
239     int        scriptin, scriptout, scripterr;
240     char      *cmd;
241     char      *command = NULL;
242     GPtrArray *argv_ptr = g_ptr_array_new();
243     FILE      *streamout;
244     char      *line;
245     char      *plugin;
246     char       level_number[NUM_STR_SIZE];
247     struct stat cmd_stat;
248     int         result;
249
250     if ((pp_script_get_execute_on(pp_script) & execute_on) == 0)
251         return;
252     if (pp_script_get_execute_where(pp_script) != ES_SERVER)
253         return;
254
255     plugin = pp_script_get_plugin(pp_script);
256
257     cmd = vstralloc(APPLICATION_DIR, "/", plugin, NULL);
258     result = stat(cmd, &cmd_stat);
259     if (result == -1) {
260         dbprintf("Can't stat script '%s': %s\n", cmd, strerror(errno));
261         amfree(cmd);
262         cmd = vstralloc(get_config_dir(), "/application/", plugin, NULL);
263         result = stat(cmd, &cmd_stat);
264         if (result == -1) {
265             dbprintf("Can't stat script '%s': %s\n", cmd, strerror(errno));
266             amfree(cmd);
267             cmd = vstralloc(CONFIG_DIR, "/application/", plugin, NULL);
268             result = stat(cmd, &cmd_stat);
269             if (result == -1) {
270                 dbprintf("Can't stat script '%s': %s\n", cmd, strerror(errno));
271                 amfree(cmd);
272                 cmd = vstralloc(APPLICATION_DIR, "/", plugin, NULL);
273             }
274         }
275     }
276
277     g_ptr_array_add(argv_ptr, stralloc(plugin));
278
279     switch (execute_on) {
280     case EXECUTE_ON_PRE_AMCHECK:
281         command = "PRE-AMCHECK";
282         break;
283     case EXECUTE_ON_PRE_DLE_AMCHECK:
284         command = "PRE-DLE-AMCHECK";
285         break;
286     case EXECUTE_ON_PRE_HOST_AMCHECK:
287         command = "PRE-HOST-AMCHECK";
288         break;
289     case EXECUTE_ON_POST_AMCHECK:
290         command = "POST-AMCHECK";
291         break;
292     case EXECUTE_ON_POST_DLE_AMCHECK:
293         command = "POST-DLE-AMCHECK";
294         break;
295     case EXECUTE_ON_POST_HOST_AMCHECK:
296         command = "POST-HOST-AMCHECK";
297         break;
298     case EXECUTE_ON_PRE_ESTIMATE:
299         command = "PRE-ESTIMATE";
300         break;
301     case EXECUTE_ON_PRE_DLE_ESTIMATE:
302         command = "PRE-DLE-ESTIMATE";
303         break;
304     case EXECUTE_ON_PRE_HOST_ESTIMATE:
305         command = "PRE-HOST-ESTIMATE";
306         break;
307     case EXECUTE_ON_POST_ESTIMATE:
308         command = "POST-ESTIMATE";
309         break;
310     case EXECUTE_ON_POST_DLE_ESTIMATE:
311         command = "POST-DLE-ESTIMATE";
312         break;
313     case EXECUTE_ON_POST_HOST_ESTIMATE:
314         command = "POST-HOST-ESTIMATE";
315         break;
316     case EXECUTE_ON_PRE_BACKUP:
317         command = "PRE-BACKUP";
318         break;
319     case EXECUTE_ON_PRE_DLE_BACKUP:
320         command = "PRE-DLE-BACKUP";
321         break;
322     case EXECUTE_ON_PRE_HOST_BACKUP:
323         command = "PRE-HOST-BACKUP";
324         break;
325     case EXECUTE_ON_POST_BACKUP:
326         command = "POST-BACKUP";
327         break;
328     case EXECUTE_ON_POST_DLE_BACKUP:
329         command = "POST-DLE-BACKUP";
330         break;
331     case EXECUTE_ON_POST_HOST_BACKUP:
332         command = "POST-HOST-BACKUP";
333         break;
334     case EXECUTE_ON_PRE_RECOVER:
335     case EXECUTE_ON_POST_RECOVER:
336     case EXECUTE_ON_PRE_LEVEL_RECOVER:
337     case EXECUTE_ON_POST_LEVEL_RECOVER:
338     case EXECUTE_ON_INTER_LEVEL_RECOVER:
339         {
340              // ERROR these script can't be executed on server.
341              return;
342         }
343     }
344
345     g_ptr_array_add(argv_ptr, stralloc(command));
346     g_ptr_array_add(argv_ptr, stralloc("--execute-where"));
347     g_ptr_array_add(argv_ptr, stralloc("server"));
348
349     if (config) {
350         g_ptr_array_add(argv_ptr, stralloc("--config"));
351         g_ptr_array_add(argv_ptr, stralloc(config));
352     }
353     if (dp->host->hostname) {
354         g_ptr_array_add(argv_ptr, stralloc("--host"));
355         g_ptr_array_add(argv_ptr, stralloc(dp->host->hostname));
356     }
357     if (dp->name) {
358         g_ptr_array_add(argv_ptr, stralloc("--disk"));
359         g_ptr_array_add(argv_ptr, stralloc(dp->name));
360     }
361     if (dp->device) {
362         g_ptr_array_add(argv_ptr, stralloc("--device"));
363         g_ptr_array_add(argv_ptr, stralloc(dp->device));
364     }
365     if (level >= 0) {
366         g_snprintf(level_number, SIZEOF(level_number), "%d", level);
367         g_ptr_array_add(argv_ptr, stralloc("--level"));
368         g_ptr_array_add(argv_ptr, stralloc(level_number));
369     }
370
371     property_add_to_argv(argv_ptr, pp_script_get_property(pp_script));
372     g_ptr_array_add(argv_ptr, NULL);
373
374     scripterr = fileno(stderr);
375     scriptpid = pipespawnv(cmd, STDIN_PIPE|STDOUT_PIPE, 0, &scriptin,
376                            &scriptout, &scripterr,
377                            (char **)argv_ptr->pdata);
378     close(scriptin);
379
380     streamout = fdopen(scriptout, "r");
381     if (streamout) {
382         while((line = agets(streamout)) != NULL) {
383             dbprintf("script: %s\n", line);
384             amfree(line);
385         }
386     }
387     fclose(streamout);
388     waitpid(scriptpid, NULL, 0);
389     g_ptr_array_free_full(argv_ptr);
390     amfree(cmd);
391 }
392
393
394 void
395 run_server_dle_scripts(
396     execute_on_t  execute_on,
397     char         *config,
398     disk_t       *dp,
399     int           level)
400 {
401     identlist_t pp_scriptlist;
402
403     for (pp_scriptlist = dp->pp_scriptlist; pp_scriptlist != NULL;
404          pp_scriptlist = pp_scriptlist->next) {
405         pp_script_t *pp_script = lookup_pp_script((char *)pp_scriptlist->data);
406         g_assert(pp_script != NULL);
407         run_server_script(pp_script, execute_on, config, dp, level);
408     }
409 }
410
411 void
412 run_server_host_scripts(
413     execute_on_t  execute_on,
414     char         *config,
415     am_host_t    *hostp)
416 {
417     identlist_t pp_scriptlist;
418     disk_t *dp;
419
420     GHashTable* executed = g_hash_table_new_full(g_str_hash, g_str_equal,
421                                                  NULL, NULL);
422     for (dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
423         if (dp->todo) {
424             for (pp_scriptlist = dp->pp_scriptlist; pp_scriptlist != NULL;
425                  pp_scriptlist = pp_scriptlist->next) {
426                 int todo = 1;
427                 pp_script_t *pp_script = lookup_pp_script((char *)pp_scriptlist->data);
428                 g_assert(pp_script != NULL);
429                 if (pp_script_get_single_execution(pp_script)) {
430                     todo = g_hash_table_lookup(executed,
431                                                pp_script_get_plugin(pp_script))
432                            == NULL;
433                 }
434                 if (todo) {
435                     run_server_script(pp_script, execute_on, config, dp, -1);
436                     if (pp_script_get_single_execution(pp_script)) {
437                         g_hash_table_insert(executed,
438                                             pp_script_get_plugin(pp_script),
439                                             GINT_TO_POINTER(1));
440                     }
441                 }
442             }
443         }
444     }
445
446     g_hash_table_destroy(executed);
447 }
448
449 void
450 run_server_global_scripts(
451     execute_on_t  execute_on,
452     char         *config)
453 {
454     identlist_t  pp_scriptlist;
455     disk_t      *dp;
456     am_host_t   *host;
457
458     GHashTable* executed = g_hash_table_new_full(g_str_hash, g_str_equal,
459                                                  NULL, NULL);
460     for (host = get_hostlist(); host != NULL; host = host->next) {
461         for (dp = host->disks; dp != NULL; dp = dp->hostnext) {
462             if (dp->todo) {
463                 for (pp_scriptlist = dp->pp_scriptlist; pp_scriptlist != NULL;
464                      pp_scriptlist = pp_scriptlist->next) {
465                     int todo = 1;
466                     pp_script_t *pp_script =
467                                  lookup_pp_script((char *)pp_scriptlist->data);
468                     g_assert(pp_script != NULL);
469                     if (pp_script_get_single_execution(pp_script)) {
470                         todo = g_hash_table_lookup(executed,
471                                 pp_script_get_plugin(pp_script)) == NULL;
472                     }
473                     if (todo) {
474                         run_server_script(pp_script, execute_on, config,
475                                           dp, -1);
476                         if (pp_script_get_single_execution(pp_script)) {
477                             g_hash_table_insert(executed,
478                                         pp_script_get_plugin(pp_script),
479                                         GINT_TO_POINTER(1));
480                         }
481                     }
482                 }
483             }
484         }
485     }
486     g_hash_table_destroy(executed);
487 }
488
489 void
490 run_amcleanup(
491     char *config_name)
492 {
493     pid_t amcleanup_pid;
494     char *amcleanup_program;
495     char *amcleanup_options[4];
496
497     switch(amcleanup_pid = fork()) {
498         case -1:
499             return;
500             break;
501         case  0: /* child process */
502             amcleanup_program = vstralloc(sbindir, "/", "amcleanup", NULL);
503             amcleanup_options[0] = amcleanup_program;
504             amcleanup_options[1] = "-p";
505             amcleanup_options[2] = config_name;
506             amcleanup_options[3] = NULL;
507             execve(amcleanup_program, amcleanup_options, safe_env());
508             error("exec %s: %s", amcleanup_program, strerror(errno));
509             /*NOTREACHED*/
510         default:
511             break;
512     }
513     waitpid(amcleanup_pid, NULL, 0);
514 }
515
516 char *
517 get_master_process(
518     char *logfile)
519 {
520     FILE *log;
521     char line[1024];
522     char *s, ch;
523     char *process_name;
524
525     log = fopen(logfile, "r");
526     if (!log)
527         return stralloc("UNKNOWN");
528
529     while(fgets(line, 1024, log)) {
530         if (strncmp_const(line, "INFO ") == 0) {
531             s = line+5;
532             ch = *s++;
533             process_name = s-1;
534             skip_non_whitespace(s, ch);
535             s[-1] = '\0';
536             skip_whitespace(s, ch);
537             skip_non_whitespace(s, ch);
538             s[-1] = '\0';
539             skip_whitespace(s, ch);
540             if (strncmp_const(s-1, "pid ") == 0) {
541                 process_name = stralloc(process_name);
542                 fclose(log);
543                 return process_name;
544             }
545         }
546     }
547     fclose(log);
548     return stralloc("UNKNOWN");
549 }
550
551
552 gint64
553 internal_server_estimate(
554     disk_t *dp,
555     info_t *info,
556     int     level,
557     int    *stats)
558 {
559     int    j;
560     gint64 size = 0;
561
562     *stats = 0;
563
564     if (level == 0) { /* use latest level 0, should do extrapolation */
565         gint64 est_size = (gint64)0;
566         int nb_est = 0;
567
568         for (j=NB_HISTORY-2; j>=0; j--) {
569             if (info->history[j].level == 0) {
570                 if (info->history[j].size < (gint64)0) continue;
571                 est_size = info->history[j].size;
572                 nb_est++;
573             }
574         }
575         if (nb_est > 0) {
576             size = est_size;
577             *stats = 1;
578         } else if (info->inf[level].size > (gint64)1000) { /* stats */
579             size = info->inf[level].size;
580             *stats = 1;
581         } else {
582             char *conf_tapetype = getconf_str(CNF_TAPETYPE);
583             tapetype_t *tape = lookup_tapetype(conf_tapetype);
584             size = (gint64)1000000;
585             if (size > tapetype_get_length(tape)/2)
586                 size = tapetype_get_length(tape)/2;
587             *stats = 0;
588         }
589     } else if (level == info->last_level) {
590         /* means of all X day at the same level */
591         #define NB_DAY 30
592         int nb_day = 0;
593         gint64 est_size_day[NB_DAY];
594         int nb_est_day[NB_DAY];
595
596         for (j=0; j<NB_DAY; j++) {
597             est_size_day[j] = (gint64)0;
598             nb_est_day[j] = 0;
599         }
600
601         for (j=NB_HISTORY-2; j>=0; j--) {
602             if (info->history[j].level <= 0) continue;
603             if (info->history[j].size < (gint64)0) continue;
604             if (info->history[j].level == info->history[j+1].level) {
605                 if (nb_day <NB_DAY-1) nb_day++;
606                 est_size_day[nb_day] += info->history[j].size;
607                 nb_est_day[nb_day]++;
608             } else {
609                 nb_day=0;
610             }
611         }
612         nb_day = info->consecutive_runs + 1;
613         if (nb_day > NB_DAY-1) nb_day = NB_DAY-1;
614
615         while (nb_day > 0 && nb_est_day[nb_day] == 0) nb_day--;
616
617         if (nb_est_day[nb_day] > 0) {
618             size = est_size_day[nb_day] / (gint64)nb_est_day[nb_day];
619             *stats = 1;
620         }
621         else if (info->inf[level].size > (gint64)1000) { /* stats */
622             size = info->inf[level].size;
623             *stats = 1;
624         }
625         else {
626             int level0_stat;
627             gint64 level0_size;
628             char *conf_tapetype = getconf_str(CNF_TAPETYPE);
629             tapetype_t *tape = lookup_tapetype(conf_tapetype);
630
631             level0_size = internal_server_estimate(dp, info, 0, &level0_stat);
632             size = (gint64)10000;
633             if (size > tapetype_get_length(tape)/2)
634                 size = tapetype_get_length(tape)/2;
635             if (level0_size > 0 && dp->strategy != DS_NOFULL) {
636                 if (size > level0_size/2)
637                     size = level0_size/2;
638             }
639             *stats = 0;
640         }
641     }
642     else if (level == info->last_level + 1) {
643         /* means of all first day at a new level */
644         gint64 est_size = (gint64)0;
645         int nb_est = 0;
646
647         for (j=NB_HISTORY-2; j>=0; j--) {
648             if (info->history[j].level <= 0) continue;
649             if (info->history[j].size < (gint64)0) continue;
650             if (info->history[j].level == info->history[j+1].level + 1 ) {
651                 est_size += info->history[j].size;
652                 nb_est++;
653             }
654         }
655         if (nb_est > 0) {
656             size = est_size / (gint64)nb_est;
657             *stats = 1;
658         } else if (info->inf[level].size > (gint64)1000) { /* stats */
659             size = info->inf[level].size;
660             *stats = 1;
661         } else {
662             int level0_stat;
663             gint64 level0_size;
664             char *conf_tapetype = getconf_str(CNF_TAPETYPE);
665             tapetype_t *tape = lookup_tapetype(conf_tapetype);
666
667             level0_size = internal_server_estimate(dp, info, 0, &level0_stat);
668             size = (gint64)100000;
669             if (size > tapetype_get_length(tape)/2)
670                 size = tapetype_get_length(tape)/2;
671             if (level0_size > 0 && dp->strategy != DS_NOFULL) {
672                 if (size > level0_size/2)
673                     size = level0_size/2;
674             }
675             *stats = 0;
676         }
677     } else {
678         char *conf_tapetype = getconf_str(CNF_TAPETYPE);
679         tapetype_t *tape = lookup_tapetype(conf_tapetype);
680         size = (gint64)100000;
681         if (size > tapetype_get_length(tape)/2)
682             size = tapetype_get_length(tape)/2;
683     }
684
685     return size;
686 }
687
688 int
689 server_can_do_estimate(
690     disk_t *dp,
691     info_t *info,
692     int     level)
693 {
694     gint64  size;
695     int     stats;
696
697     size = internal_server_estimate(dp, info, level, &stats);
698     return stats;
699 }
700