Example #1
0
static void LIBUSB_CALL usb_add_pollfd(int fd, short events, void *opaque) {
	libusb_context *context = opaque;

	log_event_debug("Got told to add libusb pollfd (handle: %d, events: %d)", fd, events);

	// FIXME: need to handle libusb timeouts
	event_add_source(fd, EVENT_SOURCE_TYPE_USB, events, usb_handle_events, context); // FIXME: handle error?
}
Example #2
0
int socat_create(Socat *socat, Socket *socket) {
	log_debug("Creating socat from UNIX domain socket (handle: %d)", socket->handle);

	socat->socket = socket;
	socat->disconnected = false;
	socat->notification_used = 0;

	return event_add_source(socat->socket->handle, EVENT_SOURCE_TYPE_GENERIC,
	                        "socat", EVENT_READ, socat_handle_receive, socat);
}
Example #3
0
int client_create(Client *client, EventHandle socket,
                  struct sockaddr_in *address, socklen_t length) {
	log_debug("Creating client from socket (handle: %d)", socket);

	client->socket = socket;
	client->packet_used = 0;

	// create pending request array
	if (array_create(&client->pending_requests, 32,
	                 sizeof(PacketHeader), 1) < 0) {
		log_error("Could not create pending request array: %s (%d)",
		          get_errno_name(errno), errno);

		return -1;
	}

	// get peer name
	client->peer = resolve_address(address, length);

	if (client->peer == NULL) {
		log_warn("Could not get peer name of client (socket: %d): %s (%d)",
		         socket, get_errno_name(errno), errno);

		client->peer = (char *)_unknown_peer_name;
	}

	// add socket as event source
	if (event_add_source(client->socket, EVENT_SOURCE_TYPE_GENERIC, EVENT_READ,
	                     client_handle_receive, client) < 0) {
		if (client->peer != _unknown_peer_name) {
			free(client->peer);
		}

		array_destroy(&client->pending_requests, NULL);

		return -1;
	}

	return 0;
}
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 iokit_init(void) {
	int phase = 0;
	Semaphore handshake;

	log_debug("Initializing IOKit subsystem");

	// create notification pipe
	if (pipe_create(&_notification_pipe) < 0) {
		log_error("Could not create notification pipe: %s (%d)",
		          get_errno_name(errno), errno);

		goto cleanup;
	}

	phase = 1;

	if (event_add_source(_notification_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC,
	                     EVENT_READ, iokit_forward_notifications, NULL) < 0) {
		goto cleanup;
	}

	phase = 2;

	// create notification poll thread
	if (semaphore_create(&handshake) < 0) {
		log_error("Could not create handshake semaphore: %s (%d)",
		          get_errno_name(errno), errno);

		goto cleanup;
	}

	thread_create(&_poll_thread, iokit_poll_notifications, &handshake);

	semaphore_acquire(&handshake);
	semaphore_destroy(&handshake);

	phase = 3;

	if (!_running) {
		log_error("Could not start notification poll thread");

		goto cleanup;
	}

	phase = 4;

cleanup:
	switch (phase) { // no breaks, all cases fall through intentionally
	case 3:
		thread_destroy(&_poll_thread);

	case 2:
		event_remove_source(_notification_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC);

	case 1:
		pipe_destroy(&_notification_pipe);

	default:
		break;
	}

	return phase == 4 ? 0 : -1;
}
Example #6
0
int event_run_platform(Array *event_sources, bool *running, EventCleanupFunction cleanup) {
	int result = -1;
	int i;
	EventSource *event_source;
	fd_set *fd_read_set;
	fd_set *fd_write_set;
	fd_set *fd_error_set;
	int ready;
	int handled;
	uint8_t byte = 1;
	int rc;
	int event_source_count;
	uint32_t received_events;

	if (event_add_source(_usb_poll_ready_pipe.read_end,
	                     EVENT_SOURCE_TYPE_GENERIC, EVENT_READ,
	                     event_forward_usb_events, event_sources) < 0) {
		return -1;
	}

	*running = true;
	_usb_poll_running = true;

	thread_create(&_usb_poll_thread, event_poll_usb_events, event_sources);

	cleanup();
	event_cleanup_sources();

	while (*running) {
		// update SocketSet arrays
		if (event_reserve_socket_set(&_socket_read_set, // FIXME: this over-allocates
		                             event_sources->count) < 0) {
			log_error("Could not resize socket read set: %s (%d)",
			          get_errno_name(errno), errno);

			goto cleanup;
		}

		if (event_reserve_socket_set(&_socket_write_set, // FIXME: this over-allocates
		                             event_sources->count) < 0) {
			log_error("Could not resize socket write set: %s (%d)",
			          get_errno_name(errno), errno);

			goto cleanup;
		}

		if (event_reserve_socket_set(&_socket_error_set, // FIXME: this over-allocates
		                             event_sources->count) < 0) {
			log_error("Could not resize socket error set: %s (%d)",
			          get_errno_name(errno), errno);

			goto cleanup;
		}

		_socket_read_set->count = 0;
		_socket_write_set->count = 0;
		_socket_error_set->count = 0;

		for (i = 0; i < event_sources->count; ++i) {
			event_source = array_get(event_sources, i);

			if (event_source->type != EVENT_SOURCE_TYPE_GENERIC) {
				continue;
			}

			if ((event_source->events & EVENT_READ) != 0) {
				_socket_read_set->sockets[_socket_read_set->count++] = event_source->handle;
			}

			if ((event_source->events & EVENT_WRITE) != 0) {
				_socket_write_set->sockets[_socket_write_set->count++] = event_source->handle;
			}

			if ((event_source->events & EVENT_PRIO) != 0) {
				log_error("Event prio is not supported");
			}

			if ((event_source->events & EVENT_ERROR) != 0) {
				_socket_error_set->sockets[_socket_error_set->count++] = event_source->handle;
			}
		}

		// start to select
		log_event_debug("Starting to select on %d + %d + %d %s event source(s)",
		                _socket_read_set->count, _socket_write_set->count, _socket_error_set->count,
		                event_get_source_type_name(EVENT_SOURCE_TYPE_GENERIC, false));

		semaphore_release(&_usb_poll_resume);

		fd_read_set = event_get_socket_set_as_fd_set(_socket_read_set);
		fd_write_set = event_get_socket_set_as_fd_set(_socket_write_set);
		fd_error_set = event_get_socket_set_as_fd_set(_socket_error_set);

		ready = select(0, fd_read_set, fd_write_set, fd_error_set, NULL);

		if (_usb_poll_running) {
			log_event_debug("Sending suspend signal to USB poll thread");

			if (usbi_write(_usb_poll_suspend_pipe[1], &byte, 1) < 0) {
				log_error("Could not write to USB suspend pipe");

				_usb_poll_stuck = true;

				goto cleanup;
			}

			semaphore_acquire(&_usb_poll_suspend);

			if (usbi_read(_usb_poll_suspend_pipe[0], &byte, 1) < 0) {
				log_error("Could not read from USB suspend pipe");

				_usb_poll_stuck = true;

				goto cleanup;
			}
		}

		if (ready == SOCKET_ERROR) {
			rc = ERRNO_WINAPI_OFFSET + WSAGetLastError();

			if (rc == ERRNO_WINAPI_OFFSET + WSAEINTR) {
				continue;
			}

			log_error("Could not select on %s event sources: %s (%d)",
			          event_get_source_type_name(EVENT_SOURCE_TYPE_GENERIC, false),
			          get_errno_name(rc), rc);

			goto cleanup;
		}

		// handle select result
		log_event_debug("Select returned %d %s event source(s) as ready",
		                ready, event_get_source_type_name(EVENT_SOURCE_TYPE_GENERIC, false));

		handled = 0;

		// cache event source count here to avoid looking at new event
		// sources that got added during the event handling
		event_source_count = event_sources->count;

		for (i = 0; *running && i < event_source_count && ready > handled; ++i) {
			event_source = array_get(event_sources, i);
			received_events = 0;

			if (event_source->type != EVENT_SOURCE_TYPE_GENERIC) {
				continue;
			}

			if (FD_ISSET(event_source->handle, fd_read_set)) {
				received_events |= EVENT_READ;
			}

			if (FD_ISSET(event_source->handle, fd_write_set)) {
				received_events |= EVENT_WRITE;
			}

			if (FD_ISSET(event_source->handle, fd_error_set)) {
				received_events |= EVENT_ERROR;
			}

			if (received_events == 0) {
				continue;
			}

			event_handle_source(event_source, received_events);

			++handled;
		}

		if (ready == handled) {
			log_event_debug("Handled all ready %s event sources",
			                event_get_source_type_name(EVENT_SOURCE_TYPE_GENERIC, false));
		} else if (*running) {
			log_warn("Handled only %d of %d ready %s event source(s)",
			         handled, ready,
			         event_get_source_type_name(EVENT_SOURCE_TYPE_GENERIC, false));
		}

		// now cleanup event sources that got marked as disconnected/removed
		// during the event handling
		cleanup();
		event_cleanup_sources();
	}

	result = 0;

cleanup:
	*running = false;

	if (_usb_poll_running && !_usb_poll_stuck) {
		_usb_poll_running = false;

		log_debug("Stopping USB poll thread");

		if (usbi_write(_usb_poll_suspend_pipe[1], &byte, 1) < 0) {
			log_error("Could not write to USB suspend pipe");
		} else {
			semaphore_release(&_usb_poll_resume);
			thread_join(&_usb_poll_thread);
		}
	}

	thread_destroy(&_usb_poll_thread);

	event_remove_source(_usb_poll_ready_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC);

	return result;
}
Example #7
0
int event_init_platform(void) {
	int phase = 0;

	// create read set
	if (event_reserve_socket_set(&_socket_read_set, 32) < 0) {
		log_error("Could not create socket read set: %s (%d)",
		          get_errno_name(errno), errno);

		goto cleanup;
	}

	phase = 1;

	// create write set
	if (event_reserve_socket_set(&_socket_write_set, 32) < 0) {
		log_error("Could not create socket write set: %s (%d)",
		          get_errno_name(errno), errno);

		goto cleanup;
	}

	phase = 2;

	// create error set
	if (event_reserve_socket_set(&_socket_error_set, 32) < 0) {
		log_error("Could not create socket error set: %s (%d)",
		          get_errno_name(errno), errno);

		goto cleanup;
	}

	phase = 3;

	// create stop pipe
	if (pipe_create(&_stop_pipe, 0) < 0) {
		log_error("Could not create stop pipe: %s (%d)",
		          get_errno_name(errno), errno);

		goto cleanup;
	}

	phase = 4;

	if (event_add_source(_stop_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC,
	                     EVENT_READ, NULL, NULL) < 0) {
		goto cleanup;
	}

	phase = 5;

	// create USB poll thread
	_usb_poll_running = false;
	_usb_poll_stuck = false;

	if (usbi_pipe(_usb_poll_suspend_pipe) < 0) {
		log_error("Could not create USB suspend pipe");

		goto cleanup;
	}

	phase = 6;

	if (pipe_create(&_usb_poll_ready_pipe, 0) < 0) {
		log_error("Could not create USB ready pipe: %s (%d)",
		          get_errno_name(errno), errno);

		goto cleanup;
	}

	phase = 7;

	if (array_create(&_usb_poll_pollfds, 32, sizeof(struct usbi_pollfd), true) < 0) {
		log_error("Could not create USB pollfd array: %s (%d)",
		          get_errno_name(errno), errno);

		goto cleanup;
	}

	phase = 8;

	if (semaphore_create(&_usb_poll_resume) < 0) {
		log_error("Could not create USB resume semaphore: %s (%d)",
		          get_errno_name(errno), errno);

		goto cleanup;
	}

	phase = 9;

	if (semaphore_create(&_usb_poll_suspend) < 0) {
		log_error("Could not create USB suspend semaphore: %s (%d)",
		          get_errno_name(errno), errno);

		goto cleanup;
	}

	phase = 10;

cleanup:
	switch (phase) { // no breaks, all cases fall through intentionally
	case 9:
		semaphore_destroy(&_usb_poll_suspend);

	case 8:
		semaphore_destroy(&_usb_poll_resume);

	case 7:
		pipe_destroy(&_usb_poll_ready_pipe);

	case 6:
		usbi_close(_usb_poll_suspend_pipe[0]);
		usbi_close(_usb_poll_suspend_pipe[1]);

	case 5:
		event_remove_source(_stop_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC);

	case 4:
		pipe_destroy(&_stop_pipe);

	case 3:
		free(_socket_error_set);

	case 2:
		free(_socket_write_set);

	case 1:
		free(_socket_read_set);

	default:
		break;
	}

	return phase == 10 ? 0 : -1;
}
Example #8
0
// NOTE: this function needs to call RegisterServiceCtrlHandlerEx and
// SetServiceStatus in all circumstances if brickd is running as service
static int generic_main(int log_to_file, int debug) {
	int exit_code = EXIT_FAILURE;
	const char *mutex_name = "Global\\Tinkerforge-Brick-Daemon-Single-Instance";
	HANDLE mutex_handle = NULL;
	int mutex_error = 0;
	DWORD service_exit_code = NO_ERROR;
	int rc;
	char filename[1024];
	int i;
	FILE *logfile = NULL;
	WSADATA wsa_data;
	DEV_BROADCAST_DEVICEINTERFACE notification_filter;
	HDEVNOTIFY notification_handle;

	mutex_handle = OpenMutex(SYNCHRONIZE, FALSE, mutex_name);

	if (mutex_handle == NULL) {
		rc = GetLastError();

		if (rc == ERROR_ACCESS_DENIED) {
			rc = service_is_running();

			if (rc < 0) {
				mutex_error = 1;
				// FIXME: set service_exit_code

				goto error_mutex;
			} else if (rc) {
				mutex_error = 1;
				service_exit_code = ERROR_SERVICE_ALREADY_RUNNING;

				log_error("Could not start as %s, another instance is already running as service",
				          _run_as_service ? "service" : "console application");

				goto error_mutex;
			}
		}

		if (rc != ERROR_FILE_NOT_FOUND) {
			mutex_error = 1;
			// FIXME: set service_exit_code
			rc += ERRNO_WINAPI_OFFSET;

			log_error("Could not open single instance mutex: %s (%d)",
			          get_errno_name(rc), rc);

			goto error_mutex;
		}
	}

	if (mutex_handle != NULL) {
		mutex_error = 1;
		service_exit_code = ERROR_SERVICE_ALREADY_RUNNING;

		log_error("Could not start as %s, another instance is already running",
		          _run_as_service ? "service" : "console application");

		goto error_mutex;
	}

	mutex_handle = CreateMutex(NULL, FALSE, mutex_name);

	if (mutex_handle == NULL) {
		mutex_error = 1;
		// FIXME: set service_exit_code
		rc = ERRNO_WINAPI_OFFSET + GetLastError();

		log_error("Could not create single instance mutex: %s (%d)",
		          get_errno_name(rc), rc);

		goto error_mutex;
	}

	if (!_run_as_service &&
	    !SetConsoleCtrlHandler(console_ctrl_handler, TRUE)) {
		rc = ERRNO_WINAPI_OFFSET + GetLastError();

		log_warn("Could not set console control handler: %s (%d)",
		          get_errno_name(rc), rc);
	}

	if (log_to_file) {
		if (GetModuleFileName(NULL, filename, sizeof(filename)) == 0) {
			rc = ERRNO_WINAPI_OFFSET + GetLastError();

			log_warn("Could not get module file name: %s (%d)",
			         get_errno_name(rc), rc);
		} else {
			i = strlen(filename);

			if (i < 4) {
				log_warn("Module file name '%s' is too short", filename);
			} else {
				strcpy(filename + i - 3, "log");

				logfile = fopen(filename, "a+");

				if (logfile == NULL) {
					log_warn("Could not open logfile '%s'", filename);
				} else {
					log_set_file(logfile);
				}
			}
		}
	}

	if (debug) {
		log_set_level(LOG_CATEGORY_EVENT, LOG_LEVEL_DEBUG);
		log_set_level(LOG_CATEGORY_USB, LOG_LEVEL_DEBUG);
		log_set_level(LOG_CATEGORY_NETWORK, LOG_LEVEL_DEBUG);
		log_set_level(LOG_CATEGORY_HOTPLUG, LOG_LEVEL_DEBUG);
		log_set_level(LOG_CATEGORY_OTHER, LOG_LEVEL_DEBUG);
	} else {
		log_set_level(LOG_CATEGORY_EVENT, config_get_log_level(LOG_CATEGORY_EVENT));
		log_set_level(LOG_CATEGORY_USB, config_get_log_level(LOG_CATEGORY_USB));
		log_set_level(LOG_CATEGORY_NETWORK, config_get_log_level(LOG_CATEGORY_NETWORK));
		log_set_level(LOG_CATEGORY_HOTPLUG, config_get_log_level(LOG_CATEGORY_HOTPLUG));
		log_set_level(LOG_CATEGORY_OTHER, config_get_log_level(LOG_CATEGORY_OTHER));
	}

	if (_run_as_service) {
		log_info("Brick Daemon %s started (as service)", VERSION_STRING);
	} else {
		log_info("Brick Daemon %s started", VERSION_STRING);
	}

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

	// initialize service status
error_mutex:
	if (_run_as_service) {
		if (service_init(service_control_handler) < 0) {
			// FIXME: set service_exit_code
			goto error;
		}

		if (!mutex_error) {
			// service is starting
			service_set_status(SERVICE_START_PENDING, NO_ERROR);
		}
	}

	if (mutex_error) {
		goto error;
	}

	// initialize WinSock2
	if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) {
		// FIXME: set service_exit_code
		rc = ERRNO_WINAPI_OFFSET + WSAGetLastError();

		log_error("Could not initialize Windows Sockets 2.2: %s (%d)",
		          get_errno_name(rc), rc);

		goto error_event;
	}

	if (event_init() < 0) {
		// FIXME: set service_exit_code
		goto error_event;
	}

	if (usb_init() < 0) {
		// FIXME: set service_exit_code
		goto error_usb;
	}

	// create notification pipe
	if (pipe_create(_notification_pipe) < 0) {
		// FIXME: set service_exit_code

		log_error("Could not create notification pipe: %s (%d)",
		          get_errno_name(errno), errno);

		goto error_pipe;
	}

	if (event_add_source(_notification_pipe[0], EVENT_SOURCE_TYPE_GENERIC,
	                     EVENT_READ, forward_notifications, NULL) < 0) {
		// FIXME: set service_exit_code
		goto error_pipe_add;
	}

	// register device notification
	ZeroMemory(&notification_filter, sizeof(notification_filter));

	notification_filter.dbcc_size = sizeof(notification_filter);
	notification_filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
	notification_filter.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;

	if (_run_as_service) {
		notification_handle = RegisterDeviceNotification((HANDLE)service_get_status_handle(),
		                                                 &notification_filter,
		                                                 DEVICE_NOTIFY_SERVICE_HANDLE);
	} else {
		if (message_pump_start() < 0) {
			// FIXME: set service_exit_code
			goto error_pipe_add;
		}

		notification_handle = RegisterDeviceNotification(_message_pump_hwnd,
		                                                 &notification_filter,
		                                                 DEVICE_NOTIFY_WINDOW_HANDLE);
	}

	if (notification_handle == NULL) {
		// FIXME: set service_exit_code
		rc = ERRNO_WINAPI_OFFSET + GetLastError();

		log_error("Could not register for device notification: %s (%d)",
		          get_errno_name(rc), rc);

		goto error_notification;
	}

	if (network_init() < 0) {
		// FIXME: set service_exit_code
		goto error_network;
	}

	// running
	if (_run_as_service) {
		service_set_status(SERVICE_RUNNING, NO_ERROR);
	}

	if (event_run() < 0) {
		// FIXME: set service_exit_code
		goto error_run;
	}

	exit_code = EXIT_SUCCESS;

