GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/rdist/docmd.c Lines: 0 468 0.0 %
Date: 2017-11-07 Branches: 0 376 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: docmd.c,v 1.33 2017/07/09 14:04:50 espie Exp $	*/
2
3
/*
4
 * Copyright (c) 1983 Regents of the University of California.
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 * 3. Neither the name of the University nor the names of its contributors
16
 *    may be used to endorse or promote products derived from this software
17
 *    without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
 * SUCH DAMAGE.
30
 */
31
32
#include <ctype.h>
33
#include <dirent.h>
34
#include <errno.h>
35
#include <fcntl.h>
36
#include <paths.h>
37
#include <stdlib.h>
38
#include <string.h>
39
#include <unistd.h>
40
41
#include "client.h"
42
#include "gram.h"
43
44
/*
45
 * Functions for rdist that do command (cmd) related activities.
46
 */
47
48
struct subcmd	       *subcmds;		/* list of sub-commands for
49
						   current cmd */
50
struct namelist	       *filelist;		/* list of source files */
51
time_t			lastmod;		/* Last modify time */
52
53
static void closeconn(void);
54
static void notify(char *, struct namelist *, time_t);
55
static void checkcmd(struct cmd *);
56
static void markfailed(struct cmd *, struct cmd *);
57
static int remotecmd(char *, char *, char *, char *);
58
static int makeconn(char *);
59
static void doarrow(struct cmd *, char **);
60
static void rcmptime(struct stat *, struct subcmd *, char **);
61
static void cmptime(char *, struct subcmd *, char **);
62
static void dodcolon(struct cmd *, char **);
63
static void docmdhost(struct cmd *, char **);
64
static void docmd(struct cmd *, int, char **);
65
66
/*
67
 * Signal end of connection.
68
 */
69
static void
70
closeconn(void)
71
{
72
	debugmsg(DM_CALL, "closeconn() called\n");
73
74
	if (rem_w >= 0) {
75
		/* We don't care if the connection is still good or not */
76
		signal(SIGPIPE, SIG_IGN);
77
78
		(void) sendcmd(C_FERRMSG, NULL);
79
		(void) close(rem_w);
80
		(void) close(rem_r); /* This can't hurt */
81
		rem_w = -1;
82
		rem_r = -1;
83
	}
84
}
85
86
/*
87
 * Notify the list of people the changes that were made.
88
 * rhost == NULL if we are mailing a list of changes compared to at time
89
 * stamp file.
90
 */
91
static void
92
notify(char *rhost, struct namelist *to, time_t lmod)
93
{
94
	int fd;
95
	ssize_t len;
96
	FILE *pf;
97
	struct stat stb;
98
	static char buf[BUFSIZ];
99
	char *file, *user;
100
101
	if (IS_ON(options, DO_VERIFY) || to == NULL)
102
		return;
103
104
	if ((file = getnotifyfile()) == NULL)
105
		return;
106
107
	if (!IS_ON(options, DO_QUIET)) {
108
		message(MT_INFO, "notify %s%s %s",
109
			(rhost) ? "@" : "",
110
			(rhost) ? rhost : "", getnlstr(to));
111
	}
112
113
	if (nflag)
114
		return;
115
116
	debugmsg(DM_MISC, "notify() temp file = '%s'", file);
117
118
	if ((fd = open(file, O_RDONLY)) < 0) {
119
		error("%s: open for reading failed: %s", file, SYSERR);
120
		return;
121
	}
122
	if (fstat(fd, &stb) < 0) {
123
		error("%s: fstat failed: %s", file, SYSERR);
124
		(void) close(fd);
125
		return;
126
	}
127
	if (stb.st_size == 0) {
128
		(void) close(fd);
129
		return;
130
	}
131
	/*
132
	 * Create a pipe to mailing program.
133
	 * Set IFS to avoid possible security problem with users
134
	 * setting "IFS=/".
135
	 */
136
	(void) snprintf(buf, sizeof(buf), "IFS=\" \t\"; export IFS; %s -oi -t",
137
		       _PATH_SENDMAIL);
138
	pf = popen(buf, "w");
139
	if (pf == NULL) {
140
		error("notify: \"%s\" failed\n", _PATH_SENDMAIL);
141
		(void) unlink(file);
142
		(void) close(fd);
143
		return;
144
	}
145
	/*
146
	 * Output the proper header information.
147
	 */
148
	(void) fprintf(pf, "Auto-Submitted: auto-generated\n");
149
	(void) fprintf(pf, "From: rdist (Remote distribution program)\n");
150
	(void) fprintf(pf, "To:");
151
	if (!any('@', to->n_name) && rhost != NULL)
152
		(void) fprintf(pf, " %s@%s", to->n_name, rhost);
153
	else
154
		(void) fprintf(pf, " %s", to->n_name);
155
	to = to->n_next;
156
	while (to != NULL) {
157
		if (!any('@', to->n_name) && rhost != NULL)
158
			(void) fprintf(pf, ", %s@%s", to->n_name, rhost);
159
		else
160
			(void) fprintf(pf, ", %s", to->n_name);
161
		to = to->n_next;
162
	}
163
	(void) putc('\n', pf);
164
165
	if ((user = getlogin()) == NULL)
166
		user = locuser;
167
168
	if (rhost != NULL)
169
		(void) fprintf(pf,
170
			 "Subject: files updated by %s from %s to %s\n",
171
			 locuser, host, rhost);
172
	else
173
		(void) fprintf(pf, "Subject: files updated after %s\n",
174
			       ctime(&lmod));
175
	(void) putc('\n', pf);
176
	(void) putc('\n', pf);
177
	(void) fprintf(pf, "Options: %s\n\n", getondistoptlist(options));
178
179
	while ((len = read(fd, buf, sizeof(buf))) > 0)
180
		(void) fwrite(buf, 1, len, pf);
181
182
	(void) pclose(pf);
183
	(void) close(fd);
184
	(void) unlink(file);
185
}
186
187
/*
188
 * XXX Hack for NFS.  If a hostname from the distfile
189
 * ends with a '+', then the normal restriction of
190
 * skipping files that are on an NFS filesystem is
191
 * bypassed.  We always strip '+' to be consistent.
192
 */
