Asterisk / PHP-AGI / Pthreads

Home » Asterisk Users » Asterisk / PHP-AGI / Pthreads
Asterisk Users 5 Comments

Hi there,

does anyone have experience with Asterisk-AGI-Scripts in PHP while using pthreads in PHP? Are there any limitations or problems known?

Best regards
-Thorsten-

5 thoughts on - Asterisk / PHP-AGI / Pthreads

  • Would you mind sharing a sample of your pthread-ed C AGI?
    This will help someone like me who has written AGI in Perl/PHP and now exploring C AGI.

    Thanks,
    –Satish

  • The source code for this particular AGI is about 600 lines and uses my own AGI library (written before other C AGI libraries were available) so I
    don’t think it has a whole lot of value to other programmers.

    First a little background on why I used pthreads in this AGI…

    The application was an adult chat platform. (Mostly) guys call in to chat to (mostly) women and pay $1 to $4 per minute. At the end of the call, the accumulated charges are billed to their their credit card.

    Charges to credit cards usually take 2 transactions. The first transaction
    (‘OPEN’) places a temporary ‘hold’ on the card for $XXX. At the end of the call, a second transaction (‘SALE’) finalizes the billing for the actual charges for the call.

    (This ‘2 step’ process is why you may have trouble charging an expensive dinner after checking into a hotel — the hotel may have placed a ‘hold’
    against your credit limit for the expected charge for your entire stay.)

    As originally implemented, Asterisk would play a prompt asking the caller to enter their card details. After the caller entered a ‘valid’ card number (that passes Luhn mod 10) and expiration date (between the current month & current year and current year + 7). My AGI would then play a prompt (‘Please wait while your card is being verified’) and then transmit the card details to our card processor. Getting a response from the card processor takes a second or so. My AGI returned the card processor response code as the ‘priority’ so the dialplan could continue to the main menu or play a prompt indicating why the card details failed. After 3
    failures, a caller was played a prompt instructing them on alternative billing methods (high rate NPAs, check by phone, etc).

    This client was extremely particular about the ‘caller experience’ and thought the ‘second or so’ of silence while the card was being verified was excessive and had to be eliminated.

    Since the card processing time was not under my control, my solution was to change my AGI to create a second thread to play the ‘please wait’
    prompt while the ‘main line’ code shipped off the card details to the processor.

    When I got the processor’s response (which was usually before the prompt finished playing), the ‘main line’ code would ‘join’ the prompt thread. If the prompt thread was finished playing the prompt, execution continued immediately. If the prompt thread was still playing the prompt, execution continued as soon as the prompt thread received the AGI response from Asterisk. None of this took any special programming or synchronizing from me. It all ‘just worked’ because of the way pthreads are implemented.

    So, what did I learn that would be of value to you? Really understand the AGI protocol and you won’t make a bunch of silly mistakes.

    The AGI protocol is very simple:

    1) Read the AGI environment from stdin before you do anything in your AGI
    that interacts with Asterisk.

    2) Write your AGI request to stdout.

    3) Read Asterisk’s AGI response from stdin.

    4) Rinse, lather, repeat (steps 2 & 3).

    If you use an established AGI library (like you really, really should), this should be taken care of automagically, but understanding what’s really going on will help when things don’t work as expected.

    The number 1 mistake new AGI programmers make is not reading the AGI
    environment. Sometimes they ‘get away’ with it, some times they don’t.

    The number 2 mistake is not reading the responses (step 3). Again, sometimes it works, sometimes it doesn’t

    The number 3 mistake new (and old salts like me still make on occasion) is doing any I/O on stdin or stdout. A lot of bad debugging centers around
    ‘throw in a printf somewhere to see what’s going on’ instead of using
    ‘real programmer’ tools like gdb*.

    If you’re using an established AGI library, the crucial relationship between steps 2 and 3 will be take care of — unless you’re using multiple threads.

    If thread 1 issues an AGI request (writing to stdout) and then thread 2
    issues an AGI request (also writing to stdout) before thread 1 reads it’s response, you’ve broken the protocol and bad things will happen. Maybe not immediately, but certainly when you’re demonstrating the system to your boss.

    So my first suggestion (after using an established library and without knowing what your use case is) would be to ‘designate’ a single thread as
    ‘the Asterisk’ thread and only interact (via stdin and stdout) with Asterisk in that thread. If that’s not feasible, you’re going to need some sort of ‘semaphore’ mechanism so you don’t ‘intermingle’ your AGI requests and responses and thereby violate the AGI protocol.

    Another couple of suggestions unrelated to threads, but are best (IMNSHO)
    practices for AGIs in general:

    1) Use getopt_long(). Most (everybody but me?) seems to like vague, conflicting, and impossible to document or remember AGI command line options. Seriously, which would you rather see in “the other guy’s”
    dialplan 5 years after he’s gone:

    exten = *,n,agi(foo|5555551212|10255|d)

    or

    exten = *,n,agi(foo,–debug,–aniU55551212,–charge255)

    2) Use syslog() to say what’s going on in your AGI. You can vary the level of ‘verbosity’ by signals or by command line options (see #1).

    Sheesh I’m long winded 🙂

    If you still think it would have value to you after reading this email, please let me know.

    *) A valuable debugging technique is to enable AGI debugging in the Asterisk CLI and capture the session when your AGI executes. This file can be munged into a file of the AGI environment and the AGI responses. Then, when you have your AGI loaded into gdb you can ‘run’ it specifying this file as input (‘r

  • 🙂 You reminded me of my teacher of old school days. Very well explained. I have somewhat similar requirement where I need to play some announcements to entertain a caller while passing/processing some data through webservice call ().

    Thanks a lot for your response.

    –Satish Barot

  • Hi Thorsten Normally I use ‘PHPAGI’ in my Asterisk applications but as I said want to explore C as an option.

    Thanks,
    –Satish Barot