config.c 70 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938
  1. #include "queue.h"
  2. /* Alloca is defined in stdlib.h in NetBSD/FreeBSD */
  3. #if !defined(__NetBSD__) && !defined(__FreeBSD__)
  4. #include <alloca.h>
  5. #endif
  6. #include <limits.h>
  7. #include <ctype.h>
  8. #include <dirent.h>
  9. #include <errno.h>
  10. #include <fcntl.h>
  11. #include <glob.h>
  12. #include <grp.h>
  13. #include <popt.h>
  14. #include <pwd.h>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include <sys/stat.h>
  19. #include <time.h>
  20. #include <unistd.h>
  21. #include <assert.h>
  22. #include <wchar.h>
  23. #include <wctype.h>
  24. #include <fnmatch.h>
  25. #include <sys/mman.h>
  26. #include <libgen.h>
  27. #if !defined(PATH_MAX) && defined(__FreeBSD__)
  28. #include <sys/param.h>
  29. #endif
  30. #include "log.h"
  31. #include "logrotate.h"
  32. #if !defined(GLOB_ABORTED) && defined(GLOB_ABEND)
  33. #define GLOB_ABORTED GLOB_ABEND
  34. #endif
  35. #define REALLOC_STEP 10
  36. #define GLOB_STR_REALLOC_STEP 0x100
  37. #if defined(SunOS)
  38. #include <limits.h>
  39. #if !defined(isblank)
  40. #define isblank(c) ( (c) == ' ' || (c) == '\t' ) ? 1 : 0
  41. #endif
  42. #endif
  43. #ifdef __hpux
  44. #include "asprintf.c"
  45. #endif
  46. #if !defined(HAVE_ASPRINTF) && !defined(_FORTIFY_SOURCE)
  47. #include <stdarg.h>
  48. int asprintf(char **string_ptr, const char *format, ...)
  49. {
  50. va_list arg;
  51. char *str;
  52. int size;
  53. int rv;
  54. va_start(arg, format);
  55. size = vsnprintf(NULL, 0, format, arg);
  56. size++;
  57. va_end(arg);
  58. va_start(arg, format);
  59. str = malloc(size);
  60. if (str == NULL) {
  61. va_end(arg);
  62. /*
  63. * Strictly speaking, GNU asprintf doesn't do this,
  64. * but the caller isn't checking the return value.
  65. */
  66. fprintf(stderr, "failed to allocate memory\\n");
  67. exit(1);
  68. }
  69. rv = vsnprintf(str, size, format, arg);
  70. va_end(arg);
  71. *string_ptr = str;
  72. return (rv);
  73. }
  74. #endif
  75. #if !defined(HAVE_STRNDUP)
  76. char *strndup(const char *s, size_t n)
  77. {
  78. size_t nAvail;
  79. char *p;
  80. if(!s)
  81. return NULL;
  82. /* min() */
  83. nAvail = strlen(s) + 1;
  84. if ( (n + 1) < nAvail)
  85. nAvail = n + 1;
  86. p = malloc(nAvail);
  87. if (!p)
  88. return NULL;
  89. memcpy(p, s, nAvail);
  90. p[nAvail - 1] = 0;
  91. return p;
  92. }
  93. #endif
  94. /* list of compression commands and the corresponding file extensions */
  95. struct compress_cmd_item {
  96. const char *cmd;
  97. const char *ext;
  98. };
  99. static const struct compress_cmd_item compress_cmd_list[] = {
  100. {"gzip", ".gz"},
  101. {"bzip2", ".bz2"},
  102. {"xz", ".xz"},
  103. {"compress", ".Z"},
  104. {"zip", "zip"},
  105. };
  106. static const int compress_cmd_list_size = sizeof(compress_cmd_list)
  107. / sizeof(compress_cmd_list[0]);
  108. enum {
  109. STATE_DEFAULT = 2,
  110. STATE_SKIP_LINE = 4,
  111. STATE_DEFINITION_END = 8,
  112. STATE_SKIP_CONFIG = 16,
  113. STATE_LOAD_SCRIPT = 32,
  114. STATE_ERROR = 64,
  115. };
  116. static const char *defTabooExts[] = {
  117. ",v",
  118. ".cfsaved",
  119. ".disabled",
  120. ".dpkg-bak",
  121. ".dpkg-del",
  122. ".dpkg-dist",
  123. ".dpkg-new",
  124. ".dpkg-old",
  125. ".dpkg-tmp",
  126. ".rhn-cfg-tmp-*",
  127. ".rpmnew",
  128. ".rpmorig",
  129. ".rpmsave",
  130. ".swp",
  131. ".ucf-dist",
  132. ".ucf-new",
  133. ".ucf-old",
  134. "~"
  135. };
  136. static const int defTabooCount = sizeof(defTabooExts) / sizeof(char *);
  137. /* I shouldn't use globals here :-( */
  138. static char **tabooPatterns = NULL;
  139. static int tabooCount = 0;
  140. static int glob_errno = 0;
  141. static int readConfigFile(const char *configFile, struct logInfo *defConfig);
  142. static int globerr(const char *pathname, int theerr);
  143. static char *isolateLine(char **strt, char **buf, size_t length) {
  144. char *endtag, *start, *tmp;
  145. const char *max = *buf + length;
  146. char *key;
  147. start = *strt;
  148. endtag = start;
  149. while (endtag < max && *endtag != '\n') {
  150. endtag++;}
  151. if (max < endtag)
  152. return NULL;
  153. tmp = endtag - 1;
  154. while (isspace((unsigned char)*endtag))
  155. endtag--;
  156. key = strndup(start, endtag - start + 1);
  157. *strt = tmp;
  158. return key;
  159. }
  160. static char *isolateValue(const char *fileName, int lineNum, const char *key,
  161. char **startPtr, char **buf, size_t length)
  162. {
  163. char *chptr = *startPtr;
  164. const char *max = *startPtr + length;
  165. while (chptr < max && isblank((unsigned char)*chptr))
  166. chptr++;
  167. if (chptr < max && *chptr == '=') {
  168. chptr++;
  169. while ( chptr < max && isblank((unsigned char)*chptr))
  170. chptr++;
  171. }
  172. if (chptr < max && *chptr == '\n') {
  173. message(MESS_ERROR, "%s:%d argument expected after %s\n",
  174. fileName, lineNum, key);
  175. return NULL;
  176. }
  177. *startPtr = chptr;
  178. return isolateLine(startPtr, buf, length);
  179. }
  180. static char *isolateWord(char **strt, char **buf, size_t length) {
  181. char *endtag, *start;
  182. const char *max = *buf + length;
  183. char *key;
  184. start = *strt;
  185. while (start < max && isblank((unsigned char)*start))
  186. start++;
  187. endtag = start;
  188. while (endtag < max && isalpha((unsigned char)*endtag)) {
  189. endtag++;}
  190. if (max < endtag)
  191. return NULL;
  192. key = strndup(start, endtag - start);
  193. *strt = endtag;
  194. return key;
  195. }
  196. static char *readPath(const char *configFile, int lineNum, const char *key,
  197. char **startPtr, char **buf, size_t length)
  198. {
  199. char *path = isolateValue(configFile, lineNum, key, startPtr, buf, length);
  200. if (path != NULL) {
  201. wchar_t pwc;
  202. size_t len;
  203. char *chptr = path;
  204. while (*chptr && (len = mbrtowc(&pwc, chptr, strlen(chptr), NULL)) != 0) {
  205. if (len == (size_t)(-1) || len == (size_t)(-2) || !iswprint(pwc) || iswblank(pwc)) {
  206. message(MESS_ERROR, "%s:%d bad %s path %s\n",
  207. configFile, lineNum, key, path);
  208. free(path);
  209. return NULL;
  210. }
  211. chptr += len;
  212. }
  213. }
  214. return path;
  215. }
  216. /* set *pUid to UID of the given user, return non-zero on failure */
  217. static int resolveUid(const char *userName, uid_t *pUid)
  218. {
  219. struct passwd *pw;
  220. #ifdef __CYGWIN__
  221. if (strcmp(userName, "root") == 0) {
  222. *pUid = 0;
  223. return 0;
  224. }
  225. #endif
  226. pw = getpwnam(userName);
  227. if (!pw)
  228. return -1;
  229. *pUid = pw->pw_uid;
  230. endpwent();
  231. return 0;
  232. }
  233. /* set *pGid to GID of the given group, return non-zero on failure */
  234. static int resolveGid(const char *groupName, gid_t *pGid)
  235. {
  236. struct group *gr;
  237. #ifdef __CYGWIN__
  238. if (strcmp(groupName, "root") == 0) {
  239. *pGid = 0;
  240. return 0;
  241. }
  242. #endif
  243. gr = getgrnam(groupName);
  244. if (!gr)
  245. return -1;
  246. *pGid = gr->gr_gid;
  247. endgrent();
  248. return 0;
  249. }
  250. static int readModeUidGid(const char *configFile, int lineNum, char *key,
  251. const char *directive, mode_t *mode, uid_t *pUid,
  252. gid_t *pGid)
  253. {
  254. char u[200], g[200];
  255. unsigned int m = 0;
  256. char tmp;
  257. int rc;
  258. if (!strcmp("su", directive))
  259. /* do not read <mode> for the 'su' directive */
  260. rc = 0;
  261. else
  262. rc = sscanf(key, "%o %199s %199s%c", &m, u, g, &tmp);
  263. /* We support 'key <owner> <group> notation now */
  264. if (rc == 0) {
  265. rc = sscanf(key, "%199s %199s%c", u, g, &tmp);
  266. /* Simulate that we have read mode and keep the default value. */
  267. if (rc > 0) {
  268. m = *mode;
  269. rc += 1;
  270. }
  271. }
  272. if (rc == 4) {
  273. message(MESS_ERROR, "%s:%d extra arguments for "
  274. "%s\n", configFile, lineNum, directive);
  275. return -1;
  276. }
  277. if (rc > 0) {
  278. *mode = m;
  279. }
  280. if (rc > 1) {
  281. if (resolveUid(u, pUid) != 0) {
  282. message(MESS_ERROR, "%s:%d unknown user '%s'\n",
  283. configFile, lineNum, u);
  284. return -1;
  285. }
  286. }
  287. if (rc > 2) {
  288. if (resolveGid(g, pGid) != 0) {
  289. message(MESS_ERROR, "%s:%d unknown group '%s'\n",
  290. configFile, lineNum, g);
  291. return -1;
  292. }
  293. }
  294. return 0;
  295. }
  296. static char *readAddress(const char *configFile, int lineNum, const char *key,
  297. char **startPtr, char **buf, size_t length)
  298. {
  299. char *start = *startPtr;
  300. char *address = isolateValue(configFile, lineNum, key, startPtr, buf, length);
  301. if (address != NULL) {
  302. /* validate the address */
  303. char *chptr = address;
  304. while (isprint((unsigned char) *chptr) && *chptr != ' ') {
  305. chptr++;
  306. }
  307. if (*chptr) {
  308. message(MESS_ERROR, "%s:%d bad %s address %s\n",
  309. configFile, lineNum, key, start);
  310. free(address);
  311. return NULL;
  312. }
  313. }
  314. return address;
  315. }
  316. static int do_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid) {
  317. if (mkdir(path, mode) == 0) {
  318. /* newly created directory, set the owner and permissions */
  319. if (chown(path, uid, gid) != 0) {
  320. message(MESS_ERROR, "error setting owner of %s to uid %u and gid %u: %s\n",
  321. path, (unsigned) uid, (unsigned) gid, strerror(errno));
  322. return -1;
  323. }
  324. if (chmod(path, mode) != 0) {
  325. message(MESS_ERROR, "error setting permissions of %s to 0%o: %s\n",
  326. path, mode, strerror(errno));
  327. return -1;
  328. }
  329. return 0;
  330. }
  331. if (errno == EEXIST) {
  332. /* path already exists, check whether it is a directory or not */
  333. struct stat sb;
  334. if ((stat(path, &sb) == 0) && S_ISDIR(sb.st_mode))
  335. return 0;
  336. message(MESS_ERROR, "path %s already exists, but it is not a directory\n", path);
  337. errno = ENOTDIR;
  338. return -1;
  339. }
  340. message(MESS_ERROR, "error creating %s: %s\n", path, strerror(errno));
  341. return -1;
  342. }
  343. static int mkpath(const char *path, mode_t mode, uid_t uid, gid_t gid) {
  344. char *pp;
  345. char *sp;
  346. int rv;
  347. char *copypath = strdup(path);
  348. rv = 0;
  349. pp = copypath;
  350. while (rv == 0 && (sp = strchr(pp, '/')) != NULL) {
  351. if (sp != pp) {
  352. *sp = '\0';
  353. rv = do_mkdir(copypath, mode, uid, gid);
  354. *sp = '/';
  355. }
  356. pp = sp + 1;
  357. }
  358. if (rv == 0) {
  359. rv = do_mkdir(path, mode, uid, gid);
  360. }
  361. free(copypath);
  362. return rv;
  363. }
  364. static int checkFile(const char *fname)
  365. {
  366. int i;
  367. /* Check if fname is '.' or '..'; if so, return false */
  368. if (fname[0] == '.' && (!fname[1] || (fname[1] == '.' && !fname[2])))
  369. return 0;
  370. /* Check if fname is ending in a taboo-extension; if so, return false */
  371. for (i = 0; i < tabooCount; i++) {
  372. const char *pattern = tabooPatterns[i];
  373. if (!fnmatch(pattern, fname, FNM_PERIOD))
  374. {
  375. message(MESS_DEBUG, "Ignoring %s, because of %s pattern match\n",
  376. fname, pattern);
  377. return 0;
  378. }
  379. }
  380. /* All checks have been passed; return true */
  381. return 1;
  382. }
  383. /* Used by qsort to sort filelist */
  384. static int compar(const void *p, const void *q)
  385. {
  386. return strcoll(*((char **) p), *((char **) q));
  387. }
  388. /* Free memory blocks pointed to by pointers in a 2d array and the array itself */
  389. static void free_2d_array(char **array, int lines_count)
  390. {
  391. int i;
  392. for (i = 0; i < lines_count; ++i)
  393. free(array[i]);
  394. free(array);
  395. }
  396. static void copyLogInfo(struct logInfo *to, struct logInfo *from)
  397. {
  398. memset(to, 0, sizeof(*to));
  399. if (from->oldDir)
  400. to->oldDir = strdup(from->oldDir);
  401. to->criterium = from->criterium;
  402. to->weekday = from->weekday;
  403. to->threshold = from->threshold;
  404. to->minsize = from->minsize;
  405. to->maxsize = from->maxsize;
  406. to->rotateCount = from->rotateCount;
  407. to->rotateMinAge = from->rotateMinAge;
  408. to->rotateAge = from->rotateAge;
  409. to->logStart = from->logStart;
  410. if (from->pre)
  411. to->pre = strdup(from->pre);
  412. if (from->post)
  413. to->post = strdup(from->post);
  414. if (from->first)
  415. to->first = strdup(from->first);
  416. if (from->last)
  417. to->last = strdup(from->last);
  418. if (from->preremove)
  419. to->preremove = strdup(from->preremove);
  420. if (from->logAddress)
  421. to->logAddress = strdup(from->logAddress);
  422. if (from->extension)
  423. to->extension = strdup(from->extension);
  424. if (from->compress_prog)
  425. to->compress_prog = strdup(from->compress_prog);
  426. if (from->uncompress_prog)
  427. to->uncompress_prog = strdup(from->uncompress_prog);
  428. if (from->compress_ext)
  429. to->compress_ext = strdup(from->compress_ext);
  430. to->flags = from->flags;
  431. to->shred_cycles = from->shred_cycles;
  432. to->createMode = from->createMode;
  433. to->createUid = from->createUid;
  434. to->createGid = from->createGid;
  435. to->suUid = from->suUid;
  436. to->suGid = from->suGid;
  437. to->olddirMode = from->olddirMode;
  438. to->olddirUid = from->olddirUid;
  439. to->olddirGid = from->olddirGid;
  440. if (from->compress_options_count) {
  441. poptDupArgv(from->compress_options_count, from->compress_options_list,
  442. &to->compress_options_count, &to->compress_options_list);
  443. }
  444. if (from->dateformat)
  445. to->dateformat = strdup(from->dateformat);
  446. }
  447. static void freeLogInfo(struct logInfo *log)
  448. {
  449. free(log->pattern);
  450. free_2d_array(log->files, log->numFiles);
  451. free(log->oldDir);
  452. free(log->pre);
  453. free(log->post);
  454. free(log->first);
  455. free(log->last);
  456. free(log->preremove);
  457. free(log->logAddress);
  458. free(log->extension);
  459. free(log->compress_prog);
  460. free(log->uncompress_prog);
  461. free(log->compress_ext);
  462. free(log->compress_options_list);
  463. free(log->dateformat);
  464. }
  465. static struct logInfo *newLogInfo(struct logInfo *template)
  466. {
  467. struct logInfo *new;
  468. if ((new = malloc(sizeof(*new))) == NULL)
  469. return NULL;
  470. copyLogInfo(new, template);
  471. TAILQ_INSERT_TAIL(&logs, new, list);
  472. numLogs++;
  473. return new;
  474. }
  475. static void removeLogInfo(struct logInfo *log)
  476. {
  477. if (log == NULL)
  478. return;
  479. freeLogInfo(log);
  480. TAILQ_REMOVE(&logs, log, list);
  481. numLogs--;
  482. }
  483. static void freeTailLogs(int num)
  484. {
  485. message(MESS_DEBUG, "removing last %d log configs\n", num);
  486. while (num--)
  487. removeLogInfo(TAILQ_LAST(&logs, logInfoHead));
  488. }
  489. static const char *crit_to_string(enum criterium crit)
  490. {
  491. switch (crit) {
  492. case ROT_HOURLY: return "hourly";
  493. case ROT_DAYS: return "daily";
  494. case ROT_WEEKLY: return "weekly";
  495. case ROT_MONTHLY: return "montly";
  496. case ROT_YEARLY: return "yearly";
  497. case ROT_SIZE: return "size";
  498. default: return "XXX";
  499. }
  500. }
  501. static void set_criterium(enum criterium *pDst, enum criterium src, int *pSet)
  502. {
  503. if (*pSet && (*pDst != src)) {
  504. /* we are overriding a previously set criterium */
  505. message(MESS_VERBOSE, "warning: '%s' overrides previously specified '%s'\n",
  506. crit_to_string(src), crit_to_string(*pDst));
  507. }
  508. *pDst = src;
  509. *pSet = 1;
  510. }
  511. static int readConfigPath(const char *path, struct logInfo *defConfig)
  512. {
  513. struct stat sb;
  514. int result = 0;
  515. struct logInfo defConfigBackup;
  516. if (stat(path, &sb)) {
  517. message(MESS_ERROR, "cannot stat %s: %s\n", path, strerror(errno));
  518. return 1;
  519. }
  520. if (S_ISDIR(sb.st_mode)) {
  521. char **namelist, **p;
  522. struct dirent *dp;
  523. int files_count, here, i;
  524. DIR *dirp;
  525. if ((here = open(".", O_RDONLY)) == -1) {
  526. message(MESS_ERROR, "cannot open current directory: %s\n",
  527. strerror(errno));
  528. return 1;
  529. }
  530. if ((dirp = opendir(path)) == NULL) {
  531. message(MESS_ERROR, "cannot open directory %s: %s\n", path,
  532. strerror(errno));
  533. close(here);
  534. return 1;
  535. }
  536. files_count = 0;
  537. namelist = NULL;
  538. while ((dp = readdir(dirp)) != NULL) {
  539. if (checkFile(dp->d_name)) {
  540. /* Realloc memory for namelist array if necessary */
  541. if (files_count % REALLOC_STEP == 0) {
  542. p = (char **) realloc(namelist,
  543. (files_count +
  544. REALLOC_STEP) * sizeof(char *));
  545. if (p) {
  546. namelist = p;
  547. memset(namelist + files_count, '\0',
  548. REALLOC_STEP * sizeof(char *));
  549. } else {
  550. free_2d_array(namelist, files_count);
  551. closedir(dirp);
  552. close(here);
  553. message(MESS_ERROR, "cannot realloc: %s\n",
  554. strerror(errno));
  555. return 1;
  556. }
  557. }
  558. /* Alloc memory for file name */
  559. if ((namelist[files_count] =
  560. (char *) malloc(strlen(dp->d_name) + 1))) {
  561. strcpy(namelist[files_count], dp->d_name);
  562. files_count++;
  563. } else {
  564. free_2d_array(namelist, files_count);
  565. closedir(dirp);
  566. close(here);
  567. message(MESS_ERROR, "cannot realloc: %s\n",
  568. strerror(errno));
  569. return 1;
  570. }
  571. }
  572. }
  573. closedir(dirp);
  574. if (files_count > 0) {
  575. qsort(namelist, files_count, sizeof(char *), compar);
  576. } else {
  577. close(here);
  578. return 0;
  579. }
  580. if (chdir(path)) {
  581. message(MESS_ERROR, "error in chdir(\"%s\"): %s\n", path,
  582. strerror(errno));
  583. close(here);
  584. free_2d_array(namelist, files_count);
  585. return 1;
  586. }
  587. for (i = 0; i < files_count; ++i) {
  588. assert(namelist[i] != NULL);
  589. copyLogInfo(&defConfigBackup, defConfig);
  590. if (readConfigFile(namelist[i], defConfig)) {
  591. message(MESS_ERROR, "found error in file %s, skipping\n", namelist[i]);
  592. freeLogInfo(defConfig);
  593. copyLogInfo(defConfig, &defConfigBackup);
  594. freeLogInfo(&defConfigBackup);
  595. result = 1;
  596. continue;
  597. }
  598. freeLogInfo(&defConfigBackup);
  599. }
  600. if (fchdir(here) < 0) {
  601. message(MESS_ERROR, "could not change directory to '.'");
  602. }
  603. close(here);
  604. free_2d_array(namelist, files_count);
  605. } else {
  606. copyLogInfo(&defConfigBackup, defConfig);
  607. if (readConfigFile(path, defConfig)) {
  608. freeLogInfo(defConfig);
  609. copyLogInfo(defConfig, &defConfigBackup);
  610. result = 1;
  611. }
  612. freeLogInfo(&defConfigBackup);
  613. }
  614. return result;
  615. }
  616. int readAllConfigPaths(const char **paths)
  617. {
  618. int i, result = 0;
  619. const char **file;
  620. struct logInfo defConfig = {
  621. .pattern = NULL,
  622. .files = NULL,
  623. .numFiles = 0,
  624. .oldDir = NULL,
  625. .criterium = ROT_SIZE,
  626. .threshold = 1024 * 1024,
  627. .minsize = 0,
  628. .maxsize = 0,
  629. .rotateCount = 0,
  630. .rotateMinAge = 0,
  631. .rotateAge = 0,
  632. .logStart = -1,
  633. .pre = NULL,
  634. .post = NULL,
  635. .first = NULL,
  636. .last = NULL,
  637. .preremove = NULL,
  638. .logAddress = NULL,
  639. .extension = NULL,
  640. .addextension = NULL,
  641. .compress_prog = NULL,
  642. .uncompress_prog = NULL,
  643. .compress_ext = NULL,
  644. .dateformat = NULL,
  645. .flags = LOG_FLAG_IFEMPTY,
  646. .shred_cycles = 0,
  647. .createMode = NO_MODE,
  648. .createUid = NO_UID,
  649. .createGid = NO_GID,
  650. .olddirMode = NO_MODE,
  651. .olddirUid = NO_UID,
  652. .olddirGid = NO_GID,
  653. .suUid = NO_UID,
  654. .suGid = NO_GID,
  655. .compress_options_list = NULL,
  656. .compress_options_count = 0
  657. };
  658. tabooPatterns = malloc(sizeof(*tabooPatterns) * defTabooCount);
  659. for (i = 0; i < defTabooCount; i++) {
  660. int bytes;
  661. char *pattern = NULL;
  662. /* generate a pattern by concatenating star (wildcard) to the
  663. * suffix literal
  664. */
  665. bytes = asprintf(&pattern, "*%s", defTabooExts[i]);
  666. if (bytes != -1) {
  667. tabooPatterns[i] = pattern;
  668. tabooCount++;
  669. } else {
  670. free_2d_array(tabooPatterns, tabooCount);
  671. message(MESS_ERROR, "cannot malloc: %s\n", strerror(errno));
  672. return 1;
  673. }
  674. }
  675. for (file = paths; *file; file++) {
  676. if (readConfigPath(*file, &defConfig))
  677. result = 1;
  678. }
  679. free_2d_array(tabooPatterns, tabooCount);
  680. freeLogInfo(&defConfig);
  681. return result;
  682. }
  683. static char* parseGlobString(const char *configFile, int lineNum,
  684. const char *buf, off_t length, char **ppos)
  685. {
  686. /* output buffer */
  687. char *globString = NULL;
  688. size_t globStringPos = 0;
  689. size_t globStringAlloc = 0;
  690. enum {
  691. PGS_INIT, /* picking blanks, looking for '#' */
  692. PGS_DATA, /* picking data, looking for end of line */
  693. PGS_COMMENT /* skipping comment, looking for end of line */
  694. } state = PGS_INIT;
  695. /* move the cursor at caller's side while going through the input */
  696. for (; (*ppos - buf < length) && **ppos; (*ppos)++) {
  697. /* state transition (see above) */
  698. switch (state) {
  699. case PGS_INIT:
  700. if ('#' == **ppos)
  701. state = PGS_COMMENT;
  702. else if (!isspace((unsigned char) **ppos))
  703. state = PGS_DATA;
  704. break;
  705. default:
  706. if ('\n' == **ppos)
  707. state = PGS_INIT;
  708. };
  709. if (PGS_COMMENT == state)
  710. /* skip comment */
  711. continue;
  712. switch (**ppos) {
  713. case '}':
  714. message(MESS_ERROR, "%s:%d unexpected } (missing previous '{')\n", configFile, lineNum);
  715. free(globString);
  716. return NULL;
  717. case '{':
  718. /* NUL-terminate globString */
  719. assert(globStringPos < globStringAlloc);
  720. globString[globStringPos] = '\0';
  721. return globString;
  722. default:
  723. break;
  724. }
  725. /* grow the output buffer if needed */
  726. if (globStringPos + 2 > globStringAlloc) {
  727. char *ptr;
  728. globStringAlloc += GLOB_STR_REALLOC_STEP;
  729. ptr = realloc(globString, globStringAlloc);
  730. if (!ptr) {
  731. /* out of memory */
  732. free(globString);
  733. return NULL;
  734. }
  735. globString = ptr;
  736. }
  737. /* copy a single character */
  738. globString[globStringPos++] = **ppos;
  739. }
  740. /* premature end of input */
  741. message(MESS_ERROR, "%s:%d missing '{' after log files definition\n", configFile, lineNum);
  742. free(globString);
  743. return NULL;
  744. }
  745. static int globerr(const char *pathname, int theerr)
  746. {
  747. (void) pathname;
  748. /* prevent glob() from being aborted in certain cases */
  749. switch (theerr) {
  750. case ENOTDIR:
  751. /* non-directory where directory was expected by the glob */
  752. return 0;
  753. case ENOENT:
  754. /* most likely symlink with non-existent target */
  755. return 0;
  756. default:
  757. break;
  758. }
  759. glob_errno = theerr;
  760. /* We want the glob operation to abort on error, so return 1 */
  761. return 1;
  762. }
  763. #define freeLogItem(what) \
  764. do { \
  765. free(newlog->what); \
  766. newlog->what = NULL; \
  767. } while (0);
  768. #define RAISE_ERROR() \
  769. if (newlog != defConfig) { \
  770. state = STATE_ERROR; \
  771. continue; \
  772. } else { \
  773. goto error; \
  774. }
  775. #define MAX_NESTING 16U
  776. static int readConfigFile(const char *configFile, struct logInfo *defConfig)
  777. {
  778. int fd;
  779. char *buf, *endtag, *key = NULL;
  780. off_t length;
  781. int lineNum = 1;
  782. unsigned long long multiplier;
  783. int i, k;
  784. char *scriptStart = NULL;
  785. char **scriptDest = NULL;
  786. struct logInfo *newlog = defConfig;
  787. char *start, *chptr;
  788. char *dirName;
  789. struct passwd *pw = NULL;
  790. int rc;
  791. struct stat sb, sb2;
  792. glob_t globResult;
  793. const char **argv;
  794. int argc, argNum;
  795. int flags;
  796. int state = STATE_DEFAULT;
  797. int logerror = 0;
  798. struct logInfo *log;
  799. /* to check if incompatible criteria are specified */
  800. int criterium_set = 0;
  801. static unsigned recursion_depth = 0U;
  802. char *globerr_msg = NULL;
  803. int in_config = 0;
  804. int rv;
  805. struct flock fd_lock = {
  806. .l_start = 0,
  807. .l_len = 0,
  808. .l_whence = SEEK_SET,
  809. .l_type = F_RDLCK
  810. };
  811. /* FIXME: createOwner and createGroup probably shouldn't be fixed
  812. length arrays -- of course, if we aren't run setuid it doesn't
  813. matter much */
  814. fd = open(configFile, O_RDONLY);
  815. if (fd < 0) {
  816. message(MESS_ERROR, "failed to open config file %s: %s\n",
  817. configFile, strerror(errno));
  818. return 1;
  819. }
  820. if ((flags = fcntl(fd, F_GETFD)) == -1) {
  821. message(MESS_ERROR, "Could not retrieve flags from file %s\n",
  822. configFile);
  823. close(fd);
  824. return 1;
  825. }
  826. flags |= FD_CLOEXEC;
  827. if (fcntl(fd, F_SETFD, flags) == -1) {
  828. message(MESS_ERROR, "Could not set flags on file %s\n",
  829. configFile);
  830. close(fd);
  831. return 1;
  832. }
  833. /* We don't want anybody to change the file while we parse it,
  834. * let's try to lock it for reading. */
  835. if (fcntl(fd, F_SETLK, &fd_lock) == -1) {
  836. message(MESS_ERROR, "Could not lock file %s for reading\n",
  837. configFile);
  838. }
  839. if (fstat(fd, &sb)) {
  840. message(MESS_ERROR, "fstat of %s failed: %s\n", configFile,
  841. strerror(errno));
  842. close(fd);
  843. return 1;
  844. }
  845. if (!S_ISREG(sb.st_mode)) {
  846. message(MESS_DEBUG,
  847. "Ignoring %s because it's not a regular file.\n",
  848. configFile);
  849. close(fd);
  850. return 0;
  851. }
  852. if (!(pw = getpwuid(getuid()))) {
  853. message(MESS_ERROR, "Logrotate UID is not in passwd file.\n");
  854. close(fd);
  855. return 1;
  856. }
  857. if (getuid() == ROOT_UID) {
  858. if ((sb.st_mode & 07533) != 0400) {
  859. message(MESS_DEBUG,
  860. "Potentially dangerous mode on %s: 0%o\n",
  861. configFile, (unsigned) (sb.st_mode & 07777));
  862. }
  863. if (sb.st_mode & 0022) {
  864. message(MESS_ERROR,
  865. "Ignoring %s because it is writable by group or others.\n",
  866. configFile);
  867. close(fd);
  868. return 0;
  869. }
  870. if ((pw = getpwuid(ROOT_UID)) == NULL) {
  871. message(MESS_ERROR,
  872. "Ignoring %s because there's no password entry for the owner.\n",
  873. configFile);
  874. close(fd);
  875. return 0;
  876. }
  877. if (sb.st_uid != ROOT_UID && (pw == NULL ||
  878. sb.st_uid != pw->pw_uid ||
  879. pw->pw_uid != ROOT_UID)) {
  880. message(MESS_ERROR,
  881. "Ignoring %s because the file owner is wrong (should be root or user with uid 0).\n",
  882. configFile);
  883. close(fd);
  884. return 0;
  885. }
  886. }
  887. length = sb.st_size;
  888. if (length > 0xffffff) {
  889. message(MESS_ERROR, "file %s too large, probably not a config file.\n",
  890. configFile);
  891. close(fd);
  892. return 1;
  893. }
  894. /* We can't mmap empty file... */
  895. if (length == 0) {
  896. message(MESS_DEBUG,
  897. "Ignoring %s because it's empty.\n",
  898. configFile);
  899. close(fd);
  900. return 0;
  901. }
  902. #ifdef MAP_POPULATE
  903. buf = mmap(NULL, (size_t) length, PROT_READ,
  904. MAP_PRIVATE | MAP_POPULATE, fd, (off_t) 0);
  905. #else /* MAP_POPULATE */
  906. buf = mmap(NULL, (size_t) length, PROT_READ,
  907. MAP_PRIVATE, fd, (off_t) 0);
  908. #endif /* MAP_POPULATE */
  909. if (buf == MAP_FAILED) {
  910. message(MESS_ERROR, "Error mapping config file %s: %s\n",
  911. configFile, strerror(errno));
  912. close(fd);
  913. return 1;
  914. }
  915. #ifdef HAVE_MADVISE
  916. #ifdef MADV_DONTFORK
  917. madvise(buf, (size_t)(length + 2),
  918. MADV_SEQUENTIAL | MADV_WILLNEED | MADV_DONTFORK);
  919. #else /* MADV_DONTFORK */
  920. madvise(buf, (size_t)(length + 2),
  921. MADV_SEQUENTIAL | MADV_WILLNEED);
  922. #endif /* MADV_DONTFORK */
  923. #endif /* HAVE_MADVISE */
  924. message(MESS_DEBUG, "reading config file %s\n", configFile);
  925. start = buf;
  926. for (start = buf; start - buf < length; start++) {
  927. switch (state) {
  928. case STATE_DEFAULT:
  929. if (isblank((unsigned char)*start))
  930. continue;
  931. /* Skip comment */
  932. if (*start == '#') {
  933. state = STATE_SKIP_LINE;
  934. continue;
  935. }
  936. if (isalpha((unsigned char)*start)) {
  937. free(key);
  938. key = isolateWord(&start, &buf, length);
  939. if (key == NULL)
  940. continue;
  941. if (!strcmp(key, "compress")) {
  942. newlog->flags |= LOG_FLAG_COMPRESS;
  943. } else if (!strcmp(key, "nocompress")) {
  944. newlog->flags &= ~LOG_FLAG_COMPRESS;
  945. } else if (!strcmp(key, "delaycompress")) {
  946. newlog->flags |= LOG_FLAG_DELAYCOMPRESS;
  947. } else if (!strcmp(key, "nodelaycompress")) {
  948. newlog->flags &= ~LOG_FLAG_DELAYCOMPRESS;
  949. } else if (!strcmp(key, "shred")) {
  950. newlog->flags |= LOG_FLAG_SHRED;
  951. } else if (!strcmp(key, "noshred")) {
  952. newlog->flags &= ~LOG_FLAG_SHRED;
  953. } else if (!strcmp(key, "sharedscripts")) {
  954. newlog->flags |= LOG_FLAG_SHAREDSCRIPTS;
  955. } else if (!strcmp(key, "nosharedscripts")) {
  956. newlog->flags &= ~LOG_FLAG_SHAREDSCRIPTS;
  957. } else if (!strcmp(key, "copytruncate")) {
  958. newlog->flags |= LOG_FLAG_COPYTRUNCATE;
  959. } else if (!strcmp(key, "nocopytruncate")) {
  960. newlog->flags &= ~LOG_FLAG_COPYTRUNCATE;
  961. } else if (!strcmp(key, "renamecopy")) {
  962. newlog->flags |= LOG_FLAG_TMPFILENAME;
  963. } else if (!strcmp(key, "norenamecopy")) {
  964. newlog->flags &= ~LOG_FLAG_TMPFILENAME;
  965. } else if (!strcmp(key, "copy")) {
  966. newlog->flags |= LOG_FLAG_COPY;
  967. } else if (!strcmp(key, "nocopy")) {
  968. newlog->flags &= ~LOG_FLAG_COPY;
  969. } else if (!strcmp(key, "ifempty")) {
  970. newlog->flags |= LOG_FLAG_IFEMPTY;
  971. } else if (!strcmp(key, "notifempty")) {
  972. newlog->flags &= ~LOG_FLAG_IFEMPTY;
  973. } else if (!strcmp(key, "dateext")) {
  974. newlog->flags |= LOG_FLAG_DATEEXT;
  975. } else if (!strcmp(key, "nodateext")) {
  976. newlog->flags &= ~LOG_FLAG_DATEEXT;
  977. } else if (!strcmp(key, "dateyesterday")) {
  978. newlog->flags |= LOG_FLAG_DATEYESTERDAY;
  979. } else if (!strcmp(key, "datehourago")) {
  980. newlog->flags |= LOG_FLAG_DATEHOURAGO;
  981. } else if (!strcmp(key, "dateformat")) {
  982. freeLogItem(dateformat);
  983. newlog->dateformat = isolateLine(&start, &buf, length);
  984. if (newlog->dateformat == NULL)
  985. continue;
  986. } else if (!strcmp(key, "noolddir")) {
  987. newlog->oldDir = NULL;
  988. } else if (!strcmp(key, "mailfirst")) {
  989. newlog->flags |= LOG_FLAG_MAILFIRST;
  990. } else if (!strcmp(key, "maillast")) {
  991. newlog->flags &= ~LOG_FLAG_MAILFIRST;
  992. } else if (!strcmp(key, "su")) {
  993. mode_t tmp_mode = NO_MODE;
  994. free(key);
  995. key = isolateLine(&start, &buf, length);
  996. if (key == NULL)
  997. continue;
  998. rv = readModeUidGid(configFile, lineNum, key, "su",
  999. &tmp_mode, &newlog->suUid,
  1000. &newlog->suGid);
  1001. if (rv == -1) {
  1002. RAISE_ERROR();
  1003. }
  1004. else if (tmp_mode != NO_MODE) {
  1005. message(MESS_ERROR, "%s:%d extra arguments for "
  1006. "su\n", configFile, lineNum);
  1007. RAISE_ERROR();
  1008. }
  1009. newlog->flags |= LOG_FLAG_SU;
  1010. } else if (!strcmp(key, "create")) {
  1011. free(key);
  1012. key = isolateLine(&start, &buf, length);
  1013. if (key == NULL)
  1014. continue;
  1015. rv = readModeUidGid(configFile, lineNum, key, "create",
  1016. &newlog->createMode, &newlog->createUid,
  1017. &newlog->createGid);
  1018. if (rv == -1) {
  1019. RAISE_ERROR();
  1020. }
  1021. newlog->flags |= LOG_FLAG_CREATE;
  1022. } else if (!strcmp(key, "createolddir")) {
  1023. free(key);
  1024. key = isolateLine(&start, &buf, length);
  1025. if (key == NULL)
  1026. continue;
  1027. rv = readModeUidGid(configFile, lineNum, key, "createolddir",
  1028. &newlog->olddirMode, &newlog->olddirUid,
  1029. &newlog->olddirGid);
  1030. if (rv == -1) {
  1031. RAISE_ERROR();
  1032. }
  1033. newlog->flags |= LOG_FLAG_OLDDIRCREATE;
  1034. } else if (!strcmp(key, "nocreateolddir")) {
  1035. newlog->flags &= ~LOG_FLAG_OLDDIRCREATE;
  1036. } else if (!strcmp(key, "nocreate")) {
  1037. newlog->flags &= ~LOG_FLAG_CREATE;
  1038. } else if (!strcmp(key, "size") || !strcmp(key, "minsize") ||
  1039. !strcmp(key, "maxsize")) {
  1040. unsigned long long size = 0;
  1041. char *opt = key;
  1042. key = isolateValue(configFile, lineNum, opt, &start, &buf, length);
  1043. if (key && key[0]) {
  1044. int l = strlen(key) - 1;
  1045. if (key[l] == 'k' || key[l] == 'K') {
  1046. key[l] = '\0';
  1047. multiplier = 1024;
  1048. } else if (key[l] == 'M') {
  1049. key[l] = '\0';
  1050. multiplier = 1024 * 1024;
  1051. } else if (key[l] == 'G') {
  1052. key[l] = '\0';
  1053. multiplier = 1024 * 1024 * 1024;
  1054. } else if (!isdigit((unsigned char)key[l])) {
  1055. free(opt);
  1056. message(MESS_ERROR, "%s:%d unknown unit '%c'\n",
  1057. configFile, lineNum, key[l]);
  1058. RAISE_ERROR();
  1059. } else {
  1060. multiplier = 1;
  1061. }
  1062. size = multiplier * strtoull(key, &chptr, 0);
  1063. if (*chptr) {
  1064. message(MESS_ERROR, "%s:%d bad size '%s'\n",
  1065. configFile, lineNum, key);
  1066. free(opt);
  1067. RAISE_ERROR();
  1068. }
  1069. if (!strncmp(opt, "size", 4)) {
  1070. set_criterium(&newlog->criterium, ROT_SIZE, &criterium_set);
  1071. newlog->threshold = size;
  1072. } else if (!strncmp(opt, "maxsize", 7)) {
  1073. newlog->maxsize = size;
  1074. } else {
  1075. newlog->minsize = size;
  1076. }
  1077. free(opt);
  1078. }
  1079. else {
  1080. free(opt);
  1081. continue;
  1082. }
  1083. } else if (!strcmp(key, "shredcycles")) {
  1084. free(key);
  1085. key = isolateValue(configFile, lineNum, "shred cycles",
  1086. &start, &buf, length);
  1087. if (key == NULL)
  1088. continue;
  1089. newlog->shred_cycles = strtoul(key, &chptr, 0);
  1090. if (*chptr || newlog->shred_cycles < 0) {
  1091. message(MESS_ERROR, "%s:%d bad shred cycles '%s'\n",
  1092. configFile, lineNum, key);
  1093. goto error;
  1094. }
  1095. } else if (!strcmp(key, "hourly")) {
  1096. set_criterium(&newlog->criterium, ROT_HOURLY, &criterium_set);
  1097. } else if (!strcmp(key, "daily")) {
  1098. set_criterium(&newlog->criterium, ROT_DAYS, &criterium_set);
  1099. newlog->threshold = 1;
  1100. } else if (!strcmp(key, "monthly")) {
  1101. set_criterium(&newlog->criterium, ROT_MONTHLY, &criterium_set);
  1102. } else if (!strcmp(key, "weekly")) {
  1103. unsigned weekday;
  1104. char tmp;
  1105. set_criterium(&newlog->criterium, ROT_WEEKLY, &criterium_set);
  1106. free(key);
  1107. key = isolateLine(&start, &buf, length);
  1108. if (key == NULL || key[0] == '\0') {
  1109. /* default to Sunday if no argument was given */
  1110. newlog->weekday = 0;
  1111. continue;
  1112. }
  1113. if (1 == sscanf(key, "%u%c", &weekday, &tmp) && weekday <= 7) {
  1114. /* use the selected weekday, 7 means "once per week" */
  1115. newlog->weekday = weekday;
  1116. continue;
  1117. }
  1118. message(MESS_ERROR, "%s:%d bad weekly directive '%s'\n",
  1119. configFile, lineNum, key);
  1120. goto error;
  1121. } else if (!strcmp(key, "yearly")) {
  1122. set_criterium(&newlog->criterium, ROT_YEARLY, &criterium_set);
  1123. } else if (!strcmp(key, "rotate")) {
  1124. free(key);
  1125. key = isolateValue(configFile, lineNum, "rotate count", &start,
  1126. &buf, length);
  1127. if (key == NULL)
  1128. continue;
  1129. newlog->rotateCount = strtol(key, &chptr, 0);
  1130. if (*chptr || newlog->rotateCount < -1) {
  1131. message(MESS_ERROR,
  1132. "%s:%d bad rotation count '%s'\n",
  1133. configFile, lineNum, key);
  1134. RAISE_ERROR();
  1135. }
  1136. } else if (!strcmp(key, "start")) {
  1137. free(key);
  1138. key = isolateValue(configFile, lineNum, "start count", &start,
  1139. &buf, length);
  1140. if (key == NULL)
  1141. continue;
  1142. newlog->logStart = strtoul(key, &chptr, 0);
  1143. if (*chptr || newlog->logStart < 0) {
  1144. message(MESS_ERROR, "%s:%d bad start count '%s'\n",
  1145. configFile, lineNum, key);
  1146. RAISE_ERROR();
  1147. }
  1148. } else if (!strcmp(key, "minage")) {
  1149. free(key);
  1150. key = isolateValue(configFile, lineNum, "minage count", &start,
  1151. &buf, length);
  1152. if (key == NULL)
  1153. continue;
  1154. newlog->rotateMinAge = strtoul(key, &chptr, 0);
  1155. if (*chptr || newlog->rotateMinAge < 0) {
  1156. message(MESS_ERROR, "%s:%d bad minimum age '%s'\n",
  1157. configFile, lineNum, start);
  1158. RAISE_ERROR();
  1159. }
  1160. } else if (!strcmp(key, "maxage")) {
  1161. free(key);
  1162. key = isolateValue(configFile, lineNum, "maxage count", &start,
  1163. &buf, length);
  1164. if (key == NULL)
  1165. continue;
  1166. newlog->rotateAge = strtoul(key, &chptr, 0);
  1167. if (*chptr || newlog->rotateAge < 0) {
  1168. message(MESS_ERROR, "%s:%d bad maximum age '%s'\n",
  1169. configFile, lineNum, start);
  1170. RAISE_ERROR();
  1171. }
  1172. } else if (!strcmp(key, "errors")) {
  1173. message(MESS_DEBUG,
  1174. "%s: %d: the errors directive is deprecated and no longer used.\n",
  1175. configFile, lineNum);
  1176. } else if (!strcmp(key, "mail")) {
  1177. freeLogItem(logAddress);
  1178. if (!(newlog->logAddress = readAddress(configFile, lineNum,
  1179. "mail", &start, &buf, length))) {
  1180. RAISE_ERROR();
  1181. }
  1182. else continue;
  1183. } else if (!strcmp(key, "nomail")) {
  1184. freeLogItem(logAddress);
  1185. } else if (!strcmp(key, "missingok")) {
  1186. newlog->flags |= LOG_FLAG_MISSINGOK;
  1187. } else if (!strcmp(key, "nomissingok")) {
  1188. newlog->flags &= ~LOG_FLAG_MISSINGOK;
  1189. } else if (!strcmp(key, "prerotate")) {
  1190. freeLogItem (pre);
  1191. scriptStart = start;
  1192. scriptDest = &newlog->pre;
  1193. state = STATE_LOAD_SCRIPT;
  1194. } else if (!strcmp(key, "firstaction")) {
  1195. freeLogItem (first);
  1196. scriptStart = start;
  1197. scriptDest = &newlog->first;
  1198. state = STATE_LOAD_SCRIPT;
  1199. } else if (!strcmp(key, "postrotate")) {
  1200. freeLogItem (post);
  1201. scriptStart = start;
  1202. scriptDest = &newlog->post;
  1203. state = STATE_LOAD_SCRIPT;
  1204. } else if (!strcmp(key, "lastaction")) {
  1205. freeLogItem (last);
  1206. scriptStart = start;
  1207. scriptDest = &newlog->last;
  1208. state = STATE_LOAD_SCRIPT;
  1209. } else if (!strcmp(key, "preremove")) {
  1210. freeLogItem (preremove);
  1211. scriptStart = start;
  1212. scriptDest = &newlog->preremove;
  1213. state = STATE_LOAD_SCRIPT;
  1214. } else if (!strcmp(key, "tabooext")) {
  1215. if (newlog != defConfig) {
  1216. message(MESS_ERROR,
  1217. "%s:%d tabooext may not appear inside "
  1218. "of log file definition\n", configFile,
  1219. lineNum);
  1220. state = STATE_ERROR;
  1221. continue;
  1222. }
  1223. free(key);
  1224. key = isolateValue(configFile, lineNum, "tabooext", &start,
  1225. &buf, length);
  1226. if (key == NULL)
  1227. continue;
  1228. endtag = key;
  1229. if (*endtag == '+') {
  1230. endtag++;
  1231. while (isspace((unsigned char)*endtag) && *endtag)
  1232. endtag++;
  1233. } else {
  1234. free_2d_array(tabooPatterns, tabooCount);
  1235. tabooCount = 0;
  1236. /* realloc of NULL is safe by definition */
  1237. tabooPatterns = NULL;
  1238. }
  1239. while (*endtag) {
  1240. int bytes;
  1241. char *pattern = NULL;
  1242. chptr = endtag;
  1243. while (!isspace((unsigned char)*chptr) && *chptr != ',' && *chptr)
  1244. chptr++;
  1245. /* accept only non-empty patterns to avoid exclusion of everything */
  1246. if (endtag < chptr) {
  1247. tabooPatterns = realloc(tabooPatterns, sizeof(*tabooPatterns) *
  1248. (tabooCount + 1));
  1249. bytes = asprintf(&pattern, "*%.*s", (int)(chptr - endtag), endtag);
  1250. /* should test for malloc() failure */
  1251. assert(bytes != -1);
  1252. tabooPatterns[tabooCount] = pattern;
  1253. tabooCount++;
  1254. }
  1255. endtag = chptr;
  1256. if (*endtag == ',')
  1257. endtag++;
  1258. while (*endtag && isspace((unsigned char)*endtag))
  1259. endtag++;
  1260. }
  1261. } else if (!strcmp(key, "taboopat")) {
  1262. if (newlog != defConfig) {
  1263. message(MESS_ERROR,
  1264. "%s:%d taboopat may not appear inside "
  1265. "of log file definition\n", configFile,
  1266. lineNum);
  1267. state = STATE_ERROR;
  1268. continue;
  1269. }
  1270. free(key);
  1271. key = isolateValue(configFile, lineNum, "taboopat", &start,
  1272. &buf, length);
  1273. if (key == NULL)
  1274. continue;
  1275. endtag = key;
  1276. if (*endtag == '+') {
  1277. endtag++;
  1278. while (isspace((unsigned char)*endtag) && *endtag)
  1279. endtag++;
  1280. } else {
  1281. free_2d_array(tabooPatterns, tabooCount);
  1282. tabooCount = 0;
  1283. /* realloc of NULL is safe by definition */
  1284. tabooPatterns = NULL;
  1285. }
  1286. while (*endtag) {
  1287. int bytes;
  1288. char *pattern = NULL;
  1289. chptr = endtag;
  1290. while (!isspace((unsigned char)*chptr) && *chptr != ',' && *chptr)
  1291. chptr++;
  1292. tabooPatterns = realloc(tabooPatterns, sizeof(*tabooPatterns) *
  1293. (tabooCount + 1));
  1294. bytes = asprintf(&pattern, "%.*s", (int)(chptr - endtag), endtag);
  1295. /* should test for malloc() failure */
  1296. assert(bytes != -1);
  1297. tabooPatterns[tabooCount] = pattern;
  1298. tabooCount++;
  1299. endtag = chptr;
  1300. if (*endtag == ',')
  1301. endtag++;
  1302. while (*endtag && isspace((unsigned char)*endtag))
  1303. endtag++;
  1304. }
  1305. } else if (!strcmp(key, "include")) {
  1306. free(key);
  1307. key = isolateValue(configFile, lineNum, "include", &start,
  1308. &buf, length);
  1309. if (key == NULL)
  1310. continue;
  1311. message(MESS_DEBUG, "including %s\n", key);
  1312. if (recursion_depth >= MAX_NESTING) {
  1313. message(MESS_ERROR, "%s:%d include nesting too deep\n",
  1314. configFile, lineNum);
  1315. logerror = 1;
  1316. continue;
  1317. }
  1318. ++recursion_depth;
  1319. rv = readConfigPath(key, newlog);
  1320. --recursion_depth;
  1321. if (rv) {
  1322. logerror = 1;
  1323. continue;
  1324. }
  1325. } else if (!strcmp(key, "olddir")) {
  1326. freeLogItem (oldDir);
  1327. if (!(newlog->oldDir = readPath(configFile, lineNum,
  1328. "olddir", &start, &buf, length))) {
  1329. RAISE_ERROR();
  1330. }
  1331. message(MESS_DEBUG, "olddir is now %s\n", newlog->oldDir);
  1332. } else if (!strcmp(key, "extension")) {
  1333. free(key);
  1334. key = isolateValue(configFile, lineNum, "extension name", &start,
  1335. &buf, length);
  1336. if (key == NULL)
  1337. continue;
  1338. freeLogItem (extension);
  1339. newlog->extension = key;
  1340. key = NULL;
  1341. message(MESS_DEBUG, "extension is now %s\n", newlog->extension);
  1342. } else if (!strcmp(key, "addextension")) {
  1343. free(key);
  1344. key = isolateValue(configFile, lineNum, "addextension name", &start,
  1345. &buf, length);
  1346. if (key == NULL)
  1347. continue;
  1348. freeLogItem (addextension);
  1349. newlog->addextension = key;
  1350. key = NULL;
  1351. message(MESS_DEBUG, "addextension is now %s\n",
  1352. newlog->addextension);
  1353. } else if (!strcmp(key, "compresscmd")) {
  1354. char *compresscmd_base;
  1355. freeLogItem (compress_prog);
  1356. if (!
  1357. (newlog->compress_prog =
  1358. readPath(configFile, lineNum, "compress", &start, &buf, length))) {
  1359. RAISE_ERROR();
  1360. }
  1361. message(MESS_DEBUG, "compress_prog is now %s\n",
  1362. newlog->compress_prog);
  1363. compresscmd_base = strdup(basename(newlog->compress_prog));
  1364. /* we check whether we changed the compress_cmd. In case we use the appropriate extension
  1365. as listed in compress_cmd_list */
  1366. for(i = 0; i < compress_cmd_list_size; i++) {
  1367. if (!strcmp(compress_cmd_list[i].cmd, compresscmd_base)) {
  1368. freeLogItem (compress_ext);
  1369. newlog->compress_ext = strdup((char *)compress_cmd_list[i].ext);
  1370. message(MESS_DEBUG, "compress_ext was changed to %s\n", newlog->compress_ext);
  1371. break;
  1372. }
  1373. }
  1374. free(compresscmd_base);
  1375. } else if (!strcmp(key, "uncompresscmd")) {
  1376. freeLogItem (uncompress_prog);
  1377. if (!
  1378. (newlog->uncompress_prog =
  1379. readPath(configFile, lineNum, "uncompress",
  1380. &start, &buf, length))) {
  1381. RAISE_ERROR();
  1382. }
  1383. message(MESS_DEBUG, "uncompress_prog is now %s\n",
  1384. newlog->uncompress_prog);
  1385. } else if (!strcmp(key, "compressoptions")) {
  1386. char *options;
  1387. if (newlog->compress_options_list) {
  1388. free(newlog->compress_options_list);
  1389. newlog->compress_options_list = NULL;
  1390. newlog->compress_options_count = 0;
  1391. }
  1392. if (!(options = isolateLine(&start, &buf, length))) {
  1393. RAISE_ERROR();
  1394. }
  1395. if (poptParseArgvString(options,
  1396. &newlog->compress_options_count,
  1397. &newlog->compress_options_list)) {
  1398. message(MESS_ERROR,
  1399. "%s:%d invalid compression options\n",
  1400. configFile, lineNum);
  1401. free(options);
  1402. RAISE_ERROR();
  1403. }
  1404. message(MESS_DEBUG, "compress_options is now %s\n",
  1405. options);
  1406. free(options);
  1407. } else if (!strcmp(key, "compressext")) {
  1408. freeLogItem (compress_ext);
  1409. if (!
  1410. (newlog->compress_ext =
  1411. readPath(configFile, lineNum, "compress-ext",
  1412. &start, &buf, length))) {
  1413. RAISE_ERROR();
  1414. }
  1415. message(MESS_DEBUG, "compress_ext is now %s\n",
  1416. newlog->compress_ext);
  1417. } else {
  1418. message(MESS_ERROR, "%s:%d unknown option '%s' "
  1419. "-- ignoring line\n", configFile, lineNum, key);
  1420. if (*start != '\n')
  1421. state = STATE_SKIP_LINE;
  1422. }
  1423. } else if (*start == '/' || *start == '"' || *start == '\''
  1424. #ifdef GLOB_TILDE
  1425. || *start == '~'
  1426. #endif
  1427. ) {
  1428. char *glob_string;
  1429. size_t glob_count;
  1430. in_config = 0;
  1431. if (newlog != defConfig) {
  1432. message(MESS_ERROR, "%s:%d unexpected log filename\n",
  1433. configFile, lineNum);
  1434. state = STATE_ERROR;
  1435. continue;
  1436. }
  1437. /* If no compression options were found in config file, set
  1438. default values */
  1439. if (!newlog->compress_prog)
  1440. newlog->compress_prog = strdup(COMPRESS_COMMAND);
  1441. if (!newlog->uncompress_prog)
  1442. newlog->uncompress_prog = strdup(UNCOMPRESS_COMMAND);
  1443. if (!newlog->compress_ext)
  1444. newlog->compress_ext = strdup(COMPRESS_EXT);
  1445. /* Allocate a new logInfo structure and insert it into the logs
  1446. queue, copying the actual values from defConfig */
  1447. if ((newlog = newLogInfo(defConfig)) == NULL)
  1448. goto error;
  1449. glob_string = parseGlobString(configFile, lineNum, buf, length, &start);
  1450. if (glob_string)
  1451. in_config = 1;
  1452. else
  1453. /* error already printed */
  1454. goto error;
  1455. if (poptParseArgvString(glob_string, &argc, &argv)) {
  1456. message(MESS_ERROR, "%s:%d error parsing filename\n",
  1457. configFile, lineNum);
  1458. free(glob_string);
  1459. goto error;
  1460. } else if (argc < 1) {
  1461. message(MESS_ERROR,
  1462. "%s:%d { expected after log file name(s)\n",
  1463. configFile, lineNum);
  1464. free(glob_string);
  1465. goto error;
  1466. }
  1467. newlog->files = NULL;
  1468. newlog->numFiles = 0;
  1469. for (argNum = 0; argNum < argc; argNum++) {
  1470. if (globerr_msg) {
  1471. free(globerr_msg);
  1472. globerr_msg = NULL;
  1473. }
  1474. rc = glob(argv[argNum], GLOB_NOCHECK
  1475. #ifdef GLOB_TILDE
  1476. | GLOB_TILDE
  1477. #endif
  1478. , globerr, &globResult);
  1479. if (rc == GLOB_ABORTED) {
  1480. if (newlog->flags & LOG_FLAG_MISSINGOK) {
  1481. continue;
  1482. }
  1483. /* We don't yet know whether this stanza has "missingok"
  1484. * set, so store the error message for later. */
  1485. rc = asprintf(&globerr_msg, "%s:%d glob failed for %s: %s\n",
  1486. configFile, lineNum, argv[argNum], strerror(glob_errno));
  1487. if (rc == -1)
  1488. globerr_msg = NULL;
  1489. globResult.gl_pathc = 0;
  1490. }
  1491. newlog->files =
  1492. realloc(newlog->files,
  1493. sizeof(*newlog->files) * (newlog->numFiles +
  1494. globResult.
  1495. gl_pathc));
  1496. for (glob_count = 0; glob_count < globResult.gl_pathc; glob_count++) {
  1497. /* if we glob directories we can get false matches */
  1498. if (!lstat(globResult.gl_pathv[glob_count], &sb) &&
  1499. S_ISDIR(sb.st_mode)) {
  1500. continue;
  1501. }
  1502. for (log = logs.tqh_first; log != NULL;
  1503. log = log->list.tqe_next) {
  1504. for (k = 0; k < log->numFiles; k++) {
  1505. if (!strcmp(log->files[k],
  1506. globResult.gl_pathv[glob_count])) {
  1507. message(MESS_ERROR,
  1508. "%s:%d duplicate log entry for %s\n",
  1509. configFile, lineNum,
  1510. globResult.gl_pathv[glob_count]);
  1511. logerror = 1;
  1512. goto duperror;
  1513. }
  1514. }
  1515. }
  1516. newlog->files[newlog->numFiles] =
  1517. strdup(globResult.gl_pathv[glob_count]);
  1518. newlog->numFiles++;
  1519. }
  1520. duperror:
  1521. globfree(&globResult);
  1522. }
  1523. newlog->pattern = glob_string;
  1524. free(argv);
  1525. } else if (*start == '}') {
  1526. if (newlog == defConfig) {
  1527. message(MESS_ERROR, "%s:%d unexpected }\n", configFile,
  1528. lineNum);
  1529. goto error;
  1530. }
  1531. if (!in_config) {
  1532. message(MESS_ERROR, "%s:%d unexpected } (missing previous '{')\n", configFile,
  1533. lineNum);
  1534. goto error;
  1535. }
  1536. in_config = 0;
  1537. if (globerr_msg) {
  1538. if (!(newlog->flags & LOG_FLAG_MISSINGOK))
  1539. message(MESS_ERROR, "%s", globerr_msg);
  1540. free(globerr_msg);
  1541. globerr_msg = NULL;
  1542. if (!(newlog->flags & LOG_FLAG_MISSINGOK))
  1543. goto error;
  1544. }
  1545. if (newlog->oldDir) {
  1546. for (i = 0; i < newlog->numFiles; i++) {
  1547. char *ld;
  1548. char *dirpath;
  1549. dirpath = strdup(newlog->files[i]);
  1550. dirName = dirname(dirpath);
  1551. if (stat(dirName, &sb2)) {
  1552. if (!(newlog->flags & LOG_FLAG_MISSINGOK)) {
  1553. message(MESS_ERROR,
  1554. "%s:%d error verifying log file "
  1555. "path %s: %s\n", configFile, lineNum,
  1556. dirName, strerror(errno));
  1557. free(dirpath);
  1558. goto error;
  1559. }
  1560. else {
  1561. message(MESS_DEBUG,
  1562. "%s:%d verifying log file "
  1563. "path failed %s: %s, log is probably missing, "
  1564. "but missingok is set, so this is not an error.\n",
  1565. configFile, lineNum,
  1566. dirName, strerror(errno));
  1567. free(dirpath);
  1568. continue;
  1569. }
  1570. }
  1571. ld = alloca(strlen(dirName) + strlen(newlog->oldDir) + 2);
  1572. sprintf(ld, "%s/%s", dirName, newlog->oldDir);
  1573. free(dirpath);
  1574. if (newlog->oldDir[0] != '/') {
  1575. dirName = ld;
  1576. }
  1577. else {
  1578. dirName = newlog->oldDir;
  1579. }
  1580. if (stat(dirName, &sb)) {
  1581. if (errno == ENOENT && newlog->flags & LOG_FLAG_OLDDIRCREATE) {
  1582. int ret;
  1583. if (newlog->flags & LOG_FLAG_SU) {
  1584. if (switch_user(newlog->suUid, newlog->suGid) != 0) {
  1585. goto error;
  1586. }
  1587. }
  1588. ret = mkpath(dirName, newlog->olddirMode,
  1589. newlog->olddirUid, newlog->olddirGid);
  1590. if (newlog->flags & LOG_FLAG_SU) {
  1591. if (switch_user_back() != 0) {
  1592. goto error;
  1593. }
  1594. }
  1595. if (ret) {
  1596. goto error;
  1597. }
  1598. }
  1599. else {
  1600. message(MESS_ERROR, "%s:%d error verifying olddir "
  1601. "path %s: %s\n", configFile, lineNum,
  1602. dirName, strerror(errno));
  1603. goto error;
  1604. }
  1605. }
  1606. if (sb.st_dev != sb2.st_dev
  1607. && !(newlog->flags & (LOG_FLAG_COPYTRUNCATE | LOG_FLAG_COPY | LOG_FLAG_TMPFILENAME))) {
  1608. message(MESS_ERROR,
  1609. "%s:%d olddir %s and log file %s "
  1610. "are on different devices\n", configFile,
  1611. lineNum, newlog->oldDir, newlog->files[i]);
  1612. goto error;
  1613. }
  1614. }
  1615. }
  1616. criterium_set = 0;
  1617. newlog = defConfig;
  1618. state = STATE_DEFINITION_END;
  1619. } else if (*start != '\n') {
  1620. message(MESS_ERROR, "%s:%d lines must begin with a keyword "
  1621. "or a filename (possibly in double quotes)\n",
  1622. configFile, lineNum);
  1623. state = STATE_SKIP_LINE;
  1624. }
  1625. break;
  1626. case STATE_SKIP_LINE:
  1627. case STATE_SKIP_LINE | STATE_SKIP_CONFIG:
  1628. if (*start == '\n')
  1629. state = state & STATE_SKIP_CONFIG ? STATE_SKIP_CONFIG : STATE_DEFAULT;
  1630. break;
  1631. case STATE_SKIP_LINE | STATE_LOAD_SCRIPT:
  1632. if (*start == '\n')
  1633. state = STATE_LOAD_SCRIPT;
  1634. break;
  1635. case STATE_SKIP_LINE | STATE_LOAD_SCRIPT | STATE_SKIP_CONFIG:
  1636. if (*start == '\n')
  1637. state = STATE_LOAD_SCRIPT | STATE_SKIP_CONFIG;
  1638. break;
  1639. case STATE_DEFINITION_END:
  1640. case STATE_DEFINITION_END | STATE_SKIP_CONFIG:
  1641. if (isblank((unsigned char)*start))
  1642. continue;
  1643. if (*start != '\n') {
  1644. message(MESS_ERROR, "%s:%d, unexpected text after }\n",
  1645. configFile, lineNum);
  1646. state = STATE_SKIP_LINE | (state & STATE_SKIP_CONFIG ? STATE_SKIP_CONFIG : 0);
  1647. }
  1648. else
  1649. state = state & STATE_SKIP_CONFIG ? STATE_SKIP_CONFIG : STATE_DEFAULT;
  1650. break;
  1651. case STATE_ERROR:
  1652. assert(newlog != defConfig);
  1653. message(MESS_ERROR, "found error in %s, skipping\n",
  1654. newlog->pattern ? newlog->pattern : "log config");
  1655. logerror = 1;
  1656. state = STATE_SKIP_CONFIG;
  1657. break;
  1658. case STATE_LOAD_SCRIPT:
  1659. case STATE_LOAD_SCRIPT | STATE_SKIP_CONFIG:
  1660. free(key);
  1661. key = isolateWord(&start, &buf, length);
  1662. if (key == NULL)
  1663. continue;
  1664. if (strcmp(key, "endscript") == 0) {
  1665. if (state & STATE_SKIP_CONFIG) {
  1666. state = STATE_SKIP_CONFIG;
  1667. }
  1668. else {
  1669. endtag = start - 9;
  1670. while (*endtag != '\n')
  1671. endtag--;
  1672. endtag++;
  1673. *scriptDest = malloc(endtag - scriptStart + 1);
  1674. strncpy(*scriptDest, scriptStart,
  1675. endtag - scriptStart);
  1676. (*scriptDest)[endtag - scriptStart] = '\0';
  1677. scriptDest = NULL;
  1678. scriptStart = NULL;
  1679. }
  1680. state = state & STATE_SKIP_CONFIG ? STATE_SKIP_CONFIG : STATE_DEFAULT;
  1681. }
  1682. else {
  1683. state = (*start == '\n' ? 0 : STATE_SKIP_LINE) |
  1684. STATE_LOAD_SCRIPT |
  1685. (state & STATE_SKIP_CONFIG ? STATE_SKIP_CONFIG : 0);
  1686. }
  1687. break;
  1688. case STATE_SKIP_CONFIG:
  1689. if (*start == '}') {
  1690. state = STATE_DEFAULT;
  1691. freeTailLogs(1);
  1692. newlog = defConfig;
  1693. }
  1694. else {
  1695. free(key);
  1696. key = isolateWord(&start, &buf, length);
  1697. if (key == NULL)
  1698. continue;
  1699. if (
  1700. (strcmp(key, "postrotate") == 0) ||
  1701. (strcmp(key, "prerotate") == 0) ||
  1702. (strcmp(key, "firstaction") == 0) ||
  1703. (strcmp(key, "lastaction") == 0) ||
  1704. (strcmp(key, "preremove") == 0)
  1705. ) {
  1706. state = STATE_LOAD_SCRIPT | STATE_SKIP_CONFIG;
  1707. }
  1708. else {
  1709. /* isolateWord moves the "start" pointer.
  1710. * If we have a line like
  1711. * rotate 5
  1712. * after isolateWord "start" points to "5" and it
  1713. * is OK to skip the line, but if we have a line
  1714. * like the following
  1715. * nocompress
  1716. * after isolateWord "start" points to "\n". In
  1717. * this case if we skip a line, we skip the next
  1718. * line, not the current "nocompress" one,
  1719. * because in the for cycle the "start"
  1720. * pointer is increased by one and, after this,
  1721. * "start" points to the beginning of the next line.
  1722. */
  1723. if (*start != '\n') {
  1724. state = STATE_SKIP_LINE | STATE_SKIP_CONFIG;
  1725. }
  1726. }
  1727. }
  1728. break;
  1729. default:
  1730. message(MESS_DEBUG,
  1731. "%s: %d: readConfigFile() unknown state\n",
  1732. configFile, lineNum);
  1733. }
  1734. if (*start == '\n') {
  1735. lineNum++;
  1736. }
  1737. }
  1738. if (scriptStart) {
  1739. message(MESS_ERROR,
  1740. "%s:prerotate, postrotate or preremove without endscript\n",
  1741. configFile);
  1742. goto error;
  1743. }
  1744. free(key);
  1745. munmap(buf, (size_t) length);
  1746. close(fd);
  1747. return logerror;
  1748. error:
  1749. /* free is a NULL-safe operation */
  1750. free(key);
  1751. munmap(buf, (size_t) length);
  1752. close(fd);
  1753. return 1;
  1754. }
  1755. /* vim: set et sw=4 ts=4: */