Recent

Author Topic: Starting detached program  (Read 3768 times)

hakelm

  • Full Member
  • ***
  • Posts: 161
Starting detached program
« on: July 04, 2018, 05:28:20 pm »
I want to start an external detached program from a Lazarus main application under Linux. To this end I use the following copied from http://wiki.freepascal.org/Executing_External_Programs#Run_detached_program.
Code: Pascal  [Select][+][-]
  1. procedure startprogram(exe, param: string);
  2. var AProcess: TProcess; I: Integer;
  3. begin
  4.   AProcess := TProcess.Create(nil);
  5.   try
  6.     AProcess.InheritHandles := False;
  7.     AProcess.Options := [];
  8.     AProcess.ShowWindow := swoShow;
  9.     for I := 1 to GetEnvironmentVariableCount do
  10.       AProcess.Environment.Add(GetEnvironmentString(I));
  11.     AProcess.Executable:= exe;
  12.     AProcess.Parameters.Add(param);
  13.     AProcess.Execute;
  14.   finally
  15.     AProcess.Free;
  16.   end;
  17. end;      
This nicely starts my external program but when  I close my main program the external program also exits if the external program wasn't already running when called upon.
I can start an external program that survives the main program with LCLIntf.opendocument but that is really not what I want.
Some help please
H

jamie

  • Hero Member
  • *****
  • Posts: 7587
Re: Starting detached program
« Reply #1 on: July 04, 2018, 09:58:58 pm »
I am not a Linux coder but I do know a little about external apps..

Keep the TProcess Instance you created alive through out the course of your main app.

Assign the OnForkEvent so your main app can get notified of it's closing..

When the event gets called, you can then signal the Process instance to be invalid, use TAG or what ever.

When you close your main app, test for the process instance you create to see if it is still
alive and if so, call the Process.Terminate//

Then Free all of the TPocesses you create before exiting your main app.


that is the best I understand it, as far as I know (windows) all you can do is have the child process inherent
the handles of the parent process, but I don't think this is going to help here.

The only true wisdom is knowing you know nothing

hakelm

  • Full Member
  • ***
  • Posts: 161
Re: Starting detached program
« Reply #2 on: July 05, 2018, 02:03:49 pm »
Thanks, in Linux this is, however, rather easy to accomplish in a shell so instead of using the flawed suggestion from the tutorial
http://wiki.lazarus.freepascal.org/Executing_External_Programs#Run_detached_program
I do it in this, perhaps not so beautiful, way:

Code: Pascal  [Select][+][-]
  1. procedure startdetachedprogram(cmd: string);
  2. var sh:tstringlist;
  3. const fn='/dev/shm/startdetachscript';
  4. begin
  5.   sh:=tstringlist.Create;
  6.   sh.add('nohup '+cmd+' >/dev/null 2>&1 &');
  7.   sh.add('disown');
  8.   sh.add('exit');
  9.   sh.SaveToFile(fn);
  10.   fpsystem('bash < '+fn);
  11.   deletefile(fn);
  12.   sh.Free;
  13. end;  
H


westlock

  • Newbie
  • Posts: 2
Re: Starting detached program
« Reply #3 on: October 21, 2022, 12:19:07 pm »
Similar, shorter Variant with TProcess, using bash without separate file, starting a subshell, like:
Code: Pascal  [Select][+][-]
  1.  bash -c "(nohup program -args &)"

Code: Pascal  [Select][+][-]
  1. procedure restartSelf;
  2. VAR Process : TProcess;
  3.  begin
  4.   Process := TProcess.Create(nil);
  5.     try
  6.       // started program shall not be a child process
  7.       Process.executable:='/usr/bin/bash';
  8.       Process.Parameters.add('-c');
  9.       Process.Parameters.add('(/usr/bin/nohup ' + Application.ExeName + ' ' + args + ' &)');
  10.       Process.Execute;      
  11.     finally
  12.       Process.Free;
  13.     end;
  14.  end;

Warfley

  • Hero Member
  • *****
  • Posts: 2040
Re: Starting detached program
« Reply #4 on: October 21, 2022, 05:39:04 pm »
Linux does not kill a process just because the parent process died. But any child process inherits the output stream of the parent process, which goes to the same terminal. So what you are probably experiencing is that the terminal closes and thereby either closes the application through the hang up signal (SIGHUP) or because the input/output/error stream got closed and this causes an error when trying to write to it.

This can be circumvented with the nohup utility, as written previously, but I feel that I have to warn you, to avoid calling either fpsystem or using TProcess for calling bash. There is absolutely no reason to start up a full scale scripting language interpreter (like bash) for such a simple task. In fact, depending on what inputs you give it (e.g. arguments from user input) this can be a security risk.
nohup already redirects the output to a file, no need to call bash at all for file redirection.

If the process you start doesn't produce output, you don't even need nohup, you can also very easiely do it yourself. Nohup does nothing else than simply calling signal to set SIGHUP on ignore, before executing the target process. In your programm you can simply before starting the process set SIGHUP to ignore, start the process (which inherits the signal handling configuration) and then revert it afterwards back to normal for your main program.

If you want to log the output and have nohup functionality without relying on nohup, this is also quite simple, you just need to do the fork execvp yourself. Not tested, but it should look like this:
Code: Pascal  [Select][+][-]
  1. procedure RunDetached(Exec: String; Args: TStringArray; const OutputFile: String);
  2. var
  3.   fs: TFileStream;
  4. begin
  5.   if fpFork <> 0 then Exit; // Parent process can return
  6.   fs := TFileStream.Create(OutputFile, fmCreate);
  7.   try
  8.     fpdup2(fs.Handle, StdOutputHandle); // Redirect output to file
  9.     fpdup2(fs.Handle, StdErrorHandle); // Redirect error output to file
  10.   finally
  11.     fs.Free;
  12.   end;
  13.   SetLength(Args, Length(Args) + 1); // Must be 0 terminated
  14.   fpSignal(SIGHUP, SIG_IGN); // Ignore Terminal closing signal
  15.   // Execute process
  16.   fpexecvp(PChar(Exec), PPChar(Args));
  17. end;

nohup isn't doing pretty much anything different (well it has more error handling but this is just proof of concept), so this is extremely simple to do

westlock

  • Newbie
  • Posts: 2
Re: Starting detached program
« Reply #5 on: November 01, 2022, 04:53:01 pm »
Very nice explanation and certainly better than bash+nohup. Your code did not compile, I found it to be working like this:

Code: Pascal  [Select][+][-]
  1. procedure RunDetached(Exec: String; Args: String; const OutputFile: String = '/dev/null');
  2. var
  3.   fs: TFileStream;
  4. begin
  5.   if fpFork <> 0 then Exit; // Parent process can return
  6.   fs := TFileStream.Create(OutputFile, fmCreate);
  7.   try
  8.     fpdup2(fs.Handle, StdOutputHandle); // Redirect output to file
  9.     fpdup2(fs.Handle, StdErrorHandle); // Redirect error output to file
  10.   finally
  11.     fs.Free;
  12.   end;
  13.   fpSignal(SIGHUP, signalhandler(SIG_IGN)); // Ignore Terminal closing signal
  14.   fpexecvp(PChar(Exec), StringToPPChar(Args,1)); // Execute process
  15. end;
  16.  

 

TinyPortal © 2005-2018