193
static void
194
checkcmd(struct cmd *cmd)
195
{
196
	int l;
197
198
	if (!cmd || !(cmd->c_name)) {
199
		debugmsg(DM_MISC, "checkcmd() NULL cmd parameter");
200
		return;
201
	}
202
203
	l = strlen(cmd->c_name);
204
	if (l <= 0)
205
		return;
206
	if (cmd->c_name[l-1] == '+') {
207
		cmd->c_flags |= CMD_NOCHKNFS;
208
		cmd->c_name[l-1] = CNULL;
209
	}
210
}
211
212
/*
213
 * Mark all other entries for this command (cmd)
214
 * as assigned.
215
 */
216
void
217
markassigned(struct cmd *cmd, struct cmd *cmdlist)
218
{
219
	struct cmd *pcmd;
220
221
	for (pcmd = cmdlist; pcmd; pcmd = pcmd->c_next) {
222
		checkcmd(pcmd);
223
		if (pcmd->c_type == cmd->c_type &&
224
		    strcmp(pcmd->c_name, cmd->c_name)==0)
225
			pcmd->c_flags |= CMD_ASSIGNED;
226
	}
227
}
228
229
/*
230
 * Mark the command "cmd" as failed for all commands in list cmdlist.
231
 */
232
static void
233
markfailed(struct cmd *cmd, struct cmd *cmdlist)
234
{
235
	struct cmd *pc;
236
237
	if (!cmd) {
238
		debugmsg(DM_MISC, "markfailed() NULL cmd parameter");
239
		return;
240
	}
241
242
	checkcmd(cmd);
243
	cmd->c_flags |= CMD_CONNFAILED;
244
	for (pc = cmdlist; pc; pc = pc->c_next) {
245
		checkcmd(pc);
246
		if (pc->c_type == cmd->c_type &&
247
		    strcmp(pc->c_name, cmd->c_name)==0)
248
			pc->c_flags |= CMD_CONNFAILED;
249
	}
250
}
251
252
static int
253
remotecmd(char *rhost, char *luser, char *ruser, char *cmd)
254
{
255
	int desc;
256
257
	debugmsg(DM_MISC, "local user = %s remote user = %s\n", luser, ruser);
258
	debugmsg(DM_MISC, "Remote command = '%s'\n", cmd);
259
260
	(void) fflush(stdout);
261
	(void) fflush(stderr);
262
	(void) signal(SIGALRM, sighandler);
263
	(void) alarm(RTIMEOUT);
264
265
	debugmsg(DM_MISC, "Remote shell command = '%s'\n",
266
	    path_remsh ? path_remsh : "default");
267
	(void) signal(SIGPIPE, SIG_IGN);
268
	desc = rcmdsh(&rhost, -1, luser, ruser, cmd, path_remsh);
269
	if (desc > 0)
270
		(void) signal(SIGPIPE, sighandler);
271
272
	(void) alarm(0);
273
274
	return(desc);
275
}
276
277
/*
278
 * Create a connection to the rdist server on the machine rhost.
279
 * Return 0 if the connection fails or 1 if it succeeds.
280
 */