error_run:
	network_exit();

error_network:
	UnregisterDeviceNotification(notification_handle);

error_notification:
	if (!_run_as_service) {
		message_pump_stop();
	}

	event_remove_source(_notification_pipe[0], EVENT_SOURCE_TYPE_GENERIC);

error_pipe_add:
	pipe_destroy(_notification_pipe);

error_pipe:
	usb_exit();

error_usb:
	event_exit();

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

error:
	log_exit();

	config_exit();

	if (_run_as_service) {
		// because the service process can be terminated at any time after
		// entering SERVICE_STOPPED state the mutex is closed beforehand,
		// even though this creates a tiny time window in which the service
		// is still running but the mutex is not held anymore
		if (mutex_handle != NULL) {
			CloseHandle(mutex_handle);
		}

		// service is now stopped
		service_set_status(SERVICE_STOPPED, service_exit_code);
	} else {
		if (_pause_before_exit) {
			printf("Press any key to exit...\n");
			getch();
		}

		if (mutex_handle != NULL) {
			CloseHandle(mutex_handle);
		}
	}

	return exit_code;
}
Example #9
0
static void bricklet_stack_transceive(BrickletStack *bricklet_stack) {
	// If we have not seen any data from the Bricklet we increase a counter.
	// If the counter reaches BRICKLET_STACK_FIRST_MESSAGE_TRIES we assume that
	// there is no Bricklet and we stop trying to send to initial message (if a
	// Bricklet is hotplugged it will send a enumerate itself).
	if(!bricklet_stack->data_seen) {
		if(bricklet_stack->first_message_tries < BRICKLET_STACK_FIRST_MESSAGE_TRIES) {
			bricklet_stack->first_message_tries++;
		} else {
			bricklet_stack->buffer_send_length = 0;
		}
	}

	const uint16_t length_read = bricklet_stack_check_missing_length(bricklet_stack);
	if(bricklet_stack->buffer_send_length == 0) {
		// If buffer is empty we try to send request from the queue.
		bricklet_stack_check_request_queue(bricklet_stack);
		if((bricklet_stack->buffer_send_length == 0) && (bricklet_stack->ack_to_send)) {
			// If there is no request in the queue (buffer still empty)
			// and we have to send an ACK still, we send the ACK.
			bricklet_stack_send_ack(bricklet_stack);
		}
	}
	uint16_t length_write = bricklet_stack->wait_for_ack ? 0 : bricklet_stack->buffer_send_length; 
	uint16_t length = MAX(MAX(length_read, length_write), 1);

	uint8_t rx[SPITFP_MAX_TFP_MESSAGE_LENGTH] = {0};
	uint8_t tx[SPITFP_MAX_TFP_MESSAGE_LENGTH] = {0};

	if((length == 1) || (!bricklet_stack->data_seen)) {
		// If there is nothing to read or to write, we give the Bricklet some breathing
		// room before we start polling again.

		// If we have nothing to send and we are currently not awaiting data from the Bricklet, we will
		// poll every 200 us.
		uint32_t sleep_us = 200;
		if(!bricklet_stack->data_seen) {
			// If we have never seen any data, we will first poll every 1ms with the StackEnumerate message
			// and switch to polling every 500ms after we tried BRICKLET_STACK_FIRST_MESSAGE_TRIES times.
			// In this case there is likely no Bricklet connected. If a Bricklet is hotpluged "data_seen"
			// will be true and we will switch to polling every 200us immediately.
			if(bricklet_stack->first_message_tries < BRICKLET_STACK_FIRST_MESSAGE_TRIES) {
				sleep_us = 1*1000;
			} else {
				sleep_us = 500*1000;
			}
		}
		struct timespec t;
		t.tv_sec = 0;
		t.tv_nsec = 1000*sleep_us;
		clock_nanosleep(CLOCK_MONOTONIC, 0, &t, NULL);
	}

	memcpy(tx, bricklet_stack->buffer_send, length_write);

	struct spi_ioc_transfer spi_transfer = {
		.tx_buf = (unsigned long)tx,
		.rx_buf = (unsigned long)rx,
		.len = length,
	};

	// Make sure that we only access SPI once at a time
	mutex_lock(bricklet_stack->config.mutex);

	// Do chip select by hand if necessary
	if(bricklet_stack->config.chip_select_driver == CHIP_SELECT_GPIO) {
		if(gpio_sysfs_set_output(&bricklet_stack->config.chip_select_gpio_sysfs, GPIO_SYSFS_VALUE_LOW) < 0) {
			log_error("Could not enable chip select");
			return;
		}
	}

	int rc = ioctl(bricklet_stack->spi_fd, SPI_IOC_MESSAGE(1), &spi_transfer);

	// If the length is 1 (i.e. we wanted to see if the SPI slave has data for us)
	// and he does have data for us, we will immediately retrieve the data without
	// giving back the mutex.
	if((length == 1) && (rx[0] != 0) && (rc == length) && (length_write == 0)) {
		// First add the one byte of already received data to the ringbuffer
		ringbuffer_add(&bricklet_stack->ringbuffer_recv, rx[0]);

		// Set rc to 0, so if there is no more data to read, we don't get the
		// "unexpected result" error
		rc = 0;

		// Get length for rest of message
		length = bricklet_stack_check_missing_length(bricklet_stack);
		if(length != 0) {
			// Set first byte back to 0 and the new length, the rest was not touched
			// and we don't need to reinizialize it.
			rx[0] = 0;
			spi_transfer.len = length;
			rc = ioctl(bricklet_stack->spi_fd, SPI_IOC_MESSAGE(1), &spi_transfer);
		}
	}

	// Do chip deselect by hand if necessary
	if(bricklet_stack->config.chip_select_driver == CHIP_SELECT_GPIO) {
		if(gpio_sysfs_set_output(&bricklet_stack->config.chip_select_gpio_sysfs, GPIO_SYSFS_VALUE_HIGH) < 0) {
			log_error("Could not disable chip select");
			return;
		}
	}

	mutex_unlock(bricklet_stack->config.mutex);

	if (rc < 0) {
		log_error("ioctl failed: %s (%d)", get_errno_name(errno), errno);
		return;
	}

	if (rc != length) {
		log_error("ioctl has unexpected result (actual: %d != expected: %d)", rc, length);
		return;
	}

	// We don't expect an ACK to be acked, so we can set the length to 0 here
	if(bricklet_stack->buffer_send_length == SPITFP_PROTOCOL_OVERHEAD) {
		bricklet_stack->buffer_send_length = 0;
	} 
	
	if(bricklet_stack->buffer_send_length >= SPITFP_MIN_TFP_MESSAGE_LENGTH) {
		bricklet_stack->wait_for_ack = true;
	}

	for(uint16_t i = 0; i < length; i++) {
		ringbuffer_add(&bricklet_stack->ringbuffer_recv, rx[i]);
	}
}

