Browse code

t_suspend() and t_continue() functions are introduced.

These fuctions can be used by other modules to implement
asynchronous actions: t_suspend() saves the transaction, returns its
identifiers, and t_continue() continues the SIP request processing.
(The transaction processing does not continue from the same point
in the script, a separate route block defined by the parameter of
t_continue() is executed instead. The reply lock is held during the
route block execution.) FR timer is ticking while the
transaction is suspended, and the transaction's failure route is
executed if t_continue() is not called in time.

Missing: msg lumps are saved by t_suspend() and are not updated by
the subsequent t_relay(). This means that the modifications made
between them are lost.

Miklos Tirpak authored on 10/11/2008 12:47:02
Showing 9 changed files
... ...
@@ -103,7 +103,12 @@ int cancel_uacs( struct cell *t, branch_bm_t cancel_bm, int flags)
103 103
 	/* cancel pending client transactions, if any */
104 104
 	for( i=0 ; i<t->nr_of_outgoings ; i++ ) 
105 105
 		if (cancel_bm & (1<<i)){
106
-			r=cancel_branch(t, i, flags);
106
+			r=cancel_branch(
107
+				t,
108
+				i,
109
+				flags | ((t->uac[i].request.buffer==NULL)?
110
+					F_CANCEL_B_FAKE_REPLY:0) /* blind UAC? */
111
+			);
107 112
 			ret|=(r!=0)<<i;
108 113
 		}
109 114
 	return ret;
... ...
@@ -733,7 +733,13 @@ void e2e_cancel( struct sip_msg *cancel_msg,
733 733
 			 * called with the cancel as the "current" transaction so
734 734
 			 * at most t_cancel REPLY_LOCK is held in this process =>
735 735
 			 * no deadlock possibility */
736
-			ret=cancel_branch(t_invite, i, cfg_get(tm,tm_cfg, cancel_b_flags));
736
+			ret=cancel_branch(
737
+				t_invite,
738
+				i,
739
+				cfg_get(tm,tm_cfg, cancel_b_flags)
740
+					| ((t_invite->uac[i].request.buffer==NULL)?
741
+						F_CANCEL_B_FAKE_REPLY:0) /* blind UAC? */
742
+			);
737 743
 			if (ret<0) cancel_bm &= ~(1<<i);
738 744
 			if (ret<lowest_error) lowest_error=ret;
739 745
 		}
