/* * Copyright (C) 1996-2023 The Squid Software Foundation and contributors * * Squid software is distributed under GPLv2+ license and includes * contributions from numerous individuals and organizations. * Please see the COPYING and CONTRIBUTORS files for details. */ #include "squid.h" #include #include #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_SYS_PARAM_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_PATHS_H #include #endif #include "helper/protocol_defines.h" /* parse buffer - ie, length of longest expected line */ #define LOGFILE_BUF_LEN 65536 static void rotate(const char *path, int rotate_count) { #ifdef S_ISREG struct stat sb; #endif int i; char from[MAXPATHLEN]; char to[MAXPATHLEN]; assert(path); #ifdef S_ISREG if (stat(path, &sb) == 0) if (S_ISREG(sb.st_mode) == 0) return; #endif /* Rotate numbers 0 through N up one */ for (i = rotate_count; i > 1;) { --i; snprintf(from, MAXPATHLEN, "%s.%d", path, i - 1); snprintf(to, MAXPATHLEN, "%s.%d", path, i); #if _SQUID_OS2_ || _SQUID_WINDOWS_ if (remove(to) < 0) { int xerrno = errno; fprintf(stderr, "WARNING: remove '%s' failure: %s\n", to, xstrerr(xerrno)); } #endif if (rename(from, to) < 0 && errno != ENOENT) { int xerrno = errno; fprintf(stderr, "WARNING: rename '%s' to '%s' failure: %s\n", from, to, xstrerr(xerrno)); } } if (rotate_count > 0) { snprintf(to, MAXPATHLEN, "%s.%d", path, 0); #if _SQUID_OS2_ || _SQUID_WINDOWS_ if (remove(to) < 0) { int xerrno = errno; fprintf(stderr, "WARNING: remove '%s' failure: %s\n", to, xstrerr(xerrno)); } #endif if (rename(path, to) < 0 && errno != ENOENT) { int xerrno = errno; fprintf(stderr, "WARNING: rename %s to %s failure: %s\n", path, to, xstrerr(xerrno)); } } } /** * The commands: * * L\n - logfile data * R\n - rotate file * T\n - truncate file * O\n - repoen file * F\n - flush file * r\n - set rotate count to * b\n - 1 = buffer output, 0 = don't buffer output */ int main(int argc, char *argv[]) { int t; FILE *fp; char buf[LOGFILE_BUF_LEN]; int rotate_count = 10; int do_buffer = 1; if (argc < 2) { printf("Error: usage: %s \n", argv[0]); exit(EXIT_FAILURE); } fp = fopen(argv[1], "a"); if (fp == NULL) { perror("fopen"); exit(EXIT_FAILURE); } setbuf(stdout, NULL); /* XXX stderr should not be closed, but in order to support squid must be * able to collect and manage modules's stderr first. */ close(2); t = open(_PATH_DEVNULL, O_RDWR); assert(t > -1); dup2(t, 2); while (fgets(buf, LOGFILE_BUF_LEN, stdin)) { /* First byte indicates what we're logging! */ switch (buf[0]) { case 'L': if (buf[1] != '\0') { fprintf(fp, "%s", buf + 1); /* try to detect the 32-bit file too big write error and rotate */ int err = ferror(fp); clearerr(fp); if (err != 0) { /* file too big - recover by rotating the logs and starting a new one. * out of device space - recover by rotating and hoping that rotation count drops a big one. */ if (err == EFBIG || err == ENOSPC) { fprintf(stderr, "WARNING: %s writing %s. Attempting to recover via a log rotation.\n",xstrerr(err),argv[1]); fclose(fp); rotate(argv[1], rotate_count); fp = fopen(argv[1], "a"); if (fp == NULL) { perror("fopen"); exit(EXIT_FAILURE); } fprintf(fp, "%s", buf + 1); } else { perror("fprintf"); exit(EXIT_FAILURE); } } } if (!do_buffer) fflush(fp); break; case 'R': fclose(fp); rotate(argv[1], rotate_count); fp = fopen(argv[1], "a"); if (fp == NULL) { perror("fopen"); exit(EXIT_FAILURE); } break; case 'T': break; case 'O': break; case 'r': //fprintf(fp, "SET ROTATE: %s\n", buf + 1); rotate_count = atoi(buf + 1); break; case 'b': //fprintf(fp, "SET BUFFERED: %s\n", buf + 1); do_buffer = (buf[1] == '1'); break; case 'F': fflush(fp); break; default: /* Just in case .. */ fprintf(fp, "%s", buf); break; } } fclose(fp); fp = NULL; return EXIT_SUCCESS; }