File: | src/usr.bin/rdist/docmd.c |
Warning: | line 166, column 3 Value stored to 'user' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | |
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_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 | */ |
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[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 | */ |
193 | static void |
194 | checkcmd(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 | */ |
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_ASSIGNED0x01; |
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_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 | |
252 | static int |
253 | remotecmd(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 | */ |
281 | static int |
282 | makeconn(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 | */ |
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_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 | |
587 | int |
588 | okname(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 | |
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_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 | */ |
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_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 | */ |
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_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 | */ |
797 | int |
798 | except(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 | */ |
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_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 | */ |
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((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 | */ |
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(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 | } |