mbim-proxy support
[project/umbim.git] / cli.c
1 /*
2 * umbim
3 * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2
7 * as published by the Free Software Foundation
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15 #define __STDC_FORMAT_MACROS
16 #include <inttypes.h>
17
18 #include <sys/types.h>
19 #include <sys/stat.h>
20
21 #include <alloca.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <ctype.h>
28
29 #include <libubox/utils.h>
30 #include <libubox/uloop.h>
31
32 #include "mbim.h"
33
34 #include "data/mbim-service-basic-connect.h"
35
36 int return_code = -1;
37 int verbose;
38
39 struct mbim_handler *current_handler;
40 static uint8_t uuid_context_type_internet[16] = { 0x7E, 0x5E, 0x2A, 0x7E, 0x4E, 0x6F, 0x72, 0x72, 0x73, 0x6B, 0x65, 0x6E, 0x7E, 0x5E, 0x2A, 0x7E };
41 static int _argc;
42 static char **_argv;
43
44 static int
45 mbim_device_caps_response(void *buffer, int len)
46 {
47 struct mbim_basic_connect_device_caps_r *caps = (struct mbim_basic_connect_device_caps_r *) buffer;
48 char *deviceid, *firmwareinfo, *hardwareinfo;
49
50 if (len < sizeof(struct mbim_basic_connect_device_caps_r)) {
51 fprintf(stderr, "message not long enough\n");
52 return -1;
53 }
54
55 deviceid = mbim_get_string(&caps->deviceid, buffer);
56 firmwareinfo = mbim_get_string(&caps->firmwareinfo, buffer);
57 hardwareinfo = mbim_get_string(&caps->hardwareinfo, buffer);
58
59 printf(" devicetype: %04X - %s\n", le32toh(caps->devicetype),
60 mbim_enum_string(mbim_device_type_values, le32toh(caps->devicetype)));
61 printf(" cellularclass: %04X\n", le32toh(caps->cellularclass));
62 printf(" voiceclass: %04X - %s\n", le32toh(caps->voiceclass),
63 mbim_enum_string(mbim_voice_class_values, le32toh(caps->voiceclass)));
64 printf(" simclass: %04X\n", le32toh(caps->simclass));
65 printf(" dataclass: %04X\n", le32toh(caps->dataclass));
66 printf(" smscaps: %04X\n", le32toh(caps->smscaps));
67 printf(" controlcaps: %04X\n", le32toh(caps->controlcaps));
68 printf(" maxsessions: %04X\n", le32toh(caps->maxsessions));
69 printf(" deviceid: %s\n", deviceid);
70 printf(" firmwareinfo: %s\n", firmwareinfo);
71 printf(" hardwareinfo: %s\n", hardwareinfo);
72
73 return 0;
74 }
75
76 static int
77 mbim_pin_state_response(void *buffer, int len)
78 {
79 struct mbim_basic_connect_pin_r *pin = (struct mbim_basic_connect_pin_r *) buffer;
80
81 if (len < sizeof(struct mbim_basic_connect_pin_r)) {
82 fprintf(stderr, "message not long enough\n");
83 return -1;
84 }
85
86 if (le32toh(pin->pinstate) != MBIM_PIN_STATE_UNLOCKED) {
87 fprintf(stderr, "required pin: %d - %s\n",
88 le32toh(pin->pintype), mbim_enum_string(mbim_pin_type_values, le32toh(pin->pintype)));
89 fprintf(stderr, "remaining attempts: %d\n", le32toh(pin->remainingattempts));
90 return le32toh(pin->pintype);
91 }
92
93 fprintf(stderr, "Pin Unlocked\n");
94
95 return 0;
96 }
97
98 static int
99 mbim_registration_response(void *buffer, int len)
100 {
101 struct mbim_basic_connect_register_state_r *state = (struct mbim_basic_connect_register_state_r *) buffer;
102 char *provider_id, *provider_name, *roamingtext;
103
104 if (len < sizeof(struct mbim_basic_connect_register_state_r)) {
105 fprintf(stderr, "message not long enough\n");
106 return -1;
107 }
108
109 provider_id = mbim_get_string(&state->providerid, buffer);
110 provider_name = mbim_get_string(&state->providername, buffer);
111 roamingtext = mbim_get_string(&state->roamingtext, buffer);
112
113 printf(" nwerror: %04X - %s\n", le32toh(state->nwerror),
114 mbim_enum_string(mbim_nw_error_values, le32toh(state->nwerror)));
115 printf(" registerstate: %04X - %s\n", le32toh(state->registerstate),
116 mbim_enum_string(mbim_register_state_values, le32toh(state->registerstate)));
117 printf(" registermode: %04X - %s\n", le32toh(state->registermode),
118 mbim_enum_string(mbim_register_mode_values, le32toh(state->registermode)));
119 printf(" availabledataclasses: %04X - %s\n", le32toh(state->availabledataclasses),
120 mbim_enum_string(mbim_data_class_values, le32toh(state->availabledataclasses)));
121 printf(" currentcellularclass: %04X - %s\n", le32toh(state->currentcellularclass),
122 mbim_enum_string(mbim_cellular_class_values, le32toh(state->currentcellularclass)));
123 printf(" provider_id: %s\n", provider_id);
124 printf(" provider_name: %s\n", provider_name);
125 printf(" roamingtext: %s\n", roamingtext);
126
127 if (le32toh(state->registerstate) == MBIM_REGISTER_STATE_HOME)
128 return 0;
129
130 return le32toh(state->registerstate);
131 }
132
133 static int
134 mbim_subscriber_response(void *buffer, int len)
135 {
136 struct mbim_basic_connect_subscriber_ready_status_r *state = (struct mbim_basic_connect_subscriber_ready_status_r *) buffer;
137 char *subscriberid, *simiccid;
138 int nr;
139
140 if (len < sizeof(struct mbim_basic_connect_subscriber_ready_status_r)) {
141 fprintf(stderr, "message not long enough\n");
142 return -1;
143 }
144
145 subscriberid = mbim_get_string(&state->subscriberid, buffer);
146 simiccid = mbim_get_string(&state->simiccid, buffer);
147
148 printf(" readystate: %04X - %s\n", le32toh(state->readystate),
149 mbim_enum_string(mbim_subscriber_ready_state_values, le32toh(state->readystate)));
150 printf(" simiccid: %s\n", simiccid);
151 printf(" subscriberid: %s\n", subscriberid);
152 if (le32toh(state->readyinfo) & MBIM_READY_INFO_FLAG_PROTECT_UNIQUE_ID)
153 printf(" dont display subscriberID: 1\n");
154 for (nr = 0; nr < le32toh(state->telephonenumberscount); nr++) {
155 struct mbim_string *str = (void *)&state->telephonenumbers + (nr * sizeof(struct mbim_string));
156 char *number = mbim_get_string(str, buffer);
157 printf(" number: %s\n", number);
158 }
159
160 if (MBIM_SUBSCRIBER_READY_STATE_INITIALIZED == le32toh(state->readystate))
161 return 0;
162
163 return le32toh(state->readystate);
164 }
165
166 static int
167 mbim_attach_response(void *buffer, int len)
168 {
169 struct mbim_basic_connect_packet_service_r *ps = (struct mbim_basic_connect_packet_service_r *) buffer;
170
171 if (len < sizeof(struct mbim_basic_connect_packet_service_r)) {
172 fprintf(stderr, "message not long enough\n");
173 return -1;
174 }
175
176 printf(" nwerror: %04X - %s\n", le32toh(ps->nwerror),
177 mbim_enum_string(mbim_nw_error_values, le32toh(ps->nwerror)));
178 printf(" packetservicestate: %04X - %s\n", le32toh(ps->packetservicestate),
179 mbim_enum_string(mbim_packet_service_state_values, le32toh(ps->packetservicestate)));
180 printf(" uplinkspeed: %"PRIu64"\n", (uint64_t) le64toh(ps->uplinkspeed));
181 printf(" downlinkspeed: %"PRIu64"\n", (uint64_t) le64toh(ps->downlinkspeed));
182
183 if (MBIM_PACKET_SERVICE_STATE_ATTACHED == le32toh(ps->packetservicestate))
184 return 0;
185
186 return le32toh(ps->packetservicestate);
187 }
188
189 static int
190 mbim_connect_response(void *buffer, int len)
191 {
192 struct mbim_basic_connect_connect_r *c = (struct mbim_basic_connect_connect_r *) buffer;
193
194 if (len < sizeof(struct mbim_basic_connect_connect_r)) {
195 fprintf(stderr, "message not long enough\n");
196 return -1;
197 }
198
199 printf(" sessionid: %d\n", le32toh(c->sessionid));
200 printf(" activationstate: %04X - %s\n", le32toh(c->activationstate),
201 mbim_enum_string(mbim_activation_state_values, le32toh(c->activationstate)));
202 printf(" voicecallstate: %04X - %s\n", le32toh(c->voicecallstate),
203 mbim_enum_string(mbim_voice_call_state_values, le32toh(c->voicecallstate)));
204 printf(" nwerror: %04X - %s\n", le32toh(c->nwerror),
205 mbim_enum_string(mbim_nw_error_values, le32toh(c->nwerror)));
206 printf(" iptype: %04X - %s\n", le32toh(c->iptype),
207 mbim_enum_string(mbim_context_ip_type_values, le32toh(c->iptype)));
208
209 if (MBIM_ACTIVATION_STATE_ACTIVATED == le32toh(c->activationstate))
210 return 0;
211
212 return le32toh(c->activationstate);
213 }
214
215 static int
216 mbim_config_response(void *buffer, int len)
217 {
218 struct mbim_basic_connect_ip_configuration_r *ip = (struct mbim_basic_connect_ip_configuration_r *) buffer;
219 char out[40];
220 int i;
221 uint32_t offset;
222
223 if (len < sizeof(struct mbim_basic_connect_ip_configuration_r)) {
224 fprintf(stderr, "message not long enough\n");
225 return -1;
226 }
227
228 if (le32toh(ip->ipv4configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS)
229 for (i = 0; i < le32toh(ip->ipv4addresscount); i++) {
230 offset = le32toh(ip->ipv4address) + (i * 4);
231 mbim_get_ipv4(buffer, out, 4 + offset);
232 printf(" ipv4address: %s/%d\n", out, mbim_get_int(buffer, offset));
233 }
234 if (le32toh(ip->ipv4configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) {
235 mbim_get_ipv4(buffer, out, le32toh(ip->ipv4gateway));
236 printf(" ipv4gateway: %s\n", out);
237 }
238 if (le32toh(ip->ipv4configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU)
239 printf(" ipv4mtu: %d\n", le32toh(ip->ipv4mtu));
240 if (le32toh(ip->ipv4configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS)
241 for (i = 0; i < le32toh(ip->ipv4dnsservercount); i++) {
242 mbim_get_ipv4(buffer, out, le32toh(ip->ipv4dnsserver) + (i * 4));
243 printf(" ipv4dnsserver: %s\n", out);
244 }
245
246 if (le32toh(ip->ipv6configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS)
247 for (i = 0; i < le32toh(ip->ipv6addresscount); i++) {
248 offset = le32toh(ip->ipv6address) + (i * 16);
249 mbim_get_ipv6(buffer, out, 4 + offset);
250 printf(" ipv6address: %s/%d\n", out, mbim_get_int(buffer, offset));
251 }
252 if (le32toh(ip->ipv6configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) {
253 mbim_get_ipv6(buffer, out, le32toh(ip->ipv6gateway));
254 printf(" ipv6gateway: %s\n", out);
255 }
256 if (le32toh(ip->ipv6configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU)
257 printf(" ipv6mtu: %d\n", le32toh(ip->ipv6mtu));
258 if (le32toh(ip->ipv6configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS)
259 for (i = 0; i < le32toh(ip->ipv6dnsservercount); i++) {
260 mbim_get_ipv6(buffer, out, le32toh(ip->ipv6dnsserver) + (i * 16));
261 printf(" ipv6dnsserver: %s\n", out);
262 }
263
264 return 0;
265 }
266
267 static int
268 mbim_radio_response(void *buffer, int len)
269 {
270 struct mbim_basic_connect_radio_state_r *r = (struct mbim_basic_connect_radio_state_r *) buffer;
271
272 if (len < sizeof(struct mbim_basic_connect_radio_state_r)) {
273 fprintf(stderr, "message not long enough\n");
274 return -1;
275 }
276 printf(" hwradiostate: %s\n", r->hwradiostate ? "on" : "off");
277 printf(" swradiostate: %s\n", r->swradiostate ? "on" : "off");
278 return 0;
279 }
280
281 static int
282 mbim_device_caps_request(void)
283 {
284 mbim_setup_command_msg(basic_connect, MBIM_MESSAGE_COMMAND_TYPE_QUERY, MBIM_CMD_BASIC_CONNECT_DEVICE_CAPS, 0);
285
286 return mbim_send_command_msg();
287 }
288
289 static int
290 mbim_pin_state_request(void)
291 {
292 mbim_setup_command_msg(basic_connect, MBIM_MESSAGE_COMMAND_TYPE_QUERY, MBIM_CMD_BASIC_CONNECT_PIN, 0);
293
294 return mbim_send_command_msg();
295 }
296
297 static int
298 mbim_registration_request(void)
299 {
300 if (_argc > 0) {
301 struct mbim_basic_connect_register_state_s *rs =
302 (struct mbim_basic_connect_register_state_s *) mbim_setup_command_msg(basic_connect,
303 MBIM_MESSAGE_COMMAND_TYPE_SET, MBIM_CMD_BASIC_CONNECT_REGISTER_STATE,
304 sizeof(struct mbim_basic_connect_register_state_s));
305
306 rs->registeraction = htole32(MBIM_REGISTER_ACTION_AUTOMATIC);
307 } else {
308 mbim_setup_command_msg(basic_connect, MBIM_MESSAGE_COMMAND_TYPE_QUERY, MBIM_CMD_BASIC_CONNECT_REGISTER_STATE, 0);
309 }
310
311 return mbim_send_command_msg();
312 }
313
314 static int
315 mbim_subscriber_request(void)
316 {
317 mbim_setup_command_msg(basic_connect, MBIM_MESSAGE_COMMAND_TYPE_QUERY, MBIM_CMD_BASIC_CONNECT_SUBSCRIBER_READY_STATUS, 0);
318
319 return mbim_send_command_msg();
320 }
321
322 static int
323 _mbim_attach_request(int action)
324 {
325 struct mbim_basic_connect_packet_service_s *ps =
326 (struct mbim_basic_connect_packet_service_s *) mbim_setup_command_msg(basic_connect,
327 MBIM_MESSAGE_COMMAND_TYPE_SET, MBIM_CMD_BASIC_CONNECT_PACKET_SERVICE,
328 sizeof(struct mbim_basic_connect_packet_service_s));
329
330 ps->packetserviceaction = htole32(action);
331
332 return mbim_send_command_msg();
333 }
334
335 static int
336 mbim_attach_request(void)
337 {
338 return _mbim_attach_request(MBIM_PACKET_SERVICE_ACTION_ATTACH);
339 }
340
341 static int
342 mbim_detach_request(void)
343 {
344 return _mbim_attach_request(MBIM_PACKET_SERVICE_ACTION_DETACH);
345 }
346
347 static int
348 mbim_connect_request(void)
349 {
350 char *apn;
351 struct mbim_basic_connect_connect_s *c =
352 (struct mbim_basic_connect_connect_s *) mbim_setup_command_msg(basic_connect,
353 MBIM_MESSAGE_COMMAND_TYPE_SET, MBIM_CMD_BASIC_CONNECT_CONNECT,
354 sizeof(struct mbim_basic_connect_connect_s));
355
356 c->activationcommand = htole32(MBIM_ACTIVATION_COMMAND_ACTIVATE);
357 c->iptype = htole32(MBIM_CONTEXT_IP_TYPE_DEFAULT);
358 memcpy(c->contexttype, uuid_context_type_internet, 16);
359 if (_argc > 0) {
360 apn = index(*_argv, ':');
361 if (!apn) {
362 apn = *_argv;
363 } else {
364 apn[0] = 0;
365 apn++;
366 if (!strcmp(*_argv, "ipv4"))
367 c->iptype = htole32(MBIM_CONTEXT_IP_TYPE_IPV4);
368 else if (!strcmp(*_argv, "ipv6"))
369 c->iptype = htole32(MBIM_CONTEXT_IP_TYPE_IPV6);
370 else if (!strcmp(*_argv, "ipv4v6"))
371 c->iptype = htole32(MBIM_CONTEXT_IP_TYPE_IPV4V6);
372 }
373 mbim_encode_string(&c->accessstring, apn);
374 }
375 if (_argc > 3) {
376 if (!strcmp(_argv[1], "pap"))
377 c->authprotocol = htole32(MBIM_AUTH_PROTOCOL_PAP);
378 else if (!strcmp(_argv[1], "chap"))
379 c->authprotocol = htole32(MBIM_AUTH_PROTOCOL_CHAP);
380 else if (!strcmp(_argv[1], "mschapv2"))
381 c->authprotocol = htole32(MBIM_AUTH_PROTOCOL_MSCHAPV2);
382
383 if (c->authprotocol) {
384 mbim_encode_string(&c->username, _argv[2]);
385 mbim_encode_string(&c->password, _argv[3]);
386 }
387 }
388 return mbim_send_command_msg();
389 }
390
391 static int
392 mbim_disconnect_request(void)
393 {
394 struct mbim_basic_connect_connect_s *c =
395 (struct mbim_basic_connect_connect_s *) mbim_setup_command_msg(basic_connect,
396 MBIM_MESSAGE_COMMAND_TYPE_SET, MBIM_CMD_BASIC_CONNECT_CONNECT,
397 sizeof(struct mbim_basic_connect_connect_s));
398
399 c->activationcommand = htole32(MBIM_ACTIVATION_COMMAND_DEACTIVATE);
400 memcpy(c->contexttype, uuid_context_type_internet, 16);
401
402 no_close = 0;
403
404 return mbim_send_command_msg();
405 }
406
407 static char*
408 mbim_pin_sanitize(char *pin)
409 {
410 char *p;
411
412 while (*pin && !isdigit(*pin))
413 pin++;
414 p = pin;
415 if (!*p)
416 return NULL;
417 while (*pin && isdigit(*pin))
418 pin++;
419 if (*pin)
420 *pin = '\0';
421
422 return p;
423 }
424
425 static int
426 mbim_pin_unlock_request(void)
427 {
428 struct mbim_basic_connect_pin_s *p =
429 (struct mbim_basic_connect_pin_s *) mbim_setup_command_msg(basic_connect,
430 MBIM_MESSAGE_COMMAND_TYPE_SET, MBIM_CMD_BASIC_CONNECT_PIN,
431 sizeof(struct mbim_basic_connect_pin_s));
432 char *pin = mbim_pin_sanitize(_argv[0]);
433
434 if (!pin || !strlen(pin)) {
435 fprintf(stderr, "failed to sanitize the pincode\n");
436 return -1;
437 }
438
439 p->pintype = htole32(MBIM_PIN_TYPE_PIN1);
440 p->pinoperation = htole32(MBIM_PIN_OPERATION_ENTER);
441 mbim_encode_string(&p->pin, _argv[0]);
442
443 return mbim_send_command_msg();
444 }
445
446 static int
447 mbim_config_request(void)
448 {
449 mbim_setup_command_msg(basic_connect,
450 MBIM_MESSAGE_COMMAND_TYPE_QUERY, MBIM_CMD_BASIC_CONNECT_IP_CONFIGURATION,
451 sizeof(struct mbim_basic_connect_ip_configuration_q));
452
453 return mbim_send_command_msg();
454 }
455
456 static int
457 mbim_radio_request(void)
458 {
459 if (_argc > 0) {
460 struct mbim_basic_connect_radio_state_s *rs =
461 (struct mbim_basic_connect_radio_state_s *) mbim_setup_command_msg(basic_connect,
462 MBIM_MESSAGE_COMMAND_TYPE_SET, MBIM_CMD_BASIC_CONNECT_RADIO_STATE,
463 sizeof(struct mbim_basic_connect_radio_state_r));
464
465 if (!strcmp(_argv[0], "off"))
466 rs->radiostate = htole32(MBIM_RADIO_SWITCH_STATE_OFF);
467 else
468 rs->radiostate = htole32(MBIM_RADIO_SWITCH_STATE_ON);
469 } else {
470 mbim_setup_command_msg(basic_connect,
471 MBIM_MESSAGE_COMMAND_TYPE_QUERY, MBIM_CMD_BASIC_CONNECT_RADIO_STATE,
472 sizeof(struct mbim_basic_connect_radio_state_r));
473 }
474 return mbim_send_command_msg();
475 }
476
477 static struct mbim_handler handlers[] = {
478 { "caps", 0, mbim_device_caps_request, mbim_device_caps_response },
479 { "pinstate", 0, mbim_pin_state_request, mbim_pin_state_response },
480 { "unlock", 1, mbim_pin_unlock_request, mbim_pin_state_response },
481 { "registration", 0, mbim_registration_request, mbim_registration_response },
482 { "subscriber", 0, mbim_subscriber_request, mbim_subscriber_response },
483 { "attach", 0, mbim_attach_request, mbim_attach_response },
484 { "detach", 0, mbim_detach_request, mbim_attach_response },
485 { "connect", 0, mbim_connect_request, mbim_connect_response },
486 { "disconnect", 0, mbim_disconnect_request, mbim_connect_response },
487 { "config", 0, mbim_config_request, mbim_config_response },
488 { "radio", 0, mbim_radio_request, mbim_radio_response },
489 };
490
491 static int
492 usage(void)
493 {
494 fprintf(stderr, "Usage: umbim <caps|pinstate|unlock|registration|subscriber|attach|detach|connect|disconnect|config|radio> [options]\n"
495 "Options:\n"
496 #ifdef LIBQMI_MBIM_PROXY
497 " -p use mbim-proxy\n"
498 #endif
499 " -d <device> the device (/dev/cdc-wdmX)\n"
500 " -t <transaction> the transaction id\n"
501 " -n no close\n\n"
502 " -v verbose\n\n");
503 return 1;
504 }
505
506 int
507 main(int argc, char **argv)
508 {
509 char *cmd, *device = NULL;
510 int no_open = 0, ch, i;
511 #ifdef LIBQMI_MBIM_PROXY
512 int proxy = 0;
513 #endif
514
515 while ((ch = getopt(argc, argv, "pnvd:t:")) != -1) {
516 switch (ch) {
517 case 'v':
518 verbose = 1;
519 break;
520 case 'n':
521 no_close = 1;
522 break;
523 case 'd':
524 device = optarg;
525 break;
526 case 't':
527 no_open = 1;
528 transaction_id = atoi(optarg);
529 break;
530 #ifdef LIBQMI_MBIM_PROXY
531 case 'p':
532 proxy = 1;
533 break;
534 #endif
535 default:
536 return usage();
537 }
538 }
539
540 if (!device || optind == argc)
541 return usage();
542
543 cmd = argv[optind];
544 optind++;
545
546 _argc = argc - optind;
547 _argv = &argv[optind];
548
549 for (i = 0; i < ARRAY_SIZE(handlers); i++)
550 if (!strcmp(cmd, handlers[i].name))
551 current_handler = &handlers[i];
552
553 if (!current_handler || (optind + current_handler->argc > argc))
554 return usage();
555
556 uloop_init();
557
558 #ifdef LIBQMI_MBIM_PROXY
559 if (proxy)
560 mbim_proxy_open(device);
561 else
562 #endif
563 mbim_open(device);
564 if (!no_open)
565 mbim_send_open_msg();
566 else if (current_handler->request() < 0)
567 return -1;
568
569 uloop_run();
570 uloop_done();
571
572 return return_code;
573 }