main.c revision 241c93e01b36f4644039cedacfbd80fdc0ff220d
1/* 2 * 3 * BlueZ - Bluetooth protocol stack for Linux 4 * 5 * Copyright (C) 2000-2001 Qualcomm Incorporated 6 * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com> 7 * Copyright (C) 2002-2009 Marcel Holtmann <marcel@holtmann.org> 8 * 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 * 24 */ 25 26#ifdef HAVE_CONFIG_H 27#include <config.h> 28#endif 29 30#include <stdio.h> 31#include <errno.h> 32#include <fcntl.h> 33#include <unistd.h> 34#include <stdlib.h> 35#include <string.h> 36#include <signal.h> 37#include <sys/stat.h> 38#include <sys/ioctl.h> 39#include <sys/socket.h> 40#include <sys/types.h> 41#include <sys/wait.h> 42 43#include <bluetooth/bluetooth.h> 44#include <bluetooth/hci.h> 45#include <bluetooth/hci_lib.h> 46 47#include <glib.h> 48 49#include <dbus/dbus.h> 50 51#include "logging.h" 52 53#include "hcid.h" 54#include "sdpd.h" 55#include "adapter.h" 56#include "dbus-hci.h" 57#include "dbus-common.h" 58#include "agent.h" 59#include "manager.h" 60#include "storage.h" 61 62#define HCID_DEFAULT_DISCOVERABLE_TIMEOUT 180 /* 3 minutes */ 63 64enum { 65 HCID_SET_NAME, 66 HCID_SET_CLASS, 67 HCID_SET_PAGETO, 68 HCID_SET_DISCOVTO, 69}; 70 71struct main_opts main_opts; 72 73static int child_pipe[2]; 74 75static gboolean starting = TRUE; 76 77static GKeyFile *load_config(const char *file) 78{ 79 GError *err = NULL; 80 GKeyFile *keyfile; 81 82 keyfile = g_key_file_new(); 83 84 g_key_file_set_list_separator(keyfile, ','); 85 86 if (!g_key_file_load_from_file(keyfile, file, 0, &err)) { 87 error("Parsing %s failed: %s", file, err->message); 88 g_error_free(err); 89 g_key_file_free(keyfile); 90 return NULL; 91 } 92 93 return keyfile; 94} 95 96static void parse_config(GKeyFile *config) 97{ 98 GError *err = NULL; 99 char *str; 100 int val; 101 gboolean boolean; 102 103 if (!config) 104 return; 105 106 debug("parsing main.conf"); 107 108 val = g_key_file_get_integer(config, "General", 109 "DiscoverableTimeout", &err); 110 if (err) { 111 debug("%s", err->message); 112 g_clear_error(&err); 113 } else { 114 debug("discovto=%d", val); 115 main_opts.discovto = val; 116 main_opts.flags |= 1 << HCID_SET_DISCOVTO; 117 } 118 119 val = g_key_file_get_integer(config, "General", 120 "PairableTimeout", &err); 121 if (err) { 122 debug("%s", err->message); 123 g_clear_error(&err); 124 } else { 125 debug("pairto=%d", val); 126 main_opts.pairto = val; 127 } 128 129 val = g_key_file_get_integer(config, "General", "PageTimeout", &err); 130 if (err) { 131 debug("%s", err->message); 132 g_clear_error(&err); 133 } else { 134 debug("pageto=%d", val); 135 main_opts.pageto = val; 136 main_opts.flags |= 1 << HCID_SET_PAGETO; 137 } 138 139 str = g_key_file_get_string(config, "General", "Name", &err); 140 if (err) { 141 debug("%s", err->message); 142 g_clear_error(&err); 143 } else { 144 debug("name=%s", str); 145 g_free(main_opts.name); 146 main_opts.name = g_strdup(str); 147 main_opts.flags |= 1 << HCID_SET_NAME; 148 g_free(str); 149 } 150 151 str = g_key_file_get_string(config, "General", "Class", &err); 152 if (err) { 153 debug("%s", err->message); 154 g_clear_error(&err); 155 } else { 156 debug("class=%s", str); 157 main_opts.class = strtol(str, NULL, 16); 158 main_opts.flags |= 1 << HCID_SET_CLASS; 159 g_free(str); 160 } 161 162 val = g_key_file_get_integer(config, "General", 163 "DiscoverSchedulerInterval", &err); 164 if (err) { 165 debug("%s", err->message); 166 g_clear_error(&err); 167 } else { 168 debug("inqmode=%d", val); 169 main_opts.inqmode = val; 170 } 171 172 boolean = g_key_file_get_boolean(config, "General", 173 "InitiallyPowered", &err); 174 if (err) { 175 debug("%s", err->message); 176 g_clear_error(&err); 177 } else if (boolean == FALSE) 178 main_opts.mode = MODE_OFF; 179 180 boolean = g_key_file_get_boolean(config, "General", 181 "RememberPowered", &err); 182 if (err) { 183 debug("%s", err->message); 184 g_clear_error(&err); 185 } else 186 main_opts.remember_powered = boolean; 187 188 str = g_key_file_get_string(config, "General", "DeviceID", &err); 189 if (err) { 190 debug("%s", err->message); 191 g_clear_error(&err); 192 } else { 193 debug("deviceid=%s", str); 194 strncpy(main_opts.deviceid, str, 195 sizeof(main_opts.deviceid) - 1); 196 g_free(str); 197 } 198 199 boolean = g_key_file_get_boolean(config, "General", 200 "ReverseServiceDiscovery", &err); 201 if (err) { 202 debug("%s", err->message); 203 g_clear_error(&err); 204 } else 205 main_opts.reverse_sdp = boolean; 206 207 main_opts.link_mode = HCI_LM_ACCEPT; 208 209 main_opts.link_policy = HCI_LP_RSWITCH | HCI_LP_SNIFF | 210 HCI_LP_HOLD | HCI_LP_PARK; 211} 212 213static void update_service_classes(const bdaddr_t *bdaddr, uint8_t value) 214{ 215 struct hci_dev_list_req *dl; 216 struct hci_dev_req *dr; 217 int i, sk; 218 219 sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); 220 if (sk < 0) 221 return; 222 223 dl = g_malloc0(HCI_MAX_DEV * sizeof(*dr) + sizeof(*dl)); 224 225 dl->dev_num = HCI_MAX_DEV; 226 dr = dl->dev_req; 227 228 if (ioctl(sk, HCIGETDEVLIST, dl) < 0) { 229 close(sk); 230 g_free(dl); 231 return; 232 } 233 234 dr = dl->dev_req; 235 236 for (i = 0; i < dl->dev_num; i++, dr++) { 237 struct hci_dev_info di; 238 239 if (hci_devinfo(dr->dev_id, &di) < 0) 240 continue; 241 242 if (hci_test_bit(HCI_RAW, &di.flags)) 243 continue; 244 245 if (!hci_test_bit(HCI_UP, &di.flags)) 246 continue; 247 248 if (bacmp(bdaddr, BDADDR_ANY) != 0 && 249 bacmp(bdaddr, &di.bdaddr) != 0) 250 continue; 251 252 manager_update_adapter(di.dev_id, value, starting); 253 } 254 255 g_free(dl); 256 257 close(sk); 258} 259 260/* 261 * Device name expansion 262 * %d - device id 263 */ 264static char *expand_name(char *dst, int size, char *str, int dev_id) 265{ 266 register int sp, np, olen; 267 char *opt, buf[10]; 268 269 if (!str && !dst) 270 return NULL; 271 272 sp = np = 0; 273 while (np < size - 1 && str[sp]) { 274 switch (str[sp]) { 275 case '%': 276 opt = NULL; 277 278 switch (str[sp+1]) { 279 case 'd': 280 sprintf(buf, "%d", dev_id); 281 opt = buf; 282 break; 283 284 case 'h': 285 opt = main_opts.host_name; 286 break; 287 288 case '%': 289 dst[np++] = str[sp++]; 290 /* fall through */ 291 default: 292 sp++; 293 continue; 294 } 295 296 if (opt) { 297 /* substitute */ 298 olen = strlen(opt); 299 if (np + olen < size - 1) 300 memcpy(dst + np, opt, olen); 301 np += olen; 302 } 303 sp += 2; 304 continue; 305 306 case '\\': 307 sp++; 308 /* fall through */ 309 default: 310 dst[np++] = str[sp++]; 311 break; 312 } 313 } 314 dst[np] = '\0'; 315 return dst; 316} 317 318static gboolean child_exit(GIOChannel *io, GIOCondition cond, void *user_data) 319{ 320 int status, fd = g_io_channel_unix_get_fd(io); 321 pid_t child_pid; 322 323 if (read(fd, &child_pid, sizeof(child_pid)) != sizeof(child_pid)) { 324 error("child_exit: unable to read child pid from pipe"); 325 return TRUE; 326 } 327 328 if (waitpid(child_pid, &status, 0) != child_pid) 329 error("waitpid(%d) failed", child_pid); 330 else 331 debug("child %d exited", child_pid); 332 333 return TRUE; 334} 335 336static void at_child_exit(void) 337{ 338 pid_t pid = getpid(); 339 340 if (write(child_pipe[1], &pid, sizeof(pid)) != sizeof(pid)) 341 error("unable to write to child pipe"); 342} 343 344static void configure_device(int dev_id) 345{ 346 struct hci_dev_info di; 347 uint16_t policy; 348 int dd; 349 350 if (hci_devinfo(dev_id, &di) < 0) 351 return; 352 353 if (hci_test_bit(HCI_RAW, &di.flags)) 354 return; 355 356 dd = hci_open_dev(dev_id); 357 if (dd < 0) { 358 error("Can't open device hci%d: %s (%d)", 359 dev_id, strerror(errno), errno); 360 return; 361 } 362 363 /* Set device name */ 364 if ((main_opts.flags & (1 << HCID_SET_NAME)) && main_opts.name) { 365 change_local_name_cp cp; 366 367 memset(cp.name, 0, sizeof(cp.name)); 368 expand_name((char *) cp.name, sizeof(cp.name), 369 main_opts.name, dev_id); 370 371 hci_send_cmd(dd, OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME, 372 CHANGE_LOCAL_NAME_CP_SIZE, &cp); 373 } 374 375 /* Set device class */ 376 if ((main_opts.flags & (1 << HCID_SET_CLASS))) { 377 write_class_of_dev_cp cp; 378 uint32_t class; 379 uint8_t cls[3]; 380 381 if (read_local_class(&di.bdaddr, cls) < 0) { 382 class = htobl(main_opts.class); 383 cls[2] = get_service_classes(&di.bdaddr); 384 memcpy(cp.dev_class, &class, 3); 385 } else { 386 if (!(main_opts.scan & SCAN_INQUIRY)) 387 cls[1] &= 0xdf; /* Clear discoverable bit */ 388 cls[2] = get_service_classes(&di.bdaddr); 389 memcpy(cp.dev_class, cls, 3); 390 } 391 392 hci_send_cmd(dd, OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV, 393 WRITE_CLASS_OF_DEV_CP_SIZE, &cp); 394 } 395 396 /* Set page timeout */ 397 if ((main_opts.flags & (1 << HCID_SET_PAGETO))) { 398 write_page_timeout_cp cp; 399 400 cp.timeout = htobs(main_opts.pageto); 401 hci_send_cmd(dd, OGF_HOST_CTL, OCF_WRITE_PAGE_TIMEOUT, 402 WRITE_PAGE_TIMEOUT_CP_SIZE, &cp); 403 } 404 405 /* Set default link policy */ 406 policy = htobs(main_opts.link_policy); 407 hci_send_cmd(dd, OGF_LINK_POLICY, 408 OCF_WRITE_DEFAULT_LINK_POLICY, 2, &policy); 409 410 hci_close_dev(dd); 411} 412 413static void init_device(int dev_id) 414{ 415 struct hci_dev_req dr; 416 struct hci_dev_info di; 417 pid_t pid; 418 int dd; 419 420 /* Do initialization in the separate process */ 421 pid = fork(); 422 switch (pid) { 423 case 0: 424 atexit(at_child_exit); 425 break; 426 case -1: 427 error("Fork failed. Can't init device hci%d: %s (%d)", 428 dev_id, strerror(errno), errno); 429 default: 430 debug("child %d forked", pid); 431 return; 432 } 433 434 dd = hci_open_dev(dev_id); 435 if (dd < 0) { 436 error("Can't open device hci%d: %s (%d)", 437 dev_id, strerror(errno), errno); 438 exit(1); 439 } 440 441 memset(&dr, 0, sizeof(dr)); 442 dr.dev_id = dev_id; 443 444 /* Set link mode */ 445 dr.dev_opt = main_opts.link_mode; 446 if (ioctl(dd, HCISETLINKMODE, (unsigned long) &dr) < 0) { 447 error("Can't set link mode on hci%d: %s (%d)", 448 dev_id, strerror(errno), errno); 449 } 450 451 /* Set link policy */ 452 dr.dev_opt = main_opts.link_policy; 453 if (ioctl(dd, HCISETLINKPOL, (unsigned long) &dr) < 0 && 454 errno != ENETDOWN) { 455 error("Can't set link policy on hci%d: %s (%d)", 456 dev_id, strerror(errno), errno); 457 } 458 459 /* Start HCI device */ 460 if (ioctl(dd, HCIDEVUP, dev_id) < 0 && errno != EALREADY) { 461 error("Can't init device hci%d: %s (%d)", 462 dev_id, strerror(errno), errno); 463 goto fail; 464 } 465 466 if (hci_devinfo(dev_id, &di) < 0) 467 goto fail; 468 469 if (hci_test_bit(HCI_RAW, &di.flags)) 470 goto done; 471 472done: 473 hci_close_dev(dd); 474 exit(0); 475 476fail: 477 hci_close_dev(dd); 478 exit(1); 479} 480 481static void device_devreg_setup(int dev_id, gboolean devup) 482{ 483 struct hci_dev_info di; 484 485 init_device(dev_id); 486 487 memset(&di, 0, sizeof(di)); 488 489 if (hci_devinfo(dev_id, &di) < 0) 490 return; 491 492 if (!hci_test_bit(HCI_RAW, &di.flags)) 493 manager_register_adapter(dev_id, devup); 494} 495 496static void device_devup_setup(int dev_id) 497{ 498 configure_device(dev_id); 499 500 start_security_manager(dev_id); 501 502 /* Return value 1 means ioctl(DEVDOWN) was performed */ 503 if (manager_start_adapter(dev_id) == 1) 504 stop_security_manager(dev_id); 505} 506 507static void init_all_devices(int ctl) 508{ 509 struct hci_dev_list_req *dl; 510 struct hci_dev_req *dr; 511 int i; 512 513 dl = g_try_malloc0(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t)); 514 if (!dl) { 515 info("Can't allocate devlist buffer: %s (%d)", 516 strerror(errno), errno); 517 exit(1); 518 } 519 520 dl->dev_num = HCI_MAX_DEV; 521 dr = dl->dev_req; 522 523 if (ioctl(ctl, HCIGETDEVLIST, (void *) dl) < 0) { 524 info("Can't get device list: %s (%d)", 525 strerror(errno), errno); 526 exit(1); 527 } 528 529 for (i = 0; i < dl->dev_num; i++, dr++) { 530 gboolean devup; 531 532 devup = hci_test_bit(HCI_UP, &dr->dev_opt); 533 534 info("HCI dev %d registered", dr->dev_id); 535 device_devreg_setup(dr->dev_id, devup); 536 if (devup) { 537 info("HCI dev %d already up", dr->dev_id); 538 device_devup_setup(dr->dev_id); 539 } 540 } 541 542 g_free(dl); 543} 544 545static void init_defaults(void) 546{ 547 /* Default HCId settings */ 548 memset(&main_opts, 0, sizeof(main_opts)); 549 main_opts.scan = SCAN_PAGE; 550 main_opts.mode = MODE_CONNECTABLE; 551 main_opts.name = g_strdup("BlueZ"); 552 main_opts.discovto = HCID_DEFAULT_DISCOVERABLE_TIMEOUT; 553 main_opts.remember_powered = TRUE; 554 main_opts.reverse_sdp = TRUE; 555 556 if (gethostname(main_opts.host_name, sizeof(main_opts.host_name) - 1) < 0) 557 strcpy(main_opts.host_name, "noname"); 558} 559 560static inline void device_event(GIOChannel *chan, evt_stack_internal *si) 561{ 562 evt_si_device *sd = (void *) &si->data; 563 564 switch (sd->event) { 565 case HCI_DEV_REG: 566 info("HCI dev %d registered", sd->dev_id); 567 device_devreg_setup(sd->dev_id, FALSE); 568 break; 569 570 case HCI_DEV_UNREG: 571 info("HCI dev %d unregistered", sd->dev_id); 572 manager_unregister_adapter(sd->dev_id); 573 break; 574 575 case HCI_DEV_UP: 576 info("HCI dev %d up", sd->dev_id); 577 device_devup_setup(sd->dev_id); 578 break; 579 580 case HCI_DEV_DOWN: 581 info("HCI dev %d down", sd->dev_id); 582 manager_stop_adapter(sd->dev_id); 583 stop_security_manager(sd->dev_id); 584 break; 585 } 586} 587 588static gboolean io_stack_event(GIOChannel *chan, GIOCondition cond, 589 gpointer data) 590{ 591 unsigned char buf[HCI_MAX_FRAME_SIZE], *ptr; 592 evt_stack_internal *si; 593 hci_event_hdr *eh; 594 int type; 595 size_t len; 596 GIOError err; 597 598 ptr = buf; 599 600 err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len); 601 if (err) { 602 if (err == G_IO_ERROR_AGAIN) 603 return TRUE; 604 605 error("Read from control socket failed: %s (%d)", 606 strerror(errno), errno); 607 return FALSE; 608 } 609 610 type = *ptr++; 611 612 if (type != HCI_EVENT_PKT) 613 return TRUE; 614 615 eh = (hci_event_hdr *) ptr; 616 if (eh->evt != EVT_STACK_INTERNAL) 617 return TRUE; 618 619 ptr += HCI_EVENT_HDR_SIZE; 620 621 si = (evt_stack_internal *) ptr; 622 switch (si->type) { 623 case EVT_SI_DEVICE: 624 device_event(chan, si); 625 break; 626 } 627 628 return TRUE; 629} 630 631static GMainLoop *event_loop; 632 633static void sig_term(int sig) 634{ 635 g_main_loop_quit(event_loop); 636} 637 638static void sig_debug(int sig) 639{ 640 toggle_debug(); 641} 642 643static gboolean option_detach = TRUE; 644static gboolean option_debug = FALSE; 645 646static GOptionEntry options[] = { 647 { "nodaemon", 'n', G_OPTION_FLAG_REVERSE, 648 G_OPTION_ARG_NONE, &option_detach, 649 "Don't run as daemon in background" }, 650 { "debug", 'd', 0, G_OPTION_ARG_NONE, &option_debug, 651 "Enable debug information output" }, 652 { NULL }, 653}; 654 655int main(int argc, char *argv[]) 656{ 657 GOptionContext *context; 658 GError *err = NULL; 659 struct sockaddr_hci addr; 660 struct hci_filter flt; 661 struct sigaction sa; 662 GIOChannel *ctl_io, *child_io; 663 uint16_t mtu = 0; 664 GKeyFile *config; 665 666 init_defaults(); 667 668 context = g_option_context_new(NULL); 669 g_option_context_add_main_entries(context, options, NULL); 670 671 if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) { 672 if (err != NULL) { 673 g_printerr("%s\n", err->message); 674 g_error_free(err); 675 } else 676 g_printerr("An unknown error occurred\n"); 677 exit(1); 678 } 679 680 g_option_context_free(context); 681 682 if (option_detach == TRUE) { 683 if (daemon(0, 0)) { 684 perror("Can't start daemon"); 685 exit(1); 686 } 687 } 688 689 umask(0077); 690 691 start_logging("bluetoothd", "Bluetooth daemon %s", VERSION); 692 693 memset(&sa, 0, sizeof(sa)); 694 sa.sa_flags = SA_NOCLDSTOP; 695 sa.sa_handler = sig_term; 696 sigaction(SIGTERM, &sa, NULL); 697 sigaction(SIGINT, &sa, NULL); 698 699 sa.sa_handler = sig_debug; 700 sigaction(SIGUSR2, &sa, NULL); 701 702 sa.sa_handler = SIG_IGN; 703 sigaction(SIGPIPE, &sa, NULL); 704 705 if (option_debug == TRUE) { 706 info("Enabling debug information"); 707 enable_debug(); 708 } 709 710 /* Create and bind HCI socket */ 711 main_opts.sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); 712 if (main_opts.sock < 0) { 713 error("Can't open HCI socket: %s (%d)", strerror(errno), 714 errno); 715 exit(1); 716 } 717 718 /* Set filter */ 719 hci_filter_clear(&flt); 720 hci_filter_set_ptype(HCI_EVENT_PKT, &flt); 721 hci_filter_set_event(EVT_STACK_INTERNAL, &flt); 722 if (setsockopt(main_opts.sock, SOL_HCI, HCI_FILTER, &flt, 723 sizeof(flt)) < 0) { 724 error("Can't set filter: %s (%d)", strerror(errno), errno); 725 exit(1); 726 } 727 728 memset(&addr, 0, sizeof(addr)); 729 addr.hci_family = AF_BLUETOOTH; 730 addr.hci_dev = HCI_DEV_NONE; 731 if (bind(main_opts.sock, (struct sockaddr *) &addr, 732 sizeof(addr)) < 0) { 733 error("Can't bind HCI socket: %s (%d)", 734 strerror(errno), errno); 735 exit(1); 736 } 737 738 config = load_config(CONFIGDIR "/main.conf"); 739 740 parse_config(config); 741 742 if (pipe(child_pipe) < 0) { 743 error("pipe(): %s (%d)", strerror(errno), errno); 744 exit(1); 745 } 746 747 child_io = g_io_channel_unix_new(child_pipe[0]); 748 g_io_channel_set_close_on_unref(child_io, TRUE); 749 g_io_add_watch(child_io, 750 G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, 751 child_exit, NULL); 752 g_io_channel_unref(child_io); 753 754 agent_init(); 755 756 if (hcid_dbus_init() < 0) { 757 error("Unable to get on D-Bus"); 758 exit(1); 759 } 760 761 start_sdp_server(mtu, main_opts.deviceid, SDP_SERVER_COMPAT); 762 set_service_classes_callback(update_service_classes); 763 764 /* Loading plugins has to be done after D-Bus has been setup since 765 * the plugins might wanna expose some paths on the bus. However the 766 * best order of how to init various subsystems of the Bluetooth 767 * daemon needs to be re-worked. */ 768 plugin_init(config); 769 770 event_loop = g_main_loop_new(NULL, FALSE); 771 772 ctl_io = g_io_channel_unix_new(main_opts.sock); 773 g_io_channel_set_close_on_unref(ctl_io, TRUE); 774 775 g_io_add_watch(ctl_io, G_IO_IN, io_stack_event, NULL); 776 777 g_io_channel_unref(ctl_io); 778 779 /* Initialize already connected devices */ 780 init_all_devices(main_opts.sock); 781 782 starting = FALSE; 783 784 manager_startup_complete(); 785 786 debug("Entering main loop"); 787 788 g_main_loop_run(event_loop); 789 790 hcid_dbus_unregister(); 791 792 hcid_dbus_exit(); 793 794 plugin_cleanup(); 795 796 stop_sdp_server(); 797 798 agent_exit(); 799 800 g_main_loop_unref(event_loop); 801 802 if (config) 803 g_key_file_free(config); 804 805 info("Exit"); 806 807 stop_logging(); 808 809 return 0; 810} 811