static void bricklet_stack_spi_thread(void *opaque) {
	BrickletStack *bricklet_stack = (BrickletStack*)opaque;
	bricklet_stack->spi_thread_running = true;

	// Depending on the configuration we wait on startup for
	// other Bricklets to identify themself first.
	struct timespec t = {
		.tv_sec = bricklet_stack->config.startup_wait_time,
	};
	clock_nanosleep(CLOCK_MONOTONIC, 0, &t, NULL);	

	// Pre-fill the send buffer with the "StackEnumerate"-Packet.
	// This packet will trigger an initial enumeration in the Bricklet.
	// If the Brick Daemon is restarted, we need to
	// trigger the initial enumeration, since the Bricklet does not know
	// that it has to enumerate itself again.
	PacketHeader header = {
		.uid                         = 0,
		.length                      = sizeof(PacketHeader),
		.function_id                 = FUNCTION_STACK_ENUMERATE,
		.sequence_number_and_options = 0x08, // return expected
		.error_code_and_future_use   = 0
	};
	bricklet_stack_send_ack_and_message(bricklet_stack, (uint8_t*)&header, sizeof(PacketHeader));

	while (bricklet_stack->spi_thread_running) {
		bricklet_stack_transceive(bricklet_stack);
		bricklet_stack_check_message(bricklet_stack);
	}
}

static int bricklet_stack_init_spi(BrickletStack *bricklet_stack) {
	// Use hw chip select if it is done by SPI hardware unit, otherwise set SPI_NO_CS flag.
	const int mode          = BRICKLET_STACK_SPI_CONFIG_MODE | (bricklet_stack->config.chip_select_driver == CHIP_SELECT_HARDWARE ? 0 : SPI_NO_CS);
	const int lsb_first     = BRICKLET_STACK_SPI_CONFIG_LSB_FIRST;
	const int bits_per_word = BRICKLET_STACK_SPI_CONFIG_BITS_PER_WORD;
	const int max_speed_hz  = BRICKLET_STACK_SPI_CONFIG_MAX_SPEED_HZ;

	// Open spidev
	bricklet_stack->spi_fd = open(bricklet_stack->config.spi_device, O_RDWR);
	if (bricklet_stack->spi_fd < 0) {
		log_error("Could not open %s: : %s (%d)",
		          bricklet_stack->config.spi_device, get_errno_name(errno), errno);
		return -1;
	}

	if (ioctl(bricklet_stack->spi_fd, SPI_IOC_WR_MODE, &mode) < 0) {
		log_error("Could not configure SPI mode: %s (%d)",
		          get_errno_name(errno), errno);
		return -1;
	}

	if (ioctl(bricklet_stack->spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &max_speed_hz) < 0) {
		log_error("Could not configure SPI max speed: %s (%d)",
		          get_errno_name(errno), errno);
		return -1;
	}

	if (ioctl(bricklet_stack->spi_fd, SPI_IOC_WR_BITS_PER_WORD, &bits_per_word) < 0) {
		log_error("Could not configure SPI bits per word: %s (%d)",
		          get_errno_name(errno), errno);
		return -1;
	}

	if (ioctl(bricklet_stack->spi_fd, SPI_IOC_WR_LSB_FIRST, &lsb_first) < 0) {
		log_error("Could not configure SPI lsb first: %s (%d)",
		          get_errno_name(errno), errno);
		return -1;
	}

	thread_create(&bricklet_stack->spi_thread, bricklet_stack_spi_thread, bricklet_stack);

	return 0;
}

