This post is also available in: Português
Oracle will always generate audit files for some SYSDBA operations in audit_file_dest, no matter what you do. As Doc ID 1528170.1, "Some auditing of SYSDBA is mandatory and cannot be disabled (STARTUP, SHUTDOWN, and CONNECT)." If audit_sys_operations is currently set to TRUE, many files will be created in audit_file_dest by SYSDBA user activity.
In this article, I will explain and show you how to read this information easily within an Oracle table.
I will divide it in 3 steps:
- Create a shell script process to generate temporary aud files in "loader" format.
- Create a external table and a view to dynamically read this contents.
- Create a crontab job to minutely convert new generated audit files.
So let's begin. First of all, create a folder on your OS to keep the shell script and the converted audit files.
In this example, my folder for audit script is /home/oracle/audsys and I will keep the transformed files in /home/oracle/audsys/out. My audit_file_dest is "/u02/admin/orcl/adump".
The script below will convert the audit files from lines to columns with fields separator:
#! /bin/ksh # Created by Rodrigo Jorge (www.dbarj.com.br) in Oct/2014 audfiles=/u02/admin/orcl/adump/*.aud outfolder=/home/oracle/audsys/out outsufix=".ext" # Sufix for temp files procfile () { cont=0 seq=1 delim="|||" ifile=$1 ofile=$2 rm -f $ofile while read var; do if [[ $var == ACTION* ]] then cont=1 result="${linha_2ant}${delim}$(echo $linha_ant | sed 's/^[^:]*: //')" fi if [ $cont -ge 1 -a $cont -le 7 ] then if [[ $cont == 2 ]] && [[ $var != DATABASE* ]] then result="${result} ${var}" else result=${result}${delim}$(echo $var | sed 's/^[^:]*:\[[0-9]*\] //') cont=$((cont+1)) fi fi if [[ $cont == 8 ]] then cont=0 result="${result}${delim}$(basename $ifile)${delim}${seq}" seq=$((seq+1)) echo $result >> $ofile fi linha_2ant=$linha_ant linha_ant=$var done <$ifile } for f in $audfiles do outfile=$outfolder/$(basename $f)${outsufix} if [ ! -f $outfile -o "$outfile" -ot "$f" ] then procfile $f $outfile touch -m -r $f $outfile fi done shfile=${outfolder}/catfiles.sh [ ! -f $shfile ] && echo "$(which cat) ${outfolder}/*.aud${outsufix}" > $shfile [ ! -x $shfile ] && chmod +x $shfile echo > $outfolder/null.txt
Run the script to generate the initial files. The first run can take a while. After the first, the next executions will be differential.
On the next step, we are creating the directory pointing to our temp folder:
SQL> CREATE DIRECTORY AUDSYSDIR AS '/home/oracle/audsys/out'; Directory created. SQL>
Now, it's time to create the external table:
CREATE TABLE TB_AUDSYS ( LOGDATE VARCHAR2(2000), LENGTH NUMBER, ACTION CLOB, DATABASE_USER VARCHAR2(30), PRIVILEGE VARCHAR2(2000), CLIENT_USER VARCHAR2(2000), CLIENT_TERMINAL VARCHAR2(2000), STATUS NUMBER, DBID NUMBER, FILENAME VARCHAR2(2000), SEQ NUMBER ) ORGANIZATION EXTERNAL ( TYPE ORACLE_LOADER DEFAULT DIRECTORY AUDSYSDIR ACCESS PARAMETERS ( records delimited by newline preprocessor audsysdir:'catfiles.sh' badfile audsysdir:'query%a_%p.bad' logfile audsysdir:'query%a_%p.log' fields terminated by '|||' missing field values are null ( LOGDATE CHAR(2000), LENGTH CHAR(2000) enclosed by X'27', ACTION CHAR(100000), DATABASE_USER CHAR(2000) enclosed by X'27', PRIVILEGE CHAR(2000) enclosed by X'27', CLIENT_USER CHAR(2000) enclosed by X'27', CLIENT_TERMINAL CHAR(2000) enclosed by X'27', STATUS CHAR(2000) enclosed by X'27', DBID CHAR(2000) enclosed by X'27', FILENAME CHAR(2000), SEQ CHAR(2000) ) ) LOCATION ('null.txt') ) PARALLEL REJECT LIMIT 0;
Note here that we use the "fields terminated by '|||'", the same pattern used on the shell script above. Some fileds are also enclosed by X'27', the HEX value for "single quotation". The "location" must be a null file (and it must exist) as we are going to use the preprocessor script, that will "cat" all the files dinamically inside the the temporary folder.
Let's check the output for the table:
SQL> set lines 1000 pages 1000 wrap off SQL> col logdate format a32 SQL> col length format 9999 SQL> col action format a30 SQL> col database_user format a15 SQL> col privilege format a7 SQL> col client_user format a20 SQL> col client_terminal format a5 SQL> col status format 9999 SQL> col dbid format 9999999999 SQL> col filename format a15 SQL> col seq format 999 SQL> select * from tb_audsys sample (1); LOGDATE LENGTH ACTION DATABASE_USER PRIVILE CLIENT_USER CLIEN STATUS DBID FILENAME SEQ -------------------------------- ------ ------------------------------ --------------- ------- -------------------- ----- ------ ----------- --------------- ---- Wed Oct 1 16:26:34 2014 -03:00 169 'CONNECT' / NONE emc12 Not A 1017 2044245708 sirac_ora_15997 2 Sun Oct 12 16:26:48 2014 -03:00 155 'CONNECT' / NONE emc12 1017 2044245708 sirac_ora_20302 1 Mon Sep 29 14:29:29 2014 -03:00 166 'CONNECT' MDOXDBA SYSDBA oracle pts/0 0 2044245708 sirac_ora_29181 1 Sat Oct 11 16:26:38 2014 -03:00 155 'CONNECT' / NONE emc12 1017 2044245708 sirac_ora_45362 1 Mon Sep 29 16:53:30 2014 -03:00 166 'CONNECT' MDOXDBA SYSDBA oracle pts/1 0 2044245708 sirac_ora_53002 1 Thu Sep 18 03:00:03 2014 -03:00 165 'CONNECT' RMANBACKUP SYSDBA oracle 0 2044245708 sirac_ora_57015 1 Thu Oct 2 16:26:33 2014 -03:00 169 'CONNECT' / NONE emc12 Not A 1017 2044245708 sirac_ora_62200 2 Fri Oct 10 16:26:37 2014 -03:00 155 'CONNECT' / NONE emc12 1017 2044245708 sirac_ora_63673 1 8 rows selected. SQL>
Good, everything is working. I can create now a view to convert the LOGDATE column from varchar2 to timestamp:
CREATE VIEW VW_AUDSYS AS SELECT TO_TIMESTAMP_TZ(SUBSTR(LOGDATE, 5), 'Mon fmDDfm hh24:mi:ss YYYY TZH:TZM') LOG_TZ, LENGTH, TO_CHAR(SUBSTR(ACTION,1,4000)) ACTION, DATABASE_USER, PRIVILEGE, CLIENT_USER, CLIENT_TERMINAL, STATUS, DBID, FILENAME, SEQ FROM TB_AUDSYS;
The last step is to schedule the shell to run every X minutes. So we will have our table always updated table with the latest SYSDBA actions:
[oracle@serveraudsys]$ crontab -l */5 * * * * /home/oracle/audsys/procaud.shHave you enjoyed? Please leave a comment or give a 👍!
27 comments
Skip to comment form
Very Good !
Awesome, It worked perfectly in linux.
But solaris i am getting error at below line.
if [[ $var == ACTION* ]] . Seems this command doesn't works in solaris. I ll check for similar command in solaris.
Its a great script.
Rajkishore
Author
I'm happy you enjoyed! =]
Regards,
RJ
Hi Jorge,
Sorry , i don't want to disturb you. I implemented this one all linux server and it worked perfect.
While doing the same in solaris box. i few problems. I did some changes to solve some of them.
But still getting error at if [[ $var == ACTION* ]]
linha_2ant=Thu Oct 9 10:14:31 2014 -04:00
linha_ant=LENGTH : '158'
+ read var
+ [[ ACTION :[6] 'COMMIT' == ACTION* ]]
procaud.sh: [[: not found
+ [ 0 -ge 1 -a 0 -le 7 ]
+ [ 0 = 8 ]
linha_2ant=LENGTH : '158'
linha_ant=ACTION :[6] 'COMMIT'
I tried lot of alternatives to validate this condition, but not working .
It would be kind of you, if you look into this one.
usa0300uz1151-tcsdba-NDR1>uname -a
SunOS usa0300uz1151 5.10 Generic_150400-13 sun4u sparc SUNW,SPARC-Enterprise
usa0300uz1151-tcsdba-NDR1>echo $SHELL
/bin/ksh
Author
First of all, you are not disturbing me asking a question! =]
Try to just change the first line from:
#! /bin/sh
To:
#! /bin/ksh
If it doesn't work, try to force the ksh execution with: "ksh procaud.sh"
Regards,
Rodrigo
Hi Rodrigo,
after running with ksh issue has been solved. But i observed one more thing. some of the records are getting rejected . ( fyi, i have put reject limit unlimited in external table)
It seems if the action part contain single code (') , then it is putting them in bad file.
My logfile is like :
FILENAME CHAR (2000)
Terminated by "|||"
Enclosed by "27" and "27"
Trim whitespace same as SQL Loader
KUP-04021: field formatting error for field ACTION
KUP-04037: terminator not found
KUP-04101: record 4 rejected in file /uv1044/u321/home/tcsdba/audsys/out/null.txt
KUP-04021: field formatting error for field ACTION
KUP-04037: terminator not found
KUP-04101: record 5 rejected in file /uv1044/u321/home/tcsdba/audsys/out/null.txt
KUP-04021: field formatting error for field ACTION
KUP-04037: terminator not found
KUP-04101: record 9 rejected in file /uv1044/u321/home/tcsdba/audsys/out/null.txt
KUP-04021: field formatting error for field ACTION
my bad file is like:
'Thu Oct 9 10:16:45 2014 -04:00'|||'188'|||'insert into tcsdba.t1 values('RAJ')'|||'/'|||'SYSDBA'|||'tcsdba'|||'pts/12'|||'0'|||'845124382'|||'ndr1_ora_9731_1.aud'
'Thu Oct 9 10:16:46 2014 -04:00'|||'188'|||'insert into tcsdba.t1 values('RAJ')'|||'/'|||'SYSDBA'|||'tcsdba'|||'pts/12'|||'0'|||'845124382'|||'ndr1_ora_9731_1.aud'
'Thu Oct 9 10:16:46 2014 -04:00'|||'188'|||'insert into tcsdba.t1 values('RAJ')'|||'/'|||'SYSDBA'|||'tcsdba'|||'pts/12'|||'0'|||'845124382'|||'ndr1_ora_9731_1.aud'
'Thu Oct 9 10:16:46 2014 -04:00'|||'188'|||'insert into tcsdba.t1 values('RAJ')'|||'/'|||'SYSDBA'|||'tcsdba'|||'pts/12'|||'0'|||'845124382'|||'ndr1_ora_9731_1.aud'
So some of the records are getting skipped.
May we have to use some kind of delimiter, But i m not sure where to put that.
Regards
Rajkishore
Author
I will improve the script to deal with that situation and I will update you when it's done!
Author
Hi Rajkishore,
I improved the script, the table and the view code. Please try again and check if it works!
Regards,
Rodrigo
Hi Rodrigo,
Its awesome. its perfect now. No action is getting rejected now. : ) 🙂 🙂
I am grateful to you for this help. I have learnt a lot of things from you.
Its a pleasure discussing with you. I will stay in touch with you . :))
Regards
Rajkishore
Author
I'm glad you enjoyed! =]
See you!
Regards,
Rodrigo
Hi Rodrigo,
This is awesome stuff, worked like charm for me... great work on the script... 🙂
Thanks,
Abdo.
Hi,
excellent script and work perfect in 11G, but does not do in 10G.
Author
Hi Raja,
Yes, it use some features that didn't exist in 10G.
Regards,
RJ
RJ hello thanks for this script it wonderful I have one small issue im trying to resolve
if sql statement is not one complete line
ex: user does
select name
from v$database
user hit enter after select name
then aud file looks like this
Fri Jan 13 14:52:29 2017 -06:00
LENGTH : '181'
ACTION :[27] 'select name
from v$database'
DATABASE USER:[1] '/'
PRIVILEGE :[6] 'SYSDBA'
CLIENT USER:[6] 'oracle'
CLIENT TERMINAL:[5] 'pts/1'
STATUS:[1] '0'
DBID:[10] '12344556666'
and you get |||select name|||from v$database rather than |||'select name from v$database'|||/
this caused record to go in bad file
Thanks
Author
Hi Rudy,
What's happening to you is very strange, as the shell that I've written should be appending the whole SQL statement removing line breaks until the DATABASE USER line appears. Please ensure your shell is running with ksh or provide some output for debug.
Tks,
RJ
RJ
Thanks I believe that was my issue, was not using ksh did some tests with logon as sysdba and queries with multiple line breaks and all is good
when using ksh
One thing I had to do not related to line breaks was I had to do set -f I had some sql being captured that contained *
ex: select * from v$datafiles and when processed the * caused output to be a ls -l of what ever directory I was in when running shell
so I would get select (ls output) from v$datafile as action
set -f took care of issue
I added it like this
for f in $audfiles
do
set -f
outfile=$outfolder/$(basename $f)${outsufix}
if [ ! -f $outfile -o "$outfile" -ot "$f" ]
then
procfile $f $outfile
touch -m -r $f $outfile
fi
done
Thanks again, a wonderful script Ive been looking for something like this for awhile and was struggling developing myself
Hi RJ,
Firstly thank you for the awesome script, it has save me a lot of time in converting the files.
Just one question, the client wants the number that is next to the ACTION (ACTION :[147] 'select /*+ opt_param('parallel_execution_enabled')
to be included in the converted data. As I am not well versed in shell scripting can you please give me some advise on how to include it.
It will be greatly appreciated.
Thank You
Carin Webb
Author
Hi Carin, there is a sed command in my script that remove the numbers on all those fields:
sed 's/^[^:]*:\[[0-9]*\] //'
Replace it by
sed 's/^[^:]*://'
to keep the numbers in all fields. If you want to keep this number only on ACTION field, you would need to add an extra if clause.Regards,
RJ
Hi RJ,
Thank you for you response and assistance, I had to get the numbers and have it as a seperate column, which I have achieved, here is the code I added for others to also use and reference:
if [ $cont -ge 1 -a $cont -le 7 ]
then
if [[ $cont == 2 ]] && [[ $var != DATABASE* ]]
then
result="${result} ${var}"
else
if [[ $var == ACTION* ]]
then
result=${result}${delim}${concat}$(echo ${var:9:3} | sed 's/^[^:]*: //')${concat}
result=${result}${delim}$(echo $var | sed 's/^[^:]*:\[[0-9]*\] //')
cont=$((cont+1))
else
result=${result}${delim}$(echo $var | sed 's/^[^:]*:\[[0-9]*\] //')
cont=$((cont+1))
fi
fi
I'm running the script on AIX server. It is giving me error " Segmentation Fault".
Please help me.
Author
You are probably facing some bug.. it shouldn't happen. I'd advise to open a SR in MOS..
Hi RJ
Awesome script I tried it on my 12c database and was able to create all following above setps. But when I select from table its not getting rows and it gives no row selected I can see log files generating in temp directory. What else can I check ?
Regards
Radhika
Author
Hi Radhika,
You need to check all the steps and see if you have pointed correctly the folder of audit files and the output one. Maybe Oracle is not being able to read/write files from the directory path that you have created. In that case, I would advise you to test an expdp to this directory to check if any file is being generated there.
Regards,
RJ
Thank You RJ i was able to fetch data for my 12,11G databases but I have a 10G database which needs this to be placed as well but preprocessor clause is not working for the same can you let me know what can be an alternative for it.
Regards
Radhika.
Hi RJ,
Unfortunately, I am not able to understand the shell script 🙁
It would be very helpful, if you can give an explaination about the shell script in detail, like how it's working & what it's doing exactly ?
Appreciate your response & time.
ERROR at line 1:
ORA-29913: error in executing ODCIEXTTABLEOPEN callout
ORA-29400: data cartridge error
KUP-00554: error encountered while parsing access parameters
KUP-01005: syntax error: found "identifier": expecting one of: "badfile, byteordermark, characterset, column, data, delimited, discardfile, disable_directory_link_check, exit, fields, fixed, load, logfile, language, nodiscardfile, nobadfile, nologfile, date_cache, processing, readsize, string, skip, territory, varia"
KUP-01008: the bad identifier was: preprocessor
KUP-01007: at line 2 column 7
ORA-06512: at "SYS.ORACLE_LOADER", line 19
Hi Team,
IF we dont want sysdba user details and need to fetch application users list means where we need to make changes in this script .
Could you please give your inputs.
Regards,
Mahesh S