what to write script in

billfaith

New Member
I would like to write a script to automate some of the progress utilities like proshut. What can I use to write the script? I have a windows enviroment. Can I use something like notepad? I dont have the progress programming tools so I cant write progress code I dont think(not that I can anyway). What would I use?
 

joey.jeremiah

ProgressTalk Moderator
Staff member
There are and always have been command-line utils, like proserve, proshut and promon to start, stop and monitor databases using diff startup parameters.

Another option is to use the Progress Explorer to create and manage diff settings ( startup parameters ) for progress servers ( all progress servers, database, appserver, webspeed, sonic adapters etc. ).

And either use a command-line util ( dbman for databases, asbman for appservers etc. ), for example, from a batch file or shell script or the Progress Explorer to start, stop and monitor a server under a defined setting.


I'd recommend first reading up on the Progress Explorer in the "Installation and Configuration for Windows" doc, the "Configuration" chapter.

You can find references on the dbman and other database command-line utils in the "Database Administration" doc, the "Reference" chapter.

Like everything else there are diff opinions and point of views, that you can read on @peg.com.


You mentioned you're not familiar with the 4GL, are you familiar with database server and the diff settings ?

Another thing is that in general it's not recommended to routinely start and stop databases, for example, if you have other servers connected to the database. Maybe you should start with what is the problem you're trying to solve ? HTH
 

billfaith

New Member
Thanks for you reply

I am hoping I can write something that will look at the _connect table and disconnect users based on the the time they logged in. I would like to run it 2 or 3 times a day and disconnect users if they have been logged on more than 3 hours from the their logon (connect) time.

Thanks.
 

joey.jeremiah

ProgressTalk Moderator
Staff member
here's a quick and dirty script written in 4gl, maybe have someone compile it or xcode and encrypted compiler mode would also work.

but you can also use odbc, jdbc and any other language to do the same. i didn't add comments but i think it's pretty self explanatory, as usual. HTH

<snippet>
FUNCTION getConnectTime RETURNS DATETIME ( pcConnectTime AS CHAR ):

&SCOPED piMonth LOOKUP( ENTRY( 2, pcConnectTime, " " ), "jan,feb,mar,apr,may,jun,jul,aug,sep,oct,nov,dec" )
&SCOPED piDay INT( ENTRY( 3, pcConnectTime, " " ) )
&SCOPED piYear INT( ENTRY( 5, pcConnectTime, " " ) )

&SCOPED pcTime ENTRY( 4, pcConnectTime, " " )
&SCOPED piHour INT( ENTRY( 1, {&pcTime}, ":" ) )
&SCOPED piMinute INT( ENTRY( 2, {&pcTime}, ":" ) )

RETURN DATETIME( {&piMonth}, {&piDay}, {&piYear}, {&piHour}, {&piMinute} ).

END FUNCTION. /* getConnectTime */



DEFINE VAR cBatch AS CHAR NO-UNDO.
RUN adecomm/_tmpfile.p ( "b", ".bat", OUTPUT cBatch ).

OUTPUT TO VALUE( cBatch ).

FOR FIRST _myConnection NO-LOCK,

EACH _connect
WHERE _connect-usr <> _myConn-userid
AND ( _connect-type = "self" OR _connect-type = "remc" )
NO-LOCK:

IF INTERVAL( NOW, getConnectTime( _connect-time ), "hours" ) >= 3 THEN
PUT UNFORMATTED "call proshut " + QUOTER( PDBNAME( 1 ) ) + " -C disconnect " + STRING( _connect-usr ) SKIP.

END. /* for each _connect */

PUT UNFORMATTED "exit" SKIP.

OUTPUT CLOSE. /* cBatch */

OS-COMMAND VALUE( "call " + QUOTER( cBatch ) ).
OS-DELETE VALUE( cBatch ).
</snippet>
 

joey.jeremiah

ProgressTalk Moderator
Staff member
maybe another option is to use proshut <database> -C list to show all connected users and their logged in time.

direct the output to a file and write a batch that parses the data and either calls proshut or creates an accumulated batch. HTH
 

billfaith

New Member
Thanks JJ

I will try and get someone to compile this for me. What will I end up with after it is compiled? Is there a way to schedule this to run at certrain times?

And also what do you mean by using odbc and other languages?
 
We use MS-DOS batch files to stop/start servers, trucate Bis and log people off, for Progress V8/9 servers on a Windows 2000 Server.

When we used SCO UNIX as our server, we had similar scripts written as plain shell scripts.

Normally, you can automate daily routines using standard batch files, especially if you don't have 24/7 operations.

Simon
 

billfaith

New Member
Thanks Simon

Do your batch files log off people by length of time logged into the database? Is it similar to what JJ wrote above?
 

joey.jeremiah

ProgressTalk Moderator
Staff member
If you're interested, I'll put together a program that let's you run
scripts even though you don't have a development license.

It will only run scripts that access the system tables i.e schema and
VST's, not access data.

There is also an inherited 3k script size limit, and a few others.
Basically it'll let you write and run small 4gl admin scripts.


Technically it will use -rx encrypted compiler mode and preprocessor
arguments, similar to the data dictionary.

I'm guessing it might be alright since the dictionary uses a similar
solution and it also has substantial limitation.

Though if someone did want to compile code technically there are
other ways.
 

joey.jeremiah

ProgressTalk Moderator
Staff member
Thanks Lee, I really liked that !

Without me getting all defensive, I kind of have a hard time with comments. For me structuring comments is sometimes as much work as structuring the proc. It needs some thought to go into it and on how to bring the idea across etc.

Well I know it's half ***, but I mostly try making the code as obvious and elegant as possible, which is always desirable, for the next guy who'll need to work on it. It also keeps the proc small and elegant, minimal, just not understandable :)
 
bill faith:
> Thanks Simon
> Do your batch files log off people by length of time logged into the
> database? Is it similar to what JJ wrote above?

Generally, we don't log people off using proshut, unless their sessions have died. What I have instead is a standard menu that only allows 1 hour inactive time spent at the menu before it shuts itself down. It's not ideal as people can go into programs and hide behind a wait-for all day.

We introduced it because people were starting up 2 or 3 menus in the morning and leaving them open all day, in case they needed to use them. This ate into our resources big time. We also had some people on slower machines who would start a menu, not be patient enough to wait and would double-click on the menu again and again, spawning three, four or even five menus, then they'd leave them open all day.

Here is the code we use to kill RDT sessions on a SCO Unix box that have died because the guns are out of contact, their batteries have died or the operators have forgotten to log them off.




/*
killuser.p
Written for SCO Unix
To be run as part of a continual process under root (yes, I know but I
don't care)
*/

def var this_line as char no-undo.
def var this_command as char initial "who -uH".
def var this_col as int extent 6.
def var this_width as int extent 6.
def var idle_loop as int no-undo.
def var this_mult as int initial 1 no-undo.
def var max_idle_time as int no-undo.

def temp-table t_list no-undo
field username as char form "x(20)"
field tty as char
field date_time as char
field idle as char
field time_idle as int
field pid as char
field killme as logical.

if opsys <> "unix" then quit. /* This is only for Unix users - RDT Guns */

/* So I can customise the program through shell scripts */
if os-getenv ("MAX_IDLE_TIME") <> ? then max_idle_time = int (os-getenv ("MAX_IDLE_TIME")) no-error.
if max_idle_time = 0 or max_idle_time = ? then max_idle_time = 60. /* 1 hour */

input through value (this_command).
repeat:
this_line = "".
import unformatted this_line.
if index (this_line,"PID") > 0 then do:
this_col [1] = index (this_line,"NAME").
this_col [2] = index (this_line,"LINE").
this_col [3] = index (this_line,"TIME").
this_col [4] = index (this_line,"IDLE").
this_col [5] = index (this_line,"PID").
this_col [6] = index (this_line,"COMMENTS").

this_width [1] = this_col [2] - this_col [1].
this_width [2] = this_col [3] - this_col [2].
this_width [3] = this_col [4] - this_col [3].
this_width [4] = this_col [5] - this_col [4].
this_width [5] = this_col [6] - this_col [5].
this_width [6] = 100.
end.
else do:
create t_list.
t_list.username = trim(substring (this_line,this_col [1],this_width [1])).
t_list.tty = substring (this_line,this_col [2],this_width [2]).
t_list.date_time = substring (this_line,this_col [3],this_width [3]).
t_list.idle = trim(substring (this_line,this_col [4],this_width [4])).
t_list.pid = trim(substring (this_line,this_col [5],this_width [5])).

