blob: e08a7c5024d506a8b3d4917da21eeace4df018a7 [file] [log] [blame]
/*
* Copyright (C) Tildeslash Ltd. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3.
*
* This program 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 Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
*
* You must obey the GNU Affero General Public License in all respects
* for all of the code used other than OpenSSL.
*/
%option noyywrap
%{
/*
* DESCRIPTION
*
* Lexical grammar for tokenizing the control file.
*
*/
#include "config.h"
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_GLOB_H
#include <glob.h>
#endif
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include "monit.h"
#include "tokens.h"
// libmonit
#include "util/Str.h"
#define MAX_STACK_DEPTH 512
int buffer_stack_ptr=0;
struct buffer_stack_s {
int lineno;
char *currentfile;
YY_BUFFER_STATE buffer;
} buffer_stack[MAX_STACK_DEPTH];
int lineno= 1;
int arglineno= 1;
char *currentfile=NULL;
char *argcurrentfile=NULL;
char *argyytext=NULL;
/* Prototypes */
extern void yyerror(const char*,...);
extern void yywarning(const char *,...);
static void steplinenobycr(char *);
static void save_arg(void);
static void include_file(char *);
static char *handle_quoted_string(char *);
static void push_buffer_state(YY_BUFFER_STATE, char*);
static int pop_buffer_state(void);
static URL_T create_URL(char *proto);
%}
ws [ \r\t]+
wws [ \r\t;,()]+
number [0-9]+
real [0-9]+([.][0-9]+)?
str [^\000-\041@:{}"';(),%]+
hostname {str}(\.{str})*
greater ("greater"|"gt"|">")
less ("less"|"lt"|"<")
equal ("equal"|"eq"|"=="|"=")
notequal ("notequal"|"ne"|"!=")
loadavg1 load(avg)[ ]*(\([ ]*1[ ]*(m|min)?[ ]*\))?
loadavg5 load(avg)[ ]*\([ ]*5[ ]*(m|min)?[ ]*\)
loadavg15 load(avg)[ ]*\([ ]*15[ ]*(m|min)?[ ]*\)
cpuuser cpu[ ]*(usage)*[ ]*\([ ]*(us|usr|user)?[ ]*\)
cpusyst cpu[ ]*(usage)*[ ]*\([ ]*(sy|sys|system)?[ ]*\)
cpuwait cpu[ ]*(usage)*[ ]*\([ ]*(wa|wait)?[ ]*\)
startarg start{ws}?(program)?{ws}?([=]{ws})?["]
stoparg stop{ws}?(program)?{ws}?([=]{ws})?["]
restartarg restart{ws}?(program)?{ws}?([=]{ws})?["]
execarg exec(ute)?{ws}?["]
percent ("percent"|"%")
byte ("byte"|"b")
kilobyte ("kilobyte"|"kb")
megabyte ("megabyte"|"mb")
gigabyte ("gigabyte"|"gb")
%x ARGUMENT_COND DEPEND_COND SERVICE_COND URL_COND STRING_COND EVERY_COND INCLUDE
%%
{wws} { /* Wide white space */ }
(#.*)?\\?\n { lineno++; }
is {/* EMPTY */}
as {/* EMPTY */}
are {/* EMPTY */}
for {/* EMPTY */}
on(ly)? {/* EMPTY */}
with(in|out)? {/* EMPTY */}
program(s)? {/* EMPTY */}
and {/* EMPTY */}
has {/* EMPTY */}
using {/* EMPTY */}
use {/* EMPTY */}
the {/* EMPTY */}
sum {/* EMPTY */}
than {/* EMPTY */}
usage {/* EMPTY */}
was {/* EMPTY */}
times {/* EMPTY */}
but {/* EMPTY */}
of {/* EMPTY */}
does {/* EMPTY */}
{startarg} { BEGIN(ARGUMENT_COND); return START; }
{stoparg} { BEGIN(ARGUMENT_COND); return STOP; }
{restartarg} { BEGIN(ARGUMENT_COND); return RESTART; }
{execarg} { BEGIN(ARGUMENT_COND); return EXEC; }
if { return IF; }
or { return OR; }
then { return THEN; }
failed { return FAILED; }
ssl { return HTTPDSSL; }
enable { return ENABLE; }
disable { return DISABLE; }
set { return SET; }
daemon { return DAEMON; }
delay { return DELAY; }
logfile { return LOGFILE; }
syslog { return SYSLOG; }
facility { return FACILITY; }
httpd { return HTTPD; }
address { return ADDRESS; }
clientpemfile { return CLIENTPEMFILE; }
allowselfcertification { return ALLOWSELFCERTIFICATION; }
certmd5 { return CERTMD5; }
pemfile { return PEMFILE; }
init { return INIT; }
allow { return ALLOW; }
read[-]?only { return READONLY; }
pidfile { return PIDFILE; }
idfile { return IDFILE; }
statefile { return STATEFILE; }
path { return PATHTOK; }
start { return START; }
stop { return STOP; }
port(number)? { return PORT; }
unix(socket)? { return UNIXSOCKET; }
type { return TYPE; }
proto(col)? { return PROTOCOL; }
tcp { return TCP; }
tcpssl { return TCPSSL; }
udp { return UDP; }
alert { return ALERT; }
noalert { return NOALERT; }
mail-format { return MAILFORMAT; }
resource { return RESOURCE; }
restart(s)? { return RESTART; }
cycle(s)? { return CYCLE;}
timeout { return TIMEOUT; }
retry { return RETRY; }
checksum { return CHECKSUM; }
mailserver { return MAILSERVER; }
host { return HOST; }
hostheader { return HOSTHEADER; }
status { return STATUS; }
system { return SYSTEM; }
default { return DEFAULT; }
http { return HTTP; }
apache-status { return APACHESTATUS; }
ftp { return FTP; }
smtp { return SMTP; }
postfix-policy { return POSTFIXPOLICY; }
pop { return POP; }
imap { return IMAP; }
clamav { return CLAMAV; }
dns { return DNS; }
mysql { return MYSQL; }
nntp { return NNTP; }
ntp3 { return NTP3; }
ssh { return SSH; }
dwp { return DWP; }
ldap2 { return LDAP2; }
ldap3 { return LDAP3; }
rdate { return RDATE; }
lmtp { return LMTP; }
rsync { return RSYNC; }
tns { return TNS; }
pgsql { return PGSQL; }
sip { return SIP; }
gps { return GPS; }
radius { return RADIUS; }
memcache { return MEMCACHE; }
target { return TARGET; }
maxforward { return MAXFORWARD; }
mode { return MODE; }
active { return ACTIVE; }
passive { return PASSIVE; }
manual { return MANUAL; }
uid { return UID; }
gid { return GID; }
request { return REQUEST; }
secret { return SECRET; }
loglimit { return LOGLIMIT; }
closelimit { return CLOSELIMIT; }
dnslimit { return DNSLIMIT; }
keepalivelimit { return KEEPALIVELIMIT; }
replylimit { return REPLYLIMIT; }
requestlimit { return REQUESTLIMIT; }
startlimit { return STARTLIMIT; }
waitlimit { return WAITLIMIT; }
gracefullimit { return GRACEFULLIMIT; }
cleanuplimit { return CLEANUPLIMIT; }
mem(ory)? { return MEMORY; }
swap { return SWAP; }
totalmem(ory)? { return TOTALMEMORY; }
cpu { return CPU; }
totalcpu { return TOTALCPU; }
child(ren) { return CHILDREN; }
timestamp { return TIMESTAMP; }
changed { return CHANGED; }
second(s)? { return SECOND; }
minute(s)? { return MINUTE; }
hour(s)? { return HOUR; }
day(s)? { return DAY; }
sslv2 { return SSLV2; }
sslv3 { return SSLV3; }
tlsv1 { return TLSV1; }
sslauto { return SSLAUTO; }
inode(s)? { return INODE; }
space { return SPACE; }
perm(ission)? { return PERMISSION; }
exec(ute)? { return EXEC; }
size { return SIZE; }
uptime { return UPTIME; }
basedir { return BASEDIR; }
slot(s)? { return SLOT; }
eventqueue { return EVENTQUEUE; }
match(ing)? { return MATCH; }
not { return NOT; }
ignore { return IGNORE; }
connection { return CONNECTION; }
unmonitor { return UNMONITOR; }
action { return ACTION; }
icmp { return ICMP; }
echo { return ICMPECHO; }
send { return SEND; }
expect { return EXPECT; }
expectbuffer { return EXPECTBUFFER; }
cleartext { return CLEARTEXT; }
md5 { return MD5HASH; }
sha1 { return SHA1HASH; }
crypt { return CRYPT; }
signature { return SIGNATURE; }
nonexist { return NONEXIST; }
exist { return EXIST; }
invalid { return INVALID; }
data { return DATA; }
recovered { return RECOVERED; }
passed { return PASSED; }
succeeded { return SUCCEEDED; }
else { return ELSE; }
mmonit { return MMONIT; }
url { return URL; }
content { return CONTENT; }
pid { return PID; }
ppid { return PPID; }
count { return COUNT; }
reminder { return REMINDER; }
instance { return INSTANCE; }
hostname { return HOSTNAME; }
username { return USERNAME; }
password { return PASSWORD; }
credentials { return CREDENTIALS; }
register { return REGISTER; }
fsflag(s)? { return FSFLAG; }
fips { return FIPS; }
{byte} { return BYTE; }
{kilobyte} { return KILOBYTE; }
{megabyte} { return MEGABYTE; }
{gigabyte} { return GIGABYTE; }
{loadavg1} { return LOADAVG1; }
{loadavg5} { return LOADAVG5; }
{loadavg15} { return LOADAVG15; }
{cpuuser} { return CPUUSER; }
{cpusyst} { return CPUSYSTEM; }
{cpuwait} { return CPUWAIT; }
{greater} { return GREATER; }
{less} { return LESS; }
{equal} { return EQUAL; }
{notequal} { return NOTEQUAL; }
include { BEGIN(INCLUDE); }
not[ ]+every {
BEGIN(EVERY_COND);
return NOTEVERY;
}
every {
BEGIN(EVERY_COND);
return EVERY;
}
depend(s)?[ \t]+(on[ \t]*)? {
BEGIN(DEPEND_COND);
return DEPENDS;
}
check[ \t]+(process[ \t])? {
BEGIN(SERVICE_COND);
return CHECKPROC;
}
check[ \t]+(program[ \t])? {
BEGIN(SERVICE_COND);
return CHECKPROGRAM;
}
check[ \t]+device { /* Filesystem alias for backward compatibility */
BEGIN(SERVICE_COND);
return CHECKFILESYS;
}
check[ \t]+filesystem {
BEGIN(SERVICE_COND);
return CHECKFILESYS;
}
check[ \t]+file {
BEGIN(SERVICE_COND);
return CHECKFILE;
}
check[ \t]+directory {
BEGIN(SERVICE_COND);
return CHECKDIR;
}
check[ \t]+host {
BEGIN(SERVICE_COND);
return CHECKHOST;
}
check[ \t]+system {
BEGIN(SERVICE_COND);
return CHECKSYSTEM;
}
check[ \t]+fifo {
BEGIN(SERVICE_COND);
return CHECKFIFO;
}
check[ \t]+program {
BEGIN(SERVICE_COND);
return CHECKPROGRAM;
}
group[ \t]+ {
BEGIN(STRING_COND);
return GROUP;
}
[a-zA-Z0-9]+"://" {
yylval.url=
create_URL(Str_ndup(yytext, strlen(yytext)-3));
BEGIN(URL_COND);
}
[0-9]{2}":"[0-9]{2}":"[0-9]{2} {
yylval.string= Str_dup(yytext);
save_arg(); return TIMESPEC;
}
{number} {
yylval.number= atoi(yytext);
save_arg(); return NUMBER;
}
{real} {
yylval.real= atof(yytext);
save_arg(); return REAL;
}
{percent} {
return PERCENT;
}
[a-zA-Z0-9]{str} {
yylval.string= Str_dup(yytext);
save_arg(); return STRING;
}
\"[/][^\"\n]*\" {
yylval.string= handle_quoted_string(yytext);
save_arg(); return PATH;
}
\'[/][^\'\n]*\' {
yylval.string= handle_quoted_string(yytext);
save_arg(); return PATH;
}
\"[^\"]*\" {
steplinenobycr(yytext);
yylval.string= handle_quoted_string(yytext);
save_arg(); return STRING;
}
\'[^\']*\' {
steplinenobycr(yytext);
yylval.string= handle_quoted_string(yytext);
save_arg(); return STRING;
}
{str}[@]{str} {
yylval.string= Str_dup(yytext);
save_arg(); return MAILADDR;
}
[/]{str} {
yylval.string= Str_dup(yytext);
save_arg(); return PATH;
}
"/" {
yylval.string= Str_dup(yytext);
save_arg(); return PATH;
}
"from:"[ \t]*{str}[@]{str} {
char *p= yytext+strlen("from:");
yylval.string = Str_trim(Str_dup(p));
save_arg(); return MAILFROM;
}
"reply-to:"[ \t]*{str}[@]{str} {
char *p= yytext+strlen("reply-to:");
yylval.string = Str_trim(Str_dup(p));
save_arg(); return MAILREPLYTO;
}
"subject:"[^}\n]* {
char *p= yytext+strlen("subject:");
yylval.string = Str_trim(Str_dup(p));
save_arg(); return MAILSUBJECT;
}
"message:"[^}]* {
char *p= yytext+strlen("message:");
steplinenobycr(yytext);
yylval.string = Str_trim(Str_dup(p));
save_arg(); return MAILBODY;
}
{hostname} {
yylval.string = Str_dup(yytext);
save_arg(); return STRING;
}
[\"\'] {
yyerror("unbalanced quotes");
}
<SERVICE_COND>{
{ws} ;
[\n] {
lineno++;
}
{str} {
yylval.string= Str_dup(yytext);
BEGIN(INITIAL);
save_arg(); return SERVICENAME;
}
\"{str}\" {
yylval.string= handle_quoted_string(yytext);
BEGIN(INITIAL);
save_arg(); return SERVICENAME;
}
\'{str}\' {
yylval.string= handle_quoted_string(yytext);
BEGIN(INITIAL);
save_arg(); return SERVICENAME;
}
[\"\'] {
yyerror("unbalanced quotes");
}
}
<DEPEND_COND>{
{wws} ;
{wws}?[\n]{wws}? {
lineno++;
}
{str} {
yylval.string= Str_dup(yytext);
save_arg(); return SERVICENAME;
}
[ \r\n\t]+[^,] {
steplinenobycr(yytext);
unput(yytext[strlen(yytext)-1]);
BEGIN(INITIAL);
}
}
<ARGUMENT_COND>{
{ws} ;
[\n] {
lineno++;
}
\" {
BEGIN(INITIAL);
}
\'[^\']*\' {
steplinenobycr(yytext);
yylval.string= handle_quoted_string(yytext);
save_arg(); return STRING;
}
\' {
yyerror("unbalanced quotes");
}
[^ \t\n\"]+ {
yylval.string= Str_dup(yytext);
save_arg(); return STRING;
}
}
<URL_COND>{
{ws}|[\n] {
BEGIN(INITIAL);
if(!yylval.url->hostname)
yyerror("missing hostname in URL");
if(!yylval.url->path)
yylval.url->path= Str_dup("/");
yylval.url->url= Str_cat("%s://%s:%d%s%s%s",
yylval.url->protocol,
/* possible credentials are hidden */
yylval.url->hostname,
yylval.url->port,
yylval.url->path,
yylval.url->query?"?":"",
yylval.url->query?yylval.url->query:"");
save_arg(); return URLOBJECT;
}
[^:@ ]+/[:][^@: ]+[@] {
yylval.url->user= Str_dup(yytext);
}
[:][^@ ]+[@] {
yytext++;
yylval.url->password= Str_ndup(yytext, strlen(yytext)-1);
}
[^/?:#\r\n ]+ {
yylval.url->hostname= Str_dup(yytext);
}
[:]{number} {
yylval.url->port= atoi(++yytext);
}
[/][^?#\r\n ]* {
yylval.url->path= Util_urlEncode(yytext);
}
[?][^#\r\n ]* {
yylval.url->query= Util_urlEncode(++yytext);
}
[#][^\r\n ]* {
/* EMPTY - reference is ignored */
}
}
<STRING_COND>{
{str} {
yylval.string= Str_dup(yytext);
BEGIN(INITIAL);
save_arg(); return STRINGNAME;
}
\"{str}\" {
yylval.string= handle_quoted_string(yytext);
BEGIN(INITIAL);
save_arg(); return STRINGNAME;
}
\'{str}\' {
yylval.string= handle_quoted_string(yytext);
BEGIN(INITIAL);
save_arg(); return STRINGNAME;
}
[\"\'] {
yyerror("unbalanced quotes");
}
}
<EVERY_COND>{
{ws} ;
{number} {
yylval.number= atoi(yytext);
BEGIN(INITIAL);
save_arg(); return NUMBER;
}
['"]{ws}?[0-9,*-]+{ws}[0-9,*-]+{ws}[0-9,*-]+{ws}[0-9,*-]+{ws}[0-9,*-]+{ws}?['"] { // A minimal syntax check of the cron format string; 5 fields separated with white-space
yylval.string= Str_dup(Str_unquote(yytext));
BEGIN(INITIAL);
save_arg(); return TIMESPEC;
}
. {
BEGIN(INITIAL);
yyerror("invalid every format");
}
}
<INITIAL,ARGUMENT_COND,SERVICE_COND,DEPEND_COND,URL_COND,STRING_COND,EVERY_COND>. {
return yytext[0];
}
<INCLUDE>[ \t]* /* eat the whitespace */
<INCLUDE>\"[^\"\r\n]+\" { /* got the include file name with double quotes */
char *temp=Str_dup(yytext);
Str_unquote(temp);
include_file(temp);
FREE(temp);
BEGIN(INITIAL);
}
<INCLUDE>\'[^\'\r\n]+\' { /* got the include file name with single quotes*/
char *temp=Str_dup(yytext);
Str_unquote(temp);
include_file(temp);
FREE(temp);
BEGIN(INITIAL);
}
<INCLUDE>[^ \t\r\n]+ { /* got the include file name without quotes*/
char *temp=Str_dup(yytext);
include_file(temp);
FREE(temp);
BEGIN(INITIAL);
}
<<EOF>> {
if ( !pop_buffer_state() )
{
yyterminate();
} else {
BEGIN(INITIAL);
}
}
%%
/*
* Do lineno++ for every occurrence of '\n' in a string. This is
* necessary whenever a yytext has an unknown number of CRs.
*/
static void steplinenobycr(char *string) {
char *pos= string;
while(*pos)
if('\n'==*pos++) {
lineno++;
}
}
static char *handle_quoted_string(char *string) {
char *buf= Str_dup(string);
Str_unquote(buf);
Util_handleEscapes(buf);
return buf;
}
static void include_file(char *pattern) {
glob_t globbuf;
int i;
if (glob(pattern, GLOB_MARK, NULL, &globbuf) != 0)
return; // no include files found
for (i = 0; i < globbuf.gl_pathc; i++) {
FILE *yyin;
size_t filename_length = strlen(globbuf.gl_pathv[i]);
/* check whenever we have caught a directory or file backup copy */
if ((filename_length == 0) || (globbuf.gl_pathv[i][filename_length-1] == '~' ) || (globbuf.gl_pathv[i][filename_length-1] == '/' ))
continue;
yyin = fopen( globbuf.gl_pathv[i], "r" );
if (! yyin)
yyerror( "failed to include file" );
else
push_buffer_state(yy_create_buffer( yyin, YY_BUF_SIZE ), globbuf.gl_pathv[i]);
}
globfree(&globbuf);
}
static void push_buffer_state(YY_BUFFER_STATE buffer, char *filename) {
if ( buffer_stack_ptr >= MAX_STACK_DEPTH )
{
yyerror("include files are nested too deeply" );
exit( 1 );
}
buffer_stack[buffer_stack_ptr].lineno = lineno;
buffer_stack[buffer_stack_ptr].currentfile = currentfile;
buffer_stack[buffer_stack_ptr].buffer = YY_CURRENT_BUFFER;
buffer_stack_ptr++;
lineno = 1;
currentfile = Str_dup(filename);
yy_switch_to_buffer(buffer);
BEGIN(INITIAL);
}
static int pop_buffer_state(void) {
if ( --buffer_stack_ptr < 0 ) {
return 0;
} else {
fclose(yyin);
lineno=buffer_stack[buffer_stack_ptr].lineno;
FREE(currentfile);
currentfile=buffer_stack[buffer_stack_ptr].currentfile;
yy_delete_buffer( YY_CURRENT_BUFFER );
yy_switch_to_buffer( buffer_stack[buffer_stack_ptr].buffer );
}
return 1;
}
static void save_arg(void) {
arglineno=lineno;
argcurrentfile=currentfile;
FREE(argyytext);
argyytext=Str_dup(yytext);
}
static URL_T create_URL(char *proto) {
URL_T url;
ASSERT(proto);
NEW(url);
url->protocol= proto;
if(IS(url->protocol, "https")) {
url->port= 443;
if(!have_ssl())
yyerror("HTTPS protocol not supported -- SSL support disabled" );
} else if(IS(url->protocol, "http")) {
url->port= 80;
} else {
yyerror("URL protocol not supported -- ");
}
return url;
}