-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
feat: support token substitution in EventStartCommand/EventEndCommand #4618
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
feat: support token substitution in EventStartCommand/EventEndCommand #4618
Conversation
Replace execlp() with execl("/bin/sh") and substitute %EID%, %MID%,
and %EC% tokens before execution. This allows users to pass arguments
directly in the command string, e.g.:
/path/to/zm_detect.py -c /etc/zm/config.yml -e %EID% -m %MID% -r "%EC%" -n
Previously execlp() treated the entire command string as the executable
path, making it impossible to pass arguments without a wrapper script.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR updates how ZoneMinder monitor event hook commands are executed so they can include arguments and receive event metadata via inline token substitution.
Changes:
- Execute
EventStartCommand/EventEndCommandvia/bin/sh -cinstead ofexeclp(...)to support commands with arguments. - Add token substitution for
%EID%and%MID%in both commands, and%EC%(event cause) forEventStartCommand.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| std::string cmd = ReplaceAll(ReplaceAll( | ||
| ReplaceAll(event_start_command, "%EID%", std::to_string(event->Id())), | ||
| "%MID%", std::to_string(event->MonitorId())), | ||
| "%EC%", cause); | ||
| execl("/bin/sh", "sh", "-c", cmd.c_str(), nullptr); |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switching to sh -c means %EC% is now interpolated into a shell command line. cause can include spaces and potentially shell metacharacters, which can change the executed command (and can be a command-injection vector if cause ever comes from an external trigger). Consider shell-escaping cause (and any other non-numeric substitutions) before substitution, or provide a dedicated “shell-escaped” token variant (and/or pass data via environment variables).
| std::string cmd = ReplaceAll(ReplaceAll( | ||
| ReplaceAll(event_start_command, "%EID%", std::to_string(event->Id())), | ||
| "%MID%", std::to_string(event->MonitorId())), | ||
| "%EC%", cause); | ||
| execl("/bin/sh", "sh", "-c", cmd.c_str(), nullptr); | ||
| logInit(log_id.c_str()); | ||
| Error("Error execing %s: %s", event_start_command.c_str(), strerror(errno)); | ||
| Error("Error execing %s: %s", cmd.c_str(), strerror(errno)); |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change removes the previous behavior where EventStartCommand was executed with positional args (event_id, monitor_id) (and EventEndCommand similarly). Existing scripts that relied on argv[1]/argv[2] will now receive no arguments unless the user updates their command to include %EID%/%MID% explicitly. If backward compatibility is required, consider continuing to provide the IDs as arguments (e.g., by structuring the shell invocation so the configured command still receives the same argv) while also supporting token substitution for new configs.
| std::string cmd = ReplaceAll( | ||
| ReplaceAll(command, "%EID%", std::to_string(event_id)), | ||
| "%MID%", std::to_string(monitor_id)); | ||
| execl("/bin/sh", "sh", "-c", cmd.c_str(), nullptr); |
Copilot
AI
Feb 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as EventStartCommand: switching to sh -c here drops the prior positional arguments (event_id, monitor_id) that were passed to EventEndCommand. This is a breaking behavior change for any existing handler that expects argv parameters rather than inlined tokens; consider preserving the old argv contract or documenting/enforcing a migration path.
| execl("/bin/sh", "sh", "-c", cmd.c_str(), nullptr); | |
| std::string event_id_str = std::to_string(event_id); | |
| std::string monitor_id_str = std::to_string(monitor_id); | |
| execl("/bin/sh", "sh", "-c", cmd.c_str(), | |
| event_id_str.c_str(), monitor_id_str.c_str(), (char *)nullptr); |
Summary
execlp()withexecl("/bin/sh", "sh", "-c", cmd, nullptr)inzm_monitor.cppfor bothEventStartCommandandEventEndCommand, so commands can include arguments%EID%,%MID%,%EC%token substitution (using existingReplaceAll()fromzm_utils.h) matching the convention used byzmfilter.pl%EC%(event cause) is only available inEventStartCommand;EventEndCommandsubstitutes%EID%and%MID%Motivation
Previously,
execlp()treated the entire command string as an executable path, making it impossible to pass arguments. This meant users needed wrapper scripts forEventStartCommand/EventEndCommand. With this change, commands like:work directly without a wrapper.
Test plan
EventStartCommandfires with correct%EID%,%MID%,%EC%substitutionEventEndCommandfires with correct%EID%,%MID%substitution🤖 Generated with Claude Code