1: #include "parms.h" 2: #include "structs.h" 3: 4: #ifdef RCSIDENT 5: static char rcsid[] = "$Header: archiver.c,v 1.7.0.4 85/06/05 14:16:24 notes Rel $"; 6: #endif RCSIDENT 7: 8: /* 9: * archiver - archives a notesfile. Takes all articles older 10: * than 'daysold' days and places them, in generic format, in 11: * a sub-directory in the archie directory. The files are marked 12: * by the time that they were created. 13: * The deleteonly parameter is normally zero. If it is non-zero, 14: * no archive is taken; the old notes are merely thrown away. 15: * 16: * Ray Essick March 1982 17: * 18: * modified so that could also toggle on the director message. 19: * in addition to the days untouched. 20: * Ray Essick June 1982 21: * 22: * Now grabs an expiration threshold and a "working set size" 23: * from the notesfile itself. If zero, the values passed 24: * in as paramaters are used. 25: */ 26: 27: archiver (nfname, daysold, worksetsize, deleteonly, dirmsgflag) 28: char *nfname; 29: int daysold; 30: int worksetsize; 31: int deleteonly; 32: int dirmsgflag; 33: { 34: struct io_f io, 35: archio; 36: struct when_f zaptime; /* boundary time */ 37: struct note_f note; 38: struct note_f note2; 39: int i, 40: ncount, 41: rcount; 42: int dnotes, /* duplicates */ 43: dresps, /* in the archive */ 44: adopts; /* and adoptions */ 45: int deletable; /* how many can zap */ 46: int presps; 47: char line[WDLEN]; 48: char archdest[WDLEN]; /* target notesfile */ 49: char archbase[WDLEN]; /* target directory */ 50: char archend[WDLEN]; /* and nf name */ 51: char timeline[DATELEN]; 52: char *endname; 53: FILE * log; 54: int wasopen; 55: int locktarget; /* whether to */ 56: struct daddr_f where; 57: int rnum; /* copy responses */ 58: int newnum; /* note place in arch */ 59: int rblock, 60: roffset; 61: struct resp_f resp; 62: FILE * txtfile; /* for saving text */ 63: char txtfn[WDLEN]; /* its name */ 64: int dup_place; /* is in archive? */ 65: int dup_resp; /* dup supression */ 66: 67: if (init (&io, nfname) < 0) 68: return (-1); /* no notesfile */ 69: 70: 71: if (allow (&io, DRCTOK) == 0 && globuid != Notesuid) 72: { 73: closenf (&io); 74: printf ("Archiver: %s: You don't have permission to archive\n", 75: io.fullname); 76: fflush (stdout); 77: return (-1); 78: } 79: 80: if (io.descr.d_stat & ISARCH) /* can't archive an archive */ 81: { 82: closenf (&io); 83: printf ("Archiver: %s: You can't archive an archive\n", io.fullname); 84: fflush (stdout); 85: return (-1); 86: } 87: 88: /* 89: * select the archive name 90: */ 91: 92: switch (nfalias (io.fullname, archdest, ARCHALIAS)) 93: { 94: case -1: /* no file */ 95: case 0: /* no match */ 96: if (*nfname == '/') /* absolute path name */ 97: { 98: strcpy (archend, io.nf); /* get nf */ 99: strcpy (archbase, ARCHDIR); /* base directory */ 100: printf ("Archiver: WARNING: possible naming conflict in %s (%s)\n", 101: nfname, io.fullname); 102: fflush (stdout); 103: } 104: else 105: { 106: strcpy (archend, io.nf); 107: strcpy (archbase, ARCHDIR); /* base directory */ 108: } 109: break; 110: 111: case 1: /* an alias! */ 112: if (archdest[0] != '/') /* expand it */ 113: { 114: strcpy (archbase, ARCHDIR); 115: strcpy (archend, archdest); /* hold it */ 116: } 117: else 118: { 119: endname = rindex (archdest, '/'); 120: *endname++ = '\0'; /* split */ 121: strcpy (archbase, archdest); /* directory */ 122: strcpy (archend, endname); /* and nf */ 123: } 124: break; 125: 126: } 127: 128: sprintf (archdest, "%s/%s", archbase, archend); /* full name */ 129: sprintf (txtfn, "/tmp/nfa%d", getpid ()); /* hold texts */ 130: ncount = rcount = 0; /* count archived */ 131: dnotes = dresps = adopts = 0; /* duplicates */ 132: locktarget = 0; /* changed if should */ 133: 134: /* 135: * check notesfile specific thresholds, sizes and other options 136: */ 137: 138: if (io.descr.d_archtime == NEVER) /* don't archive */ 139: { 140: printf ("Archiver: %s has archive threshold of `never'\n", 141: nfname); 142: fflush (stdout); 143: goto docompress; /* compress anyway */ 144: } 145: 146: if (io.descr.d_archtime != 0) /* non-default */ 147: { 148: daysold = (int) io.descr.d_archtime; /* use this one */ 149: printf ("Archiver: %s specifies threshold of %d days\n", 150: nfname, daysold); 151: fflush (stdout); 152: } 153: 154: if (io.descr.d_workset != 0) 155: { 156: worksetsize = (int) io.descr.d_workset; 157: printf ("Archiver: %s specifies working set size of %d\n", 158: nfname, worksetsize); 159: fflush (stdout); 160: } 161: 162: if (io.descr.d_dmesgstat != DIRDFLT) /* specific */ 163: { 164: dirmsgflag = (int) io.descr.d_dmesgstat; /* set it */ 165: printf ("Archiver: %s specifies dirmsg status of %s for expiring\n", 166: nfname, 167: dirmsgflag == DIRON ? "ON" : 168: dirmsgflag == DIROFF ? "OFF" : 169: "NOCARE"); 170: fflush (stdout); 171: } 172: 173: if (io.descr.d_archkeep != KEEPDFLT) /* keep/delete */ 174: { 175: if (io.descr.d_archkeep == KEEPYES) 176: deleteonly = 0; 177: else 178: deleteonly = 1; 179: printf ("Archiver: %s specifies %s expired notes\n", 180: nfname, 181: deleteonly ? "deleting" : "archiving"); 182: fflush (stdout); 183: } 184: 185: deletable = ((int) io.descr.d_nnote) - ((int) io.descr.d_delnote) - worksetsize; 186: if (deletable <= 0) /* candidates? */ 187: { 188: if (io.descr.d_nnote - io.descr.d_delnote > 0) /* only if non-empty */ 189: { 190: printf ("Archiver: %s: %d notes <= working set size of %d\n", 191: nfname, 192: io.descr.d_nnote - io.descr.d_delnote, 193: worksetsize); 194: fflush (stdout); 195: } 196: goto docompress; 197: } 198: 199: gettime (&zaptime); /* threshold */ 200: zaptime.w_gmttime -= 60L * 60L * 24L * ((long) daysold);/* internal */ 201: maketime (&zaptime, zaptime.w_gmttime); /* re-format */ 202: 203: 204: if (!deleteonly) 205: { 206: if (init (&archio, archdest) < 0) /* not already */ 207: { 208: printf ("Archiver creating archive notesfile %s\n", archdest); 209: fflush (stdout); 210: if (buildnf (archend, archbase, 0, 0, 0) < 0)/* make one */ 211: { 212: printf ("Archiver: Problems creating %s for archival\n", 213: archdest); 214: fflush (stdout); 215: goto docompress; 216: } 217: if (init (&archio, archdest) < 0) /* and open it */ 218: { 219: printf ("Archiver: Problems opening %s for archival\n", 220: archdest); 221: fflush (stdout); 222: goto docompress; 223: } 224: locknf (&archio, DSCRLOCK); /* watch conflicts */ 225: getdscr (&archio, &archio.descr); 226: archio.descr.d_stat |= ISARCH + OPEN; 227: putdscr (&archio, &archio.descr); 228: unlocknf (&archio, DSCRLOCK); 229: /* 230: * Copy the active notesfile's access list to 231: * the archive notesfile. 232: */ 233: { 234: #ifdef FASTFORK 235: char old[WDLEN]; 236: char new[WDLEN]; 237: sprintf (old, "%s/%s/%s", io.basedir, io.nf, ACCESS); 238: sprintf (new, "%s/%s/%s", archio.basedir, archio.nf, ACCESS); 239: dounix (0, 0, "/bin/cp", old, new, 0, 0); 240: #else ! FASTFORK 241: char cmdline[WDLEN + WDLEN + 10]; 242: sprintf (cmdline, "%s %s/%s/%s %s/%s/%s", 243: "/bin/cp", 244: io.basedir, io.nf, ACCESS, 245: archio.basedir, archio.nf, ACCESS); 246: dounix (cmdline, 0, 0); 247: #endif ! FASTFORK 248: } 249: } 250: 251: locktarget = strcmp (io.nf, archio.nf); /* lock if differ */ 252: 253: 254: if (!(archio.descr.d_stat & ISARCH)) /* into archive? */ 255: { 256: printf ("Archiver: %s: Target %s is not an archive\n", 257: nfname, archdest); 258: fflush (stdout); 259: closenf (&archio); /* close that */ 260: goto docompress; /* compress him anyway */ 261: } 262: } 263: 264: 265: #ifdef OLDGROUP 266: /* 267: * This code looks at the directory to see if the notesfile 268: * has been idle long enough to be deleted. 269: * 270: * This code hasn't been tested by me. It works in the 271: * Salkind/Spickelmier version. 272: * 273: * Should stuff a "wait-till-expire" in the master descriptor 274: * of each notesfile so "junk" ones can expire faster or 275: * something like that. Essentially we want the age at which 276: * the notesfile is deleted to be grabbed from the notesfile 277: * itself. 278: * 279: * My personal opinion is that they shouldn't disappear 280: * auto-magically 281: * NOTE: this probably no longer works with the 282: * changes I've made to archiving. (Dec '83) 283: * 284: * N.B. Need some locking in here 285: */ 286: 287: /* delete inactive groups - RLS 1/8/83 */ 288: 289: sprintf (line, "%s/%s", MSTDIR, nfname); 290: stat (line, &buf); 291: current = time (0); 292: if (current - buf.st_mtime > 60 * 60 * 24 * (OLDGROUP - daysold)) 293: { 294: finish (&io); 295: 296: sprintf (line, "/bin/rm -rf %s/%s", MSTDIR, nfname); 297: system (line); 298: 299: gettime (&zaptime); 300: sprdate (&zaptime, timeline); 301: 302: /* message in nfmaint */ 303: sprintf (line, "Archiver: removed %s\n", nfname); 304: nfcomment (NOSUCHWARN, line, line, 0, 0); 305: 306: sprintf (line, "%s/%s/%s", MSTDIR, UTILITY, NETLOG); 307: x ((log = fopen (line, "a")) == NULL, "archiver: no logfile"); 308: fprintf (log, "Archiver: deleted %s at %s\n", nfname, timeline); 309: printf ("Archiver: deleted %s at %s\n", nfname, timeline); 310: fclose (log); 311: fflush (stdout); 312: return (0); 313: } 314: #endif OLDGROUP 315: 316: 317: locknf (&io, DSCRLOCK); /* MUTEX */ 318: if (locktarget) /* and target */ 319: locknf (&archio, DSCRLOCK); 320: getdscr (&io, &io.descr); 321: wasopen = io.descr.d_stat & OPEN; /* hold this */ 322: io.descr.d_stat &= NOT OPEN; /* privacy */ 323: putdscr (&io, &io.descr); 324: 325: for (i = 1; i <= io.descr.d_nnote && deletable; i++) 326: { 327: getnrec (&io, i, ¬e); 328: if (note.n_stat & DELETED) 329: continue; /* gone already */ 330: if (dirmsgflag == DIROFF && (note.n_stat & DIRMES)) 331: continue; /* don't if dir on */ 332: if (dirmsgflag == DIRON && (note.n_stat & DIRMES) == 0) 333: continue; /* don't if dir off */ 334: if (inorder (&zaptime, ¬e.n_lmod)) 335: continue; /* too recent */ 336: presps = note.n_nresp; /* response count */ 337: if (!deleteonly) /* save it? */ 338: { 339: /* 340: * check to see if this one is already in the archive 341: */ 342: dup_place = chknote (&archio, ¬e.n_id, ¬e2);/* already there? */ 343: if (dup_place == 0) /* not there */ 344: { 345: /* 346: * This code copied almost verbatim from compression routines 347: */ 348: #ifdef notdef 349: x ((txtfile = fopen (txtfn, "w")) == NULL, "archiver:bad txt"); 350: pageout (&io, ¬e.n_addr, txtfile); 351: fclose (txtfile); 352: x ((txtfile = fopen (txtfn, "r")) == NULL, "archiver: txt read"); 353: pagein (&archio, txtfile, &where); 354: fclose (txtfile); 355: #else 356: pagemove (&io, ¬e.n_addr, &archio, &where, LOCKIT); 357: #endif 358: newnum = putnote (&archio, &where, note.ntitle, note.n_stat, ¬e, 359: ¬e.n_auth, NOPOLICY, NOLOCKIT, NOADDID, note.n_from, NOADDTIME); 360: getnrec (&archio, newnum, ¬e2); /* get copy */ 361: } 362: else 363: { 364: if ((note2.n_stat & ORPHND) && /* archived is foster */ 365: !(note.n_stat & ORPHND)) /* and active isn't */ 366: { 367: #ifdef notdef 368: x ((txtfile = fopen (txtfn, "w")) == NULL, "archiver:bad txt"); 369: pageout (&io, ¬e.n_addr, txtfile); 370: fclose (txtfile); 371: x ((txtfile = fopen (txtfn, "r")) == NULL, "archiver: txt read"); 372: pagein (&archio, txtfile, &where); 373: fclose (txtfile); 374: #else 375: pagemove (&io, ¬e.n_addr, &archio, &where, LOCKIT); 376: #endif 377: 378: note.n_nresp = note2.n_nresp; /* save resp chain */ 379: note.n_rindx = note2.n_rindx; 380: note.n_addr = where; /* get text pointer */ 381: putnrec (&archio, dup_place, ¬e);/* replace descriptor */ 382: note2 = note; /* save good copy */ 383: adopts++; /* count 'em */ 384: } 385: else 386: { 387: dnotes++; /* count duplicate */ 388: } 389: newnum = dup_place; /* for linking resps */ 390: } 391: 392: for (rnum = 1; rnum <= presps; rnum++) /* process responses */ 393: { 394: if (lrsp (&io, i, rnum, &resp, &roffset, &rblock) != 0) 395: break; /* bad response chain - drop rest */ 396: if (dup_place) /* better check... */ 397: { 398: dup_resp = chkresp (&archio, &resp.r_id[roffset], ¬e2, newnum); 399: if (dup_resp) /* already there */ 400: { 401: dresps++; /* count doubles */ 402: continue; /* skip this response */ 403: } 404: } 405: #ifdef notdef 406: x ((txtfile = fopen (txtfn, "w")) == NULL, "compress:bad txt"); 407: pageout (&io, &resp.r_addr[roffset], txtfile); 408: fclose (txtfile); 409: x ((txtfile = fopen (txtfn, "r")) == NULL, "compress: bad txt read"); 410: pagein (&archio, txtfile, &where); 411: fclose (txtfile); 412: #else 413: pagemove (&io, &resp.r_addr[roffset], &archio, &where, LOCKIT); 414: #endif 415: putresp (&archio, &where, resp.r_stat[roffset], newnum, &resp.r_when[roffset], 416: &resp.r_auth[roffset], ¬e, NOLOCKIT, &resp.r_id[roffset], 417: NOADDID, resp.r_from[roffset], NOADDTIME, &resp.r_rcvd[roffset]); 418: } 419: } 420: delnote (&io, i, NOLOCKIT); /* delete entry */ 421: ncount++; 422: rcount += presps; /* and responses */ 423: deletable--; /* one down */ 424: } 425: 426: unlocknf (&io, DSCRLOCK); /* Un MUTEX */ 427: if (locktarget) /* and the target */ 428: unlocknf (&archio, DSCRLOCK); 429: 430: if (!deleteonly) 431: { 432: finish (&archio); /* close target */ 433: unlink (txtfn); /* don't litter */ 434: } 435: 436: /* 437: * Time to compress the notesfile and eliminate those 438: * unsightly holes in the data structure. 439: */ 440: docompress: 441: 442: locknf (&io, DSCRLOCK); /* MUTEX */ 443: if (io.descr.d_nnote != 0) /* non-empty */ 444: { 445: if (io.descr.d_delnote != 0 || io.descr.d_delresp != 0) 446: { /* has holes */ 447: int nnote, 448: nresp; 449: compress (&io, NOLOCKIT, 0, &nnote, &nresp); 450: printf ("Archiver: %s contains (%d,%d) after compress\n", 451: nfname, nnote, nresp); 452: fflush (stdout); 453: } 454: else /* no holes so */ 455: { /* don't compress */ 456: printf ("Archiver: %s already compressed\n", nfname); 457: fflush (stdout); 458: } 459: } 460: else /* nothing there to */ 461: { /* compress */ 462: printf ("Archiver: %s is empty.\n", nfname); 463: fflush (stdout); 464: } 465: 466: 467: if (wasopen) /* if it was already */ 468: { 469: getdscr (&io, &io.descr); /* open season */ 470: io.descr.d_stat |= OPEN; 471: putdscr (&io, &io.descr); /* replace in file */ 472: } 473: 474: unlocknf (&io, DSCRLOCK); /* all done with this */ 475: finish (&io); /* and close the notesfile */ 476: 477: gettime (&zaptime); 478: sprdate (&zaptime, timeline); 479: if (ncount) /* log only if did somethine */ 480: { 481: sprintf (line, "%s/%s/%s", Mstdir, UTILITY, NETLOG); 482: x ((log = fopen (line, "a")) == NULL, "archiver: no logfile"); 483: if (!deleteonly) 484: fprintf (log, "%s: archived (%d,%d) [%d,%d dups, %d adopted] into %s at %s\n", 485: nfname, ncount, rcount, dnotes, dresps, adopts, archdest, timeline); 486: else 487: fprintf (log, "%s: Archiver deleted (%d,%d) at %s\n", 488: nfname, ncount, rcount, timeline); 489: fclose (log); 490: } 491: 492: if (!deleteonly) 493: { 494: if (ncount) 495: printf ("Archiver: %s: (%d,%d) [%d,%d dups, %d adoptions] into %s at %s\n", 496: nfname, ncount, rcount, dnotes, dresps, adopts, archdest, timeline); 497: else 498: printf ("Archiver: %s: no notes archived\n", 499: nfname); 500: } 501: else 502: printf ("Archiver: %s: deleted (%d,%d) at %s\n", 503: nfname, ncount, rcount, timeline); 504: fflush (stdout); 505: return (0); /* and return */ 506: 507: }