BrickletStack* bricklet_stack_init(BrickletStackConfig *config) {
    int phase = 0;
	char bricklet_stack_name[129] = {'\0'};
	char notification_name[129] = {'\0'};

    log_debug("Initializing BrickletStack subsystem for '%s' (num %d)", config->spi_device, config->chip_select_gpio_sysfs.num);

	if(config->chip_select_driver == CHIP_SELECT_GPIO) {
		if(gpio_sysfs_export(&config->chip_select_gpio_sysfs) < 0) {
			goto cleanup;
		}

		if(gpio_sysfs_set_direction(&config->chip_select_gpio_sysfs, GPIO_SYSFS_DIRECTION_OUTPUT) < 0) {
			goto cleanup;
		}

		if(gpio_sysfs_set_output(&config->chip_select_gpio_sysfs, GPIO_SYSFS_VALUE_HIGH) < 0) {
			goto cleanup;
		}
	}

	// create bricklet_stack struct
	BrickletStack *bricklet_stack = (BrickletStack*)malloc(sizeof(BrickletStack));	
	if(bricklet_stack == NULL) {
		goto cleanup;
	}

	memset(bricklet_stack, 0, sizeof(BrickletStack));

	bricklet_stack->spi_fd = -1;
	bricklet_stack->spi_thread_running = false;

	memcpy(&bricklet_stack->config, config, sizeof(BrickletStackConfig));

	ringbuffer_init(&bricklet_stack->ringbuffer_recv, 
	                BRICKLET_STACK_SPI_RECEIVE_BUFFER_LENGTH, 
					bricklet_stack->buffer_recv);

	// create base stack
	if (snprintf(bricklet_stack_name, 128, "bricklet-stack-%s", bricklet_stack->config.spi_device) < 0) {
		goto cleanup;
	}
	if (stack_create(&bricklet_stack->base, bricklet_stack_name, bricklet_stack_dispatch_to_spi) < 0) {
		log_error("Could not create base stack for BrickletStack: %s (%d)",
		          get_errno_name(errno), errno);

		goto cleanup;
	}

	phase = 1;

	// add to stacks array
	if (hardware_add_stack(&bricklet_stack->base) < 0) {
		goto cleanup;
	}

	phase = 2;

	if ((bricklet_stack->notification_event = eventfd(0, EFD_NONBLOCK | EFD_SEMAPHORE)) < 0) {
		log_error("Could not create bricklet 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 (snprintf(notification_name, 128, "bricklet-stack-notification-%s", bricklet_stack->config.spi_device) < 0) {
		goto cleanup;
	}
	if (event_add_source(bricklet_stack->notification_event, EVENT_SOURCE_TYPE_GENERIC,
	                     notification_name, EVENT_READ,
	                     bricklet_stack_dispatch_from_spi, bricklet_stack) < 0) {
		log_error("Could not add bricklet notification pipe as event source");

		goto cleanup;
	}

	phase = 4;

	// Initialize SPI packet queues
	if (queue_create(&bricklet_stack->request_queue, sizeof(Packet)) < 0) {
		log_error("Could not create SPI request queue: %s (%d)",
		          get_errno_name(errno), errno);

		goto cleanup;
	}
	mutex_create(&bricklet_stack->request_queue_mutex);

	phase = 5;

	if (queue_create(&bricklet_stack->response_queue, sizeof(Packet)) < 0) {
		log_error("Could not create SPI response queue: %s (%d)",
		          get_errno_name(errno), errno);

		goto cleanup;
	}
	mutex_create(&bricklet_stack->response_queue_mutex);

	phase = 6;

	if (bricklet_stack_init_spi(bricklet_stack) < 0) {
		goto cleanup;
	}

	phase = 7;

    cleanup:
	switch (phase) { // no breaks, all cases fall through intentionally
	case 6:
		mutex_destroy(&bricklet_stack->response_queue_mutex);
		queue_destroy(&bricklet_stack->response_queue, NULL);
		// fall through

	case 5:
		mutex_destroy(&bricklet_stack->request_queue_mutex);
		queue_destroy(&bricklet_stack->request_queue, NULL);

		// fall through

	case 4:
		event_remove_source(bricklet_stack->notification_event, EVENT_SOURCE_TYPE_GENERIC);
		// fall through

	case 3:
		robust_close(bricklet_stack->notification_event);
		// fall through

	case 2:
		hardware_remove_stack(&bricklet_stack->base);
		// fall through

	case 1:
		stack_destroy(&bricklet_stack->base);
		// fall through

	default:
		break;
	}

	return phase == 7 ? bricklet_stack : NULL;
}

void bricklet_stack_exit(BrickletStack *bricklet_stack) {
	// Remove event as possible poll source
	event_remove_source(bricklet_stack->notification_event, EVENT_SOURCE_TYPE_GENERIC);

	// Make sure that Thread shuts down properly
	if (bricklet_stack->spi_thread_running) {
		bricklet_stack->spi_thread_running = false;

		thread_join(&bricklet_stack->spi_thread);
		thread_destroy(&bricklet_stack->spi_thread);
	}

	hardware_remove_stack(&bricklet_stack->base);
	stack_destroy(&bricklet_stack->base);

	queue_destroy(&bricklet_stack->request_queue, NULL);
	mutex_destroy(&bricklet_stack->request_queue_mutex);

	queue_destroy(&bricklet_stack->response_queue, NULL);
	mutex_destroy(&bricklet_stack->response_queue_mutex);

	// Close file descriptors
	robust_close(bricklet_stack->notification_event);
	robust_close(bricklet_stack->spi_fd);

	// Everything is closed and the threads are destroyed. We can
	// now free the Bricklet Stack memory. It will not be accessed anymore.
	free(bricklet_stack);
}
Example #10
0
int usb_create_context(libusb_context **context) {
	int phase = 0;
	int rc;
	const struct libusb_pollfd **pollfds = NULL;
	const struct libusb_pollfd **pollfd;
	const struct libusb_pollfd **last_added_pollfd = NULL;

	rc = libusb_init(context);

	if (rc < 0) {
		log_error("Could not initialize libusb context: %s (%d)",
		          usb_get_error_name(rc), rc);

		goto cleanup;
	}

	switch (log_get_effective_level()) {
	case LOG_LEVEL_ERROR:
		libusb_set_debug(*context, 1);
		break;

	case LOG_LEVEL_WARN:
		libusb_set_debug(*context, 2);
		break;

	case LOG_LEVEL_INFO:
		libusb_set_debug(*context, 3);
		break;

	case LOG_LEVEL_DEBUG:
		if (log_is_included(LOG_LEVEL_DEBUG, &_libusb_log_source,
		                    LOG_DEBUG_GROUP_LIBUSB)) {
			libusb_set_debug(*context, 4);
		} else {
			libusb_set_debug(*context, 3);
		}

		break;

	default:
		break;
	}

	phase = 1;

	// get pollfds from main libusb context
	pollfds = libusb_get_pollfds(*context);

	if (pollfds == NULL) {
		log_error("Could not get pollfds from libusb context");

		goto cleanup;
	}

	for (pollfd = pollfds; *pollfd != NULL; ++pollfd) {
		if (event_add_source((*pollfd)->fd, EVENT_SOURCE_TYPE_USB,
		                     (*pollfd)->events, usb_handle_events,
		                     *context) < 0) {
			goto cleanup;
		}

		last_added_pollfd = pollfd;
		phase = 2;
	}

	phase = 3;

	// register pollfd notifiers
	libusb_set_pollfd_notifiers(*context, usb_add_pollfd, usb_remove_pollfd,
	                            *context);

cleanup:
	switch (phase) { // no breaks, all cases fall through intentionally
	case 2:
		for (pollfd = pollfds; pollfd != last_added_pollfd; ++pollfd) {
			event_remove_source((*pollfd)->fd, EVENT_SOURCE_TYPE_USB);
		}

	case 1:
		libusb_exit(*context);

	default:
		break;
	}

#if defined(_WIN32) || (defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000104) // libusb 1.0.20
	libusb_free_pollfds(pollfds); // avoids possible heap-mismatch on Windows
#else
	free(pollfds);
#endif

	return phase == 3 ? 0 : -1;
}
Example #11
0
int udev_init(void) {
	int phase = 0;
	int rc;

	log_debug("Initializing udev subsystem");

#ifdef BRICKD_WITH_LIBUDEV_DLOPEN
	if (udev_dlopen() < 0) {
		goto cleanup;
	}

	phase = 1;
#endif

	// create udev context
	_udev_context = udev_new();

	if (_udev_context == NULL) {
		log_error("Could not create udev context");

		goto cleanup;
	}

	phase = 2;

	// create udev monitor
	_udev_monitor = udev_monitor_new_from_netlink(_udev_context, "udev");

	if (_udev_monitor == NULL) {
		log_error("Could not initialize udev monitor");

		goto cleanup;
	}

	phase = 3;

	// create filter for USB
	rc = udev_monitor_filter_add_match_subsystem_devtype(_udev_monitor, "usb", 0);

	if (rc != 0) {
		log_error("Could not initialize udev monitor filter for 'usb' subsystem: %d", rc);

		goto cleanup;
	}

	rc = udev_monitor_enable_receiving(_udev_monitor);

	if (rc != 0) {
		log_error("Could not enable the udev monitor: %d", rc);

		goto cleanup;
	}

	// add event source
	_udev_monitor_fd = udev_monitor_get_fd(_udev_monitor);

	if (event_add_source(_udev_monitor_fd, EVENT_SOURCE_TYPE_GENERIC,
	                     EVENT_READ, udev_handle_event, NULL) < 0) {
		goto cleanup;
	}

	phase = 4;

cleanup:
	switch (phase) { // no breaks, all cases fall through intentionally
	case 3:
		udev_monitor_unref(_udev_monitor);

	case 2:
		udev_unref(_udev_context);

#ifdef BRICKD_WITH_LIBUDEV_DLOPEN
	case 1:
		udev_dlclose();
#endif

	default:
		break;
	}

	return phase == 4 ? 0 : -1;
}
Example #12
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 #13
0
// public API
APIE process_spawn(ObjectID executable_id, ObjectID arguments_id,
                   ObjectID environment_id, ObjectID working_directory_id,
                   uint32_t uid, uint32_t gid, ObjectID stdin_id,
                   ObjectID stdout_id, ObjectID stderr_id, Session *session,
                   uint16_t object_create_flags, bool release_on_death,
                   ProcessStateChangedFunction state_changed, void *opaque,
                   ObjectID *id, Process **object) {
	int phase = 0;
	APIE error_code;
	String *executable;
	List *arguments;
	Array arguments_array;
	int i;
	char **item;
	List *environment;
	Array environment_array;
	String *working_directory;
	File *stdin;
	File *stdout;
	File *stderr;
	pid_t pid;
	int status_pipe[2];
	int sc_open_max;
	FILE *log_file;
	Process *process;

	// acquire and lock executable string object
	error_code = string_get_acquired_and_locked(executable_id, &executable);

	if (error_code != API_E_SUCCESS) {
		goto cleanup;
	}

	phase = 1;

	if (*executable->buffer == '\0') {
		error_code = API_E_INVALID_PARAMETER;

		log_warn("Cannot spawn child process using empty executable name");

		goto cleanup;
	}

	// lock arguments list object
	error_code = list_get_acquired_and_locked(arguments_id, OBJECT_TYPE_STRING,
	                                          &arguments);

	if (error_code != API_E_SUCCESS) {
		goto cleanup;
	}

	phase = 2;

	// prepare arguments array for execvpe
	if (array_create(&arguments_array, 1 + arguments->items.count + 1, sizeof(char *), true) < 0) {
		error_code = api_get_error_code_from_errno();

		log_error("Could not create arguments array for spawning child process (executable: %s): %s (%d)",
		          executable->buffer, get_errno_name(errno), errno);

		goto cleanup;
	}

	phase = 3;

	item = array_append(&arguments_array);

	if (item == NULL) {
		error_code = api_get_error_code_from_errno();

		log_error("Could not append to arguments array for spawning child process (executable: %s): %s (%d)",
		          executable->buffer, get_errno_name(errno), errno);

		goto cleanup;
	}

	*item = executable->buffer;

	for (i = 0; i < arguments->items.count; ++i) {
		item = array_append(&arguments_array);

		if (item == NULL) {
			error_code = api_get_error_code_from_errno();

			log_error("Could not append to arguments array for spawning child process (executable: %s): %s (%d)",
			          executable->buffer, get_errno_name(errno), errno);

			goto cleanup;
		}

		*item = (*(String **)array_get(&arguments->items, i))->buffer;
	}

	item = array_append(&arguments_array);

	if (item == NULL) {
		error_code = api_get_error_code_from_errno();

		log_error("Could not append to arguments array for spawning child process (executable: %s): %s (%d)",
		          executable->buffer, get_errno_name(errno), errno);

		goto cleanup;
	}

	*item = NULL;

	// lock environment list object
	error_code = list_get_acquired_and_locked(environment_id, OBJECT_TYPE_STRING,
	                                          &environment);

	if (error_code != API_E_SUCCESS) {
		goto cleanup;
	}

	phase = 4;

	// prepare environment array for execvpe
	if (array_create(&environment_array, environment->items.count + 1, sizeof(char *), true) < 0) {
		error_code = api_get_error_code_from_errno();

		log_error("Could not create environment array for spawning child process (executable: %s): %s (%d)",
		          executable->buffer, get_errno_name(errno), errno);

		goto cleanup;
	}

	phase = 5;

	for (i = 0; i < environment->items.count; ++i) {
		item = array_append(&environment_array);

		if (item == NULL) {
			error_code = api_get_error_code_from_errno();

			log_error("Could not append to environment array for spawning child process (executable: %s): %s (%d)",
			          executable->buffer, get_errno_name(errno), errno);

			goto cleanup;
		}

		// FIXME: if item is not <name>=<value>, but just <name> then use the parent <value>

		*item = (*(String **)array_get(&environment->items, i))->buffer;
	}

	item = array_append(&environment_array);

	if (item == NULL) {
		error_code = api_get_error_code_from_errno();

		log_error("Could not append to environment array for spawning child process (executable: %s): %s (%d)",
		          executable->buffer, get_errno_name(errno), errno);

		goto cleanup;
	}

	*item = NULL;

	// acquire and lock working directory string object
	error_code = string_get_acquired_and_locked(working_directory_id, &working_directory);

	if (error_code != API_E_SUCCESS) {
		goto cleanup;
	}

	phase = 6;

	if (*working_directory->buffer == '\0') {
		error_code = API_E_INVALID_PARAMETER;

		log_warn("Cannot spawn child process (executable: %s) using empty working directory name",
		         executable->buffer);

		goto cleanup;
	}

	if (*working_directory->buffer != '/') {
		error_code = API_E_INVALID_PARAMETER;

		log_warn("Cannot spawn child process (executable: %s) using working directory with relative name '%s'",
		         executable->buffer, working_directory->buffer);

		goto cleanup;
	}

	// acquire stdin file object
	error_code = file_get_acquired(stdin_id, &stdin);

	if (error_code != API_E_SUCCESS) {
		goto cleanup;
	}

	phase = 7;

	// acquire stdout file object
	error_code = file_get_acquired(stdout_id, &stdout);

	if (error_code != API_E_SUCCESS) {
		goto cleanup;
	}

	phase = 8;

	// acquire stderr file object
	error_code = file_get_acquired(stderr_id, &stderr);

	if (error_code != API_E_SUCCESS) {
		goto cleanup;
	}

	phase = 9;

	// create status pipe
	if (pipe(status_pipe) < 0) {
		error_code = api_get_error_code_from_errno();

		log_error("Could not create status pipe for spawning child process (executable: %s): %s (%d)",
		          executable->buffer, get_errno_name(errno), errno);

		goto cleanup;
	}

	phase = 10;

	// fork
	log_debug("Forking to spawn child process (executable: %s)", executable->buffer);

	error_code = process_fork(&pid);

	if (error_code != API_E_SUCCESS) {
		goto cleanup;
	}

	if (pid == 0) { // child
		close(status_pipe[0]);

		// change user and groups
		error_code = process_set_identity(uid, gid);

		if (error_code != API_E_SUCCESS) {
			goto child_error;
		}

		// change directory
		if (chdir(working_directory->buffer) < 0) {
			error_code = api_get_error_code_from_errno();

			log_error("Could not change directory to '%s' for child process (executable: %s, pid: %u): %s (%d)",
			          working_directory->buffer, executable->buffer, getpid(), get_errno_name(errno), errno);

			goto child_error;
		}

		// get open FD limit
		sc_open_max = sysconf(_SC_OPEN_MAX);

		if (sc_open_max < 0) {
			error_code = api_get_error_code_from_errno();

			log_error("Could not get SC_OPEN_MAX value: %s (%d)",
			          get_errno_name(errno), errno);

			goto child_error;
		}

		// redirect stdin
		if (dup2(file_get_read_handle(stdin), STDIN_FILENO) != STDIN_FILENO) {
			error_code = api_get_error_code_from_errno();

			log_error("Could not redirect stdin for child process (executable: %s, pid: %u): %s (%d)",
			          executable->buffer, getpid(), get_errno_name(errno), errno);

			goto child_error;
		}

		// redirect stdout
		if (dup2(file_get_write_handle(stdout), STDOUT_FILENO) != STDOUT_FILENO) {
			error_code = api_get_error_code_from_errno();

			log_error("Could not redirect stdout for child process (executable: %s, pid: %u): %s (%d)",
			          executable->buffer, getpid(), get_errno_name(errno), errno);

			goto child_error;
		}

		// stderr is the default log output in non-daemon mode. if this is
		// the case then disable the log output before redirecting stderr to
		// avoid polluting stderr for the new process
		log_file = log_get_file();

		if (log_file != NULL && fileno(log_file) == STDERR_FILENO) {
			log_debug("Disable logging to stderr for child process (executable: %s, pid: %u)",
			          executable->buffer, getpid());

			log_set_file(NULL);
		}

		// redirect stderr
		if (dup2(file_get_write_handle(stderr), STDERR_FILENO) != STDERR_FILENO) {
			error_code = api_get_error_code_from_errno();

			log_error("Could not redirect stderr for child process (executable: %s, pid: %u): %s (%d)",
			          executable->buffer, getpid(), get_errno_name(errno), errno);

			goto child_error;
		}

		// notify parent
		if (robust_write(status_pipe[1], &error_code, sizeof(error_code)) < 0) {
			error_code = api_get_error_code_from_errno();

			log_error("Could not write to status pipe for child process (executable: %s, pid: %u): %s (%d)",
			          executable->buffer, getpid(), get_errno_name(errno), errno);

			goto child_error;
		}

		// disable log output. if stderr was not the current log output then
		// the log file is still open at this point. the next step is to close
		// all remaining file descriptors. just for good measure disable the
		// log output beforehand
		log_set_file(NULL);

		// close all file descriptors except the std* ones
		for (i = STDERR_FILENO + 1; i < sc_open_max; ++i) {
			close(i);
		}

		// execvpe only returns in case of an error
		execvpe(executable->buffer, (char **)arguments_array.bytes, (char **)environment_array.bytes);

		if (errno == ENOENT) {
			_exit(PROCESS_E_DOES_NOT_EXIST);
		} else {
			_exit(PROCESS_E_CANNOT_EXECUTE);
		}

	child_error:
		// notify parent in all cases
		if (robust_write(status_pipe[1], &error_code, sizeof(error_code)) < 0) {
			log_error("Could not write to status pipe for child process (executable: %s, pid: %u): %s (%d)",
			          executable->buffer, getpid(), get_errno_name(errno), errno);
		}

		close(status_pipe[1]);

		_exit(PROCESS_E_INTERNAL_ERROR);
	}

	phase = 11;

	// wait for child to start successfully
	if (robust_read(status_pipe[0], &error_code, sizeof(error_code)) < 0) {
		error_code = api_get_error_code_from_errno();

		log_error("Could not read from status pipe for child process (executable: %s, pid: %u): %s (%d)",
		          executable->buffer, pid, get_errno_name(errno), errno);

		goto cleanup;
	}

	if (error_code != API_E_SUCCESS) {
		goto cleanup;
	}

	// create process object
	process = calloc(1, sizeof(Process));

	if (process == NULL) {
		error_code = API_E_NO_FREE_MEMORY;

		log_error("Could not allocate process object: %s (%d)",
		          get_errno_name(ENOMEM), ENOMEM);

		goto cleanup;
	}

	phase = 12;

	// setup process object
	process->executable = executable;
	process->arguments = arguments;
	process->environment = environment;
	process->working_directory = working_directory;
	process->uid = uid;
	process->gid = gid;
	process->pid = pid;
	process->stdin = stdin;
	process->stdout = stdout;
	process->stderr = stderr;
	process->release_on_death = release_on_death;
	process->state_changed = state_changed;
	process->opaque = opaque;
	process->state = PROCESS_STATE_RUNNING;
	process->timestamp = time(NULL);
	process->exit_code = 0; // invalid

	if (pipe_create(&process->state_change_pipe, 0) < 0) {
		error_code = api_get_error_code_from_errno();

		log_error("Could not create state change pipe child process (executable: %s, pid: %u): %s (%d)",
		          executable->buffer, pid, get_errno_name(errno), errno);

		goto cleanup;
	}

	phase = 13;

	if (event_add_source(process->state_change_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC,
	                     EVENT_READ, process_handle_state_change, process) < 0) {
		goto cleanup;
	}

	phase = 14;

	// create process object
	error_code = object_create(&process->base,
	                           OBJECT_TYPE_PROCESS,
	                           session,
	                           object_create_flags |
	                           OBJECT_CREATE_FLAG_INTERNAL,
	                           process_destroy,
	                           process_signature);

	if (error_code != API_E_SUCCESS) {
		goto cleanup;
	}

	phase = 15;

	if (id != NULL) {
		*id = process->base.id;
	}

	if (object != NULL) {
		*object = process;
	}

	// start thread to wait for child process state changes
	thread_create(&process->wait_thread, process_wait, process);

	log_debug("Spawned process object (id: %u, executable: %s, pid: %u)",
	          process->base.id, executable->buffer, process->pid);

	close(status_pipe[0]);
	close(status_pipe[1]);
	array_destroy(&arguments_array, NULL);
	array_destroy(&environment_array, NULL);

cleanup:
	switch (phase) { // no breaks, all cases fall through intentionally
	case 14:
		event_remove_source(process->state_change_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC);

	case 13:
		pipe_destroy(&process->state_change_pipe);

	case 12:
		free(process);

	case 11:
		kill(pid, SIGKILL);

	case 10:
		close(status_pipe[0]);
		close(status_pipe[1]);

	case 9:
		file_release(stderr);

	case 8:
		file_release(stdout);

	case 7:
		file_release(stdin);

	case 6:
		string_unlock_and_release(working_directory);

	case 5:
		array_destroy(&environment_array, NULL);

	case 4:
		list_unlock_and_release(environment);

	case 3:
		array_destroy(&arguments_array, NULL);

	case 2:
		list_unlock_and_release(arguments);

	case 1:
		string_unlock_and_release(executable);

	default:
		break;
	}

	return phase == 15 ? API_E_SUCCESS : error_code;
}
Example #14
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;
}
Example #15
0
int event_run_platform(Array *event_sources, int *running) {
	int result = -1;
	int i;
	EventSource *event_source;
	fd_set *fd_read_set;
	fd_set *fd_write_set;
	int ready;
	int handled;
	uint8_t byte = 1;
	int rc;
	int event_source_count;
	int received_events;

	if (event_add_source(_usb_poller.ready_pipe[0], EVENT_SOURCE_TYPE_GENERIC,
	                     EVENT_READ, event_forward_usb_events,
	                     event_sources) < 0) {
		return -1;
	}

	*running = 1;

	_usb_poller.running = 1;

	thread_create(&_usb_poller.thread, event_poll_usb_events, event_sources);

	event_cleanup_sources();

	while (*running) {
		// update SocketSet arrays
		if (event_reserve_socket_set(&_socket_read_set, // FIXME: this over allocates
		                             event_sources->count) < 0) {
			log_error("Could not resize socket read set: %s (%d)",
			          get_errno_name(errno), errno);

			goto cleanup;
		}

		if (event_reserve_socket_set(&_socket_write_set, // FIXME: this over allocates
		                             event_sources->count) < 0) {
			log_error("Could not resize socket write set: %s (%d)",
			          get_errno_name(errno), errno);

			goto cleanup;
		}

		_socket_read_set->count = 0;
		_socket_write_set->count = 0;

		for (i = 0; i < event_sources->count; i++) {
			event_source = array_get(event_sources, i);

			if (event_source->type != EVENT_SOURCE_TYPE_GENERIC) {
				continue;
			}

			if (event_source->events & EVENT_READ) {
				_socket_read_set->sockets[_socket_read_set->count++] = event_source->handle;
			}

			if (event_source->events & EVENT_WRITE) {
				_socket_write_set->sockets[_socket_write_set->count++] = event_source->handle;
			}
		}

		// start to select
		log_debug("Starting to select on %d + %d %s event source(s)",
		          _socket_read_set->count, _socket_write_set->count,
		          event_get_source_type_name(EVENT_SOURCE_TYPE_GENERIC, 0));

		semaphore_release(&_usb_poller.resume);

		fd_read_set = event_get_socket_set_as_fd_set(_socket_read_set);
		fd_write_set = event_get_socket_set_as_fd_set(_socket_write_set);

		ready = select(0, fd_read_set, fd_write_set, NULL, NULL);

		if (_usb_poller.running) {
			log_debug("Sending suspend signal to USB poll thread");

			if (usbi_write(_usb_poller.suspend_pipe[1], &byte, 1) < 0) {
				log_error("Could not write to USB suspend pipe");

				_usb_poller.stuck = 1;
				*running = 0;

				goto cleanup;
			}

			semaphore_acquire(&_usb_poller.suspend);

			if (usbi_read(_usb_poller.suspend_pipe[0], &byte, 1) < 0) {
				log_error("Could not read from USB suspend pipe");

				_usb_poller.stuck = 1;
				*running = 0;

				goto cleanup;
			}
		}

		if (ready < 0) {
			rc = ERRNO_WINAPI_OFFSET + WSAGetLastError();

			if (rc == ERRNO_WINAPI_OFFSET + WSAEINTR) {
				continue;
			}

			log_error("Could not select on %s event sources: %s (%d)",
			          event_get_source_type_name(EVENT_SOURCE_TYPE_GENERIC, 0),
			          get_errno_name(rc), rc);

			*running = 0;

			goto cleanup;
		}

		// handle select result
		log_debug("Select returned %d %s event source(s) as ready",
		          ready,
		          event_get_source_type_name(EVENT_SOURCE_TYPE_GENERIC, 0));

		handled = 0;

		// cache event source count here to avoid looking at new event
		// sources that got added during the event handling
		event_source_count = event_sources->count;

		for (i = 0; i < event_source_count && ready > handled; ++i) {
			event_source = array_get(event_sources, i);
			received_events = 0;

			if (event_source->type != EVENT_SOURCE_TYPE_GENERIC) {
				continue;
			}

			if (FD_ISSET(event_source->handle, fd_read_set)) {
				received_events |= EVENT_READ;
			}

			if (FD_ISSET(event_source->handle, fd_write_set)) {
				received_events |= EVENT_WRITE;
			}

			if (received_events == 0) {
				continue;
			}

			if (event_source->state != EVENT_SOURCE_STATE_NORMAL) {
				log_debug("Ignoring %s event source (handle: %d, received events: %d) marked as removed at index %d",
				          event_get_source_type_name(event_source->type, 0),
				          event_source->handle, received_events, i);
			} else {
				log_debug("Handling %s event source (handle: %d, received events: %d) at index %d",
				          event_get_source_type_name(event_source->type, 0),
				          event_source->handle, received_events, i);

				if (event_source->function != NULL) {
					event_source->function(event_source->opaque);
				}
			}

			++handled;

			if (!*running) {
				break;
			}
		}

		if (ready == handled) {
			log_debug("Handled all ready %s event sources",
			          event_get_source_type_name(EVENT_SOURCE_TYPE_GENERIC, 0));
		} else {
			log_warn("Handled only %d of %d ready %s event source(s)",
			         handled, ready,
			         event_get_source_type_name(EVENT_SOURCE_TYPE_GENERIC, 0));
		}

		// now remove event sources that got marked as removed during the
		// event handling
		event_cleanup_sources();
	}

	result = 0;

cleanup:
	if (_usb_poller.running && !_usb_poller.stuck) {
		_usb_poller.running = 0;

		log_debug("Stopping USB poll thread");

		if (usbi_write(_usb_poller.suspend_pipe[1], &byte, 1) < 0) {
			log_error("Could not write to USB suspend pipe");
		} else {
			semaphore_release(&_usb_poller.resume);
			thread_join(&_usb_poller.thread);
		}
	}

	event_remove_source(_usb_poller.ready_pipe[0], EVENT_SOURCE_TYPE_GENERIC);

	return result;
}
Example #16
0
// Init function called from central brickd code
int rs485_extension_init(void) {
    uint8_t _tmp_eeprom_read_buf[4];
    int _eeprom_read_status;
    int phase = 0;
    
    log_info("RS485: Checking presence of extension");
    
    // Modbus config: TYPE
    _eeprom_read_status =
    i2c_eeprom_read((uint16_t)RS485_EXTENSION_MODBUS_CONFIG_LOCATION_TYPE,
                    _tmp_eeprom_read_buf, 4);
    if (_eeprom_read_status <= 0) {
        log_error("RS485: EEPROM read error. Most probably no RS485 extension present");
		return 0;
    }
    _modbus_serial_config_type = (uint32_t)((_tmp_eeprom_read_buf[0] << 0) |
                                 (_tmp_eeprom_read_buf[1] << 8) |
                                 (_tmp_eeprom_read_buf[2] << 16) |
                                 (_tmp_eeprom_read_buf[3] << 24));
        
    if (_modbus_serial_config_type == RS485_EXTENSION_TYPE) {

        log_info("RS485: Initializing extension subsystem");
        
        // Create base stack
        if(stack_create(&_rs485_extension.base, "rs485_extension",
                        (StackDispatchRequestFunction)rs485_extension_dispatch_to_modbus) < 0) {
            log_error("RS485: Could not create base stack for extension: %s (%d)",
                      get_errno_name(errno), errno);

            goto cleanup;
        }

        phase = 1;
        
        // Add to stacks array
        if(hardware_add_stack(&_rs485_extension.base) < 0) {
            goto cleanup;
        }

        phase = 2;
        
        // Initialize modbus packet queue
        if(queue_create(&_rs485_extension.packet_to_modbus_queue, sizeof(RS485ExtensionPacket)) < 0) {
            log_error("RS485: Could not create Modbus queue: %s (%d)",
                    get_errno_name(errno), errno);
            goto cleanup;
        }
        
        // Reading and storing eeprom config
        
        // Modbus config: ADDRESS
        _eeprom_read_status =
        i2c_eeprom_read((uint16_t)RS485_EXTENSION_MODBUS_CONFIG_LOCATION_ADDRESS,
                        _tmp_eeprom_read_buf, 4);
        if (_eeprom_read_status <= 0) {
            log_error("RS485: Could not read config ADDRESS from EEPROM");
            goto cleanup;
        }
        _modbus_serial_config_address = (uint32_t)((_tmp_eeprom_read_buf[0] << 0) |
                                        (_tmp_eeprom_read_buf[1] << 8) |
                                        (_tmp_eeprom_read_buf[2] << 16) |
                                        (_tmp_eeprom_read_buf[3] << 24));
                                        
        // Modbus config: BAUDRATE
        _eeprom_read_status = i2c_eeprom_read((uint16_t)RS485_EXTENSION_MODBUS_CONFIG_LOCATION_BAUDRATE,
                                              _tmp_eeprom_read_buf, 4);
        if (_eeprom_read_status <= 0) {
            log_error("RS485: Could not read config BAUDRATE from EEPROM");
            goto cleanup;
        }
        _modbus_serial_config_baudrate = (uint32_t)((_tmp_eeprom_read_buf[0] << 0) |
                                         (_tmp_eeprom_read_buf[1] << 8) |
                                         (_tmp_eeprom_read_buf[2] << 16) |
                                         (_tmp_eeprom_read_buf[3] << 24));
    
        // Modbus config: PARITY
        _eeprom_read_status = i2c_eeprom_read((uint16_t)RS485_EXTENSION_MODBUS_CONFIG_LOCATION_PARTIY,
                                              _tmp_eeprom_read_buf, 1);
        if (_eeprom_read_status <= 0) {
            log_error("RS485: Could not read config PARITY from EEPROM");
            goto cleanup;
        }
        if(_tmp_eeprom_read_buf[0] == RS485_EXTENSION_SERIAL_PARITY_NONE) {
            _modbus_serial_config_parity = RS485_EXTENSION_SERIAL_PARITY_NONE;
        }
        else if (_tmp_eeprom_read_buf[0] == RS485_EXTENSION_SERIAL_PARITY_EVEN){
            _modbus_serial_config_parity = RS485_EXTENSION_SERIAL_PARITY_EVEN;
        }
        else {
            _modbus_serial_config_parity = RS485_EXTENSION_SERIAL_PARITY_ODD;
        }
    
        // Modbus config: STOPBITS
        _eeprom_read_status =
        i2c_eeprom_read((uint16_t)RS485_EXTENSION_MODBUS_CONFIG_LOCATION_STOPBITS,
                        _tmp_eeprom_read_buf, 1);
        if (_eeprom_read_status <= 0) {
            log_error("RS485: Could not read config STOPBITS from EEPROM");
            goto cleanup;
        }
        _modbus_serial_config_stopbits = _tmp_eeprom_read_buf[0];

        // Modbus config (if master): SLAVE ADDRESSES
        if(_modbus_serial_config_address == 0) {
            _rs485_extension.slave_num = 0;
            uint16_t _current_eeprom_location =
            RS485_EXTENSION_MODBUS_CONFIG_LOCATION_SLAVE_ADDRESSES_START;
            uint32_t _current_slave_address;

            _rs485_extension.slave_num = 0;

            do {
                _eeprom_read_status = i2c_eeprom_read(_current_eeprom_location, _tmp_eeprom_read_buf, 4);
                if (_eeprom_read_status <= 0) {
                    log_error("RS485: Could not read config SLAVE ADDRESSES from EEPROM");
                    goto cleanup;
                }
                _current_slave_address = (uint32_t)((_tmp_eeprom_read_buf[0] << 0) |
                                         (_tmp_eeprom_read_buf[1] << 8) |
                                         (_tmp_eeprom_read_buf[2] << 16) |
                                         (_tmp_eeprom_read_buf[3] << 24));
        
                if(_current_slave_address != 0) {
                    _rs485_extension.slaves[_rs485_extension.slave_num] = _current_slave_address;
                    _rs485_extension.slave_num ++;
                }
                _current_eeprom_location = _current_eeprom_location + 4;
            }
            while(_current_slave_address != 0 
                  && 
                  _rs485_extension.slave_num < RS485_EXTENSION_MODBUS_MAX_SLAVES);
        }
        
        // Configuring serial interface from the configs
        if(rs485_extension_serial_init(RS485_EXTENSION_SERIAL_DEVICE) < 0) {
            goto cleanup;
        }
        
        // Initial RS485 TX/RX state
        init_tx_rx_state();
        
        phase = 3;

        // Setup partial data receive timer        
        setup_timer(&partial_receive_timer, TIME_UNIT_NSEC, PARTIAL_RECEIVE_TIMEOUT);
        _partial_receive_timeout_event = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
        
        if(!(_partial_receive_timeout_event < 0)) {
            if(event_add_source(_partial_receive_timeout_event, EVENT_SOURCE_TYPE_GENERIC,
                                EVENT_READ, partial_receive_timeout_handler, NULL) < 0) {
                log_error("RS485: Could not add partial receive timeout notification pipe as event source");
                goto cleanup;
            }
        }
        else {
            log_error("RS485: Could not create partial receive timer");
            goto cleanup;
        }

        phase = 4;

        // Adding serial data available event
        if(event_add_source(_rs485_serial_fd, EVENT_SOURCE_TYPE_GENERIC,
                            EVENT_READ, rs485_serial_data_available_handler, NULL) < 0) {
            log_error("RS485: Could not add new serial data event");
            goto cleanup;
        }

        phase = 5;

        // Setup master retry timer        
        setup_timer(&master_retry_timer, TIME_UNIT_NSEC, MASTER_RETRY_TIMEOUT);
        _master_retry_event = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
        
        if(!(_master_retry_event < 0)) {
            if(event_add_source(_master_retry_event, EVENT_SOURCE_TYPE_GENERIC,
                                EVENT_READ, master_retry_timeout_handler, NULL) < 0) {
                log_error("RS485: Could not add Modbus master retry notification pipe as event source");
                goto cleanup;
            }
        }
        else {
            log_error("RS485: Could not create Modbus master retry timer");
            goto cleanup;
        }

        phase = 6;

        // Setup send verify timer
        setup_timer(&send_verify_timer, TIME_UNIT_NSEC, SEND_VERIFY_TIMEOUT);
        _send_verify_event = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
        
        if(!(_send_verify_event < 0)) {
            if(event_add_source(_send_verify_event, EVENT_SOURCE_TYPE_GENERIC,
                                EVENT_READ, send_verify_timeout_handler, NULL) < 0) {
                log_error("RS485: Could not add Modbus send verify notification pipe as event source");
                goto cleanup;
            }
        }
        else {
            log_error("RS485: Could not create Modbus send verify timer");
            goto cleanup;
        }

        phase = 7;

        // Get things going in case of a master
        if(_modbus_serial_config_address == 0 && _rs485_extension.slave_num > 0) {
            // Setup master poll slave timer
            setup_timer(&master_poll_slave_timer, TIME_UNIT_NSEC, MASTER_POLL_SLAVE_TIMEOUT);
            _master_poll_slave_event = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
            if(!(_master_poll_slave_event < 0) ) {
                if(event_add_source(_master_poll_slave_event, EVENT_SOURCE_TYPE_GENERIC,
                                    EVENT_READ, master_poll_slave_timeout_handler, NULL) < 0) {
                    log_error("RS485: Could not add Modbus master poll slave notification pipe as event source");
                    goto cleanup;
                }
            }
            else {
                log_error("RS485: Could not create Modbus master poll slave timer");
                goto cleanup;
            }
            master_poll_slave_timeout_handler(NULL);
        }

        phase = 8;
        _initialized = true;
    }
    else {
        log_info("RS485: Extension not present");
        goto cleanup;
    }
    
    cleanup:
        switch (phase) { // no breaks, all cases fall through intentionally
            case 7:
                close(_send_verify_event);
                event_remove_source(_send_verify_event, EVENT_SOURCE_TYPE_GENERIC);
                
            case 6:
                close(_master_retry_event);
                event_remove_source(_master_retry_event, EVENT_SOURCE_TYPE_GENERIC);
            
            case 5:
                close(_rs485_serial_fd);
                event_remove_source(_rs485_serial_fd, EVENT_SOURCE_TYPE_GENERIC);

            case 4:
                close(_partial_receive_timeout_event);
                event_remove_source(_partial_receive_timeout_event, EVENT_SOURCE_TYPE_GENERIC);

            case 3:
                queue_destroy(&_rs485_extension.packet_to_modbus_queue, NULL);
                
            case 2:
                hardware_remove_stack(&_rs485_extension.base);

            case 1:
                stack_destroy(&_rs485_extension.base);

            default:
                break;
        }
    return phase == 8 ? 0 : -1;
}
Example #17
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 #18
0
// NOTE: this function needs to call RegisterServiceCtrlHandlerEx and
// SetServiceStatus in all circumstances if brickd is running as service
static int generic_main(bool log_to_file, bool debug, bool libusb_debug) {
	int exit_code = EXIT_FAILURE;
	const char *mutex_name = "Global\\Tinkerforge-Brick-Daemon-Single-Instance";
	HANDLE mutex_handle = NULL;
	bool fatal_error = false;
	DWORD service_exit_code = NO_ERROR;
	int rc;
	char filename[1024];
	int i;
	FILE *log_file = NULL;
	WSADATA wsa_data;
	DEV_BROADCAST_DEVICEINTERFACE notification_filter;
	HDEVNOTIFY notification_handle;

	mutex_handle = OpenMutex(SYNCHRONIZE, FALSE, mutex_name);

	if (mutex_handle == NULL) {
		rc = GetLastError();

		if (rc == ERROR_ACCESS_DENIED) {
			rc = service_is_running();

			if (rc < 0) {
				fatal_error = true;
				// FIXME: set service_exit_code

				goto error_mutex;
			} else if (rc) {
				fatal_error = true;
				service_exit_code = ERROR_SERVICE_ALREADY_RUNNING;

				log_error("Could not start as %s, another instance is already running as service",
				          _run_as_service ? "service" : "console application");

				goto error_mutex;
			}
		}

		if (rc != ERROR_FILE_NOT_FOUND) {
			fatal_error = true;
			// FIXME: set service_exit_code
			rc += ERRNO_WINAPI_OFFSET;

			log_error("Could not open single instance mutex: %s (%d)",
			          get_errno_name(rc), rc);

			goto error_mutex;
		}
	}

	if (mutex_handle != NULL) {
		fatal_error = true;
		service_exit_code = ERROR_SERVICE_ALREADY_RUNNING;

		log_error("Could not start as %s, another instance is already running",
		          _run_as_service ? "service" : "console application");

		goto error_mutex;
	}

	mutex_handle = CreateMutex(NULL, FALSE, mutex_name);

	if (mutex_handle == NULL) {
		fatal_error = true;
		// FIXME: set service_exit_code
		rc = ERRNO_WINAPI_OFFSET + GetLastError();

		log_error("Could not create single instance mutex: %s (%d)",
		          get_errno_name(rc), rc);

		goto error_mutex;
	}

	if (log_to_file) {
		if (GetModuleFileName(NULL, filename, sizeof(filename)) == 0) {
			rc = ERRNO_WINAPI_OFFSET + GetLastError();

			log_warn("Could not get module file name: %s (%d)",
			         get_errno_name(rc), rc);
		} else {
			i = strlen(filename);

			if (i < 4) {
				log_warn("Module file name '%s' is too short", filename);
			} else {
				filename[i - 3] = '\0';
				string_append(filename, "log", sizeof(filename));

				log_file = fopen(filename, "a+");

				if (log_file == NULL) {
					log_warn("Could not open log file '%s'", filename);
				} else {
					printf("Logging to '%s'\n", filename);

					log_set_file(log_file);
				}
			}
		}
	} else if (_run_as_service) {
		log_set_file(NULL);
	}

	if (!_run_as_service &&
	    !SetConsoleCtrlHandler(console_ctrl_handler, TRUE)) {
		rc = ERRNO_WINAPI_OFFSET + GetLastError();

		log_warn("Could not set console control handler: %s (%d)",
		         get_errno_name(rc), rc);
	}

	log_set_debug_override(debug);

	log_set_level(LOG_CATEGORY_EVENT, config_get_option("log_level.event")->value.log_level);
	log_set_level(LOG_CATEGORY_USB, config_get_option("log_level.usb")->value.log_level);
	log_set_level(LOG_CATEGORY_NETWORK, config_get_option("log_level.network")->value.log_level);
	log_set_level(LOG_CATEGORY_HOTPLUG, config_get_option("log_level.hotplug")->value.log_level);
	log_set_level(LOG_CATEGORY_HARDWARE, config_get_option("log_level.hardware")->value.log_level);
	log_set_level(LOG_CATEGORY_WEBSOCKET, config_get_option("log_level.websocket")->value.log_level);
	log_set_level(LOG_CATEGORY_OTHER, config_get_option("log_level.other")->value.log_level);

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

		fatal_error = true;

		goto error_config;
	}

	if (_run_as_service) {
		log_info("Brick Daemon %s started (as service)", VERSION_STRING);
	} else {
		log_info("Brick Daemon %s started", VERSION_STRING);
	}

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

	// initialize service status
