Page 1 of 1

Spawn new copy of program

Posted: Thu Mar 22, 2018 2:57 am
by mritter0
Is it possible to spawn a totally new copy of a program from itself? Totally independent of the first copy.

IDOS->CreateNewProcTags(
NP_Entry, main,
TAG_END);

And can you pass command line args to the new copy?

Re: Spawn new copy of program

Posted: Fri Mar 23, 2018 12:28 am
by colinw
mritter0 wrote: Is it possible to spawn a totally new copy of a program from itself? Totally independent of the first copy.
And can you pass command line args to the new copy?
Sure, you just effectively run youself and prepare the arguments. You do have to make sure that you are
not already being "run" or are already async from Workbench startup, but it's quite easy.
I'll attach some example source code to play with. (I'll just inline it here.)

Code: Select all


;/* Execute this to compile...
;
gcc run_detached.c  -nostdlib -o  RunDetached
strip -g --strip-unneeded  RunDetached -o RunDetached
quit
*/

/******************************************************************************/
/* "Run_Detached_Test"  Example code by Colin Wenzel. QLD, Australia.         */
/******************************************************************************/
  
#define __NOGLOBALIFACE__
#define __NOLIBBASE__

/******************************************************************************/

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>

#include <dos/startup.h>

/******************************************************************************/

STATIC CONST USED TEXT sversion[]="\0$VER: Run_Detached_Test 53.02 (26.08.2012)\n\r";

/******************************************************************************/

#define NO  !
#define NOT !

/******************************************************************************/

struct globaldata 
{ 
	struct Library      *SysBase;
	struct Library      *DOSBase;
	struct Library      *UtilityBase;

	struct ExecIFace    *IExec;
	struct DOSIFace     *IDos;
	struct UtilityIFace *IUtil;

	struct CommandLineInterface *Cli;
	struct WBStartup *WBMsg;
};



/***************************************************************************/
/* Handy macros to reference the globaldata members */

#define DOSBASE   (gd->DOSBase)
#define SYSBASE   (gd->SysBase)
#define UTILBASE  (gd->UtilityBase)

#define IEXEC     (gd->IExec)
#define IDOS      (gd->IDos)
#define IUTILITY  (gd->IUtil)

#define CLI       (gd->Cli)
#define WBM       (gd->WBMsg)

/***************************************************************************/


static int32 async_main( struct globaldata *gd )
{

	/*
	** This code is now guaranteed to be running ASYNC
	** from either workbench or CLI.
	*/ 


	if( CLI )
	{
		IDOS->PutStr( "Hello from async Cli process.\n\n" );
		IDOS->Printf( "Supplied commandline; %s\n\n", IDOS->GetArgStr() );

		IDOS->PutStr( "\nProgram will exit in 10 seconds.\n" );

		IDOS->Delay(10*TICKS_PER_SECOND);
	}
	else
	{
		IDOS->TimedDosRequesterTags(TDR_Timeout, 10,
							TDR_FormatString, "Hello from async Workbench process.\n",
							TDR_GadgetString, "OK",
							TDR_ImageType, TDRIMAGE_INFO,
							TAG_END );
	}

	return(RETURN_OK);
}




/*****************************************************************************/



/* DOS entry function */

int32  _start(STRPTR args UNUSED, int32 arglen UNUSED, struct ExecBase *sysbase)  
{
	struct ExecIFace *iexec = (APTR)sysbase->MainInterface;
	struct globaldata *gd;
	struct Process *process;
	APTR   wbmsg = NULL;
	int32  rc = RETURN_FAIL;



	iexec->Obtain();
	process = (struct Process *) iexec->FindTask(NULL);
	
	if( NOT process->pr_CLI )  /* must get a workbench startup message first.*/ 
	{
		iexec->WaitPort(&process->pr_MsgPort);
		wbmsg = iexec->GetMsg(&process->pr_MsgPort);
	}

	gd = iexec->AllocVecTags( sizeof(*gd),
		                      AVT_Type,MEMF_SHARED,
		                      AVT_Lock,FALSE,
		                      AVT_ClearWithValue,0,
		                      TAG_END );
	do
	{
		if( NO gd )
		{
			break;
		}

		IEXEC   = iexec;
		SYSBASE = (APTR)sysbase;

		if( NOT (DOSBASE = IEXEC->OpenLibrary ("dos.library", 50) ))
		{
			break;
		}
		if( NOT (IDOS = (struct DOSIFace *)IEXEC->GetInterface(DOSBASE, "main", 1, NULL)))
		{
			break;
		}

		if( NOT (UTILBASE = IEXEC->OpenLibrary ("utility.library", 50) ))
		{
			break;
		}
		if( NOT (IUTILITY = (struct UtilityIFace *)IEXEC->GetInterface(UTILBASE, "main", 1, NULL) ))
		{
			break; 
		}

		CLI = IDOS->Cli();
		WBM = wbmsg;




		/*
		** If we are started from workbench, we are already async.
		** If we are started from the CLI "run" command, we're async too.
		** Only if we are started from CLI and not "run", we just run ourself.
		** The 'cli_Background' is TRUE if we were "run", which includes self running.
		** Check the CLI commandname BSTR as well, just for safety reasons.
		*/

		if(( CLI ) && ( NOT CLI->cli_Background ) && ( CLI->cli_CommandName )) 
		{
			TEXT   command[256+256];

			/*
			** Async output stream may simply be a "NIL:" spec (ZERO)
			** or something more exotic like a console window if you prefer.
			*/
			BPTR   stream = IDOS->Open("CON:20/30/600/180/StdOutput//",MODE_OLDFILE);
		//	BPTR   stream = ZERO;

			/*
			** We need to put our command name in double quotes
			** incase it contains a space, then append our supplied
			** arguments to that, after one space.
			*/
			IUTILITY->SNPrintf(command,sizeof(command),"\"%b\" %s", 
		                       CLI->cli_CommandName, IDOS->GetArgStr() );

			rc = IDOS->SystemTags(command, 
			                      SYS_Input,  stream,
			                      SYS_Output, ZERO,
			                      SYS_Error,  ZERO, 
			                      SYS_Asynch, TRUE,
			                      TAG_END );
			if( rc )
			{
				rc = RETURN_ERROR;   /* transform to a CLI error code. */
				IDOS->Printf("Unable to start async CLI process; %s\n",command);

				IDOS->Close(stream); /* ZERO is safe */
			}		
			else
			{
				IDOS->PutStr("Async CLI process started successfully. \n");
			}

			break;	/* we leave this command instance now */
		}

		rc =  async_main(gd);
	}
	while(0);

	if( gd )
	{
		IEXEC->DropInterface((APTR)IDOS);		/* NULL safe for these */
		IEXEC->DropInterface((APTR)IUTILITY);

		IEXEC->CloseLibrary(DOSBASE);
		IEXEC->CloseLibrary(UTILBASE);			/* NULL safe for these */

		IEXEC->FreeVec(gd);
	}

	if( wbmsg )               /* reply to any workbench startup message */ 
	{
		iexec->ReplyMsg( wbmsg );
	}

	iexec->Release();
	return(rc);
}


