]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/csup/detailer.c
This commit was generated by cvs2svn to compensate for changes in r172677,
[FreeBSD/FreeBSD.git] / contrib / csup / detailer.c
1 /*-
2  * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <assert.h>
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include "config.h"
35 #include "detailer.h"
36 #include "fixups.h"
37 #include "misc.h"
38 #include "mux.h"
39 #include "proto.h"
40 #include "status.h"
41 #include "stream.h"
42
43 /* Internal error codes. */
44 #define DETAILER_ERR_PROTO      (-1)    /* Protocol error. */
45 #define DETAILER_ERR_MSG        (-2)    /* Error is in detailer->errmsg. */
46 #define DETAILER_ERR_READ       (-3)    /* Error reading from server. */
47 #define DETAILER_ERR_WRITE      (-4)    /* Error writing to server. */
48
49 struct detailer {
50         struct config *config;
51         struct stream *rd;
52         struct stream *wr;
53         char *errmsg;
54 };
55
56 static int      detailer_batch(struct detailer *);
57 static int      detailer_coll(struct detailer *, struct coll *,
58                     struct status *);
59 static int      detailer_dofile(struct detailer *, struct coll *,
60                     struct status *, char *);
61
62 void *
63 detailer(void *arg)
64 {
65         struct thread_args *args;
66         struct detailer dbuf, *d;
67         int error;
68
69         args = arg;
70
71         d = &dbuf;
72         d->config = args->config;
73         d->rd = args->rd;
74         d->wr = args->wr;
75         d->errmsg = NULL;
76
77         error = detailer_batch(d);
78         switch (error) {
79         case DETAILER_ERR_PROTO:
80                 xasprintf(&args->errmsg, "Detailer failed: Protocol error");
81                 args->status = STATUS_FAILURE;
82                 break;
83         case DETAILER_ERR_MSG:
84                 xasprintf(&args->errmsg, "Detailer failed: %s", d->errmsg);
85                 free(d->errmsg);
86                 args->status = STATUS_FAILURE;
87                 break;
88         case DETAILER_ERR_READ:
89                 if (stream_eof(d->rd)) {
90                         xasprintf(&args->errmsg, "Detailer failed: "
91                             "Premature EOF from server");
92                 } else {
93                         xasprintf(&args->errmsg, "Detailer failed: "
94                             "Network read failure: %s", strerror(errno));
95                 }
96                 args->status = STATUS_TRANSIENTFAILURE;
97                 break;
98         case DETAILER_ERR_WRITE:
99                 xasprintf(&args->errmsg, "Detailer failed: "
100                     "Network write failure: %s", strerror(errno));
101                 args->status = STATUS_TRANSIENTFAILURE;
102                 break;
103         default:
104                 assert(error == 0);
105                 args->status = STATUS_SUCCESS;
106         }
107         return (NULL);
108 }
109
110 static int
111 detailer_batch(struct detailer *d)
112 {
113         struct config *config;
114         struct stream *rd, *wr;
115         struct coll *coll;
116         struct status *st;
117         struct fixup *fixup;
118         char *cmd, *collname, *line, *release;
119         int error, fixupseof;
120
121         config = d->config;
122         rd = d->rd;
123         wr = d->wr;
124         STAILQ_FOREACH(coll, &config->colls, co_next) {
125                 if (coll->co_options & CO_SKIP)
126                         continue;
127                 line = stream_getln(rd, NULL);
128                 cmd = proto_get_ascii(&line);
129                 collname = proto_get_ascii(&line);
130                 release = proto_get_ascii(&line);
131                 error = proto_get_time(&line, &coll->co_scantime);
132                 if (error || line != NULL || strcmp(cmd, "COLL") != 0 ||
133                     strcmp(collname, coll->co_name) != 0 ||
134                     strcmp(release, coll->co_release) != 0)
135                         return (DETAILER_ERR_PROTO);
136                 error = proto_printf(wr, "COLL %s %s\n", coll->co_name,
137                     coll->co_release);
138                 if (error)
139                         return (DETAILER_ERR_WRITE);
140                 stream_flush(wr);
141                 if (coll->co_options & CO_COMPRESS) {
142                         stream_filter_start(rd, STREAM_FILTER_ZLIB, NULL);
143                         stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL);
144                 }
145                 st = status_open(coll, -1, &d->errmsg);
146                 if (st == NULL)
147                         return (DETAILER_ERR_MSG);
148                 error = detailer_coll(d, coll, st);
149                 status_close(st, NULL);
150                 if (error)
151                         return (error);
152                 if (coll->co_options & CO_COMPRESS) {
153                         stream_filter_stop(rd);
154                         stream_filter_stop(wr);
155                 }
156                 stream_flush(wr);
157         }
158         line = stream_getln(rd, NULL);
159         if (line == NULL)
160                 return (DETAILER_ERR_READ);
161         if (strcmp(line, ".") != 0)
162                 return (DETAILER_ERR_PROTO);
163         error = proto_printf(wr, ".\n");
164         if (error)
165                 return (DETAILER_ERR_WRITE);
166         stream_flush(wr);
167
168         /* Now send fixups if needed. */
169         fixup = NULL;
170         fixupseof = 0;
171         STAILQ_FOREACH(coll, &config->colls, co_next) {
172                 if (coll->co_options & CO_SKIP)
173                         continue;
174                 error = proto_printf(wr, "COLL %s %s\n", coll->co_name,
175                     coll->co_release);
176                 if (error)
177                         return (DETAILER_ERR_WRITE);
178                 if (coll->co_options & CO_COMPRESS)
179                         stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL);
180                 while (!fixupseof) {
181                         if (fixup == NULL)
182                                 fixup = fixups_get(config->fixups);
183                         if (fixup == NULL) {
184                                 fixupseof = 1;
185                                 break;
186                         }
187                         if (fixup->f_coll != coll)
188                                 break;
189                         error = proto_printf(wr, "Y %s %s %s\n", fixup->f_name,
190                             coll->co_tag, coll->co_date);
191                         if (error)
192                                 return (DETAILER_ERR_WRITE);
193                         fixup = NULL;
194                 }
195                 error = proto_printf(wr, ".\n");
196                 if (error)
197                         return (DETAILER_ERR_WRITE);
198                 if (coll->co_options & CO_COMPRESS)
199                         stream_filter_stop(wr);
200                 stream_flush(wr);
201         }
202         error = proto_printf(wr, ".\n");
203         if (error)
204                 return (DETAILER_ERR_WRITE);
205         return (0);
206 }
207
208 static int
209 detailer_coll(struct detailer *d, struct coll *coll, struct status *st)
210 {
211         struct stream *rd, *wr;
212         char *cmd, *file, *line, *msg;
213         int error;
214
215         rd = d->rd;
216         wr = d->wr;
217         line = stream_getln(rd, NULL);
218         if (line == NULL)
219                 return (DETAILER_ERR_READ);
220         while (strcmp(line, ".") != 0) {
221                 cmd = proto_get_ascii(&line);
222                 if (cmd == NULL || strlen(cmd) != 1)
223                         return (DETAILER_ERR_PROTO);
224                 switch (cmd[0]) {
225                 case 'D':
226                         /* Delete file. */
227                         file = proto_get_ascii(&line);
228                         if (file == NULL || line != NULL)
229                                 return (DETAILER_ERR_PROTO);
230                         error = proto_printf(wr, "D %s\n", file);
231                         if (error)
232                                 return (DETAILER_ERR_WRITE);
233                         break;
234                 case 'U':
235                         /* Add or update file. */
236                         file = proto_get_ascii(&line);
237                         if (file == NULL || line != NULL)
238                                 return (DETAILER_ERR_PROTO);
239                         error = detailer_dofile(d, coll, st, file);
240                         if (error)
241                                 return (error);
242                         break;
243                 case '!':
244                         /* Warning from server. */
245                         msg = proto_get_rest(&line);
246                         if (msg == NULL)
247                                 return (DETAILER_ERR_PROTO);
248                         lprintf(-1, "Server warning: %s\n", msg);
249                         break;
250                 default:
251                         return (DETAILER_ERR_PROTO);
252                 }
253                 stream_flush(wr);
254                 line = stream_getln(rd, NULL);
255                 if (line == NULL)
256                         return (DETAILER_ERR_READ);
257         }
258         error = proto_printf(wr, ".\n");
259         if (error)
260                 return (DETAILER_ERR_WRITE);
261         return (0);
262 }
263
264 static int
265 detailer_dofile(struct detailer *d, struct coll *coll, struct status *st,
266     char *file)
267 {
268         char md5[MD5_DIGEST_SIZE];
269         struct stream *wr;
270         struct fattr *fa;
271         struct statusrec *sr;
272         char *path;
273         int error, ret;
274
275         wr = d->wr;
276         path = checkoutpath(coll->co_prefix, file);
277         if (path == NULL)
278                 return (DETAILER_ERR_PROTO);
279         fa = fattr_frompath(path, FATTR_NOFOLLOW);
280         if (fa == NULL) {
281                 /* We don't have the file, so the only option at this
282                    point is to tell the server to send it.  The server
283                    may figure out that the file is dead, in which case
284                    it will tell us. */
285                 error = proto_printf(wr, "C %s %s %s\n",
286                     file, coll->co_tag, coll->co_date);
287                 free(path);
288                 if (error)
289                         return (DETAILER_ERR_WRITE);
290                 return (0);
291         }
292         ret = status_get(st, file, 0, 0, &sr);
293         if (ret == -1) {
294                 d->errmsg = status_errmsg(st);
295                 free(path);
296                 return (DETAILER_ERR_MSG);
297         }
298         if (ret == 0)
299                 sr = NULL;
300
301         /* If our recorded information doesn't match the file that the
302            client has, then ignore the recorded information. */
303         if (sr != NULL && (sr->sr_type != SR_CHECKOUTLIVE ||
304             !fattr_equal(sr->sr_clientattr, fa)))
305                 sr = NULL;
306         fattr_free(fa);
307         if (sr != NULL && strcmp(sr->sr_revdate, ".") != 0) {
308                 error = proto_printf(wr, "U %s %s %s %s %s\n", file,
309                     coll->co_tag, coll->co_date, sr->sr_revnum, sr->sr_revdate);
310                 free(path);
311                 if (error)
312                         return (DETAILER_ERR_WRITE);
313                 return (0);
314         }
315
316         /*
317          * We don't have complete and/or accurate recorded information
318          * about what version of the file we have.  Compute the file's
319          * checksum as an aid toward identifying which version it is.
320          */
321         error = MD5_File(path, md5);
322         if (error) {
323                 xasprintf(&d->errmsg,
324                     "Cannot calculate checksum for \"%s\": %s", path,
325                     strerror(errno));
326                 return (DETAILER_ERR_MSG);
327         }
328         free(path);
329         if (sr == NULL) {
330                 error = proto_printf(wr, "S %s %s %s %s\n", file,
331                     coll->co_tag, coll->co_date, md5);
332         } else {
333                 error = proto_printf(wr, "s %s %s %s %s %s\n", file,
334                     coll->co_tag, coll->co_date, sr->sr_revnum, md5);
335         }
336         if (error)
337                 return (DETAILER_ERR_WRITE);
338         return (0);
339 }