1: #include "parms.h" 2: #include "structs.h" 3: 4: #ifdef RCSIDENT 5: static char rcsid[] = "$Header: archiver.c,v 1.7.0.6 86/01/25 22:54:08 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: dirmsgflag == DIRANYON ? "ANYON" : 170: "NOCARE"); 171: fflush (stdout); 172: } 173: 174: if (io.descr.d_archkeep != KEEPDFLT) /* keep/delete */ 175: { 176: if (io.descr.d_archkeep == KEEPYES) 177: deleteonly = 0; 178: else 179: deleteonly = 1; 180: printf ("Archiver: %s specifies %s expired notes\n", 181: nfname, 182: deleteonly ? "deleting" : "archiving"); 183: fflush (stdout); 184: } 185: 186: deletable = ((int) io.descr.d_nnote) - ((int) io.descr.d_delnote) - worksetsize; 187: if (deletable <= 0) /* candidates? */ 188: { 189: if (io.descr.d_nnote - io.descr.d_delnote > 0) /* only if non-empty */ 190: { 191: printf ("Archiver: %s: %d notes <= working set size of %d\n", 192: nfname, 193: io.descr.d_nnote - io.descr.d_delnote, 194: worksetsize); 195: fflush (stdout); 196: } 197: goto docompress; 198: } 199: 200: gettime (&zaptime); /* threshold */ 201: zaptime.w_gmttime -= 60L * 60L * 24L * ((long) daysold);/* internal */ 202: maketime (&zaptime, zaptime.w_gmttime); /* re-format */ 203: 204: 205: if (!deleteonly) 206: { 207: if (init (&archio, archdest) < 0) /* not already */ 208: { 209: printf ("Archiver creating archive notesfile %s\n", archdest); 210: fflush (stdout); 211: if (buildnf (archend, archbase, 0, 0, 0) < 0)/* make one */ 212: { 213: printf ("Archiver: Problems creating %s for archival\n", 214: archdest); 215: fflush (stdout); 216: goto docompress; 217: } 218: if (init (&archio, archdest) < 0) /* and open it */ 219: { 220: printf ("Archiver: Problems opening %s for archival\n", 221: archdest); 222: fflush (stdout); 223: goto docompress; 224: } 225: locknf (&archio, DSCRLOCK); /* watch conflicts */ 226: getdscr (&archio, &archio.descr); 227: archio.descr.d_stat |= ISARCH + OPEN; 228: putdscr (&archio, &archio.descr); 229: unlocknf (&archio, DSCRLOCK); 230: /* 231: * Copy the active notesfile's access list to 232: * the archive notesfile. 233: */ 234: { 235: #ifdef FASTFORK 236: char old[WDLEN]; 237: char new[WDLEN]; 238: sprintf (old, "%s/%s/%s", io.basedir, io.nf, ACCESS); 239: sprintf (new, "%s/%s/%s", archio.basedir, archio.nf, ACCESS); 240: dounix (0, 0, "/bin/cp", old, new, 0, 0); 241: #else ! FASTFORK 242: char cmdline[WDLEN + WDLEN + 32]; 243: sprintf (cmdline, "%s %s/%s/%s %s/%s/%s", 244: "/bin/cp", 245: io.basedir, io.nf, ACCESS, 246: archio.basedir, archio.nf, ACCESS); 247: dounix (cmdline, 0, 0); 248: #endif ! FASTFORK 249: } 250: } 251: 252: locktarget = strcmp (io.nf, archio.nf); /* lock if differ */ 253: 254: 255: if (!(archio.descr.d_stat & ISARCH)) /* into archive? */ 256: { 257: printf ("Archiver: %s: Target %s is not an archive\n", 258: nfname, archdest); 259: fflush (stdout); 260: closenf (&archio); /* close that */ 261: goto docompress; /* compress him anyway */ 262: } 263: } 264: 265: 266: #ifdef OLDGROUP 267: /* 268: * This code looks at the directory to see if the notesfile 269: * has been idle long enough to be deleted. 270: * 271: * This code hasn't been tested by me. It works in the 272: * Salkind/Spickelmier version. 273: * 274: * Should stuff a "wait-till-expire" in the master descriptor 275: * of each notesfile so "junk" ones can expire faster or 276: * something like that. Essentially we want the age at which 277: * the notesfile is deleted to be grabbed from the notesfile 278: * itself. 279: * 280: * My personal opinion is that they shouldn't disappear 281: * auto-magically 282: * NOTE: this probably no longer works with the 283: * changes I've made to archiving. (Dec '83) 284: * 285: * N.B. Need some locking in here 286: */ 287: 288: /* delete inactive groups - RLS 1/8/83 */ 289: 290: sprintf (line, "%s/%s", MSTDIR, nfname); 291: stat (line, &buf); 292: current = time (0); 293: if (current - buf.st_mtime > 60 * 60 * 24 * (OLDGROUP - daysold)) 294: { 295: finish (&io); 296: 297: sprintf (line, "/bin/rm -rf %s/%s", MSTDIR, nfname); 298: system (line); 299: 300: gettime (&zaptime); 301: sprdate (&zaptime, timeline); 302: 303: /* message in nfmaint */ 304: sprintf (line, "Archiver: removed %s\n", nfname); 305: nfcomment (NOSUCHWARN, line, line, 0, 0); 306: 307: sprintf (line, "%s/%s/%s", MSTDIR, UTILITY, NETLOG); 308: x ((log = fopen (line, "a")) == NULL, "archiver: no logfile"); 309: fprintf (log, "Archiver: deleted %s at %s\n", nfname, timeline); 310: printf ("Archiver: deleted %s at %s\n", nfname, timeline); 311: fclose (log); 312: fflush (stdout); 313: return (0); 314: } 315: #endif OLDGROUP 316: 317: 318: locknf (&io, DSCRLOCK); /* MUTEX */ 319: if (locktarget) /* and target */ 320: locknf (&archio, DSCRLOCK); 321: getdscr (&io, &io.descr); 322: wasopen = io.descr.d_stat & OPEN; /* hold this */ 323: io.descr.d_stat &= NOT OPEN; /* privacy */ 324: putdscr (&io, &io.descr); 325: 326: for (i = 1; i <= io.descr.d_nnote && deletable; i++) 327: { 328: getnrec (&io, i, ¬e); 329: if (note.n_stat & DELETED) /* already gone */ 330: continue; 331: if (inorder (&zaptime, ¬e.n_lmod)) /* check times */ 332: continue; /* too recent */ 333: if (dirmsgflag == DIROFF && (note.n_stat & DIRMES)) 334: continue; /* only dir off */ 335: if (dirmsgflag == DIRON && (note.n_stat & DIRMES) == 0) 336: continue; /* only dir on */ 337: if (dirmsgflag == DIRANYON) /* any w/dir on */ 338: { 339: /* 340: * if anything in the note string has dir message on, 341: * expire the sucker. 342: */ 343: if (note.n_stat & DIRMES) /* note has dir */ 344: goto killit; /* so expire it */ 345: for (rnum = 1; rnum <= note.n_nresp; rnum++) 346: { /* for each response */ 347: if (lrsp (&io, i, rnum, &resp, &roffset, &rblock) != 0) 348: break; /* busted resp chain */ 349: if (resp.r_stat[roffset] & DIRMES) /* is dir message */ 350: goto killit; /* so expire */ 351: } 352: continue; /* do not expire */ 353: } 354: killit: /* goto target */ 355: presps = note.n_nresp; /* response count */ 356: if (!deleteonly) /* save it? */ 357: { 358: /* 359: * check to see if this one is already in the archive 360: */ 361: dup_place = chknote (&archio, ¬e.n_id, ¬e2);/* already there? */ 362: if (dup_place == 0) /* not there */ 363: { 364: /* 365: * This code copied almost verbatim from compression routines 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: newnum = putnote (&archio, &where, note.ntitle, note.n_stat, ¬e, 378: ¬e.n_auth, NOPOLICY, NOLOCKIT, NOADDID, note.n_from, NOADDTIME); 379: getnrec (&archio, newnum, ¬e2); /* get copy */ 380: } 381: else 382: { 383: if ((note2.n_stat & ORPHND) && /* archived is foster */ 384: !(note.n_stat & ORPHND)) /* and active isn't */ 385: { 386: #ifdef notdef 387: x ((txtfile = fopen (txtfn, "w")) == NULL, "archiver:bad txt"); 388: pageout (&io, ¬e.n_addr, txtfile); 389: fclose (txtfile); 390: x ((txtfile = fopen (txtfn, "r")) == NULL, "archiver: txt read"); 391: pagein (&archio, txtfile, &where); 392: fclose (txtfile); 393: #else 394: pagemove (&io, ¬e.n_addr, &archio, &where, LOCKIT); 395: #endif 396: 397: note.n_nresp = note2.n_nresp; /* save resp chain */ 398: note.n_rindx = note2.n_rindx; 399: note.n_addr = where; /* get text pointer */ 400: putnrec (&archio, dup_place, ¬e);/* replace descriptor */ 401: note2 = note; /* save good copy */ 402: adopts++; /* count 'em */ 403: } 404: else 405: { 406: dnotes++; /* count duplicate */ 407: } 408: newnum = dup_place; /* for linking resps */ 409: } 410: 411: for (rnum = 1; rnum <= presps; rnum++) /* process responses */ 412: { 413: if (lrsp (&io, i, rnum, &resp, &roffset, &rblock) != 0) 414: break; /* bad response chain - drop rest */ 415: if (dup_place) /* better check... */ 416: { 417: dup_resp = chkresp (&archio, &resp.r_id[roffset], ¬e2, newnum); 418: if (dup_resp) /* already there */ 419: { 420: dresps++; /* count doubles */ 421: continue; /* skip this response */ 422: } 423: } 424: #ifdef notdef 425: x ((txtfile = fopen (txtfn, "w")) == NULL, "compress:bad txt"); 426: pageout (&io, &resp.r_addr[roffset], txtfile); 427: fclose (txtfile); 428: x ((txtfile = fopen (txtfn, "r")) == NULL, "compress: bad txt read"); 429: pagein (&archio, txtfile, &where); 430: fclose (txtfile); 431: #else 432: pagemove (&io, &resp.r_addr[roffset], &archio, &where, LOCKIT); 433: #endif 434: putresp (&archio, &where, resp.r_stat[roffset], newnum, &resp.r_when[roffset], 435: &resp.r_auth[roffset], ¬e, NOLOCKIT, &resp.r_id[roffset], 436: NOADDID, resp.r_from[roffset], NOADDTIME, &resp.r_rcvd[roffset]); 437: } 438: } 439: delnote (&io, i, NOLOCKIT); /* delete entry */ 440: ncount++; 441: rcount += presps; /* and responses */ 442: deletable--; /* one down */ 443: } 444: 445: unlocknf (&io, DSCRLOCK); /* Un MUTEX */ 446: if (locktarget) /* and the target */ 447: unlocknf (&archio, DSCRLOCK); 448: 449: if (!deleteonly) 450: { 451: finish (&archio); /* close target */ 452: unlink (txtfn); /* don't litter */ 453: } 454: 455: /* 456: * Time to compress the notesfile and eliminate those 457: * unsightly holes in the data structure. 458: */ 459: docompress: 460: 461: locknf (&io, DSCRLOCK); /* MUTEX */ 462: if (io.descr.d_nnote != 0) /* non-empty */ 463: { 464: if (io.descr.d_delnote != 0 || io.descr.d_delresp != 0) 465: { /* has holes */ 466: int nnote, 467: nresp; 468: compress (&io, NOLOCKIT, 0, &nnote, &nresp); 469: printf ("Archiver: %s contains (%d,%d) after compress\n", 470: nfname, nnote, nresp); 471: fflush (stdout); 472: } 473: else /* no holes so */ 474: { /* don't compress */ 475: printf ("Archiver: %s already compressed\n", nfname); 476: fflush (stdout); 477: } 478: } 479: else /* nothing there to */ 480: { /* compress */ 481: printf ("Archiver: %s is empty.\n", nfname); 482: fflush (stdout); 483: } 484: 485: 486: if (wasopen) /* if it was already */ 487: { 488: getdscr (&io, &io.descr); /* open season */ 489: io.descr.d_stat |= OPEN; 490: putdscr (&io, &io.descr); /* replace in file */ 491: } 492: 493: unlocknf (&io, DSCRLOCK); /* all done with this */ 494: finish (&io); /* and close the notesfile */ 495: 496: gettime (&zaptime); 497: sprdate (&zaptime, timeline); 498: if (ncount) /* log only if did somethine */ 499: { 500: sprintf (line, "%s/%s/%s", Mstdir, UTILITY, NETLOG); 501: x ((log = fopen (line, "a")) == NULL, "archiver: no logfile"); 502: if (!deleteonly) 503: fprintf (log, "%s: archived (%d,%d) [%d,%d dups, %d adopted] into %s at %s\n", 504: nfname, ncount, rcount, dnotes, dresps, adopts, archdest, timeline); 505: else 506: fprintf (log, "%s: Archiver deleted (%d,%d) at %s\n", 507: nfname, ncount, rcount, timeline); 508: fclose (log); 509: } 510: 511: if (!deleteonly) 512: { 513: if (ncount) 514: printf ("Archiver: %s: (%d,%d) [%d,%d dups, %d adoptions] into %s at %s\n", 515: nfname, ncount, rcount, dnotes, dresps, adopts, archdest, timeline); 516: else 517: printf ("Archiver: %s: no notes archived\n", 518: nfname); 519: } 520: else 521: printf ("Archiver: %s: deleted (%d,%d) at %s\n", 522: nfname, ncount, rcount, timeline); 523: fflush (stdout); 524: return (0); /* and return */ 525: 526: }