Recent

Author Topic: Commandline parsing  (Read 1294 times)

MarkMLl

  • Hero Member
  • *****
  • Posts: 7527
Commandline parsing
« on: May 28, 2024, 10:52:01 am »
On unix, which in practice means Linux, if system.argv and system.argc were changed would ParamStr() and ParamCount() track them?

This is an entirely speculative ponder but I was wondering whether, if they were changed to point to an area of shared memory, a SIGHUP via the sigqueue() syscall could be used to pass an updated commandline to a longrunning program.

I'm obviously aware that there's plenty of other ways to do this, for example a unix-domain socket.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Thaddy

  • Hero Member
  • *****
  • Posts: 15555
  • Censorship about opinions does not belong here.
Re: Commandline parsing
« Reply #1 on: May 28, 2024, 11:44:30 am »
They are supposed to be direct mappings to the system argc/argv. Examine the source and you will see that.
That also means that once a program is started it does not listen to any changes, unless you would use like you suggest. ( Sorry, interesting, but I do not care to test this )
« Last Edit: May 28, 2024, 11:49:08 am by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

MarkMLl

  • Hero Member
  • *****
  • Posts: 7527
Re: Commandline parsing
« Reply #2 on: May 28, 2024, 02:01:44 pm »
They are supposed to be direct mappings to the system argc/argv. Examine the source and you will see that.
That also means that once a program is started it does not listen to any changes, unless you would use like you suggest. ( Sorry, interesting, but I do not care to test this )

Having (before I posted) looked at the source I see

Code: Pascal  [Select][+][-]
  1. var argv: PPChar;
  2. ...
  3.      paramstr:=strpas(argv[l])
  4.  

However I'm not confident enough in my C to be comfortable with that... I read it as the OS presenting the RTL with a pointer to an array of pointers which I suppose is reasonable.

However argc is a longint, not a pointer to a longint, so even if the long-running program had changed its argv to point to a block of shm (with a known name, perhaps in /var/run in lieu of a pid file) the number of parameters wouldn't track correctly... unless it took that from the numeric SIGHUP parameter instead of that being a pointer to the memory block.

So /in/ /principle/ it might be possible to do this in a way which would be picked up by ParamStr() etc. But I'd be the first to admit that it's rather a nitwit idea: it's a pity though that there isn't an easy way of passing more than just an integer/pointer with a SIGHUP.

To borrow from Pournelle: "unix is mostly a collection of nitwit ideas that worked".

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

rvk

  • Hero Member
  • *****
  • Posts: 6328
Re: Commandline parsing
« Reply #3 on: May 28, 2024, 03:06:22 pm »
You could still use the SIGHUP to signal the process and then pass your data trough a named pipe (both both processes need to be active for that).

Otherwise you could use shared memory but for that both processes need to be child of the same main process (which might not be the case here).


MarkMLl

  • Hero Member
  • *****
  • Posts: 7527
Re: Commandline parsing
« Reply #4 on: May 28, 2024, 03:23:59 pm »
You could still use the SIGHUP to signal the process and then pass your data trough a named pipe (both both processes need to be active for that).

That is true, but I did say that I was aware of multiple ways of doing it.

Quote
Otherwise you could use shared memory but for that both processes need to be child of the same main process (which might not be the case here).

Named shared memory represented by a file and made accessible in the namespace using mmap(). However at that point it would almost be as easy to rewrite a .ini file.

I first started going down the rabbithole due to an erroneous assumption that the kernel buffered a message automatically. However once I started looking at the various parameter blocks etc. it was obvious that wasn't the case: the bottom line is that using the standard signal mechanism (specifically kill with the -q option) you can pass a single integer (which might in practice be a pointer to pre-shared memory) but that's the limit.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Thaddy

  • Hero Member
  • *****
  • Posts: 15555
  • Censorship about opinions does not belong here.
Re: Commandline parsing
« Reply #5 on: May 28, 2024, 04:42:19 pm »
I had my afternoon nap and  8) , your idea is achievable (not on all systems) by using the envp pointer that is a pointer to the environment block that is passed to the executable.
The argc and argv pointers are part of that.
See: https://www.freepascal.org/docs-html/rtl/system/envp.html

Another thing is you (I already wrote that they map directly to Pascal elsewhere) can use argc and argv directly. See the same doc page.
So basically re-reading the environment block after it has changed.

The syscalls you mentioned should be in baseUnix, possibly prefixed with fp. Did not check that.(yet).
It may be wishful thinking but, maybe, maybe, that block is re-read when accessed...

