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