DirCreateMode was always zero. work-around now is to do a...
[rsyslog.git] / tools / omfile.c
1 /* omfile.c
2  * This is the implementation of the build-in file output module.
3  *
4  * Handles: eTypeCONSOLE, eTypeTTY, eTypeFILE, eTypePIPE
5  *
6  * NOTE: read comments in module-template.h to understand how this file
7  *       works!
8  *
9  * File begun on 2007-07-21 by RGerhards (extracted from syslogd.c)
10  * This file is under development and has not yet arrived at being fully
11  * self-contained and a real object. So far, it is mostly an excerpt
12  * of the "old" message code without any modifications. However, it
13  * helps to have things at the right place one we go to the meat of it.
14  *
15  * A large re-write of this file was done in June, 2009. The focus was
16  * to introduce many more features (like zipped writing), clean up the code
17  * and make it more reliable. In short, that rewrite tries to provide a new
18  * solid basis for the next three to five years to come. During it, bugs
19  * may have been introduced ;) -- rgerhards, 2009-06-04
20  *
21  * Copyright 2007-2009 Rainer Gerhards and Adiscon GmbH.
22  *
23  * This file is part of rsyslog.
24  *
25  * Rsyslog is free software: you can redistribute it and/or modify
26  * it under the terms of the GNU General Public License as published by
27  * the Free Software Foundation, either version 3 of the License, or
28  * (at your option) any later version.
29  *
30  * Rsyslog is distributed in the hope that it will be useful,
31  * but WITHOUT ANY WARRANTY; without even the implied warranty of
32  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
33  * GNU General Public License for more details.
34  *
35  * You should have received a copy of the GNU General Public License
36  * along with Rsyslog.  If not, see <http://www.gnu.org/licenses/>.
37  *
38  * A copy of the GPL can be found in the file "COPYING" in this distribution.
39  */
40 #include "config.h"
41 #include "rsyslog.h"
42 #include <stdio.h>
43 #include <stdarg.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <time.h>
47 #include <assert.h>
48 #include <errno.h>
49 #include <ctype.h>
50 #include <libgen.h>
51 #include <unistd.h>
52 #include <sys/file.h>
53
54 #ifdef OS_SOLARIS
55 #       include <fcntl.h>
56 #endif
57
58 #include "conf.h"
59 #include "syslogd-types.h"
60 #include "srUtils.h"
61 #include "template.h"
62 #include "outchannel.h"
63 #include "omfile.h"
64 #include "cfsysline.h"
65 #include "module-template.h"
66 #include "errmsg.h"
67 #include "stream.h"
68 #include "unicode-helper.h"
69 #include "atomic.h"
70
71 MODULE_TYPE_OUTPUT
72
73 /* internal structures
74  */
75 DEF_OMOD_STATIC_DATA
76 DEFobjCurrIf(errmsg)
77 DEFobjCurrIf(strm)
78
79 /* The following structure is a dynafile name cache entry.
80  */
81 struct s_dynaFileCacheEntry {
82         uchar *pName;           /* name currently open, if dynamic name */
83         strm_t  *pStrm;         /* our output stream */
84         time_t  lastUsed;       /* for LRU - last access */ // TODO: perforamcne change to counter (see other comment!) 
85 };
86 typedef struct s_dynaFileCacheEntry dynaFileCacheEntry;
87
88
89 #define IOBUF_DFLT_SIZE 1024    /* default size for io buffers */
90 #define FLUSH_INTRVL_DFLT 1     /* default buffer flush interval (in seconds) */
91
92 /* globals for default values */
93 static int iDynaFileCacheSize = 10; /* max cache for dynamic files */
94 static int fCreateMode = 0644; /* mode to use when creating files */
95 static int fDirCreateMode = 0700; /* mode to use when creating files */
96 static int      bFailOnChown;   /* fail if chown fails? */
97 static uid_t    fileUID;        /* UID to be used for newly created files */
98 static uid_t    fileGID;        /* GID to be used for newly created files */
99 static uid_t    dirUID;         /* UID to be used for newly created directories */
100 static uid_t    dirGID;         /* GID to be used for newly created directories */
101 static int      bCreateDirs;    /* auto-create directories for dynaFiles: 0 - no, 1 - yes */
102 static int      bEnableSync = 0;/* enable syncing of files (no dash in front of pathname in conf): 0 - no, 1 - yes */
103 static int      iZipLevel = 0;  /* zip compression mode (0..9 as usual) */
104 static bool     bFlushOnTXEnd = 1;/* flush write buffers when transaction has ended? */
105 static int      iIOBufSize = IOBUF_DFLT_SIZE;   /* size of an io buffer */
106 static int      iFlushInterval = FLUSH_INTRVL_DFLT;     /* how often flush the output buffer on inactivity? */
107 static uchar    *pszTplName = NULL; /* name of the default template to use */
108 /* end globals for default values */
109
110
111 typedef struct _instanceData {
112         uchar   f_fname[MAXFNAME];/* file or template name (display only) */
113         strm_t  *pStrm;         /* our output stream */
114         char    bDynamicName;   /* 0 - static name, 1 - dynamic name (with properties) */
115         int     fCreateMode;    /* file creation mode for open() */
116         int     fDirCreateMode; /* creation mode for mkdir() */
117         int     bCreateDirs;    /* auto-create directories? */
118         int     bSyncFile;      /* should the file by sync()'ed? 1- yes, 0- no */
119         uid_t   fileUID;        /* IDs for creation */
120         uid_t   dirUID;
121         gid_t   fileGID;
122         gid_t   dirGID;
123         int     bFailOnChown;   /* fail creation if chown fails? */
124         int     iCurrElt;       /* currently active cache element (-1 = none) */
125         int     iCurrCacheSize; /* currently cache size (1-based) */
126         int     iDynaFileCacheSize; /* size of file handle cache */
127         /* The cache is implemented as an array. An empty element is indicated
128          * by a NULL pointer. Memory is allocated as needed. The following
129          * pointer points to the overall structure.
130          */
131         dynaFileCacheEntry **dynCache;
132         off_t   iSizeLimit;             /* file size limit, 0 = no limit */
133         uchar   *pszSizeLimitCmd;       /* command to carry out when size limit is reached */
134         int     iZipLevel;              /* zip mode to use for this selector */
135         int     iIOBufSize;             /* size of associated io buffer */
136         int     iFlushInterval;         /* how fast flush buffer on inactivity? */
137         bool    bFlushOnTXEnd;          /* flush write buffers when transaction has ended? */
138 } instanceData;
139
140
141 BEGINisCompatibleWithFeature
142 CODESTARTisCompatibleWithFeature
143         if(eFeat == sFEATURERepeatedMsgReduction)
144                 iRet = RS_RET_OK;
145 ENDisCompatibleWithFeature
146
147
148 BEGINdbgPrintInstInfo
149 CODESTARTdbgPrintInstInfo
150         if(pData->bDynamicName) {
151                 dbgprintf("[dynamic]\n\ttemplate='%s'"
152                        "\tfile cache size=%d\n"
153                        "\tcreate directories: %s\n"
154                        "\tfile owner %d, group %d\n"
155                        "\tdirectory owner %d, group %d\n"
156                        "\tdir create mode 0%3.3o, file create mode 0%3.3o\n"
157                        "\tfail if owner/group can not be set: %s\n",
158                         pData->f_fname,
159                         pData->iDynaFileCacheSize,
160                         pData->bCreateDirs ? "yes" : "no",
161                         pData->fileUID, pData->fileGID,
162                         pData->dirUID, pData->dirGID,
163                         pData->fDirCreateMode, pData->fCreateMode,
164                         pData->bFailOnChown ? "yes" : "no"
165                         );
166         } else { /* regular file */
167                 dbgprintf("%s", pData->f_fname);
168                 if (pData->pStrm == NULL)
169                         dbgprintf(" (unused)");
170         }
171 ENDdbgPrintInstInfo
172
173
174 /* set the dynaFile cache size. Does some limit checking.
175  * rgerhards, 2007-07-31
176  */
177 rsRetVal setDynaFileCacheSize(void __attribute__((unused)) *pVal, int iNewVal)
178 {
179         DEFiRet;
180         uchar errMsg[128];      /* for dynamic error messages */
181
182         if(iNewVal < 1) {
183                 snprintf((char*) errMsg, sizeof(errMsg)/sizeof(uchar),
184                          "DynaFileCacheSize must be greater 0 (%d given), changed to 1.", iNewVal);
185                 errno = 0;
186                 errmsg.LogError(0, RS_RET_VAL_OUT_OF_RANGE, "%s", errMsg);
187                 iRet = RS_RET_VAL_OUT_OF_RANGE;
188                 iNewVal = 1;
189         } else if(iNewVal > 1000) {
190                 snprintf((char*) errMsg, sizeof(errMsg)/sizeof(uchar),
191                          "DynaFileCacheSize maximum is 1,000 (%d given), changed to 1,000.", iNewVal);
192                 errno = 0;
193                 errmsg.LogError(0, RS_RET_VAL_OUT_OF_RANGE, "%s", errMsg);
194                 iRet = RS_RET_VAL_OUT_OF_RANGE;
195                 iNewVal = 1000;
196         }
197
198         iDynaFileCacheSize = iNewVal;
199         DBGPRINTF("DynaFileCacheSize changed to %d.\n", iNewVal);
200
201         RETiRet;
202 }
203
204
205 /* Helper to cfline(). Parses a output channel name up until the first
206  * comma and then looks for the template specifier. Tries
207  * to find that template. Maps the output channel to the 
208  * proper filed structure settings. Everything is stored in the
209  * filed struct. Over time, the dependency on filed might be
210  * removed.
211  * rgerhards 2005-06-21
212  */
213 static rsRetVal cflineParseOutchannel(instanceData *pData, uchar* p, omodStringRequest_t *pOMSR, int iEntry, int iTplOpts)
214 {
215         DEFiRet;
216         size_t i;
217         struct outchannel *pOch;
218         char szBuf[128];        /* should be more than sufficient */
219
220         ++p; /* skip '$' */
221         i = 0;
222         /* get outchannel name */
223         while(*p && *p != ';' && *p != ' ' &&
224               i < sizeof(szBuf) / sizeof(char)) {
225               szBuf[i++] = *p++;
226         }
227         szBuf[i] = '\0';
228
229         /* got the name, now look up the channel... */
230         pOch = ochFind(szBuf, i);
231
232         if(pOch == NULL) {
233                 char errMsg[128];
234                 errno = 0;
235                 snprintf(errMsg, sizeof(errMsg)/sizeof(char),
236                          "outchannel '%s' not found - ignoring action line",
237                          szBuf);
238                 errmsg.LogError(0, RS_RET_NOT_FOUND, "%s", errMsg);
239                 ABORT_FINALIZE(RS_RET_NOT_FOUND);
240         }
241
242         /* check if there is a file name in the outchannel... */
243         if(pOch->pszFileTemplate == NULL) {
244                 char errMsg[128];
245                 errno = 0;
246                 snprintf(errMsg, sizeof(errMsg)/sizeof(char),
247                          "outchannel '%s' has no file name template - ignoring action line",
248                          szBuf);
249                 errmsg.LogError(0, RS_RET_ERR, "%s", errMsg);
250                 ABORT_FINALIZE(RS_RET_ERR);
251         }
252
253         /* OK, we finally got a correct template. So let's use it... */
254         ustrncpy(pData->f_fname, pOch->pszFileTemplate, MAXFNAME);
255         pData->iSizeLimit = pOch->uSizeLimit;
256         /* WARNING: It is dangerous "just" to pass the pointer. As we
257          * never rebuild the output channel description, this is acceptable here.
258          */
259         pData->pszSizeLimitCmd = pOch->cmdOnSizeLimit;
260
261         iRet = cflineParseTemplateName(&p, pOMSR, iEntry, iTplOpts,
262                                        (pszTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszTplName);
263
264 finalize_it:
265         RETiRet;
266 }
267
268
269 /* This function deletes an entry from the dynamic file name
270  * cache. A pointer to the cache must be passed in as well
271  * as the index of the to-be-deleted entry. This index may
272  * point to an unallocated entry, in whcih case the
273  * function immediately returns. Parameter bFreeEntry is 1
274  * if the entry should be d_free()ed and 0 if not.
275  */
276 static rsRetVal
277 dynaFileDelCacheEntry(dynaFileCacheEntry **pCache, int iEntry, int bFreeEntry)
278 {
279         DEFiRet;
280         ASSERT(pCache != NULL);
281
282         if(pCache[iEntry] == NULL)
283                 FINALIZE;
284
285         DBGPRINTF("Removed entry %d for file '%s' from dynaCache.\n", iEntry,
286                 pCache[iEntry]->pName == NULL ? UCHAR_CONSTANT("[OPEN FAILED]") : pCache[iEntry]->pName);
287         /* if the name is NULL, this is an improperly initilized entry which
288          * needs to be discarded. In this case, neither the file is to be closed
289          * not the name to be freed.
290          */
291         if(pCache[iEntry]->pName != NULL) {
292                 if(pCache[iEntry]->pStrm != NULL)
293                         strm.Destruct(&pCache[iEntry]->pStrm);
294                 d_free(pCache[iEntry]->pName);
295                 pCache[iEntry]->pName = NULL;
296         }
297
298         if(bFreeEntry) {
299                 d_free(pCache[iEntry]);
300                 pCache[iEntry] = NULL;
301         }
302
303 finalize_it:
304         RETiRet;
305 }
306
307
308 /* This function frees all dynamic file name cache entries and closes the
309  * relevant files. Part of Shutdown and HUP processing.
310  * rgerhards, 2008-10-23
311  */
312 static inline void
313 dynaFileFreeCacheEntries(instanceData *pData)
314 {
315         register int i;
316         ASSERT(pData != NULL);
317
318         BEGINfunc;
319         for(i = 0 ; i < pData->iCurrCacheSize ; ++i) {
320                 dynaFileDelCacheEntry(pData->dynCache, i, 1);
321         }
322         ENDfunc;
323 }
324
325
326 /* This function frees the dynamic file name cache.
327  */
328 static void dynaFileFreeCache(instanceData *pData)
329 {
330         ASSERT(pData != NULL);
331
332         BEGINfunc;
333         dynaFileFreeCacheEntries(pData);
334         if(pData->dynCache != NULL)
335                 d_free(pData->dynCache);
336         ENDfunc;
337 }
338
339
340 /* This is now shared code for all types of files. It simply prepares
341  * file access, which, among others, means the the file wil be opened
342  * and any directories in between will be created (based on config, of
343  * course). -- rgerhards, 2008-10-22
344  * changed to iRet interface - 2009-03-19
345  */
346 static rsRetVal
347 prepareFile(instanceData *pData, uchar *newFileName)
348 {
349         int fd;
350         DEFiRet;
351
352         if(access((char*)newFileName, F_OK) != 0) {
353                 /* file does not exist, create it (and eventually parent directories */
354                 fd = -1;
355                 if(pData->bCreateDirs) {
356                         /* We first need to create parent dirs if they are missing.
357                          * We do not report any errors here ourselfs but let the code
358                          * fall through to error handler below.
359                          */
360                         if(makeFileParentDirs(newFileName, ustrlen(newFileName),
361                              pData->fDirCreateMode, pData->dirUID,
362                              pData->dirGID, pData->bFailOnChown) != 0) {
363                                 ABORT_FINALIZE(RS_RET_ERR); /* we give up */
364                         }
365                 }
366                 /* no matter if we needed to create directories or not, we now try to create
367                  * the file. -- rgerhards, 2008-12-18 (based on patch from William Tisater)
368                  */
369                 fd = open((char*) newFileName, O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY|O_CLOEXEC,
370                                 pData->fCreateMode);
371                 if(fd != -1) {
372                         /* check and set uid/gid */
373                         if(pData->fileUID != (uid_t)-1 || pData->fileGID != (gid_t) -1) {
374                                 /* we need to set owner/group */
375                                 if(fchown(fd, pData->fileUID, pData->fileGID) != 0) {
376                                         if(pData->bFailOnChown) {
377                                                 int eSave = errno;
378                                                 close(fd);
379                                                 fd = -1;
380                                                 errno = eSave;
381                                         }
382                                         /* we will silently ignore the chown() failure
383                                          * if configured to do so.
384                                          */
385                                 }
386                         }
387                         close(fd); /* close again, as we need a stream further on */
388                 }
389         }
390
391         /* the copies below are clumpsy, but there is no way around given the
392          * anomalies in dirname() and basename() [they MODIFY the provided buffer...]
393          */
394         uchar szNameBuf[MAXFNAME];
395         uchar szDirName[MAXFNAME];
396         uchar szBaseName[MAXFNAME];
397         ustrncpy(szNameBuf, newFileName, MAXFNAME);
398         ustrncpy(szDirName, (uchar*)dirname((char*)szNameBuf), MAXFNAME);
399         ustrncpy(szNameBuf, newFileName, MAXFNAME);
400         ustrncpy(szBaseName, (uchar*)basename((char*)szNameBuf), MAXFNAME);
401
402         CHKiRet(strm.Construct(&pData->pStrm));
403         CHKiRet(strm.SetFName(pData->pStrm, szBaseName, ustrlen(szBaseName)));
404         CHKiRet(strm.SetDir(pData->pStrm, szDirName, ustrlen(szDirName)));
405         CHKiRet(strm.SetiZipLevel(pData->pStrm, pData->iZipLevel));
406         CHKiRet(strm.SetsIOBufSize(pData->pStrm, (size_t) pData->iIOBufSize));
407         CHKiRet(strm.SettOperationsMode(pData->pStrm, STREAMMODE_WRITE_APPEND));
408         CHKiRet(strm.SettOpenMode(pData->pStrm, fCreateMode));
409         CHKiRet(strm.SetbSync(pData->pStrm, pData->bSyncFile));
410         CHKiRet(strm.SetsType(pData->pStrm, STREAMTYPE_FILE_SINGLE));
411         CHKiRet(strm.SetiSizeLimit(pData->pStrm, pData->iSizeLimit));
412         /* set the flush interval only if we actually use it - otherwise it will activate
413          * async processing, which is a real performance waste if we do not do buffered
414          * writes! -- rgerhards, 2009-07-06
415          */
416         if(!pData->bFlushOnTXEnd)
417                 CHKiRet(strm.SetiFlushInterval(pData->pStrm, pData->iFlushInterval));
418         if(pData->pszSizeLimitCmd != NULL)
419                 CHKiRet(strm.SetpszSizeLimitCmd(pData->pStrm, ustrdup(pData->pszSizeLimitCmd)));
420         CHKiRet(strm.ConstructFinalize(pData->pStrm));
421         
422 finalize_it:
423         RETiRet;
424 }
425
426
427 /* This function handles dynamic file names. It checks if the
428  * requested file name is already open and, if not, does everything
429  * needed to switch to the it.
430  * Function returns 0 if all went well and non-zero otherwise.
431  * On successful return pData->fd must point to the correct file to
432  * be written.
433  * This is a helper to writeFile(). rgerhards, 2007-07-03
434  */
435 static inline rsRetVal
436 prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts)
437 {
438         time_t ttOldest; /* timestamp of oldest element */
439         int iOldest;
440         int i;
441         int iFirstFree;
442         rsRetVal localRet;
443         dynaFileCacheEntry **pCache;
444         DEFiRet;
445
446         ASSERT(pData != NULL);
447         ASSERT(newFileName != NULL);
448
449         pCache = pData->dynCache;
450
451         /* first check, if we still have the current file
452          * I *hope* this will be a performance enhancement.
453          */
454         if(   (pData->iCurrElt != -1)
455            && !ustrcmp(newFileName, pCache[pData->iCurrElt]->pName)) {
456                 /* great, we are all set */
457                 pCache[pData->iCurrElt]->lastUsed = time(NULL); /* update timestamp for LRU */ // TODO: optimize time call!
458                 // LRU needs only a strictly monotonically increasing counter, so such a one could do
459                 FINALIZE;
460         }
461
462         /* ok, no luck. Now let's search the table if we find a matching spot.
463          * While doing so, we also prepare for creation of a new one.
464          */
465         pData->iCurrElt = -1;   /* invalid current element pointer */
466         iFirstFree = -1; /* not yet found */
467         iOldest = 0; /* we assume the first element to be the oldest - that will change as we loop */
468         ttOldest = time(NULL) + 1; /* there must always be an older one */
469         for(i = 0 ; i < pData->iCurrCacheSize ; ++i) {
470                 if(pCache[i] == NULL) {
471                         if(iFirstFree == -1)
472                                 iFirstFree = i;
473                 } else { /* got an element, let's see if it matches */
474                         if(!ustrcmp(newFileName, pCache[i]->pName)) {
475                                 /* we found our element! */
476                                 pData->pStrm = pCache[i]->pStrm;
477                                 pData->iCurrElt = i;
478                                 pCache[i]->lastUsed = time(NULL); /* update timestamp for LRU */
479                                 FINALIZE;
480                         }
481                         /* did not find it - so lets keep track of the counters for LRU */
482                         if(pCache[i]->lastUsed < ttOldest) {
483                                 ttOldest = pCache[i]->lastUsed;
484                                 iOldest = i;
485                                 }
486                 }
487         }
488
489         /* we have not found an entry */
490         if(iFirstFree == -1 && (pData->iCurrCacheSize < pData->iDynaFileCacheSize)) {
491                 /* there is space left, so set it to that index */
492                 iFirstFree = pData->iCurrCacheSize++;
493         }
494
495         if(iFirstFree == -1) {
496                 dynaFileDelCacheEntry(pCache, iOldest, 0);
497                 iFirstFree = iOldest; /* this one *is* now free ;) */
498         } else {
499                 /* we need to allocate memory for the cache structure */
500                 CHKmalloc(pCache[iFirstFree] = (dynaFileCacheEntry*) calloc(1, sizeof(dynaFileCacheEntry)));
501         }
502
503         /* Ok, we finally can open the file */
504         localRet = prepareFile(pData, newFileName); /* ignore exact error, we check fd below */
505
506         /* file is either open now or an error state set */
507         if(pData->pStrm == NULL) {
508                 /* do not report anything if the message is an internally-generated
509                  * message. Otherwise, we could run into a never-ending loop. The bad
510                  * news is that we also lose errors on startup messages, but so it is.
511                  */
512                 if(iMsgOpts & INTERNAL_MSG) {
513                         DBGPRINTF("Could not open dynaFile, discarding message\n");
514                 } else {
515                         errmsg.LogError(0, NO_ERRCODE, "Could not open dynamic file '%s' - discarding message", newFileName);
516                 }
517                 dynaFileDelCacheEntry(pCache, iFirstFree, 1);
518                 ABORT_FINALIZE(localRet);
519         }
520
521         CHKmalloc(pCache[iFirstFree]->pName = ustrdup(newFileName));
522         pCache[iFirstFree]->pStrm = pData->pStrm;
523         pCache[iFirstFree]->lastUsed = time(NULL); // monotonically increasing value! TODO: performance
524         pData->iCurrElt = iFirstFree;
525         DBGPRINTF("Added new entry %d for file cache, file '%s'.\n", iFirstFree, newFileName);
526
527 finalize_it:
528         RETiRet;
529 }
530
531
532 /* do the actual write process. This function is to be called once we are ready for writing.
533  * It will do buffered writes and persist data only when the buffer is full. Note that we must
534  * be careful to detect when the file handle changed.
535  * rgerhards, 2009-06-03
536  */
537 static  rsRetVal
538 doWrite(instanceData *pData, uchar *pszBuf, int lenBuf)
539 {
540         DEFiRet;
541         ASSERT(pData != NULL);
542         ASSERT(pszBuf != NULL);
543
544 dbgprintf("doWrite, pData->pStrm %p, lenBuf %d\n", pData->pStrm, lenBuf);
545         if(pData->pStrm != NULL){
546                 CHKiRet(strm.Write(pData->pStrm, pszBuf, lenBuf));
547                 FINALIZE;
548         }
549
550 finalize_it:
551         RETiRet;
552 }
553
554
555 /* rgerhards 2004-11-11: write to a file output. This
556  * will be called for all outputs using file semantics,
557  * for example also for pipes.
558  */
559 static rsRetVal
560 writeFile(uchar **ppString, unsigned iMsgOpts, instanceData *pData)
561 {
562         DEFiRet;
563
564         ASSERT(pData != NULL);
565
566         /* first check if we have a dynamic file name and, if so,
567          * check if it still is ok or a new file needs to be created
568          */
569         if(pData->bDynamicName) {
570                 CHKiRet(prepareDynFile(pData, ppString[1], iMsgOpts));
571         } else { /* "regular", non-dynafile */
572                 if(pData->pStrm == NULL) {
573                         CHKiRet(prepareFile(pData, pData->f_fname));
574                 }
575         }
576
577         CHKiRet(doWrite(pData, ppString[0], strlen(CHAR_CONVERT(ppString[0]))));
578
579 finalize_it:
580         if(iRet != RS_RET_OK) {
581                 /* in v5, we shall return different states for message-cause failur (but only there!) */
582                 iRet = RS_RET_SUSPENDED;
583         }
584         RETiRet;
585 }
586
587
588 BEGINcreateInstance
589 CODESTARTcreateInstance
590         pData->pStrm = NULL;
591 ENDcreateInstance
592
593
594 BEGINfreeInstance
595 CODESTARTfreeInstance
596         if(pData->bDynamicName) {
597                 dynaFileFreeCache(pData);
598         } else if(pData->pStrm != NULL)
599                 strm.Destruct(&pData->pStrm);
600 ENDfreeInstance
601
602
603 BEGINtryResume
604 CODESTARTtryResume
605 ENDtryResume
606
607 BEGINdoAction
608 CODESTARTdoAction
609         DBGPRINTF("file to log to: %s\n", pData->f_fname);
610         CHKiRet(writeFile(ppString, iMsgOpts, pData));
611         if(pData->bFlushOnTXEnd) {
612                 /* TODO v5: do this in endTransaction only! */
613                 CHKiRet(strm.Flush(pData->pStrm));
614         }
615 finalize_it:
616 ENDdoAction
617
618
619 BEGINparseSelectorAct
620 CODESTARTparseSelectorAct
621 dbgprintf("XXX: dir create mode, enter omfile,  0%3.3o set in action\n", fDirCreateMode);
622         if(!(*p == '$' || *p == '?' || *p == '|' || *p == '/' || *p == '-'))
623                 ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
624
625         CHKiRet(createInstance(&pData));
626
627         if(*p == '-') {
628                 pData->bSyncFile = 0;
629                 p++;
630         } else {
631                 pData->bSyncFile = bEnableSync;
632         }
633         pData->iSizeLimit = 0; /* default value, use outchannels to configure! */
634
635         switch(*p) {
636         case '$':
637                 CODE_STD_STRING_REQUESTparseSelectorAct(1)
638                 /* rgerhards 2005-06-21: this is a special setting for output-channel
639                  * definitions. In the long term, this setting will probably replace
640                  * anything else, but for the time being we must co-exist with the
641                  * traditional mode lines.
642                  * rgerhards, 2007-07-24: output-channels will go away. We keep them
643                  * for compatibility reasons, but seems to have been a bad idea.
644                  */
645                 CHKiRet(cflineParseOutchannel(pData, p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS));
646                 pData->bDynamicName = 0;
647                 break;
648
649         case '?': /* This is much like a regular file handle, but we need to obtain
650                    * a template name. rgerhards, 2007-07-03
651                    */
652                 CODE_STD_STRING_REQUESTparseSelectorAct(2)
653                 ++p; /* eat '?' */
654                 CHKiRet(cflineParseFileName(p, (uchar*) pData->f_fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
655                                                (pszTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszTplName));
656                 /* "filename" is actually a template name, we need this as string 1. So let's add it
657                  * to the pOMSR. -- rgerhards, 2007-07-27
658                  */
659                 CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(pData->f_fname), OMSR_NO_RQD_TPL_OPTS));
660
661                 pData->bDynamicName = 1;
662                 pData->iCurrElt = -1;             /* no current element */
663                 /* we now allocate the cache table */
664                 CHKmalloc(pData->dynCache = (dynaFileCacheEntry**)
665                                 calloc(iDynaFileCacheSize, sizeof(dynaFileCacheEntry*)));
666                 break;
667
668         case '|':
669         case '/':
670                 CODE_STD_STRING_REQUESTparseSelectorAct(1)
671                 CHKiRet(cflineParseFileName(p, (uchar*) pData->f_fname, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
672                                                (pszTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : pszTplName));
673                 pData->bDynamicName = 0;
674                 break;
675         default:
676                 ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
677         }
678
679         /* freeze current paremeters for this action */
680         pData->iDynaFileCacheSize = iDynaFileCacheSize;
681         pData->fCreateMode = fCreateMode;
682         pData->fDirCreateMode = fDirCreateMode;
683 dbgprintf("XXX: dir create mode 0%3.3o set in action\n", fDirCreateMode);
684         pData->bCreateDirs = bCreateDirs;
685         pData->bFailOnChown = bFailOnChown;
686         pData->fileUID = fileUID;
687         pData->fileGID = fileGID;
688         pData->dirUID = dirUID;
689         pData->dirGID = dirGID;
690         pData->iZipLevel = iZipLevel;
691         pData->bFlushOnTXEnd = bFlushOnTXEnd;
692         pData->iIOBufSize = iIOBufSize;
693         pData->iFlushInterval = iFlushInterval;
694
695         if(pData->bDynamicName == 0) {
696                 /* try open and emit error message if not possible. At this stage, we ignore the
697                  * return value of prepareFile, this is taken care of in later steps.
698                  */
699                 prepareFile(pData, pData->f_fname);
700                         
701                 if(pData->pStrm == NULL) {
702                         DBGPRINTF("Error opening log file: %s\n", pData->f_fname);
703                         errmsg.LogError(0, RS_RET_NO_FILE_ACCESS, "Could no open output file '%s'", pData->f_fname);
704                 }
705         }
706 CODE_STD_FINALIZERparseSelectorAct
707 ENDparseSelectorAct
708
709
710 /* Reset config variables for this module to default values.
711  * rgerhards, 2007-07-17
712  */
713 static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
714 {
715         fileUID = -1;
716         fileGID = -1;
717         dirUID = -1;
718         dirGID = -1;
719         bFailOnChown = 1;
720         iDynaFileCacheSize = 10;
721         fCreateMode = 0644;
722         fDirCreateMode = 0700;
723         bCreateDirs = 1;
724         bEnableSync = 0;
725         iZipLevel = 0;
726         bFlushOnTXEnd = 1;
727         iIOBufSize = IOBUF_DFLT_SIZE;
728         iFlushInterval = FLUSH_INTRVL_DFLT;
729         if(pszTplName != NULL) {
730                 free(pszTplName);
731                 pszTplName = NULL;
732         }
733
734         return RS_RET_OK;
735 }
736
737
738 BEGINdoHUP
739 CODESTARTdoHUP
740         if(pData->bDynamicName) {
741                 dynaFileFreeCacheEntries(pData);
742                 pData->iCurrElt = -1; /* invalidate current element */
743         } else {
744                 if(pData->pStrm != NULL) {
745                         strm.Destruct(&pData->pStrm);
746                         pData->pStrm = NULL;
747                 }
748         }
749 ENDdoHUP
750
751
752 BEGINmodExit
753 CODESTARTmodExit
754         objRelease(errmsg, CORE_COMPONENT);
755         objRelease(strm, CORE_COMPONENT);
756         free(pszTplName);
757 ENDmodExit
758
759
760 BEGINqueryEtryPt
761 CODESTARTqueryEtryPt
762 CODEqueryEtryPt_STD_OMOD_QUERIES
763 CODEqueryEtryPt_doHUP
764 ENDqueryEtryPt
765
766
767 BEGINmodInit(File)
768 CODESTARTmodInit
769         fDirCreateMode = 0700;  /* for some reason, we need to do this write, else the variable always is 0 :S */
770
771         *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
772 CODEmodInit_QueryRegCFSLineHdlr
773         CHKiRet(objUse(errmsg, CORE_COMPONENT));
774         CHKiRet(objUse(strm, CORE_COMPONENT));
775         CHKiRet(omsdRegCFSLineHdlr((uchar *)"dynafilecachesize", 0, eCmdHdlrInt, (void*) setDynaFileCacheSize, NULL, STD_LOADABLE_MODULE_ID));
776         CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileziplevel", 0, eCmdHdlrInt, NULL, &iZipLevel, STD_LOADABLE_MODULE_ID));
777         CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileflushinterval", 0, eCmdHdlrInt, NULL, &iFlushInterval, STD_LOADABLE_MODULE_ID));
778         CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileflushontxend", 0, eCmdHdlrBinary, NULL, &bFlushOnTXEnd, STD_LOADABLE_MODULE_ID));
779         CHKiRet(omsdRegCFSLineHdlr((uchar *)"omfileiobuffersize", 0, eCmdHdlrSize, NULL, &iIOBufSize, STD_LOADABLE_MODULE_ID));
780         CHKiRet(omsdRegCFSLineHdlr((uchar *)"dirowner", 0, eCmdHdlrUID, NULL, &dirUID, STD_LOADABLE_MODULE_ID));
781         CHKiRet(omsdRegCFSLineHdlr((uchar *)"dirgroup", 0, eCmdHdlrGID, NULL, &dirGID, STD_LOADABLE_MODULE_ID));
782         CHKiRet(omsdRegCFSLineHdlr((uchar *)"fileowner", 0, eCmdHdlrUID, NULL, &fileUID, STD_LOADABLE_MODULE_ID));
783         CHKiRet(omsdRegCFSLineHdlr((uchar *)"filegroup", 0, eCmdHdlrGID, NULL, &fileGID, STD_LOADABLE_MODULE_ID));
784         CHKiRet(omsdRegCFSLineHdlr((uchar *)"dircreatemode", 0, eCmdHdlrFileCreateMode, NULL, &fDirCreateMode, STD_LOADABLE_MODULE_ID));
785         CHKiRet(omsdRegCFSLineHdlr((uchar *)"filecreatemode", 0, eCmdHdlrFileCreateMode, NULL, &fCreateMode, STD_LOADABLE_MODULE_ID));
786         CHKiRet(omsdRegCFSLineHdlr((uchar *)"createdirs", 0, eCmdHdlrBinary, NULL, &bCreateDirs, STD_LOADABLE_MODULE_ID));
787         CHKiRet(omsdRegCFSLineHdlr((uchar *)"failonchownfailure", 0, eCmdHdlrBinary, NULL, &bFailOnChown, STD_LOADABLE_MODULE_ID));
788         CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionfileenablesync", 0, eCmdHdlrBinary, NULL, &bEnableSync, STD_LOADABLE_MODULE_ID));
789         CHKiRet(regCfSysLineHdlr((uchar *)"actionfiledefaulttemplate", 0, eCmdHdlrGetWord, NULL, &pszTplName, NULL));
790         CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
791 ENDmodInit
792 /* vi:set ai:
793  */