/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #pragma ident "%Z%%M% %I% %E% SMI" /* * uucleanup - This is a program based on the heuristics * for cleaning up and doing something * useful with old files left in the uucp queues. * It also will send warning messags to users where requests are not * going out due to failure to contact the remote system. * * This program knows a lot about the construction and * contents of the C., D. and X. files. In addition, it * thinks it knows what mail and netnews data files look like. * * At present, this is what is done: * For WARNING messages: * C. files of age given by -W option are read, looking for * either user files to be sent or received, or * mail to be sent. (Other remote execution that * does not involve sending user files is not checked * for now.) In either of the cases, the user is * informed by mail that the request is not being * processed due to lack of communications with the remote * system, and the request will be deleted in the future * if it the condition remains for several more days. * * For DELETIONS: * C. files - if they reference only D. files, the C. is * merely deleted, because the D. files are usually * mail or news, and the later D. processing will * take care of them. * - if they reference files from the file system, * a message is constructed that will contain a * lines like * We can't contact the remote. * * local!file -> remote!otherfile * * can't be executed. * X. files - merely deleted at present - D.s will be taken * care of later. Besides, some of the D.s are * missing or else the X. wouldn't be left around. * D. files - mail type data is sent to a local person if that * is where it originated. If not, it is returned to the * sender -- assumed to be from the first From line. If * a sender can't be determing, the file is merely deleted. * - rnews: if locally generated, just delete. If remote, * the X. got lost, so execute rnews. * other files - just delete them. * .Workspace files over a day old * * Deletions and executions are logged in * (CLEANUPLOG)--/var/uucp/.Admin/uucleanup */ #include "uucp.h" #ifdef V7 #define O_RDONLY 0 #endif #define USAGE "[-oDAYS] [-mSTRING] [-Cdays] [-Ddays] [-Wdays] [-Xdays] [-xLEVEL] [-sSYSTEM]" extern int _age(); /* find the age of a file */ extern void procdtype(), oprocess(), xprocess(), cprocess(); extern void dXprocess(), dNprocess(), dMprocess(), dDprocess(), wprocess(); extern int toWho(), sendMail(), execRnews(); extern void logit(); /* need these dummys to satisy some .o files */ void cleanup(){} void systat(){} void logent(){} static void cleanworkspace(void); /* types of D. files */ #define D_MAIL 1 #define D_NEWS 2 #define D_XFILE 3 #define D_DATA 4 #define FULLNAME(full,dir,file) (void) sprintf(full, "%s/%s", dir, file); int _Ddays = 7; /* D. limit */ int _Cdays = 7; /* C. limit */ int _Xdays = 2; /* X. limit */ int _Odays = 2; /* O. limit */ int _Wdays = 1; /* Warning limit for C. files */ char _ShortLocal[6]; /* 5 char or less version of local name */ char *_Undeliverable[] = { "Subject: Undeliverable Mail\n", "This mail message is undeliverable.\n", "(Probably to or from system '%s')\n", "It was sent to you or by you.\n", "Sorry for the inconvenience.\n", "", }; #define CANT1 2 /* first line to fill in */ #define CANT2 3 /* second line to fill in */ char *_CantContact[] = { "Subject: Job Killed By uucp\n", "We can't contact machine '%s'.\n", " ", /* uucleanup will fill in variable text here */ " ", /* fill in jobid of killed job */ "", }; #define WARN1 2 #define WARN2 5 #define WARN3 6 char *_Warning[] = { "Subject: Warning From uucp\n", "We have been unable to contact machine '%s' since you queued your job.\n", " ", /* wprocess FILLS IN THIS LINE OF TEXT */ "Attempts will continue for a few more days.\n", "", " ", /* wprocess FILLS IN THIS LINE WITH: uucp job id is JOBid. */ " ", /* FILL IN THE -m STRING IF SPECIFIED */ "", }; int main(argc, argv, envp) int argc; char *argv[]; char **envp; { DIR *jcdir, *machdir, *spooldir; char fullname[MAXFULLNAME], statfile[MAXFULLNAME], machname[MAXFULLNAME]; char file1[NAMESIZE+1], file2[NAMESIZE+1], file3[NAMESIZE+1]; char soptName[MAXFULLNAME], lockname[MAXFULLNAME]; /* name from -s option */ int i, value; soptName[0] = NULLCHAR; (void) strcpy(Logfile, CLEANUPLOGFILE); uucpname(Myname); (void) strncpy(_ShortLocal, Myname, 5); _ShortLocal[5] = NULLCHAR; (void) strcpy(Progname, "uucleanup"); while ((i = getopt(argc, argv, "C:D:W:X:m:o:s:x:")) != EOF) { switch(i){ case 's': /* for debugging - choose system */ (void) strcpy(soptName, optarg); break; case 'x': Debug = atoi(optarg); if (Debug <= 0 || Debug >= 10) { fprintf(stderr, "WARNING: %s: invalid debug level %s ignored, using level 1\n", Progname, optarg); Debug = 1; } #ifdef SMALL fprintf(stderr, "WARNING: uucleanup built with SMALL flag defined -- no debug info available\n"); #endif /* SMALL */ break; case 'm': _Warning[WARN3] = optarg; break; default: (void) fprintf(stderr, "\tusage: %s %s\n", Progname, USAGE); exit(1); case 'C': case 'D': case 'W': case 'X': case 'o': value = atoi(optarg); if (value < 1) { fprintf(stderr," Options: CDWXo require value > 0\n"); exit(1); } switch(i) { case 'C': _Cdays = value; break; case 'D': _Ddays = value; break; case 'W': _Wdays = value; break; case 'X': _Xdays = value; break; case 'o': _Odays = value; break; } break; } } if (argc != optind) { (void) fprintf(stderr, "\tusage: %s %s\n", Progname, USAGE); exit(1); } DEBUG(5, "Progname (%s): STARTED\n", Progname); DEBUG(5, "Myname (%s), ", Myname); DEBUG(5, "_ShortLocal (%s)\n", _ShortLocal); DEBUG(5, "Days C.(%d), ", _Cdays); DEBUG(5, "D.(%d), ", _Ddays); DEBUG(5, "W.(%d), ", _Wdays); DEBUG(5, "X.(%d), ", _Xdays); DEBUG(5, "other (%d)\n", _Odays); cleanworkspace(); if (chdir(SPOOL) != 0) { (void) fprintf(stderr, "CAN'T CHDIR (%s): errno (%d)\n", SPOOL, errno); exit(1); } if ((spooldir = opendir(SPOOL)) == NULL) { (void) fprintf(stderr, "CAN'T OPEN (%s): errno (%d)\n", SPOOL, errno); exit(1); } while (gdirf(spooldir, file1, SPOOL) == TRUE) { if (*soptName && !EQUALS(soptName, file1)) continue; (void) strcpy(Rmtname, file1); (void) sprintf(machname, "%s/%s", SPOOL, file1); if ((machdir = opendir(machname)) == NULL) { (void) fprintf(stderr, "CAN'T OPEN (%s): errno (%d)\n", machname, errno); if (*soptName) break; else continue; } DEBUG(7, "Directory: (%s) is open\n", file1); while (gnamef(machdir, file2) == TRUE) { (void) sprintf(statfile, "%s/%s", machname, file2); if (DIRECTORY(statfile)) { (void) sprintf(lockname, "%s.%.*s.%s", LOCKPRE, SYSNSIZE, file1, file2); if (cklock(lockname)) continue; if ((jcdir = opendir(statfile)) == NULL) { (void) fprintf(stderr, "CAN'T OPEN (%s): errno (%d)\n", statfile, errno); continue; } DEBUG(7, "Directory: (%s) is open\n", file2); while (gnamef(jcdir, file3)) { DEBUG(9, "file: %s\n", file3); FULLNAME(fullname, statfile, file3); DEBUG(9,"Fullname is (%s)\n", fullname); if (EQUALSN(file3, "C.", 2)) { if (_age(fullname) >= _Cdays) cprocess(fullname); else if(_age(fullname) >= _Wdays) wprocess(statfile, file3); } else if (EQUALSN(file3, "D.", 2)) { if (_age(fullname) >= _Ddays) procdtype(statfile, file3); } else if (_age(fullname) >= _Odays) oprocess(fullname); } closedir(jcdir); continue; } DEBUG(9, "file: %s\n", file2); DEBUG(9, "Fullname is (%s)\n", statfile); if (EQUALSN(file2, "X.", 2)) { if (_age(statfile) >= _Xdays) xprocess(statfile); } else if (EQUALSN(file2, "D.", 2)) { if (_age(statfile) >= _Ddays) procdtype(machname, file2); } else if (_age(statfile) >= _Odays) oprocess(statfile); } closedir(machdir); } closedir(spooldir); return (0); } /* procdtype - select the type of processing that a D. file should receive */ void procdtype(dir, file) char *dir, *file; { char fullname[MAXFULLNAME]; FULLNAME(fullname, dir, file); switch(dType(fullname)) { case D_DATA: dDprocess(fullname); break; case D_MAIL: dMprocess(dir, file); break; case D_NEWS: dNprocess(dir, file); break; case D_XFILE: dXprocess(fullname); break; default: break; } return; } /* xprocess - X. file processing -- just remove the X. for now */ void xprocess(fullname) char *fullname; { char text[BUFSIZ]; DEBUG(5, "xprocess(%s), ", fullname); DEBUG(5, "unlink(%s)\n", fullname); (void) sprintf(text, "xprocess: unlink(%s)", fullname); errno = 0; (void) unlink(fullname); logit(text, errno); return; } /* * cprocess - Process old C. files * */ #define CMFMT "\n\t%s!%s -> %s!%s (Date %2.2d/%2.2d)\n\nCan't be executed." #define XFMT "\n\t%s!%s (Date %2.2d/%2.2d)\n" #define XMFMT "\n\tmail %s!%s (Date %2.2d/%2.2d)\n" #define WFMT "\n\t%s!%s -> %s!%s (Date %2.2d/%2.2d)\n" void cprocess(fullname) char *fullname; { struct stat s; struct tm *tp; char buf[BUFSIZ], user[9]; char file1[BUFSIZ], file2[BUFSIZ], file3[BUFSIZ], type[2], opt[256]; char text[BUFSIZ], text1[BUFSIZ], text2[BUFSIZ]; FILE *fp; int ret; DEBUG(5, "cprocess(%s)\n", fullname); fp = fopen(fullname, "r"); if (fp == NULL) { DEBUG(5, "Can't open file (%s), ", fullname); DEBUG(5, "errno=%d -- skip it!\n", errno); return; } if (fstat(fileno(fp), &s) != 0) { /* can't happen; _age() did stat of this file and file is opened */ (void) fclose(fp); return; } tp = localtime(&s.st_mtime); if (s.st_size == 0) { /* dummy C. for polling */ DEBUG(5, "dummy C. -- unlink(%s)\n", fullname); (void) sprintf(text, "dDprocess: dummy C. unlinked(%s)", fullname); errno = 0; (void) unlink(fullname); logit(text, errno); (void) fclose(fp); return; } /* Read the C. file and process it */ while (fgets(buf, BUFSIZ, fp) != NULL) { buf[strlen(buf)-1] = NULLCHAR; /* remove \n */ if (sscanf(buf,"%s%s%s%s%s%s", type, file1, file2, user, opt, file3) <5) { (void) sprintf(text, "cprocess: Bad C. %s, unlink(%s)", buf, fullname); break; } *text = NULLCHAR; ret = 0; /* fill in line 3 of text */ (void) sprintf(text2, "Job (%s) killed!\n", BASENAME(fullname, '/')+2); _CantContact[CANT2] = text2; if (*type == 'S') { if (EQUALSN(file1, "D.", 2)) /* generated file (mail/news) I think */ /* D. processing will return it later */ continue; /* some data was requested -- tell user */ (void) sprintf(text1, CMFMT, Myname, file1, Rmtname, file2, tp->tm_mon + 1, tp->tm_mday); _CantContact[CANT1] = text1; ret = sendMail((char *) NULL, user, "", _CantContact); } else if (*type == 'R') { (void) sprintf(text1, CMFMT, Rmtname, file1, Myname, file2, tp->tm_mon + 1, tp->tm_mday); _CantContact[CANT1] = text1; ret = sendMail((char *) NULL, user, "", _CantContact); } } if (!*text) { (void) sprintf(text, "cprocess: C. %s, mail returned (%d), unlink(%s)", buf, ret, fullname); } DEBUG(3, "text (%s)\n", text); errno = 0; (void) unlink(fullname); logit(text, errno); (void) fclose(fp); return; } /* * wprocess - send warning messages for C. == Wdays */ void wprocess(dir, file) char *dir, *file; { struct stat s; struct tm *tp; char fullname[BUFSIZ], xfile[BUFSIZ], xF_file[BUFSIZ]; char buf[BUFSIZ], user[BUFSIZ]; char file1[BUFSIZ], file2[BUFSIZ], file3[BUFSIZ], type[2], opt[256]; char text[BUFSIZ], text1[BUFSIZ], text2[BUFSIZ]; char *realuser, uline_m[NAMESIZE], uline_u[BUFSIZ], retaddr[BUFSIZ]; FILE *fp, *xfp; int ret; FULLNAME(fullname, dir, file); DEBUG(5, "wprocess(%s)\n", fullname); fp = fopen(fullname, "r"); if (fp == NULL) { DEBUG(4, "Can't open file (%s), ", fullname); DEBUG(4, "errno=%d -- skip it!\n", errno); return; } if (fstat(fileno(fp), &s) != 0) { /* can't happen; _age() did stat of this file and file is opened */ (void) fclose(fp); return; } tp = localtime(&s.st_mtime); if (s.st_size == 0) { /* dummy C. for polling */ DEBUG(5, "dummy C. -- skip(%s)\n", fullname); (void) fclose(fp); return; } /* read C. and process it */ while (fgets(buf, BUFSIZ, fp) != NULL) { buf[strlen(buf)-1] = NULLCHAR; /* remove \n */ if (sscanf(buf,"%s%s%s%s%s%s", type, file1, file2, user, opt, file3) <5) { DEBUG(5, "short line (%s): ", buf); DEBUG(5, "bad D. -- skip(%s)\n", fullname); (void) fclose(fp); return; } /* set up the 6th text line of the mail message */ (void) sprintf(text2, "\nuucp job id is %s.\n", BASENAME(fullname, '/')+2); _Warning[WARN2] = text2; /* if Send type then do C. file processing */ if (*type == 'S') { /* if this is a uux job - tell user about it */ if (EQUALSN(file2, "X.", 2)) { FULLNAME(xfile, dir, file1); /* if X.file can't be read then skip it */ if ((xfp = fopen(xfile, "r")) == NULL) { DEBUG(3, "Can't read %s\n", xfile); break; } *retaddr = *uline_u = *uline_m = *text = NULLCHAR; while (fgets(buf, BUFSIZ, xfp) != NULL) { /* remove \n from end of buffer */ buf[strlen(buf)-1] = NULLCHAR; switch(*buf) { /* save the file name */ case 'F': FULLNAME(xF_file, dir, &buf[2]); break; /* save return address */ case 'R': sscanf(buf+2, "%s", retaddr); DEBUG(7, "retaddr (%s)\n", retaddr); break; /* save machine, user */ case 'U': sscanf(buf+2, "%s%s", uline_u, uline_m); break; } if (buf[0] != 'C') continue; realuser = uline_u; if (*retaddr != NULLCHAR) realuser = retaddr; if (*realuser == NULLCHAR) strcpy(realuser, user); if (!EQUALS(uline_m, Myname)) sprintf(user, "%s!%s", uline_m, realuser); else strcpy(user, realuser); /* give mail special handling */ if (EQUALSN(buf+2, "rmail ", 6)) (void) sprintf(text1, XMFMT, Rmtname, buf+8, tp->tm_mon+1, tp->tm_mday); else (void) sprintf(text1, XFMT, Rmtname, buf+2, tp->tm_mon+1, tp->tm_mday); _Warning[WARN1] = text1; if (EQUALSN(&buf[2], "rmail", 5)) /* * this is mail; append * user mail (xF_file). */ ret = sendMail((char *) NULL, user, xF_file, _Warning); else ret = sendMail((char *) NULL, user, "", _Warning); break; } (void) fclose(xfp); break; } /* if file1 is a D. file the it might be (mail/news) */ /* if so then D. processing will take of it later */ if (EQUALSN(file1, "D.", 2)) continue; /* some data was requested -- tell user */ /* set up the 2nd text line of the mail message */ (void) sprintf(text1, WFMT, Myname, file1, Rmtname, file2, tp->tm_mon + 1, tp->tm_mday); _Warning[WARN1] = text1; ret = sendMail((char *) NULL, user, "", _Warning); } /* Receive C. file processing */ else if (*type == 'R') { if (EQUALSN(file1, "D.", 2) && EQUALSN(file2, "D.", 2)) continue; (void) sprintf(text1, WFMT, Rmtname, file1, Myname, file2, tp->tm_mon + 1, tp->tm_mday); _Warning[WARN1] = text1; ret = sendMail((char *) NULL, user, "", _Warning); } } /* end while - read C. lines loop */ (void) sprintf(text, "wprocess: %s: %s, warning message sent to %s, returned (%d)", fullname, buf, user, ret); DEBUG(3, "text (%s)\n", text); logit(text, errno); (void) fclose(fp); return; } /* * oprocess - some unknown file just remove the file */ void oprocess(fullname) char *fullname; { char *p, text[BUFSIZ]; p = BASENAME(fullname, '/'); if (EQUALSN(p, "P.", 2) == 0) if (_age(fullname) <= _Cdays) return; DEBUG(5, "oprocess(%s), ", fullname); DEBUG(5, "unlink(%s)\n", fullname); (void) sprintf(text, "oprocess: unlink(%s)", fullname); errno = 0; (void) unlink(fullname); logit(text, errno); return; } /* * dDprocess - random D. file (not mail or rnews) *--just delete it for now */ void dDprocess(fullname) char *fullname; { char text[BUFSIZ]; DEBUG(5, "dDprocess(%s), ", fullname); DEBUG(5, "unlink(%s)\n", fullname); (void) sprintf(text, "dDprocess: unlink(%s)", fullname); errno = 0; (void) unlink(fullname); logit(text, errno); return; } /* * dXprocess - process D. files that are destined for X. on remote * --for now just delete it */ void dXprocess(fullname) char *fullname; { char text[BUFSIZ]; DEBUG(5, "dXprocess(%s), ", fullname); DEBUG(5, " unlink(%s)\n", fullname); (void) sprintf(text, "dXprocess: unlink(%s)", fullname); errno = 0; (void) unlink(fullname); logit(text, errno); return; } /* * dMprocess - process ophan D. mail files * There are two types: ones generated locally and * others that are from remotes. They can be identified * by the system name following the D. * Local ones have the local name. */ void dMprocess(dir, file) char *dir, *file; { int ret; char fullname[MAXFULLNAME]; char *toUser, *toSystem; char text[BUFSIZ]; (void) sprintf(fullname, "%s/%s", dir, file); DEBUG(5, "dMprocess(%s)\n", fullname); if (PREFIX(_ShortLocal, &file[2])) { DEBUG(5, " Local file %s: ", file); } else { DEBUG(5, " Remote file %s: ", file); } if (toWho(fullname, &toUser, &toSystem)) { DEBUG(5, "toUser %s, ", toUser); DEBUG(5, "toSystem %s ", toSystem); ret = sendMail(toSystem, toUser, fullname, _Undeliverable); DEBUG(5, "Mail sent, unlink(%s)\n", fullname); (void) sprintf(text, "dMprocess: mail %s to %s!%s, returned (%d), unlink(%s)", fullname, toSystem, toUser, ret, fullname); errno = 0; (void) unlink(fullname); logit(text, errno); } return; } /* * dNprocess - process ophan D. netnews files * There are two types: ones generated locally and * others that are from remotes. They can be identified * by the system name following the D. * Local ones have the local name. */ void dNprocess(dir, file) char *dir, *file; { char fullname[MAXFULLNAME]; char text[BUFSIZ]; int ret; (void) sprintf(fullname, "%s/%s", dir, file); DEBUG(5, "dNprocess(%s)\n", fullname); if (PREFIX(_ShortLocal, &file[2])) { /* just delete it, the C. is gone */ DEBUG(5, " Local file %s, ", file); DEBUG(5, "unlink(%s)\n", fullname); (void) unlink(fullname); (void) sprintf(text, "dNprocess: Local news item unlink(%s)", fullname); errno = 0; (void) unlink(fullname); logit(text, errno); } else { /* execute rnews with this file - the X. is missing */ DEBUG(5, " Remote file %s, ", file); DEBUG(5, "exec rnews(%s), ", fullname); ret = execRnews(fullname); DEBUG(5, "unlink(%s)\n", fullname); (void) sprintf(text, "dNprocess: Remote - exec rnews %s: returned (%d), unlink(%s)", fullname, ret, fullname); errno = 0; (void) unlink(fullname); logit(text, errno); } return; } static long _sec_per_day = 86400L; /* * _age - find the age of "file" in days * return: * age of file * 0 - if stat fails */ int _age(fullname) char *fullname; { static time_t ptime = 0; time_t time(); struct stat stbuf; int e; if (!ptime) (void) time(&ptime); if (stat(fullname, &stbuf) != -1) { return ((int)((ptime - stbuf.st_mtime)/_sec_per_day)); } e = errno; DEBUG(9, "_age: stat (%s) failed", fullname); DEBUG(9, ", errno %d\n", e); return(0); } /* * dType - return the type of D. file * return: * FAIL - can't read D. file * D_MAIL - mail message D. file * D_NEWS - netnews D. file * D_DATA - other kind of D. file * D_XFILE - destined for X. on destination machine */ /* NLINES - number of lines of D. file to read to determine type */ #define NLINES 10 int dType(fullname) char *fullname; { char buf[BUFSIZ]; FILE *fp; int i, type; fp = fopen(fullname, "r"); if (fp == NULL) { DEBUG(4, "Can't open file (%s), ", fullname); DEBUG(4, "errno=%d -- skip it!\n", errno); return(FAIL); } type = D_DATA; /* read first NLINES lines to determine file type */ for (i=0; iFrom ", line)) return(0); s = strchr(line, ' ') + 1; for (i = 0; *s && *s != ' ' && *s != '\n'; i++) user[i] = *s++; user[i] = NULLCHAR; /* look for "remote from" */ while (*s && ((s = strchr(s, ' ')) != NULL)) { s++; if (PREFIX("remote from ", s)) { /* found it */ s = s + strlen("remote from "); for (i = 0; (i ptr to text string * status errno number * Returns: * none */ void logit(text, status) char *text; int status; { if (Nstat.t_pid == 0) Nstat.t_pid = getpid(); if (_Lf == NULL) { _Lf = fopen(Logfile, "a"); (void) chmod(Logfile, LOGFILEMODE); if (_Lf == NULL) return; setbuf(_Lf, CNULL); } (void) fseek(_Lf, 0L, 2); (void) fprintf(_Lf, "%s ", Rmtname); (void) fprintf(_Lf, "(%s,%ld,%d) ", timeStamp(), (long) Nstat.t_pid, Seqn); (void) fprintf(_Lf, "%s (%d)\n", text, status); return; } static void cleanworkspace(void) { DIR *spooldir; char f[MAXFULLNAME]; if (chdir(WORKSPACE) != 0) { (void) fprintf(stderr, "CAN'T CHDIR (%s): errno (%d)\n", WORKSPACE, errno); return; } if ((spooldir = opendir(WORKSPACE)) == NULL) { (void) fprintf(stderr, "CAN'T OPEN (%s): errno (%d)\n", WORKSPACE, errno); return; } while (gnamef(spooldir, f) == TRUE) if (_age(f) >= 1) if (unlink(f) != 0) (void) fprintf(stderr, "CAN'T UNLINK (%s): errno (%d)\n", f, errno); }