SET SERVEROUTPUT ON
DECLARE
-- =================================================================
-- CONFIGURATION (Tri-Mode)
-- =================================================================
-- 1. Sniper Mode (Specific Session)
v_sid_raw VARCHAR2(50) := TRIM('&sid');
v_serial_raw VARCHAR2(50) := TRIM('&serial');
-- 2. Purge Mode (All sessions for User)
v_target_user VARCHAR2(128) := TRIM('&username');
-- 3. SQL Killer Mode (All sessions running specific SQL)
v_target_sql VARCHAR2(13) := TRIM('&sql_id');
-- =================================================================
v_sid NUMBER;
v_serial NUMBER;
-- Protected Users List
TYPE t_protected IS TABLE OF VARCHAR2(30);
v_protected t_protected := t_protected('SYS','SYSTEM','RDSADMIN','DBSNMP','XDB','AUDSYS');
-- The "Wide Net" Cursor
CURSOR c_sessions IS
SELECT sid, serial#, username, type, program, status, sql_id
FROM v$session
WHERE (v_target_user IS NOT NULL AND username = v_target_user)
OR (v_sid IS NOT NULL AND sid = v_sid AND serial# = v_serial)
OR (v_target_sql IS NOT NULL AND sql_id = v_target_sql);
v_killed_cnt NUMBER := 0;
v_is_safe BOOLEAN := TRUE;
v_mode_msg VARCHAR2(100);
v_inputs_set NUMBER := 0;
-- Kill Logic
PROCEDURE kill_session(p_sid NUMBER, p_serial NUMBER, p_program VARCHAR2, p_status VARCHAR2) IS
l_job_name VARCHAR2(128);
l_check NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE(' Targeting SID: ' || p_sid || ' | Serial: ' || p_serial);
-- Scheduler Check
IF p_program LIKE '%(J%' THEN
BEGIN
SELECT job_name INTO l_job_name FROM dba_scheduler_running_jobs WHERE session_id = p_sid;
DBMS_OUTPUT.PUT_LINE(' Detected Job: ' || l_job_name);
DBMS_SCHEDULER.STOP_JOB(l_job_name, force => TRUE);
DBMS_OUTPUT.PUT_LINE('STOP_JOB executed.');
RETURN;
EXCEPTION WHEN OTHERS THEN NULL;
END;
END IF;
-- Already Dead Check
IF p_status = 'KILLED' THEN
DBMS_OUTPUT.PUT_LINE('Already KILLED. Moving to PROCESS kill.');
rdsadmin.rdsadmin_util.kill(p_sid, p_serial, 'PROCESS');
RETURN;
END IF;
-- Standard Kill
BEGIN
rdsadmin.rdsadmin_util.kill(p_sid, p_serial, 'IMMEDIATE');
DBMS_OUTPUT.PUT_LINE('KILL IMMEDIATE sent.');
SELECT COUNT(*) INTO l_check FROM v$session WHERE sid = p_sid AND serial# = p_serial;
IF l_check > 0 THEN
rdsadmin.rdsadmin_util.kill(p_sid, p_serial, 'PROCESS');
DBMS_OUTPUT.PUT_LINE(' OS Process terminated.');
END IF;
EXCEPTION WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(' Error: ' || SQLERRM);
END;
END;
BEGIN
-- [STEP 1] Safe Number Conversion
BEGIN
v_sid := TO_NUMBER(v_sid_raw);
v_serial := TO_NUMBER(v_serial_raw);
EXCEPTION WHEN VALUE_ERROR THEN
DBMS_OUTPUT.PUT_LINE('ERROR: SID and Serial must be numbers.');
RETURN;
END;
-- Handle literal "NULL" strings
IF UPPER(v_target_user) = 'NULL' THEN v_target_user := NULL; END IF;
IF UPPER(v_target_sql) = 'NULL' THEN v_target_sql := NULL; END IF;
DBMS_OUTPUT.PUT_LINE('=== OPERATION START ===');
-- [SAFETY CHECK 0] Ambiguity Check (The "Pick One" Rule)
IF v_sid IS NOT NULL THEN v_inputs_set := v_inputs_set + 1; END IF;
IF v_target_user IS NOT NULL THEN v_inputs_set := v_inputs_set + 1; END IF;
IF v_target_sql IS NOT NULL THEN v_inputs_set := v_inputs_set + 1; END IF;
IF v_inputs_set > 1 THEN
DBMS_OUTPUT.PUT_LINE('ERROR: Ambiguous Input detected.');
DBMS_OUTPUT.PUT_LINE(' You provided multiple inputs. Please choose EXACTLY ONE mode:');
DBMS_OUTPUT.PUT_LINE(' 1. SID/Serial only (Sniper Mode)');
DBMS_OUTPUT.PUT_LINE(' 2. Username only (Purge Mode)');
DBMS_OUTPUT.PUT_LINE(' 3. SQL_ID only (SQL Killer Mode)');
RETURN;
END IF;
IF v_inputs_set = 0 THEN
DBMS_OUTPUT.PUT_LINE('ERROR: No input detected. Provide SID, Username, OR SQL_ID.');
RETURN;
END IF;
-- Determine Mode Message
IF v_target_user IS NOT NULL THEN
v_mode_msg := 'PURGE USER [' || v_target_user || ']';
ELSIF v_target_sql IS NOT NULL THEN
v_mode_msg := 'SQL KILLER [SQL_ID=' || v_target_sql || ']';
ELSE
v_mode_msg := 'SNIPER [SID=' || v_sid || ']';
END IF;
DBMS_OUTPUT.PUT_LINE('Mode: ' || v_mode_msg);
-- [SAFETY CHECK 2] Prevent Self-Kill
IF v_target_user = USER THEN
DBMS_OUTPUT.PUT_LINE('BLOCKED: You cannot purge yourself.');
RETURN;
END IF;
-- EXECUTION LOOP
FOR r IN c_sessions LOOP
v_is_safe := TRUE;
-- [SAFETY CHECK 3] Background
IF r.type = 'BACKGROUND' THEN
DBMS_OUTPUT.PUT_LINE('SKIPPING SID ' || r.sid || ' (Background Process).');
CONTINUE;
END IF;
-- [SAFETY CHECK 4] Self
IF r.sid = SYS_CONTEXT('USERENV', 'SID') THEN
CONTINUE;
END IF;
-- [SAFETY CHECK 5] Protected Users (Crucial for SQL_ID mode!)
-- Even if we kill by SQL_ID, we check WHO is running it.
-- If RDSADMIN is running the bad SQL, we MUST NOT kill it.
IF r.username IS NOT NULL THEN
FOR i IN 1..v_protected.COUNT LOOP
IF r.username = v_protected(i) THEN
DBMS_OUTPUT.PUT_LINE('BLOCKED SID ' || r.sid || ': User ' || r.username || ' is PROTECTED.');
v_is_safe := FALSE;
EXIT;
END IF;
END LOOP;
END IF;
IF v_is_safe THEN
kill_session(r.sid, r.serial#, r.program, r.status);
v_killed_cnt := v_killed_cnt + 1;
END IF;
END LOOP;
IF v_killed_cnt = 0 THEN
DBMS_OUTPUT.PUT_LINE('No actionable sessions found.');
ELSE
DBMS_OUTPUT.PUT_LINE('=== COMPLETED. Killed ' || v_killed_cnt || ' session(s). ===');
END IF;
END;
/
===============
SET SERVEROUTPUT ON
DECLARE
-- =================================================================
-- CONFIGURATION (Fixed for Empty Inputs)
-- =================================================================
-- We read these as STRINGS first to handle empty inputs safely.
v_sid_raw VARCHAR2(50) := TRIM('&sid');
v_serial_raw VARCHAR2(50) := TRIM('&serial');
v_target_user VARCHAR2(128) := TRIM('&username');
-- Now we convert them to numbers for logic
v_sid NUMBER;
v_serial NUMBER;
-- =================================================================
-- Protected Users List
TYPE t_protected IS TABLE OF VARCHAR2(30);
v_protected t_protected := t_protected('SYS','SYSTEM','RDSADMIN','DBSNMP','XDB','AUDSYS');
CURSOR c_sessions IS
SELECT sid, serial#, username, type, program, status
FROM v$session
WHERE (v_target_user IS NOT NULL AND username = v_target_user)
OR (v_target_user IS NULL AND sid = v_sid AND serial# = v_serial);
v_killed_cnt NUMBER := 0;
v_is_safe BOOLEAN := TRUE;
v_mode_msg VARCHAR2(100);
-- Internal Procedure
PROCEDURE kill_session(p_sid NUMBER, p_serial NUMBER, p_program VARCHAR2, p_status VARCHAR2) IS
l_job_name VARCHAR2(128);
l_check NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE(' Targeting SID: ' || p_sid || ' | Serial: ' || p_serial);
-- Scheduler Check
IF p_program LIKE '%(J%' THEN
BEGIN
SELECT job_name INTO l_job_name FROM dba_scheduler_running_jobs WHERE session_id = p_sid;
DBMS_OUTPUT.PUT_LINE(' Detected Job: ' || l_job_name);
DBMS_SCHEDULER.STOP_JOB(l_job_name, force => TRUE);
DBMS_OUTPUT.PUT_LINE(' STOP_JOB executed.');
RETURN;
EXCEPTION WHEN OTHERS THEN NULL;
END;
END IF;
-- Already Dead Check
IF p_status = 'KILLED' THEN
DBMS_OUTPUT.PUT_LINE(' Already KILLED. Moving to PROCESS kill.');
rdsadmin.rdsadmin_util.kill(p_sid, p_serial, 'PROCESS');
RETURN;
END IF;
-- Standard Kill
BEGIN
rdsadmin.rdsadmin_util.kill(p_sid, p_serial, 'IMMEDIATE');
DBMS_OUTPUT.PUT_LINE(' 🔪 KILL IMMEDIATE sent.');
SELECT COUNT(*) INTO l_check FROM v$session WHERE sid = p_sid AND serial# = p_serial;
IF l_check > 0 THEN
rdsadmin.rdsadmin_util.kill(p_sid, p_serial, 'PROCESS');
DBMS_OUTPUT.PUT_LINE(' OS Process terminated.');
END IF;
EXCEPTION WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(' Error: ' || SQLERRM);
END;
END;
BEGIN
-- [STEP 1] Safe Conversion: Convert string inputs to Numbers
-- If empty, TO_NUMBER returns NULL (which is what we want)
BEGIN
v_sid := TO_NUMBER(v_sid_raw);
v_serial := TO_NUMBER(v_serial_raw);
EXCEPTION WHEN VALUE_ERROR THEN
DBMS_OUTPUT.PUT_LINE('ERROR: SID and Serial must be numbers.');
RETURN;
END;
-- Handle literal "NULL" string input
IF UPPER(v_target_user) = 'NULL' THEN v_target_user := NULL; END IF;
DBMS_OUTPUT.PUT_LINE('=== OPERATION START ===');
-- [SAFETY CHECK 0] Ambiguity Check
IF v_target_user IS NOT NULL AND (v_sid IS NOT NULL OR v_serial IS NOT NULL) THEN
DBMS_OUTPUT.PUT_LINE('ERROR: Ambiguous Input detected.');
DBMS_OUTPUT.PUT_LINE(' Please choose ONE mode (Sniper OR Purge).');
RETURN;
END IF;
-- Display Mode
IF v_target_user IS NOT NULL THEN
v_mode_msg := 'PURGE USER [' || v_target_user || ']';
ELSE
v_mode_msg := 'SNIPER [SID=' || NVL(TO_CHAR(v_sid),'NULL') || ']';
END IF;
DBMS_OUTPUT.PUT_LINE('Mode: ' || v_mode_msg);
-- [SAFETY CHECK 1] No Input
IF v_target_user IS NULL AND v_sid IS NULL THEN
DBMS_OUTPUT.PUT_LINE('ERROR: No input detected.');
RETURN;
END IF;
-- [SAFETY CHECK 2] Self-Kill
IF v_target_user = USER THEN
DBMS_OUTPUT.PUT_LINE('BLOCKED: You cannot purge yourself.');
RETURN;
END IF;
-- EXECUTION LOOP
FOR r IN c_sessions LOOP
v_is_safe := TRUE;
-- [SAFETY CHECK 3] Background
IF r.type = 'BACKGROUND' THEN
DBMS_OUTPUT.PUT_LINE('SKIPPING SID ' || r.sid || ' (Background Process).');
CONTINUE;
END IF;
-- [SAFETY CHECK 4] Self (Current Session)
IF r.sid = SYS_CONTEXT('USERENV', 'SID') THEN
CONTINUE;
END IF;
-- [SAFETY CHECK 5] Protected Users
IF r.username IS NOT NULL THEN
FOR i IN 1..v_protected.COUNT LOOP
IF r.username = v_protected(i) THEN
DBMS_OUTPUT.PUT_LINE('BLOCKED SID ' || r.sid || ': User ' || r.username || ' is PROTECTED.');
v_is_safe := FALSE;
EXIT;
END IF;
END LOOP;
END IF;
IF v_is_safe THEN
kill_session(r.sid, r.serial#, r.program, r.status);
v_killed_cnt := v_killed_cnt + 1;
END IF;
END LOOP;
IF v_killed_cnt = 0 THEN
DBMS_OUTPUT.PUT_LINE('No actionable sessions found.');
ELSE
DBMS_OUTPUT.PUT_LINE('=== COMPLETED. Killed ' || v_killed_cnt || ' session(s). ===');
END IF;
END;
/