2
|
1 /*
|
237
|
2 * aim_txqueue.c
|
|
3 *
|
|
4 * Herein lies all the mangement routines for the transmit (Tx) queue.
|
|
5 *
|
2
|
6 */
|
|
7
|
237
|
8 #include <aim.h>
|
|
9
|
|
10 /*
|
|
11 * Allocate a new tx frame.
|
|
12 *
|
|
13 * This is more for looks than anything else.
|
|
14 *
|
|
15 * Right now, that is. If/when we implement a pool of transmit
|
|
16 * frames, this will become the request-an-unused-frame part.
|
|
17 */
|
|
18 struct command_tx_struct *aim_tx_new(int chan, struct aim_conn_t *conn, int datalen)
|
2
|
19 {
|
237
|
20 struct command_tx_struct *new;
|
2
|
21
|
237
|
22 if (!conn)
|
|
23 return NULL;
|
|
24
|
|
25 new = (struct command_tx_struct *)malloc(sizeof(struct command_tx_struct));
|
|
26 if (!new)
|
|
27 return NULL;
|
|
28 memset(new, 0, sizeof(struct command_tx_struct));
|
2
|
29
|
237
|
30 new->conn = conn;
|
|
31 new->type = chan;
|
|
32
|
|
33 if(datalen) {
|
|
34 new->data = (u_char *)malloc(datalen);
|
|
35 new->commandlen = datalen;
|
|
36 }
|
|
37
|
|
38 return new;
|
|
39 }
|
2
|
40
|
237
|
41 /*
|
|
42 * aim_tx_enqeue()
|
|
43 *
|
|
44 * The overall purpose here is to enqueue the passed in command struct
|
|
45 * into the outgoing (tx) queue. Basically...
|
|
46 * 1) Make a scope-irrelevent copy of the struct
|
|
47 * 2) Lock the struct
|
|
48 * 3) Mark as not-sent-yet
|
|
49 * 4) Enqueue the struct into the list
|
|
50 * 5) Unlock the struct once it's linked in
|
|
51 * 6) Return
|
|
52 *
|
|
53 */
|
|
54 int aim_tx_enqueue(struct aim_session_t *sess,
|
|
55 struct command_tx_struct *newpacket)
|
|
56 {
|
|
57 struct command_tx_struct *cur;
|
2
|
58
|
237
|
59 if (newpacket->conn == NULL) {
|
|
60 faimdprintf(1, "aim_tx_enqueue: WARNING: enqueueing packet with no connecetion\n");
|
|
61 newpacket->conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS);
|
|
62 }
|
|
63
|
|
64 /* assign seqnum */
|
|
65 newpacket->seqnum = aim_get_next_txseqnum(newpacket->conn);
|
|
66 /* set some more fields */
|
|
67 newpacket->lock = 1; /* lock */
|
|
68 newpacket->sent = 0; /* not sent yet */
|
|
69 newpacket->next = NULL; /* always last */
|
2
|
70
|
237
|
71 /* see overhead note in aim_rxqueue counterpart */
|
|
72 if (sess->queue_outgoing == NULL) {
|
|
73 sess->queue_outgoing = newpacket;
|
|
74 } else {
|
|
75 for (cur = sess->queue_outgoing;
|
|
76 cur->next;
|
|
77 cur = cur->next)
|
|
78 ;
|
|
79 cur->next = newpacket;
|
|
80 }
|
|
81
|
|
82 newpacket->lock = 0; /* unlock so it can be sent */
|
|
83
|
|
84 #if debug == 2
|
|
85 faimdprintf(2, "calling aim_tx_printqueue()\n");
|
|
86 aim_tx_printqueue(sess);
|
|
87 faimdprintf(2, "back from aim_tx_printqueue()\n");
|
2
|
88 #endif
|
|
89
|
|
90 return 0;
|
|
91 }
|
|
92
|
|
93 /*
|
237
|
94 * aim_get_next_txseqnum()
|
|
95 *
|
|
96 * This increments the tx command count, and returns the seqnum
|
|
97 * that should be stamped on the next FLAP packet sent. This is
|
|
98 * normally called during the final step of packet preparation
|
|
99 * before enqueuement (in aim_tx_enqueue()).
|
|
100 *
|
2
|
101 */
|
237
|
102 u_int aim_get_next_txseqnum(struct aim_conn_t *conn)
|
2
|
103 {
|
|
104 return ( ++conn->seqnum );
|
|
105 }
|
|
106
|
|
107 /*
|
237
|
108 * aim_tx_printqueue()
|
|
109 *
|
|
110 * This is basically for debuging purposes only. It dumps all the
|
|
111 * records in the tx queue and their current status. Very helpful
|
|
112 * if the queue isn't working quite right.
|
|
113 *
|
2
|
114 */
|
237
|
115 #if debug == 2
|
|
116 int aim_tx_printqueue(struct aim_session_t *sess)
|
2
|
117 {
|
237
|
118 struct command_tx_struct *cur;
|
2
|
119
|
237
|
120 faimdprintf(2, "\ncurrent aim_queue_outgoing...\n");
|
|
121 faimdprintf(2, "\ttype seqnum len lock sent\n");
|
2
|
122
|
237
|
123 if (sess->queue_outgoing == NULL)
|
|
124 faimdprintf(2, "aim_tx_flushqueue(): queue empty");
|
|
125 else {
|
|
126 for (cur = sess->queue_outgoing; cur; cur = cur->next) {
|
|
127 faimdprintf(2, "\t %2x %4x %4x %1d %1d\n",
|
|
128 cur->type, cur->seqnum,
|
|
129 cur->commandlen, cur->lock,
|
|
130 cur->sent);
|
|
131 }
|
|
132 }
|
|
133
|
|
134 faimdprintf(2, "\n(done printing queue)\n");
|
2
|
135
|
|
136 return 0;
|
|
137 }
|
|
138 #endif
|
|
139
|
|
140 /*
|
237
|
141 * aim_tx_flushqueue()
|
|
142 *
|
|
143 * This the function is responsable for putting the queued commands
|
|
144 * onto the wire. This function is critical to the operation of
|
|
145 * the queue and therefore is the most prone to brokenness. It
|
|
146 * seems to be working quite well at this point.
|
|
147 *
|
|
148 * Procedure:
|
|
149 * 1) Traverse the list, only operate on commands that are unlocked
|
|
150 * and haven't been sent yet.
|
|
151 * 2) Lock the struct
|
|
152 * 3) Allocate a temporary buffer to store the finished, fully
|
|
153 * processed packet in.
|
|
154 * 4) Build the packet from the command_tx_struct data.
|
|
155 * 5) Write the packet to the socket.
|
|
156 * 6) If success, mark the packet sent, if fail report failure, do NOT
|
|
157 * mark the packet sent (so it will not get purged and therefore
|
|
158 * be attempted again on next call).
|
|
159 * 7) Unlock the struct.
|
|
160 * 8) Free the temp buffer
|
|
161 * 9) Step to next struct in list and go back to 1.
|
|
162 *
|
2
|
163 */
|
237
|
164 int aim_tx_flushqueue(struct aim_session_t *sess)
|
2
|
165 {
|
237
|
166 struct command_tx_struct *cur;
|
|
167 u_char *curPacket = NULL;
|
2
|
168 #if debug > 1
|
|
169 int i = 0;
|
|
170 #endif
|
|
171
|
237
|
172 if (sess->queue_outgoing == NULL)
|
|
173 return 0;
|
|
174
|
|
175 faimdprintf(2, "beginning txflush...\n");
|
|
176 for (cur = sess->queue_outgoing; cur; cur = cur->next) {
|
|
177 /* only process if its unlocked and unsent */
|
|
178 if (!cur->lock && !cur->sent) {
|
|
179
|
|
180 /*
|
|
181 * And now for the meager attempt to force transmit
|
|
182 * latency and avoid missed messages.
|
|
183 */
|
|
184 if ((cur->conn->lastactivity + cur->conn->forcedlatency) >= time(NULL)) {
|
|
185 /* FIXME FIXME -- should be a break! we dont want to block the upper layers */
|
|
186 sleep((cur->conn->lastactivity + cur->conn->forcedlatency) - time(NULL));
|
|
187 }
|
|
188
|
|
189 cur->lock = 1; /* lock the struct */
|
|
190
|
|
191 /* allocate full-packet buffer */
|
|
192 curPacket = (char *) malloc(cur->commandlen + 6);
|
|
193
|
|
194 /* command byte */
|
|
195 curPacket[0] = 0x2a;
|
|
196
|
|
197 /* type/family byte */
|
|
198 curPacket[1] = cur->type;
|
|
199
|
|
200 /* bytes 3+4: word: FLAP sequence number */
|
|
201 aimutil_put16(curPacket+2, cur->seqnum);
|
2
|
202
|
237
|
203 /* bytes 5+6: word: SNAC len */
|
|
204 aimutil_put16(curPacket+4, cur->commandlen);
|
|
205
|
|
206 /* bytes 7 and on: raw: SNAC data */
|
|
207 memcpy(&(curPacket[6]), cur->data, cur->commandlen);
|
|
208
|
|
209 /* full image of raw packet data now in curPacket */
|
|
210 if ( (u_int)write(cur->conn->fd, curPacket, (cur->commandlen + 6)) != (cur->commandlen + 6)) {
|
|
211 printf("\nWARNING: Error in sending packet 0x%4x -- will try again next time\n\n", cur->seqnum);
|
|
212 cur->sent = 0; /* mark it unsent */
|
|
213 continue; /* bail out */
|
|
214 } else {
|
|
215 faimdprintf(2, "\nSENT 0x%4x\n\n", cur->seqnum);
|
|
216
|
|
217 cur->sent = 1; /* mark the struct as sent */
|
|
218 cur->conn->lastactivity = time(NULL);
|
|
219 }
|
2
|
220 #if debug > 2
|
237
|
221 faimdprintf(2, "\nPacket:");
|
|
222 for (i = 0; i < (cur->commandlen + 6); i++) {
|
|
223 if ((i % 8) == 0) {
|
|
224 faimdprintf(2, "\n\t");
|
|
225 }
|
|
226 if (curPacket[i] >= ' ' && curPacket[i]<127) {
|
|
227 faimdprintf(2, "%c=%02x ", curPacket[i], curPacket[i]);
|
|
228 } else {
|
|
229 faimdprintf(2, "0x%2x ", curPacket[i]);
|
|
230 }
|
|
231 }
|
|
232 faimdprintf(2, "\n");
|
2
|
233 #endif
|
237
|
234 cur->lock = 0; /* unlock the struct */
|
|
235 free(curPacket); /* free up full-packet buffer */
|
2
|
236 }
|
237
|
237 }
|
2
|
238
|
|
239 /* purge sent commands from queue */
|
237
|
240 aim_tx_purgequeue(sess);
|
2
|
241
|
|
242 return 0;
|
|
243 }
|
|
244
|
|
245 /*
|
237
|
246 * aim_tx_purgequeue()
|
|
247 *
|
|
248 * This is responsable for removing sent commands from the transmit
|
|
249 * queue. This is not a required operation, but it of course helps
|
|
250 * reduce memory footprint at run time!
|
|
251 *
|
2
|
252 */
|
237
|
253 void aim_tx_purgequeue(struct aim_session_t *sess)
|
2
|
254 {
|
237
|
255 struct command_tx_struct *cur = NULL;
|
|
256 struct command_tx_struct *tmp;
|
|
257
|
|
258 if (sess->queue_outgoing == NULL)
|
|
259 return;
|
|
260
|
|
261 if (sess->queue_outgoing->next == NULL) {
|
|
262 if (!sess->queue_outgoing->lock && sess->queue_outgoing->sent) {
|
|
263 tmp = sess->queue_outgoing;
|
|
264 sess->queue_outgoing = NULL;
|
|
265 free(tmp->data);
|
|
266 free(tmp);
|
2
|
267 }
|
237
|
268 return;
|
|
269 }
|
|
270
|
|
271 for(cur = sess->queue_outgoing; cur->next != NULL; ) {
|
|
272 if (!cur->next->lock && cur->next->sent) {
|
|
273 tmp = cur->next;
|
|
274 cur->next = tmp->next;
|
|
275 free(tmp->data);
|
|
276 free(tmp);
|
|
277 }
|
|
278 cur = cur->next;
|
2
|
279
|
237
|
280 /*
|
|
281 * Be careful here. Because of the way we just
|
|
282 * manipulated the pointer, cur may be NULL and
|
|
283 * the for() will segfault doing the check unless
|
|
284 * we find this case first.
|
|
285 */
|
|
286 if (cur == NULL)
|
|
287 break;
|
|
288 }
|
|
289 return;
|
2
|
290 }
|