281
static int
282
makeconn(char *rhost)
283
{
284
	char *ruser, *cp;
285
	static char *cur_host = NULL;
286
	char tuser[BUFSIZ], buf[BUFSIZ];
287
	u_char respbuff[BUFSIZ];
288
	int n;
289
290
	debugmsg(DM_CALL, "makeconn(%s)", rhost);
291
292
	/*
293
	 * See if we're already connected to this host
294
	 */
295
	if (cur_host != NULL && rem_w >= 0) {
296
		if (strcmp(cur_host, rhost) == 0)
297
			return(1);
298
		closeconn();
299
	}
300
301
	/*
302
	 * Determine remote user and current host names
303
	 */
304
	cur_host = rhost;
305
	cp = strchr(rhost, '@');
306
307
	if (cp != NULL) {
308
		char c = *cp;
309
310
		*cp = CNULL;
311
		(void) strlcpy((char *)tuser, rhost, sizeof(tuser));
312
		*cp = c;
313
		rhost = cp + 1;
314
		ruser = tuser;
315
		if (*ruser == CNULL)
316
			ruser = locuser;
317
		else if (!okname(ruser))
318
			return(0);
319
	} else
320
		ruser = locuser;
321
322
	if (!IS_ON(options, DO_QUIET))
323
		message(MT_VERBOSE, "updating host %s", rhost);
324
325
	(void) snprintf(buf, sizeof(buf), "%.*s -S",
326
			(int)(sizeof(buf)-5), path_rdistd);
327
328
	if ((rem_r = rem_w = remotecmd(rhost, locuser, ruser, buf)) < 0)
329
		return(0);
330
331
	/*
332
	 * First thing received should be S_VERSION
333
	 */
334
	respbuff[0] = '\0';
335
	n = remline(respbuff, sizeof(respbuff), TRUE);
336
	if (n <= 0 || respbuff[0] != S_VERSION) {
337
		if (n > 0)
338
		    error("Unexpected input from server: \"%s\".", respbuff);
339
		else
340
		    error("No input from server.");
341
		closeconn();
342
		return(0);
343
	}
344
345
	/*
346
	 * For future compatibility we check to see if the server
347
	 * sent it's version number to us.  If it did, we use it,
348
	 * otherwise, we send our version number to the server and let
349
	 * it decide if it can handle our protocol version.
350
	 */
351
	if (respbuff[1] == CNULL) {
352
		/*
353
		 * The server wants us to send it our version number
354
		 */
355
		(void) sendcmd(S_VERSION, "%d", VERSION);
356
		if (response() < 0)
357
			return(0);
358
	} else {
359
		/*
360
		 * The server sent it's version number to us
361
		 */
362
		int proto_version = atoi(&respbuff[1]);
363
		if (proto_version != VERSION) {
364
			fatalerr(
365
		  "Server version (%d) is not the same as local version (%d).",
366
			      proto_version, VERSION);
367
			return(0);
368
		}
369
	}
370
371
	/*
372
	 * Send config commands
373
	 */
374
	if (host[0]) {
375
		(void) sendcmd(C_SETCONFIG, "%c%s", SC_HOSTNAME, host);
376
		if (response() < 0)
377
			return(0);
378
	}
379
	if (min_freespace) {
380
		(void) sendcmd(C_SETCONFIG, "%c%lld", SC_FREESPACE,
381
			       min_freespace);
382
		if (response() < 0)
383
			return(0);
384
	}
385
	if (min_freefiles) {
386
		(void) sendcmd(C_SETCONFIG, "%c%lld", SC_FREEFILES,
387
			       min_freefiles);
388
		if (response() < 0)
389
			return(0);
390
	}
391
	if (remotemsglist) {
392
		(void) sendcmd(C_SETCONFIG, "%c%s", SC_LOGGING, remotemsglist);
393
		if (response() < 0)
394
			return(0);
395
	}
396
	if (strcmp(defowner, "bin") != 0) {
397
		(void) sendcmd(C_SETCONFIG, "%c%s", SC_DEFOWNER, defowner);
398
		if (response() < 0)
399
			return(0);
400
	}
401
	if (strcmp(defgroup, "bin") != 0) {
402
		(void) sendcmd(C_SETCONFIG, "%c%s", SC_DEFGROUP, defgroup);
403
		if (response() < 0)
404
			return(0);
405
	}
406
407
	return(1);
408
}
409
410
/*
411
 * Process commands for sending files to other machines.
412
 */