You've got me curious after all... O:-)

edit: you may need the dos unit (which is a misnominer because it is cross-platform)  for easy environment access, but argc/argv/env are part of system.
« Last Edit: May 28, 2024, 05:24:41 pm by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

MarkMLl

  • Hero Member
  • *****
  • Posts: 7527
Re: Commandline parsing
« Reply #6 on: May 28, 2024, 05:47:35 pm »
The problem there is that I don't think that argc exists as a pointer anywhere: only as a scalar. So the best that could be done would be something like:

* In /var/run, the long-running program sets up a block of shm (mmap in appropriate places in what follows).

* At the start of this it writes pid followed by /n and some zero padding and a magic number, so that it looks like a conventional pid file.

* Once its finished processing its own options and parameters, it changes argv to point (say) 512 bytes into the shm block and zeroes argc.

* A newly-started program with e.g. RESTART as its final parameter checks the shm block's magic number then sets up a ppchar 512 bytes into it and copies its options and parameters.

* The newly-started program then sends a SIGHUP using sigqueue() to the pid in the shm block, passing its argc as the integer parameter.

* The long-running process copies the integer parameter into argc. From that point ParamCount() and ParamStr() should just work.

I tweaked an existing signal handler yesterday morning to look like this:

Code: Pascal  [Select][+][-]
  1. var     sigNo, sigErr, sigCode, sigPid: integer; (* For debugging   *)
  2.         sigVal: ptruint;
  3.  
  4.  
  5. (* We don't ever want to screw the MCP by responding to a casual ^C. We do,
  6.   however, want to do our best to shut down in good order if we get an urgent
  7.   signal such as SIGTERM, since it might indicate an incipient power failure.
  8. *)
  9. procedure termHandler(sig: longint; info: PSigInfo; context: PSigContext); cdecl;
  10.  
  11. const
  12.   SI_USER=      0;              (* sent by kill, sigsend, raise *)
  13.   SI_KERNEL=    $80;            (* sent by the kernel from somewhere *)
  14.   SI_QUEUE=     -1;             (* sent by sigqueue *)
  15.   SI_TIMER=     -2;             (* sent by timer expiration *)
  16.   SI_MESGQ=     -3;             (* sent by real time mesq state change *)
  17.   SI_ASYNCIO=   -4;             (* sent by AIO completion *)
  18.   SI_SIGIO=     -5;             (* sent by queued SIGIO *)
  19.   SI_TKILL=     -6;             (* sent by tkill system call *)
  20.   SI_DETHREAD=  -7;             (* sent by execve() killing subsidiary threads *)
  21.   SI_ASYNCNL=   -60;            (* sent by glibc async name lookup completion *)
  22.  
  23. begin
  24.   with info^ do begin                   (* For debugging                        *)
  25.     sigNo := si_signo;
  26.     sigErr := si_errno;
  27.     sigCode := si_code;                 (* See SI_USER, SI_QUEUE etc. above     *)
  28.     sigPid := _sifields._kill._pid;
  29.     sigVal := ptruint(_sifields._rt._sigval) (* Used by sigqueue but not by kill *)
  30.   end;
  31.   case sig of
  32. { TODO : Trouble here: ^C kills radio process even if close is cancelled. }
  33.     SIGINT:   shutdown := true;
  34.     SIGUSR1:  dumpDebug := true;
  35.     SIGUSR2,
  36.     SIGHUP,
  37. { TODO : Receiver process restart stuff. See https://man7.org/linux/man-pages/man3/sigqueue.3.html
  38. which might be useful as a way of passing parameters in lieu of a command line
  39. or .ini file. }
  40. // kill -q can pass an integer, killall has no provision for this.
  41.     SIGQUIT,
  42.     SIGTERM,
  43.     SIGPWR:   begin
  44.               end
  45.   otherwise
  46.   end
  47. end { termHandler } ;
  48.  

Noting that that implies an extension to what the RTL documents regarding PSigInfo etc. However I've not tested it yet, and won't be at least until I investigate some process group stuff.

In practice, the process group stuff is far more pressing: I've got a background program running via a TProcess which in some cases I want to be able to restart and in other cases (e.g. if I SIGINT the foreground program but change my mind) I definitely don't want to interfere with.

And allowing that the pressing issue is to be able to change the gain background process's signal path, there's lots of better ways of moving commands around including- obviously- a menu entry or control on the frontend GUI.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

 

TinyPortal © 2005-2018