Example #1
0
int mesh_init(void) {
    uint16_t mesh_listen_port = \
                                (uint16_t)config_get_option_value("listen.mesh_port")->integer;

    if (mesh_listen_port == 0) {
        log_info("Mesh support is disabled");

        return 0;
    }

    log_info("Initializing mesh subsystem");

    if (mesh_start_listening(&mesh_listen_socket,
                             mesh_listen_port,
                             socket_create_allocated) >= 0) {
        is_mesh_listen_socket_open = true;
    }

    if (!is_mesh_listen_socket_open) {
        log_error("Could not open mesh listen socket");

        return -1;
    }

    // Create mesh stack array.
    if (array_create(&mesh_stacks, MAX_MESH_STACKS, sizeof(MeshStack), false) < 0) {
        log_error("Could not create mesh stack array: %s (%d)",
                  get_errno_name(errno), errno);

        return -1;
    }

    return 0;
}
Example #2
0
static void client_handle_authenticate_request(Client *client,
        AuthenticateRequest *request) {
    uint32_t nonces[2];
    uint8_t digest[SHA1_DIGEST_LENGTH];
    const char *secret;
    char packet_signature[PACKET_MAX_SIGNATURE_LENGTH];
    AuthenticateResponse response;

    if (client->authentication_state == CLIENT_AUTHENTICATION_STATE_DISABLED) {
        log_error("Client ("CLIENT_SIGNATURE_FORMAT") tries to authenticate, but authentication is disabled, disconnecting client",
                  client_expand_signature(client));

        client->disconnected = true;

        return;
    }

    if (client->authentication_state != CLIENT_AUTHENTICATION_STATE_NONCE_SEND) {
        log_error("Client ("CLIENT_SIGNATURE_FORMAT") performed invalid authentication sequence (%s -> %s), disconnecting client",
                  client_expand_signature(client),
                  client_get_authentication_state_name(client->authentication_state),
                  client_get_authentication_state_name(CLIENT_AUTHENTICATION_STATE_DONE));

        client->disconnected = true;

        return;
    }

    memcpy(&nonces[0], &client->authentication_nonce, sizeof(client->authentication_nonce));
    memcpy(&nonces[1], request->client_nonce, sizeof(request->client_nonce));

    secret = config_get_option_value("authentication.secret")->string;

    hmac_sha1((uint8_t *)secret, strlen(secret),
              (uint8_t *)nonces, sizeof(nonces), digest);

    if (memcmp(request->digest, digest, SHA1_DIGEST_LENGTH) != 0) {
        log_error("Authenticate request (%s) from client ("CLIENT_SIGNATURE_FORMAT") did not contain the expected data, disconnecting client",
                  packet_get_request_signature(packet_signature, (Packet *)request),
                  client_expand_signature(client));

        client->disconnected = true;

        return;
    }

    client->authentication_state = CLIENT_AUTHENTICATION_STATE_DONE;

    log_info("Client ("CLIENT_SIGNATURE_FORMAT") successfully finished authentication",
             client_expand_signature(client));

    if (packet_header_get_response_expected(&request->header)) {
        response.header = request->header;
        response.header.length = sizeof(response);

        packet_header_set_error_code(&response.header, PACKET_E_SUCCESS);

        client_dispatch_response(client, NULL, (Packet *)&response, false, false);
    }
}
Example #3
0
static const char *network_get_address_family_name(int family, bool report_dual_stack) {
    switch (family) {
    case AF_INET:
        return "IPv4";

    case AF_INET6:
        if (report_dual_stack && config_get_option_value("listen.dual_stack")->boolean) {
            return "IPv6 dual-stack";
        } else {
            return "IPv6";
        }

    default:
        return "<unknown>";
    }
}
Example #4
0
int client_create(Client *client, const char *name, IO *io,
                  uint32_t authentication_nonce,
                  ClientDestroyDoneFunction destroy_done) {
    log_debug("Creating client from %s (handle: %d)", io->type, io->handle);

    string_copy(client->name, sizeof(client->name), name);

    client->io = io;
    client->disconnected = false;
    client->request_used = 0;
    client->request_header_checked = false;
    client->pending_request_count = 0;
    client->authentication_state = CLIENT_AUTHENTICATION_STATE_DISABLED;
    client->authentication_nonce = authentication_nonce;
    client->destroy_done = destroy_done;

    if (config_get_option_value("authentication.secret")->string != NULL) {
        client->authentication_state = CLIENT_AUTHENTICATION_STATE_ENABLED;
    }

    node_reset(&client->pending_request_sentinel);

    // create response writer
    if (writer_create(&client->response_writer, client->io,
                      "response", packet_get_response_signature,
                      "client", client_get_recipient_signature,
                      client_recipient_disconnect, client) < 0) {
        log_error("Could not create response writer: %s (%d)",
                  get_errno_name(errno), errno);

        return -1;
    }

    // add I/O object as event source
    return event_add_source(client->io->handle, EVENT_SOURCE_TYPE_GENERIC,
                            EVENT_READ, client_handle_read, client);
}
Example #5
0
int mesh_start_listening(Socket *mesh_listen_socket,
                         uint16_t mesh_listen_socket_port,
                         SocketCreateAllocatedFunction create_allocated) {
    int phase = 0;
    struct addrinfo *resolved_address = NULL;
    const char *address = config_get_option_value("listen.address")->string;

    log_info("Opening mesh listen socket (P: %u)", mesh_listen_socket_port);

    resolved_address = socket_hostname_to_address(address, mesh_listen_socket_port);

    // Currently no IPv6 support for mesh.
    if(resolved_address->ai_family == AF_INET6) {
        log_error("Mesh gateway does not support IPv6");

        goto CLEANUP;
    }

    if (resolved_address == NULL) {
        log_error("Could not resolve mesh listen address '%s' (P: %u): %s (%d)",
                  address,
                  mesh_listen_socket_port,
                  get_errno_name(errno),
                  errno);

        goto CLEANUP;
    }

    phase = 1;

    // Create socket.
    if (socket_create(mesh_listen_socket) < 0) {
        log_error("Could not create mesh listen socket: %s (%d)",
                  get_errno_name(errno),
                  errno);

        goto CLEANUP;
    }

    phase = 2;

    if (socket_open(mesh_listen_socket,
                    resolved_address->ai_family,
                    resolved_address->ai_socktype,
                    resolved_address->ai_protocol) < 0) {
        log_error("Could not open %s mesh listen socket: %s (%d)",
                  network_get_address_family_name(resolved_address->ai_family, false),
                  get_errno_name(errno),
                  errno);

        goto CLEANUP;
    }

#ifndef _WIN32
    /*
     * On Unix the SO_REUSEADDR socket option allows to rebind sockets in
     * CLOSE-WAIT state. this is a desired effect. On Windows SO_REUSEADDR
     * allows to rebind sockets in any state. This is dangerous. Therefore,
     * don't set SO_REUSEADDR on Windows. Sockets can be rebound in CLOSE-WAIT
     * state on Windows by default.
     */
    if (socket_set_address_reuse(mesh_listen_socket, true) < 0) {
        log_error("Could not enable address-reuse mode for mesh listen socket: %s (%d)",
                  get_errno_name(errno),
                  errno);

        goto CLEANUP;
    }
#endif

    // Bind socket and start to listen.
    if (socket_bind(mesh_listen_socket,
                    resolved_address->ai_addr,
                    resolved_address->ai_addrlen) < 0) {
        log_error("Could not bind %s mesh listen socket to (A: %s, P: %u): %s (%d)",
                  network_get_address_family_name(resolved_address->ai_family, true),
                  address,
                  mesh_listen_socket_port,
                  get_errno_name(errno),
                  errno);

        goto CLEANUP;
    }

    if (socket_listen(mesh_listen_socket, 10, create_allocated) < 0) {
        log_error("Could not listen to %s mesh socket (A: %s, P: %u): %s (%d)",
                  network_get_address_family_name(resolved_address->ai_family, true),
                  address,
                  mesh_listen_socket_port,
                  get_errno_name(errno),
                  errno);

        goto CLEANUP;
    }

    log_info("Mesh gateway started listening on (A: %s, P: %u, F: %s)",
             address,
             mesh_listen_socket_port,
             network_get_address_family_name(resolved_address->ai_family, true));

    if(event_add_source(mesh_listen_socket->base.handle,
                        EVENT_SOURCE_TYPE_GENERIC,
                        EVENT_READ,
                        mesh_handle_accept,
                        mesh_listen_socket) < 0) {
        goto CLEANUP;
    }

    phase = 3;

    freeaddrinfo(resolved_address);

CLEANUP:
    switch(phase) { // No breaks, all cases fall through intentionally.
    case 2:
        socket_destroy(mesh_listen_socket);

    case 1:
        freeaddrinfo(resolved_address);

    default:
        break;
    }

    return phase == 3 ? 0 : -1;
}
Example #6
0
int main(int argc, char **argv) {
	int exit_code = EXIT_FAILURE;
	int i;
	bool help = false;
	bool version = false;
	bool check_config = false;
	bool daemon = false;
	const char *debug_filter = NULL;
	int pid_fd = -1;
#ifdef BRICKD_WITH_LIBUDEV
	bool initialized_udev = false;
#endif

	for (i = 1; i < argc; ++i) {
		if (strcmp(argv[i], "--help") == 0) {
			help = true;
		} else if (strcmp(argv[i], "--version") == 0) {
			version = true;
		} else if (strcmp(argv[i], "--check-config") == 0) {
			check_config = true;
		} else if (strcmp(argv[i], "--daemon") == 0) {
			daemon = true;
		} else if (strcmp(argv[i], "--debug") == 0) {
			if (i + 1 < argc && strncmp(argv[i + 1], "--", 2) != 0) {
				debug_filter = argv[++i];
			} else {
				debug_filter = "";
			}
		} else {
			fprintf(stderr, "Unknown option '%s'\n\n", argv[i]);
			print_usage();

			return EXIT_FAILURE;
		}
	}

	if (help) {
		print_usage();

		return EXIT_SUCCESS;
	}

	if (version) {
		printf("%s\n", VERSION_STRING);

		return EXIT_SUCCESS;
	}

	if (prepare_paths() < 0) {
		return EXIT_FAILURE;
	}

	if (check_config) {
		return config_check(_config_filename) < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
	}

	config_init(_config_filename);

	if (config_has_error()) {
		fprintf(stderr, "Error(s) occurred while reading config file '%s'\n",
		        _config_filename);

		goto error_config;
	}

	log_init();

	if (daemon) {
		pid_fd = daemon_start(_log_filename, &_log_file, _pid_filename, true);
	} else {
		pid_fd = pid_file_acquire(_pid_filename, getpid());

		if (pid_fd == PID_FILE_ALREADY_ACQUIRED) {
			fprintf(stderr, "Already running according to '%s'\n", _pid_filename);
		}
	}

	if (pid_fd < 0) {
		goto error_pid_file;
	}

	log_info("Brick Daemon %s started (pid: %u, daemonized: %d)",
	         VERSION_STRING, getpid(), daemon ? 1 : 0);

	if (debug_filter != NULL) {
		log_enable_debug_override(debug_filter);
	}

	if (config_has_warning()) {
		log_warn("Warning(s) in config file '%s', run with --check-config option for details",
		         _config_filename);
	}

	if (event_init() < 0) {
		goto error_event;
	}

	if (signal_init(handle_sighup, handle_sigusr1) < 0) {
		goto error_signal;
	}

	if (hardware_init() < 0) {
		goto error_hardware;
	}

	if (usb_init() < 0) {
		goto error_usb;
	}

#ifdef BRICKD_WITH_LIBUDEV
	if (!usb_has_hotplug()) {
		if (udev_init() < 0) {
			goto error_udev;
		}

		initialized_udev = true;
	}
#endif

	if (network_init() < 0) {
		goto error_network;
	}

	if (mesh_init() < 0) {
		goto error_mesh;
	}

#ifdef BRICKD_WITH_RED_BRICK
	if (gpio_init() < 0) {
		goto error_gpio;
	}

	if (redapid_init() < 0) {
		goto error_redapid;
	}

	if (red_stack_init() < 0) {
		goto error_red_stack;
	}

	if (red_extension_init() < 0) {
		goto error_red_extension;
	}

	if (red_usb_gadget_init() < 0) {
		goto error_red_usb_gadget;
	}

	red_led_set_trigger(RED_LED_GREEN, config_get_option_value("led_trigger.green")->symbol);
	red_led_set_trigger(RED_LED_RED, config_get_option_value("led_trigger.red")->symbol);
#endif

	if (event_run(network_cleanup_clients_and_zombies) < 0) {
		goto error_run;
	}

#ifdef BRICKD_WITH_RED_BRICK
	hardware_announce_disconnect();
	network_announce_red_brick_disconnect();
	red_usb_gadget_announce_red_brick_disconnect();
#endif

	exit_code = EXIT_SUCCESS;

error_run:
#ifdef BRICKD_WITH_RED_BRICK
	red_usb_gadget_exit();

error_red_usb_gadget:
	red_extension_exit();

error_red_extension:
	red_stack_exit();

error_red_stack:
	redapid_exit();

error_redapid:
	//gpio_exit();

error_gpio:
#endif
	network_exit();

error_mesh:
	mesh_exit();

error_network:
#ifdef BRICKD_WITH_LIBUDEV
	if (initialized_udev) {
		udev_exit();
	}

error_udev:
#endif
	usb_exit();

error_usb:
	hardware_exit();

error_hardware:
	signal_exit();

error_signal:
	event_exit();

error_event:
	log_info("Brick Daemon %s stopped", VERSION_STRING);

error_pid_file:
	if (pid_fd >= 0) {
		pid_file_release(_pid_filename, pid_fd);
	}

	log_exit();

error_config:
	config_exit();

	return exit_code;
}
Example #7
0
int network_init(void) {
	uint16_t plain_port = (uint16_t)config_get_option_value("listen.plain_port")->integer;
	uint16_t websocket_port = (uint16_t)config_get_option_value("listen.websocket_port")->integer;

	log_debug("Initializing network subsystem");

	node_reset(&_pending_request_sentinel);

	if (config_get_option_value("authentication.secret")->string != NULL) {
		log_info("Authentication is enabled");

		_next_authentication_nonce = get_random_uint32();
	}

	// create client array. the Client struct is not relocatable, because a
	// pointer to it is passed as opaque parameter to the event subsystem
	if (array_create(&_clients, 32, sizeof(Client), false) < 0) {
		log_error("Could not create client array: %s (%d)",
		          get_errno_name(errno), errno);

		return -1;
	}

	// create zombie array. the Zombie struct is not relocatable, because a
	// pointer to it is passed as opaque parameter to its timer object
	if (array_create(&_zombies, 32, sizeof(Zombie), false) < 0) {
		log_error("Could not create zombie array: %s (%d)",
		          get_errno_name(errno), errno);

		array_destroy(&_clients, (ItemDestroyFunction)client_destroy);

		return -1;
	}

	if (network_open_server_socket(&_plain_server_socket, plain_port,
	                               socket_create_allocated) >= 0) {
		_plain_server_socket_open = true;
	}

	if (websocket_port != 0) {
		if (config_get_option_value("authentication.secret")->string == NULL) {
			log_warn("WebSocket support is enabled without authentication");
		}

		if (network_open_server_socket(&_websocket_server_socket, websocket_port,
		                               websocket_create_allocated) >= 0) {
			_websocket_server_socket_open = true;
		}
	}

	if (!_plain_server_socket_open && !_websocket_server_socket_open) {
		log_error("Could not open any socket to listen to");

		array_destroy(&_zombies, (ItemDestroyFunction)zombie_destroy);
		array_destroy(&_clients, (ItemDestroyFunction)client_destroy);

		return -1;
	}

	return 0;
}
Example #8
0
static int network_open_server_socket(Socket *server_socket, uint16_t port,
                                      SocketCreateAllocatedFunction create_allocated) {
	int phase = 0;
	const char *address = config_get_option_value("listen.address")->string;
	struct addrinfo *resolved_address = NULL;
	bool dual_stack;

	log_debug("Opening server socket on port %u", port);

	// resolve listen address
	// FIXME: bind to all returned addresses, instead of just the first one.
	//        requires special handling if IPv4 and IPv6 addresses are returned
	//        and dual-stack mode is enabled
	resolved_address = socket_hostname_to_address(address, port);

	if (resolved_address == NULL) {
		log_error("Could not resolve listen address '%s' (port: %u): %s (%d)",
		          address, port, get_errno_name(errno), errno);

		goto cleanup;
	}

	phase = 1;

	// create socket
	if (socket_create(server_socket) < 0) {
		log_error("Could not create socket: %s (%d)",
		          get_errno_name(errno), errno);

		goto cleanup;
	}

	phase = 2;

	if (socket_open(server_socket, resolved_address->ai_family,
	                resolved_address->ai_socktype, resolved_address->ai_protocol) < 0) {
		log_error("Could not open %s server socket: %s (%d)",
		          network_get_address_family_name(resolved_address->ai_family, false),
		          get_errno_name(errno), errno);

		goto cleanup;
	}

	if (resolved_address->ai_family == AF_INET6) {
		dual_stack = config_get_option_value("listen.dual_stack")->boolean;

		if (socket_set_dual_stack(server_socket, dual_stack) < 0) {
			log_error("Could not %s dual-stack mode for IPv6 server socket: %s (%d)",
			          dual_stack ? "enable" : "disable",
			          get_errno_name(errno), errno);

			goto cleanup;
		}
	}

#ifndef _WIN32
	// on Unix the SO_REUSEADDR socket option allows to rebind sockets in
	// CLOSE-WAIT state. this is a desired effect. on Windows SO_REUSEADDR
	// allows to rebind sockets in any state. this is dangerous. therefore,
	// don't set SO_REUSEADDR on Windows. sockets can be rebound in CLOSE-WAIT
	// state on Windows by default.
	if (socket_set_address_reuse(server_socket, true) < 0) {
		log_error("Could not enable address-reuse mode for server socket: %s (%d)",
		          get_errno_name(errno), errno);

		goto cleanup;
	}
#endif

	// bind socket and start to listen
	if (socket_bind(server_socket, resolved_address->ai_addr,
	                resolved_address->ai_addrlen) < 0) {
		log_error("Could not bind %s server socket to '%s' on port %u: %s (%d)",
		          network_get_address_family_name(resolved_address->ai_family, true),
		          address, port, get_errno_name(errno), errno);

		goto cleanup;
	}

	if (socket_listen(server_socket, 10, create_allocated) < 0) {
		log_error("Could not listen to %s server socket bound to '%s' on port %u: %s (%d)",
		          network_get_address_family_name(resolved_address->ai_family, true),
		          address, port, get_errno_name(errno), errno);

		goto cleanup;
	}

	log_debug("Started listening to '%s' (%s) on port %u",
	          address,
	          network_get_address_family_name(resolved_address->ai_family, true),
	          port);

	if (event_add_source(server_socket->base.handle, EVENT_SOURCE_TYPE_GENERIC,
	                     EVENT_READ, network_handle_accept, server_socket) < 0) {
		goto cleanup;
	}

	phase = 3;

	freeaddrinfo(resolved_address);

cleanup:
	switch (phase) { // no breaks, all cases fall through intentionally
	case 2:
		socket_destroy(server_socket);

	case 1:
		freeaddrinfo(resolved_address);

	default:
		break;
	}

	return phase == 3 ? 0 : -1;
}
Example #9
0
int main(int argc, char **argv) {
	int exit_code = EXIT_FAILURE;
	int i;
	bool help = false;
	bool version = false;
	bool check_config = false;
	bool daemon = false;
	bool debug = false;
	bool libusb_debug = false;
	int pid_fd = -1;
#ifdef BRICKD_WITH_LIBUDEV
	bool initialized_udev = false;
#endif

	for (i = 1; i < argc; ++i) {
		if (strcmp(argv[i], "--help") == 0) {
			help = true;
		} else if (strcmp(argv[i], "--version") == 0) {
			version = true;
		} else if (strcmp(argv[i], "--check-config") == 0) {
			check_config = true;
		} else if (strcmp(argv[i], "--daemon") == 0) {
			daemon = true;
		} else if (strcmp(argv[i], "--debug") == 0) {
			debug = true;
		} else if (strcmp(argv[i], "--libusb-debug") == 0) {
			libusb_debug = true;
		} else {
			fprintf(stderr, "Unknown option '%s'\n\n", argv[i]);
			print_usage();

			return EXIT_FAILURE;
		}
	}

	if (help) {
		print_usage();

		return EXIT_SUCCESS;
	}

	if (version) {
		printf("%s\n", VERSION_STRING);

		return EXIT_SUCCESS;
	}

	if (prepare_paths() < 0) {
		return EXIT_FAILURE;
	}

	if (check_config) {
		return config_check(_config_filename) < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
	}

	config_init(_config_filename);

	log_init();

	if (daemon) {
		pid_fd = daemon_start(_log_filename, _pid_filename, true);
	} else {
		pid_fd = pid_file_acquire(_pid_filename, getpid());

		if (pid_fd == PID_FILE_ALREADY_ACQUIRED) {
			fprintf(stderr, "Already running according to '%s'\n", _pid_filename);
		}
	}

	if (pid_fd < 0) {
		goto error_log;
	}

	log_set_debug_override(debug);

	log_set_level(LOG_CATEGORY_EVENT, config_get_option_value("log_level.event")->log_level);
	log_set_level(LOG_CATEGORY_USB, config_get_option_value("log_level.usb")->log_level);
	log_set_level(LOG_CATEGORY_NETWORK, config_get_option_value("log_level.network")->log_level);
	log_set_level(LOG_CATEGORY_HOTPLUG, config_get_option_value("log_level.hotplug")->log_level);
	log_set_level(LOG_CATEGORY_HARDWARE, config_get_option_value("log_level.hardware")->log_level);
	log_set_level(LOG_CATEGORY_WEBSOCKET, config_get_option_value("log_level.websocket")->log_level);
#ifdef BRICKD_WITH_RED_BRICK
	log_set_level(LOG_CATEGORY_RED_BRICK, config_get_option_value("log_level.red_brick")->log_level);
	log_set_level(LOG_CATEGORY_SPI, config_get_option_value("log_level.spi")->log_level);
	log_set_level(LOG_CATEGORY_RS485, config_get_option_value("log_level.rs485")->log_level);
#endif
	log_set_level(LOG_CATEGORY_OTHER, config_get_option_value("log_level.other")->log_level);

	if (config_has_error()) {
		log_error("Error(s) occurred while reading config file '%s'",
		          _config_filename);

		goto error_config;
	}

	if (daemon) {
		log_info("Brick Daemon %s started (daemonized)", VERSION_STRING);
	} else {
		log_info("Brick Daemon %s started", VERSION_STRING);
	}

	if (config_has_warning()) {
		log_error("Warning(s) in config file '%s', run with --check-config option for details",
		          _config_filename);
	}

	if (event_init() < 0) {
		goto error_event;
	}

	if (signal_init(handle_sigusr1) < 0) {
		goto error_signal;
	}

	if (hardware_init() < 0) {
		goto error_hardware;
	}

	if (usb_init(libusb_debug) < 0) {
		goto error_usb;
	}

#ifdef BRICKD_WITH_LIBUDEV
	if (!usb_has_hotplug()) {
		if (udev_init() < 0) {
			goto error_udev;
		}

		initialized_udev = true;
	}
#endif

	if (network_init() < 0) {
		goto error_network;
	}

#ifdef BRICKD_WITH_RED_BRICK
	if (gpio_init() < 0) {
		goto error_gpio;
	}

	if (redapid_init() < 0) {
		goto error_redapid;
	}

	if (red_stack_init() < 0) {
		goto error_red_stack;
	}

	if (red_extension_init() < 0) {
		goto error_red_extension;
	}

	if (red_usb_gadget_init() < 0) {
		goto error_red_usb_gadget;
	}

	red_led_set_trigger(RED_LED_GREEN, config_get_option_value("led_trigger.green")->red_led_trigger);
	red_led_set_trigger(RED_LED_RED, config_get_option_value("led_trigger.red")->red_led_trigger);
#endif

	if (event_run(network_cleanup_clients_and_zombies) < 0) {
		goto error_run;
	}

#ifdef BRICKD_WITH_RED_BRICK
	hardware_announce_disconnect();
	network_announce_red_brick_disconnect();
	red_usb_gadget_announce_red_brick_disconnect();
#endif

	exit_code = EXIT_SUCCESS;

error_run:
#ifdef BRICKD_WITH_RED_BRICK
	red_usb_gadget_exit();

error_red_usb_gadget:
	red_extension_exit();

error_red_extension:
	red_stack_exit();

error_red_stack:
	redapid_exit();

error_redapid:
	//gpio_exit();

error_gpio:
#endif
	network_exit();

error_network:
#ifdef BRICKD_WITH_LIBUDEV
	if (initialized_udev) {
		udev_exit();
	}

error_udev:
#endif
	usb_exit();

error_usb:
	hardware_exit();

error_hardware:
	signal_exit();

error_signal:
	event_exit();

error_event:
	log_info("Brick Daemon %s stopped", VERSION_STRING);

error_config:
error_log:
	log_exit();

	if (pid_fd >= 0) {
		pid_file_release(_pid_filename, pid_fd);
	}

	config_exit();

	return exit_code;
}
Example #10
0
int red_stack_init(void) {
	int i = 0;
	int phase = 0;

	log_debug("Initializing RED Brick SPI Stack subsystem");

	_red_stack_spi_poll_delay = config_get_option_value("poll_delay.spi")->integer;

	if (gpio_sysfs_export(RED_STACK_RESET_PIN_GPIO_NUM) < 0) {
		// Just issue a warning, RED Brick will work without reset interrupt
		log_warn("Could not export GPIO %d in sysfs, disabling reset interrupt",
		         RED_STACK_RESET_PIN_GPIO_NUM);
	} else {
		if ((_red_stack_reset_fd = gpio_sysfs_get_value_fd(RED_STACK_RESET_PIN_GPIO_NAME)) < 0) {
			// Just issue a warning, RED Brick will work without reset interrupt
			log_warn("Could not retrieve fd for GPIO %s in sysfs, disabling reset interrupt",
			         RED_STACK_RESET_PIN_GPIO_NAME);
		} else {
			// If everything worked we can set the interrupt to falling.
			// We ignore the return value here, it may work despite error.
			gpio_sysfs_set_edge(RED_STACK_RESET_PIN_GPIO_NAME, "falling");
		}
	}

	// create base stack
	if (stack_create(&_red_stack.base, "red_stack", red_stack_dispatch_to_spi) < 0) {
		log_error("Could not create base stack for RED Brick SPI Stack: %s (%d)",
		          get_errno_name(errno), errno);

		goto cleanup;
	}

	phase = 1;

	// add to stacks array
	if (hardware_add_stack(&_red_stack.base) < 0) {
		goto cleanup;
	}

	phase = 2;

	if ((_red_stack_notification_event = eventfd(0, 0)) < 0) {
		log_error("Could not create red stack notification event: %s (%d)",
		          get_errno_name(errno), errno);

		goto cleanup;
	}

	phase = 3;

	// Add notification pipe as event source.
	// Event is used to dispatch packets.
	if (event_add_source(_red_stack_notification_event, EVENT_SOURCE_TYPE_GENERIC,
	                     EVENT_READ, red_stack_dispatch_from_spi, NULL) < 0) {
		log_error("Could not add red stack notification pipe as event source");

		goto cleanup;
	}

	phase = 4;

	// Initialize SPI packet queues
	for (i = 0; i < RED_STACK_SPI_MAX_SLAVES; i++) {
		if (queue_create(&_red_stack.slaves[i].packet_to_spi_queue, sizeof(REDStackPacket)) < 0) {
			log_error("Could not create SPI queue %d: %s (%d)",
			          i, get_errno_name(errno), errno);

			goto cleanup;
		}
	}

	if (semaphore_create(&_red_stack_dispatch_packet_from_spi_semaphore) < 0) {
		log_error("Could not create SPI request semaphore: %s (%d)",
		          get_errno_name(errno), errno);

		goto cleanup;
	}

	for (i = 0; i < RED_STACK_SPI_MAX_SLAVES; i++) {
		mutex_create(&_red_stack.slaves[i].packet_queue_mutex);
	}

	phase = 5;

	if (red_stack_init_spi() < 0) {
		goto cleanup;
	}

	// Add reset interrupt as event source
	if (_red_stack_reset_fd > 0) {
		char buf[2];
		lseek(_red_stack_reset_fd, 0, SEEK_SET);
		if (read(_red_stack_reset_fd, buf, 2) < 0) {} // ignore return value

		if (event_add_source(_red_stack_reset_fd, EVENT_SOURCE_TYPE_GENERIC,
		                     EVENT_PRIO | EVENT_ERROR, red_stack_reset_handler, NULL) < 0) {
			log_error("Could not add reset fd event");

			goto cleanup;
		}
	}

	phase = 6;

cleanup:
	switch (phase) { // no breaks, all cases fall through intentionally
	case 5:
		for (i = 0; i < RED_STACK_SPI_MAX_SLAVES; i++) {
			mutex_destroy(&_red_stack.slaves[i].packet_queue_mutex);
		}

		semaphore_destroy(&_red_stack_dispatch_packet_from_spi_semaphore);

	case 4:
		for (i--; i >= 0; i--) {
			queue_destroy(&_red_stack.slaves[i].packet_to_spi_queue, NULL);
		}

		event_remove_source(_red_stack_notification_event, EVENT_SOURCE_TYPE_GENERIC);

	case 3:
		close(_red_stack_notification_event);

	case 2:
		hardware_remove_stack(&_red_stack.base);

	case 1:
		stack_destroy(&_red_stack.base);

	default:
		break;
	}

	return phase == 6 ? 0 : -1;
}