1 /* HAO.C        (c) Copyright Bernard van der Helm, 2002-2009        */
2 /*             Hercules Automatic Operator Implementation            */
3 
4 /*---------------------------------------------------------------------------*/
5 /* file: hao.c                                                               */
6 /*                                                                           */
7 /* Implementation of the automatic operator within the Hercules emulator.    */
8 /*                                                                           */
9 /*                            (c) Copyright Bernard van der Helm, 2002-2009  */
10 /*                            Noordwijkerhout, The Netherlands.              */
11 /*                                                                           */
12 /*---------------------------------------------------------------------------*/
13 
14 
15 
16 #include "hstdinc.h"
17 
18 #define _HAO_C_
19 #define _HENGINE_DLL_
20 
21 #include "hercules.h"
22 
23 #if defined(OPTION_HAO)
24 
25 /*---------------------------------------------------------------------------*/
26 /* constants                                                                 */
27 /*---------------------------------------------------------------------------*/
28 #define HHCAO001I "HHCAO001I Hercules Automatic Operator thread started;\n" \
29                   "          tid="TIDPAT", pri=%d, pid=%d\n"
30 #define HHCAO002I "HHCAO002I Hercules Automatic Operator thread ended\n"
31 #define HHCAO003I "HHCAO003I Firing command: '%s'\n"
32 #define HHCAO004I "HHCAO004I The defined Automatic Operator rule(s) are:\n"
33 #define HHCAO005I "HHCAO005I %02d: '%s' -> '%s'\n"
34 #define HHCAO006I "HHCAO006I %d rule(s) displayed\n"
35 #define HHCAO007E "HHCAO007E Unknown hao command, valid commands are:\n" \
36                   "  hao tgt <tgt> : define target rule (pattern) to react on\n" \
37                   "  hao cmd <cmd> : define command for previously defined rule\n" \
38                   "  hao list <n>  : list all rules/commands or only at index <n>\n" \
39                   "  hao del <n>   : delete the rule at index <n>\n" \
40                   "  hao clear     : delete all rules (stops automatic operator)\n"
41 #define HHCAO008E "HHCAO008E No rule defined at index %d\n"
42 #define HHCAO009E "HHCAO009E Invalid index, index must be between 0 and %d\n"
43 #define HHCAO010E "HHCAO010E Target not added, table full\n"
44 #define HHCAO011E "HHCAO011E Tgt command given, but cmd command expected\n"
45 #define HHCAO012E "HHCAO012E Empty target specified\n"
46 #define HHCAO013E "HHCAO013E Target not added, duplicate found in table\n"
47 #define HHCAO014E "HHCAO014E %s\n"
48 #define HHCAO015E "HHCAO015E %s\n"
49 #define HHCAO016I "HHCAO016I Target placed at index %d\n"
50 #define HHCAO017E "HHCAO017E Cmd command given, but tgt command expected\n"
51 #define HHCAO018E "HHCAO018E Empty command specified\n"
52 #define HHCAO019E "HHCAO019E Command not added; causes loop with target at index %d\n"
53 #define HHCAO020I "HHCAO020I Command placed at index %d\n"
54 #define HHCAO021E "HHCAO021E Target not added, causes loop with command at index %d\n"
55 #define HHCAO022I "HHCAO022I All automatic operation rules cleared\n"
56 #define HHCAO023E "HHCAO023E hao del command given without a valid index\n"
57 #define HHCAO024E "HHCAO024E Rule at index %d not deleted, already empty\n"
58 #define HHCAO025I "HHCAO025I Rule at index %d succesfully deleted\n"
59 #define HHCA0026E "HHCA0026E Command not added, may cause dead locks\n"
60 
61 #define HAO_WKLEN    256    /* (maximum message length able to tolerate) */
62 #define HAO_MAXRULE  64     /* (purely arbitrary and easily increasable) */
63 #define HAO_MAXCAPT  9      /* (maximum number of capturing groups)      */
64 
65 /*---------------------------------------------------------------------------*/
66 /* local variables                                                           */
67 /*---------------------------------------------------------------------------*/
68 static LOCK     ao_lock;
69 static regex_t  ao_preg[HAO_MAXRULE];
70 static char    *ao_cmd[HAO_MAXRULE];
71 static char    *ao_tgt[HAO_MAXRULE];
72 static char     ao_msgbuf[LOG_DEFSIZE+1];   /* (plus+1 for NULL termination) */
73 
74 /*---------------------------------------------------------------------------*/
75 /* function prototypes                                                       */
76 /*---------------------------------------------------------------------------*/
77 DLL_EXPORT int   hao_initialize(void);
78 DLL_EXPORT void  hao_command(char *cmd);
79 DLL_EXPORT void  hao_message(char *buf);
80 static     void  hao_clear(void);
81 static     void  hao_cmd(char *arg);
82 static     void  hao_cpstrp(char *dest, char *src);
83 static     void  hao_del(char *arg);
84 static     void  hao_list(char *arg);
85 static     void  hao_tgt(char *arg);
86 static     void* hao_thread(void* dummy);
87 
88 /*---------------------------------------------------------------------------*/
89 /* void hao_initialize(void)                                                 */
90 /*                                                                           */
91 /* This function is called at system startup by impl.c 'process_rc_file'     */
92 /* It initializes all global variables.                                      */
93 /*---------------------------------------------------------------------------*/
hao_initialize(void)94 DLL_EXPORT int hao_initialize(void)
95 {
96   int i = 0;
97 
98   initialize_lock(&ao_lock);
99 
100   /* serialize */
101   obtain_lock(&ao_lock);
102 
103   /* initialize variables */
104   for(i = 0; i < HAO_MAXRULE; i++)
105   {
106     ao_cmd[i] = NULL;
107     ao_tgt[i] = NULL;
108   }
109 
110   /* initialize message buffer */
111   memset(ao_msgbuf, 0, sizeof(ao_msgbuf));
112 
113   /* Start message monitoring thread */
114   if ( create_thread (&sysblk.haotid, JOINABLE,
115     hao_thread, NULL, "hao_thread") )
116   {
117     i = FALSE;
118   }
119   else
120     i = TRUE;
121 
122   release_lock(&ao_lock);
123 
124   return(i);
125 }
126 
127 /*---------------------------------------------------------------------------*/
128 /* void hao_command(char *cmd)                                               */
129 /*                                                                           */
130 /* Within panel this function is called when a command is given that starts  */
131 /* with the string hao. Here we check if a correct hao command is requested, */
132 /* otherwise a help is printed.                                              */
133 /*---------------------------------------------------------------------------*/
hao_command(char * cmd)134 DLL_EXPORT void hao_command(char *cmd)
135 {
136   char work[HAO_WKLEN];
137   char work2[HAO_WKLEN];
138 
139   /* copy and strip spaces */
140   hao_cpstrp(work, cmd);
141 
142   /* again without starting hao */
143   hao_cpstrp(work2, &work[3]);
144 
145   if(!strncasecmp(work2, "tgt", 3))
146   {
147     /* again without starting tgt */
148     hao_cpstrp(work, &work2[3]);
149     hao_tgt(work);
150     return;
151   }
152 
153   if(!strncasecmp(work2, "cmd", 3))
154   {
155     /* again without starting cmd */
156     hao_cpstrp(work, &work2[3]);
157     hao_cmd(work);
158     return;
159   }
160 
161   if(!strncasecmp(work2, "del", 3))
162   {
163     /* again without starting del */
164     hao_cpstrp(work, &work2[3]);
165     hao_del(work);
166     return;
167   }
168 
169   if(!strncasecmp(work2, "list", 4))
170   {
171     /* again without starting list */
172     hao_cpstrp(work, &work2[4]);
173     hao_list(work);
174     return;
175   }
176 
177   if(!strncasecmp(work2, "clear", 4))
178   {
179     hao_clear();
180     return;
181   }
182 
183   logmsg(HHCAO007E);
184 }
185 
186 /*---------------------------------------------------------------------------*/
187 /* hao_cpstrp(char *dest, char *src)                                         */
188 /*                                                                           */
189 /* This function copies the string from src to dest, without the trailing    */
190 /* and ending spaces.                                                        */
191 /*---------------------------------------------------------------------------*/
hao_cpstrp(char * dest,char * src)192 static void hao_cpstrp(char *dest, char *src)
193 {
194   int i;
195 
196   for(i = 0; src[i] == ' '; i++);
197   strncpy(dest, &src[i], HAO_WKLEN);
198   dest[HAO_WKLEN-1] = 0;
199   for(i = strlen(dest); i && dest[i - 1] == ' '; i--);
200   dest[i] = 0;
201 }
202 
203 /*---------------------------------------------------------------------------*/
204 /* void hao_tgt(char *arg)                                                   */
205 /*                                                                           */
206 /* This function is given when the hao tgt command is given. A free slot is  */
207 /* to be found and filled with the rule. There will be loop checking.        */
208 /*---------------------------------------------------------------------------*/
hao_tgt(char * arg)209 static void hao_tgt(char *arg)
210 {
211   int i;
212   int j;
213   int rc;
214   char work[HAO_WKLEN];
215 
216   /* serialize */
217   obtain_lock(&ao_lock);
218 
219   /* find a free slot */
220   for(i = 0; ao_tgt[i] && i < HAO_MAXRULE; i++)
221 
222   /* check for table full */
223   if(i == HAO_MAXRULE)
224   {
225     release_lock(&ao_lock);
226     logmsg(HHCAO010E);
227     return;
228   }
229 
230   /* check if not command is expected */
231   for(j = 0; j < HAO_MAXRULE; j++)
232   {
233     if(ao_tgt[j] && !ao_cmd[j])
234     {
235       release_lock(&ao_lock);
236       logmsg(HHCAO011E);
237       return;
238     }
239   }
240 
241   /* check for empty target */
242   if(!strlen(arg))
243   {
244     release_lock(&ao_lock);
245     logmsg(HHCAO012E);
246     return;
247   }
248 
249   /* check for duplicate targets */
250   for(j = 0; j < HAO_MAXRULE; j++)
251   {
252     if(ao_tgt[j] && !strcmp(arg, ao_tgt[j]))
253     {
254       release_lock(&ao_lock);
255       logmsg(HHCAO013E);
256       return;
257     }
258   }
259 
260   /* compile the target string */
261   rc = regcomp(&ao_preg[i], arg, REG_EXTENDED);
262 
263   /* check for error */
264   if(rc)
265   {
266     release_lock(&ao_lock);
267 
268     /* place error in work */
269     regerror(rc, (const regex_t *) &ao_preg[i], work, HAO_WKLEN);
270     logmsg(HHCAO014E, work);
271     return;
272   }
273 
274   /* check for possible loop */
275   for(j = 0; j < HAO_MAXRULE; j++)
276   {
277     if(ao_cmd[j] && !regexec(&ao_preg[i], ao_cmd[j], 0, NULL, 0))
278     {
279       release_lock(&ao_lock);
280       regfree(&ao_preg[i]);
281       logmsg(HHCAO021E, i);
282       return;
283     }
284   }
285 
286   /* duplicate the target */
287   ao_tgt[i] = strdup(arg);
288 
289   /* check duplication */
290   if(!ao_tgt[i])
291   {
292     release_lock(&ao_lock);
293     regfree(&ao_preg[i]);
294     logmsg(HHCAO015E, strerror(ENOMEM));
295     return;
296   }
297 
298   release_lock(&ao_lock);
299   logmsg(HHCAO016I, i);
300 }
301 
302 /*---------------------------------------------------------------------------*/
303 /* void hao_cmd(char *arg)                                                   */
304 /*                                                                           */
305 /* This function is called when the hao cmd command is given. It searches    */
306 /* the index of the last given hao tgt command. Does some checking and fills */
307 /* the entry with the given command. There will be loop checking             */
308 /*---------------------------------------------------------------------------*/
hao_cmd(char * arg)309 static void hao_cmd(char *arg)
310 {
311   int i;
312   int j;
313 
314   /* serialize */
315   obtain_lock(&ao_lock);
316 
317   /* find the free slot */
318   for(i = 0; ao_cmd[i] && i < HAO_MAXRULE; i++);
319 
320   /* check for table full -> so tgt cmd expected */
321   if(i == HAO_MAXRULE)
322   {
323     release_lock(&ao_lock);
324     logmsg(HHCAO017E);
325     return;
326   }
327 
328   /* check if target is given */
329   if(!ao_tgt[i])
330   {
331     release_lock(&ao_lock);
332     logmsg(HHCAO017E);
333     return;
334   }
335 
336   /* check for empty cmd string */
337   if(!strlen(arg))
338   {
339     release_lock(&ao_lock);
340     logmsg(HHCAO018E);
341     return;
342   }
343 
344   /* check for hao command, prevent deadlock */
345   for(j = 0; !strncasecmp(&arg[j], "herc ", 4); j += 5);
346   if(!strcasecmp(&arg[j], "hao") || !strncasecmp(&arg[j], "hao ", 4))
347   {
348     release_lock(&ao_lock);
349     logmsg(HHCA0026E);
350     return;
351   }
352 
353   /* check for possible loop */
354   for(j = 0; j < HAO_MAXRULE; j++)
355   {
356     if(ao_tgt[j] && !regexec(&ao_preg[j], arg, 0, NULL, 0))
357     {
358       release_lock(&ao_lock);
359       logmsg(HHCAO019E, j);
360       return;
361     }
362   }
363 
364   /* duplicate the string */
365   ao_cmd[i] = strdup(arg);
366 
367   /* check duplication */
368   if(!ao_cmd[i])
369   {
370     release_lock(&ao_lock);
371     logmsg(HHCAO015E, strerror(ENOMEM));
372     return;
373   }
374 
375   release_lock(&ao_lock);
376   logmsg(HHCAO020I, i);
377 }
378 
379 /*---------------------------------------------------------------------------*/
380 /* void hao_del(char *arg)                                                   */
381 /*                                                                           */
382 /* This function is called when the command hao del is given. the rule in    */
383 /* the given index is cleared.                                               */
384 /*---------------------------------------------------------------------------*/
hao_del(char * arg)385 static void hao_del(char *arg)
386 {
387   int i;
388   int rc;
389 
390   /* read the index number to delete */
391   rc = sscanf(arg, "%d", &i);
392   if(!rc || rc == -1)
393   {
394     logmsg(HHCAO023E);
395     return;
396   }
397 
398   /* check if index is valid */
399   if(i < 0 || i >= HAO_MAXRULE)
400   {
401     logmsg(HHCAO009E, HAO_MAXRULE - 1);
402     return;
403   }
404 
405   /* serialize */
406   obtain_lock(&ao_lock);
407 
408   /* check if entry exists */
409   if(!ao_tgt[i])
410   {
411     release_lock(&ao_lock);
412     logmsg(HHCAO024E, i);
413     return;
414   }
415 
416   /* delete the entry */
417   free(ao_tgt[i]);
418   ao_tgt[i] = NULL;
419   regfree(&ao_preg[i]);
420   if(ao_cmd[i])
421   {
422     free(ao_cmd[i]);
423     ao_cmd[i] = NULL;
424   }
425 
426   release_lock(&ao_lock);
427   logmsg(HHCAO025I, i);
428 }
429 
430 /*---------------------------------------------------------------------------*/
431 /* void hao_list(char *arg)                                                  */
432 /*                                                                           */
433 /* this function is called when the hao list command is given. It lists all  */
434 /* rules. When given an index, only that index will be showed.               */
435 /*---------------------------------------------------------------------------*/
hao_list(char * arg)436 static void hao_list(char *arg)
437 {
438   int i;
439   int rc;
440   int size;
441 
442   rc = sscanf(arg, "%d", &i);
443   if(!rc || rc == -1)
444   {
445     /* list all rules */
446     logmsg(HHCAO004I);
447     size = 0;
448 
449     /* serialize */
450     obtain_lock(&ao_lock);
451 
452     for(i = 0; i < HAO_MAXRULE; i++)
453     {
454       if(ao_tgt[i])
455       {
456         logmsg(HHCAO005I, i, ao_tgt[i], (ao_cmd[i] ? ao_cmd[i] : "<not specified>"));
457         size++;
458       }
459     }
460     release_lock(&ao_lock);
461     logmsg(HHCAO006I, size);
462   }
463   else
464   {
465     /* list specific index */
466     if(i < 0 || i >= HAO_MAXRULE)
467       logmsg(HHCAO009E, HAO_MAXRULE - 1);
468     else
469     {
470       /* serialize */
471       obtain_lock(&ao_lock);
472 
473       if(!ao_tgt[i])
474         logmsg(HHCAO008E, i);
475       else
476         logmsg(HHCAO005I, i, ao_tgt[i], (ao_cmd[i] ? ao_cmd[i] : "not specified"));
477 
478       release_lock(&ao_lock);
479     }
480   }
481 }
482 
483 /*---------------------------------------------------------------------------*/
484 /* void hao_clear(void)                                                      */
485 /*                                                                           */
486 /* This function is called when the hao clear command is given. This         */
487 /* function just clears all defined rules. Handy command for panic           */
488 /* situations.                                                               */
489 /*---------------------------------------------------------------------------*/
hao_clear(void)490 static void hao_clear(void)
491 {
492   int i;
493 
494   /* serialize */
495   obtain_lock(&ao_lock);
496 
497   /* clear all defined rules */
498   for(i = 0; i < HAO_MAXRULE; i++)
499   {
500     if(ao_tgt[i])
501     {
502       free(ao_tgt[i]);
503       ao_tgt[i] = NULL;
504       regfree(&ao_preg[i]);
505     }
506     if(ao_cmd[i])
507     {
508       free(ao_cmd[i]);
509       ao_cmd[i] = NULL;
510     }
511   }
512 
513   release_lock(&ao_lock);
514   logmsg(HHCAO022I);
515 }
516 
517 /*---------------------------------------------------------------------------*/
518 /* void* hao_thread(void* dummy)                                             */
519 /*                                                                           */
520 /* This thread is created by hao_initialize. It examines every message       */
521 /* printed. Here we check if a rule applies to the message. If so we fire    */
522 /* the command within the rule.                                              */
523 /*---------------------------------------------------------------------------*/
hao_thread(void * dummy)524 static void* hao_thread(void* dummy)
525 {
526   char*  msgbuf  = NULL;
527   int    msgidx  = -1;
528   int    msgamt  = 0;
529   char*  msgend  = NULL;
530   char   svchar  = 0;
531   int    bufamt  = 0;
532 
533   UNREFERENCED(dummy);
534 
535   logmsg(HHCAO001I, thread_id(), getpriority(PRIO_PROCESS,0), getpid());
536 
537   /* Wait for panel thread to engage */
538   while (!sysblk.panel_init && !sysblk.shutdown)
539     usleep( 10 * 1000 );
540 
541   /* Do until shutdown */
542   while (!sysblk.shutdown && msgamt >= 0)
543   {
544     /* wait for message data */
545     if ((msgamt = log_read(&msgbuf, &msgidx, LOG_BLOCK)) > 0 )
546     {
547       /* append to existing data */
548       if (msgamt > (int)((sizeof(ao_msgbuf) - 1) - bufamt) )
549           msgamt = (int)((sizeof(ao_msgbuf) - 1) - bufamt);
550       strncpy( &ao_msgbuf[bufamt], msgbuf, msgamt );
551       ao_msgbuf[bufamt += msgamt] = 0;
552       msgbuf = ao_msgbuf;
553 
554       /* process only complete messages */
555       while (NULL != (msgend = strchr(msgbuf,'\n')))
556       {
557         /* null terminate message */
558         svchar = *(msgend+1);
559         *(msgend+1) = 0;
560 
561         /* process message */
562         hao_message(msgbuf);
563 
564         /* restore destroyed byte */
565         *(msgend+1) = svchar;
566         msgbuf = msgend+1;
567       }
568 
569       /* shift message buffer */
570       memmove( ao_msgbuf, msgbuf, bufamt -= (msgbuf - ao_msgbuf) );
571     }
572   }
573 
574   logmsg(HHCAO002I);
575   return NULL;
576 }
577 
578 /*---------------------------------------------------------------------------*/
579 /* size_t hao_subst(char *str, size_t soff, size_t eoff,                     */
580 /*              char *cmd, size_t coff, size_t csize)                        */
581 /*                                                                           */
582 /* This function copies a substring of the original string into              */
583 /* the command buffer. The input parameters are:                             */
584 /*      str     the original string                                          */
585 /*      soff    starting offset of the substring in the original string      */
586 /*      eoff    offset of the first character following the substring        */
587 /*      cmd     the destination command buffer                               */
588 /*      coff    offset in the command buffer to copy the substring           */
589 /*      csize   size of the command buffer (including terminating zero)      */
590 /* The return value is the number of characters copied.                      */
591 /*---------------------------------------------------------------------------*/
hao_subst(char * str,size_t soff,size_t eoff,char * cmd,size_t coff,size_t csize)592 static size_t hao_subst(char *str, size_t soff, size_t eoff,
593         char *cmd, size_t coff, size_t csize)
594 {
595   size_t len = eoff - soff;
596 
597   if (soff + len > strlen(str)) len = strlen(str) - soff;
598   if (coff + len > csize-1) len = csize-1 - coff;
599   memcpy(cmd + coff, str + soff, len);
600   return len;
601 }
602 
603 /*---------------------------------------------------------------------------*/
604 /* void hao_message(char *buf)                                               */
605 /*                                                                           */
606 /* This function is called by hao_thread whenever a message is about to be   */
607 /* printed. Here we check if a rule applies to the message. If so we fire    */
608 /* the command within the rule.                                              */
609 /*---------------------------------------------------------------------------*/
hao_message(char * buf)610 DLL_EXPORT void hao_message(char *buf)
611 {
612   char work[HAO_WKLEN];
613   char cmd[HAO_WKLEN];
614   regmatch_t rm[HAO_MAXCAPT+1];
615   int i, j, k, numcapt;
616   size_t n;
617   char *p;
618 
619   /* copy and strip spaces */
620   hao_cpstrp(work, buf);
621 
622   /* strip the herc prefix */
623   while(!strncmp(work, "herc", 4))
624     hao_cpstrp(work, &work[4]);
625 
626   /* don't react on own messages */
627   if(!strncmp(work, "HHCAO", 5))
628     return;
629 
630   /* don't react on own commands */
631   if(!strncasecmp(work, "hao", 3))
632     return;
633 
634   /* also from the .rc file */
635   if(!strncasecmp(work, "> hao", 5))
636     return;
637 
638   /* serialize */
639   obtain_lock(&ao_lock);
640 
641   /* check all defined rules */
642   for(i = 0; i < HAO_MAXRULE; i++)
643   {
644     if(ao_tgt[i] && ao_cmd[i])  /* complete rule defined in this slot? */
645     {
646       /* does this rule match our message? */
647       if (regexec(&ao_preg[i], work, HAO_MAXCAPT+1, rm, 0) == 0)
648       {
649         /* count the capturing group matches */
650         for (j = 0; j <= HAO_MAXCAPT && rm[j].rm_so >= 0; j++);
651         numcapt = j - 1;
652 
653         /* copy the command and process replacement patterns */
654         for (n=0, p=ao_cmd[i]; *p && n < sizeof(cmd)-1; ) {
655           /* replace $$ by $ */
656           if (*p == '$' && p[1] == '$') {
657             cmd[n++] = '$';
658             p += 2;
659             continue;
660           }
661           /* replace $` by characters to the left of the match */
662           if (*p == '$' && p[1] == '`') {
663             n += hao_subst(work, 0, rm[0].rm_so, cmd, n, sizeof(cmd));
664             p += 2;
665             continue;
666           }
667           /* replace $' by characters to the right of the match */
668           if (*p == '$' && p[1] == '\'') {
669             n += hao_subst(work, rm[0].rm_eo, strlen(work), cmd, n, sizeof(cmd));
670             p += 2;
671             continue;
672           }
673           /* replace $1..$99 by the corresponding capturing group */
674           if (*p == '$' && isdigit(p[1])) {
675             if (isdigit(p[2])) {
676               j = (p[1]-'0') * 10 + (p[2]-'0');
677               k = 3;
678             } else {
679               j = p[1]-'0';
680               k = 2;
681             }
682             if (j > 0 && j <= numcapt) {
683               n += hao_subst(work, rm[j].rm_so, rm[j].rm_eo, cmd, n, sizeof(cmd));
684               p += k;
685               continue;
686             }
687           }
688           /* otherwise copy one character */
689           cmd[n++] = *p++;
690         }
691         cmd[n] = '\0';
692 
693         /* issue command for this rule */
694         logmsg(HHCAO003I, cmd);
695         panel_command(cmd);
696       }
697     }
698   }
699   release_lock(&ao_lock);
700 }
701 
702 #endif /* defined(OPTION_HAO) */
703 
704