summaryrefslogtreecommitdiffstats
path: root/tools/omusrmsg.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/omusrmsg.c')
-rw-r--r--tools/omusrmsg.c551
1 files changed, 551 insertions, 0 deletions
diff --git a/tools/omusrmsg.c b/tools/omusrmsg.c
new file mode 100644
index 0000000..479db5b
--- /dev/null
+++ b/tools/omusrmsg.c
@@ -0,0 +1,551 @@
+/* omusrmsg.c
+ * This is the implementation of the build-in output module for sending
+ * user messages.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2007-07-20 by RGerhards (extracted from syslogd.c, which at the
+ * time of the fork from sysklogd was under BSD license)
+ *
+ * Copyright 2007-2018 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_UTMP_H
+# include <utmp.h>
+# define STRUCTUTMP struct utmp
+# define UTNAME ut_name
+#else
+# include <utmpx.h>
+# define STRUCTUTMP struct utmpx
+# define UTNAME ut_user
+#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
+#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"
+
+
+/* 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;
+} instanceData;
+
+typedef struct wrkrInstanceData {
+ instanceData *pData;
+} wrkrInstanceData_t;
+
+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 }
+};
+static struct cnfparamblk actpblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(actpdescr)/sizeof(struct cnfparamdescr),
+ actpdescr
+ };
+
+BEGINinitConfVars /* (re)set config variables to default values */
+CODESTARTinitConfVars
+ENDinitConfVars
+
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ENDcreateInstance
+
+
+BEGINcreateWrkrInstance
+CODESTARTcreateWrkrInstance
+ENDcreateWrkrInstance
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURERepeatedMsgReduction)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ free(pData->tplName);
+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)
+# define setutent(void) setutxent(void)
+# define getutent(void) getutxent(void)
+# define endutent(void) endutxent(void)
+#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 */
+
+
+static void sendwallmsg(const char *tty, uchar* pMsg)
+{
+ uchar szErr[512];
+ int errnoSave;
+ char p[sizeof(_PATH_DEV) + UNAMESZ];
+ int ttyf;
+ struct stat statb;
+ int wrRet;
+
+ /* compute the device name */
+ strcpy(p, _PATH_DEV);
+ strncat(p, tty, UNAMESZ);
+
+ /* 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);
+ }
+}
+
+/* WALLMSG -- Write a message to the world at large
+ *
+ * Write the specified message to either the entire
+ * world, or a list of approved users.
+ *
+ * rgerhards, 2005-10-19: applying the following sysklogd patch:
+ * Tue May 4 16:52:01 CEST 2004: Solar Designer <solar@openwall.com>
+ * Adjust the size of a variable to prevent a buffer overflow
+ * should _PATH_DEV ever contain something different than "/dev/".
+ * rgerhards, 2008-07-04: changing the function to no longer use fork() but
+ * continue run on its thread instead.
+ */
+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;
+ 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);
+ user = pws->pw_name; /* DO NOT FREE, OS/LIB internal memory! */
+
+ if (user == NULL) {
+ dbgprintf("failed to get username for userid '%d'\n", uid);
+ continue;
+ }
+ } else {
+ /* we record the state to the debug log */
+ rs_strerror_r(-sdRet, (char*)szErr, sizeof(szErr));
+ dbgprintf("get userid for session '%s' failed with [%d]:%s\n",
+ sessions_list[j], -sdRet, szErr);
+ 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));
+ dbgprintf("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");
+ iRet = wallmsg(ppString[0], pWrkrData->pData);
+ENDdoAction
+
+
+static void
+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;
+}
+
+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")) {
+ pData->tplName = (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->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));
+ }
+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_OMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit(UsrMsg)
+CODESTARTmodInit
+INITLegCnfVars
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ENDmodInit
+
+/* vim:set ai:
+ */