end.
end.
input close.
for each t_list:
if r-index (t_list.idle," ") > 0 then assign
t_list.pid = substring (t_list.idle,r-index (t_list.idle," ") + 1) + t_list.pid
t_list.idle = entry (1,t_list.idle," ").
t_list.idle = trim (t_list.idle).
t_list.tty = trim (t_list.tty).

if t_list.username = "progdev" then do:
t_list.killme = yes.
if t_list.tty = "tty03" or t_list.tty = "tty04" then t_list.killme = no.
/* Automatic Procedures should not die - they are always on the console */
this_mult = 1.

if t_list.idle = "." then t_list.killme = no. /* Currently active process */
else if index (t_list.idle,":") > 0 then do idle_loop = num-entries (t_list.idle,":") to 1 by -1:
t_list.time_idle = t_list.time_idle +
int (entry (idle_loop,t_list.idle,":")) * this_mult.
this_mult = this_mult * 60.
end.

if t_list.idle <> "old" and t_list.time_idle < max_idle_time then
t_list.killme = no.

end.

if t_list.killme then unix silent value ("kill -9 " + t_list.pid).
/* Yes, I know kill -9 is very naughty, indeed, but most of our RDT Guns sit waiting at the start of a program, tying up resources */
end.
quit. /* Because I run it and forget about it */

 
billfaith:
> I would like to write a script to automate some of the progress utilities
> like proshut. What can I use to write the script? I have a windows
> enviroment. Can I use something like notepad? I dont have the progress
> programming tools so I cant write progress code I dont think(not that I
> can anyway). What would I use?

You would write the batch files in something like notepad, as they are simply MS-DOS Batch Files.

> I am hoping I can write something that will look at the _connect table
> and disconnect users based on the the time they logged in. I would like
> to run it 2 or 3 times a day and disconnect users if they have been
> logged on more than 3 hours from the their logon (connect) time.

If you ran the stop mydb script then you could manually shut down users who have been logged on for a while, but then you wouldn't know if they had been inactive or not. You might just log someone off who was doing something important (and then they'll shout and moan and be a nuisance).

You need to be a little bit careful, these should be run on the server with Progress installed to a local or network drive and the DLC and PROMSGS environment variables set to point to the correct places.

I've included samples for starting, stopping, batch stopping (logging everyone out) and truncating BIs.

You could use fancier ways of controlling the servers, but these work just fine, at least the originals did before I changed all the database names and paths.

Each script is separated by a rem ======= - you'd have to break them up into separate scripts and change the following:
mydb = server name
p:\progress\dlc = Installed Progress Path
p:\progress\dlc\promsgs = Promsgs path, you could use %DLC%\PROMSGS
\progress\dlc\bin = Path containing Progress Batch Files - I can't remember why I don't call these with the path included, but I might have had path not found errors.
c:\db\mydb = Location of the database

I hope that helps.

Simon


rem ============================================
rem start mydb server
@echo off
set dlc=p:\progress\dlc
set promsgs=p:\progress\dlc\promsgs
p:
cd \progress\dlc\bin
c:
cd \db\mydb
echo Starting mydb Server
call p:proserve mydb -H fhwins1 -S mydb -N tcp -Mm 512 -L 8000 -B 6000 -n 30
c:
cd \db\scripts

rem ============================================

rem stop mydb server
@echo off
set dlc=p:\progress\dlc
set promsgs=p:\progress\dlc\promsgs
p:
cd \progress\dlc\bin
c:
cd \db\mydb
echo Batch Stopping mydb Server
call p:proshut mydb
cd \db\scripts

rem ============================================

rem stop a server
@echo off
set dlc=p:\progress\dlc
set promsgs=p:\progress\dlc\promsgs
p:
cd \progress\dlc\bin
c:
cd \db\mydb
echo Batch Stopping mydb Server
call p:proshut -by mydb
cd \db\scripts

rem ============================================

rem Truncate a bi
set dlc=p:\progress\dlc
set promsgs=p:\progress\dlc\promsgs
p:
cd \progress\dlc\bin
c:
cd \db\mydb
echo Truncating mydb BI File
call p:proutil mydb -C truncate bi
echo Truncating mydb LOG File
c:prolog mydb
cd \db\scripts


