Bug Summary

File:src/usr.bin/rdist/docmd.c
Warning:line 166, column 3
Value stored to 'user' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name docmd.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/rdist/obj -resource-dir /usr/local/lib/clang/13.0.0 -I . -I /usr/src/usr.bin/rdist -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/rdist/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/rdist/docmd.c
1/* $OpenBSD: docmd.c,v 1.34 2019/06/28 13:35:03 deraadt 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(*__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
48struct subcmd *subcmds; /* list of sub-commands for
49 current cmd */
50struct namelist *filelist; /* list of source files */
51time_t lastmod; /* Last modify time */
52
53static void closeconn(void);
54static void notify(char *, struct namelist *, time_t);
55static void checkcmd(struct cmd *);
56static void markfailed(struct cmd *, struct cmd *);
57static int remotecmd(char *, char *, char *, char *);
58static int makeconn(char *);
59static void doarrow(struct cmd *, char **);
60static void rcmptime(struct stat *, struct subcmd *, char **);
61static void cmptime(char *, struct subcmd *, char **);
62static void dodcolon(struct cmd *, char **);
63static void docmdhost(struct cmd *, char **);
64static void docmd(struct cmd *, int, char **);
65
66/*
67 * Signal end of connection.
68 */
69static void
70closeconn(void)
71{
72 debugmsg(DM_CALL0x01, "closeconn() called\n");
73
74 if (rem_w >= 0) {
75 /* We don't care if the connection is still good or not */
76 signal(SIGPIPE13, SIG_IGN(void (*)(int))1);
77
78 (void) sendcmd(C_FERRMSG'\2', NULL((void *)0));
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 */
91static void
92notify(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[BUFSIZ1024];
99 char *file, *user;
100
101 if (IS_ON(options, DO_VERIFY)(options & 0x0000001) || to == NULL((void *)0))
102 return;
103
104 if ((file = getnotifyfile()) == NULL((void *)0))
105 return;
106
107 if (!IS_ON(options, DO_QUIET)(options & 0x0000100)) {
108 message(MT_INFO0x0040, "notify %s%s %s",
109 (rhost) ? "@" : "",
110 (rhost) ? rhost : "", getnlstr(to));
111 }
112
113 if (nflag)
114 return;
115
116 debugmsg(DM_MISC0x10, "notify() temp file = '%s'", file);
117
118 if ((fd = open(file, O_RDONLY0x0000)) == -1) {
119 error("%s: open for reading failed: %s", file, SYSERRstrerror((*__errno())));
120 return;
121 }
122 if (fstat(fd, &stb) == -1) {
123 error("%s: fstat failed: %s", file, SYSERRstrerror((*__errno())));
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"/usr/sbin/sendmail");
138 pf = popen(buf, "w");
139 if (pf == NULL((void *)0)) {
140 error("notify: \"%s\" failed\n", _PATH_SENDMAIL"/usr/sbin/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((void *)0))
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((void *)0)) {
157 if (!any('@', to->n_name) && rhost != NULL((void *)0))
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)(!__isthreaded ? __sputc('\n', pf) : (putc)('\n', pf));
164
165 if ((user = getlogin()) == NULL((void *)0))
166 user = locuser;
Value stored to 'user' is never read
167
168 if (rhost != NULL((void *)0))
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)(!__isthreaded ? __sputc('\n', pf) : (putc)('\n', pf));
176 (void) putc('\n', pf)(!__isthreaded ? __sputc('\n', pf) : (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 */
193static void
194checkcmd(struct cmd *cmd)
195{
196 int l;
197
198 if (!cmd || !(cmd->c_name)) {
199 debugmsg(DM_MISC0x10, "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_NOCHKNFS0x04;
208 cmd->c_name[l-1] = CNULL'\0';
209 }
210}
211
212/*
213 * Mark all other entries for this command (cmd)
214 * as assigned.
215 */
216void
217markassigned(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_ASSIGNED0x01;
226 }
227}
228
229/*
230 * Mark the command "cmd" as failed for all commands in list cmdlist.
231 */
232static void
233markfailed(struct cmd *cmd, struct cmd *cmdlist)
234{
235 struct cmd *pc;
236
237 if (!cmd) {
238 debugmsg(DM_MISC0x10, "markfailed() NULL cmd parameter");
239 return;
240 }
241
242 checkcmd(cmd);
243 cmd->c_flags |= CMD_CONNFAILED0x02;
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_CONNFAILED0x02;
249 }
250}
251
252static int
253remotecmd(char *rhost, char *luser, char *ruser, char *cmd)
254{
255 int desc;
256
257 debugmsg(DM_MISC0x10, "local user = %s remote user = %s\n", luser, ruser);
258 debugmsg(DM_MISC0x10, "Remote command = '%s'\n", cmd);
259
260 (void) fflush(stdout(&__sF[1]));
261 (void) fflush(stderr(&__sF[2]));
262 (void) signal(SIGALRM14, sighandler);
263 (void) alarm(RTIMEOUT900);
264
265 debugmsg(DM_MISC0x10, "Remote shell command = '%s'\n",
266 path_remsh ? path_remsh : "default");
267 (void) signal(SIGPIPE13, SIG_IGN(void (*)(int))1);
268 desc = rcmdsh(&rhost, -1, luser, ruser, cmd, path_remsh);
269 if (desc > 0)
270 (void) signal(SIGPIPE13, 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 */
281static int
282makeconn(char *rhost)
283{
284 char *ruser, *cp;
285 static char *cur_host = NULL((void *)0);
286 char tuser[BUFSIZ1024], buf[BUFSIZ1024];
287 u_char respbuff[BUFSIZ1024];
288 int n;
289
290 debugmsg(DM_CALL0x01, "makeconn(%s)", rhost);
291
292 /*
293 * See if we're already connected to this host
294 */
295 if (cur_host != NULL((void *)0) && 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((void *)0)) {
308 char c = *cp;
309
310 *cp = CNULL'\0';
311 (void) strlcpy((char *)tuser, rhost, sizeof(tuser));
312 *cp = c;
313 rhost = cp + 1;
314 ruser = tuser;
315 if (*ruser == CNULL'\0')
316 ruser = locuser;
317 else if (!okname(ruser))
318 return(0);
319 } else
320 ruser = locuser;
321
322 if (!IS_ON(options, DO_QUIET)(options & 0x0000100))
323 message(MT_VERBOSE0x2000, "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), TRUE1);
336 if (n <= 0 || respbuff[0] != S_VERSION'V') {
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'\0') {
352 /*
353 * The server wants us to send it our version number
354 */
355 (void) sendcmd(S_VERSION'V', "%d", VERSION6);
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 != VERSION6) {
364 fatalerr(
365 "Server version (%d) is not the same as local version (%d).",
366 proto_version, VERSION6);
367 return(0);
368 }
369 }
370
371 /*
372 * Send config commands
373 */
374 if (host[0]) {
375 (void) sendcmd(C_SETCONFIG'c', "%c%s", SC_HOSTNAME'H', host);
376 if (response() < 0)
377 return(0);
378 }
379 if (min_freespace) {
380 (void) sendcmd(C_SETCONFIG'c', "%c%lld", SC_FREESPACE's',
381 min_freespace);
382 if (response() < 0)
383 return(0);
384 }
385 if (min_freefiles) {
386 (void) sendcmd(C_SETCONFIG'c', "%c%lld", SC_FREEFILES'f',
387 min_freefiles);
388 if (response() < 0)
389 return(0);
390 }
391 if (remotemsglist) {
392 (void) sendcmd(C_SETCONFIG'c', "%c%s", SC_LOGGING'L', remotemsglist);
393 if (response() < 0)
394 return(0);
395 }
396 if (strcmp(defowner, "bin") != 0) {
397 (void) sendcmd(C_SETCONFIG'c', "%c%s", SC_DEFOWNER'o', defowner);
398 if (response() < 0)
399 return(0);
400 }
401 if (strcmp(defgroup, "bin") != 0) {
402 (void) sendcmd(C_SETCONFIG'c', "%c%s", SC_DEFGROUP'g', 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 */
413static void
414doarrow(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_MISC0x10, "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((void *)0)) {
441 error("No files to be updated on %s for target \"%s\"",
442 rhost, cmd->c_label);
443 return;
444 }
445
446 debugmsg(DM_CALL0x01, "doarrow(%p, %s, %p) start",
447 files, A(rhost)((rhost) ? rhost : "<null>"), sbcmds);
448
449 if (nflag)
450 (void) printf("updating host %s\n", rhost);
451 else {
452 if (cmd->c_flags & CMD_CONNFAILED0x02) {
453 debugmsg(DM_MISC0x10,
454 "makeconn %s failed before; skipping\n",
455 rhost);
456 return;
457 }
458
459 if (setjmp(finish_jmpbuf)) {
460 setjmp_ok = FALSE0;
461 debugmsg(DM_MISC0x10, "setjmp to finish_jmpbuf");
462 markfailed(cmd, cmds);
463 return;
464 }
465 setjmp_ok = TRUE1;
466
467 if (!makeconn(rhost)) {
468 setjmp_ok = FALSE0;
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((void *)0); sc = sc->sc_next) {
479 if (sc->sc_type != INSTALL6)
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((void *)0); /* 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)((s.st_mode & 0170000) == 0040000);
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_MISC0x10,
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_MISC0x10,
519 "Debug sc->sc_name=%p, destdir=%d, ddir=%d",
520 sc->sc_name, destdir, ddir);
521
522 for (f = files; f != NULL((void *)0); 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((void *)0); 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((void *)0), 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(SIGPIPE13, cleanup);
564
565 for (sc = sbcmds; sc != NULL((void *)0); sc = sc->sc_next)
566 if (sc->sc_type == NOTIFY7)
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((void *)0); freelinkinfo(l), l = nextl) {
573 nextl = l->nextp;
574 if (contimedout || IS_ON(opts, DO_IGNLNKS)(opts & 0x0000040) ||
575 l->count == 0)
576 continue;
577 message(MT_WARNING0x0010, "%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((void *)0);
583 }
584 setjmp_ok = FALSE0;
585}
586
587int
588okname(char *name)
589{
590 char *cp = name;
591 int c, isbad;
592
593 for (isbad = FALSE0; *cp && !isbad; ++cp) {
594 c = *cp;
595 if (c & 0200)
596 isbad = TRUE1;
597 if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
598 isbad = TRUE1;
599 }
600
601 if (isbad) {
602 error("Invalid user name \"%s\"\n", name);
603 return(0);
604 }
605 return(1);
606}
607
608static void
609rcmptime(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_CALL0x01, "rcmptime(%p) start", st);
618
619 if ((d = opendir((char *) target)) == NULL((void *)0)) {
620 error("%s: open directory failed: %s", target, SYSERRstrerror((*__errno())));
621 return;
622 }
623 optarget = ptarget;
624 len = ptarget - target;
625 while ((dp = readdir(d)) != NULL((void *)0)) {
626 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
627 continue;
628 if (len + 1 + (int)strlen(dp->d_name) >= BUFSIZ1024 - 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 */
648static void
649cmptime(char *name, struct subcmd *sbcmds, char **env)
650{
651 struct subcmd *sc;
652 struct stat stb;
653
654 debugmsg(DM_CALL0x01, "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((void *)0)) {
668 if (exptilde(target, name, sizeof(target)) == NULL((void *)0))
669 return;
670 ptarget = name = target;
671 while (*ptarget)
672 ptarget++;
673 }
674 if (access(name, R_OK0x04) == -1 || stat(name, &stb) == -1) {
675 error("%s: cannot access file: %s", name, SYSERRstrerror((*__errno())));
676 return;
677 }
678
679 if (S_ISDIR(stb.st_mode)((stb.st_mode & 0170000) == 0040000)) {
680 rcmptime(&stb, sbcmds, env);
681 return;
682 } else if (!S_ISREG(stb.st_mode)((stb.st_mode & 0170000) == 0100000)) {
683 error("%s: not a plain file", name);
684 return;
685 }
686
687 if (stb.st_mtimest_mtim.tv_sec > lastmod) {
688 message(MT_INFO0x0040, "%s: file is newer", name);
689 for (sc = sbcmds; sc != NULL((void *)0); sc = sc->sc_next) {
690 char buf[BUFSIZ1024];
691 if (sc->sc_type != SPECIAL10)
692 continue;
693 if (sc->sc_args != NULL((void *)0) && !inlist(sc->sc_args, name))
694 continue;
695 (void) snprintf(buf, sizeof(buf), "%s=%s;%s",
696 E_LOCFILE"FILE", name, sc->sc_name);
697 message(MT_CHANGE0x0020, "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)(options & 0x0000001))
705 continue;
706
707 runcommand(buf);
708 }
709 }
710}
711
712/*
713 * Process commands for comparing files to time stamp files.
714 */
715static void
716dodcolon(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_CALL0x01, "dodcolon()");
727
728 if (files == NULL((void *)0)) {
729 error("No files to be updated for target \"%s\"",
730 cmd->c_label);
731 return;
732 }
733 if (stat(stamp, &stb) == -1) {
734 error("%s: stat failed: %s", stamp, SYSERRstrerror((*__errno())));
735 return;
736 }
737
738 debugmsg(DM_MISC0x10, "%s: mtime %lld\n", stamp, (long long)stb.st_mtimest_mtim.tv_sec);
739
740 env = NULL((void *)0);
741 for (sc = sbcmds; sc != NULL((void *)0); sc = sc->sc_next) {
742 if (sc->sc_type == CMDSPECIAL11) {
743 env = xmalloc(sizeof(E_FILES"FILES") + 3);
744 (void) snprintf(env, sizeof(E_FILES"FILES") + 3,
745 "%s='", E_FILES"FILES");
746 break;
747 }
748 }
749
750 subcmds = sbcmds;
751 filelist = files;
752
753 lastmod = stb.st_mtimest_mtim.tv_sec;
754 if (!nflag && !IS_ON(options, DO_VERIFY)(options & 0x0000001))
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((void *)0); 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((void *)0);
769 cmptime(f->n_name, sbcmds, &env);
770 }
771
772 for (sc = sbcmds; sc != NULL((void *)0); sc = sc->sc_next) {
773 if (sc->sc_type == NOTIFY7)
774 notify(NULL((void *)0), sc->sc_args, (time_t)lastmod);
775 else if (sc->sc_type == CMDSPECIAL11 && env) {
776 size_t len = strlen(env);
777 if (env[len - 1] == ':')
778 env[--len] = CNULL'\0';
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_CHANGE0x0020, "cmdspecial \"%s\"", env);
784 if (!nflag && IS_OFF(options, DO_VERIFY)!((options & 0x0000001)))
785 runcommand(env);
786 (void) free(env);
787 env = NULL((void *)0); /* so cmdspecial is only called once */
788 }
789 }
790 if (!nflag && !IS_ON(options, DO_VERIFY)(options & 0x0000001) && (cp = getnotifyfile()))
791 (void) unlink(cp);
792}
793
794/*
795 * Return TRUE if file is in the exception list.
796 */
797int
798except(char *file)
799{
800 struct subcmd *sc;
801 struct namelist *nl;
802
803 debugmsg(DM_CALL0x01, "except(%s)", file);
804
805 for (sc = subcmds; sc != NULL((void *)0); sc = sc->sc_next) {
806 if (sc->sc_type == EXCEPT8) {
807 for (nl = sc->sc_args; nl != NULL((void *)0); nl = nl->n_next)
808 if (strcmp(file, nl->n_name) == 0)
809 return(1);
810 continue;
811 }
812 if (sc->sc_type == PATTERN9) {
813 for (nl = sc->sc_args; nl != NULL((void *)0); nl = nl->n_next) {
814 char ebuf[BUFSIZ1024];
815 int ecode = 0;
816
817 /* allocate and compile n_regex as needed */
818 if (nl->n_regex == NULL((void *)0)) {
819 nl->n_regex = xmalloc(sizeof(regex_t));
820 ecode = regcomp(nl->n_regex, nl->n_name,
821 REG_NOSUB0004);
822 }
823 if (ecode == 0) {
824 ecode = regexec(nl->n_regex, file, 0,
825 NULL((void *)0), 0);
826 }
827 switch (ecode) {
828 case REG_NOMATCH1:
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 */
848static void
849docmdhost(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_NOCHKNFS0x04)
880 FLAG_OFF(options, DO_CHKNFS)options &= ~(0x0000200);
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 ARROW1:
889 doarrow(cmd, filev);
890 break;
891 case DCOLON3:
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 */
902static void
903docmd(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((void *)0) &&
911 strcmp(cmd->c_label, argv[i]) == 0) {
912 docmdhost(cmd, NULL((void *)0));
913 return;
914 }
915 for (f = cmd->c_files; f != NULL((void *)0); 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((void *)0));
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 */
944void
945docmds(struct namelist *hostlist, int argc, char **argv)
946{
947 struct cmd *c;
948 char *cp;
949 int i;
950
951 (void) signal(SIGHUP1, sighandler);
952 (void) signal(SIGINT2, sighandler);
953 (void) signal(SIGQUIT3, sighandler);
954 (void) signal(SIGTERM15, sighandler);
955
956 if (!nflag)
957 setvbuf(stdout(&__sF[1]), NULL((void *)0), _IOLBF1, 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 = FALSE0, c = cmds; c != NULL((void *)0); c = c->c_next) {
967 if (c->c_label && argv[i] &&
968 strcmp(c->c_label, argv[i]) == 0) {
969 found = TRUE1;
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((void *)0); 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_ASSIGNED0x01) {
993 /* This cmd has been previously assigned */
994 debugmsg(DM_MISC0x10, "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)(options & 0x0000100))
1026 message(MT_VERBOSE0x2000, "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_MISC0x10,
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}