home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume19 / rkive / part04 / rkive.c < prev   
C/C++ Source or Header  |  1989-06-29  |  17KB  |  611 lines

  1. /*
  2. **                                                                
  3. **  Subsystem:   USENET Sources Archiver             
  4. **  File Name:   rkive.c               
  5. **                                                        
  6. **  usage: rkive [ -dgstuvV ] [ -f config_file ] [-n newsgroup ]
  7. **
  8. **
  9. ** This software is Copyright (c) 1989 by Kent Landfield.
  10. **
  11. ** Permission is hereby granted to copy, distribute or otherwise 
  12. ** use any part of this package as long as you do not try to make 
  13. ** money from it or pretend that you wrote it.  This copyright 
  14. ** notice must be maintained in any copy made.
  15. **
  16. ** Use of this software constitutes acceptance for use in an AS IS 
  17. ** condition. There are NO warranties with regard to this software.  
  18. ** In no event shall the author be liable for any damages whatsoever 
  19. ** arising out of or in connection with the use or performance of this 
  20. ** software.  Any use of this software is at the user's own risk.
  21. **
  22. **  If you make modifications to this software that you feel 
  23. **  increases it usefulness for the rest of the community, please 
  24. **  email the changes, enhancements, bug fixes as well as any and 
  25. **  all ideas to me. This software is going to be maintained and 
  26. **  enhanced as deemed necessary by the community.
  27. **
  28. **              Kent Landfield
  29. **              uunet!ssbell!kent
  30. **
  31. **  History:
  32. **    Creation: Tue Feb 21 08:52:35 CST 1989 due to necessity.
  33. **                                                               
  34. */
  35. char sccsid[] = "@(#)rkive.c    1.1 6/1/89";
  36.  
  37. #include <sys/types.h>
  38. #include <sys/stat.h>
  39. #include <dirent.h>
  40. #include <stdio.h>
  41. #include "article.h"
  42. #include "cfg.h"
  43.  
  44. /* 
  45. ** This is necessary since the builtin makedir call uses
  46. ** mknod which is a superuser only call for directories.
  47. */
  48. #if (!HAVE_MKDIR && !USE_SYSMKDIR)
  49. #define ROOT_ONLY
  50. #endif
  51.  
  52. #define UFMT "usage: %s [ -dgstuvV ] [ -f config_file ] [ -n newsgroup ]\n"
  53.  
  54. int overwrite;
  55. int status_only;
  56. struct stat sbuf;
  57. struct group_archive *newsgrp;
  58.  
  59. char tmp_mailfile[] = "/tmp/rkive.mail";
  60. char global_mailfile[] = "/tmp/gbl.mail";
  61.  
  62. char *save_article();
  63. char *compress_file();
  64. char *do_compress();
  65. char *basename();
  66. char *suffix();
  67. void archive();
  68.  
  69. char *strcpy();
  70. char *strcat();
  71. char *strchr();
  72. FILE *efopen();
  73. void exit();
  74.  
  75. extern int debug;
  76. extern int verbose;
  77. extern int test;
  78. extern int problem_article;
  79.  
  80. main(argc, argv)
  81. int argc;
  82. char **argv;
  83. {
  84.    int c;
  85.    extern char *optarg;
  86.    char *nwsg = NULL;
  87.  
  88.    progname = argv[0];
  89.    errfp = stderr;
  90.    logfp = stdout;
  91.  
  92.    status_only = debug = verbose = 0;
  93.    test = overwrite = fill_in_defaults = 0;
  94.  
  95.    /*
  96.    ** Setup the default config file to be used
  97.    ** unless the user specifies otherwise.
  98.    */
  99.    config_file = LOCATION;
  100.  
  101.    if (argc > 1) {
  102.       while ((c = getopt(argc, argv, "dgstuvVn:f:")) != EOF) {
  103.          switch (c) {
  104.              case 'f':
  105.                  config_file = optarg;  
  106.                  break;  
  107.              case 'd':
  108.                  debug++;
  109.                  verbose++;
  110.                  break;
  111.              case 'g':
  112.                  fill_in_defaults++;
  113.                  break;
  114.              case 'n':
  115.                  nwsg = optarg;
  116.                  break;
  117.              case 's':
  118.                  status_only++;
  119.                  break;
  120.              case 't':
  121.                  test++;
  122.                  verbose++;
  123.                  break;
  124.              case 'u':
  125.                  overwrite++;
  126.                  break;
  127.              case 'v':
  128.                  verbose++;
  129.                  break;
  130.              case 'V':
  131.                  version();
  132.              default:
  133.                  (void) fprintf(errfp, UFMT, progname);
  134.                  return(1);
  135.          }
  136.       }
  137.    }
  138.  
  139.    setup_defaults();
  140.  
  141.    init_article();
  142.  
  143.    for (c = 0; c <= num; c++)  {
  144.        newsgrp = &group[c];
  145.        /*
  146.        ** Was a newsgroup specified on the command line ?
  147.        */
  148.        if (nwsg != NULL) {
  149.           if (strcmp(nwsg, newsgrp->ng_name) != 0)
  150.               continue;
  151.        }
  152.        archive();
  153.    }
  154.  
  155.    if (!status_only) {
  156.        /*
  157.        ** Mail notification of the archived members to the 
  158.        ** list of users specified in the configuration file
  159.        ** and remove the file containing the archived info.
  160.        */
  161.        mail_file(mail, global_mailfile, "Complete Archive Results ");
  162.        (void) unlink(global_mailfile);
  163.    }
  164.    return(0);
  165. }
  166.  
  167. void archive()
  168. {
  169.     struct dirent *dp;
  170.     int cct;
  171.     DIR *dfd;
  172.     char *rp, *rec;
  173.     char *dir = ".";
  174.     char *new_member;
  175.     char *archived_file;
  176.     char *get_archived_rec();
  177.     char newsgroup_directory[MAXNAMLEN];
  178.     
  179. #ifdef ROOT_ONLY
  180.     /*
  181.     ** check to assure that the user is root if 
  182.     ** actual archiving is to take place. This is necessary
  183.     ** if there is no mkdir system call.
  184.     */
  185.  
  186.     if (!status_only && (getuid() != 0)) {
  187.         (void) fprintf(errfp, "%s: Sorry, Must be root to rkive.\n",
  188.                         progname);
  189.         exit(1);
  190.     }
  191. #endif
  192.  
  193.     /* Remove any existing temporary mail file */
  194.  
  195.     (void) unlink(tmp_mailfile);
  196.     cct = 0;  /* counter for newsgroup message in global mail */
  197.  
  198.     /*
  199.     ** Assure that there something specified in the 
  200.     ** archive location variable...
  201.     */
  202.     if (!*newsgrp->location) {
  203.         (void) fprintf(errfp, "SKIPPING %s: No archive location specified..\n",
  204.                         newsgrp->ng_name);
  205.         return;
  206.     }
  207.  
  208.     /*
  209.     ** print out the appropriate 
  210.     ** header for the newsgroup.
  211.     */
  212.  
  213.     if (debug || (verbose && status_only)) {
  214.         (void) fprintf(logfp,"\n\n");
  215.         display_group_info(newsgrp);
  216.         (void) fprintf(logfp,"\n");
  217.     }
  218.     else if (status_only)
  219.         (void) fprintf(logfp, "%s\n",newsgrp->ng_name);
  220.  
  221.     /* convert newsgroup name into a disk path */
  222.  
  223.     rp = newsgrp->ng_name;
  224.  
  225.     /*
  226.     ** convert all '.' to '/' to generate a path to the
  227.     ** newsgroup directory relative from the specified SPOOLDIR.
  228.     */
  229.  
  230.     while (*rp) {             /* convert all */
  231.         if (*rp == '.')       /* '.'s to '/' */
  232.             *rp = '/';        /* to create   */
  233.         rp++;                 /* the disk    */
  234.     }                         /* location    */
  235.  
  236.     (void) sprintf(newsgroup_directory,"%s/%s", spooldir,newsgrp->ng_name);
  237.  
  238.     if (chdir(newsgroup_directory) != 0) {
  239.        (void) fprintf(errfp,"Can't change directory to %s, %s not archived\n",
  240.                      newsgroup_directory, newsgrp->ng_name);
  241.        return;
  242.     }
  243.  
  244.     /*
  245.     ** Create a path to the .archived file for the newsgroup's archive.
  246.     ** This file is used to determine if an article has already been
  247.     ** archived.
  248.     */
  249.     (void) sprintf(newsgrp->arc_done,"%s/.archived",newsgrp->location);
  250.  
  251.     /*
  252.     ** Create a path to the .patchlog file for the newsgroup's archive.
  253.     ** This file is used to record patches to posted software so that
  254.     ** it can easily be determined what the full set of software is.
  255.     */
  256.     (void) sprintf(newsgrp->patchlog,"%s/.patchlog",newsgrp->location);
  257.  
  258.     /*
  259.     ** locate a file that needs to be archived. This is done by
  260.     ** a linear search of the directory with a linear search of
  261.     ** of the contents of the .archived file. If the file is not
  262.     ** specified in the .archived file, it has not been archived
  263.     ** before and we can proceed with the archiving.
  264.     */
  265.     if ((dfd  = opendir(dir)) == NULL) {
  266.         (void) fprintf(errfp, "can't open %s\n", newsgroup_directory);
  267.         return;
  268.     }
  269.     while ((dp = readdir(dfd)) != NULL) {
  270.        if (strcmp(dp->d_name,".") == 0
  271.            || strcmp(dp->d_name,"..") == 0)
  272.            continue;
  273.  
  274.        if (stat(dp->d_name, &sbuf) != 0)  {
  275.            (void) fprintf(errfp, "can't stat %s/%s\n",
  276.                           newsgroup_directory, dp->d_name);
  277.            continue;
  278.        }
  279.  
  280.        /* 
  281.        ** If its not a regular file, we cannot archive it. 
  282.        */
  283.  
  284.        else if ((sbuf.st_mode & S_IFMT) != S_IFREG) 
  285.            continue; 
  286.         
  287.        /* 
  288.        ** If the user has specified that a quick status 
  289.        ** listing should be produced then hop to it....
  290.        */
  291.  
  292.        if (status_only) {
  293.             if ((rec = get_archived_rec(dp->d_name)) == NULL) 
  294.                 (void) fprintf(logfp,"\t<%s> Awaiting Archiving\n",dp->d_name);
  295.             else if ((rp = strchr(rec,' ')) == NULL)
  296.                 (void) fprintf(logfp,"\t<%s> Archived\n",dp->d_name);
  297.             else {
  298.                 rp++;
  299.                 *(rp-1) = '\0';
  300.                 (void) fprintf(logfp,"\t<%s> Archived as <%s>\n",rec,rp);
  301.             }
  302.             continue;
  303.        }
  304.  
  305.        /* 
  306.        ** Archiving from here on out.
  307.        */
  308.  
  309.        if (!needs_to_be_archived(dp->d_name)) 
  310.            continue;
  311.            
  312.        if ((new_member = save_article(dp->d_name,newsgrp)) != NULL) {
  313.            archived_file = compress_file(new_member,newsgrp);
  314.            set_ownership(archived_file,newsgrp);
  315.            
  316.            /*
  317.            ** If a problem has been encountered,
  318.            ** the function do_problem handles
  319.            ** the logging, and notifying.
  320.            */
  321.  
  322.            if (!problem_article) {
  323.                log_activities(archived_file,newsgrp);
  324.                build_index(new_member,newsgrp);
  325.                notify_users(archived_file,newsgrp,cct++);
  326.            }
  327.        }
  328.        else 
  329.            (void) fprintf(logfp,"Unable to archive %s/%s!!!\n",
  330.                           newsgrp->ng_name, dp->d_name);
  331.     }
  332.     (void) closedir(dfd);
  333.  
  334.     if (!status_only) {
  335.         /* Remove the expired entries from the .archived file */
  336.         /* stored in the newsgroup's BASEDIR directory.       */
  337.  
  338.         remove_expired();
  339.  
  340.         /* Mail notification of the archived members to the   */
  341.         /* list of users specified in the configuration file  */
  342.         /* and remove the file containing the archived info.  */
  343.  
  344.         mail_file(newsgrp->mail_list, tmp_mailfile, newsgrp->ng_name);
  345.         (void) unlink(tmp_mailfile);
  346.     }
  347.     return;
  348. }
  349.  
  350. /* 
  351. ** Notify Users of Archiving.
  352. **      If users have been specified to be informed, check to see
  353. **      if they have requested a specific logging format. If so
  354. **      use the specified format to notify the user. If not, use
  355. **      "file archived at path" message.
  356. */
  357. notify_users(filename,ng,num_msgs)
  358. char *filename;
  359. struct group_archive *ng;
  360. int num_msgs;
  361. {
  362.     /*
  363.     ** Are there users specified in the 
  364.     ** newsgroup section ? 
  365.     */
  366.     if ( *(ng->mail_list) ) {
  367.         if ( *(ng->logformat) )
  368.            logit(tmp_mailfile, ng->logformat, filename);
  369.         else
  370.            logit(tmp_mailfile, DEFAULT_LOG_FORMAT, filename);
  371.     }
  372.  
  373.     /* 
  374.     ** Are there users specified in the 
  375.     ** global section ? 
  376.     */
  377.     if ( *mail ) {
  378.         if (num_msgs == 0) /* print the newsgroup name out */
  379.             logit(global_mailfile, "\n\t\t:%G:\n",filename);
  380.         if (*log_format)
  381.             logit(global_mailfile, log_format,filename);
  382.         else 
  383.             logit(global_mailfile, DEFAULT_LOG_FORMAT, filename);
  384.     }
  385. }
  386.  
  387. /*
  388. ** Log_activities
  389. **
  390. ** There are two possible logfiles that need to be written. 
  391. ** The group specific logfile (ng->logfile) and the global 
  392. ** log. If it has been configured to use a specific format
  393. ** for the logging, do so. Else, just record the fact the
  394. ** file was sucessfully archived and the date.          
  395. */
  396. log_activities(filename,ng)
  397. char *filename;
  398. struct group_archive *ng;
  399. {
  400.    long clock;
  401.    long time();
  402.    char *ctime();
  403.    
  404.    char logbuf[BUFSIZ];
  405.    char dms_date[30];
  406.    
  407.    if ( !*(ng->logformat) || !*log_format) {
  408.        clock = time((long *)0);
  409.        (void) strcpy(dms_date, ctime(&clock));
  410.        *(dms_date+(strlen(dms_date)-1)) = '\0';
  411.        (void) sprintf(logbuf,"%s archived %s",filename, dms_date);
  412.    }
  413.  
  414.    if ( *(ng->logformat) )
  415.        logit(ng->logfile, ng->logformat, filename);
  416.    else
  417.        logit(ng->logfile, logbuf, filename);
  418.  
  419.    if ( *log_format )
  420.        logit(log, log_format, filename);
  421.    else
  422.        logit(log, logbuf, filename);
  423. }
  424.  
  425. /*
  426. ** logit
  427. **
  428. ** This function is used to append a logfile record 
  429. ** if there is a logfile name specified.
  430. **
  431. */
  432.  
  433. logit(filename, format_of_log, arch_file)
  434. char *filename;
  435. char *format_of_log;
  436. char *arch_file;
  437. {
  438.     FILE *fp, *fopen();
  439.  
  440.     if ( *(filename) ) {   /* Is a logfile specified ? */
  441.         if ((fp = fopen(filename,"a")) != NULL) {
  442.             format_output(fp, format_of_log, arch_file, ARCHIVE);
  443.             (void) fclose(fp);
  444.         }
  445.     }
  446. }    
  447.  
  448.  
  449. set_ownership(filename,ng)
  450. char *filename;
  451. struct group_archive *ng;
  452. {
  453.     if (verbose) {  /* Print out the actions about to be preformed */
  454.         (void) fprintf(logfp,"chown\t<%d> <%s>\n", ng->owner, filename);
  455.         (void) fprintf(logfp,"chgrp\t<%d> <%s>\n", ng->group, filename);
  456.     }
  457.  
  458.     if (!test) {    /* chown the owner/group to the desired values */
  459.         if (chown(filename,ng->owner, ng->group) != 0)
  460.             error("Can't change ownership of", filename);
  461.     }
  462.  
  463.     if (verbose) {  /* Print out the actions about to be preformed */
  464.         (void) fprintf(logfp,"chmod\t<%o> <%s>\n", ng->modes, filename);
  465.     }
  466.  
  467.     if (!test) {    /* change the file modes to the specified modes */
  468.         if (chmod(filename,ng->modes) != 0)
  469.             error("Can't change modes of", filename);
  470.     }
  471. }
  472.  
  473. mail_file(user_list, file_to_mail, nwsgrp)
  474. char *user_list;
  475. char *file_to_mail;
  476. char *nwsgrp;
  477. {
  478.     char  *list, *name;
  479.     char  cmdstr[80];
  480.  
  481.     /* Is there a list of users to mail to ? */
  482.     if ( !*user_list || (strlen(user_list) == 0))
  483.         return;
  484.  
  485.     /* Was there a notification file created ? */
  486.     if (stat(file_to_mail, &sbuf) != 0) 
  487.         return;
  488.  
  489.     name = user_list;
  490.     do {
  491.        if ((list = strchr(name,',')) != NULL) {
  492.             list++;
  493.             *(list-1) = '\0';
  494.         }
  495.  
  496. #ifdef SUBJECT_LINE
  497.         (void) sprintf(cmdstr, "%s -s '%s' %s < %s", 
  498.                    MAIL, nwsgrp, name, file_to_mail);
  499. #else
  500.         (void) sprintf(cmdstr, "%s %s < %s", MAIL, name, file_to_mail);
  501. #endif
  502.         if (verbose)
  503.             (void) fprintf(logfp,"Mailing %s Archived results to %s\n",
  504.                            nwsgrp, name);
  505.         if (!test) 
  506.             (void) system(cmdstr);
  507.  
  508.         name = list;
  509.  
  510.     } while (name != NULL);
  511.     return;
  512. }
  513.  
  514. build_index(filename,ng)
  515. char *filename;
  516. struct group_archive *ng;
  517. {
  518.     if (*(ng->index)) {        /* Is there a newsgroup index file ?  */
  519.         if (*(ng->indformat))  /* Yes, Is there a index file format? */
  520.             logit(ng->index, ng->indformat, filename);
  521.         else if (*index_format)    /* No, is there a global format ? */
  522.             logit(ng->index, index_format, filename);
  523.         else                   /* No, use the default index format   */
  524.             logit(ng->index, DEFAULT_INDEX_FORMAT, filename);
  525.     }
  526.  
  527.     if (*index) {            /* Is there a global index file ?       */
  528.         if (*index_format)   /* Yes, Is there a global file format ? */
  529.             logit(index, index_format, filename);
  530.         else                 /* No, so use the default index format  */
  531.             logit(ng->index, DEFAULT_INDEX_FORMAT , filename);
  532.     }
  533. }
  534.  
  535.  
  536. char *compress_file(filename,ng)
  537. char *filename;
  538. struct group_archive *ng;
  539. {
  540.     static char compressed[MAXNAMLEN];
  541.  
  542.     (void) strcpy(compressed, filename);  /* store the filename */
  543.  
  544.     /* Check to see if a group specific compress was specified.      */
  545.     /* If so, then execute the command with the filename passed in.  */
  546.     /* Else check to see if a global compress was specified. If so,  */
  547.     /* then execute the command with the filename passed in.         */
  548.     /* If both are NULL, no compression is done.                     */
  549.  
  550.     if (*(ng->compress)) 
  551.         (void) strcat(compressed, do_compress(ng->compress, filename));
  552.     else if (*compress) 
  553.         (void) strcat(compressed, do_compress(compress, filename));
  554.  
  555.     return(compressed);
  556. }
  557.  
  558. char *do_compress(packit,filename)
  559. char *packit;
  560. char *filename;
  561. {
  562.     char *comp_cmd;
  563.     char cmd[BUFSIZ];
  564.  
  565.     (void) sprintf(cmd,"%s %s", packit, filename);
  566.  
  567.     /* 
  568.     ** get the basename of the command to use.
  569.     */
  570.     comp_cmd = basename(packit);
  571.  
  572.     if (verbose)
  573.        (void) fprintf(logfp,"%s %s\n", comp_cmd, filename);
  574.  
  575.     if (!test) 
  576.        (void) system(cmd);
  577.  
  578.     return(suffix(comp_cmd));
  579. }
  580.  
  581.  
  582. /*
  583. ** Record_problem()
  584. **    This function is used to log problems encountered
  585. **    to the designated parties.
  586. */
  587. record_problem(msg_fmt,filename,ng)
  588. char *msg_fmt;
  589. char *filename;
  590. struct group_archive *ng;
  591. {
  592.     /* 
  593.     ** This function is used in the event that a problem
  594.     ** has occurred during archiving. It mails a message
  595.     ** to the newsgroup speecified list and it mails a 
  596.     ** message to the globally specified users.
  597.     ** 
  598.     ** It then logs the fact into both the newsgroup 
  599.     ** and the global logfiles if they have been specified.
  600.     */
  601.  
  602.     if ( *(ng->mail_list) ) 
  603.         logit(tmp_mailfile, msg_fmt, filename);
  604.     
  605.     if ( *mail ) 
  606.         logit(global_mailfile, msg_fmt,filename);
  607.     
  608.     logit(ng->logfile, msg_fmt, filename);
  609.     logit(log, msg_fmt, filename);
  610. }
  611.