Skip to content

fix: make start.sh --stop skip nodes in other directories - #7075

Open
SeriousCoding789 wants to merge 3 commits into
tronprotocol:release_v4.8.3from
Little-Peony:fix/start-sh-stop-own-node
Open

SeriousCoding789 wants to merge 3 commits into
tronprotocol:release_v4.8.3from
Little-Peony:fix/start-sh-stop-own-node

Conversation

@SeriousCoding789

@SeriousCoding789 SeriousCoding789 commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

start.sh --stop now stops only the node started from the current directory when that directory has a start.log but no <jar name>.pid, that is, a node started by an earlier version of the script or one whose pid file was removed. Such a node is matched by its jar name and its working directory, instead of by its jar name alone. The process recorded in a pid file is checked the same way, so an id reused by a node of another directory no longer gets that node stopped.

  • start.sh
    • Add processDir, which reads a process's working directory from /proc/<pid>/cwd on Linux and with lsof on macOS.
    • Add matchDirPid, which keeps the jar-name matches whose working directory is the current directory (pwd -P); checkPid uses it when there is no pid file.
    • With a pid file, a recorded process whose working directory reads as another directory is taken as a stopped node and the pid file is emptied. If its directory cannot be read, the pid file is kept and the process is stopped as before.
    • Without a pid file, a process whose working directory cannot be read and that this user can signal ends --stop and the stop step of a restart with an error on stderr, which asks for the node's process id in the pid file; no signal is sent. A process this user cannot signal (another user's, or one already gone) is left running with a warning.
  • shell.md: describe the directory check, and what to do when a working directory cannot be read.

Why are these changes required?

#7003 keeps the node's process id in <jar name>.pid and still finds a node of the earlier script by its jar name. That lookup takes every process on the machine whose command line has -jar <jar name>, and all of them are passed to kill -15, then to kill -9 after MAX_STOP_TIME. On a host that runs several nodes with the same jar name in different directories, --stop or a restart in one directory also stops the nodes of the other directories, and the restart starts only its own node again. This happens even when the node of the current directory is not running.

The earlier script stopped nothing when more than one process matched (its [ $pid ] test fails on several ids), and otherwise stopped the one match, whatever directory it ran from.

The pid-file path checked only the jar name as well: when a node stopped without its pid file being emptied and its id was later reused by a node that runs the same jar from another directory, --stop stopped that node.

Both the earlier and the current script start the node from its directory, and the JVM keeps that working directory, so the working directory tells the node started from a directory apart from the nodes of other directories.

This PR has been tested by:

  • Manual Testing on macOS (bash 3.2, lsof). Dummy processes with -jar <test jar> in their command line ran from separate directories, and --stop ran with the script before and after this change:
Scenario Before After
No pid file in A; A and B running; --stop in A A and B stopped A stopped, B running
No pid file in A; only B running; --stop in A B stopped B running
Pid file in A; A and B running; --stop in A A stopped, B running A stopped, B running
No pid file; directory name with a space, entered through a symlink both stopped A stopped, B running
First row, run with sh start.sh both stopped A stopped, B running
No pid file in B; processes in A, B and their parent directory; --stop in B all stopped B stopped, the others running
First row with a PATH that has no lsof n/a error on stderr and exit 1, A and B running; after A's id is written to the pid file, A stopped, B running
Pid file in A holding the id of B's node (a reused id); only B running; --stop in A B stopped B running, pid file emptied, warning on stderr
A process this user cannot signal (pid 1) passed through matchDirPid n/a left out with a warning on stderr, no error
  • bash -n start.sh; shellcheck start.sh reports nothing new compared with the base.
  • Not yet run: the /proc branch on Linux, and a restart of a real node.

Follow up

None.

Extra details

  • With a pid file, only a working directory read as another directory overrides the recorded process. One that cannot be read keeps it, which is what writing the node's id to the pid file relies on after the error.
  • matchNodePid, the stop loop and the way a node is started are unchanged, and no Java code is changed.
  • Reading the working directory of another user's process needs root on Linux and macOS, as does stopping it, so another user's node is matched only when the script runs as root; otherwise it is left running with a warning.
  • The error needs a process that this user can signal but whose working directory cannot be read. In practice that is a system without /proc or lsof: Linux has /proc, and macOS ships lsof in /usr/sbin.

