Added a new filter which can directly read the output
[gnucomo.git] / src / gcm_input / df_filter.cpp
1
2 /**************************************************************************
3 **  (c) Copyright 2007, Andromeda Technology & Automation
4 ** This is free software; you can redistribute it and/or modify it under the
5 ** terms of the GNU General Public License, see the file COPYING.
6 ***************************************************************************
7 ** MODULE INFORMATION *
8 ***********************
9 **      FILE NAME      : df_filter.cpp
10 **      SYSTEM NAME    : 
11 **      VERSION NUMBER : $Revision: 1.1 $
12 **
13 **  DESCRIPTION      :  Transform output of 'df' into a Gnucomo XML document
14 **
15 **  EXPORTED OBJECTS : 
16 **  LOCAL    OBJECTS : 
17 **  MODULES  USED    :
18 ***************************************************************************
19 **  ADMINISTRATIVE INFORMATION *
20 ********************************
21 **      ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl
22 **      CREATION DATE   : Oct 30, 2007
23 **      LAST UPDATE     : Nov 02, 2007
24 **      MODIFICATIONS   : 
25 **************************************************************************/
26
27 /*****************************
28    $Log: df_filter.cpp,v $
29    Revision 1.1  2007-11-03 10:26:13  arjen
30    Added a new filter which can directly read the output
31    of the UNIX df command. A brief description is added in
32    the user manual.
33
34 *****************************/
35
36 /* static const char *RCSID = "$Id: df_filter.cpp,v 1.1 2007-11-03 10:26:13 arjen Exp $"; */
37
38 #include <ctype.h>
39 #include <vector>
40
41 #include "df_filter.h"
42
43
44 std::vector<String> tokenize(String line)
45 {
46    std::vector<String> words;
47    int    l;           //  letter index in line
48
49    l = 0;
50    while (l < ~line)
51    {
52       int start, length;
53
54       while (l < ~line && isspace(line[l]))
55       {
56          l++;
57       }
58       start = l;
59       length = 0;
60
61       while (l < ~line && !isspace(line[l]))
62       {
63          l++;
64          length++;
65       }
66       String w = line(start, length);
67       words.push_back(w);
68    }
69    return words;
70 }
71
72 //  Find a regular expression in a vector of Strings
73
74 int  findword(const std::vector<String> &tokens, const regex &pattern)
75 {
76    int   i;
77
78    i = 0;
79    while (i < tokens.size() && !(tokens[i] == pattern))
80    {
81       i++;
82    }
83
84    if (i == tokens.size())
85    {
86       std::cerr << "Token not found.\n";
87    }
88
89    return i;
90 }
91
92 /*=========================================================================
93 **  NAME           : constructXML
94 **  SYNOPSIS       : int constructXML(message_buffer &in, strstream &xml)
95 **  PARAMETERS     : 
96 **  RETURN VALUE   : Create an XML document from output of 'df -k'
97 **
98 **  DESCRIPTION    : 
99 **
100 **  VARS USED      :
101 **  VARS CHANGED   :
102 **  FUNCTIONS USED :
103 **  SEE ALSO       :
104 **  LAST MODIFIED  : Nov 02, 2007
105 **=========================================================================
106 */
107
108 static const regex re_df_header("^Filesystem.+[Uu]se.+[Aa]vail.+Mounted");
109 static const regex re_df_i_header("^Filesystem +Inodes.+IUse.+Mounted");
110 static const regex re_df_data(".+ [0-9]+ +[0-9]+ +[0-9]+");
111
112 void df_filter::construct_XML(message_buffer &in, std::strstream &xml)
113 {
114    String line;
115    enum   df_report_detection { NONE, BLOCKS, INODES };
116
117    df_report_detection  report_type = NONE;
118
119    int    field_position[4];  //  Mountpoint, blocks, used, available
120
121    std::vector<String>  tokens;
122
123    scan_email_header(in);
124    construct_header(xml);
125
126    xml << "  <gcmt:data>\n";
127    xml << "    <gcmt:parameters class='filesystem'>\n";
128
129    while (in >> line)
130    { 
131       String  mountpoint;
132       String  device;
133       String  blocks;
134       String  inodes;
135       String  used;
136       String  available;
137
138       switch (report_type)
139       {
140       case NONE:  //  Header.
141          if (line == re_df_header)
142          {
143             report_type = BLOCKS;
144             tokens = tokenize(line);
145             //  Make a map to find the fields we're interested in
146             field_position[0] = findword(tokens, regex("Mounted"));
147             field_position[1] = findword(tokens, regex("kbytes|-blocks"));
148             field_position[2] = findword(tokens, regex("[uU]se"));
149             field_position[3] = findword(tokens, regex("[aA]vail"));
150          }
151          if (line == re_df_i_header)
152          {
153             report_type = INODES;
154             tokens = tokenize(line);
155             //  Make a map to find the fields we're interested in
156             field_position[0] = findword(tokens, regex("Mounted"));
157             field_position[1] = findword(tokens, regex("Inodes"));
158             field_position[2] = findword(tokens, regex("IUsed"));
159             field_position[3] = findword(tokens, regex("IFree"));
160          }
161          break;
162
163       case BLOCKS: // df output lines.
164          tokens = tokenize(line);
165          if (tokens.size() > 4)
166          {
167             mountpoint = tokens[field_position[0]];
168             blocks     = tokens[field_position[1]];
169             used       = tokens[field_position[2]];
170             available  = tokens[field_position[3]];
171             device     = tokens[0];
172             if (mountpoint[0] == '/')
173             {
174
175                //   Create the XML element.
176
177                xml << "    <gcmt:parameter name='" << mountpoint << "'>\n";
178                xml << "      <gcmt:property name='device'>" << device << "</gcmt:property>\n";
179                xml << "      <gcmt:property name='size'>" << blocks << "</gcmt:property>\n";
180                xml << "      <gcmt:property name='used'>"   << used   << "</gcmt:property>\n";
181                xml << "      <gcmt:property name='available'>" << available << "</gcmt:property>\n";
182                xml << "    </gcmt:parameter>\n";
183             }
184          }
185          else
186          {
187             //cerr << "Mismatch on " << line << "\n";
188          }
189          break;
190
191       case INODES: // df output lines.
192          tokens = tokenize(line);
193          if (tokens.size() > 4)
194          {
195             mountpoint = tokens[field_position[0]];
196             inodes     = tokens[field_position[1]];
197             used       = tokens[field_position[2]];
198             available  = tokens[field_position[3]];
199             device     = tokens[0];
200             if (mountpoint[0] == '/')
201             {
202
203                //   Create the XML element.
204
205                xml << "    <gcmt:parameter name='" << mountpoint << "'>\n";
206                xml << "      <gcmt:property name='device'>" << device << "</gcmt:property>\n";
207                xml << "      <gcmt:property name='Inodes'>" << inodes << "</gcmt:property>\n";
208                xml << "      <gcmt:property name='Iused'>"   << used   << "</gcmt:property>\n";
209                xml << "      <gcmt:property name='Ifree'>" << available << "</gcmt:property>\n";
210                xml << "    </gcmt:parameter>\n";
211             }
212          }
213          else
214          {
215             //cerr << "Mismatch on " << line << "\n";
216          }
217          break;
218       }
219    }
220    xml << "    </gcmt:parameters>\n";
221    xml << "  </gcmt:data>\n";
222    xml << "</gcmt:message>\n";
223 }
224
225 bool df_cooker::check_pattern(String logline)
226 {
227    if (df_detected)
228    {
229       df_detected = (logline == re_df_data);
230    }
231    else
232    {
233       df_detected = (logline == re_df_header || logline == re_df_i_header);
234    }
235
236    return df_detected;
237 }
238
239 bool df_cooker::cook_this(String logline, UTC arrival)
240 {
241    return true;
242 }