error_config:
error_mutex:
	if (_run_as_service) {
		if (service_init(service_control_handler) < 0) {
			// FIXME: set service_exit_code
			goto error;
		}

		if (!fatal_error) {
			// service is starting
			service_set_status(SERVICE_START_PENDING, NO_ERROR);
		}
	}

	if (fatal_error) {
		goto error;
	}

	// initialize WinSock2
	if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) {
		// FIXME: set service_exit_code
		rc = ERRNO_WINAPI_OFFSET + WSAGetLastError();

		log_error("Could not initialize Windows Sockets 2.2: %s (%d)",
		          get_errno_name(rc), rc);

		goto error_event;
	}

	if (event_init() < 0) {
		// FIXME: set service_exit_code
		goto error_event;
	}

	if (hardware_init() < 0) {
		// FIXME: set service_exit_code
		goto error_hardware;
	}

	if (usb_init(libusb_debug) < 0) {
		// FIXME: set service_exit_code
		goto error_usb;
	}

	// create notification pipe
	if (pipe_create(&_notification_pipe) < 0) {
		// FIXME: set service_exit_code

		log_error("Could not create notification pipe: %s (%d)",
		          get_errno_name(errno), errno);

		goto error_pipe;
	}

	if (event_add_source(_notification_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC,
	                     EVENT_READ, forward_notifications, NULL) < 0) {
		// FIXME: set service_exit_code
		goto error_pipe_add;
	}

	// register device notification
	ZeroMemory(&notification_filter, sizeof(notification_filter));

	notification_filter.dbcc_size = sizeof(notification_filter);
	notification_filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
	notification_filter.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;

	if (_run_as_service) {
		notification_handle = RegisterDeviceNotification((HANDLE)service_get_status_handle(),
		                                                 &notification_filter,
		                                                 DEVICE_NOTIFY_SERVICE_HANDLE);
	} else {
		if (message_pump_start() < 0) {
			// FIXME: set service_exit_code
			goto error_pipe_add;
		}

		notification_handle = RegisterDeviceNotification(_message_pump_hwnd,
		                                                 &notification_filter,
		                                                 DEVICE_NOTIFY_WINDOW_HANDLE);
	}

	if (notification_handle == NULL) {
		// FIXME: set service_exit_code
		rc = ERRNO_WINAPI_OFFSET + GetLastError();

		log_error("Could not register for device notification: %s (%d)",
		          get_errno_name(rc), rc);

		goto error_notification;
	}

	if (network_init() < 0) {
		// FIXME: set service_exit_code
		goto error_network;
	}

	// running
	if (_run_as_service) {
		service_set_status(SERVICE_RUNNING, NO_ERROR);
	}

	if (event_run(network_cleanup_clients_and_zombies) < 0) {
		// FIXME: set service_exit_code
		goto error_run;
	}

	exit_code = EXIT_SUCCESS;