413
static void
414
doarrow(struct cmd *cmd, char **filev)
415
{
416
	struct namelist *f;
417
	struct subcmd *sc;
418
	char **cpp;
419
	int n, ddir, destdir;
420
	volatile opt_t opts = options;
421
	struct namelist *files;
422
	struct subcmd *sbcmds;
423
	char *rhost;
424
	volatile int didupdate = 0;
425
426
        if (setjmp_ok) {
427
		error("reentrant call to doarrow");
428
		abort();
429
	}
430
431
	if (!cmd) {
432
		debugmsg(DM_MISC, "doarrow() NULL cmd parameter");
433
		return;
434
	}
435
436
	files = cmd->c_files;
437
	sbcmds = cmd->c_cmds;
438
	rhost = cmd->c_name;
439
440
	if (files == NULL) {
441
		error("No files to be updated on %s for target \"%s\"",
442
		      rhost, cmd->c_label);
443
		return;
444
	}
445
446
	debugmsg(DM_CALL, "doarrow(%p, %s, %p) start",
447
		 files, A(rhost), sbcmds);
448
449
	if (nflag)
450
		(void) printf("updating host %s\n", rhost);
451
	else {
452
		if (cmd->c_flags & CMD_CONNFAILED) {
453
			debugmsg(DM_MISC,
454
				 "makeconn %s failed before; skipping\n",
455
				 rhost);
456
			return;
457
		}
458
459
		if (setjmp(finish_jmpbuf)) {
460
			setjmp_ok = FALSE;
461
			debugmsg(DM_MISC, "setjmp to finish_jmpbuf");
462
			markfailed(cmd, cmds);
463
			return;
464
		}
465
		setjmp_ok = TRUE;
466
467
		if (!makeconn(rhost)) {
468
			setjmp_ok = FALSE;
469
			markfailed(cmd, cmds);
470
			return;
471
		}
472
	}
473
474
	subcmds = sbcmds;
475
	filelist = files;
476
477
	n = 0;
478
	for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
479
		if (sc->sc_type != INSTALL)
480
			continue;
481
		n++;
482
	/*
483
	 * destination is a directory if one of the following is true:
484
	 * a) more than one name specified on left side of -> directive
485
	 * b) basename of destination in "install" directive is "."
486
	 *    (e.g. install /tmp/.;)
487
	 * c) name on left side of -> directive is a directory on local system.
488
 	 *
489
 	 * We need 2 destdir flags (destdir and ddir) because single directory
490
 	 * source is handled differently.  In this case, ddir is 0 (which
491
 	 * tells install() not to send DIRTARGET directive to remote rdistd)
492
 	 * and destdir is 1 (which tells remfilename() how to build the FILE
493
 	 * variables correctly).  In every other case, destdir and ddir will
494
 	 * have the same value.
495
	 */
496
  	ddir = files->n_next != NULL;	/* destination is a directory */
497
	if (!ddir) {
498
		struct stat s;
499
 		int isadir = 0;
500
501
		if (lstat(files->n_name, &s) == 0)
502
 			isadir = S_ISDIR(s.st_mode);
503
 		if (!isadir && sc->sc_name && *sc->sc_name)
504
 			ddir = !strcmp(xbasename(sc->sc_name),".");
505
 		destdir = isadir | ddir;
506
 	} else
507
 		destdir = ddir;
508
509
	debugmsg(DM_MISC,
510
		 "Debug files->n_next= %p, destdir=%d, ddir=%d",
511
		 files->n_next, destdir, ddir);
512
513
	if (!sc->sc_name || !*sc->sc_name) {
514
		destdir = 0;
515
		ddir = 0;
516
	}
517
518
	debugmsg(DM_MISC,
519
		 "Debug sc->sc_name=%p, destdir=%d, ddir=%d",
520
		 sc->sc_name, destdir, ddir);
521
522
	for (f = files; f != NULL; f = f->n_next) {
523
		if (filev) {
524
			for (cpp = filev; *cpp; cpp++)
525
				if (strcmp(f->n_name, *cpp) == 0)
526
					goto found;
527
			continue;
528
		}
529
	found:
530
		if (install(f->n_name, sc->sc_name, ddir, destdir,
531
				sc->sc_options) > 0)
532
			++didupdate;
533
		opts = sc->sc_options;
534
	}
535
536
	} /* end loop for each INSTALL command */
