简体   繁体   中英

Why won't my PHP cron script play sound?

I'm working on a small application to ring school bells on a schedule, which can be updated from a website. Everything is working great, except the script that is scheduled as a cron job won't play the sound when the script is run. I have added output piping and echo commands to the script to verify that cron is running it, but the part that plays the sound doesn't work. The script works as expected when run manually from CLI.

The script extracts a time and a sound file for each period of the day on the schedule, then compares the time associated to the sound file with the current time - if it's a match, it will

exec("/usr/bin/aplay /var/www/site/".$soundfile);

Cron is then scheduled to run this script every minute during the school day:

* 8-16 * 1-6,9-12 1-5 root /usr/bin/php -f /var/www/site/scripts/playsound.php > /dev/null

Again, if I manually run the script when there is sound scheduled, the sound plays through the attached speakers. When I have test code that will echo to the screen or output to a file entered as well, cron will dump the output to the files, confirming it is running the script as scheduled. It just won't play the darn sound part of the script.

I've checked all my permissions and since everything else works, they seem to be accurate. I can even write a simple BASH script to get Cron to play a sound on a schedule, so it seems the system has the right group memberships to access both script and sound file. I have switched out exec() for shell_exec() , tried using just the commands as well as the absolute paths to the commands, and the cron job is scheduled to run as root. Still can't figure out why this one small feature that is unfortunately so critical to this program succeeding won't work.

Any advice is greatly appreciated.