error_run:
	network_exit();

error_network:
	UnregisterDeviceNotification(notification_handle);

error_notification:
	if (!_run_as_service) {
		message_pump_stop();
	}

	event_remove_source(_notification_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC);

error_pipe_add:
	pipe_destroy(&_notification_pipe);

error_pipe:
	usb_exit();

error_usb:
	hardware_exit();

error_hardware:
	event_exit();

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

error:
	if (!_run_as_service) {
		// unregister the console handler before exiting the log. otherwise a
		// control event might be send to the control handler after the log
		// is not available anymore and the control handler tries to write a
		// log messages triggering a crash. this situation could easily be
		// created by clicking the close button of the command prompt window
		// while the getch call is waiting for the user to press a key.
		SetConsoleCtrlHandler(console_ctrl_handler, FALSE);
	}

	log_exit();

	config_exit();

	if (_run_as_service) {
		// because the service process can be terminated at any time after
		// entering SERVICE_STOPPED state the mutex is closed beforehand,
		// even though this creates a tiny time window in which the service
		// is still running but the mutex is not held anymore
		if (mutex_handle != NULL) {
			CloseHandle(mutex_handle);
		}

		// service is now stopped
		service_set_status(SERVICE_STOPPED, service_exit_code);
	} else {
		if (_pause_before_exit) {
			printf("Press any key to exit...\n");
			getch();
		}

		if (mutex_handle != NULL) {
			CloseHandle(mutex_handle);
		}
	}

	return exit_code;
}