Skip to content

Conversation

@pliablepixels
Copy link
Member

Summary

  • Replace execlp() with execl("/bin/sh", "sh", "-c", cmd, nullptr) in zm_monitor.cpp for both EventStartCommand and EventEndCommand, so commands can include arguments
  • Add %EID%, %MID%, %EC% token substitution (using existing ReplaceAll() from zm_utils.h) matching the convention used by zmfilter.pl
  • %EC% (event cause) is only available in EventStartCommand; EventEndCommand substitutes %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 for EventStartCommand/EventEndCommand. With this change, commands like:

/usr/bin/zm_detect.py -c /etc/zm/objectconfig.yml -e %EID% -m %MID% -r "%EC%" -n

work directly without a wrapper.

Test plan

  • Verify EventStartCommand fires with correct %EID%, %MID%, %EC% substitution
  • Verify EventEndCommand fires with correct %EID%, %MID% substitution
  • Verify commands with arguments execute correctly (no "Bad file descriptor" errors)

🤖 Generated with Claude Code

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>
Copilot AI review requested due to automatic review settings February 11, 2026 11:48
Copy link

Copilot AI left a 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/EventEndCommand via /bin/sh -c instead of execlp(...) to support commands with arguments.
  • Add token substitution for %EID% and %MID% in both commands, and %EC% (event cause) for EventStartCommand.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +3239 to +3243
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);
Copy link

Copilot AI Feb 11, 2026

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).

Copilot uses AI. Check for mistakes.
Comment on lines +3239 to +3245
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));
Copy link

Copilot AI Feb 11, 2026

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.

Copilot uses AI. Check for mistakes.
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);
Copy link

Copilot AI Feb 11, 2026

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.

Suggested change
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);

Copilot uses AI. Check for mistakes.
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.

1 participant