537
538
	/* if no INSTALL commands present, do default install */
539
	if (!n) {
540
		for (f = files; f != NULL; f = f->n_next) {
541
			if (filev) {
542
				for (cpp = filev; *cpp; cpp++)
543
					if (strcmp(f->n_name, *cpp) == 0)
544
						goto found2;
545
				continue;
546
			}
547
		found2:
548
			/* ddir & destdir set to zero for default install */
549
			if (install(f->n_name, NULL, 0, 0, options) > 0)
550
				++didupdate;
551
		}
552
	}
553
554
	/*
555
	 * Run any commands for the entire cmd
556
	 */
557
	if (didupdate > 0) {
558
		runcmdspecial(cmd, opts);
559
		didupdate = 0;
560
	}
561
562
	if (!nflag)
563
		(void) signal(SIGPIPE, cleanup);
564
565
	for (sc = sbcmds; sc != NULL; sc = sc->sc_next)
566
		if (sc->sc_type == NOTIFY)
567
			notify(rhost, sc->sc_args, (time_t) 0);
568
569
	if (!nflag) {
570
		struct linkbuf *nextl, *l;
571
572
		for (l = ihead; l != NULL; freelinkinfo(l), l = nextl) {
573
			nextl = l->nextp;
574
			if (contimedout || IS_ON(opts, DO_IGNLNKS) ||
575
			    l->count == 0)
576
				continue;
577
			message(MT_WARNING, "%s: Warning: %d %s link%s",
578
				l->pathname, abs(l->count),
579
				(l->count > 0) ? "missing" : "extra",
580
				(l->count == 1) ? "" : "s");
581
		}
582
		ihead = NULL;
583
	}
584
	setjmp_ok = FALSE;
585
}
586
587
int
588
okname(char *name)
589
{
590
	char *cp = name;
591
	int c, isbad;
592
593
	for (isbad = FALSE; *cp && !isbad; ++cp) {
594
		c = *cp;
595
		if (c & 0200)
596
			isbad = TRUE;
597
		if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
598
			isbad = TRUE;
599
	}
600
601
	if (isbad) {
602
		error("Invalid user name \"%s\"\n", name);
603
		return(0);
604
	}
605
	return(1);
606
}
607
608
static void
609
rcmptime(struct stat *st, struct subcmd *sbcmds, char **env)
610
{
611
	DIR *d;
612
	struct dirent *dp;
613
	char *cp;
614
	char *optarget;
615
	int len;
616
617
	debugmsg(DM_CALL, "rcmptime(%p) start", st);
618
619
	if ((d = opendir((char *) target)) == NULL) {
620
		error("%s: open directory failed: %s", target, SYSERR);
621
		return;
622
	}
623
	optarget = ptarget;
624
	len = ptarget - target;
625
	while ((dp = readdir(d)) != NULL) {
626
		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
627
			continue;
628
		if (len + 1 + (int)strlen(dp->d_name) >= BUFSIZ - 1) {
629
			error("%s/%s: Name too long\n", target, dp->d_name);
630
			continue;
631
		}
632
		ptarget = optarget;
633
		*ptarget++ = '/';
634
		cp = dp->d_name;
635
		while ((*ptarget++ = *cp++) != '\0')
636
			;
637
		ptarget--;
638
		cmptime(target, sbcmds, env);
639
	}
640
	(void) closedir((DIR *) d);
641
	ptarget = optarget;
642
	*ptarget = '\0';
643
}
644
645
/*
646
 * Compare the mtime of file to the list of time stamps.
647
 */
