detailer.c revision 156230
1156230Smux/*- 2156230Smux * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> 3156230Smux * All rights reserved. 4156230Smux * 5156230Smux * Redistribution and use in source and binary forms, with or without 6156230Smux * modification, are permitted provided that the following conditions 7156230Smux * are met: 8156230Smux * 1. Redistributions of source code must retain the above copyright 9156230Smux * notice, this list of conditions and the following disclaimer. 10156230Smux * 2. Redistributions in binary form must reproduce the above copyright 11156230Smux * notice, this list of conditions and the following disclaimer in the 12156230Smux * documentation and/or other materials provided with the distribution. 13156230Smux * 14156230Smux * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15156230Smux * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16156230Smux * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17156230Smux * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18156230Smux * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19156230Smux * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20156230Smux * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21156230Smux * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22156230Smux * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23156230Smux * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24156230Smux * SUCH DAMAGE. 25156230Smux * 26156230Smux * $FreeBSD: vendor/csup/dist/contrib/csup/detailer.c 156230 2006-03-03 04:11:29Z mux $ 27156230Smux */ 28156230Smux 29156230Smux#include <assert.h> 30156230Smux#include <errno.h> 31156230Smux#include <stdlib.h> 32156230Smux#include <string.h> 33156230Smux 34156230Smux#include "config.h" 35156230Smux#include "detailer.h" 36156230Smux#include "fixups.h" 37156230Smux#include "misc.h" 38156230Smux#include "mux.h" 39156230Smux#include "proto.h" 40156230Smux#include "status.h" 41156230Smux#include "stream.h" 42156230Smux 43156230Smux/* Internal error codes. */ 44156230Smux#define DETAILER_ERR_PROTO (-1) /* Protocol error. */ 45156230Smux#define DETAILER_ERR_MSG (-2) /* Error is in detailer->errmsg. */ 46156230Smux#define DETAILER_ERR_READ (-3) /* Error reading from server. */ 47156230Smux#define DETAILER_ERR_WRITE (-4) /* Error writing to server. */ 48156230Smux 49156230Smuxstruct detailer { 50156230Smux struct config *config; 51156230Smux struct stream *rd; 52156230Smux struct stream *wr; 53156230Smux char *errmsg; 54156230Smux}; 55156230Smux 56156230Smuxstatic int detailer_batch(struct detailer *); 57156230Smuxstatic int detailer_coll(struct detailer *, struct coll *, 58156230Smux struct status *); 59156230Smuxstatic int detailer_dofile(struct detailer *, struct coll *, 60156230Smux struct status *, char *); 61156230Smux 62156230Smuxvoid * 63156230Smuxdetailer(void *arg) 64156230Smux{ 65156230Smux struct thread_args *args; 66156230Smux struct detailer dbuf, *d; 67156230Smux int error; 68156230Smux 69156230Smux args = arg; 70156230Smux 71156230Smux d = &dbuf; 72156230Smux d->config = args->config; 73156230Smux d->rd = args->rd; 74156230Smux d->wr = args->wr; 75156230Smux d->errmsg = NULL; 76156230Smux 77156230Smux error = detailer_batch(d); 78156230Smux switch (error) { 79156230Smux case DETAILER_ERR_PROTO: 80156230Smux xasprintf(&args->errmsg, "Detailer failed: Protocol error"); 81156230Smux args->status = STATUS_FAILURE; 82156230Smux break; 83156230Smux case DETAILER_ERR_MSG: 84156230Smux xasprintf(&args->errmsg, "Detailer failed: %s", d->errmsg); 85156230Smux free(d->errmsg); 86156230Smux args->status = STATUS_FAILURE; 87156230Smux break; 88156230Smux case DETAILER_ERR_READ: 89156230Smux if (stream_eof(d->rd)) { 90156230Smux xasprintf(&args->errmsg, "Detailer failed: " 91156230Smux "Premature EOF from server"); 92156230Smux } else { 93156230Smux xasprintf(&args->errmsg, "Detailer failed: " 94156230Smux "Network read failure: %s", strerror(errno)); 95156230Smux } 96156230Smux args->status = STATUS_TRANSIENTFAILURE; 97156230Smux break; 98156230Smux case DETAILER_ERR_WRITE: 99156230Smux xasprintf(&args->errmsg, "Detailer failed: " 100156230Smux "Network write failure: %s", strerror(errno)); 101156230Smux args->status = STATUS_TRANSIENTFAILURE; 102156230Smux break; 103156230Smux default: 104156230Smux assert(error == 0); 105156230Smux args->status = STATUS_SUCCESS; 106156230Smux } 107156230Smux return (NULL); 108156230Smux} 109156230Smux 110156230Smuxstatic int 111156230Smuxdetailer_batch(struct detailer *d) 112156230Smux{ 113156230Smux struct config *config; 114156230Smux struct stream *rd, *wr; 115156230Smux struct coll *coll; 116156230Smux struct status *st; 117156230Smux struct fixup *fixup; 118156230Smux char *cmd, *collname, *line, *release; 119156230Smux int error, fixupseof; 120156230Smux 121156230Smux config = d->config; 122156230Smux rd = d->rd; 123156230Smux wr = d->wr; 124156230Smux STAILQ_FOREACH(coll, &config->colls, co_next) { 125156230Smux if (coll->co_options & CO_SKIP) 126156230Smux continue; 127156230Smux line = stream_getln(rd, NULL); 128156230Smux cmd = proto_get_ascii(&line); 129156230Smux collname = proto_get_ascii(&line); 130156230Smux release = proto_get_ascii(&line); 131156230Smux error = proto_get_time(&line, &coll->co_scantime); 132156230Smux if (error || line != NULL || strcmp(cmd, "COLL") != 0 || 133156230Smux strcmp(collname, coll->co_name) != 0 || 134156230Smux strcmp(release, coll->co_release) != 0) 135156230Smux return (DETAILER_ERR_PROTO); 136156230Smux error = proto_printf(wr, "COLL %s %s\n", coll->co_name, 137156230Smux coll->co_release); 138156230Smux if (error) 139156230Smux return (DETAILER_ERR_WRITE); 140156230Smux stream_flush(wr); 141156230Smux if (coll->co_options & CO_COMPRESS) { 142156230Smux stream_filter_start(rd, STREAM_FILTER_ZLIB, NULL); 143156230Smux stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL); 144156230Smux } 145156230Smux st = status_open(coll, -1, &d->errmsg); 146156230Smux if (st == NULL) 147156230Smux return (DETAILER_ERR_MSG); 148156230Smux error = detailer_coll(d, coll, st); 149156230Smux status_close(st, NULL); 150156230Smux if (error) 151156230Smux return (error); 152156230Smux if (coll->co_options & CO_COMPRESS) { 153156230Smux stream_filter_stop(rd); 154156230Smux stream_filter_stop(wr); 155156230Smux } 156156230Smux stream_flush(wr); 157156230Smux } 158156230Smux line = stream_getln(rd, NULL); 159156230Smux if (line == NULL) 160156230Smux return (DETAILER_ERR_READ); 161156230Smux if (strcmp(line, ".") != 0) 162156230Smux return (DETAILER_ERR_PROTO); 163156230Smux error = proto_printf(wr, ".\n"); 164156230Smux if (error) 165156230Smux return (DETAILER_ERR_WRITE); 166156230Smux stream_flush(wr); 167156230Smux 168156230Smux /* Now send fixups if needed. */ 169156230Smux fixup = NULL; 170156230Smux fixupseof = 0; 171156230Smux STAILQ_FOREACH(coll, &config->colls, co_next) { 172156230Smux if (coll->co_options & CO_SKIP) 173156230Smux continue; 174156230Smux error = proto_printf(wr, "COLL %s %s\n", coll->co_name, 175156230Smux coll->co_release); 176156230Smux if (error) 177156230Smux return (DETAILER_ERR_WRITE); 178156230Smux if (coll->co_options & CO_COMPRESS) 179156230Smux stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL); 180156230Smux while (!fixupseof) { 181156230Smux if (fixup == NULL) 182156230Smux fixup = fixups_get(config->fixups); 183156230Smux if (fixup == NULL) { 184156230Smux fixupseof = 1; 185156230Smux break; 186156230Smux } 187156230Smux if (fixup->f_coll != coll) 188156230Smux break; 189156230Smux error = proto_printf(wr, "Y %s %s %s\n", fixup->f_name, 190156230Smux coll->co_tag, coll->co_date); 191156230Smux if (error) 192156230Smux return (DETAILER_ERR_WRITE); 193156230Smux fixup = NULL; 194156230Smux } 195156230Smux error = proto_printf(wr, ".\n"); 196156230Smux if (error) 197156230Smux return (DETAILER_ERR_WRITE); 198156230Smux if (coll->co_options & CO_COMPRESS) 199156230Smux stream_filter_stop(wr); 200156230Smux stream_flush(wr); 201156230Smux } 202156230Smux error = proto_printf(wr, ".\n"); 203156230Smux if (error) 204156230Smux return (DETAILER_ERR_WRITE); 205156230Smux return (0); 206156230Smux} 207156230Smux 208156230Smuxstatic int 209156230Smuxdetailer_coll(struct detailer *d, struct coll *coll, struct status *st) 210156230Smux{ 211156230Smux struct stream *rd, *wr; 212156230Smux char *cmd, *file, *line, *msg; 213156230Smux int error; 214156230Smux 215156230Smux rd = d->rd; 216156230Smux wr = d->wr; 217156230Smux line = stream_getln(rd, NULL); 218156230Smux if (line == NULL) 219156230Smux return (DETAILER_ERR_READ); 220156230Smux while (strcmp(line, ".") != 0) { 221156230Smux cmd = proto_get_ascii(&line); 222156230Smux if (cmd == NULL || strlen(cmd) != 1) 223156230Smux return (DETAILER_ERR_PROTO); 224156230Smux switch (cmd[0]) { 225156230Smux case 'D': 226156230Smux /* Delete file. */ 227156230Smux file = proto_get_ascii(&line); 228156230Smux if (file == NULL || line != NULL) 229156230Smux return (DETAILER_ERR_PROTO); 230156230Smux error = proto_printf(wr, "D %s\n", file); 231156230Smux if (error) 232156230Smux return (DETAILER_ERR_WRITE); 233156230Smux break; 234156230Smux case 'U': 235156230Smux /* Add or update file. */ 236156230Smux file = proto_get_ascii(&line); 237156230Smux if (file == NULL || line != NULL) 238156230Smux return (DETAILER_ERR_PROTO); 239156230Smux error = detailer_dofile(d, coll, st, file); 240156230Smux if (error) 241156230Smux return (error); 242156230Smux break; 243156230Smux case '!': 244156230Smux /* Warning from server. */ 245156230Smux msg = proto_get_rest(&line); 246156230Smux if (msg == NULL) 247156230Smux return (DETAILER_ERR_PROTO); 248156230Smux lprintf(-1, "Server warning: %s\n", msg); 249156230Smux break; 250156230Smux default: 251156230Smux return (DETAILER_ERR_PROTO); 252156230Smux } 253156230Smux stream_flush(wr); 254156230Smux line = stream_getln(rd, NULL); 255156230Smux if (line == NULL) 256156230Smux return (DETAILER_ERR_READ); 257156230Smux } 258156230Smux error = proto_printf(wr, ".\n"); 259156230Smux if (error) 260156230Smux return (DETAILER_ERR_WRITE); 261156230Smux return (0); 262156230Smux} 263156230Smux 264156230Smuxstatic int 265156230Smuxdetailer_dofile(struct detailer *d, struct coll *coll, struct status *st, 266156230Smux char *file) 267156230Smux{ 268156230Smux char md5[MD5_DIGEST_SIZE]; 269156230Smux struct stream *wr; 270156230Smux struct fattr *fa; 271156230Smux struct statusrec *sr; 272156230Smux char *path; 273156230Smux int error, ret; 274156230Smux 275156230Smux wr = d->wr; 276156230Smux path = checkoutpath(coll->co_prefix, file); 277156230Smux if (path == NULL) 278156230Smux return (DETAILER_ERR_PROTO); 279156230Smux fa = fattr_frompath(path, FATTR_NOFOLLOW); 280156230Smux if (fa == NULL) { 281156230Smux /* We don't have the file, so the only option at this 282156230Smux point is to tell the server to send it. The server 283156230Smux may figure out that the file is dead, in which case 284156230Smux it will tell us. */ 285156230Smux error = proto_printf(wr, "C %s %s %s\n", 286156230Smux file, coll->co_tag, coll->co_date); 287156230Smux free(path); 288156230Smux if (error) 289156230Smux return (DETAILER_ERR_WRITE); 290156230Smux return (0); 291156230Smux } 292156230Smux ret = status_get(st, file, 0, 0, &sr); 293156230Smux if (ret == -1) { 294156230Smux d->errmsg = status_errmsg(st); 295156230Smux free(path); 296156230Smux return (DETAILER_ERR_MSG); 297156230Smux } 298156230Smux if (ret == 0) 299156230Smux sr = NULL; 300156230Smux 301156230Smux /* If our recorded information doesn't match the file that the 302156230Smux client has, then ignore the recorded information. */ 303156230Smux if (sr != NULL && (sr->sr_type != SR_CHECKOUTLIVE || 304156230Smux !fattr_equal(sr->sr_clientattr, fa))) 305156230Smux sr = NULL; 306156230Smux fattr_free(fa); 307156230Smux if (sr != NULL && strcmp(sr->sr_revdate, ".") != 0) { 308156230Smux error = proto_printf(wr, "U %s %s %s %s %s\n", file, 309156230Smux coll->co_tag, coll->co_date, sr->sr_revnum, sr->sr_revdate); 310156230Smux free(path); 311156230Smux if (error) 312156230Smux return (DETAILER_ERR_WRITE); 313156230Smux return (0); 314156230Smux } 315156230Smux 316156230Smux /* 317156230Smux * We don't have complete and/or accurate recorded information 318156230Smux * about what version of the file we have. Compute the file's 319156230Smux * checksum as an aid toward identifying which version it is. 320156230Smux */ 321156230Smux error = MD5_File(path, md5); 322156230Smux if (error) { 323156230Smux xasprintf(&d->errmsg, 324156230Smux "Cannot calculate checksum for \"%s\": %s", path, 325156230Smux strerror(errno)); 326156230Smux return (DETAILER_ERR_MSG); 327156230Smux } 328156230Smux free(path); 329156230Smux if (sr == NULL) { 330156230Smux error = proto_printf(wr, "S %s %s %s %s\n", file, 331156230Smux coll->co_tag, coll->co_date, md5); 332156230Smux } else { 333156230Smux error = proto_printf(wr, "s %s %s %s %s %s\n", file, 334156230Smux coll->co_tag, coll->co_date, sr->sr_revnum, md5); 335156230Smux } 336156230Smux if (error) 337156230Smux return (DETAILER_ERR_WRITE); 338156230Smux return (0); 339156230Smux} 340