Imported Upstream version 2.6.1
[debian/amanda] / server-src / chunker.c
index 5e6c1ef45c7619ec9bb761fe871494d8714a5e5f..caaf0bfb02b6f4955fcb50cc667f94cd73224427 100644 (file)
@@ -37,7 +37,6 @@
 #include "protocol.h"
 #include "security.h"
 #include "stream.h"
-#include "token.h"
 #include "version.h"
 #include "fileheader.h"
 #include "amfeatures.h"
@@ -93,7 +92,7 @@ static char *options = NULL;
 static char *progname = NULL;
 static int level;
 static char *dumpdate = NULL;
-static int command_in_transit;
+static struct cmdargs *command_in_transit = NULL;
 static char *chunker_timestamp = NULL;
 
 static dumpfile_t file;
@@ -114,18 +113,17 @@ main(
     char **    argv)
 {
     static struct databuf db;
-    struct cmdargs cmdargs;
-    cmd_t cmd;
+    struct cmdargs *cmdargs;
     int infd;
     char *q = NULL;
     char *filename = NULL;
-    char *qfilename = NULL;
     off_t chunksize, use;
     times_t runtime;
     am_feature_t *their_features = NULL;
     int a;
     config_overwrites_t *cfg_ovr = NULL;
     char *cfg_opt = NULL;
+    char *m;
 
     /*
      * Configure program for internationalization:
@@ -153,16 +151,23 @@ main(
     if (argc > 1) 
        cfg_opt = argv[1];
 
-    config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD | CONFIG_INIT_FATAL,
-               cfg_opt);
+    config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD, cfg_opt);
     apply_config_overwrites(cfg_ovr);
 
+    if (config_errors(NULL) >= CFGERR_WARNINGS) {
+       config_print_errors();
+       if (config_errors(NULL) >= CFGERR_ERRORS) {
+           g_critical(_("errors processing config file"));
+       }
+    }
+
     safe_cd(); /* do this *after* config_init() */
 
     check_running_as(RUNNING_AS_DUMPUSER);
 
-    dbrename(config_name, DBG_SUBDIR_SERVER);
+    dbrename(get_config_name(), DBG_SUBDIR_SERVER);
 
+    log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid());
     g_fprintf(stderr,
            _("%s: pid %ld executable %s version %s\n"),
            get_pname(), (long) getpid(),
@@ -174,20 +179,21 @@ main(
     signal(SIGPIPE, SIG_IGN);
     signal(SIGCHLD, SIG_IGN);
 
-    cmd = getcmd(&cmdargs);
-    if(cmd == START) {
-       if(cmdargs.argc <= 1)
+    cmdargs = getcmd();
+    if(cmdargs->cmd == START) {
+       if(cmdargs->argc <= 1)
            error(_("error [dumper START: not enough args: timestamp]"));
-       chunker_timestamp = newstralloc(chunker_timestamp, cmdargs.argv[2]);
+       chunker_timestamp = newstralloc(chunker_timestamp, cmdargs->argv[1]);
     }
     else {
+       log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid());
        error(_("Didn't get START command"));
     }
 
 /*    do {*/
-       cmd = getcmd(&cmdargs);
+       cmdargs = getcmd();
 
-       switch(cmd) {
+       switch(cmdargs->cmd) {
        case QUIT:
            break;
 
@@ -206,96 +212,91 @@ main(
             *   use
             *   options
             */
-           cmdargs.argc++;                     /* true count of args */
-           a = 2;
+           a = 1;
 
-           if(a >= cmdargs.argc) {
+           if(a >= cmdargs->argc) {
                error(_("error [chunker PORT-WRITE: not enough args: handle]"));
                /*NOTREACHED*/
            }
-           handle = newstralloc(handle, cmdargs.argv[a++]);
+           handle = newstralloc(handle, cmdargs->argv[a++]);
 
-           if(a >= cmdargs.argc) {
+           if(a >= cmdargs->argc) {
                error(_("error [chunker PORT-WRITE: not enough args: filename]"));
                /*NOTREACHED*/
            }
-           qfilename = newstralloc(qfilename, cmdargs.argv[a++]);
-           if (filename != NULL)
-               amfree(filename);
-           filename = unquote_string(qfilename);
-           amfree(qfilename);
+           filename = newstralloc(filename, cmdargs->argv[a++]);
 
-           if(a >= cmdargs.argc) {
+           if(a >= cmdargs->argc) {
                error(_("error [chunker PORT-WRITE: not enough args: hostname]"));
                /*NOTREACHED*/
            }
-           hostname = newstralloc(hostname, cmdargs.argv[a++]);
+           hostname = newstralloc(hostname, cmdargs->argv[a++]);
 
-           if(a >= cmdargs.argc) {
+           if(a >= cmdargs->argc) {
                error(_("error [chunker PORT-WRITE: not enough args: features]"));
                /*NOTREACHED*/
            }
            am_release_feature_set(their_features);
-           their_features = am_string_to_feature(cmdargs.argv[a++]);
+           their_features = am_string_to_feature(cmdargs->argv[a++]);
 
-           if(a >= cmdargs.argc) {
+           if(a >= cmdargs->argc) {
                error(_("error [chunker PORT-WRITE: not enough args: diskname]"));
                /*NOTREACHED*/
            }
-           qdiskname = newstralloc(qdiskname, cmdargs.argv[a++]);
-           if (diskname != NULL)
-               amfree(diskname);
-           diskname = unquote_string(qdiskname);
+           diskname = newstralloc(diskname, cmdargs->argv[a++]);
+           if (qdiskname)
+               amfree(qdiskname);
+           qdiskname = quote_string(diskname); /* qdiskname is a global */
 
-           if(a >= cmdargs.argc) {
+           if(a >= cmdargs->argc) {
                error(_("error [chunker PORT-WRITE: not enough args: level]"));
                /*NOTREACHED*/
            }
-           level = atoi(cmdargs.argv[a++]);
+           level = atoi(cmdargs->argv[a++]);
 
-           if(a >= cmdargs.argc) {
+           if(a >= cmdargs->argc) {
                error(_("error [chunker PORT-WRITE: not enough args: dumpdate]"));
                /*NOTREACHED*/
            }
-           dumpdate = newstralloc(dumpdate, cmdargs.argv[a++]);
+           dumpdate = newstralloc(dumpdate, cmdargs->argv[a++]);
 
-           if(a >= cmdargs.argc) {
+           if(a >= cmdargs->argc) {
                error(_("error [chunker PORT-WRITE: not enough args: chunksize]"));
                /*NOTREACHED*/
            }
-           chunksize = OFF_T_ATOI(cmdargs.argv[a++]);
+           chunksize = OFF_T_ATOI(cmdargs->argv[a++]);
            chunksize = am_floor(chunksize, (off_t)DISK_BLOCK_KB);
 
-           if(a >= cmdargs.argc) {
+           if(a >= cmdargs->argc) {
                error(_("error [chunker PORT-WRITE: not enough args: progname]"));
                /*NOTREACHED*/
            }
-           progname = newstralloc(progname, cmdargs.argv[a++]);
+           progname = newstralloc(progname, cmdargs->argv[a++]);
 
-           if(a >= cmdargs.argc) {
+           if(a >= cmdargs->argc) {
                error(_("error [chunker PORT-WRITE: not enough args: use]"));
                /*NOTREACHED*/
            }
-           use = am_floor(OFF_T_ATOI(cmdargs.argv[a++]), DISK_BLOCK_KB);
+           use = am_floor(OFF_T_ATOI(cmdargs->argv[a++]), DISK_BLOCK_KB);
 
-           if(a >= cmdargs.argc) {
+           if(a >= cmdargs->argc) {
                error(_("error [chunker PORT-WRITE: not enough args: options]"));
                /*NOTREACHED*/
            }
-           options = newstralloc(options, cmdargs.argv[a++]);
+           options = newstralloc(options, cmdargs->argv[a++]);
 
-           if(a != cmdargs.argc) {
+           if(a != cmdargs->argc) {
                error(_("error [chunker PORT-WRITE: too many args: %d != %d]"),
-                     cmdargs.argc, a);
+                     cmdargs->argc, a);
                /*NOTREACHED*/
            }
 
            if((infd = startup_chunker(filename, use, chunksize, &db)) < 0) {
-               q = squotef(_("[chunker startup failed: %s]"), errstr);
+               q = quote_string(vstrallocf(_("[chunker startup failed: %s]"), errstr));
                putresult(TRYAGAIN, "%s %s\n", handle, q);
-               error("startup_chunker failed");
+               error("startup_chunker failed: %s", errstr);
            }
-           command_in_transit = -1;
+           command_in_transit = NULL;
            if(infd >= 0 && do_chunk(infd, &db)) {
                char kb_str[NUM_STR_SIZE];
                char kps_str[NUM_STR_SIZE];
@@ -309,12 +310,16 @@ main(
                                isnormal(rt) ? (double)dumpsize / rt : 0.0);
                errstr = newvstrallocf(errstr, "sec %s kb %s kps %s",
                                walltime_str(runtime), kb_str, kps_str);
-               q = squotef("[%s]", errstr);
-               if(command_in_transit != -1)
-                   cmd = command_in_transit;
-               else
-                   cmd = getcmd(&cmdargs);
-               switch(cmd) {
+               m = vstrallocf("[%s]", errstr);
+               q = quote_string(m);
+               amfree(m);
+               if(command_in_transit != NULL) {
+                   cmdargs = command_in_transit;
+                   command_in_transit = NULL;
+               } else {
+                   cmdargs = getcmd();
+               }
+               switch(cmdargs->cmd) {
                case DONE:
                    putresult(DONE, "%s %lld %s\n", handle,
                             (long long)(dumpsize - (off_t)headersize), q);
@@ -334,9 +339,11 @@ main(
                    }
                    else {
                        errstr = newvstrallocf(errstr,
-                                       _("dumper returned %s"), cmdstr[cmd]);
+                                       _("dumper returned %s"), cmdstr[cmdargs->cmd]);
                        amfree(q);
-                       q = squotef("[%s]",errstr);
+                       m = vstrallocf("[%s]",errstr);
+                       q = quote_string(m);
+                       amfree(m);
                        putresult(FAILED, "%s %s\n", handle, q);
                        log_add(L_FAIL, "%s %s %s %d [%s]",
                                hostname, qdiskname, chunker_timestamp, level, errstr);
@@ -345,25 +352,25 @@ main(
                }
                amfree(q);
            } else if(infd != -2) {
+               if(q == NULL) {
+                   m = vstrallocf("[%s]", errstr);
+                   q = quote_string(m);
+                   amfree(m);
+               }
                if(!abort_pending) {
-                   if(q == NULL) {
-                       q = squotef("[%s]", errstr);
-                   }
                    putresult(FAILED, "%s %s\n", handle, q);
-                   log_add(L_FAIL, "%s %s %s %d [%s]",
-                           hostname, qdiskname, chunker_timestamp, level, errstr);
-                   amfree(q);
                }
+               log_add(L_FAIL, "%s %s %s %d [%s]",
+                       hostname, qdiskname, chunker_timestamp, level, errstr);
+               amfree(q);
            }
            amfree(filename);
            amfree(db.filename);
            break;
 
        default:
-           if(cmdargs.argc >= 1) {
-               q = squote(cmdargs.argv[1]);
-           } else if(cmdargs.argc >= 0) {
-               q = squote(cmdargs.argv[0]);
+           if(cmdargs->argc >= 1) {
+               q = quote_string(cmdargs->argv[0]);
            } else {
                q = stralloc(_("(no input?)"));
            }
@@ -372,7 +379,9 @@ main(
            break;
        }
 
-/*    } while(cmd != QUIT); */
+/*    } while(cmdargs->cmd != QUIT); */
+
+    log_add(L_INFO, "pid-done %ld", (long)getpid());
 
     amfree(errstr);
     amfree(chunker_timestamp);
@@ -383,6 +392,9 @@ main(
     amfree(dumpdate);
     amfree(progname);
     amfree(options);
+    free_cmdargs(cmdargs);
+    if (command_in_transit)
+       free_cmdargs(command_in_transit);
     am_release_feature_set(their_features);
     their_features = NULL;
 
@@ -417,6 +429,7 @@ startup_chunker(
     }
     data_socket = stream_server(res->ai_family, &data_port, 0,
                                STREAM_BUFSIZE, 0);
+    if (res) freeaddrinfo(res);
 
     if(data_socket < 0) {
        errstr = vstrallocf(_("error creating stream server: %s"), strerror(errno));
@@ -426,6 +439,7 @@ startup_chunker(
     putresult(PORT, "%d\n", data_port);
 
     infd = stream_accept(data_socket, CONNECT_TIMEOUT, 0, STREAM_BUFSIZE);
+    aclose(data_socket);
     if(infd == -1) {
        errstr = vstrallocf(_("error accepting stream: %s"), strerror(errno));
        return -1;
@@ -433,15 +447,18 @@ startup_chunker(
 
     tmp_filename = vstralloc(filename, ".tmp", NULL);
     pc = strrchr(tmp_filename, '/');
+    g_assert(pc != NULL);
     *pc = '\0';
     mkholdingdir(tmp_filename);
     *pc = '/';
     if ((outfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600)) < 0) {
        int save_errno = errno;
-
-       errstr = squotef(_("holding file \"%s\": %s"),
+       char *m = vstrallocf(_("holding file \"%s\": %s"),
                         tmp_filename,
                         strerror(errno));
+
+       errstr = quote_string(m);
+       amfree(m);
        amfree(tmp_filename);
        aclose(infd);
        if(save_errno == ENOSPC) {
@@ -463,7 +480,7 @@ do_chunk(
     int                        infd,
     struct databuf *   db)
 {
-    ssize_t nread;
+    size_t nread;
     char header_buf[DISK_BLOCK_BYTES];
 
     startclock();
@@ -477,23 +494,23 @@ do_chunk(
      * need to save into "file", as well as write out.  Later, the
      * chunk code will rewrite it.
      */
-    nread = fullread(infd, header_buf, SIZEOF(header_buf));
-    if (nread != DISK_BLOCK_BYTES) {
-       if(nread < 0) {
+    nread = full_read(infd, header_buf, SIZEOF(header_buf));
+    if (nread != sizeof(header_buf)) {
+       if(errno != 0) {
            errstr = vstrallocf(_("cannot read header: %s"), strerror(errno));
        } else {
-           errstr = vstrallocf(_("cannot read header: got %zd bytes instead of %d"),
-                               nread,
-                               DISK_BLOCK_BYTES);
+           errstr = vstrallocf(_("cannot read header: got %zd bytes instead of %zd"),
+                               nread, sizeof(header_buf));
        }
        return 0;
     }
     parse_file_header(header_buf, &file, (size_t)nread);
     if(write_tapeheader(db->fd, &file)) {
        int save_errno = errno;
-
-       errstr = squotef(_("write_tapeheader file %s: %s"),
+       char *m = vstrallocf(_("write_tapeheader file %s: %s"),
                         db->filename, strerror(errno));
+       errstr = quote_string(m);
+       amfree(m);
        if(save_errno == ENOSPC) {
            putresult(NO_ROOM, "%s %lld\n", handle, 
                      (long long)(db->use+db->split_size-dumpsize));
@@ -508,7 +525,7 @@ do_chunk(
      * We've written the file header.  Now, just write data until the
      * end.
      */
-    while ((nread = fullread(infd, db->buf,
+    while ((nread = full_read(infd, db->buf,
                             (size_t)(db->datalimit - db->datain))) > 0) {
        db->datain += nread;
        while(db->dataout < db->datain) {
@@ -558,12 +575,11 @@ static int
 databuf_flush(
     struct databuf *   db)
 {
-    struct cmdargs cmdargs;
+    struct cmdargs *cmdargs = NULL;
     int rc = 1;
-    ssize_t written;
+    size_t written;
     off_t left_in_chunk;
     char *arg_filename = NULL;
-    char *qarg_filename = NULL;
     char *new_filename = NULL;
     char *tmp_filename = NULL;
     char sequence[NUM_STR_SIZE];
@@ -588,16 +604,14 @@ databuf_flush(
            /*
             * Probably no more space on this disk.  Request some more.
             */
-           cmd_t cmd;
-
            putresult(RQ_MORE_DISK, "%s\n", handle);
-           cmd = getcmd(&cmdargs);
-           if(command_in_transit == -1 &&
-              (cmd == DONE || cmd == TRYAGAIN || cmd == FAILED)) {
-               command_in_transit = cmd;
-               cmd = getcmd(&cmdargs);
+           cmdargs = getcmd();
+           if(command_in_transit == NULL &&
+              (cmdargs->cmd == DONE || cmdargs->cmd == TRYAGAIN || cmdargs->cmd == FAILED)) {
+               command_in_transit = cmdargs;
+               cmdargs = getcmd();
            }
-           if(cmd == CONTINUE) {
+           if(cmdargs->cmd == CONTINUE) {
                /*
                 * CONTINUE
                 *   serial
@@ -605,34 +619,30 @@ databuf_flush(
                 *   chunksize
                 *   use
                 */
-               cmdargs.argc++;                 /* true count of args */
-               a = 3;
+               a = 2; /* skip CONTINUE and serial */
 
-               if(a >= cmdargs.argc) {
+               if(a >= cmdargs->argc) {
                    error(_("error [chunker CONTINUE: not enough args: filename]"));
                    /*NOTREACHED*/
                }
-               qarg_filename = newstralloc(qarg_filename, cmdargs.argv[a++]);
-               if (arg_filename != NULL)
-                   amfree(arg_filename);
-               arg_filename = unquote_string(qarg_filename);
+               arg_filename = newstralloc(arg_filename, cmdargs->argv[a++]);
 
-               if(a >= cmdargs.argc) {
+               if(a >= cmdargs->argc) {
                    error(_("error [chunker CONTINUE: not enough args: chunksize]"));
                    /*NOTREACHED*/
                }
-               db->chunk_size = OFF_T_ATOI(cmdargs.argv[a++]);
+               db->chunk_size = OFF_T_ATOI(cmdargs->argv[a++]);
                db->chunk_size = am_floor(db->chunk_size, (off_t)DISK_BLOCK_KB);
 
-               if(a >= cmdargs.argc) {
+               if(a >= cmdargs->argc) {
                    error(_("error [chunker CONTINUE: not enough args: use]"));
                    /*NOTREACHED*/
                }
-               db->use = OFF_T_ATOI(cmdargs.argv[a++]);
+               db->use = OFF_T_ATOI(cmdargs->argv[a++]);
 
-               if(a != cmdargs.argc) {
+               if(a != cmdargs->argc) {
                    error(_("error [chunker CONTINUE: too many args: %d != %d]"),
-                         cmdargs.argc, a);
+                         cmdargs->argc, a);
                    /*NOTREACHED*/
                }
 
@@ -662,17 +672,15 @@ databuf_flush(
                     */
                    db->filename = newstralloc(db->filename, arg_filename);
                }
-           } else if(cmd == ABORT) {
+           } else if(cmdargs->cmd == ABORT) {
                abort_pending = 1;
-               errstr = newstralloc(errstr, "ERROR");
+               errstr = newstralloc(errstr, cmdargs->argv[1]);
                putresult(ABORT_FINISHED, "%s\n", handle);
                rc = 0;
                goto common_exit;
            } else {
-               if(cmdargs.argc >= 1) {
-                   q = squote(cmdargs.argv[1]);
-               } else if(cmdargs.argc >= 0) {
-                   q = squote(cmdargs.argv[0]);
+               if(cmdargs->argc >= 1) {
+                   q = quote_string(cmdargs->argv[0]);
                } else {
                    q = stralloc(_("(no input?)"));
                }
@@ -700,12 +708,14 @@ databuf_flush(
                                    ".tmp",
                                    NULL);
        pc = strrchr(tmp_filename, '/');
+        g_assert(pc != NULL); /* Only a problem if db->filename has no /. */
        *pc = '\0';
        mkholdingdir(tmp_filename);
        *pc = '/';
        newfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
        if (newfd == -1) {
            int save_errno = errno;
+           char *m;
 
            if(save_errno == ENOSPC) {
                putresult(NO_ROOM, "%s %lld\n", handle, 
@@ -714,9 +724,11 @@ databuf_flush(
                db->split_size = dumpsize;
                continue;
            }
-           errstr = squotef(_("creating chunk holding file \"%s\": %s"),
+           m = vstrallocf(_("creating chunk holding file \"%s\": %s"),
                             tmp_filename,
                             strerror(errno));
+           errstr = quote_string(m);
+           amfree(m);
            aclose(db->fd);
            rc = 0;
            goto common_exit;
@@ -726,6 +738,7 @@ databuf_flush(
        file.cont_filename[0] = '\0';
        if(write_tapeheader(newfd, &file)) {
            int save_errno = errno;
+           char *m;
 
            aclose(newfd);
            if(save_errno == ENOSPC) {
@@ -735,9 +748,11 @@ databuf_flush(
                db->split_size = dumpsize;
                continue;
            }
-           errstr = squotef(_("write_tapeheader file %s: %s"),
+           m = vstrallocf(_("write_tapeheader file %s: %s"),
                             tmp_filename,
                             strerror(errno));
+           errstr = quote_string(m);
+           amfree(m);
            rc = 0;
            goto common_exit;
        }
@@ -747,9 +762,11 @@ databuf_flush(
         * to the next chunk, and then close it.
         */
        if (lseek(db->fd, (off_t)0, SEEK_SET) < (off_t)0) {
-           errstr = squotef(_("lseek holding file %s: %s"),
+           char *m = vstrallocf(_("lseek holding file %s: %s"),
                             db->filename,
                             strerror(errno));
+           errstr = quote_string(m);
+           amfree(m);
            aclose(newfd);
            rc = 0;
            goto common_exit;
@@ -759,9 +776,11 @@ databuf_flush(
        strncpy(file.cont_filename, new_filename, SIZEOF(file.cont_filename));
        file.cont_filename[SIZEOF(file.cont_filename)-1] = '\0';
        if(write_tapeheader(db->fd, &file)) {
-           errstr = squotef(_("write_tapeheader file \"%s\": %s"),
+           char * m = vstrallocf(_("write_tapeheader file \"%s\": %s"),
                             db->filename,
                             strerror(errno));
+           errstr = quote_string(m);
+           amfree(m);
            aclose(newfd);
            unlink(tmp_filename);
            rc = 0;
@@ -806,7 +825,7 @@ databuf_flush(
     /*
      * Write out the buffer
      */
-    written = fullwrite(db->fd, db->dataout,
+    written = full_write(db->fd, db->dataout,
                        (size_t)(db->datain - db->dataout));
     if (written > 0) {
        db->dataout += written;
@@ -815,9 +834,11 @@ databuf_flush(
     dumpsize += (dumpbytes / (off_t)1024);
     filesize += (dumpbytes / (off_t)1024);
     dumpbytes %= 1024;
-    if (written < 0) {
+    if (written == 0) {
        if (errno != ENOSPC) {
-           errstr = squotef(_("data write: %s"), strerror(errno));
+           char *m = vstrallocf(_("data write: %s"), strerror(errno));
+           errstr = quote_string(m);
+           amfree(m);
            rc = 0;
            goto common_exit;
        }
@@ -841,10 +862,11 @@ databuf_flush(
 
 common_exit:
 
+    if (cmdargs)
+       free_cmdargs(cmdargs);
     amfree(new_filename);
     /*@i@*/ amfree(tmp_filename);
     amfree(arg_filename);
-    amfree(qarg_filename);
     return rc;
 }
 
@@ -858,15 +880,18 @@ write_tapeheader(
     dumpfile_t *file)
 {
     char *buffer;
-    ssize_t written;
+    size_t written;
 
     file->blocksize = DISK_BLOCK_BYTES;
     buffer = build_header(file, DISK_BLOCK_BYTES);
 
-    written = fullwrite(outfd, buffer, DISK_BLOCK_BYTES);
+    written = full_write(outfd, buffer, DISK_BLOCK_BYTES);
     amfree(buffer);
     if(written == DISK_BLOCK_BYTES) return 0;
-    if(written < 0) return written;
-    errno = ENOSPC;
+
+    /* fake ENOSPC when we get a short write without errno set */
+    if(errno == 0)
+       errno = ENOSPC;
+
     return (ssize_t)-1;
 }