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