rem ============================================
 
And for UNIX, at least SCO Unix:

All scripts are samples, they will need to be rewritten and converted to Unix formats, no chr(13), only chr(10) at the end of lines, script permissions set etc., but they should give an idea of very simple scripts.


# Crontab entry - nightly.s runs at 20:40 and daily.s runs at 04:00
# Server Control
10 20 * * * /usr/bin/user/nightly.s
0 4 * * * /usr/bin/user/daily.s
#######################################
# nightly.s
/usr/bin/user/bmydbshut.s
/usr/bin/user/dmybtrunc.s
#######################################
# daily.s
/usr/bin/user/mydbserver.s
#######################################
# mydbserver.s
DLC=/usr/dlcnew
PROPATH=$PROPATH:$DLC:$DLC/bin
export DLC PROPATH
cd /usr/dlcnew/bin
proserve /usr/mydb/db/mydb -S mydb -ld mydb -H fhnrs1 -N tcp -Mm 512 -n 20 -L 8000 -B 6000
prowdog /usr/mydb/db/mydb
chmod 777 /usr/mydb/db/*
#######################################
# bmydbshut.s
DLC=/usr/dlcnew
PROPATH=$PROPATH:$DLC:$DLC/bin
export DLC PROPATH
cd /usr/dlcnew/bin
proshut -by /usr/mydb/db/mydb
#######################################
# bmydbtrunc.s
DLC=/usr/dlcnew
PROPATH=$PROPATH:$DLC:$DLC/bin
export DLC PROPATH
cd /usr/dlcnew/bin
proutil /usr/mydb/db/mydb -C truncate bi
prolog /usr/mydb/db/mydb
chmod 777 /usr/mydb/db/*
#######################################
 
joey.jeremiah said:
Thanks Lee, I really liked that !

Without me getting all defensive, I kind of have a hard time with comments. ....
I'm glad you didn't take it personally. All though I take a different approach to you comment-wise, I can sometimes go too far in the other direction, and end up in a DailyWTF type situation (well, I'm not that bad, but I do tend to over analyse). I only get annoyed with other coders when they assume I'm as quick-thinking as they are, and understand immediately what their absurd hieroglyphics represent.

But in your case, I've seen you're a more thoughtful programmer than that. I was just pulling your leg, and I'm glad the link amused.
 
And some more scripts to bore the pants off you.

OK, so they are not particularly fantastic, but they do work (at least the ones on my system work, I've changed all the drives, directories and databases for public consumption) and they save you having to go up to the computer room every time someone complains they have been kicked out of a database and need killing.

What they don't do is to check that you are killing off the right user, or that the user exists, or that it isn't the MD, or that the office junior shouldn't be killing off progress users, but - hey - it's only a script.

You can then set up an icon on some shared directory that everyone has but nobody goes to. Then when someone calls needing killing off, you just start a command prompt, go to the shared directory and type "killuser mydb" to kill the users off. Magic.


killuser.bat:

This takes a file listing the current users for a database, displays it on screen, asks for the usr number to be logged off and writes a batchfile to be run by another process (checkdel.bat).

s: is a shared drive
s:\db is a directory containing database information
On the database server, the script to delete the user is in c:\scripts

So, if you ran killuser mydb, it checks s:\db\mydblist.txt, prompts for a user id, creates a batch file s:\db\mydbdel.bat which disconnects the user from mydb.

@echo off
set /a LISTFILE=s:\db\%1list.txt
echo %LISTFILE%
if exist s:\db\%1list.txt goto list
echo There is no list of Users for database %1%
echo Cannot Log user off
goto nothingtodo
:list
more s:\db\%1%list.txt
echo.
echo.
set DELUSER=
set /p DELUSER=Please key in the User ID (usr) To Be Deleted:
if "%DELUSER%"=="" goto nothingtodo
echo Preparing to delete %DELUSER%
echo @echo off > s:\db\%1del.bat
echo c:\scripts\%1del %DELUSER% >> s:\db\%1del.bat
goto end
:nothingtodo
echo Nothing to Do
:end

=================================================

delcheck.bat:

OK, so this one does a continual loop, listing the databases' users to separate files, checking to see if there is anything to do (whether the deletion batch files exist) and then running the deltion batch files if they exist, deleting them afterwards.

If file s:db\checkdel.stop exists then the batch file terminates. You would create this file just before taking down the databases and delete it when starting up the databases.

c:\db\mydblist.bat lists the users for mydb
c:\db\mydbdel.bat deletes a user for mydb
c:\belfry is where the bats are

@echo off
:start
cls
call c:\db\scripts\mydblist

rem Pause for 60 seconds
c:\belfry\wait 60

if exist s:\db\checkdel.stop goto end

if exist s:\db\mydbdel.bat goto mydbdel
if exist s:\db\mydb2del.bat goto mydbdel2
if exist s:\db\mydb3del.bat goto mydbdel3

goto start
:mydbdel
call s:\db\sapdel.bat
del s:\db\sapdel.bat
goto start

:mydb2del
call s:\db\mydb2del.bat
del s:\db\mydb2del.bat
goto start

:mydb3del
call s:\db\mydb3del.bat
del s:\db\mydb3del.bat
goto start

rem and so on ....

:end
if exist s:\db\checkdel.stop del s:\db\checkdel.stop

=================================================

mydblist.bat:

@echo off
set dlc=p:\progress\dlc
set promsgs=p:\progress\dlc\promsgs
c:
cd \progress\dlc\bin
c:
cd \db\mydb
echo Listing Mydb Users
call c:proshut mydb -C list > w:\everyone\db\mydblist.txt
cd \db\scripts

=================================================

mydbdel.bat:

@echo off
if "%1"=="" goto end
set dlc=p:\progress\dlc
set promsgs=p:\progress\dlc\promsgs
c:
cd \progress\dlc\bin
c:
cd \db\mydb
echo Deleting Mydb Users
call c:proshut mydb -C disconnect %1
call c:\db\scripts\mydblist
:end

=================================================
 
Billfaith:

> Do your batch files log off people by length of time logged into the
> database? Is it similar to what JJ wrote above?

The ones I was using didn't go by length of time logged on, but this one does.

It is written in javascript, which I don't know particularly well, so there are bound to be ways to make it better. You run it with cscript on windows, preferably with a batchfile rdirecting output to a textfile.

It worked like a dream ealier, especially when I used the live output batch file, so it logged live users off :)

Just copy the javascript below into text editor, notepad will do, and save it off as killuser.js, or whatever you want.

Please let me know if you have any problems with it.

I ran it with a batch file that went something like this:
@echo off
if exist %2%1del.bat del %2%1del.bat
cscript //nologo line.js %1 %2 > %2%1del.log
if exist %2%1del.bat more %2%1del.bat

and you call it as:
killuser.bat mydb s:\db
where mydb is the database name and s:\db is a shared directory that the checkdel.bat script looks at.

I'll tell you one thing - Javascript is a lot fiddlier than Progress, and less readable, and nastier. But, this will work even if you can't compile/run Progress programs.

I hope that helps.

Simon

////////////////////////////////////////////////////////////////////////////////
// killuser.js
//
// This script takes parameters for the database name and working directory
// It then inputs and parses a textfile to find out which users are logged on and how long they have been logged on
// Finally, it creates a batch file containing instructions on deleting users more than 5 hours old
// Copyright (c) Simon Phipp 2005
//
// Usage cscript //nologo killuser.js dbname workingdirectory
//
// As it uses stdout, you have to use cscript
// You could change it to use wscript.echo if you wanted to
//
// Warning - this does not check for permissions, or to see what processes the person is running
// Care should be taken when using this as it will log anyone off who has been logged on for 5 hours
//
////////////////////////////////////////////////////////////////////////////////

var stdout = WScript.StdOut;
var stdin = WScript.StdIn;
var lineno;
var usrinfo = new Array ();
var usrline = new Array ();
var usrcol = new Array ();
var usrdate = new Array ();
var usrno = new Array ();
var usrtime = new Array ();
var usrdiff = new Array ();
var key;
var fso;
var textfile;
var MinMilli = 1000 * 60;
var HrMilli = MinMilli * 60;
var DyMilli = HrMilli * 24;
var ForAppending = 8;
var ForWriting = 2;
var ForReading = 1;
var firsttime;
var objargs;
var i;
objArgs = WScript.Arguments;
var dbname, inputdir, inputfile, batchfile, killbatch;
// Get the starting parameters - warning - if 2 parameters are not apssed, this will error and stop
dbname = "";
dbname = objArgs (0);
inputdir = objArgs (1);
inputfile = inputdir.concat (dbname,"list.txt");
batchfile = inputdir.concat (dbname,"del.bat");
killbatch = "call f:\\db\\scripts\\";
killbatch = killbatch.concat (dbname,"del");
// Write some info to the standard output
stdout.write("Checking Users for Database: ");
stdout.writeline(dbname);
stdout.write("Input File: ");
stdout.writeline(inputfile);
stdout.write("Batch File: ");
stdout.writeline(batchfile);
// Get the starting time - to use as a reference time

firsttime = GetCurrentTime();
lineno = 0;
ReadFiles(inputfile);
WriteResults ();
function ReadFiles(inp_file)
{
var colno;
// Write info to the standard output
stdout.write("Checking File: ");
stdout.WriteLine(inp_file);
fso = new ActiveXObject("Scripting.FileSystemObject");
textfile = fso_OpenTextFile(inp_file, ForReading);
while (!textfile.AtEndOfStream)
{
// Read a line form the textfile to an array variable
lineno++;
usrinfo [lineno] = textfile.ReadLine();
// Split the read-in line into space-delimited fields, parse the fields, ignoring blanks and "-"
colno = 0;
usrline[lineno] = usrinfo[lineno].split (" ");
usrcol[lineno] = new Array ();
for (key in usrline[lineno]) {
if (!(usrline[lineno] [key] == "") && !(usrline[lineno] [key] == "-")) {
colno++;
usrcol[lineno][colno] = usrline[lineno] [key];
}
}

// Build up the date/time from the correct fields
usrdate[lineno] = " ";
usrdate[lineno] = usrdate [lineno].concat (usrcol[lineno][5]," ",usrcol[lineno][4]," ",usrcol[lineno][7]," ",usrcol[lineno][6]);
usrno[lineno] = usrcol[lineno][1];
// Work out the number of hours between the log-on time and the time set earlier
usrdiff[lineno] = NumHours (firsttime - GetOtherTime(usrdate[lineno]));
}
textfile.Close();
}

function GetCurrentTime(){
var d, t;
d = new Date();
t = d.getTime();
return(t);
}
function GetOtherTime(inp_date){
var d, t;
d = new Date(inp_date);
t = d.getTime();
return(t);
}
function NumHours(inp_num){
var h;
h = Math.round (inp_num / HrMilli);
return(h);
}
function WriteResults () {
var dbaction;
fso = new ActiveXObject("Scripting.FileSystemObject");
textfile = fso_OpenTextFile(batchfile, ForWriting, true);
textfile.writeline ("@echo off");
// Write all read-in lines as a reference
for (key = 1; key <= lineno; key++) {
stdout.writeline (usrinfo[key]);
}
stdout.writeline ("");
stdout.writeline ("");
// Decide whether to ignore, keep or kill users, writing the results to the standard output
for (key = 1; key <= lineno; key++) {
dbaction = "Keep ";
if (usrno[key] == "PROGRESS" || usrno[key] == "usr")
dbaction = "Ignore ";
if (usrdiff [key] > 5) {
dbaction = "Kill ";
// textfile.Write("call f:\\db\\scripts\\sapdel ");
textfile.Write(killbatch);
textfile.Write(" ");
textfile.WriteLine(usrno[key]);
}
stdout.write (dbaction);
stdout.write (usrno[key]);
stdout.write (" ");
stdout.writeline (usrinfo[key]);
}
textfile.Close();
}

////////////////////////////////////////////////////////////////////////////////
 

billfaith

New Member
Thanks for all these Simon. I am trying to decipher how these work. I need to sep up a test server because I could see myselft type something wrong and screw it all up.
 

joey.jeremiah

ProgressTalk Moderator
Staff member
Hi Bill,

Following our conversation, I posted a program that will let you run short 4gl scripts
even though you may not have a development license i.e. Admin Script.

Could you install it ( download and run setup.bat ), and run sample.bat to see
if it works ? we can pick it up from there.
 

billfaith

New Member
JJ

I compiled it and ran it. I got a blue screen to come up but I got an error about it not finding a file. I assume its talking about my database. Do I need to change a path?
 
Top