Stop the Job That Restarts Itself

LinuxBeginner
Practice Now

Introduction

The Status Desk normally refreshes a small cache every minute. An unfamiliar uploader is also sending synthetic training requests to an internal collector. Stopping it seems to help, but it returns. Your task is to remove its recurring trigger while keeping the normal status refresh alive.

You need only basic terminal use. The Status Server terminal is an authorized administrator shell inside the target container, starting in /srv/status. Status Desk displays the real collector counters. Both containers share the training instance's kernel. All data and requests are harmless; this lab covers cron recurrence, not a complete production-host investigation.

Observe the Returning Program

In this step, you will stop a running uploader and see why stopping one instance does not remove the cause.

Open Status Desk. refreshes counts successful cache-refresh requests; it should grow each minute. uploads counts the unwanted training requests, normally one every five seconds. Historical counts will remain after repair. Reload the page to obtain current values.

Switch to Status Server. A process is a running program. Its PID identifies this particular instance; the executable path and arguments describe what it does. ps lists processes. -e selects all of them, while -o chooses the columns: PID, parent PID (PPID), and full command with arguments.

ps -eo pid,ppid,args

Find /usr/bin/python3 /opt/status/uploader.py upload. Python is the interpreter, /opt/status/uploader.py is the program file, and upload selects its training operation. The nearby flock process holds a lock so cron cannot create multiple concurrent copies. Leave the cache-refresh program alone.

A signal is a request to a process. TERM asks it to terminate normally. The following pkill selects a full command line with -f; the quoted pattern's ^ and $ anchor the beginning and end, so it targets this Python worker rather than its parent or your shell. Run it once:

pkill -TERM -f '^/usr/bin/python3 /opt/status/uploader.py upload$'

No output is normal. If there was no match, list processes again rather than broadening the pattern to every Python process. Allow one second for termination, then list them:

sleep 1
ps -eo pid,ppid,args

The Python uploader should be absent. The checkpoint checks that the original instance ended; it does not claim the host is clean.

sleep pauses for a number of seconds. Wait 80 seconds, long enough to cross at least one genuine minute boundary, then list processes again:

sleep 80
ps -eo pid,ppid,args

The same path and upload argument should return with a new PID. Its parent chain now includes CRON, the scheduler's job launcher. A new PID means a new execution, not a different underlying job. If a busy environment delays the tick, wait another 20 seconds and list again. Reload Status Desk: uploads resume, and legitimate refreshes continue. We must find what launches the program.

Find and Disable the Recurring Trigger

In this step, you will relate the returning process to a specific schedule and disable that entry without deleting legitimate jobs.

Cron is a daemon: a service that runs in the background and starts scheduled commands. A user crontab holds one account's jobs. This terminal is root inside the training container, so crontab -l lists that target account's schedule. It does not affect the platform account.

crontab -l

You should see:

* * * * * /usr/bin/flock -n /run/uploader.lock /usr/bin/python3 /opt/status/uploader.py upload

The first five fields are minute, hour, day of month, month, and day of week. * means every value, so * * * * * runs every minute. The remaining text is the command. flock -n takes a nonblocking lock: if another uploader already owns it, the duplicate invocation exits. The target matches the process you stopped.

System schedules also live in /etc/cron.d/. ls lists the files; cat prints a short file. The * after the directory expands to its files.

ls /etc/cron.d
cat /etc/cron.d/*

The status-cache file contains:

* * * * * root /usr/bin/python3 /opt/status/cache-refresh.py refresh

Unlike a user crontab, a system cron file includes an account field, here root, between the five time fields and command. This permitted command updates the Status Desk. Similar timing alone does not make a job unauthorized. Other package-provided maintenance entries can remain.

To edit safely, first copy the current user schedule into a working text file. > writes the listing to that file, replacing an older copy if present. Do this only once before editing; repeating it would overwrite your draft.

crontab -l > /srv/status/root.cron

nano opens a text editor. Put # at the beginning of the uploader line. Cron ignores comment lines beginning with #. Keep the line's text for recovery, and leave other entries unchanged.

nano /srv/status/root.cron

Use arrow keys to reach the start of the line, type #, press Ctrl+O, Enter to save, then Ctrl+X to exit. Install the edited file with crontab followed by its path:

crontab /srv/status/root.cron
crontab -l

The uploader line should begin with #. Cron reloads schedule changes automatically; no restart is needed. Commenting the working copy alone is insufficient until you install it. If you edited the wrong line, reopen the draft, restore it, and install again. The current uploader can still be running because removing a schedule does not terminate an already running process.

Remove the Running Copy and Verify Recovery

In this step, you will finish the cleanup and compare unwanted activity with the business function over a full scheduling interval.

Stop the current Python uploader using the same exact pattern:

pkill -TERM -f '^/usr/bin/python3 /opt/status/uploader.py upload$'

If it is already absent, a no-match exit is expected and you can continue. Wait one second for the worker to exit:

sleep 1

curl makes a network request. -sS hides the progress meter but shows errors. This URL names the internal collector container; it is reachable from Status Server. /state returns current counts and the Unix timestamps of the latest requests.

curl -sS http://collector:8080/state

Note the uploads, refreshes, and last_refresh values. Then let real cron run:

sleep 80
curl -sS http://collector:8080/state

uploads must stay unchanged, while refreshes and last_refresh increase. Reload Status Desk to see the same facts. Old uploads remain as evidence; clearing the counter would hide history, not fix recurrence.

List processes once more:

ps -eo pid,ppid,args

cron -f remains present; -f keeps this container's daemon in the foreground. The uploader should stay absent. The checkpoint independently observes another 80-second interval, so allow about 90 seconds for grading. It only reads configuration and live state.

If uploads increase, revisit both cron locations and check that you installed the commented user crontab. If refreshes stop, restore the permitted status-cache entry. Never stop cron or remove all schedules to hide the symptom: the status service needs them. This repair removes the demonstrated cron mechanism; it is not proof that every possible persistence mechanism on a real host is absent.

Summary

You distinguished a process instance from its launcher, followed a parent chain to cron, inspected user and system schedules, disabled one unwanted trigger, and confirmed that real business refreshes continued through an upload-free minute.