Oracle 8i無原生HOST或SYSTEM命令,OEM Job調度參數固定、缺乏靈活性。本文提供一套基於DBMS_PIPE的守護進程模式完整實現:PL/SQL打包命令發送至命名管道 → 外部守護進程(Pro*C/Shell/Perl)讀取管道執行命令 → 結果回寫管道 → PL/SQL解包返回。含包規範、包體、守護進程邏輯、權限控制、異常處理、安全加固建議。
PL/SQL (DBMS_PIPE) <--管道: DAEMON_CMD--> 守護進程 (OS進程) <--管道: DAEMON_RSP_--> PL/SQL
DAEMON_CMD,PL/SQL寫入,守護進程讀取。DAEMON_RSP_,守護進程寫入,PL/SQL讀取,實現會話隔離。['SYSTEM', response_pipe_name, command_string]['DONE', exit_code] 或 ['ERROR', err_msg]CREATE OR REPLACE PACKAGE daemon_pkg AS
FUNCTION execute_system(p_command VARCHAR2, p_timeout NUMBER DEFAULT 30) RETURN NUMBER;
PROCEDURE stop_daemon(p_timeout NUMBER DEFAULT 10);
END daemon_pkg;
/
CREATE OR REPLACE PACKAGE BODY daemon_pkg AS
FUNCTION execute_system(p_command VARCHAR2, p_timeout NUMBER DEFAULT 30) RETURN NUMBER IS
v_status NUMBER;
v_result VARCHAR2(20);
v_exit_code NUMBER;
v_cmd_pipe CONSTANT VARCHAR2(30) := 'DAEMON_CMD';
v_rsp_pipe VARCHAR2(30);
BEGIN
v_rsp_pipe := DBMS_PIPE.UNIQUE_SESSION_NAME; -- 生成唯一響應管道名
-- 1. 打包請求
DBMS_PIPE.PACK_MESSAGE('SYSTEM');
DBMS_PIPE.PACK_MESSAGE(v_rsp_pipe);
DBMS_PIPE.PACK_MESSAGE(p_command);
-- 2. 發送至命令管道
v_status := DBMS_PIPE.SEND_MESSAGE(v_cmd_pipe, p_timeout);
IF v_status != 0 THEN
RAISE_APPLICATION_ERROR(-20010, 'SEND_MESSAGE failed: ' || v_status);
END IF;
-- 3. 等待響應
v_status := DBMS_PIPE.RECEIVE_MESSAGE(v_rsp_pipe, p_timeout);
IF v_status != 0 THEN
RAISE_APPLICATION_ERROR(-20011, 'RECEIVE_MESSAGE failed: ' || v_status);
END IF;
-- 4. 解包響應
DBMS_PIPE.UNPACK_MESSAGE(v_result);
IF v_result != 'DONE' THEN
DBMS_PIPE.UNPACK_MESSAGE(v_result); -- error msg
RAISE_APPLICATION_ERROR(-20012, 'Daemon error: ' || v_result);
END IF;
DBMS_PIPE.UNPACK_MESSAGE(v_exit_code);
DBMS_OUTPUT.PUT_LINE('OS command executed, exit_code=' || v_exit_code);
RETURN v_exit_code;
END execute_system;
PROCEDURE stop_daemon(p_timeout NUMBER DEFAULT 10) IS
v_status NUMBER;
BEGIN
DBMS_PIPE.PACK_MESSAGE('STOP');
v_status := DBMS_PIPE.SEND_MESSAGE('DAEMON_CMD', p_timeout);
IF v_status != 0 THEN
RAISE_APPLICATION_ERROR(-20030, 'STOP send failed: ' || v_status);
END IF;
END stop_daemon;
END daemon_pkg;
#!/bin/bash
# daemon.sh - 以oracle用戶常駐運行
CMD_PIPE="DAEMON_CMD"
while true; do
# 1. 讀取命令管道 (阻塞)
msg=$(sqlplus -s /nolog <
CREATE PROCEDURE權限用戶執行上述DDL。GRANT EXECUTE ON daemon_pkg TO app_user;/etc/init.d/oracle_daemon服務腳本,隨OS啟動,以oracle用戶運行。IF p_command NOT IN ('allowed_cmd1','allowed_cmd2') THEN RAISE... 防止任意命令注入。USER, SYSDATE, p_command, exit_code至審計表。| 風險 | 對策 |
|---|---|
| 任意命令執行 | 白名單校驗、最小權限OS用戶、命令參數化 |
| 管道名碰撞/劫持 | UNIQUE_SESSION_NAME保證響應管道唯一;命令管道權限DBMS_PIPE.CREATE_PIPE('DAEMON_CMD', 1000, TRUE)私有化 |
| 守護進程掛死導致PL/SQL阻塞 | 發送/接收設置合理p_timeout (建議30-60秒);守護進程心跳監控自動重啟 |
| 結果集過大溢出VARCHAR2 | 大輸出改寫臨時表/文件,管道僅傳遞文件路徑 |
DBMS_SCHEDULER.CREATE_JOB(job_type => 'EXECUTABLE', ...) 原生支持OS命令調度,無需自建管道。DBMS_CLOUD、DBMS_SQL增強、外部表預處理器 PREPROCESSOR。DBMS_PIPE雙向通信模式是Oracle 8i/9i時代實現PL/SQL異步調用外部程序的經典範式。理解其協議設計、超時控制、會話隔離機制,對維護老系統、設計新系統異步邊界均有啟發價值。