Siemens Unified MTP1200 Scripting: Delete Files on Network Shares

David Krause11 min read
HMI ProgrammingSiemensTutorial / How-to
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Overview

The Siemens SIMATIC Unified Comfort Panel MTP1200 (and the wider MTP1000/1500/1900/2200 Unified family) exposes a JavaScript-based runtime environment that can launch native Linux shell scripts through the StartProgram function. Out of the box, the panel can copy files to a configured SMB/CIFS network share using the bundled helper /opt/siemens/App_Restriction/copy.sh, but file deletion, renaming, or directory cleanup from the share is not exposed as a built-in method on every firmware revision. Engineers must therefore deploy a custom .sh script and trigger it from the runtime. This article documents the correct location for that script, the user/permission model that determines whether the script can actually write to the share, the difference between scheduled-task and runtime execution, and the recommended way to drive the script from a PLC tag.

Prerequisites

  • SIMATIC Unified Comfort Panel MTP1200 (or compatible MTP1x00/2x00) running WinCC Unified V17, V17 Update 6, V18, or V18 Update 2.
  • Configured network drive under Control Panel → Network and Internet → Network drive, as documented in the TIA Portal Unified Comfort Panels operating manual — Network drive section.
  • SMB/CIFS share with write permission for the Linux user industrial (default runtime identity).
  • TIA Portal project with the HMI device configured and a runtime script in the project tree (Scripts → Runtime scripts).
  • For PLC-driven deletion: an HMI tag of data type Bool or Int exposed on the panel's HMI connection.

Runtime User Model: industrial vs. scs

Every script that runs on a Unified Comfort Panel executes under one of two system identities. Misunderstanding this model is the single most common reason a StartProgram call works in the test environment but fails in production.

Execution context Linux user Group Typical use
Scheduled task (system scheduler) scs industrial (V17 Update 6+) Periodic housekeeping, log rotation, backups
Runtime script (dynamization / event / script tag) industrial industrial PLC-triggered actions, operator-button events

User privileges are defined by the system image and cannot be changed from the runtime. The following firmware-level behavior changes are relevant:

  • V17 (GA through Update 5): the share is writable only by the industrial user. A scheduled task running as scs cannot delete or overwrite files on the mounted share.
  • V17 Update 6: the user group industrial is granted write permission on the share mount, and both scs and industrial are members of that group. Scheduled tasks now succeed.
  • V18 Update 2: the group-based write permission is the documented default and is preserved across reboots.
Rule of thumb: if the action must be initiated by a PLC tag, the script must be a runtime script so that it executes as industrial. If the action is purely time-based, a scheduled task is acceptable from V17 Update 6 onward.

Why copy.sh Lives in /opt/siemens/App_Restriction/

The directory /opt/siemens/App_Restriction/ is part of the read-only system image. Only the Siemens build process or an admin-level maintenance login can write to it. The copy.sh helper is delivered there so that the industrial user can execute it without any additional privilege escalation. Custom shell scripts placed anywhere under /opt/siemens/... will not be writable from the runtime, and copying the helper out of App_Restriction/ generally breaks it because the script depends on relative paths and SELinux/SMACK labels set during provisioning.

For user-supplied scripts, the supported writable location is /home/industrial/. Subdirectories created there inherit the industrial:industrial ownership, which is the only combination the runtime can both write to and execute.

Step-by-Step: Build the Delete Script

1. Author the bash script locally

Create delete_share_file.sh on your engineering workstation. Use the shell $1 positional parameter so that the target path is supplied by the runtime, not hard-coded:

#!/bin/bash
# delete_share_file.sh
# Usage: delete_share_file.sh "<mount_relative_path>"
TARGET="$1"
MOUNT="/home/industrial/share"   # default network-drive mount point

if [ -z "$TARGET" ]; then
  echo "No target specified" >&2
  exit 2
fi

FULL="${MOUNT}/${TARGET}"
if [ ! -e "$FULL" ]; then
  echo "Not found: $FULL" >&2
  exit 3
fi

rm -f -- "$FULL"
echo "OK $FULL"
exit 0

Make it executable locally so accidental FTP transfers preserve the bit:

chmod 755 delete_share_file.sh

2. Transfer the script to the panel

Two equivalent methods are supported:

  1. File Manager (Control Panel): Connect a USB stick or point the panel's file manager at the same network share that you will later write to. Copy delete_share_file.sh into /home/industrial/. The default ownership is preserved by the industrial session that the File Manager runs as.
  2. WriteFile from a runtime script: Embed the script body as a string constant in a JavaScript runtime script and write it to disk on first run:
// HMI Runtime JavaScript (V17/V18)
var script = "#!/bin/bash\nrm -f -- \"$1\"\nexit 0\n";
var path   = "/home/industrial/delete_share_file.sh";
var fso    = new ActiveXObject("Scripting.FileSystemObject");
// Use the Files C# helper exposed by Unified panels:
HMIRuntime.FileSystem.WriteFile(path, script, "utf-8");
HMIRuntime.FileSystem.SetFileMode(path, 0o755);

On Unified panels, the canonical helper is HMIRuntime.FileSystem with methods WriteFile, DeleteFile, and DeleteDirectory. If the number of files to remove is small and known at runtime, the high-level DeleteFile call may replace the bash script entirely — provided the target is a local path the runtime is permitted to touch.

3. Configure the network drive mount

In the Control Panel, open Network and Internet → Network drive and add the share, then mount it. The panel mounts the share under /home/industrial/share (or the path you specified). The mount is created by the industrial user and inherits the ownership rules described in the runtime user model section. Verify from the service desktop (Control Panel → Taskbar → Start → Service Desktop) with:

ls -la /home/industrial/share
mount | grep cifs

The share should appear with uid=industrial,gid=industrial (or gid=industrial on V17 Update 6+) and mode 0770.

4. Wrap the call in a runtime script

Create a JavaScript runtime script that builds the path and invokes StartProgram:

// Scripts -> Runtime scripts
// Trigger: a cyclic tag "DeleteTrigger" (Bool) from the PLC
export function Delete_OnTrigger(tag) {
  if (tag.GetValue() !== true) return;

  var fileName = Tags("DeleteFileName").Read(); // String tag, e.g. "archive/old.csv"
  if (!fileName || fileName.length === 0) {
    HMIRuntime.Trace("Delete: empty filename");
    return;
  }

  var rc = HMIRuntime.Processes.StartProgram(
    "/home/industrial/delete_share_file.sh",
    fileName,
    false,                  // waitForCompletion
    HMIRuntime.Processes.WindowStyle.Hidden
  );

  HMIRuntime.Trace("Delete rc=" + rc + " file=" + fileName);
}
Argument escaping: the StartProgram wrapper concatenates the argument string as-is. If fileName may contain spaces, build it with explicit quoting inside the bash script (the example above already uses -- "$1" to guard against names starting with -).

5. Trigger from the PLC

Two approaches are production-acceptable:

Approach How Notes
Tags (recommended) On the script's Trigger tab, select a single HMI tag and the change condition. The script runs only when the tag value changes. Lowest CPU overhead. The standard Siemens recommendation.
Tags — automatic (V18+) Same as above, but the panel additionally re-evaluates on each acquisition cycle if the value has flipped back. Useful for edge-triggered one-shots without writing the reset logic on the PLC.
Cyclic dynamization (avoid) Bind a screen object's property to a script with a 1 s cycle. Works, but the script fires every cycle regardless of state; performance impact grows with screen count.
Scheduled task Use the panel's scheduler to call the script on a calendar. Runs as scs. Requires V17 Update 6 or V18 Update 2 to write to the share.

The preferred pattern is a single Bool tag DeleteTrigger toggled by the PLC. The PLC sets it true, waits for the panel's trace or a status tag to confirm completion, then resets it to false. This avoids the one-second polling loop that a screen-cyclic dynamization would create.

6. Verify on the panel

  1. Open the Control Panel → Logs and tail /var/log/wmibacktrace.log for the trace line Delete rc=....
  2. Switch to the Service Desktop (password protected) and confirm with ls -la /home/industrial/share/<fileName> that the file is gone.
  3. Inspect the mount with mount | grep cifs; an unmounted share will fail silently inside the script with exit code 3.

Why StartProgram Must Point Inside the Writable Tree

The StartProgram call does not enforce a path prefix — it will execute any binary the calling user can read and that has the execute bit set. The reason custom scripts must live in /home/industrial/ (and not, for example, in /tmp/ or an SD card path) is twofold:

  1. The scs and industrial users have no write access to /opt/siemens/, so the script cannot be created there from the runtime.
  2. The runtime is launched with a SMACK policy that blocks industrial from executing files labeled differently from the home directory tree. Files written to /tmp from one process may not be executable by another.

Built-in Alternatives to a Custom Script