648
static void
649
cmptime(char *name, struct subcmd *sbcmds, char **env)
650
{
651
	struct subcmd *sc;
652
	struct stat stb;
653
654
	debugmsg(DM_CALL, "cmptime(%s)", name);
655
656
	if (except(name))
657
		return;
658
659
	if (nflag) {
660
		(void) printf("comparing dates: %s\n", name);
661
		return;
662
	}
663
664
	/*
665
	 * first time cmptime() is called?
666
	 */
667
	if (ptarget == NULL) {
668
		if (exptilde(target, name, sizeof(target)) == NULL)
669
			return;
670
		ptarget = name = target;
671
		while (*ptarget)
672
			ptarget++;
673
	}
674
	if (access(name, R_OK) < 0 || stat(name, &stb) < 0) {
675
		error("%s: cannot access file: %s", name, SYSERR);
676
		return;
677
	}
678
679
	if (S_ISDIR(stb.st_mode)) {
680
		rcmptime(&stb, sbcmds, env);
681
		return;
682
	} else if (!S_ISREG(stb.st_mode)) {
683
		error("%s: not a plain file", name);
684
		return;
685
	}
686
687
	if (stb.st_mtime > lastmod) {
688
		message(MT_INFO, "%s: file is newer", name);
689
		for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
690
			char buf[BUFSIZ];
691
			if (sc->sc_type != SPECIAL)
692
				continue;
693
			if (sc->sc_args != NULL && !inlist(sc->sc_args, name))
694
				continue;
695
			(void) snprintf(buf, sizeof(buf), "%s=%s;%s",
696
				        E_LOCFILE, name, sc->sc_name);
697
			message(MT_CHANGE, "special \"%s\"", buf);
698
			if (*env) {
699
				size_t len = strlen(*env) + strlen(name) + 2;
700
				*env = xrealloc(*env, len);
701
				(void) strlcat(*env, name, len);
702
				(void) strlcat(*env, ":", len);
703
			}
704
			if (IS_ON(options, DO_VERIFY))
705
				continue;
706
707
			runcommand(buf);
708
		}
709
	}
710
}
711
712
/*
713
 * Process commands for comparing files to time stamp files.
714
 */
715
static void
716
dodcolon(struct cmd *cmd, char **filev)
717
{
718
	struct subcmd *sc;
719
	struct namelist *f;
720
	char *cp, **cpp;
721
	struct stat stb;
722
	struct namelist *files = cmd->c_files;
723
	struct subcmd *sbcmds = cmd->c_cmds;
724
	char *env, *stamp = cmd->c_name;
725
726
	debugmsg(DM_CALL, "dodcolon()");
727
728
	if (files == NULL) {
729
		error("No files to be updated for target \"%s\"",
730
		      cmd->c_label);
731
		return;
732
	}
733
	if (stat(stamp, &stb) < 0) {
734
		error("%s: stat failed: %s", stamp, SYSERR);
735
		return;
736
	}
737
738
	debugmsg(DM_MISC, "%s: mtime %lld\n", stamp, (long long)stb.st_mtime);
739
740
	env = NULL;
741
	for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
742
		if (sc->sc_type == CMDSPECIAL) {
743
			env = xmalloc(sizeof(E_FILES) + 3);
744
			(void) snprintf(env, sizeof(E_FILES) + 3,
745
					"%s='", E_FILES);
746
			break;
747
		}
748
	}
749
750
	subcmds = sbcmds;
751
	filelist = files;
752
753
	lastmod = stb.st_mtime;
754
	if (!nflag && !IS_ON(options, DO_VERIFY))
755
		/*
756
		 * Set atime and mtime to current time
757
		 */
758
		(void) setfiletime(stamp, (time_t) 0, (time_t) 0);
759
760
	for (f = files; f != NULL; f = f->n_next) {
761
		if (filev) {
762
			for (cpp = filev; *cpp; cpp++)
763
				if (strcmp(f->n_name, *cpp) == 0)
764
					goto found;
765
			continue;
766
		}
767
	found:
768
		ptarget = NULL;
769
		cmptime(f->n_name, sbcmds, &env);
770
	}
771
772
	for (sc = sbcmds; sc != NULL; sc = sc->sc_next) {
773
		if (sc->sc_type == NOTIFY)
774
			notify(NULL, sc->sc_args, (time_t)lastmod);
775
		else if (sc->sc_type == CMDSPECIAL && env) {
776
			size_t len = strlen(env);
777
			if (env[len - 1] == ':')
778
				env[--len] = CNULL;
779
			len += 2 + strlen(sc->sc_name) + 1;
780
			env = xrealloc(env, len);
781
			(void) strlcat(env, "';", len);
782
			(void) strlcat(env, sc->sc_name, len);
783
			message(MT_CHANGE, "cmdspecial \"%s\"", env);
784
			if (!nflag && IS_OFF(options, DO_VERIFY))
785
				runcommand(env);
786
			(void) free(env);
787
			env = NULL;	/* so cmdspecial is only called once */
788
		}
789
	}
