123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811 |
- #include "queue.h"
- /* alloca() is defined in stdlib.h in NetBSD */
- #if !defined(__NetBSD__) && !defined(__FreeBSD__)
- #include <alloca.h>
- #endif
- #include <limits.h>
- #include <ctype.h>
- #include <dirent.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <popt.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/stat.h>
- #include <sys/wait.h>
- #include <time.h>
- #include <unistd.h>
- #include <glob.h>
- #include <locale.h>
- #include <sys/types.h>
- #include <utime.h>
- #include <stdint.h>
- #include <libgen.h>
- #if defined(SunOS)
- #include <limits.h>
- #endif
- #if !defined(PATH_MAX) && defined(__FreeBSD__)
- #include <sys/param.h>
- #endif
- #include "log.h"
- #include "logrotate.h"
- static void *prev_context;
- #ifdef WITH_SELINUX
- #include <selinux/selinux.h>
- static int selinux_enabled = 0;
- static int selinux_enforce = 0;
- #endif
- #ifdef WITH_ACL
- #include "sys/acl.h"
- #define acl_type acl_t
- #else
- #define acl_type void *
- #endif
- static acl_type prev_acl = NULL;
- #if !defined(GLOB_ABORTED) && defined(GLOB_ABEND)
- #define GLOB_ABORTED GLOB_ABEND
- #endif
- #ifdef PATH_MAX
- #define STATEFILE_BUFFER_SIZE 2 * PATH_MAX + 16
- #else
- #define STATEFILE_BUFFER_SIZE 4096
- #endif
- #ifdef __hpux
- extern int asprintf(char **str, const char *fmt, ...);
- #endif
- /* Number of seconds in a day */
- #define DAY_SECONDS 86400
- struct logState {
- char *fn;
- struct tm lastRotated; /* only tm_hour, tm_mday, tm_mon, tm_year are good! */
- struct stat sb;
- int doRotate;
- int isUsed; /* True if there is real log file in system for this state. */
- LIST_ENTRY(logState) list;
- };
- struct logNames {
- char *firstRotated;
- char *disposeName;
- char *finalName;
- char *dirName;
- char *baseName;
- };
- struct compData {
- int prefix_len;
- const char *dformat;
- };
- static struct logStateList {
- LIST_HEAD(stateSet, logState) head;
- } **states;
- int numLogs = 0;
- int debug = 0;
- static unsigned int hashSize;
- static const char *mailCommand = DEFAULT_MAIL_COMMAND;
- static time_t nowSecs = 0;
- static uid_t save_euid;
- static gid_t save_egid;
- static int globerr(const char *pathname, int theerr)
- {
- message(MESS_ERROR, "error accessing %s: %s\n", pathname,
- strerror(theerr));
- /* We want the glob operation to continue, so return 0 */
- return 1;
- }
- #if defined(HAVE_STRPTIME) && defined(HAVE_QSORT)
- /* We could switch to qsort_r to get rid of this global variable,
- * but qsort_r is not portable enough (Linux vs. *BSD vs ...)... */
- static struct compData _compData;
- static int compGlobResult(const void *result1, const void *result2) {
- struct tm time_tmp;
- time_t t1, t2;
- const char *r1 = *(const char **)(result1);
- const char *r2 = *(const char **)(result2);
- memset(&time_tmp, 0, sizeof(struct tm));
- strptime(r1 + _compData.prefix_len, _compData.dformat, &time_tmp);
- t1 = mktime(&time_tmp);
- memset(&time_tmp, 0, sizeof(struct tm));
- strptime(r2 + _compData.prefix_len, _compData.dformat, &time_tmp);
- t2 = mktime(&time_tmp);
- if (t1 < t2) return -1;
- if (t1 > t2) return 1;
- return 0;
- }
- static void sortGlobResult(glob_t *result, int prefix_len, const char *dformat) {
- if (!dformat || *dformat == '\0') {
- return;
- }
- _compData.prefix_len = prefix_len;
- _compData.dformat = dformat;
- qsort(result->gl_pathv, result->gl_pathc, sizeof(char *), compGlobResult);
- }
- #else
- static void sortGlobResult(glob_t *result, int prefix_len, const char *dformat) {
- /* TODO */
- }
- #endif
- int switch_user(uid_t user, gid_t group) {
- save_egid = getegid();
- save_euid = geteuid();
- if (save_euid == user && save_egid == group)
- return 0;
- message(MESS_DEBUG, "switching euid to %u and egid to %u\n",
- (unsigned) user, (unsigned) group);
- if (setegid(group) || seteuid(user)) {
- message(MESS_ERROR, "error switching euid to %u and egid to %u: %s\n",
- (unsigned) user, (unsigned) group, strerror(errno));
- return 1;
- }
- return 0;
- }
- static int switch_user_permanently(const struct logInfo *log) {
- gid_t group = getegid();
- uid_t user = geteuid();
- if (!(log->flags & LOG_FLAG_SU)) {
- return 0;
- }
- if (getuid() == user && getgid() == group)
- return 0;
- /* switch to full root first */
- if (setgid(getgid()) || setuid(getuid())) {
- message(MESS_ERROR, "error getting rid of euid != uid\n");
- return 1;
- }
- message(MESS_DEBUG, "switching uid to %u and gid to %u\n",
- (unsigned) user, (unsigned) group);
- if (setgid(group) || setuid(user)) {
- message(MESS_ERROR, "error switching euid to %u and egid to %u: %s\n",
- (unsigned) user, (unsigned) group, strerror(errno));
- return 1;
- }
- return 0;
- }
- int switch_user_back(void) {
- return switch_user(save_euid, save_egid);
- }
- static int switch_user_back_permanently(void) {
- gid_t tmp_egid = save_egid;
- uid_t tmp_euid = save_euid;
- int ret = switch_user(save_euid, save_egid);
- save_euid = tmp_euid;
- save_egid = tmp_egid;
- return ret;
- }
- static void unescape(char *arg)
- {
- char *p = arg;
- char *next;
- char escaped;
- while ((next = strchr(p, '\\')) != NULL) {
- p = next;
- switch (p[1]) {
- case 'n':
- escaped = '\n';
- break;
- case '\\':
- escaped = '\\';
- break;
- default:
- ++p;
- continue;
- }
- /* Overwrite the backslash with the intended character,
- * and shift everything down one */
- *p++ = escaped;
- memmove(p, p+1, 1 + strlen(p+1));
- }
- }
- #define HASH_SIZE_MIN 64
- static int allocateHash(unsigned int hs)
- {
- unsigned int i;
- /* Enforce some reasonable minimum hash size */
- if (hs < HASH_SIZE_MIN)
- hs = HASH_SIZE_MIN;
- message(MESS_DEBUG, "Allocating hash table for state file, size %u entries\n",
- hs);
- states = calloc(hs, sizeof(struct logStateList *));
- if (states == NULL) {
- message(MESS_ERROR, "could not allocate memory for "
- "hash table\n");
- return 1;
- }
- for (i = 0; i < hs; i++) {
- states[i] = malloc(sizeof *states[0]);
- if (states[i] == NULL) {
- message(MESS_ERROR, "could not allocate memory for "
- "hash element\n");
- return 1;
- }
- LIST_INIT(&(states[i]->head));
- }
- hashSize = hs;
- return 0;
- }
- #define HASH_CONST 13
- static int hashIndex(const char *fn)
- {
- unsigned hash = 0;
- if (!hashSize)
- /* hash table not yet allocated */
- return -1;
- while (*fn) {
- hash *= HASH_CONST;
- hash += *fn++;
- }
- return hash % hashSize;
- }
- /* safe implementation of dup2(oldfd, nefd) followed by close(oldfd) */
- static int movefd(int oldfd, int newfd)
- {
- int rc;
- if (oldfd == newfd)
- /* avoid accidental close of newfd in case it is equal to oldfd */
- return 0;
- rc = dup2(oldfd, newfd);
- if (rc == 0)
- close(oldfd);
- return rc;
- }
- static int setSecCtx(int fdSrc, const char *src, void **pPrevCtx)
- {
- #ifdef WITH_SELINUX
- security_context_t srcCtx;
- *pPrevCtx = NULL;
- if (!selinux_enabled)
- /* pretend success */
- return 0;
- /* read security context of fdSrc */
- if (fgetfilecon_raw(fdSrc, &srcCtx) < 0) {
- if (errno == ENOTSUP)
- /* pretend success */
- return 0;
- message(MESS_ERROR, "getting file context %s: %s\n", src,
- strerror(errno));
- return selinux_enforce;
- }
- /* save default security context for restoreSecCtx() */
- if (getfscreatecon_raw((security_context_t *)pPrevCtx) < 0) {
- message(MESS_ERROR, "getting default context: %s\n", strerror(errno));
- return selinux_enforce;
- }
- /* set default security context to match fdSrc */
- if (setfscreatecon_raw(srcCtx) < 0) {
- message(MESS_ERROR, "setting default context to %s: %s\n", srcCtx,
- strerror(errno));
- freecon(srcCtx);
- return selinux_enforce;
- }
- message(MESS_DEBUG, "set default create context to %s\n", srcCtx);
- freecon(srcCtx);
- #else
- (void) fdSrc;
- (void) src;
- (void) pPrevCtx;
- #endif
- return 0;
- }
- static int setSecCtxByName(const char *src, void **pPrevCtx)
- {
- int hasErrors = 0;
- #ifdef WITH_SELINUX
- int fd = open(src, O_RDONLY | O_NOFOLLOW);
- if (fd < 0) {
- message(MESS_ERROR, "error opening %s: %s\n", src, strerror(errno));
- return 1;
- }
- hasErrors = setSecCtx(fd, src, pPrevCtx);
- close(fd);
- #else
- (void) src;
- (void) pPrevCtx;
- #endif
- return hasErrors;
- }
- static void restoreSecCtx(void **pPrevCtx)
- {
- #ifdef WITH_SELINUX
- const security_context_t prevCtx = (security_context_t) *pPrevCtx;
- if (!prevCtx)
- /* no security context saved for restoration */
- return;
- /* set default security context to the previously stored one */
- if (selinux_enabled && setfscreatecon_raw(prevCtx) < 0)
- message(MESS_ERROR, "setting default context to %s: %s\n", prevCtx,
- strerror(errno));
- /* free the memory allocated to save the security context */
- freecon(prevCtx);
- *pPrevCtx = NULL;
- #else
- (void) pPrevCtx;
- #endif
- }
- static struct logState *newState(const char *fn)
- {
- struct tm now = *localtime(&nowSecs);
- struct logState *new;
- time_t lr_time;
- message(MESS_DEBUG, "Creating new state\n");
- if ((new = malloc(sizeof(*new))) == NULL)
- return NULL;
- if ((new->fn = strdup(fn)) == NULL) {
- free(new);
- return NULL;
- }
- new->doRotate = 0;
- new->isUsed = 0;
- memset(&new->lastRotated, 0, sizeof(new->lastRotated));
- new->lastRotated.tm_hour = now.tm_hour;
- new->lastRotated.tm_mday = now.tm_mday;
- new->lastRotated.tm_mon = now.tm_mon;
- new->lastRotated.tm_year = now.tm_year;
- new->lastRotated.tm_isdst = now.tm_isdst;
- /* fill in the rest of the new->lastRotated fields */
- lr_time = mktime(&new->lastRotated);
- new->lastRotated = *localtime(&lr_time);
- return new;
- }
- static struct logState *findState(const char *fn)
- {
- const int i = hashIndex(fn);
- struct logState *p;
- if (i < 0)
- /* hash table not yet allocated */
- return NULL;
- for (p = states[i]->head.lh_first; p != NULL; p = p->list.le_next)
- if (!strcmp(fn, p->fn))
- break;
- /* new state */
- if (p == NULL) {
- if ((p = newState(fn)) == NULL)
- return NULL;
- LIST_INSERT_HEAD(&(states[i]->head), p, list);
- }
- return p;
- }
- static int runScript(struct logInfo *log, char *logfn, char *logrotfn, char *script)
- {
- int rc;
- if (debug) {
- message(MESS_DEBUG, "running script with args %s %s: \"%s\"\n",
- logfn, logrotfn, script);
- return 0;
- }
- if (!fork()) {
- if (log->flags & LOG_FLAG_SU) {
- if (switch_user_back_permanently() != 0) {
- exit(1);
- }
- }
- execl("/bin/sh", "sh", "-c", script, "logrotate_script", logfn, logrotfn, (char *) NULL);
- exit(1);
- }
- wait(&rc);
- return rc;
- }
- #ifdef WITH_ACL
- static int is_acl_well_supported(int err)
- {
- switch (err) {
- case ENOTSUP: /* no file system support */
- case EINVAL: /* acl does not point to a valid ACL */
- case ENOSYS: /* compatibility - acl_(g|s)et_fd(3) should never return this */
- case EBUSY: /* compatibility - acl_(g|s)et_fd(3) should never return this */
- return 0;
- default:
- return 1;
- }
- }
- #endif /* WITH_ACL */
- static int createOutputFile(char *fileName, int flags, struct stat *sb,
- acl_type acl, int force_mode)
- {
- int fd = -1;
- struct stat sb_create;
- int acl_set = 0;
- int i;
- for (i = 0; i < 2; ++i) {
- struct tm now;
- size_t fileName_size, buf_size;
- char *backupName, *ptr;
- fd = open(fileName, (flags | O_EXCL | O_NOFOLLOW),
- (S_IRUSR | S_IWUSR) & sb->st_mode);
- if ((fd >= 0) || (errno != EEXIST))
- break;
- /* the destination file already exists, while it should not */
- now = *localtime(&nowSecs);
- fileName_size = strlen(fileName);
- buf_size = fileName_size + sizeof("-YYYYMMDDHH.backup");
- backupName = alloca(buf_size);
- ptr = backupName;
- /* construct backupName starting with fileName */
- strcpy(ptr, fileName);
- ptr += fileName_size;
- buf_size -= fileName_size;
- /* append the -YYYYMMDDHH time stamp and the .backup suffix */
- ptr += strftime(ptr, buf_size, "-%Y%m%d%H", &now);
- strcpy(ptr, ".backup");
- message(MESS_ERROR, "destination %s already exists, renaming to %s\n",
- fileName, backupName);
- if (rename(fileName, backupName) != 0) {
- message(MESS_ERROR, "error renaming already existing output file"
- " %s to %s: %s\n", fileName, backupName, strerror(errno));
- return -1;
- }
- /* existing file renamed, try it once again */
- }
- if (fd < 0) {
- message(MESS_ERROR, "error creating output file %s: %s\n",
- fileName, strerror(errno));
- return -1;
- }
- if (fchmod(fd, (S_IRUSR | S_IWUSR) & sb->st_mode)) {
- message(MESS_ERROR, "error setting mode of %s: %s\n",
- fileName, strerror(errno));
- close(fd);
- return -1;
- }
- if (fstat(fd, &sb_create)) {
- message(MESS_ERROR, "fstat of %s failed: %s\n", fileName,
- strerror(errno));
- close(fd);
- return -1;
- }
- if ((sb_create.st_uid != sb->st_uid || sb_create.st_gid != sb->st_gid) &&
- fchown(fd, sb->st_uid, sb->st_gid)) {
- message(MESS_ERROR, "error setting owner of %s to uid %u and gid %u: %s\n",
- fileName, (unsigned) sb->st_uid, (unsigned) sb->st_gid, strerror(errno));
- close(fd);
- return -1;
- }
- #ifdef WITH_ACL
- if (!force_mode && acl) {
- if (acl_set_fd(fd, acl) == -1) {
- if (is_acl_well_supported(errno)) {
- message(MESS_ERROR, "setting ACL for %s: %s\n",
- fileName, strerror(errno));
- close(fd);
- return -1;
- }
- acl_set = 0;
- }
- else {
- acl_set = 1;
- }
- }
- #else
- (void) acl_set;
- #endif
- if (!acl_set || force_mode) {
- if (fchmod(fd, sb->st_mode)) {
- message(MESS_ERROR, "error setting mode of %s: %s\n",
- fileName, strerror(errno));
- close(fd);
- return -1;
- }
- }
- return fd;
- }
- #define DIGITS 12
- /* unlink, but try to call shred from GNU coreutils if LOG_FLAG_SHRED
- * is enabled (in that case fd needs to be a valid file descriptor) */
- static int shred_file(int fd, char *filename, struct logInfo *log)
- {
- char count[DIGITS]; /* that's a lot of shredding :) */
- const char **fullCommand;
- int id = 0;
- int status;
- if (log->preremove) {
- message(MESS_DEBUG, "running preremove script\n");
- if (runScript(log, filename, NULL, log->preremove)) {
- message(MESS_ERROR,
- "error running preremove script "
- "for %s of '%s'. Not removing this file.\n",
- filename, log->pattern);
- /* What ever was supposed to happen did not happen,
- * therefore do not unlink the file yet. */
- return 1;
- }
- }
- if (!(log->flags & LOG_FLAG_SHRED)) {
- goto unlink_file;
- }
- message(MESS_DEBUG, "Using shred to remove the file %s\n", filename);
- if (log->shred_cycles != 0) {
- fullCommand = alloca(sizeof(*fullCommand) * 6);
- }
- else {
- fullCommand = alloca(sizeof(*fullCommand) * 4);
- }
- fullCommand[id++] = "shred";
- fullCommand[id++] = "-u";
- if (log->shred_cycles != 0) {
- fullCommand[id++] = "-n";
- snprintf(count, DIGITS - 1, "%d", log->shred_cycles);
- fullCommand[id++] = count;
- }
- fullCommand[id++] = "-";
- fullCommand[id++] = NULL;
- if (!fork()) {
- movefd(fd, STDOUT_FILENO);
- if (switch_user_permanently(log) != 0) {
- exit(1);
- }
- execvp(fullCommand[0], (void *) fullCommand);
- exit(1);
- }
- wait(&status);
- if (!WIFEXITED(status) || WEXITSTATUS(status)) {
- message(MESS_ERROR, "Failed to shred %s\n, trying unlink", filename);
- return unlink(filename);
- }
- /* We have to unlink it after shred anyway,
- * because it doesn't remove the file itself */
- unlink_file:
- if (unlink(filename) == 0)
- return 0;
- if (errno != ENOENT)
- return 1;
- /* unlink of log file that no longer exists is not a fatal error */
- message(MESS_ERROR, "error unlinking log file %s: %s\n", filename,
- strerror(errno));
- return 0;
- }
- static int removeLogFile(char *name, struct logInfo *log)
- {
- int fd = -1;
- int result = 0;
- message(MESS_DEBUG, "removing old log %s\n", name);
- if (log->flags & LOG_FLAG_SHRED) {
- fd = open(name, O_RDWR | O_NOFOLLOW);
- if (fd < 0) {
- message(MESS_ERROR, "error opening %s: %s\n",
- name, strerror(errno));
- return 1;
- }
- }
- if (!debug && shred_file(fd, name, log)) {
- message(MESS_ERROR, "Failed to remove old log %s: %s\n",
- name, strerror(errno));
- result = 1;
- }
- if (fd != -1)
- close(fd);
- return result;
- }
- static void setAtimeMtime(const char *filename, const struct stat *sb)
- {
- /* If we can't change atime/mtime, it's not a disaster. It might
- possibly fail under SELinux. But do try to preserve the
- fractional part if we have utimensat(). */
- #if defined HAVE_UTIMENSAT && !defined(__APPLE__)
- struct timespec ts[2];
- ts[0] = sb->st_atim;
- ts[1] = sb->st_mtim;
- utimensat(AT_FDCWD, filename, ts, 0);
- #else
- struct utimbuf utim;
- utim.actime = sb->st_atime;
- utim.modtime = sb->st_mtime;
- utime(filename, &utim);
- #endif
- }
- static int compressLogFile(char *name, struct logInfo *log, struct stat *sb)
- {
- char *compressedName;
- char *envInFilename;
- const char **fullCommand;
- int inFile;
- int outFile;
- int i;
- int status;
- int compressPipe[2];
- char buff[4092];
- int error_printed = 0;
- void *prevCtx;
- message(MESS_DEBUG, "compressing log with: %s\n", log->compress_prog);
- if (debug)
- return 0;
- fullCommand = alloca(sizeof(*fullCommand) *
- (log->compress_options_count + 2));
- fullCommand[0] = log->compress_prog;
- for (i = 0; i < log->compress_options_count; i++)
- fullCommand[i + 1] = log->compress_options_list[i];
- fullCommand[log->compress_options_count + 1] = NULL;
- compressedName = alloca(strlen(name) + strlen(log->compress_ext) + 2);
- sprintf(compressedName, "%s%s", name, log->compress_ext);
- if ((inFile = open(name, O_RDWR | O_NOFOLLOW)) < 0) {
- message(MESS_ERROR, "unable to open %s for compression\n", name);
- return 1;
- }
- if (setSecCtx(inFile, name, &prevCtx) != 0) {
- /* error msg already printed */
- close(inFile);
- return 1;
- }
- #ifdef WITH_ACL
- if ((prev_acl = acl_get_fd(inFile)) == NULL) {
- if (is_acl_well_supported(errno)) {
- message(MESS_ERROR, "getting file ACL %s: %s\n",
- name, strerror(errno));
- restoreSecCtx(&prevCtx);
- close(inFile);
- return 1;
- }
- }
- #endif
- outFile =
- createOutputFile(compressedName, O_RDWR | O_CREAT, sb, prev_acl, 0);
- restoreSecCtx(&prevCtx);
- #ifdef WITH_ACL
- if (prev_acl) {
- acl_free(prev_acl);
- prev_acl = NULL;
- }
- #endif
- if (outFile < 0) {
- close(inFile);
- return 1;
- }
- /* pipe used to capture stderr of the compress process */
- if (pipe(compressPipe) < 0) {
- message(MESS_ERROR, "error opening pipe for compress: %s",
- strerror(errno));
- close(inFile);
- close(outFile);
- return 1;
- }
- if (!fork()) {
- /* close read end of pipe in the child process */
- close(compressPipe[0]);
- movefd(inFile, STDIN_FILENO);
- movefd(outFile, STDOUT_FILENO);
- if (switch_user_permanently(log) != 0) {
- exit(1);
- }
- movefd(compressPipe[1], STDERR_FILENO);
- envInFilename = alloca(strlen("LOGROTATE_COMPRESSED_FILENAME=") + strlen(name) + 2);
- sprintf(envInFilename, "LOGROTATE_COMPRESSED_FILENAME=%s", name);
- putenv(envInFilename);
- execvp(fullCommand[0], (void *) fullCommand);
- exit(1);
- }
- /* close write end of pipe in the parent process */
- close(compressPipe[1]);
- while ((i = read(compressPipe[0], buff, sizeof(buff) - 1)) > 0) {
- if (!error_printed) {
- error_printed = 1;
- message(MESS_ERROR, "Compressing program wrote following message "
- "to stderr when compressing log %s:\n", name);
- }
- buff[i] = '\0';
- fprintf(stderr, "%s", buff);
- }
- close(compressPipe[0]);
- wait(&status);
- fsync(outFile);
- close(outFile);
- if (!WIFEXITED(status) || WEXITSTATUS(status)) {
- message(MESS_ERROR, "failed to compress log %s\n", name);
- close(inFile);
- unlink(compressedName);
- return 1;
- }
- setAtimeMtime(compressedName, sb);
- shred_file(inFile, name, log);
- close(inFile);
- return 0;
- }
- static int mailLog(struct logInfo *log, char *logFile, const char *mailComm,
- char *uncompressCommand, char *address, char *subject)
- {
- int mailInput;
- pid_t mailChild, uncompressChild = 0;
- int mailStatus, uncompressStatus;
- int uncompressPipe[2];
- char * const mailArgv[] = { (char *) mailComm, (char *) "-s", subject, address, NULL };
- int rc = 0;
- if ((mailInput = open(logFile, O_RDONLY | O_NOFOLLOW)) < 0) {
- message(MESS_ERROR, "failed to open %s for mailing: %s\n", logFile,
- strerror(errno));
- return 1;
- }
- if (uncompressCommand) {
- /* pipe used to capture ouput of the uncompress process */
- if (pipe(uncompressPipe) < 0) {
- message(MESS_ERROR, "error opening pipe for uncompress: %s",
- strerror(errno));
- close(mailInput);
- return 1;
- }
- if (!(uncompressChild = fork())) {
- /* uncompress child */
- /* close read end of pipe in the child process */
- close(uncompressPipe[0]);
- movefd(mailInput, STDIN_FILENO);
- movefd(uncompressPipe[1], STDOUT_FILENO);
- if (switch_user_permanently(log) != 0) {
- exit(1);
- }
- execlp(uncompressCommand, uncompressCommand, (char *) NULL);
- exit(1);
- }
- close(mailInput);
- mailInput = uncompressPipe[0];
- close(uncompressPipe[1]);
- }
- if (!(mailChild = fork())) {
- movefd(mailInput, STDIN_FILENO);
- close(STDOUT_FILENO);
- /* mail command runs as root */
- if (log->flags & LOG_FLAG_SU) {
- if (switch_user_back_permanently() != 0) {
- exit(1);
- }
- }
- execvp(mailArgv[0], mailArgv);
- exit(1);
- }
- close(mailInput);
- waitpid(mailChild, &mailStatus, 0);
- if (!WIFEXITED(mailStatus) || WEXITSTATUS(mailStatus)) {
- message(MESS_ERROR, "mail command failed for %s\n", logFile);
- rc = 1;
- }
- if (uncompressCommand) {
- waitpid(uncompressChild, &uncompressStatus, 0);
- if (!WIFEXITED(uncompressStatus) || WEXITSTATUS(uncompressStatus)) {
- message(MESS_ERROR, "uncompress command failed mailing %s\n",
- logFile);
- rc = 1;
- }
- }
- return rc;
- }
- static int mailLogWrapper(char *mailFilename, const char *mailComm,
- int logNum, struct logInfo *log)
- {
- /* uncompress already compressed log files before mailing them */
- char *uncompress_prog = (log->flags & LOG_FLAG_COMPRESS)
- ? log->uncompress_prog
- : NULL;
- char *subject = mailFilename;
- if (log->flags & LOG_FLAG_MAILFIRST) {
- if (log->flags & LOG_FLAG_DELAYCOMPRESS)
- /* the log we are mailing has not been compressed yet */
- uncompress_prog = NULL;
- if (uncompress_prog)
- /* use correct subject when mailfirst is enabled */
- subject = log->files[logNum];
- }
- return mailLog(log, mailFilename, mailComm, uncompress_prog,
- log->logAddress, subject);
- }
- /* Use a heuristic to determine whether stat buffer SB comes from a file
- with sparse blocks. If the file has fewer blocks than would normally
- be needed for a file of its size, then at least one of the blocks in
- the file is a hole. In that case, return true. */
- static int is_probably_sparse(struct stat const *sb)
- {
- #if defined(HAVE_STRUCT_STAT_ST_BLOCKS) && defined(HAVE_STRUCT_STAT_ST_BLKSIZE)
- return (S_ISREG (sb->st_mode)
- && sb->st_blocks < sb->st_size / sb->st_blksize);
- #else
- return 0;
- #endif
- }
- #define MIN(a,b) ((a) < (b) ? (a) : (b))
- /* Return whether the buffer consists entirely of NULs.
- Note the word after the buffer must be non NUL. */
- static int is_nul (void const *buf, size_t bufsize)
- {
- char const *cbuf = buf;
- char const *cp = buf;
- /* Find the first nonzero *byte*, or the sentinel. */
- while (*cp++ == 0)
- continue;
- return cbuf + bufsize < cp;
- }
- static size_t full_write(int fd, const void *buf, size_t count)
- {
- size_t total = 0;
- const char *ptr = (const char *) buf;
- while (count > 0)
- {
- size_t n_rw;
- for (;;)
- {
- n_rw = write (fd, buf, count);
- if (errno == EINTR)
- continue;
- else
- break;
- }
- if (n_rw == (size_t) -1)
- break;
- if (n_rw == 0)
- break;
- total += n_rw;
- ptr += n_rw;
- count -= n_rw;
- }
- return total;
- }
- static int sparse_copy(int src_fd, int dest_fd, struct stat *sb,
- const char *saveLog, const char *currLog)
- {
- int make_holes = is_probably_sparse(sb);
- size_t max_n_read = SIZE_MAX;
- int last_write_made_hole = 0;
- off_t total_n_read = 0;
- char buf[BUFSIZ + 1];
- while (max_n_read) {
- int make_hole = 0;
- ssize_t n_read = read (src_fd, buf, MIN (max_n_read, BUFSIZ));
- if (n_read < 0) {
- if (errno == EINTR) {
- continue;
- }
- message(MESS_ERROR, "error reading %s: %s\n",
- currLog, strerror(errno));
- return 0;
- }
- if (n_read == 0)
- break;
- max_n_read -= n_read;
- total_n_read += n_read;
- if (make_holes) {
- /* Sentinel required by is_nul(). */
- buf[n_read] = '\1';
- if ((make_hole = is_nul(buf, n_read))) {
- if (lseek (dest_fd, n_read, SEEK_CUR) < 0) {
- message(MESS_ERROR, "error seeking %s: %s\n",
- saveLog, strerror(errno));
- return 0;
- }
- }
- }
- if (!make_hole) {
- size_t n = n_read;
- if (full_write (dest_fd, buf, n) != n) {
- message(MESS_ERROR, "error writing to %s: %s\n",
- saveLog, strerror(errno));
- return 0;
- }
- }
- last_write_made_hole = make_hole;
- }
- if (last_write_made_hole) {
- if (ftruncate(dest_fd, total_n_read) < 0) {
- message(MESS_ERROR, "error ftruncate %s: %s\n",
- saveLog, strerror(errno));
- return 0;
- }
- }
- return 1;
- }
- static int copyTruncate(char *currLog, char *saveLog, struct stat *sb,
- int flags, int skip_copy)
- {
- int rc = 1;
- int fdcurr = -1, fdsave = -1;
- void *prevCtx;
- message(MESS_DEBUG, "copying %s to %s\n", currLog, saveLog);
- if (!debug) {
- /* read access is sufficient for 'copy' but not for 'copytruncate' */
- const int read_only = (flags & LOG_FLAG_COPY)
- && !(flags & LOG_FLAG_COPYTRUNCATE);
- if ((fdcurr = open(currLog, ((read_only) ? O_RDONLY : O_RDWR) | O_NOFOLLOW)) < 0) {
- message(MESS_ERROR, "error opening %s: %s\n", currLog,
- strerror(errno));
- goto fail;
- }
- if (!skip_copy) {
- if (setSecCtx(fdcurr, currLog, &prevCtx) != 0) {
- /* error msg already printed */
- goto fail;
- }
- #ifdef WITH_ACL
- if ((prev_acl = acl_get_fd(fdcurr)) == NULL) {
- if (is_acl_well_supported(errno)) {
- message(MESS_ERROR, "getting file ACL %s: %s\n",
- currLog, strerror(errno));
- restoreSecCtx(&prevCtx);
- goto fail;
- }
- }
- #endif /* WITH_ACL */
- fdsave = createOutputFile(saveLog, O_WRONLY | O_CREAT, sb, prev_acl, 0);
- restoreSecCtx(&prevCtx);
- #ifdef WITH_ACL
- if (prev_acl) {
- acl_free(prev_acl);
- prev_acl = NULL;
- }
- #endif
- if (fdsave < 0)
- goto fail;
- if (sparse_copy(fdcurr, fdsave, sb, saveLog, currLog) != 1) {
- message(MESS_ERROR, "error copying %s to %s: %s\n", currLog,
- saveLog, strerror(errno));
- unlink(saveLog);
- goto fail;
- }
- }
- }
- if (flags & LOG_FLAG_COPYTRUNCATE) {
- message(MESS_DEBUG, "truncating %s\n", currLog);
- if (!debug) {
- if (fdsave >= 0)
- fsync(fdsave);
- if (ftruncate(fdcurr, 0)) {
- message(MESS_ERROR, "error truncating %s: %s\n", currLog,
- strerror(errno));
- goto fail;
- }
- }
- } else
- message(MESS_DEBUG, "Not truncating %s\n", currLog);
- rc = 0;
- fail:
- if (fdcurr >= 0) {
- close(fdcurr);
- }
- if (fdsave >= 0) {
- close(fdsave);
- }
- return rc;
- }
- /* return value similar to mktime() but the exact time is ignored */
- static time_t mktimeFromDateOnly(const struct tm *src)
- {
- /* explicit struct copy to retain C89 compatibility */
- struct tm tmp;
- memcpy(&tmp, src, sizeof tmp);
- /* abstract out (nullify) fields expressing the exact time */
- tmp.tm_hour = 0;
- tmp.tm_min = 0;
- tmp.tm_sec = 0;
- return mktime(&tmp);
- }
- /* return by how many days the date was advanced but ignore exact time */
- static int daysElapsed(const struct tm *now, const struct tm *last)
- {
- const time_t diff = mktimeFromDateOnly(now) - mktimeFromDateOnly(last);
- return diff / (24 * 3600);
- }
- static int findNeedRotating(struct logInfo *log, int logNum, int force)
- {
- struct stat sb;
- struct logState *state = NULL;
- struct tm now = *localtime(&nowSecs);
- message(MESS_DEBUG, "considering log %s\n", log->files[logNum]);
- /* Check if parent directory of this log has safe permissions */
- if ((log->flags & LOG_FLAG_SU) == 0 && getuid() == 0) {
- char *logpath = strdup(log->files[logNum]);
- char *ld = dirname(logpath);
- if (stat(ld, &sb)) {
- /* If parent directory doesn't exist, it's not real error
- (unless nomissingok is specified)
- and rotation is not needed */
- if (errno != ENOENT || (errno == ENOENT && (log->flags & LOG_FLAG_MISSINGOK) == 0)) {
- message(MESS_ERROR, "stat of %s failed: %s\n", ld,
- strerror(errno));
- free(logpath);
- return 1;
- }
- free(logpath);
- return 0;
- }
- /* Don't rotate in directories writable by others or group which is not "root" */
- if ((sb.st_gid != 0 && sb.st_mode & S_IWGRP) || sb.st_mode & S_IWOTH) {
- message(MESS_ERROR, "skipping \"%s\" because parent directory has insecure permissions"
- " (It's world writable or writable by group which is not \"root\")"
- " Set \"su\" directive in config file to tell logrotate which user/group"
- " should be used for rotation.\n"
- ,log->files[logNum]);
- free(logpath);
- return 1;
- }
- free(logpath);
- }
- if (lstat(log->files[logNum], &sb)) {
- if ((log->flags & LOG_FLAG_MISSINGOK) && (errno == ENOENT)) {
- message(MESS_DEBUG, " log %s does not exist -- skipping\n",
- log->files[logNum]);
- return 0;
- }
- message(MESS_ERROR, "stat of %s failed: %s\n", log->files[logNum],
- strerror(errno));
- return 1;
- }
- state = findState(log->files[logNum]);
- if (!state)
- return 1;
- state->doRotate = 0;
- state->sb = sb;
- state->isUsed = 1;
- if ((sb.st_mode & S_IFMT) == S_IFLNK) {
- message(MESS_DEBUG, " log %s is symbolic link. Rotation of symbolic"
- " links is not allowed to avoid security issues -- skipping.\n",
- log->files[logNum]);
- return 0;
- }
- message(MESS_DEBUG, " Now: %d-%02d-%02d %02d:%02d\n", 1900 + now.tm_year,
- 1 + now.tm_mon, now.tm_mday,
- now.tm_hour, now.tm_min);
- message(MESS_DEBUG, " Last rotated at %d-%02d-%02d %02d:%02d\n", 1900 + state->lastRotated.tm_year,
- 1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
- state->lastRotated.tm_hour, state->lastRotated.tm_min);
- if (force) {
- /* user forced rotation of logs from command line */
- state->doRotate = 1;
- }
- else if (log->maxsize && sb.st_size > log->maxsize) {
- state->doRotate = 1;
- }
- else if (log->criterium == ROT_SIZE) {
- state->doRotate = (sb.st_size >= log->threshold);
- if (!state->doRotate) {
- message(MESS_DEBUG, " log does not need rotating "
- "(log size is below the 'size' threshold)\n");
- }
- } else if (mktime(&state->lastRotated) - mktime(&now) > (25 * 3600)) {
- /* 25 hours allows for DST changes as well as geographical moves */
- message(MESS_ERROR,
- "log %s last rotated in the future -- rotation forced\n",
- log->files[logNum]);
- state->doRotate = 1;
- } else if (state->lastRotated.tm_year != now.tm_year ||
- state->lastRotated.tm_mon != now.tm_mon ||
- state->lastRotated.tm_mday != now.tm_mday ||
- state->lastRotated.tm_hour != now.tm_hour) {
- int days;
- switch (log->criterium) {
- case ROT_WEEKLY:
- days = daysElapsed(&now, &state->lastRotated);
- /* rotate if date is advanced by 7+ days (exact time is ignored) */
- state->doRotate = (days >= 7)
- /* ... or if we have not yet rotated today */
- || (days >= 1
- /* ... and the selected weekday is today */
- && now.tm_wday == log->weekday);
- if (!state->doRotate) {
- message(MESS_DEBUG, " log does not need rotating "
- "(log has been rotated at %d-%d-%d %d:%d, "
- "that is not week ago yet)\n", 1900 + state->lastRotated.tm_year,
- 1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
- state->lastRotated.tm_hour, state->lastRotated.tm_min);
- }
- break;
- case ROT_HOURLY:
- state->doRotate = ((now.tm_hour != state->lastRotated.tm_hour) ||
- (now.tm_mday != state->lastRotated.tm_mday) ||
- (now.tm_mon != state->lastRotated.tm_mon) ||
- (now.tm_year != state->lastRotated.tm_year));
- if (!state->doRotate) {
- message(MESS_DEBUG, " log does not need rotating "
- "(log has been rotated at %d-%d-%d %d:%d, "
- "that is not hour ago yet)\n", 1900 + state->lastRotated.tm_year,
- 1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
- state->lastRotated.tm_hour, state->lastRotated.tm_min);
- }
- break;
- case ROT_DAYS:
- /* FIXME: only days=1 is implemented!! */
- state->doRotate = ((now.tm_mday != state->lastRotated.tm_mday) ||
- (now.tm_mon != state->lastRotated.tm_mon) ||
- (now.tm_year != state->lastRotated.tm_year));
- if (!state->doRotate) {
- message(MESS_DEBUG, " log does not need rotating "
- "(log has been rotated at %d-%d-%d %d:%d, "
- "that is not day ago yet)\n", 1900 + state->lastRotated.tm_year,
- 1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
- state->lastRotated.tm_hour, state->lastRotated.tm_min);
- }
- break;
- case ROT_MONTHLY:
- /* rotate if the logs haven't been rotated this month or
- this year */
- state->doRotate = ((now.tm_mon != state->lastRotated.tm_mon) ||
- (now.tm_year != state->lastRotated.tm_year));
- if (!state->doRotate) {
- message(MESS_DEBUG, " log does not need rotating "
- "(log has been rotated at %d-%d-%d %d:%d, "
- "that is not month ago yet)\n", 1900 + state->lastRotated.tm_year,
- 1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
- state->lastRotated.tm_hour, state->lastRotated.tm_min);
- }
- break;
- case ROT_YEARLY:
- /* rotate if the logs haven't been rotated this year */
- state->doRotate = (now.tm_year != state->lastRotated.tm_year);
- if (!state->doRotate) {
- message(MESS_DEBUG, " log does not need rotating "
- "(log has been rotated at %d-%d-%d %d:%d, "
- "that is not year ago yet)\n", 1900 + state->lastRotated.tm_year,
- 1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
- state->lastRotated.tm_hour, state->lastRotated.tm_min);
- }
- break;
- case ROT_SIZE:
- default:
- /* ack! */
- state->doRotate = 0;
- break;
- }
- if (log->minsize && sb.st_size < log->minsize) {
- state->doRotate = 0;
- message(MESS_DEBUG, " log does not need rotating "
- "('minsize' directive is used and the log "
- "size is smaller than the minsize value)\n");
- }
- if (log->rotateMinAge && log->rotateMinAge * DAY_SECONDS >= nowSecs - sb.st_mtime) {
- state->doRotate = 0;
- message(MESS_DEBUG, " log does not need rotating "
- "('minage' directive is used and the log "
- "age is smaller than the minage days)\n");
- }
- }
- else if (!state->doRotate) {
- message(MESS_DEBUG, " log does not need rotating "
- "(log has been already rotated)\n");
- }
- /* The notifempty flag overrides the normal criteria */
- if (state->doRotate && !(log->flags & LOG_FLAG_IFEMPTY) && !sb.st_size) {
- state->doRotate = 0;
- message(MESS_DEBUG, " log does not need rotating "
- "(log is empty)\n");
- }
- if (state->doRotate) {
- message(MESS_DEBUG, " log needs rotating\n");
- }
- return 0;
- }
- /* find the rotated file with the highest index */
- static int findLastRotated(const struct logNames *rotNames,
- const char *fileext, const char *compext)
- {
- char *pattern;
- int glob_rc;
- glob_t globResult;
- size_t i;
- int last = 0;
- size_t prefixLen, suffixLen;
- if (asprintf(&pattern, "%s/%s.*%s%s", rotNames->dirName,
- rotNames->baseName, fileext, compext) < 0)
- /* out of memory */
- return -1;
- glob_rc = glob(pattern, 0, globerr, &globResult);
- free(pattern);
- switch (glob_rc) {
- case 0:
- /* glob() succeeded */
- break;
- case GLOB_NOMATCH:
- /* found nothing -> assume first rotation */
- return 0;
- default:
- /* glob() failed */
- return -1;
- }
- prefixLen = strlen(rotNames->dirName) + /* '/' */1
- + strlen(rotNames->baseName) + /* '.' */ 1;
- suffixLen = strlen(fileext) + strlen(compext);
- for (i = 0; i < globResult.gl_pathc; ++i) {
- char *fileName = globResult.gl_pathv[i];
- const size_t fileNameLen = strlen(fileName);
- int num;
- char c;
- if (fileNameLen <= prefixLen + suffixLen)
- /* not enough room for index in this file name */
- continue;
- /* cut off prefix/suffix */
- fileName[fileNameLen - suffixLen] = '\0';
- fileName += prefixLen;
- if (sscanf(fileName, "%d%c", &num, &c) != 1)
- /* index not matched in this file name */
- continue;
- /* update last index */
- if (last < num)
- last = num;
- }
- globfree(&globResult);
- return last;
- }
- static int prerotateSingleLog(struct logInfo *log, int logNum,
- struct logState *state, struct logNames *rotNames)
- {
- struct tm now = *localtime(&nowSecs);
- char *oldName = NULL;
- const char *compext = "";
- const char *fileext = "";
- int hasErrors = 0;
- char *glob_pattern;
- glob_t globResult;
- int rc;
- int rotateCount = log->rotateCount ? log->rotateCount : 1;
- int logStart = (log->logStart == -1) ? 1 : log->logStart;
- #define DATEEXT_LEN 64
- #define PATTERN_LEN (DATEEXT_LEN * 2)
- char dext_str[DATEEXT_LEN];
- char dformat[PATTERN_LEN] = "";
- char dext_pattern[PATTERN_LEN];
- char *dext;
- if (!state->doRotate)
- return 0;
- /* Logs with rotateCounts of 0 are rotated once, then removed. This
- lets scripts run properly, and everything gets mailed properly. */
- message(MESS_DEBUG, "rotating log %s, log->rotateCount is %d\n",
- log->files[logNum], log->rotateCount);
- if (log->compress_ext && (log->flags & LOG_FLAG_COMPRESS))
- compext = log->compress_ext;
- state->lastRotated = now;
- {
- char *logpath = strdup(log->files[logNum]);
- char *ld = dirname(logpath);
- if (log->oldDir) {
- if (log->oldDir[0] != '/') {
- rotNames->dirName =
- malloc(strlen(ld) + strlen(log->oldDir) + 2);
- sprintf(rotNames->dirName, "%s/%s", ld, log->oldDir);
- } else
- rotNames->dirName = strdup(log->oldDir);
- } else
- rotNames->dirName = strdup(ld);
- free(logpath);
- }
- rotNames->baseName = strdup(basename(log->files[logNum]));
- if (log->addextension) {
- size_t baseLen = strlen(rotNames->baseName);
- size_t extLen = strlen(log->addextension);
- if (baseLen >= extLen &&
- strncmp(&(rotNames->baseName[baseLen - extLen]),
- log->addextension, extLen) == 0) {
- char *tempstr;
- tempstr = calloc(baseLen - extLen + 1, sizeof(char));
- strncat(tempstr, rotNames->baseName, baseLen - extLen);
- free(rotNames->baseName);
- rotNames->baseName = tempstr;
- }
- fileext = log->addextension;
- }
- if (log->extension &&
- strncmp(&
- (rotNames->
- baseName[strlen(rotNames->baseName) -
- strlen(log->extension)]), log->extension,
- strlen(log->extension)) == 0) {
- char *tempstr;
- fileext = log->extension;
- tempstr =
- calloc(strlen(rotNames->baseName) - strlen(log->extension) + 1,
- sizeof(char));
- strncat(tempstr, rotNames->baseName,
- strlen(rotNames->baseName) - strlen(log->extension));
- free(rotNames->baseName);
- rotNames->baseName = tempstr;
- }
- /* Adjust "now" if we want yesterday's date */
- if (log->flags & LOG_FLAG_DATEYESTERDAY) {
- now.tm_hour = 12; /* set hour to noon to work around DST issues */
- now.tm_mday = now.tm_mday - 1;
- mktime(&now);
- }
- if (log->flags & LOG_FLAG_DATEHOURAGO) {
- now.tm_hour -= 1;
- mktime(&now);
- }
- /* Construct the glob pattern corresponding to the date format */
- dext_str[0] = '\0';
- if (log->dateformat) {
- size_t i = 0, j = 0;
- memset(dext_pattern, 0, sizeof(dext_pattern));
- dext = log->dateformat;
- while (*dext == ' ')
- dext++;
- while ((*dext != '\0') && (!hasErrors)) {
- /* Will there be a space for a char and '\0'? */
- if (j >= (sizeof(dext_pattern) - 1)) {
- message(MESS_ERROR, "Date format %s is too long\n",
- log->dateformat);
- hasErrors = 1;
- break;
- }
- if (*dext == '%') {
- switch (*(dext + 1)) {
- case 'Y':
- strncat(dext_pattern, "[0-9][0-9]",
- sizeof(dext_pattern) - strlen(dext_pattern) - 1);
- j += 10; /* strlen("[0-9][0-9]") */
- /* FALLTHRU */
- case 'm':
- case 'd':
- case 'H':
- case 'M':
- case 'S':
- case 'V':
- strncat(dext_pattern, "[0-9][0-9]",
- sizeof(dext_pattern) - strlen(dext_pattern) - 1);
- j += 10;
- if (j >= (sizeof(dext_pattern) - 1)) {
- message(MESS_ERROR, "Date format %s is too long\n",
- log->dateformat);
- hasErrors = 1;
- break;
- }
- dformat[i++] = *(dext++);
- dformat[i] = *dext;
- break;
- case 's':
- /* End of year 2293 this pattern does not work. */
- strncat(dext_pattern,
- "[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",
- sizeof(dext_pattern) - strlen(dext_pattern) - 1);
- j += 50;
- if (j >= (sizeof(dext_pattern) - 1)) {
- message(MESS_ERROR, "Date format %s is too long\n",
- log->dateformat);
- hasErrors = 1;
- break;
- }
- dformat[i++] = *(dext++);
- dformat[i] = *dext;
- break;
- default:
- dformat[i++] = *dext;
- dformat[i] = '%';
- dext_pattern[j++] = *dext;
- break;
- }
- } else {
- dformat[i] = *dext;
- dext_pattern[j++] = *dext;
- }
- ++i;
- ++dext;
- }
- dformat[i] = '\0';
- message(MESS_DEBUG, "Converted '%s' -> '%s'\n", log->dateformat, dformat);
- strftime(dext_str, sizeof(dext_str), dformat, &now);
- } else {
- if (log->criterium == ROT_HOURLY) {
- /* hourly adds another two digits */
- strftime(dext_str, sizeof(dext_str), "-%Y%m%d%H", &now);
- strncpy(dext_pattern, "-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",
- sizeof(dext_pattern));
- } else {
- /* The default dateformat and glob pattern */
- strftime(dext_str, sizeof(dext_str), "-%Y%m%d", &now);
- strncpy(dext_pattern, "-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",
- sizeof(dext_pattern));
- }
- dext_pattern[PATTERN_LEN - 1] = '\0';
- }
- message(MESS_DEBUG, "dateext suffix '%s'\n", dext_str);
- message(MESS_DEBUG, "glob pattern '%s'\n", dext_pattern);
- if (setSecCtxByName(log->files[logNum], &prev_context) != 0) {
- /* error msg already printed */
- return 1;
- }
- /* First compress the previous log when necessary */
- if (log->flags & LOG_FLAG_COMPRESS &&
- log->flags & LOG_FLAG_DELAYCOMPRESS) {
- if (log->flags & LOG_FLAG_DATEEXT) {
- /* glob for uncompressed files with our pattern */
- if (asprintf(&glob_pattern, "%s/%s%s%s", rotNames->dirName,
- rotNames->baseName, dext_pattern, fileext) < 0) {
- message(MESS_FATAL, "could not allocate glob pattern memory\n");
- }
- rc = glob(glob_pattern, 0, globerr, &globResult);
- if (!rc && globResult.gl_pathc > 0) {
- size_t glob_count;
- sortGlobResult(&globResult, strlen(rotNames->dirName) + 1 + strlen(rotNames->baseName), dformat);
- for (glob_count = 0; glob_count < globResult.gl_pathc && !hasErrors; glob_count++) {
- struct stat sbprev;
- if (asprintf(&oldName, "%s", (globResult.gl_pathv)[glob_count]) < 0) {
- message(MESS_FATAL, "could not allocate glob result memory\n");
- }
- if (stat(oldName, &sbprev)) {
- if (errno == ENOENT)
- message(MESS_DEBUG, "previous log %s does not exist\n", oldName);
- else
- message(MESS_ERROR, "cannot stat %s: %s\n", oldName, strerror(errno));
- } else {
- hasErrors = compressLogFile(oldName, log, &sbprev);
- }
- free(oldName);
- }
- } else {
- message(MESS_DEBUG,
- "glob finding logs to compress failed\n");
- /* fallback to old behaviour */
- if (asprintf(&oldName, "%s/%s.%d%s", rotNames->dirName,
- rotNames->baseName, logStart, fileext) < 0) {
- message(MESS_FATAL, "could not allocate oldName memory\n");
- }
- free(oldName);
- }
- globfree(&globResult);
- free(glob_pattern);
- } else {
- struct stat sbprev;
- if (asprintf(&oldName, "%s/%s.%d%s", rotNames->dirName,
- rotNames->baseName, logStart, fileext) < 0) {
- message(MESS_FATAL, "could not allocate oldName memory\n");
- }
- if (stat(oldName, &sbprev)) {
- if (errno == ENOENT)
- message(MESS_DEBUG, "previous log %s does not exist\n", oldName);
- else
- message(MESS_ERROR, "cannot stat %s: %s\n", oldName, strerror(errno));
- } else {
- hasErrors = compressLogFile(oldName, log, &sbprev);
- }
- free(oldName);
- }
- }
- /* adding 2 due to / and \0 being added by snprintf */
- rotNames->firstRotated =
- malloc(strlen(rotNames->dirName) + strlen(rotNames->baseName) +
- strlen(fileext) + strlen(compext) + DATEEXT_LEN + 2 );
- if (log->flags & LOG_FLAG_DATEEXT) {
- /* glob for compressed files with our pattern
- * and compress ext */
- if (asprintf(&glob_pattern, "%s/%s%s%s%s", rotNames->dirName,
- rotNames->baseName, dext_pattern, fileext, compext) < 0) {
- message(MESS_ERROR, "could not allocate glob pattern memory\n");
- }
- rc = glob(glob_pattern, 0, globerr, &globResult);
- if (!rc) {
- /* search for files to drop, if we find one remember it,
- * if we find another one mail and remove the first and
- * remember the second and so on */
- struct stat fst_buf;
- int mail_out = -1;
- size_t glob_count;
- /* Remove the first (n - rotateCount) matches no real rotation
- * needed, since the files have the date in their name. Note that
- * (size_t)-1 == SIZE_T_MAX in rotateCount */
- sortGlobResult(&globResult, strlen(rotNames->dirName) + 1 + strlen(rotNames->baseName), dformat);
- for (glob_count = 0; glob_count < globResult.gl_pathc; glob_count++) {
- if (!stat((globResult.gl_pathv)[glob_count], &fst_buf)) {
- if (((globResult.gl_pathc >= (size_t)rotateCount) && (glob_count <= (globResult.gl_pathc - rotateCount)))
- || ((log->rotateAge > 0)
- &&
- (((nowSecs - fst_buf.st_mtime) / DAY_SECONDS)
- > log->rotateAge))) {
- if (mail_out != -1) {
- char *mailFilename =
- (globResult.gl_pathv)[mail_out];
- if (!hasErrors && log->logAddress)
- hasErrors = mailLogWrapper(mailFilename, mailCommand,
- logNum, log);
- if (!hasErrors) {
- message(MESS_DEBUG, "removing %s\n", mailFilename);
- hasErrors = removeLogFile(mailFilename, log);
- }
- }
- mail_out = glob_count;
- }
- }
- }
- if (mail_out != -1) {
- /* oldName is oldest Backup found (for unlink later) */
- if (asprintf(&oldName, "%s", (globResult.gl_pathv)[mail_out]) < 0) {
- message(MESS_FATAL, "could not allocate mailout memory\n");
- }
- rotNames->disposeName = malloc(strlen(oldName)+1);
- strcpy(rotNames->disposeName, oldName);
- free(oldName);
- } else {
- free(rotNames->disposeName);
- rotNames->disposeName = NULL;
- }
- } else {
- message(MESS_DEBUG, "glob finding old rotated logs failed\n");
- free(rotNames->disposeName);
- rotNames->disposeName = NULL;
- }
- /* firstRotated is most recently created/compressed rotated log */
- sprintf(rotNames->firstRotated, "%s/%s%s%s%s",
- rotNames->dirName, rotNames->baseName, dext_str, fileext,
- (log->flags & LOG_FLAG_DELAYCOMPRESS) ? "" : compext);
- globfree(&globResult);
- free(glob_pattern);
- } else {
- int i;
- char *newName = NULL;
- if (log->rotateAge) {
- struct stat fst_buf;
- /* we will not enter the loop in case rotateCount == -1 */
- for (i = 1; i <= rotateCount + 1; i++) {
- if (asprintf(&oldName, "%s/%s.%d%s%s", rotNames->dirName,
- rotNames->baseName, i, fileext, compext) < 0) {
- message(MESS_FATAL, "could not allocate mailFilename memory\n");
- }
- if (!stat(oldName, &fst_buf)
- && (((nowSecs - fst_buf.st_mtime) / DAY_SECONDS)
- > log->rotateAge)) {
- char *mailFilename = oldName;
- if (!hasErrors && log->logAddress)
- hasErrors =
- mailLogWrapper(mailFilename, mailCommand,
- logNum, log);
- if (!hasErrors)
- hasErrors = removeLogFile(mailFilename, log);
- }
- free(oldName);
- }
- }
- if (rotateCount == -1) {
- rotateCount = findLastRotated(rotNames, fileext, compext);
- if (rotateCount < 0) {
- message(MESS_ERROR, "could not find last rotated file: %s/%s.*%s%s\n",
- rotNames->dirName, rotNames->baseName, fileext, compext);
- return 1;
- }
- }
- if (asprintf(&oldName, "%s/%s.%d%s%s", rotNames->dirName,
- rotNames->baseName, logStart + rotateCount, fileext,
- compext) < 0) {
- message(MESS_FATAL, "could not allocate disposeName memory\n");
- }
- if (log->rotateCount != -1)
- rotNames->disposeName = strdup(oldName);
- sprintf(rotNames->firstRotated, "%s/%s.%d%s%s", rotNames->dirName,
- rotNames->baseName, logStart, fileext,
- (log->flags & LOG_FLAG_DELAYCOMPRESS) ? "" : compext);
- for (i = rotateCount + logStart - 1; (i >= 0) && !hasErrors; i--) {
- free(newName);
- newName = oldName;
- if (asprintf(&oldName, "%s/%s.%d%s%s", rotNames->dirName,
- rotNames->baseName, i, fileext, compext) < 0) {
- message(MESS_FATAL, "could not allocate oldName memory\n");
- oldName = NULL;
- break;
- }
- message(MESS_DEBUG,
- "renaming %s to %s (rotatecount %d, logstart %d, i %d), \n",
- oldName, newName, rotateCount, logStart, i);
- if (!debug && rename(oldName, newName)) {
- if (errno == ENOENT) {
- message(MESS_DEBUG, "old log %s does not exist\n",
- oldName);
- } else {
- message(MESS_ERROR, "error renaming %s to %s: %s\n",
- oldName, newName, strerror(errno));
- hasErrors = 1;
- }
- }
- }
- free(newName);
- free(oldName);
- } /* !LOG_FLAG_DATEEXT */
- if (log->flags & LOG_FLAG_DATEEXT) {
- char *destFile;
- struct stat fst_buf;
- if (asprintf(&(rotNames->finalName), "%s/%s%s%s", rotNames->dirName,
- rotNames->baseName, dext_str, fileext) < 0) {
- message(MESS_FATAL, "could not allocate finalName memory\n");
- }
- if (asprintf(&destFile, "%s%s", rotNames->finalName, compext) < 0) {
- message(MESS_FATAL, "could not allocate destFile memory\n");
- }
- if (!stat(destFile, &fst_buf)) {
- message(MESS_ERROR,
- "destination %s already exists, skipping rotation\n",
- rotNames->firstRotated);
- hasErrors = 1;
- }
- free(destFile);
- } else {
- /* note: the gzip extension is *not* used here! */
- if (asprintf(&(rotNames->finalName), "%s/%s.%d%s", rotNames->dirName,
- rotNames->baseName, logStart, fileext) < 0) {
- message(MESS_ERROR, "could not allocate finalName memory\n");
- }
- }
- /* if the last rotation doesn't exist, that's okay */
- if (rotNames->disposeName && access(rotNames->disposeName, F_OK)) {
- message(MESS_DEBUG,
- "log %s doesn't exist -- won't try to dispose of it\n",
- rotNames->disposeName);
- free(rotNames->disposeName);
- rotNames->disposeName = NULL;
- }
- return hasErrors;
- }
- static int rotateSingleLog(struct logInfo *log, int logNum,
- struct logState *state, struct logNames *rotNames)
- {
- int hasErrors = 0;
- struct stat sb;
- int fd;
- void *savedContext = NULL;
- char *tmpFilename = NULL;
- if (!state->doRotate)
- return 0;
- if (!hasErrors) {
- if (!(log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))) {
- if (setSecCtxByName(log->files[logNum], &savedContext) != 0) {
- /* error msg already printed */
- return 1;
- }
- #ifdef WITH_ACL
- if ((prev_acl = acl_get_file(log->files[logNum], ACL_TYPE_ACCESS)) == NULL) {
- if (is_acl_well_supported(errno)) {
- message(MESS_ERROR, "getting file ACL %s: %s\n",
- log->files[logNum], strerror(errno));
- hasErrors = 1;
- }
- }
- #endif /* WITH_ACL */
- if (log->flags & LOG_FLAG_TMPFILENAME) {
- if (asprintf(&tmpFilename, "%s%s", log->files[logNum], ".tmp") < 0) {
- message(MESS_FATAL, "could not allocate tmpFilename memory\n");
- restoreSecCtx(&savedContext);
- return 1;
- }
- message(MESS_DEBUG, "renaming %s to %s\n", log->files[logNum],
- tmpFilename);
- if (!debug && !hasErrors && rename(log->files[logNum], tmpFilename)) {
- message(MESS_ERROR, "failed to rename %s to %s: %s\n",
- log->files[logNum], tmpFilename,
- strerror(errno));
- hasErrors = 1;
- }
- }
- else {
- message(MESS_DEBUG, "renaming %s to %s\n", log->files[logNum],
- rotNames->finalName);
- if (!debug && !hasErrors &&
- rename(log->files[logNum], rotNames->finalName)) {
- message(MESS_ERROR, "failed to rename %s to %s: %s\n",
- log->files[logNum], rotNames->finalName,
- strerror(errno));
- hasErrors = 1;
- }
- }
- if (!log->rotateCount) {
- const char *ext = "";
- if (log->compress_ext && (log->flags & LOG_FLAG_COMPRESS))
- ext = log->compress_ext;
- free(rotNames->disposeName);
- if (asprintf(&rotNames->disposeName, "%s%s", rotNames->finalName, ext) < 0)
- message(MESS_FATAL, "could not allocate disposeName memory\n");
- message(MESS_DEBUG, "disposeName will be %s\n", rotNames->disposeName);
- }
- }
- if (!hasErrors && log->flags & LOG_FLAG_CREATE &&
- !(log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY))) {
- int have_create_mode = 0;
- if (log->createUid == NO_UID)
- sb.st_uid = state->sb.st_uid;
- else
- sb.st_uid = log->createUid;
- if (log->createGid == NO_GID)
- sb.st_gid = state->sb.st_gid;
- else
- sb.st_gid = log->createGid;
- if (log->createMode == NO_MODE)
- sb.st_mode = state->sb.st_mode & 0777;
- else {
- sb.st_mode = log->createMode;
- have_create_mode = 1;
- }
- message(MESS_DEBUG, "creating new %s mode = 0%o uid = %d "
- "gid = %d\n", log->files[logNum], (unsigned int) sb.st_mode,
- (int) sb.st_uid, (int) sb.st_gid);
- if (!debug) {
- if (!hasErrors) {
- fd = createOutputFile(log->files[logNum], O_CREAT | O_RDWR,
- &sb, prev_acl, have_create_mode);
- #ifdef WITH_ACL
- if (prev_acl) {
- acl_free(prev_acl);
- prev_acl = NULL;
- }
- #endif
- if (fd < 0)
- hasErrors = 1;
- else {
- close(fd);
- }
- }
- }
- }
- restoreSecCtx(&savedContext);
- if (!hasErrors
- && log->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY)
- && !(log->flags & LOG_FLAG_TMPFILENAME)) {
- hasErrors = copyTruncate(log->files[logNum], rotNames->finalName,
- &state->sb, log->flags, !log->rotateCount);
- }
- #ifdef WITH_ACL
- if (prev_acl) {
- acl_free(prev_acl);
- prev_acl = NULL;
- }
- #endif /* WITH_ACL */
- }
- return hasErrors;
- }
- static int postrotateSingleLog(struct logInfo *log, int logNum,
- struct logState *state,
- struct logNames *rotNames)
- {
- int hasErrors = 0;
- if (!state->doRotate) {
- return 0;
- }
- if (!hasErrors && log->flags & LOG_FLAG_TMPFILENAME) {
- char *tmpFilename = NULL;
- if (asprintf(&tmpFilename, "%s%s", log->files[logNum], ".tmp") < 0) {
- message(MESS_FATAL, "could not allocate tmpFilename memory\n");
- return 1;
- }
- hasErrors = copyTruncate(tmpFilename, rotNames->finalName,
- &state->sb, LOG_FLAG_COPY, /* skip_copy */ 0);
- message(MESS_DEBUG, "removing tmp log %s \n", tmpFilename);
- if (!debug && !hasErrors) {
- unlink(tmpFilename);
- }
- }
- if (!hasErrors && (log->flags & LOG_FLAG_COMPRESS) &&
- !(log->flags & LOG_FLAG_DELAYCOMPRESS)) {
- hasErrors = compressLogFile(rotNames->finalName, log, &state->sb);
- }
- if (!hasErrors && log->logAddress) {
- char *mailFilename;
- if (log->flags & LOG_FLAG_MAILFIRST)
- mailFilename = rotNames->firstRotated;
- else
- mailFilename = rotNames->disposeName;
- if (mailFilename)
- hasErrors = mailLogWrapper(mailFilename, mailCommand, logNum, log);
- }
- if (!hasErrors && rotNames->disposeName)
- hasErrors = removeLogFile(rotNames->disposeName, log);
- restoreSecCtx(&prev_context);
- return hasErrors;
- }
- static int rotateLogSet(struct logInfo *log, int force)
- {
- int i, j;
- int hasErrors = 0;
- int *logHasErrors;
- int numRotated = 0;
- struct logState **state;
- struct logNames **rotNames;
- logHasErrors = calloc(log->numFiles, sizeof(int));
- if (!logHasErrors) {
- message(MESS_ERROR, "could not allocate memory for "
- "logHasErrors\n");
- return 1;
- }
- message(MESS_DEBUG, "\nrotating pattern: %s ", log->pattern);
- if (force) {
- message(MESS_DEBUG, "forced from command line ");
- }
- else {
- switch (log->criterium) {
- case ROT_HOURLY:
- message(MESS_DEBUG, "hourly ");
- break;
- case ROT_DAYS:
- message(MESS_DEBUG, "after %jd days ", (intmax_t)log->threshold);
- break;
- case ROT_WEEKLY:
- message(MESS_DEBUG, "weekly ");
- break;
- case ROT_MONTHLY:
- message(MESS_DEBUG, "monthly ");
- break;
- case ROT_YEARLY:
- message(MESS_DEBUG, "yearly ");
- break;
- case ROT_SIZE:
- message(MESS_DEBUG, "%jd bytes ", (intmax_t)log->threshold);
- break;
- default:
- message(MESS_DEBUG, "rotateLogSet() does not have case for: %u ",
- (unsigned) log->criterium);
- }
- }
- if (log->rotateCount > 0)
- message(MESS_DEBUG, "(%d rotations)\n", log->rotateCount);
- else if (log->rotateCount == 0)
- message(MESS_DEBUG, "(no old logs will be kept)\n");
- if (log->oldDir)
- message(MESS_DEBUG, "olddir is %s, ", log->oldDir);
- if (log->flags & LOG_FLAG_IFEMPTY)
- message(MESS_DEBUG, "empty log files are rotated, ");
- else
- message(MESS_DEBUG, "empty log files are not rotated, ");
- if (log->minsize)
- message(MESS_DEBUG, "only log files >= %jd bytes are rotated, ", (intmax_t)log->minsize);
- if (log->maxsize)
- message(MESS_DEBUG, "log files >= %jd are rotated earlier, ", (intmax_t)log->maxsize);
- if (log->rotateMinAge)
- message(MESS_DEBUG, "only log files older than %d days are rotated, ", log->rotateMinAge);
- if (log->logAddress) {
- message(MESS_DEBUG, "old logs mailed to %s\n", log->logAddress);
- } else {
- message(MESS_DEBUG, "old logs are removed\n");
- }
- if (log->numFiles == 0) {
- message(MESS_DEBUG, "No logs found. Rotation not needed.\n");
- free(logHasErrors);
- return 0;
- }
- if (log->flags & LOG_FLAG_SU) {
- if (switch_user(log->suUid, log->suGid) != 0) {
- free(logHasErrors);
- return 1;
- }
- }
- for (i = 0; i < log->numFiles; i++) {
- struct logState *logState;
- logHasErrors[i] = findNeedRotating(log, i, force);
- hasErrors |= logHasErrors[i];
- /* sure is a lot of findStating going on .. */
- if (((logState = findState(log->files[i]))) && logState->doRotate)
- numRotated++;
- }
- if (log->first) {
- if (!numRotated) {
- message(MESS_DEBUG, "not running first action script, "
- "since no logs will be rotated\n");
- } else {
- message(MESS_DEBUG, "running first action script\n");
- if (runScript(log, log->pattern, NULL, log->first)) {
- message(MESS_ERROR, "error running first action script "
- "for %s\n", log->pattern);
- hasErrors = 1;
- if (log->flags & LOG_FLAG_SU) {
- if (switch_user_back() != 0) {
- free(logHasErrors);
- return 1;
- }
- }
- /* finish early, firstaction failed, affects all logs in set */
- free(logHasErrors);
- return hasErrors;
- }
- }
- }
- state = malloc(log->numFiles * sizeof(struct logState *));
- rotNames = malloc(log->numFiles * sizeof(struct logNames *));
- for (j = 0;
- (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && j < log->numFiles)
- || ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && j < 1); j++) {
- for (i = j;
- ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
- || (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
- state[i] = findState(log->files[i]);
- if (!state[i])
- logHasErrors[i] = 1;
- rotNames[i] = malloc(sizeof(struct logNames));
- memset(rotNames[i], 0, sizeof(struct logNames));
- logHasErrors[i] |=
- prerotateSingleLog(log, i, state[i], rotNames[i]);
- hasErrors |= logHasErrors[i];
- }
- if (log->pre
- && (!(
- ((logHasErrors[j] || !state[j]->doRotate) && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
- || (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS))
- ))
- ) {
- if (!numRotated) {
- message(MESS_DEBUG, "not running prerotate script, "
- "since no logs will be rotated\n");
- } else {
- message(MESS_DEBUG, "running prerotate script\n");
- if (runScript(log, log->flags & LOG_FLAG_SHAREDSCRIPTS ? log->pattern : log->files[j], NULL, log->pre)) {
- if (log->flags & LOG_FLAG_SHAREDSCRIPTS)
- message(MESS_ERROR,
- "error running shared prerotate script "
- "for '%s'\n", log->pattern);
- else {
- message(MESS_ERROR,
- "error running non-shared prerotate script "
- "for %s of '%s'\n", log->files[j], log->pattern);
- }
- logHasErrors[j] = 1;
- hasErrors = 1;
- }
- }
- }
- for (i = j;
- ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
- || (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
- if (! ( (logHasErrors[i] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
- || (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) ) {
- logHasErrors[i] |=
- rotateSingleLog(log, i, state[i], rotNames[i]);
- hasErrors |= logHasErrors[i];
- }
- }
- if (log->post
- && (!(
- ((logHasErrors[j] || !state[j]->doRotate) && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
- || (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS))
- ))
- ) {
- if (!numRotated) {
- message(MESS_DEBUG, "not running postrotate script, "
- "since no logs were rotated\n");
- } else {
- char *logfn = (log->flags & LOG_FLAG_SHAREDSCRIPTS) ? log->pattern : log->files[j];
- /* It only makes sense to pass in a final rotated filename if scripts are not shared */
- char *logrotfn = (log->flags & LOG_FLAG_SHAREDSCRIPTS) ? NULL : rotNames[j]->finalName;
- message(MESS_DEBUG, "running postrotate script\n");
- if (runScript(log, logfn, logrotfn, log->post)) {
- if (log->flags & LOG_FLAG_SHAREDSCRIPTS)
- message(MESS_ERROR,
- "error running shared postrotate script "
- "for '%s'\n", log->pattern);
- else {
- message(MESS_ERROR,
- "error running non-shared postrotate script "
- "for %s of '%s'\n", log->files[j], log->pattern);
- }
- logHasErrors[j] = 1;
- hasErrors = 1;
- }
- }
- }
- for (i = j;
- ((log->flags & LOG_FLAG_SHAREDSCRIPTS) && i < log->numFiles)
- || (!(log->flags & LOG_FLAG_SHAREDSCRIPTS) && i == j); i++) {
- if (! ( (logHasErrors[i] && !(log->flags & LOG_FLAG_SHAREDSCRIPTS))
- || (hasErrors && (log->flags & LOG_FLAG_SHAREDSCRIPTS)) ) ) {
- logHasErrors[i] |=
- postrotateSingleLog(log, i, state[i], rotNames[i]);
- hasErrors |= logHasErrors[i];
- }
- }
- }
- for (i = 0; i < log->numFiles; i++) {
- free(rotNames[i]->firstRotated);
- free(rotNames[i]->disposeName);
- free(rotNames[i]->finalName);
- free(rotNames[i]->dirName);
- free(rotNames[i]->baseName);
- free(rotNames[i]);
- }
- free(rotNames);
- free(state);
- if (log->last) {
- if (!numRotated) {
- message(MESS_DEBUG, "not running last action script, "
- "since no logs will be rotated\n");
- } else {
- message(MESS_DEBUG, "running last action script\n");
- if (runScript(log, log->pattern, NULL, log->last)) {
- message(MESS_ERROR, "error running last action script "
- "for %s\n", log->pattern);
- hasErrors = 1;
- }
- }
- }
- if (log->flags & LOG_FLAG_SU) {
- if (switch_user_back() != 0) {
- free(logHasErrors);
- return 1;
- }
- }
- free(logHasErrors);
- return hasErrors;
- }
- static int writeState(const char *stateFilename)
- {
- struct logState *p;
- FILE *f;
- char *chptr;
- unsigned int i = 0;
- int error = 0;
- int bytes = 0;
- int fdcurr;
- int fdsave;
- struct stat sb;
- char *tmpFilename = NULL;
- struct tm now = *localtime(&nowSecs);
- time_t now_time, last_time;
- void *prevCtx;
- tmpFilename = malloc(strlen(stateFilename) + 5 );
- if (tmpFilename == NULL) {
- message(MESS_ERROR, "could not allocate memory for "
- "tmp state filename\n");
- return 1;
- }
- strcpy(tmpFilename, stateFilename);
- strcat(tmpFilename, ".tmp");
- /* Remove possible tmp state file from previous run */
- unlink(tmpFilename);
- if ((fdcurr = open(stateFilename, O_RDONLY)) < 0) {
- message(MESS_ERROR, "error opening %s: %s\n", stateFilename,
- strerror(errno));
- free(tmpFilename);
- return 1;
- }
- if (setSecCtx(fdcurr, stateFilename, &prevCtx) != 0) {
- /* error msg already printed */
- free(tmpFilename);
- close(fdcurr);
- return 1;
- }
- #ifdef WITH_ACL
- if ((prev_acl = acl_get_fd(fdcurr)) == NULL) {
- if (is_acl_well_supported(errno)) {
- message(MESS_ERROR, "getting file ACL %s: %s\n",
- stateFilename, strerror(errno));
- restoreSecCtx(&prevCtx);
- free(tmpFilename);
- close(fdcurr);
- return 1;
- }
- }
- #endif
- close(fdcurr);
- if (stat(stateFilename, &sb) == -1) {
- message(MESS_ERROR, "error stating %s: %s\n", stateFilename, strerror(errno));
- free(tmpFilename);
- #ifdef WITH_ACL
- if (prev_acl)
- acl_free(prev_acl);
- #endif
- return 1;
- }
- fdsave = createOutputFile(tmpFilename, O_RDWR | O_CREAT | O_TRUNC, &sb, prev_acl, 0);
- #ifdef WITH_ACL
- if (prev_acl) {
- acl_free(prev_acl);
- prev_acl = NULL;
- }
- #endif
- restoreSecCtx(&prevCtx);
- if (fdsave < 0) {
- free(tmpFilename);
- return 1;
- }
- f = fdopen(fdsave, "w");
- if (!f) {
- message(MESS_ERROR, "error creating temp state file %s: %s\n",
- tmpFilename, strerror(errno));
- free(tmpFilename);
- return 1;
- }
- bytes = fprintf(f, "logrotate state -- version 2\n");
- if (bytes < 0)
- error = bytes;
- /*
- * Time in seconds it takes earth to go around sun. The value is
- * astronomical measurement (solar year) rather than something derived from
- * a convention (calendar year).
- */
- #define SECONDS_IN_YEAR 31556926
- for (i = 0; i < hashSize && error == 0; i++) {
- for (p = states[i]->head.lh_first; p != NULL && error == 0;
- p = p->list.le_next) {
- /* Skip states which are not used for more than a year. */
- now_time = mktime(&now);
- last_time = mktime(&p->lastRotated);
- if (!p->isUsed && difftime(now_time, last_time) > SECONDS_IN_YEAR) {
- message(MESS_DEBUG, "Removing %s from state file, "
- "because it does not exist and has not been rotated for one year\n",
- p->fn);
- continue;
- }
- error = fputc('"', f) == EOF;
- for (chptr = p->fn; *chptr && error == 0; chptr++) {
- switch (*chptr) {
- case '"':
- case '\\':
- error = fputc('\\', f) == EOF;
- break;
- case '\n':
- error = fputc('\\', f) == EOF;
- if (error == 0) {
- error = fputc('n', f) == EOF;
- }
- continue;
- default:
- break;
- }
- if (error == 0 && fputc(*chptr, f) == EOF) {
- error = 1;
- }
- }
- if (error == 0 && fputc('"', f) == EOF)
- error = 1;
- if (error == 0) {
- bytes = fprintf(f, " %d-%d-%d-%d:%d:%d\n",
- p->lastRotated.tm_year + 1900,
- p->lastRotated.tm_mon + 1,
- p->lastRotated.tm_mday,
- p->lastRotated.tm_hour,
- p->lastRotated.tm_min,
- p->lastRotated.tm_sec);
- if (bytes < 0)
- error = bytes;
- }
- }
- }
- if (error == 0)
- error = fflush(f);
- if (error == 0)
- error = fsync(fdsave);
- if (error == 0)
- error = fclose(f);
- else
- fclose(f);
- if (error == 0) {
- if (rename(tmpFilename, stateFilename)) {
- unlink(tmpFilename);
- error = 1;
- message(MESS_ERROR, "error renaming temp state file %s\n",
- tmpFilename);
- }
- }
- else {
- unlink(tmpFilename);
- if (errno)
- message(MESS_ERROR, "error creating temp state file %s: %s\n",
- tmpFilename, strerror(errno));
- else
- message(MESS_ERROR, "error creating temp state file %s%s\n",
- tmpFilename, error == ENOMEM ?
- ": Insufficient storage space is available." : "" );
- }
- free(tmpFilename);
- return error;
- }
- static int readState(const char *stateFilename)
- {
- FILE *f;
- char buf[STATEFILE_BUFFER_SIZE];
- char *filename;
- const char **argv;
- int argc;
- int year, month, day, hour, minute, second;
- int i;
- int line = 0;
- int error;
- struct logState *st;
- time_t lr_time;
- struct stat f_stat;
- int rc = 0;
- message(MESS_DEBUG, "Reading state from file: %s\n", stateFilename);
- error = stat(stateFilename, &f_stat);
- if (error) {
- /* treat non-statable file as an empty file */
- f_stat.st_size = 0;
- if (errno != ENOENT) {
- message(MESS_ERROR, "error stat()ing state file %s: %s\n",
- stateFilename, strerror(errno));
- /* do not return until the hash table is allocated */
- rc = 1;
- }
- }
- if (!debug && (f_stat.st_size == 0)) {
- /* create the file before continuing to ensure we have write
- access to the file */
- f = fopen(stateFilename, "w");
- if (f) {
- fprintf(f, "logrotate state -- version 2\n");
- fclose(f);
- } else {
- message(MESS_ERROR, "error creating state file %s: %s\n",
- stateFilename, strerror(errno));
- /* do not return until the hash table is allocated */
- rc = 1;
- }
- }
- /* Try to estimate how many state entries we have in the state file.
- * We expect single entry to have around 80 characters (Of course this is
- * just an estimation). During the testing I've found out that 200 entries
- * per single hash entry gives good mem/performance ratio. */
- if (allocateHash(f_stat.st_size / 80 / 200))
- return 1;
- if (rc || (f_stat.st_size == 0))
- /* error already occurred, or we have no state file to read from */
- return rc;
- f = fopen(stateFilename, "r");
- if (!f) {
- message(MESS_ERROR, "error opening state file %s: %s\n",
- stateFilename, strerror(errno));
- return 1;
- }
- if (!fgets(buf, sizeof(buf) - 1, f)) {
- message(MESS_ERROR, "error reading top line of %s\n",
- stateFilename);
- fclose(f);
- return 1;
- }
- if (strcmp(buf, "logrotate state -- version 1\n") &&
- strcmp(buf, "logrotate state -- version 2\n")) {
- fclose(f);
- message(MESS_ERROR, "bad top line in state file %s\n",
- stateFilename);
- return 1;
- }
- line++;
- while (fgets(buf, sizeof(buf) - 1, f)) {
- argv = NULL;
- line++;
- i = strlen(buf);
- if (buf[i - 1] != '\n') {
- message(MESS_ERROR, "line %d too long in state file %s\n",
- line, stateFilename);
- fclose(f);
- return 1;
- }
- buf[i - 1] = '\0';
- if (i == 1)
- continue;
- year = month = day = hour = minute = second = 0;
- if (poptParseArgvString(buf, &argc, &argv) || (argc != 2) ||
- (sscanf(argv[1], "%d-%d-%d-%d:%d:%d", &year, &month, &day, &hour, &minute, &second) < 3)) {
- message(MESS_ERROR, "bad line %d in state file %s\n",
- line, stateFilename);
- free(argv);
- fclose(f);
- return 1;
- }
- /* Hack to hide earlier bug */
- if ((year != 1900) && (year < 1970 || year > 2100)) {
- message(MESS_ERROR,
- "bad year %d for file %s in state file %s\n", year,
- argv[0], stateFilename);
- free(argv);
- fclose(f);
- return 1;
- }
- if (month < 1 || month > 12) {
- message(MESS_ERROR,
- "bad month %d for file %s in state file %s\n", month,
- argv[0], stateFilename);
- free(argv);
- fclose(f);
- return 1;
- }
- /* 0 to hide earlier bug */
- if (day < 0 || day > 31) {
- message(MESS_ERROR,
- "bad day %d for file %s in state file %s\n", day,
- argv[0], stateFilename);
- free(argv);
- fclose(f);
- return 1;
- }
- if (hour < 0 || hour > 23) {
- message(MESS_ERROR,
- "bad hour %d for file %s in state file %s\n", hour,
- argv[0], stateFilename);
- free(argv);
- fclose(f);
- return 1;
- }
- if (minute < 0 || minute > 59) {
- message(MESS_ERROR,
- "bad minute %d for file %s in state file %s\n", minute,
- argv[0], stateFilename);
- free(argv);
- fclose(f);
- return 1;
- }
- if (second < 0 || second > 59) {
- message(MESS_ERROR,
- "bad second %d for file %s in state file %s\n", second,
- argv[0], stateFilename);
- free(argv);
- fclose(f);
- return 1;
- }
- year -= 1900, month -= 1;
- filename = strdup(argv[0]);
- unescape(filename);
- if ((st = findState(filename)) == NULL) {
- free(argv);
- free(filename);
- fclose(f);
- return 1;
- }
- memset(&st->lastRotated, 0, sizeof(st->lastRotated));
- st->lastRotated.tm_year = year;
- st->lastRotated.tm_mon = month;
- st->lastRotated.tm_mday = day;
- st->lastRotated.tm_hour = hour;
- st->lastRotated.tm_min = minute;
- st->lastRotated.tm_sec = second;
- st->lastRotated.tm_isdst = -1;
- /* fill in the rest of the st->lastRotated fields */
- lr_time = mktime(&st->lastRotated);
- st->lastRotated = *localtime(&lr_time);
- free(argv);
- free(filename);
- }
- fclose(f);
- return 0;
- }
- int main(int argc, const char **argv)
- {
- int force = 0;
- const char *stateFile = STATEFILE;
- char *logFile = NULL;
- FILE *logFd = NULL;
- int rc = 0;
- int arg;
- const char **files;
- poptContext optCon;
- struct logInfo *log;
- struct poptOption options[] = {
- {"debug", 'd', 0, NULL, 'd',
- "Don't do anything, just test and print debug messages", NULL},
- {"force", 'f', 0, &force, 0, "Force file rotation", NULL},
- {"mail", 'm', POPT_ARG_STRING, &mailCommand, 0,
- "Command to send mail (instead of `" DEFAULT_MAIL_COMMAND "')",
- "command"},
- {"state", 's', POPT_ARG_STRING, &stateFile, 0,
- "Path of state file",
- "statefile"},
- {"verbose", 'v', 0, NULL, 'v', "Display messages during rotation", NULL},
- {"log", 'l', POPT_ARG_STRING, &logFile, 'l', "Log file or 'syslog' to log to syslog",
- "logfile"},
- {"version", '\0', POPT_ARG_NONE, NULL, 'V', "Display version information", NULL},
- POPT_AUTOHELP { NULL, 0, 0, NULL, 0, NULL, NULL }
- };
- logSetLevel(MESS_NORMAL);
- setlocale (LC_ALL, "");
- optCon = poptGetContext("logrotate", argc, argv, options, 0);
- poptReadDefaultConfig(optCon, 1);
- poptSetOtherOptionHelp(optCon, "[OPTION...] <configfile>");
- while ((arg = poptGetNextOpt(optCon)) >= 0) {
- switch (arg) {
- case 'd':
- debug = 1;
- message(MESS_NORMAL, "WARNING: logrotate in debug mode does nothing"
- " except printing debug messages! Consider using verbose"
- " mode (-v) instead if this is not what you want.\n\n");
- /* fallthrough */
- case 'v':
- logSetLevel(MESS_DEBUG);
- break;
- case 'l':
- if (strcmp(logFile, "syslog") == 0) {
- logToSyslog(1);
- }
- else {
- logFd = fopen(logFile, "w");
- if (!logFd) {
- message(MESS_ERROR, "error opening log file %s: %s\n",
- logFile, strerror(errno));
- break;
- }
- logSetMessageFile(logFd);
- }
- break;
- case 'V':
- printf("logrotate %s\n", VERSION);
- printf("\n");
- printf(" Default mail command: %s\n", DEFAULT_MAIL_COMMAND);
- printf(" Default compress command: %s\n", COMPRESS_COMMAND);
- printf(" Default uncompress command: %s\n", UNCOMPRESS_COMMAND);
- printf(" Default compress extension: %s\n", COMPRESS_EXT);
- printf(" Default state file path: %s\n", STATEFILE);
- #ifdef WITH_ACL
- printf(" ACL support: yes\n");
- #else
- printf(" ACL support: no\n");
- #endif
- #ifdef WITH_SELINUX
- printf(" SELinux support: yes\n");
- #else
- printf(" SELinux support: no\n");
- #endif
- poptFreeContext(optCon);
- exit(0);
- default:
- break;
- }
- }
- if (arg < -1) {
- fprintf(stderr, "logrotate: bad argument %s: %s\n",
- poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
- poptStrerror(rc));
- poptFreeContext(optCon);
- return 2;
- }
- files = poptGetArgs((poptContext) optCon);
- if (!files) {
- fprintf(stderr, "logrotate " VERSION
- " - Copyright (C) 1995-2001 Red Hat, Inc.\n");
- fprintf(stderr,
- "This may be freely redistributed under the terms of "
- "the GNU General Public License\n\n");
- poptPrintUsage(optCon, stderr, 0);
- poptFreeContext(optCon);
- exit(1);
- }
- #ifdef WITH_SELINUX
- selinux_enabled = (is_selinux_enabled() > 0);
- selinux_enforce = security_getenforce();
- #endif
- TAILQ_INIT(&logs);
- if (readAllConfigPaths(files))
- rc = 1;
- poptFreeContext(optCon);
- nowSecs = time(NULL);
- if (readState(stateFile))
- rc = 1;
- message(MESS_DEBUG, "\nHandling %d logs\n", numLogs);
- for (log = logs.tqh_first; log != NULL; log = log->list.tqe_next)
- rc |= rotateLogSet(log, force);
- if (!debug)
- rc |= writeState(stateFile);
- return (rc != 0);
- }
- /* vim: set et sw=4 ts=4: */
|