"Fossies" - the Fresh Open Source Software Archive

Member "mod_qos-11.63/tools/src/qsrotate.c" (25 May 2019, 14791 Bytes) of package /linux/www/apache_httpd_modules/mod_qos-11.63.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "qsrotate.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 11.61_vs_11.62.

    1 /**
    2  * Utilities for the quality of service module mod_qos.
    3  *
    4  * qsrotate.c: Log rotation tool.
    5  *
    6  * See http://mod-qos.sourceforge.net/ for further
    7  * details.
    8  *
    9  * Copyright (C) 2019 Pascal Buchbinder
   10  *
   11  * Licensed to the Apache Software Foundation (ASF) under one or more
   12  * contributor license agreements.  See the NOTICE file distributed with
   13  * this work for additional information regarding copyright ownership.
   14  * The ASF licenses this file to You under the Apache License, Version 2.0
   15  * (the "License"); you may not use this file except in compliance with
   16  * the License.  You may obtain a copy of the License at
   17  *
   18  *     http://www.apache.org/licenses/LICENSE-2.0
   19  *
   20  * Unless required by applicable law or agreed to in writing, software
   21  * distributed under the License is distributed on an "AS IS" BASIS,
   22  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   23  * See the License for the specific language governing permissions and
   24  * limitations under the License.
   25  *
   26  */
   27 
   28 static const char revision[] = "$Id: qsrotate.c 2542 2019-02-22 06:37:10Z pbuchbinder $";
   29 
   30 #include <stdio.h>
   31 #include <string.h>
   32 
   33 #include <errno.h>
   34 #include <fcntl.h>
   35 #include <dirent.h>
   36 
   37 #include <stdlib.h>
   38 #include <unistd.h>
   39 
   40 #include <pthread.h>
   41 
   42 #include <time.h>
   43 #include <zlib.h>     
   44 
   45 #include <signal.h>
   46 #include <sys/types.h>
   47 #include <sys/wait.h>
   48 #include <sys/stat.h>
   49 
   50 #include "qs_util.h"
   51 
   52 #define HUGE_STR       1024
   53 
   54 //yyyy-mm-dd<sp>hh-mm-ss<sp>
   55 #define TME_STR_LEN    20
   56 
   57 /* global variables used by main and support thread */
   58 static int m_force_rotation = 0;
   59 static time_t m_tLogEnd = 0;
   60 static time_t m_tRotation = 86400; /* default are 24h */
   61 static int m_nLogFD = -1;
   62 static int m_generations = -1;
   63 static mode_t m_mode = 0660;
   64 static char *m_file_name = NULL;
   65 static long m_messages = 0;
   66 static char *m_cmd = NULL;
   67 static int m_compress = 0;
   68 static int m_stdout = 0;
   69 static int m_timestamp = 0;
   70 static char time_string[TME_STR_LEN];
   71 static long m_counter = 0;
   72 static long m_limit = 2147483648 - (128 * 1024);
   73 static int m_offset = 0;
   74 static int m_offset_enabled = 0;
   75 
   76 static void usage(char *cmd, int man) {
   77   if(man) {
   78     //.TH [name of program] [section number] [center footer] [left footer] [center header]
   79     printf(".TH %s 1 \"%s\" \"mod_qos utilities %s\" \"%s man page\"\n", qs_CMD(cmd), man_date,
   80        man_version, cmd);
   81   }
   82   printf("\n");
   83   if(man) {
   84     printf(".SH NAME\n");
   85   }
   86   qs_man_print(man, "%s - a log rotation tool (similar to Apache's rotatelogs).\n", cmd);
   87   printf("\n");
   88   if(man) {
   89     printf(".SH SYNOPSIS\n");
   90   }
   91   qs_man_print(man, "%s%s -o <file> [-s <sec> [-t <hours>]] [-b <bytes>] [-f] [-z] [-g <num>] [-u <name>] [-m <mask>] [-p] [-d]\n", man ? "" : "Usage: ", cmd);
   92   printf("\n");
   93   if(man) {
   94     printf(".SH DESCRIPTION\n");
   95   } else {
   96     printf("Summary\n");
   97   }
   98   qs_man_print(man, "%s reads from stdin (piped log) and writes the data to the provided\n", cmd);
   99   qs_man_print(man, "file rotating the file after the specified time.\n");
  100   printf("\n");
  101   if(man) {
  102     printf(".SH OPTIONS\n");
  103   } else {
  104     printf("Options\n");
  105   }
  106   if(man) printf(".TP\n");
  107   qs_man_print(man, "  -o <file>\n");
  108   if(man) printf("\n");
  109   qs_man_print(man, "     Output log file to write the data to (use an absolute path).\n");
  110   if(man) printf("\n.TP\n");
  111   qs_man_print(man, "  -s <sec>\n");
  112   if(man) printf("\n");
  113   qs_man_print(man, "     Rotation interval in seconds, default are 86400 seconds.\n");
  114   if(man) printf("\n.TP\n");
  115   qs_man_print(man, "  -t <hours>\n");
  116   if(man) printf("\n");
  117   qs_man_print(man, "     Offset to UTC (enables also DST support), default is 0.\n");
  118   if(man) printf("\n.TP\n");
  119   qs_man_print(man, "  -b <bytes>\n");
  120   if(man) printf("\n");
  121   qs_man_print(man, "     File size limitation (default/max. are %ld bytes, min. are 1048576 bytes).\n", m_limit);
  122   if(man) printf("\n.TP\n");
  123   qs_man_print(man, "  -f\n");
  124   if(man) printf("\n");
  125   qs_man_print(man, "     Forced log rotation at the specified interval even no data is written.\n");
  126   if(man) printf("\n.TP\n");
  127   qs_man_print(man, "  -z\n");
  128   if(man) printf("\n");
  129   qs_man_print(man, "     Compress (gzip) the rotated file.\n");
  130   if(man) printf("\n.TP\n");
  131   qs_man_print(man, "  -g <num>\n");
  132   if(man) printf("\n");
  133   qs_man_print(man, "     Generations (number of files to keep).\n");
  134   if(man) printf("\n.TP\n");
  135   qs_man_print(man, "  -u <name>\n");
  136   if(man) printf("\n");
  137   qs_man_print(man, "     Become another user, e.g. www-data.\n");
  138   qs_man_print(man, "  -m <mask>\n");
  139   if(man) printf("\n");
  140   qs_man_print(man, "     File permission which is either 600, 640, 660 (default) or 664.\n");
  141   if(man) printf("\n.TP\n");
  142   qs_man_print(man, "  -p\n");
  143   if(man) printf("\n");
  144   qs_man_print(man, "     Writes data also to stdout (for piped logging).\n");
  145   qs_man_print(man, "  -d\n");
  146   if(man) printf("\n");
  147   qs_man_print(man, "     Line-by-line data reading prefixing every line with a timestamp.\n");
  148   printf("\n");
  149   if(man) {
  150     printf(".SH EXAMPLE\n");
  151   } else {
  152     printf("Example:\n");
  153   }
  154   qs_man_println(man, "  TransferLog \"|/usr/bin/%s -f -z -g 3 -o /var/log/apache/access.log -s 86400\"\n", cmd);
  155   printf("\n");
  156   qs_man_print(man, "The name of the rotated file will be /dest/filee.YYYYmmddHHMMSS\n");
  157   qs_man_print(man, "where YYYYmmddHHMMSS is the system time at which the data has been\n");
  158   qs_man_print(man, "rotated.\n");
  159   printf("\n");
  160   if(man) {
  161     printf(".SH NOTE\n");
  162   } else {
  163     printf("Notes:\n");
  164   }
  165   qs_man_println(man, " - Each %s instance must use an individual file.\n", cmd);
  166   qs_man_println(man, " - You may trigger a file rotation manually by sending the signal USR1\n");
  167   qs_man_print(man, "   to the process.\n");
  168   printf("\n");
  169   if(man) {
  170     printf(".SH SEE ALSO\n");
  171     printf("qsdt(1), qsexec(1), qsfilter2(1), qsgeo(1), qsgrep(1), qshead(1), qslog(1), qslogger(1), qsre(1), qsrespeed(1), qspng(1), qssign(1), qstail(1)\n");
  172     printf(".SH AUTHOR\n");
  173     printf("Pascal Buchbinder, http://mod-qos.sourceforge.net/\n");
  174   } else {
  175     printf("See http://mod-qos.sourceforge.net/ for further details.\n");
  176   }
  177   if(man) {
  178     exit(0);
  179   } else {
  180     exit(1);
  181   }
  182 }
  183 
  184 static time_t get_now() {
  185   time_t now = time(NULL);
  186   if(m_offset_enabled) {
  187     struct tm lcl = *localtime(&now);
  188     if(lcl.tm_isdst) {
  189       now += 3600;
  190     }
  191     now += m_offset;
  192   }
  193   return now;
  194 }
  195 
  196 static int openFile(const char *cmd, const char *file_name) {
  197   int m_nLogFD = open(file_name, O_WRONLY | O_CREAT | O_APPEND, m_mode);
  198   /* error while opening log file */
  199   if(m_nLogFD < 0) {
  200     fprintf(stderr,"[%s]: ERROR, failed to open file <%s>\n", cmd, file_name);
  201   }
  202   return m_nLogFD;
  203 }
  204 
  205 /**
  206  * Compress method called by a child process (forked)
  207  * used to compress the rotated file.
  208  *
  209  * @param cmd Command name (used when logging errors)
  210  * @param arch Path to the file to compress. File gets renamed to <arch>.gz
  211  */
  212 static void compressThread(const char *cmd, const char *arch) {
  213   gzFile *outfp;
  214   int infp;
  215   char dest[HUGE_STR+20];
  216   char buf[HUGE_STR];
  217   int len;
  218   snprintf(dest, sizeof(dest), "%s.gz", arch);
  219   /* low prio */
  220   if(nice(10) == -1) {
  221     fprintf(stderr, "[%s]: WARNING, failed to change nice value: %s\n", cmd, strerror(errno));
  222   }
  223   if((infp = open(arch, O_RDONLY)) == -1) {
  224     /* failed to open file, can't compress it */
  225     fprintf(stderr,"[%s]: ERROR, could not open file for compression <%s>\n", cmd, arch);
  226     return;
  227   }
  228   if((outfp = gzopen(dest,"wb")) == NULL) {
  229     fprintf(stderr,"[%s]: ERROR, could not open file for compression <%s>\n", cmd, dest);
  230     close(infp);
  231     return;
  232   }
  233   chmod(dest, m_mode);
  234   while((len = read(infp, buf, sizeof(buf))) > 0) {
  235     gzwrite(outfp, buf, len);
  236   }
  237   gzclose(outfp);
  238   close(infp);
  239   /* done, delete the old file */
  240   unlink(arch);
  241 }
  242 
  243 void sigchild(int signo) {
  244   pid_t pid;
  245   int stat;   
  246   while((pid=waitpid(-1,&stat,WNOHANG)) > 0) {
  247   }
  248 }
  249 
  250 void writeTimestamp() {
  251   time_t tm = time(NULL);
  252   struct tm *ptr = localtime(&tm);
  253   strftime(time_string, TME_STR_LEN, "%Y-%m-%d %H:%M:%S ", ptr);
  254   write(m_nLogFD, time_string, TME_STR_LEN);
  255 }
  256 
  257 /**
  258  * Rotates a file
  259  *
  260  * @param cmd Command name to be used in log messages
  261  * @param now
  262  * @param file_name Name of the file to rotate (rename)
  263  * @param messages Number of lines/buffers which had been read
  264  */
  265 static void rotate(const char *cmd, time_t now,
  266            const char *file_name, long *messages) {
  267   int rc;
  268   char arch[HUGE_STR+20];
  269   char tmb[20];
  270   struct tm *ptr = localtime(&now);
  271   strftime(tmb, sizeof(tmb), "%Y%m%d%H%M%S", ptr);
  272   snprintf(arch, sizeof(arch), "%s.%s", file_name, tmb);
  273 
  274   /* set next rotation time */
  275   m_tLogEnd = ((now / m_tRotation) * m_tRotation) + m_tRotation;
  276   // reset byte counter
  277   m_counter = 0;
  278   
  279   /* rename current file */
  280   if(m_nLogFD >= 0) {
  281     close(m_nLogFD);
  282     rename(file_name, arch);
  283   }
  284   
  285   /* open new file */
  286   m_nLogFD = openFile(cmd, file_name);
  287   if(m_nLogFD < 0) {
  288     /* opening a new file has failed!
  289        try to reopen and clear the last file */
  290     char msg[HUGE_STR];
  291     snprintf(msg, sizeof(msg), "ERROR while writing to file, %ld messages lost\n", *messages);
  292     fprintf(stderr,"[%s]: ERROR, while writing to file <%s>\n", cmd, file_name);
  293     rename(arch,  file_name);
  294     m_nLogFD = openFile(cmd, file_name);
  295     if(m_nLogFD > 0) {
  296       rc = ftruncate(m_nLogFD, 0);
  297       rc = write(m_nLogFD, msg, strlen(msg));
  298     }
  299   } else {
  300     *messages = 0;
  301     if(m_compress || (m_generations != -1)) {
  302       signal(SIGCHLD,sigchild);
  303       if(fork() == 0) {
  304     if(m_compress) {
  305       compressThread(cmd, arch);
  306     }
  307     if(m_generations != -1) {
  308       qs_deleteOldFiles(file_name, m_generations);
  309     }
  310     exit(0);
  311       }
  312     }
  313   }
  314 }
  315 
  316 /**
  317  * Separate thread which initiates file rotation even no
  318  * log data is written.
  319  *
  320  * @param argv (not used)
  321  */
  322 static void *forcedRotationThread(void *argv) {
  323   time_t now;
  324   time_t n;
  325   while(1) {
  326     qs_csLock();
  327     now = get_now();
  328     if(now > m_tLogEnd) {
  329       rotate(m_cmd, now, m_file_name, &m_messages);
  330     }
  331     qs_csUnLock();
  332     now = get_now();
  333     n = 1 + m_tLogEnd - now;
  334     sleep(n);
  335   }
  336   return NULL;
  337 }
  338 
  339 void handle_signal1(int signal) {
  340   rotate(m_cmd, get_now(), m_file_name, &m_messages);
  341   return;
  342 }
  343 
  344 int main(int argc, char **argv) {
  345   char *username = NULL;
  346   int rc;
  347   char *buf;
  348   int nRead, nWrite;
  349   time_t now;
  350   struct stat st;
  351   long sizeLimit = 0;
  352 
  353   pthread_attr_t *tha = NULL;
  354   pthread_t tid;
  355   struct sigaction sa;
  356  
  357   char *cmd = strrchr(argv[0], '/');
  358 
  359   sa.sa_handler = &handle_signal1;
  360   sa.sa_flags = SA_RESTART;
  361    
  362   if(cmd == NULL) {
  363     cmd = argv[0];
  364   } else {
  365     cmd++;
  366   }
  367   m_cmd = calloc(1, strlen(cmd)+1);
  368   strcpy(m_cmd, cmd); // copy as we wan't to pass it when forking
  369 
  370   while(argc >= 1) {
  371     if(strcmp(*argv,"-o") == 0) {
  372       if (--argc >= 1) {
  373     m_file_name = *(++argv);
  374       }
  375     } else if(strcmp(*argv,"-u") == 0) {
  376       if (--argc >= 1) {
  377     username = *(++argv);
  378       }
  379     } else if(strcmp(*argv,"-s") == 0) {
  380       if (--argc >= 1) {
  381     m_tRotation = atoi(*(++argv));
  382       } 
  383     } else if(strcmp(*argv,"-t") == 0) {
  384       if (--argc >= 1) {
  385     m_offset = atoi(*(++argv));
  386     m_offset = m_offset * 3600;
  387     m_offset_enabled = 1;
  388       } 
  389     } else if(strcmp(*argv,"-g") == 0) {
  390       if (--argc >= 1) {
  391     m_generations = atoi(*(++argv));
  392       } 
  393     } else if(strcmp(*argv,"-b") == 0) {
  394       if (--argc >= 1) {
  395     sizeLimit = atol(*(++argv));
  396       } 
  397     } else if(strcmp(*argv,"-m") == 0) {
  398       if (--argc >= 1) {
  399     int mode = atoi(*(++argv));
  400     if(mode == 600) {
  401       m_mode = 0600;
  402     } else if(mode == 640) {
  403       m_mode = 0640;
  404     } else if(mode == 660) {
  405       m_mode = 0660;
  406     } else if(mode == 664) {
  407       m_mode = 0664;
  408     }
  409       } 
  410     } else if(strcmp(*argv,"-z") == 0) {
  411       m_compress = 1;
  412     } else if(strcmp(*argv,"-p") == 0) {
  413       m_stdout = 1;
  414     } else if(strcmp(*argv,"-d") == 0) {
  415       m_timestamp = 1;
  416       memset(time_string, 32, TME_STR_LEN);
  417     } else if(strcmp(*argv,"-f") == 0) {
  418       m_force_rotation = 1;
  419     } else if(strcmp(*argv,"-h") == 0) {
  420       usage(m_cmd, 0);
  421     } else if(strcmp(*argv,"--help") == 0) {
  422       usage(m_cmd, 0);
  423     } else if(strcmp(*argv,"-?") == 0) {
  424       usage(m_cmd, 0);
  425     } else if(strcmp(*argv,"--man") == 0) {
  426       usage(m_cmd, 1);
  427     }
  428 
  429     argc--;
  430     argv++;
  431   }
  432 
  433   if(m_file_name == NULL) usage(m_cmd, 0);
  434   if(sizeLimit > 0 && sizeLimit < m_limit && sizeLimit >= (1024 * 1024)) {
  435     m_limit = sizeLimit;
  436   } else if(sizeLimit > 0 && sizeLimit < (1024 * 1024)) {
  437     m_limit = 1024 * 1024;
  438   }
  439 
  440   if(stat(m_file_name, &st) == 0) {
  441     m_counter = st.st_size;
  442   }
  443 
  444   sigaction(SIGUSR1, &sa, NULL);
  445   qs_setuid(username, m_cmd);
  446   
  447   /* set next rotation time */
  448   now = get_now();
  449   m_tLogEnd = ((now / m_tRotation) * m_tRotation) + m_tRotation;
  450   /* open file */
  451   m_nLogFD = openFile(m_cmd, m_file_name);
  452   if(m_nLogFD < 0) {
  453     /* startup did not success */
  454     exit(2);
  455   }
  456 
  457   if(m_force_rotation) {
  458     qs_csInitLock();
  459     pthread_create(&tid, tha, forcedRotationThread, NULL);
  460   }
  461 
  462   buf = calloc(1, MAX_LINE_BUFFER+1);
  463   for(;;) {
  464     if(m_timestamp) {
  465       // low perf line-by-line read
  466       if(fgets(buf, MAX_LINE_BUFFER, stdin) == NULL) {
  467     exit(3);
  468       } else {
  469     nRead = strlen(buf);
  470     if(m_force_rotation) {
  471     qs_csLock();                       // >@CTR1
  472       }
  473     m_counter += (nRead + TME_STR_LEN);
  474     now = get_now();
  475     writeTimestamp();
  476     nWrite = write(m_nLogFD, buf, nRead);
  477       }
  478     } else {
  479       // normal/fast buffer read/process
  480       nRead = read(0, buf, MAX_LINE_BUFFER);
  481       if(nRead == 0) exit(3);
  482       if(nRead < 0) if(errno != EINTR) exit(4);
  483       if(m_force_rotation) {
  484     qs_csLock();                         // >@CTR1
  485       }
  486       m_counter += nRead;
  487       now = get_now();
  488       /* write data if we have a file handle (else continue but drop log data,
  489      re-try to open the file at next rotation time) */
  490       if(m_nLogFD >= 0) {
  491     do {
  492       nWrite = write(m_nLogFD, buf, nRead);
  493       if(m_stdout) {
  494         printf("%.*s", nRead, buf);
  495       }
  496     } while (nWrite < 0 && errno == EINTR);
  497       }
  498       m_messages++;
  499       if(nWrite != nRead) {
  500     if(m_nLogFD >= 0) {
  501       char msg[HUGE_STR];
  502       snprintf(msg, sizeof(msg), "ERROR while writing to file, %ld messages lost\n", m_messages);
  503       /* error while writing data, try to delete the old file and continue ... */
  504       rc = ftruncate(m_nLogFD, 0);
  505       rc = write(m_nLogFD, msg, strlen(msg));
  506       m_messages = 0;
  507     }
  508       }
  509     }
  510     // end buffer or line read
  511     if((now > m_tLogEnd) || (m_counter > m_limit)) {
  512       /* rotate! */
  513       rotate(m_cmd, now, m_file_name, &m_messages);
  514     }
  515     if(m_force_rotation) {
  516       qs_csUnLock();                         // <@CTR1
  517     }
  518   }
  519   memset(buf, 0, MAX_LINE_BUFFER);
  520   free(buf);
  521   return 0;
  522 }