1 /*
2  * DisMan Schedule MIB:
3  *     Implementation of the schedule MIB config handling
4  */
5 
6 #include <net-snmp/net-snmp-config.h>
7 #include <net-snmp/net-snmp-features.h>
8 #include <net-snmp/net-snmp-includes.h>
9 #include <net-snmp/agent/net-snmp-agent-includes.h>
10 #include <ctype.h>
11 #include "disman/schedule/schedCore.h"
12 #include "disman/schedule/schedConf.h"
13 
14 netsnmp_feature_require(iquery);
15 netsnmp_feature_require(string_time_to_secs);
16 
17 static int schedEntries;
18 
19 /** Initializes the schedConf module */
20 void
init_schedConf(void)21 init_schedConf(void)
22 {
23     DEBUGMSGTL(("disman:schedule:init", "Initializing config module\n"));
24     init_schedule_container();
25 
26     /*
27      * Register public configuration directives
28      */
29     snmpd_register_config_handler("repeat", parse_sched_periodic, NULL,
30                                   "repeat period  OID = value");
31     snmpd_register_config_handler("cron",   parse_sched_timed,    NULL,
32                                   "cron * * * * * OID = value");
33     snmpd_register_config_handler("at",     parse_sched_timed,    NULL,
34                                   "at   * * * * * OID = value");
35 
36     /*
37      * Register internal configuration directive,
38      *   and arrange for dynamically configured entries to be saved
39      */
40     snmpd_register_config_handler("_schedTable", parse_schedTable, NULL, NULL);
41     snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_STORE_DATA,
42                                      store_schedTable, NULL);
43     schedEntries = 0;
44 }
45 
46 
47 /* =======================================================
48  *
49  * Handlers for user-configured (static) scheduled actions
50  *
51  * ======================================================= */
52 
53 void
parse_sched_periodic(const char * token,char * line)54 parse_sched_periodic( const char *token, char *line )
55 {
56     netsnmp_tdata_row       *row;
57     struct schedTable_entry *entry;
58     char   buf[24], tmpbuf[SPRINT_MAX_LEN];
59     long   frequency;
60     long   value;
61     size_t tmpint;
62     oid    variable[ MAX_OID_LEN], *var_ptr = variable;
63     size_t var_len = MAX_OID_LEN;
64 
65     schedEntries++;
66     sprintf(buf, "_conf%03d", schedEntries);
67 
68     DEBUGMSGTL(( "disman:schedule:conf", "periodic: %s %s\n", token, line));
69     /*
70      *  Parse the configure directive line
71      */
72     line = copy_nword(line, tmpbuf, sizeof(tmpbuf));
73     frequency = netsnmp_string_time_to_secs(tmpbuf);
74     if (frequency == -1) {
75         config_perror("Illegal frequency specified");
76         return;
77     }
78 
79     line = read_config_read_data(ASN_OBJECT_ID, line, &var_ptr,   &var_len);
80     if (var_len == 0) {
81         config_perror("invalid specification for schedVariable");
82         return;
83     }
84     /*
85      * Skip over optional assignment in "var = value"
86      */
87     while (line && isspace((unsigned char)(*line)))
88         line++;
89     if (line && *line == '=' ) {
90         line++;
91         while (line && isspace((unsigned char)(*line))) {
92             line++;
93         }
94     }
95     line = read_config_read_data(ASN_INTEGER,   line, &value, &tmpint);
96 
97     /*
98      * Create an entry in the schedTable
99      */
100     row   = schedTable_createEntry( "snmpd.conf", buf );
101     if (!row || !row->data) {
102         config_perror("create schedule entry failure");
103         return;
104     }
105     entry = (struct schedTable_entry *)row->data;
106 
107     entry->schedInterval     = frequency;
108     entry->schedValue        = value;
109     entry->schedVariable_len = var_len;
110     memcpy(entry->schedVariable, variable, var_len*sizeof(oid));
111 
112     entry->schedType         = SCHED_TYPE_PERIODIC;
113     entry->schedStorageType  = ST_READONLY;  /* or PERMANENT */
114     entry->flags             = SCHEDULE_FLAG_ENABLED |
115                                SCHEDULE_FLAG_ACTIVE  |
116                                SCHEDULE_FLAG_VALID;
117     entry->session           = netsnmp_query_get_default_session();
118     sched_nextTime( entry );
119 }
120 
121 
122 /*
123  * Timed-schedule utility:
124  *     Convert from a cron-style specification to the equivalent set
125  *     of bits. Note that minute, hour and weekday crontab fields are
126  *     0-based, while day and month more naturally start from 1.
127  */
128 void
_sched_convert_bits(char * cron_spec,char * bit_buf,int bit_buf_len,int max_val,int startAt1)129 _sched_convert_bits( char *cron_spec, char *bit_buf,
130                      int  bit_buf_len, int max_val, int startAt1 ) {
131     char *cp = cron_spec;
132     u_char b[] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
133     int val, major, minor;
134     int overshoot;
135 
136     if (!cron_spec || !bit_buf)
137         return;
138 
139     /*
140      * Wildcard field - set all bits
141      */
142     if ( *cp == '*' ) {
143         memset( bit_buf, 0xff, bit_buf_len );
144 
145         /*
146          * An "all-bits" specification may not be an exact multiple of 8.
147          * Work out how far we've overshot things, and tidy up the excess.
148          */
149         overshoot = 8*bit_buf_len-max_val;
150         while ( overshoot > 0 ) {
151             bit_buf[ bit_buf_len-1 ] ^= b[8-overshoot];
152             overshoot--;
153         }
154         return;
155     }
156 
157     /*
158      * Otherwise, clear the bit string buffer,
159      * and start calculating which bits to set
160      */
161     memset( bit_buf, 0, bit_buf_len );
162 
163     while (1) {
164         sscanf( cp, "%d", &val);
165         /* Handle negative day specification */
166         if ( val < 0 ) {
167             val = max_val - val;
168         }
169         if ( startAt1 )
170             val--;
171         major = val/8;
172         minor = val%8;
173         bit_buf[ major ] |= b[minor];
174 
175         /* XXX - ideally we should handle "X-Y" syntax as well */
176         while (*cp && *cp!=',')
177             cp++;
178         if (!*cp)
179             break;
180         cp++;
181     }
182 }
183 
184 void
parse_sched_timed(const char * token,char * line)185 parse_sched_timed( const char *token, char *line )
186 {
187     netsnmp_tdata_row       *row;
188     struct schedTable_entry *entry;
189     char   buf[24], *cp;
190 
191     char  minConf[512];  size_t  min_len = sizeof(minConf);  char  minVal[8];
192     char hourConf[512];  size_t hour_len = sizeof(hourConf); char hourVal[3];
193     char dateConf[512];  size_t date_len = sizeof(dateConf); char dateVal[8];
194     char  monConf[512];  size_t  mon_len = sizeof(monConf);  char  monVal[2];
195     char  dayConf[512];  size_t  day_len = sizeof(dayConf);  char  dayVal;
196 
197     long   value;
198     size_t tmpint;
199     oid    variable[ MAX_OID_LEN], *var_ptr = variable;
200     size_t var_len = MAX_OID_LEN;
201 
202     schedEntries++;
203     sprintf(buf, "_conf%03d", schedEntries);
204 
205     DEBUGMSGTL(( "sched", "config: %s %s\n", token, line));
206     /*
207      *  Parse the configure directive line
208      */
209     cp       = minConf;
210     line = read_config_read_data(ASN_OCTET_STR, line, &cp,   &min_len);
211     cp       = hourConf;
212     line = read_config_read_data(ASN_OCTET_STR, line, &cp,  &hour_len);
213     cp       = dateConf;
214     line = read_config_read_data(ASN_OCTET_STR, line, &cp,  &date_len);
215     cp       = monConf;
216     line = read_config_read_data(ASN_OCTET_STR, line, &cp,   &mon_len);
217     cp       = dayConf;
218     line = read_config_read_data(ASN_OCTET_STR, line, &cp,   &day_len);
219     if (!line) {
220         config_perror("invalid schedule time specification");
221         return;
222     }
223 
224     line = read_config_read_data(ASN_OBJECT_ID, line, &var_ptr,   &var_len);
225     if (var_len == 0) {
226         config_perror("invalid specification for schedVariable");
227         return;
228     }
229     /*
230      * Skip over optional assignment in "var = value"
231      */
232     while (line && isspace((unsigned char)(*line)))
233         line++;
234     if ( *line == '=' ) {
235         line++;
236         while (line && isspace((unsigned char)(*line))) {
237             line++;
238         }
239     }
240     line = read_config_read_data(ASN_INTEGER,   line, &value, &tmpint);
241 
242     /*
243      * Convert from cron-style specifications into bits
244      */
245     _sched_convert_bits( minConf,  minVal,  8, 60, 0 );
246     _sched_convert_bits( hourConf, hourVal, 3, 24, 0 );
247     memset(dateVal+4, 0, 4); /* Clear the reverse day bits */
248     _sched_convert_bits( dateConf, dateVal, 4, 31, 1 );
249     _sched_convert_bits( monConf,  monVal,  2, 12, 1 );
250     _sched_convert_bits( dayConf, &dayVal,  1,  8, 0 );
251     if ( dayVal & 0x01 ) {  /* sunday(7) = sunday(0) */
252          dayVal |= 0x80;
253          dayVal &= 0xfe;
254     }
255 
256     /*
257      * Create an entry in the schedTable
258      */
259     row   = schedTable_createEntry("snmpd.conf", buf);
260     if (!row || !row->data) {
261         config_perror("create schedule entry failure");
262         return;
263     }
264     entry = (struct schedTable_entry *)row->data;
265 
266     entry->schedWeekDay = dayVal;
267     memcpy(entry->schedMonth,  monVal,  2);
268     memcpy(entry->schedDay,    dateVal, 4+4);
269     memcpy(entry->schedHour,   hourVal, 3);
270     memcpy(entry->schedMinute, minVal,  8);
271 
272     memcpy(entry->schedVariable, variable, var_len*sizeof(oid));
273     entry->schedVariable_len = var_len;
274     entry->schedValue        = value;
275 
276     if ( !strcmp( token, "at" ))
277         entry->schedType     = SCHED_TYPE_ONESHOT;
278     else
279         entry->schedType     = SCHED_TYPE_CALENDAR;
280     entry->schedStorageType  = ST_READONLY;  /* or PERMANENT */
281     entry->flags             = SCHEDULE_FLAG_ENABLED |
282                                SCHEDULE_FLAG_ACTIVE  |
283                                SCHEDULE_FLAG_VALID;
284     entry->session           = netsnmp_query_get_default_session();
285     sched_nextTime( entry );
286 }
287 
288 
289 /* ========================================
290  *
291  * Handlers for persistent schedule entries
292  *
293  * ======================================== */
294 
295 void
parse_schedTable(const char * token,char * line)296 parse_schedTable( const char *token, char *line )
297 {
298     char   owner[SCHED_STR1_LEN+1];
299     char   name[ SCHED_STR1_LEN+1];
300     char   time_bits[22];  /* schedWeekDay..schedMinute */
301     void  *vp;
302     size_t len;
303     netsnmp_tdata_row       *row;
304     struct schedTable_entry *entry;
305 
306     DEBUGMSGTL(("disman:schedule:conf", "Parsing schedTable config...  "));
307 
308     /*
309      * Read in the index information for this entry
310      *  and create a (non-fixed) data structure for it.
311      */
312     memset( owner, 0, sizeof(owner));
313     memset( name,  0, sizeof(name));
314     len  = SCHED_STR1_LEN; vp = owner;
315     line = read_config_read_data(ASN_OCTET_STR, line, &vp,  &len);
316     len  = SCHED_STR1_LEN; vp = name;
317     line = read_config_read_data(ASN_OCTET_STR, line, &vp,  &len);
318     row  = schedTable_createEntry(owner, name);
319     if (!row || !row->data) {
320         config_perror("create schedule entry failure");
321         return;
322     }
323     entry = (struct schedTable_entry *)row->data;
324     DEBUGMSG(("disman:schedule:conf", "(%s, %s) ", owner, name));
325 
326     /*
327      * Read in the column values.
328      */
329     len  = SCHED_STR2_LEN; vp = entry->schedDescr;
330     line = read_config_read_data(ASN_OCTET_STR, line, &vp,  &len);
331     line = read_config_read_data(ASN_UNSIGNED,  line,
332                                      &entry->schedInterval, NULL);
333         /* Unpick the various timed bits */
334     len  = 22; vp = time_bits;
335     line = read_config_read_data(ASN_OCTET_STR, line, &vp,  &len);
336     entry->schedWeekDay  = time_bits[0];
337     entry->schedMonth[0] = time_bits[1];
338     entry->schedMonth[1] = time_bits[2];
339     entry->schedHour[0]  = time_bits[11];
340     entry->schedHour[1]  = time_bits[12];
341     entry->schedHour[2]  = time_bits[13];
342     memcpy(entry->schedDay,    time_bits+3,  8);
343     memcpy(entry->schedMinute, time_bits+14, 8);
344 
345     len  = SCHED_STR1_LEN; vp = entry->schedContextName;
346     line = read_config_read_data(ASN_OCTET_STR, line, &vp,  &len);
347     len  = MAX_OID_LEN;    vp = entry->schedVariable;
348     line = read_config_read_data(ASN_OBJECT_ID, line, &vp,  &len);
349     entry->schedVariable_len = len;
350     line = read_config_read_data(ASN_INTEGER,  line,
351                                      &entry->schedValue, NULL);
352     line = read_config_read_data(ASN_UNSIGNED, line,
353                                      &entry->schedType,  NULL);
354     line = read_config_read_data(ASN_UNSIGNED, line, &len, NULL);
355     entry->flags |= (len /* & WHAT ?? */);
356     /* XXX - Will need to read in the 'iquery' access information */
357     entry->flags |= SCHEDULE_FLAG_VALID;
358 
359     DEBUGMSG(("disman:schedule:conf", "\n"));
360 }
361 
362 /*
363  * Save dynamically-configured schedTable entries into persistent storage
364  */
365 int
store_schedTable(int majorID,int minorID,void * serverarg,void * clientarg)366 store_schedTable(int majorID, int minorID, void *serverarg, void *clientarg)
367 {
368     char              line[SNMP_MAXBUF];
369     char              time_bits[22];  /* schedWeekDay..schedMinute */
370     char             *cptr, *cp;
371     void             *vp;
372     size_t            tint;
373     netsnmp_tdata_row *row;
374     struct schedTable_entry *entry;
375 
376 
377     DEBUGMSGTL(( "disman:schedule:conf", "Storing schedTable:\n"));
378 
379     for (row = netsnmp_tdata_row_first( schedule_table );
380          row;
381          row = netsnmp_tdata_row_next(  schedule_table, row )) {
382 
383         if (!row->data)
384             continue;
385         entry = (struct schedTable_entry *)row->data;
386 
387         /*
388          * Only save (dynamically-created) 'nonVolatile' entries
389          *    (XXX - what about dynamic 'permanent' entries ??)
390          */
391         if (entry->schedStorageType != ST_NONVOLATILE )
392             continue;
393         DEBUGMSGTL(( "disman:schedule:conf", "  Storing (%s, %s)\n",
394                              entry->schedOwner, entry->schedName));
395 
396         memset(line, 0, sizeof(line));
397         strcpy(line, "_schedTable ");
398         cptr = line + strlen(line);
399 
400         cp   = entry->schedOwner;     tint = strlen( cp );
401         cptr = read_config_store_data(ASN_OCTET_STR, cptr, &cp,  &tint );
402         cp   = entry->schedName;      tint = strlen( cp );
403         cptr = read_config_store_data(ASN_OCTET_STR, cptr, &cp,  &tint );
404         cp   = entry->schedDescr;     tint = strlen( cp );
405         cptr = read_config_store_data(ASN_OCTET_STR, cptr, &cp,  &tint );
406         tint = entry->schedInterval;
407         cptr = read_config_store_data(ASN_UNSIGNED,  cptr, &tint, NULL );
408 
409         /* Combine all the timed bits into a single field */
410         time_bits[0]  = entry->schedWeekDay;
411         time_bits[1]  = entry->schedMonth[0];
412         time_bits[2]  = entry->schedMonth[1];
413         time_bits[11] = entry->schedHour[0];
414         time_bits[12] = entry->schedHour[1];
415         time_bits[13] = entry->schedHour[2];
416         memcpy(time_bits+3,  entry->schedDay,    8);
417         memcpy(time_bits+14, entry->schedMinute, 8);
418         vp   = time_bits;    tint = 22;
419         cptr = read_config_store_data(ASN_OCTET_STR, cptr, &vp, &tint );
420 
421         cp   = entry->schedContextName; tint = strlen( cp );
422         cptr = read_config_store_data(ASN_OCTET_STR, cptr, &cp,  &tint );
423         vp   = entry->schedVariable;
424         tint = entry->schedVariable_len;
425         cptr = read_config_store_data(ASN_OBJECT_ID, cptr, &vp,  &tint );
426         tint = entry->schedValue;
427         cptr = read_config_store_data(ASN_INTEGER,   cptr, &tint, NULL );
428         tint = entry->schedType;
429         cptr = read_config_store_data(ASN_UNSIGNED,  cptr, &tint, NULL );
430         tint = entry->flags /* & WHAT ?? */;
431         cptr = read_config_store_data(ASN_UNSIGNED,  cptr, &tint, NULL );
432         /* XXX - Need to store the 'iquery' access information */
433         snmpd_store_config(line);
434     }
435     DEBUGMSGTL(( "disman:schedule:conf", "  done.\n"));
436     return SNMPERR_SUCCESS;
437 }
438