Beispiel #1
0
static int
pipe_attach_remote_writer (pipe_t *self, const char *remote, bool unicast)
{
    assert (self);
    if (self->reader == REMOTE_NODE) {
        //  We're witnessing two nodes chatting, so we can drop the pipe
        //  and forget all about it
        pipe_destroy (&self);
        return 0;
    }
    else
    if (self->writer == NULL) {
        //  This is how we indicate a remote writer
        self->writer = REMOTE_NODE;
        self->remote = strdup (remote);
        zsys_info ("%s: attach remote writer", self->name);

        if (self->reader && !unicast) {
            //  Tell remote node we're acting as reader, if we got a
            //  broadcast message. If we got a unicast message, the peer
            //  already knows about us, so don't re-echo the message
            zmsg_t *msg = zmsg_new ();
            zmsg_addstr (msg, "HAVE READER");
            zmsg_addstr (msg, self->name);
            zyre_whisper (self->server->zyre, self->remote, &msg);
            zsys_info ("%s: tell peer we are now reader", self->name);
        }
        return 0;
    }
    zsys_info ("%s: pipe already has writer: ignored", self->name);
    return -1;
}
Beispiel #2
0
static void
pipe_drop_local_writer (pipe_t **self_p)
{
    assert (self_p);
    if (*self_p) {
        pipe_t *self = *self_p;
        //  TODO: what if self->writer is REMOTE_NODE?
        self->writer = NULL;
        if (self->reader) {
            if (self->reader == REMOTE_NODE) {
                //  Tell remote node we're dropping off
                zmsg_t *msg = zmsg_new ();
                zmsg_addstr (msg, "DROP WRITER");
                zmsg_addstr (msg, self->name);
                zyre_whisper (self->server->zyre, self->remote, &msg);
                zsys_info ("%s: tell peer we stopped being writer", self->name);
            }
            else {
                engine_send_event (self->reader, writer_dropped_event);
                //  Don't destroy pipe yet - reader is still using it
                *self_p = NULL;
            }
        }
        pipe_destroy (self_p);
    }
}
Beispiel #3
0
static int pipe_check_availability(void) {
  grpc_wakeup_fd fd;
  if (pipe_init(&fd) == GRPC_ERROR_NONE) {
    pipe_destroy(&fd);
    return 1;
  } else {
    return 0;
  }
}
Beispiel #4
0
void event_exit_platform(void) {
	semaphore_destroy(&_usb_poll_resume);
	semaphore_destroy(&_usb_poll_suspend);

	array_destroy(&_usb_poll_pollfds, NULL);

	pipe_destroy(&_usb_poll_ready_pipe);

	usbi_close(_usb_poll_suspend_pipe[0]);
	usbi_close(_usb_poll_suspend_pipe[1]);

	event_remove_source(_stop_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC);
	pipe_destroy(&_stop_pipe);

	free(_socket_error_set);
	free(_socket_write_set);
	free(_socket_read_set);
}
Beispiel #5
0
void l2_packet_deinit(struct l2_packet_data *l2)
{
	if (l2 == NULL)
		return;

	if (l2->fd >= 0) {
		eloop_unregister_read_sock(l2->fd);
		pipe_destroy(l2->fd);
	}
		
	os_free(l2);
}
Beispiel #6
0
void iokit_exit(void) {
	log_debug("Shutting down IOKit subsystem");

	if (_running) {
		_running = false;

		CFRunLoopStop(_run_loop);
		CFRelease(_run_loop);

		thread_join(&_poll_thread);
	}

	thread_destroy(&_poll_thread);

	event_remove_source(_notification_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC);

	pipe_destroy(&_notification_pipe);
}
Beispiel #7
0
static void
pipe_drop_remote_writer (pipe_t **self_p, const char *remote)
{
    assert (self_p);
    if (*self_p) {
        pipe_t *self = *self_p;
        if (self->writer == REMOTE_NODE && streq (self->remote, remote)) {
            self->writer = NULL;
            if (self->reader) {
                assert (self->reader != REMOTE_NODE);
                engine_send_event (self->reader, writer_dropped_event);
                //  Don't destroy pipe yet - reader is still using it
                *self_p = NULL;
            }
        }
        pipe_destroy (self_p);
    }
}
Beispiel #8
0
static void process_destroy(Object *object) {
	Process *process = (Process *)object;
	int rc;
	bool stuck = false;

	// remove the state change pipe from the event loop to avoid sending
	// callbacks in case the child process is still alive and has to be killed
	event_remove_source(process->state_change_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC);

	// FIXME: this code here has the same race condition as process_kill
	if (process_is_alive(process)) {
		log_warn("Destroying process object (id: %u, executable: %s) while child process (pid: %u) is still alive",
		         process->base.id, process->executable->buffer, process->pid);

		rc = kill(process->pid, SIGKILL);

		if (rc < 0) {
			if (errno != ESRCH) {
				stuck = true;
			}

			log_error("Could not send SIGKILL signal to child process (executable: %s, pid: %u): %s (%d)",
			          process->executable->buffer, process->pid, get_errno_name(errno), errno);
		}
	}

	if (!stuck) {
		thread_join(&process->wait_thread);
	}

	thread_destroy(&process->wait_thread);

	pipe_destroy(&process->state_change_pipe);

	file_release(process->stderr);
	file_release(process->stdout);
	file_release(process->stdin);
	string_unlock_and_release(process->working_directory);
	list_unlock_and_release(process->environment);
	list_unlock_and_release(process->arguments);
	string_unlock_and_release(process->executable);

	free(process);
}
Beispiel #9
0
DWORD
CE_Cleanup(PCONFIGURATION_ENTRY pce)
{
    DWORD dwErr = NO_ERROR;
    int i;

    if (pce->hRtmNotificationHandle) {
        dwErr = RtmDeregisterFromChangeNotification(
            pce->hRtmHandle,
            pce->hRtmNotificationHandle);
        if (dwErr != NO_ERROR)
            TRACE1(CONFIGURATION,
                   "Error %u deregistering for change from RTM", dwErr);
    }
    pce->hRtmNotificationHandle = NULL;

    if (pce->hRtmHandle) {
        dwErr = RtmDeregisterEntity(pce->hRtmHandle);

        if (dwErr != NO_ERROR)
            TRACE1(CONFIGURATION,
                   "Error %u deregistering from RTM", dwErr);
    }
    pce->hRtmHandle             = NULL;

    for (i = 0; i < PIPE_INSTANCES; i++) {
	if (pce->pipes[i]) {
	    pipe_destroy(pce->pipes[i]);
	    pce->pipes[i] = NULL;
	}
    }

    if (pce->hMprConfig != NULL) {
	MprConfigServerDisconnect(pce->hMprConfig);
    }
    pce->hMprConfig = NULL;

    pce->iscStatus = XORPRTM_STATUS_STOPPED;

    return NO_ERROR;
}
Beispiel #10
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;
}
Beispiel #11
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;
}
Beispiel #12
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;
}
Beispiel #13
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;
}
Beispiel #14
0
static void
s_delete_pipe (void *argument)
{
    pipe_t *pipe = (pipe_t *) argument;
    pipe_destroy (&pipe);
}
Beispiel #15
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;
}
Beispiel #16
0
/*
 * Create a new instance of a pipe and return a pointer to
 * its instance structure.
 */
