* Silence -Wold-style-definition (i.e., ansify).
[dragonfly.git] / usr.bin / rdist / docmd.c
1 /*
2  * Copyright (c) 1983, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#)docmd.c  8.1 (Berkeley) 6/9/93
34  * $FreeBSD: src/usr.bin/rdist/docmd.c,v 1.12 1999/08/28 01:05:06 peter Exp $
35  * $DragonFly: src/usr.bin/rdist/docmd.c,v 1.5 2008/10/16 01:52:32 swildner Exp $
36  */
37
38 #include <sys/types.h>
39
40 #include <netdb.h>
41 #include <regex.h>
42 #include <setjmp.h>
43
44 #include "defs.h"
45
46 FILE    *lfp;                   /* log file for recording files updated */
47 struct  subcmd *subcmds;        /* list of sub-commands for current cmd */
48 jmp_buf env;
49
50 static int       makeconn(char *);
51 static int       okname(char *);
52 static void      closeconn(void);
53 static void      cmptime(char *);
54 static void      doarrow(char **,
55                     struct namelist *, char *, struct subcmd *);
56 static void      dodcolon(char **,
57                     struct namelist *, char *, struct subcmd *);
58 static void      notify(char *, char *, struct namelist *, time_t);
59 static void      rcmptime(struct stat *);
60 static int       remotecmd(char *, char *, char *, char *);
61
62 /*
63  * Do the commands in cmds (initialized by yyparse).
64  */
65 void
66 docmds(char **dhosts, int argc, char **argv)
67 {
68         struct cmd *c;
69         struct namelist *f;
70         char **cpp;
71         extern struct cmd *cmds;
72
73         signal(SIGHUP, cleanup);
74         signal(SIGINT, cleanup);
75         signal(SIGQUIT, cleanup);
76         signal(SIGTERM, cleanup);
77
78         for (c = cmds; c != NULL; c = c->c_next) {
79                 if (dhosts != NULL && *dhosts != NULL) {
80                         for (cpp = dhosts; *cpp; cpp++)
81                                 if (strcmp(c->c_name, *cpp) == 0)
82                                         goto fndhost;
83                         continue;
84                 }
85         fndhost:
86                 if (argc) {
87                         for (cpp = argv; *cpp; cpp++) {
88                                 if (c->c_label != NULL &&
89                                     strcmp(c->c_label, *cpp) == 0) {
90                                         cpp = NULL;
91                                         goto found;
92                                 }
93                                 for (f = c->c_files; f != NULL; f = f->n_next)
94                                         if (strcmp(f->n_name, *cpp) == 0)
95                                                 goto found;
96                         }
97                         continue;
98                 } else
99                         cpp = NULL;
100         found:
101                 switch (c->c_type) {
102                 case ARROW:
103                         doarrow(cpp, c->c_files, c->c_name, c->c_cmds);
104                         break;
105                 case DCOLON:
106                         dodcolon(cpp, c->c_files, c->c_name, c->c_cmds);
107                         break;
108                 default:
109                         fatal("illegal command type %d\n", c->c_type);
110                 }
111         }
112         closeconn();
113 }
114
115 /*
116  * Process commands for sending files to other machines.
117  */
118 static void
119 doarrow(char **filev, struct namelist *files, char *rhost, struct subcmd *cmds)
120 {
121         struct namelist *f;
122         struct subcmd *sc;
123         char **cpp;
124         int n, ddir, opts = options;
125
126         if (debug)
127                 printf("doarrow(%p, %s, %p)\n", files, rhost, cmds);
128
129         if (files == NULL) {
130                 error("no files to be updated\n");
131                 return;
132         }
133
134         subcmds = cmds;
135         ddir = files->n_next != NULL;   /* destination is a directory */
136         if (nflag)
137                 printf("updating host %s\n", rhost);
138         else {
139                 if (setjmp(env))
140                         goto done;
141                 signal(SIGPIPE, lostconn);
142                 if (!makeconn(rhost))
143                         return;
144                 if ((lfp = fopen(tempfile, "w")) == NULL) {
145                         fatal("cannot open %s\n", tempfile);
146                         exit(1);
147                 }
148         }
149         for (f = files; f != NULL; f = f->n_next) {
150                 if (filev) {
151                         for (cpp = filev; *cpp; cpp++)
152                                 if (strcmp(f->n_name, *cpp) == 0)
153                                         goto found;
154                         if (!nflag)
155                                 fclose(lfp);
156                         continue;
157                 }
158         found:
159                 n = 0;
160                 for (sc = cmds; sc != NULL; sc = sc->sc_next) {
161                         if (sc->sc_type != INSTALL)
162                                 continue;
163                         n++;
164                         install(f->n_name, sc->sc_name,
165                                 sc->sc_name == NULL ? 0 : ddir, sc->sc_options);
166                         opts = sc->sc_options;
167                 }
168                 if (n == 0)
169                         install(f->n_name, NULL, 0, options);
170         }
171 done:
172         if (!nflag) {
173                 signal(SIGPIPE, cleanup);
174                 if (lfp)
175                         fclose(lfp);
176                 lfp = NULL;
177         }
178         for (sc = cmds; sc != NULL; sc = sc->sc_next)
179                 if (sc->sc_type == NOTIFY)
180                         notify(tempfile, rhost, sc->sc_args, 0);
181         if (!nflag) {
182                 struct linkbuf *nextihead;
183
184                 unlink(tempfile);
185                 for (; ihead != NULL; ihead = nextihead) {
186                         nextihead = ihead->nextp;
187                         if ((opts & IGNLNKS) || ihead->count == 0)
188                                 continue;
189                         log(lfp, "%s: Warning: missing links\n",
190                                 ihead->pathname);
191                         free(ihead);
192                 }
193         }
194 }
195
196 static int remotecmd(char *rhost, char *luser, char *ruser, char *cmd)
197 {
198         int desc;
199         static int port = -1;
200
201         if (debug) {
202                 printf("local user = %s remote user = %s\n", luser, ruser);
203                 printf("Remote command = '%s'\n", cmd);
204         }
205
206         fflush(stdout);
207         fflush(stderr);
208         signal(SIGALRM, cleanup);
209         alarm(RTIMEOUT);
210
211         if (geteuid() == 0 && strcmp(path_rsh, _PATH_RSH) == 0) {
212                 if (debug) 
213                         printf("I am root, therefore direct rcmd\n");
214
215                 signal(SIGPIPE, cleanup);
216
217                 if (port < 0) {
218                         struct servent *sp;
219                 
220                         if ((sp = getservbyname("shell", "tcp")) == NULL)
221                                 fatal("shell/tcp: unknown service");
222                         port = sp->s_port;
223                 }
224
225                 desc = rcmd(&rhost, port, luser, ruser, cmd, 0);
226         } else {
227                 if (debug)
228                         printf("Remote shell command = '%s'\n", path_rsh);
229                 signal(SIGPIPE, SIG_IGN);
230                 desc = rshrcmd(&rhost, -1, luser, ruser, cmd, 0);
231                 if (desc > 0)
232                         signal(SIGPIPE, cleanup);
233         }
234
235         alarm(0);
236
237         return(desc);
238 }
239
240
241 /*
242  * Create a connection to the rdist server on the machine rhost.
243  */
244 static int
245 makeconn(char *rhost)
246 {
247         char *ruser, *cp;
248         static char *cur_host = NULL;
249         static int port = -1;
250         char tuser[20];
251         int n;
252         extern char user[];
253 #if defined(DIRECT_RCMD)
254         extern int userid;
255 #endif
256
257         if (debug)
258                 printf("makeconn(%s)\n", rhost);
259
260         if (cur_host != NULL && rem >= 0) {
261                 if (strcmp(cur_host, rhost) == 0)
262                         return(1);
263                 closeconn();
264         }
265         cur_host = rhost;
266         cp = index(rhost, '@');
267         if (cp != NULL) {
268                 char c = *cp;
269
270                 *cp = '\0';
271                 strncpy(tuser, rhost, sizeof(tuser)-1);
272                 *cp = c;
273                 rhost = cp + 1;
274                 ruser = tuser;
275                 if (*ruser == '\0')
276                         ruser = user;
277                 else if (!okname(ruser))
278                         return(0);
279         } else
280                 ruser = user;
281         if (!qflag)
282                 printf("updating host %s\n", rhost);
283         snprintf(buf, sizeof(buf), "%s -Server%s", _PATH_RDIST, qflag ? " -q" : "");
284         if (port < 0) {
285                 struct servent *sp;
286
287                 if ((sp = getservbyname("shell", "tcp")) == NULL)
288                         fatal("shell/tcp: unknown service");
289                 port = sp->s_port;
290         }
291
292         if (debug) {
293                 printf("port = %d, luser = %s, ruser = %s\n", ntohs(port), user, ruser);
294                 printf("buf = %s\n", buf);
295         }
296
297         fflush(stdout);
298 #if defined(DIRECT_RCMD)
299         seteuid(0);
300         rem = rcmd(&rhost, port, user, ruser, buf, 0);
301         seteuid(userid);
302 #else
303         rem = remotecmd(rhost, user, ruser, buf);
304 #endif
305         if (rem < 0)
306                 return(0);
307         cp = buf;
308         if (read(rem, cp, 1) != 1)
309                 lostconn(0);
310         if (*cp == 'V') {
311                 do {
312                         if (read(rem, cp, 1) != 1)
313                                 lostconn(0);
314                 } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
315                 *--cp = '\0';
316                 cp = buf;
317                 n = 0;
318                 while (*cp >= '0' && *cp <= '9')
319                         n = (n * 10) + (*cp++ - '0');
320                 if (*cp == '\0' && n == VERSION)
321                         return(1);
322                 error("connection failed: version numbers don't match (local %d, remote %d)\n", VERSION, n);
323         } else {
324                 error("connection failed: version numbers don't match\n");
325                 error("got unexpected input:");
326                 do {
327                         error("%c", *cp);
328                 } while (*cp != '\n' && read(rem, cp, 1) == 1);
329         }
330         closeconn();
331         return(0);
332 }
333
334 /*
335  * Signal end of previous connection.
336  */
337 static void
338 closeconn(void)
339 {
340         if (debug)
341                 printf("closeconn()\n");
342
343         if (rem >= 0) {
344                 write(rem, "\2\n", 2);
345                 close(rem);
346                 rem = -1;
347         }
348 }
349
350 void
351 lostconn(int signo)
352 {
353         if (iamremote)
354                 cleanup(0);
355         log(lfp, "rdist: lost connection\n");
356         longjmp(env, 1);
357 }
358
359 static int
360 okname(char *name)
361 {
362         char *cp;
363         int c;
364
365         cp = name;
366
367         do {
368                 c = *cp;
369                 if (c & 0200)
370                         goto bad;
371                 if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
372                         goto bad;
373                 cp++;
374         } while (*cp);
375         return(1);
376 bad:
377         error("invalid user name %s\n", name);
378         return(0);
379 }
380
381 time_t  lastmod;
382 FILE    *tfp;
383 extern  char target[], *tp;
384
385 /*
386  * Process commands for comparing files to time stamp files.
387  */
388 static void
389 dodcolon(char **filev, struct namelist *files, char *stamp, struct subcmd *cmds)
390 {
391         struct subcmd *sc;
392         struct namelist *f;
393         char **cpp;
394         struct timeval tv[2];
395         struct timezone tz;
396         struct stat stb;
397
398         if (debug)
399                 printf("dodcolon()\n");
400
401         if (files == NULL) {
402                 error("no files to be updated\n");
403                 return;
404         }
405         if (stat(stamp, &stb) < 0) {
406                 error("%s: %s\n", stamp, strerror(errno));
407                 return;
408         }
409         if (debug)
410                 printf("%s: %ld\n", stamp, stb.st_mtime);
411
412         subcmds = cmds;
413         lastmod = stb.st_mtime;
414         if (nflag || (options & VERIFY))
415                 tfp = NULL;
416         else {
417                 if ((tfp = fopen(tempfile, "w")) == NULL) {
418                         error("%s: %s\n", stamp, strerror(errno));
419                         return;
420                 }
421                 gettimeofday(&tv[0], &tz);
422                 tv[1] = tv[0];
423                 utimes(stamp, tv);
424         }
425
426         for (f = files; f != NULL; f = f->n_next) {
427                 if (filev) {
428                         for (cpp = filev; *cpp; cpp++)
429                                 if (strcmp(f->n_name, *cpp) == 0)
430                                         goto found;
431                         continue;
432                 }
433         found:
434                 tp = NULL;
435                 cmptime(f->n_name);
436         }
437
438         if (tfp != NULL)
439                 fclose(tfp);
440         for (sc = cmds; sc != NULL; sc = sc->sc_next)
441                 if (sc->sc_type == NOTIFY)
442                         notify(tempfile, NULL, sc->sc_args, lastmod);
443         if (!nflag && !(options & VERIFY))
444                 unlink(tempfile);
445 }
446
447 /*
448  * Compare the mtime of file to the list of time stamps.
449  */
450 static void
451 cmptime(char *name)
452 {
453         struct stat stb;
454
455         if (debug)
456                 printf("cmptime(%s)\n", name);
457
458         if (except(name))
459                 return;
460
461         if (nflag) {
462                 printf("comparing dates: %s\n", name);
463                 return;
464         }
465
466         /*
467          * first time cmptime() is called?
468          */
469         if (tp == NULL) {
470                 if (exptilde(target, name, sizeof(target)) == NULL)
471                         return;
472                 tp = name = target;
473                 while (*tp)
474                         tp++;
475         }
476         if (access(name, 4) < 0 || stat(name, &stb) < 0) {
477                 error("%s: %s\n", name, strerror(errno));
478                 return;
479         }
480
481         switch (stb.st_mode & S_IFMT) {
482         case S_IFREG:
483                 break;
484
485         case S_IFDIR:
486                 rcmptime(&stb);
487                 return;
488
489         default:
490                 error("%s: not a plain file\n", name);
491                 return;
492         }
493
494         if (stb.st_mtime > lastmod)
495                 log(tfp, "new: %s\n", name);
496 }
497
498 static void
499 rcmptime(struct stat *st)
500 {
501         DIR *d;
502         struct dirent *dp;
503         char *cp;
504         char *otp;
505         int len;
506
507         if (debug)
508                 printf("rcmptime(%p)\n", st);
509
510         if ((d = opendir(target)) == NULL) {
511                 error("%s: %s\n", target, strerror(errno));
512                 return;
513         }
514         otp = tp;
515         len = tp - target;
516         while ((dp = readdir(d))) {
517                 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
518                         continue;
519                 if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
520                         error("%s/%s: Name too long\n", target, dp->d_name);
521                         continue;
522                 }
523                 tp = otp;
524                 *tp++ = '/';
525                 cp = dp->d_name;
526                 while ((*tp++ = *cp++))
527                         ;
528                 tp--;
529                 cmptime(target);
530         }
531         closedir(d);
532         tp = otp;
533         *tp = '\0';
534 }
535
536 /*
537  * Notify the list of people the changes that were made.
538  * rhost == NULL if we are mailing a list of changes compared to at time
539  * stamp file.
540  */
541 static void
542 notify(char *file, char *rhost, struct namelist *to, time_t lmod)
543 {
544         int fd, len;
545         struct stat stb;
546         FILE *pf;
547
548         if ((options & VERIFY) || to == NULL)
549                 return;
550         if (!qflag) {
551                 printf("notify ");
552                 if (rhost)
553                         printf("@%s ", rhost);
554                 prnames(to);
555         }
556         if (nflag)
557                 return;
558
559         if ((fd = open(file, 0)) < 0) {
560                 error("%s: %s\n", file, strerror(errno));
561                 return;
562         }
563         if (fstat(fd, &stb) < 0) {
564                 error("%s: %s\n", file, strerror(errno));
565                 close(fd);
566                 return;
567         }
568         if (stb.st_size == 0) {
569                 close(fd);
570                 return;
571         }
572         /*
573          * Create a pipe to mailling program.
574          */
575         snprintf(buf, sizeof(buf), "%s -oi -t", _PATH_SENDMAIL);
576         pf = popen(buf, "w");
577         if (pf == NULL) {
578                 error("notify: \"%s\" failed\n", _PATH_SENDMAIL);
579                 close(fd);
580                 return;
581         }
582         /*
583          * Output the proper header information.
584          */
585         fprintf(pf, "From: rdist (Remote distribution program)\n");
586         fprintf(pf, "To:");
587         if (!any('@', to->n_name) && rhost != NULL)
588                 fprintf(pf, " %s@%s", to->n_name, rhost);
589         else
590                 fprintf(pf, " %s", to->n_name);
591         to = to->n_next;
592         while (to != NULL) {
593                 if (!any('@', to->n_name) && rhost != NULL)
594                         fprintf(pf, ", %s@%s", to->n_name, rhost);
595                 else
596                         fprintf(pf, ", %s", to->n_name);
597                 to = to->n_next;
598         }
599         putc('\n', pf);
600         if (rhost != NULL)
601                 fprintf(pf, "Subject: files updated by rdist from %s to %s\n",
602                         host, rhost);
603         else
604                 fprintf(pf, "Subject: files updated after %s\n", ctime(&lmod));
605         putc('\n', pf);
606
607         while ((len = read(fd, buf, BUFSIZ)) > 0)
608                 fwrite(buf, 1, len, pf);
609
610         close(fd);
611         pclose(pf);
612 }
613
614 /*
615  * Return true if name is in the list.
616  */
617 int
618 inlist(struct namelist *list, char *file)
619 {
620         struct namelist *nl;
621
622         for (nl = list; nl != NULL; nl = nl->n_next)
623                 if (!strcmp(file, nl->n_name))
624                         return(1);
625         return(0);
626 }
627
628 /*
629  * Return TRUE if file is in the exception list.
630  */
631 int
632 except(char *file)
633 {
634         struct  subcmd *sc;
635         struct  namelist *nl;
636         regex_t rx;
637         int val;
638
639         if (debug)
640                 printf("except(%s)\n", file);
641
642         for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
643                 if (sc->sc_type != EXCEPT && sc->sc_type != PATTERN)
644                         continue;
645                 for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) {
646                         if (sc->sc_type == EXCEPT) {
647                                 if (!strcmp(file, nl->n_name))
648                                         return(1);
649                                 continue;
650                         }
651                         val = regcomp(&rx, nl->n_name,
652                                       REG_EXTENDED | REG_NOSUB);
653                         if (!regexec(&rx, file, 0, 0, 0)) {
654                                 regfree(&rx);
655                                 return 1;
656                         }
657                         regfree(&rx);
658                 }
659         }
660         return(0);
661 }
662
663 char *
664 colon(char *cp)
665 {
666
667         while (*cp) {
668                 if (*cp == ':')
669                         return(cp);
670                 if (*cp == '/')
671                         return(0);
672                 cp++;
673         }
674         return(0);
675 }