added -r option to remove extra files.
[unix-history] / usr / src / usr.bin / rdist / docmd.c
CommitLineData
b6b5501c 1#ifndef lint
d1dee8e8 2static char *sccsid = "@(#)docmd.c 4.5 (Berkeley) 83/10/20";
b6b5501c
RC
3#endif
4
5#include "defs.h"
6
7FILE *lfp; /* log file for recording files updated */
8
9/*
82572cb6 10 * Process commands for sending files to other machines.
b6b5501c 11 */
82572cb6 12dohcmds(files, hosts, cmds)
b6b5501c
RC
13 struct block *files, *hosts, *cmds;
14{
15 register struct block *h, *f, *c;
3024eb6f 16 register char **cpp;
82572cb6 17 int n, ddir;
b6b5501c
RC
18
19 if (debug)
82572cb6 20 printf("dohcmds(%x, %x, %x)\n", files, hosts, cmds);
b6b5501c 21
82572cb6
RC
22 files = expand(files, 0);
23 hosts = expand(hosts, 1);
b6b5501c
RC
24 if (files == NULL)
25 fatal("no files to be updated\n");
26 if (hosts == NULL)
27 fatal("empty list of hosts to be updated\n");
28 except = cmds;
82572cb6 29 ddir = files->b_next != NULL;
b6b5501c
RC
30
31 for (h = hosts; h != NULL; h = h->b_next) {
82572cb6
RC
32 if (!qflag)
33 printf("updating host %s\n", h->b_name);
b6b5501c 34 if (!nflag) {
82572cb6
RC
35 if (!makeconn(h->b_name))
36 continue;
b6b5501c
RC
37 if ((lfp = fopen(tmpfile, "w")) == NULL) {
38 fatal("cannot open %s\n", tmpfile);
39 exit(1);
40 }
b6b5501c
RC
41 }
42 for (f = files; f != NULL; f = f->b_next) {
43 if (filec) {
44 for (cpp = filev; *cpp; cpp++)
45 if (!strcmp(f->b_name, *cpp))
46 goto found;
82572cb6
RC
47 if (!nflag) {
48 (void) fclose(lfp);
49 }
b6b5501c
RC
50 continue;
51 }
52 found:
53 n = 0;
d1dee8e8
RC
54 for (c = cmds; c != NULL; c = c->b_next) {
55 if (c->b_type != INSTALL)
56 continue;
57 n++;
58 if (c->b_name == NULL)
59 install(f->b_name, f->b_name, 0, c->b_options);
60 else
3024eb6f 61 install(f->b_name, c->b_name, ddir, c->b_options);
d1dee8e8 62 }
b6b5501c 63 if (n == 0)
f7770429 64 install(f->b_name, f->b_name, 0, options);
b6b5501c
RC
65 }
66 if (!nflag) {
82572cb6
RC
67 /* signal end of connection */
68 (void) write(rem, "\2\n", 2);
b6b5501c 69 (void) close(rem);
82572cb6 70 (void) fclose(lfp);
b6b5501c
RC
71 }
72 for (c = cmds; c != NULL; c = c->b_next)
73 if (c->b_type == NOTIFY)
f7770429 74 notify(tmpfile, h->b_name, c->b_args, 0);
b6b5501c
RC
75 }
76 if (!nflag)
77 (void) unlink(tmpfile);
78}
79
80/*
81 * Create a connection to the rdist server on the machine rhost.
82 */
83makeconn(rhost)
84 char *rhost;
85{
86 register char *ruser;
87 extern char user[];
88
f7770429
RC
89 (void) sprintf(buf, "/usr/local/rdist -Server%s%s",
90 nflag ? " -n" : "", qflag ? " -q" : "");
b6b5501c
RC
91
92 ruser = rindex(rhost, '.');
93 if (ruser != NULL) {
94 *ruser++ = '\0';
95 if (!okname(ruser))
96 return(0);
97 } else
98 ruser = user;
99
100 if (debug) {
101 printf("makeconn(%s)\n", rhost);
102 printf("luser = %s, ruser = %s\n", user, ruser);
103 printf("buf = %s\n", buf);
104 }
105
82572cb6 106 fflush(stdout);
b6b5501c
RC
107 rem = rcmd(&rhost, IPPORT_CMDSERVER, user, ruser, buf, 0);
108 if (rem < 0)
109 return(0);
110 if (response() < 0)
111 return(0);
112 return(1);
113}
114
115/*
116 * Update the file(s) if they are different.
82572cb6
RC
117 * destdir = 1 if destination should be a directory
118 * (i.e., more than one source is being copied to the same destination).
b6b5501c 119 */
f7770429 120install(src, dest, destdir, opts)
b6b5501c 121 char *src, *dest;
f7770429 122 int destdir, opts;
b6b5501c 123{
3024eb6f
RC
124 register char *cp;
125
82572cb6
RC
126 if (exclude(src))
127 return;
b6b5501c 128
3024eb6f 129 if (nflag || debug) {
d1dee8e8 130 printf("%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",
f7770429 131 opts & WHOLE ? " -w" : "",
d1dee8e8
RC
132 opts & YOUNGER ? " -y" : "",
133 opts & REMOVE ? " -r" : "", src, dest);
3024eb6f
RC
134 if (nflag)
135 return;
82572cb6 136 }
b6b5501c
RC
137 /*
138 * Pass the destination file/directory name to remote.
139 */
82572cb6 140 (void) sprintf(buf, "%c%s\n", destdir ? 'T' : 't', dest);
b6b5501c
RC
141 if (debug)
142 printf("buf = %s", buf);
143 (void) write(rem, buf, strlen(buf));
3024eb6f 144
f7770429
RC
145 if (!destdir && (opts & WHOLE))
146 opts |= STRIP;
d1dee8e8
RC
147 if (opts & REMOVE) {
148 opts &= ~REMOVE;
149 rmchk(src, NULL, opts);
150 }
f7770429 151 sendf(src, NULL, opts);
82572cb6
RC
152}
153
154struct tstamp {
155 time_t lastmod;
156 FILE *tfp;
157} ts[NSTAMPS];
158
159int nstamps;
160
3024eb6f
RC
161extern char target[], *tp;
162
82572cb6
RC
163/*
164 * Process commands for comparing files to time stamp files.
165 */
166dofcmds(files, stamps, cmds)
167 struct block *files, *stamps, *cmds;
168{
169 register struct block *b;
170 register struct tstamp *t;
171 register char **cpp;
f7770429
RC
172 struct timeval tv[2];
173 struct timezone tz;
82572cb6
RC
174 struct stat stb;
175 extern char *tmpinc;
82572cb6
RC
176
177 if (debug)
178 printf("dofcmds()\n");
179
180 files = expand(files, 0);
f7770429 181 stamps = expand(stamps, 0);
82572cb6
RC
182 if (files == NULL)
183 fatal("no files to be updated\n");
184 if (stamps == NULL)
185 fatal("empty time stamp file list\n");
186 except = cmds;
187
188 t = ts;
189 nstamps = 0;
190 for (b = stamps; b != NULL; b = b->b_next) {
191 if (stat(b->b_name, &stb) < 0) {
192 error("%s: %s\n", b->b_name, sys_errlist[errno]);
193 continue;
194 }
195 if (++nstamps > NSTAMPS)
196 fatal("too many time stamp files in one command\n");
197 if (debug)
198 printf("%s: %d\n", b->b_name, stb.st_mtime);
199 t->lastmod = stb.st_mtime;
f7770429
RC
200 (void) gettimeofday(&tv[0], &tz);
201 tv[1] = tv[0];
202 (void) utimes(b->b_name, tv);
203 if (!nflag && !(options & VERIFY)) {
82572cb6
RC
204 if ((t->tfp = fopen(tmpfile, "w")) == NULL)
205 error("%s: %s\n", b->b_name, sys_errlist[errno]);
206 (*tmpinc)++;
207 } else
208 t->tfp = NULL;
209 t++;
210 }
f7770429 211
82572cb6
RC
212 for (b = files; b != NULL; b = b->b_next) {
213 if (filec) {
214 for (cpp = filev; *cpp; cpp++)
215 if (!strcmp(b->b_name, *cpp))
216 goto found;
217 continue;
218 }
219 found:
220 tp = NULL;
221 cmptime(b->b_name);
222 }
f7770429 223
82572cb6 224 *tmpinc = 'A';
f7770429
RC
225 for (t = ts; t < &ts[nstamps]; t++) {
226 if (t->tfp != NULL)
227 (void) fclose(t->tfp);
82572cb6
RC
228 for (b = cmds; b != NULL; b = b->b_next)
229 if (b->b_type == NOTIFY)
f7770429
RC
230 notify(tmpfile, NULL, b->b_args, t->lastmod);
231 if (!nflag && !(options & VERIFY))
82572cb6
RC
232 (void) unlink(tmpfile);
233 (*tmpinc)++;
234 }
235}
236
237/*
238 * Compare the mtime of file to the list of time stamps.
239 */
240cmptime(name)
241 char *name;
242{
243 register struct tstamp *t;
244 struct stat stb;
245
246 if (debug)
247 printf("cmptime(%s)\n", name);
248
249 if (exclude(name))
250 return;
251
252 /*
253 * first time cmptime() is called?
254 */
255 if (tp == NULL) {
256 exptilde(target, name);
257 tp = name = target;
258 while (*tp)
259 tp++;
260 }
261 if (access(name, 4) < 0 || stat(name, &stb) < 0) {
262 error("%s: %s\n", name, sys_errlist[errno]);
263 return;
264 }
265
266 switch (stb.st_mode & S_IFMT) {
267 case S_IFREG:
268 break;
269
270 case S_IFDIR:
271 rcmptime(&stb);
272 return;
273
274 default:
275 error("%s: not a plain file\n", name);
276 return;
277 }
278
279 for (t = ts; t < &ts[nstamps]; t++) {
f7770429
RC
280 if (stb.st_mtime > t->lastmod)
281 log(t->tfp, "new: %s\n", name);
82572cb6
RC
282 }
283}
284
285rcmptime(st)
286 struct stat *st;
287{
288 register DIR *d;
289 register struct direct *dp;
290 register char *cp;
291 char *otp;
292 int len;
293
294 if (debug)
295 printf("rcmptime(%x)\n", st);
296
297 if ((d = opendir(target)) == NULL) {
298 error("%s: %s\n", target, sys_errlist[errno]);
299 return;
300 }
301 otp = tp;
302 len = tp - target;
303 while (dp = readdir(d)) {
304 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
305 continue;
306 if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
307 error("%s/%s: Name too long\n", target, dp->d_name);
308 continue;
309 }
310 tp = otp;
311 *tp++ = '/';
312 cp = dp->d_name;
313 while (*tp++ = *cp++)
314 ;
315 tp--;
316 cmptime(target);
317 }
318 closedir(d);
319 tp = otp;
320 *tp = '\0';
b6b5501c
RC
321}
322
323/*
324 * Notify the list of people the changes that were made.
f7770429
RC
325 * rhost == NULL if we are mailing a list of changes compared to at time
326 * stamp file.
b6b5501c 327 */
f7770429 328notify(file, rhost, to, lmod)
3024eb6f 329 char *file, *rhost;
b6b5501c 330 register struct block *to;
f7770429 331 time_t lmod;
b6b5501c
RC
332{
333 register int fd, len;
334 FILE *pf, *popen();
335 struct stat stb;
336
f7770429 337 if (options & VERIFY)
b6b5501c
RC
338 return;
339 if (!qflag) {
82572cb6 340 printf("notify ");
3024eb6f
RC
341 if (rhost)
342 printf("@%s ", rhost);
b6b5501c
RC
343 prnames(to);
344 }
345 if (nflag)
346 return;
347
82572cb6
RC
348 if ((fd = open(file, 0)) < 0) {
349 error("%s: %s\n", file, sys_errlist[errno]);
350 return;
351 }
352 if (fstat(fd, &stb) < 0) {
353 error("%s: %s\n", file, sys_errlist[errno]);
354 (void) close(fd);
355 return;
356 }
357 if (stb.st_size == 0) {
358 (void) close(fd);
b6b5501c
RC
359 return;
360 }
361 /*
362 * Create a pipe to mailling program.
363 */
364 pf = popen(MAILCMD, "w");
3024eb6f
RC
365 if (pf == NULL) {
366 error("notify: \"%s\" failed\n", MAILCMD);
367 (void) close(fd);
368 return;
369 }
b6b5501c
RC
370 /*
371 * Output the proper header information.
372 */
373 fprintf(pf, "From: rdist (Remote distribution program)\n");
374 fprintf(pf, "To:");
f7770429
RC
375 if (!any('@', to->b_name) && host != NULL)
376 fprintf(pf, " %s@%s", to->b_name, rhost);
377 else
378 fprintf(pf, " %s", to->b_name);
379 to = to->b_next;
b6b5501c 380 while (to != NULL) {
3024eb6f 381 if (!any('@', to->b_name) && host != NULL)
f7770429 382 fprintf(pf, ", %s@%s", to->b_name, rhost);
82572cb6 383 else
f7770429 384 fprintf(pf, ", %s", to->b_name);
b6b5501c
RC
385 to = to->b_next;
386 }
387 putc('\n', pf);
f7770429
RC
388 if (rhost != NULL)
389 fprintf(pf, "Subject: files updated by rdist from %s to %s\n",
390 host, rhost);
391 else
392 fprintf(pf, "Subject: files updated after %s\n", ctime(&lmod));
b6b5501c
RC
393 putc('\n', pf);
394
395 while ((len = read(fd, buf, BUFSIZ)) > 0)
396 (void) fwrite(buf, 1, len, pf);
397 (void) close(fd);
398 (void) pclose(pf);
399}
400
401struct block *except; /* list of files to exclude */
402
403/*
404 * Return true if name is in list.
405 */
406exclude(file)
407 char *file;
408{
409 register struct block *b, *c;
410
411 for (c = except; c != NULL; c = c->b_next) {
412 if (c->b_type != EXCEPT)
413 continue;
414 for (b = c->b_args; b != NULL; b = b->b_next)
415 if (!strcmp(file, b->b_name))
416 return(1);
417 }
418 return(0);
419}
420
421okname(name)
422 register char *name;
423{
424 register char *cp = name;
425 register int c;
426
427 do {
428 c = *cp;
429 if (c & 0200)
430 goto bad;
431 if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
432 goto bad;
433 cp++;
434 } while (*cp);
435 return(1);
436bad:
437 error("invalid user name %s\n", name);
438 return(0);
439}
440
441char *
442colon(cp)
443 register char *cp;
444{
445
446 while (*cp) {
447 if (*cp == ':')
448 return(cp);
449 if (*cp == '/')
450 return(0);
451 cp++;
452 }
453 return(0);
454}