A directory with a start.log but no pid file was used by an earlier
version of the script, and --stop found its node by the jar name alone,
so it also stopped nodes with the same jar name in other directories.
Pick the node whose working directory is the current directory, read
from /proc on Linux or with lsof on macOS. When neither is available,
exit with an error that asks for the process id in the pid file.
Comment thread start.sh Resolved
Comment thread start.sh Resolved
The pid file path matched only the jar name, so a recorded id reused by a
node that runs the same jar from another directory would have stopped
that node. A recorded process whose working directory reads as another
directory is now taken as a stopped node and the pid file is emptied;
one whose directory cannot be read keeps the pid file and is stopped as
before.

Without a pid file, a process whose working directory could not be read
was skipped silently, so --stop could report the node stopped while it
was still running. If this user can signal such a process, --stop and
restart now end with an error on stderr; a process this user cannot
signal is left running with a warning.
Comment thread start.sh Outdated
Comment on lines 498 to 501

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NIT] Propagate stop failures and prevent unsafe restarts.

If a managed process is still running but cannot be signalled, stopService() may repeatedly print kill errors and eventually return success because its final command is sleep 5.

Meanwhile, restart() ignores the stop result and proceeds with rebuilding and starting the node, potentially overwriting the PID file of the still-running process.

As a separate follow-up, please ensure that stopService() returns a failure when a managed process remains running but cannot be stopped, and that restart() aborts subsequent steps on such failures.

Please distinguish this from the case where no node was previously started, so first-time --run remains supported. Also, recheck process liveness after a failed kill, since the process may have exited normally between inspection and signalling.

This is pre-existing behavior and need not block the current PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, addressed in b2a4b67 rather than as a follow-up, since the change is small. stopService now signals each process separately, and a failed kill counts only if the process still exists afterwards, so one that exited in between is treated as stopped. If a process cannot be signalled, or the node still runs after SIGKILL, it reports an error on stderr and returns 2: --stop exits with that status, and restart stops there before starting anything. A return of 1 still means no node was ever started from the directory, so a first --run is unchanged.

Comment thread shell.md Outdated
```

`--run` records the process id in `<jar name>.pid` (`FullNode.jar.pid` by default) next to `start.log`, and `--stop` reads it. Run `--stop` in the directory the node was started from, with the same `-j` name if one was given. After `--release` or `-cb` the node runs in `FullNode/`; `--stop` finds it there from the parent directory as well. A node started by an earlier version of the script (a `start.log` but no pid file) is still found by its jar name.
`--run` records the process id in `<jar name>.pid` (`FullNode.jar.pid` by default) next to `start.log`, and `--stop` reads it. Run `--stop` in the directory the node was started from, with the same `-j` name if one was given. After `--release` or `-cb` the node runs in `FullNode/`; `--stop` finds it there from the parent directory as well. `--stop` only stops a process that runs the jar from that directory, read from `/proc` on Linux or with `lsof` on macOS, so nodes with the same jar name in other directories keep running; a pid file whose process now runs from another directory counts as a stopped node. A node started by an earlier version of the script (a `start.log` but no pid file) is found by its jar name; if the working directory of such a process cannot be read, `--stop` exits with an error: write the node's process id to the pid file and run `--stop` again.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NIT, documentation] Clarify the limits of directory isolation in --stop.

Could we qualify the statement that --stop only stops processes running from the target directory?

As discussed, the PID-file path intentionally trusts the recorded PID when its CWD cannot be inspected. In this fallback case, a stale PID reused by another process running the same JAR name could still be selected and stopped.

Please clarify in the documentation that directory isolation is guaranteed only when CWD inspection succeeds, and that the PID-file fallback requires the recorded PID to be independently verified.

This would accurately describe the intended compatibility tradeoff without requiring implementation changes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, clarified in b2a4b67: shell.md now says the directory check needs a readable working directory, and that when the process in the pid file cannot be inspected, the recorded id is trusted as it is, so it has to be known to belong to this node (for example when it is written into the pid file by hand).

stopService ended with sleep 5 and returned success even when the node
was still running: a process this user cannot signal made every kill
fail until the timeout, and a restart then started another node and
wrote its id over the pid file of the running one. A failed signal now
counts while the process still exists, and a node still running after
SIGKILL fails too; both report an error on stderr and return 2, which
ends --stop with that status and stops a restart before it starts a
node. A return of 1 still means no node was ever started from the
directory, so --run starts the node as before.

shell.md now states that the directory check needs a readable working
directory, and that a pid file whose process cannot be inspected is
trusted as recorded.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants