# RouterOS 7.23.7 target. Import ONLY after reviewing and enrolling a lab device. # Replace token privately; the script source is a credential. Do not export/share it. /system script add name=pcsrt-poll policy=read,write,test,reboot dont-require-permissions=no source={ :local pcsrtBase "https://mikrotik.pcsrt.com" :local pcsrtToken "REPLACE_WITH_DEVICE_TOKEN" :local pcsrtLte "lte1" # Must match the server enrollment. Empty means VPN enable is forbidden. :local pcsrtVpn "" :if ([/system script job print count-only as-value where script=[:jobname]] > 1) do={ :return "" } :global pcsrtPendingId :global pcsrtPendingResult :local pcsrtPost do={ :local pcsrtReply [/tool fetch url=($pBase.$pPath) http-method=post http-header-field=("Authorization: Bearer ".$pToken.",Content-Type: application/json") http-data=[:serialize to=json value=$pBody options=json.no-string-conversion] check-certificate=yes http-max-redirect-count=0 idle-timeout=10s duration=20s output=user as-value] :return [:deserialize from=json value=($pcsrtReply->"data") options=json.no-string-conversion] } :do { # Result retries never repeat the command. If RAM is lost the server shows uncertainty. :if ([:len $pcsrtPendingId] > 0) do={ :do { :local pcsrtSent [$pcsrtPost pBase=$pcsrtBase pToken=$pcsrtToken pPath=("/api/device/jobs/".$pcsrtPendingId."/result") pBody=$pcsrtPendingResult] :set pcsrtPendingId "" :set pcsrtPendingResult } on-error={ :log warning "PCSRT: result delivery pending" } } :local pcsrtMetrics [:toarray ""] :set ($pcsrtMetrics->"uptime_seconds") ([:tonsec [/system resource get uptime]] / 1000000000) :set ($pcsrtMetrics->"cpu_percent") [/system resource get cpu-load] :set ($pcsrtMetrics->"memory_free") [/system resource get free-memory] :set ($pcsrtMetrics->"memory_total") [/system resource get total-memory] :set ($pcsrtMetrics->"version") [/system resource get version] :local pcsrtSchedule [/system scheduler find where name="pcsrt-poll"] :local pcsrtInterval 60 :if ([:len $pcsrtSchedule] = 1) do={ :set pcsrtInterval ([:tonsec [/system scheduler get $pcsrtSchedule interval]] / 1000000000) } :set ($pcsrtMetrics->"interval") $pcsrtInterval :set ($pcsrtMetrics->"rx_bytes") 0 :set ($pcsrtMetrics->"tx_bytes") 0 :set ($pcsrtMetrics->"operator") "" :set ($pcsrtMetrics->"connection") "unavailable" :local pcsrtLteId [/interface lte find where name=$pcsrtLte] :if ([:len $pcsrtLteId] = 1) do={ :set ($pcsrtMetrics->"rx_bytes") [/interface get $pcsrtLteId rx-byte] :set ($pcsrtMetrics->"tx_bytes") [/interface get $pcsrtLteId tx-byte] :do { :local pcsrtRadio [/interface lte monitor [:pick $pcsrtLteId 0] once as-value] :set ($pcsrtMetrics->"operator") [:tostr ($pcsrtRadio->"current-operator")] :set ($pcsrtMetrics->"connection") [:tostr ($pcsrtRadio->"status")] :foreach pcsrtField in={"rssi";"rsrp";"rsrq";"sinr"} do={ :set ($pcsrtMetrics->$pcsrtField) [:tostr ($pcsrtRadio->$pcsrtField)] } } on-error={ :set ($pcsrtMetrics->"connection") "monitor-error" } } :local pcsrtResponse [$pcsrtPost pBase=$pcsrtBase pToken=$pcsrtToken pPath="/api/device/checkin" pBody=$pcsrtMetrics] :if ([:len $pcsrtPendingId] > 0) do={ :return "" } :local pcsrtJob ($pcsrtResponse->"job") :if ([:typeof $pcsrtJob] != "array") do={ :return "" } :local pcsrtJobId ($pcsrtJob->"id") :local pcsrtKind ($pcsrtJob->"kind") :local pcsrtArgs ($pcsrtJob->"payload") :local pcsrtNow ([:tonsec [:timestamp]] / 1000000000) :if ($pcsrtNow >= ($pcsrtJob->"expires")) do={ :return "" } :local pcsrtPermit [$pcsrtPost pBase=$pcsrtBase pToken=$pcsrtToken pPath=("/api/device/jobs/".$pcsrtJobId."/claim") pBody=[:toarray ""]] :if (($pcsrtPermit->"execute") != true) do={ :return "" } # Only this unique, successfully received permit allows execution. No automatic claim retry. :set pcsrtPendingId $pcsrtJobId :set pcsrtPendingResult {"status"="failed";"message"="Command failed; inspect device locally"} :local pcsrtDoReboot false :do { # Do not execute a permit held beyond expiration or after a stalled fetch. :if (([:tonsec [:timestamp]] / 1000000000) >= ($pcsrtJob->"expires")) do={ :error "expired" } :local pcsrtKnown false :if ($pcsrtKind = "diagnostics") do={ :set pcsrtKnown true :set ($pcsrtPendingResult->"diagnostics") $pcsrtMetrics } :if ($pcsrtKind = "set_interval") do={ :set pcsrtKnown true :local pcsrtSeconds ($pcsrtArgs->"seconds") :if (([:typeof $pcsrtSeconds] != "num") || ($pcsrtSeconds < 30) || ($pcsrtSeconds > 3600) || ([:len $pcsrtSchedule] != 1)) do={ :error "interval" } /system scheduler set $pcsrtSchedule interval=[:totime ($pcsrtSeconds."s")] } :if ($pcsrtKind = "configure") do={ :set pcsrtKnown true :local pcsrtIdentity ($pcsrtArgs->"identity") :local pcsrtComment ($pcsrtArgs->"lte_comment") # Values are passed as data, never parsed as RouterOS code. :if ([:typeof $pcsrtComment] != "nil") do={ :if (([:len $pcsrtComment] > 128) || ([:len $pcsrtLteId] != 1)) do={ :error "comment" } } :if ([:typeof $pcsrtIdentity] != "nil") do={ :if (([:len $pcsrtIdentity] < 1) || ([:len $pcsrtIdentity] > 64)) do={ :error "identity" } /system identity set name=$pcsrtIdentity } :if ([:typeof $pcsrtComment] != "nil") do={ /interface set $pcsrtLteId comment=$pcsrtComment } } :if ($pcsrtKind = "enable_support_vpn") do={ :set pcsrtKnown true :if (([:len $pcsrtVpn] = 0) || (($pcsrtArgs->"interface") != $pcsrtVpn)) do={ :error "VPN not approved" } :local pcsrtVpnId [/interface find where name=$pcsrtVpn] :if ([:len $pcsrtVpnId] != 1) do={ :error "VPN missing" } :local pcsrtVpnType [/interface get $pcsrtVpnId type] :if (($pcsrtVpnType != "wg") && ($pcsrtVpnType != "wireguard") && ($pcsrtVpnType != "l2tp-out") && ($pcsrtVpnType != "sstp-out") && ($pcsrtVpnType != "ovpn-out")) do={ :error "VPN type not allowed" } /interface enable $pcsrtVpnId } :if ($pcsrtKind = "reboot") do={ :set pcsrtKnown true; :set pcsrtDoReboot true } :if (!$pcsrtKnown) do={ :error "unsupported command" } :set ($pcsrtPendingResult->"status") "succeeded" :set ($pcsrtPendingResult->"message") "Command applied" :if ($pcsrtDoReboot) do={ :set ($pcsrtPendingResult->"status") "reboot_requested" :set ($pcsrtPendingResult->"message") "Reboot requested; completion must be checked from subsequent telemetry" } } on-error={ :set pcsrtDoReboot false } :do { :local pcsrtSent [$pcsrtPost pBase=$pcsrtBase pToken=$pcsrtToken pPath=("/api/device/jobs/".$pcsrtPendingId."/result") pBody=$pcsrtPendingResult] :set pcsrtPendingId "" :set pcsrtPendingResult } on-error={ :log warning "PCSRT: result delivery pending" } :if ($pcsrtDoReboot) do={ /system reboot } } on-error={ # Never log fetch errors: they may include headers or script source. :log warning "PCSRT: poll failed; check connectivity, certificate, clock and enrollment" } }