Probably permissions on /dev/snd/pcmC0D0p are the problem. (That's the device for ALSA Card 0: Device 0: playback.) If there is a desktop session running on the server, it may have a pulseaudio daemon holding the device open.

Setting things up for multiple users to be able to play sound at the same time is a mess, and requires configuring pulseaudio in a low-performance mode that isn't zero-copy, so don't do that. Just make sure the server can exclusively open the sound device when needed.

To see whether an ALSA sound device is open or not (regardless of paused state or w/e):

$ cat /proc/asound/card0/pcm0p/sub0/hw_params 
access: MMAP_INTERLEAVED
format: S16_LE
subformat: STD
channels: 2
rate: 44100 (44100/1)
period_size: 8192
buffer_size: 16384

$ cat /proc/asound/card0/pcm1p/sub0/hw_params 
closed

So the first playback PCM on my first sound card is open, but the 2nd PCM (the S/PDIF output of my motherboard audio) is closed. Further opens of the sound device only work because the default ALSA setup makes the "default" device a pulseaudio wrapper. hw:0 would fail, because it's busy, and this sound card doesn't have a hardware mixer.

(Fun fact: some old soundcards, eg some PCI Soundblaster cards, supported multiple opens of the hardware device. Multiple streams of PCM data could be DMAed to the card, where they would be mixed by its DSP. Unless I'm totally wrong and the kernel driver was mixing >.< But anyway, you didn't need pulseaudio if you had one.)

Instead of setting as cron, use php interval or javascript (asynchronous - ajax) to call the intended php file. I think this will resolve your issue.

The cron job executing in server, it will not throw any output to your browser.

Cron Job is good to send notifications/mails, update/insert into db in particular intervals.

For your requirements, you have to run the script through browser.

As others have mentioned in the comments you really must save the logs and then examine them for clues.

From what I recall from my experiences using a dedicated script which is executed by PHP can be beneficial. You would feed the bash script the name of the sound file as a parameter. For example:

exec("/path/to/script.sh $SOUNDFILEPATH");

In the script you can setup your working directory, environment variables, etc.

Of course, you must escape variables if you want to avoid the students getting mischievous ideas.

One technique which I've always fallen back on when the going gets tough is to have one script running under cron which is controlled by a file stored in say /tmp/ which you can write to via the web server and PHP.

So just get PHP to write control data to a file in /tmp (or any other writable directory) and get your tested user bash script to monitor it regularly via cron. It could be as simple as having one line that points to the sound file in your case.

That's always worked for me with no surprises.

As I understand it, cron | crontab cron | crontab runs in it's own environment; eg, see accepted answer in:

https://serverfault.com/questions/337631/crontab-execution-doesnt-have-the-same-environment-variables-as-executing-user

Thus, even though aplay will play a sound ( WAV ) file when invoked as a regular user or as root ( su | sudo ), it won't play that file when it is called from cron -- eg, executing a bash (.sh) script that contains your aplay statement).

This works, on my Arch Linux system.

Here is my cron_notification.sh script:

#!/usr/bin/bash

# /mnt/Vancouver/Programming/scripts/cron_notification.sh

# For use with crontab [ sudo gedit /etc/crontab ]
# cron (Arch Linux: cronie) requires full paths:
#   /usr/bin/aplay
#   /usr/bin/notify-send

for i in 1 2 3 4 5
  do
    #aplay alarm.mp3    ## << aplay cannot play MP3 files; use WAV
    # ----------------------------------------
    # NEEDED TO RUN 'aplay' FROM crontab:
    # https://unix.stackexchange.com/questions/231941/cant-run-aplay-as-root
    # https://www.reddit.com/r/linuxquestions/comments/37vcbo/playing_audio_from_a_cronjob/
    # PulseAudio needs XDG_RUNTIME_DIR, so:

    XDG_RUNTIME_DIR=/run/user/`id -u` /usr/bin/aplay /mnt/Vancouver/Programming/scripts/PHASER.WAV
    # ----------------------------------------
    sleep 0.25
done

# ----------------------------------------------------------------------------
# "Critical" alerts persist until clicked (i.e., do not appear, then fade after ~20"):
# notify-send -u critical 'Hello Victoria!' 'Countdown has ended!' --icon=dialog-information

/usr/bin/notify-send -u critical 'Hello Victoria!' "It's 3 pm!" -i /mnt/Vancouver/Programming/scripts/alert.jpg

And, here is the relevant portion of my /etc/crontab file:

# /etc/crontab

# system-wide crontab
# edit: sudo {your favorite text editor: gedit; geany; ...} /etc/crontab
# https://crontab.guru      ## online crontab values checker, planner

# =====================================================================
# SHELL
# =====================================================================

# cron (crontab) requires full paths:
SHELL=/usr/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# =====================================================================
# ARCH LINUX-RELATED [Arch Linux x86_64]
# =====================================================================

# cron | https://wiki.archlinux.org/index.php/Cron
# Will install, use "cronie" cron (crontab):
# Install cronie:  sudo pacman -Syu cronie
#    Enable cron:  sudo systemctl enable --now cronie.service
#     Start cron:  sudo systemctl start cronie.service
#   Restart cron:  sudo systemctl restart cronie.service

# =====================================================================
# APLAY NOTIFICATION (3:00pm M-F)
# =====================================================================

# Two issues when running
#   /mnt/Vancouver/Programming/scripts/cron_notification.sh
# from cron:

# ----------------------------------------
# 1. "notify-send":

# https://bbs.archlinux.org/viewtopic.php?id=216912
# notify-send also needs access to your DBUS_SESSION_BUS_ADDRESS. Assuming that your UID is 1000:

DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/1000/bus"

# More here: https://wiki.archlinux.org/index.php/Desktop_notifications#Usage_in_programming

# ----------------------------------------
# 2. "aplay":

# TO RUN 'aplay' FROM crontab:
# https://www.reddit.com/r/linuxquestions/comments/37vcbo/playing_audio_from_a_cronjob/
# PulseAudio needs XDG_RUNTIME_DIR, so:

# XDG_RUNTIME_DIR=/run/user/`id -u` /usr/bin/aplay /mnt/Vancouver/Programming/scripts/PHASER.WAV

# Line above added to "/mnt/Vancouver/Programming/scripts/cron_notification.sh" script.

# ----------------------------------------

# m    h    dom    mon    dow    user    nice    command

# "At 15:00 on every day-of-week from Monday through Friday”
# [https://crontab.guru/#0_15_*_*_1-5]:
0    15    *    *    1-5    victoria    nice -n 19    /usr/bin/bash /mnt/Vancouver/Programming/scripts/cron_notification.sh

# NOTE -- running as user ("victoria"), not "root".

# Test - every minute:
#*    *    *    *    1-5    victoria    nice -n 19    /usr/bin/bash /mnt/Vancouver/Programming/scripts/cron_notification.sh

I left the comments in place, for references and clarity.

alert.jpg

通知

You can download the PHASER.WAV sound file from my website, here:

http://persagen.com/files/PHASER.WAV

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM