/************************************************************************ File: daemon.pc Written by: Scott Urman, Language Support Last Modified: 11/7/94 This is the source code for the daemon listener to implement dynamic sql and system commands from plsql. The program accepts three 'daemon commands': STOP: causes the daemon to disconnect from Oracle and exit. SYSTEM: causes the daemon to execute the next item on the pipe as a operating system command. SQL: causes the daemon to execute the next item on the pipe as an sql statement. Also returns the sqlcode resulting from the statement. The daemon commands are received over the pipe named 'daemon'. As part of the first message sent along this pipe, the name of the pipe to use for the return session is passed. Modifications by Steve Rea: 7/17/03 - If the same command is executed more than 5 times in a row, assume the connection has been lost to the database, and shut down daemon. Otherwise, it gets in an infinite loop executing the last command that came through the pipe over and over. ************************************************************************/ #include #include EXEC SQL include sqlca; EXEC SQL begin declare section; char *uid = "scott/tiger"; /* User/password to connect to Oracle <<< CHANGE THIS <<< */ int status; /* Return value for dbms_pipe.send_message and dbms_pipe.receive_message */ varchar command[20]; /* Daemon command to execute */ varchar value[2000]; /* Value (SQL statement or system command) associated with previous daemon command */ varchar return_name[30]; /* Name of the pipe on which to send the results */ varchar prev_value[2000]; int prev_count = 0; EXEC SQL end declare section; /* This is the error handler for connecting to Oracle. If we failed on the connection attempt, we need to exit the program. */ void connect_error() { char msg_buffer[512]; int msg_length; int buffer_size = 512; EXEC SQL whenever sqlerror continue; sqlglm(msg_buffer, &buffer_size, &msg_length); printf("Daemon error while connecting:\n"); printf("%.*s\n", msg_length, msg_buffer); printf("Daemon quitting.\n"); exit(1); } /* This is the general error handler. Note that we don't exit the program in this case. We just print the error and continue. This is because any errors probably will not affect future operations, and we should keep the daemon running. This of course depends on the error, and you may want to change this behavior. */ void sql_error() { char msg_buffer[512]; int msg_length; int buffer_size = 512; EXEC SQL whenever sqlerror continue; sqlglm(msg_buffer, &buffer_size, &msg_length); printf("Daemon error while executing:\n"); printf("%.*s\n", msg_length, msg_buffer); printf("Daemon continuing.\n"); } main() { strcpy((char *)prev_value.arr, "~~~"); prev_value.len = strlen((char *)prev_value.arr); EXEC SQL whenever sqlerror do connect_error(); EXEC SQL connect :uid; printf("Daemon connected.\n"); EXEC SQL whenever sqlerror do sql_error(); printf("Daemon waiting...\n"); while (1) { /* Wait for a message to be received, using pipe daemon. */ EXEC SQL EXECUTE begin :status := dbms_pipe.receive_message('daemon'); if :status = 0 then dbms_pipe.unpack_message(:command); end if; end; END-EXEC; if (status == 0) { /* At this point, we have successfully received a message. Now we need to determine which daemon command to execute. */ command.arr[command.len] = '\0'; if (!strcmp((char *)command.arr, "STOP")) { /* STOP command received. Simply exit the program. */ printf("Daemon exiting.\n"); break; } else if (!strcmp((char *)command.arr, "SYSTEM")) { /* SYSTEM command received. Unpack the next 2 values. These will be the name of the return pipe, and the command to pass to the operating system. */ EXEC SQL EXECUTE begin dbms_pipe.unpack_message(:return_name); dbms_pipe.unpack_message(:value); end; END-EXEC; value.arr[value.len] = '\0'; printf("Will execute system command '%s'\n", value.arr); /* Execute the command. */ status = system(value.arr); /* Send a message back to indicate that the command has been executed. Also send the result of the system command. Use the pipe passed in from the first message for this. */ EXEC SQL EXECUTE begin dbms_pipe.pack_message('done'); dbms_pipe.pack_message(:status); :status := dbms_pipe.send_message(:return_name); end; END-EXEC; if (status) { printf("Daemon error while responding to system command."); printf(" status: %d\n", status); } } else if (!strcmp((char *)command.arr, "SQL")) { /* SQL command received. Unpack the next 2 values. These will be the name of the return pipe, and the SQL command to execute. */ EXEC SQL EXECUTE begin dbms_pipe.unpack_message(:return_name); dbms_pipe.unpack_message(:value); end; END-EXEC; value.arr[value.len] = '\0'; printf("Will execute sql command '%s'\n", value.arr); /* Execute the command. Note that we don't want to go to the error handler if there is a problem - we just pass the code back. */ EXEC SQL whenever sqlerror continue; EXEC SQL EXECUTE IMMEDIATE :value; status = sqlca.sqlcode; /* Reset the error handler, and send a message back to indicate that the command has been executed. Also send the sqlcode. Use the pipe passed in from the first message for this. */ EXEC SQL whenever sqlerror do sql_error(); EXEC SQL EXECUTE begin dbms_pipe.pack_message('done'); dbms_pipe.pack_message(:status); :status := dbms_pipe.send_message(:return_name); end; END-EXEC; if (status) { printf("Daemon error while responding to sql command."); printf(" status: %d\n", status); } } else { /* Invalid daemon command received. */ printf("Daemon error: invalid command '%s' received.\n", command.arr); } } /* If the same command is executed more than 5 times in a row, assume the connection has been lost to the database, and shut down daemon. */ if ((!strcmp((char *)command.arr, "SYSTEM")) || (!strcmp((char *)command.arr, "SQL"))) { if (!strcmp((char *)value.arr, (char *)prev_value.arr)) { prev_count++; if (prev_count >= 5) { printf("Daemon exiting. Lost connection (looping on same message)? Message is:\n%s\n", value.arr); /* Put this in if you want a lost connection notice e-mailed to you (CHANGE myname@mysite.edu) sprintf((char *)prev_value.arr, "mail -s 'Daemon exiting. Lost connection?' myname@mysite.edu <<~~~~\nDaemon exiting. Lost connection (looping on same message)? Message is:\n%s\n~~~~", value.arr); status = system(prev_value.arr); */ break; } } else { strcpy((char *)prev_value.arr, (char *)value.arr); prev_value.len = value.len; prev_count = 1; } } else { /* We get here if an error was received while the daemon was waiting. If the status = 1, this is a timeout and is probably not a problem. However, the default timeout for the receive_message function is 1000 days, so unless the daemon is kept running for over 3 years without receiving a signal, you won't time out. */ printf("Daemon error while waiting for signal."); printf(" status = %d\n", status); } } EXEC SQL commit work release; }