If the deletion targets a small, predictable list, the HMIRuntime.FileSystem object removes the need for a bash wrapper:

try {
  HMIRuntime.FileSystem.DeleteFile("/home/industrial/share/archive/old.csv");
} catch (e) {
  HMIRuntime.Trace("DeleteFile failed: " + e.message);
}

DeleteDirectory recursively removes a folder. Both calls run inside the industrial session and therefore succeed on the share mount without further permission work. Use this path whenever the filename is known at runtime; reserve the StartProgram route for glob patterns, age-based cleanup, or cases where the script also needs to call smbclient / mount.cifs to re-establish a dropped share.

Troubleshooting Matrix

Symptom Likely cause Fix
StartProgram returns a negative code, no file removed Script not executable, or path outside /home/industrial/ Verify with ls -l /home/industrial/delete_share_file.sh and set chmod 755
Script runs in scheduler but not from PLC trigger Scheduled task uses scs, runtime uses industrial; on V17 prior to Update 6, only industrial can write to the share Move trigger to a runtime script, or upgrade to V17 Update 6 / V18 Update 2
rm: Permission denied in script stdout Mount created before login session, or share ACL excludes the Linux group Remount the network drive from the Control Panel; verify the SMB share grants write to the panel's service account
Script fires every cycle, high CPU Cyclic screen dynamization instead of tag trigger Switch the runtime script trigger to Tags or Tags - automatic (V18+)
File visible locally but not from network clients Write succeeded but the SMB server's opportunistic lock has not flushed Add sync to the bash script before exit, or reduce the SMB client's oplock timeout
Mount disappears after reboot Network drive configured but not enabled to auto-mount In the Control Panel, edit the network drive and check Reconnect at login

Firmware-Specific Notes

  • V17 GA → V17 Update 5: only runtime scripts (user industrial) can write to the mounted share. Document this in the project hazard log because operations teams will often assume a scheduled task should work.
  • V17 Update 6: the industrial group gains write access. Scheduled tasks now succeed. No project change required, but the panel must be updated.
  • V18 GA → V18 Update 1: documentation explicitly recommends the new Tags — automatic trigger mode for one-shot PLC-driven events.
  • V18 Update 2: group-based write permission is the documented default and is preserved across reboots and image updates.

Security Considerations

Deleting files from a network share is a destructive action. Apply the following controls on production panels:

  • Restrict the runtime script to a single HMI tag trigger sourced from a known PLC address. Do not expose the delete function to operator buttons without an additional confirmation screen.
  • Use a dedicated SMB account with delete rights limited to a single subfolder; never share credentials with the read-only logging account.
  • Log every invocation through HMIRuntime.Trace and forward traces to the central log server so that an audit trail exists outside the panel.
  • Validate the filename in the PLC against a whitelist on the panel side; the bash script should reject any argument containing .. or a leading / to prevent path traversal out of the share.

FAQ

Can I put my custom bash script in /opt/siemens/App_Restriction/ like copy.sh?

No. /opt/siemens/App_Restriction/ is provisioned read-only by the Siemens image; the runtime industrial user cannot write to it. Place user scripts in /home/industrial/ instead, where ownership defaults to industrial:industrial and the SMACK label allows execution.

Why does my scheduled task fail to delete files even though the script works from a button?

Scheduled tasks run as the scs user. On firmware older than V17 Update 6 the network-share mount grants write permission only to industrial, so scs cannot delete. Upgrade to V17 Update 6 (or V18 Update 2), which grants the industrial group write access and lets scs delete as a group member.

Do I have to use StartProgram, or can I delete the file directly from JavaScript?

On Unified panels, HMIRuntime.FileSystem.DeleteFile and DeleteDirectory work against any path the industrial user can write to, including the mounted network drive. Use these built-ins when the filename is known; use StartProgram only for glob patterns, age-based cleanup, or remount logic.

Which PLC-trigger method is recommended for a one-shot delete?

Use a single Bool HMI tag as the trigger of a runtime script. Configure the trigger as Tags, or in V18 as Tags - automatic, so the script fires on a 0 -> 1 transition only. Avoid cyclic screen dynamizations, which poll every cycle and add CPU load.

How do I confirm the script ran successfully from the field?

Tail /var/log/wmibacktrace.log from the panel's Control Panel Logs view and look for the HMIRuntime.Trace line, or return a status code from the script to a Bool or Int tag the PLC can read. A successful rm -f returns 0; a missing file returns 3 in the example above.

Back to blog