790
	if (!nflag && !IS_ON(options, DO_VERIFY) && (cp = getnotifyfile()))
791
		(void) unlink(cp);
792
}
793
794
/*
795
 * Return TRUE if file is in the exception list.
796
 */
797
int
798
except(char *file)
799
{
800
	struct	subcmd *sc;
801
	struct	namelist *nl;
802
803
	debugmsg(DM_CALL, "except(%s)", file);
804
805
	for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
806
		if (sc->sc_type == EXCEPT) {
807
			for (nl = sc->sc_args; nl != NULL; nl = nl->n_next)
808
				if (strcmp(file, nl->n_name) == 0)
809
					return(1);
810
  			continue;
811
		}
812
		if (sc->sc_type == PATTERN) {
813
			for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) {
814
				char ebuf[BUFSIZ];
815
				int ecode = 0;
816
817
				/* allocate and compile n_regex as needed */
818
				if (nl->n_regex == NULL) {
819
					nl->n_regex = xmalloc(sizeof(regex_t));
820
					ecode = regcomp(nl->n_regex, nl->n_name,
821
							REG_NOSUB);
822
				}
823
				if (ecode == 0) {
824
					ecode = regexec(nl->n_regex, file, 0,
825
					    NULL, 0);
826
				}
827
				switch (ecode) {
828
				case REG_NOMATCH:
829
					break;
830
				case 0:
831
					return(1);	/* match! */
832
				default:
833
					regerror(ecode, nl->n_regex, ebuf,
834
						 sizeof(ebuf));
835
					error("Regex error \"%s\" for \"%s\".",
836
					      ebuf, nl->n_name);
837
					return(0);
838
				}
839
			}
840
		}
841
	}
842
	return(0);
843
}
844
845
/*
846
 * Do a specific command for a specific host
847
 */
848
static void
849
docmdhost(struct cmd *cmd, char **filev)
850
{
851
	checkcmd(cmd);
852
853
	/*
854
	 * If we're multi-threaded and we're the parent, spawn a
855
	 * new child process.
856
	 */
857
	if (do_fork && !amchild) {
858
		pid_t pid;
859
860
		/*
861
		 * If we're at maxchildren, wait for number of active
862
		 * children to fall below max number of children.
863
		 */
864
		while (activechildren >= maxchildren)
865
			waitup();
866
867
		pid = spawn(cmd, cmds);
868
		if (pid == 0)
869
			/* Child */
870
			amchild = 1;
871
		else
872
			/* Parent */
873
			return;
874
	}
875
876
	/*
877
	 * Disable NFS checks
878
	 */
879
	if (cmd->c_flags & CMD_NOCHKNFS)
880
		FLAG_OFF(options, DO_CHKNFS);
881
882
	if (!nflag) {
883
		currenthost = (cmd->c_name) ? cmd->c_name : "<unknown>";
884
		setproctitle("update %s", currenthost);
885
	}
886
887
	switch (cmd->c_type) {
888
	case ARROW:
889
		doarrow(cmd, filev);
890
		break;
891
	case DCOLON:
892
		dodcolon(cmd, filev);
893
		break;
894
	default:
895
		fatalerr("illegal command type %d", cmd->c_type);
896
	}
897
}
898
899
/*
900
 * Do a specific command (cmd)
901
 */