pipe_instance_t *
pipe_new(void)
{
    pipe_instance_t *npp;
    int failed;
    DWORD result;

    TRACE0(ENTER, "Entering pipe_new");

    npp = malloc(sizeof(*npp));
    if (npp == NULL)
	return NULL;
    ZeroMemory(npp, sizeof(*npp));

    failed = 1;

    /* XXX buffer management */
    npp->rsize = PIPE_READBUF_SIZE;
    npp->state = PIPE_STATE_INIT;

    InitializeCriticalSection(&npp->rcs);

    /*
     * Create the event object used to signal connection completion.
     */
    npp->cevent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (npp->cevent == NULL) {
	result = GetLastError();
        TRACE1(CONFIGURATION, "Error %u creating event", result);
	goto fail;
    }
    npp->cov.hEvent = npp->cevent;

    /*
     * Create the event object used to signal read completion.
     */
    npp->revent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (npp->revent == NULL) {
	result = GetLastError();
        TRACE1(CONFIGURATION, "Error %u creating event", result);
	goto fail;
    }
    npp->rov.hEvent = npp->revent;

    /*
     * Create the instance of the named pipe itself.
     */
    npp->pipe = CreateNamedPipeA(XORPRTM_PIPENAME,
				 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
				 PIPE_TYPE_MESSAGE, PIPE_INSTANCES, 0, 0,
				 XORPRTM_PIPETIMEOUT, NULL);
    if (npp->pipe == NULL) {
	result = GetLastError();
	TRACE1(CONFIGURATION, "Error %u creating named pipe", result);
	goto fail;
    }

    failed = 0;
  fail:
    if (failed) {
	pipe_destroy(npp);
	npp = NULL;
    }
    TRACE1(ENTER, "Leaving pipe_new %p", npp);
    return (npp);
}