Score:1

Shell script works manually but doesn't work correctly in Crontab

md flag

I have a Python script to record audio/video using the "subprocess" module to execute ffmpeg commands, as follows:

def kill_ffmpeg_process(args):
    for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
        if proc.info['name'] == 'ffmpeg' and args in proc.info['cmdline']:
            print("found: " + str(proc))
            proc.kill()

kill_ffmpeg_process("ffmpeg")

rand_num = str(random.randint(0,99))
record_internal_cmd = f"ffmpeg -f pulse -ac 2 -i 'alsa_input.pci-0000_00_05.0.analog-stereo' -f v4l2 -i /dev/video4 -vcodec libx264 -crf 28 Desktop/recordings/{get_today_timestamp()}_{str(rand_num)}_internal_feed.mkv"
record_external_cmd = f"ffmpeg -f v4l2 -i /dev/video5 Desktop/recordings/{get_today_timestamp()}_{str(rand_num)}_external_feed.mkv"

print("Recording /dev/video4 with audio")
subprocess.Popen(record_internal_cmd,shell=True,stderr=subprocess.PIPE,stdout=subprocess.PIPE)
time.sleep(1)
print("Recording /dev/video5")
subprocess.Popen(record_external_cmd,shell=True,stderr=subprocess.PIPE,stdout=subprocess.PIPE)
time.sleep(3)

I want to schedule this script to be executed every 1 hour (automatically) with crontab, so I created a .sh file that calls the Python script #!/bin/bash python3 script.py. when executing it manually in terminal ./shell_script.sh it works fine, but when adding it to a crontab it doesn't work because it cannot find the audio source "personal thoughts" because when I remove audio recording from it, it works. But pulse audio recording doesn't work; it only executes the second command and creates the file that includes video recording.

Elias Tommeh avatar
md flag
I don't think the problem is with paths, because it's working as expected, except for the audio record part that can be found in "record_internal_cmd" it ignores it and executes "record_external_cmd"
vanadium avatar
cn flag
Still, `ffmpeg` may not be found in a cronjob because conjobs have a very limited environment, so even PATH may not be set.
Score:1
jp flag

Why is this happening?

Your script obviously needs Pulse Audio running ... Pulse audio is run(automatically) as a user service ... You can see the service's status with:

