summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--plugins/imklog/bsd.c18
-rw-r--r--plugins/imklog/imklog.c76
-rw-r--r--plugins/imklog/imklog.h7
-rw-r--r--plugins/imklog/ksym.c32
-rw-r--r--plugins/imklog/ksym_mod.c12
-rw-r--r--plugins/imklog/linux.c46
7 files changed, 112 insertions, 83 deletions
diff --git a/ChangeLog b/ChangeLog
index 5c75d60c..ff1cbd3f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,6 +3,10 @@ Version 3.17.1 (rgerhards), 2008-04-??
- implemented klogd functionality for BSD
- implemented high precision timestamps for the kernel log. Thanks to
Michale Biebl for pointing out that the kernel log did not have them.
+- provided ability to discard non-kernel messages if they are present
+ in the kernel log (seems to happen on BSD)
+- implemented $KLogInternalMsgFacility config directive
+- implemented $KLogPermitNonKernelFacility config directive
---------------------------------------------------------------------------
Version 3.17.0 (rgerhards), 2008-04-08
- added native ability to send mail messages
diff --git a/plugins/imklog/bsd.c b/plugins/imklog/bsd.c
index c12103f3..c5b79541 100644
--- a/plugins/imklog/bsd.c
+++ b/plugins/imklog/bsd.c
@@ -121,7 +121,7 @@ readklog(void)
line[i + len] = '\0';
} else {
if (i < 0 && errno != EINTR && errno != EAGAIN) {
- Syslog(LOG_ERR,
+ imklogLogIntMsg(LOG_ERR,
"imklog error %d reading kernel log - shutting down imklog",
errno);
fklog = -1;
@@ -131,18 +131,18 @@ readklog(void)
for (p = line; (q = strchr(p, '\n')) != NULL; p = q + 1) {
*q = '\0';
- Syslog(LOG_INFO, "%s", p);
+ Syslog(LOG_INFO, p);
}
len = strlen(p);
if (len >= MAXLINE - 1) {
- Syslog(LOG_INFO, "%s", p);
+ Syslog(LOG_INFO, p);
len = 0;
}
if (len > 0)
memmove(line, p, len + 1);
}
if (len > 0)
- Syslog(LOG_INFO, "%s", line);
+ Syslog(LOG_INFO, line);
}
@@ -169,3 +169,13 @@ rsRetVal klogLogKMsg(void)
readklog();
RETiRet;
}
+
+
+/* provide the (system-specific) default facility for internal messages
+ * rgerhards, 2008-04-14
+ */
+int
+klogFacilIntMsg(void)
+{
+ return LOG_SYSLOG;
+}
diff --git a/plugins/imklog/imklog.c b/plugins/imklog/imklog.c
index a5832658..e6bd2326 100644
--- a/plugins/imklog/imklog.c
+++ b/plugins/imklog/imklog.c
@@ -39,19 +39,14 @@
*/
#include "config.h"
#include "rsyslog.h"
-#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
-#include <signal.h>
#include <string.h>
-#include <pthread.h>
#include <stdarg.h>
-#include <paths.h>
#include <ctype.h>
#include "syslogd.h"
#include "cfsysline.h"
-#include "template.h"
#include "obj.h"
#include "msg.h"
#include "module-template.h"
@@ -70,6 +65,7 @@ int symbols_twice = 0;
int use_syscall = 0;
int symbol_lookup = 1;
int bPermitNonKernel = 0; /* permit logging of messages not having LOG_KERN facility */
+int iFacilIntMsg; /* the facility to use for internal messages (set by driver) */
/* TODO: configuration for the following directives must be implemented. It
* was not done yet because we either do not yet have a config handler for
* that type or I thought it was acceptable to push it to a later stage when
@@ -80,31 +76,19 @@ char *symfile = NULL;
int console_log_level = -1;
-/* Includes. */
-#include <unistd.h>
-#include <errno.h>
-#include <sys/fcntl.h>
-#include <sys/stat.h>
-
-#if HAVE_TIME_H
-# include <time.h>
-#endif
-
-#define __LIBRARY__
-#include <unistd.h>
-
-
/* enqueue the the kernel message into the message queue.
* The provided msg string is not freed - thus must be done
* by the caller.
* rgerhards, 2008-04-12
*/
-static rsRetVal enqMsg(uchar *msg, int iFacility, int iSeverity)
+static rsRetVal
+enqMsg(uchar *msg, uchar* pszTag, int iFacility, int iSeverity)
{
DEFiRet;
msg_t *pMsg;
assert(msg != NULL);
+ assert(pszTag != NULL);
CHKiRet(msgConstruct(&pMsg));
MsgSetFlowControlType(pMsg, eFLOWCTL_LIGHT_DELAY);
@@ -112,7 +96,7 @@ static rsRetVal enqMsg(uchar *msg, int iFacility, int iSeverity)
MsgSetRawMsg(pMsg, (char*)msg);
MsgSetMSG(pMsg, (char*)msg);
MsgSetHOSTNAME(pMsg, LocalHostName);
- MsgSetTAG(pMsg, "kernel:");
+ MsgSetTAG(pMsg, (char*)pszTag);
pMsg->iFacility = LOG_FAC(iFacility);
pMsg->iSeverity = LOG_PRI(iSeverity);
pMsg->bParseHOSTNAME = 0;
@@ -162,36 +146,47 @@ finalize_it:
}
-rsRetVal Syslog(int priority, char *fmt, ...) __attribute__((format(printf,2, 3)));
-rsRetVal Syslog(int priority, char *fmt, ...)
+/* log an imklog-internal message
+ * rgerhards, 2008-04-14
+ */
+rsRetVal imklogLogIntMsg(int priority, char *fmt, ...)
{
DEFiRet;
va_list ap;
uchar msgBuf[2048]; /* we use the same size as sysklogd to remain compatible */
uchar *pLogMsg;
+
+ va_start(ap, fmt);
+ vsnprintf((char*)msgBuf, sizeof(msgBuf) / sizeof(char), fmt, ap);
+ pLogMsg = msgBuf;
+ va_end(ap);
+
+ iRet = enqMsg((uchar*)pLogMsg, (uchar*) ((iFacilIntMsg == LOG_KERN) ? "kernel:" : "imklog:"),
+ iFacilIntMsg, LOG_PRI(priority));
+
+ RETiRet;
+}
+
+
+/* log a kernel message
+ * rgerhards, 2008-04-14
+ */
+rsRetVal Syslog(int priority, uchar *pMsg)
+{
+ DEFiRet;
rsRetVal localRet;
/* Output using syslog */
- if(!strcmp(fmt, "%s")) {
- va_start(ap, fmt);
- pLogMsg = va_arg(ap, uchar *);
- localRet = parsePRI(&pLogMsg, &priority);
- if(localRet != RS_RET_INVALID_PRI && localRet != RS_RET_OK)
- FINALIZE;
- /* if we don't get the pri, we use whatever we were supplied */
- va_end(ap);
- } else { /* TODO: I think we can remove this once we pull in the errmsg object -- rgerhards, 2008-04-14 */
- va_start(ap, fmt);
- vsnprintf((char*)msgBuf, sizeof(msgBuf) / sizeof(char), fmt, ap);
- pLogMsg = msgBuf;
- va_end(ap);
- }
+ localRet = parsePRI(&pMsg, &priority);
+ if(localRet != RS_RET_INVALID_PRI && localRet != RS_RET_OK)
+ FINALIZE;
+ /* if we don't get the pri, we use whatever we were supplied */
/* ignore non-kernel messages if not permitted */
if(bPermitNonKernel == 0 && LOG_FAC(priority) != LOG_KERN)
FINALIZE; /* silently ignore */
- iRet = enqMsg((uchar*)pLogMsg, LOG_FAC(priority), LOG_PRI(priority));
+ iRet = enqMsg((uchar*)pMsg, (uchar*) "kernel:", LOG_FAC(priority), LOG_PRI(priority));
finalize_it:
RETiRet;
@@ -246,6 +241,8 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
use_syscall = 0;
symfile = NULL;
symbol_lookup = 1;
+ bPermitNonKernel = 0;
+ iFacilIntMsg = klogFacilIntMsg();
return RS_RET_OK;
}
@@ -255,11 +252,14 @@ CODESTARTmodInit
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(datetime, CORE_COMPONENT));
+ iFacilIntMsg = klogFacilIntMsg();
+
CHKiRet(omsdRegCFSLineHdlr((uchar *)"debugprintkernelsymbols", 0, eCmdHdlrBinary, NULL, &dbgPrintSymbols, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogsymbollookup", 0, eCmdHdlrBinary, NULL, &symbol_lookup, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogsymbolstwice", 0, eCmdHdlrBinary, NULL, &symbols_twice, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogusesyscallinterface", 0, eCmdHdlrBinary, NULL, &use_syscall, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogpermitnonkernelfacility", 0, eCmdHdlrBinary, NULL, &bPermitNonKernel, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"kloginternalmsgfacility", 0, eCmdHdlrFacility, NULL, &iFacilIntMsg, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
/* vim:set ai:
diff --git a/plugins/imklog/imklog.h b/plugins/imklog/imklog.h
index 2fea879f..a37ecc9e 100644
--- a/plugins/imklog/imklog.h
+++ b/plugins/imklog/imklog.h
@@ -39,6 +39,7 @@
rsRetVal klogLogKMsg(void);
rsRetVal klogWillRun(void);
rsRetVal klogAfterRun(void);
+int klogFacilIntMsg(void);
/* the following data members may be accessed by the "drivers"
* I admit this is not the cleanest way to doing things, but I honestly
@@ -52,6 +53,10 @@ extern char *symfile;
extern int console_log_level;
extern int dbgPrintSymbols;
+/* the functions below may be called by the drivers */
+rsRetVal imklogLogIntMsg(int priority, char *fmt, ...) __attribute__((format(printf,2, 3)));
+rsRetVal Syslog(int priority, uchar *msg);
+
/* prototypes */
extern int InitKsyms(char *);
extern void DeinitKsyms(void);
@@ -59,8 +64,6 @@ extern int InitMsyms(void);
extern void DeinitMsyms(void);
extern char * ExpandKadds(char *, char *);
extern void SetParanoiaLevel(int);
-//TODO: remove? extern void vsyslog(int pri, const char *fmt, va_list ap);
-rsRetVal Syslog(int priority, char *fmt, ...) __attribute__((format(printf,2, 3)));
#endif /* #ifndef IMKLOG_H_INCLUDED */
/* vi:set ai:
diff --git a/plugins/imklog/ksym.c b/plugins/imklog/ksym.c
index 716ad926..1c2af124 100644
--- a/plugins/imklog/ksym.c
+++ b/plugins/imklog/ksym.c
@@ -185,18 +185,18 @@ extern int InitKsyms(char *mapfile)
if ( mapfile != (char *) 0 ) {
if ( (sym_file = fopen(mapfile, "r")) == (FILE *) 0 )
{
- Syslog(LOG_WARNING, "Cannot open map file: %s.", mapfile);
+ imklogLogIntMsg(LOG_WARNING, "Cannot open map file: %s.", mapfile);
return(0);
}
} else {
if ( (mapfile = FindSymbolFile()) == (char *) 0 ) {
- Syslog(LOG_WARNING, "Cannot find map file.");
+ imklogLogIntMsg(LOG_WARNING, "Cannot find map file.");
dbgprintf("Cannot find map file.\n");
return(0);
}
if ( (sym_file = fopen(mapfile, "r")) == (FILE *) 0 ) {
- Syslog(LOG_WARNING, "Cannot open map file.");
+ imklogLogIntMsg(LOG_WARNING, "Cannot open map file.");
dbgprintf("Cannot open map file.\n");
return(0);
}
@@ -213,7 +213,7 @@ extern int InitKsyms(char *mapfile)
*/
while ( !feof(sym_file) ) {
if ( fscanf(sym_file, "%lx %c %s\n", &address, &type, sym) != 3 ) {
- Syslog(LOG_ERR, "Error in symbol table input (#1).");
+ imklogLogIntMsg(LOG_ERR, "Error in symbol table input (#1).");
fclose(sym_file);
return(0);
}
@@ -221,7 +221,7 @@ extern int InitKsyms(char *mapfile)
dbgprintf("Address: %lx, Type: %c, Symbol: %s\n", address, type, sym);
if ( AddSymbol(address, sym) == 0 ) {
- Syslog(LOG_ERR, "Error adding symbol - %s.", sym);
+ imklogLogIntMsg(LOG_ERR, "Error adding symbol - %s.", sym);
fclose(sym_file);
return(0);
}
@@ -231,19 +231,19 @@ extern int InitKsyms(char *mapfile)
}
- Syslog(LOG_INFO, "Loaded %d symbols from %s.", num_syms, mapfile);
+ imklogLogIntMsg(LOG_INFO, "Loaded %d symbols from %s.", num_syms, mapfile);
switch(version) {
case -1:
- Syslog(LOG_WARNING, "Symbols do not match kernel version.");
+ imklogLogIntMsg(LOG_WARNING, "Symbols do not match kernel version.");
num_syms = 0;
break;
case 0:
- Syslog(LOG_WARNING, "Cannot verify that symbols match kernel version.");
+ imklogLogIntMsg(LOG_WARNING, "Cannot verify that symbols match kernel version.");
break;
case 1:
- Syslog(LOG_INFO, "Symbols match kernel version %s.", vstring);
+ imklogLogIntMsg(LOG_INFO, "Symbols match kernel version %s.", vstring);
break;
}
@@ -301,7 +301,7 @@ static char *FindSymbolFile(void)
auto FILE *sym_file = (FILE *) 0;
if ( uname(&utsname) < 0 ) {
- Syslog(LOG_ERR, "Cannot get kernel version information.");
+ imklogLogIntMsg(LOG_ERR, "Cannot get kernel version information.");
return(0);
}
@@ -410,13 +410,13 @@ static int CheckVersion(char *version)
* version level.
*/
if ( uname(&utsname) < 0 ) {
- Syslog(LOG_ERR, "Cannot get kernel version information.");
+ imklogLogIntMsg(LOG_ERR, "Cannot get kernel version information.");
return(0);
}
dbgprintf("Comparing kernel %s with symbol table %s.\n", utsname.release, vstring);
if ( sscanf (utsname.release, "%d.%d.%d", &major, &minor, &patch) < 3 ) {
- Syslog(LOG_ERR, "Kernel send bogus release string `%s'.", utsname.release);
+ imklogLogIntMsg(LOG_ERR, "Kernel send bogus release string `%s'.", utsname.release);
return(0);
}
@@ -470,12 +470,12 @@ static int CheckMapVersion(char *fname)
* now need to search this file and look for version
* information.
*/
- Syslog(LOG_INFO, "Inspecting %s", fname);
+ imklogLogIntMsg(LOG_INFO, "Inspecting %s", fname);
version = 0;
while ( !feof(sym_file) && (version == 0) ) {
if ( fscanf(sym_file, "%lx %c %s\n", &address, &type, sym) != 3 ) {
- Syslog(LOG_ERR, "Error in symbol table input (#2).");
+ imklogLogIntMsg(LOG_ERR, "Error in symbol table input (#2).");
fclose(sym_file);
return(0);
}
@@ -487,7 +487,7 @@ static int CheckMapVersion(char *fname)
switch ( version ) {
case -1:
- Syslog(LOG_ERR, "Symbol table has incorrect version number.\n");
+ imklogLogIntMsg(LOG_ERR, "Symbol table has incorrect version number.\n");
break;
case 0:
dbgprintf("No version information found.\n");
@@ -684,7 +684,7 @@ extern char *ExpandKadds(char *line, char *el)
*/
if ( i_am_paranoid &&
(strstr(line, "Oops:") != (char *) 0) && !InitMsyms() )
- Syslog(LOG_WARNING, "Cannot load kernel module symbols.\n");
+ imklogLogIntMsg(LOG_WARNING, "Cannot load kernel module symbols.\n");
/*
diff --git a/plugins/imklog/ksym_mod.c b/plugins/imklog/ksym_mod.c
index 11535a5f..bef810b4 100644
--- a/plugins/imklog/ksym_mod.c
+++ b/plugins/imklog/ksym_mod.c
@@ -158,10 +158,10 @@ extern int InitMsyms(void)
if ( ksyms == NULL ) {
if ( errno == ENOENT )
- Syslog(LOG_INFO, "No module symbols loaded - "
+ imklogLogIntMsg(LOG_INFO, "No module symbols loaded - "
"kernel modules not enabled.\n");
else
- Syslog(LOG_ERR, "Error loading kernel symbols " \
+ imklogLogIntMsg(LOG_ERR, "Error loading kernel symbols " \
"- %s\n", strerror(errno));
fclose(ksyms);
return(0);
@@ -201,9 +201,9 @@ extern int InitMsyms(void)
}
if ( rtn == 0 )
- Syslog(LOG_INFO, "No module symbols loaded.");
+ imklogLogIntMsg(LOG_INFO, "No module symbols loaded.");
else
- Syslog(LOG_INFO, "Loaded %d %s from %d module%s", rtn, \
+ imklogLogIntMsg(LOG_INFO, "Loaded %d %s from %d module%s", rtn, \
(rtn == 1) ? "symbol" : "symbols", \
num_modules, (num_modules == 1) ? "." : "s.");
@@ -296,7 +296,7 @@ struct Module *AddModule(module)
if ( sym_array_modules == NULL )
{
- Syslog(LOG_WARNING, "Cannot allocate Module array.\n");
+ imklogLogIntMsg(LOG_WARNING, "Cannot allocate Module array.\n");
return NULL;
}
mp = sym_array_modules;
@@ -308,7 +308,7 @@ struct Module *AddModule(module)
if ( mp == NULL )
{
- Syslog(LOG_WARNING, "Cannot allocate Module array.\n");
+ imklogLogIntMsg(LOG_WARNING, "Cannot allocate Module array.\n");
return NULL;
}
diff --git a/plugins/imklog/linux.c b/plugins/imklog/linux.c
index dc669b15..a742a456 100644
--- a/plugins/imklog/linux.c
+++ b/plugins/imklog/linux.c
@@ -98,11 +98,11 @@ static void CloseLogSrc(void)
{
case kernel:
ksyslog(0, 0, 0);
- Syslog(LOG_INFO, "Kernel logging (ksyslog) stopped.");
+ imklogLogIntMsg(LOG_INFO, "Kernel logging (ksyslog) stopped.");
break;
case proc:
close(kmsg);
- Syslog(LOG_INFO, "Kernel logging (proc) stopped.");
+ imklogLogIntMsg(LOG_INFO, "Kernel logging (proc) stopped.");
break;
case none:
break;
@@ -127,7 +127,7 @@ static enum LOGSRC GetKernelLogSrc(void)
* issue an error message and simply shut-off console
* logging completely.
*/
- Syslog(LOG_WARNING, "Cannot set console log level - disabling "
+ imklogLogIntMsg(LOG_WARNING, "Cannot set console log level - disabling "
"console output.");
}
@@ -140,7 +140,7 @@ static enum LOGSRC GetKernelLogSrc(void)
{
/* Initialize kernel logging. */
ksyslog(1, NULL, 0);
- Syslog(LOG_INFO, "imklogd %s, log source = ksyslog "
+ imklogLogIntMsg(LOG_INFO, "imklogd %s, log source = ksyslog "
"started.", VERSION);
return(kernel);
}
@@ -154,7 +154,7 @@ static enum LOGSRC GetKernelLogSrc(void)
return(none);
}
- Syslog(LOG_INFO, "imklog %s, log source = %s started.", VERSION, _PATH_KLOG);
+ imklogLogIntMsg(LOG_INFO, "imklog %s, log source = %s started.", VERSION, _PATH_KLOG);
return(proc);
}
@@ -165,7 +165,7 @@ static enum LOGSRC GetKernelLogSrc(void)
*
* Returns the actual number of chars copied.
*/
-static int copyin( char *line, int space,
+static int copyin( uchar *line, int space,
const char *ptr, int len,
const char *delim )
{
@@ -209,13 +209,13 @@ static void LogLine(char *ptr, int len)
PARSING_SYMEND /* at ] */
};
- static char line_buff[LOG_LINE_LENGTH];
+ static uchar line_buff[LOG_LINE_LENGTH];
- static char *line =line_buff;
+ static uchar *line =line_buff;
static enum parse_state_enum parse_state = PARSING_TEXT;
- static int space = sizeof(line_buff)-1;
+ static int space = sizeof(line_buff)-1;
- static char *sym_start; /* points at the '<' of a symbol */
+ static uchar *sym_start; /* points at the '<' of a symbol */
auto int delta = 0; /* number of chars copied */
auto int symbols_expanded = 0; /* 1 if symbols were expanded */
@@ -235,7 +235,7 @@ static void LogLine(char *ptr, int len)
dbgprintf("Line buffer full:\n");
dbgprintf("\tLine: %s\n", line);
- Syslog( LOG_INFO, "%s", line_buff );
+ Syslog(LOG_INFO, line_buff);
line = line_buff;
space = sizeof(line_buff)-1;
parse_state = PARSING_TEXT;
@@ -248,7 +248,7 @@ static void LogLine(char *ptr, int len)
switch( parse_state )
{
case PARSING_TEXT:
- delta = copyin( line, space, ptr, len, "\n[" );
+ delta = copyin(line, space, ptr, len, "\n[" );
line += delta;
ptr += delta;
space -= delta;
@@ -275,7 +275,7 @@ static void LogLine(char *ptr, int len)
len -= 1;
*line = 0; /* force null terminator */
- Syslog( LOG_INFO, "%s", line_buff );
+ Syslog(LOG_INFO, line_buff);
line = line_buff;
space = sizeof(line_buff)-1;
if (symbols_twice) {
@@ -373,7 +373,7 @@ static void LogLine(char *ptr, int len)
auto char *symbol;
*(line-1) = 0; /* null terminate the address string */
- value = strtoul(sym_start+1, (char **) 0, 16);
+ value = strtoul((char*)(sym_start+1), (char **) 0, 16);
*(line-1) = '>'; /* put back delim */
if ( !symbol_lookup || (symbol = LookupSymbol(value, &sym)) == (char *)0 )
@@ -392,7 +392,8 @@ static void LogLine(char *ptr, int len)
break;
}
- delta = sprintf( sym_start, "%s+%d/%d]",
+ // TODO: sprintf!!!!
+ delta = sprintf( (char*) sym_start, "%s+%d/%d]",
symbol, sym.offset, sym.size );
space = sym_space + delta;
@@ -453,7 +454,7 @@ static void LogProcLine(void)
if ( (rdcnt = read(kmsg, log_buffer, sizeof(log_buffer)-1)) < 0 ) {
if ( errno == EINTR )
return;
- Syslog(LOG_ERR, "Cannot read proc file system: %d - %s.", errno, strerror(errno));
+ imklogLogIntMsg(LOG_ERR, "Cannot read proc file system: %d - %s.", errno, strerror(errno));
} else {
LogLine(log_buffer, rdcnt);
}
@@ -503,7 +504,7 @@ rsRetVal klogWillRun(void)
symbol_lookup = (InitKsyms(symfile) == 1);
symbol_lookup |= InitMsyms();
if (symbol_lookup == 0) {
- Syslog(LOG_WARNING, "cannot find any symbols, turning off symbol lookups\n");
+ imklogLogIntMsg(LOG_WARNING, "cannot find any symbols, turning off symbol lookups\n");
}
}
}
@@ -528,5 +529,16 @@ rsRetVal klogAfterRun(void)
RETiRet;
}
+
+/* provide the (system-specific) default facility for internal messages
+ * rgerhards, 2008-04-14
+ */
+int
+klogFacilIntMsg(void)
+{
+ return LOG_KERN;
+}
+
+
/* vi:set ai:
*/