... ...
@@ -626,7 +626,7 @@ static int _reply( struct cell *trans, struct sip_msg* p_msg,
626 626
 
627 627
 /*if msg is set -> it will fake the env. vars conforming with the msg; if NULL
628 628
  * the env. will be restore to original */
629
-static inline void faked_env( struct cell *t,struct sip_msg *msg)
629
+void faked_env( struct cell *t,struct sip_msg *msg)
630 630
 {
631 631
 	static enum route_mode backup_mode;
632 632
 	static struct cell *backup_t;
... ...
@@ -683,7 +683,7 @@ static inline void faked_env( struct cell *t,struct sip_msg *msg)
683 683
 }
684 684
 
685 685
 
686
-static inline int fake_req(struct sip_msg *faked_req,
686
+int fake_req(struct sip_msg *faked_req,
687 687
 							struct sip_msg *shmem_msg, int extra_flags)
688 688
 {
689 689
 	/* on_negative_reply faked msg now copied from shmem msg (as opposed
... ...
@@ -731,7 +731,7 @@ error00:
731 731
 	return 0;
732 732
 }
733 733
 
734
-void inline static free_faked_req(struct sip_msg *faked_req, struct cell *t)
734
+void free_faked_req(struct sip_msg *faked_req, struct cell *t)
735 735
 {
736 736
 	struct hdr_field *hdr;
737 737
 
... ...
@@ -156,4 +156,10 @@ void t_drop_replies(void);
156 156
 extern const char* rpc_reply_doc[2];
157 157
 void rpc_reply(rpc_t* rpc, void* c);
158 158
 
159
+void faked_env( struct cell *t,struct sip_msg *msg);
160
+int fake_req(struct sip_msg *faked_req,
161
+			struct sip_msg *shmem_msg, int extra_flags);
162
+
163
+void free_faked_req(struct sip_msg *faked_req, struct cell *t);
164
+
159 165
 #endif
160 166
new file mode 100644
... ...
@@ -0,0 +1,154 @@
0
+/*
1
+ * $Id$
2
+ *
3
+ * Copyright (C) 2008 iptelorg GmbH
4
+ *
5
+ * This file is part of ser, a free SIP server.
6
+ *
7
+ * ser is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version
11
+ *
12
+ * For a license to use the ser software under conditions
13
+ * other than those described here, or to purchase support for this
14
+ * software, please contact iptel.org by e-mail at the following addresses:
15
+ *    info@iptel.org
16
+ *
17
+ * ser is distributed in the hope that it will be useful,
18
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
+ * GNU General Public License for more details.
21
+ *
22
+ * You should have received a copy of the GNU General Public License 
23
+ * along with this program; if not, write to the Free Software 
24
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
+ *
26
+ * History:
27
+ * --------
28
+ *  2008-11-10	Initial version (Miklos)
29
+ *
30
+ */
31
+
32
+#include "sip_msg.h"
33
+#include "t_reply.h"
34
+#include "h_table.h"
35
+#include "t_lookup.h"
36
+#include "t_fwd.h"
37
+#include "timer.h"
38
+#include "t_suspend.h"
39
+
40
+int t_suspend(struct sip_msg *msg,
41
+		unsigned int *hash_index, unsigned int *label)
42
+{
43
+	struct cell	*t;
44
+
45
+	t = get_t();
46
+	if (!t || t == T_UNDEFINED) {
47
+		LOG(L_ERR, "ERROR: t_suspend: " \
48
+			"transaction has not been created yet\n");
49
+		return -1;
50
+	}
51
+
52
+	/* send a 100 Trying reply, because the INVITE processing
53
+	will probably take a long time */
54
+	if (msg->REQ_METHOD==METHOD_INVITE && (t->flags&T_AUTO_INV_100)) {
55
+		if (!t_reply( t, msg , 100 ,
56
+			"trying -- your call is important to us"))
57
+				DBG("SER: ERROR: t_suspend (100)\n");
58
+	}
59
+
60
+#ifdef POSTPONE_MSG_CLONING
61
+	if ((t->nr_of_outgoings==0) && /* if there had already been
62
+				an UAC created, then the lumps were
63
+				saved as well */
64
+		save_msg_lumps(t->uas.request, msg)
65
+	) {
66
+		LOG(L_ERR, "ERROR: t_suspend: " \
67
+			"failed to save the message lumps\n");
68
+		return -1;
69
+	}
70
+#endif
71
+	/* save the message flags */
72
+	t->uas.request->flags = msg->flags;
73
+
74
+	*hash_index = t->hash_index;
75
+	*label = t->label;
76
+
77
+	/* add a bling UAC to let the fr timer running */
78
+	if (add_blind_uac() < 0) {
79
+		LOG(L_ERR, "ERROR: t_suspend: " \
80
+			"failed to add the blind UAC\n");
81
+		return -1;
82
+	}
83
+
84
+	return 0;
85
+}
86
+
87
+int t_continue(unsigned int hash_index, unsigned int label,
88
+		struct action *route)
89
+{
90
+	struct cell	*t;
91
+	struct sip_msg	faked_req;
92
+	struct run_act_ctx	ra_ctx;
93
+	int	branch;
94
+
95
+	if (t_lookup_ident(&t, hash_index, label) < 0) {
96
+		LOG(L_ERR, "ERROR: t_continue: transaction not found\n");
97
+		return -1;
98
+	}
99
+
100
+	/* The transaction has to be locked to protect it
101
+	 * form calling t_continue() multiple times simultaneously */
102
+	LOCK_REPLIES(t);
103
+
104
+	/* Try to find the blind UAC, and cancel its fr timer.
105
+	 * We assume that the last blind uac called t_continue(). */
106
+	for (	branch = t->nr_of_outgoings-1;
107
+		branch >= 0 && t->uac[branch].request.buffer;
108
+		branch--);
109
+
110
+	if (branch >= 0) {
111
+		stop_rb_timers(&t->uac[branch].request);
112
+		/* Set last_received to something >= 200,
113
+		 * the actual value does not matter, the branch
114
+		 * will never be picked up for response forwarding.
115
+		 * If last_received is lower than 200,
116
+		 * then the branch may tried to be cancelled later,
117
+		 * for example when t_reply() is called from
118
+		 * a failure rute => deadlock, because both
119
+		 * of them need the reply lock to be held. */
120
+		t->uac[branch].last_received=500;
121
+	}
122
+	/* else
123
+		Not a huge problem, fr timer will fire, but CANCEL
124
+		will not be sent. last_received will be set to 408. */
125
+
126
+	/* fake the request and the environment, like in failure_route */
127
+	if (!fake_req(&faked_req, t->uas.request, 0 /* extra flags */)) {
128
+		LOG(L_ERR, "ERROR: t_continue: fake_req failed\n");
129
+		UNLOCK_REPLIES(t);
130
+		return -1;
131
+	}
132
+	faked_env( t, &faked_req);
133
+
134
+	init_run_actions_ctx(&ra_ctx);
135
+	if (run_actions(&ra_ctx, route, &faked_req)<0)
136
+		LOG(L_ERR, "ERROR: t_continue: Error in run_action\n");
137
+
138
+	/* TODO: save_msg_lumps should clone the lumps to shm mem */
139
+
140
+	/* restore original environment and free the fake msg */
141
+	faked_env( t, 0);
142
+	free_faked_req(&faked_req, t);
143
+
144
+	/* update the flags */
145
+	t->uas.request->flags = faked_req.flags;
146
+
147
+	UNLOCK_REPLIES(t);
148
+
149
+	/* release the transaction */
150
+	t_unref(t->uas.request);
151
+
152
+	return 0;
153
+}
0 154
new file mode 100644
... ...
@@ -0,0 +1,42 @@
0
+/*
1
+ * $Id$
2
+ *
3
+ * Copyright (C) 2008 iptelorg GmbH
4
+ *
5
+ * This file is part of ser, a free SIP server.
6
+ *
7
+ * ser is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version
11
+ *
12
+ * For a license to use the ser software under conditions
13
+ * other than those described here, or to purchase support for this
14
+ * software, please contact iptel.org by e-mail at the following addresses:
15
+ *    info@iptel.org
16
+ *
17
+ * ser is distributed in the hope that it will be useful,
18
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
+ * GNU General Public License for more details.
21
+ *
22
+ * You should have received a copy of the GNU General Public License 
23
+ * along with this program; if not, write to the Free Software 
24
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
+ *
26
+ */
27
+
28
+#ifndef _T_SUSPEND_H
29
+#define _T_SUSPEND_H
30
+
31
+int t_suspend(struct sip_msg *msg,
32
+		unsigned int *hash_index, unsigned int *label);
33
+typedef int (*t_suspend_f)(struct sip_msg *msg,
34
+		unsigned int *hash_index, unsigned int *label);
35
+
36
+int t_continue(unsigned int hash_index, unsigned int label,
37
+		struct action *route);
38
+typedef int (*t_continue_f)(unsigned int hash_index, unsigned int label,
39
+		struct action *route);
40
+
41
+#endif /* _T_SUSPEND_H */
... ...
@@ -400,6 +400,8 @@ static cmd_export_t cmds[]={
400 400
 	{"t_get_canceled_ident",   (cmd_function)t_get_canceled_ident,  NO_SCRIPT,
401 401
 			0, 0},
402 402
 #endif
403
+	{"t_suspend",          (cmd_function)t_suspend,         NO_SCRIPT,   0, 0},
404
+	{"t_continue",         (cmd_function)t_continue,        NO_SCRIPT,   0, 0},
403 405
 	{0,0,0,0,0}
404 406
 };
405 407
 
... ...
@@ -216,5 +216,15 @@ int load_tm( struct tm_binds *tmb)
216 216
 		return -1;
217 217
 	}
218 218
 #endif
219
+	if (! (tmb->t_suspend=(t_suspend_f)find_export("t_suspend",
220
+			NO_SCRIPT, 0))) {
221
+		LOG( L_ERR, LOAD_ERROR "'t_suspend' not found\n");
222
+		return -1;
223
+	}
224
+	if (! (tmb->t_continue=(t_continue_f)find_export("t_continue",
225
+			NO_SCRIPT, 0))) {
226
+		LOG( L_ERR, LOAD_ERROR "'t_continue' not found\n");
227
+		return -1;
228
+	}
219 229
 	return 1;
220 230
 }
... ...
@@ -47,6 +47,7 @@
47 47
 #include "t_reply.h"
48 48
 #include "dlg.h"
49 49
 #include "t_cancel.h"
50
+#include "t_suspend.h"
50 51
 
51 52
 /* export not usable from scripts */
52 53
 #define NO_SCRIPT	-1
... ...
@@ -129,6 +130,8 @@ struct tm_binds {
129 129
 	void* reserved3;
130 130
 	void* reserved4;
131 131
 #endif
132
+	t_suspend_f	t_suspend;
133
+	t_continue_f	t_continue;
132 134
 };
133 135
 
134 136
 extern int tm_init;