mirror of
https://github.com/rsyslog/rsyslog.git
synced 2026-06-19 12:52:57 +02:00
670 lines
20 KiB
C
670 lines
20 KiB
C
/**
|
|
* @file omusrmsg.c
|
|
* @brief Implementation of the built-in user message output module.
|
|
*
|
|
* This module provides message delivery to logged-in users. Depending on
|
|
* configuration it can either send messages to specific user terminals or
|
|
* broadcast them using a "wall" style interface.
|
|
*
|
|
* NOTE: read comments in module-template.h to understand how this file works!
|
|
*
|
|
* File begun on 2007-07-20 by Rainer Gerhards (extracted from syslogd.c,
|
|
* which at the time of the fork from sysklogd was under BSD license).
|
|
*
|
|
* Copyright 2007-2026 Adiscon GmbH.
|
|
*
|
|
* This file is part of rsyslog.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
* -or-
|
|
* see COPYING.ASL20 in the source distribution
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#include "config.h"
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <signal.h>
|
|
#include <ctype.h>
|
|
#include <sys/param.h>
|
|
#ifdef HAVE_UTMPX_H
|
|
#include <utmpx.h>
|
|
#define STRUCTUTMP struct utmpx
|
|
#define UTNAME ut_user
|
|
#define setutent setutxent
|
|
#define getutent getutxent
|
|
#define endutent endutxent
|
|
#ifndef UT_LINESIZE
|
|
/* __UT_LINESIZE for glibc; _UTX_LINESIZE common on Solaris */
|
|
#ifdef __UT_LINESIZE
|
|
#define UT_LINESIZE __UT_LINESIZE
|
|
#elif defined(_UTX_LINESIZE)
|
|
#define UT_LINESIZE _UTX_LINESIZE
|
|
#else /* method of last resort */
|
|
#define UT_LINESIZE 32
|
|
#endif
|
|
#endif
|
|
#elif defined(HAVE_UTMP_H)
|
|
#include <utmp.h>
|
|
#define STRUCTUTMP struct utmp
|
|
#define UTNAME ut_name
|
|
#endif
|
|
#include <unistd.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#if HAVE_FCNTL_H
|
|
#include <fcntl.h>
|
|
#else
|
|
#include <sys/msgbuf.h>
|
|
#endif
|
|
#ifdef HAVE_PATHS_H
|
|
#include <paths.h>
|
|
#endif
|
|
#ifndef _PATH_UTMP
|
|
#define _PATH_UTMP "/var/run/utmp"
|
|
#endif
|
|
#ifdef HAVE_LIBSYSTEMD
|
|
#include <systemd/sd-daemon.h>
|
|
#include <systemd/sd-login.h>
|
|
#include <pwd.h>
|
|
#endif
|
|
#include "rsyslog.h"
|
|
#include "srUtils.h"
|
|
#include "stringbuf.h"
|
|
#include "syslogd-types.h"
|
|
#include "conf.h"
|
|
#include "omusrmsg.h"
|
|
#include "module-template.h"
|
|
#include "errmsg.h"
|
|
#include "ratelimit.h"
|
|
|
|
|
|
/* portability: */
|
|
#ifndef _PATH_DEV
|
|
#define _PATH_DEV "/dev/"
|
|
#endif
|
|
|
|
#ifdef UT_NAMESIZE
|
|
#define UNAMESZ UT_NAMESIZE /* length of a login name */
|
|
#else
|
|
#define UNAMESZ 32 /* length of a login name, 32 seems current (2018) good bet */
|
|
#endif
|
|
#define MAXUNAMES 20 /* maximum number of user names */
|
|
|
|
#ifdef OS_SOLARIS
|
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
#endif
|
|
|
|
MODULE_TYPE_OUTPUT;
|
|
MODULE_TYPE_NOKEEP;
|
|
MODULE_CNFNAME("omusrmsg")
|
|
|
|
/* internal structures
|
|
*/
|
|
DEF_OMOD_STATIC_DATA;
|
|
|
|
typedef struct _instanceData {
|
|
int bIsWall; /* 1- is wall, 0 - individual users */
|
|
char uname[MAXUNAMES][UNAMESZ + 1];
|
|
uchar *tplName;
|
|
int ratelimitInterval;
|
|
int ratelimitBurst;
|
|
uchar *pszRatelimitName;
|
|
ratelimit_t *ratelimiter;
|
|
} instanceData;
|
|
|
|
typedef struct wrkrInstanceData {
|
|
instanceData *pData;
|
|
} wrkrInstanceData_t;
|
|
|
|
struct modConfData_s {
|
|
rsconf_t *pConf;
|
|
};
|
|
static modConfData_t *loadModConf = NULL;
|
|
static modConfData_t *runModConf = NULL;
|
|
|
|
typedef struct configSettings_s {
|
|
EMPTY_STRUCT
|
|
} configSettings_t;
|
|
static configSettings_t __attribute__((unused)) cs;
|
|
|
|
|
|
/* tables for interfacing with the v6 config system */
|
|
/* action (instance) parameters */
|
|
static struct cnfparamdescr actpdescr[] = {{"users", eCmdHdlrString, CNFPARAM_REQUIRED},
|
|
{"template", eCmdHdlrGetWord, 0},
|
|
{"ratelimit.interval", eCmdHdlrInt, 0},
|
|
{"ratelimit.burst", eCmdHdlrInt, 0},
|
|
{"ratelimit.name", eCmdHdlrString, 0}};
|
|
static struct cnfparamblk actpblk = {CNFPARAMBLK_VERSION, sizeof(actpdescr) / sizeof(struct cnfparamdescr), actpdescr};
|
|
|
|
BEGINinitConfVars /* (re)set config variables to default values */
|
|
CODESTARTinitConfVars;
|
|
ENDinitConfVars
|
|
|
|
|
|
BEGINbeginCnfLoad
|
|
CODESTARTbeginCnfLoad;
|
|
loadModConf = pModConf;
|
|
pModConf->pConf = pConf;
|
|
ENDbeginCnfLoad
|
|
|
|
|
|
BEGINendCnfLoad
|
|
CODESTARTendCnfLoad;
|
|
loadModConf = NULL;
|
|
ENDendCnfLoad
|
|
|
|
|
|
BEGINcheckCnf
|
|
CODESTARTcheckCnf;
|
|
ENDcheckCnf
|
|
|
|
|
|
BEGINactivateCnf
|
|
CODESTARTactivateCnf;
|
|
runModConf = pModConf;
|
|
ENDactivateCnf
|
|
|
|
|
|
BEGINfreeCnf
|
|
CODESTARTfreeCnf;
|
|
ENDfreeCnf
|
|
|
|
|
|
BEGINcreateInstance
|
|
CODESTARTcreateInstance;
|
|
ENDcreateInstance
|
|
|
|
|
|
BEGINcreateWrkrInstance
|
|
CODESTARTcreateWrkrInstance;
|
|
ENDcreateWrkrInstance
|
|
|
|
|
|
BEGINisCompatibleWithFeature
|
|
CODESTARTisCompatibleWithFeature;
|
|
if (eFeat == sFEATURERepeatedMsgReduction) iRet = RS_RET_OK;
|
|
ENDisCompatibleWithFeature
|
|
|
|
|
|
BEGINfreeInstance
|
|
CODESTARTfreeInstance;
|
|
free(pData->tplName);
|
|
free(pData->pszRatelimitName);
|
|
if (pData->ratelimiter != NULL) {
|
|
ratelimitDestruct(pData->ratelimiter);
|
|
pData->ratelimiter = NULL;
|
|
}
|
|
ENDfreeInstance
|
|
|
|
|
|
BEGINfreeWrkrInstance
|
|
CODESTARTfreeWrkrInstance;
|
|
ENDfreeWrkrInstance
|
|
|
|
|
|
BEGINdbgPrintInstInfo
|
|
register int i;
|
|
CODESTARTdbgPrintInstInfo;
|
|
for (i = 0; i < MAXUNAMES && *pData->uname[i]; i++) dbgprintf("%s, ", pData->uname[i]);
|
|
ENDdbgPrintInstInfo
|
|
|
|
|
|
/**
|
|
* BSD setutent/getutent() replacement routines
|
|
* The following routines emulate setutent() and getutent() under
|
|
* BSD because they are not available there. We only emulate what we actually
|
|
* need! rgerhards 2005-03-18
|
|
*/
|
|
#ifdef OS_BSD
|
|
/* Since version 900007, FreeBSD has a POSIX compliant <utmpx.h> */
|
|
#if defined(__FreeBSD__) && (__FreeBSD_version >= 900007)
|
|
#ifndef setutent
|
|
#define setutent() setutxent()
|
|
#endif
|
|
#ifndef getutent
|
|
#define getutent() getutxent()
|
|
#endif
|
|
#ifndef endutent
|
|
#define endutent() endutxent()
|
|
#endif
|
|
#else
|
|
static FILE *BSD_uf = NULL;
|
|
void setutent(void) {
|
|
assert(BSD_uf == NULL);
|
|
if ((BSD_uf = fopen(_PATH_UTMP, "r")) == NULL) {
|
|
LogError(errno, NO_ERRCODE, "error opening utmp %s", _PATH_UTMP);
|
|
return;
|
|
}
|
|
}
|
|
|
|
STRUCTUTMP *getutent(void) {
|
|
static STRUCTUTMP st_utmp;
|
|
|
|
if (fread((char *)&st_utmp, sizeof(st_utmp), 1, BSD_uf) != 1) return NULL;
|
|
|
|
return (&st_utmp);
|
|
}
|
|
|
|
void endutent(void) {
|
|
fclose(BSD_uf);
|
|
BSD_uf = NULL;
|
|
}
|
|
#endif /* if defined(__FreeBSD__) */
|
|
#endif /* #ifdef OS_BSD */
|
|
|
|
|
|
/**
|
|
* Send a message to a specific terminal device.
|
|
*
|
|
* The function builds the full device path from the supplied tty name
|
|
* and writes the message using non-blocking I/O so that a blocked
|
|
* terminal does not stall the caller.
|
|
*
|
|
* @param tty terminal name (e.g. "pts/0")
|
|
* @param pMsg message text to send
|
|
*/
|
|
static void sendwallmsg(const char *tty, uchar *pMsg) {
|
|
uchar szErr[512];
|
|
int errnoSave;
|
|
char p[sizeof(_PATH_DEV) + UT_LINESIZE];
|
|
int ttyf;
|
|
struct stat statb;
|
|
int wrRet;
|
|
|
|
/* compute the device name */
|
|
RS_COPY_LITERAL(p, _PATH_DEV);
|
|
size_t base_len = strlen(p);
|
|
size_t avail = sizeof(p) - base_len - 1;
|
|
size_t ttylen = strnlen(tty, avail);
|
|
memcpy(p + base_len, tty, ttylen);
|
|
p[base_len + ttylen] = '\0';
|
|
|
|
/* we must be careful when writing to the terminal. A terminal may block
|
|
* (for example, a user has pressed <ctl>-s). In that case, we can not
|
|
* wait indefinitely. So we need to use non-blocking I/O. In case we would
|
|
* block, we simply do not send the message, because that's the best we can
|
|
* do. -- rgerhards, 2008-07-04
|
|
*/
|
|
|
|
/* open the terminal */
|
|
if ((ttyf = open(p, O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
|
|
if (fstat(ttyf, &statb) == 0 && (statb.st_mode & S_IWRITE)) {
|
|
wrRet = write(ttyf, pMsg, strlen((char *)pMsg));
|
|
if (Debug && wrRet == -1) {
|
|
/* we record the state to the debug log */
|
|
errnoSave = errno;
|
|
rs_strerror_r(errno, (char *)szErr, sizeof(szErr));
|
|
dbgprintf("write to terminal '%s' failed with [%d]:%s\n", p, errnoSave, szErr);
|
|
}
|
|
}
|
|
close(ttyf);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Write a message to the world at large.
|
|
*
|
|
* The message can either be broadcast to all logged-in users or sent only
|
|
* to the set configured in @a pData. The implementation avoids forking a
|
|
* helper process and instead performs delivery on the caller's thread.
|
|
*
|
|
* @param[in] pMsg message text to deliver
|
|
* @param[in] pData per-action configuration
|
|
*
|
|
* @return rsRetVal
|
|
*/
|
|
static rsRetVal wallmsg(uchar *pMsg, instanceData *pData) {
|
|
register int i;
|
|
STRUCTUTMP ut;
|
|
STRUCTUTMP *uptr;
|
|
DEFiRet;
|
|
|
|
assert(pMsg != NULL);
|
|
|
|
#ifdef HAVE_LIBSYSTEMD
|
|
if (sd_booted() > 0) {
|
|
register int j;
|
|
int sdRet;
|
|
char **sessions_list = NULL;
|
|
int sessions = sd_get_sessions(&sessions_list);
|
|
|
|
for (j = 0; j < sessions; j++) {
|
|
uchar szErr[512];
|
|
char *tty;
|
|
const char *user = NULL;
|
|
uid_t uid;
|
|
struct passwd *pws;
|
|
|
|
sdRet = sd_session_get_uid(sessions_list[j], &uid);
|
|
if (sdRet >= 0) {
|
|
pws = getpwuid(uid);
|
|
|
|
if (pws == NULL) {
|
|
LogError(0, NO_ERRCODE, "failed to get passwd for userid '%d'\n", uid);
|
|
free(sessions_list[j]);
|
|
continue;
|
|
}
|
|
|
|
user = pws->pw_name; /* DO NOT FREE, OS/LIB internal memory! */
|
|
|
|
if (user == NULL) {
|
|
LogError(0, NO_ERRCODE, "failed to get username for userid '%d'\n", uid);
|
|
free(sessions_list[j]);
|
|
continue;
|
|
}
|
|
} else {
|
|
/* we record the state to the debug log */
|
|
rs_strerror_r(-sdRet, (char *)szErr, sizeof(szErr));
|
|
LogError(0, NO_ERRCODE, "get userid for session '%s' failed with [%d]:%s\n", sessions_list[j], -sdRet,
|
|
szErr);
|
|
free(sessions_list[j]);
|
|
continue; /* try next session */
|
|
}
|
|
/* should we send the message to this user? */
|
|
if (pData->bIsWall == 0) {
|
|
for (i = 0; i < MAXUNAMES; i++) {
|
|
if (!pData->uname[i][0]) {
|
|
i = MAXUNAMES;
|
|
break;
|
|
}
|
|
if (strncmp(pData->uname[i], user, UNAMESZ) == 0) break;
|
|
}
|
|
if (i == MAXUNAMES) { /* user not found? */
|
|
free(sessions_list[j]);
|
|
continue; /* on to next user! */
|
|
}
|
|
}
|
|
if ((sdRet = sd_session_get_tty(sessions_list[j], &tty)) < 0) {
|
|
/* we record the state to the debug log */
|
|
rs_strerror_r(-sdRet, (char *)szErr, sizeof(szErr));
|
|
LogError(0, NO_ERRCODE, "get tty for session '%s' failed with [%d]:%s\n", sessions_list[j], -sdRet,
|
|
szErr);
|
|
free(sessions_list[j]);
|
|
continue; /* try next session */
|
|
}
|
|
|
|
sendwallmsg(tty, pMsg);
|
|
|
|
free(tty);
|
|
free(sessions_list[j]);
|
|
}
|
|
free(sessions_list);
|
|
} else {
|
|
#endif
|
|
|
|
/* open the user login file */
|
|
setutent();
|
|
|
|
/* scan the user login file */
|
|
while ((uptr = getutent())) {
|
|
memcpy(&ut, uptr, sizeof(ut));
|
|
/* is this slot used? */
|
|
if (ut.UTNAME[0] == '\0') continue;
|
|
#ifndef OS_BSD
|
|
if (ut.ut_type != USER_PROCESS) continue;
|
|
#endif
|
|
if (!(memcmp(ut.UTNAME, "LOGIN", 6))) /* paranoia */
|
|
continue;
|
|
|
|
/* should we send the message to this user? */
|
|
if (pData->bIsWall == 0) {
|
|
for (i = 0; i < MAXUNAMES; i++) {
|
|
if (!pData->uname[i][0]) {
|
|
i = MAXUNAMES;
|
|
break;
|
|
}
|
|
if (strncmp(pData->uname[i], ut.UTNAME, UNAMESZ) == 0) break;
|
|
}
|
|
if (i == MAXUNAMES) /* user not found? */
|
|
continue; /* on to next user! */
|
|
}
|
|
|
|
sendwallmsg(ut.ut_line, pMsg);
|
|
}
|
|
|
|
/* close the user login file */
|
|
endutent();
|
|
#ifdef HAVE_LIBSYSTEMD
|
|
}
|
|
#endif
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
BEGINtryResume
|
|
CODESTARTtryResume;
|
|
ENDtryResume
|
|
|
|
BEGINdoAction
|
|
CODESTARTdoAction;
|
|
dbgprintf("\n");
|
|
if (pWrkrData->pData->ratelimiter == NULL ||
|
|
ratelimitMsgCount(pWrkrData->pData->ratelimiter, 0, "omusrmsg") != RS_RET_DISCARDMSG) {
|
|
iRet = wallmsg(ppString[0], pWrkrData->pData);
|
|
}
|
|
ENDdoAction
|
|
|
|
|
|
static void
|
|
/**
|
|
* Parse a comma separated list of usernames.
|
|
*
|
|
* The user list is copied into the instance data. Whitespace is skipped
|
|
* and overly long names are reported via the error log. If more than
|
|
* MAXUNAMES users are specified, excess entries are ignored.
|
|
*
|
|
* @param[in] pData Instance configuration to populate
|
|
* @param[in] usrs comma separated list of users
|
|
*/
|
|
populateUsers(instanceData *pData, es_str_t *usrs)
|
|
{
|
|
int i;
|
|
int iDst;
|
|
es_size_t iUsr;
|
|
es_size_t len;
|
|
uchar *c;
|
|
|
|
len = es_strlen(usrs);
|
|
c = es_getBufAddr(usrs);
|
|
pData->bIsWall = 0; /* write to individual users */
|
|
iUsr = 0;
|
|
for (i = 0; i < MAXUNAMES && iUsr < len; ++i) {
|
|
for (iDst = 0; iDst < UNAMESZ && iUsr < len && c[iUsr] != ','; ++iDst, ++iUsr) {
|
|
pData->uname[i][iDst] = c[iUsr];
|
|
}
|
|
pData->uname[i][iDst] = '\0';
|
|
DBGPRINTF("omusrmsg: send to user '%s'\n", pData->uname[i]);
|
|
if (iUsr < len && c[iUsr] != ',') {
|
|
LogError(0, RS_RET_ERR,
|
|
"user name '%s...' too long - "
|
|
"ignored",
|
|
pData->uname[i]);
|
|
--i;
|
|
++iUsr;
|
|
while (iUsr < len && c[iUsr] != ',') ++iUsr; /* skip to next name */
|
|
} else if (iDst == 0) {
|
|
LogError(0, RS_RET_ERR,
|
|
"no user name given - "
|
|
"ignored");
|
|
--i;
|
|
++iUsr;
|
|
while (iUsr < len && c[iUsr] != ',') ++iUsr; /* skip to next name */
|
|
}
|
|
if (iUsr < len) {
|
|
++iUsr; /* skip "," */
|
|
while (iUsr < len && isspace(c[iUsr])) ++iUsr; /* skip whitespace */
|
|
}
|
|
}
|
|
if (i == MAXUNAMES && iUsr != len) {
|
|
LogError(0, RS_RET_ERR,
|
|
"omusrmsg supports only up to %d "
|
|
"user names in a single action - all others have been ignored",
|
|
MAXUNAMES);
|
|
}
|
|
}
|
|
|
|
|
|
static inline void setInstParamDefaults(instanceData *pData) {
|
|
pData->bIsWall = 0;
|
|
pData->tplName = NULL;
|
|
pData->ratelimiter = NULL;
|
|
pData->ratelimitInterval = -1;
|
|
pData->ratelimitBurst = -1;
|
|
pData->pszRatelimitName = NULL;
|
|
}
|
|
|
|
BEGINnewActInst
|
|
struct cnfparamvals *pvals;
|
|
int i;
|
|
CODESTARTnewActInst;
|
|
if ((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) {
|
|
ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
|
|
}
|
|
|
|
CHKiRet(createInstance(&pData));
|
|
setInstParamDefaults(pData);
|
|
|
|
CODE_STD_STRING_REQUESTnewActInst(1);
|
|
for (i = 0; i < actpblk.nParams; ++i) {
|
|
if (!pvals[i].bUsed) continue;
|
|
if (!strcmp(actpblk.descr[i].name, "users")) {
|
|
if (!es_strbufcmp(pvals[i].val.d.estr, (uchar *)"*", 1)) {
|
|
pData->bIsWall = 1;
|
|
} else {
|
|
populateUsers(pData, pvals[i].val.d.estr);
|
|
}
|
|
} else if (!strcmp(actpblk.descr[i].name, "template")) {
|
|
CHKmalloc(pData->tplName = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL));
|
|
} else if (!strcmp(actpblk.descr[i].name, "ratelimit.interval")) {
|
|
pData->ratelimitInterval = (int)pvals[i].val.d.n;
|
|
} else if (!strcmp(actpblk.descr[i].name, "ratelimit.burst")) {
|
|
pData->ratelimitBurst = (int)pvals[i].val.d.n;
|
|
} else if (!strcmp(actpblk.descr[i].name, "ratelimit.name")) {
|
|
CHKmalloc(pData->pszRatelimitName = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL));
|
|
} else {
|
|
dbgprintf(
|
|
"omusrmsg: program error, non-handled "
|
|
"param '%s'\n",
|
|
actpblk.descr[i].name);
|
|
}
|
|
}
|
|
|
|
if (pData->pszRatelimitName != NULL) {
|
|
if (pData->ratelimitInterval != -1 || pData->ratelimitBurst != -1) {
|
|
LogError(0, RS_RET_INVALID_PARAMS,
|
|
"omusrmsg: ratelimit.name is mutually exclusive with "
|
|
"ratelimit.interval and ratelimit.burst - using ratelimit.name");
|
|
}
|
|
pData->ratelimitInterval = 0;
|
|
pData->ratelimitBurst = 0;
|
|
} else {
|
|
if (pData->ratelimitInterval == -1) pData->ratelimitInterval = 0;
|
|
if (pData->ratelimitBurst == -1) pData->ratelimitBurst = 200;
|
|
}
|
|
|
|
if (pData->tplName == NULL) {
|
|
CHKiRet(OMSRsetEntry(*ppOMSR, 0, (uchar *)strdup(pData->bIsWall ? " WallFmt" : " StdUsrMsgFmt"),
|
|
OMSR_NO_RQD_TPL_OPTS));
|
|
} else {
|
|
CHKiRet(OMSRsetEntry(*ppOMSR, 0, (uchar *)strdup((char *)pData->tplName), OMSR_NO_RQD_TPL_OPTS));
|
|
}
|
|
|
|
if (pData->pszRatelimitName != NULL) {
|
|
CHKiRet(ratelimitNewFromConfig(&pData->ratelimiter, loadModConf->pConf, (char *)pData->pszRatelimitName,
|
|
"omusrmsg", NULL));
|
|
ratelimitSetNoTimeCache(pData->ratelimiter);
|
|
} else if (pData->ratelimitInterval > 0) {
|
|
CHKiRet(ratelimitNew(&pData->ratelimiter, "omusrmsg", NULL));
|
|
ratelimitSetLinuxLike(pData->ratelimiter, (unsigned)pData->ratelimitInterval, (unsigned)pData->ratelimitBurst);
|
|
ratelimitSetNoTimeCache(pData->ratelimiter);
|
|
}
|
|
|
|
CODE_STD_FINALIZERnewActInst;
|
|
cnfparamvalsDestruct(pvals, &actpblk);
|
|
ENDnewActInst
|
|
|
|
|
|
BEGINparseSelectorAct
|
|
es_str_t *usrs;
|
|
int bHadWarning;
|
|
CODESTARTparseSelectorAct;
|
|
CODE_STD_STRING_REQUESTparseSelectorAct(1) bHadWarning = 0;
|
|
if (!strncmp((char *)p, ":omusrmsg:", sizeof(":omusrmsg:") - 1)) {
|
|
p += sizeof(":omusrmsg:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
|
|
} else {
|
|
if (!*p || !((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == '_' ||
|
|
*p == '.' || *p == '*')) {
|
|
ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
|
|
} else {
|
|
LogMsg(0, RS_RET_OUTDATED_STMT, LOG_WARNING,
|
|
"action '%s' treated as ':omusrmsg:%s' - please "
|
|
"use ':omusrmsg:%s' syntax instead, '%s' will "
|
|
"not be supported in the future",
|
|
p, p, p, p);
|
|
bHadWarning = 1;
|
|
}
|
|
}
|
|
|
|
CHKiRet(createInstance(&pData));
|
|
|
|
if (*p == '*') { /* wall */
|
|
dbgprintf("write-all");
|
|
++p; /* eat '*' */
|
|
pData->bIsWall = 1; /* write to all users */
|
|
CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar *)" WallFmt"));
|
|
} else {
|
|
/* everything else is currently treated as a user name */
|
|
usrs = es_newStr(128);
|
|
while (*p && *p != ';') {
|
|
es_addChar(&usrs, *p);
|
|
++p;
|
|
}
|
|
populateUsers(pData, usrs);
|
|
es_deleteStr(usrs);
|
|
if ((iRet = cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar *)" StdUsrMsgFmt")) !=
|
|
RS_RET_OK)
|
|
goto finalize_it;
|
|
}
|
|
if (iRet == RS_RET_OK && bHadWarning) iRet = RS_RET_OK_WARN;
|
|
CODE_STD_FINALIZERparseSelectorAct
|
|
ENDparseSelectorAct
|
|
|
|
|
|
BEGINmodExit
|
|
CODESTARTmodExit;
|
|
ENDmodExit
|
|
|
|
|
|
BEGINqueryEtryPt
|
|
CODESTARTqueryEtryPt;
|
|
CODEqueryEtryPt_STD_OMOD_QUERIES;
|
|
CODEqueryEtryPt_STD_OMOD8_QUERIES;
|
|
CODEqueryEtryPt_STD_CONF2_QUERIES;
|
|
CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES;
|
|
ENDqueryEtryPt
|
|
|
|
|
|
BEGINmodInitNoPredecl(UsrMsg)
|
|
CODESTARTmodInit;
|
|
INITLegCnfVars;
|
|
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
|
|
CODEmodInit_QueryRegCFSLineHdlr;
|
|
ENDmodInit
|
|
|
|
/* vim:set ai:
|
|
*/
|