5

I have a macOS server with a little site which converts text snippets to audio using say command.

With upgrade to Sierra, everything went smooth except one thing: the say command doesn't work any more when wrapped in exec() in my PHP script.

The page just times out. No error is caught either.

<?php
    try {
        exec('/usr/bin/say "hello"');
    }
    catch (Exception $e) { echo $e->getMessage(); }
?>

Typically I would save the audio snippets with say -o filename but I tried all the variants and also other shell commands which worked fine including creating files in my output folder.

Interesting is that if I run it from a command line, it works - either says it loud or creates an output file.

macOS Sierra has PHP 5.6.24 so I don't think safe_mode applies, righht?

I would like to emphasise that the change in either PHP or say command was quite recent, with the new OS. Yes I did look into and tried different output and stderr redirection but the script just hangs.

Seeing the say command in Activity Viewer (GUI for top equivalent), I tried to sample it, not sure if it helps:

2695 Thread_1742595   DispatchQueue_1: com.apple.main-thread  (serial)
+ 2695 start  (in libdyld.dylib) + 1  [0x7fffb0f58255]
+   2695 ???  (in say)  load address 0x10907d000 + 0x1fac  [0x10907efac]
+     2695 NewSpeechChannel  (in SpeechSynthesis) + 52  [0x7fff9acd3f19]
+       2695 SpeechChannelHandle::SpeechChannelHandle()  (in SpeechSynthesis) + 265  [0x7fff9acd797f]
+         2695 dispatch_once_f  (in libdispatch.dylib) + 38  [0x7fffb0f220e5]
+           2695 _dispatch_client_callout  (in libdispatch.dylib) + 8  [0x7fffb0f22128]
+             2695 ___ZN13SpeechGlobals8InstanceEv_block_invoke  (in SpeechSynthesis) + 28  [0x7fff9acd54da]
+               2695 SpeechGlobals::SpeechGlobals()  (in SpeechSynthesis) + 471  [0x7fff9acd56db]
+                 2695 xpc_connection_send_message_with_reply_sync  (in libxpc.dylib) + 154  [0x7fffb11b65a8]
+                   2695 dispatch_mach_send_with_result_and_wait_for_reply  (in libdispatch.dylib) + 45  [0x7fffb0f3cf39]
+                     2695 _dispatch_mach_send_and_wait_for_reply  (in libdispatch.dylib) + 591  [0x7fffb0f3cad4]
+                       2695 mach_msg  (in libsystem_kernel.dylib) + 55  [0x7fffb107e867]
+                         2695 mach_msg_trap  (in libsystem_kernel.dylib) + 10  [0x7fffb107f41a]
2695 Thread_1742600
  2695 start_wqthread  (in libsystem_pthread.dylib) + 13  [0x7fffb116f211]
    2695 _pthread_wqthread  (in libsystem_pthread.dylib) + 1426  [0x7fffb116f7b5]
      2695 __workq_kernreturn  (in libsystem_kernel.dylib) + 10  [0x7fffb10874e6]

These are statistics: Activity Viewer > Statistics

And from open files and ports, I could see that I set both stdout and stderr to /private/var/log/apache2/error_log yet nothing shows there at all.

Also, tried to capture outputs with more elaborate run, but no joy, just timeout (the script folder is also writable):

<?php   
    try {
        $pipes = array();
        proc_close(proc_open("say hi", array(0 => array("pipe", "r"), 1 => array("pipe", "r"), 2 => array("pipe", "r")), $pipes, dirname(__FILE__), null));
    } catch (Exception $e) { error_log($e->getMessage()); }
?>

UPDATE: High Sierra is the same.

FINAL UPDATE: after installing Mojave, which removes most of the Server.app features, I added MAMP to handle this task. Hear it for yourselves if you wish - it's at macspeaks.com.

THE STORY CONTINUES: somehow during the install of Catalina, or was it MAMP update? (now on 5.5), I killed it again. Sigh...

Josef Habr
  • 254
  • 2
  • 14
  • Possible duplicate of [PHP StdErr after Exec()](http://stackoverflow.com/questions/2320608/php-stderr-after-exec) – Clay Oct 25 '16 at 13:38
  • Trying running one of the answers from the duplicate question and see if you can capture the stderr output which may give you a useful error message to find out what's really happening. – Clay Oct 25 '16 at 13:39
  • Thanks, I will give it a go. Though, the `say` command doesn't usually have any output - except from the sound or file. – Josef Habr Oct 25 '16 at 20:39
  • Well, unfortunately... I would like to emphasise that the change in either PHP or `say` command was quite recent, with the new OS. Yes I did look into and tried different output and stderr redirection but the script just hangs. – Josef Habr Oct 25 '16 at 20:52
  • Can you run the command as the apache user from the command line? perhaps it is a permission issue. If you can su into the apache (or nxginx user) then run the say command with dtrace /strace maybe it'll give you a hint as to why. – Clay Oct 25 '16 at 21:38
  • My apache runs as me on my development machine. Permissions should be fine. I see the `say` command in the Activity Monitor, it just stays there, forever. I added more info in the question. – Josef Habr Oct 26 '16 at 17:24
  • Tried the `proc_open` approach but it's the same... Timeout. – Josef Habr Oct 26 '16 at 22:43
  • Try disabling System Integrity Protection. – Harikrishnan Oct 28 '16 at 04:14
  • Any hint why SIP should have something to do with `say`, @Harikrishnan? – Josef Habr Oct 30 '16 at 23:36
  • No. Just try. I had lots of issues after enabling SIP. – Harikrishnan Oct 31 '16 at 03:37
  • No, unfortunately, `csrutil disable` didn't help. The same timeout. – Josef Habr Nov 07 '16 at 20:17

2 Answers2

0

I too have experienced this same problem.

Here is a work-around that I just came up with, but honestly I think it's a BIG MESS just to be able to play audio from an apache based php script. I have some ideas on why this might be happening, but upon MANY tests I seem to destroy my own theories. I thought it might have been related to not having an active TTY. I was not able to play audio by launching a shell with sudo -i, and MANY MANY other attmepts from php, but I was able to play audio using all the same commands from the local terminal, and as it turns out, also by SSH'ing into the computer, so that led to my latest work-around. Once again, I think this is WAY overkill, but so far is the only way I've been able to get the audio back in my web based php-driven scripts (mostly geo-fencing related.)

So, here we go, and yes I understand the risks and stupidity involved in this:

In my php script I generate an audio file in the /tmp directory with a command similar to this:

exec "sudo -u <username> /usr/bin/say -o /tmp/outputfile.aiff --voice=Ava \"<What to Say>\"";

Then after the audio file is generated, the only way I've found to play it (and actually hear the output) from apache/php is by using an expect script to ssh in locally and play it. So my next line is:

exec "sudo -u <username> -i ~<username>/expectscript";

My expect script is as follows:

#!/usr/bin/expect -f

spawn /usr/bin/ssh localhost
expect "Password"
send "<PASSWORD>\r"
expect "<username>"
send "/usr/bin/afplay /tmp/outputfile.aiff\r"
expect "<username>"
send "/usr/bin/touch /tmp/touchthis\r"
expect "<username>"
send "exit\r"

Make sure you replace all the <username> above with your username (without the <> obviously,) and <PASSWORD> with your password. You may need to tweak the expect script if your bash prompts don't contain your username, as that is what I used in my expect script to find the returned prompt. The touch is just to determine that the expect script worked, and you have reference of the last time the file was touched.

I hope this opens up the discussion on what is actually causing this and we can determine a more reasonable solution. I went down a lot of rabbit holes trying to find different ways of getting this to work, I created Automator applications and called those from PHP (didn't work.) I launched shells within shells as my user, all the execution of commands always completed successfully, just no audio output. All of the solutions I tried would work fine from the terminal (and even php from the terminal), just not from Apache/PHP.

Symo
  • 11
  • 1
  • 2
  • OK I have to admit I didn't care about playing the file out loud on my server (although the use cases might be interesting, announcing eg visitors to your website), so the first part interests me more - once I would be able to create a file, I could just copy it somewhere with public permissions where a website could access it. So I would be really curious what username you `sudo`'d to - but `sudo` alone wouldn't create a TTY? It is a good idea to try something like that. Worst scenario, ssh login... I've tried `exec('login -f my_web_user say hello');` and that didn't work. – Josef Habr Dec 04 '16 at 13:32
  • BTW this is info from Activity Viewer's Open Files and Ports, seen when inspecting `say` process which gets stuck there for eternity: stdin is `/dev/null`, stdout is `->0x77aec90ed5ed63cb` and stderr is `/private/var/log/apache2/error_log` – Josef Habr Dec 04 '16 at 13:43
  • The stdout changes every time. Also, there's a lot of "faults" in statistic - almost 800, if it means something... – Josef Habr Dec 04 '16 at 13:50
  • Josef, I originally had thie problem with El Capitan, that was solved just by doing the original sudo as my username. When Sierra was released, then I was not able to get the audio to play (which is hooked to my whole-home audio) for announcements etc. If you don't need it to audibly "play" you should be able to create the sounds file with the -o option as long as you sudo to your local user account. – Symo Dec 05 '16 at 18:34
  • @Josef Sorry, hit enter before I finished. You should just be able to use: `exec "sudo -u /usr/bin/say -o /tmp/outputfile.aiff --voice=Ava \"\"";` Let me know your results. -Jon – Symo Dec 05 '16 at 18:37
  • I wish, but it still doesn't work for me. As soon as the exec is run by the Apache, it never finishes. I've just installed a MAMP so I will try if it works there. – Josef Habr Jan 03 '17 at 07:31
  • Oh. So it works in MAMP. I should have tried that earlier. So it is probably not the system but some PHP settings. BTW it was just `exec('say hello')`, nothing special there. – Josef Habr Jan 03 '17 at 07:41
  • No user change, no tweaking privileges. I even tried to run MAMP on port 80 and it worked as well. – Josef Habr Jan 03 '17 at 10:26
0

I have experienced same problem while using say command on my Jenkins job. My Jenkins server is working as daemon on macOS Sierra.

In my case I solved this problem like this:

  1. Login as jenkins user and generate ssh key with ssh-keygen.
  2. Append ~/.ssh/id_rsa.pub to .ssh/authorized-keys so that password will not be requested.

    cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized-keys
    
  3. Change my Jenkins job which use say command like this:

    ssh localhost say -v Alex "Test"
    

I’m sorry, I have not tested with Apache/PHP, but why don't you try this way like this:

<?php
    try {
        exec('/usr/bin/ssh localhost /usr/bin/say "hello"');
    }
    catch (Exception $e) { echo $e->getMessage(); }
?>
kunichiko
  • 113
  • 1
  • 7