📄 apps.sgml
字号:
<chapter> <title>Application Writing</title> <para> <application moreinfo="none">ser</application> offers several ways to couple its functionality with applications. The coupling is bidirectional: <application moreinfo="none">ser</application> can utilize external applications and external applications can utilize <application moreinfo="none">ser</application>. An example of the former direction would be an external program determining a least-cost route for a called destination using a pricing table. An example of the latter case is a web application for server provisioning. Such an application may want to send instant messages, query all current user's locations and monitor server health. An existing web interface to <application moreinfo="none">ser</application>, <application moreinfo="none">serweb</application>, actually does all of it. </para> <para> The easiest, language-independent way of using external logic from <application moreinfo="none">ser</application> is provided by exec module. exec module allows <application moreinfo="none">ser</application> to start external programs on receipt of a request. The programs can execute arbitrary logic and/or affect routing of SIP requests. A great benefit of this programming method is it is language-independent. Programmers may use programming languages that are effective or with which they are best familiar. <xref linkend="usingexec"> gives additional examples illustrating use of the exec module. </para> <para> Another method for extending <application moreinfo="none">ser</application> capabilities is to write new modules in C. This method takes deeper understanding of <application moreinfo="none">ser</application> internals but gains the highest flexibility. Modules can implement arbitrary brand-new commands upon which <application moreinfo="none">ser</application> scripts can rely on. Guidelines on module programming can be found in <application moreinfo="none">ser</application> programmer's handbook available from iptel.org website. </para> <para> To address needs of applications wishing to leverage <application moreinfo="none">ser</application>, <application moreinfo="none">ser</application> exports parts of its functionality via its built-in "Application FIFO server". This is a simple textual interface that allows any external applications to communicate with the server. It can be used to send instant messages, manipulate user contacts, watch server health, etc. Programs written in any language (PHP, shell scripts, Perl, C, etc.) can utilize this feature. How to use it is shown in <xref linkend="fifoserver">. </para> <section id="usingexec"> <title>Using exec Module</title> <para> The easiest way is to couple <application moreinfo="none">ser</application> with external applications via the <emphasis>exec</emphasis> module. This module allows execution of logic and URI manipulation by external applications on request receipt. While very simple, many useful services can be implemented this way. External applications can be written in any programming language and do not be aware of SIP at all. <application moreinfo="none">ser</application> interacts with the application via standard input/output and environment variables. </para> <para> For example, an external shell script may send an email whenever a request for a user arrives. </para> <example> <title>Using exec: Step 1</title> <programlisting format="linespecific"># send email if a request for user "jiri" arrivesif (uri=~"^sip:jiri@") { exec_msg("echo 'body: call arrived'|mail -s 'call for you' jiri");} </programlisting> </example> <!-- step 1 --> <para> In this example, the <command moreinfo="none">exec_msg</command> action starts an external shell. It passes a received SIP request to shell's input. In the shell, <command>mail</command> command is called to send a notification by e-mail. The script however features several simplifications: <orderedlist inheritnum="ignore" continuation="restarts"> <listitem> <para> The email notification does not tell who was calling. </para> </listitem> <listitem> <para> The logic is not general: it only supports one well-known user (jiri). </para> </listitem> <listitem> <para> The logic is stateless. It will be executed on every retransmission. </para> </listitem> <listitem> <para> It is a script fragment not explaining the context. This particular example may be for example used to report on missed calls. </para> </listitem> </orderedlist> All of these simplifications are addressed step-by-step in the following examples. </para> <example> <!-- step 2: who called me --> <title>Using exec: Step 2, Who Called Me</title> <para> This example shows how to display caller's address in email notification. The trick is easy: process request received on shell program's input and grep From header field. </para> <programlisting format="linespecific">&execstep2; </programlisting> <para> The following two figures show an example SIP request and email notification generated on its receipt. <screen format="linespecific"><![CDATA[INVITE sip:jiri@iptel.org SIP/2.0Via: SIP/2.0/UDP 195.37.77.100:5040Max-Forwards: 10From: "alice" <sip:alice@iptel.org>;tag=76ff7a07-c091-4192-84a0-d56e91fe104fTo: <sip:jiri@iptel.org>Call-ID: d10815e0-bf17-4afa-8412-d9130a793d96@213.20.128.35CSeq: 2 INVITEContact: <sip:123.20.128.35:9315>Content-Type: application/sdpContent-Length: 451--- SDP payload snipped ---]]> </screen> email received: <screen format="linespecific"><![CDATA[Date: Thu, 12 Dec 2002 14:25:02 +0100From: root <root@cat.iptel.org>To: jiri@cat.iptel.orgSubject: request for youFrom: "alice" <sip:alice@iptel.org>;tag=76ff7a07-c091-4192-84a0-d56e91fe104frequest received]]> </screen> </para> <para> There is another way to learn values of request header fields, simpler than use of <command moreinfo="none">grep</command>. <application moreinfo="none">ser</application> parses header fields and passes their values in environment variables. Their names correspond to header field names prefixed with "SIP_HF_". <programlisting format="linespecific"># send email if a request for "jiri" arrivesif (uri=~"^sip:jiri@") { exec_msg("echo request received from $SIP_HF_FROM | mail -s 'request for you' jiri");}; </programlisting> Moreover, several other values are passed in environment variables. <varname>SIP_TID</varname> is a token uniquely identifying transaction, to which the request belongs. <varname>SIP_DID</varname> includes to-tag, and is empty in requests creating a dialog. <varname>SIP_SRCIP</varname> includes IP address, from which the request was sent. <varname>SIP_RURI</varname> and <varname>SIP_ORURI</varname> include current request-uri and original request-uri respectively, <varname>SIP_USER</varname> and <varname>SIP_OUSER</varname> username parts of these. The following listing shows environment variables passed to a shell script on receipt of the previous message: <programlisting format="linespecific"><![CDATA[SIP_HF_MAX_FORWARDS=10SIP_HF_VIA=SIP/2.0/UDP 195.37.77.100:5040SIP_HF_CSEQ=2 INVITESIP_HF_FROM="alice" <sip:alice@iptel.org>;tag=76ff7a07-c091-4192-84a0-d56e91fe104fSIP_ORUI=sip:jiri@iptel.orgSIP_HF_CONTENT_LENGTH=451SIP_TID=3b6b8295db0835815847b1f35f3b29b8SIP_DID=SIP_RURI=iptel.orgSIP_HF_TO=<sip:jiri@iptel.org>SIP_OUSER=jiriSIP_HF_CALLID=d10815e0-bf17-4afa-8412-d9130a793d96@213.20.128.35SIP_SRCIP=195.37.77.100SIP_HF_CONTENT_TYPE=application/sdpSIP_HF_CONTACT=<sip:123.20.128.35:9315>]]> </programlisting> </para> </example> <!-- step 2, who called me --> <example> <!-- step 3, make the script work for anyone --> <title>Using exec: step 3, Make The Script Work For Anyone</title> <para> A drawback of the previous example is it works only for one well-known user: request URI is matched against his SIP address and notification is sent to his hard-wired email address. In real scenarios, one would like to enable such a service for all users without enumerating their addresses in the script. The missing piece is translation of user's SIP name to his email address. This information is maintained in subscriber profiles, stored in MySQL by <application moreinfo="none">ser</application>. To translate the username to email address, the executed script needs to query the MySQL database. That is what this example shows. First, an SQL query is constructed which looks up email address of user, for whom a request arrived. If the query does not return a valid email address, the script returns with an error status and <application moreinfo="none">ser</application> script replies with "user does not exist". Otherwise an email notification is sent. <programlisting format="linespecific">&execstep3; </programlisting> </para> </example> <!-- step 3 make the script work for anyone --> <example> <!-- step 4, stateful processing --> <title>Adding Stateful Processing</title> <para> The previously improved example still features a shortcoming. When a message retransmission arrives due to a network mistake such as lost reply, the email notification is executed again and again. That happens because the script is stateless, i.e., no track of current transactions is kept. The script does not know whether a request is a new or a retransmitted one. Transaction management may be introduced by use of tm module as described in <xref linkend="statefulua">. In the script, <command moreinfo="none">t_newtran</command> is first called to absorb requests retransmission -- if they occur, script does not continue. Then, as in the previous example, an exec module action is called. Eventually, a reply is sent statefully. <note> <para> Note carefully: it is important that the stateful reply processing (<command moreinfo="none">t_reply</command>) is used as opposed to using stateless replies (<command moreinfo="none">sl_send_reply</command>). Otherwise, the outgoing reply would not affect transactional context and would not be resent on receipt of a request retransmission. </para> </note> <programlisting format="linespecific">&execstep4; </programlisting> </para> </example> <!-- step 4, stateful processing --> <example> <!-- step 5, full exec use --> <title>Full Example of exec Use</title> <para> The last example iteration shows how to integrate the email notification on missed calls with the default <application moreinfo="none">ser</application> script (see <xref linkend="defaultscript">). It generates an email for every call invitation to an off-line user. <programlisting format="linespecific">&execstep5; </programlisting> </para> <para> Production "missed calls" services may want to report on calls missed for other reasons than being off-line too. Particularly, users may wish to be reported calls missed due to call cancellation, busy status or a downstream failure. Such missed calls can be easily reported to syslog or mysql using the acc module (see <xref linkend="missedcalls">). The other, more general way, is to return to request processing on receipt of a negative reply. (see <xref linkend="replyprocessingsection">). Before a request is forwarded, it is labeled to be re-processed in a <command moreinfo="none">failure_route</command> on receipt of a negative reply -- this is what <command moreinfo="none">t_on_failure</command> action is used for. It does not matter what caused the transaction to fail -- it may be unresponsive downstream server, server responding with 6xx, or server sending a 487 reply, because an INVITE was canceled. When any such circumstances occur (i.e., transaction does not complete with a 2xx status code), <command moreinfo="none">failure_route</command> is entered. </para> <para> The following <application moreinfo="none">ser</application> script reports missed calls in all possible cases. It reports them when a user is off-line as well as when a user is on-line, but INVITE transaction does not complete successfully. <programlisting format="linespecific">&execstep5b; </programlisting> </para> </example> <!-- step 5, full exec use --> </section> <!-- using exec --> <section id="fifoserver"> <title>Application FIFO Server</title> <para> Application FIFO server is a very powerful method to program SIP services. The most valuable benefit is it works with SIP-unaware applications written in any programming language. Textual nature of the FIFO interface allows for easy integration with a lot of existing programs. Today, <application moreinfo="none">ser</application>'s complementary web-interface, <application moreinfo="none">serweb</application>, written in PHP, leverages the FIFO interface when displaying and changing user location records stored in server's memory. It uses this interface to send instant messages too, without any knowledge of underlying <acronym>SIP</acronym> stack. Another application relying on the FIFO interface is <application moreinfo="none">serctl</application>, <application moreinfo="none">ser</application> management utility. The command-line utility can browse server's in-memory user-location database, display running processes and operational statistics. </para> <para> The way the FIFO server works is similar to how <filename moreinfo="none">/proc</filename> filesystem works on some operating systems. It provides a human-readable way to access <application moreinfo="none">ser</application>'s internals. Applications dump their requests into the FIFO server and receive a status report when request processing completes. <application moreinfo="none">ser</application> exports a lot of its functionality located in both the core and external modules through the FIFO server. </para> <para> FIFO requests are formed easily. They begin with a command enclosed in colons and followed by name of file or pipe (relative to <filename moreinfo="none">/tmp/</filename> path), to which a reply should be printed. The first request line may be followed by additional lines with command-specific parameters. For example, the <command moreinfo="none">t_uac_dlg</command> FIFO command for initiating a transaction allows to pass additional header fields and message body to a newly created transaction. Each request is terminated by an empty line. Whole requests must be sent by applications atomically in a single batch to avoid mixing with requests from other applications. Requests are sent to pipe at which <application moreinfo="none">ser</application> listens (filename configured by the <varname>fifo</varname> config file option). </para> <para> An easy way to use the FIFO interface is via the <application moreinfo="none">serctl</application> command-line tool. When called along with "fifo", FIFO command name, and optional parameters, the tool generates a FIFO request and prints request result. The following example shows use of this tool with the <command moreinfo="none">uptime</command> and <command moreinfo="none">which</command> commands. <command moreinfo="none">uptime</command> returns server's running time, <command moreinfo="none">which</command> returns list of available FIFO commands. Note that only the built-in FIFO command set is displayed as no modules were loaded in this example. <example> <title>Use of <application moreinfo="none">serctl</application> to Access FIFO Server</title> <programlisting format="linespecific">[jiri@cat test]$ serctl fifo uptimeNow: Fri Dec 6 17:56:10 2002Up Since: Fri Dec 6 17:56:07 2002Up time: 3 [sec][jiri@cat test]$ serctl fifo which
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -