diff options
Diffstat (limited to 'tools/omusrmsg.c')
-rw-r--r-- | tools/omusrmsg.c | 352 |
1 files changed, 352 insertions, 0 deletions
diff --git a/tools/omusrmsg.c b/tools/omusrmsg.c new file mode 100644 index 00000000..42d3291d --- /dev/null +++ b/tools/omusrmsg.c @@ -0,0 +1,352 @@ +/* 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) + * This file is under development and has not yet arrived at being fully + * self-contained and a real object. So far, it is mostly an excerpt + * of the "old" message code without any modifications. However, it + * helps to have things at the right place one we go to the meat of it. + * + * Copyright 2007 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of rsyslog. + * + * Rsyslog is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Rsyslog is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>. + * + * A copy of the GPL can be found in the file "COPYING" in this distribution. + */ +#include "config.h" +#include "rsyslog.h" +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <signal.h> +#include <sys/param.h> +#include <utmp.h> +#include <unistd.h> +#include <sys/uio.h> +#include <sys/stat.h> +#include <setjmp.h> +#if HAVE_FCNTL_H +#include <fcntl.h> +#else +#include <sys/msgbuf.h> +#endif +#if HAVE_PATHS_H +#include <paths.h> +#endif +#include "srUtils.h" +#include "stringbuf.h" +#include "syslogd-types.h" +#include "syslogd.h" +#include "omusrmsg.h" +#include "module-template.h" +#include "errmsg.h" + + +/* portability: */ +#ifndef _PATH_DEV +# define _PATH_DEV "/dev/" +#endif + + +MODULE_TYPE_OUTPUT + +/* internal structures + */ +DEF_OMOD_STATIC_DATA +DEFobjCurrIf(errmsg) + +typedef struct _instanceData { + int bIsWall; /* 1- is wall, 0 - individual users */ + char uname[MAXUNAMES][UNAMESZ+1]; +} instanceData; + + +BEGINcreateInstance +CODESTARTcreateInstance +ENDcreateInstance + + +BEGINisCompatibleWithFeature +CODESTARTisCompatibleWithFeature + if(eFeat == sFEATURERepeatedMsgReduction) + iRet = RS_RET_OK; +ENDisCompatibleWithFeature + + +BEGINfreeInstance +CODESTARTfreeInstance + /* TODO: free the instance pointer (currently a leak, will go away) */ +ENDfreeInstance + + +BEGINdbgPrintInstInfo + register int i; +CODESTARTdbgPrintInstInfo + for (i = 0; i < MAXUNAMES && *pData->uname[i]; i++) + dbgprintf("%s, ", pData->uname[i]); +ENDdbgPrintInstInfo + + +static jmp_buf ttybuf; + +static void endtty() +{ + longjmp(ttybuf, 1); +} + +/** + * 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 +static FILE *BSD_uf = NULL; +void setutent(void) +{ + assert(BSD_uf == NULL); + if ((BSD_uf = fopen(_PATH_UTMP, "r")) == NULL) { + errmsg.LogError(NO_ERRCODE, "%s", _PATH_UTMP); + return; + } +} + +struct utmp* getutent(void) +{ + static struct utmp 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 /* #ifdef OS_BSD */ + + +/* + * 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/". + */ +static rsRetVal wallmsg(uchar* pMsg, instanceData *pData) +{ + + char p[sizeof(_PATH_DEV) + UNAMESZ]; + register int i; + int ttyf; + static int reenter = 0; + struct utmp ut; + struct utmp *uptr; + struct sigaction sigAct; + + assert(pMsg != NULL); + + if (reenter++) + return RS_RET_OK; + + /* open the user login file */ + setutent(); + + /* + * Might as well fork instead of using nonblocking I/O + * and doing notty(). + */ + if (fork() == 0) { + memset(&sigAct, 0, sizeof(sigAct)); + sigemptyset(&sigAct.sa_mask); + sigAct.sa_handler = SIG_DFL; + sigaction(SIGTERM, &sigAct, NULL); + alarm(0); + +# ifdef SIGTTOU + sigAct.sa_handler = SIG_DFL; + sigaction(SIGTERM, &sigAct, NULL); +# endif + /* It is save to call sigprocmask here, as we are now executing the child (no threads) */ + sigprocmask(SIG_SETMASK, &sigAct.sa_mask, NULL); + /* TODO: find a way to limit the max size of the message. hint: this + * should go into the template! + */ + + /* rgerhards 2005-10-24: HINT: this code might be run in a seperate thread + * instead of a seperate process once we have multithreading... + */ + + /* scan the user login file */ + while ((uptr = getutent())) { + memcpy(&ut, uptr, sizeof(ut)); + /* is this slot used? */ + if (ut.ut_name[0] == '\0') + continue; +#ifndef OS_BSD + if (ut.ut_type != USER_PROCESS) + continue; +#endif + if (!(strncmp (ut.ut_name,"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.ut_name, UNAMESZ) == 0) + break; + } + if (i >= MAXUNAMES) + continue; + } + + /* compute the device name */ + strcpy(p, _PATH_DEV); + strncat(p, ut.ut_line, UNAMESZ); + + if (setjmp(ttybuf) == 0) { + sigAct.sa_handler = endtty; + sigaction(SIGALRM, &sigAct, NULL); + (void) alarm(15); + /* open the terminal */ + ttyf = open(p, O_WRONLY|O_NOCTTY); + if (ttyf >= 0) { + struct stat statb; + + if (fstat(ttyf, &statb) == 0 && + (statb.st_mode & S_IWRITE)) { + (void) write(ttyf, pMsg, strlen((char*)pMsg)); + } + close(ttyf); + ttyf = -1; + } + } + (void) alarm(0); + } + exit(0); /* "good" exit - this terminates the child forked just for message delivery */ + } + /* close the user login file */ + endutent(); + reenter = 0; + return RS_RET_OK; +} + + +BEGINtryResume +CODESTARTtryResume +ENDtryResume + +BEGINdoAction +CODESTARTdoAction + dbgprintf("\n"); + iRet = wallmsg(ppString[0], pData); +ENDdoAction + + +BEGINparseSelectorAct + uchar *q; + int i; +CODESTARTparseSelectorAct +CODE_STD_STRING_REQUESTparseSelectorAct(1) + + /* User names must begin with a gnu e-regex: + * [a-zA-Z0-9_.] + * plus '*' for wall + */ + if (!*p || !((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') + || (*p >= '0' && *p <= '9') || *p == '_' || *p == '.' || *p == '*')) + ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED); + + if((iRet = createInstance(&pData)) != RS_RET_OK) + goto finalize_it; + + + if(*p == '*') { /* wall */ + dbgprintf("write-all"); + ++p; /* eat '*' */ + pData->bIsWall = 1; /* write to all users */ + if((iRet = cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar*) " WallFmt")) + != RS_RET_OK) + goto finalize_it; + } else { + /* everything else beginning with the regex above + * is currently treated as a user name + * TODO: is this portable? + */ + dbgprintf("users: %s\n", p); /* ASP */ + pData->bIsWall = 0; /* write to individual users */ + for (i = 0; i < MAXUNAMES && *p && *p != ';'; i++) { + for (q = p; *q && *q != ',' && *q != ';'; ) + q++; + (void) strncpy((char*) pData->uname[i], (char*) p, UNAMESZ); + if ((q - p) > UNAMESZ) + pData->uname[i][UNAMESZ] = '\0'; + else + pData->uname[i][q - p] = '\0'; + while (*q == ',' || *q == ' ') + q++; + p = q; + } + /* done, on to the template + * TODO: we need to handle the case where i >= MAXUNAME! + */ + if((iRet = cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar*)" StdUsrMsgFmt")) + != RS_RET_OK) + goto finalize_it; + } +CODE_STD_FINALIZERparseSelectorAct +ENDparseSelectorAct + + +BEGINmodExit +CODESTARTmodExit +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_OMOD_QUERIES +ENDqueryEtryPt + + +BEGINmodInit(UsrMsg) +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr + CHKiRet(objUse(errmsg, CORE_COMPONENT)); +ENDmodInit + +/* + * vi:set ai: + */ |