$ systemctl --user status pulseaudio
● pulseaudio.service - Sound Service
     Loaded: loaded (/usr/lib/systemd/user/pulseaudio.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2023-04-25 14:26:50 +03; 13min ago
TriggeredBy: ● pulseaudio.socket
   Main PID: 120691 (pulseaudio)
      Tasks: 4 (limit: 23594)
     Memory: 7.3M
        CPU: 389ms
     CGroup: /user.slice/user-1000.slice/[email protected]/session.slice/pulseaudio.service
             └─120691 /usr/bin/pulseaudio --daemonize=no --log-target=journal

Apr 25 14:26:50 Lenovo systemd[5211]: Starting Sound Service...
Apr 25 14:26:50 Lenovo systemd[5211]: Started Sound Service.

The service unit itself can be inspected with:

$ cat /usr/lib/systemd/user/pulseaudio.service
[Unit]
Description=Sound Service

# We require pulseaudio.socket to be active before starting the daemon, because
# while it is possible to use the service without the socket, it is not clear
# why it would be desirable.
#
# A user installing pulseaudio and doing `systemctl --user start pulseaudio`
# will not get the socket started, which might be confusing and problematic if
# the server is to be restarted later on, as the client autospawn feature
# might kick in. Also, a start of the socket unit will fail, adding to the
# confusion.
#
# After=pulseaudio.socket is not needed, as it is already implicit in the
# socket-service relationship, see systemd.socket(5).
Requires=pulseaudio.socket
ConditionUser=!root

[Service]
ExecStart=/usr/bin/pulseaudio --daemonize=no --log-target=journal
LockPersonality=yes
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
Restart=on-failure
RestrictNamespaces=yes
SystemCallArchitectures=native
SystemCallFilter=@system-service
# Note that notify will only work if --daemonize=no
Type=notify
UMask=0077
Slice=session.slice

[Install]
Also=pulseaudio.socket
WantedBy=default.target

To see three important settings:

  1. WantedBy=default.target which means the service won't necessarily be available until just before default.target(the last multi-user enabled interface in the boot process).

  2. Requires=pulseaudio.socket which is only available after a user session has been started (i.e. after login)

  3. ConditionUser=!root which means the user cannot be root.

Both the above 1 and 2 conditions work against the availability of a running Pulse Audio daemon for a crontab job during boot and before login.

Condition 3, however, will not allow running a Pulse Audio daemon under root even after login.

What to do about it?

  • You can add your cron job in your user's crontab(Not root's crontab) and ensure your user is logged in when the cron job runs.

  • You can manually run a Pulse Audio daemon in your script running your script as a certain user that you explicitly specify and in this case, even root can be used ... compare for example:

    $ sudo su root -c 'pacmd list-sources'
    No PulseAudio daemon running, or not running as session daemon.
    

    Versus:

    $ sudo su root -c 'pulseaudio --start; pacmd list-sources'
    W: [pulseaudio] main.c: This program is not intended to be run as root (unless --system is specified).
    2 source(s) available.
         index: 0
         name: <alsa_output.pci-0000_00_1f.3.analog-stereo.monitor>
         driver: <module-alsa-card.c>
         flags: DECIBEL_VOLUME LATENCY DYNAMIC_LATENCY
         state: IDLE
         suspend cause: (none)
         priority: 1030
         volume: front-left: 65536 / 100% / 0.00 dB,   front-right: 65536 / 100% / 0.00 dB
                 balance 0.00
         base volume: 65536 / 100% / 0.00 dB
         volume steps: 65537
         muted: no
         current latency: 0.00 ms
         max rewind: 344 KiB
         sample spec: s16le 2ch 44100Hz
         channel map: front-left,front-right
                      Stereo
         used by: 0
         linked by: 0
         configured latency: 2000.00 ms; range is 0.50 .. 2000.00 ms
         monitor_of: 0
         card: 0 <alsa_card.pci-0000_00_1f.3>
         module: 7
         properties:
             device.description = "Monitor of Built-in Audio Analogue Stereo"
             device.class = "monitor"
             alsa.card = "0"
             alsa.card_name = "HDA Intel PCH"
             alsa.long_card_name = "HDA Intel PCH at 0xa12a8000 irq 136"
             alsa.driver_name = "snd_hda_intel"
             device.bus_path = "pci-0000:00:1f.3"
             sysfs.path = "/devices/pci0000:00/0000:00:1f.3/sound/card0"
             device.bus = "pci"
             device.vendor.id = "8086"
             device.vendor.name = "Intel Corporation"
             device.product.id = "9d71"
             device.product.name = "Sunrise Point-LP HD Audio"
             device.form_factor = "internal"
             device.string = "0"
             module-udev-detect.discovered = "1"
             device.icon_name = "audio-card-pci"
       * index: 1
         name: <alsa_input.pci-0000_00_1f.3.analog-stereo>
         driver: <module-alsa-card.c>
         flags: HARDWARE HW_MUTE_CTRL HW_VOLUME_CTRL DECIBEL_VOLUME LATENCY DYNAMIC_LATENCY
         state: IDLE
         suspend cause: (none)
         priority: 9039
         volume: front-left: 3381 /   5% / -77.25 dB,   front-right: 3381 /   5% / -77.25 dB
                 balance 0.00
         base volume: 6554 /  10% / -60.00 dB
         volume steps: 65537
         muted: yes
         current latency: 353.71 ms
         max rewind: 0 KiB
         sample spec: s16le 2ch 44100Hz
         channel map: front-left,front-right
                      Stereo
         used by: 0
         linked by: 0
         configured latency: 2000.00 ms; range is 0.50 .. 2000.00 ms
         card: 0 <alsa_card.pci-0000_00_1f.3>
         module: 7
         properties:
             alsa.resolution_bits = "16"
             device.api = "alsa"
             device.class = "sound"
             alsa.class = "generic"
             alsa.subclass = "generic-mix"
             alsa.name = "ALC236 Analog"
             alsa.id = "ALC236 Analog"
             alsa.subdevice = "0"
             alsa.subdevice_name = "subdevice #0"
             alsa.device = "0"
             alsa.card = "0"
             alsa.card_name = "HDA Intel PCH"
             alsa.long_card_name = "HDA Intel PCH at 0xa12a8000 irq 136"
             alsa.driver_name = "snd_hda_intel"
             device.bus_path = "pci-0000:00:1f.3"
             sysfs.path = "/devices/pci0000:00/0000:00:1f.3/sound/card0"
             device.bus = "pci"
             device.vendor.id = "8086"
             device.vendor.name = "Intel Corporation"
             device.product.id = "9d71"
             device.product.name = "Sunrise Point-LP HD Audio"
             device.form_factor = "internal"
             device.string = "front:0"
             device.buffering.buffer_size = "352800"
             device.buffering.fragment_size = "176400"
             device.access_mode = "mmap+timer"
             device.profile.name = "analog-stereo"
             device.profile.description = "Analogue Stereo"
             device.description = "Built-in Audio Analogue Stereo"
             module-udev-detect.discovered = "1"
             device.icon_name = "audio-card-pci"
         ports:
             analog-input-internal-mic: Internal Microphone (priority 8900, latency offset 0 usec, available: unknown)
                 properties:
                     device.icon_name = "audio-input-microphone"
             analog-input-mic: Microphone (priority 8700, latency offset 0 usec, available: no)
                 properties:
                     device.icon_name = "audio-input-microphone"
         active port: <analog-input-internal-mic>
    
  • You can run Pulse Audio as system-wide daemon.

Elias Tommeh avatar
md flag
Thank you for all that info, makes much more sense right now. actually, it was solved by adding a few more lines to my .sh script. based on: [ffmpeg returns "no such process" error when called by cron](https://stackoverflow.com/questions/68069727/ffmpeg-returns-no-such-process-error-when-called-by-cron)
Raffa avatar
jp flag
@EliasTommeh You’re welcome … Glad that solved it … However, you might need to pay attention that [PULSE_RUNTIME_PATH](https://pulseaudio-discuss.freedesktop.narkive.com/LE8XtC9t/patch-don-t-let-user-set-pulse-runtime-path-values-affect-behaviour) might be deprecated soon … In case it stops working in the future.
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.