/*****************************************************************************/
/* EOF */

Re: Spawn new copy of program

Posted: Fri Mar 23, 2018 1:24 pm
by nbache
colinw wrote:You do have to make sure that you are not already being "run" or are already async from Workbench startup
Why?

Is there anything preventing one asynchronous process from spawning another asynchronous one?

I'm just curious, I don't know whether that is what mritter0 wanted in the first place.

Best regards,

Niels

Re: Spawn new copy of program

Posted: Fri Mar 23, 2018 3:42 pm
by xenic
nbache wrote: I'm just curious, I don't know whether that is what mritter0 wanted in the first place.
Yea. My impression was that he wants to run a program like an AmigaDOS 'Resident' command. The code Colin provided looks like an OS4 safe way to detach a program that was initially run synchronously. Maybe mritter0 should clarify exactly what he meant.

Re: Spawn new copy of program

Posted: Fri Mar 23, 2018 3:52 pm
by mritter0
@colinw

That is what I have been doing, using SystemTags() to launch a new copy, with optional command line args.

I was just curious if there was an "internal" way to do it. It is for Workbench Explorer, to open more windows for people who want multiple listers.

I was thinking maybe you can't enter at main(), or I am not giving it enough/correct info. I was thinking of splitting main() into 2 functions. main() do very little and pass the command line args to main2() which does the rest of the initial setup. Then the new process would start there.

CreateNewProc(
NP_Entry, main2,
NP_Arguments, "blah=doh QUIET",
TAG_DONE);

I want each process to be totally independent so the original copy can be closed at any time and not affect the others.

Re: Spawn new copy of program

Posted: Fri Mar 23, 2018 9:59 pm
by salass00
mritter0 wrote: I want each process to be totally independent so the original copy can be closed at any time and not affect the others.
The simplest way to achieve this is with SystemTags().

If you use CreateNewProc() with NP_Entry then the created process will be dependent on the seglist of the parent process and if the parent process exits before the child things will go badly.

Re: Spawn new copy of program

Posted: Fri Mar 23, 2018 10:42 pm
by colinw
nbache wrote:
colinw wrote:You do have to make sure that you are not already being "run" or are already async from Workbench startup
Why?
Is there anything preventing one asynchronous process from spawning another asynchronous one?
I'm just curious, I don't know whether that is what mritter0 wanted in the first place.
Best regards,
Niels
The answer is outlined in the IDOS->RunCommand() autodoc, but i'll mention it here anyway.

The reason is because when you start anything from WB, it creates a distinct new process with CreateNewProc().
When you start a program from the shell with the "RUN" command, it creates a distinct new process with System().
When you start a program from the shell without "RUN", it does NOT create a new process but uses the existing
shell process to launched it via RunCommand(), the shell remains "busy" while the program runs on its process.

So, this code effectively checks to see if it's a WB or RUN launched program first, before detaching itself,
simply because it would be entirely pointless doing it twice, plus you would have to use some other
custom method, other than the standard system method to determine if you had already run yourself before.

Re: Spawn new copy of program

Posted: Fri Mar 23, 2018 10:55 pm
by colinw
mritter0 wrote:@colinw
I want each process to be totally independent so the original copy can be closed at any time and not affect the others.
Then that code is what you need, all that's required is to substitute main() for _start() and change the arguments to suit.
Basically, just use the bit between "do{ ... }while(0); and remove the library opening and interfaces stuff at the top
above; CLI=IDOS->Cli(). Also, remove the macros / globaldata references and just use a local variable for "CLI".

Each copy will have it's own i/o streams and locks and not be attached in any way to other running copies.

Re: Spawn new copy of program

Posted: Fri Mar 23, 2018 11:15 pm
by broadblues
@mritter

If you go down that route, bear in mind that you can't have any globals, or you do they will need semapahore protection.

If you didn't design it that way from the get go, you might be making a rod to beat your own back.

Re: Spawn new copy of program

Posted: Fri Mar 23, 2018 11:30 pm
by mritter0
I will just stick with what I have. Just checking out my options.

Thanks, guys.