902
static void
903
docmd(struct cmd *cmd, int argc, char **argv)
904
{
905
	struct namelist *f;
906
	int i;
907
908
	if (argc) {
909
		for (i = 0; i < argc; i++) {
910
			if (cmd->c_label != NULL &&
911
			    strcmp(cmd->c_label, argv[i]) == 0) {
912
				docmdhost(cmd, NULL);
913
				return;
914
			}
915
			for (f = cmd->c_files; f != NULL; f = f->n_next)
916
				if (strcmp(f->n_name, argv[i]) == 0) {
917
					docmdhost(cmd, &argv[i]);
918
					return;
919
				}
920
		}
921
	} else
922
		docmdhost(cmd, NULL);
923
}
924
925
/*
926
 *
927
 * Multiple hosts are updated at once via a "ring" of at most
928
 * maxchildren rdist processes.  The parent rdist fork()'s a child
929
 * for a given host.  That child will update the given target files
930
 * and then continue scanning through the remaining targets looking
931
 * for more work for a given host.  Meanwhile, the parent gets the
932
 * next target command and makes sure that it hasn't encountered
933
 * that host yet since the children are responsible for everything
934
 * for that host.  If no children have done this host, then check
935
 * to see if the number of active proc's is less than maxchildren.
936
 * If so, then spawn a new child for that host.  Otherwise, wait
937
 * for a child to finish.
938
 *
939
 */
940
941
/*
942
 * Do the commands in cmds (initialized by yyparse).
943
 */
944
void
945
docmds(struct namelist *hostlist, int argc, char **argv)
946
{
947
	struct cmd *c;
948
	char *cp;
949
	int i;
950
951
	(void) signal(SIGHUP, sighandler);
952
	(void) signal(SIGINT, sighandler);
953
	(void) signal(SIGQUIT, sighandler);
954
	(void) signal(SIGTERM, sighandler);
955
956
	if (!nflag)
957
		setvbuf(stdout, NULL, _IOLBF, 0);
958
959
	/*
960
	 * Print errors for any command line targets we didn't find.
961
	 * If any errors are found, return to main() which will then exit.
962
	 */
963
	for (i = 0; i < argc; i++) {
964
		int found;
965
966
		for (found = FALSE, c = cmds; c != NULL; c = c->c_next) {
967
			if (c->c_label && argv[i] &&
968
			    strcmp(c->c_label, argv[i]) == 0) {
969
				found = TRUE;
970
				break;
971
			}
972
		}
973
		if (!found)
974
			error("Label \"%s\" is not defined in the distfile.",
975
			      argv[i]);
976
	}
977
	if (nerrs)
978
		return;
979
980
	/*
981
	 * Main command loop.  Loop through all the commands.
982
	 */
983
	for (c = cmds; c != NULL; c = c->c_next) {
984
		checkcmd(c);
985
		if (do_fork) {
986
			/*
987
			 * Let the children take care of their assigned host
988
			 */
989
			if (amchild) {
990
				if (strcmp(c->c_name, currenthost) != 0)
991
					continue;
992
			} else if (c->c_flags & CMD_ASSIGNED) {
993
				/* This cmd has been previously assigned */
994
				debugmsg(DM_MISC, "prev assigned: %s\n",
995
					 c->c_name);
996
				continue;
997
			}
998
		}
999
1000
		if (hostlist) {
1001
			/* Do specific hosts as specified on command line */
1002
			struct namelist *nlptr;
1003
1004
			for (nlptr = hostlist; nlptr; nlptr = nlptr->n_next)
1005
				/*
1006
				 * Try an exact match and then a match
1007
				 * without '@' (if present).
1008
				 */
1009
				if ((strcmp(c->c_name, nlptr->n_name) == 0) ||
1010
				    ((cp = strchr(c->c_name, '@')) &&
1011
				     strcmp(++cp, nlptr->n_name) == 0))
1012
					docmd(c, argc, argv);
1013
			continue;
1014
		} else
1015
			/* Do all of the command */
1016
			docmd(c, argc, argv);
1017
	}
1018
1019
	if (do_fork) {
1020
		/*
1021
		 * We're multi-threaded, so do appropriate shutdown
1022
		 * actions based on whether we're the parent or a child.
1023
		 */
1024
		if (amchild) {
1025
			if (!IS_ON(options, DO_QUIET))
1026
				message(MT_VERBOSE, "updating of %s finished",
1027
					currenthost);
1028
			closeconn();
1029
			cleanup(0);
1030
			exit(nerrs);
1031
		}
1032
1033
		/*
1034
		 * Wait for all remaining active children to finish
1035
		 */
1036
		while (activechildren > 0) {
1037
			debugmsg(DM_MISC,
1038
				 "Waiting for %d children to finish.\n",
1039
				 activechildren);
1040
			waitup();
1041
		}
1042
	} else if (!nflag) {
1043
		/*
1044
		 * We're single-threaded so close down current connection
1045
		 */
1046
		closeconn();
1047
		cleanup(0);
1048
	}
1049
}